Some folk wanted a user mode API, so their drivers could
be written by developers without kernel skillz.  (And
making it easier to have non-GPL drivers.)

So I thought I'd mention that there _is_ such an API in
bk://kernel.bkbits.net/db/linux/gadget-2.[45] repositories,
called "gadgetfs".

It's not like "usbfs" in several ways.  Certainly, it's
not a host side API; drivers implement control requests,
rather than issuing them.  It uses one file descriptor
per endpoint, instead of multiplexing them.  And it
doesn't use ioctl() calls all over the place ... which
among other things makes it practical to use this from
programming environments like Perl or Java, where ioctl
access has issues that C programmers often ignore.

On 2.5 it uses "libfs", and rather than figure out enough
of the filesystem code to backport that, the 2.4 code just
uses character special devices.  (People who know the FS
layer better than I do might contribute better solutions.)

I attach a sample driver, runs on 2.4 and 2.5, so you
can see how it works.  This uses pthreads, since the FS
code doesn't yet have poll()/select() support, 2.5 AIO,
or even (in most places) O_ASYNC support.  Again, adding
such mechanisms would be good contributions.

This is just FYI for the moment.  However, it does seem
to me that this "fd per endpoint" model would be a good
thing to adopt for "usbfs2" (whenever that happens).

- Dave
/* cc -Wall -g -Ikernel2x/include -o usb usb.c usbstring.c -lpthread */

/*
 * this is an example pthreaded USER MODE driver implementing a
 * USB Gadget/Device with simple bulk source/sink functionality.
 *
 * needs "gadgetfs" and a USB device controller in the kernel.
 */

#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <memory.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <ctype.h>

#include <sys/ioctl.h>
#include <asm/byteorder.h>

#include <linux/types.h>
#include <linux/usb_gadgetfs.h>
#include <linux/usb_ch9.h>

#include "usbstring.h"


static int verbose;

#define NET2280                 /* the controller we expect */


/*-------------------------------------------------------------------------*/

/* controller-specific configuration */

#ifdef  NET2280
#define DEVNAME                 "net2280"
#define HIGHSPEED
#define DRIVER_VERSION_NUM      0x0000
#define EP_IN_NUM               7
static const char EP_IN_NAME [] = "ep-a";
#define EP_OUT_NUM              3
static const char EP_OUT_NAME [] = "ep-b";
#define EP_STATUS_NUM           11
static const char EP_STATUS_NAME [] = "ep-f";
#endif  /* NET2280 */

#ifdef  PXA25X
#define DEVNAME                 "pxa25x"
#define DRIVER_VERSION_NUM      0x0001
#define EP_IN_NUM               6
static const char EP_IN_NAME [] = "ep6in-bulk";
#define EP_OUT_NUM              7
static const char EP_OUT_NAME [] = "ep7out-bulk";
#define EP_STATUS_NUM           11
static const char EP_STATUS_NAME [] = "ep11in-bulk";
#endif  /* PXA25X */

#ifdef  SA1100
#define DEVNAME                 "sa1100"
#define DRIVER_VERSION_NUM      0x0002
#define EP_IN_NUM               2
static const char EP_IN_NAME [] = "ep2in-bulk";
#define EP_OUT_NUM              1
static const char EP_OUT_NAME [] = "ep1out-bulk";
#endif  /* PXA25X */


/*-------------------------------------------------------------------------*/

/* Thanks to NetChip Technologies for donating this product ID.
 *
 * DO NOT REUSE THESE IDs with any other driver!!  Ever!!
 * Instead:  allocate your own, using normal USB-IF procedures.
 */
#define DRIVER_VENDOR_NUM       0x0525          /* NetChip */
#define DRIVER_PRODUCT_NUM      0xa4a4          /* user mode sink/src */

/* NOTE:  these IDs don't imply endpoint numbering; host side drivers
 * should use endpoint descriptors, or perhaps bcdDevice, to configure
 * such things.  Other product IDs could have different policies.
 */

/*-------------------------------------------------------------------------*/

