carl9170 firmware: add fall through comments
[carl9170fw.git] / carlfw / usb / usb.c
index 49852fb70611a318d4410cf66d1e3c9855c688f8..aedf30d1404830bb989e9ba8caeb4a459aee5fc1 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (c) 2000-2005  ZyDAS Technology Corporation
  * Copyright (c) 2007-2009 Atheros Communications, Inc.
  * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
- * Copyright   2009    Christian Lamparter <chunkeey@googlemail.com>
+ * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -19,8 +19,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * with this program; If not, see <http://www.gnu.org/licenses/>.
  */
 #include "carl9170.h"
 #include "usb.h"
@@ -40,7 +39,11 @@ static struct ar9170_usb_config usb_config_highspeed = {
                .bNumInterfaces = 1,
                .bConfigurationValue = 1,
                .iConfiguration = 0,
-               .bmAttributes = USB_CONFIG_ATT_ONE,
+               .bmAttributes = USB_CONFIG_ATT_ONE |
+#ifdef CONFIG_CARL9170FW_WOL
+                               USB_CONFIG_ATT_WAKEUP |
+#endif /* CONFIG_CARL9170FW_WOL */
+                               0,
                .bMaxPower = 0xfa, /* 500 mA */
        },
 
@@ -103,7 +106,11 @@ static struct ar9170_usb_config usb_config_fullspeed = {
                .bNumInterfaces = 1,
                .bConfigurationValue = 1,
                .iConfiguration = 0,
-               .bmAttributes = USB_CONFIG_ATT_ONE,
+               .bmAttributes = USB_CONFIG_ATT_ONE |
+#ifdef CONFIG_CARL9170FW_WOL
+                               USB_CONFIG_ATT_WAKEUP |
+#endif /* CONFIG_CARL9170FW_WOL */
+                               0,
                .bMaxPower = 0xfa, /* 500 mA */
        },
 
@@ -151,15 +158,14 @@ static struct ar9170_usb_config usb_config_fullspeed = {
                        .bLength = USB_DT_ENDPOINT_SIZE,
                        .bDescriptorType = USB_DT_ENDPOINT,
                        .bEndpointAddress = USB_DIR_OUT | AR9170_USB_EP_CMD,
-                       .bmAttributes = USB_ENDPOINT_XFER_INT,
+                       .bmAttributes = USB_ENDPOINT_XFER_BULK,
                        .wMaxPacketSize = cpu_to_le16(64),
                        .bInterval = 1,
                },
        },
 };
 
