Create your own USB gadget with GadgetFS

Monday, 25 July 2016
|
Écrit par
Grégory Soutadé

Do It Yourself, make your own objects, this is in vogue. Since the first version RaspberryPI, we can see a lot of little boards with GPIO connections that handles basic sensors/connectors. Even if some prefer to use wireless communications to communicate with these little devices, USB is still there !

Today I'll show an example of a basic raw bulk USB communication. We could setup our device with the serial line gadget (/dev/ttyGS0), but if you need performance or you want to handle specific USB feature, it's interesting to use raw transfers. Plus, all of this is done in userspace thanks to GadgetFS.

I waste a lot of time due to a buggy USB driver (dwc2), but, finally, the right code is simple.

1. USB keywords

All technical details about USB can be found within usb.org or linux-usb.org, it's quite heavy. Basically, USB communication use a master/slave schema : host side (mainly a PC) sends requests to device side (device). Device never asks questions, it only replies.

Configuration of USB port can be static (host or device) or dynamic. This is the case for DRD (Dual Role Device) configurations, which was previously called OTG (On The Go : who is the first to talk ?).

On the device, we have endpoints grouped into interfaces. Each endpoint contains a hardware buffer (memory) to deal with requests. An endpoint is setup (hardcoded) to be in or out, meaning, it can do only "in" or "out" operation.

In addition to direction, the type of endpoint is important (it's assigned during endpoint configuration). It can be :

  • control for configuration/control requests
  • bulk for bulk transfers
  • isochronous for periodic transfers with a reserved bandwidth
  • int for transfers by interruption

A special endpoint called ep0 (the first one) is always present. It's an in and out endpoint with control attribute. It allows to read/write configuration of other endpoints.

All these information are described in descriptors. You can look at them with

lsusb -v

The low level command sent to controller is called an URB (USB Request Block).

A picture to sum up :

USB host/device schema

2. Enable and mount GadgetFS

First thing to do is to enable GadgetFS in the kernel you're running (if it's not already the case).

Run make menuconfig [ARCH=XXX] in the kernel build directory. Then, enable

Device Drivers -> USB support -> USB Gadget Support -> USB Gadget Drivers -> Gadget Filesystem

In the same section (or in the section above depending on your controller), select the right Peripheral Controller.

You can now rebuild your Linux kernel.

Once booted, mount GadgetFS (in /dev for example)

mkdir /dev/gadget
mount -t gadgetfs gadgetfs /dev/gadget

3. Device side

There is a reference code that can be found in linux-usb.org which manage everything and can use aio for asynchronous requests management. Mine is simpler, full code link can be found in Conclusion part. I'll explain each parts in details.

Let's start. Do includes. usbtring.c is copied from linux-usb.org (strings sent back to host must be encoded in UTF-16). Most of structures and defines are referenced in ch9.h (which corresponds to chapter 9 of the USB norm) and gadgetfs.h

#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>

#include <linux/types.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadgetfs.h>

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <pthread.h>

#include <errno.h>

#include "usbstring.c"

Then, defines

#define FETCH(_var_)                            \
    memcpy(cp, &_var_, _var_.bLength);          \
    cp += _var_.bLength;

#define CONFIG_VALUE 2

// Specific to controller
#define USB_DEV "/dev/gadget/dwc2"
#define USB_EPIN "/dev/gadget/ep1in"
#define USB_EPOUT "/dev/gadget/ep2out"

enum {
    STRINGID_MANUFACTURER = 1,
    STRINGID_PRODUCT,
    STRINGID_SERIAL,
    STRINGID_CONFIG_HS,
    STRINGID_CONFIG_LS,
    STRINGID_INTERFACE,
    STRINGID_MAX
};

Config value is the number of endpoints. After that, we have paths relative to GadgetFS. When mounted, there is only USB_DEV, endpoints appears after the first configuration (ep0). Name of endpoints is dependent of the driver implementation.

Structures and static variables :

struct io_thread_args {
    unsigned stop;
    int fd_in, fd_out;
};

static struct io_thread_args thread_args;

static struct usb_string stringtab [] = {
    { STRINGID_MANUFACTURER, "MyOwnGadget", },
    { STRINGID_PRODUCT,      "Custom gadget", },
    { STRINGID_SERIAL,       "0001", },
    { STRINGID_CONFIG_HS,    "High speed configuration", },
    { STRINGID_CONFIG_LS,    "Low speed configuration", },
    { STRINGID_INTERFACE,    "Custom interface", },
    { STRINGID_MAX, NULL},
};

static struct usb_gadget_strings strings = {
    .language = 0x0409, /* en-us */
    .strings = stringtab,
};

static struct usb_endpoint_descriptor ep_descriptor_in;
static struct usb_endpoint_descriptor ep_descriptor_out;

The main thing here is the description of strings inside stringtag that will be parsed by usbstring functions.

int main()
{
    int fd=-1, ret, err=-1;
    uint32_t send_size;
    struct usb_config_descriptor config;
    struct usb_config_descriptor config_hs;
    struct usb_device_descriptor device_descriptor;
    struct usb_interface_descriptor if_descriptor;
    uint8_t init_config[2048];
    uint8_t* cp;

    fd = open(USB_DEV, O_RDWR|O_SYNC);

    if (fd <= 0)
    {
        printf("Unable to open %s (%m)\n", USB_DEV);
        return 1;
    }

    *(uint32_t*)init_config = 0;
    cp = &init_config[4];

    device_descriptor.bLength = USB_DT_DEVICE_SIZE;
    device_descriptor.bDescriptorType = USB_DT_DEVICE;
    device_descriptor.bDeviceClass = USB_CLASS_COMM;
    device_descriptor.bDeviceSubClass = 0;
    device_descriptor.bDeviceProtocol = 0;
    //device_descriptor.bMaxPacketSize0 = 255; Set by driver
    device_descriptor.idVendor = 0xAA; // My own id
    device_descriptor.idProduct = 0xBB; // My own id
    device_descriptor.bcdDevice = 0x0200; // Version
    // Strings
    device_descriptor.iManufacturer = STRINGID_MANUFACTURER;
    device_descriptor.iProduct = STRINGID_PRODUCT;
    device_descriptor.iSerialNumber = STRINGID_SERIAL;
    device_descriptor.bNumConfigurations = 1; // Only one configuration

    ep_descriptor_in.bLength = USB_DT_ENDPOINT_SIZE;
    ep_descriptor_in.bDescriptorType = USB_DT_ENDPOINT;
    ep_descriptor_in.bEndpointAddress = USB_DIR_IN | 1;
    ep_descriptor_in.bmAttributes = USB_ENDPOINT_XFER_BULK;
    ep_descriptor_in.wMaxPacketSize = 512; // HS size

    ep_descriptor_out.bLength = USB_DT_ENDPOINT_SIZE;
    ep_descriptor_out.bDescriptorType = USB_DT_ENDPOINT;
    ep_descriptor_out.bEndpointAddress = USB_DIR_OUT | 2;
    ep_descriptor_out.bmAttributes = USB_ENDPOINT_XFER_BULK;
    ep_descriptor_out.wMaxPacketSize = 512; // HS size

    if_descriptor.bLength = sizeof(if_descriptor);
    if_descriptor.bDescriptorType = USB_DT_INTERFACE;
    if_descriptor.bInterfaceNumber = 0;
    if_descriptor.bAlternateSetting = 0;
    if_descriptor.bNumEndpoints = 2;
    if_descriptor.bInterfaceClass = USB_CLASS_COMM;
    if_descriptor.bInterfaceSubClass = 0;
    if_descriptor.bInterfaceProtocol = 0;
    if_descriptor.iInterface = STRINGID_INTERFACE;

    config_hs.bLength = sizeof(config_hs);
    config_hs.bDescriptorType = USB_DT_CONFIG;
    config_hs.wTotalLength = config_hs.bLength +
        if_descriptor.bLength + ep_descriptor_in.bLength + ep_descriptor_out.bLength;
    config_hs.bNumInterfaces = 1;
    config_hs.bConfigurationValue = CONFIG_VALUE;
    config_hs.iConfiguration = STRINGID_CONFIG_HS;
    config_hs.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER;
    config_hs.bMaxPower = 1;

    config.bLength = sizeof(config);
    config.bDescriptorType = USB_DT_CONFIG;
    config.wTotalLength = config.bLength +
        if_descriptor.bLength + ep_descriptor_in.bLength + ep_descriptor_out.bLength;
    config.bNumInterfaces = 1;
    config.bConfigurationValue = CONFIG_VALUE;
    config.iConfiguration = STRINGID_CONFIG_LS;
    config.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER;
    config.bMaxPower = 1;

    FETCH(config);
    FETCH(if_descriptor);
    FETCH(ep_descriptor_in);
    FETCH(ep_descriptor_out);

    FETCH(config_hs);
    FETCH(if_descriptor);
    FETCH(ep_descriptor_in);
    FETCH(ep_descriptor_out);

    FETCH(device_descriptor);

    // Configure ep0
    send_size = (uint32_t)cp-(uint32_t)init_config;
    ret = write(fd, init_config, send_size);

    if (ret != send_size)
    {
        printf("Write error %d (%m)\n", ret);
        goto end;
    }

    printf("ep0 configured\n");

    handle_ep0(fd);

end:
    if (fd != -1) close(fd);

    return err;
}

