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>
This commit is contained in:
Родитель
1cde7f68ce
Коммит
4656d5de95
|
@ -57,9 +57,6 @@
|
|||
* Called by notif.c:wusb_handle_dn_connect()
|
||||
* when a DN_Connect is received.
|
||||
*
|
||||
* wusbhc_devconnect_auth() Called by rh.c:wusbhc_rh_port_reset() when
|
||||
* doing the device connect sequence.
|
||||
*
|
||||
* wusb_devconnect_acked() Ack done, release resources.
|
||||
*
|
||||
* wusb_handle_dn_alive() Called by notif.c:wusb_handle_dn()
|
||||
|
@ -69,9 +66,6 @@
|
|||
* process a disconenct request from a
|
||||
* device.
|
||||
*
|
||||
* wusb_dev_reset() Called by rh.c:wusbhc_rh_port_reset() when
|
||||
* resetting a device.
|
||||
*
|
||||
* __wusb_dev_disable() Called by rh.c:wusbhc_rh_clear_port_feat() when
|
||||
* disabling a port.
|
||||
*
|
||||
|
@ -366,12 +360,10 @@ void wusbhc_devconnect_ack(struct wusbhc *wusbhc, struct wusb_dn_connect *dnc,
|
|||
port->wusb_dev = wusb_dev;
|
||||
port->status |= USB_PORT_STAT_CONNECTION;
|
||||
port->change |= USB_PORT_STAT_C_CONNECTION;
|
||||
port->reset_count = 0;
|
||||
/* Now the port status changed to connected; khubd will
|
||||
* pick the change up and try to reset the port to bring it to
|
||||
* the enabled state--so this process returns up to the stack
|
||||
* and it calls back into wusbhc_rh_port_reset() who will call
|
||||
* devconnect_auth().
|
||||
* and it calls back into wusbhc_rh_port_reset().
|
||||
*/
|
||||
error_unlock:
|
||||
mutex_unlock(&wusbhc->mutex);
|
||||
|
@ -413,9 +405,6 @@ static void __wusbhc_dev_disconnect(struct wusbhc *wusbhc,
|
|||
wusb_dev_put(wusb_dev);
|
||||
}
|
||||
port->wusb_dev = NULL;
|
||||
/* don't reset the reset_count to zero or wusbhc_rh_port_reset will get
|
||||
* confused! We only reset to zero when we connect a new device.
|
||||
*/
|
||||
|
||||
/* After a device disconnects, change the GTK (see [WUSB]
|
||||
* section 6.2.11.2). */
|
||||
|
@ -428,39 +417,6 @@ static void __wusbhc_dev_disconnect(struct wusbhc *wusbhc,
|
|||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Authenticate a device into the WUSB Cluster
|
||||
*
|
||||
* Called from the Root Hub code (rh.c:wusbhc_rh_port_reset()) when
|
||||
* asking for a reset on a port that is not enabled (ie: first connect
|
||||
* on the port).
|
||||
*
|
||||
* Performs the 4way handshake to allow the device to comunicate w/ the
|
||||
* WUSB Cluster securely; once done, issue a request to the device for
|
||||
* it to change to address 0.
|
||||
*
|
||||
* This mimics the reset step of Wired USB that once resetting a
|
||||
* device, leaves the port in enabled state and the dev with the
|
||||
* default address (0).
|
||||
*
|
||||
* WUSB1.0[7.1.2]
|
||||
*
|
||||
* @port_idx: port where the change happened--This is the index into
|
||||
* the wusbhc port array, not the USB port number.
|
||||
*/
|
||||
int wusbhc_devconnect_auth(struct wusbhc *wusbhc, u8 port_idx)
|
||||
{
|
||||
struct device *dev = wusbhc->dev;
|
||||
struct wusb_port *port = wusb_port_by_idx(wusbhc, port_idx);
|
||||
|
||||
d_fnstart(3, dev, "(%p, %u)\n", wusbhc, port_idx);
|
||||
port->status &= ~USB_PORT_STAT_RESET;
|
||||
port->status |= USB_PORT_STAT_ENABLE;
|
||||
port->change |= USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_ENABLE;
|
||||
d_fnend(3, dev, "(%p, %u) = 0\n", wusbhc, port_idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Refresh the list of keep alives to emit in the MMC
|
||||
*
|
||||
|
@ -661,60 +617,6 @@ static void wusbhc_handle_dn_disconnect(struct wusbhc *wusbhc, struct wusb_dev *
|
|||
mutex_unlock(&wusbhc->mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset a WUSB device on a HWA
|
||||
*
|
||||
* @wusbhc
|
||||
* @port_idx Index of the port where the device is
|
||||
*
|
||||
* In Wireless USB, a reset is more or less equivalent to a full
|
||||
* disconnect; so we just do a full disconnect and send the device a
|
||||
* Device Reset IE (WUSB1.0[7.5.11]) giving it a few millisecs (6 MMCs).
|
||||
*
|
||||
* @wusbhc should be refcounted and unlocked
|
||||
*/
|
||||
int wusbhc_dev_reset(struct wusbhc *wusbhc, u8 port_idx)
|
||||
{
|
||||
int result;
|
||||
struct device *dev = wusbhc->dev;
|
||||
struct wusb_dev *wusb_dev;
|
||||
struct wuie_reset *ie;
|
||||
|
||||
d_fnstart(3, dev, "(%p, %u)\n", wusbhc, port_idx);
|
||||
mutex_lock(&wusbhc->mutex);
|
||||
result = 0;
|
||||
wusb_dev = wusb_port_by_idx(wusbhc, port_idx)->wusb_dev;
|
||||
if (wusb_dev == NULL) {
|
||||
/* reset no device? ignore */
|
||||
dev_dbg(dev, "RESET: no device at port %u, ignoring\n",
|
||||
port_idx);
|
||||
goto error_unlock;
|
||||
}
|
||||
result = -ENOMEM;
|
||||
ie = kzalloc(sizeof(*ie), GFP_KERNEL);
|
||||
if (ie == NULL)
|
||||
goto error_unlock;
|
||||
ie->hdr.bLength = sizeof(ie->hdr) + sizeof(ie->CDID);
|
||||
ie->hdr.bIEIdentifier = WUIE_ID_RESET_DEVICE;
|
||||
ie->CDID = wusb_dev->cdid;
|
||||
result = wusbhc_mmcie_set(wusbhc, 0xff, 6, &ie->hdr);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "RESET: cant's set MMC: %d\n", result);
|
||||
goto error_kfree;
|
||||
}
|
||||
__wusbhc_dev_disconnect(wusbhc, wusb_port_by_idx(wusbhc, port_idx));
|
||||
|
||||
/* 120ms, hopefully 6 MMCs (FIXME) */
|
||||
msleep(120);
|
||||
wusbhc_mmcie_rm(wusbhc, &ie->hdr);
|
||||
error_kfree:
|
||||
kfree(ie);
|
||||
error_unlock:
|
||||
mutex_unlock(&wusbhc->mutex);
|
||||
d_fnend(3, dev, "(%p, %u) = %d\n", wusbhc, port_idx, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle a Device Notification coming a host
|
||||
*
|
||||
|
|
|
@ -77,13 +77,17 @@
|
|||
/*
|
||||
* Reset a fake port
|
||||
*
|
||||
* This can be called to reset a port from any other state or to reset
|
||||
* it when connecting. In Wireless USB they are different; when doing
|
||||
* a new connect that involves going over the authentication. When
|
||||
* just reseting, its a different story.
|
||||
* Using a Reset Device IE is too heavyweight as it causes the device
|
||||
* to enter the UnConnected state and leave the cluster, this can mean
|
||||
* that when the device reconnects it is connected to a different fake
|
||||
* port.
|
||||
*
|
||||
* The Linux USB stack resets a port twice before it considers it
|
||||
* enabled, so we have to detect and ignore that.
|
||||
* Instead, reset authenticated devices with a SetAddress(0), followed
|
||||
* by a SetAddresss(AuthAddr).
|
||||
*
|
||||
* For unauthenticated devices just pretend to reset but do nothing.
|
||||
* If the device initialization continues to fail it will eventually
|
||||
* time out after TrustTimeout and enter the UnConnected state.
|
||||
*
|
||||
* @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
|
||||
*
|
||||
|
@ -97,20 +101,20 @@ static int wusbhc_rh_port_reset(struct wusbhc *wusbhc, u8 port_idx)
|
|||
{
|
||||
int result = 0;
|
||||
struct wusb_port *port = wusb_port_by_idx(wusbhc, port_idx);
|
||||
struct wusb_dev *wusb_dev = port->wusb_dev;
|
||||
|
||||
d_fnstart(3, wusbhc->dev, "(wusbhc %p port_idx %u)\n",
|
||||
wusbhc, port_idx);
|
||||
if (port->reset_count == 0) {
|
||||
wusbhc_devconnect_auth(wusbhc, port_idx);
|
||||
port->reset_count++;
|
||||
} else if (port->reset_count == 1)
|
||||
/* see header */
|
||||
d_printf(2, wusbhc->dev, "Ignoring second reset on port_idx "
|
||||
"%u\n", port_idx);
|
||||
port->status |= USB_PORT_STAT_RESET;
|
||||
port->change |= USB_PORT_STAT_C_RESET;
|
||||
|
||||
if (wusb_dev->addr & WUSB_DEV_ADDR_UNAUTH)
|
||||
result = 0;
|
||||
else
|
||||
result = wusbhc_dev_reset(wusbhc, port_idx);
|
||||
d_fnend(3, wusbhc->dev, "(wusbhc %p port_idx %u) = %d\n",
|
||||
wusbhc, port_idx, result);
|
||||
result = wusb_dev_update_address(wusbhc, wusb_dev);
|
||||
|
||||
port->status &= ~USB_PORT_STAT_RESET;
|
||||
port->status |= USB_PORT_STAT_ENABLE;
|
||||
port->change |= USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_ENABLE;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -338,8 +338,7 @@ static void hs_printk(unsigned level, struct device *dev,
|
|||
* Before the device's address (as known by it) was usb_dev->devnum |
|
||||
* 0x80 (unauthenticated address). With this we update it to usb_dev->devnum.
|
||||
*/
|
||||
static int wusb_dev_update_address(struct wusbhc *wusbhc,
|
||||
struct wusb_dev *wusb_dev)
|
||||
int wusb_dev_update_address(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
|
||||
{
|
||||
int result = -ENOMEM;
|
||||
struct usb_device *usb_dev = wusb_dev->usb_dev;
|
||||
|
|
|
@ -154,7 +154,6 @@ struct wusb_port {
|
|||
u16 status;
|
||||
u16 change;
|
||||
struct wusb_dev *wusb_dev; /* connected device's info */
|
||||
unsigned reset_count;
|
||||
u32 ptk_tkid;
|
||||
};
|
||||
|
||||
|
@ -387,10 +386,8 @@ extern void wusbhc_devconnect_destroy(struct wusbhc *);
|
|||
extern int wusbhc_devconnect_start(struct wusbhc *wusbhc,
|
||||
const struct wusb_ckhdid *chid);
|
||||
extern void wusbhc_devconnect_stop(struct wusbhc *wusbhc);
|
||||
extern int wusbhc_devconnect_auth(struct wusbhc *, u8);
|
||||
extern void wusbhc_handle_dn(struct wusbhc *, u8 srcaddr,
|
||||
struct wusb_dn_hdr *dn_hdr, size_t size);
|
||||
extern int wusbhc_dev_reset(struct wusbhc *wusbhc, u8 port);
|
||||
extern void __wusbhc_dev_disable(struct wusbhc *wusbhc, u8 port);
|
||||
extern int wusb_usb_ncb(struct notifier_block *nb, unsigned long val,
|
||||
void *priv);
|
||||
|
@ -436,6 +433,7 @@ extern void wusb_dev_sec_rm(struct wusb_dev *) ;
|
|||
extern int wusb_dev_4way_handshake(struct wusbhc *, struct wusb_dev *,
|
||||
struct wusb_ckhdid *ck);
|
||||
void wusbhc_gtk_rekey(struct wusbhc *wusbhc);
|
||||
int wusb_dev_update_address(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev);
|
||||
|
||||
|
||||
/* WUSB Cluster ID handling */
|
||||
|
|
Загрузка…
Ссылка в новой задаче