Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755289AbcCBTkt (ORCPT ); Wed, 2 Mar 2016 14:40:49 -0500 Received: from mail-wm0-f68.google.com ([74.125.82.68]:34843 "EHLO mail-wm0-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751121AbcCBTiv (ORCPT ); Wed, 2 Mar 2016 14:38:51 -0500 From: "Felipe F. Tonello" To: linux-usb@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Felipe Balbi , Michal Nazarewicz , Clemens Ladisch Subject: [PATCH 1/5] usb: gadget: f_midi: refactor state machine Date: Wed, 2 Mar 2016 19:40:36 +0000 Message-Id: <1456947640-20673-2-git-send-email-eu@felipetonello.com> X-Mailer: git-send-email 2.7.2 In-Reply-To: <1456947640-20673-1-git-send-email-eu@felipetonello.com> References: <1456947640-20673-1-git-send-email-eu@felipetonello.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 7087 Lines: 291 This refactor results in a cleaner state machine code and as a result fixed a bug when packaging a USB-MIDI packet right after a non-conformant MIDI byte stream. Signed-off-by: Felipe F. Tonello --- drivers/usb/gadget/function/f_midi.c | 204 ++++++++++++++++++++++------------- 1 file changed, 129 insertions(+), 75 deletions(-) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 84c0ee5ebd1e..3cdb0741f3f8 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -50,6 +50,19 @@ static const char f_midi_longname[] = "MIDI Gadget"; */ #define MAX_PORTS 16 +/* MIDI message states */ +enum { + STATE_INITIAL = 0, /* pseudo state */ + STATE_1PARAM, + STATE_2PARAM_1, + STATE_2PARAM_2, + STATE_SYSEX_0, + STATE_SYSEX_1, + STATE_SYSEX_2, + STATE_REAL_TIME, + STATE_FINISHED, /* pseudo state */ +}; + /* * This is a gadget, and the IN/OUT naming is from the host's perspective. * USB -> OUT endpoint -> rawmidi @@ -60,13 +73,6 @@ struct gmidi_in_port { int active; uint8_t cable; uint8_t state; -#define STATE_UNKNOWN 0 -#define STATE_1PARAM 1 -#define STATE_2PARAM_1 2 -#define STATE_2PARAM_2 3 -#define STATE_SYSEX_0 4 -#define STATE_SYSEX_1 5 -#define STATE_SYSEX_2 6 uint8_t data[2]; }; @@ -400,118 +406,166 @@ static int f_midi_snd_free(struct snd_device *device) return 0; } -static void f_midi_transmit_packet(struct usb_request *req, uint8_t p0, - uint8_t p1, uint8_t p2, uint8_t p3) -{ - unsigned length = req->length; - u8 *buf = (u8 *)req->buf + length; - - buf[0] = p0; - buf[1] = p1; - buf[2] = p2; - buf[3] = p3; - req->length = length + 4; -} - /* * Converts MIDI commands to USB MIDI packets. */ static void f_midi_transmit_byte(struct usb_request *req, struct gmidi_in_port *port, uint8_t b) { - uint8_t p0 = port->cable << 4; + uint8_t p[4] = { port->cable << 4, 0, 0, 0 }; + uint8_t next_state = STATE_INITIAL; + + switch (b) { + case 0xf8 ... 0xff: + /* System Real-Time Messages */ + p[0] |= 0x0f; + p[1] = b; + next_state = port->state; + port->state = STATE_REAL_TIME; + break; + + case 0xf7: + /* End of SysEx */ + switch (port->state) { + case STATE_SYSEX_0: + p[0] |= 0x05; + p[1] = 0xf7; + next_state = STATE_FINISHED; + break; + case STATE_SYSEX_1: + p[0] |= 0x06; + p[1] = port->data[0]; + p[2] = 0xf7; + next_state = STATE_FINISHED; + break; + case STATE_SYSEX_2: + p[0] |= 0x07; + p[1] = port->data[0]; + p[2] = port->data[1]; + p[3] = 0xf7; + next_state = STATE_FINISHED; + break; + default: + /* Ignore byte */ + next_state = port->state; + port->state = STATE_INITIAL; + } + break; - if (b >= 0xf8) { - f_midi_transmit_packet(req, p0 | 0x0f, b, 0, 0); - } else if (b >= 0xf0) { + case 0xf0 ... 0xf6: + /* System Common Messages */ + port->data[0] = port->data[1] = 0; + port->state = STATE_INITIAL; switch (b) { case 0xf0: port->data[0] = b; - port->state = STATE_SYSEX_1; + port->data[1] = 0; + next_state = STATE_SYSEX_1; break; case 0xf1: case 0xf3: port->data[0] = b; - port->state = STATE_1PARAM; + next_state = STATE_1PARAM; break; case 0xf2: port->data[0] = b; - port->state = STATE_2PARAM_1; + next_state = STATE_2PARAM_1; break; case 0xf4: case 0xf5: - port->state = STATE_UNKNOWN; + next_state = STATE_INITIAL; break; case 0xf6: - f_midi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0); - port->state = STATE_UNKNOWN; - break; - case 0xf7: - switch (port->state) { - case STATE_SYSEX_0: - f_midi_transmit_packet(req, - p0 | 0x05, 0xf7, 0, 0); - break; - case STATE_SYSEX_1: - f_midi_transmit_packet(req, - p0 | 0x06, port->data[0], 0xf7, 0); - break; - case STATE_SYSEX_2: - f_midi_transmit_packet(req, - p0 | 0x07, port->data[0], - port->data[1], 0xf7); - break; - } - port->state = STATE_UNKNOWN; + p[0] |= 0x05; + p[1] = 0xf6; + next_state = STATE_FINISHED; break; } - } else if (b >= 0x80) { + break; + + case 0x80 ... 0xef: + /* + * Channel Voice Messages, Channel Mode Messages + * and Control Change Messages. + */ port->data[0] = b; + port->data[1] = 0; + port->state = STATE_INITIAL; if (b >= 0xc0 && b <= 0xdf) - port->state = STATE_1PARAM; + next_state = STATE_1PARAM; else - port->state = STATE_2PARAM_1; - } else { /* b < 0x80 */ + next_state = STATE_2PARAM_1; + break; + + case 0x00 ... 0x7f: + /* Message parameters */ switch (port->state) { case STATE_1PARAM: - if (port->data[0] < 0xf0) { - p0 |= port->data[0] >> 4; - } else { - p0 |= 0x02; - port->state = STATE_UNKNOWN; - } - f_midi_transmit_packet(req, p0, port->data[0], b, 0); + if (port->data[0] < 0xf0) + p[0] |= port->data[0] >> 4; + else + p[0] |= 0x02; + + p[1] = port->data[0]; + p[2] = b; + /* This is to allow Running State Messages */ + next_state = STATE_1PARAM; break; case STATE_2PARAM_1: port->data[1] = b; - port->state = STATE_2PARAM_2; + next_state = STATE_2PARAM_2; break; case STATE_2PARAM_2: - if (port->data[0] < 0xf0) { - p0 |= port->data[0] >> 4; - port->state = STATE_2PARAM_1; - } else { - p0 |= 0x03; - port->state = STATE_UNKNOWN; - } - f_midi_transmit_packet(req, - p0, port->data[0], port->data[1], b); + if (port->data[0] < 0xf0) + p[0] |= port->data[0] >> 4; + else + p[0] |= 0x03; + + p[1] = port->data[0]; + p[2] = port->data[1]; + p[3] = b; + /* This is to allow Running State Messages */ + next_state = STATE_2PARAM_1; break; case STATE_SYSEX_0: port->data[0] = b; - port->state = STATE_SYSEX_1; + next_state = STATE_SYSEX_1; break; case STATE_SYSEX_1: port->data[1] = b; - port->state = STATE_SYSEX_2; + next_state = STATE_SYSEX_2; break; case STATE_SYSEX_2: - f_midi_transmit_packet(req, - p0 | 0x04, port->data[0], port->data[1], b); - port->state = STATE_SYSEX_0; + p[0] |= 0x04; + p[1] = port->data[0]; + p[2] = port->data[1]; + p[3] = b; + next_state = STATE_SYSEX_0; break; } + break; + } + + /* States where we have to write into the USB request */ + if (next_state == STATE_FINISHED || + port->state == STATE_SYSEX_2 || + port->state == STATE_1PARAM || + port->state == STATE_2PARAM_2 || + port->state == STATE_REAL_TIME) { + + unsigned length = req->length; + u8 *buf = (u8 *)req->buf + length; + + memcpy(buf, p, sizeof(p)); + req->length = length + sizeof(p); + + if (next_state == STATE_FINISHED) { + next_state = STATE_INITIAL; + port->data[0] = port->data[1] = 0; + } } + + port->state = next_state; } static void f_midi_drop_out_substreams(struct f_midi *midi) @@ -631,7 +685,7 @@ static int f_midi_in_open(struct snd_rawmidi_substream *substream) VDBG(midi, "%s()\n", __func__); port = midi->in_ports_array + substream->number; port->substream = substream; - port->state = STATE_UNKNOWN; + port->state = STATE_INITIAL; return 0; } -- 2.7.2