libm: Fix misleading indent.
[dragonfly.git] / sys / emulation / ndis / subr_u4bd.c
blob909c774e9c78a2b2ba8d81bb16421ed3bc34ffb3
1 /*-
2 * Copyright (c) 2005
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
7 * are met:
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.22 2012/11/17 01:51:26 svnexp 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>
42 #include <sys/lock.h>
43 #include <sys/condvar.h>
44 #include <sys/module.h>
45 #include <sys/conf.h>
46 #include <sys/mbuf.h>
47 #include <sys/socket.h>
48 #include <sys/bus.h>
50 #include <sys/queue.h>
52 #include <net/if.h>
53 #include <net/if_media.h>
54 #include <netproto/802_11/ieee80211_var.h>
55 #include <netproto/802_11/ieee80211_ioctl.h>
57 #include <bus/u4b/usb.h>
58 #include <bus/u4b/usbdi.h>
59 #include <bus/u4b/usbdi_util.h>
60 #include <bus/u4b/usb_busdma.h>
61 #include <bus/u4b/usb_device.h>
62 #include <bus/u4b/usb_request.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/u4bd_var.h>
71 #include <dev/netif/ndis/if_ndisvar.h>
73 static driver_object usbd_driver;
74 static usb_callback_t usbd_non_isoc_callback;
75 static usb_callback_t usbd_ctrl_callback;
77 #define USBD_CTRL_READ_PIPE 0
78 #define USBD_CTRL_WRITE_PIPE 1
79 #define USBD_CTRL_MAX_PIPE 2
80 #define USBD_CTRL_READ_BUFFER_SP 256
81 #define USBD_CTRL_WRITE_BUFFER_SP 256
82 #define USBD_CTRL_READ_BUFFER_SIZE \
83 (sizeof(struct usb_device_request) + USBD_CTRL_READ_BUFFER_SP)
84 #define USBD_CTRL_WRITE_BUFFER_SIZE \
85 (sizeof(struct usb_device_request) + USBD_CTRL_WRITE_BUFFER_SP)
86 static struct usb_config usbd_default_epconfig[USBD_CTRL_MAX_PIPE] = {
87 [USBD_CTRL_READ_PIPE] = {
88 .type = UE_CONTROL,
89 .endpoint = 0x00, /* control pipe */
90 .direction = UE_DIR_ANY,
91 .if_index = 0,
92 .bufsize = USBD_CTRL_READ_BUFFER_SIZE,
93 .flags = { .short_xfer_ok = 1, },
94 .callback = &usbd_ctrl_callback,
95 .timeout = 5000, /* 5 seconds */
97 [USBD_CTRL_WRITE_PIPE] = {
98 .type = UE_CONTROL,
99 .endpoint = 0x00, /* control pipe */
100 .direction = UE_DIR_ANY,
101 .if_index = 0,
102 .bufsize = USBD_CTRL_WRITE_BUFFER_SIZE,
103 .flags = { .proxy_buffer = 1, },
104 .callback = &usbd_ctrl_callback,
105 .timeout = 5000, /* 5 seconds */
109 static int32_t usbd_func_bulkintr(irp *);
110 static int32_t usbd_func_vendorclass(irp *);
111 static int32_t usbd_func_selconf(irp *);
112 static int32_t usbd_func_abort_pipe(irp *);
113 static usb_error_t usbd_setup_endpoint(irp *, uint8_t,
114 struct usb_endpoint_descriptor *);
115 static usb_error_t usbd_setup_endpoint_default(irp *, uint8_t);
116 static usb_error_t usbd_setup_endpoint_one(irp *, uint8_t,
117 struct ndisusb_ep *, struct usb_config *);
118 static int32_t usbd_func_getdesc(irp *);
119 static union usbd_urb *usbd_geturb(irp *);
120 static struct ndisusb_ep*usbd_get_ndisep(irp *, usb_endpoint_descriptor_t *);
121 static int32_t usbd_iodispatch(device_object *, irp *);
122 static int32_t usbd_ioinvalid(device_object *, irp *);
123 static int32_t usbd_pnp(device_object *, irp *);
124 static int32_t usbd_power(device_object *, irp *);
125 static void usbd_irpcancel(device_object *, irp *);
126 static int32_t usbd_submit_urb(irp *);
127 static int32_t usbd_urb2nt(int32_t);
128 static void usbd_task(device_object *, void *);
129 static int32_t usbd_taskadd(irp *, unsigned);
130 static void usbd_xfertask(device_object *, void *);
131 static void dummy(void);
133 static union usbd_urb *USBD_CreateConfigurationRequestEx(
134 usb_config_descriptor_t *,
135 struct usbd_interface_list_entry *);
136 static union usbd_urb *USBD_CreateConfigurationRequest(
137 usb_config_descriptor_t *,
138 uint16_t *);
139 static void USBD_GetUSBDIVersion(usbd_version_info *);
140 static usb_interface_descriptor_t *USBD_ParseConfigurationDescriptorEx(
141 usb_config_descriptor_t *, void *, int32_t, int32_t,
142 int32_t, int32_t, int32_t);
143 static usb_interface_descriptor_t *USBD_ParseConfigurationDescriptor(
144 usb_config_descriptor_t *, uint8_t, uint8_t);
147 * We need to wrap these functions because these need `context switch' from
148 * Windows to UNIX before it's called.
150 static funcptr usbd_iodispatch_wrap;
151 static funcptr usbd_ioinvalid_wrap;
152 static funcptr usbd_pnp_wrap;
153 static funcptr usbd_power_wrap;
154 static funcptr usbd_irpcancel_wrap;
155 static funcptr usbd_task_wrap;
156 static funcptr usbd_xfertask_wrap;
159 usbd_libinit(void)
161 image_patch_table *patch;
162 int i;
164 patch = usbd_functbl;
165 while (patch->ipt_func != NULL) {
166 windrv_wrap((funcptr)patch->ipt_func,
167 (funcptr *)&patch->ipt_wrap,
168 patch->ipt_argcnt, patch->ipt_ftype);
169 patch++;
172 windrv_wrap((funcptr)usbd_ioinvalid,
173 (funcptr *)&usbd_ioinvalid_wrap, 2, WINDRV_WRAP_STDCALL);
174 windrv_wrap((funcptr)usbd_iodispatch,
175 (funcptr *)&usbd_iodispatch_wrap, 2, WINDRV_WRAP_STDCALL);
176 windrv_wrap((funcptr)usbd_pnp,
177 (funcptr *)&usbd_pnp_wrap, 2, WINDRV_WRAP_STDCALL);
178 windrv_wrap((funcptr)usbd_power,
179 (funcptr *)&usbd_power_wrap, 2, WINDRV_WRAP_STDCALL);
180 windrv_wrap((funcptr)usbd_irpcancel,
181 (funcptr *)&usbd_irpcancel_wrap, 2, WINDRV_WRAP_STDCALL);
182 windrv_wrap((funcptr)usbd_task,
183 (funcptr *)&usbd_task_wrap, 2, WINDRV_WRAP_STDCALL);
184 windrv_wrap((funcptr)usbd_xfertask,
185 (funcptr *)&usbd_xfertask_wrap, 2, WINDRV_WRAP_STDCALL);
187 /* Create a fake USB driver instance. */
189 windrv_bus_attach(&usbd_driver, "USB Bus");
191 /* Set up our dipatch routine. */
192 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
193 usbd_driver.dro_dispatch[i] =
194 (driver_dispatch)usbd_ioinvalid_wrap;
196 usbd_driver.dro_dispatch[IRP_MJ_INTERNAL_DEVICE_CONTROL] =
197 (driver_dispatch)usbd_iodispatch_wrap;
198 usbd_driver.dro_dispatch[IRP_MJ_DEVICE_CONTROL] =
199 (driver_dispatch)usbd_iodispatch_wrap;
200 usbd_driver.dro_dispatch[IRP_MJ_POWER] =
201 (driver_dispatch)usbd_power_wrap;
202 usbd_driver.dro_dispatch[IRP_MJ_PNP] =
203 (driver_dispatch)usbd_pnp_wrap;
205 return (0);
209 usbd_libfini(void)
211 image_patch_table *patch;
213 patch = usbd_functbl;
214 while (patch->ipt_func != NULL) {
215 windrv_unwrap(patch->ipt_wrap);
216 patch++;
219 windrv_unwrap(usbd_ioinvalid_wrap);
220 windrv_unwrap(usbd_iodispatch_wrap);
221 windrv_unwrap(usbd_pnp_wrap);
222 windrv_unwrap(usbd_power_wrap);
223 windrv_unwrap(usbd_irpcancel_wrap);
224 windrv_unwrap(usbd_task_wrap);
225 windrv_unwrap(usbd_xfertask_wrap);
227 kfree(usbd_driver.dro_drivername.us_buf, M_DEVBUF);
229 return (0);
232 static int32_t
233 usbd_iodispatch(device_object *dobj, irp *ip)
235 device_t dev = dobj->do_devext;
236 int32_t status;
237 struct io_stack_location *irp_sl;
239 irp_sl = IoGetCurrentIrpStackLocation(ip);
240 switch (irp_sl->isl_parameters.isl_ioctl.isl_iocode) {
241 case IOCTL_INTERNAL_USB_SUBMIT_URB:
242 IRP_NDIS_DEV(ip) = dev;
244 status = usbd_submit_urb(ip);
245 break;
246 default:
247 device_printf(dev, "ioctl 0x%x isn't supported\n",
248 irp_sl->isl_parameters.isl_ioctl.isl_iocode);
249 status = USBD_STATUS_NOT_SUPPORTED;
250 break;
253 if (status == USBD_STATUS_PENDING)
254 return (STATUS_PENDING);
256 ip->irp_iostat.isb_status = usbd_urb2nt(status);
257 if (status != USBD_STATUS_SUCCESS)
258 ip->irp_iostat.isb_info = 0;
259 return (ip->irp_iostat.isb_status);
262 static int32_t
263 usbd_ioinvalid(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, "invalid I/O dispatch %d:%d\n", irp_sl->isl_major,
270 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 static int32_t
281 usbd_pnp(device_object *dobj, irp *ip)
283 device_t dev = dobj->do_devext;
284 struct io_stack_location *irp_sl;
286 irp_sl = IoGetCurrentIrpStackLocation(ip);
287 device_printf(dev, "%s: unsupported I/O dispatch %d:%d\n",
288 __func__, irp_sl->isl_major, irp_sl->isl_minor);
290 ip->irp_iostat.isb_status = STATUS_FAILURE;
291 ip->irp_iostat.isb_info = 0;
293 IoCompleteRequest(ip, IO_NO_INCREMENT);
295 return (STATUS_FAILURE);
298 static int32_t
299 usbd_power(device_object *dobj, irp *ip)
301 device_t dev = dobj->do_devext;
302 struct io_stack_location *irp_sl;
304 irp_sl = IoGetCurrentIrpStackLocation(ip);
305 device_printf(dev, "%s: unsupported I/O dispatch %d:%d\n",
306 __func__, irp_sl->isl_major, irp_sl->isl_minor);
308 ip->irp_iostat.isb_status = STATUS_FAILURE;
309 ip->irp_iostat.isb_info = 0;
311 IoCompleteRequest(ip, IO_NO_INCREMENT);
313 return (STATUS_FAILURE);
316 /* Convert USBD_STATUS to NTSTATUS */
317 static int32_t
318 usbd_urb2nt(int32_t status)
321 switch (status) {
322 case USBD_STATUS_SUCCESS:
323 return (STATUS_SUCCESS);
324 case USBD_STATUS_DEVICE_GONE:
325 return (STATUS_DEVICE_NOT_CONNECTED);
326 case USBD_STATUS_PENDING:
327 return (STATUS_PENDING);
328 case USBD_STATUS_NOT_SUPPORTED:
329 return (STATUS_NOT_IMPLEMENTED);
330 case USBD_STATUS_NO_MEMORY:
331 return (STATUS_NO_MEMORY);
332 case USBD_STATUS_REQUEST_FAILED:
333 return (STATUS_NOT_SUPPORTED);
334 case USBD_STATUS_CANCELED:
335 return (STATUS_CANCELLED);
336 default:
337 break;
340 return (STATUS_FAILURE);
343 /* Convert FreeBSD's usb_error_t to USBD_STATUS */
344 static int32_t
345 usbd_usb2urb(int status)
348 switch (status) {
349 case USB_ERR_NORMAL_COMPLETION:
350 return (USBD_STATUS_SUCCESS);
351 case USB_ERR_PENDING_REQUESTS:
352 return (USBD_STATUS_PENDING);
353 case USB_ERR_TIMEOUT:
354 return (USBD_STATUS_TIMEOUT);
355 case USB_ERR_SHORT_XFER:
356 return (USBD_STATUS_ERROR_SHORT_TRANSFER);
357 case USB_ERR_IOERROR:
358 return (USBD_STATUS_XACT_ERROR);
359 case USB_ERR_NOMEM:
360 return (USBD_STATUS_NO_MEMORY);
361 case USB_ERR_INVAL:
362 return (USBD_STATUS_REQUEST_FAILED);
363 case USB_ERR_NOT_STARTED:
364 case USB_ERR_TOO_DEEP:
365 case USB_ERR_NO_POWER:
366 return (USBD_STATUS_DEVICE_GONE);
367 case USB_ERR_CANCELLED:
368 return (USBD_STATUS_CANCELED);
369 default:
370 break;
373 return (USBD_STATUS_NOT_SUPPORTED);
376 static union usbd_urb *
377 usbd_geturb(irp *ip)
379 struct io_stack_location *irp_sl;
381 irp_sl = IoGetCurrentIrpStackLocation(ip);
383 return (irp_sl->isl_parameters.isl_others.isl_arg1);
386 static int32_t
387 usbd_submit_urb(irp *ip)
389 device_t dev = IRP_NDIS_DEV(ip);
390 int32_t status;
391 union usbd_urb *urb;
393 urb = usbd_geturb(ip);
395 * In a case of URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER,
396 * USBD_URB_STATUS(urb) would be set at callback functions like
397 * usbd_intr() or usbd_xfereof().
399 switch (urb->uu_hdr.uuh_func) {
400 case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
401 status = usbd_func_bulkintr(ip);
402 if (status != USBD_STATUS_SUCCESS &&
403 status != USBD_STATUS_PENDING)
404 USBD_URB_STATUS(urb) = status;
405 break;
406 case URB_FUNCTION_VENDOR_DEVICE:
407 case URB_FUNCTION_VENDOR_INTERFACE:
408 case URB_FUNCTION_VENDOR_ENDPOINT:
409 case URB_FUNCTION_VENDOR_OTHER:
410 case URB_FUNCTION_CLASS_DEVICE:
411 case URB_FUNCTION_CLASS_INTERFACE:
412 case URB_FUNCTION_CLASS_ENDPOINT:
413 case URB_FUNCTION_CLASS_OTHER:
414 status = usbd_func_vendorclass(ip);
415 USBD_URB_STATUS(urb) = status;
416 break;
417 case URB_FUNCTION_SELECT_CONFIGURATION:
418 status = usbd_func_selconf(ip);
419 USBD_URB_STATUS(urb) = status;
420 break;
421 case URB_FUNCTION_ABORT_PIPE:
422 status = usbd_func_abort_pipe(ip);
423 USBD_URB_STATUS(urb) = status;
424 break;
425 case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
426 status = usbd_func_getdesc(ip);
427 USBD_URB_STATUS(urb) = status;
428 break;
429 default:
430 device_printf(dev, "func 0x%x isn't supported\n",
431 urb->uu_hdr.uuh_func);
432 USBD_URB_STATUS(urb) = status = USBD_STATUS_NOT_SUPPORTED;
433 break;
436 return (status);
439 static int32_t
440 usbd_func_getdesc(irp *ip)
442 #define NDISUSB_GETDESC_MAXRETRIES 3
443 device_t dev = IRP_NDIS_DEV(ip);
444 struct ndis_softc *sc = device_get_softc(dev);
445 struct usbd_urb_control_descriptor_request *ctldesc;
446 uint16_t actlen;
447 uint32_t len;
448 union usbd_urb *urb;
449 usb_config_descriptor_t *cdp;
450 usb_error_t status;
452 urb = usbd_geturb(ip);
453 ctldesc = &urb->uu_ctldesc;
454 if (ctldesc->ucd_desctype == UDESC_CONFIG) {
456 * The NDIS driver is not allowed to change the
457 * config! There is only one choice!
459 cdp = usbd_get_config_descriptor(sc->ndisusb_dev);
460 if (cdp == NULL) {
461 status = USB_ERR_INVAL;
462 goto exit;
464 if (cdp->bDescriptorType != UDESC_CONFIG) {
465 device_printf(dev, "bad desc %d\n",
466 cdp->bDescriptorType);
467 status = USB_ERR_INVAL;
468 goto exit;
470 /* get minimum length */
471 len = MIN(UGETW(cdp->wTotalLength), ctldesc->ucd_trans_buflen);
472 /* copy out config descriptor */
473 memcpy(ctldesc->ucd_trans_buf, cdp, len);
474 /* set actual length */
475 actlen = len;
476 status = USB_ERR_NORMAL_COMPLETION;
477 } else {
478 NDISUSB_LOCK(sc);
479 status = usbd_req_get_desc(sc->ndisusb_dev, &sc->ndisusb_lock,
480 &actlen, ctldesc->ucd_trans_buf, 2,
481 ctldesc->ucd_trans_buflen, ctldesc->ucd_langid,
482 ctldesc->ucd_desctype, ctldesc->ucd_idx,
483 NDISUSB_GETDESC_MAXRETRIES);
484 NDISUSB_UNLOCK(sc);
486 exit:
487 if (status != USB_ERR_NORMAL_COMPLETION) {
488 ctldesc->ucd_trans_buflen = 0;
489 return usbd_usb2urb(status);
492 ctldesc->ucd_trans_buflen = actlen;
493 ip->irp_iostat.isb_info = actlen;
495 return (USBD_STATUS_SUCCESS);
496 #undef NDISUSB_GETDESC_MAXRETRIES
499 static int32_t
500 usbd_func_selconf(irp *ip)
502 device_t dev = IRP_NDIS_DEV(ip);
503 int i, j;
504 struct ndis_softc *sc = device_get_softc(dev);
505 struct usb_device *udev = sc->ndisusb_dev;
506 struct usb_endpoint *ep = NULL;
507 struct usbd_interface_information *intf;
508 struct usbd_pipe_information *pipe;
509 struct usbd_urb_select_configuration *selconf;
510 union usbd_urb *urb;
511 usb_config_descriptor_t *conf;
512 usb_endpoint_descriptor_t *edesc;
513 usb_error_t ret;
515 urb = usbd_geturb(ip);
517 selconf = &urb->uu_selconf;
518 conf = selconf->usc_conf;
519 if (conf == NULL) {
520 device_printf(dev, "select configuration is NULL\n");
521 return usbd_usb2urb(USB_ERR_NORMAL_COMPLETION);
524 intf = &selconf->usc_intf;
525 for (i = 0; i < conf->bNumInterface && intf->uii_len > 0; i++) {
526 ret = usbd_set_alt_interface_index(udev,
527 intf->uii_intfnum, intf->uii_altset);
528 if (ret != USB_ERR_NORMAL_COMPLETION && ret != USB_ERR_IN_USE) {
529 device_printf(dev,
530 "setting alternate interface failed: %s\n",
531 usbd_errstr(ret));
532 return usbd_usb2urb(ret);
535 for (j = 0; (ep = usb_endpoint_foreach(udev, ep)); j++) {
536 if (j >= intf->uii_numeps) {
537 device_printf(dev,
538 "endpoint %d and above are ignored",
539 intf->uii_numeps);
540 break;
542 edesc = ep->edesc;
543 pipe = &intf->uii_pipes[j];
544 pipe->upi_handle = edesc;
545 pipe->upi_epaddr = edesc->bEndpointAddress;
546 pipe->upi_maxpktsize = UGETW(edesc->wMaxPacketSize);
547 pipe->upi_type = UE_GET_XFERTYPE(edesc->bmAttributes);
549 ret = usbd_setup_endpoint(ip, intf->uii_intfnum, edesc);
550 if (ret != USB_ERR_NORMAL_COMPLETION)
551 return usbd_usb2urb(ret);
553 if (pipe->upi_type != UE_INTERRUPT)
554 continue;
556 /* XXX we're following linux USB's interval policy. */
557 if (udev->speed == USB_SPEED_LOW)
558 pipe->upi_interval = edesc->bInterval + 5;
559 else if (udev->speed == USB_SPEED_FULL)
560 pipe->upi_interval = edesc->bInterval;
561 else {
562 int k0 = 0, k1 = 1;
563 do {
564 k1 = k1 * 2;
565 k0 = k0 + 1;
566 } while (k1 < edesc->bInterval);
567 pipe->upi_interval = k0;
571 intf = (struct usbd_interface_information *)(((char *)intf) +
572 intf->uii_len);
575 return (USBD_STATUS_SUCCESS);
578 static usb_error_t
579 usbd_setup_endpoint_one(irp *ip, uint8_t ifidx, struct ndisusb_ep *ne,
580 struct usb_config *epconf)
582 device_t dev = IRP_NDIS_DEV(ip);
583 struct ndis_softc *sc = device_get_softc(dev);
584 struct usb_xfer *xfer;
585 usb_error_t status;
587 InitializeListHead(&ne->ne_active);
588 InitializeListHead(&ne->ne_pending);
589 KeInitializeSpinLock(&ne->ne_lock);
591 status = usbd_transfer_setup(sc->ndisusb_dev, &ifidx, ne->ne_xfer,
592 epconf, 1, sc, &sc->ndisusb_lock);
593 if (status != USB_ERR_NORMAL_COMPLETION) {
594 device_printf(dev, "couldn't setup xfer: %s\n",
595 usbd_errstr(status));
596 return (status);
598 xfer = ne->ne_xfer[0];
599 usbd_xfer_set_priv(xfer, ne);
601 return (status);
604 static usb_error_t
605 usbd_setup_endpoint_default(irp *ip, uint8_t ifidx)
607 device_t dev = IRP_NDIS_DEV(ip);
608 struct ndis_softc *sc = device_get_softc(dev);
609 usb_error_t status;
611 if (ifidx > 0)
612 device_printf(dev, "warning: ifidx > 0 isn't supported.\n");
614 status = usbd_setup_endpoint_one(ip, ifidx, &sc->ndisusb_dread_ep,
615 &usbd_default_epconfig[USBD_CTRL_READ_PIPE]);
616 if (status != USB_ERR_NORMAL_COMPLETION)
617 return (status);
619 status = usbd_setup_endpoint_one(ip, ifidx, &sc->ndisusb_dwrite_ep,
620 &usbd_default_epconfig[USBD_CTRL_WRITE_PIPE]);
621 return (status);
624 static usb_error_t
625 usbd_setup_endpoint(irp *ip, uint8_t ifidx,
626 struct usb_endpoint_descriptor *ep)
628 device_t dev = IRP_NDIS_DEV(ip);
629 struct ndis_softc *sc = device_get_softc(dev);
630 struct ndisusb_ep *ne;
631 struct usb_config cfg;
632 struct usb_xfer *xfer;
633 usb_error_t status;
635 /* check for non-supported transfer types */
636 if (UE_GET_XFERTYPE(ep->bmAttributes) == UE_CONTROL ||
637 UE_GET_XFERTYPE(ep->bmAttributes) == UE_ISOCHRONOUS) {
638 device_printf(dev, "%s: unsuppotted transfer types %#x\n",
639 __func__, UE_GET_XFERTYPE(ep->bmAttributes));
640 return (USB_ERR_INVAL);
643 ne = &sc->ndisusb_ep[NDISUSB_GET_ENDPT(ep->bEndpointAddress)];
644 InitializeListHead(&ne->ne_active);
645 InitializeListHead(&ne->ne_pending);
646 KeInitializeSpinLock(&ne->ne_lock);
647 ne->ne_dirin = UE_GET_DIR(ep->bEndpointAddress) >> 7;
649 memset(&cfg, 0, sizeof(struct usb_config));
650 cfg.type = UE_GET_XFERTYPE(ep->bmAttributes);
651 cfg.endpoint = UE_GET_ADDR(ep->bEndpointAddress);
652 cfg.direction = UE_GET_DIR(ep->bEndpointAddress);
653 cfg.callback = &usbd_non_isoc_callback;
654 cfg.bufsize = UGETW(ep->wMaxPacketSize);
655 cfg.flags.proxy_buffer = 1;
656 if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN)
657 cfg.flags.short_xfer_ok = 1;
659 status = usbd_transfer_setup(sc->ndisusb_dev, &ifidx, ne->ne_xfer,
660 &cfg, 1, sc, &sc->ndisusb_lock);
661 if (status != USB_ERR_NORMAL_COMPLETION) {
662 device_printf(dev, "couldn't setup xfer: %s\n",
663 usbd_errstr(status));
664 return (status);
666 xfer = ne->ne_xfer[0];
667 usbd_xfer_set_priv(xfer, ne);
668 if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN)
669 usbd_xfer_set_timeout(xfer, NDISUSB_NO_TIMEOUT);
670 else {
671 if (UE_GET_XFERTYPE(ep->bmAttributes) == UE_BULK)
672 usbd_xfer_set_timeout(xfer, NDISUSB_TX_TIMEOUT);
673 else
674 usbd_xfer_set_timeout(xfer, NDISUSB_INTR_TIMEOUT);
677 return (status);
680 static int32_t
681 usbd_func_abort_pipe(irp *ip)
683 device_t dev = IRP_NDIS_DEV(ip);
684 struct ndis_softc *sc = device_get_softc(dev);
685 struct ndisusb_ep *ne;
686 union usbd_urb *urb;
688 urb = usbd_geturb(ip);
689 ne = usbd_get_ndisep(ip, urb->uu_pipe.upr_handle);
690 if (ne == NULL) {
691 device_printf(IRP_NDIS_DEV(ip), "get NULL endpoint info.\n");
692 return (USBD_STATUS_INVALID_PIPE_HANDLE);
695 NDISUSB_LOCK(sc);
696 usbd_transfer_stop(ne->ne_xfer[0]);
697 usbd_transfer_start(ne->ne_xfer[0]);
698 NDISUSB_UNLOCK(sc);
700 return (USBD_STATUS_SUCCESS);
703 static int32_t
704 usbd_func_vendorclass(irp *ip)
706 device_t dev = IRP_NDIS_DEV(ip);
707 int32_t error;
708 struct ndis_softc *sc = device_get_softc(dev);
709 struct ndisusb_ep *ne;
710 struct ndisusb_xfer *nx;
711 struct usbd_urb_vendor_or_class_request *vcreq;
712 union usbd_urb *urb;
714 if (!(sc->ndisusb_status & NDISUSB_STATUS_SETUP_EP)) {
716 * XXX In some cases the interface number isn't 0. However
717 * some driver (eg. RTL8187L NDIS driver) calls this function
718 * before calling URB_FUNCTION_SELECT_CONFIGURATION.
720 error = usbd_setup_endpoint_default(ip, 0);
721 if (error != USB_ERR_NORMAL_COMPLETION)
722 return usbd_usb2urb(error);
723 sc->ndisusb_status |= NDISUSB_STATUS_SETUP_EP;
726 urb = usbd_geturb(ip);
727 vcreq = &urb->uu_vcreq;
728 ne = (vcreq->uvc_trans_flags & USBD_TRANSFER_DIRECTION_IN) ?
729 &sc->ndisusb_dread_ep : &sc->ndisusb_dwrite_ep;
730 IRP_NDISUSB_EP(ip) = ne;
731 ip->irp_cancelfunc = (cancel_func)usbd_irpcancel_wrap;
733 nx = kmalloc(sizeof(struct ndisusb_xfer), M_USBDEV, M_NOWAIT | M_ZERO);
734 if (nx == NULL) {
735 device_printf(IRP_NDIS_DEV(ip), "out of memory\n");
736 return (USBD_STATUS_NO_MEMORY);
738 nx->nx_ep = ne;
739 nx->nx_priv = ip;
740 KeAcquireSpinLockAtDpcLevel(&ne->ne_lock);
741 InsertTailList((&ne->ne_pending), (&nx->nx_next));
742 KeReleaseSpinLockFromDpcLevel(&ne->ne_lock);
744 /* we've done to setup xfer. Let's transfer it. */
745 ip->irp_iostat.isb_status = STATUS_PENDING;
746 ip->irp_iostat.isb_info = 0;
747 USBD_URB_STATUS(urb) = USBD_STATUS_PENDING;
748 IoMarkIrpPending(ip);
750 error = usbd_taskadd(ip, NDISUSB_TASK_VENDOR);
751 if (error != USBD_STATUS_SUCCESS)
752 return (error);
754 return (USBD_STATUS_PENDING);
757 static void
758 usbd_irpcancel(device_object *dobj, irp *ip)
760 device_t dev = IRP_NDIS_DEV(ip);
761 struct ndis_softc *sc = device_get_softc(dev);
762 struct ndisusb_ep *ne = IRP_NDISUSB_EP(ip);
764 if (ne == NULL) {
765 ip->irp_cancel = TRUE;
766 IoReleaseCancelSpinLock(ip->irp_cancelirql);
767 return;
771 * Make sure that the current USB transfer proxy is
772 * cancelled and then restarted.
774 NDISUSB_LOCK(sc);
775 usbd_transfer_stop(ne->ne_xfer[0]);
776 usbd_transfer_start(ne->ne_xfer[0]);
777 NDISUSB_UNLOCK(sc);
779 ip->irp_cancel = TRUE;
780 IoReleaseCancelSpinLock(ip->irp_cancelirql);
783 static void
784 usbd_xfer_complete(struct ndis_softc *sc, struct ndisusb_ep *ne,
785 struct ndisusb_xfer *nx, usb_error_t status)
787 struct ndisusb_xferdone *nd;
788 uint8_t irql;
790 nd = kmalloc(sizeof(struct ndisusb_xferdone), M_USBDEV,
791 M_NOWAIT | M_ZERO);
792 if (nd == NULL) {
793 device_printf(sc->ndis_dev, "out of memory");
794 return;
796 nd->nd_xfer = nx;
797 nd->nd_status = status;
799 KeAcquireSpinLock(&sc->ndisusb_xferdonelock, &irql);
800 InsertTailList((&sc->ndisusb_xferdonelist), (&nd->nd_donelist));
801 KeReleaseSpinLock(&sc->ndisusb_xferdonelock, irql);
803 IoQueueWorkItem(sc->ndisusb_xferdoneitem,
804 (io_workitem_func)usbd_xfertask_wrap, WORKQUEUE_CRITICAL, sc);
807 static struct ndisusb_xfer *
808 usbd_aq_getfirst(struct ndis_softc *sc, struct ndisusb_ep *ne)
810 struct ndisusb_xfer *nx;
812 KeAcquireSpinLockAtDpcLevel(&ne->ne_lock);
813 if (IsListEmpty(&ne->ne_active)) {
814 device_printf(sc->ndis_dev,
815 "%s: the active queue can't be empty.\n", __func__);
816 KeReleaseSpinLockFromDpcLevel(&ne->ne_lock);
817 return (NULL);
819 nx = CONTAINING_RECORD(ne->ne_active.nle_flink, struct ndisusb_xfer,
820 nx_next);
821 RemoveEntryList(&nx->nx_next);
822 KeReleaseSpinLockFromDpcLevel(&ne->ne_lock);
824 return (nx);
827 static void
828 usbd_non_isoc_callback(struct usb_xfer *xfer, usb_error_t error)
830 irp *ip;
831 struct ndis_softc *sc = usbd_xfer_softc(xfer);
832 struct ndisusb_ep *ne = usbd_xfer_get_priv(xfer);
833 struct ndisusb_xfer *nx;
834 struct usbd_urb_bulk_or_intr_transfer *ubi;
835 struct usb_page_cache *pc;
836 uint8_t irql;
837 uint32_t len;
838 union usbd_urb *urb;
839 usb_endpoint_descriptor_t *ep;
840 int actlen, sumlen;
842 usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
844 switch (USB_GET_STATE(xfer)) {
845 case USB_ST_TRANSFERRED:
846 nx = usbd_aq_getfirst(sc, ne);
847 pc = usbd_xfer_get_frame(xfer, 0);
848 if (nx == NULL)
849 return;
851 /* copy in data with regard to the URB */
852 if (ne->ne_dirin != 0)
853 usbd_copy_out(pc, 0, nx->nx_urbbuf, actlen);
854 nx->nx_urbbuf += actlen;
855 nx->nx_urbactlen += actlen;
856 nx->nx_urblen -= actlen;
858 /* check for short transfer */
859 if (actlen < sumlen)
860 nx->nx_urblen = 0;
861 else {
862 /* check remainder */
863 if (nx->nx_urblen > 0) {
864 KeAcquireSpinLock(&ne->ne_lock, &irql);
865 InsertHeadList((&ne->ne_active), (&nx->nx_next));
866 KeReleaseSpinLock(&ne->ne_lock, irql);
868 ip = nx->nx_priv;
869 urb = usbd_geturb(ip);
870 ubi = &urb->uu_bulkintr;
871 ep = ubi->ubi_epdesc;
872 goto extra;
875 usbd_xfer_complete(sc, ne, nx,
876 ((actlen < sumlen) && (nx->nx_shortxfer == 0)) ?
877 USB_ERR_SHORT_XFER : USB_ERR_NORMAL_COMPLETION);
879 /* fall through */
880 case USB_ST_SETUP:
881 next:
882 /* get next transfer */
883 KeAcquireSpinLock(&ne->ne_lock, &irql);
884 if (IsListEmpty(&ne->ne_pending)) {
885 KeReleaseSpinLock(&ne->ne_lock, irql);
886 return;
888 nx = CONTAINING_RECORD(ne->ne_pending.nle_flink,
889 struct ndisusb_xfer, nx_next);
890 RemoveEntryList(&nx->nx_next);
891 /* add a entry to the active queue's tail. */
892 InsertTailList((&ne->ne_active), (&nx->nx_next));
893 KeReleaseSpinLock(&ne->ne_lock, irql);
895 ip = nx->nx_priv;
896 urb = usbd_geturb(ip);
897 ubi = &urb->uu_bulkintr;
898 ep = ubi->ubi_epdesc;
900 nx->nx_urbbuf = ubi->ubi_trans_buf;
901 nx->nx_urbactlen = 0;
902 nx->nx_urblen = ubi->ubi_trans_buflen;
903 nx->nx_shortxfer = (ubi->ubi_trans_flags &
904 USBD_SHORT_TRANSFER_OK) ? 1 : 0;
905 extra:
906 len = MIN(usbd_xfer_max_len(xfer), nx->nx_urblen);
907 pc = usbd_xfer_get_frame(xfer, 0);
908 if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_OUT)
909 usbd_copy_in(pc, 0, nx->nx_urbbuf, len);
910 usbd_xfer_set_frame_len(xfer, 0, len);
911 usbd_xfer_set_frames(xfer, 1);
912 usbd_transfer_submit(xfer);
913 break;
914 default:
915 nx = usbd_aq_getfirst(sc, ne);
916 if (nx == NULL)
917 return;
918 if (error != USB_ERR_CANCELLED) {
919 usbd_xfer_set_stall(xfer);
920 device_printf(sc->ndis_dev, "usb xfer warning (%s)\n",
921 usbd_errstr(error));
923 usbd_xfer_complete(sc, ne, nx, error);
924 if (error != USB_ERR_CANCELLED)
925 goto next;
926 break;
930 static void
931 usbd_ctrl_callback(struct usb_xfer *xfer, usb_error_t error)
933 irp *ip;
934 struct ndis_softc *sc = usbd_xfer_softc(xfer);
935 struct ndisusb_ep *ne = usbd_xfer_get_priv(xfer);
936 struct ndisusb_xfer *nx;
937 uint8_t irql;
938 union usbd_urb *urb;
939 struct usbd_urb_vendor_or_class_request *vcreq;
940 struct usb_page_cache *pc;
941 uint8_t type = 0;
942 struct usb_device_request req;
943 int len;
945 switch (USB_GET_STATE(xfer)) {
946 case USB_ST_TRANSFERRED:
947 nx = usbd_aq_getfirst(sc, ne);
948 if (nx == NULL)
949 return;
951 ip = nx->nx_priv;
952 urb = usbd_geturb(ip);
953 vcreq = &urb->uu_vcreq;
955 if (vcreq->uvc_trans_flags & USBD_TRANSFER_DIRECTION_IN) {
956 pc = usbd_xfer_get_frame(xfer, 1);
957 len = usbd_xfer_frame_len(xfer, 1);
958 usbd_copy_out(pc, 0, vcreq->uvc_trans_buf, len);
959 nx->nx_urbactlen += len;
962 usbd_xfer_complete(sc, ne, nx, USB_ERR_NORMAL_COMPLETION);
963 /* fall through */
964 case USB_ST_SETUP:
965 next:
966 /* get next transfer */
967 KeAcquireSpinLock(&ne->ne_lock, &irql);
968 if (IsListEmpty(&ne->ne_pending)) {
969 KeReleaseSpinLock(&ne->ne_lock, irql);
970 return;
972 nx = CONTAINING_RECORD(ne->ne_pending.nle_flink,
973 struct ndisusb_xfer, nx_next);
974 RemoveEntryList(&nx->nx_next);
975 /* add a entry to the active queue's tail. */
976 InsertTailList((&ne->ne_active), (&nx->nx_next));
977 KeReleaseSpinLock(&ne->ne_lock, irql);
979 ip = nx->nx_priv;
980 urb = usbd_geturb(ip);
981 vcreq = &urb->uu_vcreq;
983 switch (urb->uu_hdr.uuh_func) {
984 case URB_FUNCTION_CLASS_DEVICE:
985 type = UT_CLASS | UT_DEVICE;
986 break;
987 case URB_FUNCTION_CLASS_INTERFACE:
988 type = UT_CLASS | UT_INTERFACE;
989 break;
990 case URB_FUNCTION_CLASS_OTHER:
991 type = UT_CLASS | UT_OTHER;
992 break;
993 case URB_FUNCTION_CLASS_ENDPOINT:
994 type = UT_CLASS | UT_ENDPOINT;
995 break;
996 case URB_FUNCTION_VENDOR_DEVICE:
997 type = UT_VENDOR | UT_DEVICE;
998 break;
999 case URB_FUNCTION_VENDOR_INTERFACE:
1000 type = UT_VENDOR | UT_INTERFACE;
1001 break;
1002 case URB_FUNCTION_VENDOR_OTHER:
1003 type = UT_VENDOR | UT_OTHER;
1004 break;
1005 case URB_FUNCTION_VENDOR_ENDPOINT:
1006 type = UT_VENDOR | UT_ENDPOINT;
1007 break;
1008 default:
1009 /* never reached. */
1010 break;
1013 type |= (vcreq->uvc_trans_flags & USBD_TRANSFER_DIRECTION_IN) ?
1014 UT_READ : UT_WRITE;
1015 type |= vcreq->uvc_reserved1;
1017 req.bmRequestType = type;
1018 req.bRequest = vcreq->uvc_req;
1019 USETW(req.wIndex, vcreq->uvc_idx);
1020 USETW(req.wValue, vcreq->uvc_value);
1021 USETW(req.wLength, vcreq->uvc_trans_buflen);
1023 nx->nx_urbbuf = vcreq->uvc_trans_buf;
1024 nx->nx_urblen = vcreq->uvc_trans_buflen;
1025 nx->nx_urbactlen = 0;
1027 pc = usbd_xfer_get_frame(xfer, 0);
1028 usbd_copy_in(pc, 0, &req, sizeof(req));
1029 usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
1030 usbd_xfer_set_frames(xfer, 1);
1031 if (vcreq->uvc_trans_flags & USBD_TRANSFER_DIRECTION_IN) {
1032 if (vcreq->uvc_trans_buflen >= USBD_CTRL_READ_BUFFER_SP)
1033 device_printf(sc->ndis_dev,
1034 "warning: not enough buffer space (%d).\n",
1035 vcreq->uvc_trans_buflen);
1036 usbd_xfer_set_frame_len(xfer, 1,
1037 MIN(usbd_xfer_max_len(xfer),
1038 vcreq->uvc_trans_buflen));
1039 usbd_xfer_set_frames(xfer, 2);
1040 } else {
1041 if (nx->nx_urblen > USBD_CTRL_WRITE_BUFFER_SP)
1042 device_printf(sc->ndis_dev,
1043 "warning: not enough write buffer space"
1044 " (%d).\n", nx->nx_urblen);
1046 * XXX with my local tests there was no cases to require
1047 * a extra buffer until now but it'd need to update in
1048 * the future if it needs to be.
1050 if (nx->nx_urblen > 0) {
1051 pc = usbd_xfer_get_frame(xfer, 1);
1052 usbd_copy_in(pc, 0, nx->nx_urbbuf,
1053 nx->nx_urblen);
1054 usbd_xfer_set_frame_len(xfer, 1, nx->nx_urblen);
1055 usbd_xfer_set_frames(xfer, 2);
1058 usbd_transfer_submit(xfer);
1059 break;
1060 default:
1061 nx = usbd_aq_getfirst(sc, ne);
1062 if (nx == NULL)
1063 return;
1064 if (error != USB_ERR_CANCELLED) {
1065 usbd_xfer_set_stall(xfer);
1066 device_printf(sc->ndis_dev, "usb xfer warning (%s)\n",
1067 usbd_errstr(error));
1069 usbd_xfer_complete(sc, ne, nx, error);
1070 if (error != USB_ERR_CANCELLED)
1071 goto next;
1072 break;
1076 static struct ndisusb_ep *
1077 usbd_get_ndisep(irp *ip, usb_endpoint_descriptor_t *ep)
1079 device_t dev = IRP_NDIS_DEV(ip);
1080 struct ndis_softc *sc = device_get_softc(dev);
1081 struct ndisusb_ep *ne;
1083 ne = &sc->ndisusb_ep[NDISUSB_GET_ENDPT(ep->bEndpointAddress)];
1085 IRP_NDISUSB_EP(ip) = ne;
1086 ip->irp_cancelfunc = (cancel_func)usbd_irpcancel_wrap;
1088 return (ne);
1091 static void
1092 usbd_xfertask(device_object *dobj, void *arg)
1094 int error;
1095 irp *ip;
1096 list_entry *l;
1097 struct ndis_softc *sc = arg;
1098 struct ndisusb_xferdone *nd;
1099 struct ndisusb_xfer *nq;
1100 struct usbd_urb_bulk_or_intr_transfer *ubi;
1101 struct usbd_urb_vendor_or_class_request *vcreq;
1102 union usbd_urb *urb;
1103 usb_error_t status;
1104 void *priv;
1106 if (IsListEmpty(&sc->ndisusb_xferdonelist))
1107 return;
1109 KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_xferdonelock);
1110 l = sc->ndisusb_xferdonelist.nle_flink;
1111 while (l != &sc->ndisusb_xferdonelist) {
1112 nd = CONTAINING_RECORD(l, struct ndisusb_xferdone, nd_donelist);
1113 nq = nd->nd_xfer;
1114 priv = nq->nx_priv;
1115 status = nd->nd_status;
1116 error = 0;
1117 ip = priv;
1118 urb = usbd_geturb(ip);
1120 ip->irp_cancelfunc = NULL;
1121 IRP_NDISUSB_EP(ip) = NULL;
1123 switch (status) {
1124 case USB_ERR_NORMAL_COMPLETION:
1125 if (urb->uu_hdr.uuh_func ==
1126 URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER) {
1127 ubi = &urb->uu_bulkintr;
1128 ubi->ubi_trans_buflen = nq->nx_urbactlen;
1129 } else {
1130 vcreq = &urb->uu_vcreq;
1131 vcreq->uvc_trans_buflen = nq->nx_urbactlen;
1133 ip->irp_iostat.isb_info = nq->nx_urbactlen;
1134 ip->irp_iostat.isb_status = STATUS_SUCCESS;
1135 USBD_URB_STATUS(urb) = USBD_STATUS_SUCCESS;
1136 break;
1137 case USB_ERR_CANCELLED:
1138 ip->irp_iostat.isb_info = 0;
1139 ip->irp_iostat.isb_status = STATUS_CANCELLED;
1140 USBD_URB_STATUS(urb) = USBD_STATUS_CANCELED;
1141 break;
1142 default:
1143 ip->irp_iostat.isb_info = 0;
1144 USBD_URB_STATUS(urb) = usbd_usb2urb(status);
1145 ip->irp_iostat.isb_status =
1146 usbd_urb2nt(USBD_URB_STATUS(urb));
1147 break;
1150 l = l->nle_flink;
1151 RemoveEntryList(&nd->nd_donelist);
1152 kfree(nq, M_USBDEV);
1153 kfree(nd, M_USBDEV);
1154 if (error)
1155 continue;
1156 KeReleaseSpinLockFromDpcLevel(&sc->ndisusb_xferdonelock);
1157 /* NB: call after cleaning */
1158 IoCompleteRequest(ip, IO_NO_INCREMENT);
1159 KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_xferdonelock);
1161 KeReleaseSpinLockFromDpcLevel(&sc->ndisusb_xferdonelock);
1165 * this function is for mainly deferring a task to the another thread because
1166 * we don't want to be in the scope of HAL lock.
1168 static int32_t
1169 usbd_taskadd(irp *ip, unsigned type)
1171 device_t dev = IRP_NDIS_DEV(ip);
1172 struct ndis_softc *sc = device_get_softc(dev);
1173 struct ndisusb_task *nt;
1175 nt = kmalloc(sizeof(struct ndisusb_task), M_USBDEV, M_NOWAIT | M_ZERO);
1176 if (nt == NULL)
1177 return (USBD_STATUS_NO_MEMORY);
1178 nt->nt_type = type;
1179 nt->nt_ctx = ip;
1181 KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_tasklock);
1182 InsertTailList((&sc->ndisusb_tasklist), (&nt->nt_tasklist));
1183 KeReleaseSpinLockFromDpcLevel(&sc->ndisusb_tasklock);
1185 IoQueueWorkItem(sc->ndisusb_taskitem,
1186 (io_workitem_func)usbd_task_wrap, WORKQUEUE_CRITICAL, sc);
1188 return (USBD_STATUS_SUCCESS);
1191 static void
1192 usbd_task(device_object *dobj, void *arg)
1194 irp *ip;
1195 list_entry *l;
1196 struct ndis_softc *sc = arg;
1197 struct ndisusb_ep *ne;
1198 struct ndisusb_task *nt;
1199 union usbd_urb *urb;
1201 if (IsListEmpty(&sc->ndisusb_tasklist))
1202 return;
1204 KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_tasklock);
1205 l = sc->ndisusb_tasklist.nle_flink;
1206 while (l != &sc->ndisusb_tasklist) {
1207 nt = CONTAINING_RECORD(l, struct ndisusb_task, nt_tasklist);
1209 ip = nt->nt_ctx;
1210 urb = usbd_geturb(ip);
1212 KeReleaseSpinLockFromDpcLevel(&sc->ndisusb_tasklock);
1213 NDISUSB_LOCK(sc);
1214 switch (nt->nt_type) {
1215 case NDISUSB_TASK_TSTART:
1216 ne = usbd_get_ndisep(ip, urb->uu_bulkintr.ubi_epdesc);
1217 if (ne == NULL)
1218 goto exit;
1219 usbd_transfer_start(ne->ne_xfer[0]);
1220 break;
1221 case NDISUSB_TASK_IRPCANCEL:
1222 ne = usbd_get_ndisep(ip,
1223 (nt->nt_type == NDISUSB_TASK_IRPCANCEL) ?
1224 urb->uu_bulkintr.ubi_epdesc :
1225 urb->uu_pipe.upr_handle);
1226 if (ne == NULL)
1227 goto exit;
1229 usbd_transfer_stop(ne->ne_xfer[0]);
1230 usbd_transfer_start(ne->ne_xfer[0]);
1231 break;
1232 case NDISUSB_TASK_VENDOR:
1233 ne = (urb->uu_vcreq.uvc_trans_flags &
1234 USBD_TRANSFER_DIRECTION_IN) ?
1235 &sc->ndisusb_dread_ep : &sc->ndisusb_dwrite_ep;
1236 usbd_transfer_start(ne->ne_xfer[0]);
1237 break;
1238 default:
1239 break;
1241 exit:
1242 NDISUSB_UNLOCK(sc);
1243 KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_tasklock);
1245 l = l->nle_flink;
1246 RemoveEntryList(&nt->nt_tasklist);
1247 kfree(nt, M_USBDEV);
1249 KeReleaseSpinLockFromDpcLevel(&sc->ndisusb_tasklock);
1252 static int32_t
1253 usbd_func_bulkintr(irp *ip)
1255 int32_t error;
1256 struct ndisusb_ep *ne;
1257 struct ndisusb_xfer *nx;
1258 struct usbd_urb_bulk_or_intr_transfer *ubi;
1259 union usbd_urb *urb;
1260 usb_endpoint_descriptor_t *ep;
1262 urb = usbd_geturb(ip);
1263 ubi = &urb->uu_bulkintr;
1264 ep = ubi->ubi_epdesc;
1265 if (ep == NULL)
1266 return (USBD_STATUS_INVALID_PIPE_HANDLE);
1268 ne = usbd_get_ndisep(ip, ep);
1269 if (ne == NULL) {
1270 device_printf(IRP_NDIS_DEV(ip), "get NULL endpoint info.\n");
1271 return (USBD_STATUS_INVALID_PIPE_HANDLE);
1274 nx = kmalloc(sizeof(struct ndisusb_xfer), M_USBDEV, M_NOWAIT | M_ZERO);
1275 if (nx == NULL) {
1276 device_printf(IRP_NDIS_DEV(ip), "out of memory\n");
1277 return (USBD_STATUS_NO_MEMORY);
1279 nx->nx_ep = ne;
1280 nx->nx_priv = ip;
1281 KeAcquireSpinLockAtDpcLevel(&ne->ne_lock);
1282 InsertTailList((&ne->ne_pending), (&nx->nx_next));
1283 KeReleaseSpinLockFromDpcLevel(&ne->ne_lock);
1285 /* we've done to setup xfer. Let's transfer it. */
1286 ip->irp_iostat.isb_status = STATUS_PENDING;
1287 ip->irp_iostat.isb_info = 0;
1288 USBD_URB_STATUS(urb) = USBD_STATUS_PENDING;
1289 IoMarkIrpPending(ip);
1291 error = usbd_taskadd(ip, NDISUSB_TASK_TSTART);
1292 if (error != USBD_STATUS_SUCCESS)
1293 return (error);
1295 return (USBD_STATUS_PENDING);
1298 static union usbd_urb *
1299 USBD_CreateConfigurationRequest(usb_config_descriptor_t *conf, uint16_t *len)
1301 struct usbd_interface_list_entry list[2];
1302 union usbd_urb *urb;
1304 bzero(list, sizeof(struct usbd_interface_list_entry) * 2);
1305 list[0].uil_intfdesc = USBD_ParseConfigurationDescriptorEx(conf, conf,
1306 -1, -1, -1, -1, -1);
1307 urb = USBD_CreateConfigurationRequestEx(conf, list);
1308 if (urb == NULL)
1309 return (NULL);
1311 *len = urb->uu_selconf.usc_hdr.uuh_len;
1312 return (urb);
1315 static union usbd_urb *
1316 USBD_CreateConfigurationRequestEx(usb_config_descriptor_t *conf,
1317 struct usbd_interface_list_entry *list)
1319 int i, j, size;
1320 struct usbd_interface_information *intf;
1321 struct usbd_pipe_information *pipe;
1322 struct usbd_urb_select_configuration *selconf;
1323 usb_interface_descriptor_t *desc;
1325 for (i = 0, size = 0; i < conf->bNumInterface; i++) {
1326 j = list[i].uil_intfdesc->bNumEndpoints;
1327 size = size + sizeof(struct usbd_interface_information) +
1328 sizeof(struct usbd_pipe_information) * (j - 1);
1330 size += sizeof(struct usbd_urb_select_configuration) -
1331 sizeof(struct usbd_interface_information);
1333 selconf = ExAllocatePoolWithTag(NonPagedPool, size, 0);
1334 if (selconf == NULL)
1335 return (NULL);
1336 selconf->usc_hdr.uuh_func = URB_FUNCTION_SELECT_CONFIGURATION;
1337 selconf->usc_hdr.uuh_len = size;
1338 selconf->usc_handle = conf;
1339 selconf->usc_conf = conf;
1341 intf = &selconf->usc_intf;
1342 for (i = 0; i < conf->bNumInterface; i++) {
1343 if (list[i].uil_intfdesc == NULL)
1344 break;
1346 list[i].uil_intf = intf;
1347 desc = list[i].uil_intfdesc;
1349 intf->uii_len = sizeof(struct usbd_interface_information) +
1350 (desc->bNumEndpoints - 1) *
1351 sizeof(struct usbd_pipe_information);
1352 intf->uii_intfnum = desc->bInterfaceNumber;
1353 intf->uii_altset = desc->bAlternateSetting;
1354 intf->uii_intfclass = desc->bInterfaceClass;
1355 intf->uii_intfsubclass = desc->bInterfaceSubClass;
1356 intf->uii_intfproto = desc->bInterfaceProtocol;
1357 intf->uii_handle = desc;
1358 intf->uii_numeps = desc->bNumEndpoints;
1360 pipe = &intf->uii_pipes[0];
1361 for (j = 0; j < intf->uii_numeps; j++)
1362 pipe[j].upi_maxtxsize =
1363 USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE;
1365 intf = (struct usbd_interface_information *)((char *)intf +
1366 intf->uii_len);
1369 return ((union usbd_urb *)selconf);
1372 static void
1373 USBD_GetUSBDIVersion(usbd_version_info *ui)
1376 /* Pretend to be Windows XP. */
1378 ui->uvi_usbdi_vers = USBDI_VERSION;
1379 ui->uvi_supported_vers = USB_VER_2_0;
1382 static usb_interface_descriptor_t *
1383 USBD_ParseConfigurationDescriptor(usb_config_descriptor_t *conf,
1384 uint8_t intfnum, uint8_t altset)
1387 return USBD_ParseConfigurationDescriptorEx(conf, conf, intfnum, altset,
1388 -1, -1, -1);
1391 static usb_interface_descriptor_t *
1392 USBD_ParseConfigurationDescriptorEx(usb_config_descriptor_t *conf,
1393 void *start, int32_t intfnum, int32_t altset, int32_t intfclass,
1394 int32_t intfsubclass, int32_t intfproto)
1396 struct usb_descriptor *next = NULL;
1397 usb_interface_descriptor_t *desc;
1399 while ((next = usb_desc_foreach(conf, next)) != NULL) {
1400 desc = (usb_interface_descriptor_t *)next;
1401 if (desc->bDescriptorType != UDESC_INTERFACE)
1402 continue;
1403 if (!(intfnum == -1 || desc->bInterfaceNumber == intfnum))
1404 continue;
1405 if (!(altset == -1 || desc->bAlternateSetting == altset))
1406 continue;
1407 if (!(intfclass == -1 || desc->bInterfaceClass == intfclass))
1408 continue;
1409 if (!(intfsubclass == -1 ||
1410 desc->bInterfaceSubClass == intfsubclass))
1411 continue;
1412 if (!(intfproto == -1 || desc->bInterfaceProtocol == intfproto))
1413 continue;
1414 return (desc);
1417 return (NULL);
1420 static void
1421 dummy(void)
1423 kprintf("USBD dummy called\n");
1426 image_patch_table usbd_functbl[] = {
1427 IMPORT_SFUNC(USBD_CreateConfigurationRequest, 2),
1428 IMPORT_SFUNC(USBD_CreateConfigurationRequestEx, 2),
1429 IMPORT_SFUNC_MAP(_USBD_CreateConfigurationRequestEx@8,
1430 USBD_CreateConfigurationRequestEx, 2),
1431 IMPORT_SFUNC(USBD_GetUSBDIVersion, 1),
1432 IMPORT_SFUNC(USBD_ParseConfigurationDescriptor, 3),
1433 IMPORT_SFUNC(USBD_ParseConfigurationDescriptorEx, 7),
1434 IMPORT_SFUNC_MAP(_USBD_ParseConfigurationDescriptorEx@28,
1435 USBD_ParseConfigurationDescriptorEx, 7),
1438 * This last entry is a catch-all for any function we haven't
1439 * implemented yet. The PE import list patching routine will
1440 * use it for any function that doesn't have an explicit match
1441 * in this table.
1444 { NULL, (FUNC)dummy, NULL, 0, WINDRV_WRAP_STDCALL },
1446 /* End of list. */
1448 { NULL, NULL, NULL }
1451 MODULE_DEPEND(ndis, usb, 1, 1, 1);