Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1296874 > unrolled thread

[PATCH 1/2] usb: gadget: f_midi: refactor state machine

Started by"Felipe F. Tonello" <eu@felipetonello.com>
First post2015-12-22 17:10 +0100
Last post2015-12-23 10:30 +0100
Articles 6 — 4 participants

Back to article view | Back to linux.kernel


Contents

  [PATCH 1/2] usb: gadget: f_midi: refactor state machine "Felipe F. Tonello" <eu@felipetonello.com> - 2015-12-22 17:10 +0100
    Re: [PATCH 1/2] usb: gadget: f_midi: refactor state machine Clemens Ladisch <clemens@ladisch.de> - 2015-12-22 18:20 +0100
      Re: [PATCH 1/2] usb: gadget: f_midi: refactor state machine Felipe Ferreri Tonello <eu@felipetonello.com> - 2015-12-23 13:00 +0100
        Re: [PATCH 1/2] usb: gadget: f_midi: refactor state machine Clemens Ladisch <clemens@ladisch.de> - 2015-12-23 23:00 +0100
    Re: [PATCH 1/2] usb: gadget: f_midi: refactor state machine Felipe Balbi <balbi@ti.com> - 2015-12-22 18:50 +0100
      Re: [PATCH 1/2] usb: gadget: f_midi: refactor state machine Felipe Ferreri Tonello <eu@felipetonello.com> - 2015-12-23 10:30 +0100

#1296874 — [PATCH 1/2] usb: gadget: f_midi: refactor state machine

From"Felipe F. Tonello" <eu@felipetonello.com>
Date2015-12-22 17:10 +0100
Subject[PATCH 1/2] usb: gadget: f_midi: refactor state machine
Message-ID<qIxFE-694-31@gated-at.bofh.it>
This refactor includes the following:
 * Cleaner state machine code;
 * Reset state if MIDI message parsed is non-conformant;
 * Fixed bug when a conformant MIDI message was followed by a non-conformant
   causing the MIDI-USB message to use old temporary data (port->data[0..1]),
   thus packing a wrong MIDI-USB request.

Signed-off-by: Felipe F. Tonello <eu@felipetonello.com>
---
 drivers/usb/gadget/function/f_midi.c | 243 ++++++++++++++++++++---------------
 1 file changed, 141 insertions(+), 102 deletions(-)

diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index fb1fe96d..b70a830 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -50,6 +50,18 @@ static const char f_midi_longname[] = "MIDI Gadget";
  */
 #define MAX_PORTS 16
 