#define STRINGID_MFGR           1
#define STRINGID_PRODUCT        2
#define STRINGID_SERIAL         3
#define STRINGID_CONFIG         4
#define STRINGID_INTERFACE      5

static const struct usb_device_descriptor
device_desc = {
        .bLength =              sizeof device_desc,
        .bDescriptorType =      USB_DT_DEVICE,

        .bcdUSB =               __constant_cpu_to_le16 (0x0200),
        .bDeviceClass =         USB_CLASS_VENDOR_SPEC,
        .bDeviceSubClass =      0,
        .bDeviceProtocol =      0,
        // .bMaxPacketSize0 ... set by gadgetfs
        .idVendor =             __constant_cpu_to_le16 (DRIVER_VENDOR_NUM),
        .idProduct =            __constant_cpu_to_le16 (DRIVER_PRODUCT_NUM),
        .bcdDevice =            __constant_cpu_to_le16 (DRIVER_VERSION_NUM),
        .iManufacturer =        STRINGID_MFGR,
        .iProduct =             STRINGID_PRODUCT,
        .iSerialNumber =        STRINGID_SERIAL,
        .bNumConfigurations =   1,
};

#define MAX_USB_POWER           1

#define CONFIG_VALUE            3

#ifdef EP_STATUS_NUM
#       define  N_EP    3
#else
#       define  N_EP    2
#endif

static const struct usb_config_descriptor
config = {
        .bLength =              sizeof config,
        .bDescriptorType =      USB_DT_CONFIG,

        /* must compute wTotalLength ... */
        .bNumInterfaces =       1,
        .bConfigurationValue =  CONFIG_VALUE,
        .iConfiguration =       STRINGID_CONFIG,
        .bmAttributes =         USB_CONFIG_ATT_ONE
                                        | USB_CONFIG_ATT_SELFPOWER,
        .bMaxPower =            (MAX_USB_POWER + 1) / 2,
};

static const struct usb_interface_descriptor
source_sink_intf = {
        .bLength =              sizeof source_sink_intf,
        .bDescriptorType =      USB_DT_INTERFACE,

        .bNumEndpoints =        N_EP,
        .bInterfaceClass =      USB_CLASS_VENDOR_SPEC,
        .iInterface =           STRINGID_INTERFACE,
};

static const struct usb_endpoint_descriptor
fs_source_desc = {
        .bLength =              USB_DT_ENDPOINT_SIZE,
        .bDescriptorType =      USB_DT_ENDPOINT,

        .bEndpointAddress =     EP_IN_NUM | USB_DIR_IN,
        .bmAttributes =         USB_ENDPOINT_XFER_BULK,
        /* NOTE some controllers may need FS bulk max packet size
         * to be smaller.  it would be a chip-specific option.
         */
        .wMaxPacketSize =       __constant_cpu_to_le16 (64),
};

static const struct usb_endpoint_descriptor
fs_sink_desc = {
        .bLength =              USB_DT_ENDPOINT_SIZE,
        .bDescriptorType =      USB_DT_ENDPOINT,

        .bEndpointAddress =     EP_OUT_NUM,
        .bmAttributes =         USB_ENDPOINT_XFER_BULK,
        .wMaxPacketSize =       __constant_cpu_to_le16 (64),
};

#ifdef EP_STATUS_NUM

#define STATUS_MAXPACKET        8
#define LOG2_STATUS_POLL_MSEC   3

static const struct usb_endpoint_descriptor
fs_status_desc = {
        .bLength =              USB_DT_ENDPOINT_SIZE,
        .bDescriptorType =      USB_DT_ENDPOINT,

        .bEndpointAddress =     EP_STATUS_NUM | USB_DIR_IN,
        .bmAttributes =         USB_ENDPOINT_XFER_INT,
        .wMaxPacketSize =       __constant_cpu_to_le16 (STATUS_MAXPACKET),
        .bInterval =            (1 << LOG2_STATUS_POLL_MSEC),
};
#endif

static const struct usb_endpoint_descriptor *fs_eps [3] = {
        &fs_source_desc,
        &fs_sink_desc,
#ifdef EP_STATUS_NUM
        &fs_status_desc,
#endif
};


