libusb / libusb

A cross-platform library to access USB devices
https://libusb.info
GNU Lesser General Public License v2.1
5.23k stars 1.9k forks source link

Windows: libusb Windows backend for some USB composite device with IAD #85

Open mcuee opened 9 years ago

mcuee commented 9 years ago

Ref: 1) http://libusbx.1081486.n5.nabble.com/Libusbx-devel-Accessing-composite-devices-using-interface-association-td1726.html https://www.mail-archive.com/libusbx-devel@lists.sourceforge.net/msg03059.html 2) http://marc.info/?t=139209996400001&r=1&w=2 3) http://sourceforge.net/p/openocd/tickets/96/

Work-around is to have WinUSB driver as the driver for the whole USB device (replacing Windows defaut USB Composite Parent driver).

diabolo38 commented 9 years ago

not sure it is a real issue in all linked above in some case i suspect it is more an issue in the installation file used (.inf) i used libusb with cdc/acm type device only referencing the MI_xx i wanted and not using the IAD at all. That is working of course if multiple interface from the IAD are to be used they must all be claimed.

typicality using zadig of over inf builder wizard select one say first interface, generates driver files (chose extract do not install) then edit .inf and add Mi_xx missing , for example in a composite device with two interafce 0 and 1 to be used set

%DeviceName%=LUsbK_Device, USB\%DeviceID%&MI_01, USB\%DeviceID%&MI_00

when install is done by hand pointing to the inf file it may have to be repeated for all the multiple interface separately. The device manager will finaly appear as two separate device but it is armless.

Installing for the full device like below is also a solution (will appear only once): %DeviceName%=LUsbK_Device, USB\%DeviceID%

But that will prevent installing some standard class driver present in the composite device for instance a cdc/acm with standard serial driver along with specific device.

mcuee commented 9 years ago

http://libusbx.1081486.n5.nabble.com/Libusbx-devel-Accessing-composite-devices-using-interface-association-tp1726p1737.html Nabble libusbx archive is dead.

https://www.mail-archive.com/libusbx-devel@lists.sourceforge.net/msg03070.html From Xiaofan: " I think there is a way to hack the inf-file generated by Zadig to install WinUSB driver for each interface. Interface 0 --> MI_00 Interface 1 --> MI_01 Interface 2 --> MI_02 " From Tim Roberts: "No, that won't work. When you have an IAD that wraps those three interfaces, usbccgp only creates a PDO for MI_00. You'll only get one driver, and that driver is assigned ownership of all three interfaces. There will never be an opportunity to load MI_01 and MI_02, because it won't create those PDOs. "

leok7v commented 9 years ago

Parsing IADs is very trivial:

    /** Endpoint descriptor. See libusb_endpoint_descriptor. */
    LIBUSB_DT_ENDPOINT = 0x05,

    /** Interface Association Descriptor */
    LIBUSB_DT_IAD = 0x0b,

    /** BOS descriptor */
    LIBUSB_DT_BOS = 0x0f,

struct libusb_iad_descriptor {
    uint8_t  bLength;
    uint8_t  bDescriptorType;
    uint8_t  bFirstInterface;
    uint8_t  bInterfaceCount;
    uint8_t  bFunctionClass;
    uint8_t  bFunctionSubClass;
    uint8_t  bFunctionProtocol;
    uint8_t  iFunction;
}; // add to configuration

static int parse_iad(struct libusb_context *ctx,
    struct libusb_iad_descriptor *iad, unsigned char *buffer,
    int size, int host_endian)
{
    if (size <= 8) {
        usbi_err(ctx, "ias descriptor size %d expected 8", size);
        return LIBUSB_ERROR_IO;
    }
    usbi_parse_descriptor(buffer, "bbbbbbbb", iad, host_endian);
    return 8;
}

