18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Line 6 Linux USB driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "midibuf.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistatic int midibuf_message_length(unsigned char code) 148c2ecf20Sopenharmony_ci{ 158c2ecf20Sopenharmony_ci int message_length; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci if (code < 0x80) 188c2ecf20Sopenharmony_ci message_length = -1; 198c2ecf20Sopenharmony_ci else if (code < 0xf0) { 208c2ecf20Sopenharmony_ci static const int length[] = { 3, 3, 3, 3, 2, 2, 3 }; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci message_length = length[(code >> 4) - 8]; 238c2ecf20Sopenharmony_ci } else { 248c2ecf20Sopenharmony_ci static const int length[] = { -1, 2, 2, 2, -1, -1, 1, 1, 1, -1, 258c2ecf20Sopenharmony_ci 1, 1, 1, -1, 1, 1 268c2ecf20Sopenharmony_ci }; 278c2ecf20Sopenharmony_ci message_length = length[code & 0x0f]; 288c2ecf20Sopenharmony_ci } 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci return message_length; 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic int midibuf_is_empty(struct midi_buffer *this) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci return (this->pos_read == this->pos_write) && !this->full; 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic int midibuf_is_full(struct midi_buffer *this) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci return this->full; 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_civoid line6_midibuf_reset(struct midi_buffer *this) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci this->pos_read = this->pos_write = this->full = 0; 468c2ecf20Sopenharmony_ci this->command_prev = -1; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ciint line6_midibuf_init(struct midi_buffer *this, int size, int split) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci this->buf = kmalloc(size, GFP_KERNEL); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if (this->buf == NULL) 548c2ecf20Sopenharmony_ci return -ENOMEM; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci this->size = size; 578c2ecf20Sopenharmony_ci this->split = split; 588c2ecf20Sopenharmony_ci line6_midibuf_reset(this); 598c2ecf20Sopenharmony_ci return 0; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ciint line6_midibuf_bytes_free(struct midi_buffer *this) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci return 658c2ecf20Sopenharmony_ci midibuf_is_full(this) ? 668c2ecf20Sopenharmony_ci 0 : 678c2ecf20Sopenharmony_ci (this->pos_read - this->pos_write + this->size - 1) % this->size + 688c2ecf20Sopenharmony_ci 1; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ciint line6_midibuf_bytes_used(struct midi_buffer *this) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci return 748c2ecf20Sopenharmony_ci midibuf_is_empty(this) ? 758c2ecf20Sopenharmony_ci 0 : 768c2ecf20Sopenharmony_ci (this->pos_write - this->pos_read + this->size - 1) % this->size + 778c2ecf20Sopenharmony_ci 1; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ciint line6_midibuf_write(struct midi_buffer *this, unsigned char *data, 818c2ecf20Sopenharmony_ci int length) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci int bytes_free; 848c2ecf20Sopenharmony_ci int length1, length2; 858c2ecf20Sopenharmony_ci int skip_active_sense = 0; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (midibuf_is_full(this) || (length <= 0)) 888c2ecf20Sopenharmony_ci return 0; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* skip trailing active sense */ 918c2ecf20Sopenharmony_ci if (data[length - 1] == 0xfe) { 928c2ecf20Sopenharmony_ci --length; 938c2ecf20Sopenharmony_ci skip_active_sense = 1; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci bytes_free = line6_midibuf_bytes_free(this); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (length > bytes_free) 998c2ecf20Sopenharmony_ci length = bytes_free; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (length > 0) { 1028c2ecf20Sopenharmony_ci length1 = this->size - this->pos_write; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (length < length1) { 1058c2ecf20Sopenharmony_ci /* no buffer wraparound */ 1068c2ecf20Sopenharmony_ci memcpy(this->buf + this->pos_write, data, length); 1078c2ecf20Sopenharmony_ci this->pos_write += length; 1088c2ecf20Sopenharmony_ci } else { 1098c2ecf20Sopenharmony_ci /* buffer wraparound */ 1108c2ecf20Sopenharmony_ci length2 = length - length1; 1118c2ecf20Sopenharmony_ci memcpy(this->buf + this->pos_write, data, length1); 1128c2ecf20Sopenharmony_ci memcpy(this->buf, data + length1, length2); 1138c2ecf20Sopenharmony_ci this->pos_write = length2; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (this->pos_write == this->pos_read) 1178c2ecf20Sopenharmony_ci this->full = 1; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return length + skip_active_sense; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ciint line6_midibuf_read(struct midi_buffer *this, unsigned char *data, 1248c2ecf20Sopenharmony_ci int length, int read_type) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci int bytes_used; 1278c2ecf20Sopenharmony_ci int length1, length2; 1288c2ecf20Sopenharmony_ci int command; 1298c2ecf20Sopenharmony_ci int midi_length; 1308c2ecf20Sopenharmony_ci int repeat = 0; 1318c2ecf20Sopenharmony_ci int i; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* we need to be able to store at least a 3 byte MIDI message */ 1348c2ecf20Sopenharmony_ci if (length < 3) 1358c2ecf20Sopenharmony_ci return -EINVAL; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (midibuf_is_empty(this)) 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci bytes_used = line6_midibuf_bytes_used(this); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (length > bytes_used) 1438c2ecf20Sopenharmony_ci length = bytes_used; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci length1 = this->size - this->pos_read; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci command = this->buf[this->pos_read]; 1488c2ecf20Sopenharmony_ci /* 1498c2ecf20Sopenharmony_ci PODxt always has status byte lower nibble set to 0010, 1508c2ecf20Sopenharmony_ci when it means to send 0000, so we correct if here so 1518c2ecf20Sopenharmony_ci that control/program changes come on channel 1 and 1528c2ecf20Sopenharmony_ci sysex message status byte is correct 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci if (read_type == LINE6_MIDIBUF_READ_RX) { 1558c2ecf20Sopenharmony_ci if (command == 0xb2 || command == 0xc2 || command == 0xf2) { 1568c2ecf20Sopenharmony_ci unsigned char fixed = command & 0xf0; 1578c2ecf20Sopenharmony_ci this->buf[this->pos_read] = fixed; 1588c2ecf20Sopenharmony_ci command = fixed; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* check MIDI command length */ 1638c2ecf20Sopenharmony_ci if (command & 0x80) { 1648c2ecf20Sopenharmony_ci midi_length = midibuf_message_length(command); 1658c2ecf20Sopenharmony_ci this->command_prev = command; 1668c2ecf20Sopenharmony_ci } else { 1678c2ecf20Sopenharmony_ci if (this->command_prev > 0) { 1688c2ecf20Sopenharmony_ci int midi_length_prev = 1698c2ecf20Sopenharmony_ci midibuf_message_length(this->command_prev); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (midi_length_prev > 1) { 1728c2ecf20Sopenharmony_ci midi_length = midi_length_prev - 1; 1738c2ecf20Sopenharmony_ci repeat = 1; 1748c2ecf20Sopenharmony_ci } else 1758c2ecf20Sopenharmony_ci midi_length = -1; 1768c2ecf20Sopenharmony_ci } else 1778c2ecf20Sopenharmony_ci midi_length = -1; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (midi_length < 0) { 1818c2ecf20Sopenharmony_ci /* search for end of message */ 1828c2ecf20Sopenharmony_ci if (length < length1) { 1838c2ecf20Sopenharmony_ci /* no buffer wraparound */ 1848c2ecf20Sopenharmony_ci for (i = 1; i < length; ++i) 1858c2ecf20Sopenharmony_ci if (this->buf[this->pos_read + i] & 0x80) 1868c2ecf20Sopenharmony_ci break; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci midi_length = i; 1898c2ecf20Sopenharmony_ci } else { 1908c2ecf20Sopenharmony_ci /* buffer wraparound */ 1918c2ecf20Sopenharmony_ci length2 = length - length1; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci for (i = 1; i < length1; ++i) 1948c2ecf20Sopenharmony_ci if (this->buf[this->pos_read + i] & 0x80) 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (i < length1) 1988c2ecf20Sopenharmony_ci midi_length = i; 1998c2ecf20Sopenharmony_ci else { 2008c2ecf20Sopenharmony_ci for (i = 0; i < length2; ++i) 2018c2ecf20Sopenharmony_ci if (this->buf[i] & 0x80) 2028c2ecf20Sopenharmony_ci break; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci midi_length = length1 + i; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (midi_length == length) 2098c2ecf20Sopenharmony_ci midi_length = -1; /* end of message not found */ 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (midi_length < 0) { 2138c2ecf20Sopenharmony_ci if (!this->split) 2148c2ecf20Sopenharmony_ci return 0; /* command is not yet complete */ 2158c2ecf20Sopenharmony_ci } else { 2168c2ecf20Sopenharmony_ci if (length < midi_length) 2178c2ecf20Sopenharmony_ci return 0; /* command is not yet complete */ 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci length = midi_length; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (length < length1) { 2238c2ecf20Sopenharmony_ci /* no buffer wraparound */ 2248c2ecf20Sopenharmony_ci memcpy(data + repeat, this->buf + this->pos_read, length); 2258c2ecf20Sopenharmony_ci this->pos_read += length; 2268c2ecf20Sopenharmony_ci } else { 2278c2ecf20Sopenharmony_ci /* buffer wraparound */ 2288c2ecf20Sopenharmony_ci length2 = length - length1; 2298c2ecf20Sopenharmony_ci memcpy(data + repeat, this->buf + this->pos_read, length1); 2308c2ecf20Sopenharmony_ci memcpy(data + repeat + length1, this->buf, length2); 2318c2ecf20Sopenharmony_ci this->pos_read = length2; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (repeat) 2358c2ecf20Sopenharmony_ci data[0] = this->command_prev; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci this->full = 0; 2388c2ecf20Sopenharmony_ci return length + repeat; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ciint line6_midibuf_ignore(struct midi_buffer *this, int length) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci int bytes_used = line6_midibuf_bytes_used(this); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (length > bytes_used) 2468c2ecf20Sopenharmony_ci length = bytes_used; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci this->pos_read = (this->pos_read + length) % this->size; 2498c2ecf20Sopenharmony_ci this->full = 0; 2508c2ecf20Sopenharmony_ci return length; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_civoid line6_midibuf_destroy(struct midi_buffer *this) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci kfree(this->buf); 2568c2ecf20Sopenharmony_ci this->buf = NULL; 2578c2ecf20Sopenharmony_ci} 258