#ifdef  HIGHSPEED

/* NOTE:  it's important to hand only high speed descriptors to
 * high speed hosts.  (Full speed ones go only to full speed hosts.)
 */

static const struct usb_endpoint_descriptor
hs_source_desc = {
        .bLength =              USB_DT_ENDPOINT_SIZE,
        .bDescriptorType =      USB_DT_ENDPOINT,

        .bEndpointAddress =     EP_IN_NUM | USB_DIR_IN,
        .bmAttributes =         USB_ENDPOINT_XFER_BULK,
        .wMaxPacketSize =       __constant_cpu_to_le16 (512),
};

static const struct usb_endpoint_descriptor
hs_sink_desc = {
        .bLength =              USB_DT_ENDPOINT_SIZE,
        .bDescriptorType =      USB_DT_ENDPOINT,

        .bEndpointAddress =     EP_OUT_NUM,
        .bmAttributes =         USB_ENDPOINT_XFER_BULK,
        .wMaxPacketSize =       __constant_cpu_to_le16 (512),
        .bInterval =            1,
};

#ifdef EP_STATUS_NUM
static const struct usb_endpoint_descriptor
hs_status_desc = {
        .bLength =              USB_DT_ENDPOINT_SIZE,
        .bDescriptorType =      USB_DT_ENDPOINT,

        .bEndpointAddress =     EP_STATUS_NUM | USB_DIR_IN,
        .bmAttributes =         USB_ENDPOINT_XFER_INT,
        .wMaxPacketSize =       __constant_cpu_to_le16 (STATUS_MAXPACKET),
        .bInterval =            LOG2_STATUS_POLL_MSEC + 3,
};
#endif

static const struct usb_endpoint_descriptor *hs_eps [] = {
        &hs_source_desc,
        &hs_sink_desc,
#ifdef EP_STATUS_NUM
        &hs_status_desc,
#endif
};

#endif  /* HIGHSPEED */

/*-------------------------------------------------------------------------*/

static char serial [32];

struct usb_string stringtab [] = {
        { STRINGID_MFGR,        "Licensed to Code, LLC", },
        { STRINGID_PRODUCT,     "My Product", },
        { STRINGID_SERIAL,      serial, },
        { STRINGID_CONFIG,      "The Configuration", },
        { STRINGID_INTERFACE,   "Source/Sink", },
};

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

/*-------------------------------------------------------------------------*/

/* full duplex data, with at least three threads: ep0, sink, and source */

static pthread_t        ep0;

static pthread_t        source;
static int              source_fd = -1;

static pthread_t        sink;
static int              sink_fd = -1;

// FIXME no status i/o yet

static void close_fd (void *fd_ptr)
{
        int     status, fd;

        fd = *(int *)fd_ptr;
        *(int *)fd_ptr = -1;

        /* test the FIFO ioctls (non-ep0 code paths) */
        if (pthread_self () != ep0) {
                status = ioctl (fd, GADGETFS_FIFO_STATUS);
                if (status < 0) {
                        /* ENODEV reported after disconnect */
                        if (errno != ENODEV)
                                perror ("get fifo status");
                } else {
                        fprintf (stderr, "fd %d, unclaimed = %d\n",
                                fd, status);
                        if (status) {
                                status = ioctl (fd, GADGETFS_FIFO_FLUSH);
                                if (status < 0)
                                        perror ("fifo flush");
                        }
                }
        }

        if (close (fd) < 0)
                perror ("close");
}

/* FIXME share more code between source/sink threads */

