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 endpoint freezes

Hello everyone,

please help me find the solution to this problem:

I'm writing a composite device firmware for an Atmel SAM7S256 mcu. The firmware incorporates a usb keyboard and a HID. Because I have only 4 endpoints to use with this MCU, I am assigning 1 endpoint (EP1) to the keyboard interface, and 2 endpoints (EP2 (in) and EP3 (out)) to the other interface.

The problem is that when I write to EP3, my host program hangs (probably because the firmware hangs too). My firmware is an adaptation of the Atmel provided sample keyboard firmware. The composite device enumerates just fine (keyboard + HID).

What could be wrong?

I don't know enough of the AT91 usb stack to understand IF I have to change anything else in the code to add those 2 endpoints to the second interface. My understand is that this change would be enough:

typedef struct { USBConfigurationDescriptor configuration; // first interface USBInterfaceDescriptor interface0; HIDDescriptor hid0; USBEndpointDescriptor interruptIn0; // second interface USBInterfaceDescriptor interface1; HIDDescriptor hid1; USBEndpointDescriptor interruptIn1; USBEndpointDescriptor interruptOut1;
} __attribute__ ((packed)) HIDDKeyboardDriverConfigurationDescriptors;

Here's my understanding of how HID device works: when the device firmware receives a SET_REPORT request on the control EP, it reads the content of the control EP and makes a call to USB_Read() passing the number of the endpoint to read and a call back function. So, I am checking the content of the interface #, and if it is 1, I pass the EP3 to USB_Read(). But this mechanism doesn't seem to be working for me. :(

Please help me out with any hints. Is Tsuneo still in the forum?

Thanks a million.
Paulo

Parents
  • Maybe, you are working on the Atmel package/AT91LIB, aren't you?

    AT91SAM7S-EK Software Package for IAR 5.2, Keil and GNU
    or
    AT91LIB version 1.5
    www.atmel.com/.../SAM7S-EK.aspx

    To make a composite device of keyboard and vendor-specific HID on this stack, you have to bind two class drivers,
    \at91sam7s-ek\packages\usb-device-hid-keyboard-project-at91sam7s-ek\at91lib\usb\device\hid-keyboard
    \at91sam7s-ek\packages\usb-device-hid-transfer-project-at91sam7s-ek\at91lib\usb\device\hid-transfer

    As your device is enumerated, you've finished descriptor work.

    When the device receives a control transfer, USBDCallbacks_RequestReceived() is called by the stack. Your composite firmware implements this callback as follows, so that the request is passed to the target interface.

    enum {
        HIDDKeyboardDriver_INTERFACENUM,
        HIDDTransferDriver_INTERFACENUM,
        COMPOSITEDDriverDescriptors_NUMINTERFACE    // number of interfaces
    };
    
    void USBDCallbacks_RequestReceived(const USBGenericRequest *request)
    {
        if ( USBGenericRequest_GetIndex(request) == HIDDKeyboardDriver_INTERFACENUM ) {
            HIDDKeyboardDriver_RequestHandler(request);
        }
        if ( USBGenericRequest_GetIndex(request) == HIDDTransferDriver_INTERFACENUM ) {
            HIDDTransferDriver_RequestHandler(request);
        }
    }
    

    > Here's my understanding of how HID device works: when the device firmware receives a SET_REPORT request on the control EP, it reads the content of the control EP and makes a call to USB_Read() passing the number of the endpoint to read and a call back function.

    Your understanding is fine, when your PC application really puts a SET_REPORT request.

    > The problem is that when I write to EP3, my host program hangs

    Write on EP3 of the host app doesn't cause SET_REPORT request.
    Your firmware calls USBD_Read() for EP3 at USBDDriverCallbacks_ConfigurationChanged(), as follows.

    void USBDDriverCallbacks_ConfigurationChanged(unsigned char cfgnum)
    {
        if (cfgnum > 0) {
    
            hiddTransferDriver.iLen = 0;
            USBD_Read(HIDDTransferDriverDescriptors_INTERRUPTOUT,  // <--- EP3
                      hiddTransferDriver.iBuf,
                      HIDDTransferDriver_REPORTSIZE,
                      HIDDTransferDriver_DataReceived,
                      0);
        }
    }
    

    This USBD_Read() arms a buffer (hiddTransferDriver.iBuf) and callback (HIDDTransferDriver_DataReceived) to EP3. When the stack receives an output report (packet) on EP3, this report is kept on this buffer, and registered callback is called.

    Tsuneo

Reply
  • Maybe, you are working on the Atmel package/AT91LIB, aren't you?

    AT91SAM7S-EK Software Package for IAR 5.2, Keil and GNU
    or
    AT91LIB version 1.5
    www.atmel.com/.../SAM7S-EK.aspx

    To make a composite device of keyboard and vendor-specific HID on this stack, you have to bind two class drivers,
    \at91sam7s-ek\packages\usb-device-hid-keyboard-project-at91sam7s-ek\at91lib\usb\device\hid-keyboard
    \at91sam7s-ek\packages\usb-device-hid-transfer-project-at91sam7s-ek\at91lib\usb\device\hid-transfer

    As your device is enumerated, you've finished descriptor work.

    When the device receives a control transfer, USBDCallbacks_RequestReceived() is called by the stack. Your composite firmware implements this callback as follows, so that the request is passed to the target interface.

    enum {
        HIDDKeyboardDriver_INTERFACENUM,
        HIDDTransferDriver_INTERFACENUM,
        COMPOSITEDDriverDescriptors_NUMINTERFACE    // number of interfaces
    };
    
    void USBDCallbacks_RequestReceived(const USBGenericRequest *request)
    {
        if ( USBGenericRequest_GetIndex(request) == HIDDKeyboardDriver_INTERFACENUM ) {
            HIDDKeyboardDriver_RequestHandler(request);
        }
        if ( USBGenericRequest_GetIndex(request) == HIDDTransferDriver_INTERFACENUM ) {
            HIDDTransferDriver_RequestHandler(request);
        }
    }
    

    > Here's my understanding of how HID device works: when the device firmware receives a SET_REPORT request on the control EP, it reads the content of the control EP and makes a call to USB_Read() passing the number of the endpoint to read and a call back function.

    Your understanding is fine, when your PC application really puts a SET_REPORT request.

    > The problem is that when I write to EP3, my host program hangs

    Write on EP3 of the host app doesn't cause SET_REPORT request.
    Your firmware calls USBD_Read() for EP3 at USBDDriverCallbacks_ConfigurationChanged(), as follows.

    void USBDDriverCallbacks_ConfigurationChanged(unsigned char cfgnum)
    {
        if (cfgnum > 0) {
    
            hiddTransferDriver.iLen = 0;
            USBD_Read(HIDDTransferDriverDescriptors_INTERRUPTOUT,  // <--- EP3
                      hiddTransferDriver.iBuf,
                      HIDDTransferDriver_REPORTSIZE,
                      HIDDTransferDriver_DataReceived,
                      0);
        }
    }
    

    This USBD_Read() arms a buffer (hiddTransferDriver.iBuf) and callback (HIDDTransferDriver_DataReceived) to EP3. When the stack receives an output report (packet) on EP3, this report is kept on this buffer, and registered callback is called.

    Tsuneo

Children
  • Tsuneo,

    you made some very good points. Although my device enumerates fine, I see your point about having two RequestHandlers. The way I am responding to the HIDGenericRequest_SETREPORT request for the HID transfer device isn't correct.

    I'll make a few changes and will report back here.

    I immensely appreciate your help.

    Paulo.

  • Tsuneo,

    After several attempts, unfortunately the changes you suggested didn't work.

    I thought that this part of your suggestion was crucial to make things work because I have to treat the requests individually, since they are two different devices in one:

    void USBDCallbacks_RequestReceived(const USBGenericRequest *request)
    {
        if ( USBGenericRequest_GetIndex(request) == HIDDKeyboardDriver_INTERFACENUM ) {
            HIDDKeyboardDriver_RequestHandler(request);
        }
        if ( USBGenericRequest_GetIndex(request) == HIDDTransferDriver_INTERFACENUM ) {
            HIDDTransferDriver_RequestHandler(request);
        }
    }
    

    The problem is that the GetIndex() doesn't return the number of the interface. In fact, after reading the HID spec more, I see that it says that the number of the interface to which the request is for will only be in wIndex when the type of request is HID class descriptor (page 49).

    So, I'm basically back to square 1, as I have not been able to extract the interface number from the request. :(

    I have actually been able to create a composite of 2 HIDs (just not with AT91 USB Framework), but they have exactly the same descriptors, so in that case I didn't have to worry about the interface number as I have to in this case.

    Any other hints? Is there anything special about the Configuration Descriptor besides the fact that it says 2 interfaces and that the first one is 0, the next is 1? Out of ideas here....

    Thanks immensely.
    Paulo

  • Ah, sorry. My code analysis was not enough.
    The Atmel stack requires unified decode of request including standard device requests at class implementation layer. It isn't well organized to plug in class-specific request parser, as I wrote.

    > I have actually been able to create a composite of 2 HIDs.., but they have exactly the same descriptors

    Do you mean both interfaces are keyboard ones?
    You can't put Output report from your Windows application to keyboard, because keyboard is a "system" device.
    For keyboard, just Feature report is allowed to exchange.

    Tsuneo

  • >Do you mean both interfaces are keyboard ones?

    No. They are two HID transfer devices. And they work nicely: from the host program, I write to the Interrupt OUT of one interface and read from the Interrupt IN of the other interface.

    I was pleased that that scheme worked 'cause that was a stepping stone to this firmware I am writing now: I want to send a command through the Interrupt OUT of one interface (the regular HID transfer one), and have the other interface (the keyboard device) to send a response to the host.

    What's so frustrating is that I have not been able to build any composite device from Atmel samples. The closest I got, after some changes, was to build the HIDMSD (keyboard + msd) but the enumeration fails for the MSD device.

    Do you have any working composite firmware sample for the AT91SAM7S series?

    Anything else I could try?

    Thanks.
    Paulo

  • Indirectly, I also answered your second point about sending output report to the keyboard. I am not trying to do that; as I said, I write to the interrupt out of the HID transfer device, and expect the keyboard to send an input report back to host.

    Paulo