]> git.karo-electronics.de Git - mv-sheeva.git/blob - drivers/staging/line6/midibuf.c
Staging: add line6 usb driver
[mv-sheeva.git] / drivers / staging / line6 / midibuf.c
1 /*
2  * Line6 Linux USB driver - 0.8.0
3  *
4  * Copyright (C) 2004-2009 Markus Grabner (grabner@icg.tugraz.at)
5  *
6  *      This program is free software; you can redistribute it and/or
7  *      modify it under the terms of the GNU General Public License as
8  *      published by the Free Software Foundation, version 2.
9  *
10  */
11
12 #include "config.h"
13
14 #include <linux/slab.h>
15
16 #include "midibuf.h"
17
18
19 int midibuf_message_length(unsigned char code)
20 {
21         if(code < 0x80)
22                 return -1;
23         else if(code < 0xf0) {
24                 static const int length[] = { 3, 3, 3, 3, 2, 2, 3 };
25                 return length[(code >> 4) - 8];
26         }
27         else {
28                 /*
29                         Note that according to the MIDI specification 0xf2 is the "Song Position
30                         Pointer", but this is used by Line6 to send sysex messages to the host.
31                 */
32                 static const int length[] = { -1, 2, -1, 2, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1 };
33                 return length[code & 0x0f];
34         }
35 }
36
37 void midibuf_reset(struct MidiBuffer *this)
38 {
39         this->pos_read = this->pos_write = this->full = 0;
40         this->command_prev = -1;
41 }
42
43 int midibuf_init(struct MidiBuffer *this, int size, int split)
44 {
45         this->buf = (unsigned char *)kmalloc(size, GFP_KERNEL);
46
47         if(this->buf == 0)
48                 return -ENOMEM;
49
50         this->size = size;
51         this->split = split;
52         midibuf_reset(this);
53         return 0;
54 }
55
56 void midibuf_status(struct MidiBuffer *this)
57 {
58         printk("midibuf size=%d split=%d pos_read=%d pos_write=%d full=%d command_prev=%02x\n",
59                                  this->size, this->split, this->pos_read, this->pos_write, this->full, this->command_prev);
60 }
61
62 int midibuf_is_empty(struct MidiBuffer *this)
63 {
64         return (this->pos_read == this->pos_write) && !this->full;
65 }
66
67 int midibuf_is_full(struct MidiBuffer *this)
68 {
69         return this->full;
70 }
71
72 int midibuf_bytes_free(struct MidiBuffer *this)
73 {
74         return
75                 midibuf_is_full(this) ?
76                 0 :
77                 (this->pos_read - this->pos_write + this->size - 1) % this->size + 1;
78 }
79
80 int midibuf_bytes_used(struct MidiBuffer *this)
81 {
82         return
83                 midibuf_is_empty(this) ?
84                 0 :
85                 (this->pos_write - this->pos_read + this->size - 1) % this->size + 1;
86 }
87
88 int midibuf_write(struct MidiBuffer *this, unsigned char *data, int length)
89 {
90         int bytes_free;
91         int length1, length2;
92         int skip_active_sense = 0;
93
94         if(midibuf_is_full(this) || (length <= 0))
95                 return 0;
96
97         /* skip trailing active sense */
98         if(data[length - 1] == 0xfe) {
99                 --length;
100                 skip_active_sense = 1;
101         }
102
103         bytes_free = midibuf_bytes_free(this);
104
105         if(length > bytes_free)
106                 length = bytes_free;
107
108         if(length > 0) {
109                 length1 = this->size - this->pos_write;
110
111                 if(length < length1) {
112                         /* no buffer wraparound */
113                         memcpy(this->buf + this->pos_write, data, length);
114                         this->pos_write += length;
115                 }
116                 else {
117                         /* buffer wraparound */
118                         length2 = length - length1;
119                         memcpy(this->buf + this->pos_write, data, length1);
120                         memcpy(this->buf, data + length1, length2);
121                         this->pos_write = length2;
122                 }
123
124                 if(this->pos_write == this->pos_read)
125                         this->full = 1;
126         }
127
128         return length + skip_active_sense;
129 }
130
131 int midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
132 {
133         int bytes_used;
134         int length1, length2;
135         int command;
136         int midi_length;
137         int repeat = 0;
138         int i;
139
140         if(length < 3)
141                 return -EINVAL;  /* we need to be able to store at least a 3 byte MIDI message */
142
143         if(midibuf_is_empty(this))
144                 return 0;
145
146         bytes_used = midibuf_bytes_used(this);
147
148         if(length > bytes_used)
149                 length = bytes_used;
150
151         length1 = this->size - this->pos_read;
152
153         /* check MIDI command length */
154         command = this->buf[this->pos_read];
155
156         if(command & 0x80) {
157                 midi_length = midibuf_message_length(command);
158                 this->command_prev = command;
159         }
160         else {
161                 if(this->command_prev > 0) {
162                         int midi_length_prev = midibuf_message_length(this->command_prev);
163
164                         if(midi_length_prev > 0) {
165                                 midi_length = midi_length_prev - 1;
166                                 repeat = 1;
167                         }
168                         else
169                                 midi_length = -1;
170                 }
171                 else
172                         midi_length = -1;
173         }
174
175         if(midi_length < 0) {
176                 /* search for end of message */
177                 if(length < length1) {
178                         /* no buffer wraparound */
179                         for(i = 1; i < length; ++i)
180                                 if(this->buf[this->pos_read + i] & 0x80)
181                                         break;
182
183                         midi_length = i;
184                 }
185                 else {
186                         /* buffer wraparound */
187                         length2 = length - length1;
188
189                         for(i = 1; i < length1; ++i)
190                                 if(this->buf[this->pos_read + i] & 0x80)
191                                         break;
192
193                         if(i < length1)
194                                 midi_length = i;
195                         else {
196                                 for(i = 0; i < length2; ++i)
197                                         if(this->buf[i] & 0x80)
198                                                 break;
199
200                                 midi_length = length1 + i;
201                         }
202                 }
203
204                 if(midi_length == length)
205                         midi_length = -1;  /* end of message not found */
206         }
207
208         if(midi_length < 0) {
209                 if(!this->split)
210                         return 0;  /* command is not yet complete */
211         }
212         else {
213                 if(length < midi_length)
214                         return 0;  /* command is not yet complete */
215
216                 length = midi_length;
217         }
218
219         if(length < length1) {
220                 /* no buffer wraparound */
221                 memcpy(data + repeat, this->buf + this->pos_read, length);
222                 this->pos_read += length;
223         }
224         else {
225                 /* buffer wraparound */
226                 length2 = length - length1;
227                 memcpy(data + repeat, this->buf + this->pos_read, length1);
228                 memcpy(data + repeat + length1, this->buf, length2);
229                 this->pos_read = length2;
230         }
231
232         if(repeat)
233                 data[0] = this->command_prev;
234
235         this->full = 0;
236         return length + repeat;
237 }
238
239 int midibuf_ignore(struct MidiBuffer *this, int length)
240 {
241         int bytes_used = midibuf_bytes_used(this);
242
243         if(length > bytes_used)
244                 length = bytes_used;
245
246         this->pos_read = (this->pos_read + length) % this->size;
247         this->full = 0;
248         return length;
249 }
250
251 int midibuf_skip_message(struct MidiBuffer *this, unsigned short mask)
252 {
253         int cmd = this->command_prev;
254
255         if((cmd >= 0x80) && (cmd < 0xf0))
256                 if((mask & (1 << (cmd & 0x0f))) == 0)
257                         return 1;
258
259         return 0;
260 }
261
262 void midibuf_destroy(struct MidiBuffer *this)
263 {
264         if(this->buf != 0) {
265                 kfree(this->buf);
266                 this->buf = 0;
267         }
268 }