Author Topic: [Code] USB Device FS driver on GD32 (bare metal)  (Read 2975 times)

0 Members and 1 Guest are viewing this topic.

Offline SaimounTopic starter

  • Frequent Contributor
  • **
  • Posts: 550
  • Country: dk
[Code] USB Device FS driver on GD32 (bare metal)
« on: October 22, 2022, 11:19:00 am »
Hi all :)

Just started to write USB Device FS driver for GD32 (it will probably work similarly on STM32 since the registers are literally the same).

The device is a MIDI device, able to both send and receive MIDI over USB.
I am not using any library, only GD32 registers.

I thought I will write and share my journey here.

Simon
« Last Edit: October 22, 2022, 11:34:30 am by simonlasnier »
 

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3712
  • Country: nl
Re: [Code] USB Device FS driver on GD32 (from scratch)
« Reply #1 on: October 22, 2022, 11:25:21 am »
Hi Simon,

this might be of some help. https://github.com/pecostm32/STM32F103C8_USB_CH340

I wrote this to emulate a CH340 on a STM32F103 as bare metal implementation. For midi replace the descriptor with the correct one and fill in the needed in and out points.

Success,

Peter

Offline SaimounTopic starter

  • Frequent Contributor
  • **
  • Posts: 550
  • Country: dk
Re: [Code] USB Device FS driver on GD32 (from scratch)
« Reply #2 on: October 22, 2022, 11:34:10 am »
Starting now - I am using Alex's USB analyzer for... well, analyzing.  ;D
=> https://github.com/ataradov/usb-sniffer-lite for those who do not know about it

