qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* USB port claiming / set configuration problems
@ 2021-03-04  3:45 Ben Leslie
  2021-03-04 14:31 ` Gerd Hoffmann
  0 siblings, 1 reply; 4+ messages in thread
From: Ben Leslie @ 2021-03-04  3:45 UTC (permalink / raw)
  To: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 3556 bytes --]

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

[-- Attachment #2: Type: text/html, Size: 4078 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: USB port claiming / set configuration problems
  2021-03-04  3:45 USB port claiming / set configuration problems Ben Leslie
@ 2021-03-04 14:31 ` Gerd Hoffmann
  2021-03-05  0:50   ` Ben Leslie
  0 siblings, 1 reply; 4+ messages in thread
From: Gerd Hoffmann @ 2021-03-04 14:31 UTC (permalink / raw)
  To: Ben Leslie; +Cc: qemu-devel

  Hi,

> 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.

If libusb is able to correctly report what the current configuration is
(i.e. what the kernel has picked at device detection time) then yes, we
can do that.  And it would probably the best way to tackle the problem
as it should not have bad side effects on other devices so we don't need
a config option to enable/disable this.

> 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?

The switch to libusb sure.  That solves lots of portability issues.  Not
claiming the port was probably lost because libusb doesn't offer
support for that (don't remember all details after years).

> Would adding support to host-libusb to use these
> ioctl to claim the port be beneficial?

I don't feel like side-stepping libusb.  That is asking for trouble
because usbdevfs might behave differently then and confuse libusb.

So, if anything, libusb would need support that, then qemu can use it.

> 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).

We could try that too (set config to zero first, then set the config we
actually want) and see if that works better.

Another option could maybe be a device reset + set config.

These two would probably need a config option to enable/disable the
quirk though.

take care,
  Gerd



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: USB port claiming / set configuration problems
  2021-03-04 14:31 ` Gerd Hoffmann
@ 2021-03-05  0:50   ` Ben Leslie
  2021-03-09  7:52     ` Ben Leslie
  0 siblings, 1 reply; 4+ messages in thread
From: Ben Leslie @ 2021-03-05  0:50 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 2749 bytes --]

On Fri, 5 Mar 2021 at 01:31, Gerd Hoffmann <kraxel@redhat.com> wrote:

>   Hi,
>
> > Would adding support to host-libusb to use these
> > ioctl to claim the port be beneficial?
>
> I don't feel like side-stepping libusb.  That is asking for trouble
> because usbdevfs might behave differently then and confuse libusb.
>
> So, if anything, libusb would need support that, then qemu can use it.
>
> > 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).
>
> We could try that too (set config to zero first, then set the config we
> actually want) and see if that works better.
>

This approach seems to work well, and let's me just use libusb, which is a
plus.

It seems I had actually misdiagnosed (or only partially diagnosed) the issue
with my 'problem devices'. It turned out that setting *any* valid
configuration
twice in a row causes problems for the device! So, for example, if the
current
configuration was set to 1, and then set configuration 2 was called that
would
also cause problems. I guess that drivers on other systems ensured that
such a sequence never occurred.

I reverted bfe44898848614cfcb3a269bc965afbe1f0f331c and made this change:

--- a/hw/usb/host-libusb.c
+++ b/hw/usb/host-libusb.c
@@ -955,6 +955,11 @@ static int usb_host_open(USBHostDevice *s,
libusb_device *dev, int hostfd)

     usb_host_detach_kernel(s);

+    rc = libusb_set_configuration(s->dh, 0);
+    if (rc != 0) {
+        goto fail;
+    }
+
     libusb_get_device_descriptor(dev, &s->ddesc);
     usb_host_get_port(s->dev, s->port, sizeof(s->port));

This appears to work for my use cases. (Although I still have more testing
to do).