static void *source_thread (void *param)
{
        char            *name = (char *) param;
        int             status;
        char            buf [8 * 1024];
        unsigned long   id;

        id = pthread_self ();

        /* open and initialize with endpoint descriptor(s) */
        source_fd = open (name, O_RDWR);
        if (source_fd < 0) {
                perror ("open source ep");
                return 0;
        }

        /* one (fs or ls) or two (fs + hs) sets of config descriptors */
        *(__u32 *)buf = 1;      /* tag for this format */
        memcpy (buf + 4, &fs_source_desc, USB_DT_ENDPOINT_SIZE);
#ifdef  HIGHSPEED
        memcpy (buf + 4 + USB_DT_ENDPOINT_SIZE,
                        &hs_source_desc, USB_DT_ENDPOINT_SIZE);
#endif
        status = write (source_fd, buf,
                4 + USB_DT_ENDPOINT_SIZE
#ifdef  HIGHSPEED
                        + USB_DT_ENDPOINT_SIZE
#endif
                );
        if (status < 0) {
                perror ("write source descriptors");
                close (source_fd);
                return 0;
        }

        pthread_cleanup_push (close_fd, &source_fd);
        if (verbose)
                fprintf (stderr, "start %ld fd %d %s\n",
                        id, source_fd, __FUNCTION__);
        do {
                unsigned        len = sizeof buf;

#ifdef  DO_PIPE
                /* pipe stdin to host */
                len = fread (buf, 1, len, stdin);
#else
                /* synchronous writes of endless streams of zeros */
                memset (buf, 0, len);
#endif

                /* original LinuxThreads cancelation doesn't work right
                 * so test for it explicitly.
                 */
                pthread_testcancel ();
                status = write (source_fd, buf, len);

        } while (status > 0);
        if (status == 0) {
                if (verbose)
                        fprintf (stderr, "done %ld %s\n", id, __FUNCTION__);
        } else if (verbose > 2 || errno != ESHUTDOWN) /* normal disconnect */
                perror (__FUNCTION__);
        fflush (stderr);
        pthread_cleanup_pop (1);

        return 0;
}

static void *sink_thread (void *param)
{
        char            *name = (char *) param;
        int             status;
        char            buf [8 * 1024];
        unsigned long   id;

        id = pthread_self ();

        /* open and initialize with endpoint descriptor(s) */
        sink_fd = open (name, O_RDWR);
        if (sink_fd < 0) {
                perror ("open sink ep");
                return 0;
        }

        /* one (fs or ls) or two (fs + hs) sets of config descriptors */
        *(__u32 *)buf = 1;      /* tag for this format */
        memcpy (buf + 4, &fs_sink_desc, USB_DT_ENDPOINT_SIZE);
#ifdef  HIGHSPEED
        memcpy (buf + 4 + USB_DT_ENDPOINT_SIZE,
                        &hs_sink_desc, USB_DT_ENDPOINT_SIZE);
#endif
        status = write (sink_fd, buf,
                4 + USB_DT_ENDPOINT_SIZE
#ifdef  HIGHSPEED
                        + USB_DT_ENDPOINT_SIZE
#endif
                );
        if (status < 0) {
                perror ("write sink descriptors");
                close (sink_fd);
                return 0;
        }

        /* synchronous reads of endless streams of data */
        pthread_cleanup_push (close_fd, &sink_fd);
        if (verbose)
                fprintf (stderr, "start %ld fd %d %s\n",
                        id, sink_fd, __FUNCTION__);
        do {
                /* original LinuxThreads cancelation doesn't work right
                 * so test for it explicitly.
                 */
                pthread_testcancel ();
                errno = 0;
                status = read (sink_fd, buf, sizeof buf);

                if (status < 0)
                        break;
#ifdef  DO_PIPE
                /* pipe from host to stdout */
                if (status > 0) {
                        int             len;

                        len = fwrite (buf, status, 1, stdout);
                        if (len != status) {
                                if (ferror (stdout))
                                        perror ("write stdout");
                                break;
                        }
                }
#endif
        } while (status > 0);
        if (status == 0) {
                if (verbose)
                        fprintf (stderr, "done %ld %s\n", id, __FUNCTION__);
        } else if (verbose > 2 || errno != ESHUTDOWN) /* normal disconnect */
                perror (__FUNCTION__);
#ifdef  DO_PIPE
        fflush (stdout);
#endif
        fflush (stderr);
        pthread_cleanup_pop (1);

        return 0;
}

