162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Line 6 Linux USB driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "midibuf.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistatic int midibuf_message_length(unsigned char code) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci int message_length; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci if (code < 0x80) 1862306a36Sopenharmony_ci message_length = -1; 1962306a36Sopenharmony_ci else if (code < 0xf0) { 2062306a36Sopenharmony_ci static const int length[] = { 3, 3, 3, 3, 2, 2, 3 }; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci message_length = length[(code >> 4) - 8]; 2362306a36Sopenharmony_ci } else { 2462306a36Sopenharmony_ci static const int length[] = { -1, 2, 2, 2, -1, -1, 1, 1, 1, -1, 2562306a36Sopenharmony_ci 1, 1, 1, -1, 1, 1 2662306a36Sopenharmony_ci }; 2762306a36Sopenharmony_ci message_length = length[code & 0x0f]; 2862306a36Sopenharmony_ci } 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci return message_length; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int midibuf_is_empty(struct midi_buffer *this) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci return (this->pos_read == this->pos_write) && !this->full; 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int midibuf_is_full(struct midi_buffer *this) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci return this->full; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_civoid line6_midibuf_reset(struct midi_buffer *this) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci this->pos_read = this->pos_write = this->full = 0; 4662306a36Sopenharmony_ci this->command_prev = -1; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ciint line6_midibuf_init(struct midi_buffer *this, int size, int split) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci this->buf = kmalloc(size, GFP_KERNEL); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (this->buf == NULL) 5462306a36Sopenharmony_ci return -ENOMEM; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci this->size = size; 5762306a36Sopenharmony_ci this->split = split; 5862306a36Sopenharmony_ci line6_midibuf_reset(this); 5962306a36Sopenharmony_ci return 0; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ciint line6_midibuf_bytes_free(struct midi_buffer *this) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci return 6562306a36Sopenharmony_ci midibuf_is_full(this) ? 6662306a36Sopenharmony_ci 0 : 6762306a36Sopenharmony_ci (this->pos_read - this->pos_write + this->size - 1) % this->size + 6862306a36Sopenharmony_ci 1; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ciint line6_midibuf_bytes_used(struct midi_buffer *this) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci return 7462306a36Sopenharmony_ci midibuf_is_empty(this) ? 7562306a36Sopenharmony_ci 0 : 7662306a36Sopenharmony_ci (this->pos_write - this->pos_read + this->size - 1) % this->size + 7762306a36Sopenharmony_ci 1; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ciint line6_midibuf_write(struct midi_buffer *this, unsigned char *data, 8162306a36Sopenharmony_ci int length) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci int bytes_free; 8462306a36Sopenharmony_ci int length1, length2; 8562306a36Sopenharmony_ci int skip_active_sense = 0; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (midibuf_is_full(this) || (length <= 0)) 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* skip trailing active sense */ 9162306a36Sopenharmony_ci if (data[length - 1] == 0xfe) { 9262306a36Sopenharmony_ci --length; 9362306a36Sopenharmony_ci skip_active_sense = 1; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci bytes_free = line6_midibuf_bytes_free(this); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (length > bytes_free) 9962306a36Sopenharmony_ci length = bytes_free; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (length > 0) { 10262306a36Sopenharmony_ci length1 = this->size - this->pos_write; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (length < length1) { 10562306a36Sopenharmony_ci /* no buffer wraparound */ 10662306a36Sopenharmony_ci memcpy(this->buf + this->pos_write, data, length); 10762306a36Sopenharmony_ci this->pos_write += length; 10862306a36Sopenharmony_ci } else { 10962306a36Sopenharmony_ci /* buffer wraparound */ 11062306a36Sopenharmony_ci length2 = length - length1; 11162306a36Sopenharmony_ci memcpy(this->buf + this->pos_write, data, length1); 11262306a36Sopenharmony_ci memcpy(this->buf, data + length1, length2); 11362306a36Sopenharmony_ci this->pos_write = length2; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (this->pos_write == this->pos_read) 11762306a36Sopenharmony_ci this->full = 1; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return length + skip_active_sense; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ciint line6_midibuf_read(struct midi_buffer *this, unsigned char *data, 12462306a36Sopenharmony_ci int length, int read_type) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci int bytes_used; 12762306a36Sopenharmony_ci int length1, length2; 12862306a36Sopenharmony_ci int command; 12962306a36Sopenharmony_ci int midi_length; 13062306a36Sopenharmony_ci int repeat = 0; 13162306a36Sopenharmony_ci int i; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* we need to be able to store at least a 3 byte MIDI message */ 13462306a36Sopenharmony_ci if (length < 3) 13562306a36Sopenharmony_ci return -EINVAL; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (midibuf_is_empty(this)) 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci bytes_used = line6_midibuf_bytes_used(this); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (length > bytes_used) 14362306a36Sopenharmony_ci length = bytes_used; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci length1 = this->size - this->pos_read; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci command = this->buf[this->pos_read]; 14862306a36Sopenharmony_ci /* 14962306a36Sopenharmony_ci PODxt always has status byte lower nibble set to 0010, 15062306a36Sopenharmony_ci when it means to send 0000, so we correct if here so 15162306a36Sopenharmony_ci that control/program changes come on channel 1 and 15262306a36Sopenharmony_ci sysex message status byte is correct 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_ci if (read_type == LINE6_MIDIBUF_READ_RX) { 15562306a36Sopenharmony_ci if (command == 0xb2 || command == 0xc2 || command == 0xf2) { 15662306a36Sopenharmony_ci unsigned char fixed = command & 0xf0; 15762306a36Sopenharmony_ci this->buf[this->pos_read] = fixed; 15862306a36Sopenharmony_ci command = fixed; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* check MIDI command length */ 16362306a36Sopenharmony_ci if (command & 0x80) { 16462306a36Sopenharmony_ci midi_length = midibuf_message_length(command); 16562306a36Sopenharmony_ci this->command_prev = command; 16662306a36Sopenharmony_ci } else { 16762306a36Sopenharmony_ci if (this->command_prev > 0) { 16862306a36Sopenharmony_ci int midi_length_prev = 16962306a36Sopenharmony_ci midibuf_message_length(this->command_prev); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (midi_length_prev > 1) { 17262306a36Sopenharmony_ci midi_length = midi_length_prev - 1; 17362306a36Sopenharmony_ci repeat = 1; 17462306a36Sopenharmony_ci } else 17562306a36Sopenharmony_ci midi_length = -1; 17662306a36Sopenharmony_ci } else 17762306a36Sopenharmony_ci midi_length = -1; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (midi_length < 0) { 18162306a36Sopenharmony_ci /* search for end of message */ 18262306a36Sopenharmony_ci if (length < length1) { 18362306a36Sopenharmony_ci /* no buffer wraparound */ 18462306a36Sopenharmony_ci for (i = 1; i < length; ++i) 18562306a36Sopenharmony_ci if (this->buf[this->pos_read + i] & 0x80) 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci midi_length = i; 18962306a36Sopenharmony_ci } else { 19062306a36Sopenharmony_ci /* buffer wraparound */ 19162306a36Sopenharmony_ci length2 = length - length1; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci for (i = 1; i < length1; ++i) 19462306a36Sopenharmony_ci if (this->buf[this->pos_read + i] & 0x80) 19562306a36Sopenharmony_ci break; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (i < length1) 19862306a36Sopenharmony_ci midi_length = i; 19962306a36Sopenharmony_ci else { 20062306a36Sopenharmony_ci for (i = 0; i < length2; ++i) 20162306a36Sopenharmony_ci if (this->buf[i] & 0x80) 20262306a36Sopenharmony_ci break; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci midi_length = length1 + i; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (midi_length == length) 20962306a36Sopenharmony_ci midi_length = -1; /* end of message not found */ 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (midi_length < 0) { 21362306a36Sopenharmony_ci if (!this->split) 21462306a36Sopenharmony_ci return 0; /* command is not yet complete */ 21562306a36Sopenharmony_ci } else { 21662306a36Sopenharmony_ci if (length < midi_length) 21762306a36Sopenharmony_ci return 0; /* command is not yet complete */ 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci length = midi_length; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (length < length1) { 22362306a36Sopenharmony_ci /* no buffer wraparound */ 22462306a36Sopenharmony_ci memcpy(data + repeat, this->buf + this->pos_read, length); 22562306a36Sopenharmony_ci this->pos_read += length; 22662306a36Sopenharmony_ci } else { 22762306a36Sopenharmony_ci /* buffer wraparound */ 22862306a36Sopenharmony_ci length2 = length - length1; 22962306a36Sopenharmony_ci memcpy(data + repeat, this->buf + this->pos_read, length1); 23062306a36Sopenharmony_ci memcpy(data + repeat + length1, this->buf, length2); 23162306a36Sopenharmony_ci this->pos_read = length2; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (repeat) 23562306a36Sopenharmony_ci data[0] = this->command_prev; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci this->full = 0; 23862306a36Sopenharmony_ci return length + repeat; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ciint line6_midibuf_ignore(struct midi_buffer *this, int length) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci int bytes_used = line6_midibuf_bytes_used(this); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (length > bytes_used) 24662306a36Sopenharmony_ci length = bytes_used; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci this->pos_read = (this->pos_read + length) % this->size; 24962306a36Sopenharmony_ci this->full = 0; 25062306a36Sopenharmony_ci return length; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_civoid line6_midibuf_destroy(struct midi_buffer *this) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci kfree(this->buf); 25662306a36Sopenharmony_ci this->buf = NULL; 25762306a36Sopenharmony_ci} 258