The main function. We build the descriptors and send them to ep0. It's needed to send both low/full speed (USB 1) and high speed (USB 2) configurations. Here, they are quite the same. We have only one interface with two endpoints, one for in, and one for out. Descriptors are sent as a big char array that must starts by an uint32_t tag set to 0. All values are expressed in little endian.

ep0 function :

static void handle_ep0(int fd)
{
    int ret, nevents, i;
    fd_set read_set;
    struct usb_gadgetfs_event events[5];

    while (1)
    {
        FD_ZERO(&read_set);
        FD_SET(fd, &read_set);

        select(fd+1, &read_set, NULL, NULL, NULL);

        ret = read(fd, &events, sizeof(events));

        if (ret < 0)
        {
            printf("Read error %d (%m)\n", ret);
            goto end;        
        }

        nevents = ret / sizeof(events[0]);

        printf("%d event(s)\n", nevents);

        for (i=0; i<nevents; i++)
        {
            switch (events[i].type)
            {
            case GADGETFS_CONNECT:
                printf("EP0 CONNECT\n");
                break;
            case GADGETFS_DISCONNECT:
                printf("EP0 DISCONNECT\n");
                break;
            case GADGETFS_SETUP:
                printf("EP0 SETUP\n");
                handle_setup_request(fd, &events[i].u.setup);
                break;
            case GADGETFS_NOP:
            case GADGETFS_SUSPEND:
                break;
            }
        }
    }

end:
    return;
}

This one receives events and handle them. The most important are setup requests, which are requests that kernel cannot full handle by itself (or notice userspace).

static void handle_setup_request(int fd, struct usb_ctrlrequest* setup)
{
    int status;
    uint8_t buffer[512];
    pthread_t thread;

    printf("Setup request %d\n", setup->bRequest);

    switch (setup->bRequest)
    {
    case USB_REQ_GET_DESCRIPTOR:
        if (setup->bRequestType != USB_DIR_IN)
            goto stall;
        switch (setup->wValue >> 8)
        {
            case USB_DT_STRING:
                printf("Get string id #%d (max length %d)\n", setup->wValue & 0xff,
                    setup->wLength);
                status = usb_gadget_get_string (&strings, setup->wValue & 0xff, buffer);
                // Error 
                if (status < 0)
                {
                    printf("String not found !!\n");
                    break;
                }
                else
                {
                    printf("Found %d bytes\n", status);
                }
                write (fd, buffer, status);
                return;
        default:
            printf("Cannot return descriptor %d\n", (setup->wValue >> 8));
        }
        break;
    case USB_REQ_SET_CONFIGURATION:
        if (setup->bRequestType != USB_DIR_OUT)
        {
            printf("Bad dir\n");
            goto stall;
        }
        switch (setup->wValue) {
        case CONFIG_VALUE:
            printf("Set config value\n");
            if (!thread_args.stop)
            {
                thread_args.stop = 1;
                usleep(200000); // Wait for termination
            }
            if (thread_args.fd_in <= 0)
            {
                status = init_ep (&thread_args.fd_in, &thread_args.fd_out);
            }
            else
                status = 0;
            if (!status)
            {
                thread_args.stop = 0;
                pthread_create(&thread, NULL, io_thread, &thread_args);
            }
            break;
        case 0:
            printf("Disable threads\n");
            thread_args.stop = 1;
            break;
        default:
            printf("Unhandled configuration value %d\n", setup->wValue);
            break;
        }        
        // Just ACK
        status = read (fd, &status, 0);
        return;
    case USB_REQ_GET_INTERFACE:
        printf("GET_INTERFACE\n");
        buffer[0] = 0;
        write (fd, buffer, 1);
        return;
    case USB_REQ_SET_INTERFACE:
        printf("SET_INTERFACE\n");
        ioctl (thread_args.fd_in, GADGETFS_CLEAR_HALT);
        ioctl (thread_args.fd_out, GADGETFS_CLEAR_HALT);
        // ACK
        status = read (fd, &status, 0);
        return;
    }

stall:
    printf("Stalled\n");
    // Error
    if (setup->bRequestType & USB_DIR_IN)
        read (fd, &status, 0);
    else
        write (fd, &status, 0);
}

A bad response within this function can stall the endpoint. Two principle functions are to send back strings (not managed by driver) and starts/stop io_thread().

The init_ep() function is pretty simple. It justs sends endpoint descriptors (in low/full and high speed configuration). Like ep0, it must starts with an uint32_t tag of value 1 :

static int init_ep(int* fd_in, int* fd_out)
{
    uint8_t init_config[2048];
    uint8_t* cp;
    int ret = -1;
    uint32_t send_size;

    // Configure ep1 (low/full speed + high speed)
    *fd_in = open(USB_EPIN, O_RDWR);

    if (*fd_in <= 0)
    {
        printf("Unable to open %s (%m)\n", USB_EPIN);
        goto end;
    }

    *(uint32_t*)init_config = 1;
    cp = &init_config[4];

    FETCH(ep_descriptor_in);
    FETCH(ep_descriptor_in);

    send_size = (uint32_t)cp-(uint32_t)init_config;
    ret = write(*fd_in, init_config, send_size);

    if (ret != send_size)
    {
        printf("Write error %d (%m)\n", ret);
        goto end;
    }

    printf("ep1 configured\n");

    // Configure ep2 (low/full speed + high speed)
    *fd_out = open(USB_EPOUT, O_RDWR);

    if (*fd_out <= 0)
    {
        printf("Unable to open %s (%m)\n", USB_EPOUT);
        goto end;
    }

    *(uint32_t*)init_config = 1;
    cp = &init_config[4];

    FETCH(ep_descriptor_out);
    FETCH(ep_descriptor_out);

    send_size = (uint32_t)cp-(uint32_t)init_config;
    ret = write(*fd_out, init_config, send_size);

    if (ret != send_size)
    {
        printf("Write error %d (%m)\n", ret);
        goto end;
    }

    printf("ep2 configured\n");

    ret = 0;

end:
    return ret;
}

Finally, the io_thread() that responds to host requests. Here, I use select, but it seems not to be handled by driver, I/Os are just blocking, but it could be necessary if we want to stop thread.

