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


Auteur :


e-mail* :


Le commentaire :




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