staging: comedi: 8255_pci: fix possible NULL deref during detach
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / staging / line6 / midibuf.c
CommitLineData
705ececd 1/*
e1a164d7 2 * Line6 Linux USB driver - 0.9.1beta
705ececd 3 *
1027f476 4 * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
705ececd
MG
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
705ececd
MG
12#include <linux/slab.h>
13
14#include "midibuf.h"
15
b702ed25 16static int midibuf_message_length(unsigned char code)
705ececd 17{
ce9b490c 18 if (code < 0x80)
705ececd 19 return -1;
ce9b490c 20 else if (code < 0xf0) {
705ececd
MG
21 static const int length[] = { 3, 3, 3, 3, 2, 2, 3 };
22 return length[(code >> 4) - 8];
ce9b490c 23 } else {
705ececd 24 /*
e1a164d7
MG
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 */
ce9b490c 29 static const int length[] = { -1, 2, -1, 2, -1, -1, 1, 1, 1, 1,
e1a164d7
MG
30 1, 1, 1, -1, 1, 1
31 };
705ececd
MG
32 return length[code & 0x0f];
33 }
34}
35
1027f476
MG
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)
705ececd
MG
47{
48 this->pos_read = this->pos_write = this->full = 0;
49 this->command_prev = -1;
50}
51
1027f476 52int line6_midibuf_init(struct MidiBuffer *this, int size, int split)
705ececd 53{
ce9b490c 54 this->buf = kmalloc(size, GFP_KERNEL);
705ececd 55
536165d8 56 if (this->buf == NULL)
705ececd
MG
57 return -ENOMEM;
58
59 this->size = size;
60 this->split = split;
1027f476 61 line6_midibuf_reset(this);
705ececd
MG
62 return 0;
63}
64
1027f476 65void line6_midibuf_status(struct MidiBuffer *this)
705ececd 66{
88784515 67 pr_debug("midibuf size=%d split=%d pos_read=%d pos_write=%d "
ce9b490c
GKH
68 "full=%d command_prev=%02x\n", this->size, this->split,
69 this->pos_read, this->pos_write, this->full, this->command_prev);
705ececd
MG
70}
71
1027f476 72int line6_midibuf_bytes_free(struct MidiBuffer *this)
705ececd
MG
73{
74 return
e1a164d7
MG
75 midibuf_is_full(this) ?
76 0 :
77 (this->pos_read - this->pos_write + this->size - 1) % this->size +
78 1;
705ececd
MG
79}
80
1027f476 81int line6_midibuf_bytes_used(struct MidiBuffer *this)
705ececd
MG
82{
83 return
e1a164d7
MG
84 midibuf_is_empty(this) ?
85 0 :
86 (this->pos_write - this->pos_read + this->size - 1) % this->size +
87 1;
705ececd
MG
88}
89
e1a164d7
MG
90int line6_midibuf_write(struct MidiBuffer *this, unsigned char *data,
91 int length)
705ececd
MG
92{
93 int bytes_free;
94 int length1, length2;
95 int skip_active_sense = 0;
96
ce9b490c 97 if (midibuf_is_full(this) || (length <= 0))
705ececd
MG
98 return 0;
99
100 /* skip trailing active sense */
ce9b490c 101 if (data[length - 1] == 0xfe) {
705ececd
MG
102 --length;
103 skip_active_sense = 1;
104 }
105
1027f476 106 bytes_free = line6_midibuf_bytes_free(this);
705ececd 107
ce9b490c 108 if (length > bytes_free)
705ececd
MG
109 length = bytes_free;
110
ce9b490c 111 if (length > 0) {
705ececd
MG
112 length1 = this->size - this->pos_write;
113
ce9b490c 114 if (length < length1) {
705ececd
MG
115 /* no buffer wraparound */
116 memcpy(this->buf + this->pos_write, data, length);
117 this->pos_write += length;
ce9b490c 118 } else {
705ececd
MG
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
ce9b490c 126 if (this->pos_write == this->pos_read)
705ececd
MG
127 this->full = 1;
128 }
129
130 return length + skip_active_sense;
131}
132
1027f476 133int line6_midibuf_read(struct MidiBuffer *this, unsigned char *data, int length)
705ececd
MG
134{
135 int bytes_used;
136 int length1, length2;
137 int command;
138 int midi_length;
139 int repeat = 0;
140 int i;
141
ce9b490c
GKH
142 /* we need to be able to store at least a 3 byte MIDI message */
143 if (length < 3)
144 return -EINVAL;
705ececd 145
ce9b490c 146 if (midibuf_is_empty(this))
705ececd
MG
147 return 0;
148
1027f476 149 bytes_used = line6_midibuf_bytes_used(this);
705ececd 150
ce9b490c 151 if (length > bytes_used)
705ececd
MG
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
ce9b490c 159 if (command & 0x80) {
705ececd
MG
160 midi_length = midibuf_message_length(command);
161 this->command_prev = command;
ce9b490c
GKH
162 } else {
163 if (this->command_prev > 0) {
e1a164d7
MG
164 int midi_length_prev =
165 midibuf_message_length(this->command_prev);
705ececd 166
ce9b490c 167 if (midi_length_prev > 0) {
705ececd
MG
168 midi_length = midi_length_prev - 1;
169 repeat = 1;
ce9b490c 170 } else
705ececd 171 midi_length = -1;
ce9b490c 172 } else
705ececd
MG
173 midi_length = -1;
174 }
175
ce9b490c 176 if (midi_length < 0) {
705ececd 177 /* search for end of message */
ce9b490c 178 if (length < length1) {
705ececd 179 /* no buffer wraparound */
ce9b490c
GKH
180 for (i = 1; i < length; ++i)
181 if (this->buf[this->pos_read + i] & 0x80)
705ececd
MG
182 break;
183
184 midi_length = i;
ce9b490c 185 } else {
705ececd
MG
186 /* buffer wraparound */
187 length2 = length - length1;
188
ce9b490c
GKH
189 for (i = 1; i < length1; ++i)
190 if (this->buf[this->pos_read + i] & 0x80)
705ececd
MG
191 break;
192
ce9b490c 193 if (i < length1)
705ececd
MG
194 midi_length = i;
195 else {
ce9b490c
GKH
196 for (i = 0; i < length2; ++i)
197 if (this->buf[i] & 0x80)
705ececd
MG
198 break;
199
200 midi_length = length1 + i;
201 }
202 }
203
ce9b490c 204 if (midi_length == length)
e1a164d7 205 midi_length = -1; /* end of message not found */
705ececd
MG
206 }
207
ce9b490c
GKH
208 if (midi_length < 0) {
209 if (!this->split)
e1a164d7 210 return 0; /* command is not yet complete */
ce9b490c
GKH
211 } else {
212 if (length < midi_length)
e1a164d7 213 return 0; /* command is not yet complete */
705ececd
MG
214
215 length = midi_length;
216 }
217
ce9b490c 218 if (length < length1) {
705ececd
MG
219 /* no buffer wraparound */
220 memcpy(data + repeat, this->buf + this->pos_read, length);
221 this->pos_read += length;
ce9b490c 222 } else {
705ececd
MG
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
ce9b490c 230 if (repeat)
705ececd
MG
231 data[0] = this->command_prev;
232
233 this->full = 0;
234 return length + repeat;
235}
236
1027f476 237int line6_midibuf_ignore(struct MidiBuffer *this, int length)
705ececd 238{
1027f476 239 int bytes_used = line6_midibuf_bytes_used(this);
705ececd 240
ce9b490c 241 if (length > bytes_used)
705ececd
MG
242 length = bytes_used;
243
244 this->pos_read = (this->pos_read + length) % this->size;
245 this->full = 0;
246 return length;
247}
248
1027f476 249int line6_midibuf_skip_message(struct MidiBuffer *this, unsigned short mask)
705ececd
MG
250{
251 int cmd = this->command_prev;
252
ce9b490c
GKH
253 if ((cmd >= 0x80) && (cmd < 0xf0))
254 if ((mask & (1 << (cmd & 0x0f))) == 0)
705ececd
MG
255 return 1;
256
257 return 0;
258}
259
1027f476 260void line6_midibuf_destroy(struct MidiBuffer *this)
705ececd 261{
536165d8
GKH
262 kfree(this->buf);
263 this->buf = NULL;
705ececd 264}