Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi...
authorJohn W. Linville <linville@tuxdriver.com>
Mon, 11 Jun 2012 18:50:59 +0000 (14:50 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 11 Jun 2012 18:50:59 +0000 (14:50 -0400)
Conflicts:
drivers/net/wireless/iwlwifi/iwl-eeprom.c

143 files changed:
MAINTAINERS
drivers/bcma/driver_chipcommon_pmu.c
drivers/bcma/sprom.c
drivers/net/wireless/ath/ath9k/Makefile
drivers/net/wireless/ath/ath9k/ahb.c
drivers/net/wireless/ath/ath9k/antenna.c [new file with mode: 0644]
drivers/net/wireless/ath/ath9k/ar9003_calib.c
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
drivers/net/wireless/ath/ath9k/ar9003_mci.c
drivers/net/wireless/ath/ath9k/ar9003_mci.h
drivers/net/wireless/ath/ath9k/ar9003_phy.c
drivers/net/wireless/ath/ath9k/ar9003_phy.h
drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/beacon.c
drivers/net/wireless/ath/ath9k/btcoex.c
drivers/net/wireless/ath/ath9k/btcoex.h
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/debug.h
drivers/net/wireless/ath/ath9k/gpio.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/link.c [new file with mode: 0644]
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/mci.c
drivers/net/wireless/ath/ath9k/mci.h
drivers/net/wireless/ath/ath9k/pci.c
drivers/net/wireless/ath/ath9k/recv.c
drivers/net/wireless/ath/ath9k/reg.h
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/b43legacy/main.c
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
drivers/net/wireless/brcm80211/brcmsmac/aiutils.c
drivers/net/wireless/brcm80211/brcmsmac/aiutils.h
drivers/net/wireless/iwlegacy/4965-mac.c
drivers/net/wireless/iwlwifi/dvm/mac80211.c
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/libertas/cfg.c
drivers/net/wireless/libertas/dev.h
drivers/net/wireless/libertas/mesh.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/orinoco/cfg.c
drivers/net/wireless/rt2x00/rt2800.h
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2x00.h
drivers/net/wireless/rt2x00/rt2x00dev.c
drivers/net/wireless/rt2x00/rt2x00mac.c
drivers/net/wireless/rt2x00/rt2x00queue.c
drivers/net/wireless/ti/Kconfig
drivers/net/wireless/ti/Makefile
drivers/net/wireless/ti/wl12xx/Makefile
drivers/net/wireless/ti/wl12xx/acx.h
drivers/net/wireless/ti/wl12xx/cmd.c
drivers/net/wireless/ti/wl12xx/debugfs.c [new file with mode: 0644]
drivers/net/wireless/ti/wl12xx/debugfs.h [new file with mode: 0644]
drivers/net/wireless/ti/wl12xx/main.c
drivers/net/wireless/ti/wl12xx/wl12xx.h
drivers/net/wireless/ti/wl18xx/Kconfig [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/Makefile [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/acx.c [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/acx.h [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/conf.h [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/debugfs.c [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/debugfs.h [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/io.c [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/io.h [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/main.c [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/reg.h [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/tx.c [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/tx.h [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/wl18xx.h [new file with mode: 0644]
drivers/net/wireless/ti/wlcore/acx.c
drivers/net/wireless/ti/wlcore/acx.h
drivers/net/wireless/ti/wlcore/boot.c
drivers/net/wireless/ti/wlcore/boot.h
drivers/net/wireless/ti/wlcore/cmd.c
drivers/net/wireless/ti/wlcore/cmd.h
drivers/net/wireless/ti/wlcore/conf.h
drivers/net/wireless/ti/wlcore/debugfs.c
drivers/net/wireless/ti/wlcore/debugfs.h
drivers/net/wireless/ti/wlcore/event.c
drivers/net/wireless/ti/wlcore/hw_ops.h
drivers/net/wireless/ti/wlcore/init.c
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/ps.c
drivers/net/wireless/ti/wlcore/rx.c
drivers/net/wireless/ti/wlcore/rx.h
drivers/net/wireless/ti/wlcore/scan.c
drivers/net/wireless/ti/wlcore/scan.h
drivers/net/wireless/ti/wlcore/sdio.c
drivers/net/wireless/ti/wlcore/tx.c
drivers/net/wireless/ti/wlcore/tx.h
drivers/net/wireless/ti/wlcore/wlcore.h
drivers/net/wireless/ti/wlcore/wlcore_i.h [moved from drivers/net/wireless/ti/wlcore/wl12xx.h with 95% similarity]
drivers/nfc/pn533.c
drivers/nfc/pn544_hci.c
drivers/ssb/scan.c
include/linux/nfc.h
include/linux/nl80211.h
include/linux/ssb/ssb.h
include/net/cfg80211.h
include/net/mac80211.h
include/net/nfc/hci.h
include/net/nfc/nfc.h
include/net/nfc/shdlc.h
net/bluetooth/af_bluetooth.c
net/mac80211/Makefile
net/mac80211/agg-rx.c
net/mac80211/cfg.c
net/mac80211/debugfs_netdev.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/main.c
net/mac80211/mlme.c
net/mac80211/offchannel.c
net/mac80211/pm.c
net/mac80211/scan.c
net/mac80211/sta_info.c
net/mac80211/status.c
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/work.c [deleted file]
net/nfc/core.c
net/nfc/hci/core.c
net/nfc/hci/shdlc.c
net/nfc/llcp/commands.c
net/nfc/llcp/llcp.c
net/nfc/llcp/llcp.h
net/nfc/llcp/sock.c
net/nfc/nci/core.c
net/nfc/netlink.c
net/nfc/nfc.h
net/rfkill/core.c
net/wireless/chan.c
net/wireless/core.h
net/wireless/ibss.c
net/wireless/mesh.c
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/util.c
net/wireless/wext-compat.c
net/wireless/wext-sme.c

index 55f0fda..c5fd905 100644 (file)
@@ -329,7 +329,7 @@ F:  drivers/hwmon/adm1029.c
 
 ADM8211 WIRELESS DRIVER
 L:     linux-wireless@vger.kernel.org
-W:     http://linuxwireless.org/
+W:     http://wireless.kernel.org/
 S:     Orphan
 F:     drivers/net/wireless/adm8211.*
 
@@ -1423,7 +1423,7 @@ B43 WIRELESS DRIVER
 M:     Stefano Brivio <stefano.brivio@polimi.it>
 L:     linux-wireless@vger.kernel.org
 L:     b43-dev@lists.infradead.org
-W:     http://linuxwireless.org/en/users/Drivers/b43
+W:     http://wireless.kernel.org/en/users/Drivers/b43
 S:     Maintained
 F:     drivers/net/wireless/b43/
 
@@ -1432,7 +1432,7 @@ M:        Larry Finger <Larry.Finger@lwfinger.net>
 M:     Stefano Brivio <stefano.brivio@polimi.it>
 L:     linux-wireless@vger.kernel.org
 L:     b43-dev@lists.infradead.org
-W:     http://linuxwireless.org/en/users/Drivers/b43
+W:     http://wireless.kernel.org/en/users/Drivers/b43
 S:     Maintained
 F:     drivers/net/wireless/b43legacy/
 
@@ -4339,7 +4339,7 @@ F:        arch/m68k/hp300/
 MAC80211
 M:     Johannes Berg <johannes@sipsolutions.net>
 L:     linux-wireless@vger.kernel.org
-W:     http://linuxwireless.org/
+W:     http://wireless.kernel.org/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless.git
 S:     Maintained
 F:     Documentation/networking/mac80211-injection.txt
@@ -4350,7 +4350,7 @@ MAC80211 PID RATE CONTROL
 M:     Stefano Brivio <stefano.brivio@polimi.it>
 M:     Mattias Nissler <mattias.nissler@gmx.de>
 L:     linux-wireless@vger.kernel.org
-W:     http://linuxwireless.org/en/developers/Documentation/mac80211/RateControl/PID
+W:     http://wireless.kernel.org/en/developers/Documentation/mac80211/RateControl/PID
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless.git
 S:     Maintained
 F:     net/mac80211/rc80211_pid*
@@ -5027,7 +5027,7 @@ F:        fs/ocfs2/
 
 ORINOCO DRIVER
 L:     linux-wireless@vger.kernel.org
-W:     http://linuxwireless.org/en/users/Drivers/orinoco
+W:     http://wireless.kernel.org/en/users/Drivers/orinoco
 W:     http://www.nongnu.org/orinoco/
 S:     Orphan
 F:     drivers/net/wireless/orinoco/
@@ -5729,7 +5729,7 @@ F:        net/rose/
 RTL8180 WIRELESS DRIVER
 M:     "John W. Linville" <linville@tuxdriver.com>
 L:     linux-wireless@vger.kernel.org
-W:     http://linuxwireless.org/
+W:     http://wireless.kernel.org/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
 S:     Maintained
 F:     drivers/net/wireless/rtl818x/rtl8180/
@@ -5739,7 +5739,7 @@ M:        Herton Ronaldo Krzesinski <herton@canonical.com>
 M:     Hin-Tak Leung <htl10@users.sourceforge.net>
 M:     Larry Finger <Larry.Finger@lwfinger.net>
 L:     linux-wireless@vger.kernel.org
-W:     http://linuxwireless.org/
+W:     http://wireless.kernel.org/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
 S:     Maintained
 F:     drivers/net/wireless/rtl818x/rtl8187/
@@ -5748,7 +5748,7 @@ RTL8192CE WIRELESS DRIVER
 M:     Larry Finger <Larry.Finger@lwfinger.net>
 M:     Chaoming Li <chaoming_li@realsil.com.cn>
 L:     linux-wireless@vger.kernel.org
-W:     http://linuxwireless.org/
+W:     http://wireless.kernel.org/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
 S:     Maintained
 F:     drivers/net/wireless/rtlwifi/
index a058842..61ce405 100644 (file)
@@ -139,7 +139,9 @@ void bcma_pmu_workarounds(struct bcma_drv_cc *cc)
                bcma_chipco_chipctl_maskset(cc, 0, ~0, 0x7);
                break;
        case 0x4331:
-               /* BCM4331 workaround is SPROM-related, we put it in sprom.c */
+       case 43431:
+               /* Ext PA lines must be enabled for tx on BCM4331 */
+               bcma_chipco_bcm4331_ext_pa_lines_ctl(cc, true);
                break;
        case 43224:
                if (bus->chipinfo.rev == 0) {
index c7f9335..f16f42d 100644 (file)
@@ -579,13 +579,13 @@ int bcma_sprom_get(struct bcma_bus *bus)
        if (!sprom)
                return -ENOMEM;
 
-       if (bus->chipinfo.id == 0x4331)
+       if (bus->chipinfo.id == 0x4331 || bus->chipinfo.id == 43431)
                bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false);
 
        pr_debug("SPROM offset 0x%x\n", offset);
        bcma_sprom_read(bus, offset, sprom);
 
-       if (bus->chipinfo.id == 0x4331)
+       if (bus->chipinfo.id == 0x4331 || bus->chipinfo.id == 43431)
                bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true);
 
        err = bcma_sprom_valid(sprom);
index 3f0b847..9c41232 100644 (file)
@@ -3,7 +3,9 @@ ath9k-y +=      beacon.o \
                init.o \
                main.o \
                recv.o \
-               xmit.o
+               xmit.o \
+               link.o \
+               antenna.o
 
 ath9k-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += mci.o
 ath9k-$(CONFIG_ATH9K_RATE_CONTROL) += rc.o
index 5e47ca6..4a4e8a2 100644 (file)
@@ -126,7 +126,7 @@ static int ath_ahb_probe(struct platform_device *pdev)
        sc->irq = irq;
 
        /* Will be cleared in ath9k_start() */
-       sc->sc_flags |= SC_OP_INVALID;
+       set_bit(SC_OP_INVALID, &sc->sc_flags);
 
        ret = request_irq(irq, ath_isr, IRQF_SHARED, "ath9k", sc);
        if (ret) {
diff --git a/drivers/net/wireless/ath/ath9k/antenna.c b/drivers/net/wireless/ath/ath9k/antenna.c
new file mode 100644 (file)
index 0000000..bbcfeb3
--- /dev/null
@@ -0,0 +1,776 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, 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 "ath9k.h"
+
+static inline bool ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta,
+                                              int mindelta, int main_rssi_avg,
+                                              int alt_rssi_avg, int pkt_count)
+{
+       return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
+                (alt_rssi_avg > main_rssi_avg + maxdelta)) ||
+               (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
+}
+
+static inline bool ath_ant_div_comb_alt_check(u8 div_group, int alt_ratio,
+                                             int curr_main_set, int curr_alt_set,
+                                             int alt_rssi_avg, int main_rssi_avg)
+{
+       bool result = false;
+       switch (div_group) {
+       case 0:
+               if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
+                       result = true;
+               break;
+       case 1:
+       case 2:
+               if ((((curr_main_set == ATH_ANT_DIV_COMB_LNA2) &&
+                     (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) &&
+                     (alt_rssi_avg >= (main_rssi_avg - 5))) ||
+                    ((curr_main_set == ATH_ANT_DIV_COMB_LNA1) &&
+                     (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) &&
+                     (alt_rssi_avg >= (main_rssi_avg - 2)))) &&
+                   (alt_rssi_avg >= 4))
+                       result = true;
+               else
+                       result = false;
+               break;
+       }
+
+       return result;
+}
+
+static void ath_lnaconf_alt_good_scan(struct ath_ant_comb *antcomb,
+                                     struct ath_hw_antcomb_conf ant_conf,
+                                     int main_rssi_avg)
+{
+       antcomb->quick_scan_cnt = 0;
+
+       if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
+               antcomb->rssi_lna2 = main_rssi_avg;
+       else if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA1)
+               antcomb->rssi_lna1 = main_rssi_avg;
+
+       switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) {
+       case 0x10: /* LNA2 A-B */
+               antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+               antcomb->first_quick_scan_conf =
+                       ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+               antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
+               break;
+       case 0x20: /* LNA1 A-B */
+               antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+               antcomb->first_quick_scan_conf =
+                       ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+               antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
+               break;
+       case 0x21: /* LNA1 LNA2 */
+               antcomb->main_conf = ATH_ANT_DIV_COMB_LNA2;
+               antcomb->first_quick_scan_conf =
+                       ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+               antcomb->second_quick_scan_conf =
+                       ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+               break;
+       case 0x12: /* LNA2 LNA1 */
+               antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1;
+               antcomb->first_quick_scan_conf =
+                       ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+               antcomb->second_quick_scan_conf =
+                       ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+               break;
+       case 0x13: /* LNA2 A+B */
+               antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+               antcomb->first_quick_scan_conf =
+                       ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+               antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
+               break;
+       case 0x23: /* LNA1 A+B */
+               antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+               antcomb->first_quick_scan_conf =
+                       ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+               antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
+               break;
+       default:
+               break;
+       }
+}
+
+static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
+                                      struct ath_hw_antcomb_conf *div_ant_conf,
+                                      int main_rssi_avg, int alt_rssi_avg,
+                                      int alt_ratio)
+{
+       /* alt_good */
+       switch (antcomb->quick_scan_cnt) {
+       case 0:
+               /* set alt to main, and alt to first conf */
+               div_ant_conf->main_lna_conf = antcomb->main_conf;
+               div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
+               break;
+       case 1:
+               /* set alt to main, and alt to first conf */
+               div_ant_conf->main_lna_conf = antcomb->main_conf;
+               div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
+               antcomb->rssi_first = main_rssi_avg;
+               antcomb->rssi_second = alt_rssi_avg;
+
+               if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
+                       /* main is LNA1 */
+                       if (ath_is_alt_ant_ratio_better(alt_ratio,
+                                               ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
+                                               ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
+                                               main_rssi_avg, alt_rssi_avg,
+                                               antcomb->total_pkt_count))
+                               antcomb->first_ratio = true;
+                       else
+                               antcomb->first_ratio = false;
+               } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
+                       if (ath_is_alt_ant_ratio_better(alt_ratio,
+                                               ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
+                                               ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
+                                               main_rssi_avg, alt_rssi_avg,
+                                               antcomb->total_pkt_count))
+                               antcomb->first_ratio = true;
+                       else
+                               antcomb->first_ratio = false;
+               } else {
+                       if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
+                             (alt_rssi_avg > main_rssi_avg +
+                              ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
+                            (alt_rssi_avg > main_rssi_avg)) &&
+                           (antcomb->total_pkt_count > 50))
+                               antcomb->first_ratio = true;
+                       else
+                               antcomb->first_ratio = false;
+               }
+               break;
+       case 2:
+               antcomb->alt_good = false;
+               antcomb->scan_not_start = false;
+               antcomb->scan = false;
+               antcomb->rssi_first = main_rssi_avg;
+               antcomb->rssi_third = alt_rssi_avg;
+
+               if (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1)
+                       antcomb->rssi_lna1 = alt_rssi_avg;
+               else if (antcomb->second_quick_scan_conf ==
+                        ATH_ANT_DIV_COMB_LNA2)
+                       antcomb->rssi_lna2 = alt_rssi_avg;
+               else if (antcomb->second_quick_scan_conf ==
+                        ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
+                       if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)
+                               antcomb->rssi_lna2 = main_rssi_avg;
+                       else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1)
+                               antcomb->rssi_lna1 = main_rssi_avg;
+               }
+
+               if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
+                   ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
+                       div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+               else
+                       div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+
+               if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
+                       if (ath_is_alt_ant_ratio_better(alt_ratio,
+                                               ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
+                                               ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
+                                               main_rssi_avg, alt_rssi_avg,
+                                               antcomb->total_pkt_count))
+                               antcomb->second_ratio = true;
+                       else
+                               antcomb->second_ratio = false;
+               } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
+                       if (ath_is_alt_ant_ratio_better(alt_ratio,
+                                               ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
+                                               ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
+                                               main_rssi_avg, alt_rssi_avg,
+                                               antcomb->total_pkt_count))
+                               antcomb->second_ratio = true;
+                       else
+                               antcomb->second_ratio = false;
+               } else {
+                       if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
+                             (alt_rssi_avg > main_rssi_avg +
+                              ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
+                            (alt_rssi_avg > main_rssi_avg)) &&
+                           (antcomb->total_pkt_count > 50))
+                               antcomb->second_ratio = true;
+                       else
+                               antcomb->second_ratio = false;
+               }
+
+               /* set alt to the conf with maximun ratio */
+               if (antcomb->first_ratio && antcomb->second_ratio) {
+                       if (antcomb->rssi_second > antcomb->rssi_third) {
+                               /* first alt*/
+                               if ((antcomb->first_quick_scan_conf ==
+                                   ATH_ANT_DIV_COMB_LNA1) ||
+                                   (antcomb->first_quick_scan_conf ==
+                                   ATH_ANT_DIV_COMB_LNA2))
+                                       /* Set alt LNA1 or LNA2*/
+                                       if (div_ant_conf->main_lna_conf ==
+                                           ATH_ANT_DIV_COMB_LNA2)
+                                               div_ant_conf->alt_lna_conf =
+                                                       ATH_ANT_DIV_COMB_LNA1;
+                                       else
+                                               div_ant_conf->alt_lna_conf =
+                                                       ATH_ANT_DIV_COMB_LNA2;
+                               else
+                                       /* Set alt to A+B or A-B */
+                                       div_ant_conf->alt_lna_conf =
+                                               antcomb->first_quick_scan_conf;
+                       } else if ((antcomb->second_quick_scan_conf ==
+                                  ATH_ANT_DIV_COMB_LNA1) ||
+                                  (antcomb->second_quick_scan_conf ==
+                                  ATH_ANT_DIV_COMB_LNA2)) {
+                               /* Set alt LNA1 or LNA2 */
+                               if (div_ant_conf->main_lna_conf ==
+                                   ATH_ANT_DIV_COMB_LNA2)
+                                       div_ant_conf->alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                               else
+                                       div_ant_conf->alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA2;
+                       } else {
+                               /* Set alt to A+B or A-B */
+                               div_ant_conf->alt_lna_conf =
+                                       antcomb->second_quick_scan_conf;
+                       }
+               } else if (antcomb->first_ratio) {
+                       /* first alt */
+                       if ((antcomb->first_quick_scan_conf ==
+                           ATH_ANT_DIV_COMB_LNA1) ||
+                           (antcomb->first_quick_scan_conf ==
+                           ATH_ANT_DIV_COMB_LNA2))
+                                       /* Set alt LNA1 or LNA2 */
+                               if (div_ant_conf->main_lna_conf ==
+                                   ATH_ANT_DIV_COMB_LNA2)
+                                       div_ant_conf->alt_lna_conf =
+                                                       ATH_ANT_DIV_COMB_LNA1;
+                               else
+                                       div_ant_conf->alt_lna_conf =
+                                                       ATH_ANT_DIV_COMB_LNA2;
+                       else
+                               /* Set alt to A+B or A-B */
+                               div_ant_conf->alt_lna_conf =
+                                               antcomb->first_quick_scan_conf;
+               } else if (antcomb->second_ratio) {
+                               /* second alt */
+                       if ((antcomb->second_quick_scan_conf ==
+                           ATH_ANT_DIV_COMB_LNA1) ||
+                           (antcomb->second_quick_scan_conf ==
+                           ATH_ANT_DIV_COMB_LNA2))
+                               /* Set alt LNA1 or LNA2 */
+                               if (div_ant_conf->main_lna_conf ==
+                                   ATH_ANT_DIV_COMB_LNA2)
+                                       div_ant_conf->alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                               else
+                                       div_ant_conf->alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA2;
+                       else
+                               /* Set alt to A+B or A-B */
+                               div_ant_conf->alt_lna_conf =
+                                               antcomb->second_quick_scan_conf;
+               } else {
+                       /* main is largest */
+                       if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) ||
+                           (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2))
+                               /* Set alt LNA1 or LNA2 */
+                               if (div_ant_conf->main_lna_conf ==
+                                   ATH_ANT_DIV_COMB_LNA2)
+                                       div_ant_conf->alt_lna_conf =
+                                                       ATH_ANT_DIV_COMB_LNA1;
+                               else
+                                       div_ant_conf->alt_lna_conf =
+                                                       ATH_ANT_DIV_COMB_LNA2;
+                       else
+                               /* Set alt to A+B or A-B */
+                               div_ant_conf->alt_lna_conf = antcomb->main_conf;
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
+                                         struct ath_ant_comb *antcomb,
+                                         int alt_ratio)
+{
+       if (ant_conf->div_group == 0) {
+               /* Adjust the fast_div_bias based on main and alt lna conf */
+               switch ((ant_conf->main_lna_conf << 4) |
+                               ant_conf->alt_lna_conf) {
+               case 0x01: /* A-B LNA2 */
+                       ant_conf->fast_div_bias = 0x3b;
+                       break;
+               case 0x02: /* A-B LNA1 */
+                       ant_conf->fast_div_bias = 0x3d;
+                       break;
+               case 0x03: /* A-B A+B */
+                       ant_conf->fast_div_bias = 0x1;
+                       break;
+               case 0x10: /* LNA2 A-B */
+                       ant_conf->fast_div_bias = 0x7;
+                       break;
+               case 0x12: /* LNA2 LNA1 */
+                       ant_conf->fast_div_bias = 0x2;
+                       break;
+               case 0x13: /* LNA2 A+B */
+                       ant_conf->fast_div_bias = 0x7;
+                       break;
+               case 0x20: /* LNA1 A-B */
+                       ant_conf->fast_div_bias = 0x6;
+                       break;
+               case 0x21: /* LNA1 LNA2 */
+                       ant_conf->fast_div_bias = 0x0;
+                       break;
+               case 0x23: /* LNA1 A+B */
+                       ant_conf->fast_div_bias = 0x6;
+                       break;
+               case 0x30: /* A+B A-B */
+                       ant_conf->fast_div_bias = 0x1;
+                       break;
+               case 0x31: /* A+B LNA2 */
+                       ant_conf->fast_div_bias = 0x3b;
+                       break;
+               case 0x32: /* A+B LNA1 */
+                       ant_conf->fast_div_bias = 0x3d;
+                       break;
+               default:
+                       break;
+               }
+       } else if (ant_conf->div_group == 1) {
+               /* Adjust the fast_div_bias based on main and alt_lna_conf */
+               switch ((ant_conf->main_lna_conf << 4) |
+                       ant_conf->alt_lna_conf) {
+               case 0x01: /* A-B LNA2 */
+                       ant_conf->fast_div_bias = 0x1;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x02: /* A-B LNA1 */
+                       ant_conf->fast_div_bias = 0x1;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x03: /* A-B A+B */
+                       ant_conf->fast_div_bias = 0x1;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x10: /* LNA2 A-B */
+                       if (!(antcomb->scan) &&
+                           (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+                               ant_conf->fast_div_bias = 0x3f;
+                       else
+                               ant_conf->fast_div_bias = 0x1;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x12: /* LNA2 LNA1 */
+                       ant_conf->fast_div_bias = 0x1;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x13: /* LNA2 A+B */
+                       if (!(antcomb->scan) &&
+                           (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+                               ant_conf->fast_div_bias = 0x3f;
+                       else
+                               ant_conf->fast_div_bias = 0x1;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x20: /* LNA1 A-B */
+                       if (!(antcomb->scan) &&
+                           (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+                               ant_conf->fast_div_bias = 0x3f;
+                       else
+                               ant_conf->fast_div_bias = 0x1;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x21: /* LNA1 LNA2 */
+                       ant_conf->fast_div_bias = 0x1;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x23: /* LNA1 A+B */
+                       if (!(antcomb->scan) &&
+                           (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+                               ant_conf->fast_div_bias = 0x3f;
+                       else
+                               ant_conf->fast_div_bias = 0x1;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x30: /* A+B A-B */
+                       ant_conf->fast_div_bias = 0x1;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x31: /* A+B LNA2 */
+                       ant_conf->fast_div_bias = 0x1;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x32: /* A+B LNA1 */
+                       ant_conf->fast_div_bias = 0x1;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               default:
+                       break;
+               }
+       } else if (ant_conf->div_group == 2) {
+               /* Adjust the fast_div_bias based on main and alt_lna_conf */
+               switch ((ant_conf->main_lna_conf << 4) |
+                               ant_conf->alt_lna_conf) {
+               case 0x01: /* A-B LNA2 */
+                       ant_conf->fast_div_bias = 0x1;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x02: /* A-B LNA1 */
+                       ant_conf->fast_div_bias = 0x1;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x03: /* A-B A+B */
+                       ant_conf->fast_div_bias = 0x1;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x10: /* LNA2 A-B */
+                       if (!(antcomb->scan) &&
+                               (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+                               ant_conf->fast_div_bias = 0x1;
+                       else
+                               ant_conf->fast_div_bias = 0x2;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x12: /* LNA2 LNA1 */
+                       ant_conf->fast_div_bias = 0x1;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x13: /* LNA2 A+B */
+                       if (!(antcomb->scan) &&
+                               (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+                               ant_conf->fast_div_bias = 0x1;
+                       else
+                               ant_conf->fast_div_bias = 0x2;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x20: /* LNA1 A-B */
+                       if (!(antcomb->scan) &&
+                               (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+                               ant_conf->fast_div_bias = 0x1;
+                       else
+                               ant_conf->fast_div_bias = 0x2;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x21: /* LNA1 LNA2 */
+                       ant_conf->fast_div_bias = 0x1;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x23: /* LNA1 A+B */
+                       if (!(antcomb->scan) &&
+                               (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+                               ant_conf->fast_div_bias = 0x1;
+                       else
+                               ant_conf->fast_div_bias = 0x2;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x30: /* A+B A-B */
+                       ant_conf->fast_div_bias = 0x1;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x31: /* A+B LNA2 */
+                       ant_conf->fast_div_bias = 0x1;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               case 0x32: /* A+B LNA1 */
+                       ant_conf->fast_div_bias = 0x1;
+                       ant_conf->main_gaintb = 0;
+                       ant_conf->alt_gaintb = 0;
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
+{
+       struct ath_hw_antcomb_conf div_ant_conf;
+       struct ath_ant_comb *antcomb = &sc->ant_comb;
+       int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
+       int curr_main_set;
+       int main_rssi = rs->rs_rssi_ctl0;
+       int alt_rssi = rs->rs_rssi_ctl1;
+       int rx_ant_conf,  main_ant_conf;
+       bool short_scan = false;
+
+       rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) &
+                      ATH_ANT_RX_MASK;
+       main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) &
+                        ATH_ANT_RX_MASK;
+
+       /* Record packet only when both main_rssi and  alt_rssi is positive */
+       if (main_rssi > 0 && alt_rssi > 0) {
+               antcomb->total_pkt_count++;
+               antcomb->main_total_rssi += main_rssi;
+               antcomb->alt_total_rssi  += alt_rssi;
+               if (main_ant_conf == rx_ant_conf)
+                       antcomb->main_recv_cnt++;
+               else
+                       antcomb->alt_recv_cnt++;
+       }
+
+       /* Short scan check */
+       if (antcomb->scan && antcomb->alt_good) {
+               if (time_after(jiffies, antcomb->scan_start_time +
+                   msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
+                       short_scan = true;
+               else
+                       if (antcomb->total_pkt_count ==
+                           ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
+                               alt_ratio = ((antcomb->alt_recv_cnt * 100) /
+                                           antcomb->total_pkt_count);
+                               if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
+                                       short_scan = true;
+                       }
+       }
+
+       if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
+           rs->rs_moreaggr) && !short_scan)
+               return;
+
+       if (antcomb->total_pkt_count) {
+               alt_ratio = ((antcomb->alt_recv_cnt * 100) /
+                            antcomb->total_pkt_count);
+               main_rssi_avg = (antcomb->main_total_rssi /
+                                antcomb->total_pkt_count);
+               alt_rssi_avg = (antcomb->alt_total_rssi /
+                                antcomb->total_pkt_count);
+       }
+
+
+       ath9k_hw_antdiv_comb_conf_get(sc->sc_ah, &div_ant_conf);
+       curr_alt_set = div_ant_conf.alt_lna_conf;
+       curr_main_set = div_ant_conf.main_lna_conf;
+
+       antcomb->count++;
+
+       if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
+               if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
+                       ath_lnaconf_alt_good_scan(antcomb, div_ant_conf,
+                                                 main_rssi_avg);
+                       antcomb->alt_good = true;
+               } else {
+                       antcomb->alt_good = false;
+               }
+
+               antcomb->count = 0;
+               antcomb->scan = true;
+               antcomb->scan_not_start = true;
+       }
+
+       if (!antcomb->scan) {
+               if (ath_ant_div_comb_alt_check(div_ant_conf.div_group,
+                                       alt_ratio, curr_main_set, curr_alt_set,
+                                       alt_rssi_avg, main_rssi_avg)) {
+                       if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) {
+                               /* Switch main and alt LNA */
+                               div_ant_conf.main_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA2;
+                               div_ant_conf.alt_lna_conf  =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                       } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) {
+                               div_ant_conf.main_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                               div_ant_conf.alt_lna_conf  =
+                                               ATH_ANT_DIV_COMB_LNA2;
+                       }
+
+                       goto div_comb_done;
+               } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) &&
+                          (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) {
+                       /* Set alt to another LNA */
+                       if (curr_main_set == ATH_ANT_DIV_COMB_LNA2)
+                               div_ant_conf.alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                       else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1)
+                               div_ant_conf.alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA2;
+
+                       goto div_comb_done;
+               }
+
+               if ((alt_rssi_avg < (main_rssi_avg +
+                                    div_ant_conf.lna1_lna2_delta)))
+                       goto div_comb_done;
+       }
+
+       if (!antcomb->scan_not_start) {
+               switch (curr_alt_set) {
+               case ATH_ANT_DIV_COMB_LNA2:
+                       antcomb->rssi_lna2 = alt_rssi_avg;
+                       antcomb->rssi_lna1 = main_rssi_avg;
+                       antcomb->scan = true;
+                       /* set to A+B */
+                       div_ant_conf.main_lna_conf =
+                               ATH_ANT_DIV_COMB_LNA1;
+                       div_ant_conf.alt_lna_conf  =
+                               ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+                       break;
+               case ATH_ANT_DIV_COMB_LNA1:
+                       antcomb->rssi_lna1 = alt_rssi_avg;
+                       antcomb->rssi_lna2 = main_rssi_avg;
+                       antcomb->scan = true;
+                       /* set to A+B */
+                       div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+                       div_ant_conf.alt_lna_conf  =
+                               ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+                       break;
+               case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
+                       antcomb->rssi_add = alt_rssi_avg;
+                       antcomb->scan = true;
+                       /* set to A-B */
+                       div_ant_conf.alt_lna_conf =
+                               ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+                       break;
+               case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2:
+                       antcomb->rssi_sub = alt_rssi_avg;
+                       antcomb->scan = false;
+                       if (antcomb->rssi_lna2 >
+                           (antcomb->rssi_lna1 +
+                           ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
+                               /* use LNA2 as main LNA */
+                               if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
+                                   (antcomb->rssi_add > antcomb->rssi_sub)) {
+                                       /* set to A+B */
+                                       div_ant_conf.main_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA2;
+                                       div_ant_conf.alt_lna_conf  =
+                                               ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+                               } else if (antcomb->rssi_sub >
+                                          antcomb->rssi_lna1) {
+                                       /* set to A-B */
+                                       div_ant_conf.main_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA2;
+                                       div_ant_conf.alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+                               } else {
+                                       /* set to LNA1 */
+                                       div_ant_conf.main_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA2;
+                                       div_ant_conf.alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                               }
+                       } else {
+                               /* use LNA1 as main LNA */
+                               if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
+                                   (antcomb->rssi_add > antcomb->rssi_sub)) {
+                                       /* set to A+B */
+                                       div_ant_conf.main_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                                       div_ant_conf.alt_lna_conf  =
+                                               ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+                               } else if (antcomb->rssi_sub >
+                                          antcomb->rssi_lna1) {
+                                       /* set to A-B */
+                                       div_ant_conf.main_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                                       div_ant_conf.alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+                               } else {
+                                       /* set to LNA2 */
+                                       div_ant_conf.main_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                                       div_ant_conf.alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA2;
+                               }
+                       }
+                       break;
+               default:
+                       break;
+               }
+       } else {
+               if (!antcomb->alt_good) {
+                       antcomb->scan_not_start = false;
+                       /* Set alt to another LNA */
+                       if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) {
+                               div_ant_conf.main_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA2;
+                               div_ant_conf.alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                       } else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) {
+                               div_ant_conf.main_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA1;
+                               div_ant_conf.alt_lna_conf =
+                                               ATH_ANT_DIV_COMB_LNA2;
+                       }
+                       goto div_comb_done;
+               }
+       }
+
+       ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
+                                          main_rssi_avg, alt_rssi_avg,
+                                          alt_ratio);
+
+       antcomb->quick_scan_cnt++;
+
+div_comb_done:
+       ath_ant_div_conf_fast_divbias(&div_ant_conf, antcomb, alt_ratio);
+       ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf);
+
+       antcomb->scan_start_time = jiffies;
+       antcomb->total_pkt_count = 0;
+       antcomb->main_total_rssi = 0;
+       antcomb->alt_total_rssi = 0;
+       antcomb->main_recv_cnt = 0;
+       antcomb->alt_recv_cnt = 0;
+}
+
+void ath_ant_comb_update(struct ath_softc *sc)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_hw_antcomb_conf div_ant_conf;
+       u8 lna_conf;
+
+       ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf);
+
+       if (sc->ant_rx == 1)
+               lna_conf = ATH_ANT_DIV_COMB_LNA1;
+       else
+               lna_conf = ATH_ANT_DIV_COMB_LNA2;
+
+       div_ant_conf.main_lna_conf = lna_conf;
+       div_ant_conf.alt_lna_conf = lna_conf;
+
+       ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf);
+}
index 9fdd70f..d7deb8c 100644 (file)
@@ -653,7 +653,6 @@ static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement,
 }
 
 static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah,
-                                                u8 num_chains,
                                                 struct coeff *coeff,
                                                 bool is_reusable)
 {
@@ -677,7 +676,9 @@ static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah,
        }
 
        /* Load the average of 2 passes */
-       for (i = 0; i < num_chains; i++) {
+       for (i = 0; i < AR9300_MAX_CHAINS; i++) {
+               if (!(ah->txchainmask & (1 << i)))
+                       continue;
                nmeasurement = REG_READ_FIELD(ah,
                                AR_PHY_TX_IQCAL_STATUS_B0,
                                AR_PHY_CALIBRATED_GAINS_0);
@@ -767,16 +768,13 @@ static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable)
        };
        struct coeff coeff;
        s32 iq_res[6];
-       u8 num_chains = 0;
        int i, im, j;
        int nmeasurement;
 
        for (i = 0; i < AR9300_MAX_CHAINS; i++) {
-               if (ah->txchainmask & (1 << i))
-                       num_chains++;
-       }
+               if (!(ah->txchainmask & (1 << i)))
+                       continue;
 
-       for (i = 0; i < num_chains; i++) {
                nmeasurement = REG_READ_FIELD(ah,
                                AR_PHY_TX_IQCAL_STATUS_B0,
                                AR_PHY_CALIBRATED_GAINS_0);
@@ -839,8 +837,7 @@ static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable)
                                coeff.phs_coeff[i][im] -= 128;
                }
        }
-       ar9003_hw_tx_iqcal_load_avg_2_passes(ah, num_chains,
-                                            &coeff, is_reusable);
+       ar9003_hw_tx_iqcal_load_avg_2_passes(ah, &coeff, is_reusable);
 
        return;
 
@@ -901,7 +898,6 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
        bool is_reusable = true, status = true;
        bool run_rtt_cal = false, run_agc_cal;
        bool rtt = !!(ah->caps.hw_caps & ATH9K_HW_CAP_RTT);
-       bool mci = !!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI);
        u32 agc_ctrl = 0, agc_supp_cals = AR_PHY_AGC_CONTROL_OFFSET_CAL |
                                          AR_PHY_AGC_CONTROL_FLTR_CAL   |
                                          AR_PHY_AGC_CONTROL_PKDET_CAL;
@@ -970,7 +966,7 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
        } else if (caldata && !caldata->done_txiqcal_once)
                run_agc_cal = true;
 
-       if (mci && IS_CHAN_2GHZ(chan) && run_agc_cal)
+       if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal)
                ar9003_mci_init_cal_req(ah, &is_reusable);
 
        if (!(IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan))) {
@@ -993,7 +989,7 @@ skip_tx_iqcal:
                                       0, AH_WAIT_TIMEOUT);
        }
 
-       if (mci && IS_CHAN_2GHZ(chan) && run_agc_cal)
+       if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal)
                ar9003_mci_init_cal_done(ah);
 
        if (rtt && !run_rtt_cal) {
index f794828..2cdf82b 100644 (file)
@@ -3613,6 +3613,7 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
                value = ar9003_switch_com_spdt_get(ah, is2ghz);
                REG_RMW_FIELD(ah, AR_PHY_GLB_CONTROL,
                                AR_SWITCH_TABLE_COM_SPDT_ALL, value);
+               REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, AR_BTCOEX_CTRL_SPDT_ENABLE);
        }
 
        value = ar9003_hw_ant_ctrl_common_2_get(ah, is2ghz);
index ffbb180..b1ced2a 100644 (file)
@@ -35,31 +35,30 @@ static int ar9003_mci_wait_for_interrupt(struct ath_hw *ah, u32 address,
        struct ath_common *common = ath9k_hw_common(ah);
 
        while (time_out) {
-               if (REG_READ(ah, address) & bit_position) {
-                       REG_WRITE(ah, address, bit_position);
-
-                       if (address == AR_MCI_INTERRUPT_RX_MSG_RAW) {
-                               if (bit_position &
-                                   AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)
-                                       ar9003_mci_reset_req_wakeup(ah);
-
-                               if (bit_position &
-                                   (AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING |
-                                    AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING))
-                                       REG_WRITE(ah, AR_MCI_INTERRUPT_RAW,
-                                       AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE);
-
-                               REG_WRITE(ah, AR_MCI_INTERRUPT_RAW,
-                                         AR_MCI_INTERRUPT_RX_MSG);
-                       }
-                       break;
-               }
+               if (!(REG_READ(ah, address) & bit_position)) {
+                       udelay(10);
+                       time_out -= 10;
 
-               udelay(10);
-               time_out -= 10;
+                       if (time_out < 0)
+                               break;
+                       else
+                               continue;
+               }
+               REG_WRITE(ah, address, bit_position);
 
-               if (time_out < 0)
+               if (address != AR_MCI_INTERRUPT_RX_MSG_RAW)
                        break;
+
+               if (bit_position & AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)
+                       ar9003_mci_reset_req_wakeup(ah);
+
+               if (bit_position & (AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING |
+                                   AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING))
+                       REG_WRITE(ah, AR_MCI_INTERRUPT_RAW,
+                                 AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE);
+
+               REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, AR_MCI_INTERRUPT_RX_MSG);
+               break;
        }
 
        if (time_out <= 0) {
@@ -127,14 +126,13 @@ static void ar9003_mci_send_coex_version_query(struct ath_hw *ah,
        struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
        u32 payload[4] = {0, 0, 0, 0};
 
-       if (!mci->bt_version_known &&
-           (mci->bt_state != MCI_BT_SLEEP)) {
-               MCI_GPM_SET_TYPE_OPCODE(payload,
-                                       MCI_GPM_COEX_AGENT,
-                                       MCI_GPM_COEX_VERSION_QUERY);
-               ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16,
-                                       wait_done, true);
-       }
+       if (mci->bt_version_known ||
+           (mci->bt_state == MCI_BT_SLEEP))
+               return;
+
+       MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT,
+                               MCI_GPM_COEX_VERSION_QUERY);
+       ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, wait_done, true);
 }
 
 static void ar9003_mci_send_coex_version_response(struct ath_hw *ah,
@@ -158,15 +156,14 @@ static void ar9003_mci_send_coex_wlan_channels(struct ath_hw *ah,
        struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
        u32 *payload = &mci->wlan_channels[0];
 
-       if ((mci->wlan_channels_update == true) &&
-           (mci->bt_state != MCI_BT_SLEEP)) {
-               MCI_GPM_SET_TYPE_OPCODE(payload,
-                                       MCI_GPM_COEX_AGENT,
-                                       MCI_GPM_COEX_WLAN_CHANNELS);
-               ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16,
-                                       wait_done, true);
-               MCI_GPM_SET_TYPE_OPCODE(payload, 0xff, 0xff);
-       }
+       if (!mci->wlan_channels_update ||
+           (mci->bt_state == MCI_BT_SLEEP))
+               return;
+
+       MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT,
+                               MCI_GPM_COEX_WLAN_CHANNELS);
+       ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, wait_done, true);
+       MCI_GPM_SET_TYPE_OPCODE(payload, 0xff, 0xff);
 }
 
 static void ar9003_mci_send_coex_bt_status_query(struct ath_hw *ah,
@@ -174,29 +171,30 @@ static void ar9003_mci_send_coex_bt_status_query(struct ath_hw *ah,
 {
        struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
        u32 payload[4] = {0, 0, 0, 0};
-       bool query_btinfo = !!(query_type & (MCI_GPM_COEX_QUERY_BT_ALL_INFO |
-                                            MCI_GPM_COEX_QUERY_BT_TOPOLOGY));
-
-       if (mci->bt_state != MCI_BT_SLEEP) {
+       bool query_btinfo;
 
-               MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT,
-                                       MCI_GPM_COEX_STATUS_QUERY);
+       if (mci->bt_state == MCI_BT_SLEEP)
+               return;
 
-               *(((u8 *)payload) + MCI_GPM_COEX_B_BT_BITMAP) = query_type;
+       query_btinfo = !!(query_type & (MCI_GPM_COEX_QUERY_BT_ALL_INFO |
+                                       MCI_GPM_COEX_QUERY_BT_TOPOLOGY));
+       MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT,
+                               MCI_GPM_COEX_STATUS_QUERY);
 
-               /*
-                * If bt_status_query message is  not sent successfully,
-                * then need_flush_btinfo should be set again.
-                */
-               if (!ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16,
-                                            wait_done, true)) {
-                       if (query_btinfo)
-                               mci->need_flush_btinfo = true;
-               }
+       *(((u8 *)payload) + MCI_GPM_COEX_B_BT_BITMAP) = query_type;
 
+       /*
+        * If bt_status_query message is  not sent successfully,
+        * then need_flush_btinfo should be set again.
+        */
+       if (!ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16,
+                               wait_done, true)) {
                if (query_btinfo)
-                       mci->query_bt = false;
+                       mci->need_flush_btinfo = true;
        }
+
+       if (query_btinfo)
+               mci->query_bt = false;
 }
 
 static void ar9003_mci_send_coex_halt_bt_gpm(struct ath_hw *ah, bool halt,
@@ -241,73 +239,73 @@ static void ar9003_mci_prep_interface(struct ath_hw *ah)
        ar9003_mci_remote_reset(ah, true);
        ar9003_mci_send_req_wake(ah, true);
 
-       if (ar9003_mci_wait_for_interrupt(ah, AR_MCI_INTERRUPT_RX_MSG_RAW,
-                                 AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING, 500)) {
+       if (!ar9003_mci_wait_for_interrupt(ah, AR_MCI_INTERRUPT_RX_MSG_RAW,
+                                 AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING, 500))
+               goto clear_redunt;
 
-               mci->bt_state = MCI_BT_AWAKE;
+       mci->bt_state = MCI_BT_AWAKE;
 
-               /*
-                * we don't need to send more remote_reset at this moment.
-                * If BT receive first remote_reset, then BT HW will
-                * be cleaned up and will be able to receive req_wake
-                * and BT HW will respond sys_waking.
-                * In this case, WLAN will receive BT's HW sys_waking.
-                * Otherwise, if BT SW missed initial remote_reset,
-                * that remote_reset will still clean up BT MCI RX,
-                * and the req_wake will wake BT up,
-                * and BT SW will respond this req_wake with a remote_reset and
-                * sys_waking. In this case, WLAN will receive BT's SW
-                * sys_waking. In either case, BT's RX is cleaned up. So we
-                * don't need to reply BT's remote_reset now, if any.
-                * Similarly, if in any case, WLAN can receive BT's sys_waking,
-                * that means WLAN's RX is also fine.
-                */
-               ar9003_mci_send_sys_waking(ah, true);
-               udelay(10);
+       /*
+        * we don't need to send more remote_reset at this moment.
+        * If BT receive first remote_reset, then BT HW will
+        * be cleaned up and will be able to receive req_wake
+        * and BT HW will respond sys_waking.
+        * In this case, WLAN will receive BT's HW sys_waking.
+        * Otherwise, if BT SW missed initial remote_reset,
+        * that remote_reset will still clean up BT MCI RX,
+        * and the req_wake will wake BT up,
+        * and BT SW will respond this req_wake with a remote_reset and
+        * sys_waking. In this case, WLAN will receive BT's SW
+        * sys_waking. In either case, BT's RX is cleaned up. So we
+        * don't need to reply BT's remote_reset now, if any.
+        * Similarly, if in any case, WLAN can receive BT's sys_waking,
+        * that means WLAN's RX is also fine.
+        */
+       ar9003_mci_send_sys_waking(ah, true);
+       udelay(10);
 
-               /*
-                * Set BT priority interrupt value to be 0xff to
-                * avoid having too many BT PRIORITY interrupts.
-                */
-               REG_WRITE(ah, AR_MCI_BT_PRI0, 0xFFFFFFFF);
-               REG_WRITE(ah, AR_MCI_BT_PRI1, 0xFFFFFFFF);
-               REG_WRITE(ah, AR_MCI_BT_PRI2, 0xFFFFFFFF);
-               REG_WRITE(ah, AR_MCI_BT_PRI3, 0xFFFFFFFF);
-               REG_WRITE(ah, AR_MCI_BT_PRI, 0X000000FF);
+       /*
+        * Set BT priority interrupt value to be 0xff to
+        * avoid having too many BT PRIORITY interrupts.
+        */
+       REG_WRITE(ah, AR_MCI_BT_PRI0, 0xFFFFFFFF);
+       REG_WRITE(ah, AR_MCI_BT_PRI1, 0xFFFFFFFF);
+       REG_WRITE(ah, AR_MCI_BT_PRI2, 0xFFFFFFFF);
+       REG_WRITE(ah, AR_MCI_BT_PRI3, 0xFFFFFFFF);
+       REG_WRITE(ah, AR_MCI_BT_PRI, 0X000000FF);
 
-               /*
-                * A contention reset will be received after send out
-                * sys_waking. Also BT priority interrupt bits will be set.
-                * Clear those bits before the next step.
-                */
+       /*
+        * A contention reset will be received after send out
+        * sys_waking. Also BT priority interrupt bits will be set.
+        * Clear those bits before the next step.
+        */
 
-               REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW,
-                         AR_MCI_INTERRUPT_RX_MSG_CONT_RST);
-               REG_WRITE(ah, AR_MCI_INTERRUPT_RAW,
-                         AR_MCI_INTERRUPT_BT_PRI);
+       REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW,
+                 AR_MCI_INTERRUPT_RX_MSG_CONT_RST);
+       REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, AR_MCI_INTERRUPT_BT_PRI);
 
-               if (mci->is_2g) {
-                       ar9003_mci_send_lna_transfer(ah, true);
-                       udelay(5);
-               }
+       if (mci->is_2g) {
+               ar9003_mci_send_lna_transfer(ah, true);
+               udelay(5);
+       }
 
-               if ((mci->is_2g && !mci->update_2g5g)) {
-                       if (ar9003_mci_wait_for_interrupt(ah,
-                                         AR_MCI_INTERRUPT_RX_MSG_RAW,
-                                         AR_MCI_INTERRUPT_RX_MSG_LNA_INFO,
-                                         mci_timeout))
-                               ath_dbg(common, MCI,
-                                       "MCI WLAN has control over the LNA & BT obeys it\n");
-                       else
-                               ath_dbg(common, MCI,
-                                       "MCI BT didn't respond to LNA_TRANS\n");
-               }
+       if ((mci->is_2g && !mci->update_2g5g)) {
+               if (ar9003_mci_wait_for_interrupt(ah,
+                                       AR_MCI_INTERRUPT_RX_MSG_RAW,
+                                       AR_MCI_INTERRUPT_RX_MSG_LNA_INFO,
+                                       mci_timeout))
+                       ath_dbg(common, MCI,
+                               "MCI WLAN has control over the LNA & BT obeys it\n");
+               else
+                       ath_dbg(common, MCI,
+                               "MCI BT didn't respond to LNA_TRANS\n");
        }
 
+clear_redunt:
        /* Clear the extra redundant SYS_WAKING from BT */
        if ((mci->bt_state == MCI_BT_AWAKE) &&
-               (REG_READ_FIELD(ah, AR_MCI_INTERRUPT_RX_MSG_RAW,
-                               AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING)) &&
+           (REG_READ_FIELD(ah, AR_MCI_INTERRUPT_RX_MSG_RAW,
+                           AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING)) &&
            (REG_READ_FIELD(ah, AR_MCI_INTERRUPT_RX_MSG_RAW,
                            AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING) == 0)) {
                REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW,
@@ -330,7 +328,6 @@ void ar9003_mci_set_full_sleep(struct ath_hw *ah)
        }
 
        mci->ready = false;
-       REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
 }
 
 static void ar9003_mci_disable_interrupt(struct ath_hw *ah)
@@ -615,9 +612,9 @@ static u32 ar9003_mci_wait_for_gpm(struct ath_hw *ah, u8 gpm_type,
                                }
                                break;
                        }
-               } else if ((recv_type == gpm_type) && (recv_opcode == gpm_opcode)) {
+               } else if ((recv_type == gpm_type) &&
+                          (recv_opcode == gpm_opcode))
                        break;
-               }
 
                /*
                 * check if it's cal_grant
@@ -731,38 +728,38 @@ int ar9003_mci_end_reset(struct ath_hw *ah, struct ath9k_channel *chan,
        if (!IS_CHAN_2GHZ(chan) || (mci_hw->bt_state != MCI_BT_SLEEP))
                goto exit;
 
-       if (ar9003_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET) ||
-           ar9003_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)) {
+       if (!ar9003_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET) &&
+           !ar9003_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE))
+               goto exit;
 
-               /*
-                * BT is sleeping. Check if BT wakes up during
-                * WLAN calibration. If BT wakes up during
-                * WLAN calibration, need to go through all
-                * message exchanges again and recal.
-                */
-               REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW,
-                         AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET |
-                         AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE);
+       /*
+        * BT is sleeping. Check if BT wakes up during
+        * WLAN calibration. If BT wakes up during
+        * WLAN calibration, need to go through all
+        * message exchanges again and recal.
+        */
+       REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW,
+                 (AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET |
+                  AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE));
 
-               ar9003_mci_remote_reset(ah, true);
-               ar9003_mci_send_sys_waking(ah, true);
-               udelay(1);
+       ar9003_mci_remote_reset(ah, true);
+       ar9003_mci_send_sys_waking(ah, true);
+       udelay(1);
 
-               if (IS_CHAN_2GHZ(chan))
-                       ar9003_mci_send_lna_transfer(ah, true);
+       if (IS_CHAN_2GHZ(chan))
+               ar9003_mci_send_lna_transfer(ah, true);
 
-               mci_hw->bt_state = MCI_BT_AWAKE;
+       mci_hw->bt_state = MCI_BT_AWAKE;
 
-               if (caldata) {
-                       caldata->done_txiqcal_once = false;
-                       caldata->done_txclcal_once = false;
-                       caldata->rtt_done = false;
-               }
+       if (caldata) {
+               caldata->done_txiqcal_once = false;
+               caldata->done_txclcal_once = false;
+               caldata->rtt_done = false;
+       }
 
-               if (!ath9k_hw_init_cal(ah, chan))
-                       return -EIO;
+       if (!ath9k_hw_init_cal(ah, chan))
+               return -EIO;
 
-       }
 exit:
        ar9003_mci_enable_interrupt(ah);
        return 0;
@@ -798,29 +795,27 @@ static void ar9003_mci_osla_setup(struct ath_hw *ah, bool enable)
        struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
        u32 thresh;
 
-       if (enable) {
-               REG_RMW_FIELD(ah, AR_MCI_SCHD_TABLE_2,
-                             AR_MCI_SCHD_TABLE_2_HW_BASED, 1);
-               REG_RMW_FIELD(ah, AR_MCI_SCHD_TABLE_2,
-                             AR_MCI_SCHD_TABLE_2_MEM_BASED, 1);
-
-               if (!(mci->config & ATH_MCI_CONFIG_DISABLE_AGGR_THRESH)) {
-                       thresh = MS(mci->config, ATH_MCI_CONFIG_AGGR_THRESH);
-                       REG_RMW_FIELD(ah, AR_BTCOEX_CTRL,
-                                     AR_BTCOEX_CTRL_AGGR_THRESH, thresh);
-                       REG_RMW_FIELD(ah, AR_BTCOEX_CTRL,
-                                     AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN, 1);
-               } else {
-                       REG_RMW_FIELD(ah, AR_BTCOEX_CTRL,
-                                     AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN, 0);
-               }
-
-               REG_RMW_FIELD(ah, AR_BTCOEX_CTRL,
-                             AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN, 1);
-       } else {
+       if (!enable) {
                REG_CLR_BIT(ah, AR_BTCOEX_CTRL,
                            AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN);
+               return;
        }
+       REG_RMW_FIELD(ah, AR_MCI_SCHD_TABLE_2, AR_MCI_SCHD_TABLE_2_HW_BASED, 1);
+       REG_RMW_FIELD(ah, AR_MCI_SCHD_TABLE_2,
+                     AR_MCI_SCHD_TABLE_2_MEM_BASED, 1);
+
+       if (!(mci->config & ATH_MCI_CONFIG_DISABLE_AGGR_THRESH)) {
+               thresh = MS(mci->config, ATH_MCI_CONFIG_AGGR_THRESH);
+               REG_RMW_FIELD(ah, AR_BTCOEX_CTRL,
+                             AR_BTCOEX_CTRL_AGGR_THRESH, thresh);
+               REG_RMW_FIELD(ah, AR_BTCOEX_CTRL,
+                             AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN, 1);
+       } else
+               REG_RMW_FIELD(ah, AR_BTCOEX_CTRL,
+                             AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN, 0);
+
+       REG_RMW_FIELD(ah, AR_BTCOEX_CTRL,
+                     AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN, 1);
 }
 
 void ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g,
@@ -943,26 +938,27 @@ static void ar9003_mci_send_2g5g_status(struct ath_hw *ah, bool wait_done)
        struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
        u32 new_flags, to_set, to_clear;
 
-       if (mci->update_2g5g && (mci->bt_state != MCI_BT_SLEEP)) {
-               if (mci->is_2g) {
-                       new_flags = MCI_2G_FLAGS;
-                       to_clear = MCI_2G_FLAGS_CLEAR_MASK;
-                       to_set = MCI_2G_FLAGS_SET_MASK;
-               } else {
-                       new_flags = MCI_5G_FLAGS;
-                       to_clear = MCI_5G_FLAGS_CLEAR_MASK;
-                       to_set = MCI_5G_FLAGS_SET_MASK;
-               }
+       if (!mci->update_2g5g || (mci->bt_state == MCI_BT_SLEEP))
+               return;
+
+       if (mci->is_2g) {
+               new_flags = MCI_2G_FLAGS;
+               to_clear = MCI_2G_FLAGS_CLEAR_MASK;
+               to_set = MCI_2G_FLAGS_SET_MASK;
+       } else {
+               new_flags = MCI_5G_FLAGS;
+               to_clear = MCI_5G_FLAGS_CLEAR_MASK;
+               to_set = MCI_5G_FLAGS_SET_MASK;
+       }
 
-               if (to_clear)
-                       ar9003_mci_send_coex_bt_flags(ah, wait_done,
+       if (to_clear)
+               ar9003_mci_send_coex_bt_flags(ah, wait_done,
                                              MCI_GPM_COEX_BT_FLAGS_CLEAR,
                                              to_clear);
-               if (to_set)
-                       ar9003_mci_send_coex_bt_flags(ah, wait_done,
+       if (to_set)
+               ar9003_mci_send_coex_bt_flags(ah, wait_done,
                                              MCI_GPM_COEX_BT_FLAGS_SET,
                                              to_set);
-       }
 }
 
 static void ar9003_mci_queue_unsent_gpm(struct ath_hw *ah, u8 header,
@@ -1018,34 +1014,34 @@ void ar9003_mci_2g5g_switch(struct ath_hw *ah, bool wait_done)
 {
        struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
 
-       if (mci->update_2g5g) {
-               if (mci->is_2g) {
-                       ar9003_mci_send_2g5g_status(ah, true);
-                       ar9003_mci_send_lna_transfer(ah, true);
-                       udelay(5);
+       if (!mci->update_2g5g)
+               return;
 
-                       REG_CLR_BIT(ah, AR_MCI_TX_CTRL,
-                                   AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE);
-                       REG_CLR_BIT(ah, AR_PHY_GLB_CONTROL,
-                                   AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL);
+       if (mci->is_2g) {
+               ar9003_mci_send_2g5g_status(ah, true);
+               ar9003_mci_send_lna_transfer(ah, true);
+               udelay(5);
 
-                       if (!(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA)) {
-                               REG_SET_BIT(ah, AR_BTCOEX_CTRL,
-                                           AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN);
-                       }
-               } else {
-                       ar9003_mci_send_lna_take(ah, true);
-                       udelay(5);
-
-                       REG_SET_BIT(ah, AR_MCI_TX_CTRL,
-                                   AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE);
-                       REG_SET_BIT(ah, AR_PHY_GLB_CONTROL,
-                                   AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL);
-                       REG_CLR_BIT(ah, AR_BTCOEX_CTRL,
+               REG_CLR_BIT(ah, AR_MCI_TX_CTRL,
+                           AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE);
+               REG_CLR_BIT(ah, AR_PHY_GLB_CONTROL,
+                           AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL);
+
+               if (!(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA))
+                       REG_SET_BIT(ah, AR_BTCOEX_CTRL,
                                    AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN);
+       } else {
+               ar9003_mci_send_lna_take(ah, true);
+               udelay(5);
 
-                       ar9003_mci_send_2g5g_status(ah, true);
-               }
+               REG_SET_BIT(ah, AR_MCI_TX_CTRL,
+                           AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE);
+               REG_SET_BIT(ah, AR_PHY_GLB_CONTROL,
+                           AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL);
+               REG_CLR_BIT(ah, AR_BTCOEX_CTRL,
+                           AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN);
+
+               ar9003_mci_send_2g5g_status(ah, true);
        }
 }
 
@@ -1132,7 +1128,7 @@ void ar9003_mci_init_cal_req(struct ath_hw *ah, bool *is_reusable)
        if (ar9003_mci_wait_for_gpm(ah, MCI_GPM_BT_CAL_GRANT, 0, 50000)) {
                ath_dbg(common, MCI, "MCI BT_CAL_GRANT received\n");
        } else {
-               is_reusable = false;
+               *is_reusable = false;
                ath_dbg(common, MCI, "MCI BT_CAL_GRANT not received\n");
        }
 }
@@ -1259,12 +1255,12 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data)
                        }
                        if (p_data)
                                *p_data = more_gpm;
-                       }
+               }
 
-                       if (value != MCI_GPM_INVALID)
-                               value <<= 4;
+               if (value != MCI_GPM_INVALID)
+                       value <<= 4;
 
-                       break;
+               break;
        case MCI_STATE_LAST_SCHD_MSG_OFFSET:
                value = MS(REG_READ(ah, AR_MCI_RX_STATUS),
                                    AR_MCI_RX_LAST_SCHD_MSG_INDEX);
@@ -1359,24 +1355,22 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data)
                ar9003_mci_send_coex_bt_status_query(ah, true, query_type);
                break;
        case MCI_STATE_NEED_FLUSH_BT_INFO:
-                       /*
-                        * btcoex_hw.mci.unhalt_bt_gpm means whether it's
-                        * needed to send UNHALT message. It's set whenever
-                        * there's a request to send HALT message.
-                        * mci_halted_bt_gpm means whether HALT message is sent
-                        * out successfully.
-                        *
-                        * Checking (mci_unhalt_bt_gpm == false) instead of
-                        * checking (ah->mci_halted_bt_gpm == false) will make
-                        * sure currently is in UNHALT-ed mode and BT can
-                        * respond to status query.
-                        */
-                       value = (!mci->unhalt_bt_gpm &&
-                                mci->need_flush_btinfo) ? 1 : 0;
-                       if (p_data)
-                               mci->need_flush_btinfo =
-                                       (*p_data != 0) ? true : false;
-                       break;
+               /*
+                * btcoex_hw.mci.unhalt_bt_gpm means whether it's
+                * needed to send UNHALT message. It's set whenever
+                * there's a request to send HALT message.
+                * mci_halted_bt_gpm means whether HALT message is sent
+                * out successfully.
+                *
+                * Checking (mci_unhalt_bt_gpm == false) instead of
+                * checking (ah->mci_halted_bt_gpm == false) will make
+                * sure currently is in UNHALT-ed mode and BT can
+                * respond to status query.
+                */
+               value = (!mci->unhalt_bt_gpm && mci->need_flush_btinfo) ? 1 : 0;
+               if (p_data)
+                       mci->need_flush_btinfo = (*p_data != 0) ? true : false;
+               break;
        case MCI_STATE_RECOVER_RX:
                ar9003_mci_prep_interface(ah);
                mci->query_bt = true;
@@ -1387,9 +1381,6 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data)
        case MCI_STATE_NEED_FTP_STOMP:
                value = !(mci->config & ATH_MCI_CONFIG_DISABLE_FTP_STOMP);
                break;
-       case MCI_STATE_NEED_TUNING:
-               value = !(mci->config & ATH_MCI_CONFIG_DISABLE_TUNING);
-               break;
        default:
                break;
        }
@@ -1397,3 +1388,19 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data)
        return value;
 }
 EXPORT_SYMBOL(ar9003_mci_state);
+
+void ar9003_mci_bt_gain_ctrl(struct ath_hw *ah)
+{
+       struct ath_common *common = ath9k_hw_common(ah);
+       struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci;
+
+       ath_dbg(common, MCI, "Give LNA and SPDT control to BT\n");
+
+       REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL);
+       mci->is_2g = false;
+       mci->update_2g5g = true;
+       ar9003_mci_send_2g5g_status(ah, true);
+
+       /* Force another 2g5g update at next scanning */
+       mci->update_2g5g = true;
+}
index 4842f6c..10282e2 100644 (file)
@@ -212,7 +212,6 @@ enum mci_state_type {
        MCI_STATE_SET_CONCUR_TX_PRI,
        MCI_STATE_RECOVER_RX,
        MCI_STATE_NEED_FTP_STOMP,
-       MCI_STATE_NEED_TUNING,
        MCI_STATE_DEBUG,
        MCI_STATE_MAX
 };
@@ -266,6 +265,7 @@ void ar9003_mci_setup(struct ath_hw *ah, u32 gpm_addr, void *gpm_buf,
 void ar9003_mci_cleanup(struct ath_hw *ah);
 void ar9003_mci_get_interrupt(struct ath_hw *ah, u32 *raw_intr,
                              u32 *rx_msg_intr);
+void ar9003_mci_bt_gain_ctrl(struct ath_hw *ah);
 
 /*
  * These functions are used by ath9k_hw.
@@ -273,10 +273,6 @@ void ar9003_mci_get_interrupt(struct ath_hw *ah, u32 *raw_intr,
 
 #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
 
-static inline bool ar9003_mci_is_ready(struct ath_hw *ah)
-{
-       return ah->btcoex_hw.mci.ready;
-}
 void ar9003_mci_stop_bt(struct ath_hw *ah, bool save_fullsleep);
 void ar9003_mci_init_cal_req(struct ath_hw *ah, bool *is_reusable);
 void ar9003_mci_init_cal_done(struct ath_hw *ah);
@@ -292,10 +288,6 @@ void ar9003_mci_get_isr(struct ath_hw *ah, enum ath9k_int *masked);
 
 #else
 
-static inline bool ar9003_mci_is_ready(struct ath_hw *ah)
-{
-       return false;
-}
 static inline void ar9003_mci_stop_bt(struct ath_hw *ah, bool save_fullsleep)
 {
 }
index 11abb97..d6baf69 100644 (file)
@@ -676,6 +676,10 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
        if (chan->channel == 2484)
                ar9003_hw_prog_ini(ah, &ah->ini_japan2484, 1);
 
+       if (AR_SREV_9462(ah))
+               REG_WRITE(ah, AR_GLB_SWREG_DISCONT_MODE,
+                         AR_GLB_SWREG_DISCONT_EN_BT_WLAN);
+
        ah->modes_index = modesIndex;
        ar9003_hw_override_ini(ah);
        ar9003_hw_set_channel_regs(ah, chan);
index 7268a48..ed662c3 100644 (file)
 #define AR_PHY_CHAN_INFO_MEMORY_CAPTURE_MASK         0x0001
 #define AR_PHY_RX_DELAY_DELAY   0x00003FFF
 #define AR_PHY_CCK_TX_CTRL_JAPAN    0x00000010
-#define AR_PHY_SPECTRAL_SCAN_ENABLE         0x00000001
-#define AR_PHY_SPECTRAL_SCAN_ENABLE_S       0
-#define AR_PHY_SPECTRAL_SCAN_ACTIVE         0x00000002
-#define AR_PHY_SPECTRAL_SCAN_ACTIVE_S       1
-#define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD     0x000000F0
-#define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD_S   4
-#define AR_PHY_SPECTRAL_SCAN_PERIOD         0x0000FF00
-#define AR_PHY_SPECTRAL_SCAN_PERIOD_S       8
-#define AR_PHY_SPECTRAL_SCAN_COUNT          0x00FF0000
-#define AR_PHY_SPECTRAL_SCAN_COUNT_S        16
-#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT   0x01000000
-#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_S 24
+
+#define AR_PHY_SPECTRAL_SCAN_ENABLE           0x00000001
+#define AR_PHY_SPECTRAL_SCAN_ENABLE_S         0
+#define AR_PHY_SPECTRAL_SCAN_ACTIVE           0x00000002
+#define AR_PHY_SPECTRAL_SCAN_ACTIVE_S         1
+#define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD       0x000000F0
+#define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD_S     4
+#define AR_PHY_SPECTRAL_SCAN_PERIOD           0x0000FF00
+#define AR_PHY_SPECTRAL_SCAN_PERIOD_S         8
+#define AR_PHY_SPECTRAL_SCAN_COUNT            0x0FFF0000
+#define AR_PHY_SPECTRAL_SCAN_COUNT_S          16
+#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT     0x10000000
+#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_S   28
+#define AR_PHY_SPECTRAL_SCAN_PRIORITY         0x20000000
+#define AR_PHY_SPECTRAL_SCAN_PRIORITY_S       29
+#define AR_PHY_SPECTRAL_SCAN_USE_ERR5         0x40000000
+#define AR_PHY_SPECTRAL_SCAN_USE_ERR5_S       30
+#define AR_PHY_SPECTRAL_SCAN_COMPRESSED_RPT   0x80000000
+#define AR_PHY_SPECTRAL_SCAN_COMPRESSED_RPT_S 31
+
 #define AR_PHY_CHANNEL_STATUS_RX_CLEAR      0x00000004
 #define AR_PHY_RTT_CTRL_ENA_RADIO_RETENTION     0x00000001
 #define AR_PHY_RTT_CTRL_ENA_RADIO_RETENTION_S   0
index 1d6658e..a10ece0 100644 (file)
@@ -958,7 +958,7 @@ static const u32 ar9462_2p0_radio_core[][2] = {
        {0x0001604c, 0x2699e04f},
        {0x00016050, 0x6db6db6c},
        {0x00016058, 0x6c200000},
-       {0x00016080, 0x00040000},
+       {0x00016080, 0x000c0000},
        {0x00016084, 0x9a68048c},
        {0x00016088, 0x54214514},
        {0x0001608c, 0x1203040b},
@@ -981,7 +981,7 @@ static const u32 ar9462_2p0_radio_core[][2] = {
        {0x00016144, 0x02084080},
        {0x00016148, 0x000080c0},
        {0x00016280, 0x050a0001},
-       {0x00016284, 0x3d841400},
+       {0x00016284, 0x3d841418},
        {0x00016288, 0x00000000},
        {0x0001628c, 0xe3000000},
        {0x00016290, 0xa1005080},
index a277cf6..02fc1c1 100644 (file)
@@ -307,6 +307,7 @@ struct ath_rx {
        u8 defant;
        u8 rxotherant;
        u32 *rxlink;
+       u32 num_pkts;
        unsigned int rxfilter;
        spinlock_t rxbuflock;
        struct list_head rxbuf;
@@ -325,6 +326,9 @@ int ath_rx_init(struct ath_softc *sc, int nbufs);
 void ath_rx_cleanup(struct ath_softc *sc);
 int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp);
 struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype);
+void ath_txq_lock(struct ath_softc *sc, struct ath_txq *txq);
+void ath_txq_unlock(struct ath_softc *sc, struct ath_txq *txq);
+void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq);
 void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq);
 bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx);
 void ath_draintxq(struct ath_softc *sc,
@@ -414,9 +418,9 @@ int ath_beaconq_config(struct ath_softc *sc);
 void ath_set_beacon(struct ath_softc *sc);
 void ath9k_set_beaconing_status(struct ath_softc *sc, bool status);
 
-/*******/
-/* ANI */
-/*******/
+/*******************/
+/* Link Monitoring */
+/*******************/
 
 #define ATH_STA_SHORT_CALINTERVAL 1000    /* 1 second */
 #define ATH_AP_SHORT_CALINTERVAL  100     /* 100 ms */
@@ -427,7 +431,9 @@ void ath9k_set_beaconing_status(struct ath_softc *sc, bool status);
 #define ATH_RESTART_CALINTERVAL   1200000 /* 20 minutes */
 
 #define ATH_PAPRD_TIMEOUT      100 /* msecs */
+#define ATH_PLL_WORK_INTERVAL   100
 
+void ath_tx_complete_poll_work(struct work_struct *work);
 void ath_reset_work(struct work_struct *work);
 void ath_hw_check(struct work_struct *work);
 void ath_hw_pll_work(struct work_struct *work);
@@ -436,22 +442,31 @@ void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon);
 void ath_paprd_calibrate(struct work_struct *work);
 void ath_ani_calibrate(unsigned long data);
 void ath_start_ani(struct ath_common *common);
+int ath_update_survey_stats(struct ath_softc *sc);
+void ath_update_survey_nf(struct ath_softc *sc, int channel);
 
 /**********/
 /* BTCOEX */
 /**********/
 
+enum bt_op_flags {
+       BT_OP_PRIORITY_DETECTED,
+       BT_OP_SCAN,
+};
+
 struct ath_btcoex {
        bool hw_timer_enabled;
        spinlock_t btcoex_lock;
        struct timer_list period_timer; /* Timer for BT period */
        u32 bt_priority_cnt;
        unsigned long bt_priority_time;
+       unsigned long op_flags;
        int bt_stomp_type; /* Types of BT stomping */
        u32 btcoex_no_stomp; /* in usec */
        u32 btcoex_period; /* in usec */
        u32 btscan_no_stomp; /* in usec */
        u32 duty_cycle;
+       u32 bt_wait_time;
        struct ath_gen_timer *no_stomp_timer; /* Timer for no BT stomping */
        struct ath_mci_profile mci;
 };
@@ -513,8 +528,10 @@ static inline void ath_deinit_leds(struct ath_softc *sc)
 }
 #endif
 
-
+/*******************************/
 /* Antenna diversity/combining */
+/*******************************/
+
 #define ATH_ANT_RX_CURRENT_SHIFT 4
 #define ATH_ANT_RX_MAIN_SHIFT 2
 #define ATH_ANT_RX_MASK 0x3
@@ -567,6 +584,9 @@ struct ath_ant_comb {
        unsigned long scan_start_time;
 };
 
+void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs);
+void ath_ant_comb_update(struct ath_softc *sc);
+
 /********************/
 /* Main driver core */
 /********************/
@@ -584,15 +604,15 @@ struct ath_ant_comb {
 #define ATH_TXPOWER_MAX         100     /* .5 dBm units */
 #define ATH_RATE_DUMMY_MARKER   0
 
-#define SC_OP_INVALID                BIT(0)
-#define SC_OP_BEACONS                BIT(1)
-#define SC_OP_OFFCHANNEL             BIT(2)
-#define SC_OP_RXFLUSH                BIT(3)
-#define SC_OP_TSF_RESET              BIT(4)
-#define SC_OP_BT_PRIORITY_DETECTED   BIT(5)
-#define SC_OP_BT_SCAN                BIT(6)
-#define SC_OP_ANI_RUN                BIT(7)
-#define SC_OP_PRIM_STA_VIF           BIT(8)
+enum sc_op_flags {
+       SC_OP_INVALID,
+       SC_OP_BEACONS,
+       SC_OP_RXFLUSH,
+       SC_OP_TSF_RESET,
+       SC_OP_ANI_RUN,
+       SC_OP_PRIM_STA_VIF,
+       SC_OP_HW_RESET,
+};
 
 /* Powersave flags */
 #define PS_WAIT_FOR_BEACON        BIT(0)
@@ -638,9 +658,9 @@ struct ath_softc {
        struct completion paprd_complete;
 
        unsigned int hw_busy_count;
+       unsigned long sc_flags;
 
        u32 intrstatus;
-       u32 sc_flags; /* SC_OP_* */
        u16 ps_flags; /* PS_* */
        u16 curtxpow;
        bool ps_enabled;
@@ -736,5 +756,4 @@ void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif,
                               struct ath9k_vif_iter_data *iter_data);
 
-
 #endif /* ATH9K_H */
index 11bc55e..40775da 100644 (file)
@@ -48,7 +48,10 @@ int ath_beaconq_config(struct ath_softc *sc)
                txq = sc->tx.txq_map[WME_AC_BE];
                ath9k_hw_get_txq_props(ah, txq->axq_qnum, &qi_be);
                qi.tqi_aifs = qi_be.tqi_aifs;
-               qi.tqi_cwmin = 4*qi_be.tqi_cwmin;
+               if (ah->slottime == ATH9K_SLOT_TIME_20)
+                       qi.tqi_cwmin = 2*qi_be.tqi_cwmin;
+               else
+                       qi.tqi_cwmin = 4*qi_be.tqi_cwmin;
                qi.tqi_cwmax = qi_be.tqi_cwmax;
        }
 
@@ -387,7 +390,7 @@ void ath_beacon_tasklet(unsigned long data)
                } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) {
                        ath_dbg(common, BSTUCK, "beacon is officially stuck\n");
                        sc->beacon.bmisscnt = 0;
-                       sc->sc_flags |= SC_OP_TSF_RESET;
+                       set_bit(SC_OP_TSF_RESET, &sc->sc_flags);
                        ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
                }
 
@@ -477,16 +480,16 @@ static void ath9k_beacon_init(struct ath_softc *sc,
                              u32 next_beacon,
                              u32 beacon_period)
 {
-       if (sc->sc_flags & SC_OP_TSF_RESET) {
+       if (test_bit(SC_OP_TSF_RESET, &sc->sc_flags)) {
                ath9k_ps_wakeup(sc);
                ath9k_hw_reset_tsf(sc->sc_ah);
        }
 
        ath9k_hw_beaconinit(sc->sc_ah, next_beacon, beacon_period);
 
-       if (sc->sc_flags & SC_OP_TSF_RESET) {
+       if (test_bit(SC_OP_TSF_RESET, &sc->sc_flags)) {
                ath9k_ps_restore(sc);
-               sc->sc_flags &= ~SC_OP_TSF_RESET;
+               clear_bit(SC_OP_TSF_RESET, &sc->sc_flags);
        }
 }
 
@@ -516,7 +519,7 @@ static void ath_beacon_config_ap(struct ath_softc *sc,
        /* Set the computed AP beacon timers */
 
        ath9k_hw_disable_interrupts(ah);
-       sc->sc_flags |= SC_OP_TSF_RESET;
+       set_bit(SC_OP_TSF_RESET, &sc->sc_flags);
        ath9k_beacon_init(sc, nexttbtt, intval);
        sc->beacon.bmisscnt = 0;
        ath9k_hw_set_interrupts(ah);
@@ -659,7 +662,7 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc,
        u32 tsf, intval, nexttbtt;
 
        ath9k_reset_beacon_status(sc);
-       if (!(sc->sc_flags & SC_OP_BEACONS))
+       if (!test_bit(SC_OP_BEACONS, &sc->sc_flags))
                ath9k_hw_settsf64(ah, sc->beacon.bc_tstamp);
 
        intval = TU_TO_USEC(conf->beacon_interval);
@@ -724,7 +727,7 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc,
         */
        if ((sc->sc_ah->opmode == NL80211_IFTYPE_STATION) &&
            (vif->type == NL80211_IFTYPE_STATION) &&
-           (sc->sc_flags & SC_OP_BEACONS) &&
+           test_bit(SC_OP_BEACONS, &sc->sc_flags) &&
            !avp->primary_sta_vif) {
                ath_dbg(common, CONFIG,
                        "Beacon already configured for a station interface\n");
@@ -810,7 +813,7 @@ void ath_set_beacon(struct ath_softc *sc)
                return;
        }
 
-       sc->sc_flags |= SC_OP_BEACONS;
+       set_bit(SC_OP_BEACONS, &sc->sc_flags);
 }
 
 void ath9k_set_beaconing_status(struct ath_softc *sc, bool status)
@@ -818,7 +821,7 @@ void ath9k_set_beaconing_status(struct ath_softc *sc, bool status)
        struct ath_hw *ah = sc->sc_ah;
 
        if (!ath_has_valid_bslot(sc)) {
-               sc->sc_flags &= ~SC_OP_BEACONS;
+               clear_bit(SC_OP_BEACONS, &sc->sc_flags);
                return;
        }
 
index 1ca6da8..acd4373 100644 (file)
@@ -336,10 +336,16 @@ static void ar9003_btcoex_bt_stomp(struct ath_hw *ah,
                         enum ath_stomp_type stomp_type)
 {
        struct ath_btcoex_hw *btcoex = &ah->btcoex_hw;
-       const u32 *weight = AR_SREV_9462(ah) ? ar9003_wlan_weights[stomp_type] :
-                                              ar9462_wlan_weights[stomp_type];
+       const u32 *weight = ar9003_wlan_weights[stomp_type];
        int i;
 
+       if (AR_SREV_9462(ah)) {
+               if ((stomp_type == ATH_BTCOEX_STOMP_LOW) &&
+                   btcoex->mci.stomp_ftp)
+                       stomp_type = ATH_BTCOEX_STOMP_LOW_FTP;
+               weight = ar9462_wlan_weights[stomp_type];
+       }
+
        for (i = 0; i < AR9300_NUM_WLAN_WEIGHTS; i++) {
                btcoex->bt_weight[i] = AR9300_BT_WGHT;
                btcoex->wlan_weight[i] = weight[i];
index 3a1e1cf..20092f9 100644 (file)
@@ -36,6 +36,9 @@
 #define ATH_BT_CNT_THRESHOLD          3
 #define ATH_BT_CNT_SCAN_THRESHOLD      15
 
+#define ATH_BTCOEX_RX_WAIT_TIME       100
+#define ATH_BTCOEX_STOMP_FTP_THRESH   5
+
 #define AR9300_NUM_BT_WEIGHTS   4
 #define AR9300_NUM_WLAN_WEIGHTS 4
 /* Defines the BT AR_BT_COEX_WGHT used */
@@ -80,6 +83,7 @@ struct ath9k_hw_mci {
        u8 bt_ver_major;
        u8 bt_ver_minor;
        u8 bt_state;
+       u8 stomp_ftp;
 };
 
 struct ath_btcoex_hw {
index fde700c..2831258 100644 (file)
@@ -205,10 +205,10 @@ static ssize_t write_file_disable_ani(struct file *file,
        common->disable_ani = !!disable_ani;
 
        if (disable_ani) {
-               sc->sc_flags &= ~SC_OP_ANI_RUN;
+               clear_bit(SC_OP_ANI_RUN, &sc->sc_flags);
                del_timer_sync(&common->ani.timer);
        } else {
-               sc->sc_flags |= SC_OP_ANI_RUN;
+               set_bit(SC_OP_ANI_RUN, &sc->sc_flags);
                ath_start_ani(common);
        }
 
@@ -374,6 +374,8 @@ void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status)
                sc->debug.stats.istats.dtim++;
        if (status & ATH9K_INT_TSFOOR)
                sc->debug.stats.istats.tsfoor++;
+       if (status & ATH9K_INT_MCI)
+               sc->debug.stats.istats.mci++;
 }
 
 static ssize_t read_file_interrupt(struct file *file, char __user *user_buf,
@@ -418,6 +420,7 @@ static ssize_t read_file_interrupt(struct file *file, char __user *user_buf,
        PR_IS("DTIMSYNC", dtimsync);
        PR_IS("DTIM", dtim);
        PR_IS("TSFOOR", tsfoor);
+       PR_IS("MCI", mci);
        PR_IS("TOTAL", total);
 
        len += snprintf(buf + len, mxlen - len,
@@ -1318,7 +1321,7 @@ static int open_file_bb_mac_samps(struct inode *inode, struct file *file)
        u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
        u8 nread;
 
-       if (sc->sc_flags & SC_OP_INVALID)
+       if (test_bit(SC_OP_INVALID, &sc->sc_flags))
                return -EAGAIN;
 
        buf = vmalloc(size);
index c34da09..d0f851c 100644 (file)
@@ -86,6 +86,7 @@ struct ath_interrupt_stats {
        u32 dtim;
        u32 bb_watchdog;
        u32 tsfoor;
+       u32 mci;
 
        /* Sync-cause stats */
        u32 sync_cause_all;
index 281a9af..af6d273 100644 (file)
@@ -132,17 +132,18 @@ static void ath_detect_bt_priority(struct ath_softc *sc)
 
        if (time_after(jiffies, btcoex->bt_priority_time +
                        msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) {
-               sc->sc_flags &= ~(SC_OP_BT_PRIORITY_DETECTED | SC_OP_BT_SCAN);
+               clear_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags);
+               clear_bit(BT_OP_SCAN, &btcoex->op_flags);
                /* Detect if colocated bt started scanning */
                if (btcoex->bt_priority_cnt >= ATH_BT_CNT_SCAN_THRESHOLD) {
                        ath_dbg(ath9k_hw_common(sc->sc_ah), BTCOEX,
                                "BT scan detected\n");
-                       sc->sc_flags |= (SC_OP_BT_SCAN |
-                                        SC_OP_BT_PRIORITY_DETECTED);
+                       set_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags);
+                       set_bit(BT_OP_SCAN, &btcoex->op_flags);
                } else if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) {
                        ath_dbg(ath9k_hw_common(sc->sc_ah), BTCOEX,
                                "BT priority traffic detected\n");
-                       sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED;
+                       set_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags);
                }
 
                btcoex->bt_priority_cnt = 0;
@@ -190,13 +191,26 @@ static void ath_btcoex_period_timer(unsigned long data)
        struct ath_softc *sc = (struct ath_softc *) data;
        struct ath_hw *ah = sc->sc_ah;
        struct ath_btcoex *btcoex = &sc->btcoex;
+       struct ath_mci_profile *mci = &btcoex->mci;
        u32 timer_period;
        bool is_btscan;
 
        ath9k_ps_wakeup(sc);
        if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI))
                ath_detect_bt_priority(sc);
-       is_btscan = sc->sc_flags & SC_OP_BT_SCAN;
+       is_btscan = test_bit(BT_OP_SCAN, &btcoex->op_flags);
+
+       btcoex->bt_wait_time += btcoex->btcoex_period;
+       if (btcoex->bt_wait_time > ATH_BTCOEX_RX_WAIT_TIME) {
+               if (ar9003_mci_state(ah, MCI_STATE_NEED_FTP_STOMP, NULL) &&
+                   (mci->num_pan || mci->num_other_acl))
+                       ah->btcoex_hw.mci.stomp_ftp =
+                               (sc->rx.num_pkts < ATH_BTCOEX_STOMP_FTP_THRESH);
+               else
+                       ah->btcoex_hw.mci.stomp_ftp = false;
+               btcoex->bt_wait_time = 0;
+               sc->rx.num_pkts = 0;
+       }
 
        spin_lock_bh(&btcoex->btcoex_lock);
 
@@ -219,8 +233,7 @@ static void ath_btcoex_period_timer(unsigned long data)
 
        ath9k_ps_restore(sc);
        timer_period = btcoex->btcoex_period / 1000;
-       mod_timer(&btcoex->period_timer, jiffies +
-                                 msecs_to_jiffies(timer_period));
+       mod_timer(&btcoex->period_timer, jiffies + msecs_to_jiffies(timer_period));
 }
 
 /*
@@ -233,14 +246,14 @@ static void ath_btcoex_no_stomp_timer(void *arg)
        struct ath_hw *ah = sc->sc_ah;
        struct ath_btcoex *btcoex = &sc->btcoex;
        struct ath_common *common = ath9k_hw_common(ah);
-       bool is_btscan = sc->sc_flags & SC_OP_BT_SCAN;
 
        ath_dbg(common, BTCOEX, "no stomp timer running\n");
 
        ath9k_ps_wakeup(sc);
        spin_lock_bh(&btcoex->btcoex_lock);
 
-       if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || is_btscan)
+       if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW ||
+           test_bit(BT_OP_SCAN, &btcoex->op_flags))
                ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE);
         else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL)
                ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_LOW);
@@ -292,7 +305,7 @@ void ath9k_btcoex_timer_resume(struct ath_softc *sc)
 
        btcoex->bt_priority_cnt = 0;
        btcoex->bt_priority_time = jiffies;
-       sc->sc_flags &= ~(SC_OP_BT_PRIORITY_DETECTED | SC_OP_BT_SCAN);
+       btcoex->op_flags &= ~(BT_OP_PRIORITY_DETECTED | BT_OP_SCAN);
 
        mod_timer(&btcoex->period_timer, jiffies);
 }
@@ -316,12 +329,13 @@ void ath9k_btcoex_timer_pause(struct ath_softc *sc)
 
 u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen)
 {
+       struct ath_btcoex *btcoex = &sc->btcoex;
        struct ath_mci_profile *mci = &sc->btcoex.mci;
        u16 aggr_limit = 0;
 
        if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI) && mci->aggr_limit)
                aggr_limit = (max_4ms_framelen * mci->aggr_limit) >> 4;
-       else if (sc->sc_flags & SC_OP_BT_PRIORITY_DETECTED)
+       else if (test_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags))
                aggr_limit = min((max_4ms_framelen * 3) / 8,
                                 (u32)ATH_AMPDU_LIMIT_MAX);
 
index 7db1890..45e6700 100644 (file)
@@ -390,14 +390,6 @@ static void ath9k_hw_disablepcie(struct ath_hw *ah)
        REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000);
 }
 
-static void ath9k_hw_aspm_init(struct ath_hw *ah)
-{
-       struct ath_common *common = ath9k_hw_common(ah);
-
-       if (common->bus_ops->aspm_init)
-               common->bus_ops->aspm_init(common);
-}
-
 /* This should work for all families including legacy */
 static bool ath9k_hw_chip_test(struct ath_hw *ah)
 {
@@ -693,9 +685,6 @@ static int __ath9k_hw_init(struct ath_hw *ah)
        if (r)
                return r;
 
-       if (ah->is_pciexpress)
-               ath9k_hw_aspm_init(ah);
-
        r = ath9k_hw_init_macaddr(ah);
        if (r) {
                ath_err(common, "Failed to initialize MAC address\n");
@@ -1443,9 +1432,6 @@ static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type)
                break;
        }
 
-       if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI)
-               REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
-
        return ret;
 }
 
@@ -1721,7 +1707,7 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan)
        ath9k_hw_loadnf(ah, ah->curchan);
        ath9k_hw_start_nfcal(ah, true);
 
-       if ((ah->caps.hw_caps & ATH9K_HW_CAP_MCI) && ar9003_mci_is_ready(ah))
+       if (ath9k_hw_mci_is_enabled(ah))
                ar9003_mci_2g5g_switch(ah, true);
 
        if (AR_SREV_9271(ah))
@@ -1742,10 +1728,9 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
        u64 tsf = 0;
        int i, r;
        bool start_mci_reset = false;
-       bool mci = !!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI);
        bool save_fullsleep = ah->chip_fullsleep;
 
-       if (mci) {
+       if (ath9k_hw_mci_is_enabled(ah)) {
                start_mci_reset = ar9003_mci_start_reset(ah, chan);
                if (start_mci_reset)
                        return 0;
@@ -1774,7 +1759,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
                        return r;
        }
 
-       if (mci)
+       if (ath9k_hw_mci_is_enabled(ah))
                ar9003_mci_stop_bt(ah, save_fullsleep);
 
        saveDefAntenna = REG_READ(ah, AR_DEF_ANTENNA);
@@ -1832,7 +1817,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
        if (r)
                return r;
 
-       if (mci)
+       if (ath9k_hw_mci_is_enabled(ah))
                ar9003_mci_reset(ah, false, IS_CHAN_2GHZ(chan), save_fullsleep);
 
        /*
@@ -1951,7 +1936,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
        ath9k_hw_loadnf(ah, chan);
        ath9k_hw_start_nfcal(ah, true);
 
-       if (mci && ar9003_mci_end_reset(ah, chan, caldata))
+       if (ath9k_hw_mci_is_enabled(ah) && ar9003_mci_end_reset(ah, chan, caldata))
                return -EIO;
 
        ENABLE_REGWRITE_BUFFER(ah);
@@ -1996,7 +1981,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
        if (ath9k_hw_btcoex_is_enabled(ah))
                ath9k_hw_btcoex_enable(ah);
 
-       if (mci)
+       if (ath9k_hw_mci_is_enabled(ah))
                ar9003_mci_check_bt(ah);
 
        if (AR_SREV_9300_20_OR_LATER(ah)) {
@@ -2019,39 +2004,35 @@ EXPORT_SYMBOL(ath9k_hw_reset);
  * Notify Power Mgt is disabled in self-generated frames.
  * If requested, force chip to sleep.
  */
-static void ath9k_set_power_sleep(struct ath_hw *ah, int setChip)
+static void ath9k_set_power_sleep(struct ath_hw *ah)
 {
        REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
-       if (setChip) {
-               if (AR_SREV_9462(ah)) {
-                       REG_WRITE(ah, AR_TIMER_MODE,
-                                 REG_READ(ah, AR_TIMER_MODE) & 0xFFFFFF00);
-                       REG_WRITE(ah, AR_NDP2_TIMER_MODE, REG_READ(ah,
-                                 AR_NDP2_TIMER_MODE) & 0xFFFFFF00);
-                       REG_WRITE(ah, AR_SLP32_INC,
-                                 REG_READ(ah, AR_SLP32_INC) & 0xFFF00000);
-                       /* xxx Required for WLAN only case ? */
-                       REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, 0);
-                       udelay(100);
-               }
 
-               /*
-                * Clear the RTC force wake bit to allow the
-                * mac to go to sleep.
-                */
-               REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
+       if (AR_SREV_9462(ah)) {
+               REG_CLR_BIT(ah, AR_TIMER_MODE, 0xff);
+               REG_CLR_BIT(ah, AR_NDP2_TIMER_MODE, 0xff);
+               REG_CLR_BIT(ah, AR_SLP32_INC, 0xfffff);
+               /* xxx Required for WLAN only case ? */
+               REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, 0);
+               udelay(100);
+       }
 
-               if (AR_SREV_9462(ah))
-                       udelay(100);
+       /*
+        * Clear the RTC force wake bit to allow the
+        * mac to go to sleep.
+        */
+       REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
+
+       if (ath9k_hw_mci_is_enabled(ah))
+               udelay(100);
 
-               if (!AR_SREV_9100(ah) && !AR_SREV_9300_20_OR_LATER(ah))
-                       REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF);
+       if (!AR_SREV_9100(ah) && !AR_SREV_9300_20_OR_LATER(ah))
+               REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF);
 
-               /* Shutdown chip. Active low */
-               if (!AR_SREV_5416(ah) && !AR_SREV_9271(ah)) {
-                       REG_CLR_BIT(ah, AR_RTC_RESET, AR_RTC_RESET_EN);
-                       udelay(2);
-               }
+       /* Shutdown chip. Active low */
+       if (!AR_SREV_5416(ah) && !AR_SREV_9271(ah)) {
+               REG_CLR_BIT(ah, AR_RTC_RESET, AR_RTC_RESET_EN);
+               udelay(2);
        }
 
        /* Clear Bit 14 of AR_WA after putting chip into Full Sleep mode. */
@@ -2064,44 +2045,38 @@ static void ath9k_set_power_sleep(struct ath_hw *ah, int setChip)
  * frames. If request, set power mode of chip to
  * auto/normal.  Duration in units of 128us (1/8 TU).
  */
-static void ath9k_set_power_network_sleep(struct ath_hw *ah, int setChip)
+static void ath9k_set_power_network_sleep(struct ath_hw *ah)
 {
-       u32 val;
+       struct ath9k_hw_capabilities *pCap = &ah->caps;
 
        REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
-       if (setChip) {
-               struct ath9k_hw_capabilities *pCap = &ah->caps;
 
-               if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
-                       /* Set WakeOnInterrupt bit; clear ForceWake bit */
-                       REG_WRITE(ah, AR_RTC_FORCE_WAKE,
-                                 AR_RTC_FORCE_WAKE_ON_INT);
-               } else {
+       if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
+               /* Set WakeOnInterrupt bit; clear ForceWake bit */
+               REG_WRITE(ah, AR_RTC_FORCE_WAKE,
+                         AR_RTC_FORCE_WAKE_ON_INT);
+       } else {
 
-                       /* When chip goes into network sleep, it could be waken
-                        * up by MCI_INT interrupt caused by BT's HW messages
-                        * (LNA_xxx, CONT_xxx) which chould be in a very fast
-                        * rate (~100us). This will cause chip to leave and
-                        * re-enter network sleep mode frequently, which in
-                        * consequence will have WLAN MCI HW to generate lots of
-                        * SYS_WAKING and SYS_SLEEPING messages which will make
-                        * BT CPU to busy to process.
-                        */
-                       if (AR_SREV_9462(ah)) {
-                               val = REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_EN) &
-                                       ~AR_MCI_INTERRUPT_RX_HW_MSG_MASK;
-                               REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, val);
-                       }
-                       /*
-                        * Clear the RTC force wake bit to allow the
-                        * mac to go to sleep.
-                        */
-                       REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE,
-                                   AR_RTC_FORCE_WAKE_EN);
-
-                       if (AR_SREV_9462(ah))
-                               udelay(30);
-               }
+               /* When chip goes into network sleep, it could be waken
+                * up by MCI_INT interrupt caused by BT's HW messages
+                * (LNA_xxx, CONT_xxx) which chould be in a very fast
+                * rate (~100us). This will cause chip to leave and
+                * re-enter network sleep mode frequently, which in
+                * consequence will have WLAN MCI HW to generate lots of
+                * SYS_WAKING and SYS_SLEEPING messages which will make
+                * BT CPU to busy to process.
+                */
+               if (ath9k_hw_mci_is_enabled(ah))
+                       REG_CLR_BIT(ah, AR_MCI_INTERRUPT_RX_MSG_EN,
+                                   AR_MCI_INTERRUPT_RX_HW_MSG_MASK);
+               /*
+                * Clear the RTC force wake bit to allow the
+                * mac to go to sleep.
+                */
+               REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
+
+               if (ath9k_hw_mci_is_enabled(ah))
+                       udelay(30);
        }
 
        /* Clear Bit 14 of AR_WA after putting chip into Net Sleep mode. */
@@ -2109,7 +2084,7 @@ static void ath9k_set_power_network_sleep(struct ath_hw *ah, int setChip)
                REG_WRITE(ah, AR_WA, ah->WARegVal & ~AR_WA_D3_L1_DISABLE);
 }
 
-static bool ath9k_hw_set_power_awake(struct ath_hw *ah, int setChip)
+static bool ath9k_hw_set_power_awake(struct ath_hw *ah)
 {
        u32 val;
        int i;
@@ -2120,37 +2095,35 @@ static bool ath9k_hw_set_power_awake(struct ath_hw *ah, int setChip)
                udelay(10);
        }
 
-       if (setChip) {
-               if ((REG_READ(ah, AR_RTC_STATUS) &
-                    AR_RTC_STATUS_M) == AR_RTC_STATUS_SHUTDOWN) {
-                       if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) {
-                               return false;
-                       }
-                       if (!AR_SREV_9300_20_OR_LATER(ah))
-                               ath9k_hw_init_pll(ah, NULL);
+       if ((REG_READ(ah, AR_RTC_STATUS) &
+            AR_RTC_STATUS_M) == AR_RTC_STATUS_SHUTDOWN) {
+               if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) {
+                       return false;
                }
-               if (AR_SREV_9100(ah))
-                       REG_SET_BIT(ah, AR_RTC_RESET,
-                                   AR_RTC_RESET_EN);
+               if (!AR_SREV_9300_20_OR_LATER(ah))
+                       ath9k_hw_init_pll(ah, NULL);
+       }
+       if (AR_SREV_9100(ah))
+               REG_SET_BIT(ah, AR_RTC_RESET,
+                           AR_RTC_RESET_EN);
 
+       REG_SET_BIT(ah, AR_RTC_FORCE_WAKE,
+                   AR_RTC_FORCE_WAKE_EN);
+       udelay(50);
+
+       for (i = POWER_UP_TIME / 50; i > 0; i--) {
+               val = REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M;
+               if (val == AR_RTC_STATUS_ON)
+                       break;
+               udelay(50);
                REG_SET_BIT(ah, AR_RTC_FORCE_WAKE,
                            AR_RTC_FORCE_WAKE_EN);
-               udelay(50);
-
-               for (i = POWER_UP_TIME / 50; i > 0; i--) {
-                       val = REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M;
-                       if (val == AR_RTC_STATUS_ON)
-                               break;
-                       udelay(50);
-                       REG_SET_BIT(ah, AR_RTC_FORCE_WAKE,
-                                   AR_RTC_FORCE_WAKE_EN);
-               }
-               if (i == 0) {
-                       ath_err(ath9k_hw_common(ah),
-                               "Failed to wakeup in %uus\n",
-                               POWER_UP_TIME / 20);
-                       return false;
-               }
+       }
+       if (i == 0) {
+               ath_err(ath9k_hw_common(ah),
+                       "Failed to wakeup in %uus\n",
+                       POWER_UP_TIME / 20);
+               return false;
        }
 
        REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
@@ -2161,7 +2134,7 @@ static bool ath9k_hw_set_power_awake(struct ath_hw *ah, int setChip)
 bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode)
 {
        struct ath_common *common = ath9k_hw_common(ah);
-       int status = true, setChip = true;
+       int status = true;
        static const char *modes[] = {
                "AWAKE",
                "FULL-SLEEP",
@@ -2177,25 +2150,17 @@ bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode)
 
        switch (mode) {
        case ATH9K_PM_AWAKE:
-               status = ath9k_hw_set_power_awake(ah, setChip);
-
-               if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI)
-                       REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
-
+               status = ath9k_hw_set_power_awake(ah);
                break;
        case ATH9K_PM_FULL_SLEEP:
-               if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI)
+               if (ath9k_hw_mci_is_enabled(ah))
                        ar9003_mci_set_full_sleep(ah);
 
-               ath9k_set_power_sleep(ah, setChip);
+               ath9k_set_power_sleep(ah);
                ah->chip_fullsleep = true;
                break;
        case ATH9K_PM_NETWORK_SLEEP:
-
-               if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI)
-                       REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2);
-
-               ath9k_set_power_network_sleep(ah, setChip);
+               ath9k_set_power_network_sleep(ah);
                break;
        default:
                ath_err(common, "Unknown power mode %u\n", mode);
@@ -2765,6 +2730,9 @@ EXPORT_SYMBOL(ath9k_hw_setrxfilter);
 
 bool ath9k_hw_phy_disable(struct ath_hw *ah)
 {
+       if (ath9k_hw_mci_is_enabled(ah))
+               ar9003_mci_bt_gain_ctrl(ah);
+
        if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM))
                return false;
 
index b620c55..03d5909 100644 (file)
@@ -824,7 +824,6 @@ struct ath_hw {
        struct ar5416IniArray ini_japan2484;
        struct ar5416IniArray iniModes_9271_ANI_reg;
        struct ar5416IniArray ini_radio_post_sys2ant;
-       struct ar5416IniArray ini_BTCOEX_MAX_TXPWR;
 
        struct ar5416IniArray iniMac[ATH_INI_NUM_SPLIT];
        struct ar5416IniArray iniBB[ATH_INI_NUM_SPLIT];
@@ -1037,6 +1036,11 @@ static inline bool ath9k_hw_btcoex_is_enabled(struct ath_hw *ah)
 {
        return ah->btcoex_hw.enabled;
 }
+static inline bool ath9k_hw_mci_is_enabled(struct ath_hw *ah)
+{
+       return ah->btcoex_hw.enabled && (ah->caps.hw_caps & ATH9K_HW_CAP_MCI);
+
+}
 void ath9k_hw_btcoex_enable(struct ath_hw *ah);
 static inline enum ath_btcoex_scheme
 ath9k_hw_get_btcoex_scheme(struct ath_hw *ah)
@@ -1048,6 +1052,10 @@ static inline bool ath9k_hw_btcoex_is_enabled(struct ath_hw *ah)
 {
        return false;
 }
+static inline bool ath9k_hw_mci_is_enabled(struct ath_hw *ah)
+{
+       return false;
+}
 static inline void ath9k_hw_btcoex_enable(struct ath_hw *ah)
 {
 }
index dee9e09..9dfce1a 100644 (file)
@@ -489,6 +489,7 @@ static void ath9k_init_misc(struct ath_softc *sc)
 
        setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc);
 
+       sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
        sc->config.txpowlimit = ATH_TXPOWER_MAX;
        memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
        sc->beacon.slottime = ATH9K_SLOT_TIME_9;
@@ -560,6 +561,12 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet,
                     (unsigned long)sc);
 
+       INIT_WORK(&sc->hw_reset_work, ath_reset_work);
+       INIT_WORK(&sc->hw_check_work, ath_hw_check);
+       INIT_WORK(&sc->paprd_work, ath_paprd_calibrate);
+       INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work);
+       setup_timer(&sc->rx_poll_timer, ath_rx_poll, (unsigned long)sc);
+
        /*
         * Cache line size is used to size and align various
         * structures used to communicate with the hardware.
@@ -590,6 +597,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        ath9k_cmn_init_crypto(sc->sc_ah);
        ath9k_init_misc(sc);
 
+       if (common->bus_ops->aspm_init)
+               common->bus_ops->aspm_init(common);
+
        return 0;
 
 err_btcoex:
@@ -782,11 +792,6 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
                ARRAY_SIZE(ath9k_tpt_blink));
 #endif
 
-       INIT_WORK(&sc->hw_reset_work, ath_reset_work);
-       INIT_WORK(&sc->hw_check_work, ath_hw_check);
-       INIT_WORK(&sc->paprd_work, ath_paprd_calibrate);
-       INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work);
-
        /* Register with mac80211 */
        error = ieee80211_register_hw(hw);
        if (error)
@@ -805,9 +810,6 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
                        goto error_world;
        }
 
-       setup_timer(&sc->rx_poll_timer, ath_rx_poll, (unsigned long)sc);
-       sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
-
        ath_init_leds(sc);
        ath_start_rfkill_poll(sc);
 
diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c
new file mode 100644 (file)
index 0000000..0cc4c70
--- /dev/null
@@ -0,0 +1,502 @@
+/*
+ * Copyright (c) 2012 Qualcomm Atheros, 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 "ath9k.h"
+
+/*
+ * TX polling - checks if the TX engine is stuck somewhere
+ * and issues a chip reset if so.
+ */
+void ath_tx_complete_poll_work(struct work_struct *work)
+{
+       struct ath_softc *sc = container_of(work, struct ath_softc,
+                                           tx_complete_work.work);
+       struct ath_txq *txq;
+       int i;
+       bool needreset = false;
+#ifdef CONFIG_ATH9K_DEBUGFS
+       sc->tx_complete_poll_work_seen++;
+#endif
+
+       for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
+               if (ATH_TXQ_SETUP(sc, i)) {
+                       txq = &sc->tx.txq[i];
+                       ath_txq_lock(sc, txq);
+                       if (txq->axq_depth) {
+                               if (txq->axq_tx_inprogress) {
+                                       needreset = true;
+                                       ath_txq_unlock(sc, txq);
+                                       break;
+                               } else {
+                                       txq->axq_tx_inprogress = true;
+                               }
+                       }
+                       ath_txq_unlock_complete(sc, txq);
+               }
+
+       if (needreset) {
+               ath_dbg(ath9k_hw_common(sc->sc_ah), RESET,
+                       "tx hung, resetting the chip\n");
+               RESET_STAT_INC(sc, RESET_TYPE_TX_HANG);
+               ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+               return;
+       }
+
+       ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
+                                    msecs_to_jiffies(ATH_TX_COMPLETE_POLL_INT));
+}
+
+/*
+ * Checks if the BB/MAC is hung.
+ */
+void ath_hw_check(struct work_struct *work)
+{
+       struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work);
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       unsigned long flags;
+       int busy;
+       u8 is_alive, nbeacon = 1;
+
+       ath9k_ps_wakeup(sc);
+       is_alive = ath9k_hw_check_alive(sc->sc_ah);
+
+       if (is_alive && !AR_SREV_9300(sc->sc_ah))
+               goto out;
+       else if (!is_alive && AR_SREV_9300(sc->sc_ah)) {
+               ath_dbg(common, RESET,
+                       "DCU stuck is detected. Schedule chip reset\n");
+               RESET_STAT_INC(sc, RESET_TYPE_MAC_HANG);
+               goto sched_reset;
+       }
+
+       spin_lock_irqsave(&common->cc_lock, flags);
+       busy = ath_update_survey_stats(sc);
+       spin_unlock_irqrestore(&common->cc_lock, flags);
+
+       ath_dbg(common, RESET, "Possible baseband hang, busy=%d (try %d)\n",
+               busy, sc->hw_busy_count + 1);
+       if (busy >= 99) {
+               if (++sc->hw_busy_count >= 3) {
+                       RESET_STAT_INC(sc, RESET_TYPE_BB_HANG);
+                       goto sched_reset;
+               }
+       } else if (busy >= 0) {
+               sc->hw_busy_count = 0;
+               nbeacon = 3;
+       }
+
+       ath_start_rx_poll(sc, nbeacon);
+       goto out;
+
+sched_reset:
+       ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+out:
+       ath9k_ps_restore(sc);
+}
+
+/*
+ * PLL-WAR for AR9485/AR9340
+ */
+static bool ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum)
+{
+       static int count;
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+       if (pll_sqsum >= 0x40000) {
+               count++;
+               if (count == 3) {
+                       ath_dbg(common, RESET, "PLL WAR, resetting the chip\n");
+                       RESET_STAT_INC(sc, RESET_TYPE_PLL_HANG);
+                       ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+                       count = 0;
+                       return true;
+               }
+       } else {
+               count = 0;
+       }
+
+       return false;
+}
+
+void ath_hw_pll_work(struct work_struct *work)
+{
+       u32 pll_sqsum;
+       struct ath_softc *sc = container_of(work, struct ath_softc,
+                                           hw_pll_work.work);
+
+       ath9k_ps_wakeup(sc);
+       pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah);
+       ath9k_ps_restore(sc);
+       if (ath_hw_pll_rx_hang_check(sc, pll_sqsum))
+               return;
+
+       ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work,
+                                    msecs_to_jiffies(ATH_PLL_WORK_INTERVAL));
+}
+
+/*
+ * RX Polling - monitors baseband hangs.
+ */
+void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon)
+{
+       if (!AR_SREV_9300(sc->sc_ah))
+               return;
+
+       if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags))
+               return;
+
+       mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies
+                 (nbeacon * sc->cur_beacon_conf.beacon_interval));
+}
+
+void ath_rx_poll(unsigned long data)
+{
+       struct ath_softc *sc = (struct ath_softc *)data;
+
+       ieee80211_queue_work(sc->hw, &sc->hw_check_work);
+}
+
+/*
+ * PA Pre-distortion.
+ */
+static void ath_paprd_activate(struct ath_softc *sc)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath9k_hw_cal_data *caldata = ah->caldata;
+       int chain;
+
+       if (!caldata || !caldata->paprd_done)
+               return;
+
+       ath9k_ps_wakeup(sc);
+       ar9003_paprd_enable(ah, false);
+       for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
+               if (!(ah->txchainmask & BIT(chain)))
+                       continue;
+
+               ar9003_paprd_populate_single_table(ah, caldata, chain);
+       }
+
+       ar9003_paprd_enable(ah, true);
+       ath9k_ps_restore(sc);
+}
+
+static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int chain)
+{
+       struct ieee80211_hw *hw = sc->hw;
+       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+       struct ath_tx_control txctl;
+       int time_left;
+
+       memset(&txctl, 0, sizeof(txctl));
+       txctl.txq = sc->tx.txq_map[WME_AC_BE];
+
+       memset(tx_info, 0, sizeof(*tx_info));
+       tx_info->band = hw->conf.channel->band;
+       tx_info->flags |= IEEE80211_TX_CTL_NO_ACK;
+       tx_info->control.rates[0].idx = 0;
+       tx_info->control.rates[0].count = 1;
+       tx_info->control.rates[0].flags = IEEE80211_TX_RC_MCS;
+       tx_info->control.rates[1].idx = -1;
+
+       init_completion(&sc->paprd_complete);
+       txctl.paprd = BIT(chain);
+
+       if (ath_tx_start(hw, skb, &txctl) != 0) {
+               ath_dbg(common, CALIBRATE, "PAPRD TX failed\n");
+               dev_kfree_skb_any(skb);
+               return false;
+       }
+
+       time_left = wait_for_completion_timeout(&sc->paprd_complete,
+                       msecs_to_jiffies(ATH_PAPRD_TIMEOUT));
+
+       if (!time_left)
+               ath_dbg(common, CALIBRATE,
+                       "Timeout waiting for paprd training on TX chain %d\n",
+                       chain);
+
+       return !!time_left;
+}
+
+void ath_paprd_calibrate(struct work_struct *work)
+{
+       struct ath_softc *sc = container_of(work, struct ath_softc, paprd_work);
+       struct ieee80211_hw *hw = sc->hw;
+       struct ath_hw *ah = sc->sc_ah;
+       struct ieee80211_hdr *hdr;
+       struct sk_buff *skb = NULL;
+       struct ath9k_hw_cal_data *caldata = ah->caldata;
+       struct ath_common *common = ath9k_hw_common(ah);
+       int ftype;
+       int chain_ok = 0;
+       int chain;
+       int len = 1800;
+
+       if (!caldata)
+               return;
+
+       ath9k_ps_wakeup(sc);
+
+       if (ar9003_paprd_init_table(ah) < 0)
+               goto fail_paprd;
+
+       skb = alloc_skb(len, GFP_KERNEL);
+       if (!skb)
+               goto fail_paprd;
+
+       skb_put(skb, len);
+       memset(skb->data, 0, len);
+       hdr = (struct ieee80211_hdr *)skb->data;
+       ftype = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC;
+       hdr->frame_control = cpu_to_le16(ftype);
+       hdr->duration_id = cpu_to_le16(10);
+       memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN);
+       memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN);
+       memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN);
+
+       for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
+               if (!(ah->txchainmask & BIT(chain)))
+                       continue;
+
+               chain_ok = 0;
+
+               ath_dbg(common, CALIBRATE,
+                       "Sending PAPRD frame for thermal measurement on chain %d\n",
+                       chain);
+               if (!ath_paprd_send_frame(sc, skb, chain))
+                       goto fail_paprd;
+
+               ar9003_paprd_setup_gain_table(ah, chain);
+
+               ath_dbg(common, CALIBRATE,
+                       "Sending PAPRD training frame on chain %d\n", chain);
+               if (!ath_paprd_send_frame(sc, skb, chain))
+                       goto fail_paprd;
+
+               if (!ar9003_paprd_is_done(ah)) {
+                       ath_dbg(common, CALIBRATE,
+                               "PAPRD not yet done on chain %d\n", chain);
+                       break;
+               }
+
+               if (ar9003_paprd_create_curve(ah, caldata, chain)) {
+                       ath_dbg(common, CALIBRATE,
+                               "PAPRD create curve failed on chain %d\n",
+                               chain);
+                       break;
+               }
+
+               chain_ok = 1;
+       }
+       kfree_skb(skb);
+
+       if (chain_ok) {
+               caldata->paprd_done = true;
+               ath_paprd_activate(sc);
+       }
+
+fail_paprd:
+       ath9k_ps_restore(sc);
+}
+
+/*
+ *  ANI performs periodic noise floor calibration
+ *  that is used to adjust and optimize the chip performance.  This
+ *  takes environmental changes (location, temperature) into account.
+ *  When the task is complete, it reschedules itself depending on the
+ *  appropriate interval that was calculated.
+ */
+void ath_ani_calibrate(unsigned long data)
+{
+       struct ath_softc *sc = (struct ath_softc *)data;
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+       bool longcal = false;
+       bool shortcal = false;
+       bool aniflag = false;
+       unsigned int timestamp = jiffies_to_msecs(jiffies);
+       u32 cal_interval, short_cal_interval, long_cal_interval;
+       unsigned long flags;
+
+       if (ah->caldata && ah->caldata->nfcal_interference)
+               long_cal_interval = ATH_LONG_CALINTERVAL_INT;
+       else
+               long_cal_interval = ATH_LONG_CALINTERVAL;
+
+       short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ?
+               ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL;
+
+       /* Only calibrate if awake */
+       if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE)
+               goto set_timer;
+
+       ath9k_ps_wakeup(sc);
+
+       /* Long calibration runs independently of short calibration. */
+       if ((timestamp - common->ani.longcal_timer) >= long_cal_interval) {
+               longcal = true;
+               common->ani.longcal_timer = timestamp;
+       }
+
+       /* Short calibration applies only while caldone is false */
+       if (!common->ani.caldone) {
+               if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) {
+                       shortcal = true;
+                       common->ani.shortcal_timer = timestamp;
+                       common->ani.resetcal_timer = timestamp;
+               }
+       } else {
+               if ((timestamp - common->ani.resetcal_timer) >=
+                   ATH_RESTART_CALINTERVAL) {
+                       common->ani.caldone = ath9k_hw_reset_calvalid(ah);
+                       if (common->ani.caldone)
+                               common->ani.resetcal_timer = timestamp;
+               }
+       }
+
+       /* Verify whether we must check ANI */
+       if (sc->sc_ah->config.enable_ani
+           && (timestamp - common->ani.checkani_timer) >=
+           ah->config.ani_poll_interval) {
+               aniflag = true;
+               common->ani.checkani_timer = timestamp;
+       }
+
+       /* Call ANI routine if necessary */
+       if (aniflag) {
+               spin_lock_irqsave(&common->cc_lock, flags);
+               ath9k_hw_ani_monitor(ah, ah->curchan);
+               ath_update_survey_stats(sc);
+               spin_unlock_irqrestore(&common->cc_lock, flags);
+       }
+
+       /* Perform calibration if necessary */
+       if (longcal || shortcal) {
+               common->ani.caldone =
+                       ath9k_hw_calibrate(ah, ah->curchan,
+                                          ah->rxchainmask, longcal);
+       }
+
+       ath_dbg(common, ANI,
+               "Calibration @%lu finished: %s %s %s, caldone: %s\n",
+               jiffies,
+               longcal ? "long" : "", shortcal ? "short" : "",
+               aniflag ? "ani" : "", common->ani.caldone ? "true" : "false");
+
+       ath9k_ps_restore(sc);
+
+set_timer:
+       /*
+       * Set timer interval based on previous results.
+       * The interval must be the shortest necessary to satisfy ANI,
+       * short calibration and long calibration.
+       */
+       ath9k_debug_samp_bb_mac(sc);
+       cal_interval = ATH_LONG_CALINTERVAL;
+       if (sc->sc_ah->config.enable_ani)
+               cal_interval = min(cal_interval,
+                                  (u32)ah->config.ani_poll_interval);
+       if (!common->ani.caldone)
+               cal_interval = min(cal_interval, (u32)short_cal_interval);
+
+       mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval));
+       if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_PAPRD) && ah->caldata) {
+               if (!ah->caldata->paprd_done)
+                       ieee80211_queue_work(sc->hw, &sc->paprd_work);
+               else if (!ah->paprd_table_write_done)
+                       ath_paprd_activate(sc);
+       }
+}
+
+void ath_start_ani(struct ath_common *common)
+{
+       struct ath_hw *ah = common->ah;
+       unsigned long timestamp = jiffies_to_msecs(jiffies);
+       struct ath_softc *sc = (struct ath_softc *) common->priv;
+
+       if (!test_bit(SC_OP_ANI_RUN, &sc->sc_flags))
+               return;
+
+       if (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)
+               return;
+
+       common->ani.longcal_timer = timestamp;
+       common->ani.shortcal_timer = timestamp;
+       common->ani.checkani_timer = timestamp;
+
+       mod_timer(&common->ani.timer,
+                 jiffies + msecs_to_jiffies((u32)ah->config.ani_poll_interval));
+}
+
+void ath_update_survey_nf(struct ath_softc *sc, int channel)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath9k_channel *chan = &ah->channels[channel];
+       struct survey_info *survey = &sc->survey[channel];
+
+       if (chan->noisefloor) {
+               survey->filled |= SURVEY_INFO_NOISE_DBM;
+               survey->noise = ath9k_hw_getchan_noise(ah, chan);
+       }
+}
+
+/*
+ * Updates the survey statistics and returns the busy time since last
+ * update in %, if the measurement duration was long enough for the
+ * result to be useful, -1 otherwise.
+ */
+int ath_update_survey_stats(struct ath_softc *sc)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath_common *common = ath9k_hw_common(ah);
+       int pos = ah->curchan - &ah->channels[0];
+       struct survey_info *survey = &sc->survey[pos];
+       struct ath_cycle_counters *cc = &common->cc_survey;
+       unsigned int div = common->clockrate * 1000;
+       int ret = 0;
+
+       if (!ah->curchan)
+               return -1;
+
+       if (ah->power_mode == ATH9K_PM_AWAKE)
+               ath_hw_cycle_counters_update(common);
+
+       if (cc->cycles > 0) {
+               survey->filled |= SURVEY_INFO_CHANNEL_TIME |
+                       SURVEY_INFO_CHANNEL_TIME_BUSY |
+                       SURVEY_INFO_CHANNEL_TIME_RX |
+                       SURVEY_INFO_CHANNEL_TIME_TX;
+               survey->channel_time += cc->cycles / div;
+               survey->channel_time_busy += cc->rx_busy / div;
+               survey->channel_time_rx += cc->rx_frame / div;
+               survey->channel_time_tx += cc->tx_frame / div;
+       }
+
+       if (cc->cycles < div)
+               return -1;
+
+       if (cc->cycles > 0)
+               ret = cc->rx_busy * 100 / cc->cycles;
+
+       memset(cc, 0, sizeof(*cc));
+
+       ath_update_survey_nf(sc, pos);
+
+       return ret;
+}
index cfbcc04..c0f478b 100644 (file)
@@ -101,6 +101,7 @@ void ath9k_ps_wakeup(struct ath_softc *sc)
                spin_lock(&common->cc_lock);
                ath_hw_cycle_counters_update(common);
                memset(&common->cc_survey, 0, sizeof(common->cc_survey));
+               memset(&common->cc_ani, 0, sizeof(common->cc_ani));
                spin_unlock(&common->cc_lock);
        }
 
@@ -143,84 +144,6 @@ void ath9k_ps_restore(struct ath_softc *sc)
        spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
 }
 
-void ath_start_ani(struct ath_common *common)
-{
-       struct ath_hw *ah = common->ah;
-       unsigned long timestamp = jiffies_to_msecs(jiffies);
-       struct ath_softc *sc = (struct ath_softc *) common->priv;
-
-       if (!(sc->sc_flags & SC_OP_ANI_RUN))
-               return;
-
-       if (sc->sc_flags & SC_OP_OFFCHANNEL)
-               return;
-
-       common->ani.longcal_timer = timestamp;
-       common->ani.shortcal_timer = timestamp;
-       common->ani.checkani_timer = timestamp;
-
-       mod_timer(&common->ani.timer,
-                 jiffies +
-                       msecs_to_jiffies((u32)ah->config.ani_poll_interval));
-}
-
-static void ath_update_survey_nf(struct ath_softc *sc, int channel)
-{
-       struct ath_hw *ah = sc->sc_ah;
-       struct ath9k_channel *chan = &ah->channels[channel];
-       struct survey_info *survey = &sc->survey[channel];
-
-       if (chan->noisefloor) {
-               survey->filled |= SURVEY_INFO_NOISE_DBM;
-               survey->noise = ath9k_hw_getchan_noise(ah, chan);
-       }
-}
-
-/*
- * Updates the survey statistics and returns the busy time since last
- * update in %, if the measurement duration was long enough for the
- * result to be useful, -1 otherwise.
- */
-static int ath_update_survey_stats(struct ath_softc *sc)
-{
-       struct ath_hw *ah = sc->sc_ah;
-       struct ath_common *common = ath9k_hw_common(ah);
-       int pos = ah->curchan - &ah->channels[0];
-       struct survey_info *survey = &sc->survey[pos];
-       struct ath_cycle_counters *cc = &common->cc_survey;
-       unsigned int div = common->clockrate * 1000;
-       int ret = 0;
-
-       if (!ah->curchan)
-               return -1;
-
-       if (ah->power_mode == ATH9K_PM_AWAKE)
-               ath_hw_cycle_counters_update(common);
-
-       if (cc->cycles > 0) {
-               survey->filled |= SURVEY_INFO_CHANNEL_TIME |
-                       SURVEY_INFO_CHANNEL_TIME_BUSY |
-                       SURVEY_INFO_CHANNEL_TIME_RX |
-                       SURVEY_INFO_CHANNEL_TIME_TX;
-               survey->channel_time += cc->cycles / div;
-               survey->channel_time_busy += cc->rx_busy / div;
-               survey->channel_time_rx += cc->rx_frame / div;
-               survey->channel_time_tx += cc->tx_frame / div;
-       }
-
-       if (cc->cycles < div)
-               return -1;
-
-       if (cc->cycles > 0)
-               ret = cc->rx_busy * 100 / cc->cycles;
-
-       memset(cc, 0, sizeof(*cc));
-
-       ath_update_survey_nf(sc, pos);
-
-       return ret;
-}
-
 static void __ath_cancel_work(struct ath_softc *sc)
 {
        cancel_work_sync(&sc->paprd_work);
@@ -235,6 +158,22 @@ static void ath_cancel_work(struct ath_softc *sc)
        cancel_work_sync(&sc->hw_reset_work);
 }
 
+static void ath_restart_work(struct ath_softc *sc)
+{
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+       ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
+
+       if (AR_SREV_9485(sc->sc_ah) || AR_SREV_9340(sc->sc_ah))
+               ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work,
+                                    msecs_to_jiffies(ATH_PLL_WORK_INTERVAL));
+
+       ath_start_rx_poll(sc, 3);
+
+       if (!common->disable_ani)
+               ath_start_ani(common);
+}
+
 static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush)
 {
        struct ath_hw *ah = sc->sc_ah;
@@ -271,6 +210,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
+       unsigned long flags;
 
        if (ath_startrecv(sc) != 0) {
                ath_err(common, "Unable to restart recv logic\n");
@@ -279,36 +219,30 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
 
        ath9k_cmn_update_txpow(ah, sc->curtxpow,
                               sc->config.txpowlimit, &sc->curtxpow);
+
+       clear_bit(SC_OP_HW_RESET, &sc->sc_flags);
        ath9k_hw_set_interrupts(ah);
        ath9k_hw_enable_interrupts(ah);
 
-       if (!(sc->sc_flags & (SC_OP_OFFCHANNEL)) && start) {
-               if (sc->sc_flags & SC_OP_BEACONS)
-                       ath_set_beacon(sc);
+       if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) && start) {
+               if (!test_bit(SC_OP_BEACONS, &sc->sc_flags))
+                       goto work;
 
-               ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
-               ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2);
-               ath_start_rx_poll(sc, 3);
-               if (!common->disable_ani)
-                       ath_start_ani(common);
-       }
-
-       if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx != 3) {
-               struct ath_hw_antcomb_conf div_ant_conf;
-               u8 lna_conf;
-
-               ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf);
+               ath_set_beacon(sc);
 
-               if (sc->ant_rx == 1)
-                       lna_conf = ATH_ANT_DIV_COMB_LNA1;
-               else
-                       lna_conf = ATH_ANT_DIV_COMB_LNA2;
-               div_ant_conf.main_lna_conf = lna_conf;
-               div_ant_conf.alt_lna_conf = lna_conf;
-
-               ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf);
+               if (ah->opmode == NL80211_IFTYPE_STATION &&
+                   test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) {
+                       spin_lock_irqsave(&sc->sc_pm_lock, flags);
+                       sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
+                       spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
+               }
+       work:
+               ath_restart_work(sc);
        }
 
+       if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx != 3)
+               ath_ant_comb_update(sc);
+
        ieee80211_wake_queues(sc->hw);
 
        return true;
@@ -328,7 +262,7 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan,
 
        spin_lock_bh(&sc->sc_pcu_lock);
 
-       if (!(sc->sc_flags & SC_OP_OFFCHANNEL)) {
+       if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) {
                fastcc = false;
                caldata = &sc->caldata;
        }
@@ -371,7 +305,7 @@ static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
 {
        int r;
 
-       if (sc->sc_flags & SC_OP_INVALID)
+       if (test_bit(SC_OP_INVALID, &sc->sc_flags))
                return -EIO;
 
        r = ath_reset_internal(sc, hchan, false);
@@ -379,258 +313,6 @@ static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
        return r;
 }
 
-static void ath_paprd_activate(struct ath_softc *sc)
-{
-       struct ath_hw *ah = sc->sc_ah;
-       struct ath9k_hw_cal_data *caldata = ah->caldata;
-       int chain;
-
-       if (!caldata || !caldata->paprd_done)
-               return;
-
-       ath9k_ps_wakeup(sc);
-       ar9003_paprd_enable(ah, false);
-       for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
-               if (!(ah->txchainmask & BIT(chain)))
-                       continue;
-
-               ar9003_paprd_populate_single_table(ah, caldata, chain);
-       }
-
-       ar9003_paprd_enable(ah, true);
-       ath9k_ps_restore(sc);
-}
-
-static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int chain)
-{
-       struct ieee80211_hw *hw = sc->hw;
-       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
-       struct ath_hw *ah = sc->sc_ah;
-       struct ath_common *common = ath9k_hw_common(ah);
-       struct ath_tx_control txctl;
-       int time_left;
-
-       memset(&txctl, 0, sizeof(txctl));
-       txctl.txq = sc->tx.txq_map[WME_AC_BE];
-
-       memset(tx_info, 0, sizeof(*tx_info));
-       tx_info->band = hw->conf.channel->band;
-       tx_info->flags |= IEEE80211_TX_CTL_NO_ACK;
-       tx_info->control.rates[0].idx = 0;
-       tx_info->control.rates[0].count = 1;
-       tx_info->control.rates[0].flags = IEEE80211_TX_RC_MCS;
-       tx_info->control.rates[1].idx = -1;
-
-       init_completion(&sc->paprd_complete);
-       txctl.paprd = BIT(chain);
-
-       if (ath_tx_start(hw, skb, &txctl) != 0) {
-               ath_dbg(common, CALIBRATE, "PAPRD TX failed\n");
-               dev_kfree_skb_any(skb);
-               return false;
-       }
-
-       time_left = wait_for_completion_timeout(&sc->paprd_complete,
-                       msecs_to_jiffies(ATH_PAPRD_TIMEOUT));
-
-       if (!time_left)
-               ath_dbg(common, CALIBRATE,
-                       "Timeout waiting for paprd training on TX chain %d\n",
-                       chain);
-
-       return !!time_left;
-}
-
-void ath_paprd_calibrate(struct work_struct *work)
-{
-       struct ath_softc *sc = container_of(work, struct ath_softc, paprd_work);
-       struct ieee80211_hw *hw = sc->hw;
-       struct ath_hw *ah = sc->sc_ah;
-       struct ieee80211_hdr *hdr;
-       struct sk_buff *skb = NULL;
-       struct ath9k_hw_cal_data *caldata = ah->caldata;
-       struct ath_common *common = ath9k_hw_common(ah);
-       int ftype;
-       int chain_ok = 0;
-       int chain;
-       int len = 1800;
-
-       if (!caldata)
-               return;
-
-       ath9k_ps_wakeup(sc);
-
-       if (ar9003_paprd_init_table(ah) < 0)
-               goto fail_paprd;
-
-       skb = alloc_skb(len, GFP_KERNEL);
-       if (!skb)
-               goto fail_paprd;
-
-       skb_put(skb, len);
-       memset(skb->data, 0, len);
-       hdr = (struct ieee80211_hdr *)skb->data;
-       ftype = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC;
-       hdr->frame_control = cpu_to_le16(ftype);
-       hdr->duration_id = cpu_to_le16(10);
-       memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN);
-       memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN);
-       memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN);
-
-       for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
-               if (!(ah->txchainmask & BIT(chain)))
-                       continue;
-
-               chain_ok = 0;
-
-               ath_dbg(common, CALIBRATE,
-                       "Sending PAPRD frame for thermal measurement on chain %d\n",
-                       chain);
-               if (!ath_paprd_send_frame(sc, skb, chain))
-                       goto fail_paprd;
-
-               ar9003_paprd_setup_gain_table(ah, chain);
-
-               ath_dbg(common, CALIBRATE,
-                       "Sending PAPRD training frame on chain %d\n", chain);
-               if (!ath_paprd_send_frame(sc, skb, chain))
-                       goto fail_paprd;
-
-               if (!ar9003_paprd_is_done(ah)) {
-                       ath_dbg(common, CALIBRATE,
-                               "PAPRD not yet done on chain %d\n", chain);
-                       break;
-               }
-
-               if (ar9003_paprd_create_curve(ah, caldata, chain)) {
-                       ath_dbg(common, CALIBRATE,
-                               "PAPRD create curve failed on chain %d\n",
-                                                                  chain);
-                       break;
-               }
-
-               chain_ok = 1;
-       }
-       kfree_skb(skb);
-
-       if (chain_ok) {
-               caldata->paprd_done = true;
-               ath_paprd_activate(sc);
-       }
-
-fail_paprd:
-       ath9k_ps_restore(sc);
-}
-
-/*
- *  This routine performs the periodic noise floor calibration function
- *  that is used to adjust and optimize the chip performance.  This
- *  takes environmental changes (location, temperature) into account.
- *  When the task is complete, it reschedules itself depending on the
- *  appropriate interval that was calculated.
- */
-void ath_ani_calibrate(unsigned long data)
-{
-       struct ath_softc *sc = (struct ath_softc *)data;
-       struct ath_hw *ah = sc->sc_ah;
-       struct ath_common *common = ath9k_hw_common(ah);
-       bool longcal = false;
-       bool shortcal = false;
-       bool aniflag = false;
-       unsigned int timestamp = jiffies_to_msecs(jiffies);
-       u32 cal_interval, short_cal_interval, long_cal_interval;
-       unsigned long flags;
-
-       if (ah->caldata && ah->caldata->nfcal_interference)
-               long_cal_interval = ATH_LONG_CALINTERVAL_INT;
-       else
-               long_cal_interval = ATH_LONG_CALINTERVAL;
-
-       short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ?
-               ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL;
-
-       /* Only calibrate if awake */
-       if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE)
-               goto set_timer;
-
-       ath9k_ps_wakeup(sc);
-
-       /* Long calibration runs independently of short calibration. */
-       if ((timestamp - common->ani.longcal_timer) >= long_cal_interval) {
-               longcal = true;
-               common->ani.longcal_timer = timestamp;
-       }
-
-       /* Short calibration applies only while caldone is false */
-       if (!common->ani.caldone) {
-               if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) {
-                       shortcal = true;
-                       common->ani.shortcal_timer = timestamp;
-                       common->ani.resetcal_timer = timestamp;
-               }
-       } else {
-               if ((timestamp - common->ani.resetcal_timer) >=
-                   ATH_RESTART_CALINTERVAL) {
-                       common->ani.caldone = ath9k_hw_reset_calvalid(ah);
-                       if (common->ani.caldone)
-                               common->ani.resetcal_timer = timestamp;
-               }
-       }
-
-       /* Verify whether we must check ANI */
-       if (sc->sc_ah->config.enable_ani
-           && (timestamp - common->ani.checkani_timer) >=
-           ah->config.ani_poll_interval) {
-               aniflag = true;
-               common->ani.checkani_timer = timestamp;
-       }
-
-       /* Call ANI routine if necessary */
-       if (aniflag) {
-               spin_lock_irqsave(&common->cc_lock, flags);
-               ath9k_hw_ani_monitor(ah, ah->curchan);
-               ath_update_survey_stats(sc);
-               spin_unlock_irqrestore(&common->cc_lock, flags);
-       }
-
-       /* Perform calibration if necessary */
-       if (longcal || shortcal) {
-               common->ani.caldone =
-                       ath9k_hw_calibrate(ah, ah->curchan,
-                                               ah->rxchainmask, longcal);
-       }
-
-       ath_dbg(common, ANI,
-               "Calibration @%lu finished: %s %s %s, caldone: %s\n",
-               jiffies,
-               longcal ? "long" : "", shortcal ? "short" : "",
-               aniflag ? "ani" : "", common->ani.caldone ? "true" : "false");
-
-       ath9k_ps_restore(sc);
-
-set_timer:
-       /*
-       * Set timer interval based on previous results.
-       * The interval must be the shortest necessary to satisfy ANI,
-       * short calibration and long calibration.
-       */
-       ath9k_debug_samp_bb_mac(sc);
-       cal_interval = ATH_LONG_CALINTERVAL;
-       if (sc->sc_ah->config.enable_ani)
-               cal_interval = min(cal_interval,
-                                  (u32)ah->config.ani_poll_interval);
-       if (!common->ani.caldone)
-               cal_interval = min(cal_interval, (u32)short_cal_interval);
-
-       mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval));
-       if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_PAPRD) && ah->caldata) {
-               if (!ah->caldata->paprd_done)
-                       ieee80211_queue_work(sc->hw, &sc->paprd_work);
-               else if (!ah->paprd_table_write_done)
-                       ath_paprd_activate(sc);
-       }
-}
-
 static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta,
                            struct ieee80211_vif *vif)
 {
@@ -668,13 +350,12 @@ static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta)
                ath_tx_node_cleanup(sc, an);
 }
 
-
 void ath9k_tasklet(unsigned long data)
 {
        struct ath_softc *sc = (struct ath_softc *)data;
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
-
+       unsigned long flags;
        u32 status = sc->intrstatus;
        u32 rxmask;
 
@@ -693,10 +374,12 @@ void ath9k_tasklet(unsigned long data)
 
                RESET_STAT_INC(sc, type);
 #endif
+               set_bit(SC_OP_HW_RESET, &sc->sc_flags);
                ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
                goto out;
        }
 
+       spin_lock_irqsave(&sc->sc_pm_lock, flags);
        if ((status & ATH9K_INT_TSFOOR) && sc->ps_enabled) {
                /*
                 * TSF sync does not look correct; remain awake to sync with
@@ -705,6 +388,7 @@ void ath9k_tasklet(unsigned long data)
                ath_dbg(common, PS, "TSFOOR - Sync with next Beacon\n");
                sc->ps_flags |= PS_WAIT_FOR_BEACON | PS_BEACON_SYNC;
        }
+       spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
 
        if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
                rxmask = (ATH9K_INT_RXHP | ATH9K_INT_RXLP | ATH9K_INT_RXEOL |
@@ -766,15 +450,17 @@ irqreturn_t ath_isr(int irq, void *dev)
         * touch anything. Note this can happen early
         * on if the IRQ is shared.
         */
-       if (sc->sc_flags & SC_OP_INVALID)
+       if (test_bit(SC_OP_INVALID, &sc->sc_flags))
                return IRQ_NONE;
 
-
        /* shared irq, not for us */
 
        if (!ath9k_hw_intrpend(ah))
                return IRQ_NONE;
 
+       if(test_bit(SC_OP_HW_RESET, &sc->sc_flags))
+               return IRQ_HANDLED;
+
        /*
         * Figure out the reason(s) for the interrupt.  Note
         * that the hal returns a pseudo-ISR that may include
@@ -852,8 +538,10 @@ irqreturn_t ath_isr(int irq, void *dev)
                        /* Clear RxAbort bit so that we can
                         * receive frames */
                        ath9k_setpower(sc, ATH9K_PM_AWAKE);
+                       spin_lock(&sc->sc_pm_lock);
                        ath9k_hw_setrxabort(sc->sc_ah, 0);
                        sc->ps_flags |= PS_WAIT_FOR_BEACON;
+                       spin_unlock(&sc->sc_pm_lock);
                }
 
 chip_reset:
@@ -902,87 +590,6 @@ void ath_reset_work(struct work_struct *work)
        ath_reset(sc, true);
 }
 
-void ath_hw_check(struct work_struct *work)
-{
-       struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work);
-       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       unsigned long flags;
-       int busy;
-       u8 is_alive, nbeacon = 1;
-
-       ath9k_ps_wakeup(sc);
-       is_alive = ath9k_hw_check_alive(sc->sc_ah);
-
-       if (is_alive && !AR_SREV_9300(sc->sc_ah))
-               goto out;
-       else if (!is_alive && AR_SREV_9300(sc->sc_ah)) {
-               ath_dbg(common, RESET,
-                       "DCU stuck is detected. Schedule chip reset\n");
-               RESET_STAT_INC(sc, RESET_TYPE_MAC_HANG);
-               goto sched_reset;
-       }
-
-       spin_lock_irqsave(&common->cc_lock, flags);
-       busy = ath_update_survey_stats(sc);
-       spin_unlock_irqrestore(&common->cc_lock, flags);
-
-       ath_dbg(common, RESET, "Possible baseband hang, busy=%d (try %d)\n",
-               busy, sc->hw_busy_count + 1);
-       if (busy >= 99) {
-               if (++sc->hw_busy_count >= 3) {
-                       RESET_STAT_INC(sc, RESET_TYPE_BB_HANG);
-                       goto sched_reset;
-               }
-       } else if (busy >= 0) {
-               sc->hw_busy_count = 0;
-               nbeacon = 3;
-       }
-
-       ath_start_rx_poll(sc, nbeacon);
-       goto out;
-
-sched_reset:
-       ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
-out:
-       ath9k_ps_restore(sc);
-}
-
-static void ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum)
-{
-       static int count;
-       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-
-       if (pll_sqsum >= 0x40000) {
-               count++;
-               if (count == 3) {
-                       /* Rx is hung for more than 500ms. Reset it */
-                       ath_dbg(common, RESET, "Possible RX hang, resetting\n");
-                       RESET_STAT_INC(sc, RESET_TYPE_PLL_HANG);
-                       ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
-                       count = 0;
-               }
-       } else
-               count = 0;
-}
-
-void ath_hw_pll_work(struct work_struct *work)
-{
-       struct ath_softc *sc = container_of(work, struct ath_softc,
-                                           hw_pll_work.work);
-       u32 pll_sqsum;
-
-       if (AR_SREV_9485(sc->sc_ah)) {
-
-               ath9k_ps_wakeup(sc);
-               pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah);
-               ath9k_ps_restore(sc);
-
-               ath_hw_pll_rx_hang_check(sc, pll_sqsum);
-
-               ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/5);
-       }
-}
-
 /**********************/
 /* mac80211 callbacks */
 /**********************/
@@ -1045,10 +652,9 @@ static int ath9k_start(struct ieee80211_hw *hw)
        if (ah->caps.hw_caps & ATH9K_HW_CAP_HT)
                ah->imask |= ATH9K_INT_CST;
 
-       if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI)
-               ah->imask |= ATH9K_INT_MCI;
+       ath_mci_enable(sc);
 
-       sc->sc_flags &= ~SC_OP_INVALID;
+       clear_bit(SC_OP_INVALID, &sc->sc_flags);
        sc->sc_ah->is_monitoring = false;
 
        if (!ath_complete_reset(sc, false)) {
@@ -1090,6 +696,7 @@ static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ath_tx_control txctl;
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       unsigned long flags;
 
        if (sc->ps_enabled) {
                /*
@@ -1112,6 +719,7 @@ static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
                 * completed and if needed, also for RX of buffered frames.
                 */
                ath9k_ps_wakeup(sc);
+               spin_lock_irqsave(&sc->sc_pm_lock, flags);
                if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
                        ath9k_hw_setrxabort(sc->sc_ah, 0);
                if (ieee80211_is_pspoll(hdr->frame_control)) {
@@ -1127,6 +735,7 @@ static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
                 * the ps_flags bit is cleared. We are just dropping
                 * the ps_usecount here.
                 */
+               spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
                ath9k_ps_restore(sc);
        }
 
@@ -1167,7 +776,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
        ath_cancel_work(sc);
        del_timer_sync(&sc->rx_poll_timer);
 
-       if (sc->sc_flags & SC_OP_INVALID) {
+       if (test_bit(SC_OP_INVALID, &sc->sc_flags)) {
                ath_dbg(common, ANY, "Device not present\n");
                mutex_unlock(&sc->mutex);
                return;
@@ -1224,7 +833,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
 
        ath9k_ps_restore(sc);
 
-       sc->sc_flags |= SC_OP_INVALID;
+       set_bit(SC_OP_INVALID, &sc->sc_flags);
        sc->ps_idle = prev_idle;
 
        mutex_unlock(&sc->mutex);
@@ -1328,11 +937,11 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw,
        /* Set op-mode & TSF */
        if (iter_data.naps > 0) {
                ath9k_hw_set_tsfadjust(ah, 1);
-               sc->sc_flags |= SC_OP_TSF_RESET;
+               set_bit(SC_OP_TSF_RESET, &sc->sc_flags);
                ah->opmode = NL80211_IFTYPE_AP;
        } else {
                ath9k_hw_set_tsfadjust(ah, 0);
-               sc->sc_flags &= ~SC_OP_TSF_RESET;
+               clear_bit(SC_OP_TSF_RESET, &sc->sc_flags);
 
                if (iter_data.nmeshes)
                        ah->opmode = NL80211_IFTYPE_MESH_POINT;
@@ -1363,12 +972,12 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw,
                sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
 
                if (!common->disable_ani) {
-                       sc->sc_flags |= SC_OP_ANI_RUN;
+                       set_bit(SC_OP_ANI_RUN, &sc->sc_flags);
                        ath_start_ani(common);
                }
 
        } else {
-               sc->sc_flags &= ~SC_OP_ANI_RUN;
+               clear_bit(SC_OP_ANI_RUN, &sc->sc_flags);
                del_timer_sync(&common->ani.timer);
        }
 }
@@ -1389,25 +998,6 @@ static void ath9k_do_vif_add_setup(struct ieee80211_hw *hw,
        }
 }
 
-void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon)
-{
-       if (!AR_SREV_9300(sc->sc_ah))
-               return;
-
-       if (!(sc->sc_flags & SC_OP_PRIM_STA_VIF))
-               return;
-
-       mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies
-                       (nbeacon * sc->cur_beacon_conf.beacon_interval));
-}
-
-void ath_rx_poll(unsigned long data)
-{
-       struct ath_softc *sc = (struct ath_softc *)data;
-
-       ieee80211_queue_work(sc->hw, &sc->hw_check_work);
-}
-
 static int ath9k_add_interface(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif)
 {
@@ -1627,11 +1217,6 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
                if (ah->curchan)
                        old_pos = ah->curchan - &ah->channels[0];
 
-               if (hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)
-                       sc->sc_flags |= SC_OP_OFFCHANNEL;
-               else
-                       sc->sc_flags &= ~SC_OP_OFFCHANNEL;
-
                ath_dbg(common, CONFIG, "Set channel: %d MHz type: %d\n",
                        curchan->center_freq, conf->channel_type);
 
@@ -1911,16 +1496,16 @@ static void ath9k_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
        struct ath_vif *avp = (void *)vif->drv_priv;
-
+       unsigned long flags;
        /*
         * Skip iteration if primary station vif's bss info
         * was not changed
         */
-       if (sc->sc_flags & SC_OP_PRIM_STA_VIF)
+       if (test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags))
                return;
 
        if (bss_conf->assoc) {
-               sc->sc_flags |= SC_OP_PRIM_STA_VIF;
+               set_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags);
                avp->primary_sta_vif = true;
                memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
                common->curaid = bss_conf->aid;
@@ -1933,7 +1518,10 @@ static void ath9k_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
                 * on the receipt of the first Beacon frame (i.e.,
                 * after time sync with the AP).
                 */
+               spin_lock_irqsave(&sc->sc_pm_lock, flags);
                sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
+               spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
+
                /* Reset rssi stats */
                sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
                sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
@@ -1941,7 +1529,7 @@ static void ath9k_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
                ath_start_rx_poll(sc, 3);
 
                if (!common->disable_ani) {
-                       sc->sc_flags |= SC_OP_ANI_RUN;
+                       set_bit(SC_OP_ANI_RUN, &sc->sc_flags);
                        ath_start_ani(common);
                }
 
@@ -1961,7 +1549,8 @@ static void ath9k_config_bss(struct ath_softc *sc, struct ieee80211_vif *vif)
        if (avp->primary_sta_vif && !bss_conf->assoc) {
                ath_dbg(common, CONFIG, "Bss Info DISASSOC %d, bssid %pM\n",
                        common->curaid, common->curbssid);
-               sc->sc_flags &= ~(SC_OP_PRIM_STA_VIF | SC_OP_BEACONS);
+               clear_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags);
+               clear_bit(SC_OP_BEACONS, &sc->sc_flags);
                avp->primary_sta_vif = false;
                memset(common->curbssid, 0, ETH_ALEN);
                common->curaid = 0;
@@ -1974,10 +1563,9 @@ static void ath9k_config_bss(struct ath_softc *sc, struct ieee80211_vif *vif)
         * None of station vifs are associated.
         * Clear bssid & aid
         */
-       if (!(sc->sc_flags & SC_OP_PRIM_STA_VIF)) {
+       if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) {
                ath9k_hw_write_associd(sc->sc_ah);
-               /* Stop ANI */
-               sc->sc_flags &= ~SC_OP_ANI_RUN;
+               clear_bit(SC_OP_ANI_RUN, &sc->sc_flags);
                del_timer_sync(&common->ani.timer);
                del_timer_sync(&sc->rx_poll_timer);
                memset(&sc->caldata, 0, sizeof(sc->caldata));
@@ -2015,12 +1603,12 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
                        sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
 
                        if (!common->disable_ani) {
-                               sc->sc_flags |= SC_OP_ANI_RUN;
+                               set_bit(SC_OP_ANI_RUN, &sc->sc_flags);
                                ath_start_ani(common);
                        }
 
                } else {
-                       sc->sc_flags &= ~SC_OP_ANI_RUN;
+                       clear_bit(SC_OP_ANI_RUN, &sc->sc_flags);
                        del_timer_sync(&common->ani.timer);
                        del_timer_sync(&sc->rx_poll_timer);
                }
@@ -2032,7 +1620,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
         */
        if ((changed & BSS_CHANGED_BEACON_INT) &&
            (vif->type == NL80211_IFTYPE_AP))
-               sc->sc_flags |= SC_OP_TSF_RESET;
+               set_bit(SC_OP_TSF_RESET, &sc->sc_flags);
 
        /* Configure beaconing (AP, IBSS, MESH) */
        if (ath9k_uses_beacons(vif->type) &&
@@ -2224,7 +1812,7 @@ static void ath9k_flush(struct ieee80211_hw *hw, bool drop)
                return;
        }
 
-       if (sc->sc_flags & SC_OP_INVALID) {
+       if (test_bit(SC_OP_INVALID, &sc->sc_flags)) {
                ath_dbg(common, ANY, "Device not present\n");
                mutex_unlock(&sc->mutex);
                return;
index 29fe52d..49137f4 100644 (file)
@@ -116,42 +116,58 @@ static void ath_mci_update_scheme(struct ath_softc *sc)
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ath_btcoex *btcoex = &sc->btcoex;
        struct ath_mci_profile *mci = &btcoex->mci;
+       struct ath9k_hw_mci *mci_hw = &sc->sc_ah->btcoex_hw.mci;
        struct ath_mci_profile_info *info;
        u32 num_profile = NUM_PROF(mci);
 
+       if (mci_hw->config & ATH_MCI_CONFIG_DISABLE_TUNING)
+               goto skip_tuning;
+
        if (num_profile == 1) {
                info = list_first_entry(&mci->info,
                                        struct ath_mci_profile_info,
                                        list);
-               if (mci->num_sco && info->T == 12) {
-                       mci->aggr_limit = 8;
+               if (mci->num_sco) {
+                       if (info->T == 12)
+                               mci->aggr_limit = 8;
+                       else if (info->T == 6) {
+                               mci->aggr_limit = 6;
+                               btcoex->duty_cycle = 30;
+                       }
                        ath_dbg(common, MCI,
-                               "Single SCO, aggregation limit 2 ms\n");
-               } else if ((info->type == MCI_GPM_COEX_PROFILE_BNEP) &&
-                          !info->master) {
-                       btcoex->btcoex_period = 60;
+                               "Single SCO, aggregation limit %d 1/4 ms\n",
+                               mci->aggr_limit);
+               } else if (mci->num_pan || mci->num_other_acl) {
+                       /*
+                        * For single PAN/FTP profile, allocate 35% for BT
+                        * to improve WLAN throughput.
+                        */
+                       btcoex->duty_cycle = 35;
+                       btcoex->btcoex_period = 53;
                        ath_dbg(common, MCI,
-                               "Single slave PAN/FTP, bt period 60 ms\n");
-               } else if ((info->type == MCI_GPM_COEX_PROFILE_HID) &&
-                        (info->T > 0 && info->T < 50) &&
-                        (info->A > 1 || info->W > 1)) {
+                               "Single PAN/FTP bt period %d ms dutycycle %d\n",
+                               btcoex->duty_cycle, btcoex->btcoex_period);
+               } else if (mci->num_hid) {
                        btcoex->duty_cycle = 30;
-                       mci->aggr_limit = 8;
+                       mci->aggr_limit = 6;
                        ath_dbg(common, MCI,
                                "Multiple attempt/timeout single HID "
-                               "aggregation limit 2 ms dutycycle 30%%\n");
+                               "aggregation limit 1.5 ms dutycycle 30%%\n");
                }
-       } else if ((num_profile == 2) && (mci->num_hid == 2)) {
-               btcoex->duty_cycle = 30;
-               mci->aggr_limit = 8;
-               ath_dbg(common, MCI,
-                       "Two HIDs aggregation limit 2 ms dutycycle 30%%\n");
-       } else if (num_profile > 3) {
+       } else if (num_profile == 2) {
+               if (mci->num_hid == 2)
+                       btcoex->duty_cycle = 30;
                mci->aggr_limit = 6;
                ath_dbg(common, MCI,
-                       "Three or more profiles aggregation limit 1.5 ms\n");
+                       "Two BT profiles aggr limit 1.5 ms dutycycle %d%%\n",
+                       btcoex->duty_cycle);
+       } else if (num_profile >= 3) {
+               mci->aggr_limit = 4;
+               ath_dbg(common, MCI,
+                       "Three or more profiles aggregation limit 1 ms\n");
        }
 
+skip_tuning:
        if (IS_CHAN_2GHZ(sc->sc_ah->curchan)) {
                if (IS_CHAN_HT(sc->sc_ah->curchan))
                        ath_mci_adjust_aggr_limit(btcoex);
@@ -538,3 +554,14 @@ void ath_mci_intr(struct ath_softc *sc)
                mci_int &= ~(AR_MCI_INTERRUPT_RX_INVALID_HDR |
                             AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT);
 }
+
+void ath_mci_enable(struct ath_softc *sc)
+{
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+       if (!common->btcoex_enabled)
+               return;
+
+       if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI)
+               sc->sc_ah->imask |= ATH9K_INT_MCI;
+}
index c841444..fc14eea 100644 (file)
@@ -130,4 +130,13 @@ void ath_mci_flush_profile(struct ath_mci_profile *mci);
 int ath_mci_setup(struct ath_softc *sc);
 void ath_mci_cleanup(struct ath_softc *sc);
 void ath_mci_intr(struct ath_softc *sc);
-#endif
+
+#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+void ath_mci_enable(struct ath_softc *sc);
+#else
+static inline void ath_mci_enable(struct ath_softc *sc)
+{
+}
+#endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */
+
+#endif /* MCI_H*/
index a856b51..aa0e83a 100644 (file)
@@ -115,6 +115,9 @@ static void ath_pci_aspm_init(struct ath_common *common)
        int pos;
        u8 aspm;
 
+       if (!ah->is_pciexpress)
+               return;
+
        pos = pci_pcie_cap(pdev);
        if (!pos)
                return;
@@ -138,6 +141,7 @@ static void ath_pci_aspm_init(struct ath_common *common)
                aspm &= ~(PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
                pci_write_config_byte(parent, pos + PCI_EXP_LNKCTL, aspm);
 
+               ath_info(common, "Disabling ASPM since BTCOEX is enabled\n");
                return;
        }
 
@@ -147,6 +151,7 @@ static void ath_pci_aspm_init(struct ath_common *common)
                ah->aspm_enabled = true;
                /* Initialize PCIe PM and SERDES registers. */
                ath9k_hw_configpcipowersave(ah, false);
+               ath_info(common, "ASPM enabled: 0x%x\n", aspm);
        }
 }
 
@@ -246,7 +251,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        sc->mem = mem;
 
        /* Will be cleared in ath9k_start() */
-       sc->sc_flags |= SC_OP_INVALID;
+       set_bit(SC_OP_INVALID, &sc->sc_flags);
 
        ret = request_irq(pdev->irq, ath_isr, IRQF_SHARED, "ath9k", sc);
        if (ret) {
index e1fcc68..fbdcc80 100644 (file)
 
 #define SKB_CB_ATHBUF(__skb)   (*((struct ath_buf **)__skb->cb))
 
-static inline bool ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta,
-                                              int mindelta, int main_rssi_avg,
-                                              int alt_rssi_avg, int pkt_count)
-{
-       return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
-               (alt_rssi_avg > main_rssi_avg + maxdelta)) ||
-               (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
-}
-
-static inline bool ath_ant_div_comb_alt_check(u8 div_group, int alt_ratio,
-                                       int curr_main_set, int curr_alt_set,
-                                       int alt_rssi_avg, int main_rssi_avg)
-{
-       bool result = false;
-       switch (div_group) {
-       case 0:
-               if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
-                       result = true;
-               break;
-       case 1:
-       case 2:
-               if ((((curr_main_set == ATH_ANT_DIV_COMB_LNA2) &&
-                       (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) &&
-                               (alt_rssi_avg >= (main_rssi_avg - 5))) ||
-                       ((curr_main_set == ATH_ANT_DIV_COMB_LNA1) &&
-                       (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) &&
-                               (alt_rssi_avg >= (main_rssi_avg - 2)))) &&
-                                                       (alt_rssi_avg >= 4))
-                       result = true;
-               else
-                       result = false;
-               break;
-       }
-
-       return result;
-}
-
 static inline bool ath9k_check_auto_sleep(struct ath_softc *sc)
 {
        return sc->ps_enabled &&
@@ -303,7 +266,7 @@ static void ath_edma_start_recv(struct ath_softc *sc)
 
        ath_opmode_init(sc);
 
-       ath9k_hw_startpcureceive(sc->sc_ah, (sc->sc_flags & SC_OP_OFFCHANNEL));
+       ath9k_hw_startpcureceive(sc->sc_ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL));
 
        spin_unlock_bh(&sc->rx.rxbuflock);
 }
@@ -322,8 +285,8 @@ int ath_rx_init(struct ath_softc *sc, int nbufs)
        int error = 0;
 
        spin_lock_init(&sc->sc_pcu_lock);
-       sc->sc_flags &= ~SC_OP_RXFLUSH;
        spin_lock_init(&sc->rx.rxbuflock);
+       clear_bit(SC_OP_RXFLUSH, &sc->sc_flags);
 
        common->rx_bufsize = IEEE80211_MAX_MPDU_LEN / 2 +
                             sc->sc_ah->caps.rx_status_len;
@@ -500,7 +463,7 @@ int ath_startrecv(struct ath_softc *sc)
 
 start_recv:
        ath_opmode_init(sc);
-       ath9k_hw_startpcureceive(ah, (sc->sc_flags & SC_OP_OFFCHANNEL));
+       ath9k_hw_startpcureceive(ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL));
 
        spin_unlock_bh(&sc->rx.rxbuflock);
 
@@ -535,11 +498,11 @@ bool ath_stoprecv(struct ath_softc *sc)
 
 void ath_flushrecv(struct ath_softc *sc)
 {
-       sc->sc_flags |= SC_OP_RXFLUSH;
+       set_bit(SC_OP_RXFLUSH, &sc->sc_flags);
        if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
                ath_rx_tasklet(sc, 1, true);
        ath_rx_tasklet(sc, 1, false);
-       sc->sc_flags &= ~SC_OP_RXFLUSH;
+       clear_bit(SC_OP_RXFLUSH, &sc->sc_flags);
 }
 
 static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb)
@@ -624,13 +587,13 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb, bool mybeacon)
 
        /* Process Beacon and CAB receive in PS state */
        if (((sc->ps_flags & PS_WAIT_FOR_BEACON) || ath9k_check_auto_sleep(sc))
-           && mybeacon)
+           && mybeacon) {
                ath_rx_ps_beacon(sc, skb);
-       else if ((sc->ps_flags & PS_WAIT_FOR_CAB) &&
-                (ieee80211_is_data(hdr->frame_control) ||
-                 ieee80211_is_action(hdr->frame_control)) &&
-                is_multicast_ether_addr(hdr->addr1) &&
-                !ieee80211_has_moredata(hdr->frame_control)) {
+       else if ((sc->ps_flags & PS_WAIT_FOR_CAB) &&
+                  (ieee80211_is_data(hdr->frame_control) ||
+                   ieee80211_is_action(hdr->frame_control)) &&
+                  is_multicast_ether_addr(hdr->addr1) &&
+                  !ieee80211_has_moredata(hdr->frame_control)) {
                /*
                 * No more broadcast/multicast frames to be received at this
                 * point.
@@ -1067,709 +1030,6 @@ static void ath9k_rx_skb_postprocess(struct ath_common *common,
                rxs->flag &= ~RX_FLAG_DECRYPTED;
 }
 
-static void ath_lnaconf_alt_good_scan(struct ath_ant_comb *antcomb,
-                                     struct ath_hw_antcomb_conf ant_conf,
-                                     int main_rssi_avg)
-{
-       antcomb->quick_scan_cnt = 0;
-
-       if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
-               antcomb->rssi_lna2 = main_rssi_avg;
-       else if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA1)
-               antcomb->rssi_lna1 = main_rssi_avg;
-
-       switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) {
-       case 0x10: /* LNA2 A-B */
-               antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
-               antcomb->first_quick_scan_conf =
-                       ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-               antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
-               break;
-       case 0x20: /* LNA1 A-B */
-               antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
-               antcomb->first_quick_scan_conf =
-                       ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-               antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
-               break;
-       case 0x21: /* LNA1 LNA2 */
-               antcomb->main_conf = ATH_ANT_DIV_COMB_LNA2;
-               antcomb->first_quick_scan_conf =
-                       ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
-               antcomb->second_quick_scan_conf =
-                       ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-               break;
-       case 0x12: /* LNA2 LNA1 */
-               antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1;
-               antcomb->first_quick_scan_conf =
-                       ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
-               antcomb->second_quick_scan_conf =
-                       ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-               break;
-       case 0x13: /* LNA2 A+B */
-               antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-               antcomb->first_quick_scan_conf =
-                       ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
-               antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
-               break;
-       case 0x23: /* LNA1 A+B */
-               antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-               antcomb->first_quick_scan_conf =
-                       ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
-               antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
-               break;
-       default:
-               break;
-       }
-}
-
-static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
-                               struct ath_hw_antcomb_conf *div_ant_conf,
-                               int main_rssi_avg, int alt_rssi_avg,
-                               int alt_ratio)
-{
-       /* alt_good */
-       switch (antcomb->quick_scan_cnt) {
-       case 0:
-               /* set alt to main, and alt to first conf */
-               div_ant_conf->main_lna_conf = antcomb->main_conf;
-               div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
-               break;
-       case 1:
-               /* set alt to main, and alt to first conf */
-               div_ant_conf->main_lna_conf = antcomb->main_conf;
-               div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
-               antcomb->rssi_first = main_rssi_avg;
-               antcomb->rssi_second = alt_rssi_avg;
-
-               if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
-                       /* main is LNA1 */
-                       if (ath_is_alt_ant_ratio_better(alt_ratio,
-                                               ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
-                                               ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
-                                               main_rssi_avg, alt_rssi_avg,
-                                               antcomb->total_pkt_count))
-                               antcomb->first_ratio = true;
-                       else
-                               antcomb->first_ratio = false;
-               } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
-                       if (ath_is_alt_ant_ratio_better(alt_ratio,
-                                               ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
-                                               ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
-                                               main_rssi_avg, alt_rssi_avg,
-                                               antcomb->total_pkt_count))
-                               antcomb->first_ratio = true;
-                       else
-                               antcomb->first_ratio = false;
-               } else {
-                       if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
-                           (alt_rssi_avg > main_rssi_avg +
-                           ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
-                           (alt_rssi_avg > main_rssi_avg)) &&
-                           (antcomb->total_pkt_count > 50))
-                               antcomb->first_ratio = true;
-                       else
-                               antcomb->first_ratio = false;
-               }
-               break;
-       case 2:
-               antcomb->alt_good = false;
-               antcomb->scan_not_start = false;
-               antcomb->scan = false;
-               antcomb->rssi_first = main_rssi_avg;
-               antcomb->rssi_third = alt_rssi_avg;
-
-               if (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1)
-                       antcomb->rssi_lna1 = alt_rssi_avg;
-               else if (antcomb->second_quick_scan_conf ==
-                        ATH_ANT_DIV_COMB_LNA2)
-                       antcomb->rssi_lna2 = alt_rssi_avg;
-               else if (antcomb->second_quick_scan_conf ==
-                        ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
-                       if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)
-                               antcomb->rssi_lna2 = main_rssi_avg;
-                       else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1)
-                               antcomb->rssi_lna1 = main_rssi_avg;
-               }
-
-               if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
-                   ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
-                       div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
-               else
-                       div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
-
-               if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
-                       if (ath_is_alt_ant_ratio_better(alt_ratio,
-                                               ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
-                                               ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
-                                               main_rssi_avg, alt_rssi_avg,
-                                               antcomb->total_pkt_count))
-                               antcomb->second_ratio = true;
-                       else
-                               antcomb->second_ratio = false;
-               } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
-                       if (ath_is_alt_ant_ratio_better(alt_ratio,
-                                               ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
-                                               ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
-                                               main_rssi_avg, alt_rssi_avg,
-                                               antcomb->total_pkt_count))
-                               antcomb->second_ratio = true;
-                       else
-                               antcomb->second_ratio = false;
-               } else {
-                       if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
-                           (alt_rssi_avg > main_rssi_avg +
-                           ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
-                           (alt_rssi_avg > main_rssi_avg)) &&
-                           (antcomb->total_pkt_count > 50))
-                               antcomb->second_ratio = true;
-                       else
-                               antcomb->second_ratio = false;
-               }
-
-               /* set alt to the conf with maximun ratio */
-               if (antcomb->first_ratio && antcomb->second_ratio) {
-                       if (antcomb->rssi_second > antcomb->rssi_third) {
-                               /* first alt*/
-                               if ((antcomb->first_quick_scan_conf ==
-                                   ATH_ANT_DIV_COMB_LNA1) ||
-                                   (antcomb->first_quick_scan_conf ==
-                                   ATH_ANT_DIV_COMB_LNA2))
-                                       /* Set alt LNA1 or LNA2*/
-                                       if (div_ant_conf->main_lna_conf ==
-                                           ATH_ANT_DIV_COMB_LNA2)
-                                               div_ant_conf->alt_lna_conf =
-                                                       ATH_ANT_DIV_COMB_LNA1;
-                                       else
-                                               div_ant_conf->alt_lna_conf =
-                                                       ATH_ANT_DIV_COMB_LNA2;
-                               else
-                                       /* Set alt to A+B or A-B */
-                                       div_ant_conf->alt_lna_conf =
-                                               antcomb->first_quick_scan_conf;
-                       } else if ((antcomb->second_quick_scan_conf ==
-                                  ATH_ANT_DIV_COMB_LNA1) ||
-                                  (antcomb->second_quick_scan_conf ==
-                                  ATH_ANT_DIV_COMB_LNA2)) {
-                               /* Set alt LNA1 or LNA2 */
-                               if (div_ant_conf->main_lna_conf ==
-                                   ATH_ANT_DIV_COMB_LNA2)
-                                       div_ant_conf->alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1;
-                               else
-                                       div_ant_conf->alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA2;
-                       } else {
-                               /* Set alt to A+B or A-B */
-                               div_ant_conf->alt_lna_conf =
-                                       antcomb->second_quick_scan_conf;
-                       }
-               } else if (antcomb->first_ratio) {
-                       /* first alt */
-                       if ((antcomb->first_quick_scan_conf ==
-                           ATH_ANT_DIV_COMB_LNA1) ||
-                           (antcomb->first_quick_scan_conf ==
-                           ATH_ANT_DIV_COMB_LNA2))
-                                       /* Set alt LNA1 or LNA2 */
-                               if (div_ant_conf->main_lna_conf ==
-                                   ATH_ANT_DIV_COMB_LNA2)
-                                       div_ant_conf->alt_lna_conf =
-                                                       ATH_ANT_DIV_COMB_LNA1;
-                               else
-                                       div_ant_conf->alt_lna_conf =
-                                                       ATH_ANT_DIV_COMB_LNA2;
-                       else
-                               /* Set alt to A+B or A-B */
-                               div_ant_conf->alt_lna_conf =
-                                               antcomb->first_quick_scan_conf;
-               } else if (antcomb->second_ratio) {
-                               /* second alt */
-                       if ((antcomb->second_quick_scan_conf ==
-                           ATH_ANT_DIV_COMB_LNA1) ||
-                           (antcomb->second_quick_scan_conf ==
-                           ATH_ANT_DIV_COMB_LNA2))
-                               /* Set alt LNA1 or LNA2 */
-                               if (div_ant_conf->main_lna_conf ==
-                                   ATH_ANT_DIV_COMB_LNA2)
-                                       div_ant_conf->alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1;
-                               else
-                                       div_ant_conf->alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA2;
-                       else
-                               /* Set alt to A+B or A-B */
-                               div_ant_conf->alt_lna_conf =
-                                               antcomb->second_quick_scan_conf;
-               } else {
-                       /* main is largest */
-                       if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) ||
-                           (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2))
-                               /* Set alt LNA1 or LNA2 */
-                               if (div_ant_conf->main_lna_conf ==
-                                   ATH_ANT_DIV_COMB_LNA2)
-                                       div_ant_conf->alt_lna_conf =
-                                                       ATH_ANT_DIV_COMB_LNA1;
-                               else
-                                       div_ant_conf->alt_lna_conf =
-                                                       ATH_ANT_DIV_COMB_LNA2;
-                       else
-                               /* Set alt to A+B or A-B */
-                               div_ant_conf->alt_lna_conf = antcomb->main_conf;
-               }
-               break;
-       default:
-               break;
-       }
-}
-
-static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
-               struct ath_ant_comb *antcomb, int alt_ratio)
-{
-       if (ant_conf->div_group == 0) {
-               /* Adjust the fast_div_bias based on main and alt lna conf */
-               switch ((ant_conf->main_lna_conf << 4) |
-                               ant_conf->alt_lna_conf) {
-               case 0x01: /* A-B LNA2 */
-                       ant_conf->fast_div_bias = 0x3b;
-                       break;
-               case 0x02: /* A-B LNA1 */
-                       ant_conf->fast_div_bias = 0x3d;
-                       break;
-               case 0x03: /* A-B A+B */
-                       ant_conf->fast_div_bias = 0x1;
-                       break;
-               case 0x10: /* LNA2 A-B */
-                       ant_conf->fast_div_bias = 0x7;
-                       break;
-               case 0x12: /* LNA2 LNA1 */
-                       ant_conf->fast_div_bias = 0x2;
-                       break;
-               case 0x13: /* LNA2 A+B */
-                       ant_conf->fast_div_bias = 0x7;
-                       break;
-               case 0x20: /* LNA1 A-B */
-                       ant_conf->fast_div_bias = 0x6;
-                       break;
-               case 0x21: /* LNA1 LNA2 */
-                       ant_conf->fast_div_bias = 0x0;
-                       break;
-               case 0x23: /* LNA1 A+B */
-                       ant_conf->fast_div_bias = 0x6;
-                       break;
-               case 0x30: /* A+B A-B */
-                       ant_conf->fast_div_bias = 0x1;
-                       break;
-               case 0x31: /* A+B LNA2 */
-                       ant_conf->fast_div_bias = 0x3b;
-                       break;
-               case 0x32: /* A+B LNA1 */
-                       ant_conf->fast_div_bias = 0x3d;
-                       break;
-               default:
-                       break;
-               }
-       } else if (ant_conf->div_group == 1) {
-               /* Adjust the fast_div_bias based on main and alt_lna_conf */
-               switch ((ant_conf->main_lna_conf << 4) |
-                       ant_conf->alt_lna_conf) {
-               case 0x01: /* A-B LNA2 */
-                       ant_conf->fast_div_bias = 0x1;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x02: /* A-B LNA1 */
-                       ant_conf->fast_div_bias = 0x1;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x03: /* A-B A+B */
-                       ant_conf->fast_div_bias = 0x1;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x10: /* LNA2 A-B */
-                       if (!(antcomb->scan) &&
-                           (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
-                               ant_conf->fast_div_bias = 0x3f;
-                       else
-                               ant_conf->fast_div_bias = 0x1;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x12: /* LNA2 LNA1 */
-                       ant_conf->fast_div_bias = 0x1;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x13: /* LNA2 A+B */
-                       if (!(antcomb->scan) &&
-                           (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
-                               ant_conf->fast_div_bias = 0x3f;
-                       else
-                               ant_conf->fast_div_bias = 0x1;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x20: /* LNA1 A-B */
-                       if (!(antcomb->scan) &&
-                           (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
-                               ant_conf->fast_div_bias = 0x3f;
-                       else
-                               ant_conf->fast_div_bias = 0x1;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x21: /* LNA1 LNA2 */
-                       ant_conf->fast_div_bias = 0x1;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x23: /* LNA1 A+B */
-                       if (!(antcomb->scan) &&
-                           (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
-                               ant_conf->fast_div_bias = 0x3f;
-                       else
-                               ant_conf->fast_div_bias = 0x1;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x30: /* A+B A-B */
-                       ant_conf->fast_div_bias = 0x1;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x31: /* A+B LNA2 */
-                       ant_conf->fast_div_bias = 0x1;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x32: /* A+B LNA1 */
-                       ant_conf->fast_div_bias = 0x1;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               default:
-                       break;
-               }
-       } else if (ant_conf->div_group == 2) {
-               /* Adjust the fast_div_bias based on main and alt_lna_conf */
-               switch ((ant_conf->main_lna_conf << 4) |
-                               ant_conf->alt_lna_conf) {
-               case 0x01: /* A-B LNA2 */
-                       ant_conf->fast_div_bias = 0x1;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x02: /* A-B LNA1 */
-                       ant_conf->fast_div_bias = 0x1;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x03: /* A-B A+B */
-                       ant_conf->fast_div_bias = 0x1;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x10: /* LNA2 A-B */
-                       if (!(antcomb->scan) &&
-                               (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
-                               ant_conf->fast_div_bias = 0x1;
-                       else
-                               ant_conf->fast_div_bias = 0x2;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x12: /* LNA2 LNA1 */
-                       ant_conf->fast_div_bias = 0x1;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x13: /* LNA2 A+B */
-                       if (!(antcomb->scan) &&
-                               (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
-                               ant_conf->fast_div_bias = 0x1;
-                       else
-                               ant_conf->fast_div_bias = 0x2;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x20: /* LNA1 A-B */
-                       if (!(antcomb->scan) &&
-                               (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
-                               ant_conf->fast_div_bias = 0x1;
-                       else
-                               ant_conf->fast_div_bias = 0x2;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x21: /* LNA1 LNA2 */
-                       ant_conf->fast_div_bias = 0x1;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x23: /* LNA1 A+B */
-                       if (!(antcomb->scan) &&
-                               (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
-                               ant_conf->fast_div_bias = 0x1;
-                       else
-                               ant_conf->fast_div_bias = 0x2;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x30: /* A+B A-B */
-                       ant_conf->fast_div_bias = 0x1;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x31: /* A+B LNA2 */
-                       ant_conf->fast_div_bias = 0x1;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               case 0x32: /* A+B LNA1 */
-                       ant_conf->fast_div_bias = 0x1;
-                       ant_conf->main_gaintb = 0;
-                       ant_conf->alt_gaintb = 0;
-                       break;
-               default:
-                       break;
-               }
-       }
-}
-
-/* Antenna diversity and combining */
-static void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
-{
-       struct ath_hw_antcomb_conf div_ant_conf;
-       struct ath_ant_comb *antcomb = &sc->ant_comb;
-       int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
-       int curr_main_set;
-       int main_rssi = rs->rs_rssi_ctl0;
-       int alt_rssi = rs->rs_rssi_ctl1;
-       int rx_ant_conf,  main_ant_conf;
-       bool short_scan = false;
-
-       rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) &
-                      ATH_ANT_RX_MASK;
-       main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) &
-                        ATH_ANT_RX_MASK;
-
-       /* Record packet only when both main_rssi and  alt_rssi is positive */
-       if (main_rssi > 0 && alt_rssi > 0) {
-               antcomb->total_pkt_count++;
-               antcomb->main_total_rssi += main_rssi;
-               antcomb->alt_total_rssi  += alt_rssi;
-               if (main_ant_conf == rx_ant_conf)
-                       antcomb->main_recv_cnt++;
-               else
-                       antcomb->alt_recv_cnt++;
-       }
-
-       /* Short scan check */
-       if (antcomb->scan && antcomb->alt_good) {
-               if (time_after(jiffies, antcomb->scan_start_time +
-                   msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
-                       short_scan = true;
-               else
-                       if (antcomb->total_pkt_count ==
-                           ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
-                               alt_ratio = ((antcomb->alt_recv_cnt * 100) /
-                                           antcomb->total_pkt_count);
-                               if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
-                                       short_scan = true;
-                       }
-       }
-
-       if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
-           rs->rs_moreaggr) && !short_scan)
-               return;
-
-       if (antcomb->total_pkt_count) {
-               alt_ratio = ((antcomb->alt_recv_cnt * 100) /
-                            antcomb->total_pkt_count);
-               main_rssi_avg = (antcomb->main_total_rssi /
-                                antcomb->total_pkt_count);
-               alt_rssi_avg = (antcomb->alt_total_rssi /
-                                antcomb->total_pkt_count);
-       }
-
-
-       ath9k_hw_antdiv_comb_conf_get(sc->sc_ah, &div_ant_conf);
-       curr_alt_set = div_ant_conf.alt_lna_conf;
-       curr_main_set = div_ant_conf.main_lna_conf;
-
-       antcomb->count++;
-
-       if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
-               if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
-                       ath_lnaconf_alt_good_scan(antcomb, div_ant_conf,
-                                                 main_rssi_avg);
-                       antcomb->alt_good = true;
-               } else {
-                       antcomb->alt_good = false;
-               }
-
-               antcomb->count = 0;
-               antcomb->scan = true;
-               antcomb->scan_not_start = true;
-       }
-
-       if (!antcomb->scan) {
-               if (ath_ant_div_comb_alt_check(div_ant_conf.div_group,
-                                       alt_ratio, curr_main_set, curr_alt_set,
-                                       alt_rssi_avg, main_rssi_avg)) {
-                       if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) {
-                               /* Switch main and alt LNA */
-                               div_ant_conf.main_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA2;
-                               div_ant_conf.alt_lna_conf  =
-                                               ATH_ANT_DIV_COMB_LNA1;
-                       } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) {
-                               div_ant_conf.main_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1;
-                               div_ant_conf.alt_lna_conf  =
-                                               ATH_ANT_DIV_COMB_LNA2;
-                       }
-
-                       goto div_comb_done;
-               } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) &&
-                          (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) {
-                       /* Set alt to another LNA */
-                       if (curr_main_set == ATH_ANT_DIV_COMB_LNA2)
-                               div_ant_conf.alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1;
-                       else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1)
-                               div_ant_conf.alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA2;
-
-                       goto div_comb_done;
-               }
-
-               if ((alt_rssi_avg < (main_rssi_avg +
-                                               div_ant_conf.lna1_lna2_delta)))
-                       goto div_comb_done;
-       }
-
-       if (!antcomb->scan_not_start) {
-               switch (curr_alt_set) {
-               case ATH_ANT_DIV_COMB_LNA2:
-                       antcomb->rssi_lna2 = alt_rssi_avg;
-                       antcomb->rssi_lna1 = main_rssi_avg;
-                       antcomb->scan = true;
-                       /* set to A+B */
-                       div_ant_conf.main_lna_conf =
-                               ATH_ANT_DIV_COMB_LNA1;
-                       div_ant_conf.alt_lna_conf  =
-                               ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-                       break;
-               case ATH_ANT_DIV_COMB_LNA1:
-                       antcomb->rssi_lna1 = alt_rssi_avg;
-                       antcomb->rssi_lna2 = main_rssi_avg;
-                       antcomb->scan = true;
-                       /* set to A+B */
-                       div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
-                       div_ant_conf.alt_lna_conf  =
-                               ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-                       break;
-               case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
-                       antcomb->rssi_add = alt_rssi_avg;
-                       antcomb->scan = true;
-                       /* set to A-B */
-                       div_ant_conf.alt_lna_conf =
-                               ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
-                       break;
-               case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2:
-                       antcomb->rssi_sub = alt_rssi_avg;
-                       antcomb->scan = false;
-                       if (antcomb->rssi_lna2 >
-                           (antcomb->rssi_lna1 +
-                           ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
-                               /* use LNA2 as main LNA */
-                               if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
-                                   (antcomb->rssi_add > antcomb->rssi_sub)) {
-                                       /* set to A+B */
-                                       div_ant_conf.main_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA2;
-                                       div_ant_conf.alt_lna_conf  =
-                                               ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-                               } else if (antcomb->rssi_sub >
-                                          antcomb->rssi_lna1) {
-                                       /* set to A-B */
-                                       div_ant_conf.main_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA2;
-                                       div_ant_conf.alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
-                               } else {
-                                       /* set to LNA1 */
-                                       div_ant_conf.main_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA2;
-                                       div_ant_conf.alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1;
-                               }
-                       } else {
-                               /* use LNA1 as main LNA */
-                               if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
-                                   (antcomb->rssi_add > antcomb->rssi_sub)) {
-                                       /* set to A+B */
-                                       div_ant_conf.main_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1;
-                                       div_ant_conf.alt_lna_conf  =
-                                               ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
-                               } else if (antcomb->rssi_sub >
-                                          antcomb->rssi_lna1) {
-                                       /* set to A-B */
-                                       div_ant_conf.main_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1;
-                                       div_ant_conf.alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
-                               } else {
-                                       /* set to LNA2 */
-                                       div_ant_conf.main_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1;
-                                       div_ant_conf.alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA2;
-                               }
-                       }
-                       break;
-               default:
-                       break;
-               }
-       } else {
-               if (!antcomb->alt_good) {
-                       antcomb->scan_not_start = false;
-                       /* Set alt to another LNA */
-                       if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) {
-                               div_ant_conf.main_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA2;
-                               div_ant_conf.alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1;
-                       } else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) {
-                               div_ant_conf.main_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA1;
-                               div_ant_conf.alt_lna_conf =
-                                               ATH_ANT_DIV_COMB_LNA2;
-                       }
-                       goto div_comb_done;
-               }
-       }
-
-       ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
-                                          main_rssi_avg, alt_rssi_avg,
-                                          alt_ratio);
-
-       antcomb->quick_scan_cnt++;
-
-div_comb_done:
-       ath_ant_div_conf_fast_divbias(&div_ant_conf, antcomb, alt_ratio);
-       ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf);
-
-       antcomb->scan_start_time = jiffies;
-       antcomb->total_pkt_count = 0;
-       antcomb->main_total_rssi = 0;
-       antcomb->alt_total_rssi = 0;
-       antcomb->main_recv_cnt = 0;
-       antcomb->alt_recv_cnt = 0;
-}
-
 int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
 {
        struct ath_buf *bf;
@@ -1803,7 +1063,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
 
        do {
                /* If handling rx interrupt and flush is in progress => exit */
-               if ((sc->sc_flags & SC_OP_RXFLUSH) && (flush == 0))
+               if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags) && (flush == 0))
                        break;
 
                memset(&rs, 0, sizeof(rs));
@@ -1841,13 +1101,14 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
                else
                        rs.is_mybeacon = false;
 
+               sc->rx.num_pkts++;
                ath_debug_stat_rx(sc, &rs);
 
                /*
                 * If we're asked to flush receive queue, directly
                 * chain it back at the queue without processing it.
                 */
-               if (sc->sc_flags & SC_OP_RXFLUSH) {
+               if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags)) {
                        RX_STAT_INC(rx_drop_rxflush);
                        goto requeue_drop_frag;
                }
@@ -1968,7 +1229,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
                        skb_trim(skb, skb->len - 8);
 
                spin_lock_irqsave(&sc->sc_pm_lock, flags);
-
                if ((sc->ps_flags & (PS_WAIT_FOR_BEACON |
                                     PS_WAIT_FOR_CAB |
                                     PS_WAIT_FOR_PSPOLL_DATA)) ||
index 458f81b..560d6ef 100644 (file)
@@ -2211,5 +2211,7 @@ enum {
 #define AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT      0x00000fff
 #define AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT_S    0
 
+#define AR_GLB_SWREG_DISCONT_MODE         0x2002c
+#define AR_GLB_SWREG_DISCONT_EN_BT_WLAN   0x3
 
 #endif
index d59dd01..f777ddc 100644 (file)
@@ -105,19 +105,19 @@ static int ath_max_4ms_framelen[4][32] = {
 /* Aggregation logic */
 /*********************/
 
-static void ath_txq_lock(struct ath_softc *sc, struct ath_txq *txq)
+void ath_txq_lock(struct ath_softc *sc, struct ath_txq *txq)
        __acquires(&txq->axq_lock)
 {
        spin_lock_bh(&txq->axq_lock);
 }
 
-static void ath_txq_unlock(struct ath_softc *sc, struct ath_txq *txq)
+void ath_txq_unlock(struct ath_softc *sc, struct ath_txq *txq)
        __releases(&txq->axq_lock)
 {
        spin_unlock_bh(&txq->axq_lock);
 }
 
-static void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq)
+void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq)
        __releases(&txq->axq_lock)
 {
        struct sk_buff_head q;
@@ -1536,7 +1536,7 @@ bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
        int i;
        u32 npend = 0;
 
-       if (sc->sc_flags & SC_OP_INVALID)
+       if (test_bit(SC_OP_INVALID, &sc->sc_flags))
                return true;
 
        ath9k_hw_abort_tx_dma(ah);
@@ -1994,6 +1994,7 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ieee80211_hdr * hdr = (struct ieee80211_hdr *)skb->data;
        int q, padpos, padsize;
+       unsigned long flags;
 
        ath_dbg(common, XMIT, "TX complete: skb: %p\n", skb);
 
@@ -2012,6 +2013,7 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
                skb_pull(skb, padsize);
        }
 
+       spin_lock_irqsave(&sc->sc_pm_lock, flags);
        if ((sc->ps_flags & PS_WAIT_FOR_TX_ACK) && !txq->axq_depth) {
                sc->ps_flags &= ~PS_WAIT_FOR_TX_ACK;
                ath_dbg(common, PS,
@@ -2021,6 +2023,7 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
                                        PS_WAIT_FOR_PSPOLL_DATA |
                                        PS_WAIT_FOR_TX_ACK));
        }
+       spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
 
        q = skb_get_queue_mapping(skb);
        if (txq == sc->tx.txq_map[q]) {
@@ -2231,46 +2234,6 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
        ath_txq_unlock_complete(sc, txq);
 }
 
-static void ath_tx_complete_poll_work(struct work_struct *work)
-{
-       struct ath_softc *sc = container_of(work, struct ath_softc,
-                       tx_complete_work.work);
-       struct ath_txq *txq;
-       int i;
-       bool needreset = false;
-#ifdef CONFIG_ATH9K_DEBUGFS
-       sc->tx_complete_poll_work_seen++;
-#endif
-
-       for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
-               if (ATH_TXQ_SETUP(sc, i)) {
-                       txq = &sc->tx.txq[i];
-                       ath_txq_lock(sc, txq);
-                       if (txq->axq_depth) {
-                               if (txq->axq_tx_inprogress) {
-                                       needreset = true;
-                                       ath_txq_unlock(sc, txq);
-                                       break;
-                               } else {
-                                       txq->axq_tx_inprogress = true;
-                               }
-                       }
-                       ath_txq_unlock_complete(sc, txq);
-               }
-
-       if (needreset) {
-               ath_dbg(ath9k_hw_common(sc->sc_ah), RESET,
-                       "tx hung, resetting the chip\n");
-               RESET_STAT_INC(sc, RESET_TYPE_TX_HANG);
-               ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
-       }
-
-       ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
-                       msecs_to_jiffies(ATH_TX_COMPLETE_POLL_INT));
-}
-
-
-
 void ath_tx_tasklet(struct ath_softc *sc)
 {
        struct ath_hw *ah = sc->sc_ah;
index cd9c9bc..8b06ca5 100644 (file)
@@ -1508,7 +1508,7 @@ static void b43legacy_release_firmware(struct b43legacy_wldev *dev)
 
 static void b43legacy_print_fw_helptext(struct b43legacy_wl *wl)
 {
-       b43legacyerr(wl, "You must go to http://linuxwireless.org/en/users/"
+       b43legacyerr(wl, "You must go to http://wireless.kernel.org/en/users/"
                     "Drivers/b43#devicefirmware "
                     "and download the correct firmware (version 3).\n");
 }
index e2480d1..8e7e692 100644 (file)
@@ -89,9 +89,9 @@ int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev)
        data |= 1 << SDIO_FUNC_1 | 1 << SDIO_FUNC_2 | 1;
        brcmf_sdio_regwb(sdiodev, SDIO_CCCR_IENx, data, &ret);
 
-       /* redirect, configure ane enable io for interrupt signal */
+       /* redirect, configure and enable io for interrupt signal */
        data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE;
-       if (sdiodev->irq_flags | IRQF_TRIGGER_HIGH)
+       if (sdiodev->irq_flags & IRQF_TRIGGER_HIGH)
                data |= SDIO_SEPINT_ACT_HI;
        brcmf_sdio_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret);
 
index 6d8b721..3c6f9b1 100644 (file)
@@ -631,9 +631,8 @@ uint ai_cc_reg(struct si_pub *sih, uint regoff, u32 mask, u32 val)
        cc = sii->icbus->drv_cc.core;
 
        /* mask and set */
-       if (mask || val) {
+       if (mask || val)
                bcma_maskset32(cc, regoff, ~mask, val);
-       }
 
        /* readback */
        w = bcma_read32(cc, regoff);
index d9f04a6..d6fa982 100644 (file)
@@ -193,7 +193,7 @@ extern void ai_detach(struct si_pub *sih);
 extern uint ai_cc_reg(struct si_pub *sih, uint regoff, u32 mask, u32 val);
 extern void ai_clkctl_init(struct si_pub *sih);
 extern u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih);
-extern bool ai_clkctl_cc(struct si_pub *sih, uint mode);
+extern bool ai_clkctl_cc(struct si_pub *sih, enum bcma_clkmode mode);
 extern bool ai_deviceremoved(struct si_pub *sih);
 
 extern void ai_pci_down(struct si_pub *sih);
index 509301a..d24eaf8 100644 (file)
@@ -5724,7 +5724,8 @@ il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length)
            BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC);
 
        hw->wiphy->flags |=
-           WIPHY_FLAG_CUSTOM_REGULATORY | WIPHY_FLAG_DISABLE_BEACON_HINTS;
+           WIPHY_FLAG_CUSTOM_REGULATORY | WIPHY_FLAG_DISABLE_BEACON_HINTS |
+           WIPHY_FLAG_IBSS_RSN;
 
        /*
         * For now, disable PS by default because it affects
@@ -5873,6 +5874,16 @@ il4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                return -EOPNOTSUPP;
        }
 
+       /*
+        * To support IBSS RSN, don't program group keys in IBSS, the
+        * hardware will then not attempt to decrypt the frames.
+        */
+       if (vif->type == NL80211_IFTYPE_ADHOC &&
+           !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
+               D_MAC80211("leave - ad-hoc group key\n");
+               return -EOPNOTSUPP;
+       }
+
        sta_id = il_sta_id_or_broadcast(il, sta);
        if (sta_id == IL_INVALID_STATION)
                return -EINVAL;
index f60be39..599e8b4 100644 (file)
@@ -201,6 +201,7 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv,
                            WIPHY_FLAG_DISABLE_BEACON_HINTS |
                            WIPHY_FLAG_IBSS_RSN;
 
+#ifdef CONFIG_PM_SLEEP
        if (priv->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
            priv->trans->ops->wowlan_suspend &&
            device_can_wakeup(priv->trans->dev)) {
@@ -219,6 +220,7 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv,
                hw->wiphy->wowlan.pattern_max_len =
                                        IWLAGN_WOWLAN_MAX_PATTERN_LEN;
        }
+#endif
 
        if (iwlwifi_mod_params.power_save)
                hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
@@ -251,6 +253,7 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv,
        ret = ieee80211_register_hw(priv->hw);
        if (ret) {
                IWL_ERR(priv, "Failed to register hw (error %d)\n", ret);
+               iwl_leds_exit(priv);
                return ret;
        }
        priv->mac80211_registered = 1;
index cdfdfae..49df0e9 100644 (file)
@@ -897,7 +897,6 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
 
        /* We have our copies now, allow OS release its copies */
        release_firmware(ucode_raw);
-       complete(&drv->request_firmware_complete);
 
        op = &iwlwifi_opmode_table[DVM_OP_MODE];
 
@@ -907,10 +906,19 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
        if (op->ops) {
                const struct iwl_op_mode_ops *ops = op->ops;
                drv->op_mode = ops->start(drv->trans, drv->cfg, &drv->fw);
+
+               if (!drv->op_mode)
+                       goto out_unbind;
        } else {
                request_module_nowait("%s", op->name);
        }
 
+       /*
+        * Complete the firmware request last so that
+        * a driver unbind (stop) doesn't run while we
+        * are doing the start() above.
+        */
+       complete(&drv->request_firmware_complete);
        return;
 
  try_again:
index 2fa879b..f4a2030 100644 (file)
@@ -435,24 +435,40 @@ static int lbs_add_wpa_tlv(u8 *tlv, const u8 *ie, u8 ie_len)
  * Set Channel
  */
 
-static int lbs_cfg_set_channel(struct wiphy *wiphy,
-       struct net_device *netdev,
-       struct ieee80211_channel *channel,
-       enum nl80211_channel_type channel_type)
+static int lbs_cfg_set_monitor_channel(struct wiphy *wiphy,
+                                      struct ieee80211_channel *channel,
+                                      enum nl80211_channel_type channel_type)
 {
        struct lbs_private *priv = wiphy_priv(wiphy);
        int ret = -ENOTSUPP;
 
-       lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d, type %d",
-                          netdev_name(netdev), channel->center_freq, channel_type);
+       lbs_deb_enter_args(LBS_DEB_CFG80211, "freq %d, type %d",
+                          channel->center_freq, channel_type);
 
        if (channel_type != NL80211_CHAN_NO_HT)
                goto out;
 
-       if (netdev == priv->mesh_dev)
-               ret = lbs_mesh_set_channel(priv, channel->hw_value);
-       else
-               ret = lbs_set_channel(priv, channel->hw_value);
+       ret = lbs_set_channel(priv, channel->hw_value);
+
+ out:
+       lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+       return ret;
+}
+
+static int lbs_cfg_set_mesh_channel(struct wiphy *wiphy,
+                                   struct net_device *netdev,
+                                   struct ieee80211_channel *channel)
+{
+       struct lbs_private *priv = wiphy_priv(wiphy);
+       int ret = -ENOTSUPP;
+
+       lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d",
+                          netdev_name(netdev), channel->center_freq);
+
+       if (netdev != priv->mesh_dev)
+               goto out;
+
+       ret = lbs_mesh_set_channel(priv, channel->hw_value);
 
  out:
        lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
@@ -2029,7 +2045,8 @@ static int lbs_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
  */
 
 static struct cfg80211_ops lbs_cfg80211_ops = {
-       .set_channel = lbs_cfg_set_channel,
+       .set_monitor_channel = lbs_cfg_set_monitor_channel,
+       .libertas_set_mesh_channel = lbs_cfg_set_mesh_channel,
        .scan = lbs_cfg_scan,
        .connect = lbs_cfg_connect,
        .disconnect = lbs_cfg_disconnect,
index 6720054..60996ce 100644 (file)
@@ -58,6 +58,7 @@ struct lbs_private {
        uint16_t mesh_tlv;
        u8 mesh_ssid[IEEE80211_MAX_SSID_LEN + 1];
        u8 mesh_ssid_len;
+       u8 mesh_channel;
 #endif
 
        /* Debugfs */
index e87c031..9780775 100644 (file)
@@ -131,16 +131,13 @@ static int lbs_mesh_config(struct lbs_private *priv, uint16_t action,
 
 int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel)
 {
+       priv->mesh_channel = channel;
        return lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, channel);
 }
 
 static uint16_t lbs_mesh_get_channel(struct lbs_private *priv)
 {
-       struct wireless_dev *mesh_wdev = priv->mesh_dev->ieee80211_ptr;
-       if (mesh_wdev->channel)
-               return mesh_wdev->channel->hw_value;
-       else
-               return 1;
+       return priv->mesh_channel ?: 1;
 }
 
 /***************************************************************************
index fb787df..4c9336c 100644 (file)
@@ -1721,6 +1721,24 @@ static void hwsim_exit_netlink(void)
                       "unregister family %i\n", ret);
 }
 
+static const struct ieee80211_iface_limit hwsim_if_limits[] = {
+       { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) },
+       { .max = 2048,  .types = BIT(NL80211_IFTYPE_STATION) |
+                                BIT(NL80211_IFTYPE_P2P_CLIENT) |
+#ifdef CONFIG_MAC80211_MESH
+                                BIT(NL80211_IFTYPE_MESH_POINT) |
+#endif
+                                BIT(NL80211_IFTYPE_AP) |
+                                BIT(NL80211_IFTYPE_P2P_GO) },
+};
+
+static const struct ieee80211_iface_combination hwsim_if_comb = {
+       .limits = hwsim_if_limits,
+       .n_limits = ARRAY_SIZE(hwsim_if_limits),
+       .max_interfaces = 2048,
+       .num_different_channels = 1,
+};
+
 static int __init init_mac80211_hwsim(void)
 {
        int i, err = 0;
@@ -1782,6 +1800,9 @@ static int __init init_mac80211_hwsim(void)
                hw->wiphy->n_addresses = 2;
                hw->wiphy->addresses = data->addresses;
 
+               hw->wiphy->iface_combinations = &hwsim_if_comb;
+               hw->wiphy->n_iface_combinations = 1;
+
                if (fake_hw_scan) {
                        hw->wiphy->max_scan_ssids = 255;
                        hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
index f7b15b8..e156755 100644 (file)
@@ -160,10 +160,9 @@ static int orinoco_scan(struct wiphy *wiphy, struct net_device *dev,
        return err;
 }
 
-static int orinoco_set_channel(struct wiphy *wiphy,
-                       struct net_device *netdev,
-                       struct ieee80211_channel *chan,
-                       enum nl80211_channel_type channel_type)
+static int orinoco_set_monitor_channel(struct wiphy *wiphy,
+                                      struct ieee80211_channel *chan,
+                                      enum nl80211_channel_type channel_type)
 {
        struct orinoco_private *priv = wiphy_priv(wiphy);
        int err = 0;
@@ -286,7 +285,7 @@ static int orinoco_set_wiphy_params(struct wiphy *wiphy, u32 changed)
 
 const struct cfg80211_ops orinoco_cfg_ops = {
        .change_virtual_intf = orinoco_change_vif,
-       .set_channel = orinoco_set_channel,
+       .set_monitor_channel = orinoco_set_monitor_channel,
        .scan = orinoco_scan,
        .set_wiphy_params = orinoco_set_wiphy_params,
 };
index 6403f49..1ca88cd 100644 (file)
@@ -72,6 +72,7 @@
 #define RF5370                         0x5370
 #define RF5372                         0x5372
 #define RF5390                         0x5390
+#define RF5392                         0x5392
 
 /*
  * Chipset revisions.
@@ -1946,6 +1947,11 @@ struct mac_iveiv_entry {
 #define RFCSR49_TX                     FIELD8(0x3f)
 
 /*
+ * RFCSR 50:
+ */
+#define RFCSR50_TX                     FIELD8(0x3f)
+
+/*
  * RF registers
  */
 
index 11488e7..4d3747c 100644 (file)
@@ -1958,7 +1958,22 @@ static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev,
                rt2x00_set_field8(&rfcsr, RFCSR49_TX, info->default_power1);
        rt2800_rfcsr_write(rt2x00dev, 49, rfcsr);
 
+       if (rt2x00_rt(rt2x00dev, RT5392)) {
+               rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr);
+               if (info->default_power1 > RT5390_POWER_BOUND)
+                       rt2x00_set_field8(&rfcsr, RFCSR50_TX,
+                                         RT5390_POWER_BOUND);
+               else
+                       rt2x00_set_field8(&rfcsr, RFCSR50_TX,
+                                         info->default_power2);
+               rt2800_rfcsr_write(rt2x00dev, 50, rfcsr);
+       }
+
        rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr);
+       if (rt2x00_rt(rt2x00dev, RT5392)) {
+               rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1);
+               rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1);
+       }
        rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1);
        rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1);
        rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1);
@@ -2064,6 +2079,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
        case RF5370:
        case RF5372:
        case RF5390:
+       case RF5392:
                rt2800_config_channel_rf53xx(rt2x00dev, conf, rf, info);
                break;
        default:
@@ -2554,6 +2570,7 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev)
        case RF5370:
        case RF5372:
        case RF5390:
+       case RF5392:
                rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr);
                rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1);
                rt2800_rfcsr_write(rt2x00dev, 3, rfcsr);
@@ -4269,6 +4286,7 @@ int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev)
        case RF5370:
        case RF5372:
        case RF5390:
+       case RF5392:
                break;
        default:
                ERROR(rt2x00dev, "Invalid RF chipset 0x%04x detected.\n",
@@ -4583,7 +4601,8 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
                   rt2x00_rf(rt2x00dev, RF5360) ||
                   rt2x00_rf(rt2x00dev, RF5370) ||
                   rt2x00_rf(rt2x00dev, RF5372) ||
-                  rt2x00_rf(rt2x00dev, RF5390)) {
+                  rt2x00_rf(rt2x00dev, RF5390) ||
+                  rt2x00_rf(rt2x00dev, RF5392)) {
                spec->num_channels = 14;
                spec->channels = rf_vals_3x;
        } else if (rt2x00_rf(rt2x00dev, RF3052)) {
@@ -4670,6 +4689,7 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        case RF5370:
        case RF5372:
        case RF5390:
+       case RF5392:
                __set_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags);
                break;
        }
index ca36ccc..8f75402 100644 (file)
@@ -396,8 +396,7 @@ struct rt2x00_intf {
         * for hardware which doesn't support hardware
         * sequence counting.
         */
-       spinlock_t seqlock;
-       u16 seqno;
+       atomic_t seqno;
 };
 
 static inline struct rt2x00_intf* vif_to_intf(struct ieee80211_vif *vif)
index e5404e5..a6b88bd 100644 (file)
@@ -1161,6 +1161,8 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
                    BIT(NL80211_IFTYPE_MESH_POINT) |
                    BIT(NL80211_IFTYPE_WDS);
 
+       rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
+
        /*
         * Initialize work.
         */
index b49773e..4ff26c2 100644 (file)
@@ -277,7 +277,6 @@ int rt2x00mac_add_interface(struct ieee80211_hw *hw,
        else
                rt2x00dev->intf_sta_count++;
 
-       spin_lock_init(&intf->seqlock);
        mutex_init(&intf->beacon_skb_mutex);
        intf->beacon = entry;
 
@@ -507,9 +506,19 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 
        if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
                return 0;
-       else if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags))
+
+       if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags))
+               return -EOPNOTSUPP;
+
+       /*
+        * To support IBSS RSN, don't program group keys in IBSS, the
+        * hardware will then not attempt to decrypt the frames.
+        */
+       if (vif->type == NL80211_IFTYPE_ADHOC &&
+           !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
                return -EOPNOTSUPP;
-       else if (key->keylen > 32)
+
+       if (key->keylen > 32)
                return -ENOSPC;
 
        memset(&crypto, 0, sizeof(crypto));
index 4c662ec..2fd8301 100644 (file)
@@ -207,6 +207,7 @@ static void rt2x00queue_create_tx_descriptor_seq(struct rt2x00_dev *rt2x00dev,
        struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
        struct rt2x00_intf *intf = vif_to_intf(tx_info->control.vif);
+       u16 seqno;
 
        if (!(tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ))
                return;
@@ -238,15 +239,13 @@ static void rt2x00queue_create_tx_descriptor_seq(struct rt2x00_dev *rt2x00dev,
         * sequence counting per-frame, since those will override the
         * sequence counter given by mac80211.
         */
-       spin_lock(&intf->seqlock);
-
        if (test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags))
-               intf->seqno += 0x10;
-       hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
-       hdr->seq_ctrl |= cpu_to_le16(intf->seqno);
-
-       spin_unlock(&intf->seqlock);
+               seqno = atomic_add_return(0x10, &intf->seqno);
+       else
+               seqno = atomic_read(&intf->seqno);
 
+       hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
+       hdr->seq_ctrl |= cpu_to_le16(seqno);
 }
 
 static void rt2x00queue_create_tx_descriptor_plcp(struct rt2x00_dev *rt2x00dev,
index 1a72932..be80011 100644 (file)
@@ -8,6 +8,7 @@ menuconfig WL_TI
 if WL_TI
 source "drivers/net/wireless/ti/wl1251/Kconfig"
 source "drivers/net/wireless/ti/wl12xx/Kconfig"
+source "drivers/net/wireless/ti/wl18xx/Kconfig"
 
 # keep last for automatic dependencies
 source "drivers/net/wireless/ti/wlcore/Kconfig"
index 0a56562..4d68239 100644 (file)
@@ -2,3 +2,4 @@ obj-$(CONFIG_WLCORE)                    += wlcore/
 obj-$(CONFIG_WL12XX)                   += wl12xx/
 obj-$(CONFIG_WL12XX_PLATFORM_DATA)     += wlcore/
 obj-$(CONFIG_WL1251)                   += wl1251/
+obj-$(CONFIG_WL18XX)                   += wl18xx/
index 87f64b1..da509aa 100644 (file)
@@ -1,3 +1,3 @@
-wl12xx-objs    = main.o cmd.o acx.o
+wl12xx-objs    = main.o cmd.o acx.o debugfs.o
 
 obj-$(CONFIG_WL12XX)           += wl12xx.o
index d1f5aba..2a26868 100644 (file)
 #define __WL12XX_ACX_H__
 
 #include "../wlcore/wlcore.h"
+#include "../wlcore/acx.h"
+
+#define WL12XX_ACX_ALL_EVENTS_VECTOR   (WL1271_ACX_INTR_WATCHDOG      | \
+                                       WL1271_ACX_INTR_INIT_COMPLETE | \
+                                       WL1271_ACX_INTR_EVENT_A       | \
+                                       WL1271_ACX_INTR_EVENT_B       | \
+                                       WL1271_ACX_INTR_CMD_COMPLETE  | \
+                                       WL1271_ACX_INTR_HW_AVAILABLE  | \
+                                       WL1271_ACX_INTR_DATA)
+
+#define WL12XX_INTR_MASK               (WL1271_ACX_INTR_WATCHDOG      | \
+                                       WL1271_ACX_INTR_EVENT_A       | \
+                                       WL1271_ACX_INTR_EVENT_B       | \
+                                       WL1271_ACX_INTR_HW_AVAILABLE  | \
+                                       WL1271_ACX_INTR_DATA)
 
 struct wl1271_acx_host_config_bitmap {
        struct acx_header header;
@@ -31,6 +46,228 @@ struct wl1271_acx_host_config_bitmap {
        __le32 host_cfg_bitmap;
 } __packed;
 
+struct wl12xx_acx_tx_statistics {
+       __le32 internal_desc_overflow;
+}  __packed;
+
+struct wl12xx_acx_rx_statistics {
+       __le32 out_of_mem;
+       __le32 hdr_overflow;
+       __le32 hw_stuck;
+       __le32 dropped;
+       __le32 fcs_err;
+       __le32 xfr_hint_trig;
+       __le32 path_reset;
+       __le32 reset_counter;
+} __packed;
+
+struct wl12xx_acx_dma_statistics {
+       __le32 rx_requested;
+       __le32 rx_errors;
+       __le32 tx_requested;
+       __le32 tx_errors;
+}  __packed;
+
+struct wl12xx_acx_isr_statistics {
+       /* host command complete */
+       __le32 cmd_cmplt;
+
+       /* fiqisr() */
+       __le32 fiqs;
+
+       /* (INT_STS_ND & INT_TRIG_RX_HEADER) */
+       __le32 rx_headers;
+
+       /* (INT_STS_ND & INT_TRIG_RX_CMPLT) */
+       __le32 rx_completes;
+
+       /* (INT_STS_ND & INT_TRIG_NO_RX_BUF) */
+       __le32 rx_mem_overflow;
+
+       /* (INT_STS_ND & INT_TRIG_S_RX_RDY) */
+       __le32 rx_rdys;
+
+       /* irqisr() */
+       __le32 irqs;
+
+       /* (INT_STS_ND & INT_TRIG_TX_PROC) */
+       __le32 tx_procs;
+
+       /* (INT_STS_ND & INT_TRIG_DECRYPT_DONE) */
+       __le32 decrypt_done;
+
+       /* (INT_STS_ND & INT_TRIG_DMA0) */
+       __le32 dma0_done;
+
+       /* (INT_STS_ND & INT_TRIG_DMA1) */
+       __le32 dma1_done;
+
+       /* (INT_STS_ND & INT_TRIG_TX_EXC_CMPLT) */
+       __le32 tx_exch_complete;
+
+       /* (INT_STS_ND & INT_TRIG_COMMAND) */
+       __le32 commands;
+
+       /* (INT_STS_ND & INT_TRIG_RX_PROC) */
+       __le32 rx_procs;
+
+       /* (INT_STS_ND & INT_TRIG_PM_802) */
+       __le32 hw_pm_mode_changes;
+
+       /* (INT_STS_ND & INT_TRIG_ACKNOWLEDGE) */
+       __le32 host_acknowledges;
+
+       /* (INT_STS_ND & INT_TRIG_PM_PCI) */
+       __le32 pci_pm;
+
+       /* (INT_STS_ND & INT_TRIG_ACM_WAKEUP) */
+       __le32 wakeups;
+
+       /* (INT_STS_ND & INT_TRIG_LOW_RSSI) */
+       __le32 low_rssi;
+} __packed;
+
+struct wl12xx_acx_wep_statistics {
+       /* WEP address keys configured */
+       __le32 addr_key_count;
+
+       /* default keys configured */
+       __le32 default_key_count;
+
+       __le32 reserved;
+
+       /* number of times that WEP key not found on lookup */
+       __le32 key_not_found;
+
+       /* number of times that WEP key decryption failed */
+       __le32 decrypt_fail;
+
+       /* WEP packets decrypted */
+       __le32 packets;
+
+       /* WEP decrypt interrupts */
+       __le32 interrupt;
+} __packed;
+
+#define ACX_MISSED_BEACONS_SPREAD 10
+
+struct wl12xx_acx_pwr_statistics {
+       /* the amount of enters into power save mode (both PD & ELP) */
+       __le32 ps_enter;
+
+       /* the amount of enters into ELP mode */
+       __le32 elp_enter;
+
+       /* the amount of missing beacon interrupts to the host */
+       __le32 missing_bcns;
+
+       /* the amount of wake on host-access times */
+       __le32 wake_on_host;
+
+       /* the amount of wake on timer-expire */
+       __le32 wake_on_timer_exp;
+
+       /* the number of packets that were transmitted with PS bit set */
+       __le32 tx_with_ps;
+
+       /* the number of packets that were transmitted with PS bit clear */
+       __le32 tx_without_ps;
+
+       /* the number of received beacons */
+       __le32 rcvd_beacons;
+
+       /* the number of entering into PowerOn (power save off) */
+       __le32 power_save_off;
+
+       /* the number of entries into power save mode */
+       __le16 enable_ps;
+
+       /*
+        * the number of exits from power save, not including failed PS
+        * transitions
+        */
+       __le16 disable_ps;
+
+       /*
+        * the number of times the TSF counter was adjusted because
+        * of drift
+        */
+       __le32 fix_tsf_ps;
+
+       /* Gives statistics about the spread continuous missed beacons.
+        * The 16 LSB are dedicated for the PS mode.
+        * The 16 MSB are dedicated for the PS mode.
+        * cont_miss_bcns_spread[0] - single missed beacon.
+        * cont_miss_bcns_spread[1] - two continuous missed beacons.
+        * cont_miss_bcns_spread[2] - three continuous missed beacons.
+        * ...
+        * cont_miss_bcns_spread[9] - ten and more continuous missed beacons.
+       */
+       __le32 cont_miss_bcns_spread[ACX_MISSED_BEACONS_SPREAD];
+
+       /* the number of beacons in awake mode */
+       __le32 rcvd_awake_beacons;
+} __packed;
+
+struct wl12xx_acx_mic_statistics {
+       __le32 rx_pkts;
+       __le32 calc_failure;
+} __packed;
+
+struct wl12xx_acx_aes_statistics {
+       __le32 encrypt_fail;
+       __le32 decrypt_fail;
+       __le32 encrypt_packets;
+       __le32 decrypt_packets;
+       __le32 encrypt_interrupt;
+       __le32 decrypt_interrupt;
+} __packed;
+
+struct wl12xx_acx_event_statistics {
+       __le32 heart_beat;
+       __le32 calibration;
+       __le32 rx_mismatch;
+       __le32 rx_mem_empty;
+       __le32 rx_pool;
+       __le32 oom_late;
+       __le32 phy_transmit_error;
+       __le32 tx_stuck;
+} __packed;
+
+struct wl12xx_acx_ps_statistics {
+       __le32 pspoll_timeouts;
+       __le32 upsd_timeouts;
+       __le32 upsd_max_sptime;
+       __le32 upsd_max_apturn;
+       __le32 pspoll_max_apturn;
+       __le32 pspoll_utilization;
+       __le32 upsd_utilization;
+} __packed;
+
+struct wl12xx_acx_rxpipe_statistics {
+       __le32 rx_prep_beacon_drop;
+       __le32 descr_host_int_trig_rx_data;
+       __le32 beacon_buffer_thres_host_int_trig_rx_data;
+       __le32 missed_beacon_host_int_trig_rx_data;
+       __le32 tx_xfr_host_int_trig_rx_data;
+} __packed;
+
+struct wl12xx_acx_statistics {
+       struct acx_header header;
+
+       struct wl12xx_acx_tx_statistics tx;
+       struct wl12xx_acx_rx_statistics rx;
+       struct wl12xx_acx_dma_statistics dma;
+       struct wl12xx_acx_isr_statistics isr;
+       struct wl12xx_acx_wep_statistics wep;
+       struct wl12xx_acx_pwr_statistics pwr;
+       struct wl12xx_acx_aes_statistics aes;
+       struct wl12xx_acx_mic_statistics mic;
+       struct wl12xx_acx_event_statistics event;
+       struct wl12xx_acx_ps_statistics ps;
+       struct wl12xx_acx_rxpipe_statistics rxpipe;
+} __packed;
+
 int wl1271_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap);
 
 #endif /* __WL12XX_ACX_H__ */
index 8ffaeb5..50ba748 100644 (file)
@@ -65,6 +65,7 @@ int wl1271_cmd_general_parms(struct wl1271 *wl)
        struct wl1271_general_parms_cmd *gen_parms;
        struct wl1271_ini_general_params *gp =
                &((struct wl1271_nvs_file *)wl->nvs)->general_params;
+       struct wl12xx_priv *priv = wl->priv;
        bool answer = false;
        int ret;
 
@@ -88,7 +89,7 @@ int wl1271_cmd_general_parms(struct wl1271 *wl)
                answer = true;
 
        /* Override the REF CLK from the NVS with the one from platform data */
-       gen_parms->general_params.ref_clock = wl->ref_clock;
+       gen_parms->general_params.ref_clock = priv->ref_clock;
 
        ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer);
        if (ret < 0) {
@@ -118,6 +119,7 @@ int wl128x_cmd_general_parms(struct wl1271 *wl)
        struct wl128x_general_parms_cmd *gen_parms;
        struct wl128x_ini_general_params *gp =
                &((struct wl128x_nvs_file *)wl->nvs)->general_params;
+       struct wl12xx_priv *priv = wl->priv;
        bool answer = false;
        int ret;
 
@@ -141,8 +143,8 @@ int wl128x_cmd_general_parms(struct wl1271 *wl)
                answer = true;
 
        /* Replace REF and TCXO CLKs with the ones from platform data */
-       gen_parms->general_params.ref_clock = wl->ref_clock;
-       gen_parms->general_params.tcxo_ref_clock = wl->tcxo_clock;
+       gen_parms->general_params.ref_clock = priv->ref_clock;
+       gen_parms->general_params.tcxo_ref_clock = priv->tcxo_clock;
 
        ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer);
        if (ret < 0) {
diff --git a/drivers/net/wireless/ti/wl12xx/debugfs.c b/drivers/net/wireless/ti/wl12xx/debugfs.c
new file mode 100644 (file)
index 0000000..0521cbf
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Copyright (C) 2011-2012 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "../wlcore/debugfs.h"
+#include "../wlcore/wlcore.h"
+
+#include "wl12xx.h"
+#include "acx.h"
+#include "debugfs.h"
+
+#define WL12XX_DEBUGFS_FWSTATS_FILE(a, b, c) \
+       DEBUGFS_FWSTATS_FILE(a, b, c, wl12xx_acx_statistics)
+
+WL12XX_DEBUGFS_FWSTATS_FILE(tx, internal_desc_overflow, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, out_of_mem, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, hdr_overflow, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, hw_stuck, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, dropped, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, fcs_err, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, xfr_hint_trig, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, path_reset, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rx, reset_counter, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(dma, rx_requested, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(dma, rx_errors, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(dma, tx_requested, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(dma, tx_errors, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, cmd_cmplt, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, fiqs, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_headers, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_mem_overflow, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_rdys, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, irqs, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, tx_procs, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, decrypt_done, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, dma0_done, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, dma1_done, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, tx_exch_complete, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, commands, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_procs, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, hw_pm_mode_changes, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, host_acknowledges, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, pci_pm, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, wakeups, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(isr, low_rssi, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(wep, addr_key_count, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(wep, default_key_count, "%u");
+/* skipping wep.reserved */
+WL12XX_DEBUGFS_FWSTATS_FILE(wep, key_not_found, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(wep, decrypt_fail, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(wep, packets, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(wep, interrupt, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, ps_enter, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, elp_enter, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, missing_bcns, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, wake_on_host, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, wake_on_timer_exp, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, tx_with_ps, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, tx_without_ps, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_beacons, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, power_save_off, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, enable_ps, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, disable_ps, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, fix_tsf_ps, "%u");
+/* skipping cont_miss_bcns_spread for now */
+WL12XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_beacons, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(mic, rx_pkts, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(mic, calc_failure, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(aes, encrypt_fail, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(aes, decrypt_fail, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(aes, encrypt_packets, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(aes, decrypt_packets, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(aes, encrypt_interrupt, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(aes, decrypt_interrupt, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(event, heart_beat, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(event, calibration, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(event, rx_mismatch, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(event, rx_mem_empty, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(event, rx_pool, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(event, oom_late, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(event, phy_transmit_error, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(event, tx_stuck, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(ps, pspoll_timeouts, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_timeouts, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_max_sptime, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_max_apturn, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(ps, pspoll_max_apturn, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(ps, pspoll_utilization, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_utilization, "%u");
+
+WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, rx_prep_beacon_drop, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, descr_host_int_trig_rx_data, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, beacon_buffer_thres_host_int_trig_rx_data,
+                           "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, missed_beacon_host_int_trig_rx_data, "%u");
+WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, tx_xfr_host_int_trig_rx_data, "%u");
+
+int wl12xx_debugfs_add_files(struct wl1271 *wl,
+                            struct dentry *rootdir)
+{
+       int ret = 0;
+       struct dentry *entry, *stats, *moddir;
+
+       moddir = debugfs_create_dir(KBUILD_MODNAME, rootdir);
+       if (!moddir || IS_ERR(moddir)) {
+               entry = moddir;
+               goto err;
+       }
+
+       stats = debugfs_create_dir("fw_stats", moddir);
+       if (!stats || IS_ERR(stats)) {
+               entry = stats;
+               goto err;
+       }
+
+       DEBUGFS_FWSTATS_ADD(tx, internal_desc_overflow);
+
+       DEBUGFS_FWSTATS_ADD(rx, out_of_mem);
+       DEBUGFS_FWSTATS_ADD(rx, hdr_overflow);
+       DEBUGFS_FWSTATS_ADD(rx, hw_stuck);
+       DEBUGFS_FWSTATS_ADD(rx, dropped);
+       DEBUGFS_FWSTATS_ADD(rx, fcs_err);
+       DEBUGFS_FWSTATS_ADD(rx, xfr_hint_trig);
+       DEBUGFS_FWSTATS_ADD(rx, path_reset);
+       DEBUGFS_FWSTATS_ADD(rx, reset_counter);
+
+       DEBUGFS_FWSTATS_ADD(dma, rx_requested);
+       DEBUGFS_FWSTATS_ADD(dma, rx_errors);
+       DEBUGFS_FWSTATS_ADD(dma, tx_requested);
+       DEBUGFS_FWSTATS_ADD(dma, tx_errors);
+
+       DEBUGFS_FWSTATS_ADD(isr, cmd_cmplt);
+       DEBUGFS_FWSTATS_ADD(isr, fiqs);
+       DEBUGFS_FWSTATS_ADD(isr, rx_headers);
+       DEBUGFS_FWSTATS_ADD(isr, rx_mem_overflow);
+       DEBUGFS_FWSTATS_ADD(isr, rx_rdys);
+       DEBUGFS_FWSTATS_ADD(isr, irqs);
+       DEBUGFS_FWSTATS_ADD(isr, tx_procs);
+       DEBUGFS_FWSTATS_ADD(isr, decrypt_done);
+       DEBUGFS_FWSTATS_ADD(isr, dma0_done);
+       DEBUGFS_FWSTATS_ADD(isr, dma1_done);
+       DEBUGFS_FWSTATS_ADD(isr, tx_exch_complete);
+       DEBUGFS_FWSTATS_ADD(isr, commands);
+       DEBUGFS_FWSTATS_ADD(isr, rx_procs);
+       DEBUGFS_FWSTATS_ADD(isr, hw_pm_mode_changes);
+       DEBUGFS_FWSTATS_ADD(isr, host_acknowledges);
+       DEBUGFS_FWSTATS_ADD(isr, pci_pm);
+       DEBUGFS_FWSTATS_ADD(isr, wakeups);
+       DEBUGFS_FWSTATS_ADD(isr, low_rssi);
+
+       DEBUGFS_FWSTATS_ADD(wep, addr_key_count);
+       DEBUGFS_FWSTATS_ADD(wep, default_key_count);
+       /* skipping wep.reserved */
+       DEBUGFS_FWSTATS_ADD(wep, key_not_found);
+       DEBUGFS_FWSTATS_ADD(wep, decrypt_fail);
+       DEBUGFS_FWSTATS_ADD(wep, packets);
+       DEBUGFS_FWSTATS_ADD(wep, interrupt);
+
+       DEBUGFS_FWSTATS_ADD(pwr, ps_enter);
+       DEBUGFS_FWSTATS_ADD(pwr, elp_enter);
+       DEBUGFS_FWSTATS_ADD(pwr, missing_bcns);
+       DEBUGFS_FWSTATS_ADD(pwr, wake_on_host);
+       DEBUGFS_FWSTATS_ADD(pwr, wake_on_timer_exp);
+       DEBUGFS_FWSTATS_ADD(pwr, tx_with_ps);
+       DEBUGFS_FWSTATS_ADD(pwr, tx_without_ps);
+       DEBUGFS_FWSTATS_ADD(pwr, rcvd_beacons);
+       DEBUGFS_FWSTATS_ADD(pwr, power_save_off);
+       DEBUGFS_FWSTATS_ADD(pwr, enable_ps);
+       DEBUGFS_FWSTATS_ADD(pwr, disable_ps);
+       DEBUGFS_FWSTATS_ADD(pwr, fix_tsf_ps);
+       /* skipping cont_miss_bcns_spread for now */
+       DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_beacons);
+
+       DEBUGFS_FWSTATS_ADD(mic, rx_pkts);
+       DEBUGFS_FWSTATS_ADD(mic, calc_failure);
+
+       DEBUGFS_FWSTATS_ADD(aes, encrypt_fail);
+       DEBUGFS_FWSTATS_ADD(aes, decrypt_fail);
+       DEBUGFS_FWSTATS_ADD(aes, encrypt_packets);
+       DEBUGFS_FWSTATS_ADD(aes, decrypt_packets);
+       DEBUGFS_FWSTATS_ADD(aes, encrypt_interrupt);
+       DEBUGFS_FWSTATS_ADD(aes, decrypt_interrupt);
+
+       DEBUGFS_FWSTATS_ADD(event, heart_beat);
+       DEBUGFS_FWSTATS_ADD(event, calibration);
+       DEBUGFS_FWSTATS_ADD(event, rx_mismatch);
+       DEBUGFS_FWSTATS_ADD(event, rx_mem_empty);
+       DEBUGFS_FWSTATS_ADD(event, rx_pool);
+       DEBUGFS_FWSTATS_ADD(event, oom_late);
+       DEBUGFS_FWSTATS_ADD(event, phy_transmit_error);
+       DEBUGFS_FWSTATS_ADD(event, tx_stuck);
+
+       DEBUGFS_FWSTATS_ADD(ps, pspoll_timeouts);
+       DEBUGFS_FWSTATS_ADD(ps, upsd_timeouts);
+       DEBUGFS_FWSTATS_ADD(ps, upsd_max_sptime);
+       DEBUGFS_FWSTATS_ADD(ps, upsd_max_apturn);
+       DEBUGFS_FWSTATS_ADD(ps, pspoll_max_apturn);
+       DEBUGFS_FWSTATS_ADD(ps, pspoll_utilization);
+       DEBUGFS_FWSTATS_ADD(ps, upsd_utilization);
+
+       DEBUGFS_FWSTATS_ADD(rxpipe, rx_prep_beacon_drop);
+       DEBUGFS_FWSTATS_ADD(rxpipe, descr_host_int_trig_rx_data);
+       DEBUGFS_FWSTATS_ADD(rxpipe, beacon_buffer_thres_host_int_trig_rx_data);
+       DEBUGFS_FWSTATS_ADD(rxpipe, missed_beacon_host_int_trig_rx_data);
+       DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data);
+
+       return 0;
+
+err:
+       if (IS_ERR(entry))
+               ret = PTR_ERR(entry);
+       else
+               ret = -ENOMEM;
+
+       return ret;
+}
diff --git a/drivers/net/wireless/ti/wl12xx/debugfs.h b/drivers/net/wireless/ti/wl12xx/debugfs.h
new file mode 100644 (file)
index 0000000..96898e2
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL12XX_DEBUGFS_H__
+#define __WL12XX_DEBUGFS_H__
+
+int wl12xx_debugfs_add_files(struct wl1271 *wl,
+                            struct dentry *rootdir);
+
+#endif /* __WL12XX_DEBUGFS_H__ */
index d7dd3de..85d1600 100644 (file)
 #include "reg.h"
 #include "cmd.h"
 #include "acx.h"
+#include "debugfs.h"
+
+static char *fref_param;
+static char *tcxo_param;
 
 static struct wlcore_conf wl12xx_conf = {
        .sg = {
@@ -212,7 +216,7 @@ static struct wlcore_conf wl12xx_conf = {
                .suspend_wake_up_event       = CONF_WAKE_UP_EVENT_N_DTIM,
                .suspend_listen_interval     = 3,
                .bcn_filt_mode               = CONF_BCN_FILT_MODE_ENABLED,
-               .bcn_filt_ie_count           = 2,
+               .bcn_filt_ie_count           = 3,
                .bcn_filt_ie = {
                        [0] = {
                                .ie          = WLAN_EID_CHANNEL_SWITCH,
@@ -222,9 +226,13 @@ static struct wlcore_conf wl12xx_conf = {
                                .ie          = WLAN_EID_HT_OPERATION,
                                .rule        = CONF_BCN_RULE_PASS_ON_CHANGE,
                        },
+                       [2] = {
+                               .ie          = WLAN_EID_ERP_INFO,
+                               .rule        = CONF_BCN_RULE_PASS_ON_CHANGE,
+                       },
                },
-               .synch_fail_thold            = 10,
-               .bss_lose_timeout            = 100,
+               .synch_fail_thold            = 12,
+               .bss_lose_timeout            = 400,
                .beacon_rx_timeout           = 10000,
                .broadcast_timeout           = 20000,
                .rx_broadcast_in_ps          = 1,
@@ -234,7 +242,7 @@ static struct wlcore_conf wl12xx_conf = {
                .psm_entry_retries           = 8,
                .psm_exit_retries            = 16,
                .psm_entry_nullfunc_retries  = 3,
-               .dynamic_ps_timeout          = 40,
+               .dynamic_ps_timeout          = 200,
                .forced_ps                   = false,
                .keep_alive_interval         = 55000,
                .max_listen_interval         = 20,
@@ -245,7 +253,7 @@ static struct wlcore_conf wl12xx_conf = {
        },
        .pm_config = {
                .host_clk_settling_time = 5000,
-               .host_fast_wakeup_support = false
+               .host_fast_wakeup_support = CONF_FAST_WAKEUP_DISABLE,
        },
        .roam_trigger = {
                .trigger_pacing               = 1,
@@ -305,8 +313,8 @@ static struct wlcore_conf wl12xx_conf = {
                .swallow_period               = 5,
                .n_divider_fref_set_1         = 0xff,       /* default */
                .n_divider_fref_set_2         = 12,
-               .m_divider_fref_set_1         = 148,
-               .m_divider_fref_set_2         = 0xffff,     /* default */
+               .m_divider_fref_set_1         = 0xffff,
+               .m_divider_fref_set_2         = 148,        /* default */
                .coex_pll_stabilization_time  = 0xffffffff, /* default */
                .ldo_stabilization_time       = 0xffff,     /* default */
                .fm_disturbed_band_margin     = 0xff,       /* default */
@@ -593,7 +601,7 @@ static void wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len)
 {
        if (wl->chip.id != CHIP_ID_1283_PG20) {
                struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map;
-               struct wl1271_rx_mem_pool_addr rx_mem_addr;
+               struct wl127x_rx_mem_pool_addr rx_mem_addr;
 
                /*
                 * Choose the block we want to read
@@ -621,10 +629,8 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
                wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
                               wl->chip.id);
 
-               /* clear the alignment quirk, since we don't support it */
-               wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;
-
-               wl->quirks |= WLCORE_QUIRK_LEGACY_NVS;
+               wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
+                             WLCORE_QUIRK_TKIP_HEADER_SPACE;
                wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
                wl->mr_fw_name = WL127X_FW_NAME_MULTI;
                memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x,
@@ -639,10 +645,8 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
                wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
                             wl->chip.id);
 
-               /* clear the alignment quirk, since we don't support it */
-               wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;
-
-               wl->quirks |= WLCORE_QUIRK_LEGACY_NVS;
+               wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
+                             WLCORE_QUIRK_TKIP_HEADER_SPACE;
                wl->plt_fw_name = WL127X_PLT_FW_NAME;
                wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
                wl->mr_fw_name = WL127X_FW_NAME_MULTI;
@@ -660,6 +664,11 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
                wl->plt_fw_name = WL128X_PLT_FW_NAME;
                wl->sr_fw_name = WL128X_FW_NAME_SINGLE;
                wl->mr_fw_name = WL128X_FW_NAME_MULTI;
+
+               /* wl128x requires TX blocksize alignment */
+               wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
+                             WLCORE_QUIRK_TKIP_HEADER_SPACE;
+
                break;
        case CHIP_ID_1283_PG10:
        default:
@@ -773,6 +782,7 @@ static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk)
        u16 spare_reg;
        u16 pll_config;
        u8 input_freq;
+       struct wl12xx_priv *priv = wl->priv;
 
        /* Mask bits [3:1] in the sys_clk_cfg register */
        spare_reg = wl12xx_top_reg_read(wl, WL_SPARE_REG);
@@ -782,8 +792,8 @@ static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk)
        wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg);
 
        /* Handle special cases of the TCXO clock */
-       if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_8 ||
-           wl->tcxo_clock == WL12XX_TCXOCLOCK_33_6)
+       if (priv->tcxo_clock == WL12XX_TCXOCLOCK_16_8 ||
+           priv->tcxo_clock == WL12XX_TCXOCLOCK_33_6)
                return wl128x_manually_configure_mcs_pll(wl);
 
        /* Set the input frequency according to the selected clock source */
@@ -808,11 +818,12 @@ static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk)
  */
 static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock)
 {
+       struct wl12xx_priv *priv = wl->priv;
        u16 sys_clk_cfg;
 
        /* For XTAL-only modes, FREF will be used after switching from TCXO */
-       if (wl->ref_clock == WL12XX_REFCLOCK_26_XTAL ||
-           wl->ref_clock == WL12XX_REFCLOCK_38_XTAL) {
+       if (priv->ref_clock == WL12XX_REFCLOCK_26_XTAL ||
+           priv->ref_clock == WL12XX_REFCLOCK_38_XTAL) {
                if (!wl128x_switch_tcxo_to_fref(wl))
                        return -EINVAL;
                goto fref_clk;
@@ -826,8 +837,8 @@ static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock)
                goto fref_clk;
 
        /* If TCXO is either 32.736MHz or 16.368MHz, switch to FREF */
-       if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_368 ||
-           wl->tcxo_clock == WL12XX_TCXOCLOCK_32_736) {
+       if (priv->tcxo_clock == WL12XX_TCXOCLOCK_16_368 ||
+           priv->tcxo_clock == WL12XX_TCXOCLOCK_32_736) {
                if (!wl128x_switch_tcxo_to_fref(wl))
                        return -EINVAL;
                goto fref_clk;
@@ -836,14 +847,14 @@ static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock)
        /* TCXO clock is selected */
        if (!wl128x_is_tcxo_valid(wl))
                return -EINVAL;
-       *selected_clock = wl->tcxo_clock;
+       *selected_clock = priv->tcxo_clock;
        goto config_mcs_pll;
 
 fref_clk:
        /* FREF clock is selected */
        if (!wl128x_is_fref_valid(wl))
                return -EINVAL;
-       *selected_clock = wl->ref_clock;
+       *selected_clock = priv->ref_clock;
 
 config_mcs_pll:
        return wl128x_configure_mcs_pll(wl, *selected_clock);
@@ -851,25 +862,27 @@ config_mcs_pll:
 
 static int wl127x_boot_clk(struct wl1271 *wl)
 {
+       struct wl12xx_priv *priv = wl->priv;
        u32 pause;
        u32 clk;
 
        if (WL127X_PG_GET_MAJOR(wl->hw_pg_ver) < 3)
                wl->quirks |= WLCORE_QUIRK_END_OF_TRANSACTION;
 
-       if (wl->ref_clock == CONF_REF_CLK_19_2_E ||
-           wl->ref_clock == CONF_REF_CLK_38_4_E ||
-           wl->ref_clock == CONF_REF_CLK_38_4_M_XTAL)
+       if (priv->ref_clock == CONF_REF_CLK_19_2_E ||
+           priv->ref_clock == CONF_REF_CLK_38_4_E ||
+           priv->ref_clock == CONF_REF_CLK_38_4_M_XTAL)
                /* ref clk: 19.2/38.4/38.4-XTAL */
                clk = 0x3;
-       else if (wl->ref_clock == CONF_REF_CLK_26_E ||
-                wl->ref_clock == CONF_REF_CLK_52_E)
+       else if (priv->ref_clock == CONF_REF_CLK_26_E ||
+                priv->ref_clock == CONF_REF_CLK_26_M_XTAL ||
+                priv->ref_clock == CONF_REF_CLK_52_E)
                /* ref clk: 26/52 */
                clk = 0x5;
        else
                return -EINVAL;
 
-       if (wl->ref_clock != CONF_REF_CLK_19_2_E) {
+       if (priv->ref_clock != CONF_REF_CLK_19_2_E) {
                u16 val;
                /* Set clock type (open drain) */
                val = wl12xx_top_reg_read(wl, OCP_REG_CLK_TYPE);
@@ -939,6 +952,7 @@ static int wl1271_boot_soft_reset(struct wl1271 *wl)
 
 static int wl12xx_pre_boot(struct wl1271 *wl)
 {
+       struct wl12xx_priv *priv = wl->priv;
        int ret = 0;
        u32 clk;
        int selected_clock = -1;
@@ -970,7 +984,7 @@ static int wl12xx_pre_boot(struct wl1271 *wl)
        if (wl->chip.id == CHIP_ID_1283_PG20)
                clk |= ((selected_clock & 0x3) << 1) << 4;
        else
-               clk |= (wl->ref_clock << 1) << 4;
+               clk |= (priv->ref_clock << 1) << 4;
 
        wl1271_write32(wl, WL12XX_DRPW_SCRATCH_START, clk);
 
@@ -989,7 +1003,7 @@ out:
 
 static void wl12xx_pre_upload(struct wl1271 *wl)
 {
-       u32 tmp;
+       u32 tmp, polarity;
 
        /* write firmware's last address (ie. it's length) to
         * ACX_EEPROMLESS_IND_REG */
@@ -1009,23 +1023,23 @@ static void wl12xx_pre_upload(struct wl1271 *wl)
 
        if (wl->chip.id == CHIP_ID_1283_PG20)
                wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA);
-}
-
-static void wl12xx_enable_interrupts(struct wl1271 *wl)
-{
-       u32 polarity;
 
+       /* polarity must be set before the firmware is loaded */
        polarity = wl12xx_top_reg_read(wl, OCP_REG_POLARITY);
 
        /* We use HIGH polarity, so unset the LOW bit */
        polarity &= ~POLARITY_LOW;
        wl12xx_top_reg_write(wl, OCP_REG_POLARITY, polarity);
 
-       wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_ALL_EVENTS_VECTOR);
+}
+
+static void wl12xx_enable_interrupts(struct wl1271 *wl)
+{
+       wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL12XX_ACX_ALL_EVENTS_VECTOR);
 
        wlcore_enable_interrupts(wl);
        wlcore_write_reg(wl, REG_INTERRUPT_MASK,
-                        WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK));
+                        WL1271_ACX_INTR_ALL & ~(WL12XX_INTR_MASK));
 
        wl1271_write32(wl, WL12XX_HI_CFG, HI_CFG_DEF_VAL);
 }
@@ -1149,7 +1163,8 @@ static u32 wl12xx_get_rx_packet_len(struct wl1271 *wl, void *rx_data,
 
 static void wl12xx_tx_delayed_compl(struct wl1271 *wl)
 {
-       if (wl->fw_status->tx_results_counter == (wl->tx_results_count & 0xff))
+       if (wl->fw_status_1->tx_results_counter ==
+           (wl->tx_results_count & 0xff))
                return;
 
        wl1271_tx_complete(wl);
@@ -1288,10 +1303,90 @@ static void wl12xx_get_mac(struct wl1271 *wl)
                wl12xx_get_fuse_mac(wl);
 }
 
+static void wl12xx_set_tx_desc_csum(struct wl1271 *wl,
+                                   struct wl1271_tx_hw_descr *desc,
+                                   struct sk_buff *skb)
+{
+       desc->wl12xx_reserved = 0;
+}
+
+static int wl12xx_plt_init(struct wl1271 *wl)
+{
+       int ret;
+
+       ret = wl->ops->boot(wl);
+       if (ret < 0)
+               goto out;
+
+       ret = wl->ops->hw_init(wl);
+       if (ret < 0)
+               goto out_irq_disable;
+
+       ret = wl1271_acx_init_mem_config(wl);
+       if (ret < 0)
+               goto out_irq_disable;
+
+       ret = wl12xx_acx_mem_cfg(wl);
+       if (ret < 0)
+               goto out_free_memmap;
+
+       /* Enable data path */
+       ret = wl1271_cmd_data_path(wl, 1);
+       if (ret < 0)
+               goto out_free_memmap;
+
+       /* Configure for CAM power saving (ie. always active) */
+       ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
+       if (ret < 0)
+               goto out_free_memmap;
+
+       /* configure PM */
+       ret = wl1271_acx_pm_config(wl);
+       if (ret < 0)
+               goto out_free_memmap;
+
+       goto out;
+
+out_free_memmap:
+       kfree(wl->target_mem_map);
+       wl->target_mem_map = NULL;
+
+out_irq_disable:
+       mutex_unlock(&wl->mutex);
+       /* Unlocking the mutex in the middle of handling is
+          inherently unsafe. In this case we deem it safe to do,
+          because we need to let any possibly pending IRQ out of
+          the system (and while we are WL1271_STATE_OFF the IRQ
+          work function will not do anything.) Also, any other
+          possible concurrent operations will fail due to the
+          current state, hence the wl1271 struct should be safe. */
+       wlcore_disable_interrupts(wl);
+       mutex_lock(&wl->mutex);
+out:
+       return ret;
+}
+
+static int wl12xx_get_spare_blocks(struct wl1271 *wl, bool is_gem)
+{
+       if (is_gem)
+               return WL12XX_TX_HW_BLOCK_GEM_SPARE;
+
+       return WL12XX_TX_HW_BLOCK_SPARE_DEFAULT;
+}
+
+static int wl12xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
+                         struct ieee80211_vif *vif,
+                         struct ieee80211_sta *sta,
+                         struct ieee80211_key_conf *key_conf)
+{
+       return wlcore_set_key(wl, cmd, vif, sta, key_conf);
+}
+
 static struct wlcore_ops wl12xx_ops = {
        .identify_chip          = wl12xx_identify_chip,
        .identify_fw            = wl12xx_identify_fw,
        .boot                   = wl12xx_boot,
+       .plt_init               = wl12xx_plt_init,
        .trigger_cmd            = wl12xx_trigger_cmd,
        .ack_event              = wl12xx_ack_event,
        .calc_tx_blocks         = wl12xx_calc_tx_blocks,
@@ -1306,6 +1401,13 @@ static struct wlcore_ops wl12xx_ops = {
        .sta_get_ap_rate_mask   = wl12xx_sta_get_ap_rate_mask,
        .get_pg_ver             = wl12xx_get_pg_ver,
        .get_mac                = wl12xx_get_mac,
+       .set_tx_desc_csum       = wl12xx_set_tx_desc_csum,
+       .set_rx_csum            = NULL,
+       .ap_get_mimo_wide_rate_mask = NULL,
+       .debugfs_init           = wl12xx_debugfs_add_files,
+       .get_spare_blocks       = wl12xx_get_spare_blocks,
+       .set_key                = wl12xx_set_key,
+       .pre_pkt_send           = NULL,
 };
 
 static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
@@ -1323,6 +1425,7 @@ static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
 
 static int __devinit wl12xx_probe(struct platform_device *pdev)
 {
+       struct wl12xx_platform_data *pdata = pdev->dev.platform_data;
        struct wl1271 *wl;
        struct ieee80211_hw *hw;
        struct wl12xx_priv *priv;
@@ -1334,19 +1437,65 @@ static int __devinit wl12xx_probe(struct platform_device *pdev)
        }
 
        wl = hw->priv;
+       priv = wl->priv;
        wl->ops = &wl12xx_ops;
        wl->ptable = wl12xx_ptable;
        wl->rtable = wl12xx_rtable;
        wl->num_tx_desc = 16;
-       wl->normal_tx_spare = WL12XX_TX_HW_BLOCK_SPARE_DEFAULT;
-       wl->gem_tx_spare = WL12XX_TX_HW_BLOCK_GEM_SPARE;
+       wl->num_rx_desc = 8;
        wl->band_rate_to_idx = wl12xx_band_rate_to_idx;
        wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX;
        wl->hw_min_ht_rate = WL12XX_CONF_HW_RXTX_RATE_MCS0;
        wl->fw_status_priv_len = 0;
-       memcpy(&wl->ht_cap, &wl12xx_ht_cap, sizeof(wl12xx_ht_cap));
+       wl->stats.fw_stats_len = sizeof(struct wl12xx_acx_statistics);
+       memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ], &wl12xx_ht_cap,
+              sizeof(wl12xx_ht_cap));
+       memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ], &wl12xx_ht_cap,
+              sizeof(wl12xx_ht_cap));
        wl12xx_conf_init(wl);
 
+       if (!fref_param) {
+               priv->ref_clock = pdata->board_ref_clock;
+       } else {
+               if (!strcmp(fref_param, "19.2"))
+                       priv->ref_clock = WL12XX_REFCLOCK_19;
+               else if (!strcmp(fref_param, "26"))
+                       priv->ref_clock = WL12XX_REFCLOCK_26;
+               else if (!strcmp(fref_param, "26x"))
+                       priv->ref_clock = WL12XX_REFCLOCK_26_XTAL;
+               else if (!strcmp(fref_param, "38.4"))
+                       priv->ref_clock = WL12XX_REFCLOCK_38;
+               else if (!strcmp(fref_param, "38.4x"))
+                       priv->ref_clock = WL12XX_REFCLOCK_38_XTAL;
+               else if (!strcmp(fref_param, "52"))
+                       priv->ref_clock = WL12XX_REFCLOCK_52;
+               else
+                       wl1271_error("Invalid fref parameter %s", fref_param);
+       }
+
+       if (!tcxo_param) {
+               priv->tcxo_clock = pdata->board_tcxo_clock;
+       } else {
+               if (!strcmp(tcxo_param, "19.2"))
+                       priv->tcxo_clock = WL12XX_TCXOCLOCK_19_2;
+               else if (!strcmp(tcxo_param, "26"))
+                       priv->tcxo_clock = WL12XX_TCXOCLOCK_26;
+               else if (!strcmp(tcxo_param, "38.4"))
+                       priv->tcxo_clock = WL12XX_TCXOCLOCK_38_4;
+               else if (!strcmp(tcxo_param, "52"))
+                       priv->tcxo_clock = WL12XX_TCXOCLOCK_52;
+               else if (!strcmp(tcxo_param, "16.368"))
+                       priv->tcxo_clock = WL12XX_TCXOCLOCK_16_368;
+               else if (!strcmp(tcxo_param, "32.736"))
+                       priv->tcxo_clock = WL12XX_TCXOCLOCK_32_736;
+               else if (!strcmp(tcxo_param, "16.8"))
+                       priv->tcxo_clock = WL12XX_TCXOCLOCK_16_8;
+               else if (!strcmp(tcxo_param, "33.6"))
+                       priv->tcxo_clock = WL12XX_TCXOCLOCK_33_6;
+               else
+                       wl1271_error("Invalid tcxo parameter %s", tcxo_param);
+       }
+
        return wlcore_probe(wl, pdev);
 }
 
@@ -1378,6 +1527,13 @@ static void __exit wl12xx_exit(void)
 }
 module_exit(wl12xx_exit);
 
+module_param_named(fref, fref_param, charp, 0);
+MODULE_PARM_DESC(fref, "FREF clock: 19.2, 26, 26x, 38.4, 38.4x, 52");
+
+module_param_named(tcxo, tcxo_param, charp, 0);
+MODULE_PARM_DESC(tcxo,
+                "TCXO clock: 19.2, 26, 38.4, 52, 16.368, 32.736, 16.8, 33.6");
+
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
 MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE);
index 74cd332..de11324 100644 (file)
 
 #include "conf.h"
 
+struct wl127x_rx_mem_pool_addr {
+       u32 addr;
+       u32 addr_extra;
+};
+
 struct wl12xx_priv {
        struct wl12xx_priv_conf conf;
+
+       int ref_clock;
+       int tcxo_clock;
 };
 
 #endif /* __WL12XX_PRIV_H__ */
diff --git a/drivers/net/wireless/ti/wl18xx/Kconfig b/drivers/net/wireless/ti/wl18xx/Kconfig
new file mode 100644 (file)
index 0000000..1cfdb25
--- /dev/null
@@ -0,0 +1,7 @@
+config WL18XX
+       tristate "TI wl18xx support"
+       depends on MAC80211
+       select WLCORE
+       ---help---
+         This module adds support for wireless adapters based on TI
+         WiLink 8 chipsets.
diff --git a/drivers/net/wireless/ti/wl18xx/Makefile b/drivers/net/wireless/ti/wl18xx/Makefile
new file mode 100644 (file)
index 0000000..67c0987
--- /dev/null
@@ -0,0 +1,3 @@
+wl18xx-objs    = main.o acx.o tx.o io.o debugfs.o
+
+obj-$(CONFIG_WL18XX)           += wl18xx.o
diff --git a/drivers/net/wireless/ti/wl18xx/acx.c b/drivers/net/wireless/ti/wl18xx/acx.c
new file mode 100644 (file)
index 0000000..72840e2
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "../wlcore/cmd.h"
+#include "../wlcore/debug.h"
+#include "../wlcore/acx.h"
+
+#include "acx.h"
+
+int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap,
+                                 u32 sdio_blk_size, u32 extra_mem_blks,
+                                 u32 len_field_size)
+{
+       struct wl18xx_acx_host_config_bitmap *bitmap_conf;
+       int ret;
+
+       wl1271_debug(DEBUG_ACX, "acx cfg bitmap %d blk %d spare %d field %d",
+                    host_cfg_bitmap, sdio_blk_size, extra_mem_blks,
+                    len_field_size);
+
+       bitmap_conf = kzalloc(sizeof(*bitmap_conf), GFP_KERNEL);
+       if (!bitmap_conf) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       bitmap_conf->host_cfg_bitmap = cpu_to_le32(host_cfg_bitmap);
+       bitmap_conf->host_sdio_block_size = cpu_to_le32(sdio_blk_size);
+       bitmap_conf->extra_mem_blocks = cpu_to_le32(extra_mem_blks);
+       bitmap_conf->length_field_size = cpu_to_le32(len_field_size);
+
+       ret = wl1271_cmd_configure(wl, ACX_HOST_IF_CFG_BITMAP,
+                                  bitmap_conf, sizeof(*bitmap_conf));
+       if (ret < 0) {
+               wl1271_warning("wl1271 bitmap config opt failed: %d", ret);
+               goto out;
+       }
+
+out:
+       kfree(bitmap_conf);
+
+       return ret;
+}
+
+int wl18xx_acx_set_checksum_state(struct wl1271 *wl)
+{
+       struct wl18xx_acx_checksum_state *acx;
+       int ret;
+
+       wl1271_debug(DEBUG_ACX, "acx checksum state");
+
+       acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+       if (!acx) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       acx->checksum_state = CHECKSUM_OFFLOAD_ENABLED;
+
+       ret = wl1271_cmd_configure(wl, ACX_CHECKSUM_CONFIG, acx, sizeof(*acx));
+       if (ret < 0) {
+               wl1271_warning("failed to set Tx checksum state: %d", ret);
+               goto out;
+       }
+
+out:
+       kfree(acx);
+       return ret;
+}
+
+int wl18xx_acx_clear_statistics(struct wl1271 *wl)
+{
+       struct wl18xx_acx_clear_statistics *acx;
+       int ret = 0;
+
+       wl1271_debug(DEBUG_ACX, "acx clear statistics");
+
+       acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+       if (!acx) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       ret = wl1271_cmd_configure(wl, ACX_CLEAR_STATISTICS, acx, sizeof(*acx));
+       if (ret < 0) {
+               wl1271_warning("failed to clear firmware statistics: %d", ret);
+               goto out;
+       }
+
+out:
+       kfree(acx);
+       return ret;
+}
diff --git a/drivers/net/wireless/ti/wl18xx/acx.h b/drivers/net/wireless/ti/wl18xx/acx.h
new file mode 100644 (file)
index 0000000..ebbaf61
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL18XX_ACX_H__
+#define __WL18XX_ACX_H__
+
+#include "../wlcore/wlcore.h"
+#include "../wlcore/acx.h"
+
+enum {
+       ACX_CLEAR_STATISTICS             = 0x0047,
+};
+
+/* numbers of bits the length field takes (add 1 for the actual number) */
+#define WL18XX_HOST_IF_LEN_SIZE_FIELD 15
+
+#define WL18XX_ACX_EVENTS_VECTOR_PG1   (WL1271_ACX_INTR_WATCHDOG      | \
+                                       WL1271_ACX_INTR_INIT_COMPLETE | \
+                                       WL1271_ACX_INTR_EVENT_A       | \
+                                       WL1271_ACX_INTR_EVENT_B       | \
+                                       WL1271_ACX_INTR_CMD_COMPLETE  | \
+                                       WL1271_ACX_INTR_HW_AVAILABLE  | \
+                                       WL1271_ACX_INTR_DATA)
+
+#define WL18XX_ACX_EVENTS_VECTOR_PG2   (WL18XX_ACX_EVENTS_VECTOR_PG1 | \
+                                       WL1271_ACX_SW_INTR_WATCHDOG)
+
+#define WL18XX_INTR_MASK_PG1           (WL1271_ACX_INTR_WATCHDOG      | \
+                                       WL1271_ACX_INTR_EVENT_A       | \
+                                       WL1271_ACX_INTR_EVENT_B       | \
+                                       WL1271_ACX_INTR_HW_AVAILABLE  | \
+                                       WL1271_ACX_INTR_DATA)
+
+#define WL18XX_INTR_MASK_PG2           (WL18XX_INTR_MASK_PG1         | \
+                                       WL1271_ACX_SW_INTR_WATCHDOG)
+
+struct wl18xx_acx_host_config_bitmap {
+       struct acx_header header;
+
+       __le32 host_cfg_bitmap;
+
+       __le32 host_sdio_block_size;
+
+       /* extra mem blocks per frame in TX. */
+       __le32 extra_mem_blocks;
+
+       /*
+        * number of bits of the length field in the first TX word
+        * (up to 15 - for using the entire 16 bits).
+        */
+       __le32 length_field_size;
+
+} __packed;
+
+enum {
+       CHECKSUM_OFFLOAD_DISABLED = 0,
+       CHECKSUM_OFFLOAD_ENABLED  = 1,
+       CHECKSUM_OFFLOAD_FAKE_RX  = 2,
+       CHECKSUM_OFFLOAD_INVALID  = 0xFF
+};
+
+struct wl18xx_acx_checksum_state {
+       struct acx_header header;
+
+        /* enum acx_checksum_state */
+       u8 checksum_state;
+       u8 pad[3];
+} __packed;
+
+
+struct wl18xx_acx_error_stats {
+       u32 error_frame;
+       u32 error_null_Frame_tx_start;
+       u32 error_numll_frame_cts_start;
+       u32 error_bar_retry;
+       u32 error_frame_cts_nul_flid;
+} __packed;
+
+struct wl18xx_acx_debug_stats {
+       u32 debug1;
+       u32 debug2;
+       u32 debug3;
+       u32 debug4;
+       u32 debug5;
+       u32 debug6;
+} __packed;
+
+struct wl18xx_acx_ring_stats {
+       u32 prepared_descs;
+       u32 tx_cmplt;
+} __packed;
+
+struct wl18xx_acx_tx_stats {
+       u32 tx_prepared_descs;
+       u32 tx_cmplt;
+       u32 tx_template_prepared;
+       u32 tx_data_prepared;
+       u32 tx_template_programmed;
+       u32 tx_data_programmed;
+       u32 tx_burst_programmed;
+       u32 tx_starts;
+       u32 tx_imm_resp;
+       u32 tx_start_templates;
+       u32 tx_start_int_templates;
+       u32 tx_start_fw_gen;
+       u32 tx_start_data;
+       u32 tx_start_null_frame;
+       u32 tx_exch;
+       u32 tx_retry_template;
+       u32 tx_retry_data;
+       u32 tx_exch_pending;
+       u32 tx_exch_expiry;
+       u32 tx_done_template;
+       u32 tx_done_data;
+       u32 tx_done_int_template;
+       u32 tx_frame_checksum;
+       u32 tx_checksum_result;
+       u32 frag_called;
+       u32 frag_mpdu_alloc_failed;
+       u32 frag_init_called;
+       u32 frag_in_process_called;
+       u32 frag_tkip_called;
+       u32 frag_key_not_found;
+       u32 frag_need_fragmentation;
+       u32 frag_bad_mblk_num;
+       u32 frag_failed;
+       u32 frag_cache_hit;
+       u32 frag_cache_miss;
+} __packed;
+
+struct wl18xx_acx_rx_stats {
+       u32 rx_beacon_early_term;
+       u32 rx_out_of_mpdu_nodes;
+       u32 rx_hdr_overflow;
+       u32 rx_dropped_frame;
+       u32 rx_done_stage;
+       u32 rx_done;
+       u32 rx_defrag;
+       u32 rx_defrag_end;
+       u32 rx_cmplt;
+       u32 rx_pre_complt;
+       u32 rx_cmplt_task;
+       u32 rx_phy_hdr;
+       u32 rx_timeout;
+       u32 rx_timeout_wa;
+       u32 rx_wa_density_dropped_frame;
+       u32 rx_wa_ba_not_expected;
+       u32 rx_frame_checksum;
+       u32 rx_checksum_result;
+       u32 defrag_called;
+       u32 defrag_init_called;
+       u32 defrag_in_process_called;
+       u32 defrag_tkip_called;
+       u32 defrag_need_defrag;
+       u32 defrag_decrypt_failed;
+       u32 decrypt_key_not_found;
+       u32 defrag_need_decrypt;
+       u32 rx_tkip_replays;
+} __packed;
+
+struct wl18xx_acx_isr_stats {
+       u32 irqs;
+} __packed;
+
+#define PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD 10
+
+struct wl18xx_acx_pwr_stats {
+       u32 missing_bcns_cnt;
+       u32 rcvd_bcns_cnt;
+       u32 connection_out_of_sync;
+       u32 cont_miss_bcns_spread[PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD];
+       u32 rcvd_awake_bcns_cnt;
+} __packed;
+
+struct wl18xx_acx_event_stats {
+       u32 calibration;
+       u32 rx_mismatch;
+       u32 rx_mem_empty;
+} __packed;
+
+struct wl18xx_acx_ps_poll_stats {
+       u32 ps_poll_timeouts;
+       u32 upsd_timeouts;
+       u32 upsd_max_ap_turn;
+       u32 ps_poll_max_ap_turn;
+       u32 ps_poll_utilization;
+       u32 upsd_utilization;
+} __packed;
+
+struct wl18xx_acx_rx_filter_stats {
+       u32 beacon_filter;
+       u32 arp_filter;
+       u32 mc_filter;
+       u32 dup_filter;
+       u32 data_filter;
+       u32 ibss_filter;
+       u32 protection_filter;
+       u32 accum_arp_pend_requests;
+       u32 max_arp_queue_dep;
+} __packed;
+
+struct wl18xx_acx_rx_rate_stats {
+       u32 rx_frames_per_rates[50];
+} __packed;
+
+#define AGGR_STATS_TX_AGG      16
+#define AGGR_STATS_TX_RATE     16
+#define AGGR_STATS_RX_SIZE_LEN 16
+
+struct wl18xx_acx_aggr_stats {
+       u32 tx_agg_vs_rate[AGGR_STATS_TX_AGG * AGGR_STATS_TX_RATE];
+       u32 rx_size[AGGR_STATS_RX_SIZE_LEN];
+} __packed;
+
+#define PIPE_STATS_HW_FIFO     11
+
+struct wl18xx_acx_pipeline_stats {
+       u32 hs_tx_stat_fifo_int;
+       u32 hs_rx_stat_fifo_int;
+       u32 tcp_tx_stat_fifo_int;
+       u32 tcp_rx_stat_fifo_int;
+       u32 enc_tx_stat_fifo_int;
+       u32 enc_rx_stat_fifo_int;
+       u32 rx_complete_stat_fifo_int;
+       u32 pre_proc_swi;
+       u32 post_proc_swi;
+       u32 sec_frag_swi;
+       u32 pre_to_defrag_swi;
+       u32 defrag_to_csum_swi;
+       u32 csum_to_rx_xfer_swi;
+       u32 dec_packet_in;
+       u32 dec_packet_in_fifo_full;
+       u32 dec_packet_out;
+       u32 cs_rx_packet_in;
+       u32 cs_rx_packet_out;
+       u16 pipeline_fifo_full[PIPE_STATS_HW_FIFO];
+} __packed;
+
+struct wl18xx_acx_mem_stats {
+       u32 rx_free_mem_blks;
+       u32 tx_free_mem_blks;
+       u32 fwlog_free_mem_blks;
+       u32 fw_gen_free_mem_blks;
+} __packed;
+
+struct wl18xx_acx_statistics {
+       struct acx_header header;
+
+       struct wl18xx_acx_error_stats           error;
+       struct wl18xx_acx_debug_stats           debug;
+       struct wl18xx_acx_tx_stats              tx;
+       struct wl18xx_acx_rx_stats              rx;
+       struct wl18xx_acx_isr_stats             isr;
+       struct wl18xx_acx_pwr_stats             pwr;
+       struct wl18xx_acx_ps_poll_stats         ps_poll;
+       struct wl18xx_acx_rx_filter_stats       rx_filter;
+       struct wl18xx_acx_rx_rate_stats         rx_rate;
+       struct wl18xx_acx_aggr_stats            aggr_size;
+       struct wl18xx_acx_pipeline_stats        pipeline;
+       struct wl18xx_acx_mem_stats             mem;
+} __packed;
+
+struct wl18xx_acx_clear_statistics {
+       struct acx_header header;
+};
+
+int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap,
+                                 u32 sdio_blk_size, u32 extra_mem_blks,
+                                 u32 len_field_size);
+int wl18xx_acx_set_checksum_state(struct wl1271 *wl);
+int wl18xx_acx_clear_statistics(struct wl1271 *wl);
+
+#endif /* __WL18XX_ACX_H__ */
diff --git a/drivers/net/wireless/ti/wl18xx/conf.h b/drivers/net/wireless/ti/wl18xx/conf.h
new file mode 100644 (file)
index 0000000..fac0b7e
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL18XX_CONF_H__
+#define __WL18XX_CONF_H__
+
+#define WL18XX_CONF_MAGIC      0x10e100ca
+#define WL18XX_CONF_VERSION    (WLCORE_CONF_VERSION | 0x0002)
+#define WL18XX_CONF_MASK       0x0000ffff
+#define WL18XX_CONF_SIZE       (WLCORE_CONF_SIZE + \
+                                sizeof(struct wl18xx_priv_conf))
+
+#define NUM_OF_CHANNELS_11_ABG 150
+#define NUM_OF_CHANNELS_11_P 7
+#define WL18XX_NUM_OF_SUB_BANDS 9
+#define SRF_TABLE_LEN 16
+#define PIN_MUXING_SIZE 2
+
+struct wl18xx_mac_and_phy_params {
+       u8 phy_standalone;
+       u8 rdl;
+       u8 enable_clpc;
+       u8 enable_tx_low_pwr_on_siso_rdl;
+       u8 auto_detect;
+       u8 dedicated_fem;
+
+       u8 low_band_component;
+
+       /* Bit 0: One Hot, Bit 1: Control Enable, Bit 2: 1.8V, Bit 3: 3V */
+       u8 low_band_component_type;
+
+       u8 high_band_component;
+
+       /* Bit 0: One Hot, Bit 1: Control Enable, Bit 2: 1.8V, Bit 3: 3V */
+       u8 high_band_component_type;
+       u8 number_of_assembled_ant2_4;
+       u8 number_of_assembled_ant5;
+       u8 pin_muxing_platform_options[PIN_MUXING_SIZE];
+       u8 external_pa_dc2dc;
+       u8 tcxo_ldo_voltage;
+       u8 xtal_itrim_val;
+       u8 srf_state;
+       u8 srf1[SRF_TABLE_LEN];
+       u8 srf2[SRF_TABLE_LEN];
+       u8 srf3[SRF_TABLE_LEN];
+       u8 io_configuration;
+       u8 sdio_configuration;
+       u8 settings;
+       u8 rx_profile;
+       u8 per_chan_pwr_limit_arr_11abg[NUM_OF_CHANNELS_11_ABG];
+       u8 pwr_limit_reference_11_abg;
+       u8 per_chan_pwr_limit_arr_11p[NUM_OF_CHANNELS_11_P];
+       u8 pwr_limit_reference_11p;
+       u8 per_sub_band_tx_trace_loss[WL18XX_NUM_OF_SUB_BANDS];
+       u8 per_sub_band_rx_trace_loss[WL18XX_NUM_OF_SUB_BANDS];
+       u8 primary_clock_setting_time;
+       u8 clock_valid_on_wake_up;
+       u8 secondary_clock_setting_time;
+       u8 board_type;
+       /* enable point saturation */
+       u8 psat;
+       /* low/medium/high Tx power in dBm */
+       s8 low_power_val;
+       s8 med_power_val;
+       s8 high_power_val;
+       u8 padding[1];
+} __packed;
+
+struct wl18xx_priv_conf {
+       /* this structure is copied wholesale to FW */
+       struct wl18xx_mac_and_phy_params phy;
+} __packed;
+
+#endif /* __WL18XX_CONF_H__ */
diff --git a/drivers/net/wireless/ti/wl18xx/debugfs.c b/drivers/net/wireless/ti/wl18xx/debugfs.c
new file mode 100644 (file)
index 0000000..3ce6f10
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Copyright (C) 2011-2012 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "../wlcore/debugfs.h"
+#include "../wlcore/wlcore.h"
+
+#include "wl18xx.h"
+#include "acx.h"
+#include "debugfs.h"
+
+#define WL18XX_DEBUGFS_FWSTATS_FILE(a, b, c) \
+       DEBUGFS_FWSTATS_FILE(a, b, c, wl18xx_acx_statistics)
+#define WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(a, b, c) \
+       DEBUGFS_FWSTATS_FILE_ARRAY(a, b, c, wl18xx_acx_statistics)
+
+
+WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug1, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug2, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug3, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug4, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug5, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug6, "%u");
+
+WL18XX_DEBUGFS_FWSTATS_FILE(error, error_frame, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(error, error_null_Frame_tx_start, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(error, error_numll_frame_cts_start, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(error, error_bar_retry, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(error, error_frame_cts_nul_flid, "%u");
+
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_prepared_descs, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_cmplt, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_template_prepared, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_data_prepared, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_template_programmed, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_data_programmed, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_burst_programmed, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_starts, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_imm_resp, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_templates, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_int_templates, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_fw_gen, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_data, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_null_frame, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_retry_template, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_retry_data, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch_pending, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch_expiry, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_template, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_data, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_int_template, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_frame_checksum, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_checksum_result, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_called, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_mpdu_alloc_failed, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_init_called, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_in_process_called, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_tkip_called, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_key_not_found, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_need_fragmentation, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_bad_mblk_num, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_failed, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_cache_hit, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_cache_miss, "%u");
+
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_beacon_early_term, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_out_of_mpdu_nodes, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_hdr_overflow, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_dropped_frame, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_done, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_defrag, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_defrag_end, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_cmplt, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_pre_complt, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_cmplt_task, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_phy_hdr, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_timeout, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_timeout_wa, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_wa_density_dropped_frame, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_wa_ba_not_expected, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_frame_checksum, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_checksum_result, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_called, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_init_called, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_in_process_called, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_tkip_called, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_need_defrag, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_decrypt_failed, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, decrypt_key_not_found, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_need_decrypt, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_tkip_replays, "%u");
+
+WL18XX_DEBUGFS_FWSTATS_FILE(isr, irqs, "%u");
+
+WL18XX_DEBUGFS_FWSTATS_FILE(pwr, missing_bcns_cnt, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_bcns_cnt, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pwr, connection_out_of_sync, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(pwr, cont_miss_bcns_spread,
+                                 PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD);
+WL18XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_bcns_cnt, "%u");
+
+
+WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, ps_poll_timeouts, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, upsd_timeouts, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, upsd_max_ap_turn, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, ps_poll_max_ap_turn, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, ps_poll_utilization, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, upsd_utilization, "%u");
+
+WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, beacon_filter, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, arp_filter, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, mc_filter, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, dup_filter, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, data_filter, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, ibss_filter, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, protection_filter, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, accum_arp_pend_requests, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, max_arp_queue_dep, "%u");
+
+WL18XX_DEBUGFS_FWSTATS_FILE(rx_rate, rx_frames_per_rates, "%u");
+
+WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, tx_agg_vs_rate,
+                                 AGGR_STATS_TX_AGG*AGGR_STATS_TX_RATE);
+WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, rx_size,
+                                 AGGR_STATS_RX_SIZE_LEN);
+
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, hs_tx_stat_fifo_int, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, tcp_tx_stat_fifo_int, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, tcp_rx_stat_fifo_int, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, enc_tx_stat_fifo_int, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, enc_rx_stat_fifo_int, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, rx_complete_stat_fifo_int, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, pre_proc_swi, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, post_proc_swi, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, sec_frag_swi, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, pre_to_defrag_swi, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, defrag_to_csum_swi, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, csum_to_rx_xfer_swi, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_in, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_in_fifo_full, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_out, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, cs_rx_packet_in, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, cs_rx_packet_out, "%u");
+
+WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(pipeline, pipeline_fifo_full,
+                                 PIPE_STATS_HW_FIFO);
+
+WL18XX_DEBUGFS_FWSTATS_FILE(mem, rx_free_mem_blks, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(mem, tx_free_mem_blks, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(mem, fwlog_free_mem_blks, "%u");
+WL18XX_DEBUGFS_FWSTATS_FILE(mem, fw_gen_free_mem_blks, "%u");
+
+static ssize_t conf_read(struct file *file, char __user *user_buf,
+                        size_t count, loff_t *ppos)
+{
+       struct wl1271 *wl = file->private_data;
+       struct wl18xx_priv *priv = wl->priv;
+       struct wlcore_conf_header header;
+       char *buf, *pos;
+       size_t len;
+       int ret;
+
+       len = WL18XX_CONF_SIZE;
+       buf = kmalloc(len, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       header.magic    = cpu_to_le32(WL18XX_CONF_MAGIC);
+       header.version  = cpu_to_le32(WL18XX_CONF_VERSION);
+       header.checksum = 0;
+
+       mutex_lock(&wl->mutex);
+
+       pos = buf;
+       memcpy(pos, &header, sizeof(header));
+       pos += sizeof(header);
+       memcpy(pos, &wl->conf, sizeof(wl->conf));
+       pos += sizeof(wl->conf);
+       memcpy(pos, &priv->conf, sizeof(priv->conf));
+
+       mutex_unlock(&wl->mutex);
+
+       ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+       kfree(buf);
+       return ret;
+}
+
+static const struct file_operations conf_ops = {
+       .read = conf_read,
+       .open = simple_open,
+       .llseek = default_llseek,
+};
+
+static ssize_t clear_fw_stats_write(struct file *file,
+                             const char __user *user_buf,
+                             size_t count, loff_t *ppos)
+{
+       struct wl1271 *wl = file->private_data;
+       int ret;
+
+       mutex_lock(&wl->mutex);
+
+       if (wl->state == WL1271_STATE_OFF)
+               goto out;
+
+       ret = wl18xx_acx_clear_statistics(wl);
+       if (ret < 0) {
+               count = ret;
+               goto out;
+       }
+out:
+       mutex_unlock(&wl->mutex);
+       return count;
+}
+
+static const struct file_operations clear_fw_stats_ops = {
+       .write = clear_fw_stats_write,
+       .open = simple_open,
+       .llseek = default_llseek,
+};
+
+int wl18xx_debugfs_add_files(struct wl1271 *wl,
+                            struct dentry *rootdir)
+{
+       int ret = 0;
+       struct dentry *entry, *stats, *moddir;
+
+       moddir = debugfs_create_dir(KBUILD_MODNAME, rootdir);
+       if (!moddir || IS_ERR(moddir)) {
+               entry = moddir;
+               goto err;
+       }
+
+       stats = debugfs_create_dir("fw_stats", moddir);
+       if (!stats || IS_ERR(stats)) {
+               entry = stats;
+               goto err;
+       }
+
+       DEBUGFS_ADD(clear_fw_stats, stats);
+
+       DEBUGFS_FWSTATS_ADD(debug, debug1);
+       DEBUGFS_FWSTATS_ADD(debug, debug2);
+       DEBUGFS_FWSTATS_ADD(debug, debug3);
+       DEBUGFS_FWSTATS_ADD(debug, debug4);
+       DEBUGFS_FWSTATS_ADD(debug, debug5);
+       DEBUGFS_FWSTATS_ADD(debug, debug6);
+
+       DEBUGFS_FWSTATS_ADD(error, error_frame);
+       DEBUGFS_FWSTATS_ADD(error, error_null_Frame_tx_start);
+       DEBUGFS_FWSTATS_ADD(error, error_numll_frame_cts_start);
+       DEBUGFS_FWSTATS_ADD(error, error_bar_retry);
+       DEBUGFS_FWSTATS_ADD(error, error_frame_cts_nul_flid);
+
+       DEBUGFS_FWSTATS_ADD(tx, tx_prepared_descs);
+       DEBUGFS_FWSTATS_ADD(tx, tx_cmplt);
+       DEBUGFS_FWSTATS_ADD(tx, tx_template_prepared);
+       DEBUGFS_FWSTATS_ADD(tx, tx_data_prepared);
+       DEBUGFS_FWSTATS_ADD(tx, tx_template_programmed);
+       DEBUGFS_FWSTATS_ADD(tx, tx_data_programmed);
+       DEBUGFS_FWSTATS_ADD(tx, tx_burst_programmed);
+       DEBUGFS_FWSTATS_ADD(tx, tx_starts);
+       DEBUGFS_FWSTATS_ADD(tx, tx_imm_resp);
+       DEBUGFS_FWSTATS_ADD(tx, tx_start_templates);
+       DEBUGFS_FWSTATS_ADD(tx, tx_start_int_templates);
+       DEBUGFS_FWSTATS_ADD(tx, tx_start_fw_gen);
+       DEBUGFS_FWSTATS_ADD(tx, tx_start_data);
+       DEBUGFS_FWSTATS_ADD(tx, tx_start_null_frame);
+       DEBUGFS_FWSTATS_ADD(tx, tx_exch);
+       DEBUGFS_FWSTATS_ADD(tx, tx_retry_template);
+       DEBUGFS_FWSTATS_ADD(tx, tx_retry_data);
+       DEBUGFS_FWSTATS_ADD(tx, tx_exch_pending);
+       DEBUGFS_FWSTATS_ADD(tx, tx_exch_expiry);
+       DEBUGFS_FWSTATS_ADD(tx, tx_done_template);
+       DEBUGFS_FWSTATS_ADD(tx, tx_done_data);
+       DEBUGFS_FWSTATS_ADD(tx, tx_done_int_template);
+       DEBUGFS_FWSTATS_ADD(tx, tx_frame_checksum);
+       DEBUGFS_FWSTATS_ADD(tx, tx_checksum_result);
+       DEBUGFS_FWSTATS_ADD(tx, frag_called);
+       DEBUGFS_FWSTATS_ADD(tx, frag_mpdu_alloc_failed);
+       DEBUGFS_FWSTATS_ADD(tx, frag_init_called);
+       DEBUGFS_FWSTATS_ADD(tx, frag_in_process_called);
+       DEBUGFS_FWSTATS_ADD(tx, frag_tkip_called);
+       DEBUGFS_FWSTATS_ADD(tx, frag_key_not_found);
+       DEBUGFS_FWSTATS_ADD(tx, frag_need_fragmentation);
+       DEBUGFS_FWSTATS_ADD(tx, frag_bad_mblk_num);
+       DEBUGFS_FWSTATS_ADD(tx, frag_failed);
+       DEBUGFS_FWSTATS_ADD(tx, frag_cache_hit);
+       DEBUGFS_FWSTATS_ADD(tx, frag_cache_miss);
+
+       DEBUGFS_FWSTATS_ADD(rx, rx_beacon_early_term);
+       DEBUGFS_FWSTATS_ADD(rx, rx_out_of_mpdu_nodes);
+       DEBUGFS_FWSTATS_ADD(rx, rx_hdr_overflow);
+       DEBUGFS_FWSTATS_ADD(rx, rx_dropped_frame);
+       DEBUGFS_FWSTATS_ADD(rx, rx_done);
+       DEBUGFS_FWSTATS_ADD(rx, rx_defrag);
+       DEBUGFS_FWSTATS_ADD(rx, rx_defrag_end);
+       DEBUGFS_FWSTATS_ADD(rx, rx_cmplt);
+       DEBUGFS_FWSTATS_ADD(rx, rx_pre_complt);
+       DEBUGFS_FWSTATS_ADD(rx, rx_cmplt_task);
+       DEBUGFS_FWSTATS_ADD(rx, rx_phy_hdr);
+       DEBUGFS_FWSTATS_ADD(rx, rx_timeout);
+       DEBUGFS_FWSTATS_ADD(rx, rx_timeout_wa);
+       DEBUGFS_FWSTATS_ADD(rx, rx_wa_density_dropped_frame);
+       DEBUGFS_FWSTATS_ADD(rx, rx_wa_ba_not_expected);
+       DEBUGFS_FWSTATS_ADD(rx, rx_frame_checksum);
+       DEBUGFS_FWSTATS_ADD(rx, rx_checksum_result);
+       DEBUGFS_FWSTATS_ADD(rx, defrag_called);
+       DEBUGFS_FWSTATS_ADD(rx, defrag_init_called);
+       DEBUGFS_FWSTATS_ADD(rx, defrag_in_process_called);
+       DEBUGFS_FWSTATS_ADD(rx, defrag_tkip_called);
+       DEBUGFS_FWSTATS_ADD(rx, defrag_need_defrag);
+       DEBUGFS_FWSTATS_ADD(rx, defrag_decrypt_failed);
+       DEBUGFS_FWSTATS_ADD(rx, decrypt_key_not_found);
+       DEBUGFS_FWSTATS_ADD(rx, defrag_need_decrypt);
+       DEBUGFS_FWSTATS_ADD(rx, rx_tkip_replays);
+
+       DEBUGFS_FWSTATS_ADD(isr, irqs);
+
+       DEBUGFS_FWSTATS_ADD(pwr, missing_bcns_cnt);
+       DEBUGFS_FWSTATS_ADD(pwr, rcvd_bcns_cnt);
+       DEBUGFS_FWSTATS_ADD(pwr, connection_out_of_sync);
+       DEBUGFS_FWSTATS_ADD(pwr, cont_miss_bcns_spread);
+       DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_bcns_cnt);
+
+       DEBUGFS_FWSTATS_ADD(ps_poll, ps_poll_timeouts);
+       DEBUGFS_FWSTATS_ADD(ps_poll, upsd_timeouts);
+       DEBUGFS_FWSTATS_ADD(ps_poll, upsd_max_ap_turn);
+       DEBUGFS_FWSTATS_ADD(ps_poll, ps_poll_max_ap_turn);
+       DEBUGFS_FWSTATS_ADD(ps_poll, ps_poll_utilization);
+       DEBUGFS_FWSTATS_ADD(ps_poll, upsd_utilization);
+
+       DEBUGFS_FWSTATS_ADD(rx_filter, beacon_filter);
+       DEBUGFS_FWSTATS_ADD(rx_filter, arp_filter);
+       DEBUGFS_FWSTATS_ADD(rx_filter, mc_filter);
+       DEBUGFS_FWSTATS_ADD(rx_filter, dup_filter);
+       DEBUGFS_FWSTATS_ADD(rx_filter, data_filter);
+       DEBUGFS_FWSTATS_ADD(rx_filter, ibss_filter);
+       DEBUGFS_FWSTATS_ADD(rx_filter, protection_filter);
+       DEBUGFS_FWSTATS_ADD(rx_filter, accum_arp_pend_requests);
+       DEBUGFS_FWSTATS_ADD(rx_filter, max_arp_queue_dep);
+
+       DEBUGFS_FWSTATS_ADD(rx_rate, rx_frames_per_rates);
+
+       DEBUGFS_FWSTATS_ADD(aggr_size, tx_agg_vs_rate);
+       DEBUGFS_FWSTATS_ADD(aggr_size, rx_size);
+
+       DEBUGFS_FWSTATS_ADD(pipeline, hs_tx_stat_fifo_int);
+       DEBUGFS_FWSTATS_ADD(pipeline, tcp_tx_stat_fifo_int);
+       DEBUGFS_FWSTATS_ADD(pipeline, tcp_rx_stat_fifo_int);
+       DEBUGFS_FWSTATS_ADD(pipeline, enc_tx_stat_fifo_int);
+       DEBUGFS_FWSTATS_ADD(pipeline, enc_rx_stat_fifo_int);
+       DEBUGFS_FWSTATS_ADD(pipeline, rx_complete_stat_fifo_int);
+       DEBUGFS_FWSTATS_ADD(pipeline, pre_proc_swi);
+       DEBUGFS_FWSTATS_ADD(pipeline, post_proc_swi);
+       DEBUGFS_FWSTATS_ADD(pipeline, sec_frag_swi);
+       DEBUGFS_FWSTATS_ADD(pipeline, pre_to_defrag_swi);
+       DEBUGFS_FWSTATS_ADD(pipeline, defrag_to_csum_swi);
+       DEBUGFS_FWSTATS_ADD(pipeline, csum_to_rx_xfer_swi);
+       DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_in);
+       DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_in_fifo_full);
+       DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_out);
+       DEBUGFS_FWSTATS_ADD(pipeline, cs_rx_packet_in);
+       DEBUGFS_FWSTATS_ADD(pipeline, cs_rx_packet_out);
+       DEBUGFS_FWSTATS_ADD(pipeline, pipeline_fifo_full);
+
+       DEBUGFS_FWSTATS_ADD(mem, rx_free_mem_blks);
+       DEBUGFS_FWSTATS_ADD(mem, tx_free_mem_blks);
+       DEBUGFS_FWSTATS_ADD(mem, fwlog_free_mem_blks);
+       DEBUGFS_FWSTATS_ADD(mem, fw_gen_free_mem_blks);
+
+       DEBUGFS_ADD(conf, moddir);
+
+       return 0;
+
+err:
+       if (IS_ERR(entry))
+               ret = PTR_ERR(entry);
+       else
+               ret = -ENOMEM;
+
+       return ret;
+}
diff --git a/drivers/net/wireless/ti/wl18xx/debugfs.h b/drivers/net/wireless/ti/wl18xx/debugfs.h
new file mode 100644 (file)
index 0000000..ed679be
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL18XX_DEBUGFS_H__
+#define __WL18XX_DEBUGFS_H__
+
+int wl18xx_debugfs_add_files(struct wl1271 *wl,
+                            struct dentry *rootdir);
+
+#endif /* __WL18XX_DEBUGFS_H__ */
diff --git a/drivers/net/wireless/ti/wl18xx/io.c b/drivers/net/wireless/ti/wl18xx/io.c
new file mode 100644 (file)
index 0000000..598c057
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "../wlcore/wlcore.h"
+#include "../wlcore/io.h"
+
+#include "io.h"
+
+void wl18xx_top_reg_write(struct wl1271 *wl, int addr, u16 val)
+{
+       u32 tmp;
+
+       if (WARN_ON(addr % 2))
+               return;
+
+       if ((addr % 4) == 0) {
+               tmp = wl1271_read32(wl, addr);
+               tmp = (tmp & 0xffff0000) | val;
+               wl1271_write32(wl, addr, tmp);
+       } else {
+               tmp = wl1271_read32(wl, addr - 2);
+               tmp = (tmp & 0xffff) | (val << 16);
+               wl1271_write32(wl, addr - 2, tmp);
+       }
+}
+
+u16 wl18xx_top_reg_read(struct wl1271 *wl, int addr)
+{
+       u32 val;
+
+       if (WARN_ON(addr % 2))
+               return 0;
+
+       if ((addr % 4) == 0) {
+               /* address is 4-bytes aligned */
+               val = wl1271_read32(wl, addr);
+               return val & 0xffff;
+       } else {
+               val = wl1271_read32(wl, addr - 2);
+               return (val & 0xffff0000) >> 16;
+       }
+}
diff --git a/drivers/net/wireless/ti/wl18xx/io.h b/drivers/net/wireless/ti/wl18xx/io.h
new file mode 100644 (file)
index 0000000..be4e126
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL18XX_IO_H__
+#define __WL18XX_IO_H__
+
+void wl18xx_top_reg_write(struct wl1271 *wl, int addr, u16 val);
+u16 wl18xx_top_reg_read(struct wl1271 *wl, int addr);
+
+#endif /* __WL18XX_IO_H__ */
diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c
new file mode 100644 (file)
index 0000000..ed9c365
--- /dev/null
@@ -0,0 +1,1463 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/ip.h>
+#include <linux/firmware.h>
+
+#include "../wlcore/wlcore.h"
+#include "../wlcore/debug.h"
+#include "../wlcore/io.h"
+#include "../wlcore/acx.h"
+#include "../wlcore/tx.h"
+#include "../wlcore/rx.h"
+#include "../wlcore/io.h"
+#include "../wlcore/boot.h"
+
+#include "reg.h"
+#include "conf.h"
+#include "acx.h"
+#include "tx.h"
+#include "wl18xx.h"
+#include "io.h"
+#include "debugfs.h"
+
+#define WL18XX_RX_CHECKSUM_MASK      0x40
+
+static char *ht_mode_param = "wide";
+static char *board_type_param = "hdk";
+static bool checksum_param = false;
+static bool enable_11a_param = true;
+
+/* phy paramters */
+static int dc2dc_param = -1;
+static int n_antennas_2_param = -1;
+static int n_antennas_5_param = -1;
+static int low_band_component_param = -1;
+static int low_band_component_type_param = -1;
+static int high_band_component_param = -1;
+static int high_band_component_type_param = -1;
+static int pwr_limit_reference_11_abg_param = -1;
+
+static const u8 wl18xx_rate_to_idx_2ghz[] = {
+       /* MCS rates are used only with 11n */
+       15,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS15 */
+       14,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS14 */
+       13,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS13 */
+       12,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS12 */
+       11,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS11 */
+       10,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS10 */
+       9,                             /* WL18XX_CONF_HW_RXTX_RATE_MCS9 */
+       8,                             /* WL18XX_CONF_HW_RXTX_RATE_MCS8 */
+       7,                             /* WL18XX_CONF_HW_RXTX_RATE_MCS7 */
+       6,                             /* WL18XX_CONF_HW_RXTX_RATE_MCS6 */
+       5,                             /* WL18XX_CONF_HW_RXTX_RATE_MCS5 */
+       4,                             /* WL18XX_CONF_HW_RXTX_RATE_MCS4 */
+       3,                             /* WL18XX_CONF_HW_RXTX_RATE_MCS3 */
+       2,                             /* WL18XX_CONF_HW_RXTX_RATE_MCS2 */
+       1,                             /* WL18XX_CONF_HW_RXTX_RATE_MCS1 */
+       0,                             /* WL18XX_CONF_HW_RXTX_RATE_MCS0 */
+
+       11,                            /* WL18XX_CONF_HW_RXTX_RATE_54   */
+       10,                            /* WL18XX_CONF_HW_RXTX_RATE_48   */
+       9,                             /* WL18XX_CONF_HW_RXTX_RATE_36   */
+       8,                             /* WL18XX_CONF_HW_RXTX_RATE_24   */
+
+       /* TI-specific rate */
+       CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_22   */
+
+       7,                             /* WL18XX_CONF_HW_RXTX_RATE_18   */
+       6,                             /* WL18XX_CONF_HW_RXTX_RATE_12   */
+       3,                             /* WL18XX_CONF_HW_RXTX_RATE_11   */
+       5,                             /* WL18XX_CONF_HW_RXTX_RATE_9    */
+       4,                             /* WL18XX_CONF_HW_RXTX_RATE_6    */
+       2,                             /* WL18XX_CONF_HW_RXTX_RATE_5_5  */
+       1,                             /* WL18XX_CONF_HW_RXTX_RATE_2    */
+       0                              /* WL18XX_CONF_HW_RXTX_RATE_1    */
+};
+
+static const u8 wl18xx_rate_to_idx_5ghz[] = {
+       /* MCS rates are used only with 11n */
+       15,                           /* WL18XX_CONF_HW_RXTX_RATE_MCS15 */
+       14,                           /* WL18XX_CONF_HW_RXTX_RATE_MCS14 */
+       13,                           /* WL18XX_CONF_HW_RXTX_RATE_MCS13 */
+       12,                           /* WL18XX_CONF_HW_RXTX_RATE_MCS12 */
+       11,                           /* WL18XX_CONF_HW_RXTX_RATE_MCS11 */
+       10,                           /* WL18XX_CONF_HW_RXTX_RATE_MCS10 */
+       9,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS9 */
+       8,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS8 */
+       7,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS7 */
+       6,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS6 */
+       5,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS5 */
+       4,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS4 */
+       3,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS3 */
+       2,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS2 */
+       1,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS1 */
+       0,                            /* WL18XX_CONF_HW_RXTX_RATE_MCS0 */
+
+       7,                             /* WL18XX_CONF_HW_RXTX_RATE_54   */
+       6,                             /* WL18XX_CONF_HW_RXTX_RATE_48   */
+       5,                             /* WL18XX_CONF_HW_RXTX_RATE_36   */
+       4,                             /* WL18XX_CONF_HW_RXTX_RATE_24   */
+
+       /* TI-specific rate */
+       CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_22   */
+
+       3,                             /* WL18XX_CONF_HW_RXTX_RATE_18   */
+       2,                             /* WL18XX_CONF_HW_RXTX_RATE_12   */
+       CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_11   */
+       1,                             /* WL18XX_CONF_HW_RXTX_RATE_9    */
+       0,                             /* WL18XX_CONF_HW_RXTX_RATE_6    */
+       CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_5_5  */
+       CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_2    */
+       CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_1    */
+};
+
+static const u8 *wl18xx_band_rate_to_idx[] = {
+       [IEEE80211_BAND_2GHZ] = wl18xx_rate_to_idx_2ghz,
+       [IEEE80211_BAND_5GHZ] = wl18xx_rate_to_idx_5ghz
+};
+
+enum wl18xx_hw_rates {
+       WL18XX_CONF_HW_RXTX_RATE_MCS15 = 0,
+       WL18XX_CONF_HW_RXTX_RATE_MCS14,
+       WL18XX_CONF_HW_RXTX_RATE_MCS13,
+       WL18XX_CONF_HW_RXTX_RATE_MCS12,
+       WL18XX_CONF_HW_RXTX_RATE_MCS11,
+       WL18XX_CONF_HW_RXTX_RATE_MCS10,
+       WL18XX_CONF_HW_RXTX_RATE_MCS9,
+       WL18XX_CONF_HW_RXTX_RATE_MCS8,
+       WL18XX_CONF_HW_RXTX_RATE_MCS7,
+       WL18XX_CONF_HW_RXTX_RATE_MCS6,
+       WL18XX_CONF_HW_RXTX_RATE_MCS5,
+       WL18XX_CONF_HW_RXTX_RATE_MCS4,
+       WL18XX_CONF_HW_RXTX_RATE_MCS3,
+       WL18XX_CONF_HW_RXTX_RATE_MCS2,
+       WL18XX_CONF_HW_RXTX_RATE_MCS1,
+       WL18XX_CONF_HW_RXTX_RATE_MCS0,
+       WL18XX_CONF_HW_RXTX_RATE_54,
+       WL18XX_CONF_HW_RXTX_RATE_48,
+       WL18XX_CONF_HW_RXTX_RATE_36,
+       WL18XX_CONF_HW_RXTX_RATE_24,
+       WL18XX_CONF_HW_RXTX_RATE_22,
+       WL18XX_CONF_HW_RXTX_RATE_18,
+       WL18XX_CONF_HW_RXTX_RATE_12,
+       WL18XX_CONF_HW_RXTX_RATE_11,
+       WL18XX_CONF_HW_RXTX_RATE_9,
+       WL18XX_CONF_HW_RXTX_RATE_6,
+       WL18XX_CONF_HW_RXTX_RATE_5_5,
+       WL18XX_CONF_HW_RXTX_RATE_2,
+       WL18XX_CONF_HW_RXTX_RATE_1,
+       WL18XX_CONF_HW_RXTX_RATE_MAX,
+};
+
+static struct wlcore_conf wl18xx_conf = {
+       .sg = {
+               .params = {
+                       [CONF_SG_ACL_BT_MASTER_MIN_BR] = 10,
+                       [CONF_SG_ACL_BT_MASTER_MAX_BR] = 180,
+                       [CONF_SG_ACL_BT_SLAVE_MIN_BR] = 10,
+                       [CONF_SG_ACL_BT_SLAVE_MAX_BR] = 180,
+                       [CONF_SG_ACL_BT_MASTER_MIN_EDR] = 10,
+                       [CONF_SG_ACL_BT_MASTER_MAX_EDR] = 80,
+                       [CONF_SG_ACL_BT_SLAVE_MIN_EDR] = 10,
+                       [CONF_SG_ACL_BT_SLAVE_MAX_EDR] = 80,
+                       [CONF_SG_ACL_WLAN_PS_MASTER_BR] = 8,
+                       [CONF_SG_ACL_WLAN_PS_SLAVE_BR] = 8,
+                       [CONF_SG_ACL_WLAN_PS_MASTER_EDR] = 20,
+                       [CONF_SG_ACL_WLAN_PS_SLAVE_EDR] = 20,
+                       [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR] = 20,
+                       [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR] = 35,
+                       [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR] = 16,
+                       [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR] = 35,
+                       [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR] = 32,
+                       [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR] = 50,
+                       [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR] = 28,
+                       [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR] = 50,
+                       [CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR] = 10,
+                       [CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR] = 20,
+                       [CONF_SG_ACL_PASSIVE_SCAN_BT_BR] = 75,
+                       [CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR] = 15,
+                       [CONF_SG_ACL_PASSIVE_SCAN_BT_EDR] = 27,
+                       [CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR] = 17,
+                       /* active scan params */
+                       [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
+                       [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
+                       [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100,
+                       /* passive scan params */
+                       [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_BR] = 800,
+                       [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_EDR] = 200,
+                       [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200,
+                       /* passive scan in dual antenna params */
+                       [CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0,
+                       [CONF_SG_BCN_HV3_COLLISION_THRESH_IN_PASSIVE_SCAN] = 0,
+                       [CONF_SG_TX_RX_PROTECTION_BWIDTH_IN_PASSIVE_SCAN] = 0,
+                       /* general params */
+                       [CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1,
+                       [CONF_SG_ANTENNA_CONFIGURATION] = 0,
+                       [CONF_SG_BEACON_MISS_PERCENT] = 60,
+                       [CONF_SG_DHCP_TIME] = 5000,
+                       [CONF_SG_RXT] = 1200,
+                       [CONF_SG_TXT] = 1000,
+                       [CONF_SG_ADAPTIVE_RXT_TXT] = 1,
+                       [CONF_SG_GENERAL_USAGE_BIT_MAP] = 3,
+                       [CONF_SG_HV3_MAX_SERVED] = 6,
+                       [CONF_SG_PS_POLL_TIMEOUT] = 10,
+                       [CONF_SG_UPSD_TIMEOUT] = 10,
+                       [CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2,
+                       [CONF_SG_STA_RX_WINDOW_AFTER_DTIM] = 5,
+                       [CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 30,
+                       /* AP params */
+                       [CONF_AP_BEACON_MISS_TX] = 3,
+                       [CONF_AP_RX_WINDOW_AFTER_BEACON] = 10,
+                       [CONF_AP_BEACON_WINDOW_INTERVAL] = 2,
+                       [CONF_AP_CONNECTION_PROTECTION_TIME] = 0,
+                       [CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25,
+                       [CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25,
+                       /* CTS Diluting params */
+                       [CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0,
+                       [CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0,
+               },
+               .state = CONF_SG_PROTECTIVE,
+       },
+       .rx = {
+               .rx_msdu_life_time           = 512000,
+               .packet_detection_threshold  = 0,
+               .ps_poll_timeout             = 15,
+               .upsd_timeout                = 15,
+               .rts_threshold               = IEEE80211_MAX_RTS_THRESHOLD,
+               .rx_cca_threshold            = 0,
+               .irq_blk_threshold           = 0xFFFF,
+               .irq_pkt_threshold           = 0,
+               .irq_timeout                 = 600,
+               .queue_type                  = CONF_RX_QUEUE_TYPE_LOW_PRIORITY,
+       },
+       .tx = {
+               .tx_energy_detection         = 0,
+               .sta_rc_conf                 = {
+                       .enabled_rates       = 0,
+                       .short_retry_limit   = 10,
+                       .long_retry_limit    = 10,
+                       .aflags              = 0,
+               },
+               .ac_conf_count               = 4,
+               .ac_conf                     = {
+                       [CONF_TX_AC_BE] = {
+                               .ac          = CONF_TX_AC_BE,
+                               .cw_min      = 15,
+                               .cw_max      = 63,
+                               .aifsn       = 3,
+                               .tx_op_limit = 0,
+                       },
+                       [CONF_TX_AC_BK] = {
+                               .ac          = CONF_TX_AC_BK,
+                               .cw_min      = 15,
+                               .cw_max      = 63,
+                               .aifsn       = 7,
+                               .tx_op_limit = 0,
+                       },
+                       [CONF_TX_AC_VI] = {
+                               .ac          = CONF_TX_AC_VI,
+                               .cw_min      = 15,
+                               .cw_max      = 63,
+                               .aifsn       = CONF_TX_AIFS_PIFS,
+                               .tx_op_limit = 3008,
+                       },
+                       [CONF_TX_AC_VO] = {
+                               .ac          = CONF_TX_AC_VO,
+                               .cw_min      = 15,
+                               .cw_max      = 63,
+                               .aifsn       = CONF_TX_AIFS_PIFS,
+                               .tx_op_limit = 1504,
+                       },
+               },
+               .max_tx_retries = 100,
+               .ap_aging_period = 300,
+               .tid_conf_count = 4,
+               .tid_conf = {
+                       [CONF_TX_AC_BE] = {
+                               .queue_id    = CONF_TX_AC_BE,
+                               .channel_type = CONF_CHANNEL_TYPE_EDCF,
+                               .tsid        = CONF_TX_AC_BE,
+                               .ps_scheme   = CONF_PS_SCHEME_LEGACY,
+                               .ack_policy  = CONF_ACK_POLICY_LEGACY,
+                               .apsd_conf   = {0, 0},
+                       },
+                       [CONF_TX_AC_BK] = {
+                               .queue_id    = CONF_TX_AC_BK,
+                               .channel_type = CONF_CHANNEL_TYPE_EDCF,
+                               .tsid        = CONF_TX_AC_BK,
+                               .ps_scheme   = CONF_PS_SCHEME_LEGACY,
+                               .ack_policy  = CONF_ACK_POLICY_LEGACY,
+                               .apsd_conf   = {0, 0},
+                       },
+                       [CONF_TX_AC_VI] = {
+                               .queue_id    = CONF_TX_AC_VI,
+                               .channel_type = CONF_CHANNEL_TYPE_EDCF,
+                               .tsid        = CONF_TX_AC_VI,
+                               .ps_scheme   = CONF_PS_SCHEME_LEGACY,
+                               .ack_policy  = CONF_ACK_POLICY_LEGACY,
+                               .apsd_conf   = {0, 0},
+                       },
+                       [CONF_TX_AC_VO] = {
+                               .queue_id    = CONF_TX_AC_VO,
+                               .channel_type = CONF_CHANNEL_TYPE_EDCF,
+                               .tsid        = CONF_TX_AC_VO,
+                               .ps_scheme   = CONF_PS_SCHEME_LEGACY,
+                               .ack_policy  = CONF_ACK_POLICY_LEGACY,
+                               .apsd_conf   = {0, 0},
+                       },
+               },
+               .frag_threshold              = IEEE80211_MAX_FRAG_THRESHOLD,
+               .tx_compl_timeout            = 350,
+               .tx_compl_threshold          = 10,
+               .basic_rate                  = CONF_HW_BIT_RATE_1MBPS,
+               .basic_rate_5                = CONF_HW_BIT_RATE_6MBPS,
+               .tmpl_short_retry_limit      = 10,
+               .tmpl_long_retry_limit       = 10,
+               .tx_watchdog_timeout         = 5000,
+       },
+       .conn = {
+               .wake_up_event               = CONF_WAKE_UP_EVENT_DTIM,
+               .listen_interval             = 1,
+               .suspend_wake_up_event       = CONF_WAKE_UP_EVENT_N_DTIM,
+               .suspend_listen_interval     = 3,
+               .bcn_filt_mode               = CONF_BCN_FILT_MODE_ENABLED,
+               .bcn_filt_ie_count           = 3,
+               .bcn_filt_ie = {
+                       [0] = {
+                               .ie          = WLAN_EID_CHANNEL_SWITCH,
+                               .rule        = CONF_BCN_RULE_PASS_ON_APPEARANCE,
+                       },
+                       [1] = {
+                               .ie          = WLAN_EID_HT_OPERATION,
+                               .rule        = CONF_BCN_RULE_PASS_ON_CHANGE,
+                       },
+                       [2] = {
+                               .ie          = WLAN_EID_ERP_INFO,
+                               .rule        = CONF_BCN_RULE_PASS_ON_CHANGE,
+                       },
+               },
+               .synch_fail_thold            = 12,
+               .bss_lose_timeout            = 400,
+               .beacon_rx_timeout           = 10000,
+               .broadcast_timeout           = 20000,
+               .rx_broadcast_in_ps          = 1,
+               .ps_poll_threshold           = 10,
+               .bet_enable                  = CONF_BET_MODE_ENABLE,
+               .bet_max_consecutive         = 50,
+               .psm_entry_retries           = 8,
+               .psm_exit_retries            = 16,
+               .psm_entry_nullfunc_retries  = 3,
+               .dynamic_ps_timeout          = 200,
+               .forced_ps                   = false,
+               .keep_alive_interval         = 55000,
+               .max_listen_interval         = 20,
+       },
+       .itrim = {
+               .enable = false,
+               .timeout = 50000,
+       },
+       .pm_config = {
+               .host_clk_settling_time = 5000,
+               .host_fast_wakeup_support = CONF_FAST_WAKEUP_DISABLE,
+       },
+       .roam_trigger = {
+               .trigger_pacing               = 1,
+               .avg_weight_rssi_beacon       = 20,
+               .avg_weight_rssi_data         = 10,
+               .avg_weight_snr_beacon        = 20,
+               .avg_weight_snr_data          = 10,
+       },
+       .scan = {
+               .min_dwell_time_active        = 7500,
+               .max_dwell_time_active        = 30000,
+               .min_dwell_time_passive       = 100000,
+               .max_dwell_time_passive       = 100000,
+               .num_probe_reqs               = 2,
+               .split_scan_timeout           = 50000,
+       },
+       .sched_scan = {
+               /*
+                * Values are in TU/1000 but since sched scan FW command
+                * params are in TUs rounding up may occur.
+                */
+               .base_dwell_time                = 7500,
+               .max_dwell_time_delta           = 22500,
+               /* based on 250bits per probe @1Mbps */
+               .dwell_time_delta_per_probe     = 2000,
+               /* based on 250bits per probe @6Mbps (plus a bit more) */
+               .dwell_time_delta_per_probe_5   = 350,
+               .dwell_time_passive             = 100000,
+               .dwell_time_dfs                 = 150000,
+               .num_probe_reqs                 = 2,
+               .rssi_threshold                 = -90,
+               .snr_threshold                  = 0,
+       },
+       .ht = {
+               .rx_ba_win_size = 10,
+               .tx_ba_win_size = 64,
+               .inactivity_timeout = 10000,
+               .tx_ba_tid_bitmap = CONF_TX_BA_ENABLED_TID_BITMAP,
+       },
+       .mem = {
+               .num_stations                 = 1,
+               .ssid_profiles                = 1,
+               .rx_block_num                 = 40,
+               .tx_min_block_num             = 40,
+               .dynamic_memory               = 1,
+               .min_req_tx_blocks            = 45,
+               .min_req_rx_blocks            = 22,
+               .tx_min                       = 27,
+       },
+       .fm_coex = {
+               .enable                       = true,
+               .swallow_period               = 5,
+               .n_divider_fref_set_1         = 0xff,       /* default */
+               .n_divider_fref_set_2         = 12,
+               .m_divider_fref_set_1         = 0xffff,
+               .m_divider_fref_set_2         = 148,        /* default */
+               .coex_pll_stabilization_time  = 0xffffffff, /* default */
+               .ldo_stabilization_time       = 0xffff,     /* default */
+               .fm_disturbed_band_margin     = 0xff,       /* default */
+               .swallow_clk_diff             = 0xff,       /* default */
+       },
+       .rx_streaming = {
+               .duration                      = 150,
+               .queues                        = 0x1,
+               .interval                      = 20,
+               .always                        = 0,
+       },
+       .fwlog = {
+               .mode                         = WL12XX_FWLOG_ON_DEMAND,
+               .mem_blocks                   = 2,
+               .severity                     = 0,
+               .timestamp                    = WL12XX_FWLOG_TIMESTAMP_DISABLED,
+               .output                       = WL12XX_FWLOG_OUTPUT_HOST,
+               .threshold                    = 0,
+       },
+       .rate = {
+               .rate_retry_score = 32000,
+               .per_add = 8192,
+               .per_th1 = 2048,
+               .per_th2 = 4096,
+               .max_per = 8100,
+               .inverse_curiosity_factor = 5,
+               .tx_fail_low_th = 4,
+               .tx_fail_high_th = 10,
+               .per_alpha_shift = 4,
+               .per_add_shift = 13,
+               .per_beta1_shift = 10,
+               .per_beta2_shift = 8,
+               .rate_check_up = 2,
+               .rate_check_down = 12,
+               .rate_retry_policy = {
+                       0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00,
+               },
+       },
+       .hangover = {
+               .recover_time               = 0,
+               .hangover_period            = 20,
+               .dynamic_mode               = 1,
+               .early_termination_mode     = 1,
+               .max_period                 = 20,
+               .min_period                 = 1,
+               .increase_delta             = 1,
+               .decrease_delta             = 2,
+               .quiet_time                 = 4,
+               .increase_time              = 1,
+               .window_size                = 16,
+       },
+};
+
+static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
+       .phy = {
+               .phy_standalone                 = 0x00,
+               .primary_clock_setting_time     = 0x05,
+               .clock_valid_on_wake_up         = 0x00,
+               .secondary_clock_setting_time   = 0x05,
+               .rdl                            = 0x01,
+               .auto_detect                    = 0x00,
+               .dedicated_fem                  = FEM_NONE,
+               .low_band_component             = COMPONENT_2_WAY_SWITCH,
+               .low_band_component_type        = 0x05,
+               .high_band_component            = COMPONENT_2_WAY_SWITCH,
+               .high_band_component_type       = 0x09,
+               .tcxo_ldo_voltage               = 0x00,
+               .xtal_itrim_val                 = 0x04,
+               .srf_state                      = 0x00,
+               .io_configuration               = 0x01,
+               .sdio_configuration             = 0x00,
+               .settings                       = 0x00,
+               .enable_clpc                    = 0x00,
+               .enable_tx_low_pwr_on_siso_rdl  = 0x00,
+               .rx_profile                     = 0x00,
+               .pwr_limit_reference_11_abg     = 0xc8,
+               .psat                           = 0,
+               .low_power_val                  = 0x00,
+               .med_power_val                  = 0x0a,
+               .high_power_val                 = 0x1e,
+               .external_pa_dc2dc              = 0,
+               .number_of_assembled_ant2_4     = 1,
+               .number_of_assembled_ant5       = 1,
+       },
+};
+
+static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = {
+       [PART_TOP_PRCM_ELP_SOC] = {
+               .mem  = { .start = 0x00A02000, .size  = 0x00010000 },
+               .reg  = { .start = 0x00807000, .size  = 0x00005000 },
+               .mem2 = { .start = 0x00800000, .size  = 0x0000B000 },
+               .mem3 = { .start = 0x00000000, .size  = 0x00000000 },
+       },
+       [PART_DOWN] = {
+               .mem  = { .start = 0x00000000, .size  = 0x00014000 },
+               .reg  = { .start = 0x00810000, .size  = 0x0000BFFF },
+               .mem2 = { .start = 0x00000000, .size  = 0x00000000 },
+               .mem3 = { .start = 0x00000000, .size  = 0x00000000 },
+       },
+       [PART_BOOT] = {
+               .mem  = { .start = 0x00700000, .size = 0x0000030c },
+               .reg  = { .start = 0x00802000, .size = 0x00014578 },
+               .mem2 = { .start = 0x00B00404, .size = 0x00001000 },
+               .mem3 = { .start = 0x00C00000, .size = 0x00000400 },
+       },
+       [PART_WORK] = {
+               .mem  = { .start = 0x00800000, .size  = 0x000050FC },
+               .reg  = { .start = 0x00B00404, .size  = 0x00001000 },
+               .mem2 = { .start = 0x00C00000, .size  = 0x00000400 },
+               .mem3 = { .start = 0x00000000, .size  = 0x00000000 },
+       },
+       [PART_PHY_INIT] = {
+               .mem  = { .start = 0x80926000,
+                         .size = sizeof(struct wl18xx_mac_and_phy_params) },
+               .reg  = { .start = 0x00000000, .size = 0x00000000 },
+               .mem2 = { .start = 0x00000000, .size = 0x00000000 },
+               .mem3 = { .start = 0x00000000, .size = 0x00000000 },
+       },
+};
+
+static const int wl18xx_rtable[REG_TABLE_LEN] = {
+       [REG_ECPU_CONTROL]              = WL18XX_REG_ECPU_CONTROL,
+       [REG_INTERRUPT_NO_CLEAR]        = WL18XX_REG_INTERRUPT_NO_CLEAR,
+       [REG_INTERRUPT_ACK]             = WL18XX_REG_INTERRUPT_ACK,
+       [REG_COMMAND_MAILBOX_PTR]       = WL18XX_REG_COMMAND_MAILBOX_PTR,
+       [REG_EVENT_MAILBOX_PTR]         = WL18XX_REG_EVENT_MAILBOX_PTR,
+       [REG_INTERRUPT_TRIG]            = WL18XX_REG_INTERRUPT_TRIG_H,
+       [REG_INTERRUPT_MASK]            = WL18XX_REG_INTERRUPT_MASK,
+       [REG_PC_ON_RECOVERY]            = WL18XX_SCR_PAD4,
+       [REG_CHIP_ID_B]                 = WL18XX_REG_CHIP_ID_B,
+       [REG_CMD_MBOX_ADDRESS]          = WL18XX_CMD_MBOX_ADDRESS,
+
+       /* data access memory addresses, used with partition translation */
+       [REG_SLV_MEM_DATA]              = WL18XX_SLV_MEM_DATA,
+       [REG_SLV_REG_DATA]              = WL18XX_SLV_REG_DATA,
+
+       /* raw data access memory addresses */
+       [REG_RAW_FW_STATUS_ADDR]        = WL18XX_FW_STATUS_ADDR,
+};
+
+static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = {
+       [CLOCK_CONFIG_16_2_M]   = { 7,  104,  801, 4,  true },
+       [CLOCK_CONFIG_16_368_M] = { 9,  132, 3751, 4,  true },
+       [CLOCK_CONFIG_16_8_M]   = { 7,  100,    0, 0, false },
+       [CLOCK_CONFIG_19_2_M]   = { 8,  100,    0, 0, false },
+       [CLOCK_CONFIG_26_M]     = { 13, 120,    0, 0, false },
+       [CLOCK_CONFIG_32_736_M] = { 9,  132, 3751, 4,  true },
+       [CLOCK_CONFIG_33_6_M]   = { 7,  100,    0, 0, false },
+       [CLOCK_CONFIG_38_468_M] = { 8,  100,    0, 0, false },
+       [CLOCK_CONFIG_52_M]     = { 13, 120,    0, 0, false },
+};
+
+/* TODO: maybe move to a new header file? */
+#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw.bin"
+
+static int wl18xx_identify_chip(struct wl1271 *wl)
+{
+       int ret = 0;
+
+       switch (wl->chip.id) {
+       case CHIP_ID_185x_PG20:
+               wl1271_debug(DEBUG_BOOT, "chip id 0x%x (185x PG20)",
+                                wl->chip.id);
+               wl->sr_fw_name = WL18XX_FW_NAME;
+               /* wl18xx uses the same firmware for PLT */
+               wl->plt_fw_name = WL18XX_FW_NAME;
+               wl->quirks |= WLCORE_QUIRK_NO_ELP |
+                             WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN |
+                             WLCORE_QUIRK_TX_PAD_LAST_FRAME;
+
+               break;
+       case CHIP_ID_185x_PG10:
+               wl1271_debug(DEBUG_BOOT, "chip id 0x%x (185x PG10)",
+                            wl->chip.id);
+               wl->sr_fw_name = WL18XX_FW_NAME;
+               /* wl18xx uses the same firmware for PLT */
+               wl->plt_fw_name = WL18XX_FW_NAME;
+               wl->quirks |= WLCORE_QUIRK_NO_ELP |
+                       WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED |
+                       WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN |
+                       WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;
+
+               /* PG 1.0 has some problems with MCS_13, so disable it */
+               wl->ht_cap[IEEE80211_BAND_2GHZ].mcs.rx_mask[1] &= ~BIT(5);
+
+               break;
+       default:
+               wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
+               ret = -ENODEV;
+               goto out;
+       }
+
+out:
+       return ret;
+}
+
+static void wl18xx_set_clk(struct wl1271 *wl)
+{
+       u32 clk_freq;
+
+       wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]);
+
+       /* TODO: PG2: apparently we need to read the clk type */
+
+       clk_freq = wl18xx_top_reg_read(wl, PRIMARY_CLK_DETECT);
+       wl1271_debug(DEBUG_BOOT, "clock freq %d (%d, %d, %d, %d, %s)", clk_freq,
+                    wl18xx_clk_table[clk_freq].n, wl18xx_clk_table[clk_freq].m,
+                    wl18xx_clk_table[clk_freq].p, wl18xx_clk_table[clk_freq].q,
+                    wl18xx_clk_table[clk_freq].swallow ? "swallow" : "spit");
+
+       wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_N, wl18xx_clk_table[clk_freq].n);
+       wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_M, wl18xx_clk_table[clk_freq].m);
+
+       if (wl18xx_clk_table[clk_freq].swallow) {
+               /* first the 16 lower bits */
+               wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_Q_FACTOR_CFG_1,
+                                    wl18xx_clk_table[clk_freq].q &
+                                    PLLSH_WCS_PLL_Q_FACTOR_CFG_1_MASK);
+               /* then the 16 higher bits, masked out */
+               wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_Q_FACTOR_CFG_2,
+                                    (wl18xx_clk_table[clk_freq].q >> 16) &
+                                    PLLSH_WCS_PLL_Q_FACTOR_CFG_2_MASK);
+
+               /* first the 16 lower bits */
+               wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_P_FACTOR_CFG_1,
+                                    wl18xx_clk_table[clk_freq].p &
+                                    PLLSH_WCS_PLL_P_FACTOR_CFG_1_MASK);
+               /* then the 16 higher bits, masked out */
+               wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_P_FACTOR_CFG_2,
+                                    (wl18xx_clk_table[clk_freq].p >> 16) &
+                                    PLLSH_WCS_PLL_P_FACTOR_CFG_2_MASK);
+       } else {
+               wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_SWALLOW_EN,
+                                    PLLSH_WCS_PLL_SWALLOW_EN_VAL2);
+       }
+}
+
+static void wl18xx_boot_soft_reset(struct wl1271 *wl)
+{
+       /* disable Rx/Tx */
+       wl1271_write32(wl, WL18XX_ENABLE, 0x0);
+
+       /* disable auto calibration on start*/
+       wl1271_write32(wl, WL18XX_SPARE_A2, 0xffff);
+}
+
+static int wl18xx_pre_boot(struct wl1271 *wl)
+{
+       wl18xx_set_clk(wl);
+
+       /* Continue the ELP wake up sequence */
+       wl1271_write32(wl, WL18XX_WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL);
+       udelay(500);
+
+       wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
+
+       /* Disable interrupts */
+       wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL);
+
+       wl18xx_boot_soft_reset(wl);
+
+       return 0;
+}
+
+static void wl18xx_pre_upload(struct wl1271 *wl)
+{
+       u32 tmp;
+
+       wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
+
+       /* TODO: check if this is all needed */
+       wl1271_write32(wl, WL18XX_EEPROMLESS_IND, WL18XX_EEPROMLESS_IND);
+
+       tmp = wlcore_read_reg(wl, REG_CHIP_ID_B);
+
+       wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp);
+
+       tmp = wl1271_read32(wl, WL18XX_SCR_PAD2);
+}
+
+static void wl18xx_set_mac_and_phy(struct wl1271 *wl)
+{
+       struct wl18xx_priv *priv = wl->priv;
+       size_t len;
+
+       /* the parameters struct is smaller for PG1 */
+       if (wl->chip.id == CHIP_ID_185x_PG10)
+               len = offsetof(struct wl18xx_mac_and_phy_params, psat) + 1;
+       else
+               len = sizeof(struct wl18xx_mac_and_phy_params);
+
+       wlcore_set_partition(wl, &wl->ptable[PART_PHY_INIT]);
+       wl1271_write(wl, WL18XX_PHY_INIT_MEM_ADDR, (u8 *)&priv->conf.phy, len,
+                    false);
+}
+
+static void wl18xx_enable_interrupts(struct wl1271 *wl)
+{
+       u32 event_mask, intr_mask;
+
+       if (wl->chip.id == CHIP_ID_185x_PG10) {
+               event_mask = WL18XX_ACX_EVENTS_VECTOR_PG1;
+               intr_mask = WL18XX_INTR_MASK_PG1;
+       } else {
+               event_mask = WL18XX_ACX_EVENTS_VECTOR_PG2;
+               intr_mask = WL18XX_INTR_MASK_PG2;
+       }
+
+       wlcore_write_reg(wl, REG_INTERRUPT_MASK, event_mask);
+
+       wlcore_enable_interrupts(wl);
+       wlcore_write_reg(wl, REG_INTERRUPT_MASK,
+                        WL1271_ACX_INTR_ALL & ~intr_mask);
+}
+
+static int wl18xx_boot(struct wl1271 *wl)
+{
+       int ret;
+
+       ret = wl18xx_pre_boot(wl);
+       if (ret < 0)
+               goto out;
+
+       wl18xx_pre_upload(wl);
+
+       ret = wlcore_boot_upload_firmware(wl);
+       if (ret < 0)
+               goto out;
+
+       wl18xx_set_mac_and_phy(wl);
+
+       ret = wlcore_boot_run_firmware(wl);
+       if (ret < 0)
+               goto out;
+
+       wl18xx_enable_interrupts(wl);
+
+out:
+       return ret;
+}
+
+static void wl18xx_trigger_cmd(struct wl1271 *wl, int cmd_box_addr,
+                              void *buf, size_t len)
+{
+       struct wl18xx_priv *priv = wl->priv;
+
+       memcpy(priv->cmd_buf, buf, len);
+       memset(priv->cmd_buf + len, 0, WL18XX_CMD_MAX_SIZE - len);
+
+       wl1271_write(wl, cmd_box_addr, priv->cmd_buf, WL18XX_CMD_MAX_SIZE,
+                    false);
+}
+
+static void wl18xx_ack_event(struct wl1271 *wl)
+{
+       wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL18XX_INTR_TRIG_EVENT_ACK);
+}
+
+static u32 wl18xx_calc_tx_blocks(struct wl1271 *wl, u32 len, u32 spare_blks)
+{
+       u32 blk_size = WL18XX_TX_HW_BLOCK_SIZE;
+       return (len + blk_size - 1) / blk_size + spare_blks;
+}
+
+static void
+wl18xx_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,
+                         u32 blks, u32 spare_blks)
+{
+       desc->wl18xx_mem.total_mem_blocks = blks;
+}
+
+static void
+wl18xx_set_tx_desc_data_len(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,
+                           struct sk_buff *skb)
+{
+       desc->length = cpu_to_le16(skb->len);
+
+       /* if only the last frame is to be padded, we unset this bit on Tx */
+       if (wl->quirks & WLCORE_QUIRK_TX_PAD_LAST_FRAME)
+               desc->wl18xx_mem.ctrl = WL18XX_TX_CTRL_NOT_PADDED;
+       else
+               desc->wl18xx_mem.ctrl = 0;
+
+       wl1271_debug(DEBUG_TX, "tx_fill_hdr: hlid: %d "
+                    "len: %d life: %d mem: %d", desc->hlid,
+                    le16_to_cpu(desc->length),
+                    le16_to_cpu(desc->life_time),
+                    desc->wl18xx_mem.total_mem_blocks);
+}
+
+static enum wl_rx_buf_align
+wl18xx_get_rx_buf_align(struct wl1271 *wl, u32 rx_desc)
+{
+       if (rx_desc & RX_BUF_PADDED_PAYLOAD)
+               return WLCORE_RX_BUF_PADDED;
+
+       return WLCORE_RX_BUF_ALIGNED;
+}
+
+static u32 wl18xx_get_rx_packet_len(struct wl1271 *wl, void *rx_data,
+                                   u32 data_len)
+{
+       struct wl1271_rx_descriptor *desc = rx_data;
+
+       /* invalid packet */
+       if (data_len < sizeof(*desc))
+               return 0;
+
+       return data_len - sizeof(*desc);
+}
+
+static void wl18xx_tx_immediate_completion(struct wl1271 *wl)
+{
+       wl18xx_tx_immediate_complete(wl);
+}
+
+static int wl18xx_set_host_cfg_bitmap(struct wl1271 *wl, u32 extra_mem_blk)
+{
+       int ret;
+       u32 sdio_align_size = 0;
+       u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE |
+                             HOST_IF_CFG_ADD_RX_ALIGNMENT;
+
+       /* Enable Tx SDIO padding */
+       if (wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN) {
+               host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK;
+               sdio_align_size = WL12XX_BUS_BLOCK_SIZE;
+       }
+
+       /* Enable Rx SDIO padding */
+       if (wl->quirks & WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN) {
+               host_cfg_bitmap |= HOST_IF_CFG_RX_PAD_TO_SDIO_BLK;
+               sdio_align_size = WL12XX_BUS_BLOCK_SIZE;
+       }
+
+       ret = wl18xx_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap,
+                                           sdio_align_size, extra_mem_blk,
+                                           WL18XX_HOST_IF_LEN_SIZE_FIELD);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int wl18xx_hw_init(struct wl1271 *wl)
+{
+       int ret;
+       struct wl18xx_priv *priv = wl->priv;
+
+       /* (re)init private structures. Relevant on recovery as well. */
+       priv->last_fw_rls_idx = 0;
+       priv->extra_spare_vif_count = 0;
+
+       /* set the default amount of spare blocks in the bitmap */
+       ret = wl18xx_set_host_cfg_bitmap(wl, WL18XX_TX_HW_BLOCK_SPARE);
+       if (ret < 0)
+               return ret;
+
+       if (checksum_param) {
+               ret = wl18xx_acx_set_checksum_state(wl);
+               if (ret != 0)
+                       return ret;
+       }
+
+       return ret;
+}
+
+static void wl18xx_set_tx_desc_csum(struct wl1271 *wl,
+                                   struct wl1271_tx_hw_descr *desc,
+                                   struct sk_buff *skb)
+{
+       u32 ip_hdr_offset;
+       struct iphdr *ip_hdr;
+
+       if (!checksum_param) {
+               desc->wl18xx_checksum_data = 0;
+               return;
+       }
+
+       if (skb->ip_summed != CHECKSUM_PARTIAL) {
+               desc->wl18xx_checksum_data = 0;
+               return;
+       }
+
+       ip_hdr_offset = skb_network_header(skb) - skb_mac_header(skb);
+       if (WARN_ON(ip_hdr_offset >= (1<<7))) {
+               desc->wl18xx_checksum_data = 0;
+               return;
+       }
+
+       desc->wl18xx_checksum_data = ip_hdr_offset << 1;
+
+       /* FW is interested only in the LSB of the protocol  TCP=0 UDP=1 */
+       ip_hdr = (void *)skb_network_header(skb);
+       desc->wl18xx_checksum_data |= (ip_hdr->protocol & 0x01);
+}
+
+static void wl18xx_set_rx_csum(struct wl1271 *wl,
+                              struct wl1271_rx_descriptor *desc,
+                              struct sk_buff *skb)
+{
+       if (desc->status & WL18XX_RX_CHECKSUM_MASK)
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+}
+
+/*
+ * TODO: instead of having these two functions to get the rate mask,
+ * we should modify the wlvif->rate_set instead
+ */
+static u32 wl18xx_sta_get_ap_rate_mask(struct wl1271 *wl,
+                                      struct wl12xx_vif *wlvif)
+{
+       u32 hw_rate_set = wlvif->rate_set;
+
+       if (wlvif->channel_type == NL80211_CHAN_HT40MINUS ||
+           wlvif->channel_type == NL80211_CHAN_HT40PLUS) {
+               wl1271_debug(DEBUG_ACX, "using wide channel rate mask");
+               hw_rate_set |= CONF_TX_RATE_USE_WIDE_CHAN;
+
+               /* we don't support MIMO in wide-channel mode */
+               hw_rate_set &= ~CONF_TX_MIMO_RATES;
+       }
+
+       return hw_rate_set;
+}
+
+static u32 wl18xx_ap_get_mimo_wide_rate_mask(struct wl1271 *wl,
+                                            struct wl12xx_vif *wlvif)
+{
+       if ((wlvif->channel_type == NL80211_CHAN_HT40MINUS ||
+            wlvif->channel_type == NL80211_CHAN_HT40PLUS) &&
+           !strcmp(ht_mode_param, "wide")) {
+               wl1271_debug(DEBUG_ACX, "using wide channel rate mask");
+               return CONF_TX_RATE_USE_WIDE_CHAN;
+       } else if (!strcmp(ht_mode_param, "mimo")) {
+               wl1271_debug(DEBUG_ACX, "using MIMO rate mask");
+
+               /*
+                * PG 1.0 has some problems with MCS_13, so disable it
+                *
+                * TODO: instead of hacking this in here, we should
+                * make it more general and change a bit in the
+                * wlvif->rate_set instead.
+                */
+               if (wl->chip.id == CHIP_ID_185x_PG10)
+                       return CONF_TX_MIMO_RATES & ~CONF_HW_BIT_RATE_MCS_13;
+
+               return CONF_TX_MIMO_RATES;
+       } else {
+               return 0;
+       }
+}
+
+static s8 wl18xx_get_pg_ver(struct wl1271 *wl)
+{
+       u32 fuse;
+
+       wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]);
+
+       fuse = wl1271_read32(wl, WL18XX_REG_FUSE_DATA_1_3);
+       fuse = (fuse & WL18XX_PG_VER_MASK) >> WL18XX_PG_VER_OFFSET;
+
+       wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
+
+       return (s8)fuse;
+}
+
+#define WL18XX_CONF_FILE_NAME "ti-connectivity/wl18xx-conf.bin"
+static int wl18xx_conf_init(struct wl1271 *wl, struct device *dev)
+{
+       struct wl18xx_priv *priv = wl->priv;
+       struct wlcore_conf_file *conf_file;
+       const struct firmware *fw;
+       int ret;
+
+       ret = request_firmware(&fw, WL18XX_CONF_FILE_NAME, dev);
+       if (ret < 0) {
+               wl1271_error("could not get configuration binary %s: %d",
+                            WL18XX_CONF_FILE_NAME, ret);
+               goto out_fallback;
+       }
+
+       if (fw->size != WL18XX_CONF_SIZE) {
+               wl1271_error("configuration binary file size is wrong, "
+                            "expected %ld got %zd",
+                            WL18XX_CONF_SIZE, fw->size);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       conf_file = (struct wlcore_conf_file *) fw->data;
+
+       if (conf_file->header.magic != cpu_to_le32(WL18XX_CONF_MAGIC)) {
+               wl1271_error("configuration binary file magic number mismatch, "
+                            "expected 0x%0x got 0x%0x", WL18XX_CONF_MAGIC,
+                            conf_file->header.magic);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       if (conf_file->header.version != cpu_to_le32(WL18XX_CONF_VERSION)) {
+               wl1271_error("configuration binary file version not supported, "
+                            "expected 0x%08x got 0x%08x",
+                            WL18XX_CONF_VERSION, conf_file->header.version);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       memcpy(&wl->conf, &conf_file->core, sizeof(wl18xx_conf));
+       memcpy(&priv->conf, &conf_file->priv, sizeof(priv->conf));
+
+       goto out;
+
+out_fallback:
+       wl1271_warning("falling back to default config");
+
+       /* apply driver default configuration */
+       memcpy(&wl->conf, &wl18xx_conf, sizeof(wl18xx_conf));
+       /* apply default private configuration */
+       memcpy(&priv->conf, &wl18xx_default_priv_conf, sizeof(priv->conf));
+
+       /* For now we just fallback */
+       return 0;
+
+out:
+       release_firmware(fw);
+       return ret;
+}
+
+static int wl18xx_plt_init(struct wl1271 *wl)
+{
+       wl1271_write32(wl, WL18XX_SCR_PAD8, WL18XX_SCR_PAD8_PLT);
+
+       return wl->ops->boot(wl);
+}
+
+static void wl18xx_get_mac(struct wl1271 *wl)
+{
+       u32 mac1, mac2;
+
+       wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]);
+
+       mac1 = wl1271_read32(wl, WL18XX_REG_FUSE_BD_ADDR_1);
+       mac2 = wl1271_read32(wl, WL18XX_REG_FUSE_BD_ADDR_2);
+
+       /* these are the two parts of the BD_ADDR */
+       wl->fuse_oui_addr = ((mac2 & 0xffff) << 8) +
+               ((mac1 & 0xff000000) >> 24);
+       wl->fuse_nic_addr = (mac1 & 0xffffff);
+
+       wlcore_set_partition(wl, &wl->ptable[PART_DOWN]);
+}
+
+static int wl18xx_handle_static_data(struct wl1271 *wl,
+                                    struct wl1271_static_data *static_data)
+{
+       struct wl18xx_static_data_priv *static_data_priv =
+               (struct wl18xx_static_data_priv *) static_data->priv;
+
+       wl1271_info("PHY firmware version: %s", static_data_priv->phy_version);
+
+       return 0;
+}
+
+static int wl18xx_get_spare_blocks(struct wl1271 *wl, bool is_gem)
+{
+       struct wl18xx_priv *priv = wl->priv;
+
+       /* If we have VIFs requiring extra spare, indulge them */
+       if (priv->extra_spare_vif_count)
+               return WL18XX_TX_HW_EXTRA_BLOCK_SPARE;
+
+       return WL18XX_TX_HW_BLOCK_SPARE;
+}
+
+static int wl18xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
+                         struct ieee80211_vif *vif,
+                         struct ieee80211_sta *sta,
+                         struct ieee80211_key_conf *key_conf)
+{
+       struct wl18xx_priv *priv = wl->priv;
+       bool change_spare = false;
+       int ret;
+
+       /*
+        * when adding the first or removing the last GEM/TKIP interface,
+        * we have to adjust the number of spare blocks.
+        */
+       change_spare = (key_conf->cipher == WL1271_CIPHER_SUITE_GEM ||
+               key_conf->cipher == WLAN_CIPHER_SUITE_TKIP) &&
+               ((priv->extra_spare_vif_count == 0 && cmd == SET_KEY) ||
+                (priv->extra_spare_vif_count == 1 && cmd == DISABLE_KEY));
+
+       /* no need to change spare - just regular set_key */
+       if (!change_spare)
+               return wlcore_set_key(wl, cmd, vif, sta, key_conf);
+
+       /*
+        * stop the queues and flush to ensure the next packets are
+        * in sync with FW spare block accounting
+        */
+       wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK);
+       wl1271_tx_flush(wl);
+
+       ret = wlcore_set_key(wl, cmd, vif, sta, key_conf);
+       if (ret < 0)
+               goto out;
+
+       /* key is now set, change the spare blocks */
+       if (cmd == SET_KEY) {
+               ret = wl18xx_set_host_cfg_bitmap(wl,
+                                       WL18XX_TX_HW_EXTRA_BLOCK_SPARE);
+               if (ret < 0)
+                       goto out;
+
+               priv->extra_spare_vif_count++;
+       } else {
+               ret = wl18xx_set_host_cfg_bitmap(wl,
+                                       WL18XX_TX_HW_BLOCK_SPARE);
+               if (ret < 0)
+                       goto out;
+
+               priv->extra_spare_vif_count--;
+       }
+
+out:
+       wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK);
+       return ret;
+}
+
+static u32 wl18xx_pre_pkt_send(struct wl1271 *wl,
+                              u32 buf_offset, u32 last_len)
+{
+       if (wl->quirks & WLCORE_QUIRK_TX_PAD_LAST_FRAME) {
+               struct wl1271_tx_hw_descr *last_desc;
+
+               /* get the last TX HW descriptor written to the aggr buf */
+               last_desc = (struct wl1271_tx_hw_descr *)(wl->aggr_buf +
+                                                       buf_offset - last_len);
+
+               /* the last frame is padded up to an SDIO block */
+               last_desc->wl18xx_mem.ctrl &= ~WL18XX_TX_CTRL_NOT_PADDED;
+               return ALIGN(buf_offset, WL12XX_BUS_BLOCK_SIZE);
+       }
+
+       /* no modifications */
+       return buf_offset;
+}
+
+static struct wlcore_ops wl18xx_ops = {
+       .identify_chip  = wl18xx_identify_chip,
+       .boot           = wl18xx_boot,
+       .plt_init       = wl18xx_plt_init,
+       .trigger_cmd    = wl18xx_trigger_cmd,
+       .ack_event      = wl18xx_ack_event,
+       .calc_tx_blocks = wl18xx_calc_tx_blocks,
+       .set_tx_desc_blocks = wl18xx_set_tx_desc_blocks,
+       .set_tx_desc_data_len = wl18xx_set_tx_desc_data_len,
+       .get_rx_buf_align = wl18xx_get_rx_buf_align,
+       .get_rx_packet_len = wl18xx_get_rx_packet_len,
+       .tx_immediate_compl = wl18xx_tx_immediate_completion,
+       .tx_delayed_compl = NULL,
+       .hw_init        = wl18xx_hw_init,
+       .set_tx_desc_csum = wl18xx_set_tx_desc_csum,
+       .get_pg_ver     = wl18xx_get_pg_ver,
+       .set_rx_csum = wl18xx_set_rx_csum,
+       .sta_get_ap_rate_mask = wl18xx_sta_get_ap_rate_mask,
+       .ap_get_mimo_wide_rate_mask = wl18xx_ap_get_mimo_wide_rate_mask,
+       .get_mac        = wl18xx_get_mac,
+       .debugfs_init   = wl18xx_debugfs_add_files,
+       .handle_static_data     = wl18xx_handle_static_data,
+       .get_spare_blocks = wl18xx_get_spare_blocks,
+       .set_key        = wl18xx_set_key,
+       .pre_pkt_send   = wl18xx_pre_pkt_send,
+};
+
+/* HT cap appropriate for wide channels */
+static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap = {
+       .cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
+              IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40,
+       .ht_supported = true,
+       .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
+       .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
+       .mcs = {
+               .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+               .rx_highest = cpu_to_le16(150),
+               .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+               },
+};
+
+/* HT cap appropriate for SISO 20 */
+static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = {
+       .cap = IEEE80211_HT_CAP_SGI_20,
+       .ht_supported = true,
+       .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
+       .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
+       .mcs = {
+               .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+               .rx_highest = cpu_to_le16(72),
+               .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+               },
+};
+
+/* HT cap appropriate for MIMO rates in 20mhz channel */
+static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_2ghz = {
+       .cap = IEEE80211_HT_CAP_SGI_20,
+       .ht_supported = true,
+       .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
+       .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
+       .mcs = {
+               .rx_mask = { 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, },
+               .rx_highest = cpu_to_le16(144),
+               .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+               },
+};
+
+static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_5ghz = {
+       .cap = IEEE80211_HT_CAP_SGI_20,
+       .ht_supported = true,
+       .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
+       .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
+       .mcs = {
+               .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+               .rx_highest = cpu_to_le16(72),
+               .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+               },
+};
+
+static int __devinit wl18xx_probe(struct platform_device *pdev)
+{
+       struct wl1271 *wl;
+       struct ieee80211_hw *hw;
+       struct wl18xx_priv *priv;
+       int ret;
+
+       hw = wlcore_alloc_hw(sizeof(*priv));
+       if (IS_ERR(hw)) {
+               wl1271_error("can't allocate hw");
+               ret = PTR_ERR(hw);
+               goto out;
+       }
+
+       wl = hw->priv;
+       priv = wl->priv;
+       wl->ops = &wl18xx_ops;
+       wl->ptable = wl18xx_ptable;
+       wl->rtable = wl18xx_rtable;
+       wl->num_tx_desc = 32;
+       wl->num_rx_desc = 16;
+       wl->band_rate_to_idx = wl18xx_band_rate_to_idx;
+       wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX;
+       wl->hw_min_ht_rate = WL18XX_CONF_HW_RXTX_RATE_MCS0;
+       wl->fw_status_priv_len = sizeof(struct wl18xx_fw_status_priv);
+       wl->stats.fw_stats_len = sizeof(struct wl18xx_acx_statistics);
+       wl->static_data_priv_len = sizeof(struct wl18xx_static_data_priv);
+
+       if (!strcmp(ht_mode_param, "wide")) {
+               memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ],
+                      &wl18xx_siso40_ht_cap,
+                      sizeof(wl18xx_siso40_ht_cap));
+               memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ],
+                      &wl18xx_siso40_ht_cap,
+                      sizeof(wl18xx_siso40_ht_cap));
+       } else if (!strcmp(ht_mode_param, "mimo")) {
+               memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ],
+                      &wl18xx_mimo_ht_cap_2ghz,
+                      sizeof(wl18xx_mimo_ht_cap_2ghz));
+               memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ],
+                      &wl18xx_mimo_ht_cap_5ghz,
+                      sizeof(wl18xx_mimo_ht_cap_5ghz));
+       } else if (!strcmp(ht_mode_param, "siso20")) {
+               memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ],
+                      &wl18xx_siso20_ht_cap,
+                      sizeof(wl18xx_siso20_ht_cap));
+               memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ],
+                      &wl18xx_siso20_ht_cap,
+                      sizeof(wl18xx_siso20_ht_cap));
+       } else {
+               wl1271_error("invalid ht_mode '%s'", ht_mode_param);
+               ret = -EINVAL;
+               goto out_free;
+       }
+
+       ret = wl18xx_conf_init(wl, &pdev->dev);
+       if (ret < 0)
+               goto out_free;
+
+       if (!strcmp(board_type_param, "fpga")) {
+               priv->conf.phy.board_type = BOARD_TYPE_FPGA_18XX;
+       } else if (!strcmp(board_type_param, "hdk")) {
+               priv->conf.phy.board_type = BOARD_TYPE_HDK_18XX;
+               /* HACK! Just for now we hardcode HDK to 0x06 */
+               priv->conf.phy.low_band_component_type = 0x06;
+       } else if (!strcmp(board_type_param, "dvp")) {
+               priv->conf.phy.board_type = BOARD_TYPE_DVP_18XX;
+       } else if (!strcmp(board_type_param, "evb")) {
+               priv->conf.phy.board_type = BOARD_TYPE_EVB_18XX;
+       } else if (!strcmp(board_type_param, "com8")) {
+               priv->conf.phy.board_type = BOARD_TYPE_COM8_18XX;
+               /* HACK! Just for now we hardcode COM8 to 0x06 */
+               priv->conf.phy.low_band_component_type = 0x06;
+       } else {
+               wl1271_error("invalid board type '%s'", board_type_param);
+               ret = -EINVAL;
+               goto out_free;
+       }
+
+       /* If the module param is set, update it in conf */
+       if (low_band_component_param != -1)
+               priv->conf.phy.low_band_component = low_band_component_param;
+       if (low_band_component_type_param != -1)
+               priv->conf.phy.low_band_component_type =
+                       low_band_component_type_param;
+       if (high_band_component_param != -1)
+               priv->conf.phy.high_band_component = high_band_component_param;
+       if (high_band_component_type_param != -1)
+               priv->conf.phy.high_band_component_type =
+                       high_band_component_type_param;
+       if (pwr_limit_reference_11_abg_param != -1)
+               priv->conf.phy.pwr_limit_reference_11_abg =
+                       pwr_limit_reference_11_abg_param;
+       if (n_antennas_2_param != -1)
+               priv->conf.phy.number_of_assembled_ant2_4 = n_antennas_2_param;
+       if (n_antennas_5_param != -1)
+               priv->conf.phy.number_of_assembled_ant5 = n_antennas_5_param;
+       if (dc2dc_param != -1)
+               priv->conf.phy.external_pa_dc2dc = dc2dc_param;
+
+       if (!checksum_param) {
+               wl18xx_ops.set_rx_csum = NULL;
+               wl18xx_ops.init_vif = NULL;
+       }
+
+       wl->enable_11a = enable_11a_param;
+
+       return wlcore_probe(wl, pdev);
+
+out_free:
+       wlcore_free_hw(wl);
+out:
+       return ret;
+}
+
+static const struct platform_device_id wl18xx_id_table[] __devinitconst = {
+       { "wl18xx", 0 },
+       {  } /* Terminating Entry */
+};
+MODULE_DEVICE_TABLE(platform, wl18xx_id_table);
+
+static struct platform_driver wl18xx_driver = {
+       .probe          = wl18xx_probe,
+       .remove         = __devexit_p(wlcore_remove),
+       .id_table       = wl18xx_id_table,
+       .driver = {
+               .name   = "wl18xx_driver",
+               .owner  = THIS_MODULE,
+       }
+};
+
+static int __init wl18xx_init(void)
+{
+       return platform_driver_register(&wl18xx_driver);
+}
+module_init(wl18xx_init);
+
+static void __exit wl18xx_exit(void)
+{
+       platform_driver_unregister(&wl18xx_driver);
+}
+module_exit(wl18xx_exit);
+
+module_param_named(ht_mode, ht_mode_param, charp, S_IRUSR);
+MODULE_PARM_DESC(ht_mode, "Force HT mode: wide (default), mimo or siso20");
+
+module_param_named(board_type, board_type_param, charp, S_IRUSR);
+MODULE_PARM_DESC(board_type, "Board type: fpga, hdk (default), evb, com8 or "
+                "dvp");
+
+module_param_named(checksum, checksum_param, bool, S_IRUSR);
+MODULE_PARM_DESC(checksum, "Enable TCP checksum: boolean (defaults to false)");
+
+module_param_named(enable_11a, enable_11a_param, bool, S_IRUSR);
+MODULE_PARM_DESC(enable_11a, "Enable 11a (5GHz): boolean (defaults to true)");
+
+module_param_named(dc2dc, dc2dc_param, int, S_IRUSR);
+MODULE_PARM_DESC(dc2dc, "External DC2DC: u8 (defaults to 0)");
+
+module_param_named(n_antennas_2, n_antennas_2_param, int, S_IRUSR);
+MODULE_PARM_DESC(n_antennas_2,
+                "Number of installed 2.4GHz antennas: 1 (default) or 2");
+
+module_param_named(n_antennas_5, n_antennas_5_param, int, S_IRUSR);
+MODULE_PARM_DESC(n_antennas_5,
+                "Number of installed 5GHz antennas: 1 (default) or 2");
+
+module_param_named(low_band_component, low_band_component_param, int,
+                  S_IRUSR);
+MODULE_PARM_DESC(low_band_component, "Low band component: u8 "
+                "(default is 0x01)");
+
+module_param_named(low_band_component_type, low_band_component_type_param,
+                  int, S_IRUSR);
+MODULE_PARM_DESC(low_band_component_type, "Low band component type: u8 "
+                "(default is 0x05 or 0x06 depending on the board_type)");
+
+module_param_named(high_band_component, high_band_component_param, int,
+                  S_IRUSR);
+MODULE_PARM_DESC(high_band_component, "High band component: u8, "
+                "(default is 0x01)");
+
+module_param_named(high_band_component_type, high_band_component_type_param,
+                  int, S_IRUSR);
+MODULE_PARM_DESC(high_band_component_type, "High band component type: u8 "
+                "(default is 0x09)");
+
+module_param_named(pwr_limit_reference_11_abg,
+                  pwr_limit_reference_11_abg_param, int, S_IRUSR);
+MODULE_PARM_DESC(pwr_limit_reference_11_abg, "Power limit reference: u8 "
+                "(default is 0xc8)");
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
+MODULE_FIRMWARE(WL18XX_FW_NAME);
diff --git a/drivers/net/wireless/ti/wl18xx/reg.h b/drivers/net/wireless/ti/wl18xx/reg.h
new file mode 100644 (file)
index 0000000..937b71d
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * This file is part of wlcore
+ *
+ * Copyright (C) 2011 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __REG_H__
+#define __REG_H__
+
+#define WL18XX_REGISTERS_BASE      0x00800000
+#define WL18XX_CODE_BASE           0x00000000
+#define WL18XX_DATA_BASE           0x00400000
+#define WL18XX_DOUBLE_BUFFER_BASE  0x00600000
+#define WL18XX_MCU_KEY_SEARCH_BASE 0x00700000
+#define WL18XX_PHY_BASE            0x00900000
+#define WL18XX_TOP_OCP_BASE        0x00A00000
+#define WL18XX_PACKET_RAM_BASE     0x00B00000
+#define WL18XX_HOST_BASE           0x00C00000
+
+#define WL18XX_REGISTERS_DOWN_SIZE 0x0000B000
+
+#define WL18XX_REG_BOOT_PART_START 0x00802000
+#define WL18XX_REG_BOOT_PART_SIZE  0x00014578
+
+#define WL18XX_PHY_INIT_MEM_ADDR   0x80926000
+
+#define WL18XX_SDIO_WSPI_BASE          (WL18XX_REGISTERS_BASE)
+#define WL18XX_REG_CONFIG_BASE         (WL18XX_REGISTERS_BASE + 0x02000)
+#define WL18XX_WGCM_REGS_BASE          (WL18XX_REGISTERS_BASE + 0x03000)
+#define WL18XX_ENC_BASE                        (WL18XX_REGISTERS_BASE + 0x04000)
+#define WL18XX_INTERRUPT_BASE          (WL18XX_REGISTERS_BASE + 0x05000)
+#define WL18XX_UART_BASE               (WL18XX_REGISTERS_BASE + 0x06000)
+#define WL18XX_WELP_BASE               (WL18XX_REGISTERS_BASE + 0x07000)
+#define WL18XX_TCP_CKSM_BASE           (WL18XX_REGISTERS_BASE + 0x08000)
+#define WL18XX_FIFO_BASE               (WL18XX_REGISTERS_BASE + 0x09000)
+#define WL18XX_OCP_BRIDGE_BASE         (WL18XX_REGISTERS_BASE + 0x0A000)
+#define WL18XX_PMAC_RX_BASE            (WL18XX_REGISTERS_BASE + 0x14800)
+#define WL18XX_PMAC_ACM_BASE           (WL18XX_REGISTERS_BASE + 0x14C00)
+#define WL18XX_PMAC_TX_BASE            (WL18XX_REGISTERS_BASE + 0x15000)
+#define WL18XX_PMAC_CSR_BASE           (WL18XX_REGISTERS_BASE + 0x15400)
+
+#define WL18XX_REG_ECPU_CONTROL                (WL18XX_REGISTERS_BASE + 0x02004)
+#define WL18XX_REG_INTERRUPT_NO_CLEAR  (WL18XX_REGISTERS_BASE + 0x050E8)
+#define WL18XX_REG_INTERRUPT_ACK       (WL18XX_REGISTERS_BASE + 0x050F0)
+#define WL18XX_REG_INTERRUPT_TRIG      (WL18XX_REGISTERS_BASE + 0x5074)
+#define WL18XX_REG_INTERRUPT_TRIG_H    (WL18XX_REGISTERS_BASE + 0x5078)
+#define WL18XX_REG_INTERRUPT_MASK      (WL18XX_REGISTERS_BASE + 0x0050DC)
+
+#define WL18XX_REG_CHIP_ID_B           (WL18XX_REGISTERS_BASE + 0x01542C)
+
+#define WL18XX_SLV_MEM_DATA            (WL18XX_HOST_BASE + 0x0018)
+#define WL18XX_SLV_REG_DATA            (WL18XX_HOST_BASE + 0x0008)
+
+/* Scratch Pad registers*/
+#define WL18XX_SCR_PAD0                        (WL18XX_REGISTERS_BASE + 0x0154EC)
+#define WL18XX_SCR_PAD1                        (WL18XX_REGISTERS_BASE + 0x0154F0)
+#define WL18XX_SCR_PAD2                        (WL18XX_REGISTERS_BASE + 0x0154F4)
+#define WL18XX_SCR_PAD3                        (WL18XX_REGISTERS_BASE + 0x0154F8)
+#define WL18XX_SCR_PAD4                        (WL18XX_REGISTERS_BASE + 0x0154FC)
+#define WL18XX_SCR_PAD4_SET            (WL18XX_REGISTERS_BASE + 0x015504)
+#define WL18XX_SCR_PAD4_CLR            (WL18XX_REGISTERS_BASE + 0x015500)
+#define WL18XX_SCR_PAD5                        (WL18XX_REGISTERS_BASE + 0x015508)
+#define WL18XX_SCR_PAD5_SET            (WL18XX_REGISTERS_BASE + 0x015510)
+#define WL18XX_SCR_PAD5_CLR            (WL18XX_REGISTERS_BASE + 0x01550C)
+#define WL18XX_SCR_PAD6                        (WL18XX_REGISTERS_BASE + 0x015514)
+#define WL18XX_SCR_PAD7                        (WL18XX_REGISTERS_BASE + 0x015518)
+#define WL18XX_SCR_PAD8                        (WL18XX_REGISTERS_BASE + 0x01551C)
+#define WL18XX_SCR_PAD9                        (WL18XX_REGISTERS_BASE + 0x015520)
+
+/* Spare registers*/
+#define WL18XX_SPARE_A1                        (WL18XX_REGISTERS_BASE + 0x002194)
+#define WL18XX_SPARE_A2                        (WL18XX_REGISTERS_BASE + 0x002198)
+#define WL18XX_SPARE_A3                        (WL18XX_REGISTERS_BASE + 0x00219C)
+#define WL18XX_SPARE_A4                        (WL18XX_REGISTERS_BASE + 0x0021A0)
+#define WL18XX_SPARE_A5                        (WL18XX_REGISTERS_BASE + 0x0021A4)
+#define WL18XX_SPARE_A6                        (WL18XX_REGISTERS_BASE + 0x0021A8)
+#define WL18XX_SPARE_A7                        (WL18XX_REGISTERS_BASE + 0x0021AC)
+#define WL18XX_SPARE_A8                        (WL18XX_REGISTERS_BASE + 0x0021B0)
+#define WL18XX_SPARE_B1                        (WL18XX_REGISTERS_BASE + 0x015524)
+#define WL18XX_SPARE_B2                        (WL18XX_REGISTERS_BASE + 0x015528)
+#define WL18XX_SPARE_B3                        (WL18XX_REGISTERS_BASE + 0x01552C)
+#define WL18XX_SPARE_B4                        (WL18XX_REGISTERS_BASE + 0x015530)
+#define WL18XX_SPARE_B5                        (WL18XX_REGISTERS_BASE + 0x015534)
+#define WL18XX_SPARE_B6                        (WL18XX_REGISTERS_BASE + 0x015538)
+#define WL18XX_SPARE_B7                        (WL18XX_REGISTERS_BASE + 0x01553C)
+#define WL18XX_SPARE_B8                        (WL18XX_REGISTERS_BASE + 0x015540)
+
+#define WL18XX_REG_COMMAND_MAILBOX_PTR (WL18XX_SCR_PAD0)
+#define WL18XX_REG_EVENT_MAILBOX_PTR   (WL18XX_SCR_PAD1)
+#define WL18XX_EEPROMLESS_IND          (WL18XX_SCR_PAD4)
+
+#define WL18XX_WELP_ARM_COMMAND                (WL18XX_REGISTERS_BASE + 0x7100)
+#define WL18XX_ENABLE                  (WL18XX_REGISTERS_BASE + 0x01543C)
+
+/* PRCM registers */
+#define PLATFORM_DETECTION             0xA0E3E0
+#define OCS_EN                         0xA02080
+#define PRIMARY_CLK_DETECT             0xA020A6
+#define PLLSH_WCS_PLL_N                        0xA02362
+#define PLLSH_WCS_PLL_M                        0xA02360
+#define PLLSH_WCS_PLL_Q_FACTOR_CFG_1   0xA02364
+#define PLLSH_WCS_PLL_Q_FACTOR_CFG_2   0xA02366
+#define PLLSH_WCS_PLL_P_FACTOR_CFG_1   0xA02368
+#define PLLSH_WCS_PLL_P_FACTOR_CFG_2   0xA0236A
+#define PLLSH_WCS_PLL_SWALLOW_EN       0xA0236C
+#define PLLSH_WL_PLL_EN                        0xA02392
+
+#define PLLSH_WCS_PLL_Q_FACTOR_CFG_1_MASK      0xFFFF
+#define PLLSH_WCS_PLL_Q_FACTOR_CFG_2_MASK      0x007F
+#define PLLSH_WCS_PLL_P_FACTOR_CFG_1_MASK      0xFFFF
+#define PLLSH_WCS_PLL_P_FACTOR_CFG_2_MASK      0x000F
+
+#define PLLSH_WCS_PLL_SWALLOW_EN_VAL1  0x1
+#define PLLSH_WCS_PLL_SWALLOW_EN_VAL2  0x12
+
+#define WL18XX_REG_FUSE_DATA_1_3       0xA0260C
+#define WL18XX_PG_VER_MASK             0x70
+#define WL18XX_PG_VER_OFFSET           4
+
+#define WL18XX_REG_FUSE_BD_ADDR_1      0xA02602
+#define WL18XX_REG_FUSE_BD_ADDR_2      0xA02606
+
+#define WL18XX_CMD_MBOX_ADDRESS                0xB007B4
+
+#define WL18XX_FW_STATUS_ADDR          0x50F8
+
+#define CHIP_ID_185x_PG10              (0x06030101)
+#define CHIP_ID_185x_PG20              (0x06030111)
+
+/*
+ * Host Command Interrupt. Setting this bit masks
+ * the interrupt that the host issues to inform
+ * the FW that it has sent a command
+ * to the Wlan hardware Command Mailbox.
+ */
+#define WL18XX_INTR_TRIG_CMD       BIT(28)
+
+/*
+ * Host Event Acknowlegde Interrupt. The host
+ * sets this bit to acknowledge that it received
+ * the unsolicited information from the event
+ * mailbox.
+ */
+#define WL18XX_INTR_TRIG_EVENT_ACK BIT(29)
+
+/*
+ * To boot the firmware in PLT mode we need to write this value in
+ * SCR_PAD8 before starting.
+ */
+#define WL18XX_SCR_PAD8_PLT    0xBABABEBE
+
+enum {
+       COMPONENT_NO_SWITCH     = 0x0,
+       COMPONENT_2_WAY_SWITCH  = 0x1,
+       COMPONENT_3_WAY_SWITCH  = 0x2,
+       COMPONENT_MATCHING      = 0x3,
+};
+
+enum {
+       FEM_NONE        = 0x0,
+       FEM_VENDOR_1    = 0x1,
+       FEM_VENDOR_2    = 0x2,
+       FEM_VENDOR_3    = 0x3,
+};
+
+enum {
+       BOARD_TYPE_EVB_18XX     = 0,
+       BOARD_TYPE_DVP_18XX     = 1,
+       BOARD_TYPE_HDK_18XX     = 2,
+       BOARD_TYPE_FPGA_18XX    = 3,
+       BOARD_TYPE_COM8_18XX    = 4,
+
+       NUM_BOARD_TYPES,
+};
+
+#endif /* __REG_H__ */
diff --git a/drivers/net/wireless/ti/wl18xx/tx.c b/drivers/net/wireless/ti/wl18xx/tx.c
new file mode 100644 (file)
index 0000000..5b1fb10
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "../wlcore/wlcore.h"
+#include "../wlcore/cmd.h"
+#include "../wlcore/debug.h"
+#include "../wlcore/acx.h"
+#include "../wlcore/tx.h"
+
+#include "wl18xx.h"
+#include "tx.h"
+
+static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
+{
+       struct ieee80211_tx_info *info;
+       struct sk_buff *skb;
+       int id = tx_stat_byte & WL18XX_TX_STATUS_DESC_ID_MASK;
+       bool tx_success;
+
+       /* check for id legality */
+       if (unlikely(id >= wl->num_tx_desc || wl->tx_frames[id] == NULL)) {
+               wl1271_warning("illegal id in tx completion: %d", id);
+               return;
+       }
+
+       /* a zero bit indicates Tx success */
+       tx_success = !(tx_stat_byte & BIT(WL18XX_TX_STATUS_STAT_BIT_IDX));
+
+
+       skb = wl->tx_frames[id];
+       info = IEEE80211_SKB_CB(skb);
+
+       if (wl12xx_is_dummy_packet(wl, skb)) {
+               wl1271_free_tx_id(wl, id);
+               return;
+       }
+
+       /* update the TX status info */
+       if (tx_success && !(info->flags & IEEE80211_TX_CTL_NO_ACK))
+               info->flags |= IEEE80211_TX_STAT_ACK;
+
+       /* no real data about Tx completion */
+       info->status.rates[0].idx = -1;
+       info->status.rates[0].count = 0;
+       info->status.rates[0].flags = 0;
+       info->status.ack_signal = -1;
+
+       if (!tx_success)
+               wl->stats.retry_count++;
+
+       /*
+        * TODO: update sequence number for encryption? seems to be
+        * unsupported for now. needed for recovery with encryption.
+        */
+
+       /* remove private header from packet */
+       skb_pull(skb, sizeof(struct wl1271_tx_hw_descr));
+
+       /* remove TKIP header space if present */
+       if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) &&
+           info->control.hw_key &&
+           info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
+               int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+               memmove(skb->data + WL1271_EXTRA_SPACE_TKIP, skb->data, hdrlen);
+               skb_pull(skb, WL1271_EXTRA_SPACE_TKIP);
+       }
+
+       wl1271_debug(DEBUG_TX, "tx status id %u skb 0x%p success %d",
+                    id, skb, tx_success);
+
+       /* return the packet to the stack */
+       skb_queue_tail(&wl->deferred_tx_queue, skb);
+       queue_work(wl->freezable_wq, &wl->netstack_work);
+       wl1271_free_tx_id(wl, id);
+}
+
+void wl18xx_tx_immediate_complete(struct wl1271 *wl)
+{
+       struct wl18xx_fw_status_priv *status_priv =
+               (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
+       struct wl18xx_priv *priv = wl->priv;
+       u8 i;
+
+       /* nothing to do here */
+       if (priv->last_fw_rls_idx == status_priv->fw_release_idx)
+               return;
+
+       /* freed Tx descriptors */
+       wl1271_debug(DEBUG_TX, "last released desc = %d, current idx = %d",
+                    priv->last_fw_rls_idx, status_priv->fw_release_idx);
+
+       if (status_priv->fw_release_idx >= WL18XX_FW_MAX_TX_STATUS_DESC) {
+               wl1271_error("invalid desc release index %d",
+                            status_priv->fw_release_idx);
+               WARN_ON(1);
+               return;
+       }
+
+       for (i = priv->last_fw_rls_idx;
+            i != status_priv->fw_release_idx;
+            i = (i + 1) % WL18XX_FW_MAX_TX_STATUS_DESC) {
+               wl18xx_tx_complete_packet(wl,
+                       status_priv->released_tx_desc[i]);
+
+               wl->tx_results_count++;
+       }
+
+       priv->last_fw_rls_idx = status_priv->fw_release_idx;
+}
diff --git a/drivers/net/wireless/ti/wl18xx/tx.h b/drivers/net/wireless/ti/wl18xx/tx.h
new file mode 100644 (file)
index 0000000..ccddc54
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL18XX_TX_H__
+#define __WL18XX_TX_H__
+
+#include "../wlcore/wlcore.h"
+
+#define WL18XX_TX_HW_BLOCK_SPARE        1
+/* for special cases - namely, TKIP and GEM */
+#define WL18XX_TX_HW_EXTRA_BLOCK_SPARE  2
+#define WL18XX_TX_HW_BLOCK_SIZE         268
+
+#define WL18XX_TX_STATUS_DESC_ID_MASK    0x7F
+#define WL18XX_TX_STATUS_STAT_BIT_IDX    7
+
+/* Indicates this TX HW frame is not padded to SDIO block size */
+#define WL18XX_TX_CTRL_NOT_PADDED      BIT(7)
+
+/*
+ * The FW uses a special bit to indicate a wide channel should be used in
+ * the rate policy.
+ */
+#define CONF_TX_RATE_USE_WIDE_CHAN BIT(31)
+
+void wl18xx_tx_immediate_complete(struct wl1271 *wl);
+
+#endif /* __WL12XX_TX_H__ */
diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h
new file mode 100644 (file)
index 0000000..bc67a47
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL18XX_PRIV_H__
+#define __WL18XX_PRIV_H__
+
+#include "conf.h"
+
+#define WL18XX_CMD_MAX_SIZE          740
+
+struct wl18xx_priv {
+       /* buffer for sending commands to FW */
+       u8 cmd_buf[WL18XX_CMD_MAX_SIZE];
+
+       struct wl18xx_priv_conf conf;
+
+       /* Index of last released Tx desc in FW */
+       u8 last_fw_rls_idx;
+
+       /* number of VIFs requiring extra spare mem-blocks */
+       int extra_spare_vif_count;
+};
+
+#define WL18XX_FW_MAX_TX_STATUS_DESC 33
+
+struct wl18xx_fw_status_priv {
+       /*
+        * Index in released_tx_desc for first byte that holds
+        * released tx host desc
+        */
+       u8 fw_release_idx;
+
+       /*
+        * Array of host Tx descriptors, where fw_release_idx
+        * indicated the first released idx.
+        */
+       u8 released_tx_desc[WL18XX_FW_MAX_TX_STATUS_DESC];
+
+       u8 padding[2];
+};
+
+#define WL18XX_PHY_VERSION_MAX_LEN 20
+
+struct wl18xx_static_data_priv {
+       char phy_version[WL18XX_PHY_VERSION_MAX_LEN];
+};
+
+struct wl18xx_clk_cfg {
+       u32 n;
+       u32 m;
+       u32 p;
+       u32 q;
+       bool swallow;
+};
+
+enum {
+       CLOCK_CONFIG_16_2_M     = 1,
+       CLOCK_CONFIG_16_368_M,
+       CLOCK_CONFIG_16_8_M,
+       CLOCK_CONFIG_19_2_M,
+       CLOCK_CONFIG_26_M,
+       CLOCK_CONFIG_32_736_M,
+       CLOCK_CONFIG_33_6_M,
+       CLOCK_CONFIG_38_468_M,
+       CLOCK_CONFIG_52_M,
+
+       NUM_CLOCK_CONFIGS,
+};
+
+#endif /* __WL18XX_PRIV_H__ */
index f3d6fa5..b9ec42c 100644 (file)
@@ -86,6 +86,7 @@ out:
        kfree(auth);
        return ret;
 }
+EXPORT_SYMBOL_GPL(wl1271_acx_sleep_auth);
 
 int wl1271_acx_tx_power(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                        int power)
@@ -708,14 +709,14 @@ out:
        return ret;
 }
 
-int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats)
+int wl1271_acx_statistics(struct wl1271 *wl, void *stats)
 {
        int ret;
 
        wl1271_debug(DEBUG_ACX, "acx statistics");
 
        ret = wl1271_cmd_interrogate(wl, ACX_STATISTICS, stats,
-                                    sizeof(*stats));
+                                    wl->stats.fw_stats_len);
        if (ret < 0) {
                wl1271_warning("acx statistics failed: %d", ret);
                return -ENOMEM;
@@ -997,6 +998,7 @@ out:
        kfree(mem_conf);
        return ret;
 }
+EXPORT_SYMBOL_GPL(wl12xx_acx_mem_cfg);
 
 int wl1271_acx_init_mem_config(struct wl1271 *wl)
 {
@@ -1027,6 +1029,7 @@ int wl1271_acx_init_mem_config(struct wl1271 *wl)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(wl1271_acx_init_mem_config);
 
 int wl1271_acx_init_rx_interrupt(struct wl1271 *wl)
 {
@@ -1150,6 +1153,7 @@ out:
        kfree(acx);
        return ret;
 }
+EXPORT_SYMBOL_GPL(wl1271_acx_pm_config);
 
 int wl1271_acx_keep_alive_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                               bool enable)
index e6a7486..c018125 100644 (file)
 #define WL1271_ACX_INTR_TRACE_A            BIT(7)
 /* Trace message on MBOX #B */
 #define WL1271_ACX_INTR_TRACE_B            BIT(8)
+/* SW FW Initiated interrupt Watchdog timer expiration */
+#define WL1271_ACX_SW_INTR_WATCHDOG        BIT(9)
 
-#define WL1271_ACX_INTR_ALL               0xFFFFFFFF
-#define WL1271_ACX_ALL_EVENTS_VECTOR       (WL1271_ACX_INTR_WATCHDOG      | \
-                                           WL1271_ACX_INTR_INIT_COMPLETE | \
-                                           WL1271_ACX_INTR_EVENT_A       | \
-                                           WL1271_ACX_INTR_EVENT_B       | \
-                                           WL1271_ACX_INTR_CMD_COMPLETE  | \
-                                           WL1271_ACX_INTR_HW_AVAILABLE  | \
-                                           WL1271_ACX_INTR_DATA)
-
-#define WL1271_INTR_MASK                   (WL1271_ACX_INTR_WATCHDOG     | \
-                                           WL1271_ACX_INTR_EVENT_A      | \
-                                           WL1271_ACX_INTR_EVENT_B      | \
-                                           WL1271_ACX_INTR_HW_AVAILABLE | \
-                                           WL1271_ACX_INTR_DATA)
+#define WL1271_ACX_INTR_ALL             0xFFFFFFFF
+
+/* all possible interrupts - only appropriate ones will be masked in */
+#define WLCORE_ALL_INTR_MASK           (WL1271_ACX_INTR_WATCHDOG     | \
+                                       WL1271_ACX_INTR_EVENT_A       | \
+                                       WL1271_ACX_INTR_EVENT_B       | \
+                                       WL1271_ACX_INTR_HW_AVAILABLE  | \
+                                       WL1271_ACX_INTR_DATA          | \
+                                       WL1271_ACX_SW_INTR_WATCHDOG)
 
 /* Target's information element */
 struct acx_header {
@@ -417,228 +414,6 @@ struct acx_ctsprotect {
        u8 padding[2];
 } __packed;
 
-struct acx_tx_statistics {
-       __le32 internal_desc_overflow;
-}  __packed;
-
-struct acx_rx_statistics {
-       __le32 out_of_mem;
-       __le32 hdr_overflow;
-       __le32 hw_stuck;
-       __le32 dropped;
-       __le32 fcs_err;
-       __le32 xfr_hint_trig;
-       __le32 path_reset;
-       __le32 reset_counter;
-} __packed;
-
-struct acx_dma_statistics {
-       __le32 rx_requested;
-       __le32 rx_errors;
-       __le32 tx_requested;
-       __le32 tx_errors;
-}  __packed;
-
-struct acx_isr_statistics {
-       /* host command complete */
-       __le32 cmd_cmplt;
-
-       /* fiqisr() */
-       __le32 fiqs;
-
-       /* (INT_STS_ND & INT_TRIG_RX_HEADER) */
-       __le32 rx_headers;
-
-       /* (INT_STS_ND & INT_TRIG_RX_CMPLT) */
-       __le32 rx_completes;
-
-       /* (INT_STS_ND & INT_TRIG_NO_RX_BUF) */
-       __le32 rx_mem_overflow;
-
-       /* (INT_STS_ND & INT_TRIG_S_RX_RDY) */
-       __le32 rx_rdys;
-
-       /* irqisr() */
-       __le32 irqs;
-
-       /* (INT_STS_ND & INT_TRIG_TX_PROC) */
-       __le32 tx_procs;
-
-       /* (INT_STS_ND & INT_TRIG_DECRYPT_DONE) */
-       __le32 decrypt_done;
-
-       /* (INT_STS_ND & INT_TRIG_DMA0) */
-       __le32 dma0_done;
-
-       /* (INT_STS_ND & INT_TRIG_DMA1) */
-       __le32 dma1_done;
-
-       /* (INT_STS_ND & INT_TRIG_TX_EXC_CMPLT) */
-       __le32 tx_exch_complete;
-
-       /* (INT_STS_ND & INT_TRIG_COMMAND) */
-       __le32 commands;
-
-       /* (INT_STS_ND & INT_TRIG_RX_PROC) */
-       __le32 rx_procs;
-
-       /* (INT_STS_ND & INT_TRIG_PM_802) */
-       __le32 hw_pm_mode_changes;
-
-       /* (INT_STS_ND & INT_TRIG_ACKNOWLEDGE) */
-       __le32 host_acknowledges;
-
-       /* (INT_STS_ND & INT_TRIG_PM_PCI) */
-       __le32 pci_pm;
-
-       /* (INT_STS_ND & INT_TRIG_ACM_WAKEUP) */
-       __le32 wakeups;
-
-       /* (INT_STS_ND & INT_TRIG_LOW_RSSI) */
-       __le32 low_rssi;
-} __packed;
-
-struct acx_wep_statistics {
-       /* WEP address keys configured */
-       __le32 addr_key_count;
-
-       /* default keys configured */
-       __le32 default_key_count;
-
-       __le32 reserved;
-
-       /* number of times that WEP key not found on lookup */
-       __le32 key_not_found;
-
-       /* number of times that WEP key decryption failed */
-       __le32 decrypt_fail;
-
-       /* WEP packets decrypted */
-       __le32 packets;
-
-       /* WEP decrypt interrupts */
-       __le32 interrupt;
-} __packed;
-
-#define ACX_MISSED_BEACONS_SPREAD 10
-
-struct acx_pwr_statistics {
-       /* the amount of enters into power save mode (both PD & ELP) */
-       __le32 ps_enter;
-
-       /* the amount of enters into ELP mode */
-       __le32 elp_enter;
-
-       /* the amount of missing beacon interrupts to the host */
-       __le32 missing_bcns;
-
-       /* the amount of wake on host-access times */
-       __le32 wake_on_host;
-
-       /* the amount of wake on timer-expire */
-       __le32 wake_on_timer_exp;
-
-       /* the number of packets that were transmitted with PS bit set */
-       __le32 tx_with_ps;
-
-       /* the number of packets that were transmitted with PS bit clear */
-       __le32 tx_without_ps;
-
-       /* the number of received beacons */
-       __le32 rcvd_beacons;
-
-       /* the number of entering into PowerOn (power save off) */
-       __le32 power_save_off;
-
-       /* the number of entries into power save mode */
-       __le16 enable_ps;
-
-       /*
-        * the number of exits from power save, not including failed PS
-        * transitions
-        */
-       __le16 disable_ps;
-
-       /*
-        * the number of times the TSF counter was adjusted because
-        * of drift
-        */
-       __le32 fix_tsf_ps;
-
-       /* Gives statistics about the spread continuous missed beacons.
-        * The 16 LSB are dedicated for the PS mode.
-        * The 16 MSB are dedicated for the PS mode.
-        * cont_miss_bcns_spread[0] - single missed beacon.
-        * cont_miss_bcns_spread[1] - two continuous missed beacons.
-        * cont_miss_bcns_spread[2] - three continuous missed beacons.
-        * ...
-        * cont_miss_bcns_spread[9] - ten and more continuous missed beacons.
-       */
-       __le32 cont_miss_bcns_spread[ACX_MISSED_BEACONS_SPREAD];
-
-       /* the number of beacons in awake mode */
-       __le32 rcvd_awake_beacons;
-} __packed;
-
-struct acx_mic_statistics {
-       __le32 rx_pkts;
-       __le32 calc_failure;
-} __packed;
-
-struct acx_aes_statistics {
-       __le32 encrypt_fail;
-       __le32 decrypt_fail;
-       __le32 encrypt_packets;
-       __le32 decrypt_packets;
-       __le32 encrypt_interrupt;
-       __le32 decrypt_interrupt;
-} __packed;
-
-struct acx_event_statistics {
-       __le32 heart_beat;
-       __le32 calibration;
-       __le32 rx_mismatch;
-       __le32 rx_mem_empty;
-       __le32 rx_pool;
-       __le32 oom_late;
-       __le32 phy_transmit_error;
-       __le32 tx_stuck;
-} __packed;
-
-struct acx_ps_statistics {
-       __le32 pspoll_timeouts;
-       __le32 upsd_timeouts;
-       __le32 upsd_max_sptime;
-       __le32 upsd_max_apturn;
-       __le32 pspoll_max_apturn;
-       __le32 pspoll_utilization;
-       __le32 upsd_utilization;
-} __packed;
-
-struct acx_rxpipe_statistics {
-       __le32 rx_prep_beacon_drop;
-       __le32 descr_host_int_trig_rx_data;
-       __le32 beacon_buffer_thres_host_int_trig_rx_data;
-       __le32 missed_beacon_host_int_trig_rx_data;
-       __le32 tx_xfr_host_int_trig_rx_data;
-} __packed;
-
-struct acx_statistics {
-       struct acx_header header;
-
-       struct acx_tx_statistics tx;
-       struct acx_rx_statistics rx;
-       struct acx_dma_statistics dma;
-       struct acx_isr_statistics isr;
-       struct acx_wep_statistics wep;
-       struct acx_pwr_statistics pwr;
-       struct acx_aes_statistics aes;
-       struct acx_mic_statistics mic;
-       struct acx_event_statistics event;
-       struct acx_ps_statistics ps;
-       struct acx_rxpipe_statistics rxpipe;
-} __packed;
-
 struct acx_rate_class {
        __le32 enabled_rates;
        u8 short_retry_limit;
@@ -828,6 +603,8 @@ struct wl1271_acx_keep_alive_config {
 #define HOST_IF_CFG_RX_FIFO_ENABLE     BIT(0)
 #define HOST_IF_CFG_TX_EXTRA_BLKS_SWAP BIT(1)
 #define HOST_IF_CFG_TX_PAD_TO_SDIO_BLK BIT(3)
+#define HOST_IF_CFG_RX_PAD_TO_SDIO_BLK BIT(4)
+#define HOST_IF_CFG_ADD_RX_ALIGNMENT   BIT(6)
 
 enum {
        WL1271_ACX_TRIG_TYPE_LEVEL = 0,
@@ -946,7 +723,7 @@ struct wl1271_acx_ht_information {
        u8 padding[2];
 } __packed;
 
-#define RX_BA_MAX_SESSIONS 2
+#define RX_BA_MAX_SESSIONS 3
 
 struct wl1271_acx_ba_initiator_policy {
        struct acx_header header;
@@ -1243,6 +1020,7 @@ enum {
        ACX_CONFIG_HANGOVER              = 0x0042,
        ACX_FEATURE_CFG                  = 0x0043,
        ACX_PROTECTION_CFG               = 0x0044,
+       ACX_CHECKSUM_CONFIG              = 0x0045,
 };
 
 
@@ -1281,7 +1059,7 @@ int wl1271_acx_set_preamble(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                            enum acx_preamble_type preamble);
 int wl1271_acx_cts_protect(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                           enum acx_ctsprotect_type ctsprotect);
-int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats);
+int wl1271_acx_statistics(struct wl1271 *wl, void *stats);
 int wl1271_acx_sta_rate_policies(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c,
                      u8 idx);
index 9b98230..0fda500 100644 (file)
@@ -45,10 +45,17 @@ static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag)
        wlcore_write_reg(wl, REG_ECPU_CONTROL, cpu_ctrl);
 }
 
-static int wlcore_parse_fw_ver(struct wl1271 *wl)
+static int wlcore_boot_parse_fw_ver(struct wl1271 *wl,
+                                   struct wl1271_static_data *static_data)
 {
        int ret;
 
+       strncpy(wl->chip.fw_ver_str, static_data->fw_version,
+               sizeof(wl->chip.fw_ver_str));
+
+       /* make sure the string is NULL-terminated */
+       wl->chip.fw_ver_str[sizeof(wl->chip.fw_ver_str) - 1] = '\0';
+
        ret = sscanf(wl->chip.fw_ver_str + 4, "%u.%u.%u.%u.%u",
                     &wl->chip.fw_ver[0], &wl->chip.fw_ver[1],
                     &wl->chip.fw_ver[2], &wl->chip.fw_ver[3],
@@ -57,43 +64,43 @@ static int wlcore_parse_fw_ver(struct wl1271 *wl)
        if (ret != 5) {
                wl1271_warning("fw version incorrect value");
                memset(wl->chip.fw_ver, 0, sizeof(wl->chip.fw_ver));
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
        ret = wlcore_identify_fw(wl);
        if (ret < 0)
-               return ret;
-
-       return 0;
+               goto out;
+out:
+       return ret;
 }
 
-static int wlcore_boot_fw_version(struct wl1271 *wl)
+static int wlcore_boot_static_data(struct wl1271 *wl)
 {
        struct wl1271_static_data *static_data;
+       size_t len = sizeof(*static_data) + wl->static_data_priv_len;
        int ret;
 
-       static_data = kmalloc(sizeof(*static_data), GFP_KERNEL | GFP_DMA);
+       static_data = kmalloc(len, GFP_KERNEL);
        if (!static_data) {
-               wl1271_error("Couldn't allocate memory for static data!");
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto out;
        }
 
-       wl1271_read(wl, wl->cmd_box_addr, static_data, sizeof(*static_data),
-                   false);
-
-       strncpy(wl->chip.fw_ver_str, static_data->fw_version,
-               sizeof(wl->chip.fw_ver_str));
-
-       kfree(static_data);
+       wl1271_read(wl, wl->cmd_box_addr, static_data, len, false);
 
-       /* make sure the string is NULL-terminated */
-       wl->chip.fw_ver_str[sizeof(wl->chip.fw_ver_str) - 1] = '\0';
+       ret = wlcore_boot_parse_fw_ver(wl, static_data);
+       if (ret < 0)
+               goto out_free;
 
-       ret = wlcore_parse_fw_ver(wl);
+       ret = wlcore_handle_static_data(wl, static_data);
        if (ret < 0)
-               return ret;
+               goto out_free;
 
-       return 0;
+out_free:
+       kfree(static_data);
+out:
+       return ret;
 }
 
 static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
@@ -204,8 +211,10 @@ int wlcore_boot_upload_nvs(struct wl1271 *wl)
        u32 dest_addr, val;
        u8 *nvs_ptr, *nvs_aligned;
 
-       if (wl->nvs == NULL)
+       if (wl->nvs == NULL) {
+               wl1271_error("NVS file is needed during boot");
                return -ENODEV;
+       }
 
        if (wl->quirks & WLCORE_QUIRK_LEGACY_NVS) {
                struct wl1271_nvs_file *nvs =
@@ -400,9 +409,9 @@ int wlcore_boot_run_firmware(struct wl1271 *wl)
        wl1271_debug(DEBUG_MAILBOX, "MBOX ptrs: 0x%x 0x%x",
                     wl->mbox_ptr[0], wl->mbox_ptr[1]);
 
-       ret = wlcore_boot_fw_version(wl);
+       ret = wlcore_boot_static_data(wl);
        if (ret < 0) {
-               wl1271_error("couldn't boot firmware");
+               wl1271_error("error getting static data");
                return ret;
        }
 
index 094981d..a525225 100644 (file)
@@ -40,6 +40,7 @@ struct wl1271_static_data {
        u8 fw_version[WL1271_FW_VERSION_MAX_LEN];
        u32 hw_version;
        u8 tx_power_table[WL1271_NO_SUBBANDS][WL1271_NO_POWER_LEVELS];
+       u8 priv[0];
 };
 
 /* number of times we try to read the INIT interrupt */
index 5b128a9..885364c 100644 (file)
@@ -36,6 +36,7 @@
 #include "cmd.h"
 #include "event.h"
 #include "tx.h"
+#include "hw_ops.h"
 
 #define WL1271_CMD_FAST_POLL_COUNT       50
 
@@ -291,6 +292,23 @@ static int wl12xx_get_new_session_id(struct wl1271 *wl,
        return wlvif->session_counter;
 }
 
+static u8 wlcore_get_native_channel_type(u8 nl_channel_type)
+{
+       switch (nl_channel_type) {
+       case NL80211_CHAN_NO_HT:
+               return WLCORE_CHAN_NO_HT;
+       case NL80211_CHAN_HT20:
+               return WLCORE_CHAN_HT20;
+       case NL80211_CHAN_HT40MINUS:
+               return WLCORE_CHAN_HT40MINUS;
+       case NL80211_CHAN_HT40PLUS:
+               return WLCORE_CHAN_HT40PLUS;
+       default:
+               WARN_ON(1);
+               return WLCORE_CHAN_NO_HT;
+       }
+}
+
 static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
                                     struct wl12xx_vif *wlvif)
 {
@@ -407,6 +425,7 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        memcpy(cmd->sta.ssid, wlvif->ssid, wlvif->ssid_len);
        memcpy(cmd->sta.bssid, vif->bss_conf.bssid, ETH_ALEN);
        cmd->sta.local_rates = cpu_to_le32(wlvif->rate_set);
+       cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type);
 
        if (wlvif->sta.hlid == WL12XX_INVALID_LINK_ID) {
                ret = wl12xx_allocate_link(wl, wlvif, &wlvif->sta.hlid);
@@ -482,6 +501,7 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        struct wl12xx_cmd_role_start *cmd;
        struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
        struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+       u32 supported_rates;
        int ret;
 
        wl1271_debug(DEBUG_CMD, "cmd role start ap %d", wlvif->role_id);
@@ -519,6 +539,7 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        /* FIXME: Change when adding DFS */
        cmd->ap.reset_tsf = 1;  /* By default reset AP TSF */
        cmd->channel = wlvif->channel;
+       cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type);
 
        if (!bss_conf->hidden_ssid) {
                /* take the SSID from the beacon for backward compatibility */
@@ -531,7 +552,13 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                memcpy(cmd->ap.ssid, bss_conf->ssid, bss_conf->ssid_len);
        }
 
-       cmd->ap.local_rates = cpu_to_le32(0xffffffff);
+       supported_rates = CONF_TX_AP_ENABLED_RATES | CONF_TX_MCS_RATES |
+               wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif);
+
+       wl1271_debug(DEBUG_CMD, "cmd role start ap with supported_rates 0x%08x",
+                    supported_rates);
+
+       cmd->ap.local_rates = cpu_to_le32(supported_rates);
 
        switch (wlvif->band) {
        case IEEE80211_BAND_2GHZ:
@@ -797,6 +824,7 @@ out:
        kfree(cmd);
        return ret;
 }
+EXPORT_SYMBOL_GPL(wl1271_cmd_data_path);
 
 int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                       u8 ps_mode, u16 auto_ps_timeout)
@@ -1018,7 +1046,7 @@ out:
 
 int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
-       int ret, extra;
+       int ret, extra = 0;
        u16 fc;
        struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
        struct sk_buff *skb;
@@ -1057,7 +1085,8 @@ int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        /* encryption space */
        switch (wlvif->encryption_type) {
        case KEY_TKIP:
-               extra = WL1271_EXTRA_SPACE_TKIP;
+               if (wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE)
+                       extra = WL1271_EXTRA_SPACE_TKIP;
                break;
        case KEY_AES:
                extra = WL1271_EXTRA_SPACE_AES;
@@ -1346,13 +1375,18 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 
        for (i = 0; i < NUM_ACCESS_CATEGORIES_COPY; i++)
                if (sta->wme && (sta->uapsd_queues & BIT(i)))
-                       cmd->psd_type[i] = WL1271_PSD_UPSD_TRIGGER;
+                       cmd->psd_type[NUM_ACCESS_CATEGORIES_COPY-1-i] =
+                                       WL1271_PSD_UPSD_TRIGGER;
                else
-                       cmd->psd_type[i] = WL1271_PSD_LEGACY;
+                       cmd->psd_type[NUM_ACCESS_CATEGORIES_COPY-1-i] =
+                                       WL1271_PSD_LEGACY;
+
 
        sta_rates = sta->supp_rates[wlvif->band];
        if (sta->ht_cap.ht_supported)
-               sta_rates |= sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET;
+               sta_rates |=
+                       (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET) |
+                       (sta->ht_cap.mcs.rx_mask[1] << HW_MIMO_RATES_OFFSET);
 
        cmd->supported_rates =
                cpu_to_le32(wl1271_tx_enabled_rates_get(wl, sta_rates,
@@ -1573,19 +1607,25 @@ out:
 int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id)
 {
        int ret = 0;
+       bool is_first_roc;
 
        if (WARN_ON(test_bit(role_id, wl->roc_map)))
                return 0;
 
+       is_first_roc = (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) >=
+                       WL12XX_MAX_ROLES);
+
        ret = wl12xx_cmd_roc(wl, wlvif, role_id);
        if (ret < 0)
                goto out;
 
-       ret = wl1271_cmd_wait_for_event(wl,
-                                       REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID);
-       if (ret < 0) {
-               wl1271_error("cmd roc event completion error");
-               goto out;
+       if (is_first_roc) {
+               ret = wl1271_cmd_wait_for_event(wl,
+                                          REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID);
+               if (ret < 0) {
+                       wl1271_error("cmd roc event completion error");
+                       goto out;
+               }
        }
 
        __set_bit(role_id, wl->roc_map);
index a46ae07..85171f2 100644 (file)
@@ -192,7 +192,7 @@ enum cmd_templ {
 #define WL1271_COMMAND_TIMEOUT     2000
 #define WL1271_CMD_TEMPL_DFLT_SIZE 252
 #define WL1271_CMD_TEMPL_MAX_SIZE  512
-#define WL1271_EVENT_TIMEOUT       750
+#define WL1271_EVENT_TIMEOUT       1000
 
 struct wl1271_cmd_header {
        __le16 id;
@@ -266,13 +266,22 @@ enum wlcore_band {
        WLCORE_BAND_MAX_RADIO           = 0x7F,
 };
 
+enum wlcore_channel_type {
+       WLCORE_CHAN_NO_HT,
+       WLCORE_CHAN_HT20,
+       WLCORE_CHAN_HT40MINUS,
+       WLCORE_CHAN_HT40PLUS
+};
+
 struct wl12xx_cmd_role_start {
        struct wl1271_cmd_header header;
 
        u8 role_id;
        u8 band;
        u8 channel;
-       u8 padding;
+
+       /* enum wlcore_channel_type */
+       u8 channel_type;
 
        union {
                struct {
index fef0db4..03c6358 100644 (file)
@@ -45,7 +45,15 @@ enum {
        CONF_HW_BIT_RATE_MCS_4   = BIT(17),
        CONF_HW_BIT_RATE_MCS_5   = BIT(18),
        CONF_HW_BIT_RATE_MCS_6   = BIT(19),
-       CONF_HW_BIT_RATE_MCS_7   = BIT(20)
+       CONF_HW_BIT_RATE_MCS_7   = BIT(20),
+       CONF_HW_BIT_RATE_MCS_8   = BIT(21),
+       CONF_HW_BIT_RATE_MCS_9   = BIT(22),
+       CONF_HW_BIT_RATE_MCS_10  = BIT(23),
+       CONF_HW_BIT_RATE_MCS_11  = BIT(24),
+       CONF_HW_BIT_RATE_MCS_12  = BIT(25),
+       CONF_HW_BIT_RATE_MCS_13  = BIT(26),
+       CONF_HW_BIT_RATE_MCS_14  = BIT(27),
+       CONF_HW_BIT_RATE_MCS_15  = BIT(28),
 };
 
 enum {
@@ -310,7 +318,7 @@ enum {
 struct conf_sg_settings {
        u32 params[CONF_SG_PARAMS_MAX];
        u8 state;
-};
+} __packed;
 
 enum conf_rx_queue_type {
        CONF_RX_QUEUE_TYPE_LOW_PRIORITY,  /* All except the high priority */
@@ -394,7 +402,7 @@ struct conf_rx_settings {
         * Range: RX_QUEUE_TYPE_RX_LOW_PRIORITY, RX_QUEUE_TYPE_RX_HIGH_PRIORITY,
         */
        u8 queue_type;
-};
+} __packed;
 
 #define CONF_TX_MAX_RATE_CLASSES       10
 
@@ -435,6 +443,12 @@ struct conf_rx_settings {
        CONF_HW_BIT_RATE_MCS_5 | CONF_HW_BIT_RATE_MCS_6 |        \
        CONF_HW_BIT_RATE_MCS_7)
 
+#define CONF_TX_MIMO_RATES (CONF_HW_BIT_RATE_MCS_8 |             \
+       CONF_HW_BIT_RATE_MCS_9 | CONF_HW_BIT_RATE_MCS_10 |       \
+       CONF_HW_BIT_RATE_MCS_11 | CONF_HW_BIT_RATE_MCS_12 |      \
+       CONF_HW_BIT_RATE_MCS_13 | CONF_HW_BIT_RATE_MCS_14 |      \
+       CONF_HW_BIT_RATE_MCS_15)
+
 /*
  * Default rates for management traffic when operating in AP mode. This
  * should be configured according to the basic rate set of the AP
@@ -487,7 +501,7 @@ struct conf_tx_rate_class {
         *               the policy (0 - long preamble, 1 - short preamble.
         */
        u8 aflags;
-};
+} __packed;
 
 #define CONF_TX_MAX_AC_COUNT 4
 
@@ -504,7 +518,7 @@ enum conf_tx_ac {
        CONF_TX_AC_VI = 2,         /* video */
        CONF_TX_AC_VO = 3,         /* voice */
        CONF_TX_AC_CTS2SELF = 4,   /* fictitious AC, follows AC_VO */
-       CONF_TX_AC_ANY_TID = 0x1f
+       CONF_TX_AC_ANY_TID = 0xff
 };
 
 struct conf_tx_ac_category {
@@ -544,7 +558,7 @@ struct conf_tx_ac_category {
         * Range: u16
         */
        u16 tx_op_limit;
-};
+} __packed;
 
 #define CONF_TX_MAX_TID_COUNT 8
 
@@ -578,7 +592,7 @@ struct conf_tx_tid {
        u8 ps_scheme;
        u8 ack_policy;
        u32 apsd_conf[2];
-};
+} __packed;
 
 struct conf_tx_settings {
        /*
@@ -664,7 +678,7 @@ struct conf_tx_settings {
 
        /* Time in ms for Tx watchdog timer to expire */
        u32 tx_watchdog_timeout;
-};
+} __packed;
 
 enum {
        CONF_WAKE_UP_EVENT_BEACON    = 0x01, /* Wake on every Beacon*/
@@ -711,7 +725,7 @@ struct conf_bcn_filt_rule {
         * Version for the vendor specifie IE (221)
         */
        u8 version[CONF_BCN_IE_VER_LEN];
-};
+} __packed;
 
 #define CONF_MAX_RSSI_SNR_TRIGGERS 8
 
@@ -762,7 +776,7 @@ struct conf_sig_weights {
         * Range: u8
         */
        u8 snr_pkt_avg_weight;
-};
+} __packed;
 
 enum conf_bcn_filt_mode {
        CONF_BCN_FILT_MODE_DISABLED = 0,
@@ -810,7 +824,7 @@ struct conf_conn_settings {
         *
         * Range: CONF_BCN_FILT_MODE_*
         */
-       enum conf_bcn_filt_mode bcn_filt_mode;
+       u8 bcn_filt_mode;
 
        /*
         * Configure Beacon filter pass-thru rules.
@@ -937,7 +951,7 @@ struct conf_conn_settings {
         * Range: u16
         */
        u8 max_listen_interval;
-};
+} __packed;
 
 enum {
        CONF_REF_CLK_19_2_E,
@@ -965,6 +979,11 @@ struct conf_itrim_settings {
 
        /* moderation timeout in microsecs from the last TX */
        u32 timeout;
+} __packed;
+
+enum conf_fast_wakeup {
+       CONF_FAST_WAKEUP_ENABLE,
+       CONF_FAST_WAKEUP_DISABLE,
 };
 
 struct conf_pm_config_settings {
@@ -978,10 +997,10 @@ struct conf_pm_config_settings {
        /*
         * Host fast wakeup support
         *
-        * Range: true, false
+        * Range: enum conf_fast_wakeup
         */
-       bool host_fast_wakeup_support;
-};
+       u8 host_fast_wakeup_support;
+} __packed;
 
 struct conf_roam_trigger_settings {
        /*
@@ -1018,7 +1037,7 @@ struct conf_roam_trigger_settings {
         * Range: 0 - 255
         */
        u8 avg_weight_snr_data;
-};
+} __packed;
 
 struct conf_scan_settings {
        /*
@@ -1064,7 +1083,7 @@ struct conf_scan_settings {
         * Range: u32 Microsecs
         */
        u32 split_scan_timeout;
-};
+} __packed;
 
 struct conf_sched_scan_settings {
        /*
@@ -1102,7 +1121,7 @@ struct conf_sched_scan_settings {
 
        /* SNR threshold to be used for filtering */
        s8 snr_threshold;
-};
+} __packed;
 
 struct conf_ht_setting {
        u8 rx_ba_win_size;
@@ -1111,7 +1130,7 @@ struct conf_ht_setting {
 
        /* bitmap of enabled TIDs for TX BA sessions */
        u8 tx_ba_tid_bitmap;
-};
+} __packed;
 
 struct conf_memory_settings {
        /* Number of stations supported in IBSS mode */
@@ -1151,7 +1170,7 @@ struct conf_memory_settings {
         * Range: 0-120
         */
        u8 tx_min;
-};
+} __packed;
 
 struct conf_fm_coex {
        u8 enable;
@@ -1164,7 +1183,7 @@ struct conf_fm_coex {
        u16 ldo_stabilization_time;
        u8 fm_disturbed_band_margin;
        u8 swallow_clk_diff;
-};
+} __packed;
 
 struct conf_rx_streaming_settings {
        /*
@@ -1193,7 +1212,7 @@ struct conf_rx_streaming_settings {
         * enable rx streaming also when there is no coex activity
         */
        u8 always;
-};
+} __packed;
 
 struct conf_fwlog {
        /* Continuous or on-demand */
@@ -1217,7 +1236,7 @@ struct conf_fwlog {
 
        /* Regulates the frequency of log messages */
        u8 threshold;
-};
+} __packed;
 
 #define ACX_RATE_MGMT_NUM_OF_RATES 13
 struct conf_rate_policy_settings {
@@ -1236,7 +1255,7 @@ struct conf_rate_policy_settings {
        u8 rate_check_up;
        u8 rate_check_down;
        u8 rate_retry_policy[ACX_RATE_MGMT_NUM_OF_RATES];
-};
+} __packed;
 
 struct conf_hangover_settings {
        u32 recover_time;
@@ -1250,7 +1269,23 @@ struct conf_hangover_settings {
        u8 quiet_time;
        u8 increase_time;
        u8 window_size;
-};
+} __packed;
+
+/*
+ * The conf version consists of 4 bytes.  The two MSB are the wlcore
+ * version, the two LSB are the lower driver's private conf
+ * version.
+ */
+#define WLCORE_CONF_VERSION    (0x0001 << 16)
+#define WLCORE_CONF_MASK       0xffff0000
+#define WLCORE_CONF_SIZE       (sizeof(struct wlcore_conf_header) +    \
+                                sizeof(struct wlcore_conf))
+
+struct wlcore_conf_header {
+       __le32 magic;
+       __le32 version;
+       __le32 checksum;
+} __packed;
 
 struct wlcore_conf {
        struct conf_sg_settings sg;
@@ -1269,6 +1304,12 @@ struct wlcore_conf {
        struct conf_fwlog fwlog;
        struct conf_rate_policy_settings rate;
        struct conf_hangover_settings hangover;
-};
+} __packed;
+
+struct wlcore_conf_file {
+       struct wlcore_conf_header header;
+       struct wlcore_conf core;
+       u8 priv[0];
+} __packed;
 
 #endif
index d5aea1f..689a847 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <linux/skbuff.h>
 #include <linux/slab.h>
+#include <linux/module.h>
 
 #include "wlcore.h"
 #include "debug.h"
 #include "ps.h"
 #include "io.h"
 #include "tx.h"
+#include "hw_ops.h"
 
 /* ms */
 #define WL1271_DEBUGFS_STATS_LIFETIME 1000
 
 /* debugfs macros idea from mac80211 */
-#define DEBUGFS_FORMAT_BUFFER_SIZE 100
-static int wl1271_format_buffer(char __user *userbuf, size_t count,
-                                   loff_t *ppos, char *fmt, ...)
+int wl1271_format_buffer(char __user *userbuf, size_t count,
+                        loff_t *ppos, char *fmt, ...)
 {
        va_list args;
        char buf[DEBUGFS_FORMAT_BUFFER_SIZE];
@@ -51,59 +52,9 @@ static int wl1271_format_buffer(char __user *userbuf, size_t count,
 
        return simple_read_from_buffer(userbuf, count, ppos, buf, res);
 }
+EXPORT_SYMBOL_GPL(wl1271_format_buffer);
 
-#define DEBUGFS_READONLY_FILE(name, fmt, value...)                     \
-static ssize_t name## _read(struct file *file, char __user *userbuf,   \
-                           size_t count, loff_t *ppos)                 \
-{                                                                      \
-       struct wl1271 *wl = file->private_data;                         \
-       return wl1271_format_buffer(userbuf, count, ppos,               \
-                                   fmt "\n", ##value);                 \
-}                                                                      \
-                                                                       \
-static const struct file_operations name## _ops = {                    \
-       .read = name## _read,                                           \
-       .open = simple_open,                                            \
-       .llseek = generic_file_llseek,                                  \
-};
-
-#define DEBUGFS_ADD(name, parent)                                      \
-       entry = debugfs_create_file(#name, 0400, parent,                \
-                                   wl, &name## _ops);                  \
-       if (!entry || IS_ERR(entry))                                    \
-               goto err;                                               \
-
-#define DEBUGFS_ADD_PREFIX(prefix, name, parent)                       \
-       do {                                                            \
-               entry = debugfs_create_file(#name, 0400, parent,        \
-                                   wl, &prefix## _## name## _ops);     \
-               if (!entry || IS_ERR(entry))                            \
-                       goto err;                                       \
-       } while (0);
-
-#define DEBUGFS_FWSTATS_FILE(sub, name, fmt)                           \
-static ssize_t sub## _ ##name## _read(struct file *file,               \
-                                     char __user *userbuf,             \
-                                     size_t count, loff_t *ppos)       \
-{                                                                      \
-       struct wl1271 *wl = file->private_data;                         \
-                                                                       \
-       wl1271_debugfs_update_stats(wl);                                \
-                                                                       \
-       return wl1271_format_buffer(userbuf, count, ppos, fmt "\n",     \
-                                   wl->stats.fw_stats->sub.name);      \
-}                                                                      \
-                                                                       \
-static const struct file_operations sub## _ ##name## _ops = {          \
-       .read = sub## _ ##name## _read,                                 \
-       .open = simple_open,                                            \
-       .llseek = generic_file_llseek,                                  \
-};
-
-#define DEBUGFS_FWSTATS_ADD(sub, name)                         \
-       DEBUGFS_ADD(sub## _ ##name, stats)
-
-static void wl1271_debugfs_update_stats(struct wl1271 *wl)
+void wl1271_debugfs_update_stats(struct wl1271 *wl)
 {
        int ret;
 
@@ -125,97 +76,7 @@ static void wl1271_debugfs_update_stats(struct wl1271 *wl)
 out:
        mutex_unlock(&wl->mutex);
 }
-
-DEBUGFS_FWSTATS_FILE(tx, internal_desc_overflow, "%u");
-
-DEBUGFS_FWSTATS_FILE(rx, out_of_mem, "%u");
-DEBUGFS_FWSTATS_FILE(rx, hdr_overflow, "%u");
-DEBUGFS_FWSTATS_FILE(rx, hw_stuck, "%u");
-DEBUGFS_FWSTATS_FILE(rx, dropped, "%u");
-DEBUGFS_FWSTATS_FILE(rx, fcs_err, "%u");
-DEBUGFS_FWSTATS_FILE(rx, xfr_hint_trig, "%u");
-DEBUGFS_FWSTATS_FILE(rx, path_reset, "%u");
-DEBUGFS_FWSTATS_FILE(rx, reset_counter, "%u");
-
-DEBUGFS_FWSTATS_FILE(dma, rx_requested, "%u");
-DEBUGFS_FWSTATS_FILE(dma, rx_errors, "%u");
-DEBUGFS_FWSTATS_FILE(dma, tx_requested, "%u");
-DEBUGFS_FWSTATS_FILE(dma, tx_errors, "%u");
-
-DEBUGFS_FWSTATS_FILE(isr, cmd_cmplt, "%u");
-DEBUGFS_FWSTATS_FILE(isr, fiqs, "%u");
-DEBUGFS_FWSTATS_FILE(isr, rx_headers, "%u");
-DEBUGFS_FWSTATS_FILE(isr, rx_mem_overflow, "%u");
-DEBUGFS_FWSTATS_FILE(isr, rx_rdys, "%u");
-DEBUGFS_FWSTATS_FILE(isr, irqs, "%u");
-DEBUGFS_FWSTATS_FILE(isr, tx_procs, "%u");
-DEBUGFS_FWSTATS_FILE(isr, decrypt_done, "%u");
-DEBUGFS_FWSTATS_FILE(isr, dma0_done, "%u");
-DEBUGFS_FWSTATS_FILE(isr, dma1_done, "%u");
-DEBUGFS_FWSTATS_FILE(isr, tx_exch_complete, "%u");
-DEBUGFS_FWSTATS_FILE(isr, commands, "%u");
-DEBUGFS_FWSTATS_FILE(isr, rx_procs, "%u");
-DEBUGFS_FWSTATS_FILE(isr, hw_pm_mode_changes, "%u");
-DEBUGFS_FWSTATS_FILE(isr, host_acknowledges, "%u");
-DEBUGFS_FWSTATS_FILE(isr, pci_pm, "%u");
-DEBUGFS_FWSTATS_FILE(isr, wakeups, "%u");
-DEBUGFS_FWSTATS_FILE(isr, low_rssi, "%u");
-
-DEBUGFS_FWSTATS_FILE(wep, addr_key_count, "%u");
-DEBUGFS_FWSTATS_FILE(wep, default_key_count, "%u");
-/* skipping wep.reserved */
-DEBUGFS_FWSTATS_FILE(wep, key_not_found, "%u");
-DEBUGFS_FWSTATS_FILE(wep, decrypt_fail, "%u");
-DEBUGFS_FWSTATS_FILE(wep, packets, "%u");
-DEBUGFS_FWSTATS_FILE(wep, interrupt, "%u");
-
-DEBUGFS_FWSTATS_FILE(pwr, ps_enter, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, elp_enter, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, missing_bcns, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, wake_on_host, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, wake_on_timer_exp, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, tx_with_ps, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, tx_without_ps, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, rcvd_beacons, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, power_save_off, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, enable_ps, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, disable_ps, "%u");
-DEBUGFS_FWSTATS_FILE(pwr, fix_tsf_ps, "%u");
-/* skipping cont_miss_bcns_spread for now */
-DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_beacons, "%u");
-
-DEBUGFS_FWSTATS_FILE(mic, rx_pkts, "%u");
-DEBUGFS_FWSTATS_FILE(mic, calc_failure, "%u");
-
-DEBUGFS_FWSTATS_FILE(aes, encrypt_fail, "%u");
-DEBUGFS_FWSTATS_FILE(aes, decrypt_fail, "%u");
-DEBUGFS_FWSTATS_FILE(aes, encrypt_packets, "%u");
-DEBUGFS_FWSTATS_FILE(aes, decrypt_packets, "%u");
-DEBUGFS_FWSTATS_FILE(aes, encrypt_interrupt, "%u");
-DEBUGFS_FWSTATS_FILE(aes, decrypt_interrupt, "%u");
-
-DEBUGFS_FWSTATS_FILE(event, heart_beat, "%u");
-DEBUGFS_FWSTATS_FILE(event, calibration, "%u");
-DEBUGFS_FWSTATS_FILE(event, rx_mismatch, "%u");
-DEBUGFS_FWSTATS_FILE(event, rx_mem_empty, "%u");
-DEBUGFS_FWSTATS_FILE(event, rx_pool, "%u");
-DEBUGFS_FWSTATS_FILE(event, oom_late, "%u");
-DEBUGFS_FWSTATS_FILE(event, phy_transmit_error, "%u");
-DEBUGFS_FWSTATS_FILE(event, tx_stuck, "%u");
-
-DEBUGFS_FWSTATS_FILE(ps, pspoll_timeouts, "%u");
-DEBUGFS_FWSTATS_FILE(ps, upsd_timeouts, "%u");
-DEBUGFS_FWSTATS_FILE(ps, upsd_max_sptime, "%u");
-DEBUGFS_FWSTATS_FILE(ps, upsd_max_apturn, "%u");
-DEBUGFS_FWSTATS_FILE(ps, pspoll_max_apturn, "%u");
-DEBUGFS_FWSTATS_FILE(ps, pspoll_utilization, "%u");
-DEBUGFS_FWSTATS_FILE(ps, upsd_utilization, "%u");
-
-DEBUGFS_FWSTATS_FILE(rxpipe, rx_prep_beacon_drop, "%u");
-DEBUGFS_FWSTATS_FILE(rxpipe, descr_host_int_trig_rx_data, "%u");
-DEBUGFS_FWSTATS_FILE(rxpipe, beacon_buffer_thres_host_int_trig_rx_data, "%u");
-DEBUGFS_FWSTATS_FILE(rxpipe, missed_beacon_host_int_trig_rx_data, "%u");
-DEBUGFS_FWSTATS_FILE(rxpipe, tx_xfr_host_int_trig_rx_data, "%u");
+EXPORT_SYMBOL_GPL(wl1271_debugfs_update_stats);
 
 DEBUGFS_READONLY_FILE(retry_count, "%u", wl->stats.retry_count);
 DEBUGFS_READONLY_FILE(excessive_retries, "%u",
@@ -241,6 +102,89 @@ static const struct file_operations tx_queue_len_ops = {
        .llseek = default_llseek,
 };
 
+static void chip_op_handler(struct wl1271 *wl, unsigned long value,
+                           void *arg)
+{
+       int ret;
+       int (*chip_op) (struct wl1271 *wl);
+
+       if (!arg) {
+               wl1271_warning("debugfs chip_op_handler with no callback");
+               return;
+       }
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               return;
+
+       chip_op = arg;
+       chip_op(wl);
+
+       wl1271_ps_elp_sleep(wl);
+}
+
+
+static inline void no_write_handler(struct wl1271 *wl,
+                                   unsigned long value,
+                                   unsigned long param)
+{
+}
+
+#define WL12XX_CONF_DEBUGFS(param, conf_sub_struct,                    \
+                           min_val, max_val, write_handler_locked,     \
+                           write_handler_arg)                          \
+       static ssize_t param##_read(struct file *file,                  \
+                                     char __user *user_buf,            \
+                                     size_t count, loff_t *ppos)       \
+       {                                                               \
+       struct wl1271 *wl = file->private_data;                         \
+       return wl1271_format_buffer(user_buf, count,                    \
+                                   ppos, "%d\n",                       \
+                                   wl->conf.conf_sub_struct.param);    \
+       }                                                               \
+                                                                       \
+       static ssize_t param##_write(struct file *file,                 \
+                                    const char __user *user_buf,       \
+                                    size_t count, loff_t *ppos)        \
+       {                                                               \
+       struct wl1271 *wl = file->private_data;                         \
+       unsigned long value;                                            \
+       int ret;                                                        \
+                                                                       \
+       ret = kstrtoul_from_user(user_buf, count, 10, &value);          \
+       if (ret < 0) {                                                  \
+               wl1271_warning("illegal value for " #param);            \
+               return -EINVAL;                                         \
+       }                                                               \
+                                                                       \
+       if (value < min_val || value > max_val) {                       \
+               wl1271_warning(#param " is not in valid range");        \
+               return -ERANGE;                                         \
+       }                                                               \
+                                                                       \
+       mutex_lock(&wl->mutex);                                         \
+       wl->conf.conf_sub_struct.param = value;                         \
+                                                                       \
+       write_handler_locked(wl, value, write_handler_arg);             \
+                                                                       \
+       mutex_unlock(&wl->mutex);                                       \
+       return count;                                                   \
+       }                                                               \
+                                                                       \
+       static const struct file_operations param##_ops = {             \
+               .read = param##_read,                                   \
+               .write = param##_write,                                 \
+               .open = simple_open,                                    \
+               .llseek = default_llseek,                               \
+       };
+
+WL12XX_CONF_DEBUGFS(irq_pkt_threshold, rx, 0, 65535,
+                   chip_op_handler, wl1271_acx_init_rx_interrupt)
+WL12XX_CONF_DEBUGFS(irq_blk_threshold, rx, 0, 65535,
+                   chip_op_handler, wl1271_acx_init_rx_interrupt)
+WL12XX_CONF_DEBUGFS(irq_timeout, rx, 0, 100,
+                   chip_op_handler, wl1271_acx_init_rx_interrupt)
+
 static ssize_t gpio_power_read(struct file *file, char __user *user_buf,
                          size_t count, loff_t *ppos)
 {
@@ -535,8 +479,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
        DRIVER_STATE_PRINT_LHEX(ap_ps_map);
        DRIVER_STATE_PRINT_HEX(quirks);
        DRIVER_STATE_PRINT_HEX(irq);
-       DRIVER_STATE_PRINT_HEX(ref_clock);
-       DRIVER_STATE_PRINT_HEX(tcxo_clock);
+       /* TODO: ref_clock and tcxo_clock were moved to wl12xx priv */
        DRIVER_STATE_PRINT_HEX(hw_pg_ver);
        DRIVER_STATE_PRINT_HEX(platform_quirks);
        DRIVER_STATE_PRINT_HEX(chip.id);
@@ -647,7 +590,6 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
                VIF_STATE_PRINT_INT(last_rssi_event);
                VIF_STATE_PRINT_INT(ba_support);
                VIF_STATE_PRINT_INT(ba_allowed);
-               VIF_STATE_PRINT_INT(is_gem);
                VIF_STATE_PRINT_LLHEX(tx_security_seq);
                VIF_STATE_PRINT_INT(tx_security_last_seq_lsb);
        }
@@ -1002,108 +944,30 @@ static const struct file_operations beacon_filtering_ops = {
        .llseek = default_llseek,
 };
 
-static int wl1271_debugfs_add_files(struct wl1271 *wl,
-                                    struct dentry *rootdir)
+static ssize_t fw_stats_raw_read(struct file *file,
+                                char __user *userbuf,
+                                size_t count, loff_t *ppos)
 {
-       int ret = 0;
-       struct dentry *entry, *stats, *streaming;
+       struct wl1271 *wl = file->private_data;
 
-       stats = debugfs_create_dir("fw-statistics", rootdir);
-       if (!stats || IS_ERR(stats)) {
-               entry = stats;
-               goto err;
-       }
+       wl1271_debugfs_update_stats(wl);
 
-       DEBUGFS_FWSTATS_ADD(tx, internal_desc_overflow);
-
-       DEBUGFS_FWSTATS_ADD(rx, out_of_mem);
-       DEBUGFS_FWSTATS_ADD(rx, hdr_overflow);
-       DEBUGFS_FWSTATS_ADD(rx, hw_stuck);
-       DEBUGFS_FWSTATS_ADD(rx, dropped);
-       DEBUGFS_FWSTATS_ADD(rx, fcs_err);
-       DEBUGFS_FWSTATS_ADD(rx, xfr_hint_trig);
-       DEBUGFS_FWSTATS_ADD(rx, path_reset);
-       DEBUGFS_FWSTATS_ADD(rx, reset_counter);
-
-       DEBUGFS_FWSTATS_ADD(dma, rx_requested);
-       DEBUGFS_FWSTATS_ADD(dma, rx_errors);
-       DEBUGFS_FWSTATS_ADD(dma, tx_requested);
-       DEBUGFS_FWSTATS_ADD(dma, tx_errors);
-
-       DEBUGFS_FWSTATS_ADD(isr, cmd_cmplt);
-       DEBUGFS_FWSTATS_ADD(isr, fiqs);
-       DEBUGFS_FWSTATS_ADD(isr, rx_headers);
-       DEBUGFS_FWSTATS_ADD(isr, rx_mem_overflow);
-       DEBUGFS_FWSTATS_ADD(isr, rx_rdys);
-       DEBUGFS_FWSTATS_ADD(isr, irqs);
-       DEBUGFS_FWSTATS_ADD(isr, tx_procs);
-       DEBUGFS_FWSTATS_ADD(isr, decrypt_done);
-       DEBUGFS_FWSTATS_ADD(isr, dma0_done);
-       DEBUGFS_FWSTATS_ADD(isr, dma1_done);
-       DEBUGFS_FWSTATS_ADD(isr, tx_exch_complete);
-       DEBUGFS_FWSTATS_ADD(isr, commands);
-       DEBUGFS_FWSTATS_ADD(isr, rx_procs);
-       DEBUGFS_FWSTATS_ADD(isr, hw_pm_mode_changes);
-       DEBUGFS_FWSTATS_ADD(isr, host_acknowledges);
-       DEBUGFS_FWSTATS_ADD(isr, pci_pm);
-       DEBUGFS_FWSTATS_ADD(isr, wakeups);
-       DEBUGFS_FWSTATS_ADD(isr, low_rssi);
-
-       DEBUGFS_FWSTATS_ADD(wep, addr_key_count);
-       DEBUGFS_FWSTATS_ADD(wep, default_key_count);
-       /* skipping wep.reserved */
-       DEBUGFS_FWSTATS_ADD(wep, key_not_found);
-       DEBUGFS_FWSTATS_ADD(wep, decrypt_fail);
-       DEBUGFS_FWSTATS_ADD(wep, packets);
-       DEBUGFS_FWSTATS_ADD(wep, interrupt);
-
-       DEBUGFS_FWSTATS_ADD(pwr, ps_enter);
-       DEBUGFS_FWSTATS_ADD(pwr, elp_enter);
-       DEBUGFS_FWSTATS_ADD(pwr, missing_bcns);
-       DEBUGFS_FWSTATS_ADD(pwr, wake_on_host);
-       DEBUGFS_FWSTATS_ADD(pwr, wake_on_timer_exp);
-       DEBUGFS_FWSTATS_ADD(pwr, tx_with_ps);
-       DEBUGFS_FWSTATS_ADD(pwr, tx_without_ps);
-       DEBUGFS_FWSTATS_ADD(pwr, rcvd_beacons);
-       DEBUGFS_FWSTATS_ADD(pwr, power_save_off);
-       DEBUGFS_FWSTATS_ADD(pwr, enable_ps);
-       DEBUGFS_FWSTATS_ADD(pwr, disable_ps);
-       DEBUGFS_FWSTATS_ADD(pwr, fix_tsf_ps);
-       /* skipping cont_miss_bcns_spread for now */
-       DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_beacons);
-
-       DEBUGFS_FWSTATS_ADD(mic, rx_pkts);
-       DEBUGFS_FWSTATS_ADD(mic, calc_failure);
-
-       DEBUGFS_FWSTATS_ADD(aes, encrypt_fail);
-       DEBUGFS_FWSTATS_ADD(aes, decrypt_fail);
-       DEBUGFS_FWSTATS_ADD(aes, encrypt_packets);
-       DEBUGFS_FWSTATS_ADD(aes, decrypt_packets);
-       DEBUGFS_FWSTATS_ADD(aes, encrypt_interrupt);
-       DEBUGFS_FWSTATS_ADD(aes, decrypt_interrupt);
-
-       DEBUGFS_FWSTATS_ADD(event, heart_beat);
-       DEBUGFS_FWSTATS_ADD(event, calibration);
-       DEBUGFS_FWSTATS_ADD(event, rx_mismatch);
-       DEBUGFS_FWSTATS_ADD(event, rx_mem_empty);
-       DEBUGFS_FWSTATS_ADD(event, rx_pool);
-       DEBUGFS_FWSTATS_ADD(event, oom_late);
-       DEBUGFS_FWSTATS_ADD(event, phy_transmit_error);
-       DEBUGFS_FWSTATS_ADD(event, tx_stuck);
-
-       DEBUGFS_FWSTATS_ADD(ps, pspoll_timeouts);
-       DEBUGFS_FWSTATS_ADD(ps, upsd_timeouts);
-       DEBUGFS_FWSTATS_ADD(ps, upsd_max_sptime);
-       DEBUGFS_FWSTATS_ADD(ps, upsd_max_apturn);
-       DEBUGFS_FWSTATS_ADD(ps, pspoll_max_apturn);
-       DEBUGFS_FWSTATS_ADD(ps, pspoll_utilization);
-       DEBUGFS_FWSTATS_ADD(ps, upsd_utilization);
-
-       DEBUGFS_FWSTATS_ADD(rxpipe, rx_prep_beacon_drop);
-       DEBUGFS_FWSTATS_ADD(rxpipe, descr_host_int_trig_rx_data);
-       DEBUGFS_FWSTATS_ADD(rxpipe, beacon_buffer_thres_host_int_trig_rx_data);
-       DEBUGFS_FWSTATS_ADD(rxpipe, missed_beacon_host_int_trig_rx_data);
-       DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data);
+       return simple_read_from_buffer(userbuf, count, ppos,
+                                      wl->stats.fw_stats,
+                                      wl->stats.fw_stats_len);
+}
+
+static const struct file_operations fw_stats_raw_ops = {
+       .read = fw_stats_raw_read,
+       .open = simple_open,
+       .llseek = default_llseek,
+};
+
+static int wl1271_debugfs_add_files(struct wl1271 *wl,
+                                   struct dentry *rootdir)
+{
+       int ret = 0;
+       struct dentry *entry, *streaming;
 
        DEBUGFS_ADD(tx_queue_len, rootdir);
        DEBUGFS_ADD(retry_count, rootdir);
@@ -1120,6 +984,10 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl,
        DEBUGFS_ADD(dynamic_ps_timeout, rootdir);
        DEBUGFS_ADD(forced_ps, rootdir);
        DEBUGFS_ADD(split_scan_timeout, rootdir);
+       DEBUGFS_ADD(irq_pkt_threshold, rootdir);
+       DEBUGFS_ADD(irq_blk_threshold, rootdir);
+       DEBUGFS_ADD(irq_timeout, rootdir);
+       DEBUGFS_ADD(fw_stats_raw, rootdir);
 
        streaming = debugfs_create_dir("rx_streaming", rootdir);
        if (!streaming || IS_ERR(streaming))
@@ -1145,7 +1013,7 @@ void wl1271_debugfs_reset(struct wl1271 *wl)
        if (!wl->stats.fw_stats)
                return;
 
-       memset(wl->stats.fw_stats, 0, sizeof(*wl->stats.fw_stats));
+       memset(wl->stats.fw_stats, 0, wl->stats.fw_stats_len);
        wl->stats.retry_count = 0;
        wl->stats.excessive_retries = 0;
 }
@@ -1160,34 +1028,34 @@ int wl1271_debugfs_init(struct wl1271 *wl)
 
        if (IS_ERR(rootdir)) {
                ret = PTR_ERR(rootdir);
-               goto err;
+               goto out;
        }
 
-       wl->stats.fw_stats = kzalloc(sizeof(*wl->stats.fw_stats),
-                                     GFP_KERNEL);
-
+       wl->stats.fw_stats = kzalloc(wl->stats.fw_stats_len, GFP_KERNEL);
        if (!wl->stats.fw_stats) {
                ret = -ENOMEM;
-               goto err_fw;
+               goto out_remove;
        }
 
        wl->stats.fw_stats_update = jiffies;
 
        ret = wl1271_debugfs_add_files(wl, rootdir);
+       if (ret < 0)
+               goto out_exit;
 
+       ret = wlcore_debugfs_init(wl, rootdir);
        if (ret < 0)
-               goto err_file;
+               goto out_exit;
 
-       return 0;
+       goto out;
 
-err_file:
-       kfree(wl->stats.fw_stats);
-       wl->stats.fw_stats = NULL;
+out_exit:
+       wl1271_debugfs_exit(wl);
 
-err_fw:
+out_remove:
        debugfs_remove_recursive(rootdir);
 
-err:
+out:
        return ret;
 }
 
index a8d3aef..f7381dd 100644 (file)
 
 #include "wlcore.h"
 
+int wl1271_format_buffer(char __user *userbuf, size_t count,
+                        loff_t *ppos, char *fmt, ...);
+
 int wl1271_debugfs_init(struct wl1271 *wl);
 void wl1271_debugfs_exit(struct wl1271 *wl);
 void wl1271_debugfs_reset(struct wl1271 *wl);
+void wl1271_debugfs_update_stats(struct wl1271 *wl);
+
+#define DEBUGFS_FORMAT_BUFFER_SIZE 256
+
+#define DEBUGFS_READONLY_FILE(name, fmt, value...)                     \
+static ssize_t name## _read(struct file *file, char __user *userbuf,   \
+                           size_t count, loff_t *ppos)                 \
+{                                                                      \
+       struct wl1271 *wl = file->private_data;                         \
+       return wl1271_format_buffer(userbuf, count, ppos,               \
+                                   fmt "\n", ##value);                 \
+}                                                                      \
+                                                                       \
+static const struct file_operations name## _ops = {                    \
+       .read = name## _read,                                           \
+       .open = simple_open,                                            \
+       .llseek = generic_file_llseek,                                  \
+};
+
+#define DEBUGFS_ADD(name, parent)                                      \
+       do {                                                            \
+               entry = debugfs_create_file(#name, 0400, parent,        \
+                                           wl, &name## _ops);          \
+               if (!entry || IS_ERR(entry))                            \
+                       goto err;                                       \
+       } while (0);
+
+
+#define DEBUGFS_ADD_PREFIX(prefix, name, parent)                       \
+       do {                                                            \
+               entry = debugfs_create_file(#name, 0400, parent,        \
+                                   wl, &prefix## _## name## _ops);     \
+               if (!entry || IS_ERR(entry))                            \
+                       goto err;                                       \
+       } while (0);
+
+#define DEBUGFS_FWSTATS_FILE(sub, name, fmt, struct_type)              \
+static ssize_t sub## _ ##name## _read(struct file *file,               \
+                                     char __user *userbuf,             \
+                                     size_t count, loff_t *ppos)       \
+{                                                                      \
+       struct wl1271 *wl = file->private_data;                         \
+       struct struct_type *stats = wl->stats.fw_stats;                 \
+                                                                       \
+       wl1271_debugfs_update_stats(wl);                                \
+                                                                       \
+       return wl1271_format_buffer(userbuf, count, ppos, fmt "\n",     \
+                                   stats->sub.name);                   \
+}                                                                      \
+                                                                       \
+static const struct file_operations sub## _ ##name## _ops = {          \
+       .read = sub## _ ##name## _read,                                 \
+       .open = simple_open,                                            \
+       .llseek = generic_file_llseek,                                  \
+};
+
+#define DEBUGFS_FWSTATS_FILE_ARRAY(sub, name, len, struct_type)                \
+static ssize_t sub## _ ##name## _read(struct file *file,               \
+                                     char __user *userbuf,             \
+                                     size_t count, loff_t *ppos)       \
+{                                                                      \
+       struct wl1271 *wl = file->private_data;                         \
+       struct struct_type *stats = wl->stats.fw_stats;                 \
+       char buf[DEBUGFS_FORMAT_BUFFER_SIZE] = "";                      \
+       int res, i;                                                     \
+                                                                       \
+       wl1271_debugfs_update_stats(wl);                                \
+                                                                       \
+       for (i = 0; i < len; i++)                                       \
+               res = snprintf(buf, sizeof(buf), "%s[%d] = %d\n",       \
+                              buf, i, stats->sub.name[i]);             \
+                                                                       \
+       return wl1271_format_buffer(userbuf, count, ppos, "%s", buf);   \
+}                                                                      \
+                                                                       \
+static const struct file_operations sub## _ ##name## _ops = {          \
+       .read = sub## _ ##name## _read,                                 \
+       .open = simple_open,                                            \
+       .llseek = generic_file_llseek,                                  \
+};
+
+#define DEBUGFS_FWSTATS_ADD(sub, name)                                 \
+       DEBUGFS_ADD(sub## _ ##name, stats)
+
 
 #endif /* WL1271_DEBUGFS_H */
index 28e2a63..c976f04 100644 (file)
@@ -148,15 +148,33 @@ static int wl1271_event_process(struct wl1271 *wl)
                int delay = wl->conf.conn.synch_fail_thold *
                                        wl->conf.conn.bss_lose_timeout;
                wl1271_info("Beacon loss detected.");
-               cancel_delayed_work_sync(&wl->connection_loss_work);
+
+               /*
+                * if the work is already queued, it should take place. We
+                * don't want to delay the connection loss indication
+                * any more.
+                */
                ieee80211_queue_delayed_work(wl->hw, &wl->connection_loss_work,
-                     msecs_to_jiffies(delay));
+                                            msecs_to_jiffies(delay));
+
+               wl12xx_for_each_wlvif_sta(wl, wlvif) {
+                       vif = wl12xx_wlvif_to_vif(wlvif);
+
+                       ieee80211_cqm_rssi_notify(
+                                       vif,
+                                       NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
+                                       GFP_KERNEL);
+               }
        }
 
        if (vector & REGAINED_BSS_EVENT_ID) {
                /* TODO: check for multi-role */
                wl1271_info("Beacon regained.");
-               cancel_delayed_work_sync(&wl->connection_loss_work);
+               cancel_delayed_work(&wl->connection_loss_work);
+
+               /* sanity check - we can't lose and gain the beacon together */
+               WARN(vector & BSS_LOSE_EVENT_ID,
+                    "Concurrent beacon loss and gain from FW");
        }
 
        if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) {
index 9384b4d..9e7787b 100644 (file)
@@ -119,4 +119,82 @@ static inline int wlcore_identify_fw(struct wl1271 *wl)
        return 0;
 }
 
+static inline void
+wlcore_hw_set_tx_desc_csum(struct wl1271 *wl,
+                          struct wl1271_tx_hw_descr *desc,
+                          struct sk_buff *skb)
+{
+       if (!wl->ops->set_tx_desc_csum)
+               BUG_ON(1);
+
+       wl->ops->set_tx_desc_csum(wl, desc, skb);
+}
+
+static inline void
+wlcore_hw_set_rx_csum(struct wl1271 *wl,
+                     struct wl1271_rx_descriptor *desc,
+                     struct sk_buff *skb)
+{
+       if (wl->ops->set_rx_csum)
+               wl->ops->set_rx_csum(wl, desc, skb);
+}
+
+static inline u32
+wlcore_hw_ap_get_mimo_wide_rate_mask(struct wl1271 *wl,
+                                    struct wl12xx_vif *wlvif)
+{
+       if (wl->ops->ap_get_mimo_wide_rate_mask)
+               return wl->ops->ap_get_mimo_wide_rate_mask(wl, wlvif);
+
+       return 0;
+}
+
+static inline int
+wlcore_debugfs_init(struct wl1271 *wl, struct dentry *rootdir)
+{
+       if (wl->ops->debugfs_init)
+               return wl->ops->debugfs_init(wl, rootdir);
+
+       return 0;
+}
+
+static inline int
+wlcore_handle_static_data(struct wl1271 *wl, void *static_data)
+{
+       if (wl->ops->handle_static_data)
+               return wl->ops->handle_static_data(wl, static_data);
+
+       return 0;
+}
+
+static inline int
+wlcore_hw_get_spare_blocks(struct wl1271 *wl, bool is_gem)
+{
+       if (!wl->ops->get_spare_blocks)
+               BUG_ON(1);
+
+       return wl->ops->get_spare_blocks(wl, is_gem);
+}
+
+static inline int
+wlcore_hw_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
+                 struct ieee80211_vif *vif,
+                 struct ieee80211_sta *sta,
+                 struct ieee80211_key_conf *key_conf)
+{
+       if (!wl->ops->set_key)
+               BUG_ON(1);
+
+       return wl->ops->set_key(wl, cmd, vif, sta, key_conf);
+}
+
+static inline u32
+wlcore_hw_pre_pkt_send(struct wl1271 *wl, u32 buf_offset, u32 last_len)
+{
+       if (wl->ops->pre_pkt_send)
+               return wl->ops->pre_pkt_send(wl, buf_offset, last_len);
+
+       return buf_offset;
+}
+
 #endif
index 9f89255..645abd4 100644 (file)
@@ -460,6 +460,9 @@ int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        /* unconditionally enable HT rates */
        supported_rates |= CONF_TX_MCS_RATES;
 
+       /* get extra MIMO or wide-chan rates where the HW supports it */
+       supported_rates |= wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif);
+
        /* configure unicast TX rate classes */
        for (i = 0; i < wl->conf.tx.ac_conf_count; i++) {
                rc.enabled_rates = supported_rates;
index acef933..1156e3f 100644 (file)
@@ -320,46 +320,6 @@ static void wlcore_adjust_conf(struct wl1271 *wl)
        }
 }
 
-static int wl1271_plt_init(struct wl1271 *wl)
-{
-       int ret;
-
-       ret = wl->ops->hw_init(wl);
-       if (ret < 0)
-               return ret;
-
-       ret = wl1271_acx_init_mem_config(wl);
-       if (ret < 0)
-               return ret;
-
-       ret = wl12xx_acx_mem_cfg(wl);
-       if (ret < 0)
-               goto out_free_memmap;
-
-       /* Enable data path */
-       ret = wl1271_cmd_data_path(wl, 1);
-       if (ret < 0)
-               goto out_free_memmap;
-
-       /* Configure for CAM power saving (ie. always active) */
-       ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
-       if (ret < 0)
-               goto out_free_memmap;
-
-       /* configure PM */
-       ret = wl1271_acx_pm_config(wl);
-       if (ret < 0)
-               goto out_free_memmap;
-
-       return 0;
-
- out_free_memmap:
-       kfree(wl->target_mem_map);
-       wl->target_mem_map = NULL;
-
-       return ret;
-}
-
 static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
                                        struct wl12xx_vif *wlvif,
                                        u8 hlid, u8 tx_pkts)
@@ -387,7 +347,7 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
 
 static void wl12xx_irq_update_links_status(struct wl1271 *wl,
                                           struct wl12xx_vif *wlvif,
-                                          struct wl_fw_status *status)
+                                          struct wl_fw_status_2 *status)
 {
        struct wl1271_link *lnk;
        u32 cur_fw_ps_map;
@@ -419,7 +379,8 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl,
 }
 
 static void wl12xx_fw_status(struct wl1271 *wl,
-                            struct wl_fw_status *status)
+                            struct wl_fw_status_1 *status_1,
+                            struct wl_fw_status_2 *status_2)
 {
        struct wl12xx_vif *wlvif;
        struct timespec ts;
@@ -428,37 +389,38 @@ static void wl12xx_fw_status(struct wl1271 *wl,
        int i;
        size_t status_len;
 
-       status_len = sizeof(*status) + wl->fw_status_priv_len;
+       status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
+               sizeof(*status_2) + wl->fw_status_priv_len;
 
-       wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status,
+       wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status_1,
                             status_len, false);
 
        wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
                     "drv_rx_counter = %d, tx_results_counter = %d)",
-                    status->intr,
-                    status->fw_rx_counter,
-                    status->drv_rx_counter,
-                    status->tx_results_counter);
+                    status_1->intr,
+                    status_1->fw_rx_counter,
+                    status_1->drv_rx_counter,
+                    status_1->tx_results_counter);
 
        for (i = 0; i < NUM_TX_QUEUES; i++) {
                /* prevent wrap-around in freed-packets counter */
                wl->tx_allocated_pkts[i] -=
-                               (status->counters.tx_released_pkts[i] -
+                               (status_2->counters.tx_released_pkts[i] -
                                wl->tx_pkts_freed[i]) & 0xff;
 
-               wl->tx_pkts_freed[i] = status->counters.tx_released_pkts[i];
+               wl->tx_pkts_freed[i] = status_2->counters.tx_released_pkts[i];
        }
 
        /* prevent wrap-around in total blocks counter */
        if (likely(wl->tx_blocks_freed <=
-                  le32_to_cpu(status->total_released_blks)))
-               freed_blocks = le32_to_cpu(status->total_released_blks) -
+                  le32_to_cpu(status_2->total_released_blks)))
+               freed_blocks = le32_to_cpu(status_2->total_released_blks) -
                               wl->tx_blocks_freed;
        else
                freed_blocks = 0x100000000LL - wl->tx_blocks_freed +
-                              le32_to_cpu(status->total_released_blks);
+                              le32_to_cpu(status_2->total_released_blks);
 
-       wl->tx_blocks_freed = le32_to_cpu(status->total_released_blks);
+       wl->tx_blocks_freed = le32_to_cpu(status_2->total_released_blks);
 
        wl->tx_allocated_blocks -= freed_blocks;
 
@@ -474,7 +436,7 @@ static void wl12xx_fw_status(struct wl1271 *wl,
                        cancel_delayed_work(&wl->tx_watchdog_work);
        }
 
-       avail = le32_to_cpu(status->tx_total) - wl->tx_allocated_blocks;
+       avail = le32_to_cpu(status_2->tx_total) - wl->tx_allocated_blocks;
 
        /*
         * The FW might change the total number of TX memblocks before
@@ -493,13 +455,13 @@ static void wl12xx_fw_status(struct wl1271 *wl,
 
        /* for AP update num of allocated TX blocks per link and ps status */
        wl12xx_for_each_wlvif_ap(wl, wlvif) {
-               wl12xx_irq_update_links_status(wl, wlvif, status);
+               wl12xx_irq_update_links_status(wl, wlvif, status_2);
        }
 
        /* update the host-chipset time offset */
        getnstimeofday(&ts);
        wl->time_offset = (timespec_to_ns(&ts) >> 10) -
-               (s64)le32_to_cpu(status->fw_localtime);
+               (s64)le32_to_cpu(status_2->fw_localtime);
 }
 
 static void wl1271_flush_deferred_work(struct wl1271 *wl)
@@ -568,20 +530,30 @@ static irqreturn_t wl1271_irq(int irq, void *cookie)
                clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
                smp_mb__after_clear_bit();
 
-               wl12xx_fw_status(wl, wl->fw_status);
+               wl12xx_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
 
                wlcore_hw_tx_immediate_compl(wl);
 
-               intr = le32_to_cpu(wl->fw_status->intr);
-               intr &= WL1271_INTR_MASK;
+               intr = le32_to_cpu(wl->fw_status_1->intr);
+               intr &= WLCORE_ALL_INTR_MASK;
                if (!intr) {
                        done = true;
                        continue;
                }
 
                if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) {
-                       wl1271_error("watchdog interrupt received! "
+                       wl1271_error("HW watchdog interrupt received! starting recovery.");
+                       wl->watchdog_recovery = true;
+                       wl12xx_queue_recovery_work(wl);
+
+                       /* restarting the chip. ignore any other interrupt. */
+                       goto out;
+               }
+
+               if (unlikely(intr & WL1271_ACX_SW_INTR_WATCHDOG)) {
+                       wl1271_error("SW watchdog interrupt received! "
                                     "starting recovery.");
+                       wl->watchdog_recovery = true;
                        wl12xx_queue_recovery_work(wl);
 
                        /* restarting the chip. ignore any other interrupt. */
@@ -591,7 +563,7 @@ static irqreturn_t wl1271_irq(int irq, void *cookie)
                if (likely(intr & WL1271_ACX_INTR_DATA)) {
                        wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
 
-                       wl12xx_rx(wl, wl->fw_status);
+                       wl12xx_rx(wl, wl->fw_status_1);
 
                        /* Check if any tx blocks were freed */
                        spin_lock_irqsave(&wl->wl_lock, flags);
@@ -743,7 +715,7 @@ out:
        return ret;
 }
 
-static int wl1271_fetch_nvs(struct wl1271 *wl)
+static void wl1271_fetch_nvs(struct wl1271 *wl)
 {
        const struct firmware *fw;
        int ret;
@@ -751,16 +723,15 @@ static int wl1271_fetch_nvs(struct wl1271 *wl)
        ret = request_firmware(&fw, WL12XX_NVS_NAME, wl->dev);
 
        if (ret < 0) {
-               wl1271_error("could not get nvs file %s: %d", WL12XX_NVS_NAME,
-                            ret);
-               return ret;
+               wl1271_debug(DEBUG_BOOT, "could not get nvs file %s: %d",
+                            WL12XX_NVS_NAME, ret);
+               return;
        }
 
        wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
 
        if (!wl->nvs) {
                wl1271_error("could not allocate memory for the nvs file");
-               ret = -ENOMEM;
                goto out;
        }
 
@@ -768,8 +739,6 @@ static int wl1271_fetch_nvs(struct wl1271 *wl)
 
 out:
        release_firmware(fw);
-
-       return ret;
 }
 
 void wl12xx_queue_recovery_work(struct wl1271 *wl)
@@ -820,14 +789,16 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 
        /*
         * Make sure the chip is awake and the logger isn't active.
-        * This might fail if the firmware hanged.
+        * Do not send a stop fwlog command if the fw is hanged.
         */
-       if (!wl1271_ps_elp_wakeup(wl))
+       if (!wl1271_ps_elp_wakeup(wl) && !wl->watchdog_recovery)
                wl12xx_cmd_stop_fwlog(wl);
+       else
+               goto out;
 
        /* Read the first memory block address */
-       wl12xx_fw_status(wl, wl->fw_status);
-       first_addr = le32_to_cpu(wl->fw_status->log_start_addr);
+       wl12xx_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
+       first_addr = le32_to_cpu(wl->fw_status_2->log_start_addr);
        if (!first_addr)
                goto out;
 
@@ -872,9 +843,14 @@ static void wl1271_recovery_work(struct work_struct *work)
 
        wl12xx_read_fwlog_panic(wl);
 
-       wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x",
+       /* change partitions momentarily so we can read the FW pc */
+       wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
+       wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x "
+                   "hint_sts: 0x%08x",
                    wl->chip.fw_ver_str,
-                   wlcore_read_reg(wl, REG_PC_ON_RECOVERY));
+                   wlcore_read_reg(wl, REG_PC_ON_RECOVERY),
+                   wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR));
+       wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
 
        BUG_ON(bug_on_recovery &&
               !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
@@ -885,8 +861,6 @@ static void wl1271_recovery_work(struct work_struct *work)
                goto out_unlock;
        }
 
-       BUG_ON(bug_on_recovery);
-
        /*
         * Advance security sequence number to overcome potential progress
         * in the firmware during recovery. This doens't hurt if the network is
@@ -900,7 +874,7 @@ static void wl1271_recovery_work(struct work_struct *work)
        }
 
        /* Prevent spurious TX during FW restart */
-       ieee80211_stop_queues(wl->hw);
+       wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART);
 
        if (wl->sched_scanning) {
                ieee80211_sched_scan_stopped(wl->hw);
@@ -914,6 +888,7 @@ static void wl1271_recovery_work(struct work_struct *work)
                vif = wl12xx_wlvif_to_vif(wlvif);
                __wl1271_op_remove_interface(wl, vif, false);
        }
+        wl->watchdog_recovery = false;
        mutex_unlock(&wl->mutex);
        wl1271_op_stop(wl->hw);
 
@@ -925,9 +900,10 @@ static void wl1271_recovery_work(struct work_struct *work)
         * Its safe to enable TX now - the queues are stopped after a request
         * to restart the HW.
         */
-       ieee80211_wake_queues(wl->hw);
+       wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART);
        return;
 out_unlock:
+        wl->watchdog_recovery = false;
        mutex_unlock(&wl->mutex);
 }
 
@@ -938,13 +914,19 @@ static void wl1271_fw_wakeup(struct wl1271 *wl)
 
 static int wl1271_setup(struct wl1271 *wl)
 {
-       wl->fw_status = kmalloc(sizeof(*wl->fw_status), GFP_KERNEL);
-       if (!wl->fw_status)
+       wl->fw_status_1 = kmalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
+                                 sizeof(*wl->fw_status_2) +
+                                 wl->fw_status_priv_len, GFP_KERNEL);
+       if (!wl->fw_status_1)
                return -ENOMEM;
 
+       wl->fw_status_2 = (struct wl_fw_status_2 *)
+                               (((u8 *) wl->fw_status_1) +
+                               WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc));
+
        wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
        if (!wl->tx_res_if) {
-               kfree(wl->fw_status);
+               kfree(wl->fw_status_1);
                return -ENOMEM;
        }
 
@@ -987,13 +969,12 @@ static int wl12xx_chip_wakeup(struct wl1271 *wl, bool plt)
         * simplify the code and since the performance impact is
         * negligible, we use the same block size for all different
         * chip types.
+        *
+        * Check if the bus supports blocksize alignment and, if it
+        * doesn't, make sure we don't have the quirk.
         */
-       if (wl1271_set_block_size(wl))
-               wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;
-
-       ret = wl->ops->identify_chip(wl);
-       if (ret < 0)
-               goto out;
+       if (!wl1271_set_block_size(wl))
+               wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;
 
        /* TODO: make sure the lower driver has set things up correctly */
 
@@ -1005,13 +986,6 @@ static int wl12xx_chip_wakeup(struct wl1271 *wl, bool plt)
        if (ret < 0)
                goto out;
 
-       /* No NVS from netlink, try to get it from the filesystem */
-       if (wl->nvs == NULL) {
-               ret = wl1271_fetch_nvs(wl);
-               if (ret < 0)
-                       goto out;
-       }
-
 out:
        return ret;
 }
@@ -1039,14 +1013,10 @@ int wl1271_plt_start(struct wl1271 *wl)
                if (ret < 0)
                        goto power_off;
 
-               ret = wl->ops->boot(wl);
+               ret = wl->ops->plt_init(wl);
                if (ret < 0)
                        goto power_off;
 
-               ret = wl1271_plt_init(wl);
-               if (ret < 0)
-                       goto irq_disable;
-
                wl->plt = true;
                wl->state = WL1271_STATE_ON;
                wl1271_notice("firmware booted in PLT mode (%s)",
@@ -1059,19 +1029,6 @@ int wl1271_plt_start(struct wl1271 *wl)
 
                goto out;
 
-irq_disable:
-               mutex_unlock(&wl->mutex);
-               /* Unlocking the mutex in the middle of handling is
-                  inherently unsafe. In this case we deem it safe to do,
-                  because we need to let any possibly pending IRQ out of
-                  the system (and while we are WL1271_STATE_OFF the IRQ
-                  work function will not do anything.) Also, any other
-                  possible concurrent operations will fail due to the
-                  current state, hence the wl1271 struct should be safe. */
-               wlcore_disable_interrupts(wl);
-               wl1271_flush_deferred_work(wl);
-               cancel_work_sync(&wl->netstack_work);
-               mutex_lock(&wl->mutex);
 power_off:
                wl1271_power_off(wl);
        }
@@ -1154,9 +1111,16 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 
        spin_lock_irqsave(&wl->wl_lock, flags);
 
-       /* queue the packet */
+       /*
+        * drop the packet if the link is invalid or the queue is stopped
+        * for any reason but watermark. Watermark is a "soft"-stop so we
+        * allow these packets through.
+        */
        if (hlid == WL12XX_INVALID_LINK_ID ||
-           (wlvif && !test_bit(hlid, wlvif->links_map))) {
+           (wlvif && !test_bit(hlid, wlvif->links_map)) ||
+            (wlcore_is_queue_stopped(wl, q) &&
+             !wlcore_is_queue_stopped_by_reason(wl, q,
+                       WLCORE_QUEUE_STOP_REASON_WATERMARK))) {
                wl1271_debug(DEBUG_TX, "DROP skb hlid %d q %d", hlid, q);
                ieee80211_free_txskb(hw, skb);
                goto out;
@@ -1174,8 +1138,8 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
         */
        if (wl->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK) {
                wl1271_debug(DEBUG_TX, "op_tx: stopping queues for q %d", q);
-               ieee80211_stop_queue(wl->hw, mapping);
-               set_bit(q, &wl->stopped_queues_map);
+               wlcore_stop_queue_locked(wl, q,
+                                        WLCORE_QUEUE_STOP_REASON_WATERMARK);
        }
 
        /*
@@ -1758,7 +1722,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
        cancel_delayed_work_sync(&wl->connection_loss_work);
 
        /* let's notify MAC80211 about the remaining pending TX frames */
-       wl12xx_tx_reset(wl, true);
+       wl12xx_tx_reset(wl);
        mutex_lock(&wl->mutex);
 
        wl1271_power_off(wl);
@@ -1767,6 +1731,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
 
        wl->rx_counter = 0;
        wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
+       wl->channel_type = NL80211_CHAN_NO_HT;
        wl->tx_blocks_available = 0;
        wl->tx_allocated_blocks = 0;
        wl->tx_results_count = 0;
@@ -1799,8 +1764,9 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
 
        wl1271_debugfs_reset(wl);
 
-       kfree(wl->fw_status);
-       wl->fw_status = NULL;
+       kfree(wl->fw_status_1);
+       wl->fw_status_1 = NULL;
+       wl->fw_status_2 = NULL;
        kfree(wl->tx_res_if);
        wl->tx_res_if = NULL;
        kfree(wl->target_mem_map);
@@ -1894,6 +1860,9 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
                wl12xx_allocate_rate_policy(wl, &wlvif->sta.basic_rate_idx);
                wl12xx_allocate_rate_policy(wl, &wlvif->sta.ap_rate_idx);
                wl12xx_allocate_rate_policy(wl, &wlvif->sta.p2p_rate_idx);
+               wlvif->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
+               wlvif->basic_rate = CONF_TX_RATE_MASK_BASIC;
+               wlvif->rate_set = CONF_TX_RATE_MASK_BASIC;
        } else {
                /* init ap data */
                wlvif->ap.bcast_hlid = WL12XX_INVALID_LINK_ID;
@@ -1903,13 +1872,19 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
                for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++)
                        wl12xx_allocate_rate_policy(wl,
                                                &wlvif->ap.ucast_rate_idx[i]);
+               wlvif->basic_rate_set = CONF_TX_AP_ENABLED_RATES;
+               /*
+                * TODO: check if basic_rate shouldn't be
+                * wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
+                * instead (the same thing for STA above).
+               */
+               wlvif->basic_rate = CONF_TX_AP_ENABLED_RATES;
+               /* TODO: this seems to be used only for STA, check it */
+               wlvif->rate_set = CONF_TX_AP_ENABLED_RATES;
        }
 
        wlvif->bitrate_masks[IEEE80211_BAND_2GHZ] = wl->conf.tx.basic_rate;
        wlvif->bitrate_masks[IEEE80211_BAND_5GHZ] = wl->conf.tx.basic_rate_5;
-       wlvif->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
-       wlvif->basic_rate = CONF_TX_RATE_MASK_BASIC;
-       wlvif->rate_set = CONF_TX_RATE_MASK_BASIC;
        wlvif->beacon_int = WL1271_DEFAULT_BEACON_INT;
 
        /*
@@ -1919,6 +1894,7 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
        wlvif->band = wl->band;
        wlvif->channel = wl->channel;
        wlvif->power_level = wl->power_level;
+       wlvif->channel_type = wl->channel_type;
 
        INIT_WORK(&wlvif->rx_streaming_enable_work,
                  wl1271_rx_streaming_enable_work);
@@ -2444,7 +2420,7 @@ static int wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        } else {
                /* The current firmware only supports sched_scan in idle */
                if (wl->sched_scanning) {
-                       wl1271_scan_sched_scan_stop(wl);
+                       wl1271_scan_sched_scan_stop(wl, wlvif);
                        ieee80211_sched_scan_stopped(wl->hw);
                }
 
@@ -2469,13 +2445,20 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        /* if the channel changes while joined, join again */
        if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
            ((wlvif->band != conf->channel->band) ||
-            (wlvif->channel != channel))) {
+            (wlvif->channel != channel) ||
+            (wlvif->channel_type != conf->channel_type))) {
                /* send all pending packets */
                wl1271_tx_work_locked(wl);
                wlvif->band = conf->channel->band;
                wlvif->channel = channel;
+               wlvif->channel_type = conf->channel_type;
 
-               if (!is_ap) {
+               if (is_ap) {
+                       ret = wl1271_init_ap_rates(wl, wlvif);
+                       if (ret < 0)
+                               wl1271_error("AP rate policy change failed %d",
+                                            ret);
+               } else {
                        /*
                         * FIXME: the mac80211 should really provide a fixed
                         * rate to use here. for now, just use the smallest
@@ -2583,8 +2566,9 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
         * frames, such as the deauth. To make sure those frames reach the air,
         * wait here until the TX queue is fully flushed.
         */
-       if ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
-           (conf->flags & IEEE80211_CONF_IDLE))
+       if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) ||
+           ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
+            (conf->flags & IEEE80211_CONF_IDLE)))
                wl1271_tx_flush(wl);
 
        mutex_lock(&wl->mutex);
@@ -2593,6 +2577,7 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
        if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
                wl->band = conf->channel->band;
                wl->channel = channel;
+               wl->channel_type = conf->channel_type;
        }
 
        if (changed & IEEE80211_CONF_CHANGE_POWER)
@@ -2825,17 +2810,6 @@ static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        int ret;
        bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
 
-       /*
-        * A role set to GEM cipher requires different Tx settings (namely
-        * spare blocks). Note when we are in this mode so the HW can adjust.
-        */
-       if (key_type == KEY_GEM) {
-               if (action == KEY_ADD_OR_REPLACE)
-                       wlvif->is_gem = true;
-               else if (action == KEY_REMOVE)
-                       wlvif->is_gem = false;
-       }
-
        if (is_ap) {
                struct wl1271_station *wl_sta;
                u8 hlid;
@@ -2913,12 +2887,21 @@ static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        return 0;
 }
 
-static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+static int wlcore_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                             struct ieee80211_vif *vif,
                             struct ieee80211_sta *sta,
                             struct ieee80211_key_conf *key_conf)
 {
        struct wl1271 *wl = hw->priv;
+
+       return wlcore_hw_set_key(wl, cmd, vif, sta, key_conf);
+}
+
+int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
+                  struct ieee80211_vif *vif,
+                  struct ieee80211_sta *sta,
+                  struct ieee80211_key_conf *key_conf)
+{
        struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        int ret;
        u32 tx_seq_32 = 0;
@@ -3029,6 +3012,7 @@ out_unlock:
 
        return ret;
 }
+EXPORT_SYMBOL_GPL(wlcore_set_key);
 
 static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif,
@@ -3167,6 +3151,7 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
                                      struct ieee80211_vif *vif)
 {
        struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        int ret;
 
        wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_stop");
@@ -3180,7 +3165,7 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
        if (ret < 0)
                goto out;
 
-       wl1271_scan_sched_scan_stop(wl);
+       wl1271_scan_sched_scan_stop(wl, wlvif);
 
        wl1271_ps_elp_sleep(wl);
 out:
@@ -3316,8 +3301,15 @@ static int wl1271_ap_set_probe_resp_tmpl(struct wl1271 *wl, u32 rates,
                                      skb->data,
                                      skb->len, 0,
                                      rates);
-
        dev_kfree_skb(skb);
+
+       if (ret < 0)
+               goto out;
+
+       wl1271_debug(DEBUG_AP, "probe response updated");
+       set_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags);
+
+out:
        return ret;
 }
 
@@ -3422,6 +3414,87 @@ out:
        return ret;
 }
 
+static int wlcore_set_beacon_template(struct wl1271 *wl,
+                                     struct ieee80211_vif *vif,
+                                     bool is_ap)
+{
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       struct ieee80211_hdr *hdr;
+       u32 min_rate;
+       int ret;
+       int ieoffset = offsetof(struct ieee80211_mgmt,
+                               u.beacon.variable);
+       struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif);
+       u16 tmpl_id;
+
+       if (!beacon) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       wl1271_debug(DEBUG_MASTER, "beacon updated");
+
+       ret = wl1271_ssid_set(vif, beacon, ieoffset);
+       if (ret < 0) {
+               dev_kfree_skb(beacon);
+               goto out;
+       }
+       min_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
+       tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON :
+               CMD_TEMPL_BEACON;
+       ret = wl1271_cmd_template_set(wl, wlvif->role_id, tmpl_id,
+                                     beacon->data,
+                                     beacon->len, 0,
+                                     min_rate);
+       if (ret < 0) {
+               dev_kfree_skb(beacon);
+               goto out;
+       }
+
+       /*
+        * In case we already have a probe-resp beacon set explicitly
+        * by usermode, don't use the beacon data.
+        */
+       if (test_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags))
+               goto end_bcn;
+
+       /* remove TIM ie from probe response */
+       wl12xx_remove_ie(beacon, WLAN_EID_TIM, ieoffset);
+
+       /*
+        * remove p2p ie from probe response.
+        * the fw reponds to probe requests that don't include
+        * the p2p ie. probe requests with p2p ie will be passed,
+        * and will be responded by the supplicant (the spec
+        * forbids including the p2p ie when responding to probe
+        * requests that didn't include it).
+        */
+       wl12xx_remove_vendor_ie(beacon, WLAN_OUI_WFA,
+                               WLAN_OUI_TYPE_WFA_P2P, ieoffset);
+
+       hdr = (struct ieee80211_hdr *) beacon->data;
+       hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                        IEEE80211_STYPE_PROBE_RESP);
+       if (is_ap)
+               ret = wl1271_ap_set_probe_resp_tmpl_legacy(wl, vif,
+                                                          beacon->data,
+                                                          beacon->len,
+                                                          min_rate);
+       else
+               ret = wl1271_cmd_template_set(wl, wlvif->role_id,
+                                             CMD_TEMPL_PROBE_RESPONSE,
+                                             beacon->data,
+                                             beacon->len, 0,
+                                             min_rate);
+end_bcn:
+       dev_kfree_skb(beacon);
+       if (ret < 0)
+               goto out;
+
+out:
+       return ret;
+}
+
 static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
                                          struct ieee80211_vif *vif,
                                          struct ieee80211_bss_conf *bss_conf,
@@ -3440,81 +3513,12 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
 
        if ((changed & BSS_CHANGED_AP_PROBE_RESP) && is_ap) {
                u32 rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
-               if (!wl1271_ap_set_probe_resp_tmpl(wl, rate, vif)) {
-                       wl1271_debug(DEBUG_AP, "probe response updated");
-                       set_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags);
-               }
+
+               wl1271_ap_set_probe_resp_tmpl(wl, rate, vif);
        }
 
        if ((changed & BSS_CHANGED_BEACON)) {
-               struct ieee80211_hdr *hdr;
-               u32 min_rate;
-               int ieoffset = offsetof(struct ieee80211_mgmt,
-                                       u.beacon.variable);
-               struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif);
-               u16 tmpl_id;
-
-               if (!beacon) {
-                       ret = -EINVAL;
-                       goto out;
-               }
-
-               wl1271_debug(DEBUG_MASTER, "beacon updated");
-
-               ret = wl1271_ssid_set(vif, beacon, ieoffset);
-               if (ret < 0) {
-                       dev_kfree_skb(beacon);
-                       goto out;
-               }
-               min_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
-               tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON :
-                                 CMD_TEMPL_BEACON;
-               ret = wl1271_cmd_template_set(wl, wlvif->role_id, tmpl_id,
-                                             beacon->data,
-                                             beacon->len, 0,
-                                             min_rate);
-               if (ret < 0) {
-                       dev_kfree_skb(beacon);
-                       goto out;
-               }
-
-               /*
-                * In case we already have a probe-resp beacon set explicitly
-                * by usermode, don't use the beacon data.
-                */
-               if (test_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags))
-                       goto end_bcn;
-
-               /* remove TIM ie from probe response */
-               wl12xx_remove_ie(beacon, WLAN_EID_TIM, ieoffset);
-
-               /*
-                * remove p2p ie from probe response.
-                * the fw reponds to probe requests that don't include
-                * the p2p ie. probe requests with p2p ie will be passed,
-                * and will be responded by the supplicant (the spec
-                * forbids including the p2p ie when responding to probe
-                * requests that didn't include it).
-                */
-               wl12xx_remove_vendor_ie(beacon, WLAN_OUI_WFA,
-                                       WLAN_OUI_TYPE_WFA_P2P, ieoffset);
-
-               hdr = (struct ieee80211_hdr *) beacon->data;
-               hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
-                                                IEEE80211_STYPE_PROBE_RESP);
-               if (is_ap)
-                       ret = wl1271_ap_set_probe_resp_tmpl_legacy(wl, vif,
-                                               beacon->data,
-                                               beacon->len,
-                                               min_rate);
-               else
-                       ret = wl1271_cmd_template_set(wl, wlvif->role_id,
-                                               CMD_TEMPL_PROBE_RESPONSE,
-                                               beacon->data,
-                                               beacon->len, 0,
-                                               min_rate);
-end_bcn:
-               dev_kfree_skb(beacon);
+               ret = wlcore_set_beacon_template(wl, vif, is_ap);
                if (ret < 0)
                        goto out;
        }
@@ -3551,6 +3555,14 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
                ret = wl1271_ap_init_templates(wl, vif);
                if (ret < 0)
                        goto out;
+
+               ret = wl1271_ap_set_probe_resp_tmpl(wl, wlvif->basic_rate, vif);
+               if (ret < 0)
+                       goto out;
+
+               ret = wlcore_set_beacon_template(wl, vif, true);
+               if (ret < 0)
+                       goto out;
        }
 
        ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, changed);
@@ -3691,7 +3703,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                sta_rate_set = sta->supp_rates[wl->hw->conf.channel->band];
                if (sta->ht_cap.ht_supported)
                        sta_rate_set |=
-                           (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET);
+                         (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET) |
+                         (sta->ht_cap.mcs.rx_mask[1] << HW_MIMO_RATES_OFFSET);
                sta_ht_cap = sta->ht_cap;
                sta_exists = true;
 
@@ -3704,13 +3717,11 @@ sta_not_found:
                        u32 rates;
                        int ieoffset;
                        wlvif->aid = bss_conf->aid;
+                       wlvif->channel_type = bss_conf->channel_type;
                        wlvif->beacon_int = bss_conf->beacon_int;
                        do_join = true;
                        set_assoc = true;
 
-                       /* Cancel connection_loss_work */
-                       cancel_delayed_work_sync(&wl->connection_loss_work);
-
                        /*
                         * use basic rates from AP, and determine lowest rate
                         * to use with control frames.
@@ -3960,6 +3971,17 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
        wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed 0x%x",
                     (int)changed);
 
+       /*
+        * make sure to cancel pending disconnections if our association
+        * state changed
+        */
+       if (!is_ap && (changed & BSS_CHANGED_ASSOC))
+               cancel_delayed_work_sync(&wl->connection_loss_work);
+
+       if (is_ap && (changed & BSS_CHANGED_BEACON_ENABLED) &&
+           !bss_conf->enable_beacon)
+               wl1271_tx_flush(wl);
+
        mutex_lock(&wl->mutex);
 
        if (unlikely(wl->state == WL1271_STATE_OFF))
@@ -4636,7 +4658,7 @@ static const struct ieee80211_ops wl1271_ops = {
        .prepare_multicast = wl1271_op_prepare_multicast,
        .configure_filter = wl1271_op_configure_filter,
        .tx = wl1271_op_tx,
-       .set_key = wl1271_op_set_key,
+       .set_key = wlcore_op_set_key,
        .hw_scan = wl1271_op_hw_scan,
        .cancel_hw_scan = wl1271_op_cancel_hw_scan,
        .sched_scan_start = wl1271_op_sched_scan_start,
@@ -4905,14 +4927,8 @@ static int wl1271_register_hw(struct wl1271 *wl)
        if (wl->mac80211_registered)
                return 0;
 
-       ret = wl12xx_get_hw_info(wl);
-       if (ret < 0) {
-               wl1271_error("couldn't get hw info");
-               goto out;
-       }
-
-       ret = wl1271_fetch_nvs(wl);
-       if (ret == 0) {
+       wl1271_fetch_nvs(wl);
+       if (wl->nvs != NULL) {
                /* NOTE: The wl->nvs->nvs element must be first, in
                 * order to simplify the casting, we assume it is at
                 * the beginning of the wl->nvs structure.
@@ -4970,9 +4986,11 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
                WL1271_CIPHER_SUITE_GEM,
        };
 
-       /* The tx descriptor buffer and the TKIP space. */
-       wl->hw->extra_tx_headroom = WL1271_EXTRA_SPACE_TKIP +
-               sizeof(struct wl1271_tx_hw_descr);
+       /* The tx descriptor buffer */
+       wl->hw->extra_tx_headroom = sizeof(struct wl1271_tx_hw_descr);
+
+       if (wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE)
+               wl->hw->extra_tx_headroom += WL1271_EXTRA_SPACE_TKIP;
 
        /* unit us */
        /* FIXME: find a proper value */
@@ -5025,12 +5043,14 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
         */
        memcpy(&wl->bands[IEEE80211_BAND_2GHZ], &wl1271_band_2ghz,
               sizeof(wl1271_band_2ghz));
-       memcpy(&wl->bands[IEEE80211_BAND_2GHZ].ht_cap, &wl->ht_cap,
-              sizeof(wl->ht_cap));
+       memcpy(&wl->bands[IEEE80211_BAND_2GHZ].ht_cap,
+              &wl->ht_cap[IEEE80211_BAND_2GHZ],
+              sizeof(*wl->ht_cap));
        memcpy(&wl->bands[IEEE80211_BAND_5GHZ], &wl1271_band_5ghz,
               sizeof(wl1271_band_5ghz));
-       memcpy(&wl->bands[IEEE80211_BAND_5GHZ].ht_cap, &wl->ht_cap,
-              sizeof(wl->ht_cap));
+       memcpy(&wl->bands[IEEE80211_BAND_5GHZ].ht_cap,
+              &wl->ht_cap[IEEE80211_BAND_5GHZ],
+              sizeof(*wl->ht_cap));
 
        wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
                &wl->bands[IEEE80211_BAND_2GHZ];
@@ -5117,6 +5137,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size)
        wl->rx_counter = 0;
        wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
        wl->band = IEEE80211_BAND_2GHZ;
+       wl->channel_type = NL80211_CHAN_NO_HT;
        wl->flags = 0;
        wl->sg_enabled = true;
        wl->hw_pg_ver = -1;
@@ -5142,6 +5163,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size)
        wl->state = WL1271_STATE_OFF;
        wl->fw_type = WL12XX_FW_TYPE_NONE;
        mutex_init(&wl->mutex);
+       mutex_init(&wl->flush_mutex);
 
        order = get_order(WL1271_AGGR_BUFFER_SIZE);
        wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
@@ -5222,7 +5244,7 @@ int wlcore_free_hw(struct wl1271 *wl)
        kfree(wl->nvs);
        wl->nvs = NULL;
 
-       kfree(wl->fw_status);
+       kfree(wl->fw_status_1);
        kfree(wl->tx_res_if);
        destroy_workqueue(wl->freezable_wq);
 
@@ -5279,8 +5301,6 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
        wlcore_adjust_conf(wl);
 
        wl->irq = platform_get_irq(pdev, 0);
-       wl->ref_clock = pdata->board_ref_clock;
-       wl->tcxo_clock = pdata->board_tcxo_clock;
        wl->platform_quirks = pdata->platform_quirks;
        wl->set_power = pdata->set_power;
        wl->dev = &pdev->dev;
@@ -5316,6 +5336,16 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
        }
        disable_irq(wl->irq);
 
+       ret = wl12xx_get_hw_info(wl);
+       if (ret < 0) {
+               wl1271_error("couldn't get hw info");
+               goto out;
+       }
+
+       ret = wl->ops->identify_chip(wl);
+       if (ret < 0)
+               goto out;
+
        ret = wl1271_init_ieee80211(wl);
        if (ret)
                goto out_irq;
index 756eee2..47e81b3 100644 (file)
@@ -28,6 +28,8 @@
 
 #define WL1271_WAKEUP_TIMEOUT 500
 
+#define ELP_ENTRY_DELAY  5
+
 void wl1271_elp_work(struct work_struct *work)
 {
        struct delayed_work *dwork;
@@ -72,6 +74,7 @@ out:
 void wl1271_ps_elp_sleep(struct wl1271 *wl)
 {
        struct wl12xx_vif *wlvif;
+       u32 timeout;
 
        if (wl->quirks & WLCORE_QUIRK_NO_ELP)
                return;
@@ -89,8 +92,13 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl)
                        return;
        }
 
+       if (wl->conf.conn.forced_ps)
+               timeout = ELP_ENTRY_DELAY;
+       else
+               timeout = wl->conf.conn.dynamic_ps_timeout;
+
        ieee80211_queue_delayed_work(wl->hw, &wl->elp_work,
-               msecs_to_jiffies(wl->conf.conn.dynamic_ps_timeout));
+                                    msecs_to_jiffies(timeout));
 }
 
 int wl1271_ps_elp_wakeup(struct wl1271 *wl)
@@ -185,8 +193,12 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 
                set_bit(WLVIF_FLAG_IN_PS, &wlvif->flags);
 
-               /* enable beacon early termination. Not relevant for 5GHz */
-               if (wlvif->band == IEEE80211_BAND_2GHZ) {
+               /*
+                * enable beacon early termination.
+                * Not relevant for 5GHz and for high rates.
+                */
+               if ((wlvif->band == IEEE80211_BAND_2GHZ) &&
+                   (wlvif->basic_rate < CONF_HW_BIT_RATE_9MBPS)) {
                        ret = wl1271_acx_bet_enable(wl, wlvif, true);
                        if (ret < 0)
                                return ret;
@@ -196,7 +208,8 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                wl1271_debug(DEBUG_PSM, "leaving psm");
 
                /* disable beacon early termination */
-               if (wlvif->band == IEEE80211_BAND_2GHZ) {
+               if ((wlvif->band == IEEE80211_BAND_2GHZ) &&
+                   (wlvif->basic_rate < CONF_HW_BIT_RATE_9MBPS)) {
                        ret = wl1271_acx_bet_enable(wl, wlvif, false);
                        if (ret < 0)
                                return ret;
index d6a3c6b..78200dc 100644 (file)
@@ -186,6 +186,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
                is_data = 1;
 
        wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon);
+       wlcore_hw_set_rx_csum(wl, desc, skb);
 
        seq_num = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4;
        wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s seq %d hlid %d", skb,
@@ -199,12 +200,12 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
        return is_data;
 }
 
-void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status *status)
+void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status_1 *status)
 {
        unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
        u32 buf_size;
-       u32 fw_rx_counter  = status->fw_rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
-       u32 drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
+       u32 fw_rx_counter = status->fw_rx_counter % wl->num_rx_desc;
+       u32 drv_rx_counter = wl->rx_counter % wl->num_rx_desc;
        u32 rx_counter;
        u32 pkt_len, align_pkt_len;
        u32 pkt_offset, des;
@@ -223,7 +224,7 @@ void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status *status)
                                break;
                        buf_size += align_pkt_len;
                        rx_counter++;
-                       rx_counter &= NUM_RX_PKT_DESC_MOD_MASK;
+                       rx_counter %= wl->num_rx_desc;
                }
 
                if (buf_size == 0) {
@@ -263,7 +264,7 @@ void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status *status)
 
                        wl->rx_counter++;
                        drv_rx_counter++;
-                       drv_rx_counter &= NUM_RX_PKT_DESC_MOD_MASK;
+                       drv_rx_counter %= wl->num_rx_desc;
                        pkt_offset += wlcore_rx_get_align_buf_size(wl, pkt_len);
                }
        }
index e9a162a..9be7801 100644 (file)
@@ -38,8 +38,6 @@
 #define RX_DESC_PACKETID_SHIFT 11
 #define RX_MAX_PACKET_ID 3
 
-#define NUM_RX_PKT_DESC_MOD_MASK   7
-
 #define RX_DESC_VALID_FCS         0x0001
 #define RX_DESC_MATCH_RXADDR1     0x0002
 #define RX_DESC_MCAST             0x0004
 /* If set, the start of IP payload is not 4 bytes aligned */
 #define RX_BUF_UNALIGNED_PAYLOAD     BIT(20)
 
+/* If set, the buffer was padded by the FW to be 4 bytes aligned */
+#define RX_BUF_PADDED_PAYLOAD        BIT(30)
+
 /* Describes the alignment state of a Rx buffer */
 enum wl_rx_buf_align {
        WLCORE_RX_BUF_ALIGNED,
@@ -136,7 +137,7 @@ struct wl1271_rx_descriptor {
        u8  reserved;
 } __packed;
 
-void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status *status);
+void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status_1 *status);
 u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band);
 int wl1271_rx_filter_enable(struct wl1271 *wl,
                            int index, bool enable,
index ade21a0..d9daed5 100644 (file)
@@ -411,7 +411,8 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
                                    struct cfg80211_sched_scan_request *req,
                                    struct conn_scan_ch_params *channels,
                                    u32 band, bool radar, bool passive,
-                                   int start, int max_channels)
+                                   int start, int max_channels,
+                                   u8 *n_pactive_ch)
 {
        struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
        int i, j;
@@ -479,6 +480,23 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
                        channels[j].tx_power_att = req->channels[i]->max_power;
                        channels[j].channel = req->channels[i]->hw_value;
 
+                       if ((band == IEEE80211_BAND_2GHZ) &&
+                           (channels[j].channel >= 12) &&
+                           (channels[j].channel <= 14) &&
+                           (flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
+                           !force_passive) {
+                               /* pactive channels treated as DFS */
+                               channels[j].flags = SCAN_CHANNEL_FLAGS_DFS;
+
+                               /*
+                                * n_pactive_ch is counted down from the end of
+                                * the passive channel list
+                                */
+                               (*n_pactive_ch)++;
+                               wl1271_debug(DEBUG_SCAN, "n_pactive_ch = %d",
+                                            *n_pactive_ch);
+                       }
+
                        j++;
                }
        }
@@ -491,38 +509,47 @@ wl1271_scan_sched_scan_channels(struct wl1271 *wl,
                                struct cfg80211_sched_scan_request *req,
                                struct wl1271_cmd_sched_scan_config *cfg)
 {
+       u8 n_pactive_ch = 0;
+
        cfg->passive[0] =
                wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2,
                                                    IEEE80211_BAND_2GHZ,
                                                    false, true, 0,
-                                                   MAX_CHANNELS_2GHZ);
+                                                   MAX_CHANNELS_2GHZ,
+                                                   &n_pactive_ch);
        cfg->active[0] =
                wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2,
                                                    IEEE80211_BAND_2GHZ,
                                                    false, false,
                                                    cfg->passive[0],
-                                                   MAX_CHANNELS_2GHZ);
+                                                   MAX_CHANNELS_2GHZ,
+                                                   &n_pactive_ch);
        cfg->passive[1] =
                wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
                                                    IEEE80211_BAND_5GHZ,
                                                    false, true, 0,
-                                                   MAX_CHANNELS_5GHZ);
+                                                   MAX_CHANNELS_5GHZ,
+                                                   &n_pactive_ch);
        cfg->dfs =
                wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
                                                    IEEE80211_BAND_5GHZ,
                                                    true, true,
                                                    cfg->passive[1],
-                                                   MAX_CHANNELS_5GHZ);
+                                                   MAX_CHANNELS_5GHZ,
+                                                   &n_pactive_ch);
        cfg->active[1] =
                wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
                                                    IEEE80211_BAND_5GHZ,
                                                    false, false,
                                                    cfg->passive[1] + cfg->dfs,
-                                                   MAX_CHANNELS_5GHZ);
+                                                   MAX_CHANNELS_5GHZ,
+                                                   &n_pactive_ch);
        /* 802.11j channels are not supported yet */
        cfg->passive[2] = 0;
        cfg->active[2] = 0;
 
+       cfg->n_pactive_ch = n_pactive_ch;
+
        wl1271_debug(DEBUG_SCAN, "    2.4GHz: active %d passive %d",
                     cfg->active[0], cfg->passive[0]);
        wl1271_debug(DEBUG_SCAN, "    5GHz: active %d passive %d",
@@ -537,6 +564,7 @@ wl1271_scan_sched_scan_channels(struct wl1271 *wl,
 /* Returns the scan type to be used or a negative value on error */
 static int
 wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl,
+                                struct wl12xx_vif *wlvif,
                                 struct cfg80211_sched_scan_request *req)
 {
        struct wl1271_cmd_sched_scan_ssid_list *cmd = NULL;
@@ -565,6 +593,7 @@ wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl,
                goto out;
        }
 
+       cmd->role_id = wlvif->dev_role_id;
        if (!n_match_ssids) {
                /* No filter, with ssids */
                type = SCAN_SSID_FILTER_DISABLED;
@@ -603,7 +632,9 @@ wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl,
                                        continue;
 
                                for (j = 0; j < cmd->n_ssids; j++)
-                                       if (!memcmp(req->ssids[i].ssid,
+                                       if ((req->ssids[i].ssid_len ==
+                                            req->ssids[j].ssid_len) &&
+                                           !memcmp(req->ssids[i].ssid,
                                                   cmd->ssids[j].ssid,
                                                   req->ssids[i].ssid_len)) {
                                                cmd->ssids[j].type =
@@ -652,6 +683,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
        if (!cfg)
                return -ENOMEM;
 
+       cfg->role_id = wlvif->dev_role_id;
        cfg->rssi_threshold = c->rssi_threshold;
        cfg->snr_threshold  = c->snr_threshold;
        cfg->n_probe_reqs = c->num_probe_reqs;
@@ -669,7 +701,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
                cfg->intervals[i] = cpu_to_le32(req->interval);
 
        cfg->ssid_len = 0;
-       ret = wl12xx_scan_sched_scan_ssid_list(wl, req);
+       ret = wl12xx_scan_sched_scan_ssid_list(wl, wlvif, req);
        if (ret < 0)
                goto out;
 
@@ -741,6 +773,7 @@ int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        if (!start)
                return -ENOMEM;
 
+       start->role_id = wlvif->dev_role_id;
        start->tag = WL1271_SCAN_DEFAULT_TAG;
 
        ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start,
@@ -762,7 +795,7 @@ void wl1271_scan_sched_scan_results(struct wl1271 *wl)
        ieee80211_sched_scan_results(wl->hw);
 }
 
-void wl1271_scan_sched_scan_stop(struct wl1271 *wl)
+void wl1271_scan_sched_scan_stop(struct wl1271 *wl,  struct wl12xx_vif *wlvif)
 {
        struct wl1271_cmd_sched_scan_stop *stop;
        int ret = 0;
@@ -776,6 +809,7 @@ void wl1271_scan_sched_scan_stop(struct wl1271 *wl)
                return;
        }
 
+       stop->role_id = wlvif->dev_role_id;
        stop->tag = WL1271_SCAN_DEFAULT_TAG;
 
        ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop,
index 81ee36a..29f3c8d 100644 (file)
@@ -40,7 +40,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
                                     struct cfg80211_sched_scan_request *req,
                                     struct ieee80211_sched_scan_ies *ies);
 int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif);
-void wl1271_scan_sched_scan_stop(struct wl1271 *wl);
+void wl1271_scan_sched_scan_stop(struct wl1271 *wl,  struct wl12xx_vif *wlvif);
 void wl1271_scan_sched_scan_results(struct wl1271 *wl);
 
 #define WL1271_SCAN_MAX_CHANNELS       24
@@ -142,7 +142,8 @@ enum {
        SCAN_BSS_TYPE_ANY,
 };
 
-#define SCAN_CHANNEL_FLAGS_DFS         BIT(0)
+#define SCAN_CHANNEL_FLAGS_DFS         BIT(0) /* channel is passive until an
+                                                 activity is detected on it */
 #define SCAN_CHANNEL_FLAGS_DFS_ENABLED BIT(1)
 
 struct conn_scan_ch_params {
@@ -185,7 +186,10 @@ struct wl1271_cmd_sched_scan_config {
 
        u8 dfs;
 
-       u8 padding[3];
+       u8 n_pactive_ch; /* number of pactive (passive until fw detects energy)
+                           channels in BG band */
+       u8 role_id;
+       u8 padding[1];
 
        struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
        struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ];
@@ -212,21 +216,24 @@ struct wl1271_cmd_sched_scan_ssid_list {
 
        u8 n_ssids;
        struct wl1271_ssid ssids[SCHED_SCAN_MAX_SSIDS];
-       u8 padding[3];
+       u8 role_id;
+       u8 padding[2];
 } __packed;
 
 struct wl1271_cmd_sched_scan_start {
        struct wl1271_cmd_header header;
 
        u8 tag;
-       u8 padding[3];
+       u8 role_id;
+       u8 padding[2];
 } __packed;
 
 struct wl1271_cmd_sched_scan_stop {
        struct wl1271_cmd_header header;
 
        u8 tag;
-       u8 padding[3];
+       u8 role_id;
+       u8 padding[2];
 } __packed;
 
 
index 0a72347..c67ec48 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/module.h>
 #include <linux/vmalloc.h>
 #include <linux/platform_device.h>
+#include <linux/mmc/sdio.h>
 #include <linux/mmc/sdio_func.h>
 #include <linux/mmc/sdio_ids.h>
 #include <linux/mmc/card.h>
@@ -32,6 +33,7 @@
 #include <linux/gpio.h>
 #include <linux/wl12xx.h>
 #include <linux/pm_runtime.h>
+#include <linux/printk.h>
 
 #include "wlcore.h"
 #include "wl12xx_80211.h"
@@ -45,6 +47,8 @@
 #define SDIO_DEVICE_ID_TI_WL1271       0x4076
 #endif
 
+static bool dump = false;
+
 struct wl12xx_sdio_glue {
        struct device *dev;
        struct platform_device *core;
@@ -76,6 +80,13 @@ static void wl12xx_sdio_raw_read(struct device *child, int addr, void *buf,
 
        sdio_claim_host(func);
 
+       if (unlikely(dump)) {
+               printk(KERN_DEBUG "wlcore_sdio: READ from 0x%04x\n", addr);
+               print_hex_dump(KERN_DEBUG, "wlcore_sdio: READ ",
+                               DUMP_PREFIX_OFFSET, 16, 1,
+                               buf, len, false);
+       }
+
        if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG)) {
                ((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret);
                dev_dbg(child->parent, "sdio read 52 addr 0x%x, byte 0x%02x\n",
@@ -105,6 +116,13 @@ static void wl12xx_sdio_raw_write(struct device *child, int addr, void *buf,
 
        sdio_claim_host(func);
 
+       if (unlikely(dump)) {
+               printk(KERN_DEBUG "wlcore_sdio: WRITE to 0x%04x\n", addr);
+               print_hex_dump(KERN_DEBUG, "wlcore_sdio: WRITE ",
+                               DUMP_PREFIX_OFFSET, 16, 1,
+                               buf, len, false);
+       }
+
        if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG)) {
                sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret);
                dev_dbg(child->parent, "sdio write 52 addr 0x%x, byte 0x%02x\n",
@@ -196,6 +214,7 @@ static int __devinit wl1271_probe(struct sdio_func *func,
        struct resource res[1];
        mmc_pm_flag_t mmcflags;
        int ret = -ENOMEM;
+       const char *chip_family;
 
        /* We are only able to handle the wlan function */
        if (func->num != 0x02)
@@ -236,7 +255,18 @@ static int __devinit wl1271_probe(struct sdio_func *func,
        /* Tell PM core that we don't need the card to be powered now */
        pm_runtime_put_noidle(&func->dev);
 
-       glue->core = platform_device_alloc("wl12xx", -1);
+       /*
+        * Due to a hardware bug, we can't differentiate wl18xx from
+        * wl12xx, because both report the same device ID.  The only
+        * way to differentiate is by checking the SDIO revision,
+        * which is 3.00 on the wl18xx chips.
+        */
+       if (func->card->cccr.sdio_vsn == SDIO_SDIO_REV_3_00)
+               chip_family = "wl18xx";
+       else
+               chip_family = "wl12xx";
+
+       glue->core = platform_device_alloc(chip_family, -1);
        if (!glue->core) {
                dev_err(glue->dev, "can't allocate platform_device");
                ret = -ENOMEM;
@@ -367,6 +397,9 @@ static void __exit wl1271_exit(void)
 module_init(wl1271_init);
 module_exit(wl1271_exit);
 
+module_param(dump, bool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(dump, "Enable sdio read/write dumps.");
+
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
 MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
index 6893bc2..6983e7a 100644 (file)
@@ -72,7 +72,7 @@ static int wl1271_alloc_tx_id(struct wl1271 *wl, struct sk_buff *skb)
        return id;
 }
 
-static void wl1271_free_tx_id(struct wl1271 *wl, int id)
+void wl1271_free_tx_id(struct wl1271 *wl, int id)
 {
        if (__test_and_clear_bit(id, wl->tx_frames_map)) {
                if (unlikely(wl->tx_frames_cnt == wl->num_tx_desc))
@@ -82,6 +82,7 @@ static void wl1271_free_tx_id(struct wl1271 *wl, int id)
                wl->tx_frames_cnt--;
        }
 }
+EXPORT_SYMBOL(wl1271_free_tx_id);
 
 static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl,
                                                 struct sk_buff *skb)
@@ -127,6 +128,7 @@ bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb)
 {
        return wl->dummy_packet == skb;
 }
+EXPORT_SYMBOL(wl12xx_is_dummy_packet);
 
 u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                         struct sk_buff *skb)
@@ -146,10 +148,10 @@ u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                        return wl->system_hlid;
 
                hdr = (struct ieee80211_hdr *)skb->data;
-               if (ieee80211_is_mgmt(hdr->frame_control))
-                       return wlvif->ap.global_hlid;
-               else
+               if (is_multicast_ether_addr(ieee80211_get_DA(hdr)))
                        return wlvif->ap.bcast_hlid;
+               else
+                       return wlvif->ap.global_hlid;
        }
 }
 
@@ -176,37 +178,34 @@ u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl,
                                          unsigned int packet_length)
 {
-       if (wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN)
-               return ALIGN(packet_length, WL12XX_BUS_BLOCK_SIZE);
-       else
+       if ((wl->quirks & WLCORE_QUIRK_TX_PAD_LAST_FRAME) ||
+           !(wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN))
                return ALIGN(packet_length, WL1271_TX_ALIGN_TO);
+       else
+               return ALIGN(packet_length, WL12XX_BUS_BLOCK_SIZE);
 }
 EXPORT_SYMBOL(wlcore_calc_packet_alignment);
 
 static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                              struct sk_buff *skb, u32 extra, u32 buf_offset,
-                             u8 hlid)
+                             u8 hlid, bool is_gem)
 {
        struct wl1271_tx_hw_descr *desc;
        u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra;
        u32 total_blocks;
        int id, ret = -EBUSY, ac;
-       u32 spare_blocks = wl->normal_tx_spare;
-       bool is_dummy = false;
+       u32 spare_blocks;
 
        if (buf_offset + total_len > WL1271_AGGR_BUFFER_SIZE)
                return -EAGAIN;
 
+       spare_blocks = wlcore_hw_get_spare_blocks(wl, is_gem);
+
        /* allocate free identifier for the packet */
        id = wl1271_alloc_tx_id(wl, skb);
        if (id < 0)
                return id;
 
-       if (unlikely(wl12xx_is_dummy_packet(wl, skb)))
-               is_dummy = true;
-       else if (wlvif->is_gem)
-               spare_blocks = wl->gem_tx_spare;
-
        total_blocks = wlcore_hw_calc_tx_blocks(wl, total_len, spare_blocks);
 
        if (total_blocks <= wl->tx_blocks_available) {
@@ -228,7 +227,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
                wl->tx_allocated_pkts[ac]++;
 
-               if (!is_dummy && wlvif &&
+               if (!wl12xx_is_dummy_packet(wl, skb) && wlvif &&
                    wlvif->bss_type == BSS_TYPE_AP_BSS &&
                    test_bit(hlid, wlvif->ap.sta_hlid_map))
                        wl->links[hlid].allocated_pkts++;
@@ -268,6 +267,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        if (extra) {
                int hdrlen = ieee80211_hdrlen(frame_control);
                memmove(frame_start, hdr, hdrlen);
+               skb_set_network_header(skb, skb_network_offset(skb) + extra);
        }
 
        /* configure packet life time */
@@ -330,9 +330,9 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
            ieee80211_has_protected(frame_control))
                tx_attr |= TX_HW_ATTR_HOST_ENCRYPT;
 
-       desc->reserved = 0;
        desc->tx_attr = cpu_to_le16(tx_attr);
 
+       wlcore_hw_set_tx_desc_csum(wl, desc, skb);
        wlcore_hw_set_tx_desc_data_len(wl, desc, skb);
 }
 
@@ -346,6 +346,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        u32 total_len;
        u8 hlid;
        bool is_dummy;
+       bool is_gem = false;
 
        if (!skb)
                return -EINVAL;
@@ -355,7 +356,8 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        /* TODO: handle dummy packets on multi-vifs */
        is_dummy = wl12xx_is_dummy_packet(wl, skb);
 
-       if (info->control.hw_key &&
+       if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) &&
+           info->control.hw_key &&
            info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
                extra = WL1271_EXTRA_SPACE_TKIP;
 
@@ -373,6 +375,8 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                                return ret;
                        wlvif->default_key = idx;
                }
+
+               is_gem = (cipher == WL1271_CIPHER_SUITE_GEM);
        }
        hlid = wl12xx_tx_get_hlid(wl, wlvif, skb);
        if (hlid == WL12XX_INVALID_LINK_ID) {
@@ -380,7 +384,8 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                return -EINVAL;
        }
 
-       ret = wl1271_tx_allocate(wl, wlvif, skb, extra, buf_offset, hlid);
+       ret = wl1271_tx_allocate(wl, wlvif, skb, extra, buf_offset, hlid,
+                                is_gem);
        if (ret < 0)
                return ret;
 
@@ -425,10 +430,10 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set,
                rate_set >>= 1;
        }
 
-       /* MCS rates indication are on bits 16 - 23 */
+       /* MCS rates indication are on bits 16 - 31 */
        rate_set >>= HW_HT_RATES_OFFSET - band->n_bitrates;
 
-       for (bit = 0; bit < 8; bit++) {
+       for (bit = 0; bit < 16; bit++) {
                if (rate_set & 0x1)
                        enabled_rates |= (CONF_HW_BIT_RATE_MCS_0 << bit);
                rate_set >>= 1;
@@ -439,18 +444,15 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set,
 
 void wl1271_handle_tx_low_watermark(struct wl1271 *wl)
 {
-       unsigned long flags;
        int i;
 
        for (i = 0; i < NUM_TX_QUEUES; i++) {
-               if (test_bit(i, &wl->stopped_queues_map) &&
+               if (wlcore_is_queue_stopped_by_reason(wl, i,
+                       WLCORE_QUEUE_STOP_REASON_WATERMARK) &&
                    wl->tx_queue_count[i] <= WL1271_TX_QUEUE_LOW_WATERMARK) {
                        /* firmware buffer has space, restart queues */
-                       spin_lock_irqsave(&wl->wl_lock, flags);
-                       ieee80211_wake_queue(wl->hw,
-                                            wl1271_tx_get_mac80211_queue(i));
-                       clear_bit(i, &wl->stopped_queues_map);
-                       spin_unlock_irqrestore(&wl->wl_lock, flags);
+                       wlcore_wake_queue(wl, i,
+                                         WLCORE_QUEUE_STOP_REASON_WATERMARK);
                }
        }
 }
@@ -661,7 +663,7 @@ void wl1271_tx_work_locked(struct wl1271 *wl)
        struct wl12xx_vif *wlvif;
        struct sk_buff *skb;
        struct wl1271_tx_hw_descr *desc;
-       u32 buf_offset = 0;
+       u32 buf_offset = 0, last_len = 0;
        bool sent_packets = false;
        unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
        int ret;
@@ -685,6 +687,9 @@ void wl1271_tx_work_locked(struct wl1271 *wl)
                         * Flush buffer and try again.
                         */
                        wl1271_skb_queue_head(wl, wlvif, skb);
+
+                       buf_offset = wlcore_hw_pre_pkt_send(wl, buf_offset,
+                                                           last_len);
                        wlcore_write_data(wl, REG_SLV_MEM_DATA, wl->aggr_buf,
                                          buf_offset, true);
                        sent_packets = true;
@@ -710,7 +715,8 @@ void wl1271_tx_work_locked(struct wl1271 *wl)
                                ieee80211_free_txskb(wl->hw, skb);
                        goto out_ack;
                }
-               buf_offset += ret;
+               last_len = ret;
+               buf_offset += last_len;
                wl->tx_packets_count++;
                if (has_data) {
                        desc = (struct wl1271_tx_hw_descr *) skb->data;
@@ -720,6 +726,7 @@ void wl1271_tx_work_locked(struct wl1271 *wl)
 
 out_ack:
        if (buf_offset) {
+               buf_offset = wlcore_hw_pre_pkt_send(wl, buf_offset, last_len);
                wlcore_write_data(wl, REG_SLV_MEM_DATA, wl->aggr_buf,
                                  buf_offset, true);
                sent_packets = true;
@@ -849,7 +856,8 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
        skb_pull(skb, sizeof(struct wl1271_tx_hw_descr));
 
        /* remove TKIP header space if present */
-       if (info->control.hw_key &&
+       if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) &&
+           info->control.hw_key &&
            info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
                int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
                memmove(skb->data + WL1271_EXTRA_SPACE_TKIP, skb->data,
@@ -958,7 +966,7 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 
 }
 /* caller must hold wl->mutex and TX must be stopped */
-void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
+void wl12xx_tx_reset(struct wl1271 *wl)
 {
        int i;
        struct sk_buff *skb;
@@ -973,15 +981,12 @@ void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
                        wl->tx_queue_count[i] = 0;
        }
 
-       wl->stopped_queues_map = 0;
-
        /*
         * Make sure the driver is at a consistent state, in case this
         * function is called from a context other than interface removal.
         * This call will always wake the TX queues.
         */
-       if (reset_tx_queues)
-               wl1271_handle_tx_low_watermark(wl);
+       wl1271_handle_tx_low_watermark(wl);
 
        for (i = 0; i < wl->num_tx_desc; i++) {
                if (wl->tx_frames[i] == NULL)
@@ -998,7 +1003,8 @@ void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
                         */
                        info = IEEE80211_SKB_CB(skb);
                        skb_pull(skb, sizeof(struct wl1271_tx_hw_descr));
-                       if (info->control.hw_key &&
+                       if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) &&
+                           info->control.hw_key &&
                            info->control.hw_key->cipher ==
                            WLAN_CIPHER_SUITE_TKIP) {
                                int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
@@ -1024,6 +1030,11 @@ void wl1271_tx_flush(struct wl1271 *wl)
        int i;
        timeout = jiffies + usecs_to_jiffies(WL1271_TX_FLUSH_TIMEOUT);
 
+       /* only one flush should be in progress, for consistent queue state */
+       mutex_lock(&wl->flush_mutex);
+
+       wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FLUSH);
+
        while (!time_after(jiffies, timeout)) {
                mutex_lock(&wl->mutex);
                wl1271_debug(DEBUG_TX, "flushing tx buffer: %d %d",
@@ -1032,7 +1043,7 @@ void wl1271_tx_flush(struct wl1271 *wl)
                if ((wl->tx_frames_cnt == 0) &&
                    (wl1271_tx_total_queue_count(wl) == 0)) {
                        mutex_unlock(&wl->mutex);
-                       return;
+                       goto out;
                }
                mutex_unlock(&wl->mutex);
                msleep(1);
@@ -1045,7 +1056,12 @@ void wl1271_tx_flush(struct wl1271 *wl)
        for (i = 0; i < WL12XX_MAX_LINKS; i++)
                wl1271_tx_reset_link_queues(wl, i);
        mutex_unlock(&wl->mutex);
+
+out:
+       wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FLUSH);
+       mutex_unlock(&wl->flush_mutex);
 }
+EXPORT_SYMBOL_GPL(wl1271_tx_flush);
 
 u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set)
 {
@@ -1054,3 +1070,96 @@ u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set)
 
        return BIT(__ffs(rate_set));
 }
+
+void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue,
+                             enum wlcore_queue_stop_reason reason)
+{
+       bool stopped = !!wl->queue_stop_reasons[queue];
+
+       /* queue should not be stopped for this reason */
+       WARN_ON(test_and_set_bit(reason, &wl->queue_stop_reasons[queue]));
+
+       if (stopped)
+               return;
+
+       ieee80211_stop_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue));
+}
+
+void wlcore_stop_queue(struct wl1271 *wl, u8 queue,
+                      enum wlcore_queue_stop_reason reason)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&wl->wl_lock, flags);
+       wlcore_stop_queue_locked(wl, queue, reason);
+       spin_unlock_irqrestore(&wl->wl_lock, flags);
+}
+
+void wlcore_wake_queue(struct wl1271 *wl, u8 queue,
+                      enum wlcore_queue_stop_reason reason)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&wl->wl_lock, flags);
+
+       /* queue should not be clear for this reason */
+       WARN_ON(!test_and_clear_bit(reason, &wl->queue_stop_reasons[queue]));
+
+       if (wl->queue_stop_reasons[queue])
+               goto out;
+
+       ieee80211_wake_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue));
+
+out:
+       spin_unlock_irqrestore(&wl->wl_lock, flags);
+}
+
+void wlcore_stop_queues(struct wl1271 *wl,
+                       enum wlcore_queue_stop_reason reason)
+{
+       int i;
+
+       for (i = 0; i < NUM_TX_QUEUES; i++)
+               wlcore_stop_queue(wl, i, reason);
+}
+EXPORT_SYMBOL_GPL(wlcore_stop_queues);
+
+void wlcore_wake_queues(struct wl1271 *wl,
+                       enum wlcore_queue_stop_reason reason)
+{
+       int i;
+
+       for (i = 0; i < NUM_TX_QUEUES; i++)
+               wlcore_wake_queue(wl, i, reason);
+}
+EXPORT_SYMBOL_GPL(wlcore_wake_queues);
+
+void wlcore_reset_stopped_queues(struct wl1271 *wl)
+{
+       int i;
+       unsigned long flags;
+
+       spin_lock_irqsave(&wl->wl_lock, flags);
+
+       for (i = 0; i < NUM_TX_QUEUES; i++) {
+               if (!wl->queue_stop_reasons[i])
+                       continue;
+
+               wl->queue_stop_reasons[i] = 0;
+               ieee80211_wake_queue(wl->hw,
+                                    wl1271_tx_get_mac80211_queue(i));
+       }
+
+       spin_unlock_irqrestore(&wl->wl_lock, flags);
+}
+
+bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue,
+                            enum wlcore_queue_stop_reason reason)
+{
+       return test_bit(reason, &wl->queue_stop_reasons[queue]);
+}
+
+bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue)
+{
+       return !!wl->queue_stop_reasons[queue];
+}
index 2fd6e5d..fa4be1b 100644 (file)
@@ -85,6 +85,19 @@ struct wl128x_tx_mem {
        u8 extra_bytes;
 } __packed;
 
+struct wl18xx_tx_mem {
+       /*
+        * Total number of memory blocks allocated by the host for
+        * this packet.
+        */
+       u8 total_mem_blocks;
+
+       /*
+        * control bits
+        */
+       u8 ctrl;
+} __packed;
+
 /*
  * On wl128x based devices, when TX packets are aggregated, each packet
  * size must be aligned to the SDIO block size. The maximum block size
@@ -100,6 +113,7 @@ struct wl1271_tx_hw_descr {
        union {
                struct wl127x_tx_mem wl127x_mem;
                struct wl128x_tx_mem wl128x_mem;
+               struct wl18xx_tx_mem wl18xx_mem;
        } __packed;
        /* Device time (in us) when the packet arrived to the driver */
        __le32 start_time;
@@ -116,7 +130,16 @@ struct wl1271_tx_hw_descr {
        u8 tid;
        /* host link ID (HLID) */
        u8 hlid;
-       u8 reserved;
+
+       union {
+               u8 wl12xx_reserved;
+
+               /*
+                * bit 0   -> 0 = udp, 1 = tcp
+                * bit 1:7 -> IP header offset
+                */
+               u8 wl18xx_checksum_data;
+       } __packed;
 } __packed;
 
 enum wl1271_tx_hw_res_status {
@@ -161,6 +184,13 @@ struct wl1271_tx_hw_res_if {
        struct wl1271_tx_hw_res_descr tx_results_queue[TX_HW_RESULT_QUEUE_LEN];
 } __packed;
 
+enum wlcore_queue_stop_reason {
+       WLCORE_QUEUE_STOP_REASON_WATERMARK,
+       WLCORE_QUEUE_STOP_REASON_FW_RESTART,
+       WLCORE_QUEUE_STOP_REASON_FLUSH,
+       WLCORE_QUEUE_STOP_REASON_SPARE_BLK, /* 18xx specific */
+};
+
 static inline int wl1271_tx_get_queue(int queue)
 {
        switch (queue) {
@@ -207,7 +237,7 @@ void wl1271_tx_work(struct work_struct *work);
 void wl1271_tx_work_locked(struct wl1271 *wl);
 void wl1271_tx_complete(struct wl1271 *wl);
 void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif);
-void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues);
+void wl12xx_tx_reset(struct wl1271 *wl);
 void wl1271_tx_flush(struct wl1271 *wl);
 u8 wlcore_rate_to_idx(struct wl1271 *wl, u8 rate, enum ieee80211_band band);
 u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set,
@@ -223,6 +253,21 @@ bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb);
 void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids);
 unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl,
                                          unsigned int packet_length);
+void wl1271_free_tx_id(struct wl1271 *wl, int id);
+void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue,
+                             enum wlcore_queue_stop_reason reason);
+void wlcore_stop_queue(struct wl1271 *wl, u8 queue,
+                      enum wlcore_queue_stop_reason reason);
+void wlcore_wake_queue(struct wl1271 *wl, u8 queue,
+                      enum wlcore_queue_stop_reason reason);
+void wlcore_stop_queues(struct wl1271 *wl,
+                       enum wlcore_queue_stop_reason reason);
+void wlcore_wake_queues(struct wl1271 *wl,
+                       enum wlcore_queue_stop_reason reason);
+void wlcore_reset_stopped_queues(struct wl1271 *wl);
+bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue,
+                                      enum wlcore_queue_stop_reason reason);
+bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue);
 
 /* from main.c */
 void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid);
index 0b3f0b5..761a72f 100644 (file)
@@ -24,8 +24,9 @@
 
 #include <linux/platform_device.h>
 
-#include "wl12xx.h"
+#include "wlcore_i.h"
 #include "event.h"
+#include "boot.h"
 
 /* The maximum number of Tx descriptors in all chip families */
 #define WLCORE_MAX_TX_DESCRIPTORS 32
 /* forward declaration */
 struct wl1271_tx_hw_descr;
 enum wl_rx_buf_align;
+struct wl1271_rx_descriptor;
 
 struct wlcore_ops {
        int (*identify_chip)(struct wl1271 *wl);
        int (*identify_fw)(struct wl1271 *wl);
        int (*boot)(struct wl1271 *wl);
+       int (*plt_init)(struct wl1271 *wl);
        void (*trigger_cmd)(struct wl1271 *wl, int cmd_box_addr,
                            void *buf, size_t len);
        void (*ack_event)(struct wl1271 *wl);
@@ -61,6 +64,23 @@ struct wlcore_ops {
                                    struct wl12xx_vif *wlvif);
        s8 (*get_pg_ver)(struct wl1271 *wl);
        void (*get_mac)(struct wl1271 *wl);
+       void (*set_tx_desc_csum)(struct wl1271 *wl,
+                                struct wl1271_tx_hw_descr *desc,
+                                struct sk_buff *skb);
+       void (*set_rx_csum)(struct wl1271 *wl,
+                           struct wl1271_rx_descriptor *desc,
+                           struct sk_buff *skb);
+       u32 (*ap_get_mimo_wide_rate_mask)(struct wl1271 *wl,
+                                         struct wl12xx_vif *wlvif);
+       int (*debugfs_init)(struct wl1271 *wl, struct dentry *rootdir);
+       int (*handle_static_data)(struct wl1271 *wl,
+                                 struct wl1271_static_data *static_data);
+       int (*get_spare_blocks)(struct wl1271 *wl, bool is_gem);
+       int (*set_key)(struct wl1271 *wl, enum set_key_cmd cmd,
+                      struct ieee80211_vif *vif,
+                      struct ieee80211_sta *sta,
+                      struct ieee80211_key_conf *key_conf);
+       u32 (*pre_pkt_send)(struct wl1271 *wl, u32 buf_offset, u32 last_len);
 };
 
 enum wlcore_partitions {
@@ -109,6 +129,15 @@ enum wlcore_registers {
        REG_TABLE_LEN,
 };
 
+struct wl1271_stats {
+       void *fw_stats;
+       unsigned long fw_stats_update;
+       size_t fw_stats_len;
+
+       unsigned int retry_count;
+       unsigned int excessive_retries;
+};
+
 struct wl1271 {
        struct ieee80211_hw *hw;
        bool mac80211_registered;
@@ -121,7 +150,6 @@ struct wl1271 {
 
        void (*set_power)(bool enable);
        int irq;
-       int ref_clock;
 
        spinlock_t wl_lock;
 
@@ -186,7 +214,7 @@ struct wl1271 {
 
        /* Frames scheduled for transmission, not handled yet */
        int tx_queue_count[NUM_TX_QUEUES];
-       long stopped_queues_map;
+       unsigned long queue_stop_reasons[NUM_TX_QUEUES];
 
        /* Frames received, not handled yet by mac80211 */
        struct sk_buff_head deferred_rx_queue;
@@ -205,9 +233,6 @@ struct wl1271 {
        /* FW Rx counter */
        u32 rx_counter;
 
-       /* Rx memory pool address */
-       struct wl1271_rx_mem_pool_addr rx_mem_pool_addr;
-
        /* Intermediate buffer, used for packet aggregation */
        u8 *aggr_buf;
 
@@ -228,6 +253,7 @@ struct wl1271 {
 
        /* Hardware recovery work */
        struct work_struct recovery_work;
+       bool watchdog_recovery;
 
        /* Pointer that holds DMA-friendly block for the mailbox */
        struct event_mailbox *mbox;
@@ -263,7 +289,8 @@ struct wl1271 {
        u32 buffer_cmd;
        u32 buffer_busyword[WL1271_BUSY_WORD_CNT];
 
-       struct wl_fw_status *fw_status;
+       struct wl_fw_status_1 *fw_status_1;
+       struct wl_fw_status_2 *fw_status_2;
        struct wl1271_tx_hw_res_if *tx_res_if;
 
        /* Current chipset configuration */
@@ -279,8 +306,6 @@ struct wl1271 {
        /* bands supported by this instance of wl12xx */
        struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS];
 
-       int tcxo_clock;
-
        /*
         * wowlan trigger was configured during suspend.
         * (currently, only "ANY" trigger is supported)
@@ -333,10 +358,8 @@ struct wl1271 {
 
        /* number of TX descriptors the HW supports. */
        u32 num_tx_desc;
-
-       /* spare Tx blocks for normal/GEM operating modes */
-       u32 normal_tx_spare;
-       u32 gem_tx_spare;
+       /* number of RX descriptors the HW supports. */
+       u32 num_rx_desc;
 
        /* translate HW Tx rates to standard rate-indices */
        const u8 **band_rate_to_idx;
@@ -348,19 +371,32 @@ struct wl1271 {
        u8 hw_min_ht_rate;
 
        /* HW HT (11n) capabilities */
-       struct ieee80211_sta_ht_cap ht_cap;
+       struct ieee80211_sta_ht_cap ht_cap[IEEE80211_NUM_BANDS];
 
        /* size of the private FW status data */
        size_t fw_status_priv_len;
 
        /* RX Data filter rule state - enabled/disabled */
        bool rx_filter_enabled[WL1271_MAX_RX_FILTERS];
+
+       /* size of the private static data */
+       size_t static_data_priv_len;
+
+       /* the current channel type */
+       enum nl80211_channel_type channel_type;
+
+       /* mutex for protecting the tx_flush function */
+       struct mutex flush_mutex;
 };
 
 int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev);
 int __devexit wlcore_remove(struct platform_device *pdev);
 struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size);
 int wlcore_free_hw(struct wl1271 *wl);
+int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
+                  struct ieee80211_vif *vif,
+                  struct ieee80211_sta *sta,
+                  struct ieee80211_key_conf *key_conf);
 
 /* Firmware image load chunk size */
 #define CHUNK_SIZE     16384
@@ -385,6 +421,12 @@ int wlcore_free_hw(struct wl1271 *wl);
 /* Some firmwares may not support ELP */
 #define WLCORE_QUIRK_NO_ELP                    BIT(6)
 
+/* pad only the last frame in the aggregate buffer */
+#define WLCORE_QUIRK_TX_PAD_LAST_FRAME         BIT(7)
+
+/* extra header space is required for TKIP */
+#define WLCORE_QUIRK_TKIP_HEADER_SPACE         BIT(8)
+
 /* TODO: move to the lower drivers when all usages are abstracted */
 #define CHIP_ID_1271_PG10              (0x4030101)
 #define CHIP_ID_1271_PG20              (0x4030111)
similarity index 95%
rename from drivers/net/wireless/ti/wlcore/wl12xx.h
rename to drivers/net/wireless/ti/wlcore/wlcore_i.h
index f12bdf7..8260b1e 100644 (file)
@@ -22,8 +22,8 @@
  *
  */
 
-#ifndef __WL12XX_H__
-#define __WL12XX_H__
+#ifndef __WLCORE_I_H__
+#define __WLCORE_I_H__
 
 #include <linux/mutex.h>
 #include <linux/completion.h>
@@ -89,7 +89,7 @@
 #define WL1271_AP_BSS_INDEX        0
 #define WL1271_AP_DEF_BEACON_EXP   20
 
-#define WL1271_AGGR_BUFFER_SIZE (4 * PAGE_SIZE)
+#define WL1271_AGGR_BUFFER_SIZE (5 * PAGE_SIZE)
 
 enum wl1271_state {
        WL1271_STATE_OFF,
@@ -132,16 +132,7 @@ struct wl1271_chip {
        unsigned int fw_ver[NUM_FW_VER];
 };
 
-struct wl1271_stats {
-       struct acx_statistics *fw_stats;
-       unsigned long fw_stats_update;
-
-       unsigned int retry_count;
-       unsigned int excessive_retries;
-};
-
 #define NUM_TX_QUEUES              4
-#define NUM_RX_PKT_DESC            8
 
 #define AP_MAX_STATIONS            8
 
@@ -159,13 +150,26 @@ struct wl_fw_packet_counters {
 } __packed;
 
 /* FW status registers */
-struct wl_fw_status {
+struct wl_fw_status_1 {
        __le32 intr;
        u8  fw_rx_counter;
        u8  drv_rx_counter;
        u8  reserved;
        u8  tx_results_counter;
-       __le32 rx_pkt_descs[NUM_RX_PKT_DESC];
+       __le32 rx_pkt_descs[0];
+} __packed;
+
+/*
+ * Each HW arch has a different number of Rx descriptors.
+ * The length of the status depends on it, since it holds an array
+ * of descriptors.
+ */
+#define WLCORE_FW_STATUS_1_LEN(num_rx_desc) \
+               (sizeof(struct wl_fw_status_1) + \
+               (sizeof(((struct wl_fw_status_1 *)0)->rx_pkt_descs[0])) * \
+               num_rx_desc)
+
+struct wl_fw_status_2 {
        __le32 fw_localtime;
 
        /*
@@ -194,11 +198,6 @@ struct wl_fw_status {
        u8 priv[0];
 } __packed;
 
-struct wl1271_rx_mem_pool_addr {
-       u32 addr;
-       u32 addr_extra;
-};
-
 #define WL1271_MAX_CHANNELS 64
 struct wl1271_scan {
        struct cfg80211_scan_request *req;
@@ -367,6 +366,7 @@ struct wl12xx_vif {
        /* The current band */
        enum ieee80211_band band;
        int channel;
+       enum nl80211_channel_type channel_type;
 
        u32 bitrate_masks[IEEE80211_NUM_BANDS];
        u32 basic_rate_set;
@@ -417,9 +417,6 @@ struct wl12xx_vif {
        struct work_struct rx_streaming_disable_work;
        struct timer_list rx_streaming_timer;
 
-       /* does the current role use GEM for encryption (AP or STA) */
-       bool is_gem;
-
        /*
         * This struct must be last!
         * data that has to be saved acrossed reconfigs (e.g. recovery)
@@ -501,7 +498,8 @@ void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter,
 /* Macros to handle wl1271.sta_rate_set */
 #define HW_BG_RATES_MASK       0xffff
 #define HW_HT_RATES_OFFSET     16
+#define HW_MIMO_RATES_OFFSET   24
 
 #define WL12XX_HW_BLOCK_SIZE   256
 
-#endif
+#endif /* __WLCORE_I_H__ */
index 19110f0..9ac829e 100644 (file)
@@ -45,6 +45,9 @@ static const struct usb_device_id pn533_table[] = {
 };
 MODULE_DEVICE_TABLE(usb, pn533_table);
 
+/* How much time we spend listening for initiators */
+#define PN533_LISTEN_TIME 2
+
 /* frame definitions */
 #define PN533_FRAME_TAIL_SIZE 2
 #define PN533_FRAME_SIZE(f) (sizeof(struct pn533_frame) + f->datalen + \
@@ -74,6 +77,10 @@ MODULE_DEVICE_TABLE(usb, pn533_table);
 #define PN533_CMD_IN_RELEASE 0x52
 #define PN533_CMD_IN_JUMP_FOR_DEP 0x56
 
+#define PN533_CMD_TG_INIT_AS_TARGET 0x8c
+#define PN533_CMD_TG_GET_DATA 0x86
+#define PN533_CMD_TG_SET_DATA 0x8e
+
 #define PN533_CMD_RESPONSE(cmd) (cmd + 1)
 
 /* PN533 Return codes */
@@ -81,6 +88,9 @@ MODULE_DEVICE_TABLE(usb, pn533_table);
 #define PN533_CMD_MI_MASK 0x40
 #define PN533_CMD_RET_SUCCESS 0x00
 
+/* PN533 status codes */
+#define PN533_STATUS_TARGET_RELEASED 0x29
+
 struct pn533;
 
 typedef int (*pn533_cmd_complete_t) (struct pn533 *dev, void *arg,
@@ -97,8 +107,14 @@ struct pn533_fw_version {
 };
 
 /* PN533_CMD_RF_CONFIGURATION */
+#define PN533_CFGITEM_TIMING 0x02
 #define PN533_CFGITEM_MAX_RETRIES 0x05
 
+#define PN533_CONFIG_TIMING_102 0xb
+#define PN533_CONFIG_TIMING_204 0xc
+#define PN533_CONFIG_TIMING_409 0xd
+#define PN533_CONFIG_TIMING_819 0xe
+
 #define PN533_CONFIG_MAX_RETRIES_NO_RETRY 0x00
 #define PN533_CONFIG_MAX_RETRIES_ENDLESS 0xFF
 
@@ -108,6 +124,12 @@ struct pn533_config_max_retries {
        u8 mx_rty_passive_act;
 } __packed;
 
+struct pn533_config_timing {
+       u8 rfu;
+       u8 atr_res_timeout;
+       u8 dep_timeout;
+} __packed;
+
 /* PN533_CMD_IN_LIST_PASSIVE_TARGET */
 
 /* felica commands opcode */
@@ -144,6 +166,7 @@ enum {
        PN533_POLL_MOD_424KBPS_FELICA,
        PN533_POLL_MOD_106KBPS_JEWEL,
        PN533_POLL_MOD_847KBPS_B,
+       PN533_LISTEN_MOD,
 
        __PN533_POLL_MOD_AFTER_LAST,
 };
@@ -211,6 +234,9 @@ const struct pn533_poll_modulations poll_mod[] = {
                },
                .len = 3,
        },
+       [PN533_LISTEN_MOD] = {
+               .len = 0,
+       },
 };
 
 /* PN533_CMD_IN_ATR */
@@ -237,7 +263,7 @@ struct pn533_cmd_jump_dep {
        u8 active;
        u8 baud;
        u8 next;
-       u8 gt[];
+       u8 data[];
 } __packed;
 
 struct pn533_cmd_jump_dep_response {
@@ -253,6 +279,29 @@ struct pn533_cmd_jump_dep_response {
        u8 gt[];
 } __packed;
 
+
+/* PN533_TG_INIT_AS_TARGET */
+#define PN533_INIT_TARGET_PASSIVE 0x1
+#define PN533_INIT_TARGET_DEP 0x2
+
+#define PN533_INIT_TARGET_RESP_FRAME_MASK 0x3
+#define PN533_INIT_TARGET_RESP_ACTIVE     0x1
+#define PN533_INIT_TARGET_RESP_DEP        0x4
+
+struct pn533_cmd_init_target {
+       u8 mode;
+       u8 mifare[6];
+       u8 felica[18];
+       u8 nfcid3[10];
+       u8 gb_len;
+       u8 gb[];
+} __packed;
+
+struct pn533_cmd_init_target_response {
+       u8 mode;
+       u8 cmd[];
+} __packed;
+
 struct pn533 {
        struct usb_device *udev;
        struct usb_interface *interface;
@@ -270,22 +319,31 @@ struct pn533 {
 
        struct workqueue_struct *wq;
        struct work_struct cmd_work;
+       struct work_struct poll_work;
        struct work_struct mi_work;
+       struct work_struct tg_work;
+       struct timer_list listen_timer;
        struct pn533_frame *wq_in_frame;
        int wq_in_error;
+       int cancel_listen;
 
        pn533_cmd_complete_t cmd_complete;
        void *cmd_complete_arg;
-       struct semaphore cmd_lock;
+       struct mutex cmd_lock;
        u8 cmd;
 
        struct pn533_poll_modulations *poll_mod_active[PN533_POLL_MOD_MAX + 1];
        u8 poll_mod_count;
        u8 poll_mod_curr;
        u32 poll_protocols;
+       u32 listen_protocols;
+
+       u8 *gb;
+       size_t gb_len;
 
        u8 tgt_available_prots;
        u8 tgt_active_prot;
+       u8 tgt_mode;
 };
 
 struct pn533_frame {
@@ -405,7 +463,7 @@ static void pn533_wq_cmd_complete(struct work_struct *work)
                                        PN533_FRAME_CMD_PARAMS_LEN(in_frame));
 
        if (rc != -EINPROGRESS)
-               up(&dev->cmd_lock);
+               mutex_unlock(&dev->cmd_lock);
 }
 
 static void pn533_recv_response(struct urb *urb)
@@ -583,7 +641,7 @@ static int pn533_send_cmd_frame_async(struct pn533 *dev,
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       if (down_trylock(&dev->cmd_lock))
+       if (!mutex_trylock(&dev->cmd_lock))
                return -EBUSY;
 
        rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame,
@@ -593,7 +651,7 @@ static int pn533_send_cmd_frame_async(struct pn533 *dev,
 
        return 0;
 error:
-       up(&dev->cmd_lock);
+       mutex_unlock(&dev->cmd_lock);
        return rc;
 }
 
@@ -963,6 +1021,11 @@ static int pn533_target_found(struct pn533 *dev,
        return 0;
 }
 
+static inline void pn533_poll_next_mod(struct pn533 *dev)
+{
+       dev->poll_mod_curr = (dev->poll_mod_curr + 1) % dev->poll_mod_count;
+}
+
 static void pn533_poll_reset_mod_list(struct pn533 *dev)
 {
        dev->poll_mod_count = 0;
@@ -975,102 +1038,283 @@ static void pn533_poll_add_mod(struct pn533 *dev, u8 mod_index)
        dev->poll_mod_count++;
 }
 
-static void pn533_poll_create_mod_list(struct pn533 *dev, u32 protocols)
+static void pn533_poll_create_mod_list(struct pn533 *dev,
+                                      u32 im_protocols, u32 tm_protocols)
 {
        pn533_poll_reset_mod_list(dev);
 
-       if (protocols & NFC_PROTO_MIFARE_MASK
-                                       || protocols & NFC_PROTO_ISO14443_MASK
-                                       || protocols & NFC_PROTO_NFC_DEP_MASK)
+       if (im_protocols & NFC_PROTO_MIFARE_MASK
+           || im_protocols & NFC_PROTO_ISO14443_MASK
+           || im_protocols & NFC_PROTO_NFC_DEP_MASK)
                pn533_poll_add_mod(dev, PN533_POLL_MOD_106KBPS_A);
 
-       if (protocols & NFC_PROTO_FELICA_MASK
-                                       || protocols & NFC_PROTO_NFC_DEP_MASK) {
+       if (im_protocols & NFC_PROTO_FELICA_MASK
+           || im_protocols & NFC_PROTO_NFC_DEP_MASK) {
                pn533_poll_add_mod(dev, PN533_POLL_MOD_212KBPS_FELICA);
                pn533_poll_add_mod(dev, PN533_POLL_MOD_424KBPS_FELICA);
        }
 
-       if (protocols & NFC_PROTO_JEWEL_MASK)
+       if (im_protocols & NFC_PROTO_JEWEL_MASK)
                pn533_poll_add_mod(dev, PN533_POLL_MOD_106KBPS_JEWEL);
 
-       if (protocols & NFC_PROTO_ISO14443_MASK)
+       if (im_protocols & NFC_PROTO_ISO14443_MASK)
                pn533_poll_add_mod(dev, PN533_POLL_MOD_847KBPS_B);
+
+       if (tm_protocols)
+               pn533_poll_add_mod(dev, PN533_LISTEN_MOD);
+}
+
+static int pn533_start_poll_complete(struct pn533 *dev, void *arg,
+                                    u8 *params, int params_len)
+{
+       struct pn533_poll_response *resp;
+       int rc;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+       resp = (struct pn533_poll_response *) params;
+       if (resp->nbtg) {
+               rc = pn533_target_found(dev, resp, params_len);
+
+               /* We must stop the poll after a valid target found */
+               if (rc == 0) {
+                       pn533_poll_reset_mod_list(dev);
+                       return 0;
+               }
+       }
+
+       return -EAGAIN;
 }
 
-static void pn533_start_poll_frame(struct pn533_frame *frame,
-                                       struct pn533_poll_modulations *mod)
+static int pn533_init_target_frame(struct pn533_frame *frame,
+                                  u8 *gb, size_t gb_len)
 {
+       struct pn533_cmd_init_target *cmd;
+       size_t cmd_len;
+       u8 felica_params[18] = {0x1, 0xfe, /* DEP */
+                               0x0, 0x0, 0x0, 0x0, 0x0, 0x0, /* random */
+                               0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+                               0xff, 0xff}; /* System code */
+       u8 mifare_params[6] = {0x1, 0x1, /* SENS_RES */
+                              0x0, 0x0, 0x0,
+                              0x40}; /* SEL_RES for DEP */
+
+       cmd_len = sizeof(struct pn533_cmd_init_target) + gb_len + 1;
+       cmd = kzalloc(cmd_len, GFP_KERNEL);
+       if (cmd == NULL)
+               return -ENOMEM;
+
+       pn533_tx_frame_init(frame, PN533_CMD_TG_INIT_AS_TARGET);
+
+       /* DEP support only */
+       cmd->mode |= PN533_INIT_TARGET_DEP;
+
+       /* Felica params */
+       memcpy(cmd->felica, felica_params, 18);
+       get_random_bytes(cmd->felica + 2, 6);
+
+       /* NFCID3 */
+       memset(cmd->nfcid3, 0, 10);
+       memcpy(cmd->nfcid3, cmd->felica, 8);
 
-       pn533_tx_frame_init(frame, PN533_CMD_IN_LIST_PASSIVE_TARGET);
+       /* MIFARE params */
+       memcpy(cmd->mifare, mifare_params, 6);
 
-       memcpy(PN533_FRAME_CMD_PARAMS_PTR(frame), &mod->data, mod->len);
-       frame->datalen += mod->len;
+       /* General bytes */
+       cmd->gb_len = gb_len;
+       memcpy(cmd->gb, gb, gb_len);
+
+       /* Len Tk */
+       cmd->gb[gb_len] = 0;
+
+       memcpy(PN533_FRAME_CMD_PARAMS_PTR(frame), cmd, cmd_len);
+
+       frame->datalen += cmd_len;
 
        pn533_tx_frame_finish(frame);
+
+       kfree(cmd);
+
+       return 0;
 }
 
-static int pn533_start_poll_complete(struct pn533 *dev, void *arg,
-                                               u8 *params, int params_len)
+#define PN533_CMD_DATAEXCH_HEAD_LEN (sizeof(struct pn533_frame) + 3)
+#define PN533_CMD_DATAEXCH_DATA_MAXLEN 262
+static int pn533_tm_get_data_complete(struct pn533 *dev, void *arg,
+                                     u8 *params, int params_len)
 {
-       struct pn533_poll_response *resp;
-       struct pn533_poll_modulations *next_mod;
-       int rc;
+       struct sk_buff *skb_resp = arg;
+       struct pn533_frame *in_frame = (struct pn533_frame *) skb_resp->data;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
-       if (params_len == -ENOENT) {
-               nfc_dev_dbg(&dev->interface->dev, "Polling operation has been"
-                                                               " stopped");
-               goto stop_poll;
+       if (params_len < 0) {
+               nfc_dev_err(&dev->interface->dev,
+                           "Error %d when starting as a target",
+                           params_len);
+
+               return params_len;
        }
 
+       if (params_len > 0 && params[0] != 0) {
+               nfc_tm_deactivated(dev->nfc_dev);
+
+               dev->tgt_mode = 0;
+
+               kfree_skb(skb_resp);
+               return 0;
+       }
+
+       skb_put(skb_resp, PN533_FRAME_SIZE(in_frame));
+       skb_pull(skb_resp, PN533_CMD_DATAEXCH_HEAD_LEN);
+       skb_trim(skb_resp, skb_resp->len - PN533_FRAME_TAIL_SIZE);
+
+       return nfc_tm_data_received(dev->nfc_dev, skb_resp);
+}
+
+static void pn533_wq_tg_get_data(struct work_struct *work)
+{
+       struct pn533 *dev = container_of(work, struct pn533, tg_work);
+       struct pn533_frame *in_frame;
+       struct sk_buff *skb_resp;
+       size_t skb_resp_len;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+       skb_resp_len = PN533_CMD_DATAEXCH_HEAD_LEN +
+               PN533_CMD_DATAEXCH_DATA_MAXLEN +
+               PN533_FRAME_TAIL_SIZE;
+
+       skb_resp = nfc_alloc_recv_skb(skb_resp_len, GFP_KERNEL);
+       if (!skb_resp)
+               return;
+
+       in_frame = (struct pn533_frame *)skb_resp->data;
+
+       pn533_tx_frame_init(dev->out_frame, PN533_CMD_TG_GET_DATA);
+       pn533_tx_frame_finish(dev->out_frame);
+
+       pn533_send_cmd_frame_async(dev, dev->out_frame, in_frame,
+                                  skb_resp_len,
+                                  pn533_tm_get_data_complete,
+                                  skb_resp, GFP_KERNEL);
+
+       return;
+}
+
+#define ATR_REQ_GB_OFFSET 17
+static int pn533_init_target_complete(struct pn533 *dev, void *arg,
+                                     u8 *params, int params_len)
+{
+       struct pn533_cmd_init_target_response *resp;
+       u8 frame, comm_mode = NFC_COMM_PASSIVE, *gb;
+       size_t gb_len;
+       int rc;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
        if (params_len < 0) {
-               nfc_dev_err(&dev->interface->dev, "Error %d when running poll",
-                                                               params_len);
-               goto stop_poll;
+               nfc_dev_err(&dev->interface->dev,
+                           "Error %d when starting as a target",
+                           params_len);
+
+               return params_len;
        }
 
-       resp = (struct pn533_poll_response *) params;
-       if (resp->nbtg) {
-               rc = pn533_target_found(dev, resp, params_len);
+       if (params_len < ATR_REQ_GB_OFFSET + 1)
+               return -EINVAL;
 
-               /* We must stop the poll after a valid target found */
-               if (rc == 0)
-                       goto stop_poll;
+       resp = (struct pn533_cmd_init_target_response *) params;
+
+       nfc_dev_dbg(&dev->interface->dev, "Target mode 0x%x param len %d\n",
+                   resp->mode, params_len);
+
+       frame = resp->mode & PN533_INIT_TARGET_RESP_FRAME_MASK;
+       if (frame == PN533_INIT_TARGET_RESP_ACTIVE)
+               comm_mode = NFC_COMM_ACTIVE;
 
-               if (rc != -EAGAIN)
-                       nfc_dev_err(&dev->interface->dev, "The target found is"
-                                       " not valid - continuing to poll");
+       /* Again, only DEP */
+       if ((resp->mode & PN533_INIT_TARGET_RESP_DEP) == 0)
+               return -EOPNOTSUPP;
+
+       gb = resp->cmd + ATR_REQ_GB_OFFSET;
+       gb_len = params_len - (ATR_REQ_GB_OFFSET + 1);
+
+       rc = nfc_tm_activated(dev->nfc_dev, NFC_PROTO_NFC_DEP_MASK,
+                             comm_mode, gb, gb_len);
+       if (rc < 0) {
+               nfc_dev_err(&dev->interface->dev,
+                           "Error when signaling target activation");
+               return rc;
        }
 
-       dev->poll_mod_curr = (dev->poll_mod_curr + 1) % dev->poll_mod_count;
+       dev->tgt_mode = 1;
 
-       next_mod = dev->poll_mod_active[dev->poll_mod_curr];
+       queue_work(dev->wq, &dev->tg_work);
 
-       nfc_dev_dbg(&dev->interface->dev, "Polling next modulation (0x%x)",
-                                                       dev->poll_mod_curr);
+       return 0;
+}
+
+static void pn533_listen_mode_timer(unsigned long data)
+{
+       struct pn533 *dev = (struct pn533 *) data;
+
+       nfc_dev_dbg(&dev->interface->dev, "Listen mode timeout");
+
+       /* An ack will cancel the last issued command (poll) */
+       pn533_send_ack(dev, GFP_ATOMIC);
+
+       dev->cancel_listen = 1;
+
+       mutex_unlock(&dev->cmd_lock);
+
+       pn533_poll_next_mod(dev);
+
+       queue_work(dev->wq, &dev->poll_work);
+}
 
-       pn533_start_poll_frame(dev->out_frame, next_mod);
+static int pn533_poll_complete(struct pn533 *dev, void *arg,
+                              u8 *params, int params_len)
+{
+       struct pn533_poll_modulations *cur_mod;
+       int rc;
 
-       /* Don't need to down the semaphore again */
-       rc = __pn533_send_cmd_frame_async(dev, dev->out_frame, dev->in_frame,
-                               dev->in_maxlen, pn533_start_poll_complete,
-                               NULL, GFP_ATOMIC);
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+       if (params_len == -ENOENT) {
+               if (dev->poll_mod_count != 0)
+                       return 0;
+
+               nfc_dev_err(&dev->interface->dev,
+                           "Polling operation has been stopped");
 
-       if (rc == -EPERM) {
-               nfc_dev_dbg(&dev->interface->dev, "Cannot poll next modulation"
-                                       " because poll has been stopped");
                goto stop_poll;
        }
 
-       if (rc) {
-               nfc_dev_err(&dev->interface->dev, "Error %d when trying to poll"
-                                                       " next modulation", rc);
+       if (params_len < 0) {
+               nfc_dev_err(&dev->interface->dev,
+                           "Error %d when running poll", params_len);
+
                goto stop_poll;
        }
 
-       /* Inform caller function to do not up the semaphore */
-       return -EINPROGRESS;
+       cur_mod = dev->poll_mod_active[dev->poll_mod_curr];
+
+       if (cur_mod->len == 0) {
+               del_timer(&dev->listen_timer);
+
+               return pn533_init_target_complete(dev, arg, params, params_len);
+       } else {
+               rc = pn533_start_poll_complete(dev, arg, params, params_len);
+               if (!rc)
+                       return rc;
+       }
+
+       pn533_poll_next_mod(dev);
+
+       queue_work(dev->wq, &dev->poll_work);
+
+       return 0;
 
 stop_poll:
        pn533_poll_reset_mod_list(dev);
@@ -1078,61 +1322,104 @@ stop_poll:
        return 0;
 }
 
-static int pn533_start_poll(struct nfc_dev *nfc_dev, u32 protocols)
+static void pn533_build_poll_frame(struct pn533 *dev,
+                                  struct pn533_frame *frame,
+                                  struct pn533_poll_modulations *mod)
 {
-       struct pn533 *dev = nfc_get_drvdata(nfc_dev);
-       struct pn533_poll_modulations *start_mod;
-       int rc;
+       nfc_dev_dbg(&dev->interface->dev, "mod len %d\n", mod->len);
 
-       nfc_dev_dbg(&dev->interface->dev, "%s - protocols=0x%x", __func__,
-                                                               protocols);
+       if (mod->len == 0) {
+               /* Listen mode */
+               pn533_init_target_frame(frame, dev->gb, dev->gb_len);
+       } else {
+               /* Polling mode */
+               pn533_tx_frame_init(frame, PN533_CMD_IN_LIST_PASSIVE_TARGET);
 
-       if (dev->poll_mod_count) {
-               nfc_dev_err(&dev->interface->dev, "Polling operation already"
-                                                               " active");
-               return -EBUSY;
-       }
+               memcpy(PN533_FRAME_CMD_PARAMS_PTR(frame), &mod->data, mod->len);
+               frame->datalen += mod->len;
 
-       if (dev->tgt_active_prot) {
-               nfc_dev_err(&dev->interface->dev, "Cannot poll with a target"
-                                                       " already activated");
-               return -EBUSY;
+               pn533_tx_frame_finish(frame);
        }
+}
 
-       pn533_poll_create_mod_list(dev, protocols);
+static int pn533_send_poll_frame(struct pn533 *dev)
+{
+       struct pn533_poll_modulations *cur_mod;
+       int rc;
 
-       if (!dev->poll_mod_count) {
-               nfc_dev_err(&dev->interface->dev, "No valid protocols"
-                                                               " specified");
-               rc = -EINVAL;
-               goto error;
+       cur_mod = dev->poll_mod_active[dev->poll_mod_curr];
+
+       pn533_build_poll_frame(dev, dev->out_frame, cur_mod);
+
+       rc = pn533_send_cmd_frame_async(dev, dev->out_frame, dev->in_frame,
+                               dev->in_maxlen, pn533_poll_complete,
+                               NULL, GFP_KERNEL);
+       if (rc)
+               nfc_dev_err(&dev->interface->dev, "Polling loop error %d", rc);
+
+       return rc;
+}
+
+static void pn533_wq_poll(struct work_struct *work)
+{
+       struct pn533 *dev = container_of(work, struct pn533, poll_work);
+       struct pn533_poll_modulations *cur_mod;
+       int rc;
+
+       cur_mod = dev->poll_mod_active[dev->poll_mod_curr];
+
+       nfc_dev_dbg(&dev->interface->dev,
+                   "%s cancel_listen %d modulation len %d",
+                   __func__, dev->cancel_listen, cur_mod->len);
+
+       if (dev->cancel_listen == 1) {
+               dev->cancel_listen = 0;
+               usb_kill_urb(dev->in_urb);
        }
 
-       nfc_dev_dbg(&dev->interface->dev, "It will poll %d modulations types",
-                                                       dev->poll_mod_count);
+       rc = pn533_send_poll_frame(dev);
+       if (rc)
+               return;
 
-       dev->poll_mod_curr = 0;
-       start_mod = dev->poll_mod_active[dev->poll_mod_curr];
+       if (cur_mod->len == 0 && dev->poll_mod_count > 1)
+               mod_timer(&dev->listen_timer, jiffies + PN533_LISTEN_TIME * HZ);
 
-       pn533_start_poll_frame(dev->out_frame, start_mod);
+       return;
+}
 
-       rc = pn533_send_cmd_frame_async(dev, dev->out_frame, dev->in_frame,
-                               dev->in_maxlen, pn533_start_poll_complete,
-                               NULL, GFP_KERNEL);
+static int pn533_start_poll(struct nfc_dev *nfc_dev,
+                           u32 im_protocols, u32 tm_protocols)
+{
+       struct pn533 *dev = nfc_get_drvdata(nfc_dev);
 
-       if (rc) {
-               nfc_dev_err(&dev->interface->dev, "Error %d when trying to"
-                                                       " start poll", rc);
-               goto error;
+       nfc_dev_dbg(&dev->interface->dev,
+                   "%s: im protocols 0x%x tm protocols 0x%x",
+                   __func__, im_protocols, tm_protocols);
+
+       if (dev->tgt_active_prot) {
+               nfc_dev_err(&dev->interface->dev,
+                           "Cannot poll with a target already activated");
+               return -EBUSY;
        }
 
-       dev->poll_protocols = protocols;
+       if (dev->tgt_mode) {
+               nfc_dev_err(&dev->interface->dev,
+                           "Cannot poll while already being activated");
+               return -EBUSY;
+       }
 
-       return 0;
+       if (tm_protocols) {
+               dev->gb = nfc_get_local_general_bytes(nfc_dev, &dev->gb_len);
+               if (dev->gb == NULL)
+                       tm_protocols = 0;
+       }
 
-error:
-       pn533_poll_reset_mod_list(dev);
-       return rc;
+       dev->poll_mod_curr = 0;
+       pn533_poll_create_mod_list(dev, im_protocols, tm_protocols);
+       dev->poll_protocols = im_protocols;
+       dev->listen_protocols = tm_protocols;
+
+       return pn533_send_poll_frame(dev);
 }
 
 static void pn533_stop_poll(struct nfc_dev *nfc_dev)
@@ -1141,6 +1428,8 @@ static void pn533_stop_poll(struct nfc_dev *nfc_dev)
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
+       del_timer(&dev->listen_timer);
+
        if (!dev->poll_mod_count) {
                nfc_dev_dbg(&dev->interface->dev, "Polling operation was not"
                                                                " running");
@@ -1152,6 +1441,8 @@ static void pn533_stop_poll(struct nfc_dev *nfc_dev)
 
        /* prevent pn533_start_poll_complete to issue a new poll meanwhile */
        usb_kill_urb(dev->in_urb);
+
+       pn533_poll_reset_mod_list(dev);
 }
 
 static int pn533_activate_target_nfcdep(struct pn533 *dev)
@@ -1349,13 +1640,29 @@ static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg,
        return 0;
 }
 
+static int pn533_mod_to_baud(struct pn533 *dev)
+{
+       switch (dev->poll_mod_curr) {
+       case PN533_POLL_MOD_106KBPS_A:
+               return 0;
+       case PN533_POLL_MOD_212KBPS_FELICA:
+               return 1;
+       case PN533_POLL_MOD_424KBPS_FELICA:
+               return 2;
+       default:
+               return -EINVAL;
+       }
+}
+
+#define PASSIVE_DATA_LEN 5
 static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
                             u8 comm_mode, u8* gb, size_t gb_len)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
        struct pn533_cmd_jump_dep *cmd;
-       u8 cmd_len;
-       int rc;
+       u8 cmd_len, *data_ptr;
+       u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3};
+       int rc, baud;
 
        nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
 
@@ -1371,7 +1678,17 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
                return -EBUSY;
        }
 
+       baud = pn533_mod_to_baud(dev);
+       if (baud < 0) {
+               nfc_dev_err(&dev->interface->dev,
+                           "Invalid curr modulation %d", dev->poll_mod_curr);
+               return baud;
+       }
+
        cmd_len = sizeof(struct pn533_cmd_jump_dep) + gb_len;
+       if (comm_mode == NFC_COMM_PASSIVE)
+               cmd_len += PASSIVE_DATA_LEN;
+
        cmd = kzalloc(cmd_len, GFP_KERNEL);
        if (cmd == NULL)
                return -ENOMEM;
@@ -1379,10 +1696,18 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
        pn533_tx_frame_init(dev->out_frame, PN533_CMD_IN_JUMP_FOR_DEP);
 
        cmd->active = !comm_mode;
-       cmd->baud = 0;
+       cmd->next = 0;
+       cmd->baud = baud;
+       data_ptr = cmd->data;
+       if (comm_mode == NFC_COMM_PASSIVE && cmd->baud > 0) {
+               memcpy(data_ptr, passive_data, PASSIVE_DATA_LEN);
+               cmd->next |= 1;
+               data_ptr += PASSIVE_DATA_LEN;
+       }
+
        if (gb != NULL && gb_len > 0) {
-               cmd->next = 4; /* We have some Gi */
-               memcpy(cmd->gt, gb, gb_len);
+               cmd->next |= 4; /* We have some Gi */
+               memcpy(data_ptr, gb, gb_len);
        } else {
                cmd->next = 0;
        }
@@ -1407,15 +1732,25 @@ out:
 
 static int pn533_dep_link_down(struct nfc_dev *nfc_dev)
 {
-       pn533_deactivate_target(nfc_dev, 0);
+       struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+
+       pn533_poll_reset_mod_list(dev);
+
+       if (dev->tgt_mode || dev->tgt_active_prot) {
+               pn533_send_ack(dev, GFP_KERNEL);
+               usb_kill_urb(dev->in_urb);
+       }
+
+       dev->tgt_active_prot = 0;
+       dev->tgt_mode = 0;
+
+       skb_queue_purge(&dev->resp_q);
 
        return 0;
 }
 
-#define PN533_CMD_DATAEXCH_HEAD_LEN (sizeof(struct pn533_frame) + 3)
-#define PN533_CMD_DATAEXCH_DATA_MAXLEN 262
-
-static int pn533_data_exchange_tx_frame(struct pn533 *dev, struct sk_buff *skb)
+static int pn533_build_tx_frame(struct pn533 *dev, struct sk_buff *skb,
+                               bool target)
 {
        int payload_len = skb->len;
        struct pn533_frame *out_frame;
@@ -1432,14 +1767,20 @@ static int pn533_data_exchange_tx_frame(struct pn533 *dev, struct sk_buff *skb)
                return -ENOSYS;
        }
 
-       skb_push(skb, PN533_CMD_DATAEXCH_HEAD_LEN);
-       out_frame = (struct pn533_frame *) skb->data;
+       if (target == true) {
+               skb_push(skb, PN533_CMD_DATAEXCH_HEAD_LEN);
+               out_frame = (struct pn533_frame *) skb->data;
 
-       pn533_tx_frame_init(out_frame, PN533_CMD_IN_DATA_EXCHANGE);
+               pn533_tx_frame_init(out_frame, PN533_CMD_IN_DATA_EXCHANGE);
+               tg = 1;
+               memcpy(PN533_FRAME_CMD_PARAMS_PTR(out_frame), &tg, sizeof(u8));
+               out_frame->datalen += sizeof(u8);
+       } else {
+               skb_push(skb, PN533_CMD_DATAEXCH_HEAD_LEN - 1);
+               out_frame = (struct pn533_frame *) skb->data;
+               pn533_tx_frame_init(out_frame, PN533_CMD_TG_SET_DATA);
+       }
 
-       tg = 1;
-       memcpy(PN533_FRAME_CMD_PARAMS_PTR(out_frame), &tg, sizeof(u8));
-       out_frame->datalen += sizeof(u8);
 
        /* The data is already in the out_frame, just update the datalen */
        out_frame->datalen += payload_len;
@@ -1550,9 +1891,9 @@ error:
        return 0;
 }
 
-static int pn533_data_exchange(struct nfc_dev *nfc_dev,
-                              struct nfc_target *target, struct sk_buff *skb,
-                              data_exchange_cb_t cb, void *cb_context)
+static int pn533_transceive(struct nfc_dev *nfc_dev,
+                           struct nfc_target *target, struct sk_buff *skb,
+                           data_exchange_cb_t cb, void *cb_context)
 {
        struct pn533 *dev = nfc_get_drvdata(nfc_dev);
        struct pn533_frame *out_frame, *in_frame;
@@ -1570,7 +1911,7 @@ static int pn533_data_exchange(struct nfc_dev *nfc_dev,
                goto error;
        }
 
-       rc = pn533_data_exchange_tx_frame(dev, skb);
+       rc = pn533_build_tx_frame(dev, skb, true);
        if (rc)
                goto error;
 
@@ -1618,6 +1959,63 @@ error:
        return rc;
 }
 
+static int pn533_tm_send_complete(struct pn533 *dev, void *arg,
+                                 u8 *params, int params_len)
+{
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+       if (params_len < 0) {
+               nfc_dev_err(&dev->interface->dev,
+                           "Error %d when sending data",
+                           params_len);
+
+               return params_len;
+       }
+
+       if (params_len > 0 && params[0] != 0) {
+               nfc_tm_deactivated(dev->nfc_dev);
+
+               dev->tgt_mode = 0;
+
+               return 0;
+       }
+
+       queue_work(dev->wq, &dev->tg_work);
+
+       return 0;
+}
+
+static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
+{
+       struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+       struct pn533_frame *out_frame;
+       int rc;
+
+       nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+       rc = pn533_build_tx_frame(dev, skb, false);
+       if (rc)
+               goto error;
+
+       out_frame = (struct pn533_frame *) skb->data;
+
+       rc = pn533_send_cmd_frame_async(dev, out_frame, dev->in_frame,
+                                       dev->in_maxlen, pn533_tm_send_complete,
+                                       NULL, GFP_KERNEL);
+       if (rc) {
+               nfc_dev_err(&dev->interface->dev,
+                           "Error %d when trying to send data", rc);
+               goto error;
+       }
+
+       return 0;
+
+error:
+       kfree_skb(skb);
+
+       return rc;
+}
+
 static void pn533_wq_mi_recv(struct work_struct *work)
 {
        struct pn533 *dev = container_of(work, struct pn533, mi_work);
@@ -1638,7 +2036,7 @@ static void pn533_wq_mi_recv(struct work_struct *work)
 
        skb_reserve(skb_cmd, PN533_CMD_DATAEXCH_HEAD_LEN);
 
-       rc = pn533_data_exchange_tx_frame(dev, skb_cmd);
+       rc = pn533_build_tx_frame(dev, skb_cmd, true);
        if (rc)
                goto error_frame;
 
@@ -1677,7 +2075,7 @@ error_cmd:
 
        kfree(arg);
 
-       up(&dev->cmd_lock);
+       mutex_unlock(&dev->cmd_lock);
 }
 
 static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata,
@@ -1712,7 +2110,8 @@ struct nfc_ops pn533_nfc_ops = {
        .stop_poll = pn533_stop_poll,
        .activate_target = pn533_activate_target,
        .deactivate_target = pn533_deactivate_target,
-       .data_exchange = pn533_data_exchange,
+       .im_transceive = pn533_transceive,
+       .tm_send = pn533_tm_send,
 };
 
 static int pn533_probe(struct usb_interface *interface,
@@ -1723,6 +2122,7 @@ static int pn533_probe(struct usb_interface *interface,
        struct usb_host_interface *iface_desc;
        struct usb_endpoint_descriptor *endpoint;
        struct pn533_config_max_retries max_retries;
+       struct pn533_config_timing timing;
        int in_endpoint = 0;
        int out_endpoint = 0;
        int rc = -ENOMEM;
@@ -1735,7 +2135,7 @@ static int pn533_probe(struct usb_interface *interface,
 
        dev->udev = usb_get_dev(interface_to_usbdev(interface));
        dev->interface = interface;
-       sema_init(&dev->cmd_lock, 1);
+       mutex_init(&dev->cmd_lock);
 
        iface_desc = interface->cur_altsetting;
        for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
@@ -1779,12 +2179,18 @@ static int pn533_probe(struct usb_interface *interface,
 
        INIT_WORK(&dev->cmd_work, pn533_wq_cmd_complete);
        INIT_WORK(&dev->mi_work, pn533_wq_mi_recv);
+       INIT_WORK(&dev->tg_work, pn533_wq_tg_get_data);
+       INIT_WORK(&dev->poll_work, pn533_wq_poll);
        dev->wq = alloc_workqueue("pn533",
                                  WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM,
                                  1);
        if (dev->wq == NULL)
                goto error;
 
+       init_timer(&dev->listen_timer);
+       dev->listen_timer.data = (unsigned long) dev;
+       dev->listen_timer.function = pn533_listen_mode_timer;
+
        skb_queue_head_init(&dev->resp_q);
 
        usb_set_intfdata(interface, dev);
@@ -1830,13 +2236,29 @@ static int pn533_probe(struct usb_interface *interface,
        if (rc) {
                nfc_dev_err(&dev->interface->dev, "Error on setting MAX_RETRIES"
                                                                " config");
-               goto free_nfc_dev;
+               goto unregister_nfc_dev;
+       }
+
+       timing.rfu = PN533_CONFIG_TIMING_102;
+       timing.atr_res_timeout = PN533_CONFIG_TIMING_204;
+       timing.dep_timeout = PN533_CONFIG_TIMING_409;
+
+       rc = pn533_set_configuration(dev, PN533_CFGITEM_TIMING,
+                               (u8 *) &timing, sizeof(timing));
+       if (rc) {
+               nfc_dev_err(&dev->interface->dev,
+                           "Error on setting RF timings");
+               goto unregister_nfc_dev;
        }
 
        return 0;
 
+unregister_nfc_dev:
+       nfc_unregister_device(dev->nfc_dev);
+
 free_nfc_dev:
        nfc_free_device(dev->nfc_dev);
+
 destroy_wq:
        destroy_workqueue(dev->wq);
 error:
@@ -1865,6 +2287,8 @@ static void pn533_disconnect(struct usb_interface *interface)
 
        skb_queue_purge(&dev->resp_q);
 
+       del_timer(&dev->listen_timer);
+
        kfree(dev->in_frame);
        usb_free_urb(dev->in_urb);
        kfree(dev->out_frame);
index 281f18c..457eac3 100644 (file)
@@ -576,7 +576,8 @@ static int pn544_hci_xmit(struct nfc_shdlc *shdlc, struct sk_buff *skb)
        return pn544_hci_i2c_write(client, skb->data, skb->len);
 }
 
-static int pn544_hci_start_poll(struct nfc_shdlc *shdlc, u32 protocols)
+static int pn544_hci_start_poll(struct nfc_shdlc *shdlc,
+                               u32 im_protocols, u32 tm_protocols)
 {
        struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc);
        u8 phases = 0;
@@ -584,7 +585,8 @@ static int pn544_hci_start_poll(struct nfc_shdlc *shdlc, u32 protocols)
        u8 duration[2];
        u8 activated;
 
-       pr_info(DRIVER_DESC ": %s protocols = %d\n", __func__, protocols);
+       pr_info(DRIVER_DESC ": %s protocols 0x%x 0x%x\n",
+               __func__, im_protocols, tm_protocols);
 
        r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
                               NFC_HCI_EVT_END_OPERATION, NULL, 0);
@@ -604,10 +606,10 @@ static int pn544_hci_start_poll(struct nfc_shdlc *shdlc, u32 protocols)
        if (r < 0)
                return r;
 
-       if (protocols & (NFC_PROTO_ISO14443_MASK | NFC_PROTO_MIFARE_MASK |
+       if (im_protocols & (NFC_PROTO_ISO14443_MASK | NFC_PROTO_MIFARE_MASK |
                         NFC_PROTO_JEWEL_MASK))
                phases |= 1;            /* Type A */
-       if (protocols & NFC_PROTO_FELICA_MASK) {
+       if (im_protocols & NFC_PROTO_FELICA_MASK) {
                phases |= (1 << 2);     /* Type F 212 */
                phases |= (1 << 3);     /* Type F 424 */
        }
index 266c7c5..ab4627c 100644 (file)
@@ -90,6 +90,8 @@ const char *ssb_core_name(u16 coreid)
                return "ARM 1176";
        case SSB_DEV_ARM_7TDMI:
                return "ARM 7TDMI";
+       case SSB_DEV_ARM_CM3:
+               return "ARM Cortex M3";
        }
        return "UNKNOWN";
 }
index 0ae9b58..f4e6dd9 100644 (file)
  *     %NFC_ATTR_PROTOCOLS)
  * @NFC_EVENT_DEVICE_REMOVED: event emitted when a device is removed
  *     (it sends %NFC_ATTR_DEVICE_INDEX)
+ * @NFC_EVENT_TM_ACTIVATED: event emitted when the adapter is activated in
+ *      target mode.
+ * @NFC_EVENT_DEVICE_DEACTIVATED: event emitted when the adapter is deactivated
+ *      from target mode.
  */
 enum nfc_commands {
        NFC_CMD_UNSPEC,
@@ -71,6 +75,8 @@ enum nfc_commands {
        NFC_EVENT_DEVICE_ADDED,
        NFC_EVENT_DEVICE_REMOVED,
        NFC_EVENT_TARGET_LOST,
+       NFC_EVENT_TM_ACTIVATED,
+       NFC_EVENT_TM_DEACTIVATED,
 /* private: internal use only */
        __NFC_CMD_AFTER_LAST
 };
@@ -94,6 +100,8 @@ enum nfc_commands {
  * @NFC_ATTR_TARGET_SENSF_RES: NFC-F targets extra information, max 18 bytes
  * @NFC_ATTR_COMM_MODE: Passive or active mode
  * @NFC_ATTR_RF_MODE: Initiator or target
+ * @NFC_ATTR_IM_PROTOCOLS: Initiator mode protocols to poll for
+ * @NFC_ATTR_TM_PROTOCOLS: Target mode protocols to listen for
  */
 enum nfc_attrs {
        NFC_ATTR_UNSPEC,
@@ -109,6 +117,8 @@ enum nfc_attrs {
        NFC_ATTR_COMM_MODE,
        NFC_ATTR_RF_MODE,
        NFC_ATTR_DEVICE_POWERED,
+       NFC_ATTR_IM_PROTOCOLS,
+       NFC_ATTR_TM_PROTOCOLS,
 /* private: internal use only */
        __NFC_ATTR_AFTER_LAST
 };
@@ -118,6 +128,7 @@ enum nfc_attrs {
 #define NFC_NFCID1_MAXSIZE 10
 #define NFC_SENSB_RES_MAXSIZE 12
 #define NFC_SENSF_RES_MAXSIZE 18
+#define NFC_GB_MAXSIZE        48
 
 /* NFC protocols */
 #define NFC_PROTO_JEWEL                1
@@ -135,6 +146,7 @@ enum nfc_attrs {
 /* NFC RF modes */
 #define NFC_RF_INITIATOR 0
 #define NFC_RF_TARGET    1
+#define NFC_RF_NONE      2
 
 /* NFC protocols masks used in bitsets */
 #define NFC_PROTO_JEWEL_MASK   (1 << NFC_PROTO_JEWEL)
index 85e5037..970afdf 100644 (file)
@@ -1522,6 +1522,8 @@ enum nl80211_attrs {
 #define NL80211_MAX_NR_CIPHER_SUITES           5
 #define NL80211_MAX_NR_AKM_SUITES              2
 
+#define NL80211_MIN_REMAIN_ON_CHANNEL_TIME     10
+
 /**
  * enum nl80211_iftype - (virtual) interface types
  *
index bc14bd7..bb674c0 100644 (file)
@@ -243,6 +243,7 @@ struct ssb_bus_ops {
 #define SSB_DEV_MINI_MACPHY    0x823
 #define SSB_DEV_ARM_1176       0x824
 #define SSB_DEV_ARM_7TDMI      0x825
+#define SSB_DEV_ARM_CM3                0x82A
 
 /* Vendor-ID values */
 #define SSB_VENDOR_BROADCOM    0x4243
index 4c90c44..7319f25 100644 (file)
@@ -1420,11 +1420,14 @@ struct cfg80211_gtk_rekey_data {
  *
  * @set_txq_params: Set TX queue parameters
  *
- * @set_channel: Set channel for a given wireless interface. Some devices
- *     may support multi-channel operation (by channel hopping) so cfg80211
- *     doesn't verify much. Note, however, that the passed netdev may be
- *     %NULL as well if the user requested changing the channel for the
- *     device itself, or for a monitor interface.
+ * @libertas_set_mesh_channel: Only for backward compatibility for libertas,
+ *     as it doesn't implement join_mesh and needs to set the channel to
+ *     join the mesh instead.
+ *
+ * @set_monitor_channel: Set the monitor mode channel for the device. If other
+ *     interfaces are active this callback should reject the configuration.
+ *     If no interfaces are active or the device is down, the channel should
+ *     be stored for when a monitor interface becomes active.
  * @get_channel: Get the current operating channel, should return %NULL if
  *     there's no single defined operating channel if for example the
  *     device implements channel hopping for multi-channel virtual interfaces.
@@ -1614,9 +1617,13 @@ struct cfg80211_ops {
        int     (*set_txq_params)(struct wiphy *wiphy, struct net_device *dev,
                                  struct ieee80211_txq_params *params);
 
-       int     (*set_channel)(struct wiphy *wiphy, struct net_device *dev,
-                              struct ieee80211_channel *chan,
-                              enum nl80211_channel_type channel_type);
+       int     (*libertas_set_mesh_channel)(struct wiphy *wiphy,
+                                            struct net_device *dev,
+                                            struct ieee80211_channel *chan);
+
+       int     (*set_monitor_channel)(struct wiphy *wiphy,
+                                      struct ieee80211_channel *chan,
+                                      enum nl80211_channel_type channel_type);
 
        int     (*scan)(struct wiphy *wiphy, struct net_device *dev,
                        struct cfg80211_scan_request *request);
@@ -2325,7 +2332,6 @@ struct wireless_dev {
        spinlock_t event_lock;
 
        struct cfg80211_internal_bss *current_bss; /* associated / joined */
-       struct ieee80211_channel *channel;
        struct ieee80211_channel *preset_chan;
        enum nl80211_channel_type preset_chantype;
 
index d2ed86d..d152f54 100644 (file)
@@ -2183,7 +2183,10 @@ enum ieee80211_rate_control_changed {
  *     offload. Frames to transmit on the off-channel channel are transmitted
  *     normally except for the %IEEE80211_TX_CTL_TX_OFFCHAN flag. When the
  *     duration (which will always be non-zero) expires, the driver must call
- *     ieee80211_remain_on_channel_expired(). This callback may sleep.
+ *     ieee80211_remain_on_channel_expired().
+ *     Note that this callback may be called while the device is in IDLE and
+ *     must be accepted in this case.
+ *     This callback may sleep.
  * @cancel_remain_on_channel: Requests that an ongoing off-channel period is
  *     aborted before it expires. This callback may sleep.
  *
index 4467c94..e30e6a8 100644 (file)
@@ -31,7 +31,8 @@ struct nfc_hci_ops {
        void (*close) (struct nfc_hci_dev *hdev);
        int (*hci_ready) (struct nfc_hci_dev *hdev);
        int (*xmit) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
-       int (*start_poll) (struct nfc_hci_dev *hdev, u32 protocols);
+       int (*start_poll) (struct nfc_hci_dev *hdev,
+                          u32 im_protocols, u32 tm_protocols);
        int (*target_from_gate) (struct nfc_hci_dev *hdev, u8 gate,
                                 struct nfc_target *target);
        int (*complete_target_discovered) (struct nfc_hci_dev *hdev, u8 gate,
index b7ca4a2..180964b 100644 (file)
@@ -53,7 +53,8 @@ struct nfc_target;
 struct nfc_ops {
        int (*dev_up)(struct nfc_dev *dev);
        int (*dev_down)(struct nfc_dev *dev);
-       int (*start_poll)(struct nfc_dev *dev, u32 protocols);
+       int (*start_poll)(struct nfc_dev *dev,
+                         u32 im_protocols, u32 tm_protocols);
        void (*stop_poll)(struct nfc_dev *dev);
        int (*dep_link_up)(struct nfc_dev *dev, struct nfc_target *target,
                           u8 comm_mode, u8 *gb, size_t gb_len);
@@ -62,9 +63,10 @@ struct nfc_ops {
                               u32 protocol);
        void (*deactivate_target)(struct nfc_dev *dev,
                                  struct nfc_target *target);
-       int (*data_exchange)(struct nfc_dev *dev, struct nfc_target *target,
+       int (*im_transceive)(struct nfc_dev *dev, struct nfc_target *target,
                             struct sk_buff *skb, data_exchange_cb_t cb,
                             void *cb_context);
+       int (*tm_send)(struct nfc_dev *dev, struct sk_buff *skb);
        int (*check_presence)(struct nfc_dev *dev, struct nfc_target *target);
 };
 
@@ -99,10 +101,10 @@ struct nfc_dev {
        int targets_generation;
        struct device dev;
        bool dev_up;
+       u8 rf_mode;
        bool polling;
        struct nfc_target *active_target;
        bool dep_link_up;
-       u32 dep_rf_mode;
        struct nfc_genl_data genl_data;
        u32 supported_protocols;
 
@@ -188,6 +190,7 @@ struct sk_buff *nfc_alloc_recv_skb(unsigned int size, gfp_t gfp);
 
 int nfc_set_remote_general_bytes(struct nfc_dev *dev,
                                 u8 *gt, u8 gt_len);
+u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, size_t *gb_len);
 
 int nfc_targets_found(struct nfc_dev *dev,
                      struct nfc_target *targets, int ntargets);
@@ -196,4 +199,9 @@ int nfc_target_lost(struct nfc_dev *dev, u32 target_idx);
 int nfc_dep_link_is_up(struct nfc_dev *dev, u32 target_idx,
                       u8 comm_mode, u8 rf_mode);
 
+int nfc_tm_activated(struct nfc_dev *dev, u32 protocol, u8 comm_mode,
+                    u8 *gb, size_t gb_len);
+int nfc_tm_deactivated(struct nfc_dev *dev);
+int nfc_tm_data_received(struct nfc_dev *dev, struct sk_buff *skb);
+
 #endif /* __NET_NFC_H */
index ab06afd..35e930d 100644 (file)
@@ -27,7 +27,8 @@ struct nfc_shdlc_ops {
        void (*close) (struct nfc_shdlc *shdlc);
        int (*hci_ready) (struct nfc_shdlc *shdlc);
        int (*xmit) (struct nfc_shdlc *shdlc, struct sk_buff *skb);
-       int (*start_poll) (struct nfc_shdlc *shdlc, u32 protocols);
+       int (*start_poll) (struct nfc_shdlc *shdlc,
+                          u32 im_protocols, u32 tm_protocols);
        int (*target_from_gate) (struct nfc_shdlc *shdlc, u8 gate,
                                 struct nfc_target *target);
        int (*complete_target_discovered) (struct nfc_shdlc *shdlc, u8 gate,
index 46e7f86..3e18af4 100644 (file)
@@ -210,7 +210,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
                }
 
                if (sk->sk_state == BT_CONNECTED || !newsock ||
-                   test_bit(BT_DEFER_SETUP, &bt_sk(parent)->flags)) {
+                   test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags)) {
                        bt_accept_unlink(sk);
                        if (newsock)
                                sock_graft(sk, newsock);
index 3e9d931..2b1470b 100644 (file)
@@ -9,7 +9,6 @@ mac80211-y := \
        scan.o offchannel.o \
        ht.o agg-tx.o agg-rx.o \
        ibss.o \
-       work.o \
        iface.o \
        rate.o \
        michael.o \
index a096b0d..32ef11d 100644 (file)
@@ -142,15 +142,20 @@ static void sta_rx_agg_session_timer_expired(unsigned long data)
        struct tid_ampdu_rx *tid_rx;
        unsigned long timeout;
 
+       rcu_read_lock();
        tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[*ptid]);
-       if (!tid_rx)
+       if (!tid_rx) {
+               rcu_read_unlock();
                return;
+       }
 
        timeout = tid_rx->last_rx + TU_TO_JIFFIES(tid_rx->timeout);
        if (time_is_after_jiffies(timeout)) {
                mod_timer(&tid_rx->session_timer, timeout);
+               rcu_read_unlock();
                return;
        }
+       rcu_read_unlock();
 
        ht_vdbg("rx session timer expired on tid %d\n", (u16)*ptid);
 
index f47af8b..498c94e 100644 (file)
@@ -533,16 +533,16 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy,
                sinfo.filled = 0;
                sta_set_sinfo(sta, &sinfo);
 
-               if (sinfo.filled | STATION_INFO_TX_BITRATE)
+               if (sinfo.filled & STATION_INFO_TX_BITRATE)
                        data[i] = 100000 *
                                cfg80211_calculate_bitrate(&sinfo.txrate);
                i++;
-               if (sinfo.filled | STATION_INFO_RX_BITRATE)
+               if (sinfo.filled & STATION_INFO_RX_BITRATE)
                        data[i] = 100000 *
                                cfg80211_calculate_bitrate(&sinfo.rxrate);
                i++;
 
-               if (sinfo.filled | STATION_INFO_SIGNAL_AVG)
+               if (sinfo.filled & STATION_INFO_SIGNAL_AVG)
                        data[i] = (u8)sinfo.signal_avg;
                i++;
        } else {
@@ -709,6 +709,13 @@ static int ieee80211_set_channel(struct wiphy *wiphy,
        return 0;
 }
 
+static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
+                                        struct ieee80211_channel *chan,
+                                        enum nl80211_channel_type channel_type)
+{
+       return ieee80211_set_channel(wiphy, NULL, chan, channel_type);
+}
+
 static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
                                    const u8 *resp, size_t resp_len)
 {
@@ -2105,35 +2112,171 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
        return 0;
 }
 
-static int ieee80211_remain_on_channel_hw(struct ieee80211_local *local,
-                                         struct net_device *dev,
-                                         struct ieee80211_channel *chan,
-                                         enum nl80211_channel_type chantype,
-                                         unsigned int duration, u64 *cookie)
+static int ieee80211_start_roc_work(struct ieee80211_local *local,
+                                   struct ieee80211_sub_if_data *sdata,
+                                   struct ieee80211_channel *channel,
+                                   enum nl80211_channel_type channel_type,
+                                   unsigned int duration, u64 *cookie,
+                                   struct sk_buff *txskb)
 {
+       struct ieee80211_roc_work *roc, *tmp;
+       bool queued = false;
        int ret;
-       u32 random_cookie;
 
        lockdep_assert_held(&local->mtx);
 
-       if (local->hw_roc_cookie)
-               return -EBUSY;
-       /* must be nonzero */
-       random_cookie = random32() | 1;
-
-       *cookie = random_cookie;
-       local->hw_roc_dev = dev;
-       local->hw_roc_cookie = random_cookie;
-       local->hw_roc_channel = chan;
-       local->hw_roc_channel_type = chantype;
-       local->hw_roc_duration = duration;
-       ret = drv_remain_on_channel(local, chan, chantype, duration);
+       roc = kzalloc(sizeof(*roc), GFP_KERNEL);
+       if (!roc)
+               return -ENOMEM;
+
+       roc->chan = channel;
+       roc->chan_type = channel_type;
+       roc->duration = duration;
+       roc->req_duration = duration;
+       roc->frame = txskb;
+       roc->mgmt_tx_cookie = (unsigned long)txskb;
+       roc->sdata = sdata;
+       INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work);
+       INIT_LIST_HEAD(&roc->dependents);
+
+       /* if there's one pending or we're scanning, queue this one */
+       if (!list_empty(&local->roc_list) || local->scanning)
+               goto out_check_combine;
+
+       /* if not HW assist, just queue & schedule work */
+       if (!local->ops->remain_on_channel) {
+               ieee80211_queue_delayed_work(&local->hw, &roc->work, 0);
+               goto out_queue;
+       }
+
+       /* otherwise actually kick it off here (for error handling) */
+
+       /*
+        * If the duration is zero, then the driver
+        * wouldn't actually do anything. Set it to
+        * 10 for now.
+        *
+        * TODO: cancel the off-channel operation
+        *       when we get the SKB's TX status and
+        *       the wait time was zero before.
+        */
+       if (!duration)
+               duration = 10;
+
+       ret = drv_remain_on_channel(local, channel, channel_type, duration);
        if (ret) {
-               local->hw_roc_channel = NULL;
-               local->hw_roc_cookie = 0;
+               kfree(roc);
+               return ret;
        }
 
-       return ret;
+       roc->started = true;
+       goto out_queue;
+
+ out_check_combine:
+       list_for_each_entry(tmp, &local->roc_list, list) {
+               if (tmp->chan != channel || tmp->chan_type != channel_type)
+                       continue;
+
+               /*
+                * Extend this ROC if possible:
+                *
+                * If it hasn't started yet, just increase the duration
+                * and add the new one to the list of dependents.
+                */
+               if (!tmp->started) {
+                       list_add_tail(&roc->list, &tmp->dependents);
+                       tmp->duration = max(tmp->duration, roc->duration);
+                       queued = true;
+                       break;
+               }
+
+               /* If it has already started, it's more difficult ... */
+               if (local->ops->remain_on_channel) {
+                       unsigned long j = jiffies;
+
+                       /*
+                        * In the offloaded ROC case, if it hasn't begun, add
+                        * this new one to the dependent list to be handled
+                        * when the the master one begins. If it has begun,
+                        * check that there's still a minimum time left and
+                        * if so, start this one, transmitting the frame, but
+                        * add it to the list directly after this one with a
+                        * a reduced time so we'll ask the driver to execute
+                        * it right after finishing the previous one, in the
+                        * hope that it'll also be executed right afterwards,
+                        * effectively extending the old one.
+                        * If there's no minimum time left, just add it to the
+                        * normal list.
+                        */
+                       if (!tmp->hw_begun) {
+                               list_add_tail(&roc->list, &tmp->dependents);
+                               queued = true;
+                               break;
+                       }
+
+                       if (time_before(j + IEEE80211_ROC_MIN_LEFT,
+                                       tmp->hw_start_time +
+                                       msecs_to_jiffies(tmp->duration))) {
+                               int new_dur;
+
+                               ieee80211_handle_roc_started(roc);
+
+                               new_dur = roc->duration -
+                                         jiffies_to_msecs(tmp->hw_start_time +
+                                                          msecs_to_jiffies(
+                                                               tmp->duration) -
+                                                          j);
+
+                               if (new_dur > 0) {
+                                       /* add right after tmp */
+                                       list_add(&roc->list, &tmp->list);
+                               } else {
+                                       list_add_tail(&roc->list,
+                                                     &tmp->dependents);
+                               }
+                               queued = true;
+                       }
+               } else if (del_timer_sync(&tmp->work.timer)) {
+                       unsigned long new_end;
+
+                       /*
+                        * In the software ROC case, cancel the timer, if
+                        * that fails then the finish work is already
+                        * queued/pending and thus we queue the new ROC
+                        * normally, if that succeeds then we can extend
+                        * the timer duration and TX the frame (if any.)
+                        */
+
+                       list_add_tail(&roc->list, &tmp->dependents);
+                       queued = true;
+
+                       new_end = jiffies + msecs_to_jiffies(roc->duration);
+
+                       /* ok, it was started & we canceled timer */
+                       if (time_after(new_end, tmp->work.timer.expires))
+                               mod_timer(&tmp->work.timer, new_end);
+                       else
+                               add_timer(&tmp->work.timer);
+
+                       ieee80211_handle_roc_started(roc);
+               }
+               break;
+       }
+
+ out_queue:
+       if (!queued)
+               list_add_tail(&roc->list, &local->roc_list);
+
+       /*
+        * cookie is either the roc (for normal roc)
+        * or the SKB (for mgmt TX)
+        */
+       if (txskb)
+               *cookie = (unsigned long)txskb;
+       else
+               *cookie = (unsigned long)roc;
+
+       return 0;
 }
 
 static int ieee80211_remain_on_channel(struct wiphy *wiphy,
@@ -2145,42 +2288,64 @@ static int ieee80211_remain_on_channel(struct wiphy *wiphy,
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
+       int ret;
 
-       if (local->ops->remain_on_channel) {
-               int ret;
-
-               mutex_lock(&local->mtx);
-               ret = ieee80211_remain_on_channel_hw(local, dev,
-                                                    chan, channel_type,
-                                                    duration, cookie);
-               local->hw_roc_for_tx = false;
-               mutex_unlock(&local->mtx);
-
-               return ret;
-       }
+       mutex_lock(&local->mtx);
+       ret = ieee80211_start_roc_work(local, sdata, chan, channel_type,
+                                      duration, cookie, NULL);
+       mutex_unlock(&local->mtx);
 
-       return ieee80211_wk_remain_on_channel(sdata, chan, channel_type,
-                                             duration, cookie);
+       return ret;
 }
 
-static int ieee80211_cancel_remain_on_channel_hw(struct ieee80211_local *local,
-                                                u64 cookie)
+static int ieee80211_cancel_roc(struct ieee80211_local *local,
+                               u64 cookie, bool mgmt_tx)
 {
+       struct ieee80211_roc_work *roc, *tmp, *found = NULL;
        int ret;
 
-       lockdep_assert_held(&local->mtx);
+       mutex_lock(&local->mtx);
+       list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
+               if (!mgmt_tx && (unsigned long)roc != cookie)
+                       continue;
+               else if (mgmt_tx && roc->mgmt_tx_cookie != cookie)
+                       continue;
+
+               found = roc;
+               break;
+       }
 
-       if (local->hw_roc_cookie != cookie)
+       if (!found) {
+               mutex_unlock(&local->mtx);
                return -ENOENT;
+       }
 
-       ret = drv_cancel_remain_on_channel(local);
-       if (ret)
-               return ret;
+       if (local->ops->remain_on_channel) {
+               if (found->started) {
+                       ret = drv_cancel_remain_on_channel(local);
+                       if (WARN_ON_ONCE(ret)) {
+                               mutex_unlock(&local->mtx);
+                               return ret;
+                       }
+               }
+
+               list_del(&found->list);
+
+               ieee80211_run_deferred_scan(local);
+               ieee80211_start_next_roc(local);
+               mutex_unlock(&local->mtx);
 
-       local->hw_roc_cookie = 0;
-       local->hw_roc_channel = NULL;
+               ieee80211_roc_notify_destroy(found);
+       } else {
+               /* work may be pending so use it all the time */
+               found->abort = true;
+               ieee80211_queue_delayed_work(&local->hw, &found->work, 0);
+
+               mutex_unlock(&local->mtx);
 
-       ieee80211_recalc_idle(local);
+               /* work will clean up etc */
+               flush_delayed_work(&found->work);
+       }
 
        return 0;
 }
@@ -2192,39 +2357,7 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
 
-       if (local->ops->cancel_remain_on_channel) {
-               int ret;
-
-               mutex_lock(&local->mtx);
-               ret = ieee80211_cancel_remain_on_channel_hw(local, cookie);
-               mutex_unlock(&local->mtx);
-
-               return ret;
-       }
-
-       return ieee80211_wk_cancel_remain_on_channel(sdata, cookie);
-}
-
-static enum work_done_result
-ieee80211_offchan_tx_done(struct ieee80211_work *wk, struct sk_buff *skb)
-{
-       /*
-        * Use the data embedded in the work struct for reporting
-        * here so if the driver mangled the SKB before dropping
-        * it (which is the only way we really should get here)
-        * then we don't report mangled data.
-        *
-        * If there was no wait time, then by the time we get here
-        * the driver will likely not have reported the status yet,
-        * so in that case userspace will have to deal with it.
-        */
-
-       if (wk->offchan_tx.wait && !wk->offchan_tx.status)
-               cfg80211_mgmt_tx_status(wk->sdata->dev,
-                                       (unsigned long) wk->offchan_tx.frame,
-                                       wk->data, wk->data_len, false, GFP_KERNEL);
-
-       return WORK_DONE_DESTROY;
+       return ieee80211_cancel_roc(local, cookie, false);
 }
 
 static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
@@ -2238,10 +2371,10 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_local *local = sdata->local;
        struct sk_buff *skb;
        struct sta_info *sta;
-       struct ieee80211_work *wk;
        const struct ieee80211_mgmt *mgmt = (void *)buf;
+       bool need_offchan = false;
        u32 flags;
-       bool is_offchan = false;
+       int ret;
 
        if (dont_wait_for_ack)
                flags = IEEE80211_TX_CTL_NO_ACK;
@@ -2249,33 +2382,28 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
                flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX |
                        IEEE80211_TX_CTL_REQ_TX_STATUS;
 
-       /* Check that we are on the requested channel for transmission */
-       if (chan != local->tmp_channel &&
-           chan != local->oper_channel)
-               is_offchan = true;
-       if (channel_type_valid &&
-           (channel_type != local->tmp_channel_type &&
-            channel_type != local->_oper_channel_type))
-               is_offchan = true;
-
-       if (chan == local->hw_roc_channel) {
-               /* TODO: check channel type? */
-               is_offchan = false;
-               flags |= IEEE80211_TX_CTL_TX_OFFCHAN;
-       }
-
        if (no_cck)
                flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
 
-       if (is_offchan && !offchan)
-               return -EBUSY;
-
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_ADHOC:
+               if (!sdata->vif.bss_conf.ibss_joined)
+                       need_offchan = true;
+               /* fall through */
+#ifdef CONFIG_MAC80211_MESH
+       case NL80211_IFTYPE_MESH_POINT:
+               if (ieee80211_vif_is_mesh(&sdata->vif) &&
+                   !sdata->u.mesh.mesh_id_len)
+                       need_offchan = true;
+               /* fall through */
+#endif
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_AP_VLAN:
        case NL80211_IFTYPE_P2P_GO:
-       case NL80211_IFTYPE_MESH_POINT:
+               if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+                   !ieee80211_vif_is_mesh(&sdata->vif) &&
+                   !rcu_access_pointer(sdata->bss->beacon))
+                       need_offchan = true;
                if (!ieee80211_is_action(mgmt->frame_control) ||
                    mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)
                        break;
@@ -2287,105 +2415,60 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
                break;
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_CLIENT:
+               if (!sdata->u.mgd.associated)
+                       need_offchan = true;
                break;
        default:
                return -EOPNOTSUPP;
        }
 
+       mutex_lock(&local->mtx);
+
+       /* Check if the operating channel is the requested channel */
+       if (!need_offchan) {
+               need_offchan = chan != local->oper_channel;
+               if (channel_type_valid &&
+                   channel_type != local->_oper_channel_type)
+                       need_offchan = true;
+       }
+
+       if (need_offchan && !offchan) {
+               ret = -EBUSY;
+               goto out_unlock;
+       }
+
        skb = dev_alloc_skb(local->hw.extra_tx_headroom + len);
-       if (!skb)
-               return -ENOMEM;
+       if (!skb) {
+               ret = -ENOMEM;
+               goto out_unlock;
+       }
        skb_reserve(skb, local->hw.extra_tx_headroom);
 
        memcpy(skb_put(skb, len), buf, len);
 
        IEEE80211_SKB_CB(skb)->flags = flags;
 
-       if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL &&
-           flags & IEEE80211_TX_CTL_TX_OFFCHAN)
-               IEEE80211_SKB_CB(skb)->hw_queue =
-                       local->hw.offchannel_tx_hw_queue;
-
        skb->dev = sdata->dev;
 
-       *cookie = (unsigned long) skb;
-
-       if (is_offchan && local->ops->remain_on_channel) {
-               unsigned int duration;
-               int ret;
-
-               mutex_lock(&local->mtx);
-               /*
-                * If the duration is zero, then the driver
-                * wouldn't actually do anything. Set it to
-                * 100 for now.
-                *
-                * TODO: cancel the off-channel operation
-                *       when we get the SKB's TX status and
-                *       the wait time was zero before.
-                */
-               duration = 100;
-               if (wait)
-                       duration = wait;
-               ret = ieee80211_remain_on_channel_hw(local, dev, chan,
-                                                    channel_type,
-                                                    duration, cookie);
-               if (ret) {
-                       kfree_skb(skb);
-                       mutex_unlock(&local->mtx);
-                       return ret;
-               }
-
-               local->hw_roc_for_tx = true;
-               local->hw_roc_duration = wait;
-
-               /*
-                * queue up frame for transmission after
-                * ieee80211_ready_on_channel call
-                */
-
-               /* modify cookie to prevent API mismatches */
-               *cookie ^= 2;
-               IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN;
-               if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)
-                       IEEE80211_SKB_CB(skb)->hw_queue =
-                               local->hw.offchannel_tx_hw_queue;
-               local->hw_roc_skb = skb;
-               local->hw_roc_skb_for_status = skb;
-               mutex_unlock(&local->mtx);
-
-               return 0;
-       }
-
-       /*
-        * Can transmit right away if the channel was the
-        * right one and there's no wait involved... If a
-        * wait is involved, we might otherwise not be on
-        * the right channel for long enough!
-        */
-       if (!is_offchan && !wait && !sdata->vif.bss_conf.idle) {
+       if (!need_offchan) {
                ieee80211_tx_skb(sdata, skb);
-               return 0;
-       }
-
-       wk = kzalloc(sizeof(*wk) + len, GFP_KERNEL);
-       if (!wk) {
-               kfree_skb(skb);
-               return -ENOMEM;
+               ret = 0;
+               goto out_unlock;
        }
 
-       wk->type = IEEE80211_WORK_OFFCHANNEL_TX;
-       wk->chan = chan;
-       wk->chan_type = channel_type;
-       wk->sdata = sdata;
-       wk->done = ieee80211_offchan_tx_done;
-       wk->offchan_tx.frame = skb;
-       wk->offchan_tx.wait = wait;
-       wk->data_len = len;
-       memcpy(wk->data, buf, len);
+       IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN;
+       if (local->hw.flags & IEEE80211_HW_QUEUE_CONTROL)
+               IEEE80211_SKB_CB(skb)->hw_queue =
+                       local->hw.offchannel_tx_hw_queue;
 
-       ieee80211_add_work(wk);
-       return 0;
+       /* This will handle all kinds of coalescing and immediate TX */
+       ret = ieee80211_start_roc_work(local, sdata, chan, channel_type,
+                                      wait, cookie, skb);
+       if (ret)
+               kfree_skb(skb);
+ out_unlock:
+       mutex_unlock(&local->mtx);
+       return ret;
 }
 
 static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
@@ -2394,45 +2477,8 @@ static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
-       struct ieee80211_work *wk;
-       int ret = -ENOENT;
 
-       mutex_lock(&local->mtx);
-
-       if (local->ops->cancel_remain_on_channel) {
-               cookie ^= 2;
-               ret = ieee80211_cancel_remain_on_channel_hw(local, cookie);
-
-               if (ret == 0) {
-                       kfree_skb(local->hw_roc_skb);
-                       local->hw_roc_skb = NULL;
-                       local->hw_roc_skb_for_status = NULL;
-               }
-
-               mutex_unlock(&local->mtx);
-
-               return ret;
-       }
-
-       list_for_each_entry(wk, &local->work_list, list) {
-               if (wk->sdata != sdata)
-                       continue;
-
-               if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX)
-                       continue;
-
-               if (cookie != (unsigned long) wk->offchan_tx.frame)
-                       continue;
-
-               wk->timeout = jiffies;
-
-               ieee80211_queue_work(&local->hw, &local->work_work);
-               ret = 0;
-               break;
-       }
-       mutex_unlock(&local->mtx);
-
-       return ret;
+       return ieee80211_cancel_roc(local, cookie, true);
 }
 
 static void ieee80211_mgmt_frame_register(struct wiphy *wiphy,
@@ -2932,7 +2978,7 @@ struct cfg80211_ops mac80211_config_ops = {
 #endif
        .change_bss = ieee80211_change_bss,
        .set_txq_params = ieee80211_set_txq_params,
-       .set_channel = ieee80211_set_channel,
+       .set_monitor_channel = ieee80211_set_monitor_channel,
        .suspend = ieee80211_suspend,
        .resume = ieee80211_resume,
        .scan = ieee80211_scan,
index 1a3b361..d4272ff 100644 (file)
@@ -607,6 +607,7 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata)
        MESHPARAMS_ADD(min_discovery_timeout);
        MESHPARAMS_ADD(dot11MeshHWMPRootMode);
        MESHPARAMS_ADD(dot11MeshHWMPRannInterval);
+       MESHPARAMS_ADD(dot11MeshForwarding);
        MESHPARAMS_ADD(dot11MeshGateAnnouncementProtocol);
        MESHPARAMS_ADD(rssi_threshold);
        MESHPARAMS_ADD(ht_opmode);
index 8c026ab..e6cbf5b 100644 (file)
@@ -317,55 +317,30 @@ struct mesh_preq_queue {
        u8 flags;
 };
 
-enum ieee80211_work_type {
-       IEEE80211_WORK_ABORT,
-       IEEE80211_WORK_REMAIN_ON_CHANNEL,
-       IEEE80211_WORK_OFFCHANNEL_TX,
-};
-
-/**
- * enum work_done_result - indicates what to do after work was done
- *
- * @WORK_DONE_DESTROY: This work item is no longer needed, destroy.
- * @WORK_DONE_REQUEUE: This work item was reset to be reused, and
- *     should be requeued.
- */
-enum work_done_result {
-       WORK_DONE_DESTROY,
-       WORK_DONE_REQUEUE,
-};
+#if HZ/100 == 0
+#define IEEE80211_ROC_MIN_LEFT 1
+#else
+#define IEEE80211_ROC_MIN_LEFT (HZ/100)
+#endif
 
-struct ieee80211_work {
+struct ieee80211_roc_work {
        struct list_head list;
+       struct list_head dependents;
 
-       struct rcu_head rcu_head;
+       struct delayed_work work;
 
        struct ieee80211_sub_if_data *sdata;
 
-       enum work_done_result (*done)(struct ieee80211_work *wk,
-                                     struct sk_buff *skb);
-
        struct ieee80211_channel *chan;
        enum nl80211_channel_type chan_type;
 
-       unsigned long timeout;
-       enum ieee80211_work_type type;
+       bool started, abort, hw_begun, notified;
 
-       bool started;
+       unsigned long hw_start_time;
 
-       union {
-               struct {
-                       u32 duration;
-               } remain;
-               struct {
-                       struct sk_buff *frame;
-                       u32 wait;
-                       bool status;
-               } offchan_tx;
-       };
-
-       size_t data_len;
-       u8 data[];
+       u32 duration, req_duration;
+       struct sk_buff *frame;
+       u64 mgmt_tx_cookie;
 };
 
 /* flags used in struct ieee80211_if_managed.flags */
@@ -848,13 +823,6 @@ struct ieee80211_local {
        const struct ieee80211_ops *ops;
 
        /*
-        * work stuff, potentially off-channel (in the future)
-        */
-       struct list_head work_list;
-       struct timer_list work_timer;
-       struct work_struct work_work;
-
-       /*
         * private workqueue to mac80211. mac80211 makes this accessible
         * via ieee80211_queue_work()
         */
@@ -1088,14 +1056,12 @@ struct ieee80211_local {
        } debugfs;
 #endif
 
-       struct ieee80211_channel *hw_roc_channel;
-       struct net_device *hw_roc_dev;
-       struct sk_buff *hw_roc_skb, *hw_roc_skb_for_status;
+       /*
+        * Remain-on-channel support
+        */
+       struct list_head roc_list;
        struct work_struct hw_roc_start, hw_roc_done;
-       enum nl80211_channel_type hw_roc_channel_type;
-       unsigned int hw_roc_duration;
-       u32 hw_roc_cookie;
-       bool hw_roc_for_tx;
+       unsigned long hw_roc_start_time;
 
        struct idr ack_status_frames;
        spinlock_t ack_status_lock;
@@ -1291,7 +1257,12 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local,
                                    bool offchannel_ps_enable);
 void ieee80211_offchannel_return(struct ieee80211_local *local,
                                 bool offchannel_ps_disable);
-void ieee80211_hw_roc_setup(struct ieee80211_local *local);
+void ieee80211_roc_setup(struct ieee80211_local *local);
+void ieee80211_start_next_roc(struct ieee80211_local *local);
+void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata);
+void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc);
+void ieee80211_sw_roc_work(struct work_struct *work);
+void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);
 
 /* interface handling */
 int ieee80211_iface_init(void);
@@ -1501,18 +1472,6 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
                               enum nl80211_channel_type channel_type,
                               u16 prot_mode);
 
-/* internal work items */
-void ieee80211_work_init(struct ieee80211_local *local);
-void ieee80211_add_work(struct ieee80211_work *wk);
-void free_work(struct ieee80211_work *wk);
-void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata);
-int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata,
-                                  struct ieee80211_channel *chan,
-                                  enum nl80211_channel_type channel_type,
-                                  unsigned int duration, u64 *cookie);
-int ieee80211_wk_cancel_remain_on_channel(
-       struct ieee80211_sub_if_data *sdata, u64 cookie);
-
 /* channel management */
 enum ieee80211_chan_mode {
        CHAN_MODE_UNDEFINED,
index bebc45d..87aeb4f 100644 (file)
@@ -528,10 +528,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
         */
        netif_tx_stop_all_queues(sdata->dev);
 
-       /*
-        * Purge work for this interface.
-        */
-       ieee80211_work_purge(sdata);
+       ieee80211_roc_purge(sdata);
 
        /*
         * Remove all stations associated with this interface.
@@ -1444,9 +1441,9 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
 {
        struct ieee80211_sub_if_data *sdata;
        int count = 0;
-       bool working = false, scanning = false, hw_roc = false;
-       struct ieee80211_work *wk;
+       bool working = false, scanning = false;
        unsigned int led_trig_start = 0, led_trig_stop = 0;
+       struct ieee80211_roc_work *roc;
 
 #ifdef CONFIG_PROVE_LOCKING
        WARN_ON(debug_locks && !lockdep_rtnl_is_held() &&
@@ -1481,9 +1478,11 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
                count++;
        }
 
-       list_for_each_entry(wk, &local->work_list, list) {
-               working = true;
-               wk->sdata->vif.bss_conf.idle = false;
+       if (!local->ops->remain_on_channel) {
+               list_for_each_entry(roc, &local->roc_list, list) {
+                       working = true;
+                       roc->sdata->vif.bss_conf.idle = false;
+               }
        }
 
        if (local->scan_sdata &&
@@ -1492,9 +1491,6 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
                local->scan_sdata->vif.bss_conf.idle = false;
        }
 
-       if (local->hw_roc_channel)
-               hw_roc = true;
-
        list_for_each_entry(sdata, &local->interfaces, list) {
                if (sdata->vif.type == NL80211_IFTYPE_MONITOR ||
                    sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
@@ -1506,7 +1502,7 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
                ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
        }
 
-       if (working || scanning || hw_roc)
+       if (working || scanning)
                led_trig_start |= IEEE80211_TPT_LEDTRIG_FL_WORK;
        else
                led_trig_stop |= IEEE80211_TPT_LEDTRIG_FL_WORK;
@@ -1518,8 +1514,6 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
 
        ieee80211_mod_tpt_led_trig(local, led_trig_start, led_trig_stop);
 
-       if (hw_roc)
-               return ieee80211_idle_off(local, "hw remain-on-channel");
        if (working)
                return ieee80211_idle_off(local, "working");
        if (scanning)
index 779ac61..d81c178 100644 (file)
@@ -625,8 +625,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
 
        INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work);
 
-       ieee80211_work_init(local);
-
        INIT_WORK(&local->restart_work, ieee80211_restart_work);
 
        INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter);
@@ -669,7 +667,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
 
        ieee80211_led_names(local);
 
-       ieee80211_hw_roc_setup(local);
+       ieee80211_roc_setup(local);
 
        return &local->hw;
 }
@@ -1016,12 +1014,6 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
 
        rtnl_unlock();
 
-       /*
-        * Now all work items will be gone, but the
-        * timer might still be armed, so delete it
-        */
-       del_timer_sync(&local->work_timer);
-
        cancel_work_sync(&local->restart_work);
        cancel_work_sync(&local->reconfig_filter);
 
index 7c07715..0f45d02 100644 (file)
@@ -930,11 +930,6 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
                return;
        }
 
-       if (!list_empty(&local->work_list)) {
-               local->ps_sdata = NULL;
-               goto change;
-       }
-
        list_for_each_entry(sdata, &local->interfaces, list) {
                if (!ieee80211_sdata_running(sdata))
                        continue;
@@ -1007,7 +1002,6 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
                local->ps_sdata = NULL;
        }
 
- change:
        ieee80211_change_ps(local);
 }
 
@@ -1211,6 +1205,22 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
        sdata->vif.bss_conf.qos = true;
 }
 
+static void __ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata)
+{
+       lockdep_assert_held(&sdata->local->mtx);
+
+       sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL |
+                               IEEE80211_STA_BEACON_POLL);
+       ieee80211_run_deferred_scan(sdata->local);
+}
+
+static void ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata)
+{
+       mutex_lock(&sdata->local->mtx);
+       __ieee80211_stop_poll(sdata);
+       mutex_unlock(&sdata->local->mtx);
+}
+
 static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
                                           u16 capab, bool erp_valid, u8 erp)
 {
@@ -1276,8 +1286,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
        sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE;
 
        /* just to be sure */
-       sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL |
-                               IEEE80211_STA_BEACON_POLL);
+       ieee80211_stop_poll(sdata);
 
        ieee80211_led_assoc(local, 1);
 
@@ -1447,8 +1456,7 @@ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
                return;
        }
 
-       ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL |
-                         IEEE80211_STA_BEACON_POLL);
+       __ieee80211_stop_poll(sdata);
 
        mutex_lock(&local->iflist_mtx);
        ieee80211_recalc_ps(local, -1);
@@ -1468,7 +1476,6 @@ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
                  round_jiffies_up(jiffies +
                                   IEEE80211_CONNECTION_IDLE_TIME));
 out:
-       ieee80211_run_deferred_scan(local);
        mutex_unlock(&local->mtx);
 }
 
@@ -2394,7 +2401,11 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                net_dbg_ratelimited("%s: cancelling probereq poll due to a received beacon\n",
                                    sdata->name);
 #endif
+               mutex_lock(&local->mtx);
                ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL;
+               ieee80211_run_deferred_scan(local);
+               mutex_unlock(&local->mtx);
+
                mutex_lock(&local->iflist_mtx);
                ieee80211_recalc_ps(local, -1);
                mutex_unlock(&local->iflist_mtx);
@@ -2581,8 +2592,7 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        u8 frame_buf[DEAUTH_DISASSOC_LEN];
 
-       ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL |
-                         IEEE80211_STA_BEACON_POLL);
+       ieee80211_stop_poll(sdata);
 
        ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
                               false, frame_buf);
@@ -2860,8 +2870,7 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
        u32 flags;
 
        if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-               sdata->u.mgd.flags &= ~(IEEE80211_STA_BEACON_POLL |
-                                       IEEE80211_STA_CONNECTION_POLL);
+               __ieee80211_stop_poll(sdata);
 
                /* let's probe the connection once */
                flags = sdata->local->hw.flags;
@@ -2930,7 +2939,10 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
        if (test_and_clear_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running))
                add_timer(&ifmgd->chswitch_timer);
        ieee80211_sta_reset_beacon_monitor(sdata);
+
+       mutex_lock(&sdata->local->mtx);
        ieee80211_restart_sta_timer(sdata);
+       mutex_unlock(&sdata->local->mtx);
 }
 #endif
 
@@ -3299,11 +3311,15 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
         * We can set this to true for non-11n hardware, that'll be checked
         * separately along with the peer capabilities.
         */
-       for (i = 0; i < req->crypto.n_ciphers_pairwise; i++)
+       for (i = 0; i < req->crypto.n_ciphers_pairwise; i++) {
                if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 ||
                    req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP ||
-                   req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104)
+                   req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) {
                        ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+                       netdev_info(sdata->dev,
+                                   "disabling HT due to WEP/TKIP use\n");
+               }
+       }
 
        if (req->flags & ASSOC_REQ_DISABLE_HT)
                ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
@@ -3311,8 +3327,11 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        /* Also disable HT if we don't support it or the AP doesn't use WMM */
        sband = local->hw.wiphy->bands[req->bss->channel->band];
        if (!sband->ht_cap.ht_supported ||
-           local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used)
+           local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) {
                ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+               netdev_info(sdata->dev,
+                           "disabling HT as WMM/QoS is not supported\n");
+       }
 
        memcpy(&ifmgd->ht_capa, &req->ht_capa, sizeof(ifmgd->ht_capa));
        memcpy(&ifmgd->ht_capa_mask, &req->ht_capa_mask,
index f054e94..abb226d 100644 (file)
@@ -16,6 +16,7 @@
 #include <net/mac80211.h>
 #include "ieee80211_i.h"
 #include "driver-trace.h"
+#include "driver-ops.h"
 
 /*
  * Tell our hardware to disable PS.
@@ -181,34 +182,58 @@ void ieee80211_offchannel_return(struct ieee80211_local *local,
        mutex_unlock(&local->iflist_mtx);
 }
 
+void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc)
+{
+       if (roc->notified)
+               return;
+
+       if (roc->mgmt_tx_cookie) {
+               if (!WARN_ON(!roc->frame)) {
+                       ieee80211_tx_skb(roc->sdata, roc->frame);
+                       roc->frame = NULL;
+               }
+       } else {
+               cfg80211_ready_on_channel(roc->sdata->dev, (unsigned long)roc,
+                                         roc->chan, roc->chan_type,
+                                         roc->req_duration, GFP_KERNEL);
+       }
+
+       roc->notified = true;
+}
+
 static void ieee80211_hw_roc_start(struct work_struct *work)
 {
        struct ieee80211_local *local =
                container_of(work, struct ieee80211_local, hw_roc_start);
-       struct ieee80211_sub_if_data *sdata;
+       struct ieee80211_roc_work *roc, *dep, *tmp;
 
        mutex_lock(&local->mtx);
 
-       if (!local->hw_roc_channel) {
-               mutex_unlock(&local->mtx);
-               return;
-       }
+       if (list_empty(&local->roc_list))
+               goto out_unlock;
 
-       if (local->hw_roc_skb) {
-               sdata = IEEE80211_DEV_TO_SUB_IF(local->hw_roc_dev);
-               ieee80211_tx_skb(sdata, local->hw_roc_skb);
-               local->hw_roc_skb = NULL;
-       } else {
-               cfg80211_ready_on_channel(local->hw_roc_dev,
-                                         local->hw_roc_cookie,
-                                         local->hw_roc_channel,
-                                         local->hw_roc_channel_type,
-                                         local->hw_roc_duration,
-                                         GFP_KERNEL);
-       }
+       roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work,
+                              list);
+
+       if (!roc->started)
+               goto out_unlock;
 
-       ieee80211_recalc_idle(local);
+       roc->hw_begun = true;
+       roc->hw_start_time = local->hw_roc_start_time;
 
+       ieee80211_handle_roc_started(roc);
+       list_for_each_entry_safe(dep, tmp, &roc->dependents, list) {
+               ieee80211_handle_roc_started(dep);
+
+               if (dep->duration > roc->duration) {
+                       u32 dur = dep->duration;
+                       dep->duration = dur - roc->duration;
+                       roc->duration = dur;
+                       list_del(&dep->list);
+                       list_add(&dep->list, &roc->list);
+               }
+       }
+ out_unlock:
        mutex_unlock(&local->mtx);
 }
 
@@ -216,36 +241,179 @@ void ieee80211_ready_on_channel(struct ieee80211_hw *hw)
 {
        struct ieee80211_local *local = hw_to_local(hw);
 
+       local->hw_roc_start_time = jiffies;
+
        trace_api_ready_on_channel(local);
 
        ieee80211_queue_work(hw, &local->hw_roc_start);
 }
 EXPORT_SYMBOL_GPL(ieee80211_ready_on_channel);
 
+void ieee80211_start_next_roc(struct ieee80211_local *local)
+{
+       struct ieee80211_roc_work *roc;
+
+       lockdep_assert_held(&local->mtx);
+
+       if (list_empty(&local->roc_list)) {
+               ieee80211_run_deferred_scan(local);
+               return;
+       }
+
+       roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work,
+                              list);
+
+       if (local->ops->remain_on_channel) {
+               int ret, duration = roc->duration;
+
+               /* XXX: duplicated, see ieee80211_start_roc_work() */
+               if (!duration)
+                       duration = 10;
+
+               ret = drv_remain_on_channel(local, roc->chan,
+                                           roc->chan_type,
+                                           duration);
+
+               roc->started = true;
+
+               if (ret) {
+                       wiphy_warn(local->hw.wiphy,
+                                  "failed to start next HW ROC (%d)\n", ret);
+                       /*
+                        * queue the work struct again to avoid recursion
+                        * when multiple failures occur
+                        */
+                       ieee80211_remain_on_channel_expired(&local->hw);
+               }
+       } else {
+               /* delay it a bit */
+               ieee80211_queue_delayed_work(&local->hw, &roc->work,
+                                            round_jiffies_relative(HZ/2));
+       }
+}
+
+void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc)
+{
+       struct ieee80211_roc_work *dep, *tmp;
+
+       /* was never transmitted */
+       if (roc->frame) {
+               cfg80211_mgmt_tx_status(roc->sdata->dev,
+                                       (unsigned long)roc->frame,
+                                       roc->frame->data, roc->frame->len,
+                                       false, GFP_KERNEL);
+               kfree_skb(roc->frame);
+       }
+
+       if (!roc->mgmt_tx_cookie)
+               cfg80211_remain_on_channel_expired(roc->sdata->dev,
+                                                  (unsigned long)roc,
+                                                  roc->chan, roc->chan_type,
+                                                  GFP_KERNEL);
+
+       list_for_each_entry_safe(dep, tmp, &roc->dependents, list)
+               ieee80211_roc_notify_destroy(dep);
+
+       kfree(roc);
+}
+
+void ieee80211_sw_roc_work(struct work_struct *work)
+{
+       struct ieee80211_roc_work *roc =
+               container_of(work, struct ieee80211_roc_work, work.work);
+       struct ieee80211_sub_if_data *sdata = roc->sdata;
+       struct ieee80211_local *local = sdata->local;
+
+       mutex_lock(&local->mtx);
+
+       if (roc->abort)
+               goto finish;
+
+       if (WARN_ON(list_empty(&local->roc_list)))
+               goto out_unlock;
+
+       if (WARN_ON(roc != list_first_entry(&local->roc_list,
+                                           struct ieee80211_roc_work,
+                                           list)))
+               goto out_unlock;
+
+       if (!roc->started) {
+               struct ieee80211_roc_work *dep;
+
+               /* start this ROC */
+
+               /* switch channel etc */
+               ieee80211_recalc_idle(local);
+
+               local->tmp_channel = roc->chan;
+               local->tmp_channel_type = roc->chan_type;
+               ieee80211_hw_config(local, 0);
+
+               /* tell userspace or send frame */
+               ieee80211_handle_roc_started(roc);
+               list_for_each_entry(dep, &roc->dependents, list)
+                       ieee80211_handle_roc_started(dep);
+
+               /* if it was pure TX, just finish right away */
+               if (!roc->duration)
+                       goto finish;
+
+               roc->started = true;
+               ieee80211_queue_delayed_work(&local->hw, &roc->work,
+                                            msecs_to_jiffies(roc->duration));
+       } else {
+               /* finish this ROC */
+ finish:
+               list_del(&roc->list);
+               ieee80211_roc_notify_destroy(roc);
+
+               if (roc->started) {
+                       drv_flush(local, false);
+
+                       local->tmp_channel = NULL;
+                       ieee80211_hw_config(local, 0);
+
+                       ieee80211_offchannel_return(local, true);
+               }
+
+               ieee80211_recalc_idle(local);
+
+               ieee80211_start_next_roc(local);
+               ieee80211_run_deferred_scan(local);
+       }
+
+ out_unlock:
+       mutex_unlock(&local->mtx);
+}
+
 static void ieee80211_hw_roc_done(struct work_struct *work)
 {
        struct ieee80211_local *local =
                container_of(work, struct ieee80211_local, hw_roc_done);
+       struct ieee80211_roc_work *roc;
 
        mutex_lock(&local->mtx);
 
-       if (!local->hw_roc_channel) {
-               mutex_unlock(&local->mtx);
-               return;
-       }
+       if (list_empty(&local->roc_list))
+               goto out_unlock;
 
-       if (!local->hw_roc_for_tx)
-               cfg80211_remain_on_channel_expired(local->hw_roc_dev,
-                                                  local->hw_roc_cookie,
-                                                  local->hw_roc_channel,
-                                                  local->hw_roc_channel_type,
-                                                  GFP_KERNEL);
+       roc = list_first_entry(&local->roc_list, struct ieee80211_roc_work,
+                              list);
+
+       if (!roc->started)
+               goto out_unlock;
 
-       local->hw_roc_channel = NULL;
-       local->hw_roc_cookie = 0;
+       list_del(&roc->list);
 
-       ieee80211_recalc_idle(local);
+       ieee80211_roc_notify_destroy(roc);
 
+       /* if there's another roc, start it now */
+       ieee80211_start_next_roc(local);
+
+       /* or scan maybe */
+       ieee80211_run_deferred_scan(local);
+
+ out_unlock:
        mutex_unlock(&local->mtx);
 }
 
@@ -259,8 +427,48 @@ void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw)
 }
 EXPORT_SYMBOL_GPL(ieee80211_remain_on_channel_expired);
 
-void ieee80211_hw_roc_setup(struct ieee80211_local *local)
+void ieee80211_roc_setup(struct ieee80211_local *local)
 {
        INIT_WORK(&local->hw_roc_start, ieee80211_hw_roc_start);
        INIT_WORK(&local->hw_roc_done, ieee80211_hw_roc_done);
+       INIT_LIST_HEAD(&local->roc_list);
+}
+
+void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_roc_work *roc, *tmp;
+       LIST_HEAD(tmp_list);
+
+       mutex_lock(&local->mtx);
+       list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
+               if (roc->sdata != sdata)
+                       continue;
+
+               if (roc->started && local->ops->remain_on_channel) {
+                       /* can race, so ignore return value */
+                       drv_cancel_remain_on_channel(local);
+               }
+
+               list_move_tail(&roc->list, &tmp_list);
+               roc->abort = true;
+       }
+
+       ieee80211_start_next_roc(local);
+       ieee80211_run_deferred_scan(local);
+       mutex_unlock(&local->mtx);
+
+       list_for_each_entry_safe(roc, tmp, &tmp_list, list) {
+               if (local->ops->remain_on_channel) {
+                       list_del(&roc->list);
+                       ieee80211_roc_notify_destroy(roc);
+               } else {
+                       ieee80211_queue_delayed_work(&local->hw, &roc->work, 0);
+
+                       /* work will clean up etc */
+                       flush_delayed_work(&roc->work);
+               }
+       }
+
+       WARN_ON_ONCE(!list_empty(&tmp_list));
 }
index af1c4e2..98c128b 100644 (file)
@@ -77,6 +77,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
                int err = drv_suspend(local, wowlan);
                if (err < 0) {
                        local->quiescing = false;
+                       local->wowlan = false;
                        return err;
                } else if (err > 0) {
                        WARN_ON(err != 1);
index 169da07..379f178 100644 (file)
@@ -323,7 +323,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
        ieee80211_mlme_notify_scan_completed(local);
        ieee80211_ibss_notify_scan_completed(local);
        ieee80211_mesh_notify_scan_completed(local);
-       ieee80211_queue_work(&local->hw, &local->work_work);
+       ieee80211_start_next_roc(local);
 }
 
 void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
@@ -376,7 +376,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
 static bool ieee80211_can_scan(struct ieee80211_local *local,
                               struct ieee80211_sub_if_data *sdata)
 {
-       if (!list_empty(&local->work_list))
+       if (!list_empty(&local->roc_list))
                return false;
 
        if (sdata->vif.type == NL80211_IFTYPE_STATION &&
index 0a70f79..77dcf2f 100644 (file)
@@ -377,7 +377,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
        /* make the station visible */
        sta_info_hash_add(local, sta);
 
-       list_add(&sta->list, &local->sta_list);
+       list_add_rcu(&sta->list, &local->sta_list);
 
        set_sta_flag(sta, WLAN_STA_INSERTED);
 
@@ -686,7 +686,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
        if (ret)
                return ret;
 
-       list_del(&sta->list);
+       list_del_rcu(&sta->list);
 
        mutex_lock(&local->key_mtx);
        for (i = 0; i < NUM_DEFAULT_KEYS; i++)
index 63a7690..6b4f425 100644 (file)
@@ -520,36 +520,16 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
 
        if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
                u64 cookie = (unsigned long)skb;
+               acked = info->flags & IEEE80211_TX_STAT_ACK;
 
                if (ieee80211_is_nullfunc(hdr->frame_control) ||
-                   ieee80211_is_qos_nullfunc(hdr->frame_control)) {
-                       acked = info->flags & IEEE80211_TX_STAT_ACK;
-
+                   ieee80211_is_qos_nullfunc(hdr->frame_control))
                        cfg80211_probe_status(skb->dev, hdr->addr1,
                                              cookie, acked, GFP_ATOMIC);
-               } else {
-                       struct ieee80211_work *wk;
-
-                       rcu_read_lock();
-                       list_for_each_entry_rcu(wk, &local->work_list, list) {
-                               if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX)
-                                       continue;
-                               if (wk->offchan_tx.frame != skb)
-                                       continue;
-                               wk->offchan_tx.status = true;
-                               break;
-                       }
-                       rcu_read_unlock();
-                       if (local->hw_roc_skb_for_status == skb) {
-                               cookie = local->hw_roc_cookie ^ 2;
-                               local->hw_roc_skb_for_status = NULL;
-                       }
-
+               else
                        cfg80211_mgmt_tx_status(
                                skb->dev, cookie, skb->data, skb->len,
-                               !!(info->flags & IEEE80211_TX_STAT_ACK),
-                               GFP_ATOMIC);
-               }
+                               acked, GFP_ATOMIC);
        }
 
        if (unlikely(info->ack_frame_id)) {
index 0c34765..af25c4e 100644 (file)
@@ -1735,7 +1735,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
        __le16 fc;
        struct ieee80211_hdr hdr;
        struct ieee80211s_hdr mesh_hdr __maybe_unused;
-       struct mesh_path __maybe_unused *mppath = NULL;
+       struct mesh_path __maybe_unused *mppath = NULL, *mpath = NULL;
        const u8 *encaps_data;
        int encaps_len, skip_header_bytes;
        int nh_pos, h_pos;
@@ -1801,8 +1801,11 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                        goto fail;
                }
                rcu_read_lock();
-               if (!is_multicast_ether_addr(skb->data))
-                       mppath = mpp_path_lookup(skb->data, sdata);
+               if (!is_multicast_ether_addr(skb->data)) {
+                       mpath = mesh_path_lookup(skb->data, sdata);
+                       if (!mpath)
+                               mppath = mpp_path_lookup(skb->data, sdata);
+               }
 
                /*
                 * Use address extension if it is a packet from
index a44c680..1df4019 100644 (file)
@@ -804,7 +804,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_tx_queue_params qparam;
        int ac;
-       bool use_11b;
+       bool use_11b, enable_qos;
        int aCWmin, aCWmax;
 
        if (!local->ops->conf_tx)
@@ -818,6 +818,13 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
        use_11b = (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ) &&
                 !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE);
 
+       /*
+        * By default disable QoS in STA mode for old access points, which do
+        * not support 802.11e. New APs will provide proper queue parameters,
+        * that we will configure later.
+        */
+       enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION);
+
        for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
                /* Set defaults according to 802.11-2007 Table 7-37 */
                aCWmax = 1023;
@@ -826,38 +833,47 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
                else
                        aCWmin = 15;
 
-               switch (ac) {
-               case IEEE80211_AC_BK:
-                       qparam.cw_max = aCWmax;
-                       qparam.cw_min = aCWmin;
-                       qparam.txop = 0;
-                       qparam.aifs = 7;
-                       break;
-               default: /* never happens but let's not leave undefined */
-               case IEEE80211_AC_BE:
+               if (enable_qos) {
+                       switch (ac) {
+                       case IEEE80211_AC_BK:
+                               qparam.cw_max = aCWmax;
+                               qparam.cw_min = aCWmin;
+                               qparam.txop = 0;
+                               qparam.aifs = 7;
+                               break;
+                       /* never happens but let's not leave undefined */
+                       default:
+                       case IEEE80211_AC_BE:
+                               qparam.cw_max = aCWmax;
+                               qparam.cw_min = aCWmin;
+                               qparam.txop = 0;
+                               qparam.aifs = 3;
+                               break;
+                       case IEEE80211_AC_VI:
+                               qparam.cw_max = aCWmin;
+                               qparam.cw_min = (aCWmin + 1) / 2 - 1;
+                               if (use_11b)
+                                       qparam.txop = 6016/32;
+                               else
+                                       qparam.txop = 3008/32;
+                               qparam.aifs = 2;
+                               break;
+                       case IEEE80211_AC_VO:
+                               qparam.cw_max = (aCWmin + 1) / 2 - 1;
+                               qparam.cw_min = (aCWmin + 1) / 4 - 1;
+                               if (use_11b)
+                                       qparam.txop = 3264/32;
+                               else
+                                       qparam.txop = 1504/32;
+                               qparam.aifs = 2;
+                               break;
+                       }
+               } else {
+                       /* Confiure old 802.11b/g medium access rules. */
                        qparam.cw_max = aCWmax;
                        qparam.cw_min = aCWmin;
                        qparam.txop = 0;
-                       qparam.aifs = 3;
-                       break;
-               case IEEE80211_AC_VI:
-                       qparam.cw_max = aCWmin;
-                       qparam.cw_min = (aCWmin + 1) / 2 - 1;
-                       if (use_11b)
-                               qparam.txop = 6016/32;
-                       else
-                               qparam.txop = 3008/32;
-                       qparam.aifs = 2;
-                       break;
-               case IEEE80211_AC_VO:
-                       qparam.cw_max = (aCWmin + 1) / 2 - 1;
-                       qparam.cw_min = (aCWmin + 1) / 4 - 1;
-                       if (use_11b)
-                               qparam.txop = 3264/32;
-                       else
-                               qparam.txop = 1504/32;
                        qparam.aifs = 2;
-                       break;
                }
 
                qparam.uapsd = false;
@@ -866,12 +882,8 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
                drv_conf_tx(local, sdata, ac, &qparam);
        }
 
-       /* after reinitialize QoS TX queues setting to default,
-        * disable QoS at all */
-
        if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
-               sdata->vif.bss_conf.qos =
-                       sdata->vif.type != NL80211_IFTYPE_STATION;
+               sdata->vif.bss_conf.qos = enable_qos;
                if (bss_notify)
                        ieee80211_bss_info_change_notify(sdata,
                                                         BSS_CHANGED_QOS);
@@ -1267,14 +1279,19 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        /* add STAs back */
        mutex_lock(&local->sta_mtx);
        list_for_each_entry(sta, &local->sta_list, list) {
-               if (sta->uploaded) {
-                       enum ieee80211_sta_state state;
+               enum ieee80211_sta_state state;
 
-                       for (state = IEEE80211_STA_NOTEXIST;
-                            state < sta->sta_state - 1; state++)
-                               WARN_ON(drv_sta_state(local, sta->sdata, sta,
-                                                     state, state + 1));
-               }
+               if (!sta->uploaded)
+                       continue;
+
+               /* AP-mode stations will be added later */
+               if (sta->sdata->vif.type == NL80211_IFTYPE_AP)
+                       continue;
+
+               for (state = IEEE80211_STA_NOTEXIST;
+                    state < sta->sta_state; state++)
+                       WARN_ON(drv_sta_state(local, sta->sdata, sta, state,
+                                             state + 1));
        }
        mutex_unlock(&local->sta_mtx);
 
@@ -1371,6 +1388,24 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                }
        }
 
+       /* APs are now beaconing, add back stations */
+       mutex_lock(&local->sta_mtx);
+       list_for_each_entry(sta, &local->sta_list, list) {
+               enum ieee80211_sta_state state;
+
+               if (!sta->uploaded)
+                       continue;
+
+               if (sta->sdata->vif.type != NL80211_IFTYPE_AP)
+                       continue;
+
+               for (state = IEEE80211_STA_NOTEXIST;
+                    state < sta->sta_state; state++)
+                       WARN_ON(drv_sta_state(local, sta->sdata, sta, state,
+                                             state + 1));
+       }
+       mutex_unlock(&local->sta_mtx);
+
        /* add back keys */
        list_for_each_entry(sdata, &local->interfaces, list)
                if (ieee80211_sdata_running(sdata))
diff --git a/net/mac80211/work.c b/net/mac80211/work.c
deleted file mode 100644 (file)
index b2650a9..0000000
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * mac80211 work implementation
- *
- * Copyright 2003-2008, Jouni Malinen <j@w1.fi>
- * Copyright 2004, Instant802 Networks, Inc.
- * Copyright 2005, Devicescape Software, Inc.
- * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
- * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
- * Copyright 2009, 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 version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/delay.h>
-#include <linux/if_ether.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/etherdevice.h>
-#include <linux/crc32.h>
-#include <linux/slab.h>
-#include <net/mac80211.h>
-#include <asm/unaligned.h>
-
-#include "ieee80211_i.h"
-#include "rate.h"
-#include "driver-ops.h"
-
-enum work_action {
-       WORK_ACT_NONE,
-       WORK_ACT_TIMEOUT,
-};
-
-
-/* utils */
-static inline void ASSERT_WORK_MTX(struct ieee80211_local *local)
-{
-       lockdep_assert_held(&local->mtx);
-}
-
-/*
- * We can have multiple work items (and connection probing)
- * scheduling this timer, but we need to take care to only
- * reschedule it when it should fire _earlier_ than it was
- * asked for before, or if it's not pending right now. This
- * function ensures that. Note that it then is required to
- * run this function for all timeouts after the first one
- * has happened -- the work that runs from this timer will
- * do that.
- */
-static void run_again(struct ieee80211_local *local,
-                     unsigned long timeout)
-{
-       ASSERT_WORK_MTX(local);
-
-       if (!timer_pending(&local->work_timer) ||
-           time_before(timeout, local->work_timer.expires))
-               mod_timer(&local->work_timer, timeout);
-}
-
-void free_work(struct ieee80211_work *wk)
-{
-       kfree_rcu(wk, rcu_head);
-}
-
-static enum work_action __must_check
-ieee80211_remain_on_channel_timeout(struct ieee80211_work *wk)
-{
-       /*
-        * First time we run, do nothing -- the generic code will
-        * have switched to the right channel etc.
-        */
-       if (!wk->started) {
-               wk->timeout = jiffies + msecs_to_jiffies(wk->remain.duration);
-
-               cfg80211_ready_on_channel(wk->sdata->dev, (unsigned long) wk,
-                                         wk->chan, wk->chan_type,
-                                         wk->remain.duration, GFP_KERNEL);
-
-               return WORK_ACT_NONE;
-       }
-
-       return WORK_ACT_TIMEOUT;
-}
-
-static enum work_action __must_check
-ieee80211_offchannel_tx(struct ieee80211_work *wk)
-{
-       if (!wk->started) {
-               wk->timeout = jiffies + msecs_to_jiffies(wk->offchan_tx.wait);
-
-               /*
-                * After this, offchan_tx.frame remains but now is no
-                * longer a valid pointer -- we still need it as the
-                * cookie for canceling this work/status matching.
-                */
-               ieee80211_tx_skb(wk->sdata, wk->offchan_tx.frame);
-
-               return WORK_ACT_NONE;
-       }
-
-       return WORK_ACT_TIMEOUT;
-}
-
-static void ieee80211_work_timer(unsigned long data)
-{
-       struct ieee80211_local *local = (void *) data;
-
-       if (local->quiescing)
-               return;
-
-       ieee80211_queue_work(&local->hw, &local->work_work);
-}
-
-static void ieee80211_work_work(struct work_struct *work)
-{
-       struct ieee80211_local *local =
-               container_of(work, struct ieee80211_local, work_work);
-       struct ieee80211_work *wk, *tmp;
-       LIST_HEAD(free_work);
-       enum work_action rma;
-       bool remain_off_channel = false;
-
-       /*
-        * ieee80211_queue_work() should have picked up most cases,
-        * here we'll pick the rest.
-        */
-       if (WARN(local->suspended, "work scheduled while going to suspend\n"))
-               return;
-
-       mutex_lock(&local->mtx);
-
-       if (local->scanning) {
-               mutex_unlock(&local->mtx);
-               return;
-       }
-
-       ieee80211_recalc_idle(local);
-
-       list_for_each_entry_safe(wk, tmp, &local->work_list, list) {
-               bool started = wk->started;
-
-               /* mark work as started if it's on the current off-channel */
-               if (!started && local->tmp_channel &&
-                   wk->chan == local->tmp_channel &&
-                   wk->chan_type == local->tmp_channel_type) {
-                       started = true;
-                       wk->timeout = jiffies;
-               }
-
-               if (!started && !local->tmp_channel) {
-                       ieee80211_offchannel_stop_vifs(local, true);
-
-                       local->tmp_channel = wk->chan;
-                       local->tmp_channel_type = wk->chan_type;
-
-                       ieee80211_hw_config(local, 0);
-
-                       started = true;
-                       wk->timeout = jiffies;
-               }
-
-               /* don't try to work with items that aren't started */
-               if (!started)
-                       continue;
-
-               if (time_is_after_jiffies(wk->timeout)) {
-                       /*
-                        * This work item isn't supposed to be worked on
-                        * right now, but take care to adjust the timer
-                        * properly.
-                        */
-                       run_again(local, wk->timeout);
-                       continue;
-               }
-
-               switch (wk->type) {
-               default:
-                       WARN_ON(1);
-                       /* nothing */
-                       rma = WORK_ACT_NONE;
-                       break;
-               case IEEE80211_WORK_ABORT:
-                       rma = WORK_ACT_TIMEOUT;
-                       break;
-               case IEEE80211_WORK_REMAIN_ON_CHANNEL:
-                       rma = ieee80211_remain_on_channel_timeout(wk);
-                       break;
-               case IEEE80211_WORK_OFFCHANNEL_TX:
-                       rma = ieee80211_offchannel_tx(wk);
-                       break;
-               }
-
-               wk->started = started;
-
-               switch (rma) {
-               case WORK_ACT_NONE:
-                       /* might have changed the timeout */
-                       run_again(local, wk->timeout);
-                       break;
-               case WORK_ACT_TIMEOUT:
-                       list_del_rcu(&wk->list);
-                       synchronize_rcu();
-                       list_add(&wk->list, &free_work);
-                       break;
-               default:
-                       WARN(1, "unexpected: %d", rma);
-               }
-       }
-
-       list_for_each_entry(wk, &local->work_list, list) {
-               if (!wk->started)
-                       continue;
-               if (wk->chan != local->tmp_channel ||
-                   wk->chan_type != local->tmp_channel_type)
-                       continue;
-               remain_off_channel = true;
-       }
-
-       if (!remain_off_channel && local->tmp_channel) {
-               local->tmp_channel = NULL;
-               ieee80211_hw_config(local, 0);
-
-               ieee80211_offchannel_return(local, true);
-
-               /* give connection some time to breathe */
-               run_again(local, jiffies + HZ/2);
-       }
-
-       ieee80211_recalc_idle(local);
-       ieee80211_run_deferred_scan(local);
-
-       mutex_unlock(&local->mtx);
-
-       list_for_each_entry_safe(wk, tmp, &free_work, list) {
-               wk->done(wk, NULL);
-               list_del(&wk->list);
-               kfree(wk);
-       }
-}
-
-void ieee80211_add_work(struct ieee80211_work *wk)
-{
-       struct ieee80211_local *local;
-
-       if (WARN_ON(!wk->chan))
-               return;
-
-       if (WARN_ON(!wk->sdata))
-               return;
-
-       if (WARN_ON(!wk->done))
-               return;
-
-       if (WARN_ON(!ieee80211_sdata_running(wk->sdata)))
-               return;
-
-       wk->started = false;
-
-       local = wk->sdata->local;
-       mutex_lock(&local->mtx);
-       list_add_tail(&wk->list, &local->work_list);
-       mutex_unlock(&local->mtx);
-
-       ieee80211_queue_work(&local->hw, &local->work_work);
-}
-
-void ieee80211_work_init(struct ieee80211_local *local)
-{
-       INIT_LIST_HEAD(&local->work_list);
-       setup_timer(&local->work_timer, ieee80211_work_timer,
-                   (unsigned long)local);
-       INIT_WORK(&local->work_work, ieee80211_work_work);
-}
-
-void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata)
-{
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_work *wk;
-       bool cleanup = false;
-
-       mutex_lock(&local->mtx);
-       list_for_each_entry(wk, &local->work_list, list) {
-               if (wk->sdata != sdata)
-                       continue;
-               cleanup = true;
-               wk->type = IEEE80211_WORK_ABORT;
-               wk->started = true;
-               wk->timeout = jiffies;
-       }
-       mutex_unlock(&local->mtx);
-
-       /* run cleanups etc. */
-       if (cleanup)
-               ieee80211_work_work(&local->work_work);
-
-       mutex_lock(&local->mtx);
-       list_for_each_entry(wk, &local->work_list, list) {
-               if (wk->sdata != sdata)
-                       continue;
-               WARN_ON(1);
-               break;
-       }
-       mutex_unlock(&local->mtx);
-}
-
-static enum work_done_result ieee80211_remain_done(struct ieee80211_work *wk,
-                                                  struct sk_buff *skb)
-{
-       /*
-        * We are done serving the remain-on-channel command.
-        */
-       cfg80211_remain_on_channel_expired(wk->sdata->dev, (unsigned long) wk,
-                                          wk->chan, wk->chan_type,
-                                          GFP_KERNEL);
-
-       return WORK_DONE_DESTROY;
-}
-
-int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata,
-                                  struct ieee80211_channel *chan,
-                                  enum nl80211_channel_type channel_type,
-                                  unsigned int duration, u64 *cookie)
-{
-       struct ieee80211_work *wk;
-
-       wk = kzalloc(sizeof(*wk), GFP_KERNEL);
-       if (!wk)
-               return -ENOMEM;
-
-       wk->type = IEEE80211_WORK_REMAIN_ON_CHANNEL;
-       wk->chan = chan;
-       wk->chan_type = channel_type;
-       wk->sdata = sdata;
-       wk->done = ieee80211_remain_done;
-
-       wk->remain.duration = duration;
-
-       *cookie = (unsigned long) wk;
-
-       ieee80211_add_work(wk);
-
-       return 0;
-}
-
-int ieee80211_wk_cancel_remain_on_channel(struct ieee80211_sub_if_data *sdata,
-                                         u64 cookie)
-{
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_work *wk, *tmp;
-       bool found = false;
-
-       mutex_lock(&local->mtx);
-       list_for_each_entry_safe(wk, tmp, &local->work_list, list) {
-               if ((unsigned long) wk == cookie) {
-                       wk->timeout = jiffies;
-                       found = true;
-                       break;
-               }
-       }
-       mutex_unlock(&local->mtx);
-
-       if (!found)
-               return -ENOENT;
-
-       ieee80211_queue_work(&local->hw, &local->work_work);
-
-       return 0;
-}
index 9f6ce01..4177bb5 100644 (file)
@@ -121,14 +121,14 @@ error:
  * The device remains polling for targets until a target is found or
  * the nfc_stop_poll function is called.
  */
-int nfc_start_poll(struct nfc_dev *dev, u32 protocols)
+int nfc_start_poll(struct nfc_dev *dev, u32 im_protocols, u32 tm_protocols)
 {
        int rc;
 
-       pr_debug("dev_name=%s protocols=0x%x\n",
-                dev_name(&dev->dev), protocols);
+       pr_debug("dev_name %s initiator protocols 0x%x target protocols 0x%x\n",
+                dev_name(&dev->dev), im_protocols, tm_protocols);
 
-       if (!protocols)
+       if (!im_protocols && !tm_protocols)
                return -EINVAL;
 
        device_lock(&dev->dev);
@@ -143,9 +143,11 @@ int nfc_start_poll(struct nfc_dev *dev, u32 protocols)
                goto error;
        }
 
-       rc = dev->ops->start_poll(dev, protocols);
-       if (!rc)
+       rc = dev->ops->start_poll(dev, im_protocols, tm_protocols);
+       if (!rc) {
                dev->polling = true;
+               dev->rf_mode = NFC_RF_NONE;
+       }
 
 error:
        device_unlock(&dev->dev);
@@ -235,8 +237,10 @@ int nfc_dep_link_up(struct nfc_dev *dev, int target_index, u8 comm_mode)
        }
 
        rc = dev->ops->dep_link_up(dev, target, comm_mode, gb, gb_len);
-       if (!rc)
+       if (!rc) {
                dev->active_target = target;
+               dev->rf_mode = NFC_RF_INITIATOR;
+       }
 
 error:
        device_unlock(&dev->dev);
@@ -264,11 +268,6 @@ int nfc_dep_link_down(struct nfc_dev *dev)
                goto error;
        }
 
-       if (dev->dep_rf_mode == NFC_RF_TARGET) {
-               rc = -EOPNOTSUPP;
-               goto error;
-       }
-
        rc = dev->ops->dep_link_down(dev);
        if (!rc) {
                dev->dep_link_up = false;
@@ -286,7 +285,6 @@ int nfc_dep_link_is_up(struct nfc_dev *dev, u32 target_idx,
                       u8 comm_mode, u8 rf_mode)
 {
        dev->dep_link_up = true;
-       dev->dep_rf_mode = rf_mode;
 
        nfc_llcp_mac_is_up(dev, target_idx, comm_mode, rf_mode);
 
@@ -330,6 +328,7 @@ int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol)
        rc = dev->ops->activate_target(dev, target, protocol);
        if (!rc) {
                dev->active_target = target;
+               dev->rf_mode = NFC_RF_INITIATOR;
 
                if (dev->ops->check_presence)
                        mod_timer(&dev->check_pres_timer, jiffies +
@@ -409,27 +408,30 @@ int nfc_data_exchange(struct nfc_dev *dev, u32 target_idx, struct sk_buff *skb,
                goto error;
        }
 
-       if (dev->active_target == NULL) {
-               rc = -ENOTCONN;
-               kfree_skb(skb);
-               goto error;
-       }
+       if (dev->rf_mode == NFC_RF_INITIATOR && dev->active_target != NULL) {
+               if (dev->active_target->idx != target_idx) {
+                       rc = -EADDRNOTAVAIL;
+                       kfree_skb(skb);
+                       goto error;
+               }
 
-       if (dev->active_target->idx != target_idx) {
-               rc = -EADDRNOTAVAIL;
+               if (dev->ops->check_presence)
+                       del_timer_sync(&dev->check_pres_timer);
+
+               rc = dev->ops->im_transceive(dev, dev->active_target, skb, cb,
+                                            cb_context);
+
+               if (!rc && dev->ops->check_presence)
+                       mod_timer(&dev->check_pres_timer, jiffies +
+                                 msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
+       } else if (dev->rf_mode == NFC_RF_TARGET && dev->ops->tm_send != NULL) {
+               rc = dev->ops->tm_send(dev, skb);
+       } else {
+               rc = -ENOTCONN;
                kfree_skb(skb);
                goto error;
        }
 
-       if (dev->ops->check_presence)
-               del_timer_sync(&dev->check_pres_timer);
-
-       rc = dev->ops->data_exchange(dev, dev->active_target, skb, cb,
-                                    cb_context);
-
-       if (!rc && dev->ops->check_presence)
-               mod_timer(&dev->check_pres_timer, jiffies +
-                         msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
 
 error:
        device_unlock(&dev->dev);
@@ -447,6 +449,63 @@ int nfc_set_remote_general_bytes(struct nfc_dev *dev, u8 *gb, u8 gb_len)
 }
 EXPORT_SYMBOL(nfc_set_remote_general_bytes);
 
+u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, size_t *gb_len)
+{
+       pr_debug("dev_name=%s\n", dev_name(&dev->dev));
+
+       return nfc_llcp_general_bytes(dev, gb_len);
+}
+EXPORT_SYMBOL(nfc_get_local_general_bytes);
+
+int nfc_tm_data_received(struct nfc_dev *dev, struct sk_buff *skb)
+{
+       /* Only LLCP target mode for now */
+       if (dev->dep_link_up == false) {
+               kfree_skb(skb);
+               return -ENOLINK;
+       }
+
+       return nfc_llcp_data_received(dev, skb);
+}
+EXPORT_SYMBOL(nfc_tm_data_received);
+
+int nfc_tm_activated(struct nfc_dev *dev, u32 protocol, u8 comm_mode,
+                    u8 *gb, size_t gb_len)
+{
+       int rc;
+
+       device_lock(&dev->dev);
+
+       dev->polling = false;
+
+       if (gb != NULL) {
+               rc = nfc_set_remote_general_bytes(dev, gb, gb_len);
+               if (rc < 0)
+                       goto out;
+       }
+
+       dev->rf_mode = NFC_RF_TARGET;
+
+       if (protocol == NFC_PROTO_NFC_DEP_MASK)
+               nfc_dep_link_is_up(dev, 0, comm_mode, NFC_RF_TARGET);
+
+       rc = nfc_genl_tm_activated(dev, protocol);
+
+out:
+       device_unlock(&dev->dev);
+
+       return rc;
+}
+EXPORT_SYMBOL(nfc_tm_activated);
+
+int nfc_tm_deactivated(struct nfc_dev *dev)
+{
+       dev->dep_link_up = false;
+
+       return nfc_genl_tm_deactivated(dev);
+}
+EXPORT_SYMBOL(nfc_tm_deactivated);
+
 /**
  * nfc_alloc_send_skb - allocate a skb for data exchange responses
  *
@@ -678,7 +737,7 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
        struct nfc_dev *dev;
 
        if (!ops->start_poll || !ops->stop_poll || !ops->activate_target ||
-           !ops->deactivate_target || !ops->data_exchange)
+           !ops->deactivate_target || !ops->im_transceive)
                return NULL;
 
        if (!supported_protocols)
index e1a640d..a8b0b71 100644 (file)
@@ -481,12 +481,13 @@ static int hci_dev_down(struct nfc_dev *nfc_dev)
        return 0;
 }
 
-static int hci_start_poll(struct nfc_dev *nfc_dev, u32 protocols)
+static int hci_start_poll(struct nfc_dev *nfc_dev,
+                         u32 im_protocols, u32 tm_protocols)
 {
        struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
 
        if (hdev->ops->start_poll)
-               return hdev->ops->start_poll(hdev, protocols);
+               return hdev->ops->start_poll(hdev, im_protocols, tm_protocols);
        else
                return nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
                                       NFC_HCI_EVT_READER_REQUESTED, NULL, 0);
@@ -511,9 +512,9 @@ static void hci_deactivate_target(struct nfc_dev *nfc_dev,
 {
 }
 
-static int hci_data_exchange(struct nfc_dev *nfc_dev, struct nfc_target *target,
-                            struct sk_buff *skb, data_exchange_cb_t cb,
-                            void *cb_context)
+static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
+                         struct sk_buff *skb, data_exchange_cb_t cb,
+                         void *cb_context)
 {
        struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
        int r;
@@ -579,7 +580,7 @@ static struct nfc_ops hci_nfc_ops = {
        .stop_poll = hci_stop_poll,
        .activate_target = hci_activate_target,
        .deactivate_target = hci_deactivate_target,
-       .data_exchange = hci_data_exchange,
+       .im_transceive = hci_transceive,
        .check_presence = hci_check_presence,
 };
 
index 5665dc6..6b836e6 100644 (file)
@@ -765,14 +765,16 @@ static int nfc_shdlc_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb)
        return 0;
 }
 
-static int nfc_shdlc_start_poll(struct nfc_hci_dev *hdev, u32 protocols)
+static int nfc_shdlc_start_poll(struct nfc_hci_dev *hdev,
+                               u32 im_protocols, u32 tm_protocols)
 {
        struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
 
        pr_debug("\n");
 
        if (shdlc->ops->start_poll)
-               return shdlc->ops->start_poll(shdlc, protocols);
+               return shdlc->ops->start_poll(shdlc,
+                                             im_protocols, tm_protocols);
 
        return 0;
 }
index bf8ae4f..b982b5b 100644 (file)
@@ -51,7 +51,7 @@ static u8 llcp_tlv8(u8 *tlv, u8 type)
        return tlv[2];
 }
 
-static u8 llcp_tlv16(u8 *tlv, u8 type)
+static u16 llcp_tlv16(u8 *tlv, u8 type)
 {
        if (tlv[0] != type || tlv[1] != llcp_tlv_length[tlv[0]])
                return 0;
@@ -67,7 +67,7 @@ static u8 llcp_tlv_version(u8 *tlv)
 
 static u16 llcp_tlv_miux(u8 *tlv)
 {
-       return llcp_tlv16(tlv, LLCP_TLV_MIUX) & 0x7f;
+       return llcp_tlv16(tlv, LLCP_TLV_MIUX) & 0x7ff;
 }
 
 static u16 llcp_tlv_wks(u8 *tlv)
@@ -117,8 +117,8 @@ u8 *nfc_llcp_build_tlv(u8 type, u8 *value, u8 value_length, u8 *tlv_length)
        return tlv;
 }
 
-int nfc_llcp_parse_tlv(struct nfc_llcp_local *local,
-                      u8 *tlv_array, u16 tlv_array_len)
+int nfc_llcp_parse_gb_tlv(struct nfc_llcp_local *local,
+                         u8 *tlv_array, u16 tlv_array_len)
 {
        u8 *tlv = tlv_array, type, length, offset = 0;
 
@@ -149,8 +149,45 @@ int nfc_llcp_parse_tlv(struct nfc_llcp_local *local,
                case LLCP_TLV_OPT:
                        local->remote_opt = llcp_tlv_opt(tlv);
                        break;
+               default:
+                       pr_err("Invalid gt tlv value 0x%x\n", type);
+                       break;
+               }
+
+               offset += length + 2;
+               tlv += length + 2;
+       }
+
+       pr_debug("version 0x%x miu %d lto %d opt 0x%x wks 0x%x\n",
+                local->remote_version, local->remote_miu,
+                local->remote_lto, local->remote_opt,
+                local->remote_wks);
+
+       return 0;
+}
+
+int nfc_llcp_parse_connection_tlv(struct nfc_llcp_sock *sock,
+                                 u8 *tlv_array, u16 tlv_array_len)
+{
+       u8 *tlv = tlv_array, type, length, offset = 0;
+
+       pr_debug("TLV array length %d\n", tlv_array_len);
+
+       if (sock == NULL)
+               return -ENOTCONN;
+
+       while (offset < tlv_array_len) {
+               type = tlv[0];
+               length = tlv[1];
+
+               pr_debug("type 0x%x length %d\n", type, length);
+
+               switch (type) {
+               case LLCP_TLV_MIUX:
+                       sock->miu = llcp_tlv_miux(tlv) + 128;
+                       break;
                case LLCP_TLV_RW:
-                       local->remote_rw = llcp_tlv_rw(tlv);
+                       sock->rw = llcp_tlv_rw(tlv);
                        break;
                case LLCP_TLV_SN:
                        break;
@@ -163,10 +200,7 @@ int nfc_llcp_parse_tlv(struct nfc_llcp_local *local,
                tlv += length + 2;
        }
 
-       pr_debug("version 0x%x miu %d lto %d opt 0x%x wks 0x%x rw %d\n",
-                local->remote_version, local->remote_miu,
-                local->remote_lto, local->remote_opt,
-                local->remote_wks, local->remote_rw);
+       pr_debug("sock %p rw %d miu %d\n", sock, sock->rw, sock->miu);
 
        return 0;
 }
@@ -474,7 +508,7 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
 
        while (remaining_len > 0) {
 
-               frag_len = min_t(size_t, local->remote_miu, remaining_len);
+               frag_len = min_t(size_t, sock->miu, remaining_len);
 
                pr_debug("Fragment %zd bytes remaining %zd",
                         frag_len, remaining_len);
index 42994fa..5d503ee 100644 (file)
@@ -31,47 +31,41 @@ static u8 llcp_magic[3] = {0x46, 0x66, 0x6d};
 
 static struct list_head llcp_devices;
 
-static void nfc_llcp_socket_release(struct nfc_llcp_local *local)
+void nfc_llcp_sock_link(struct llcp_sock_list *l, struct sock *sk)
 {
-       struct nfc_llcp_sock *parent, *s, *n;
-       struct sock *sk, *parent_sk;
-       int i;
-
-       mutex_lock(&local->socket_lock);
-
-       for (i = 0; i < LLCP_MAX_SAP; i++) {
-               parent = local->sockets[i];
-               if (parent == NULL)
-                       continue;
-
-               /* Release all child sockets */
-               list_for_each_entry_safe(s, n, &parent->list, list) {
-                       list_del_init(&s->list);
-                       sk = &s->sk;
-
-                       lock_sock(sk);
-
-                       if (sk->sk_state == LLCP_CONNECTED)
-                               nfc_put_device(s->dev);
+       write_lock(&l->lock);
+       sk_add_node(sk, &l->head);
+       write_unlock(&l->lock);
+}
 
-                       sk->sk_state = LLCP_CLOSED;
+void nfc_llcp_sock_unlink(struct llcp_sock_list *l, struct sock *sk)
+{
+       write_lock(&l->lock);
+       sk_del_node_init(sk);
+       write_unlock(&l->lock);
+}
 
-                       release_sock(sk);
+static void nfc_llcp_socket_release(struct nfc_llcp_local *local)
+{
+       struct sock *sk;
+       struct hlist_node *node, *tmp;
+       struct nfc_llcp_sock *llcp_sock;
 
-                       sock_orphan(sk);
+       write_lock(&local->sockets.lock);
 
-                       s->local = NULL;
-               }
+       sk_for_each_safe(sk, node, tmp, &local->sockets.head) {
+               llcp_sock = nfc_llcp_sock(sk);
 
-               parent_sk = &parent->sk;
+               lock_sock(sk);
 
-               lock_sock(parent_sk);
+               if (sk->sk_state == LLCP_CONNECTED)
+                       nfc_put_device(llcp_sock->dev);
 
-               if (parent_sk->sk_state == LLCP_LISTEN) {
+               if (sk->sk_state == LLCP_LISTEN) {
                        struct nfc_llcp_sock *lsk, *n;
                        struct sock *accept_sk;
 
-                       list_for_each_entry_safe(lsk, n, &parent->accept_queue,
+                       list_for_each_entry_safe(lsk, n, &llcp_sock->accept_queue,
                                                 accept_queue) {
                                accept_sk = &lsk->sk;
                                lock_sock(accept_sk);
@@ -83,24 +77,53 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local)
                                release_sock(accept_sk);
 
                                sock_orphan(accept_sk);
-
-                               lsk->local = NULL;
                        }
                }
 
-               if (parent_sk->sk_state == LLCP_CONNECTED)
-                       nfc_put_device(parent->dev);
-
-               parent_sk->sk_state = LLCP_CLOSED;
+               sk->sk_state = LLCP_CLOSED;
 
-               release_sock(parent_sk);
+               release_sock(sk);
 
-               sock_orphan(parent_sk);
+               sock_orphan(sk);
 
-               parent->local = NULL;
+               sk_del_node_init(sk);
        }
 
-       mutex_unlock(&local->socket_lock);
+       write_unlock(&local->sockets.lock);
+}
+
+struct nfc_llcp_local *nfc_llcp_local_get(struct nfc_llcp_local *local)
+{
+       kref_get(&local->ref);
+
+       return local;
+}
+
+static void local_release(struct kref *ref)
+{
+       struct nfc_llcp_local *local;
+
+       local = container_of(ref, struct nfc_llcp_local, ref);
+
+       list_del(&local->list);
+       nfc_llcp_socket_release(local);
+       del_timer_sync(&local->link_timer);
+       skb_queue_purge(&local->tx_queue);
+       destroy_workqueue(local->tx_wq);
+       destroy_workqueue(local->rx_wq);
+       destroy_workqueue(local->timeout_wq);
+       kfree_skb(local->rx_pending);
+       kfree(local);
+}
+
+int nfc_llcp_local_put(struct nfc_llcp_local *local)
+{
+       WARN_ON(local == NULL);
+
+       if (local == NULL)
+               return 0;
+
+       return kref_put(&local->ref, local_release);
 }
 
 static void nfc_llcp_clear_sdp(struct nfc_llcp_local *local)
@@ -384,31 +407,9 @@ int nfc_llcp_set_remote_gb(struct nfc_dev *dev, u8 *gb, u8 gb_len)
                return -EINVAL;
        }
 
-       return nfc_llcp_parse_tlv(local,
-                                 &local->remote_gb[3],
-                                 local->remote_gb_len - 3);
-}
-
-static void nfc_llcp_tx_work(struct work_struct *work)
-{
-       struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local,
-                                                   tx_work);
-       struct sk_buff *skb;
-
-       skb = skb_dequeue(&local->tx_queue);
-       if (skb != NULL) {
-               pr_debug("Sending pending skb\n");
-               print_hex_dump(KERN_DEBUG, "LLCP Tx: ", DUMP_PREFIX_OFFSET,
-                              16, 1, skb->data, skb->len, true);
-
-               nfc_data_exchange(local->dev, local->target_idx,
-                                 skb, nfc_llcp_recv, local);
-       } else {
-               nfc_llcp_send_symm(local->dev);
-       }
-
-       mod_timer(&local->link_timer,
-                 jiffies + msecs_to_jiffies(local->remote_lto));
+       return nfc_llcp_parse_gb_tlv(local,
+                                    &local->remote_gb[3],
+                                    local->remote_gb_len - 3);
 }
 
 static u8 nfc_llcp_dsap(struct sk_buff *pdu)
@@ -443,46 +444,146 @@ static void nfc_llcp_set_nrns(struct nfc_llcp_sock *sock, struct sk_buff *pdu)
        sock->recv_ack_n = (sock->recv_n - 1) % 16;
 }
 
+static void nfc_llcp_tx_work(struct work_struct *work)
+{
+       struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local,
+                                                   tx_work);
+       struct sk_buff *skb;
+       struct sock *sk;
+       struct nfc_llcp_sock *llcp_sock;
+
+       skb = skb_dequeue(&local->tx_queue);
+       if (skb != NULL) {
+               sk = skb->sk;
+               llcp_sock = nfc_llcp_sock(sk);
+               if (llcp_sock != NULL) {
+                       int ret;
+
+                       pr_debug("Sending pending skb\n");
+                       print_hex_dump(KERN_DEBUG, "LLCP Tx: ",
+                                      DUMP_PREFIX_OFFSET, 16, 1,
+                                      skb->data, skb->len, true);
+
+                       ret = nfc_data_exchange(local->dev, local->target_idx,
+                                               skb, nfc_llcp_recv, local);
+
+                       if (!ret && nfc_llcp_ptype(skb) == LLCP_PDU_I) {
+                               skb = skb_get(skb);
+                               skb_queue_tail(&llcp_sock->tx_pending_queue,
+                                              skb);
+                       }
+               } else {
+                       nfc_llcp_send_symm(local->dev);
+               }
+       } else {
+               nfc_llcp_send_symm(local->dev);
+       }
+
+       mod_timer(&local->link_timer,
+                 jiffies + msecs_to_jiffies(2 * local->remote_lto));
+}
+
+static struct nfc_llcp_sock *nfc_llcp_connecting_sock_get(struct nfc_llcp_local *local,
+                                                         u8 ssap)
+{
+       struct sock *sk;
+       struct nfc_llcp_sock *llcp_sock;
+       struct hlist_node *node;
+
+       read_lock(&local->connecting_sockets.lock);
+
+       sk_for_each(sk, node, &local->connecting_sockets.head) {
+               llcp_sock = nfc_llcp_sock(sk);
+
+               if (llcp_sock->ssap == ssap) {
+                       sock_hold(&llcp_sock->sk);
+                       goto out;
+               }
+       }
+
+       llcp_sock = NULL;
+
+out:
+       read_unlock(&local->connecting_sockets.lock);
+
+       return llcp_sock;
+}
+
 static struct nfc_llcp_sock *nfc_llcp_sock_get(struct nfc_llcp_local *local,
                                               u8 ssap, u8 dsap)
 {
-       struct nfc_llcp_sock *sock, *llcp_sock, *n;
+       struct sock *sk;
+       struct hlist_node *node;
+       struct nfc_llcp_sock *llcp_sock;
 
        pr_debug("ssap dsap %d %d\n", ssap, dsap);
 
        if (ssap == 0 && dsap == 0)
                return NULL;
 
-       mutex_lock(&local->socket_lock);
-       sock = local->sockets[ssap];
-       if (sock == NULL) {
-               mutex_unlock(&local->socket_lock);
-               return NULL;
-       }
+       read_lock(&local->sockets.lock);
 
-       pr_debug("root dsap %d (%d)\n", sock->dsap, dsap);
+       llcp_sock = NULL;
 
-       if (sock->dsap == dsap) {
-               sock_hold(&sock->sk);
-               mutex_unlock(&local->socket_lock);
-               return sock;
+       sk_for_each(sk, node, &local->sockets.head) {
+               llcp_sock = nfc_llcp_sock(sk);
+
+               if (llcp_sock->ssap == ssap &&
+                   llcp_sock->dsap == dsap)
+                       break;
        }
 
-       list_for_each_entry_safe(llcp_sock, n, &sock->list, list) {
-               pr_debug("llcp_sock %p sk %p dsap %d\n", llcp_sock,
-                        &llcp_sock->sk, llcp_sock->dsap);
-               if (llcp_sock->dsap == dsap) {
-                       sock_hold(&llcp_sock->sk);
-                       mutex_unlock(&local->socket_lock);
-                       return llcp_sock;
-               }
+       read_unlock(&local->sockets.lock);
+
+       if (llcp_sock == NULL)
+               return NULL;
+
+       sock_hold(&llcp_sock->sk);
+
+       return llcp_sock;
+}
+
+static struct nfc_llcp_sock *nfc_llcp_sock_get_sn(struct nfc_llcp_local *local,
+                                                 u8 *sn, size_t sn_len)
+{
+       struct sock *sk;
+       struct hlist_node *node;
+       struct nfc_llcp_sock *llcp_sock;
+
+       pr_debug("sn %zd\n", sn_len);
+
+       if (sn == NULL || sn_len == 0)
+               return NULL;
+
+       read_lock(&local->sockets.lock);
+
+       llcp_sock = NULL;
+
+       sk_for_each(sk, node, &local->sockets.head) {
+               llcp_sock = nfc_llcp_sock(sk);
+
+               if (llcp_sock->sk.sk_state != LLCP_LISTEN)
+                       continue;
+
+               if (llcp_sock->service_name == NULL ||
+                   llcp_sock->service_name_len == 0)
+                       continue;
+
+               if (llcp_sock->service_name_len != sn_len)
+                       continue;
+
+               if (memcmp(sn, llcp_sock->service_name, sn_len) == 0)
+                       break;
        }
 
-       pr_err("Could not find socket for %d %d\n", ssap, dsap);
+       read_unlock(&local->sockets.lock);
 
-       mutex_unlock(&local->socket_lock);
+       if (llcp_sock == NULL)
+               return NULL;
 
-       return NULL;
+       sock_hold(&llcp_sock->sk);
+
+       return llcp_sock;
 }
 
 static void nfc_llcp_sock_put(struct nfc_llcp_sock *sock)
@@ -518,35 +619,19 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,
 {
        struct sock *new_sk, *parent;
        struct nfc_llcp_sock *sock, *new_sock;
-       u8 dsap, ssap, bound_sap, reason;
+       u8 dsap, ssap, reason;
 
        dsap = nfc_llcp_dsap(skb);
        ssap = nfc_llcp_ssap(skb);
 
        pr_debug("%d %d\n", dsap, ssap);
 
-       nfc_llcp_parse_tlv(local, &skb->data[LLCP_HEADER_SIZE],
-                          skb->len - LLCP_HEADER_SIZE);
-
        if (dsap != LLCP_SAP_SDP) {
-               bound_sap = dsap;
-
-               mutex_lock(&local->socket_lock);
-               sock = local->sockets[dsap];
-               if (sock == NULL) {
-                       mutex_unlock(&local->socket_lock);
+               sock = nfc_llcp_sock_get(local, dsap, LLCP_SAP_SDP);
+               if (sock == NULL || sock->sk.sk_state != LLCP_LISTEN) {
                        reason = LLCP_DM_NOBOUND;
                        goto fail;
                }
-
-               sock_hold(&sock->sk);
-               mutex_unlock(&local->socket_lock);
-
-               lock_sock(&sock->sk);
-
-               if (sock->dsap == LLCP_SAP_SDP &&
-                   sock->sk.sk_state == LLCP_LISTEN)
-                       goto enqueue;
        } else {
                u8 *sn;
                size_t sn_len;
@@ -559,40 +644,15 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,
 
                pr_debug("Service name length %zu\n", sn_len);
 
-               mutex_lock(&local->socket_lock);
-               for (bound_sap = 0; bound_sap < LLCP_LOCAL_SAP_OFFSET;
-                    bound_sap++) {
-                       sock = local->sockets[bound_sap];
-                       if (sock == NULL)
-                               continue;
-
-                       if (sock->service_name == NULL ||
-                           sock->service_name_len == 0)
-                                       continue;
-
-                       if (sock->service_name_len != sn_len)
-                               continue;
-
-                       if (sock->dsap == LLCP_SAP_SDP &&
-                           sock->sk.sk_state == LLCP_LISTEN &&
-                           !memcmp(sn, sock->service_name, sn_len)) {
-                               pr_debug("Found service name at SAP %d\n",
-                                        bound_sap);
-                               sock_hold(&sock->sk);
-                               mutex_unlock(&local->socket_lock);
-
-                               lock_sock(&sock->sk);
-
-                               goto enqueue;
-                       }
+               sock = nfc_llcp_sock_get_sn(local, sn, sn_len);
+               if (sock == NULL) {
+                       reason = LLCP_DM_NOBOUND;
+                       goto fail;
                }
-               mutex_unlock(&local->socket_lock);
        }
 
-       reason = LLCP_DM_NOBOUND;
-       goto fail;
+       lock_sock(&sock->sk);
 
-enqueue:
        parent = &sock->sk;
 
        if (sk_acceptq_is_full(parent)) {
@@ -612,15 +672,19 @@ enqueue:
 
        new_sock = nfc_llcp_sock(new_sk);
        new_sock->dev = local->dev;
-       new_sock->local = local;
+       new_sock->local = nfc_llcp_local_get(local);
+       new_sock->miu = local->remote_miu;
        new_sock->nfc_protocol = sock->nfc_protocol;
-       new_sock->ssap = bound_sap;
+       new_sock->ssap = sock->ssap;
        new_sock->dsap = ssap;
        new_sock->parent = parent;
 
+       nfc_llcp_parse_connection_tlv(new_sock, &skb->data[LLCP_HEADER_SIZE],
+                                     skb->len - LLCP_HEADER_SIZE);
+
        pr_debug("new sock %p sk %p\n", new_sock, &new_sock->sk);
 
-       list_add_tail(&new_sock->list, &sock->list);
+       nfc_llcp_sock_link(&local->sockets, new_sk);
 
        nfc_llcp_accept_enqueue(&sock->sk, new_sk);
 
@@ -654,12 +718,12 @@ int nfc_llcp_queue_i_frames(struct nfc_llcp_sock *sock)
 
        pr_debug("Remote ready %d tx queue len %d remote rw %d",
                 sock->remote_ready, skb_queue_len(&sock->tx_pending_queue),
-                local->remote_rw);
+                sock->rw);
 
        /* Try to queue some I frames for transmission */
        while (sock->remote_ready &&
-              skb_queue_len(&sock->tx_pending_queue) < local->remote_rw) {
-               struct sk_buff *pdu, *pending_pdu;
+              skb_queue_len(&sock->tx_pending_queue) < sock->rw) {
+               struct sk_buff *pdu;
 
                pdu = skb_dequeue(&sock->tx_queue);
                if (pdu == NULL)
@@ -668,10 +732,7 @@ int nfc_llcp_queue_i_frames(struct nfc_llcp_sock *sock)
                /* Update N(S)/N(R) */
                nfc_llcp_set_nrns(sock, pdu);
 
-               pending_pdu = skb_clone(pdu, GFP_KERNEL);
-
                skb_queue_tail(&local->tx_queue, pdu);
-               skb_queue_tail(&sock->tx_pending_queue, pending_pdu);
                nr_frames++;
        }
 
@@ -728,11 +789,21 @@ static void nfc_llcp_recv_hdlc(struct nfc_llcp_local *local,
 
                llcp_sock->send_ack_n = nr;
 
-               skb_queue_walk_safe(&llcp_sock->tx_pending_queue, s, tmp)
-                       if (nfc_llcp_ns(s) <= nr) {
-                               skb_unlink(s, &llcp_sock->tx_pending_queue);
-                               kfree_skb(s);
-                       }
+               /* Remove and free all skbs until ns == nr */
+               skb_queue_walk_safe(&llcp_sock->tx_pending_queue, s, tmp) {
+                       skb_unlink(s, &llcp_sock->tx_pending_queue);
+                       kfree_skb(s);
+
+                       if (nfc_llcp_ns(s) == nr)
+                               break;
+               }
+
+               /* Re-queue the remaining skbs for transmission */
+               skb_queue_reverse_walk_safe(&llcp_sock->tx_pending_queue,
+                                           s, tmp) {
+                       skb_unlink(s, &llcp_sock->tx_pending_queue);
+                       skb_queue_head(&local->tx_queue, s);
+               }
        }
 
        if (ptype == LLCP_PDU_RR)
@@ -740,7 +811,7 @@ static void nfc_llcp_recv_hdlc(struct nfc_llcp_local *local,
        else if (ptype == LLCP_PDU_RNR)
                llcp_sock->remote_ready = false;
 
-       if (nfc_llcp_queue_i_frames(llcp_sock) == 0)
+       if (nfc_llcp_queue_i_frames(llcp_sock) == 0 && ptype == LLCP_PDU_I)
                nfc_llcp_send_rr(llcp_sock);
 
        release_sock(sk);
@@ -791,11 +862,7 @@ static void nfc_llcp_recv_cc(struct nfc_llcp_local *local, struct sk_buff *skb)
        dsap = nfc_llcp_dsap(skb);
        ssap = nfc_llcp_ssap(skb);
 
-       llcp_sock = nfc_llcp_sock_get(local, dsap, ssap);
-
-       if (llcp_sock == NULL)
-               llcp_sock = nfc_llcp_sock_get(local, dsap, LLCP_SAP_SDP);
-
+       llcp_sock = nfc_llcp_connecting_sock_get(local, dsap);
        if (llcp_sock == NULL) {
                pr_err("Invalid CC\n");
                nfc_llcp_send_dm(local, dsap, ssap, LLCP_DM_NOCONN);
@@ -803,11 +870,15 @@ static void nfc_llcp_recv_cc(struct nfc_llcp_local *local, struct sk_buff *skb)
                return;
        }
 
-       llcp_sock->dsap = ssap;
        sk = &llcp_sock->sk;
 
-       nfc_llcp_parse_tlv(local, &skb->data[LLCP_HEADER_SIZE],
-                          skb->len - LLCP_HEADER_SIZE);
+       /* Unlink from connecting and link to the client array */
+       nfc_llcp_sock_unlink(&local->connecting_sockets, sk);
+       nfc_llcp_sock_link(&local->sockets, sk);
+       llcp_sock->dsap = ssap;
+
+       nfc_llcp_parse_connection_tlv(llcp_sock, &skb->data[LLCP_HEADER_SIZE],
+                                     skb->len - LLCP_HEADER_SIZE);
 
        sk->sk_state = LLCP_CONNECTED;
        sk->sk_state_change(sk);
@@ -891,6 +962,21 @@ void nfc_llcp_recv(void *data, struct sk_buff *skb, int err)
        return;
 }
 
+int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb)
+{
+       struct nfc_llcp_local *local;
+
+       local = nfc_llcp_find_local(dev);
+       if (local == NULL)
+               return -ENODEV;
+
+       local->rx_pending = skb_get(skb);
+       del_timer(&local->link_timer);
+       queue_work(local->rx_wq, &local->rx_work);
+
+       return 0;
+}
+
 void nfc_llcp_mac_is_down(struct nfc_dev *dev)
 {
        struct nfc_llcp_local *local;
@@ -943,8 +1029,8 @@ int nfc_llcp_register_device(struct nfc_dev *ndev)
 
        local->dev = ndev;
        INIT_LIST_HEAD(&local->list);
+       kref_init(&local->ref);
        mutex_init(&local->sdp_lock);
-       mutex_init(&local->socket_lock);
        init_timer(&local->link_timer);
        local->link_timer.data = (unsigned long) local;
        local->link_timer.function = nfc_llcp_symm_timer;
@@ -984,11 +1070,13 @@ int nfc_llcp_register_device(struct nfc_dev *ndev)
                goto err_rx_wq;
        }
 
+       local->sockets.lock = __RW_LOCK_UNLOCKED(local->sockets.lock);
+       local->connecting_sockets.lock = __RW_LOCK_UNLOCKED(local->connecting_sockets.lock);
+
        nfc_llcp_build_gb(local);
 
        local->remote_miu = LLCP_DEFAULT_MIU;
        local->remote_lto = LLCP_DEFAULT_LTO;
-       local->remote_rw = LLCP_DEFAULT_RW;
 
        list_add(&llcp_devices, &local->list);
 
@@ -1015,14 +1103,7 @@ void nfc_llcp_unregister_device(struct nfc_dev *dev)
                return;
        }
 
-       list_del(&local->list);
-       nfc_llcp_socket_release(local);
-       del_timer_sync(&local->link_timer);
-       skb_queue_purge(&local->tx_queue);
-       destroy_workqueue(local->tx_wq);
-       destroy_workqueue(local->rx_wq);
-       kfree_skb(local->rx_pending);
-       kfree(local);
+       nfc_llcp_local_put(local);
 }
 
 int __init nfc_llcp_init(void)
index 50680ce..7286c86 100644 (file)
@@ -40,12 +40,18 @@ enum llcp_state {
 
 struct nfc_llcp_sock;
 
+struct llcp_sock_list {
+       struct hlist_head head;
+       rwlock_t          lock;
+};
+
 struct nfc_llcp_local {
        struct list_head list;
        struct nfc_dev *dev;
 
+       struct kref ref;
+
        struct mutex sdp_lock;
-       struct mutex socket_lock;
 
        struct timer_list link_timer;
        struct sk_buff_head tx_queue;
@@ -77,24 +83,26 @@ struct nfc_llcp_local {
        u16 remote_lto;
        u8  remote_opt;
        u16 remote_wks;
-       u8  remote_rw;
 
        /* sockets array */
-       struct nfc_llcp_sock *sockets[LLCP_MAX_SAP];
+       struct llcp_sock_list sockets;
+       struct llcp_sock_list connecting_sockets;
 };
 
 struct nfc_llcp_sock {
        struct sock sk;
-       struct list_head list;
        struct nfc_dev *dev;
        struct nfc_llcp_local *local;
        u32 target_idx;
        u32 nfc_protocol;
 
+       /* Link parameters */
        u8 ssap;
        u8 dsap;
        char *service_name;
        size_t service_name_len;
+       u8 rw;
+       u16 miu;
 
        /* Link variables */
        u8 send_n;
@@ -164,7 +172,11 @@ struct nfc_llcp_sock {
 #define LLCP_DM_REJ     0x03
 
 
+void nfc_llcp_sock_link(struct llcp_sock_list *l, struct sock *s);
+void nfc_llcp_sock_unlink(struct llcp_sock_list *l, struct sock *s);
 struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev);
+struct nfc_llcp_local *nfc_llcp_local_get(struct nfc_llcp_local *local);
+int nfc_llcp_local_put(struct nfc_llcp_local *local);
 u8 nfc_llcp_get_sdp_ssap(struct nfc_llcp_local *local,
                         struct nfc_llcp_sock *sock);
 u8 nfc_llcp_get_local_ssap(struct nfc_llcp_local *local);
@@ -179,8 +191,10 @@ void nfc_llcp_accept_enqueue(struct sock *parent, struct sock *sk);
 struct sock *nfc_llcp_accept_dequeue(struct sock *sk, struct socket *newsock);
 
 /* TLV API */
-int nfc_llcp_parse_tlv(struct nfc_llcp_local *local,
-                      u8 *tlv_array, u16 tlv_array_len);
+int nfc_llcp_parse_gb_tlv(struct nfc_llcp_local *local,
+                         u8 *tlv_array, u16 tlv_array_len);
+int nfc_llcp_parse_connection_tlv(struct nfc_llcp_sock *sock,
+                                 u8 *tlv_array, u16 tlv_array_len);
 
 /* Commands API */
 void nfc_llcp_recv(void *data, struct sk_buff *skb, int err);
index 3f339b1..30e3cc7 100644 (file)
@@ -111,7 +111,7 @@ static int llcp_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
        }
 
        llcp_sock->dev = dev;
-       llcp_sock->local = local;
+       llcp_sock->local = nfc_llcp_local_get(local);
        llcp_sock->nfc_protocol = llcp_addr.nfc_protocol;
        llcp_sock->service_name_len = min_t(unsigned int,
                                            llcp_addr.service_name_len,
@@ -124,7 +124,7 @@ static int llcp_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
        if (llcp_sock->ssap == LLCP_MAX_SAP)
                goto put_dev;
 
-       local->sockets[llcp_sock->ssap] = llcp_sock;
+       nfc_llcp_sock_link(&local->sockets, sk);
 
        pr_debug("Socket bound to SAP %d\n", llcp_sock->ssap);
 
@@ -379,15 +379,6 @@ static int llcp_sock_release(struct socket *sock)
                goto out;
        }
 
-       mutex_lock(&local->socket_lock);
-
-       if (llcp_sock == local->sockets[llcp_sock->ssap])
-               local->sockets[llcp_sock->ssap] = NULL;
-       else
-               list_del_init(&llcp_sock->list);
-
-       mutex_unlock(&local->socket_lock);
-
        lock_sock(sk);
 
        /* Send a DISC */
@@ -412,14 +403,12 @@ static int llcp_sock_release(struct socket *sock)
                }
        }
 
-       /* Freeing the SAP */
-       if ((sk->sk_state == LLCP_CONNECTED
-            && llcp_sock->ssap > LLCP_LOCAL_SAP_OFFSET) ||
-           sk->sk_state == LLCP_BOUND || sk->sk_state == LLCP_LISTEN)
-               nfc_llcp_put_ssap(llcp_sock->local, llcp_sock->ssap);
+       nfc_llcp_put_ssap(llcp_sock->local, llcp_sock->ssap);
 
        release_sock(sk);
 
+       nfc_llcp_sock_unlink(&local->sockets, sk);
+
 out:
        sock_orphan(sk);
        sock_put(sk);
@@ -487,7 +476,8 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr,
        }
 
        llcp_sock->dev = dev;
-       llcp_sock->local = local;
+       llcp_sock->local = nfc_llcp_local_get(local);
+       llcp_sock->miu = llcp_sock->local->remote_miu;
        llcp_sock->ssap = nfc_llcp_get_local_ssap(local);
        if (llcp_sock->ssap == LLCP_SAP_MAX) {
                ret = -ENOMEM;
@@ -505,21 +495,26 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr,
                                          llcp_sock->service_name_len,
                                          GFP_KERNEL);
 
-       local->sockets[llcp_sock->ssap] = llcp_sock;
+       nfc_llcp_sock_link(&local->connecting_sockets, sk);
 
        ret = nfc_llcp_send_connect(llcp_sock);
        if (ret)
-               goto put_dev;
+               goto sock_unlink;
 
        ret = sock_wait_state(sk, LLCP_CONNECTED,
                              sock_sndtimeo(sk, flags & O_NONBLOCK));
        if (ret)
-               goto put_dev;
+               goto sock_unlink;
 
        release_sock(sk);
 
        return 0;
 
+sock_unlink:
+       nfc_llcp_put_ssap(local, llcp_sock->ssap);
+
+       nfc_llcp_sock_unlink(&local->connecting_sockets, sk);
+
 put_dev:
        nfc_put_device(dev);
 
@@ -684,13 +679,14 @@ struct sock *nfc_llcp_sock_alloc(struct socket *sock, int type, gfp_t gfp)
 
        llcp_sock->ssap = 0;
        llcp_sock->dsap = LLCP_SAP_SDP;
+       llcp_sock->rw = LLCP_DEFAULT_RW;
+       llcp_sock->miu = LLCP_DEFAULT_MIU;
        llcp_sock->send_n = llcp_sock->send_ack_n = 0;
        llcp_sock->recv_n = llcp_sock->recv_ack_n = 0;
        llcp_sock->remote_ready = 1;
        skb_queue_head_init(&llcp_sock->tx_queue);
        skb_queue_head_init(&llcp_sock->tx_pending_queue);
        skb_queue_head_init(&llcp_sock->tx_backlog_queue);
-       INIT_LIST_HEAD(&llcp_sock->list);
        INIT_LIST_HEAD(&llcp_sock->accept_queue);
 
        if (sock != NULL)
@@ -701,8 +697,6 @@ struct sock *nfc_llcp_sock_alloc(struct socket *sock, int type, gfp_t gfp)
 
 void nfc_llcp_sock_free(struct nfc_llcp_sock *sock)
 {
-       struct nfc_llcp_local *local = sock->local;
-
        kfree(sock->service_name);
 
        skb_queue_purge(&sock->tx_queue);
@@ -711,12 +705,9 @@ void nfc_llcp_sock_free(struct nfc_llcp_sock *sock)
 
        list_del_init(&sock->accept_queue);
 
-       if (local != NULL && sock == local->sockets[sock->ssap])
-               local->sockets[sock->ssap] = NULL;
-       else
-               list_del_init(&sock->list);
-
        sock->parent = NULL;
+
+       nfc_llcp_local_put(sock->local);
 }
 
 static int llcp_sock_create(struct net *net, struct socket *sock,
index d560e6f..766a02b 100644 (file)
@@ -387,7 +387,8 @@ static int nci_dev_down(struct nfc_dev *nfc_dev)
        return nci_close_device(ndev);
 }
 
-static int nci_start_poll(struct nfc_dev *nfc_dev, __u32 protocols)
+static int nci_start_poll(struct nfc_dev *nfc_dev,
+                         __u32 im_protocols, __u32 tm_protocols)
 {
        struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
        int rc;
@@ -413,11 +414,11 @@ static int nci_start_poll(struct nfc_dev *nfc_dev, __u32 protocols)
                        return -EBUSY;
        }
 
-       rc = nci_request(ndev, nci_rf_discover_req, protocols,
+       rc = nci_request(ndev, nci_rf_discover_req, im_protocols,
                         msecs_to_jiffies(NCI_RF_DISC_TIMEOUT));
 
        if (!rc)
-               ndev->poll_prots = protocols;
+               ndev->poll_prots = im_protocols;
 
        return rc;
 }
@@ -521,9 +522,9 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev,
        }
 }
 
-static int nci_data_exchange(struct nfc_dev *nfc_dev, struct nfc_target *target,
-                            struct sk_buff *skb,
-                            data_exchange_cb_t cb, void *cb_context)
+static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
+                         struct sk_buff *skb,
+                         data_exchange_cb_t cb, void *cb_context)
 {
        struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
        int rc;
@@ -556,7 +557,7 @@ static struct nfc_ops nci_nfc_ops = {
        .stop_poll = nci_stop_poll,
        .activate_target = nci_activate_target,
        .deactivate_target = nci_deactivate_target,
-       .data_exchange = nci_data_exchange,
+       .im_transceive = nci_transceive,
 };
 
 /* ---- Interface to NCI drivers ---- */
index 581d419..03c31db 100644 (file)
@@ -49,6 +49,8 @@ static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = {
        [NFC_ATTR_COMM_MODE] = { .type = NLA_U8 },
        [NFC_ATTR_RF_MODE] = { .type = NLA_U8 },
        [NFC_ATTR_DEVICE_POWERED] = { .type = NLA_U8 },
+       [NFC_ATTR_IM_PROTOCOLS] = { .type = NLA_U32 },
+       [NFC_ATTR_TM_PROTOCOLS] = { .type = NLA_U32 },
 };
 
 static int nfc_genl_send_target(struct sk_buff *msg, struct nfc_target *target,
@@ -219,6 +221,68 @@ free_msg:
        return -EMSGSIZE;
 }
 
+int nfc_genl_tm_activated(struct nfc_dev *dev, u32 protocol)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+                         NFC_EVENT_TM_ACTIVATED);
+       if (!hdr)
+               goto free_msg;
+
+       if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
+               goto nla_put_failure;
+       if (nla_put_u32(msg, NFC_ATTR_TM_PROTOCOLS, protocol))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+
+       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+free_msg:
+       nlmsg_free(msg);
+       return -EMSGSIZE;
+}
+
+int nfc_genl_tm_deactivated(struct nfc_dev *dev)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0,
+                         NFC_EVENT_TM_DEACTIVATED);
+       if (!hdr)
+               goto free_msg;
+
+       if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+
+       genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL);
+
+       return 0;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+free_msg:
+       nlmsg_free(msg);
+       return -EMSGSIZE;
+}
+
 int nfc_genl_device_added(struct nfc_dev *dev)
 {
        struct sk_buff *msg;
@@ -519,16 +583,25 @@ static int nfc_genl_start_poll(struct sk_buff *skb, struct genl_info *info)
        struct nfc_dev *dev;
        int rc;
        u32 idx;
-       u32 protocols;
+       u32 im_protocols = 0, tm_protocols = 0;
 
        pr_debug("Poll start\n");
 
        if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
-           !info->attrs[NFC_ATTR_PROTOCOLS])
+           ((!info->attrs[NFC_ATTR_IM_PROTOCOLS] &&
+             !info->attrs[NFC_ATTR_PROTOCOLS]) &&
+            !info->attrs[NFC_ATTR_TM_PROTOCOLS]))
                return -EINVAL;
 
        idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
-       protocols = nla_get_u32(info->attrs[NFC_ATTR_PROTOCOLS]);
+
+       if (info->attrs[NFC_ATTR_TM_PROTOCOLS])
+               tm_protocols = nla_get_u32(info->attrs[NFC_ATTR_TM_PROTOCOLS]);
+
+       if (info->attrs[NFC_ATTR_IM_PROTOCOLS])
+               im_protocols = nla_get_u32(info->attrs[NFC_ATTR_IM_PROTOCOLS]);
+       else if (info->attrs[NFC_ATTR_PROTOCOLS])
+               im_protocols = nla_get_u32(info->attrs[NFC_ATTR_PROTOCOLS]);
 
        dev = nfc_get_device(idx);
        if (!dev)
@@ -536,7 +609,7 @@ static int nfc_genl_start_poll(struct sk_buff *skb, struct genl_info *info)
 
        mutex_lock(&dev->genl_data.genl_data_mutex);
 
-       rc = nfc_start_poll(dev, protocols);
+       rc = nfc_start_poll(dev, im_protocols, tm_protocols);
        if (!rc)
                dev->genl_data.poll_req_pid = info->snd_pid;
 
index 3dd4232..c5e42b7 100644 (file)
@@ -55,6 +55,7 @@ int nfc_llcp_register_device(struct nfc_dev *dev);
 void nfc_llcp_unregister_device(struct nfc_dev *dev);
 int nfc_llcp_set_remote_gb(struct nfc_dev *dev, u8 *gb, u8 gb_len);
 u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len);
+int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb);
 int __init nfc_llcp_init(void);
 void nfc_llcp_exit(void);
 
@@ -90,6 +91,12 @@ static inline u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *gb_len)
        return NULL;
 }
 
+static inline int nfc_llcp_data_received(struct nfc_dev *dev,
+                                        struct sk_buff *skb)
+{
+       return 0;
+}
+
 static inline int nfc_llcp_init(void)
 {
        return 0;
@@ -128,6 +135,9 @@ int nfc_genl_dep_link_up_event(struct nfc_dev *dev, u32 target_idx,
                               u8 comm_mode, u8 rf_mode);
 int nfc_genl_dep_link_down_event(struct nfc_dev *dev);
 
+int nfc_genl_tm_activated(struct nfc_dev *dev, u32 protocol);
+int nfc_genl_tm_deactivated(struct nfc_dev *dev);
+
 struct nfc_dev *nfc_get_device(unsigned int idx);
 
 static inline void nfc_put_device(struct nfc_dev *dev)
@@ -158,7 +168,7 @@ int nfc_dev_up(struct nfc_dev *dev);
 
 int nfc_dev_down(struct nfc_dev *dev);
 
-int nfc_start_poll(struct nfc_dev *dev, u32 protocols);
+int nfc_start_poll(struct nfc_dev *dev, u32 im_protocols, u32 tm_protocols);
 
 int nfc_stop_poll(struct nfc_dev *dev);
 
index f974961..752b723 100644 (file)
@@ -325,7 +325,7 @@ static void __rfkill_switch_all(const enum rfkill_type type, bool blocked)
 
        rfkill_global_states[type].cur = blocked;
        list_for_each_entry(rfkill, &rfkill_list, node) {
-               if (rfkill->type != type)
+               if (rfkill->type != type && type != RFKILL_TYPE_ALL)
                        continue;
 
                rfkill_set_block(rfkill, blocked);
index 20b87d8..c1999e4 100644 (file)
@@ -78,50 +78,17 @@ bool cfg80211_can_beacon_sec_chan(struct wiphy *wiphy,
 }
 EXPORT_SYMBOL(cfg80211_can_beacon_sec_chan);
 
-int cfg80211_set_freq(struct cfg80211_registered_device *rdev,
-                     struct wireless_dev *wdev, int freq,
-                     enum nl80211_channel_type channel_type)
+int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
+                                int freq, enum nl80211_channel_type chantype)
 {
        struct ieee80211_channel *chan;
-       int result;
 
-       if (wdev && wdev->iftype == NL80211_IFTYPE_MONITOR)
-               wdev = NULL;
-
-       if (wdev) {
-               ASSERT_WDEV_LOCK(wdev);
-
-               if (!netif_running(wdev->netdev))
-                       return -ENETDOWN;
-       }
-
-       if (!rdev->ops->set_channel)
+       if (!rdev->ops->set_monitor_channel)
                return -EOPNOTSUPP;
 
-       chan = rdev_freq_to_chan(rdev, freq, channel_type);
+       chan = rdev_freq_to_chan(rdev, freq, chantype);
        if (!chan)
                return -EINVAL;
 
-       /* Both channels should be able to initiate communication */
-       if (wdev && (wdev->iftype == NL80211_IFTYPE_ADHOC ||
-                    wdev->iftype == NL80211_IFTYPE_AP ||
-                    wdev->iftype == NL80211_IFTYPE_AP_VLAN ||
-                    wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
-                    wdev->iftype == NL80211_IFTYPE_P2P_GO) &&
-           !cfg80211_can_beacon_sec_chan(&rdev->wiphy, chan, channel_type)) {
-               printk(KERN_DEBUG
-                      "cfg80211: Secondary channel not allowed to beacon\n");
-               return -EINVAL;
-       }
-
-       result = rdev->ops->set_channel(&rdev->wiphy,
-                                       wdev ? wdev->netdev : NULL,
-                                       chan, channel_type);
-       if (result)
-               return result;
-
-       if (wdev)
-               wdev->channel = chan;
-
-       return 0;
+       return rdev->ops->set_monitor_channel(&rdev->wiphy, chan, chantype);
 }
index 1d3d241..9348a47 100644 (file)
@@ -444,9 +444,8 @@ cfg80211_can_add_interface(struct cfg80211_registered_device *rdev,
 struct ieee80211_channel *
 rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
                  int freq, enum nl80211_channel_type channel_type);
-int cfg80211_set_freq(struct cfg80211_registered_device *rdev,
-                     struct wireless_dev *wdev, int freq,
-                     enum nl80211_channel_type channel_type);
+int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
+                                int freq, enum nl80211_channel_type chantype);
 
 int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,
                           const u8 *rates, unsigned int n_rates,
index d2a19b0..89baa33 100644 (file)
@@ -42,6 +42,7 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid)
        cfg80211_hold_bss(bss_from_pub(bss));
        wdev->current_bss = bss_from_pub(bss);
 
+       wdev->sme_state = CFG80211_SME_CONNECTED;
        cfg80211_upload_connect_keys(wdev);
 
        nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid,
@@ -60,7 +61,7 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp)
        struct cfg80211_event *ev;
        unsigned long flags;
 
-       CFG80211_DEV_WARN_ON(!wdev->ssid_len);
+       CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING);
 
        ev = kzalloc(sizeof(*ev), gfp);
        if (!ev)
@@ -115,9 +116,11 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
 #ifdef CONFIG_CFG80211_WEXT
        wdev->wext.ibss.channel = params->channel;
 #endif
+       wdev->sme_state = CFG80211_SME_CONNECTING;
        err = rdev->ops->join_ibss(&rdev->wiphy, dev, params);
        if (err) {
                wdev->connect_keys = NULL;
+               wdev->sme_state = CFG80211_SME_IDLE;
                return err;
        }
 
@@ -169,6 +172,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
        }
 
        wdev->current_bss = NULL;
+       wdev->sme_state = CFG80211_SME_IDLE;
        wdev->ssid_len = 0;
 #ifdef CONFIG_CFG80211_WEXT
        if (!nowext)
index 2e3b700..b44c736 100644 (file)
@@ -179,6 +179,13 @@ int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev,
 {
        struct ieee80211_channel *channel;
 
+       channel = rdev_freq_to_chan(rdev, freq, channel_type);
+       if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy,
+                                                     channel,
+                                                     channel_type)) {
+               return -EINVAL;
+       }
+
        /*
         * Workaround for libertas (only!), it puts the interface
         * into mesh mode but doesn't implement join_mesh. Instead,
@@ -186,27 +193,20 @@ int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev,
         * you set the channel. Note that the libertas mesh isn't
         * compatible with 802.11 mesh.
         */
-       if (!rdev->ops->join_mesh) {
-               int err;
+       if (rdev->ops->libertas_set_mesh_channel) {
+               if (channel_type != NL80211_CHAN_NO_HT)
+                       return -EINVAL;
 
                if (!netif_running(wdev->netdev))
                        return -ENETDOWN;
-               wdev_lock(wdev);
-               err = cfg80211_set_freq(rdev, wdev, freq, channel_type);
-               wdev_unlock(wdev);
-
-               return err;
+               return rdev->ops->libertas_set_mesh_channel(&rdev->wiphy,
+                                                           wdev->netdev,
+                                                           channel);
        }
 
        if (wdev->mesh_id_len)
                return -EBUSY;
 
-       channel = rdev_freq_to_chan(rdev, freq, channel_type);
-       if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy,
-                                                     channel,
-                                                     channel_type)) {
-               return -EINVAL;
-       }
        wdev->preset_chan = channel;
        wdev->preset_chantype = channel_type;
        return 0;
index eb90988..da4406f 100644 (file)
@@ -947,8 +947,6 @@ void cfg80211_ch_switch_notify(struct net_device *dev, int freq,
        if (WARN_ON(!chan))
                goto out;
 
-       wdev->channel = chan;
-
        nl80211_ch_switch_notify(rdev, dev, freq, type, GFP_KERNEL);
 out:
        wdev_unlock(wdev);
index b22f1f8..7ae54b8 100644 (file)
@@ -921,7 +921,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
                if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS))
                        goto nla_put_failure;
        }
-       if (dev->ops->set_channel || dev->ops->start_ap ||
+       if (dev->ops->set_monitor_channel || dev->ops->start_ap ||
            dev->ops->join_mesh) {
                i++;
                if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL))
@@ -1178,8 +1178,8 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
         * the channel in the start-ap or join-mesh commands instead.
         *
         * Monitors are special as they are normally slaved to
-        * whatever else is going on, so they behave as though
-        * you tried setting the wiphy channel itself.
+        * whatever else is going on, so they have their own special
+        * operation to set the monitor channel if possible.
         */
        return !wdev ||
                wdev->iftype == NL80211_IFTYPE_AP ||
@@ -1217,6 +1217,10 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
        enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
        u32 freq;
        int result;
+       enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR;
+
+       if (wdev)
+               iftype = wdev->iftype;
 
        if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
                return -EINVAL;
@@ -1231,7 +1235,7 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
        freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
 
        mutex_lock(&rdev->devlist_mtx);
-       if (wdev) switch (wdev->iftype) {
+       switch (iftype) {
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_P2P_GO:
                if (wdev->beacon_interval) {
@@ -1252,12 +1256,11 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
        case NL80211_IFTYPE_MESH_POINT:
                result = cfg80211_set_mesh_freq(rdev, wdev, freq, channel_type);
                break;
+       case NL80211_IFTYPE_MONITOR:
+               result = cfg80211_set_monitor_channel(rdev, freq, channel_type);
+               break;
        default:
-               wdev_lock(wdev);
-               result = cfg80211_set_freq(rdev, wdev, freq, channel_type);
-               wdev_unlock(wdev);
-       } else {
-               result = cfg80211_set_freq(rdev, NULL, freq, channel_type);
+               result = -EINVAL;
        }
        mutex_unlock(&rdev->devlist_mtx);
 
@@ -5542,18 +5545,18 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,
 
        duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
 
+       if (!rdev->ops->remain_on_channel ||
+           !(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL))
+               return -EOPNOTSUPP;
+
        /*
-        * We should be on that channel for at least one jiffie,
-        * and more than 5 seconds seems excessive.
+        * We should be on that channel for at least a minimum amount of
+        * time (10ms) but no longer than the driver supports.
         */
-       if (!duration || !msecs_to_jiffies(duration) ||
+       if (duration < NL80211_MIN_REMAIN_ON_CHANNEL_TIME ||
            duration > rdev->wiphy.max_remain_on_channel_duration)
                return -EINVAL;
 
-       if (!rdev->ops->remain_on_channel ||
-           !(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL))
-               return -EOPNOTSUPP;
-
        if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
            !nl80211_valid_channel_type(info, &channel_type))
                return -EINVAL;
@@ -5824,6 +5827,15 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
                if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
                        return -EINVAL;
                wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
+
+               /*
+                * We should wait on the channel for at least a minimum amount
+                * of time (10ms) but no longer than the driver supports.
+                */
+               if (wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME ||
+                   wait > rdev->wiphy.max_remain_on_channel_duration)
+                       return -EINVAL;
+
        }
 
        if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
index 55d9946..8f2d68f 100644 (file)
@@ -935,6 +935,7 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
                                  enum nl80211_iftype iftype)
 {
        struct wireless_dev *wdev_iter;
+       u32 used_iftypes = BIT(iftype);
        int num[NUM_NL80211_IFTYPES];
        int total = 1;
        int i, j;
@@ -961,6 +962,7 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
 
                num[wdev_iter->iftype]++;
                total++;
+               used_iftypes |= BIT(wdev_iter->iftype);
        }
        mutex_unlock(&rdev->devlist_mtx);
 
@@ -970,6 +972,7 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
        for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) {
                const struct ieee80211_iface_combination *c;
                struct ieee80211_iface_limit *limits;
+               u32 all_iftypes = 0;
 
                c = &rdev->wiphy.iface_combinations[i];
 
@@ -984,6 +987,7 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
                        if (rdev->wiphy.software_iftypes & BIT(iftype))
                                continue;
                        for (j = 0; j < c->n_limits; j++) {
+                               all_iftypes |= limits[j].types;
                                if (!(limits[j].types & BIT(iftype)))
                                        continue;
                                if (limits[j].max < num[iftype])
@@ -991,7 +995,20 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
                                limits[j].max -= num[iftype];
                        }
                }
-               /* yay, it fits */
+
+               /*
+                * Finally check that all iftypes that we're currently
+                * using are actually part of this combination. If they
+                * aren't then we can't use this combination and have
+                * to continue to the next.
+                */
+               if ((all_iftypes & used_iftypes) != used_iftypes)
+                       goto cont;
+
+               /*
+                * This combination covered all interface types and
+                * supported the requested numbers, so we're good.
+                */
                kfree(limits);
                return 0;
  cont:
index faeb035..bc87983 100644 (file)
@@ -802,9 +802,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
                if (freq == 0)
                        return -EINVAL;
                mutex_lock(&rdev->devlist_mtx);
-               wdev_lock(wdev);
-               err = cfg80211_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT);
-               wdev_unlock(wdev);
+               err = cfg80211_set_monitor_channel(rdev, freq, NL80211_CHAN_NO_HT);
                mutex_unlock(&rdev->devlist_mtx);
                return err;
        case NL80211_IFTYPE_MESH_POINT:
@@ -848,11 +846,7 @@ static int cfg80211_wext_giwfreq(struct net_device *dev,
                freq->e = 6;
                return 0;
        default:
-               if (!wdev->channel)
-                       return -EINVAL;
-               freq->m = wdev->channel->center_freq;
-               freq->e = 6;
-               return 0;
+               return -EINVAL;
        }
 }
 
index 7decbd3..1f773f6 100644 (file)
@@ -111,9 +111,15 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
 
        wdev->wext.connect.channel = chan;
 
-       /* SSID is not set, we just want to switch channel */
+       /*
+        * SSID is not set, we just want to switch monitor channel,
+        * this is really just backward compatibility, if the SSID
+        * is set then we use the channel to select the BSS to use
+        * to connect to instead. If we were connected on another
+        * channel we disconnected above and reconnect below.
+        */
        if (chan && !wdev->wext.connect.ssid_len) {
-               err = cfg80211_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT);
+               err = cfg80211_set_monitor_channel(rdev, freq, NL80211_CHAN_NO_HT);
                goto out;
        }