Electronic – Atmel USB sends STALL packet after 1 minute

atmelavrusb

After one minute of time (inactive or active) the USB stack on my Atmel SAM4E-EK sends a USB STALL packet which basically kills the USB connection with the host. How can I prevent this from happening?

The host is a Windows 7 and the status of the USB packet received on the host (PC) is 0xC0000004 from endpoint address 0x84 (UDI_CDC_DATA_EP_IN_0) and one with the same status from 0x83 (UDI_CDC_COMM_EP_0). The USB device is a USB composite device with CDC and MSC enabled.

Atmel has a example project which is also a USB Composite Device with MSC + CDC and this too times out after a couple of minutes (3-4 minutes).

Best Answer

I got similar problems with ATMEL AVR32 USB. I derived my USB class from the CDC example and AVR-SoftwareFramework-2.3.1 and I found out that they got a bug (most likely on the HW somewhere on the chip). In the example they call flush for every NB_MS_BEFORE_FLUSH-th SoF (Start of Frame) which in some cases (mostly when no communication is done for some time) will freeze Is_device_enumerated() call which is done in the same CDC task function (in next iteration) to the point the chip stop executing instructions,interrupts etc until HW reset is done.

My original derived code was like this (sorry I do not have the original CDC example code anymore):

#define NB_MS_BEFORE_FLUSH          100
void usb_device::flush()        { bool zlp=false; if(!Is_usb_write_enabled(TX_EP)) zlp=true; Usb_ack_in_ready_send(TX_EP); b_tx_new = true; if(zlp==true) { while(!Is_usb_write_enabled(TX_EP)); Usb_ack_in_ready_send(TX_EP); } }
void device_cdc_task(void)
    {
    if (!Is_device_enumerated()) { _reset_fifos=true; return; }
    if (_reset_fifos)
        {
        _reset_fifos=false;
        usb.fifo_send.reset();
        usb.fifo_recv.reset();
        Usb_reset_endpoint_fifo_access(RX_EP);
        Usb_reset_endpoint_fifo_access(TX_EP);
        }
    if (sof_cnt>=NB_MS_BEFORE_FLUSH)    // Flush buffer in Timeout
        {
        sof_cnt=0;
        usb.flush();
        }
    if (usb.b_send) usb.send(); else usb.recv();
    usb.b_send=!usb.b_send;
    }

And after getting to the bottom of this and fixing the issue the working code looks like this:

void device_cdc_task(void)
    {
    if (!Is_device_enumerated()) { _reset_fifos=true; return; }
    if (_reset_fifos)
        {
        _reset_fifos=false;
        usb.fifo_send.reset();
        usb.fifo_recv.reset();
        Usb_reset_endpoint_fifo_access(RX_EP);
        Usb_reset_endpoint_fifo_access(TX_EP);
        sof_cnt=NB_MS_BEFORE_FLUSH;
        }
    if (sof_cnt>=NB_MS_BEFORE_FLUSH)    // Flush buffer in Timeout
        {
        Usb_ack_in_ready_send(TX_EP);
        b_tx_new=true;
        sof_cnt=0;
        }
    if (usb.b_send) usb.send(); else usb.recv();
    usb.b_send=!usb.b_send;
    }

Where USB is my class doing specific job. Also this occasionally send stall but will not freeze the MCU to death. Also this solves the issue that after restart of host PC you need to reset or reconnect the USB.

However I am still suspicious that the USB example contains another bug possibly just wrongly set timeouts or missing acknowledge somewhere because during longer periods of inactivity pipe0 sometimes freezes.