Help with bare metal USB device driver

Status
Not open for further replies.
This is my first post so I apologize if it's in the wrong section.

I am trying to write a bare metal usb device driver on the Teensy3.1. I have been following Kevin Cuzner's approach http://kevincuzner.com/2014/12/12/teensy-3-1-bare-metal-writing-a-usb-driver/. Right now I am using visual studio with a makefile and the arm eabi M4 compilers (also thanks in part to work by Kevin Cuzner http://kevincuzner.com/2014/04/28/teensy-3-1-bare-metal/). At this point, I have the teensy working as a simple USB device functioning on endpoint 0 and responding to all the control and setup requests. This includes the device, configuration, interface, string, and endpoint descriptors. The device is successfully sets and returns a current configuration. I will post the descriptors below. Now, I am trying to add a BULK IN endpoint at endpoint 1. I am specifying the new endpoint 1 as a bulk IN endpoint under interface 1 in the configuration descriptor. I have also added a relevant handler in the teensy software, under the switch statement.

I have been using microsoft's message analyzer to debug the usb traffic. I am currently using the WinUSB driver on the host side, along with a c# app and ms .net device library to send and recieve low level pipe transfers (i.e. control and setup requests and bulk/interrupt transfers to various endpoints).

Here is a summary/my understanding of the code

  1. When a packet is recieved, an interrupt is fired by the USB module and the USBOTG_IRQHandler() is called
  2. The USB0_ISTAT (pg. 983) reg tells us what kind of USB packet was recieved
    • If USB_ISTAT_USBRST_MASK
      • I am enabling the USB0_ENDPT0 and USB0_ENDPT1 register on reset for rx,tx, and handshaking (when the device is plugged in).
    • If USB_ISTAT_TOKDNE_MASK (we are handling a new USB token)
      • The USB0_STAT register should contain the target endpoint index in high nibble for the current packet (pg. 988)
      • The token is processed though a switch statement on the endpoint index to the correct endpoint handler

So, my problem...

I am able to send and recieve requests to endpoint 0 (the default device control endpoint) in the vs app. However, when I try to send a BULK IN transfer request to endpoint 1, it seems like the USB IRQ is not firing. I have ommited alot of the code but below is the relevant section. Also, I have used PyUSB and a different driver (forget the name, but part of the package) to try a BULK IN transfer request on endpoint 1 and had the same issue. In message analyzer, once the packet times out the sniffer shows what looks like a legitimate request to endpoint 1 (I will also post this below)

But, when the switch is called 'switch (endpoint)', it value is never endpoint 1. When I send a BULK IN request on endpoint 1 through the vs app, the request times out, and I never receive a TOKDNE interrupt for endpoint 1. I have tested back to this point by the small 'SetPin(D13)' in the switch statement, which turns on the Teensy LED when that endpoint is addressed.

I will post code more code but wanted to try to narrow it down to the relevant sections. Sorry if this is a little vague, I'm not sure where to start. Thanks for any ideas/assistance

Code:
void USBOTG_IRQHandler(void)
{
	uint8_t status;
	uint8_t stat, endpoint;

	status = USB0_ISTAT;
	if (status & USB_ISTAT_USBRST_MASK)
	{
		//handle USB reset
                ... ....
                //initialize endpoint0 to 0x0d (41.5.23)
		//transmit, recieve, and handshake
		USB0_ENDPT0 = USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK | USB_ENDPT_EPHSHK_MASK;
                //same for endpoint 1
		USB0_ENDPT1 = USB_ENDPT_EPRXEN_MASK | USB_ENDPT_EPTXEN_MASK | USB_ENDPT_EPHSHK_MASK;
                ... ...
	}
	... ...
	
	if (status & USB_ISTAT_TOKDNE_MASK)
	{
		//handle completion of current token being processed
		//45.5.13, page 987 describes the STAT register
		//The stat register contains:
		// D7-D4: The endpoint. This also allows the processor to figure out which BDT was updated
		// D3: TX (0 = The most recent was a recieve operation)
		// D2: ODD (1 = The most recent was a transmit operation)
		// D1-0: zero
		stat = USB0_STAT;
		endpoint = (stat & USB_STAT_ENDP_MASK) >> USB_STAT_ENDP_SHIFT;
		switch (endpoint)
		{
		case 0x0u:
			usb_endp0_handler(stat);
			break;
		case 0x1u:
                        SetPin(D13);
			usb_endp1_handler(stat);
			break;
		default:
			STALL_ENDPT(0);
		}
		USB0_ISTAT = USB_ISTAT_TOKDNE_MASK;
	}
	... ...
}

This is from querying my device descriptor and configuration descriptor in the vs app. But, when I send a BULK IN request to endpoint 1, the stat register is never endpoint 1
Device Descriptor

Edit - Realized I was missing some of the device descriptor, added below


[0x12] 18
[0x01] 1
[0x00] 0
[0x02] 2
[0xFF] 255
[0x02] 2
[0x00] 0
[0x40] 64
[0xC0] 192
[0x16] 22
[0xDC] 220
[0x27] 39
[0x01] 1
[0x00] 0
[0x01] 1
[0x02] 2
[0x00] 0
[0x01] 1



