summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/arch/arm/xscale/files.pxa2x04
-rw-r--r--sys/arch/arm/xscale/pxa27x_udc.c817
-rw-r--r--sys/arch/arm/xscale/pxa27x_udcreg.h21
-rw-r--r--sys/arch/zaurus/conf/GENERIC6
-rw-r--r--sys/arch/zaurus/include/zaurus_reg.h6
-rw-r--r--sys/conf/files3
-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
11 files changed, 3046 insertions, 52 deletions
diff --git a/sys/arch/arm/xscale/files.pxa2x0 b/sys/arch/arm/xscale/files.pxa2x0
index d37eb8edc0a..ec2b493eb36 100644
--- a/sys/arch/arm/xscale/files.pxa2x0
+++ b/sys/arch/arm/xscale/files.pxa2x0
@@ -1,4 +1,4 @@
-# $OpenBSD: files.pxa2x0,v 1.18 2005/12/20 02:37:09 drahn Exp $
+# $OpenBSD: files.pxa2x0,v 1.19 2006/11/25 18:10:29 uwe Exp $
# $NetBSD: files.pxa2x0,v 1.6 2004/05/01 19:09:14 thorpej Exp $
#
# Configuration info for Intel PXA2[51]0 CPU support
@@ -46,7 +46,7 @@ file arch/arm/xscale/pxa2x0_a4x_space.c com_pxaip
file arch/arm/xscale/pxa2x0_a4x_io.S com_pxaip
# PXA27x USB Device Controller
-device pxaudc
+device pxaudc: usbdev
attach pxaudc at pxaip
file arch/arm/xscale/pxa27x_udc.c pxaudc
diff --git a/sys/arch/arm/xscale/pxa27x_udc.c b/sys/arch/arm/xscale/pxa27x_udc.c
index 94f843422e2..e802860af51 100644
--- a/sys/arch/arm/xscale/pxa27x_udc.c
+++ b/sys/arch/arm/xscale/pxa27x_udc.c
@@ -1,7 +1,8 @@
-/* $OpenBSD: pxa27x_udc.c,v 1.5 2005/03/30 14:24:39 dlg Exp $ */
+/* $OpenBSD: pxa27x_udc.c,v 1.6 2006/11/25 18:10:29 uwe Exp $ */
/*
* Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
+ * 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
@@ -20,31 +21,98 @@
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
+#include <sys/malloc.h>
#include <machine/intr.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>
+
#include <arm/xscale/pxa2x0reg.h>
#include <arm/xscale/pxa2x0var.h>
#include <arm/xscale/pxa2x0_gpio.h>
#include <arm/xscale/pxa27x_udcreg.h>
+#define PXAUDC_EP0MAXP 16 /* XXX */
+#define PXAUDC_NEP 24 /* total number of endpoints */
-int pxaudc_match(struct device *, void *, void *);
-void pxaudc_attach(struct device *, struct device *, void *);
-int pxaudc_detach(struct device *, int);
-void pxaudc_power(int, void *);
+#include <machine/zaurus_reg.h> /* XXX */
-struct pxaudc_softc {
- struct device sc_dev;
- bus_space_tag_t sc_iot;
- bus_space_handle_t sc_ioh;
- bus_size_t sc_size;
+struct pxaudc_xfer {
+ struct usbf_xfer xfer;
+ u_int16_t frmlen;
+};
+struct pxaudc_pipe {
+ struct usbf_pipe pipe;
+ LIST_ENTRY(pxaudc_pipe) list;
+};
+
+struct pxaudc_softc {
+ struct usbf_bus sc_bus;
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+ bus_size_t sc_size;
+ void *sc_ih;
+ void *sc_conn_ih;
void *sc_powerhook;
+ SIMPLEQ_HEAD(,usbf_xfer) sc_free_xfers; /* recycled xfers */
+ u_int32_t sc_icr0; /* enabled EP interrupts */
+ u_int32_t sc_icr1; /* enabled EP interrupts */
+ enum {
+ EP0_SETUP,
+ EP0_IN
+ } sc_ep0state;
+ u_int32_t sc_isr0; /* XXX deferred interrupts */
+ u_int32_t sc_isr1; /* XXX deferred interrupts */
+ u_int32_t sc_otgisr; /* XXX deferred interrupts */
+ struct pxaudc_pipe *sc_pipe[PXAUDC_NEP];
};
-void pxaudc_enable(struct pxaudc_softc *);
+int pxaudc_match(struct device *, void *, void *);
+void pxaudc_attach(struct device *, struct device *, void *);
+int pxaudc_detach(struct device *, int);
+void pxaudc_power(int, void *);
+
+int pxaudc_is_host(void);
+int pxaudc_is_device(void);
+void pxaudc_setup(struct pxaudc_softc *);
+void pxaudc_hide(struct pxaudc_softc *);
+void pxaudc_show(struct pxaudc_softc *);
+
+void pxaudc_enable(struct pxaudc_softc *);
+void pxaudc_disable(struct pxaudc_softc *);
+void pxaudc_read_ep0(struct pxaudc_softc *, usbf_xfer_handle);
+void pxaudc_write_ep0(struct pxaudc_softc *, usbf_xfer_handle);
+void pxaudc_write(struct pxaudc_softc *, usbf_xfer_handle);
+
+int pxaudc_connect_intr(void *);
+int pxaudc_intr(void *);
+void pxaudc_intr1(struct pxaudc_softc *);
+void pxaudc_ep0_intr(struct pxaudc_softc *);
+
+usbf_status pxaudc_open(struct usbf_pipe *);
+void pxaudc_softintr(void *);
+usbf_status pxaudc_allocm(struct usbf_bus *, usb_dma_t *, u_int32_t);
+void pxaudc_freem(struct usbf_bus *, usb_dma_t *);
+usbf_xfer_handle pxaudc_allocx(struct usbf_bus *);
+void pxaudc_freex(struct usbf_bus *, usbf_xfer_handle);
+
+usbf_status pxaudc_ctrl_transfer(usbf_xfer_handle);
+usbf_status pxaudc_ctrl_start(usbf_xfer_handle);
+void pxaudc_ctrl_abort(usbf_xfer_handle);
+void pxaudc_ctrl_done(usbf_xfer_handle);
+void pxaudc_ctrl_close(usbf_pipe_handle);
+
+usbf_status pxaudc_bulk_transfer(usbf_xfer_handle);
+usbf_status pxaudc_bulk_start(usbf_xfer_handle);
+void pxaudc_bulk_abort(usbf_xfer_handle);
+void pxaudc_bulk_done(usbf_xfer_handle);
+void pxaudc_bulk_close(usbf_pipe_handle);
struct cfattach pxaudc_ca = {
sizeof(struct pxaudc_softc), pxaudc_match, pxaudc_attach,
@@ -55,6 +123,52 @@ struct cfdriver pxaudc_cd = {
NULL, "pxaudc", DV_DULL
};
+struct usbf_bus_methods pxaudc_bus_methods = {
+ pxaudc_open,
+ pxaudc_softintr,
+ pxaudc_allocm,
+ pxaudc_freem,
+ pxaudc_allocx,
+ pxaudc_freex
+};
+
+struct usbf_pipe_methods pxaudc_ctrl_methods = {
+ pxaudc_ctrl_transfer,
+ pxaudc_ctrl_start,
+ pxaudc_ctrl_abort,
+ pxaudc_ctrl_done,
+ pxaudc_ctrl_close
+};
+
+struct usbf_pipe_methods pxaudc_bulk_methods = {
+ pxaudc_bulk_transfer,
+ pxaudc_bulk_start,
+ pxaudc_bulk_abort,
+ pxaudc_bulk_done,
+ pxaudc_bulk_close
+};
+
+#define DEVNAME(sc) USBDEVNAME((sc)->sc_bus.bdev)
+
+#define CSR_READ_4(sc, reg) \
+ bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))
+#define CSR_WRITE_4(sc, reg, val) \
+ bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
+#define CSR_WRITE_1(sc, reg, val) \
+ bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
+#define CSR_SET_4(sc, reg, val) \
+ CSR_WRITE_4((sc), (reg), CSR_READ_4((sc), (reg)) | (val))
+#define CSR_CLR_4(sc, reg, val) \
+ CSR_WRITE_4((sc), (reg), CSR_READ_4((sc), (reg)) & ~(val))
+
+#define PXAUDC_DEBUG
+#ifndef PXAUDC_DEBUG
+#define DPRINTF(l, x) do {} while (0)
+#else
+int pxaudcdebug = 0;
+#define DPRINTF(l, x) if ((l) <= pxaudcdebug) printf x; else {}
+#endif
+
int
pxaudc_match(struct device *parent, void *match, void *aux)
{
@@ -71,9 +185,6 @@ pxaudc_attach(struct device *parent, struct device *self, void *aux)
struct pxaip_attach_args *pxa = aux;
sc->sc_iot = pxa->pxa_iot;
- sc->sc_size = 0;
- sc->sc_powerhook = NULL;
-
if (bus_space_map(sc->sc_iot, PXA2X0_USBDC_BASE, PXA2X0_USBDC_SIZE, 0,
&sc->sc_ioh)) {
printf(": cannot map mem space\n");
@@ -86,18 +197,51 @@ pxaudc_attach(struct device *parent, struct device *self, void *aux)
bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, sc->sc_size,
BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);
- pxa2x0_gpio_set_function(35, GPIO_ALT_FN_2_IN); /* USB_P2_1 */
- pxa2x0_gpio_set_function(37, GPIO_ALT_FN_1_OUT); /* USB_P2_8 */
- pxa2x0_gpio_set_function(41, GPIO_ALT_FN_2_IN); /* USB_P2_7 */
- pxa2x0_gpio_set_function(89, GPIO_ALT_FN_2_OUT); /* USBHPEN<1> */
- pxa2x0_gpio_set_function(120, GPIO_ALT_FN_2_OUT); /* USBHPEN<2> */
+ /* Set up GPIO pins and disable the controller. */
+ pxaudc_setup(sc);
+ pxaudc_disable(sc);
- pxa2x0_clkman_config(CKEN_USBDC, 0);
- pxaudc_enable(sc);
+ /* Establish USB device interrupt. */
+ sc->sc_ih = pxa2x0_intr_establish(PXA2X0_INT_USB, IPL_USB,
+ pxaudc_intr, sc, DEVNAME(sc));
+ if (sc->sc_ih == NULL) {
+ printf(": unable to establish interrupt\n");
+ bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size);
+ sc->sc_size = 0;
+ return;
+ }
- pxa2x0_gpio_set_bit(37); /* USB_P2_8 */
+ /* Establish device connect interrupt. */
+#if 0
+ sc->sc_conn_ih = pxa2x0_gpio_intr_establish(C3000_USB_DEVICE_PIN, /* XXX */
+ IST_EDGE_BOTH, IPL_USB, pxaudc_connect_intr, sc, "usbc");
+#endif
+ sc->sc_conn_ih = pxa2x0_gpio_intr_establish(C3000_USB_CONNECT_PIN,
+ IST_EDGE_BOTH, IPL_USB, pxaudc_connect_intr, sc, "usbc");
+ if (sc->sc_conn_ih == NULL) {
+ printf(": unable to establish connect interrupt\n");
+ pxa2x0_intr_disestablish(sc->sc_ih);
+ bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size);
+ sc->sc_ioh = NULL;
+ sc->sc_size = 0;
+ return;
+ }
sc->sc_powerhook = powerhook_establish(pxaudc_power, sc);
+
+ /* Set up the bus struct. */
+ sc->sc_bus.methods = &pxaudc_bus_methods;
+ sc->sc_bus.pipe_size = sizeof(struct pxaudc_pipe);
+ sc->sc_bus.ep0_maxp = PXAUDC_EP0MAXP;
+ sc->sc_bus.usbrev = USBREV_1_1;
+ sc->sc_bus.dmatag = pxa->pxa_dmat;
+
+ /* Attach logical device and function. */
+ (void)config_found(self, &sc->sc_bus, NULL);
+
+ /* Enable the controller unless we're now acting as a host. */
+ if (!pxaudc_is_host())
+ pxaudc_enable(sc);
}
int
@@ -108,6 +252,12 @@ pxaudc_detach(struct device *self, int flags)
if (sc->sc_powerhook != NULL)
powerhook_disestablish(sc->sc_powerhook);
+ if (sc->sc_conn_ih != NULL)
+ pxa2x0_gpio_intr_disestablish(sc->sc_conn_ih);
+
+ if (sc->sc_ih != NULL)
+ pxa2x0_intr_disestablish(sc->sc_ih);
+
if (sc->sc_size) {
bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size);
sc->sc_size = 0;
@@ -121,32 +271,611 @@ pxaudc_power(int why, void *arg)
{
struct pxaudc_softc *sc = (struct pxaudc_softc *)arg;
- if (why == PWR_RESUME)
+ switch (why) {
+ case PWR_SUSPEND:
+ case PWR_STANDBY:
+ pxaudc_disable(sc);
+ break;
+
+ case PWR_RESUME:
pxaudc_enable(sc);
+ break;
+ }
+}
+
+/*
+ * Machine-specific functions
+ */
+
+/* XXX move to machine-specific file */
+
+int
+pxaudc_is_host(void)
+{
+ return (!pxa2x0_gpio_get_bit(C3000_USB_CONNECT_PIN) &&
+ !pxa2x0_gpio_get_bit(C3000_USB_DEVICE_PIN));
+}
+
+int
+pxaudc_is_device(void)
+{
+ return (pxa2x0_gpio_get_bit(C3000_USB_CONNECT_PIN) &&
+ pxa2x0_gpio_get_bit(C3000_USB_DEVICE_PIN));
+}
+
+void
+pxaudc_setup(struct pxaudc_softc *sc)
+{
+ pxa2x0_gpio_set_function(45, GPIO_OUT);
+ pxa2x0_gpio_set_function(C3000_USB_CONNECT_PIN, GPIO_IN); /* 41 */
+ pxa2x0_gpio_set_function(40, GPIO_OUT);
+ pxa2x0_gpio_set_function(39, GPIO_IN);
+ pxa2x0_gpio_set_function(38, GPIO_IN);
+ pxa2x0_gpio_set_function(37, GPIO_OUT);
+ pxa2x0_gpio_set_function(36, GPIO_IN);
+ pxa2x0_gpio_set_function(C3000_USB_DEVICE_PIN, GPIO_IN); /* 35 */
+ pxa2x0_gpio_set_function(34, GPIO_IN);
+ pxa2x0_gpio_set_function(89, GPIO_OUT);
+ pxa2x0_gpio_set_function(120, GPIO_OUT);
+}
+
+/* Hide us from the host. */
+void
+pxaudc_hide(struct pxaudc_softc *sc)
+{
+ pxa2x0_gpio_clear_bit(C3000_USB_PULLUP_PIN);
+}
+
+/* Show us to the host. */
+void
+pxaudc_show(struct pxaudc_softc *sc)
+{
+ pxa2x0_gpio_set_bit(C3000_USB_PULLUP_PIN);
+}
+
+/*
+ * Register manipulation
+ */
+
+#if 0
+static void
+pxaudc_dump_regs(struct pxaudc_softc *sc)
+{
+ printf("UDCCR\t%b\n", CSR_READ_4(sc, USBDC_UDCCR),
+ USBDC_UDCCR_BITS);
+ printf("UDCICR0\t%b\n", CSR_READ_4(sc, USBDC_UDCICR0),
+ USBDC_UDCISR0_BITS);
+ printf("UDCICR1\t%b\n", CSR_READ_4(sc, USBDC_UDCICR1),
+ USBDC_UDCISR1_BITS);
+ printf("OTGICR\t%b\n", CSR_READ_4(sc, USBDC_UDCOTGICR),
+ USBDC_UDCOTGISR_BITS);
}
+#endif
void
pxaudc_enable(struct pxaudc_softc *sc)
{
- u_int32_t hr;
-
- /* disable the controller */
- hr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, USBDC_UDCCR);
- bus_space_write_4(sc->sc_iot, sc->sc_ioh, USBDC_UDCCR,
- hr & ~USBDC_UDCCR_UDE);
-
- hr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, USBDC_UDCICR1);
- bus_space_write_4(sc->sc_iot, sc->sc_ioh, USBDC_UDCICR1,
- hr | USBDC_UDCICR1_IERS);
-
- bus_space_write_4(sc->sc_iot, sc->sc_ioh, USBDC_UP2OCR, 0);
- hr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, USBDC_UP2OCR);
- bus_space_write_4(sc->sc_iot, sc->sc_ioh, USBDC_UP2OCR,
- hr | USBDC_UP2OCR_HXS);
- hr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, USBDC_UP2OCR);
- bus_space_write_4(sc->sc_iot, sc->sc_ioh, USBDC_UP2OCR,
- hr | USBDC_UP2OCR_HXOE);
- hr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, USBDC_UP2OCR);
- bus_space_write_4(sc->sc_iot, sc->sc_ioh, USBDC_UP2OCR,
- hr | USBDC_UP2OCR_DPPDE|USBDC_UP2OCR_DMPDE);
+ DPRINTF(0,("pxaudc_enable\n"));
+
+ /* Start the clocks. */
+ pxa2x0_clkman_config(CKEN_USBDC, 1);
+
+ /* Configure Port 2 for USB device. */
+ CSR_WRITE_4(sc, USBDC_UP2OCR, USBDC_UP2OCR_DMPUE |
+ USBDC_UP2OCR_DPPUE | USBDC_UP2OCR_HXOE);
+
+ /* Enable interrupts for configured endpoints. */
+ CSR_SET_4(sc, USBDC_UDCICR0, USBDC_UDCICR0_IE(0) |
+ sc->sc_icr0);
+ CSR_SET_4(sc, USBDC_UDCICR1, USBDC_UDCICR1_IERS |
+ USBDC_UDCICR1_IECC | sc->sc_icr1);
+
+ /* XXX */
+ CSR_WRITE_4(sc, USBDC_UDCICR0, 0x00000C3F);
+ CSR_WRITE_4(sc, USBDC_UDCECR(1), 0x0200D103);
+ CSR_WRITE_4(sc, USBDC_UDCECR(2), 0x02014103);
+ CSR_WRITE_4(sc, USBDC_UDCECR(3), 0x0201B403);
+ CSR_WRITE_4(sc, USBDC_UDCECR(4), 0x02022403);
+ CSR_WRITE_4(sc, USBDC_UDCECR(5), 0x0202F021);
+
+ /* Enable the controller. */
+ CSR_CLR_4(sc, USBDC_UDCCR, USBDC_UDCCR_EMCE);
+ CSR_SET_4(sc, USBDC_UDCCR, USBDC_UDCCR_UDE);
+
+ /* Enable USB client on port 2. */
+ pxa2x0_gpio_clear_bit(37); /* USB_P2_8 */
+}
+
+void
+pxaudc_disable(struct pxaudc_softc *sc)
+{
+ DPRINTF(0,("pxaudc_disable\n"));
+
+ /* Disable the controller. */
+ CSR_CLR_4(sc, USBDC_UDCCR, USBDC_UDCCR_UDE);
+
+ /* Disable all interrupts. */
+ CSR_WRITE_4(sc, USBDC_UDCICR0, 0);
+ CSR_WRITE_4(sc, USBDC_UDCICR1, 0);
+ CSR_WRITE_4(sc, USBDC_UDCOTGICR, 0);
+
+ /* Set Port 2 output to "Non-OTG Host with Differential Port". */
+ CSR_WRITE_4(sc, USBDC_UP2OCR, USBDC_UP2OCR_HXS |
+ USBDC_UP2OCR_HXOE);
+
+ /* Set "Host Port 2 Transceiver D­ Pull Down Enable". */
+ CSR_SET_4(sc, USBDC_UP2OCR, USBDC_UP2OCR_DMPDE);
+
+ /* Stop the clocks. */
+ pxa2x0_clkman_config(CKEN_USBDC, 0);
+
+ /* Enable USB host on port 2. */
+ pxa2x0_gpio_set_bit(37); /* USB_P2_8 */
+}
+
+/*
+ * Endpoint FIFO handling
+ */
+
+void
+pxaudc_read_ep0(struct pxaudc_softc *sc, usbf_xfer_handle xfer)
+{
+ size_t len;
+ u_int8_t *p;
+
+ xfer->actlen = CSR_READ_4(sc, USBDC_UDCBCR(0));
+ len = MIN(xfer->actlen, xfer->length);
+ p = xfer->buffer;
+
+ while (CSR_READ_4(sc, USBDC_UDCCSR0) & USBDC_UDCCSR0_RNE) {
+ u_int32_t v = CSR_READ_4(sc, USBDC_UDCDR(0));
+
+ if (len > 0) {
+ if (((unsigned)p & 0x3) == 0)
+ *(u_int32_t *)p = v;
+ else {
+ *(p+0) = v & 0xff;
+ *(p+1) = (v >> 8) & 0xff;
+ *(p+2) = (v >> 16) & 0xff;
+ *(p+3) = (v >> 24) & 0xff;
+ }
+ p += 4;
+ len -= 4;
+ }
+ }
+
+ CSR_WRITE_4(sc, USBDC_UDCCSR0, USBDC_UDCCSR0_SA | USBDC_UDCCSR0_OPC);
+
+ xfer->status = USBF_NORMAL_COMPLETION;
+ usbf_transfer_complete(xfer);
+}
+
+void
+pxaudc_write_ep0(struct pxaudc_softc *sc, usbf_xfer_handle xfer)
+{
+ struct pxaudc_xfer *lxfer = (struct pxaudc_xfer *)xfer;
+ u_int32_t len;
+ u_int8_t *p;
+
+ if (lxfer->frmlen > 0) {
+ xfer->actlen += lxfer->frmlen;
+ lxfer->frmlen = 0;
+ }
+
+ DPRINTF(1,("%s: ep0 ctrl-in, xfer=%p, len=%u, actlen=%u\n",
+ DEVNAME(sc), xfer, xfer->length, xfer->actlen));
+
+ if (xfer->actlen >= xfer->length) {
+ sc->sc_ep0state = EP0_SETUP;
+ usbf_transfer_complete(xfer);
+ return;
+ }
+
+ sc->sc_ep0state = EP0_IN;
+
+ p = (u_char *)xfer->buffer + xfer->actlen;
+ len = xfer->length - xfer->actlen;
+ len = MIN(len, PXAUDC_EP0MAXP);
+ lxfer->frmlen = len;
+
+ while (len >= 4) {
+ u_int32_t v;
+
+ if (((unsigned)p & 0x3) == 0)
+ v = *(u_int32_t *)p;
+ else {
+ v = *(p+0);
+ v |= *(p+1) << 8;
+ v |= *(p+2) << 16;
+ v |= *(p+3) << 24;
+ }
+
+ CSR_WRITE_4(sc, USBDC_UDCDR(0), v);
+ len -= 4;
+ p += 4;
+ }
+
+ while (len > 0) {
+ CSR_WRITE_1(sc, USBDC_UDCDR(0), *p);
+ len--;
+ p++;
+ }
+
+ /* (12.6.7) Set IPR only for short packets. */
+ if (lxfer->frmlen < PXAUDC_EP0MAXP)
+ CSR_SET_4(sc, USBDC_UDCCSR0, USBDC_UDCCSR0_IPR);
+}
+
+void
+pxaudc_write(struct pxaudc_softc *sc, usbf_xfer_handle xfer)
+{
+ printf("pxaudc_write: XXX\n");
+}
+
+/*
+ * Interrupt handling
+ */
+
+int
+pxaudc_connect_intr(void *v)
+{
+ struct pxaudc_softc *sc = v;
+
+ DPRINTF(0,("pxaudc_connect_intr: connect=%d device=%d\n",
+ pxa2x0_gpio_get_bit(C3000_USB_CONNECT_PIN),
+ pxa2x0_gpio_get_bit(C3000_USB_DEVICE_PIN)));
+
+ /* XXX only set a flag here */
+ if (pxaudc_is_host()) {
+ pxaudc_disable(sc);
+ } else {
+ pxaudc_enable(sc);
+ }
+
+ /* Claim this interrupt. */
+ return 1;
+}
+
+int
+pxaudc_intr(void *v)
+{
+ struct pxaudc_softc *sc = v;
+ u_int32_t isr0, isr1, otgisr;
+
+ isr0 = CSR_READ_4(sc, USBDC_UDCISR0);
+ isr1 = CSR_READ_4(sc, USBDC_UDCISR1);
+ otgisr = CSR_READ_4(sc, USBDC_UDCOTGISR);
+
+ DPRINTF(1,("pxaudc_intr: isr0=%b, isr1=%b, otgisr=%b\n",
+ isr0, USBDC_UDCISR0_BITS, isr1, USBDC_UDCISR1_BITS,
+ otgisr, USBDC_UDCOTGISR_BITS));
+
+ if (isr0 || isr1 || otgisr) {
+ sc->sc_isr0 |= isr0;
+ sc->sc_isr1 |= isr1;
+ sc->sc_otgisr |= otgisr;
+
+ //usbf_schedsoftintr(&sc->sc_bus);
+ pxaudc_intr1(sc); /* XXX */
+ }
+
+ CSR_WRITE_4(sc, USBDC_UDCISR0, isr0);
+ CSR_WRITE_4(sc, USBDC_UDCISR1, isr1);
+ CSR_WRITE_4(sc, USBDC_UDCOTGISR, otgisr);
+
+ /* Claim this interrupt. */
+ return 1;
+}
+
+void
+pxaudc_intr1(struct pxaudc_softc *sc)
+{
+ u_int32_t isr0, isr1, otgisr;
+ //int s;
+
+ //s = splhardusb();
+ isr0 = sc->sc_isr0;
+ isr1 = sc->sc_isr1;
+ otgisr = sc->sc_otgisr;
+ sc->sc_isr0 = 0;
+ sc->sc_isr1 = 0;
+ sc->sc_otgisr = 0;
+ //splx(s);
+
+ sc->sc_bus.intr_context++;
+
+ /* Handle USB RESET condition. */
+ if (isr1 & USBDC_UDCISR1_IRRS) {
+ sc->sc_ep0state = EP0_SETUP;
+ usbf_host_reset(&sc->sc_bus);
+ /* Discard all other interrupts. */
+ goto ret;
+ }
+
+ /* Service control pipe interrupts. */
+ if (isr0 & USBDC_UDCISR0_IR(0))
+ pxaudc_ep0_intr(sc);
+
+ret:
+ sc->sc_bus.intr_context--;
+}
+
+void
+pxaudc_ep0_intr(struct pxaudc_softc *sc)
+{
+ struct pxaudc_pipe *ppipe;
+ usbf_pipe_handle pipe = NULL;
+ usbf_xfer_handle xfer = NULL;
+ u_int32_t csr0;
+
+ csr0 = CSR_READ_4(sc, USBDC_UDCCSR0);
+ DPRINTF(1,("pxaudc_ep0_intr: csr0=%b\n", csr0, USBDC_UDCCSR0_BITS));
+
+ ppipe = sc->sc_pipe[0];
+ if (ppipe != NULL) {
+ pipe = &ppipe->pipe;
+ xfer = SIMPLEQ_FIRST(&pipe->queue);
+ }
+
+ if (sc->sc_ep0state == EP0_SETUP && (csr0 & USBDC_UDCCSR0_OPC)) {
+ if (pipe == NULL) {
+ DPRINTF(0,("pxaudc_ep0_intr: no control pipe\n"));
+ return;
+ }
+
+ if (xfer == NULL) {
+ DPRINTF(0,("pxaudc_ep0_intr: no xfer\n"));
+ return;
+ }
+
+ pxaudc_read_ep0(sc, xfer);
+ } else if (sc->sc_ep0state == EP0_IN &&
+ (csr0 & USBDC_UDCCSR0_IPR) == 0 && xfer) {
+ pxaudc_write_ep0(sc, xfer);
+ }
+}
+
+/*
+ * Bus methods
+ */
+
+usbf_status
+pxaudc_open(struct usbf_pipe *pipe)
+{
+ struct pxaudc_softc *sc = (struct pxaudc_softc *)pipe->device->bus;
+ struct pxaudc_pipe *ppipe = (struct pxaudc_pipe *)pipe;
+ int s;
+
+ if (usbf_endpoint_index(pipe->endpoint) >= PXAUDC_NEP)
+ return USBF_BAD_ADDRESS;
+
+ DPRINTF(1,("pxaudc_open\n"));
+ s = splhardusb();
+
+ switch (usbf_endpoint_type(pipe->endpoint)) {
+ case UE_CONTROL:
+ pipe->methods = &pxaudc_ctrl_methods;
+ break;
+
+ case UE_BULK:
+ pipe->methods = &pxaudc_bulk_methods;
+ break;
+
+ case UE_ISOCHRONOUS:
+ case UE_INTERRUPT:
+ default:
+ /* XXX */
+ splx(s);
+ return USBF_BAD_ADDRESS;
+ }
+
+ sc->sc_pipe[usbf_endpoint_index(pipe->endpoint)] = ppipe;
+
+ splx(s);
+ return USBF_NORMAL_COMPLETION;
+}
+
+void
+pxaudc_softintr(void *v)
+{
+ struct pxaudc_softc *sc = v;
+
+ pxaudc_intr1(sc);
+}
+
+usbf_status
+pxaudc_allocm(struct usbf_bus *bus, usb_dma_t *dmap, u_int32_t size)
+{
+ return usbf_allocmem(bus, size, 0, dmap);
+}
+
+void
+pxaudc_freem(struct usbf_bus *bus, usb_dma_t *dmap)
+{
+ usbf_freemem(bus, dmap);
+}
+
+usbf_xfer_handle
+pxaudc_allocx(struct usbf_bus *bus)
+{
+ struct pxaudc_softc *sc = (struct pxaudc_softc *)bus;
+ usbf_xfer_handle xfer;
+
+ xfer = SIMPLEQ_FIRST(&sc->sc_free_xfers);
+ if (xfer != NULL)
+ SIMPLEQ_REMOVE_HEAD(&sc->sc_free_xfers, next);
+ else
+ xfer = malloc(sizeof(struct pxaudc_xfer), M_USB, M_NOWAIT);
+ if (xfer != NULL)
+ bzero(xfer, sizeof(struct pxaudc_xfer));
+ return xfer;
+}
+
+void
+pxaudc_freex(struct usbf_bus *bus, usbf_xfer_handle xfer)
+{
+ struct pxaudc_softc *sc = (struct pxaudc_softc *)bus;
+
+ SIMPLEQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next);
+}
+
+/*
+ * Control pipe methods
+ */
+
+usbf_status
+pxaudc_ctrl_transfer(usbf_xfer_handle xfer)
+{
+ usbf_status err;
+
+ /* Insert last in queue. */
+ err = usbf_insert_transfer(xfer);
+ if (err)
+ return err;
+
+ /*
+ * Pipe isn't running (otherwise err would be USBF_IN_PROGRESS),
+ * so start first.
+ */
+ return pxaudc_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue));
+}
+
+usbf_status
+pxaudc_ctrl_start(usbf_xfer_handle xfer)
+{
+ struct usbf_pipe *pipe = xfer->pipe;
+ struct pxaudc_softc *sc = (struct pxaudc_softc *)pipe->device->bus;
+ int iswrite = !(xfer->rqflags & URQ_REQUEST);
+ int s;
+
+ s = splusb();
+ xfer->status = USBF_IN_PROGRESS;
+ if (iswrite)
+ pxaudc_write_ep0(sc, xfer);
+ else {
+ /* XXX boring message, this case is normally reached if
+ * XXX the xfer for a device request is being queued. */
+ DPRINTF(0,("%s: ep0 ctrl-out, xfer=%p, len=%u, "
+ "actlen=%u\n", DEVNAME(sc), xfer, xfer->length,
+ xfer->actlen));
+ }
+ splx(s);
+ return USBF_IN_PROGRESS;
+}
+
+/* (also used by bulk pipes) */
+void
+pxaudc_ctrl_abort(usbf_xfer_handle xfer)
+{
+ struct usbf_pipe *pipe = xfer->pipe;
+ struct pxaudc_softc *sc = (struct pxaudc_softc *)pipe->device->bus;
+ int s;
+#ifdef PXAUDC_DEBUG
+ int index = usbf_endpoint_index(pipe->endpoint);
+ int dir = usbf_endpoint_dir(pipe->endpoint);
+ int type = usbf_endpoint_type(pipe->endpoint);
+#endif
+
+ DPRINTF(0,("%s: ep%d %s-%s abort, xfer=%p\n", DEVNAME(sc), index,
+ type == UE_CONTROL ? "ctrl" : "bulk", dir == UE_DIR_IN ?
+ "in" : "out", xfer));
+
+ /*
+ * Step 1: Make soft interrupt routine and hardware ignore the xfer.
+ */
+ s = splusb();
+ xfer->status = USBF_CANCELLED;
+ usb_uncallout(xfer->timeout_handle, pxaudc_timeout, NULL);
+ splx(s);
+
+ /*
+ * Step 2: Make sure hardware has finished any possible use of the
+ * xfer and the soft interrupt routine has run.
+ */
+ s = splusb();
+ /* XXX this does not seem right, what if there
+ * XXX are two xfers in the FIFO and we only want to
+ * XXX ignore one? */
+#ifdef notyet
+ pxaudc_flush(sc, usbf_endpoint_address(pipe->endpoint));
+#endif
+ /* XXX we're not doing DMA and the soft interrupt routine does not
+ XXX need to clean up anything. */
+ splx(s);
+
+ /*
+ * Step 3: Execute callback.
+ */
+ s = splusb();
+ usbf_transfer_complete(xfer);
+ splx(s);
+}
+
+void
+pxaudc_ctrl_done(usbf_xfer_handle xfer)
+{
+}
+
+void
+pxaudc_ctrl_close(usbf_pipe_handle pipe)
+{
+ /* XXX */
+}
+
+/*
+ * Bulk pipe methods
+ */
+
+usbf_status
+pxaudc_bulk_transfer(usbf_xfer_handle xfer)
+{
+ usbf_status err;
+
+ /* Insert last in queue. */
+ err = usbf_insert_transfer(xfer);
+ if (err)
+ return err;
+
+ /*
+ * Pipe isn't running (otherwise err would be USBF_IN_PROGRESS),
+ * so start first.
+ */
+ return pxaudc_bulk_start(SIMPLEQ_FIRST(&xfer->pipe->queue));
+}
+
+usbf_status
+pxaudc_bulk_start(usbf_xfer_handle xfer)
+{
+ struct usbf_pipe *pipe = xfer->pipe;
+ struct pxaudc_softc *sc = (struct pxaudc_softc *)pipe->device->bus;
+ int iswrite = usbf_endpoint_dir(pipe->endpoint) == UE_DIR_IN;
+ int s;
+
+ DPRINTF(1,("%s: ep%d bulk-%s start, xfer=%p, len=%u\n", DEVNAME(sc),
+ usbf_endpoint_index(pipe->endpoint), iswrite ? "in" : "out",
+ xfer, xfer->length));
+
+ s = splusb();
+ xfer->status = USBF_IN_PROGRESS;
+ if (iswrite)
+ pxaudc_write(sc, xfer);
+ splx(s);
+ return USBF_IN_PROGRESS;
+}
+
+void
+pxaudc_bulk_abort(usbf_xfer_handle xfer)
+{
+ pxaudc_ctrl_abort(xfer);
+}
+
+void
+pxaudc_bulk_done(usbf_xfer_handle xfer)
+{
+}
+
+void
+pxaudc_bulk_close(usbf_pipe_handle pipe)
+{
+ /* XXX */
}
diff --git a/sys/arch/arm/xscale/pxa27x_udcreg.h b/sys/arch/arm/xscale/pxa27x_udcreg.h
index f54a0e42296..b9e2e4ad76b 100644
--- a/sys/arch/arm/xscale/pxa27x_udcreg.h
+++ b/sys/arch/arm/xscale/pxa27x_udcreg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: pxa27x_udcreg.h,v 1.1 2005/02/17 22:10:35 dlg Exp $ */
+/* $OpenBSD: pxa27x_udcreg.h,v 1.2 2006/11/25 18:10:29 uwe Exp $ */
/*
* Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
@@ -149,4 +149,23 @@
#define USBDC_UDCECR_IN (7<<22) /* Interface Number */
#define USBDC_UDCECR_CN (3<<25) /* Configuration Number */
+#define USBDC_UDCCR_BITS \
+ "\20\001UDE\002UDA\003UDR\004EMCE\005SMAC\021DWRE" \
+ "\035BHNP\036AHNP\037OEN"
+#define USBDC_UDCISR0_BITS \
+ "\20\0010P\0020F\003AP\004AF\005BP\006BF\007CP\010CF" \
+ "\011DP\012DF\013EP\014EF\015FP\016FF\017GP\020GF" \
+ "\031HP\032HF\033IP\034IF\035JP\036JF\037KP\030KF" \
+ "\041LP\042LF\043MP\044MF\045NP\046NF\047PP\040PF"
+#define USBDC_UDCISR1_BITS \
+ "\20\001QP\002QF\003RP\004RF\005SP\006SF\007TP\010TF" \
+ "\011UP\012UF\013VP\014VF\015WP\016WF\017XP\020XF" \
+ "\034RS\035SU\036RU\037SOF\040CC"
+#define USBDC_UDCOTGISR_BITS \
+ "\20\001IRIDF\002IRIDR\003IRSDF\004IRSDR\005IRSVF\006IRSVR" \
+ "\007IRVV44F\010IRVV44R\011IRVV40F\012IRVV40R"
+#define USBDC_UDCCSR0_BITS \
+ "\20\001OPC\002IPR\003FTF\004DME\005SST\006FST\007RNE" \
+ "\010SA\011AREN\012ACM"
+
#endif /* _ARM_XSCALE_PXA27X_UDCREG_H_ */
diff --git a/sys/arch/zaurus/conf/GENERIC b/sys/arch/zaurus/conf/GENERIC
index d182a1c1d8c..494e4435b48 100644
--- a/sys/arch/zaurus/conf/GENERIC
+++ b/sys/arch/zaurus/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.55 2006/11/25 14:31:59 uwe Exp $
+# $OpenBSD: GENERIC,v 1.56 2006/11/25 18:10:29 uwe Exp $
#
# For further information on compiling OpenBSD kernels, see the config(8)
# man page.
@@ -64,6 +64,10 @@ wskbd* at zkbd? mux 1
pxaudc0 at pxaip? # USB Device Controller
ohci0 at pxaip? # Open Host Controller
+# USB function support
+#usbf* at pxaudc? # USB logical device
+#cdcef* at usbf? # CDC ethernet function
+
# USB bus support
usb* at ohci?
uhub* at usb? # USB Root Hub
diff --git a/sys/arch/zaurus/include/zaurus_reg.h b/sys/arch/zaurus/include/zaurus_reg.h
index 27c42c5c562..fd4e40eb3ac 100644
--- a/sys/arch/zaurus/include/zaurus_reg.h
+++ b/sys/arch/zaurus/include/zaurus_reg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: zaurus_reg.h,v 1.7 2005/12/14 14:39:38 uwe Exp $ */
+/* $OpenBSD: zaurus_reg.h,v 1.8 2006/11/25 18:10:29 uwe Exp $ */
/* $NetBSD: lubbock_reg.h,v 1.1 2003/06/18 10:51:15 bsh Exp $ */
/*
@@ -75,5 +75,9 @@
#define C3000_RC_IRQ_PIN 13 /* remote control */
#define C3000_CF0_IRQ_PIN 94
#define C3000_CF1_IRQ_PIN 93
+#define C3000_USB_DEVICE_PIN 35 /* indicate connection type */
+#define C3000_USB_CONNECT_PIN 41 /* connection interrupt */
+#define C3000_USB_PULLUP_PIN 45 /* show/hide device presence */
+#define GPIO_HP_IN_C3000 116 /* headphone jack */
#endif /* _ZAURUS_REG_H */
diff --git a/sys/conf/files b/sys/conf/files
index 8367c2dff7e..8352ee615c1 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1,4 +1,4 @@
-# $OpenBSD: files,v 1.389 2006/11/25 14:31:59 uwe Exp $
+# $OpenBSD: files,v 1.390 2006/11/25 18:10:29 uwe Exp $
# $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@@ -394,6 +394,7 @@ define eisabus {} # EISA attachment
define pcibus {[bus = -1]} # PCI attachment
define tcbus {} # TurboChannel attachment
define usbus {} # USB attachment
+define usbdev {} # USB function attachment
define pcmciabus {[controller = -1], [socket = -1]} # PCMCIA attachment
define cbbus {[slot = -1]} # CardBus attachment
define pcmciaslot {[slot = -1]} # PCMCIA slot itself
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 *);