This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

USB-CDC-driver hangs in USBD_Write

Hi,
I encountered problems using the USB-CDC-example out of the Atmel software package 1.5 with an AT91SAM9260.
Sometimes, when sending data using USBD_Write, the USB driver hangs in the function/define SET_CSR waiting for the AT91C_UDP_TXPKTRDY flag.
Other times, USBD_Write returns USBD_STATUS_LOCKED all times, while receiving data works fine.

char USBD_Write(unsigned char eptnum,
                const void *data,
                unsigned int size,
                TransferCallback callback,
                void *argument)
{
    Endpoint *endpoint = &(endpoints[eptnum]);
    Transfer *transfer = &(endpoint->transfer);

    // Check that the endpoint is in Idle state
    if (endpoint->state != UDP_ENDPOINT_IDLE) {
        return USBD_STATUS_LOCKED;
    }

    trace_LOG(trace_INFO, "Write%d(%u) ", eptnum, size);

    // Setup the transfer descriptor
    transfer->data = (char *) data;
    transfer->remaining = size;
    transfer->buffered = 0;
    transfer->transferred = 0;
    transfer->callback = callback;
    transfer->argument = argument;

    // Send the first packet
    endpoint->state = UDP_ENDPOINT_SENDING;
    while((AT91C_BASE_UDP->UDP_CSR[eptnum]&AT91C_UDP_TXPKTRDY)==AT91C_UDP_TXPKTRDY);
    UDP_WritePayload(eptnum);
    SET_CSR(eptnum, AT91C_UDP_TXPKTRDY);

    // If double buffering is enabled and there is data remaining,
    // prepare another packet
    if ((BOARD_USB_ENDPOINTS_BANKS(eptnum) > 1) && (transfer->remaining > 0)) {

        UDP_WritePayload(eptnum);
    }

    // Enable interrupt on endpoint
    AT91C_BASE_UDP->UDP_IER = 1 << eptnum;

    return USBD_STATUS_SUCCESS;
}

Does someone else have similar problems?

