brintos

brintos / linux-shallow public Read only

0
0
Text · 5.4 KiB · 57fca13 Raw
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