midibuf.c 5.7 KB
Newer Older
M
Markus Grabner 已提交
1
/*
2
 * Line 6 Linux USB driver - 0.9.1beta
M
Markus Grabner 已提交
3
 *
4
 * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
M
Markus Grabner 已提交
5 6 7 8 9 10 11 12 13 14 15
 *
 *	This program is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU General Public License as
 *	published by the Free Software Foundation, version 2.
 *
 */

#include <linux/slab.h>

#include "midibuf.h"

16
static int midibuf_message_length(unsigned char code)
M
Markus Grabner 已提交
17
{
18 19
	int message_length;

20
	if (code < 0x80)
21
		message_length = -1;
22
	else if (code < 0xf0) {
M
Markus Grabner 已提交
23
		static const int length[] = { 3, 3, 3, 3, 2, 2, 3 };
24 25

		message_length = length[(code >> 4) - 8];
26
	} else {
M
Markus Grabner 已提交
27
		/*
28
		   Note that according to the MIDI specification 0xf2 is
29
		   the "Song Position Pointer", but this is used by Line 6
30 31
		   to send sysex messages to the host.
		 */
32
		static const int length[] = { -1, 2, -1, 2, -1, -1, 1, 1, 1, 1,
33 34
			1, 1, 1, -1, 1, 1
		};
35
		message_length = length[code & 0x0f];
M
Markus Grabner 已提交
36
	}
37 38

	return message_length;
M
Markus Grabner 已提交
39 40
}

41
static int midibuf_is_empty(struct midi_buffer *this)
42 43 44 45
{
	return (this->pos_read == this->pos_write) && !this->full;
}

46
static int midibuf_is_full(struct midi_buffer *this)
47 48 49 50
{
	return this->full;
}

51
void line6_midibuf_reset(struct midi_buffer *this)
M
Markus Grabner 已提交
52 53 54 55 56
{
	this->pos_read = this->pos_write = this->full = 0;
	this->command_prev = -1;
}

57
int line6_midibuf_init(struct midi_buffer *this, int size, int split)
M
Markus Grabner 已提交
58
{
59
	this->buf = kmalloc(size, GFP_KERNEL);
M
Markus Grabner 已提交
60

61
	if (this->buf == NULL)
M
Markus Grabner 已提交
62 63 64 65
		return -ENOMEM;

	this->size = size;
	this->split = split;
66
	line6_midibuf_reset(this);
M
Markus Grabner 已提交
67 68 69
	return 0;
}

70
void line6_midibuf_status(struct midi_buffer *this)
M
Markus Grabner 已提交
71
{
72 73 74
	pr_debug("midibuf size=%d split=%d pos_read=%d pos_write=%d full=%d command_prev=%02x\n",
		 this->size, this->split, this->pos_read, this->pos_write,
		 this->full, this->command_prev);
M
Markus Grabner 已提交
75 76
}

77
int line6_midibuf_bytes_free(struct midi_buffer *this)
M
Markus Grabner 已提交
78 79
{
	return
80 81 82 83
	    midibuf_is_full(this) ?
	    0 :
	    (this->pos_read - this->pos_write + this->size - 1) % this->size +
	    1;
M
Markus Grabner 已提交
84 85
}

86
int line6_midibuf_bytes_used(struct midi_buffer *this)
M
Markus Grabner 已提交
87 88
{
	return
89 90 91 92
	    midibuf_is_empty(this) ?
	    0 :
	    (this->pos_write - this->pos_read + this->size - 1) % this->size +
	    1;
M
Markus Grabner 已提交
93 94
}

95
int line6_midibuf_write(struct midi_buffer *this, unsigned char *data,
96
			int length)
M
Markus Grabner 已提交
97 98 99 100 101
{
	int bytes_free;
	int length1, length2;
	int skip_active_sense = 0;

102
	if (midibuf_is_full(this) || (length <= 0))
M
Markus Grabner 已提交
103 104 105
		return 0;

	/* skip trailing active sense */
106
	if (data[length - 1] == 0xfe) {
M
Markus Grabner 已提交
107 108 109 110
		--length;
		skip_active_sense = 1;
	}

111
	bytes_free = line6_midibuf_bytes_free(this);
M
Markus Grabner 已提交
112

113
	if (length > bytes_free)
M
Markus Grabner 已提交
114 115
		length = bytes_free;

116
	if (length > 0) {
M
Markus Grabner 已提交
117 118
		length1 = this->size - this->pos_write;

119
		if (length < length1) {
M
Markus Grabner 已提交
120 121 122
			/* no buffer wraparound */
			memcpy(this->buf + this->pos_write, data, length);
			this->pos_write += length;
123
		} else {
M
Markus Grabner 已提交
124 125 126 127 128 129 130
			/* buffer wraparound */
			length2 = length - length1;
			memcpy(this->buf + this->pos_write, data, length1);
			memcpy(this->buf, data + length1, length2);
			this->pos_write = length2;
		}

131
		if (this->pos_write == this->pos_read)
M
Markus Grabner 已提交
132 133 134 135 136 137
			this->full = 1;
	}

	return length + skip_active_sense;
}

