Ken
Ken

Reputation: 11

How to get Linux FunctionFS USB Gadget to register as WINUSB Device on Windows 10?

I have a BeagleBone Blue that I'm attempting to build into a small USB device that I'll communicate with on my Windows machine.

I've got a shell script that I run on boot up that configures the FunctionFS gadget.

#!/bin/bash

sudo modprobe usb_f_fs

cd /sys/kernel/config/usb_gadget/

sudo mkdir -p g_my_gadget
cd g_my_gadget

echo 0x0200 > bcdUSB     # Complies with USB specification 2.0.0
echo 0xXXXX > idVendor   # VID
echo 0xXXXX > idProduct  # PID
echo 0x0100 > bcdDevice  # v1.0.0

# String descriptors (configure these before you need them)
sudo mkdir -p strings/0x0409  # 0x0409 is the code for English
echo "SERIALNUMBER" > strings/0x0409/serialnumber
echo "MY_MANUFACTURER" > strings/0x0409/manufacturer
echo "MY DEVICE" > strings/0x0409/product

# Configuration descriptor 1
sudo mkdir -p configs/c.1/strings/0x0409
echo "My Configuration" > configs/c.1/strings/0x0409/configuration  # This sets iConfiguration in Configuration Descriptor
echo 250 > configs/c.1/MaxPower  # Request 500 mA
echo 0xc0 > configs/c.1/bmAttributes

echo 0x00 > bDeviceClass
echo 0x00 > bDeviceSubClass
echo 0x00 > bDeviceProtocol

# Write OS Descriptors
echo 1 > os_desc/use
echo 0xcd > os_desc/b_vendor_code
echo MSFT100 > os_desc/qw_sign

sudo mkdir -p functions/ffs.usb0  # Calls *_alloc_inst in the kernel module--we can now set functions specific to the class

sudo ln -s functions/ffs.usb0 configs/c.1/  # calls _alloc in the kernel module

# Make the directory that the endpoints will live in + mount to the functionFS file system
sudo mkdir /dev/ffs-usb0
sudo mount -t functionfs usb0 /dev/ffs-usb0

