midibuf.c 5.1 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0-only
M
Markus Grabner 已提交
2
/*
3
 * Line 6 Linux USB driver
M
Markus Grabner 已提交
4
 *
5
 * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
M
Markus Grabner 已提交
6 7 8 9 10 11
 */

#include <linux/slab.h>

#include "midibuf.h"

12
static int midibuf_message_length(unsigned char code)
M
Markus Grabner 已提交
13
{
14 15
	int message_length;

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

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

	return message_length;
M
Markus Grabner 已提交
35 36
}

37
static int midibuf_is_empty(struct midi_buffer *this)
38 39 40 41
{
	return (this->pos_read == this->pos_write) && !this->full;
}

42
static int midibuf_is_full(struct midi_buffer *this)
43 44 45 46
{
	return this->full;
}

47
void line6_midibuf_reset(struct midi_buffer *this)
M
Markus Grabner 已提交
48 49 50 51 52
{
	this->pos_read = this->pos_write = this->full = 0;
	this->command_prev = -1;
}

53
int line6_midibuf_init(struct midi_buffer *this, int size, int split)
M
Markus Grabner 已提交
54
{
55
	this->buf = kmalloc(size, GFP_KERNEL);
M
Markus Grabner 已提交
56

57
	if (this->buf == NULL)
M
Markus Grabner 已提交
58 59 60 61
		return -ENOMEM;

	this->size = size;
	this->split = split;
62
	line6_midibuf_reset(this);
M
Markus Grabner 已提交
63 64 65
	return 0;
}

66
int line6_midibuf_bytes_free(struct midi_buffer *this)
M
Markus Grabner 已提交
67 68
{
	return
69 70 71 72
	    midibuf_is_full(this) ?
	    0 :
	    (this->pos_read - this->pos_write + this->size - 1) % this->size +
	    1;
M
Markus Grabner 已提交
73 74
}

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

84
int line6_midibuf_write(struct midi_buffer *this, unsigned char *data,
85
			int length)
M
Markus Grabner 已提交
86 87 88 89 90
{
	int bytes_free;
	int length1, length2;
	int skip_active_sense = 0;

91
	if (midibuf_is_full(this) || (length <= 0))
M
Markus Grabner 已提交
92 93 94
		return 0;

	/* skip trailing active sense */
95
	if (data[length - 1] == 0xfe) {
M
Markus Grabner 已提交
96 97 98 99
		--length;
		skip_active_sense = 1;
	}

100
	bytes_free = line6_midibuf_bytes_free(this);
M
Markus Grabner 已提交
101

102
	if (length > bytes_free)
M
Markus Grabner 已提交
103 104
		length = bytes_free;

105
	if (length > 0) {
M
Markus Grabner 已提交
106 107
		length1 = this->size - this->pos_write;

108
		if (length < length1) {
M
Markus Grabner 已提交
109 110 111
			/* no buffer wraparound */
			memcpy(this->buf + this->pos_write, data, length);
			this->pos_write += length;
112
		} else {
M
Markus Grabner 已提交
113 114 115 116 117 118 119
			/* buffer wraparound */
			length2 = length - length1;
			memcpy(this->buf + this->pos_write, data, length1);
			memcpy(this->buf, data + length1, length2);
			this->pos_write = length2;
		}

120
		if (this->pos_write == this->pos_read)
M
Markus Grabner 已提交
121 122 123 124 125 126
			this->full = 1;
	}

	return length + skip_active_sense;
}

127 128
int line6_midibuf_read(struct midi_buffer *this, unsigned char *data,
		       int length)
M
Markus Grabner 已提交
129 130 131 132 133 134 135 136
{
	int bytes_used;
	int length1, length2;
	int command;
	int midi_length;
	int repeat = 0;
	int i;

137 138 139
	/* we need to be able to store at least a 3 byte MIDI message */
	if (length < 3)
		return -EINVAL;
M
Markus Grabner 已提交
140

141
	if (midibuf_is_empty(this))
M
Markus Grabner 已提交
142 143
		return 0;

144
	bytes_used = line6_midibuf_bytes_used(this);
M
Markus Grabner 已提交
145

146
	if (length > bytes_used)
M
Markus Grabner 已提交
147 148 149 150 151 152 153
		length = bytes_used;

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

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

154
	if (command & 0x80) {
M
Markus Grabner 已提交
155 156
		midi_length = midibuf_message_length(command);
		this->command_prev = command;
157 158
	} else {
		if (this->command_prev > 0) {
159 160
			int midi_length_prev =
			    midibuf_message_length(this->command_prev);
M
Markus Grabner 已提交
161

162
			if (midi_length_prev > 1) {
M
Markus Grabner 已提交
163 164
				midi_length = midi_length_prev - 1;
				repeat = 1;
165
			} else
M
Markus Grabner 已提交
166
				midi_length = -1;
167
		} else
M
Markus Grabner 已提交
168 169 170
			midi_length = -1;
	}

171
	if (midi_length < 0) {
M
Markus Grabner 已提交
172
		/* search for end of message */
173
		if (length < length1) {
M
Markus Grabner 已提交
174
			/* no buffer wraparound */
175 176
			for (i = 1; i < length; ++i)
				if (this->buf[this->pos_read + i] & 0x80)
M
Markus Grabner 已提交
177 178 179
					break;

			midi_length = i;
180
		} else {
M
Markus Grabner 已提交
181 182 183
			/* buffer wraparound */
			length2 = length - length1;

184 185
			for (i = 1; i < length1; ++i)
				if (this->buf[this->pos_read + i] & 0x80)
M
Markus Grabner 已提交
186 187
					break;

188
			if (i < length1)
M
Markus Grabner 已提交
189 190
				midi_length = i;
			else {
191 192
				for (i = 0; i < length2; ++i)
					if (this->buf[i] & 0x80)
M
Markus Grabner 已提交
193 194 195 196 197 198
						break;

				midi_length = length1 + i;
			}
		}

199
		if (midi_length == length)
200
			midi_length = -1;	/* end of message not found */
M
Markus Grabner 已提交
201 202
	}

203 204
	if (midi_length < 0) {
		if (!this->split)
205
			return 0;	/* command is not yet complete */
206 207
	} else {
		if (length < midi_length)
208
			return 0;	/* command is not yet complete */
M
Markus Grabner 已提交
209 210 211 212

		length = midi_length;
	}

213
	if (length < length1) {
M
Markus Grabner 已提交
214 215 216
		/* no buffer wraparound */
		memcpy(data + repeat, this->buf + this->pos_read, length);
		this->pos_read += length;
217
	} else {
M
Markus Grabner 已提交
218 219 220 221 222 223 224
		/* 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;
	}

225
	if (repeat)
M
Markus Grabner 已提交
226 227 228 229 230 231
		data[0] = this->command_prev;

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

232
int line6_midibuf_ignore(struct midi_buffer *this, int length)
M
Markus Grabner 已提交
233
{
234
	int bytes_used = line6_midibuf_bytes_used(this);
M
Markus Grabner 已提交
235

236
	if (length > bytes_used)
M
Markus Grabner 已提交
237 238 239 240 241 242 243
		length = bytes_used;

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

244
void line6_midibuf_destroy(struct midi_buffer *this)
M
Markus Grabner 已提交
245
{
246 247
	kfree(this->buf);
	this->buf = NULL;
M
Markus Grabner 已提交
248
}