Configuration Descriptor
[0x09] 9
[0x02] 2
[0x19] 25
[0x00] 0
[0x01] 1
[0x01] 1
[0x00] 0
[0x80] 128
[0xFA] 250
[0x09] 9
[0x04] 4
[0x00] 0
[0x00] 0
[0x01] 1
[0xFF] 255
[0xFF] 255
[0x00] 0
[0x00] 0
[0x07] 7
[0x05] 5
[0x81] 129
[0x02] 2
[0x40] 64
[0x00] 0
[0x0A] 10

Edit:

Here is the message analyzer log showing the canceled bulk in transfer request due to timeout in the vs app (approx 30 seconds). The address 0x81 should correspond to endpoint 1 IN
Name Value:Bit Offset Bit Length Type
TransferData: (BinaryValue)
UrbPtr: 18446708890892172592 (UInt64)
DeviceInstance: 6 (Int32)
EndpointAddress 0x81 (Byte)
Type: Bulk(2) (UsbTransferType)
Direction: In(1) (UsbDirection)
LengthCompleted: 0 (UInt32)
LengthRequested: 64 (UInt32)
NtStatus: STATUS_CANCELLED(3221225760) (NTSTATUS)
Status: USBD_STATUS_CANCELED(3221291008) (UsbdStatus)
 
Last edited:
I have a couple ideas in mind: sim racing wheel with force feedback, industrial controls, and just general data transfer. I realize that Teensy already has an excellent usb library and other solutions exist. But I wanted to go through the process myself out of curiosity and because I enjoy it. I would also like to implement the PID usb class in hopes of a "generic" force feedback device, which I havent really seen in other libraries.
 
Thanks! It's been a fun project but I've been stuck on this problem for a while. For hardware I'm using a 3 series BMW wheel with the built in wheel angle encoder and an R8 seat, both of which I pulled out of the junkyard. I have a projector in the basement but I'm debugging in the computer room. It was running successfully using the built in Teensy HID joystick libraries + analog inputs for the encoder on the PC, but now I want to add force feedback functionality. Once I get the wheel going, I am planning on a 6 DOF motion platform (but thats still a ways off).
simsetup.jpg
 
You could build your force feedback on top of the existing USB code. The several device types like keyboard, mouse, serial, touchscreen all do this. They access packets from their endpoints with usb_tx(), usb_rx() and other functions.

Of course, if you enjoy doing everything from scratch, go for it.
 
Paul - You make a good point, I more just wanted to learn USB, and look at options for a low level rtos, but feel like I have a better grasp of it now. I have looked through the teensy core as well and seen how most of the usb setup is performed, I think the teensy core is excellent. I will definitely look into building on top it.

Still, I can't figure out why my code above doesn't work... If I have enabled endpoint 1 for tx, rx and handshaking when the device is configured (and on reset), why doesn't it seem like the USBOTG_IRQ() is firing for any other endpoint than endpoint 0?

Also, I am willing to post the source if anyone is willing to take a look :) but I feel like I have narrowed down the issue to this bit of code...

However, I understand if you want to focus on use of the teensy core here... Maybe I should try an arm board? And come back here for issues related to building on the teensy usb core?
 
Still, I can't figure out why my code above doesn't work... If I have enabled endpoint 1 for tx, rx and handshaking when the device is configured (and on reset), why doesn't it seem like the USBOTG_IRQ() is firing for any other endpoint than endpoint 0?

This probably won't help with your specific problem, but I know this story well. I spent an entire frustrating month of all-nighters in mid-2012 living pretty much this same story. For quite a while it seemed as if my dream of a 32 bit Teensy would be killed by the steep learning curve of Freescale's not-so-well-documented USB controller. Similar story... I had kludgey control transfers working, at least enough to enumerate if I kept the descriptors short, but bulk and interrupt transfers on the non-zero endpoints were terribly frustrating. Getting the data token toggle and internal BDT table toggle states right was tricky. Eventually the pieces all fell into place, but it did take a *lot* of fiddling and guesswork to fill in the missing info about how the hardware state machine really works.
 
Ha thanks Paul, I'm glad to hear I'm not the only one that struggled with it. I'm still not sure which data state (DATA0, DATA1) I should be in on non zero bulk/interrupt endpoints, especially after the device has its configuration set by the host. And by the BDT toggle, I assume you mean the ping-pong buffers? Also not sure when I should be resetting the toggle bit for the first time. Right now I do it on USB reset.

I'll have to start there, and probably look at writing a PID class on top of the teensy core too. The usb packet structure you have created is really neat, I'm trying to understand it a bit better. Thanks for all the assistance and I'll update with any progress.
 
Do you have a writeup anywhere, Paul? I've just spent a couple of days with the Teensy-LC USB peripheral, and I've got the barest of bare minimum control going. But there's still some fragile stuff I don't understand. I wrote from scratch, referencing Kevin Cuzner's code and trying to make my code do the same things at the same times.

The even/odd thing is really kicking my ass. After receiving a SETUP packet on EP0, it looks like I have to configure both EP0-RX endpoints to receive, even though I _think_ I should only have to have the next one (odd->even or even->odd) ready.

I think Kevin left the USB0_CTL/ODDRST bit set so that only the even BDTs were used. I guess that's one solution, but if I'm gonna waste time figuring it out, I want to figure it _all_ out!
 
Status
Not open for further replies.
Back
Top