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.
This commit is contained in:
Weongyo Jeong 2009-03-18 02:38:35 +00:00
parent 08e06b60c1
commit 577b9fa3f8
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=189950
3 changed files with 286 additions and 52 deletions

View File

@ -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 *);
@ -557,6 +592,57 @@ usbd_func_selconf(ip)
return USBD_STATUS_SUCCESS;
}
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;
@ -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;
/* 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);
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);
error = usbd_taskadd(ip, NDISUSB_TASK_VENDOR);
if (error != USBD_STATUS_SUCCESS)
return (error);
NDISUSB_LOCK(sc);
status = usb2_do_request(sc->ndisusb_dev, &sc->ndisusb_mtx, &req,
vcreq->uvc_trans_buf);
NDISUSB_UNLOCK(sc);
return usbd_usb2urb(status);
return (USBD_STATUS_PENDING);
}
static void
@ -872,6 +950,146 @@ usbd_non_isoc_callback(struct usb2_xfer *xfer)
}
}
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;
}

View File

@ -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);

View File

@ -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)