From 7535b19446bafa37c29fe0cefdcfcc8d3b16b636 Mon Sep 17 00:00:00 2001 From: Uwe Stuehler Date: Sat, 25 Nov 2006 18:10:30 +0000 Subject: Initial USB device controller (UDC) and USB CDC Ethernet function support for PXA27x/Zaurus, not enabled yet; for dlg and dale :) --- sys/dev/usb/files.usb | 17 +- sys/dev/usb/if_cdcef.c | 252 +++++++++++ sys/dev/usb/usbf.c | 698 ++++++++++++++++++++++++++++++ sys/dev/usb/usbf_subr.c | 1099 +++++++++++++++++++++++++++++++++++++++++++++++ sys/dev/usb/usbfvar.h | 175 ++++++++ 5 files changed, 2239 insertions(+), 2 deletions(-) create mode 100644 sys/dev/usb/if_cdcef.c create mode 100644 sys/dev/usb/usbf.c create mode 100644 sys/dev/usb/usbf_subr.c create mode 100644 sys/dev/usb/usbfvar.h (limited to 'sys/dev') 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 + * + * 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 +#include +#include +#include + +#include + +#include +#include +#include +#include + +#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 + * + * 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 +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#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 + * + * 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 +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#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 + * + * 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 *); -- cgit v1.2.3