I have encountered a number of devices (mostly mobile phones) which seem to
get very confused if a "SET CONFIGURATION" control transfer (for the same
interface) is performed twice.

Specifically, after receiving a 2nd SET CONFIGURATION (for the same
interface) the device times out on future bulk output transfers. (Sometimes
PING-NAK continues until the overall transfer times out, in other cases the
PING itself timeouts after ~200 PING-NAKs).

While I'm fairly confident in saying that the USB firmware on these devices
is broken they seem to work enough to be operable when running a native
operating system.

Unfortunately when running the same operating system virtualized under Qemu
we are able to trigger this bug.

This was originally found using an older (4.2) version of Qemu. It seems
like that the patch bfe44898848614cfcb3a269bc965afbe1f0f331c was able to
solve the issue for some of the devices we see. Specifically, this avoids
actually performing a SET CONFIGURATION control transfer if there is only a
single configuration. The commit message "Seems some devices become
confused when we call libusb_set_configuration()." seems to confirm some of
the behaviour we have been seeing.

Unfortunately, while this appears to have solved the issue for devices with
a single configuration we still appear to have problems when hitting
devices with multiple configurations (which is not surprising given that
the commit only changed behaviour for single-configuration devices).

To attempt a work-around and validate the theory I change the
`usb_host_set_config` function (in host-libusb.c) such that it first checks
if the current active configuration matches the request configuration, and
if so skips performing the actual SET CONFIGURATION control transfer.

Would a patch of this nature be the right approach?
Perhaps this check could replace the number of configurations check?

Taking a step back here, the larger problem is that Linux host performs
various control transfers prior to qemu (and therefore the guest operating
system) gaining control of the device.
This means the sequence of control transfers with the device is inherently
going to be different when the guest OS is virtualized as compared to
running natively. For well behaving devices this really shouldn't matter,
but not all devices are well behaving!

USBDEVFS has support for `USBDEVFS_CLAIM_PORT` (and
`USBDEVFS_RELEASE_PORT`) ioctls. From the definition this seem designed to
limit the interaction that Linux kernel might have with a device on a
claimed port, which seems perfect for this use case. This in fact used in
previous version of qemu if we go back to the host-linux.c days, but with
the change over to host-libusb.c this functionality was lost.

Was this intentional? Would adding support to host-libusb to use these
ioctl to claim the port be beneficial? Based on a simple test program and
hardware USB traces for a device connected to a 'claimed' port the kernel
does indeed leave the device in an unconfigured state. (Although it still
performs some basic control transfers to gather descriptor, and strangely
seems to in this case make an explicit SET CONFIGURATION transfer, but sets
configuration to zero, rather than an actual configuration, which, at least
for the devices I was able to test with, avoided the problems of calling
SET CONFIGURATION (1) twice). Integrating this support back into
host-libusb.c is a little more involved than the work around described
above, so I'd appreciate any feedback before going down that path.

Thanks,

Ben

Reply via email to