Skip to content

Commit

Permalink
wusb: do a proper channel stop
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
David Vrabel committed Oct 28, 2008
1 parent d409f3b commit 4d2bea4
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 74 deletions.
102 changes: 48 additions & 54 deletions drivers/usb/host/hwa-hc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -185,48 +180,13 @@ static int hwahc_op_start(struct usb_hcd *usb_hcd)
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:
goto out;

}

/*
* 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);
Expand All @@ -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
Expand All @@ -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);
Expand Down Expand Up @@ -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
*
Expand Down
2 changes: 1 addition & 1 deletion drivers/usb/host/whci/whcd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/host/whci/whci-hc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
15 changes: 12 additions & 3 deletions drivers/usb/host/whci/wusb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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,
Expand Down
8 changes: 2 additions & 6 deletions drivers/usb/wusbcore/mmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -250,18 +250,14 @@ static int __wusbhc_host_disconnect_ie(struct wusbhc *wusbhc)
* 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);
}
Expand Down
24 changes: 14 additions & 10 deletions drivers/usb/wusbcore/wusbhc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions include/linux/usb/wusb-wa.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};


Expand Down

0 comments on commit 4d2bea4

Please sign in to comment.