svn commit: r189950 - in head/sys: compat/ndis dev/if_ndis

Weongyo Jeong weongyo at FreeBSD.org
Tue Mar 17 19:38:36 PDT 2009


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)


More information about the svn-src-all mailing list