258 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Line 6 Linux USB driver4 *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 13static 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, 126 };27 message_length = length[code & 0x0f];28 }29 30 return message_length;31}32 33static int midibuf_is_empty(struct midi_buffer *this)34{35 return (this->pos_read == this->pos_write) && !this->full;36}37 38static int midibuf_is_full(struct midi_buffer *this)39{40 return this->full;41}42 43void 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 49int 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 62int line6_midibuf_bytes_free(struct midi_buffer *this)63{64 return65 midibuf_is_full(this) ?66 0 :67 (this->pos_read - this->pos_write + this->size - 1) % this->size +68 1;69}70 71int line6_midibuf_bytes_used(struct midi_buffer *this)72{73 return74 midibuf_is_empty(this) ?75 0 :76 (this->pos_write - this->pos_read + this->size - 1) % this->size +77 1;78}79 80int 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 123int 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 so151 that control/program changes come on channel 1 and152 sysex message status byte is correct153 */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 } else175 midi_length = -1;176 } else177 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 241int 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 253void line6_midibuf_destroy(struct midi_buffer *this)254{255 kfree(this->buf);256 this->buf = NULL;257}258