+/* MIDI message states */
+enum {
+	STATE_INITIAL = 0,
+	STATE_1PARAM,
+	STATE_2PARAM_1,
+	STATE_2PARAM_2,
+	STATE_SYSEX_0,
+	STATE_SYSEX_1,
+	STATE_SYSEX_2,
+	STATE_FINISHED,
+};
+
 /*
  * This is a gadget, and the IN/OUT naming is from the host's perspective.
  * USB -> OUT endpoint -> rawmidi
@@ -60,13 +72,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 +405,152 @@ 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;
-
-	if (b >= 0xf8) {
-		f_midi_transmit_packet(req, p0 | 0x0f, b, 0, 0);
-	} else if (b >= 0xf0) {
-		switch (b) {
-		case 0xf0:
-			port->data[0] = b;
-			port->state = STATE_SYSEX_1;
-			break;
-		case 0xf1:
-		case 0xf3:
-			port->data[0] = b;
-			port->state = STATE_1PARAM;
-			break;
-		case 0xf2:
+	uint8_t p[4] = { port->cable << 4, 0, 0, 0 };
+	uint8_t next_state = STATE_INITIAL;
+
+	switch (port->state) {
+	case STATE_INITIAL: {
+		if (b >= 0xf8) {
+			p[0] |= 0x0f;
+			p[1] = b;
+			next_state = STATE_FINISHED;
+		} else if (b >= 0xf0) {
+			switch (b) {
+			case 0xf0:
+				port->data[0] = b;
+				next_state = STATE_SYSEX_1;
+				break;
+			case 0xf1:
+			case 0xf3:
+				port->data[0] = b;
+				next_state = STATE_1PARAM;
+				break;
+			case 0xf2:
+				port->data[0] = b;
+				next_state = STATE_2PARAM_1;
+				break;
+			case 0xf4:
+			case 0xf5:
+				next_state = STATE_INITIAL;
+				break;
+			case 0xf6:
+				p[0] |= 0x05;
+				p[1] = 0xf6;
+				next_state = STATE_FINISHED;
+				break;
+			}
+		} else if (b >= 0x80) {
 			port->data[0] = b;
-			port->state = STATE_2PARAM_1;
-			break;
-		case 0xf4:
-		case 0xf5:
-			port->state = STATE_UNKNOWN;
-			break;
-		case 0xf6:
-			f_midi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0);
-			port->state = STATE_UNKNOWN;
-			break;
-		case 0xf7:
+			if (b >= 0xc0 && b <= 0xdf)
+				next_state = STATE_1PARAM;
+			else
+				next_state = STATE_2PARAM_1;
+		}
+		break;
+	}
+	case STATE_1PARAM:
+	case STATE_2PARAM_1:
+	case STATE_2PARAM_2:
+	case STATE_SYSEX_0:
+	case STATE_SYSEX_1:
+	case STATE_SYSEX_2: {
+		if (b == 0xf7) {
 			switch (port->state) {
 			case STATE_SYSEX_0:
-				f_midi_transmit_packet(req,
-					p0 | 0x05, 0xf7, 0, 0);
+				p[0] |= 0x05;
+				p[1] = 0xf7;
 				break;
 			case STATE_SYSEX_1:
-				f_midi_transmit_packet(req,
-					p0 | 0x06, port->data[0], 0xf7, 0);
+				p[0] |= 0x06;
+				p[1] = port->data[0];
+				p[2] = 0xf7;
 				break;
 			case STATE_SYSEX_2:
-				f_midi_transmit_packet(req,
-					p0 | 0x07, port->data[0],
-					port->data[1], 0xf7);
+				p[0] |= 0x07;
+				p[1] = port->data[0];
+				p[2] = port->data[1];
+				p[3] = 0xf7;
 				break;
 			}
-			port->state = STATE_UNKNOWN;
-			break;
-		}
-	} else if (b >= 0x80) {
-		port->data[0] = b;
-		if (b >= 0xc0 && b <= 0xdf)
-			port->state = STATE_1PARAM;
-		else
-			port->state = STATE_2PARAM_1;
-	} else { /* b < 0x80 */
-		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);
-			break;
-		case STATE_2PARAM_1:
-			port->data[1] = b;
-			port->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;
+			next_state = STATE_FINISHED;
+		} else if (b < 0x80) {
+			switch (port->state) {
+			case STATE_1PARAM:
+				if (port->data[0] < 0xf0)
+					p[0] |= port->data[0] >> 4;
+				else
+					p[0] |= 0x02;
+
+				p[1] = port->data[0];
+				p[2] = b;
+				next_state = STATE_FINISHED;
+				break;
+			case STATE_2PARAM_1:
+				port->data[1] = b;
+				next_state = STATE_2PARAM_2;
+				break;
+			case STATE_2PARAM_2:
+				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;
+				next_state = STATE_FINISHED;
+				break;
+			case STATE_SYSEX_0:
+				port->data[0] = b;
+				next_state = STATE_SYSEX_1;
+				break;
+			case STATE_SYSEX_1:
+				port->data[1] = b;
+				next_state = STATE_SYSEX_2;
+				break;
+			case STATE_SYSEX_2:
+				p[0] |= 0x04;
+				p[1] = port->data[0];
+				p[2] = port->data[1];
+				p[3] = b;
+				next_state = STATE_SYSEX_0;
+				break;
 			}
-			f_midi_transmit_packet(req,
-				p0, port->data[0], port->data[1], b);
-			break;
-		case STATE_SYSEX_0:
-			port->data[0] = b;
-			port->state = STATE_SYSEX_1;
-			break;
-		case STATE_SYSEX_1:
-			port->data[1] = b;
-			port->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;
-			break;
+		} else {
+			/*
+			 * Wrong MIDI message
+			 * Reset state
+			 */
+			next_state = STATE_INITIAL;
+			port->data[0] = 0;
+			port->data[1] = 0;
 		}
