summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/usb_request.c
diff options
context:
space:
mode:
authorthompsa <thompsa@FreeBSD.org>2010-05-12 22:42:35 +0000
committerthompsa <thompsa@FreeBSD.org>2010-05-12 22:42:35 +0000
commitf61fd930ea6d4ab1f68a77846ad3ce133636197c (patch)
tree5cb412eb53df6be590b70f2b1455668701879e35 /sys/dev/usb/usb_request.c
parent2cc73b6656a9910435455e662e86cf485e86b069 (diff)
downloadFreeBSD-src-f61fd930ea6d4ab1f68a77846ad3ce133636197c.zip
FreeBSD-src-f61fd930ea6d4ab1f68a77846ad3ce133636197c.tar.gz
If a USB device is suspended and a USB set config request is issued when the
USB enumeration lock is locked, then the USB stack fails to resume the device because locking the USB enumeration lock is part of the resume procedure. To solve this issue a new lock is introduced which only protects the suspend and resume callbacks, which can be dropped inside the usbd_do_request_flags() function, to allow suspend and resume during so-called enumeration operations. Submitted by: Hans Petter Selasky
Diffstat (limited to 'sys/dev/usb/usb_request.c')
-rw-r--r--sys/dev/usb/usb_request.c24
1 files changed, 18 insertions, 6 deletions
diff --git a/sys/dev/usb/usb_request.c b/sys/dev/usb/usb_request.c
index 6150cb1..19806c0 100644
--- a/sys/dev/usb/usb_request.c
+++ b/sys/dev/usb/usb_request.c
@@ -273,6 +273,7 @@ usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx,
usb_ticks_t max_ticks;
uint16_t length;
uint16_t temp;
+ uint8_t enum_locked;
if (timeout < 50) {
/* timeout is too small */
@@ -284,6 +285,8 @@ usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx,
}
length = UGETW(req->wLength);
+ enum_locked = usbd_enum_is_locked(udev);
+
DPRINTFN(5, "udev=%p bmRequestType=0x%02x bRequest=0x%02x "
"wValue=0x%02x%02x wIndex=0x%02x%02x wLength=0x%02x%02x\n",
udev, req->bmRequestType, req->bRequest,
@@ -308,12 +311,18 @@ usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx,
if (flags & USB_USER_DATA_PTR)
return (USB_ERR_INVAL);
#endif
- if (mtx) {
+ if ((mtx != NULL) && (mtx != &Giant)) {
mtx_unlock(mtx);
- if (mtx != &Giant) {
- mtx_assert(mtx, MA_NOTOWNED);
- }
+ mtx_assert(mtx, MA_NOTOWNED);
}
+
+ /*
+ * We need to allow suspend and resume at this point, else the
+ * control transfer will timeout if the device is suspended!
+ */
+ if (enum_locked)
+ usbd_sr_unlock(udev);
+
/*
* Grab the default sx-lock so that serialisation
* is achieved when multiple threads are involved:
@@ -536,9 +545,12 @@ usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx,
done:
sx_xunlock(&udev->ctrl_sx);
- if (mtx) {
+ if (enum_locked)
+ usbd_sr_lock(udev);
+
+ if ((mtx != NULL) && (mtx != &Giant))
mtx_lock(mtx);
- }
+
return ((usb_error_t)err);
}
OpenPOWER on IntegriCloud