blob: ab0a5f30fbca895da03672f8efbd3d1c3faa150f [file] [log] [blame]
Markus Grabner705ecec2009-02-27 19:43:04 -08001/*
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
Greg Kroah-Hartmanb702ed252009-02-27 20:45:03 -080019static int midibuf_message_length(unsigned char code)
Markus Grabner705ecec2009-02-27 19:43:04 -080020{
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -080021 if (code < 0x80)
Markus Grabner705ecec2009-02-27 19:43:04 -080022 return -1;
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -080023 else if (code < 0xf0) {
Markus Grabner705ecec2009-02-27 19:43:04 -080024 static const int length[] = { 3, 3, 3, 3, 2, 2, 3 };
25 return length[(code >> 4) - 8];
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -080026 } else {
Markus Grabner705ecec2009-02-27 19:43:04 -080027 /*
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -080028 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.
Markus Grabner705ecec2009-02-27 19:43:04 -080031 */
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -080032 static const int length[] = { -1, 2, -1, 2, -1, -1, 1, 1, 1, 1,
33 1, 1, 1, -1, 1, 1 };
Markus Grabner705ecec2009-02-27 19:43:04 -080034 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{
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -080046 this->buf = kmalloc(size, GFP_KERNEL);
Markus Grabner705ecec2009-02-27 19:43:04 -080047
Greg Kroah-Hartman536165d2009-02-27 20:49:46 -080048 if (this->buf == NULL)
Markus Grabner705ecec2009-02-27 19:43:04 -080049 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{
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -080059 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);
Markus Grabner705ecec2009-02-27 19:43:04 -080062}
63
Greg Kroah-Hartmanb702ed252009-02-27 20:45:03 -080064static int midibuf_is_empty(struct MidiBuffer *this)
Markus Grabner705ecec2009-02-27 19:43:04 -080065{
66 return (this->pos_read == this->pos_write) && !this->full;
67}
68
Greg Kroah-Hartmanb702ed252009-02-27 20:45:03 -080069static int midibuf_is_full(struct MidiBuffer *this)
Markus Grabner705ecec2009-02-27 19:43:04 -080070{
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
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -080096 if (midibuf_is_full(this) || (length <= 0))
Markus Grabner705ecec2009-02-27 19:43:04 -080097 return 0;
98
99 /* skip trailing active sense */
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800100 if (data[length - 1] == 0xfe) {
Markus Grabner705ecec2009-02-27 19:43:04 -0800101 --length;
102 skip_active_sense = 1;
103 }
104
105 bytes_free = midibuf_bytes_free(this);
106
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800107 if (length > bytes_free)
Markus Grabner705ecec2009-02-27 19:43:04 -0800108 length = bytes_free;
109
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800110 if (length > 0) {
Markus Grabner705ecec2009-02-27 19:43:04 -0800111 length1 = this->size - this->pos_write;
112
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800113 if (length < length1) {
Markus Grabner705ecec2009-02-27 19:43:04 -0800114 /* no buffer wraparound */
115 memcpy(this->buf + this->pos_write, data, length);
116 this->pos_write += length;
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800117 } else {
Markus Grabner705ecec2009-02-27 19:43:04 -0800118 /* 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
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800125 if (this->pos_write == this->pos_read)
Markus Grabner705ecec2009-02-27 19:43:04 -0800126 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
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800141 /* we need to be able to store at least a 3 byte MIDI message */
142 if (length < 3)
143 return -EINVAL;
Markus Grabner705ecec2009-02-27 19:43:04 -0800144
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800145 if (midibuf_is_empty(this))
Markus Grabner705ecec2009-02-27 19:43:04 -0800146 return 0;
147
148 bytes_used = midibuf_bytes_used(this);
149
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800150 if (length > bytes_used)
Markus Grabner705ecec2009-02-27 19:43:04 -0800151 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
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800158 if (command & 0x80) {
Markus Grabner705ecec2009-02-27 19:43:04 -0800159 midi_length = midibuf_message_length(command);
160 this->command_prev = command;
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800161 } else {
162 if (this->command_prev > 0) {
Markus Grabner705ecec2009-02-27 19:43:04 -0800163 int midi_length_prev = midibuf_message_length(this->command_prev);
164
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800165 if (midi_length_prev > 0) {
Markus Grabner705ecec2009-02-27 19:43:04 -0800166 midi_length = midi_length_prev - 1;
167 repeat = 1;
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800168 } else
Markus Grabner705ecec2009-02-27 19:43:04 -0800169 midi_length = -1;
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800170 } else
Markus Grabner705ecec2009-02-27 19:43:04 -0800171 midi_length = -1;
172 }
173
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800174 if (midi_length < 0) {
Markus Grabner705ecec2009-02-27 19:43:04 -0800175 /* search for end of message */
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800176 if (length < length1) {
Markus Grabner705ecec2009-02-27 19:43:04 -0800177 /* no buffer wraparound */
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800178 for (i = 1; i < length; ++i)
179 if (this->buf[this->pos_read + i] & 0x80)
Markus Grabner705ecec2009-02-27 19:43:04 -0800180 break;
181
182 midi_length = i;
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800183 } else {
Markus Grabner705ecec2009-02-27 19:43:04 -0800184 /* buffer wraparound */
185 length2 = length - length1;
186
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800187 for (i = 1; i < length1; ++i)
188 if (this->buf[this->pos_read + i] & 0x80)
Markus Grabner705ecec2009-02-27 19:43:04 -0800189 break;
190
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800191 if (i < length1)
Markus Grabner705ecec2009-02-27 19:43:04 -0800192 midi_length = i;
193 else {
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800194 for (i = 0; i < length2; ++i)
195 if (this->buf[i] & 0x80)
Markus Grabner705ecec2009-02-27 19:43:04 -0800196 break;
197
198 midi_length = length1 + i;
199 }
200 }
201
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800202 if (midi_length == length)
Markus Grabner705ecec2009-02-27 19:43:04 -0800203 midi_length = -1; /* end of message not found */
204 }
205
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800206 if (midi_length < 0) {
207 if (!this->split)
Markus Grabner705ecec2009-02-27 19:43:04 -0800208 return 0; /* command is not yet complete */
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800209 } else {
210 if (length < midi_length)
Markus Grabner705ecec2009-02-27 19:43:04 -0800211 return 0; /* command is not yet complete */
212
213 length = midi_length;
214 }
215
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800216 if (length < length1) {
Markus Grabner705ecec2009-02-27 19:43:04 -0800217 /* no buffer wraparound */
218 memcpy(data + repeat, this->buf + this->pos_read, length);
219 this->pos_read += length;
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800220 } else {
Markus Grabner705ecec2009-02-27 19:43:04 -0800221 /* 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
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800228 if (repeat)
Markus Grabner705ecec2009-02-27 19:43:04 -0800229 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
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800239 if (length > bytes_used)
Markus Grabner705ecec2009-02-27 19:43:04 -0800240 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
Greg Kroah-Hartmance9b4902009-02-27 22:43:11 -0800251 if ((cmd >= 0x80) && (cmd < 0xf0))
252 if ((mask & (1 << (cmd & 0x0f))) == 0)
Markus Grabner705ecec2009-02-27 19:43:04 -0800253 return 1;
254
255 return 0;
256}
257
258void midibuf_destroy(struct MidiBuffer *this)
259{
Greg Kroah-Hartman536165d2009-02-27 20:49:46 -0800260 kfree(this->buf);
261 this->buf = NULL;
Markus Grabner705ecec2009-02-27 19:43:04 -0800262}