In terms of the transaction on the wire, this is not quite as good as the
'claim port'
approach. Specifically, with the claim port after setting address and
getting some
basic descriptors the kernel will explicitly set configuration to zero and
not perform
any more transactions. Without the 'claim port' the kernel appears to
configure to
the first configuration and then read a few more descriptors. For my test
cases
at least this doesn't appear to be problematic, but I thought it was worth
calling
out the differences. Of course the great benefit of this approach is that
it uses
existing libusb functionality.

Cheers,

Ben

[-- Attachment #2: Type: text/html, Size: 3662 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: USB port claiming / set configuration problems
  2021-03-05  0:50   ` Ben Leslie
@ 2021-03-09  7:52     ` Ben Leslie
  0 siblings, 0 replies; 4+ messages in thread
From: Ben Leslie @ 2021-03-09  7:52 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 3381 bytes --]

On Fri, 5 Mar 2021 at 11:50, Ben Leslie <benno@benno.id.au> wrote:

> On Fri, 5 Mar 2021 at 01:31, Gerd Hoffmann <kraxel@redhat.com> wrote:
>
>>   Hi,
>>
>> > Would adding support to host-libusb to use these
>> > ioctl to claim the port be beneficial?
>>
>> I don't feel like side-stepping libusb.  That is asking for trouble
>> because usbdevfs might behave differently then and confuse libusb.
>>
>> So, if anything, libusb would need support that, then qemu can use it.
>>
>> > 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).
>>
>> We could try that too (set config to zero first, then set the config we
>> actually want) and see if that works better.
>>
>
> This approach seems to work well, and let's me just use libusb, which is a
> plus.
>
> It seems I had actually misdiagnosed (or only partially diagnosed) the
> issue
> with my 'problem devices'. It turned out that setting *any* valid
> configuration
> twice in a row causes problems for the device! So, for example, if the
> current
> configuration was set to 1, and then set configuration 2 was called that
> would
> also cause problems. I guess that drivers on other systems ensured that
> such a sequence never occurred.
>
> I reverted bfe44898848614cfcb3a269bc965afbe1f0f331c and made this change:
>
> --- a/hw/usb/host-libusb.c
> +++ b/hw/usb/host-libusb.c
> @@ -955,6 +955,11 @@ static int usb_host_open(USBHostDevice *s,
> libusb_device *dev, int hostfd)
>
>      usb_host_detach_kernel(s);
>
> +    rc = libusb_set_configuration(s->dh, 0);
> +    if (rc != 0) {
> +        goto fail;
> +    }
> +
>      libusb_get_device_descriptor(dev, &s->ddesc);
>      usb_host_get_port(s->dev, s->port, sizeof(s->port));
>
> This appears to work for my use cases. (Although I still have more testing
> to do).
>
> In terms of the transaction on the wire, this is not quite as good as the
> 'claim port'
> approach. Specifically, with the claim port after setting address and
> getting some
> basic descriptors the kernel will explicitly set configuration to zero and
> not perform
> any more transactions. Without the 'claim port' the kernel appears to
> configure to
> the first configuration and then read a few more descriptors. For my test
> cases
> at least this doesn't appear to be problematic, but I thought it was worth
> calling
> out the differences. Of course the great benefit of this approach is that
> it uses
> existing libusb functionality.
>
>
For the archives, unfortunately this approach did not work for my use case.
I was able to get something working using the claim port approach, but that
meant going behind the back of libusb, which is obviously not suitable to be
merged, and ends up as a reasonably sizable patch.

I'll see if there is any appetite for adding claim port functionality to
libusb
before raising it again here.

Thanks for the feedback and assistance.

Cheers,

Ben

[-- Attachment #2: Type: text/html, Size: 4545 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2021-03-09  7:54 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-04  3:45 USB port claiming / set configuration problems Ben Leslie
2021-03-04 14:31 ` Gerd Hoffmann
2021-03-05  0:50   ` Ben Leslie
2021-03-09  7:52     ` Ben Leslie

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).