static void start_io ()
{
        sigset_t        allsig, oldsig;

        sigfillset (&allsig);
        errno = pthread_sigmask (SIG_SETMASK, &allsig, &oldsig);
        if (errno < 0) {
                perror ("set thread signal mask");
                return;
        }

        if (pthread_create (&source, 0,
                        source_thread, (void *) EP_IN_NAME) != 0) {
                perror ("can't create source thread");
                goto cleanup;
        }

        if (pthread_create (&sink, 0,
                        sink_thread, (void *) EP_OUT_NAME) != 0) {
                perror ("can't create sink thread");
                pthread_cancel (source);
                source = ep0;
                goto cleanup;
        }

cleanup:
        errno = pthread_sigmask (SIG_SETMASK, &oldsig, 0);
        if (errno != 0) {
                perror ("restore sigmask");
                exit (-1);
        }
}

static void stop_io ()
{
        if (!pthread_equal (source, ep0)) {
                pthread_cancel (source);
                if (pthread_join (source, 0) != 0)
                        perror ("can't join source thread");
                source = ep0;
        }

        if (!pthread_equal (sink, ep0)) {
                pthread_cancel (sink);
                if (pthread_join (sink, 0) != 0)
                        perror ("can't join sink thread");
                sink = ep0;
        }
}

/*-------------------------------------------------------------------------*/

static char *
build_config (char *cp, const struct usb_endpoint_descriptor *ep [N_EP])
{
        struct usb_config_descriptor *c;
        int i;

        c = (struct usb_config_descriptor *) cp;

        memcpy (cp, &config, config.bLength);
        cp += config.bLength;
        memcpy (cp, &source_sink_intf, source_sink_intf.bLength);
        cp += source_sink_intf.bLength;

        for (i = 0; i < N_EP; i++) {
                memcpy (cp, ep [i], USB_DT_ENDPOINT_SIZE);
                cp += USB_DT_ENDPOINT_SIZE;
        }
        c->wTotalLength = __cpu_to_le16 (cp - (char *) c);
        return cp;
}

static int init_device (char *path)
{
        char            buf [4096], *cp = &buf [0];
        int             fd;
        int             status;

        fd = open (path, O_RDWR);
        if (fd < 0) {
                perror (path);
                return -errno;
        }

        *(__u32 *)cp = 0;       /* tag for this format */
        cp += 4;

        /* write full then high speed configs */
        cp = build_config (cp, fs_eps);
#ifdef  HIGHSPEED
        cp = build_config (cp, hs_eps);
#endif

        /* and device descriptor at the end */
        memcpy (cp, &device_desc, sizeof device_desc);
        cp += sizeof device_desc;

        status = write (fd, &buf [0], cp - &buf [0]);
        if (status < 0) {
                perror ("write dev descriptors");
                close (fd);
                return status;
        } else if (status != (cp - buf)) {
                fprintf (stderr, "dev init, wrote %d expected %d\n",
                                status, cp - buf);
                close (fd);
                return -EIO;
        }
        return fd;
}

