aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Vrabel <david.vrabel@csr.com>2008-10-27 13:12:33 -0400
committerDavid Vrabel <david.vrabel@csr.com>2008-10-28 08:10:25 -0400
commit4656d5de9555e263c5b4c0462b5af7e7bded1b42 (patch)
treec8406b562bd8f7796da869ce152c7d75480e2dda
parent1cde7f68ced8d10a20dd2370e9d1d22ab3c1ea5c (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>
-rw-r--r--drivers/usb/wusbcore/devconnect.c100
-rw-r--r--drivers/usb/wusbcore/rh.c40
-rw-r--r--drivers/usb/wusbcore/security.c3
-rw-r--r--drivers/usb/wusbcore/wusbhc.h4
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 */
376error_unlock: 368error_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 */
451int 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 */
676int 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);
710error_kfree:
711 kfree(ie);
712error_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 */
341static int wusb_dev_update_address(struct wusbhc *wusbhc, 341int 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 *);
387extern int wusbhc_devconnect_start(struct wusbhc *wusbhc, 386extern int wusbhc_devconnect_start(struct wusbhc *wusbhc,
388 const struct wusb_ckhdid *chid); 387 const struct wusb_ckhdid *chid);
389extern void wusbhc_devconnect_stop(struct wusbhc *wusbhc); 388extern void wusbhc_devconnect_stop(struct wusbhc *wusbhc);
390extern int wusbhc_devconnect_auth(struct wusbhc *, u8);
391extern void wusbhc_handle_dn(struct wusbhc *, u8 srcaddr, 389extern 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);
393extern int wusbhc_dev_reset(struct wusbhc *wusbhc, u8 port);
394extern void __wusbhc_dev_disable(struct wusbhc *wusbhc, u8 port); 391extern void __wusbhc_dev_disable(struct wusbhc *wusbhc, u8 port);
395extern int wusb_usb_ncb(struct notifier_block *nb, unsigned long val, 392extern 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 *) ;
436extern int wusb_dev_4way_handshake(struct wusbhc *, struct wusb_dev *, 433extern int wusb_dev_4way_handshake(struct wusbhc *, struct wusb_dev *,
437 struct wusb_ckhdid *ck); 434 struct wusb_ckhdid *ck);
438void wusbhc_gtk_rekey(struct wusbhc *wusbhc); 435void wusbhc_gtk_rekey(struct wusbhc *wusbhc);
436int 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 */