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 HID IN & OUT Reports through Control & Interrupt transfer

Hi,

dig around with/for HID reports, i ran into a strange problem within a WinXP test application that i wrote.

Following configuration:
1 (non compound) device with
1 config with
1 interface (HID) with
2 endpoints wheras
1 interrupt in (ep1) and
1 interrupt out (ep2)

Setting and requesting the reports through control transfer (ep0) works like a charme, but with interrupt transfers the host application first sets an report without any problem, but the request of the following in interrupt leads to a problem. I figured out that the in interrupt terminates on the wrong (ep2) endpoint.
within the win api i used readfile in an overlapped mode, but there is no chance to define a endpoint. why isn't the interface interpreted correctly by the hostapp? or is there something to define elsewhere?

thx & kr

Parents
  • The descriptors are fine, including the report descriptor.

    > do you mean with "HID device driver on PC identifies the endpoint(s) by the direction (IN or OUT)." that same endpoint addresses has to be usede - exepted the direction (eg. 01 & 81)? couldn't i use different ep addresses like 81 & 02?

    You can assign EP1 IN (0x81) and EP2 OUT (0x02) to the HID interface without any problem.

    > forgot to write that within USBlyzer the control transfer based set/get show up as supposed. the interrupt based did not.

    Do you mean you see any error on "BulkOrInterrupt" URB on USBlyzer?
    Can you post the trace around the error?

    Windows HID driver starts to poll the interrupt IN endpoint just after enumeration.
    On the sniffer trace, you'll see this sequence (ignore PnP records)

    - URB SelectConfiguration (Set_Configuration)
    - URB Set_Idle (maybe displayed as class request)
    - URB Get_Descriptor( Report descriptor )
    - URB BulkOrInterrupt <------- the first transfer to the interrupt IN EP

    What is the target endpoint of the first "BulkOrInterrupt" ?

    Tsuneo

Reply
  • The descriptors are fine, including the report descriptor.

    > do you mean with "HID device driver on PC identifies the endpoint(s) by the direction (IN or OUT)." that same endpoint addresses has to be usede - exepted the direction (eg. 01 & 81)? couldn't i use different ep addresses like 81 & 02?

    You can assign EP1 IN (0x81) and EP2 OUT (0x02) to the HID interface without any problem.

    > forgot to write that within USBlyzer the control transfer based set/get show up as supposed. the interrupt based did not.

    Do you mean you see any error on "BulkOrInterrupt" URB on USBlyzer?
    Can you post the trace around the error?

    Windows HID driver starts to poll the interrupt IN endpoint just after enumeration.
    On the sniffer trace, you'll see this sequence (ignore PnP records)

    - URB SelectConfiguration (Set_Configuration)
    - URB Set_Idle (maybe displayed as class request)
    - URB Get_Descriptor( Report descriptor )
    - URB BulkOrInterrupt <------- the first transfer to the interrupt IN EP

    What is the target endpoint of the first "BulkOrInterrupt" ?

    Tsuneo

