Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/usb/line6/midibuf.c
26425 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Line 6 Linux USB driver
4
*
5
* Copyright (C) 2004-2010 Markus Grabner ([email protected])
6
*/
7
8
#include <linux/slab.h>
9
10
#include "midibuf.h"
11
12
13
static int midibuf_message_length(unsigned char code)
14
{
15
int message_length;
16
17
if (code < 0x80)
18
message_length = -1;
19
else if (code < 0xf0) {
20
static const int length[] = { 3, 3, 3, 3, 2, 2, 3 };
21
22
message_length = length[(code >> 4) - 8];
23
} else {
24
static const int length[] = { -1, 2, 2, 2, -1, -1, 1, 1, 1, -1,
25
1, 1, 1, -1, 1, 1
26
};
27
message_length = length[code & 0x0f];
28
}
29
30
return message_length;
31
}
32
33
static int midibuf_is_empty(struct midi_buffer *this)
34
{
35
return (this->pos_read == this->pos_write) && !this->full;
36
}
37
38
static int midibuf_is_full(struct midi_buffer *this)
39
{
40
return this->full;
41
}
42
43
void line6_midibuf_reset(struct midi_buffer *this)
44
{
45
this->pos_read = this->pos_write = this->full = 0;
46
this->command_prev = -1;
47
}
48
49
int line6_midibuf_init(struct midi_buffer *this, int size, int split)
50
{
51
this->buf = kmalloc(size, GFP_KERNEL);
52
53
if (this->buf == NULL)
54
return -ENOMEM;
55
56
this->size = size;
57
this->split = split;
58
line6_midibuf_reset(this);
59
return 0;
60
}
61
62
int line6_midibuf_bytes_free(struct midi_buffer *this)
63
{
64
return
65
midibuf_is_full(this) ?
66
0 :
67
(this->pos_read - this->pos_write + this->size - 1) % this->size +
68
1;
69
}
70
71
int line6_midibuf_bytes_used(struct midi_buffer *this)
72
{
73
return
74
midibuf_is_empty(this) ?
75
0 :
76
(this->pos_write - this->pos_read + this->size - 1) % this->size +
77
1;
78
}
79
80
int line6_midibuf_write(struct midi_buffer *this, unsigned char *data,
81
int length)
82
{
83
int bytes_free;
84
int length1, length2;
85
int skip_active_sense = 0;
86
87
if (midibuf_is_full(this) || (length <= 0))
88
return 0;
89
90
/* skip trailing active sense */
91
if (data[length - 1] == 0xfe) {
92
--length;
93
skip_active_sense = 1;
94
}
95
96
bytes_free = line6_midibuf_bytes_free(this);
97
98
if (length > bytes_free)
99
length = bytes_free;
100
101
if (length > 0) {
102
length1 = this->size - this->pos_write;
103
104
if (length < length1) {
105
/* no buffer wraparound */
106
memcpy(this->buf + this->pos_write, data, length);
107
this->pos_write += length;
108
} else {
109
/* buffer wraparound */
110
length2 = length - length1;
111
memcpy(this->buf + this->pos_write, data, length1);
112
memcpy(this->buf, data + length1, length2);
113
this->pos_write = length2;
114
}
115
116
if (this->pos_write == this->pos_read)
117
this->full = 1;
118
}
119
120
return length + skip_active_sense;
121
}
122
123
int line6_midibuf_read(struct midi_buffer *this, unsigned char *data,
124
int length, int read_type)
125
{
126
int bytes_used;
127
int length1, length2;
128
int command;
129
int midi_length;
130
int repeat = 0;
131
int i;
132
133
/* we need to be able to store at least a 3 byte MIDI message */
134
if (length < 3)
135
return -EINVAL;
136
137
if (midibuf_is_empty(this))
138
return 0;
139
140
bytes_used = line6_midibuf_bytes_used(this);
141
142
if (length > bytes_used)
143
length = bytes_used;
144
145
length1 = this->size - this->pos_read;
146
147
command = this->buf[this->pos_read];
148
/*
149
PODxt always has status byte lower nibble set to 0010,
150
when it means to send 0000, so we correct if here so
151
that control/program changes come on channel 1 and
152
sysex message status byte is correct
153
*/
154
if (read_type == LINE6_MIDIBUF_READ_RX) {
155
if (command == 0xb2 || command == 0xc2 || command == 0xf2) {
156
unsigned char fixed = command & 0xf0;
157
this->buf[this->pos_read] = fixed;
158
command = fixed;
159
}
160
}
161
162
/* check MIDI command length */
163
if (command & 0x80) {
164
midi_length = midibuf_message_length(command);
165
this->command_prev = command;
166
} else {
167
if (this->command_prev > 0) {
168
int midi_length_prev =
169
midibuf_message_length(this->command_prev);
170
171
if (midi_length_prev > 1) {
172
midi_length = midi_length_prev - 1;
173
repeat = 1;
174
} else
175
midi_length = -1;
176
} else
177
midi_length = -1;
178
}
179
180
if (midi_length < 0) {
181
/* search for end of message */
182
if (length < length1) {
183
/* no buffer wraparound */
184
for (i = 1; i < length; ++i)
185
if (this->buf[this->pos_read + i] & 0x80)
186
break;
187
188
midi_length = i;
189
} else {
190
/* buffer wraparound */
191
length2 = length - length1;
192
193
for (i = 1; i < length1; ++i)
194
if (this->buf[this->pos_read + i] & 0x80)
195
break;
196
197
if (i < length1)
198
midi_length = i;
199
else {
200
for (i = 0; i < length2; ++i)
201
if (this->buf[i] & 0x80)
202
break;
203
204
midi_length = length1 + i;
205
}
206
}
207
208
if (midi_length == length)
209
midi_length = -1; /* end of message not found */
210
}
211
212
if (midi_length < 0) {
213
if (!this->split)
214
return 0; /* command is not yet complete */
215
} else {
216
if (length < midi_length)
217
return 0; /* command is not yet complete */
218
219
length = midi_length;
220
}
221
222
if (length < length1) {
223
/* no buffer wraparound */
224
memcpy(data + repeat, this->buf + this->pos_read, length);
225
this->pos_read += length;
226
} else {
227
/* buffer wraparound */
228
length2 = length - length1;
229
memcpy(data + repeat, this->buf + this->pos_read, length1);
230
memcpy(data + repeat + length1, this->buf, length2);
231
this->pos_read = length2;
232
}
233
234
if (repeat)
235
data[0] = this->command_prev;
236
237
this->full = 0;
238
return length + repeat;
239
}
240
241
int line6_midibuf_ignore(struct midi_buffer *this, int length)
242
{
243
int bytes_used = line6_midibuf_bytes_used(this);
244
245
if (length > bytes_used)
246
length = bytes_used;
247
248
this->pos_read = (this->pos_read + length) % this->size;
249
this->full = 0;
250
return length;
251
}
252
253
void line6_midibuf_destroy(struct midi_buffer *this)
254
{
255
kfree(this->buf);
256
this->buf = NULL;
257
}
258
259