138 139
int line6_midibuf_read(struct midi_buffer *this, unsigned char *data,
		       int length)
M
Markus Grabner 已提交
140 141 142 143 144 145 146 147
{
	int bytes_used;
	int length1, length2;
	int command;
	int midi_length;
	int repeat = 0;
	int i;

148 149 150
	/* we need to be able to store at least a 3 byte MIDI message */
	if (length < 3)
		return -EINVAL;
M
Markus Grabner 已提交
151

152
	if (midibuf_is_empty(this))
M
Markus Grabner 已提交
153 154
		return 0;

155
	bytes_used = line6_midibuf_bytes_used(this);
M
Markus Grabner 已提交
156

157
	if (length > bytes_used)
M
Markus Grabner 已提交
158 159 160 161 162 163 164
		length = bytes_used;

	length1 = this->size - this->pos_read;

	/* check MIDI command length */
	command = this->buf[this->pos_read];

165
	if (command & 0x80) {
M
Markus Grabner 已提交
166 167
		midi_length = midibuf_message_length(command);
		this->command_prev = command;
168 169
	} else {
		if (this->command_prev > 0) {
170 171
			int midi_length_prev =
			    midibuf_message_length(this->command_prev);
M
Markus Grabner 已提交
172

173
			if (midi_length_prev > 0) {
M
Markus Grabner 已提交
174 175
				midi_length = midi_length_prev - 1;
				repeat = 1;
176
			} else
M
Markus Grabner 已提交
177
				midi_length = -1;
178
		} else
M
Markus Grabner 已提交
179 180 181
			midi_length = -1;
	}

182
	if (midi_length < 0) {
M
Markus Grabner 已提交
183
		/* search for end of message */
184
		if (length < length1) {
M
Markus Grabner 已提交
185
			/* no buffer wraparound */
186 187
			for (i = 1; i < length; ++i)
				if (this->buf[this->pos_read + i] & 0x80)
M
Markus Grabner 已提交
188 189 190
					break;

			midi_length = i;
191
		} else {
M
Markus Grabner 已提交
192 193 194
			/* buffer wraparound */
			length2 = length - length1;

195 196
			for (i = 1; i < length1; ++i)
				if (this->buf[this->pos_read + i] & 0x80)
M
Markus Grabner 已提交
197 198
					break;

199
			if (i < length1)
M
Markus Grabner 已提交
200 201
				midi_length = i;
			else {
202 203
				for (i = 0; i < length2; ++i)
					if (this->buf[i] & 0x80)
M
Markus Grabner 已提交
204 205 206 207 208 209
						break;

				midi_length = length1 + i;
			}
		}

210
		if (midi_length == length)
211
			midi_length = -1;	/* end of message not found */
M
Markus Grabner 已提交
212 213
	}

214 215
	if (midi_length < 0) {
		if (!this->split)
216
			return 0;	/* command is not yet complete */
217 218
	} else {
		if (length < midi_length)
219
			return 0;	/* command is not yet complete */
M
Markus Grabner 已提交
220 221 222 223

		length = midi_length;
	}

224
	if (length < length1) {
M
Markus Grabner 已提交
225 226 227
		/* no buffer wraparound */
		memcpy(data + repeat, this->buf + this->pos_read, length);
		this->pos_read += length;
228
	} else {
M
Markus Grabner 已提交
229 230 231 232 233 234 235
		/* buffer wraparound */
		length2 = length - length1;
		memcpy(data + repeat, this->buf + this->pos_read, length1);
		memcpy(data + repeat + length1, this->buf, length2);
		this->pos_read = length2;
	}

236
	if (repeat)
M
Markus Grabner 已提交
237 238 239 240 241 242
		data[0] = this->command_prev;

	this->full = 0;
	return length + repeat;
}

243
int line6_midibuf_ignore(struct midi_buffer *this, int length)
M
Markus Grabner 已提交
244
{
245
	int bytes_used = line6_midibuf_bytes_used(this);
M
Markus Grabner 已提交
246

247
	if (length > bytes_used)
M
Markus Grabner 已提交
248 249 250 251 252 253 254
		length = bytes_used;

	this->pos_read = (this->pos_read + length) % this->size;
	this->full = 0;
	return length;
}

255
int line6_midibuf_skip_message(struct midi_buffer *this, unsigned short mask)
M
Markus Grabner 已提交
256 257 258
{
	int cmd = this->command_prev;

259 260
	if ((cmd >= 0x80) && (cmd < 0xf0))
		if ((mask & (1 << (cmd & 0x0f))) == 0)
M
Markus Grabner 已提交
261 262 263 264 265
			return 1;

	return 0;
}

266
void line6_midibuf_destroy(struct midi_buffer *this)
M
Markus Grabner 已提交
267
{
268 269
	kfree(this->buf);
	this->buf = NULL;
M
Markus Grabner 已提交
270
}