I've also taken a copy of ffs-test.c and updated the descriptors object as seen below. the compiled binary is also run after the shell script listed above:

    .header = {
        .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
        .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC |
                             FUNCTIONFS_HAS_HS_DESC |
                             FUNCTIONFS_HAS_SS_DESC |
                             FUNCTIONFS_HAS_MS_OS_DESC
                             ),
        .length = cpu_to_le32(sizeof descriptors),
    },
    .fs_count = cpu_to_le32(3),
    .fs_descs = {
        .intf = {
            .bLength = sizeof descriptors.fs_descs.intf,
            .bDescriptorType = USB_DT_INTERFACE,
            .bNumEndpoints = 2,
            .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
            .bInterfaceSubClass = 0xFF,
            .bInterfaceProtocol = 0xFF,
            .iInterface = 0,
        },
        .sink = {
            .bLength = sizeof descriptors.fs_descs.sink, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 1 | USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK,
            /* .wMaxPacketSize = autoconfiguration (kernel) */
        },
        .source = {
            .bLength = sizeof descriptors.fs_descs.source, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 2 | USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK,
            /* .wMaxPacketSize = autoconfiguration (kernel) */
        },
    },
    .hs_count = cpu_to_le32(3),
    .hs_descs = {
        .intf = {
            .bLength = sizeof descriptors.fs_descs.intf,
            .bDescriptorType = USB_DT_INTERFACE,
            .bNumEndpoints = 2,
            .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
            .bInterfaceSubClass = 0xFF,
            .bInterfaceProtocol = 0xFF,
            .iInterface = 0,
        },
        .sink = {
            .bLength = sizeof descriptors.hs_descs.sink,
            .bDescriptorType = USB_DT_ENDPOINT,
            .bEndpointAddress = 1 | USB_DIR_IN,
            .bmAttributes = USB_ENDPOINT_XFER_BULK,
            .wMaxPacketSize = cpu_to_le16(512),
        },
        .source = {
            .bLength = sizeof descriptors.hs_descs.source, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 2 | USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(512), .bInterval = 1, /* NAK every 1 uframe */
        },
    },
    .ss_count = cpu_to_le32(5),
    .ss_descs = {
        .intf = {
            .bLength = sizeof descriptors.fs_descs.intf,
            .bDescriptorType = USB_DT_INTERFACE,
            .bNumEndpoints = 2,
            .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
            .bInterfaceSubClass = 0xFF,
            .bInterfaceProtocol = 0xFF,
            .iInterface = 0,
        },
        .sink = {
            .bLength = sizeof descriptors.hs_descs.sink,
            .bDescriptorType = USB_DT_ENDPOINT,
            .bEndpointAddress = 1 | USB_DIR_IN,
            .bmAttributes = USB_ENDPOINT_XFER_BULK,
            .wMaxPacketSize = cpu_to_le16(1024),
        },
        .sink_comp = {
            .bLength = USB_DT_SS_EP_COMP_SIZE,
            .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
            .bMaxBurst = 0,
            .bmAttributes = 0,
            .wBytesPerInterval = 0,
        },
        .source = {
            .bLength = sizeof descriptors.hs_descs.source, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 2 | USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(1024), .bInterval = 1, /* NAK every 1 uframe */
        },
        .source_comp = {
            .bLength = USB_DT_SS_EP_COMP_SIZE,
            .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
            .bMaxBurst = 0,
            .bmAttributes = 0,
            .wBytesPerInterval = 0,
        },
    },
    .os_count = cpu_to_le32(2),
    .os_compat = {
        .header = {
            .interface = 0,
            .dwLength = cpu_to_le32(sizeof descriptors.os_compat),
            .bcdVersion = cpu_to_le16(0x01),
            .wIndex = cpu_to_le16(0x0004),
            .bCount = 0x01,
            .Reserved= 0x00,
        },
        .function = {
            .bFirstInterfaceNumber = 0x0,
            .Reserved1 = 0x01,
            .CompatibleID = "WINUSB\0\0",
            .SubCompatibleID = {0, 0, 0, 0, 0, 0, 0, 0},
            .Reserved2 = {0, 0, 0, 0, 0, 0},
        }
    },
    .os_prop = {
        .header= {
            .interface = 0,
            .dwLength = cpu_to_le32(sizeof descriptors.os_prop),
            .bcdVersion = cpu_to_le16(0x01),
            .wIndex = cpu_to_le16(0x0005),
            .wCount = cpu_to_le16(0x0001),
        },
        .desc = {
            .dwSize=cpu_to_le32(sizeof(descriptors.os_prop.desc)
                    +sizeof(descriptors.os_prop.bPropertyName)
                    +sizeof (descriptors.os_prop.wPropertyDataLength)
                    +sizeof(descriptors.os_prop.bPropertyData)),
            .dwPropertyDataType = cpu_to_le32(0x00000001),
            .wPropertyNameLength = cpu_to_le16(20),
        },
        .bPropertyName = "DeviceInterfaceGUID\0",
        .wPropertyDataLength = cpu_to_le32(39),
        .bPropertyData =  "{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}\0",
    },
};

When I plug the BeagleBone into my windows machine, it shows up in Device Manager as an "Other Device" instead of under the "Universal Serial Bus devices" heading, and I'm unable to communicate with my device.

Is there something obvious that I'm missing here?

I though I had populated my shell script and ffs-test module to allow the appropriate descriptors to be communicated to Windows, but it doesn't appear that Windows is receiving the descriptors it requires to register the device as a WINUSB device.

Upvotes: 1

Views: 756

Answers (1)

jason.xu
jason.xu

Reputation: 1

Thank you for sharing. Based on your configuration, I also tried enumerating WINUSB and got stuck in the Windows recognition step. Later, I“ https://github.com/vpelletier/python-functionfs/issues/9 ”I saw that others added an extra step ln - s to os_desc, as shown below. After modifying the script according to their approach, I can complete the winUSB device enumeration. I hope this answer can help you.

# os_desc
echo "1" > "sys/kernel/config/usb_gadget/DAF/os_desc/use"
echo "0x02" > "sys/kernel/config/usb_gadget/DAF/os_desc/b_vendor_code"
echo "MSFT100" > "sys/kernel/config/usb_gadget/DAF/os_desc/qw_sign"
ln -s "/sys/kernel/config/usb_gadget/DAF/configs/c.1" "/sys/kernel/config/usb_gadget/DAF/os_desc/"

Upvotes: 0

Related Questions