/*
 * Respond to host requests
 */
static void* io_thread(void* arg)
{
    struct io_thread_args* thread_args = (struct io_thread_args*)arg;
    fd_set read_set, write_set;
    struct timeval timeout;
    int ret, max_read_fd, max_write_fd;
    char buffer[512];

    max_read_fd = max_write_fd = 0;

    if (thread_args->fd_in > max_write_fd) max_write_fd = thread_args->fd_in;
    if (thread_args->fd_out > max_read_fd) max_read_fd  = thread_args->fd_out;

    while (!thread_args->stop)
    {
        FD_ZERO(&read_set);
        FD_SET(thread_args->fd_out, &read_set);
        timeout.tv_sec = 0;
        timeout.tv_usec = 10000; // 10ms

        memset(buffer, 0, sizeof(buffer));
        ret = select(max_read_fd+1, &read_set, NULL, NULL, &timeout);

        // Timeout
        if (ret == 0)
            continue;

        // Error
        if (ret < 0)
            break;

        ret = read (thread_args->fd_out, buffer, sizeof(buffer));

        if (ret > 0)
            printf("Read %d bytes : %s\n", ret, buffer);
        else
            printf("Read error %d(%m)\n", ret);

        FD_ZERO(&write_set);
        FD_SET(thread_args->fd_in, &write_set);

        memset(buffer, 0, sizeof(buffer));
        ret = select(max_write_fd+1, NULL, &write_set, NULL, NULL);

        // Error
        if (ret < 0)
            break;

        strcpy(buffer, "My name is USBond !");

        ret = write (thread_args->fd_in, buffer, strlen(buffer)+1);

        printf("Write status %d (%m)\n", ret);
    }

    close (thread_args->fd_in);
    close (thread_args->fd_out);

    thread_args->fd_in = -1;
    thread_args->fd_out = -1;

    return NULL;
}

4. host side

Host part is very easy to implement. This part can be handled by libusb for a more complete and generic code.

#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <linux/usbdevice_fs.h>
#include <linux/usb/ch9.h>

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>

#define USB_DEV "/proc/bus/usb/001/002"

int main()
{
    int fd, ret, err=-1;
    struct usbdevfs_connectinfo connectinfo;
    struct usbdevfs_bulktransfer transfert;
    uint32_t val;
    char buffer[512];

    printf("Build %s @ %s\n", __DATE__, __TIME__);

    fd = open(USB_DEV, O_RDWR);

    if (fd <= 0)
    {
        printf("Unable to open %s (%m)\n", USB_DEV);
        return 1;
    }

    printf("Device opened\n");

    // Optional get information
    ret = ioctl(fd, USBDEVFS_CONNECTINFO, &connectinfo);
    if (ret)
    {
        printf("USBDEVFS_CONNECTINFO error %d (%m)\n", ret);
        goto end;
    }

    printf("devnum %d, slow %d\n",
           connectinfo.devnum, connectinfo.slow);

    // Claim interface 0
    val = 0;
    ret = ioctl(fd, USBDEVFS_CLAIMINTERFACE, &val);
    if (ret)
    {
        printf("USBDEVFS_CLAIMINTERFACE error %d (%m)\n", ret);
        goto end;
    }
    else
        printf("Interface claimed\n");

    // Send data on ep2out
    strcpy(buffer, "What is your name ?");

    transfert.ep = USB_DIR_OUT + 2;
    transfert.len = strlen(buffer)+1;
    transfert.timeout = 200;
    transfert.data = buffer;

    ret = ioctl(fd, USBDEVFS_BULK, &transfert);
    if (ret < 0)
    {
        printf("USBDEVFS_BULK 1 error %d (%m)\n", ret);
        goto end;
    }
    else
        printf("Transfert 1 OK %d\n", ret);

    // Receive data on ep1in
    transfert.ep = USB_DIR_IN + 1;
    transfert.len = sizeof(buffer);
    transfert.timeout = 200;
    transfert.data = buffer;

    ret = ioctl(fd, USBDEVFS_BULK, &transfert);
    if (ret < 0)
    {
        printf("USBDEVFS_BULK 2 error %d (%m)\n", ret);
        goto end;
    }
    else
        printf("Transfert 2 OK %d %s\n", ret, buffer);

    // Release interface 0
    val = 0;
    ret = ioctl(fd, USBDEVFS_RELEASEINTERFACE, &val);
    if (ret)
    {
        printf("USBDEVFS_RELEASEINTERFACE error %d (%m)\n", ret);
        goto end;
    }

    printf("Interface released\n");

    err = 0;

end:
    close(fd);

    return err;
}

To start, we claim an interface. This ioctl fully handled in host side driver (nothing is send to device). After that, a simple send/receive protocol. Finally we release interface. be carreful, USB_DEV path change when the device is disconnected.

5. Conclusion

The full code can be found on my server. This basic example can be extended a lot : isochronous, asynch requests, streams (USB 3). Enjoy !

#
1
De
shawn
, le
25 July 2017 01:07
hello
Répondre
Auteur :


e-mail* :


Le commentaire :


#
2
De
shawn
, le
25 July 2017 01:07
Your blog really helps me!But the gadgetfs doesn't support USB3.0, and functionfs can do more.Have you ever use functionfs to transfer datastreams?
Répondre
Auteur :


e-mail* :


Le commentaire :


#
3
De
Greg
, le
25 July 2017 05:07
Hello,

Sorry, I didn't used nor functionfs nor datastreams tranferts.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
4
De
Okan
, le
12 October 2017 11:10
Hi,
Your article is rather interesting and I've been trying to follow it as a tutorial. So far, I've built "Gadget Filesystem" and "Inventra HDRC USB Peripheral Controller" as modules using the Linux menuconfig tool. It created a "gadgetfs.ko" file, but I couldn't find a module for the peripheral controller; all I can see is an object file at "linux-davinci/drivers/usb/musb/musb_hdrc.o". I just copied "gadgetfs.ko" to "/lib/modules/3.2.0/extra" on the target device and insmod'ed it. Now, when I issue the mounting command "mount -t gadgetfs gadgetfs /dev/gadget", the terminal returns an error saying: "nop musb-hdrc: failed to start (null): -120"
What do you think I could be doing wrong?
Thanks in advance.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
5
De
Greg
, le
12 October 2017 14:10
Hello,

If it's only a .o file, it seems to have been statically linked to the kernel. Did you reflash it ? I don't know why you have this error. After a rapid Google search, I see that you're not alone, but there is no clear response to your problem.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
6
De
Okan
, le
13 October 2017 11:10
No, I didn't reflash the kernel considering I didn't enable any features as <*>.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
7
De
Greg
, le
13 October 2017 12:10
OK. So I really don't know where is an error...
Répondre
Auteur :


e-mail* :


Le commentaire :


#
8
De
Okan
, le
13 October 2017 13:10
It's okay; thanks.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
9
De
ranran
, le
14 February 2018 21:02
Hi,

Is it using dma ?
I also need the device side to communicate with other kernel module. Is it possible or is it that it better be done in kernel ?

Thanks,
Ranran
Répondre
Auteur :


e-mail* :


Le commentaire :


#
10
De
Greg
, le
15 February 2018 06:02
Hello,

DMA option is a parameter of your USB driver. It's activated by default in general, but this code can't choose to use it or not.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
11
De
ranran
, le
15 February 2018 07:02
Hi Greg,