(By the way amazing work Alex @ataradov - this is super useful  :-+

I guess all it's missing is a nice GUI looking at the data and presenting it in a "nicer" way instead of having to manually read a bunch of numbers ;) If anybody feels like making a GUI simply reading the data it would already be a huge plus - then at least being able to quickly check the "fields" in the descriptors would be awesome)


So, looking at a similar device (a USB-MIDI keyboard), I can see:
* The host starts by giving it an address
* Then asks for a bunch of stuff, device descriptor, strings, config descriptor

Slightly strange thing the "bmAttributes" in the config descriptor is set to zero (while I though bit 7 should always be 1).

Then the host sends a "set configuration" - makes sense.

After that I can see regular IN packets on endpoint 4 which is the MIDI IN endpoint - so it seems it's operational from there on.
 

Offline SaimounTopic starter

  • Frequent Contributor
  • **
  • Posts: 550
  • Country: dk
Re: [Code] USB Device FS driver on GD32 (from scratch)
« Reply #3 on: October 22, 2022, 11:36:41 am »
Hi Simon,

this might be of some help. https://github.com/pecostm32/STM32F103C8_USB_CH340

I wrote this to emulate a CH340 on a STM32F103 as bare metal implementation. For midi replace the descriptor with the correct one and fill in the needed in and out points.

Success,

Peter

Gosh man that is amazing - I have been looking for something like this for a while!! Thanks!  ;D
 

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3712
  • Country: nl
Re: [Code] USB Device FS driver on GD32 (bare metal)
« Reply #4 on: October 22, 2022, 11:41:08 am »
You are welcome.

Here is another that you might find helpful. https://www.beyondlogic.org/usbnutshell/usb1.shtml

For checking USB I use wireshark with USB enabled. But I use it on linux. No idea if it works on windows.

Offline SaimounTopic starter

  • Frequent Contributor
  • **
  • Posts: 550
  • Country: dk
Re: [Code] USB Device FS driver on GD32 (bare metal)
« Reply #5 on: October 22, 2022, 02:30:59 pm »
Ok here is something I do not understand from the enumeration. After setting the configuration, the host starts requesting MIDI Data as I wrote before.

But then this shows up:

Code: [Select]
   664 : SETUP: 0x0b/0
   667 : DATA0 (8): 81 06 00 22 00 00 34 00
   676 : ACK
   678 : IN: 0x0b/4
   681 : NAK
   690 : IN: 0x0b/0
   693 : NAK
   699 : IN: 0x0b/4
   702 : NAK
   704 : IN: 0x0b/0
   707 : DATA1 (32): 06 a0 ff 09 01 a1 01 09 02 a1 00 06 a1 ff 09 03 09 04 15 18 25 7f 35 00 45 ff 75 08 95 20 81 02
   732 : ACK
   735 : IN: 0x0b/4
   738 : NAK
   771 : IN: 0x0b/0
   774 : DATA0 (20): 09 05 09 06 15 80 25 7f 35 00 45 ff 75 08 95 20 91 02 c0 c0
   791 : ACK
   794 : IN: 0x0b/4
   797 : NAK
   805 : OUT: 0x0b/0
   808 : DATA1: ZLP
   811 : ACK

I am confused, what is the host asking for?
"81 06 00 22 00 00 34 00" means get descriptor (06) sent to "interface" (81)? and the descriptor type is 0x22=34?

Clearly I must be missing something because the device sent the 0x34=52 bytes of whatever that descriptor is.
 

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3712
  • Country: nl
Re: [Code] USB Device FS driver on GD32 (bare metal)
« Reply #6 on: October 22, 2022, 03:54:21 pm »
It has been a while, but the host wants to know more about the interface.

Within the device descriptor you probably listed one or more interfaces, and for these you have to supply the host with what they are for.

Try and play with my code to see how things work for a simple USB to serial device. There are lots of comment in the code about what is for what.

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11269
  • Country: us
    • Personal site
Re: [Code] USB Device FS driver on GD32 (bare metal)
« Reply #7 on: October 22, 2022, 05:01:26 pm »
It looks like your device also has HID interface. 0x22 is HID report descriptor and your data parses well as a report descriptor (using https://eleccelerator.com/usbdescreqparser/):

Code: [Select]
0x06, 0xA0, 0xFF,  // Usage Page (Vendor Defined 0xFFA0)
0x09, 0x01,        // Usage (0x01)
0xA1, 0x01,        // Collection (Application)
0x09, 0x02,        //   Usage (0x02)
0xA1, 0x00,        //   Collection (Physical)
0x06, 0xA1, 0xFF,  //     Usage Page (Vendor Defined 0xFFA1)
0x09, 0x03,        //     Usage (0x03)
0x09, 0x04,        //     Usage (0x04)
0x15, 0x18,        //     Logical Minimum (24)
0x25, 0x7F,        //     Logical Maximum (127)
0x35, 0x00,        //     Physical Minimum (0)
0x45, 0xFF,        //     Physical Maximum (-1)
0x75, 0x08,        //     Report Size (8)
0x95, 0x20,        //     Report Count (32)
0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0x05,        //     Usage (0x05)
0x09, 0x06,        //     Usage (0x06)
0x15, 0x80,        //     Logical Minimum (-128)
0x25, 0x7F,        //     Logical Maximum (127)
0x35, 0x00,        //     Physical Minimum (0)
0x45, 0xFF,        //     Physical Maximum (-1)
0x75, 0x08,        //     Report Size (8)
0x95, 0x20,        //     Report Count (32)
0x91, 0x02,        //     Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0,              //   End Collection
0xC0,              // End Collection

// 52 bytes
Alex
 
The following users thanked this post: Saimoun

Offline SaimounTopic starter

  • Frequent Contributor
  • **
  • Posts: 550
  • Country: dk
Re: [Code] USB Device FS driver on GD32 (bare metal)
« Reply #8 on: October 22, 2022, 05:45:17 pm »
It looks like your device also has HID interface. 0x22 is HID report descriptor and your data parses well as a report descriptor (using https://eleccelerator.com/usbdescreqparser/):

Spot on, it does indeed have an HID interface (no idea why a MIDI keyboard would have an HID interface but I am sure they have their reasons... the device is an AKAI MPK mini).

I did not know the host could ask for interface specific stuff on EP 0 as "standard" requests (bmRequestType bits 6-5 = 00), I would have expected a class specific request (bmRequestType bits 6-5 = 01).

Unrelated to this - really cool tool this https://eleccelerator.com/usbdescreqparser/ . I was precisely looking for something like this use with your sniffer. Maybe you should put that link on the readme on github ;)
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11269
  • Country: us
    • Personal site
Re: [Code] USB Device FS driver on GD32 (bare metal)
« Reply #9 on: October 22, 2022, 06:08:52 pm »
I did not know the host could ask for interface specific stuff on EP 0 as "standard" requests (bmRequestType bits 6-5 = 00), I would have expected a class specific request (bmRequestType bits 6-5 = 01).
HID reports are always requested as standard. No idea why. HID is the worst class in USB. Probably because it was the first one and they had no good structure in place yet.

Maybe you should put that link on the readme on github ;)
This is a very well known tool. But  I've added the link, it can't hurt.
Alex
 

Offline SaimounTopic starter

  • Frequent Contributor
  • **
  • Posts: 550
  • Country: dk
Re: [Code] USB Device FS driver on GD32 (from scratch)
« Reply #10 on: October 22, 2022, 09:35:33 pm »
this might be of some help. https://github.com/pecostm32/STM32F103C8_USB_CH340
Thanks so much Peter for sharing that code. I've been looking at tons of other code examples, libraries, which tend to be long and complex because they try to catch every single usage. So really happy to have found your code, much more similar to my way of coding: bare bone, simple, and only sticking to the essential. SO much easier to understand, and great comments by the way :)

But I would say you even simplified too much - wondering why you decided not to handle these requests?

USB_REQUEST_GET_STATUS
USB_REQUEST_CLEAR_FEATURE
USB_REQUEST_SET_FEATURE   
USB_REQUEST_SET_DESCRIPTOR   
USB_REQUEST_GET_CONFIGURATION
USB_REQUEST_GET_INTERFACE     
USB_REQUEST_SET_INTERFACE     
USB_REQUEST_SYNC_FRAME         

And more importantly, what will happen if the device receives one of them? Wouldn't the hardware just send NAK forever? Isn't it better to configure to send STALL when one of these happens? (then at least the host knows this is not implemented)

Thanks!
 
The following users thanked this post: pcprogrammer

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11269
  • Country: us
    • Personal site
Re: [Code] USB Device FS driver on GD32 (bare metal)
« Reply #11 on: October 22, 2022, 10:25:43 pm »
And more importantly, what will happen if the device receives one of them? Wouldn't the hardware just send NAK forever?
Compliant device would send STALL indicating that request is not supported and the host should be able to handle it.

I don't implement most of this stuff in any of my stacks and it works fine.

I implement those things when necessary. For example if you actually have a device with multiple interfaces, you will have to implement get/set interface. Otherwise, why bother?
Alex
 
The following users thanked this post: Saimoun

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3712
  • Country: nl
Re: [Code] USB Device FS driver on GD32 (bare metal)
« Reply #12 on: October 23, 2022, 04:49:56 am »
Like ataradov wrote, why bother?

The host won't ask for them if the device descriptor does not list them as being used. Part of it also depends on the driver on the host. But like I wrote in my other post it has been a while that I wrote the code. I do know that it took some effort to distill it from the code generated by MXcube and other examples I found on the net. It also being my first experience with USB it might well be cut a bit to much. :)

I removed as much clutter as possible and still have it work. Developing an USB device is not easy due to a lot of rules to follow and timing that can't be stretched to much, so no debugging by single stepping.


Offline SaimounTopic starter

  • Frequent Contributor
  • **
  • Posts: 550
  • Country: dk
Re: [Code] USB Device FS driver on GD32 (bare metal)
« Reply #13 on: October 23, 2022, 11:46:25 am »
I removed as much clutter as possible and still have it work.
You did, and did great :)

Thanks to you both, and yes that makes sense. All I needed to know was what a compliant device would do (stall as I understood), as I do intend to be compliant (eventually).

Regarding the USB_REQUEST_SET_DESCRIPTOR - such a weird command which I guess no host will ever send >_< - what would the compliant way be? Because the GD32 hardware always replies ACK to a SETUP packet, so it seems the only way I can "refuse" it is by sending STALL when the hosts tries to send the descriptor? Or should I just ACK the descriptor and then ignore it?

Thanks
 

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 3712
  • Country: nl
Re: [Code] USB Device FS driver on GD32 (bare metal)
« Reply #14 on: October 23, 2022, 11:58:07 am »
It is a strange command indeed. Your device would need to be programmable in some way to be able to start using a new descriptor. Have not really thought about it though :-//

I would ignore it and just implement what is needed for MIDI. Try to find a description for the USB standard for it if it exists, and stick with implementing that.

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11269
  • Country: us
    • Personal site
Re: [Code] USB Device FS driver on GD32 (bare metal)
« Reply #15 on: October 23, 2022, 05:17:02 pm »
Because the GD32 hardware always replies ACK to a SETUP packet,
This is the standard. You can't send anything else.

so it seems the only way I can "refuse" it is by sending STALL
This is also the standard. You are supposed to send STALL for any command you don't support.
Alex
 
The following users thanked this post: Saimoun

Offline SaimounTopic starter

  • Frequent Contributor
  • **
  • Posts: 550
  • Country: dk
Re: [Code] USB Device FS driver on GD32 (bare metal)
« Reply #16 on: October 23, 2022, 05:50:39 pm »
True about the ACK response to a setup packet - my bad the GD manual actually does say it is from the standard :)

so it seems the only way I can "refuse" it is by sending STALL
This is also the standard. You are supposed to send STALL for any command you don't support.

Regarding stalling, you mean send stall on the OUT packet coming from the host, or accepting those OUT, and then when the host is requesting the zero-length packet, at that moment send STALL?
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11269
  • Country: us
    • Personal site
Re: [Code] USB Device FS driver on GD32 (bare metal)
« Reply #17 on: October 23, 2022, 06:07:05 pm »
STALL is a response token, it can't be sent by itself. In the hardware you just set a bit telling the hardware that it should send STALL next time something is requested. So, the sequence for OUT transfers would be like this: SETUP, ACK, <here you set stall request> OUT (DATA), STALL. And for IN: SETUP, ACK, <here you set stall request> IN, STALL.
Alex
 
The following users thanked this post: Saimoun

Offline SaimounTopic starter

  • Frequent Contributor
  • **
  • Posts: 550
  • Country: dk
Re: [Code] USB Device FS driver on GD32 (bare metal)
« Reply #18 on: October 23, 2022, 06:28:40 pm »
Undestood - thanks :)

My question was for the SET Descriptor, whether I should do

SETUP, ACK, OUT (descriptor), STALL (like you said)

or

SETUP, ACK, OUT (descriptor), ACK, OUT (descriptor), ... ACK, IN, STALL

Anyways - it's clear now thanks :)
 

Offline SaimounTopic starter

  • Frequent Contributor
  • **
  • Posts: 550
  • Country: dk
Re: [Code] USB Device FS driver on GD32 (bare metal)
« Reply #19 on: October 24, 2022, 09:31:44 am »
Posting my progress of the day: now got the enumeration working :D
The complex library codes and the countless people on the internet saying "oh nobody writes their own USB stack it's too complicated" make it look impossible. But actually it's really not that hard, it just takes a bit of time understand the protocol and the way the USB peripheral on the MCU handles it.

If anybody wants to write their own USB stack, I would recommend:
  • get a Raspberry Pi and install Alex's amazing sniffer
  • read https://www.usbmadesimple.co.uk/
  • read your MCU's datasheet/user manual
  • if you are using ST or GD, you can look how Peter (pcprogrammer) is handling the enumeration here: https://github.com/pecostm32/STM32F103C8_USB_CH340 (thank you so much for that)
  • Then start writing the code and debug with Alex's sniffer
  • Once this is working if you want to be USB compliant you need to start reading the USB specs

Things I had to fight with during my learnings (GD32/STM32 specific):
  • Remember that weird addressing (skip every 16 bits) on the USB internal buffer: to keep in mind everytime you need to read or write an endpoint buffer, and read or write an endpoint status register
  • Remember those bloody toggle bits in the endpoint status registers which make changing any value in those more complicated
  • I found this part of the manual (this is from ST - GD says something similar) very confusing:

    Quote from: ST manual RM0008 section 23.4.2
    System and power-on reset

    [...] the analog part of the device related to the USB transceiver must be switched on using the PDWN bit in CNTR register, which requires a special handling. [...] It is thus necessary to wait, after setting the PDWN bit in the CNTR register, before removing the reset condition on the USB part (by clearing the FRES bit in the CNTR register).
    I found that confusing because FRES is reset by default. So I thought maybe we need to set it before resetting PDWN?
    What they do not tell you is that the FRES bit is set automatically when you reset the PDWN bit.
 
The following users thanked this post: bingo600, pcprogrammer, wek


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf