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: Thomas Pugliese <thomas.pugliese@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Thomas Pugliese 2014-03-06 12:53:37 -06:00 committed by Greg Kroah-Hartman
parent 5090ecea13
commit 86e2864d7e
3 changed files with 106 additions and 42 deletions

View file

@ -261,8 +261,44 @@ static int __hwahc_op_wusbhc_start(struct wusbhc *wusbhc)
dev_err(dev, "cannot listen to notifications: %d\n", result);
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;
error_dti_start:
wa_nep_disarm(&hwahc->wa);
error_stop:
__wa_clear_feature(&hwahc->wa, WA_ENABLE);
return result;
@ -827,10 +863,12 @@ static void hwahc_disconnect(struct usb_interface *usb_iface)
static struct usb_device_id hwahc_id_table[] = {
/* Alereon 5310 */
{ 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 */
{ 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 */
{ USB_INTERFACE_INFO(0xe0, 0x02, 0x01), },
{},

View file

@ -134,8 +134,18 @@ enum wa_quirks {
* requests to be concatenated and not sent as separate packets.
*/
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
*
@ -234,6 +244,7 @@ struct wahc {
extern int wa_create(struct wahc *wa, struct usb_interface *iface,
kernel_ulong_t);
extern void __wa_destroy(struct wahc *wa);
extern int wa_dti_start(struct wahc *wa);
void wa_reset_all(struct wahc *wa);

View file

@ -2740,6 +2740,58 @@ out:
return;
}
/*
* 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.
*/
int wa_dti_start(struct wahc *wa)
{
const struct usb_endpoint_descriptor *dti_epd = wa->dti_epd;
struct device *dev = &wa->usb_iface->dev;
int result = -ENOMEM;
if (wa->dti_urb != NULL) /* DTI URB already started */
goto out;
wa->dti_urb = usb_alloc_urb(0, GFP_KERNEL);
if (wa->dti_urb == NULL) {
dev_err(dev, "Can't allocate DTI URB\n");
goto error_dti_urb_alloc;
}
usb_fill_bulk_urb(
wa->dti_urb, wa->usb_dev,
usb_rcvbulkpipe(wa->usb_dev, 0x80 | dti_epd->bEndpointAddress),
wa->dti_buf, wa->dti_buf_size,
wa_dti_cb, wa);
wa->buf_in_urb = usb_alloc_urb(0, GFP_KERNEL);
if (wa->buf_in_urb == NULL) {
dev_err(dev, "Can't allocate BUF-IN URB\n");
goto error_buf_in_urb_alloc;
}
usb_fill_bulk_urb(
wa->buf_in_urb, wa->usb_dev,
usb_rcvbulkpipe(wa->usb_dev, 0x80 | dti_epd->bEndpointAddress),
NULL, 0, wa_buf_in_cb, wa);
result = usb_submit_urb(wa->dti_urb, GFP_KERNEL);
if (result < 0) {
dev_err(dev, "DTI Error: Could not submit DTI URB (%d) resetting\n",
result);
goto error_dti_urb_submit;
}
out:
return 0;
error_dti_urb_submit:
usb_put_urb(wa->buf_in_urb);
wa->buf_in_urb = NULL;
error_buf_in_urb_alloc:
usb_put_urb(wa->dti_urb);
wa->dti_urb = NULL;
error_dti_urb_alloc:
return result;
}
EXPORT_SYMBOL_GPL(wa_dti_start);
/*
* Transfer complete notification
*
@ -2754,15 +2806,10 @@ out:
* 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 result;
struct device *dev = &wa->usb_iface->dev;
struct wa_notif_xfer *notif_xfer;
const struct usb_endpoint_descriptor *dti_epd = wa->dti_epd;
@ -2776,45 +2823,13 @@ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr)
notif_xfer->bEndpoint, dti_epd->bEndpointAddress);
goto error;
}
if (wa->dti_urb != NULL) /* DTI URB already started */
goto out;
wa->dti_urb = usb_alloc_urb(0, GFP_KERNEL);
if (wa->dti_urb == NULL) {
dev_err(dev, "Can't allocate DTI URB\n");
goto error_dti_urb_alloc;
}
usb_fill_bulk_urb(
wa->dti_urb, wa->usb_dev,
usb_rcvbulkpipe(wa->usb_dev, 0x80 | notif_xfer->bEndpoint),
wa->dti_buf, wa->dti_buf_size,
wa_dti_cb, wa);
/* attempt to start the DTI ep processing. */
if (wa_dti_start(wa) < 0)
goto error;
wa->buf_in_urb = usb_alloc_urb(0, GFP_KERNEL);
if (wa->buf_in_urb == NULL) {
dev_err(dev, "Can't allocate BUF-IN URB\n");
goto error_buf_in_urb_alloc;
}
usb_fill_bulk_urb(
wa->buf_in_urb, wa->usb_dev,
usb_rcvbulkpipe(wa->usb_dev, 0x80 | notif_xfer->bEndpoint),
NULL, 0, wa_buf_in_cb, wa);
result = usb_submit_urb(wa->dti_urb, GFP_KERNEL);
if (result < 0) {
dev_err(dev, "DTI Error: Could not submit DTI URB (%d) resetting\n",
result);
goto error_dti_urb_submit;
}
out:
return;
error_dti_urb_submit:
usb_put_urb(wa->buf_in_urb);
wa->buf_in_urb = NULL;
error_buf_in_urb_alloc:
usb_put_urb(wa->dti_urb);
wa->dti_urb = NULL;
error_dti_urb_alloc:
error:
wa_reset_all(wa);
}