Tx transfer not triggering corresponding Rx interrupt

From: Farhan Khan <farhan_at_farhan.codes>
Date: Sun, 15 May 2022 02:41:57 UTC
Hi all,

I am trying to understand why a USB Rx interrupt callback is not triggering. I 
am sending a USB Tx interrupt packet. The immediate next step is that the 
function will msleep(). The corresponding wakeup() occurs after an Rx 
interrupt.

There is no explicit usbd_transfer_start() on the Rx interrupt. Elsewhere in 
the code there is an instance where an Tx interrupt should eventually result 
in a corresponding Rx interrupt that results in a wakeup(). That does not 
occurs here and I do not understand why not.

-----------
Background:

I am working on porting OpenBSD's athn(9) driver to FreeBSD. I loaded the 
firmware and I believe this is confirmed by sending a AR_HTC_MSG_READY 
message, which is https://github.com/khanzf/freebsd/blob/
0ba9a9b92df496847058008598139e92f5e60850/sys/dev/athn/usb/if_athn_usb.c#L1009. 
There is an associated msleep() and wakeup() that suggest the firmware was 
successfully loaded. The wakeup occurs in the Rx interrupt handler.

Then, there is a section where the driver sets up the Host Transport 
Communication (HTC) interface. The driver does a transfer of data of the Tx 
Interrupt, then immediately runs msleep(). The corresponding wakeup occurs 
when an Rx (not Tx) interrupt triggers and is processed.

The code for this is located in athn_usb_htc_connect_svc().
My current implementation is here: https://github.com/khanzf/freebsd/blob/
0ba9a9b92df496847058008598139e92f5e60850/sys/dev/athn/usb/if_athn_usb.c#L1293

The OpenBSD implementation is here: https://github.com/openbsd/src/blob/
72a0854b669a0194895eeac5ffcdf5fc27bf2a30/sys/dev/usb/if_athn_usb.c#L809

Please note that in the OpenBSD implementation, the athn_usb_htc_msg() 
function calls usbd_setup_xfer/usbd_transfer, whereas my implementation calls 
usbd_transfer_start().

Also, OpenBSD opens the Rx Interrupt pipe with usbd_open_pipe_intr(), which 
gives the option for an interval. I suspect this is the same as the `interval` 
parameter in usb_config. I do not know if this is significant.

-----------
Problem:

The msleep() located on line 1314 fails with EWOULDBLOCK.
This sleep seems to be the equivalent of the acknowledgement that the Tx 
interrupt packet was received.

-----------
Expected:

I expect this to somehow result in the Rx interrupt, which runs the 
corresponding handler athn_usb_intr(). This function contains the wakeup() 
line on line 2556 (the AR_HTC_MSG_CONN_SVC case).

-----------
This happens elsewhere:

In athn_usb_load_firmware(), line 1011, there is a usbd_do_request(), followed 
by an msleep(). The wait_msg_id is AR_HTC_MSG_READY. The wakeup for this 
occurs only after an Rx interrupt is called on line 2538.

-----------
Question rephrased:

Why is a USB transfer resulting in an Rx interrupt in one place, even though a 
usbd_transfer_start() is not called on the Rx interrupt, but not for 
athn_usb_htc_connect_svc()/athn_usb_htc_msg()?

How do I make this Tx transfer result in an Rx interrupt without having to 
manually call usbd_transfer_start() (OpenBSD does not appear to do this?)

I do not understand the differences between usbd_do_request, 
usb_transfer_start and usbd_setup_xfer/usbd_transfer that might be resulting 
in different behavior, so any light on that would also help.

-----------
Been stuck for some time, so any assistance is appreciated.
Thank you!

- Farhan