Bus timeout on K20 USB host transfers

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Bus timeout on K20 USB host transfers

1,836 Views
avelinoherrera
Contributor II

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!!

Labels (2)
Tags (2)
0 Kudos
8 Replies

1,425 Views
mjbcswitzerland
Specialist V

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

1,425 Views
avelinoherrera
Contributor II

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!

0 Kudos

1,425 Views
mjbcswitzerland
Specialist V

Avelino

The data token transmitted is controlled by the DATA_1 flag in the BD control word.

Regards

Mark

1,425 Views
avelinoherrera
Contributor II

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!!

0 Kudos

1,424 Views
mjbcswitzerland
Specialist V

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

0 Kudos

1,424 Views
avelinoherrera
Contributor II

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!!

0 Kudos

1,424 Views
mjbcswitzerland
Specialist V

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

1,426 Views
avelinoherrera
Contributor II

Thanks for your help!

0 Kudos