Commit 86e2864d authored by Thomas Pugliese's avatar Thomas Pugliese Committed by Greg Kroah-Hartman

usb: wusbcore: disable transfer notifications for Alereon HWAs

The HWA driver does not do anything with transfer notifications after
receiving the first one and the Alereon HWA allows them to be disabled
as a performance optimization.  This patch sends a vendor specific
command to the Alereon HWA on startup to disable transfer notifications.
If the command is successful, the DTI system is started immediately
since that would normally be started upon the first reception of a
transfer notification which will no longer be sent.
Signed-off-by: default avatarThomas Pugliese <thomas.pugliese@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 5090ecea
...@@ -261,8 +261,44 @@ static int __hwahc_op_wusbhc_start(struct wusbhc *wusbhc) ...@@ -261,8 +261,44 @@ static int __hwahc_op_wusbhc_start(struct wusbhc *wusbhc)
dev_err(dev, "cannot listen to notifications: %d\n", result); dev_err(dev, "cannot listen to notifications: %d\n", result);
goto error_stop; goto error_stop;
} }
/*
* If WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS is set,
* disable transfer notifications.
*/
if (hwahc->wa.quirks &
WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS) {
struct usb_host_interface *cur_altsetting =
hwahc->wa.usb_iface->cur_altsetting;
result = usb_control_msg(hwahc->wa.usb_dev,
usb_sndctrlpipe(hwahc->wa.usb_dev, 0),
WA_REQ_ALEREON_DISABLE_XFER_NOTIFICATIONS,
USB_DIR_OUT | USB_TYPE_VENDOR |
USB_RECIP_INTERFACE,
WA_REQ_ALEREON_FEATURE_SET,
cur_altsetting->desc.bInterfaceNumber,
NULL, 0,
USB_CTRL_SET_TIMEOUT);
/*
* If we successfully sent the control message, start DTI here
* because no transfer notifications will be received which is
* where DTI is normally started.
*/
if (result == 0)
result = wa_dti_start(&hwahc->wa);
else
result = 0; /* OK. Continue normally. */
if (result < 0) {
dev_err(dev, "cannot start DTI: %d\n", result);
goto error_dti_start;
}
}
return result; return result;
error_dti_start:
wa_nep_disarm(&hwahc->wa);
error_stop: error_stop:
__wa_clear_feature(&hwahc->wa, WA_ENABLE); __wa_clear_feature(&hwahc->wa, WA_ENABLE);
return result; return result;
...@@ -827,10 +863,12 @@ static void hwahc_disconnect(struct usb_interface *usb_iface) ...@@ -827,10 +863,12 @@ static void hwahc_disconnect(struct usb_interface *usb_iface)
static struct usb_device_id hwahc_id_table[] = { static struct usb_device_id hwahc_id_table[] = {
/* Alereon 5310 */ /* Alereon 5310 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5310, 0xe0, 0x02, 0x01), { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5310, 0xe0, 0x02, 0x01),
.driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC }, .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC |
WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS },
/* Alereon 5611 */ /* Alereon 5611 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5611, 0xe0, 0x02, 0x01), { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5611, 0xe0, 0x02, 0x01),
.driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC }, .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC |
WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS },
/* FIXME: use class labels for this */ /* FIXME: use class labels for this */
{ USB_INTERFACE_INFO(0xe0, 0x02, 0x01), }, { USB_INTERFACE_INFO(0xe0, 0x02, 0x01), },
{}, {},
......
...@@ -134,8 +134,18 @@ enum wa_quirks { ...@@ -134,8 +134,18 @@ enum wa_quirks {
* requests to be concatenated and not sent as separate packets. * requests to be concatenated and not sent as separate packets.
*/ */
WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC = 0x01, WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC = 0x01,
/*
* The Alereon HWA can be instructed to not send transfer notifications
* as an optimization.
*/
WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS = 0x02,
}; };
enum wa_vendor_specific_requests {
WA_REQ_ALEREON_DISABLE_XFER_NOTIFICATIONS = 0x4C,
WA_REQ_ALEREON_FEATURE_SET = 0x01,
WA_REQ_ALEREON_FEATURE_CLEAR = 0x00,
};
/** /**
* Instance of a HWA Host Controller * Instance of a HWA Host Controller
* *
...@@ -234,6 +244,7 @@ struct wahc { ...@@ -234,6 +244,7 @@ struct wahc {
extern int wa_create(struct wahc *wa, struct usb_interface *iface, extern int wa_create(struct wahc *wa, struct usb_interface *iface,
kernel_ulong_t); kernel_ulong_t);
extern void __wa_destroy(struct wahc *wa); extern void __wa_destroy(struct wahc *wa);
extern int wa_dti_start(struct wahc *wa);
void wa_reset_all(struct wahc *wa); void wa_reset_all(struct wahc *wa);
......
...@@ -2741,41 +2741,15 @@ static void wa_dti_cb(struct urb *urb) ...@@ -2741,41 +2741,15 @@ static void wa_dti_cb(struct urb *urb)
} }
/* /*
* Transfer complete notification * Initialize the DTI URB for reading transfer result notifications and also
* * the buffer-in URB, for reading buffers. Then we just submit the DTI URB.
* Called from the notif.c code. We get a notification on EP2 saying
* that some endpoint has some transfer result data available. We are
* about to read it.
*
* To speed up things, we always have a URB reading the DTI URB; we
* don't really set it up and start it until the first xfer complete
* notification arrives, which is what we do here.
*
* Follow up in wa_dti_cb(), as that's where the whole state
* machine starts.
*
* So here we just initialize the DTI URB for reading transfer result
* notifications and also the buffer-in URB, for reading buffers. Then
* we just submit the DTI URB.
*
* @wa shall be referenced
*/ */
void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr) int wa_dti_start(struct wahc *wa)
{ {
int result;
struct device *dev = &wa->usb_iface->dev;
struct wa_notif_xfer *notif_xfer;
const struct usb_endpoint_descriptor *dti_epd = wa->dti_epd; const struct usb_endpoint_descriptor *dti_epd = wa->dti_epd;
struct device *dev = &wa->usb_iface->dev;
int result = -ENOMEM;
notif_xfer = container_of(notif_hdr, struct wa_notif_xfer, hdr);
BUG_ON(notif_hdr->bNotifyType != WA_NOTIF_TRANSFER);
if ((0x80 | notif_xfer->bEndpoint) != dti_epd->bEndpointAddress) {
/* FIXME: hardcoded limitation, adapt */
dev_err(dev, "BUG: DTI ep is %u, not %u (hack me)\n",
notif_xfer->bEndpoint, dti_epd->bEndpointAddress);
goto error;
}
if (wa->dti_urb != NULL) /* DTI URB already started */ if (wa->dti_urb != NULL) /* DTI URB already started */
goto out; goto out;
...@@ -2786,7 +2760,7 @@ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr) ...@@ -2786,7 +2760,7 @@ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr)
} }
usb_fill_bulk_urb( usb_fill_bulk_urb(
wa->dti_urb, wa->usb_dev, wa->dti_urb, wa->usb_dev,
usb_rcvbulkpipe(wa->usb_dev, 0x80 | notif_xfer->bEndpoint), usb_rcvbulkpipe(wa->usb_dev, 0x80 | dti_epd->bEndpointAddress),
wa->dti_buf, wa->dti_buf_size, wa->dti_buf, wa->dti_buf_size,
wa_dti_cb, wa); wa_dti_cb, wa);
...@@ -2797,7 +2771,7 @@ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr) ...@@ -2797,7 +2771,7 @@ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr)
} }
usb_fill_bulk_urb( usb_fill_bulk_urb(
wa->buf_in_urb, wa->usb_dev, wa->buf_in_urb, wa->usb_dev,
usb_rcvbulkpipe(wa->usb_dev, 0x80 | notif_xfer->bEndpoint), usb_rcvbulkpipe(wa->usb_dev, 0x80 | dti_epd->bEndpointAddress),
NULL, 0, wa_buf_in_cb, wa); NULL, 0, wa_buf_in_cb, wa);
result = usb_submit_urb(wa->dti_urb, GFP_KERNEL); result = usb_submit_urb(wa->dti_urb, GFP_KERNEL);
if (result < 0) { if (result < 0) {
...@@ -2806,7 +2780,7 @@ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr) ...@@ -2806,7 +2780,7 @@ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr)
goto error_dti_urb_submit; goto error_dti_urb_submit;
} }
out: out:
return; return 0;
error_dti_urb_submit: error_dti_urb_submit:
usb_put_urb(wa->buf_in_urb); usb_put_urb(wa->buf_in_urb);
...@@ -2815,6 +2789,47 @@ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr) ...@@ -2815,6 +2789,47 @@ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr)
usb_put_urb(wa->dti_urb); usb_put_urb(wa->dti_urb);
wa->dti_urb = NULL; wa->dti_urb = NULL;
error_dti_urb_alloc: error_dti_urb_alloc:
return result;
}
EXPORT_SYMBOL_GPL(wa_dti_start);
/*
* Transfer complete notification
*
* Called from the notif.c code. We get a notification on EP2 saying
* that some endpoint has some transfer result data available. We are
* about to read it.
*
* To speed up things, we always have a URB reading the DTI URB; we
* don't really set it up and start it until the first xfer complete
* notification arrives, which is what we do here.
*
* Follow up in wa_dti_cb(), as that's where the whole state
* machine starts.
*
* @wa shall be referenced
*/
void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr)
{
struct device *dev = &wa->usb_iface->dev;
struct wa_notif_xfer *notif_xfer;
const struct usb_endpoint_descriptor *dti_epd = wa->dti_epd;
notif_xfer = container_of(notif_hdr, struct wa_notif_xfer, hdr);
BUG_ON(notif_hdr->bNotifyType != WA_NOTIF_TRANSFER);
if ((0x80 | notif_xfer->bEndpoint) != dti_epd->bEndpointAddress) {
/* FIXME: hardcoded limitation, adapt */
dev_err(dev, "BUG: DTI ep is %u, not %u (hack me)\n",
notif_xfer->bEndpoint, dti_epd->bEndpointAddress);
goto error;
}
/* attempt to start the DTI ep processing. */
if (wa_dti_start(wa) < 0)
goto error;
return;
error: error:
wa_reset_all(wa); wa_reset_all(wa);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment