Root/drivers/staging/line6/midibuf.c

1/*
2 * Line6 Linux USB driver - 0.9.1beta
3 *
4 * Copyright (C) 2004-2010 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 <linux/slab.h>
13
14#include "midibuf.h"
15
16static int midibuf_message_length(unsigned char code)
17{
18    if (code < 0x80)
19        return -1;
20    else if (code < 0xf0) {
21        static const int length[] = { 3, 3, 3, 3, 2, 2, 3 };
22        return length[(code >> 4) - 8];
23    } else {
24        /*
25           Note that according to the MIDI specification 0xf2 is
26           the "Song Position Pointer", but this is used by Line6
27           to send sysex messages to the host.
28         */
29        static const int length[] = { -1, 2, -1, 2, -1, -1, 1, 1, 1, 1,
30            1, 1, 1, -1, 1, 1
31        };
32        return length[code & 0x0f];
33    }
34}
35
36static int midibuf_is_empty(struct MidiBuffer *this)
37{
38    return (this->pos_read == this->pos_write) && !this->full;
39}
40
41static int midibuf_is_full(struct MidiBuffer *this)
42{
43    return this->full;
44}
45
46void line6_midibuf_reset(struct MidiBuffer *this)
47{
48    this->pos_read = this->pos_write = this->full = 0;
49    this->command_prev = -1;
50}
51
52int line6_midibuf_init(struct MidiBuffer *this, int size, int split)
53{
54    this->buf = kmalloc(size, GFP_KERNEL);
55
56    if (this->buf == NULL)
57        return -ENOMEM;
58
59    this->size = size;
60    this->split = split;
61    line6_midibuf_reset(this);
62    return 0;
63}
64
65void line6_midibuf_status(struct MidiBuffer *this)
66{
67    pr_debug("midibuf size=%d split=%d pos_read=%d pos_write=%d "
68           "full=%d command_prev=%02x\n", this->size, this->split,
69           this->pos_read, this->pos_write, this->full, this->command_prev);
70}
71
72int line6_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 +
78        1;
79}
80
81int line6_midibuf_bytes_used(struct MidiBuffer *this)
82{
83    return
84        midibuf_is_empty(this) ?
85        0 :
86        (this->pos_write - this->pos_read + this->size - 1) % this->size +
87        1;
88}
89
90int line6_midibuf_write(struct MidiBuffer *this, unsigned char *data,
91            int length)
92{
93    int bytes_free;
94    int length1, length2;
95    int skip_active_sense = 0;
96
97    if (midibuf_is_full(this) || (length <= 0))
98        return 0;
99
100    /* skip trailing active sense */
101    if (data[length - 1] == 0xfe) {
102        --length;
103        skip_active_sense = 1;
104    }
105
106    bytes_free = line6_midibuf_bytes_free(this);
107
108    if (length > bytes_free)
109        length = bytes_free;
110
111    if (length > 0) {
112        length1 = this->size - this->pos_write;
113
114        if (length < length1) {
115            /* no buffer wraparound */
116            memcpy(this->buf + this->pos_write, data, length);
117            this->pos_write += length;
118        } else {
119            /* buffer wraparound */
120            length2 = length - length1;
121            memcpy(this->buf + this->pos_write, data, length1);
122            memcpy(this->buf, data + length1, length2);
123            this->pos_write = length2;
124        }
125
126        if (this->pos_write == this->pos_read)
127            this->full = 1;
128    }
129
130    return length + skip_active_sense;
131}
132
133int line6_midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
134{
135    int bytes_used;
136    int length1, length2;
137    int command;
138    int midi_length;
139    int repeat = 0;
140    int i;
141
142    /* we need to be able to store at least a 3 byte MIDI message */
143    if (length < 3)
144        return -EINVAL;
145
146    if (midibuf_is_empty(this))
147        return 0;
148
149    bytes_used = line6_midibuf_bytes_used(this);
150
151    if (length > bytes_used)
152        length = bytes_used;
153
154    length1 = this->size - this->pos_read;
155
156    /* check MIDI command length */
157    command = this->buf[this->pos_read];
158
159    if (command & 0x80) {
160        midi_length = midibuf_message_length(command);
161        this->command_prev = command;
162    } else {
163        if (this->command_prev > 0) {
164            int midi_length_prev =
165                midibuf_message_length(this->command_prev);
166
167            if (midi_length_prev > 0) {
168                midi_length = midi_length_prev - 1;
169                repeat = 1;
170            } else
171                midi_length = -1;
172        } else
173            midi_length = -1;
174    }
175
176    if (midi_length < 0) {
177        /* search for end of message */
178        if (length < length1) {
179            /* no buffer wraparound */
180            for (i = 1; i < length; ++i)
181                if (this->buf[this->pos_read + i] & 0x80)
182                    break;
183
184            midi_length = i;
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    } else {
212        if (length < midi_length)
213            return 0; /* command is not yet complete */
214
215        length = midi_length;
216    }
217
218    if (length < length1) {
219        /* no buffer wraparound */
220        memcpy(data + repeat, this->buf + this->pos_read, length);
221        this->pos_read += length;
222    } else {
223        /* buffer wraparound */
224        length2 = length - length1;
225        memcpy(data + repeat, this->buf + this->pos_read, length1);
226        memcpy(data + repeat + length1, this->buf, length2);
227        this->pos_read = length2;
228    }
229
230    if (repeat)
231        data[0] = this->command_prev;
232
233    this->full = 0;
234    return length + repeat;
235}
236
237int line6_midibuf_ignore(struct MidiBuffer *this, int length)
238{
239    int bytes_used = line6_midibuf_bytes_used(this);
240
241    if (length > bytes_used)
242        length = bytes_used;
243
244    this->pos_read = (this->pos_read + length) % this->size;
245    this->full = 0;
246    return length;
247}
248
249int line6_midibuf_skip_message(struct MidiBuffer *this, unsigned short mask)
250{
251    int cmd = this->command_prev;
252
253    if ((cmd >= 0x80) && (cmd < 0xf0))
254        if ((mask & (1 << (cmd & 0x0f))) == 0)
255            return 1;
256
257    return 0;
258}
259
260void line6_midibuf_destroy(struct MidiBuffer *this)
261{
262    kfree(this->buf);
263    this->buf = NULL;
264}
265

Archive Download this file



interactive