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