summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/ar9170
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath/ar9170')
-rw-r--r--drivers/net/wireless/ath/ar9170/Kconfig18
-rw-r--r--drivers/net/wireless/ath/ar9170/Makefile3
-rw-r--r--drivers/net/wireless/ath/ar9170/ar9170.h257
-rw-r--r--drivers/net/wireless/ath/ar9170/cmd.c129
-rw-r--r--drivers/net/wireless/ath/ar9170/cmd.h91
-rw-r--r--drivers/net/wireless/ath/ar9170/eeprom.h179
-rw-r--r--drivers/net/wireless/ath/ar9170/hw.h426
-rw-r--r--drivers/net/wireless/ath/ar9170/led.c178
-rw-r--r--drivers/net/wireless/ath/ar9170/mac.c524
-rw-r--r--drivers/net/wireless/ath/ar9170/main.c2210
-rw-r--r--drivers/net/wireless/ath/ar9170/phy.c1240
-rw-r--r--drivers/net/wireless/ath/ar9170/usb.c929
-rw-r--r--drivers/net/wireless/ath/ar9170/usb.h81
13 files changed, 6265 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ar9170/Kconfig b/drivers/net/wireless/ath/ar9170/Kconfig
new file mode 100644
index 00000000000..b99e3263ee6
--- /dev/null
+++ b/drivers/net/wireless/ath/ar9170/Kconfig
@@ -0,0 +1,18 @@
+config AR9170_USB
+ tristate "Atheros AR9170 802.11n USB support"
+ depends on USB && MAC80211 && WLAN_80211 && EXPERIMENTAL
+ select FW_LOADER
+ select ATH_COMMON
+ help
+ This is a driver for the Atheros "otus" 802.11n USB devices.
+
+ These devices require additional firmware (2 files).
+ For now, these files can be downloaded from here:
+ http://wireless.kernel.org/en/users/Drivers/ar9170
+
+ If you choose to build a module, it'll be called ar9170usb.
+
+config AR9170_LEDS
+ bool
+ depends on AR9170_USB && MAC80211_LEDS && (LEDS_CLASS = y || LEDS_CLASS = AR9170_USB)
+ default y
diff --git a/drivers/net/wireless/ath/ar9170/Makefile b/drivers/net/wireless/ath/ar9170/Makefile
new file mode 100644
index 00000000000..8d91c7ee321
--- /dev/null
+++ b/drivers/net/wireless/ath/ar9170/Makefile
@@ -0,0 +1,3 @@
+ar9170usb-objs := usb.o main.o cmd.o mac.o phy.o led.o
+
+obj-$(CONFIG_AR9170_USB) += ar9170usb.o
diff --git a/drivers/net/wireless/ath/ar9170/ar9170.h b/drivers/net/wireless/ath/ar9170/ar9170.h
new file mode 100644
index 00000000000..bb97981fb24
--- /dev/null
+++ b/drivers/net/wireless/ath/ar9170/ar9170.h
@@ -0,0 +1,257 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * Driver specific definitions
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef __AR9170_H
+#define __AR9170_H
+
+#include <linux/completion.h>
+#include <linux/spinlock.h>
+#include <net/cfg80211.h>
+#include <net/mac80211.h>
+#ifdef CONFIG_AR9170_LEDS
+#include <linux/leds.h>
+#endif /* CONFIG_AR9170_LEDS */
+#include "eeprom.h"
+#include "hw.h"
+
+#include "../regd.h"
+
+#define PAYLOAD_MAX (AR9170_MAX_CMD_LEN/4 - 1)
+
+enum ar9170_bw {
+ AR9170_BW_20,
+ AR9170_BW_40_BELOW,
+ AR9170_BW_40_ABOVE,
+
+ __AR9170_NUM_BW,
+};
+
+static inline enum ar9170_bw nl80211_to_ar9170(enum nl80211_channel_type type)
+{
+ switch (type) {
+ case NL80211_CHAN_NO_HT:
+ case NL80211_CHAN_HT20:
+ return AR9170_BW_20;
+ case NL80211_CHAN_HT40MINUS:
+ return AR9170_BW_40_BELOW;
+ case NL80211_CHAN_HT40PLUS:
+ return AR9170_BW_40_ABOVE;
+ default:
+ BUG();
+ }
+}
+
+enum ar9170_rf_init_mode {
+ AR9170_RFI_NONE,
+ AR9170_RFI_WARM,
+ AR9170_RFI_COLD,
+};
+
+#define AR9170_MAX_RX_BUFFER_SIZE 8192
+
+#ifdef CONFIG_AR9170_LEDS
+struct ar9170;
+
+struct ar9170_led {
+ struct ar9170 *ar;
+ struct led_classdev l;
+ char name[32];
+ unsigned int toggled;
+ bool last_state;
+ bool registered;
+};
+
+#endif /* CONFIG_AR9170_LEDS */
+
+enum ar9170_device_state {
+ AR9170_UNKNOWN_STATE,
+ AR9170_STOPPED,
+ AR9170_IDLE,
+ AR9170_STARTED,
+};
+
+struct ar9170_rxstream_mpdu_merge {
+ struct ar9170_rx_head plcp;
+ bool has_plcp;
+};
+
+#define AR9170_QUEUE_TIMEOUT 64
+#define AR9170_TX_TIMEOUT 8
+#define AR9170_JANITOR_DELAY 128
+#define AR9170_TX_INVALID_RATE 0xffffffff
+
+struct ar9170 {
+ struct ieee80211_hw *hw;
+ struct mutex mutex;
+ enum ar9170_device_state state;
+ unsigned long bad_hw_nagger;
+
+ int (*open)(struct ar9170 *);
+ void (*stop)(struct ar9170 *);
+ int (*tx)(struct ar9170 *, struct sk_buff *);
+ int (*exec_cmd)(struct ar9170 *, enum ar9170_cmd, u32 ,
+ void *, u32 , void *);
+ void (*callback_cmd)(struct ar9170 *, u32 , void *);
+ int (*flush)(struct ar9170 *);
+
+ /* interface mode settings */
+ struct ieee80211_vif *vif;
+ u8 mac_addr[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+
+ /* beaconing */
+ struct sk_buff *beacon;
+ struct work_struct beacon_work;
+
+ /* cryptographic engine */
+ u64 usedkeys;
+ bool rx_software_decryption;
+ bool disable_offload;
+
+ /* filter settings */
+ struct work_struct filter_config_work;
+ u64 cur_mc_hash, want_mc_hash;
+ u32 cur_filter, want_filter;
+ unsigned long filter_changed;
+ unsigned int filter_state;
+ bool sniffer_enabled;
+
+ /* PHY */
+ struct ieee80211_channel *channel;
+ int noise[4];
+
+ /* power calibration data */
+ u8 power_5G_leg[4];
+ u8 power_2G_cck[4];
+ u8 power_2G_ofdm[4];
+ u8 power_5G_ht20[8];
+ u8 power_5G_ht40[8];
+ u8 power_2G_ht20[8];
+ u8 power_2G_ht40[8];
+
+#ifdef CONFIG_AR9170_LEDS
+ struct delayed_work led_work;
+ struct ar9170_led leds[AR9170_NUM_LEDS];
+#endif /* CONFIG_AR9170_LEDS */
+
+ /* qos queue settings */
+ spinlock_t tx_stats_lock;
+ struct ieee80211_tx_queue_stats tx_stats[5];
+ struct ieee80211_tx_queue_params edcf[5];
+
+ spinlock_t cmdlock;
+ __le32 cmdbuf[PAYLOAD_MAX + 1];
+
+ /* MAC statistics */
+ struct ieee80211_low_level_stats stats;
+
+ /* EEPROM */
+ struct ar9170_eeprom eeprom;
+ struct ath_regulatory regulatory;
+
+ /* tx queues - as seen by hw - */
+ struct sk_buff_head tx_pending[__AR9170_NUM_TXQ];
+ struct sk_buff_head tx_status[__AR9170_NUM_TXQ];
+ struct delayed_work tx_janitor;
+
+ /* rxstream mpdu merge */
+ struct ar9170_rxstream_mpdu_merge rx_mpdu;
+ struct sk_buff *rx_failover;
+ int rx_failover_missing;
+};
+
+struct ar9170_sta_info {
+};
+
+#define AR9170_TX_FLAG_WAIT_FOR_ACK BIT(0)
+#define AR9170_TX_FLAG_NO_ACK BIT(1)
+#define AR9170_TX_FLAG_BLOCK_ACK BIT(2)
+
+struct ar9170_tx_info {
+ unsigned long timeout;
+ unsigned int flags;
+};
+
+#define IS_STARTED(a) (((struct ar9170 *)a)->state >= AR9170_STARTED)
+#define IS_ACCEPTING_CMD(a) (((struct ar9170 *)a)->state >= AR9170_IDLE)
+
+#define AR9170_FILTER_CHANGED_MODE BIT(0)
+#define AR9170_FILTER_CHANGED_MULTICAST BIT(1)
+#define AR9170_FILTER_CHANGED_FRAMEFILTER BIT(2)
+
+/* exported interface */
+void *ar9170_alloc(size_t priv_size);
+int ar9170_register(struct ar9170 *ar, struct device *pdev);
+void ar9170_rx(struct ar9170 *ar, struct sk_buff *skb);
+void ar9170_unregister(struct ar9170 *ar);
+void ar9170_tx_callback(struct ar9170 *ar, struct sk_buff *skb);
+void ar9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len);
+int ar9170_nag_limiter(struct ar9170 *ar);
+
+/* MAC */
+int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
+int ar9170_init_mac(struct ar9170 *ar);
+int ar9170_set_qos(struct ar9170 *ar);
+int ar9170_update_multicast(struct ar9170 *ar);
+int ar9170_update_frame_filter(struct ar9170 *ar);
+int ar9170_set_operating_mode(struct ar9170 *ar);
+int ar9170_set_beacon_timers(struct ar9170 *ar);
+int ar9170_set_dyn_sifs_ack(struct ar9170 *ar);
+int ar9170_set_slot_time(struct ar9170 *ar);
+int ar9170_set_basic_rates(struct ar9170 *ar);
+int ar9170_set_hwretry_limit(struct ar9170 *ar, u32 max_retry);
+int ar9170_update_beacon(struct ar9170 *ar);
+void ar9170_new_beacon(struct work_struct *work);
+int ar9170_upload_key(struct ar9170 *ar, u8 id, const u8 *mac, u8 ktype,
+ u8 keyidx, u8 *keydata, int keylen);
+int ar9170_disable_key(struct ar9170 *ar, u8 id);
+
+/* LEDs */
+#ifdef CONFIG_AR9170_LEDS
+int ar9170_register_leds(struct ar9170 *ar);
+void ar9170_unregister_leds(struct ar9170 *ar);
+#endif /* CONFIG_AR9170_LEDS */
+int ar9170_init_leds(struct ar9170 *ar);
+int ar9170_set_leds_state(struct ar9170 *ar, u32 led_state);
+
+/* PHY / RF */
+int ar9170_init_phy(struct ar9170 *ar, enum ieee80211_band band);
+int ar9170_init_rf(struct ar9170 *ar);
+int ar9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
+ enum ar9170_rf_init_mode rfi, enum ar9170_bw bw);
+
+#endif /* __AR9170_H */
diff --git a/drivers/net/wireless/ath/ar9170/cmd.c b/drivers/net/wireless/ath/ar9170/cmd.c
new file mode 100644
index 00000000000..f57a6200167
--- /dev/null
+++ b/drivers/net/wireless/ath/ar9170/cmd.c
@@ -0,0 +1,129 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * Basic HW register/memory/command access functions
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ar9170.h"
+#include "cmd.h"
+
+int ar9170_write_mem(struct ar9170 *ar, const __le32 *data, size_t len)
+{
+ int err;
+
+ if (unlikely(!IS_ACCEPTING_CMD(ar)))
+ return 0;
+
+ err = ar->exec_cmd(ar, AR9170_CMD_WMEM, len, (u8 *) data, 0, NULL);
+ if (err)
+ printk(KERN_DEBUG "%s: writing memory failed\n",
+ wiphy_name(ar->hw->wiphy));
+ return err;
+}
+
+int ar9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val)
+{
+ __le32 buf[2] = {
+ cpu_to_le32(reg),
+ cpu_to_le32(val),
+ };
+ int err;
+
+ if (unlikely(!IS_ACCEPTING_CMD(ar)))
+ return 0;
+
+ err = ar->exec_cmd(ar, AR9170_CMD_WREG, sizeof(buf),
+ (u8 *) buf, 0, NULL);
+ if (err)
+ printk(KERN_DEBUG "%s: writing reg %#x (val %#x) failed\n",
+ wiphy_name(ar->hw->wiphy), reg, val);
+ return err;
+}
+
+static int ar9170_read_mreg(struct ar9170 *ar, int nregs,
+ const u32 *regs, u32 *out)
+{
+ int i, err;
+ __le32 *offs, *res;
+
+ if (unlikely(!IS_ACCEPTING_CMD(ar)))
+ return 0;
+
+ /* abuse "out" for the register offsets, must be same length */
+ offs = (__le32 *)out;
+ for (i = 0; i < nregs; i++)
+ offs[i] = cpu_to_le32(regs[i]);
+
+ /* also use the same buffer for the input */
+ res = (__le32 *)out;
+
+ err = ar->exec_cmd(ar, AR9170_CMD_RREG,
+ 4 * nregs, (u8 *)offs,
+ 4 * nregs, (u8 *)res);
+ if (err)
+ return err;
+
+ /* convert result to cpu endian */
+ for (i = 0; i < nregs; i++)
+ out[i] = le32_to_cpu(res[i]);
+
+ return 0;
+}
+
+int ar9170_read_reg(struct ar9170 *ar, u32 reg, u32 *val)
+{
+ return ar9170_read_mreg(ar, 1, &reg, val);
+}
+
+int ar9170_echo_test(struct ar9170 *ar, u32 v)
+{
+ __le32 echobuf = cpu_to_le32(v);
+ __le32 echores;
+ int err;
+
+ if (unlikely(!IS_ACCEPTING_CMD(ar)))
+ return -ENODEV;
+
+ err = ar->exec_cmd(ar, AR9170_CMD_ECHO,
+ 4, (u8 *)&echobuf,
+ 4, (u8 *)&echores);
+ if (err)
+ return err;
+
+ if (echobuf != echores)
+ return -EINVAL;
+
+ return 0;
+}
diff --git a/drivers/net/wireless/ath/ar9170/cmd.h b/drivers/net/wireless/ath/ar9170/cmd.h
new file mode 100644
index 00000000000..a4f0e50e52b
--- /dev/null
+++ b/drivers/net/wireless/ath/ar9170/cmd.h
@@ -0,0 +1,91 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * Basic HW register/memory/command access functions
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef __CMD_H
+#define __CMD_H
+
+#include "ar9170.h"
+
+/* basic HW access */
+int ar9170_write_mem(struct ar9170 *ar, const __le32 *data, size_t len);
+int ar9170_write_reg(struct ar9170 *ar, const u32 reg, const u32 val);
+int ar9170_read_reg(struct ar9170 *ar, u32 reg, u32 *val);
+int ar9170_echo_test(struct ar9170 *ar, u32 v);
+
+/*
+ * Macros to facilitate writing multiple registers in a single
+ * write-combining USB command. Note that when the first group
+ * fails the whole thing will fail without any others attempted,
+ * but you won't know which write in the group failed.
+ */
+#define ar9170_regwrite_begin(ar) \
+do { \
+ int __nreg = 0, __err = 0; \
+ struct ar9170 *__ar = ar;
+
+#define ar9170_regwrite(r, v) do { \
+ __ar->cmdbuf[2 * __nreg + 1] = cpu_to_le32(r); \
+ __ar->cmdbuf[2 * __nreg + 2] = cpu_to_le32(v); \
+ __nreg++; \
+ if ((__nreg >= PAYLOAD_MAX/2)) { \
+ if (IS_ACCEPTING_CMD(__ar)) \
+ __err = ar->exec_cmd(__ar, AR9170_CMD_WREG, \
+ 8 * __nreg, \
+ (u8 *) &__ar->cmdbuf[1], \
+ 0, NULL); \
+ __nreg = 0; \
+ if (__err) \
+ goto __regwrite_out; \
+ } \
+} while (0)
+
+#define ar9170_regwrite_finish() \
+__regwrite_out : \
+ if (__nreg) { \
+ if (IS_ACCEPTING_CMD(__ar)) \
+ __err = ar->exec_cmd(__ar, AR9170_CMD_WREG, \
+ 8 * __nreg, \
+ (u8 *) &__ar->cmdbuf[1], \
+ 0, NULL); \
+ __nreg = 0; \
+ }
+
+#define ar9170_regwrite_result() \
+ __err; \
+} while (0);
+
+#endif /* __CMD_H */
diff --git a/drivers/net/wireless/ath/ar9170/eeprom.h b/drivers/net/wireless/ath/ar9170/eeprom.h
new file mode 100644
index 00000000000..d2c8cc83f1d
--- /dev/null
+++ b/drivers/net/wireless/ath/ar9170/eeprom.h
@@ -0,0 +1,179 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * EEPROM layout
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef __AR9170_EEPROM_H
+#define __AR9170_EEPROM_H
+
+#define AR5416_MAX_CHAINS 2
+#define AR5416_MODAL_SPURS 5
+
+struct ar9170_eeprom_modal {
+ __le32 antCtrlChain[AR5416_MAX_CHAINS];
+ __le32 antCtrlCommon;
+ s8 antennaGainCh[AR5416_MAX_CHAINS];
+ u8 switchSettling;
+ u8 txRxAttenCh[AR5416_MAX_CHAINS];
+ u8 rxTxMarginCh[AR5416_MAX_CHAINS];
+ s8 adcDesiredSize;
+ s8 pgaDesiredSize;
+ u8 xlnaGainCh[AR5416_MAX_CHAINS];
+ u8 txEndToXpaOff;
+ u8 txEndToRxOn;
+ u8 txFrameToXpaOn;
+ u8 thresh62;
+ s8 noiseFloorThreshCh[AR5416_MAX_CHAINS];
+ u8 xpdGain;
+ u8 xpd;
+ s8 iqCalICh[AR5416_MAX_CHAINS];
+ s8 iqCalQCh[AR5416_MAX_CHAINS];
+ u8 pdGainOverlap;
+ u8 ob;
+ u8 db;
+ u8 xpaBiasLvl;
+ u8 pwrDecreaseFor2Chain;
+ u8 pwrDecreaseFor3Chain;
+ u8 txFrameToDataStart;
+ u8 txFrameToPaOn;
+ u8 ht40PowerIncForPdadc;
+ u8 bswAtten[AR5416_MAX_CHAINS];
+ u8 bswMargin[AR5416_MAX_CHAINS];
+ u8 swSettleHt40;
+ u8 reserved[22];
+ struct spur_channel {
+ __le16 spurChan;
+ u8 spurRangeLow;
+ u8 spurRangeHigh;
+ } __packed spur_channels[AR5416_MODAL_SPURS];
+} __packed;
+
+#define AR5416_NUM_PD_GAINS 4
+#define AR5416_PD_GAIN_ICEPTS 5
+
+struct ar9170_calibration_data_per_freq {
+ u8 pwr_pdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS];
+ u8 vpd_pdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS];
+} __packed;
+
+#define AR5416_NUM_5G_CAL_PIERS 8
+#define AR5416_NUM_2G_CAL_PIERS 4
+
+#define AR5416_NUM_5G_TARGET_PWRS 8
+#define AR5416_NUM_2G_CCK_TARGET_PWRS 3
+#define AR5416_NUM_2G_OFDM_TARGET_PWRS 4
+#define AR5416_MAX_NUM_TGT_PWRS 8
+
+struct ar9170_calibration_target_power_legacy {
+ u8 freq;
+ u8 power[4];
+} __packed;
+
+struct ar9170_calibration_target_power_ht {
+ u8 freq;
+ u8 power[8];
+} __packed;
+
+#define AR5416_NUM_CTLS 24
+
+struct ar9170_calctl_edges {
+ u8 channel;
+#define AR9170_CALCTL_EDGE_FLAGS 0xC0
+ u8 power_flags;
+} __packed;
+
+#define AR5416_NUM_BAND_EDGES 8
+
+struct ar9170_calctl_data {
+ struct ar9170_calctl_edges
+ control_edges[AR5416_MAX_CHAINS][AR5416_NUM_BAND_EDGES];
+} __packed;
+
+
+struct ar9170_eeprom {
+ __le16 length;
+ __le16 checksum;
+ __le16 version;
+ u8 operating_flags;
+#define AR9170_OPFLAG_5GHZ 1
+#define AR9170_OPFLAG_2GHZ 2
+ u8 misc;
+ __le16 reg_domain[2];
+ u8 mac_address[6];
+ u8 rx_mask;
+ u8 tx_mask;
+ __le16 rf_silent;
+ __le16 bluetooth_options;
+ __le16 device_capabilities;
+ __le32 build_number;
+ u8 deviceType;
+ u8 reserved[33];
+
+ u8 customer_data[64];
+
+ struct ar9170_eeprom_modal
+ modal_header[2];
+
+ u8 cal_freq_pier_5G[AR5416_NUM_5G_CAL_PIERS];
+ u8 cal_freq_pier_2G[AR5416_NUM_2G_CAL_PIERS];
+
+ struct ar9170_calibration_data_per_freq
+ cal_pier_data_5G[AR5416_MAX_CHAINS][AR5416_NUM_5G_CAL_PIERS],
+ cal_pier_data_2G[AR5416_MAX_CHAINS][AR5416_NUM_2G_CAL_PIERS];
+
+ /* power calibration data */
+ struct ar9170_calibration_target_power_legacy
+ cal_tgt_pwr_5G[AR5416_NUM_5G_TARGET_PWRS];
+ struct ar9170_calibration_target_power_ht
+ cal_tgt_pwr_5G_ht20[AR5416_NUM_5G_TARGET_PWRS],
+ cal_tgt_pwr_5G_ht40[AR5416_NUM_5G_TARGET_PWRS];
+
+ struct ar9170_calibration_target_power_legacy
+ cal_tgt_pwr_2G_cck[AR5416_NUM_2G_CCK_TARGET_PWRS],
+ cal_tgt_pwr_2G_ofdm[AR5416_NUM_2G_OFDM_TARGET_PWRS];
+ struct ar9170_calibration_target_power_ht
+ cal_tgt_pwr_2G_ht20[AR5416_NUM_2G_OFDM_TARGET_PWRS],
+ cal_tgt_pwr_2G_ht40[AR5416_NUM_2G_OFDM_TARGET_PWRS];
+
+ /* conformance testing limits */
+ u8 ctl_index[AR5416_NUM_CTLS];
+ struct ar9170_calctl_data
+ ctl_data[AR5416_NUM_CTLS];
+
+ u8 pad;
+ __le16 subsystem_id;
+} __packed;
+
+#endif /* __AR9170_EEPROM_H */
diff --git a/drivers/net/wireless/ath/ar9170/hw.h b/drivers/net/wireless/ath/ar9170/hw.h
new file mode 100644
index 00000000000..6cbfb2f8339
--- /dev/null
+++ b/drivers/net/wireless/ath/ar9170/hw.h
@@ -0,0 +1,426 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * Hardware-specific definitions
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef __AR9170_HW_H
+#define __AR9170_HW_H
+
+#define AR9170_MAX_CMD_LEN 64
+
+enum ar9170_cmd {
+ AR9170_CMD_RREG = 0x00,
+ AR9170_CMD_WREG = 0x01,
+ AR9170_CMD_RMEM = 0x02,
+ AR9170_CMD_WMEM = 0x03,
+ AR9170_CMD_BITAND = 0x04,
+ AR9170_CMD_BITOR = 0x05,
+ AR9170_CMD_EKEY = 0x28,
+ AR9170_CMD_DKEY = 0x29,
+ AR9170_CMD_FREQUENCY = 0x30,
+ AR9170_CMD_RF_INIT = 0x31,
+ AR9170_CMD_SYNTH = 0x32,
+ AR9170_CMD_FREQ_START = 0x33,
+ AR9170_CMD_ECHO = 0x80,
+ AR9170_CMD_TALLY = 0x81,
+ AR9170_CMD_TALLY_APD = 0x82,
+ AR9170_CMD_CONFIG = 0x83,
+ AR9170_CMD_RESET = 0x90,
+ AR9170_CMD_DKRESET = 0x91,
+ AR9170_CMD_DKTX_STATUS = 0x92,
+ AR9170_CMD_FDC = 0xA0,
+ AR9170_CMD_WREEPROM = 0xB0,
+ AR9170_CMD_WFLASH = 0xB0,
+ AR9170_CMD_FLASH_ERASE = 0xB1,
+ AR9170_CMD_FLASH_PROG = 0xB2,
+ AR9170_CMD_FLASH_CHKSUM = 0xB3,
+ AR9170_CMD_FLASH_READ = 0xB4,
+ AR9170_CMD_FW_DL_INIT = 0xB5,
+ AR9170_CMD_MEM_WREEPROM = 0xBB,
+};
+
+/* endpoints */
+#define AR9170_EP_TX 1
+#define AR9170_EP_RX 2
+#define AR9170_EP_IRQ 3
+#define AR9170_EP_CMD 4
+
+#define AR9170_EEPROM_START 0x1600
+
+#define AR9170_GPIO_REG_BASE 0x1d0100
+#define AR9170_GPIO_REG_PORT_TYPE AR9170_GPIO_REG_BASE
+#define AR9170_GPIO_REG_DATA (AR9170_GPIO_REG_BASE + 4)
+#define AR9170_NUM_LEDS 2
+
+
+#define AR9170_USB_REG_BASE 0x1e1000
+#define AR9170_USB_REG_DMA_CTL (AR9170_USB_REG_BASE + 0x108)
+#define AR9170_DMA_CTL_ENABLE_TO_DEVICE 0x1
+#define AR9170_DMA_CTL_ENABLE_FROM_DEVICE 0x2
+#define AR9170_DMA_CTL_HIGH_SPEED 0x4
+#define AR9170_DMA_CTL_PACKET_MODE 0x8
+
+#define AR9170_USB_REG_MAX_AGG_UPLOAD (AR9170_USB_REG_BASE + 0x110)
+#define AR9170_USB_REG_UPLOAD_TIME_CTL (AR9170_USB_REG_BASE + 0x114)
+
+
+
+#define AR9170_MAC_REG_BASE 0x1c3000
+
+#define AR9170_MAC_REG_TSF_L (AR9170_MAC_REG_BASE + 0x514)
+#define AR9170_MAC_REG_TSF_H (AR9170_MAC_REG_BASE + 0x518)
+
+#define AR9170_MAC_REG_ATIM_WINDOW (AR9170_MAC_REG_BASE + 0x51C)
+#define AR9170_MAC_REG_BCN_PERIOD (AR9170_MAC_REG_BASE + 0x520)
+#define AR9170_MAC_REG_PRETBTT (AR9170_MAC_REG_BASE + 0x524)
+
+#define AR9170_MAC_REG_MAC_ADDR_L (AR9170_MAC_REG_BASE + 0x610)
+#define AR9170_MAC_REG_MAC_ADDR_H (AR9170_MAC_REG_BASE + 0x614)
+#define AR9170_MAC_REG_BSSID_L (AR9170_MAC_REG_BASE + 0x618)
+#define AR9170_MAC_REG_BSSID_H (AR9170_MAC_REG_BASE + 0x61c)
+
+#define AR9170_MAC_REG_GROUP_HASH_TBL_L (AR9170_MAC_REG_BASE + 0x624)
+#define AR9170_MAC_REG_GROUP_HASH_TBL_H (AR9170_MAC_REG_BASE + 0x628)
+
+#define AR9170_MAC_REG_RX_TIMEOUT (AR9170_MAC_REG_BASE + 0x62C)
+
+#define AR9170_MAC_REG_BASIC_RATE (AR9170_MAC_REG_BASE + 0x630)
+#define AR9170_MAC_REG_MANDATORY_RATE (AR9170_MAC_REG_BASE + 0x634)
+#define AR9170_MAC_REG_RTS_CTS_RATE (AR9170_MAC_REG_BASE + 0x638)
+#define AR9170_MAC_REG_BACKOFF_PROTECT (AR9170_MAC_REG_BASE + 0x63c)
+#define AR9170_MAC_REG_RX_THRESHOLD (AR9170_MAC_REG_BASE + 0x640)
+#define AR9170_MAC_REG_RX_PE_DELAY (AR9170_MAC_REG_BASE + 0x64C)
+
+#define AR9170_MAC_REG_DYNAMIC_SIFS_ACK (AR9170_MAC_REG_BASE + 0x658)
+#define AR9170_MAC_REG_SNIFFER (AR9170_MAC_REG_BASE + 0x674)
+#define AR9170_MAC_REG_SNIFFER_ENABLE_PROMISC BIT(0)
+#define AR9170_MAC_REG_SNIFFER_DEFAULTS 0x02000000
+#define AR9170_MAC_REG_ENCRYPTION (AR9170_MAC_REG_BASE + 0x678)
+#define AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE BIT(3)
+#define AR9170_MAC_REG_ENCRYPTION_DEFAULTS 0x70
+
+#define AR9170_MAC_REG_MISC_680 (AR9170_MAC_REG_BASE + 0x680)
+#define AR9170_MAC_REG_TX_UNDERRUN (AR9170_MAC_REG_BASE + 0x688)
+
+#define AR9170_MAC_REG_FRAMETYPE_FILTER (AR9170_MAC_REG_BASE + 0x68c)
+#define AR9170_MAC_REG_FTF_ASSOC_REQ BIT(0)
+#define AR9170_MAC_REG_FTF_ASSOC_RESP BIT(1)
+#define AR9170_MAC_REG_FTF_REASSOC_REQ BIT(2)
+#define AR9170_MAC_REG_FTF_REASSOC_RESP BIT(3)
+#define AR9170_MAC_REG_FTF_PRB_REQ BIT(4)
+#define AR9170_MAC_REG_FTF_PRB_RESP BIT(5)
+#define AR9170_MAC_REG_FTF_BIT6 BIT(6)
+#define AR9170_MAC_REG_FTF_BIT7 BIT(7)
+#define AR9170_MAC_REG_FTF_BEACON BIT(8)
+#define AR9170_MAC_REG_FTF_ATIM BIT(9)
+#define AR9170_MAC_REG_FTF_DEASSOC BIT(10)
+#define AR9170_MAC_REG_FTF_AUTH BIT(11)
+#define AR9170_MAC_REG_FTF_DEAUTH BIT(12)
+#define AR9170_MAC_REG_FTF_BIT13 BIT(13)
+#define AR9170_MAC_REG_FTF_BIT14 BIT(14)
+#define AR9170_MAC_REG_FTF_BIT15 BIT(15)
+#define AR9170_MAC_REG_FTF_BAR BIT(24)
+#define AR9170_MAC_REG_FTF_BIT25 BIT(25)
+#define AR9170_MAC_REG_FTF_PSPOLL BIT(26)
+#define AR9170_MAC_REG_FTF_RTS BIT(27)
+#define AR9170_MAC_REG_FTF_CTS BIT(28)
+#define AR9170_MAC_REG_FTF_ACK BIT(29)
+#define AR9170_MAC_REG_FTF_CFE BIT(30)
+#define AR9170_MAC_REG_FTF_CFE_ACK BIT(31)
+#define AR9170_MAC_REG_FTF_DEFAULTS 0x0500ffff
+#define AR9170_MAC_REG_FTF_MONITOR 0xfd00ffff
+
+#define AR9170_MAC_REG_RX_TOTAL (AR9170_MAC_REG_BASE + 0x6A0)
+#define AR9170_MAC_REG_RX_CRC32 (AR9170_MAC_REG_BASE + 0x6A4)
+#define AR9170_MAC_REG_RX_CRC16 (AR9170_MAC_REG_BASE + 0x6A8)
+#define AR9170_MAC_REG_RX_ERR_DECRYPTION_UNI (AR9170_MAC_REG_BASE + 0x6AC)
+#define AR9170_MAC_REG_RX_OVERRUN (AR9170_MAC_REG_BASE + 0x6B0)
+#define AR9170_MAC_REG_RX_ERR_DECRYPTION_MUL (AR9170_MAC_REG_BASE + 0x6BC)
+#define AR9170_MAC_REG_TX_RETRY (AR9170_MAC_REG_BASE + 0x6CC)
+#define AR9170_MAC_REG_TX_TOTAL (AR9170_MAC_REG_BASE + 0x6F4)
+
+
+#define AR9170_MAC_REG_ACK_EXTENSION (AR9170_MAC_REG_BASE + 0x690)
+#define AR9170_MAC_REG_EIFS_AND_SIFS (AR9170_MAC_REG_BASE + 0x698)
+
+#define AR9170_MAC_REG_SLOT_TIME (AR9170_MAC_REG_BASE + 0x6F0)
+
+#define AR9170_MAC_REG_POWERMANAGEMENT (AR9170_MAC_REG_BASE + 0x700)
+#define AR9170_MAC_REG_POWERMGT_IBSS 0xe0
+#define AR9170_MAC_REG_POWERMGT_AP 0xa1
+#define AR9170_MAC_REG_POWERMGT_STA 0x2
+#define AR9170_MAC_REG_POWERMGT_AP_WDS 0x3
+#define AR9170_MAC_REG_POWERMGT_DEFAULTS (0xf << 24)
+
+#define AR9170_MAC_REG_ROLL_CALL_TBL_L (AR9170_MAC_REG_BASE + 0x704)
+#define AR9170_MAC_REG_ROLL_CALL_TBL_H (AR9170_MAC_REG_BASE + 0x708)
+
+#define AR9170_MAC_REG_AC0_CW (AR9170_MAC_REG_BASE + 0xB00)
+#define AR9170_MAC_REG_AC1_CW (AR9170_MAC_REG_BASE + 0xB04)
+#define AR9170_MAC_REG_AC2_CW (AR9170_MAC_REG_BASE + 0xB08)
+#define AR9170_MAC_REG_AC3_CW (AR9170_MAC_REG_BASE + 0xB0C)
+#define AR9170_MAC_REG_AC4_CW (AR9170_MAC_REG_BASE + 0xB10)
+#define AR9170_MAC_REG_AC1_AC0_AIFS (AR9170_MAC_REG_BASE + 0xB14)
+#define AR9170_MAC_REG_AC3_AC2_AIFS (AR9170_MAC_REG_BASE + 0xB18)
+
+#define AR9170_MAC_REG_RETRY_MAX (AR9170_MAC_REG_BASE + 0xB28)
+
+#define AR9170_MAC_REG_FCS_SELECT (AR9170_MAC_REG_BASE + 0xBB0)
+#define AR9170_MAC_FCS_SWFCS 0x1
+#define AR9170_MAC_FCS_FIFO_PROT 0x4
+
+
+#define AR9170_MAC_REG_TXOP_NOT_ENOUGH_IND (AR9170_MAC_REG_BASE + 0xB30)
+
+#define AR9170_MAC_REG_AC1_AC0_TXOP (AR9170_MAC_REG_BASE + 0xB44)
+#define AR9170_MAC_REG_AC3_AC2_TXOP (AR9170_MAC_REG_BASE + 0xB48)
+
+#define AR9170_MAC_REG_AMPDU_FACTOR (AR9170_MAC_REG_BASE + 0xB9C)
+#define AR9170_MAC_REG_AMPDU_DENSITY (AR9170_MAC_REG_BASE + 0xBA0)
+
+#define AR9170_MAC_REG_ACK_TABLE (AR9170_MAC_REG_BASE + 0xC00)
+#define AR9170_MAC_REG_AMPDU_RX_THRESH (AR9170_MAC_REG_BASE + 0xC50)
+
+#define AR9170_MAC_REG_TXRX_MPI (AR9170_MAC_REG_BASE + 0xD7C)
+#define AR9170_MAC_TXRX_MPI_TX_MPI_MASK 0x0000000f
+#define AR9170_MAC_TXRX_MPI_TX_TO_MASK 0x0000fff0
+#define AR9170_MAC_TXRX_MPI_RX_MPI_MASK 0x000f0000
+#define AR9170_MAC_TXRX_MPI_RX_TO_MASK 0xfff00000
+
+#define AR9170_MAC_REG_BCN_ADDR (AR9170_MAC_REG_BASE + 0xD84)
+#define AR9170_MAC_REG_BCN_LENGTH (AR9170_MAC_REG_BASE + 0xD88)
+#define AR9170_MAC_REG_BCN_PLCP (AR9170_MAC_REG_BASE + 0xD90)
+#define AR9170_MAC_REG_BCN_CTRL (AR9170_MAC_REG_BASE + 0xD94)
+#define AR9170_MAC_REG_BCN_HT1 (AR9170_MAC_REG_BASE + 0xDA0)
+#define AR9170_MAC_REG_BCN_HT2 (AR9170_MAC_REG_BASE + 0xDA4)
+
+
+#define AR9170_PWR_REG_BASE 0x1D4000
+
+#define AR9170_PWR_REG_CLOCK_SEL (AR9170_PWR_REG_BASE + 0x008)
+#define AR9170_PWR_CLK_AHB_40MHZ 0
+#define AR9170_PWR_CLK_AHB_20_22MHZ 1
+#define AR9170_PWR_CLK_AHB_40_44MHZ 2
+#define AR9170_PWR_CLK_AHB_80_88MHZ 3
+#define AR9170_PWR_CLK_DAC_160_INV_DLY 0x70
+
+
+/* put beacon here in memory */
+#define AR9170_BEACON_BUFFER_ADDRESS 0x117900
+
+
+struct ar9170_tx_control {
+ __le16 length;
+ __le16 mac_control;
+ __le32 phy_control;
+ u8 frame_data[0];
+} __packed;
+
+/* these are either-or */
+#define AR9170_TX_MAC_PROT_RTS 0x0001
+#define AR9170_TX_MAC_PROT_CTS 0x0002
+
+#define AR9170_TX_MAC_NO_ACK 0x0004
+/* if unset, MAC will only do SIFS space before frame */
+#define AR9170_TX_MAC_BACKOFF 0x0008
+#define AR9170_TX_MAC_BURST 0x0010
+#define AR9170_TX_MAC_AGGR 0x0020
+
+/* encryption is a two-bit field */
+#define AR9170_TX_MAC_ENCR_NONE 0x0000
+#define AR9170_TX_MAC_ENCR_RC4 0x0040
+#define AR9170_TX_MAC_ENCR_CENC 0x0080
+#define AR9170_TX_MAC_ENCR_AES 0x00c0
+
+#define AR9170_TX_MAC_MMIC 0x0100
+#define AR9170_TX_MAC_HW_DURATION 0x0200
+#define AR9170_TX_MAC_QOS_SHIFT 10
+#define AR9170_TX_MAC_QOS_MASK (3 << AR9170_TX_MAC_QOS_SHIFT)
+#define AR9170_TX_MAC_AGGR_QOS_BIT1 0x0400
+#define AR9170_TX_MAC_AGGR_QOS_BIT2 0x0800
+#define AR9170_TX_MAC_DISABLE_TXOP 0x1000
+#define AR9170_TX_MAC_TXOP_RIFS 0x2000
+#define AR9170_TX_MAC_IMM_AMPDU 0x4000
+#define AR9170_TX_MAC_RATE_PROBE 0x8000
+
+/* either-or */
+#define AR9170_TX_PHY_MOD_CCK 0x00000000
+#define AR9170_TX_PHY_MOD_OFDM 0x00000001
+#define AR9170_TX_PHY_MOD_HT 0x00000002
+
+/* depends on modulation */
+#define AR9170_TX_PHY_SHORT_PREAMBLE 0x00000004
+#define AR9170_TX_PHY_GREENFIELD 0x00000004
+
+#define AR9170_TX_PHY_BW_SHIFT 3
+#define AR9170_TX_PHY_BW_MASK (3 << AR9170_TX_PHY_BW_SHIFT)
+#define AR9170_TX_PHY_BW_20MHZ 0
+#define AR9170_TX_PHY_BW_40MHZ 2
+#define AR9170_TX_PHY_BW_40MHZ_DUP 3
+
+#define AR9170_TX_PHY_TX_HEAVY_CLIP_SHIFT 6
+#define AR9170_TX_PHY_TX_HEAVY_CLIP_MASK (7 << AR9170_TX_PHY_TX_HEAVY_CLIP_SHIFT)
+
+#define AR9170_TX_PHY_TX_PWR_SHIFT 9
+#define AR9170_TX_PHY_TX_PWR_MASK (0x3f << AR9170_TX_PHY_TX_PWR_SHIFT)
+
+/* not part of the hw-spec */
+#define AR9170_TX_PHY_QOS_SHIFT 25
+#define AR9170_TX_PHY_QOS_MASK (3 << AR9170_TX_PHY_QOS_SHIFT)
+
+#define AR9170_TX_PHY_TXCHAIN_SHIFT 15
+#define AR9170_TX_PHY_TXCHAIN_MASK (7 << AR9170_TX_PHY_TXCHAIN_SHIFT)
+#define AR9170_TX_PHY_TXCHAIN_1 1
+/* use for cck, ofdm 6/9/12/18/24 and HT if capable */
+#define AR9170_TX_PHY_TXCHAIN_2 5
+
+#define AR9170_TX_PHY_MCS_SHIFT 18
+#define AR9170_TX_PHY_MCS_MASK (0x7f << AR9170_TX_PHY_MCS_SHIFT)
+
+#define AR9170_TX_PHY_SHORT_GI 0x80000000
+
+struct ar9170_rx_head {
+ u8 plcp[12];
+} __packed;
+
+struct ar9170_rx_phystatus {
+ union {
+ struct {
+ u8 rssi_ant0, rssi_ant1, rssi_ant2,
+ rssi_ant0x, rssi_ant1x, rssi_ant2x,
+ rssi_combined;
+ } __packed;
+ u8 rssi[7];
+ } __packed;
+
+ u8 evm_stream0[6], evm_stream1[6];
+ u8 phy_err;
+} __packed;
+
+struct ar9170_rx_macstatus {
+ u8 SAidx, DAidx;
+ u8 error;
+ u8 status;
+} __packed;
+
+#define AR9170_ENC_ALG_NONE 0x0
+#define AR9170_ENC_ALG_WEP64 0x1
+#define AR9170_ENC_ALG_TKIP 0x2
+#define AR9170_ENC_ALG_AESCCMP 0x4
+#define AR9170_ENC_ALG_WEP128 0x5
+#define AR9170_ENC_ALG_WEP256 0x6
+#define AR9170_ENC_ALG_CENC 0x7
+
+#define AR9170_RX_ENC_SOFTWARE 0x8
+
+static inline u8 ar9170_get_decrypt_type(struct ar9170_rx_macstatus *t)
+{
+ return (t->SAidx & 0xc0) >> 4 |
+ (t->DAidx & 0xc0) >> 6;
+}
+
+#define AR9170_RX_STATUS_MODULATION_MASK 0x03
+#define AR9170_RX_STATUS_MODULATION_CCK 0x00
+#define AR9170_RX_STATUS_MODULATION_OFDM 0x01
+#define AR9170_RX_STATUS_MODULATION_HT 0x02
+#define AR9170_RX_STATUS_MODULATION_DUPOFDM 0x03
+
+/* depends on modulation */
+#define AR9170_RX_STATUS_SHORT_PREAMBLE 0x08
+#define AR9170_RX_STATUS_GREENFIELD 0x08
+
+#define AR9170_RX_STATUS_MPDU_MASK 0x30
+#define AR9170_RX_STATUS_MPDU_SINGLE 0x00
+#define AR9170_RX_STATUS_MPDU_FIRST 0x20
+#define AR9170_RX_STATUS_MPDU_MIDDLE 0x30
+#define AR9170_RX_STATUS_MPDU_LAST 0x10
+
+#define AR9170_RX_ERROR_RXTO 0x01
+#define AR9170_RX_ERROR_OVERRUN 0x02
+#define AR9170_RX_ERROR_DECRYPT 0x04
+#define AR9170_RX_ERROR_FCS 0x08
+#define AR9170_RX_ERROR_WRONG_RA 0x10
+#define AR9170_RX_ERROR_PLCP 0x20
+#define AR9170_RX_ERROR_MMIC 0x40
+#define AR9170_RX_ERROR_FATAL 0x80
+
+struct ar9170_cmd_tx_status {
+ u8 dst[ETH_ALEN];
+ __le32 rate;
+ __le16 status;
+} __packed;
+
+#define AR9170_TX_STATUS_COMPLETE 0x00
+#define AR9170_TX_STATUS_RETRY 0x01
+#define AR9170_TX_STATUS_FAILED 0x02
+
+struct ar9170_cmd_ba_failed_count {
+ __le16 failed;
+ __le16 rate;
+} __packed;
+
+struct ar9170_cmd_response {
+ u8 flag;
+ u8 type;
+ __le16 padding;
+
+ union {
+ struct ar9170_cmd_tx_status tx_status;
+ struct ar9170_cmd_ba_failed_count ba_fail_cnt;
+ u8 data[0];
+ };
+} __packed;
+
+/* QoS */
+
+/* mac80211 queue to HW/FW map */
+static const u8 ar9170_qos_hwmap[4] = { 3, 2, 0, 1 };
+
+/* HW/FW queue to mac80211 map */
+static const u8 ar9170_qos_mac80211map[4] = { 2, 3, 1, 0 };
+
+enum ar9170_txq {
+ AR9170_TXQ_BE,
+ AR9170_TXQ_BK,
+ AR9170_TXQ_VI,
+ AR9170_TXQ_VO,
+
+ __AR9170_NUM_TXQ,
+};
+
+#define AR9170_TXQ_DEPTH 32
+#define AR9170_TX_MAX_PENDING 128
+
+#endif /* __AR9170_HW_H */
diff --git a/drivers/net/wireless/ath/ar9170/led.c b/drivers/net/wireless/ath/ar9170/led.c
new file mode 100644
index 00000000000..63fda6cd210
--- /dev/null
+++ b/drivers/net/wireless/ath/ar9170/led.c
@@ -0,0 +1,178 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * LED handling
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ar9170.h"
+#include "cmd.h"
+
+int ar9170_set_leds_state(struct ar9170 *ar, u32 led_state)
+{
+ return ar9170_write_reg(ar, AR9170_GPIO_REG_DATA, led_state);
+}
+
+int ar9170_init_leds(struct ar9170 *ar)
+{
+ int err;
+
+ /* disable LEDs */
+ /* GPIO [0/1 mode: output, 2/3: input] */
+ err = ar9170_write_reg(ar, AR9170_GPIO_REG_PORT_TYPE, 3);
+ if (err)
+ goto out;
+
+ /* GPIO 0/1 value: off */
+ err = ar9170_set_leds_state(ar, 0);
+
+out:
+ return err;
+}
+
+#ifdef CONFIG_AR9170_LEDS
+static void ar9170_update_leds(struct work_struct *work)
+{
+ struct ar9170 *ar = container_of(work, struct ar9170, led_work.work);
+ int i, tmp, blink_delay = 1000;
+ u32 led_val = 0;
+ bool rerun = false;
+
+ if (unlikely(!IS_ACCEPTING_CMD(ar)))
+ return ;
+
+ mutex_lock(&ar->mutex);
+ for (i = 0; i < AR9170_NUM_LEDS; i++)
+ if (ar->leds[i].registered && ar->leds[i].toggled) {
+ led_val |= 1 << i;
+
+ tmp = 70 + 200 / (ar->leds[i].toggled);
+ if (tmp < blink_delay)
+ blink_delay = tmp;
+
+ if (ar->leds[i].toggled > 1)
+ ar->leds[i].toggled = 0;
+
+ rerun = true;
+ }
+
+ ar9170_set_leds_state(ar, led_val);
+ mutex_unlock(&ar->mutex);
+
+ if (rerun)
+ queue_delayed_work(ar->hw->workqueue, &ar->led_work,
+ msecs_to_jiffies(blink_delay));
+}
+
+static void ar9170_led_brightness_set(struct led_classdev *led,
+ enum led_brightness brightness)
+{
+ struct ar9170_led *arl = container_of(led, struct ar9170_led, l);
+ struct ar9170 *ar = arl->ar;
+
+ if (unlikely(!arl->registered))
+ return ;
+
+ if (arl->last_state != !!brightness) {
+ arl->toggled++;
+ arl->last_state = !!brightness;
+ }
+
+ if (likely(IS_ACCEPTING_CMD(ar) && arl->toggled))
+ queue_delayed_work(ar->hw->workqueue, &ar->led_work, HZ/10);
+}
+
+static int ar9170_register_led(struct ar9170 *ar, int i, char *name,
+ char *trigger)
+{
+ int err;
+
+ snprintf(ar->leds[i].name, sizeof(ar->leds[i].name),
+ "ar9170-%s::%s", wiphy_name(ar->hw->wiphy), name);
+
+ ar->leds[i].ar = ar;
+ ar->leds[i].l.name = ar->leds[i].name;
+ ar->leds[i].l.brightness_set = ar9170_led_brightness_set;
+ ar->leds[i].l.brightness = 0;
+ ar->leds[i].l.default_trigger = trigger;
+
+ err = led_classdev_register(wiphy_dev(ar->hw->wiphy),
+ &ar->leds[i].l);
+ if (err)
+ printk(KERN_ERR "%s: failed to register %s LED (%d).\n",
+ wiphy_name(ar->hw->wiphy), ar->leds[i].name, err);
+ else
+ ar->leds[i].registered = true;
+
+ return err;
+}
+
+void ar9170_unregister_leds(struct ar9170 *ar)
+{
+ int i;
+
+ for (i = 0; i < AR9170_NUM_LEDS; i++)
+ if (ar->leds[i].registered) {
+ led_classdev_unregister(&ar->leds[i].l);
+ ar->leds[i].registered = false;
+ ar->leds[i].toggled = 0;
+ }
+
+ cancel_delayed_work_sync(&ar->led_work);
+}
+
+int ar9170_register_leds(struct ar9170 *ar)
+{
+ int err;
+
+ INIT_DELAYED_WORK(&ar->led_work, ar9170_update_leds);
+
+ err = ar9170_register_led(ar, 0, "tx",
+ ieee80211_get_tx_led_name(ar->hw));
+ if (err)
+ goto fail;
+
+ err = ar9170_register_led(ar, 1, "assoc",
+ ieee80211_get_assoc_led_name(ar->hw));
+ if (err)
+ goto fail;
+
+ return 0;
+
+fail:
+ ar9170_unregister_leds(ar);
+ return err;
+}
+
+#endif /* CONFIG_AR9170_LEDS */
diff --git a/drivers/net/wireless/ath/ar9170/mac.c b/drivers/net/wireless/ath/ar9170/mac.c
new file mode 100644
index 00000000000..d9f1f46de18
--- /dev/null
+++ b/drivers/net/wireless/ath/ar9170/mac.c
@@ -0,0 +1,524 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * MAC programming
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include "ar9170.h"
+#include "cmd.h"
+
+int ar9170_set_dyn_sifs_ack(struct ar9170 *ar)
+{
+ u32 val;
+
+ if (conf_is_ht40(&ar->hw->conf))
+ val = 0x010a;
+ else {
+ if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ)
+ val = 0x105;
+ else
+ val = 0x104;
+ }
+
+ return ar9170_write_reg(ar, AR9170_MAC_REG_DYNAMIC_SIFS_ACK, val);
+}
+
+int ar9170_set_slot_time(struct ar9170 *ar)
+{
+ u32 slottime = 20;
+
+ if (!ar->vif)
+ return 0;
+
+ if ((ar->hw->conf.channel->band == IEEE80211_BAND_5GHZ) ||
+ ar->vif->bss_conf.use_short_slot)
+ slottime = 9;
+
+ return ar9170_write_reg(ar, AR9170_MAC_REG_SLOT_TIME, slottime << 10);
+}
+
+int ar9170_set_basic_rates(struct ar9170 *ar)
+{
+ u8 cck, ofdm;
+
+ if (!ar->vif)
+ return 0;
+
+ ofdm = ar->vif->bss_conf.basic_rates >> 4;
+
+ /* FIXME: is still necessary? */
+ if (ar->hw->conf.channel->band == IEEE80211_BAND_5GHZ)
+ cck = 0;
+ else
+ cck = ar->vif->bss_conf.basic_rates & 0xf;
+
+ return ar9170_write_reg(ar, AR9170_MAC_REG_BASIC_RATE,
+ ofdm << 8 | cck);
+}
+
+int ar9170_set_qos(struct ar9170 *ar)
+{
+ ar9170_regwrite_begin(ar);
+
+ ar9170_regwrite(AR9170_MAC_REG_AC0_CW, ar->edcf[0].cw_min |
+ (ar->edcf[0].cw_max << 16));
+ ar9170_regwrite(AR9170_MAC_REG_AC1_CW, ar->edcf[1].cw_min |
+ (ar->edcf[1].cw_max << 16));
+ ar9170_regwrite(AR9170_MAC_REG_AC2_CW, ar->edcf[2].cw_min |
+ (ar->edcf[2].cw_max << 16));
+ ar9170_regwrite(AR9170_MAC_REG_AC3_CW, ar->edcf[3].cw_min |
+ (ar->edcf[3].cw_max << 16));
+ ar9170_regwrite(AR9170_MAC_REG_AC4_CW, ar->edcf[4].cw_min |
+ (ar->edcf[4].cw_max << 16));
+
+ ar9170_regwrite(AR9170_MAC_REG_AC1_AC0_AIFS,
+ ((ar->edcf[0].aifs * 9 + 10)) |
+ ((ar->edcf[1].aifs * 9 + 10) << 12) |
+ ((ar->edcf[2].aifs * 9 + 10) << 24));
+ ar9170_regwrite(AR9170_MAC_REG_AC3_AC2_AIFS,
+ ((ar->edcf[2].aifs * 9 + 10) >> 8) |
+ ((ar->edcf[3].aifs * 9 + 10) << 4) |
+ ((ar->edcf[4].aifs * 9 + 10) << 16));
+
+ ar9170_regwrite(AR9170_MAC_REG_AC1_AC0_TXOP,
+ ar->edcf[0].txop | ar->edcf[1].txop << 16);
+ ar9170_regwrite(AR9170_MAC_REG_AC3_AC2_TXOP,
+ ar->edcf[1].txop | ar->edcf[3].txop << 16);
+
+ ar9170_regwrite_finish();
+
+ return ar9170_regwrite_result();
+}
+
+static int ar9170_set_ampdu_density(struct ar9170 *ar, u8 mpdudensity)
+{
+ u32 val;
+
+ /* don't allow AMPDU density > 8us */
+ if (mpdudensity > 6)
+ return -EINVAL;
+
+ /* Watch out! Otus uses slightly different density values. */
+ val = 0x140a00 | (mpdudensity ? (mpdudensity + 1) : 0);
+
+ ar9170_regwrite_begin(ar);
+ ar9170_regwrite(AR9170_MAC_REG_AMPDU_DENSITY, val);
+ ar9170_regwrite_finish();
+
+ return ar9170_regwrite_result();
+}
+
+int ar9170_init_mac(struct ar9170 *ar)
+{
+ ar9170_regwrite_begin(ar);
+
+ ar9170_regwrite(AR9170_MAC_REG_ACK_EXTENSION, 0x40);
+
+ ar9170_regwrite(AR9170_MAC_REG_RETRY_MAX, 0);
+
+ /* enable MMIC */
+ ar9170_regwrite(AR9170_MAC_REG_SNIFFER,
+ AR9170_MAC_REG_SNIFFER_DEFAULTS);
+
+ ar9170_regwrite(AR9170_MAC_REG_RX_THRESHOLD, 0xc1f80);
+
+ ar9170_regwrite(AR9170_MAC_REG_RX_PE_DELAY, 0x70);
+ ar9170_regwrite(AR9170_MAC_REG_EIFS_AND_SIFS, 0xa144000);
+ ar9170_regwrite(AR9170_MAC_REG_SLOT_TIME, 9 << 10);
+
+ /* CF-END mode */
+ ar9170_regwrite(0x1c3b2c, 0x19000000);
+
+ /* NAV protects ACK only (in TXOP) */
+ ar9170_regwrite(0x1c3b38, 0x201);
+
+ /* Set Beacon PHY CTRL's TPC to 0x7, TA1=1 */
+ /* OTUS set AM to 0x1 */
+ ar9170_regwrite(AR9170_MAC_REG_BCN_HT1, 0x8000170);
+
+ ar9170_regwrite(AR9170_MAC_REG_BACKOFF_PROTECT, 0x105);
+
+ /* AGG test code*/
+ /* Aggregation MAX number and timeout */
+ ar9170_regwrite(0x1c3b9c, 0x10000a);
+
+ ar9170_regwrite(AR9170_MAC_REG_FRAMETYPE_FILTER,
+ AR9170_MAC_REG_FTF_DEFAULTS);
+
+ /* Enable deaggregator, response in sniffer mode */
+ ar9170_regwrite(0x1c3c40, 0x1 | 1<<30);
+
+ /* rate sets */
+ ar9170_regwrite(AR9170_MAC_REG_BASIC_RATE, 0x150f);
+ ar9170_regwrite(AR9170_MAC_REG_MANDATORY_RATE, 0x150f);
+ ar9170_regwrite(AR9170_MAC_REG_RTS_CTS_RATE, 0x10b01bb);
+
+ /* MIMO response control */
+ ar9170_regwrite(0x1c3694, 0x4003C1E);/* bit 26~28 otus-AM */
+
+ /* switch MAC to OTUS interface */
+ ar9170_regwrite(0x1c3600, 0x3);
+
+ ar9170_regwrite(AR9170_MAC_REG_AMPDU_RX_THRESH, 0xffff);
+
+ /* set PHY register read timeout (??) */
+ ar9170_regwrite(AR9170_MAC_REG_MISC_680, 0xf00008);
+
+ /* Disable Rx TimeOut, workaround for BB. */
+ ar9170_regwrite(AR9170_MAC_REG_RX_TIMEOUT, 0x0);
+
+ /* Set CPU clock frequency to 88/80MHz */
+ ar9170_regwrite(AR9170_PWR_REG_CLOCK_SEL,
+ AR9170_PWR_CLK_AHB_80_88MHZ |
+ AR9170_PWR_CLK_DAC_160_INV_DLY);
+
+ /* Set WLAN DMA interrupt mode: generate int per packet */
+ ar9170_regwrite(AR9170_MAC_REG_TXRX_MPI, 0x110011);
+
+ ar9170_regwrite(AR9170_MAC_REG_FCS_SELECT,
+ AR9170_MAC_FCS_FIFO_PROT);
+
+ /* Disables the CF_END frame, undocumented register */
+ ar9170_regwrite(AR9170_MAC_REG_TXOP_NOT_ENOUGH_IND,
+ 0x141E0F48);
+
+ ar9170_regwrite_finish();
+
+ return ar9170_regwrite_result();
+}
+
+static int ar9170_set_mac_reg(struct ar9170 *ar, const u32 reg, const u8 *mac)
+{
+ static const u8 zero[ETH_ALEN] = { 0 };
+
+ if (!mac)
+ mac = zero;
+
+ ar9170_regwrite_begin(ar);
+
+ ar9170_regwrite(reg,
+ (mac[3] << 24) | (mac[2] << 16) |
+ (mac[1] << 8) | mac[0]);
+
+ ar9170_regwrite(reg + 4, (mac[5] << 8) | mac[4]);
+
+ ar9170_regwrite_finish();
+
+ return ar9170_regwrite_result();
+}
+
+int ar9170_update_multicast(struct ar9170 *ar)
+{
+ int err;
+
+ ar9170_regwrite_begin(ar);
+ ar9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_H,
+ ar->want_mc_hash >> 32);
+ ar9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_L,
+ ar->want_mc_hash);
+
+ ar9170_regwrite_finish();
+ err = ar9170_regwrite_result();
+
+ if (err)
+ return err;
+
+ ar->cur_mc_hash = ar->want_mc_hash;
+
+ return 0;
+}
+
+int ar9170_update_frame_filter(struct ar9170 *ar)
+{
+ int err;
+
+ err = ar9170_write_reg(ar, AR9170_MAC_REG_FRAMETYPE_FILTER,
+ ar->want_filter);
+
+ if (err)
+ return err;
+
+ ar->cur_filter = ar->want_filter;
+
+ return 0;
+}
+
+static int ar9170_set_promiscouous(struct ar9170 *ar)
+{
+ u32 encr_mode, sniffer;
+ int err;
+
+ err = ar9170_read_reg(ar, AR9170_MAC_REG_SNIFFER, &sniffer);
+ if (err)
+ return err;
+
+ err = ar9170_read_reg(ar, AR9170_MAC_REG_ENCRYPTION, &encr_mode);
+ if (err)
+ return err;
+
+ if (ar->sniffer_enabled) {
+ sniffer |= AR9170_MAC_REG_SNIFFER_ENABLE_PROMISC;
+
+ /*
+ * Rx decryption works in place.
+ *
+ * If we don't disable it, the hardware will render all
+ * encrypted frames which are encrypted with an unknown
+ * key useless.
+ */
+
+ encr_mode |= AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE;
+ ar->sniffer_enabled = true;
+ } else {
+ sniffer &= ~AR9170_MAC_REG_SNIFFER_ENABLE_PROMISC;
+
+ if (ar->rx_software_decryption)
+ encr_mode |= AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE;
+ else
+ encr_mode &= ~AR9170_MAC_REG_ENCRYPTION_RX_SOFTWARE;
+ }
+
+ ar9170_regwrite_begin(ar);
+ ar9170_regwrite(AR9170_MAC_REG_ENCRYPTION, encr_mode);
+ ar9170_regwrite(AR9170_MAC_REG_SNIFFER, sniffer);
+ ar9170_regwrite_finish();
+
+ return ar9170_regwrite_result();
+}
+
+int ar9170_set_operating_mode(struct ar9170 *ar)
+{
+ u32 pm_mode = AR9170_MAC_REG_POWERMGT_DEFAULTS;
+ u8 *mac_addr, *bssid;
+ int err;
+
+ if (ar->vif) {
+ mac_addr = ar->mac_addr;
+ bssid = ar->bssid;
+
+ switch (ar->vif->type) {
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_ADHOC:
+ pm_mode |= AR9170_MAC_REG_POWERMGT_IBSS;
+ break;
+ case NL80211_IFTYPE_AP:
+ pm_mode |= AR9170_MAC_REG_POWERMGT_AP;
+ break;
+ case NL80211_IFTYPE_WDS:
+ pm_mode |= AR9170_MAC_REG_POWERMGT_AP_WDS;
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ ar->sniffer_enabled = true;
+ ar->rx_software_decryption = true;
+ break;
+ default:
+ pm_mode |= AR9170_MAC_REG_POWERMGT_STA;
+ break;
+ }
+ } else {
+ mac_addr = NULL;
+ bssid = NULL;
+ }
+
+ err = ar9170_set_mac_reg(ar, AR9170_MAC_REG_MAC_ADDR_L, mac_addr);
+ if (err)
+ return err;
+
+ err = ar9170_set_mac_reg(ar, AR9170_MAC_REG_BSSID_L, bssid);
+ if (err)
+ return err;
+
+ err = ar9170_set_promiscouous(ar);
+ if (err)
+ return err;
+
+ /* set AMPDU density to 8us. */
+ err = ar9170_set_ampdu_density(ar, 6);
+ if (err)
+ return err;
+
+ ar9170_regwrite_begin(ar);
+
+ ar9170_regwrite(AR9170_MAC_REG_POWERMANAGEMENT, pm_mode);
+ ar9170_regwrite_finish();
+
+ return ar9170_regwrite_result();
+}
+
+int ar9170_set_hwretry_limit(struct ar9170 *ar, unsigned int max_retry)
+{
+ u32 tmp = min_t(u32, 0x33333, max_retry * 0x11111);
+
+ return ar9170_write_reg(ar, AR9170_MAC_REG_RETRY_MAX, tmp);
+}
+
+int ar9170_set_beacon_timers(struct ar9170 *ar)
+{
+ u32 v = 0;
+ u32 pretbtt = 0;
+
+ if (ar->vif) {
+ v |= ar->vif->bss_conf.beacon_int;
+
+ switch (ar->vif->type) {
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_ADHOC:
+ v |= BIT(25);
+ break;
+ case NL80211_IFTYPE_AP:
+ v |= BIT(24);
+ pretbtt = (ar->vif->bss_conf.beacon_int - 6) << 16;
+ break;
+ default:
+ break;
+ }
+
+ v |= ar->vif->bss_conf.dtim_period << 16;
+ }
+
+ ar9170_regwrite_begin(ar);
+
+ ar9170_regwrite(AR9170_MAC_REG_PRETBTT, pretbtt);
+ ar9170_regwrite(AR9170_MAC_REG_BCN_PERIOD, v);
+ ar9170_regwrite_finish();
+ return ar9170_regwrite_result();
+}
+
+int ar9170_update_beacon(struct ar9170 *ar)
+{
+ struct sk_buff *skb;
+ __le32 *data, *old = NULL;
+ u32 word;
+ int i;
+
+ skb = ieee80211_beacon_get(ar->hw, ar->vif);
+ if (!skb)
+ return -ENOMEM;
+
+ data = (__le32 *)skb->data;
+ if (ar->beacon)
+ old = (__le32 *)ar->beacon->data;
+
+ ar9170_regwrite_begin(ar);
+ for (i = 0; i < DIV_ROUND_UP(skb->len, 4); i++) {
+ /*
+ * XXX: This accesses beyond skb data for up
+ * to the last 3 bytes!!
+ */
+
+ if (old && (data[i] == old[i]))
+ continue;
+
+ word = le32_to_cpu(data[i]);
+ ar9170_regwrite(AR9170_BEACON_BUFFER_ADDRESS + 4 * i, word);
+ }
+
+ /* XXX: use skb->cb info */
+ if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ)
+ ar9170_regwrite(AR9170_MAC_REG_BCN_PLCP,
+ ((skb->len + 4) << (3 + 16)) + 0x0400);
+ else
+ ar9170_regwrite(AR9170_MAC_REG_BCN_PLCP,
+ ((skb->len + 4) << 16) + 0x001b);
+
+ ar9170_regwrite(AR9170_MAC_REG_BCN_LENGTH, skb->len + 4);
+ ar9170_regwrite(AR9170_MAC_REG_BCN_ADDR, AR9170_BEACON_BUFFER_ADDRESS);
+ ar9170_regwrite(AR9170_MAC_REG_BCN_CTRL, 1);
+
+ ar9170_regwrite_finish();
+
+ dev_kfree_skb(ar->beacon);
+ ar->beacon = skb;
+
+ return ar9170_regwrite_result();
+}
+
+void ar9170_new_beacon(struct work_struct *work)
+{
+ struct ar9170 *ar = container_of(work, struct ar9170,
+ beacon_work);
+ struct sk_buff *skb;
+
+ if (unlikely(!IS_STARTED(ar)))
+ return ;
+
+ mutex_lock(&ar->mutex);
+
+ if (!ar->vif)
+ goto out;
+
+ ar9170_update_beacon(ar);
+
+ rcu_read_lock();
+ while ((skb = ieee80211_get_buffered_bc(ar->hw, ar->vif)))
+ ar9170_op_tx(ar->hw, skb);
+
+ rcu_read_unlock();
+
+ out:
+ mutex_unlock(&ar->mutex);
+}
+
+int ar9170_upload_key(struct ar9170 *ar, u8 id, const u8 *mac, u8 ktype,
+ u8 keyidx, u8 *keydata, int keylen)
+{
+ __le32 vals[7];
+ static const u8 bcast[ETH_ALEN] =
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ u8 dummy;
+
+ mac = mac ? : bcast;
+
+ vals[0] = cpu_to_le32((keyidx << 16) + id);
+ vals[1] = cpu_to_le32(mac[1] << 24 | mac[0] << 16 | ktype);
+ vals[2] = cpu_to_le32(mac[5] << 24 | mac[4] << 16 |
+ mac[3] << 8 | mac[2]);
+ memset(&vals[3], 0, 16);
+ if (keydata)
+ memcpy(&vals[3], keydata, keylen);
+
+ return ar->exec_cmd(ar, AR9170_CMD_EKEY,
+ sizeof(vals), (u8 *)vals,
+ 1, &dummy);
+}
+
+int ar9170_disable_key(struct ar9170 *ar, u8 id)
+{
+ __le32 val = cpu_to_le32(id);
+ u8 dummy;
+
+ return ar->exec_cmd(ar, AR9170_CMD_EKEY,
+ sizeof(val), (u8 *)&val,
+ 1, &dummy);
+}
diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c
new file mode 100644
index 00000000000..9d38cf60a0d
--- /dev/null
+++ b/drivers/net/wireless/ath/ar9170/main.c
@@ -0,0 +1,2210 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * mac80211 interaction code
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, Christian Lamparter <chunkeey@web.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+#include "ar9170.h"
+#include "hw.h"
+#include "cmd.h"
+
+static int modparam_nohwcrypt;
+module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO);
+MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
+
+#define RATE(_bitrate, _hw_rate, _txpidx, _flags) { \
+ .bitrate = (_bitrate), \
+ .flags = (_flags), \
+ .hw_value = (_hw_rate) | (_txpidx) << 4, \
+}
+
+static struct ieee80211_rate __ar9170_ratetable[] = {
+ RATE(10, 0, 0, 0),
+ RATE(20, 1, 1, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(55, 2, 2, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(110, 3, 3, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(60, 0xb, 0, 0),
+ RATE(90, 0xf, 0, 0),
+ RATE(120, 0xa, 0, 0),
+ RATE(180, 0xe, 0, 0),
+ RATE(240, 0x9, 0, 0),
+ RATE(360, 0xd, 1, 0),
+ RATE(480, 0x8, 2, 0),
+ RATE(540, 0xc, 3, 0),
+};
+#undef RATE
+
+#define ar9170_g_ratetable (__ar9170_ratetable + 0)
+#define ar9170_g_ratetable_size 12
+#define ar9170_a_ratetable (__ar9170_ratetable + 4)
+#define ar9170_a_ratetable_size 8
+
+/*
+ * NB: The hw_value is used as an index into the ar9170_phy_freq_params
+ * array in phy.c so that we don't have to do frequency lookups!
+ */
+#define CHAN(_freq, _idx) { \
+ .center_freq = (_freq), \
+ .hw_value = (_idx), \
+ .max_power = 18, /* XXX */ \
+}
+
+static struct ieee80211_channel ar9170_2ghz_chantable[] = {
+ CHAN(2412, 0),
+ CHAN(2417, 1),
+ CHAN(2422, 2),
+ CHAN(2427, 3),
+ CHAN(2432, 4),
+ CHAN(2437, 5),
+ CHAN(2442, 6),
+ CHAN(2447, 7),
+ CHAN(2452, 8),
+ CHAN(2457, 9),
+ CHAN(2462, 10),
+ CHAN(2467, 11),
+ CHAN(2472, 12),
+ CHAN(2484, 13),
+};
+
+static struct ieee80211_channel ar9170_5ghz_chantable[] = {
+ CHAN(4920, 14),
+ CHAN(4940, 15),
+ CHAN(4960, 16),
+ CHAN(4980, 17),
+ CHAN(5040, 18),
+ CHAN(5060, 19),
+ CHAN(5080, 20),
+ CHAN(5180, 21),
+ CHAN(5200, 22),
+ CHAN(5220, 23),
+ CHAN(5240, 24),
+ CHAN(5260, 25),
+ CHAN(5280, 26),
+ CHAN(5300, 27),
+ CHAN(5320, 28),
+ CHAN(5500, 29),
+ CHAN(5520, 30),
+ CHAN(5540, 31),
+ CHAN(5560, 32),
+ CHAN(5580, 33),
+ CHAN(5600, 34),
+ CHAN(5620, 35),
+ CHAN(5640, 36),
+ CHAN(5660, 37),
+ CHAN(5680, 38),
+ CHAN(5700, 39),
+ CHAN(5745, 40),
+ CHAN(5765, 41),
+ CHAN(5785, 42),
+ CHAN(5805, 43),
+ CHAN(5825, 44),
+ CHAN(5170, 45),
+ CHAN(5190, 46),
+ CHAN(5210, 47),
+ CHAN(5230, 48),
+};
+#undef CHAN
+
+#define AR9170_HT_CAP \
+{ \
+ .ht_supported = true, \
+ .cap = IEEE80211_HT_CAP_MAX_AMSDU | \
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \
+ IEEE80211_HT_CAP_SGI_40 | \
+ IEEE80211_HT_CAP_DSSSCCK40 | \
+ IEEE80211_HT_CAP_SM_PS, \
+ .ampdu_factor = 3, \
+ .ampdu_density = 6, \
+ .mcs = { \
+ .rx_mask = { 0xFF, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, }, \
+ }, \
+}
+
+static struct ieee80211_supported_band ar9170_band_2GHz = {
+ .channels = ar9170_2ghz_chantable,
+ .n_channels = ARRAY_SIZE(ar9170_2ghz_chantable),
+ .bitrates = ar9170_g_ratetable,
+ .n_bitrates = ar9170_g_ratetable_size,
+ .ht_cap = AR9170_HT_CAP,
+};
+
+static struct ieee80211_supported_band ar9170_band_5GHz = {
+ .channels = ar9170_5ghz_chantable,
+ .n_channels = ARRAY_SIZE(ar9170_5ghz_chantable),
+ .bitrates = ar9170_a_ratetable,
+ .n_bitrates = ar9170_a_ratetable_size,
+ .ht_cap = AR9170_HT_CAP,
+};
+
+static void ar9170_tx(struct ar9170 *ar);
+
+#ifdef AR9170_QUEUE_DEBUG
+static void ar9170_print_txheader(struct ar9170 *ar, struct sk_buff *skb)
+{
+ struct ar9170_tx_control *txc = (void *) skb->data;
+ struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb);
+ struct ar9170_tx_info *arinfo = (void *) txinfo->rate_driver_data;
+ struct ieee80211_hdr *hdr = (void *) txc->frame_data;
+
+ printk(KERN_DEBUG "%s: => FRAME [skb:%p, q:%d, DA:[%pM] flags:%x "
+ "mac_ctrl:%04x, phy_ctrl:%08x, timeout:[%d ms]]\n",
+ wiphy_name(ar->hw->wiphy), skb, skb_get_queue_mapping(skb),
+ ieee80211_get_DA(hdr), arinfo->flags,
+ le16_to_cpu(txc->mac_control), le32_to_cpu(txc->phy_control),
+ jiffies_to_msecs(arinfo->timeout - jiffies));
+}
+
+static void __ar9170_dump_txqueue(struct ar9170 *ar,
+ struct sk_buff_head *queue)
+{
+ struct sk_buff *skb;
+ int i = 0;
+
+ printk(KERN_DEBUG "---[ cut here ]---\n");
+ printk(KERN_DEBUG "%s: %d entries in queue.\n",
+ wiphy_name(ar->hw->wiphy), skb_queue_len(queue));
+
+ skb_queue_walk(queue, skb) {
+ printk(KERN_DEBUG "index:%d => \n", i++);
+ ar9170_print_txheader(ar, skb);
+ }
+ if (i != skb_queue_len(queue))
+ printk(KERN_DEBUG "WARNING: queue frame counter "
+ "mismatch %d != %d\n", skb_queue_len(queue), i);
+ printk(KERN_DEBUG "---[ end ]---\n");
+}
+
+static void ar9170_dump_txqueue(struct ar9170 *ar,
+ struct sk_buff_head *queue)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ __ar9170_dump_txqueue(ar, queue);
+ spin_unlock_irqrestore(&queue->lock, flags);
+}
+
+static void __ar9170_dump_txstats(struct ar9170 *ar)
+{
+ int i;
+
+ printk(KERN_DEBUG "%s: QoS queue stats\n",
+ wiphy_name(ar->hw->wiphy));
+
+ for (i = 0; i < __AR9170_NUM_TXQ; i++)
+ printk(KERN_DEBUG "%s: queue:%d limit:%d len:%d waitack:%d\n",
+ wiphy_name(ar->hw->wiphy), i, ar->tx_stats[i].limit,
+ ar->tx_stats[i].len, skb_queue_len(&ar->tx_status[i]));
+}
+
+static void ar9170_dump_txstats(struct ar9170 *ar)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ar->tx_stats_lock, flags);
+ __ar9170_dump_txstats(ar);
+ spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
+}
+#endif /* AR9170_QUEUE_DEBUG */
+
+/* caller must guarantee exclusive access for _bin_ queue. */
+static void ar9170_recycle_expired(struct ar9170 *ar,
+ struct sk_buff_head *queue,
+ struct sk_buff_head *bin)
+{
+ struct sk_buff *skb, *old = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ while ((skb = skb_peek(queue))) {
+ struct ieee80211_tx_info *txinfo;
+ struct ar9170_tx_info *arinfo;
+
+ txinfo = IEEE80211_SKB_CB(skb);
+ arinfo = (void *) txinfo->rate_driver_data;
+
+ if (time_is_before_jiffies(arinfo->timeout)) {
+#ifdef AR9170_QUEUE_DEBUG
+ printk(KERN_DEBUG "%s: [%ld > %ld] frame expired => "
+ "recycle \n", wiphy_name(ar->hw->wiphy),
+ jiffies, arinfo->timeout);
+ ar9170_print_txheader(ar, skb);
+#endif /* AR9170_QUEUE_DEBUG */
+ __skb_unlink(skb, queue);
+ __skb_queue_tail(bin, skb);
+ } else {
+ break;
+ }
+
+ if (unlikely(old == skb)) {
+ /* bail out - queue is shot. */
+
+ WARN_ON(1);
+ break;
+ }
+ old = skb;
+ }
+ spin_unlock_irqrestore(&queue->lock, flags);
+}
+
+static void ar9170_tx_status(struct ar9170 *ar, struct sk_buff *skb,
+ u16 tx_status)
+{
+ struct ieee80211_tx_info *txinfo;
+ unsigned int retries = 0;
+
+ txinfo = IEEE80211_SKB_CB(skb);
+ ieee80211_tx_info_clear_status(txinfo);
+
+ switch (tx_status) {
+ case AR9170_TX_STATUS_RETRY:
+ retries = 2;
+ case AR9170_TX_STATUS_COMPLETE:
+ txinfo->flags |= IEEE80211_TX_STAT_ACK;
+ break;
+
+ case AR9170_TX_STATUS_FAILED:
+ retries = ar->hw->conf.long_frame_max_tx_count;
+ break;
+
+ default:
+ printk(KERN_ERR "%s: invalid tx_status response (%x).\n",
+ wiphy_name(ar->hw->wiphy), tx_status);
+ break;
+ }
+
+ txinfo->status.rates[0].count = retries + 1;
+ skb_pull(skb, sizeof(struct ar9170_tx_control));
+ ieee80211_tx_status_irqsafe(ar->hw, skb);
+}
+
+void ar9170_tx_callback(struct ar9170 *ar, struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ar9170_tx_info *arinfo = (void *) info->rate_driver_data;
+ unsigned int queue = skb_get_queue_mapping(skb);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ar->tx_stats_lock, flags);
+ ar->tx_stats[queue].len--;
+
+ if (skb_queue_empty(&ar->tx_pending[queue])) {
+#ifdef AR9170_QUEUE_STOP_DEBUG
+ printk(KERN_DEBUG "%s: wake queue %d\n",
+ wiphy_name(ar->hw->wiphy), queue);
+ __ar9170_dump_txstats(ar);
+#endif /* AR9170_QUEUE_STOP_DEBUG */
+ ieee80211_wake_queue(ar->hw, queue);
+ }
+ spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
+
+ if (arinfo->flags & AR9170_TX_FLAG_BLOCK_ACK) {
+ dev_kfree_skb_any(skb);
+ } else if (arinfo->flags & AR9170_TX_FLAG_WAIT_FOR_ACK) {
+ arinfo->timeout = jiffies +
+ msecs_to_jiffies(AR9170_TX_TIMEOUT);
+
+ skb_queue_tail(&ar->tx_status[queue], skb);
+ } else if (arinfo->flags & AR9170_TX_FLAG_NO_ACK) {
+ ar9170_tx_status(ar, skb, AR9170_TX_STATUS_FAILED);
+ } else {
+#ifdef AR9170_QUEUE_DEBUG
+ printk(KERN_DEBUG "%s: unsupported frame flags!\n",
+ wiphy_name(ar->hw->wiphy));
+ ar9170_print_txheader(ar, skb);
+#endif /* AR9170_QUEUE_DEBUG */
+ dev_kfree_skb_any(skb);
+ }
+
+ if (!ar->tx_stats[queue].len &&
+ !skb_queue_empty(&ar->tx_pending[queue])) {
+ ar9170_tx(ar);
+ }
+}
+
+static struct sk_buff *ar9170_get_queued_skb(struct ar9170 *ar,
+ const u8 *mac,
+ struct sk_buff_head *queue,
+ const u32 rate)
+{
+ unsigned long flags;
+ struct sk_buff *skb;
+
+ /*
+ * Unfortunately, the firmware does not tell to which (queued) frame
+ * this transmission status report belongs to.
+ *
+ * So we have to make risky guesses - with the scarce information
+ * the firmware provided (-> destination MAC, and phy_control) -
+ * and hope that we picked the right one...
+ */
+
+ spin_lock_irqsave(&queue->lock, flags);
+ skb_queue_walk(queue, skb) {
+ struct ar9170_tx_control *txc = (void *) skb->data;
+ struct ieee80211_hdr *hdr = (void *) txc->frame_data;
+ u32 r;
+
+ if (mac && compare_ether_addr(ieee80211_get_DA(hdr), mac)) {
+#ifdef AR9170_QUEUE_DEBUG
+ printk(KERN_DEBUG "%s: skip frame => DA %pM != %pM\n",
+ wiphy_name(ar->hw->wiphy), mac,
+ ieee80211_get_DA(hdr));
+ ar9170_print_txheader(ar, skb);
+#endif /* AR9170_QUEUE_DEBUG */
+ continue;
+ }
+
+ r = (le32_to_cpu(txc->phy_control) & AR9170_TX_PHY_MCS_MASK) >>
+ AR9170_TX_PHY_MCS_SHIFT;
+
+ if ((rate != AR9170_TX_INVALID_RATE) && (r != rate)) {
+#ifdef AR9170_QUEUE_DEBUG
+ printk(KERN_DEBUG "%s: skip frame => rate %d != %d\n",
+ wiphy_name(ar->hw->wiphy), rate, r);
+ ar9170_print_txheader(ar, skb);
+#endif /* AR9170_QUEUE_DEBUG */
+ continue;
+ }
+
+ __skb_unlink(skb, queue);
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return skb;
+ }
+
+#ifdef AR9170_QUEUE_DEBUG
+ printk(KERN_ERR "%s: ESS:[%pM] does not have any "
+ "outstanding frames in queue.\n",
+ wiphy_name(ar->hw->wiphy), mac);
+ __ar9170_dump_txqueue(ar, queue);
+#endif /* AR9170_QUEUE_DEBUG */
+ spin_unlock_irqrestore(&queue->lock, flags);
+
+ return NULL;
+}
+
+/*
+ * This worker tries to keeps an maintain tx_status queues.
+ * So we can guarantee that incoming tx_status reports are
+ * actually for a pending frame.
+ */
+
+static void ar9170_tx_janitor(struct work_struct *work)
+{
+ struct ar9170 *ar = container_of(work, struct ar9170,
+ tx_janitor.work);
+ struct sk_buff_head waste;
+ unsigned int i;
+ bool resched = false;
+
+ if (unlikely(!IS_STARTED(ar)))
+ return ;
+
+ skb_queue_head_init(&waste);
+
+ for (i = 0; i < __AR9170_NUM_TXQ; i++) {
+#ifdef AR9170_QUEUE_DEBUG
+ printk(KERN_DEBUG "%s: garbage collector scans queue:%d\n",
+ wiphy_name(ar->hw->wiphy), i);
+ ar9170_dump_txqueue(ar, &ar->tx_pending[i]);
+ ar9170_dump_txqueue(ar, &ar->tx_status[i]);
+#endif /* AR9170_QUEUE_DEBUG */
+
+ ar9170_recycle_expired(ar, &ar->tx_status[i], &waste);
+ ar9170_recycle_expired(ar, &ar->tx_pending[i], &waste);
+ skb_queue_purge(&waste);
+
+ if (!skb_queue_empty(&ar->tx_status[i]) ||
+ !skb_queue_empty(&ar->tx_pending[i]))
+ resched = true;
+ }
+
+ if (resched)
+ queue_delayed_work(ar->hw->workqueue,
+ &ar->tx_janitor,
+ msecs_to_jiffies(AR9170_JANITOR_DELAY));
+}
+
+void ar9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len)
+{
+ struct ar9170_cmd_response *cmd = (void *) buf;
+
+ if ((cmd->type & 0xc0) != 0xc0) {
+ ar->callback_cmd(ar, len, buf);
+ return;
+ }
+
+ /* hardware event handlers */
+ switch (cmd->type) {
+ case 0xc1: {
+ /*
+ * TX status notification:
+ * bytes: 0c c1 XX YY M1 M2 M3 M4 M5 M6 R4 R3 R2 R1 S2 S1
+ *
+ * XX always 81
+ * YY always 00
+ * M1-M6 is the MAC address
+ * R1-R4 is the transmit rate
+ * S1-S2 is the transmit status
+ */
+
+ struct sk_buff *skb;
+ u32 phy = le32_to_cpu(cmd->tx_status.rate);
+ u32 q = (phy & AR9170_TX_PHY_QOS_MASK) >>
+ AR9170_TX_PHY_QOS_SHIFT;
+#ifdef AR9170_QUEUE_DEBUG
+ printk(KERN_DEBUG "%s: recv tx_status for %pM, p:%08x, q:%d\n",
+ wiphy_name(ar->hw->wiphy), cmd->tx_status.dst, phy, q);
+#endif /* AR9170_QUEUE_DEBUG */
+
+ skb = ar9170_get_queued_skb(ar, cmd->tx_status.dst,
+ &ar->tx_status[q],
+ AR9170_TX_INVALID_RATE);
+ if (unlikely(!skb))
+ return ;
+
+ ar9170_tx_status(ar, skb, le16_to_cpu(cmd->tx_status.status));
+ break;
+ }
+
+ case 0xc0:
+ /*
+ * pre-TBTT event
+ */
+ if (ar->vif && ar->vif->type == NL80211_IFTYPE_AP)
+ queue_work(ar->hw->workqueue, &ar->beacon_work);
+ break;
+
+ case 0xc2:
+ /*
+ * (IBSS) beacon send notification
+ * bytes: 04 c2 XX YY B4 B3 B2 B1
+ *
+ * XX always 80
+ * YY always 00
+ * B1-B4 "should" be the number of send out beacons.
+ */
+ break;
+
+ case 0xc3:
+ /* End of Atim Window */
+ break;
+
+ case 0xc4:
+ case 0xc5:
+ /* BlockACK events */
+ break;
+
+ case 0xc6:
+ /* Watchdog Interrupt */
+ break;
+
+ case 0xc9:
+ /* retransmission issue / SIFS/EIFS collision ?! */
+ break;
+
+ /* firmware debug */
+ case 0xca:
+ printk(KERN_DEBUG "ar9170 FW: %.*s\n", len - 4, (char *)buf + 4);
+ break;
+ case 0xcb:
+ len -= 4;
+
+ switch (len) {
+ case 1:
+ printk(KERN_DEBUG "ar9170 FW: u8: %#.2x\n",
+ *((char *)buf + 4));
+ break;
+ case 2:
+ printk(KERN_DEBUG "ar9170 FW: u8: %#.4x\n",
+ le16_to_cpup((__le16 *)((char *)buf + 4)));
+ break;
+ case 4:
+ printk(KERN_DEBUG "ar9170 FW: u8: %#.8x\n",
+ le32_to_cpup((__le32 *)((char *)buf + 4)));
+ break;
+ case 8:
+ printk(KERN_DEBUG "ar9170 FW: u8: %#.16lx\n",
+ (unsigned long)le64_to_cpup(
+ (__le64 *)((char *)buf + 4)));
+ break;
+ }
+ break;
+ case 0xcc:
+ print_hex_dump_bytes("ar9170 FW:", DUMP_PREFIX_NONE,
+ (char *)buf + 4, len - 4);
+ break;
+
+ default:
+ printk(KERN_INFO "received unhandled event %x\n", cmd->type);
+ print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE, buf, len);
+ break;
+ }
+}
+
+static void ar9170_rx_reset_rx_mpdu(struct ar9170 *ar)
+{
+ memset(&ar->rx_mpdu.plcp, 0, sizeof(struct ar9170_rx_head));
+ ar->rx_mpdu.has_plcp = false;
+}
+
+int ar9170_nag_limiter(struct ar9170 *ar)
+{
+ bool print_message;
+
+ /*
+ * we expect all sorts of errors in promiscuous mode.
+ * don't bother with it, it's OK!
+ */
+ if (ar->sniffer_enabled)
+ return false;
+
+ /*
+ * only go for frequent errors! The hardware tends to
+ * do some stupid thing once in a while under load, in
+ * noisy environments or just for fun!
+ */
+ if (time_before(jiffies, ar->bad_hw_nagger) && net_ratelimit())
+ print_message = true;
+ else
+ print_message = false;
+
+ /* reset threshold for "once in a while" */
+ ar->bad_hw_nagger = jiffies + HZ / 4;
+ return print_message;
+}
+
+static int ar9170_rx_mac_status(struct ar9170 *ar,
+ struct ar9170_rx_head *head,
+ struct ar9170_rx_macstatus *mac,
+ struct ieee80211_rx_status *status)
+{
+ u8 error, decrypt;
+
+ BUILD_BUG_ON(sizeof(struct ar9170_rx_head) != 12);
+ BUILD_BUG_ON(sizeof(struct ar9170_rx_macstatus) != 4);
+
+ error = mac->error;
+ if (error & AR9170_RX_ERROR_MMIC) {
+ status->flag |= RX_FLAG_MMIC_ERROR;
+ error &= ~AR9170_RX_ERROR_MMIC;
+ }
+
+ if (error & AR9170_RX_ERROR_PLCP) {
+ status->flag |= RX_FLAG_FAILED_PLCP_CRC;
+ error &= ~AR9170_RX_ERROR_PLCP;
+
+ if (!(ar->filter_state & FIF_PLCPFAIL))
+ return -EINVAL;
+ }
+
+ if (error & AR9170_RX_ERROR_FCS) {
+ status->flag |= RX_FLAG_FAILED_FCS_CRC;
+ error &= ~AR9170_RX_ERROR_FCS;
+
+ if (!(ar->filter_state & FIF_FCSFAIL))
+ return -EINVAL;
+ }
+
+ decrypt = ar9170_get_decrypt_type(mac);
+ if (!(decrypt & AR9170_RX_ENC_SOFTWARE) &&
+ decrypt != AR9170_ENC_ALG_NONE)
+ status->flag |= RX_FLAG_DECRYPTED;
+
+ /* ignore wrong RA errors */
+ error &= ~AR9170_RX_ERROR_WRONG_RA;
+
+ if (error & AR9170_RX_ERROR_DECRYPT) {
+ error &= ~AR9170_RX_ERROR_DECRYPT;
+ /*
+ * Rx decryption is done in place,
+ * the original data is lost anyway.
+ */
+
+ return -EINVAL;
+ }
+
+ /* drop any other error frames */
+ if (unlikely(error)) {
+ /* TODO: update netdevice's RX dropped/errors statistics */
+
+ if (ar9170_nag_limiter(ar))
+ printk(KERN_DEBUG "%s: received frame with "
+ "suspicious error code (%#x).\n",
+ wiphy_name(ar->hw->wiphy), error);
+
+ return -EINVAL;
+ }
+
+ status->band = ar->channel->band;
+ status->freq = ar->channel->center_freq;
+
+ switch (mac->status & AR9170_RX_STATUS_MODULATION_MASK) {
+ case AR9170_RX_STATUS_MODULATION_CCK:
+ if (mac->status & AR9170_RX_STATUS_SHORT_PREAMBLE)
+ status->flag |= RX_FLAG_SHORTPRE;
+ switch (head->plcp[0]) {
+ case 0x0a:
+ status->rate_idx = 0;
+ break;
+ case 0x14:
+ status->rate_idx = 1;
+ break;
+ case 0x37:
+ status->rate_idx = 2;
+ break;
+ case 0x6e:
+ status->rate_idx = 3;
+ break;
+ default:
+ if (ar9170_nag_limiter(ar))
+ printk(KERN_ERR "%s: invalid plcp cck rate "
+ "(%x).\n", wiphy_name(ar->hw->wiphy),
+ head->plcp[0]);
+ return -EINVAL;
+ }
+ break;
+
+ case AR9170_RX_STATUS_MODULATION_OFDM:
+ switch (head->plcp[0] & 0xf) {
+ case 0xb:
+ status->rate_idx = 0;
+ break;
+ case 0xf:
+ status->rate_idx = 1;
+ break;
+ case 0xa:
+ status->rate_idx = 2;
+ break;
+ case 0xe:
+ status->rate_idx = 3;
+ break;
+ case 0x9:
+ status->rate_idx = 4;
+ break;
+ case 0xd:
+ status->rate_idx = 5;
+ break;
+ case 0x8:
+ status->rate_idx = 6;
+ break;
+ case 0xc:
+ status->rate_idx = 7;
+ break;
+ default:
+ if (ar9170_nag_limiter(ar))
+ printk(KERN_ERR "%s: invalid plcp ofdm rate "
+ "(%x).\n", wiphy_name(ar->hw->wiphy),
+ head->plcp[0]);
+ return -EINVAL;
+ }
+ if (status->band == IEEE80211_BAND_2GHZ)
+ status->rate_idx += 4;
+ break;
+
+ case AR9170_RX_STATUS_MODULATION_HT:
+ if (head->plcp[3] & 0x80)
+ status->flag |= RX_FLAG_40MHZ;
+ if (head->plcp[6] & 0x80)
+ status->flag |= RX_FLAG_SHORT_GI;
+
+ status->rate_idx = clamp(0, 75, head->plcp[6] & 0x7f);
+ status->flag |= RX_FLAG_HT;
+ break;
+
+ case AR9170_RX_STATUS_MODULATION_DUPOFDM:
+ /* XXX */
+ if (ar9170_nag_limiter(ar))
+ printk(KERN_ERR "%s: invalid modulation\n",
+ wiphy_name(ar->hw->wiphy));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void ar9170_rx_phy_status(struct ar9170 *ar,
+ struct ar9170_rx_phystatus *phy,
+ struct ieee80211_rx_status *status)
+{
+ int i;
+
+ BUILD_BUG_ON(sizeof(struct ar9170_rx_phystatus) != 20);
+
+ for (i = 0; i < 3; i++)
+ if (phy->rssi[i] != 0x80)
+ status->antenna |= BIT(i);
+
+ /* post-process RSSI */
+ for (i = 0; i < 7; i++)
+ if (phy->rssi[i] & 0x80)
+ phy->rssi[i] = ((phy->rssi[i] & 0x7f) + 1) & 0x7f;
+
+ /* TODO: we could do something with phy_errors */
+ status->signal = ar->noise[0] + phy->rssi_combined;
+ status->noise = ar->noise[0];
+}
+
+static struct sk_buff *ar9170_rx_copy_data(u8 *buf, int len)
+{
+ struct sk_buff *skb;
+ int reserved = 0;
+ struct ieee80211_hdr *hdr = (void *) buf;
+
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
+ u8 *qc = ieee80211_get_qos_ctl(hdr);
+ reserved += NET_IP_ALIGN;
+
+ if (*qc & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT)
+ reserved += NET_IP_ALIGN;
+ }
+
+ if (ieee80211_has_a4(hdr->frame_control))
+ reserved += NET_IP_ALIGN;
+
+ reserved = 32 + (reserved & NET_IP_ALIGN);
+
+ skb = dev_alloc_skb(len + reserved);
+ if (likely(skb)) {
+ skb_reserve(skb, reserved);
+ memcpy(skb_put(skb, len), buf, len);
+ }
+
+ return skb;
+}
+
+/*
+ * If the frame alignment is right (or the kernel has
+ * CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS), and there
+ * is only a single MPDU in the USB frame, then we could
+ * submit to mac80211 the SKB directly. However, since
+ * there may be multiple packets in one SKB in stream
+ * mode, and we need to observe the proper ordering,
+ * this is non-trivial.
+ */
+
+static void ar9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len)
+{
+ struct ar9170_rx_head *head;
+ struct ar9170_rx_macstatus *mac;
+ struct ar9170_rx_phystatus *phy = NULL;
+ struct ieee80211_rx_status status;
+ struct sk_buff *skb;
+ int mpdu_len;
+
+ if (unlikely(!IS_STARTED(ar) || len < (sizeof(*mac))))
+ return ;
+
+ /* Received MPDU */
+ mpdu_len = len - sizeof(*mac);
+
+ mac = (void *)(buf + mpdu_len);
+ if (unlikely(mac->error & AR9170_RX_ERROR_FATAL)) {
+ /* this frame is too damaged and can't be used - drop it */
+
+ return ;
+ }
+
+ switch (mac->status & AR9170_RX_STATUS_MPDU_MASK) {
+ case AR9170_RX_STATUS_MPDU_FIRST:
+ /* first mpdu packet has the plcp header */
+ if (likely(mpdu_len >= sizeof(struct ar9170_rx_head))) {
+ head = (void *) buf;
+ memcpy(&ar->rx_mpdu.plcp, (void *) buf,
+ sizeof(struct ar9170_rx_head));
+
+ mpdu_len -= sizeof(struct ar9170_rx_head);
+ buf += sizeof(struct ar9170_rx_head);
+ ar->rx_mpdu.has_plcp = true;
+ } else {
+ if (ar9170_nag_limiter(ar))
+ printk(KERN_ERR "%s: plcp info is clipped.\n",
+ wiphy_name(ar->hw->wiphy));
+ return ;
+ }
+ break;
+
+ case AR9170_RX_STATUS_MPDU_LAST:
+ /* last mpdu has a extra tail with phy status information */
+
+ if (likely(mpdu_len >= sizeof(struct ar9170_rx_phystatus))) {
+ mpdu_len -= sizeof(struct ar9170_rx_phystatus);
+ phy = (void *)(buf + mpdu_len);
+ } else {
+ if (ar9170_nag_limiter(ar))
+ printk(KERN_ERR "%s: frame tail is clipped.\n",
+ wiphy_name(ar->hw->wiphy));
+ return ;
+ }
+
+ case AR9170_RX_STATUS_MPDU_MIDDLE:
+ /* middle mpdus are just data */
+ if (unlikely(!ar->rx_mpdu.has_plcp)) {
+ if (!ar9170_nag_limiter(ar))
+ return ;
+
+ printk(KERN_ERR "%s: rx stream did not start "
+ "with a first_mpdu frame tag.\n",
+ wiphy_name(ar->hw->wiphy));
+
+ return ;
+ }
+
+ head = &ar->rx_mpdu.plcp;
+ break;
+
+ case AR9170_RX_STATUS_MPDU_SINGLE:
+ /* single mpdu - has plcp (head) and phy status (tail) */
+ head = (void *) buf;
+
+ mpdu_len -= sizeof(struct ar9170_rx_head);
+ mpdu_len -= sizeof(struct ar9170_rx_phystatus);
+
+ buf += sizeof(struct ar9170_rx_head);
+ phy = (void *)(buf + mpdu_len);
+ break;
+
+ default:
+ BUG_ON(1);
+ break;
+ }
+
+ if (unlikely(mpdu_len < FCS_LEN))
+ return ;
+
+ memset(&status, 0, sizeof(status));
+ if (unlikely(ar9170_rx_mac_status(ar, head, mac, &status)))
+ return ;
+
+ if (phy)
+ ar9170_rx_phy_status(ar, phy, &status);
+
+ skb = ar9170_rx_copy_data(buf, mpdu_len);
+ if (likely(skb))
+ ieee80211_rx_irqsafe(ar->hw, skb, &status);
+}
+
+void ar9170_rx(struct ar9170 *ar, struct sk_buff *skb)
+{
+ unsigned int i, tlen, resplen, wlen = 0, clen = 0;
+ u8 *tbuf, *respbuf;
+
+ tbuf = skb->data;
+ tlen = skb->len;
+
+ while (tlen >= 4) {
+ clen = tbuf[1] << 8 | tbuf[0];
+ wlen = ALIGN(clen, 4);
+
+ /* check if this is stream has a valid tag.*/
+ if (tbuf[2] != 0 || tbuf[3] != 0x4e) {
+ /*
+ * TODO: handle the highly unlikely event that the
+ * corrupted stream has the TAG at the right position.
+ */
+
+ /* check if the frame can be repaired. */
+ if (!ar->rx_failover_missing) {
+ /* this is no "short read". */
+ if (ar9170_nag_limiter(ar)) {
+ printk(KERN_ERR "%s: missing tag!\n",
+ wiphy_name(ar->hw->wiphy));
+ goto err_telluser;
+ } else
+ goto err_silent;
+ }
+
+ if (ar->rx_failover_missing > tlen) {
+ if (ar9170_nag_limiter(ar)) {
+ printk(KERN_ERR "%s: possible multi "
+ "stream corruption!\n",
+ wiphy_name(ar->hw->wiphy));
+ goto err_telluser;
+ } else
+ goto err_silent;
+ }
+
+ memcpy(skb_put(ar->rx_failover, tlen), tbuf, tlen);
+ ar->rx_failover_missing -= tlen;
+
+ if (ar->rx_failover_missing <= 0) {
+ /*
+ * nested ar9170_rx call!
+ * termination is guranteed, even when the
+ * combined frame also have a element with
+ * a bad tag.
+ */
+
+ ar->rx_failover_missing = 0;
+ ar9170_rx(ar, ar->rx_failover);
+
+ skb_reset_tail_pointer(ar->rx_failover);
+ skb_trim(ar->rx_failover, 0);
+ }
+
+ return ;
+ }
+
+ /* check if stream is clipped */
+ if (wlen > tlen - 4) {
+ if (ar->rx_failover_missing) {
+ /* TODO: handle double stream corruption. */
+ if (ar9170_nag_limiter(ar)) {
+ printk(KERN_ERR "%s: double rx stream "
+ "corruption!\n",
+ wiphy_name(ar->hw->wiphy));
+ goto err_telluser;
+ } else
+ goto err_silent;
+ }
+
+ /*
+ * save incomplete data set.
+ * the firmware will resend the missing bits when
+ * the rx - descriptor comes round again.
+ */
+
+ memcpy(skb_put(ar->rx_failover, tlen), tbuf, tlen);
+ ar->rx_failover_missing = clen - tlen;
+ return ;
+ }
+ resplen = clen;
+ respbuf = tbuf + 4;
+ tbuf += wlen + 4;
+ tlen -= wlen + 4;
+
+ i = 0;
+
+ /* weird thing, but this is the same in the original driver */
+ while (resplen > 2 && i < 12 &&
+ respbuf[0] == 0xff && respbuf[1] == 0xff) {
+ i += 2;
+ resplen -= 2;
+ respbuf += 2;
+ }
+
+ if (resplen < 4)
+ continue;
+
+ /* found the 6 * 0xffff marker? */
+ if (i == 12)
+ ar9170_handle_command_response(ar, respbuf, resplen);
+ else
+ ar9170_handle_mpdu(ar, respbuf, clen);
+ }
+
+ if (tlen) {
+ if (net_ratelimit())
+ printk(KERN_ERR "%s: %d bytes of unprocessed "
+ "data left in rx stream!\n",
+ wiphy_name(ar->hw->wiphy), tlen);
+
+ goto err_telluser;
+ }
+
+ return ;
+
+err_telluser:
+ printk(KERN_ERR "%s: damaged RX stream data [want:%d, "
+ "data:%d, rx:%d, pending:%d ]\n",
+ wiphy_name(ar->hw->wiphy), clen, wlen, tlen,
+ ar->rx_failover_missing);
+
+ if (ar->rx_failover_missing)
+ print_hex_dump_bytes("rxbuf:", DUMP_PREFIX_OFFSET,
+ ar->rx_failover->data,
+ ar->rx_failover->len);
+
+ print_hex_dump_bytes("stream:", DUMP_PREFIX_OFFSET,
+ skb->data, skb->len);
+
+ printk(KERN_ERR "%s: please check your hardware and cables, if "
+ "you see this message frequently.\n",
+ wiphy_name(ar->hw->wiphy));
+
+err_silent:
+ if (ar->rx_failover_missing) {
+ skb_reset_tail_pointer(ar->rx_failover);
+ skb_trim(ar->rx_failover, 0);
+ ar->rx_failover_missing = 0;
+ }
+}
+
+#define AR9170_FILL_QUEUE(queue, ai_fs, cwmin, cwmax, _txop) \
+do { \
+ queue.aifs = ai_fs; \
+ queue.cw_min = cwmin; \
+ queue.cw_max = cwmax; \
+ queue.txop = _txop; \
+} while (0)
+
+static int ar9170_op_start(struct ieee80211_hw *hw)
+{
+ struct ar9170 *ar = hw->priv;
+ int err, i;
+
+ mutex_lock(&ar->mutex);
+
+ ar->filter_changed = 0;
+
+ /* reinitialize queues statistics */
+ memset(&ar->tx_stats, 0, sizeof(ar->tx_stats));
+ for (i = 0; i < __AR9170_NUM_TXQ; i++)
+ ar->tx_stats[i].limit = AR9170_TXQ_DEPTH;
+
+ /* reset QoS defaults */
+ AR9170_FILL_QUEUE(ar->edcf[0], 3, 15, 1023, 0); /* BEST EFFORT*/
+ AR9170_FILL_QUEUE(ar->edcf[1], 7, 15, 1023, 0); /* BACKGROUND */
+ AR9170_FILL_QUEUE(ar->edcf[2], 2, 7, 15, 94); /* VIDEO */
+ AR9170_FILL_QUEUE(ar->edcf[3], 2, 3, 7, 47); /* VOICE */
+ AR9170_FILL_QUEUE(ar->edcf[4], 2, 3, 7, 0); /* SPECIAL */
+
+ ar->bad_hw_nagger = jiffies;
+
+ err = ar->open(ar);
+ if (err)
+ goto out;
+
+ err = ar9170_init_mac(ar);
+ if (err)
+ goto out;
+
+ err = ar9170_set_qos(ar);
+ if (err)
+ goto out;
+
+ err = ar9170_init_phy(ar, IEEE80211_BAND_2GHZ);
+ if (err)
+ goto out;
+
+ err = ar9170_init_rf(ar);
+ if (err)
+ goto out;
+
+ /* start DMA */
+ err = ar9170_write_reg(ar, 0x1c3d30, 0x100);
+ if (err)
+ goto out;
+
+ ar->state = AR9170_STARTED;
+
+out:
+ mutex_unlock(&ar->mutex);
+ return err;
+}
+
+static void ar9170_op_stop(struct ieee80211_hw *hw)
+{
+ struct ar9170 *ar = hw->priv;
+ unsigned int i;
+
+ if (IS_STARTED(ar))
+ ar->state = AR9170_IDLE;
+
+ flush_workqueue(ar->hw->workqueue);
+
+ cancel_delayed_work_sync(&ar->tx_janitor);
+ cancel_work_sync(&ar->filter_config_work);
+ cancel_work_sync(&ar->beacon_work);
+ mutex_lock(&ar->mutex);
+
+ if (IS_ACCEPTING_CMD(ar)) {
+ ar9170_set_leds_state(ar, 0);
+
+ /* stop DMA */
+ ar9170_write_reg(ar, 0x1c3d30, 0);
+ ar->stop(ar);
+ }
+
+ for (i = 0; i < __AR9170_NUM_TXQ; i++) {
+ skb_queue_purge(&ar->tx_pending[i]);
+ skb_queue_purge(&ar->tx_status[i]);
+ }
+ mutex_unlock(&ar->mutex);
+}
+
+static int ar9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr;
+ struct ar9170_tx_control *txc;
+ struct ieee80211_tx_info *info;
+ struct ieee80211_tx_rate *txrate;
+ struct ar9170_tx_info *arinfo;
+ unsigned int queue = skb_get_queue_mapping(skb);
+ u16 keytype = 0;
+ u16 len, icv = 0;
+
+ BUILD_BUG_ON(sizeof(*arinfo) > sizeof(info->rate_driver_data));
+
+ hdr = (void *)skb->data;
+ info = IEEE80211_SKB_CB(skb);
+ len = skb->len;
+
+ txc = (void *)skb_push(skb, sizeof(*txc));
+
+ if (info->control.hw_key) {
+ icv = info->control.hw_key->icv_len;
+
+ switch (info->control.hw_key->alg) {
+ case ALG_WEP:
+ keytype = AR9170_TX_MAC_ENCR_RC4;
+ break;
+ case ALG_TKIP:
+ keytype = AR9170_TX_MAC_ENCR_RC4;
+ break;
+ case ALG_CCMP:
+ keytype = AR9170_TX_MAC_ENCR_AES;
+ break;
+ default:
+ WARN_ON(1);
+ goto err_out;
+ }
+ }
+
+ /* Length */
+ txc->length = cpu_to_le16(len + icv + 4);
+
+ txc->mac_control = cpu_to_le16(AR9170_TX_MAC_HW_DURATION |
+ AR9170_TX_MAC_BACKOFF);
+ txc->mac_control |= cpu_to_le16(ar9170_qos_hwmap[queue] <<
+ AR9170_TX_MAC_QOS_SHIFT);
+ txc->mac_control |= cpu_to_le16(keytype);
+ txc->phy_control = cpu_to_le32(0);
+
+ if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+ txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_NO_ACK);
+
+ txrate = &info->control.rates[0];
+ if (txrate->flags & IEEE80211_TX_RC_USE_CTS_PROTECT)
+ txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_PROT_CTS);
+ else if (txrate->flags & IEEE80211_TX_RC_USE_RTS_CTS)
+ txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_PROT_RTS);
+
+ arinfo = (void *)info->rate_driver_data;
+ arinfo->timeout = jiffies + msecs_to_jiffies(AR9170_QUEUE_TIMEOUT);
+
+ if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) &&
+ (is_valid_ether_addr(ieee80211_get_DA(hdr)))) {
+ if (info->flags & IEEE80211_TX_CTL_AMPDU) {
+ if (unlikely(!info->control.sta))
+ goto err_out;
+
+ txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_AGGR);
+ arinfo->flags = AR9170_TX_FLAG_BLOCK_ACK;
+ goto out;
+ }
+
+ txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_RATE_PROBE);
+ /*
+ * WARNING:
+ * Putting the QoS queue bits into an unexplored territory is
+ * certainly not elegant.
+ *
+ * In my defense: This idea provides a reasonable way to
+ * smuggle valuable information to the tx_status callback.
+ * Also, the idea behind this bit-abuse came straight from
+ * the original driver code.
+ */
+
+ txc->phy_control |=
+ cpu_to_le32(queue << AR9170_TX_PHY_QOS_SHIFT);
+ arinfo->flags = AR9170_TX_FLAG_WAIT_FOR_ACK;
+ } else {
+ arinfo->flags = AR9170_TX_FLAG_NO_ACK;
+ }
+
+out:
+ return 0;
+
+err_out:
+ skb_pull(skb, sizeof(*txc));
+ return -EINVAL;
+}
+
+static void ar9170_tx_prepare_phy(struct ar9170 *ar, struct sk_buff *skb)
+{
+ struct ar9170_tx_control *txc;
+ struct ieee80211_tx_info *info;
+ struct ieee80211_rate *rate = NULL;
+ struct ieee80211_tx_rate *txrate;
+ u32 power, chains;
+
+ txc = (void *) skb->data;
+ info = IEEE80211_SKB_CB(skb);
+ txrate = &info->control.rates[0];
+
+ if (txrate->flags & IEEE80211_TX_RC_GREEN_FIELD)
+ txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_GREENFIELD);
+
+ if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
+ txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_SHORT_PREAMBLE);
+
+ if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_BW_40MHZ);
+ /* this works because 40 MHz is 2 and dup is 3 */
+ if (txrate->flags & IEEE80211_TX_RC_DUP_DATA)
+ txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_BW_40MHZ_DUP);
+
+ if (txrate->flags & IEEE80211_TX_RC_SHORT_GI)
+ txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_SHORT_GI);
+
+ if (txrate->flags & IEEE80211_TX_RC_MCS) {
+ u32 r = txrate->idx;
+ u8 *txpower;
+
+ /* heavy clip control */
+ txc->phy_control |= cpu_to_le32((r & 0x7) << 7);
+
+ r <<= AR9170_TX_PHY_MCS_SHIFT;
+ BUG_ON(r & ~AR9170_TX_PHY_MCS_MASK);
+
+ txc->phy_control |= cpu_to_le32(r & AR9170_TX_PHY_MCS_MASK);
+ txc->phy_control |= cpu_to_le32(AR9170_TX_PHY_MOD_HT);
+
+ if (txrate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
+ if (info->band == IEEE80211_BAND_5GHZ)
+ txpower = ar->power_5G_ht40;
+ else
+ txpower = ar->power_2G_ht40;
+ } else {
+ if (info->band == IEEE80211_BAND_5GHZ)
+ txpower = ar->power_5G_ht20;
+ else
+ txpower = ar->power_2G_ht20;
+ }
+
+ power = txpower[(txrate->idx) & 7];
+ } else {
+ u8 *txpower;
+ u32 mod;
+ u32 phyrate;
+ u8 idx = txrate->idx;
+
+ if (info->band != IEEE80211_BAND_2GHZ) {
+ idx += 4;
+ txpower = ar->power_5G_leg;
+ mod = AR9170_TX_PHY_MOD_OFDM;
+ } else {
+ if (idx < 4) {
+ txpower = ar->power_2G_cck;
+ mod = AR9170_TX_PHY_MOD_CCK;
+ } else {
+ mod = AR9170_TX_PHY_MOD_OFDM;
+ txpower = ar->power_2G_ofdm;
+ }
+ }
+
+ rate = &__ar9170_ratetable[idx];
+
+ phyrate = rate->hw_value & 0xF;
+ power = txpower[(rate->hw_value & 0x30) >> 4];
+ phyrate <<= AR9170_TX_PHY_MCS_SHIFT;
+
+ txc->phy_control |= cpu_to_le32(mod);
+ txc->phy_control |= cpu_to_le32(phyrate);
+ }
+
+ power <<= AR9170_TX_PHY_TX_PWR_SHIFT;
+ power &= AR9170_TX_PHY_TX_PWR_MASK;
+ txc->phy_control |= cpu_to_le32(power);
+
+ /* set TX chains */
+ if (ar->eeprom.tx_mask == 1) {
+ chains = AR9170_TX_PHY_TXCHAIN_1;
+ } else {
+ chains = AR9170_TX_PHY_TXCHAIN_2;
+
+ /* >= 36M legacy OFDM - use only one chain */
+ if (rate && rate->bitrate >= 360)
+ chains = AR9170_TX_PHY_TXCHAIN_1;
+ }
+ txc->phy_control |= cpu_to_le32(chains << AR9170_TX_PHY_TXCHAIN_SHIFT);
+}
+
+static void ar9170_tx(struct ar9170 *ar)
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+ struct ieee80211_tx_info *info;
+ struct ar9170_tx_info *arinfo;
+ unsigned int i, frames, frames_failed, remaining_space;
+ int err;
+ bool schedule_garbagecollector = false;
+
+ BUILD_BUG_ON(sizeof(*arinfo) > sizeof(info->rate_driver_data));
+
+ if (unlikely(!IS_STARTED(ar)))
+ return ;
+
+ remaining_space = AR9170_TX_MAX_PENDING;
+
+ for (i = 0; i < __AR9170_NUM_TXQ; i++) {
+ spin_lock_irqsave(&ar->tx_stats_lock, flags);
+ if (ar->tx_stats[i].len >= ar->tx_stats[i].limit) {
+#ifdef AR9170_QUEUE_DEBUG
+ printk(KERN_DEBUG "%s: queue %d full\n",
+ wiphy_name(ar->hw->wiphy), i);
+
+ __ar9170_dump_txstats(ar);
+ printk(KERN_DEBUG "stuck frames: ===> \n");
+ ar9170_dump_txqueue(ar, &ar->tx_pending[i]);
+ ar9170_dump_txqueue(ar, &ar->tx_status[i]);
+#endif /* AR9170_QUEUE_DEBUG */
+ ieee80211_stop_queue(ar->hw, i);
+ spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
+ continue;
+ }
+
+ frames = min(ar->tx_stats[i].limit - ar->tx_stats[i].len,
+ skb_queue_len(&ar->tx_pending[i]));
+
+ if (remaining_space < frames) {
+#ifdef AR9170_QUEUE_DEBUG
+ printk(KERN_DEBUG "%s: tx quota reached queue:%d, "
+ "remaining slots:%d, needed:%d\n",
+ wiphy_name(ar->hw->wiphy), i, remaining_space,
+ frames);
+
+ ar9170_dump_txstats(ar);
+#endif /* AR9170_QUEUE_DEBUG */
+ frames = remaining_space;
+ }
+
+ ar->tx_stats[i].len += frames;
+ ar->tx_stats[i].count += frames;
+ spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
+
+ if (!frames)
+ continue;
+
+ frames_failed = 0;
+ while (frames) {
+ skb = skb_dequeue(&ar->tx_pending[i]);
+ if (unlikely(!skb)) {
+ frames_failed += frames;
+ frames = 0;
+ break;
+ }
+
+ info = IEEE80211_SKB_CB(skb);
+ arinfo = (void *) info->rate_driver_data;
+
+ /* TODO: cancel stuck frames */
+ arinfo->timeout = jiffies +
+ msecs_to_jiffies(AR9170_TX_TIMEOUT);
+
+#ifdef AR9170_QUEUE_DEBUG
+ printk(KERN_DEBUG "%s: send frame q:%d =>\n",
+ wiphy_name(ar->hw->wiphy), i);
+ ar9170_print_txheader(ar, skb);
+#endif /* AR9170_QUEUE_DEBUG */
+
+ err = ar->tx(ar, skb);
+ if (unlikely(err)) {
+ frames_failed++;
+ dev_kfree_skb_any(skb);
+ } else {
+ remaining_space--;
+ schedule_garbagecollector = true;
+ }
+
+ frames--;
+ }
+
+#ifdef AR9170_QUEUE_DEBUG
+ printk(KERN_DEBUG "%s: ar9170_tx report for queue %d\n",
+ wiphy_name(ar->hw->wiphy), i);
+
+ printk(KERN_DEBUG "%s: unprocessed pending frames left:\n",
+ wiphy_name(ar->hw->wiphy));
+ ar9170_dump_txqueue(ar, &ar->tx_pending[i]);
+#endif /* AR9170_QUEUE_DEBUG */
+
+ if (unlikely(frames_failed)) {
+#ifdef AR9170_QUEUE_DEBUG
+ printk(KERN_DEBUG "%s: frames failed =>\n",
+ wiphy_name(ar->hw->wiphy), frames_failed);
+#endif /* AR9170_QUEUE_DEBUG */
+
+ spin_lock_irqsave(&ar->tx_stats_lock, flags);
+ ar->tx_stats[i].len -= frames_failed;
+ ar->tx_stats[i].count -= frames_failed;
+ ieee80211_wake_queue(ar->hw, i);
+ spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
+ }
+ }
+
+ if (schedule_garbagecollector)
+ queue_delayed_work(ar->hw->workqueue,
+ &ar->tx_janitor,
+ msecs_to_jiffies(AR9170_JANITOR_DELAY));
+}
+
+int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ struct ar9170 *ar = hw->priv;
+ struct ieee80211_tx_info *info;
+
+ if (unlikely(!IS_STARTED(ar)))
+ goto err_free;
+
+ if (unlikely(ar9170_tx_prepare(ar, skb)))
+ goto err_free;
+
+ info = IEEE80211_SKB_CB(skb);
+ if (info->flags & IEEE80211_TX_CTL_AMPDU) {
+ /* drop frame, we do not allow TX A-MPDU aggregation yet. */
+ goto err_free;
+ } else {
+ unsigned int queue = skb_get_queue_mapping(skb);
+
+ ar9170_tx_prepare_phy(ar, skb);
+ skb_queue_tail(&ar->tx_pending[queue], skb);
+ }
+
+ ar9170_tx(ar);
+ return NETDEV_TX_OK;
+
+err_free:
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+}
+
+static int ar9170_op_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_if_init_conf *conf)
+{
+ struct ar9170 *ar = hw->priv;
+ int err = 0;
+
+ mutex_lock(&ar->mutex);
+
+ if (ar->vif) {
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ ar->vif = conf->vif;
+ memcpy(ar->mac_addr, conf->mac_addr, ETH_ALEN);
+
+ if (modparam_nohwcrypt || (ar->vif->type != NL80211_IFTYPE_STATION)) {
+ ar->rx_software_decryption = true;
+ ar->disable_offload = true;
+ }
+
+ ar->cur_filter = 0;
+ ar->want_filter = AR9170_MAC_REG_FTF_DEFAULTS;
+ err = ar9170_update_frame_filter(ar);
+ if (err)
+ goto unlock;
+
+ err = ar9170_set_operating_mode(ar);
+
+unlock:
+ mutex_unlock(&ar->mutex);
+ return err;
+}
+
+static void ar9170_op_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_if_init_conf *conf)
+{
+ struct ar9170 *ar = hw->priv;
+
+ mutex_lock(&ar->mutex);
+ ar->vif = NULL;
+ ar->want_filter = 0;
+ ar9170_update_frame_filter(ar);
+ ar9170_set_beacon_timers(ar);
+ dev_kfree_skb(ar->beacon);
+ ar->beacon = NULL;
+ ar->sniffer_enabled = false;
+ ar->rx_software_decryption = false;
+ ar9170_set_operating_mode(ar);
+ mutex_unlock(&ar->mutex);
+}
+
+static int ar9170_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+ struct ar9170 *ar = hw->priv;
+ int err = 0;
+
+ mutex_lock(&ar->mutex);
+
+ if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) {
+ /* TODO */
+ err = 0;
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_PS) {
+ /* TODO */
+ err = 0;
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_POWER) {
+ /* TODO */
+ err = 0;
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
+ /*
+ * is it long_frame_max_tx_count or short_frame_max_tx_count?
+ */
+
+ err = ar9170_set_hwretry_limit(ar,
+ ar->hw->conf.long_frame_max_tx_count);
+ if (err)
+ goto out;
+ }
+
+ if (changed & BSS_CHANGED_BEACON_INT) {
+ err = ar9170_set_beacon_timers(ar);
+ if (err)
+ goto out;
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+
+ /* adjust slot time for 5 GHz */
+ err = ar9170_set_slot_time(ar);
+ if (err)
+ goto out;
+
+ err = ar9170_set_dyn_sifs_ack(ar);
+ if (err)
+ goto out;
+
+ err = ar9170_set_channel(ar, hw->conf.channel,
+ AR9170_RFI_NONE,
+ nl80211_to_ar9170(hw->conf.channel_type));
+ if (err)
+ goto out;
+ }
+
+out:
+ mutex_unlock(&ar->mutex);
+ return err;
+}
+
+static void ar9170_set_filters(struct work_struct *work)
+{
+ struct ar9170 *ar = container_of(work, struct ar9170,
+ filter_config_work);
+ int err;
+
+ if (unlikely(!IS_STARTED(ar)))
+ return ;
+
+ mutex_lock(&ar->mutex);
+ if (test_and_clear_bit(AR9170_FILTER_CHANGED_MODE,
+ &ar->filter_changed)) {
+ err = ar9170_set_operating_mode(ar);
+ if (err)
+ goto unlock;
+ }
+
+ if (test_and_clear_bit(AR9170_FILTER_CHANGED_MULTICAST,
+ &ar->filter_changed)) {
+ err = ar9170_update_multicast(ar);
+ if (err)
+ goto unlock;
+ }
+
+ if (test_and_clear_bit(AR9170_FILTER_CHANGED_FRAMEFILTER,
+ &ar->filter_changed)) {
+ err = ar9170_update_frame_filter(ar);
+ if (err)
+ goto unlock;
+ }
+
+unlock:
+ mutex_unlock(&ar->mutex);
+}
+
+static void ar9170_op_configure_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags,
+ unsigned int *new_flags,
+ int mc_count, struct dev_mc_list *mclist)
+{
+ struct ar9170 *ar = hw->priv;
+
+ /* mask supported flags */
+ *new_flags &= FIF_ALLMULTI | FIF_CONTROL | FIF_BCN_PRBRESP_PROMISC |
+ FIF_PROMISC_IN_BSS | FIF_FCSFAIL | FIF_PLCPFAIL;
+ ar->filter_state = *new_flags;
+ /*
+ * We can support more by setting the sniffer bit and
+ * then checking the error flags, later.
+ */
+
+ if (changed_flags & FIF_ALLMULTI) {
+ if (*new_flags & FIF_ALLMULTI) {
+ ar->want_mc_hash = ~0ULL;
+ } else {
+ u64 mchash;
+ int i;
+
+ /* always get broadcast frames */
+ mchash = 1ULL << (0xff >> 2);
+
+ for (i = 0; i < mc_count; i++) {
+ if (WARN_ON(!mclist))
+ break;
+ mchash |= 1ULL << (mclist->dmi_addr[5] >> 2);
+ mclist = mclist->next;
+ }
+ ar->want_mc_hash = mchash;
+ }
+ set_bit(AR9170_FILTER_CHANGED_MULTICAST, &ar->filter_changed);
+ }
+
+ if (changed_flags & FIF_CONTROL) {
+ u32 filter = AR9170_MAC_REG_FTF_PSPOLL |
+ AR9170_MAC_REG_FTF_RTS |
+ AR9170_MAC_REG_FTF_CTS |
+ AR9170_MAC_REG_FTF_ACK |
+ AR9170_MAC_REG_FTF_CFE |
+ AR9170_MAC_REG_FTF_CFE_ACK;
+
+ if (*new_flags & FIF_CONTROL)
+ ar->want_filter = ar->cur_filter | filter;
+ else
+ ar->want_filter = ar->cur_filter & ~filter;
+
+ set_bit(AR9170_FILTER_CHANGED_FRAMEFILTER,
+ &ar->filter_changed);
+ }
+
+ if (changed_flags & FIF_PROMISC_IN_BSS) {
+ ar->sniffer_enabled = ((*new_flags) & FIF_PROMISC_IN_BSS) != 0;
+ set_bit(AR9170_FILTER_CHANGED_MODE,
+ &ar->filter_changed);
+ }
+
+ if (likely(IS_STARTED(ar)))
+ queue_work(ar->hw->workqueue, &ar->filter_config_work);
+}
+
+static void ar9170_op_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *bss_conf,
+ u32 changed)
+{
+ struct ar9170 *ar = hw->priv;
+ int err = 0;
+
+ mutex_lock(&ar->mutex);
+
+ if (changed & BSS_CHANGED_BSSID) {
+ memcpy(ar->bssid, bss_conf->bssid, ETH_ALEN);
+ err = ar9170_set_operating_mode(ar);
+ if (err)
+ goto out;
+ }
+
+ if (changed & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED)) {
+ err = ar9170_update_beacon(ar);
+ if (err)
+ goto out;
+
+ err = ar9170_set_beacon_timers(ar);
+ if (err)
+ goto out;
+ }
+
+ if (changed & BSS_CHANGED_ASSOC) {
+#ifndef CONFIG_AR9170_LEDS
+ /* enable assoc LED. */
+ err = ar9170_set_leds_state(ar, bss_conf->assoc ? 2 : 0);
+#endif /* CONFIG_AR9170_LEDS */
+ }
+
+ if (changed & BSS_CHANGED_BEACON_INT) {
+ err = ar9170_set_beacon_timers(ar);
+ if (err)
+ goto out;
+ }
+
+ if (changed & BSS_CHANGED_HT) {
+ /* TODO */
+ err = 0;
+ }
+
+ if (changed & BSS_CHANGED_ERP_SLOT) {
+ err = ar9170_set_slot_time(ar);
+ if (err)
+ goto out;
+ }
+
+ if (changed & BSS_CHANGED_BASIC_RATES) {
+ err = ar9170_set_basic_rates(ar);
+ if (err)
+ goto out;
+ }
+
+out:
+ mutex_unlock(&ar->mutex);
+}
+
+static u64 ar9170_op_get_tsf(struct ieee80211_hw *hw)
+{
+ struct ar9170 *ar = hw->priv;
+ int err;
+ u32 tsf_low;
+ u32 tsf_high;
+ u64 tsf;
+
+ mutex_lock(&ar->mutex);
+ err = ar9170_read_reg(ar, AR9170_MAC_REG_TSF_L, &tsf_low);
+ if (!err)
+ err = ar9170_read_reg(ar, AR9170_MAC_REG_TSF_H, &tsf_high);
+ mutex_unlock(&ar->mutex);
+
+ if (WARN_ON(err))
+ return 0;
+
+ tsf = tsf_high;
+ tsf = (tsf << 32) | tsf_low;
+ return tsf;
+}
+
+static int ar9170_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ struct ar9170 *ar = hw->priv;
+ int err = 0, i;
+ u8 ktype;
+
+ if ((!ar->vif) || (ar->disable_offload))
+ return -EOPNOTSUPP;
+
+ switch (key->alg) {
+ case ALG_WEP:
+ if (key->keylen == WLAN_KEY_LEN_WEP40)
+ ktype = AR9170_ENC_ALG_WEP64;
+ else
+ ktype = AR9170_ENC_ALG_WEP128;
+ break;
+ case ALG_TKIP:
+ ktype = AR9170_ENC_ALG_TKIP;
+ break;
+ case ALG_CCMP:
+ ktype = AR9170_ENC_ALG_AESCCMP;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ mutex_lock(&ar->mutex);
+ if (cmd == SET_KEY) {
+ if (unlikely(!IS_STARTED(ar))) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ /* group keys need all-zeroes address */
+ if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+ sta = NULL;
+
+ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
+ for (i = 0; i < 64; i++)
+ if (!(ar->usedkeys & BIT(i)))
+ break;
+ if (i == 64) {
+ ar->rx_software_decryption = true;
+ ar9170_set_operating_mode(ar);
+ err = -ENOSPC;
+ goto out;
+ }
+ } else {
+ i = 64 + key->keyidx;
+ }
+
+ key->hw_key_idx = i;
+
+ err = ar9170_upload_key(ar, i, sta ? sta->addr : NULL, ktype, 0,
+ key->key, min_t(u8, 16, key->keylen));
+ if (err)
+ goto out;
+
+ if (key->alg == ALG_TKIP) {
+ err = ar9170_upload_key(ar, i, sta ? sta->addr : NULL,
+ ktype, 1, key->key + 16, 16);
+ if (err)
+ goto out;
+
+ /*
+ * hardware is not capable generating the MMIC
+ * for fragmented frames!
+ */
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+ }
+
+ if (i < 64)
+ ar->usedkeys |= BIT(i);
+
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ } else {
+ if (unlikely(!IS_STARTED(ar))) {
+ /* The device is gone... together with the key ;-) */
+ err = 0;
+ goto out;
+ }
+
+ err = ar9170_disable_key(ar, key->hw_key_idx);
+ if (err)
+ goto out;
+
+ if (key->hw_key_idx < 64) {
+ ar->usedkeys &= ~BIT(key->hw_key_idx);
+ } else {
+ err = ar9170_upload_key(ar, key->hw_key_idx, NULL,
+ AR9170_ENC_ALG_NONE, 0,
+ NULL, 0);
+ if (err)
+ goto out;
+
+ if (key->alg == ALG_TKIP) {
+ err = ar9170_upload_key(ar, key->hw_key_idx,
+ NULL,
+ AR9170_ENC_ALG_NONE, 1,
+ NULL, 0);
+ if (err)
+ goto out;
+ }
+
+ }
+ }
+
+ ar9170_regwrite_begin(ar);
+ ar9170_regwrite(AR9170_MAC_REG_ROLL_CALL_TBL_L, ar->usedkeys);
+ ar9170_regwrite(AR9170_MAC_REG_ROLL_CALL_TBL_H, ar->usedkeys >> 32);
+ ar9170_regwrite_finish();
+ err = ar9170_regwrite_result();
+
+out:
+ mutex_unlock(&ar->mutex);
+
+ return err;
+}
+
+static void ar9170_sta_notify(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd cmd,
+ struct ieee80211_sta *sta)
+{
+}
+
+static int ar9170_get_stats(struct ieee80211_hw *hw,
+ struct ieee80211_low_level_stats *stats)
+{
+ struct ar9170 *ar = hw->priv;
+ u32 val;
+ int err;
+
+ mutex_lock(&ar->mutex);
+ err = ar9170_read_reg(ar, AR9170_MAC_REG_TX_RETRY, &val);
+ ar->stats.dot11ACKFailureCount += val;
+
+ memcpy(stats, &ar->stats, sizeof(*stats));
+ mutex_unlock(&ar->mutex);
+
+ return 0;
+}
+
+static int ar9170_get_tx_stats(struct ieee80211_hw *hw,
+ struct ieee80211_tx_queue_stats *tx_stats)
+{
+ struct ar9170 *ar = hw->priv;
+
+ spin_lock_bh(&ar->tx_stats_lock);
+ memcpy(tx_stats, ar->tx_stats, sizeof(tx_stats[0]) * hw->queues);
+ spin_unlock_bh(&ar->tx_stats_lock);
+
+ return 0;
+}
+
+static int ar9170_conf_tx(struct ieee80211_hw *hw, u16 queue,
+ const struct ieee80211_tx_queue_params *param)
+{
+ struct ar9170 *ar = hw->priv;
+ int ret;
+
+ mutex_lock(&ar->mutex);
+ if ((param) && !(queue > __AR9170_NUM_TXQ)) {
+ memcpy(&ar->edcf[ar9170_qos_hwmap[queue]],
+ param, sizeof(*param));
+
+ ret = ar9170_set_qos(ar);
+ } else
+ ret = -EINVAL;
+
+ mutex_unlock(&ar->mutex);
+ return ret;
+}
+
+static int ar9170_ampdu_action(struct ieee80211_hw *hw,
+ enum ieee80211_ampdu_mlme_action action,
+ struct ieee80211_sta *sta, u16 tid, u16 *ssn)
+{
+ switch (action) {
+ case IEEE80211_AMPDU_RX_START:
+ case IEEE80211_AMPDU_RX_STOP:
+ /*
+ * Something goes wrong -- RX locks up
+ * after a while of receiving aggregated
+ * frames -- not enabling for now.
+ */
+ return -EOPNOTSUPP;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static const struct ieee80211_ops ar9170_ops = {
+ .start = ar9170_op_start,
+ .stop = ar9170_op_stop,
+ .tx = ar9170_op_tx,
+ .add_interface = ar9170_op_add_interface,
+ .remove_interface = ar9170_op_remove_interface,
+ .config = ar9170_op_config,
+ .configure_filter = ar9170_op_configure_filter,
+ .conf_tx = ar9170_conf_tx,
+ .bss_info_changed = ar9170_op_bss_info_changed,
+ .get_tsf = ar9170_op_get_tsf,
+ .set_key = ar9170_set_key,
+ .sta_notify = ar9170_sta_notify,
+ .get_stats = ar9170_get_stats,
+ .get_tx_stats = ar9170_get_tx_stats,
+ .ampdu_action = ar9170_ampdu_action,
+};
+
+void *ar9170_alloc(size_t priv_size)
+{
+ struct ieee80211_hw *hw;
+ struct ar9170 *ar;
+ struct sk_buff *skb;
+ int i;
+
+ /*
+ * this buffer is used for rx stream reconstruction.
+ * Under heavy load this device (or the transport layer?)
+ * tends to split the streams into seperate rx descriptors.
+ */
+
+ skb = __dev_alloc_skb(AR9170_MAX_RX_BUFFER_SIZE, GFP_KERNEL);
+ if (!skb)
+ goto err_nomem;
+
+ hw = ieee80211_alloc_hw(priv_size, &ar9170_ops);
+ if (!hw)
+ goto err_nomem;
+
+ ar = hw->priv;
+ ar->hw = hw;
+ ar->rx_failover = skb;
+
+ mutex_init(&ar->mutex);
+ spin_lock_init(&ar->cmdlock);
+ spin_lock_init(&ar->tx_stats_lock);
+ for (i = 0; i < __AR9170_NUM_TXQ; i++) {
+ skb_queue_head_init(&ar->tx_status[i]);
+ skb_queue_head_init(&ar->tx_pending[i]);
+ }
+ ar9170_rx_reset_rx_mpdu(ar);
+ INIT_WORK(&ar->filter_config_work, ar9170_set_filters);
+ INIT_WORK(&ar->beacon_work, ar9170_new_beacon);
+ INIT_DELAYED_WORK(&ar->tx_janitor, ar9170_tx_janitor);
+
+ /* all hw supports 2.4 GHz, so set channel to 1 by default */
+ ar->channel = &ar9170_2ghz_chantable[0];
+
+ /* first part of wiphy init */
+ ar->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_WDS) |
+ BIT(NL80211_IFTYPE_ADHOC);
+ ar->hw->flags |= IEEE80211_HW_RX_INCLUDES_FCS |
+ IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
+ IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_NOISE_DBM;
+
+ ar->hw->queues = __AR9170_NUM_TXQ;
+ ar->hw->extra_tx_headroom = 8;
+ ar->hw->sta_data_size = sizeof(struct ar9170_sta_info);
+
+ ar->hw->max_rates = 1;
+ ar->hw->max_rate_tries = 3;
+
+ for (i = 0; i < ARRAY_SIZE(ar->noise); i++)
+ ar->noise[i] = -95; /* ATH_DEFAULT_NOISE_FLOOR */
+
+ return ar;
+
+err_nomem:
+ kfree_skb(skb);
+ return ERR_PTR(-ENOMEM);
+}
+
+static int ar9170_read_eeprom(struct ar9170 *ar)
+{
+#define RW 8 /* number of words to read at once */
+#define RB (sizeof(u32) * RW)
+ DECLARE_MAC_BUF(mbuf);
+ u8 *eeprom = (void *)&ar->eeprom;
+ u8 *addr = ar->eeprom.mac_address;
+ __le32 offsets[RW];
+ int i, j, err, bands = 0;
+
+ BUILD_BUG_ON(sizeof(ar->eeprom) & 3);
+
+ BUILD_BUG_ON(RB > AR9170_MAX_CMD_LEN - 4);
+#ifndef __CHECKER__
+ /* don't want to handle trailing remains */
+ BUILD_BUG_ON(sizeof(ar->eeprom) % RB);
+#endif
+
+ for (i = 0; i < sizeof(ar->eeprom)/RB; i++) {
+ for (j = 0; j < RW; j++)
+ offsets[j] = cpu_to_le32(AR9170_EEPROM_START +
+ RB * i + 4 * j);
+
+ err = ar->exec_cmd(ar, AR9170_CMD_RREG,
+ RB, (u8 *) &offsets,
+ RB, eeprom + RB * i);
+ if (err)
+ return err;
+ }
+
+#undef RW
+#undef RB
+
+ if (ar->eeprom.length == cpu_to_le16(0xFFFF))
+ return -ENODATA;
+
+ if (ar->eeprom.operating_flags & AR9170_OPFLAG_2GHZ) {
+ ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &ar9170_band_2GHz;
+ bands++;
+ }
+ if (ar->eeprom.operating_flags & AR9170_OPFLAG_5GHZ) {
+ ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &ar9170_band_5GHz;
+ bands++;
+ }
+ /*
+ * I measured this, a bandswitch takes roughly
+ * 135 ms and a frequency switch about 80.
+ *
+ * FIXME: measure these values again once EEPROM settings
+ * are used, that will influence them!
+ */
+ if (bands == 2)
+ ar->hw->channel_change_time = 135 * 1000;
+ else
+ ar->hw->channel_change_time = 80 * 1000;
+
+ ar->regulatory.current_rd = le16_to_cpu(ar->eeprom.reg_domain[0]);
+ ar->regulatory.current_rd_ext = le16_to_cpu(ar->eeprom.reg_domain[1]);
+
+ /* second part of wiphy init */
+ SET_IEEE80211_PERM_ADDR(ar->hw, addr);
+
+ return bands ? 0 : -EINVAL;
+}
+
+static int ar9170_reg_notifier(struct wiphy *wiphy,
+ struct regulatory_request *request)
+{
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ struct ar9170 *ar = hw->priv;
+
+ return ath_reg_notifier_apply(wiphy, request, &ar->regulatory);
+}
+
+int ar9170_register(struct ar9170 *ar, struct device *pdev)
+{
+ int err;
+
+ /* try to read EEPROM, init MAC addr */
+ err = ar9170_read_eeprom(ar);
+ if (err)
+ goto err_out;
+
+ err = ath_regd_init(&ar->regulatory, ar->hw->wiphy,
+ ar9170_reg_notifier);
+ if (err)
+ goto err_out;
+
+ err = ieee80211_register_hw(ar->hw);
+ if (err)
+ goto err_out;
+
+ if (!ath_is_world_regd(&ar->regulatory))
+ regulatory_hint(ar->hw->wiphy, ar->regulatory.alpha2);
+
+ err = ar9170_init_leds(ar);
+ if (err)
+ goto err_unreg;
+
+#ifdef CONFIG_AR9170_LEDS
+ err = ar9170_register_leds(ar);
+ if (err)
+ goto err_unreg;
+#endif /* CONFIG_AR9170_LEDS */
+
+ dev_info(pdev, "Atheros AR9170 is registered as '%s'\n",
+ wiphy_name(ar->hw->wiphy));
+
+ return err;
+
+err_unreg:
+ ieee80211_unregister_hw(ar->hw);
+
+err_out:
+ return err;
+}
+
+void ar9170_unregister(struct ar9170 *ar)
+{
+#ifdef CONFIG_AR9170_LEDS
+ ar9170_unregister_leds(ar);
+#endif /* CONFIG_AR9170_LEDS */
+
+ kfree_skb(ar->rx_failover);
+ ieee80211_unregister_hw(ar->hw);
+ mutex_destroy(&ar->mutex);
+}
diff --git a/drivers/net/wireless/ath/ar9170/phy.c b/drivers/net/wireless/ath/ar9170/phy.c
new file mode 100644
index 00000000000..df86f70cd81
--- /dev/null
+++ b/drivers/net/wireless/ath/ar9170/phy.c
@@ -0,0 +1,1240 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * PHY and RF code
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/bitrev.h>
+#include "ar9170.h"
+#include "cmd.h"
+
+static int ar9170_init_power_cal(struct ar9170 *ar)
+{
+ ar9170_regwrite_begin(ar);
+
+ ar9170_regwrite(0x1bc000 + 0x993c, 0x7f);
+ ar9170_regwrite(0x1bc000 + 0x9934, 0x3f3f3f3f);
+ ar9170_regwrite(0x1bc000 + 0x9938, 0x3f3f3f3f);
+ ar9170_regwrite(0x1bc000 + 0xa234, 0x3f3f3f3f);
+ ar9170_regwrite(0x1bc000 + 0xa238, 0x3f3f3f3f);
+ ar9170_regwrite(0x1bc000 + 0xa38c, 0x3f3f3f3f);
+ ar9170_regwrite(0x1bc000 + 0xa390, 0x3f3f3f3f);
+ ar9170_regwrite(0x1bc000 + 0xa3cc, 0x3f3f3f3f);
+ ar9170_regwrite(0x1bc000 + 0xa3d0, 0x3f3f3f3f);
+ ar9170_regwrite(0x1bc000 + 0xa3d4, 0x3f3f3f3f);
+
+ ar9170_regwrite_finish();
+ return ar9170_regwrite_result();
+}
+
+struct ar9170_phy_init {
+ u32 reg, _5ghz_20, _5ghz_40, _2ghz_40, _2ghz_20;
+};
+
+static struct ar9170_phy_init ar5416_phy_init[] = {
+ { 0x1c5800, 0x00000007, 0x00000007, 0x00000007, 0x00000007, },
+ { 0x1c5804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, },
+ { 0x1c5808, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c580c, 0xad848e19, 0xad848e19, 0xad848e19, 0xad848e19, },
+ { 0x1c5810, 0x7d14e000, 0x7d14e000, 0x7d14e000, 0x7d14e000, },
+ { 0x1c5814, 0x9c0a9f6b, 0x9c0a9f6b, 0x9c0a9f6b, 0x9c0a9f6b, },
+ { 0x1c5818, 0x00000090, 0x00000090, 0x00000090, 0x00000090, },
+ { 0x1c581c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, },
+ { 0x1c5824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, },
+ { 0x1c5828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, },
+ { 0x1c582c, 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000, },
+ { 0x1c5830, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, },
+ { 0x1c5838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, },
+ { 0x1c583c, 0x00200400, 0x00200400, 0x00200400, 0x00200400, },
+ { 0x1c5840, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e, },
+ { 0x1c5844, 0x1372161e, 0x13721c1e, 0x13721c24, 0x137216a4, },
+ { 0x1c5848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, },
+ { 0x1c584c, 0x1284233c, 0x1284233c, 0x1284233c, 0x1284233c, },
+ { 0x1c5850, 0x6c48b4e4, 0x6c48b4e4, 0x6c48b0e4, 0x6c48b0e4, },
+ { 0x1c5854, 0x00000859, 0x00000859, 0x00000859, 0x00000859, },
+ { 0x1c5858, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, },
+ { 0x1c585c, 0x31395c5e, 0x31395c5e, 0x31395c5e, 0x31395c5e, },
+ { 0x1c5860, 0x0004dd10, 0x0004dd10, 0x0004dd20, 0x0004dd20, },
+ { 0x1c5868, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, },
+ { 0x1c586c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, },
+ { 0x1c5900, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5904, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5908, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c590c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5914, 0x000007d0, 0x000007d0, 0x00000898, 0x00000898, },
+ { 0x1c5918, 0x00000118, 0x00000230, 0x00000268, 0x00000134, },
+ { 0x1c591c, 0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff, },
+ { 0x1c5920, 0x0510081c, 0x0510081c, 0x0510001c, 0x0510001c, },
+ { 0x1c5924, 0xd0058a15, 0xd0058a15, 0xd0058a15, 0xd0058a15, },
+ { 0x1c5928, 0x00000001, 0x00000001, 0x00000001, 0x00000001, },
+ { 0x1c592c, 0x00000004, 0x00000004, 0x00000004, 0x00000004, },
+ { 0x1c5934, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+ { 0x1c5938, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+ { 0x1c593c, 0x0000007f, 0x0000007f, 0x0000007f, 0x0000007f, },
+ { 0x1c5944, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020, },
+ { 0x1c5948, 0x9280b212, 0x9280b212, 0x9280b212, 0x9280b212, },
+ { 0x1c594c, 0x00020028, 0x00020028, 0x00020028, 0x00020028, },
+ { 0x1c5954, 0x5d50e188, 0x5d50e188, 0x5d50e188, 0x5d50e188, },
+ { 0x1c5958, 0x00081fff, 0x00081fff, 0x00081fff, 0x00081fff, },
+ { 0x1c5960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, },
+ { 0x1c5964, 0x00001120, 0x00001120, 0x00001120, 0x00001120, },
+ { 0x1c5970, 0x190fb515, 0x190fb515, 0x190fb515, 0x190fb515, },
+ { 0x1c5974, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5978, 0x00000001, 0x00000001, 0x00000001, 0x00000001, },
+ { 0x1c597c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5980, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5984, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5988, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c598c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5990, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5994, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5998, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c599c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c59a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c59a4, 0x00000007, 0x00000007, 0x00000007, 0x00000007, },
+ { 0x1c59a8, 0x001fff00, 0x001fff00, 0x001fff00, 0x001fff00, },
+ { 0x1c59ac, 0x006f00c4, 0x006f00c4, 0x006f00c4, 0x006f00c4, },
+ { 0x1c59b0, 0x03051000, 0x03051000, 0x03051000, 0x03051000, },
+ { 0x1c59b4, 0x00000820, 0x00000820, 0x00000820, 0x00000820, },
+ { 0x1c59c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, },
+ { 0x1c59c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, },
+ { 0x1c59c8, 0x60f6532c, 0x60f6532c, 0x60f6532c, 0x60f6532c, },
+ { 0x1c59cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, },
+ { 0x1c59d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, },
+ { 0x1c59d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c59d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c59dc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c59e0, 0x00000200, 0x00000200, 0x00000200, 0x00000200, },
+ { 0x1c59e4, 0x64646464, 0x64646464, 0x64646464, 0x64646464, },
+ { 0x1c59e8, 0x3c787878, 0x3c787878, 0x3c787878, 0x3c787878, },
+ { 0x1c59ec, 0x000000aa, 0x000000aa, 0x000000aa, 0x000000aa, },
+ { 0x1c59f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c59fc, 0x00001042, 0x00001042, 0x00001042, 0x00001042, },
+ { 0x1c5a00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5a04, 0x00000040, 0x00000040, 0x00000040, 0x00000040, },
+ { 0x1c5a08, 0x00000080, 0x00000080, 0x00000080, 0x00000080, },
+ { 0x1c5a0c, 0x000001a1, 0x000001a1, 0x00000141, 0x00000141, },
+ { 0x1c5a10, 0x000001e1, 0x000001e1, 0x00000181, 0x00000181, },
+ { 0x1c5a14, 0x00000021, 0x00000021, 0x000001c1, 0x000001c1, },
+ { 0x1c5a18, 0x00000061, 0x00000061, 0x00000001, 0x00000001, },
+ { 0x1c5a1c, 0x00000168, 0x00000168, 0x00000041, 0x00000041, },
+ { 0x1c5a20, 0x000001a8, 0x000001a8, 0x000001a8, 0x000001a8, },
+ { 0x1c5a24, 0x000001e8, 0x000001e8, 0x000001e8, 0x000001e8, },
+ { 0x1c5a28, 0x00000028, 0x00000028, 0x00000028, 0x00000028, },
+ { 0x1c5a2c, 0x00000068, 0x00000068, 0x00000068, 0x00000068, },
+ { 0x1c5a30, 0x00000189, 0x00000189, 0x000000a8, 0x000000a8, },
+ { 0x1c5a34, 0x000001c9, 0x000001c9, 0x00000169, 0x00000169, },
+ { 0x1c5a38, 0x00000009, 0x00000009, 0x000001a9, 0x000001a9, },
+ { 0x1c5a3c, 0x00000049, 0x00000049, 0x000001e9, 0x000001e9, },
+ { 0x1c5a40, 0x00000089, 0x00000089, 0x00000029, 0x00000029, },
+ { 0x1c5a44, 0x00000170, 0x00000170, 0x00000069, 0x00000069, },
+ { 0x1c5a48, 0x000001b0, 0x000001b0, 0x00000190, 0x00000190, },
+ { 0x1c5a4c, 0x000001f0, 0x000001f0, 0x000001d0, 0x000001d0, },
+ { 0x1c5a50, 0x00000030, 0x00000030, 0x00000010, 0x00000010, },
+ { 0x1c5a54, 0x00000070, 0x00000070, 0x00000050, 0x00000050, },
+ { 0x1c5a58, 0x00000191, 0x00000191, 0x00000090, 0x00000090, },
+ { 0x1c5a5c, 0x000001d1, 0x000001d1, 0x00000151, 0x00000151, },
+ { 0x1c5a60, 0x00000011, 0x00000011, 0x00000191, 0x00000191, },
+ { 0x1c5a64, 0x00000051, 0x00000051, 0x000001d1, 0x000001d1, },
+ { 0x1c5a68, 0x00000091, 0x00000091, 0x00000011, 0x00000011, },
+ { 0x1c5a6c, 0x000001b8, 0x000001b8, 0x00000051, 0x00000051, },
+ { 0x1c5a70, 0x000001f8, 0x000001f8, 0x00000198, 0x00000198, },
+ { 0x1c5a74, 0x00000038, 0x00000038, 0x000001d8, 0x000001d8, },
+ { 0x1c5a78, 0x00000078, 0x00000078, 0x00000018, 0x00000018, },
+ { 0x1c5a7c, 0x00000199, 0x00000199, 0x00000058, 0x00000058, },
+ { 0x1c5a80, 0x000001d9, 0x000001d9, 0x00000098, 0x00000098, },
+ { 0x1c5a84, 0x00000019, 0x00000019, 0x00000159, 0x00000159, },
+ { 0x1c5a88, 0x00000059, 0x00000059, 0x00000199, 0x00000199, },
+ { 0x1c5a8c, 0x00000099, 0x00000099, 0x000001d9, 0x000001d9, },
+ { 0x1c5a90, 0x000000d9, 0x000000d9, 0x00000019, 0x00000019, },
+ { 0x1c5a94, 0x000000f9, 0x000000f9, 0x00000059, 0x00000059, },
+ { 0x1c5a98, 0x000000f9, 0x000000f9, 0x00000099, 0x00000099, },
+ { 0x1c5a9c, 0x000000f9, 0x000000f9, 0x000000d9, 0x000000d9, },
+ { 0x1c5aa0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5aa4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5aa8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5aac, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ab0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ab4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ab8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5abc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ac0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ac4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ac8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5acc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ad0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ad4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ad8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5adc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ae0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ae4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5ae8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5aec, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5af0, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5af4, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5af8, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5afc, 0x000000f9, 0x000000f9, 0x000000f9, 0x000000f9, },
+ { 0x1c5b00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5b04, 0x00000001, 0x00000001, 0x00000001, 0x00000001, },
+ { 0x1c5b08, 0x00000002, 0x00000002, 0x00000002, 0x00000002, },
+ { 0x1c5b0c, 0x00000003, 0x00000003, 0x00000003, 0x00000003, },
+ { 0x1c5b10, 0x00000004, 0x00000004, 0x00000004, 0x00000004, },
+ { 0x1c5b14, 0x00000005, 0x00000005, 0x00000005, 0x00000005, },
+ { 0x1c5b18, 0x00000008, 0x00000008, 0x00000008, 0x00000008, },
+ { 0x1c5b1c, 0x00000009, 0x00000009, 0x00000009, 0x00000009, },
+ { 0x1c5b20, 0x0000000a, 0x0000000a, 0x0000000a, 0x0000000a, },
+ { 0x1c5b24, 0x0000000b, 0x0000000b, 0x0000000b, 0x0000000b, },
+ { 0x1c5b28, 0x0000000c, 0x0000000c, 0x0000000c, 0x0000000c, },
+ { 0x1c5b2c, 0x0000000d, 0x0000000d, 0x0000000d, 0x0000000d, },
+ { 0x1c5b30, 0x00000010, 0x00000010, 0x00000010, 0x00000010, },
+ { 0x1c5b34, 0x00000011, 0x00000011, 0x00000011, 0x00000011, },
+ { 0x1c5b38, 0x00000012, 0x00000012, 0x00000012, 0x00000012, },
+ { 0x1c5b3c, 0x00000013, 0x00000013, 0x00000013, 0x00000013, },
+ { 0x1c5b40, 0x00000014, 0x00000014, 0x00000014, 0x00000014, },
+ { 0x1c5b44, 0x00000015, 0x00000015, 0x00000015, 0x00000015, },
+ { 0x1c5b48, 0x00000018, 0x00000018, 0x00000018, 0x00000018, },
+ { 0x1c5b4c, 0x00000019, 0x00000019, 0x00000019, 0x00000019, },
+ { 0x1c5b50, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, },
+ { 0x1c5b54, 0x0000001b, 0x0000001b, 0x0000001b, 0x0000001b, },
+ { 0x1c5b58, 0x0000001c, 0x0000001c, 0x0000001c, 0x0000001c, },
+ { 0x1c5b5c, 0x0000001d, 0x0000001d, 0x0000001d, 0x0000001d, },
+ { 0x1c5b60, 0x00000020, 0x00000020, 0x00000020, 0x00000020, },
+ { 0x1c5b64, 0x00000021, 0x00000021, 0x00000021, 0x00000021, },
+ { 0x1c5b68, 0x00000022, 0x00000022, 0x00000022, 0x00000022, },
+ { 0x1c5b6c, 0x00000023, 0x00000023, 0x00000023, 0x00000023, },
+ { 0x1c5b70, 0x00000024, 0x00000024, 0x00000024, 0x00000024, },
+ { 0x1c5b74, 0x00000025, 0x00000025, 0x00000025, 0x00000025, },
+ { 0x1c5b78, 0x00000028, 0x00000028, 0x00000028, 0x00000028, },
+ { 0x1c5b7c, 0x00000029, 0x00000029, 0x00000029, 0x00000029, },
+ { 0x1c5b80, 0x0000002a, 0x0000002a, 0x0000002a, 0x0000002a, },
+ { 0x1c5b84, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b, },
+ { 0x1c5b88, 0x0000002c, 0x0000002c, 0x0000002c, 0x0000002c, },
+ { 0x1c5b8c, 0x0000002d, 0x0000002d, 0x0000002d, 0x0000002d, },
+ { 0x1c5b90, 0x00000030, 0x00000030, 0x00000030, 0x00000030, },
+ { 0x1c5b94, 0x00000031, 0x00000031, 0x00000031, 0x00000031, },
+ { 0x1c5b98, 0x00000032, 0x00000032, 0x00000032, 0x00000032, },
+ { 0x1c5b9c, 0x00000033, 0x00000033, 0x00000033, 0x00000033, },
+ { 0x1c5ba0, 0x00000034, 0x00000034, 0x00000034, 0x00000034, },
+ { 0x1c5ba4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5ba8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bac, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bb0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bb4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bb8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bbc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bc0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bc4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bc8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bcc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bd0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bd4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bd8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bdc, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5be0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5be4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5be8, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bec, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bf0, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bf4, 0x00000035, 0x00000035, 0x00000035, 0x00000035, },
+ { 0x1c5bf8, 0x00000010, 0x00000010, 0x00000010, 0x00000010, },
+ { 0x1c5bfc, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, },
+ { 0x1c5c00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c0c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c10, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c14, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c18, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c1c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c20, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c24, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c28, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c2c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c30, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c34, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c38, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5c3c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5cf0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5cf4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5cf8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c5cfc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c6200, 0x00000008, 0x00000008, 0x0000000e, 0x0000000e, },
+ { 0x1c6204, 0x00000440, 0x00000440, 0x00000440, 0x00000440, },
+ { 0x1c6208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788, },
+ { 0x1c620c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, },
+ { 0x1c6210, 0x40806333, 0x40806333, 0x40806333, 0x40806333, },
+ { 0x1c6214, 0x00106c10, 0x00106c10, 0x00106c10, 0x00106c10, },
+ { 0x1c6218, 0x009c4060, 0x009c4060, 0x009c4060, 0x009c4060, },
+ { 0x1c621c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, },
+ { 0x1c6220, 0x018830c6, 0x018830c6, 0x018830c6, 0x018830c6, },
+ { 0x1c6224, 0x00000400, 0x00000400, 0x00000400, 0x00000400, },
+ { 0x1c6228, 0x000009b5, 0x000009b5, 0x000009b5, 0x000009b5, },
+ { 0x1c622c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c6230, 0x00000108, 0x00000210, 0x00000210, 0x00000108, },
+ { 0x1c6234, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+ { 0x1c6238, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+ { 0x1c623c, 0x13c889af, 0x13c889af, 0x13c889af, 0x13c889af, },
+ { 0x1c6240, 0x38490a20, 0x38490a20, 0x38490a20, 0x38490a20, },
+ { 0x1c6244, 0x00007bb6, 0x00007bb6, 0x00007bb6, 0x00007bb6, },
+ { 0x1c6248, 0x0fff3ffc, 0x0fff3ffc, 0x0fff3ffc, 0x0fff3ffc, },
+ { 0x1c624c, 0x00000001, 0x00000001, 0x00000001, 0x00000001, },
+ { 0x1c6250, 0x0000a000, 0x0000a000, 0x0000a000, 0x0000a000, },
+ { 0x1c6254, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c6258, 0x0cc75380, 0x0cc75380, 0x0cc75380, 0x0cc75380, },
+ { 0x1c625c, 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01, 0x0f0f0f01, },
+ { 0x1c6260, 0xdfa91f01, 0xdfa91f01, 0xdfa91f01, 0xdfa91f01, },
+ { 0x1c6264, 0x00418a11, 0x00418a11, 0x00418a11, 0x00418a11, },
+ { 0x1c6268, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c626c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, },
+ { 0x1c6274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa, },
+ { 0x1c6278, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, },
+ { 0x1c627c, 0x051701ce, 0x051701ce, 0x051701ce, 0x051701ce, },
+ { 0x1c6300, 0x18010000, 0x18010000, 0x18010000, 0x18010000, },
+ { 0x1c6304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402, },
+ { 0x1c6308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06, },
+ { 0x1c630c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b, },
+ { 0x1c6310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b, },
+ { 0x1c6314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a, },
+ { 0x1c6318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf, },
+ { 0x1c631c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f, },
+ { 0x1c6320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f, },
+ { 0x1c6324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f, },
+ { 0x1c6328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000, },
+ { 0x1c632c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c6330, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c6334, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c6338, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c633c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c6340, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c6344, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c6348, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, },
+ { 0x1c634c, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, },
+ { 0x1c6350, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, },
+ { 0x1c6354, 0x0003ffff, 0x0003ffff, 0x0003ffff, 0x0003ffff, },
+ { 0x1c6358, 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f, 0x79a8aa1f, },
+ { 0x1c6388, 0x08000000, 0x08000000, 0x08000000, 0x08000000, },
+ { 0x1c638c, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+ { 0x1c6390, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+ { 0x1c6394, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, },
+ { 0x1c6398, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce, },
+ { 0x1c639c, 0x00000007, 0x00000007, 0x00000007, 0x00000007, },
+ { 0x1c63a0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63a4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63a8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63ac, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63b0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63b4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63b8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63bc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63c0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63c4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63c8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63cc, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+ { 0x1c63d0, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+ { 0x1c63d4, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, 0x3f3f3f3f, },
+ { 0x1c63d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, },
+ { 0x1c63dc, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, },
+ { 0x1c63e0, 0x000000c0, 0x000000c0, 0x000000c0, 0x000000c0, },
+ { 0x1c6848, 0x00180a65, 0x00180a65, 0x00180a68, 0x00180a68, },
+ { 0x1c6920, 0x0510001c, 0x0510001c, 0x0510001c, 0x0510001c, },
+ { 0x1c6960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, },
+ { 0x1c720c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, },
+ { 0x1c726c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, },
+ { 0x1c7848, 0x00180a65, 0x00180a65, 0x00180a68, 0x00180a68, },
+ { 0x1c7920, 0x0510001c, 0x0510001c, 0x0510001c, 0x0510001c, },
+ { 0x1c7960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, },
+ { 0x1c820c, 0x012e8160, 0x012e8160, 0x012a8160, 0x012a8160, },
+ { 0x1c826c, 0x09249126, 0x09249126, 0x09249126, 0x09249126, },
+/* { 0x1c8864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, }, */
+ { 0x1c8864, 0x0001c600, 0x0001c600, 0x0001c600, 0x0001c600, },
+ { 0x1c895c, 0x004b6a8e, 0x004b6a8e, 0x004b6a8e, 0x004b6a8e, },
+ { 0x1c8968, 0x000003ce, 0x000003ce, 0x000003ce, 0x000003ce, },
+ { 0x1c89bc, 0x00181400, 0x00181400, 0x00181400, 0x00181400, },
+ { 0x1c9270, 0x00820820, 0x00820820, 0x00820820, 0x00820820, },
+ { 0x1c935c, 0x066c420f, 0x066c420f, 0x066c420f, 0x066c420f, },
+ { 0x1c9360, 0x0f282207, 0x0f282207, 0x0f282207, 0x0f282207, },
+ { 0x1c9364, 0x17601685, 0x17601685, 0x17601685, 0x17601685, },
+ { 0x1c9368, 0x1f801104, 0x1f801104, 0x1f801104, 0x1f801104, },
+ { 0x1c936c, 0x37a00c03, 0x37a00c03, 0x37a00c03, 0x37a00c03, },
+ { 0x1c9370, 0x3fc40883, 0x3fc40883, 0x3fc40883, 0x3fc40883, },
+ { 0x1c9374, 0x57c00803, 0x57c00803, 0x57c00803, 0x57c00803, },
+ { 0x1c9378, 0x5fd80682, 0x5fd80682, 0x5fd80682, 0x5fd80682, },
+ { 0x1c937c, 0x7fe00482, 0x7fe00482, 0x7fe00482, 0x7fe00482, },
+ { 0x1c9380, 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba, 0x7f3c7bba, },
+ { 0x1c9384, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, 0xf3307ff0, }
+};
+
+int ar9170_init_phy(struct ar9170 *ar, enum ieee80211_band band)
+{
+ int i, err;
+ u32 val;
+ bool is_2ghz = band == IEEE80211_BAND_2GHZ;
+ bool is_40mhz = conf_is_ht40(&ar->hw->conf);
+
+ ar9170_regwrite_begin(ar);
+
+ for (i = 0; i < ARRAY_SIZE(ar5416_phy_init); i++) {
+ if (is_40mhz) {
+ if (is_2ghz)
+ val = ar5416_phy_init[i]._2ghz_40;
+ else
+ val = ar5416_phy_init[i]._5ghz_40;
+ } else {
+ if (is_2ghz)
+ val = ar5416_phy_init[i]._2ghz_20;
+ else
+ val = ar5416_phy_init[i]._5ghz_20;
+ }
+
+ ar9170_regwrite(ar5416_phy_init[i].reg, val);
+ }
+
+ ar9170_regwrite_finish();
+ err = ar9170_regwrite_result();
+ if (err)
+ return err;
+
+ /* XXX: use EEPROM data here! */
+
+ err = ar9170_init_power_cal(ar);
+ if (err)
+ return err;
+
+ /* XXX: remove magic! */
+ if (is_2ghz)
+ err = ar9170_write_reg(ar, 0x1d4014, 0x5163);
+ else
+ err = ar9170_write_reg(ar, 0x1d4014, 0x5143);
+
+ return err;
+}
+
+struct ar9170_rf_init {
+ u32 reg, _5ghz, _2ghz;
+};
+
+static struct ar9170_rf_init ar9170_rf_init[] = {
+ /* bank 0 */
+ { 0x1c58b0, 0x1e5795e5, 0x1e5795e5},
+ { 0x1c58e0, 0x02008020, 0x02008020},
+ /* bank 1 */
+ { 0x1c58b0, 0x02108421, 0x02108421},
+ { 0x1c58ec, 0x00000008, 0x00000008},
+ /* bank 2 */
+ { 0x1c58b0, 0x0e73ff17, 0x0e73ff17},
+ { 0x1c58e0, 0x00000420, 0x00000420},
+ /* bank 3 */
+ { 0x1c58f0, 0x01400018, 0x01c00018},
+ /* bank 4 */
+ { 0x1c58b0, 0x000001a1, 0x000001a1},
+ { 0x1c58e8, 0x00000001, 0x00000001},
+ /* bank 5 */
+ { 0x1c58b0, 0x00000013, 0x00000013},
+ { 0x1c58e4, 0x00000002, 0x00000002},
+ /* bank 6 */
+ { 0x1c58b0, 0x00000000, 0x00000000},
+ { 0x1c58b0, 0x00000000, 0x00000000},
+ { 0x1c58b0, 0x00000000, 0x00000000},
+ { 0x1c58b0, 0x00000000, 0x00000000},
+ { 0x1c58b0, 0x00000000, 0x00000000},
+ { 0x1c58b0, 0x00004000, 0x00004000},
+ { 0x1c58b0, 0x00006c00, 0x00006c00},
+ { 0x1c58b0, 0x00002c00, 0x00002c00},
+ { 0x1c58b0, 0x00004800, 0x00004800},
+ { 0x1c58b0, 0x00004000, 0x00004000},
+ { 0x1c58b0, 0x00006000, 0x00006000},
+ { 0x1c58b0, 0x00001000, 0x00001000},
+ { 0x1c58b0, 0x00004000, 0x00004000},
+ { 0x1c58b0, 0x00007c00, 0x00007c00},
+ { 0x1c58b0, 0x00007c00, 0x00007c00},
+ { 0x1c58b0, 0x00007c00, 0x00007c00},
+ { 0x1c58b0, 0x00007c00, 0x00007c00},
+ { 0x1c58b0, 0x00007c00, 0x00007c00},
+ { 0x1c58b0, 0x00087c00, 0x00087c00},
+ { 0x1c58b0, 0x00007c00, 0x00007c00},
+ { 0x1c58b0, 0x00005400, 0x00005400},
+ { 0x1c58b0, 0x00000c00, 0x00000c00},
+ { 0x1c58b0, 0x00001800, 0x00001800},
+ { 0x1c58b0, 0x00007c00, 0x00007c00},
+ { 0x1c58b0, 0x00006c00, 0x00006c00},
+ { 0x1c58b0, 0x00006c00, 0x00006c00},
+ { 0x1c58b0, 0x00007c00, 0x00007c00},
+ { 0x1c58b0, 0x00002c00, 0x00002c00},
+ { 0x1c58b0, 0x00003c00, 0x00003c00},
+ { 0x1c58b0, 0x00003800, 0x00003800},
+ { 0x1c58b0, 0x00001c00, 0x00001c00},
+ { 0x1c58b0, 0x00000800, 0x00000800},
+ { 0x1c58b0, 0x00000408, 0x00000408},
+ { 0x1c58b0, 0x00004c15, 0x00004c15},
+ { 0x1c58b0, 0x00004188, 0x00004188},
+ { 0x1c58b0, 0x0000201e, 0x0000201e},
+ { 0x1c58b0, 0x00010408, 0x00010408},
+ { 0x1c58b0, 0x00000801, 0x00000801},
+ { 0x1c58b0, 0x00000c08, 0x00000c08},
+ { 0x1c58b0, 0x0000181e, 0x0000181e},
+ { 0x1c58b0, 0x00001016, 0x00001016},
+ { 0x1c58b0, 0x00002800, 0x00002800},
+ { 0x1c58b0, 0x00004010, 0x00004010},
+ { 0x1c58b0, 0x0000081c, 0x0000081c},
+ { 0x1c58b0, 0x00000115, 0x00000115},
+ { 0x1c58b0, 0x00000015, 0x00000015},
+ { 0x1c58b0, 0x00000066, 0x00000066},
+ { 0x1c58b0, 0x0000001c, 0x0000001c},
+ { 0x1c58b0, 0x00000000, 0x00000000},
+ { 0x1c58b0, 0x00000004, 0x00000004},
+ { 0x1c58b0, 0x00000015, 0x00000015},
+ { 0x1c58b0, 0x0000001f, 0x0000001f},
+ { 0x1c58e0, 0x00000000, 0x00000400},
+ /* bank 7 */
+ { 0x1c58b0, 0x000000a0, 0x000000a0},
+ { 0x1c58b0, 0x00000000, 0x00000000},
+ { 0x1c58b0, 0x00000040, 0x00000040},
+ { 0x1c58f0, 0x0000001c, 0x0000001c},
+};
+
+static int ar9170_init_rf_banks_0_7(struct ar9170 *ar, bool band5ghz)
+{
+ int err, i;
+
+ ar9170_regwrite_begin(ar);
+
+ for (i = 0; i < ARRAY_SIZE(ar9170_rf_init); i++)
+ ar9170_regwrite(ar9170_rf_init[i].reg,
+ band5ghz ? ar9170_rf_init[i]._5ghz
+ : ar9170_rf_init[i]._2ghz);
+
+ ar9170_regwrite_finish();
+ err = ar9170_regwrite_result();
+ if (err)
+ printk(KERN_ERR "%s: rf init failed\n",
+ wiphy_name(ar->hw->wiphy));
+ return err;
+}
+
+static int ar9170_init_rf_bank4_pwr(struct ar9170 *ar, bool band5ghz,
+ u32 freq, enum ar9170_bw bw)
+{
+ int err;
+ u32 d0, d1, td0, td1, fd0, fd1;
+ u8 chansel;
+ u8 refsel0 = 1, refsel1 = 0;
+ u8 lf_synth = 0;
+
+ switch (bw) {
+ case AR9170_BW_40_ABOVE:
+ freq += 10;
+ break;
+ case AR9170_BW_40_BELOW:
+ freq -= 10;
+ break;
+ case AR9170_BW_20:
+ break;
+ case __AR9170_NUM_BW:
+ BUG();
+ }
+
+ if (band5ghz) {
+ if (freq % 10) {
+ chansel = (freq - 4800) / 5;
+ } else {
+ chansel = ((freq - 4800) / 10) * 2;
+ refsel0 = 0;
+ refsel1 = 1;
+ }
+ chansel = byte_rev_table[chansel];
+ } else {
+ if (freq == 2484) {
+ chansel = 10 + (freq - 2274) / 5;
+ lf_synth = 1;
+ } else
+ chansel = 16 + (freq - 2272) / 5;
+ chansel *= 4;
+ chansel = byte_rev_table[chansel];
+ }
+
+ d1 = chansel;
+ d0 = 0x21 |
+ refsel0 << 3 |
+ refsel1 << 2 |
+ lf_synth << 1;
+ td0 = d0 & 0x1f;
+ td1 = d1 & 0x1f;
+ fd0 = td1 << 5 | td0;
+
+ td0 = (d0 >> 5) & 0x7;
+ td1 = (d1 >> 5) & 0x7;
+ fd1 = td1 << 5 | td0;
+
+ ar9170_regwrite_begin(ar);
+
+ ar9170_regwrite(0x1c58b0, fd0);
+ ar9170_regwrite(0x1c58e8, fd1);
+
+ ar9170_regwrite_finish();
+ err = ar9170_regwrite_result();
+ if (err)
+ return err;
+
+ msleep(10);
+
+ return 0;
+}
+
+struct ar9170_phy_freq_params {
+ u8 coeff_exp;
+ u16 coeff_man;
+ u8 coeff_exp_shgi;
+ u16 coeff_man_shgi;
+};
+
+struct ar9170_phy_freq_entry {
+ u16 freq;
+ struct ar9170_phy_freq_params params[__AR9170_NUM_BW];
+};
+
+/* NB: must be in sync with channel tables in main! */
+static const struct ar9170_phy_freq_entry ar9170_phy_freq_params[] = {
+/*
+ * freq,
+ * 20MHz,
+ * 40MHz (below),
+ * 40Mhz (above),
+ */
+ { 2412, {
+ { 3, 21737, 3, 19563, },
+ { 3, 21827, 3, 19644, },
+ { 3, 21647, 3, 19482, },
+ } },
+ { 2417, {
+ { 3, 21692, 3, 19523, },
+ { 3, 21782, 3, 19604, },
+ { 3, 21602, 3, 19442, },
+ } },
+ { 2422, {
+ { 3, 21647, 3, 19482, },
+ { 3, 21737, 3, 19563, },
+ { 3, 21558, 3, 19402, },
+ } },
+ { 2427, {
+ { 3, 21602, 3, 19442, },
+ { 3, 21692, 3, 19523, },
+ { 3, 21514, 3, 19362, },
+ } },
+ { 2432, {
+ { 3, 21558, 3, 19402, },
+ { 3, 21647, 3, 19482, },
+ { 3, 21470, 3, 19323, },
+ } },
+ { 2437, {
+ { 3, 21514, 3, 19362, },
+ { 3, 21602, 3, 19442, },
+ { 3, 21426, 3, 19283, },
+ } },
+ { 2442, {
+ { 3, 21470, 3, 19323, },
+ { 3, 21558, 3, 19402, },
+ { 3, 21382, 3, 19244, },
+ } },
+ { 2447, {
+ { 3, 21426, 3, 19283, },
+ { 3, 21514, 3, 19362, },
+ { 3, 21339, 3, 19205, },
+ } },
+ { 2452, {
+ { 3, 21382, 3, 19244, },
+ { 3, 21470, 3, 19323, },
+ { 3, 21295, 3, 19166, },
+ } },
+ { 2457, {
+ { 3, 21339, 3, 19205, },
+ { 3, 21426, 3, 19283, },
+ { 3, 21252, 3, 19127, },
+ } },
+ { 2462, {
+ { 3, 21295, 3, 19166, },
+ { 3, 21382, 3, 19244, },
+ { 3, 21209, 3, 19088, },
+ } },
+ { 2467, {
+ { 3, 21252, 3, 19127, },
+ { 3, 21339, 3, 19205, },
+ { 3, 21166, 3, 19050, },
+ } },
+ { 2472, {
+ { 3, 21209, 3, 19088, },
+ { 3, 21295, 3, 19166, },
+ { 3, 21124, 3, 19011, },
+ } },
+ { 2484, {
+ { 3, 21107, 3, 18996, },
+ { 3, 21192, 3, 19073, },
+ { 3, 21022, 3, 18920, },
+ } },
+ { 4920, {
+ { 4, 21313, 4, 19181, },
+ { 4, 21356, 4, 19220, },
+ { 4, 21269, 4, 19142, },
+ } },
+ { 4940, {
+ { 4, 21226, 4, 19104, },
+ { 4, 21269, 4, 19142, },
+ { 4, 21183, 4, 19065, },
+ } },
+ { 4960, {
+ { 4, 21141, 4, 19027, },
+ { 4, 21183, 4, 19065, },
+ { 4, 21098, 4, 18988, },
+ } },
+ { 4980, {
+ { 4, 21056, 4, 18950, },
+ { 4, 21098, 4, 18988, },
+ { 4, 21014, 4, 18912, },
+ } },
+ { 5040, {
+ { 4, 20805, 4, 18725, },
+ { 4, 20846, 4, 18762, },
+ { 4, 20764, 4, 18687, },
+ } },
+ { 5060, {
+ { 4, 20723, 4, 18651, },
+ { 4, 20764, 4, 18687, },
+ { 4, 20682, 4, 18614, },
+ } },
+ { 5080, {
+ { 4, 20641, 4, 18577, },
+ { 4, 20682, 4, 18614, },
+ { 4, 20601, 4, 18541, },
+ } },
+ { 5180, {
+ { 4, 20243, 4, 18219, },
+ { 4, 20282, 4, 18254, },
+ { 4, 20204, 4, 18183, },
+ } },
+ { 5200, {
+ { 4, 20165, 4, 18148, },
+ { 4, 20204, 4, 18183, },
+ { 4, 20126, 4, 18114, },
+ } },
+ { 5220, {
+ { 4, 20088, 4, 18079, },
+ { 4, 20126, 4, 18114, },
+ { 4, 20049, 4, 18044, },
+ } },
+ { 5240, {
+ { 4, 20011, 4, 18010, },
+ { 4, 20049, 4, 18044, },
+ { 4, 19973, 4, 17976, },
+ } },
+ { 5260, {
+ { 4, 19935, 4, 17941, },
+ { 4, 19973, 4, 17976, },
+ { 4, 19897, 4, 17907, },
+ } },
+ { 5280, {
+ { 4, 19859, 4, 17873, },
+ { 4, 19897, 4, 17907, },
+ { 4, 19822, 4, 17840, },
+ } },
+ { 5300, {
+ { 4, 19784, 4, 17806, },
+ { 4, 19822, 4, 17840, },
+ { 4, 19747, 4, 17772, },
+ } },
+ { 5320, {
+ { 4, 19710, 4, 17739, },
+ { 4, 19747, 4, 17772, },
+ { 4, 19673, 4, 17706, },
+ } },
+ { 5500, {
+ { 4, 19065, 4, 17159, },
+ { 4, 19100, 4, 17190, },
+ { 4, 19030, 4, 17127, },
+ } },
+ { 5520, {
+ { 4, 18996, 4, 17096, },
+ { 4, 19030, 4, 17127, },
+ { 4, 18962, 4, 17065, },
+ } },
+ { 5540, {
+ { 4, 18927, 4, 17035, },
+ { 4, 18962, 4, 17065, },
+ { 4, 18893, 4, 17004, },
+ } },
+ { 5560, {
+ { 4, 18859, 4, 16973, },
+ { 4, 18893, 4, 17004, },
+ { 4, 18825, 4, 16943, },
+ } },
+ { 5580, {
+ { 4, 18792, 4, 16913, },
+ { 4, 18825, 4, 16943, },
+ { 4, 18758, 4, 16882, },
+ } },
+ { 5600, {
+ { 4, 18725, 4, 16852, },
+ { 4, 18758, 4, 16882, },
+ { 4, 18691, 4, 16822, },
+ } },
+ { 5620, {
+ { 4, 18658, 4, 16792, },
+ { 4, 18691, 4, 16822, },
+ { 4, 18625, 4, 16762, },
+ } },
+ { 5640, {
+ { 4, 18592, 4, 16733, },
+ { 4, 18625, 4, 16762, },
+ { 4, 18559, 4, 16703, },
+ } },
+ { 5660, {
+ { 4, 18526, 4, 16673, },
+ { 4, 18559, 4, 16703, },
+ { 4, 18493, 4, 16644, },
+ } },
+ { 5680, {
+ { 4, 18461, 4, 16615, },
+ { 4, 18493, 4, 16644, },
+ { 4, 18428, 4, 16586, },
+ } },
+ { 5700, {
+ { 4, 18396, 4, 16556, },
+ { 4, 18428, 4, 16586, },
+ { 4, 18364, 4, 16527, },
+ } },
+ { 5745, {
+ { 4, 18252, 4, 16427, },
+ { 4, 18284, 4, 16455, },
+ { 4, 18220, 4, 16398, },
+ } },
+ { 5765, {
+ { 4, 18189, 5, 32740, },
+ { 4, 18220, 4, 16398, },
+ { 4, 18157, 5, 32683, },
+ } },
+ { 5785, {
+ { 4, 18126, 5, 32626, },
+ { 4, 18157, 5, 32683, },
+ { 4, 18094, 5, 32570, },
+ } },
+ { 5805, {
+ { 4, 18063, 5, 32514, },
+ { 4, 18094, 5, 32570, },
+ { 4, 18032, 5, 32458, },
+ } },
+ { 5825, {
+ { 4, 18001, 5, 32402, },
+ { 4, 18032, 5, 32458, },
+ { 4, 17970, 5, 32347, },
+ } },
+ { 5170, {
+ { 4, 20282, 4, 18254, },
+ { 4, 20321, 4, 18289, },
+ { 4, 20243, 4, 18219, },
+ } },
+ { 5190, {
+ { 4, 20204, 4, 18183, },
+ { 4, 20243, 4, 18219, },
+ { 4, 20165, 4, 18148, },
+ } },
+ { 5210, {
+ { 4, 20126, 4, 18114, },
+ { 4, 20165, 4, 18148, },
+ { 4, 20088, 4, 18079, },
+ } },
+ { 5230, {
+ { 4, 20049, 4, 18044, },
+ { 4, 20088, 4, 18079, },
+ { 4, 20011, 4, 18010, },
+ } },
+};
+
+static const struct ar9170_phy_freq_params *
+ar9170_get_hw_dyn_params(struct ieee80211_channel *channel,
+ enum ar9170_bw bw)
+{
+ unsigned int chanidx = 0;
+ u16 freq = 2412;
+
+ if (channel) {
+ chanidx = channel->hw_value;
+ freq = channel->center_freq;
+ }
+
+ BUG_ON(chanidx >= ARRAY_SIZE(ar9170_phy_freq_params));
+
+ BUILD_BUG_ON(__AR9170_NUM_BW != 3);
+
+ WARN_ON(ar9170_phy_freq_params[chanidx].freq != freq);
+
+ return &ar9170_phy_freq_params[chanidx].params[bw];
+}
+
+
+int ar9170_init_rf(struct ar9170 *ar)
+{
+ const struct ar9170_phy_freq_params *freqpar;
+ __le32 cmd[7];
+ int err;
+
+ err = ar9170_init_rf_banks_0_7(ar, false);
+ if (err)
+ return err;
+
+ err = ar9170_init_rf_bank4_pwr(ar, false, 2412, AR9170_BW_20);
+ if (err)
+ return err;
+
+ freqpar = ar9170_get_hw_dyn_params(NULL, AR9170_BW_20);
+
+ cmd[0] = cpu_to_le32(2412 * 1000);
+ cmd[1] = cpu_to_le32(0);
+ cmd[2] = cpu_to_le32(1);
+ cmd[3] = cpu_to_le32(freqpar->coeff_exp);
+ cmd[4] = cpu_to_le32(freqpar->coeff_man);
+ cmd[5] = cpu_to_le32(freqpar->coeff_exp_shgi);
+ cmd[6] = cpu_to_le32(freqpar->coeff_man_shgi);
+
+ /* RF_INIT echoes the command back to us */
+ err = ar->exec_cmd(ar, AR9170_CMD_RF_INIT,
+ sizeof(cmd), (u8 *)cmd,
+ sizeof(cmd), (u8 *)cmd);
+ if (err)
+ return err;
+
+ msleep(1000);
+
+ return ar9170_echo_test(ar, 0xaabbccdd);
+}
+
+static int ar9170_find_freq_idx(int nfreqs, u8 *freqs, u8 f)
+{
+ int idx = nfreqs - 2;
+
+ while (idx >= 0) {
+ if (f >= freqs[idx])
+ return idx;
+ idx--;
+ }
+
+ return 0;
+}
+
+static s32 ar9170_interpolate_s32(s32 x, s32 x1, s32 y1, s32 x2, s32 y2)
+{
+ /* nothing to interpolate, it's horizontal */
+ if (y2 == y1)
+ return y1;
+
+ /* check if we hit one of the edges */
+ if (x == x1)
+ return y1;
+ if (x == x2)
+ return y2;
+
+ /* x1 == x2 is bad, hopefully == x */
+ if (x2 == x1)
+ return y1;
+
+ return y1 + (((y2 - y1) * (x - x1)) / (x2 - x1));
+}
+
+static u8 ar9170_interpolate_u8(u8 x, u8 x1, u8 y1, u8 x2, u8 y2)
+{
+#define SHIFT 8
+ s32 y;
+
+ y = ar9170_interpolate_s32(x << SHIFT,
+ x1 << SHIFT, y1 << SHIFT,
+ x2 << SHIFT, y2 << SHIFT);
+
+ /*
+ * XXX: unwrap this expression
+ * Isn't it just DIV_ROUND_UP(y, 1<<SHIFT)?
+ * Can we rely on the compiler to optimise away the div?
+ */
+ return (y >> SHIFT) + ((y & (1<<(SHIFT-1))) >> (SHIFT - 1));
+#undef SHIFT
+}
+
+static int ar9170_set_power_cal(struct ar9170 *ar, u32 freq, enum ar9170_bw bw)
+{
+ struct ar9170_calibration_target_power_legacy *ctpl;
+ struct ar9170_calibration_target_power_ht *ctph;
+ u8 *ctpres;
+ int ntargets;
+ int idx, i, n;
+ u8 ackpower, ackchains, f;
+ u8 pwr_freqs[AR5416_MAX_NUM_TGT_PWRS];
+
+ if (freq < 3000)
+ f = freq - 2300;
+ else
+ f = (freq - 4800)/5;
+
+ /*
+ * cycle through the various modes
+ *
+ * legacy modes first: 5G, 2G CCK, 2G OFDM
+ */
+ for (i = 0; i < 3; i++) {
+ switch (i) {
+ case 0: /* 5 GHz legacy */
+ ctpl = &ar->eeprom.cal_tgt_pwr_5G[0];
+ ntargets = AR5416_NUM_5G_TARGET_PWRS;
+ ctpres = ar->power_5G_leg;
+ break;
+ case 1: /* 2.4 GHz CCK */
+ ctpl = &ar->eeprom.cal_tgt_pwr_2G_cck[0];
+ ntargets = AR5416_NUM_2G_CCK_TARGET_PWRS;
+ ctpres = ar->power_2G_cck;
+ break;
+ case 2: /* 2.4 GHz OFDM */
+ ctpl = &ar->eeprom.cal_tgt_pwr_2G_ofdm[0];
+ ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS;
+ ctpres = ar->power_2G_ofdm;
+ break;
+ default:
+ BUG();
+ }
+
+ for (n = 0; n < ntargets; n++) {
+ if (ctpl[n].freq == 0xff)
+ break;
+ pwr_freqs[n] = ctpl[n].freq;
+ }
+ ntargets = n;
+ idx = ar9170_find_freq_idx(ntargets, pwr_freqs, f);
+ for (n = 0; n < 4; n++)
+ ctpres[n] = ar9170_interpolate_u8(
+ f,
+ ctpl[idx + 0].freq,
+ ctpl[idx + 0].power[n],
+ ctpl[idx + 1].freq,
+ ctpl[idx + 1].power[n]);
+ }
+
+ /*
+ * HT modes now: 5G HT20, 5G HT40, 2G CCK, 2G OFDM, 2G HT20, 2G HT40
+ */
+ for (i = 0; i < 4; i++) {
+ switch (i) {
+ case 0: /* 5 GHz HT 20 */
+ ctph = &ar->eeprom.cal_tgt_pwr_5G_ht20[0];
+ ntargets = AR5416_NUM_5G_TARGET_PWRS;
+ ctpres = ar->power_5G_ht20;
+ break;
+ case 1: /* 5 GHz HT 40 */
+ ctph = &ar->eeprom.cal_tgt_pwr_5G_ht40[0];
+ ntargets = AR5416_NUM_5G_TARGET_PWRS;
+ ctpres = ar->power_5G_ht40;
+ break;
+ case 2: /* 2.4 GHz HT 20 */
+ ctph = &ar->eeprom.cal_tgt_pwr_2G_ht20[0];
+ ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS;
+ ctpres = ar->power_2G_ht20;
+ break;
+ case 3: /* 2.4 GHz HT 40 */
+ ctph = &ar->eeprom.cal_tgt_pwr_2G_ht40[0];
+ ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS;
+ ctpres = ar->power_2G_ht40;
+ break;
+ default:
+ BUG();
+ }
+
+ for (n = 0; n < ntargets; n++) {
+ if (ctph[n].freq == 0xff)
+ break;
+ pwr_freqs[n] = ctph[n].freq;
+ }
+ ntargets = n;
+ idx = ar9170_find_freq_idx(ntargets, pwr_freqs, f);
+ for (n = 0; n < 8; n++)
+ ctpres[n] = ar9170_interpolate_u8(
+ f,
+ ctph[idx + 0].freq,
+ ctph[idx + 0].power[n],
+ ctph[idx + 1].freq,
+ ctph[idx + 1].power[n]);
+ }
+
+ /* set ACK/CTS TX power */
+ ar9170_regwrite_begin(ar);
+
+ if (ar->eeprom.tx_mask != 1)
+ ackchains = AR9170_TX_PHY_TXCHAIN_2;
+ else
+ ackchains = AR9170_TX_PHY_TXCHAIN_1;
+
+ if (freq < 3000)
+ ackpower = ar->power_2G_ofdm[0] & 0x3f;
+ else
+ ackpower = ar->power_5G_leg[0] & 0x3f;
+
+ ar9170_regwrite(0x1c3694, ackpower << 20 | ackchains << 26);
+ ar9170_regwrite(0x1c3bb4, ackpower << 5 | ackchains << 11 |
+ ackpower << 21 | ackchains << 27);
+
+ ar9170_regwrite_finish();
+ return ar9170_regwrite_result();
+}
+
+static int ar9170_calc_noise_dbm(u32 raw_noise)
+{
+ if (raw_noise & 0x100)
+ return ~((raw_noise & 0x0ff) >> 1);
+ else
+ return (raw_noise & 0xff) >> 1;
+}
+
+int ar9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
+ enum ar9170_rf_init_mode rfi, enum ar9170_bw bw)
+{
+ const struct ar9170_phy_freq_params *freqpar;
+ u32 cmd, tmp, offs;
+ __le32 vals[8];
+ int i, err;
+ bool bandswitch;
+
+ /* clear BB heavy clip enable */
+ err = ar9170_write_reg(ar, 0x1c59e0, 0x200);
+ if (err)
+ return err;
+
+ /* may be NULL at first setup */
+ if (ar->channel)
+ bandswitch = ar->channel->band != channel->band;
+ else
+ bandswitch = true;
+
+ /* HW workaround */
+ if (!ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] &&
+ channel->center_freq <= 2417)
+ bandswitch = true;
+
+ err = ar->exec_cmd(ar, AR9170_CMD_FREQ_START, 0, NULL, 0, NULL);
+ if (err)
+ return err;
+
+ if (rfi != AR9170_RFI_NONE || bandswitch) {
+ u32 val = 0x400;
+
+ if (rfi == AR9170_RFI_COLD)
+ val = 0x800;
+
+ /* warm/cold reset BB/ADDA */
+ err = ar9170_write_reg(ar, 0x1d4004, val);
+ if (err)
+ return err;
+
+ err = ar9170_write_reg(ar, 0x1d4004, 0x0);
+ if (err)
+ return err;
+
+ err = ar9170_init_phy(ar, channel->band);
+ if (err)
+ return err;
+
+ err = ar9170_init_rf_banks_0_7(ar,
+ channel->band == IEEE80211_BAND_5GHZ);
+ if (err)
+ return err;
+
+ cmd = AR9170_CMD_RF_INIT;
+ } else {
+ cmd = AR9170_CMD_FREQUENCY;
+ }
+
+ err = ar9170_init_rf_bank4_pwr(ar,
+ channel->band == IEEE80211_BAND_5GHZ,
+ channel->center_freq, bw);
+ if (err)
+ return err;
+
+ switch (bw) {
+ case AR9170_BW_20:
+ tmp = 0x240;
+ offs = 0;
+ break;
+ case AR9170_BW_40_BELOW:
+ tmp = 0x2c4;
+ offs = 3;
+ break;
+ case AR9170_BW_40_ABOVE:
+ tmp = 0x2d4;
+ offs = 1;
+ break;
+ default:
+ BUG();
+ return -ENOSYS;
+ }
+
+ if (ar->eeprom.tx_mask != 1)
+ tmp |= 0x100;
+
+ err = ar9170_write_reg(ar, 0x1c5804, tmp);
+ if (err)
+ return err;
+
+ err = ar9170_set_power_cal(ar, channel->center_freq, bw);
+ if (err)
+ return err;
+
+ freqpar = ar9170_get_hw_dyn_params(channel, bw);
+
+ vals[0] = cpu_to_le32(channel->center_freq * 1000);
+ vals[1] = cpu_to_le32(conf_is_ht40(&ar->hw->conf));
+ vals[2] = cpu_to_le32(offs << 2 | 1);
+ vals[3] = cpu_to_le32(freqpar->coeff_exp);
+ vals[4] = cpu_to_le32(freqpar->coeff_man);
+ vals[5] = cpu_to_le32(freqpar->coeff_exp_shgi);
+ vals[6] = cpu_to_le32(freqpar->coeff_man_shgi);
+ vals[7] = cpu_to_le32(1000);
+
+ err = ar->exec_cmd(ar, cmd, sizeof(vals), (u8 *)vals,
+ sizeof(vals), (u8 *)vals);
+ if (err)
+ return err;
+
+ for (i = 0; i < 2; i++) {
+ ar->noise[i] = ar9170_calc_noise_dbm(
+ (le32_to_cpu(vals[2 + i]) >> 19) & 0x1ff);
+
+ ar->noise[i + 2] = ar9170_calc_noise_dbm(
+ (le32_to_cpu(vals[5 + i]) >> 23) & 0x1ff);
+ }
+
+ ar->channel = channel;
+ return 0;
+}
diff --git a/drivers/net/wireless/ath/ar9170/usb.c b/drivers/net/wireless/ath/ar9170/usb.c
new file mode 100644
index 00000000000..754b1f8d8da
--- /dev/null
+++ b/drivers/net/wireless/ath/ar9170/usb.c
@@ -0,0 +1,929 @@
+/*
+ * Atheros AR9170 driver
+ *
+ * USB - frontend
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, Christian Lamparter <chunkeey@web.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+#include "ar9170.h"
+#include "cmd.h"
+#include "hw.h"
+#include "usb.h"
+
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_AUTHOR("Christian Lamparter <chunkeey@web.de>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atheros AR9170 802.11n USB wireless");
+MODULE_FIRMWARE("ar9170.fw");
+MODULE_FIRMWARE("ar9170-1.fw");
+MODULE_FIRMWARE("ar9170-2.fw");
+
+enum ar9170_requirements {
+ AR9170_REQ_FW1_ONLY = 1,
+};
+
+static struct usb_device_id ar9170_usb_ids[] = {
+ /* Atheros 9170 */
+ { USB_DEVICE(0x0cf3, 0x9170) },
+ /* Atheros TG121N */
+ { USB_DEVICE(0x0cf3, 0x1001) },
+ /* Cace Airpcap NX */
+ { USB_DEVICE(0xcace, 0x0300) },
+ /* D-Link DWA 160A */
+ { USB_DEVICE(0x07d1, 0x3c10) },
+ /* Netgear WNDA3100 */
+ { USB_DEVICE(0x0846, 0x9010) },
+ /* Netgear WN111 v2 */
+ { USB_DEVICE(0x0846, 0x9001) },
+ /* Zydas ZD1221 */
+ { USB_DEVICE(0x0ace, 0x1221) },
+ /* ZyXEL NWD271N */
+ { USB_DEVICE(0x0586, 0x3417) },
+ /* Z-Com UB81 BG */
+ { USB_DEVICE(0x0cde, 0x0023) },
+ /* Z-Com UB82 ABG */
+ { USB_DEVICE(0x0cde, 0x0026) },
+ /* Arcadyan WN7512 */
+ { USB_DEVICE(0x083a, 0xf522) },
+ /* Planex GWUS300 */
+ { USB_DEVICE(0x2019, 0x5304) },
+ /* IO-Data WNGDNUS2 */
+ { USB_DEVICE(0x04bb, 0x093f) },
+ /* AVM FRITZ!WLAN USB Stick N */
+ { USB_DEVICE(0x057C, 0x8401) },
+ /* AVM FRITZ!WLAN USB Stick N 2.4 */
+ { USB_DEVICE(0x057C, 0x8402), .driver_info = AR9170_REQ_FW1_ONLY },
+
+ /* terminate */
+ {}
+};
+MODULE_DEVICE_TABLE(usb, ar9170_usb_ids);
+
+static void ar9170_usb_submit_urb(struct ar9170_usb *aru)
+{
+ struct urb *urb;
+ unsigned long flags;
+ int err;
+
+ if (unlikely(!IS_STARTED(&aru->common)))
+ return ;
+
+ spin_lock_irqsave(&aru->tx_urb_lock, flags);
+ if (aru->tx_submitted_urbs >= AR9170_NUM_TX_URBS) {
+ spin_unlock_irqrestore(&aru->tx_urb_lock, flags);
+ return ;
+ }
+ aru->tx_submitted_urbs++;
+
+ urb = usb_get_from_anchor(&aru->tx_pending);
+ if (!urb) {
+ aru->tx_submitted_urbs--;
+ spin_unlock_irqrestore(&aru->tx_urb_lock, flags);
+
+ return ;
+ }
+ spin_unlock_irqrestore(&aru->tx_urb_lock, flags);
+
+ aru->tx_pending_urbs--;
+ usb_anchor_urb(urb, &aru->tx_submitted);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (unlikely(err)) {
+ if (ar9170_nag_limiter(&aru->common))
+ dev_err(&aru->udev->dev, "submit_urb failed (%d).\n",
+ err);
+
+ usb_unanchor_urb(urb);
+ aru->tx_submitted_urbs--;
+ ar9170_tx_callback(&aru->common, urb->context);
+ }
+
+ usb_free_urb(urb);
+}
+
+static void ar9170_usb_tx_urb_complete_frame(struct urb *urb)
+{
+ struct sk_buff *skb = urb->context;
+ struct ar9170_usb *aru = (struct ar9170_usb *)
+ usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
+
+ if (unlikely(!aru)) {
+ dev_kfree_skb_irq(skb);
+ return ;
+ }
+
+ aru->tx_submitted_urbs--;
+
+ ar9170_tx_callback(&aru->common, skb);
+
+ ar9170_usb_submit_urb(aru);
+}
+
+static void ar9170_usb_tx_urb_complete(struct urb *urb)
+{
+}
+
+static void ar9170_usb_irq_completed(struct urb *urb)
+{
+ struct ar9170_usb *aru = urb->context;
+
+ switch (urb->status) {
+ /* everything is fine */
+ case 0:
+ break;
+
+ /* disconnect */
+ case -ENOENT:
+ case -ECONNRESET:
+ case -ENODEV:
+ case -ESHUTDOWN:
+ goto free;
+
+ default:
+ goto resubmit;
+ }
+
+ ar9170_handle_command_response(&aru->common, urb->transfer_buffer,
+ urb->actual_length);
+
+resubmit:
+ usb_anchor_urb(urb, &aru->rx_submitted);
+ if (usb_submit_urb(urb, GFP_ATOMIC)) {
+ usb_unanchor_urb(urb);
+ goto free;
+ }
+
+ return;
+
+free:
+ usb_buffer_free(aru->udev, 64, urb->transfer_buffer, urb->transfer_dma);
+}
+
+static void ar9170_usb_rx_completed(struct urb *urb)
+{
+ struct sk_buff *skb = urb->context;
+ struct ar9170_usb *aru = (struct ar9170_usb *)
+ usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
+ int err;
+
+ if (!aru)
+ goto free;
+
+ switch (urb->status) {
+ /* everything is fine */
+ case 0:
+ break;
+
+ /* disconnect */
+ case -ENOENT:
+ case -ECONNRESET:
+ case -ENODEV:
+ case -ESHUTDOWN:
+ goto free;
+
+ default:
+ goto resubmit;
+ }
+
+ skb_put(skb, urb->actual_length);
+ ar9170_rx(&aru->common, skb);
+
+resubmit:
+ skb_reset_tail_pointer(skb);
+ skb_trim(skb, 0);
+
+ usb_anchor_urb(urb, &aru->rx_submitted);
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (unlikely(err)) {
+ usb_unanchor_urb(urb);
+ goto free;
+ }
+
+ return ;
+
+free:
+ dev_kfree_skb_irq(skb);
+}
+
+static int ar9170_usb_prep_rx_urb(struct ar9170_usb *aru,
+ struct urb *urb, gfp_t gfp)
+{
+ struct sk_buff *skb;
+
+ skb = __dev_alloc_skb(AR9170_MAX_RX_BUFFER_SIZE + 32, gfp);
+ if (!skb)
+ return -ENOMEM;
+
+ /* reserve some space for mac80211's radiotap */
+ skb_reserve(skb, 32);
+
+ usb_fill_bulk_urb(urb, aru->udev,
+ usb_rcvbulkpipe(aru->udev, AR9170_EP_RX),
+ skb->data, min(skb_tailroom(skb),
+ AR9170_MAX_RX_BUFFER_SIZE),
+ ar9170_usb_rx_completed, skb);
+
+ return 0;
+}
+
+static int ar9170_usb_alloc_rx_irq_urb(struct ar9170_usb *aru)
+{
+ struct urb *urb = NULL;
+ void *ibuf;
+ int err = -ENOMEM;
+
+ /* initialize interrupt endpoint */
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ goto out;
+
+ ibuf = usb_buffer_alloc(aru->udev, 64, GFP_KERNEL, &urb->transfer_dma);
+ if (!ibuf)
+ goto out;
+
+ usb_fill_int_urb(urb, aru->udev,
+ usb_rcvintpipe(aru->udev, AR9170_EP_IRQ), ibuf,
+ 64, ar9170_usb_irq_completed, aru, 1);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ usb_anchor_urb(urb, &aru->rx_submitted);
+ err = usb_submit_urb(urb, GFP_KERNEL);
+ if (err) {
+ usb_unanchor_urb(urb);
+ usb_buffer_free(aru->udev, 64, urb->transfer_buffer,
+ urb->transfer_dma);
+ }
+
+out:
+ usb_free_urb(urb);
+ return err;
+}
+
+static int ar9170_usb_alloc_rx_bulk_urbs(struct ar9170_usb *aru)
+{
+ struct urb *urb;
+ int i;
+ int err = -EINVAL;
+
+ for (i = 0; i < AR9170_NUM_RX_URBS; i++) {
+ err = -ENOMEM;
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ goto err_out;
+
+ err = ar9170_usb_prep_rx_urb(aru, urb, GFP_KERNEL);
+ if (err) {
+ usb_free_urb(urb);
+ goto err_out;
+ }
+
+ usb_anchor_urb(urb, &aru->rx_submitted);
+ err = usb_submit_urb(urb, GFP_KERNEL);
+ if (err) {
+ usb_unanchor_urb(urb);
+ dev_kfree_skb_any((void *) urb->transfer_buffer);
+ usb_free_urb(urb);
+ goto err_out;
+ }
+ usb_free_urb(urb);
+ }
+
+ /* the device now waiting for a firmware. */
+ aru->common.state = AR9170_IDLE;
+ return 0;
+
+err_out:
+
+ usb_kill_anchored_urbs(&aru->rx_submitted);
+ return err;
+}
+
+static int ar9170_usb_flush(struct ar9170 *ar)
+{
+ struct ar9170_usb *aru = (void *) ar;
+ struct urb *urb;
+ int ret, err = 0;
+
+ if (IS_STARTED(ar))
+ aru->common.state = AR9170_IDLE;
+
+ usb_wait_anchor_empty_timeout(&aru->tx_pending,
+ msecs_to_jiffies(800));
+ while ((urb = usb_get_from_anchor(&aru->tx_pending))) {
+ ar9170_tx_callback(&aru->common, (void *) urb->context);
+ usb_free_urb(urb);
+ }
+
+ /* lets wait a while until the tx - queues are dried out */
+ ret = usb_wait_anchor_empty_timeout(&aru->tx_submitted,
+ msecs_to_jiffies(100));
+ if (ret == 0)
+ err = -ETIMEDOUT;
+
+ usb_kill_anchored_urbs(&aru->tx_submitted);
+
+ if (IS_ACCEPTING_CMD(ar))
+ aru->common.state = AR9170_STARTED;
+
+ return err;
+}
+
+static void ar9170_usb_cancel_urbs(struct ar9170_usb *aru)
+{
+ int err;
+
+ aru->common.state = AR9170_UNKNOWN_STATE;
+
+ err = ar9170_usb_flush(&aru->common);
+ if (err)
+ dev_err(&aru->udev->dev, "stuck tx urbs!\n");
+
+ usb_poison_anchored_urbs(&aru->tx_submitted);
+ usb_poison_anchored_urbs(&aru->rx_submitted);
+}
+
+static int ar9170_usb_exec_cmd(struct ar9170 *ar, enum ar9170_cmd cmd,
+ unsigned int plen, void *payload,
+ unsigned int outlen, void *out)
+{
+ struct ar9170_usb *aru = (void *) ar;
+ struct urb *urb = NULL;
+ unsigned long flags;
+ int err = -ENOMEM;
+
+ if (unlikely(!IS_ACCEPTING_CMD(ar)))
+ return -EPERM;
+
+ if (WARN_ON(plen > AR9170_MAX_CMD_LEN - 4))
+ return -EINVAL;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (unlikely(!urb))
+ goto err_free;
+
+ ar->cmdbuf[0] = cpu_to_le32(plen);
+ ar->cmdbuf[0] |= cpu_to_le32(cmd << 8);
+ /* writing multiple regs fills this buffer already */
+ if (plen && payload != (u8 *)(&ar->cmdbuf[1]))
+ memcpy(&ar->cmdbuf[1], payload, plen);
+
+ spin_lock_irqsave(&aru->common.cmdlock, flags);
+ aru->readbuf = (u8 *)out;
+ aru->readlen = outlen;
+ spin_unlock_irqrestore(&aru->common.cmdlock, flags);
+
+ usb_fill_int_urb(urb, aru->udev,
+ usb_sndbulkpipe(aru->udev, AR9170_EP_CMD),
+ aru->common.cmdbuf, plen + 4,
+ ar9170_usb_tx_urb_complete, NULL, 1);
+
+ usb_anchor_urb(urb, &aru->tx_submitted);
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (unlikely(err)) {
+ usb_unanchor_urb(urb);
+ usb_free_urb(urb);
+ goto err_unbuf;
+ }
+ usb_free_urb(urb);
+
+ err = wait_for_completion_timeout(&aru->cmd_wait, HZ);
+ if (err == 0) {
+ err = -ETIMEDOUT;
+ goto err_unbuf;
+ }
+
+ if (aru->readlen != outlen) {
+ err = -EMSGSIZE;
+ goto err_unbuf;
+ }
+
+ return 0;
+
+err_unbuf:
+ /* Maybe the device was removed in the second we were waiting? */
+ if (IS_STARTED(ar)) {
+ dev_err(&aru->udev->dev, "no command feedback "
+ "received (%d).\n", err);
+
+ /* provide some maybe useful debug information */
+ print_hex_dump_bytes("ar9170 cmd: ", DUMP_PREFIX_NONE,
+ aru->common.cmdbuf, plen + 4);
+ dump_stack();
+ }
+
+ /* invalidate to avoid completing the next prematurely */
+ spin_lock_irqsave(&aru->common.cmdlock, flags);
+ aru->readbuf = NULL;
+ aru->readlen = 0;
+ spin_unlock_irqrestore(&aru->common.cmdlock, flags);
+
+err_free:
+
+ return err;
+}
+
+static int ar9170_usb_tx(struct ar9170 *ar, struct sk_buff *skb)
+{
+ struct ar9170_usb *aru = (struct ar9170_usb *) ar;
+ struct urb *urb;
+
+ if (unlikely(!IS_STARTED(ar))) {
+ /* Seriously, what were you drink... err... thinking!? */
+ return -EPERM;
+ }
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (unlikely(!urb))
+ return -ENOMEM;
+
+ usb_fill_bulk_urb(urb, aru->udev,
+ usb_sndbulkpipe(aru->udev, AR9170_EP_TX),
+ skb->data, skb->len,
+ ar9170_usb_tx_urb_complete_frame, skb);
+ urb->transfer_flags |= URB_ZERO_PACKET;
+
+ usb_anchor_urb(urb, &aru->tx_pending);
+ aru->tx_pending_urbs++;
+
+ usb_free_urb(urb);
+
+ ar9170_usb_submit_urb(aru);
+ return 0;
+}
+
+static void ar9170_usb_callback_cmd(struct ar9170 *ar, u32 len , void *buffer)
+{
+ struct ar9170_usb *aru = (void *) ar;
+ unsigned long flags;
+ u32 in, out;
+
+ if (unlikely(!buffer))
+ return ;
+
+ in = le32_to_cpup((__le32 *)buffer);
+ out = le32_to_cpu(ar->cmdbuf[0]);
+
+ /* mask off length byte */
+ out &= ~0xFF;
+
+ if (aru->readlen >= 0) {
+ /* add expected length */
+ out |= aru->readlen;
+ } else {
+ /* add obtained length */
+ out |= in & 0xFF;
+ }
+
+ /*
+ * Some commands (e.g: AR9170_CMD_FREQUENCY) have a variable response
+ * length and we cannot predict the correct length in advance.
+ * So we only check if we provided enough space for the data.
+ */
+ if (unlikely(out < in)) {
+ dev_warn(&aru->udev->dev, "received invalid command response "
+ "got %d bytes, instead of %d bytes "
+ "and the resp length is %d bytes\n",
+ in, out, len);
+ print_hex_dump_bytes("ar9170 invalid resp: ",
+ DUMP_PREFIX_OFFSET, buffer, len);
+ /*
+ * Do not complete, then the command times out,
+ * and we get a stack trace from there.
+ */
+ return ;
+ }
+
+ spin_lock_irqsave(&aru->common.cmdlock, flags);
+ if (aru->readbuf && len > 0) {
+ memcpy(aru->readbuf, buffer + 4, len - 4);
+ aru->readbuf = NULL;
+ }
+ complete(&aru->cmd_wait);
+ spin_unlock_irqrestore(&aru->common.cmdlock, flags);
+}
+
+static int ar9170_usb_upload(struct ar9170_usb *aru, const void *data,
+ size_t len, u32 addr, bool complete)
+{
+ int transfer, err;
+ u8 *buf = kmalloc(4096, GFP_KERNEL);
+
+ if (!buf)
+ return -ENOMEM;
+
+ while (len) {
+ transfer = min_t(int, len, 4096);
+ memcpy(buf, data, transfer);
+
+ err = usb_control_msg(aru->udev, usb_sndctrlpipe(aru->udev, 0),
+ 0x30 /* FW DL */, 0x40 | USB_DIR_OUT,
+ addr >> 8, 0, buf, transfer, 1000);
+
+ if (err < 0) {
+ kfree(buf);
+ return err;
+ }
+
+ len -= transfer;
+ data += transfer;
+ addr += transfer;
+ }
+ kfree(buf);
+
+ if (complete) {
+ err = usb_control_msg(aru->udev, usb_sndctrlpipe(aru->udev, 0),
+ 0x31 /* FW DL COMPLETE */,
+ 0x40 | USB_DIR_OUT, 0, 0, NULL, 0, 5000);
+ }
+
+ return 0;
+}
+
+static int ar9170_usb_request_firmware(struct ar9170_usb *aru)
+{
+ int err = 0;
+
+ err = request_firmware(&aru->firmware, "ar9170.fw",
+ &aru->udev->dev);
+ if (!err) {
+ aru->init_values = NULL;
+ return 0;
+ }
+
+ if (aru->req_one_stage_fw) {
+ dev_err(&aru->udev->dev, "ar9170.fw firmware file "
+ "not found and is required for this device\n");
+ return -EINVAL;
+ }
+
+ dev_err(&aru->udev->dev, "ar9170.fw firmware file "
+ "not found, trying old firmware...\n");
+
+ err = request_firmware(&aru->init_values, "ar9170-1.fw",
+ &aru->udev->dev);
+
+ err = request_firmware(&aru->firmware, "ar9170-2.fw", &aru->udev->dev);
+ if (err) {
+ release_firmware(aru->init_values);
+ dev_err(&aru->udev->dev, "file with init values not found.\n");
+ return err;
+ }
+
+ return err;
+}
+
+static int ar9170_usb_reset(struct ar9170_usb *aru)
+{
+ int ret, lock = (aru->intf->condition != USB_INTERFACE_BINDING);
+
+ if (lock) {
+ ret = usb_lock_device_for_reset(aru->udev, aru->intf);
+ if (ret < 0) {
+ dev_err(&aru->udev->dev, "unable to lock device "
+ "for reset (%d).\n", ret);
+ return ret;
+ }
+ }
+
+ ret = usb_reset_device(aru->udev);
+ if (lock)
+ usb_unlock_device(aru->udev);
+
+ /* let it rest - for a second - */
+ msleep(1000);
+
+ return ret;
+}
+
+static int ar9170_usb_upload_firmware(struct ar9170_usb *aru)
+{
+ int err;
+
+ if (!aru->init_values)
+ goto upload_fw_start;
+
+ /* First, upload initial values to device RAM */
+ err = ar9170_usb_upload(aru, aru->init_values->data,
+ aru->init_values->size, 0x102800, false);
+ if (err) {
+ dev_err(&aru->udev->dev, "firmware part 1 "
+ "upload failed (%d).\n", err);
+ return err;
+ }
+
+upload_fw_start:
+
+ /* Then, upload the firmware itself and start it */
+ return ar9170_usb_upload(aru, aru->firmware->data, aru->firmware->size,
+ 0x200000, true);
+}
+
+static int ar9170_usb_init_transport(struct ar9170_usb *aru)
+{
+ struct ar9170 *ar = (void *) &aru->common;
+ int err;
+
+ ar9170_regwrite_begin(ar);
+
+ /* Set USB Rx stream mode MAX packet number to 2 */
+ ar9170_regwrite(AR9170_USB_REG_MAX_AGG_UPLOAD, 0x4);
+
+ /* Set USB Rx stream mode timeout to 10us */
+ ar9170_regwrite(AR9170_USB_REG_UPLOAD_TIME_CTL, 0x80);
+
+ ar9170_regwrite_finish();
+
+ err = ar9170_regwrite_result();
+ if (err)
+ dev_err(&aru->udev->dev, "USB setup failed (%d).\n", err);
+
+ return err;
+}
+
+static void ar9170_usb_stop(struct ar9170 *ar)
+{
+ struct ar9170_usb *aru = (void *) ar;
+ int ret;
+
+ if (IS_ACCEPTING_CMD(ar))
+ aru->common.state = AR9170_STOPPED;
+
+ ret = ar9170_usb_flush(ar);
+ if (ret)
+ dev_err(&aru->udev->dev, "kill pending tx urbs.\n");
+
+ usb_poison_anchored_urbs(&aru->tx_submitted);
+
+ /*
+ * Note:
+ * So far we freed all tx urbs, but we won't dare to touch any rx urbs.
+ * Else we would end up with a unresponsive device...
+ */
+}
+
+static int ar9170_usb_open(struct ar9170 *ar)
+{
+ struct ar9170_usb *aru = (void *) ar;
+ int err;
+
+ usb_unpoison_anchored_urbs(&aru->tx_submitted);
+ err = ar9170_usb_init_transport(aru);
+ if (err) {
+ usb_poison_anchored_urbs(&aru->tx_submitted);
+ return err;
+ }
+
+ aru->common.state = AR9170_IDLE;
+ return 0;
+}
+
+static int ar9170_usb_init_device(struct ar9170_usb *aru)
+{
+ int err;
+
+ err = ar9170_usb_alloc_rx_irq_urb(aru);
+ if (err)
+ goto err_out;
+
+ err = ar9170_usb_alloc_rx_bulk_urbs(aru);
+ if (err)
+ goto err_unrx;
+
+ err = ar9170_usb_upload_firmware(aru);
+ if (err) {
+ err = ar9170_echo_test(&aru->common, 0x60d43110);
+ if (err) {
+ /* force user invention, by disabling the device */
+ err = usb_driver_set_configuration(aru->udev, -1);
+ dev_err(&aru->udev->dev, "device is in a bad state. "
+ "please reconnect it!\n");
+ goto err_unrx;
+ }
+ }
+
+ return 0;
+
+err_unrx:
+ ar9170_usb_cancel_urbs(aru);
+
+err_out:
+ return err;
+}
+
+static bool ar9170_requires_one_stage(const struct usb_device_id *id)
+{
+ if (!id->driver_info)
+ return false;
+ if (id->driver_info == AR9170_REQ_FW1_ONLY)
+ return true;
+ return false;
+}
+
+static int ar9170_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct ar9170_usb *aru;
+ struct ar9170 *ar;
+ struct usb_device *udev;
+ int err;
+
+ aru = ar9170_alloc(sizeof(*aru));
+ if (IS_ERR(aru)) {
+ err = PTR_ERR(aru);
+ goto out;
+ }
+
+ udev = interface_to_usbdev(intf);
+ usb_get_dev(udev);
+ aru->udev = udev;
+ aru->intf = intf;
+ ar = &aru->common;
+
+ aru->req_one_stage_fw = ar9170_requires_one_stage(id);
+
+ usb_set_intfdata(intf, aru);
+ SET_IEEE80211_DEV(ar->hw, &udev->dev);
+
+ init_usb_anchor(&aru->rx_submitted);
+ init_usb_anchor(&aru->tx_pending);
+ init_usb_anchor(&aru->tx_submitted);
+ init_completion(&aru->cmd_wait);
+ spin_lock_init(&aru->tx_urb_lock);
+
+ aru->tx_pending_urbs = 0;
+ aru->tx_submitted_urbs = 0;
+
+ aru->common.stop = ar9170_usb_stop;
+ aru->common.flush = ar9170_usb_flush;
+ aru->common.open = ar9170_usb_open;
+ aru->common.tx = ar9170_usb_tx;
+ aru->common.exec_cmd = ar9170_usb_exec_cmd;
+ aru->common.callback_cmd = ar9170_usb_callback_cmd;
+
+#ifdef CONFIG_PM
+ udev->reset_resume = 1;
+#endif /* CONFIG_PM */
+ err = ar9170_usb_reset(aru);
+ if (err)
+ goto err_freehw;
+
+ err = ar9170_usb_request_firmware(aru);
+ if (err)
+ goto err_freehw;
+
+ err = ar9170_usb_init_device(aru);
+ if (err)
+ goto err_freefw;
+
+ err = ar9170_usb_open(ar);
+ if (err)
+ goto err_unrx;
+
+ err = ar9170_register(ar, &udev->dev);
+
+ ar9170_usb_stop(ar);
+ if (err)
+ goto err_unrx;
+
+ return 0;
+
+err_unrx:
+ ar9170_usb_cancel_urbs(aru);
+
+err_freefw:
+ release_firmware(aru->init_values);
+ release_firmware(aru->firmware);
+
+err_freehw:
+ usb_set_intfdata(intf, NULL);
+ usb_put_dev(udev);
+ ieee80211_free_hw(ar->hw);
+out:
+ return err;
+}
+
+static void ar9170_usb_disconnect(struct usb_interface *intf)
+{
+ struct ar9170_usb *aru = usb_get_intfdata(intf);
+
+ if (!aru)
+ return;
+
+ aru->common.state = AR9170_IDLE;
+ ar9170_unregister(&aru->common);
+ ar9170_usb_cancel_urbs(aru);
+
+ release_firmware(aru->init_values);
+ release_firmware(aru->firmware);
+
+ usb_put_dev(aru->udev);
+ usb_set_intfdata(intf, NULL);
+ ieee80211_free_hw(aru->common.hw);
+}
+
+#ifdef CONFIG_PM
+static int ar9170_suspend(struct usb_interface *intf,
+ pm_message_t message)
+{
+ struct ar9170_usb *aru = usb_get_intfdata(intf);
+
+ if (!aru)
+ return -ENODEV;
+
+ aru->common.state = AR9170_IDLE;
+ ar9170_usb_cancel_urbs(aru);
+
+ return 0;
+}
+
+static int ar9170_resume(struct usb_interface *intf)
+{
+ struct ar9170_usb *aru = usb_get_intfdata(intf);
+ int err;
+
+ if (!aru)
+ return -ENODEV;
+
+ usb_unpoison_anchored_urbs(&aru->rx_submitted);
+ usb_unpoison_anchored_urbs(&aru->tx_submitted);
+
+ err = ar9170_usb_init_device(aru);
+ if (err)
+ goto err_unrx;
+
+ err = ar9170_usb_open(&aru->common);
+ if (err)
+ goto err_unrx;
+
+ return 0;
+
+err_unrx:
+ aru->common.state = AR9170_IDLE;
+ ar9170_usb_cancel_urbs(aru);
+
+ return err;
+}
+#endif /* CONFIG_PM */
+
+static struct usb_driver ar9170_driver = {
+ .name = "ar9170usb",
+ .probe = ar9170_usb_probe,
+ .disconnect = ar9170_usb_disconnect,
+ .id_table = ar9170_usb_ids,
+ .soft_unbind = 1,
+#ifdef CONFIG_PM
+ .suspend = ar9170_suspend,
+ .resume = ar9170_resume,
+ .reset_resume = ar9170_resume,
+#endif /* CONFIG_PM */
+};
+
+static int __init ar9170_init(void)
+{
+ return usb_register(&ar9170_driver);
+}
+
+static void __exit ar9170_exit(void)
+{
+ usb_deregister(&ar9170_driver);
+}
+
+module_init(ar9170_init);
+module_exit(ar9170_exit);
diff --git a/drivers/net/wireless/ath/ar9170/usb.h b/drivers/net/wireless/ath/ar9170/usb.h
new file mode 100644
index 00000000000..d098f4d5d2f
--- /dev/null
+++ b/drivers/net/wireless/ath/ar9170/usb.h
@@ -0,0 +1,81 @@
+/*
+ * Atheros AR9170 USB driver
+ *
+ * Driver specific definitions
+ *
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2009, Christian Lamparter <chunkeey@web.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, see
+ * http://www.gnu.org/licenses/.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ * Copyright (c) 2007-2008 Atheros Communications, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef __USB_H
+#define __USB_H
+
+#include <linux/usb.h>
+#include <linux/completion.h>
+#include <linux/spinlock.h>
+#include <linux/leds.h>
+#include <net/cfg80211.h>
+#include <net/mac80211.h>
+#include <linux/firmware.h>
+#include "eeprom.h"
+#include "hw.h"
+#include "ar9170.h"
+
+#define AR9170_NUM_RX_URBS 16
+#define AR9170_NUM_TX_URBS 8
+
+struct firmware;
+
+struct ar9170_usb {
+ struct ar9170 common;
+ struct usb_device *udev;
+ struct usb_interface *intf;
+
+ struct usb_anchor rx_submitted;
+ struct usb_anchor tx_pending;
+ struct usb_anchor tx_submitted;
+
+ bool req_one_stage_fw;
+
+ spinlock_t tx_urb_lock;
+ unsigned int tx_submitted_urbs;
+ unsigned int tx_pending_urbs;
+
+ struct completion cmd_wait;
+ int readlen;
+ u8 *readbuf;
+
+ const struct firmware *init_values;
+ const struct firmware *firmware;
+};
+
+#endif /* __USB_H */