diff options
author | weongyo <weongyo@FreeBSD.org> | 2008-12-27 08:03:32 +0000 |
---|---|---|
committer | weongyo <weongyo@FreeBSD.org> | 2008-12-27 08:03:32 +0000 |
commit | 0f8825b3f7e9bfd96fc59b8c4404742798efbb5a (patch) | |
tree | dc3b2fd3e52b250688d8fdeaf2b7a3c6352c07a2 | |
parent | a83406ae2e355dade6f31bbd0874fbf4d7100323 (diff) | |
download | FreeBSD-src-0f8825b3f7e9bfd96fc59b8c4404742798efbb5a.zip FreeBSD-src-0f8825b3f7e9bfd96fc59b8c4404742798efbb5a.tar.gz |
Integrate the NDIS USB support code to CURRENT.
Now the NDISulator supports NDIS USB drivers that it've tested with
devices as follows:
- Anygate XM-142 (Conexant)
- Netgear WG111v2 (Realtek)
- U-Khan UW-2054u (Marvell)
- Shuttle XPC Accessory PN20 (Realtek)
- ipTIME G054U2 (Ralink)
- UNiCORN WL-54G (ZyDAS)
- ZyXEL G-200v2 (ZyDAS)
All of them succeeded to attach and worked though there are still some
problems that it's expected to be solved.
To use NDIS USB support, you should rebuild and install ndiscvt(8) and
if you encounter a problem to attach please set `hw.ndisusb.halt' to
0 then retry.
I expect no changes of the NDIS code for PCI, PCMCIA devices.
Obtained from: //depot/projects/ndisusb/...
-rw-r--r-- | share/man/man4/ndis.4 | 2 | ||||
-rw-r--r-- | sys/compat/ndis/kern_ndis.c | 29 | ||||
-rw-r--r-- | sys/compat/ndis/kern_windrv.c | 11 | ||||
-rw-r--r-- | sys/compat/ndis/ndis_var.h | 3 | ||||
-rw-r--r-- | sys/compat/ndis/ntoskrnl_var.h | 30 | ||||
-rw-r--r-- | sys/compat/ndis/subr_ndis.c | 77 | ||||
-rw-r--r-- | sys/compat/ndis/subr_ntoskrnl.c | 119 | ||||
-rw-r--r-- | sys/compat/ndis/subr_usbd.c | 1091 | ||||
-rw-r--r-- | sys/compat/ndis/usbd_var.h | 172 | ||||
-rw-r--r-- | sys/dev/if_ndis/if_ndis.c | 56 | ||||
-rw-r--r-- | sys/dev/if_ndis/if_ndis_pccard.c | 3 | ||||
-rw-r--r-- | sys/dev/if_ndis/if_ndis_pci.c | 2 | ||||
-rw-r--r-- | sys/dev/if_ndis/if_ndis_usb.c | 99 | ||||
-rw-r--r-- | sys/dev/if_ndis/if_ndisvar.h | 46 | ||||
-rw-r--r-- | sys/modules/ndis/Makefile | 1 | ||||
-rw-r--r-- | usr.sbin/ndiscvt/inf.c | 120 | ||||
-rw-r--r-- | usr.sbin/ndiscvt/windrv_stub.c | 19 |
17 files changed, 1820 insertions, 60 deletions
diff --git a/share/man/man4/ndis.4 b/share/man/man4/ndis.4 index 7f723d0..107001a 100644 --- a/share/man/man4/ndis.4 +++ b/share/man/man4/ndis.4 @@ -105,7 +105,7 @@ file. 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 diff --git a/sys/compat/ndis/kern_ndis.c b/sys/compat/ndis/kern_ndis.c index 2c7d037..98ac538 100644 --- a/sys/compat/ndis/kern_ndis.c +++ b/sys/compat/ndis/kern_ndis.c @@ -65,6 +65,9 @@ __FBSDID("$FreeBSD$"); #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_ioctl.h> +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> + #include <compat/ndis/pe_var.h> #include <compat/ndis/cfg_var.h> #include <compat/ndis/resource_var.h> @@ -144,7 +147,6 @@ ndis_modevent(module_t mod, int cmd, void *arg) } TAILQ_INIT(&ndis_devhead); - break; case MOD_SHUTDOWN: if (TAILQ_FIRST(&ndis_devhead) == NULL) { @@ -1199,6 +1201,31 @@ ndis_shutdown_nic(arg) } 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; { diff --git a/sys/compat/ndis/kern_windrv.c b/sys/compat/ndis/kern_windrv.c index 00d8e8d..021bea6 100644 --- a/sys/compat/ndis/kern_windrv.c +++ b/sys/compat/ndis/kern_windrv.c @@ -56,6 +56,9 @@ __FBSDID("$FreeBSD$"); #include <machine/segments.h> #endif +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> + #include <compat/ndis/pe_var.h> #include <compat/ndis/cfg_var.h> #include <compat/ndis/resource_var.h> @@ -349,9 +352,11 @@ windrv_load(mod, img, len, bustype, devlist, regvals) 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) { diff --git a/sys/compat/ndis/ndis_var.h b/sys/compat/ndis/ndis_var.h index bef4f2e..6608f0c 100644 --- a/sys/compat/ndis/ndis_var.h +++ b/sys/compat/ndis/ndis_var.h @@ -1658,6 +1658,7 @@ typedef void (*ndis_return_handler)(ndis_handle, ndis_packet *); 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 void ndis_free_bufs(ndis_buffer *); 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 NdisAllocatePacket(ndis_status *, 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_ */ diff --git a/sys/compat/ndis/ntoskrnl_var.h b/sys/compat/ndis/ntoskrnl_var.h index 3c0fe58..73e01c4 100644 --- a/sys/compat/ndis/ntoskrnl_var.h +++ b/sys/compat/ndis/ntoskrnl_var.h @@ -536,6 +536,11 @@ typedef struct wait_block wait_block; #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 @@ struct irp { } 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 @@ struct irp { #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 @@ typedef struct irp irp; (cancel_func)InterlockedExchangePointer( \ (void *)&(ip)->irp_cancelfunc, (void *)(func)) +#define IoSetCancelValue(irp, val) \ + (u_long)InterlockedExchangePointer( \ + (void *)&(ip)->irp_cancel, (void *)(val)) + #define IoGetCurrentIrpStackLocation(irp) \ (irp)->irp_tail.irp_overlay.irp_csl @@ -1035,6 +1053,8 @@ typedef struct irp irp; #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 @@ typedef struct driver_object driver_object; #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 void ExFreePool(void *); 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 *); diff --git a/sys/compat/ndis/subr_ndis.c b/sys/compat/ndis/subr_ndis.c index e0f1f99..ac9d8da 100644 --- a/sys/compat/ndis/subr_ndis.c +++ b/sys/compat/ndis/subr_ndis.c @@ -95,6 +95,8 @@ __FBSDID("$FreeBSD$"); #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> #include <compat/ndis/pe_var.h> #include <compat/ndis/cfg_var.h> @@ -302,6 +304,15 @@ static void dummy(void); */ #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 @@ ndis_libinit() patch++; } + KeInitializeSpinLock(&ndis_timerlock); + InitializeListHead(&ndis_timerlist); + return(0); } @@ -1215,6 +1229,16 @@ NdisMInitializeTimer(timer, handle, func, ctx) 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,7 +1256,38 @@ NdisMInitializeTimer(timer, handle, func, ctx) 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); } /* @@ -1277,6 +1332,26 @@ NdisMCancelTimer(timer, cancelled) 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; } diff --git a/sys/compat/ndis/subr_ntoskrnl.c b/sys/compat/ndis/subr_ntoskrnl.c index d18d22b..1651c3f 100644 --- a/sys/compat/ndis/subr_ntoskrnl.c +++ b/sys/compat/ndis/subr_ntoskrnl.c @@ -207,7 +207,6 @@ static void *MmMapLockedPages(mdl *, uint8_t); 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 funcptr ntoskrnl_findwrap(funcptr); 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 @@ uint8_t 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 @@ IofCompleteRequest(ip, prioboost) 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)); + + sl = IoGetCurrentIrpStackLocation(ip); + IoSkipCurrentIrpStackLocation(ip); + + do { + if (sl->isl_ctl & SL_PENDING_RETURNED) + ip->irp_pendingreturned = TRUE; - for (i = ip->irp_currentstackloc; i < (uint32_t)ip->irp_stackcnt; i++) { - if (ip->irp_currentstackloc < ip->irp_stackcnt - 1) { - IoSkipCurrentIrpStackLocation(ip); + 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 @@ IofCompleteRequest(ip, prioboost) 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 @@ MmUnmapLockedPages(vaddr, buf) * here, but it doesn't. */ -static uint8_t +uint8_t MmIsAddressValid(vaddr) void *vaddr; { @@ -4258,6 +4268,73 @@ KeReadStateTimer(timer) 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 @@ image_patch_table ntoskrnl_functbl[] = { 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 diff --git a/sys/compat/ndis/subr_usbd.c b/sys/compat/ndis/subr_usbd.c index 3aa322e..0c47c1b 100644 --- a/sys/compat/ndis/subr_usbd.c +++ b/sys/compat/ndis/subr_usbd.c @@ -45,10 +45,24 @@ __FBSDID("$FreeBSD$"); #include <sys/module.h> #include <sys/conf.h> #include <sys/mbuf.h> +#include <sys/socket.h> +#include <machine/bus.h> #include <sys/bus.h> #include <sys/queue.h> +#include <net/if.h> +#include <net/if_media.h> +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_ioctl.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usb_quirks.h> +#include "usbdevs.h" + #include <compat/ndis/pe_var.h> #include <compat/ndis/cfg_var.h> #include <compat/ndis/resource_var.h> @@ -56,18 +70,64 @@ __FBSDID("$FreeBSD$"); #include <compat/ndis/ndis_var.h> #include <compat/ndis/hal_var.h> #include <compat/ndis/usbd_var.h> +#include <dev/if_ndis/if_ndisvar.h> 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 @@ usbd_libinit(void) 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,17 +182,949 @@ usbd_libfini(void) 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 @@ -125,6 +1139,52 @@ USBD_GetUSBDIVersion(ui) 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 @@ dummy(void) } 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 @@ image_patch_table usbd_functbl[] = { { NULL, NULL, NULL } }; +MODULE_DEPEND(ndis, usb, 1, 1, 1); + diff --git a/sys/compat/ndis/usbd_var.h b/sys/compat/ndis/usbd_var.h index 8c9f2b3..59009a7 100644 --- a/sys/compat/ndis/usbd_var.h +++ b/sys/compat/ndis/usbd_var.h @@ -35,9 +35,168 @@ #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; @@ -46,6 +205,13 @@ struct usbd_version_info { 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 diff --git a/sys/dev/if_ndis/if_ndis.c b/sys/dev/if_ndis/if_ndis.c index 1a2db17..a85effe 100644 --- a/sys/dev/if_ndis/if_ndis.c +++ b/sys/dev/if_ndis/if_ndis.c @@ -74,6 +74,8 @@ __FBSDID("$FreeBSD$"); #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> #include <compat/ndis/pe_var.h> #include <compat/ndis/cfg_var.h> @@ -81,6 +83,7 @@ __FBSDID("$FreeBSD$"); #include <compat/ndis/ntoskrnl_var.h> #include <compat/ndis/hal_var.h> #include <compat/ndis/ndis_var.h> +#include <compat/ndis/usbd_var.h> #include <dev/if_ndis/if_ndisvar.h> #define NDIS_DEBUG @@ -93,6 +96,11 @@ SYSCTL_INT(_debug, OID_AUTO, ndis, CTLFLAG_RW, &ndis_debug, 0, #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 @@ ndis_attach(dev) 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 @@ ndis_attach(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); + 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 @@ ndis_attach(dev) 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,11 +947,16 @@ got_crypto: } 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 @@ ndis_detach(dev) 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_txeof(adapter, packet, status) 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 @@ ndis_txeof(adapter, packet, status) 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 @@ ndis_linksts(adapter, status, sbuf, slen) /* 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 @@ ndis_linksts(adapter, status, sbuf, slen) 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 @@ ndis_linksts(adapter, status, sbuf, slen) 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_init(xsc) */ 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 @@ ndis_getstate_80211(sc) 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 @@ ndis_stop(sc) 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++) { diff --git a/sys/dev/if_ndis/if_ndis_pccard.c b/sys/dev/if_ndis/if_ndis_pccard.c index 4eab5d9..7eb4cd6 100644 --- a/sys/dev/if_ndis/if_ndis_pccard.c +++ b/sys/dev/if_ndis/if_ndis_pccard.c @@ -53,6 +53,9 @@ __FBSDID("$FreeBSD$"); #include <net80211/ieee80211_var.h> +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> + #include <compat/ndis/pe_var.h> #include <compat/ndis/cfg_var.h> #include <compat/ndis/resource_var.h> diff --git a/sys/dev/if_ndis/if_ndis_pci.c b/sys/dev/if_ndis/if_ndis_pci.c index 57ec77d..c03c502 100644 --- a/sys/dev/if_ndis/if_ndis_pci.c +++ b/sys/dev/if_ndis/if_ndis_pci.c @@ -54,6 +54,8 @@ __FBSDID("$FreeBSD$"); #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> #include <compat/ndis/pe_var.h> #include <compat/ndis/cfg_var.h> diff --git a/sys/dev/if_ndis/if_ndis_usb.c b/sys/dev/if_ndis/if_ndis_usb.c index 3fdc0ac..b206982 100644 --- a/sys/dev/if_ndis/if_ndis_usb.c +++ b/sys/dev/if_ndis/if_ndis_usb.c @@ -65,12 +65,16 @@ __FBSDID("$FreeBSD$"); #include <compat/ndis/resource_var.h> #include <compat/ndis/ntoskrnl_var.h> #include <compat/ndis/ndis_var.h> +#include <compat/ndis/usbd_var.h> #include <dev/if_ndis/if_ndisvar.h> +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 @@ static device_method_t ndis_methods[] = { /* 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 @@ 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->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 @@ ndisusb_match(device_t self) 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) { diff --git a/sys/dev/if_ndis/if_ndisvar.h b/sys/dev/if_ndis/if_ndisvar.h index 5f9fe75..6b6a450 100644 --- a/sys/dev/if_ndis/if_ndisvar.h +++ b/sys/dev/if_ndis/if_ndisvar.h @@ -58,6 +58,12 @@ struct ndis_pccard_type { 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 @@ struct ndis_vap { }; #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 @@ struct ndis_softc { 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) diff --git a/sys/modules/ndis/Makefile b/sys/modules/ndis/Makefile index 2d14b18..647b787 100644 --- a/sys/modules/ndis/Makefile +++ b/sys/modules/ndis/Makefile @@ -6,6 +6,7 @@ KMOD= ndis 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 diff --git a/usr.sbin/ndiscvt/inf.c b/usr.sbin/ndiscvt/inf.c index 05f863d..fe4db6a 100644 --- a/usr.sbin/ndiscvt/inf.c +++ b/usr.sbin/ndiscvt/inf.c @@ -62,8 +62,10 @@ static struct section *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 @@ inf_parse (FILE *fp, FILE *outfp) 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 @@ dump_pci_id(const char *s) } 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 @@ done: } 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; diff --git a/usr.sbin/ndiscvt/windrv_stub.c b/usr.sbin/ndiscvt/windrv_stub.c index 30e74bf..46a0e83 100644 --- a/usr.sbin/ndiscvt/windrv_stub.c +++ b/usr.sbin/ndiscvt/windrv_stub.c @@ -69,6 +69,11 @@ struct ndis_pccard_type { 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 @@ static struct ndis_pccard_type ndis_devs_pccard[] = { }; #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_modevent(mod, cmd, arg) 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 @@ windrv_modevent(mod, cmd, arg) #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; |