wusb: do a proper channel stop
When stopping the WUSB channel the host should send Channel Stop IEs giving the WUSB Channel Time of the last MMC. Both WHCI and HWA hosts provide a channel stop command for this. Signed-off-by: David Vrabel <david.vrabel@csr.com>
This commit is contained in:
parent
d409f3bf47
commit
4d2bea4ca0
|
@ -171,11 +171,6 @@ static int hwahc_op_start(struct usb_hcd *usb_hcd)
|
|||
if (result < 0)
|
||||
goto error_set_cluster_id;
|
||||
|
||||
result = wa_nep_arm(&hwahc->wa, GFP_KERNEL);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "cannot listen to notifications: %d\n", result);
|
||||
goto error_stop;
|
||||
}
|
||||
usb_hcd->uses_new_polling = 1;
|
||||
usb_hcd->poll_rh = 1;
|
||||
usb_hcd->state = HC_STATE_RUNNING;
|
||||
|
@ -185,8 +180,6 @@ out:
|
|||
d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result);
|
||||
return result;
|
||||
|
||||
error_stop:
|
||||
__wa_stop(&hwahc->wa);
|
||||
error_set_cluster_id:
|
||||
wusb_cluster_id_put(wusbhc->cluster_id);
|
||||
error_cluster_id_get:
|
||||
|
@ -194,39 +187,6 @@ error_cluster_id_get:
|
|||
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: break this function up
|
||||
*/
|
||||
static int __hwahc_op_wusbhc_start(struct wusbhc *wusbhc)
|
||||
{
|
||||
int result;
|
||||
struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
|
||||
struct device *dev = &hwahc->wa.usb_iface->dev;
|
||||
|
||||
/* Set up a Host Info WUSB Information Element */
|
||||
d_fnstart(4, dev, "(hwahc %p)\n", hwahc);
|
||||
result = -ENOSPC;
|
||||
|
||||
result = __wa_set_feature(&hwahc->wa, WA_ENABLE);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "error commanding HC to start: %d\n", result);
|
||||
goto error_stop;
|
||||
}
|
||||
result = __wa_wait_status(&hwahc->wa, WA_ENABLE, WA_ENABLE);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "error waiting for HC to start: %d\n", result);
|
||||
goto error_stop;
|
||||
}
|
||||
result = 0;
|
||||
out:
|
||||
d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result);
|
||||
return result;
|
||||
|
||||
error_stop:
|
||||
result = __wa_clear_feature(&hwahc->wa, WA_ENABLE);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int hwahc_op_suspend(struct usb_hcd *usb_hcd, pm_message_t msg)
|
||||
{
|
||||
struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
|
||||
|
@ -246,18 +206,6 @@ static int hwahc_op_resume(struct usb_hcd *usb_hcd)
|
|||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static void __hwahc_op_wusbhc_stop(struct wusbhc *wusbhc)
|
||||
{
|
||||
int result;
|
||||
struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
|
||||
struct device *dev = &hwahc->wa.usb_iface->dev;
|
||||
|
||||
d_fnstart(4, dev, "(hwahc %p)\n", hwahc);
|
||||
/* Nothing for now */
|
||||
d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* No need to abort pipes, as when this is called, all the children
|
||||
* has been disconnected and that has done it [through
|
||||
|
@ -275,8 +223,6 @@ static void hwahc_op_stop(struct usb_hcd *usb_hcd)
|
|||
d_fnstart(4, dev, "(hwahc %p)\n", hwahc);
|
||||
mutex_lock(&wusbhc->mutex);
|
||||
wusbhc_stop(wusbhc);
|
||||
wa_nep_disarm(&hwahc->wa);
|
||||
result = __wa_stop(&hwahc->wa);
|
||||
wusb_cluster_id_put(wusbhc->cluster_id);
|
||||
mutex_unlock(&wusbhc->mutex);
|
||||
d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result);
|
||||
|
@ -325,6 +271,54 @@ static void hwahc_op_endpoint_disable(struct usb_hcd *usb_hcd,
|
|||
rpipe_ep_disable(&hwahc->wa, ep);
|
||||
}
|
||||
|
||||
static int __hwahc_op_wusbhc_start(struct wusbhc *wusbhc)
|
||||
{
|
||||
int result;
|
||||
struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
|
||||
struct device *dev = &hwahc->wa.usb_iface->dev;
|
||||
|
||||
result = __wa_set_feature(&hwahc->wa, WA_ENABLE);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "error commanding HC to start: %d\n", result);
|
||||
goto error_stop;
|
||||
}
|
||||
result = __wa_wait_status(&hwahc->wa, WA_ENABLE, WA_ENABLE);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "error waiting for HC to start: %d\n", result);
|
||||
goto error_stop;
|
||||
}
|
||||
result = wa_nep_arm(&hwahc->wa, GFP_KERNEL);
|
||||
if (result < 0) {
|
||||
dev_err(dev, "cannot listen to notifications: %d\n", result);
|
||||
goto error_stop;
|
||||
}
|
||||
return result;
|
||||
|
||||
error_stop:
|
||||
__wa_clear_feature(&hwahc->wa, WA_ENABLE);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void __hwahc_op_wusbhc_stop(struct wusbhc *wusbhc, int delay)
|
||||
{
|
||||
struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
|
||||
struct wahc *wa = &hwahc->wa;
|
||||
u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
|
||||
int ret;
|
||||
|
||||
ret = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
|
||||
WUSB_REQ_CHAN_STOP,
|
||||
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
delay * 1000,
|
||||
iface_no,
|
||||
NULL, 0, 1000 /* FIXME: arbitrary */);
|
||||
if (ret == 0)
|
||||
msleep(delay);
|
||||
|
||||
wa_nep_disarm(&hwahc->wa);
|
||||
__wa_stop(&hwahc->wa);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the UWB MAS allocation for the WUSB cluster
|
||||
*
|
||||
|
|
|
@ -136,7 +136,7 @@ int whc_do_gencmd(struct whc *whc, u32 cmd, u32 params, void *addr, size_t len);
|
|||
|
||||
/* wusb.c */
|
||||
int whc_wusbhc_start(struct wusbhc *wusbhc);
|
||||
void whc_wusbhc_stop(struct wusbhc *wusbhc);
|
||||
void whc_wusbhc_stop(struct wusbhc *wusbhc, int delay);
|
||||
int whc_mmcie_add(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt,
|
||||
u8 handle, struct wuie_hdr *wuie);
|
||||
int whc_mmcie_rm(struct wusbhc *wusbhc, u8 handle);
|
||||
|
|
|
@ -410,6 +410,8 @@ struct dn_buf_entry {
|
|||
# define WUSBDNTSCTRL_SLOTS(s) ((s) << 0)
|
||||
|
||||
#define WUSBTIME 0x68
|
||||
# define WUSBTIME_CHANNEL_TIME_MASK 0x00ffffff
|
||||
|
||||
#define WUSBBPST 0x6c
|
||||
#define WUSBDIBUPDATED 0x70
|
||||
|
||||
|
|
|
@ -64,8 +64,9 @@ static int whc_update_di(struct whc *whc, int idx)
|
|||
}
|
||||
|
||||
/*
|
||||
* WHCI starts and stops MMCs based on there being a valid GTK so
|
||||
* these need only start/stop the asynchronous and periodic schedules.
|
||||
* WHCI starts MMCs based on there being a valid GTK so these need
|
||||
* only start/stop the asynchronous and periodic schedules and send a
|
||||
* channel stop command.
|
||||
*/
|
||||
|
||||
int whc_wusbhc_start(struct wusbhc *wusbhc)
|
||||
|
@ -78,12 +79,20 @@ int whc_wusbhc_start(struct wusbhc *wusbhc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void whc_wusbhc_stop(struct wusbhc *wusbhc)
|
||||
void whc_wusbhc_stop(struct wusbhc *wusbhc, int delay)
|
||||
{
|
||||
struct whc *whc = wusbhc_to_whc(wusbhc);
|
||||
u32 stop_time, now_time;
|
||||
int ret;
|
||||
|
||||
pzl_stop(whc);
|
||||
asl_stop(whc);
|
||||
|
||||
now_time = le_readl(whc->base + WUSBTIME) & WUSBTIME_CHANNEL_TIME_MASK;
|
||||
stop_time = (now_time + ((delay * 8) << 7)) & 0x00ffffff;
|
||||
ret = whc_do_gencmd(whc, WUSBGENCMDSTS_CHAN_STOP, stop_time, NULL, 0);
|
||||
if (ret == 0)
|
||||
msleep(delay);
|
||||
}
|
||||
|
||||
int whc_mmcie_add(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt,
|
||||
|
|
|
@ -250,18 +250,14 @@ error_alloc:
|
|||
* wusbhc_stop - stop transmitting MMCs
|
||||
* @wusbhc: the HC to stop
|
||||
*
|
||||
* Send a Host Disconnect IE, wait, remove all the MMCs (stop sending MMCs).
|
||||
*
|
||||
* If we can't allocate a Host Stop IE, screw it, we don't notify the
|
||||
* devices we are disconnecting...
|
||||
* Stops the WUSB channel and removes the cluster reservation.
|
||||
*/
|
||||
void wusbhc_stop(struct wusbhc *wusbhc)
|
||||
{
|
||||
if (wusbhc->active) {
|
||||
wusbhc->active = 0;
|
||||
wusbhc->stop(wusbhc);
|
||||
wusbhc->stop(wusbhc, WUSB_CHANNEL_STOP_DELAY_MS);
|
||||
wusbhc_sec_stop(wusbhc);
|
||||
__wusbhc_host_disconnect_ie(wusbhc);
|
||||
wusbhc_devconnect_stop(wusbhc);
|
||||
wusbhc_rsv_terminate(wusbhc);
|
||||
}
|
||||
|
|
|
@ -64,6 +64,13 @@
|
|||
#include <linux/uwb.h>
|
||||
#include <linux/usb/wusb.h>
|
||||
|
||||
/*
|
||||
* Time from a WUSB channel stop request to the last transmitted MMC.
|
||||
*
|
||||
* This needs to be > 4.096 ms in case no MMCs can be transmitted in
|
||||
* zone 0.
|
||||
*/
|
||||
#define WUSB_CHANNEL_STOP_DELAY_MS 8
|
||||
|
||||
/**
|
||||
* Wireless USB device
|
||||
|
@ -198,21 +205,18 @@ struct wusb_port {
|
|||
* @mmcies_max Max number of Information Elements this HC can send
|
||||
* in its MMC. Read-only.
|
||||
*
|
||||
* @start Start the WUSB channel.
|
||||
*
|
||||
* @stop Stop the WUSB channel after the specified number of
|
||||
* milliseconds. Channel Stop IEs should be transmitted
|
||||
* as required by [WUSB] 4.16.2.1.
|
||||
*
|
||||
* @mmcie_add HC specific operation (WHCI or HWA) for adding an
|
||||
* MMCIE.
|
||||
*
|
||||
* @mmcie_rm HC specific operation (WHCI or HWA) for removing an
|
||||
* MMCIE.
|
||||
*
|
||||
* @enc_types Array which describes the encryptions methods
|
||||
* supported by the host as described in WUSB1.0 --
|
||||
* one entry per supported method. As of WUSB1.0 there
|
||||
* is only four methods, we make space for eight just in
|
||||
* case they decide to add some more (and pray they do
|
||||
* it in sequential order). if 'enc_types[enc_method]
|
||||
* != 0', then it is supported by the host. enc_method
|
||||
* is USB_ENC_TYPE*.
|
||||
*
|
||||
* @set_ptk: Set the PTK and enable encryption for a device. Or, if
|
||||
* the supplied key is NULL, disable encryption for that
|
||||
* device.
|
||||
|
@ -269,7 +273,7 @@ struct wusbhc {
|
|||
u8 mmcies_max;
|
||||
/* FIXME: make wusbhc_ops? */
|
||||
int (*start)(struct wusbhc *wusbhc);
|
||||
void (*stop)(struct wusbhc *wusbhc);
|
||||
void (*stop)(struct wusbhc *wusbhc, int delay);
|
||||
int (*mmcie_add)(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt,
|
||||
u8 handle, struct wuie_hdr *wuie);
|
||||
int (*mmcie_rm)(struct wusbhc *wusbhc, u8 handle);
|
||||
|
|
|
@ -51,6 +51,7 @@ enum {
|
|||
WUSB_REQ_GET_TIME = 25,
|
||||
WUSB_REQ_SET_STREAM_IDX = 26,
|
||||
WUSB_REQ_SET_WUSB_MAS = 27,
|
||||
WUSB_REQ_CHAN_STOP = 28,
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue