1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Line 6 Linux USB driver 4 * 5 * Copyright (C) 2004-2010 Markus Grabner (line6@grabner-graz.at) 6 */ 7 8 #include <linux/slab.h> 9 10 #include "midibuf.h" 11 12 13 static int midibuf_message_length(unsigned char code) 14 { 15 int message_length; 16 17 if (code < 0x80) 18 message_length = -1; 19 else if (code < 0xf0) { 20 static const int length[] = { 3, 3, 3, 3, 2, 2, 3 }; 21 22 message_length = length[(code >> 4) - 8]; 23 } else { 24 static const int length[] = { -1, 2, 2, 2, -1, -1, 1, 1, 1, -1, 25 1, 1, 1, -1, 1, 1 26 }; 27 message_length = length[code & 0x0f]; 28 } 29 30 return message_length; 31 } 32 33 static int midibuf_is_empty(struct midi_buffer *this) 34 { 35 return (this->pos_read == this->pos_write) && !this->full; 36 } 37 38 static int midibuf_is_full(struct midi_buffer *this) 39 { 40 return this->full; 41 } 42 43 void line6_midibuf_reset(struct midi_buffer *this) 44 { 45 this->pos_read = this->pos_write = this->full = 0; 46 this->command_prev = -1; 47 } 48 49 int line6_midibuf_init(struct midi_buffer *this, int size, int split) 50 { 51 this->buf = kmalloc(size, GFP_KERNEL); 52 53 if (this->buf == NULL) 54 return -ENOMEM; 55 56 this->size = size; 57 this->split = split; 58 line6_midibuf_reset(this); 59 return 0; 60 } 61 62 int line6_midibuf_bytes_free(struct midi_buffer *this) 63 { 64 return 65 midibuf_is_full(this) ? 66 0 : 67 (this->pos_read - this->pos_write + this->size - 1) % this->size + 68 1; 69 } 70 71 int line6_midibuf_bytes_used(struct midi_buffer *this) 72 { 73 return 74 midibuf_is_empty(this) ? 75 0 : 76 (this->pos_write - this->pos_read + this->size - 1) % this->size + 77 1; 78 } 79 80 int line6_midibuf_write(struct midi_buffer *this, unsigned char *data, 81 int length) 82 { 83 int bytes_free; 84 int length1, length2; 85 int skip_active_sense = 0; 86 87 if (midibuf_is_full(this) || (length <= 0)) 88 return 0; 89 90 /* skip trailing active sense */ 91 if (data[length - 1] == 0xfe) { 92 --length; 93 skip_active_sense = 1; 94 } 95 96 bytes_free = line6_midibuf_bytes_free(this); 97 98 if (length > bytes_free) 99 length = bytes_free; 100 101 if (length > 0) { 102 length1 = this->size - this->pos_write; 103 104 if (length < length1) { 105 /* no buffer wraparound */ 106 memcpy(this->buf + this->pos_write, data, length); 107 this->pos_write += length; 108 } else { 109 /* buffer wraparound */ 110 length2 = length - length1; 111 memcpy(this->buf + this->pos_write, data, length1); 112 memcpy(this->buf, data + length1, length2); 113 this->pos_write = length2; 114 } 115 116 if (this->pos_write == this->pos_read) 117 this->full = 1; 118 } 119 120 return length + skip_active_sense; 121 } 122 123 int line6_midibuf_read(struct midi_buffer *this, unsigned char *data, 124 int length, int read_type) 125 { 126 int bytes_used; 127 int length1, length2; 128 int command; 129 int midi_length; 130 int repeat = 0; 131 int i; 132 133 /* we need to be able to store at least a 3 byte MIDI message */ 134 if (length < 3) 135 return -EINVAL; 136 137 if (midibuf_is_empty(this)) 138 return 0; 139 140 bytes_used = line6_midibuf_bytes_used(this); 141 142 if (length > bytes_used) 143 length = bytes_used; 144 145 length1 = this->size - this->pos_read; 146 147 command = this->buf[this->pos_read]; 148 /* 149 PODxt always has status byte lower nibble set to 0010, 150 when it means to send 0000, so we correct if here so 151 that control/program changes come on channel 1 and 152 sysex message status byte is correct 153 */ 154 if (read_type == LINE6_MIDIBUF_READ_RX) { 155 if (command == 0xb2 || command == 0xc2 || command == 0xf2) { 156 unsigned char fixed = command & 0xf0; 157 this->buf[this->pos_read] = fixed; 158 command = fixed; 159 } 160 } 161 162 /* check MIDI command length */ 163 if (command & 0x80) { 164 midi_length = midibuf_message_length(command); 165 this->command_prev = command; 166 } else { 167 if (this->command_prev > 0) { 168 int midi_length_prev = 169 midibuf_message_length(this->command_prev); 170 171 if (midi_length_prev > 1) { 172 midi_length = midi_length_prev - 1; 173 repeat = 1; 174 } else 175 midi_length = -1; 176 } else 177 midi_length = -1; 178 } 179 180 if (midi_length < 0) { 181 /* search for end of message */ 182 if (length < length1) { 183 /* no buffer wraparound */ 184 for (i = 1; i < length; ++i) 185 if (this->buf[this->pos_read + i] & 0x80) 186 break; 187 188 midi_length = i; 189 } else { 190 /* buffer wraparound */ 191 length2 = length - length1; 192 193 for (i = 1; i < length1; ++i) 194 if (this->buf[this->pos_read + i] & 0x80) 195 break; 196 197 if (i < length1) 198 midi_length = i; 199 else { 200 for (i = 0; i < length2; ++i) 201 if (this->buf[i] & 0x80) 202 break; 203 204 midi_length = length1 + i; 205 } 206 } 207 208 if (midi_length == length) 209 midi_length = -1; /* end of message not found */ 210 } 211 212 if (midi_length < 0) { 213 if (!this->split) 214 return 0; /* command is not yet complete */ 215 } else { 216 if (length < midi_length) 217 return 0; /* command is not yet complete */ 218 219 length = midi_length; 220 } 221 222 if (length < length1) { 223 /* no buffer wraparound */ 224 memcpy(data + repeat, this->buf + this->pos_read, length); 225 this->pos_read += length; 226 } else { 227 /* buffer wraparound */ 228 length2 = length - length1; 229 memcpy(data + repeat, this->buf + this->pos_read, length1); 230 memcpy(data + repeat + length1, this->buf, length2); 231 this->pos_read = length2; 232 } 233 234 if (repeat) 235 data[0] = this->command_prev; 236 237 this->full = 0; 238 return length + repeat; 239 } 240 241 int line6_midibuf_ignore(struct midi_buffer *this, int length) 242 { 243 int bytes_used = line6_midibuf_bytes_used(this); 244 245 if (length > bytes_used) 246 length = bytes_used; 247 248 this->pos_read = (this->pos_read + length) % this->size; 249 this->full = 0; 250 return length; 251 } 252 253 void line6_midibuf_destroy(struct midi_buffer *this) 254 { 255 kfree(this->buf); 256 this->buf = NULL; 257 } 258