Thanks again for the good article.
I would like to ask please , how did you figure out the value for USB_DEV (#define USB_DEV "/proc/bus/usb/001/002") , does it might change (enumerated differently) according to other usb device currently connected to host ? If yes - is there alternative to make it more stable with this gadget device ?

Thanks!
ranran
Répondre
Auteur :


e-mail* :


Le commentaire :


#
13
De
Greg
, le
15 February 2018 08:02
Hello,

In /proc/bus/usb, the first parameter is the bus id (might not change), but the second is an auto incremented number set when you plug in the cable, so it changes dynamically.

To fix it, I think you can create an udev rule according to your USB VIP/PID.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
15
De
ranran
, le
15 February 2018 08:02
Thank you :)
Répondre
Auteur :


e-mail* :


Le commentaire :


#
12
De
ranran
, le
15 February 2018 07:02
Hi de Greg,

I've been searching for gadgetfs documentation, but found none.
I don't understand how you knew how to create gadgetfs application.
I mean, opening the gadgfs descriptor and then writing to it in the correct sequence all the configuration and descriptors:
....usb_config_descriptor
FETCH(config);
FETCH(if_descriptor);
FETCH(ep_descriptor_in);
FETCH(ep_descriptor_out);
...
Is it documented anywhere how to work with gadgetfs, or did you have to search the gadgetfs driver code ?

Thanks,
ranran
Répondre
Auteur :


e-mail* :


Le commentaire :


#
14
De
Greg
, le
15 February 2018 08:02
I found some documentation on Internet (maybe something like github) and also directly in kernel code.

It's because online documentation is very poor that I decided to write some tutorial. Unfortuantely, it changes quickly with fast Linux updates
Répondre
Auteur :


e-mail* :


Le commentaire :


#
16
De
ranran
, le
15 February 2018 10:02
Thanks again, de Greg.
Can I ask another question please ?