static void handle_control (int fd, struct usb_ctrlrequest *setup)
{
        int             status, tmp;
        __u8            buf [256];

        if (verbose)
                fprintf (stderr, "SETUP %02x.%02x "
                                "v%04x i%04x %d\n",
                        setup->bRequestType, setup->bRequest,
                        setup->wValue, setup->wIndex,
                        setup->wLength);

        /*
        if ((setup->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
                goto stall;
        */

        switch (setup->bRequest) {      /* usb 2.0 spec ch9 requests */
        case USB_REQ_GET_DESCRIPTOR:
                if (setup->bRequestType != USB_DIR_IN)
                        goto stall;
                switch (setup->wValue >> 8) {
                case USB_DT_STRING:
                        tmp = setup->wValue & 0x0ff;
                        if (verbose > 1)
                                fprintf (stderr,
                                        "... get string %d lang %04x\n",
                                        tmp, setup->wIndex);
                        if (tmp != 0 && setup->wIndex != strings.language)
                                goto stall;
                        status = usb_gadget_get_string (&strings, tmp, buf);
                        if (status < 0)
                                goto stall;
                        tmp = status;
                        if (setup->wLength < tmp)
                                tmp = setup->wLength;
                        status = write (fd, buf, tmp);
                        if (status < 0) {
                                if (errno == EIDRM)
                                        fprintf (stderr, "string timeout\n");
                                else
                                        perror ("write string data");
                        } else if (status != tmp) {
                                fprintf (stderr, "short string write, %d\n",
                                        status);
                        }
                        break;
                default:
                        goto stall;
                }
                return;
        case USB_REQ_GET_INTERFACE:
                if (setup->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)
                                || setup->wIndex != 0
                                || setup->wLength > 1)
                        goto stall;
                
                /* only one altsetting in this driver */
                buf [0] = 0;
                status = write (fd, buf, setup->wLength);
                if (status < 0) {
                        if (errno == EIDRM)
                                fprintf (stderr, "GET_INTERFACE timeout\n");
                        else
                                perror ("write GET_INTERFACE data");
                } else if (status != setup->wLength) {
                        fprintf (stderr, "short GET_INTERFACE write, %d\n",
                                status);
                }
                return;
        case USB_REQ_SET_INTERFACE:
                if (setup->bRequestType != USB_RECIP_INTERFACE
                                || setup->wIndex != 0
                                || setup->wValue != 0)
                        goto stall;

                /* just reset toggle/halt for the interface's endpoints */
                status = 0;
                if (ioctl (source_fd, GADGETFS_CLEAR_HALT) < 0) {
                        status = errno;
                        perror ("reset source fd");
                }
                if (ioctl (sink_fd, GADGETFS_CLEAR_HALT) < 0) {
                        status = errno;
                        perror ("reset sink fd");
                }
                if (status)
                        goto stall;

                /* ... and ack */
                status = read (fd, &status, 0);
                if (status)
                        perror ("ack SET_INTERFACE");
                return;
        default:
                goto stall;
        }

stall:
        if (verbose)
                fprintf (stderr, "... protocol stall %02x.%02x\n",
                        setup->bRequestType, setup->bRequest);
        sleep (3);
        if (setup->bRequestType & USB_DIR_IN)
                status = read (fd, &status, 0);
        else
                status = write (fd, &status, 0);
        if (status != -1)
                fprintf (stderr, "can't stall ep0 for %02x.%02x\n",
                        setup->bRequestType, setup->bRequest);
        else if (errno != EL2HLT)
                perror ("ep0 stall");
}

static void signothing (int sig, siginfo_t *info, void *ptr)
{
        /* NOP */
        if (verbose > 2)
                fprintf (stderr, "%s %d\n", __FUNCTION__, sig);
}

static const char *speed (enum usb_device_speed s)
{
        switch (s) {
        case USB_SPEED_LOW:     return "low speed";
        case USB_SPEED_FULL:    return "full speed";
        case USB_SPEED_HIGH:    return "high speed";
        default:                return "UNKNOWN speed";
        }
}

/*-------------------------------------------------------------------------*/

/* control thread, handles main event loop  */

#define NEVENT  5

