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 :


#
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 :


Auteur :


e-mail* :


Le commentaire :




* Seulement pour être notifié d'une réponse à cet article