nRF54LM20A USB Device: Endpoint 0 fails to ACK zero-length GET_DESCRIPTOR requests

Hi everyone,
I recently encountered an issue while developing with the nRF54LM20A. When acting as a USB High-Speed (HS) HID mouse device, Endpoint 0 fails to respond with an OUT ACK when the PC host sends requests with wLength = 0x00 (this includes but is not limited to the command: 0x80 0x06 0x00 0x06 0x00 0x00 0x00 0x00).
My workaround with other USB chips was to directly configure an OUT ACK when detecting a GET_DESCRIPTOR request with wLength = 0x00, which complies with the USB specification. However, I couldn't find the corresponding API/interface to achieve this on NCS.

Below is the USB data when the length is non-zero; Endpoint 0 behaves normally.(n54LM20A)

Below is the USB data when the length is zero; the Endpoint 0 OUT status is abnormal.(n54LM20A)

Below is the data captured using another vendor's IC, where Endpoint 0 responds correctly with zero-length requests. (Note: This IC has been mass-produced in our nRF54L15-based products.)

Could anyone advise on how to resolve this issue?
PS: I'm using NCS v3.1.1, and this issue can be reproduced with the USB HID mouse example.

ozzy

Parents
  • Hi Ozzy,

    I want to have some clarification to align with you about the correct response. 


    According to spec and our understanding:

    9.3.5 wLength

    This field specifies the length of the data transferred during the second phase of the control transfer. The
    direction of data transfer (host-to-device or device-to-host) is indicated by the Direction bit of the
    bmRequestType field. If this field is zero, there is no data transfer phase.

    9.3.1 bmRequestType

    [...] The state of the Direction bit is ignored if the wLength field is zero, signifying there is no Data stage.

    8.5.3 Control Transfers

     [...] If the control sequence has no Data stage, then it consists of a Setup stage followed by a Status stage consisting of an IN transaction.

    So my understanding is that host is expected to send IN token after the GET_DESCRIPTOR with wLength=0. 

    Could you confirm ? 

  • Hi Hung Bui,

    Thank you for your prompt response.

    I have just revisited the USB 2.0 specification, and you are absolutely right: the spec clearly states that for a zero-length Control Read transfer the Data stage shall be omitted and the host must issue an IN token in the Status stage.
    However, in roughly 95 % of the PCs we have tested, the sequence is different:

    1. The host(windows PC) correctly skips the Data stage;
    2. Nevertheless, in the Status stage it still sends an **OUT token** instead of the expected IN token.

    The capture below illustrates this behaviour: although the request is a zero-length Control Read, the Windows driver simply bypasses the IN Data stage and immediately proceeds to what it treats as the Status stage—expecting the device to ACK the OUT packet.

    In other words, the host does **not** follow the “no-data control” flow you mentioned; it still performs an OUT handshake for status.

    Because of this widespread host-side deviation, my previous implementations (on other MCUs) always arm **both** IN and OUT endpoints with ACK whenever a zero-length Control Read is detected. This guarantees compatibility with hosts that violate the strict spec sequence.

    Best regards,
    Ozzy

Reply
  • Hi Hung Bui,

    Thank you for your prompt response.

    I have just revisited the USB 2.0 specification, and you are absolutely right: the spec clearly states that for a zero-length Control Read transfer the Data stage shall be omitted and the host must issue an IN token in the Status stage.
    However, in roughly 95 % of the PCs we have tested, the sequence is different:

    1. The host(windows PC) correctly skips the Data stage;
    2. Nevertheless, in the Status stage it still sends an **OUT token** instead of the expected IN token.

    The capture below illustrates this behaviour: although the request is a zero-length Control Read, the Windows driver simply bypasses the IN Data stage and immediately proceeds to what it treats as the Status stage—expecting the device to ACK the OUT packet.

    In other words, the host does **not** follow the “no-data control” flow you mentioned; it still performs an OUT handshake for status.

    Because of this widespread host-side deviation, my previous implementations (on other MCUs) always arm **both** IN and OUT endpoints with ACK whenever a zero-length Control Read is detected. This guarantees compatibility with hosts that violate the strict spec sequence.

    Best regards,
    Ozzy

Children
  • Hi Ozzy, 
    Thanks for the confirmation. 
    Our developer think that we need to follow the spec. In this case the device should respond with STALL packet instead of NAK packet after it receives unexpected OUT packet + DATAx packet.

    We want to know which host you used. You mentioned Windows but we want to know the actual host controller. Also please let us know which software you used to trigger GetDescriptor() with wLength=0 

  • Hi Hung Bui,

    Thank you for getting back to me. Let me add a few clarifications based on your questions:

    1. I’m not sure what you mean by “actual host controller.”
    The machines I have tested are ordinary Windows 10 and Windows 11 PCs; the nRF54LM20A is enumerated as a USB-HID mouse on those systems.

    2. I use a tool called **Bus Hound** to inject the exact request. The procedure is:
        2.1 Double-click the target device in the device list.
        2.2 Select “USB” as the protocol.
        2.3 Fill in the eight bytes of the SETUP packet (e.g. 80 06 00 06 00 00 0A 00).
        2.4 Press “Run”; the PC immediately transmits that request downstream.
        Screenshot below for reference:

    3. Your developers are absolutely right: as a rigorous vendor, Nordic should follow the USB spec to the letter.
    My only goal was to surface this real-world behaviour—seen on many commodity PCs—to let you decide whether a workaround is warranted.

Related