Children
  • hi,

    sorry for the delay. during the enumeration the following trace was made:

    URB 0089 7.467 Get Descriptor from Interface Report  in  86886998h  ACPI 84F0EDE0h
    URB 0090 7.467 Get Descriptor from Interface Report  in  89FD9028h USBPDO-7 usbehci 84F0EDE0h
    URB 0091-0090 7.467 Control Transfer Get Descriptor (Report) 06 00 FF 09 01 A1 01 15... in --:--:00 89FD9028h USBPDO-7 usbehci 84F0EDE0h Success (Success)
    URB 0092-0089 7.467 Control Transfer Get Descriptor (Report) 06 00 FF 09 01 A1 01 15... in --:--:00 86886998h  ACPI 84F0EDE0h Success (Success)
    URB 0093 7.469 Bulk or Interrupt Transfer 1024 bytes buffer  in 01:00:81 86886998h  ACPI 94C0CB50h
    URB 0094 7.469 Bulk or Interrupt Transfer 1024 bytes buffer  in 01:00:81 89FD9028h USBPDO-7 usbehci 94C0CB50h
    URB 0095 7.469 Bulk or Interrupt Transfer 1024 bytes buffer  in 01:00:81 86886998h  ACPI 84F0EDE0h
    URB 0096 7.469 Bulk or Interrupt Transfer 1024 bytes buffer  in 01:00:81 89FD9028h USBPDO-7 usbehci 84F0EDE0h
    URB 0097 9.208 Control Transfer Ex Get Descriptor (Cfg ind:0)  in  86886998h  ACPI 84FA1A38h
    URB 0098 9.208 Control Transfer Ex Get Descriptor (Cfg ind:0)  in  89FD9028h USBPDO-7 usbehci 84FA1A38h
    URB 0099-0098 9.209 Control Transfer Ex Get Descriptor (Cfg ind:0) 09 02 29 00 01 01 03 C0... in  89FD9028h USBPDO-7 usbehci 84FA1A38h Success (Success)
    URB 0100-0097 9.209 Control Transfer Ex Get Descriptor (Cfg ind:0) 09 02 29 00 01 01 03 C0... in  86886998h  ACPI 84FA1A38h Success (Success)
    .........
    URB 0149-0094 95.824 Bulk or Interrupt Transfer   in 01:00:81 89FD9028h USBPDO-7 usbehci 94C0CB50h Cancelled (Canceled)
    URB 0150-0093 95.824 Bulk or Interrupt Transfer   in 01:00:81 86886998h  ACPI 94C0CB50h Cancelled (Canceled)
    URB 0151-0096 95.824 Bulk or Interrupt Transfer   in 01:00:81 89FD9028h USBPDO-7 usbehci 84F0EDE0h Cancelled (Canceled)
    URB 0152-0095 95.824 Bulk or Interrupt Transfer   in 01:00:81 86886998h  ACPI 84F0EDE0h Cancelled (Canceled)
    

    i didn't receive anything on target's ep 81!

    any idea?

    kr

  • URB 0093 7.469 Bulk or Interrupt Transfer 1024 bytes buffer  in 01:00:81 86886998h  ACPI 94C0CB50h
    URB 0094 7.469 Bulk or Interrupt Transfer 1024 bytes buffer  in 01:00:81 89FD9028h USBPDO-7 usbehci 94C0CB50h
    ...
    URB 0149-0094 95.824 Bulk or Interrupt Transfer   in 01:00:81 89FD9028h USBPDO-7 usbehci 94C0CB50h Cancelled (Canceled)
    URB 0150-0093 95.824 Bulk or Interrupt Transfer   in 01:00:81 86886998h  ACPI 94C0CB50h Cancelled (Canceled)
    

    I don't see any error on this trace.
    (0093, 0094): 1024 bytes interrupt transfer is requested to (01:00:81) - device 01: interface 00: endpoint 81 (EP1 IN)
    This request is cancelled by the host app at (0149, 0150) - Maybe, the host app has closed the HID device handle.
    The device doesn't finish the transfer for about 88 sec (95.824 - 7.469)

    It suggests the problem lies in the device side.
    The device enables the EP1 IN. (otherwise, "Bulk or Interrupt Transfer" ends up in error, instead of canceled)
    But the device doesn't put any transaction to the IN endpoint, or it doesn't "finish" the transfer.

    The device has two format for input report.
    report ID 1: 64 bytes - report ID (1 byte) + body (63 bytes)
    report ID 2: 1024 bytes - report ID (1 byte) + body (1023 bytes)

    For the report ID 1, you have to send ZLP (Zero-Length Packet) as the second packet.
    - because the first packet is 64 bytes, which is equal to wMaxPacketSize of the endpoint. ZLP "finishes" the transfer.

    For the report ID 2, no ZLP is required, because this report is the greatest input report.
    - As the sniffer shows, HID driver always requests this size (1024 bytes) of transfer to the EP1 IN. As the transfer size matches to the request size, no ZLP is required.

    Did you send ZLP for the report ID 1 transfer?

    Tsuneo

  • Yepp, there is no USB related error - neither on the host nor the device. and the handle isn't closed by the host app anywhere - still available and valid after the chancelled transfer.

    of course there is no data loaded to ep1 at enumeration time, and therefor it should answer with an stall. but the needed interrupt (ep1) for the reply isn't received at the device side.

    btw. zlp's are implemented (ep0 and ep1), but so far aren't used within ep1 because the ep1 routine isn't reached at all.

    kr

  • > of course there is no data loaded to ep1 at enumeration time, and therefor it should answer with an stall.

    STALL??
    Ah, I see. (Canceled) means STALL.

    The endpoint should return NAK instead of STALL, while in idle.
    Don't set STALL to the IN endpoint.

    > but the needed interrupt (ep1) for the reply isn't received at the device side.

    No interrupt occurs on the endpoint, until you passes the first packet to the endpoint.
    Endpoint interrupt occurs just when the packet is sent to the host, the endpoint becomes empty.

    Tsuneo

  • NAK is a normal response, which shows the endpoint is "not ready".
    You'll often see NAK on the bus, in usual USB communication.
    USB engine (SIE) on the MCUs automatically returns NAK, when the endpoint is empty (IN endpoint),
    or when the endpoint is occupied by the last packet (OUT endpoint).

    STALL means an error, which requires specific procedure to recover it.
    For bulk and interrupt endpoint, you rarely see STALL.
    Just Mass storage class applies it actively to show data transport error.
    For control transfer on the default endpoint, STALL is used to reject the request.
    This STALL is recovered by next SETUP transaction.



    This typical misunderstanding on USB has never been solved yet.
    The reason is that the introductory readings don't explain these terms well.
    Their explanation is correct, but out of focus.

    I hope these major readings would refine their explanation to drive out this wide-spread misunderstanding.

    USB Made Simple
    www.usbmadesimple.co.uk/ums_3.htm
    NAK: Receiving device cannot accept data or transmitting device cannot send data.
    STALL: Endpoint is halted, or control pipe request is not supported.

    USB in a NutShell
    www.beyondlogic.org/.../usb3.htm
    NAK - Reports that the device temporary cannot send or received data. Also used during interrupt transactions to inform the host there is no data to send.
    STALL - The device finds its in a state that it requires intervention from the host.

    USB Complete 4th ed. by J.Axelson, Chapter 2 (p47-48)
    NAK
    NAK (negative acknowledge) means the device is busy or has no data to return.
    If the host sends data when the device is too busy to accept data, the endpoint
    returns NAK in the handshake packet. If the host requests data when the device
    has nothing to send, the endpoint returns NAK in the data packet. In either
    case, NAK indicates a temporary condition, and the host normally retries later
    up to a driver-defined limit.
    Hosts never send NAK. Isochronous transactions don’t use NAK because they
    have no handshake packet for returning a NAK. If a device or the host doesn’t
    receive transmitted isochronous data, it’s lost.

    STALL
    The STALL handshake can mean an unsupported control request, control
    request failed, or endpoint failed.
    On receiving an unsupported control-transfer request, the device returns
    STALL in the Data or Status stage. The device also returns STALL if the device
    supports the request but for some reason can’t take the requested action. For
    example, if the host sends a Set Configuration request to set the device configuration
    to 2, and the device supports only configuration 1, the device returns
    STALL. To clear this type of stall, the host sends another Setup packet to begin
    a new control transfer. The USB 2.0 specification calls this type of stall a protocol
    stall.
    Another use of STALL is a response when the endpoint’s Halt feature is set,
    which means that the endpoint is unable to send or receive data at all. The specification
    calls this type of stall a functional stall.
    Bulk and interrupt endpoints must support the functional stall. USB 2.0 control
    endpoints may support the functional stall but have little reason to do so. A
    control endpoint in a functional stall must continue to respond normally to
    other requests that monitor and control the stall condition. An endpoint that is
    capable of responding to these requests is capable of communicating and thus
    shouldn’t be stalled. Isochronous transactions don’t use STALL because they
    have no handshake packet for returning the STALL. SuperSpeed control endpoints
    can’t use the functional STALL.
    On receiving a functional STALL, the host drops all pending requests to the
    device and doesn’t resume communications until the host has sent a successful
    control request to clear the Halt feature on the device. Hosts never send
    STALL.

    Tsuneo

  • This is another wide-spread misunderstanding.

    For IN endpoint, an endpoint interrupt occurs just when the endpoint becomes empty,
    by sending a packet (transaction) to the host.
    For OUT endpoint, it occurs just when the endpoint finishes to receive a new packet.

    This interrupt timing is same as UART TX/RX.
    For UART TX, interrupt occurs just after a byte puts out to the line.
    TX interrupt never occurs until the firmware writes a byte to the TX buffer.
    For UART RX, it occurs when a byte has received on the RX buffer.

    The major difference of USB endpoint from UART lies in the IN endpoint.
    UART TX starts just after firmware writes a byte to the buffer. But IN endpoint waits for an IN transaction from host.



    This misunderstanding comes from impractical examples by major USB-MCU and compiler manufacturers, including KEIL.
    I'll pick up KEIL one here,

    In this KEIL example, the packets are passed to the USB engine in the endpoint interrupt using USB_WriteEP().
    When users just see this part of the code, they misunderstand that
    endpoint interrupt comes first, and the firmware passes the packet in response to the interrupt (wrong idea).

    usbuser.c
    
    void USB_EndPoint1 (U32 event) {
    
      switch (event) {
        case USB_EVT_IN:
          GetInReport();
          USB_WriteEP(HID_EP_IN, &InReport, sizeof(InReport));
          break;
      }
    }
    

    But actually, the first packet is passed to the IN endpoint out side of the endpoint ISR,
    in the Set_Configuration handler, as follows. Users aren't aware of this code.
    Above code in the endpoint ISR is placed to repeat the report infinitely, ignoring device's requirement.

    usbuser.c
    
    #if USB_CONFIGURE_EVENT
    void USB_Configure_Event (void) {
    
      if (USB_Configuration) {             /* Check if USB is configured */
        GetInReport();
        USB_WriteEP(HID_EP_IN, &InReport, sizeof(InReport));  // <--- the first packet is passed here
      }
    }
    #endif
    

    Harmful example.
    I hope KEIL would refine it immediately.

    Tsuneo

  • Hi Tsuneo,

    many thx for the explanations, but nevertheless

    .) neither STALL nor NAK is sent during the enumeration, in fact it looks like a "normal" time out within the host, and therefor the tranfer is CHANCELLED. I know this because i didn't see any received packet on the ep1 - if there is one, i see a rx flag withtin the controller (also if the controller handles it on its own).
    .) ok, the "imaginary" STALL during the enumeration is(was) my fault, because the application logic implements a flowchart which looks like this (after enumeration):

    1) host sends data using the OUT report over the INTERRUPT OUT ep or the ep0
    2) device decodes the data and acknowledge this within a new data packet that is generated and loaded into the INTERRUPT IN ep or the ep0 (REPORT IN structure). The decision which endpoint has to be used is made by interpreting the received data.
    3) the host requests an REPORT IN using either INTERRUPT IN or ep0 (knowing which one because it knows the previous packets).

    for testing just commented out the STALL - no change (isn't reached because no interrupt appears).

    Again, the first interrupt within the device controller (INTERRUPT out or ep0) is generated normally, data is received, decoded and a answer is prepared. The second interrupt using ep0 works without problems, but using the INTERRUPT IN ep, no interrupt is generated. I think it is the same problem as during the enumeration (there also the INTERRUPT IN is requested). A STALL was wrongly implemented by me within the interrupt routine, if there was no data. But therefor a interrupt has to be generated by the controller which isn't there.

    Back to the source some questions:
    .) report size is 64(id1-in/out) / 1024(id2-in) byte
    .) therefor the hostapp has to malloc a buffer of that size
    .) sending an out report by writing the id (1) in the first byte leads to 63 byte data
    .) requesting an in report also writing the id (1 or 2) in the first byte leads to an 63 or 1023 byte report (always stored in a 1024 byte "frame" within the host's memory)

    right?

    kr

  • > in fact it looks like a "normal" time out within the host, and therefor the tranfer is CHANCELLED.

    PC HID device driver doesn't set timeout on the interrupt IN endpoint (EP).

    As I'm not using USBLyzer usually, I don't know how it looks like exactly (though sniffers display is almost similar)
    For confirmation, I made up a HID device, which does just enumeration but nothing else.
    And ran it on USBLyzer (trial version) and on a hardware bus analyzer.
    The device is left connected for 10 min after enumeration.

    Hardware bus analyzer:
    - After enumeration, NAKs to the interrupt IN EP repeated at the rate of bInterval for these 10 min seamlessly.
    USBLyzer:
    - After enumeration, URB "Bulk or Interrupt Transfer" is issued to the interrupt IN EP, immediately.
    But no completion appears, including (Canceled), for this 10 min.

    Next, I modified the device firmware to put STALL to the interrupt IN EP, 1 sec after enumeration.
    Here is the USBLyzer trace

    URB  0051        1:42:26.306 Bulk or Interrupt Transfer 1024 bytes buffer in 01:00:81 85673DE8h USBPDO-5 usbhub 8561EE48h
    URB  0052        1:42:26.306 Bulk or Interrupt Transfer 1024 bytes buffer in 01:00:81 85673DE8h USBPDO-5 usbhub 8564D5C8h
    ...
    ...
    URB  0070-0051   1:42:27.134 Bulk or Interrupt Transfer                   in 01:00:81 85673DE8h USBPDO-5 usbhub 8561EE48h Unsuccessful (Stall PID)
    KmIO 0071        1:42:27.134 Internal USB Get Port Status                             85673DE8h USBPDO-5 usbhub 8562B880h
    KmIO 0072-0071   1:42:27.134 Internal USB Get Port Status                             85673DE8h USBPDO-5 usbhub 8562B880h Success
    URB  0073        1:42:27.134 Abort Pipe                                      01:00:81 85673DE8h USBPDO-5 usbhub 8562B880h
    URB  0074-0052   1:42:27.134 Bulk or Interrupt Transfer                   in 01:00:81 85673DE8h USBPDO-5 usbhub 8564D5C8h Cancelled (Canceled)
    URB  0075-0073   1:42:27.134 Abort Pipe                                      01:00:81 85673DE8h USBPDO-5 usbhub 8562B880h Success (Success)
    


    The hardware bus analyzer caught STALL exactly.
    USBLyzer reports "Unsuccessful (Stall PID) - USBD_STATUS_STALL_PID" for STALL from the device.
    At the same time, it reports "Cancelled (Canceled) - USBD_STATUS_CANCELED" to the paired URB.

    OK, "Cancelled" doesn't mean STALL. Instead, it means canceled (USBD_STATUS_CANCELED) by HID device driver.
    Anyway, this pattern doesn't match to your trace.

    "Cancelled" is caused by the PC side.
    Selective Suspend?
    Please post more trace around "Cancelled", including PnP events.



    > Back to the source some questions:
    > .) report size is 64(id1-in/out) / 1024(id2-in) byte
    > .) therefor the hostapp has to malloc a buffer of that size
    > .) sending an out report by writing the id (1) in the first byte leads to 63 byte data
    > .) requesting an in report also writing the id (1 or 2) in the first byte leads to an 63 or 1023 byte report (always stored in a 1024 byte "frame" within the host's memory)

    > right?

    Yes. PC HID driver works so.

    Tsuneo

  • hi,

    short answer only because i'm out of time today. i think i found a minor problem because the CHANCELLED disappered within the actual devstate. But the major problem still appears when an IN REPORT is requested through the interrupt ep. I now receive the interrupt within the dsp - fifo of the ep was loaded - but there is a permanent UNDERRUN. An underrun during the enumeration is ok (leads to an open transaction as mentioned above)

    Things i've changed until now:
    .) As mentioned before recode the STALL/NAK behaviour of ep's => should be OK now
    .) doublecheck the ZLPs => OK
    .) doublecheck the fifo loading => OK (hopefully) i will check this again tomorrow.

    i will post a longer trace tomorrow also.

    kr

  • Hi Tsuneo,

    did some checks on the source, but didn't find any further problem. But to be sure please confirm or correct the following IN REPORT using interrupt ep.
    .) report descriptor with in 2 reports different size & directions (as above) .)1 in/out(64) .)2 in(1024)
    .) during enum host gets HID CAPS and therefore adds one byte (why? i though the 64 is with id)
    .) the device writes data into the ep fifo and signals readiness (TX_READY). when data is smaller then the max packet size of the ep nothing has to be done. if data is bigger than maxp also nothing has to be done. only when the data size is a multiple of the maxp an aditional ZLP has to be sent.
    .) host writing report id in the first byte of buffer and readfile() from device.

    many thx!

  • > .) during enum host gets HID CAPS and therefore adds one byte (why? i though the 64 is with id)

    For this report descriptor (copied from the trace), HidP_GetCaps return these size,
    HIDP_CAPS.InputReportByteLength = 64 (= 1 ID + 63 body) bytes
    HIDP_CAPS.OutputReportByteLength = 1024 (= 1 ID + 1023 body, the greatest) bytes

    Interface 0 HID Report Descriptor Vendor-Defined 1
    Item Tag (Value) Raw Data
    Usage Page (Vendor-Defined 1) 06 00 FF
    Usage (Vendor-Defined 1) 09 01
    Collection (Application) A1 01
        Logical Minimum (0) 15 00
        Logical Maximum (255) 26 FF 00
        Report Size (8) 75 08
        Report ID (1) 85 01
        Report Count (63) 96 3F 00
        Usage (Vendor-Defined 1) 09 01
        Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02
        Usage (Vendor-Defined 1) 09 01
        Output (Data,Var,Abs,NWrp,Lin,Pref,NNul,NVol,Bit) 91 02
        Report ID (2) 85 02
        Report Count (1023) 96 FF 03
        Usage (Vendor-Defined 1) 09 01
        Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit) 81 02
    End Collection C0
    

    > .) the device writes data into the ep fifo and signals readiness (TX_READY). when data is smaller then the max packet size of the ep nothing has to be done.
    fine.

    > if data is bigger than maxp also nothing has to be done.
    Don't write greater size than endpoint wMaxPacketSize to the endpoint.
    The rest is written when the next IN endpoint interrupt comes.

    > only when the data size is a multiple of the maxp an aditional ZLP has to be sent.
    When you send the input report of shorter size, it's fine.
    For the greatest size report, ZLP is not required.

    > .) host writing report id in the first byte of buffer and readfile() from device.
    No.
    Host can't specify coming report.
    Host always passes a buffer of the greatest size (+ report ID).
    When ReadFile returns, the first byte shows the report ID of received report.

    Tsuneo