* 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
* 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"
.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 */
},
.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 */
},
.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;
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);
}
/*
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);
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));
-
# 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);
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;
/*
*
* 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))
#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;
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;
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)
/* Disable Device */
andb(AR9170_USB_REG_DEVICE_ADDRESS,
(uint8_t) ~(AR9170_USB_DEVICE_ADDRESS_CONFIGURE));
-#ifdef CONFIG_CARL9170FW_USB_MODESWITCH
case 1:
fw.usb.config = config;
+#ifdef CONFIG_CARL9170FW_USB_MODESWITCH
if (usb_detect_highspeed()) {
/* High Speed Configuration */
usb_init_highspeed_fifo_cfg();
/* Full Speed Configuration */
usb_init_fullspeed_fifo_cfg();
}
+ /* usb_pta_init() ? */
break;
+#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)
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)
{
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:
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)