+		break;
+	}
 	}
+
+	/* States where we have to write into the USB request */
+	if (next_state == STATE_FINISHED ||
+	    next_state == STATE_SYSEX_0) {
+		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)
@@ -632,7 +671,7 @@ static int f_midi_in_open(struct snd_rawmidi_substream *substream)
 
 	VDBG(midi, "%s()\n", __func__);
 	midi->in_substream[substream->number] = substream;
-	midi->in_port[substream->number]->state = STATE_UNKNOWN;
+	midi->in_port[substream->number]->state = STATE_INITIAL;
 	return 0;
 }
 
-- 
2.5.0

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [next] | [standalone]


#1296912

FromClemens Ladisch <clemens@ladisch.de>
Date2015-12-22 18:20 +0100
Message-ID<qIyLn-6NF-9@gated-at.bofh.it>
In reply to#1296874
Felipe F. Tonello wrote:
> This refactor includes the following:
>  * Cleaner state machine code;

It does not correctly handle system real time messages inserted between
the status and data bytes of other messages.

>  * Reset state if MIDI message parsed is non-conformant;

Why?  In a byte stream like "C1 C3 44", where the data byte of the first
message was lost, the reset would also drop the second message.

>  * Fixed bug when a conformant MIDI message was followed by a non-conformant
>    causing the MIDI-USB message to use old temporary data (port->data[0..1]),
>    thus packing a wrong MIDI-USB request.

Running status is feature.


Regards,
Clemens
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1297402

FromFelipe Ferreri Tonello <eu@felipetonello.com>
Date2015-12-23 13:00 +0100
Message-ID<qIQfg-G2-11@gated-at.bofh.it>
In reply to#1296912

[Multipart message — attachments visible in raw view] — view raw

Hi Clemens,

On 22/12/15 17:10, Clemens Ladisch wrote:
> Felipe F. Tonello wrote:
>> This refactor includes the following:
>>  * Cleaner state machine code;
> 
> It does not correctly handle system real time messages inserted between
> the status and data bytes of other messages.

True, thanks for pointing that out. I fixed that on next revision of
this patch.

> 
>>  * Reset state if MIDI message parsed is non-conformant;
> 
> Why?  In a byte stream like "C1 C3 44", where the data byte of the first
> message was lost, the reset would also drop the second message.

True. That was fixed as well.

> 
>>  * Fixed bug when a conformant MIDI message was followed by a non-conformant
>>    causing the MIDI-USB message to use old temporary data (port->data[0..1]),
>>    thus packing a wrong MIDI-USB request.
> 
> Running status is feature.

What do you mean by that? I don't qualify writing a *wrong* MIDI-USB
packet because of a previous MIDI message as a feature.

For instance, try this MIDI message:
"8A 54 24 00 40"

It will be converted to MIDI-USB as "08 8A 54 24 08 8A 00 40" which is
wrong. It should only be "08 8A 54 24" and ignore the "00 40" MIDI bytes.

On every state byte the message should basically reset data[0..1] to
zero overwriting previous data. This should also be true when a MIDI-USB
packet is complete.

Felipe

[toc] | [prev] | [next] | [standalone]


#1297668

FromClemens Ladisch <clemens@ladisch.de>
Date2015-12-23 23:00 +0100
Message-ID<qIZBV-6w1-5@gated-at.bofh.it>
In reply to#1297402
Felipe Ferreri Tonello wrote:
>> Running status is feature.
>
>What do you mean by that?

