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