static void *ep0_thread (void *param)
{
        int                     fd = *(int*) param;
        struct sigaction        action, old;
        sigset_t                sigs;

        source = sink = ep0 = pthread_self ();
        pthread_cleanup_push (close_fd, param);

        /* SIGIO tells file owner that a device event is available */
        if (fcntl (fd, F_SETOWN, getpid ()) < 0) {
                perror ("set ep0 owner");
                return 0;
        }
        if (fcntl (fd, F_SETFL, O_ASYNC) < 0) {
                perror ("set ep0 async mode");
                return 0;
        }
        action.sa_sigaction = signothing;
        sigfillset (&action.sa_mask);
        action.sa_flags = SA_SIGINFO;
        if (sigaction (SIGIO, &action, &old) < 0) {
                perror ("sigaction");
                return 0;
        }

        /* event loop */
        sigfillset (&sigs);
        for (;;) {
                int                             tmp;
                struct usb_gadgetfs_event       event [NEVENT];
                int                             connected = 0;
                int                             i, nevent;

                sigwait (&sigs, &tmp);

                /* eventually any endpoint could send signals */

                switch (tmp) {
                case SIGIO:             /* used only by ep0 */
                        tmp = read (fd, &event, sizeof event);
                        if (tmp < 0) {
                                if (errno == EAGAIN) {
                                        sleep (1);
                                        continue;
                                }
                                perror ("ep0 read after sigio");
                                goto done;
                        }
                        nevent = tmp / sizeof event [0];
                        if (nevent != 1 && verbose)
                                fprintf (stderr, "read %d ep0 events\n",
                                        nevent);

                        for (i = 0; i < nevent; i++) {
                                switch (event [i].type) {
                                case GADGETFS_NOP:
                                        if (verbose)
                                                fprintf (stderr, "NOP\n");
                                        break;
                                case GADGETFS_CONNECT:
                                        connected = 1;
                                        if (verbose)
                                                fprintf (stderr,
                                                        "CONNECT %s\n",
                                                    speed (event [i].u.speed));
                                        break;
                                case GADGETFS_CONFIGURATION:
                                        connected = 1;
                                        if (verbose)
                                                fprintf (stderr,
                                                        "CONFIG %d %s\n",
                                                        event [i].u.config
                                                        .bConfigurationValue,
                                                    speed (event [i].u.speed));

                                        switch (event [i].u.config
                                                        .bConfigurationValue) {
                                        case CONFIG_VALUE:
                                                start_io ();
                                                break;
                                        case 0:
                                                stop_io ();
                                                break;
                                        default:
                                                fprintf (stderr,
                                                        "? illegal config\n");
                                                // kernel error!
                                                break;
                                        }
                                        break;
                                case GADGETFS_SETUP:
                                        connected = 1;
                                        handle_control (fd, &event [i].u.setup);
                                        break;
                                case GADGETFS_DISCONNECT:
                                        connected = 0;
                                        if (verbose)
                                                fprintf (stderr, "DISCONNECT\n");
                                        stop_io ();
                                        break;
                                case GADGETFS_SUSPEND:
                                        // connected = 1;
                                        if (verbose)
                                                fprintf (stderr, "SUSPEND\n");
                                        break;
                                default:
                                        fprintf (stderr,
                                                "* unhandled event %d\n",
                                                event [i].type);
                        }
                        }
                        continue;
                case SIGINT:
                case SIGTERM:
                case SIGUSR1:
                        fprintf (stderr, "\nquit, sig %d\n", tmp);
                        break;
                default:
                        fprintf (stderr, "\nunrecognized signal %d\n", tmp);
                        break;
                }
done:
                fflush (stdout);
                if (connected)
                        stop_io ();
                break;
        }
        if (verbose)
                fprintf (stderr, "done\n");
        fflush (stdout);

        pthread_cleanup_pop (1);
        return 0;
}

/*-------------------------------------------------------------------------*/

int
main (int argc, char **argv)
{
        int             fd, c, i;

        srand ((int) time (0));
        while ((c = getopt (argc, argv, "r:v")) != EOF) {
                switch (c) {
                case 'r':               /* nonrandom serial */
                        srand (atoi (optarg));
                        continue;
                case 'v':               /* verbose */
                        verbose++;
                        continue;
                }
                fprintf (stderr, "usage:  %s [-r num] [-v]\n",
                                argv [0]);
                return 1;
        }
        if (chdir ("/dev/gadget") < 0) {
                perror ("can't chdir /dev/gadget");
                return 1;
        }

        /* FIXME pull unique and persistent serial from an ID prom */
        for (i = 0; i < sizeof serial - 1; ) {
                c = (char) rand ();
                if (!isascii (c) || !isalnum (c))
                        continue;
                serial [i++] = c;
        }

        fd = init_device (DEVNAME);
        if (fd < 0)
                return 1;
        fprintf (stderr,
                "/dev/gadget/" DEVNAME " ep0 configured\n"
                "serial=\"%s\"\n",
                serial);
        fflush (stderr);
        (void) ep0_thread (&fd);
        return 0;
}

Reply via email to