Hi all,
I am writting a simple USB host example code from scratch (without libraries). A minimalistic host that detects a USB device plugged, sends a SET_ADDRESS(1) setup packet (with all seems to be ok until here) and runs into "bus timeout" when send a second SET_CONFIGURATION setup packet:
void usbHostInit() {
// usbotg clock gate enable
SIM_SCGC4 |= 0x00040000;
// hard reset the usb module
USB0_USBTRC0 |= 0x80;
while (USB0_USBTRC0 & 0x80)
;
// enable usb transceiver (disable suspend state)
USB0_USBCTRL &= 0x7F;
// clear interrupt flags
USB0_ISTAT = 0xFF;
USB0_ERRSTAT = 0xFF;
USB0_OTGISTAT = 0xFF;
USB0_USBTRC0 |= 0x40; // undocumented
// configure both d+ and d- pulldown resistors
USB0_OTGCTL = 0x30;
// enable ATTACH interrupt
USB0_INTEN |= 0x40;
// enable usbotg irq
NVIC_SER[USBOTG_IRQ >> 5] = (1ULL << (USBOTG_IRQ & 0x1F));
// enable host mode
USB0_CTL |= 0x08;
// disable SOF generation
USB0_CTL &= 0xFE;
// configure BDT
memset((void *) usbBufferDescriptorTable, 0, 512);
USB0_BDTPAGE1 = (((uint32_t) usbBufferDescriptorTable) >> 8) & 0x000000FF;
USB0_BDTPAGE2 = (((uint32_t) usbBufferDescriptorTable) >> 16) & 0x000000FF;
USB0_BDTPAGE3 = (((uint32_t) usbBufferDescriptorTable) >> 24) & 0x000000FF;
usbHostStatus = USB_HOST_STATUS_IDLE;
}
void usbISR() {
uint8_t istat = USB0_ISTAT;
if (istat & 0x40) {
// usb device attached
// disable all usb interrupts
USB0_INTEN = 0x00;
// set packet size to 8 bytes (minimum)
USB0_SOFTHLD = 18;
// check device speed
if (!(USB0_CTL & 0x80)) {
// low speed device
USB0_ADDR = 0x80;
// host without hub = 1
USB0_ENDPT0 = 0x80;
}
else {
// full speed device
USB0_ADDR = 0x00;
USB0_ENDPT0 = 0x00;
}
// send usb reset for 10 ms
USB0_CTL |= 0x10;
wait10ms();
USB0_CTL &= 0xEF;
// start generation of SOF packets
USB0_CTL |= 0x01;
// set device address to 0 (keep low speed bit)
USB0_ADDR &= 0x80;
USB0_ENDPT0 |= 0x1D; // txen, rxen, enable setup transfers
// configure endpoint 0 for tx a SET_ADDRESS(1) setup token
UsbSetupPacket *setupPacket = (UsbSetupPacket *) txBuffer;
setupPacket->bmRequestType = 0x00;
setupPacket->bRequest = 0x05; // 5 = set_address
setupPacket->wValue = 1; // 1 = address
setupPacket->wIndex = 0;
setupPacket->wLength = 0;
usbBufferDescriptorTable[0].evenTx.buffer = (uint8_t *) txBuffer;
usbBufferDescriptorTable[0].evenTx.r = (((uint32_t) 8) << 16) | 0x80; // 8 bytes, data0, own
USB0_TOKEN = 0xD0; // setup token
// enable transfer completed and disable attach interrupt
USB0_INTEN = 0x08;
usbHostStatus = USB_HOST_STATUS_WAIT_SET_ADDRESS_SETUP_TX;
USB0_ISTAT = 0x40;
}
else if (istat & 0x08) {
// usb transfer completed
if (usbHostStatus == USB_HOST_STATUS_WAIT_SET_ADDRESS_SETUP_TX) {
usbBufferDescriptorTable[0].evenRx.buffer = (uint8_t *) rxBuffer;
usbBufferDescriptorTable[0].evenRx.r = (((uint32_t) 0) << 16) | 0xC0; // 0 bytes, data1, own
USB0_TOKEN = 0x90; // in token
usbHostStatus = USB_HOST_STATUS_WAIT_SET_ADDRESS_IN_RX;
}
else if (usbHostStatus == USB_HOST_STATUS_WAIT_SET_ADDRESS_IN_RX) {
// ...
// AT THIS POINT I GET A DATA1 PID FROM BUFFER DESCRIPTOR (I THINK THAT IS CORRECT, IS'N IT?)
// ...
// configure device address = 1
USB0_ADDR |= 0x01;
// reset to even banks again (the "bus timeout" appears reseting to even banks or not reseting at all)
USB0_CTL |= 0x02;
// send SET_CONFIGURATION(1) setup packet
UsbSetupPacket *setupPacket = (UsbSetupPacket *) txBuffer;
setupPacket->bmRequestType = 0x00;
setupPacket->bRequest = 0x09; // 9 = set_configuration
setupPacket->wValue = 1; // 1st configuration
setupPacket->wIndex = 0;
setupPacket->wLength = 0;
usbBufferDescriptorTable[0].evenTx.buffer = (uint8_t *) txBuffer;
usbBufferDescriptorTable[0].evenTx.r = (((uint32_t) 8) << 16) | 0x80; // 8 bytes, data0, own
// setup token
USB0_TOKEN = 0xD0;
usbHostStatus = USB_HOST_STATUS_WAIT_SET_CONFIGURATION_SETUP_TX;
}
else if (usbHostStatus == USB_HOST_STATUS_WAIT_SET_CONFIGURATION_SETUP_TX) {
// ...
// BUT HERE I GET A BUS TIMEOUT PID FROM BUFFER DESCRIPTOR (TOK_PID = 0) :-(
// ...
usbBufferDescriptorTable[0].evenRx.buffer = (uint8_t *) rxBuffer;
usbBufferDescriptorTable[0].evenRx.r = (((uint32_t) 0) << 16) | 0xC0; // 0 bytes, data1, own
USB0_TOKEN = 0x90; // in token
usbHostStatus = USB_HOST_STATUS_WAIT_SET_CONFIGURATION_IN_RX;
}
else if (usbHostStatus == USB_HOST_STATUS_WAIT_SET_CONFIGURATION_IN_RX) {
// configure endpoint 1
USB0_ENDPT1 = 0x19; // rxen
// ...
// ...
usbHostStatus = USB_HOST_STATUS_IDLE_CONFIGURED;
}
USB0_ISTAT = 0x08;
}
else
USB0_ISTAT = 0xFF;
}
I have tested different setup commands and always get the same error: The first setup packet returns a PID of a non error but the second setup packet generates a TOK_PID value of 0x00 (bus timeout) instead of DATA0/DATA1.
Can any body help me? Thanks in advance!!
Hi Avelino
Each time the USB host sends a new SETUP it needs the data token to be re-synchronised since the next transmitted one is DATA0 and the next received one will be DATA1.
If you have incorrect data tokens being sent or expected the transfers will fail, which is presumably what is happening.
It makes sense to monitor the operation with a USB analyser since such problems will be easily to identify.
You can also take a look at the uTasker project since it contains USB host mode operation and emulation of this and the USB host controller which allows the driver code to be checked: It will immediately signal problems with data toggling errors.
This is what the emulation of the connection of a (non-formatted) memory stick looks like:
USB FS device detected
USB device information ready:
USB2.0 device with 64 byte pipe
Vendor/Product = 0x1d0d/0x0213
Manufacturer = "TDKMedia"
Product = "CDC "
Serial Number = "12345"
Bus-powered device (max. 200mA) with 1 interface(s)
Mass Storage Class : Sub-class = 0x06 interface protocol = 0x50
Endpoints:
2 = BULK IN with size 64
2 = BULK OUT with size 64
3 = INTERRUPT IN with size 2 (polling interval = 1ms)
Enumerated (1)
LUN = 1
UFI INQUIRY -> Status transport - Passed
UFI REQUEST SENSE -> Status transport - Passed
UFI FORMAT CAP. -> (3:512:16777216) Status transport - Passed
UFI READ CAP. -> (16785408:1016575) Status transport - Passed
Mem-Stick mounting...
Non-formatted Memory stick
whereby the endpoint 0 transmissions during the enumeration phase are:
SETUP [DATA0] 0x80 0x06 0x00 0x01 0x00 0x00 0x12 0x00
[DATA1] ZERO-DATA
SETUP [DATA0] 0x00 0x05 0x01 0x00 0x00 0x00 0x00 0x00
SETUP [DATA0] 0x80 0x06 0x00 0x02 0x00 0x00 0xFF 0x00
[DATA1] ZERO-DATA
SETUP [DATA0] 0x80 0x06 0x00 0x03 0x00 0x00 0x21 0x00
[DATA1] ZERO-DATA
SETUP [DATA0] 0x80 0x06 0x01 0x03 0x09 0x04 0x21 0x00
[DATA1] ZERO-DATA
SETUP [DATA0] 0x80 0x06 0x02 0x03 0x09 0x04 0x21 0x00
[DATA1] ZERO-DATA
SETUP [DATA0] 0x80 0x06 0x03 0x03 0x09 0x04 0x21 0x00
[DATA1] ZERO-DATA
SETUP [DATA0] 0x00 0x09 0x01 0x00 0x00 0x00 0x00 0x00
SETUP [DATA0] 0xA1 0xFE 0x00 0x00 0x00 0x00 0x01 0x00
[DATA1] ZERO-DATA
If you use the open source version as reference you can either cut out the parts of the enumeration state-machine that you don't want or copy its operation to your environment.
The minimal frames that you are sending would be :
SETUP [DATA0] 0x00 0x05 0x01 0x00 0x00 0x00 0x00 0x00
and
SETUP [DATA0] 0x00 0x09 0x01 0x00 0x00 0x00 0x00 0x00
Regards
Mark
Kinetis: http://www.utasker.com/kinetis.html
Kinetis K20:
- http://www.utasker.com/kinetis/FRDM-K20D50M.html
- http://www.utasker.com/kinetis/TWR-K20D50M.html
- http://www.utasker.com/kinetis/TWR-K20D72M.html
- http://www.utasker.com/kinetis/TEENSY_3.1.html
- http://www.utasker.com/kinetis/tinyK20.html
Free Open Source solution: https://github.com/uTasker/uTasker-Kinetis
Working project in 15 minutes video: https://youtu.be/K8ScSgpgQ6M
For better, faster, cheaper product developments consider the uTasker developer's version, professional Kinetis support, one-on-one training and complete fast-track project solutions to set you apart from the herd : http://www.utasker.com/support.html
Hi Mark,
Thank you very much for your response! The only way to generate DATA0 or DATA1 packets is writting the descriptor table just before writting USB0_TOKEN, isn't it? I will attempt to read the uTasker project.
Thanks again!
Avelino
The data token transmitted is controlled by the DATA_1 flag in the BD control word.
Regards
Mark
Hi Mark,
That is what I am doing when sending the SET_ADDRESS(1) setup packet here:
usbBufferDescriptorTable[0].evenTx.r = (((uint32_t) 8) << 16) | 0x80; // 8 bytes, data0, own
when receiving the empty ACK DATA1 packet here:
usbBufferDescriptorTable[0].evenRx.r = (((uint32_t) 0) << 16) | 0xC0; // 0 bytes, data1, own
when sending the SET_CONFIGURATION(1) setup packet here:
usbBufferDescriptorTable[0].evenTx.r = (((uint32_t) 8) << 16) | 0x80; // 8 bytes, data0, own
and when receiving the corresponding empty ACK DATA1 packet here:
usbBufferDescriptorTable[0].evenRx.r = (((uint32_t) 0) << 16) | 0xC0; // 0 bytes, data1, own
I don't know what I am doing wrong :-/. Tranks in advance!!
Avelino
The TX buffers work in an alternating pattern - therefore you need to send the second OUT data in the oddTx buffer and not the even one (unless you specifically reset the buffer usage).
Beware that the ODD and EVEN buffer usage is not the same as the ODD and EVEN data usage. The first is more a software concept and the second is related to the USB protocol (one can send DATA_0 or DATA_1 types in either ODD or EVEN buffers - it is the buffer descriptor's control word that defines this).
I have attached a short video showing its operation when just the address and configuration are set (I manipulate the operation to just do these two steps and it is seen that the tx buffer swaps from even to odd).
Regards
Mark
Hi Mark,
The error occurs both when reuse the "even" bank or when using the "odd" bank. In the code I have attached to the question I reuse the "even" bank by inserting this code:
// reset to even banks again (the "bus timeout" appears reseting to even banks or not reseting at all)
USB0_CTL |= 0x02;
Just before transmit second SET_CONFIGURATION(1) setup packet. But the problem persists even if I ommit this line and use the "odd" banks.
Thank you!!
Hi Avelino
Low level USB is fairly tricky (especially with the USB controller in the Coldfire/Kinetis parts) so I think that if you need to develop a new stack it makes sense in investing in a good USB analyser which will aid in working out what is and what isn't working since presently it sounds as though you are working blind.
If you need a USB application there are existing solution from uTasker or NXP or Segger (low cost professional or free open source/free/expensive professional). If you are developing professionally you can also get support to debug and solve your own code in case this is the solution that is necessary [http://www.utasker.com/services.html]
Regards
Mark
Thanks for your help!