Index: share/man/man4/ndis.4 =================================================================== --- share/man/man4/ndis.4 (revision 186411) +++ share/man/man4/ndis.4 (working copy) @@ -105,7 +105,7 @@ The .Nm driver is designed to support mainly Ethernet and wireless -network devices with PCI and PCMCIA bus attachments. +network devices with PCI, PCMCIA and USB bus attachments. (Cardbus devices are also supported as a subset of PCI.) It can Index: usr.sbin/ndiscvt/windrv_stub.c =================================================================== --- usr.sbin/ndiscvt/windrv_stub.c (revision 186411) +++ usr.sbin/ndiscvt/windrv_stub.c (working copy) @@ -69,6 +69,11 @@ char *ndis_name; }; +struct ndis_usb_type { + uint16_t ndis_vid; + uint16_t ndis_did; + char *ndis_name; +}; #ifdef NDIS_PCI_DEV_TABLE static struct ndis_pci_type ndis_devs_pci[] = { @@ -84,6 +89,13 @@ }; #endif +#ifdef NDIS_USB_DEV_TABLE +static struct ndis_usb_type ndis_devs_usb[] = { + NDIS_USB_DEV_TABLE + { 0, 0, NULL } +}; +#endif + enum interface_type { InterfaceTypeUndefined = -1, Internal, @@ -224,6 +236,10 @@ windrv_load(mod, drv_data_start, drv_data_len, PCMCIABus, ndis_devs_pccard, &ndis_regvals); #endif +#ifdef NDIS_USB_DEV_TABLE + windrv_load(mod, drv_data_start, drv_data_len, PNPBus, + ndis_devs_usb, &ndis_regvals); +#endif break; case MOD_UNLOAD: windrv_loaded--; @@ -235,6 +251,9 @@ #ifdef NDIS_PCMCIA_DEV_TABLE windrv_unload(mod, drv_data_start, drv_data_len); #endif +#ifdef NDIS_USB_DEV_TABLE + windrv_unload(mod, drv_data_start, drv_data_len); +#endif break; case MOD_SHUTDOWN: break; Index: usr.sbin/ndiscvt/inf.c =================================================================== --- usr.sbin/ndiscvt/inf.c (revision 186411) +++ usr.sbin/ndiscvt/inf.c (working copy) @@ -62,8 +62,10 @@ *find_section (const char *); static void dump_deviceids_pci (void); static void dump_deviceids_pcmcia (void); +static void dump_deviceids_usb (void); static void dump_pci_id (const char *); static void dump_pcmcia_id (const char *); +static void dump_usb_id (const char *); static void dump_regvals (void); static void dump_paramreg (const struct section *, const struct reg *, int); @@ -83,6 +85,7 @@ dump_deviceids_pci(); dump_deviceids_pcmcia(); + dump_deviceids_usb(); fprintf(outfp, "#ifdef NDIS_REGVALS\n"); dump_regvals(); fprintf(outfp, "#endif /* NDIS_REGVALS */\n"); @@ -252,6 +255,30 @@ } static void +dump_usb_id(const char *s) +{ + char *p; + char vidstr[7], pidstr[7]; + + p = strcasestr(s, "VID_"); + if (p == NULL) + return; + p += 4; + strcpy(vidstr, "0x"); + strncat(vidstr, p, 4); + p = strcasestr(s, "PID_"); + if (p == NULL) + return; + p += 4; + strcpy(pidstr, "0x"); + strncat(pidstr, p, 4); + if (p == NULL) + return; + + fprintf(ofp, "\t\\\n\t{ %s, %s, ", vidstr, pidstr); +} + +static void dump_deviceids_pci() { struct assign *manf, *dev; @@ -438,6 +465,99 @@ } static void +dump_deviceids_usb() +{ + struct assign *manf, *dev; + struct section *sec; + struct assign *assign; + char xpsec[256]; + int first = 1, found = 0; + + /* Find manufacturer name */ + manf = find_assign("Manufacturer", NULL); + +nextmanf: + + /* Find manufacturer section */ + if (manf->vals[1] != NULL && + (strcasecmp(manf->vals[1], "NT.5.1") == 0 || + strcasecmp(manf->vals[1], "NTx86") == 0 || + strcasecmp(manf->vals[1], "NTx86.5.1") == 0 || + strcasecmp(manf->vals[1], "NTamd64") == 0)) { + /* Handle Windows XP INF files. */ + snprintf(xpsec, sizeof(xpsec), "%s.%s", + manf->vals[0], manf->vals[1]); + sec = find_section(xpsec); + } else + sec = find_section(manf->vals[0]); + + /* See if there are any USB device definitions. */ + + TAILQ_FOREACH(assign, &ah, link) { + if (assign->section == sec) { + dev = find_assign("strings", assign->key); + if (strcasestr(assign->vals[1], "USB") != NULL) { + found++; + break; + } + } + } + + if (found == 0) + goto done; + + found = 0; + + if (first == 1) { + /* Emit start of USB device table */ + fprintf (ofp, "#define NDIS_USB_DEV_TABLE"); + first = 0; + } + +retry: + + /* + * Now run through all the device names listed + * in the manufacturer section and dump out the + * device descriptions and vendor/device IDs. + */ + + TAILQ_FOREACH(assign, &ah, link) { + if (assign->section == sec) { + dev = find_assign("strings", assign->key); + /* Emit device IDs. */ + if (strcasestr(assign->vals[1], "USB") != NULL) + dump_usb_id(assign->vals[1]); + else + continue; + /* Emit device description */ + fprintf (ofp, "\t\\\n\t\"%s\" },", dev->vals[0]); + found++; + } + } + + /* Someone tried to fool us. Shame on them. */ + if (!found) { + found++; + sec = find_section(manf->vals[0]); + goto retry; + } + + /* Handle Manufacturer sections with multiple entries. */ + manf = find_next_assign(manf); + + if (manf != NULL) + goto nextmanf; + +done: + /* Emit end of table */ + + fprintf(ofp, "\n\n"); + + return; +} + +static void dump_addreg(const char *s, int devidx) { struct section *sec; Index: sys/modules/ndis/Makefile =================================================================== --- sys/modules/ndis/Makefile (revision 186411) +++ sys/modules/ndis/Makefile (working copy) @@ -6,6 +6,7 @@ SRCS= subr_pe.c subr_ndis.c subr_hal.c subr_ntoskrnl.c kern_ndis.c SRCS+= kern_windrv.c subr_usbd.c SRCS+= device_if.h bus_if.h pci_if.h vnode_if.h +SRCS+= opt_usb.h usbdevs.h .if ${MACHINE_ARCH} == "amd64" SRCS+= winx64_wrap.S Index: sys/dev/if_ndis/if_ndis_usb.c =================================================================== --- sys/dev/if_ndis/if_ndis_usb.c (revision 186411) +++ sys/dev/if_ndis/if_ndis_usb.c (working copy) @@ -65,12 +65,16 @@ #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 *); @@ -86,7 +90,7 @@ /* Device interface */ DEVMETHOD(device_probe, ndisusb_match), DEVMETHOD(device_attach, ndisusb_attach), - DEVMETHOD(device_detach, ndis_detach), + DEVMETHOD(device_detach, ndisusb_detach), DEVMETHOD(device_shutdown, ndis_shutdown), /* bus interface */ @@ -108,8 +112,31 @@ 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->vendor == t->ndis_vid) && + (uaa->product == 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 (windrv_lookup(0, "USB Bus") == NULL) @@ -118,35 +145,103 @@ if (uaa->iface != NULL) return (UMATCH_NONE); - return (UMATCH_NONE); + db = windrv_match((matchfuncptr)ndisusb_devcompare, self); + if (db == NULL) + return (UMATCH_NONE); + + return (UMATCH_VENDOR_PRODUCT); } static int ndisusb_attach(device_t self) { + 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; + usbd_status status; sc = (struct ndis_softc *)dummy; if (uaa->device == NULL) return ENXIO; + db = windrv_match((matchfuncptr)ndisusb_devcompare, self); + if (db == NULL) + return (ENXIO); + sc->ndis_dev = self; + sc->ndis_dobj = db->windrv_object; + sc->ndis_regvals = db->windrv_regvals; + sc->ndis_iftype = PNPBus; /* Create PDO for this device instance */ drv = windrv_lookup(0, "USB Bus"); windrv_create_pdo(drv, self); + status = usbd_set_config_no(uaa->device, NDISUSB_CONFIG_NO, 0); + if (status != USBD_NORMAL_COMPLETION) { + device_printf(self, "setting config no failed\n"); + return (ENXIO); + } + + /* Figure out exactly which device we matched. */ + + t = db->windrv_devlist; + + while (t->ndis_name != NULL) { + if ((uaa->vendor == t->ndis_vid) && + (uaa->product == t->ndis_did)) { + sc->ndis_devidx = devidx; + break; + } + t++; + devidx++; + } + if (ndis_attach(self) != 0) return ENXIO; + usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, uaa->device, self); + return 0; } +static int +ndisusb_detach(device_t self) +{ + int i; + struct ndis_softc *sc = device_get_softc(self); + struct usb_attach_arg *uaa = device_get_ivars(self); + + sc->ndisusb_status |= NDISUSB_STATUS_DETACH; + + for (i = 0; i < NDISUSB_ENDPT_MAX; i++) { + if (sc->ndisusb_ep[i] == NULL) + continue; + + usbd_abort_pipe(sc->ndisusb_ep[i]); + usbd_close_pipe(sc->ndisusb_ep[i]); + sc->ndisusb_ep[i] = NULL; + } + + if (sc->ndisusb_iin_buf != NULL) { + free(sc->ndisusb_iin_buf, M_USBDEV); + sc->ndisusb_iin_buf = NULL; + } + + ndis_pnpevent_nic(self, NDIS_PNP_EVENT_SURPRISE_REMOVED); + ndis_cancel_timerlist(); + + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, uaa->device, self); + + return ndis_detach(self); +} + static struct resource_list * ndis_get_resource_list(device_t dev, device_t child) { Index: sys/dev/if_ndis/if_ndis_pci.c =================================================================== --- sys/dev/if_ndis/if_ndis_pci.c (revision 186411) +++ sys/dev/if_ndis/if_ndis_pci.c (working copy) @@ -54,6 +54,8 @@ #include #include +#include +#include #include #include Index: sys/dev/if_ndis/if_ndis_pccard.c =================================================================== --- sys/dev/if_ndis/if_ndis_pccard.c (revision 186411) +++ sys/dev/if_ndis/if_ndis_pccard.c (working copy) @@ -53,6 +53,9 @@ #include +#include +#include + #include #include #include Index: sys/dev/if_ndis/if_ndisvar.h =================================================================== --- sys/dev/if_ndis/if_ndisvar.h (revision 186411) +++ sys/dev/if_ndis/if_ndisvar.h (working copy) @@ -58,6 +58,12 @@ char *ndis_name; }; +struct ndis_usb_type { + uint16_t ndis_vid; + uint16_t ndis_did; + char *ndis_name; +}; + struct ndis_shmem { list_entry ndis_list; bus_dma_tag_t ndis_stag; @@ -107,6 +113,17 @@ }; #define NDIS_VAP(vap) ((struct ndis_vap *)(vap)) +#define NDISUSB_CONFIG_NO 1 +#define NDISUSB_IFACE_INDEX 0 +#define NDISUSB_INTR_TIMEOUT 1000 +#define NDISUSB_TX_TIMEOUT 10000 +struct ndisusb_xfer { + usbd_xfer_handle nx_xfer; + usbd_private_handle nx_priv; + usbd_status nx_status; + list_entry nx_xferlist; +}; + struct ndis_softc { struct ifnet *ifp; struct ifmedia ifmedia; /* media info */ @@ -183,7 +200,32 @@ enum ieee80211_state, int); int ndis_tx_timer; int ndis_hang_timer; + + io_workitem *ndisusb_xferitem; + list_entry ndisusb_xferlist; + kspin_lock ndisusb_xferlock; +#define NDISUSB_ENDPT_BOUT 0 +#define NDISUSB_ENDPT_BIN 1 +#define NDISUSB_ENDPT_IIN 2 +#define NDISUSB_ENDPT_IOUT 3 +#define NDISUSB_ENDPT_MAX 4 + usbd_pipe_handle ndisusb_ep[NDISUSB_ENDPT_MAX]; + char *ndisusb_iin_buf; + int ndisusb_status; +#define NDISUSB_STATUS_DETACH 0x1 }; -#define NDIS_LOCK(_sc) mtx_lock(&(_sc)->ndis_mtx) -#define NDIS_UNLOCK(_sc) mtx_unlock(&(_sc)->ndis_mtx) +#define NDISMTX_LOCK(_sc) mtx_lock(&(_sc)->ndis_mtx) +#define NDISMTX_UNLOCK(_sc) mtx_unlock(&(_sc)->ndis_mtx) +#define NDISUSB_LOCK(_sc) mtx_lock(&Giant) +#define NDISUSB_UNLOCK(_sc) mtx_unlock(&Giant) +#define NDIS_LOCK(_sc) do { \ + if ((_sc)->ndis_iftype == PNPBus) \ + NDISUSB_LOCK(_sc); \ + NDISMTX_LOCK(_sc); \ +} while (0) +#define NDIS_UNLOCK(_sc) do { \ + if ((_sc)->ndis_iftype == PNPBus) \ + NDISUSB_UNLOCK(_sc); \ + NDISMTX_UNLOCK(_sc); \ +} while (0) Index: sys/dev/if_ndis/if_ndis.c =================================================================== --- sys/dev/if_ndis/if_ndis.c (revision 186411) +++ sys/dev/if_ndis/if_ndis.c (working copy) @@ -74,6 +74,8 @@ #include #include +#include +#include #include #include @@ -81,6 +83,7 @@ #include #include #include +#include #include #define NDIS_DEBUG @@ -93,6 +96,11 @@ #define DPRINTF(x) #endif +SYSCTL_DECL(_hw_ndisusb); +int ndisusb_halt = 1; +SYSCTL_INT(_hw_ndisusb, OID_AUTO, halt, CTLFLAG_RW, &ndisusb_halt, 0, + "Halt NDIS USB driver when it's attached"); + MODULE_DEPEND(ndis, ether, 1, 1, 1); MODULE_DEPEND(ndis, wlan, 1, 1, 1); MODULE_DEPEND(ndis, ndisapi, 1, 1, 1); @@ -538,7 +546,9 @@ mtx_init(&sc->ndis_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); KeInitializeSpinLock(&sc->ndis_rxlock); + KeInitializeSpinLock(&sc->ndisusb_xferlock); InitializeListHead(&sc->ndis_shlist); + InitializeListHead(&sc->ndisusb_xferlist); callout_init(&sc->ndis_stat_callout, CALLOUT_MPSAFE); if (sc->ndis_iftype == PCMCIABus) { @@ -602,6 +612,7 @@ 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); + sc->ndisusb_xferitem = IoAllocateWorkItem(sc->ndis_block->nmb_deviceobj); KeInitializeDpc(&sc->ndis_rxdpc, ndis_rxeof_xfr_wrap, sc->ndis_block); /* Call driver's init routine. */ @@ -696,6 +707,8 @@ if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + if (sc->ndis_iftype == PNPBus) + ifp->if_flags |= IFF_NEEDSGIANT; ifp->if_ioctl = ndis_ioctl; ifp->if_start = ndis_start; ifp->if_init = ndis_init; @@ -934,12 +947,17 @@ } fail: - if (error) + if (error) { ndis_detach(dev); - else - /* We're done talking to the NIC for now; halt it. */ - ndis_halt_nic(sc); + return (error); + } + if (sc->ndis_iftype == PNPBus && ndisusb_halt == 0) + return (error); + + /* We're done talking to the NIC for now; halt it. */ + ndis_halt_nic(sc); + return(error); } @@ -1029,6 +1047,8 @@ IoFreeWorkItem(sc->ndis_resetitem); if (sc->ndis_inputitem != NULL) IoFreeWorkItem(sc->ndis_inputitem); + if (sc->ndisusb_xferitem != NULL) + IoFreeWorkItem(sc->ndisusb_xferitem); bus_generic_detach(dev); ndis_unload_driver(sc); @@ -1538,7 +1558,7 @@ ndis_free_packet(packet); m_freem(m); - NDIS_LOCK(sc); + NDISMTX_LOCK(sc); sc->ndis_txarray[idx] = NULL; sc->ndis_txpending++; @@ -1550,7 +1570,7 @@ sc->ndis_tx_timer = 0; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - NDIS_UNLOCK(sc); + NDISMTX_UNLOCK(sc); IoQueueWorkItem(sc->ndis_startitem, (io_workitem_func)ndis_starttask_wrap, @@ -1575,9 +1595,9 @@ /* Event list is all full up, drop this one. */ - NDIS_LOCK(sc); + NDISMTX_LOCK(sc); if (sc->ndis_evt[sc->ndis_evtpidx].ne_sts) { - NDIS_UNLOCK(sc); + NDISMTX_UNLOCK(sc); return; } @@ -1587,7 +1607,7 @@ sc->ndis_evt[sc->ndis_evtpidx].ne_buf = malloc(slen, M_TEMP, M_NOWAIT); if (sc->ndis_evt[sc->ndis_evtpidx].ne_buf == NULL) { - NDIS_UNLOCK(sc); + NDISMTX_UNLOCK(sc); return; } bcopy((char *)sbuf, @@ -1596,7 +1616,7 @@ sc->ndis_evt[sc->ndis_evtpidx].ne_sts = status; sc->ndis_evt[sc->ndis_evtpidx].ne_len = slen; NDIS_EVTINC(sc->ndis_evtpidx); - NDIS_UNLOCK(sc); + NDISMTX_UNLOCK(sc); return; } @@ -1966,8 +1986,14 @@ */ ndis_stop(sc); - if (ndis_init_nic(sc)) - return; + if (!(sc->ndis_iftype == PNPBus && ndisusb_halt == 0)) { + error = ndis_init_nic(sc); + if (error != 0) { + device_printf(sc->ndis_dev, + "failed to initialize the device: %d\n", error); + return; + } + } /* Init our MAC address */ @@ -2688,6 +2714,8 @@ ifp = sc->ifp; ic = ifp->if_l2com; vap = TAILQ_FIRST(&ic->ic_vaps); + if (vap == NULL) + return; ni = vap->iv_bss; if (!NDIS_INITIALIZED(sc)) @@ -3173,7 +3201,9 @@ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); NDIS_UNLOCK(sc); - ndis_halt_nic(sc); + if (!(sc->ndis_iftype == PNPBus && ndisusb_halt == 0) || + sc->ndisusb_status & NDISUSB_STATUS_DETACH) + ndis_halt_nic(sc); NDIS_LOCK(sc); for (i = 0; i < NDIS_EVENTS; i++) { Index: sys/compat/ndis/subr_ntoskrnl.c =================================================================== --- sys/compat/ndis/subr_ntoskrnl.c (revision 186411) +++ sys/compat/ndis/subr_ntoskrnl.c (working copy) @@ -207,7 +207,6 @@ static void *MmMapLockedPagesSpecifyCache(mdl *, uint8_t, uint32_t, void *, uint32_t, uint32_t); static void MmUnmapLockedPages(void *, mdl *); -static uint8_t MmIsAddressValid(void *); static device_t ntoskrnl_finddev(device_t, uint64_t, struct resource **); static void RtlZeroMemory(void *, size_t); static void RtlCopyMemory(void *, const void *, size_t); @@ -251,6 +250,8 @@ static uint32_t DbgPrint(char *, ...); static void DbgBreakPoint(void); static void KeBugCheckEx(uint32_t, u_long, u_long, u_long, u_long); +static int32_t KeDelayExecutionThread(uint8_t, uint8_t, int64_t *); +static int32_t KeSetPriorityThread(struct thread *, int32_t); static void dummy(void); static struct mtx ntoskrnl_dispatchlock; @@ -1143,16 +1144,18 @@ IoCancelIrp(irp *ip) { cancel_func cfunc; + uint8_t cancelirql; - IoAcquireCancelSpinLock(&ip->irp_cancelirql); + IoAcquireCancelSpinLock(&cancelirql); cfunc = IoSetCancelRoutine(ip, NULL); ip->irp_cancel = TRUE; - if (ip->irp_cancelfunc == NULL) { - IoReleaseCancelSpinLock(ip->irp_cancelirql); + if (cfunc == NULL) { + IoReleaseCancelSpinLock(cancelirql); return(FALSE); } + ip->irp_cancelirql = cancelirql; MSCALL2(cfunc, IoGetCurrentIrpStackLocation(ip)->isl_devobj, ip); - return(TRUE); + return (uint8_t)IoSetCancelValue(ip, TRUE); } uint32_t @@ -1186,24 +1189,27 @@ irp *ip; uint8_t prioboost; { - uint32_t i; uint32_t status; device_object *dobj; io_stack_location *sl; completion_func cf; - ip->irp_pendingreturned = - IoGetCurrentIrpStackLocation(ip)->isl_ctl & SL_PENDING_RETURNED; - sl = (io_stack_location *)(ip + 1); + KASSERT(ip->irp_iostat.isb_status != STATUS_PENDING, + ("incorrect IRP(%p) status (STATUS_PENDING)", ip)); - for (i = ip->irp_currentstackloc; i < (uint32_t)ip->irp_stackcnt; i++) { - if (ip->irp_currentstackloc < ip->irp_stackcnt - 1) { - IoSkipCurrentIrpStackLocation(ip); + sl = IoGetCurrentIrpStackLocation(ip); + IoSkipCurrentIrpStackLocation(ip); + + do { + if (sl->isl_ctl & SL_PENDING_RETURNED) + ip->irp_pendingreturned = TRUE; + + if (ip->irp_currentstackloc != (ip->irp_stackcnt + 1)) dobj = IoGetCurrentIrpStackLocation(ip)->isl_devobj; - } else + else dobj = NULL; - if (sl[i].isl_completionfunc != NULL && + if (sl->isl_completionfunc != NULL && ((ip->irp_iostat.isb_status == STATUS_SUCCESS && sl->isl_ctl & SL_INVOKE_ON_SUCCESS) || (ip->irp_iostat.isb_status != STATUS_SUCCESS && @@ -1214,12 +1220,16 @@ status = MSCALL3(cf, dobj, ip, sl->isl_completionctx); if (status == STATUS_MORE_PROCESSING_REQUIRED) return; + } else { + if ((ip->irp_currentstackloc <= ip->irp_stackcnt) && + (ip->irp_pendingreturned == TRUE)) + IoMarkIrpPending(ip); } - if (IoGetCurrentIrpStackLocation(ip)->isl_ctl & - SL_PENDING_RETURNED) - ip->irp_pendingreturned = TRUE; - } + /* move to the next. */ + IoSkipCurrentIrpStackLocation(ip); + sl++; + } while (ip->irp_currentstackloc <= (ip->irp_stackcnt + 1)); /* Handle any associated IRPs. */ @@ -2672,7 +2682,7 @@ * here, but it doesn't. */ -static uint8_t +uint8_t MmIsAddressValid(vaddr) void *vaddr; { @@ -4258,6 +4268,73 @@ return(timer->k_header.dh_sigstate); } +static int32_t +KeDelayExecutionThread(wait_mode, alertable, interval) + uint8_t wait_mode; + uint8_t alertable; + int64_t *interval; +{ + ktimer timer; + + if (wait_mode != 0) + panic("invalid wait_mode %d", wait_mode); + + KeInitializeTimer(&timer); + KeSetTimer(&timer, *interval, NULL); + KeWaitForSingleObject(&timer, 0, 0, alertable, NULL); + + return STATUS_SUCCESS; +} + +static uint64_t +KeQueryInterruptTime(void) +{ + int ticks; + struct timeval tv; + + getmicrouptime(&tv); + + ticks = tvtohz(&tv); + + return ticks * ((10000000 + hz - 1) / hz); +} + +static struct thread * +KeGetCurrentThread(void) +{ + + return curthread; +} + +static int32_t +KeSetPriorityThread(td, pri) + struct thread *td; + int32_t pri; +{ + int32_t old; + + if (td == NULL) + return LOW_REALTIME_PRIORITY; + + if (td->td_priority <= PRI_MIN_KERN) + old = HIGH_PRIORITY; + else if (td->td_priority >= PRI_MAX_KERN) + old = LOW_PRIORITY; + else + old = LOW_REALTIME_PRIORITY; + + thread_lock(td); + if (pri == HIGH_PRIORITY) + sched_prio(td, PRI_MIN_KERN); + if (pri == LOW_REALTIME_PRIORITY) + sched_prio(td, PRI_MIN_KERN + (PRI_MAX_KERN - PRI_MIN_KERN) / 2); + if (pri == LOW_PRIORITY) + sched_prio(td, PRI_MAX_KERN); + thread_unlock(td); + + return old; +} + static void dummy() { @@ -4441,6 +4518,10 @@ IMPORT_CFUNC(WmiTraceMessage, 0), IMPORT_SFUNC(KeQuerySystemTime, 1), IMPORT_CFUNC(KeTickCount, 0), + IMPORT_SFUNC(KeDelayExecutionThread, 3), + IMPORT_SFUNC(KeQueryInterruptTime, 0), + IMPORT_SFUNC(KeGetCurrentThread, 0), + IMPORT_SFUNC(KeSetPriorityThread, 2), /* * This last entry is a catch-all for any function we haven't Index: sys/compat/ndis/ndis_var.h =================================================================== --- sys/compat/ndis/ndis_var.h (revision 186411) +++ sys/compat/ndis/ndis_var.h (working copy) @@ -1658,6 +1658,7 @@ typedef void (*ndis_enable_interrupts_handler)(ndis_handle); typedef void (*ndis_disable_interrupts_handler)(ndis_handle); typedef void (*ndis_shutdown_handler)(void *); +typedef void (*ndis_pnpevent_handler)(void *, int, void *, uint32_t); typedef void (*ndis_allocdone_handler)(ndis_handle, void *, ndis_physaddr *, uint32_t, void *); typedef uint8_t (*ndis_checkforhang_handler)(ndis_handle); @@ -1739,6 +1740,7 @@ extern int ndis_reset_nic(void *); extern int ndis_halt_nic(void *); extern int ndis_shutdown_nic(void *); +extern int ndis_pnpevent_nic(void *, int); extern int ndis_init_nic(void *); extern void ndis_return_packet(void *, void *); extern int ndis_init_dma(void *); @@ -1759,6 +1761,7 @@ extern void NdisFreePacket(ndis_packet *); extern ndis_status NdisScheduleWorkItem(ndis_work_item *); extern void NdisMSleep(uint32_t); +extern void ndis_cancel_timerlist(void); __END_DECLS #endif /* _NDIS_VAR_H_ */ Index: sys/compat/ndis/usbd_var.h =================================================================== --- sys/compat/ndis/usbd_var.h (revision 186411) +++ sys/compat/ndis/usbd_var.h (working copy) @@ -35,10 +35,169 @@ #ifndef _USBD_VAR_H_ #define _USBD_VAR_H_ -#define USBDI_VERSION 0x00000500 -#define USB_VER_1_1 0x00000110 -#define USB_VER_2_0 0x00000200 +#define IOCTL_INTERNAL_USB_SUBMIT_URB 0x00220003 +#define URB_FUNCTION_SELECT_CONFIGURATION 0x0000 +#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_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; +}; + +#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; @@ -46,6 +205,13 @@ typedef struct usbd_version_info usbd_version_info; +/* used for IRP cancel. */ +struct ndisusb_cancel { + device_t dev; + usbd_xfer_handle xfer; + struct usb_task task; +}; + extern image_patch_table usbd_functbl[]; __BEGIN_DECLS Index: sys/compat/ndis/kern_ndis.c =================================================================== --- sys/compat/ndis/kern_ndis.c (revision 186411) +++ sys/compat/ndis/kern_ndis.c (working copy) @@ -65,6 +65,9 @@ #include #include +#include +#include + #include #include #include @@ -144,7 +147,6 @@ } TAILQ_INIT(&ndis_devhead); - break; case MOD_SHUTDOWN: if (TAILQ_FIRST(&ndis_devhead) == NULL) { @@ -1199,6 +1201,31 @@ } int +ndis_pnpevent_nic(arg, type) + void *arg; + int type; +{ + struct ndis_softc *sc; + ndis_handle adapter; + ndis_pnpevent_handler pnpeventfunc; + + sc = arg; + NDIS_LOCK(sc); + adapter = sc->ndis_block->nmb_miniportadapterctx; + pnpeventfunc = sc->ndis_chars->nmc_pnpevent_handler; + NDIS_UNLOCK(sc); + if (adapter == NULL || pnpeventfunc == NULL) + return(EIO); + + if (sc->ndis_chars->nmc_rsvd0 == NULL) + MSCALL4(pnpeventfunc, adapter, type, NULL, 0); + else + MSCALL4(pnpeventfunc, sc->ndis_chars->nmc_rsvd0, type, NULL, 0); + + return (0); +} + +int ndis_init_nic(arg) void *arg; { Index: sys/compat/ndis/ntoskrnl_var.h =================================================================== --- sys/compat/ndis/ntoskrnl_var.h (revision 186411) +++ sys/compat/ndis/ntoskrnl_var.h (working copy) @@ -536,6 +536,11 @@ #define WAITKEY_VALID 0x8000 +/* kthread priority */ +#define LOW_PRIORITY 0 +#define LOW_REALTIME_PRIORITY 16 +#define HIGH_PRIORITY 31 + struct thread_context { void *tc_thrctx; void *tc_thrfunc; @@ -989,7 +994,13 @@ } s2; void *irp_fileobj; } irp_overlay; - kapc irp_apc; + union { + kapc irp_apc; + struct { + void *irp_xfer; + void *irp_dev; + } irp_usb; + } irp_misc; void *irp_compkey; } irp_tail; }; @@ -997,6 +1008,9 @@ #define irp_csl s2.u2.irp_csl #define irp_pkttype s2.u2.irp_pkttype +#define IRP_NDIS_DEV(irp) (irp)->irp_tail.irp_misc.irp_usb.irp_dev +#define IRP_NDISUSB_XFER(irp) (irp)->irp_tail.irp_misc.irp_usb.irp_xfer + typedef struct irp irp; #define InterlockedExchangePointer(dst, val) \ @@ -1009,6 +1023,10 @@ (cancel_func)InterlockedExchangePointer( \ (void *)&(ip)->irp_cancelfunc, (void *)(func)) +#define IoSetCancelValue(irp, val) \ + (uint32_t)InterlockedExchangePointer( \ + (void *)&(ip)->irp_cancel, (void *)(val)) + #define IoGetCurrentIrpStackLocation(irp) \ (irp)->irp_tail.irp_overlay.irp_csl @@ -1035,6 +1053,8 @@ #define IoMarkIrpPending(irp) \ IoGetCurrentIrpStackLocation(irp)->isl_ctl |= SL_PENDING_RETURNED +#define IoUnmarkIrpPending(irp) \ + IoGetCurrentIrpStackLocation(irp)->isl_ctl &= ~SL_PENDING_RETURNED #define IoCopyCurrentIrpStackLocationToNext(irp) \ do { \ @@ -1191,14 +1211,21 @@ #define STATUS_ALERTED 0x00000101 #define STATUS_TIMEOUT 0x00000102 #define STATUS_PENDING 0x00000103 +#define STATUS_FAILURE 0xC0000001 +#define STATUS_NOT_IMPLEMENTED 0xC0000002 #define STATUS_INVALID_PARAMETER 0xC000000D #define STATUS_INVALID_DEVICE_REQUEST 0xC0000010 #define STATUS_MORE_PROCESSING_REQUIRED 0xC0000016 +#define STATUS_NO_MEMORY 0xC0000017 #define STATUS_BUFFER_TOO_SMALL 0xC0000023 #define STATUS_MUTANT_NOT_OWNED 0xC0000046 +#define STATUS_NOT_SUPPORTED 0xC00000BB #define STATUS_INVALID_PARAMETER_2 0xC00000F0 #define STATUS_INSUFFICIENT_RESOURCES 0xC000009A +#define STATUS_DEVICE_NOT_CONNECTED 0xC000009D +#define STATUS_CANCELLED 0xC0000120 #define STATUS_NOT_FOUND 0xC0000225 +#define STATUS_DEVICE_REMOVED 0xC00002B6 #define STATUS_WAIT_0 0x00000000 @@ -1365,6 +1392,7 @@ extern uint32_t IoConnectInterrupt(kinterrupt **, void *, void *, kspin_lock *, uint32_t, uint8_t, uint8_t, uint8_t, uint8_t, uint32_t, uint8_t); +extern uint8_t MmIsAddressValid(void *); extern void *MmMapIoSpace(uint64_t, uint32_t, uint32_t); extern void MmUnmapIoSpace(void *, size_t); extern void MmBuildMdlForNonPagedPool(mdl *); Index: sys/compat/ndis/kern_windrv.c =================================================================== --- sys/compat/ndis/kern_windrv.c (revision 186411) +++ sys/compat/ndis/kern_windrv.c (working copy) @@ -56,6 +56,9 @@ #include #endif +#include +#include + #include #include #include @@ -349,9 +352,11 @@ if (pe_patch_imports(img, "NDIS", ndis_functbl)) return(ENOEXEC); - /* Dynamically link the HAL.dll routines -- also required. */ - if (pe_patch_imports(img, "HAL", hal_functbl)) - return(ENOEXEC); + /* Dynamically link the HAL.dll routines -- optional. */ + if (pe_get_import_descriptor(img, &imp_desc, "HAL") == 0) { + if (pe_patch_imports(img, "HAL", hal_functbl)) + return(ENOEXEC); + } /* Dynamically link ntoskrnl.exe -- optional. */ if (pe_get_import_descriptor(img, &imp_desc, "ntoskrnl") == 0) { Index: sys/compat/ndis/subr_ndis.c =================================================================== --- sys/compat/ndis/subr_ndis.c (revision 186411) +++ sys/compat/ndis/subr_ndis.c (working copy) @@ -95,6 +95,8 @@ #include #include +#include +#include #include #include @@ -302,6 +304,15 @@ */ #define NDIS_POOL_EXTRA 16 +struct ktimer_list { + ktimer *kl_timer; + list_entry kl_next; +}; + +static struct list_entry ndis_timerlist; +static kspin_lock ndis_timerlock; +static int ndis_isusbdev; + int ndis_libinit() { @@ -317,6 +328,9 @@ patch++; } + KeInitializeSpinLock(&ndis_timerlock); + InitializeListHead(&ndis_timerlist); + return(0); } @@ -1215,6 +1229,16 @@ ndis_timer_function func; void *ctx; { + ndis_miniport_block *block; + struct ktimer_list *kl; + struct ndis_softc *sc; + uint8_t irql; + + block = (ndis_miniport_block *)handle; + sc = device_get_softc(block->nmb_physdeviceobj->do_devext); + if (sc->ndis_iftype == PNPBus && ndis_isusbdev == 0) + ndis_isusbdev = 1; + /* Save the driver's funcptr and context */ timer->nmt_timerfunc = func; @@ -1232,9 +1256,40 @@ ndis_findwrap((funcptr)ndis_timercall), timer); timer->nmt_ktimer.k_dpc = &timer->nmt_kdpc; - return; + if (ndis_isusbdev == 1) { + kl = (struct ktimer_list *)malloc(sizeof(*kl), M_DEVBUF, + M_NOWAIT | M_ZERO); + if (kl == NULL) + panic("out of memory"); /* no way to report errors */ + + kl->kl_timer = &timer->nmt_ktimer; + KeAcquireSpinLock(&ndis_timerlock, &irql); + InsertHeadList((&ndis_timerlist), (&kl->kl_next)); + KeReleaseSpinLock(&ndis_timerlock, irql); + } } +void +ndis_cancel_timerlist(void) +{ + list_entry *l; + struct ktimer_list *kl; + uint8_t cancelled, irql; + + KeAcquireSpinLock(&ndis_timerlock, &irql); + + while(!IsListEmpty(&ndis_timerlist)) { + l = RemoveHeadList(&ndis_timerlist); + kl = CONTAINING_RECORD(l, struct ktimer_list, kl_next); + KeReleaseSpinLock(&ndis_timerlock, irql); + cancelled = KeCancelTimer(kl->kl_timer); + free(kl, M_DEVBUF); + KeAcquireSpinLock(&ndis_timerlock, &irql); + } + + KeReleaseSpinLock(&ndis_timerlock, irql); +} + /* * In Windows, there's both an NdisMSetTimer() and an NdisSetTimer(), * but the former is just a macro wrapper around the latter. @@ -1277,6 +1332,26 @@ ndis_timer *timer; uint8_t *cancelled; { + list_entry *l; + struct ktimer_list *kl; + uint8_t irql; + + if (ndis_isusbdev == 1) { + KeAcquireSpinLock(&ndis_timerlock, &irql); + l = ndis_timerlist.nle_flink; + while(l != &ndis_timerlist) { + kl = CONTAINING_RECORD(l, struct ktimer_list, kl_next); + if (kl->kl_timer == &timer->nt_ktimer) { + RemoveEntryList((&kl->kl_next)); + l = l->nle_flink; + free(kl, M_DEVBUF); + continue; + } + l = l->nle_flink; + } + KeReleaseSpinLock(&ndis_timerlock, irql); + } + *cancelled = KeCancelTimer(&timer->nt_ktimer); return; } Index: sys/compat/ndis/subr_usbd.c =================================================================== --- sys/compat/ndis/subr_usbd.c (revision 186411) +++ sys/compat/ndis/subr_usbd.c (working copy) @@ -45,10 +45,24 @@ #include #include #include +#include +#include #include #include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "usbdevs.h" + #include #include #include @@ -56,18 +70,64 @@ #include #include #include +#include static driver_object usbd_driver; -static uint32_t usbd_iodispatch(device_object *, irp *); +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_getdesc(irp *); +static usbd_status usbd_get_desc_ndis(usbd_device_handle, int, int, int, + void *, int *); +static union usbd_urb *usbd_geturb(irp *); +static usbd_status usbd_init_ndispipe(irp *, usb_endpoint_descriptor_t *); +static usbd_xfer_handle usbd_init_ndisxfer(irp *, usb_endpoint_descriptor_t *, + void *, uint32_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 void usbd_irpcancel_cb(void *); +static int32_t usbd_submit_urb(irp *); +static int32_t usbd_urb2nt(int32_t); +static void usbd_xfereof(usbd_xfer_handle, usbd_private_handle, + usbd_status); +static void usbd_xferadd(usbd_xfer_handle, usbd_private_handle, + usbd_status); +static void usbd_xfertask(device_object *, void *); +static void dummy(void); -static void USBD_GetUSBDIVersion(usbd_version_info *); -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_xfertask_wrap; + int usbd_libinit(void) { image_patch_table *patch; + int i; patch = usbd_functbl; while (patch->ipt_func != NULL) { @@ -77,14 +137,36 @@ 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_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; + (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); } @@ -100,20 +182,952 @@ 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_xfertask_wrap); + free(usbd_driver.dro_drivername.us_buf, M_DEVBUF); return(0); } -static uint32_t +static int32_t usbd_iodispatch(dobj, ip) device_object *dobj; irp *ip; { - return(0); + 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(dobj, ip) + 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(dobj, ip) + 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(dobj, ip) + 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(status) + 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 usbd_status to USBD_STATUS */ +static int32_t +usbd_usb2urb(int status) +{ + + switch (status) { + case USBD_NORMAL_COMPLETION: + return (USBD_STATUS_SUCCESS); + case USBD_IN_PROGRESS: + return (USBD_STATUS_PENDING); + case USBD_TIMEOUT: + return (USBD_STATUS_TIMEOUT); + case USBD_SHORT_XFER: + return (USBD_STATUS_ERROR_SHORT_TRANSFER); + case USBD_IOERROR: + return (USBD_STATUS_XACT_ERROR); + case USBD_NOMEM: + return (USBD_STATUS_NO_MEMORY); + case USBD_INVAL: + return (USBD_STATUS_REQUEST_FAILED); + case USBD_NOT_STARTED: + case USBD_TOO_DEEP: + case USBD_NO_POWER: + return (USBD_STATUS_DEVICE_GONE); + case USBD_CANCELLED: + return (USBD_STATUS_CANCELED); + default: + break; + } + + return (USBD_STATUS_NOT_SUPPORTED); +} + +static union usbd_urb * +usbd_geturb(ip) + 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(ip) + 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_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(ip) + irp *ip; +{ + device_t dev = IRP_NDIS_DEV(ip); + int actlen, i; + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct usbd_urb_control_descriptor_request *ctldesc; + uint32_t len; + union usbd_urb *urb; + usb_config_descriptor_t cd, *cdp; + usbd_status status; + + mtx_lock(&Giant); + + urb = usbd_geturb(ip); + ctldesc = &urb->uu_ctldesc; + if (ctldesc->ucd_desctype == UDESC_CONFIG) { + /* Get the short config descriptor. */ + status = usbd_get_config_desc(uaa->device, ctldesc->ucd_idx, + &cd); + if (status != USBD_NORMAL_COMPLETION) { + ctldesc->ucd_trans_buflen = 0; + mtx_unlock(&Giant); + return usbd_usb2urb(status); + } + /* Get the full descriptor. Try a few times for slow devices. */ + len = MIN(ctldesc->ucd_trans_buflen, UGETW(cd.wTotalLength)); + for (i = 0; i < 3; i++) { + status = usbd_get_desc_ndis(uaa->device, + ctldesc->ucd_desctype, ctldesc->ucd_idx, + len, ctldesc->ucd_trans_buf, &actlen); + if (status == USBD_NORMAL_COMPLETION) + break; + usbd_delay_ms(uaa->device, 200); + } + if (status != USBD_NORMAL_COMPLETION) { + ctldesc->ucd_trans_buflen = 0; + mtx_unlock(&Giant); + return usbd_usb2urb(status); + } + + cdp = (usb_config_descriptor_t *)ctldesc->ucd_trans_buf; + if (cdp->bDescriptorType != UDESC_CONFIG) { + device_printf(dev, "bad desc %d\n", + cdp->bDescriptorType); + status = USBD_INVAL; + } + } else if (ctldesc->ucd_desctype == UDESC_STRING) { + /* Try a few times for slow devices. */ + for (i = 0; i < 3; i++) { + status = usbd_get_string_desc(uaa->device, + (UDESC_STRING << 8) + ctldesc->ucd_idx, + ctldesc->ucd_langid, ctldesc->ucd_trans_buf, + &actlen); + if (actlen > ctldesc->ucd_trans_buflen) + panic("small string buffer for UDESC_STRING"); + if (status == USBD_NORMAL_COMPLETION) + break; + usbd_delay_ms(uaa->device, 200); + } + } else + status = usbd_get_desc_ndis(uaa->device, ctldesc->ucd_desctype, + ctldesc->ucd_idx, ctldesc->ucd_trans_buflen, + ctldesc->ucd_trans_buf, &actlen); + + if (status != USBD_NORMAL_COMPLETION) { + ctldesc->ucd_trans_buflen = 0; + mtx_unlock(&Giant); + return usbd_usb2urb(status); + } + + ctldesc->ucd_trans_buflen = actlen; + ip->irp_iostat.isb_info = actlen; + + mtx_unlock(&Giant); + + return (USBD_STATUS_SUCCESS); +} + +/* + * FIXME: at USB1, not USB2, framework, there's no a interface to get `actlen'. + * However, we need it!!! + */ +static usbd_status +usbd_get_desc_ndis(usbd_device_handle dev, int type, int index, int len, + void *desc, int *actlen) +{ + usb_device_request_t req; + + req.bmRequestType = UT_READ_DEVICE; + req.bRequest = UR_GET_DESCRIPTOR; + USETW2(req.wValue, type, index); + USETW(req.wIndex, 0); + USETW(req.wLength, len); + return usbd_do_request_flags_pipe(dev, dev->default_pipe, &req, desc, + 0, actlen, USBD_DEFAULT_TIMEOUT); +} + +static int32_t +usbd_func_selconf(ip) + irp *ip; +{ + device_t dev = IRP_NDIS_DEV(ip); + int i, j; + struct usb_attach_arg *uaa = device_get_ivars(dev); + 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; + usbd_device_handle udev = uaa->device; + usbd_interface_handle iface; + usbd_status 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(USBD_NORMAL_COMPLETION); + } + + if (conf->bConfigurationValue > NDISUSB_CONFIG_NO) + device_printf(dev, "warning: config_no is larger than default"); + + intf = &selconf->usc_intf; + for (i = 0; i < conf->bNumInterface && intf->uii_len > 0; i++) { + ret = usbd_device2interface_handle(uaa->device, + intf->uii_intfnum, &iface); + if (ret != USBD_NORMAL_COMPLETION) { + device_printf(dev, + "getting interface handle failed: %s\n", + usbd_errstr(ret)); + return usbd_usb2urb(ret); + } + + ret = usbd_set_interface(iface, intf->uii_altset); + if (ret != USBD_NORMAL_COMPLETION && ret != USBD_IN_USE) { + device_printf(dev, + "setting alternate interface failed: %s\n", + usbd_errstr(ret)); + return usbd_usb2urb(ret); + } + + for (j = 0; j < iface->idesc->bNumEndpoints; j++) { + if (j >= intf->uii_numeps) { + device_printf(dev, + "endpoint %d and above are ignored", + intf->uii_numeps); + break; + } + edesc = iface->endpoints[j].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); + 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 int32_t +usbd_func_vendorclass(ip) + irp *ip; +{ + device_t dev = IRP_NDIS_DEV(ip); + struct usb_attach_arg *uaa = device_get_ivars(dev); + struct usbd_urb_vendor_or_class_request *vcreq; + uint8_t type = 0; + union usbd_urb *urb; + usb_device_request_t req; + usbd_status status; + + 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 reach. */ + 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); + + if (vcreq->uvc_trans_flags & USBD_TRANSFER_DIRECTION_IN) { + mtx_lock(&Giant); + status = usbd_do_request(uaa->device, &req, + vcreq->uvc_trans_buf); + mtx_unlock(&Giant); + } else + status = usbd_do_request_async(uaa->device, &req, + vcreq->uvc_trans_buf); + + return usbd_usb2urb(status); +} + +static usbd_status +usbd_init_ndispipe(ip, ep) + irp *ip; + usb_endpoint_descriptor_t *ep; +{ + device_t dev = IRP_NDIS_DEV(ip); + struct ndis_softc *sc = device_get_softc(dev); + struct usb_attach_arg *uaa = device_get_ivars(dev); + usbd_interface_handle iface; + usbd_status status; + + status = usbd_device2interface_handle(uaa->device, NDISUSB_IFACE_INDEX, + &iface); + if (status != USBD_NORMAL_COMPLETION) { + device_printf(dev, "could not get interface handle\n"); + return (status); + } + + switch (UE_GET_XFERTYPE(ep->bmAttributes)) { + case UE_BULK: + if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN) { + /* RX (bulk IN) */ + if (sc->ndisusb_ep[NDISUSB_ENDPT_BIN] != NULL) + return (USBD_NORMAL_COMPLETION); + + status = usbd_open_pipe(iface, ep->bEndpointAddress, + USBD_EXCLUSIVE_USE, + &sc->ndisusb_ep[NDISUSB_ENDPT_BIN]); + break; + } + + /* TX (bulk OUT) */ + if (sc->ndisusb_ep[NDISUSB_ENDPT_BOUT] != NULL) + return (USBD_NORMAL_COMPLETION); + + status = usbd_open_pipe(iface, ep->bEndpointAddress, + USBD_EXCLUSIVE_USE, &sc->ndisusb_ep[NDISUSB_ENDPT_BOUT]); + break; + case UE_INTERRUPT: + if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN) { + /* Interrupt IN. */ + if (sc->ndisusb_ep[NDISUSB_ENDPT_IIN] != NULL) + return (USBD_NORMAL_COMPLETION); + + status = usbd_open_pipe(iface, ep->bEndpointAddress, + USBD_EXCLUSIVE_USE, + &sc->ndisusb_ep[NDISUSB_ENDPT_IIN]); + break; + } + + /* Interrupt OUT. */ + if (sc->ndisusb_ep[NDISUSB_ENDPT_IOUT] != NULL) + return (USBD_NORMAL_COMPLETION); + + status = usbd_open_pipe(iface, ep->bEndpointAddress, + USBD_EXCLUSIVE_USE, &sc->ndisusb_ep[NDISUSB_ENDPT_IOUT]); + break; + default: + device_printf(dev, "can't handle xfertype 0x%x\n", + UE_GET_XFERTYPE(ep->bmAttributes)); + return (USBD_INVAL); + } + + if (status != USBD_NORMAL_COMPLETION) + device_printf(dev, "open pipe failed: (0x%x) %s\n", + ep->bEndpointAddress, usbd_errstr(status)); + + return (status); +} + static void +usbd_irpcancel_cb(priv) + void *priv; +{ + struct ndisusb_cancel *nc = priv; + struct ndis_softc *sc = device_get_softc(nc->dev); + usbd_status status; + usbd_xfer_handle xfer = nc->xfer; + + if (sc->ndisusb_status & NDISUSB_STATUS_DETACH) + goto exit; + + status = usbd_abort_pipe(xfer->pipe); + if (status != USBD_NORMAL_COMPLETION) + device_printf(nc->dev, "can't be canceld"); +exit: + free(nc, M_USBDEV); +} + +static void +usbd_irpcancel(dobj, ip) + device_object *dobj; + irp *ip; +{ + device_t dev = IRP_NDIS_DEV(ip); + struct ndisusb_cancel *nc; + struct usb_attach_arg *uaa = device_get_ivars(dev); + + if (IRP_NDISUSB_XFER(ip) == NULL) { + ip->irp_cancel = TRUE; + IoReleaseCancelSpinLock(ip->irp_cancelirql); + return; + } + + /* + * XXX Since we're under DISPATCH_LEVEL during calling usbd_irpcancel(), + * we can't sleep at all. However, currently FreeBSD's USB stack + * requires a sleep to abort a transfer. It's inevitable! so it causes + * serveral fatal problems (e.g. kernel hangups or crashes). I think + * that there are no ways to make this reliable. In this implementation, + * I used usb_add_task() but it's not a perfect method to solve this + * because of as follows: NDIS drivers would expect that IRP's + * completely canceld when usbd_irpcancel() is returned but we need + * a sleep to do it. During canceling XFERs, usbd_intr() would be + * called with a status, USBD_CANCELLED. + */ + nc = malloc(sizeof(struct ndisusb_cancel), M_USBDEV, M_NOWAIT | M_ZERO); + if (nc == NULL) { + ip->irp_cancel = FALSE; + IoReleaseCancelSpinLock(ip->irp_cancelirql); + return; + } + + nc->dev = dev; + nc->xfer = IRP_NDISUSB_XFER(ip); + usb_init_task(&nc->task, usbd_irpcancel_cb, nc); + + IRP_NDISUSB_XFER(ip) = NULL; + usb_add_task(uaa->device, &nc->task, USB_TASKQ_DRIVER); + + ip->irp_cancel = TRUE; + IoReleaseCancelSpinLock(ip->irp_cancelirql); +} + +static usbd_xfer_handle +usbd_init_ndisxfer(ip, ep, buf, buflen) + irp *ip; + usb_endpoint_descriptor_t *ep; + void *buf; + uint32_t buflen; +{ + device_t dev = IRP_NDIS_DEV(ip); + struct usb_attach_arg *uaa = device_get_ivars(dev); + usbd_xfer_handle xfer; + + xfer = usbd_alloc_xfer(uaa->device); + if (xfer == NULL) + return (NULL); + + if (buf != NULL && MmIsAddressValid(buf) == FALSE && buflen > 0) { + xfer->buffer = usbd_alloc_buffer(xfer, buflen); + if (xfer->buffer == NULL) + return (NULL); + + if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_OUT) + memcpy(xfer->buffer, buf, buflen); + } else + xfer->buffer = buf; + + xfer->length = buflen; + + IoAcquireCancelSpinLock(&ip->irp_cancelirql); + IRP_NDISUSB_XFER(ip) = xfer; + ip->irp_cancelfunc = (cancel_func)usbd_irpcancel_wrap; + IoReleaseCancelSpinLock(ip->irp_cancelirql); + + return (xfer); +} + +static void +usbd_xferadd(xfer, priv, status) + usbd_xfer_handle xfer; + usbd_private_handle priv; + usbd_status status; +{ + irp *ip = priv; + device_t dev = IRP_NDIS_DEV(ip); + struct ndis_softc *sc = device_get_softc(dev); + struct ndisusb_xfer *nx; + uint8_t irql; + + nx = malloc(sizeof(struct ndisusb_xfer), M_USBDEV, M_NOWAIT | M_ZERO); + if (nx == NULL) { + device_printf(dev, "out of memory"); + return; + } + nx->nx_xfer = xfer; + nx->nx_priv = priv; + nx->nx_status = status; + + KeAcquireSpinLock(&sc->ndisusb_xferlock, &irql); + InsertTailList((&sc->ndisusb_xferlist), (&nx->nx_xferlist)); + KeReleaseSpinLock(&sc->ndisusb_xferlock, irql); + + IoQueueWorkItem(sc->ndisusb_xferitem, + (io_workitem_func)usbd_xfertask_wrap, WORKQUEUE_CRITICAL, sc); +} + +static void +usbd_xfereof(xfer, priv, status) + usbd_xfer_handle xfer; + usbd_private_handle priv; + usbd_status status; +{ + + usbd_xferadd(xfer, priv, status); +} + +static void +usbd_xfertask(dobj, arg) + device_object *dobj; + void *arg; +{ + int error; + irp *ip; + device_t dev; + list_entry *l; + struct ndis_softc *sc = arg; + struct ndisusb_xfer *nx; + struct usbd_urb_bulk_or_intr_transfer *ubi; + uint8_t irql; + union usbd_urb *urb; + usbd_private_handle priv; + usbd_status status; + usbd_xfer_handle xfer; + + dev = sc->ndis_dev; + + if (IsListEmpty(&sc->ndisusb_xferlist)) + return; + + KeAcquireSpinLock(&sc->ndisusb_xferlock, &irql); + l = sc->ndisusb_xferlist.nle_flink; + while (l != &sc->ndisusb_xferlist) { + nx = CONTAINING_RECORD(l, struct ndisusb_xfer, nx_xferlist); + xfer = nx->nx_xfer; + priv = nx->nx_priv; + status = nx->nx_status; + error = 0; + ip = priv; + + if (status != USBD_NORMAL_COMPLETION) { + if (status == USBD_NOT_STARTED) { + error = 1; + goto next; + } + if (status == USBD_STALLED) + usbd_clear_endpoint_stall_async(xfer->pipe); + /* + * NB: just for notice. We must handle error cases also + * because if we just return without notifying to the + * NDIS driver the driver never knows about that there + * was a error. This can cause a lot of problems like + * system hangs. + */ + device_printf(dev, "usb xfer warning (%s)\n", + usbd_errstr(status)); + } + + urb = usbd_geturb(ip); + + KASSERT(urb->uu_hdr.uuh_func == + URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER, + ("function(%d) isn't for bulk or interrupt", + urb->uu_hdr.uuh_func)); + + IoAcquireCancelSpinLock(&ip->irp_cancelirql); + + ip->irp_cancelfunc = NULL; + IRP_NDISUSB_XFER(ip) = NULL; + + switch (status) { + case USBD_NORMAL_COMPLETION: + ubi = &urb->uu_bulkintr; + ubi->ubi_trans_buflen = xfer->actlen; + if (ubi->ubi_trans_flags & USBD_TRANSFER_DIRECTION_IN) + memcpy(ubi->ubi_trans_buf, xfer->buffer, + xfer->actlen); + + ip->irp_iostat.isb_info = xfer->actlen; + ip->irp_iostat.isb_status = STATUS_SUCCESS; + USBD_URB_STATUS(urb) = USBD_STATUS_SUCCESS; + break; + case USBD_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; + } + + IoReleaseCancelSpinLock(ip->irp_cancelirql); +next: + l = l->nle_flink; + RemoveEntryList(&nx->nx_xferlist); + usbd_free_xfer(nx->nx_xfer); + free(nx, M_USBDEV); + if (error) + continue; + /* NB: call after cleaning */ + IoCompleteRequest(ip, IO_NO_INCREMENT); + } + KeReleaseSpinLock(&sc->ndisusb_xferlock, irql); +} + +static int32_t +usbd_func_bulkintr(ip) + irp *ip; +{ + device_t dev = IRP_NDIS_DEV(ip); + struct ndis_softc *sc = device_get_softc(dev); + struct usbd_urb_bulk_or_intr_transfer *ubi; + union usbd_urb *urb; + usb_endpoint_descriptor_t *ep; + usbd_status status; + usbd_xfer_handle xfer; + + urb = usbd_geturb(ip); + ubi = &urb->uu_bulkintr; + ep = ubi->ubi_epdesc; + if (ep == NULL) + return (USBD_STATUS_INVALID_PIPE_HANDLE); + + status = usbd_init_ndispipe(ip, ep); + if (status != USBD_NORMAL_COMPLETION) + return usbd_usb2urb(status); + + xfer = usbd_init_ndisxfer(ip, ep, ubi->ubi_trans_buf, + ubi->ubi_trans_buflen); + if (xfer == NULL) { + device_printf(IRP_NDIS_DEV(ip), "can't allocate xfer\n"); + return (USBD_STATUS_NO_MEMORY); + } + + if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN) { + xfer->flags |= USBD_SHORT_XFER_OK; + if (!(ubi->ubi_trans_flags & USBD_SHORT_TRANSFER_OK)) + xfer->flags &= ~USBD_SHORT_XFER_OK; + } + + if (UE_GET_XFERTYPE(ep->bmAttributes) == UE_BULK) { + if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN) + /* RX (bulk IN) */ + usbd_setup_xfer(xfer, sc->ndisusb_ep[NDISUSB_ENDPT_BIN], + ip, xfer->buffer, xfer->length, xfer->flags, + USBD_NO_TIMEOUT, usbd_xfereof); + else { + /* TX (bulk OUT) */ + xfer->flags |= USBD_NO_COPY; + + usbd_setup_xfer(xfer, sc->ndisusb_ep[NDISUSB_ENDPT_BOUT], + ip, xfer->buffer, xfer->length, xfer->flags, + NDISUSB_TX_TIMEOUT, usbd_xfereof); + } + } else { + if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN) + /* Interrupt IN */ + usbd_setup_xfer(xfer, sc->ndisusb_ep[NDISUSB_ENDPT_IIN], + ip, xfer->buffer, xfer->length, xfer->flags, + USBD_NO_TIMEOUT, usbd_xfereof); + else + /* Interrupt OUT */ + usbd_setup_xfer(xfer, sc->ndisusb_ep[NDISUSB_ENDPT_IOUT], + ip, xfer->buffer, xfer->length, xfer->flags, + NDISUSB_INTR_TIMEOUT, usbd_xfereof); + } + + /* 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); + + status = usbd_transfer(xfer); + if (status == USBD_IN_PROGRESS) + return (USBD_STATUS_PENDING); + + usbd_free_xfer(xfer); + IRP_NDISUSB_XFER(ip) = NULL; + IoUnmarkIrpPending(ip); + USBD_URB_STATUS(urb) = usbd_usb2urb(status); + + return USBD_URB_STATUS(urb); +} + +static union usbd_urb * +USBD_CreateConfigurationRequest(conf, len) + 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(conf, list) + 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(ui) usbd_version_info *ui; { @@ -125,6 +1139,52 @@ return; } +static usb_interface_descriptor_t * +USBD_ParseConfigurationDescriptor(conf, intfnum, altset) + 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(conf, start, intfnum, + altset, intfclass, intfsubclass, intfproto) + usb_config_descriptor_t *conf; + void *start; + int32_t intfnum; + int32_t altset; + int32_t intfclass; + int32_t intfsubclass; + int32_t intfproto; +{ + char *pos; + usb_interface_descriptor_t *desc; + + for (pos = start; pos < ((char *)conf + UGETW(conf->wTotalLength)); + pos += desc->bLength) { + desc = (usb_interface_descriptor_t *)pos; + 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) { @@ -133,14 +1193,15 @@ } image_patch_table usbd_functbl[] = { - IMPORT_SFUNC(USBD_GetUSBDIVersion, 0), - IMPORT_SFUNC(usbd_iodispatch, 2), -#ifdef notyet - IMPORT_FUNC_MAP(_USBD_ParseConfigurationDescriptorEx@28, - USBD_ParseConfigurationDescriptorEx), - IMPORT_FUNC_MAP(_USBD_CreateConfigurationRequestEx@8, - USBD_CreateConfigurationRequestEx), -#endif + 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 @@ -156,3 +1217,5 @@ { NULL, NULL, NULL } }; +MODULE_DEPEND(ndis, usb, 1, 1, 1); +