r1826 - trunk/src/host/qemu-neo1973

andrew at sita.openmoko.org andrew at sita.openmoko.org
Tue Apr 24 21:54:09 CEST 2007


Author: andrew
Date: 2007-04-24 21:54:08 +0200 (Tue, 24 Apr 2007)
New Revision: 1826

Added:
   trunk/src/host/qemu-neo1973/usb-linux-gadget.c
Log:
Gadgetfs support - Include the important file this time.


Added: trunk/src/host/qemu-neo1973/usb-linux-gadget.c
===================================================================
--- trunk/src/host/qemu-neo1973/usb-linux-gadget.c	2007-04-24 19:52:45 UTC (rev 1825)
+++ trunk/src/host/qemu-neo1973/usb-linux-gadget.c	2007-04-24 19:54:08 UTC (rev 1826)
@@ -0,0 +1,726 @@
+/*
+ * Linux host USB slave redirector
+ *
+ * Copyright (C) 2007 OpenMoko, Inc.
+ * Written by Andrzej Zaborowski <andrew at openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+#if defined(__linux__)
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadgetfs.h>
+#include <poll.h>
+
+/* Must be after usb_ch9.h */
+#include "vl.h"
+
+#define USBGADGETFS_PATH "/dev/gadget"
+
+struct gadget_state_s {
+    USBPort port;
+    int connected;
+    int speed;
+    int hosthighspeed;
+    int highspeed;
+    int addr;
+    int ep0fd;
+    const char *ep0path;
+    struct ep_s {
+        int fd;
+        char *path;
+        int num;
+        struct gadget_state_s *state;
+    } ep[16];
+
+    /* Device descriptor */
+    uint8_t dev_desc[128];
+    int desc_len;
+
+    /* Configuration descriptor */
+    struct usb_config_descriptor config_desc;
+    uint8_t config_tail_buffer[256];
+
+    uint8_t buffer[4096];
+    int async_count;
+    USBPacket packet[16];
+};
+
+static void gadget_stall(struct gadget_state_s *hci, USBPacket *packet)
+{
+    int ret, fd;
+    if (packet->devep)
+        fd = ((struct ep_s *) packet->complete_opaque)->fd;
+    else
+        fd = hci->ep0fd;
+
+    if (packet->pid == USB_TOKEN_IN || (packet->pid == USB_TOKEN_SETUP &&
+            (((struct usb_ctrlrequest *) packet->data)->bRequestType &
+             USB_DIR_IN)))
+        ret = read(fd, &ret, 0);
+    else
+        ret = write(fd, &ret, 0);
+    if (ret != -1)
+        fprintf(stderr, "%s: can't stall ep%i\n",
+                        __FUNCTION__, packet->devep);
+    else if (errno != EL2HLT && errno != EBADMSG)
+        fprintf(stderr, "%s: can't stall ep%i: %i\n",
+                        __FUNCTION__, packet->devep, errno);
+}
+
+static void gadget_detach(struct gadget_state_s *hci)
+{
+    char *devname;
+    if (hci->port.dev) {
+        /* XXX We should rather only detach the device
+         * (usb_attach(&hci->port, NULL)) instead of destroying it,
+         * but then the port remains in used_usb_ports -> segfault */
+        asprintf(&devname, "%i.%i", 0, hci->port.dev->addr);
+        do_usb_del(devname);
+        free(devname);
+    }
+}
+
+static void gadget_run(USBPacket *prev_packet, void *opaque)
+{
+    struct gadget_state_s *hci = (struct gadget_state_s *) opaque;
+    USBDevice *dev = hci->port.dev;
+    USBPacket *packet;
+    int ret;
+
+next:
+    if (hci->async_count) {
+        packet = &hci->packet[-- hci->async_count];
+        ret = dev->handle_packet(dev, packet);
+
+        if (ret >= 0) {
+            packet->len = ret;
+            packet->complete_cb(packet, packet->complete_opaque);
+        } else if (ret == USB_RET_STALL) {
+            hci->async_count = 0;
+            gadget_stall(hci, packet);
+        } else if (ret != USB_RET_ASYNC) {
+            fprintf(stderr, "%s: packet unhandled: %i\n", __FUNCTION__, ret);
+            if (ret == USB_RET_NAK)
+                goto next;
+            hci->async_count = 0;
+            gadget_detach(hci);
+        }
+    }
+}
+
+static void gadget_respond(USBPacket *packet, void *opaque)
+{
+    struct gadget_state_s *hci = (struct gadget_state_s *) opaque;
+    int ret;
+
+    do {
+        ret = write(hci->ep0fd, hci->buffer, packet->len);
+    } while (ret < packet->len && errno == EAGAIN);
+    if (ret < packet->len) {
+        fprintf(stderr, "%s: packet write error: %i\n", __FUNCTION__, errno);
+        return;
+    }
+
+    gadget_run(packet, hci);
+}
+
+static void gadget_token_in(USBPacket *packet, void *opaque)
+{
+    struct ep_s *ep = (struct ep_s *) opaque;
+    if (packet->len > 0)
+        write(ep->fd, packet->data, packet->len);
+}
+
+#if 0
+static int gadget_ep_poll(void *opaque)
+{
+    struct ep_s *ep = (struct ep_s *) opaque;
+    struct pollfd pfd = { ep->fd, POLLOUT, 0 };
+    int ret = poll(&pfd, 1, 0);
+    return (ret > 0) && (pfd.revents & POLLOUT);
+}
+#endif
+
+static void gadget_ep_read(void *opaque)
+{
+    struct ep_s *ep = (struct ep_s *) opaque;
+    struct gadget_state_s *hci = ep->state;
+
+#if 0
+    if (!gadget_ep_poll(opaque))
+        return;
+#endif
+
+    /* write() is supposed to not block here */
+    if (write(ep->fd, hci->buffer, 0))
+        return;
+
+    if (hci->async_count) {
+        fprintf(stderr, "%s: overrun\n", __FUNCTION__);
+        gadget_detach(hci);
+        return;
+    }
+
+    hci->async_count = 1;
+
+    hci->packet->pid = USB_TOKEN_IN;
+    hci->packet->devaddr = hci->addr;
+    hci->packet->devep = ep->num;
+    hci->packet->data = hci->buffer;
+    hci->packet->len = sizeof(hci->buffer);
+    hci->packet->complete_cb = gadget_token_in;
+    hci->packet->complete_opaque = ep;
+
+    gadget_run(0, hci);
+}
+
+static void gadget_nop(USBPacket *prev_packet, void *opaque)
+{
+}
+
+static void gadget_ep_write(void *opaque)
+{
+    struct ep_s *ep = (struct ep_s *) opaque;
+    struct gadget_state_s *hci = ep->state;
+    int ret;
+
+    ret = read(ep->fd, hci->buffer, sizeof(hci->buffer));
+    if (ret <= 0)
+        return;
+
+    if (hci->async_count) {
+        fprintf(stderr, "%s: overrun\n", __FUNCTION__);
+        gadget_detach(hci);
+        return;
+    }
+
+    hci->async_count = 1;
+
+    hci->packet->pid = USB_TOKEN_OUT;
+    hci->packet->devaddr = hci->addr;
+    hci->packet->devep = ep->num;
+    hci->packet->data = hci->buffer;
+    hci->packet->len = ret;
+    hci->packet->complete_cb = gadget_nop;
+    hci->packet->complete_opaque = ep;
+
+    gadget_run(0, hci);
+}
+
+static int gadget_ep_open(struct gadget_state_s *hci,
+                struct usb_endpoint_descriptor *desc)
+{
+    struct ep_s *ep;
+    int ret, i, dl;
+    uint32_t format;
+    uint8_t buffer[128], *dst;
+    for (ep = hci->ep, i = 0; ep->fd != -1; ep ++, i ++);
+
+    ep->state = hci;
+    ep->num = desc->bEndpointAddress & 0xf;
+    /* Only dummy_hcd and net2280 have "ep-?" configurable endpoints.
+     * Maybe we should scan all available endpoints and choose ones that
+     * match, but this would be painful.  */
+    asprintf(&ep->path, "%s/ep-%c", USBGADGETFS_PATH, 'a' + i);
+    ep->fd = open(ep->path, O_RDWR);
+    if (ep->fd < 0) {
+        ret = errno;
+        goto fail;
+    }
+
+    dl = 0;
+    dst = buffer;
+
+    /* Write the format tag */
+    format = 1;
+    memcpy(dst, &format, 4);
+    dst += 4;
+    dl += 4;
+
+    /* Write the endpoint descriptor */
+    memcpy(dst, desc, desc->bLength);
+    dst += desc->bLength;
+    dl += desc->bLength;
+
+    /* XXX write highspeed descriptor if hci->hosthighspeed */
+
+    ret = write(ep->fd, buffer, dl);
+    if (ret < dl) {
+        if (ret < 0)
+            ret = errno;
+        else
+            ret = 1;
+        close(ep->fd);
+        goto fail;
+    }
+
+    if (desc->bEndpointAddress & USB_DIR_IN)
+        qemu_set_fd_handler(ep->fd, NULL, gadget_ep_read, ep);
+    else
+        qemu_set_fd_handler(ep->fd, gadget_ep_write, NULL, ep);
+
+    return 0;
+
+fail:
+    free(ep->path);
+    ep->fd = -1;
+    return -ret;
+}
+
+static void gadget_ep_close(struct gadget_state_s *hci, int addr)
+{
+    struct ep_s *ep;
+    int i;
+    for (ep = hci->ep, i = 0; ep->num != addr || ep->fd == -1; ep ++, i ++);
+
+    qemu_set_fd_handler2(ep->fd, NULL, NULL, NULL, NULL);
+    close(ep->fd);
+    ep->fd = -1;
+    free(ep->path);
+}
+
+static void gadget_ep_done(struct gadget_state_s *hci)
+{
+    int i;
+    for (i = 0; i < 16; i ++)
+        if (hci->ep[i].fd != -1)
+            gadget_ep_close(hci, hci->ep[i].num);
+}
+
+static void gadget_ep_setup(struct gadget_state_s *hci)
+{
+    int ret, sl = 0;
+    struct usb_descriptor_header *src =
+            (struct usb_descriptor_header *) &hci->config_desc;
+
+    /* Clean-up */
+    gadget_ep_done(hci);
+
+    while (sl < hci->config_desc.wTotalLength) {
+        if (src->bDescriptorType == USB_DT_ENDPOINT) {
+            ret = gadget_ep_open(hci, (struct usb_endpoint_descriptor *) src);
+            if (ret < 0) {
+                if (ret != -ESHUTDOWN)
+                    goto fail;
+                fprintf(stderr, "%s: EPs not configured due to disconnect\n",
+                                __FUNCTION__);
+                return;
+            }
+        }
+        sl += src->bLength;
+        src = (typeof(src)) ((uint8_t *) src + src->bLength);
+    }
+    return;
+
+fail:
+    gadget_detach(hci);
+    fprintf(stderr, "%s: endpoint configuration failed: %i\n",
+                    __FUNCTION__, ret);
+}
+
+static void gadget_desc_parse(USBPacket *packet, void *opaque)
+{
+    struct gadget_state_s *hci = (struct gadget_state_s *) opaque;
+    hci->desc_len = packet->len;
+    gadget_run(packet, hci);
+}
+
+static void gadget_ep_parse(USBPacket *packet, void *opaque)
+{
+    struct gadget_state_s *hci = (struct gadget_state_s *) opaque;
+    uint8_t buffer[4096];
+    int dl = 0;
+    struct usb_config_descriptor *cfg;
+    struct usb_descriptor_header *src =
+            (struct usb_descriptor_header *) packet->data;
+    uint8_t *dst = buffer;
+    uint32_t format;
+    int ret = 1;
+
+    /* Write the format tag */
+    format = 0;
+    memcpy(dst, &format, 4);
+    dst += 4;
+    dl += 4;
+
+    /* Write the config & endpoint descriptors */
+    if (src->bDescriptorType != USB_DT_CONFIG)
+        goto fail;
+    cfg = (struct usb_config_descriptor *) src;
+    if (dl + cfg->wTotalLength > sizeof(buffer))
+        goto fail;
+    cfg->bMaxPower = 0x00;
+    cfg->bmAttributes = 0xc0; /* dummy_hcd is picky about power */
+    memcpy(dst, cfg, cfg->wTotalLength);
+    memcpy(&hci->config_desc, cfg, cfg->wTotalLength);
+    dl += cfg->wTotalLength;
+    dst += cfg->wTotalLength;
+
+    /* XXX write highspeed descriptor if hci->hosthighspeed */
+
+    /* Write the device descriptor */
+    if (dl + hci->desc_len > sizeof(buffer))
+        goto fail;
+    memcpy(dst, hci->dev_desc, hci->desc_len);
+    dl += hci->desc_len;
+    dst += hci->desc_len;
+
+    ret = write(hci->ep0fd, buffer, dl);
+    if (ret < dl) {
+        ret = -errno;
+        goto fail;
+    }
+
+    return;
+
+fail:
+    usb_attach(&hci->port, NULL); /* XXX or call gadget_detach(hci); */
+    fprintf(stderr, "%s: failed to configure gadgetfs: %i\n",
+                    __FUNCTION__, ret);
+}
+
+/* GadgetFS apparentlye xpects the device to be in Address State and
+ * not necesarily configured, at the point when device descriptor is
+ * written to ep0 fd.  Go into that state, enumerate endpoints and
+ * report endpoint and device descriptors.  */
+static void gadget_ep_configure(struct gadget_state_s *hci)
+{
+    struct usb_ctrlrequest *req;
+
+    hci->addr = 5; /* XXX How should the value be decided */
+
+    hci->async_count = 6;
+
+    /* Ask for the device descriptor */
+    hci->packet[5].pid = USB_TOKEN_SETUP;
+    hci->packet[5].devaddr = 0;
+    hci->packet[5].devep = 0;
+    hci->packet[5].data = hci->buffer;
+    hci->packet[5].len = 8;
+    hci->packet[5].complete_cb = gadget_run;
+    hci->packet[5].complete_opaque = hci;
+
+    req = (struct usb_ctrlrequest *) hci->buffer;
+    req->bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
+    req->bRequest = USB_REQ_GET_DESCRIPTOR;
+    req->wValue = (USB_DT_DEVICE << 8) | 0;
+    req->wIndex = 0x0000;
+    req->wLength = sizeof(hci->dev_desc);
+
+    /* Read the response */
+    hci->packet[4].pid = USB_TOKEN_IN;
+    hci->packet[4].devaddr = 0;
+    hci->packet[4].devep = 0;
+    hci->packet[4].data = hci->dev_desc;
+    hci->packet[4].len = sizeof(hci->dev_desc);
+    hci->packet[4].complete_cb = gadget_desc_parse;
+    hci->packet[4].complete_opaque = hci;
+
+    /* Set address */
+    hci->packet[3].pid = USB_TOKEN_SETUP;
+    hci->packet[3].devaddr = 0;
+    hci->packet[3].devep = 0;
+    hci->packet[3].data = hci->buffer + 8;
+    hci->packet[3].len = 8;
+    hci->packet[3].complete_cb = gadget_run;
+    hci->packet[3].complete_opaque = hci;
+
+    req = (struct usb_ctrlrequest *) (hci->buffer + 8);
+    req->bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
+    req->bRequest = USB_REQ_SET_ADDRESS;
+    req->wValue = hci->addr;
+    req->wIndex = 0x0000;
+    req->wLength = 0x0000;
+
+    /* Dummy read */
+    hci->packet[2].pid = USB_TOKEN_IN;
+    hci->packet[2].devaddr = 0;
+    hci->packet[2].devep = 0;
+    hci->packet[2].data = hci->buffer;
+    hci->packet[2].len = 0;
+    hci->packet[2].complete_cb = gadget_run;
+    hci->packet[2].complete_opaque = hci;
+
+    /* Ask for configuration #0 descriptor (which contains endpoints info) */
+    hci->packet[1].pid = USB_TOKEN_SETUP;
+    hci->packet[1].devaddr = hci->addr;
+    hci->packet[1].devep = 0;
+    hci->packet[1].data = hci->buffer + 16;
+    hci->packet[1].len = 8;
+    hci->packet[1].complete_cb = gadget_run;
+    hci->packet[1].complete_opaque = hci;
+
+    req = (struct usb_ctrlrequest *) (hci->buffer + 16);
+    req->bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
+    req->bRequest = USB_REQ_GET_DESCRIPTOR;
+    req->wValue = (USB_DT_CONFIG << 8) | 0;
+    req->wIndex = 0x0000;
+    req->wLength = sizeof(hci->buffer);
+
+    /* Read and parse the response */
+    hci->packet[0].pid = USB_TOKEN_IN;
+    hci->packet[0].devaddr = hci->addr;
+    hci->packet[0].devep = 0;
+    hci->packet[0].data = hci->buffer;
+    hci->packet[0].len = sizeof(hci->buffer);
+    hci->packet[0].complete_cb = gadget_ep_parse;
+    hci->packet[0].complete_opaque = hci;
+
+    usb_send_msg(hci->port.dev, USB_MSG_RESET);
+
+    gadget_run(0, hci);
+}
+
+static void gadget_read(void *opaque)
+{
+    struct gadget_state_s *s = (struct gadget_state_s *) opaque;
+    struct usb_gadgetfs_event event;
+    int ret, len;
+
+    if (!s->addr)
+        return;
+
+    ret = read(s->ep0fd, &event, sizeof(event));
+    if (ret < 0 && errno != EAGAIN)
+        fprintf(stderr, "%s: event error: %i\n", __FUNCTION__, errno);
+    if (ret < sizeof(event))
+        return;
+
+    switch (event.type) {
+    case GADGETFS_NOP:
+    case GADGETFS_SUSPEND:
+        break;
+
+    case GADGETFS_CONNECT:
+        s->connected = 1;
+        s->speed = event.u.speed;
+        gadget_ep_setup(s);
+        break;
+
+    case GADGETFS_SETUP:
+        s->connected = 1;
+        if (s->async_count) {
+            fprintf(stderr, "%s: overrun\n", __FUNCTION__);
+            gadget_detach(s);
+            return;
+        }
+
+        s->async_count = 2;
+
+        memcpy(s->buffer, &event.u.setup, 8);
+        s->packet[1].pid = USB_TOKEN_SETUP;
+        s->packet[1].devaddr = s->addr;
+        s->packet[1].devep = 0;
+        s->packet[1].data = s->buffer;
+        s->packet[1].len = 8;
+        s->packet[1].complete_cb = gadget_run;
+        s->packet[1].complete_opaque = s;
+
+        /* Handle the response */
+        if (event.u.setup.bRequestType & USB_DIR_IN) {
+            s->packet[0].pid = USB_TOKEN_IN;
+            s->packet[0].devaddr = s->addr;
+            s->packet[0].devep = 0;
+            s->packet[0].data = s->buffer;
+            s->packet[0].len = sizeof(s->buffer);
+            s->packet[0].complete_cb = gadget_respond;
+            s->packet[0].complete_opaque = s;
+        } else {
+            len = event.u.setup.wLength;
+            if (len > sizeof(s->buffer))
+                len = sizeof(s->buffer);
+            ret = read(s->ep0fd, s->buffer, len);
+            if (ret < 0) {
+                fprintf(stderr, "%s: read error\n", __FUNCTION__);
+                ret = 0;
+            }
+            s->packet[0].pid = USB_TOKEN_OUT;
+            s->packet[0].devaddr = s->addr;
+            s->packet[0].devep = 0;
+            s->packet[0].data = s->buffer;
+            s->packet[0].len = ret;
+            s->packet[0].complete_cb = gadget_run;
+            s->packet[0].complete_opaque = s;
+        }
+
+        gadget_run(0, s);
+        break;
+
+    case GADGETFS_DISCONNECT:
+        s->connected = 0;
+        s->speed = USB_SPEED_UNKNOWN;
+        gadget_ep_done(s);
+        break;
+
+    default:
+        fprintf(stderr, "%s: unhandled event: %i\n", __FUNCTION__, event.type);
+    }
+}
+
+static int gadget_open(struct gadget_state_s *hci)
+{
+    hci->ep0fd = open(hci->ep0path, O_RDWR);
+    if (hci->ep0fd < 0)
+        return -errno;
+
+    qemu_set_fd_handler(hci->ep0fd, gadget_read, NULL, hci);
+    return 0;
+}
+
+static void gadget_close(struct gadget_state_s *hci)
+{
+    gadget_ep_done(hci);
+
+    qemu_set_fd_handler(hci->ep0fd, NULL, NULL, NULL);
+
+    close(hci->ep0fd);
+}
+
+/* Attach or detach a device on the gadget hcd.  */
+static void gadget_attach(USBPort *port, USBDevice *dev)
+{
+    struct gadget_state_s *s = (struct gadget_state_s *) port->opaque;
+    int ret;
+
+    if (dev) {
+        if (port->dev) {
+            usb_attach(port, NULL);
+            /* XXX or call gadget_detach for consistency */
+        }
+
+        ret = gadget_open(s);
+        if (ret < 0) {
+            fprintf(stderr, "%s: warning: failed to open gadgetfs: %i\n",
+                            __FUNCTION__, ret);
+            return;
+        }
+
+        port->dev = dev;
+        s->highspeed = (s->hosthighspeed && dev->speed == USB_SPEED_HIGH);
+
+        /* send the attach message */
+        usb_send_msg(dev, USB_MSG_ATTACH);
+
+        gadget_ep_configure(s);
+    } else {
+        dev = port->dev;
+        if (dev) {
+            /* send the detach message */
+            usb_send_msg(dev, USB_MSG_DETACH);
+
+            gadget_close(s);
+        }
+
+        s->addr = 0;
+        port->dev = NULL;
+    }
+}
+
+static int gadget_autoconfig(struct gadget_state_s *s)
+{
+    struct stat statb;
+    s->ep0fd = -1;
+
+    s->hosthighspeed = 1;
+
+    /* dummy_hcd, high/full speed */
+    s->ep0path = USBGADGETFS_PATH "/dummy_udc";
+    if (stat(s->ep0path, &statb) == 0)
+        goto found;
+    /* NetChip 2280 PCI device, high/full speed */
+    s->ep0path = USBGADGETFS_PATH "/net2280";
+    if (stat(s->ep0path, &statb) == 0)
+        goto found;
+
+    s->hosthighspeed = 0;
+
+    /* Intel PXA 2xx processor, full speed only */
+    s->ep0path = USBGADGETFS_PATH "/pxa2xx_udc";
+    if (stat(s->ep0path, &statb) == 0)
+        goto found;
+    /* AMD au1x00 processor, full speed only */
+    s->ep0path = USBGADGETFS_PATH "/au1x00_udc";
+    if (stat(s->ep0path, &statb) == 0)
+        goto found;
+    /* Intel SA-1100 processor, full speed only */
+    s->ep0path = USBGADGETFS_PATH "/sa1100";
+    if (stat(s->ep0path, &statb) == 0)
+        goto found;
+    /* Toshiba TC86c001 PCI device, full speed only */
+    s->ep0path = USBGADGETFS_PATH "/goku_udc";
+    if (stat(s->ep0path, &statb) == 0)
+        goto found;
+    /* Renesas SH77xx processors, full speed only */
+    s->ep0path = USBGADGETFS_PATH "/sh_udc";
+    if (stat(s->ep0path, &statb) == 0)
+        goto found;
+    /* OMAP 1610 and newer devices, full speed only, fifo mode 0 or 3 */
+    s->ep0path = USBGADGETFS_PATH "/omap_udc";
+    if (stat(s->ep0path, &statb) == 0)
+        goto found;
+    /* Something based on Mentor USB Highspeed Dual-Role Controller */
+    s->ep0path = USBGADGETFS_PATH "/musb_hdrc";
+    if (stat(s->ep0path, &statb) == 0)
+        goto found;
+    /* Atmel AT91 processors, full speed only */
+    s->ep0path = USBGADGETFS_PATH "/at91_udc";
+    if (stat(s->ep0path, &statb) == 0)
+        goto found;
+    /* Sharp LH740x processors, full speed only */
+    s->ep0path = USBGADGETFS_PATH "/lh740x_udc";
+    if (stat(s->ep0path, &statb) == 0)
+        goto found;
+
+    return -ENOENT;
+
+found:
+    return 0;
+}
+
+static void gadget_done(void)
+{
+#if 0
+    if (s->ep0fd >= 0)
+        close(s->ep0fd);
+#endif
+}
+
+int usb_gadget_init(void)
+{
+    struct gadget_state_s *hci = (struct gadget_state_s *)
+            qemu_mallocz(sizeof(struct gadget_state_s));
+    int i, ret;
+    for (i = 0; i < 16; i ++)
+        hci->ep[i].fd = -1;
+
+    ret = gadget_autoconfig(hci);
+    if (ret < 0)
+        return ret;
+    atexit(gadget_done);
+
+    qemu_register_usb_port(&hci->port, hci, 0, gadget_attach);
+
+    return ret;
+}
+
+#else
+
+int usb_gadget_init(void)
+{
+    return -ENODEV;
+}
+
+#endif





More information about the commitlog mailing list