static int parse_configuration(struct libusb_context *ctx,
    struct libusb_config_descriptor *config, unsigned char *buffer,
    int size, int host_endian)
{
...
    config->extra = NULL;
    config->extra_length = 0;

    memset(&config->iad, 0, sizeof(config->iad));
    if (size >= DESC_HEADER_LENGTH) {
        usbi_parse_descriptor(buffer, "bb", &header, 0);
        if (header.bDescriptorType == LIBUSB_DT_IAD) {
            parse_iad(ctx, &config->iad, buffer, size, host_endian);
            buffer += header.bLength;
            size -= header.bLength;
        }
    }
    for (i = 0; i < config->bNumInterfaces; i++) {

...

}

// tricky part is that 

static int winusbx_open(int sub_api, struct libusb_device_handle *dev_handle)
{
            file_handle = CreateFileA(priv->usb_interface[i].path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ,
                NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
}

same file handle should be used for ALL interfaces that are setup in set_composite_interface
mawillia commented 6 years ago

I have a branch with three commits that I have been using to work around this problem in order to talk to an AIA U3V (USB 3.0 Vision) camera using Windows 10. Two of the commits introduce and fill the IAD structure outlined by Leo. The third commit will cause the back-end, when it encounters an IAD, to pile all of the endpoints of the grouped interfaces into the first interface of the IAD.

https://github.com/mawillia/libusb/commits/iad

The result is claiming the first interface in the IAD will pass, but claiming any of the other interfaces in the IAD will fail (as is currently the case). However, submitting requests to the end points on any of the associated interfaces appears to work as the back-end get_interface_by_endpoint() will always yield the first interface, which will have the correct underlying handle for the IO submission. I've been able to just claim the first interface of the IAD and exercise end points over a couple of the interfaces in the group this way. So far so good.

This is not a pull request or anything like that; I can't do sufficient testing to say that I haven't introduced a regression, etc. Maybe someone could take a peek and let me know if this approach has any merit or is a bad idea....

Novakov commented 5 years ago

I ran into this issue (or something that looks like this) today with latest libusb release (1.0.22). I was going to try patches from @mawillia but before applying them I tried latest master (2a7372db54094a406a755f0b8548b614ba8c78ec) and it worked. I'm not sure enough that issue is solved but I'm leaving this note here in case someone else is also trying to get it working. :)

My device is STM32 Blue Pill providing composite device: CDC + WinUSB (WCID with DeviceInterfaceGUID) device. Libusb is enumerating it only once, I can open it, claim interface and perform bulk transfers.

fpoussin commented 5 years ago

@Novakov Could you show us the descriptors you are using? I have an identical setup but had no luck in opening the device on Windows with Libusb. (WinUsb works when called directly) Thanks

pazourek commented 3 years ago

Work-around is to have WinUSB driver as the driver for the whole USB device (replacing Windows defaut USB Composite Parent driver).

I have a composite device - iface 0,1 = CDC-ACM, iface 2=WinUSB. I created a WinUSB driver as mentioned to replace the CDC driver. Unfortunately I wasn't able to claim an interface 1 when an order of claiming interfaces was 2,0,1. After changing it to 0,1,2 it seems the claiming works as expected. I just wanted to leave this note here for anybody else who is also trying to get it working.

tormodvolden commented 1 year ago

My device is STM32 Blue Pill providing composite device: CDC + WinUSB (WCID with DeviceInterfaceGUID) device. Libusb is enumerating it only once, I can open it, claim interface and perform bulk transfers.

@Novakov Can you please share the firmware for your Blue Pill?

Novakov commented 1 year ago

Well.. I can share parts of source code but it's rotten over the years and, unfortunately, I don't have binary anymore.

I have testing environment built around GD32VF on custom board and I'm planning to do some testing on it soon. If you think it would be useful I can port it to Blue Pill and share it.

tormodvolden commented 1 year ago

I see. It would be useful for me to have a composite device with IAD for testing. But I can look into the RPi gadgetfs instead.

Novakov commented 1 year ago

Give me few days, I will put something together for Blue Pill.

tormodvolden commented 1 year ago

Thanks, but please don't put too much effort in it for my sake. I should really look at RPi gadgetfs :)

mcuee commented 1 year ago

Thanks, but please don't put too much effort in it for my sake. I should really look at RPi gadgetfs :)

Yes gadgetfs or functionfs can be a good choice.

In case for STM32, you may try the following potential starting point, all with IAD. https://github.com/dijkstrw/5x5 https://github.com/openstenoproject/stenosaurus/blob/master/firmware/usb.c https://github.com/micropython/micropython/blob/master/ports/stm32/usbdev/class/src/usbd_cdc_msc_hid.c

Novakov commented 1 year ago

@mcuee I see that you are doing tests with RPi Pico - do you think it will be enough also for @tormodvolden purposes? My backlog of work grew a bit and if you already have something useful I will not invest building tool on STM32

tormodvolden commented 1 year ago

Yes, I am already fine with the links from mcuee above. In particular the first was wonderful.

mcuee commented 1 year ago

Now that #965 has been merged, this issue can be closed.

1238 deals with more broader issues without IAD.

mcuee commented 1 year ago

Let's wait for #1181 before closing this issue, as it deals with another aspect of this issue.

mcuee commented 1 year ago

Related issue: