We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
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
> 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.
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.
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.
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.
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.