aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Lamparter <chunkeey@web.de>2009-03-21 23:05:13 +0100
committerJohn W. Linville <linville@tuxdriver.com>2009-03-27 20:13:08 -0400
commite9348cdd280eb6a1d6d38fef513b578dc9ead363 (patch)
tree174f34bff07187d1d7d2d774d153df8250f63b7d
parent23b53f4f55d833ecc5a11b5fba646c78d3876927 (diff)
ar9170: ar9170: mac80211 interaction code
This patch contains almost all mac80211 interaction code of AR9170. Signed-off-by: Christian Lamparter <chunkeey@web.de> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/net/wireless/ar9170/ar9170.h209
-rw-r--r--drivers/net/wireless/ar9170/cmd.c130
-rw-r--r--drivers/net/wireless/ar9170/cmd.h91
-rw-r--r--drivers/net/wireless/ar9170/led.c171
-rw-r--r--drivers/net/wireless/ar9170/main.c1776
5 files changed, 2377 insertions, 0 deletions
diff --git a/drivers/net/wireless/ar9170/ar9170.h b/drivers/net/wireless/ar9170/ar9170.h
new file mode 100644
index 00000000000..c49d7138d57
--- /dev/null
+++ b/drivers/net/wireless/ar9170/ar9170.h
@@ -0,0 +1,209 @@
+/*
+ * 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/wireless.h>
+#include <net/mac80211.h>
+#ifdef CONFIG_AR9170_LEDS
+#include <linux/leds.h>
+#endif /* CONFIG_AR9170_LEDS */
+#include "eeprom.h"
+#include "hw.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,
+};
+
+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 registered;
+};
+
+#endif /* CONFIG_AR9170_LEDS */
+
+enum ar9170_device_state {
+ AR9170_UNKNOWN_STATE,
+ AR9170_STOPPED,
+ AR9170_IDLE,
+ AR9170_STARTED,
+ AR9170_ASSOCIATED,
+};
+
+struct ar9170 {
+ struct ieee80211_hw *hw;
+ struct mutex mutex;
+ enum ar9170_device_state state;
+
+ int (*open)(struct ar9170 *);
+ void (*stop)(struct ar9170 *);
+ int (*tx)(struct ar9170 *, struct sk_buff *, bool, unsigned int);
+ int (*exec_cmd)(struct ar9170 *, enum ar9170_cmd, u32 ,
+ void *, u32 , void *);
+ void (*callback_cmd)(struct ar9170 *, u32 , void *);
+
+ /* 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 int filter_changed;
+ 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;
+
+ /* global tx status for unregistered Stations. */
+ struct sk_buff_head global_tx_status;
+ struct sk_buff_head global_tx_status_waste;
+ struct delayed_work tx_status_janitor;
+};
+
+struct ar9170_sta_info {
+ struct sk_buff_head tx_status;
+};
+
+#define IS_STARTED(a) (a->state >= AR9170_STARTED)
+#define IS_ACCEPTING_CMD(a) (a->state >= AR9170_IDLE)
+
+#define AR9170_FILTER_CHANGED_PROMISC 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_handle_tx_status(struct ar9170 *ar, struct sk_buff *skb,
+ bool update_statistics, u16 tx_status);
+
+/* 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_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/ar9170/cmd.c b/drivers/net/wireless/ar9170/cmd.c
new file mode 100644
index 00000000000..fd5625c4e9d
--- /dev/null
+++ b/drivers/net/wireless/ar9170/cmd.c
@@ -0,0 +1,130 @@
+/*
+ * 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;
+}
+EXPORT_SYMBOL_GPL(ar9170_echo_test);
diff --git a/drivers/net/wireless/ar9170/cmd.h b/drivers/net/wireless/ar9170/cmd.h
new file mode 100644
index 00000000000..a4f0e50e52b
--- /dev/null
+++ b/drivers/net/wireless/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/ar9170/led.c b/drivers/net/wireless/ar9170/led.c
new file mode 100644
index 00000000000..341cead7f60
--- /dev/null
+++ b/drivers/net/wireless/ar9170/led.c
@@ -0,0 +1,171 @@
+/*
+ * 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].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;
+
+ arl->toggled++;
+
+ if (likely(IS_ACCEPTING_CMD(ar) && brightness))
+ 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;
+
+ cancel_delayed_work_sync(&ar->led_work);
+
+ 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;
+ }
+}
+
+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/ar9170/main.c b/drivers/net/wireless/ar9170/main.c
new file mode 100644
index 00000000000..a3b5323743c
--- /dev/null
+++ b/drivers/net/wireless/ar9170/main.c
@@ -0,0 +1,1776 @@
+/*
+ * 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.
+ */
+
+/*
+ * BIG FAT TODO:
+ *
+ * By the looks of things: these devices share a lot of things like
+ * EEPROM layout/design and PHY code with other Atheros WIFI products.
+ * So this driver/library will eventually become ath9k code... or vice versa ;-)
+ */
+
+#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.");
+MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atheros shared code for AR9170 wireless devices");
+
+#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
+
+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,
+};
+
+#ifdef AR9170_QUEUE_DEBUG
+/*
+ * In case some wants works with AR9170's crazy tx_status queueing techniques.
+ * He might need this rather useful probing function.
+ *
+ * NOTE: caller must hold the queue's spinlock!
+ */
+
+static void ar9170_print_txheader(struct ar9170 *ar, struct sk_buff *skb)
+{
+ struct ar9170_tx_control *txc = (void *) skb->data;
+ struct ieee80211_hdr *hdr = (void *)txc->frame_data;
+
+ printk(KERN_DEBUG "%s: => FRAME [skb:%p, queue:%d, DA:[%pM] "
+ "mac_control:%04x, phy_control:%08x]\n",
+ wiphy_name(ar->hw->wiphy), skb, skb_get_queue_mapping(skb),
+ ieee80211_get_DA(hdr), le16_to_cpu(txc->mac_control),
+ le32_to_cpu(txc->phy_control));
+}
+
+static void ar9170_dump_station_tx_status_queue(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 tx_status queue.\n",
+ wiphy_name(ar->hw->wiphy), skb_queue_len(queue));
+
+ skb_queue_walk(queue, skb) {
+ struct ar9170_tx_control *txc = (void *) skb->data;
+ struct ieee80211_hdr *hdr = (void *)txc->frame_data;
+
+ printk(KERN_DEBUG "index:%d => \n", i);
+ ar9170_print_txheader(ar, skb);
+ }
+ printk(KERN_DEBUG "---[ end ]---\n");
+}
+#endif /* AR9170_QUEUE_DEBUG */
+
+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,
+};
+
+void ar9170_handle_tx_status(struct ar9170 *ar, struct sk_buff *skb,
+ bool valid_status, u16 tx_status)
+{
+ struct ieee80211_tx_info *txinfo;
+ unsigned int retries = 0, queue = skb_get_queue_mapping(skb);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ar->tx_stats_lock, flags);
+ ar->tx_stats[queue].len--;
+ if (ieee80211_queue_stopped(ar->hw, queue))
+ ieee80211_wake_queue(ar->hw, queue);
+ spin_unlock_irqrestore(&ar->tx_stats_lock, flags);
+
+ 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;
+ }
+
+ if (valid_status)
+ txinfo->status.rates[0].count = retries + 1;
+
+ skb_pull(skb, sizeof(struct ar9170_tx_control));
+ ieee80211_tx_status_irqsafe(ar->hw, skb);
+}
+EXPORT_SYMBOL_GPL(ar9170_handle_tx_status);
+
+static struct sk_buff *ar9170_find_skb_in_queue(struct ar9170 *ar,
+ const u8 *mac,
+ const u32 queue,
+ struct sk_buff_head *q)
+{
+ unsigned long flags;
+ struct sk_buff *skb;
+
+ spin_lock_irqsave(&q->lock, flags);
+ skb_queue_walk(q, skb) {
+ struct ar9170_tx_control *txc = (void *) skb->data;
+ struct ieee80211_hdr *hdr = (void *) txc->frame_data;
+ u32 txc_queue = (le32_to_cpu(txc->phy_control) &
+ AR9170_TX_PHY_QOS_MASK) >>
+ AR9170_TX_PHY_QOS_SHIFT;
+
+ if ((queue != txc_queue) ||
+ (compare_ether_addr(ieee80211_get_DA(hdr), mac)))
+ continue;
+
+ __skb_unlink(skb, q);
+ spin_unlock_irqrestore(&q->lock, flags);
+ return skb;
+ }
+ spin_unlock_irqrestore(&q->lock, flags);
+ return NULL;
+}
+
+static struct sk_buff *ar9170_find_skb_in_queue_by_mac(struct ar9170 *ar,
+ const u8 *mac,
+ struct sk_buff_head *q)
+{
+ unsigned long flags;
+ struct sk_buff *skb;
+
+ spin_lock_irqsave(&q->lock, flags);
+ skb_queue_walk(q, skb) {
+ struct ar9170_tx_control *txc = (void *) skb->data;
+ struct ieee80211_hdr *hdr = (void *) txc->frame_data;
+
+ if (compare_ether_addr(ieee80211_get_DA(hdr), mac))
+ continue;
+
+ __skb_unlink(skb, q);
+ spin_unlock_irqrestore(&q->lock, flags);
+ return skb;
+ }
+ spin_unlock_irqrestore(&q->lock, flags);
+ return NULL;
+}
+
+static struct sk_buff *ar9170_find_skb_in_queue_by_txcq(struct ar9170 *ar,
+ const u32 queue,
+ struct sk_buff_head *q)
+{
+ unsigned long flags;
+ struct sk_buff *skb;
+
+ spin_lock_irqsave(&q->lock, flags);
+ skb_queue_walk(q, skb) {
+ struct ar9170_tx_control *txc = (void *) skb->data;
+ u32 txc_queue = (le32_to_cpu(txc->phy_control) &
+ AR9170_TX_PHY_QOS_MASK) >>
+ AR9170_TX_PHY_QOS_SHIFT;
+
+ if (queue != txc_queue)
+ continue;
+
+ __skb_unlink(skb, q);
+ spin_unlock_irqrestore(&q->lock, flags);
+ return skb;
+ }
+ spin_unlock_irqrestore(&q->lock, flags);
+ return NULL;
+}
+
+static struct sk_buff *ar9170_find_queued_skb(struct ar9170 *ar, const u8 *mac,
+ const u32 queue)
+{
+ struct ieee80211_sta *sta;
+ 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...
+ */
+ rcu_read_lock();
+ sta = ieee80211_find_sta(ar->hw, mac);
+
+ if (likely(sta)) {
+ struct ar9170_sta_info *sta_priv = (void *) sta->drv_priv;
+ skb = ar9170_find_skb_in_queue_by_txcq(ar, queue,
+ &sta_priv->tx_status);
+ } else {
+ /* STA is not in database (yet/anymore). */
+
+ /* scan the waste bin for likely candidates */
+ skb = ar9170_find_skb_in_queue(ar, mac, queue,
+ &ar->global_tx_status_waste);
+ if (!skb) {
+ /* so it still _must_ be in the global list. */
+ skb = ar9170_find_skb_in_queue(ar, mac, queue,
+ &ar->global_tx_status);
+ }
+ }
+ rcu_read_unlock();
+
+#ifdef AR9170_QUEUE_DEBUG
+ if (unlikely((!skb) && net_ratelimit())) {
+ printk(KERN_ERR "%s: ESS:[%pM] does not have any "
+ "outstanding frames in this queue (%d).\n",
+ wiphy_name(ar->hw->wiphy), mac, queue);
+ }
+#endif /* AR9170_QUEUE_DEBUG */
+
+ return skb;
+}
+
+/*
+ * This worker tries to keep the global tx_status queue empty.
+ * So we can guarantee that incoming tx_status reports for
+ * unregistered stations are always synced with the actual
+ * frame - which we think - belongs to.
+ */
+
+static void ar9170_tx_status_janitor(struct work_struct *work)
+{
+ struct ar9170 *ar = container_of(work, struct ar9170,
+ filter_config_work);
+ struct sk_buff *skb;
+
+ /* recycle the garbage back to mac80211... one by one. */
+ while ((skb = skb_dequeue(&ar->global_tx_status_waste))) {
+#ifdef AR9170_QUEUE_DEBUG
+ printk(KERN_DEBUG "%s: dispose queued frame =>\n",
+ wiphy_name(ar->hw->wiphy));
+ ar9170_print_txheader(ar, skb);
+#endif /* AR9170_QUEUE_DEBUG */
+ ar9170_handle_tx_status(ar, skb, false,
+ AR9170_TX_STATUS_FAILED);
+ }
+
+
+
+ while ((skb = skb_dequeue(&ar->global_tx_status))) {
+#ifdef AR9170_QUEUE_DEBUG
+ printk(KERN_DEBUG "%s: moving frame into waste queue =>\n",
+ wiphy_name(ar->hw->wiphy));
+
+ ar9170_print_txheader(ar, skb);
+#endif /* AR9170_QUEUE_DEBUG */
+ skb_queue_tail(&ar->global_tx_status_waste, skb);
+ }
+
+ /* recall the janitor in 100ms - if there's garbage in the can. */
+ if (skb_queue_len(&ar->global_tx_status_waste) > 0)
+ queue_delayed_work(ar->hw->workqueue, &ar->tx_status_janitor,
+ msecs_to_jiffies(100));
+}
+
+static 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 queue = (le32_to_cpu(cmd->tx_status.rate) &
+ AR9170_TX_PHY_QOS_MASK) >> AR9170_TX_PHY_QOS_SHIFT;
+
+ skb = ar9170_find_queued_skb(ar, cmd->tx_status.dst, queue);
+ if (unlikely(!skb))
+ return ;
+
+ ar9170_handle_tx_status(ar, skb, true,
+ 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