-#ifdef CONFIG_CARL9170FW_USB_MODESWITCH
-static void usb_reset_eps(void)
+void usb_reset_eps(void)
 {
        unsigned int i;
 
@@ -167,6 +173,7 @@ static void usb_reset_eps(void)
        for (i = 1; i < __AR9170_USB_NUM_MAX_EP; i++) {
                usb_set_input_ep_toggle(i);
                usb_clear_input_ep_toggle(i);
+               usb_clear_input_ep_stall(i);
        }
 
        /*
@@ -176,14 +183,13 @@ static void usb_reset_eps(void)
        for (i = 1; i < __AR9170_USB_NUM_MAX_EP; i++) {
                usb_set_output_ep_toggle(i);
                usb_clear_output_ep_toggle(i);
+               usb_clear_output_ep_stall(i);
        }
 }
-#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */
-
 
-#ifdef CONFIG_CARL9170FW_USB_INIT_FIRMWARE
 static void usb_pta_init(void)
 {
+       unsigned int usb_dma_ctrl = 0;
        /* Set PTA mode to USB */
        andl(AR9170_PTA_REG_DMA_MODE_CTRL,
                ~AR9170_PTA_DMA_MODE_CTRL_DISABLE_USB);
@@ -197,44 +203,34 @@ static void usb_pta_init(void)
                fw.usb.cfg_desc = &usb_config_highspeed;
 
                /* 512 Byte DMA transfers */
-               orl(AR9170_USB_REG_DMA_CTL, AR9170_DMA_CTL_HIGH_SPEED);
+               usb_dma_ctrl |= AR9170_USB_DMA_CTL_HIGH_SPEED;
        } else {
                fw.usb.cfg_desc = &usb_config_fullspeed;
                fw.usb.os_cfg_desc = &usb_config_highspeed;
        }
 
 #ifdef CONFIG_CARL9170FW_USB_UP_STREAM
-       /* Enable upload stream mode */
-       andl(AR9170_USB_REG_DMA_CTL, ~AR9170_DMA_CTL_UP_PACKET_MODE);
-
-       /* reset maximum transfer size */
-       andl(AR9170_USB_REG_DMA_CTL, ~(AR9170_DMA_CTL_UP_STREAM_MASK));
-
 # if (CONFIG_CARL9170FW_RX_FRAME_LEN == 4096)
-       orl(AR9170_USB_REG_DMA_CTL, AR9170_DMA_CTL_UP_STREAM_4K);
+       usb_dma_ctrl |= AR9170_USB_DMA_CTL_UP_STREAM_4K;
 # elif (CONFIG_CARL9170FW_RX_FRAME_LEN == 8192)
-       orl(AR9170_USB_REG_DMA_CTL, AR9170_DMA_CTL_UP_STREAM_8K);
+       usb_dma_ctrl |= AR9170_USB_DMA_CTL_UP_STREAM_8K;
 # elif (CONFIG_CARL9170FW_RX_FRAME_LEN == 16384)
-       orl(AR9170_USB_REG_DMA_CTL, AR9170_DMA_CTL_UP_STREAM_16K);
+       usb_dma_ctrl |= AR9170_USB_DMA_CTL_UP_STREAM_16K;
 # elif (CONFIG_CARL9170FW_RX_FRAME_LEN == 32768)
-       orl(AR9170_USB_REG_DMA_CTL, AR9170_DMA_CTL_UP_STREAM_32K);
+       usb_dma_ctrl |= AR9170_USB_DMA_CTL_UP_STREAM_32K;
 # else
 #      error "Invalid AR9170_RX_FRAME_LEN setting"
 # endif
 
 #else /* CONFIG_CARL9170FW_USB_UP_STREAM */
-       orl(AR9170_USB_REG_DMA_CTL, AR9170_DMA_CTL_UP_PACKET_MODE);
+       usb_dma_ctrl |= AR9170_USB_DMA_CTL_UP_PACKET_MODE;
 #endif /* CONFIG_CARL9170FW_USB_UP_STREAM */
 
 #ifdef CONFIG_CARL9170FW_USB_DOWN_STREAM
        /* Enable down stream mode */
-       orl(AR9170_USB_REG_DMA_CTL, AR9170_DMA_CTL_DOWN_STREAM);
+       usb_dma_ctrl |= AR9170_USB_DMA_CTL_DOWN_STREAM;
 #endif /* CONFIG_CARL9170FW_USB_DOWN_STREAM */
 
-       /* Enable up stream and down stream */
-       orl(AR9170_USB_REG_DMA_CTL, AR9170_DMA_CTL_ENABLE_TO_DEVICE |
-           AR9170_DMA_CTL_ENABLE_FROM_DEVICE);
-
 #ifdef CONFIG_CARL9170FW_USB_UP_STREAM
        /* Set the up stream mode maximum aggregate number */
        set(AR9170_USB_REG_MAX_AGG_UPLOAD, 4);
@@ -246,14 +242,16 @@ static void usb_pta_init(void)
        set(AR9170_USB_REG_UPLOAD_TIME_CTL, 0x80);
 #endif /* CONFIG_CARL9170FW_USB_UP_STREAM */
 
+       /* Enable up stream and down stream */
+       usb_dma_ctrl |= AR9170_USB_DMA_CTL_ENABLE_TO_DEVICE |
+           AR9170_USB_DMA_CTL_ENABLE_FROM_DEVICE;
+
+       set(AR9170_USB_REG_DMA_CTL, usb_dma_ctrl);
 }
