summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authorhselasky <hselasky@FreeBSD.org>2011-07-16 12:50:30 +0000
committerhselasky <hselasky@FreeBSD.org>2011-07-16 12:50:30 +0000
commit6c43b96bf23c58fd22e7c13483353b1d9befa244 (patch)
tree80fb0c8d68967170a3d0514e5e9c33ca64ef3226 /sys/dev
parent264979c9d1dc6f5cacff8dc9ec89d787dfa80612 (diff)
downloadFreeBSD-src-6c43b96bf23c58fd22e7c13483353b1d9befa244.zip
FreeBSD-src-6c43b96bf23c58fd22e7c13483353b1d9befa244.tar.gz
Fix for VirtualBox 4.x and other virtual machines that fail
to generate a port reset change event. MFC after: 1 weeks
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/usb/usb_request.c35
1 files changed, 28 insertions, 7 deletions
diff --git a/sys/dev/usb/usb_request.c b/sys/dev/usb/usb_request.c
index 4358ef4..bb5e0da 100644
--- a/sys/dev/usb/usb_request.c
+++ b/sys/dev/usb/usb_request.c
@@ -779,10 +779,17 @@ usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port)
uint16_t pr_recovery_delay;
#endif
- err = usbd_req_set_port_feature(udev, mtx, port, UHF_PORT_RESET);
- if (err) {
+ /* clear any leftover port reset changes first */
+ usbd_req_clear_port_feature(
+ udev, mtx, port, UHF_C_PORT_RESET);
+
+ /* assert port reset on the given port */
+ err = usbd_req_set_port_feature(
+ udev, mtx, port, UHF_PORT_RESET);
+
+ /* check for errors */
+ if (err)
goto done;
- }
#ifdef USB_DEBUG
/* range check input parameters */
pr_poll_delay = usb_pr_poll_delay;
@@ -798,6 +805,9 @@ usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port)
#endif
n = 0;
while (1) {
+ uint16_t status;
+ uint16_t change;
+
#ifdef USB_DEBUG
/* wait for the device to recover from reset */
usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_poll_delay));
@@ -811,14 +821,25 @@ usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port)
if (err) {
goto done;
}
+ status = UGETW(ps.wPortStatus);
+ change = UGETW(ps.wPortChange);
+
/* if the device disappeared, just give up */
- if (!(UGETW(ps.wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) {
+ if (!(status & UPS_CURRENT_CONNECT_STATUS))
goto done;
- }
+
/* check if reset is complete */
- if (UGETW(ps.wPortChange) & UPS_C_PORT_RESET) {
+ if (change & UPS_C_PORT_RESET)
break;
- }
+
+ /*
+ * Some Virtual Machines like VirtualBox 4.x fail to
+ * generate a port reset change event. Check if reset
+ * is no longer asserted.
+ */
+ if (!(status & UPS_RESET))
+ break;
+
/* check for timeout */
if (n > 1000) {
n = 0;
OpenPOWER on IntegriCloud