--- drivers/net/wireless/Kconfig | 1 drivers/net/wireless/Makefile | 1 drivers/net/wireless/ar6k/Kconfig | 11 drivers/net/wireless/ar6k/Makefile | 19 drivers/net/wireless/ar6k/ar6k.h | 68 drivers/net/wireless/ar6k/ar6k_main.c | 5579 +++++++++++++++++ drivers/net/wireless/ar6k/bmi.c | 466 + drivers/net/wireless/ar6k/hif.c | 306 drivers/net/wireless/ar6k/hif.h | 87 drivers/net/wireless/ar6k/htc/Makefile | 5 drivers/net/wireless/ar6k/htc/htc.c | 460 + drivers/net/wireless/ar6k/htc/htc_events.c | 941 ++ drivers/net/wireless/ar6k/htc/htc_internal.h | 456 + drivers/net/wireless/ar6k/htc/htc_recv.c | 204 drivers/net/wireless/ar6k/htc/htc_send.c | 389 + drivers/net/wireless/ar6k/htc/htc_utils.c | 394 + drivers/net/wireless/ar6k/include/AR6000_bmi.h | 136 drivers/net/wireless/ar6k/include/AR6000_gpio.h | 39 drivers/net/wireless/ar6k/include/AR6000_version.h | 44 drivers/net/wireless/ar6k/include/app/dset.h | 77 drivers/net/wireless/ar6k/include/ar6k_api.h | 79 drivers/net/wireless/ar6k/include/athdefs.h | 85 drivers/net/wireless/ar6k/include/athdrv.h | 597 + drivers/net/wireless/ar6k/include/dset_api.h | 47 drivers/net/wireless/ar6k/include/gpio_api.h | 60 drivers/net/wireless/ar6k/include/hif.h | 75 drivers/net/wireless/ar6k/include/host_version.h | 47 drivers/net/wireless/ar6k/include/htc.h | 115 drivers/net/wireless/ar6k/include/hw/#mbox_host_reg.h# | 416 + drivers/net/wireless/ar6k/include/hw/mbox_host_reg.h | 416 + drivers/net/wireless/ar6k/include/hw/mbox_reg.h | 550 + drivers/net/wireless/ar6k/include/hw/mc_reg.h | 1129 +++ drivers/net/wireless/ar6k/include/hw/rtc_reg.h | 1204 +++ drivers/net/wireless/ar6k/include/ieee80211.h | 333 + drivers/net/wireless/ar6k/include/ieee80211_ioctl.h | 121 drivers/net/wireless/ar6k/include/ieee80211_node.h | 74 drivers/net/wireless/ar6k/include/osapi.h | 49 drivers/net/wireless/ar6k/include/osapi_linux.h | 167 drivers/net/wireless/ar6k/include/queue.h | 550 + drivers/net/wireless/ar6k/include/wlan_api.h | 90 drivers/net/wireless/ar6k/include/wmi.h | 1133 +++ drivers/net/wireless/ar6k/include/wmi_api.h | 165 drivers/net/wireless/ar6k/include/wmi_host.h | 74 drivers/net/wireless/ar6k/include/wmix.h | 196 drivers/net/wireless/ar6k/install | 3 drivers/net/wireless/ar6k/wlan/Makefile | 2 drivers/net/wireless/ar6k/wlan/wlan_node.c | 259 drivers/net/wireless/ar6k/wlan/wlan_recv_beacon.c | 169 drivers/net/wireless/ar6k/wlan/wlan_utils.c | 53 drivers/net/wireless/ar6k/wmi.c | 2834 ++++++++ 50 files changed, 20775 insertions(+) Index: linux-2.6.22-atheros-ng/drivers/net/wireless/ar6k/ar6k.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22-atheros-ng/drivers/net/wireless/ar6k/ar6k.h 2007-12-11 22:02:08.000000000 +0100 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2004-2006 Atheros Communications Inc. + * Copyright (c) 2007 Felix Fietkau + * + * Wireless Network driver for Atheros AR6001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + */ + +#ifndef _AR6K_H_ +#define _AR6K_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "include/athdefs.h" +#include "hif.h" + +#define MAX_AR6000 1 +#define AR6000_MAX_RX_BUFFERS 16 +#define AR6000_BUFFER_SIZE 1552 +#define AR6000_TX_TIMEOUT 10 +#define AR6000_ETH_ADDR_LEN 6 +#define AR6000_MAX_ENDPOINTS 4 +#define MAX_NODE_NUM 15 +#define MAX_COOKIE_NUM 150 +#define AR6000_DATA_OFFSET 64 + +static inline struct sk_buff *ar6k_alloc_skb(int size) +{ + struct sk_buff *skb; + + skb = dev_alloc_skb(AR6000_DATA_OFFSET + size); + skb_reserve(skb, AR6000_DATA_OFFSET); + + return skb; +} + + +/* BMI API */ +void BMIInit(void); +int BMIDone(struct sdio_func *device); +int BMIGetTargetId(struct sdio_func *device, u32 * id); +int BMIReadMemory(struct sdio_func *device, u32 address, u8 * buffer, + u32 length); +int BMIWriteMemory(struct sdio_func *device, u32 address, u8 * buffer, + u32 length); +int BMIExecute(struct sdio_func *device, u32 address, u32 * param); +int BMISetAppStart(struct sdio_func *device, u32 address); +int BMIReadSOCRegister(struct sdio_func *device, u32 address, u32 * param); +int BMIWriteSOCRegister(struct sdio_func *device, u32 address, u32 param); + +#endif Index: linux-2.6.22-atheros-ng/drivers/net/wireless/ar6k/ar6k_main.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22-atheros-ng/drivers/net/wireless/ar6k/ar6k_main.c 2007-12-19 21:42:39.000000000 +0100 @@ -0,0 +1,5579 @@ +/* + * Copyright (c) 2004-2006 Atheros Communications Inc. + * Copyright (c) 2007 Felix Fietkau + * + * Wireless Network driver for Atheros AR6001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + */ + +/* + * This driver is a pseudo ethernet driver to access the Atheros AR6000 + * WLAN Device + */ + +#include "ar6k.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "include/osapi.h" +#include "include/htc.h" +#include "include/wmi.h" +#include "include/athdrv.h" +#include "include/ieee80211.h" +#include "include/ieee80211_ioctl.h" +#include "include/wlan_api.h" +#include "include/wmi_api.h" +#include "include/dset_api.h" +#include "include/app/dset.h" +#include "include/gpio_api.h" +#include "include/AR6000_gpio.h" +#include "include/host_version.h" +#include "include/hw/mbox_host_reg.h" +#include "include/hw/mc_reg.h" +#include "include/hw/mbox_reg.h" +#include "include/hw/rtc_reg.h" + +MODULE_LICENSE("GPL"); + +#if defined (SPI) +#define CONTROL_PADCONF_AA12 0x00E8 +unsigned int awake_gpio = 17; +unsigned int awake_gpio_pad_conf_offset = CONTROL_PADCONF_AA12; +unsigned int awake_gpio_pad_conf_byte = 1; +unsigned int awake_gpio_pad_mode_value = 0x3; +#endif + +int bmienable = 0; +unsigned int bypasswmi = 0; +unsigned int debuglevel = 0; +int tspecCompliance = CCX_V4_COMPLIANCE; +unsigned int busspeedlow = 0; +unsigned int onebitmode = 0; +unsigned int skipflash = 0; + +module_param(bmienable, int, 0644); +module_param(bypasswmi, int, 0644); +module_param(debuglevel, int, 0644); +module_param(tspecCompliance, int, 0644); +module_param(onebitmode, int, 0644); +module_param(busspeedlow, int, 0644); +module_param(skipflash, int, 0644); + +#define MAX_ALLOWED_TXQ_DEPTH (HTC_DATA_REQUEST_RING_BUFFER_SIZE - 2) +static int txFlowCtrlThresh[HTC_MAILBOX_NUM_MAX] + = { 0 * (MAX_ALLOWED_TXQ_DEPTH - 1), + 1 * (MAX_ALLOWED_TXQ_DEPTH - 1), + 2 * (MAX_ALLOWED_TXQ_DEPTH - 1), + 3 * (MAX_ALLOWED_TXQ_DEPTH - 1) +}; + +/* in 2.6.10 and later this is now a pointer to a uint */ +unsigned int _mboxnum = HTC_MAILBOX_NUM_MAX; +#define mboxnum &_mboxnum + +#ifdef CONFIG_AR6K_DEBUG +int debugdriver = 3; +unsigned int debughtc = 128; +unsigned int debugbmi = 1; +unsigned int debughif = 1; +unsigned int txcreditsavailable[HTC_MAILBOX_NUM_MAX] = { 0 }; +unsigned int txcreditsconsumed[HTC_MAILBOX_NUM_MAX] = { 0 }; +unsigned int txcreditintrenable[HTC_MAILBOX_NUM_MAX] = { 0 }; +unsigned int txcreditintrenableaggregate[HTC_MAILBOX_NUM_MAX] = { 0 }; + +module_param(debugdriver, int, 0644); +module_param(debughtc, int, 0644); +module_param(debugbmi, int, 0644); +module_param(debughif, int, 0644); +module_param_array(txcreditsavailable, int, mboxnum, 0644); +module_param_array(txcreditsconsumed, int, mboxnum, 0644); +module_param_array(txcreditintrenable, int, mboxnum, 0644); +module_param_array(txcreditintrenableaggregate, int, mboxnum, 0644); + +#endif /* DEBUG */ + +unsigned int tx_attempt[HTC_MAILBOX_NUM_MAX] = { 0 }; +unsigned int tx_post[HTC_MAILBOX_NUM_MAX] = { 0 }; +unsigned int tx_complete[HTC_MAILBOX_NUM_MAX] = { 0 }; +module_param_array(tx_attempt, int, mboxnum, 0644); +module_param_array(tx_post, int, mboxnum, 0644); +module_param_array(tx_complete, int, mboxnum, 0644); + +#ifdef BLOCK_TX_PATH_FLAG +int blocktx = 0; +module_param(blocktx, int, 0644); +#endif /* BLOCK_TX_PATH_FLAG */ + +#ifdef CONFIG_AR6K_DEBUG +#define AR_DEBUG_PRINTF(args...) if (debugdriver) printk(args); +#define AR_DEBUG2_PRINTF(args...) if (debugdriver >= 2) printk(args); +#else +#define AR_DEBUG_PRINTF(args...) +#define AR_DEBUG2_PRINTF(args...) +#endif + +struct ar_wep_key { + u8 arKeyIndex; + u8 arKeyLen; + u8 arKey[64]; +}; + +struct ar_node_mapping { + u8 macAddress[6]; + u8 epId; + u8 txPending; +}; + +struct ar_cookie { + u32 arc_bp[2]; /* Must be first field */ + struct ar_cookie *arc_list_next; +}; + +typedef struct ar6_softc { + struct net_device *arNetDev; /* net_device pointer */ + void *arWmi; + int arTxPending[AR6000_MAX_ENDPOINTS]; + int arTotalTxDataPending; + u8 arNumDataEndPts; + u8 arWmiEnabled; + u8 arWmiReady; + u8 arConnected; + void *arHtcTarget; + void *arHifDevice; + spinlock_t arLock; + struct semaphore arSem; + int arRxBuffers[AR6000_MAX_ENDPOINTS]; + int arSsidLen; + u_char arSsid[32]; + u8 arNetworkType; + u8 arDot11AuthMode; + u8 arAuthMode; + u8 arPairwiseCrypto; + u8 arPairwiseCryptoLen; + u8 arGroupCrypto; + u8 arGroupCryptoLen; + u8 arDefTxKeyIndex; + struct ar_wep_key arWepKeyList[WMI_MAX_KEY_INDEX + 1]; + u8 arBssid[6]; + u8 arReqBssid[6]; + u16 arChannelHint; + u16 arBssChannel; + u16 arListenInterval; + struct ar6k_version arVersion; + s8 arRssi; + u8 arTxPwr; + u8 arTxPwrSet; + s32 arBitRate; + struct net_device_stats arNetStats; + struct iw_statistics arIwStats; + s8 arNumChannels; + u16 arChannelList[32]; + u32 arRegCode; + u8 statsUpdatePending; + TARGET_STATS arTargetStats; + s8 arMaxRetries; + u8 arPhyCapability; + AR6000_WLAN_STATE arWlanState; + struct ar_node_mapping arNodeMap[MAX_NODE_NUM]; + u8 arIbssPsEnable; + u8 arNodeNum; + u8 arNexEpId; + struct ar_cookie *arCookieList; + u8 arConnectPending; +} AR_SOFTC_T; + +struct ar_giwscan_param { + char *current_ev; + char *end_buf; + u8 firstPass; +}; + +#define AR6000_STAT_INC(ar, stat) (ar->arNetStats.stat++) + +#define AR6000_SPIN_LOCK(lock, param) do { \ + if (irqs_disabled()) { \ + AR_DEBUG_PRINTF("IRQs disabled:AR6000_LOCK\n"); \ + } \ + spin_lock_bh(lock); \ +} while (0) + +#define AR6000_SPIN_UNLOCK(lock, param) do { \ + if (irqs_disabled()) { \ + AR_DEBUG_PRINTF("IRQs disabled: AR6000_UNLOCK\n"); \ + } \ + spin_unlock_bh(lock); \ +} while (0) + +/* Function declarations */ +static int ar6k_init_module(void); +static void ar6k_cleanup_module(void); + +static int ar6k_init(struct net_device *dev); +static int ar6k_open(struct net_device *dev); +static int ar6k_close(struct net_device *dev); +static void ar6k_init_control_info(AR_SOFTC_T * ar); +static void ar6k_init_profile_info(AR_SOFTC_T * ar); +static int ar6k_data_tx(struct sk_buff *skb, struct net_device *dev); +static int ar6k_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static void ar6k_destroy(struct net_device *dev); +static struct net_device_stats *ar6k_get_stats(struct net_device *dev); +static void ar6k_ioctl_iwsetup(struct iw_handler_def *def); +static int ar6k_ioctl_set_channelParams(struct net_device *dev, + struct ifreq *rq); +static int ar6k_ioctl_set_probedSsid(struct net_device *dev, + struct ifreq *rq); +static int ar6k_ioctl_set_badAp(struct net_device *dev, + struct ifreq *rq); +static int ar6k_ioctl_get_qos_queue(struct net_device *dev, + struct ifreq *rq); +static int ar6k_ioctl_delete_qos(struct net_device *dev, + struct ifreq *rq); +static int ar6k_ioctl_create_qos(struct net_device *dev, + struct ifreq *rq); +static int ar6k_ioctl_set_link_threshold(struct net_device *dev, + struct ifreq *rq); +static int ar6k_ioctl_get_target_stats(struct net_device *dev, + struct ifreq *rq); +static int ar6k_ioctl_set_error_report_bitmask(struct net_device *dev, + struct ifreq *rq); +static int ar6k_ioctl_set_access_params(struct net_device *dev, + struct ifreq *rq); +static int ar6k_ioctl_set_disconnect_timeout(struct net_device *dev, + struct ifreq *rq); +static int ar6k_xioctl_set_voice_pkt_size(struct net_device *dev, + char *userdata); +static int ar6k_xioctl_set_max_sp_len(struct net_device *dev, + char *userdata); +static int ar6k_ioctl_get_roam_tbl(struct net_device *dev, + struct ifreq *rq); +static int ar6k_ioctl_get_roam_data(struct net_device *dev, + struct ifreq *rq); +static int ar6k_ioctl_set_roam_ctrl(struct net_device *dev, + char *userdata); +static int ar6k_ioctl_set_powersave_timers(struct net_device *dev, + char *userdata); +static int ar6k_ioctl_get_power_mode(struct net_device *dev, + struct ifreq *rq); + +#ifdef HTC_RAW_INTERFACE +static void ar6k_htc_raw_read_cb(HTC_TARGET * htcTarget, + HTC_ENDPOINT_ID endPointId, + HTC_EVENT_ID evId, + HTC_EVENT_INFO * evInfo, void *arg); +static void ar6k_htc_raw_unread_cb(HTC_TARGET * htcTarget, + HTC_ENDPOINT_ID endPointId, + HTC_EVENT_ID evId, + HTC_EVENT_INFO * evInfo, void *arg); +static void ar6k_htc_raw_write_cb(HTC_TARGET * htcTarget, + HTC_ENDPOINT_ID endPointId, + HTC_EVENT_ID evId, + HTC_EVENT_INFO * evInfo, void *arg); +static int ar6k_htc_raw_open(HTC_TARGET * htcTarget); +static int ar6k_htc_raw_close(HTC_TARGET * htcTarget); +static ssize_t ar6k_htc_raw_read(HTC_TARGET * htcTarget, + HTC_ENDPOINT_ID endPointId, + char __user * buffer, size_t count); +static ssize_t ar6k_htc_raw_write(HTC_TARGET * htcTarget, + HTC_ENDPOINT_ID endPointId, + char __user * buffer, size_t count); +#endif /* HTC_RAW_INTERFACE */ +static void ar6k_install_static_wep_keys(AR_SOFTC_T * ar); +static int ar6k_ioctl_addpmkid(struct net_device *dev, + struct iw_request_info *info, void *w, + char *extra); + +#if CONFIG_HOST_DSET_SUPPORT +static void ar6k_dset_init(void); +static int ar6k_ioctl_wait_dset_req(struct net_device *dev, + struct ifreq *rq); +static int ar6k_ioctl_dset_open_reply(struct net_device *dev, + struct ifreq *rq); +static int ar6k_ioctl_dset_data_reply(struct net_device *dev, + struct ifreq *rq); +#endif /* CONFIG_HOST_DSET_SUPPORT */ + +#if CONFIG_HOST_GPIO_SUPPORT +static void ar6k_gpio_init(void); +int ar6k_gpio_output_set(struct net_device *dev, + u32 set_mask, + u32 clear_mask, + u32 enable_mask, u32 disable_mask); +static int ar6k_gpio_input_get(struct net_device *dev); +static int ar6k_gpio_register_set(struct net_device *dev, + u32 gpioreg_id, u32 value); +static int ar6k_gpio_register_get(struct net_device *dev, + u32 gpioreg_id); +static int ar6k_gpio_intr_ack(struct net_device *dev, u32 ack_mask); +#endif /* CONFIG_HOST_GPIO_SUPPORT */ + +/* + * HTC event handlers + */ +static void ar6k_avail_ev(HTC_TARGET * htcTarget, HTC_ENDPOINT_ID eid, + HTC_EVENT_ID id, HTC_EVENT_INFO * evInfo, + void *arg); + +static void ar6k_unavail_ev(HTC_TARGET * htcTarget, HTC_ENDPOINT_ID eid, + HTC_EVENT_ID id, HTC_EVENT_INFO * evInfo, + void *arg); + +static void ar6k_rx(HTC_TARGET * htcTarget, HTC_ENDPOINT_ID eid, + HTC_EVENT_ID id, HTC_EVENT_INFO * evInfo, void *arg); + +static void ar6k_rx_refill(HTC_TARGET * htcTarget, HTC_ENDPOINT_ID eid, + HTC_EVENT_ID id, HTC_EVENT_INFO * evInfo, + void *arg); + +static void ar6k_tx_complete(HTC_TARGET * htcTarget, HTC_ENDPOINT_ID eid, + HTC_EVENT_ID id, HTC_EVENT_INFO * evInfo, + void *arg); + +/* + * Static variables + */ +static struct net_device *ar6k_devices[MAX_AR6000]; +static struct iw_handler_def ath_iw_handler_def; +static DECLARE_WAIT_QUEUE_HEAD(arEvent); +static u8 bcast_mac[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +static u8 null_mac[] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; + +#if CONFIG_HOST_GPIO_SUPPORT +struct ar6k_gpio_intr_wait_cmd_s gpio_intr_results; +/* gpio_reg_results and gpio_data_available are protected by arSem */ +static struct ar6k_gpio_register_cmd_s gpio_reg_results; +static u8 gpio_data_available; /* Requested GPIO data available */ +static u8 gpio_intr_available; /* GPIO interrupt info available */ +static u8 gpio_ack_received; /* GPIO ack was received */ +#endif /* CONFIG_HOST_GPIO_SUPPORT */ +#if 0 +static struct iw_statistics *ar6k_get_iwstats(struct net_device *dev); +#endif + +/* + * adhoc PS support + */ +static void ar6k_cookie_init(AR_SOFTC_T * ar); +static void ar6k_cookie_cleanup(AR_SOFTC_T * ar); +static void ar6k_free_cookie(AR_SOFTC_T * ar, struct ar_cookie *cookie); +static struct ar_cookie *ar6k_alloc_cookie(AR_SOFTC_T * ar); + +static struct ar_cookie s_ar_cookie_mem[MAX_COOKIE_NUM]; + +static int __init ar6k_init_module(void) +{ + static int probed = 0; + int status; + + printk("##### %s() #########\n", __FUNCTION__); + + if (probed) { + return -ENODEV; + } + probed++; + +#if CONFIG_HOST_DSET_SUPPORT + ar6k_dset_init(); +#endif /* CONFIG_HOST_DSET_SUPPORT */ + +#if CONFIG_HOST_GPIO_SUPPORT + ar6k_gpio_init(); +#endif /* CONFIG_HOST_GPIO_SUPPORT */ + + status = HTCInit(); + if (status != A_OK) + return -ENODEV; + + HTCEventReg(NULL, ENDPOINT_UNUSED, HTC_TARGET_AVAILABLE, + ar6k_avail_ev, NULL); + HTCEventReg(NULL, ENDPOINT_UNUSED, HTC_TARGET_UNAVAILABLE, + ar6k_unavail_ev, NULL); + + return 0; +} + +static void __exit ar6k_cleanup_module(void) +{ + int i = 0; + struct net_device *ar6k_netdev; + + for (i = 0; i < MAX_AR6000; i++) { + if (ar6k_devices[i] != NULL) { + ar6k_netdev = ar6k_devices[i]; + ar6k_devices[i] = NULL; + ar6k_destroy(ar6k_netdev); + } + } + + /* This will unregister with the SDIO bus driver */ + HTCShutDown(NULL); + + AR_DEBUG_PRINTF("ar6k_cleanup: success\n"); +} + +/* + * Write to the AR6000 through its diagnostic window. + * No cooperation from the Target is required for this. + */ +int ar6k_WriteRegDiag(struct sdio_func *func, u32 * address, u32 * data) +{ + int ret; + + ret = hif_rw(func, HIF_WRITE, HIF_INCREMENTAL_ADDRESS, HIF_BYTE_BASIS, + WINDOW_DATA_ADDRESS, (u8 *)data, sizeof(u32)); +// ret = sdio_memcpy_toio(func, WINDOW_DATA_ADDRESS, data, sizeof(u32)); + if (ret) { + AR_DEBUG_PRINTF("Cannot write 0x%x to WINDOW_DATA_ADDRESS\n", + *data); + return A_ERROR; + } + + ret = hif_rw(func, HIF_WRITE, HIF_INCREMENTAL_ADDRESS, HIF_BYTE_BASIS, + WINDOW_DATA_ADDRESS + 1, ((u8 *)(data)) + 1, sizeof(u32) - 1); +// ret = sdio_memcpy_toio(func, WINDOW_WRITE_ADDR_ADDRESS + 1, +// ((u8 *) address) + 1, sizeof(u32) - 1); + if (ret) { + AR_DEBUG_PRINTF + ("Cannot write initial bytes of 0x%x to WINDOW_WRITE_ADDR_ADDRESS\n", + *address); + return A_ERROR; + } + + ret = hif_rw(func, HIF_WRITE, HIF_INCREMENTAL_ADDRESS, HIF_BYTE_BASIS, + WINDOW_WRITE_ADDR_ADDRESS, (u8 *)data, sizeof(u32)); +// ret = sdio_memcpy_toio(func, WINDOW_WRITE_ADDR_ADDRESS, (u8 *) address, 1); + if (ret) { + AR_DEBUG_PRINTF("Cannot write 0x%x to WINDOW_WRITE_ADDR_ADDRESS\n", + *address); + return A_ERROR; + } + + return 0; +} + +/* + * HTC Event handlers + */ +static void +ar6k_avail_ev(HTC_TARGET * htcTarget, HTC_ENDPOINT_ID eid, + HTC_EVENT_ID event, HTC_EVENT_INFO * evInfo, void *arg) +{ + int i, ret ; + struct net_device *dev; + AR_SOFTC_T *ar; + int device_index = 0; + + AR_DEBUG_PRINTF("ar6k_available\n"); + + for (i = 0; i < MAX_AR6000; i++) { + if (ar6k_devices[i] == NULL) { + break; + } + } + + if (i == MAX_AR6000) { + AR_DEBUG_PRINTF("ar6k_available: max devices reached\n"); + return; + } + + /* Save this. It gives a bit better readability especially since */ + /* we use another local "i" variable below. */ + device_index = i; + + A_ASSERT(htcTarget != NULL); + A_ASSERT(event == HTC_TARGET_AVAILABLE); + + dev = alloc_etherdev(sizeof(AR_SOFTC_T)); + if (dev == NULL) { + AR_DEBUG_PRINTF("ar6k_available: can't alloc etherdev\n"); + return; + } + + ether_setup(dev); + + if (dev->priv == NULL) { + printk(KERN_CRIT "ar6k_available: Could not allocate memory\n"); + return; + } + + A_MEMZERO(dev->priv, sizeof(AR_SOFTC_T)); + + ar = (AR_SOFTC_T *) dev->priv; + ar->arNetDev = dev; + ar->arHtcTarget = htcTarget; + ar->arHifDevice = evInfo->buffer; + ar->arWlanState = WLAN_ENABLED; + + ar6k_init_control_info(ar); + init_waitqueue_head(&arEvent); + sema_init(&ar->arSem, 1); + + /* + * If requested, perform some magic which requires no cooperation from + * the Target. It causes the Target to ignore flash and execute the + * OS from ROM. + * + * This code uses the Diagnostic Window to remap instructions at + * the start of ROM in such a way that on the next CPU reset, the + * ROM code avoids using flash. Then it uses the Diagnostic + * Window to force a CPU Warm Reset. + * + * This is intended to support recovery from a corrupted flash. + */ + if (skipflash) { + int i; + + static struct { + u32 addr; + u32 data; + } ForceROM[] = { + /* NB: This is ROM-version dependent. */ + { + 0x00001ff0, 0x175b0027 + }, /* jump instruction at 0xa0001ff0 */ + { + 0x00001ff4, 0x00000000 + }, /* nop instruction at 0xa0001ff4 */ + { + MC_REMAP_TARGET_ADDRESS, 0x00001ff0 + }, /* remap to 0xa0001ff0 */ + { + MC_REMAP_COMPARE_ADDRESS, 0x01000040 + }, /* ...from 0xbfc00040 */ + { + MC_REMAP_SIZE_ADDRESS, 0x00000000 + }, /* ...1 cache line */ + { + MC_REMAP_VALID_ADDRESS, 0x00000001 + }, /* ...remap is valid */ + { + LOCAL_COUNT_ADDRESS + 0x10, 0 + }, /* clear BMI credit counter */ + { + RESET_CONTROL_ADDRESS, RESET_CONTROL_WARM_RST_MASK + }, + }; + + AR_DEBUG_PRINTF("Force Target to execute from ROM....\n"); + for (i = 0; i < sizeof(ForceROM) / sizeof(*ForceROM); i++) { + if (ar6k_WriteRegDiag(ar->arHifDevice, + &ForceROM[i].addr, + &ForceROM[i].data) != A_OK) { + AR_DEBUG_PRINTF("Cannot force Target to execute ROM!\n"); + break; + } + } + } + + BMIInit(); + if ((BMIGetTargetId(ar->arHifDevice, &ar->arVersion.target_ver)) != + A_OK) { + return; + } + + spin_lock_init(&ar->arLock); + + /* Don't install the init function if BMI is requested */ + if (!bmienable) { + dev->init = ar6k_init; + } + dev->open = &ar6k_open; + dev->stop = &ar6k_close; + dev->hard_start_xmit = &ar6k_data_tx; + dev->get_stats = &ar6k_get_stats; +#if 0 + dev->get_wireless_stats = ar6k_get_iwstats; /* Displayed via proc + fs */ +#endif + /* dev->tx_timeout = ar6k_tx_timeout; */ + dev->do_ioctl = &ar6k_ioctl; + dev->watchdog_timeo = AR6000_TX_TIMEOUT; + ar6k_ioctl_iwsetup(&ath_iw_handler_def); + dev->wireless_handlers = &ath_iw_handler_def; + + /* + * We need the OS to provide us with more headroom in order to + * perform dix to 802.3, WMI header encap, and the HTC header + */ + dev->hard_header_len = ETH_HLEN + sizeof(ATH_LLC_SNAP_HDR) + + sizeof(WMI_DATA_HDR) + HTC_HEADER_LEN; + + /* This runs the init function */ + ret = register_netdev(dev); + if (ret) { + AR_DEBUG_PRINTF("ar6k_avail: register_netdev failed (%d)\n", ret); + ar6k_destroy(dev); + return; + } + + /* We only register the device in the global list if we succeed. */ + /* If the device is in the global list, it will be destroyed */ + /* when the module is unloaded. */ + ar6k_devices[device_index] = dev; + + AR_DEBUG_PRINTF + ("ar6k_avail: name=%s htcTarget=0x%x, dev=0x%x (%d), ar=0x%x\n", + dev->name, (u32) htcTarget, (u32) dev, device_index, (u32) ar); +} + +static void +ar6k_unavail_ev(HTC_TARGET * htcTarget, HTC_ENDPOINT_ID eid, + HTC_EVENT_ID event, HTC_EVENT_INFO * evInfo, void *arg) +{ + int i; + AR_SOFTC_T *ar; + struct net_device *ar6k_netdev; + + for (i = 0; i < MAX_AR6000; i++) { + if (ar6k_devices[i] != NULL) { + ar = (AR_SOFTC_T *) ar6k_devices[i]->priv; + if (ar && ar->arHtcTarget == htcTarget) { + ar6k_netdev = ar6k_devices[i]; + ar6k_devices[i] = NULL; + ar6k_destroy(ar6k_netdev); + } + } + } +} + +static void ar6k_destroy(struct net_device *dev) +{ + AR_SOFTC_T *ar; + + if ((dev == NULL) || ((ar = netdev_priv(dev)) == NULL)) { + AR_DEBUG_PRINTF("%s(): Failed to get device structure.\n", + __func__); + return; + } + + /* Stop the transmit queues */ + netif_stop_queue(dev); + + /* Disable the target and the interrupts associated with it */ + if (ar->arWmiReady == TRUE) { + if (!bypasswmi) { + if (ar->arConnected == TRUE || ar->arConnectPending == TRUE) { + AR_DEBUG_PRINTF("%s(): Disconnect\n", __func__); + AR6000_SPIN_LOCK(&ar->arLock, 0); + ar6k_init_profile_info(ar); + wmi_disconnect_cmd(ar->arWmi); + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + } + + /* It's necessary to wait for the tx pending cleaning */ + wait_event_interruptible(arEvent, + ar->arTxPending[WMI_CONTROL_MBOX] == + 0); + + ar->arWmiReady = FALSE; + ar->arConnected = FALSE; + ar->arConnectPending = FALSE; + wmi_shutdown(ar->arWmi); + ar->arWmiEnabled = FALSE; + ar->arWmi = NULL; + ar->arWlanState = WLAN_ENABLED; + } + + /* It's necessary to wait for the tx pending cleaning */ + wait_event_interruptible(arEvent, + ar->arTxPending[WMI_CONTROL_MBOX] == 0); + + HTCStop(ar->arHtcTarget); + + /* It's necessary to wait for the tx pending cleaning */ + wait_event_interruptible(arEvent, + ar->arTxPending[WMI_CONTROL_MBOX] == 0); + + AR_DEBUG_PRINTF("%s(): WMI and HTC stopped\n", __func__); + } else { + AR_DEBUG_PRINTF("%s(): WMI not ready 0x%08x 0x%08x\n", + __func__, (unsigned int) ar, + (unsigned int) ar->arWmi); + + /* Shut down WMI if we have started it */ + if (ar->arWmiEnabled == TRUE) { + AR_DEBUG_PRINTF("%s(): Shut down WMI\n", __func__); + wmi_shutdown(ar->arWmi); + ar->arWmiEnabled = FALSE; + ar->arWmi = NULL; + } + + /* It's necessary to wait for the tx pending cleaning */ + wait_event_interruptible(arEvent, + ar->arTxPending[WMI_CONTROL_MBOX] == 0); + + HTCStop(ar->arHtcTarget); + + /* It's necessary to wait for the tx pending cleaning */ + wait_event_interruptible(arEvent, + ar->arTxPending[WMI_CONTROL_MBOX] == 0); + } + + BMIInit(); + + /* Done with cookies */ + ar6k_cookie_cleanup(ar); + + /* Free up the device data structure */ + HTCShutDown(ar->arHtcTarget); + + unregister_netdev(dev); +#ifdef mvlcee31_2_4_20_omap2420_gsm_gprs + kfree(dev); +#else + free_netdev(dev); +#endif +} + +static void ar6k_init_profile_info(AR_SOFTC_T * ar) +{ + ar->arSsidLen = 0; + A_MEMZERO(ar->arSsid, sizeof(ar->arSsid)); + ar->arNetworkType = INFRA_NETWORK; + ar->arDot11AuthMode = OPEN_AUTH; + ar->arAuthMode = NONE_AUTH; + ar->arPairwiseCrypto = NONE_CRYPT; + ar->arPairwiseCryptoLen = 0; + ar->arGroupCrypto = NONE_CRYPT; + ar->arGroupCryptoLen = 0; + A_MEMZERO(ar->arWepKeyList, sizeof(ar->arWepKeyList)); + A_MEMZERO(ar->arReqBssid, sizeof(ar->arReqBssid)); + A_MEMZERO(ar->arBssid, sizeof(ar->arBssid)); + ar->arBssChannel = 0; +} + +static void ar6k_init_control_info(AR_SOFTC_T * ar) +{ + ar->arWmiEnabled = FALSE; + ar6k_init_profile_info(ar); + ar->arDefTxKeyIndex = 0; + A_MEMZERO(ar->arWepKeyList, sizeof(ar->arWepKeyList)); + ar->arChannelHint = 0; + ar->arListenInterval = MAX_LISTEN_INTERVAL; + ar->arVersion.host_ver = AR6000_SW_VERSION; + ar->arRssi = 0; + ar->arTxPwr = 0; + ar->arTxPwrSet = FALSE; + ar->arBitRate = 0; + ar->arMaxRetries = 0; +} + +static int ar6k_open(struct net_device *dev) +{ + /* Wake up the queues */ + netif_wake_queue(dev); + + return 0; +} + +static int ar6k_close(struct net_device *dev) +{ + netif_stop_queue(dev); + + return 0; +} + +/* This function does one time initialization for the lifetime of the device */ +static int ar6k_init(struct net_device *dev) +{ + int i; + AR_SOFTC_T *ar; + int endpoint[] = { ENDPOINT1, ENDPOINT2, ENDPOINT3, ENDPOINT4 }; + int status; + s32 timeleft; + + if ((ar = netdev_priv(dev)) == NULL) { + return (-EIO); + } + + /* Do we need to finish the BMI phase */ + if (BMIDone(ar->arHifDevice) != A_OK) { + return -EIO; + } + + if (!bypasswmi) { + if (ar->arVersion.host_ver != ar->arVersion.target_ver) { + A_PRINTF("WARNING: Host version 0x%x does not match Target " + " version 0x%x!\n", + ar->arVersion.host_ver, ar->arVersion.target_ver); + } + + /* Indicate that WMI is enabled (although not ready yet) */ + ar->arWmiEnabled = TRUE; + if ((ar->arWmi = wmi_init((void *) ar)) == NULL) { + AR_DEBUG_PRINTF("%s() Failed to initialize WMI.\n", __func__); + return (-EIO); + } + + AR_DEBUG_PRINTF("%s() Got WMI @ 0x%08x.\n", __func__, + (unsigned int) ar->arWmi); + } + + /* Install event handlers for each end point */ + for (i = 0; i < (sizeof(endpoint) / sizeof(int)); i++) { + if (HTCEventReg(ar->arHtcTarget, endpoint[i], HTC_BUFFER_RECEIVED, + ar6k_rx, ar) != A_OK) { + return (-EIO); + } + + if (HTCEventReg(ar->arHtcTarget, endpoint[i], HTC_DATA_AVAILABLE, + ar6k_rx_refill, ar) != A_OK) { + return (-EIO); + } + + if (HTCEventReg(ar->arHtcTarget, endpoint[i], HTC_BUFFER_SENT, + ar6k_tx_complete, ar) != A_OK) { + return (-EIO); + } + } + + /* + * Provide HTC with receive buffers + */ + ar6k_rx_refill(ar->arHtcTarget, ENDPOINT1, HTC_DATA_AVAILABLE, + NULL, ar); + + ar6k_rx_refill(ar->arHtcTarget, ENDPOINT2, HTC_DATA_AVAILABLE, + NULL, ar); + + /* + * We will post the receive buffers only for SPE testing and so we are + * making it conditional on the 'bypasswmi' flag. + */ + if (bypasswmi) { + ar6k_rx_refill(ar->arHtcTarget, ENDPOINT3, HTC_DATA_AVAILABLE, + NULL, ar); + + ar6k_rx_refill(ar->arHtcTarget, ENDPOINT4, HTC_DATA_AVAILABLE, + NULL, ar); + } + + /* Since cookies are used for HTC transports, they should be */ + /* initialized prior to enabling HTC. */ + ar6k_cookie_init(ar); + + /* Enable the target and the interrupts associated with it */ + status = HTCStart(ar->arHtcTarget); + + if (status != A_OK) { + if (ar->arWmiEnabled == TRUE) { + wmi_shutdown(ar->arWmi); + ar->arWmiEnabled = FALSE; + ar->arWmi = NULL; + } + ar6k_cookie_cleanup(ar); + return -EIO; + } + + if (!bypasswmi) { + /* Wait for Wmi event to be ready */ + timeleft = wait_event_interruptible_timeout(arEvent, + (ar->arWmiReady == TRUE), 10 * HZ); + + if (!timeleft || signal_pending(current)) { + AR_DEBUG_PRINTF("WMI is not ready or wait was interrupted\n"); + return -EIO; + } + + AR_DEBUG_PRINTF("%s() WMI is ready\n", __func__); + } + + ar->arNumDataEndPts = 1; + + return (0); +} + +/* This would basically hold all the private ioctls that are not related to + WLAN operation */ +static int ar6k_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + struct sdio_func *hifDevice = ar->arHifDevice; + HTC_TARGET *htcTarget = ar->arHtcTarget; + int ret, param; + unsigned int address = 0; + unsigned int length = 0; + unsigned char *buffer; + char *userdata; +#ifdef HTC_RAW_INTERFACE + HTC_ENDPOINT_ID endPointId = 0; + static u8 rawIfInit = FALSE; +#endif /* HTC_RAW_INTERFACE */ + static WMI_SCAN_PARAMS_CMD scParams = { 0, 0, 0, 0, 0, + WMI_SHORTSCANRATIO_DEFAULT + }; + + if (cmd == AR6000_IOCTL_EXTENDED) { + /* + * This allows for many more wireless ioctls than would otherwise + * be available. Applications embed the actual ioctl command in + * the first word of the parameter block, and use the command + * AR6000_IOCTL_EXTENDED_CMD on the ioctl call. + */ + get_user(cmd, (int *) rq->ifr_data); + userdata = (char *) (((unsigned int *) rq->ifr_data) + 1); + } else { + userdata = (char *) rq->ifr_data; + } + + if ((ar->arWlanState == WLAN_DISABLED) && + (cmd != AR6000_XIOCTRL_WMI_SET_WLAN_STATE)) { + return -EIO; + } + + ret = 0; + switch (cmd) { + case AR6000_XIOCTL_BMI_DONE: + if (bmienable) { + ret = ar6k_init(dev); + } else { + ret = BMIDone(hifDevice); + } + break; + + case AR6000_XIOCTL_BMI_READ_MEMORY: + get_user(address, (unsigned int *) userdata); + get_user(length, (unsigned int *) userdata + 1); + AR_DEBUG_PRINTF("Read Memory (address: 0x%x, length: %d)\n", + address, length); + if ((buffer = (unsigned char *) A_MALLOC(length)) != NULL) { + A_MEMZERO(buffer, length); + ret = BMIReadMemory(hifDevice, address, buffer, length); + if (copy_to_user(rq->ifr_data, buffer, length)) { + ret = -EFAULT; + } + A_FREE(buffer); + } else { + ret = -ENOMEM; + } + break; + + case AR6000_XIOCTL_BMI_WRITE_MEMORY: + get_user(address, (unsigned int *) userdata); + get_user(length, (unsigned int *) userdata + 1); + AR_DEBUG_PRINTF("Write Memory (address: 0x%x, length: %d)\n", + address, length); + if ((buffer = (unsigned char *) A_MALLOC(length)) != NULL) { + A_MEMZERO(buffer, length); + if (copy_from_user(buffer, &userdata[sizeof(address) + + sizeof(length)], length)) + { + ret = -EFAULT; + } else { + ret = BMIWriteMemory(hifDevice, address, buffer, length); + } + A_FREE(buffer); + } else { + ret = -ENOMEM; + } + break; + + case AR6000_XIOCTL_BMI_TEST: + AR_DEBUG_PRINTF("No longer supported\n"); + ret = -EOPNOTSUPP; + break; + + case AR6000_XIOCTL_BMI_EXECUTE: + get_user(address, (unsigned int *) userdata); + get_user(param, (unsigned int *) userdata + 1); + AR_DEBUG_PRINTF("Execute (address: 0x%x, param: %d)\n", + address, param); + ret = BMIExecute(hifDevice, address, ¶m); + put_user(param, (unsigned int *) rq->ifr_data); /* return value */ + break; + + case AR6000_XIOCTL_BMI_SET_APP_START: + get_user(address, (unsigned int *) userdata); + AR_DEBUG_PRINTF("Set App Start (address: 0x%x)\n", address); + ret = BMISetAppStart(hifDevice, address); + break; + + case AR6000_XIOCTL_BMI_READ_SOC_REGISTER: + get_user(address, (unsigned int *) userdata); + ret = BMIReadSOCRegister(hifDevice, address, ¶m); + put_user(param, (unsigned int *) rq->ifr_data); /* return value */ + break; + + case AR6000_XIOCTL_BMI_WRITE_SOC_REGISTER: + get_user(address, (unsigned int *) userdata); + get_user(param, (unsigned int *) userdata + 1); + ret = BMIWriteSOCRegister(hifDevice, address, param); + break; + +#ifdef HTC_RAW_INTERFACE + case AR6000_XIOCTL_HTC_RAW_OPEN: + ret = A_OK; + if (!rawIfInit) { + /* Terminate the BMI phase */ + ret = BMIDone(hifDevice); + if (ret == A_OK) { + ret = ar6k_htc_raw_open(htcTarget); + } + if (ret == A_OK) { + rawIfInit = TRUE; + } + } + break; + + case AR6000_XIOCTL_HTC_RAW_CLOSE: + if (rawIfInit) { + ret = ar6k_htc_raw_close(htcTarget); + rawIfInit = FALSE; + } else { + ret = A_ERROR; + } + break; + + case AR6000_XIOCTL_HTC_RAW_READ: + if (rawIfInit) { + get_user(endPointId, (unsigned int *) userdata); + get_user(length, (unsigned int *) userdata + 1); + buffer = rq->ifr_data + sizeof(length); + ret = ar6k_htc_raw_read(htcTarget, endPointId, + buffer, length); + put_user(ret, (unsigned int *) rq->ifr_data); + } else { + ret = A_ERROR; + } + break; + + case AR6000_XIOCTL_HTC_RAW_WRITE: + if (rawIfInit) { + get_user(endPointId, (unsigned int *) userdata); + get_user(length, (unsigned int *) userdata + 1); + buffer = userdata + sizeof(endPointId) + sizeof(length); + ret = ar6k_htc_raw_write(htcTarget, endPointId, + buffer, length); + put_user(ret, (unsigned int *) rq->ifr_data); + } else { + ret = A_ERROR; + } + break; +#endif /* HTC_RAW_INTERFACE */ + + case AR6000_IOCTL_WMI_GETREV: + { + if (copy_to_user(rq->ifr_data, &ar->arVersion, + sizeof(ar->arVersion))) { + ret = -EFAULT; + } + break; + } + case AR6000_IOCTL_WMI_SETPWR: + { + WMI_POWER_MODE_CMD pwrModeCmd; + + if (ar->arWmiReady == FALSE) { + ret = -EIO; + } else if (copy_from_user(&pwrModeCmd, userdata, + sizeof(pwrModeCmd))) { + ret = -EFAULT; + } else { + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (wmi_powermode_cmd(ar->arWmi, pwrModeCmd.powerMode) + != A_OK) { + ret = -EIO; + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + } + break; + } + case AR6000_IOCTL_WMI_SET_IBSS_PM_CAPS: + { + WMI_IBSS_PM_CAPS_CMD ibssPmCaps; + + if (ar->arWmiReady == FALSE) { + ret = -EIO; + } else if (copy_from_user(&ibssPmCaps, userdata, + sizeof(ibssPmCaps))) { + ret = -EFAULT; + } else { + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (wmi_ibsspmcaps_cmd + (ar->arWmi, ibssPmCaps.power_saving, ibssPmCaps.ttl, + ibssPmCaps.atim_windows, + ibssPmCaps.timeout_value) != A_OK) { + ret = -EIO; + } + ar->arIbssPsEnable = ibssPmCaps.power_saving; + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + } + break; + } + case AR6000_IOCTL_WMI_SET_PMPARAMS: + { + WMI_POWER_PARAMS_CMD pmParams; + + if (ar->arWmiReady == FALSE) { + ret = -EIO; + } else if (copy_from_user(&pmParams, userdata, + sizeof(pmParams))) { + ret = -EFAULT; + } else { + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (wmi_pmparams_cmd(ar->arWmi, pmParams.idle_period, + pmParams.pspoll_number, + pmParams.dtim_policy) != A_OK) { + ret = -EIO; + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + } + break; + } + case AR6000_IOCTL_WMI_SETSCAN: + { + if (ar->arWmiReady == FALSE) { + ret = -EIO; + } else if (copy_from_user(&scParams, userdata, + sizeof(scParams))) { + ret = -EFAULT; + } else { + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (wmi_scanparams_cmd(ar->arWmi, scParams.fg_start_period, + scParams.fg_end_period, + scParams.bg_period, + scParams.act_chdwell_time, + scParams.pas_chdwell_time, + scParams.shortScanRatio) != A_OK) { + ret = -EIO; + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + } + break; + } + case AR6000_IOCTL_WMI_SETLISTENINT: + { + WMI_LISTEN_INT_CMD listenCmd; + + if (ar->arWmiReady == FALSE) { + ret = -EIO; + } else if (copy_from_user(&listenCmd, userdata, + sizeof(listenCmd))) { + ret = -EFAULT; + } else { + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (wmi_listeninterval_cmd + (ar->arWmi, listenCmd.listenInterval, + listenCmd.numBeacons) != A_OK) { + ret = -EIO; + } else { + ar->arListenInterval = param; + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + } + break; + } + case AR6000_IOCTL_WMI_SET_BMISS_TIME: + { + WMI_BMISS_TIME_CMD bmissCmd; + + if (ar->arWmiReady == FALSE) { + ret = -EIO; + } else if (copy_from_user(&bmissCmd, userdata, + sizeof(bmissCmd))) { + ret = -EFAULT; + } else { + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (wmi_bmisstime_cmd + (ar->arWmi, bmissCmd.bmissTime, + bmissCmd.numBeacons) != A_OK) { + ret = -EIO; + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + } + break; + } + case AR6000_IOCTL_WMI_SETBSSFILTER: + { + if (ar->arWmiReady == FALSE) { + ret = -EIO; + } else { + get_user(param, (unsigned int *) userdata); + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (wmi_bssfilter_cmd(ar->arWmi, param) != A_OK) { + ret = -EIO; + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + } + break; + } + case AR6000_IOCTL_WMI_SET_LINKTHRESHOLD: + { + ret = ar6k_ioctl_set_link_threshold(dev, rq); + break; + } + case AR6000_IOCTL_WMI_SET_CHANNELPARAMS: + { + ret = ar6k_ioctl_set_channelParams(dev, rq); + break; + } + case AR6000_IOCTL_WMI_SET_PROBEDSSID: + { + ret = ar6k_ioctl_set_probedSsid(dev, rq); + break; + } + case AR6000_IOCTL_WMI_SET_BADAP: + { + ret = ar6k_ioctl_set_badAp(dev, rq); + break; + } + case AR6000_IOCTL_WMI_CREATE_QOS: + { + ret = ar6k_ioctl_create_qos(dev, rq); + break; + } + case AR6000_IOCTL_WMI_DELETE_QOS: + { + ret = ar6k_ioctl_delete_qos(dev, rq); + break; + } + case AR6000_IOCTL_WMI_GET_QOS_QUEUE: + { + ret = ar6k_ioctl_get_qos_queue(dev, rq); + break; + } + case AR6000_IOCTL_WMI_GET_TARGET_STATS: + { + ret = ar6k_ioctl_get_target_stats(dev, rq); + break; + } + case AR6000_IOCTL_WMI_SET_ERROR_REPORT_BITMASK: + { + ret = ar6k_ioctl_set_error_report_bitmask(dev, rq); + break; + } + case AR6000_IOCTL_WMI_SET_ASSOC_INFO: + { + WMI_SET_ASSOC_INFO_CMD cmd; + u8 assocInfo[WMI_MAX_ASSOC_INFO_LEN]; + + if (ar->arWmiReady == FALSE) { + ret = -EIO; + } else { + get_user(cmd.ieType, userdata); + if (cmd.ieType >= WMI_MAX_ASSOC_INFO_TYPE) { + ret = -EIO; + } else { + get_user(cmd.bufferSize, userdata + 1); + if (cmd.bufferSize > WMI_MAX_ASSOC_INFO_LEN) { + ret = -EFAULT; + break; + } + if (copy_from_user(assocInfo, userdata + 2, + cmd.bufferSize)) { + ret = -EFAULT; + } else { + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (wmi_associnfo_cmd(ar->arWmi, cmd.ieType, + cmd.bufferSize, + assocInfo) != A_OK) { + ret = -EIO; + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + } + } + } + break; + } + case AR6000_IOCTL_WMI_SET_ACCESS_PARAMS: + { + ret = ar6k_ioctl_set_access_params(dev, rq); + break; + } + case AR6000_IOCTL_WMI_SET_DISC_TIMEOUT: + { + ret = ar6k_ioctl_set_disconnect_timeout(dev, rq); + break; + } +#if CONFIG_HOST_DSET_SUPPORT + case AR6000_XIOCTL_WMI_DSET_WAIT_REQ: + { + ar6k_ioctl_wait_dset_req(dev, rq); + break; + } + case AR6000_XIOCTL_WMI_DSET_OPEN_REPLY: + { + AR6000_SPIN_LOCK(&ar->arLock, 0); + ar6k_ioctl_dset_open_reply(dev, rq); + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + break; + } + case AR6000_XIOCTL_WMI_DSET_DATA_REPLY: + { + AR6000_SPIN_LOCK(&ar->arLock, 0); + ar6k_ioctl_dset_data_reply(dev, rq); + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + break; + } +#endif /* CONFIG_HOST_DSET_SUPPORT */ + case AR6000_XIOCTL_FORCE_TARGET_RESET: + { + if (htcTarget) { +// HTCForceReset(htcTarget); + } else { + AR_DEBUG_PRINTF("ar6k_ioctl cannot attempt reset.\n"); + } + break; + } + case AR6000_XIOCTL_CHECK_TARGET_READY: + { + /* If we made it to here, then the Target exists and is ready. + */ + break; + } +#if CONFIG_HOST_GPIO_SUPPORT + case AR6000_XIOCTL_GPIO_OUTPUT_SET: + { + struct ar6k_gpio_output_set_cmd_s gpio_output_set_cmd; + + if (ar->arWmiReady == FALSE) { + return -EIO; + } + if (down_interruptible(&ar->arSem)) { + return -ERESTARTSYS; + } + + if (copy_from_user(&gpio_output_set_cmd, userdata, + sizeof(gpio_output_set_cmd))) { + ret = -EFAULT; + } else { + ret = ar6k_gpio_output_set(dev, + gpio_output_set_cmd.set_mask, + gpio_output_set_cmd. + clear_mask, + gpio_output_set_cmd. + enable_mask, + gpio_output_set_cmd. + disable_mask); + if (ret != A_OK) { + ret = EIO; + } + } + up(&ar->arSem); + break; + } + case AR6000_XIOCTL_GPIO_INPUT_GET: + { + if (ar->arWmiReady == FALSE) { + return -EIO; + } + if (down_interruptible(&ar->arSem)) { + return -ERESTARTSYS; + } + + ret = ar6k_gpio_input_get(dev); + if (ret != A_OK) { + up(&ar->arSem); + return -EIO; + } + + /* Wait for Target to respond. */ + wait_event_interruptible(arEvent, gpio_data_available); + if (signal_pending(current)) { + ret = -EINTR; + } else { + A_ASSERT(gpio_reg_results.gpioreg_id == GPIO_ID_NONE); + + if (copy_to_user(userdata, &gpio_reg_results.value, + sizeof(gpio_reg_results.value))) { + ret = -EFAULT; + } + } + up(&ar->arSem); + break; + } + case AR6000_XIOCTL_GPIO_REGISTER_SET: + { + struct ar6k_gpio_register_cmd_s gpio_register_cmd; + + if (ar->arWmiReady == FALSE) { + return -EIO; + } + if (down_interruptible(&ar->arSem)) { + return -ERESTARTSYS; + } + + if (copy_from_user(&gpio_register_cmd, userdata, + sizeof(gpio_register_cmd))) { + ret = -EFAULT; + } else { + ret = ar6k_gpio_register_set(dev, + gpio_register_cmd. + gpioreg_id, + gpio_register_cmd.value); + if (ret != A_OK) { + ret = EIO; + } + + /* Wait for acknowledgement from Target */ + wait_event_interruptible(arEvent, gpio_ack_received); + if (signal_pending(current)) { + ret = -EINTR; + } + } + up(&ar->arSem); + break; + } + case AR6000_XIOCTL_GPIO_REGISTER_GET: + { + struct ar6k_gpio_register_cmd_s gpio_register_cmd; + + if (ar->arWmiReady == FALSE) { + return -EIO; + } + if (down_interruptible(&ar->arSem)) { + return -ERESTARTSYS; + } + + if (copy_from_user(&gpio_register_cmd, userdata, + sizeof(gpio_register_cmd))) { + ret = -EFAULT; + } else { + ret = + ar6k_gpio_register_get(dev, + gpio_register_cmd.gpioreg_id); + if (ret != A_OK) { + up(&ar->arSem); + return -EIO; + } + + /* Wait for Target to respond. */ + wait_event_interruptible(arEvent, gpio_data_available); + if (signal_pending(current)) { + ret = -EINTR; + } else { + A_ASSERT(gpio_register_cmd.gpioreg_id == + gpio_reg_results.gpioreg_id); + if (copy_to_user + (userdata, &gpio_reg_results, + sizeof(gpio_reg_results))) { + ret = -EFAULT; + } + } + } + up(&ar->arSem); + break; + } + case AR6000_XIOCTL_GPIO_INTR_ACK: + { + struct ar6k_gpio_intr_ack_cmd_s gpio_intr_ack_cmd; + + if (ar->arWmiReady == FALSE) { + return -EIO; + } + if (down_interruptible(&ar->arSem)) { + return -ERESTARTSYS; + } + + if (copy_from_user(&gpio_intr_ack_cmd, userdata, + sizeof(gpio_intr_ack_cmd))) { + ret = -EFAULT; + } else { + ret = + ar6k_gpio_intr_ack(dev, gpio_intr_ack_cmd.ack_mask); + if (ret != A_OK) { + ret = EIO; + } + } + up(&ar->arSem); + break; + } + case AR6000_XIOCTL_GPIO_INTR_WAIT: + { + /* Wait for Target to report an interrupt. */ + dev_hold(dev); + rtnl_unlock(); + wait_event_interruptible(arEvent, gpio_intr_available); + rtnl_lock(); + dev_put(dev); + + if (signal_pending(current)) { + ret = -EINTR; + } else { + if (copy_to_user(userdata, &gpio_intr_results, + sizeof(gpio_intr_results))) { + ret = -EFAULT; + } + } + break; + } +#endif /* CONFIG_HOST_GPIO_SUPPORT */ + + case AR6000_XIOCTL_SET_ADHOC_BSSID: + { + WMI_SET_ADHOC_BSSID_CMD adhocBssid; + + if (ar->arWmiReady == FALSE) { + ret = -EIO; + } else if (copy_from_user(&adhocBssid, userdata, + sizeof(adhocBssid))) { + ret = -EFAULT; + } else if (A_MEMCMP(adhocBssid.bssid, bcast_mac, + AR6000_ETH_ADDR_LEN) == 0) { + ret = -EFAULT; + } else { + + A_MEMCPY(ar->arReqBssid, adhocBssid.bssid, + sizeof(ar->arReqBssid)); + } + break; + } + + case AR6000_XIOCTL_SET_OPT_MODE: + { + WMI_SET_OPT_MODE_CMD optModeCmd; + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + + if (ar->arWmiReady == FALSE) { + ret = -EIO; + } else if (copy_from_user(&optModeCmd, userdata, + sizeof(optModeCmd))) { + ret = -EFAULT; + } else if (ar->arConnected && optModeCmd.optMode == SPECIAL_ON) { + ret = -EFAULT; + + } else if (wmi_set_opt_mode_cmd(ar->arWmi, optModeCmd.optMode) + != A_OK) { + ret = -EIO; + } + break; + } + + case AR6000_XIOCTL_OPT_SEND_FRAME: + { + WMI_OPT_TX_FRAME_CMD optTxFrmCmd; + u8 data[MAX_OPT_DATA_LEN]; + + if (ar->arWmiReady == FALSE) { + ret = -EIO; + } else if (copy_from_user(&optTxFrmCmd, userdata, + sizeof(optTxFrmCmd))) { + ret = -EFAULT; + } else if (copy_from_user(data, + optTxFrmCmd.optIEData, + optTxFrmCmd.optIEDataLen)) { + ret = -EFAULT; + } else { + ret = wmi_opt_tx_frame_cmd(ar->arWmi, + optTxFrmCmd.frmType, + optTxFrmCmd.dstAddr, + optTxFrmCmd.bssid, + optTxFrmCmd.optIEDataLen, data); + } + + break; + } + + case AR6000_XIOCTL_SET_ADHOC_BEACON_INTVAL: + { + WMI_BEACON_INT_CMD bIntvlCmd; + + if (ar->arWmiReady == FALSE) { + ret = -EIO; + } else if (copy_from_user(&bIntvlCmd, userdata, + sizeof(bIntvlCmd))) { + ret = -EFAULT; + } else + if (wmi_set_adhoc_bconIntvl_cmd + (ar->arWmi, bIntvlCmd.beaconInterval) + != A_OK) { + ret = -EIO; + } + break; + } + case IEEE80211_IOCTL_SETAUTHALG: + { + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + struct ieee80211req_authalg req; + + if (ar->arWmiReady == FALSE) { + ret = -EIO; + } else if (copy_from_user(&req, userdata, + sizeof(struct ieee80211req_authalg))) + { + ret = -EFAULT; + } else if (req.auth_alg == AUTH_ALG_OPEN_SYSTEM) { + ar->arDot11AuthMode = OPEN_AUTH; + ar->arPairwiseCrypto = NONE_CRYPT; + ar->arGroupCrypto = NONE_CRYPT; + } else if (req.auth_alg == AUTH_ALG_LEAP) { + ar->arDot11AuthMode = LEAP_AUTH; + } else { + ret = -EIO; + } + break; + } + + case AR6000_XIOCTL_SET_VOICE_PKT_SIZE: + ret = ar6k_xioctl_set_voice_pkt_size(dev, userdata); + break; + + case AR6000_XIOCTL_SET_MAX_SP: + ret = ar6k_xioctl_set_max_sp_len(dev, userdata); + break; + + case AR6000_XIOCTL_WMI_GET_ROAM_TBL: + ret = ar6k_ioctl_get_roam_tbl(dev, rq); + break; + case AR6000_XIOCTL_WMI_SET_ROAM_CTRL: + ret = ar6k_ioctl_set_roam_ctrl(dev, userdata); + break; + case AR6000_XIOCTRL_WMI_SET_POWERSAVE_TIMERS: + ret = ar6k_ioctl_set_powersave_timers(dev, userdata); + break; + case AR6000_XIOCTRL_WMI_GET_POWER_MODE: + ret = ar6k_ioctl_get_power_mode(dev, rq); + break; + case AR6000_XIOCTRL_WMI_SET_WLAN_STATE: + get_user(ar->arWlanState, (unsigned int *) userdata); + if (ar->arWmiReady == FALSE) { + ret = -EIO; + break; + } + + if (ar->arWlanState == WLAN_ENABLED) { + /* Enable foreground scanning */ + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (wmi_scanparams_cmd(ar->arWmi, scParams.fg_start_period, + scParams.fg_end_period, + scParams.bg_period, + scParams.act_chdwell_time, + scParams.pas_chdwell_time, + scParams.shortScanRatio) != A_OK) { + ret = -EIO; + } + if (ar->arSsidLen) { + if (wmi_connect_cmd(ar->arWmi, ar->arNetworkType, + ar->arDot11AuthMode, ar->arAuthMode, + ar->arPairwiseCrypto, + ar->arPairwiseCryptoLen, + ar->arGroupCrypto, + ar->arGroupCryptoLen, ar->arSsidLen, + ar->arSsid, ar->arReqBssid, + ar->arChannelHint) != A_OK) { + ret = -EIO; + } else { + ar->arConnectPending = TRUE; + } + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + } else { + /* Disconnect from the AP and disable foreground scanning */ + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (ar->arConnected == TRUE || ar->arConnectPending == TRUE) { + wmi_disconnect_cmd(ar->arWmi); + } + if (wmi_scanparams_cmd(ar->arWmi, 65535, 0, 0, 0, 0, 0) != + A_OK) { + ret = -EIO; + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + } + break; + case AR6000_XIOCTL_WMI_GET_ROAM_DATA: + ret = ar6k_ioctl_get_roam_data(dev, rq); + break; + default: + ret = -EOPNOTSUPP; + } + return ret; +} + +void ar6k_bitrate_rx(void *devt, s32 rateKbps) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) devt; + + ar->arBitRate = rateKbps; + wake_up(&arEvent); +} + +void ar6k_txPwr_rx(void *devt, u8 txPwr) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) devt; + + ar->arTxPwr = txPwr; + wake_up(&arEvent); +} + +static int +ar6k_ioctl_set_channelParams(struct net_device *dev, struct ifreq *rq) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + WMI_CHANNEL_PARAMS_CMD cmd, *cmdp; + int ret = 0; + + if (ar->arWmiReady == FALSE) { + return -EIO; + } + + + if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) { + return -EFAULT; + } + + if (cmd.numChannels > 1) { + cmdp = A_MALLOC(128); + if (copy_from_user(cmdp, rq->ifr_data, + sizeof(*cmdp) + + ((cmd.numChannels - 1) * sizeof(u16)))) { + kfree(cmdp); + return -EFAULT; + } + } else { + cmdp = &cmd; + } + + if ((ar->arPhyCapability == WMI_11G_CAPABILITY) && + ((cmdp->phyMode == WMI_11A_MODE) + || (cmdp->phyMode == WMI_11AG_MODE))) { + ret = -EINVAL; + } + + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (!ret && + (wmi_set_channelParams_cmd + (ar->arWmi, cmdp->phyMode, cmdp->numChannels, + cmdp->channelList) != A_OK)) { + ret = -EIO; + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + + if (cmd.numChannels > 1) { + kfree(cmdp); + } + + return ret; +} + +static int +ar6k_ioctl_set_link_threshold(struct net_device *dev, struct ifreq *rq) +{ + + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + WMI_RSSI_THRESHOLD_PARAMS_CMD cmd; + int ret = 0; + + if (ar->arWmiReady == FALSE) { + return -EIO; + } + + if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) { + return -EFAULT; + } + + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (wmi_set_link_threshold_params(ar->arWmi, + cmd.highThreshold_upperVal, + cmd.highThreshold_lowerVal, + cmd.lowThreshold_upperVal, + cmd.lowThreshold_lowerVal, + cmd.pollTime) != A_OK) { + ret = -EIO; + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + + return ret; +} + +void ar6k_channelList_rx(void *devt, char numChan, u16 * chanList) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) devt; + + A_MEMCPY(ar->arChannelList, chanList, numChan * sizeof(u16)); + ar->arNumChannels = numChan; + + wake_up(&arEvent); +} + +static int +ar6k_ioctl_set_probedSsid(struct net_device *dev, struct ifreq *rq) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + WMI_PROBED_SSID_CMD cmd; + int ret = 0; + + if (ar->arWmiReady == FALSE) { + return -EIO; + } + + if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) { + return -EFAULT; + } + + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (wmi_probedSsid_cmd + (ar->arWmi, cmd.entryIndex, cmd.flag, cmd.ssidLength, + cmd.ssid) != A_OK) { + ret = -EIO; + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + + return ret; +} + +static int ar6k_ioctl_set_badAp(struct net_device *dev, struct ifreq *rq) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + WMI_ADD_BAD_AP_CMD cmd; + int ret = 0; + + if (ar->arWmiReady == FALSE) { + return -EIO; + } + + + if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) { + return -EFAULT; + } + + if (cmd.badApIndex > WMI_MAX_BAD_AP_INDEX) { + return -EIO; + } + + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (A_MEMCMP(cmd.bssid, null_mac, AR6000_ETH_ADDR_LEN) == 0) { + /* + * This is a delete badAP. + */ + if (wmi_deleteBadAp_cmd(ar->arWmi, cmd.badApIndex) != A_OK) { + ret = -EIO; + } + } else { + if (wmi_addBadAp_cmd(ar->arWmi, cmd.badApIndex, cmd.bssid) != A_OK) { + ret = -EIO; + } + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + + return ret; +} + +static int +ar6k_ioctl_create_qos(struct net_device *dev, struct ifreq *rq) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + WMI_CREATE_PSTREAM_CMD cmd; + int ret; + + if (ar->arWmiReady == FALSE) { + return -EIO; + } + + + if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) { + return -EFAULT; + } + + AR6000_SPIN_LOCK(&ar->arLock, 0); + cmd.rxQueueNum = 0xFF; + ret = wmi_verify_tspec_params(&cmd, tspecCompliance); + if (ret == A_OK) + ret = wmi_create_pstream_cmd(ar->arWmi, &cmd); + + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + switch (ret) { + case A_OK: + return 0; + case A_EBUSY: + return -EBUSY; + case A_NO_MEMORY: + return -ENOMEM; + case A_EINVAL: + default: + return -EFAULT; + } +} + +static int +ar6k_ioctl_delete_qos(struct net_device *dev, struct ifreq *rq) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + WMI_DELETE_PSTREAM_CMD cmd; + int ret = 0; + + if (ar->arWmiReady == FALSE) { + return -EIO; + } + + + if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) { + return -EFAULT; + } + + AR6000_SPIN_LOCK(&ar->arLock, 0); + ret = wmi_delete_pstream_cmd(ar->arWmi, cmd.txQueueNumber, + cmd.rxQueueNumber, cmd.trafficDirection); + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + switch (ret) { + case A_OK: + return 0; + case A_EBUSY: + return -EBUSY; + case A_NO_MEMORY: + return -ENOMEM; + case A_EINVAL: + default: + return -EFAULT; + } +} + +static int +ar6k_ioctl_get_qos_queue(struct net_device *dev, struct ifreq *rq) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + struct ar6k_queuereq qreq; + int ret = 0; + + if (ar->arWmiReady == FALSE) { + return -EIO; + } + + if (copy_from_user(&qreq, rq->ifr_data, sizeof(struct ar6k_queuereq))) { + return -EFAULT; + } + + qreq.queueNumber = wmi_get_mapped_qos_queue(ar->arWmi, + qreq.trafficDirection, + qreq.trafficClass); + + if (copy_to_user(rq->ifr_data, &qreq, sizeof(struct ar6k_queuereq))) { + ret = -EFAULT; + } + return ret; +} + +#ifdef HTC_RAW_INTERFACE +#define RAW_HTC_READ_BUFFERS_NUM 16 +#define RAW_HTC_WRITE_BUFFERS_NUM 16 +typedef struct { + int currPtr; + int length; + unsigned char data[AR6000_BUFFER_SIZE]; +} raw_htc_buffer; + +static struct semaphore raw_htc_read_sem[HTC_MAILBOX_NUM_MAX]; +static struct semaphore raw_htc_write_sem[HTC_MAILBOX_NUM_MAX]; +static wait_queue_head_t raw_htc_read_queue[HTC_MAILBOX_NUM_MAX]; +static wait_queue_head_t raw_htc_write_queue[HTC_MAILBOX_NUM_MAX]; +static raw_htc_buffer + raw_htc_read_buffer[HTC_MAILBOX_NUM_MAX][RAW_HTC_READ_BUFFERS_NUM]; +static raw_htc_buffer + raw_htc_write_buffer[HTC_MAILBOX_NUM_MAX][RAW_HTC_WRITE_BUFFERS_NUM]; +static u8 write_buffer_available[HTC_MAILBOX_NUM_MAX]; +static u8 read_buffer_available[HTC_MAILBOX_NUM_MAX]; + +static void +ar6k_htc_raw_read_cb(HTC_TARGET * htcTarget, HTC_ENDPOINT_ID endPointId, + HTC_EVENT_ID evId, HTC_EVENT_INFO * evInfo, + void *arg) +{ + HTC_TARGET *target; + raw_htc_buffer *busy; + + target = (HTC_TARGET *) arg; + A_ASSERT(target != NULL); + busy = (raw_htc_buffer *) evInfo->cookie; + A_ASSERT(busy != NULL); + + if (evInfo->status == A_ECANCELED) { + /* + * HTC provides A_ECANCELED status when it doesn't want to be refilled + * (probably due to a shutdown) + */ + memset(busy, 0, sizeof(raw_htc_buffer)); + return; + } +#ifdef CF + if (down_trylock(&raw_htc_read_sem[endPointId])) { +#else + if (down_interruptible(&raw_htc_read_sem[endPointId])) { +#endif /* CF */ + AR_DEBUG2_PRINTF("Unable to down the semaphore\n"); + } + + A_ASSERT(evId == HTC_BUFFER_RECEIVED); + A_ASSERT((evInfo->status != A_OK) || + (evInfo->buffer == (busy->data + HTC_HEADER_LEN))); + + busy->length = evInfo->actualLength + HTC_HEADER_LEN; + busy->currPtr = HTC_HEADER_LEN; + read_buffer_available[endPointId] = TRUE; + up(&raw_htc_read_sem[endPointId]); + + /* Signal the waiting process */ + AR_DEBUG2_PRINTF("Waking up the endpoint(%d) read process\n", + endPointId); + wake_up_interruptible(&raw_htc_read_queue[endPointId]); +} + +static void +ar6k_htc_raw_write_cb(HTC_TARGET * htcTarget, HTC_ENDPOINT_ID endPointId, + HTC_EVENT_ID evId, HTC_EVENT_INFO * evInfo, + void *arg) +{ + HTC_TARGET *target; + raw_htc_buffer *free; + + target = (HTC_TARGET *) arg; + A_ASSERT(target != NULL); + free = (raw_htc_buffer *) evInfo->cookie; + A_ASSERT(free != NULL); + + if (evInfo->status == A_ECANCELED) { + /* + * HTC provides A_ECANCELED status when it doesn't want to be refilled + * (probably due to a shutdown) + */ + memset(free, 0, sizeof(raw_htc_buffer)); + return; + } +#ifdef CF + if (down_trylock(&raw_htc_write_sem[endPointId])) { +#else + if (down_interruptible(&raw_htc_write_sem[endPointId])) { +#endif /* CF */ + AR_DEBUG2_PRINTF("Unable to down the semaphore\n"); + } + + A_ASSERT(evId == HTC_BUFFER_SENT); + A_ASSERT(evInfo->buffer == (free->data + HTC_HEADER_LEN)); + + free->length = 0; + write_buffer_available[endPointId] = TRUE; + up(&raw_htc_write_sem[endPointId]); + + /* Signal the waiting process */ + AR_DEBUG2_PRINTF("Waking up the endpoint(%d) write process\n", + endPointId); + wake_up_interruptible(&raw_htc_write_queue[endPointId]); +} + +static void +ar6k_htc_raw_unread_cb(HTC_TARGET * htcTarget, HTC_ENDPOINT_ID eid, + HTC_EVENT_ID evId, HTC_EVENT_INFO * evInfo, + void *arg) +{ + HTC_TARGET *target; + + target = (HTC_TARGET *) arg; + A_ASSERT(target != NULL); + + AR_DEBUG_PRINTF("Not implemented\n"); +} + +static int ar6k_htc_raw_open(HTC_TARGET * htcTarget) +{ + int status; + int count1, count2; + raw_htc_buffer *buffer; + + for (count1 = 0; count1 < HTC_MAILBOX_NUM_MAX; count1++) { + /* Initialize the data structures */ + init_MUTEX(&raw_htc_read_sem[count1]); + init_MUTEX(&raw_htc_write_sem[count1]); + init_waitqueue_head(&raw_htc_read_queue[count1]); + init_waitqueue_head(&raw_htc_write_queue[count1]); + + /* Register the event handlers */ + if ((status = HTCEventReg(htcTarget, count1, HTC_BUFFER_RECEIVED, + ar6k_htc_raw_read_cb, + htcTarget)) != A_OK) { + BMIInit(); + return -EIO; + } + if ((status = HTCEventReg(htcTarget, count1, HTC_DATA_AVAILABLE, + ar6k_htc_raw_unread_cb, + htcTarget)) != A_OK) { + BMIInit(); + return -EIO; + } + if ((status = HTCEventReg(htcTarget, count1, HTC_BUFFER_SENT, + ar6k_htc_raw_write_cb, + htcTarget)) != A_OK) { + BMIInit(); + return -EIO; + } + + for (count2 = 0; count2 < RAW_HTC_READ_BUFFERS_NUM; count2++) { + /* Initialize the receive buffers */ + buffer = &raw_htc_write_buffer[count1][count2]; + memset(buffer, 0, sizeof(raw_htc_buffer)); + buffer = &raw_htc_read_buffer[count1][count2]; + memset(buffer, 0, sizeof(raw_htc_buffer)); + + /* Queue buffers to HTC for receive */ + if ((status = HTCBufferReceive(htcTarget, count1, buffer->data, + AR6000_BUFFER_SIZE, + buffer)) != A_OK) { + BMIInit(); + return -EIO; + } + } + + for (count2 = 0; count2 < RAW_HTC_WRITE_BUFFERS_NUM; count2++) { + /* Initialize the receive buffers */ + buffer = &raw_htc_write_buffer[count1][count2]; + memset(buffer, 0, sizeof(raw_htc_buffer)); + } + + read_buffer_available[count1] = FALSE; + write_buffer_available[count1] = TRUE; + } + + /* Start the HTC component */ + if ((status = HTCStart(htcTarget)) != A_OK) { + BMIInit(); + return -EIO; + } + + return 0; +} + +static int ar6k_htc_raw_close(HTC_TARGET * htcTarget) +{ + int count; + int status; + + /* Stop the HTC */ + HTCStop(htcTarget); + + /* Unregister the event handlers */ + for (count = 0; count < HTC_MAILBOX_NUM_MAX; count++) { + status = HTCEventReg(htcTarget, count, HTC_BUFFER_RECEIVED, + NULL, htcTarget); + + status = HTCEventReg(htcTarget, count, HTC_DATA_AVAILABLE, + NULL, htcTarget); + + status = HTCEventReg(htcTarget, count, HTC_BUFFER_SENT, + NULL, htcTarget); + } + + /* Initialize the BMI component */ + BMIInit(); + + return 0; +} + +raw_htc_buffer *get_filled_buffer(HTC_ENDPOINT_ID endPointId) +{ + int count; + raw_htc_buffer *busy; + + /* Check for data */ + for (count = 0; count < RAW_HTC_READ_BUFFERS_NUM; count++) { + busy = &raw_htc_read_buffer[endPointId][count]; + if (busy->length) { + break; + } + } + if (busy->length) { + read_buffer_available[endPointId] = TRUE; + } else { + read_buffer_available[endPointId] = FALSE; + } + + return busy; +} + +static ssize_t +ar6k_htc_raw_read(HTC_TARGET * htcTarget, HTC_ENDPOINT_ID endPointId, + char __user * buffer, size_t length) +{ + int readPtr; + raw_htc_buffer *busy; + + if (down_interruptible(&raw_htc_read_sem[endPointId])) { + return -ERESTARTSYS; + } + + busy = get_filled_buffer(endPointId); + while (!read_buffer_available[endPointId]) { + up(&raw_htc_read_sem[endPointId]); + + /* Wait for the data */ + AR_DEBUG2_PRINTF("Sleeping endpoint(%d) read process\n", + endPointId); + if (wait_event_interruptible + (raw_htc_read_queue[endPointId], + read_buffer_available[endPointId])) { + return -EINTR; + } + if (down_interruptible(&raw_htc_read_sem[endPointId])) { + return -ERESTARTSYS; + } + busy = get_filled_buffer(endPointId); + } + + /* Read the data */ + readPtr = busy->currPtr; + if (length > busy->length - HTC_HEADER_LEN) { + length = busy->length - HTC_HEADER_LEN; + } + if (copy_to_user(buffer, &busy->data[readPtr], length)) { + up(&raw_htc_read_sem[endPointId]); + return -EFAULT; + } + + busy->currPtr += length; + if (busy->currPtr == busy->length) { + /* Packet has been completely read. Queue it with HTC */ + memset(busy, 0, sizeof(raw_htc_buffer)); + HTCBufferReceive(htcTarget, endPointId, busy->data, + AR6000_BUFFER_SIZE, busy); + } + read_buffer_available[endPointId] = FALSE; + up(&raw_htc_read_sem[endPointId]); + + return length; +} + +raw_htc_buffer *get_free_buffer(HTC_ENDPOINT_ID endPointId) +{ + int count; + raw_htc_buffer *free; + + free = NULL; + for (count = 0; count < RAW_HTC_WRITE_BUFFERS_NUM; count++) { + free = &raw_htc_write_buffer[endPointId][count]; + if (free->length == 0) { + break; + } + } + if (!free->length) { + write_buffer_available[endPointId] = TRUE; + } else { + write_buffer_available[endPointId] = FALSE; + } + + return free; +} + +static ssize_t +ar6k_htc_raw_write(HTC_TARGET * htcTarget, HTC_ENDPOINT_ID endPointId, + char __user * buffer, size_t length) +{ + int writePtr; + raw_htc_buffer *free; + + if (down_interruptible(&raw_htc_write_sem[endPointId])) { + return -ERESTARTSYS; + } + + /* Search for a free buffer */ + free = get_free_buffer(endPointId); + + /* Check if there is space to write else wait */ + while (!write_buffer_available[endPointId]) { + up(&raw_htc_write_sem[endPointId]); + + /* Wait for buffer to become free */ + AR_DEBUG2_PRINTF("Sleeping endpoint(%d) write process\n", + endPointId); + if (wait_event_interruptible + (raw_htc_write_queue[endPointId], + write_buffer_available[endPointId])) { + return -EINTR; + } + if (down_interruptible(&raw_htc_write_sem[endPointId])) { + return -ERESTARTSYS; + } + free = get_free_buffer(endPointId); + } + + /* Send the data */ + writePtr = HTC_HEADER_LEN; + if (length > (AR6000_BUFFER_SIZE - HTC_HEADER_LEN)) { + length = AR6000_BUFFER_SIZE - HTC_HEADER_LEN; + } + + if (copy_from_user(&free->data[writePtr], buffer, length)) { + up(&raw_htc_read_sem[endPointId]); + return -EFAULT; + } + + free->length = length; + HTCBufferSend(htcTarget, endPointId, &free->data[writePtr], length, + free); + write_buffer_available[endPointId] = FALSE; + up(&raw_htc_write_sem[endPointId]); + + return length; +} +#endif /* HTC_RAW_INTERFACE */ + +/* + * As soon as the DataSet Server application waits for a request, + * we know that the server is operational, and we try to process + * requests. If the Target sends any requests before the server + * has started, we reject the request. The model we use is that + * once started, the server must continue to service requests as + * long as they may arrive, and the server ought to be started + * before WMI is started (before the network interface is brought + * up. + */ +static u8 dset_server_alive; + +#if CONFIG_HOST_DSET_SUPPORT +/* + * For now, we allow for just one outstanding (unhandled) request + * at a time. The current Target doesn't need any more than that. + * If we ever need to handle many simultaneous requests, this + * pending_dset_request and pending_dset_request_valid mechanism + * can get more complicated. The use of dset_request_lock is + * also very simple; it assumes that a single well-behaved thread + * serves all requests. + */ +static u8 pending_dset_request_valid; +static dset_request_t pending_dset_request; + +static spinlock_t dset_request_lock; +static DECLARE_WAIT_QUEUE_HEAD(dset_request); + +static int +ar6k_ioctl_wait_dset_req(struct net_device *dev, struct ifreq *rq) +{ + int ret = 0; + + dset_server_alive = TRUE; + + /* Wait for a DataSet Request to arrive. */ + dev_hold(dev); + rtnl_unlock(); + wait_event_interruptible(dset_request, pending_dset_request_valid); + rtnl_lock(); + dev_put(dev); + + if (signal_pending(current)) { + ret = -EINTR; + } else { + if (copy_to_user(rq->ifr_data, + &pending_dset_request, + sizeof(pending_dset_request))) { + ret = -EFAULT; + } + + /* pending_dset_request is now available for use */ + pending_dset_request_valid = FALSE; + spin_unlock(&dset_request_lock); + } + + return ret; +} + +static int +ar6k_ioctl_dset_open_reply(struct net_device *dev, struct ifreq *rq) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + int ret = 0; + dset_open_reply_t dset_reply; + + if (copy_from_user(&dset_reply, rq->ifr_data, sizeof(dset_reply))) { + ret = -EFAULT; + } else { + A_ASSERT(dset_reply.cmd == AR6000_XIOCTL_WMI_DSET_OPEN_REPLY); + ret = wmi_dset_open_reply(ar->arWmi, + dset_reply.status, + dset_reply.access_cookie, + dset_reply.size, + dset_reply.version, + dset_reply.targ_handle, + dset_reply.targ_reply_fn, + dset_reply.targ_reply_arg); + + if (ret == A_NO_MEMORY) { + ret = -ENOMEM; + } else if (ret == A_ERROR) { + ret = -EFAULT; + } + } + + return ret; +} + +static int +ar6k_ioctl_dset_data_reply(struct net_device *dev, struct ifreq *rq) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + int ret = 0; + dset_data_reply_t dset_reply; + + if (copy_from_user(&dset_reply, rq->ifr_data, sizeof(dset_reply))) { + ret = -EFAULT; + } else { + A_ASSERT(dset_reply.cmd == AR6000_XIOCTL_WMI_DSET_DATA_REPLY); + ret = wmi_dset_data_reply(ar->arWmi, + dset_reply.status, + dset_reply.buf, + dset_reply.length, + dset_reply.targ_buf, + dset_reply.targ_reply_fn, + dset_reply.targ_reply_arg); + + if (ret == A_NO_MEMORY) { + ret = -ENOMEM; + } else if (ret == A_ERROR) { + ret = -EFAULT; + } + } + + return ret; +} +#endif /* CONFIG_HOST_DSET_SUPPORT */ + +u8 ar6k_ibss_map_epid(struct sk_buff * skb, struct net_device * dev, + u32 * mapNo) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + u8 *datap; + ATH_MAC_HDR *macHdr; + u32 i, emptMap; + + (*mapNo) = 0; + datap = skb->data; + macHdr = (ATH_MAC_HDR *) (datap + sizeof(WMI_DATA_HDR)); + if (IEEE80211_IS_MULTICAST(macHdr->dstMac)) { + return ENDPOINT3; + } + + emptMap = -1; + for (i = 0; i < ar->arNodeNum; i++) { + if (IEEE80211_ADDR_EQ(macHdr->dstMac, ar->arNodeMap[i].macAddress)) { + (*mapNo) = i + 1; + ar->arNodeMap[i].txPending++; + return ar->arNodeMap[i].epId; + } + + if ((emptMap == -1) && !ar->arNodeMap[i].txPending) { + emptMap = i; + } + } + + if (emptMap == -1) { + emptMap = ar->arNodeNum; + ar->arNodeNum++; + A_ASSERT(ar->arNodeNum <= MAX_NODE_NUM); + } + + A_MEMCPY(ar->arNodeMap[emptMap].macAddress, macHdr->dstMac, + IEEE80211_ADDR_LEN); + + for (i = ENDPOINT2; i <= ENDPOINT4; i++) { + if (!ar->arTxPending[i]) { + ar->arNodeMap[emptMap].epId = i; + break; + } + } + + if (i > ENDPOINT4) { + ar->arNodeMap[emptMap].epId = ar->arNexEpId; + ar->arNexEpId++; + if (ar->arNexEpId > ENDPOINT4) { + ar->arNexEpId = ENDPOINT2; + } + } + + (*mapNo) = emptMap + 1; + ar->arNodeMap[emptMap].txPending++; + + return ar->arNodeMap[ar->arNodeNum - 1].epId; +} + +static int ar6k_data_tx(struct sk_buff *skb, struct net_device *dev) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + HTC_ENDPOINT_ID endPointId; + u32 mapNo = 0; + struct iphdr *ipHdr; + struct sk_buff *newbuf; + int len; + struct ar_cookie *cookie; + + if (ar->arWmiReady == FALSE && bypasswmi == 0) { + dev_kfree_skb(skb); + return 0; + } +#ifdef BLOCK_TX_PATH_FLAG + if (blocktx) { + dev_kfree_skb(skb); + return 0; + } +#endif /* BLOCK_TX_PATH_FLAG */ + + AR6000_SPIN_LOCK(&ar->arLock, 0); + + /* If all data queues are full, notify upper layer to stop. */ + if (ar->arTotalTxDataPending >= + (txFlowCtrlThresh[ar->arNumDataEndPts])) { + netif_stop_queue(ar->arNetDev); + } + + AR_DEBUG2_PRINTF + ("ar6k_data_tx start - skb=0x%x, data=0x%x, len=0x%x\n", + (u32) skb, (u32) skb->data, skb->len); + + if (ar->arWmiEnabled == TRUE) { + if (skb_headroom(skb) < dev->hard_header_len) { + /* + * We really should have gotten enough headroom but sometimes + * we still get packets with not enough headroom. Copy the packet. + */ + len = skb->len; + newbuf = ar6k_alloc_skb(len); + if (newbuf == NULL) { + dev_kfree_skb(skb); + AR6000_STAT_INC(ar, tx_dropped); + AR6000_STAT_INC(ar, tx_aborted_errors); + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + return A_NO_MEMORY; + } + skb_put(newbuf, len); + A_MEMCPY(newbuf->data, skb->data, len); + dev_kfree_skb(skb); + skb = newbuf; + } + if (wmi_dix_2_dot3(ar->arWmi, skb) != A_OK) { + AR_DEBUG_PRINTF("ar6k_data_tx - wmi_dix_2_dot3 failed\n"); + AR6000_STAT_INC(ar, tx_dropped); + AR6000_STAT_INC(ar, tx_aborted_errors); + dev_kfree_skb(skb); + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + return 0; + } + + if (wmi_data_hdr_add(ar->arWmi, skb, DATA_MSGTYPE) != A_OK) { + AR_DEBUG_PRINTF("ar6k_data_tx - wmi_data_hdr_add failed\n"); + AR6000_STAT_INC(ar, tx_dropped); + AR6000_STAT_INC(ar, tx_aborted_errors); + dev_kfree_skb(skb); + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + return 0; + } + + if ((ar->arNetworkType == ADHOC_NETWORK) && + ar->arIbssPsEnable && ar->arConnected) { + endPointId = ar6k_ibss_map_epid(skb, dev, &mapNo); + } else { + /* Extract the end point information */ + endPointId = wmi_get_endpoint(ar->arWmi, + wmi_implicit_create_pstream(ar-> + arWmi, + skb, + UPLINK_TRAFFIC)); + } + } else { + /* + * the endpoint is directly based on the TOS field in the IP + * header + */ + ipHdr = (struct iphdr *) (skb->data + sizeof(ATH_MAC_HDR)); + endPointId = ((ipHdr->tos >> 1) & 0x03); + } + + AR_DEBUG2_PRINTF + ("ar6k_data_tx - ep=%d skb=0x%x, data=0x%x, len=0x%x\n", + endPointId, (u32) skb, (u32) skb->data, skb->len); + + ar->arTxPending[endPointId]++; + ar->arTotalTxDataPending++; + + /* If the particular data queue is full, silently drop the pkt. */ + if (ar->arTxPending[endPointId] > MAX_ALLOWED_TXQ_DEPTH) { + ar->arTxPending[endPointId]--; + ar->arTotalTxDataPending--; + dev_kfree_skb(skb); + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + return 0; + } + + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + + cookie = ar6k_alloc_cookie(ar); + if (cookie == NULL) { + AR6000_SPIN_LOCK(&ar->arLock, 0); + ar->arTxPending[endPointId]--; + ar->arTotalTxDataPending--; + dev_kfree_skb(skb); + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + return 0; + } + + cookie->arc_bp[0] = (u32) skb; + cookie->arc_bp[1] = mapNo; + + if (HTCBufferSend(ar->arHtcTarget, endPointId, skb->data, + skb->len, cookie) != A_OK) { + AR6000_SPIN_LOCK(&ar->arLock, 0); + ar->arTxPending[endPointId]--; + ar->arTotalTxDataPending--; + AR_DEBUG_PRINTF("Dropping the frame\n"); + ar6k_free_cookie(ar, cookie); + dev_kfree_skb(skb); + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + } + + return 0; +} + +static void +ar6k_tx_complete(HTC_TARGET * htcTarget, HTC_ENDPOINT_ID eid, + HTC_EVENT_ID evId, HTC_EVENT_INFO * evInfo, void *arg) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) arg; + void *cookie = (void *) evInfo->cookie; + struct sk_buff *skb = NULL; + u32 mapNo = 0; + + if (ar->arWmiReady == TRUE || bypasswmi != 0) { + struct ar_cookie *ar_cookie = (struct ar_cookie *) cookie; + skb = (struct sk_buff *) ar_cookie->arc_bp[0]; + mapNo = ar_cookie->arc_bp[1]; + } else { + skb = (struct sk_buff *) cookie; + AR_DEBUG_PRINTF("%s() WARNING Wierd cookie\n", __func__); + } + + A_ASSERT(skb); + A_ASSERT(evId == HTC_BUFFER_SENT); + A_ASSERT(evInfo->buffer == skb->data); + if (evInfo->status != A_ECANCELED) { + A_ASSERT(evInfo->actualLength == skb->len); + } + + AR6000_SPIN_LOCK(&ar->arLock, 0); + + ar->arTxPending[eid]--; + if (eid != WMI_CONTROL_MBOX || bypasswmi) { + ar->arTotalTxDataPending--; + } + AR_DEBUG2_PRINTF + ("ar6k_tx_complete skb=0x%x data=0x%x len=0x%x mbox=%d ", + (u32) skb, (u32) evInfo->buffer, evInfo->actualLength, eid); + + if ((eid == WMI_CONTROL_MBOX) && + (ar->arTxPending[WMI_CONTROL_MBOX] == 0)) { + wake_up(&arEvent); + } + + + if (evInfo->status != A_OK) { + AR_DEBUG_PRINTF("%s() -TX ERROR\n", __func__); + AR6000_STAT_INC(ar, tx_errors); + } else { + AR_DEBUG2_PRINTF("OK\n"); + AR6000_STAT_INC(ar, tx_packets); + ar->arNetStats.tx_bytes += skb->len; + } + + if ((ar->arNetworkType == ADHOC_NETWORK) && ar->arIbssPsEnable + && (eid != WMI_CONTROL_MBOX) && mapNo) { + mapNo--; + ar->arNodeMap[mapNo].txPending--; + + if (!ar->arNodeMap[mapNo].txPending + && (mapNo == (ar->arNodeNum - 1))) { + u32 i; + for (i = ar->arNodeNum; i > 0; i--) { + if (!ar->arNodeMap[i - 1].txPending) { + A_MEMZERO(&ar->arNodeMap[i - 1], + sizeof(struct ar_node_mapping)); + ar->arNodeNum--; + } else { + break; + } + } + } + } + + /* Freeing a cookie should not be contingent on either of */ + /* these flags, just if we have a cookie or not. */ + /* Can we even get here without a cookie? Fix later. */ + if (ar->arWmiReady == TRUE || (bypasswmi)) { + ar6k_free_cookie(ar, cookie); + } + + dev_kfree_skb(skb); + + if ((ar->arConnected == TRUE) || (bypasswmi)) { + netif_wake_queue(ar->arNetDev); + } + + AR6000_SPIN_UNLOCK(&ar->arLock, 0); +} + +/* + * Receive event handler. This is called by HTC when a packet is received + */ +int pktcount; +static void +ar6k_rx(HTC_TARGET * htcTarget, HTC_ENDPOINT_ID eid, + HTC_EVENT_ID evId, HTC_EVENT_INFO * evInfo, void *arg) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) arg; + struct sk_buff *skb = (struct sk_buff *) evInfo->cookie; + int minHdrLen; + + A_ASSERT(evId == HTC_BUFFER_RECEIVED); + A_ASSERT((evInfo->status != A_OK) + || (evInfo->buffer == (skb->data + HTC_HEADER_LEN))); + +#ifdef DEBUG + AR_DEBUG2_PRINTF + ("ar6k_rx ar=0x%x ep=%d, skb=0x%x, data=0x%x, len=0x%x ", + (u32) ar, eid, (u32) skb, (u32) evInfo->buffer, + evInfo->actualLength); + if (evInfo->status != A_OK) { + AR_DEBUG2_PRINTF("ERR\n"); + } else { + AR_DEBUG2_PRINTF("OK\n"); + } +#endif /* DEBUG */ + + ar->arRxBuffers[eid]--; + AR6000_STAT_INC(ar, rx_packets); + ar->arNetStats.rx_bytes += evInfo->actualLength; + + skb_put(skb, evInfo->actualLength + HTC_HEADER_LEN); + + skb_pull(skb, HTC_HEADER_LEN); + + if (evInfo->status != A_OK) { + AR6000_STAT_INC(ar, rx_errors); + dev_kfree_skb(skb); + } else if (ar->arWmiEnabled == TRUE) { + if (eid == WMI_CONTROL_MBOX) { + /* + * this is a wmi control msg + */ + AR6000_SPIN_LOCK(&ar->arLock, 0); + wmi_control_rx(ar->arWmi, skb); + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + } else { + WMI_DATA_HDR *dhdr = (WMI_DATA_HDR *) skb->data; + if (WMI_DATA_HDR_IS_MSG_TYPE(dhdr, CNTL_MSGTYPE)) { + /* + * this is a wmi control msg + */ + /* strip off WMI hdr */ + wmi_data_hdr_remove(ar->arWmi, skb); + AR6000_SPIN_LOCK(&ar->arLock, 0); + wmi_control_rx(ar->arWmi, skb); + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + } else { + /* + * this is a wmi data packet + */ + minHdrLen = sizeof(WMI_DATA_HDR) + sizeof(ATH_MAC_HDR) + + sizeof(ATH_LLC_SNAP_HDR); + + if ((evInfo->actualLength < minHdrLen) || + (evInfo->actualLength > AR6000_BUFFER_SIZE)) { + /* + * packet is too short or too long + */ + AR_DEBUG_PRINTF("TOO SHORT or TOO LONG\n"); + AR6000_STAT_INC(ar, rx_errors); + AR6000_STAT_INC(ar, rx_length_errors); + dev_kfree_skb(skb); + } else { + AR6000_SPIN_LOCK(&ar->arLock, 0); + wmi_implicit_create_pstream(ar->arWmi, skb, + DNLINK_TRAFFIC); + AR6000_SPIN_UNLOCK(&ar->arLock, 0); +#if 0 + /* Access RSSI values here */ + AR_DEBUG_PRINTF("RSSI %d\n", + ((WMI_DATA_HDR *) + skb->data)->rssi); +#endif + wmi_data_hdr_remove(ar->arWmi, skb); + wmi_dot3_2_dix(ar->arWmi, skb); + + if ((ar->arNetDev->flags & IFF_UP) == IFF_UP) { + skb->dev = ar->arNetDev; + skb->protocol = eth_type_trans(skb, ar->arNetDev); + netif_rx(skb); + } else { + dev_kfree_skb(skb); + } + } + } + } + } else { + if ((ar->arNetDev->flags & IFF_UP) == IFF_UP) { + skb->dev = ar->arNetDev; + skb->protocol = eth_type_trans(skb, ar->arNetDev); + netif_rx(skb); + } else { + dev_kfree_skb(skb); + } + } + + if (evInfo->status != A_ECANCELED) { + /* + * HTC provides A_ECANCELED status when it doesn't want to be refilled + * (probably due to a shutdown) + */ + ar6k_rx_refill(htcTarget, eid, HTC_DATA_AVAILABLE, NULL, ar); + } +} + +static void +ar6k_rx_refill(HTC_TARGET * htcTarget, HTC_ENDPOINT_ID eid, + HTC_EVENT_ID evId, HTC_EVENT_INFO * evInfo, void *arg) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) arg; + struct sk_buff *skb; + int arRxBuffers; + + AR6000_SPIN_LOCK(&ar->arLock, 0); + arRxBuffers = ar->arRxBuffers[eid]; + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + + AR_DEBUG2_PRINTF + ("ar6k_rx_refill: providing htc with %d buffers at eid=%d\n", + AR6000_MAX_RX_BUFFERS - arRxBuffers, eid); + while (arRxBuffers < AR6000_MAX_RX_BUFFERS) { + skb = ar6k_alloc_skb(AR6000_BUFFER_SIZE); + HTCBufferReceive(htcTarget, eid, skb->data, + AR6000_BUFFER_SIZE, skb); + arRxBuffers++; + } + + AR6000_SPIN_LOCK(&ar->arLock, 0); + ar->arRxBuffers[eid] = arRxBuffers; + AR6000_SPIN_UNLOCK(&ar->arLock, 0); +} + +static struct net_device_stats *ar6k_get_stats(struct net_device *dev) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + return &ar->arNetStats; +} + +#if 0 +static struct iw_statistics *ar6k_get_iwstats(struct net_device *dev) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + TARGET_STATS *pStats = &ar->arTargetStats; + struct iw_statistics *pIwStats = &ar->arIwStats; + + if (ar->arWmiReady == FALSE) { + pIwStats->status = 0; + pIwStats->qual.qual = 0; + pIwStats->qual.level = 0; + pIwStats->qual.noise = 0; + pIwStats->discard.code = 0; + pIwStats->discard.retries = 0; + pIwStats->miss.beacon = 0; + return pIwStats; + } + if (down_interruptible(&ar->arSem)) { + pIwStats->status = 0; + return pIwStats; + } + + AR6000_SPIN_LOCK(&ar->arLock, 0); + + ar->statsUpdatePending = TRUE; + + if (wmi_get_stats_cmd(ar->arWmi) != A_OK) { + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + up(&ar->arSem); + pIwStats->status = 0; + return pIwStats; + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + + wait_event_interruptible(arEvent, ar->statsUpdatePending == FALSE); + + if (signal_pending(current)) { + pIwStats->status = 0; + return pIwStats; + } + pIwStats->status = 1; + pIwStats->qual.qual = pStats->cs_aveBeacon_rssi; + pIwStats->qual.level = pStats->cs_aveBeacon_rssi + 161; /* noise is + -95 dBm */ + pIwStats->qual.noise = pStats->noise_floor_calibation; + pIwStats->discard.code = pStats->rx_decrypt_err; + pIwStats->discard.retries = pStats->tx_retry_cnt; + pIwStats->miss.beacon = pStats->cs_bmiss_cnt; + up(&ar->arSem); + return pIwStats; +} +#endif + +void ar6k_ready_event(void *devt, u8 * datap, u8 phyCap) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) devt; + struct net_device *dev = ar->arNetDev; + + ar->arWmiReady = TRUE; + wake_up(&arEvent); + A_MEMCPY(dev->dev_addr, datap, AR6000_ETH_ADDR_LEN); + AR_DEBUG_PRINTF("mac address = %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", + dev->dev_addr[0], dev->dev_addr[1], + dev->dev_addr[2], dev->dev_addr[3], + dev->dev_addr[4], dev->dev_addr[5]); + + ar->arPhyCapability = phyCap; +} + +u8 ar6k_iptos_to_userPriority(u8 * pkt) +{ + struct iphdr *ipHdr = (struct iphdr *) pkt; + u8 userPriority; + + /* + * IP Tos format : + * (Refer Pg 57 WMM-test-plan-v1.2) + * IP-TOS - 8bits + * : DSCP(6-bits) ECN(2-bits) + * : DSCP - P2 P1 P0 X X X + * where (P2 P1 P0) form 802.1D + */ + userPriority = ipHdr->tos >> 5; + return (userPriority & 0x7); +} + +void +ar6k_connect_event(AR_SOFTC_T * ar, u16 channel, u8 * bssid, + u16 listenInterval, u8 beaconIeLen, + u8 assocReqLen, u8 assocRespLen, u8 * assocInfo) +{ + union iwreq_data wrqu; + int i, beacon_ie_pos, assoc_resp_ie_pos, assoc_req_ie_pos; + static const char *tag1 = "ASSOCINFO(ReqIEs="; + static const char *tag2 = "ASSOCRESPIE="; + static const char *beaconIetag = "BEACONIE="; + char buf[WMI_CONTROL_MSG_MAX_LEN * 2 + sizeof(tag1)]; + char *pos; + + A_MEMCPY(ar->arBssid, bssid, sizeof(ar->arBssid)); + ar->arBssChannel = channel; + + A_PRINTF("AR6000 connected event on freq %d ", channel); + A_PRINTF("with bssid %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x " + " listenInterval=%d, beaconIeLen = %d assocReqLen=%d" + " assocRespLen =%d\n", + bssid[0], bssid[1], bssid[2], + bssid[3], bssid[4], bssid[5], + listenInterval, beaconIeLen, assocReqLen, assocRespLen); + + if (beaconIeLen && (sizeof(buf) > (9 + beaconIeLen * 2))) { + AR_DEBUG_PRINTF("\nBeaconIEs= "); + + beacon_ie_pos = 0; + A_MEMZERO(buf, sizeof(buf)); + sprintf(buf, "%s", beaconIetag); + pos = buf + 9; + for (i = beacon_ie_pos; i < beacon_ie_pos + beaconIeLen; i++) { + AR_DEBUG_PRINTF("%2.2x ", assocInfo[i]); + sprintf(pos, "%2.2x", assocInfo[i]); + pos += 2; + } + AR_DEBUG_PRINTF("\n"); + + A_MEMZERO(&wrqu, sizeof(wrqu)); + wrqu.data.length = strlen(buf); + wireless_send_event(ar->arNetDev, IWEVCUSTOM, &wrqu, buf); + } + + if (assocRespLen && (sizeof(buf) > (12 + (assocRespLen * 2)))) { + assoc_resp_ie_pos = beaconIeLen + assocReqLen + sizeof(u16) + /* capinfo */ + sizeof(u16) + /* status Code */ + sizeof(u16); /* associd */ + A_MEMZERO(buf, sizeof(buf)); + sprintf(buf, "%s", tag2); + pos = buf + 12; + AR_DEBUG_PRINTF("\nAssocRespIEs= "); + /* + * The Association Response Frame w.o. the WLAN header is delivered to + * the host, so skip over to the IEs + */ + for (i = assoc_resp_ie_pos; i < assoc_resp_ie_pos + assocRespLen; + i++) { + AR_DEBUG_PRINTF("%2.2x ", assocInfo[i]); + sprintf(pos, "%2.2x", assocInfo[i]); + pos += 2; + } + AR_DEBUG_PRINTF("\n"); + + A_MEMZERO(&wrqu, sizeof(wrqu)); + wrqu.data.length = strlen(buf); + wireless_send_event(ar->arNetDev, IWEVCUSTOM, &wrqu, buf); + } + + if (assocReqLen && (sizeof(buf) > (17 + (assocReqLen * 2)))) { + /* + * assoc Request includes capability and listen interval. Skip these. + */ + assoc_req_ie_pos = beaconIeLen + sizeof(u16) + /* capinfo */ + sizeof(u16); /* listen interval */ + + A_MEMZERO(buf, sizeof(buf)); + sprintf(buf, "%s", tag1); + pos = buf + 17; + AR_DEBUG_PRINTF("AssocReqIEs= "); + for (i = assoc_req_ie_pos; i < assoc_req_ie_pos + assocReqLen; i++) { + AR_DEBUG_PRINTF("%2.2x ", assocInfo[i]); + sprintf(pos, "%2.2x", assocInfo[i]); + pos += 2;; + } + AR_DEBUG_PRINTF("\n"); + + A_MEMZERO(&wrqu, sizeof(wrqu)); + wrqu.data.length = strlen(buf); + wireless_send_event(ar->arNetDev, IWEVCUSTOM, &wrqu, buf); + } + + if (ar->arTotalTxDataPending < txFlowCtrlThresh[ar->arNumDataEndPts]) { + netif_wake_queue(ar->arNetDev); + } + + if ((OPEN_AUTH == ar->arDot11AuthMode) && + (NONE_AUTH == ar->arAuthMode) && + (WEP_CRYPT == ar->arPairwiseCrypto)) { + if (!ar->arConnected) { + ar6k_install_static_wep_keys(ar); + } + } + + ar->arConnected = TRUE; + ar->arConnectPending = FALSE; + + A_MEMZERO(&wrqu, sizeof(wrqu)); + A_MEMCPY(wrqu.addr.sa_data, bssid, IEEE80211_ADDR_LEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + wireless_send_event(ar->arNetDev, SIOCGIWAP, &wrqu, NULL); + + if ((ar->arNetworkType == ADHOC_NETWORK) && ar->arIbssPsEnable) { + A_MEMZERO(ar->arNodeMap, sizeof(ar->arNodeMap)); + ar->arNodeNum = 0; + ar->arNexEpId = ENDPOINT2; + } +} + +void ar6k_set_numdataendpts(AR_SOFTC_T * ar, u32 num) +{ + A_ASSERT(num <= (HTC_MAILBOX_NUM_MAX - 1)); + ar->arNumDataEndPts = num; +} + +void +ar6k_disconnect_event(AR_SOFTC_T * ar, u8 reason, u8 * bssid, + u8 assocRespLen, u8 * assocInfo) +{ + u8 i; + + A_PRINTF("AR6000 disconnected"); + if (bssid[0] || bssid[1] || bssid[2] || bssid[3] || bssid[4] + || bssid[5]) { + A_PRINTF(" from %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x ", bssid[0], + bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); + } + + AR_DEBUG_PRINTF("\nAssocResp Frame = %s", assocRespLen ? " " : "NULL"); + for (i = 0; i < assocRespLen; i++) { + if (!(i % 0x10)) { + AR_DEBUG_PRINTF("\n"); + } + AR_DEBUG_PRINTF("%2.2x ", assocInfo[i]); + } + AR_DEBUG_PRINTF("\n"); + /* + * If the event is due to disconnect cmd from the host, only they the target + * would stop trying to connect. Under any other condition, target would + * keep trying to connect. + * + */ + if (reason == DISCONNECT_CMD) { + ar->arConnectPending = FALSE; + } + ar->arConnected = FALSE; + + netif_stop_queue(ar->arNetDev); + A_MEMZERO(ar->arBssid, sizeof(ar->arBssid)); + ar->arBssChannel = 0; +} + +void ar6k_regDomain_event(AR_SOFTC_T * ar, u32 regCode) +{ + A_PRINTF("AR6000 Reg Code = 0x%x\n", regCode); + ar->arRegCode = regCode; +} + +void +ar6k_neighborReport_event(AR_SOFTC_T * ar, int numAps, + WMI_NEIGHBOR_INFO * info) +{ + static const char *tag = "PRE-AUTH"; + char buf[128]; + union iwreq_data wrqu; + int i; + + A_PRINTF("AR6000 Neighbor Report Event\n"); + for (i = 0; i < numAps; info++, i++) { + A_PRINTF("bssid %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x ", + info->bssid[0], info->bssid[1], info->bssid[2], + info->bssid[3], info->bssid[4], info->bssid[5]); + if (info->bssFlags & WMI_PREAUTH_CAPABLE_BSS) { + A_PRINTF("preauth-cap"); + } + if (info->bssFlags & WMI_PMKID_VALID_BSS) { + A_PRINTF(" pmkid-valid\n"); + continue; /* we skip bss if the pmkid is already + valid */ + } + A_PRINTF("\n"); + snprintf(buf, sizeof(buf), + "%s%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x", tag, + info->bssid[0], info->bssid[1], info->bssid[2], + info->bssid[3], info->bssid[4], info->bssid[5], i, + info->bssFlags); + A_MEMZERO(&wrqu, sizeof(wrqu)); + wrqu.data.length = strlen(buf); + wireless_send_event(ar->arNetDev, IWEVCUSTOM, &wrqu, buf); + } +} + +void ar6k_tkip_micerr_event(AR_SOFTC_T * ar, u8 keyid, u8 ismcast) +{ + static const char *tag = "MLME-MICHAELMICFAILURE.indication"; + char buf[128]; + union iwreq_data wrqu; + + A_PRINTF("AR6000 TKIP MIC error received for keyid %d %scast\n", + keyid, ismcast ? "multi" : "uni"); + snprintf(buf, sizeof(buf), "%s(keyid=%d %scat)", tag, keyid, + ismcast ? "multi" : "uni"); + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = strlen(buf); + wireless_send_event(ar->arNetDev, IWEVCUSTOM, &wrqu, buf); +} + +void ar6k_scanComplete_event(AR_SOFTC_T * ar) +{ + A_PRINTF("AR6000 scan complete\n"); +} + +void ar6k_targetStats_event(AR_SOFTC_T * ar, WMI_TARGET_STATS * pTarget) +{ + TARGET_STATS *pStats = &ar->arTargetStats; + u8 ac; + + A_PRINTF("AR6000 updating target stats\n"); + pStats->tx_packets += pTarget->txrxStats.tx_stats.tx_packets; + pStats->tx_bytes += pTarget->txrxStats.tx_stats.tx_bytes; + pStats->tx_unicast_pkts += pTarget->txrxStats.tx_stats.tx_unicast_pkts; + pStats->tx_unicast_bytes += + pTarget->txrxStats.tx_stats.tx_unicast_bytes; + pStats->tx_multicast_pkts += + pTarget->txrxStats.tx_stats.tx_multicast_pkts; + pStats->tx_multicast_bytes += + pTarget->txrxStats.tx_stats.tx_multicast_bytes; + pStats->tx_broadcast_pkts += + pTarget->txrxStats.tx_stats.tx_broadcast_pkts; + pStats->tx_broadcast_bytes += + pTarget->txrxStats.tx_stats.tx_broadcast_bytes; + pStats->tx_rts_success_cnt += + pTarget->txrxStats.tx_stats.tx_rts_success_cnt; + for (ac = 0; ac < WMM_NUM_AC; ac++) + pStats->tx_packet_per_ac[ac] += + pTarget->txrxStats.tx_stats.tx_packet_per_ac[ac]; + pStats->tx_errors += pTarget->txrxStats.tx_stats.tx_errors; + pStats->tx_failed_cnt += pTarget->txrxStats.tx_stats.tx_failed_cnt; + pStats->tx_retry_cnt += pTarget->txrxStats.tx_stats.tx_retry_cnt; + pStats->tx_rts_fail_cnt += pTarget->txrxStats.tx_stats.tx_rts_fail_cnt; + + pStats->rx_packets += pTarget->txrxStats.rx_stats.rx_packets; + pStats->rx_bytes += pTarget->txrxStats.rx_stats.rx_bytes; + pStats->rx_unicast_pkts += pTarget->txrxStats.rx_stats.rx_unicast_pkts; + pStats->rx_unicast_bytes += + pTarget->txrxStats.rx_stats.rx_unicast_bytes; + pStats->rx_multicast_pkts += + pTarget->txrxStats.rx_stats.rx_multicast_pkts; + pStats->rx_multicast_bytes += + pTarget->txrxStats.rx_stats.rx_multicast_bytes; + pStats->rx_broadcast_pkts += + pTarget->txrxStats.rx_stats.rx_broadcast_pkts; + pStats->rx_broadcast_bytes += + pTarget->txrxStats.rx_stats.rx_broadcast_bytes; + pStats->rx_fragment_pkt += pTarget->txrxStats.rx_stats.rx_fragment_pkt; + pStats->rx_errors += pTarget->txrxStats.rx_stats.rx_errors; + pStats->rx_crcerr += pTarget->txrxStats.rx_stats.rx_crcerr; + pStats->rx_key_cache_miss += + pTarget->txrxStats.rx_stats.rx_key_cache_miss; + pStats->rx_decrypt_err += pTarget->txrxStats.rx_stats.rx_decrypt_err; + pStats->rx_duplicate_frames += + pTarget->txrxStats.rx_stats.rx_duplicate_frames; + + + pStats->tkip_local_mic_failure + += pTarget->txrxStats.tkipCcmpStats.tkip_local_mic_failure; + pStats->tkip_counter_measures_invoked + += pTarget->txrxStats.tkipCcmpStats.tkip_counter_measures_invoked; + pStats->tkip_replays += pTarget->txrxStats.tkipCcmpStats.tkip_replays; + pStats->tkip_format_errors += + pTarget->txrxStats.tkipCcmpStats.tkip_format_errors; + pStats->ccmp_format_errors += + pTarget->txrxStats.tkipCcmpStats.ccmp_format_errors; + pStats->ccmp_replays += pTarget->txrxStats.tkipCcmpStats.ccmp_replays; + + + pStats->power_save_failure_cnt += + pTarget->pmStats.power_save_failure_cnt; + pStats->noise_floor_calibation = pTarget->noise_floor_calibation; + + pStats->cs_bmiss_cnt += pTarget->cservStats.cs_bmiss_cnt; + pStats->cs_lowRssi_cnt += pTarget->cservStats.cs_lowRssi_cnt; + pStats->cs_connect_cnt += pTarget->cservStats.cs_connect_cnt; + pStats->cs_disconnect_cnt += pTarget->cservStats.cs_disconnect_cnt; + pStats->cs_aveBeacon_rssi = pTarget->cservStats.cs_aveBeacon_rssi; + pStats->cs_lastRoam_msec = pTarget->cservStats.cs_lastRoam_msec; + + ar->statsUpdatePending = FALSE; + wake_up(&arEvent); +} + + +void +ar6k_rssiThreshold_event(AR_SOFTC_T * ar, + WMI_RSSI_THRESHOLD_VAL newThreshold) +{ + A_PRINTF("AR6000 Threshold val = %d \n", newThreshold); +} + +void +ar6k_reportError_event(AR_SOFTC_T * ar, WMI_TARGET_ERROR_VAL errorVal) +{ + char *errString[] = { + [WMI_TARGET_PM_ERR_FAIL] "WMI_TARGET_PM_ERR_FAIL", + [WMI_TARGET_KEY_NOT_FOUND] "WMI_TARGET_KEY_NOT_FOUND", + [WMI_TARGET_DECRYPTION_ERR] "WMI_TARGET_DECRYPTION_ERR", + [WMI_TARGET_BMISS] "WMI_TARGET_BMISS", + [WMI_PSDISABLE_NODE_JOIN] "WMI_PSDISABLE_NODE_JOIN" + }; + + A_PRINTF("AR6000 Error on Target. Error = 0x%x\n", errorVal); + + /* One error is reported at a time, and errorval is a bitmask */ + if (errorVal & (errorVal - 1)) + return; + + A_PRINTF("AR6000 Error type = "); + switch (errorVal) { + case WMI_TARGET_PM_ERR_FAIL: + case WMI_TARGET_KEY_NOT_FOUND: + case WMI_TARGET_DECRYPTION_ERR: + case WMI_TARGET_BMISS: + case WMI_PSDISABLE_NODE_JOIN: + A_PRINTF("%s\n", errString[errorVal]); + break; + default: + A_PRINTF("INVALID\n"); + break; + } + +} + + +void +ar6k_cac_event(AR_SOFTC_T * ar, u8 ac, u8 cacIndication, + u8 statusCode, u8 * tspecSuggestion) +{ + WMM_TSPEC_IE *tspecIe; + + /* + * This is the TSPEC IE suggestion from AP. + * Suggestion provided by AP under some error + * cases, could be helpful for the host app. + * Check documentation. + */ + tspecIe = (WMM_TSPEC_IE *) tspecSuggestion; + + /* + * What do we do, if we get TSPEC rejection? One thought + * that comes to mind is implictly delete the pstream... + */ + A_PRINTF("AR6000 CAC notification. " + "AC = %d, cacIndication = 0x%x, statusCode = 0x%x\n", + ac, cacIndication, statusCode); +} + +#define AR6000_PRINT_BSSID(_pBss) do { \ + A_PRINTF("%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x ",\ + (_pBss)[0],(_pBss)[1],(_pBss)[2],(_pBss)[3],\ + (_pBss)[4],(_pBss)[5]); \ +} while(0) + +void ar6k_roam_tbl_event(AR_SOFTC_T * ar, WMI_TARGET_ROAM_TBL * pTbl) +{ + u8 i; + + A_PRINTF("ROAM TABLE NO OF ENTRIES is %d ROAM MODE is %d\n", + pTbl->numEntries, pTbl->roamMode); + for (i = 0; i < pTbl->numEntries; i++) { + A_PRINTF("[%d]bssid %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x ", i, + pTbl->bssRoamInfo[i].bssid[0], + pTbl->bssRoamInfo[i].bssid[1], + pTbl->bssRoamInfo[i].bssid[2], + pTbl->bssRoamInfo[i].bssid[3], + pTbl->bssRoamInfo[i].bssid[4], + pTbl->bssRoamInfo[i].bssid[5]); + A_PRINTF("RSSI %d RSSIDT %d LAST RSSI %d UTIL %d ROAM_UTIL %d" + " BIAS %d\n", pTbl->bssRoamInfo[i].rssi, + pTbl->bssRoamInfo[i].rssidt, + pTbl->bssRoamInfo[i].last_rssi, pTbl->bssRoamInfo[i].util, + pTbl->bssRoamInfo[i].roam_util, + pTbl->bssRoamInfo[i].bias); + } +} + +/* + * Report the Roaming related data collected on the target + */ +void ar6k_display_roam_time(WMI_TARGET_ROAM_TIME * p) +{ + A_PRINTF("Disconnect Data : BSSID: "); + AR6000_PRINT_BSSID(p->disassoc_bssid); + A_PRINTF(" RSSI %d DISASSOC Time %d NO_TXRX_TIME %d\n", + p->disassoc_bss_rssi, p->disassoc_time, p->no_txrx_time); + A_PRINTF("Connect Data: BSSID: "); + AR6000_PRINT_BSSID(p->assoc_bssid); + A_PRINTF(" RSSI %d ASSOC Time %d TXRX_TIME %d\n", + p->assoc_bss_rssi, p->assoc_time, p->allow_txrx_time); + A_PRINTF("Last Data Tx Time (b4 Disassoc) %d " + "First Data Tx Time (after Assoc) %d\n", + p->last_data_txrx_time, p->first_data_txrx_time); +} + +void ar6k_roam_data_event(AR_SOFTC_T * ar, WMI_TARGET_ROAM_DATA * p) +{ + switch (p->roamDataType) { + case ROAM_DATA_TIME: + ar6k_display_roam_time(&p->u.roamTime); + break; + default: + break; + } +} + +static int +ar6k_ioctl_set_error_report_bitmask(struct net_device *dev, + struct ifreq *rq) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + WMI_TARGET_ERROR_REPORT_BITMASK cmd; + int ret = 0; + + if (ar->arWmiReady == FALSE) { + return -EIO; + } + + if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) { + return -EFAULT; + } + + AR6000_SPIN_LOCK(&ar->arLock, 0); + ret = wmi_set_error_report_bitmask(ar->arWmi, cmd.bitmask); + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + + return (ret == 0 ? ret : -EINVAL); +} + + +static int +ar6k_ioctl_get_target_stats(struct net_device *dev, struct ifreq *rq) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + TARGET_STATS *pStats = &ar->arTargetStats; + int ret = 0; + + if (ar->arWmiReady == FALSE) { + return -EIO; + } + if (down_interruptible(&ar->arSem)) { + return -ERESTARTSYS; + } + + AR6000_SPIN_LOCK(&ar->arLock, 0); + + ar->statsUpdatePending = TRUE; + + if (wmi_get_stats_cmd(ar->arWmi) != A_OK) { + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + up(&ar->arSem); + return -EIO; + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + + wait_event_interruptible(arEvent, ar->statsUpdatePending == FALSE); + + if (signal_pending(current)) { + ret = -EINTR; + } + + if (!ret && copy_to_user(rq->ifr_data, pStats, sizeof(*pStats))) { + ret = -EFAULT; + } + + up(&ar->arSem); + + return ret; +} + +static int +ar6k_ioctl_set_access_params(struct net_device *dev, struct ifreq *rq) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + WMI_SET_ACCESS_PARAMS_CMD cmd; + int ret = 0; + + if (ar->arWmiReady == FALSE) { + return -EIO; + } + + if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) { + return -EFAULT; + } + + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (wmi_set_access_params_cmd + (ar->arWmi, cmd.txop, cmd.eCWmin, cmd.eCWmax, cmd.aifsn) == A_OK) { + ret = 0; + } else { + ret = -EINVAL; + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + + return (ret); +} + +static int +ar6k_ioctl_set_disconnect_timeout(struct net_device *dev, + struct ifreq *rq) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + WMI_DISC_TIMEOUT_CMD cmd; + int ret = 0; + + if (ar->arWmiReady == FALSE) { + return -EIO; + } + + if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) { + return -EFAULT; + } + + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (wmi_disctimeout_cmd(ar->arWmi, cmd.disconnectTimeout) == A_OK) { + ret = 0; + } else { + ret = -EINVAL; + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + + return (ret); +} + +static int +ar6k_xioctl_set_voice_pkt_size(struct net_device *dev, char *userdata) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + WMI_SET_VOICE_PKT_SIZE_CMD cmd; + int ret = 0; + + if (ar->arWmiReady == FALSE) { + return -EIO; + } + + if (copy_from_user(&cmd, userdata, sizeof(cmd))) { + return -EFAULT; + } + + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (wmi_set_voice_pkt_size_cmd(ar->arWmi, cmd.voicePktSize) == A_OK) { + ret = 0; + } else { + ret = -EINVAL; + } + + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + + return (ret); +} + +static int +ar6k_xioctl_set_max_sp_len(struct net_device *dev, char *userdata) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + WMI_SET_MAX_SP_LEN_CMD cmd; + int ret = 0; + + if (ar->arWmiReady == FALSE) { + return -EIO; + } + + if (copy_from_user(&cmd, userdata, sizeof(cmd))) { + return -EFAULT; + } + + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (wmi_set_max_sp_len_cmd(ar->arWmi, cmd.maxSPLen) == A_OK) { + ret = 0; + } else { + ret = -EINVAL; + } + + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + + return (ret); +} + +int ar6k_control_tx(void *devt, struct sk_buff *skb, int endPt) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) devt; + int status; + struct ar_cookie *cookie; + WMI_DATA_HDR *dhdr; + + A_ASSERT((endPt == WMI_CONTROL_MBOX) + || (endPt == WMI_LOW_PRIORITY_MBOX) + || (endPt == WMI_HIGH_PRIORITY_MBOX) + || (endPt == WMI_BEST_EFFORT_MBOX)); + + ar->arTxPending[endPt]++; + if (endPt != WMI_CONTROL_MBOX) { + ar->arTotalTxDataPending++; + } + + cookie = ar6k_alloc_cookie(ar); + + /* If the particular data queue is full, silently drop the pkt. */ + dhdr = (WMI_DATA_HDR *) skb->data; + if ((cookie == NULL) || + ((ar->arTxPending[endPt] > MAX_ALLOWED_TXQ_DEPTH) && + (!WMI_DATA_HDR_IS_MSG_TYPE(dhdr, SYNC_MSGTYPE)))) { + ar->arTxPending[endPt]--; + if (endPt != WMI_CONTROL_MBOX) { + ar->arTotalTxDataPending--; + } + dev_kfree_skb(skb); + if (cookie != NULL) { + ar6k_free_cookie(ar, cookie); + } + return A_NO_MEMORY; + } + + cookie->arc_bp[0] = (u32) skb; + cookie->arc_bp[1] = 0; + status = HTCBufferSend(ar->arHtcTarget, endPt, skb->data, + skb->len, cookie); + if (status != A_OK) { + /* + * XXX This is an error that requires us to reset chip + */ + ar->arTxPending[endPt]--; + if (endPt != WMI_CONTROL_MBOX) { + ar->arTotalTxDataPending--; + } + AR_DEBUG_PRINTF("Dropping control frame!!\n"); + ar6k_free_cookie(ar, cookie); + dev_kfree_skb(skb); + return (status); + } + + return A_OK; +} + +module_init(ar6k_init_module); +module_exit(ar6k_cleanup_module); + +/* + * SIOCGIWNAME + */ +int +ar6k_ioctl_giwname(struct net_device *dev, + struct iw_request_info *info, char *name, char *extra) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + + if (ar->arWlanState == WLAN_DISABLED) { + return -EIO; + } + + switch (ar->arPhyCapability) { + case (WMI_11A_CAPABILITY): + strncpy(name, "AR6000 802.11a", IFNAMSIZ); + break; + case (WMI_11G_CAPABILITY): + strncpy(name, "AR6000 802.11g", IFNAMSIZ); + break; + case (WMI_11AG_CAPABILITY): + strncpy(name, "AR6000 802.11ag", IFNAMSIZ); + break; + default: + strncpy(name, "AR6000 802.11", IFNAMSIZ); + break; + } + + return 0; +} + +/* + * SIOCSIWFREQ + */ +int +ar6k_ioctl_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + + if (ar->arWlanState == WLAN_DISABLED) { + return -EIO; + } + + /* + * We support limiting the channels via wmiconfig. + * + * We use this command to configure the channel hint for the connect cmd + * so it is possible the target will end up connecting to a different + * channel. + */ + if (freq->e > 1) { + return -EINVAL; + } else if (freq->e == 1) { + ar->arChannelHint = freq->m / 100000; + } else { + ar->arChannelHint = wlan_ieee2freq(freq->m); + } + + A_PRINTF("channel hint set to %d\n", ar->arChannelHint); + return 0; +} + +/* + * SIOCGIWFREQ + */ +int +ar6k_ioctl_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + + if (ar->arWlanState == WLAN_DISABLED) { + return -EIO; + } + + if (ar->arConnected != TRUE) { + return -EINVAL; + } + + freq->m = ar->arBssChannel * 100000; + freq->e = 1; + + return 0; +} + +/* + * SIOCSIWMODE + */ +int +ar6k_ioctl_siwmode(struct net_device *dev, + struct iw_request_info *info, + __u32 * mode, char *extra) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + + if (ar->arWlanState == WLAN_DISABLED) { + return -EIO; + } + + switch (*mode) { + case IW_MODE_INFRA: + ar->arNetworkType = INFRA_NETWORK; + break; + case IW_MODE_ADHOC: + ar->arNetworkType = ADHOC_NETWORK; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * SIOCGIWMODE + */ +int +ar6k_ioctl_giwmode(struct net_device *dev, + struct iw_request_info *info, + __u32 * mode, char *extra) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + + if (ar->arWlanState == WLAN_DISABLED) { + return -EIO; + } + + switch (ar->arNetworkType) { + case INFRA_NETWORK: + *mode = IW_MODE_INFRA; + break; + case ADHOC_NETWORK: + *mode = IW_MODE_ADHOC; + break; + default: + return -EIO; + } + return 0; +} + +/* + * SIOCSIWSENS + */ +int +ar6k_ioctl_siwsens(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *sens, char *extra) +{ + return 0; +} + +/* + * SIOCGIWSENS + */ +int +ar6k_ioctl_giwsens(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *sens, char *extra) +{ + sens->value = 0; + sens->fixed = 1; + + return 0; +} + +/* + * SIOCGIWRANGE + */ +int +ar6k_ioctl_giwrange(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + struct iw_range *range = (struct iw_range *) extra; + int i, ret = 0; + + if (ar->arWmiReady == FALSE) { + return -EIO; + } + + if (ar->arWlanState == WLAN_DISABLED) { + return -EIO; + } + + if (down_interruptible(&ar->arSem)) { + return -ERESTARTSYS; + } + ar->arNumChannels = -1; + A_MEMZERO(ar->arChannelList, sizeof(ar->arChannelList)); + + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (wmi_get_channelList_cmd(ar->arWmi) != A_OK) { + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + up(&ar->arSem); + return -EIO; + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + + wait_event_interruptible(arEvent, ar->arNumChannels != -1); + + if (signal_pending(current)) { + up(&ar->arSem); + return -EINTR; + } + + data->length = sizeof(struct iw_range); + A_MEMZERO(range, sizeof(struct iw_range)); + + range->txpower_capa = 0; + + range->min_pmp = 1 * 1024; + range->max_pmp = 65535 * 1024; + range->min_pmt = 1 * 1024; + range->max_pmt = 1000 * 1024; + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = 0; + + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 13; + + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT; + range->min_retry = 0; + range->max_retry = 255; + + range->num_frequency = range->num_channels = ar->arNumChannels; + for (i = 0; i < ar->arNumChannels; i++) { + range->freq[i].i = wlan_freq2ieee(ar->arChannelList[i]); + range->freq[i].m = ar->arChannelList[i] * 100000; + range->freq[i].e = 1; + } + + /* Max quality is max field value minus noise floor */ + range->max_qual.qual = 0xff - 161; + + /* + * In order to use dBm measurements, 'level' must be lower + * than any possible measurement (see iw_print_stats() in + * wireless tools). It's unclear how this is meant to be + * done, but setting zero in these values forces dBm and + * the actual numbers are not used. + */ + range->max_qual.level = 0; + range->max_qual.noise = 0; + + range->sensitivity = 3; + + range->max_encoding_tokens = 4; + /* XXX query driver to find out supported key sizes */ + range->num_encoding_sizes = 3; + range->encoding_size[0] = 5; /* 40-bit */ + range->encoding_size[1] = 13; /* 104-bit */ + range->encoding_size[2] = 16; /* 128-bit */ + + range->num_bitrates = 0; + + /* estimated maximum TCP throughput values (bps) */ + range->throughput = 22000000; + + range->min_rts = 0; + range->max_rts = 2347; + range->min_frag = 256; + range->max_frag = 2346; + + up(&ar->arSem); + + return ret; +} + +/* + * SIOCSIWAP + * This ioctl is used to set the desired bssid for the connect command. + */ +int +ar6k_ioctl_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + + if (ar->arWlanState == WLAN_DISABLED) { + return -EIO; + } + + if (ap_addr->sa_family != ARPHRD_ETHER) { + return -EIO; + } + + if (A_MEMCMP(&ap_addr->sa_data, bcast_mac, AR6000_ETH_ADDR_LEN) == 0) { + A_MEMZERO(ar->arReqBssid, sizeof(ar->arReqBssid)); + } else { + A_MEMCPY(ar->arReqBssid, &ap_addr->sa_data, + sizeof(ar->arReqBssid)); + } + + return 0; +} + +/* + * SIOCGIWAP + */ +int +ar6k_ioctl_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + + if (ar->arWlanState == WLAN_DISABLED) { + return -EIO; + } + + if (ar->arConnected != TRUE) { + return -EINVAL; + } + + A_MEMCPY(&ap_addr->sa_data, ar->arBssid, sizeof(ar->arBssid)); + ap_addr->sa_family = ARPHRD_ETHER; + + return 0; +} + +/* + * SIOCGIWAPLIST + */ +int +ar6k_ioctl_iwaplist(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + return -EIO; /* for now */ +} + +/* + * SIOCGIWSCAN + */ +int +ar6k_ioctl_siwscan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + int ret = 0; + + if (ar->arWmiReady == FALSE) { + return -EIO; + } + + if (ar->arWlanState == WLAN_DISABLED) { + return -EIO; + } + + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (wmi_startscan_cmd(ar->arWmi, WMI_LONG_SCAN) != A_OK) { + ret = -EIO; + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + + return ret; +} + +#if WIRELESS_EXT > 14 +/* + * Encode a WPA or RSN information element as a custom + * element using the hostap format. + */ +static u_int +encode_ie(void *buf, size_t bufsize, + const u_int8_t * ie, size_t ielen, + const char *leader, size_t leader_len) +{ + u_int8_t *p; + int i; + + if (bufsize < leader_len) + return 0; + p = buf; + memcpy(p, leader, leader_len); + bufsize -= leader_len; + p += leader_len; + for (i = 0; i < ielen && bufsize > 2; i++) + p += sprintf(p, "%02x", ie[i]); + return (i == ielen ? p - (u_int8_t *) buf : 0); +} +#endif /* WIRELESS_EXT > 14 */ +/* + * Units are in db above the noise floor. That means the + * rssi values reported in the tx/rx descriptors in the + * driver are the SNR expressed in db. + * + * If you assume that the noise floor is -95, which is an + * excellent assumption 99.5 % of the time, then you can + * derive the absolute signal level (i.e. -95 + rssi). + * There are some other slight factors to take into account + * depending on whether the rssi measurement is from 11b, + * 11g, or 11a. These differences are at most 2db and + * can be documented. + * + * NB: various calculations are based on the orinoco/wavelan + * drivers for compatibility + */ +static void ar6k_set_quality(struct iw_quality *iq, s8 rssi) +{ + if (rssi < 0) { + iq->qual = 0; + } else { + iq->qual = rssi; + } + + /* NB: max is 94 because noise is hardcoded to 161 */ + if (iq->qual > 94) + iq->qual = 94; + + iq->noise = 161; /* -95dBm */ + iq->level = iq->noise + iq->qual; + iq->updated = 7; +} + +void ar6k_scan_node(void *arg, bss_t * ni) +{ + struct iw_event iwe; +#if WIRELESS_EXT > 14 + char buf[64 * 2 + 30]; +#endif + struct ar_giwscan_param *param; + u8 *current_ev; + u8 *end_buf; + struct ieee80211_common_ie *cie; + + param = (struct ar_giwscan_param *) arg; + + if (param->current_ev >= param->end_buf) { + return; + } + if ((param->firstPass == TRUE) && (ni->ni_cie.ie_wpa == NULL)) { + /* + * Only forward wpa bss's in first pass + */ + return; + } + if ((param->firstPass == FALSE) && (ni->ni_cie.ie_wpa != NULL)) { + /* + * Only forward non-wpa bss's in 2nd pass + */ + return; + } + + current_ev = param->current_ev; + end_buf = param->end_buf; + + cie = &ni->ni_cie; + + A_MEMZERO(&iwe, sizeof(iwe)); + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + A_MEMCPY(iwe.u.ap_addr.sa_data, ni->ni_macaddr, 6); + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_ADDR_LEN); + + A_MEMZERO(&iwe, sizeof(iwe)); + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + iwe.u.data.length = cie->ie_ssid[1]; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, + &cie->ie_ssid[2]); + + if (cie->ie_capInfo & (IEEE80211_CAPINFO_ESS | IEEE80211_CAPINFO_IBSS)) { + A_MEMZERO(&iwe, sizeof(iwe)); + iwe.cmd = SIOCGIWMODE; + iwe.u.mode = cie->ie_capInfo & IEEE80211_CAPINFO_ESS ? + IW_MODE_MASTER : IW_MODE_ADHOC; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_UINT_LEN); + } + + A_MEMZERO(&iwe, sizeof(iwe)); + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = cie->ie_chan * 100000; + iwe.u.freq.e = 1; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_FREQ_LEN); + + A_MEMZERO(&iwe, sizeof(iwe)); + iwe.cmd = IWEVQUAL; + ar6k_set_quality(&iwe.u.qual, ni->ni_rssi); + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_QUAL_LEN); + + A_MEMZERO(&iwe, sizeof(iwe)); + iwe.cmd = SIOCGIWENCODE; + if (cie->ie_capInfo & IEEE80211_CAPINFO_PRIVACY) { + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + } else { + iwe.u.data.flags = IW_ENCODE_DISABLED; + } + iwe.u.data.length = 0; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ""); + +#ifdef NOTYET + A_MEMZERO(&iwe, sizeof(iwe)); + iwe.cmd = SIOCGIWRATE; + current_val = current_ev + IW_EV_LCP_LEN; + for (j = 0; j < ni->ni_rates.rs_nrates; j++) { + if (ni->ni_rates.rs_rates[j]) { + iwe.u.bitrate.value = ((ni->ni_rates.rs_rates[j] & + IEEE80211_RATE_VAL) / 2) * 1000000; + current_val = iwe_stream_add_value(current_ev, + current_val, end_buf, &iwe, + IW_EV_PARAM_LEN); + } + } + /* remove fixed header if no rates were added */ + if ((current_val - current_ev) > IW_EV_LCP_LEN) + current_ev = current_val; +#endif /* NOTYET */ + +#if WIRELESS_EXT > 14 + A_MEMZERO(&iwe, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + snprintf(buf, sizeof(buf), "bcn_int=%d", cie->ie_beaconInt); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf); + + if (cie->ie_wpa != NULL) { + static const char rsn_leader[] = "rsn_ie="; + static const char wpa_leader[] = "wpa_ie="; + + A_MEMZERO(&iwe, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + if (cie->ie_wpa[0] == IEEE80211_ELEMID_RSN) { + iwe.u.data.length = encode_ie(buf, sizeof(buf), cie->ie_wpa, + cie->ie_wpa[1] + 2, + rsn_leader, + sizeof(rsn_leader) - 1); + } else { + iwe.u.data.length = encode_ie(buf, sizeof(buf), cie->ie_wpa, + cie->ie_wpa[1] + 2, + wpa_leader, + sizeof(wpa_leader) - 1); + } + if (iwe.u.data.length != 0) { + current_ev = + iwe_stream_add_point(current_ev, end_buf, &iwe, buf); + } + } + + if (cie->ie_wmm != NULL) { + static const char wmm_leader[] = "wmm_ie="; + + A_MEMZERO(&iwe, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = encode_ie(buf, sizeof(buf), cie->ie_wmm, + cie->ie_wmm[1] + 2, + wmm_leader, sizeof(wmm_leader) - 1); + if (iwe.u.data.length != 0) { + current_ev = + iwe_stream_add_point(current_ev, end_buf, &iwe, buf); + } + } + + if (cie->ie_ath != NULL) { + static const char ath_leader[] = "ath_ie="; + + A_MEMZERO(&iwe, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = encode_ie(buf, sizeof(buf), cie->ie_ath, + cie->ie_ath[1] + 2, + ath_leader, sizeof(ath_leader) - 1); + if (iwe.u.data.length != 0) { + current_ev = + iwe_stream_add_point(current_ev, end_buf, &iwe, buf); + } + } +#endif /* WIRELESS_EXT > 14 */ + + param->current_ev = current_ev; +} + +int +ar6k_ioctl_giwscan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + struct ar_giwscan_param param; + int i; + + if (ar->arWlanState == WLAN_DISABLED) { + return -EIO; + } + + if (ar->arWmiReady == FALSE) { + return -EIO; + } + + param.current_ev = extra; + param.end_buf = extra + IW_SCAN_MAX_DATA; + param.firstPass = TRUE; + + /* + * Do two passes to insure WPA scan candidates + * are sorted to the front. This is a hack to deal with + * the wireless extensions capping scan results at + * IW_SCAN_MAX_DATA bytes. In densely populated environments + * it's easy to overflow this buffer (especially with WPA/RSN + * information elements). Note this sorting hack does not + * guarantee we won't overflow anyway. + */ + for (i = 0; i < 2; i++) { + /* + * Translate data to WE format. + */ + wmi_iterate_nodes(ar->arWmi, ar6k_scan_node, ¶m); + param.firstPass = FALSE; + if (param.current_ev >= param.end_buf) { + break; + } + } + + data->length = param.current_ev - extra; + return 0; +} + +/* SIOCSIWESSID */ +static int +ar6k_ioctl_siwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + int status; + + if (ar->arWlanState == WLAN_DISABLED) { + return -EIO; + } + + if (ar->arWmiReady == FALSE) { + return -EIO; + } + + /* + * iwconfig passes a null terminated string with length including this + * so we need to account for this + */ + if (data->flags && (!data->length || (data->length == 1) || + ((data->length - 1) > sizeof(ar->arSsid)))) { + /* + * ssid is invalid + */ + return -EINVAL; + } + + if (down_interruptible(&ar->arSem)) { + return -ERESTARTSYS; + } + + if (ar->arTxPending[WMI_CONTROL_MBOX]) { + /* + * sleep until the command queue drains + */ + wait_event_interruptible(arEvent, + ar->arTxPending[WMI_CONTROL_MBOX] == 0); + if (signal_pending(current)) { + return -EINTR; + } + } + + if ((ar->arSsidLen) || (!data->flags)) { + if ((!data->flags) || + (A_MEMCMP(ar->arSsid, ssid, ar->arSsidLen) != 0) || + (ar->arSsidLen != (data->length - 1))) { + /* + * SSID set previously or essid off has been issued. + * + * Disconnect Command is issued in two cases after wmi is ready + * (1) ssid is different from the previous setting + * (2) essid off has been issued + * + */ + if (ar->arWmiReady == TRUE) { + AR6000_SPIN_LOCK(&ar->arLock, 0); + status = wmi_disconnect_cmd(ar->arWmi); + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + A_MEMZERO(ar->arSsid, sizeof(ar->arSsid)); + ar->arSsidLen = 0; + A_MEMZERO(ar->arReqBssid, sizeof(ar->arReqBssid)); + if (!data->flags) { + up(&ar->arSem); + return 0; + } + } else { + up(&ar->arSem); + } + } else { + /* + * SSID is same, so we assume profile hasn't changed. + * If the interface is up and wmi is ready, we issue + * a reconnect cmd. + */ + if ((ar->arConnected == TRUE || ar->arConnectPending == TRUE) + && (ar->arWmiReady == TRUE)) { + AR6000_SPIN_LOCK(&ar->arLock, 0); + status = wmi_reconnect_cmd(ar->arWmi, ar->arReqBssid, + ar->arChannelHint); + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + up(&ar->arSem); + if (status != A_OK) { + return -EIO; + } + return 0; + } else { + up(&ar->arSem); + return 0; + } + } + } + + ar->arSsidLen = data->length - 1; + A_MEMCPY(ar->arSsid, ssid, ar->arSsidLen); + + /* The ssid length check prevents second "essid off" from the user, to + be treated as a connect cmd. The second "essid off" is ignored. */ + if ((ar->arWmiReady == TRUE) && (ar->arSsidLen > 0)) { + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (SHARED_AUTH == ar->arDot11AuthMode) { + ar6k_install_static_wep_keys(ar); + } + AR_DEBUG_PRINTF("Connect called with authmode %d dot11 auth %d" + " PW crypto %d PW crypto Len %d GRP crypto %d" + " GRP crypto Len %d\n", + ar->arAuthMode, ar->arDot11AuthMode, + ar->arPairwiseCrypto, ar->arPairwiseCryptoLen, + ar->arGroupCrypto, ar->arGroupCryptoLen); + + status = wmi_connect_cmd(ar->arWmi, ar->arNetworkType, + ar->arDot11AuthMode, ar->arAuthMode, + ar->arPairwiseCrypto, + ar->arPairwiseCryptoLen, + ar->arGroupCrypto, ar->arGroupCryptoLen, + ar->arSsidLen, ar->arSsid, ar->arReqBssid, + ar->arChannelHint); + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + + up(&ar->arSem); + + if (status != A_OK) { + return -EIO; + } + ar->arConnectPending = TRUE; + } else { + up(&ar->arSem); + } + return 0; +} + +/* SIOCGIWESSID */ +static int +ar6k_ioctl_giwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *essid) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + + if (ar->arWlanState == WLAN_DISABLED) { + return -EIO; + } + + if (!ar->arSsidLen) { + return -EINVAL; + } + + data->flags = 1; + data->length = ar->arSsidLen; + A_MEMCPY(essid, ar->arSsid, ar->arSsidLen); + + return 0; +} + +/* + * SIOCSIWRATE + */ +int +ar6k_ioctl_siwrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + u32 kbps; + + if (rrq->fixed) { + kbps = rrq->value / 1000; /* rrq->value is in bps */ + } else { + kbps = -1; /* -1 indicates auto rate */ + } + if (wmi_validate_bitrate(ar->arWmi, kbps) == A_EINVAL) { + AR_DEBUG_PRINTF("BitRate is not Valid %d\n", kbps); + return -EINVAL; + } + ar->arBitRate = kbps; + if (ar->arWmiReady == TRUE) { + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (wmi_set_bitrate_cmd(ar->arWmi, kbps) != A_OK) { + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + return -EINVAL; + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + } + return 0; +} + +/* + * SIOCGIWRATE + */ +int +ar6k_ioctl_giwrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + int ret = 0; + + if (down_interruptible(&ar->arSem)) { + return -ERESTARTSYS; + } + if (ar->arWmiReady == TRUE) { + ar->arBitRate = 0xFFFF; + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (wmi_get_bitrate_cmd(ar->arWmi) != A_OK) { + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + up(&ar->arSem); + return -EIO; + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + wait_event_interruptible(arEvent, ar->arBitRate != 0xFFFF); + if (signal_pending(current)) { + ret = -EINTR; + } + } + /* If the interface is down or wmi is not ready or the target is not + connected - return the value stored in the device structure */ + if (!ret) { + if (ar->arBitRate == -1) { + rrq->fixed = TRUE; + rrq->value = 0; + } else { + rrq->value = ar->arBitRate * 1000; + } + } + + up(&ar->arSem); + + return ret; +} + +/* + * SIOCSIWTXPOW + */ +static int +ar6k_ioctl_siwtxpow(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + u8 dbM; + + if (ar->arWlanState == WLAN_DISABLED) { + return -EIO; + } + + if (rrq->disabled) { + return -EOPNOTSUPP; + } + + if (rrq->fixed) { + if (rrq->flags != IW_TXPOW_DBM) { + return -EOPNOTSUPP; + } + ar->arTxPwr = dbM = rrq->value; + ar->arTxPwrSet = TRUE; + } else { + ar->arTxPwr = dbM = 0; + ar->arTxPwrSet = FALSE; + } + if (ar->arWmiReady == TRUE) { + AR_DEBUG_PRINTF("Set tx pwr cmd %d dbM\n", dbM); + AR6000_SPIN_LOCK(&ar->arLock, 0); + wmi_set_txPwr_cmd(ar->arWmi, dbM); + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + } + return 0; +} + +/* + * SIOCGIWTXPOW + */ +int +ar6k_ioctl_giwtxpow(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + int ret = 0; + + if (ar->arWlanState == WLAN_DISABLED) { + return -EIO; + } + + if (down_interruptible(&ar->arSem)) { + return -ERESTARTSYS; + } + if ((ar->arWmiReady == TRUE) && (ar->arConnected == TRUE)) { + ar->arTxPwr = 0; + + AR6000_SPIN_LOCK(&ar->arLock, 0); + if (wmi_get_txPwr_cmd(ar->arWmi) != A_OK) { + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + up(&ar->arSem); + return -EIO; + } + AR6000_SPIN_UNLOCK(&ar->arLock, 0); + + wait_event_interruptible(arEvent, ar->arTxPwr != 0); + + if (signal_pending(current)) { + ret = -EINTR; + } + } + /* If the interace is down or wmi is not ready or target is not + connected then return value stored in the device structure */ + + if (!ret) { + if (ar->arTxPwrSet == TRUE) { + rrq->fixed = TRUE; + } + rrq->value = ar->arTxPwr; + rrq->flags = IW_TXPOW_DBM; + } + + up(&ar->arSem); + + return ret; +} + +/* + * SIOCSIWRETRY + * since iwconfig only provides us with one max retry value, we use it + * to apply to data frames of the BE traffic class. + */ +static int +ar6k_ioctl_siwretry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + + if (ar->arWlanState == WLAN_DISABLED) { + return -EIO; + } + + if (rrq->disabled) { + return -EOPNOTSUPP; + } + + if ((rrq->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT) { + return -EOPNOTSUPP; + } + + if (!(rrq->value >= WMI_MIN_RETRIES) + || !(rrq->value <= WMI_MAX_RETRIES)) { + return -EINVAL; + } + if (ar->arWmiReady == TRUE) { + if (wmi_set_retry_limits_cmd(ar->arWmi, DATA_FRAMETYPE, WMM_AC_BE, + rrq->value) == A_OK) { + return -EINVAL; + } + } + ar->arMaxRetries = rrq->value; + return 0; +} + +/* + * SIOCGIWRETRY + */ +static int +ar6k_ioctl_giwretry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + + if (ar->arWlanState == WLAN_DISABLED) { + return -EIO; + } + + rrq->disabled = 0; + switch (rrq->flags & IW_RETRY_TYPE) { + case IW_RETRY_LIFETIME: + return -EOPNOTSUPP; + break; + case IW_RETRY_LIMIT: + rrq->flags = IW_RETRY_LIMIT; + switch (rrq->flags & IW_RETRY_MODIFIER) { + case IW_RETRY_MIN: + rrq->flags |= IW_RETRY_MIN; + rrq->value = WMI_MIN_RETRIES; + break; + case IW_RETRY_MAX: + rrq->flags |= IW_RETRY_MAX; + rrq->value = ar->arMaxRetries; + break; + } + break; + } + return 0; +} + +/* + * SIOCSIWENCODE + */ +static int +ar6k_ioctl_siwencode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, char *keybuf) +{ + AR_SOFTC_T *ar = (AR_SOFTC_T *) dev->priv; + int index; + DOT11_AUTH_MODE auth = ar->arDot11AuthMode; + /* + * Static WEP Keys should be configured before setting the SSID + */ + if (ar->arSsidLen) { + return -EIO; + } + + if (ar->arWlanState == WLAN_DISABLED) { + return -EIO; + } + + index = erq->flags & IW_ENCODE_INDEX; + + if (index && (((index - 1) < WMI_MIN_KEY_INDEX) || + ((index - 1) > WMI_MAX_KEY_INDEX))) { + return -EIO; + } + + if (erq->flags & IW_ENCODE_DISABLED) { + /* + * Encryption disabled + */ + if (index) { + /* + * If key index was specified then clear the specified key + */ + index--; + A_MEMZERO(ar->arWepKeyList[index].arKey, + sizeof(ar->arWepKeyList[index].arKey)); + ar->arWepKeyList[index].arKeyLen = 0; + } + ar->arDot11AuthMode = OPEN_AUTH; + ar->arPairwiseCrypto = NONE_CRYPT; + ar->arGroupCrypto = NONE_CRYPT; + ar->arAuthMode = NONE_AUTH; + } else { + /* + * Enabling WEP encryption + */ + if (index) { + index--; /* keyindex is off base 1 in iwconfig */ + } + + if (erq->flags & IW_ENCODE_OPEN) { + auth = OPEN_AUTH; + } else if (erq->flags & IW_ENCODE_RESTRICTED) { + auth = SHARED_AUTH; + } + + if (erq->length) { + if (!IEEE80211_IS_VALID_WEP_CIPHER_LEN(erq->length)) { + return -EIO; + } + + A_MEMZERO(ar->arWepKeyList[index].arKey, + sizeof(ar->arWepKeyList[index].arKey)); + A_MEMCPY(ar->arWepKeyList[index].arKey, keybuf, erq->length); + ar->arWepKeyList[index].arKeyLen = erq->length; + } else { + if (ar->arWepKeyList[index].arKeyLen == 0) { + return -EIO; + } + ar->arDefTxKeyIndex = index; + } + + ar->arPair