-#endif /* CONFIG_CARL9170FW_USB_INIT_FIRMWARE */
 
 void usb_init(void)
 {
-#ifdef CONFIG_CARL9170FW_USB_INIT_FIRMWARE
        usb_pta_init();
-#endif /* CONFIG_CARL9170FW_USB_INIT_FIRMWARE */
 
        fw.usb.config = 1;
        /*
@@ -262,7 +260,13 @@ void usb_init(void)
         *
         * fw.usb.interface_setting = 0;
         * fw.usb.alternate_interface_setting = 0;
+        * fw.usb.device_feature = 0;
         */
+
+#ifdef CONFIG_CARL9170FW_WOL
+       fw.usb.device_feature |= USB_DEVICE_REMOTE_WAKEUP;
+       usb_enable_remote_wakeup();
+#endif /* CONFIG_CARL9170FW_WOL */
 }
 
 #define GET_ARRAY(a, o)        ((uint32_t *) (((unsigned long) data) + offset))
@@ -315,7 +319,7 @@ static int usb_ep0tx_data(const void *data, const unsigned int len)
 #ifdef CONFIG_CARL9170FW_USB_STANDARD_CMDS
 static int usb_get_status(const struct usb_ctrlrequest *ctrl)
 {
-       __le16 status = cpu_to_le16(0);
+       __le16 status = cpu_to_le16(fw.usb.device_feature);
 
        if ((ctrl->bRequestType & USB_DIR_MASK) != USB_DIR_IN)
                return -1;
@@ -331,7 +335,30 @@ static int usb_get_status(const struct usb_ctrlrequest *ctrl)
                status = cpu_to_le16(0);
                break;
 
-       case USB_RECIP_ENDPOINT:
+       case USB_RECIP_ENDPOINT: {
+               unsigned int ep = le16_to_cpu(ctrl->wIndex) & 0xf;
+               unsigned int dir = le16_to_cpu(ctrl->wIndex) & USB_DIR_MASK;
+
+               if (ep == 0) {
+                       status = !!(getb(AR9170_USB_REG_CX_CONFIG_STATUS) & BIT(2));
+               } else {
+                       unsigned int addr;
+
+                       if (dir == USB_DIR_IN)
+                               addr = AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH;
+                       else
+                               addr = AR9170_USB_REG_EP_OUT_MAX_SIZE_HIGH;
+
+                       addr += (ep << 1);
+
+                       /*
+                        * AR9170_USB_EP_OUT_STALL == AR9170_USB_EP_IN_STALL
+                        * so it doesn't matter which one we use
+                        */
+                       status = !!(getb(addr) & AR9170_USB_EP_OUT_STALL);
+               }
+               break;
+               }
        case USB_RECIP_OTHER:
        default:
                break;
@@ -472,7 +499,7 @@ static int usb_get_configuration(const struct usb_ctrlrequest *ctrl)
        if (USB_CHECK_REQTYPE(ctrl, USB_RECIP_DEVICE, USB_DIR_IN))
                return -1;
 
-       return usb_ep0tx_data(&fw.usb.config, 1);
+       return usb_ep0tx_data(&fw.usb.config, fw.usb.config);
 }
 
 static int usb_set_configuration(const struct usb_ctrlrequest *ctrl)
@@ -488,10 +515,11 @@ static int usb_set_configuration(const struct usb_ctrlrequest *ctrl)
                /* Disable Device */
                andb(AR9170_USB_REG_DEVICE_ADDRESS,
                      (uint8_t) ~(AR9170_USB_DEVICE_ADDRESS_CONFIGURE));
-#ifdef CONFIG_CARL9170FW_USB_MODESWITCH
+               /* fall through */
        case 1:
                fw.usb.config = config;
 
+#ifdef CONFIG_CARL9170FW_USB_MODESWITCH
                if (usb_detect_highspeed()) {
                        /* High Speed Configuration */
                        usb_init_highspeed_fifo_cfg();
@@ -499,25 +527,20 @@ static int usb_set_configuration(const struct usb_ctrlrequest *ctrl)
                        /* Full Speed Configuration */
                        usb_init_fullspeed_fifo_cfg();
                }
-               break;
+               /* usb_pta_init() ? */
+#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */
 
