diff options
author | David Vrabel <david.vrabel@csr.com> | 2008-10-27 13:12:33 -0400 |
---|---|---|
committer | David Vrabel <david.vrabel@csr.com> | 2008-10-28 08:10:25 -0400 |
commit | 4656d5de9555e263c5b4c0462b5af7e7bded1b42 (patch) | |
tree | c8406b562bd8f7796da869ce152c7d75480e2dda /drivers/usb | |
parent | 1cde7f68ced8d10a20dd2370e9d1d22ab3c1ea5c (diff) |
wusb: reset WUSB devices with SetAddress(0)
Using a Reset Device IE to reset a WUSB device is too heavyweight as it
causes the devcie to disconnect (which the USB stack does not expect and
cannot handle). Instead, do a SetAddress(0); SetAddress(AuthAddr) for
authenticated devices.
Unauthenticated devices will not be reset and the stack will have to rely
on the device timing out after TrustTimeout and disconnecting.
Signed-off-by: David Vrabel <david.vrabel@csr.com>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/wusbcore/devconnect.c | 100 | ||||
-rw-r--r-- | drivers/usb/wusbcore/rh.c | 40 | ||||
-rw-r--r-- | drivers/usb/wusbcore/security.c | 3 | ||||
-rw-r--r-- | drivers/usb/wusbcore/wusbhc.h | 4 |
4 files changed, 25 insertions, 122 deletions
diff --git a/drivers/usb/wusbcore/devconnect.c b/drivers/usb/wusbcore/devconnect.c index f45d777bef34..c01c7a80744c 100644 --- a/drivers/usb/wusbcore/devconnect.c +++ b/drivers/usb/wusbcore/devconnect.c | |||
@@ -57,9 +57,6 @@ | |||
57 | * Called by notif.c:wusb_handle_dn_connect() | 57 | * Called by notif.c:wusb_handle_dn_connect() |
58 | * when a DN_Connect is received. | 58 | * when a DN_Connect is received. |
59 | * | 59 | * |
60 | * wusbhc_devconnect_auth() Called by rh.c:wusbhc_rh_port_reset() when | ||
61 | * doing the device connect sequence. | ||
62 | * | ||
63 | * wusb_devconnect_acked() Ack done, release resources. | 60 | * wusb_devconnect_acked() Ack done, release resources. |
64 | * | 61 | * |
65 | * wusb_handle_dn_alive() Called by notif.c:wusb_handle_dn() | 62 | * wusb_handle_dn_alive() Called by notif.c:wusb_handle_dn() |
@@ -69,9 +66,6 @@ | |||
69 | * process a disconenct request from a | 66 | * process a disconenct request from a |
70 | * device. | 67 | * device. |
71 | * | 68 | * |
72 | * wusb_dev_reset() Called by rh.c:wusbhc_rh_port_reset() when | ||
73 | * resetting a device. | ||
74 | * | ||
75 | * __wusb_dev_disable() Called by rh.c:wusbhc_rh_clear_port_feat() when | 69 | * __wusb_dev_disable() Called by rh.c:wusbhc_rh_clear_port_feat() when |
76 | * disabling a port. | 70 | * disabling a port. |
77 | * | 71 | * |
@@ -366,12 +360,10 @@ void wusbhc_devconnect_ack(struct wusbhc *wusbhc, struct wusb_dn_connect *dnc, | |||
366 | port->wusb_dev = wusb_dev; | 360 | port->wusb_dev = wusb_dev; |
367 | port->status |= USB_PORT_STAT_CONNECTION; | 361 | port->status |= USB_PORT_STAT_CONNECTION; |
368 | port->change |= USB_PORT_STAT_C_CONNECTION; | 362 | port->change |= USB_PORT_STAT_C_CONNECTION; |
369 | port->reset_count = 0; | ||
370 | /* Now the port status changed to connected; khubd will | 363 | /* Now the port status changed to connected; khubd will |
371 | * pick the change up and try to reset the port to bring it to | 364 | * pick the change up and try to reset the port to bring it to |
372 | * the enabled state--so this process returns up to the stack | 365 | * the enabled state--so this process returns up to the stack |
373 | * and it calls back into wusbhc_rh_port_reset() who will call | 366 | * and it calls back into wusbhc_rh_port_reset(). |
374 | * devconnect_auth(). | ||
375 | */ | 367 | */ |
376 | error_unlock: | 368 | error_unlock: |
377 | mutex_unlock(&wusbhc->mutex); | 369 | mutex_unlock(&wusbhc->mutex); |
@@ -413,9 +405,6 @@ static void __wusbhc_dev_disconnect(struct wusbhc *wusbhc, | |||
413 | wusb_dev_put(wusb_dev); | 405 | wusb_dev_put(wusb_dev); |
414 | } | 406 | } |
415 | port->wusb_dev = NULL; | 407 | port->wusb_dev = NULL; |
416 | /* don't reset the reset_count to zero or wusbhc_rh_port_reset will get | ||
417 | * confused! We only reset to zero when we connect a new device. | ||
418 | */ | ||
419 | 408 | ||
420 | /* After a device disconnects, change the GTK (see [WUSB] | 409 | /* After a device disconnects, change the GTK (see [WUSB] |
421 | * section 6.2.11.2). */ | 410 | * section 6.2.11.2). */ |
@@ -429,39 +418,6 @@ static void __wusbhc_dev_disconnect(struct wusbhc *wusbhc, | |||
429 | } | 418 | } |
430 | 419 | ||
431 | /* | 420 | /* |
432 | * Authenticate a device into the WUSB Cluster | ||
433 | * | ||
434 | * Called from the Root Hub code (rh.c:wusbhc_rh_port_reset()) when | ||
435 | * asking for a reset on a port that is not enabled (ie: first connect | ||
436 | * on the port). | ||
437 | * | ||
438 | * Performs the 4way handshake to allow the device to comunicate w/ the | ||
439 | * WUSB Cluster securely; once done, issue a request to the device for | ||
440 | * it to change to address 0. | ||
441 | * | ||
442 | * This mimics the reset step of Wired USB that once resetting a | ||
443 | * device, leaves the port in enabled state and the dev with the | ||
444 | * default address (0). | ||
445 | * | ||
446 | * WUSB1.0[7.1.2] | ||
447 | * | ||
448 | * @port_idx: port where the change happened--This is the index into | ||
449 | * the wusbhc port array, not the USB port number. | ||
450 | */ | ||
451 | int wusbhc_devconnect_auth(struct wusbhc *wusbhc, u8 port_idx) | ||
452 | { | ||
453 | struct device *dev = wusbhc->dev; | ||
454 | struct wusb_port *port = wusb_port_by_idx(wusbhc, port_idx); | ||
455 | |||
456 | d_fnstart(3, dev, "(%p, %u)\n", wusbhc, port_idx); | ||
457 | port->status &= ~USB_PORT_STAT_RESET; | ||
458 | port->status |= USB_PORT_STAT_ENABLE; | ||
459 | port->change |= USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_ENABLE; | ||
460 | d_fnend(3, dev, "(%p, %u) = 0\n", wusbhc, port_idx); | ||
461 | return 0; | ||
462 | } | ||
463 | |||
464 | /* | ||
465 | * Refresh the list of keep alives to emit in the MMC | 421 | * Refresh the list of keep alives to emit in the MMC |
466 | * | 422 | * |
467 | * Some devices don't respond to keep alives unless they've been | 423 | * Some devices don't respond to keep alives unless they've been |
@@ -662,60 +618,6 @@ static void wusbhc_handle_dn_disconnect(struct wusbhc *wusbhc, struct wusb_dev * | |||
662 | } | 618 | } |
663 | 619 | ||
664 | /* | 620 | /* |
665 | * Reset a WUSB device on a HWA | ||
666 | * | ||
667 | * @wusbhc | ||
668 | * @port_idx Index of the port where the device is | ||
669 | * | ||
670 | * In Wireless USB, a reset is more or less equivalent to a full | ||
671 | * disconnect; so we just do a full disconnect and send the device a | ||
672 | * Device Reset IE (WUSB1.0[7.5.11]) giving it a few millisecs (6 MMCs). | ||
673 | * | ||
674 | * @wusbhc should be refcounted and unlocked | ||
675 | */ | ||
676 | int wusbhc_dev_reset(struct wusbhc *wusbhc, u8 port_idx) | ||
677 | { | ||
678 | int result; | ||
679 | struct device *dev = wusbhc->dev; | ||
680 | struct wusb_dev *wusb_dev; | ||
681 | struct wuie_reset *ie; | ||
682 | |||
683 | d_fnstart(3, dev, "(%p, %u)\n", wusbhc, port_idx); | ||
684 | mutex_lock(&wusbhc->mutex); | ||
685 | result = 0; | ||
686 | wusb_dev = wusb_port_by_idx(wusbhc, port_idx)->wusb_dev; | ||
687 | if (wusb_dev == NULL) { | ||
688 | /* reset no device? ignore */ | ||
689 | dev_dbg(dev, "RESET: no device at port %u, ignoring\n", | ||
690 | port_idx); | ||
691 | goto error_unlock; | ||
692 | } | ||
693 | result = -ENOMEM; | ||
694 | ie = kzalloc(sizeof(*ie), GFP_KERNEL); | ||
695 | if (ie == NULL) | ||
696 | goto error_unlock; | ||
697 | ie->hdr.bLength = sizeof(ie->hdr) + sizeof(ie->CDID); | ||
698 | ie->hdr.bIEIdentifier = WUIE_ID_RESET_DEVICE; | ||
699 | ie->CDID = wusb_dev->cdid; | ||
700 | result = wusbhc_mmcie_set(wusbhc, 0xff, 6, &ie->hdr); | ||
701 | if (result < 0) { | ||
702 | dev_err(dev, "RESET: cant's set MMC: %d\n", result); | ||
703 | goto error_kfree; | ||
704 | } | ||
705 | __wusbhc_dev_disconnect(wusbhc, wusb_port_by_idx(wusbhc, port_idx)); | ||
706 | |||
707 | /* 120ms, hopefully 6 MMCs (FIXME) */ | ||
708 | msleep(120); | ||
709 | wusbhc_mmcie_rm(wusbhc, &ie->hdr); | ||
710 | error_kfree: | ||
711 | kfree(ie); | ||
712 | error_unlock: | ||
713 | mutex_unlock(&wusbhc->mutex); | ||
714 | d_fnend(3, dev, "(%p, %u) = %d\n", wusbhc, port_idx, result); | ||
715 | return result; | ||
716 | } | ||
717 | |||
718 | /* | ||
719 | * Handle a Device Notification coming a host | 621 | * Handle a Device Notification coming a host |
720 | * | 622 | * |
721 | * The Device Notification comes from a host (HWA, DWA or WHCI) | 623 | * The Device Notification comes from a host (HWA, DWA or WHCI) |
diff --git a/drivers/usb/wusbcore/rh.c b/drivers/usb/wusbcore/rh.c index 267a64325106..1c733192ec2a 100644 --- a/drivers/usb/wusbcore/rh.c +++ b/drivers/usb/wusbcore/rh.c | |||
@@ -77,13 +77,17 @@ | |||
77 | /* | 77 | /* |
78 | * Reset a fake port | 78 | * Reset a fake port |
79 | * | 79 | * |
80 | * This can be called to reset a port from any other state or to reset | 80 | * Using a Reset Device IE is too heavyweight as it causes the device |
81 | * it when connecting. In Wireless USB they are different; when doing | 81 | * to enter the UnConnected state and leave the cluster, this can mean |
82 | * a new connect that involves going over the authentication. When | 82 | * that when the device reconnects it is connected to a different fake |
83 | * just reseting, its a different story. | 83 | * port. |
84 | * | 84 | * |
85 | * The Linux USB stack resets a port twice before it considers it | 85 | * Instead, reset authenticated devices with a SetAddress(0), followed |
86 | * enabled, so we have to detect and ignore that. | 86 | * by a SetAddresss(AuthAddr). |
87 | * | ||
88 | * For unauthenticated devices just pretend to reset but do nothing. | ||
89 | * If the device initialization continues to fail it will eventually | ||
90 | * time out after TrustTimeout and enter the UnConnected state. | ||
87 | * | 91 | * |
88 | * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. | 92 | * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. |
89 | * | 93 | * |
@@ -97,20 +101,20 @@ static int wusbhc_rh_port_reset(struct wusbhc *wusbhc, u8 port_idx) | |||
97 | { | 101 | { |
98 | int result = 0; | 102 | int result = 0; |
99 | struct wusb_port *port = wusb_port_by_idx(wusbhc, port_idx); | 103 | struct wusb_port *port = wusb_port_by_idx(wusbhc, port_idx); |
104 | struct wusb_dev *wusb_dev = port->wusb_dev; | ||
105 | |||
106 | port->status |= USB_PORT_STAT_RESET; | ||
107 | port->change |= USB_PORT_STAT_C_RESET; | ||
100 | 108 | ||
101 | d_fnstart(3, wusbhc->dev, "(wusbhc %p port_idx %u)\n", | 109 | if (wusb_dev->addr & WUSB_DEV_ADDR_UNAUTH) |
102 | wusbhc, port_idx); | 110 | result = 0; |
103 | if (port->reset_count == 0) { | ||
104 | wusbhc_devconnect_auth(wusbhc, port_idx); | ||
105 | port->reset_count++; | ||
106 | } else if (port->reset_count == 1) | ||
107 | /* see header */ | ||
108 | d_printf(2, wusbhc->dev, "Ignoring second reset on port_idx " | ||
109 | "%u\n", port_idx); | ||
110 | else | 111 | else |
111 | result = wusbhc_dev_reset(wusbhc, port_idx); | 112 | result = wusb_dev_update_address(wusbhc, wusb_dev); |
112 | d_fnend(3, wusbhc->dev, "(wusbhc %p port_idx %u) = %d\n", | 113 | |
113 | wusbhc, port_idx, result); | 114 | port->status &= ~USB_PORT_STAT_RESET; |
115 | port->status |= USB_PORT_STAT_ENABLE; | ||
116 | port->change |= USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_ENABLE; | ||
117 | |||
114 | return result; | 118 | return result; |
115 | } | 119 | } |
116 | 120 | ||
diff --git a/drivers/usb/wusbcore/security.c b/drivers/usb/wusbcore/security.c index a101cad6a8d4..ac00640bba64 100644 --- a/drivers/usb/wusbcore/security.c +++ b/drivers/usb/wusbcore/security.c | |||
@@ -338,8 +338,7 @@ static void hs_printk(unsigned level, struct device *dev, | |||
338 | * Before the device's address (as known by it) was usb_dev->devnum | | 338 | * Before the device's address (as known by it) was usb_dev->devnum | |
339 | * 0x80 (unauthenticated address). With this we update it to usb_dev->devnum. | 339 | * 0x80 (unauthenticated address). With this we update it to usb_dev->devnum. |
340 | */ | 340 | */ |
341 | static int wusb_dev_update_address(struct wusbhc *wusbhc, | 341 | int wusb_dev_update_address(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev) |
342 | struct wusb_dev *wusb_dev) | ||
343 | { | 342 | { |
344 | int result = -ENOMEM; | 343 | int result = -ENOMEM; |
345 | struct usb_device *usb_dev = wusb_dev->usb_dev; | 344 | struct usb_device *usb_dev = wusb_dev->usb_dev; |
diff --git a/drivers/usb/wusbcore/wusbhc.h b/drivers/usb/wusbcore/wusbhc.h index b9bdf5a5f11b..8fef934ad2f3 100644 --- a/drivers/usb/wusbcore/wusbhc.h +++ b/drivers/usb/wusbcore/wusbhc.h | |||
@@ -154,7 +154,6 @@ struct wusb_port { | |||
154 | u16 status; | 154 | u16 status; |
155 | u16 change; | 155 | u16 change; |
156 | struct wusb_dev *wusb_dev; /* connected device's info */ | 156 | struct wusb_dev *wusb_dev; /* connected device's info */ |
157 | unsigned reset_count; | ||
158 | u32 ptk_tkid; | 157 | u32 ptk_tkid; |
159 | }; | 158 | }; |
160 | 159 | ||
@@ -387,10 +386,8 @@ extern void wusbhc_devconnect_destroy(struct wusbhc *); | |||
387 | extern int wusbhc_devconnect_start(struct wusbhc *wusbhc, | 386 | extern int wusbhc_devconnect_start(struct wusbhc *wusbhc, |
388 | const struct wusb_ckhdid *chid); | 387 | const struct wusb_ckhdid *chid); |
389 | extern void wusbhc_devconnect_stop(struct wusbhc *wusbhc); | 388 | extern void wusbhc_devconnect_stop(struct wusbhc *wusbhc); |
390 | extern int wusbhc_devconnect_auth(struct wusbhc *, u8); | ||
391 | extern void wusbhc_handle_dn(struct wusbhc *, u8 srcaddr, | 389 | extern void wusbhc_handle_dn(struct wusbhc *, u8 srcaddr, |
392 | struct wusb_dn_hdr *dn_hdr, size_t size); | 390 | struct wusb_dn_hdr *dn_hdr, size_t size); |
393 | extern int wusbhc_dev_reset(struct wusbhc *wusbhc, u8 port); | ||
394 | extern void __wusbhc_dev_disable(struct wusbhc *wusbhc, u8 port); | 391 | extern void __wusbhc_dev_disable(struct wusbhc *wusbhc, u8 port); |
395 | extern int wusb_usb_ncb(struct notifier_block *nb, unsigned long val, | 392 | extern int wusb_usb_ncb(struct notifier_block *nb, unsigned long val, |
396 | void *priv); | 393 | void *priv); |
@@ -436,6 +433,7 @@ extern void wusb_dev_sec_rm(struct wusb_dev *) ; | |||
436 | extern int wusb_dev_4way_handshake(struct wusbhc *, struct wusb_dev *, | 433 | extern int wusb_dev_4way_handshake(struct wusbhc *, struct wusb_dev *, |
437 | struct wusb_ckhdid *ck); | 434 | struct wusb_ckhdid *ck); |
438 | void wusbhc_gtk_rekey(struct wusbhc *wusbhc); | 435 | void wusbhc_gtk_rekey(struct wusbhc *wusbhc); |
436 | int wusb_dev_update_address(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev); | ||
439 | 437 | ||
440 | 438 | ||
441 | /* WUSB Cluster ID handling */ | 439 | /* WUSB Cluster ID handling */ |