summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/usb/files.usb17
-rw-r--r--sys/dev/usb/if_cdcef.c252
-rw-r--r--sys/dev/usb/usbf.c698
-rw-r--r--sys/dev/usb/usbf_subr.c1099
-rw-r--r--sys/dev/usb/usbfvar.h175
5 files changed, 2239 insertions, 2 deletions
diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb
index 14030ef8330..38f7a403e72 100644
--- a/sys/dev/usb/files.usb
+++ b/sys/dev/usb/files.usb
@@ -1,4 +1,4 @@
-# $OpenBSD: files.usb,v 1.65 2006/10/26 04:14:09 jsg Exp $
+# $OpenBSD: files.usb,v 1.66 2006/11/25 18:10:29 uwe Exp $
# $NetBSD: files.usb,v 1.16 2000/02/14 20:29:54 augustss Exp $
#
# Config file and device description for machine-independent USB code.
@@ -10,7 +10,7 @@ attach usb at usbus
file dev/usb/usb.c usb needs-flag
file dev/usb/usbdi.c usb
file dev/usb/usbdi_util.c usb
-file dev/usb/usb_mem.c usb
+file dev/usb/usb_mem.c usb | usbf
file dev/usb/usb_subr.c usb
file dev/usb/usb_quirks.c usb
@@ -272,6 +272,19 @@ device ueagle: atm, ifnet, ezload, firmload
attach ueagle at uhub
file dev/usb/ueagle.c ueagle
+
+# USB logical device
+device usbf {}
+attach usbf at usbdev
+file dev/usb/usbf.c usbf
+file dev/usb/usbf_subr.c usbf
+
+# Communication Device Class Ethernet function
+device cdcef {}
+attach cdcef at usbf
+file dev/usb/if_cdcef.c cdcef
+
+
# Atheros AR5005UG/AR5005UX
device uath: ether, ifnet, ifmedia, wlan, firmload
attach uath at uhub
diff --git a/sys/dev/usb/if_cdcef.c b/sys/dev/usb/if_cdcef.c
new file mode 100644
index 00000000000..aaa9dc696a3
--- /dev/null
+++ b/sys/dev/usb/if_cdcef.c
@@ -0,0 +1,252 @@
+/* $OpenBSD: if_cdcef.c,v 1.1 2006/11/25 18:10:29 uwe Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * USB Communication Device Class Ethernet Emulation Model function driver
+ * (counterpart of the host-side cdce(4) driver)
+ */
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+
+#include <net/if.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbf.h>
+#include <dev/usb/usbcdc.h>
+
+#define CDCEF_VENDOR_ID 0x0001
+#define CDCEF_PRODUCT_ID 0x0001
+#define CDCEF_DEVICE_CODE 0x0100
+#define CDCEF_VENDOR_STRING "OpenBSD.org"
+#define CDCEF_PRODUCT_STRING "CDC Ethernet Emulation"
+#define CDCEF_SERIAL_STRING "1.00"
+
+#define CDCEF_BUFSZ 65536
+
+struct cdcef_softc {
+ struct usbf_function sc_dev;
+ usbf_config_handle sc_config;
+ usbf_interface_handle sc_iface;
+ usbf_endpoint_handle sc_ep_in;
+ usbf_endpoint_handle sc_ep_out;
+ usbf_pipe_handle sc_pipe_in;
+ usbf_pipe_handle sc_pipe_out;
+ usbf_xfer_handle sc_xfer_in;
+ usbf_xfer_handle sc_xfer_out;
+ void *sc_buffer_in;
+ void *sc_buffer_out;
+};
+
+int cdcef_match(struct device *, void *, void *);
+void cdcef_attach(struct device *, struct device *, void *);
+
+usbf_status cdcef_do_request(usbf_function_handle,
+ usb_device_request_t *, void **);
+
+void cdcef_start(struct ifnet *);
+
+void cdcef_txeof(usbf_xfer_handle, usbf_private_handle,
+ usbf_status);
+void cdcef_rxeof(usbf_xfer_handle, usbf_private_handle,
+ usbf_status);
+
+struct cfattach cdcef_ca = {
+ sizeof(struct cdcef_softc), cdcef_match, cdcef_attach
+};
+
+struct cfdriver cdcef_cd = {
+ NULL, "cdcef", DV_DULL
+};
+
+struct usbf_function_methods cdcef_methods = {
+ NULL, /* set_config */
+ cdcef_do_request
+};
+
+#ifndef CDCEF_DEBUG
+#define DPRINTF(x) do {} while (0)
+#else
+#define DPRINTF(x) printf x
+#endif
+
+#define DEVNAME(sc) USBDEVNAME((sc)->sc_dev.bdev)
+
+/*
+ * USB function match/attach/detach
+ */
+
+USB_MATCH(cdcef)
+{
+ return UMATCH_GENERIC;
+}
+
+USB_ATTACH(cdcef)
+{
+ struct cdcef_softc *sc = (struct cdcef_softc *)self;
+ struct usbf_attach_arg *uaa = aux;
+ usbf_device_handle dev = uaa->device;
+ char *devinfop;
+ usbf_status err;
+ usb_cdc_union_descriptor_t udesc;
+
+ /* Set the device identification according to the function. */
+ usbf_devinfo_setup(dev, UDCLASS_IN_INTERFACE, 0, 0, CDCEF_VENDOR_ID,
+ CDCEF_PRODUCT_ID, CDCEF_DEVICE_CODE, CDCEF_VENDOR_STRING,
+ CDCEF_PRODUCT_STRING, CDCEF_SERIAL_STRING);
+
+ devinfop = usbf_devinfo_alloc(dev);
+ printf(": %s\n", devinfop);
+ usbf_devinfo_free(devinfop);
+
+ /* Fill in the fields needed by the parent device. */
+ sc->sc_dev.methods = &cdcef_methods;
+
+ /*
+ * Build descriptors according to the device class specification.
+ */
+ err = usbf_add_config(dev, &sc->sc_config);
+ if (err) {
+ printf("%s: usbf_add_config failed\n", DEVNAME(sc));
+ USB_ATTACH_ERROR_RETURN;
+ }
+ err = usbf_add_interface(sc->sc_config, UICLASS_CDC,
+ UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL, 0, NULL,
+ &sc->sc_iface);
+ if (err) {
+ printf("%s: usbf_add_interface failed\n", DEVNAME(sc));
+ USB_ATTACH_ERROR_RETURN;
+ }
+ /* XXX don't use hard-coded values 128 and 16. */
+ err = usbf_add_endpoint(sc->sc_iface, UE_DIR_IN, UE_BULK,
+ 128, 16, &sc->sc_ep_in) ||
+ usbf_add_endpoint(sc->sc_iface, UE_DIR_OUT, UE_BULK,
+ 128, 16, &sc->sc_ep_out);
+ if (err) {
+ printf("%s: usbf_add_endpoint failed\n", DEVNAME(sc));
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ /* Append a CDC union descriptor. */
+ bzero(&udesc, sizeof udesc);
+ udesc.bLength = sizeof udesc;
+ udesc.bDescriptorType = UDESC_CS_INTERFACE;
+ udesc.bDescriptorSubtype = UDESCSUB_CDC_UNION;
+ udesc.bSlaveInterface[0] = usbf_interface_number(sc->sc_iface);
+ err = usbf_add_config_desc(sc->sc_config,
+ (usb_descriptor_t *)&udesc, NULL);
+ if (err) {
+ printf("%s: usbf_add_config_desc failed\n", DEVNAME(sc));
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ /*
+ * Close the configuration and build permanent descriptors.
+ */
+ err = usbf_end_config(sc->sc_config);
+ if (err) {
+ printf("%s: usbf_end_config failed\n", DEVNAME(sc));
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ /* Preallocate xfers and data buffers. */
+ sc->sc_xfer_in = usbf_alloc_xfer(dev);
+ sc->sc_xfer_out = usbf_alloc_xfer(dev);
+ sc->sc_buffer_in = usbf_alloc_buffer(sc->sc_xfer_in,
+ CDCEF_BUFSZ);
+ sc->sc_buffer_out = usbf_alloc_buffer(sc->sc_xfer_out,
+ CDCEF_BUFSZ);
+ if (sc->sc_buffer_in == NULL || sc->sc_buffer_out == NULL) {
+ printf("%s: usbf_alloc_buffer failed\n", DEVNAME(sc));
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ /* Open the bulk pipes. */
+ err = usbf_open_pipe(sc->sc_iface,
+ usbf_endpoint_address(sc->sc_ep_in), &sc->sc_pipe_in) ||
+ usbf_open_pipe(sc->sc_iface,
+ usbf_endpoint_address(sc->sc_ep_out), &sc->sc_pipe_out);
+ if (err) {
+ printf("%s: usbf_open_pipe failed\n", DEVNAME(sc));
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ /* Get ready to receive packets. */
+ usbf_setup_xfer(sc->sc_xfer_out, sc->sc_pipe_out, (void *)sc,
+ sc->sc_buffer_out, CDCEF_BUFSZ, 0, 0, cdcef_rxeof);
+ err = usbf_transfer(sc->sc_xfer_out);
+ if (err && err != USBF_IN_PROGRESS) {
+ printf("%s: usbf_transfer failed\n", DEVNAME(sc));
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ USB_ATTACH_SUCCESS_RETURN;
+}
+
+usbf_status
+cdcef_do_request(usbf_function_handle fun, usb_device_request_t *req,
+ void **data)
+{
+ return USBF_STALLED;
+}
+
+void
+cdcef_start(struct ifnet *ifp)
+{
+}
+
+void
+cdcef_txeof(usbf_xfer_handle xfer, usbf_private_handle priv,
+ usbf_status err)
+{
+ struct cdcef_softc *sc = priv;
+
+ printf("cdcef_txeof: xfer=%p, priv=%p, %s\n", xfer, priv,
+ usbf_errstr(err));
+
+ /* Setup another xfer. */
+ usbf_setup_xfer(xfer, sc->sc_pipe_in, (void *)sc,
+ sc->sc_buffer_in, CDCEF_BUFSZ, 0, 0, cdcef_rxeof);
+ err = usbf_transfer(xfer);
+ if (err && err != USBF_IN_PROGRESS) {
+ printf("%s: usbf_transfer failed\n", DEVNAME(sc));
+ USB_ATTACH_ERROR_RETURN;
+ }
+}
+
+void
+cdcef_rxeof(usbf_xfer_handle xfer, usbf_private_handle priv,
+ usbf_status err)
+{
+ struct cdcef_softc *sc = priv;
+
+ printf("cdcef_txeof: xfer=%p, priv=%p, %s\n", xfer, priv,
+ usbf_errstr(err));
+
+ /* Setup another xfer. */
+ usbf_setup_xfer(xfer, sc->sc_pipe_out, (void *)sc,
+ sc->sc_buffer_out, CDCEF_BUFSZ, 0, 0, cdcef_rxeof);
+ err = usbf_transfer(xfer);
+ if (err && err != USBF_IN_PROGRESS) {
+ printf("%s: usbf_transfer failed\n", DEVNAME(sc));
+ USB_ATTACH_ERROR_RETURN;
+ }
+}
diff --git a/sys/dev/usb/usbf.c b/sys/dev/usb/usbf.c
new file mode 100644
index 00000000000..a1715323fbc
--- /dev/null
+++ b/sys/dev/usb/usbf.c
@@ -0,0 +1,698 @@
+/* $OpenBSD: usbf.c,v 1.1 2006/11/25 18:10:29 uwe Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * USB 2.0 logical device driver
+ *
+ * Specification non-comformities:
+ *
+ * - not all Standard Device Requests are supported (see 9.4)
+ * - USB 2.0 devices (device_descriptor.bcdUSB >= 0x0200) must support
+ * the other_speed requests but we do not
+ *
+ * Missing functionality:
+ *
+ * - isochronous pipes/transfers
+ * - clever, automatic endpoint address assignment to make optimal use
+ * of available hardware endpoints
+ * - alternate settings for interfaces are unsupported
+ */
+
+/*
+ * The source code below is marked an can be split into a number of pieces
+ * (in that order):
+ *
+ * - USB logical device match/attach/detach
+ * - USB device tasks
+ * - Bus event handling
+ * - Device request handling
+ *
+ * Stylistic issues:
+ *
+ * - "endpoint number" and "endpoint address" are sometimes confused in
+ * this source code, OTOH the endpoint number is just the address, aside
+ * from the direction bit that is added to the number to form a unique
+ * endpoint address
+ */
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/kthread.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+
+#include <machine/bus.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usbf.h>
+#include <dev/usb/usbfvar.h>
+
+#define USBF_DEBUG
+#ifndef USBF_DEBUG
+#define DPRINTF(l, x) do {} while (0)
+#else
+int usbfdebug = 0;
+#define DPRINTF(l, x) if ((l) <= usbfdebug) printf x; else {}
+#endif
+
+struct usbf_softc {
+ USBBASEDEVICE sc_dev; /* base device */
+ usbf_bus_handle sc_bus; /* USB device controller */
+ struct usbf_port sc_port; /* dummy port for function */
+ usb_proc_ptr sc_proc; /* task thread */
+ TAILQ_HEAD(,usbf_task) sc_tskq; /* task queue head */
+ int sc_dying;
+};
+
+#define DEVNAME(sc) USBDEVNAME((sc)->sc_dev)
+
+int usbf_match(struct device *, void *, void *);
+void usbf_attach(struct device *, struct device *, void *);
+void usbf_create_thread(void *);
+void usbf_task_thread(void *);
+
+usbf_status usbf_get_descriptor(usbf_device_handle, usb_device_request_t *, void **);
+void usbf_set_address(usbf_device_handle, u_int8_t);
+usbf_status usbf_set_config(usbf_device_handle, u_int8_t);
+
+#ifdef USBF_DEBUG
+void usbf_dump_request(usbf_device_handle, usb_device_request_t *);
+#endif
+
+struct cfattach usbf_ca = {
+ sizeof(struct usbf_softc), usbf_match, usbf_attach
+};
+
+struct cfdriver usbf_cd = {
+ NULL, "usbf", DV_DULL
+};
+
+static const char * const usbrev_str[] = USBREV_STR;
+
+USB_MATCH(usbf)
+{
+ return UMATCH_GENERIC;
+}
+
+USB_ATTACH(usbf)
+{
+ struct usbf_softc *sc = (struct usbf_softc *)self;
+ int usbrev;
+ int speed;
+ usbf_status err;
+
+ /* Continue to set up the bus struct. */
+ sc->sc_bus = aux;
+ sc->sc_bus->usbfctl = sc;
+
+ usbrev = sc->sc_bus->usbrev;
+ printf(": USB revision %s", usbrev_str[usbrev]);
+ switch (usbrev) {
+ case USBREV_2_0:
+ speed = USB_SPEED_HIGH;
+ break;
+ case USBREV_1_1:
+ case USBREV_1_0:
+ speed = USB_SPEED_FULL;
+ break;
+ default:
+ printf(", not supported\n");
+ sc->sc_dying = 1;
+ USB_ATTACH_ERROR_RETURN;
+ }
+ printf("\n");
+
+ /* Initialize the usbf struct. */
+ TAILQ_INIT(&sc->sc_tskq);
+
+ /* Establish the software interrupt. */
+ if (usbf_softintr_establish(sc->sc_bus)) {
+ printf("%s: can't establish softintr\n", DEVNAME(sc));
+ sc->sc_dying = 1;
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ /* Attach the function driver. */
+ err = usbf_new_device(self, sc->sc_bus, 0, speed, 0, &sc->sc_port);
+ if (err) {
+ printf("%s: usbf_new_device failed, %s\n", DEVNAME(sc),
+ usbf_errstr(err));
+ sc->sc_dying = 1;
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ /* Create a process context for asynchronous tasks. */
+ config_pending_incr();
+ kthread_create_deferred(usbf_create_thread, sc);
+}
+
+/*
+ * USB device tasks
+ */
+
+/*
+ * Add a task to be performed by the task thread. This function can be
+ * called from any context and the task function will be executed in a
+ * process context ASAP.
+ */
+void
+usbf_add_task(usbf_device_handle dev, struct usbf_task *task)
+{
+ struct usbf_softc *sc = dev->bus->usbfctl;
+ int s;
+
+ s = splusb();
+ if (!task->onqueue) {
+ DPRINTF(1,("usbf_add_task: task=%p, proc=%s\n",
+ task, sc->sc_bus->intr_context ? "(null)" :
+ curproc->p_comm));
+ TAILQ_INSERT_TAIL(&sc->sc_tskq, task, next);
+ task->onqueue = 1;
+ } else {
+ DPRINTF(0,("usbf_add_task: task=%p on q, proc=%s\n",
+ task, sc->sc_bus->intr_context ? "(null)" :
+ curproc->p_comm));
+ }
+ wakeup(&sc->sc_tskq);
+ splx(s);
+}
+
+void
+usbf_rem_task(usbf_device_handle dev, struct usbf_task *task)
+{
+ struct usbf_softc *sc = dev->bus->usbfctl;
+ int s;
+
+ s = splusb();
+ if (task->onqueue) {
+ DPRINTF(1,("usbf_rem_task: task=%p\n", task));
+ TAILQ_REMOVE(&sc->sc_tskq, task, next);
+ task->onqueue = 0;
+
+ } else {
+ DPRINTF(0,("usbf_rem_task: task=%p not on q", task));
+ }
+ splx(s);
+}
+
+/*
+ * Called from the kernel proper when it can create threads.
+ */
+void
+usbf_create_thread(void *arg)
+{
+ struct usbf_softc *sc = arg;
+
+ if (kthread_create(usbf_task_thread, sc, &sc->sc_proc, "%s",
+ DEVNAME(sc)) != 0) {
+ printf("%s: can't create task thread\n", DEVNAME(sc));
+ return;
+ }
+ config_pending_decr();
+}
+
+/*
+ * Process context for USB function tasks.
+ */
+void
+usbf_task_thread(void *arg)
+{
+ struct usbf_softc *sc = arg;
+ struct usbf_task *task;
+ int s;
+
+ DPRINTF(0,("usbf_task_thread: start (pid %d)\n", curproc->p_pid));
+
+ s = splusb();
+ while (!sc->sc_dying) {
+ task = TAILQ_FIRST(&sc->sc_tskq);
+ if (task == NULL) {
+ tsleep(&sc->sc_tskq, PWAIT, "usbtsk", 0);
+ task = TAILQ_FIRST(&sc->sc_tskq);
+ }
+ DPRINTF(1,("usbf_task_thread: woke up task=%p\n", task));
+ if (task != NULL) {
+ TAILQ_REMOVE(&sc->sc_tskq, task, next);
+ task->onqueue = 0;
+ splx(s);
+ task->fun(task->arg);
+ s = splusb();
+ DPRINTF(1,("usbf_task_thread: done task=%p\n",
+ task));
+ }
+ }
+ splx(s);
+
+ DPRINTF(0,("usbf_task_thread: exit\n"));
+ kthread_exit(0);
+}
+
+/*
+ * Bus event handling
+ */
+
+void
+usbf_host_reset(usbf_bus_handle bus)
+{
+ usbf_device_handle dev = bus->usbfctl->sc_port.device;
+
+ DPRINTF(0,("usbf_host_reset\n"));
+
+ /* Change device state from any state backe to Default. */
+ (void)usbf_set_config(dev, USB_UNCONFIG_NO);
+ dev->address = 0;
+}
+
+/*
+ * Device request handling
+ */
+
+/* XXX */
+static u_int8_t hs_config[65536];
+
+usbf_status
+usbf_get_descriptor(usbf_device_handle dev, usb_device_request_t *req,
+ void **data)
+{
+ u_int8_t type = UGETW(req->wValue) >> 8;
+ u_int8_t index = UGETW(req->wValue) & 0xff;
+ usb_device_descriptor_t *dd;
+ usb_config_descriptor_t *cd;
+ usb_string_descriptor_t *sd;
+
+ switch (type) {
+ case UDESC_DEVICE:
+ dd = usbf_device_descriptor(dev);
+ *data = dd;
+ USETW(req->wLength, MIN(UGETW(req->wLength), dd->bLength));;
+ return USBF_NORMAL_COMPLETION;
+
+ case UDESC_DEVICE_QUALIFIER: {
+ static usb_device_qualifier_t dq;
+
+ dd = usbf_device_descriptor(dev);
+ bzero(&dq, sizeof dq);
+ dq.bLength = USB_DEVICE_QUALIFIER_SIZE;
+ dq.bDescriptorType = UDESC_DEVICE_QUALIFIER;
+ USETW(dq.bcdUSB, 0x0200);
+ dq.bDeviceClass = dd->bDeviceClass;
+ dq.bDeviceSubClass = dd->bDeviceSubClass;
+ dq.bDeviceProtocol = dd->bDeviceProtocol;
+ dq.bMaxPacketSize0 = dd->bMaxPacketSize;
+ dq.bNumConfigurations = dd->bNumConfigurations;
+ *data = &dq;
+ USETW(req->wLength, MIN(UGETW(req->wLength), dq.bLength));;
+ return USBF_NORMAL_COMPLETION;
+ }
+
+ case UDESC_CONFIG:
+ cd = usbf_config_descriptor(dev, index);
+ if (cd == NULL)
+ return USBF_INVAL;
+ *data = cd;
+ USETW(req->wLength, MIN(UGETW(req->wLength),
+ UGETW(cd->wTotalLength)));
+ return USBF_NORMAL_COMPLETION;
+
+ /* XXX */
+ case UDESC_OTHER_SPEED_CONFIGURATION:
+ cd = usbf_config_descriptor(dev, index);
+ if (cd == NULL)
+ return USBF_INVAL;
+ bcopy(cd, &hs_config, UGETW(cd->wTotalLength));
+ *data = &hs_config;
+ ((usb_config_descriptor_t *)&hs_config)->bDescriptorType =
+ UDESC_OTHER_SPEED_CONFIGURATION;
+ USETW(req->wLength, MIN(UGETW(req->wLength),
+ UGETW(cd->wTotalLength)));
+ return USBF_NORMAL_COMPLETION;
+
+ case UDESC_STRING:
+ sd = usbf_string_descriptor(dev, index);
+ if (sd == NULL)
+ return USBF_INVAL;
+ *data = sd;
+ USETW(req->wLength, MIN(UGETW(req->wLength), sd->bLength));
+ return USBF_NORMAL_COMPLETION;
+
+ default:
+ DPRINTF(0,("usbf_get_descriptor: unknown descriptor type=%u\n",
+ type));
+ return USBF_INVAL;
+ }
+}
+
+/*
+ * Change device state from Default to Address, or change the device address
+ * if the device is not currently in the Default state.
+ */
+void
+usbf_set_address(usbf_device_handle dev, u_int8_t address)
+{
+ DPRINTF(0,("usbf_set_address: dev=%p, %u -> %u\n", dev,
+ dev->address, address));
+ dev->address = address;
+}
+
+/*
+ * If the device was in the Addressed state (dev->config == NULL) before, it
+ * will be in the Configured state upon successful return from this routine.
+ */
+usbf_status
+usbf_set_config(usbf_device_handle dev, u_int8_t new)
+{
+ usbf_config_handle cfg = dev->config;
+ usbf_function_handle fun = dev->function;
+ usbf_status err = USBF_NORMAL_COMPLETION;
+ u_int8_t old = cfg ? cfg->uc_cdesc->bConfigurationValue :
+ USB_UNCONFIG_NO;
+
+ if (old == new)
+ return USBF_NORMAL_COMPLETION;
+
+ DPRINTF(0,("usbf_set_config: dev=%p, %u -> %u\n", dev, old, new));
+
+ /*
+ * Resetting the device state to Unconfigured must always succeed.
+ * This happens typically when the host resets the bus.
+ */
+ if (new == USB_UNCONFIG_NO) {
+ if (dev->function->methods->set_config)
+ err = fun->methods->set_config(fun, NULL);
+ if (err) {
+ DPRINTF(0,("usbf_set_config: %s\n",
+ usbf_errstr(err)));
+ }
+ dev->config = NULL;
+ return USBF_NORMAL_COMPLETION;
+ }
+
+ /*
+ * Changing the device configuration may fail. The function
+ * may decline to set the new configuration.
+ */
+ SIMPLEQ_FOREACH(cfg, &dev->configs, next) {
+ if (cfg->uc_cdesc->bConfigurationValue == new) {
+ if (dev->function->methods->set_config)
+ err = fun->methods->set_config(fun, cfg);
+ if (!err)
+ dev->config = cfg;
+ return err;
+ }
+ }
+ return USBF_INVAL;
+}
+
+/*
+ * Handle device requests coming in via endpoint 0 pipe.
+ */
+void
+usbf_do_request(usbf_xfer_handle xfer, usbf_private_handle priv,
+ usbf_status err)
+{
+ usbf_device_handle dev = xfer->pipe->device;
+ usb_device_request_t *req = xfer->buffer;
+ usbf_config_handle cfg;
+ void *data = NULL;
+ u_int16_t value;
+ u_int16_t index;
+
+ if (err) {
+ DPRINTF(0,("usbf_do_request: receive failed, %s\n",
+ usbf_errstr(err)));
+ return;
+ }
+
+#ifdef USBF_DEBUG
+ if (usbfdebug >= 0)
+ usbf_dump_request(dev, req);
+#endif
+
+#define C(x,y) ((x) | ((y) << 8))
+ switch (C(req->bRequest, req->bmRequestType)) {
+
+ case C(UR_SET_ADDRESS, UT_WRITE_DEVICE):
+ /* Change device state from Default to Address. */
+ usbf_set_address(dev, UGETW(req->wValue));
+ break;
+
+ case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
+ /* Change device state from Address to Configured. */
+ err = usbf_set_config(dev, UGETW(req->wValue) & 0xff);
+ break;
+
+ case C(UR_GET_CONFIG, UT_READ_DEVICE):
+ { /* XXX */
+ if ((cfg = dev->config) == NULL) {
+ static u_int8_t zero = 0;
+ data = &zero;
+ } else
+ data = &cfg->uc_cdesc->bConfigurationValue;
+ USETW(req->wLength, MIN(UGETW(req->wLength), 1));;
+ }
+ break;
+
+ case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
+ err = usbf_get_descriptor(dev, req, &data);
+ break;
+
+ case C(UR_GET_STATUS, UT_READ_DEVICE):
+ DPRINTF(1,("usbf_do_request: UR_GET_STATUS %d\n",
+ UGETW(req->wLength)));
+ data = &dev->status;
+ USETW(req->wLength, MIN(UGETW(req->wLength),
+ sizeof dev->status));
+ break;
+
+ case C(UR_GET_STATUS, UT_READ_ENDPOINT): {
+ //u_int8_t addr = UGETW(req->wIndex) & 0xff;
+ static u_int16_t status = 0;
+
+ data = &status;
+ USETW(req->wLength, MIN(UGETW(req->wLength), sizeof status));
+ break;
+ }
+
+ case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT):
+ value = UGETW(req->wValue);
+ index = UGETW(req->wIndex);
+ if ((cfg = dev->config) == NULL)
+ err = USBF_STALLED;
+ else
+ err = usbf_set_endpoint_feature(cfg, index, value);
+ break;
+
+ case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
+ value = UGETW(req->wValue);
+ index = UGETW(req->wIndex);
+ if ((cfg = dev->config) == NULL)
+ err = USBF_STALLED;
+ else
+ err = usbf_clear_endpoint_feature(cfg, index, value);
+ break;
+
+ /* Alternate settings for interfaces are unsupported. */
+ case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
+ if (UGETW(req->wValue) != 0)
+ err = USBF_STALLED;
+ break;
+ case C(UR_GET_INTERFACE, UT_READ_INTERFACE): {
+ static u_int8_t zero = 0;
+ data = &zero;
+ USETW(req->wLength, MIN(UGETW(req->wLength), 1));
+ break;
+ }
+
+ default: {
+ usbf_function_handle fun = dev->function;
+
+ if (fun == NULL)
+ err = USBF_STALLED;
+ else
+ /* XXX change prototype for this method to remove
+ * XXX the data argument. */
+ err = fun->methods->do_request(fun, req, &data);
+ }
+ }
+
+ if (err) {
+ DPRINTF(0,("usbf_do_request: request=%#x, type=%#x "
+ "failed, %s\n", req->bRequest, req->bmRequestType,
+ usbf_errstr(err)));
+ usbf_stall_pipe(dev->default_pipe);
+ } else if (UGETW(req->wLength) > 0) {
+ if (data == NULL) {
+ DPRINTF(0,("usbf_do_request: no data, "
+ "sending ZLP\n"));
+ USETW(req->wLength, 0);
+ }
+ /* Transfer IN data in response to the request. */
+ usbf_setup_xfer(dev->data_xfer, dev->default_pipe,
+ NULL, data, UGETW(req->wLength), 0, 0, NULL);
+ err = usbf_transfer(dev->data_xfer);
+ if (err && err != USBF_IN_PROGRESS) {
+ DPRINTF(0,("usbf_do_request: data xfer=%p, %s\n",
+ xfer, usbf_errstr(err)));
+ }
+ }
+
+ /* Schedule another request transfer. */
+ usbf_setup_default_xfer(dev->default_xfer, dev->default_pipe,
+ NULL, &dev->def_req, 0, 0, usbf_do_request);
+ err = usbf_transfer(dev->default_xfer);
+ if (err && err != USBF_IN_PROGRESS) {
+ DPRINTF(0,("usbf_do_request: ctrl xfer=%p, %s\n", xfer,
+ usbf_errstr(err)));
+ }
+}
+
+#ifdef USBF_DEBUG
+struct usb_enum_str {
+ int code;
+ const char * const str;
+};
+
+static const struct usb_enum_str usb_request_str[] = {
+ { UR_GET_STATUS, "GET STATUS" },
+ { UR_CLEAR_FEATURE, "CLEAR FEATURE" },
+ { UR_SET_FEATURE, "SET FEATURE" },
+ { UR_SET_ADDRESS, "SET ADDRESS" },
+ { UR_GET_DESCRIPTOR, "GET DESCRIPTOR" },
+ { UR_SET_DESCRIPTOR, "SET DESCRIPTOR" },
+ { UR_GET_CONFIG, "GET CONFIG" },
+ { UR_SET_CONFIG, "SET CONFIG" },
+ { UR_GET_INTERFACE, "GET INTERFACE" },
+ { UR_SET_INTERFACE, "SET INTERFACE" },
+ { UR_SYNCH_FRAME, "SYNCH FRAME" },
+ { 0, NULL }
+};
+
+static const struct usb_enum_str usb_request_type_str[] = {
+ { UT_READ_DEVICE, "Read Device" },
+ { UT_READ_INTERFACE, "Read Interface" },
+ { UT_READ_ENDPOINT, "Read Endpoint" },
+ { UT_WRITE_DEVICE, "Write Device" },
+ { UT_WRITE_INTERFACE, "Write Interface" },
+ { UT_WRITE_ENDPOINT, "Write Endpoint" },
+ { UT_READ_CLASS_DEVICE, "Read Class Device" },
+ { UT_READ_CLASS_INTERFACE, "Read Class Interface" },
+ { UT_READ_CLASS_OTHER, "Read Class Other" },
+ { UT_READ_CLASS_ENDPOINT, "Read Class Endpoint" },
+ { UT_WRITE_CLASS_DEVICE, "Write Class Device" },
+ { UT_WRITE_CLASS_INTERFACE, "Write Class Interface" },
+ { UT_WRITE_CLASS_OTHER, "Write Class Other" },
+ { UT_WRITE_CLASS_ENDPOINT, "Write Class Endpoint" },
+ { UT_READ_VENDOR_DEVICE, "Read Vendor Device" },
+ { UT_READ_VENDOR_INTERFACE, "Read Vendor Interface" },
+ { UT_READ_VENDOR_OTHER, "Read Vendor Other" },
+ { UT_READ_VENDOR_ENDPOINT, "Read Vendor Endpoint" },
+ { UT_WRITE_VENDOR_DEVICE, "Write Vendor Device" },
+ { UT_WRITE_VENDOR_INTERFACE, "Write Vendor Interface" },
+ { UT_WRITE_VENDOR_OTHER, "Write Vendor Other" },
+ { UT_WRITE_VENDOR_ENDPOINT, "Write Vendor Endpoint" },
+ { 0, NULL }
+};
+
+static const struct usb_enum_str usb_request_desc_str[] = {
+ { UDESC_DEVICE, "Device" },
+ { UDESC_CONFIG, "Configuration" },
+ { UDESC_STRING, "String" },
+ { UDESC_INTERFACE, "Interface" },
+ { UDESC_ENDPOINT, "Endpoint" },
+ { UDESC_DEVICE_QUALIFIER, "Device Qualifier" },
+ { UDESC_OTHER_SPEED_CONFIGURATION, "Other Speed Configuration" },
+ { UDESC_INTERFACE_POWER, "Interface Power" },
+ { UDESC_OTG, "OTG" },
+ { UDESC_CS_DEVICE, "Class-specific Device" },
+ { UDESC_CS_CONFIG, "Class-specific Configuration" },
+ { UDESC_CS_STRING, "Class-specific String" },
+ { UDESC_CS_INTERFACE, "Class-specific Interface" },
+ { UDESC_CS_ENDPOINT, "Class-specific Endpoint" },
+ { UDESC_HUB, "Hub" },
+ { 0, NULL }
+};
+
+static const char *
+usb_enum_string(const struct usb_enum_str *tab, int code)
+{
+ static char buf[16];
+
+ while (tab->str != NULL) {
+ if (tab->code == code)
+ return tab->str;
+ tab++;
+ }
+
+ (void)snprintf(buf, sizeof buf, "0x%02x", code);
+ return buf;
+}
+
+static const char *
+usbf_request_code_string(usb_device_request_t *req)
+{
+ static char buf[32];
+
+ (void)snprintf(buf, sizeof buf, "%s",
+ usb_enum_string(usb_request_str, req->bRequest));
+ return buf;
+}
+
+static const char *
+usbf_request_type_string(usb_device_request_t *req)
+{
+ static char buf[32];
+
+ (void)snprintf(buf, sizeof buf, "%s",
+ usb_enum_string(usb_request_type_str, req->bmRequestType));
+ return buf;
+}
+
+static const char *
+usbf_request_desc_string(usb_device_request_t *req)
+{
+ static char buf[32];
+ u_int8_t type = UGETW(req->wValue) >> 8;
+ u_int8_t index = UGETW(req->wValue) & 0xff;
+
+ (void)snprintf(buf, sizeof buf, "%s/%u",
+ usb_enum_string(usb_request_desc_str, type), index);
+ return buf;
+}
+
+void
+usbf_dump_request(usbf_device_handle dev, usb_device_request_t *req)
+{
+ struct usbf_softc *sc = dev->bus->usbfctl;
+
+ printf("%s: %s request %s\n",
+ DEVNAME(sc), usbf_request_type_string(req),
+ usbf_request_code_string(req));
+
+ if (req->bRequest == UR_GET_DESCRIPTOR)
+ printf("%s: VALUE: 0x%04x (%s)\n", DEVNAME(sc),
+ UGETW(req->wValue), usbf_request_desc_string(req));
+ else
+ printf("%s: VALUE: 0x%04x\n", DEVNAME(sc),
+ UGETW(req->wValue));
+
+ printf("%s: INDEX: 0x%04x\n", DEVNAME(sc), UGETW(req->wIndex));
+ printf("%s: LENGTH: 0x%04x\n", DEVNAME(sc), UGETW(req->wLength));
+}
+#endif
diff --git a/sys/dev/usb/usbf_subr.c b/sys/dev/usb/usbf_subr.c
new file mode 100644
index 00000000000..afb9512a66b
--- /dev/null
+++ b/sys/dev/usb/usbf_subr.c
@@ -0,0 +1,1099 @@
+/* $OpenBSD: usbf_subr.c,v 1.1 2006/11/25 18:10:29 uwe Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * USB function driver interface subroutines
+ */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+
+#include <machine/bus.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usb_mem.h>
+#include <dev/usb/usbf.h>
+#include <dev/usb/usbfvar.h>
+
+#define USBF_DEBUG
+#ifndef USBF_DEBUG
+#define DPRINTF(l, x) do {} while (0)
+#else
+extern int usbfdebug;
+#define DPRINTF(l, x) if ((l) <= usbfdebug) printf x; else {}
+#endif
+
+void *usbf_realloc(void **, size_t *, size_t);
+size_t usbf_get_string(usbf_device_handle, u_int8_t, char *, size_t);
+usbf_status usbf_open_pipe_ival(usbf_interface_handle, u_int8_t,
+ usbf_pipe_handle *, int);
+usbf_status usbf_setup_pipe(usbf_device_handle, usbf_interface_handle,
+ struct usbf_endpoint *, int,
+ usbf_pipe_handle *);
+void usbf_start_next(usbf_pipe_handle);
+void usbf_set_endpoint_halt(usbf_endpoint_handle);
+void usbf_clear_endpoint_halt(usbf_endpoint_handle);
+
+static const char * const usbf_error_strs[] = USBF_ERROR_STRS;
+
+const char *
+usbf_errstr(usbf_status err)
+{
+ static char buffer[5];
+
+ if (err < USBD_ERROR_MAX)
+ return usbf_error_strs[err];
+
+ snprintf(buffer, sizeof buffer, "%d", err);
+ return buffer;
+}
+
+void *
+usbf_realloc(void **pp, size_t *sizep, size_t newsize)
+{
+ void *p;
+ size_t oldsize;
+
+ if (newsize == 0) {
+ if (*sizep > 0)
+ free(*pp, M_USB);
+ *pp = NULL;
+ *sizep = 0;
+ return NULL;
+ }
+
+ p = malloc(newsize, M_USB, M_NOWAIT);
+ if (p == NULL)
+ return NULL;
+
+ oldsize = MIN(*sizep, newsize);
+ if (oldsize > 0)
+ bcopy(*pp, p, oldsize);
+ *pp = p;
+ *sizep = newsize;
+ return p;
+}
+
+/*
+ * Attach a function driver.
+ */
+static usbf_status
+usbf_probe_and_attach(device_ptr_t parent, usbf_device_handle dev, int port)
+{
+ struct usbf_attach_arg uaa;
+ device_ptr_t dv;
+
+ KASSERT(dev->function == NULL);
+
+ bzero(&uaa, sizeof uaa);
+ uaa.device = dev;
+
+ /*
+ * The softc structure of a USB function driver must begin with a
+ * "struct usbf_function" member (instead of USBBASEDEV), which must
+ * be initialized in the function driver's attach routine. Also, it
+ * should use usbf_devinfo_setup() to set the device identification.
+ */
+ dv = USB_DO_ATTACH(dev, NULL, parent, &uaa, NULL, NULL);
+ if (dv != NULL) {
+ dev->function = (struct usbf_function *)dv;
+ return USBF_NORMAL_COMPLETION;
+ }
+
+ /*
+ * We failed to attach a function driver for this device, but the
+ * device can still function as a generic USB device without any
+ * interfaces.
+ */
+ return USBF_NORMAL_COMPLETION;
+}
+
+static void
+usbf_remove_device(usbf_device_handle dev, struct usbf_port *up)
+{
+ KASSERT(dev != NULL && dev == up->device);
+
+ if (dev->function != NULL)
+ config_detach((device_ptr_t)dev->function, DETACH_FORCE);
+ if (dev->default_pipe != NULL)
+ usbf_close_pipe(dev->default_pipe);
+ up->device = NULL;
+ free(dev, M_USB);
+}
+
+usbf_status
+usbf_new_device(device_ptr_t parent, usbf_bus_handle bus, int depth,
+ int speed, int port, struct usbf_port *up)
+{
+ struct usbf_device *dev;
+ usb_device_descriptor_t *ud;
+ usbf_status err;
+
+#ifdef DIAGNOSTIC
+ KASSERT(up->device == NULL);
+#endif
+
+ dev = malloc(sizeof(*dev), M_USB, M_NOWAIT);
+ if (dev == NULL)
+ return USBF_NOMEM;
+
+ bzero(dev, sizeof *dev);
+ dev->bus = bus;
+ dev->string_id = USBF_STRING_ID_MIN;
+ SIMPLEQ_INIT(&dev->configs);
+
+ /* Initialize device status. */
+ USETW(dev->status.wStatus, UDS_SELF_POWERED);
+
+ /*
+ * Initialize device descriptor. The function driver for this
+ * device (attached below) must complete the device descriptor.
+ */
+ ud = &dev->ddesc;
+ ud->bLength = USB_DEVICE_DESCRIPTOR_SIZE;
+ ud->bDescriptorType = UDESC_DEVICE;
+ ud->bMaxPacketSize = bus->ep0_maxp;
+ if (bus->usbrev >= USBREV_2_0)
+ USETW(ud->bcdUSB, UD_USB_2_0);
+ else
+ USETW(ud->bcdUSB, 0x0101);
+
+ /* Set up the default endpoint handle and descriptor. */
+ dev->def_ep.edesc = &dev->def_ep_desc;
+ dev->def_ep_desc.bLength = USB_ENDPOINT_DESCRIPTOR_SIZE;
+ dev->def_ep_desc.bDescriptorType = UDESC_ENDPOINT;
+ dev->def_ep_desc.bEndpointAddress = USB_CONTROL_ENDPOINT;
+ dev->def_ep_desc.bmAttributes = UE_CONTROL;
+ USETW(dev->def_ep_desc.wMaxPacketSize, ud->bMaxPacketSize);
+ dev->def_ep_desc.bInterval = 0;
+
+ /* Establish the default pipe. */
+ err = usbf_setup_pipe(dev, NULL, &dev->def_ep, 0,
+ &dev->default_pipe);
+ if (err) {
+ free(dev, M_USB);
+ return err;
+ }
+
+ /* Preallocate xfers for default pipe. */
+ dev->default_xfer = usbf_alloc_xfer(dev);
+ dev->data_xfer = usbf_alloc_xfer(dev);
+ if (dev->default_xfer == NULL || dev->data_xfer == NULL) {
+ if (dev->default_xfer != NULL)
+ usbf_free_xfer(dev->default_xfer);
+ usbf_close_pipe(dev->default_pipe);
+ free(dev, M_USB);
+ return USBF_NOMEM;
+ }
+
+ /* Insert device request xfer. */
+ usbf_setup_default_xfer(dev->default_xfer, dev->default_pipe,
+ NULL, &dev->def_req, 0, 0, usbf_do_request);
+ err = usbf_transfer(dev->default_xfer);
+ if (err && err != USBF_IN_PROGRESS) {
+ usbf_free_xfer(dev->default_xfer);
+ usbf_free_xfer(dev->data_xfer);
+ usbf_close_pipe(dev->default_pipe);
+ free(dev, M_USB);
+ return err;
+ }
+
+ /* Associate the upstream port with the device. */
+ bzero(up, sizeof *up);
+ up->portno = port;
+ up->device = dev;
+
+ /* Attach function driver. */
+ err = usbf_probe_and_attach(parent, dev, port);
+ if (err)
+ usbf_remove_device(dev, up);
+ return err;
+}
+
+/*
+ * Should be called by the function driver in its attach routine to change
+ * the default device identification according to the particular function.
+ */
+void
+usbf_devinfo_setup(usbf_device_handle dev, u_int8_t devclass,
+ u_int8_t subclass, u_int8_t proto, u_int16_t vendor, u_int16_t product,
+ u_int16_t device, const char *manf, const char *prod, const char *ser)
+{
+ usb_device_descriptor_t *dd;
+
+ dd = usbf_device_descriptor(dev);
+ dd->bDeviceClass = devclass;
+ dd->bDeviceSubClass = subclass;
+ dd->bDeviceProtocol = proto;
+ if (vendor != 0)
+ USETW(dd->idVendor, vendor);
+ if (product != 0)
+ USETW(dd->idProduct, product);
+ if (device != 0)
+ USETW(dd->bcdDevice, device);
+ if (manf != NULL)
+ dd->iManufacturer = usbf_add_string(dev, manf);
+ if (prod != NULL)
+ dd->iProduct = usbf_add_string(dev, prod);
+ if (ser != NULL)
+ dd->iSerialNumber = usbf_add_string(dev, ser);
+}
+
+char *
+usbf_devinfo_alloc(usbf_device_handle dev)
+{
+ char manf[40];
+ char prod[40];
+ usb_device_descriptor_t *dd;
+ size_t len;
+ char *devinfo;
+
+ dd = usbf_device_descriptor(dev);
+ usbf_get_string(dev, dd->iManufacturer, manf, sizeof manf);
+ usbf_get_string(dev, dd->iProduct, prod, sizeof prod);
+
+ len = strlen(manf) + strlen(prod) + 32;
+ devinfo = malloc(len, M_USB, M_NOWAIT);
+ if (devinfo == NULL)
+ return NULL;
+
+ snprintf(devinfo, len, "%s %s, rev %d.%02d/%d.%02d", manf, prod,
+ (UGETW(dd->bcdUSB)>>8) & 0xff, UGETW(dd->bcdUSB) & 0xff,
+ (UGETW(dd->bcdDevice)>>8) & 0xff, UGETW(dd->bcdDevice) & 0xff);
+ return devinfo;
+}
+
+void
+usbf_devinfo_free(char *devinfo)
+{
+ if (devinfo != NULL)
+ free(devinfo, M_USB);
+}
+
+/*
+ * Add a string descriptor to a logical device and return the string's id.
+ *
+ * If there is not enough memory available for the new string descriptor, or
+ * if there is no unused string id left, return the id of the empty string
+ * instead of failing.
+ */
+u_int8_t
+usbf_add_string(usbf_device_handle dev, const char *string)
+{
+ usb_string_descriptor_t *sd;
+ size_t oldsize;
+ size_t newsize;
+ size_t len, i;
+ u_int8_t id;
+
+ if (string == NULL || *string == '\0' ||
+ dev->string_id == USBF_STRING_ID_MAX)
+ return USBF_EMPTY_STRING_ID;
+
+ if ((len = strlen(string)) > USB_MAX_STRING_LEN)
+ len = USB_MAX_STRING_LEN;
+
+ oldsize = dev->sdesc_size;
+ newsize = oldsize + 2 + 2 * len;
+
+ sd = usbf_realloc((void **)&dev->sdesc, &dev->sdesc_size,
+ newsize);
+ if (sd == NULL)
+ return USBF_EMPTY_STRING_ID;
+
+ sd = (usb_string_descriptor_t *)((char *)sd + oldsize);
+ sd->bLength = newsize - oldsize;
+ sd->bDescriptorType = UDESC_STRING;
+ for (i = 0; string[i] != '\0'; i++)
+ USETW(sd->bString[i], string[i]);
+
+ id = dev->string_id++;
+ return id;
+}
+
+usb_string_descriptor_t *
+usbf_string_descriptor(usbf_device_handle dev, u_int8_t id)
+{
+ static usb_string_descriptor_t sd0;
+ static usb_string_descriptor_t sd1;
+ usb_string_descriptor_t *sd;
+
+ /* handle the special string ids */
+ switch (id) {
+ case USB_LANGUAGE_TABLE:
+ sd0.bLength = 4;
+ sd0.bDescriptorType = UDESC_STRING;
+ USETW(sd0.bString[0], 0x0409 /* en_US */);
+ return &sd0;
+
+ case USBF_EMPTY_STRING_ID:
+ sd1.bLength = 2;
+ sd1.bDescriptorType = UDESC_STRING;
+ return &sd0;
+ }
+
+ /* check if the string id is valid */
+ if (id > dev->string_id)
+ return NULL;
+
+ /* seek and return the descriptor of a non-empty string */
+ id -= USBF_STRING_ID_MIN;
+ sd = dev->sdesc;
+ while (id-- > 0)
+ sd = (usb_string_descriptor_t *)((char *)sd + sd->bLength);
+ return sd;
+}
+
+size_t
+usbf_get_string(usbf_device_handle dev, u_int8_t id, char *s, size_t size)
+{
+ usb_string_descriptor_t *sd = NULL;
+ size_t i, len;
+
+ if (id != USB_LANGUAGE_TABLE)
+ sd = usbf_string_descriptor(dev, id);
+
+ if (sd == NULL) {
+ if (size > 0)
+ *s = '\0';
+ return 0;
+ }
+
+ len = (sd->bLength - 2) / 2;
+ if (size < 1)
+ return len;
+
+ for (i = 0; i < (size - 1) && i < len; i++)
+ s[i] = UGETW(sd->bString[i]) & 0xff;
+ s[i] = '\0';
+ return len;
+}
+
+/*
+ * Add a new device configuration to an existing USB logical device.
+ * The new configuration initially has zero interfaces.
+ */
+usbf_status
+usbf_add_config(usbf_device_handle dev, usbf_config_handle *ucp)
+{
+ struct usbf_config *uc;
+ usb_config_descriptor_t *cd;
+
+ uc = malloc(sizeof *uc, M_USB, M_NOWAIT);
+ if (uc == NULL)
+ return USBF_NOMEM;
+
+ cd = malloc(sizeof *cd, M_USB, M_NOWAIT);
+ if (cd == NULL) {
+ free(uc, M_USB);
+ return USBF_NOMEM;
+ }
+
+ bzero(uc, sizeof *uc);
+ uc->uc_device = dev;
+ uc->uc_cdesc = cd;
+ uc->uc_cdesc_size = sizeof *cd;
+ SIMPLEQ_INIT(&uc->iface_head);
+
+ bzero(cd, sizeof *cd);
+ cd->bLength = USB_CONFIG_DESCRIPTOR_SIZE;
+ cd->bDescriptorType = UDESC_CONFIG;
+ USETW(cd->wTotalLength, USB_CONFIG_DESCRIPTOR_SIZE);
+ cd->bConfigurationValue = USB_UNCONFIG_NO + 1 +
+ dev->ddesc.bNumConfigurations;
+ cd->iConfiguration = 0;
+ cd->bmAttributes = UC_BUS_POWERED | UC_SELF_POWERED;
+#if 0
+ cd->bMaxPower = 100 / UC_POWER_FACTOR; /* 100 mA */
+#else
+ cd->bMaxPower = 0; /* XXX 0 mA */
+#endif
+
+ SIMPLEQ_INSERT_TAIL(&dev->configs, uc, next);
+ dev->ddesc.bNumConfigurations++;
+
+ if (ucp != NULL)
+ *ucp = uc;
+ return USBF_NORMAL_COMPLETION;
+}
+
+/*
+ * Allocate memory for a new descriptor at the end of the existing
+ * device configuration descriptor.
+ */
+usbf_status
+usbf_add_config_desc(usbf_config_handle uc, usb_descriptor_t *d,
+ usb_descriptor_t **dp)
+{
+ usb_config_descriptor_t *cd;
+ size_t oldsize;
+ size_t newsize;
+
+ oldsize = uc->uc_cdesc_size;
+ newsize = oldsize + d->bLength;
+ if (d->bLength < sizeof(usb_descriptor_t) || newsize > 65535)
+ return USBF_INVAL;
+
+ cd = usbf_realloc((void **)&uc->uc_cdesc, &uc->uc_cdesc_size,
+ newsize);
+ if (cd == NULL)
+ return USBF_NOMEM;
+
+ bcopy(d, (char *)cd + oldsize, d->bLength);
+ USETW(cd->wTotalLength, newsize);
+ if (dp != NULL)
+ *dp = (usb_descriptor_t *)((char *)cd + oldsize);
+ return USBF_NORMAL_COMPLETION;
+}
+
+usbf_status
+usbf_add_interface(usbf_config_handle uc, u_int8_t bInterfaceClass,
+ u_int8_t bInterfaceSubClass, u_int8_t bInterfaceProtocol,
+ const char *string, usbf_interface_handle *uip)
+{
+ struct usbf_interface *ui;
+ usb_interface_descriptor_t *id;
+
+ if (uc->uc_closed)
+ return USBF_INVAL;
+
+ ui = malloc(sizeof *ui, M_USB, M_NOWAIT);
+ if (ui == NULL)
+ return USBF_NOMEM;
+
+ id = malloc(sizeof *id, M_USB, M_NOWAIT);
+ if (id == NULL) {
+ free(ui, M_USB);
+ return USBF_NOMEM;
+ }
+
+ bzero(ui, sizeof *ui);
+ ui->config = uc;
+ ui->idesc = id;
+ LIST_INIT(&ui->pipes);
+ SIMPLEQ_INIT(&ui->endpoint_head);
+
+ bzero(id, sizeof *id);
+ id->bLength = USB_INTERFACE_DESCRIPTOR_SIZE;
+ id->bDescriptorType = UDESC_INTERFACE;
+ id->bInterfaceNumber = uc->uc_cdesc->bNumInterface;
+ id->bInterfaceClass = bInterfaceClass;
+ id->bInterfaceSubClass = bInterfaceSubClass;
+ id->bInterfaceProtocol = bInterfaceProtocol;
+ id->iInterface = 0; /*usbf_add_string(uc->uc_device, string);*/ /* XXX */
+
+ SIMPLEQ_INSERT_TAIL(&uc->iface_head, ui, next);
+ uc->uc_cdesc->bNumInterface++;
+
+ *uip = ui;
+ return USBF_NORMAL_COMPLETION;
+}
+
+usbf_status
+usbf_add_endpoint(usbf_interface_handle ui, u_int8_t bEndpointAddress,
+ u_int8_t bmAttributes, u_int16_t wMaxPacketSize, u_int8_t bInterval,
+ usbf_endpoint_handle *uep)
+{
+ struct usbf_endpoint *ue;
+ usb_endpoint_descriptor_t *ed;
+
+ if (ui->config->uc_closed)
+ return USBF_INVAL;
+
+ ue = malloc(sizeof *ue, M_USB, M_NOWAIT);
+ if (ue == NULL)
+ return USBF_NOMEM;
+
+ ed = malloc(sizeof *ed, M_USB, M_NOWAIT);
+ if (ed == NULL) {
+ free(ue, M_USB);
+ return USBF_NOMEM;
+ }
+
+ bzero(ue, sizeof *ue);
+ ue->iface = ui;
+ ue->edesc = ed;
+
+ bzero(ed, sizeof *ed);
+ ed->bLength = USB_ENDPOINT_DESCRIPTOR_SIZE;
+ ed->bDescriptorType = UDESC_ENDPOINT;
+ ed->bEndpointAddress = bEndpointAddress;
+ ed->bmAttributes = bmAttributes;
+ USETW(ed->wMaxPacketSize, wMaxPacketSize);
+ ed->bInterval = bInterval;
+
+ SIMPLEQ_INSERT_TAIL(&ui->endpoint_head, ue, next);
+ ui->idesc->bNumEndpoints++;
+
+ *uep = ue;
+ return USBF_NORMAL_COMPLETION;
+}
+
+/*
+ * Close the configuration, thereby combining all descriptors and creating
+ * the real USB configuration descriptor that can be sent to the USB host.
+ */
+usbf_status
+usbf_end_config(usbf_config_handle uc)
+{
+ struct usbf_interface *ui;
+ struct usbf_endpoint *ue;
+ usb_descriptor_t *d;
+ usbf_status err = USBF_NORMAL_COMPLETION;
+
+ if (uc->uc_closed)
+ return USBF_INVAL;
+
+ SIMPLEQ_FOREACH(ui, &uc->iface_head, next) {
+ err = usbf_add_config_desc(uc,
+ (usb_descriptor_t *)ui->idesc, &d);
+ if (err)
+ break;
+
+ free(ui->idesc, M_USB);
+ ui->idesc = (usb_interface_descriptor_t *)d;
+
+ SIMPLEQ_FOREACH(ue, &ui->endpoint_head, next) {
+ ue->edesc->bEndpointAddress |= 1; /* XXX humbug */
+ err = usbf_add_config_desc(uc,
+ (usb_descriptor_t *)ue->edesc, &d);
+ if (err)
+ break;
+
+ free(ue->edesc, M_USB);
+ ue->edesc = (usb_endpoint_descriptor_t *)d;
+ }
+ }
+
+ uc->uc_closed = 1;
+ return err;
+}
+
+usb_device_descriptor_t *
+usbf_device_descriptor(usbf_device_handle dev)
+{
+ return &dev->ddesc;
+}
+
+usb_config_descriptor_t *
+usbf_config_descriptor(usbf_device_handle dev, u_int8_t index)
+{
+ struct usbf_config *uc;
+
+ SIMPLEQ_FOREACH(uc, &dev->configs, next) {
+ if (index-- == 0)
+ return uc->uc_cdesc;
+ }
+ return NULL;
+}
+
+int
+usbf_interface_number(usbf_interface_handle iface)
+{
+ return iface->idesc->bInterfaceNumber;
+}
+
+u_int8_t
+usbf_endpoint_address(usbf_endpoint_handle endpoint)
+{
+ return endpoint->edesc->bEndpointAddress;
+}
+
+u_int8_t
+usbf_endpoint_attributes(usbf_endpoint_handle endpoint)
+{
+ return endpoint->edesc->bmAttributes;
+}
+
+usbf_status
+usbf_open_pipe(usbf_interface_handle iface, u_int8_t address,
+ usbf_pipe_handle *pipe)
+{
+ return usbf_open_pipe_ival(iface, address, pipe, 0);
+}
+
+usbf_status
+usbf_open_pipe_ival(usbf_interface_handle iface, u_int8_t address,
+ usbf_pipe_handle *pipe, int ival)
+{
+ struct usbf_endpoint *ep;
+ usbf_pipe_handle p;
+ usbf_status err;
+
+ ep = usbf_iface_endpoint(iface, address);
+ if (ep == NULL)
+ return USBF_BAD_ADDRESS;
+
+ err = usbf_setup_pipe(iface->config->uc_device, iface, ep,
+ ival, &p);
+ if (err)
+ return err;
+ LIST_INSERT_HEAD(&iface->pipes, p, next);
+ *pipe = p;
+ return USBF_NORMAL_COMPLETION;
+}
+
+usbf_status
+usbf_setup_pipe(usbf_device_handle dev, usbf_interface_handle iface,
+ struct usbf_endpoint *ep, int ival, usbf_pipe_handle *pipe)
+{
+ struct usbf_pipe *p;
+ usbf_status err;
+
+ p = malloc(dev->bus->pipe_size, M_USB, M_NOWAIT);
+ if (p == NULL)
+ return USBF_NOMEM;
+
+ p->device = dev;
+ p->iface = iface;
+ p->endpoint = ep;
+ ep->refcnt++;
+ p->running = 0;
+ p->refcnt = 1;
+ p->repeat = 0;
+ p->interval = ival;
+ p->methods = NULL; /* set by bus driver in open_pipe() */
+ SIMPLEQ_INIT(&p->queue);
+ err = dev->bus->methods->open_pipe(p);
+ if (err) {
+ free(p, M_USB);
+ return err;
+ }
+ *pipe = p;
+ return USBF_NORMAL_COMPLETION;
+}
+
+/* Dequeue all pipe operations. */
+void
+usbf_abort_pipe(usbf_pipe_handle pipe)
+{
+ usbf_xfer_handle xfer;
+ int s;
+
+ s = splusb();
+ pipe->repeat = 0;
+ pipe->aborting = 1;
+
+ while ((xfer = SIMPLEQ_FIRST(&pipe->queue)) != NULL) {
+ DPRINTF(0,("usbf_abort_pipe: pipe=%p, xfer=%p\n", pipe,
+ xfer));
+ /* Make the DC abort it (and invoke the callback). */
+ pipe->methods->abort(xfer);
+ }
+
+ pipe->aborting = 0;
+ splx(s);
+}
+
+/* Abort all pipe operations and close the pipe. */
+void
+usbf_close_pipe(usbf_pipe_handle pipe)
+{
+ usbf_abort_pipe(pipe);
+ pipe->methods->close(pipe);
+ pipe->endpoint->refcnt--;
+ free(pipe, M_USB);
+}
+
+void
+usbf_stall_pipe(usbf_pipe_handle pipe)
+{
+ DPRINTF(0,("usbf_stall_pipe not implemented\n"));
+}
+
+usbf_endpoint_handle
+usbf_iface_endpoint(usbf_interface_handle iface, u_int8_t address)
+{
+ usbf_endpoint_handle ep;
+
+ SIMPLEQ_FOREACH(ep, &iface->endpoint_head, next) {
+ if (ep->edesc->bEndpointAddress == address)
+ return ep;
+ }
+ return NULL;
+}
+
+usbf_endpoint_handle
+usbf_config_endpoint(usbf_config_handle cfg, u_int8_t address)
+{
+ usbf_interface_handle iface;
+ usbf_endpoint_handle ep;
+
+ SIMPLEQ_FOREACH(iface, &cfg->iface_head, next) {
+ SIMPLEQ_FOREACH(ep, &iface->endpoint_head, next) {
+ if (ep->edesc->bEndpointAddress == address)
+ return ep;
+ }
+ }
+ return NULL;
+}
+
+void
+usbf_set_endpoint_halt(usbf_endpoint_handle endpoint)
+{
+}
+
+void
+usbf_clear_endpoint_halt(usbf_endpoint_handle endpoint)
+{
+}
+
+usbf_status
+usbf_set_endpoint_feature(usbf_config_handle cfg, u_int8_t address,
+ u_int16_t value)
+{
+ usbf_endpoint_handle ep;
+
+ DPRINTF(0,("usbf_set_endpoint_feature: cfg=%p address=%#x"
+ " value=%#x\n", cfg, address, value));
+
+ ep = usbf_config_endpoint(cfg, address);
+ if (ep == NULL)
+ return USBF_BAD_ADDRESS;
+
+ switch (value) {
+ case UF_ENDPOINT_HALT:
+ usbf_set_endpoint_halt(ep);
+ return USBF_NORMAL_COMPLETION;
+ default:
+ /* unsupported feature, send STALL in data/status phase */
+ return USBF_STALLED;
+ }
+}
+
+usbf_status
+usbf_clear_endpoint_feature(usbf_config_handle cfg, u_int8_t address,
+ u_int16_t value)
+{
+ usbf_endpoint_handle ep;
+
+ DPRINTF(0,("usbf_clear_endpoint_feature: cfg=%p address=%#x"
+ " value=%#x\n", cfg, address, value));
+
+ ep = usbf_config_endpoint(cfg, address);
+ if (ep == NULL)
+ return USBF_BAD_ADDRESS;
+
+ switch (value) {
+ case UF_ENDPOINT_HALT:
+ usbf_clear_endpoint_halt(ep);
+ return USBF_NORMAL_COMPLETION;
+ default:
+ /* unsupported feature, send STALL in data/status phase */
+ return USBF_STALLED;
+ }
+}
+
+usbf_xfer_handle
+usbf_alloc_xfer(usbf_device_handle dev)
+{
+ struct usbf_xfer *xfer;
+
+ /* allocate zero-filled buffer */
+ xfer = dev->bus->methods->allocx(dev->bus);
+ if (xfer == NULL)
+ return NULL;
+ xfer->device = dev;
+ usb_callout_init(xfer->timeout_handle);
+ DPRINTF(1,("usbf_alloc_xfer() = %p\n", xfer));
+ return xfer;
+}
+
+void
+usbf_free_xfer(usbf_xfer_handle xfer)
+{
+ DPRINTF(1,("usbf_free_xfer: %p\n", xfer));
+ if (xfer->rqflags & (URQ_DEV_DMABUF | URQ_AUTO_DMABUF))
+ usbf_free_buffer(xfer);
+ xfer->device->bus->methods->freex(xfer->device->bus, xfer);
+}
+
+usbf_status
+usbf_allocmem(usbf_bus_handle bus, size_t size, size_t align, usb_dma_t *p)
+{
+ struct usbd_bus dbus;
+ usbd_status err;
+
+ /* XXX bad idea, fix usb_mem.c instead! */
+ dbus.dmatag = bus->dmatag;
+ err = usb_allocmem(&dbus, size, align, p);
+ return err ? USBF_NOMEM : USBF_NORMAL_COMPLETION;
+}
+
+void
+usbf_freemem(usbf_bus_handle bus, usb_dma_t *p)
+{
+ usb_freemem((usbd_bus_handle)NULL, p);
+}
+
+void *
+usbf_alloc_buffer(usbf_xfer_handle xfer, u_int32_t size)
+{
+ struct usbf_bus *bus = xfer->device->bus;
+ usbf_status err;
+
+#ifdef DIAGNOSTIC
+ if (xfer->rqflags & (URQ_DEV_DMABUF | URQ_AUTO_DMABUF))
+ printf("xfer %p already has a buffer\n", xfer);
+#endif
+
+ err = bus->methods->allocm(bus, &xfer->dmabuf, size);
+ if (err)
+ return NULL;
+
+ xfer->rqflags |= URQ_DEV_DMABUF;
+ return KERNADDR(&xfer->dmabuf, 0);
+}
+
+void
+usbf_free_buffer(usbf_xfer_handle xfer)
+{
+#ifdef DIAGNOSTIC
+ if (!(xfer->rqflags & URQ_DEV_DMABUF)) {
+ printf("usbf_free_buffer: no buffer\n");
+ return;
+ }
+#endif
+ xfer->rqflags &= ~URQ_DEV_DMABUF;
+ xfer->device->bus->methods->freem(xfer->device->bus, &xfer->dmabuf);
+}
+
+#ifdef USBF_DEBUG
+/*
+ * The dump format is similar to Linux' Gadget driver so that we can
+ * easily compare traces.
+ */
+static void
+usbf_dump_buffer(usbf_xfer_handle xfer)
+{
+ device_ptr_t dev = (device_ptr_t)xfer->pipe->device->bus->usbfctl;
+ usbf_endpoint_handle ep = xfer->pipe->endpoint;
+ int index = usbf_endpoint_index(ep);
+ int dir = usbf_endpoint_dir(ep);
+ u_char *p = xfer->buffer;
+ u_int i;
+
+ printf("%s: ep%d-%s, length=%u, %s", USBDEVNAME(*dev), index,
+ (xfer->rqflags & URQ_REQUEST) ? "setup" :
+ (index == 0 ? "in" : (dir == UE_DIR_IN ? "in" : "out")),
+ xfer->length, usbf_errstr(xfer->status));
+
+ for (i = 0; i < xfer->length; i++) {
+ if ((i % 16) == 0)
+ printf("\n%4x:", i);
+ else if ((i % 8) == 0)
+ printf(" ");
+ printf(" %02x", p[i]);
+ }
+ printf("\n");
+}
+#endif
+
+void
+usbf_setup_xfer(usbf_xfer_handle xfer, usbf_pipe_handle pipe,
+ usbf_private_handle priv, void *buffer, u_int32_t length,
+ u_int16_t flags, u_int32_t timeout, usbf_callback callback)
+{
+ xfer->pipe = pipe;
+ xfer->priv = priv;
+ xfer->buffer = buffer;
+ xfer->length = length;
+ xfer->actlen = 0;
+ xfer->flags = flags;
+ xfer->timeout = timeout;
+ xfer->status = USBF_NOT_STARTED;
+ xfer->callback = callback;
+ xfer->rqflags &= ~URQ_REQUEST;
+}
+
+void
+usbf_setup_default_xfer(usbf_xfer_handle xfer, usbf_pipe_handle pipe,
+ usbf_private_handle priv, usb_device_request_t *req, u_int16_t flags,
+ u_int32_t timeout, usbf_callback callback)
+{
+ xfer->pipe = pipe;
+ xfer->priv = priv;
+ xfer->buffer = req;
+ xfer->length = sizeof *req;
+ xfer->actlen = 0;
+ xfer->flags = flags;
+ xfer->timeout = timeout;
+ xfer->status = USBF_NOT_STARTED;
+ xfer->callback = callback;
+ xfer->rqflags |= URQ_REQUEST;
+}
+
+void
+usbf_get_xfer_status(usbf_xfer_handle xfer, usbf_private_handle *priv,
+ void **buffer, u_int32_t *actlen, usbf_status *status)
+{
+ if (priv != NULL)
+ *priv = xfer->priv;
+ if (buffer != NULL)
+ *buffer = xfer->buffer;
+ if (actlen != NULL)
+ *actlen = xfer->actlen;
+ if (status != NULL)
+ *status = xfer->status;
+}
+
+usbf_status
+usbf_transfer(usbf_xfer_handle xfer)
+{
+ usbf_pipe_handle pipe = xfer->pipe;
+ usbf_status err;
+
+ err = pipe->methods->transfer(xfer);
+ if (err != USBF_IN_PROGRESS && err) {
+ if (xfer->rqflags & URQ_AUTO_DMABUF) {
+ usbf_free_buffer(xfer);
+ xfer->rqflags &= ~URQ_AUTO_DMABUF;
+ }
+ }
+ return err;
+}
+
+usbf_status
+usbf_insert_transfer(usbf_xfer_handle xfer)
+{
+ usbf_pipe_handle pipe = xfer->pipe;
+ usbf_status err;
+ int s;
+
+ DPRINTF(1,("usbf_insert_transfer: xfer=%p pipe=%p running=%d\n",
+ xfer, pipe, pipe->running));
+
+ s = splusb();
+ SIMPLEQ_INSERT_TAIL(&pipe->queue, xfer, next);
+ if (pipe->running)
+ err = USBF_IN_PROGRESS;
+ else {
+ pipe->running = 1;
+ err = USBF_NORMAL_COMPLETION;
+ }
+ splx(s);
+ return err;
+}
+
+void
+usbf_start_next(usbf_pipe_handle pipe)
+{
+ usbf_xfer_handle xfer;
+ usbf_status err;
+
+ SPLUSBCHECK;
+
+ /* Get next request in queue. */
+ xfer = SIMPLEQ_FIRST(&pipe->queue);
+ if (xfer == NULL)
+ pipe->running = 0;
+ else {
+ err = pipe->methods->start(xfer);
+ if (err != USBF_IN_PROGRESS) {
+ printf("usbf_start_next: %s\n", usbf_errstr(err));
+ pipe->running = 0;
+ /* XXX do what? */
+ }
+ }
+}
+
+/* Called at splusb() */
+void
+usbf_transfer_complete(usbf_xfer_handle xfer)
+{
+ usbf_pipe_handle pipe = xfer->pipe;
+ int repeat = pipe->repeat;
+
+ SPLUSBCHECK;
+ DPRINTF(1,("usbf_transfer_complete: xfer=%p pipe=%p running=%d\n",
+ xfer, pipe, pipe->running));
+#ifdef USBF_DEBUG
+ if (usbfdebug > 0)
+ usbf_dump_buffer(xfer);
+#endif
+
+ if (!repeat) {
+ /* Remove request from queue. */
+ KASSERT(SIMPLEQ_FIRST(&pipe->queue) == xfer);
+ SIMPLEQ_REMOVE_HEAD(&pipe->queue, next);
+ }
+
+ if (xfer->status == USBF_NORMAL_COMPLETION &&
+ xfer->actlen < xfer->length &&
+ !(xfer->flags & USBD_SHORT_XFER_OK)) {
+ DPRINTF(0,("usbf_transfer_complete: short xfer=%p %u<%u\n",
+ xfer, xfer->actlen, xfer->length));
+ xfer->status = USBF_SHORT_XFER;
+ }
+
+ if (xfer->callback != NULL)
+ xfer->callback(xfer, xfer->priv, xfer->status);
+
+ pipe->methods->done(xfer);
+
+ /* XXX wake up any processes waiting for the transfer to complete */
+
+ if (!repeat) {
+ if (xfer->status != USBF_NORMAL_COMPLETION &&
+ pipe->iface != NULL) /* not control pipe */
+ pipe->running = 0;
+ else
+ usbf_start_next(pipe);
+ }
+}
+
+/*
+ * Software interrupts
+ */
+
+usbf_status
+usbf_softintr_establish(struct usbf_bus *bus)
+{
+#ifdef USB_USE_SOFTINTR
+#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
+ KASSERT(bus->soft == NULL);
+ /* XXX we should have our own level */
+ bus->soft = softintr_establish(IPL_SOFTNET,
+ bus->methods->soft_intr, bus);
+ if (bus->soft == NULL)
+ return USBF_INVAL;
+#else
+ usb_callout_init(bus->softi);
+#endif
+#endif
+ return USBF_NORMAL_COMPLETION;
+}
+
+void
+usbf_schedsoftintr(struct usbf_bus *bus)
+{
+#ifdef USB_USE_SOFTINTR
+#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
+ softintr_schedule(bus->soft);
+#else
+ if (!usb_callout_pending(bus->softi))
+ usb_callout(bus->softi, 0, bus->methods->soft_intr,
+ bus);
+#endif /* __HAVE_GENERIC_SOFT_INTERRUPTS */
+#else
+ bus->methods->soft_intr(bus);
+#endif /* USB_USE_SOFTINTR */
+}
diff --git a/sys/dev/usb/usbfvar.h b/sys/dev/usb/usbfvar.h
new file mode 100644
index 00000000000..d36836b12c0
--- /dev/null
+++ b/sys/dev/usb/usbfvar.h
@@ -0,0 +1,175 @@
+/* $OpenBSD: usbfvar.h,v 1.1 2006/11/25 18:10:29 uwe Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * USB function driver interface
+ *
+ * This file is to be included only by the logical device driver and the
+ * USB device controller (DC) driver.
+ */
+
+/*** structures corresponding to USB protocol components ***/
+
+struct usbf_endpoint {
+ struct usbf_interface *iface;
+ usb_endpoint_descriptor_t *edesc;
+ int halted; /* UF_ENDPOINT_HALT set */
+ int refcnt;
+ SIMPLEQ_ENTRY(usbf_endpoint) next;
+};
+
+struct usbf_interface {
+ struct usbf_config *config;
+ usb_interface_descriptor_t *idesc;
+ LIST_HEAD(, usbf_pipe) pipes;
+ SIMPLEQ_HEAD(, usbf_endpoint) endpoint_head;
+ SIMPLEQ_ENTRY(usbf_interface) next;
+};
+
+struct usbf_config {
+ struct usbf_device *uc_device;
+ usb_config_descriptor_t *uc_cdesc;
+ size_t uc_cdesc_size;
+ int uc_closed;
+ SIMPLEQ_HEAD(, usbf_interface) iface_head;
+ SIMPLEQ_ENTRY(usbf_config) next;
+};
+
+struct usbf_device {
+ USBBASEDEVICE bdev; /* base device */
+ struct usbf_bus *bus; /* device controller */
+ struct usbf_function *function; /* function driver */
+ struct usbf_pipe *default_pipe; /* pipe 0 (device control) */
+ struct usbf_xfer *default_xfer; /* device request xfer */
+ struct usbf_xfer *data_xfer; /* request response xfer */
+ int address; /* assigned by host (or 0) */
+ usbf_config_handle config; /* set by host (or NULL) */
+ usb_status_t status; /* device status */
+ usb_device_request_t def_req; /* device request buffer */
+ struct usbf_endpoint def_ep; /* for pipe 0 */
+ usb_endpoint_descriptor_t def_ep_desc; /* for pipe 0 */
+ usb_device_descriptor_t ddesc; /* device descriptor */
+ usb_string_descriptor_t *sdesc; /* string descriptors */
+ size_t sdesc_size; /* size of ud_sdesc */
+ uByte string_id; /* next string id */
+ SIMPLEQ_HEAD(, usbf_config) configs;
+};
+
+/*** software control structures ***/
+
+struct usbf_pipe_methods {
+ usbf_status (*transfer)(usbf_xfer_handle);
+ usbf_status (*start)(usbf_xfer_handle);
+ void (*abort)(usbf_xfer_handle);
+ void (*done)(usbf_xfer_handle);
+ void (*close)(usbf_pipe_handle);
+};
+
+struct usbf_bus_methods {
+ usbf_status (*open_pipe)(struct usbf_pipe *);
+ void (*soft_intr)(void *);
+ usbf_status (*allocm)(struct usbf_bus *, usb_dma_t *, u_int32_t);
+ void (*freem)(struct usbf_bus *, usb_dma_t *);
+ struct usbf_xfer *(*allocx)(struct usbf_bus *);
+ void (*freex)(struct usbf_bus *, struct usbf_xfer *);
+};
+
+struct usbf_softc;
+
+struct usbf_bus {
+ /* Filled by DC driver */
+ USBBASEDEVICE bdev; /* base device */
+ struct usbf_bus_methods *methods;
+ size_t pipe_size; /* size of pipe struct */
+ u_int8_t ep0_maxp; /* packet size for EP0 */
+ int usbrev; /* as in struct usbd_bus */
+ /* Filled by usbf driver */
+ struct usbf_softc *usbfctl;
+ int intr_context;
+#ifdef USB_USE_SOFTINTR
+#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
+ void *soft; /* soft interrupt cookie */
+#else
+ usb_callout_t softi; /* timeout handle */
+#endif
+#endif
+ bus_dma_tag_t dmatag; /* DMA tag */
+};
+
+struct usbf_port {
+ usb_port_status_t status;
+ u_int8_t portno;
+ struct usbf_device *device; /* connected function */
+};
+
+struct usbf_pipe {
+ struct usbf_device *device;
+ struct usbf_interface *iface; /* unless default pipe */
+ struct usbf_endpoint *endpoint;
+ int refcnt;
+ int running;
+ int aborting;
+ SIMPLEQ_HEAD(, usbf_xfer) queue;
+ LIST_ENTRY(usbf_pipe) next;
+
+ char repeat;
+ int interval;
+
+ /* Filled by DC driver. */
+ struct usbf_pipe_methods *methods;
+};
+
+struct usbf_xfer {
+ struct usbf_pipe *pipe;
+ usbf_private_handle priv;
+ void *buffer;
+ u_int32_t length;
+ u_int32_t actlen;
+ u_int16_t flags;
+ u_int32_t timeout;
+ usbf_status status;
+ usbf_callback callback;
+ SIMPLEQ_ENTRY(usbf_xfer) next;
+
+ /* for memory management */
+ struct usbf_device *device;
+ int rqflags;
+ usb_dma_t dmabuf;
+
+ usb_callout_t timeout_handle;
+};
+
+
+/* usbf.c */
+void usbf_host_reset(usbf_bus_handle);
+void usbf_do_request(usbf_xfer_handle, usbf_private_handle,
+ usbf_status);
+
+/* usbf_subr.c */
+usbf_status usbf_new_device(device_ptr_t, usbf_bus_handle, int,
+ int, int, struct usbf_port *);
+usbf_status usbf_set_endpoint_feature(usbf_config_handle, u_int8_t,
+ u_int16_t);
+usbf_status usbf_clear_endpoint_feature(usbf_config_handle, u_int8_t,
+ u_int16_t);
+usbf_status usbf_insert_transfer(usbf_xfer_handle xfer);
+void usbf_transfer_complete(usbf_xfer_handle xfer);
+usbf_status usbf_allocmem(usbf_bus_handle, size_t, size_t, usb_dma_t *);
+void usbf_freemem(usbf_bus_handle, usb_dma_t *);
+usbf_status usbf_softintr_establish(struct usbf_bus *);
+void usbf_schedsoftintr(struct usbf_bus *);