And another question concerning the USB-CDC-driver: Under WinXP usbser.sys is used as driver for the device and it can be used as a serial port. When connecting to the port (e.g. by HTerm) and then resetting the board, the connection can not be established again, until the device is plugged out and in again. Is this a problem of the USB-CDC-driver on the device or a generic problem of the windows driver?

  • > Sometimes, when sending data using USBD_Write, the USB driver hangs in the function/define SET_CSR waiting for the AT91C_UDP_TXPKTRDY flag.

    Which endpoint does USBD_Write() target, bulk IN or interrupt IN ?
    Does the hang occurs always on the same endpoint?
    Do you see IN-NAKs for the endpoint on the bus, with a hardware bus analyzer?
    (You can't see IN-NAK with any software sniffer)

    I have doubt that IN-NAK stops on the bulk IN, because of failure on the PC device driver. It is often caused by noise on the bus.




    > Under WinXP usbser.sys is used as driver for the device and it can be used as a serial port. When connecting to the port (e.g. by HTerm) and then resetting the board, the connection can not be established again, until the device is plugged out and in again.

    Error recovery is troublesome, for any programming target.
    But usbser.sys is too poor.

    As the requirement for CDC device driver, usbser.sys always holds pending requests on these pipes.
    - bulk IN pipe on the data class interface
    - interrupt IN pipe on the communication class interface.

    When surprised removal is notified to this driver, the bulk IN pipe often causes hang on canceling pending request. It occurs not just for surprised removal, also for transfer error caused by noise on the bus. With pending request, this pipe always puts IN-NAKs to the bus. As noise error attacks even this IN-NAK sequence, noise immunity of usbser.sys is very weak.

    > Is this a problem of the USB-CDC-driver on the device or a generic problem of the windows driver?

    As Oney wrote in his "Programming the Windows Driver Model", cancel handling on WDM driver is tricky. In this mean, it is a generic problem of WDM. But I believe we can manage the difficulty, following his detailed instruction on the book exactly.

    As of the recovery from disconnection/ re-connection over usbser.sys, you may have interest in this topic.

    tech.groups.yahoo.com/.../43461

    Tsuneo

  • Hi Stefan,

    I'm encountering the same problem with SET_CSR hanging up in USBD_Write on a AT91SAM7S256.

    Have you managed to find a solution?

    The only lead I have is a note in the datasheet, which explains that wait times should be used in a preemptive environment instead of polling the bits. I wonder if I am getting nested interrupts trying to set and clear the flags simultaneously.

    Here is the note from the datasheet:

    WARNING: Due to synchronization between MCK and UDPCK, the software application must wait for the end of the write operation before executing another write by polling the bits which must be set/cleared.

    Note: In a preemptive environment, set or clear the flag and wait for a time of 1 UDPCK clock cycle and 1peripheral clock cycle. However,
    RX_DATA_BLK0, TXPKTRDY, RX_DATA_BK1 require wait times of 3 UDPCK clock cycles and 3 peripheral clock cycles
    before accessing DPR.

    (from section 35.6.10 UDP Endpoint Control and Status Register in the AT91SAM7S Datasheet)

    -Steven

  • Hi,
    we replaced the define

    #define SET_CSR(endpoint, flags) \ 
        { \ 
            volatile unsigned int reg; \ 
            reg = AT91C_BASE_UDP->UDP_CSR[endpoint] ; \ 
            reg |= REG_NO_EFFECT_1_ALL; \ 
            reg |= (flags); \ 
            AT91C_BASE_UDP->UDP_CSR[endpoint] = reg; \ 
            while ( (AT91C_BASE_UDP->UDP_CSR[endpoint] & (flags)) != (flags)); \ 
        }
    

    by a function with a "timeout"

    int32_t USBD_setCSR(unsigned char endpoint, unsigned int flags)
    {
        volatile unsigned int reg;
        volatile unsigned int cnt = 100000;
        reg = AT91C_BASE_UDP->UDP_CSR[endpoint] ;
        reg |= REG_NO_EFFECT_1_ALL;
        reg |= (flags);
        AT91C_BASE_UDP->UDP_CSR[endpoint] = reg;
        while ( cnt > 0)
        {
            if ((AT91C_BASE_UDP->UDP_CSR[endpoint] & (flags)) == flags)
            {
                break;
            }
            cnt--;
        }
        if (cnt > 0)
            return TRUE;
        else
            return FALSE;
    }
    

    Thus its not hanging any longer.

  • Hi Stefan,
    > the USB driver hangs in the function/define SET_CSR waiting for the AT91C_UDP_TXPKTRDY flag.
    > we replaced the define ... by a function with a "timeout" ... Thus its not hanging any longer.

    Your comment gives me chance to think of problem on the coding on the Atmel source code.
    I have doubt how this line is optimized by compiler,

    while ( (AT91C_BASE_UDP->UDP_CSR[endpoint] & (flags)) != (flags));
    

    In the Atmel source code, the macros are defined as follows - without any volatile.

    AT91SAM9260.h
    
    #define AT91_CAST(a) (a)
    
    typedef struct _AT91S_UDP {
            AT91_REG         UDP_NUM;       // Frame Number Register
            AT91_REG         UDP_GLBSTATE;  // Global State Register
            AT91_REG         UDP_FADDR;     // Function Address Register
            AT91_REG         Reserved0[1];  //
            AT91_REG         UDP_IER;       // Interrupt Enable Register
            AT91_REG         UDP_IDR;       // Interrupt Disable Register
            AT91_REG         UDP_IMR;       // Interrupt Mask Register
            AT91_REG         UDP_ISR;       // Interrupt Status Register
            AT91_REG         UDP_ICR;       // Interrupt Clear Register
            AT91_REG         Reserved1[1];  //
            AT91_REG         UDP_RSTEP;     // Reset Endpoint Register
            AT91_REG         Reserved2[1];  //
            AT91_REG         UDP_CSR[6];    // Endpoint Control and Status Register
            AT91_REG         Reserved3[2];  //
            AT91_REG         UDP_FDR[6];    // Endpoint FIFO Data Register
            AT91_REG         Reserved4[3];  //
            AT91_REG         UDP_TXVC;      // Transceiver Control Register
    } AT91S_UDP, *AT91PS_UDP;
    
    #define AT91C_BASE_UDP       (AT91_CAST(AT91PS_UDP)     0xFFFA4000) // (UDP) Base Address
    

    Tsuneo

  • Hi Stefan,

    Thank you for posting your solution.

    I ended up going about fixing the bug differently than your solution, because I am concerned about performance hits from using a larger function like that to replace the short macro, and I'm not sure how to handle the situation where the timeout does occur.

    Noting that the datasheet states that a fixed delay should be used when code execution can be preempted, I added an interrupt lock around the USB packet sending code which includes SET_CSR. This has solved the problem for me.

    Right now I'm locking interrupts around most of the packet sending code in USBD_Write. I think I made the critical section longer than it needs to be; I could probably just lock interrupts around the SET_CSR macro itself and then the performance effect would be negligible. Interestingly, I don't get lockups in other functions which use the SET_CSR macro, even though I'm not preventing preemption in those cases.

    -Steven

  • Hi,
    I just run into the problem again.
    In the bootloader, the board hangs after transfering a specific amount of data.
    Its curious, that it only happens with some of our boards, but not all them.
    After some tests, if found, that the problem was caused by the use of a USB hub. I connected the boards directly to the PC and it runs fine.
    I think it is a timing problem, which - for whatever reason - does not apply to all boards.

    Can you post your code, please. I would like to try, if this solves the problem.