That this behavior is intended, and required.

> I don't qualify writing a *wrong* MIDI-USB
>packet because of a previous MIDI message as a feature.

The MIDI Specification qualifies Running Status as a feature.


Regards,
Clemens

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1296933

FromFelipe Balbi <balbi@ti.com>
Date2015-12-22 18:50 +0100
Message-ID<qIzep-6Xt-9@gated-at.bofh.it>
In reply to#1296874

[Multipart message — attachments visible in raw view] — view raw

Hi,

"Felipe F. Tonello" <eu@felipetonello.com> writes:
> This refactor includes the following:
>  * Cleaner state machine code;
>  * Reset state if MIDI message parsed is non-conformant;
>  * Fixed bug when a conformant MIDI message was followed by a non-conformant
>    causing the MIDI-USB message to use old temporary data (port->data[0..1]),
>    thus packing a wrong MIDI-USB request.

we don't do more than one logical thing per patch. Please split this up.

-- 
balbi

[toc] | [prev] | [next] | [standalone]


#1297291

FromFelipe Ferreri Tonello <eu@felipetonello.com>
Date2015-12-23 10:30 +0100
Message-ID<qINU6-7NH-17@gated-at.bofh.it>
In reply to#1296933

[Multipart message — attachments visible in raw view] — view raw

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

Hi Balbi,

On 22/12/15 17:49, Felipe Balbi wrote:
> 
> Hi,
> 
> "Felipe F. Tonello" <eu@felipetonello.com> writes:
>> This refactor includes the following: * Cleaner state machine
>> code; * Reset state if MIDI message parsed is non-conformant; *
>> Fixed bug when a conformant MIDI message was followed by a
>> non-conformant causing the MIDI-USB message to use old temporary
>> data (port->data[0..1]), thus packing a wrong MIDI-USB request.
> 
> we don't do more than one logical thing per patch. Please split
> this up.

Actually this patch has only one logical change, the state machine
refactoring. But by doing it, those three items were a consequence.

Felipe
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQIcBAEBCAAGBQJWemc+AAoJEMxEtNCSaY5qpXwP/1B/sVsfapRtP4dw93YF6En5
w/ej9JatIyJIaXNxauCVzgRl9uuiXGyEqErRjJdodyvHar3yKvD9HEXE6MhowEp4
JMD/phN2v9Sdj1VxRf9Z0XDSWsg6huVhfQU47HBMRGiY8ezvEgir2bvg7dYbZMsv
ACgIx8oh6N/AEHPq9GoLbEpXfJ58Pl564Sq/2o6wWsJQS06A+jp+JmqK4Y3eB5M5
18rmLW7lQLcZPO08Pf/c6BExWQLbzY/mT8KofwycvC9hWxYp9LPwJY4oMzOWROe8
AZS1ayRqlabG3qx3dPRcV4j6c6uROEfQY+HejCn+Zbi0CfVYtsI+xO5LkwnZMUyc
0qvy90h0PUQ2e37/wo5YnnVLK0ce1Gm6gY50iFXwqE69m55KHTufsIVX4eTUBfcj
PtugPtTnKEsW171r/gHPYO9A+WKxycCvjPs9Wogsljly+NrRzgPMAI7Alx//4lwq
QhwRWF1BkNOoCPEpHnVlLkcIhgdcAbbnvWxlcAVlLAQ/oYl6ShbQFv96y/2IWCVO
Mwr7Ab8a/dnyJ/GWEQdpJUmfKGQbKNpM93H5yD4AQlmz4Hj620gzm3y2XNJY2bUt
8H37Q2VWtlcRy2UHjgBSeF0YKCvdDuDmZwRZbPpajkmbcYSGtJFDve1/L6bOtZSj
7nVfYIqvtIBWrDZ3PF9G
=3uC5
-----END PGP SIGNATURE-----

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web