Date: Wed, 18 Mar 2009 02:38:36 +0000 (UTC) From: Weongyo Jeong <weongyo@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r189950 - in head/sys: compat/ndis dev/if_ndis Message-ID: <200903180238.n2I2ca0a012987@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: weongyo Date: Wed Mar 18 02:38:35 2009 New Revision: 189950 URL: http://svn.freebsd.org/changeset/base/189950 Log: Some NDIS USB drivers try to call URB funcs like URB_FUNCTION_VENDOR_xxx or URB_FUNCTION_CLASS_xxx with HAL preemption lock that means it's non-sleepable during USB requests though usb2_do_request() requires a sleep so it needs to send queries to the default pipe without those interfaces to avoid sleep. Modified: head/sys/compat/ndis/subr_usbd.c head/sys/dev/if_ndis/if_ndis_usb.c head/sys/dev/if_ndis/if_ndisvar.h Modified: head/sys/compat/ndis/subr_usbd.c ============================================================================== --- head/sys/compat/ndis/subr_usbd.c Wed Mar 18 02:26:46 2009 (r189949) +++ head/sys/compat/ndis/subr_usbd.c Wed Mar 18 02:38:35 2009 (r189950) @@ -77,6 +77,38 @@ __FBSDID("$FreeBSD$"); static driver_object usbd_driver; static usb2_callback_t usbd_non_isoc_callback; +static usb2_callback_t usbd_ctrl_callback; + +#define USBD_CTRL_READ_PIPE 0 +#define USBD_CTRL_WRITE_PIPE 1 +#define USBD_CTRL_MAX_PIPE 2 +#define USBD_CTRL_READ_BUFFER_SP 256 +#define USBD_CTRL_READ_BUFFER_SIZE \ + (sizeof(struct usb2_device_request) + USBD_CTRL_READ_BUFFER_SP) +#define USBD_CTRL_WRITE_BUFFER_SIZE \ + (sizeof(struct usb2_device_request)) +static struct usb2_config usbd_default_epconfig[USBD_CTRL_MAX_PIPE] = { + [USBD_CTRL_READ_PIPE] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* control pipe */ + .direction = UE_DIR_ANY, + .if_index = 0, + .mh.bufsize = USBD_CTRL_READ_BUFFER_SIZE, + .mh.flags = { .short_xfer_ok = 1, }, + .mh.callback = &usbd_ctrl_callback, + .mh.timeout = 5000, /* 5 seconds */ + }, + [USBD_CTRL_WRITE_PIPE] = { + .type = UE_CONTROL, + .endpoint = 0x00, /* control pipe */ + .direction = UE_DIR_ANY, + .if_index = 0, + .mh.bufsize = USBD_CTRL_WRITE_BUFFER_SIZE, + .mh.flags = { .proxy_buffer = 1, }, + .mh.callback = &usbd_ctrl_callback, + .mh.timeout = 5000, /* 5 seconds */ + } +}; static int32_t usbd_func_bulkintr(irp *); static int32_t usbd_func_vendorclass(irp *); @@ -84,6 +116,9 @@ static int32_t usbd_func_selconf(irp * static int32_t usbd_func_abort_pipe(irp *); static usb2_error_t usbd_setup_endpoint(irp *, uint8_t, struct usb2_endpoint_descriptor *); +static usb2_error_t usbd_setup_endpoint_default(irp *, uint8_t); +static usb2_error_t usbd_setup_endpoint_one(irp *, uint8_t, + struct ndisusb_ep *, struct usb2_config *); static int32_t usbd_func_getdesc(irp *); static union usbd_urb *usbd_geturb(irp *); static struct ndisusb_ep*usbd_get_ndisep(irp *, usb_endpoint_descriptor_t *); @@ -558,6 +593,57 @@ usbd_func_selconf(ip) } static usb2_error_t +usbd_setup_endpoint_one(ip, ifidx, ne, epconf) + irp *ip; + uint8_t ifidx; + struct ndisusb_ep *ne; + struct usb2_config *epconf; +{ + device_t dev = IRP_NDIS_DEV(ip); + struct ndis_softc *sc = device_get_softc(dev); + struct usb2_xfer *xfer; + usb2_error_t status; + + InitializeListHead(&ne->ne_active); + InitializeListHead(&ne->ne_pending); + KeInitializeSpinLock(&ne->ne_lock); + + status = usb2_transfer_setup(sc->ndisusb_dev, &ifidx, ne->ne_xfer, + epconf, 1, sc, &sc->ndisusb_mtx); + if (status != USB_ERR_NORMAL_COMPLETION) { + device_printf(dev, "couldn't setup xfer: %s\n", + usb2_errstr(status)); + return (status); + } + xfer = ne->ne_xfer[0]; + xfer->priv_fifo = ne; + + return (status); +} + +static usb2_error_t +usbd_setup_endpoint_default(ip, ifidx) + irp *ip; + uint8_t ifidx; +{ + device_t dev = IRP_NDIS_DEV(ip); + struct ndis_softc *sc = device_get_softc(dev); + usb2_error_t status; + + if (ifidx > 0) + device_printf(dev, "warning: ifidx > 0 isn't supported.\n"); + + status = usbd_setup_endpoint_one(ip, ifidx, &sc->ndisusb_dread_ep, + &usbd_default_epconfig[USBD_CTRL_READ_PIPE]); + if (status != USB_ERR_NORMAL_COMPLETION) + return (status); + + status = usbd_setup_endpoint_one(ip, ifidx, &sc->ndisusb_dwrite_ep, + &usbd_default_epconfig[USBD_CTRL_WRITE_PIPE]); + return (status); +} + +static usb2_error_t usbd_setup_endpoint(ip, ifidx, ep) irp *ip; uint8_t ifidx; @@ -644,62 +730,54 @@ usbd_func_vendorclass(ip) irp *ip; { device_t dev = IRP_NDIS_DEV(ip); + int32_t error; struct ndis_softc *sc = device_get_softc(dev); + struct ndisusb_ep *ne; + struct ndisusb_xfer *nx; struct usbd_urb_vendor_or_class_request *vcreq; - uint8_t type = 0; union usbd_urb *urb; - struct usb2_device_request req; - usb2_error_t status; + + if (!(sc->ndisusb_status & NDISUSB_STATUS_SETUP_EP)) { + /* + * XXX In some cases the interface number isn't 0. However + * some driver (eg. RTL8187L NDIS driver) calls this function + * before calling URB_FUNCTION_SELECT_CONFIGURATION. + */ + error = usbd_setup_endpoint_default(ip, 0); + if (error != USB_ERR_NORMAL_COMPLETION) + return usbd_usb2urb(error); + sc->ndisusb_status |= NDISUSB_STATUS_SETUP_EP; + } urb = usbd_geturb(ip); vcreq = &urb->uu_vcreq; + ne = (vcreq->uvc_trans_flags & USBD_TRANSFER_DIRECTION_IN) ? + &sc->ndisusb_dread_ep : &sc->ndisusb_dwrite_ep; + IRP_NDISUSB_EP(ip) = ne; + ip->irp_cancelfunc = (cancel_func)usbd_irpcancel_wrap; - switch (urb->uu_hdr.uuh_func) { - case URB_FUNCTION_CLASS_DEVICE: - type = UT_CLASS | UT_DEVICE; - break; - case URB_FUNCTION_CLASS_INTERFACE: - type = UT_CLASS | UT_INTERFACE; - break; - case URB_FUNCTION_CLASS_OTHER: - type = UT_CLASS | UT_OTHER; - break; - case URB_FUNCTION_CLASS_ENDPOINT: - type = UT_CLASS | UT_ENDPOINT; - break; - case URB_FUNCTION_VENDOR_DEVICE: - type = UT_VENDOR | UT_DEVICE; - break; - case URB_FUNCTION_VENDOR_INTERFACE: - type = UT_VENDOR | UT_INTERFACE; - break; - case URB_FUNCTION_VENDOR_OTHER: - type = UT_VENDOR | UT_OTHER; - break; - case URB_FUNCTION_VENDOR_ENDPOINT: - type = UT_VENDOR | UT_ENDPOINT; - break; - default: - /* never reached. */ - break; + nx = malloc(sizeof(struct ndisusb_xfer), M_USBDEV, M_NOWAIT | M_ZERO); + if (nx == NULL) { + device_printf(IRP_NDIS_DEV(ip), "out of memory\n"); + return (USBD_STATUS_NO_MEMORY); } + nx->nx_ep = ne; + nx->nx_priv = ip; + KeAcquireSpinLockAtDpcLevel(&ne->ne_lock); + InsertTailList((&ne->ne_pending), (&nx->nx_next)); + KeReleaseSpinLockFromDpcLevel(&ne->ne_lock); - 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); + /* 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); - NDISUSB_LOCK(sc); - status = usb2_do_request(sc->ndisusb_dev, &sc->ndisusb_mtx, &req, - vcreq->uvc_trans_buf); - NDISUSB_UNLOCK(sc); + error = usbd_taskadd(ip, NDISUSB_TASK_VENDOR); + if (error != USBD_STATUS_SUCCESS) + return (error); - return usbd_usb2urb(status); + return (USBD_STATUS_PENDING); } static void @@ -872,6 +950,146 @@ extra: } } +static void +usbd_ctrl_callback(struct usb2_xfer *xfer) +{ + irp *ip; + struct ndis_softc *sc = xfer->priv_sc; + struct ndisusb_ep *ne = xfer->priv_fifo; + struct ndisusb_xfer *nx; + uint8_t irql; + union usbd_urb *urb; + struct usbd_urb_vendor_or_class_request *vcreq; + uint8_t type = 0; + struct usb2_device_request req; + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + nx = usbd_aq_getfirst(sc, ne); + if (nx == NULL) + return; + + ip = nx->nx_priv; + urb = usbd_geturb(ip); + vcreq = &urb->uu_vcreq; + + if (vcreq->uvc_trans_flags & USBD_TRANSFER_DIRECTION_IN) { + usb2_copy_out(xfer->frbuffers + 1, 0, + vcreq->uvc_trans_buf, xfer->frlengths[1]); + nx->nx_urbactlen += xfer->frlengths[1]; + } + + usbd_xfer_complete(sc, ne, nx, USB_ERR_NORMAL_COMPLETION); + /* fall through */ + case USB_ST_SETUP: +next: + /* get next transfer */ + KeAcquireSpinLock(&ne->ne_lock, &irql); + if (IsListEmpty(&ne->ne_pending)) { + KeReleaseSpinLock(&ne->ne_lock, irql); + return; + } + nx = CONTAINING_RECORD(ne->ne_pending.nle_flink, + struct ndisusb_xfer, nx_next); + RemoveEntryList(&nx->nx_next); + /* add a entry to the active queue's tail. */ + InsertTailList((&ne->ne_active), (&nx->nx_next)); + KeReleaseSpinLock(&ne->ne_lock, irql); + + ip = nx->nx_priv; + urb = usbd_geturb(ip); + vcreq = &urb->uu_vcreq; + + switch (urb->uu_hdr.uuh_func) { + case URB_FUNCTION_CLASS_DEVICE: + type = UT_CLASS | UT_DEVICE; + break; + case URB_FUNCTION_CLASS_INTERFACE: + type = UT_CLASS | UT_INTERFACE; + break; + case URB_FUNCTION_CLASS_OTHER: + type = UT_CLASS | UT_OTHER; + break; + case URB_FUNCTION_CLASS_ENDPOINT: + type = UT_CLASS | UT_ENDPOINT; + break; + case URB_FUNCTION_VENDOR_DEVICE: + type = UT_VENDOR | UT_DEVICE; + break; + case URB_FUNCTION_VENDOR_INTERFACE: + type = UT_VENDOR | UT_INTERFACE; + break; + case URB_FUNCTION_VENDOR_OTHER: + type = UT_VENDOR | UT_OTHER; + break; + case URB_FUNCTION_VENDOR_ENDPOINT: + type = UT_VENDOR | UT_ENDPOINT; + break; + default: + /* never reached. */ + break; + } + + type |= (vcreq->uvc_trans_flags & USBD_TRANSFER_DIRECTION_IN) ? + UT_READ : UT_WRITE; + type |= vcreq->uvc_reserved1; + + req.bmRequestType = type; + req.bRequest = vcreq->uvc_req; + USETW(req.wIndex, vcreq->uvc_idx); + USETW(req.wValue, vcreq->uvc_value); + USETW(req.wLength, vcreq->uvc_trans_buflen); + + nx->nx_urbbuf = vcreq->uvc_trans_buf; + nx->nx_urblen = vcreq->uvc_trans_buflen; + nx->nx_urbactlen = 0; + + usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req)); + xfer->frlengths[0] = sizeof(req); + xfer->nframes = 1; + if (vcreq->uvc_trans_flags & USBD_TRANSFER_DIRECTION_IN) { + if (vcreq->uvc_trans_buflen >= USBD_CTRL_READ_BUFFER_SP) + device_printf(sc->ndis_dev, + "warning: not enough buffer space (%d).\n", + vcreq->uvc_trans_buflen); + xfer->frlengths[1] = MIN(xfer->max_data_length, + vcreq->uvc_trans_buflen); + xfer->nframes = 2; + } else { + if (nx->nx_urblen > 0) + device_printf(sc->ndis_dev, + "warning: not enough write buffer space" + " (%d).\n", nx->nx_urblen); + /* + * XXX with my local tests there was no cases to require + * a extra buffer until now but it'd need to update in + * the future if it needs to be. + */ + if (nx->nx_urblen > 0) { + usb2_copy_in(xfer->frbuffers + 1 , 0, + nx->nx_urbbuf, nx->nx_urblen); + xfer->frlengths[1] = nx->nx_urblen; + xfer->nframes = 2; + } + } + usb2_start_hardware(xfer); + break; + default: + nx = usbd_aq_getfirst(sc, ne); + if (nx == NULL) + return; + if (xfer->error != USB_ERR_CANCELLED) { + xfer->flags.stall_pipe = 1; + device_printf(sc->ndis_dev, "usb xfer warning (%s)\n", + usb2_errstr(xfer->error)); + } + usbd_xfer_complete(sc, ne, nx, xfer->error); + if (xfer->error != USB_ERR_CANCELLED) + goto next; + break; + } +} + static struct ndisusb_ep * usbd_get_ndisep(ip, ep) irp *ip; @@ -902,6 +1120,7 @@ usbd_xfertask(dobj, arg) struct ndisusb_xferdone *nd; struct ndisusb_xfer *nq; struct usbd_urb_bulk_or_intr_transfer *ubi; + struct usbd_urb_vendor_or_class_request *vcreq; union usbd_urb *urb; usb2_error_t status; void *priv; @@ -922,18 +1141,19 @@ usbd_xfertask(dobj, arg) ip = priv; 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)); - ip->irp_cancelfunc = NULL; IRP_NDISUSB_EP(ip) = NULL; switch (status) { case USB_ERR_NORMAL_COMPLETION: - ubi = &urb->uu_bulkintr; - ubi->ubi_trans_buflen = nq->nx_urbactlen; + if (urb->uu_hdr.uuh_func == + URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER) { + ubi = &urb->uu_bulkintr; + ubi->ubi_trans_buflen = nq->nx_urbactlen; + } else { + vcreq = &urb->uu_vcreq; + vcreq->uvc_trans_buflen = nq->nx_urbactlen; + } ip->irp_iostat.isb_info = nq->nx_urbactlen; ip->irp_iostat.isb_status = STATUS_SUCCESS; USBD_URB_STATUS(urb) = USBD_STATUS_SUCCESS; @@ -1037,6 +1257,12 @@ usbd_task(dobj, arg) usb2_transfer_stop(ne->ne_xfer[0]); usb2_transfer_start(ne->ne_xfer[0]); break; + case NDISUSB_TASK_VENDOR: + ne = (urb->uu_vcreq.uvc_trans_flags & + USBD_TRANSFER_DIRECTION_IN) ? + &sc->ndisusb_dread_ep : &sc->ndisusb_dwrite_ep; + usb2_transfer_start(ne->ne_xfer[0]); + break; default: break; } Modified: head/sys/dev/if_ndis/if_ndis_usb.c ============================================================================== --- head/sys/dev/if_ndis/if_ndis_usb.c Wed Mar 18 02:26:46 2009 (r189949) +++ head/sys/dev/if_ndis/if_ndis_usb.c Wed Mar 18 02:38:35 2009 (r189950) @@ -210,6 +210,10 @@ ndisusb_detach(device_t self) ndis_pnpevent_nic(self, NDIS_PNP_EVENT_SURPRISE_REMOVED); + if (sc->ndisusb_status & NDISUSB_STATUS_SETUP_EP) { + usb2_transfer_unsetup(sc->ndisusb_dread_ep.ne_xfer, 1); + usb2_transfer_unsetup(sc->ndisusb_dwrite_ep.ne_xfer, 1); + } for (i = 0; i < NDISUSB_ENDPT_MAX; i++) { ne = &sc->ndisusb_ep[i]; usb2_transfer_unsetup(ne->ne_xfer, 1); Modified: head/sys/dev/if_ndis/if_ndisvar.h ============================================================================== --- head/sys/dev/if_ndis/if_ndisvar.h Wed Mar 18 02:26:46 2009 (r189949) +++ head/sys/dev/if_ndis/if_ndisvar.h Wed Mar 18 02:38:35 2009 (r189950) @@ -146,6 +146,7 @@ struct ndisusb_task { unsigned nt_type; #define NDISUSB_TASK_TSTART 0 #define NDISUSB_TASK_IRPCANCEL 1 +#define NDISUSB_TASK_VENDOR 2 void *nt_ctx; list_entry nt_tasklist; }; @@ -229,6 +230,8 @@ struct ndis_softc { struct usb2_device *ndisusb_dev; struct mtx ndisusb_mtx; + struct ndisusb_ep ndisusb_dread_ep; + struct ndisusb_ep ndisusb_dwrite_ep; #define NDISUSB_GET_ENDPT(addr) \ ((UE_GET_DIR(addr) >> 7) | (UE_GET_ADDR(addr) << 1)) #define NDISUSB_ENDPT_MAX ((UE_ADDR + 1) * 2) @@ -241,6 +244,7 @@ struct ndis_softc { kspin_lock ndisusb_tasklock; int ndisusb_status; #define NDISUSB_STATUS_DETACH 0x1 +#define NDISUSB_STATUS_SETUP_EP 0x2 }; #define NDIS_LOCK(_sc) mtx_lock(&(_sc)->ndis_mtx)
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200903180238.n2I2ca0a012987>