Commit | Line | Data |
---|---|---|
a10e763b | 1 | // SPDX-License-Identifier: GPL-2.0-only |
705ececd | 2 | /* |
c078a4aa | 3 | * Line 6 Linux USB driver |
705ececd | 4 | * |
1027f476 | 5 | * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) |
705ececd MG |
6 | */ |
7 | ||
705ececd MG |
8 | #include <linux/slab.h> |
9 | ||
10 | #include "midibuf.h" | |
11 | ||
b702ed25 | 12 | static int midibuf_message_length(unsigned char code) |
705ececd | 13 | { |
d1d1a9d3 DT |
14 | int message_length; |
15 | ||
ce9b490c | 16 | if (code < 0x80) |
d1d1a9d3 | 17 | message_length = -1; |
ce9b490c | 18 | else if (code < 0xf0) { |
705ececd | 19 | static const int length[] = { 3, 3, 3, 3, 2, 2, 3 }; |
d1d1a9d3 DT |
20 | |
21 | message_length = length[(code >> 4) - 8]; | |
ce9b490c | 22 | } else { |
705ececd | 23 | /* |
e1a164d7 | 24 | Note that according to the MIDI specification 0xf2 is |
c6fffce9 | 25 | the "Song Position Pointer", but this is used by Line 6 |
e1a164d7 MG |
26 | to send sysex messages to the host. |
27 | */ | |
ce9b490c | 28 | static const int length[] = { -1, 2, -1, 2, -1, -1, 1, 1, 1, 1, |
e1a164d7 MG |
29 | 1, 1, 1, -1, 1, 1 |
30 | }; | |
d1d1a9d3 | 31 | message_length = length[code & 0x0f]; |
705ececd | 32 | } |
d1d1a9d3 DT |
33 | |
34 | return message_length; | |
705ececd MG |
35 | } |
36 | ||
269edc8e | 37 | static int midibuf_is_empty(struct midi_buffer *this) |
1027f476 MG |
38 | { |
39 | return (this->pos_read == this->pos_write) && !this->full; | |
40 | } | |
41 | ||
269edc8e | 42 | static int midibuf_is_full(struct midi_buffer *this) |
1027f476 MG |
43 | { |
44 | return this->full; | |
45 | } | |
46 | ||
269edc8e | 47 | void line6_midibuf_reset(struct midi_buffer *this) |
705ececd MG |
48 | { |
49 | this->pos_read = this->pos_write = this->full = 0; | |
50 | this->command_prev = -1; | |
51 | } | |
52 | ||
269edc8e | 53 | int line6_midibuf_init(struct midi_buffer *this, int size, int split) |
705ececd | 54 | { |
ce9b490c | 55 | this->buf = kmalloc(size, GFP_KERNEL); |
705ececd | 56 | |
536165d8 | 57 | if (this->buf == NULL) |
705ececd MG |
58 | return -ENOMEM; |
59 | ||
60 | this->size = size; | |
61 | this->split = split; | |
1027f476 | 62 | line6_midibuf_reset(this); |
705ececd MG |
63 | return 0; |
64 | } | |
705ececd | 65 | |
269edc8e | 66 | int line6_midibuf_bytes_free(struct midi_buffer *this) |
705ececd MG |
67 | { |
68 | return | |
e1a164d7 MG |
69 | midibuf_is_full(this) ? |
70 | 0 : | |
71 | (this->pos_read - this->pos_write + this->size - 1) % this->size + | |
72 | 1; | |
705ececd MG |
73 | } |
74 | ||
269edc8e | 75 | int line6_midibuf_bytes_used(struct midi_buffer *this) |
705ececd MG |
76 | { |
77 | return | |
e1a164d7 MG |
78 | midibuf_is_empty(this) ? |
79 | 0 : | |
80 | (this->pos_write - this->pos_read + this->size - 1) % this->size + | |
81 | 1; | |
705ececd MG |
82 | } |
83 | ||
269edc8e | 84 | int line6_midibuf_write(struct midi_buffer *this, unsigned char *data, |
e1a164d7 | 85 | int length) |
705ececd MG |
86 | { |
87 | int bytes_free; | |
88 | int length1, length2; | |
89 | int skip_active_sense = 0; | |
90 | ||
ce9b490c | 91 | if (midibuf_is_full(this) || (length <= 0)) |
705ececd MG |
92 | return 0; |
93 | ||
94 | /* skip trailing active sense */ | |
ce9b490c | 95 | if (data[length - 1] == 0xfe) { |
705ececd MG |
96 | --length; |
97 | skip_active_sense = 1; | |
98 | } | |
99 | ||
1027f476 | 100 | bytes_free = line6_midibuf_bytes_free(this); |
705ececd | 101 | |
ce9b490c | 102 | if (length > bytes_free) |
705ececd MG |
103 | length = bytes_free; |
104 | ||
ce9b490c | 105 | if (length > 0) { |
705ececd MG |
106 | length1 = this->size - this->pos_write; |
107 | ||
ce9b490c | 108 | if (length < length1) { |
705ececd MG |
109 | /* no buffer wraparound */ |
110 | memcpy(this->buf + this->pos_write, data, length); | |
111 | this->pos_write += length; | |
ce9b490c | 112 | } else { |
705ececd MG |
113 | /* buffer wraparound */ |
114 | length2 = length - length1; | |
115 | memcpy(this->buf + this->pos_write, data, length1); | |
116 | memcpy(this->buf, data + length1, length2); | |
117 | this->pos_write = length2; | |
118 | } | |
119 | ||
ce9b490c | 120 | if (this->pos_write == this->pos_read) |
705ececd MG |
121 | this->full = 1; |
122 | } | |
123 | ||
124 | return length + skip_active_sense; | |
125 | } | |
126 | ||
269edc8e SH |
127 | int line6_midibuf_read(struct midi_buffer *this, unsigned char *data, |
128 | int length) | |
705ececd MG |
129 | { |
130 | int bytes_used; | |
131 | int length1, length2; | |
132 | int command; | |
133 | int midi_length; | |
134 | int repeat = 0; | |
135 | int i; | |
136 | ||
ce9b490c GKH |
137 | /* we need to be able to store at least a 3 byte MIDI message */ |
138 | if (length < 3) | |
139 | return -EINVAL; | |
705ececd | 140 | |
ce9b490c | 141 | if (midibuf_is_empty(this)) |
705ececd MG |
142 | return 0; |
143 | ||
1027f476 | 144 | bytes_used = line6_midibuf_bytes_used(this); |
705ececd | 145 | |
ce9b490c | 146 | if (length > bytes_used) |
705ececd MG |
147 | length = bytes_used; |
148 | ||
149 | length1 = this->size - this->pos_read; | |
150 | ||
151 | /* check MIDI command length */ | |
152 | command = this->buf[this->pos_read]; | |
153 | ||
ce9b490c | 154 | if (command & 0x80) { |
705ececd MG |
155 | midi_length = midibuf_message_length(command); |
156 | this->command_prev = command; | |
ce9b490c GKH |
157 | } else { |
158 | if (this->command_prev > 0) { | |
e1a164d7 MG |
159 | int midi_length_prev = |
160 | midibuf_message_length(this->command_prev); | |
705ececd | 161 | |
d683469b | 162 | if (midi_length_prev > 1) { |
705ececd MG |
163 | midi_length = midi_length_prev - 1; |
164 | repeat = 1; | |
ce9b490c | 165 | } else |
705ececd | 166 | midi_length = -1; |
ce9b490c | 167 | } else |
705ececd MG |
168 | midi_length = -1; |
169 | } | |
170 | ||
ce9b490c | 171 | if (midi_length < 0) { |
705ececd | 172 | /* search for end of message */ |
ce9b490c | 173 | if (length < length1) { |
705ececd | 174 | /* no buffer wraparound */ |
ce9b490c GKH |
175 | for (i = 1; i < length; ++i) |
176 | if (this->buf[this->pos_read + i] & 0x80) | |
705ececd MG |
177 | break; |
178 | ||
179 | midi_length = i; | |
ce9b490c | 180 | } else { |
705ececd MG |
181 | /* buffer wraparound */ |
182 | length2 = length - length1; | |
183 | ||
ce9b490c GKH |
184 | for (i = 1; i < length1; ++i) |
185 | if (this->buf[this->pos_read + i] & 0x80) | |
705ececd MG |
186 | break; |
187 | ||
ce9b490c | 188 | if (i < length1) |
705ececd MG |
189 | midi_length = i; |
190 | else { | |
ce9b490c GKH |
191 | for (i = 0; i < length2; ++i) |
192 | if (this->buf[i] & 0x80) | |
705ececd MG |
193 | break; |
194 | ||
195 | midi_length = length1 + i; | |
196 | } | |
197 | } | |
198 | ||
ce9b490c | 199 | if (midi_length == length) |
e1a164d7 | 200 | midi_length = -1; /* end of message not found */ |
705ececd MG |
201 | } |
202 | ||
ce9b490c GKH |
203 | if (midi_length < 0) { |
204 | if (!this->split) | |
e1a164d7 | 205 | return 0; /* command is not yet complete */ |
ce9b490c GKH |
206 | } else { |
207 | if (length < midi_length) | |
e1a164d7 | 208 | return 0; /* command is not yet complete */ |
705ececd MG |
209 | |
210 | length = midi_length; | |
211 | } | |
212 | ||
ce9b490c | 213 | if (length < length1) { |
705ececd MG |
214 | /* no buffer wraparound */ |
215 | memcpy(data + repeat, this->buf + this->pos_read, length); | |
216 | this->pos_read += length; | |
ce9b490c | 217 | } else { |
705ececd MG |
218 | /* buffer wraparound */ |
219 | length2 = length - length1; | |
220 | memcpy(data + repeat, this->buf + this->pos_read, length1); | |
221 | memcpy(data + repeat + length1, this->buf, length2); | |
222 | this->pos_read = length2; | |
223 | } | |
224 | ||
ce9b490c | 225 | if (repeat) |
705ececd MG |
226 | data[0] = this->command_prev; |
227 | ||
228 | this->full = 0; | |
229 | return length + repeat; | |
230 | } | |
231 | ||
269edc8e | 232 | int line6_midibuf_ignore(struct midi_buffer *this, int length) |
705ececd | 233 | { |
1027f476 | 234 | int bytes_used = line6_midibuf_bytes_used(this); |
705ececd | 235 | |
ce9b490c | 236 | if (length > bytes_used) |
705ececd MG |
237 | length = bytes_used; |
238 | ||
239 | this->pos_read = (this->pos_read + length) % this->size; | |
240 | this->full = 0; | |
241 | return length; | |
242 | } | |
243 | ||
269edc8e | 244 | void line6_midibuf_destroy(struct midi_buffer *this) |
705ececd | 245 | { |
536165d8 GKH |
246 | kfree(this->buf); |
247 | this->buf = NULL; | |
705ececd | 248 | } |