From 2e4157ef1ec170ce50da1785c962e070497ac2ff Mon Sep 17 00:00:00 2001 From: Sascha Wildner Date: Sat, 24 Nov 2012 21:22:55 +0100 Subject: [PATCH] ndis(4): Make NDIS compile with usb4bsd too. However, testing results are pretty much as modest as with the old USB stack. Maybe someone else has more luck. New files taken from FreeBSD. --- sys/dev/netif/ndis/Makefile | 12 +- sys/dev/netif/ndis/if_ndis.c | 35 + sys/dev/netif/ndis/if_ndis_pccard.c | 7 + sys/dev/netif/ndis/if_ndis_pci.c | 7 + sys/dev/netif/ndis/if_ndis_u4b.c | 232 ++++++ sys/dev/netif/ndis/if_ndisvar.h | 72 ++ sys/emulation/ndis/Makefile | 12 +- sys/emulation/ndis/kern_ndis.c | 11 + sys/emulation/ndis/kern_windrv.c | 11 + sys/emulation/ndis/ntoskrnl_var.h | 10 + sys/emulation/ndis/subr_ndis.c | 6 + sys/emulation/ndis/subr_u4bd.c | 1454 +++++++++++++++++++++++++++++++++++ sys/emulation/ndis/u4bd_var.h | 222 ++++++ sys/platform/pc32/conf/files | 2 + sys/platform/pc64/conf/files | 2 + 15 files changed, 2091 insertions(+), 4 deletions(-) create mode 100644 sys/dev/netif/ndis/if_ndis_u4b.c create mode 100644 sys/emulation/ndis/subr_u4bd.c create mode 100644 sys/emulation/ndis/u4bd_var.h diff --git a/sys/dev/netif/ndis/Makefile b/sys/dev/netif/ndis/Makefile index 5367a0df12..b30cdb43a7 100644 --- a/sys/dev/netif/ndis/Makefile +++ b/sys/dev/netif/ndis/Makefile @@ -3,9 +3,17 @@ KMOD= if_ndis SRCS= if_ndis.c if_ndis_pci.c if_ndis_pccard.c SRCS+= device_if.h bus_if.h pci_if.h card_if.h +SRCS+= opt_usb.h use_usb4bsd.h -.if !defined(WANT_USB4BSD) -SRCS+= if_ndis_usb.c opt_usb.h +.if defined(WANT_USB4BSD) +SRCS+= if_ndis_u4b.c +.else +SRCS+= if_ndis_usb.c +.endif + +.if !defined(BUILDING_WITH_KERNEL) && defined(WANT_USB4BSD) +use_usb4bsd.h: + echo "#define NUSB4BSD 1" > ${.TARGET} .endif .include diff --git a/sys/dev/netif/ndis/if_ndis.c b/sys/dev/netif/ndis/if_ndis.c index ff0eb44ee6..7c8e4f4cea 100644 --- a/sys/dev/netif/ndis/if_ndis.c +++ b/sys/dev/netif/ndis/if_ndis.c @@ -35,6 +35,8 @@ * then hacked upon mercilessly by me. */ +#include "use_usb4bsd.h" + #include #include #include @@ -71,8 +73,13 @@ #include #include +#if NUSB4BSD > 0 +#include +#include +#else #include #include +#endif #include #include @@ -80,7 +87,11 @@ #include #include #include +#if NUSB4BSD > 0 +#include +#else #include +#endif #include #define NDIS_DEBUG @@ -549,9 +560,19 @@ ndis_attach(device_t dev) lockinit(&sc->ndis_lock, "network driver", 0, LK_CANRECURSE); KeInitializeSpinLock(&sc->ndis_rxlock); +#if NUSB4BSD > 0 + KeInitializeSpinLock(&sc->ndisusb_tasklock); + KeInitializeSpinLock(&sc->ndisusb_xferdonelock); +#else KeInitializeSpinLock(&sc->ndisusb_xferlock); +#endif InitializeListHead(&sc->ndis_shlist); +#if NUSB4BSD > 0 + InitializeListHead(&sc->ndisusb_tasklist); + InitializeListHead(&sc->ndisusb_xferdonelist); +#else InitializeListHead(&sc->ndisusb_xferlist); +#endif callout_init_mp(&sc->ndis_stat_callout); if (sc->ndis_iftype == PCMCIABus) { @@ -615,7 +636,14 @@ ndis_attach(device_t dev) sc->ndis_startitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); sc->ndis_resetitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); sc->ndis_inputitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); +#if NUSB4BSD > 0 + sc->ndisusb_xferdoneitem = + IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); + sc->ndisusb_taskitem = + IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); +#else sc->ndisusb_xferitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); +#endif KeInitializeDpc(&sc->ndis_rxdpc, ndis_rxeof_xfr_wrap, sc->ndis_block); /* Call driver's init routine. */ @@ -1051,8 +1079,15 @@ ndis_detach(device_t dev) IoFreeWorkItem(sc->ndis_resetitem); if (sc->ndis_inputitem != NULL) IoFreeWorkItem(sc->ndis_inputitem); +#if NUSB4BSD > 0 + if (sc->ndisusb_xferdoneitem != NULL) + IoFreeWorkItem(sc->ndisusb_xferdoneitem); + if (sc->ndisusb_taskitem != NULL) + IoFreeWorkItem(sc->ndisusb_taskitem); +#else if (sc->ndisusb_xferitem != NULL) IoFreeWorkItem(sc->ndisusb_xferitem); +#endif bus_generic_detach(dev); ndis_unload_driver(sc); diff --git a/sys/dev/netif/ndis/if_ndis_pccard.c b/sys/dev/netif/ndis/if_ndis_pccard.c index cd8a0d9b2b..7680e2ce21 100644 --- a/sys/dev/netif/ndis/if_ndis_pccard.c +++ b/sys/dev/netif/ndis/if_ndis_pccard.c @@ -32,6 +32,8 @@ * $FreeBSD: src/sys/dev/if_ndis/if_ndis_pccard.c,v 1.19 2010/12/16 15:19:32 jhb Exp $ */ +#include "use_usb4bsd.h" + #include #include #include @@ -51,8 +53,13 @@ #include +#if NUSB4BSD > 0 +#include +#include +#else #include #include +#endif #include #include diff --git a/sys/dev/netif/ndis/if_ndis_pci.c b/sys/dev/netif/ndis/if_ndis_pci.c index ea6f84608e..737e060a5b 100644 --- a/sys/dev/netif/ndis/if_ndis_pci.c +++ b/sys/dev/netif/ndis/if_ndis_pci.c @@ -32,6 +32,8 @@ * $FreeBSD: src/sys/dev/if_ndis/if_ndis_pci.c,v 1.26 2010/12/19 11:14:34 tijl Exp $ */ +#include "use_usb4bsd.h" + #include #include #include @@ -52,8 +54,13 @@ #include #include +#if NUSB4BSD > 0 +#include +#include +#else #include #include +#endif #include #include diff --git a/sys/dev/netif/ndis/if_ndis_u4b.c b/sys/dev/netif/ndis/if_ndis_u4b.c new file mode 100644 index 0000000000..b9c4845ebd --- /dev/null +++ b/sys/dev/netif/ndis/if_ndis_u4b.c @@ -0,0 +1,232 @@ +/*- + * Copyright (c) 2005 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/if_ndis/if_ndis_usb.c,v 1.26 2012/11/17 01:51:54 svnexp Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +SYSCTL_NODE(_hw, OID_AUTO, ndisusb, CTLFLAG_RD, 0, "NDIS USB driver parameters"); + +MODULE_DEPEND(ndis, usb, 1, 1, 1); + +static device_probe_t ndisusb_match; +static device_attach_t ndisusb_attach; +static device_detach_t ndisusb_detach; +static bus_get_resource_list_t ndis_get_resource_list; + +extern int ndisdrv_modevent (module_t, int, void *); +extern int ndis_attach (device_t); +extern int ndis_shutdown (device_t); +extern int ndis_detach (device_t); +extern int ndis_suspend (device_t); +extern int ndis_resume (device_t); + +extern unsigned char drv_data[]; + +static device_method_t ndis_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ndisusb_match), + DEVMETHOD(device_attach, ndisusb_attach), + DEVMETHOD(device_detach, ndisusb_detach), + DEVMETHOD(device_shutdown, ndis_shutdown), + + /* bus interface */ + DEVMETHOD(bus_get_resource_list, ndis_get_resource_list), + + { 0, 0 } +}; + +static driver_t ndis_driver = { + "ndis", + ndis_methods, + sizeof(struct ndis_softc) +}; + +static devclass_t ndis_devclass; + +DRIVER_MODULE(ndis, uhub, ndis_driver, ndis_devclass, ndisdrv_modevent, 0); + +static int +ndisusb_devcompare(interface_type bustype, struct ndis_usb_type *t, device_t dev) +{ + struct usb_attach_arg *uaa; + + if (bustype != PNPBus) + return (FALSE); + + uaa = device_get_ivars(dev); + + while (t->ndis_name != NULL) { + if ((uaa->info.idVendor == t->ndis_vid) && + (uaa->info.idProduct == t->ndis_did)) { + device_set_desc(dev, t->ndis_name); + return (TRUE); + } + t++; + } + + return (FALSE); +} + +static int +ndisusb_match(device_t self) +{ + struct drvdb_ent *db; + struct usb_attach_arg *uaa = device_get_ivars(self); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != NDISUSB_CONFIG_NO) + return (ENXIO); + if (uaa->info.bIfaceIndex != NDISUSB_IFACE_INDEX) + return (ENXIO); + + if (windrv_lookup(0, "USB Bus") == NULL) + return (ENXIO); + + db = windrv_match((matchfuncptr)ndisusb_devcompare, self); + if (db == NULL) + return (ENXIO); + uaa->driver_ivar = db; + + return (0); +} + +static int +ndisusb_attach(device_t self) +{ + const struct drvdb_ent *db; + struct ndisusb_softc *dummy = device_get_softc(self); + struct usb_attach_arg *uaa = device_get_ivars(self); + struct ndis_softc *sc; + struct ndis_usb_type *t; + driver_object *drv; + int devidx = 0; + + device_set_usb_desc(self); + db = uaa->driver_ivar; + sc = (struct ndis_softc *)dummy; + sc->ndis_dev = self; + lockinit(&sc->ndisusb_lock, "NDIS USB", 0, LK_CANRECURSE); + sc->ndis_dobj = db->windrv_object; + sc->ndis_regvals = db->windrv_regvals; + sc->ndis_iftype = PNPBus; + sc->ndisusb_dev = uaa->device; + + /* Create PDO for this device instance */ + + drv = windrv_lookup(0, "USB Bus"); + windrv_create_pdo(drv, self); + + /* Figure out exactly which device we matched. */ + + t = db->windrv_devlist; + + while (t->ndis_name != NULL) { + if ((uaa->info.idVendor == t->ndis_vid) && + (uaa->info.idProduct == t->ndis_did)) { + sc->ndis_devidx = devidx; + break; + } + t++; + devidx++; + } + + if (ndis_attach(self) != 0) + return (ENXIO); + + return (0); +} + +static int +ndisusb_detach(device_t self) +{ + int i; + struct ndis_softc *sc = device_get_softc(self); + struct ndisusb_ep *ne; + + sc->ndisusb_status |= NDISUSB_STATUS_DETACH; + + ndis_pnpevent_nic(self, NDIS_PNP_EVENT_SURPRISE_REMOVED); + + if (sc->ndisusb_status & NDISUSB_STATUS_SETUP_EP) { + usbd_transfer_unsetup(sc->ndisusb_dread_ep.ne_xfer, 1); + usbd_transfer_unsetup(sc->ndisusb_dwrite_ep.ne_xfer, 1); + } + for (i = 0; i < NDISUSB_ENDPT_MAX; i++) { + ne = &sc->ndisusb_ep[i]; + usbd_transfer_unsetup(ne->ne_xfer, 1); + } + + (void)ndis_detach(self); + + lockuninit(&sc->ndisusb_lock); + return (0); +} + +static struct resource_list * +ndis_get_resource_list(device_t dev, device_t child) +{ + struct ndis_softc *sc; + + sc = device_get_softc(dev); + return (BUS_GET_RESOURCE_LIST(device_get_parent(sc->ndis_dev), dev)); +} diff --git a/sys/dev/netif/ndis/if_ndisvar.h b/sys/dev/netif/ndis/if_ndisvar.h index e9f26e73bc..b1a55c1722 100644 --- a/sys/dev/netif/ndis/if_ndisvar.h +++ b/sys/dev/netif/ndis/if_ndisvar.h @@ -32,6 +32,8 @@ * $FreeBSD: src/sys/dev/if_ndis/if_ndisvar.h,v 1.39 2009/05/02 15:14:18 thompsa Exp $ */ +#include "use_usb4bsd.h" + #define NDIS_DEFAULT_NODENAME "FreeBSD NDIS node" #define NDIS_NODENAME_LEN 32 @@ -113,6 +115,45 @@ struct ndis_vap { }; #define NDIS_VAP(vap) ((struct ndis_vap *)(vap)) +#if NUSB4BSD > 0 +#define NDISUSB_CONFIG_NO 0 +#define NDISUSB_IFACE_INDEX 0 +/* XXX at USB2 there's no USBD_NO_TIMEOUT macro anymore */ +#define NDISUSB_NO_TIMEOUT 0 +#define NDISUSB_INTR_TIMEOUT 1000 +#define NDISUSB_TX_TIMEOUT 10000 +struct ndisusb_xfer; +struct ndisusb_ep { + struct usb_xfer *ne_xfer[1]; + list_entry ne_active; + list_entry ne_pending; + kspin_lock ne_lock; + uint8_t ne_dirin; +}; +struct ndisusb_xfer { + struct ndisusb_ep *nx_ep; + void *nx_priv; + uint8_t *nx_urbbuf; + uint32_t nx_urbactlen; + uint32_t nx_urblen; + uint8_t nx_shortxfer; + list_entry nx_next; +}; +struct ndisusb_xferdone { + struct ndisusb_xfer *nd_xfer; + usb_error_t nd_status; + list_entry nd_donelist; +}; + +struct ndisusb_task { + unsigned nt_type; +#define NDISUSB_TASK_TSTART 0 +#define NDISUSB_TASK_IRPCANCEL 1 +#define NDISUSB_TASK_VENDOR 2 + void *nt_ctx; + list_entry nt_tasklist; +}; +#else /* !NUSB4BSD > 0 */ #define NDISUSB_CONFIG_NO 1 #define NDISUSB_IFACE_INDEX 0 #define NDISUSB_INTR_TIMEOUT 1000 @@ -123,6 +164,7 @@ struct ndisusb_xfer { usbd_status nx_status; list_entry nx_xferlist; }; +#endif struct ndis_softc { struct ifnet *ifp; @@ -202,6 +244,35 @@ struct ndis_softc { int ndis_tx_timer; int ndis_hang_timer; +#if NUSB4BSD > 0 + struct usb_device *ndisusb_dev; + struct lock ndisusb_lock; + struct ndisusb_ep ndisusb_dread_ep; + struct ndisusb_ep ndisusb_dwrite_ep; +#define NDISUSB_GET_ENDPT(addr) \ + ((UE_GET_DIR(addr) >> 7) | (UE_GET_ADDR(addr) << 1)) +#define NDISUSB_ENDPT_MAX ((UE_ADDR + 1) * 2) + struct ndisusb_ep ndisusb_ep[NDISUSB_ENDPT_MAX]; + io_workitem *ndisusb_xferdoneitem; + list_entry ndisusb_xferdonelist; + kspin_lock ndisusb_xferdonelock; + io_workitem *ndisusb_taskitem; + list_entry ndisusb_tasklist; + kspin_lock ndisusb_tasklock; + int ndisusb_status; +#define NDISUSB_STATUS_DETACH 0x1 +#define NDISUSB_STATUS_SETUP_EP 0x2 +}; + +#define NDIS_LOCK(_sc) lockmgr(&(_sc)->ndis_lock, LK_EXCLUSIVE) +#define NDIS_UNLOCK(_sc) lockmgr(&(_sc)->ndis_lock, LK_RELEASE) +#define NDISMTX_LOCK NDIS_LOCK +#define NDISMTX_UNLOCK NDIS_UNLOCK +#define NDIS_LOCK_ASSERT(_sc, t) KKASSERT(lockstatus(&(_sc)->ndis_lock, curthread) != 0) +#define NDISUSB_LOCK(_sc) lockmgr(&(_sc)->ndisusb_lock, LK_EXCLUSIVE) +#define NDISUSB_UNLOCK(_sc) lockmgr(&(_sc)->ndisusb_lock, LK_RELEASE) +#define NDISUSB_LOCK_ASSERT(_sc, t) KKASSERT(lockstatus(&(_sc)->ndisusb_lock, curthread) != 0) +#else /* !NUSB4BSD > 0 */ io_workitem *ndisusb_xferitem; list_entry ndisusb_xferlist; kspin_lock ndisusb_xferlock; @@ -230,3 +301,4 @@ struct ndis_softc { NDISUSB_UNLOCK(_sc); \ NDISMTX_UNLOCK(_sc); \ } while (0) +#endif diff --git a/sys/emulation/ndis/Makefile b/sys/emulation/ndis/Makefile index 831355af16..9e717f0104 100644 --- a/sys/emulation/ndis/Makefile +++ b/sys/emulation/ndis/Makefile @@ -4,6 +4,7 @@ KMOD= ndis SRCS= subr_pe.c subr_ndis.c subr_hal.c subr_ntoskrnl.c kern_ndis.c SRCS+= kern_windrv.c SRCS+= device_if.h bus_if.h pci_if.h +SRCS+= opt_usb.h use_usb4bsd.h .if ${MACHINE_ARCH} == "x86_64" SRCS+= winx64_wrap.S @@ -13,8 +14,15 @@ SRCS+= winx64_wrap.S SRCS+= winx32_wrap.S .endif -.if !defined(WANT_USB4BSD) -SRCS+= subr_usbd.c opt_usb.h +.if defined(WANT_USB4BSD) +SRCS+= subr_u4bd.c +.else +SRCS+= subr_usbd.c +.endif + +.if !defined(BUILDING_WITH_KERNEL) && defined(WANT_USB4BSD) +use_usb4bsd.h: + echo "#define NUSB4BSD 1" > ${.TARGET} .endif .include diff --git a/sys/emulation/ndis/kern_ndis.c b/sys/emulation/ndis/kern_ndis.c index ef5ae1e0de..b731e39d42 100644 --- a/sys/emulation/ndis/kern_ndis.c +++ b/sys/emulation/ndis/kern_ndis.c @@ -32,6 +32,8 @@ * $FreeBSD: src/sys/compat/ndis/kern_ndis.c,v 1.111 2011/02/23 21:45:28 brucec Exp $ */ +#include "use_usb4bsd.h" + #include #include #include @@ -63,8 +65,13 @@ #include #include +#if NUSB4BSD > 0 +#include +#include +#else #include #include +#endif #include #include @@ -72,7 +79,11 @@ #include #include #include +#if NUSB4BSD > 0 +#include +#else #include +#endif #include #define NDIS_DUMMY_PATH "\\\\some\\bogus\\path" diff --git a/sys/emulation/ndis/kern_windrv.c b/sys/emulation/ndis/kern_windrv.c index ba2e5e57b5..e95ab0ff8b 100644 --- a/sys/emulation/ndis/kern_windrv.c +++ b/sys/emulation/ndis/kern_windrv.c @@ -32,6 +32,8 @@ * $FreeBSD: src/sys/compat/ndis/kern_windrv.c,v 1.21 2010/11/22 20:46:38 bschmidt Exp $ */ +#include "use_usb4bsd.h" + #include #include #include @@ -54,8 +56,13 @@ #include #endif +#if NUSB4BSD > 0 +#include +#include +#else #include #include +#endif #include #include @@ -63,7 +70,11 @@ #include #include #include +#if NUSB4BSD > 0 +#include +#else #include +#endif static struct lock drvdb_lock; static STAILQ_HEAD(drvdb, drvdb_ent) drvdb_head; diff --git a/sys/emulation/ndis/ntoskrnl_var.h b/sys/emulation/ndis/ntoskrnl_var.h index caee82ee07..81fac33bb0 100644 --- a/sys/emulation/ndis/ntoskrnl_var.h +++ b/sys/emulation/ndis/ntoskrnl_var.h @@ -35,6 +35,8 @@ #ifndef _NTOSKRNL_VAR_H_ #define _NTOSKRNL_VAR_H_ +#include "use_usb4bsd.h" + #define MTX_NTOSKRNL_SPIN_LOCK "NDIS thread lock" /* @@ -1066,7 +1068,11 @@ struct irp { union { kapc irp_apc; struct { +#if NUSB4BSD > 0 + void *irp_ep; +#else void *irp_xfer; +#endif void *irp_dev; } irp_usb; } irp_misc; @@ -1078,7 +1084,11 @@ struct irp { #define irp_pkttype s2.u2.irp_pkttype #define IRP_NDIS_DEV(irp) (irp)->irp_tail.irp_misc.irp_usb.irp_dev +#if NUSB4BSD > 0 +#define IRP_NDISUSB_EP(irp) (irp)->irp_tail.irp_misc.irp_usb.irp_ep +#else #define IRP_NDISUSB_XFER(irp) (irp)->irp_tail.irp_misc.irp_usb.irp_xfer +#endif typedef struct irp irp; diff --git a/sys/emulation/ndis/subr_ndis.c b/sys/emulation/ndis/subr_ndis.c index d9d8cee04a..fcd37c92f9 100644 --- a/sys/emulation/ndis/subr_ndis.c +++ b/sys/emulation/ndis/subr_ndis.c @@ -47,6 +47,7 @@ * expects. */ +#include "use_usb4bsd.h" #include #include @@ -90,8 +91,13 @@ #include #include +#if NUSB4BSD > 0 +#include +#include +#else #include #include +#endif #include #include diff --git a/sys/emulation/ndis/subr_u4bd.c b/sys/emulation/ndis/subr_u4bd.c new file mode 100644 index 0000000000..5b8fc96f96 --- /dev/null +++ b/sys/emulation/ndis/subr_u4bd.c @@ -0,0 +1,1454 @@ +/*- + * Copyright (c) 2005 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/compat/ndis/subr_usbd.c,v 1.22 2012/11/17 01:51:26 svnexp Exp $ + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static driver_object usbd_driver; +static usb_callback_t usbd_non_isoc_callback; +static usb_callback_t usbd_ctrl_callback; + +#define USBD_CTRL_READ_PIPE 0 +#define USBD_CTRL_WRITE_PIPE 1 +#define USBD_CTRL_MAX_PIPE 2 +#define USBD_CTRL_READ_BUFFER_SP 256 +#define USBD_CTRL_WRITE_BUFFER_SP 256 +#define USBD_CTRL_READ_BUFFER_SIZE \ + (sizeof(struct usb_device_request) + USBD_CTRL_READ_BUFFER_SP) +#define USBD_CTRL_WRITE_BUFFER_SIZE \ + (sizeof(struct usb_device_request) + USBD_CTRL_WRITE_BUFFER_SP) +static struct usb_config usbd_default_epconfig[USBD_CTRL_MAX_PIPE] = { + [USBD_CTRL_READ_PIPE] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* control pipe */ + .direction = UE_DIR_ANY, + .if_index = 0, + .bufsize = USBD_CTRL_READ_BUFFER_SIZE, + .flags = { .short_xfer_ok = 1, }, + .callback = &usbd_ctrl_callback, + .timeout = 5000, /* 5 seconds */ + }, + [USBD_CTRL_WRITE_PIPE] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* control pipe */ + .direction = UE_DIR_ANY, + .if_index = 0, + .bufsize = USBD_CTRL_WRITE_BUFFER_SIZE, + .flags = { .proxy_buffer = 1, }, + .callback = &usbd_ctrl_callback, + .timeout = 5000, /* 5 seconds */ + } +}; + +static int32_t usbd_func_bulkintr(irp *); +static int32_t usbd_func_vendorclass(irp *); +static int32_t usbd_func_selconf(irp *); +static int32_t usbd_func_abort_pipe(irp *); +static usb_error_t usbd_setup_endpoint(irp *, uint8_t, + struct usb_endpoint_descriptor *); +static usb_error_t usbd_setup_endpoint_default(irp *, uint8_t); +static usb_error_t usbd_setup_endpoint_one(irp *, uint8_t, + struct ndisusb_ep *, struct usb_config *); +static int32_t usbd_func_getdesc(irp *); +static union usbd_urb *usbd_geturb(irp *); +static struct ndisusb_ep*usbd_get_ndisep(irp *, usb_endpoint_descriptor_t *); +static int32_t usbd_iodispatch(device_object *, irp *); +static int32_t usbd_ioinvalid(device_object *, irp *); +static int32_t usbd_pnp(device_object *, irp *); +static int32_t usbd_power(device_object *, irp *); +static void usbd_irpcancel(device_object *, irp *); +static int32_t usbd_submit_urb(irp *); +static int32_t usbd_urb2nt(int32_t); +static void usbd_task(device_object *, void *); +static int32_t usbd_taskadd(irp *, unsigned); +static void usbd_xfertask(device_object *, void *); +static void dummy(void); + +static union usbd_urb *USBD_CreateConfigurationRequestEx( + usb_config_descriptor_t *, + struct usbd_interface_list_entry *); +static union usbd_urb *USBD_CreateConfigurationRequest( + usb_config_descriptor_t *, + uint16_t *); +static void USBD_GetUSBDIVersion(usbd_version_info *); +static usb_interface_descriptor_t *USBD_ParseConfigurationDescriptorEx( + usb_config_descriptor_t *, void *, int32_t, int32_t, + int32_t, int32_t, int32_t); +static usb_interface_descriptor_t *USBD_ParseConfigurationDescriptor( + usb_config_descriptor_t *, uint8_t, uint8_t); + +/* + * We need to wrap these functions because these need `context switch' from + * Windows to UNIX before it's called. + */ +static funcptr usbd_iodispatch_wrap; +static funcptr usbd_ioinvalid_wrap; +static funcptr usbd_pnp_wrap; +static funcptr usbd_power_wrap; +static funcptr usbd_irpcancel_wrap; +static funcptr usbd_task_wrap; +static funcptr usbd_xfertask_wrap; + +int +usbd_libinit(void) +{ + image_patch_table *patch; + int i; + + patch = usbd_functbl; + while (patch->ipt_func != NULL) { + windrv_wrap((funcptr)patch->ipt_func, + (funcptr *)&patch->ipt_wrap, + patch->ipt_argcnt, patch->ipt_ftype); + patch++; + } + + windrv_wrap((funcptr)usbd_ioinvalid, + (funcptr *)&usbd_ioinvalid_wrap, 2, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)usbd_iodispatch, + (funcptr *)&usbd_iodispatch_wrap, 2, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)usbd_pnp, + (funcptr *)&usbd_pnp_wrap, 2, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)usbd_power, + (funcptr *)&usbd_power_wrap, 2, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)usbd_irpcancel, + (funcptr *)&usbd_irpcancel_wrap, 2, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)usbd_task, + (funcptr *)&usbd_task_wrap, 2, WINDRV_WRAP_STDCALL); + windrv_wrap((funcptr)usbd_xfertask, + (funcptr *)&usbd_xfertask_wrap, 2, WINDRV_WRAP_STDCALL); + + /* Create a fake USB driver instance. */ + + windrv_bus_attach(&usbd_driver, "USB Bus"); + + /* Set up our dipatch routine. */ + for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) + usbd_driver.dro_dispatch[i] = + (driver_dispatch)usbd_ioinvalid_wrap; + + usbd_driver.dro_dispatch[IRP_MJ_INTERNAL_DEVICE_CONTROL] = + (driver_dispatch)usbd_iodispatch_wrap; + usbd_driver.dro_dispatch[IRP_MJ_DEVICE_CONTROL] = + (driver_dispatch)usbd_iodispatch_wrap; + usbd_driver.dro_dispatch[IRP_MJ_POWER] = + (driver_dispatch)usbd_power_wrap; + usbd_driver.dro_dispatch[IRP_MJ_PNP] = + (driver_dispatch)usbd_pnp_wrap; + + return (0); +} + +int +usbd_libfini(void) +{ + image_patch_table *patch; + + patch = usbd_functbl; + while (patch->ipt_func != NULL) { + windrv_unwrap(patch->ipt_wrap); + patch++; + } + + windrv_unwrap(usbd_ioinvalid_wrap); + windrv_unwrap(usbd_iodispatch_wrap); + windrv_unwrap(usbd_pnp_wrap); + windrv_unwrap(usbd_power_wrap); + windrv_unwrap(usbd_irpcancel_wrap); + windrv_unwrap(usbd_task_wrap); + windrv_unwrap(usbd_xfertask_wrap); + + kfree(usbd_driver.dro_drivername.us_buf, M_DEVBUF); + + return (0); +} + +static int32_t +usbd_iodispatch(device_object *dobj, irp *ip) +{ + device_t dev = dobj->do_devext; + int32_t status; + struct io_stack_location *irp_sl; + + irp_sl = IoGetCurrentIrpStackLocation(ip); + switch (irp_sl->isl_parameters.isl_ioctl.isl_iocode) { + case IOCTL_INTERNAL_USB_SUBMIT_URB: + IRP_NDIS_DEV(ip) = dev; + + status = usbd_submit_urb(ip); + break; + default: + device_printf(dev, "ioctl 0x%x isn't supported\n", + irp_sl->isl_parameters.isl_ioctl.isl_iocode); + status = USBD_STATUS_NOT_SUPPORTED; + break; + } + + if (status == USBD_STATUS_PENDING) + return (STATUS_PENDING); + + ip->irp_iostat.isb_status = usbd_urb2nt(status); + if (status != USBD_STATUS_SUCCESS) + ip->irp_iostat.isb_info = 0; + return (ip->irp_iostat.isb_status); +} + +static int32_t +usbd_ioinvalid(device_object *dobj, irp *ip) +{ + device_t dev = dobj->do_devext; + struct io_stack_location *irp_sl; + + irp_sl = IoGetCurrentIrpStackLocation(ip); + device_printf(dev, "invalid I/O dispatch %d:%d\n", irp_sl->isl_major, + irp_sl->isl_minor); + + ip->irp_iostat.isb_status = STATUS_FAILURE; + ip->irp_iostat.isb_info = 0; + + IoCompleteRequest(ip, IO_NO_INCREMENT); + + return (STATUS_FAILURE); +} + +static int32_t +usbd_pnp(device_object *dobj, irp *ip) +{ + device_t dev = dobj->do_devext; + struct io_stack_location *irp_sl; + + irp_sl = IoGetCurrentIrpStackLocation(ip); + device_printf(dev, "%s: unsupported I/O dispatch %d:%d\n", + __func__, irp_sl->isl_major, irp_sl->isl_minor); + + ip->irp_iostat.isb_status = STATUS_FAILURE; + ip->irp_iostat.isb_info = 0; + + IoCompleteRequest(ip, IO_NO_INCREMENT); + + return (STATUS_FAILURE); +} + +static int32_t +usbd_power(device_object *dobj, irp *ip) +{ + device_t dev = dobj->do_devext; + struct io_stack_location *irp_sl; + + irp_sl = IoGetCurrentIrpStackLocation(ip); + device_printf(dev, "%s: unsupported I/O dispatch %d:%d\n", + __func__, irp_sl->isl_major, irp_sl->isl_minor); + + ip->irp_iostat.isb_status = STATUS_FAILURE; + ip->irp_iostat.isb_info = 0; + + IoCompleteRequest(ip, IO_NO_INCREMENT); + + return (STATUS_FAILURE); +} + +/* Convert USBD_STATUS to NTSTATUS */ +static int32_t +usbd_urb2nt(int32_t status) +{ + + switch (status) { + case USBD_STATUS_SUCCESS: + return (STATUS_SUCCESS); + case USBD_STATUS_DEVICE_GONE: + return (STATUS_DEVICE_NOT_CONNECTED); + case USBD_STATUS_PENDING: + return (STATUS_PENDING); + case USBD_STATUS_NOT_SUPPORTED: + return (STATUS_NOT_IMPLEMENTED); + case USBD_STATUS_NO_MEMORY: + return (STATUS_NO_MEMORY); + case USBD_STATUS_REQUEST_FAILED: + return (STATUS_NOT_SUPPORTED); + case USBD_STATUS_CANCELED: + return (STATUS_CANCELLED); + default: + break; + } + + return (STATUS_FAILURE); +} + +/* Convert FreeBSD's usb_error_t to USBD_STATUS */ +static int32_t +usbd_usb2urb(int status) +{ + + switch (status) { + case USB_ERR_NORMAL_COMPLETION: + return (USBD_STATUS_SUCCESS); + case USB_ERR_PENDING_REQUESTS: + return (USBD_STATUS_PENDING); + case USB_ERR_TIMEOUT: + return (USBD_STATUS_TIMEOUT); + case USB_ERR_SHORT_XFER: + return (USBD_STATUS_ERROR_SHORT_TRANSFER); + case USB_ERR_IOERROR: + return (USBD_STATUS_XACT_ERROR); + case USB_ERR_NOMEM: + return (USBD_STATUS_NO_MEMORY); + case USB_ERR_INVAL: + return (USBD_STATUS_REQUEST_FAILED); + case USB_ERR_NOT_STARTED: + case USB_ERR_TOO_DEEP: + case USB_ERR_NO_POWER: + return (USBD_STATUS_DEVICE_GONE); + case USB_ERR_CANCELLED: + return (USBD_STATUS_CANCELED); + default: + break; + } + + return (USBD_STATUS_NOT_SUPPORTED); +} + +static union usbd_urb * +usbd_geturb(irp *ip) +{ + struct io_stack_location *irp_sl; + + irp_sl = IoGetCurrentIrpStackLocation(ip); + + return (irp_sl->isl_parameters.isl_others.isl_arg1); +} + +static int32_t +usbd_submit_urb(irp *ip) +{ + device_t dev = IRP_NDIS_DEV(ip); + int32_t status; + union usbd_urb *urb; + + urb = usbd_geturb(ip); + /* + * In a case of URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER, + * USBD_URB_STATUS(urb) would be set at callback functions like + * usbd_intr() or usbd_xfereof(). + */ + switch (urb->uu_hdr.uuh_func) { + case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: + status = usbd_func_bulkintr(ip); + if (status != USBD_STATUS_SUCCESS && + status != USBD_STATUS_PENDING) + USBD_URB_STATUS(urb) = status; + break; + case URB_FUNCTION_VENDOR_DEVICE: + case URB_FUNCTION_VENDOR_INTERFACE: + case URB_FUNCTION_VENDOR_ENDPOINT: + case URB_FUNCTION_VENDOR_OTHER: + case URB_FUNCTION_CLASS_DEVICE: + case URB_FUNCTION_CLASS_INTERFACE: + case URB_FUNCTION_CLASS_ENDPOINT: + case URB_FUNCTION_CLASS_OTHER: + status = usbd_func_vendorclass(ip); + USBD_URB_STATUS(urb) = status; + break; + case URB_FUNCTION_SELECT_CONFIGURATION: + status = usbd_func_selconf(ip); + USBD_URB_STATUS(urb) = status; + break; + case URB_FUNCTION_ABORT_PIPE: + status = usbd_func_abort_pipe(ip); + USBD_URB_STATUS(urb) = status; + break; + case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE: + status = usbd_func_getdesc(ip); + USBD_URB_STATUS(urb) = status; + break; + default: + device_printf(dev, "func 0x%x isn't supported\n", + urb->uu_hdr.uuh_func); + USBD_URB_STATUS(urb) = status = USBD_STATUS_NOT_SUPPORTED; + break; + } + + return (status); +} + +static int32_t +usbd_func_getdesc(irp *ip) +{ +#define NDISUSB_GETDESC_MAXRETRIES 3 + device_t dev = IRP_NDIS_DEV(ip); + struct ndis_softc *sc = device_get_softc(dev); + struct usbd_urb_control_descriptor_request *ctldesc; + uint16_t actlen; + uint32_t len; + union usbd_urb *urb; + usb_config_descriptor_t *cdp; + usb_error_t status; + + urb = usbd_geturb(ip); + ctldesc = &urb->uu_ctldesc; + if (ctldesc->ucd_desctype == UDESC_CONFIG) { + /* + * The NDIS driver is not allowed to change the + * config! There is only one choice! + */ + cdp = usbd_get_config_descriptor(sc->ndisusb_dev); + if (cdp == NULL) { + status = USB_ERR_INVAL; + goto exit; + } + if (cdp->bDescriptorType != UDESC_CONFIG) { + device_printf(dev, "bad desc %d\n", + cdp->bDescriptorType); + status = USB_ERR_INVAL; + goto exit; + } + /* get minimum length */ + len = MIN(UGETW(cdp->wTotalLength), ctldesc->ucd_trans_buflen); + /* copy out config descriptor */ + memcpy(ctldesc->ucd_trans_buf, cdp, len); + /* set actual length */ + actlen = len; + status = USB_ERR_NORMAL_COMPLETION; + } else { + NDISUSB_LOCK(sc); + status = usbd_req_get_desc(sc->ndisusb_dev, &sc->ndisusb_lock, + &actlen, ctldesc->ucd_trans_buf, 2, + ctldesc->ucd_trans_buflen, ctldesc->ucd_langid, + ctldesc->ucd_desctype, ctldesc->ucd_idx, + NDISUSB_GETDESC_MAXRETRIES); + NDISUSB_UNLOCK(sc); + } +exit: + if (status != USB_ERR_NORMAL_COMPLETION) { + ctldesc->ucd_trans_buflen = 0; + return usbd_usb2urb(status); + } + + ctldesc->ucd_trans_buflen = actlen; + ip->irp_iostat.isb_info = actlen; + + return (USBD_STATUS_SUCCESS); +#undef NDISUSB_GETDESC_MAXRETRIES +} + +static int32_t +usbd_func_selconf(irp *ip) +{ + device_t dev = IRP_NDIS_DEV(ip); + int i, j; + struct ndis_softc *sc = device_get_softc(dev); + struct usb_device *udev = sc->ndisusb_dev; + struct usb_endpoint *ep = NULL; + struct usbd_interface_information *intf; + struct usbd_pipe_information *pipe; + struct usbd_urb_select_configuration *selconf; + union usbd_urb *urb; + usb_config_descriptor_t *conf; + usb_endpoint_descriptor_t *edesc; + usb_error_t ret; + + urb = usbd_geturb(ip); + + selconf = &urb->uu_selconf; + conf = selconf->usc_conf; + if (conf == NULL) { + device_printf(dev, "select configuration is NULL\n"); + return usbd_usb2urb(USB_ERR_NORMAL_COMPLETION); + } + + intf = &selconf->usc_intf; + for (i = 0; i < conf->bNumInterface && intf->uii_len > 0; i++) { + ret = usbd_set_alt_interface_index(udev, + intf->uii_intfnum, intf->uii_altset); + if (ret != USB_ERR_NORMAL_COMPLETION && ret != USB_ERR_IN_USE) { + device_printf(dev, + "setting alternate interface failed: %s\n", + usbd_errstr(ret)); + return usbd_usb2urb(ret); + } + + for (j = 0; (ep = usb_endpoint_foreach(udev, ep)); j++) { + if (j >= intf->uii_numeps) { + device_printf(dev, + "endpoint %d and above are ignored", + intf->uii_numeps); + break; + } + edesc = ep->edesc; + pipe = &intf->uii_pipes[j]; + pipe->upi_handle = edesc; + pipe->upi_epaddr = edesc->bEndpointAddress; + pipe->upi_maxpktsize = UGETW(edesc->wMaxPacketSize); + pipe->upi_type = UE_GET_XFERTYPE(edesc->bmAttributes); + + ret = usbd_setup_endpoint(ip, intf->uii_intfnum, edesc); + if (ret != USB_ERR_NORMAL_COMPLETION) + return usbd_usb2urb(ret); + + if (pipe->upi_type != UE_INTERRUPT) + continue; + + /* XXX we're following linux USB's interval policy. */ + if (udev->speed == USB_SPEED_LOW) + pipe->upi_interval = edesc->bInterval + 5; + else if (udev->speed == USB_SPEED_FULL) + pipe->upi_interval = edesc->bInterval; + else { + int k0 = 0, k1 = 1; + do { + k1 = k1 * 2; + k0 = k0 + 1; + } while (k1 < edesc->bInterval); + pipe->upi_interval = k0; + } + } + + intf = (struct usbd_interface_information *)(((char *)intf) + + intf->uii_len); + } + + return (USBD_STATUS_SUCCESS); +} + +static usb_error_t +usbd_setup_endpoint_one(irp *ip, uint8_t ifidx, struct ndisusb_ep *ne, + struct usb_config *epconf) +{ + device_t dev = IRP_NDIS_DEV(ip); + struct ndis_softc *sc = device_get_softc(dev); + struct usb_xfer *xfer; + usb_error_t status; + + InitializeListHead(&ne->ne_active); + InitializeListHead(&ne->ne_pending); + KeInitializeSpinLock(&ne->ne_lock); + + status = usbd_transfer_setup(sc->ndisusb_dev, &ifidx, ne->ne_xfer, + epconf, 1, sc, &sc->ndisusb_lock); + if (status != USB_ERR_NORMAL_COMPLETION) { + device_printf(dev, "couldn't setup xfer: %s\n", + usbd_errstr(status)); + return (status); + } + xfer = ne->ne_xfer[0]; + usbd_xfer_set_priv(xfer, ne); + + return (status); +} + +static usb_error_t +usbd_setup_endpoint_default(irp *ip, uint8_t ifidx) +{ + device_t dev = IRP_NDIS_DEV(ip); + struct ndis_softc *sc = device_get_softc(dev); + usb_error_t status; + + if (ifidx > 0) + device_printf(dev, "warning: ifidx > 0 isn't supported.\n"); + + status = usbd_setup_endpoint_one(ip, ifidx, &sc->ndisusb_dread_ep, + &usbd_default_epconfig[USBD_CTRL_READ_PIPE]); + if (status != USB_ERR_NORMAL_COMPLETION) + return (status); + + status = usbd_setup_endpoint_one(ip, ifidx, &sc->ndisusb_dwrite_ep, + &usbd_default_epconfig[USBD_CTRL_WRITE_PIPE]); + return (status); +} + +static usb_error_t +usbd_setup_endpoint(irp *ip, uint8_t ifidx, + struct usb_endpoint_descriptor *ep) +{ + device_t dev = IRP_NDIS_DEV(ip); + struct ndis_softc *sc = device_get_softc(dev); + struct ndisusb_ep *ne; + struct usb_config cfg; + struct usb_xfer *xfer; + usb_error_t status; + + /* check for non-supported transfer types */ + if (UE_GET_XFERTYPE(ep->bmAttributes) == UE_CONTROL || + UE_GET_XFERTYPE(ep->bmAttributes) == UE_ISOCHRONOUS) { + device_printf(dev, "%s: unsuppotted transfer types %#x\n", + __func__, UE_GET_XFERTYPE(ep->bmAttributes)); + return (USB_ERR_INVAL); + } + + ne = &sc->ndisusb_ep[NDISUSB_GET_ENDPT(ep->bEndpointAddress)]; + InitializeListHead(&ne->ne_active); + InitializeListHead(&ne->ne_pending); + KeInitializeSpinLock(&ne->ne_lock); + ne->ne_dirin = UE_GET_DIR(ep->bEndpointAddress) >> 7; + + memset(&cfg, 0, sizeof(struct usb_config)); + cfg.type = UE_GET_XFERTYPE(ep->bmAttributes); + cfg.endpoint = UE_GET_ADDR(ep->bEndpointAddress); + cfg.direction = UE_GET_DIR(ep->bEndpointAddress); + cfg.callback = &usbd_non_isoc_callback; + cfg.bufsize = UGETW(ep->wMaxPacketSize); + cfg.flags.proxy_buffer = 1; + if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN) + cfg.flags.short_xfer_ok = 1; + + status = usbd_transfer_setup(sc->ndisusb_dev, &ifidx, ne->ne_xfer, + &cfg, 1, sc, &sc->ndisusb_lock); + if (status != USB_ERR_NORMAL_COMPLETION) { + device_printf(dev, "couldn't setup xfer: %s\n", + usbd_errstr(status)); + return (status); + } + xfer = ne->ne_xfer[0]; + usbd_xfer_set_priv(xfer, ne); + if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN) + usbd_xfer_set_timeout(xfer, NDISUSB_NO_TIMEOUT); + else { + if (UE_GET_XFERTYPE(ep->bmAttributes) == UE_BULK) + usbd_xfer_set_timeout(xfer, NDISUSB_TX_TIMEOUT); + else + usbd_xfer_set_timeout(xfer, NDISUSB_INTR_TIMEOUT); + } + + return (status); +} + +static int32_t +usbd_func_abort_pipe(irp *ip) +{ + device_t dev = IRP_NDIS_DEV(ip); + struct ndis_softc *sc = device_get_softc(dev); + struct ndisusb_ep *ne; + union usbd_urb *urb; + + urb = usbd_geturb(ip); + ne = usbd_get_ndisep(ip, urb->uu_pipe.upr_handle); + if (ne == NULL) { + device_printf(IRP_NDIS_DEV(ip), "get NULL endpoint info.\n"); + return (USBD_STATUS_INVALID_PIPE_HANDLE); + } + + NDISUSB_LOCK(sc); + usbd_transfer_stop(ne->ne_xfer[0]); + usbd_transfer_start(ne->ne_xfer[0]); + NDISUSB_UNLOCK(sc); + + return (USBD_STATUS_SUCCESS); +} + +static int32_t +usbd_func_vendorclass(irp *ip) +{ + device_t dev = IRP_NDIS_DEV(ip); + int32_t error; + struct ndis_softc *sc = device_get_softc(dev); + struct ndisusb_ep *ne; + struct ndisusb_xfer *nx; + struct usbd_urb_vendor_or_class_request *vcreq; + union usbd_urb *urb; + + if (!(sc->ndisusb_status & NDISUSB_STATUS_SETUP_EP)) { + /* + * XXX In some cases the interface number isn't 0. However + * some driver (eg. RTL8187L NDIS driver) calls this function + * before calling URB_FUNCTION_SELECT_CONFIGURATION. + */ + error = usbd_setup_endpoint_default(ip, 0); + if (error != USB_ERR_NORMAL_COMPLETION) + return usbd_usb2urb(error); + sc->ndisusb_status |= NDISUSB_STATUS_SETUP_EP; + } + + urb = usbd_geturb(ip); + vcreq = &urb->uu_vcreq; + ne = (vcreq->uvc_trans_flags & USBD_TRANSFER_DIRECTION_IN) ? + &sc->ndisusb_dread_ep : &sc->ndisusb_dwrite_ep; + IRP_NDISUSB_EP(ip) = ne; + ip->irp_cancelfunc = (cancel_func)usbd_irpcancel_wrap; + + nx = kmalloc(sizeof(struct ndisusb_xfer), M_USBDEV, M_NOWAIT | M_ZERO); + if (nx == NULL) { + device_printf(IRP_NDIS_DEV(ip), "out of memory\n"); + return (USBD_STATUS_NO_MEMORY); + } + nx->nx_ep = ne; + nx->nx_priv = ip; + KeAcquireSpinLockAtDpcLevel(&ne->ne_lock); + InsertTailList((&ne->ne_pending), (&nx->nx_next)); + KeReleaseSpinLockFromDpcLevel(&ne->ne_lock); + + /* we've done to setup xfer. Let's transfer it. */ + ip->irp_iostat.isb_status = STATUS_PENDING; + ip->irp_iostat.isb_info = 0; + USBD_URB_STATUS(urb) = USBD_STATUS_PENDING; + IoMarkIrpPending(ip); + + error = usbd_taskadd(ip, NDISUSB_TASK_VENDOR); + if (error != USBD_STATUS_SUCCESS) + return (error); + + return (USBD_STATUS_PENDING); +} + +static void +usbd_irpcancel(device_object *dobj, irp *ip) +{ + device_t dev = IRP_NDIS_DEV(ip); + struct ndis_softc *sc = device_get_softc(dev); + struct ndisusb_ep *ne = IRP_NDISUSB_EP(ip); + + if (ne == NULL) { + ip->irp_cancel = TRUE; + IoReleaseCancelSpinLock(ip->irp_cancelirql); + return; + } + + /* + * Make sure that the current USB transfer proxy is + * cancelled and then restarted. + */ + NDISUSB_LOCK(sc); + usbd_transfer_stop(ne->ne_xfer[0]); + usbd_transfer_start(ne->ne_xfer[0]); + NDISUSB_UNLOCK(sc); + + ip->irp_cancel = TRUE; + IoReleaseCancelSpinLock(ip->irp_cancelirql); +} + +static void +usbd_xfer_complete(struct ndis_softc *sc, struct ndisusb_ep *ne, + struct ndisusb_xfer *nx, usb_error_t status) +{ + struct ndisusb_xferdone *nd; + uint8_t irql; + + nd = kmalloc(sizeof(struct ndisusb_xferdone), M_USBDEV, + M_NOWAIT | M_ZERO); + if (nd == NULL) { + device_printf(sc->ndis_dev, "out of memory"); + return; + } + nd->nd_xfer = nx; + nd->nd_status = status; + + KeAcquireSpinLock(&sc->ndisusb_xferdonelock, &irql); + InsertTailList((&sc->ndisusb_xferdonelist), (&nd->nd_donelist)); + KeReleaseSpinLock(&sc->ndisusb_xferdonelock, irql); + + IoQueueWorkItem(sc->ndisusb_xferdoneitem, + (io_workitem_func)usbd_xfertask_wrap, WORKQUEUE_CRITICAL, sc); +} + +static struct ndisusb_xfer * +usbd_aq_getfirst(struct ndis_softc *sc, struct ndisusb_ep *ne) +{ + struct ndisusb_xfer *nx; + + KeAcquireSpinLockAtDpcLevel(&ne->ne_lock); + if (IsListEmpty(&ne->ne_active)) { + device_printf(sc->ndis_dev, + "%s: the active queue can't be empty.\n", __func__); + KeReleaseSpinLockFromDpcLevel(&ne->ne_lock); + return (NULL); + } + nx = CONTAINING_RECORD(ne->ne_active.nle_flink, struct ndisusb_xfer, + nx_next); + RemoveEntryList(&nx->nx_next); + KeReleaseSpinLockFromDpcLevel(&ne->ne_lock); + + return (nx); +} + +static void +usbd_non_isoc_callback(struct usb_xfer *xfer, usb_error_t error) +{ + irp *ip; + struct ndis_softc *sc = usbd_xfer_softc(xfer); + struct ndisusb_ep *ne = usbd_xfer_get_priv(xfer); + struct ndisusb_xfer *nx; + struct usbd_urb_bulk_or_intr_transfer *ubi; + struct usb_page_cache *pc; + uint8_t irql; + uint32_t len; + union usbd_urb *urb; + usb_endpoint_descriptor_t *ep; + int actlen, sumlen; + + usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + nx = usbd_aq_getfirst(sc, ne); + pc = usbd_xfer_get_frame(xfer, 0); + if (nx == NULL) + return; + + /* copy in data with regard to the URB */ + if (ne->ne_dirin != 0) + usbd_copy_out(pc, 0, nx->nx_urbbuf, actlen); + nx->nx_urbbuf += actlen; + nx->nx_urbactlen += actlen; + nx->nx_urblen -= actlen; + + /* check for short transfer */ + if (actlen < sumlen) + nx->nx_urblen = 0; + else { + /* check remainder */ + if (nx->nx_urblen > 0) { + KeAcquireSpinLock(&ne->ne_lock, &irql); + InsertHeadList((&ne->ne_active), (&nx->nx_next)); + KeReleaseSpinLock(&ne->ne_lock, irql); + + ip = nx->nx_priv; + urb = usbd_geturb(ip); + ubi = &urb->uu_bulkintr; + ep = ubi->ubi_epdesc; + goto extra; + } + } + usbd_xfer_complete(sc, ne, nx, + ((actlen < sumlen) && (nx->nx_shortxfer == 0)) ? + USB_ERR_SHORT_XFER : USB_ERR_NORMAL_COMPLETION); + + /* fall through */ + case USB_ST_SETUP: +next: + /* get next transfer */ + KeAcquireSpinLock(&ne->ne_lock, &irql); + if (IsListEmpty(&ne->ne_pending)) { + KeReleaseSpinLock(&ne->ne_lock, irql); + return; + } + nx = CONTAINING_RECORD(ne->ne_pending.nle_flink, + struct ndisusb_xfer, nx_next); + RemoveEntryList(&nx->nx_next); + /* add a entry to the active queue's tail. */ + InsertTailList((&ne->ne_active), (&nx->nx_next)); + KeReleaseSpinLock(&ne->ne_lock, irql); + + ip = nx->nx_priv; + urb = usbd_geturb(ip); + ubi = &urb->uu_bulkintr; + ep = ubi->ubi_epdesc; + + nx->nx_urbbuf = ubi->ubi_trans_buf; + nx->nx_urbactlen = 0; + nx->nx_urblen = ubi->ubi_trans_buflen; + nx->nx_shortxfer = (ubi->ubi_trans_flags & + USBD_SHORT_TRANSFER_OK) ? 1 : 0; +extra: + len = MIN(usbd_xfer_max_len(xfer), nx->nx_urblen); + pc = usbd_xfer_get_frame(xfer, 0); + if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_OUT) + usbd_copy_in(pc, 0, nx->nx_urbbuf, len); + usbd_xfer_set_frame_len(xfer, 0, len); + usbd_xfer_set_frames(xfer, 1); + usbd_transfer_submit(xfer); + break; + default: + nx = usbd_aq_getfirst(sc, ne); + if (nx == NULL) + return; + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + device_printf(sc->ndis_dev, "usb xfer warning (%s)\n", + usbd_errstr(error)); + } + usbd_xfer_complete(sc, ne, nx, error); + if (error != USB_ERR_CANCELLED) + goto next; + break; + } +} + +static void +usbd_ctrl_callback(struct usb_xfer *xfer, usb_error_t error) +{ + irp *ip; + struct ndis_softc *sc = usbd_xfer_softc(xfer); + struct ndisusb_ep *ne = usbd_xfer_get_priv(xfer); + struct ndisusb_xfer *nx; + uint8_t irql; + union usbd_urb *urb; + struct usbd_urb_vendor_or_class_request *vcreq; + struct usb_page_cache *pc; + uint8_t type = 0; + struct usb_device_request req; + int len; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + nx = usbd_aq_getfirst(sc, ne); + if (nx == NULL) + return; + + ip = nx->nx_priv; + urb = usbd_geturb(ip); + vcreq = &urb->uu_vcreq; + + if (vcreq->uvc_trans_flags & USBD_TRANSFER_DIRECTION_IN) { + pc = usbd_xfer_get_frame(xfer, 1); + len = usbd_xfer_frame_len(xfer, 1); + usbd_copy_out(pc, 0, vcreq->uvc_trans_buf, len); + nx->nx_urbactlen += len; + } + + usbd_xfer_complete(sc, ne, nx, USB_ERR_NORMAL_COMPLETION); + /* fall through */ + case USB_ST_SETUP: +next: + /* get next transfer */ + KeAcquireSpinLock(&ne->ne_lock, &irql); + if (IsListEmpty(&ne->ne_pending)) { + KeReleaseSpinLock(&ne->ne_lock, irql); + return; + } + nx = CONTAINING_RECORD(ne->ne_pending.nle_flink, + struct ndisusb_xfer, nx_next); + RemoveEntryList(&nx->nx_next); + /* add a entry to the active queue's tail. */ + InsertTailList((&ne->ne_active), (&nx->nx_next)); + KeReleaseSpinLock(&ne->ne_lock, irql); + + ip = nx->nx_priv; + urb = usbd_geturb(ip); + vcreq = &urb->uu_vcreq; + + switch (urb->uu_hdr.uuh_func) { + case URB_FUNCTION_CLASS_DEVICE: + type = UT_CLASS | UT_DEVICE; + break; + case URB_FUNCTION_CLASS_INTERFACE: + type = UT_CLASS | UT_INTERFACE; + break; + case URB_FUNCTION_CLASS_OTHER: + type = UT_CLASS | UT_OTHER; + break; + case URB_FUNCTION_CLASS_ENDPOINT: + type = UT_CLASS | UT_ENDPOINT; + break; + case URB_FUNCTION_VENDOR_DEVICE: + type = UT_VENDOR | UT_DEVICE; + break; + case URB_FUNCTION_VENDOR_INTERFACE: + type = UT_VENDOR | UT_INTERFACE; + break; + case URB_FUNCTION_VENDOR_OTHER: + type = UT_VENDOR | UT_OTHER; + break; + case URB_FUNCTION_VENDOR_ENDPOINT: + type = UT_VENDOR | UT_ENDPOINT; + break; + default: + /* never reached. */ + break; + } + + type |= (vcreq->uvc_trans_flags & USBD_TRANSFER_DIRECTION_IN) ? + UT_READ : UT_WRITE; + type |= vcreq->uvc_reserved1; + + req.bmRequestType = type; + req.bRequest = vcreq->uvc_req; + USETW(req.wIndex, vcreq->uvc_idx); + USETW(req.wValue, vcreq->uvc_value); + USETW(req.wLength, vcreq->uvc_trans_buflen); + + nx->nx_urbbuf = vcreq->uvc_trans_buf; + nx->nx_urblen = vcreq->uvc_trans_buflen; + nx->nx_urbactlen = 0; + + pc = usbd_xfer_get_frame(xfer, 0); + usbd_copy_in(pc, 0, &req, sizeof(req)); + usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); + usbd_xfer_set_frames(xfer, 1); + if (vcreq->uvc_trans_flags & USBD_TRANSFER_DIRECTION_IN) { + if (vcreq->uvc_trans_buflen >= USBD_CTRL_READ_BUFFER_SP) + device_printf(sc->ndis_dev, + "warning: not enough buffer space (%d).\n", + vcreq->uvc_trans_buflen); + usbd_xfer_set_frame_len(xfer, 1, + MIN(usbd_xfer_max_len(xfer), + vcreq->uvc_trans_buflen)); + usbd_xfer_set_frames(xfer, 2); + } else { + if (nx->nx_urblen > USBD_CTRL_WRITE_BUFFER_SP) + device_printf(sc->ndis_dev, + "warning: not enough write buffer space" + " (%d).\n", nx->nx_urblen); + /* + * XXX with my local tests there was no cases to require + * a extra buffer until now but it'd need to update in + * the future if it needs to be. + */ + if (nx->nx_urblen > 0) { + pc = usbd_xfer_get_frame(xfer, 1); + usbd_copy_in(pc, 0, nx->nx_urbbuf, + nx->nx_urblen); + usbd_xfer_set_frame_len(xfer, 1, nx->nx_urblen); + usbd_xfer_set_frames(xfer, 2); + } + } + usbd_transfer_submit(xfer); + break; + default: + nx = usbd_aq_getfirst(sc, ne); + if (nx == NULL) + return; + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + device_printf(sc->ndis_dev, "usb xfer warning (%s)\n", + usbd_errstr(error)); + } + usbd_xfer_complete(sc, ne, nx, error); + if (error != USB_ERR_CANCELLED) + goto next; + break; + } +} + +static struct ndisusb_ep * +usbd_get_ndisep(irp *ip, usb_endpoint_descriptor_t *ep) +{ + device_t dev = IRP_NDIS_DEV(ip); + struct ndis_softc *sc = device_get_softc(dev); + struct ndisusb_ep *ne; + + ne = &sc->ndisusb_ep[NDISUSB_GET_ENDPT(ep->bEndpointAddress)]; + + IRP_NDISUSB_EP(ip) = ne; + ip->irp_cancelfunc = (cancel_func)usbd_irpcancel_wrap; + + return (ne); +} + +static void +usbd_xfertask(device_object *dobj, void *arg) +{ + int error; + irp *ip; + device_t dev; + list_entry *l; + struct ndis_softc *sc = arg; + struct ndisusb_xferdone *nd; + struct ndisusb_xfer *nq; + struct usbd_urb_bulk_or_intr_transfer *ubi; + struct usbd_urb_vendor_or_class_request *vcreq; + union usbd_urb *urb; + usb_error_t status; + void *priv; + + dev = sc->ndis_dev; + + if (IsListEmpty(&sc->ndisusb_xferdonelist)) + return; + + KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_xferdonelock); + l = sc->ndisusb_xferdonelist.nle_flink; + while (l != &sc->ndisusb_xferdonelist) { + nd = CONTAINING_RECORD(l, struct ndisusb_xferdone, nd_donelist); + nq = nd->nd_xfer; + priv = nq->nx_priv; + status = nd->nd_status; + error = 0; + ip = priv; + urb = usbd_geturb(ip); + + ip->irp_cancelfunc = NULL; + IRP_NDISUSB_EP(ip) = NULL; + + switch (status) { + case USB_ERR_NORMAL_COMPLETION: + if (urb->uu_hdr.uuh_func == + URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER) { + ubi = &urb->uu_bulkintr; + ubi->ubi_trans_buflen = nq->nx_urbactlen; + } else { + vcreq = &urb->uu_vcreq; + vcreq->uvc_trans_buflen = nq->nx_urbactlen; + } + ip->irp_iostat.isb_info = nq->nx_urbactlen; + ip->irp_iostat.isb_status = STATUS_SUCCESS; + USBD_URB_STATUS(urb) = USBD_STATUS_SUCCESS; + break; + case USB_ERR_CANCELLED: + ip->irp_iostat.isb_info = 0; + ip->irp_iostat.isb_status = STATUS_CANCELLED; + USBD_URB_STATUS(urb) = USBD_STATUS_CANCELED; + break; + default: + ip->irp_iostat.isb_info = 0; + USBD_URB_STATUS(urb) = usbd_usb2urb(status); + ip->irp_iostat.isb_status = + usbd_urb2nt(USBD_URB_STATUS(urb)); + break; + } + + l = l->nle_flink; + RemoveEntryList(&nd->nd_donelist); + kfree(nq, M_USBDEV); + kfree(nd, M_USBDEV); + if (error) + continue; + KeReleaseSpinLockFromDpcLevel(&sc->ndisusb_xferdonelock); + /* NB: call after cleaning */ + IoCompleteRequest(ip, IO_NO_INCREMENT); + KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_xferdonelock); + } + KeReleaseSpinLockFromDpcLevel(&sc->ndisusb_xferdonelock); +} + +/* + * this function is for mainly deferring a task to the another thread because + * we don't want to be in the scope of HAL lock. + */ +static int32_t +usbd_taskadd(irp *ip, unsigned type) +{ + device_t dev = IRP_NDIS_DEV(ip); + struct ndis_softc *sc = device_get_softc(dev); + struct ndisusb_task *nt; + + nt = kmalloc(sizeof(struct ndisusb_task), M_USBDEV, M_NOWAIT | M_ZERO); + if (nt == NULL) + return (USBD_STATUS_NO_MEMORY); + nt->nt_type = type; + nt->nt_ctx = ip; + + KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_tasklock); + InsertTailList((&sc->ndisusb_tasklist), (&nt->nt_tasklist)); + KeReleaseSpinLockFromDpcLevel(&sc->ndisusb_tasklock); + + IoQueueWorkItem(sc->ndisusb_taskitem, + (io_workitem_func)usbd_task_wrap, WORKQUEUE_CRITICAL, sc); + + return (USBD_STATUS_SUCCESS); +} + +static void +usbd_task(device_object *dobj, void *arg) +{ + irp *ip; + list_entry *l; + struct ndis_softc *sc = arg; + struct ndisusb_ep *ne; + struct ndisusb_task *nt; + union usbd_urb *urb; + + if (IsListEmpty(&sc->ndisusb_tasklist)) + return; + + KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_tasklock); + l = sc->ndisusb_tasklist.nle_flink; + while (l != &sc->ndisusb_tasklist) { + nt = CONTAINING_RECORD(l, struct ndisusb_task, nt_tasklist); + + ip = nt->nt_ctx; + urb = usbd_geturb(ip); + + KeReleaseSpinLockFromDpcLevel(&sc->ndisusb_tasklock); + NDISUSB_LOCK(sc); + switch (nt->nt_type) { + case NDISUSB_TASK_TSTART: + ne = usbd_get_ndisep(ip, urb->uu_bulkintr.ubi_epdesc); + if (ne == NULL) + goto exit; + usbd_transfer_start(ne->ne_xfer[0]); + break; + case NDISUSB_TASK_IRPCANCEL: + ne = usbd_get_ndisep(ip, + (nt->nt_type == NDISUSB_TASK_IRPCANCEL) ? + urb->uu_bulkintr.ubi_epdesc : + urb->uu_pipe.upr_handle); + if (ne == NULL) + goto exit; + + usbd_transfer_stop(ne->ne_xfer[0]); + usbd_transfer_start(ne->ne_xfer[0]); + break; + case NDISUSB_TASK_VENDOR: + ne = (urb->uu_vcreq.uvc_trans_flags & + USBD_TRANSFER_DIRECTION_IN) ? + &sc->ndisusb_dread_ep : &sc->ndisusb_dwrite_ep; + usbd_transfer_start(ne->ne_xfer[0]); + break; + default: + break; + } +exit: + NDISUSB_UNLOCK(sc); + KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_tasklock); + + l = l->nle_flink; + RemoveEntryList(&nt->nt_tasklist); + kfree(nt, M_USBDEV); + } + KeReleaseSpinLockFromDpcLevel(&sc->ndisusb_tasklock); +} + +static int32_t +usbd_func_bulkintr(irp *ip) +{ + int32_t error; + struct ndisusb_ep *ne; + struct ndisusb_xfer *nx; + struct usbd_urb_bulk_or_intr_transfer *ubi; + union usbd_urb *urb; + usb_endpoint_descriptor_t *ep; + + urb = usbd_geturb(ip); + ubi = &urb->uu_bulkintr; + ep = ubi->ubi_epdesc; + if (ep == NULL) + return (USBD_STATUS_INVALID_PIPE_HANDLE); + + ne = usbd_get_ndisep(ip, ep); + if (ne == NULL) { + device_printf(IRP_NDIS_DEV(ip), "get NULL endpoint info.\n"); + return (USBD_STATUS_INVALID_PIPE_HANDLE); + } + + nx = kmalloc(sizeof(struct ndisusb_xfer), M_USBDEV, M_NOWAIT | M_ZERO); + if (nx == NULL) { + device_printf(IRP_NDIS_DEV(ip), "out of memory\n"); + return (USBD_STATUS_NO_MEMORY); + } + nx->nx_ep = ne; + nx->nx_priv = ip; + KeAcquireSpinLockAtDpcLevel(&ne->ne_lock); + InsertTailList((&ne->ne_pending), (&nx->nx_next)); + KeReleaseSpinLockFromDpcLevel(&ne->ne_lock); + + /* we've done to setup xfer. Let's transfer it. */ + ip->irp_iostat.isb_status = STATUS_PENDING; + ip->irp_iostat.isb_info = 0; + USBD_URB_STATUS(urb) = USBD_STATUS_PENDING; + IoMarkIrpPending(ip); + + error = usbd_taskadd(ip, NDISUSB_TASK_TSTART); + if (error != USBD_STATUS_SUCCESS) + return (error); + + return (USBD_STATUS_PENDING); +} + +static union usbd_urb * +USBD_CreateConfigurationRequest(usb_config_descriptor_t *conf, uint16_t *len) +{ + struct usbd_interface_list_entry list[2]; + union usbd_urb *urb; + + bzero(list, sizeof(struct usbd_interface_list_entry) * 2); + list[0].uil_intfdesc = USBD_ParseConfigurationDescriptorEx(conf, conf, + -1, -1, -1, -1, -1); + urb = USBD_CreateConfigurationRequestEx(conf, list); + if (urb == NULL) + return (NULL); + + *len = urb->uu_selconf.usc_hdr.uuh_len; + return (urb); +} + +static union usbd_urb * +USBD_CreateConfigurationRequestEx(usb_config_descriptor_t *conf, + struct usbd_interface_list_entry *list) +{ + int i, j, size; + struct usbd_interface_information *intf; + struct usbd_pipe_information *pipe; + struct usbd_urb_select_configuration *selconf; + usb_interface_descriptor_t *desc; + + for (i = 0, size = 0; i < conf->bNumInterface; i++) { + j = list[i].uil_intfdesc->bNumEndpoints; + size = size + sizeof(struct usbd_interface_information) + + sizeof(struct usbd_pipe_information) * (j - 1); + } + size += sizeof(struct usbd_urb_select_configuration) - + sizeof(struct usbd_interface_information); + + selconf = ExAllocatePoolWithTag(NonPagedPool, size, 0); + if (selconf == NULL) + return (NULL); + selconf->usc_hdr.uuh_func = URB_FUNCTION_SELECT_CONFIGURATION; + selconf->usc_hdr.uuh_len = size; + selconf->usc_handle = conf; + selconf->usc_conf = conf; + + intf = &selconf->usc_intf; + for (i = 0; i < conf->bNumInterface; i++) { + if (list[i].uil_intfdesc == NULL) + break; + + list[i].uil_intf = intf; + desc = list[i].uil_intfdesc; + + intf->uii_len = sizeof(struct usbd_interface_information) + + (desc->bNumEndpoints - 1) * + sizeof(struct usbd_pipe_information); + intf->uii_intfnum = desc->bInterfaceNumber; + intf->uii_altset = desc->bAlternateSetting; + intf->uii_intfclass = desc->bInterfaceClass; + intf->uii_intfsubclass = desc->bInterfaceSubClass; + intf->uii_intfproto = desc->bInterfaceProtocol; + intf->uii_handle = desc; + intf->uii_numeps = desc->bNumEndpoints; + + pipe = &intf->uii_pipes[0]; + for (j = 0; j < intf->uii_numeps; j++) + pipe[j].upi_maxtxsize = + USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE; + + intf = (struct usbd_interface_information *)((char *)intf + + intf->uii_len); + } + + return ((union usbd_urb *)selconf); +} + +static void +USBD_GetUSBDIVersion(usbd_version_info *ui) +{ + + /* Pretend to be Windows XP. */ + + ui->uvi_usbdi_vers = USBDI_VERSION; + ui->uvi_supported_vers = USB_VER_2_0; +} + +static usb_interface_descriptor_t * +USBD_ParseConfigurationDescriptor(usb_config_descriptor_t *conf, + uint8_t intfnum, uint8_t altset) +{ + + return USBD_ParseConfigurationDescriptorEx(conf, conf, intfnum, altset, + -1, -1, -1); +} + +static usb_interface_descriptor_t * +USBD_ParseConfigurationDescriptorEx(usb_config_descriptor_t *conf, + void *start, int32_t intfnum, int32_t altset, int32_t intfclass, + int32_t intfsubclass, int32_t intfproto) +{ + struct usb_descriptor *next = NULL; + usb_interface_descriptor_t *desc; + + while ((next = usb_desc_foreach(conf, next)) != NULL) { + desc = (usb_interface_descriptor_t *)next; + if (desc->bDescriptorType != UDESC_INTERFACE) + continue; + if (!(intfnum == -1 || desc->bInterfaceNumber == intfnum)) + continue; + if (!(altset == -1 || desc->bAlternateSetting == altset)) + continue; + if (!(intfclass == -1 || desc->bInterfaceClass == intfclass)) + continue; + if (!(intfsubclass == -1 || + desc->bInterfaceSubClass == intfsubclass)) + continue; + if (!(intfproto == -1 || desc->bInterfaceProtocol == intfproto)) + continue; + return (desc); + } + + return (NULL); +} + +static void +dummy(void) +{ + kprintf("USBD dummy called\n"); +} + +image_patch_table usbd_functbl[] = { + IMPORT_SFUNC(USBD_CreateConfigurationRequest, 2), + IMPORT_SFUNC(USBD_CreateConfigurationRequestEx, 2), + IMPORT_SFUNC_MAP(_USBD_CreateConfigurationRequestEx@8, + USBD_CreateConfigurationRequestEx, 2), + IMPORT_SFUNC(USBD_GetUSBDIVersion, 1), + IMPORT_SFUNC(USBD_ParseConfigurationDescriptor, 3), + IMPORT_SFUNC(USBD_ParseConfigurationDescriptorEx, 7), + IMPORT_SFUNC_MAP(_USBD_ParseConfigurationDescriptorEx@28, + USBD_ParseConfigurationDescriptorEx, 7), + + /* + * This last entry is a catch-all for any function we haven't + * implemented yet. The PE import list patching routine will + * use it for any function that doesn't have an explicit match + * in this table. + */ + + { NULL, (FUNC)dummy, NULL, 0, WINDRV_WRAP_STDCALL }, + + /* End of list. */ + + { NULL, NULL, NULL } +}; + +MODULE_DEPEND(ndis, usb, 1, 1, 1); diff --git a/sys/emulation/ndis/u4bd_var.h b/sys/emulation/ndis/u4bd_var.h new file mode 100644 index 0000000000..8a35aa6c49 --- /dev/null +++ b/sys/emulation/ndis/u4bd_var.h @@ -0,0 +1,222 @@ +/*- + * Copyright (c) 2003 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/compat/ndis/usbd_var.h,v 1.4 2012/11/17 01:51:26 svnexp Exp $ + */ + +#ifndef _USBD_VAR_H_ +#define _USBD_VAR_H_ + +#define IOCTL_INTERNAL_USB_SUBMIT_URB 0x00220003 + +#define URB_FUNCTION_SELECT_CONFIGURATION 0x0000 +#define URB_FUNCTION_ABORT_PIPE 0x0002 +#define URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER 0x0009 +#define URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE 0x000B +#define URB_FUNCTION_VENDOR_DEVICE 0x0017 +#define URB_FUNCTION_VENDOR_INTERFACE 0x0018 +#define URB_FUNCTION_VENDOR_ENDPOINT 0x0019 +#define URB_FUNCTION_CLASS_DEVICE 0x001A +#define URB_FUNCTION_CLASS_INTERFACE 0x001B +#define URB_FUNCTION_CLASS_ENDPOINT 0x001C +#define URB_FUNCTION_CLASS_OTHER 0x001F +#define URB_FUNCTION_VENDOR_OTHER 0x0020 + +#define USBD_STATUS_SUCCESS 0x00000000 +#define USBD_STATUS_CANCELED 0x00010000 +#define USBD_STATUS_PENDING 0x40000000 +#define USBD_STATUS_NO_MEMORY 0x80000100 +#define USBD_STATUS_REQUEST_FAILED 0x80000500 +#define USBD_STATUS_INVALID_PIPE_HANDLE 0x80000600 +#define USBD_STATUS_ERROR_SHORT_TRANSFER 0x80000900 +#define USBD_STATUS_CRC 0xC0000001 +#define USBD_STATUS_BTSTUFF 0xC0000002 +#define USBD_STATUS_DATA_TOGGLE_MISMATCH 0xC0000003 +#define USBD_STATUS_STALL_PID 0xC0000004 +#define USBD_STATUS_DEV_NOT_RESPONDING 0xC0000005 +#define USBD_STATUS_PID_CHECK_FAILURE 0xC0000006 +#define USBD_STATUS_UNEXPECTED_PID 0xC0000007 +#define USBD_STATUS_DATA_OVERRUN 0xC0000008 +#define USBD_STATUS_DATA_UNDERRUN 0xC0000009 +#define USBD_STATUS_RESERVED1 0xC000000A +#define USBD_STATUS_RESERVED2 0xC000000B +#define USBD_STATUS_BUFFER_OVERRUN 0xC000000C +#define USBD_STATUS_BUFFER_UNDERRUN 0xC000000D +#define USBD_STATUS_NOT_ACCESSED 0xC000000F +#define USBD_STATUS_FIFO 0xC0000010 +#define USBD_STATUS_XACT_ERROR 0xC0000011 +#define USBD_STATUS_BABBLE_DETECTED 0xC0000012 +#define USBD_STATUS_DATA_BUFFER_ERROR 0xC0000013 +#define USBD_STATUS_NOT_SUPPORTED 0xC0000E00 +#define USBD_STATUS_TIMEOUT 0xC0006000 +#define USBD_STATUS_DEVICE_GONE 0xC0007000 + +struct usbd_urb_header { + uint16_t uuh_len; + uint16_t uuh_func; + int32_t uuh_status; + void *uuh_handle; + uint32_t uuh_flags; +}; + +enum usbd_pipe_type { + UsbdPipeTypeControl = UE_CONTROL, + UsbdPipeTypeIsochronous = UE_ISOCHRONOUS, + UsbdPipeTypeBulk = UE_BULK, + UsbdPipeTypeInterrupt = UE_INTERRUPT +}; + +struct usbd_pipe_information { + uint16_t upi_maxpktsize; + uint8_t upi_epaddr; + uint8_t upi_interval; + enum usbd_pipe_type upi_type; + usb_endpoint_descriptor_t *upi_handle; + uint32_t upi_maxtxsize; +#define USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE PAGE_SIZE + uint32_t upi_flags; +}; + +struct usbd_interface_information { + uint16_t uii_len; + uint8_t uii_intfnum; + uint8_t uii_altset; + uint8_t uii_intfclass; + uint8_t uii_intfsubclass; + uint8_t uii_intfproto; + uint8_t uii_reserved; + void *uii_handle; + uint32_t uii_numeps; + struct usbd_pipe_information uii_pipes[1]; +}; + +struct usbd_urb_select_interface { + struct usbd_urb_header usi_hdr; + void *usi_handle; + struct usbd_interface_information uusi_intf; +}; + +struct usbd_urb_select_configuration { + struct usbd_urb_header usc_hdr; + usb_config_descriptor_t *usc_conf; + void *usc_handle; + struct usbd_interface_information usc_intf; +}; + +struct usbd_urb_pipe_request { + struct usbd_urb_header upr_hdr; + usb_endpoint_descriptor_t *upr_handle; +}; + +struct usbd_hcd_area { + void *reserved8[8]; +}; + +struct usbd_urb_bulk_or_intr_transfer { + struct usbd_urb_header ubi_hdr; + usb_endpoint_descriptor_t *ubi_epdesc; + uint32_t ubi_trans_flags; +#define USBD_SHORT_TRANSFER_OK 0x00000002 + uint32_t ubi_trans_buflen; + void *ubi_trans_buf; + struct mdl *ubi_mdl; + union usbd_urb *ubi_urblink; + struct usbd_hcd_area ubi_hca; +}; + +struct usbd_urb_control_descriptor_request { + struct usbd_urb_header ucd_hdr; + void *ucd_reserved0; + uint32_t ucd_reserved1; + uint32_t ucd_trans_buflen; + void *ucd_trans_buf; + struct mdl *ucd_mdl; + union nt_urb *ucd_urblink; + struct usbd_hcd_area ucd_hca; + uint16_t ucd_reserved2; + uint8_t ucd_idx; + uint8_t ucd_desctype; + uint16_t ucd_langid; + uint16_t ucd_reserved3; +}; + +struct usbd_urb_vendor_or_class_request { + struct usbd_urb_header uvc_hdr; + void *uvc_reserved0; + uint32_t uvc_trans_flags; +#define USBD_TRANSFER_DIRECTION_IN 1 + uint32_t uvc_trans_buflen; + void *uvc_trans_buf; + struct mdl *uvc_mdl; + union nt_urb *uvc_urblink; + struct usbd_hcd_area uvc_hca; + uint8_t uvc_reserved1; + uint8_t uvc_req; + uint16_t uvc_value; + uint16_t uvc_idx; + uint16_t uvc_reserved2; +}; + +struct usbd_interface_list_entry { + usb_interface_descriptor_t *uil_intfdesc; + struct usbd_interface_information *uil_intf; +}; + +union usbd_urb { + struct usbd_urb_header uu_hdr; + struct usbd_urb_select_configuration uu_selconf; + struct usbd_urb_bulk_or_intr_transfer uu_bulkintr; + struct usbd_urb_control_descriptor_request uu_ctldesc; + struct usbd_urb_vendor_or_class_request uu_vcreq; + struct usbd_urb_pipe_request uu_pipe; +}; + +#define USBD_URB_STATUS(urb) ((urb)->uu_hdr.uuh_status) + +#define USBDI_VERSION 0x00000500 +#define USB_VER_1_1 0x00000110 +#define USB_VER_2_0 0x00000200 + +struct usbd_version_info { + uint32_t uvi_usbdi_vers; + uint32_t uvi_supported_vers; +}; + +typedef struct usbd_version_info usbd_version_info; + +extern image_patch_table usbd_functbl[]; + +__BEGIN_DECLS +extern int usbd_libinit(void); +extern int usbd_libfini(void); +__END_DECLS + +#endif /* _USBD_VAR_H_ */ diff --git a/sys/platform/pc32/conf/files b/sys/platform/pc32/conf/files index 33491d4b66..3f74c2211d 100644 --- a/sys/platform/pc32/conf/files +++ b/sys/platform/pc32/conf/files @@ -75,6 +75,7 @@ emulation/ndis/subr_ndis.c optional ndisapi pci emulation/ndis/subr_ntoskrnl.c optional ndisapi pci emulation/ndis/subr_pe.c optional ndisapi pci emulation/ndis/subr_usbd.c optional ndisapi pci usb +emulation/ndis/subr_u4bd.c optional ndisapi pci usb4bsd emulation/ndis/winx32_wrap.S optional ndisapi pci # bf_enc.o optional ipsec ipsec_esp \ @@ -144,6 +145,7 @@ dev/netif/ndis/if_ndis_pccard.c optional ndis pccard dev/netif/ndis/if_ndis_pci.c optional ndis cardbus dev/netif/ndis/if_ndis_pci.c optional ndis pci dev/netif/ndis/if_ndis_usb.c optional ndis usb +dev/netif/ndis/if_ndis_u4b.c optional ndis usb4bsd dev/misc/kbd/atkbd.c optional atkbd dev/misc/kbd/atkbdc.c optional atkbdc dev/misc/kbd/kbd.c optional atkbd diff --git a/sys/platform/pc64/conf/files b/sys/platform/pc64/conf/files index 1fd45cf336..507d4cb89f 100644 --- a/sys/platform/pc64/conf/files +++ b/sys/platform/pc64/conf/files @@ -68,6 +68,7 @@ emulation/ndis/subr_ndis.c optional ndisapi pci emulation/ndis/subr_ntoskrnl.c optional ndisapi pci emulation/ndis/subr_pe.c optional ndisapi pci emulation/ndis/subr_usbd.c optional ndisapi pci usb +emulation/ndis/subr_u4bd.c optional ndisapi pci usb4bsd emulation/ndis/winx64_wrap.S optional ndisapi pci # XXX use assembler versions of bf_enc and des_enc @@ -122,6 +123,7 @@ dev/netif/ndis/if_ndis_pccard.c optional ndis pccard dev/netif/ndis/if_ndis_pci.c optional ndis cardbus dev/netif/ndis/if_ndis_pci.c optional ndis pci dev/netif/ndis/if_ndis_usb.c optional ndis usb +dev/netif/ndis/if_ndis_u4b.c optional ndis usb4bsd dev/misc/kbd/atkbd.c optional atkbd dev/misc/kbd/atkbdc.c optional atkbdc dev/misc/kbd/kbd.c optional atkbd -- 2.11.4.GIT