-       default:
-               return -1;
-       }
-       /* usb_pta_init() ? */
+               usb_reset_eps();
+               orb(AR9170_USB_REG_DEVICE_ADDRESS,
+                   (AR9170_USB_DEVICE_ADDRESS_CONFIGURE));
 
-       usb_reset_eps();
-       orb(AR9170_USB_REG_DEVICE_ADDRESS,
-           (AR9170_USB_DEVICE_ADDRESS_CONFIGURE));
+               usb_enable_global_int();
+               usb_trigger_out();
+               return 1;
 
-       usb_enable_global_int();
-       usb_trigger_out();
-       return 1;
-#else
        default:
                return -1;
        }
-#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */
 }
 
 static int usb_set_address(const struct usb_ctrlrequest *ctrl)
@@ -562,6 +585,77 @@ static int usb_get_interface(const struct usb_ctrlrequest *ctrl)
        return usb_ep0tx_data(&fw.usb.alternate_interface_setting, 1);
 }
 
+static int usb_manipulate_feature(const struct usb_ctrlrequest *ctrl, bool __unused clear)
+{
+       unsigned int feature;
+
+       if ((ctrl->bRequestType & USB_DIR_MASK) != USB_DIR_OUT)
+               return -1;
+
+       if (usb_configured() == false)
+               return -1;
+
+       feature = le16_to_cpu(ctrl->wValue);
+
+       switch (ctrl->bRequestType & USB_RECIP_MASK) {
+       case USB_RECIP_DEVICE: {
+#ifdef CONFIG_CARL9170FW_WOL
+               if (feature & USB_DEVICE_REMOTE_WAKEUP) {
+                       if (clear)
+                               usb_disable_remote_wakeup();
+                       else
+                               usb_enable_remote_wakeup();
+               }
+#endif /* CONFIG_CARL9170FW_WOL */
+
+               if (clear)
+                       fw.usb.device_feature &= ~feature;
+               else
+                       fw.usb.device_feature |= feature;
+
+
+               break;
+               }
+
+       case USB_RECIP_ENDPOINT: {
+               unsigned int ep, dir;
+
+               ep = le16_to_cpu(ctrl->wIndex) & 0xf;
+               dir = le16_to_cpu(ctrl->wIndex) & USB_DIR_MASK;
+               if (ep == 0) {
+                       /* According to the spec, EP cannot be stopped this way. */
+                       return -1;
+               } else {
+                       unsigned int addr;
+
+                       if (dir == USB_DIR_IN)
+                               addr = AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH;
+                       else
+                               addr = AR9170_USB_REG_EP_OUT_MAX_SIZE_HIGH;
+
+                       addr += (ep << 1);
+
+                       if (clear)
+                               andb(addr, ~AR9170_USB_EP_OUT_STALL);
+                       else
+                               orb(addr, AR9170_USB_EP_OUT_STALL);
+               }
+               break;
+               }
+
+       case USB_RECIP_INTERFACE:
+               /*
+                * the current USB Specification Revision 2
+                * specifies no interface features.
+                */
+
+       default:
+               return -1;
+       }
+
+       return 1;
+}
+
 #ifdef CONFIG_CARL9170FW_USB_MODESWITCH
 static int usb_set_interface(const struct usb_ctrlrequest *ctrl)
 {
@@ -610,9 +704,8 @@ static int usb_standard_command(const struct usb_ctrlrequest *ctrl __unused)
                break;
 
        case USB_REQ_CLEAR_FEATURE:
-               break;
-
        case USB_REQ_SET_FEATURE:
+               status = usb_manipulate_feature(ctrl, ctrl->bRequest == USB_REQ_CLEAR_FEATURE);
                break;
 
        case USB_REQ_SET_ADDRESS:
@@ -700,8 +793,10 @@ void usb_ep0setup(void)
 
        if (status < 0)
                fw.usb.ep0_action |= CARL9170_EP0_STALL;
+#ifdef CONFIG_CARL9170FW_USB_STANDARD_CMDS
        if (status > 0)
                fw.usb.ep0_action |= CARL9170_EP0_TRIGGER;
+#endif /* CONFIG_CARL9170FW_USB_STANDARD_CMDS */
 }
 
 void usb_ep0rx(void)