1. Is it that gadgetfs does not support 3.0 (I've seen a previous question in this page about it, yet not sure what is the answer)
2. Is there any advantage in using libusb (instead of opening sysfs/proc as done here) ?

Thanks,
ranran
Répondre
Auteur :


e-mail* :


Le commentaire :


#
17
De
Greg
, le
15 February 2018 13:02
1. Gadgetfs is supported by kernel 3.0 and plus. Sometimes it's not compiled by default, you may need to build your own kernel.
2. Libusb could be good to abstract dynamic path system, but I'm not sure you can have a direct access to USB filedescriptor.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
97
De
Michel
, le
27 May 2021 08:05
Hi,
I was looking for THAT gadgetFS and found it. Thanks.
About the question, I confirm that libusb is only for host side. And libusb API supplies access to file descriptor if ever.
Your host example using ioctl is probably more efficient than libusb.
Thanks
Répondre
Auteur :


e-mail* :


Le commentaire :


#
18
De
ranran
, le
02 March 2018 06:03
Hello de Greg,

I would please like to ask about the following line in code:

write (thread_args->fd_in, buffer, strlen(buffer)+1);

It seems that device write on that endpoint (fd_in), but should not device first check that there is request on that specific endpoint (in fd_in endpoint, not to be confused with fd_out )

Thanks,
ranran
Répondre
Auteur :


e-mail* :


Le commentaire :


#
19
De
Greg
, le
02 March 2018 07:03
Hello,

This checks should normally be done by previous select() call.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
20
De
Shea Ako
, le
17 July 2018 22:07
For reference, here's a similar example:
http://www.linux-usb.org/gadget/usb.c
Répondre
Auteur :


e-mail* :


Le commentaire :


#
21
De
Greg
, le
18 July 2018 06:07
Thanks for the link
Répondre
Auteur :


e-mail* :


Le commentaire :


#
22
De
ranran
, le
04 September 2018 09:09
Hello Greg,

I need to simulate touch screen in a linux device. Is it possible using the above method for this purpose ?

Another question, Why can't I find endpoint configuration in the shell description here:
https://www.kernel.org/doc/Documentation/usb/gadget_configfs.txt

Thank you for the time,
ranran
Répondre
Auteur :


e-mail* :


Le commentaire :


#
23
De
Greg
, le
04 September 2018 11:09
Hello,

It should be possible to use it to simulate your device.

For the document you show me, do you have a kernel with configfs feature ?
Répondre
Auteur :


e-mail* :


Le commentaire :


#
24
De
ranran
, le
04 September 2018 12:09
Hello Greg,

I see that in this way we create usb with endpoints, but what if we need a simple HID (just controller endpoint),
for example from shell it is done easily with function/hid.usb0:
https://github.com/qlyoung/keyboard-gadget/blob/master/gadget-setup.sh

But I don't see how we create hid inside application.
Is there an example showing how to implement HID in application ?

Thank you,
ranran
Répondre
Auteur :


e-mail* :


Le commentaire :


#
25
De
Greg
, le
04 September 2018 13:09
I think it should be possible, but I don't know how.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
26
De
ranran
, le
04 September 2018 14:09
Thank you very much!!!
Répondre
Auteur :


e-mail* :


Le commentaire :


#
27
De
Anthony DeRosa
, le
13 October 2018 16:10
There is a bug in device.c which cost me about 3 hours of debugging. You need to initialize timeout.tv_sec = 0, or else it is undefined. In my case, the call to select was returning an error because of an invalid timeout.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
28
De
Greg
, le
13 October 2018 18:10
You're right !
Répondre
Auteur :


e-mail* :


Le commentaire :


#
29
De
julien.claisse
, le
16 October 2018 09:10
Hi,

Thanks for this tutorial.

As usb 3.0 seems to be not supported by gadgetfs, do you know another framework for usb 3.0 (user space)? Do I have to use kernel space API?
Répondre
Auteur :


e-mail* :


Le commentaire :


#
30
De
Greg
, le
16 October 2018 10:10
Maybe libusb. It depends what you want to do, I don't think it supports gadget management, but you can handle USB transfers.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
31
De
julien.claisse
, le
16 October 2018 11:10
I have 2 linux: 1 host and 1 device (gadget) which are connected through usb3.0. For the host side, I will use libusb. But for the device side, I don't know which API I have to use as GadgetFS doesn't handle usb3.0. Do you know another framework for gadget usb 3.0? I prefer user space.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
32
De
Greg
, le
16 October 2018 11:10
I don't know what could exists for your problem. But it's strange that USB3 is not supported right now by GadgetFS.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
33
De
Albert Burbea
, le
27 November 2018 10:11
Hi
thanks for sharing this work.
Do you know which modification - if any - should be applied to support USB3?
Thanks in advance
Répondre
Auteur :


e-mail* :


Le commentaire :


#
34
De
Greg
, le
27 November 2018 11:11
Unfortunately not...
Répondre
Auteur :


e-mail* :


Le commentaire :


#
35
De
Gary
, le
05 December 2018 18:12
Hi Gregory,

Thank you for the time you took on this piece.

So, how do you and when do you run this user level driver?

Could it be:
./driver

Should you run it first before the Host comes up?

Thanks for your response - Gary
Répondre
Auteur :


e-mail* :


Le commentaire :


#
36
De
Greg
, le
06 December 2018 06:12
Hello,

Your're right. There is no special argument to send to gadget. Just run it and after, start host side.

Greg
Répondre
Auteur :


e-mail* :


Le commentaire :


#
37
De
Greg
, le
06 December 2018 06:12
Hello,

Your're right. There is no special argument to send to gadget. Just run it and after, start host side.

Greg
Répondre
Auteur :


e-mail* :


Le commentaire :


#
38
De
Gary
, le
07 December 2018 12:12
Hi Greg,

On the Host side I now have:
/dev/bus/usb/001# ll
total 0
drwxr-xr-x 2 root root 80 Jan 1 00:00 ./
drwxr-xr-x 3 root root 60 Jan 1 00:00 ../
crw-rw-r-- 1 root root 189, 0 Jan 1 00:00 001
crw-rw-r-- 1 root root 189, 1 Jan 1 00:00 002

2 devices.

On the device side looks like it worked:
(there is a stalled?? and is it now in a endless rd/wr loop??)

Start init
ep0 configured

1 event(s)
EP0 CONNECT
1 event(s)
EP0 DISCONNECT
1 event(s)
EP0 CONNECT
1 event(s)
EP0 SETUP
Setup request 6
Get string id #0 (max length 255)
Found 4 bytes
1 event(s)
EP0 SETUP
Setup request 6
Get string id #2 (max length 255)
Found 28 bytes
1 event(s)
EP0 SETUP
Setup request 6
Get string id #1 (max length 255)
Found 24 bytes
1 event(s)
EP0 SETUP
Setup request 6
Get string id #3 (max length 255)
Found 10 bytes
1 event(s)
EP0 SETUP
Setup request 9
Set config value
ep1 configured
ep2 configured
1 event(s)
EP0 SETUP
Setup request 6
Get string id #4 (max length 255)
Found 50 bytes
1 event(s)
EP0 SETUP
Setup request 6
Get string id #6 (max length 255)
Found 34 bytes
1 event(s)
EP0 SETUP
Setup request 0
Stalled
1 event(s)
EP0 SETUP
Setup request 6
Get string id #0 (max length 4)
Found 4 bytes
1 event(s)
EP0 SETUP
Setup request 6
Get string id #1 (max length 254)
Found 24 bytes
1 event(s)
EP0 SETUP
Setup request 6
Get string id #0 (max length 4)
Found 4 bytes
1 event(s)
EP0 SETUP
Setup request 6
Get string id #2 (max length 254)
Found 28 bytes
1 event(s)
Répondre
Auteur :


e-mail* :


Le commentaire :


#
39
De
Gary
, le
12 December 2018 17:12
I do not have this device '/proc/bus/usb/001/002' on the Host side. What could the problem be?
Répondre
Auteur :


e-mail* :


Le commentaire :


#
40
De
Mahesh
, le
13 February 2019 17:02
Hi Greg,

Thanks for your work shared. It helped me a lot to bringup USB using gadgetfs. When I try your example, I haven't succeeded to receive bytes from host to device. The device always gets "Read error -1 (Bad address)" but write (device to host) works.

Your suggestions are highly appreciated.

Thanks,
Mahe
Répondre
Auteur :


e-mail* :


Le commentaire :


#
41
De
Gary
, le
13 February 2019 17:02
Hi Mahe,

In order to get the host side devices (in: /dev/bus/usb/001) the host side should respond to the Gadget interrupt with a .probe function. This function will establish the USB endpoints and register the device. So, it must be that the host side has no driver running.

Hope this helps, Gary
Répondre
Auteur :


e-mail* :


Le commentaire :


#
42
De
Mahesh
, le
13 February 2019 18:02
Hi Gary,

Thanks for the quick response. I have a windows host application to send and receive bytes through endpoints. In the wireshark, I can observe the packet has been sent from host to device to the correct end point. but device reading from the corresponding endpoint file discriptor prints error.

The host appln. prints successfully sent to the EP.

Looking forward to your reply.

-Mahe
Répondre
Auteur :


e-mail* :


Le commentaire :


#
43
De
Greg
, le
14 February 2019 06:02
Hello,

I should write it (this example is a bit old). But it may be more secure to use libusb on host side to manage enumeration & transferts. I think it can solve a lot of problems.


Greg
Répondre
Auteur :


e-mail* :


Le commentaire :


#
44
De
Gary
, le
14 February 2019 12:02
Mahe,

Do these open correctly in the Gadget driver?
#define USB_DEV "/dev/gadget/e0002000.usb"
#define USB_EPIN "/dev/gadget/ep1in"
#define USB_EPOUT "/dev/gadget/ep2out"
If so, I'm not sure what is wrong.

This "/dev/gadget/e0002000.usb" could be different in you case.
-Gary
Répondre
Auteur :


e-mail* :


Le commentaire :


#
45
De
Mahesh
, le
15 February 2019 17:02
Hi Gary,

Thanks for your replies. I have the correct endpoints configured. During configuration/negotiation, the device can read from endpoint0 and the EP1 and EP2 are configured. But reading from the endpoint-out by the device fails with BAD ADDRESS. I am debugging this issue. May I know if I use functionfs instead of gadgetfs it might work?

Have a nice weekend. Thanks again for your time and consideration.

-Mahe
Répondre
Auteur :


e-mail* :


Le commentaire :


#
64
De
Dawn Paul
, le
05 July 2019 06:07
Hi Mahesh,
I do have the same problem writing from host to device fails with BAD ADDRESS error. Have you resolved the issue. Please share if you know the solution
Répondre
Auteur :


e-mail* :


Le commentaire :


#
65
De
Mahesh
, le
05 July 2019 15:07
Hi Paul,


I have switched to functionfs instead of gadgetfs and the issue has been solved.

-Mahe
Répondre
Auteur :


e-mail* :


Le commentaire :


#
66
De
Dawn Paul
, le
08 July 2019 08:07
Is there any tutorials available for funcionfs. also i require usb 3.0. Does functionfs support usb 3.0.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
46
De
Gary
, le
15 February 2019 18:02
Hi Mahe,

I never used the functionfs so, I do not know.
But reading from the endpoint-out is that correct? Shouldn't you be writing to the out ep?

Regards,
Gary
Répondre
Auteur :


e-mail* :


Le commentaire :


#
47
De
Ali
, le
27 March 2019 10:03
HI Gray , Thanks alot for this post , i need your help , i configures my kernel to have gadgetfs.ko then i modprode it on my device still when i run your example it can not open these , what should i do ? Thanks in advance .

// specific to controller
#define USB_DEV "/dev/gadget/e1580000.dwc2"
#define USB_EPIN "/dev/gadget/ep1in"
#define USB_EPOUT "/dev/gadget/ep2out"
Répondre
Auteur :


e-mail* :


Le commentaire :


#
48
De
Ali
, le
27 March 2019 11:03
i resolved it , i had to mount first :
modprobe gadgetfs
mkdir /dev/gadget
mount -t gadgetfs none /dev/gadget

if you get error with the mount then make sure to have no gadget loaded unload it first . the reload it if you need it for example for debug purposes .

But i get now another issue that the write fails :
Start init
Write error -1 (No such device) ???
Répondre
Auteur :


e-mail* :


Le commentaire :


#
49
De
Mahe
, le
28 March 2019 09:03
Hi Ali,

Check if you could see the enumerated endpoints at /dev/gadget?

-Mahe
Répondre
Auteur :


e-mail* :


Le commentaire :


#
50
De
nico
, le
12 April 2019 20:04
Hi,

I tested with a kernel 3.3 with USB 3.0 enabled. After mounting gadgetfs "mount -t gadgetfs gadgetfs /dev/gadget" I can see the file "dwc3-gadget"
I changed USB_DEV to the following name: dwc3-gadge.
#define USB_DEV "dwc3-gadge"

run the code : I see the following
ep0 configured
1 event(s)
EP0 CONNECT
1 event(s)
EP0 DISCONNECT
1 event(s)
EP0 CONNECT
1 event(s)
EP0 DISCONNECT
1 event(s)

On the peripheral device, It never gets the SETUP event, any idea? is the host code needed to be running in order for the peripheral device to start the SETUP event?

I tried running the host example on the host system but it doesn't find {#define USB_DEV "/proc/bus/usb/001/002"} , in fact the host system does recognize any USB device when plugged to the peripheral while the peripheral code is running.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
51
De
Greg
, le
13 April 2019 05:04
Hello,

Is there any other devices in /proc/bus/usb/* ? 001/002 may not be the right one.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
52
De
Nico
, le
13 April 2019 18:04
There is not device under the location /proc/bus/usb/* ; in fact don't see a folder called USB in /proc/bus

On the Host , was I suppose to load anything for the gadgetFS to be detected?
Répondre
Auteur :


e-mail* :


Le commentaire :


#
53
De
Greg
, le
14 April 2019 06:04
Yes. But maybe it's just because you didn't mount usbfs and/or procfs. What about lsusb command ? Can you see any new gadget ?
Répondre
Auteur :


e-mail* :


Le commentaire :


#
54
De
Nico
, le
17 April 2019 00:04
lsusb does not show the recognition of a new device.
I didn't mount any gadget on the host device. I didn't know there was a gadget called usbfs or procfs, I looked online and didn't find any reference about usbfs or procfs.

What gadget do I have to mount on the host in order for the USB directory under /proc/bus/usb to come out?
is the gadget that you are referring to a kernel module enabled by default?
Répondre
Auteur :


e-mail* :


Le commentaire :


#
55
De
Greg
, le
17 April 2019 06:04
If lsusb don't display your gadget, that means it's not correctly registered (something went wrong).

For procfs and usbfs. Both are usually automatically mounted by your kernel/distribution :

http://www.iitk.ac.in/LDP/LDP/lfs/5.0/html/chapter06/proc.html
https://docs.vmware.com/en/VMware-Workstation-Pro/12.0/com.vmware.ws.using.doc/GUID-95232C52-C5A8-471B-A6A1-C7323A97FECA.html
Répondre
Auteur :


e-mail* :


Le commentaire :


#
56
De
Okan
, le
24 May 2019 08:05
Hi,
I've been using GadgetFS for quite a while now. It works properly in general. However, I recently realized that the source thread of the peripheral (the thread that "write"s data into the GadgetFS file descriptor) sometimes blocks indefinitely, making the data flow collapse.
I tried "poll"ing the file descriptor with POLLOUT before actually writing, which returns 1, implying there should be available space on the buffer to write to. Yet the "write" call still blocks even after polling. I suspect there IS space in the buffer but not enough for the data I'm trying to write (about 20KB each) and "poll" returns as soon as a byte of space is available.
Long story short, is there a way to check how many bytes of space is available in outgoing USB buffer (like "ioctl TIOCOUTQ" for termios)?
Thanks in advance,
Okan
Répondre
Auteur :


e-mail* :


Le commentaire :


#
57
De
Greg
, le
24 May 2019 09:05
Hello,

I don't think there is a way to check available space. Try to use libusb on host side, it may resolve your problem.


Greg
Répondre
Auteur :


e-mail* :


Le commentaire :


#
63
De
Okan
, le
12 June 2019 06:06
Thanks, Greg.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
58
De
gavin.li
, le
03 June 2019 15:06
Hello,
I've successfully run the demo code between Linux device and Ubuntu host machine. The setup event can be sent from gadgetfs to user space. The ep1 and ep2 can be still configured even though the host program is not running. But when the Linux device was connected to Windows 10 host, the setup event cannot send to the use space and ep1 and ep2 is not configured. The system is suspended and the suspend event is found in user space. What's the difference between windows and Ubuntu host? Should I need write the windows driver to ensure ep0, ep1 and ep2 can be configured correctly on the device side? Thanks.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
59
De
Greg
, le
04 June 2019 07:06
Hello,

I don't know the differences between Windows and Linux for this point. USB stack should be different, but have to work with everything... It's a strange problem. I never tested it on Windows.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
61
De
gavin.li
, le
11 June 2019 02:06
Thanks, Greg.
I'll try to use Winusb to setup windows communication and test again. Any windows communication example is appreciated.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
67
De
, le
13 August 2019 15:08
This might actually be related to https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-os-1-0-descriptors-specification

Windows tries to get a string with string id 0xee to see if the usb device supports ms os descriptors. If it gets a valid string (Something starting with MSFT100), it does additional checks, in particular, it sends some vendor-specific control transfers.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
60
De
Anthony
, le
07 June 2019 11:06
For anyone who is attempting to run this example on a newerish kernel but not the latest ones (an example would be 4.1.15) you will most likely find that the program is hanging after the initial endpoint zero config. This has to do with the fact that gadgetfs is a much older legacy system and the way UDC works with the newer composite functions like configfs has changed. To fix this you need to apply a patch to inode.c and rebuild gadgetfs.ko. The patch can be found here. https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/usb/gadget/legacy/inode.c?id=c2eb312f3137d85a715af3f9009246b98e7ba993

after applying the patch use insmod to load gadgetfs.ko before mounting the gadgetfs file system, otherwise the builtin version of gadgetfs will be used
Répondre
Auteur :


e-mail* :


Le commentaire :


#
62
De
gavin.li
, le
11 June 2019 02:06
Thanks, Anthony.
Good patch. I'll try it and feedback.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
68
De
Vishal
, le
11 December 2019 07:12
Hi,

I am using the above code to transfer data between nvidia jetson tx2 and PC application(Windows).

The Communication works as follows:
-> PC application sends a 15MB bmp image to Jetson TX2 after initial handshake
-> Jetson TX2 performs some operations on 15MB image and creates a jpg, txt and json file
-> One by one - txt , image and JSON is sent back to PC application in this order after appropriate ACK from PC

Problem :
-> The Jetson TX2 hangs after at sending a text file randomly. But, the communication between PC application and jetson does not hang when only bmp images are exchanged.
-> The point at which communication hangs I do not receive data at PC end which means the write function was not done and it's still blocking the process.

Question:
-> Is there a encoding issue with text file? Why does it work flawlessly for bmp and not for json or text?
Répondre
Auteur :


e-mail* :


Le commentaire :


#
69
De
Greg
, le
11 December 2019 08:12
Hello,

There is no encoding information in BULK exchanges, it's only binary.
Your issue may be located in data formatting and/or open/read/write issue (bad size due to encoding or something like that).

Greg
Répondre
Auteur :


e-mail* :


Le commentaire :


#
70
De
Vishal
, le
11 December 2019 11:12
Thanks for reply.

I did couple of experiments to debug this issue:

Case 1: [SUCCESS]
-> A static text file was kept with constant data and was sent after BMP image and it worked like a charm

Case 2: [FAIL]
->The content in text file was different each time it was sent to PC and communication stalled after random number of transfers

Why does the blog mention that "strings sent back to host must be encoded in UTF-16"?

Please let me know if any alternative ways of sending text and JSON back to PC.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
71
De
Greg
, le
11 December 2019 11:12
OK. So it seems to be an error inside your text file.
UTF-16 encoding is only for strings inside descriptors.
Datasheet of Jetson TX2 board mentions ethernet, Wifi and bluetooth or simply uart that can be used to debug your issue.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
72
De
Vishal
, le
11 December 2019 11:12
Hey,
I kept the same text file to which the communication broke and sent it to PC and it was successful. The communication does not fail for same content everytime it is just random.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
73
De
Vishal
, le
11 December 2019 11:12
Hey,
I kept the same text file to which the communication broke and sent it to PC and it was successful. The communication does not fail for same content everytime it is just random.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
74
De
Vishal
, le
16 December 2019 13:12
Hey,

I have latest update on this issue. If the size of text file or JSON is multiple of 512 bytes then the transfer fails.

What would be the reason for this?
Répondre
Auteur :


e-mail* :


Le commentaire :


#
75
De
Greg
, le
16 December 2019 15:12
512 bytes is commonly the size of a single bulk transfer.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
76
De
Vishal
, le
17 December 2019 11:12
I manipulated the size to be not a multiple of 512 by adding a space to the text file and everything worked fine. But root cause of the issue is unknown.

This Ti forum talks about some patch to be applied : https://e2e.ti.com/support/processors/f/791/t/739627?Linux-AM3354-AM3354-USB-fail-when-Bulk-transfer-size-is-multiple-of-512-bytes

"bug in the kernel which causes the 0-byte packet is not handled properly"
Répondre
Auteur :


e-mail* :


Le commentaire :


#
77
De
Vedant
, le
06 March 2020 12:03
Hello Greg,
I have been using gadgetfs for a while.I have been facing a problem everytime that whenever I remove the usb and reconnect it, my write function gets blocked and I'm unable to handshake again. Could you been provide the solution for it.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
78
De
Greg
, le
07 March 2020 15:03
Hello,

You should add a trace for all received events, maybe it's needed to do something on disconnect/reconnect not handled by this code.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
83
De
Vedant
, le
25 May 2020 08:05
Hello Grey,
In the disconnect event, I have cancelled and joined all the threads as well closed all the created fd's including the ep0 fd and I create everything again after connect event but still unable to handshake after reconnecting.Can you please help with the solution, what I need to add more in the disconnect event so that I can communicate with the PC.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
79
De
Subham
, le
06 April 2020 08:04
Dear Greg,

Your blog is really helpful, my device is able to create endpoint upto 5 in "/dev/gadget/".
And I am able to communicate with all the existing endpoint 1-5.

I have one question.
I want to use ep6 as communication endpoint, As gadgetfs only able to create endpoint upto 5 can you guide me how to create endpoint 6 in the device.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
80
De
Greg
, le
06 April 2020 10:04
Hello,

I suppose it's a limitation of USB specification. As mentioned here : https://www.beyondlogic.org/usbnutshell/usb5.shtml#EndpointDescriptors

you have only 4 bits for endpoints addresses (0..3) and each IN and OUT endpoint should have a different address. So if you want full duplex endpoints (IN/OUT), you have a maximum of 7 endpoints (which is greater than 5...).

If you want more endpoints, you should add an interface (an group your endpoint functions by interface).
Répondre
Auteur :


e-mail* :


Le commentaire :


#
81
De
Subham
, le
08 April 2020 13:04
Dear Greg,

First of all, thank you for your reply.

Yes my expectation also same, with 4bit unique address, It Should able to create endpoint 1-7 in "/dev/gadget/". But my device able is able to create only 1-5.

After debug came to know the value maximum supported endpoint is coming from the HW register GHWPARAMS3 "device.c". I think the value is fixed to 12 as a result gadgetfs only able to create endpoint ep0 - ep5. Any way we can change the fixed value to 14.

And as per your suggestion if I introduce one more interface, Do you think it will allow to create 6th endpoint? I am little sceptical about it.

One more suggestion I want from you, Can you have user defined endpoint number?
I mean instead of creating unnecessary endpoint can I create user defined number endpoint(in this case may be ep6). Will it work?
Répondre
Auteur :


e-mail* :


Le commentaire :


#
82
De
Greg
, le
08 April 2020 14:04
With one more interface you should be able to create other endpoints.
Each endpoint is related to one interface, so result should be something like :

interface 0: ep0 -> ep5
interface 1: ep0 -> ep5
...

Then, you have to write your USB protocol to correctly use them. Don't forget that a USB device is something hardware and it was not designed to have a dynamic number of endpoints nor a great number of it. Do simple things. If necessary, add a route management/routage layer to your protocol.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
84
De
Martin
, le
09 February 2021 09:02
Dear Greg,

thank you for your work!

I am trying to emulate a USB-Device with a Rapsberry for automated driver tests. (So that Windows can't tell the diference between the real device and the pi)
And your example was the first that let me define freely the InterfaceClass, Endpointaddresses, etc.

I have now currently the issue that all the String Descriptors arent available.
(iManufacturer,...) On Linux host I get "Error Accessing String", Windows says "not available while device is in low power state".

Do you have any Idea how I could change that?
Répondre
Auteur :


e-mail* :


Le commentaire :


#
85
De
Greg
, le
10 February 2021 07:02
Hi,

I never saw errors relative to low power state. Is it USB3 that you use ? Did you clear the structure before using it ? (Some parameters may be incorrectly initialized). This example is a bit old and should be re wrote...
Répondre
Auteur :


e-mail* :


Le commentaire :


#
86
De
Martin
, le
10 February 2021 09:02
well for how old it is, it is awesome that you still respond to comments!

Im using a Raspberry Pi 4. It has a usb-c connector but the host is only USB2.
So i hope that i dont have do also look into the new stuff from USB3.

So far i "only" changed some parameters (vendor, Product, Strings) and set
device_class to 0
interfaceClass = 0xff
and changed the endpoint_address vor bulk in to 0x82

I also found that the USB_REQ_GET_STATUS was missing in handle_setup_request. So I added the response. (as far as i understod just two bytes)

On Windows i dont get the request that configures ep1 & ep2 and starts the Thread.
On Linux this his will happen but when i try to write to the endpoint i get a Input/Output Error.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
87
De
Greg
, le
10 February 2021 09:02
I think your error come from device class :
https://www.usb.org/defined-class-codes
If you set 0, you have to describe class information in interface descriptors which is not he cas in my example.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
88
De
Martin
, le
10 February 2021 09:02
Yes I think so to that this is caused due to the device class.

i have set the
bInterfaceClass = 0xff
bInterfaceSubClass = 0xff
bInterfaceProtocol = 0x2

do i miss fields in the interface descriptor?
Répondre
Auteur :


e-mail* :


Le commentaire :


#
98
De
Martin
, le
07 July 2021 15:07
Hey,
before I ask a new question I want to close this one :)

To solve this I had to implement aditional Vendor Specific SETUP requests.
And now for this device everything works perfectly fine.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
89
De
Martin
, le
10 February 2021 10:02
I found a nother issue:

at the Linux sytem i get a setup request 11 - SET_INTERFACE.
but the device never leaves the line (line 282)
ioctl(thread_args.fd_out, GADGETFS_CLEAR_HALT);
Répondre
Auteur :


e-mail* :


Le commentaire :


#
90
De
Greg
, le
10 February 2021 10:02
Don't know why, it doesn't seems to block (from linux comments).
Répondre
Auteur :


e-mail* :


Le commentaire :


#
91
De
Alvaro
, le
25 February 2021 13:02
Great work! Looks really interesting.

I'm trying to implement a USB gadget device on a product running Linux.

My setup is:
PC running Ubuntu.
Embedded system running BuildRoot(device).

I used your example to configure my embedded system as device gadget(with minor changes in some configuration strings in device.c).

The output is:

Start init
ep0 configured
[ 129.720164] gadgetfs: connected
1 event(s)
EP0 CONNECT
[ 129.727033] gadgetfs: disconnected
1 event(s)
EP0 DISCONNECT
[ 129.920062] gadgetfs: connected
1 event(s)
EP0 CONNECT
1 event(s)
EP0 SE[ 129.925141] gadgetfs: configuration #2
TUP
Setup request 6
Get string id #0 (max length 255)
Found 4 bytes
1 event(s)
EP0 SETUP
Setup request 6
Get string id #2 (max length 255)
Found 40 bytes
1 event(s)
EP0 SETUP
Setup request 6
Get string id #1 (max length 255)
Found 8 bytes
1 event(s)
EP0 SETUP
Setup request 6
Get string id #3 (max length 255)
Found 34 bytes
1 event(s)
EP0 SETUP
Setup request 9
Set config value
ep1 configured
ep2 configured


After that, I've claimed the relevant interface from my PC (using WebUSB) and sent some random data to the device, and nothing happened.
I've tried to change device.c code to print something when a message is received, but had no success.

Do you have any suggestions?

I'm trying to implement some kind of a bulk transfer gadget, that will receive data from my PC.

Thanks.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
92
De
Greg
, le
25 February 2021 14:02
Communication seems functional as you receive string requests but I cannot help you with only your report. This example worked (if I read latest comments).
Répondre
Auteur :


e-mail* :


Le commentaire :


#
93
De
Alvaro
, le
28 February 2021 10:02
Thanks for the reply.

Here is an example:

I'm trying to use a python script (usb.core) to send messages to the usb gadget.
I'm using the script to send "Example" string to the gadget.

I can see the data sent using Wireshark, but nothing happens on gadget.
I'm just trying to trigger some output in the gadget.

Work flow:
* Running "device" bin file in usb gadget.
* Wireshark captures a "GET DESCRIPTOR Response CONFIGURATION" packet sent from USB gadget to my PC.

Source: 3.17.0, Destination: host, Protocol: USB, Length: 96

0000 00 a9 2f 0a 28 90 ff ff 43 02 80 11 03 00 2d 00 .©/.(.ÿÿC.....-.
0010 7e 6a 3b 60 00 00 00 00 ee 43 0d 00 00 00 00 00 ~j;`....îC......
0020 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 ... ...........
0030 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 ................
0040 09 02 20 00 01 02 04 c0 01 09 04 00 00 02 ff 42 .. ....À......ÿB
0050 03 06 07 05 81 02 00 02 00 07 05 02 02 00 02 00 ................

* running my python script to send "Example" string.
* Wireshark captures 2 URB_BULK out packets.

Source: host, Destination: 3.17.2, Protocol: USB, Length: 71

0000 c0 cc 2a f9 27 90 ff ff 53 03 02 11 03 00 2d 00 ÀÌ*ù'.ÿÿS.....-.
0010 60 6b 3b 60 00 00 00 00 9b db 0a 00 8d ff ff ff `k;`.....Û...ÿÿÿ
0020 07 00 00 00 07 00 00 00 00 00 00 00 00 00 00 00 ................
0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0040 45 78 61 6d 70 6c 65 Example


Source: 3.17.2, Destination: host, Protocol: USB, Length: 64

0000 c0 cc 2a f9 27 90 ff ff 43 03 02 11 03 00 2d 3e ÀÌ*ù'.ÿÿC.....->
0010 60 6b 3b 60 00 00 00 00 c9 db 0a 00 b9 ff ff ff `k;`....ÉÛ..¹ÿÿÿ
0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................


As you can see, the data is sent to gadget, but nothing happens.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
94
De
Alvaro
, le
28 February 2021 11:02
After more debugging I found that the process get stuck at line:

ret = read (thread_args->fd_out, buffer, sizeof(buffer));

in function static void* io_thread(void* arg)
In file device.c
Répondre
Auteur :


e-mail* :


Le commentaire :


#
95
De
Greg
, le
28 February 2021 12:02
Maybe a problem due to packet size. Here, we expect 512 bytes (default BULK packet size), not sure this is what you send.
Another problem could be cache. Some libraries doesn't send packets immediately.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
96
De
Alvaro
, le
28 February 2021 12:02
I've tried to send a 512B packet, same error.
"usb.core.USBTimeoutError: [Errno 110] Operation timed out"

The same thing happens when I try to send data to the gadget using 'usb' module (nodejs), and with WebUsb.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
99
De
Martin
, le
07 July 2021 16:07
Dear Greg,

I now ran into a realy strange issue.

When i try to implement a standard Printer Class:
bInterfaceClass = 0x7
bInterfaceSubClass = 0x1
bInterfaceProtocol = 0x2

now the issue is that as soon as some Bulk out transfer happens, the ep0 "crashes".
The data on the Bulk transfer is transmitted correctly but when the next Setup command is send to ep0 the line in handle_ep0(...)

ret = read(fd, events, sizeof(events));

generates: Error 51 (level 2 halted)

I am not shure what that error means/how it is generated.
Do you have any knowledge to this error?
Répondre
Auteur :


e-mail* :


Le commentaire :


#
100
De
Greg
, le
09 July 2021 12:07
Hello,

Sorry, this doesn't sounds to me. I think it's generated by kernel itself, do you fit correctly printer protocol ?
Répondre
Auteur :


e-mail* :


Le commentaire :


#
101
De
Martin
, le
21 October 2021 13:10
Hey,

before adding a new question I give a short solution to this one:
The problem was a wrong wMaxPacketSize for the endpoints.


now to my new Issue:
I now have some Class request OUT with additional data (wLength = 255).
so when I try to read the endpoint0 file inside of handle_setup_request

read(fd, request_buffer, setup->wLength);

it returns 255 but the data inside the buffer is the last data I have send via ep0.
It is my first time that i have to process the additional data in a Setup request on ep0. With the USB-Debugger i see that it is transmitted from the host.

Is there something special for reading data on ep0?
Répondre
Auteur :


e-mail* :


Le commentaire :


#
102
De
Greg
, le
24 October 2021 07:10
Hi,

From the specification : http://sdpha2.ucsd.edu/Lab_Equip_Manuals/usb_20.pdf (9.3.5).

"On an input request, a device must never return more data than is indicated by the wLength value; it may
return less. On an output request, wLength will always indicate the exact amount of data to be sent by the
host. Device behavior is undefined if the host should send more data than is specified in wLength."

If I understand well, there is no 255 bytes on the buffer. Instead, you should not send more than 255 bytes to the host.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
103
De
Martin
, le
25 October 2021 11:10
Hi,

well there is data - or at least should be.

so the situation is as follows:

the function

static void handle_setup_request(int fd, struct usb_ctrlrequest *setup)

gets called with
setup->bRequestType: 0xA1
setup->bRequest: 0x09
setup->wValue: 0x0202
setup->wIndex: 0x0001
setup->wLength: 0x00ff

so the Host has send 255 Bytes of data.

if i now do:
ret = read(fd, request_buffer, setup->wLength);

It reads 255 bytes. But the resulting content of request_buffer is not the data that the host send. It is the data that was last send to the host (eg. a USB_DT_STRING)
Répondre
Auteur :


e-mail* :


Le commentaire :


#
104
De
Martin
, le
25 October 2021 12:10
sorry it is:

setup->bRequestType: 0x21
Répondre
Auteur :


e-mail* :


Le commentaire :


#
105
De
Greg
, le
25 October 2021 13:10
OK. It's an HID_SET_REPORT request. I don't have a response to your question (I suppose data is available on another endpoint). Look for this in the literature.
Répondre
Auteur :


e-mail* :


Le commentaire :


#
106
De
Martin
, le
25 October 2021 15:10
If I add a poll with > 400ms timeout I sometimes have the right data in the request buffer. But everytime poll returns 0 (means timeout happend)

struct pollfd fds[1];
fds[0].fd = fd;
fds[0].events = POLLIN;

poll(fds, 1, 400);

I have no clue whats the problem...
Répondre
Auteur :


e-mail* :


Le commentaire :


#
107
De
Martin
, le
03 December 2021 10:12
Hello again,

I want to give a quick update on my progress.
I found that this is a bug in gadgetfs. When you have more than 64Byte in the setup_request it does odd stuff.
And there is a even more strange "fix":
in usb/gadget/legacy/inode.c changing N_EVENT to 20 results in a stable behaviour on the Raspberry Pi 4

Cheers!
Répondre
Auteur :


e-mail* :


Le commentaire :


#
108
De
Ron
, le
02 August 2023 16:08
Dear Greg,

I am trying to read a Vendor OUT transaction to ep0 that immediately follows a SETUP transaction on ep0 but am not sure how to do it on GadgetFS. Do you have any ideas?

Cheers!
Répondre
Auteur :


e-mail* :


Le commentaire :


#
109
De
Greg
, le
16 August 2023 13:08
Solution from Ron :

Just need to read USB_DEV file.

Thanks !
Répondre
Auteur :


e-mail* :


Le commentaire :


Auteur :


e-mail* :


Le commentaire :




* Seulement pour être notifié d'une réponse à cet article
* Only for email notification