usb: wusbcore: disable transfer notifications for Alereon HWAs
authorThomas Pugliese <thomas.pugliese@gmail.com>
Thu, 6 Mar 2014 18:53:37 +0000 (12:53 -0600)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 7 Mar 2014 20:52:04 +0000 (12:52 -0800)
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>
drivers/usb/host/hwa-hc.c
drivers/usb/wusbcore/wa-hc.h
drivers/usb/wusbcore/wa-xfer.c

index e07669993f5835858e5fffc087cfc45dc869a75b..d0d8fadf706610ca6483cd1cea19583f6fffbf1f 100644 (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), },
        {},
index a2ef84b8397e18efc96fcd413f583e88657a7af1..7510960588dcedaebbad9690cea8f019a341b4d2 100644 (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);
 
 
index cf7c95ceebe06e0ceaa6bd733d48de1731724ece..52c7259705e6567ae73bfe663340689b13dbdf09 100644 (file)
@@ -2741,41 +2741,15 @@ out:
 }
 
 /*
- * 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.
- *
- * 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
+ * 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.
  */
-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;
+       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 */
                goto out;
 
@@ -2786,7 +2760,7 @@ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr)
        }
        usb_fill_bulk_urb(
                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_cb, wa);
 
@@ -2797,7 +2771,7 @@ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr)
        }
        usb_fill_bulk_urb(
                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);
        result = usb_submit_urb(wa->dti_urb, GFP_KERNEL);
        if (result < 0) {
@@ -2806,7 +2780,7 @@ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr)
                goto error_dti_urb_submit;
        }
 out:
-       return;
+       return 0;
 
 error_dti_urb_submit:
        usb_put_urb(wa->buf_in_urb);
@@ -2815,6 +2789,47 @@ 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
+ *
+ * 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:
        wa_reset_all(wa);
 }
This page took 0.029789 seconds and 5 git commands to generate.