3 * Bill Paul <wpaul@windriver.com>. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Bill Paul.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30 * THE POSSIBILITY OF SUCH DAMAGE.
32 * $FreeBSD: src/sys/compat/ndis/subr_usbd.c,v 1.6 2009/02/24 18:09:31 rdivacky Exp $
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/unistd.h>
38 #include <sys/types.h>
40 #include <sys/kernel.h>
41 #include <sys/malloc.h>
43 #include <sys/mutex.h>
44 #include <sys/module.h>
47 #include <sys/socket.h>
50 #include <sys/queue.h>
51 #include <sys/mplock2.h>
54 #include <net/if_media.h>
55 #include <netproto/802_11/ieee80211_var.h>
56 #include <netproto/802_11/ieee80211_ioctl.h>
58 #include <bus/usb/usb.h>
59 #include <bus/usb/usbdi.h>
60 #include <bus/usb/usbdi_util.h>
61 #include <bus/usb/usbdivar.h>
62 #include <bus/usb/usb_quirks.h>
64 #include <emulation/ndis/pe_var.h>
65 #include <emulation/ndis/cfg_var.h>
66 #include <emulation/ndis/resource_var.h>
67 #include <emulation/ndis/ntoskrnl_var.h>
68 #include <emulation/ndis/ndis_var.h>
69 #include <emulation/ndis/hal_var.h>
70 #include <emulation/ndis/usbd_var.h>
71 #include <dev/netif/ndis/if_ndisvar.h>
73 static driver_object usbd_driver;
75 static int32_t usbd_func_bulkintr(irp *);
76 static int32_t usbd_func_vendorclass(irp *);
77 static int32_t usbd_func_selconf(irp *);
78 static int32_t usbd_func_abort_pipe(irp *);
79 static int32_t usbd_func_getdesc(irp *);
80 static usbd_status usbd_get_desc_ndis(usbd_device_handle, int, int, int,
82 static union usbd_urb *usbd_geturb(irp *);
83 static usbd_status usbd_init_ndispipe(irp *, usb_endpoint_descriptor_t *);
84 static usbd_xfer_handle usbd_init_ndisxfer(irp *, usb_endpoint_descriptor_t *,
86 static int32_t usbd_iodispatch(device_object *, irp *);
87 static int32_t usbd_ioinvalid(device_object *, irp *);
88 static int32_t usbd_pnp(device_object *, irp *);
89 static int32_t usbd_power(device_object *, irp *);
90 static void usbd_irpcancel(device_object *, irp *);
91 static void usbd_irpcancel_cb(void *);
92 static int32_t usbd_submit_urb(irp *);
93 static int32_t usbd_urb2nt(int32_t);
94 static void usbd_xfereof(usbd_xfer_handle, usbd_private_handle,
96 static void usbd_xferadd(usbd_xfer_handle, usbd_private_handle,
98 static void usbd_xfertask(device_object *, void *);
99 static void dummy(void);
101 static union usbd_urb *USBD_CreateConfigurationRequestEx(
102 usb_config_descriptor_t *,
103 struct usbd_interface_list_entry *);
104 static union usbd_urb *USBD_CreateConfigurationRequest(
105 usb_config_descriptor_t *,
107 static void USBD_GetUSBDIVersion(usbd_version_info *);
108 static usb_interface_descriptor_t *USBD_ParseConfigurationDescriptorEx(
109 usb_config_descriptor_t *, void *, int32_t, int32_t,
110 int32_t, int32_t, int32_t);
111 static usb_interface_descriptor_t *USBD_ParseConfigurationDescriptor(
112 usb_config_descriptor_t *, uint8_t, uint8_t);
115 * We need to wrap these functions because these need `context switch' from
116 * Windows to UNIX before it's called.
118 static funcptr usbd_iodispatch_wrap;
119 static funcptr usbd_ioinvalid_wrap;
120 static funcptr usbd_pnp_wrap;
121 static funcptr usbd_power_wrap;
122 static funcptr usbd_irpcancel_wrap;
123 static funcptr usbd_xfertask_wrap;
128 image_patch_table *patch;
131 patch = usbd_functbl;
132 while (patch->ipt_func != NULL) {
133 windrv_wrap((funcptr)patch->ipt_func,
134 (funcptr *)&patch->ipt_wrap,
135 patch->ipt_argcnt, patch->ipt_ftype);
139 windrv_wrap((funcptr)usbd_ioinvalid,
140 (funcptr *)&usbd_ioinvalid_wrap, 2, WINDRV_WRAP_STDCALL);
141 windrv_wrap((funcptr)usbd_iodispatch,
142 (funcptr *)&usbd_iodispatch_wrap, 2, WINDRV_WRAP_STDCALL);
143 windrv_wrap((funcptr)usbd_pnp,
144 (funcptr *)&usbd_pnp_wrap, 2, WINDRV_WRAP_STDCALL);
145 windrv_wrap((funcptr)usbd_power,
146 (funcptr *)&usbd_power_wrap, 2, WINDRV_WRAP_STDCALL);
147 windrv_wrap((funcptr)usbd_irpcancel,
148 (funcptr *)&usbd_irpcancel_wrap, 2, WINDRV_WRAP_STDCALL);
149 windrv_wrap((funcptr)usbd_xfertask,
150 (funcptr *)&usbd_xfertask_wrap, 2, WINDRV_WRAP_STDCALL);
152 /* Create a fake USB driver instance. */
154 windrv_bus_attach(&usbd_driver, "USB Bus");
156 /* Set up our dipatch routine. */
157 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
158 usbd_driver.dro_dispatch[i] =
159 (driver_dispatch)usbd_ioinvalid_wrap;
161 usbd_driver.dro_dispatch[IRP_MJ_INTERNAL_DEVICE_CONTROL] =
162 (driver_dispatch)usbd_iodispatch_wrap;
163 usbd_driver.dro_dispatch[IRP_MJ_DEVICE_CONTROL] =
164 (driver_dispatch)usbd_iodispatch_wrap;
165 usbd_driver.dro_dispatch[IRP_MJ_POWER] =
166 (driver_dispatch)usbd_power_wrap;
167 usbd_driver.dro_dispatch[IRP_MJ_PNP] =
168 (driver_dispatch)usbd_pnp_wrap;
176 image_patch_table *patch;
178 patch = usbd_functbl;
179 while (patch->ipt_func != NULL) {
180 windrv_unwrap(patch->ipt_wrap);
184 windrv_unwrap(usbd_ioinvalid_wrap);
185 windrv_unwrap(usbd_iodispatch_wrap);
186 windrv_unwrap(usbd_pnp_wrap);
187 windrv_unwrap(usbd_power_wrap);
188 windrv_unwrap(usbd_irpcancel_wrap);
189 windrv_unwrap(usbd_xfertask_wrap);
191 kfree(usbd_driver.dro_drivername.us_buf, M_DEVBUF);
197 usbd_iodispatch(device_object *dobj, irp *ip)
199 device_t dev = dobj->do_devext;
201 struct io_stack_location *irp_sl;
203 irp_sl = IoGetCurrentIrpStackLocation(ip);
204 switch (irp_sl->isl_parameters.isl_ioctl.isl_iocode) {
205 case IOCTL_INTERNAL_USB_SUBMIT_URB:
206 IRP_NDIS_DEV(ip) = dev;
208 status = usbd_submit_urb(ip);
211 device_printf(dev, "ioctl 0x%x isn't supported\n",
212 irp_sl->isl_parameters.isl_ioctl.isl_iocode);
213 status = USBD_STATUS_NOT_SUPPORTED;
217 if (status == USBD_STATUS_PENDING)
218 return (STATUS_PENDING);
220 ip->irp_iostat.isb_status = usbd_urb2nt(status);
221 if (status != USBD_STATUS_SUCCESS)
222 ip->irp_iostat.isb_info = 0;
223 return (ip->irp_iostat.isb_status);
227 usbd_ioinvalid(device_object *dobj, irp *ip)
229 device_t dev = dobj->do_devext;
230 struct io_stack_location *irp_sl;
232 irp_sl = IoGetCurrentIrpStackLocation(ip);
233 device_printf(dev, "invalid I/O dispatch %d:%d\n", irp_sl->isl_major,
236 ip->irp_iostat.isb_status = STATUS_FAILURE;
237 ip->irp_iostat.isb_info = 0;
239 IoCompleteRequest(ip, IO_NO_INCREMENT);
241 return (STATUS_FAILURE);
245 usbd_pnp(device_object *dobj, irp *ip)
247 device_t dev = dobj->do_devext;
248 struct io_stack_location *irp_sl;
250 irp_sl = IoGetCurrentIrpStackLocation(ip);
251 device_printf(dev, "%s: unsupported I/O dispatch %d:%d\n",
252 __func__, irp_sl->isl_major, irp_sl->isl_minor);
254 ip->irp_iostat.isb_status = STATUS_FAILURE;
255 ip->irp_iostat.isb_info = 0;
257 IoCompleteRequest(ip, IO_NO_INCREMENT);
259 return (STATUS_FAILURE);
263 usbd_power(device_object *dobj, irp *ip)
265 device_t dev = dobj->do_devext;
266 struct io_stack_location *irp_sl;
268 irp_sl = IoGetCurrentIrpStackLocation(ip);
269 device_printf(dev, "%s: unsupported I/O dispatch %d:%d\n",
270 __func__, irp_sl->isl_major, irp_sl->isl_minor);
272 ip->irp_iostat.isb_status = STATUS_FAILURE;
273 ip->irp_iostat.isb_info = 0;
275 IoCompleteRequest(ip, IO_NO_INCREMENT);
277 return (STATUS_FAILURE);
280 /* Convert USBD_STATUS to NTSTATUS */
282 usbd_urb2nt(int32_t status)
286 case USBD_STATUS_SUCCESS:
287 return (STATUS_SUCCESS);
288 case USBD_STATUS_DEVICE_GONE:
289 return (STATUS_DEVICE_NOT_CONNECTED);
290 case USBD_STATUS_PENDING:
291 return (STATUS_PENDING);
292 case USBD_STATUS_NOT_SUPPORTED:
293 return (STATUS_NOT_IMPLEMENTED);
294 case USBD_STATUS_NO_MEMORY:
295 return (STATUS_NO_MEMORY);
296 case USBD_STATUS_REQUEST_FAILED:
297 return (STATUS_NOT_SUPPORTED);
298 case USBD_STATUS_CANCELED:
299 return (STATUS_CANCELLED);
304 return (STATUS_FAILURE);
307 /* Convert FreeBSD's usbd_status to USBD_STATUS */
309 usbd_usb2urb(int status)
313 case USBD_NORMAL_COMPLETION:
314 return (USBD_STATUS_SUCCESS);
315 case USBD_IN_PROGRESS:
316 return (USBD_STATUS_PENDING);
318 return (USBD_STATUS_TIMEOUT);
319 case USBD_SHORT_XFER:
320 return (USBD_STATUS_ERROR_SHORT_TRANSFER);
322 return (USBD_STATUS_XACT_ERROR);
324 return (USBD_STATUS_NO_MEMORY);
326 return (USBD_STATUS_REQUEST_FAILED);
327 case USBD_NOT_STARTED:
330 return (USBD_STATUS_DEVICE_GONE);
332 return (USBD_STATUS_CANCELED);
337 return (USBD_STATUS_NOT_SUPPORTED);
340 static union usbd_urb *
343 struct io_stack_location *irp_sl;
345 irp_sl = IoGetCurrentIrpStackLocation(ip);
347 return (irp_sl->isl_parameters.isl_others.isl_arg1);
351 usbd_submit_urb(irp *ip)
353 device_t dev = IRP_NDIS_DEV(ip);
357 urb = usbd_geturb(ip);
359 * In a case of URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER,
360 * USBD_URB_STATUS(urb) would be set at callback functions like
361 * usbd_intr() or usbd_xfereof().
363 switch (urb->uu_hdr.uuh_func) {
364 case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
365 status = usbd_func_bulkintr(ip);
366 if (status != USBD_STATUS_SUCCESS &&
367 status != USBD_STATUS_PENDING)
368 USBD_URB_STATUS(urb) = status;
370 case URB_FUNCTION_VENDOR_DEVICE:
371 case URB_FUNCTION_VENDOR_INTERFACE:
372 case URB_FUNCTION_VENDOR_ENDPOINT:
373 case URB_FUNCTION_VENDOR_OTHER:
374 case URB_FUNCTION_CLASS_DEVICE:
375 case URB_FUNCTION_CLASS_INTERFACE:
376 case URB_FUNCTION_CLASS_ENDPOINT:
377 case URB_FUNCTION_CLASS_OTHER:
378 status = usbd_func_vendorclass(ip);
379 USBD_URB_STATUS(urb) = status;
381 case URB_FUNCTION_SELECT_CONFIGURATION:
382 status = usbd_func_selconf(ip);
383 USBD_URB_STATUS(urb) = status;
385 case URB_FUNCTION_ABORT_PIPE:
386 status = usbd_func_abort_pipe(ip);
387 USBD_URB_STATUS(urb) = status;
389 case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
390 status = usbd_func_getdesc(ip);
391 USBD_URB_STATUS(urb) = status;
394 device_printf(dev, "func 0x%x isn't supported\n",
395 urb->uu_hdr.uuh_func);
396 USBD_URB_STATUS(urb) = status = USBD_STATUS_NOT_SUPPORTED;
404 usbd_func_getdesc(irp *ip)
406 device_t dev = IRP_NDIS_DEV(ip);
408 struct usb_attach_arg *uaa = device_get_ivars(dev);
409 struct usbd_urb_control_descriptor_request *ctldesc;
412 usb_config_descriptor_t cd, *cdp;
417 urb = usbd_geturb(ip);
418 ctldesc = &urb->uu_ctldesc;
419 if (ctldesc->ucd_desctype == UDESC_CONFIG) {
420 /* Get the short config descriptor. */
421 status = usbd_get_config_desc(uaa->device, ctldesc->ucd_idx,
423 if (status != USBD_NORMAL_COMPLETION) {
424 ctldesc->ucd_trans_buflen = 0;
426 return usbd_usb2urb(status);
428 /* Get the full descriptor. Try a few times for slow devices. */
429 len = MIN(ctldesc->ucd_trans_buflen, UGETW(cd.wTotalLength));
430 for (i = 0; i < 3; i++) {
431 status = usbd_get_desc_ndis(uaa->device,
432 ctldesc->ucd_desctype, ctldesc->ucd_idx,
433 len, ctldesc->ucd_trans_buf, &actlen);
434 if (status == USBD_NORMAL_COMPLETION)
436 usbd_delay_ms(uaa->device, 200);
438 if (status != USBD_NORMAL_COMPLETION) {
439 ctldesc->ucd_trans_buflen = 0;
441 return usbd_usb2urb(status);
444 cdp = (usb_config_descriptor_t *)ctldesc->ucd_trans_buf;
445 if (cdp->bDescriptorType != UDESC_CONFIG) {
446 device_printf(dev, "bad desc %d\n",
447 cdp->bDescriptorType);
450 } else if (ctldesc->ucd_desctype == UDESC_STRING) {
451 /* Try a few times for slow devices. */
452 for (i = 0; i < 3; i++) {
453 status = usbd_get_string_desc(uaa->device,
454 (UDESC_STRING << 8) + ctldesc->ucd_idx,
455 ctldesc->ucd_langid, ctldesc->ucd_trans_buf,
457 if (actlen > ctldesc->ucd_trans_buflen)
458 panic("small string buffer for UDESC_STRING");
459 if (status == USBD_NORMAL_COMPLETION)
461 usbd_delay_ms(uaa->device, 200);
464 status = usbd_get_desc_ndis(uaa->device, ctldesc->ucd_desctype,
465 ctldesc->ucd_idx, ctldesc->ucd_trans_buflen,
466 ctldesc->ucd_trans_buf, &actlen);
468 if (status != USBD_NORMAL_COMPLETION) {
469 ctldesc->ucd_trans_buflen = 0;
471 return usbd_usb2urb(status);
474 ctldesc->ucd_trans_buflen = actlen;
475 ip->irp_iostat.isb_info = actlen;
479 return (USBD_STATUS_SUCCESS);
483 * FIXME: at USB1, not USB2, framework, there's no a interface to get `actlen'.
484 * However, we need it!!!
487 usbd_get_desc_ndis(usbd_device_handle dev, int type, int index, int len,
488 void *desc, int *actlen)
490 usb_device_request_t req;
492 req.bmRequestType = UT_READ_DEVICE;
493 req.bRequest = UR_GET_DESCRIPTOR;
494 USETW2(req.wValue, type, index);
495 USETW(req.wIndex, 0);
496 USETW(req.wLength, len);
497 return usbd_do_request_flags_pipe(dev, dev->default_pipe, &req, desc,
498 0, actlen, USBD_DEFAULT_TIMEOUT);
502 usbd_func_selconf(irp *ip)
504 device_t dev = IRP_NDIS_DEV(ip);
506 struct usb_attach_arg *uaa = device_get_ivars(dev);
507 struct usbd_interface_information *intf;
508 struct usbd_pipe_information *pipe;
509 struct usbd_urb_select_configuration *selconf;
511 usb_config_descriptor_t *conf;
512 usb_endpoint_descriptor_t *edesc;
513 usbd_device_handle udev = uaa->device;
514 usbd_interface_handle iface;
517 urb = usbd_geturb(ip);
519 selconf = &urb->uu_selconf;
520 conf = selconf->usc_conf;
522 device_printf(dev, "select configuration is NULL\n");
523 return usbd_usb2urb(USBD_NORMAL_COMPLETION);
526 if (conf->bConfigurationValue > NDISUSB_CONFIG_NO)
527 device_printf(dev, "warning: config_no is larger than default");
529 intf = &selconf->usc_intf;
530 for (i = 0; i < conf->bNumInterface && intf->uii_len > 0; i++) {
531 ret = usbd_device2interface_handle(uaa->device,
532 intf->uii_intfnum, &iface);
533 if (ret != USBD_NORMAL_COMPLETION) {
535 "getting interface handle failed: %s\n",
537 return usbd_usb2urb(ret);
540 ret = usbd_set_interface(iface, intf->uii_altset);
541 if (ret != USBD_NORMAL_COMPLETION && ret != USBD_IN_USE) {
543 "setting alternate interface failed: %s\n",
545 return usbd_usb2urb(ret);
548 for (j = 0; j < iface->idesc->bNumEndpoints; j++) {
549 if (j >= intf->uii_numeps) {
551 "endpoint %d and above are ignored",
555 edesc = iface->endpoints[j].edesc;
556 pipe = &intf->uii_pipes[j];
557 pipe->upi_handle = edesc;
558 pipe->upi_epaddr = edesc->bEndpointAddress;
559 pipe->upi_maxpktsize = UGETW(edesc->wMaxPacketSize);
560 pipe->upi_type = UE_GET_XFERTYPE(edesc->bmAttributes);
561 if (pipe->upi_type != UE_INTERRUPT)
564 /* XXX we're following linux USB's interval policy. */
565 if (udev->speed == USB_SPEED_LOW)
566 pipe->upi_interval = edesc->bInterval + 5;
567 else if (udev->speed == USB_SPEED_FULL)
568 pipe->upi_interval = edesc->bInterval;
574 } while (k1 < edesc->bInterval);
575 pipe->upi_interval = k0;
579 intf = (struct usbd_interface_information *)(((char *)intf) +
583 return (USBD_STATUS_SUCCESS);
587 usbd_func_abort_pipe(irp *ip)
589 device_t dev = IRP_NDIS_DEV(ip);
590 struct ndis_softc *sc = device_get_softc(dev);
592 struct usbd_urb_bulk_or_intr_transfer *ubi;
593 usb_endpoint_descriptor_t *ep;
597 urb = usbd_geturb(ip);
598 ubi = &urb->uu_bulkintr;
599 ep = ubi->ubi_epdesc;
601 return (USBD_STATUS_INVALID_PIPE_HANDLE);
603 KeRaiseIrql(DISPATCH_LEVEL, &irql);
604 status = usbd_abort_pipe(sc->ndisusb_ep[NDISUSB_ENDPT_BIN]);
605 if (status != USBD_NORMAL_COMPLETION)
606 device_printf(dev, "can't be canceld");
609 return (USBD_STATUS_SUCCESS);
613 usbd_func_vendorclass(irp *ip)
615 device_t dev = IRP_NDIS_DEV(ip);
616 struct usb_attach_arg *uaa = device_get_ivars(dev);
617 struct usbd_urb_vendor_or_class_request *vcreq;
620 usb_device_request_t req;
623 urb = usbd_geturb(ip);
624 vcreq = &urb->uu_vcreq;
626 switch (urb->uu_hdr.uuh_func) {
627 case URB_FUNCTION_CLASS_DEVICE:
628 type = UT_CLASS | UT_DEVICE;
630 case URB_FUNCTION_CLASS_INTERFACE:
631 type = UT_CLASS | UT_INTERFACE;
633 case URB_FUNCTION_CLASS_OTHER:
634 type = UT_CLASS | UT_OTHER;
636 case URB_FUNCTION_CLASS_ENDPOINT:
637 type = UT_CLASS | UT_ENDPOINT;
639 case URB_FUNCTION_VENDOR_DEVICE:
640 type = UT_VENDOR | UT_DEVICE;
642 case URB_FUNCTION_VENDOR_INTERFACE:
643 type = UT_VENDOR | UT_INTERFACE;
645 case URB_FUNCTION_VENDOR_OTHER:
646 type = UT_VENDOR | UT_OTHER;
648 case URB_FUNCTION_VENDOR_ENDPOINT:
649 type = UT_VENDOR | UT_ENDPOINT;
656 type |= (vcreq->uvc_trans_flags & USBD_TRANSFER_DIRECTION_IN) ?
658 type |= vcreq->uvc_reserved1;
660 req.bmRequestType = type;
661 req.bRequest = vcreq->uvc_req;
662 USETW(req.wIndex, vcreq->uvc_idx);
663 USETW(req.wValue, vcreq->uvc_value);
664 USETW(req.wLength, vcreq->uvc_trans_buflen);
666 if (vcreq->uvc_trans_flags & USBD_TRANSFER_DIRECTION_IN) {
668 status = usbd_do_request(uaa->device, &req,
669 vcreq->uvc_trans_buf);
672 status = usbd_do_request_async(uaa->device, &req,
673 vcreq->uvc_trans_buf);
675 return usbd_usb2urb(status);
679 usbd_init_ndispipe(irp *ip, usb_endpoint_descriptor_t *ep)
681 device_t dev = IRP_NDIS_DEV(ip);
682 struct ndis_softc *sc = device_get_softc(dev);
683 struct usb_attach_arg *uaa = device_get_ivars(dev);
684 usbd_interface_handle iface;
687 status = usbd_device2interface_handle(uaa->device, NDISUSB_IFACE_INDEX,
689 if (status != USBD_NORMAL_COMPLETION) {
690 device_printf(dev, "could not get interface handle\n");
694 switch (UE_GET_XFERTYPE(ep->bmAttributes)) {
696 if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN) {
698 if (sc->ndisusb_ep[NDISUSB_ENDPT_BIN] != NULL)
699 return (USBD_NORMAL_COMPLETION);
701 status = usbd_open_pipe(iface, ep->bEndpointAddress,
703 &sc->ndisusb_ep[NDISUSB_ENDPT_BIN]);
708 if (sc->ndisusb_ep[NDISUSB_ENDPT_BOUT] != NULL)
709 return (USBD_NORMAL_COMPLETION);
711 status = usbd_open_pipe(iface, ep->bEndpointAddress,
712 USBD_EXCLUSIVE_USE, &sc->ndisusb_ep[NDISUSB_ENDPT_BOUT]);
715 if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN) {
717 if (sc->ndisusb_ep[NDISUSB_ENDPT_IIN] != NULL)
718 return (USBD_NORMAL_COMPLETION);
720 status = usbd_open_pipe(iface, ep->bEndpointAddress,
722 &sc->ndisusb_ep[NDISUSB_ENDPT_IIN]);
727 if (sc->ndisusb_ep[NDISUSB_ENDPT_IOUT] != NULL)
728 return (USBD_NORMAL_COMPLETION);
730 status = usbd_open_pipe(iface, ep->bEndpointAddress,
731 USBD_EXCLUSIVE_USE, &sc->ndisusb_ep[NDISUSB_ENDPT_IOUT]);
734 device_printf(dev, "can't handle xfertype 0x%x\n",
735 UE_GET_XFERTYPE(ep->bmAttributes));
739 if (status != USBD_NORMAL_COMPLETION)
740 device_printf(dev, "open pipe failed: (0x%x) %s\n",
741 ep->bEndpointAddress, usbd_errstr(status));
747 usbd_irpcancel_cb(void *priv)
749 struct ndisusb_cancel *nc = priv;
750 struct ndis_softc *sc = device_get_softc(nc->dev);
752 usbd_xfer_handle xfer = nc->xfer;
754 if (sc->ndisusb_status & NDISUSB_STATUS_DETACH)
757 status = usbd_abort_pipe(xfer->pipe);
758 if (status != USBD_NORMAL_COMPLETION)
759 device_printf(nc->dev, "can't be canceld");
765 usbd_irpcancel(device_object *dobj, irp *ip)
767 device_t dev = IRP_NDIS_DEV(ip);
768 struct ndisusb_cancel *nc;
769 struct usb_attach_arg *uaa = device_get_ivars(dev);
771 if (IRP_NDISUSB_XFER(ip) == NULL) {
772 ip->irp_cancel = TRUE;
773 IoReleaseCancelSpinLock(ip->irp_cancelirql);
778 * XXX Since we're under DISPATCH_LEVEL during calling usbd_irpcancel(),
779 * we can't sleep at all. However, currently FreeBSD's USB stack
780 * requires a sleep to abort a transfer. It's inevitable! so it causes
781 * serveral fatal problems (e.g. kernel hangups or crashes). I think
782 * that there are no ways to make this reliable. In this implementation,
783 * I used usb_add_task() but it's not a perfect method to solve this
784 * because of as follows: NDIS drivers would expect that IRP's
785 * completely canceld when usbd_irpcancel() is returned but we need
786 * a sleep to do it. During canceling XFERs, usbd_intr() would be
787 * called with a status, USBD_CANCELLED.
789 nc = kmalloc(sizeof(struct ndisusb_cancel), M_USBDEV, M_NOWAIT | M_ZERO);
791 ip->irp_cancel = FALSE;
792 IoReleaseCancelSpinLock(ip->irp_cancelirql);
797 nc->xfer = IRP_NDISUSB_XFER(ip);
798 usb_init_task(&nc->task, usbd_irpcancel_cb, nc);
800 IRP_NDISUSB_XFER(ip) = NULL;
801 usb_add_task(uaa->device, &nc->task, USB_TASKQ_DRIVER);
803 ip->irp_cancel = TRUE;
804 IoReleaseCancelSpinLock(ip->irp_cancelirql);
807 static usbd_xfer_handle
808 usbd_init_ndisxfer(irp *ip, usb_endpoint_descriptor_t *ep, void *buf,
811 device_t dev = IRP_NDIS_DEV(ip);
812 struct usb_attach_arg *uaa = device_get_ivars(dev);
813 usbd_xfer_handle xfer;
815 xfer = usbd_alloc_xfer(uaa->device);
819 if (buf != NULL && MmIsAddressValid(buf) == FALSE && buflen > 0) {
820 xfer->buffer = usbd_alloc_buffer(xfer, buflen);
821 if (xfer->buffer == NULL)
824 if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_OUT)
825 memcpy(xfer->buffer, buf, buflen);
829 xfer->length = buflen;
831 IoAcquireCancelSpinLock(&ip->irp_cancelirql);
832 IRP_NDISUSB_XFER(ip) = xfer;
833 ip->irp_cancelfunc = (cancel_func)usbd_irpcancel_wrap;
834 IoReleaseCancelSpinLock(ip->irp_cancelirql);
840 usbd_xferadd(usbd_xfer_handle xfer, usbd_private_handle priv,
844 device_t dev = IRP_NDIS_DEV(ip);
845 struct ndis_softc *sc = device_get_softc(dev);
846 struct ndisusb_xfer *nx;
849 nx = kmalloc(sizeof(struct ndisusb_xfer), M_USBDEV, M_NOWAIT | M_ZERO);
851 device_printf(dev, "out of memory");
856 nx->nx_status = status;
858 KeAcquireSpinLock(&sc->ndisusb_xferlock, &irql);
859 InsertTailList((&sc->ndisusb_xferlist), (&nx->nx_xferlist));
860 KeReleaseSpinLock(&sc->ndisusb_xferlock, irql);
862 IoQueueWorkItem(sc->ndisusb_xferitem,
863 (io_workitem_func)usbd_xfertask_wrap, WORKQUEUE_CRITICAL, sc);
867 usbd_xfereof(usbd_xfer_handle xfer, usbd_private_handle priv,
871 usbd_xferadd(xfer, priv, status);
875 usbd_xfertask(device_object *dobj, void *arg)
881 struct ndis_softc *sc = arg;
882 struct ndisusb_xfer *nx;
883 struct usbd_urb_bulk_or_intr_transfer *ubi;
886 usbd_private_handle priv;
888 usbd_xfer_handle xfer;
892 if (IsListEmpty(&sc->ndisusb_xferlist))
895 KeAcquireSpinLock(&sc->ndisusb_xferlock, &irql);
896 l = sc->ndisusb_xferlist.nle_flink;
897 while (l != &sc->ndisusb_xferlist) {
898 nx = CONTAINING_RECORD(l, struct ndisusb_xfer, nx_xferlist);
901 status = nx->nx_status;
905 if (status != USBD_NORMAL_COMPLETION) {
906 if (status == USBD_NOT_STARTED) {
910 if (status == USBD_STALLED)
911 usbd_clear_endpoint_stall_async(xfer->pipe);
913 * NB: just for notice. We must handle error cases also
914 * because if we just return without notifying to the
915 * NDIS driver the driver never knows about that there
916 * was a error. This can cause a lot of problems like
919 device_printf(dev, "usb xfer warning (%s)\n",
920 usbd_errstr(status));
923 urb = usbd_geturb(ip);
925 KASSERT(urb->uu_hdr.uuh_func ==
926 URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER,
927 ("function(%d) isn't for bulk or interrupt",
928 urb->uu_hdr.uuh_func));
930 IoAcquireCancelSpinLock(&ip->irp_cancelirql);
932 ip->irp_cancelfunc = NULL;
933 IRP_NDISUSB_XFER(ip) = NULL;
936 case USBD_NORMAL_COMPLETION:
937 ubi = &urb->uu_bulkintr;
938 ubi->ubi_trans_buflen = xfer->actlen;
939 if (ubi->ubi_trans_flags & USBD_TRANSFER_DIRECTION_IN)
940 memcpy(ubi->ubi_trans_buf, xfer->buffer,
943 ip->irp_iostat.isb_info = xfer->actlen;
944 ip->irp_iostat.isb_status = STATUS_SUCCESS;
945 USBD_URB_STATUS(urb) = USBD_STATUS_SUCCESS;
948 ip->irp_iostat.isb_info = 0;
949 ip->irp_iostat.isb_status = STATUS_CANCELLED;
950 USBD_URB_STATUS(urb) = USBD_STATUS_CANCELED;
953 ip->irp_iostat.isb_info = 0;
954 USBD_URB_STATUS(urb) = usbd_usb2urb(status);
955 ip->irp_iostat.isb_status =
956 usbd_urb2nt(USBD_URB_STATUS(urb));
960 IoReleaseCancelSpinLock(ip->irp_cancelirql);
963 RemoveEntryList(&nx->nx_xferlist);
964 usbd_free_xfer(nx->nx_xfer);
968 /* NB: call after cleaning */
969 IoCompleteRequest(ip, IO_NO_INCREMENT);
971 KeReleaseSpinLock(&sc->ndisusb_xferlock, irql);
975 usbd_func_bulkintr(irp *ip)
977 device_t dev = IRP_NDIS_DEV(ip);
978 struct ndis_softc *sc = device_get_softc(dev);
979 struct usbd_urb_bulk_or_intr_transfer *ubi;
981 usb_endpoint_descriptor_t *ep;
983 usbd_xfer_handle xfer;
985 urb = usbd_geturb(ip);
986 ubi = &urb->uu_bulkintr;
987 ep = ubi->ubi_epdesc;
989 return (USBD_STATUS_INVALID_PIPE_HANDLE);
991 status = usbd_init_ndispipe(ip, ep);
992 if (status != USBD_NORMAL_COMPLETION)
993 return usbd_usb2urb(status);
995 xfer = usbd_init_ndisxfer(ip, ep, ubi->ubi_trans_buf,
996 ubi->ubi_trans_buflen);
998 device_printf(IRP_NDIS_DEV(ip), "can't allocate xfer\n");
999 return (USBD_STATUS_NO_MEMORY);
1002 if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN) {
1003 xfer->flags |= USBD_SHORT_XFER_OK;
1004 if (!(ubi->ubi_trans_flags & USBD_SHORT_TRANSFER_OK))
1005 xfer->flags &= ~USBD_SHORT_XFER_OK;
1008 if (UE_GET_XFERTYPE(ep->bmAttributes) == UE_BULK) {
1009 if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN)
1011 usbd_setup_xfer(xfer, sc->ndisusb_ep[NDISUSB_ENDPT_BIN],
1012 ip, xfer->buffer, xfer->length, xfer->flags,
1013 USBD_NO_TIMEOUT, usbd_xfereof);
1016 xfer->flags |= USBD_NO_COPY;
1018 usbd_setup_xfer(xfer, sc->ndisusb_ep[NDISUSB_ENDPT_BOUT],
1019 ip, xfer->buffer, xfer->length, xfer->flags,
1020 NDISUSB_TX_TIMEOUT, usbd_xfereof);
1023 if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN)
1025 usbd_setup_xfer(xfer, sc->ndisusb_ep[NDISUSB_ENDPT_IIN],
1026 ip, xfer->buffer, xfer->length, xfer->flags,
1027 USBD_NO_TIMEOUT, usbd_xfereof);
1030 usbd_setup_xfer(xfer, sc->ndisusb_ep[NDISUSB_ENDPT_IOUT],
1031 ip, xfer->buffer, xfer->length, xfer->flags,
1032 NDISUSB_INTR_TIMEOUT, usbd_xfereof);
1035 /* we've done to setup xfer. Let's transfer it. */
1036 ip->irp_iostat.isb_status = STATUS_PENDING;
1037 ip->irp_iostat.isb_info = 0;
1038 USBD_URB_STATUS(urb) = USBD_STATUS_PENDING;
1039 IoMarkIrpPending(ip);
1041 status = usbd_transfer(xfer);
1042 if (status == USBD_IN_PROGRESS)
1043 return (USBD_STATUS_PENDING);
1045 usbd_free_xfer(xfer);
1046 IRP_NDISUSB_XFER(ip) = NULL;
1047 IoUnmarkIrpPending(ip);
1048 USBD_URB_STATUS(urb) = usbd_usb2urb(status);
1050 return USBD_URB_STATUS(urb);
1053 static union usbd_urb *
1054 USBD_CreateConfigurationRequest(usb_config_descriptor_t *conf, uint16_t *len)
1056 struct usbd_interface_list_entry list[2];
1057 union usbd_urb *urb;
1059 bzero(list, sizeof(struct usbd_interface_list_entry) * 2);
1060 list[0].uil_intfdesc = USBD_ParseConfigurationDescriptorEx(conf, conf,
1061 -1, -1, -1, -1, -1);
1062 urb = USBD_CreateConfigurationRequestEx(conf, list);
1066 *len = urb->uu_selconf.usc_hdr.uuh_len;
1070 static union usbd_urb *
1071 USBD_CreateConfigurationRequestEx(usb_config_descriptor_t *conf,
1072 struct usbd_interface_list_entry *list)
1075 struct usbd_interface_information *intf;
1076 struct usbd_pipe_information *pipe;
1077 struct usbd_urb_select_configuration *selconf;
1078 usb_interface_descriptor_t *desc;
1080 for (i = 0, size = 0; i < conf->bNumInterface; i++) {
1081 j = list[i].uil_intfdesc->bNumEndpoints;
1082 size = size + sizeof(struct usbd_interface_information) +
1083 sizeof(struct usbd_pipe_information) * (j - 1);
1085 size += sizeof(struct usbd_urb_select_configuration) -
1086 sizeof(struct usbd_interface_information);
1088 selconf = ExAllocatePoolWithTag(NonPagedPool, size, 0);
1089 if (selconf == NULL)
1091 selconf->usc_hdr.uuh_func = URB_FUNCTION_SELECT_CONFIGURATION;
1092 selconf->usc_hdr.uuh_len = size;
1093 selconf->usc_handle = conf;
1094 selconf->usc_conf = conf;
1096 intf = &selconf->usc_intf;
1097 for (i = 0; i < conf->bNumInterface; i++) {
1098 if (list[i].uil_intfdesc == NULL)
1101 list[i].uil_intf = intf;
1102 desc = list[i].uil_intfdesc;
1104 intf->uii_len = sizeof(struct usbd_interface_information) +
1105 (desc->bNumEndpoints - 1) *
1106 sizeof(struct usbd_pipe_information);
1107 intf->uii_intfnum = desc->bInterfaceNumber;
1108 intf->uii_altset = desc->bAlternateSetting;
1109 intf->uii_intfclass = desc->bInterfaceClass;
1110 intf->uii_intfsubclass = desc->bInterfaceSubClass;
1111 intf->uii_intfproto = desc->bInterfaceProtocol;
1112 intf->uii_handle = desc;
1113 intf->uii_numeps = desc->bNumEndpoints;
1115 pipe = &intf->uii_pipes[0];
1116 for (j = 0; j < intf->uii_numeps; j++)
1117 pipe[j].upi_maxtxsize =
1118 USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE;
1120 intf = (struct usbd_interface_information *)((char *)intf +
1124 return ((union usbd_urb *)selconf);
1128 USBD_GetUSBDIVersion(usbd_version_info *ui)
1131 /* Pretend to be Windows XP. */
1133 ui->uvi_usbdi_vers = USBDI_VERSION;
1134 ui->uvi_supported_vers = USB_VER_2_0;
1137 static usb_interface_descriptor_t *
1138 USBD_ParseConfigurationDescriptor(usb_config_descriptor_t *conf,
1139 uint8_t intfnum, uint8_t altset)
1142 return USBD_ParseConfigurationDescriptorEx(conf, conf, intfnum, altset,
1146 static usb_interface_descriptor_t *
1147 USBD_ParseConfigurationDescriptorEx(usb_config_descriptor_t *conf,
1148 void *start, int32_t intfnum, int32_t altset, int32_t intfclass,
1149 int32_t intfsubclass, int32_t intfproto)
1152 usb_interface_descriptor_t *desc;
1154 for (pos = start; pos < ((char *)conf + UGETW(conf->wTotalLength));
1155 pos += desc->bLength) {
1156 desc = (usb_interface_descriptor_t *)pos;
1157 if (desc->bDescriptorType != UDESC_INTERFACE)
1159 if (!(intfnum == -1 || desc->bInterfaceNumber == intfnum))
1161 if (!(altset == -1 || desc->bAlternateSetting == altset))
1163 if (!(intfclass == -1 || desc->bInterfaceClass == intfclass))
1165 if (!(intfsubclass == -1 ||
1166 desc->bInterfaceSubClass == intfsubclass))
1168 if (!(intfproto == -1 || desc->bInterfaceProtocol == intfproto))
1179 kprintf("USBD dummy called\n");
1182 image_patch_table usbd_functbl[] = {
1183 IMPORT_SFUNC(USBD_CreateConfigurationRequest, 2),
1184 IMPORT_SFUNC(USBD_CreateConfigurationRequestEx, 2),
1185 IMPORT_SFUNC_MAP(_USBD_CreateConfigurationRequestEx@8,
1186 USBD_CreateConfigurationRequestEx, 2),
1187 IMPORT_SFUNC(USBD_GetUSBDIVersion, 1),
1188 IMPORT_SFUNC(USBD_ParseConfigurationDescriptor, 3),
1189 IMPORT_SFUNC(USBD_ParseConfigurationDescriptorEx, 7),
1190 IMPORT_SFUNC_MAP(_USBD_ParseConfigurationDescriptorEx@28,
1191 USBD_ParseConfigurationDescriptorEx, 7),
1194 * This last entry is a catch-all for any function we haven't
1195 * implemented yet. The PE import list patching routine will
1196 * use it for any function that doesn't have an explicit match
1200 { NULL, (FUNC)dummy, NULL, 0, WINDRV_WRAP_STDCALL },
1204 { NULL, NULL, NULL }
1207 MODULE_DEPEND(ndis, usb, 1, 1, 1);