summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-05-29 21:56:46 +0200
committerJohannes Berg <johannes.berg@intel.com>2013-05-29 21:56:46 +0200
commit774439518ac01049e9bf44f2fa4c604981f39dbf (patch)
treeb324e2e129ee4d9dcb1c1466590ce71d087e8158
parentb571a69745dc90aec3d0505a7ceac2702a93861b (diff)
parenta910e4a94f6923c8c988565525f017f687bf7205 (diff)
Merge remote-tracking branch 'wireless-next/master' into iwlwifi-next
-rw-r--r--Documentation/DocBook/80211.tmpl2
-rw-r--r--MAINTAINERS5
-rw-r--r--drivers/bcma/sprom.c71
-rw-r--r--drivers/net/wireless/Kconfig1
-rw-r--r--drivers/net/wireless/Makefile2
-rw-r--r--drivers/net/wireless/ath/ath9k/Kconfig8
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_paprd.c19
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h2
-rw-r--r--drivers/net/wireless/ath/ath9k/beacon.c8
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.c337
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.h20
-rw-r--r--drivers/net/wireless/ath/ath9k/htc.h7
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_beacon.c5
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_init.c7
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_main.c29
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_txrx.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/init.c3
-rw-r--r--drivers/net/wireless/ath/ath9k/link.c1
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c13
-rw-r--r--drivers/net/wireless/ath/wil6210/Kconfig12
-rw-r--r--drivers/net/wireless/ath/wil6210/Makefile21
-rw-r--r--drivers/net/wireless/ath/wil6210/cfg80211.c4
-rw-r--r--drivers/net/wireless/ath/wil6210/debug.c69
-rw-r--r--drivers/net/wireless/ath/wil6210/debugfs.c8
-rw-r--r--drivers/net/wireless/ath/wil6210/interrupt.c29
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c24
-rw-r--r--drivers/net/wireless/ath/wil6210/netdev.c54
-rw-r--r--drivers/net/wireless/ath/wil6210/trace.c20
-rw-r--r--drivers/net/wireless/ath/wil6210/trace.h235
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.c175
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.h32
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h27
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c13
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c3
-rw-r--r--drivers/net/wireless/cw1200/Kconfig46
-rw-r--r--drivers/net/wireless/cw1200/Makefile24
-rw-r--r--drivers/net/wireless/cw1200/bh.c616
-rw-r--r--drivers/net/wireless/cw1200/bh.h28
-rw-r--r--drivers/net/wireless/cw1200/cw1200.h332
-rw-r--r--drivers/net/wireless/cw1200/cw1200_sagrad.c145
-rw-r--r--drivers/net/wireless/cw1200/cw1200_sdio.c409
-rw-r--r--drivers/net/wireless/cw1200/cw1200_spi.c480
-rw-r--r--drivers/net/wireless/cw1200/debug.c664
-rw-r--r--drivers/net/wireless/cw1200/debug.h98
-rw-r--r--drivers/net/wireless/cw1200/fwio.c525
-rw-r--r--drivers/net/wireless/cw1200/fwio.h39
-rw-r--r--drivers/net/wireless/cw1200/hwio.c311
-rw-r--r--drivers/net/wireless/cw1200/hwio.h247
-rw-r--r--drivers/net/wireless/cw1200/itp.c730
-rw-r--r--drivers/net/wireless/cw1200/itp.h144
-rw-r--r--drivers/net/wireless/cw1200/main.c618
-rw-r--r--drivers/net/wireless/cw1200/pm.c367
-rw-r--r--drivers/net/wireless/cw1200/pm.h38
-rw-r--r--drivers/net/wireless/cw1200/queue.c583
-rw-r--r--drivers/net/wireless/cw1200/queue.h116
-rw-r--r--drivers/net/wireless/cw1200/sbus.h37
-rw-r--r--drivers/net/wireless/cw1200/scan.c461
-rw-r--r--drivers/net/wireless/cw1200/scan.h56
-rw-r--r--drivers/net/wireless/cw1200/sta.c2406
-rw-r--r--drivers/net/wireless/cw1200/sta.h123
-rw-r--r--drivers/net/wireless/cw1200/txrx.c1474
-rw-r--r--drivers/net/wireless/cw1200/txrx.h106
-rw-r--r--drivers/net/wireless/cw1200/wsm.c1884
-rw-r--r--drivers/net/wireless/cw1200/wsm.h1879
-rw-r--r--drivers/net/wireless/iwlegacy/commands.h8
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/commands.h8
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c14
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/tx.c6
-rw-r--r--drivers/net/wireless/mac80211_hwsim.c22
-rw-r--r--drivers/net/wireless/mwifiex/Kconfig4
-rw-r--r--drivers/net/wireless/mwifiex/cfg80211.c77
-rw-r--r--drivers/net/wireless/mwifiex/cmdevt.c1
-rw-r--r--drivers/net/wireless/mwifiex/fw.h17
-rw-r--r--drivers/net/wireless/mwifiex/init.c53
-rw-r--r--drivers/net/wireless/mwifiex/join.c5
-rw-r--r--drivers/net/wireless/mwifiex/main.c14
-rw-r--r--drivers/net/wireless/mwifiex/main.h5
-rw-r--r--drivers/net/wireless/mwifiex/scan.c23
-rw-r--r--drivers/net/wireless/mwifiex/sdio.c463
-rw-r--r--drivers/net/wireless/mwifiex/sdio.h340
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmd.c62
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmdresp.c17
-rw-r--r--drivers/net/wireless/mwifiex/uap_cmd.c21
-rw-r--r--drivers/net/wireless/mwifiex/uap_event.c25
-rw-r--r--drivers/net/wireless/mwl8k.c11
-rw-r--r--drivers/net/wireless/p54/p54spi.c37
-rw-r--r--drivers/net/wireless/rt2x00/rt2800lib.c782
-rw-r--r--drivers/net/wireless/rt2x00/rt2800pci.c2
-rw-r--r--drivers/net/wireless/rt2x00/rt2800usb.c2
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00dev.c36
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00pci.c9
-rw-r--r--drivers/net/wireless/rt2x00/rt61pci.c2
-rw-r--r--include/linux/cw1200_platform.h46
-rw-r--r--include/linux/ieee80211.h9
-rw-r--r--include/linux/ssb/ssb_regs.h1
-rw-r--r--include/net/cfg80211.h86
-rw-r--r--include/net/ieee80211_radiotap.h7
-rw-r--r--include/net/mac80211.h26
-rw-r--r--include/uapi/linux/nl80211.h20
-rw-r--r--net/mac80211/aes_ccm.c6
-rw-r--r--net/mac80211/cfg.c20
-rw-r--r--net/mac80211/debugfs_netdev.c15
-rw-r--r--net/mac80211/ht.c4
-rw-r--r--net/mac80211/ibss.c49
-rw-r--r--net/mac80211/ieee80211_i.h29
-rw-r--r--net/mac80211/iface.c18
-rw-r--r--net/mac80211/key.c24
-rw-r--r--net/mac80211/key.h15
-rw-r--r--net/mac80211/main.c4
-rw-r--r--net/mac80211/mesh.c38
-rw-r--r--net/mac80211/mesh_plink.c7
-rw-r--r--net/mac80211/mlme.c410
-rw-r--r--net/mac80211/rate.c9
-rw-r--r--net/mac80211/rx.c33
-rw-r--r--net/mac80211/sta_info.c2
-rw-r--r--net/mac80211/sta_info.h5
-rw-r--r--net/mac80211/tkip.c4
-rw-r--r--net/mac80211/util.c37
-rw-r--r--net/mac80211/wep.c48
-rw-r--r--net/mac80211/wpa.c68
-rw-r--r--net/wireless/core.c237
-rw-r--r--net/wireless/core.h93
-rw-r--r--net/wireless/debugfs.c4
-rw-r--r--net/wireless/ibss.c10
-rw-r--r--net/wireless/mesh.c3
-rw-r--r--net/wireless/mlme.c216
-rw-r--r--net/wireless/nl80211.c346
-rw-r--r--net/wireless/reg.c136
-rw-r--r--net/wireless/scan.c47
-rw-r--r--net/wireless/sme.c113
-rw-r--r--net/wireless/sysfs.c8
-rw-r--r--net/wireless/trace.h27
-rw-r--r--net/wireless/util.c39
-rw-r--r--net/wireless/wext-compat.c22
-rw-r--r--net/wireless/wext-sme.c41
136 files changed, 18099 insertions, 2607 deletions
diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl
index 0f6a3edcd44..ebe89694cf8 100644
--- a/Documentation/DocBook/80211.tmpl
+++ b/Documentation/DocBook/80211.tmpl
@@ -132,9 +132,7 @@
!Finclude/net/cfg80211.h cfg80211_send_rx_assoc
!Finclude/net/cfg80211.h cfg80211_send_assoc_timeout
!Finclude/net/cfg80211.h cfg80211_send_deauth
-!Finclude/net/cfg80211.h __cfg80211_send_deauth
!Finclude/net/cfg80211.h cfg80211_send_disassoc
-!Finclude/net/cfg80211.h __cfg80211_send_disassoc
!Finclude/net/cfg80211.h cfg80211_ibss_joined
!Finclude/net/cfg80211.h cfg80211_connect_result
!Finclude/net/cfg80211.h cfg80211_roamed
diff --git a/MAINTAINERS b/MAINTAINERS
index 3d7782b9f90..e5069c22339 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2299,6 +2299,11 @@ M: Jaya Kumar <jayakumar.alsa@gmail.com>
S: Maintained
F: sound/pci/cs5535audio/
+CW1200 WLAN driver
+M: Solomon Peachy <pizza@shaftnet.org>
+S: Maintained
+F: drivers/net/wireless/cw1200/
+
CX18 VIDEO4LINUX DRIVER
M: Andy Walls <awalls@md.metrocast.net>
L: ivtv-devel@ivtvdriver.org (moderated for non-subscribers)
diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c
index 8934298a638..de15b4f4b23 100644
--- a/drivers/bcma/sprom.c
+++ b/drivers/bcma/sprom.c
@@ -72,12 +72,12 @@ fail:
* R/W ops.
**************************************************/
-static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom)
+static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom,
+ size_t words)
{
int i;
- for (i = 0; i < SSB_SPROMSIZE_WORDS_R4; i++)
- sprom[i] = bcma_read16(bus->drv_cc.core,
- offset + (i * 2));
+ for (i = 0; i < words; i++)
+ sprom[i] = bcma_read16(bus->drv_cc.core, offset + (i * 2));
}
/**************************************************
@@ -124,29 +124,29 @@ static inline u8 bcma_crc8(u8 crc, u8 data)
return t[crc ^ data];
}
-static u8 bcma_sprom_crc(const u16 *sprom)
+static u8 bcma_sprom_crc(const u16 *sprom, size_t words)
{
int word;
u8 crc = 0xFF;
- for (word = 0; word < SSB_SPROMSIZE_WORDS_R4 - 1; word++) {
+ for (word = 0; word < words - 1; word++) {
crc = bcma_crc8(crc, sprom[word] & 0x00FF);
crc = bcma_crc8(crc, (sprom[word] & 0xFF00) >> 8);
}
- crc = bcma_crc8(crc, sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & 0x00FF);
+ crc = bcma_crc8(crc, sprom[words - 1] & 0x00FF);
crc ^= 0xFF;
return crc;
}
-static int bcma_sprom_check_crc(const u16 *sprom)
+static int bcma_sprom_check_crc(const u16 *sprom, size_t words)
{
u8 crc;
u8 expected_crc;
u16 tmp;
- crc = bcma_sprom_crc(sprom);
- tmp = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_CRC;
+ crc = bcma_sprom_crc(sprom, words);
+ tmp = sprom[words - 1] & SSB_SPROM_REVISION_CRC;
expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT;
if (crc != expected_crc)
return -EPROTO;
@@ -154,21 +154,25 @@ static int bcma_sprom_check_crc(const u16 *sprom)
return 0;
}
-static int bcma_sprom_valid(const u16 *sprom)
+static int bcma_sprom_valid(struct bcma_bus *bus, const u16 *sprom,
+ size_t words)
{
u16 revision;
int err;
- err = bcma_sprom_check_crc(sprom);
+ err = bcma_sprom_check_crc(sprom, words);
if (err)
return err;
- revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_REV;
- if (revision != 8 && revision != 9) {
+ revision = sprom[words - 1] & SSB_SPROM_REVISION_REV;
+ if (revision != 8 && revision != 9 && revision != 10) {
pr_err("Unsupported SPROM revision: %d\n", revision);
return -ENOENT;
}
+ bus->sprom.revision = revision;
+ bcma_debug(bus, "Found SPROM revision %d\n", revision);
+
return 0;
}
@@ -208,9 +212,6 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
ARRAY_SIZE(bus->sprom.core_pwr_info));
- bus->sprom.revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] &
- SSB_SPROM_REVISION_REV;
-
for (i = 0; i < 3; i++) {
v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i];
*(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v);
@@ -502,7 +503,6 @@ static bool bcma_sprom_onchip_available(struct bcma_bus *bus)
case BCMA_CHIP_ID_BCM4331:
present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT;
break;
-
case BCMA_CHIP_ID_BCM43224:
case BCMA_CHIP_ID_BCM43225:
/* for these chips OTP is always available */
@@ -550,7 +550,9 @@ int bcma_sprom_get(struct bcma_bus *bus)
{
u16 offset = BCMA_CC_SPROM;
u16 *sprom;
- int err = 0;
+ size_t sprom_sizes[] = { SSB_SPROMSIZE_WORDS_R4,
+ SSB_SPROMSIZE_WORDS_R10, };
+ int i, err = 0;
if (!bus->drv_cc.core)
return -EOPNOTSUPP;
@@ -579,32 +581,37 @@ int bcma_sprom_get(struct bcma_bus *bus)
}
}
- sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
- GFP_KERNEL);
- if (!sprom)
- return -ENOMEM;
-
if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 ||
bus->chipinfo.id == BCMA_CHIP_ID_BCM43431)
bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false);
bcma_debug(bus, "SPROM offset 0x%x\n", offset);
- bcma_sprom_read(bus, offset, sprom);
+ for (i = 0; i < ARRAY_SIZE(sprom_sizes); i++) {
+ size_t words = sprom_sizes[i];
+
+ sprom = kcalloc(words, sizeof(u16), GFP_KERNEL);
+ if (!sprom)
+ return -ENOMEM;
+
+ bcma_sprom_read(bus, offset, sprom, words);
+ err = bcma_sprom_valid(bus, sprom, words);
+ if (!err)
+ break;
+
+ kfree(sprom);
+ }
if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 ||
bus->chipinfo.id == BCMA_CHIP_ID_BCM43431)
bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true);
- err = bcma_sprom_valid(sprom);
if (err) {
- bcma_warn(bus, "invalid sprom read from the PCIe card, try to use fallback sprom\n");
+ bcma_warn(bus, "Invalid SPROM read from the PCIe card, trying to use fallback SPROM\n");
err = bcma_fill_sprom_with_fallback(bus, &bus->sprom);
- goto out;
+ } else {
+ bcma_sprom_extract_r8(bus, sprom);
+ kfree(sprom);
}
- bcma_sprom_extract_r8(bus, sprom);
-
-out:
- kfree(sprom);
return err;
}
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index f8f0156dff4..200020eb300 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -280,5 +280,6 @@ source "drivers/net/wireless/rtlwifi/Kconfig"
source "drivers/net/wireless/ti/Kconfig"
source "drivers/net/wireless/zd1211rw/Kconfig"
source "drivers/net/wireless/mwifiex/Kconfig"
+source "drivers/net/wireless/cw1200/Kconfig"
endif # WLAN
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 67156efe14c..0fab227025b 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -57,3 +57,5 @@ obj-$(CONFIG_MWIFIEX) += mwifiex/
obj-$(CONFIG_BRCMFMAC) += brcm80211/
obj-$(CONFIG_BRCMSMAC) += brcm80211/
+
+obj-$(CONFIG_CW1200) += cw1200/
diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig
index 17507dc8a1e..ec33c8007b2 100644
--- a/drivers/net/wireless/ath/ath9k/Kconfig
+++ b/drivers/net/wireless/ath/ath9k/Kconfig
@@ -84,14 +84,6 @@ config ATH9K_DFS_CERTIFIED
developed. At this point enabling this option won't do anything
except increase code size.
-config ATH9K_MAC_DEBUG
- bool "Atheros MAC statistics"
- depends on ATH9K_DEBUGFS
- default y
- ---help---
- This option enables collection of statistics for Rx/Tx status
- data and some other MAC related statistics
-
config ATH9K_RATE_CONTROL
bool "Atheros ath9k rate control"
depends on ATH9K
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c
index 09c1f9da67a..6343cc91953 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c
@@ -454,6 +454,8 @@ static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain)
if (accum_cnt <= thresh_accum_cnt)
continue;
+ max_index++;
+
/* sum(tx amplitude) */
accum_tx = ((data_L[i] >> 16) & 0xffff) |
((data_U[i] & 0x7ff) << 16);
@@ -468,20 +470,21 @@ static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain)
accum_tx <<= scale_factor;
accum_rx <<= scale_factor;
- x_est[i + 1] = (((accum_tx + accum_cnt) / accum_cnt) + 32) >>
- scale_factor;
+ x_est[max_index] =
+ (((accum_tx + accum_cnt) / accum_cnt) + 32) >>
+ scale_factor;
- Y[i + 1] = ((((accum_rx + accum_cnt) / accum_cnt) + 32) >>
+ Y[max_index] =
+ ((((accum_rx + accum_cnt) / accum_cnt) + 32) >>
scale_factor) +
- (1 << scale_factor) * max_index + 16;
+ (1 << scale_factor) * i + 16;
if (accum_ang >= (1 << 26))
accum_ang -= 1 << 27;
- theta[i + 1] = ((accum_ang * (1 << scale_factor)) + accum_cnt) /
- accum_cnt;
-
- max_index++;
+ theta[max_index] =
+ ((accum_ang * (1 << scale_factor)) + accum_cnt) /
+ accum_cnt;
}
/*
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 8a1888d0207..579ed9c40b3 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -646,6 +646,7 @@ enum sc_op_flags {
SC_OP_ANI_RUN,
SC_OP_PRIM_STA_VIF,
SC_OP_HW_RESET,
+ SC_OP_SCANNING,
};
/* Powersave flags */
@@ -759,7 +760,6 @@ struct ath_softc {
struct rchan *rfs_chan_spec_scan;
enum spectral_mode spectral_mode;
struct ath_spec_scan spec_config;
- int scanning;
#ifdef CONFIG_PM_SLEEP
atomic_t wow_got_bmiss_intr;
diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index 2ff570f7f8f..fd1eebab864 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -39,7 +39,8 @@ static void ath9k_beaconq_config(struct ath_softc *sc)
ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi);
- if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
+ if (sc->sc_ah->opmode == NL80211_IFTYPE_AP ||
+ sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) {
/* Always burst out beacon and CAB traffic. */
qi.tqi_aifs = 1;
qi.tqi_cwmin = 0;
@@ -273,7 +274,8 @@ static int ath9k_beacon_choose_slot(struct ath_softc *sc)
u64 tsf;
int slot;
- if (sc->sc_ah->opmode != NL80211_IFTYPE_AP) {
+ if (sc->sc_ah->opmode != NL80211_IFTYPE_AP &&
+ sc->sc_ah->opmode != NL80211_IFTYPE_MESH_POINT) {
ath_dbg(common, BEACON, "slot 0, tsf: %llu\n",
ath9k_hw_gettsf64(sc->sc_ah));
return 0;
@@ -765,10 +767,10 @@ void ath9k_set_beacon(struct ath_softc *sc)
switch (sc->sc_ah->opmode) {
case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_MESH_POINT:
ath9k_beacon_config_ap(sc, cur_conf);
break;
case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_MESH_POINT:
ath9k_beacon_config_adhoc(sc, cur_conf);
break;
case NL80211_IFTYPE_STATION:
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index e6307b86363..fc96ad3a135 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -738,8 +738,6 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf,
struct ath_tx_status *ts, struct ath_txq *txq,
unsigned int flags)
{
-#define TX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].ts\
- [sc->debug.tsidx].c)
int qnum = txq->axq_qnum;
TX_STAT_INC(qnum, tx_pkts_all);
@@ -771,37 +769,6 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf,
TX_STAT_INC(qnum, data_underrun);
if (ts->ts_flags & ATH9K_TX_DELIM_UNDERRUN)
TX_STAT_INC(qnum, delim_underrun);
-
-#ifdef CONFIG_ATH9K_MAC_DEBUG
- spin_lock(&sc->debug.samp_lock);
- TX_SAMP_DBG(jiffies) = jiffies;
- TX_SAMP_DBG(rssi_ctl0) = ts->ts_rssi_ctl0;
- TX_SAMP_DBG(rssi_ctl1) = ts->ts_rssi_ctl1;
- TX_SAMP_DBG(rssi_ctl2) = ts->ts_rssi_ctl2;
- TX_SAMP_DBG(rssi_ext0) = ts->ts_rssi_ext0;
- TX_SAMP_DBG(rssi_ext1) = ts->ts_rssi_ext1;
- TX_SAMP_DBG(rssi_ext2) = ts->ts_rssi_ext2;
- TX_SAMP_DBG(rateindex) = ts->ts_rateindex;
- TX_SAMP_DBG(isok) = !!(ts->ts_status & ATH9K_TXERR_MASK);
- TX_SAMP_DBG(rts_fail_cnt) = ts->ts_shortretry;
- TX_SAMP_DBG(data_fail_cnt) = ts->ts_longretry;
- TX_SAMP_DBG(rssi) = ts->ts_rssi;
- TX_SAMP_DBG(tid) = ts->tid;
- TX_SAMP_DBG(qid) = ts->qid;
-
- if (ts->ts_flags & ATH9K_TX_BA) {
- TX_SAMP_DBG(ba_low) = ts->ba_low;
- TX_SAMP_DBG(ba_high) = ts->ba_high;
- } else {
- TX_SAMP_DBG(ba_low) = 0;
- TX_SAMP_DBG(ba_high) = 0;
- }
-
- sc->debug.tsidx = (sc->debug.tsidx + 1) % ATH_DBG_MAX_SAMPLES;
- spin_unlock(&sc->debug.samp_lock);
-#endif
-
-#undef TX_SAMP_DBG
}
static const struct file_operations fops_xmit = {
@@ -915,8 +882,6 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf,
void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs)
{
#define RX_PHY_ERR_INC(c) sc->debug.stats.rxstats.phy_err_stats[c]++
-#define RX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].rs\
- [sc->debug.rsidx].c)
RX_STAT_INC(rx_pkts_all);
sc->debug.stats.rxstats.rx_bytes_all += rs->rs_datalen;
@@ -940,27 +905,7 @@ void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs)
RX_PHY_ERR_INC(rs->rs_phyerr);
}
-#ifdef CONFIG_ATH9K_MAC_DEBUG
- spin_lock(&sc->debug.samp_lock);
- RX_SAMP_DBG(jiffies) = jiffies;
- RX_SAMP_DBG(rssi_ctl0) = rs->rs_rssi_ctl0;
- RX_SAMP_DBG(rssi_ctl1) = rs->rs_rssi_ctl1;
- RX_SAMP_DBG(rssi_ctl2) = rs->rs_rssi_ctl2;
- RX_SAMP_DBG(rssi_ext0) = rs->rs_rssi_ext0;
- RX_SAMP_DBG(rssi_ext1) = rs->rs_rssi_ext1;
- RX_SAMP_DBG(rssi_ext2) = rs->rs_rssi_ext2;
- RX_SAMP_DBG(antenna) = rs->rs_antenna;
- RX_SAMP_DBG(rssi) = rs->rs_rssi;
- RX_SAMP_DBG(rate) = rs->rs_rate;
- RX_SAMP_DBG(is_mybeacon) = rs->is_mybeacon;
-
- sc->debug.rsidx = (sc->debug.rsidx + 1) % ATH_DBG_MAX_SAMPLES;
- spin_unlock(&sc->debug.samp_lock);
-
-#endif
-
#undef RX_PHY_ERR_INC
-#undef RX_SAMP_DBG
}
static const struct file_operations fops_recv = {
@@ -1485,283 +1430,6 @@ static const struct file_operations fops_modal_eeprom = {
.llseek = default_llseek,
};
-#ifdef CONFIG_ATH9K_MAC_DEBUG
-
-void ath9k_debug_samp_bb_mac(struct ath_softc *sc)
-{
-#define ATH_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].c)
- struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
- unsigned long flags;
- int i;
-
- ath9k_ps_wakeup(sc);
-
- spin_lock_bh(&sc->debug.samp_lock);
-
- spin_lock_irqsave(&common->cc_lock, flags);
- ath_hw_cycle_counters_update(common);
-
- ATH_SAMP_DBG(cc.cycles) = common->cc_ani.cycles;
- ATH_SAMP_DBG(cc.rx_busy) = common->cc_ani.rx_busy;
- ATH_SAMP_DBG(cc.rx_frame) = common->cc_ani.rx_frame;
- ATH_SAMP_DBG(cc.tx_frame) = common->cc_ani.tx_frame;
- spin_unlock_irqrestore(&common->cc_lock, flags);
-
- ATH_SAMP_DBG(noise) = ah->noise;
-
- REG_WRITE_D(ah, AR_MACMISC,
- ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) |
- (AR_MACMISC_MISC_OBS_BUS_1 <<
- AR_MACMISC_MISC_OBS_BUS_MSB_S)));
-
- for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++)
- ATH_SAMP_DBG(dma_dbg_reg_vals[i]) = REG_READ_D(ah,
- AR_DMADBG_0 + (i * sizeof(u32)));
-
- ATH_SAMP_DBG(pcu_obs) = REG_READ_D(ah, AR_OBS_BUS_1);
- ATH_SAMP_DBG(pcu_cr) = REG_READ_D(ah, AR_CR);
-
- memcpy(ATH_SAMP_DBG(nfCalHist), sc->caldata.nfCalHist,
- sizeof(ATH_SAMP_DBG(nfCalHist)));
-
- sc->debug.sampidx = (sc->debug.sampidx + 1) % ATH_DBG_MAX_SAMPLES;
- spin_unlock_bh(&sc->debug.samp_lock);
- ath9k_ps_restore(sc);
-
-#undef ATH_SAMP_DBG
-}
-
-static int open_file_bb_mac_samps(struct inode *inode, struct file *file)
-{
-#define ATH_SAMP_DBG(c) bb_mac_samp[sampidx].c
- struct ath_softc *sc = inode->i_private;
- struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
- struct ieee80211_conf *conf = &common->hw->conf;
- struct ath_dbg_bb_mac_samp *bb_mac_samp;
- struct ath9k_nfcal_hist *h;
- int i, j, qcuOffset = 0, dcuOffset = 0;
- u32 *qcuBase, *dcuBase, size = 30000, len = 0;
- u32 sampidx = 0;
- u8 *buf;
- u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
- u8 nread;
-
- if (test_bit(SC_OP_INVALID, &sc->sc_flags))
- return -EAGAIN;
-
- buf = vmalloc(size);
- if (!buf)
- return -ENOMEM;
- bb_mac_samp = vmalloc(sizeof(*bb_mac_samp) * ATH_DBG_MAX_SAMPLES);
- if (!bb_mac_samp) {
- vfree(buf);
- return -ENOMEM;
- }
- /* Account the current state too */
- ath9k_debug_samp_bb_mac(sc);
-
- spin_lock_bh(&sc->debug.samp_lock);
- memcpy(bb_mac_samp, sc->debug.bb_mac_samp,
- sizeof(*bb_mac_samp) * ATH_DBG_MAX_SAMPLES);
- len += snprintf(buf + len, size - len,
- "Current Sample Index: %d\n", sc->debug.sampidx);
- spin_unlock_bh(&sc->debug.samp_lock);
-
- len += snprintf(buf + len, size - len,
- "Raw DMA Debug Dump:\n");
- len += snprintf(buf + len, size - len, "Sample |\t");
- for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++)
- len += snprintf(buf + len, size - len, " DMA Reg%d |\t", i);
- len += snprintf(buf + len, size - len, "\n");
-
- for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
- len += snprintf(buf + len, size - len, "%d\t", sampidx);
-
- for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++)
- len += snprintf(buf + len, size - len, " %08x\t",
- ATH_SAMP_DBG(dma_dbg_reg_vals[i]));
- len += snprintf(buf + len, size - len, "\n");
- }
- len += snprintf(buf + len, size - len, "\n");
-
- len += snprintf(buf + len, size - len,
- "Sample Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n");
- for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
- qcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[0]);
- dcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[4]);
-
- for (i = 0; i < ATH9K_NUM_QUEUES; i++,
- qcuOffset += 4, dcuOffset += 5) {
- if (i == 8) {
- qcuOffset = 0;
- qcuBase++;
- }
-
- if (i == 6) {
- dcuOffset = 0;
- dcuBase++;
- }
- if (!sc->debug.stats.txstats[i].queued)
- continue;
-
- len += snprintf(buf + len, size - len,
- "%4d %7d %2x %1x %2x %2x\n",
- sampidx, i,
- (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset,
- (*qcuBase & (0x8 << qcuOffset)) >>
- (qcuOffset + 3),
- ATH_SAMP_DBG(dma_dbg_reg_vals[2]) &
- (0x7 << (i * 3)) >> (i * 3),
- (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset);
- }
- len += snprintf(buf + len, size - len, "\n");
- }
- len += snprintf(buf + len, size - len,
- "samp qcu_sh qcu_fh qcu_comp dcu_comp dcu_arb dcu_fp "
- "ch_idle_dur ch_idle_dur_val txfifo_val0 txfifo_val1 "
- "txfifo_dcu0 txfifo_dcu1 pcu_obs AR_CR\n");
-
- for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
- qcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[0]);
- dcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[4]);
-
- len += snprintf(buf + len, size - len, "%4d %5x %5x ", sampidx,
- (ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x003c0000) >> 18,
- (ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x03c00000) >> 22);
- len += snprintf(buf + len, size - len, "%7x %8x ",
- (ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x1c000000) >> 26,
- (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x3));
- len += snprintf(buf + len, size - len, "%7x %7x ",
- (ATH_SAMP_DBG(dma_dbg_reg_vals[5]) & 0x06000000) >> 25,
- (ATH_SAMP_DBG(dma_dbg_reg_vals[5]) & 0x38000000) >> 27);
- len += snprintf(buf + len, size - len, "%7d %12d ",
- (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x000003fc) >> 2,
- (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00000400) >> 10);
- len += snprintf(buf + len, size - len, "%12d %12d ",
- (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00000800) >> 11,
- (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00001000) >> 12);
- len += snprintf(buf + len, size - len, "%12d %12d ",
- (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x0001e000) >> 13,
- (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x001e0000) >> 17);
- len += snprintf(buf + len, size - len, "0x%07x 0x%07x\n",
- ATH_SAMP_DBG(pcu_obs), ATH_SAMP_DBG(pcu_cr));
- }
-
- len += snprintf(buf + len, size - len,
- "Sample ChNoise Chain privNF #Reading Readings\n");
- for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
- h = ATH_SAMP_DBG(nfCalHist);
- if (!ATH_SAMP_DBG(noise))
- continue;
-
- for (i = 0; i < NUM_NF_READINGS; i++) {
- if (!(chainmask & (1 << i)) ||
- ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)))
- continue;
-
- nread = AR_PHY_CCA_FILTERWINDOW_LENGTH -
- h[i].invalidNFcount;
- len += snprintf(buf + len, size - len,
- "%4d %5d %4d\t %d\t %d\t",
- sampidx, ATH_SAMP_DBG(noise),
- i, h[i].privNF, nread);
- for (j = 0; j < nread; j++)
- len += snprintf(buf + len, size - len,
- " %d", h[i].nfCalBuffer[j]);
- len += snprintf(buf + len, size - len, "\n");
- }
- }
- len += snprintf(buf + len, size - len, "\nCycle counters:\n"
- "Sample Total Rxbusy Rxframes Txframes\n");
- for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
- if (!ATH_SAMP_DBG(cc.cycles))
- continue;
- len += snprintf(buf + len, size - len,
- "%4d %08x %08x %08x %08x\n",
- sampidx, ATH_SAMP_DBG(cc.cycles),
- ATH_SAMP_DBG(cc.rx_busy),
- ATH_SAMP_DBG(cc.rx_frame),
- ATH_SAMP_DBG(cc.tx_frame));
- }
-
- len += snprintf(buf + len, size - len, "Tx status Dump :\n");
- len += snprintf(buf + len, size - len,
- "Sample rssi:- ctl0 ctl1 ctl2 ext0 ext1 ext2 comb "
- "isok rts_fail data_fail rate tid qid "
- "ba_low ba_high tx_before(ms)\n");
- for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
- for (i = 0; i < ATH_DBG_MAX_SAMPLES; i++) {
- if (!ATH_SAMP_DBG(ts[i].jiffies))
- continue;
- len += snprintf(buf + len, size - len, "%-14d"
- "%-4d %-4d %-4d %-4d %-4d %-4d %-4d %-4d %-8d "
- "%-9d %-4d %-3d %-3d %08x %08x %-11d\n",
- sampidx,
- ATH_SAMP_DBG(ts[i].rssi_ctl0),
- ATH_SAMP_DBG(ts[i].rssi_ctl1),
- ATH_SAMP_DBG(ts[i].rssi_ctl2),
- ATH_SAMP_DBG(ts[i].rssi_ext0),
- ATH_SAMP_DBG(ts[i].rssi_ext1),
- ATH_SAMP_DBG(ts[i].rssi_ext2),
- ATH_SAMP_DBG(ts[i].rssi),
- ATH_SAMP_DBG(ts[i].isok),
- ATH_SAMP_DBG(ts[i].rts_fail_cnt),
- ATH_SAMP_DBG(ts[i].data_fail_cnt),
- ATH_SAMP_DBG(ts[i].rateindex),
- ATH_SAMP_DBG(ts[i].tid),
- ATH_SAMP_DBG(ts[i].qid),
- ATH_SAMP_DBG(ts[i].ba_low),
- ATH_SAMP_DBG(ts[i].ba_high),
- jiffies_to_msecs(jiffies -
- ATH_SAMP_DBG(ts[i].jiffies)));
- }
- }
-
- len += snprintf(buf + len, size - len, "Rx status Dump :\n");
- len += snprintf(buf + len, size - len, "Sample rssi:- ctl0 ctl1 ctl2 "
- "ext0 ext1 ext2 comb beacon ant rate rx_before(ms)\n");
- for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
- for (i = 0; i < ATH_DBG_MAX_SAMPLES; i++) {
- if (!ATH_SAMP_DBG(rs[i].jiffies))
- continue;
- len += snprintf(buf + len, size - len, "%-14d"
- "%-4d %-4d %-4d %-4d %-4d %-4d %-4d %-9s %-2d %02x %-13d\n",
- sampidx,
- ATH_SAMP_DBG(rs[i].rssi_ctl0),
- ATH_SAMP_DBG(rs[i].rssi_ctl1),
- ATH_SAMP_DBG(rs[i].rssi_ctl2),
- ATH_SAMP_DBG(rs[i].rssi_ext0),
- ATH_SAMP_DBG(rs[i].rssi_ext1),
- ATH_SAMP_DBG(rs[i].rssi_ext2),
- ATH_SAMP_DBG(rs[i].rssi),
- ATH_SAMP_DBG(rs[i].is_mybeacon) ?
- "True" : "False",
- ATH_SAMP_DBG(rs[i].antenna),
- ATH_SAMP_DBG(rs[i].rate),
- jiffies_to_msecs(jiffies -
- ATH_SAMP_DBG(rs[i].jiffies)));
- }
- }
-
- vfree(bb_mac_samp);
- file->private_data = buf;
-
- return 0;
-#undef ATH_SAMP_DBG
-}
-
-static const struct file_operations fops_samps = {
- .open = open_file_bb_mac_samps,
- .read = ath9k_debugfs_read_buf,
- .release = ath9k_debugfs_release_buf,
- .owner = THIS_MODULE,
- .llseek = default_llseek,
-};
-
-#endif
-
#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
static ssize_t read_file_btcoex(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
@@ -2087,11 +1755,6 @@ int ath9k_init_debug(struct ath_hw *ah)
debugfs_create_file("spectral_fft_period", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, sc,
&fops_spectral_fft_period);
-
-#ifdef CONFIG_ATH9K_MAC_DEBUG
- debugfs_create_file("samples", S_IRUSR, sc->debug.debugfs_phy, sc,
- &fops_samps);
-#endif
debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask);
debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR,
diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h
index 794a7ec83a2..62da19c6f4e 100644
--- a/drivers/net/wireless/ath/ath9k/debug.h
+++ b/drivers/net/wireless/ath/ath9k/debug.h
@@ -294,13 +294,6 @@ struct ath9k_debug {
struct dentry *debugfs_phy;
u32 regidx;
struct ath_stats stats;
-#ifdef CONFIG_ATH9K_MAC_DEBUG
- spinlock_t samp_lock;
- struct ath_dbg_bb_mac_samp bb_mac_samp[ATH_DBG_MAX_SAMPLES];
- u8 sampidx;
- u8 tsidx;
- u8 rsidx;
-#endif
};
int ath9k_init_debug(struct ath_hw *ah);
@@ -359,17 +352,4 @@ static inline void ath_debug_stat_rx(struct ath_softc *sc,
#endif /* CONFIG_ATH9K_DEBUGFS */
-#ifdef CONFIG_ATH9K_MAC_DEBUG
-
-void ath9k_debug_samp_bb_mac(struct ath_softc *sc);
-
-#else
-
-static inline void ath9k_debug_samp_bb_mac(struct ath_softc *sc)
-{
-}
-
-#endif
-
-
#endif /* DEBUG_H */
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index d3b099d7898..0085e643132 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -208,6 +208,9 @@ struct ath9k_htc_target_rx_stats {
case NL80211_IFTYPE_AP: \
_priv->num_ap_vif++; \
break; \
+ case NL80211_IFTYPE_MESH_POINT: \
+ _priv->num_mbss_vif++; \
+ break; \
default: \
break; \
} \
@@ -224,6 +227,9 @@ struct ath9k_htc_target_rx_stats {
case NL80211_IFTYPE_AP: \
_priv->num_ap_vif--; \
break; \
+ case NL80211_IFTYPE_MESH_POINT: \
+ _priv->num_mbss_vif--; \
+ break; \
default: \
break; \
} \
@@ -450,6 +456,7 @@ struct ath9k_htc_priv {
u8 sta_slot;
u8 vif_sta_pos[ATH9K_HTC_MAX_VIF];
u8 num_ibss_vif;
+ u8 num_mbss_vif;
u8 num_sta_vif;
u8 num_sta_assoc_vif;
u8 num_ap_vif;
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
index f13f458dd65..e0c03bd6418 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
@@ -28,7 +28,8 @@ void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv)
ath9k_hw_get_txq_props(ah, priv->beaconq, &qi);
- if (priv->ah->opmode == NL80211_IFTYPE_AP) {
+ if (priv->ah->opmode == NL80211_IFTYPE_AP ||
+ priv->ah->opmode == NL80211_IFTYPE_MESH_POINT) {
qi.tqi_aifs = 1;
qi.tqi_cwmin = 0;
qi.tqi_cwmax = 0;
@@ -628,6 +629,7 @@ void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv,
case NL80211_IFTYPE_ADHOC:
ath9k_htc_beacon_config_adhoc(priv, cur_conf);
break;
+ case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_AP:
ath9k_htc_beacon_config_ap(priv, cur_conf);
break;
@@ -649,6 +651,7 @@ void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv)
case NL80211_IFTYPE_ADHOC:
ath9k_htc_beacon_config_adhoc(priv, cur_conf);
break;
+ case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_AP:
ath9k_htc_beacon_config_ap(priv, cur_conf);
break;
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
index a47f5e05fc0..59f64367e8c 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
@@ -698,7 +698,8 @@ static const struct ieee80211_iface_limit if_limits[] = {
{ .max = 2, .types = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_P2P_CLIENT) },
{ .max = 2, .types = BIT(NL80211_IFTYPE_AP) |
- BIT(NL80211_IFTYPE_P2P_GO) },
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_MESH_POINT) },
};
static const struct ieee80211_iface_combination if_comb = {
@@ -721,6 +722,7 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK |
IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+ IEEE80211_HW_MFP_CAPABLE |
IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING;
hw->wiphy->interface_modes =
@@ -728,7 +730,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_P2P_GO) |
- BIT(NL80211_IFTYPE_P2P_CLIENT);
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_MESH_POINT);
hw->wiphy->iface_combinations = &if_comb;
hw->wiphy->n_iface_combinations = 1;
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index 0743a47cef8..2a67b57c3ec 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -113,7 +113,9 @@ static void ath9k_htc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
struct ath9k_htc_priv *priv = data;
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
- if ((vif->type == NL80211_IFTYPE_AP) && bss_conf->enable_beacon)
+ if ((vif->type == NL80211_IFTYPE_AP ||
+ vif->type == NL80211_IFTYPE_MESH_POINT) &&
+ bss_conf->enable_beacon)
priv->reconfig_beacon = true;
if (bss_conf->assoc) {
@@ -180,6 +182,8 @@ static void ath9k_htc_set_opmode(struct ath9k_htc_priv *priv)
priv->ah->opmode = NL80211_IFTYPE_ADHOC;
else if (priv->num_ap_vif)
priv->ah->opmode = NL80211_IFTYPE_AP;
+ else if (priv->num_mbss_vif)
+ priv->ah->opmode = NL80211_IFTYPE_MESH_POINT;
else
priv->ah->opmode = NL80211_IFTYPE_STATION;
@@ -1052,6 +1056,9 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
case NL80211_IFTYPE_AP:
hvif.opmode = HTC_M_HOSTAP;
break;
+ case NL80211_IFTYPE_MESH_POINT:
+ hvif.opmode = HTC_M_WDS; /* close enough */
+ break;
default:
ath_err(common,
"Interface type %d not yet supported\n", vif->type);
@@ -1084,6 +1091,7 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
INC_VIF(priv, vif->type);
if ((vif->type == NL80211_IFTYPE_AP) ||
+ (vif->type == NL80211_IFTYPE_MESH_POINT) ||
(vif->type == NL80211_IFTYPE_ADHOC))
ath9k_htc_assign_bslot(priv, vif);
@@ -1134,6 +1142,7 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
DEC_VIF(priv, vif->type);
if ((vif->type == NL80211_IFTYPE_AP) ||
+ vif->type == NL80211_IFTYPE_MESH_POINT ||
(vif->type == NL80211_IFTYPE_ADHOC))
ath9k_htc_remove_bslot(priv, vif);
@@ -1525,9 +1534,10 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) {
/*
* Disable SWBA interrupt only if there are no
- * AP/IBSS interfaces.
+ * concurrent AP/mesh or IBSS interfaces.
*/
- if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) {
+ if ((priv->num_ap_vif + priv->num_mbss_vif <= 1) ||
+ priv->num_ibss_vif) {
ath_dbg(common, CONFIG,
"Beacon disabled for BSS: %pM\n",
bss_conf->bssid);
@@ -1538,12 +1548,15 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_BEACON_INT) {
/*
- * Reset the HW TSF for the first AP interface.
+ * Reset the HW TSF for the first AP or mesh interface.
*/
- if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
- (priv->nvifs == 1) &&
- (priv->num_ap_vif == 1) &&
- (vif->type == NL80211_IFTYPE_AP)) {
+ if (priv->nvifs == 1 &&
+ ((priv->ah->opmode == NL80211_IFTYPE_AP &&
+ vif->type == NL80211_IFTYPE_AP &&
+ priv->num_ap_vif == 1) ||
+ (priv->ah->opmode == NL80211_IFTYPE_MESH_POINT &&
+ vif->type == NL80211_IFTYPE_MESH_POINT &&
+ priv->num_mbss_vif == 1))) {
set_bit(OP_TSF_RESET, &priv->op_flags);
}
ath_dbg(common, CONFIG,
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
index 6bd0e92ea2a..e602c951970 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
@@ -887,7 +887,7 @@ u32 ath9k_htc_calcrxfilter(struct ath9k_htc_priv *priv)
if (priv->rxfilter & FIF_PSPOLL)
rfilt |= ATH9K_RX_FILTER_PSPOLL;
- if (priv->nvifs > 1)
+ if (priv->nvifs > 1 || priv->rxfilter & FIF_OTHER_BSS)
rfilt |= ATH9K_RX_FILTER_MCAST_BCAST_ALL;
return rfilt;
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 7f25da8444f..a263ccc00a4 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -1245,10 +1245,10 @@ static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode)
switch (opmode) {
case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_MESH_POINT:
set |= AR_STA_ID1_ADHOC;
REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION);
break;
+ case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_AP:
set |= AR_STA_ID1_STA_AP;
/* fall through */
@@ -2246,12 +2246,12 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period)
switch (ah->opmode) {
case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_MESH_POINT:
REG_SET_BIT(ah, AR_TXCFG,
AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY);
REG_WRITE(ah, AR_NEXT_NDP_TIMER, next_beacon +
TU_TO_USEC(ah->atim_window ? ah->atim_window : 1));
flags |= AR_NDP_TIMER_EN;
+ case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_AP:
REG_WRITE(ah, AR_NEXT_TBTT_TIMER, next_beacon);
REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, next_beacon -
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 0237b286896..c7b888f2270 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -613,9 +613,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
spin_lock_init(&sc->sc_serial_rw);
spin_lock_init(&sc->sc_pm_lock);
mutex_init(&sc->mutex);
-#ifdef CONFIG_ATH9K_MAC_DEBUG
- spin_lock_init(&sc->debug.samp_lock);
-#endif
tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet,
(unsigned long)sc);
diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c
index 849259b0737..cc422a4dfa1 100644
--- a/drivers/net/wireless/ath/ath9k/link.c
+++ b/drivers/net/wireless/ath/ath9k/link.c
@@ -418,7 +418,6 @@ void ath_ani_calibrate(unsigned long data)
longcal ? "long" : "", shortcal ? "short" : "",
aniflag ? "ani" : "", common->ani.caldone ? "true" : "false");
- ath9k_debug_samp_bb_mac(sc);
ath9k_ps_restore(sc);
set_timer:
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index a18414b5948..a1630d1ee3c 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -193,7 +193,6 @@ static bool ath_prepare_reset(struct ath_softc *sc)
ath_stop_ani(sc);
del_timer_sync(&sc->rx_poll_timer);
- ath9k_debug_samp_bb_mac(sc);
ath9k_hw_disable_interrupts(ah);
if (!ath_drain_all_txq(sc))
@@ -1273,7 +1272,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
curchan->center_freq);
} else {
/* perform spectral scan if requested. */
- if (sc->scanning &&
+ if (test_bit(SC_OP_SCANNING, &sc->sc_flags) &&
sc->spectral_mode == SPECTRAL_CHANSCAN)
ath9k_spectral_scan_trigger(hw);
}
@@ -1689,7 +1688,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
struct ath_softc *sc = hw->priv;
int ret = 0;
- local_bh_disable();
+ mutex_lock(&sc->mutex);
switch (action) {
case IEEE80211_AMPDU_RX_START:
@@ -1720,7 +1719,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
ath_err(ath9k_hw_common(sc->sc_ah), "Unknown AMPDU action\n");
}
- local_bh_enable();
+ mutex_unlock(&sc->mutex);
return ret;
}
@@ -2339,15 +2338,13 @@ static void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled)
static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
{
struct ath_softc *sc = hw->priv;
-
- sc->scanning = 1;
+ set_bit(SC_OP_SCANNING, &sc->sc_flags);
}
static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
{
struct ath_softc *sc = hw->priv;
-
- sc->scanning = 0;
+ clear_bit(SC_OP_SCANNING, &sc->sc_flags);
}
struct ieee80211_ops ath9k_ops = {
diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig
index bac3d98a0cf..5644ac54fac 100644
--- a/drivers/net/wireless/ath/wil6210/Kconfig
+++ b/drivers/net/wireless/ath/wil6210/Kconfig
@@ -27,3 +27,15 @@ config WIL6210_ISR_COR
self-clear when accessed for debug purposes, it makes
such monitoring impossible.
Say y unless you debug interrupts
+
+config ATH6KL_TRACING
+ bool "wil6210 tracing support"
+ depends on WIL6210
+ depends on EVENT_TRACING
+ default y
+ ---help---
+ Say Y here to enable tracepoints for the wil6210 driver
+ using the kernel tracing infrastructure. Select this
+ option if you are interested in debugging the driver.
+
+ If unsure, say Y to make it easier to debug problems.
diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile
index d288eea0a26..f891d514d88 100644
--- a/drivers/net/wireless/ath/wil6210/Makefile
+++ b/drivers/net/wireless/ath/wil6210/Makefile
@@ -1,15 +1,20 @@
obj-$(CONFIG_WIL6210) += wil6210.o
-wil6210-objs := main.o
-wil6210-objs += netdev.o
-wil6210-objs += cfg80211.o
-wil6210-objs += pcie_bus.o
-wil6210-objs += debugfs.o
-wil6210-objs += wmi.o
-wil6210-objs += interrupt.o
-wil6210-objs += txrx.o
+wil6210-y := main.o
+wil6210-y += netdev.o
+wil6210-y += cfg80211.o
+wil6210-y += pcie_bus.o
+wil6210-y += debugfs.o
+wil6210-y += wmi.o
+wil6210-y += interrupt.o
+wil6210-y += txrx.o
+wil6210-y += debug.o
+wil6210-$(CONFIG_WIL6210_TRACING) += trace.o
ifeq (, $(findstring -W,$(EXTRA_CFLAGS)))
subdir-ccflags-y += -Werror
endif
+# for tracing framework to find trace.h
+CFLAGS_trace.o := -I$(src)
+
subdir-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index c5d4a87abaa..4eb05d0818c 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -322,12 +322,16 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
* FW don't support scan after connection attempt
*/
set_bit(wil_status_dontscan, &wil->status);
+ set_bit(wil_status_fwconnecting, &wil->status);
rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn));
if (rc == 0) {
/* Connect can take lots of time */
mod_timer(&wil->connect_timer,
jiffies + msecs_to_jiffies(2000));
+ } else {
+ clear_bit(wil_status_dontscan, &wil->status);
+ clear_bit(wil_status_fwconnecting, &wil->status);
}
out:
diff --git a/drivers/net/wireless/ath/wil6210/debug.c b/drivers/net/wireless/ath/wil6210/debug.c
new file mode 100644
index 00000000000..9eeabf4a587
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/debug.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2013 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 "wil6210.h"
+#include "trace.h"
+
+int wil_err(struct wil6210_priv *wil, const char *fmt, ...)
+{
+ struct net_device *ndev = wil_to_ndev(wil);
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+ int ret;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ ret = netdev_err(ndev, "%pV", &vaf);
+ trace_wil6210_log_err(&vaf);
+ va_end(args);
+
+ return ret;
+}
+
+int wil_info(struct wil6210_priv *wil, const char *fmt, ...)
+{
+ struct net_device *ndev = wil_to_ndev(wil);
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+ int ret;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ ret = netdev_info(ndev, "%pV", &vaf);
+ trace_wil6210_log_info(&vaf);
+ va_end(args);
+
+ return ret;
+}
+
+int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...)
+{
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ trace_wil6210_log_dbg(&vaf);
+ va_end(args);
+
+ return 0;
+}
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index 727b1f53e6a..e8308ec3097 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -418,9 +418,15 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
if (skb) {
unsigned char printbuf[16 * 3 + 2];
int i = 0;
- int len = skb_headlen(skb);
+ int len = le16_to_cpu(d->dma.length);
void *p = skb->data;
+ if (len != skb_headlen(skb)) {
+ seq_printf(s, "!!! len: desc = %d skb = %d\n",
+ len, skb_headlen(skb));
+ len = min_t(int, len, skb_headlen(skb));
+ }
+
seq_printf(s, " len = %d\n", len);
while (i < len) {
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index e3c1e7684f9..8205d3e4ab6 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -17,6 +17,7 @@
#include <linux/interrupt.h>
#include "wil6210.h"
+#include "trace.h"
/**
* Theory of operation:
@@ -103,14 +104,14 @@ static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil)
clear_bit(wil_status_irqen, &wil->status);
}
-static void wil6210_unmask_irq_tx(struct wil6210_priv *wil)
+void wil6210_unmask_irq_tx(struct wil6210_priv *wil)
{
iowrite32(WIL6210_IMC_TX, wil->csr +
HOSTADDR(RGF_DMA_EP_TX_ICR) +
offsetof(struct RGF_ICR, IMC));
}
-static void wil6210_unmask_irq_rx(struct wil6210_priv *wil)
+void wil6210_unmask_irq_rx(struct wil6210_priv *wil)
{
iowrite32(WIL6210_IMC_RX, wil->csr +
HOSTADDR(RGF_DMA_EP_RX_ICR) +
@@ -168,6 +169,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
HOSTADDR(RGF_DMA_EP_RX_ICR) +
offsetof(struct RGF_ICR, ICR));
+ trace_wil6210_irq_rx(isr);
wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr);
if (!isr) {
@@ -180,13 +182,14 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) {
wil_dbg_irq(wil, "RX done\n");
isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE;
- wil_rx_handle(wil);
+ wil_dbg_txrx(wil, "NAPI schedule\n");
+ napi_schedule(&wil->napi_rx);
}
if (isr)
wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr);
- wil6210_unmask_irq_rx(wil);
+ /* Rx IRQ will be enabled when NAPI processing finished */
return IRQ_HANDLED;
}
@@ -198,6 +201,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
HOSTADDR(RGF_DMA_EP_TX_ICR) +
offsetof(struct RGF_ICR, ICR));
+ trace_wil6210_irq_tx(isr);
wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr);
if (!isr) {
@@ -208,23 +212,17 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
wil6210_mask_irq_tx(wil);
if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) {
- uint i;
wil_dbg_irq(wil, "TX done\n");
+ napi_schedule(&wil->napi_tx);
isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE;
- for (i = 0; i < 24; i++) {
- u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i);
- if (isr & mask) {
- isr &= ~mask;
- wil_dbg_irq(wil, "TX done(%i)\n", i);
- wil_tx_complete(wil, i);
- }
- }
+ /* clear also all VRING interrupts */
+ isr &= ~(BIT(25) - 1UL);
}
if (isr)
wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr);
- wil6210_unmask_irq_tx(wil);
+ /* Tx IRQ will be enabled when NAPI processing finished */
return IRQ_HANDLED;
}
@@ -256,6 +254,7 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
HOSTADDR(RGF_DMA_EP_MISC_ICR) +
offsetof(struct RGF_ICR, ICR));
+ trace_wil6210_irq_misc(isr);
wil_dbg_irq(wil, "ISR MISC 0x%08x\n", isr);
if (!isr) {
@@ -301,6 +300,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
struct wil6210_priv *wil = cookie;
u32 isr = wil->isr_misc;
+ trace_wil6210_irq_misc_thread(isr);
wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr);
if (isr & ISR_MISC_FW_ERROR) {
@@ -408,6 +408,7 @@ static irqreturn_t wil6210_hardirq(int irq, void *cookie)
if (wil6210_debug_irq_mask(wil, pseudo_cause))
return IRQ_NONE;
+ trace_wil6210_irq_pseudo(pseudo_cause);
wil_dbg_irq(wil, "Pseudo IRQ 0x%08x\n", pseudo_cause);
wil6210_mask_irq_pseudo(wil);
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index a0478e2f686..c97b864667c 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -56,27 +56,21 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
{
uint i;
struct net_device *ndev = wil_to_ndev(wil);
- struct wireless_dev *wdev = wil->wdev;
wil_dbg_misc(wil, "%s()\n", __func__);
wil_link_off(wil);
- clear_bit(wil_status_fwconnected, &wil->status);
-
- switch (wdev->sme_state) {
- case CFG80211_SME_CONNECTED:
- cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE,
+ if (test_bit(wil_status_fwconnected, &wil->status)) {
+ clear_bit(wil_status_fwconnected, &wil->status);
+ cfg80211_disconnected(ndev,
+ WLAN_STATUS_UNSPECIFIED_FAILURE,
NULL, 0, GFP_KERNEL);
- break;
- case CFG80211_SME_CONNECTING:
+ } else if (test_bit(wil_status_fwconnecting, &wil->status)) {
cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE,
GFP_KERNEL);
- break;
- default:
- break;
}
-
+ clear_bit(wil_status_fwconnecting, &wil->status);
for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++)
wil_vring_fini_tx(wil, i);
@@ -365,6 +359,9 @@ static int __wil_up(struct wil6210_priv *wil)
/* Rx VRING. After MAC and beacon */
wil_rx_init(wil);
+ napi_enable(&wil->napi_rx);
+ napi_enable(&wil->napi_tx);
+
return 0;
}
@@ -381,6 +378,9 @@ int wil_up(struct wil6210_priv *wil)
static int __wil_down(struct wil6210_priv *wil)
{
+ napi_disable(&wil->napi_rx);
+ napi_disable(&wil->napi_tx);
+
if (wil->scan_request) {
cfg80211_scan_done(wil->scan_request, true);
wil->scan_request = NULL;
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index 098a8ec6b84..29dd1e58cb1 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -40,6 +40,55 @@ static const struct net_device_ops wil_netdev_ops = {
.ndo_validate_addr = eth_validate_addr,
};
+static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget)
+{
+ struct wil6210_priv *wil = container_of(napi, struct wil6210_priv,
+ napi_rx);
+ int quota = budget;
+ int done;
+
+ wil_rx_handle(wil, &quota);
+ done = budget - quota;
+
+ if (done <= 1) { /* burst ends - only one packet processed */
+ napi_complete(napi);
+ wil6210_unmask_irq_rx(wil);
+ wil_dbg_txrx(wil, "NAPI RX complete\n");
+ }
+
+ wil_dbg_txrx(wil, "NAPI RX poll(%d) done %d\n", budget, done);
+
+ return done;
+}
+
+static int wil6210_netdev_poll_tx(struct napi_struct *napi, int budget)
+{
+ struct wil6210_priv *wil = container_of(napi, struct wil6210_priv,
+ napi_tx);
+ int tx_done = 0;
+ uint i;
+
+ /* always process ALL Tx complete, regardless budget - it is fast */
+ for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
+ struct vring *vring = &wil->vring_tx[i];
+
+ if (!vring->va)
+ continue;
+
+ tx_done += wil_tx_complete(wil, i);
+ }
+
+ if (tx_done <= 1) { /* burst ends - only one packet processed */
+ napi_complete(napi);
+ wil6210_unmask_irq_tx(wil);
+ wil_dbg_txrx(wil, "NAPI TX complete\n");
+ }
+
+ wil_dbg_txrx(wil, "NAPI TX poll(%d) done %d\n", budget, tx_done);
+
+ return min(tx_done, budget);
+}
+
void *wil_if_alloc(struct device *dev, void __iomem *csr)
{
struct net_device *ndev;
@@ -81,6 +130,11 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)
SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
wdev->netdev = ndev;
+ netif_napi_add(ndev, &wil->napi_rx, wil6210_netdev_poll_rx,
+ WIL6210_NAPI_BUDGET);
+ netif_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx,
+ WIL6210_NAPI_BUDGET);
+
wil_link_off(wil);
return wil;
diff --git a/drivers/net/wireless/ath/wil6210/trace.c b/drivers/net/wireless/ath/wil6210/trace.c
new file mode 100644
index 00000000000..cd2534b9c5a
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/trace.c
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2013 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 <linux/module.h>
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
diff --git a/drivers/net/wireless/ath/wil6210/trace.h b/drivers/net/wireless/ath/wil6210/trace.h
new file mode 100644
index 00000000000..eff1239be53
--- /dev/null
+++ b/drivers/net/wireless/ath/wil6210/trace.h
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2013 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.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM wil6210
+#if !defined(WIL6210_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define WIL6210_TRACE_H
+
+#include <linux/tracepoint.h>
+#include "wil6210.h"
+#include "txrx.h"
+
+/* create empty functions when tracing is disabled */
+#if !defined(CONFIG_WIL6210_TRACING) || defined(__CHECKER__)
+
+#undef TRACE_EVENT
+#define TRACE_EVENT(name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#undef DECLARE_EVENT_CLASS
+#define DECLARE_EVENT_CLASS(...)
+#undef DEFINE_EVENT
+#define DEFINE_EVENT(evt_class, name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#endif /* !CONFIG_WIL6210_TRACING || defined(__CHECKER__) */
+
+DECLARE_EVENT_CLASS(wil6210_wmi,
+ TP_PROTO(u16 id, void *buf, u16 buf_len),
+
+ TP_ARGS(id, buf, buf_len),
+
+ TP_STRUCT__entry(
+ __field(u16, id)
+ __field(u16, buf_len)
+ __dynamic_array(u8, buf, buf_len)
+ ),
+
+ TP_fast_assign(
+ __entry->id = id;
+ __entry->buf_len = buf_len;
+ memcpy(__get_dynamic_array(buf), buf, buf_len);
+ ),
+
+ TP_printk(
+ "id 0x%04x len %d",
+ __entry->id, __entry->buf_len
+ )
+);
+
+DEFINE_EVENT(wil6210_wmi, wil6210_wmi_cmd,
+ TP_PROTO(u16 id, void *buf, u16 buf_len),
+ TP_ARGS(id, buf, buf_len)
+);
+
+DEFINE_EVENT(wil6210_wmi, wil6210_wmi_event,
+ TP_PROTO(u16 id, void *buf, u16 buf_len),
+ TP_ARGS(id, buf, buf_len)
+);
+
+#define WIL6210_MSG_MAX (200)
+
+DECLARE_EVENT_CLASS(wil6210_log_event,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf),
+ TP_STRUCT__entry(
+ __dynamic_array(char, msg, WIL6210_MSG_MAX)
+ ),
+ TP_fast_assign(
+ WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
+ WIL6210_MSG_MAX,
+ vaf->fmt,
+ *vaf->va) >= WIL6210_MSG_MAX);
+ ),
+ TP_printk("%s", __get_str(msg))
+);
+
+DEFINE_EVENT(wil6210_log_event, wil6210_log_err,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(wil6210_log_event, wil6210_log_info,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(wil6210_log_event, wil6210_log_dbg,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
+#define wil_pseudo_irq_cause(x) __print_flags(x, "|", \
+ {BIT_DMA_PSEUDO_CAUSE_RX, "Rx" }, \
+ {BIT_DMA_PSEUDO_CAUSE_TX, "Tx" }, \
+ {BIT_DMA_PSEUDO_CAUSE_MISC, "Misc" })
+
+TRACE_EVENT(wil6210_irq_pseudo,
+ TP_PROTO(u32 x),
+ TP_ARGS(x),
+ TP_STRUCT__entry(
+ __field(u32, x)
+ ),
+ TP_fast_assign(
+ __entry->x = x;
+ ),
+ TP_printk("cause 0x%08x : %s", __entry->x,
+ wil_pseudo_irq_cause(__entry->x))
+);
+
+DECLARE_EVENT_CLASS(wil6210_irq,
+ TP_PROTO(u32 x),
+ TP_ARGS(x),
+ TP_STRUCT__entry(
+ __field(u32, x)
+ ),
+ TP_fast_assign(
+ __entry->x = x;
+ ),
+ TP_printk("cause 0x%08x", __entry->x)
+);
+
+DEFINE_EVENT(wil6210_irq, wil6210_irq_rx,
+ TP_PROTO(u32 x),
+ TP_ARGS(x)
+);
+
+DEFINE_EVENT(wil6210_irq, wil6210_irq_tx,
+ TP_PROTO(u32 x),
+ TP_ARGS(x)
+);
+
+DEFINE_EVENT(wil6210_irq, wil6210_irq_misc,
+ TP_PROTO(u32 x),
+ TP_ARGS(x)
+);
+
+DEFINE_EVENT(wil6210_irq, wil6210_irq_misc_thread,
+ TP_PROTO(u32 x),
+ TP_ARGS(x)
+);
+
+TRACE_EVENT(wil6210_rx,
+ TP_PROTO(u16 index, struct vring_rx_desc *d),
+ TP_ARGS(index, d),
+ TP_STRUCT__entry(
+ __field(u16, index)
+ __field(unsigned int, len)
+ __field(u8, mid)
+ __field(u8, cid)
+ __field(u8, tid)
+ __field(u8, type)
+ __field(u8, subtype)
+ __field(u16, seq)
+ __field(u8, mcs)
+ ),
+ TP_fast_assign(
+ __entry->index = index;
+ __entry->len = d->dma.length;
+ __entry->mid = wil_rxdesc_mid(d);
+ __entry->cid = wil_rxdesc_cid(d);
+ __entry->tid = wil_rxdesc_tid(d);
+ __entry->type = wil_rxdesc_ftype(d);
+ __entry->subtype = wil_rxdesc_subtype(d);
+ __entry->seq = wil_rxdesc_seq(d);
+ __entry->mcs = wil_rxdesc_mcs(d);
+ ),
+ TP_printk("index %d len %d mid %d cid %d tid %d mcs %d seq 0x%03x"
+ " type 0x%1x subtype 0x%1x", __entry->index, __entry->len,
+ __entry->mid, __entry->cid, __entry->tid, __entry->mcs,
+ __entry->seq, __entry->type, __entry->subtype)
+);
+
+TRACE_EVENT(wil6210_tx,
+ TP_PROTO(u8 vring, u16 index, unsigned int len, u8 frags),
+ TP_ARGS(vring, index, len, frags),
+ TP_STRUCT__entry(
+ __field(u8, vring)
+ __field(u8, frags)
+ __field(u16, index)
+ __field(unsigned int, len)
+ ),
+ TP_fast_assign(
+ __entry->vring = vring;
+ __entry->frags = frags;
+ __entry->index = index;
+ __entry->len = len;
+ ),
+ TP_printk("vring %d index %d len %d frags %d",
+ __entry->vring, __entry->index, __entry->len, __entry->frags)
+);
+
+TRACE_EVENT(wil6210_tx_done,
+ TP_PROTO(u8 vring, u16 index, unsigned int len, u8 err),
+ TP_ARGS(vring, index, len, err),
+ TP_STRUCT__entry(
+ __field(u8, vring)
+ __field(u8, err)
+ __field(u16, index)
+ __field(unsigned int, len)
+ ),
+ TP_fast_assign(
+ __entry->vring = vring;
+ __entry->index = index;
+ __entry->len = len;
+ __entry->err = err;
+ ),
+ TP_printk("vring %d index %d len %d err 0x%02x",
+ __entry->vring, __entry->index, __entry->len,
+ __entry->err)
+);
+
+#endif /* WIL6210_TRACE_H || TRACE_HEADER_MULTI_READ*/
+
+#if defined(CONFIG_WIL6210_TRACING) && !defined(__CHECKER__)
+/* we don't want to use include/trace/events */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
+#endif /* defined(CONFIG_WIL6210_TRACING) && !defined(__CHECKER__) */
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 797024507c7..00dffeda983 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -22,6 +22,7 @@
#include "wil6210.h"
#include "wmi.h"
#include "txrx.h"
+#include "trace.h"
static bool rtap_include_phy_info;
module_param(rtap_include_phy_info, bool, S_IRUGO);
@@ -89,8 +90,8 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
* we can use any
*/
for (i = 0; i < vring->size; i++) {
- volatile struct vring_tx_desc *d = &(vring->va[i].tx);
- d->dma.status = TX_DMA_STATUS_DU;
+ volatile struct vring_tx_desc *_d = &(vring->va[i].tx);
+ _d->dma.status = TX_DMA_STATUS_DU;
}
wil_dbg_misc(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size,
@@ -106,30 +107,39 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,
size_t sz = vring->size * sizeof(vring->va[0]);
while (!wil_vring_is_empty(vring)) {
+ dma_addr_t pa;
+ struct sk_buff *skb;
+ u16 dmalen;
+
if (tx) {
- volatile struct vring_tx_desc *d =
+ struct vring_tx_desc dd, *d = &dd;
+ volatile struct vring_tx_desc *_d =
&vring->va[vring->swtail].tx;
- dma_addr_t pa = d->dma.addr_low |
- ((u64)d->dma.addr_high << 32);
- struct sk_buff *skb = vring->ctx[vring->swtail];
+
+ *d = *_d;
+ pa = wil_desc_addr(&d->dma.addr);
+ dmalen = le16_to_cpu(d->dma.length);
+ skb = vring->ctx[vring->swtail];
if (skb) {
- dma_unmap_single(dev, pa, d->dma.length,
+ dma_unmap_single(dev, pa, dmalen,
DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
vring->ctx[vring->swtail] = NULL;
} else {
- dma_unmap_page(dev, pa, d->dma.length,
+ dma_unmap_page(dev, pa, dmalen,
DMA_TO_DEVICE);
}
vring->swtail = wil_vring_next_tail(vring);
} else { /* rx */
- volatile struct vring_rx_desc *d =
+ struct vring_rx_desc dd, *d = &dd;
+ volatile struct vring_rx_desc *_d =
&vring->va[vring->swtail].rx;
- dma_addr_t pa = d->dma.addr_low |
- ((u64)d->dma.addr_high << 32);
- struct sk_buff *skb = vring->ctx[vring->swhead];
- dma_unmap_single(dev, pa, d->dma.length,
- DMA_FROM_DEVICE);
+
+ *d = *_d;
+ pa = wil_desc_addr(&d->dma.addr);
+ dmalen = le16_to_cpu(d->dma.length);
+ skb = vring->ctx[vring->swhead];
+ dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE);
kfree_skb(skb);
wil_vring_advance_head(vring, 1);
}
@@ -151,7 +161,8 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
{
struct device *dev = wil_to_dev(wil);
unsigned int sz = RX_BUF_LEN;
- volatile struct vring_rx_desc *d = &(vring->va[i].rx);
+ struct vring_rx_desc dd, *d = &dd;
+ volatile struct vring_rx_desc *_d = &(vring->va[i].rx);
dma_addr_t pa;
/* TODO align */
@@ -169,13 +180,13 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
}
d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT;
- d->dma.addr_low = lower_32_bits(pa);
- d->dma.addr_high = (u16)upper_32_bits(pa);
+ wil_desc_addr_set(&d->dma.addr, pa);
/* ip_length don't care */
/* b11 don't care */
/* error don't care */
d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
- d->dma.length = sz;
+ d->dma.length = cpu_to_le16(sz);
+ *_d = *d;
vring->ctx[i] = skb;
return 0;
@@ -321,11 +332,12 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
{
struct device *dev = wil_to_dev(wil);
struct net_device *ndev = wil_to_ndev(wil);
- volatile struct vring_rx_desc *d;
- struct vring_rx_desc *d1;
+ volatile struct vring_rx_desc *_d;
+ struct vring_rx_desc *d;
struct sk_buff *skb;
dma_addr_t pa;
unsigned int sz = RX_BUF_LEN;
+ u16 dmalen;
u8 ftype;
u8 ds_bits;
@@ -334,32 +346,44 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
if (wil_vring_is_empty(vring))
return NULL;
- d = &(vring->va[vring->swhead].rx);
- if (!(d->dma.status & RX_DMA_STATUS_DU)) {
+ _d = &(vring->va[vring->swhead].rx);
+ if (!(_d->dma.status & RX_DMA_STATUS_DU)) {
/* it is not error, we just reached end of Rx done area */
return NULL;
}
- pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
skb = vring->ctx[vring->swhead];
+ d = wil_skb_rxdesc(skb);
+ *d = *_d;
+ pa = wil_desc_addr(&d->dma.addr);
+ vring->ctx[vring->swhead] = NULL;
+ wil_vring_advance_head(vring, 1);
+
dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE);
- skb_trim(skb, d->dma.length);
+ dmalen = le16_to_cpu(d->dma.length);
- d1 = wil_skb_rxdesc(skb);
- *d1 = *d;
+ trace_wil6210_rx(vring->swhead, d);
+ wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, dmalen);
+ wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4,
+ (const void *)d, sizeof(*d), false);
- wil->stats.last_mcs_rx = wil_rxdesc_mcs(d1);
+ if (dmalen > sz) {
+ wil_err(wil, "Rx size too large: %d bytes!\n", dmalen);
+ kfree_skb(skb);
+ return NULL;
+ }
+ skb_trim(skb, dmalen);
+
+ wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
+ skb->data, skb_headlen(skb), false);
+
+
+ wil->stats.last_mcs_rx = wil_rxdesc_mcs(d);
/* use radiotap header only if required */
if (ndev->type == ARPHRD_IEEE80211_RADIOTAP)
wil_rx_add_radiotap_header(wil, skb);
- wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length);
- wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4,
- (const void *)d, sizeof(*d), false);
-
- wil_vring_advance_head(vring, 1);
-
/* no extra checks if in sniffer mode */
if (ndev->type != ARPHRD_ETHER)
return skb;
@@ -368,7 +392,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
* Driver should recognize it by frame type, that is found
* in Rx descriptor. If type is not data, it is 802.11 frame as is
*/
- ftype = wil_rxdesc_ftype(d1) << 2;
+ ftype = wil_rxdesc_ftype(d) << 2;
if (ftype != IEEE80211_FTYPE_DATA) {
wil_dbg_txrx(wil, "Non-data frame ftype 0x%08x\n", ftype);
/* TODO: process it */
@@ -383,7 +407,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
return NULL;
}
- ds_bits = wil_rxdesc_ds_bits(d1);
+ ds_bits = wil_rxdesc_ds_bits(d);
if (ds_bits == 1) {
/*
* HW bug - in ToDS mode, i.e. Rx on AP side,
@@ -425,6 +449,7 @@ static int wil_rx_refill(struct wil6210_priv *wil, int count)
/*
* Pass Rx packet to the netif. Update statistics.
+ * Called in softirq context (NAPI poll).
*/
static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
{
@@ -433,10 +458,7 @@ static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
skb_orphan(skb);
- if (in_interrupt())
- rc = netif_rx(skb);
- else
- rc = netif_rx_ni(skb);
+ rc = netif_receive_skb(skb);
if (likely(rc == NET_RX_SUCCESS)) {
ndev->stats.rx_packets++;
@@ -450,9 +472,9 @@ static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
/**
* Proceed all completed skb's from Rx VRING
*
- * Safe to call from IRQ
+ * Safe to call from NAPI poll, i.e. softirq with interrupts enabled
*/
-void wil_rx_handle(struct wil6210_priv *wil)
+void wil_rx_handle(struct wil6210_priv *wil, int *quota)
{
struct net_device *ndev = wil_to_ndev(wil);
struct vring *v = &wil->vring_rx;
@@ -463,9 +485,8 @@ void wil_rx_handle(struct wil6210_priv *wil)
return;
}
wil_dbg_txrx(wil, "%s()\n", __func__);
- while (NULL != (skb = wil_vring_reap_rx(wil, v))) {
- wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
- skb->data, skb_headlen(skb), false);
+ while ((*quota > 0) && (NULL != (skb = wil_vring_reap_rx(wil, v)))) {
+ (*quota)--;
if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) {
skb->dev = ndev;
@@ -600,17 +621,15 @@ static struct vring *wil_find_tx_vring(struct wil6210_priv *wil,
return NULL;
}
-static int wil_tx_desc_map(volatile struct vring_tx_desc *d,
- dma_addr_t pa, u32 len)
+static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len)
{
- d->dma.addr_low = lower_32_bits(pa);
- d->dma.addr_high = (u16)upper_32_bits(pa);
+ wil_desc_addr_set(&d->dma.addr, pa);
d->dma.ip_length = 0;
/* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/
d->dma.b11 = 0/*14 | BIT(7)*/;
d->dma.error = 0;
d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
- d->dma.length = len;
+ d->dma.length = cpu_to_le16((u16)len);
d->dma.d0 = 0;
d->mac.d[0] = 0;
d->mac.d[1] = 0;
@@ -630,7 +649,8 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
struct sk_buff *skb)
{
struct device *dev = wil_to_dev(wil);
- volatile struct vring_tx_desc *d;
+ struct vring_tx_desc dd, *d = &dd;
+ volatile struct vring_tx_desc *_d;
u32 swhead = vring->swhead;
int avail = wil_vring_avail_tx(vring);
int nr_frags = skb_shinfo(skb)->nr_frags;
@@ -648,7 +668,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
1 + nr_frags);
return -ENOMEM;
}
- d = &(vring->va[i].tx);
+ _d = &(vring->va[i].tx);
/* FIXME FW can accept only unicast frames for the peer */
memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN);
@@ -667,25 +687,30 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
wil_tx_desc_map(d, pa, skb_headlen(skb));
d->mac.d[2] |= ((nr_frags + 1) <<
MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS);
+ if (nr_frags)
+ *_d = *d;
+
/* middle segments */
for (f = 0; f < nr_frags; f++) {
const struct skb_frag_struct *frag =
&skb_shinfo(skb)->frags[f];
int len = skb_frag_size(frag);
i = (swhead + f + 1) % vring->size;
- d = &(vring->va[i].tx);
+ _d = &(vring->va[i].tx);
pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag),
DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(dev, pa)))
goto dma_error;
wil_tx_desc_map(d, pa, len);
vring->ctx[i] = NULL;
+ *_d = *d;
}
/* for the last seg only */
d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS);
d->dma.d0 |= BIT(9); /* BUG: undocumented bit */
d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS);
d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_0_QID_POS);
+ *_d = *d;
wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
@@ -693,6 +718,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
/* advance swhead */
wil_vring_advance_head(vring, nr_frags + 1);
wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
+ trace_wil6210_tx(vring_index, swhead, skb->len, nr_frags);
iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail));
/* hold reference to skb
* to prevent skb release before accounting
@@ -705,14 +731,18 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
/* unmap what we have mapped */
/* Note: increment @f to operate with positive index */
for (f++; f > 0; f--) {
+ u16 dmalen;
+
i = (swhead + f) % vring->size;
- d = &(vring->va[i].tx);
- d->dma.status = TX_DMA_STATUS_DU;
- pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
+ _d = &(vring->va[i].tx);
+ *d = *_d;
+ _d->dma.status = TX_DMA_STATUS_DU;
+ pa = wil_desc_addr(&d->dma.addr);
+ dmalen = le16_to_cpu(d->dma.length);
if (vring->ctx[i])
- dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE);
+ dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
else
- dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE);
+ dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
}
return -EINVAL;
@@ -761,7 +791,6 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
break; /* goto drop; */
}
drop:
- netif_tx_stop_all_queues(ndev);
ndev->stats.tx_dropped++;
dev_kfree_skb_any(skb);
@@ -771,41 +800,48 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
/**
* Clean up transmitted skb's from the Tx VRING
*
+ * Return number of descriptors cleared
+ *
* Safe to call from IRQ
*/
-void wil_tx_complete(struct wil6210_priv *wil, int ringid)
+int wil_tx_complete(struct wil6210_priv *wil, int ringid)
{
struct net_device *ndev = wil_to_ndev(wil);
struct device *dev = wil_to_dev(wil);
struct vring *vring = &wil->vring_tx[ringid];
+ int done = 0;
if (!vring->va) {
wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid);
- return;
+ return 0;
}
wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid);
while (!wil_vring_is_empty(vring)) {
- volatile struct vring_tx_desc *d1 =
+ volatile struct vring_tx_desc *_d =
&vring->va[vring->swtail].tx;
struct vring_tx_desc dd, *d = &dd;
dma_addr_t pa;
struct sk_buff *skb;
+ u16 dmalen;
- dd = *d1;
+ *d = *_d;
if (!(d->dma.status & TX_DMA_STATUS_DU))
break;
+ dmalen = le16_to_cpu(d->dma.length);
+ trace_wil6210_tx_done(ringid, vring->swtail, dmalen,
+ d->dma.error);
wil_dbg_txrx(wil,
"Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n",
- vring->swtail, d->dma.length, d->dma.status,
+ vring->swtail, dmalen, d->dma.status,
d->dma.error);
wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
- pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
+ pa = wil_desc_addr(&d->dma.addr);
skb = vring->ctx[vring->swtail];
if (skb) {
if (d->dma.error == 0) {
@@ -815,18 +851,21 @@ void wil_tx_complete(struct wil6210_priv *wil, int ringid)
ndev->stats.tx_errors++;
}
- dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE);
+ dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
vring->ctx[vring->swtail] = NULL;
} else {
- dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE);
+ dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
}
- d->dma.addr_low = 0;
- d->dma.addr_high = 0;
+ d->dma.addr.addr_low = 0;
+ d->dma.addr.addr_high = 0;
d->dma.length = 0;
d->dma.status = TX_DMA_STATUS_DU;
vring->swtail = wil_vring_next_tail(vring);
+ done++;
}
if (wil_vring_avail_tx(vring) > vring->size/4)
netif_tx_wake_all_queues(wil_to_ndev(wil));
+
+ return done;
}
diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h
index adef12fb2ae..23c0781cdb9 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.h
+++ b/drivers/net/wireless/ath/wil6210/txrx.h
@@ -27,6 +27,28 @@
#define WIL6210_RTAP_SIZE (128)
/* Tx/Rx path */
+
+/*
+ * Common representation of physical address in Vring
+ */
+struct vring_dma_addr {
+ __le32 addr_low;
+ __le16 addr_high;
+} __packed;
+
+static inline dma_addr_t wil_desc_addr(struct vring_dma_addr *addr)
+{
+ return le32_to_cpu(addr->addr_low) |
+ ((u64)le16_to_cpu(addr->addr_high) << 32);
+}
+
+static inline void wil_desc_addr_set(struct vring_dma_addr *addr,
+ dma_addr_t pa)
+{
+ addr->addr_low = cpu_to_le32(lower_32_bits(pa));
+ addr->addr_high = cpu_to_le16((u16)upper_32_bits(pa));
+}
+
/*
* Tx descriptor - MAC part
* [dword 0]
@@ -216,13 +238,12 @@ struct vring_tx_mac {
struct vring_tx_dma {
u32 d0;
- u32 addr_low;
- u16 addr_high;
+ struct vring_dma_addr addr;
u8 ip_length;
u8 b11; /* 0..6: mac_length; 7:ip_version */
u8 error; /* 0..2: err; 3..7: reserved; */
u8 status; /* 0: used; 1..7; reserved */
- u16 length;
+ __le16 length;
} __packed;
/*
@@ -315,13 +336,12 @@ struct vring_rx_mac {
struct vring_rx_dma {
u32 d0;
- u32 addr_low;
- u16 addr_high;
+ struct vring_dma_addr addr;
u8 ip_length;
u8 b11;
u8 error;
u8 status;
- u16 length;
+ __le16 length;
} __packed;
struct vring_tx_desc {
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 8f76ecd8a7e..373cf656f5b 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -34,9 +34,11 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
#define WIL6210_MEM_SIZE (2*1024*1024UL)
-#define WIL6210_RX_RING_SIZE (128)
-#define WIL6210_TX_RING_SIZE (128)
-#define WIL6210_MAX_TX_RINGS (24)
+#define WIL6210_RX_RING_SIZE (128)
+#define WIL6210_TX_RING_SIZE (128)
+#define WIL6210_MAX_TX_RINGS (24) /* HW limit */
+#define WIL6210_MAX_CID (8) /* HW limit */
+#define WIL6210_NAPI_BUDGET (16) /* arbitrary */
/* Hardware definitions begin */
@@ -184,6 +186,7 @@ struct vring {
enum { /* for wil6210_priv.status */
wil_status_fwready = 0,
+ wil_status_fwconnecting,
wil_status_fwconnected,
wil_status_dontscan,
wil_status_reset_done,
@@ -239,6 +242,8 @@ struct wil6210_priv {
* - consumed in thread by wmi_event_worker
*/
spinlock_t wmi_ev_lock;
+ struct napi_struct napi_rx;
+ struct napi_struct napi_tx;
/* DMA related */
struct vring vring_rx;
struct vring vring_tx[WIL6210_MAX_TX_RINGS];
@@ -267,9 +272,13 @@ struct wil6210_priv {
#define wil_to_ndev(i) (wil_to_wdev(i)->netdev)
#define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr))
-#define wil_dbg(wil, fmt, arg...) netdev_dbg(wil_to_ndev(wil), fmt, ##arg)
-#define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg)
-#define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg)
+int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...);
+int wil_err(struct wil6210_priv *wil, const char *fmt, ...);
+int wil_info(struct wil6210_priv *wil, const char *fmt, ...);
+#define wil_dbg(wil, fmt, arg...) do { \
+ netdev_dbg(wil_to_ndev(wil), fmt, ##arg); \
+ wil_dbg_trace(wil, fmt, ##arg); \
+} while (0)
#define wil_dbg_irq(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg)
#define wil_dbg_txrx(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg)
@@ -356,10 +365,12 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
void wil_vring_fini_tx(struct wil6210_priv *wil, int id);
netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev);
-void wil_tx_complete(struct wil6210_priv *wil, int ringid);
+int wil_tx_complete(struct wil6210_priv *wil, int ringid);
+void wil6210_unmask_irq_tx(struct wil6210_priv *wil);
/* RX API */
-void wil_rx_handle(struct wil6210_priv *wil);
+void wil_rx_handle(struct wil6210_priv *wil, int *quota);
+void wil6210_unmask_irq_rx(struct wil6210_priv *wil);
int wil_iftype_nl2wmi(enum nl80211_iftype type);
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 45b04e383f9..527ffb54382 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -20,6 +20,7 @@
#include "wil6210.h"
#include "txrx.h"
#include "wmi.h"
+#include "trace.h"
/**
* WMI event receiving - theory of operations
@@ -246,6 +247,8 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
iowrite32(r->head = next_head, wil->csr + HOST_MBOX +
offsetof(struct wil6210_mbox_ctl, tx.head));
+ trace_wil6210_wmi_cmd(cmdid, buf, len);
+
/* interrupt to FW */
iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT);
@@ -406,7 +409,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
(wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
- if (wdev->sme_state != CFG80211_SME_CONNECTING) {
+ if (!test_bit(wil_status_fwconnecting, &wil->status)) {
wil_err(wil, "Not in connecting state\n");
return;
}
@@ -430,6 +433,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL);
}
+ clear_bit(wil_status_fwconnecting, &wil->status);
set_bit(wil_status_fwconnected, &wil->status);
/* FIXME FW can transmit only ucast frames to peer */
@@ -635,8 +639,9 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
hdr.flags);
if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) &&
(len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
- wil_dbg_wmi(wil, "WMI event 0x%04x\n",
- evt->event.wmi.id);
+ u16 id = le16_to_cpu(evt->event.wmi.id);
+ wil_dbg_wmi(wil, "WMI event 0x%04x\n", id);
+ trace_wil6210_wmi_event(id, &evt->event.wmi, len);
}
wil_hex_dump_wmi("evt ", DUMP_PREFIX_OFFSET, 16, 1,
&evt->event.hdr, sizeof(hdr) + len, true);
@@ -724,7 +729,7 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan)
.bcon_interval = cpu_to_le16(bi),
.network_type = wmi_nettype,
.disable_sec_offload = 1,
- .channel = chan,
+ .channel = chan - 1,
};
struct {
struct wil6210_mbox_hdr_wmi wmi;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
index 44fa0cdbf97..11400b39cf0 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
@@ -606,7 +606,8 @@ static int brcmf_sdio_pd_remove(struct platform_device *pdev)
static struct platform_driver brcmf_sdio_pd = {
.remove = brcmf_sdio_pd_remove,
.driver = {
- .name = BRCMFMAC_SDIO_PDATA_NAME
+ .name = BRCMFMAC_SDIO_PDATA_NAME,
+ .owner = THIS_MODULE,
}
};
diff --git a/drivers/net/wireless/cw1200/Kconfig b/drivers/net/wireless/cw1200/Kconfig
new file mode 100644
index 00000000000..13e36119ae9
--- /dev/null
+++ b/drivers/net/wireless/cw1200/Kconfig
@@ -0,0 +1,46 @@
+config CW1200
+ tristate "CW1200 WLAN support"
+ depends on MAC80211 && CFG80211
+ help
+ This is a driver for the ST-E CW1100 & CW1200 WLAN chipsets.
+ This option just enables the driver core, see below for
+ specific bus support.
+
+if CW1200
+
+config CW1200_WLAN_SDIO
+ tristate "Support SDIO platforms"
+ depends on CW1200 && MMC
+ help
+ Enable support for the CW1200 connected via an SDIO bus.
+
+config CW1200_WLAN_SPI
+ tristate "Support SPI platforms"
+ depends on CW1200 && SPI
+ help
+ Enables support for the CW1200 connected via a SPI bus.
+
+config CW1200_WLAN_SAGRAD
+ tristate "Support Sagrad SG901-1091/1098 modules"
+ depends on CW1200_WLAN_SDIO
+ help
+ This provides the platform data glue to support the
+ Sagrad SG901-1091/1098 modules in their standard SDIO EVK.
+ It also includes example SPI platform data.
+
+menu "Driver debug features"
+ depends on CW1200 && DEBUG_FS
+
+config CW1200_ETF
+ bool "Enable CW1200 Engineering Test Framework hooks"
+ help
+ If you don't know what this is, just say N.
+
+config CW1200_ITP
+ bool "Enable ITP access"
+ help
+ If you don't know what this is, just say N.
+
+endmenu
+
+endif
diff --git a/drivers/net/wireless/cw1200/Makefile b/drivers/net/wireless/cw1200/Makefile
new file mode 100644
index 00000000000..c19737276be
--- /dev/null
+++ b/drivers/net/wireless/cw1200/Makefile
@@ -0,0 +1,24 @@
+cw1200_core-y := \
+ fwio.o \
+ txrx.o \
+ main.o \
+ queue.o \
+ hwio.o \
+ bh.o \
+ wsm.o \
+ sta.o \
+ scan.o \
+ pm.o \
+ debug.o
+cw1200_core-$(CONFIG_CW1200_ITP) += itp.o
+
+# CFLAGS_sta.o += -DDEBUG
+
+cw1200_wlan_sdio-y := cw1200_sdio.o
+cw1200_wlan_spi-y := cw1200_spi.o
+cw1200_wlan_sagrad-y := cw1200_sagrad.o
+
+obj-$(CONFIG_CW1200) += cw1200_core.o
+obj-$(CONFIG_CW1200_WLAN_SDIO) += cw1200_wlan_sdio.o
+obj-$(CONFIG_CW1200_WLAN_SPI) += cw1200_wlan_spi.o
+obj-$(CONFIG_CW1200_WLAN_SAGRAD) += cw1200_wlan_sagrad.o
diff --git a/drivers/net/wireless/cw1200/bh.c b/drivers/net/wireless/cw1200/bh.c
new file mode 100644
index 00000000000..cf7375f92fb
--- /dev/null
+++ b/drivers/net/wireless/cw1200/bh.c
@@ -0,0 +1,616 @@
+/*
+ * Device handling thread implementation for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * ST-Ericsson UMAC CW1200 driver, which is
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
+ *
+ * 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/module.h>
+#include <net/mac80211.h>
+#include <linux/kthread.h>
+#include <linux/timer.h>
+
+#include "cw1200.h"
+#include "bh.h"
+#include "hwio.h"
+#include "wsm.h"
+#include "sbus.h"
+#include "debug.h"
+#include "fwio.h"
+
+static int cw1200_bh(void *arg);
+
+#define DOWNLOAD_BLOCK_SIZE_WR (0x1000 - 4)
+/* an SPI message cannot be bigger than (2"12-1)*2 bytes
+ * "*2" to cvt to bytes */
+#define MAX_SZ_RD_WR_BUFFERS (DOWNLOAD_BLOCK_SIZE_WR*2)
+#define PIGGYBACK_CTRL_REG (2)
+#define EFFECTIVE_BUF_SIZE (MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG)
+
+/* Suspend state privates */
+enum cw1200_bh_pm_state {
+ CW1200_BH_RESUMED = 0,
+ CW1200_BH_SUSPEND,
+ CW1200_BH_SUSPENDED,
+ CW1200_BH_RESUME,
+};
+
+typedef int (*cw1200_wsm_handler)(struct cw1200_common *priv,
+ u8 *data, size_t size);
+
+static void cw1200_bh_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, bh_work);
+ cw1200_bh(priv);
+}
+
+int cw1200_register_bh(struct cw1200_common *priv)
+{
+ int err = 0;
+ /* Realtime workqueue */
+ priv->bh_workqueue = alloc_workqueue("cw1200_bh",
+ WQ_MEM_RECLAIM | WQ_HIGHPRI
+ | WQ_CPU_INTENSIVE, 1);
+
+ if (!priv->bh_workqueue)
+ return -ENOMEM;
+
+ INIT_WORK(&priv->bh_work, cw1200_bh_work);
+
+ pr_debug("[BH] register.\n");
+
+ atomic_set(&priv->bh_rx, 0);
+ atomic_set(&priv->bh_tx, 0);
+ atomic_set(&priv->bh_term, 0);
+ atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED);
+ priv->bh_error = 0;
+ priv->hw_bufs_used = 0;
+ priv->buf_id_tx = 0;
+ priv->buf_id_rx = 0;
+ init_waitqueue_head(&priv->bh_wq);
+ init_waitqueue_head(&priv->bh_evt_wq);
+
+ err = !queue_work(priv->bh_workqueue, &priv->bh_work);
+ WARN_ON(err);
+ return err;
+}
+
+void cw1200_unregister_bh(struct cw1200_common *priv)
+{
+ atomic_add(1, &priv->bh_term);
+ wake_up(&priv->bh_wq);
+
+ flush_workqueue(priv->bh_workqueue);
+
+ destroy_workqueue(priv->bh_workqueue);
+ priv->bh_workqueue = NULL;
+
+ pr_debug("[BH] unregistered.\n");
+}
+
+void cw1200_irq_handler(struct cw1200_common *priv)
+{
+ pr_debug("[BH] irq.\n");
+
+ /* Disable Interrupts! */
+ /* NOTE: sbus_ops->lock already held */
+ __cw1200_irq_enable(priv, 0);
+
+ if (/* WARN_ON */(priv->bh_error))
+ return;
+
+ if (atomic_add_return(1, &priv->bh_rx) == 1)
+ wake_up(&priv->bh_wq);
+}
+EXPORT_SYMBOL_GPL(cw1200_irq_handler);
+
+void cw1200_bh_wakeup(struct cw1200_common *priv)
+{
+ pr_debug("[BH] wakeup.\n");
+ if (priv->bh_error) {
+ pr_err("[BH] wakeup failed (BH error)\n");
+ return;
+ }
+
+ if (atomic_add_return(1, &priv->bh_tx) == 1)
+ wake_up(&priv->bh_wq);
+}
+
+int cw1200_bh_suspend(struct cw1200_common *priv)
+{
+ pr_debug("[BH] suspend.\n");
+ if (priv->bh_error) {
+ wiphy_warn(priv->hw->wiphy, "BH error -- can't suspend\n");
+ return -EINVAL;
+ }
+
+ atomic_set(&priv->bh_suspend, CW1200_BH_SUSPEND);
+ wake_up(&priv->bh_wq);
+ return wait_event_timeout(priv->bh_evt_wq, priv->bh_error ||
+ (CW1200_BH_SUSPENDED == atomic_read(&priv->bh_suspend)),
+ 1 * HZ) ? 0 : -ETIMEDOUT;
+}
+
+int cw1200_bh_resume(struct cw1200_common *priv)
+{
+ pr_debug("[BH] resume.\n");
+ if (priv->bh_error) {
+ wiphy_warn(priv->hw->wiphy, "BH error -- can't resume\n");
+ return -EINVAL;
+ }
+
+ atomic_set(&priv->bh_suspend, CW1200_BH_RESUME);
+ wake_up(&priv->bh_wq);
+ return wait_event_timeout(priv->bh_evt_wq, priv->bh_error ||
+ (CW1200_BH_RESUMED == atomic_read(&priv->bh_suspend)),
+ 1 * HZ) ? 0 : -ETIMEDOUT;
+}
+
+static inline void wsm_alloc_tx_buffer(struct cw1200_common *priv)
+{
+ ++priv->hw_bufs_used;
+}
+
+int wsm_release_tx_buffer(struct cw1200_common *priv, int count)
+{
+ int ret = 0;
+ int hw_bufs_used = priv->hw_bufs_used;
+
+ priv->hw_bufs_used -= count;
+ if (WARN_ON(priv->hw_bufs_used < 0))
+ ret = -1;
+ else if (hw_bufs_used >= priv->wsm_caps.input_buffers)
+ ret = 1;
+ if (!priv->hw_bufs_used)
+ wake_up(&priv->bh_evt_wq);
+ return ret;
+}
+
+static int cw1200_bh_read_ctrl_reg(struct cw1200_common *priv,
+ u16 *ctrl_reg)
+{
+ int ret;
+
+ ret = cw1200_reg_read_16(priv,
+ ST90TDS_CONTROL_REG_ID, ctrl_reg);
+ if (ret) {
+ ret = cw1200_reg_read_16(priv,
+ ST90TDS_CONTROL_REG_ID, ctrl_reg);
+ if (ret)
+ pr_err("[BH] Failed to read control register.\n");
+ }
+
+ return ret;
+}
+
+static int cw1200_device_wakeup(struct cw1200_common *priv)
+{
+ u16 ctrl_reg;
+ int ret;
+
+ pr_debug("[BH] Device wakeup.\n");
+
+ /* First, set the dpll register */
+ ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID,
+ cw1200_dpll_from_clk(priv->hw_refclk));
+ if (WARN_ON(ret))
+ return ret;
+
+ /* To force the device to be always-on, the host sets WLAN_UP to 1 */
+ ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID,
+ ST90TDS_CONT_WUP_BIT);
+ if (WARN_ON(ret))
+ return ret;
+
+ ret = cw1200_bh_read_ctrl_reg(priv, &ctrl_reg);
+ if (WARN_ON(ret))
+ return ret;
+
+ /* If the device returns WLAN_RDY as 1, the device is active and will
+ * remain active. */
+ if (ctrl_reg & ST90TDS_CONT_RDY_BIT) {
+ pr_debug("[BH] Device awake.\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Must be called from BH thraed. */
+void cw1200_enable_powersave(struct cw1200_common *priv,
+ bool enable)
+{
+ pr_debug("[BH] Powerave is %s.\n",
+ enable ? "enabled" : "disabled");
+ priv->powersave_enabled = enable;
+}
+
+static int cw1200_bh_rx_helper(struct cw1200_common *priv,
+ uint16_t *ctrl_reg,
+ int *tx)
+{
+ size_t read_len = 0;
+ struct sk_buff *skb_rx = NULL;
+ struct wsm_hdr *wsm;
+ size_t wsm_len;
+ u16 wsm_id;
+ u8 wsm_seq;
+ int rx_resync = 1;
+
+ size_t alloc_len;
+ u8 *data;
+
+ read_len = (*ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2;
+ if (!read_len)
+ return 0; /* No more work */
+
+ if (WARN_ON((read_len < sizeof(struct wsm_hdr)) ||
+ (read_len > EFFECTIVE_BUF_SIZE))) {
+ pr_debug("Invalid read len: %zu (%04x)",
+ read_len, *ctrl_reg);
+ goto err;
+ }
+
+ /* Add SIZE of PIGGYBACK reg (CONTROL Reg)
+ * to the NEXT Message length + 2 Bytes for SKB */
+ read_len = read_len + 2;
+
+ alloc_len = priv->sbus_ops->align_size(
+ priv->sbus_priv, read_len);
+
+ /* Check if not exceeding CW1200 capabilities */
+ if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) {
+ pr_debug("Read aligned len: %zu\n",
+ alloc_len);
+ }
+
+ skb_rx = dev_alloc_skb(alloc_len);
+ if (WARN_ON(!skb_rx))
+ goto err;
+
+ skb_trim(skb_rx, 0);
+ skb_put(skb_rx, read_len);
+ data = skb_rx->data;
+ if (WARN_ON(!data))
+ goto err;
+
+ if (WARN_ON(cw1200_data_read(priv, data, alloc_len))) {
+ pr_err("rx blew up, len %zu\n", alloc_len);
+ goto err;
+ }
+
+ /* Piggyback */
+ *ctrl_reg = __le16_to_cpu(
+ ((__le16 *)data)[alloc_len / 2 - 1]);
+
+ wsm = (struct wsm_hdr *)data;
+ wsm_len = __le16_to_cpu(wsm->len);
+ if (WARN_ON(wsm_len > read_len))
+ goto err;
+
+ if (priv->wsm_enable_wsm_dumps)
+ print_hex_dump_bytes("<-- ",
+ DUMP_PREFIX_NONE,
+ data, wsm_len);
+
+ wsm_id = __le16_to_cpu(wsm->id) & 0xFFF;
+ wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7;
+
+ skb_trim(skb_rx, wsm_len);
+
+ if (wsm_id == 0x0800) {
+ wsm_handle_exception(priv,
+ &data[sizeof(*wsm)],
+ wsm_len - sizeof(*wsm));
+ goto err;
+ } else if (!rx_resync) {
+ if (WARN_ON(wsm_seq != priv->wsm_rx_seq))
+ goto err;
+ }
+ priv->wsm_rx_seq = (wsm_seq + 1) & 7;
+ rx_resync = 0;
+
+ if (wsm_id & 0x0400) {
+ int rc = wsm_release_tx_buffer(priv, 1);
+ if (WARN_ON(rc < 0))
+ return rc;
+ else if (rc > 0)
+ *tx = 1;
+ }
+
+ /* cw1200_wsm_rx takes care on SKB livetime */
+ if (WARN_ON(wsm_handle_rx(priv, wsm_id, wsm, &skb_rx)))
+ goto err;
+
+ if (skb_rx) {
+ dev_kfree_skb(skb_rx);
+ skb_rx = NULL;
+ }
+
+ return 0;
+
+err:
+ if (skb_rx) {
+ dev_kfree_skb(skb_rx);
+ skb_rx = NULL;
+ }
+ return -1;
+}
+
+static int cw1200_bh_tx_helper(struct cw1200_common *priv,
+ int *pending_tx,
+ int *tx_burst)
+{
+ size_t tx_len;
+ u8 *data;
+ int ret;
+ struct wsm_hdr *wsm;
+
+ if (priv->device_can_sleep) {
+ ret = cw1200_device_wakeup(priv);
+ if (WARN_ON(ret < 0)) { /* Error in wakeup */
+ *pending_tx = 1;
+ return 0;
+ } else if (ret) { /* Woke up */
+ priv->device_can_sleep = false;
+ } else { /* Did not awake */
+ *pending_tx = 1;
+ return 0;
+ }
+ }
+
+ wsm_alloc_tx_buffer(priv);
+ ret = wsm_get_tx(priv, &data, &tx_len, tx_burst);
+ if (ret <= 0) {
+ wsm_release_tx_buffer(priv, 1);
+ if (WARN_ON(ret < 0))
+ return ret; /* Error */
+ return 0; /* No work */
+ }
+
+ wsm = (struct wsm_hdr *)data;
+ BUG_ON(tx_len < sizeof(*wsm));
+ BUG_ON(__le16_to_cpu(wsm->len) != tx_len);
+
+ atomic_add(1, &priv->bh_tx);
+
+ tx_len = priv->sbus_ops->align_size(
+ priv->sbus_priv, tx_len);
+
+ /* Check if not exceeding CW1200 capabilities */
+ if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE))
+ pr_debug("Write aligned len: %zu\n", tx_len);
+
+ wsm->id &= __cpu_to_le16(0xffff ^ WSM_TX_SEQ(WSM_TX_SEQ_MAX));
+ wsm->id |= __cpu_to_le16(WSM_TX_SEQ(priv->wsm_tx_seq));
+
+ if (WARN_ON(cw1200_data_write(priv, data, tx_len))) {
+ pr_err("tx blew up, len %zu\n", tx_len);
+ wsm_release_tx_buffer(priv, 1);
+ return -1; /* Error */
+ }
+
+ if (priv->wsm_enable_wsm_dumps)
+ print_hex_dump_bytes("--> ",
+ DUMP_PREFIX_NONE,
+ data,
+ __le16_to_cpu(wsm->len));
+
+ wsm_txed(priv, data);
+ priv->wsm_tx_seq = (priv->wsm_tx_seq + 1) & WSM_TX_SEQ_MAX;
+
+ if (*tx_burst > 1) {
+ cw1200_debug_tx_burst(priv);
+ return 1; /* Work remains */
+ }
+
+ return 0;
+}
+
+static int cw1200_bh(void *arg)
+{
+ struct cw1200_common *priv = arg;
+ int rx, tx, term, suspend;
+ u16 ctrl_reg = 0;
+ int tx_allowed;
+ int pending_tx = 0;
+ int tx_burst;
+ long status;
+ u32 dummy;
+ int ret;
+
+ for (;;) {
+ if (!priv->hw_bufs_used &&
+ priv->powersave_enabled &&
+ !priv->device_can_sleep &&
+ !atomic_read(&priv->recent_scan)) {
+ status = 1 * HZ;
+ pr_debug("[BH] Device wakedown. No data.\n");
+ cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, 0);
+ priv->device_can_sleep = true;
+ } else if (priv->hw_bufs_used) {
+ /* Interrupt loss detection */
+ status = 1 * HZ;
+ } else {
+ status = MAX_SCHEDULE_TIMEOUT;
+ }
+
+ /* Dummy Read for SDIO retry mechanism*/
+ if ((priv->hw_type != -1) &&
+ (atomic_read(&priv->bh_rx) == 0) &&
+ (atomic_read(&priv->bh_tx) == 0))
+ cw1200_reg_read(priv, ST90TDS_CONFIG_REG_ID,
+ &dummy, sizeof(dummy));
+
+ pr_debug("[BH] waiting ...\n");
+ status = wait_event_interruptible_timeout(priv->bh_wq, ({
+ rx = atomic_xchg(&priv->bh_rx, 0);
+ tx = atomic_xchg(&priv->bh_tx, 0);
+ term = atomic_xchg(&priv->bh_term, 0);
+ suspend = pending_tx ?
+ 0 : atomic_read(&priv->bh_suspend);
+ (rx || tx || term || suspend || priv->bh_error);
+ }), status);
+
+ pr_debug("[BH] - rx: %d, tx: %d, term: %d, suspend: %d, status: %ld\n",
+ rx, tx, term, suspend, status);
+
+ /* Did an error occur? */
+ if ((status < 0 && status != -ERESTARTSYS) ||
+ term || priv->bh_error) {
+ break;
+ }
+ if (!status) { /* wait_event timed out */
+ unsigned long timestamp = jiffies;
+ long timeout;
+ int pending = 0;
+ int i;
+
+ /* Check to see if we have any outstanding frames */
+ if (priv->hw_bufs_used && (!rx || !tx)) {
+ wiphy_warn(priv->hw->wiphy,
+ "Missed interrupt? (%d frames outstanding)\n",
+ priv->hw_bufs_used);
+ rx = 1;
+
+ /* Get a timestamp of "oldest" frame */
+ for (i = 0; i < 4; ++i)
+ pending += cw1200_queue_get_xmit_timestamp(
+ &priv->tx_queue[i],
+ &timestamp,
+ priv->pending_frame_id);
+
+ /* Check if frame transmission is timed out.
+ * Add an extra second with respect to possible
+ * interrupt loss.
+ */
+ timeout = timestamp +
+ WSM_CMD_LAST_CHANCE_TIMEOUT +
+ 1 * HZ -
+ jiffies;
+
+ /* And terminate BH thread if the frame is "stuck" */
+ if (pending && timeout < 0) {
+ wiphy_warn(priv->hw->wiphy,
+ "Timeout waiting for TX confirm (%d/%d pending, %ld vs %lu).\n",
+ priv->hw_bufs_used, pending,
+ timestamp, jiffies);
+ break;
+ }
+ } else if (!priv->device_can_sleep &&
+ !atomic_read(&priv->recent_scan)) {
+ pr_debug("[BH] Device wakedown. Timeout.\n");
+ cw1200_reg_write_16(priv,
+ ST90TDS_CONTROL_REG_ID, 0);
+ priv->device_can_sleep = true;
+ }
+ goto done;
+ } else if (suspend) {
+ pr_debug("[BH] Device suspend.\n");
+ if (priv->powersave_enabled) {
+ pr_debug("[BH] Device wakedown. Suspend.\n");
+ cw1200_reg_write_16(priv,
+ ST90TDS_CONTROL_REG_ID, 0);
+ priv->device_can_sleep = true;
+ }
+
+ atomic_set(&priv->bh_suspend, CW1200_BH_SUSPENDED);
+ wake_up(&priv->bh_evt_wq);
+ status = wait_event_interruptible(priv->bh_wq,
+ CW1200_BH_RESUME == atomic_read(&priv->bh_suspend));
+ if (status < 0) {
+ wiphy_err(priv->hw->wiphy,
+ "Failed to wait for resume: %ld.\n",
+ status);
+ break;
+ }
+ pr_debug("[BH] Device resume.\n");
+ atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED);
+ wake_up(&priv->bh_evt_wq);
+ atomic_add(1, &priv->bh_rx);
+ goto done;
+ }
+
+ rx:
+ tx += pending_tx;
+ pending_tx = 0;
+
+ if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg))
+ break;
+
+ /* Don't bother trying to rx unless we have data to read */
+ if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) {
+ ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx);
+ if (ret < 0)
+ break;
+ /* Double up here if there's more data.. */
+ if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) {
+ ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx);
+ if (ret < 0)
+ break;
+ }
+ }
+
+ tx:
+ if (tx) {
+ tx = 0;
+
+ BUG_ON(priv->hw_bufs_used > priv->wsm_caps.input_buffers);
+ tx_burst = priv->wsm_caps.input_buffers - priv->hw_bufs_used;
+ tx_allowed = tx_burst > 0;
+
+ if (!tx_allowed) {
+ /* Buffers full. Ensure we process tx
+ * after we handle rx..
+ */
+ pending_tx = tx;
+ goto done_rx;
+ }
+ ret = cw1200_bh_tx_helper(priv, &pending_tx, &tx_burst);
+ if (ret < 0)
+ break;
+ if (ret > 0) /* More to transmit */
+ tx = ret;
+
+ /* Re-read ctrl reg */
+ if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg))
+ break;
+ }
+
+ done_rx:
+ if (priv->bh_error)
+ break;
+ if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)
+ goto rx;
+ if (tx)
+ goto tx;
+
+ done:
+ /* Re-enable device interrupts */
+ priv->sbus_ops->lock(priv->sbus_priv);
+ __cw1200_irq_enable(priv, 1);
+ priv->sbus_ops->unlock(priv->sbus_priv);
+ }
+
+ /* Explicitly disable device interrupts */
+ priv->sbus_ops->lock(priv->sbus_priv);
+ __cw1200_irq_enable(priv, 0);
+ priv->sbus_ops->unlock(priv->sbus_priv);
+
+ if (!term) {
+ pr_err("[BH] Fatal error, exiting.\n");
+ priv->bh_error = 1;
+ /* TODO: schedule_work(recovery) */
+ }
+ return 0;
+}
diff --git a/drivers/net/wireless/cw1200/bh.h b/drivers/net/wireless/cw1200/bh.h
new file mode 100644
index 00000000000..af6a4853728
--- /dev/null
+++ b/drivers/net/wireless/cw1200/bh.h
@@ -0,0 +1,28 @@
+/*
+ * Device handling thread interface for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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.
+ */
+
+#ifndef CW1200_BH_H
+#define CW1200_BH_H
+
+/* extern */ struct cw1200_common;
+
+int cw1200_register_bh(struct cw1200_common *priv);
+void cw1200_unregister_bh(struct cw1200_common *priv);
+void cw1200_irq_handler(struct cw1200_common *priv);
+void cw1200_bh_wakeup(struct cw1200_common *priv);
+int cw1200_bh_suspend(struct cw1200_common *priv);
+int cw1200_bh_resume(struct cw1200_common *priv);
+/* Must be called from BH thread. */
+void cw1200_enable_powersave(struct cw1200_common *priv,
+ bool enable);
+int wsm_release_tx_buffer(struct cw1200_common *priv, int count);
+
+#endif /* CW1200_BH_H */
diff --git a/drivers/net/wireless/cw1200/cw1200.h b/drivers/net/wireless/cw1200/cw1200.h
new file mode 100644
index 00000000000..2aa17ca60ba
--- /dev/null
+++ b/drivers/net/wireless/cw1200/cw1200.h
@@ -0,0 +1,332 @@
+/*
+ * Common private data for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on the mac80211 Prism54 code, which is
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ *
+ * Based on the islsm (softmac prism54) driver, which is:
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ *
+ * 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.
+ */
+
+#ifndef CW1200_H
+#define CW1200_H
+
+#include <linux/wait.h>
+#include <linux/version.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <net/mac80211.h>
+
+#include "queue.h"
+#include "wsm.h"
+#include "scan.h"
+#include "txrx.h"
+#include "pm.h"
+
+/* Forward declarations */
+struct sbus_ops;
+struct task_struct;
+struct cw1200_debug_priv;
+struct firmware;
+
+#ifdef CONFIG_CW1200_ETF
+extern int etf_mode;
+extern char *etf_firmware;
+#endif
+
+#define CW1200_MAX_CTRL_FRAME_LEN (0x1000)
+
+#define CW1200_MAX_STA_IN_AP_MODE (5)
+#define CW1200_LINK_ID_AFTER_DTIM (CW1200_MAX_STA_IN_AP_MODE + 1)
+#define CW1200_LINK_ID_UAPSD (CW1200_MAX_STA_IN_AP_MODE + 2)
+#define CW1200_LINK_ID_MAX (CW1200_MAX_STA_IN_AP_MODE + 3)
+#define CW1200_MAX_REQUEUE_ATTEMPTS (5)
+
+#define CW1200_MAX_TID (8)
+
+#define CW1200_BLOCK_ACK_CNT (30)
+#define CW1200_BLOCK_ACK_THLD (800)
+#define CW1200_BLOCK_ACK_HIST (3)
+#define CW1200_BLOCK_ACK_INTERVAL (1 * HZ / CW1200_BLOCK_ACK_HIST)
+
+#define CW1200_JOIN_TIMEOUT (1 * HZ)
+#define CW1200_AUTH_TIMEOUT (5 * HZ)
+
+struct cw1200_ht_info {
+ struct ieee80211_sta_ht_cap ht_cap;
+ enum nl80211_channel_type channel_type;
+ u16 operation_mode;
+};
+
+/* Please keep order */
+enum cw1200_join_status {
+ CW1200_JOIN_STATUS_PASSIVE = 0,
+ CW1200_JOIN_STATUS_MONITOR,
+ CW1200_JOIN_STATUS_JOINING,
+ CW1200_JOIN_STATUS_PRE_STA,
+ CW1200_JOIN_STATUS_STA,
+ CW1200_JOIN_STATUS_IBSS,
+ CW1200_JOIN_STATUS_AP,
+};
+
+enum cw1200_link_status {
+ CW1200_LINK_OFF,
+ CW1200_LINK_RESERVE,
+ CW1200_LINK_SOFT,
+ CW1200_LINK_HARD,
+ CW1200_LINK_RESET,
+ CW1200_LINK_RESET_REMAP,
+};
+
+extern int cw1200_power_mode;
+extern const char * const cw1200_fw_types[];
+
+struct cw1200_link_entry {
+ unsigned long timestamp;
+ enum cw1200_link_status status;
+ enum cw1200_link_status prev_status;
+ u8 mac[ETH_ALEN];
+ u8 buffered[CW1200_MAX_TID];
+ struct sk_buff_head rx_queue;
+};
+
+struct cw1200_common {
+ /* interfaces to the rest of the stack */
+ struct ieee80211_hw *hw;
+ struct ieee80211_vif *vif;
+ struct device *pdev;
+
+ /* Statistics */
+ struct ieee80211_low_level_stats stats;
+
+ /* Our macaddr */
+ u8 mac_addr[ETH_ALEN];
+
+ /* Hardware interface */
+ const struct sbus_ops *sbus_ops;
+ struct sbus_priv *sbus_priv;
+
+ /* Hardware information */
+ enum {
+ HIF_9000_SILICON_VERSATILE = 0,
+ HIF_8601_VERSATILE,
+ HIF_8601_SILICON,
+ } hw_type;
+ enum {
+ CW1200_HW_REV_CUT10 = 10,
+ CW1200_HW_REV_CUT11 = 11,
+ CW1200_HW_REV_CUT20 = 20,
+ CW1200_HW_REV_CUT22 = 22,
+ CW1X60_HW_REV = 40,
+ } hw_revision;
+ int hw_refclk;
+ bool hw_have_5ghz;
+ const struct firmware *sdd;
+ char *sdd_path;
+
+ struct cw1200_debug_priv *debug;
+
+ struct workqueue_struct *workqueue;
+ struct mutex conf_mutex;
+
+ struct cw1200_queue tx_queue[4];
+ struct cw1200_queue_stats tx_queue_stats;
+ int tx_burst_idx;
+
+ /* firmware/hardware info */
+ unsigned int tx_hdr_len;
+
+ /* Radio data */
+ int output_power;
+
+ /* BBP/MAC state */
+ struct ieee80211_rate *rates;
+ struct ieee80211_rate *mcs_rates;
+ struct ieee80211_channel *channel;
+ struct wsm_edca_params edca;
+ struct wsm_tx_queue_params tx_queue_params;
+ struct wsm_mib_association_mode association_mode;
+ struct wsm_set_bss_params bss_params;
+ struct cw1200_ht_info ht_info;
+ struct wsm_set_pm powersave_mode;
+ struct wsm_set_pm firmware_ps_mode;
+ int cqm_rssi_thold;
+ unsigned cqm_rssi_hyst;
+ bool cqm_use_rssi;
+ int cqm_beacon_loss_count;
+ int channel_switch_in_progress;
+ wait_queue_head_t channel_switch_done;
+ u8 long_frame_max_tx_count;
+ u8 short_frame_max_tx_count;
+ int mode;
+ bool enable_beacon;
+ int beacon_int;
+ bool listening;
+ struct wsm_rx_filter rx_filter;
+ struct wsm_mib_multicast_filter multicast_filter;
+ bool has_multicast_subscription;
+ bool disable_beacon_filter;
+ struct work_struct update_filtering_work;
+ struct work_struct set_beacon_wakeup_period_work;
+
+ u8 ba_rx_tid_mask;
+ u8 ba_tx_tid_mask;
+
+ struct cw1200_pm_state pm_state;
+
+ struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo;
+ struct wsm_uapsd_info uapsd_info;
+ bool setbssparams_done;
+ bool bt_present;
+ u8 conf_listen_interval;
+ u32 listen_interval;
+ u32 erp_info;
+ u32 rts_threshold;
+
+ /* BH */
+ atomic_t bh_rx;
+ atomic_t bh_tx;
+ atomic_t bh_term;
+ atomic_t bh_suspend;
+
+ struct workqueue_struct *bh_workqueue;
+ struct work_struct bh_work;
+
+ int bh_error;
+ wait_queue_head_t bh_wq;
+ wait_queue_head_t bh_evt_wq;
+ u8 buf_id_tx;
+ u8 buf_id_rx;
+ u8 wsm_rx_seq;
+ u8 wsm_tx_seq;
+ int hw_bufs_used;
+ bool powersave_enabled;
+ bool device_can_sleep;
+
+ /* Scan status */
+ struct cw1200_scan scan;
+ /* Keep cw1200 awake (WUP = 1) 1 second after each scan to avoid
+ * FW issue with sleeping/waking up. */
+ atomic_t recent_scan;
+ struct delayed_work clear_recent_scan_work;
+
+ /* WSM */
+ struct wsm_startup_ind wsm_caps;
+ struct mutex wsm_cmd_mux;
+ struct wsm_buf wsm_cmd_buf;
+ struct wsm_cmd wsm_cmd;
+ wait_queue_head_t wsm_cmd_wq;
+ wait_queue_head_t wsm_startup_done;
+ int firmware_ready;
+ atomic_t tx_lock;
+
+ /* WSM debug */
+ int wsm_enable_wsm_dumps;
+
+ /* WSM Join */
+ enum cw1200_join_status join_status;
+ u32 pending_frame_id;
+ bool join_pending;
+ struct delayed_work join_timeout;
+ struct work_struct unjoin_work;
+ struct work_struct join_complete_work;
+ int join_complete_status;
+ int join_dtim_period;
+ bool delayed_unjoin;
+
+ /* TX/RX and security */
+ s8 wep_default_key_id;
+ struct work_struct wep_key_work;
+ u32 key_map;
+ struct wsm_add_key keys[WSM_KEY_MAX_INDEX + 1];
+
+ /* AP powersave */
+ u32 link_id_map;
+ struct cw1200_link_entry link_id_db[CW1200_MAX_STA_IN_AP_MODE];
+ struct work_struct link_id_work;
+ struct delayed_work link_id_gc_work;
+ u32 sta_asleep_mask;
+ u32 pspoll_mask;
+ bool aid0_bit_set;
+ spinlock_t ps_state_lock; /* Protect power save state */
+ bool buffered_multicasts;
+ bool tx_multicast;
+ struct work_struct set_tim_work;
+ struct work_struct set_cts_work;
+ struct work_struct multicast_start_work;
+ struct work_struct multicast_stop_work;
+ struct timer_list mcast_timeout;
+
+ /* WSM events and CQM implementation */
+ spinlock_t event_queue_lock; /* Protect event queue */
+ struct list_head event_queue;
+ struct work_struct event_handler;
+
+ struct delayed_work bss_loss_work;
+ spinlock_t bss_loss_lock; /* Protect BSS loss state */
+ int bss_loss_state;
+ int bss_loss_confirm_id;
+ int delayed_link_loss;
+ struct work_struct bss_params_work;
+
+ /* TX rate policy cache */
+ struct tx_policy_cache tx_policy_cache;
+ struct work_struct tx_policy_upload_work;
+
+ /* legacy PS mode switch in suspend */
+ int ps_mode_switch_in_progress;
+ wait_queue_head_t ps_mode_switch_done;
+
+ /* Workaround for WFD testcase 6.1.10*/
+ struct work_struct linkid_reset_work;
+ u8 action_frame_sa[ETH_ALEN];
+ u8 action_linkid;
+
+#ifdef CONFIG_CW1200_ETF
+ struct sk_buff_head etf_q;
+#endif
+};
+
+struct cw1200_sta_priv {
+ int link_id;
+};
+
+/* interfaces for the drivers */
+int cw1200_core_probe(const struct sbus_ops *sbus_ops,
+ struct sbus_priv *sbus,
+ struct device *pdev,
+ struct cw1200_common **pself,
+ int ref_clk, const u8 *macaddr,
+ const char *sdd_path, bool have_5ghz);
+void cw1200_core_release(struct cw1200_common *self);
+
+#define FWLOAD_BLOCK_SIZE (1024)
+
+static inline int cw1200_is_ht(const struct cw1200_ht_info *ht_info)
+{
+ return ht_info->channel_type != NL80211_CHAN_NO_HT;
+}
+
+static inline int cw1200_ht_greenfield(const struct cw1200_ht_info *ht_info)
+{
+ return cw1200_is_ht(ht_info) &&
+ (ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) &&
+ !(ht_info->operation_mode &
+ IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
+}
+
+static inline int cw1200_ht_ampdu_density(const struct cw1200_ht_info *ht_info)
+{
+ if (!cw1200_is_ht(ht_info))
+ return 0;
+ return ht_info->ht_cap.ampdu_density;
+}
+
+#endif /* CW1200_H */
diff --git a/drivers/net/wireless/cw1200/cw1200_sagrad.c b/drivers/net/wireless/cw1200/cw1200_sagrad.c
new file mode 100644
index 00000000000..a5ada0eda1d
--- /dev/null
+++ b/drivers/net/wireless/cw1200/cw1200_sagrad.c
@@ -0,0 +1,145 @@
+/*
+ * Platform glue data for ST-Ericsson CW1200 driver
+ *
+ * Copyright (c) 2013, Sagrad, Inc
+ * Author: Solomon Peachy <speachy@sagrad.com>
+ *
+ * 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/module.h>
+#include <linux/cw1200_platform.h>
+
+MODULE_AUTHOR("Solomon Peachy <speachy@sagrad.com>");
+MODULE_DESCRIPTION("ST-Ericsson CW1200 Platform glue driver");
+MODULE_LICENSE("GPL");
+
+/* Define just one of these. Feel free to customize as needed */
+#define SAGRAD_1091_1098_EVK_SDIO
+/* #define SAGRAD_1091_1098_EVK_SPI */
+
+#ifdef SAGRAD_1091_1098_EVK_SDIO
+#if 0
+static struct resource cw1200_href_resources[] = {
+ {
+ .start = 215, /* fix me as appropriate */
+ .end = 215, /* ditto */
+ .flags = IORESOURCE_IO,
+ .name = "cw1200_wlan_reset",
+ },
+ {
+ .start = 216, /* fix me as appropriate */
+ .end = 216, /* ditto */
+ .flags = IORESOURCE_IO,
+ .name = "cw1200_wlan_powerup",
+ },
+ {
+ .start = NOMADIK_GPIO_TO_IRQ(216), /* fix me as appropriate */
+ .end = NOMADIK_GPIO_TO_IRQ(216), /* ditto */
+ .flags = IORESOURCE_IRQ,
+ .name = "cw1200_wlan_irq",
+ },
+};
+#endif
+
+static int cw1200_power_ctrl(const struct cw1200_platform_data_sdio *pdata,
+ bool enable)
+{
+ /* Control 3v3 and 1v8 to hardware as appropriate */
+ /* Note this is not needed if it's controlled elsewhere or always on */
+
+ /* May require delay for power to stabilize */
+ return 0;
+}
+
+static int cw1200_clk_ctrl(const struct cw1200_platform_data_sdio *pdata,
+ bool enable)
+{
+ /* Turn CLK_32K off and on as appropriate. */
+ /* Note this is not needed if it's always on */
+
+ /* May require delay for clock to stabilize */
+ return 0;
+}
+
+static struct cw1200_platform_data_sdio cw1200_platform_data = {
+ .ref_clk = 38400,
+ .have_5ghz = false,
+#if 0
+ .reset = &cw1200_href_resources[0],
+ .powerup = &cw1200_href_resources[1],
+ .irq = &cw1200_href_resources[2],
+#endif
+ .power_ctrl = cw1200_power_ctrl,
+ .clk_ctrl = cw1200_clk_ctrl,
+/* .macaddr = ??? */
+ .sdd_file = "sdd_sagrad_1091_1098.bin",
+};
+#endif
+
+#ifdef SAGRAD_1091_1098_EVK_SPI
+/* Note that this is an example of integrating into your board support file */
+static struct resource cw1200_href_resources[] = {
+ {
+ .start = GPIO_RF_RESET,
+ .end = GPIO_RF_RESET,
+ .flags = IORESOURCE_IO,
+ .name = "cw1200_wlan_reset",
+ },
+ {
+ .start = GPIO_RF_POWERUP,
+ .end = GPIO_RF_POWERUP,
+ .flags = IORESOURCE_IO,
+ .name = "cw1200_wlan_powerup",
+ },
+};
+
+static int cw1200_power_ctrl(const struct cw1200_platform_data_spi *pdata,
+ bool enable)
+{
+ /* Control 3v3 and 1v8 to hardware as appropriate */
+ /* Note this is not needed if it's controlled elsewhere or always on */
+
+ /* May require delay for power to stabilize */
+ return 0;
+}
+static int cw1200_clk_ctrl(const struct cw1200_platform_data_spi *pdata,
+ bool enable)
+{
+ /* Turn CLK_32K off and on as appropriate. */
+ /* Note this is not needed if it's always on */
+
+ /* May require delay for clock to stabilize */
+ return 0;
+}
+
+static struct cw1200_platform_data_spi cw1200_platform_data = {
+ .ref_clk = 38400,
+ .spi_bits_per_word = 16,
+ .reset = &cw1200_href_resources[0],
+ .powerup = &cw1200_href_resources[1],
+ .power_ctrl = cw1200_power_ctrl,
+ .clk_ctrl = cw1200_clk_ctrl,
+/* .macaddr = ??? */
+ .sdd_file = "sdd_sagrad_1091_1098.bin",
+};
+static struct spi_board_info myboard_spi_devices[] __initdata = {
+ {
+ .modalias = "cw1200_wlan_spi",
+ .max_speed_hz = 10000000, /* 52MHz Max */
+ .bus_num = 0,
+ .irq = WIFI_IRQ,
+ .platform_data = &cw1200_platform_data,
+ .chip_select = 0,
+ },
+};
+#endif
+
+
+const void *cw1200_get_platform_data(void)
+{
+ return &cw1200_platform_data;
+}
+EXPORT_SYMBOL_GPL(cw1200_get_platform_data);
diff --git a/drivers/net/wireless/cw1200/cw1200_sdio.c b/drivers/net/wireless/cw1200/cw1200_sdio.c
new file mode 100644
index 00000000000..f6e22192d80
--- /dev/null
+++ b/drivers/net/wireless/cw1200/cw1200_sdio.c
@@ -0,0 +1,409 @@
+/*
+ * Mac80211 SDIO driver for ST-Ericsson CW1200 device
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/version.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+#include <net/mac80211.h>
+
+#include "cw1200.h"
+#include "sbus.h"
+#include <linux/cw1200_platform.h>
+#include "hwio.h"
+
+MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>");
+MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SDIO driver");
+MODULE_LICENSE("GPL");
+
+#define SDIO_BLOCK_SIZE (512)
+
+struct sbus_priv {
+ struct sdio_func *func;
+ struct cw1200_common *core;
+ const struct cw1200_platform_data_sdio *pdata;
+};
+
+#ifndef SDIO_VENDOR_ID_STE
+#define SDIO_VENDOR_ID_STE 0x0020
+#endif
+
+#ifndef SDIO_DEVICE_ID_STE_CW1200
+#define SDIO_DEVICE_ID_STE_CW1200 0x2280
+#endif
+
+static const struct sdio_device_id cw1200_sdio_ids[] = {
+ { SDIO_DEVICE(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200) },
+ { /* end: all zeroes */ },
+};
+
+/* sbus_ops implemetation */
+
+static int cw1200_sdio_memcpy_fromio(struct sbus_priv *self,
+ unsigned int addr,
+ void *dst, int count)
+{
+ return sdio_memcpy_fromio(self->func, dst, addr, count);
+}
+
+static int cw1200_sdio_memcpy_toio(struct sbus_priv *self,
+ unsigned int addr,
+ const void *src, int count)
+{
+ return sdio_memcpy_toio(self->func, addr, (void *)src, count);
+}
+
+static void cw1200_sdio_lock(struct sbus_priv *self)
+{
+ sdio_claim_host(self->func);
+}
+
+static void cw1200_sdio_unlock(struct sbus_priv *self)
+{
+ sdio_release_host(self->func);
+}
+
+static void cw1200_sdio_irq_handler(struct sdio_func *func)
+{
+ struct sbus_priv *self = sdio_get_drvdata(func);
+
+ /* note: sdio_host already claimed here. */
+ if (self->core)
+ cw1200_irq_handler(self->core);
+}
+
+static irqreturn_t cw1200_gpio_hardirq(int irq, void *dev_id)
+{
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t cw1200_gpio_irq(int irq, void *dev_id)
+{
+ struct sbus_priv *self = dev_id;
+
+ if (self->core) {
+ sdio_claim_host(self->func);
+ cw1200_irq_handler(self->core);
+ sdio_release_host(self->func);
+ return IRQ_HANDLED;
+ } else {
+ return IRQ_NONE;
+ }
+}
+
+static int cw1200_request_irq(struct sbus_priv *self)
+{
+ int ret;
+ const struct resource *irq = self->pdata->irq;
+ u8 cccr;
+
+ cccr = sdio_f0_readb(self->func, SDIO_CCCR_IENx, &ret);
+ if (WARN_ON(ret))
+ goto err;
+
+ /* Master interrupt enable ... */
+ cccr |= BIT(0);
+
+ /* ... for our function */
+ cccr |= BIT(self->func->num);
+
+ sdio_f0_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret);
+ if (WARN_ON(ret))
+ goto err;
+
+ ret = enable_irq_wake(irq->start);
+ if (WARN_ON(ret))
+ goto err;
+
+ /* Request the IRQ */
+ ret = request_threaded_irq(irq->start, cw1200_gpio_hardirq,
+ cw1200_gpio_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ irq->name, self);
+ if (WARN_ON(ret))
+ goto err;
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static int cw1200_sdio_irq_subscribe(struct sbus_priv *self)
+{
+ int ret = 0;
+
+ pr_debug("SW IRQ subscribe\n");
+ sdio_claim_host(self->func);
+ if (self->pdata->irq)
+ ret = cw1200_request_irq(self);
+ else
+ ret = sdio_claim_irq(self->func, cw1200_sdio_irq_handler);
+
+ sdio_release_host(self->func);
+ return ret;
+}
+
+static int cw1200_sdio_irq_unsubscribe(struct sbus_priv *self)
+{
+ int ret = 0;
+
+ pr_debug("SW IRQ unsubscribe\n");
+
+ if (self->pdata->irq) {
+ disable_irq_wake(self->pdata->irq->start);
+ free_irq(self->pdata->irq->start, self);
+ } else {
+ sdio_claim_host(self->func);
+ ret = sdio_release_irq(self->func);
+ sdio_release_host(self->func);
+ }
+ return ret;
+}
+
+static int cw1200_sdio_off(const struct cw1200_platform_data_sdio *pdata)
+{
+ const struct resource *reset = pdata->reset;
+
+ if (reset) {
+ gpio_set_value(reset->start, 0);
+ msleep(30); /* Min is 2 * CLK32K cycles */
+ gpio_free(reset->start);
+ }
+
+ if (pdata->power_ctrl)
+ pdata->power_ctrl(pdata, false);
+ if (pdata->clk_ctrl)
+ pdata->clk_ctrl(pdata, false);
+
+ return 0;
+}
+
+static int cw1200_sdio_on(const struct cw1200_platform_data_sdio *pdata)
+{
+ const struct resource *reset = pdata->reset;
+ const struct resource *powerup = pdata->reset;
+
+ /* Ensure I/Os are pulled low */
+ if (reset) {
+ gpio_request(reset->start, reset->name);
+ gpio_direction_output(reset->start, 0);
+ }
+ if (powerup) {
+ gpio_request(powerup->start, powerup->name);
+ gpio_direction_output(powerup->start, 0);
+ }
+ if (reset || powerup)
+ msleep(50); /* Settle time */
+
+ /* Enable 3v3 and 1v8 to hardware */
+ if (pdata->power_ctrl) {
+ if (pdata->power_ctrl(pdata, true)) {
+ pr_err("power_ctrl() failed!\n");
+ return -1;
+ }
+ }
+
+ /* Enable CLK32K */
+ if (pdata->clk_ctrl) {
+ if (pdata->clk_ctrl(pdata, true)) {
+ pr_err("clk_ctrl() failed!\n");
+ return -1;
+ }
+ msleep(10); /* Delay until clock is stable for 2 cycles */
+ }
+
+ /* Enable POWERUP signal */
+ if (powerup) {
+ gpio_set_value(powerup->start, 1);
+ msleep(250); /* or more..? */
+ }
+ /* Enable RSTn signal */
+ if (reset) {
+ gpio_set_value(reset->start, 1);
+ msleep(50); /* Or more..? */
+ }
+ return 0;
+}
+
+static size_t cw1200_sdio_align_size(struct sbus_priv *self, size_t size)
+{
+ if (self->pdata->no_nptb)
+ size = round_up(size, SDIO_BLOCK_SIZE);
+ else
+ size = sdio_align_size(self->func, size);
+
+ return size;
+}
+
+static int cw1200_sdio_pm(struct sbus_priv *self, bool suspend)
+{
+ int ret = 0;
+
+ if (self->pdata->irq)
+ ret = irq_set_irq_wake(self->pdata->irq->start, suspend);
+ return ret;
+}
+
+static struct sbus_ops cw1200_sdio_sbus_ops = {
+ .sbus_memcpy_fromio = cw1200_sdio_memcpy_fromio,
+ .sbus_memcpy_toio = cw1200_sdio_memcpy_toio,
+ .lock = cw1200_sdio_lock,
+ .unlock = cw1200_sdio_unlock,
+ .align_size = cw1200_sdio_align_size,
+ .power_mgmt = cw1200_sdio_pm,
+};
+
+/* Probe Function to be called by SDIO stack when device is discovered */
+static int cw1200_sdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ struct sbus_priv *self;
+ int status;
+
+ pr_info("cw1200_wlan_sdio: Probe called\n");
+
+ /* We are only able to handle the wlan function */
+ if (func->num != 0x01)
+ return -ENODEV;
+
+ self = kzalloc(sizeof(*self), GFP_KERNEL);
+ if (!self) {
+ pr_err("Can't allocate SDIO sbus_priv.\n");
+ return -ENOMEM;
+ }
+
+ func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
+
+ self->pdata = cw1200_get_platform_data();
+ self->func = func;
+ sdio_set_drvdata(func, self);
+ sdio_claim_host(func);
+ sdio_enable_func(func);
+ sdio_release_host(func);
+
+ status = cw1200_sdio_irq_subscribe(self);
+
+ status = cw1200_core_probe(&cw1200_sdio_sbus_ops,
+ self, &func->dev, &self->core,
+ self->pdata->ref_clk,
+ self->pdata->macaddr,
+ self->pdata->sdd_file,
+ self->pdata->have_5ghz);
+ if (status) {
+ cw1200_sdio_irq_unsubscribe(self);
+ sdio_claim_host(func);
+ sdio_disable_func(func);
+ sdio_release_host(func);
+ sdio_set_drvdata(func, NULL);
+ kfree(self);
+ }
+
+ return status;
+}
+
+/* Disconnect Function to be called by SDIO stack when
+ * device is disconnected */
+static void cw1200_sdio_disconnect(struct sdio_func *func)
+{
+ struct sbus_priv *self = sdio_get_drvdata(func);
+
+ if (self) {
+ cw1200_sdio_irq_unsubscribe(self);
+ if (self->core) {
+ cw1200_core_release(self->core);
+ self->core = NULL;
+ }
+ sdio_claim_host(func);
+ sdio_disable_func(func);
+ sdio_release_host(func);
+ sdio_set_drvdata(func, NULL);
+ kfree(self);
+ }
+}
+
+static int cw1200_sdio_suspend(struct device *dev)
+{
+ int ret;
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ struct sbus_priv *self = sdio_get_drvdata(func);
+
+ if (!cw1200_can_suspend(self->core))
+ return -EAGAIN;
+
+ /* Notify SDIO that CW1200 will remain powered during suspend */
+ ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+ if (ret)
+ pr_err("Error setting SDIO pm flags: %i\n", ret);
+
+ return ret;
+}
+
+static int cw1200_sdio_resume(struct device *dev)
+{
+ return 0;
+}
+
+static const struct dev_pm_ops cw1200_pm_ops = {
+ .suspend = cw1200_sdio_suspend,
+ .resume = cw1200_sdio_resume,
+};
+
+static struct sdio_driver sdio_driver = {
+ .name = "cw1200_wlan_sdio",
+ .id_table = cw1200_sdio_ids,
+ .probe = cw1200_sdio_probe,
+ .remove = cw1200_sdio_disconnect,
+ .drv = {
+ .pm = &cw1200_pm_ops,
+ }
+};
+
+/* Init Module function -> Called by insmod */
+static int __init cw1200_sdio_init(void)
+{
+ const struct cw1200_platform_data_sdio *pdata;
+ int ret;
+
+ pdata = cw1200_get_platform_data();
+
+ if (cw1200_sdio_on(pdata)) {
+ ret = -1;
+ goto err;
+ }
+
+ ret = sdio_register_driver(&sdio_driver);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ cw1200_sdio_off(pdata);
+ return ret;
+}
+
+/* Called at Driver Unloading */
+static void __exit cw1200_sdio_exit(void)
+{
+ const struct cw1200_platform_data_sdio *pdata;
+ pdata = cw1200_get_platform_data();
+ sdio_unregister_driver(&sdio_driver);
+ cw1200_sdio_off(pdata);
+}
+
+
+module_init(cw1200_sdio_init);
+module_exit(cw1200_sdio_exit);
diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c
new file mode 100644
index 00000000000..04af6853485
--- /dev/null
+++ b/drivers/net/wireless/cw1200/cw1200_spi.c
@@ -0,0 +1,480 @@
+/*
+ * Mac80211 SPI driver for ST-Ericsson CW1200 device
+ *
+ * Copyright (c) 2011, Sagrad Inc.
+ * Author: Solomon Peachy <speachy@sagrad.com>
+ *
+ * Based on cw1200_sdio.c
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/version.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <net/mac80211.h>
+
+#include <linux/spi/spi.h>
+#include <linux/device.h>
+
+#include "cw1200.h"
+#include "sbus.h"
+#include <linux/cw1200_platform.h>
+#include "hwio.h"
+
+MODULE_AUTHOR("Solomon Peachy <speachy@sagrad.com>");
+MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SPI driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:cw1200_wlan_spi");
+
+/* #define SPI_DEBUG */
+
+struct sbus_priv {
+ struct spi_device *func;
+ struct cw1200_common *core;
+ const struct cw1200_platform_data_spi *pdata;
+ spinlock_t lock; /* Serialize all bus operations */
+ int claimed;
+};
+
+#define SDIO_TO_SPI_ADDR(addr) ((addr & 0x1f)>>2)
+#define SET_WRITE 0x7FFF /* usage: and operation */
+#define SET_READ 0x8000 /* usage: or operation */
+
+/*
+ Notes on byte ordering:
+ LE: B0 B1 B2 B3
+ BE: B3 B2 B1 B0
+
+ Hardware expects 32-bit data to be written as 16-bit BE words:
+
+ B1 B0 B3 B2
+
+*/
+
+static int cw1200_spi_memcpy_fromio(struct sbus_priv *self,
+ unsigned int addr,
+ void *dst, int count)
+{
+ int ret, i;
+ uint16_t regaddr;
+ struct spi_message m;
+
+ struct spi_transfer t_addr = {
+ .tx_buf = &regaddr,
+ .len = sizeof(regaddr),
+ };
+ struct spi_transfer t_msg = {
+ .rx_buf = dst,
+ .len = count,
+ };
+
+ regaddr = (SDIO_TO_SPI_ADDR(addr))<<12;
+ regaddr |= SET_READ;
+ regaddr |= (count>>1);
+ regaddr = cpu_to_le16(regaddr);
+
+#ifdef SPI_DEBUG
+ pr_info("READ : %04d from 0x%02x (%04x)\n", count, addr,
+ le16_to_cpu(regaddr));
+#endif
+
+#if defined(__LITTLE_ENDIAN)
+ /* We have to byteswap if the SPI bus is limited to 8b operation */
+ if (self->func->bits_per_word == 8)
+#endif
+ regaddr = swab16(regaddr);
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t_addr, &m);
+ spi_message_add_tail(&t_msg, &m);
+ ret = spi_sync(self->func, &m);
+
+#ifdef SPI_DEBUG
+ pr_info("READ : ");
+ for (i = 0; i < t_addr.len; i++)
+ printk("%02x ", ((u8 *)t_addr.tx_buf)[i]);
+ printk(" : ");
+ for (i = 0; i < t_msg.len; i++)
+ printk("%02x ", ((u8 *)t_msg.rx_buf)[i]);
+ printk("\n");
+#endif
+
+#if defined(__LITTLE_ENDIAN)
+ /* We have to byteswap if the SPI bus is limited to 8b operation */
+ if (self->func->bits_per_word == 8)
+#endif
+ {
+ uint16_t *buf = (uint16_t *)dst;
+ for (i = 0; i < ((count + 1) >> 1); i++)
+ buf[i] = swab16(buf[i]);
+ }
+
+ return ret;
+}
+
+static int cw1200_spi_memcpy_toio(struct sbus_priv *self,
+ unsigned int addr,
+ const void *src, int count)
+{
+ int rval, i;
+ uint16_t regaddr;
+ struct spi_transfer t_addr = {
+ .tx_buf = &regaddr,
+ .len = sizeof(regaddr),
+ };
+ struct spi_transfer t_msg = {
+ .tx_buf = src,
+ .len = count,
+ };
+ struct spi_message m;
+
+ regaddr = (SDIO_TO_SPI_ADDR(addr))<<12;
+ regaddr &= SET_WRITE;
+ regaddr |= (count>>1);
+ regaddr = cpu_to_le16(regaddr);
+
+#ifdef SPI_DEBUG
+ pr_info("WRITE: %04d to 0x%02x (%04x)\n", count, addr,
+ le16_to_cpu(regaddr));
+#endif
+
+#if defined(__LITTLE_ENDIAN)
+ /* We have to byteswap if the SPI bus is limited to 8b operation */
+ if (self->func->bits_per_word == 8)
+#endif
+ {
+ uint16_t *buf = (uint16_t *)src;
+ regaddr = swab16(regaddr);
+ for (i = 0; i < ((count + 1) >> 1); i++)
+ buf[i] = swab16(buf[i]);
+ }
+
+#ifdef SPI_DEBUG
+ pr_info("WRITE: ");
+ for (i = 0; i < t_addr.len; i++)
+ printk("%02x ", ((u8 *)t_addr.tx_buf)[i]);
+ printk(" : ");
+ for (i = 0; i < t_msg.len; i++)
+ printk("%02x ", ((u8 *)t_msg.tx_buf)[i]);
+ printk("\n");
+#endif
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t_addr, &m);
+ spi_message_add_tail(&t_msg, &m);
+ rval = spi_sync(self->func, &m);
+
+#ifdef SPI_DEBUG
+ pr_info("WROTE: %d\n", m.actual_length);
+#endif
+
+#if defined(__LITTLE_ENDIAN)
+ /* We have to byteswap if the SPI bus is limited to 8b operation */
+ if (self->func->bits_per_word == 8)
+#endif
+ {
+ uint16_t *buf = (uint16_t *)src;
+ for (i = 0; i < ((count + 1) >> 1); i++)
+ buf[i] = swab16(buf[i]);
+ }
+ return rval;
+}
+
+static void cw1200_spi_lock(struct sbus_priv *self)
+{
+ unsigned long flags;
+
+ might_sleep();
+
+ spin_lock_irqsave(&self->lock, flags);
+ while (1) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (!self->claimed)
+ break;
+ spin_unlock_irqrestore(&self->lock, flags);
+ schedule();
+ spin_lock_irqsave(&self->lock, flags);
+ }
+ set_current_state(TASK_RUNNING);
+ self->claimed = 1;
+ spin_unlock_irqrestore(&self->lock, flags);
+
+ return;
+}
+
+static void cw1200_spi_unlock(struct sbus_priv *self)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&self->lock, flags);
+ self->claimed = 0;
+ spin_unlock_irqrestore(&self->lock, flags);
+ return;
+}
+
+static irqreturn_t cw1200_spi_irq_handler(int irq, void *dev_id)
+{
+ struct sbus_priv *self = dev_id;
+
+ if (self->core) {
+ cw1200_irq_handler(self->core);
+ return IRQ_HANDLED;
+ } else {
+ return IRQ_NONE;
+ }
+}
+
+static int cw1200_spi_irq_subscribe(struct sbus_priv *self)
+{
+ int ret;
+
+ pr_debug("SW IRQ subscribe\n");
+
+ ret = request_any_context_irq(self->func->irq, cw1200_spi_irq_handler,
+ IRQF_TRIGGER_HIGH,
+ "cw1200_wlan_irq", self);
+ if (WARN_ON(ret < 0))
+ goto exit;
+
+ ret = enable_irq_wake(self->func->irq);
+ if (WARN_ON(ret))
+ goto free_irq;
+
+ return 0;
+
+free_irq:
+ free_irq(self->func->irq, self);
+exit:
+ return ret;
+}
+
+static int cw1200_spi_irq_unsubscribe(struct sbus_priv *self)
+{
+ int ret = 0;
+
+ pr_debug("SW IRQ unsubscribe\n");
+ disable_irq_wake(self->func->irq);
+ free_irq(self->func->irq, self);
+
+ return ret;
+}
+
+static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata)
+{
+ const struct resource *reset = pdata->reset;
+
+ if (reset) {
+ gpio_set_value(reset->start, 0);
+ msleep(30); /* Min is 2 * CLK32K cycles */
+ gpio_free(reset->start);
+ }
+
+ if (pdata->power_ctrl)
+ pdata->power_ctrl(pdata, false);
+ if (pdata->clk_ctrl)
+ pdata->clk_ctrl(pdata, false);
+
+ return 0;
+}
+
+static int cw1200_spi_on(const struct cw1200_platform_data_spi *pdata)
+{
+ const struct resource *reset = pdata->reset;
+ const struct resource *powerup = pdata->reset;
+
+ /* Ensure I/Os are pulled low */
+ if (reset) {
+ gpio_request(reset->start, reset->name);
+ gpio_direction_output(reset->start, 0);
+ }
+ if (powerup) {
+ gpio_request(powerup->start, powerup->name);
+ gpio_direction_output(powerup->start, 0);
+ }
+ if (reset || powerup)
+ msleep(10); /* Settle time? */
+
+ /* Enable 3v3 and 1v8 to hardware */
+ if (pdata->power_ctrl) {
+ if (pdata->power_ctrl(pdata, true)) {
+ pr_err("power_ctrl() failed!\n");
+ return -1;
+ }
+ }
+
+ /* Enable CLK32K */
+ if (pdata->clk_ctrl) {
+ if (pdata->clk_ctrl(pdata, true)) {
+ pr_err("clk_ctrl() failed!\n");
+ return -1;
+ }
+ msleep(10); /* Delay until clock is stable for 2 cycles */
+ }
+
+ /* Enable POWERUP signal */
+ if (powerup) {
+ gpio_set_value(powerup->start, 1);
+ msleep(250); /* or more..? */
+ }
+ /* Enable RSTn signal */
+ if (reset) {
+ gpio_set_value(reset->start, 1);
+ msleep(50); /* Or more..? */
+ }
+ return 0;
+}
+
+static size_t cw1200_spi_align_size(struct sbus_priv *self, size_t size)
+{
+ return size & 1 ? size + 1 : size;
+}
+
+static int cw1200_spi_pm(struct sbus_priv *self, bool suspend)
+{
+ return irq_set_irq_wake(self->func->irq, suspend);
+}
+
+static struct sbus_ops cw1200_spi_sbus_ops = {
+ .sbus_memcpy_fromio = cw1200_spi_memcpy_fromio,
+ .sbus_memcpy_toio = cw1200_spi_memcpy_toio,
+ .lock = cw1200_spi_lock,
+ .unlock = cw1200_spi_unlock,
+ .align_size = cw1200_spi_align_size,
+ .power_mgmt = cw1200_spi_pm,
+};
+
+/* Probe Function to be called by SPI stack when device is discovered */
+static int cw1200_spi_probe(struct spi_device *func)
+{
+ const struct cw1200_platform_data_spi *plat_data =
+ func->dev.platform_data;
+ struct sbus_priv *self;
+ int status;
+
+ /* Sanity check speed */
+ if (func->max_speed_hz > 52000000)
+ func->max_speed_hz = 52000000;
+ if (func->max_speed_hz < 1000000)
+ func->max_speed_hz = 1000000;
+
+ /* Fix up transfer size */
+ if (plat_data->spi_bits_per_word)
+ func->bits_per_word = plat_data->spi_bits_per_word;
+ if (!func->bits_per_word)
+ func->bits_per_word = 16;
+
+ /* And finally.. */
+ func->mode = SPI_MODE_0;
+
+ pr_info("cw1200_wlan_spi: Probe called (CS %d M %d BPW %d CLK %d)\n",
+ func->chip_select, func->mode, func->bits_per_word,
+ func->max_speed_hz);
+
+ if (cw1200_spi_on(plat_data)) {
+ pr_err("spi_on() failed!\n");
+ return -1;
+ }
+
+ if (spi_setup(func)) {
+ pr_err("spi_setup() failed!\n");
+ return -1;
+ }
+
+ self = kzalloc(sizeof(*self), GFP_KERNEL);
+ if (!self) {
+ pr_err("Can't allocate SPI sbus_priv.");
+ return -ENOMEM;
+ }
+
+ self->pdata = plat_data;
+ self->func = func;
+ spin_lock_init(&self->lock);
+
+ spi_set_drvdata(func, self);
+
+ status = cw1200_spi_irq_subscribe(self);
+
+ status = cw1200_core_probe(&cw1200_spi_sbus_ops,
+ self, &func->dev, &self->core,
+ self->pdata->ref_clk,
+ self->pdata->macaddr,
+ self->pdata->sdd_file,
+ self->pdata->have_5ghz);
+
+ if (status) {
+ cw1200_spi_irq_unsubscribe(self);
+ cw1200_spi_off(plat_data);
+ kfree(self);
+ }
+
+ return status;
+}
+
+/* Disconnect Function to be called by SPI stack when device is disconnected */
+static int cw1200_spi_disconnect(struct spi_device *func)
+{
+ struct sbus_priv *self = spi_get_drvdata(func);
+
+ if (self) {
+ cw1200_spi_irq_unsubscribe(self);
+ if (self->core) {
+ cw1200_core_release(self->core);
+ self->core = NULL;
+ }
+ kfree(self);
+ }
+ cw1200_spi_off(func->dev.platform_data);
+
+ return 0;
+}
+
+static int cw1200_spi_suspend(struct device *dev, pm_message_t state)
+{
+ struct sbus_priv *self = spi_get_drvdata(to_spi_device(dev));
+
+ if (!cw1200_can_suspend(self->core))
+ return -EAGAIN;
+
+ /* XXX notify host that we have to keep CW1200 powered on? */
+ return 0;
+}
+
+static int cw1200_spi_resume(struct device *dev)
+{
+ return 0;
+}
+
+static struct spi_driver spi_driver = {
+ .probe = cw1200_spi_probe,
+ .remove = cw1200_spi_disconnect,
+ .driver = {
+ .name = "cw1200_wlan_spi",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ .suspend = cw1200_spi_suspend,
+ .resume = cw1200_spi_resume,
+ },
+};
+
+/* Init Module function -> Called by insmod */
+static int __init cw1200_spi_init(void)
+{
+ return spi_register_driver(&spi_driver);
+}
+
+/* Called at Driver Unloading */
+static void __exit cw1200_spi_exit(void)
+{
+ spi_unregister_driver(&spi_driver);
+}
+
+module_init(cw1200_spi_init);
+module_exit(cw1200_spi_exit);
diff --git a/drivers/net/wireless/cw1200/debug.c b/drivers/net/wireless/cw1200/debug.c
new file mode 100644
index 00000000000..b815181802e
--- /dev/null
+++ b/drivers/net/wireless/cw1200/debug.c
@@ -0,0 +1,664 @@
+/*
+ * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers
+ * DebugFS code
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/module.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include "cw1200.h"
+#include "debug.h"
+#include "fwio.h"
+
+/* join_status */
+static const char * const cw1200_debug_join_status[] = {
+ "passive",
+ "monitor",
+ "station (joining)",
+ "station (not authenticated yet)",
+ "station",
+ "adhoc",
+ "access point",
+};
+
+/* WSM_JOIN_PREAMBLE_... */
+static const char * const cw1200_debug_preamble[] = {
+ "long",
+ "short",
+ "long on 1 and 2 Mbps",
+};
+
+
+static const char * const cw1200_debug_link_id[] = {
+ "OFF",
+ "REQ",
+ "SOFT",
+ "HARD",
+};
+
+static const char *cw1200_debug_mode(int mode)
+{
+ switch (mode) {
+ case NL80211_IFTYPE_UNSPECIFIED:
+ return "unspecified";
+ case NL80211_IFTYPE_MONITOR:
+ return "monitor";
+ case NL80211_IFTYPE_STATION:
+ return "station";
+ case NL80211_IFTYPE_ADHOC:
+ return "adhoc";
+ case NL80211_IFTYPE_MESH_POINT:
+ return "mesh point";
+ case NL80211_IFTYPE_AP:
+ return "access point";
+ case NL80211_IFTYPE_P2P_CLIENT:
+ return "p2p client";
+ case NL80211_IFTYPE_P2P_GO:
+ return "p2p go";
+ default:
+ return "unsupported";
+ }
+}
+
+static void cw1200_queue_status_show(struct seq_file *seq,
+ struct cw1200_queue *q)
+{
+ int i;
+ seq_printf(seq, "Queue %d:\n", q->queue_id);
+ seq_printf(seq, " capacity: %zu\n", q->capacity);
+ seq_printf(seq, " queued: %zu\n", q->num_queued);
+ seq_printf(seq, " pending: %zu\n", q->num_pending);
+ seq_printf(seq, " sent: %zu\n", q->num_sent);
+ seq_printf(seq, " locked: %s\n", q->tx_locked_cnt ? "yes" : "no");
+ seq_printf(seq, " overfull: %s\n", q->overfull ? "yes" : "no");
+ seq_puts(seq, " link map: 0-> ");
+ for (i = 0; i < q->stats->map_capacity; ++i)
+ seq_printf(seq, "%.2d ", q->link_map_cache[i]);
+ seq_printf(seq, "<-%zu\n", q->stats->map_capacity);
+}
+
+static void cw1200_debug_print_map(struct seq_file *seq,
+ struct cw1200_common *priv,
+ const char *label,
+ u32 map)
+{
+ int i;
+ seq_printf(seq, "%s0-> ", label);
+ for (i = 0; i < priv->tx_queue_stats.map_capacity; ++i)
+ seq_printf(seq, "%s ", (map & BIT(i)) ? "**" : "..");
+ seq_printf(seq, "<-%zu\n", priv->tx_queue_stats.map_capacity - 1);
+}
+
+static int cw1200_status_show(struct seq_file *seq, void *v)
+{
+ int i;
+ struct list_head *item;
+ struct cw1200_common *priv = seq->private;
+ struct cw1200_debug_priv *d = priv->debug;
+
+ seq_puts(seq, "CW1200 Wireless LAN driver status\n");
+ seq_printf(seq, "Hardware: %d.%d\n",
+ priv->wsm_caps.hw_id,
+ priv->wsm_caps.hw_subid);
+ seq_printf(seq, "Firmware: %s %d.%d\n",
+ cw1200_fw_types[priv->wsm_caps.fw_type],
+ priv->wsm_caps.fw_ver,
+ priv->wsm_caps.fw_build);
+ seq_printf(seq, "FW API: %d\n",
+ priv->wsm_caps.fw_api);
+ seq_printf(seq, "FW caps: 0x%.4X\n",
+ priv->wsm_caps.fw_cap);
+ seq_printf(seq, "FW label: '%s'\n",
+ priv->wsm_caps.fw_label);
+ seq_printf(seq, "Mode: %s%s\n",
+ cw1200_debug_mode(priv->mode),
+ priv->listening ? " (listening)" : "");
+ seq_printf(seq, "Join state: %s\n",
+ cw1200_debug_join_status[priv->join_status]);
+ if (priv->channel)
+ seq_printf(seq, "Channel: %d%s\n",
+ priv->channel->hw_value,
+ priv->channel_switch_in_progress ?
+ " (switching)" : "");
+ if (priv->rx_filter.promiscuous)
+ seq_puts(seq, "Filter: promisc\n");
+ else if (priv->rx_filter.fcs)
+ seq_puts(seq, "Filter: fcs\n");
+ if (priv->rx_filter.bssid)
+ seq_puts(seq, "Filter: bssid\n");
+ if (!priv->disable_beacon_filter)
+ seq_puts(seq, "Filter: beacons\n");
+
+ if (priv->enable_beacon ||
+ priv->mode == NL80211_IFTYPE_AP ||
+ priv->mode == NL80211_IFTYPE_ADHOC ||
+ priv->mode == NL80211_IFTYPE_MESH_POINT ||
+ priv->mode == NL80211_IFTYPE_P2P_GO)
+ seq_printf(seq, "Beaconing: %s\n",
+ priv->enable_beacon ?
+ "enabled" : "disabled");
+
+ for (i = 0; i < 4; ++i)
+ seq_printf(seq, "EDCA(%d): %d, %d, %d, %d, %d\n", i,
+ priv->edca.params[i].cwmin,
+ priv->edca.params[i].cwmax,
+ priv->edca.params[i].aifns,
+ priv->edca.params[i].txop_limit,
+ priv->edca.params[i].max_rx_lifetime);
+
+ if (priv->join_status == CW1200_JOIN_STATUS_STA) {
+ static const char *pm_mode = "unknown";
+ switch (priv->powersave_mode.mode) {
+ case WSM_PSM_ACTIVE:
+ pm_mode = "off";
+ break;
+ case WSM_PSM_PS:
+ pm_mode = "on";
+ break;
+ case WSM_PSM_FAST_PS:
+ pm_mode = "dynamic";
+ break;
+ }
+ seq_printf(seq, "Preamble: %s\n",
+ cw1200_debug_preamble[priv->association_mode.preamble]);
+ seq_printf(seq, "AMPDU spcn: %d\n",
+ priv->association_mode.mpdu_start_spacing);
+ seq_printf(seq, "Basic rate: 0x%.8X\n",
+ le32_to_cpu(priv->association_mode.basic_rate_set));
+ seq_printf(seq, "Bss lost: %d beacons\n",
+ priv->bss_params.beacon_lost_count);
+ seq_printf(seq, "AID: %d\n",
+ priv->bss_params.aid);
+ seq_printf(seq, "Rates: 0x%.8X\n",
+ priv->bss_params.operational_rate_set);
+ seq_printf(seq, "Powersave: %s\n", pm_mode);
+ }
+ seq_printf(seq, "HT: %s\n",
+ cw1200_is_ht(&priv->ht_info) ? "on" : "off");
+ if (cw1200_is_ht(&priv->ht_info)) {
+ seq_printf(seq, "Greenfield: %s\n",
+ cw1200_ht_greenfield(&priv->ht_info) ? "yes" : "no");
+ seq_printf(seq, "AMPDU dens: %d\n",
+ cw1200_ht_ampdu_density(&priv->ht_info));
+ }
+ seq_printf(seq, "RSSI thold: %d\n",
+ priv->cqm_rssi_thold);
+ seq_printf(seq, "RSSI hyst: %d\n",
+ priv->cqm_rssi_hyst);
+ seq_printf(seq, "Long retr: %d\n",
+ priv->long_frame_max_tx_count);
+ seq_printf(seq, "Short retr: %d\n",
+ priv->short_frame_max_tx_count);
+ spin_lock_bh(&priv->tx_policy_cache.lock);
+ i = 0;
+ list_for_each(item, &priv->tx_policy_cache.used)
+ ++i;
+ spin_unlock_bh(&priv->tx_policy_cache.lock);
+ seq_printf(seq, "RC in use: %d\n", i);
+
+ seq_puts(seq, "\n");
+ for (i = 0; i < 4; ++i) {
+ cw1200_queue_status_show(seq, &priv->tx_queue[i]);
+ seq_puts(seq, "\n");
+ }
+
+ cw1200_debug_print_map(seq, priv, "Link map: ",
+ priv->link_id_map);
+ cw1200_debug_print_map(seq, priv, "Asleep map: ",
+ priv->sta_asleep_mask);
+ cw1200_debug_print_map(seq, priv, "PSPOLL map: ",
+ priv->pspoll_mask);
+
+ seq_puts(seq, "\n");
+
+ for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
+ if (priv->link_id_db[i].status) {
+ seq_printf(seq, "Link %d: %s, %pM\n",
+ i + 1,
+ cw1200_debug_link_id[priv->link_id_db[i].status],
+ priv->link_id_db[i].mac);
+ }
+ }
+
+ seq_puts(seq, "\n");
+
+ seq_printf(seq, "BH status: %s\n",
+ atomic_read(&priv->bh_term) ? "terminated" : "alive");
+ seq_printf(seq, "Pending RX: %d\n",
+ atomic_read(&priv->bh_rx));
+ seq_printf(seq, "Pending TX: %d\n",
+ atomic_read(&priv->bh_tx));
+ if (priv->bh_error)
+ seq_printf(seq, "BH errcode: %d\n",
+ priv->bh_error);
+ seq_printf(seq, "TX bufs: %d x %d bytes\n",
+ priv->wsm_caps.input_buffers,
+ priv->wsm_caps.input_buffer_size);
+ seq_printf(seq, "Used bufs: %d\n",
+ priv->hw_bufs_used);
+ seq_printf(seq, "Powermgmt: %s\n",
+ priv->powersave_enabled ? "on" : "off");
+ seq_printf(seq, "Device: %s\n",
+ priv->device_can_sleep ? "asleep" : "awake");
+
+ spin_lock(&priv->wsm_cmd.lock);
+ seq_printf(seq, "WSM status: %s\n",
+ priv->wsm_cmd.done ? "idle" : "active");
+ seq_printf(seq, "WSM cmd: 0x%.4X (%td bytes)\n",
+ priv->wsm_cmd.cmd, priv->wsm_cmd.len);
+ seq_printf(seq, "WSM retval: %d\n",
+ priv->wsm_cmd.ret);
+ spin_unlock(&priv->wsm_cmd.lock);
+
+ seq_printf(seq, "Datapath: %s\n",
+ atomic_read(&priv->tx_lock) ? "locked" : "unlocked");
+ if (atomic_read(&priv->tx_lock))
+ seq_printf(seq, "TXlock cnt: %d\n",
+ atomic_read(&priv->tx_lock));
+
+ seq_printf(seq, "TXed: %d\n",
+ d->tx);
+ seq_printf(seq, "AGG TXed: %d\n",
+ d->tx_agg);
+ seq_printf(seq, "MULTI TXed: %d (%d)\n",
+ d->tx_multi, d->tx_multi_frames);
+ seq_printf(seq, "RXed: %d\n",
+ d->rx);
+ seq_printf(seq, "AGG RXed: %d\n",
+ d->rx_agg);
+ seq_printf(seq, "TX miss: %d\n",
+ d->tx_cache_miss);
+ seq_printf(seq, "TX align: %d\n",
+ d->tx_align);
+ seq_printf(seq, "TX burst: %d\n",
+ d->tx_burst);
+ seq_printf(seq, "TX TTL: %d\n",
+ d->tx_ttl);
+ seq_printf(seq, "Scan: %s\n",
+ atomic_read(&priv->scan.in_progress) ? "active" : "idle");
+
+ return 0;
+}
+
+static int cw1200_status_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, &cw1200_status_show,
+ inode->i_private);
+}
+
+static const struct file_operations fops_status = {
+ .open = cw1200_status_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int cw1200_counters_show(struct seq_file *seq, void *v)
+{
+ int ret;
+ struct cw1200_common *priv = seq->private;
+ struct wsm_mib_counters_table counters;
+
+ ret = wsm_get_counters_table(priv, &counters);
+ if (ret)
+ return ret;
+
+#define PUT_COUNTER(tab, name) \
+ seq_printf(seq, "%s:" tab "%d\n", #name, \
+ __le32_to_cpu(counters.name))
+
+ PUT_COUNTER("\t\t", plcp_errors);
+ PUT_COUNTER("\t\t", fcs_errors);
+ PUT_COUNTER("\t\t", tx_packets);
+ PUT_COUNTER("\t\t", rx_packets);
+ PUT_COUNTER("\t\t", rx_packet_errors);
+ PUT_COUNTER("\t", rx_decryption_failures);
+ PUT_COUNTER("\t\t", rx_mic_failures);
+ PUT_COUNTER("\t", rx_no_key_failures);
+ PUT_COUNTER("\t", tx_multicast_frames);
+ PUT_COUNTER("\t", tx_frames_success);
+ PUT_COUNTER("\t", tx_frame_failures);
+ PUT_COUNTER("\t", tx_frames_retried);
+ PUT_COUNTER("\t", tx_frames_multi_retried);
+ PUT_COUNTER("\t", rx_frame_duplicates);
+ PUT_COUNTER("\t\t", rts_success);
+ PUT_COUNTER("\t\t", rts_failures);
+ PUT_COUNTER("\t\t", ack_failures);
+ PUT_COUNTER("\t", rx_multicast_frames);
+ PUT_COUNTER("\t", rx_frames_success);
+ PUT_COUNTER("\t", rx_cmac_icv_errors);
+ PUT_COUNTER("\t\t", rx_cmac_replays);
+ PUT_COUNTER("\t", rx_mgmt_ccmp_replays);
+
+#undef PUT_COUNTER
+
+ return 0;
+}
+
+static int cw1200_counters_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, &cw1200_counters_show,
+ inode->i_private);
+}
+
+static const struct file_operations fops_counters = {
+ .open = cw1200_counters_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int cw1200_generic_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+#ifdef CONFIG_CW1200_ETF
+static int cw1200_etf_out_show(struct seq_file *seq, void *v)
+{
+ struct cw1200_common *priv = seq->private;
+ struct sk_buff *skb;
+ u32 len = 0;
+
+ skb = skb_dequeue(&priv->etf_q);
+
+ if (skb)
+ len = skb->len;
+
+ seq_write(seq, &len, sizeof(len));
+
+ if (skb) {
+ seq_write(seq, skb->data, len);
+ kfree_skb(skb);
+ }
+
+ return 0;
+}
+
+static int cw1200_etf_out_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, &cw1200_etf_out_show,
+ inode->i_private);
+}
+
+static const struct file_operations fops_etf_out = {
+ .open = cw1200_etf_out_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+struct etf_req_msg;
+static int etf_request(struct cw1200_common *priv,
+ struct etf_req_msg *msg, u32 len);
+
+#define MAX_RX_SZE 2600
+
+struct etf_in_state {
+ struct cw1200_common *priv;
+ u32 total_len;
+ u8 buf[MAX_RX_SZE];
+ u32 written;
+};
+
+static int cw1200_etf_in_open(struct inode *inode, struct file *file)
+{
+ struct etf_in_state *etf = kmalloc(sizeof(struct etf_in_state),
+ GFP_KERNEL);
+
+ if (!etf)
+ return -ENOMEM;
+
+ etf->written = 0;
+ etf->total_len = 0;
+ etf->priv = inode->i_private;
+
+ file->private_data = etf;
+
+ return 0;
+}
+
+static int cw1200_etf_in_release(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+ return 0;
+}
+
+static ssize_t cw1200_etf_in_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct etf_in_state *etf = file->private_data;
+
+ ssize_t written = 0;
+
+ if (!etf->total_len) {
+ if (count < sizeof(etf->total_len)) {
+ pr_err("count < sizeof(total_len)\n");
+ return -EINVAL;
+ }
+
+ if (copy_from_user(&etf->total_len, user_buf,
+ sizeof(etf->total_len))) {
+ pr_err("copy_from_user (len) failed\n");
+ return -EFAULT;
+ }
+
+ written += sizeof(etf->total_len);
+ count -= sizeof(etf->total_len);
+ }
+
+ if (!count)
+ goto done;
+
+ if (copy_from_user(etf->buf + etf->written, user_buf + written,
+ count)) {
+ pr_err("copy_from_user (payload %zu) failed\n", count);
+ return -EFAULT;
+ }
+
+ written += count;
+ etf->written += count;
+
+ if (etf->written >= etf->total_len) {
+ if (etf_request(etf->priv, (struct etf_req_msg *)etf->buf,
+ etf->total_len)) {
+ pr_err("etf_request failed\n");
+ return -EIO;
+ }
+ }
+
+done:
+ return written;
+}
+
+static const struct file_operations fops_etf_in = {
+ .open = cw1200_etf_in_open,
+ .release = cw1200_etf_in_release,
+ .write = cw1200_etf_in_write,
+ .llseek = default_llseek,
+ .owner = THIS_MODULE,
+};
+#endif /* CONFIG_CW1200_ETF */
+
+static ssize_t cw1200_wsm_dumps(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct cw1200_common *priv = file->private_data;
+ char buf[1];
+
+ if (!count)
+ return -EINVAL;
+ if (copy_from_user(buf, user_buf, 1))
+ return -EFAULT;
+
+ if (buf[0] == '1')
+ priv->wsm_enable_wsm_dumps = 1;
+ else
+ priv->wsm_enable_wsm_dumps = 0;
+
+ return count;
+}
+
+static const struct file_operations fops_wsm_dumps = {
+ .open = cw1200_generic_open,
+ .write = cw1200_wsm_dumps,
+ .llseek = default_llseek,
+};
+
+int cw1200_debug_init(struct cw1200_common *priv)
+{
+ int ret = -ENOMEM;
+ struct cw1200_debug_priv *d = kzalloc(sizeof(struct cw1200_debug_priv),
+ GFP_KERNEL);
+ priv->debug = d;
+ if (!d)
+ return ret;
+
+ d->debugfs_phy = debugfs_create_dir("cw1200",
+ priv->hw->wiphy->debugfsdir);
+ if (!d->debugfs_phy)
+ goto err;
+
+ if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy,
+ priv, &fops_status))
+ goto err;
+
+ if (!debugfs_create_file("counters", S_IRUSR, d->debugfs_phy,
+ priv, &fops_counters))
+ goto err;
+
+#ifdef CONFIG_CW1200_ETF
+ if (etf_mode) {
+ skb_queue_head_init(&priv->etf_q);
+
+ if (!debugfs_create_file("etf_out", S_IRUSR, d->debugfs_phy,
+ priv, &fops_etf_out))
+ goto err;
+ if (!debugfs_create_file("etf_in", S_IWUSR, d->debugfs_phy,
+ priv, &fops_etf_in))
+ goto err;
+ }
+#endif /* CONFIG_CW1200_ETF */
+
+ if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy,
+ priv, &fops_wsm_dumps))
+ goto err;
+
+ ret = cw1200_itp_init(priv);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ priv->debug = NULL;
+ debugfs_remove_recursive(d->debugfs_phy);
+ kfree(d);
+ return ret;
+}
+
+void cw1200_debug_release(struct cw1200_common *priv)
+{
+ struct cw1200_debug_priv *d = priv->debug;
+ if (d) {
+ cw1200_itp_release(priv);
+ priv->debug = NULL;
+ kfree(d);
+ }
+}
+
+#ifdef CONFIG_CW1200_ETF
+struct cw1200_sdd {
+ u8 id;
+ u8 len;
+ u8 data[];
+};
+
+struct etf_req_msg {
+ u32 id;
+ u32 len;
+ u8 data[];
+};
+
+static int parse_sdd_file(struct cw1200_common *priv, u8 *data, u32 length)
+{
+ struct cw1200_sdd *ie;
+
+ while (length > 0) {
+ ie = (struct cw1200_sdd *)data;
+ if (ie->id == SDD_REFERENCE_FREQUENCY_ELT_ID) {
+ priv->hw_refclk = cpu_to_le16(*((u16 *)ie->data));
+ pr_info("Using Reference clock frequency %d KHz\n",
+ priv->hw_refclk);
+ break;
+ }
+
+ length -= ie->len + sizeof(*ie);
+ data += ie->len + sizeof(*ie);
+ }
+ return 0;
+}
+
+char *etf_firmware;
+
+#define ST90TDS_START_ADAPTER 0x09 /* Loads firmware too */
+#define ST90TDS_STOP_ADAPTER 0x0A
+#define ST90TDS_CONFIG_ADAPTER 0x0E /* Send configuration params */
+#define ST90TDS_SBUS_READ 0x13
+#define ST90TDS_SBUS_WRITE 0x14
+#define ST90TDS_GET_DEVICE_OPTION 0x19
+#define ST90TDS_SET_DEVICE_OPTION 0x1A
+#define ST90TDS_SEND_SDD 0x1D /* SDD File used to find DPLL */
+
+#include "fwio.h"
+
+static int etf_request(struct cw1200_common *priv,
+ struct etf_req_msg *msg,
+ u32 len)
+{
+ int rval = -1;
+ switch (msg->id) {
+ case ST90TDS_START_ADAPTER:
+ etf_firmware = "cw1200_etf.bin";
+ pr_info("ETF_START (len %d, '%s')\n", len, etf_firmware);
+ rval = cw1200_load_firmware(priv);
+ break;
+ case ST90TDS_STOP_ADAPTER:
+ pr_info("ETF_STOP (unhandled)\n");
+ break;
+ case ST90TDS_SEND_SDD:
+ pr_info("ETF_SDD\n");
+ rval = parse_sdd_file(priv, msg->data, msg->len);
+ break;
+ case ST90TDS_CONFIG_ADAPTER:
+ pr_info("ETF_CONFIG_ADAP (unhandled)\n");
+ break;
+ case ST90TDS_SBUS_READ:
+ pr_info("ETF_SBUS_READ (unhandled)\n");
+ break;
+ case ST90TDS_SBUS_WRITE:
+ pr_info("ETF_SBUS_WRITE (unhandled)\n");
+ break;
+ case ST90TDS_SET_DEVICE_OPTION:
+ pr_info("ETF_SET_DEV_OPT (unhandled)\n");
+ break;
+ default:
+ pr_info("ETF_PASSTHRU (0x%08x)\n", msg->id);
+ rval = wsm_raw_cmd(priv, (u8 *)msg, len);
+ break;
+ }
+
+ return rval;
+}
+#endif /* CONFIG_CW1200_ETF */
diff --git a/drivers/net/wireless/cw1200/debug.h b/drivers/net/wireless/cw1200/debug.h
new file mode 100644
index 00000000000..1fea5b29a81
--- /dev/null
+++ b/drivers/net/wireless/cw1200/debug.h
@@ -0,0 +1,98 @@
+/*
+ * DebugFS code for ST-Ericsson CW1200 mac80211 driver
+ *
+ * Copyright (c) 2011, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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.
+ */
+
+#ifndef CW1200_DEBUG_H_INCLUDED
+#define CW1200_DEBUG_H_INCLUDED
+
+#include "itp.h"
+
+struct cw1200_debug_priv {
+ struct dentry *debugfs_phy;
+ int tx;
+ int tx_agg;
+ int rx;
+ int rx_agg;
+ int tx_multi;
+ int tx_multi_frames;
+ int tx_cache_miss;
+ int tx_align;
+ int tx_ttl;
+ int tx_burst;
+ int ba_cnt;
+ int ba_acc;
+ int ba_cnt_rx;
+ int ba_acc_rx;
+#ifdef CONFIG_CW1200_ITP
+ struct cw1200_itp itp;
+#endif /* CONFIG_CW1200_ITP */
+};
+
+int cw1200_debug_init(struct cw1200_common *priv);
+void cw1200_debug_release(struct cw1200_common *priv);
+
+static inline void cw1200_debug_txed(struct cw1200_common *priv)
+{
+ ++priv->debug->tx;
+}
+
+static inline void cw1200_debug_txed_agg(struct cw1200_common *priv)
+{
+ ++priv->debug->tx_agg;
+}
+
+static inline void cw1200_debug_txed_multi(struct cw1200_common *priv,
+ int count)
+{
+ ++priv->debug->tx_multi;
+ priv->debug->tx_multi_frames += count;
+}
+
+static inline void cw1200_debug_rxed(struct cw1200_common *priv)
+{
+ ++priv->debug->rx;
+}
+
+static inline void cw1200_debug_rxed_agg(struct cw1200_common *priv)
+{
+ ++priv->debug->rx_agg;
+}
+
+static inline void cw1200_debug_tx_cache_miss(struct cw1200_common *priv)
+{
+ ++priv->debug->tx_cache_miss;
+}
+
+static inline void cw1200_debug_tx_align(struct cw1200_common *priv)
+{
+ ++priv->debug->tx_align;
+}
+
+static inline void cw1200_debug_tx_ttl(struct cw1200_common *priv)
+{
+ ++priv->debug->tx_ttl;
+}
+
+static inline void cw1200_debug_tx_burst(struct cw1200_common *priv)
+{
+ ++priv->debug->tx_burst;
+}
+
+static inline void cw1200_debug_ba(struct cw1200_common *priv,
+ int ba_cnt, int ba_acc,
+ int ba_cnt_rx, int ba_acc_rx)
+{
+ priv->debug->ba_cnt = ba_cnt;
+ priv->debug->ba_acc = ba_acc;
+ priv->debug->ba_cnt_rx = ba_cnt_rx;
+ priv->debug->ba_acc_rx = ba_acc_rx;
+}
+
+#endif /* CW1200_DEBUG_H_INCLUDED */
diff --git a/drivers/net/wireless/cw1200/fwio.c b/drivers/net/wireless/cw1200/fwio.c
new file mode 100644
index 00000000000..ad01cd2a59e
--- /dev/null
+++ b/drivers/net/wireless/cw1200/fwio.c
@@ -0,0 +1,525 @@
+/*
+ * Firmware I/O code for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * ST-Ericsson UMAC CW1200 driver which is
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
+ *
+ * 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/init.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+
+#include "cw1200.h"
+#include "fwio.h"
+#include "hwio.h"
+#include "sbus.h"
+#include "bh.h"
+
+static int cw1200_get_hw_type(u32 config_reg_val, int *major_revision)
+{
+ int hw_type = -1;
+ u32 silicon_type = (config_reg_val >> 24) & 0x7;
+ u32 silicon_vers = (config_reg_val >> 31) & 0x1;
+
+ switch (silicon_type) {
+ case 0x00:
+ *major_revision = 1;
+ hw_type = HIF_9000_SILICON_VERSATILE;
+ break;
+ case 0x01:
+ case 0x02: /* CW1x00 */
+ case 0x04: /* CW1x60 */
+ *major_revision = silicon_type;
+ if (silicon_vers)
+ hw_type = HIF_8601_VERSATILE;
+ else
+ hw_type = HIF_8601_SILICON;
+ break;
+ default:
+ break;
+ }
+
+ return hw_type;
+}
+
+static int cw1200_load_firmware_cw1200(struct cw1200_common *priv)
+{
+ int ret, block, num_blocks;
+ unsigned i;
+ u32 val32;
+ u32 put = 0, get = 0;
+ u8 *buf = NULL;
+ const char *fw_path;
+ const struct firmware *firmware = NULL;
+
+ /* Macroses are local. */
+#define APB_WRITE(reg, val) \
+ do { \
+ ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \
+ if (ret < 0) \
+ goto error; \
+ } while (0)
+#define APB_READ(reg, val) \
+ do { \
+ ret = cw1200_apb_read_32(priv, CW1200_APB(reg), &(val)); \
+ if (ret < 0) \
+ goto error; \
+ } while (0)
+#define REG_WRITE(reg, val) \
+ do { \
+ ret = cw1200_reg_write_32(priv, (reg), (val)); \
+ if (ret < 0) \
+ goto error; \
+ } while (0)
+#define REG_READ(reg, val) \
+ do { \
+ ret = cw1200_reg_read_32(priv, (reg), &(val)); \
+ if (ret < 0) \
+ goto error; \
+ } while (0)
+
+ switch (priv->hw_revision) {
+ case CW1200_HW_REV_CUT10:
+ fw_path = FIRMWARE_CUT10;
+ if (!priv->sdd_path)
+ priv->sdd_path = SDD_FILE_10;
+ break;
+ case CW1200_HW_REV_CUT11:
+ fw_path = FIRMWARE_CUT11;
+ if (!priv->sdd_path)
+ priv->sdd_path = SDD_FILE_11;
+ break;
+ case CW1200_HW_REV_CUT20:
+ fw_path = FIRMWARE_CUT20;
+ if (!priv->sdd_path)
+ priv->sdd_path = SDD_FILE_20;
+ break;
+ case CW1200_HW_REV_CUT22:
+ fw_path = FIRMWARE_CUT22;
+ if (!priv->sdd_path)
+ priv->sdd_path = SDD_FILE_22;
+ break;
+ case CW1X60_HW_REV:
+ fw_path = FIRMWARE_CW1X60;
+ if (!priv->sdd_path)
+ priv->sdd_path = SDD_FILE_CW1X60;
+ break;
+ default:
+ pr_err("Invalid silicon revision %d.\n", priv->hw_revision);
+ return -EINVAL;
+ }
+
+ /* Initialize common registers */
+ APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, DOWNLOAD_ARE_YOU_HERE);
+ APB_WRITE(DOWNLOAD_PUT_REG, 0);
+ APB_WRITE(DOWNLOAD_GET_REG, 0);
+ APB_WRITE(DOWNLOAD_STATUS_REG, DOWNLOAD_PENDING);
+ APB_WRITE(DOWNLOAD_FLAGS_REG, 0);
+
+ /* Write the NOP Instruction */
+ REG_WRITE(ST90TDS_SRAM_BASE_ADDR_REG_ID, 0xFFF20000);
+ REG_WRITE(ST90TDS_AHB_DPORT_REG_ID, 0xEAFFFFFE);
+
+ /* Release CPU from RESET */
+ REG_READ(ST90TDS_CONFIG_REG_ID, val32);
+ val32 &= ~ST90TDS_CONFIG_CPU_RESET_BIT;
+ REG_WRITE(ST90TDS_CONFIG_REG_ID, val32);
+
+ /* Enable Clock */
+ val32 &= ~ST90TDS_CONFIG_CPU_CLK_DIS_BIT;
+ REG_WRITE(ST90TDS_CONFIG_REG_ID, val32);
+
+#ifdef CONFIG_CW1200_ETF
+ if (etf_mode)
+ fw_path = etf_firmware;
+#endif
+
+ /* Load a firmware file */
+ ret = request_firmware(&firmware, fw_path, priv->pdev);
+ if (ret) {
+ pr_err("Can't load firmware file %s.\n", fw_path);
+ goto error;
+ }
+
+ buf = kmalloc(DOWNLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA);
+ if (!buf) {
+ pr_err("Can't allocate firmware load buffer.\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ /* Check if the bootloader is ready */
+ for (i = 0; i < 100; i += 1 + i / 2) {
+ APB_READ(DOWNLOAD_IMAGE_SIZE_REG, val32);
+ if (val32 == DOWNLOAD_I_AM_HERE)
+ break;
+ mdelay(i);
+ } /* End of for loop */
+
+ if (val32 != DOWNLOAD_I_AM_HERE) {
+ pr_err("Bootloader is not ready.\n");
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ /* Calculcate number of download blocks */
+ num_blocks = (firmware->size - 1) / DOWNLOAD_BLOCK_SIZE + 1;
+
+ /* Updating the length in Download Ctrl Area */
+ val32 = firmware->size; /* Explicit cast from size_t to u32 */
+ APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, val32);
+
+ /* Firmware downloading loop */
+ for (block = 0; block < num_blocks; block++) {
+ size_t tx_size;
+ size_t block_size;
+
+ /* check the download status */
+ APB_READ(DOWNLOAD_STATUS_REG, val32);
+ if (val32 != DOWNLOAD_PENDING) {
+ pr_err("Bootloader reported error %d.\n", val32);
+ ret = -EIO;
+ goto error;
+ }
+
+ /* loop until put - get <= 24K */
+ for (i = 0; i < 100; i++) {
+ APB_READ(DOWNLOAD_GET_REG, get);
+ if ((put - get) <=
+ (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE))
+ break;
+ mdelay(i);
+ }
+
+ if ((put - get) > (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) {
+ pr_err("Timeout waiting for FIFO.\n");
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ /* calculate the block size */
+ tx_size = block_size = min((size_t)(firmware->size - put),
+ (size_t)DOWNLOAD_BLOCK_SIZE);
+
+ memcpy(buf, &firmware->data[put], block_size);
+ if (block_size < DOWNLOAD_BLOCK_SIZE) {
+ memset(&buf[block_size], 0,
+ DOWNLOAD_BLOCK_SIZE - block_size);
+ tx_size = DOWNLOAD_BLOCK_SIZE;
+ }
+
+ /* send the block to sram */
+ ret = cw1200_apb_write(priv,
+ CW1200_APB(DOWNLOAD_FIFO_OFFSET +
+ (put & (DOWNLOAD_FIFO_SIZE - 1))),
+ buf, tx_size);
+ if (ret < 0) {
+ pr_err("Can't write firmware block @ %d!\n",
+ put & (DOWNLOAD_FIFO_SIZE - 1));
+ goto error;
+ }
+
+ /* update the put register */
+ put += block_size;
+ APB_WRITE(DOWNLOAD_PUT_REG, put);
+ } /* End of firmware download loop */
+
+ /* Wait for the download completion */
+ for (i = 0; i < 300; i += 1 + i / 2) {
+ APB_READ(DOWNLOAD_STATUS_REG, val32);
+ if (val32 != DOWNLOAD_PENDING)
+ break;
+ mdelay(i);
+ }
+ if (val32 != DOWNLOAD_SUCCESS) {
+ pr_err("Wait for download completion failed: 0x%.8X\n", val32);
+ ret = -ETIMEDOUT;
+ goto error;
+ } else {
+ pr_info("Firmware download completed.\n");
+ ret = 0;
+ }
+
+error:
+ kfree(buf);
+ if (firmware)
+ release_firmware(firmware);
+ return ret;
+
+#undef APB_WRITE
+#undef APB_READ
+#undef REG_WRITE
+#undef REG_READ
+}
+
+
+static int config_reg_read(struct cw1200_common *priv, u32 *val)
+{
+ switch (priv->hw_type) {
+ case HIF_9000_SILICON_VERSATILE: {
+ u16 val16;
+ int ret = cw1200_reg_read_16(priv,
+ ST90TDS_CONFIG_REG_ID,
+ &val16);
+ if (ret < 0)
+ return ret;
+ *val = val16;
+ return 0;
+ }
+ case HIF_8601_VERSATILE:
+ case HIF_8601_SILICON:
+ default:
+ cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, val);
+ break;
+ }
+ return 0;
+}
+
+static int config_reg_write(struct cw1200_common *priv, u32 val)
+{
+ switch (priv->hw_type) {
+ case HIF_9000_SILICON_VERSATILE:
+ return cw1200_reg_write_16(priv,
+ ST90TDS_CONFIG_REG_ID,
+ (u16)val);
+ case HIF_8601_VERSATILE:
+ case HIF_8601_SILICON:
+ default:
+ return cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val);
+ break;
+ }
+ return 0;
+}
+
+int cw1200_load_firmware(struct cw1200_common *priv)
+{
+ int ret;
+ int i;
+ u32 val32;
+ u16 val16;
+ int major_revision = -1;
+
+ /* Read CONFIG Register */
+ ret = cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
+ if (ret < 0) {
+ pr_err("Can't read config register.\n");
+ goto out;
+ }
+
+ if (val32 == 0 || val32 == 0xffffffff) {
+ pr_err("Bad config register value (0x%08x)\n", val32);
+ ret = -EIO;
+ goto out;
+ }
+
+ priv->hw_type = cw1200_get_hw_type(val32, &major_revision);
+ if (priv->hw_type < 0) {
+ pr_err("Can't deduce hardware type.\n");
+ ret = -ENOTSUPP;
+ goto out;
+ }
+
+ /* Set DPLL Reg value, and read back to confirm writes work */
+ ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID,
+ cw1200_dpll_from_clk(priv->hw_refclk));
+ if (ret < 0) {
+ pr_err("Can't write DPLL register.\n");
+ goto out;
+ }
+
+ msleep(20);
+
+ ret = cw1200_reg_read_32(priv,
+ ST90TDS_TSET_GEN_R_W_REG_ID, &val32);
+ if (ret < 0) {
+ pr_err("Can't read DPLL register.\n");
+ goto out;
+ }
+
+ if (val32 != cw1200_dpll_from_clk(priv->hw_refclk)) {
+ pr_err("Unable to initialise DPLL register. Wrote 0x%.8X, Read 0x%.8X.\n",
+ cw1200_dpll_from_clk(priv->hw_refclk), val32);
+ ret = -EIO;
+ goto out;
+ }
+
+ /* Set wakeup bit in device */
+ ret = cw1200_reg_read_16(priv, ST90TDS_CONTROL_REG_ID, &val16);
+ if (ret < 0) {
+ pr_err("set_wakeup: can't read control register.\n");
+ goto out;
+ }
+
+ ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID,
+ val16 | ST90TDS_CONT_WUP_BIT);
+ if (ret < 0) {
+ pr_err("set_wakeup: can't write control register.\n");
+ goto out;
+ }
+
+ /* Wait for wakeup */
+ for (i = 0; i < 300; i += (1 + i / 2)) {
+ ret = cw1200_reg_read_16(priv,
+ ST90TDS_CONTROL_REG_ID, &val16);
+ if (ret < 0) {
+ pr_err("wait_for_wakeup: can't read control register.\n");
+ goto out;
+ }
+
+ if (val16 & ST90TDS_CONT_RDY_BIT)
+ break;
+
+ msleep(i);
+ }
+
+ if ((val16 & ST90TDS_CONT_RDY_BIT) == 0) {
+ pr_err("wait_for_wakeup: device is not responding.\n");
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+
+ switch (major_revision) {
+ case 1:
+ /* CW1200 Hardware detection logic : Check for CUT1.1 */
+ ret = cw1200_ahb_read_32(priv, CW1200_CUT_ID_ADDR, &val32);
+ if (ret) {
+ pr_err("HW detection: can't read CUT ID.\n");
+ goto out;
+ }
+
+ switch (val32) {
+ case CW1200_CUT_11_ID_STR:
+ pr_info("CW1x00 Cut 1.1 silicon detected.\n");
+ priv->hw_revision = CW1200_HW_REV_CUT11;
+ break;
+ default:
+ pr_info("CW1x00 Cut 1.0 silicon detected.\n");
+ priv->hw_revision = CW1200_HW_REV_CUT10;
+ break;
+ }
+
+ /* According to ST-E, CUT<2.0 has busted BA TID0-3.
+ Just disable it entirely...
+ */
+ priv->ba_rx_tid_mask = 0;
+ priv->ba_tx_tid_mask = 0;
+ break;
+ case 2: {
+ u32 ar1, ar2, ar3;
+ ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR, &ar1);
+ if (ret) {
+ pr_err("(1) HW detection: can't read CUT ID\n");
+ goto out;
+ }
+ ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 4, &ar2);
+ if (ret) {
+ pr_err("(2) HW detection: can't read CUT ID.\n");
+ goto out;
+ }
+
+ ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 8, &ar3);
+ if (ret) {
+ pr_err("(3) HW detection: can't read CUT ID.\n");
+ goto out;
+ }
+
+ if (ar1 == CW1200_CUT_22_ID_STR1 &&
+ ar2 == CW1200_CUT_22_ID_STR2 &&
+ ar3 == CW1200_CUT_22_ID_STR3) {
+ pr_info("CW1x00 Cut 2.2 silicon detected.\n");
+ priv->hw_revision = CW1200_HW_REV_CUT22;
+ } else {
+ pr_info("CW1x00 Cut 2.0 silicon detected.\n");
+ priv->hw_revision = CW1200_HW_REV_CUT20;
+ }
+ break;
+ }
+ case 4:
+ pr_info("CW1x60 silicon detected.\n");
+ priv->hw_revision = CW1X60_HW_REV;
+ break;
+ default:
+ pr_err("Unsupported silicon major revision %d.\n",
+ major_revision);
+ ret = -ENOTSUPP;
+ goto out;
+ }
+
+ /* Checking for access mode */
+ ret = config_reg_read(priv, &val32);
+ if (ret < 0) {
+ pr_err("Can't read config register.\n");
+ goto out;
+ }
+
+ if (!(val32 & ST90TDS_CONFIG_ACCESS_MODE_BIT)) {
+ pr_err("Device is already in QUEUE mode!\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ switch (priv->hw_type) {
+ case HIF_8601_SILICON:
+ if (priv->hw_revision == CW1X60_HW_REV) {
+ pr_err("Can't handle CW1160/1260 firmware load yet.\n");
+ ret = -ENOTSUPP;
+ goto out;
+ }
+ ret = cw1200_load_firmware_cw1200(priv);
+ break;
+ default:
+ pr_err("Can't perform firmware load for hw type %d.\n",
+ priv->hw_type);
+ ret = -ENOTSUPP;
+ goto out;
+ }
+ if (ret < 0) {
+ pr_err("Firmware load error.\n");
+ goto out;
+ }
+
+ /* Enable interrupt signalling */
+ priv->sbus_ops->lock(priv->sbus_priv);
+ ret = __cw1200_irq_enable(priv, 1);
+ priv->sbus_ops->unlock(priv->sbus_priv);
+ if (ret < 0)
+ goto unsubscribe;
+
+ /* Configure device for MESSSAGE MODE */
+ ret = config_reg_read(priv, &val32);
+ if (ret < 0) {
+ pr_err("Can't read config register.\n");
+ goto unsubscribe;
+ }
+ ret = config_reg_write(priv, val32 & ~ST90TDS_CONFIG_ACCESS_MODE_BIT);
+ if (ret < 0) {
+ pr_err("Can't write config register.\n");
+ goto unsubscribe;
+ }
+
+ /* Unless we read the CONFIG Register we are
+ * not able to get an interrupt
+ */
+ mdelay(10);
+ config_reg_read(priv, &val32);
+
+out:
+ return ret;
+
+unsubscribe:
+ /* Disable interrupt signalling */
+ priv->sbus_ops->lock(priv->sbus_priv);
+ ret = __cw1200_irq_enable(priv, 0);
+ priv->sbus_ops->unlock(priv->sbus_priv);
+ return ret;
+}
diff --git a/drivers/net/wireless/cw1200/fwio.h b/drivers/net/wireless/cw1200/fwio.h
new file mode 100644
index 00000000000..ea3099362cd
--- /dev/null
+++ b/drivers/net/wireless/cw1200/fwio.h
@@ -0,0 +1,39 @@
+/*
+ * Firmware API for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * ST-Ericsson UMAC CW1200 driver which is
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
+ *
+ * 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.
+ */
+
+#ifndef FWIO_H_INCLUDED
+#define FWIO_H_INCLUDED
+
+#define BOOTLOADER_CW1X60 "boot_cw1x60.bin"
+#define FIRMWARE_CW1X60 "wsm_cw1x60.bin"
+#define FIRMWARE_CUT22 "wsm_22.bin"
+#define FIRMWARE_CUT20 "wsm_20.bin"
+#define FIRMWARE_CUT11 "wsm_11.bin"
+#define FIRMWARE_CUT10 "wsm_10.bin"
+#define SDD_FILE_CW1X60 "sdd_cw1x60.bin"
+#define SDD_FILE_22 "sdd_22.bin"
+#define SDD_FILE_20 "sdd_20.bin"
+#define SDD_FILE_11 "sdd_11.bin"
+#define SDD_FILE_10 "sdd_10.bin"
+
+int cw1200_load_firmware(struct cw1200_common *priv);
+
+/* SDD definitions */
+#define SDD_PTA_CFG_ELT_ID 0xEB
+#define SDD_REFERENCE_FREQUENCY_ELT_ID 0xc5
+u32 cw1200_dpll_from_clk(u16 clk);
+
+#endif
diff --git a/drivers/net/wireless/cw1200/hwio.c b/drivers/net/wireless/cw1200/hwio.c
new file mode 100644
index 00000000000..1af7b3d421b
--- /dev/null
+++ b/drivers/net/wireless/cw1200/hwio.c
@@ -0,0 +1,311 @@
+/*
+ * Low-level device IO routines for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * ST-Ericsson UMAC CW1200 driver, which is
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Ajitpal Singh <ajitpal.singh@lockless.no>
+ *
+ * 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/types.h>
+
+#include "cw1200.h"
+#include "hwio.h"
+#include "sbus.h"
+
+ /* Sdio addr is 4*spi_addr */
+#define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2)
+#define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \
+ ((((buf_id) & 0x1F) << 7) \
+ | (((mpf) & 1) << 6) \
+ | (((rfu) & 1) << 5) \
+ | (((reg_id_ofs) & 0x1F) << 0))
+#define MAX_RETRY 3
+
+
+static int __cw1200_reg_read(struct cw1200_common *priv, u16 addr,
+ void *buf, size_t buf_len, int buf_id)
+{
+ u16 addr_sdio;
+ u32 sdio_reg_addr_17bit;
+
+ /* Check if buffer is aligned to 4 byte boundary */
+ if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) {
+ pr_err("buffer is not aligned.\n");
+ return -EINVAL;
+ }
+
+ /* Convert to SDIO Register Address */
+ addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
+ sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
+
+ return priv->sbus_ops->sbus_memcpy_fromio(priv->sbus_priv,
+ sdio_reg_addr_17bit,
+ buf, buf_len);
+}
+
+static int __cw1200_reg_write(struct cw1200_common *priv, u16 addr,
+ const void *buf, size_t buf_len, int buf_id)
+{
+ u16 addr_sdio;
+ u32 sdio_reg_addr_17bit;
+
+ /* Convert to SDIO Register Address */
+ addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
+ sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
+
+ return priv->sbus_ops->sbus_memcpy_toio(priv->sbus_priv,
+ sdio_reg_addr_17bit,
+ buf, buf_len);
+}
+
+static inline int __cw1200_reg_read_32(struct cw1200_common *priv,
+ u16 addr, u32 *val)
+{
+ int i = __cw1200_reg_read(priv, addr, val, sizeof(*val), 0);
+ *val = le32_to_cpu(*val);
+ return i;
+}
+
+static inline int __cw1200_reg_write_32(struct cw1200_common *priv,
+ u16 addr, u32 val)
+{
+ val = cpu_to_le32(val);
+ return __cw1200_reg_write(priv, addr, &val, sizeof(val), 0);
+}
+
+static inline int __cw1200_reg_read_16(struct cw1200_common *priv,
+ u16 addr, u16 *val)
+{
+ int i = __cw1200_reg_read(priv, addr, val, sizeof(*val), 0);
+ *val = le16_to_cpu(*val);
+ return i;
+}
+
+static inline int __cw1200_reg_write_16(struct cw1200_common *priv,
+ u16 addr, u16 val)
+{
+ val = cpu_to_le16(val);
+ return __cw1200_reg_write(priv, addr, &val, sizeof(val), 0);
+}
+
+int cw1200_reg_read(struct cw1200_common *priv, u16 addr, void *buf,
+ size_t buf_len)
+{
+ int ret;
+ priv->sbus_ops->lock(priv->sbus_priv);
+ ret = __cw1200_reg_read(priv, addr, buf, buf_len, 0);
+ priv->sbus_ops->unlock(priv->sbus_priv);
+ return ret;
+}
+
+int cw1200_reg_write(struct cw1200_common *priv, u16 addr, const void *buf,
+ size_t buf_len)
+{
+ int ret;
+ priv->sbus_ops->lock(priv->sbus_priv);
+ ret = __cw1200_reg_write(priv, addr, buf, buf_len, 0);
+ priv->sbus_ops->unlock(priv->sbus_priv);
+ return ret;
+}
+
+int cw1200_data_read(struct cw1200_common *priv, void *buf, size_t buf_len)
+{
+ int ret, retry = 1;
+ int buf_id_rx = priv->buf_id_rx;
+
+ priv->sbus_ops->lock(priv->sbus_priv);
+
+ while (retry <= MAX_RETRY) {
+ ret = __cw1200_reg_read(priv,
+ ST90TDS_IN_OUT_QUEUE_REG_ID, buf,
+ buf_len, buf_id_rx + 1);
+ if (!ret) {
+ buf_id_rx = (buf_id_rx + 1) & 3;
+ priv->buf_id_rx = buf_id_rx;
+ break;
+ } else {
+ retry++;
+ mdelay(1);
+ pr_err("error :[%d]\n", ret);
+ }
+ }
+
+ priv->sbus_ops->unlock(priv->sbus_priv);
+ return ret;
+}
+
+int cw1200_data_write(struct cw1200_common *priv, const void *buf,
+ size_t buf_len)
+{
+ int ret, retry = 1;
+ int buf_id_tx = priv->buf_id_tx;
+
+ priv->sbus_ops->lock(priv->sbus_priv);
+
+ while (retry <= MAX_RETRY) {
+ ret = __cw1200_reg_write(priv,
+ ST90TDS_IN_OUT_QUEUE_REG_ID, buf,
+ buf_len, buf_id_tx);
+ if (!ret) {
+ buf_id_tx = (buf_id_tx + 1) & 31;
+ priv->buf_id_tx = buf_id_tx;
+ break;
+ } else {
+ retry++;
+ mdelay(1);
+ pr_err("error :[%d]\n", ret);
+ }
+ }
+
+ priv->sbus_ops->unlock(priv->sbus_priv);
+ return ret;
+}
+
+int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf,
+ size_t buf_len, u32 prefetch, u16 port_addr)
+{
+ u32 val32 = 0;
+ int i, ret;
+
+ if ((buf_len / 2) >= 0x1000) {
+ pr_err("Can't read more than 0xfff words.\n");
+ return -EINVAL;
+ goto out;
+ }
+
+ priv->sbus_ops->lock(priv->sbus_priv);
+ /* Write address */
+ ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
+ if (ret < 0) {
+ pr_err("Can't write address register.\n");
+ goto out;
+ }
+
+ /* Read CONFIG Register Value - We will read 32 bits */
+ ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
+ if (ret < 0) {
+ pr_err("Can't read config register.\n");
+ goto out;
+ }
+
+ /* Set PREFETCH bit */
+ ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID,
+ val32 | prefetch);
+ if (ret < 0) {
+ pr_err("Can't write prefetch bit.\n");
+ goto out;
+ }
+
+ /* Check for PRE-FETCH bit to be cleared */
+ for (i = 0; i < 20; i++) {
+ ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
+ if (ret < 0) {
+ pr_err("Can't check prefetch bit.\n");
+ goto out;
+ }
+ if (!(val32 & prefetch))
+ break;
+
+ mdelay(i);
+ }
+
+ if (val32 & prefetch) {
+ pr_err("Prefetch bit is not cleared.\n");
+ goto out;
+ }
+
+ /* Read data port */
+ ret = __cw1200_reg_read(priv, port_addr, buf, buf_len, 0);
+ if (ret < 0) {
+ pr_err("Can't read data port.\n");
+ goto out;
+ }
+
+out:
+ priv->sbus_ops->unlock(priv->sbus_priv);
+ return ret;
+}
+
+int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf,
+ size_t buf_len)
+{
+ int ret;
+
+ if ((buf_len / 2) >= 0x1000) {
+ pr_err("Can't write more than 0xfff words.\n");
+ return -EINVAL;
+ }
+
+ priv->sbus_ops->lock(priv->sbus_priv);
+
+ /* Write address */
+ ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
+ if (ret < 0) {
+ pr_err("Can't write address register.\n");
+ goto out;
+ }
+
+ /* Write data port */
+ ret = __cw1200_reg_write(priv, ST90TDS_SRAM_DPORT_REG_ID,
+ buf, buf_len, 0);
+ if (ret < 0) {
+ pr_err("Can't write data port.\n");
+ goto out;
+ }
+
+out:
+ priv->sbus_ops->unlock(priv->sbus_priv);
+ return ret;
+}
+
+int __cw1200_irq_enable(struct cw1200_common *priv, int enable)
+{
+ u32 val32;
+ u16 val16;
+ int ret;
+
+ if (HIF_8601_SILICON == priv->hw_type) {
+ ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
+ if (ret < 0) {
+ pr_err("Can't read config register.\n");
+ return ret;
+ }
+
+ if (enable)
+ val32 |= ST90TDS_CONF_IRQ_RDY_ENABLE;
+ else
+ val32 &= ~ST90TDS_CONF_IRQ_RDY_ENABLE;
+
+ ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val32);
+ if (ret < 0) {
+ pr_err("Can't write config register.\n");
+ return ret;
+ }
+ } else {
+ ret = __cw1200_reg_read_16(priv, ST90TDS_CONFIG_REG_ID, &val16);
+ if (ret < 0) {
+ pr_err("Can't read control register.\n");
+ return ret;
+ }
+
+ if (enable)
+ val16 |= ST90TDS_CONT_IRQ_RDY_ENABLE;
+ else
+ val16 &= ~ST90TDS_CONT_IRQ_RDY_ENABLE;
+
+ ret = __cw1200_reg_write_16(priv, ST90TDS_CONFIG_REG_ID, val16);
+ if (ret < 0) {
+ pr_err("Can't write control register.\n");
+ return ret;
+ }
+ }
+ return 0;
+}
diff --git a/drivers/net/wireless/cw1200/hwio.h b/drivers/net/wireless/cw1200/hwio.h
new file mode 100644
index 00000000000..7ee73fe32cc
--- /dev/null
+++ b/drivers/net/wireless/cw1200/hwio.h
@@ -0,0 +1,247 @@
+/*
+ * Low-level API for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * ST-Ericsson UMAC CW1200 driver which is
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
+ *
+ * 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.
+ */
+
+#ifndef CW1200_HWIO_H_INCLUDED
+#define CW1200_HWIO_H_INCLUDED
+
+/* extern */ struct cw1200_common;
+
+#define CW1200_CUT_11_ID_STR (0x302E3830)
+#define CW1200_CUT_22_ID_STR1 (0x302e3132)
+#define CW1200_CUT_22_ID_STR2 (0x32302e30)
+#define CW1200_CUT_22_ID_STR3 (0x3335)
+#define CW1200_CUT_ID_ADDR (0xFFF17F90)
+#define CW1200_CUT2_ID_ADDR (0xFFF1FF90)
+
+/* Download control area */
+/* boot loader start address in SRAM */
+#define DOWNLOAD_BOOT_LOADER_OFFSET (0x00000000)
+/* 32K, 0x4000 to 0xDFFF */
+#define DOWNLOAD_FIFO_OFFSET (0x00004000)
+/* 32K */
+#define DOWNLOAD_FIFO_SIZE (0x00008000)
+/* 128 bytes, 0xFF80 to 0xFFFF */
+#define DOWNLOAD_CTRL_OFFSET (0x0000FF80)
+#define DOWNLOAD_CTRL_DATA_DWORDS (32-6)
+
+struct download_cntl_t {
+ /* size of whole firmware file (including Cheksum), host init */
+ u32 image_size;
+ /* downloading flags */
+ u32 flags;
+ /* No. of bytes put into the download, init & updated by host */
+ u32 put;
+ /* last traced program counter, last ARM reg_pc */
+ u32 trace_pc;
+ /* No. of bytes read from the download, host init, device updates */
+ u32 get;
+ /* r0, boot losader status, host init to pending, device updates */
+ u32 status;
+ /* Extra debug info, r1 to r14 if status=r0=DOWNLOAD_EXCEPTION */
+ u32 debug_data[DOWNLOAD_CTRL_DATA_DWORDS];
+};
+
+#define DOWNLOAD_IMAGE_SIZE_REG \
+ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, image_size))
+#define DOWNLOAD_FLAGS_REG \
+ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, flags))
+#define DOWNLOAD_PUT_REG \
+ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, put))
+#define DOWNLOAD_TRACE_PC_REG \
+ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, trace_pc))
+#define DOWNLOAD_GET_REG \
+ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, get))
+#define DOWNLOAD_STATUS_REG \
+ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, status))
+#define DOWNLOAD_DEBUG_DATA_REG \
+ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, debug_data))
+#define DOWNLOAD_DEBUG_DATA_LEN (108)
+
+#define DOWNLOAD_BLOCK_SIZE (1024)
+
+/* For boot loader detection */
+#define DOWNLOAD_ARE_YOU_HERE (0x87654321)
+#define DOWNLOAD_I_AM_HERE (0x12345678)
+
+/* Download error code */
+#define DOWNLOAD_PENDING (0xFFFFFFFF)
+#define DOWNLOAD_SUCCESS (0)
+#define DOWNLOAD_EXCEPTION (1)
+#define DOWNLOAD_ERR_MEM_1 (2)
+#define DOWNLOAD_ERR_MEM_2 (3)
+#define DOWNLOAD_ERR_SOFTWARE (4)
+#define DOWNLOAD_ERR_FILE_SIZE (5)
+#define DOWNLOAD_ERR_CHECKSUM (6)
+#define DOWNLOAD_ERR_OVERFLOW (7)
+#define DOWNLOAD_ERR_IMAGE (8)
+#define DOWNLOAD_ERR_HOST (9)
+#define DOWNLOAD_ERR_ABORT (10)
+
+
+#define SYS_BASE_ADDR_SILICON (0)
+#define PAC_BASE_ADDRESS_SILICON (SYS_BASE_ADDR_SILICON + 0x09000000)
+#define PAC_SHARED_MEMORY_SILICON (PAC_BASE_ADDRESS_SILICON)
+
+#define CW1200_APB(addr) (PAC_SHARED_MEMORY_SILICON + (addr))
+
+/* ***************************************************************
+*Device register definitions
+*************************************************************** */
+/* WBF - SPI Register Addresses */
+#define ST90TDS_ADDR_ID_BASE (0x0000)
+/* 16/32 bits */
+#define ST90TDS_CONFIG_REG_ID (0x0000)
+/* 16/32 bits */
+#define ST90TDS_CONTROL_REG_ID (0x0001)
+/* 16 bits, Q mode W/R */
+#define ST90TDS_IN_OUT_QUEUE_REG_ID (0x0002)
+/* 32 bits, AHB bus R/W */
+#define ST90TDS_AHB_DPORT_REG_ID (0x0003)
+/* 16/32 bits */
+#define ST90TDS_SRAM_BASE_ADDR_REG_ID (0x0004)
+/* 32 bits, APB bus R/W */
+#define ST90TDS_SRAM_DPORT_REG_ID (0x0005)
+/* 32 bits, t_settle/general */
+#define ST90TDS_TSET_GEN_R_W_REG_ID (0x0006)
+/* 16 bits, Q mode read, no length */
+#define ST90TDS_FRAME_OUT_REG_ID (0x0007)
+#define ST90TDS_ADDR_ID_MAX (ST90TDS_FRAME_OUT_REG_ID)
+
+/* WBF - Control register bit set */
+/* next o/p length, bit 11 to 0 */
+#define ST90TDS_CONT_NEXT_LEN_MASK (0x0FFF)
+#define ST90TDS_CONT_WUP_BIT (BIT(12))
+#define ST90TDS_CONT_RDY_BIT (BIT(13))
+#define ST90TDS_CONT_IRQ_ENABLE (BIT(14))
+#define ST90TDS_CONT_RDY_ENABLE (BIT(15))
+#define ST90TDS_CONT_IRQ_RDY_ENABLE (BIT(14)|BIT(15))
+
+/* SPI Config register bit set */
+#define ST90TDS_CONFIG_FRAME_BIT (BIT(2))
+#define ST90TDS_CONFIG_WORD_MODE_BITS (BIT(3)|BIT(4))
+#define ST90TDS_CONFIG_WORD_MODE_1 (BIT(3))
+#define ST90TDS_CONFIG_WORD_MODE_2 (BIT(4))
+#define ST90TDS_CONFIG_ERROR_0_BIT (BIT(5))
+#define ST90TDS_CONFIG_ERROR_1_BIT (BIT(6))
+#define ST90TDS_CONFIG_ERROR_2_BIT (BIT(7))
+/* TBD: Sure??? */
+#define ST90TDS_CONFIG_CSN_FRAME_BIT (BIT(7))
+#define ST90TDS_CONFIG_ERROR_3_BIT (BIT(8))
+#define ST90TDS_CONFIG_ERROR_4_BIT (BIT(9))
+/* QueueM */
+#define ST90TDS_CONFIG_ACCESS_MODE_BIT (BIT(10))
+/* AHB bus */
+#define ST90TDS_CONFIG_AHB_PRFETCH_BIT (BIT(11))
+#define ST90TDS_CONFIG_CPU_CLK_DIS_BIT (BIT(12))
+/* APB bus */
+#define ST90TDS_CONFIG_PRFETCH_BIT (BIT(13))
+/* cpu reset */
+#define ST90TDS_CONFIG_CPU_RESET_BIT (BIT(14))
+#define ST90TDS_CONFIG_CLEAR_INT_BIT (BIT(15))
+
+/* For CW1200 the IRQ Enable and Ready Bits are in CONFIG register */
+#define ST90TDS_CONF_IRQ_ENABLE (BIT(16))
+#define ST90TDS_CONF_RDY_ENABLE (BIT(17))
+#define ST90TDS_CONF_IRQ_RDY_ENABLE (BIT(16)|BIT(17))
+
+int cw1200_data_read(struct cw1200_common *priv,
+ void *buf, size_t buf_len);
+int cw1200_data_write(struct cw1200_common *priv,
+ const void *buf, size_t buf_len);
+
+int cw1200_reg_read(struct cw1200_common *priv, u16 addr,
+ void *buf, size_t buf_len);
+int cw1200_reg_write(struct cw1200_common *priv, u16 addr,
+ const void *buf, size_t buf_len);
+
+static inline int cw1200_reg_read_16(struct cw1200_common *priv,
+ u16 addr, u16 *val)
+{
+ u32 tmp;
+ int i;
+ i = cw1200_reg_read(priv, addr, &tmp, sizeof(tmp));
+ tmp = le32_to_cpu(tmp);
+ *val = tmp & 0xffff;
+ return i;
+}
+
+static inline int cw1200_reg_write_16(struct cw1200_common *priv,
+ u16 addr, u16 val)
+{
+ u32 tmp = val;
+ tmp = cpu_to_le32(tmp);
+ return cw1200_reg_write(priv, addr, &tmp, sizeof(tmp));
+}
+
+static inline int cw1200_reg_read_32(struct cw1200_common *priv,
+ u16 addr, u32 *val)
+{
+ int i = cw1200_reg_read(priv, addr, val, sizeof(*val));
+ *val = le32_to_cpu(*val);
+ return i;
+}
+
+static inline int cw1200_reg_write_32(struct cw1200_common *priv,
+ u16 addr, u32 val)
+{
+ val = cpu_to_le32(val);
+ return cw1200_reg_write(priv, addr, &val, sizeof(val));
+}
+
+int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf,
+ size_t buf_len, u32 prefetch, u16 port_addr);
+int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf,
+ size_t buf_len);
+
+static inline int cw1200_apb_read(struct cw1200_common *priv, u32 addr,
+ void *buf, size_t buf_len)
+{
+ return cw1200_indirect_read(priv, addr, buf, buf_len,
+ ST90TDS_CONFIG_PRFETCH_BIT,
+ ST90TDS_SRAM_DPORT_REG_ID);
+}
+
+static inline int cw1200_ahb_read(struct cw1200_common *priv, u32 addr,
+ void *buf, size_t buf_len)
+{
+ return cw1200_indirect_read(priv, addr, buf, buf_len,
+ ST90TDS_CONFIG_AHB_PRFETCH_BIT,
+ ST90TDS_AHB_DPORT_REG_ID);
+}
+
+static inline int cw1200_apb_read_32(struct cw1200_common *priv,
+ u32 addr, u32 *val)
+{
+ int i = cw1200_apb_read(priv, addr, val, sizeof(*val));
+ *val = le32_to_cpu(*val);
+ return i;
+}
+
+static inline int cw1200_apb_write_32(struct cw1200_common *priv,
+ u32 addr, u32 val)
+{
+ val = cpu_to_le32(val);
+ return cw1200_apb_write(priv, addr, &val, sizeof(val));
+}
+static inline int cw1200_ahb_read_32(struct cw1200_common *priv,
+ u32 addr, u32 *val)
+{
+ int i = cw1200_ahb_read(priv, addr, val, sizeof(*val));
+ *val = le32_to_cpu(*val);
+ return i;
+}
+
+#endif /* CW1200_HWIO_H_INCLUDED */
diff --git a/drivers/net/wireless/cw1200/itp.c b/drivers/net/wireless/cw1200/itp.c
new file mode 100644
index 00000000000..c0730bb49b7
--- /dev/null
+++ b/drivers/net/wireless/cw1200/itp.c
@@ -0,0 +1,730 @@
+/*
+ * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers
+ * ITP code
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/module.h>
+#include <linux/debugfs.h>
+#include <linux/poll.h>
+#include <linux/time.h>
+#include <linux/random.h>
+#include <linux/kallsyms.h>
+#include <net/mac80211.h>
+#include "cw1200.h"
+#include "debug.h"
+#include "itp.h"
+#include "sta.h"
+
+static int __cw1200_itp_open(struct cw1200_common *priv);
+static int __cw1200_itp_close(struct cw1200_common *priv);
+static void cw1200_itp_rx_start(struct cw1200_common *priv);
+static void cw1200_itp_rx_stop(struct cw1200_common *priv);
+static void cw1200_itp_rx_stats(struct cw1200_common *priv);
+static void cw1200_itp_rx_reset(struct cw1200_common *priv);
+static void cw1200_itp_tx_stop(struct cw1200_common *priv);
+static void cw1200_itp_handle(struct cw1200_common *priv,
+ struct sk_buff *skb);
+static void cw1200_itp_err(struct cw1200_common *priv,
+ int err,
+ int arg);
+static void __cw1200_itp_tx_stop(struct cw1200_common *priv);
+
+static ssize_t cw1200_itp_read(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct cw1200_common *priv = file->private_data;
+ struct cw1200_itp *itp = &priv->debug->itp;
+ struct sk_buff *skb;
+ int ret;
+
+ if (skb_queue_empty(&itp->log_queue))
+ return 0;
+
+ skb = skb_dequeue(&itp->log_queue);
+ ret = copy_to_user(user_buf, skb->data, skb->len);
+ *ppos += skb->len;
+ skb->data[skb->len] = 0;
+ pr_debug("[ITP] >>> %s", skb->data);
+ consume_skb(skb);
+
+ return skb->len - ret;
+}
+
+static ssize_t cw1200_itp_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct cw1200_common *priv = file->private_data;
+ struct sk_buff *skb;
+
+ if (!count || count > 1024)
+ return -EINVAL;
+ skb = dev_alloc_skb(count + 1);
+ if (!skb)
+ return -ENOMEM;
+ skb_trim(skb, 0);
+ skb_put(skb, count + 1);
+ if (copy_from_user(skb->data, user_buf, count)) {
+ kfree_skb(skb);
+ return -EFAULT;
+ }
+ skb->data[count] = 0;
+
+ cw1200_itp_handle(priv, skb);
+ consume_skb(skb);
+ return count;
+}
+
+static unsigned int cw1200_itp_poll(struct file *file, poll_table *wait)
+{
+ struct cw1200_common *priv = file->private_data;
+ struct cw1200_itp *itp = &priv->debug->itp;
+ unsigned int mask = 0;
+
+ poll_wait(file, &itp->read_wait, wait);
+
+ if (!skb_queue_empty(&itp->log_queue))
+ mask |= POLLIN | POLLRDNORM;
+
+ mask |= POLLOUT | POLLWRNORM;
+
+ return mask;
+}
+
+static int cw1200_itp_open(struct inode *inode, struct file *file)
+{
+ struct cw1200_common *priv = inode->i_private;
+ struct cw1200_itp *itp = &priv->debug->itp;
+ int ret = 0;
+
+ file->private_data = priv;
+ if (atomic_inc_return(&itp->open_count) == 1) {
+ ret = __cw1200_itp_open(priv);
+ if (ret && !atomic_dec_return(&itp->open_count))
+ __cw1200_itp_close(priv);
+ } else {
+ atomic_dec(&itp->open_count);
+ ret = -EBUSY;
+ }
+
+ return ret;
+}
+
+static int cw1200_itp_close(struct inode *inode, struct file *file)
+{
+ struct cw1200_common *priv = file->private_data;
+ struct cw1200_itp *itp = &priv->debug->itp;
+ if (!atomic_dec_return(&itp->open_count)) {
+ __cw1200_itp_close(priv);
+ wake_up(&itp->close_wait);
+ }
+ return 0;
+}
+
+static const struct file_operations fops_itp = {
+ .open = cw1200_itp_open,
+ .read = cw1200_itp_read,
+ .write = cw1200_itp_write,
+ .poll = cw1200_itp_poll,
+ .release = cw1200_itp_close,
+ .llseek = default_llseek,
+ .owner = THIS_MODULE,
+};
+
+static void cw1200_itp_fill_pattern(u8 *data, int size,
+ enum cw1200_itp_data_modes mode)
+{
+ if (size <= 0)
+ return;
+
+ switch (mode) {
+ default:
+ case ITP_DATA_ZEROS:
+ memset(data, 0x0, size);
+ break;
+ case ITP_DATA_ONES:
+ memset(data, 0xff, size);
+ break;
+ case ITP_DATA_ZERONES:
+ memset(data, 0x55, size);
+ break;
+ case ITP_DATA_RANDOM:
+ get_random_bytes(data, size);
+ break;
+ }
+ return;
+}
+
+static void cw1200_itp_tx_work(struct work_struct *work)
+{
+ struct cw1200_itp *itp = container_of(work, struct cw1200_itp,
+ tx_work.work);
+ struct cw1200_common *priv = itp->priv;
+ atomic_set(&priv->bh_tx, 1);
+ wake_up(&priv->bh_wq);
+}
+
+static void cw1200_itp_tx_finish(struct work_struct *work)
+{
+ struct cw1200_itp *itp = container_of(work, struct cw1200_itp,
+ tx_finish.work);
+ __cw1200_itp_tx_stop(itp->priv);
+}
+
+int cw1200_itp_init(struct cw1200_common *priv)
+{
+ struct cw1200_itp *itp = &priv->debug->itp;
+
+ itp->priv = priv;
+ atomic_set(&itp->open_count, 0);
+ atomic_set(&itp->stop_tx, 0);
+ atomic_set(&itp->awaiting_confirm, 0);
+ skb_queue_head_init(&itp->log_queue);
+ spin_lock_init(&itp->tx_lock);
+ init_waitqueue_head(&itp->read_wait);
+ init_waitqueue_head(&itp->write_wait);
+ init_waitqueue_head(&itp->close_wait);
+ INIT_DELAYED_WORK(&itp->tx_work, cw1200_itp_tx_work);
+ INIT_DELAYED_WORK(&itp->tx_finish, cw1200_itp_tx_finish);
+ itp->data = NULL;
+ itp->hdr_len = WSM_TX_EXTRA_HEADROOM +
+ sizeof(struct ieee80211_hdr_3addr);
+
+ if (!debugfs_create_file("itp", S_IRUSR | S_IWUSR,
+ priv->debug->debugfs_phy, priv, &fops_itp))
+ return -ENOMEM;
+
+ return 0;
+}
+
+void cw1200_itp_release(struct cw1200_common *priv)
+{
+ struct cw1200_itp *itp = &priv->debug->itp;
+
+ wait_event_interruptible(itp->close_wait,
+ !atomic_read(&itp->open_count));
+
+ WARN_ON(atomic_read(&itp->open_count));
+
+ skb_queue_purge(&itp->log_queue);
+ cw1200_itp_tx_stop(priv);
+}
+
+static int __cw1200_itp_open(struct cw1200_common *priv)
+{
+ struct cw1200_itp *itp = &priv->debug->itp;
+
+ if (!priv->vif)
+ return -EINVAL;
+ if (priv->join_status)
+ return -EINVAL;
+ itp->saved_channel = priv->channel;
+ if (!priv->channel)
+ priv->channel = &priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0];
+ wsm_set_bssid_filtering(priv, false);
+ cw1200_itp_rx_reset(priv);
+ return 0;
+}
+
+static int __cw1200_itp_close(struct cw1200_common *priv)
+{
+ struct cw1200_itp *itp = &priv->debug->itp;
+ if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST)
+ cw1200_itp_rx_stop(priv);
+ cw1200_itp_tx_stop(priv);
+ cw1200_disable_listening(priv);
+ cw1200_update_filtering(priv);
+ priv->channel = itp->saved_channel;
+ return 0;
+}
+
+bool cw1200_is_itp(struct cw1200_common *priv)
+{
+ struct cw1200_itp *itp = &priv->debug->itp;
+ return atomic_read(&itp->open_count) != 0;
+}
+
+static void cw1200_itp_rx_reset(struct cw1200_common *priv)
+{
+ struct cw1200_itp *itp = &priv->debug->itp;
+ itp->rx_cnt = 0;
+ itp->rx_rssi = 0;
+ itp->rx_rssi_max = -1000;
+ itp->rx_rssi_min = 1000;
+}
+
+static void cw1200_itp_rx_start(struct cw1200_common *priv)
+{
+ struct cw1200_itp *itp = &priv->debug->itp;
+
+ pr_debug("[ITP] RX start, band = %d, ch = %d\n",
+ itp->band, itp->ch);
+ atomic_set(&itp->test_mode, TEST_MODE_RX_TEST);
+ cw1200_update_listening(priv, false);
+ priv->channel = &priv->hw->
+ wiphy->bands[itp->band]->channels[itp->ch];
+ cw1200_update_listening(priv, true);
+ wsm_set_bssid_filtering(priv, false);
+}
+
+static void cw1200_itp_rx_stop(struct cw1200_common *priv)
+{
+ struct cw1200_itp *itp = &priv->debug->itp;
+ pr_debug("[ITP] RX stop\n");
+ atomic_set(&itp->test_mode, TEST_MODE_NO_TEST);
+ cw1200_itp_rx_reset(priv);
+}
+
+static void cw1200_itp_rx_stats(struct cw1200_common *priv)
+{
+ struct cw1200_itp *itp = &priv->debug->itp;
+ struct sk_buff *skb;
+ char buf[128];
+ int len, ret;
+ struct wsm_mib_counters_table counters;
+
+ ret = wsm_get_counters_table(priv, &counters);
+
+ if (ret)
+ cw1200_itp_err(priv, -EBUSY, 20);
+
+ if (!itp->rx_cnt)
+ len = snprintf(buf, sizeof(buf), "1,0,0,0,0,%d\n",
+ counters.rx_packet_errors);
+ else
+ len = snprintf(buf, sizeof(buf), "1,%d,%ld,%d,%d,%d\n",
+ itp->rx_cnt,
+ itp->rx_cnt ? itp->rx_rssi / itp->rx_cnt : 0,
+ itp->rx_rssi_min, itp->rx_rssi_max,
+ counters.rx_packet_errors);
+
+ if (len <= 0) {
+ cw1200_itp_err(priv, -EBUSY, 21);
+ return;
+ }
+
+ skb = dev_alloc_skb(len);
+ if (!skb) {
+ cw1200_itp_err(priv, -ENOMEM, 22);
+ return;
+ }
+
+ itp->rx_cnt = 0;
+ itp->rx_rssi = 0;
+ itp->rx_rssi_max = -1000;
+ itp->rx_rssi_min = 1000;
+
+ skb_trim(skb, 0);
+ skb_put(skb, len);
+
+ memcpy(skb->data, buf, len);
+ skb_queue_tail(&itp->log_queue, skb);
+ wake_up(&itp->read_wait);
+}
+
+static void cw1200_itp_tx_start(struct cw1200_common *priv)
+{
+ struct wsm_tx *tx;
+ struct ieee80211_hdr_3addr *hdr;
+ struct cw1200_itp *itp = &priv->debug->itp;
+ struct wsm_mib_association_mode assoc_mode = {
+ .flags = WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE,
+ .preamble = itp->preamble,
+ };
+ int len;
+ u8 da_addr[6] = ITP_DEFAULT_DA_ADDR;
+
+ /* Rates index 4 and 5 are not supported */
+ if (itp->rate > 3)
+ itp->rate += 2;
+
+ pr_debug("[ITP] TX start: band = %d, ch = %d, rate = %d, preamble = %d, number = %d, data_mode = %d, interval = %d, power = %d, data_len = %d\n",
+ itp->band, itp->ch, itp->rate, itp->preamble,
+ itp->number, itp->data_mode, itp->interval_us,
+ itp->power, itp->data_len);
+
+ len = itp->hdr_len + itp->data_len;
+
+ itp->data = kmalloc(len, GFP_KERNEL);
+ tx = (struct wsm_tx *)itp->data;
+ tx->hdr.len = itp->data_len + itp->hdr_len;
+ tx->hdr.id = __cpu_to_le16(0x0004 | 1 << 6);
+ tx->max_tx_rate = itp->rate;
+ tx->queue_id = 3;
+ tx->more = 0;
+ tx->flags = 0xc;
+ tx->packet_id = 0x55ff55;
+ tx->reserved = 0;
+ tx->expire_time = 1;
+
+ if (itp->preamble == ITP_PREAMBLE_GREENFIELD)
+ tx->ht_tx_parameters = WSM_HT_TX_GREENFIELD;
+ else if (itp->preamble == ITP_PREAMBLE_MIXED)
+ tx->ht_tx_parameters = WSM_HT_TX_MIXED;
+
+ hdr = (struct ieee80211_hdr_3addr *)&itp->data[sizeof(struct wsm_tx)];
+ memset(hdr, 0, sizeof(*hdr));
+ hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_FCTL_TODS);
+ memcpy(hdr->addr1, da_addr, ETH_ALEN);
+ memcpy(hdr->addr2, priv->vif->addr, ETH_ALEN);
+ memcpy(hdr->addr3, da_addr, ETH_ALEN);
+
+ cw1200_itp_fill_pattern(&itp->data[itp->hdr_len],
+ itp->data_len, itp->data_mode);
+
+ cw1200_update_listening(priv, false);
+ priv->channel = &priv->hw->wiphy->bands[itp->band]->channels[itp->ch];
+ WARN_ON(wsm_set_output_power(priv, itp->power));
+ if (itp->preamble == ITP_PREAMBLE_SHORT ||
+ itp->preamble == ITP_PREAMBLE_LONG)
+ WARN_ON(wsm_set_association_mode(priv,
+ &assoc_mode));
+ wsm_set_bssid_filtering(priv, false);
+ cw1200_update_listening(priv, true);
+
+ spin_lock_bh(&itp->tx_lock);
+ atomic_set(&itp->test_mode, TEST_MODE_TX_TEST);
+ atomic_set(&itp->awaiting_confirm, 0);
+ atomic_set(&itp->stop_tx, 0);
+ atomic_set(&priv->bh_tx, 1);
+ ktime_get_ts(&itp->last_sent);
+ wake_up(&priv->bh_wq);
+ spin_unlock_bh(&itp->tx_lock);
+}
+
+void __cw1200_itp_tx_stop(struct cw1200_common *priv)
+{
+ struct cw1200_itp *itp = &priv->debug->itp;
+ spin_lock_bh(&itp->tx_lock);
+ kfree(itp->data);
+ itp->data = NULL;
+ atomic_set(&itp->test_mode, TEST_MODE_NO_TEST);
+ spin_unlock_bh(&itp->tx_lock);
+}
+
+static void cw1200_itp_tx_stop(struct cw1200_common *priv)
+{
+ struct cw1200_itp *itp = &priv->debug->itp;
+ pr_debug("[ITP] TX stop\n");
+ atomic_set(&itp->stop_tx, 1);
+ flush_workqueue(priv->workqueue);
+
+ /* time for FW to confirm all tx requests */
+ msleep(500);
+
+ __cw1200_itp_tx_stop(priv);
+}
+
+static int cw1200_print_fw_version(struct cw1200_common *priv,
+ u8 *buf, size_t len)
+{
+ return snprintf(buf, len, "%s %d.%d",
+ cw1200_fw_types[priv->wsm_caps.fw_type],
+ priv->wsm_caps.fw_ver,
+ priv->wsm_caps.fw_build);
+}
+
+static void cw1200_itp_get_version(struct cw1200_common *priv,
+ enum cw1200_itp_version_type type)
+{
+ struct cw1200_itp *itp = &priv->debug->itp;
+ struct sk_buff *skb;
+ char buf[ITP_BUF_SIZE];
+ size_t size = 0;
+ int len;
+ pr_debug("[ITP] print %s version\n",
+ type == ITP_CHIP_ID ? "chip" : "firmware");
+
+ len = snprintf(buf, ITP_BUF_SIZE, "2,");
+ if (len <= 0) {
+ cw1200_itp_err(priv, -EINVAL, 40);
+ return;
+ }
+ size += len;
+
+ switch (type) {
+ case ITP_CHIP_ID:
+ len = cw1200_print_fw_version(priv, buf+size,
+ ITP_BUF_SIZE - size);
+
+ if (len <= 0) {
+ cw1200_itp_err(priv, -EINVAL, 41);
+ return;
+ }
+ size += len;
+ break;
+ case ITP_FW_VER:
+ len = snprintf(buf+size, ITP_BUF_SIZE - size,
+ "%d.%d", priv->wsm_caps.hw_id,
+ priv->wsm_caps.hw_subid);
+ if (len <= 0) {
+ cw1200_itp_err(priv, -EINVAL, 42);
+ return;
+ }
+ size += len;
+ break;
+ default:
+ cw1200_itp_err(priv, -EINVAL, 43);
+ break;
+ }
+
+ len = snprintf(buf+size, ITP_BUF_SIZE-size, "\n");
+ if (len <= 0) {
+ cw1200_itp_err(priv, -EINVAL, 44);
+ return;
+ }
+ size += len;
+
+ skb = dev_alloc_skb(size);
+ if (!skb) {
+ cw1200_itp_err(priv, -ENOMEM, 45);
+ return;
+ }
+
+ skb_trim(skb, 0);
+ skb_put(skb, size);
+
+ memcpy(skb->data, buf, size);
+ skb_queue_tail(&itp->log_queue, skb);
+ wake_up(&itp->read_wait);
+}
+
+int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data,
+ size_t *tx_len, int *burst)
+{
+ struct cw1200_itp *itp;
+ struct timespec now;
+ int time_left_us;
+
+ if (!priv->debug)
+ return 0;
+
+ itp = &priv->debug->itp;
+
+ if (!itp)
+ return 0;
+
+ spin_lock_bh(&itp->tx_lock);
+ if (atomic_read(&itp->test_mode) != TEST_MODE_TX_TEST)
+ goto out;
+
+ if (atomic_read(&itp->stop_tx))
+ goto out;
+
+ if (itp->number == 0) {
+ atomic_set(&itp->stop_tx, 1);
+ queue_delayed_work(priv->workqueue, &itp->tx_finish, HZ/10);
+ goto out;
+ }
+
+ if (!itp->data)
+ goto out;
+
+ if (priv->hw_bufs_used >= 2) {
+ if (!atomic_read(&priv->bh_rx))
+ atomic_set(&priv->bh_rx, 1);
+ atomic_set(&priv->bh_tx, 1);
+ goto out;
+ }
+
+ ktime_get_ts(&now);
+ time_left_us = (itp->last_sent.tv_sec - now.tv_sec)*1000000 +
+ (itp->last_sent.tv_nsec - now.tv_nsec)/1000 +
+ itp->interval_us;
+
+ if (time_left_us > ITP_TIME_THRES_US) {
+ queue_delayed_work(priv->workqueue, &itp->tx_work,
+ ITP_US_TO_MS(time_left_us)*HZ/1000);
+ goto out;
+ }
+
+ if (time_left_us > 50)
+ udelay(time_left_us);
+
+ if (itp->number > 0)
+ itp->number--;
+
+ *data = itp->data;
+ *tx_len = itp->data_len + itp->hdr_len;
+
+ if (itp->data_mode == ITP_DATA_RANDOM)
+ cw1200_itp_fill_pattern(&itp->data[itp->hdr_len],
+ itp->data_len, itp->data_mode);
+ *burst = 2;
+ atomic_set(&priv->bh_tx, 1);
+ ktime_get_ts(&itp->last_sent);
+ atomic_add(1, &itp->awaiting_confirm);
+ spin_unlock_bh(&itp->tx_lock);
+ return 1;
+
+out:
+ spin_unlock_bh(&itp->tx_lock);
+ return 0;
+}
+
+bool cw1200_itp_rxed(struct cw1200_common *priv, struct sk_buff *skb)
+{
+ struct cw1200_itp *itp = &priv->debug->itp;
+ struct ieee80211_rx_status *rx = IEEE80211_SKB_RXCB(skb);
+ int signal;
+
+ if (atomic_read(&itp->test_mode) != TEST_MODE_RX_TEST)
+ return cw1200_is_itp(priv);
+ if (rx->freq != priv->channel->center_freq)
+ return true;
+
+ signal = rx->signal;
+ itp->rx_cnt++;
+ itp->rx_rssi += signal;
+ if (itp->rx_rssi_min > rx->signal)
+ itp->rx_rssi_min = rx->signal;
+ if (itp->rx_rssi_max < rx->signal)
+ itp->rx_rssi_max = rx->signal;
+
+ return true;
+}
+
+void cw1200_itp_wake_up_tx(struct cw1200_common *priv)
+{
+ wake_up(&priv->debug->itp.write_wait);
+}
+
+bool cw1200_itp_tx_running(struct cw1200_common *priv)
+{
+ if (atomic_read(&priv->debug->itp.awaiting_confirm) ||
+ atomic_read(&priv->debug->itp.test_mode) ==
+ TEST_MODE_TX_TEST) {
+ atomic_sub(1, &priv->debug->itp.awaiting_confirm);
+ return true;
+ }
+ return false;
+}
+
+static void cw1200_itp_handle(struct cw1200_common *priv,
+ struct sk_buff *skb)
+{
+ struct cw1200_itp *itp = &priv->debug->itp;
+ const struct wiphy *wiphy = priv->hw->wiphy;
+ int cmd;
+ int ret;
+
+ pr_debug("[ITP] <<< %s", skb->data);
+ if (sscanf(skb->data, "%d", &cmd) != 1) {
+ cw1200_itp_err(priv, -EINVAL, 1);
+ return;
+ }
+
+ switch (cmd) {
+ case 1: /* RX test */
+ if (atomic_read(&itp->test_mode)) {
+ cw1200_itp_err(priv, -EBUSY, 0);
+ return;
+ }
+ ret = sscanf(skb->data, "%d,%d,%d",
+ &cmd, &itp->band, &itp->ch);
+ if (ret != 3) {
+ cw1200_itp_err(priv, -EINVAL, ret + 1);
+ return;
+ }
+ if (itp->band >= 2) {
+ cw1200_itp_err(priv, -EINVAL, 2);
+ } else if (!wiphy->bands[itp->band]) {
+ cw1200_itp_err(priv, -EINVAL, 2);
+ } else if (itp->ch >= wiphy->bands[itp->band]->n_channels) {
+ cw1200_itp_err(priv, -EINVAL, 3);
+ } else {
+ cw1200_itp_rx_stats(priv);
+ cw1200_itp_rx_start(priv);
+ }
+ break;
+ case 2: /* RX stat */
+ cw1200_itp_rx_stats(priv);
+ break;
+ case 3: /* RX/TX stop */
+ if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST) {
+ cw1200_itp_rx_stats(priv);
+ cw1200_itp_rx_stop(priv);
+ } else if (atomic_read(&itp->test_mode) == TEST_MODE_TX_TEST) {
+ cw1200_itp_tx_stop(priv);
+ } else {
+ cw1200_itp_err(priv, -EBUSY, 0);
+ }
+ break;
+ case 4: /* TX start */
+ if (atomic_read(&itp->test_mode) != TEST_MODE_NO_TEST) {
+ cw1200_itp_err(priv, -EBUSY, 0);
+ return;
+ }
+ ret = sscanf(skb->data, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
+ &cmd, &itp->band, &itp->ch, &itp->rate,
+ &itp->preamble, &itp->number, &itp->data_mode,
+ &itp->interval_us, &itp->power, &itp->data_len);
+ if (ret != 10) {
+ cw1200_itp_err(priv, -EINVAL, ret + 1);
+ return;
+ }
+ if (itp->band >= 2) {
+ cw1200_itp_err(priv, -EINVAL, 2);
+ } else if (!wiphy->bands[itp->band]) {
+ cw1200_itp_err(priv, -EINVAL, 2);
+ } else if (itp->ch >= wiphy->bands[itp->band]->n_channels) {
+ cw1200_itp_err(priv, -EINVAL, 3);
+ } else if (itp->rate >= 20) {
+ cw1200_itp_err(priv, -EINVAL, 4);
+ } else if (itp->preamble >= ITP_PREAMBLE_MAX) {
+ cw1200_itp_err(priv, -EINVAL, 5);
+ } else if (itp->data_mode >= ITP_DATA_MAX_MODE) {
+ cw1200_itp_err(priv, -EINVAL, 7);
+ } else if (itp->data_len < ITP_MIN_DATA_SIZE ||
+ itp->data_len > (priv->wsm_caps.input_buffer_size - itp->hdr_len)) {
+ cw1200_itp_err(priv, -EINVAL, 8);
+ } else {
+ cw1200_itp_tx_start(priv);
+ }
+ break;
+ case 5:
+ cw1200_itp_get_version(priv, ITP_CHIP_ID);
+ break;
+ case 6:
+ cw1200_itp_get_version(priv, ITP_FW_VER);
+ break;
+ }
+}
+
+static void cw1200_itp_err(struct cw1200_common *priv,
+ int err, int arg)
+{
+ struct cw1200_itp *itp = &priv->debug->itp;
+ struct sk_buff *skb;
+ static char buf[255];
+ int len;
+
+ len = snprintf(buf, sizeof(buf), "%d,%d\n",
+ err, arg);
+ if (len <= 0)
+ return;
+
+ skb = dev_alloc_skb(len);
+ if (!skb)
+ return;
+
+ skb_trim(skb, 0);
+ skb_put(skb, len);
+
+ memcpy(skb->data, buf, len);
+ skb_queue_tail(&itp->log_queue, skb);
+ wake_up(&itp->read_wait);
+
+ len = sprint_symbol(buf,
+ (unsigned long)__builtin_return_address(0));
+ if (len <= 0)
+ return;
+ pr_debug("[ITP] error %d,%d from %s\n",
+ err, arg, buf);
+}
diff --git a/drivers/net/wireless/cw1200/itp.h b/drivers/net/wireless/cw1200/itp.h
new file mode 100644
index 00000000000..1e9dfb7fcad
--- /dev/null
+++ b/drivers/net/wireless/cw1200/itp.h
@@ -0,0 +1,144 @@
+/*
+ * ITP code for ST-Ericsson CW1200 mac80211 driver
+ *
+ * Copyright (c) 2011, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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.
+ */
+
+#ifndef CW1200_ITP_H_INCLUDED
+#define CW1200_ITP_H_INCLUDED
+
+struct cw200_common;
+struct wsm_tx_confirm;
+struct dentry;
+
+#ifdef CONFIG_CW1200_ITP
+
+/*extern*/ struct ieee80211_channel;
+
+#define TEST_MODE_NO_TEST (0)
+#define TEST_MODE_RX_TEST (1)
+#define TEST_MODE_TX_TEST (2)
+#define ITP_DEFAULT_DA_ADDR {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+#define ITP_MIN_DATA_SIZE 6
+#define ITP_MAX_DATA_SIZE 1600
+#define ITP_TIME_THRES_US 10000
+#define ITP_US_TO_MS(x) ((x)/1000)
+#define ITP_MS_TO_US(x) ((x)*1000)
+#define ITP_BUF_SIZE 255
+
+
+enum cw1200_itp_data_modes {
+ ITP_DATA_ZEROS,
+ ITP_DATA_ONES,
+ ITP_DATA_ZERONES,
+ ITP_DATA_RANDOM,
+ ITP_DATA_MAX_MODE,
+};
+
+enum cw1200_itp_version_type {
+ ITP_CHIP_ID,
+ ITP_FW_VER,
+};
+
+enum cw1200_itp_preamble_type {
+ ITP_PREAMBLE_LONG,
+ ITP_PREAMBLE_SHORT,
+ ITP_PREAMBLE_OFDM,
+ ITP_PREAMBLE_MIXED,
+ ITP_PREAMBLE_GREENFIELD,
+ ITP_PREAMBLE_MAX,
+};
+
+
+struct cw1200_itp {
+ struct cw1200_common *priv;
+ atomic_t open_count;
+ atomic_t awaiting_confirm;
+ struct sk_buff_head log_queue;
+ wait_queue_head_t read_wait;
+ wait_queue_head_t write_wait;
+ wait_queue_head_t close_wait;
+ struct ieee80211_channel *saved_channel;
+ atomic_t stop_tx;
+ struct delayed_work tx_work;
+ struct delayed_work tx_finish;
+ spinlock_t tx_lock;
+ struct timespec last_sent;
+ atomic_t test_mode;
+ int rx_cnt;
+ long rx_rssi;
+ int rx_rssi_max;
+ int rx_rssi_min;
+ unsigned band;
+ unsigned ch;
+ unsigned rate;
+ unsigned preamble;
+ unsigned int number;
+ unsigned data_mode;
+ int interval_us;
+ int power;
+ u8 *data;
+ int hdr_len;
+ int data_len;
+};
+
+int cw1200_itp_init(struct cw1200_common *priv);
+void cw1200_itp_release(struct cw1200_common *priv);
+
+bool cw1200_is_itp(struct cw1200_common *priv);
+bool cw1200_itp_rxed(struct cw1200_common *priv, struct sk_buff *skb);
+void cw1200_itp_wake_up_tx(struct cw1200_common *priv);
+int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data,
+ size_t *tx_len, int *burst);
+bool cw1200_itp_tx_running(struct cw1200_common *priv);
+
+#else /* CONFIG_CW1200_ITP */
+
+static inline int cw1200_itp_init(struct cw1200_common *priv)
+{
+ return 0;
+}
+
+static inline void cw1200_itp_release(struct cw1200_common *priv)
+{
+}
+
+static inline bool cw1200_is_itp(struct cw1200_common *priv)
+{
+ return false;
+}
+
+static inline bool cw1200_itp_rxed(struct cw1200_common *priv,
+ struct sk_buff *skb)
+{
+ return false;
+}
+
+
+static inline void cw1200_itp_consume_txed(struct cw1200_common *priv)
+{
+}
+
+static inline void cw1200_itp_wake_up_tx(struct cw1200_common *priv)
+{
+}
+
+static inline int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data,
+ size_t *tx_len, int *burst)
+{
+ return 0;
+}
+
+static inline bool cw1200_itp_tx_running(struct cw1200_common *priv)
+{
+ return false;
+}
+
+#endif /* CONFIG_CW1200_ITP */
+
+#endif /* CW1200_ITP_H_INCLUDED */
diff --git a/drivers/net/wireless/cw1200/main.c b/drivers/net/wireless/cw1200/main.c
new file mode 100644
index 00000000000..8426d3d7607
--- /dev/null
+++ b/drivers/net/wireless/cw1200/main.c
@@ -0,0 +1,618 @@
+/*
+ * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on:
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
+ * Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
+ *
+ * Based on:
+ * - the islsm (softmac prism54) driver, which is:
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ * - stlc45xx driver
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+#include <linux/vmalloc.h>
+#include <linux/random.h>
+#include <linux/sched.h>
+#include <net/mac80211.h>
+
+#include "cw1200.h"
+#include "txrx.h"
+#include "sbus.h"
+#include "fwio.h"
+#include "hwio.h"
+#include "bh.h"
+#include "sta.h"
+#include "scan.h"
+#include "debug.h"
+#include "pm.h"
+
+MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>");
+MODULE_DESCRIPTION("Softmac ST-Ericsson CW1200 common code");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("cw1200_core");
+
+/* Accept MAC address of the form macaddr=0x00,0x80,0xE1,0x30,0x40,0x50 */
+static u8 cw1200_mac_template[ETH_ALEN] = {0x02, 0x80, 0xe1, 0x00, 0x00, 0x00};
+module_param_array_named(macaddr, cw1200_mac_template, byte, NULL, S_IRUGO);
+MODULE_PARM_DESC(macaddr, "Override platform_data MAC address");
+
+static char *cw1200_sdd_path;
+module_param(cw1200_sdd_path, charp, 0644);
+MODULE_PARM_DESC(cw1200_sdd_path, "Override platform_data SDD file");
+static int cw1200_refclk;
+module_param(cw1200_refclk, int, 0644);
+MODULE_PARM_DESC(cw1200_refclk, "Override platform_data reference clock");
+
+int cw1200_power_mode = wsm_power_mode_quiescent;
+module_param(cw1200_power_mode, int, 0644);
+MODULE_PARM_DESC(cw1200_power_mode, "WSM power mode. 0 == active, 1 == doze, 2 == quiescent (default)");
+
+#ifdef CONFIG_CW1200_ETF
+int etf_mode;
+module_param(etf_mode, int, 0644);
+MODULE_PARM_DESC(etf_mode, "Enable EngineeringTestingFramework operation");
+#endif
+
+#define RATETAB_ENT(_rate, _rateid, _flags) \
+ { \
+ .bitrate = (_rate), \
+ .hw_value = (_rateid), \
+ .flags = (_flags), \
+ }
+
+static struct ieee80211_rate cw1200_rates[] = {
+ RATETAB_ENT(10, 0, 0),
+ RATETAB_ENT(20, 1, 0),
+ RATETAB_ENT(55, 2, 0),
+ RATETAB_ENT(110, 3, 0),
+ RATETAB_ENT(60, 6, 0),
+ RATETAB_ENT(90, 7, 0),
+ RATETAB_ENT(120, 8, 0),
+ RATETAB_ENT(180, 9, 0),
+ RATETAB_ENT(240, 10, 0),
+ RATETAB_ENT(360, 11, 0),
+ RATETAB_ENT(480, 12, 0),
+ RATETAB_ENT(540, 13, 0),
+};
+
+static struct ieee80211_rate cw1200_mcs_rates[] = {
+ RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS),
+ RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS),
+ RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS),
+ RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS),
+ RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS),
+ RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS),
+ RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS),
+ RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS),
+};
+
+#define cw1200_a_rates (cw1200_rates + 4)
+#define cw1200_a_rates_size (ARRAY_SIZE(cw1200_rates) - 4)
+#define cw1200_g_rates (cw1200_rates + 0)
+#define cw1200_g_rates_size (ARRAY_SIZE(cw1200_rates))
+#define cw1200_n_rates (cw1200_mcs_rates)
+#define cw1200_n_rates_size (ARRAY_SIZE(cw1200_mcs_rates))
+
+
+#define CHAN2G(_channel, _freq, _flags) { \
+ .band = IEEE80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+#define CHAN5G(_channel, _flags) { \
+ .band = IEEE80211_BAND_5GHZ, \
+ .center_freq = 5000 + (5 * (_channel)), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+static struct ieee80211_channel cw1200_2ghz_chantable[] = {
+ CHAN2G(1, 2412, 0),
+ CHAN2G(2, 2417, 0),
+ CHAN2G(3, 2422, 0),
+ CHAN2G(4, 2427, 0),
+ CHAN2G(5, 2432, 0),
+ CHAN2G(6, 2437, 0),
+ CHAN2G(7, 2442, 0),
+ CHAN2G(8, 2447, 0),
+ CHAN2G(9, 2452, 0),
+ CHAN2G(10, 2457, 0),
+ CHAN2G(11, 2462, 0),
+ CHAN2G(12, 2467, 0),
+ CHAN2G(13, 2472, 0),
+ CHAN2G(14, 2484, 0),
+};
+
+static struct ieee80211_channel cw1200_5ghz_chantable[] = {
+ CHAN5G(34, 0), CHAN5G(36, 0),
+ CHAN5G(38, 0), CHAN5G(40, 0),
+ CHAN5G(42, 0), CHAN5G(44, 0),
+ CHAN5G(46, 0), CHAN5G(48, 0),
+ CHAN5G(52, 0), CHAN5G(56, 0),
+ CHAN5G(60, 0), CHAN5G(64, 0),
+ CHAN5G(100, 0), CHAN5G(104, 0),
+ CHAN5G(108, 0), CHAN5G(112, 0),
+ CHAN5G(116, 0), CHAN5G(120, 0),
+ CHAN5G(124, 0), CHAN5G(128, 0),
+ CHAN5G(132, 0), CHAN5G(136, 0),
+ CHAN5G(140, 0), CHAN5G(149, 0),
+ CHAN5G(153, 0), CHAN5G(157, 0),
+ CHAN5G(161, 0), CHAN5G(165, 0),
+ CHAN5G(184, 0), CHAN5G(188, 0),
+ CHAN5G(192, 0), CHAN5G(196, 0),
+ CHAN5G(200, 0), CHAN5G(204, 0),
+ CHAN5G(208, 0), CHAN5G(212, 0),
+ CHAN5G(216, 0),
+};
+
+static struct ieee80211_supported_band cw1200_band_2ghz = {
+ .channels = cw1200_2ghz_chantable,
+ .n_channels = ARRAY_SIZE(cw1200_2ghz_chantable),
+ .bitrates = cw1200_g_rates,
+ .n_bitrates = cw1200_g_rates_size,
+ .ht_cap = {
+ .cap = IEEE80211_HT_CAP_GRN_FLD |
+ (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
+ IEEE80211_HT_CAP_MAX_AMSDU,
+ .ht_supported = 1,
+ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
+ .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
+ .mcs = {
+ .rx_mask[0] = 0xFF,
+ .rx_highest = __cpu_to_le16(0x41),
+ .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+ },
+ },
+};
+
+static struct ieee80211_supported_band cw1200_band_5ghz = {
+ .channels = cw1200_5ghz_chantable,
+ .n_channels = ARRAY_SIZE(cw1200_5ghz_chantable),
+ .bitrates = cw1200_a_rates,
+ .n_bitrates = cw1200_a_rates_size,
+ .ht_cap = {
+ .cap = IEEE80211_HT_CAP_GRN_FLD |
+ (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
+ IEEE80211_HT_CAP_MAX_AMSDU,
+ .ht_supported = 1,
+ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
+ .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
+ .mcs = {
+ .rx_mask[0] = 0xFF,
+ .rx_highest = __cpu_to_le16(0x41),
+ .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+ },
+ },
+};
+
+static const unsigned long cw1200_ttl[] = {
+ 1 * HZ, /* VO */
+ 2 * HZ, /* VI */
+ 5 * HZ, /* BE */
+ 10 * HZ /* BK */
+};
+
+static const struct ieee80211_ops cw1200_ops = {
+ .start = cw1200_start,
+ .stop = cw1200_stop,
+ .add_interface = cw1200_add_interface,
+ .remove_interface = cw1200_remove_interface,
+ .change_interface = cw1200_change_interface,
+ .tx = cw1200_tx,
+ .hw_scan = cw1200_hw_scan,
+ .set_tim = cw1200_set_tim,
+ .sta_notify = cw1200_sta_notify,
+ .sta_add = cw1200_sta_add,
+ .sta_remove = cw1200_sta_remove,
+ .set_key = cw1200_set_key,
+ .set_rts_threshold = cw1200_set_rts_threshold,
+ .config = cw1200_config,
+ .bss_info_changed = cw1200_bss_info_changed,
+ .prepare_multicast = cw1200_prepare_multicast,
+ .configure_filter = cw1200_configure_filter,
+ .conf_tx = cw1200_conf_tx,
+ .get_stats = cw1200_get_stats,
+ .ampdu_action = cw1200_ampdu_action,
+ .flush = cw1200_flush,
+ .suspend = cw1200_wow_suspend,
+ .resume = cw1200_wow_resume,
+ /* Intentionally not offloaded: */
+ /*.channel_switch = cw1200_channel_switch, */
+ /*.remain_on_channel = cw1200_remain_on_channel, */
+ /*.cancel_remain_on_channel = cw1200_cancel_remain_on_channel, */
+};
+
+int cw1200_ba_rx_tids = -1;
+int cw1200_ba_tx_tids = -1;
+module_param(cw1200_ba_rx_tids, int, 0644);
+module_param(cw1200_ba_tx_tids, int, 0644);
+MODULE_PARM_DESC(cw1200_ba_rx_tids, "Block ACK RX TIDs");
+MODULE_PARM_DESC(cw1200_ba_tx_tids, "Block ACK TX TIDs");
+
+static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr,
+ const bool have_5ghz)
+{
+ int i, band;
+ struct ieee80211_hw *hw;
+ struct cw1200_common *priv;
+
+ hw = ieee80211_alloc_hw(sizeof(struct cw1200_common), &cw1200_ops);
+ if (!hw)
+ return NULL;
+
+ priv = hw->priv;
+ priv->hw = hw;
+ priv->hw_type = -1;
+ priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+ priv->rates = cw1200_rates; /* TODO: fetch from FW */
+ priv->mcs_rates = cw1200_n_rates;
+ if (cw1200_ba_rx_tids != -1)
+ priv->ba_rx_tid_mask = cw1200_ba_rx_tids;
+ else
+ priv->ba_rx_tid_mask = 0xFF; /* Enable RX BLKACK for all TIDs */
+ if (cw1200_ba_tx_tids != -1)
+ priv->ba_tx_tid_mask = cw1200_ba_tx_tids;
+ else
+ priv->ba_tx_tid_mask = 0xff; /* Enable TX BLKACK for all TIDs */
+
+ hw->flags = IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_SUPPORTS_PS |
+ IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
+ IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+ IEEE80211_HW_SUPPORTS_UAPSD |
+ IEEE80211_HW_CONNECTION_MONITOR |
+ IEEE80211_HW_AMPDU_AGGREGATION |
+ IEEE80211_HW_TX_AMPDU_SETUP_IN_HW |
+ IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC;
+
+ hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_MESH_POINT) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO);
+
+ /* Support only for limited wowlan functionalities */
+ hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY |
+ WIPHY_WOWLAN_DISCONNECT;
+ hw->wiphy->wowlan.n_patterns = 0;
+
+ hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+
+ hw->channel_change_time = 1000; /* TODO: find actual value */
+ hw->queues = 4;
+
+ priv->rts_threshold = -1;
+
+ hw->max_rates = 8;
+ hw->max_rate_tries = 15;
+ hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM +
+ 8; /* TKIP IV */
+
+ hw->sta_data_size = sizeof(struct cw1200_sta_priv);
+
+ hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &cw1200_band_2ghz;
+ if (have_5ghz)
+ hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &cw1200_band_5ghz;
+
+ /* Channel params have to be cleared before registering wiphy again */
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+ struct ieee80211_supported_band *sband = hw->wiphy->bands[band];
+ if (!sband)
+ continue;
+ for (i = 0; i < sband->n_channels; i++) {
+ sband->channels[i].flags = 0;
+ sband->channels[i].max_antenna_gain = 0;
+ sband->channels[i].max_power = 30;
+ }
+ }
+
+ hw->wiphy->max_scan_ssids = 2;
+ hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+
+ if (macaddr)
+ SET_IEEE80211_PERM_ADDR(hw, (u8 *)macaddr);
+ else
+ SET_IEEE80211_PERM_ADDR(hw, cw1200_mac_template);
+
+ /* Fix up mac address if necessary */
+ if (hw->wiphy->perm_addr[3] == 0 &&
+ hw->wiphy->perm_addr[4] == 0 &&
+ hw->wiphy->perm_addr[5] == 0) {
+ get_random_bytes(&hw->wiphy->perm_addr[3], 3);
+ }
+
+ mutex_init(&priv->wsm_cmd_mux);
+ mutex_init(&priv->conf_mutex);
+ priv->workqueue = create_singlethread_workqueue("cw1200_wq");
+ sema_init(&priv->scan.lock, 1);
+ INIT_WORK(&priv->scan.work, cw1200_scan_work);
+ INIT_DELAYED_WORK(&priv->scan.probe_work, cw1200_probe_work);
+ INIT_DELAYED_WORK(&priv->scan.timeout, cw1200_scan_timeout);
+ INIT_DELAYED_WORK(&priv->clear_recent_scan_work,
+ cw1200_clear_recent_scan_work);
+ INIT_DELAYED_WORK(&priv->join_timeout, cw1200_join_timeout);
+ INIT_WORK(&priv->unjoin_work, cw1200_unjoin_work);
+ INIT_WORK(&priv->join_complete_work, cw1200_join_complete_work);
+ INIT_WORK(&priv->wep_key_work, cw1200_wep_key_work);
+ INIT_WORK(&priv->tx_policy_upload_work, tx_policy_upload_work);
+ spin_lock_init(&priv->event_queue_lock);
+ INIT_LIST_HEAD(&priv->event_queue);
+ INIT_WORK(&priv->event_handler, cw1200_event_handler);
+ INIT_DELAYED_WORK(&priv->bss_loss_work, cw1200_bss_loss_work);
+ INIT_WORK(&priv->bss_params_work, cw1200_bss_params_work);
+ spin_lock_init(&priv->bss_loss_lock);
+ spin_lock_init(&priv->ps_state_lock);
+ INIT_WORK(&priv->set_cts_work, cw1200_set_cts_work);
+ INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work);
+ INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work);
+ INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work);
+ INIT_WORK(&priv->link_id_work, cw1200_link_id_work);
+ INIT_DELAYED_WORK(&priv->link_id_gc_work, cw1200_link_id_gc_work);
+ INIT_WORK(&priv->linkid_reset_work, cw1200_link_id_reset);
+ INIT_WORK(&priv->update_filtering_work, cw1200_update_filtering_work);
+ INIT_WORK(&priv->set_beacon_wakeup_period_work,
+ cw1200_set_beacon_wakeup_period_work);
+ init_timer(&priv->mcast_timeout);
+ priv->mcast_timeout.data = (unsigned long)priv;
+ priv->mcast_timeout.function = cw1200_mcast_timeout;
+
+ if (cw1200_queue_stats_init(&priv->tx_queue_stats,
+ CW1200_LINK_ID_MAX,
+ cw1200_skb_dtor,
+ priv)) {
+ ieee80211_free_hw(hw);
+ return NULL;
+ }
+
+ for (i = 0; i < 4; ++i) {
+ if (cw1200_queue_init(&priv->tx_queue[i],
+ &priv->tx_queue_stats, i, 16,
+ cw1200_ttl[i])) {
+ for (; i > 0; i--)
+ cw1200_queue_deinit(&priv->tx_queue[i - 1]);
+ cw1200_queue_stats_deinit(&priv->tx_queue_stats);
+ ieee80211_free_hw(hw);
+ return NULL;
+ }
+ }
+
+ init_waitqueue_head(&priv->channel_switch_done);
+ init_waitqueue_head(&priv->wsm_cmd_wq);
+ init_waitqueue_head(&priv->wsm_startup_done);
+ init_waitqueue_head(&priv->ps_mode_switch_done);
+ wsm_buf_init(&priv->wsm_cmd_buf);
+ spin_lock_init(&priv->wsm_cmd.lock);
+ priv->wsm_cmd.done = 1;
+ tx_policy_init(priv);
+
+ return hw;
+}
+
+static int cw1200_register_common(struct ieee80211_hw *dev)
+{
+ struct cw1200_common *priv = dev->priv;
+ int err;
+
+#ifdef CONFIG_CW1200_ETF
+ if (etf_mode)
+ goto done;
+#endif
+
+ err = cw1200_pm_init(&priv->pm_state, priv);
+ if (err) {
+ pr_err("Cannot init PM. (%d).\n",
+ err);
+ return err;
+ }
+
+ err = ieee80211_register_hw(dev);
+ if (err) {
+ pr_err("Cannot register device (%d).\n",
+ err);
+ cw1200_pm_deinit(&priv->pm_state);
+ return err;
+ }
+
+#ifdef CONFIG_CW1200_ETF
+done:
+#endif
+ cw1200_debug_init(priv);
+
+ pr_info("Registered as '%s'\n", wiphy_name(dev->wiphy));
+ return 0;
+}
+
+static void cw1200_free_common(struct ieee80211_hw *dev)
+{
+ ieee80211_free_hw(dev);
+}
+
+static void cw1200_unregister_common(struct ieee80211_hw *dev)
+{
+ struct cw1200_common *priv = dev->priv;
+ int i;
+
+#ifdef CONFIG_CW1200_ETF
+ if (!etf_mode) {
+#endif
+ ieee80211_unregister_hw(dev);
+#ifdef CONFIG_CW1200_ETF
+ }
+#endif
+
+ del_timer_sync(&priv->mcast_timeout);
+ cw1200_unregister_bh(priv);
+
+ cw1200_debug_release(priv);
+
+ mutex_destroy(&priv->conf_mutex);
+
+ wsm_buf_deinit(&priv->wsm_cmd_buf);
+
+ destroy_workqueue(priv->workqueue);
+ priv->workqueue = NULL;
+
+ if (priv->sdd) {
+ release_firmware(priv->sdd);
+ priv->sdd = NULL;
+ }
+
+ for (i = 0; i < 4; ++i)
+ cw1200_queue_deinit(&priv->tx_queue[i]);
+
+ cw1200_queue_stats_deinit(&priv->tx_queue_stats);
+ cw1200_pm_deinit(&priv->pm_state);
+}
+
+/* Clock is in KHz */
+u32 cw1200_dpll_from_clk(u16 clk_khz)
+{
+ switch (clk_khz) {
+ case 0x32C8: /* 13000 KHz */
+ return 0x1D89D241;
+ case 0x3E80: /* 16000 KHz */
+ return 0x000001E1;
+ case 0x41A0: /* 16800 KHz */
+ return 0x124931C1;
+ case 0x4B00: /* 19200 KHz */
+ return 0x00000191;
+ case 0x5DC0: /* 24000 KHz */
+ return 0x00000141;
+ case 0x6590: /* 26000 KHz */
+ return 0x0EC4F121;
+ case 0x8340: /* 33600 KHz */
+ return 0x092490E1;
+ case 0x9600: /* 38400 KHz */
+ return 0x100010C1;
+ case 0x9C40: /* 40000 KHz */
+ return 0x000000C1;
+ case 0xBB80: /* 48000 KHz */
+ return 0x000000A1;
+ case 0xCB20: /* 52000 KHz */
+ return 0x07627091;
+ default:
+ pr_err("Unknown Refclk freq (0x%04x), using 2600KHz\n",
+ clk_khz);
+ return 0x0EC4F121;
+ }
+}
+
+int cw1200_core_probe(const struct sbus_ops *sbus_ops,
+ struct sbus_priv *sbus,
+ struct device *pdev,
+ struct cw1200_common **core,
+ int ref_clk, const u8 *macaddr,
+ const char *sdd_path, bool have_5ghz)
+{
+ int err = -EINVAL;
+ struct ieee80211_hw *dev;
+ struct cw1200_common *priv;
+ struct wsm_operational_mode mode = {
+ .power_mode = cw1200_power_mode,
+ .disable_more_flag_usage = true,
+ };
+
+ dev = cw1200_init_common(macaddr, have_5ghz);
+ if (!dev)
+ goto err;
+
+ priv = dev->priv;
+ priv->hw_refclk = ref_clk;
+ if (cw1200_refclk)
+ priv->hw_refclk = cw1200_refclk;
+
+ priv->sdd_path = (char *)sdd_path;
+ if (cw1200_sdd_path)
+ priv->sdd_path = cw1200_sdd_path;
+
+ priv->sbus_ops = sbus_ops;
+ priv->sbus_priv = sbus;
+ priv->pdev = pdev;
+ SET_IEEE80211_DEV(priv->hw, pdev);
+
+ /* Pass struct cw1200_common back up */
+ *core = priv;
+
+ err = cw1200_register_bh(priv);
+ if (err)
+ goto err1;
+
+#ifdef CONFIG_CW1200_ETF
+ if (etf_mode)
+ goto skip_fw;
+#endif
+
+ err = cw1200_load_firmware(priv);
+ if (err)
+ goto err2;
+
+ if (wait_event_interruptible_timeout(priv->wsm_startup_done,
+ priv->firmware_ready,
+ 3*HZ) <= 0) {
+ /* TODO: Need to find how to reset device
+ in QUEUE mode properly.
+ */
+ pr_err("Timeout waiting on device startup\n");
+ err = -ETIMEDOUT;
+ goto err2;
+ }
+
+ /* Set low-power mode. */
+ wsm_set_operational_mode(priv, &mode);
+
+ /* Enable multi-TX confirmation */
+ wsm_use_multi_tx_conf(priv, true);
+
+#ifdef CONFIG_CW1200_ETF
+skip_fw:
+#endif
+ err = cw1200_register_common(dev);
+ if (err)
+ goto err2;
+
+ return err;
+
+err2:
+ cw1200_unregister_bh(priv);
+err1:
+ cw1200_free_common(dev);
+err:
+ *core = NULL;
+ return err;
+}
+EXPORT_SYMBOL_GPL(cw1200_core_probe);
+
+void cw1200_core_release(struct cw1200_common *self)
+{
+ /* Disable device interrupts */
+ self->sbus_ops->lock(self->sbus_priv);
+ __cw1200_irq_enable(self, 0);
+ self->sbus_ops->unlock(self->sbus_priv);
+
+ /* And then clean up */
+ cw1200_unregister_common(self->hw);
+ cw1200_free_common(self->hw);
+ return;
+}
+EXPORT_SYMBOL_GPL(cw1200_core_release);
diff --git a/drivers/net/wireless/cw1200/pm.c b/drivers/net/wireless/cw1200/pm.c
new file mode 100644
index 00000000000..79edfb93b29
--- /dev/null
+++ b/drivers/net/wireless/cw1200/pm.c
@@ -0,0 +1,367 @@
+/*
+ * Mac80211 power management API for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2011, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/module.h>
+#include <linux/if_ether.h>
+#include "cw1200.h"
+#include "pm.h"
+#include "sta.h"
+#include "bh.h"
+#include "sbus.h"
+
+#define CW1200_BEACON_SKIPPING_MULTIPLIER 3
+
+struct cw1200_udp_port_filter {
+ struct wsm_udp_port_filter_hdr hdr;
+ /* Up to 4 filters are allowed. */
+ struct wsm_udp_port_filter filters[WSM_MAX_FILTER_ELEMENTS];
+} __packed;
+
+struct cw1200_ether_type_filter {
+ struct wsm_ether_type_filter_hdr hdr;
+ /* Up to 4 filters are allowed. */
+ struct wsm_ether_type_filter filters[WSM_MAX_FILTER_ELEMENTS];
+} __packed;
+
+static struct cw1200_udp_port_filter cw1200_udp_port_filter_on = {
+ .hdr.num = 2,
+ .filters = {
+ [0] = {
+ .action = WSM_FILTER_ACTION_FILTER_OUT,
+ .type = WSM_FILTER_PORT_TYPE_DST,
+ .port = __cpu_to_le16(67), /* DHCP Bootps */
+ },
+ [1] = {
+ .action = WSM_FILTER_ACTION_FILTER_OUT,
+ .type = WSM_FILTER_PORT_TYPE_DST,
+ .port = __cpu_to_le16(68), /* DHCP Bootpc */
+ },
+ }
+};
+
+static struct wsm_udp_port_filter_hdr cw1200_udp_port_filter_off = {
+ .num = 0,
+};
+
+#ifndef ETH_P_WAPI
+#define ETH_P_WAPI 0x88B4
+#endif
+
+static struct cw1200_ether_type_filter cw1200_ether_type_filter_on = {
+ .hdr.num = 4,
+ .filters = {
+ [0] = {
+ .action = WSM_FILTER_ACTION_FILTER_IN,
+ .type = __cpu_to_le16(ETH_P_IP),
+ },
+ [1] = {
+ .action = WSM_FILTER_ACTION_FILTER_IN,
+ .type = __cpu_to_le16(ETH_P_PAE),
+ },
+ [2] = {
+ .action = WSM_FILTER_ACTION_FILTER_IN,
+ .type = __cpu_to_le16(ETH_P_WAPI),
+ },
+ [3] = {
+ .action = WSM_FILTER_ACTION_FILTER_IN,
+ .type = __cpu_to_le16(ETH_P_ARP),
+ },
+ },
+};
+
+static struct wsm_ether_type_filter_hdr cw1200_ether_type_filter_off = {
+ .num = 0,
+};
+
+/* private */
+struct cw1200_suspend_state {
+ unsigned long bss_loss_tmo;
+ unsigned long join_tmo;
+ unsigned long direct_probe;
+ unsigned long link_id_gc;
+ bool beacon_skipping;
+ u8 prev_ps_mode;
+};
+
+static void cw1200_pm_stay_awake_tmo(unsigned long arg)
+{
+ /* XXX what's the point of this ? */
+}
+
+int cw1200_pm_init(struct cw1200_pm_state *pm,
+ struct cw1200_common *priv)
+{
+ spin_lock_init(&pm->lock);
+
+ init_timer(&pm->stay_awake);
+ pm->stay_awake.data = (unsigned long)pm;
+ pm->stay_awake.function = cw1200_pm_stay_awake_tmo;
+
+ return 0;
+}
+
+void cw1200_pm_deinit(struct cw1200_pm_state *pm)
+{
+ del_timer_sync(&pm->stay_awake);
+}
+
+void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
+ unsigned long tmo)
+{
+ long cur_tmo;
+ spin_lock_bh(&pm->lock);
+ cur_tmo = pm->stay_awake.expires - jiffies;
+ if (!timer_pending(&pm->stay_awake) || cur_tmo < (long)tmo)
+ mod_timer(&pm->stay_awake, jiffies + tmo);
+ spin_unlock_bh(&pm->lock);
+}
+
+static long cw1200_suspend_work(struct delayed_work *work)
+{
+ int ret = cancel_delayed_work(work);
+ long tmo;
+ if (ret > 0) {
+ /* Timer is pending */
+ tmo = work->timer.expires - jiffies;
+ if (tmo < 0)
+ tmo = 0;
+ } else {
+ tmo = -1;
+ }
+ return tmo;
+}
+
+static int cw1200_resume_work(struct cw1200_common *priv,
+ struct delayed_work *work,
+ unsigned long tmo)
+{
+ if ((long)tmo < 0)
+ return 1;
+
+ return queue_delayed_work(priv->workqueue, work, tmo);
+}
+
+int cw1200_can_suspend(struct cw1200_common *priv)
+{
+ if (atomic_read(&priv->bh_rx)) {
+ wiphy_dbg(priv->hw->wiphy, "Suspend interrupted.\n");
+ return 0;
+ }
+ return 1;
+}
+EXPORT_SYMBOL_GPL(cw1200_can_suspend);
+
+int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+{
+ struct cw1200_common *priv = hw->priv;
+ struct cw1200_pm_state *pm_state = &priv->pm_state;
+ struct cw1200_suspend_state *state;
+ int ret;
+
+ spin_lock_bh(&pm_state->lock);
+ ret = timer_pending(&pm_state->stay_awake);
+ spin_unlock_bh(&pm_state->lock);
+ if (ret)
+ return -EAGAIN;
+
+ /* Do not suspend when datapath is not idle */
+ if (priv->tx_queue_stats.num_queued)
+ return -EBUSY;
+
+ /* Make sure there is no configuration requests in progress. */
+ if (!mutex_trylock(&priv->conf_mutex))
+ return -EBUSY;
+
+ /* Ensure pending operations are done.
+ * Note also that wow_suspend must return in ~2.5sec, before
+ * watchdog is triggered.
+ */
+ if (priv->channel_switch_in_progress)
+ goto revert1;
+
+ /* Do not suspend when join is pending */
+ if (priv->join_pending)
+ goto revert1;
+
+ /* Do not suspend when scanning */
+ if (down_trylock(&priv->scan.lock))
+ goto revert1;
+
+ /* Lock TX. */
+ wsm_lock_tx_async(priv);
+
+ /* Wait to avoid possible race with bh code.
+ * But do not wait too long...
+ */
+ if (wait_event_timeout(priv->bh_evt_wq,
+ !priv->hw_bufs_used, HZ / 10) <= 0)
+ goto revert2;
+
+ /* Set UDP filter */
+ wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_on.hdr);
+
+ /* Set ethernet frame type filter */
+ wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_on.hdr);
+
+ /* Allocate state */
+ state = kzalloc(sizeof(struct cw1200_suspend_state), GFP_KERNEL);
+ if (!state)
+ goto revert3;
+
+ /* Change to legacy PS while going to suspend */
+ if (!priv->vif->p2p &&
+ priv->join_status == CW1200_JOIN_STATUS_STA &&
+ priv->powersave_mode.mode != WSM_PSM_PS) {
+ state->prev_ps_mode = priv->powersave_mode.mode;
+ priv->powersave_mode.mode = WSM_PSM_PS;
+ cw1200_set_pm(priv, &priv->powersave_mode);
+ if (wait_event_interruptible_timeout(priv->ps_mode_switch_done,
+ !priv->ps_mode_switch_in_progress, 1*HZ) <= 0) {
+ goto revert3;
+ }
+ }
+
+ /* Store delayed work states. */
+ state->bss_loss_tmo =
+ cw1200_suspend_work(&priv->bss_loss_work);
+ state->join_tmo =
+ cw1200_suspend_work(&priv->join_timeout);
+ state->direct_probe =
+ cw1200_suspend_work(&priv->scan.probe_work);
+ state->link_id_gc =
+ cw1200_suspend_work(&priv->link_id_gc_work);
+
+ cancel_delayed_work_sync(&priv->clear_recent_scan_work);
+ atomic_set(&priv->recent_scan, 0);
+
+ /* Enable beacon skipping */
+ if (priv->join_status == CW1200_JOIN_STATUS_STA &&
+ priv->join_dtim_period &&
+ !priv->has_multicast_subscription) {
+ state->beacon_skipping = true;
+ wsm_set_beacon_wakeup_period(priv,
+ priv->join_dtim_period,
+ CW1200_BEACON_SKIPPING_MULTIPLIER * priv->join_dtim_period);
+ }
+
+ /* Stop serving thread */
+ if (cw1200_bh_suspend(priv))
+ goto revert4;
+
+ ret = timer_pending(&priv->mcast_timeout);
+ if (ret)
+ goto revert5;
+
+ /* Store suspend state */
+ pm_state->suspend_state = state;
+
+ /* Enable IRQ wake */
+ ret = priv->sbus_ops->power_mgmt(priv->sbus_priv, true);
+ if (ret) {
+ wiphy_err(priv->hw->wiphy,
+ "PM request failed: %d. WoW is disabled.\n", ret);
+ cw1200_wow_resume(hw);
+ return -EBUSY;
+ }
+
+ /* Force resume if event is coming from the device. */
+ if (atomic_read(&priv->bh_rx)) {
+ cw1200_wow_resume(hw);
+ return -EAGAIN;
+ }
+
+ return 0;
+
+revert5:
+ WARN_ON(cw1200_bh_resume(priv));
+revert4:
+ cw1200_resume_work(priv, &priv->bss_loss_work,
+ state->bss_loss_tmo);
+ cw1200_resume_work(priv, &priv->join_timeout,
+ state->join_tmo);
+ cw1200_resume_work(priv, &priv->scan.probe_work,
+ state->direct_probe);
+ cw1200_resume_work(priv, &priv->link_id_gc_work,
+ state->link_id_gc);
+ kfree(state);
+revert3:
+ wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off);
+ wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off);
+revert2:
+ wsm_unlock_tx(priv);
+ up(&priv->scan.lock);
+revert1:
+ mutex_unlock(&priv->conf_mutex);
+ return -EBUSY;
+}
+
+int cw1200_wow_resume(struct ieee80211_hw *hw)
+{
+ struct cw1200_common *priv = hw->priv;
+ struct cw1200_pm_state *pm_state = &priv->pm_state;
+ struct cw1200_suspend_state *state;
+
+ state = pm_state->suspend_state;
+ pm_state->suspend_state = NULL;
+
+ /* Disable IRQ wake */
+ priv->sbus_ops->power_mgmt(priv->sbus_priv, false);
+
+ /* Scan.lock must be released before BH is resumed other way
+ * in case when BSS_LOST command arrived the processing of the
+ * command will be delayed.
+ */
+ up(&priv->scan.lock);
+
+ /* Resume BH thread */
+ WARN_ON(cw1200_bh_resume(priv));
+
+ /* Restores previous PS mode */
+ if (!priv->vif->p2p && priv->join_status == CW1200_JOIN_STATUS_STA) {
+ priv->powersave_mode.mode = state->prev_ps_mode;
+ cw1200_set_pm(priv, &priv->powersave_mode);
+ }
+
+ if (state->beacon_skipping) {
+ wsm_set_beacon_wakeup_period(priv, priv->beacon_int *
+ priv->join_dtim_period >
+ MAX_BEACON_SKIP_TIME_MS ? 1 :
+ priv->join_dtim_period, 0);
+ state->beacon_skipping = false;
+ }
+
+ /* Resume delayed work */
+ cw1200_resume_work(priv, &priv->bss_loss_work,
+ state->bss_loss_tmo);
+ cw1200_resume_work(priv, &priv->join_timeout,
+ state->join_tmo);
+ cw1200_resume_work(priv, &priv->scan.probe_work,
+ state->direct_probe);
+ cw1200_resume_work(priv, &priv->link_id_gc_work,
+ state->link_id_gc);
+
+ /* Remove UDP port filter */
+ wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off);
+
+ /* Remove ethernet frame type filter */
+ wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off);
+
+ /* Unlock datapath */
+ wsm_unlock_tx(priv);
+
+ /* Unlock configuration mutex */
+ mutex_unlock(&priv->conf_mutex);
+
+ /* Free memory */
+ kfree(state);
+
+ return 0;
+}
diff --git a/drivers/net/wireless/cw1200/pm.h b/drivers/net/wireless/cw1200/pm.h
new file mode 100644
index 00000000000..516d9671dd1
--- /dev/null
+++ b/drivers/net/wireless/cw1200/pm.h
@@ -0,0 +1,38 @@
+/*
+ * Mac80211 power management interface for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2011, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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.
+ */
+
+#ifndef PM_H_INCLUDED
+#define PM_H_INCLUDED
+
+/* ******************************************************************** */
+/* mac80211 API */
+
+/* extern */ struct cw1200_common;
+/* private */ struct cw1200_suspend_state;
+
+struct cw1200_pm_state {
+ struct cw1200_suspend_state *suspend_state;
+ struct timer_list stay_awake;
+ struct platform_device *pm_dev;
+ spinlock_t lock; /* Protect access */
+};
+
+int cw1200_pm_init(struct cw1200_pm_state *pm,
+ struct cw1200_common *priv);
+void cw1200_pm_deinit(struct cw1200_pm_state *pm);
+void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
+ unsigned long tmo);
+int cw1200_wow_suspend(struct ieee80211_hw *hw,
+ struct cfg80211_wowlan *wowlan);
+int cw1200_wow_resume(struct ieee80211_hw *hw);
+int cw1200_can_suspend(struct cw1200_common *priv);
+
+#endif
diff --git a/drivers/net/wireless/cw1200/queue.c b/drivers/net/wireless/cw1200/queue.c
new file mode 100644
index 00000000000..8510454d5db
--- /dev/null
+++ b/drivers/net/wireless/cw1200/queue.c
@@ -0,0 +1,583 @@
+/*
+ * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <net/mac80211.h>
+#include <linux/sched.h>
+#include "queue.h"
+#include "cw1200.h"
+#include "debug.h"
+
+/* private */ struct cw1200_queue_item
+{
+ struct list_head head;
+ struct sk_buff *skb;
+ u32 packet_id;
+ unsigned long queue_timestamp;
+ unsigned long xmit_timestamp;
+ struct cw1200_txpriv txpriv;
+ u8 generation;
+};
+
+static inline void __cw1200_queue_lock(struct cw1200_queue *queue)
+{
+ struct cw1200_queue_stats *stats = queue->stats;
+ if (queue->tx_locked_cnt++ == 0) {
+ pr_debug("[TX] Queue %d is locked.\n",
+ queue->queue_id);
+ ieee80211_stop_queue(stats->priv->hw, queue->queue_id);
+ }
+}
+
+static inline void __cw1200_queue_unlock(struct cw1200_queue *queue)
+{
+ struct cw1200_queue_stats *stats = queue->stats;
+ BUG_ON(!queue->tx_locked_cnt);
+ if (--queue->tx_locked_cnt == 0) {
+ pr_debug("[TX] Queue %d is unlocked.\n",
+ queue->queue_id);
+ ieee80211_wake_queue(stats->priv->hw, queue->queue_id);
+ }
+}
+
+static inline void cw1200_queue_parse_id(u32 packet_id, u8 *queue_generation,
+ u8 *queue_id, u8 *item_generation,
+ u8 *item_id)
+{
+ *item_id = (packet_id >> 0) & 0xFF;
+ *item_generation = (packet_id >> 8) & 0xFF;
+ *queue_id = (packet_id >> 16) & 0xFF;
+ *queue_generation = (packet_id >> 24) & 0xFF;
+}
+
+static inline u32 cw1200_queue_mk_packet_id(u8 queue_generation, u8 queue_id,
+ u8 item_generation, u8 item_id)
+{
+ return ((u32)item_id << 0) |
+ ((u32)item_generation << 8) |
+ ((u32)queue_id << 16) |
+ ((u32)queue_generation << 24);
+}
+
+static void cw1200_queue_post_gc(struct cw1200_queue_stats *stats,
+ struct list_head *gc_list)
+{
+ struct cw1200_queue_item *item, *tmp;
+
+ list_for_each_entry_safe(item, tmp, gc_list, head) {
+ list_del(&item->head);
+ stats->skb_dtor(stats->priv, item->skb, &item->txpriv);
+ kfree(item);
+ }
+}
+
+static void cw1200_queue_register_post_gc(struct list_head *gc_list,
+ struct cw1200_queue_item *item)
+{
+ struct cw1200_queue_item *gc_item;
+ gc_item = kmalloc(sizeof(struct cw1200_queue_item),
+ GFP_ATOMIC);
+ BUG_ON(!gc_item);
+ memcpy(gc_item, item, sizeof(struct cw1200_queue_item));
+ list_add_tail(&gc_item->head, gc_list);
+}
+
+static void __cw1200_queue_gc(struct cw1200_queue *queue,
+ struct list_head *head,
+ bool unlock)
+{
+ struct cw1200_queue_stats *stats = queue->stats;
+ struct cw1200_queue_item *item = NULL, *tmp;
+ bool wakeup_stats = false;
+
+ list_for_each_entry_safe(item, tmp, &queue->queue, head) {
+ if (jiffies - item->queue_timestamp < queue->ttl)
+ break;
+ --queue->num_queued;
+ --queue->link_map_cache[item->txpriv.link_id];
+ spin_lock_bh(&stats->lock);
+ --stats->num_queued;
+ if (!--stats->link_map_cache[item->txpriv.link_id])
+ wakeup_stats = true;
+ spin_unlock_bh(&stats->lock);
+ cw1200_debug_tx_ttl(stats->priv);
+ cw1200_queue_register_post_gc(head, item);
+ item->skb = NULL;
+ list_move_tail(&item->head, &queue->free_pool);
+ }
+
+ if (wakeup_stats)
+ wake_up(&stats->wait_link_id_empty);
+
+ if (queue->overfull) {
+ if (queue->num_queued <= (queue->capacity >> 1)) {
+ queue->overfull = false;
+ if (unlock)
+ __cw1200_queue_unlock(queue);
+ } else if (item) {
+ unsigned long tmo = item->queue_timestamp + queue->ttl;
+ mod_timer(&queue->gc, tmo);
+ cw1200_pm_stay_awake(&stats->priv->pm_state,
+ tmo - jiffies);
+ }
+ }
+}
+
+static void cw1200_queue_gc(unsigned long arg)
+{
+ LIST_HEAD(list);
+ struct cw1200_queue *queue =
+ (struct cw1200_queue *)arg;
+
+ spin_lock_bh(&queue->lock);
+ __cw1200_queue_gc(queue, &list, true);
+ spin_unlock_bh(&queue->lock);
+ cw1200_queue_post_gc(queue->stats, &list);
+}
+
+int cw1200_queue_stats_init(struct cw1200_queue_stats *stats,
+ size_t map_capacity,
+ cw1200_queue_skb_dtor_t skb_dtor,
+ struct cw1200_common *priv)
+{
+ memset(stats, 0, sizeof(*stats));
+ stats->map_capacity = map_capacity;
+ stats->skb_dtor = skb_dtor;
+ stats->priv = priv;
+ spin_lock_init(&stats->lock);
+ init_waitqueue_head(&stats->wait_link_id_empty);
+
+ stats->link_map_cache = kzalloc(sizeof(int) * map_capacity,
+ GFP_KERNEL);
+ if (!stats->link_map_cache)
+ return -ENOMEM;
+
+ return 0;
+}
+
+int cw1200_queue_init(struct cw1200_queue *queue,
+ struct cw1200_queue_stats *stats,
+ u8 queue_id,
+ size_t capacity,
+ unsigned long ttl)
+{
+ size_t i;
+
+ memset(queue, 0, sizeof(*queue));
+ queue->stats = stats;
+ queue->capacity = capacity;
+ queue->queue_id = queue_id;
+ queue->ttl = ttl;
+ INIT_LIST_HEAD(&queue->queue);
+ INIT_LIST_HEAD(&queue->pending);
+ INIT_LIST_HEAD(&queue->free_pool);
+ spin_lock_init(&queue->lock);
+ init_timer(&queue->gc);
+ queue->gc.data = (unsigned long)queue;
+ queue->gc.function = cw1200_queue_gc;
+
+ queue->pool = kzalloc(sizeof(struct cw1200_queue_item) * capacity,
+ GFP_KERNEL);
+ if (!queue->pool)
+ return -ENOMEM;
+
+ queue->link_map_cache = kzalloc(sizeof(int) * stats->map_capacity,
+ GFP_KERNEL);
+ if (!queue->link_map_cache) {
+ kfree(queue->pool);
+ queue->pool = NULL;
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < capacity; ++i)
+ list_add_tail(&queue->pool[i].head, &queue->free_pool);
+
+ return 0;
+}
+
+int cw1200_queue_clear(struct cw1200_queue *queue)
+{
+ int i;
+ LIST_HEAD(gc_list);
+ struct cw1200_queue_stats *stats = queue->stats;
+ struct cw1200_queue_item *item, *tmp;
+
+ spin_lock_bh(&queue->lock);
+ queue->generation++;
+ list_splice_tail_init(&queue->queue, &queue->pending);
+ list_for_each_entry_safe(item, tmp, &queue->pending, head) {
+ WARN_ON(!item->skb);
+ cw1200_queue_register_post_gc(&gc_list, item);
+ item->skb = NULL;
+ list_move_tail(&item->head, &queue->free_pool);
+ }
+ queue->num_queued = 0;
+ queue->num_pending = 0;
+
+ spin_lock_bh(&stats->lock);
+ for (i = 0; i < stats->map_capacity; ++i) {
+ stats->num_queued -= queue->link_map_cache[i];
+ stats->link_map_cache[i] -= queue->link_map_cache[i];
+ queue->link_map_cache[i] = 0;
+ }
+ spin_unlock_bh(&stats->lock);
+ if (queue->overfull) {
+ queue->overfull = false;
+ __cw1200_queue_unlock(queue);
+ }
+ spin_unlock_bh(&queue->lock);
+ wake_up(&stats->wait_link_id_empty);
+ cw1200_queue_post_gc(stats, &gc_list);
+ return 0;
+}
+
+void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats)
+{
+ kfree(stats->link_map_cache);
+ stats->link_map_cache = NULL;
+}
+
+void cw1200_queue_deinit(struct cw1200_queue *queue)
+{
+ cw1200_queue_clear(queue);
+ del_timer_sync(&queue->gc);
+ INIT_LIST_HEAD(&queue->free_pool);
+ kfree(queue->pool);
+ kfree(queue->link_map_cache);
+ queue->pool = NULL;
+ queue->link_map_cache = NULL;
+ queue->capacity = 0;
+}
+
+size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue,
+ u32 link_id_map)
+{
+ size_t ret;
+ int i, bit;
+ size_t map_capacity = queue->stats->map_capacity;
+
+ if (!link_id_map)
+ return 0;
+
+ spin_lock_bh(&queue->lock);
+ if (link_id_map == (u32)-1) {
+ ret = queue->num_queued - queue->num_pending;
+ } else {
+ ret = 0;
+ for (i = 0, bit = 1; i < map_capacity; ++i, bit <<= 1) {
+ if (link_id_map & bit)
+ ret += queue->link_map_cache[i];
+ }
+ }
+ spin_unlock_bh(&queue->lock);
+ return ret;
+}
+
+int cw1200_queue_put(struct cw1200_queue *queue,
+ struct sk_buff *skb,
+ struct cw1200_txpriv *txpriv)
+{
+ int ret = 0;
+ LIST_HEAD(gc_list);
+ struct cw1200_queue_stats *stats = queue->stats;
+
+ if (txpriv->link_id >= queue->stats->map_capacity)
+ return -EINVAL;
+
+ spin_lock_bh(&queue->lock);
+ if (!WARN_ON(list_empty(&queue->free_pool))) {
+ struct cw1200_queue_item *item = list_first_entry(
+ &queue->free_pool, struct cw1200_queue_item, head);
+ BUG_ON(item->skb);
+
+ list_move_tail(&item->head, &queue->queue);
+ item->skb = skb;
+ item->txpriv = *txpriv;
+ item->generation = 0;
+ item->packet_id = cw1200_queue_mk_packet_id(queue->generation,
+ queue->queue_id,
+ item->generation,
+ item - queue->pool);
+ item->queue_timestamp = jiffies;
+
+ ++queue->num_queued;
+ ++queue->link_map_cache[txpriv->link_id];
+
+ spin_lock_bh(&stats->lock);
+ ++stats->num_queued;
+ ++stats->link_map_cache[txpriv->link_id];
+ spin_unlock_bh(&stats->lock);
+
+ /* TX may happen in parallel sometimes.
+ * Leave extra queue slots so we don't overflow.
+ */
+ if (queue->overfull == false &&
+ queue->num_queued >=
+ (queue->capacity - (num_present_cpus() - 1))) {
+ queue->overfull = true;
+ __cw1200_queue_lock(queue);
+ mod_timer(&queue->gc, jiffies);
+ }
+ } else {
+ ret = -ENOENT;
+ }
+ spin_unlock_bh(&queue->lock);
+ return ret;
+}
+
+int cw1200_queue_get(struct cw1200_queue *queue,
+ u32 link_id_map,
+ struct wsm_tx **tx,
+ struct ieee80211_tx_info **tx_info,
+ const struct cw1200_txpriv **txpriv)
+{
+ int ret = -ENOENT;
+ struct cw1200_queue_item *item;
+ struct cw1200_queue_stats *stats = queue->stats;
+ bool wakeup_stats = false;
+
+ spin_lock_bh(&queue->lock);
+ list_for_each_entry(item, &queue->queue, head) {
+ if (link_id_map & BIT(item->txpriv.link_id)) {
+ ret = 0;
+ break;
+ }
+ }
+
+ if (!WARN_ON(ret)) {
+ *tx = (struct wsm_tx *)item->skb->data;
+ *tx_info = IEEE80211_SKB_CB(item->skb);
+ *txpriv = &item->txpriv;
+ (*tx)->packet_id = __cpu_to_le32(item->packet_id);
+ list_move_tail(&item->head, &queue->pending);
+ ++queue->num_pending;
+ --queue->link_map_cache[item->txpriv.link_id];
+ item->xmit_timestamp = jiffies;
+
+ spin_lock_bh(&stats->lock);
+ --stats->num_queued;
+ if (!--stats->link_map_cache[item->txpriv.link_id])
+ wakeup_stats = true;
+ spin_unlock_bh(&stats->lock);
+ }
+ spin_unlock_bh(&queue->lock);
+ if (wakeup_stats)
+ wake_up(&stats->wait_link_id_empty);
+ return ret;
+}
+
+int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id)
+{
+ int ret = 0;
+ u8 queue_generation, queue_id, item_generation, item_id;
+ struct cw1200_queue_item *item;
+ struct cw1200_queue_stats *stats = queue->stats;
+
+ cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
+ &item_generation, &item_id);
+
+ item = &queue->pool[item_id];
+
+ spin_lock_bh(&queue->lock);
+ BUG_ON(queue_id != queue->queue_id);
+ if (queue_generation != queue->generation) {
+ ret = -ENOENT;
+ } else if (item_id >= (unsigned) queue->capacity) {
+ WARN_ON(1);
+ ret = -EINVAL;
+ } else if (item->generation != item_generation) {
+ WARN_ON(1);
+ ret = -ENOENT;
+ } else {
+ --queue->num_pending;
+ ++queue->link_map_cache[item->txpriv.link_id];
+
+ spin_lock_bh(&stats->lock);
+ ++stats->num_queued;
+ ++stats->link_map_cache[item->txpriv.link_id];
+ spin_unlock_bh(&stats->lock);
+
+ item->generation = ++item_generation;
+ item->packet_id = cw1200_queue_mk_packet_id(queue_generation,
+ queue_id,
+ item_generation,
+ item_id);
+ list_move(&item->head, &queue->queue);
+ }
+ spin_unlock_bh(&queue->lock);
+ return ret;
+}
+
+int cw1200_queue_requeue_all(struct cw1200_queue *queue)
+{
+ struct cw1200_queue_item *item, *tmp;
+ struct cw1200_queue_stats *stats = queue->stats;
+ spin_lock_bh(&queue->lock);
+
+ list_for_each_entry_safe_reverse(item, tmp, &queue->pending, head) {
+ --queue->num_pending;
+ ++queue->link_map_cache[item->txpriv.link_id];
+
+ spin_lock_bh(&stats->lock);
+ ++stats->num_queued;
+ ++stats->link_map_cache[item->txpriv.link_id];
+ spin_unlock_bh(&stats->lock);
+
+ ++item->generation;
+ item->packet_id = cw1200_queue_mk_packet_id(queue->generation,
+ queue->queue_id,
+ item->generation,
+ item - queue->pool);
+ list_move(&item->head, &queue->queue);
+ }
+ spin_unlock_bh(&queue->lock);
+
+ return 0;
+}
+
+int cw1200_queue_remove(struct cw1200_queue *queue, u32 packet_id)
+{
+ int ret = 0;
+ u8 queue_generation, queue_id, item_generation, item_id;
+ struct cw1200_queue_item *item;
+ struct cw1200_queue_stats *stats = queue->stats;
+ struct sk_buff *gc_skb = NULL;
+ struct cw1200_txpriv gc_txpriv;
+
+ cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
+ &item_generation, &item_id);
+
+ item = &queue->pool[item_id];
+
+ spin_lock_bh(&queue->lock);
+ BUG_ON(queue_id != queue->queue_id);
+ if (queue_generation != queue->generation) {
+ ret = -ENOENT;
+ } else if (item_id >= (unsigned) queue->capacity) {
+ WARN_ON(1);
+ ret = -EINVAL;
+ } else if (item->generation != item_generation) {
+ WARN_ON(1);
+ ret = -ENOENT;
+ } else {
+ gc_txpriv = item->txpriv;
+ gc_skb = item->skb;
+ item->skb = NULL;
+ --queue->num_pending;
+ --queue->num_queued;
+ ++queue->num_sent;
+ ++item->generation;
+ /* Do not use list_move_tail here, but list_move:
+ * try to utilize cache row.
+ */
+ list_move(&item->head, &queue->free_pool);
+
+ if (queue->overfull &&
+ (queue->num_queued <= (queue->capacity >> 1))) {
+ queue->overfull = false;
+ __cw1200_queue_unlock(queue);
+ }
+ }
+ spin_unlock_bh(&queue->lock);
+
+ if (gc_skb)
+ stats->skb_dtor(stats->priv, gc_skb, &gc_txpriv);
+
+ return ret;
+}
+
+int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id,
+ struct sk_buff **skb,
+ const struct cw1200_txpriv **txpriv)
+{
+ int ret = 0;
+ u8 queue_generation, queue_id, item_generation, item_id;
+ struct cw1200_queue_item *item;
+ cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
+ &item_generation, &item_id);
+
+ item = &queue->pool[item_id];
+
+ spin_lock_bh(&queue->lock);
+ BUG_ON(queue_id != queue->queue_id);
+ if (queue_generation != queue->generation) {
+ ret = -ENOENT;
+ } else if (item_id >= (unsigned) queue->capacity) {
+ WARN_ON(1);
+ ret = -EINVAL;
+ } else if (item->generation != item_generation) {
+ WARN_ON(1);
+ ret = -ENOENT;
+ } else {
+ *skb = item->skb;
+ *txpriv = &item->txpriv;
+ }
+ spin_unlock_bh(&queue->lock);
+ return ret;
+}
+
+void cw1200_queue_lock(struct cw1200_queue *queue)
+{
+ spin_lock_bh(&queue->lock);
+ __cw1200_queue_lock(queue);
+ spin_unlock_bh(&queue->lock);
+}
+
+void cw1200_queue_unlock(struct cw1200_queue *queue)
+{
+ spin_lock_bh(&queue->lock);
+ __cw1200_queue_unlock(queue);
+ spin_unlock_bh(&queue->lock);
+}
+
+bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue,
+ unsigned long *timestamp,
+ u32 pending_frame_id)
+{
+ struct cw1200_queue_item *item;
+ bool ret;
+
+ spin_lock_bh(&queue->lock);
+ ret = !list_empty(&queue->pending);
+ if (ret) {
+ list_for_each_entry(item, &queue->pending, head) {
+ if (item->packet_id != pending_frame_id)
+ if (time_before(item->xmit_timestamp,
+ *timestamp))
+ *timestamp = item->xmit_timestamp;
+ }
+ }
+ spin_unlock_bh(&queue->lock);
+ return ret;
+}
+
+bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats,
+ u32 link_id_map)
+{
+ bool empty = true;
+
+ spin_lock_bh(&stats->lock);
+ if (link_id_map == (u32)-1) {
+ empty = stats->num_queued == 0;
+ } else {
+ int i;
+ for (i = 0; i < stats->map_capacity; ++i) {
+ if (link_id_map & BIT(i)) {
+ if (stats->link_map_cache[i]) {
+ empty = false;
+ break;
+ }
+ }
+ }
+ }
+ spin_unlock_bh(&stats->lock);
+
+ return empty;
+}
diff --git a/drivers/net/wireless/cw1200/queue.h b/drivers/net/wireless/cw1200/queue.h
new file mode 100644
index 00000000000..119f9c79c14
--- /dev/null
+++ b/drivers/net/wireless/cw1200/queue.h
@@ -0,0 +1,116 @@
+/*
+ * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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.
+ */
+
+#ifndef CW1200_QUEUE_H_INCLUDED
+#define CW1200_QUEUE_H_INCLUDED
+
+/* private */ struct cw1200_queue_item;
+
+/* extern */ struct sk_buff;
+/* extern */ struct wsm_tx;
+/* extern */ struct cw1200_common;
+/* extern */ struct ieee80211_tx_queue_stats;
+/* extern */ struct cw1200_txpriv;
+
+/* forward */ struct cw1200_queue_stats;
+
+typedef void (*cw1200_queue_skb_dtor_t)(struct cw1200_common *priv,
+ struct sk_buff *skb,
+ const struct cw1200_txpriv *txpriv);
+
+struct cw1200_queue {
+ struct cw1200_queue_stats *stats;
+ size_t capacity;
+ size_t num_queued;
+ size_t num_pending;
+ size_t num_sent;
+ struct cw1200_queue_item *pool;
+ struct list_head queue;
+ struct list_head free_pool;
+ struct list_head pending;
+ int tx_locked_cnt;
+ int *link_map_cache;
+ bool overfull;
+ spinlock_t lock; /* Protect queue entry */
+ u8 queue_id;
+ u8 generation;
+ struct timer_list gc;
+ unsigned long ttl;
+};
+
+struct cw1200_queue_stats {
+ spinlock_t lock; /* Protect stats entry */
+ int *link_map_cache;
+ int num_queued;
+ size_t map_capacity;
+ wait_queue_head_t wait_link_id_empty;
+ cw1200_queue_skb_dtor_t skb_dtor;
+ struct cw1200_common *priv;
+};
+
+struct cw1200_txpriv {
+ u8 link_id;
+ u8 raw_link_id;
+ u8 tid;
+ u8 rate_id;
+ u8 offset;
+};
+
+int cw1200_queue_stats_init(struct cw1200_queue_stats *stats,
+ size_t map_capacity,
+ cw1200_queue_skb_dtor_t skb_dtor,
+ struct cw1200_common *priv);
+int cw1200_queue_init(struct cw1200_queue *queue,
+ struct cw1200_queue_stats *stats,
+ u8 queue_id,
+ size_t capacity,
+ unsigned long ttl);
+int cw1200_queue_clear(struct cw1200_queue *queue);
+void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats);
+void cw1200_queue_deinit(struct cw1200_queue *queue);
+
+size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue,
+ u32 link_id_map);
+int cw1200_queue_put(struct cw1200_queue *queue,
+ struct sk_buff *skb,
+ struct cw1200_txpriv *txpriv);
+int cw1200_queue_get(struct cw1200_queue *queue,
+ u32 link_id_map,
+ struct wsm_tx **tx,
+ struct ieee80211_tx_info **tx_info,
+ const struct cw1200_txpriv **txpriv);
+int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id);
+int cw1200_queue_requeue_all(struct cw1200_queue *queue);
+int cw1200_queue_remove(struct cw1200_queue *queue,
+ u32 packet_id);
+int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id,
+ struct sk_buff **skb,
+ const struct cw1200_txpriv **txpriv);
+void cw1200_queue_lock(struct cw1200_queue *queue);
+void cw1200_queue_unlock(struct cw1200_queue *queue);
+bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue,
+ unsigned long *timestamp,
+ u32 pending_frame_id);
+
+bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats,
+ u32 link_id_map);
+
+static inline u8 cw1200_queue_get_queue_id(u32 packet_id)
+{
+ return (packet_id >> 16) & 0xFF;
+}
+
+static inline u8 cw1200_queue_get_generation(u32 packet_id)
+{
+ return (packet_id >> 8) & 0xFF;
+}
+
+#endif /* CW1200_QUEUE_H_INCLUDED */
diff --git a/drivers/net/wireless/cw1200/sbus.h b/drivers/net/wireless/cw1200/sbus.h
new file mode 100644
index 00000000000..603fd25eaa4
--- /dev/null
+++ b/drivers/net/wireless/cw1200/sbus.h
@@ -0,0 +1,37 @@
+/*
+ * Common sbus abstraction layer interface for cw1200 wireless driver
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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.
+ */
+
+#ifndef CW1200_SBUS_H
+#define CW1200_SBUS_H
+
+/*
+ * sbus priv forward definition.
+ * Implemented and instantiated in particular modules.
+ */
+struct sbus_priv;
+
+void cw1200_irq_handler(struct cw1200_common *priv);
+
+/* This MUST be wrapped with sbus_ops->lock/unlock! */
+int __cw1200_irq_enable(struct cw1200_common *priv, int enable);
+
+struct sbus_ops {
+ int (*sbus_memcpy_fromio)(struct sbus_priv *self, unsigned int addr,
+ void *dst, int count);
+ int (*sbus_memcpy_toio)(struct sbus_priv *self, unsigned int addr,
+ const void *src, int count);
+ void (*lock)(struct sbus_priv *self);
+ void (*unlock)(struct sbus_priv *self);
+ size_t (*align_size)(struct sbus_priv *self, size_t size);
+ int (*power_mgmt)(struct sbus_priv *self, bool suspend);
+};
+
+#endif /* CW1200_SBUS_H */
diff --git a/drivers/net/wireless/cw1200/scan.c b/drivers/net/wireless/cw1200/scan.c
new file mode 100644
index 00000000000..ee3c19037aa
--- /dev/null
+++ b/drivers/net/wireless/cw1200/scan.c
@@ -0,0 +1,461 @@
+/*
+ * Scan implementation for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/sched.h>
+#include "cw1200.h"
+#include "scan.h"
+#include "sta.h"
+#include "pm.h"
+
+static void cw1200_scan_restart_delayed(struct cw1200_common *priv);
+
+static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan)
+{
+ int ret, i;
+ int tmo = 2000;
+
+ switch (priv->join_status) {
+ case CW1200_JOIN_STATUS_PRE_STA:
+ case CW1200_JOIN_STATUS_JOINING:
+ return -EBUSY;
+ default:
+ break;
+ }
+
+ wiphy_dbg(priv->hw->wiphy, "[SCAN] hw req, type %d, %d channels, flags: 0x%x.\n",
+ scan->type, scan->num_channels, scan->flags);
+
+ for (i = 0; i < scan->num_channels; ++i)
+ tmo += scan->ch[i].max_chan_time + 10;
+
+ cancel_delayed_work_sync(&priv->clear_recent_scan_work);
+ atomic_set(&priv->scan.in_progress, 1);
+ atomic_set(&priv->recent_scan, 1);
+ cw1200_pm_stay_awake(&priv->pm_state, tmo * HZ / 1000);
+ queue_delayed_work(priv->workqueue, &priv->scan.timeout,
+ tmo * HZ / 1000);
+ ret = wsm_scan(priv, scan);
+ if (ret) {
+ atomic_set(&priv->scan.in_progress, 0);
+ cancel_delayed_work_sync(&priv->scan.timeout);
+ cw1200_scan_restart_delayed(priv);
+ }
+ return ret;
+}
+
+int cw1200_hw_scan(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_scan_request *req)
+{
+ struct cw1200_common *priv = hw->priv;
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
+ };
+ int i, ret;
+
+ if (!priv->vif)
+ return -EINVAL;
+
+ /* Scan when P2P_GO corrupt firmware MiniAP mode */
+ if (priv->join_status == CW1200_JOIN_STATUS_AP)
+ return -EOPNOTSUPP;
+
+ if (req->n_ssids == 1 && !req->ssids[0].ssid_len)
+ req->n_ssids = 0;
+
+ wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n",
+ req->n_ssids);
+
+ if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS)
+ return -EINVAL;
+
+ frame.skb = ieee80211_probereq_get(hw, priv->vif, NULL, 0,
+ req->ie_len);
+ if (!frame.skb)
+ return -ENOMEM;
+
+ if (req->ie_len)
+ memcpy(skb_put(frame.skb, req->ie_len), req->ie, req->ie_len);
+
+ /* will be unlocked in cw1200_scan_work() */
+ down(&priv->scan.lock);
+ mutex_lock(&priv->conf_mutex);
+
+ ret = wsm_set_template_frame(priv, &frame);
+ if (!ret) {
+ /* Host want to be the probe responder. */
+ ret = wsm_set_probe_responder(priv, true);
+ }
+ if (ret) {
+ mutex_unlock(&priv->conf_mutex);
+ up(&priv->scan.lock);
+ dev_kfree_skb(frame.skb);
+ return ret;
+ }
+
+ wsm_lock_tx(priv);
+
+ BUG_ON(priv->scan.req);
+ priv->scan.req = req;
+ priv->scan.n_ssids = 0;
+ priv->scan.status = 0;
+ priv->scan.begin = &req->channels[0];
+ priv->scan.curr = priv->scan.begin;
+ priv->scan.end = &req->channels[req->n_channels];
+ priv->scan.output_power = priv->output_power;
+
+ for (i = 0; i < req->n_ssids; ++i) {
+ struct wsm_ssid *dst = &priv->scan.ssids[priv->scan.n_ssids];
+ memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid));
+ dst->length = req->ssids[i].ssid_len;
+ ++priv->scan.n_ssids;
+ }
+
+ mutex_unlock(&priv->conf_mutex);
+
+ if (frame.skb)
+ dev_kfree_skb(frame.skb);
+ queue_work(priv->workqueue, &priv->scan.work);
+ return 0;
+}
+
+void cw1200_scan_work(struct work_struct *work)
+{
+ struct cw1200_common *priv = container_of(work, struct cw1200_common,
+ scan.work);
+ struct ieee80211_channel **it;
+ struct wsm_scan scan = {
+ .type = WSM_SCAN_TYPE_FOREGROUND,
+ .flags = WSM_SCAN_FLAG_SPLIT_METHOD,
+ };
+ bool first_run = (priv->scan.begin == priv->scan.curr &&
+ priv->scan.begin != priv->scan.end);
+ int i;
+
+ if (first_run) {
+ /* Firmware gets crazy if scan request is sent
+ * when STA is joined but not yet associated.
+ * Force unjoin in this case.
+ */
+ if (cancel_delayed_work_sync(&priv->join_timeout) > 0)
+ cw1200_join_timeout(&priv->join_timeout.work);
+ }
+
+ mutex_lock(&priv->conf_mutex);
+
+ if (first_run) {
+ if (priv->join_status == CW1200_JOIN_STATUS_STA &&
+ !(priv->powersave_mode.mode & WSM_PSM_PS)) {
+ struct wsm_set_pm pm = priv->powersave_mode;
+ pm.mode = WSM_PSM_PS;
+ cw1200_set_pm(priv, &pm);
+ } else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
+ /* FW bug: driver has to restart p2p-dev mode
+ * after scan
+ */
+ cw1200_disable_listening(priv);
+ }
+ }
+
+ if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) {
+ if (priv->scan.output_power != priv->output_power)
+ wsm_set_output_power(priv, priv->output_power * 10);
+ if (priv->join_status == CW1200_JOIN_STATUS_STA &&
+ !(priv->powersave_mode.mode & WSM_PSM_PS))
+ cw1200_set_pm(priv, &priv->powersave_mode);
+
+ if (priv->scan.status < 0)
+ wiphy_dbg(priv->hw->wiphy, "[SCAN] Scan failed (%d).\n",
+ priv->scan.status);
+ else if (priv->scan.req)
+ wiphy_dbg(priv->hw->wiphy,
+ "[SCAN] Scan completed.\n");
+ else
+ wiphy_dbg(priv->hw->wiphy,
+ "[SCAN] Scan canceled.\n");
+
+ priv->scan.req = NULL;
+ cw1200_scan_restart_delayed(priv);
+ wsm_unlock_tx(priv);
+ mutex_unlock(&priv->conf_mutex);
+ ieee80211_scan_completed(priv->hw, priv->scan.status ? 1 : 0);
+ up(&priv->scan.lock);
+ return;
+ } else {
+ struct ieee80211_channel *first = *priv->scan.curr;
+ for (it = priv->scan.curr + 1, i = 1;
+ it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS;
+ ++it, ++i) {
+ if ((*it)->band != first->band)
+ break;
+ if (((*it)->flags ^ first->flags) &
+ IEEE80211_CHAN_PASSIVE_SCAN)
+ break;
+ if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
+ (*it)->max_power != first->max_power)
+ break;
+ }
+ scan.band = first->band;
+
+ if (priv->scan.req->no_cck)
+ scan.max_tx_rate = WSM_TRANSMIT_RATE_6;
+ else
+ scan.max_tx_rate = WSM_TRANSMIT_RATE_1;
+ scan.num_probes =
+ (first->flags & IEEE80211_CHAN_PASSIVE_SCAN) ? 0 : 2;
+ scan.num_ssids = priv->scan.n_ssids;
+ scan.ssids = &priv->scan.ssids[0];
+ scan.num_channels = it - priv->scan.curr;
+ /* TODO: Is it optimal? */
+ scan.probe_delay = 100;
+ /* It is not stated in WSM specification, however
+ * FW team says that driver may not use FG scan
+ * when joined.
+ */
+ if (priv->join_status == CW1200_JOIN_STATUS_STA) {
+ scan.type = WSM_SCAN_TYPE_BACKGROUND;
+ scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
+ }
+ scan.ch = kzalloc(
+ sizeof(struct wsm_scan_ch) * (it - priv->scan.curr),
+ GFP_KERNEL);
+ if (!scan.ch) {
+ priv->scan.status = -ENOMEM;
+ goto fail;
+ }
+ for (i = 0; i < scan.num_channels; ++i) {
+ scan.ch[i].number = priv->scan.curr[i]->hw_value;
+ if (priv->scan.curr[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN) {
+ scan.ch[i].min_chan_time = 50;
+ scan.ch[i].max_chan_time = 100;
+ } else {
+ scan.ch[i].min_chan_time = 10;
+ scan.ch[i].max_chan_time = 25;
+ }
+ }
+ if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
+ priv->scan.output_power != first->max_power) {
+ priv->scan.output_power = first->max_power;
+ wsm_set_output_power(priv,
+ priv->scan.output_power * 10);
+ }
+ priv->scan.status = cw1200_scan_start(priv, &scan);
+ kfree(scan.ch);
+ if (priv->scan.status)
+ goto fail;
+ priv->scan.curr = it;
+ }
+ mutex_unlock(&priv->conf_mutex);
+ return;
+
+fail:
+ priv->scan.curr = priv->scan.end;
+ mutex_unlock(&priv->conf_mutex);
+ queue_work(priv->workqueue, &priv->scan.work);
+ return;
+}
+
+static void cw1200_scan_restart_delayed(struct cw1200_common *priv)
+{
+ /* FW bug: driver has to restart p2p-dev mode after scan. */
+ if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
+ cw1200_enable_listening(priv);
+ cw1200_update_filtering(priv);
+ }
+
+ if (priv->delayed_unjoin) {
+ priv->delayed_unjoin = false;
+ if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(priv);
+ } else if (priv->delayed_link_loss) {
+ wiphy_dbg(priv->hw->wiphy, "[CQM] Requeue BSS loss.\n");
+ priv->delayed_link_loss = 0;
+ cw1200_cqm_bssloss_sm(priv, 1, 0, 0);
+ }
+}
+
+static void cw1200_scan_complete(struct cw1200_common *priv)
+{
+ queue_delayed_work(priv->workqueue, &priv->clear_recent_scan_work, HZ);
+ if (priv->scan.direct_probe) {
+ wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe complete.\n");
+ cw1200_scan_restart_delayed(priv);
+ priv->scan.direct_probe = 0;
+ up(&priv->scan.lock);
+ wsm_unlock_tx(priv);
+ } else {
+ cw1200_scan_work(&priv->scan.work);
+ }
+}
+
+void cw1200_scan_failed_cb(struct cw1200_common *priv)
+{
+ if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+ /* STA is stopped. */
+ return;
+
+ if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) {
+ priv->scan.status = -EIO;
+ queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0);
+ }
+}
+
+
+void cw1200_scan_complete_cb(struct cw1200_common *priv,
+ struct wsm_scan_complete *arg)
+{
+ if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+ /* STA is stopped. */
+ return;
+
+ if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) {
+ priv->scan.status = 1;
+ queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0);
+ }
+}
+
+void cw1200_clear_recent_scan_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common,
+ clear_recent_scan_work.work);
+ atomic_xchg(&priv->recent_scan, 0);
+}
+
+void cw1200_scan_timeout(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, scan.timeout.work);
+ if (atomic_xchg(&priv->scan.in_progress, 0)) {
+ if (priv->scan.status > 0) {
+ priv->scan.status = 0;
+ } else if (!priv->scan.status) {
+ wiphy_warn(priv->hw->wiphy,
+ "Timeout waiting for scan complete notification.\n");
+ priv->scan.status = -ETIMEDOUT;
+ priv->scan.curr = priv->scan.end;
+ wsm_stop_scan(priv);
+ }
+ cw1200_scan_complete(priv);
+ }
+}
+
+void cw1200_probe_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, scan.probe_work.work);
+ u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id);
+ struct cw1200_queue *queue = &priv->tx_queue[queue_id];
+ const struct cw1200_txpriv *txpriv;
+ struct wsm_tx *wsm;
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
+ };
+ struct wsm_ssid ssids[1] = {{
+ .length = 0,
+ } };
+ struct wsm_scan_ch ch[1] = {{
+ .min_chan_time = 0,
+ .max_chan_time = 10,
+ } };
+ struct wsm_scan scan = {
+ .type = WSM_SCAN_TYPE_FOREGROUND,
+ .num_probes = 1,
+ .probe_delay = 0,
+ .num_channels = 1,
+ .ssids = ssids,
+ .ch = ch,
+ };
+ u8 *ies;
+ size_t ies_len;
+ int ret;
+
+ wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe work.\n");
+
+ mutex_lock(&priv->conf_mutex);
+ if (down_trylock(&priv->scan.lock)) {
+ /* Scan is already in progress. Requeue self. */
+ schedule();
+ queue_delayed_work(priv->workqueue,
+ &priv->scan.probe_work, HZ / 10);
+ mutex_unlock(&priv->conf_mutex);
+ return;
+ }
+
+ /* Make sure we still have a pending probe req */
+ if (cw1200_queue_get_skb(queue, priv->pending_frame_id,
+ &frame.skb, &txpriv)) {
+ up(&priv->scan.lock);
+ mutex_unlock(&priv->conf_mutex);
+ wsm_unlock_tx(priv);
+ return;
+ }
+ wsm = (struct wsm_tx *)frame.skb->data;
+ scan.max_tx_rate = wsm->max_tx_rate;
+ scan.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
+ WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
+ if (priv->join_status == CW1200_JOIN_STATUS_STA ||
+ priv->join_status == CW1200_JOIN_STATUS_IBSS) {
+ scan.type = WSM_SCAN_TYPE_BACKGROUND;
+ scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
+ }
+ ch[0].number = priv->channel->hw_value;
+
+ skb_pull(frame.skb, txpriv->offset);
+
+ ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)];
+ ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr);
+
+ if (ies_len) {
+ u8 *ssidie =
+ (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len);
+ if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) {
+ u8 *nextie = &ssidie[2 + ssidie[1]];
+ /* Remove SSID from the IE list. It has to be provided
+ * as a separate argument in cw1200_scan_start call
+ */
+
+ /* Store SSID localy */
+ ssids[0].length = ssidie[1];
+ memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length);
+ scan.num_ssids = 1;
+
+ /* Remove SSID from IE list */
+ ssidie[1] = 0;
+ memmove(&ssidie[2], nextie, &ies[ies_len] - nextie);
+ skb_trim(frame.skb, frame.skb->len - ssids[0].length);
+ }
+ }
+
+ /* FW bug: driver has to restart p2p-dev mode after scan */
+ if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
+ cw1200_disable_listening(priv);
+ ret = wsm_set_template_frame(priv, &frame);
+ priv->scan.direct_probe = 1;
+ if (!ret) {
+ wsm_flush_tx(priv);
+ ret = cw1200_scan_start(priv, &scan);
+ }
+ mutex_unlock(&priv->conf_mutex);
+
+ skb_push(frame.skb, txpriv->offset);
+ if (!ret)
+ IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK;
+ BUG_ON(cw1200_queue_remove(queue, priv->pending_frame_id));
+
+ if (ret) {
+ priv->scan.direct_probe = 0;
+ up(&priv->scan.lock);
+ wsm_unlock_tx(priv);
+ }
+
+ return;
+}
diff --git a/drivers/net/wireless/cw1200/scan.h b/drivers/net/wireless/cw1200/scan.h
new file mode 100644
index 00000000000..5a8296ccfa8
--- /dev/null
+++ b/drivers/net/wireless/cw1200/scan.h
@@ -0,0 +1,56 @@
+/*
+ * Scan interface for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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.
+ */
+
+#ifndef SCAN_H_INCLUDED
+#define SCAN_H_INCLUDED
+
+#include <linux/semaphore.h>
+#include "wsm.h"
+
+/* external */ struct sk_buff;
+/* external */ struct cfg80211_scan_request;
+/* external */ struct ieee80211_channel;
+/* external */ struct ieee80211_hw;
+/* external */ struct work_struct;
+
+struct cw1200_scan {
+ struct semaphore lock;
+ struct work_struct work;
+ struct delayed_work timeout;
+ struct cfg80211_scan_request *req;
+ struct ieee80211_channel **begin;
+ struct ieee80211_channel **curr;
+ struct ieee80211_channel **end;
+ struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS];
+ int output_power;
+ int n_ssids;
+ int status;
+ atomic_t in_progress;
+ /* Direct probe requests workaround */
+ struct delayed_work probe_work;
+ int direct_probe;
+};
+
+int cw1200_hw_scan(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_scan_request *req);
+void cw1200_scan_work(struct work_struct *work);
+void cw1200_scan_timeout(struct work_struct *work);
+void cw1200_clear_recent_scan_work(struct work_struct *work);
+void cw1200_scan_complete_cb(struct cw1200_common *priv,
+ struct wsm_scan_complete *arg);
+void cw1200_scan_failed_cb(struct cw1200_common *priv);
+
+/* ******************************************************************** */
+/* Raw probe requests TX workaround */
+void cw1200_probe_work(struct work_struct *work);
+
+#endif
diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c
new file mode 100644
index 00000000000..679c55f15c6
--- /dev/null
+++ b/drivers/net/wireless/cw1200/sta.c
@@ -0,0 +1,2406 @@
+/*
+ * Mac80211 STA API for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+
+#include "cw1200.h"
+#include "sta.h"
+#include "fwio.h"
+#include "bh.h"
+#include "debug.h"
+
+#ifndef ERP_INFO_BYTE_OFFSET
+#define ERP_INFO_BYTE_OFFSET 2
+#endif
+
+static void cw1200_do_join(struct cw1200_common *priv);
+static void cw1200_do_unjoin(struct cw1200_common *priv);
+
+static int cw1200_upload_beacon(struct cw1200_common *priv);
+static int cw1200_upload_pspoll(struct cw1200_common *priv);
+static int cw1200_upload_null(struct cw1200_common *priv);
+static int cw1200_upload_qosnull(struct cw1200_common *priv);
+static int cw1200_start_ap(struct cw1200_common *priv);
+static int cw1200_update_beaconing(struct cw1200_common *priv);
+static int cw1200_enable_beaconing(struct cw1200_common *priv,
+ bool enable);
+static void __cw1200_sta_notify(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd notify_cmd,
+ int link_id);
+static int __cw1200_flush(struct cw1200_common *priv, bool drop);
+
+static inline void __cw1200_free_event_queue(struct list_head *list)
+{
+ struct cw1200_wsm_event *event, *tmp;
+ list_for_each_entry_safe(event, tmp, list, link) {
+ list_del(&event->link);
+ kfree(event);
+ }
+}
+
+/* ******************************************************************** */
+/* STA API */
+
+int cw1200_start(struct ieee80211_hw *dev)
+{
+ struct cw1200_common *priv = dev->priv;
+ int ret = 0;
+
+ cw1200_pm_stay_awake(&priv->pm_state, HZ);
+
+ mutex_lock(&priv->conf_mutex);
+
+ /* default EDCA */
+ WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47, 0xc8, false);
+ WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94, 0xc8, false);
+ WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0, 0xc8, false);
+ WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0, 0xc8, false);
+ ret = wsm_set_edca_params(priv, &priv->edca);
+ if (ret)
+ goto out;
+
+ ret = cw1200_set_uapsd_param(priv, &priv->edca);
+ if (ret)
+ goto out;
+
+ priv->setbssparams_done = false;
+
+ memcpy(priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN);
+ priv->mode = NL80211_IFTYPE_MONITOR;
+ priv->wep_default_key_id = -1;
+
+ priv->cqm_beacon_loss_count = 10;
+
+ ret = cw1200_setup_mac(priv);
+ if (ret)
+ goto out;
+
+out:
+ mutex_unlock(&priv->conf_mutex);
+ return ret;
+}
+
+void cw1200_stop(struct ieee80211_hw *dev)
+{
+ struct cw1200_common *priv = dev->priv;
+ LIST_HEAD(list);
+ int i;
+
+ wsm_lock_tx(priv);
+
+ while (down_trylock(&priv->scan.lock)) {
+ /* Scan is in progress. Force it to stop. */
+ priv->scan.req = NULL;
+ schedule();
+ }
+ up(&priv->scan.lock);
+
+ cancel_delayed_work_sync(&priv->scan.probe_work);
+ cancel_delayed_work_sync(&priv->scan.timeout);
+ cancel_delayed_work_sync(&priv->clear_recent_scan_work);
+ cancel_delayed_work_sync(&priv->join_timeout);
+ cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
+ cancel_work_sync(&priv->unjoin_work);
+ cancel_delayed_work_sync(&priv->link_id_gc_work);
+ flush_workqueue(priv->workqueue);
+ del_timer_sync(&priv->mcast_timeout);
+ mutex_lock(&priv->conf_mutex);
+ priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+ priv->listening = false;
+
+ spin_lock(&priv->event_queue_lock);
+ list_splice_init(&priv->event_queue, &list);
+ spin_unlock(&priv->event_queue_lock);
+ __cw1200_free_event_queue(&list);
+
+
+ priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+ priv->join_pending = false;
+
+ for (i = 0; i < 4; i++)
+ cw1200_queue_clear(&priv->tx_queue[i]);
+ mutex_unlock(&priv->conf_mutex);
+ tx_policy_clean(priv);
+
+ /* HACK! */
+ if (atomic_xchg(&priv->tx_lock, 1) != 1)
+ pr_debug("[STA] TX is force-unlocked due to stop request.\n");
+
+ wsm_unlock_tx(priv);
+ atomic_xchg(&priv->tx_lock, 0); /* for recovery to work */
+}
+
+static int cw1200_bssloss_mitigation = 1;
+module_param(cw1200_bssloss_mitigation, int, 0644);
+MODULE_PARM_DESC(cw1200_bssloss_mitigation, "BSS Loss mitigation. 0 == disabled, 1 == enabled (default)");
+
+
+void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv,
+ int init, int good, int bad)
+{
+ int tx = 0;
+
+ priv->delayed_link_loss = 0;
+ cancel_work_sync(&priv->bss_params_work);
+
+ pr_debug("[STA] CQM BSSLOSS_SM: state: %d init %d good %d bad: %d txlock: %d uj: %d\n",
+ priv->bss_loss_state,
+ init, good, bad,
+ atomic_read(&priv->tx_lock),
+ priv->delayed_unjoin);
+
+ /* If we have a pending unjoin */
+ if (priv->delayed_unjoin)
+ return;
+
+ if (init) {
+ queue_delayed_work(priv->workqueue,
+ &priv->bss_loss_work,
+ HZ);
+ priv->bss_loss_state = 0;
+
+ /* Skip the confimration procedure in P2P case */
+ if (!priv->vif->p2p && !atomic_read(&priv->tx_lock))
+ tx = 1;
+ } else if (good) {
+ cancel_delayed_work_sync(&priv->bss_loss_work);
+ priv->bss_loss_state = 0;
+ queue_work(priv->workqueue, &priv->bss_params_work);
+ } else if (bad) {
+ /* XXX Should we just keep going until we time out? */
+ if (priv->bss_loss_state < 3)
+ tx = 1;
+ } else {
+ cancel_delayed_work_sync(&priv->bss_loss_work);
+ priv->bss_loss_state = 0;
+ }
+
+ /* Bypass mitigation if it's disabled */
+ if (!cw1200_bssloss_mitigation)
+ tx = 0;
+
+ /* Spit out a NULL packet to our AP if necessary */
+ if (tx) {
+ struct sk_buff *skb;
+
+ priv->bss_loss_state++;
+
+ skb = ieee80211_nullfunc_get(priv->hw, priv->vif);
+ WARN_ON(!skb);
+ if (skb)
+ cw1200_tx(priv->hw, NULL, skb);
+ }
+}
+
+int cw1200_add_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif)
+{
+ int ret;
+ struct cw1200_common *priv = dev->priv;
+ /* __le32 auto_calibration_mode = __cpu_to_le32(1); */
+
+ vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
+ IEEE80211_VIF_SUPPORTS_CQM_RSSI;
+
+ mutex_lock(&priv->conf_mutex);
+
+ if (priv->mode != NL80211_IFTYPE_MONITOR) {
+ mutex_unlock(&priv->conf_mutex);
+ return -EOPNOTSUPP;
+ }
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_AP:
+ priv->mode = vif->type;
+ break;
+ default:
+ mutex_unlock(&priv->conf_mutex);
+ return -EOPNOTSUPP;
+ }
+
+ priv->vif = vif;
+ memcpy(priv->mac_addr, vif->addr, ETH_ALEN);
+ ret = cw1200_setup_mac(priv);
+ /* Enable auto-calibration */
+ /* Exception in subsequent channel switch; disabled.
+ * wsm_write_mib(priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE,
+ * &auto_calibration_mode, sizeof(auto_calibration_mode));
+ */
+
+ mutex_unlock(&priv->conf_mutex);
+ return ret;
+}
+
+void cw1200_remove_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif)
+{
+ struct cw1200_common *priv = dev->priv;
+ struct wsm_reset reset = {
+ .reset_statistics = true,
+ };
+ int i;
+
+ mutex_lock(&priv->conf_mutex);
+ switch (priv->join_status) {
+ case CW1200_JOIN_STATUS_JOINING:
+ case CW1200_JOIN_STATUS_PRE_STA:
+ case CW1200_JOIN_STATUS_STA:
+ case CW1200_JOIN_STATUS_IBSS:
+ wsm_lock_tx(priv);
+ if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(priv);
+ break;
+ case CW1200_JOIN_STATUS_AP:
+ for (i = 0; priv->link_id_map; ++i) {
+ if (priv->link_id_map & BIT(i)) {
+ reset.link_id = i;
+ wsm_reset(priv, &reset);
+ priv->link_id_map &= ~BIT(i);
+ }
+ }
+ memset(priv->link_id_db, 0, sizeof(priv->link_id_db));
+ priv->sta_asleep_mask = 0;
+ priv->enable_beacon = false;
+ priv->tx_multicast = false;
+ priv->aid0_bit_set = false;
+ priv->buffered_multicasts = false;
+ priv->pspoll_mask = 0;
+ reset.link_id = 0;
+ wsm_reset(priv, &reset);
+ break;
+ case CW1200_JOIN_STATUS_MONITOR:
+ cw1200_update_listening(priv, false);
+ break;
+ default:
+ break;
+ }
+ priv->vif = NULL;
+ priv->mode = NL80211_IFTYPE_MONITOR;
+ memset(priv->mac_addr, 0, ETH_ALEN);
+ memset(&priv->p2p_ps_modeinfo, 0, sizeof(priv->p2p_ps_modeinfo));
+ cw1200_free_keys(priv);
+ cw1200_setup_mac(priv);
+ priv->listening = false;
+ priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+ if (!__cw1200_flush(priv, true))
+ wsm_unlock_tx(priv);
+
+ mutex_unlock(&priv->conf_mutex);
+}
+
+int cw1200_change_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ enum nl80211_iftype new_type,
+ bool p2p)
+{
+ int ret = 0;
+ pr_debug("change_interface new: %d (%d), old: %d (%d)\n", new_type,
+ p2p, vif->type, vif->p2p);
+
+ if (new_type != vif->type || vif->p2p != p2p) {
+ cw1200_remove_interface(dev, vif);
+ vif->type = new_type;
+ vif->p2p = p2p;
+ ret = cw1200_add_interface(dev, vif);
+ }
+
+ return ret;
+}
+
+int cw1200_config(struct ieee80211_hw *dev, u32 changed)
+{
+ int ret = 0;
+ struct cw1200_common *priv = dev->priv;
+ struct ieee80211_conf *conf = &dev->conf;
+
+ pr_debug("CONFIG CHANGED: %08x\n", changed);
+
+ down(&priv->scan.lock);
+ mutex_lock(&priv->conf_mutex);
+ /* TODO: IEEE80211_CONF_CHANGE_QOS */
+ /* TODO: IEEE80211_CONF_CHANGE_LISTEN_INTERVAL */
+
+ if (changed & IEEE80211_CONF_CHANGE_POWER) {
+ priv->output_power = conf->power_level;
+ pr_debug("[STA] TX power: %d\n", priv->output_power);
+ wsm_set_output_power(priv, priv->output_power * 10);
+ }
+
+ if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) &&
+ (priv->channel != conf->chandef.chan)) {
+ struct ieee80211_channel *ch = conf->chandef.chan;
+ struct wsm_switch_channel channel = {
+ .channel_number = ch->hw_value,
+ };
+ pr_debug("[STA] Freq %d (wsm ch: %d).\n",
+ ch->center_freq, ch->hw_value);
+
+ /* __cw1200_flush() implicitly locks tx, if successful */
+ if (!__cw1200_flush(priv, false)) {
+ if (!wsm_switch_channel(priv, &channel)) {
+ ret = wait_event_timeout(priv->channel_switch_done,
+ !priv->channel_switch_in_progress,
+ 3 * HZ);
+ if (ret) {
+ /* Already unlocks if successful */
+ priv->channel = ch;
+ ret = 0;
+ } else {
+ ret = -ETIMEDOUT;
+ }
+ } else {
+ /* Unlock if switch channel fails */
+ wsm_unlock_tx(priv);
+ }
+ }
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_PS) {
+ if (!(conf->flags & IEEE80211_CONF_PS))
+ priv->powersave_mode.mode = WSM_PSM_ACTIVE;
+ else if (conf->dynamic_ps_timeout <= 0)
+ priv->powersave_mode.mode = WSM_PSM_PS;
+ else
+ priv->powersave_mode.mode = WSM_PSM_FAST_PS;
+
+ /* Firmware requires that value for this 1-byte field must
+ * be specified in units of 500us. Values above the 128ms
+ * threshold are not supported.
+ */
+ if (conf->dynamic_ps_timeout >= 0x80)
+ priv->powersave_mode.fast_psm_idle_period = 0xFF;
+ else
+ priv->powersave_mode.fast_psm_idle_period =
+ conf->dynamic_ps_timeout << 1;
+
+ if (priv->join_status == CW1200_JOIN_STATUS_STA &&
+ priv->bss_params.aid)
+ cw1200_set_pm(priv, &priv->powersave_mode);
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
+ /* TBD: It looks like it's transparent
+ * there's a monitor interface present -- use this
+ * to determine for example whether to calculate
+ * timestamps for packets or not, do not use instead
+ * of filter flags!
+ */
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_IDLE) {
+ struct wsm_operational_mode mode = {
+ .power_mode = cw1200_power_mode,
+ .disable_more_flag_usage = true,
+ };
+
+ wsm_lock_tx(priv);
+ /* Disable p2p-dev mode forced by TX request */
+ if ((priv->join_status == CW1200_JOIN_STATUS_MONITOR) &&
+ (conf->flags & IEEE80211_CONF_IDLE) &&
+ !priv->listening) {
+ cw1200_disable_listening(priv);
+ priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+ }
+ wsm_set_operational_mode(priv, &mode);
+ wsm_unlock_tx(priv);
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
+ pr_debug("[STA] Retry limits: %d (long), %d (short).\n",
+ conf->long_frame_max_tx_count,
+ conf->short_frame_max_tx_count);
+ spin_lock_bh(&priv->tx_policy_cache.lock);
+ priv->long_frame_max_tx_count = conf->long_frame_max_tx_count;
+ priv->short_frame_max_tx_count =
+ (conf->short_frame_max_tx_count < 0x0F) ?
+ conf->short_frame_max_tx_count : 0x0F;
+ priv->hw->max_rate_tries = priv->short_frame_max_tx_count;
+ spin_unlock_bh(&priv->tx_policy_cache.lock);
+ }
+ mutex_unlock(&priv->conf_mutex);
+ up(&priv->scan.lock);
+ return ret;
+}
+
+void cw1200_update_filtering(struct cw1200_common *priv)
+{
+ int ret;
+ bool bssid_filtering = !priv->rx_filter.bssid;
+ bool is_p2p = priv->vif && priv->vif->p2p;
+ bool is_sta = priv->vif && NL80211_IFTYPE_STATION == priv->vif->type;
+
+ static struct wsm_beacon_filter_control bf_ctrl;
+ static struct wsm_mib_beacon_filter_table bf_tbl = {
+ .entry[0].ie_id = WLAN_EID_VENDOR_SPECIFIC,
+ .entry[0].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+ WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+ WSM_BEACON_FILTER_IE_HAS_APPEARED,
+ .entry[0].oui[0] = 0x50,
+ .entry[0].oui[1] = 0x6F,
+ .entry[0].oui[2] = 0x9A,
+ .entry[1].ie_id = WLAN_EID_HT_OPERATION,
+ .entry[1].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+ WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+ WSM_BEACON_FILTER_IE_HAS_APPEARED,
+ .entry[2].ie_id = WLAN_EID_ERP_INFO,
+ .entry[2].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+ WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+ WSM_BEACON_FILTER_IE_HAS_APPEARED,
+ };
+
+ if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE)
+ return;
+ else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
+ bssid_filtering = false;
+
+ if (priv->disable_beacon_filter) {
+ bf_ctrl.enabled = 0;
+ bf_ctrl.bcn_count = 1;
+ bf_tbl.num = __cpu_to_le32(0);
+ } else if (is_p2p || !is_sta) {
+ bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE |
+ WSM_BEACON_FILTER_AUTO_ERP;
+ bf_ctrl.bcn_count = 0;
+ bf_tbl.num = __cpu_to_le32(2);
+ } else {
+ bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE;
+ bf_ctrl.bcn_count = 0;
+ bf_tbl.num = __cpu_to_le32(3);
+ }
+
+ /*
+ * When acting as p2p client being connected to p2p GO, in order to
+ * receive frames from a different p2p device, turn off bssid filter.
+ *
+ * WARNING: FW dependency!
+ * This can only be used with FW WSM371 and its successors.
+ * In that FW version even with bssid filter turned off,
+ * device will block most of the unwanted frames.
+ */
+ if (is_p2p)
+ bssid_filtering = false;
+
+ ret = wsm_set_rx_filter(priv, &priv->rx_filter);
+ if (!ret)
+ ret = wsm_set_beacon_filter_table(priv, &bf_tbl);
+ if (!ret)
+ ret = wsm_beacon_filter_control(priv, &bf_ctrl);
+ if (!ret)
+ ret = wsm_set_bssid_filtering(priv, bssid_filtering);
+ if (!ret)
+ ret = wsm_set_multicast_filter(priv, &priv->multicast_filter);
+ if (ret)
+ wiphy_err(priv->hw->wiphy,
+ "Update filtering failed: %d.\n", ret);
+ return;
+}
+
+void cw1200_update_filtering_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common,
+ update_filtering_work);
+
+ cw1200_update_filtering(priv);
+}
+
+void cw1200_set_beacon_wakeup_period_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common,
+ set_beacon_wakeup_period_work);
+
+ wsm_set_beacon_wakeup_period(priv,
+ priv->beacon_int * priv->join_dtim_period >
+ MAX_BEACON_SKIP_TIME_MS ? 1 :
+ priv->join_dtim_period, 0);
+}
+
+u64 cw1200_prepare_multicast(struct ieee80211_hw *hw,
+ struct netdev_hw_addr_list *mc_list)
+{
+ static u8 broadcast_ipv6[ETH_ALEN] = {
+ 0x33, 0x33, 0x00, 0x00, 0x00, 0x01
+ };
+ static u8 broadcast_ipv4[ETH_ALEN] = {
+ 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01
+ };
+ struct cw1200_common *priv = hw->priv;
+ struct netdev_hw_addr *ha;
+ int count = 0;
+
+ /* Disable multicast filtering */
+ priv->has_multicast_subscription = false;
+ memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter));
+
+ if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES)
+ return 0;
+
+ /* Enable if requested */
+ netdev_hw_addr_list_for_each(ha, mc_list) {
+ pr_debug("[STA] multicast: %pM\n", ha->addr);
+ memcpy(&priv->multicast_filter.macaddrs[count],
+ ha->addr, ETH_ALEN);
+ if (memcmp(ha->addr, broadcast_ipv4, ETH_ALEN) &&
+ memcmp(ha->addr, broadcast_ipv6, ETH_ALEN))
+ priv->has_multicast_subscription = true;
+ count++;
+ }
+
+ if (count) {
+ priv->multicast_filter.enable = __cpu_to_le32(1);
+ priv->multicast_filter.num_addrs = __cpu_to_le32(count);
+ }
+
+ return netdev_hw_addr_list_count(mc_list);
+}
+
+void cw1200_configure_filter(struct ieee80211_hw *dev,
+ unsigned int changed_flags,
+ unsigned int *total_flags,
+ u64 multicast)
+{
+ struct cw1200_common *priv = dev->priv;
+ bool listening = !!(*total_flags &
+ (FIF_PROMISC_IN_BSS |
+ FIF_OTHER_BSS |
+ FIF_BCN_PRBRESP_PROMISC |
+ FIF_PROBE_REQ));
+
+ *total_flags &= FIF_PROMISC_IN_BSS |
+ FIF_OTHER_BSS |
+ FIF_FCSFAIL |
+ FIF_BCN_PRBRESP_PROMISC |
+ FIF_PROBE_REQ;
+
+ down(&priv->scan.lock);
+ mutex_lock(&priv->conf_mutex);
+
+ priv->rx_filter.promiscuous = (*total_flags & FIF_PROMISC_IN_BSS)
+ ? 1 : 0;
+ priv->rx_filter.bssid = (*total_flags & (FIF_OTHER_BSS |
+ FIF_PROBE_REQ)) ? 1 : 0;
+ priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0;
+ priv->disable_beacon_filter = !(*total_flags &
+ (FIF_BCN_PRBRESP_PROMISC |
+ FIF_PROMISC_IN_BSS |
+ FIF_PROBE_REQ));
+ if (priv->listening != listening) {
+ priv->listening = listening;
+ wsm_lock_tx(priv);
+ cw1200_update_listening(priv, listening);
+ wsm_unlock_tx(priv);
+ }
+ cw1200_update_filtering(priv);
+ mutex_unlock(&priv->conf_mutex);
+ up(&priv->scan.lock);
+}
+
+int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+ u16 queue, const struct ieee80211_tx_queue_params *params)
+{
+ struct cw1200_common *priv = dev->priv;
+ int ret = 0;
+ /* To prevent re-applying PM request OID again and again*/
+ bool old_uapsd_flags;
+
+ mutex_lock(&priv->conf_mutex);
+
+ if (queue < dev->queues) {
+ old_uapsd_flags = priv->uapsd_info.uapsd_flags;
+
+ WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0);
+ ret = wsm_set_tx_queue_params(priv,
+ &priv->tx_queue_params.params[queue], queue);
+ if (ret) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ WSM_EDCA_SET(&priv->edca, queue, params->aifs,
+ params->cw_min, params->cw_max,
+ params->txop, 0xc8,
+ params->uapsd);
+ ret = wsm_set_edca_params(priv, &priv->edca);
+ if (ret) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (priv->mode == NL80211_IFTYPE_STATION) {
+ ret = cw1200_set_uapsd_param(priv, &priv->edca);
+ if (!ret && priv->setbssparams_done &&
+ (priv->join_status == CW1200_JOIN_STATUS_STA) &&
+ (old_uapsd_flags != priv->uapsd_info.uapsd_flags))
+ ret = cw1200_set_pm(priv, &priv->powersave_mode);
+ }
+ } else {
+ ret = -EINVAL;
+ }
+
+out:
+ mutex_unlock(&priv->conf_mutex);
+ return ret;
+}
+
+int cw1200_get_stats(struct ieee80211_hw *dev,
+ struct ieee80211_low_level_stats *stats)
+{
+ struct cw1200_common *priv = dev->priv;
+
+ memcpy(stats, &priv->stats, sizeof(*stats));
+ return 0;
+}
+
+int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg)
+{
+ struct wsm_set_pm pm = *arg;
+
+ if (priv->uapsd_info.uapsd_flags != 0)
+ pm.mode &= ~WSM_PSM_FAST_PS_FLAG;
+
+ if (memcmp(&pm, &priv->firmware_ps_mode,
+ sizeof(struct wsm_set_pm))) {
+ priv->firmware_ps_mode = pm;
+ return wsm_set_pm(priv, &pm);
+ } else {
+ return 0;
+ }
+}
+
+int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ int ret = -EOPNOTSUPP;
+ struct cw1200_common *priv = dev->priv;
+ struct ieee80211_key_seq seq;
+
+ mutex_lock(&priv->conf_mutex);
+
+ if (cmd == SET_KEY) {
+ u8 *peer_addr = NULL;
+ int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ?
+ 1 : 0;
+ int idx = cw1200_alloc_key(priv);
+ struct wsm_add_key *wsm_key = &priv->keys[idx];
+
+ if (idx < 0) {
+ ret = -EINVAL;
+ goto finally;
+ }
+
+ if (sta)
+ peer_addr = sta->addr;
+
+ key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ if (key->keylen > 16) {
+ cw1200_free_key(priv, idx);
+ ret = -EINVAL;
+ goto finally;
+ }
+
+ if (pairwise) {
+ wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE;
+ memcpy(wsm_key->wep_pairwise.peer,
+ peer_addr, ETH_ALEN);
+ memcpy(wsm_key->wep_pairwise.keydata,
+ &key->key[0], key->keylen);
+ wsm_key->wep_pairwise.keylen = key->keylen;
+ } else {
+ wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT;
+ memcpy(wsm_key->wep_group.keydata,
+ &key->key[0], key->keylen);
+ wsm_key->wep_group.keylen = key->keylen;
+ wsm_key->wep_group.keyid = key->keyidx;
+ }
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ ieee80211_get_key_rx_seq(key, 0, &seq);
+ if (pairwise) {
+ wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE;
+ memcpy(wsm_key->tkip_pairwise.peer,
+ peer_addr, ETH_ALEN);
+ memcpy(wsm_key->tkip_pairwise.keydata,
+ &key->key[0], 16);
+ memcpy(wsm_key->tkip_pairwise.tx_mic_key,
+ &key->key[16], 8);
+ memcpy(wsm_key->tkip_pairwise.rx_mic_key,
+ &key->key[24], 8);
+ } else {
+ size_t mic_offset =
+ (priv->mode == NL80211_IFTYPE_AP) ?
+ 16 : 24;
+ wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP;
+ memcpy(wsm_key->tkip_group.keydata,
+ &key->key[0], 16);
+ memcpy(wsm_key->tkip_group.rx_mic_key,
+ &key->key[mic_offset], 8);
+
+ wsm_key->tkip_group.rx_seqnum[0] = seq.tkip.iv16 & 0xff;
+ wsm_key->tkip_group.rx_seqnum[1] = (seq.tkip.iv16 >> 8) & 0xff;
+ wsm_key->tkip_group.rx_seqnum[2] = seq.tkip.iv32 & 0xff;
+ wsm_key->tkip_group.rx_seqnum[3] = (seq.tkip.iv32 >> 8) & 0xff;
+ wsm_key->tkip_group.rx_seqnum[4] = (seq.tkip.iv32 >> 16) & 0xff;
+ wsm_key->tkip_group.rx_seqnum[5] = (seq.tkip.iv32 >> 24) & 0xff;
+ wsm_key->tkip_group.rx_seqnum[6] = 0;
+ wsm_key->tkip_group.rx_seqnum[7] = 0;
+
+ wsm_key->tkip_group.keyid = key->keyidx;
+ }
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ ieee80211_get_key_rx_seq(key, 0, &seq);
+ if (pairwise) {
+ wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE;
+ memcpy(wsm_key->aes_pairwise.peer,
+ peer_addr, ETH_ALEN);
+ memcpy(wsm_key->aes_pairwise.keydata,
+ &key->key[0], 16);
+ } else {
+ wsm_key->type = WSM_KEY_TYPE_AES_GROUP;
+ memcpy(wsm_key->aes_group.keydata,
+ &key->key[0], 16);
+
+ wsm_key->aes_group.rx_seqnum[0] = seq.ccmp.pn[5];
+ wsm_key->aes_group.rx_seqnum[1] = seq.ccmp.pn[4];
+ wsm_key->aes_group.rx_seqnum[2] = seq.ccmp.pn[3];
+ wsm_key->aes_group.rx_seqnum[3] = seq.ccmp.pn[2];
+ wsm_key->aes_group.rx_seqnum[4] = seq.ccmp.pn[1];
+ wsm_key->aes_group.rx_seqnum[5] = seq.ccmp.pn[0];
+ wsm_key->aes_group.rx_seqnum[6] = 0;
+ wsm_key->aes_group.rx_seqnum[7] = 0;
+ wsm_key->aes_group.keyid = key->keyidx;
+ }
+ break;
+ case WLAN_CIPHER_SUITE_SMS4:
+ if (pairwise) {
+ wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE;
+ memcpy(wsm_key->wapi_pairwise.peer,
+ peer_addr, ETH_ALEN);
+ memcpy(wsm_key->wapi_pairwise.keydata,
+ &key->key[0], 16);
+ memcpy(wsm_key->wapi_pairwise.mic_key,
+ &key->key[16], 16);
+ wsm_key->wapi_pairwise.keyid = key->keyidx;
+ } else {
+ wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP;
+ memcpy(wsm_key->wapi_group.keydata,
+ &key->key[0], 16);
+ memcpy(wsm_key->wapi_group.mic_key,
+ &key->key[16], 16);
+ wsm_key->wapi_group.keyid = key->keyidx;
+ }
+ break;
+ default:
+ pr_warn("Unhandled key type %d\n", key->cipher);
+ cw1200_free_key(priv, idx);
+ ret = -EOPNOTSUPP;
+ goto finally;
+ }
+ ret = wsm_add_key(priv, wsm_key);
+ if (!ret)
+ key->hw_key_idx = idx;
+ else
+ cw1200_free_key(priv, idx);
+ } else if (cmd == DISABLE_KEY) {
+ struct wsm_remove_key wsm_key = {
+ .index = key->hw_key_idx,
+ };
+
+ if (wsm_key.index > WSM_KEY_MAX_INDEX) {
+ ret = -EINVAL;
+ goto finally;
+ }
+
+ cw1200_free_key(priv, wsm_key.index);
+ ret = wsm_remove_key(priv, &wsm_key);
+ } else {
+ pr_warn("Unhandled key command %d\n", cmd);
+ }
+
+finally:
+ mutex_unlock(&priv->conf_mutex);
+ return ret;
+}
+
+void cw1200_wep_key_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, wep_key_work);
+ u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id);
+ struct cw1200_queue *queue = &priv->tx_queue[queue_id];
+ __le32 wep_default_key_id = __cpu_to_le32(
+ priv->wep_default_key_id);
+
+ pr_debug("[STA] Setting default WEP key: %d\n",
+ priv->wep_default_key_id);
+ wsm_flush_tx(priv);
+ wsm_write_mib(priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID,
+ &wep_default_key_id, sizeof(wep_default_key_id));
+ cw1200_queue_requeue(queue, priv->pending_frame_id);
+ wsm_unlock_tx(priv);
+}
+
+int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+ int ret = 0;
+ __le32 val32;
+ struct cw1200_common *priv = hw->priv;
+
+ if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+ return 0;
+
+ if (value != (u32) -1)
+ val32 = __cpu_to_le32(value);
+ else
+ val32 = 0; /* disabled */
+
+ if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
+ /* device is down, can _not_ set threshold */
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (priv->rts_threshold == value)
+ goto out;
+
+ pr_debug("[STA] Setting RTS threshold: %d\n",
+ priv->rts_threshold);
+
+ /* mutex_lock(&priv->conf_mutex); */
+ ret = wsm_write_mib(priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD,
+ &val32, sizeof(val32));
+ if (!ret)
+ priv->rts_threshold = value;
+ /* mutex_unlock(&priv->conf_mutex); */
+
+out:
+ return ret;
+}
+
+/* If successful, LOCKS the TX queue! */
+static int __cw1200_flush(struct cw1200_common *priv, bool drop)
+{
+ int i, ret;
+
+ for (;;) {
+ /* TODO: correct flush handling is required when dev_stop.
+ * Temporary workaround: 2s
+ */
+ if (drop) {
+ for (i = 0; i < 4; ++i)
+ cw1200_queue_clear(&priv->tx_queue[i]);
+ } else {
+ ret = wait_event_timeout(
+ priv->tx_queue_stats.wait_link_id_empty,
+ cw1200_queue_stats_is_empty(
+ &priv->tx_queue_stats, -1),
+ 2 * HZ);
+ }
+
+ if (!drop && ret <= 0) {
+ ret = -ETIMEDOUT;
+ break;
+ } else {
+ ret = 0;
+ }
+
+ wsm_lock_tx(priv);
+ if (!cw1200_queue_stats_is_empty(&priv->tx_queue_stats, -1)) {
+ /* Highly unlikely: WSM requeued frames. */
+ wsm_unlock_tx(priv);
+ continue;
+ }
+ break;
+ }
+ return ret;
+}
+
+void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+{
+ struct cw1200_common *priv = hw->priv;
+
+ switch (priv->mode) {
+ case NL80211_IFTYPE_MONITOR:
+ drop = true;
+ break;
+ case NL80211_IFTYPE_AP:
+ if (!priv->enable_beacon)
+ drop = true;
+ break;
+ }
+
+ if (!__cw1200_flush(priv, drop))
+ wsm_unlock_tx(priv);
+
+ return;
+}
+
+/* ******************************************************************** */
+/* WSM callbacks */
+
+void cw1200_free_event_queue(struct cw1200_common *priv)
+{
+ LIST_HEAD(list);
+
+ spin_lock(&priv->event_queue_lock);
+ list_splice_init(&priv->event_queue, &list);
+ spin_unlock(&priv->event_queue_lock);
+
+ __cw1200_free_event_queue(&list);
+}
+
+void cw1200_event_handler(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, event_handler);
+ struct cw1200_wsm_event *event;
+ LIST_HEAD(list);
+
+ spin_lock(&priv->event_queue_lock);
+ list_splice_init(&priv->event_queue, &list);
+ spin_unlock(&priv->event_queue_lock);
+
+ list_for_each_entry(event, &list, link) {
+ switch (event->evt.id) {
+ case WSM_EVENT_ERROR:
+ pr_err("Unhandled WSM Error from LMAC\n");
+ break;
+ case WSM_EVENT_BSS_LOST:
+ pr_debug("[CQM] BSS lost.\n");
+ cancel_work_sync(&priv->unjoin_work);
+ if (!down_trylock(&priv->scan.lock)) {
+ cw1200_cqm_bssloss_sm(priv, 1, 0, 0);
+ up(&priv->scan.lock);
+ } else {
+ /* Scan is in progress. Delay reporting.
+ * Scan complete will trigger bss_loss_work
+ */
+ priv->delayed_link_loss = 1;
+ /* Also start a watchdog. */
+ queue_delayed_work(priv->workqueue,
+ &priv->bss_loss_work, 5*HZ);
+ }
+ break;
+ case WSM_EVENT_BSS_REGAINED:
+ pr_debug("[CQM] BSS regained.\n");
+ cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
+ cancel_work_sync(&priv->unjoin_work);
+ break;
+ case WSM_EVENT_RADAR_DETECTED:
+ wiphy_info(priv->hw->wiphy, "radar pulse detected\n");
+ break;
+ case WSM_EVENT_RCPI_RSSI:
+ {
+ /* RSSI: signed Q8.0, RCPI: unsigned Q7.1
+ * RSSI = RCPI / 2 - 110
+ */
+ int rcpiRssi = (int)(event->evt.data & 0xFF);
+ int cqm_evt;
+ if (priv->cqm_use_rssi)
+ rcpiRssi = (s8)rcpiRssi;
+ else
+ rcpiRssi = rcpiRssi / 2 - 110;
+
+ cqm_evt = (rcpiRssi <= priv->cqm_rssi_thold) ?
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW :
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
+ pr_debug("[CQM] RSSI event: %d.\n", rcpiRssi);
+ ieee80211_cqm_rssi_notify(priv->vif, cqm_evt,
+ GFP_KERNEL);
+ break;
+ }
+ case WSM_EVENT_BT_INACTIVE:
+ pr_warn("Unhandled BT INACTIVE from LMAC\n");
+ break;
+ case WSM_EVENT_BT_ACTIVE:
+ pr_warn("Unhandled BT ACTIVE from LMAC\n");
+ break;
+ }
+ }
+ __cw1200_free_event_queue(&list);
+}
+
+void cw1200_bss_loss_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, bss_loss_work.work);
+
+ pr_debug("[CQM] Reporting connection loss.\n");
+ wsm_lock_tx(priv);
+ if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(priv);
+}
+
+void cw1200_bss_params_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, bss_params_work);
+ mutex_lock(&priv->conf_mutex);
+
+ priv->bss_params.reset_beacon_loss = 1;
+ wsm_set_bss_params(priv, &priv->bss_params);
+ priv->bss_params.reset_beacon_loss = 0;
+
+ mutex_unlock(&priv->conf_mutex);
+}
+
+/* ******************************************************************** */
+/* Internal API */
+
+/*
+ * This function is called to Parse the SDD file
+ * to extract listen_interval and PTA related information
+ * sdd is a TLV: u8 id, u8 len, u8 data[]
+ */
+static int cw1200_parse_sdd_file(struct cw1200_common *priv)
+{
+ const u8 *p = priv->sdd->data;
+ int ret = 0;
+
+ while (p + 2 <= priv->sdd->data + priv->sdd->size) {
+ if (p + p[1] + 2 > priv->sdd->data + priv->sdd->size) {
+ pr_warn("Malformed sdd structure\n");
+ return -1;
+ }
+ switch (p[0]) {
+ case SDD_PTA_CFG_ELT_ID: {
+ u16 v;
+ if (p[1] < 4) {
+ pr_warn("SDD_PTA_CFG_ELT_ID malformed\n");
+ ret = -1;
+ break;
+ }
+ v = le16_to_cpu(*((u16 *)(p + 2)));
+ if (!v) /* non-zero means this is enabled */
+ break;
+
+ v = le16_to_cpu(*((u16 *)(p + 4)));
+ priv->conf_listen_interval = (v >> 7) & 0x1F;
+ pr_debug("PTA found; Listen Interval %d\n",
+ priv->conf_listen_interval);
+ break;
+ }
+ case SDD_REFERENCE_FREQUENCY_ELT_ID: {
+ u16 clk = le16_to_cpu(*((u16 *)(p + 2)));
+ if (clk != priv->hw_refclk)
+ pr_warn("SDD file doesn't match configured refclk (%d vs %d)\n",
+ clk, priv->hw_refclk);
+ break;
+ }
+ default:
+ break;
+ }
+ p += p[1] + 2;
+ }
+
+ if (!priv->bt_present) {
+ pr_debug("PTA element NOT found.\n");
+ priv->conf_listen_interval = 0;
+ }
+ return ret;
+}
+
+int cw1200_setup_mac(struct cw1200_common *priv)
+{
+ int ret = 0;
+
+ /* NOTE: There is a bug in FW: it reports signal
+ * as RSSI if RSSI subscription is enabled.
+ * It's not enough to set WSM_RCPI_RSSI_USE_RSSI.
+ *
+ * NOTE2: RSSI based reports have been switched to RCPI, since
+ * FW has a bug and RSSI reported values are not stable,
+ * what can leads to signal level oscilations in user-end applications
+ */
+ struct wsm_rcpi_rssi_threshold threshold = {
+ .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE |
+ WSM_RCPI_RSSI_DONT_USE_UPPER |
+ WSM_RCPI_RSSI_DONT_USE_LOWER,
+ .rollingAverageCount = 16,
+ };
+
+ struct wsm_configuration cfg = {
+ .dot11StationId = &priv->mac_addr[0],
+ };
+
+ /* Remember the decission here to make sure, we will handle
+ * the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS
+ */
+ if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI)
+ priv->cqm_use_rssi = true;
+
+ if (!priv->sdd) {
+ ret = request_firmware(&priv->sdd, priv->sdd_path, priv->pdev);
+ if (ret) {
+ pr_err("Can't load sdd file %s.\n", priv->sdd_path);
+ return ret;
+ }
+ cw1200_parse_sdd_file(priv);
+ }
+
+ cfg.dpdData = priv->sdd->data;
+ cfg.dpdData_size = priv->sdd->size;
+ ret = wsm_configuration(priv, &cfg);
+ if (ret)
+ return ret;
+
+ /* Configure RSSI/SCPI reporting as RSSI. */
+ wsm_set_rcpi_rssi_threshold(priv, &threshold);
+
+ return 0;
+}
+
+static void cw1200_join_complete(struct cw1200_common *priv)
+{
+ pr_debug("[STA] Join complete (%d)\n", priv->join_complete_status);
+
+ priv->join_pending = false;
+ if (priv->join_complete_status) {
+ priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+ cw1200_update_listening(priv, priv->listening);
+ cw1200_do_unjoin(priv);
+ ieee80211_connection_loss(priv->vif);
+ } else {
+ if (priv->mode == NL80211_IFTYPE_ADHOC)
+ priv->join_status = CW1200_JOIN_STATUS_IBSS;
+ else
+ priv->join_status = CW1200_JOIN_STATUS_PRE_STA;
+ }
+ wsm_unlock_tx(priv); /* Clearing the lock held before do_join() */
+}
+
+void cw1200_join_complete_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, join_complete_work);
+ mutex_lock(&priv->conf_mutex);
+ cw1200_join_complete(priv);
+ mutex_unlock(&priv->conf_mutex);
+}
+
+void cw1200_join_complete_cb(struct cw1200_common *priv,
+ struct wsm_join_complete *arg)
+{
+ pr_debug("[STA] cw1200_join_complete_cb called, status=%d.\n",
+ arg->status);
+
+ if (cancel_delayed_work(&priv->join_timeout)) {
+ priv->join_complete_status = arg->status;
+ queue_work(priv->workqueue, &priv->join_complete_work);
+ }
+}
+
+/* MUST be called with tx_lock held! It will be unlocked for us. */
+static void cw1200_do_join(struct cw1200_common *priv)
+{
+ const u8 *bssid;
+ struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
+ struct cfg80211_bss *bss = NULL;
+ struct wsm_protected_mgmt_policy mgmt_policy;
+ struct wsm_join join = {
+ .mode = conf->ibss_joined ?
+ WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS,
+ .preamble_type = WSM_JOIN_PREAMBLE_LONG,
+ .probe_for_join = 1,
+ .atim_window = 0,
+ .basic_rate_set = cw1200_rate_mask_to_wsm(priv,
+ conf->basic_rates),
+ };
+ if (delayed_work_pending(&priv->join_timeout)) {
+ pr_warn("[STA] - Join request already pending, skipping..\n");
+ wsm_unlock_tx(priv);
+ return;
+ }
+
+ if (priv->join_status)
+ cw1200_do_unjoin(priv);
+
+ bssid = priv->vif->bss_conf.bssid;
+
+ bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel,
+ bssid, NULL, 0, 0, 0);
+
+ if (!bss && !conf->ibss_joined) {
+ wsm_unlock_tx(priv);
+ return;
+ }
+
+ mutex_lock(&priv->conf_mutex);
+
+ /* Under the conf lock: check scan status and
+ * bail out if it is in progress.
+ */
+ if (atomic_read(&priv->scan.in_progress)) {
+ wsm_unlock_tx(priv);
+ goto done_put;
+ }
+
+ priv->join_pending = true;
+
+ /* Sanity check basic rates */
+ if (!join.basic_rate_set)
+ join.basic_rate_set = 7;
+
+ /* Sanity check beacon interval */
+ if (!priv->beacon_int)
+ priv->beacon_int = 1;
+
+ join.beacon_interval = priv->beacon_int;
+
+ /* BT Coex related changes */
+ if (priv->bt_present) {
+ if (((priv->conf_listen_interval * 100) %
+ priv->beacon_int) == 0)
+ priv->listen_interval =
+ ((priv->conf_listen_interval * 100) /
+ priv->beacon_int);
+ else
+ priv->listen_interval =
+ ((priv->conf_listen_interval * 100) /
+ priv->beacon_int + 1);
+ }
+
+ if (priv->hw->conf.ps_dtim_period)
+ priv->join_dtim_period = priv->hw->conf.ps_dtim_period;
+ join.dtim_period = priv->join_dtim_period;
+
+ join.channel_number = priv->channel->hw_value;
+ join.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
+ WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
+
+ memcpy(join.bssid, bssid, sizeof(join.bssid));
+
+ pr_debug("[STA] Join BSSID: %pM DTIM: %d, interval: %d\n",
+ join.bssid,
+ join.dtim_period, priv->beacon_int);
+
+ if (!conf->ibss_joined) {
+ const u8 *ssidie;
+ rcu_read_lock();
+ ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
+ if (ssidie) {
+ join.ssid_len = ssidie[1];
+ memcpy(join.ssid, &ssidie[2], join.ssid_len);
+ }
+ rcu_read_unlock();
+ }
+
+ if (priv->vif->p2p) {
+ join.flags |= WSM_JOIN_FLAGS_P2P_GO;
+ join.basic_rate_set =
+ cw1200_rate_mask_to_wsm(priv, 0xFF0);
+ }
+
+ /* Enable asynchronous join calls */
+ if (!conf->ibss_joined) {
+ join.flags |= WSM_JOIN_FLAGS_FORCE;
+ join.flags |= WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND;
+ }
+
+ wsm_flush_tx(priv);
+
+ /* Stay Awake for Join and Auth Timeouts and a bit more */
+ cw1200_pm_stay_awake(&priv->pm_state,
+ CW1200_JOIN_TIMEOUT + CW1200_AUTH_TIMEOUT);
+
+ cw1200_update_listening(priv, false);
+
+ /* Turn on Block ACKs */
+ wsm_set_block_ack_policy(priv, priv->ba_tx_tid_mask,
+ priv->ba_rx_tid_mask);
+
+ /* Set up timeout */
+ if (join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND) {
+ priv->join_status = CW1200_JOIN_STATUS_JOINING;
+ queue_delayed_work(priv->workqueue,
+ &priv->join_timeout,
+ CW1200_JOIN_TIMEOUT);
+ }
+
+ /* 802.11w protected mgmt frames */
+ mgmt_policy.protectedMgmtEnable = 0;
+ mgmt_policy.unprotectedMgmtFramesAllowed = 1;
+ mgmt_policy.encryptionForAuthFrame = 1;
+ wsm_set_protected_mgmt_policy(priv, &mgmt_policy);
+
+ /* Perform actual join */
+ if (wsm_join(priv, &join)) {
+ pr_err("[STA] cw1200_join_work: wsm_join failed!\n");
+ cancel_delayed_work_sync(&priv->join_timeout);
+ cw1200_update_listening(priv, priv->listening);
+ /* Tx lock still held, unjoin will clear it. */
+ if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(priv);
+ } else {
+ if (!(join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND))
+ cw1200_join_complete(priv); /* Will clear tx_lock */
+
+ /* Upload keys */
+ cw1200_upload_keys(priv);
+
+ /* Due to beacon filtering it is possible that the
+ * AP's beacon is not known for the mac80211 stack.
+ * Disable filtering temporary to make sure the stack
+ * receives at least one
+ */
+ priv->disable_beacon_filter = true;
+ }
+ cw1200_update_filtering(priv);
+
+done_put:
+ mutex_unlock(&priv->conf_mutex);
+ if (bss)
+ cfg80211_put_bss(priv->hw->wiphy, bss);
+}
+
+void cw1200_join_timeout(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, join_timeout.work);
+ pr_debug("[WSM] Join timed out.\n");
+ wsm_lock_tx(priv);
+ if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(priv);
+}
+
+static void cw1200_do_unjoin(struct cw1200_common *priv)
+{
+ struct wsm_reset reset = {
+ .reset_statistics = true,
+ };
+
+ cancel_delayed_work_sync(&priv->join_timeout);
+
+ mutex_lock(&priv->conf_mutex);
+ priv->join_pending = false;
+
+ if (atomic_read(&priv->scan.in_progress)) {
+ if (priv->delayed_unjoin)
+ wiphy_dbg(priv->hw->wiphy, "Delayed unjoin is already scheduled.\n");
+ else
+ priv->delayed_unjoin = true;
+ goto done;
+ }
+
+ priv->delayed_link_loss = false;
+
+ if (!priv->join_status)
+ goto done;
+
+ if (priv->join_status > CW1200_JOIN_STATUS_IBSS) {
+ wiphy_err(priv->hw->wiphy, "Unexpected: join status: %d\n",
+ priv->join_status);
+ BUG_ON(1);
+ }
+
+ cancel_work_sync(&priv->update_filtering_work);
+ cancel_work_sync(&priv->set_beacon_wakeup_period_work);
+ priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+
+ /* Unjoin is a reset. */
+ wsm_flush_tx(priv);
+ wsm_keep_alive_period(priv, 0);
+ wsm_reset(priv, &reset);
+ wsm_set_output_power(priv, priv->output_power * 10);
+ priv->join_dtim_period = 0;
+ cw1200_setup_mac(priv);
+ cw1200_free_event_queue(priv);
+ cancel_work_sync(&priv->event_handler);
+ cw1200_update_listening(priv, priv->listening);
+ cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
+
+ /* Disable Block ACKs */
+ wsm_set_block_ack_policy(priv, 0, 0);
+
+ priv->disable_beacon_filter = false;
+ cw1200_update_filtering(priv);
+ memset(&priv->association_mode, 0,
+ sizeof(priv->association_mode));
+ memset(&priv->bss_params, 0, sizeof(priv->bss_params));
+ priv->setbssparams_done = false;
+ memset(&priv->firmware_ps_mode, 0,
+ sizeof(priv->firmware_ps_mode));
+
+ pr_debug("[STA] Unjoin completed.\n");
+
+done:
+ mutex_unlock(&priv->conf_mutex);
+}
+
+void cw1200_unjoin_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, unjoin_work);
+
+ cw1200_do_unjoin(priv);
+
+ /* Tell the stack we're dead */
+ ieee80211_connection_loss(priv->vif);
+
+ wsm_unlock_tx(priv);
+}
+
+int cw1200_enable_listening(struct cw1200_common *priv)
+{
+ struct wsm_start start = {
+ .mode = WSM_START_MODE_P2P_DEV,
+ .band = WSM_PHY_BAND_2_4G,
+ .beacon_interval = 100,
+ .dtim_period = 1,
+ .probe_delay = 0,
+ .basic_rate_set = 0x0F,
+ };
+
+ if (priv->channel) {
+ start.band = priv->channel->band == IEEE80211_BAND_5GHZ ?
+ WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
+ start.channel_number = priv->channel->hw_value;
+ } else {
+ start.band = WSM_PHY_BAND_2_4G;
+ start.channel_number = 1;
+ }
+
+ return wsm_start(priv, &start);
+}
+
+int cw1200_disable_listening(struct cw1200_common *priv)
+{
+ int ret;
+ struct wsm_reset reset = {
+ .reset_statistics = true,
+ };
+ ret = wsm_reset(priv, &reset);
+ return ret;
+}
+
+void cw1200_update_listening(struct cw1200_common *priv, bool enabled)
+{
+ if (enabled) {
+ if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) {
+ if (!cw1200_enable_listening(priv))
+ priv->join_status = CW1200_JOIN_STATUS_MONITOR;
+ wsm_set_probe_responder(priv, true);
+ }
+ } else {
+ if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
+ if (!cw1200_disable_listening(priv))
+ priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+ wsm_set_probe_responder(priv, false);
+ }
+ }
+}
+
+int cw1200_set_uapsd_param(struct cw1200_common *priv,
+ const struct wsm_edca_params *arg)
+{
+ int ret;
+ u16 uapsd_flags = 0;
+
+ /* Here's the mapping AC [queue, bit]
+ * VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0]
+ */
+
+ if (arg->uapsd_enable[0])
+ uapsd_flags |= 1 << 3;
+
+ if (arg->uapsd_enable[1])
+ uapsd_flags |= 1 << 2;
+
+ if (arg->uapsd_enable[2])
+ uapsd_flags |= 1 << 1;
+
+ if (arg->uapsd_enable[3])
+ uapsd_flags |= 1;
+
+ /* Currently pseudo U-APSD operation is not supported, so setting
+ * MinAutoTriggerInterval, MaxAutoTriggerInterval and
+ * AutoTriggerStep to 0
+ */
+
+ priv->uapsd_info.uapsd_flags = cpu_to_le16(uapsd_flags);
+ priv->uapsd_info.min_auto_trigger_interval = 0;
+ priv->uapsd_info.max_auto_trigger_interval = 0;
+ priv->uapsd_info.auto_trigger_step = 0;
+
+ ret = wsm_set_uapsd_info(priv, &priv->uapsd_info);
+ return ret;
+}
+
+/* ******************************************************************** */
+/* AP API */
+
+int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct cw1200_common *priv = hw->priv;
+ struct cw1200_sta_priv *sta_priv =
+ (struct cw1200_sta_priv *)&sta->drv_priv;
+ struct cw1200_link_entry *entry;
+ struct sk_buff *skb;
+
+ if (priv->mode != NL80211_IFTYPE_AP)
+ return 0;
+
+ sta_priv->link_id = cw1200_find_link_id(priv, sta->addr);
+ if (WARN_ON(!sta_priv->link_id)) {
+ wiphy_info(priv->hw->wiphy,
+ "[AP] No more link IDs available.\n");
+ return -ENOENT;
+ }
+
+ entry = &priv->link_id_db[sta_priv->link_id - 1];
+ spin_lock_bh(&priv->ps_state_lock);
+ if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) ==
+ IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
+ priv->sta_asleep_mask |= BIT(sta_priv->link_id);
+ entry->status = CW1200_LINK_HARD;
+ while ((skb = skb_dequeue(&entry->rx_queue)))
+ ieee80211_rx_irqsafe(priv->hw, skb);
+ spin_unlock_bh(&priv->ps_state_lock);
+ return 0;
+}
+
+int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct cw1200_common *priv = hw->priv;
+ struct cw1200_sta_priv *sta_priv =
+ (struct cw1200_sta_priv *)&sta->drv_priv;
+ struct cw1200_link_entry *entry;
+
+ if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id)
+ return 0;
+
+ entry = &priv->link_id_db[sta_priv->link_id - 1];
+ spin_lock_bh(&priv->ps_state_lock);
+ entry->status = CW1200_LINK_RESERVE;
+ entry->timestamp = jiffies;
+ wsm_lock_tx_async(priv);
+ if (queue_work(priv->workqueue, &priv->link_id_work) <= 0)
+ wsm_unlock_tx(priv);
+ spin_unlock_bh(&priv->ps_state_lock);
+ flush_workqueue(priv->workqueue);
+ return 0;
+}
+
+static void __cw1200_sta_notify(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd notify_cmd,
+ int link_id)
+{
+ struct cw1200_common *priv = dev->priv;
+ u32 bit, prev;
+
+ /* Zero link id means "for all link IDs" */
+ if (link_id)
+ bit = BIT(link_id);
+ else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE))
+ bit = 0;
+ else
+ bit = priv->link_id_map;
+ prev = priv->sta_asleep_mask & bit;
+
+ switch (notify_cmd) {
+ case STA_NOTIFY_SLEEP:
+ if (!prev) {
+ if (priv->buffered_multicasts &&
+ !priv->sta_asleep_mask)
+ queue_work(priv->workqueue,
+ &priv->multicast_start_work);
+ priv->sta_asleep_mask |= bit;
+ }
+ break;
+ case STA_NOTIFY_AWAKE:
+ if (prev) {
+ priv->sta_asleep_mask &= ~bit;
+ priv->pspoll_mask &= ~bit;
+ if (priv->tx_multicast && link_id &&
+ !priv->sta_asleep_mask)
+ queue_work(priv->workqueue,
+ &priv->multicast_stop_work);
+ cw1200_bh_wakeup(priv);
+ }
+ break;
+ }
+}
+
+void cw1200_sta_notify(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd notify_cmd,
+ struct ieee80211_sta *sta)
+{
+ struct cw1200_common *priv = dev->priv;
+ struct cw1200_sta_priv *sta_priv =
+ (struct cw1200_sta_priv *)&sta->drv_priv;
+
+ spin_lock_bh(&priv->ps_state_lock);
+ __cw1200_sta_notify(dev, vif, notify_cmd, sta_priv->link_id);
+ spin_unlock_bh(&priv->ps_state_lock);
+}
+
+static void cw1200_ps_notify(struct cw1200_common *priv,
+ int link_id, bool ps)
+{
+ if (link_id > CW1200_MAX_STA_IN_AP_MODE)
+ return;
+
+ pr_debug("%s for LinkId: %d. STAs asleep: %.8X\n",
+ ps ? "Stop" : "Start",
+ link_id, priv->sta_asleep_mask);
+
+ __cw1200_sta_notify(priv->hw, priv->vif,
+ ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id);
+}
+
+static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set)
+{
+ struct sk_buff *skb;
+ struct wsm_update_ie update_ie = {
+ .what = WSM_UPDATE_IE_BEACON,
+ .count = 1,
+ };
+ u16 tim_offset, tim_length;
+
+ pr_debug("[AP] mcast: %s.\n", aid0_bit_set ? "ena" : "dis");
+
+ skb = ieee80211_beacon_get_tim(priv->hw, priv->vif,
+ &tim_offset, &tim_length);
+ if (!skb) {
+ if (!__cw1200_flush(priv, true))
+ wsm_unlock_tx(priv);
+ return -ENOENT;
+ }
+
+ if (tim_offset && tim_length >= 6) {
+ /* Ignore DTIM count from mac80211:
+ * firmware handles DTIM internally.
+ */
+ skb->data[tim_offset + 2] = 0;
+
+ /* Set/reset aid0 bit */
+ if (aid0_bit_set)
+ skb->data[tim_offset + 4] |= 1;
+ else
+ skb->data[tim_offset + 4] &= ~1;
+ }
+
+ update_ie.ies = &skb->data[tim_offset];
+ update_ie.length = tim_length;
+ wsm_update_ie(priv, &update_ie);
+
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+void cw1200_set_tim_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, set_tim_work);
+ (void)cw1200_set_tim_impl(priv, priv->aid0_bit_set);
+}
+
+int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
+ bool set)
+{
+ struct cw1200_common *priv = dev->priv;
+ queue_work(priv->workqueue, &priv->set_tim_work);
+ return 0;
+}
+
+void cw1200_set_cts_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, set_cts_work);
+
+ u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0};
+ struct wsm_update_ie update_ie = {
+ .what = WSM_UPDATE_IE_BEACON,
+ .count = 1,
+ .ies = erp_ie,
+ .length = 3,
+ };
+ u32 erp_info;
+ __le32 use_cts_prot;
+ mutex_lock(&priv->conf_mutex);
+ erp_info = priv->erp_info;
+ mutex_unlock(&priv->conf_mutex);
+ use_cts_prot =
+ erp_info & WLAN_ERP_USE_PROTECTION ?
+ __cpu_to_le32(1) : 0;
+
+ erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info;
+
+ pr_debug("[STA] ERP information 0x%x\n", erp_info);
+
+ wsm_write_mib(priv, WSM_MIB_ID_NON_ERP_PROTECTION,
+ &use_cts_prot, sizeof(use_cts_prot));
+ wsm_update_ie(priv, &update_ie);
+
+ return;
+}
+
+static int cw1200_set_btcoexinfo(struct cw1200_common *priv)
+{
+ struct wsm_override_internal_txrate arg;
+ int ret = 0;
+
+ if (priv->mode == NL80211_IFTYPE_STATION) {
+ /* Plumb PSPOLL and NULL template */
+ cw1200_upload_pspoll(priv);
+ cw1200_upload_null(priv);
+ cw1200_upload_qosnull(priv);
+ } else {
+ return 0;
+ }
+
+ memset(&arg, 0, sizeof(struct wsm_override_internal_txrate));
+
+ if (!priv->vif->p2p) {
+ /* STATION mode */
+ if (priv->bss_params.operational_rate_set & ~0xF) {
+ pr_debug("[STA] STA has ERP rates\n");
+ /* G or BG mode */
+ arg.internalTxRate = (__ffs(
+ priv->bss_params.operational_rate_set & ~0xF));
+ } else {
+ pr_debug("[STA] STA has non ERP rates\n");
+ /* B only mode */
+ arg.internalTxRate = (__ffs(priv->association_mode.basic_rate_set));
+ }
+ arg.nonErpInternalTxRate = (__ffs(priv->association_mode.basic_rate_set));
+ } else {
+ /* P2P mode */
+ arg.internalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF));
+ arg.nonErpInternalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF));
+ }
+
+ pr_debug("[STA] BTCOEX_INFO MODE %d, internalTxRate : %x, nonErpInternalTxRate: %x\n",
+ priv->mode,
+ arg.internalTxRate,
+ arg.nonErpInternalTxRate);
+
+ ret = wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
+ &arg, sizeof(arg));
+
+ return ret;
+}
+
+void cw1200_bss_info_changed(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+ u32 changed)
+{
+ struct cw1200_common *priv = dev->priv;
+ bool do_join = false;
+
+ mutex_lock(&priv->conf_mutex);
+
+ pr_debug("BSS CHANGED: %08x\n", changed);
+
+ /* TODO: BSS_CHANGED_QOS */
+ /* TODO: BSS_CHANGED_TXPOWER */
+
+ if (changed & BSS_CHANGED_ARP_FILTER) {
+ struct wsm_mib_arp_ipv4_filter filter = {0};
+ int i;
+
+ pr_debug("[STA] BSS_CHANGED_ARP_FILTER cnt: %d\n",
+ info->arp_addr_cnt);
+
+ /* Currently only one IP address is supported by firmware.
+ * In case of more IPs arp filtering will be disabled.
+ */
+ if (info->arp_addr_cnt > 0 &&
+ info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) {
+ for (i = 0; i < info->arp_addr_cnt; i++) {
+ filter.ipv4addrs[i] = info->arp_addr_list[i];
+ pr_debug("[STA] addr[%d]: 0x%X\n",
+ i, filter.ipv4addrs[i]);
+ }
+ filter.enable = __cpu_to_le32(1);
+ }
+
+ pr_debug("[STA] arp ip filter enable: %d\n",
+ __le32_to_cpu(filter.enable));
+
+ wsm_set_arp_ipv4_filter(priv, &filter);
+ }
+
+ if (changed &
+ (BSS_CHANGED_BEACON |
+ BSS_CHANGED_AP_PROBE_RESP |
+ BSS_CHANGED_BSSID |
+ BSS_CHANGED_SSID |
+ BSS_CHANGED_IBSS)) {
+ pr_debug("BSS_CHANGED_BEACON\n");
+ priv->beacon_int = info->beacon_int;
+ cw1200_update_beaconing(priv);
+ cw1200_upload_beacon(priv);
+ }
+
+ if (changed & BSS_CHANGED_BEACON_ENABLED) {
+ pr_debug("BSS_CHANGED_BEACON_ENABLED (%d)\n", info->enable_beacon);
+
+ if (priv->enable_beacon != info->enable_beacon) {
+ cw1200_enable_beaconing(priv, info->enable_beacon);
+ priv->enable_beacon = info->enable_beacon;
+ }
+ }
+
+ if (changed & BSS_CHANGED_BEACON_INT) {
+ pr_debug("CHANGED_BEACON_INT\n");
+ if (info->ibss_joined)
+ do_join = true;
+ else if (priv->join_status == CW1200_JOIN_STATUS_AP)
+ cw1200_update_beaconing(priv);
+ }
+
+ /* assoc/disassoc, or maybe AID changed */
+ if (changed & BSS_CHANGED_ASSOC) {
+ wsm_lock_tx(priv);
+ priv->wep_default_key_id = -1;
+ wsm_unlock_tx(priv);
+ }
+
+ if (changed & BSS_CHANGED_BSSID) {
+ pr_debug("BSS_CHANGED_BSSID\n");
+ do_join = true;
+ }
+
+ if (changed &
+ (BSS_CHANGED_ASSOC |
+ BSS_CHANGED_BSSID |
+ BSS_CHANGED_IBSS |
+ BSS_CHANGED_BASIC_RATES |
+ BSS_CHANGED_HT)) {
+ pr_debug("BSS_CHANGED_ASSOC\n");
+ if (info->assoc) {
+ if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA) {
+ ieee80211_connection_loss(vif);
+ mutex_unlock(&priv->conf_mutex);
+ return;
+ } else if (priv->join_status == CW1200_JOIN_STATUS_PRE_STA) {
+ priv->join_status = CW1200_JOIN_STATUS_STA;
+ }
+ } else {
+ do_join = true;
+ }
+
+ if (info->assoc || info->ibss_joined) {
+ struct ieee80211_sta *sta = NULL;
+ u32 val = 0;
+
+ if (info->dtim_period)
+ priv->join_dtim_period = info->dtim_period;
+ priv->beacon_int = info->beacon_int;
+
+ rcu_read_lock();
+
+ if (info->bssid && !info->ibss_joined)
+ sta = ieee80211_find_sta(vif, info->bssid);
+ if (sta) {
+ priv->ht_info.ht_cap = sta->ht_cap;
+ priv->bss_params.operational_rate_set =
+ cw1200_rate_mask_to_wsm(priv,
+ sta->supp_rates[priv->channel->band]);
+ priv->ht_info.channel_type = cfg80211_get_chandef_type(&dev->conf.chandef);
+ priv->ht_info.operation_mode = info->ht_operation_mode;
+ } else {
+ memset(&priv->ht_info, 0,
+ sizeof(priv->ht_info));
+ priv->bss_params.operational_rate_set = -1;
+ }
+ rcu_read_unlock();
+
+ /* Non Greenfield stations present */
+ if (priv->ht_info.operation_mode &
+ IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT)
+ val |= WSM_NON_GREENFIELD_STA_PRESENT;
+
+ /* Set HT protection method */
+ val |= (priv->ht_info.operation_mode & IEEE80211_HT_OP_MODE_PROTECTION) << 2;
+
+ /* TODO:
+ * STBC_param.dual_cts
+ * STBC_param.LSIG_TXOP_FILL
+ */
+
+ val = cpu_to_le32(val);
+ wsm_write_mib(priv, WSM_MIB_ID_SET_HT_PROTECTION,
+ &val, sizeof(val));
+
+ priv->association_mode.greenfield =
+ cw1200_ht_greenfield(&priv->ht_info);
+ priv->association_mode.flags =
+ WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES |
+ WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE |
+ WSM_ASSOCIATION_MODE_USE_HT_MODE |
+ WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET |
+ WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING;
+ priv->association_mode.preamble =
+ info->use_short_preamble ?
+ WSM_JOIN_PREAMBLE_SHORT :
+ WSM_JOIN_PREAMBLE_LONG;
+ priv->association_mode.basic_rate_set = __cpu_to_le32(
+ cw1200_rate_mask_to_wsm(priv,
+ info->basic_rates));
+ priv->association_mode.mpdu_start_spacing =
+ cw1200_ht_ampdu_density(&priv->ht_info);
+
+ cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
+ cancel_work_sync(&priv->unjoin_work);
+
+ priv->bss_params.beacon_lost_count = priv->cqm_beacon_loss_count;
+ priv->bss_params.aid = info->aid;
+
+ if (priv->join_dtim_period < 1)
+ priv->join_dtim_period = 1;
+
+ pr_debug("[STA] DTIM %d, interval: %d\n",
+ priv->join_dtim_period, priv->beacon_int);
+ pr_debug("[STA] Preamble: %d, Greenfield: %d, Aid: %d, Rates: 0x%.8X, Basic: 0x%.8X\n",
+ priv->association_mode.preamble,
+ priv->association_mode.greenfield,
+ priv->bss_params.aid,
+ priv->bss_params.operational_rate_set,
+ priv->association_mode.basic_rate_set);
+ wsm_set_association_mode(priv, &priv->association_mode);
+
+ if (!info->ibss_joined) {
+ wsm_keep_alive_period(priv, 30 /* sec */);
+ wsm_set_bss_params(priv, &priv->bss_params);
+ priv->setbssparams_done = true;
+ cw1200_set_beacon_wakeup_period_work(&priv->set_beacon_wakeup_period_work);
+ cw1200_set_pm(priv, &priv->powersave_mode);
+ }
+ if (priv->vif->p2p) {
+ pr_debug("[STA] Setting p2p powersave configuration.\n");
+ wsm_set_p2p_ps_modeinfo(priv,
+ &priv->p2p_ps_modeinfo);
+ }
+ if (priv->bt_present)
+ cw1200_set_btcoexinfo(priv);
+ } else {
+ memset(&priv->association_mode, 0,
+ sizeof(priv->association_mode));
+ memset(&priv->bss_params, 0, sizeof(priv->bss_params));
+ }
+ }
+
+ /* ERP Protection */
+ if (changed & (BSS_CHANGED_ASSOC |
+ BSS_CHANGED_ERP_CTS_PROT |
+ BSS_CHANGED_ERP_PREAMBLE)) {
+ u32 prev_erp_info = priv->erp_info;
+ if (info->use_cts_prot)
+ priv->erp_info |= WLAN_ERP_USE_PROTECTION;
+ else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT))
+ priv->erp_info &= ~WLAN_ERP_USE_PROTECTION;
+
+ if (info->use_short_preamble)
+ priv->erp_info |= WLAN_ERP_BARKER_PREAMBLE;
+ else
+ priv->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE;
+
+ pr_debug("[STA] ERP Protection: %x\n", priv->erp_info);
+
+ if (prev_erp_info != priv->erp_info)
+ queue_work(priv->workqueue, &priv->set_cts_work);
+ }
+
+ /* ERP Slottime */
+ if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) {
+ __le32 slot_time = info->use_short_slot ?
+ __cpu_to_le32(9) : __cpu_to_le32(20);
+ pr_debug("[STA] Slot time: %d us.\n",
+ __le32_to_cpu(slot_time));
+ wsm_write_mib(priv, WSM_MIB_ID_DOT11_SLOT_TIME,
+ &slot_time, sizeof(slot_time));
+ }
+
+ if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) {
+ struct wsm_rcpi_rssi_threshold threshold = {
+ .rollingAverageCount = 8,
+ };
+ pr_debug("[CQM] RSSI threshold subscribe: %d +- %d\n",
+ info->cqm_rssi_thold, info->cqm_rssi_hyst);
+ priv->cqm_rssi_thold = info->cqm_rssi_thold;
+ priv->cqm_rssi_hyst = info->cqm_rssi_hyst;
+
+ if (info->cqm_rssi_thold || info->cqm_rssi_hyst) {
+ /* RSSI subscription enabled */
+ /* TODO: It's not a correct way of setting threshold.
+ * Upper and lower must be set equal here and adjusted
+ * in callback. However current implementation is much
+ * more relaible and stable.
+ */
+
+ /* RSSI: signed Q8.0, RCPI: unsigned Q7.1
+ * RSSI = RCPI / 2 - 110
+ */
+ if (priv->cqm_use_rssi) {
+ threshold.upperThreshold =
+ info->cqm_rssi_thold + info->cqm_rssi_hyst;
+ threshold.lowerThreshold =
+ info->cqm_rssi_thold;
+ threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI;
+ } else {
+ threshold.upperThreshold = (info->cqm_rssi_thold + info->cqm_rssi_hyst + 110) * 2;
+ threshold.lowerThreshold = (info->cqm_rssi_thold + 110) * 2;
+ }
+ threshold.rssiRcpiMode |= WSM_RCPI_RSSI_THRESHOLD_ENABLE;
+ } else {
+ /* There is a bug in FW, see sta.c. We have to enable
+ * dummy subscription to get correct RSSI values.
+ */
+ threshold.rssiRcpiMode |=
+ WSM_RCPI_RSSI_THRESHOLD_ENABLE |
+ WSM_RCPI_RSSI_DONT_USE_UPPER |
+ WSM_RCPI_RSSI_DONT_USE_LOWER;
+ if (priv->cqm_use_rssi)
+ threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI;
+ }
+ wsm_set_rcpi_rssi_threshold(priv, &threshold);
+ }
+ mutex_unlock(&priv->conf_mutex);
+
+ if (do_join) {
+ wsm_lock_tx(priv);
+ cw1200_do_join(priv); /* Will unlock it for us */
+ }
+}
+
+void cw1200_multicast_start_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, multicast_start_work);
+ long tmo = priv->join_dtim_period *
+ (priv->beacon_int + 20) * HZ / 1024;
+
+ cancel_work_sync(&priv->multicast_stop_work);
+
+ if (!priv->aid0_bit_set) {
+ wsm_lock_tx(priv);
+ cw1200_set_tim_impl(priv, true);
+ priv->aid0_bit_set = true;
+ mod_timer(&priv->mcast_timeout, jiffies + tmo);
+ wsm_unlock_tx(priv);
+ }
+}
+
+void cw1200_multicast_stop_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, multicast_stop_work);
+
+ if (priv->aid0_bit_set) {
+ del_timer_sync(&priv->mcast_timeout);
+ wsm_lock_tx(priv);
+ priv->aid0_bit_set = false;
+ cw1200_set_tim_impl(priv, false);
+ wsm_unlock_tx(priv);
+ }
+}
+
+void cw1200_mcast_timeout(unsigned long arg)
+{
+ struct cw1200_common *priv =
+ (struct cw1200_common *)arg;
+
+ wiphy_warn(priv->hw->wiphy,
+ "Multicast delivery timeout.\n");
+ spin_lock_bh(&priv->ps_state_lock);
+ priv->tx_multicast = priv->aid0_bit_set &&
+ priv->buffered_multicasts;
+ if (priv->tx_multicast)
+ cw1200_bh_wakeup(priv);
+ spin_unlock_bh(&priv->ps_state_lock);
+}
+
+int cw1200_ampdu_action(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum ieee80211_ampdu_mlme_action action,
+ struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+ u8 buf_size)
+{
+ /* Aggregation is implemented fully in firmware,
+ * including block ack negotiation. Do not allow
+ * mac80211 stack to do anything: it interferes with
+ * the firmware.
+ */
+
+ /* Note that we still need this function stubbed. */
+ return -ENOTSUPP;
+}
+
+/* ******************************************************************** */
+/* WSM callback */
+void cw1200_suspend_resume(struct cw1200_common *priv,
+ struct wsm_suspend_resume *arg)
+{
+ pr_debug("[AP] %s: %s\n",
+ arg->stop ? "stop" : "start",
+ arg->multicast ? "broadcast" : "unicast");
+
+ if (arg->multicast) {
+ bool cancel_tmo = false;
+ spin_lock_bh(&priv->ps_state_lock);
+ if (arg->stop) {
+ priv->tx_multicast = false;
+ } else {
+ /* Firmware sends this indication every DTIM if there
+ * is a STA in powersave connected. There is no reason
+ * to suspend, following wakeup will consume much more
+ * power than it could be saved.
+ */
+ cw1200_pm_stay_awake(&priv->pm_state,
+ priv->join_dtim_period *
+ (priv->beacon_int + 20) * HZ / 1024);
+ priv->tx_multicast = (priv->aid0_bit_set &&
+ priv->buffered_multicasts);
+ if (priv->tx_multicast) {
+ cancel_tmo = true;
+ cw1200_bh_wakeup(priv);
+ }
+ }
+ spin_unlock_bh(&priv->ps_state_lock);
+ if (cancel_tmo)
+ del_timer_sync(&priv->mcast_timeout);
+ } else {
+ spin_lock_bh(&priv->ps_state_lock);
+ cw1200_ps_notify(priv, arg->link_id, arg->stop);
+ spin_unlock_bh(&priv->ps_state_lock);
+ if (!arg->stop)
+ cw1200_bh_wakeup(priv);
+ }
+ return;
+}
+
+/* ******************************************************************** */
+/* AP privates */
+
+static int cw1200_upload_beacon(struct cw1200_common *priv)
+{
+ int ret = 0;
+ struct ieee80211_mgmt *mgmt;
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_BEACON,
+ };
+
+ u16 tim_offset;
+ u16 tim_len;
+
+ if (priv->mode == NL80211_IFTYPE_STATION ||
+ priv->mode == NL80211_IFTYPE_MONITOR ||
+ priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+ goto done;
+
+ if (priv->vif->p2p)
+ frame.rate = WSM_TRANSMIT_RATE_6;
+
+ frame.skb = ieee80211_beacon_get_tim(priv->hw, priv->vif,
+ &tim_offset, &tim_len);
+ if (!frame.skb)
+ return -ENOMEM;
+
+ ret = wsm_set_template_frame(priv, &frame);
+
+ if (ret)
+ goto done;
+
+ /* TODO: Distill probe resp; remove TIM
+ * and any other beacon-specific IEs
+ */
+ mgmt = (void *)frame.skb->data;
+ mgmt->frame_control =
+ __cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_PROBE_RESP);
+
+ frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE;
+ if (priv->vif->p2p) {
+ ret = wsm_set_probe_responder(priv, true);
+ } else {
+ ret = wsm_set_template_frame(priv, &frame);
+ wsm_set_probe_responder(priv, false);
+ }
+
+done:
+ dev_kfree_skb(frame.skb);
+
+ return ret;
+}
+
+static int cw1200_upload_pspoll(struct cw1200_common *priv)
+{
+ int ret = 0;
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_PS_POLL,
+ .rate = 0xFF,
+ };
+
+
+ frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif);
+ if (!frame.skb)
+ return -ENOMEM;
+
+ ret = wsm_set_template_frame(priv, &frame);
+
+ dev_kfree_skb(frame.skb);
+
+ return ret;
+}
+
+static int cw1200_upload_null(struct cw1200_common *priv)
+{
+ int ret = 0;
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_NULL,
+ .rate = 0xFF,
+ };
+
+ frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif);
+ if (!frame.skb)
+ return -ENOMEM;
+
+ ret = wsm_set_template_frame(priv, &frame);
+
+ dev_kfree_skb(frame.skb);
+
+ return ret;
+}
+
+static int cw1200_upload_qosnull(struct cw1200_common *priv)
+{
+ int ret = 0;
+ /* TODO: This needs to be implemented
+
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_QOS_NULL,
+ .rate = 0xFF,
+ };
+
+ frame.skb = ieee80211_qosnullfunc_get(priv->hw, priv->vif);
+ if (!frame.skb)
+ return -ENOMEM;
+
+ ret = wsm_set_template_frame(priv, &frame);
+
+ dev_kfree_skb(frame.skb);
+
+ */
+ return ret;
+}
+
+static int cw1200_enable_beaconing(struct cw1200_common *priv,
+ bool enable)
+{
+ struct wsm_beacon_transmit transmit = {
+ .enable_beaconing = enable,
+ };
+
+ return wsm_beacon_transmit(priv, &transmit);
+}
+
+static int cw1200_start_ap(struct cw1200_common *priv)
+{
+ int ret;
+ struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
+ struct wsm_start start = {
+ .mode = priv->vif->p2p ?
+ WSM_START_MODE_P2P_GO : WSM_START_MODE_AP,
+ .band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
+ WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G,
+ .channel_number = priv->channel->hw_value,
+ .beacon_interval = conf->beacon_int,
+ .dtim_period = conf->dtim_period,
+ .preamble = conf->use_short_preamble ?
+ WSM_JOIN_PREAMBLE_SHORT :
+ WSM_JOIN_PREAMBLE_LONG,
+ .probe_delay = 100,
+ .basic_rate_set = cw1200_rate_mask_to_wsm(priv,
+ conf->basic_rates),
+ };
+ struct wsm_operational_mode mode = {
+ .power_mode = cw1200_power_mode,
+ .disable_more_flag_usage = true,
+ };
+
+ memset(start.ssid, 0, sizeof(start.ssid));
+ if (!conf->hidden_ssid) {
+ start.ssid_len = conf->ssid_len;
+ memcpy(start.ssid, conf->ssid, start.ssid_len);
+ }
+
+ priv->beacon_int = conf->beacon_int;
+ priv->join_dtim_period = conf->dtim_period;
+
+ memset(&priv->link_id_db, 0, sizeof(priv->link_id_db));
+
+ pr_debug("[AP] ch: %d(%d), bcn: %d(%d), brt: 0x%.8X, ssid: %.*s.\n",
+ start.channel_number, start.band,
+ start.beacon_interval, start.dtim_period,
+ start.basic_rate_set,
+ start.ssid_len, start.ssid);
+ ret = wsm_start(priv, &start);
+ if (!ret)
+ ret = cw1200_upload_keys(priv);
+ if (!ret && priv->vif->p2p) {
+ pr_debug("[AP] Setting p2p powersave configuration.\n");
+ wsm_set_p2p_ps_modeinfo(priv, &priv->p2p_ps_modeinfo);
+ }
+ if (!ret) {
+ wsm_set_block_ack_policy(priv, 0, 0);
+ priv->join_status = CW1200_JOIN_STATUS_AP;
+ cw1200_update_filtering(priv);
+ }
+ wsm_set_operational_mode(priv, &mode);
+ return ret;
+}
+
+static int cw1200_update_beaconing(struct cw1200_common *priv)
+{
+ struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
+ struct wsm_reset reset = {
+ .link_id = 0,
+ .reset_statistics = true,
+ };
+
+ if (priv->mode == NL80211_IFTYPE_AP) {
+ /* TODO: check if changed channel, band */
+ if (priv->join_status != CW1200_JOIN_STATUS_AP ||
+ priv->beacon_int != conf->beacon_int) {
+ pr_debug("ap restarting\n");
+ wsm_lock_tx(priv);
+ if (priv->join_status != CW1200_JOIN_STATUS_PASSIVE)
+ wsm_reset(priv, &reset);
+ priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
+ cw1200_start_ap(priv);
+ wsm_unlock_tx(priv);
+ } else
+ pr_debug("ap started join_status: %d\n",
+ priv->join_status);
+ }
+ return 0;
+}
diff --git a/drivers/net/wireless/cw1200/sta.h b/drivers/net/wireless/cw1200/sta.h
new file mode 100644
index 00000000000..35babb62cc6
--- /dev/null
+++ b/drivers/net/wireless/cw1200/sta.h
@@ -0,0 +1,123 @@
+/*
+ * Mac80211 STA interface for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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.
+ */
+
+#ifndef STA_H_INCLUDED
+#define STA_H_INCLUDED
+
+/* ******************************************************************** */
+/* mac80211 API */
+
+int cw1200_start(struct ieee80211_hw *dev);
+void cw1200_stop(struct ieee80211_hw *dev);
+int cw1200_add_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif);
+void cw1200_remove_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif);
+int cw1200_change_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ enum nl80211_iftype new_type,
+ bool p2p);
+int cw1200_config(struct ieee80211_hw *dev, u32 changed);
+void cw1200_configure_filter(struct ieee80211_hw *dev,
+ unsigned int changed_flags,
+ unsigned int *total_flags,
+ u64 multicast);
+int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+ u16 queue, const struct ieee80211_tx_queue_params *params);
+int cw1200_get_stats(struct ieee80211_hw *dev,
+ struct ieee80211_low_level_stats *stats);
+int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key);
+
+int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value);
+
+void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop);
+
+u64 cw1200_prepare_multicast(struct ieee80211_hw *hw,
+ struct netdev_hw_addr_list *mc_list);
+
+int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg);
+
+/* ******************************************************************** */
+/* WSM callbacks */
+
+void cw1200_join_complete_cb(struct cw1200_common *priv,
+ struct wsm_join_complete *arg);
+
+/* ******************************************************************** */
+/* WSM events */
+
+void cw1200_free_event_queue(struct cw1200_common *priv);
+void cw1200_event_handler(struct work_struct *work);
+void cw1200_bss_loss_work(struct work_struct *work);
+void cw1200_bss_params_work(struct work_struct *work);
+void cw1200_keep_alive_work(struct work_struct *work);
+void cw1200_tx_failure_work(struct work_struct *work);
+
+void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv, int init, int good,
+ int bad);
+static inline void cw1200_cqm_bssloss_sm(struct cw1200_common *priv,
+ int init, int good, int bad)
+{
+ spin_lock(&priv->bss_loss_lock);
+ __cw1200_cqm_bssloss_sm(priv, init, good, bad);
+ spin_unlock(&priv->bss_loss_lock);
+}
+
+/* ******************************************************************** */
+/* Internal API */
+
+int cw1200_setup_mac(struct cw1200_common *priv);
+void cw1200_join_timeout(struct work_struct *work);
+void cw1200_unjoin_work(struct work_struct *work);
+void cw1200_join_complete_work(struct work_struct *work);
+void cw1200_wep_key_work(struct work_struct *work);
+void cw1200_update_listening(struct cw1200_common *priv, bool enabled);
+void cw1200_update_filtering(struct cw1200_common *priv);
+void cw1200_update_filtering_work(struct work_struct *work);
+void cw1200_set_beacon_wakeup_period_work(struct work_struct *work);
+int cw1200_enable_listening(struct cw1200_common *priv);
+int cw1200_disable_listening(struct cw1200_common *priv);
+int cw1200_set_uapsd_param(struct cw1200_common *priv,
+ const struct wsm_edca_params *arg);
+void cw1200_ba_work(struct work_struct *work);
+void cw1200_ba_timer(unsigned long arg);
+
+/* AP stuffs */
+int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
+ bool set);
+int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+ enum sta_notify_cmd notify_cmd,
+ struct ieee80211_sta *sta);
+void cw1200_bss_info_changed(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+ u32 changed);
+int cw1200_ampdu_action(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum ieee80211_ampdu_mlme_action action,
+ struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+ u8 buf_size);
+
+void cw1200_suspend_resume(struct cw1200_common *priv,
+ struct wsm_suspend_resume *arg);
+void cw1200_set_tim_work(struct work_struct *work);
+void cw1200_set_cts_work(struct work_struct *work);
+void cw1200_multicast_start_work(struct work_struct *work);
+void cw1200_multicast_stop_work(struct work_struct *work);
+void cw1200_mcast_timeout(unsigned long arg);
+
+#endif
diff --git a/drivers/net/wireless/cw1200/txrx.c b/drivers/net/wireless/cw1200/txrx.c
new file mode 100644
index 00000000000..0e40890e199
--- /dev/null
+++ b/drivers/net/wireless/cw1200/txrx.c
@@ -0,0 +1,1474 @@
+/*
+ * Datapath implementation for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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 <net/mac80211.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "cw1200.h"
+#include "wsm.h"
+#include "bh.h"
+#include "sta.h"
+#include "debug.h"
+
+#define CW1200_INVALID_RATE_ID (0xFF)
+
+static int cw1200_handle_action_rx(struct cw1200_common *priv,
+ struct sk_buff *skb);
+static const struct ieee80211_rate *
+cw1200_get_tx_rate(const struct cw1200_common *priv,
+ const struct ieee80211_tx_rate *rate);
+
+/* ******************************************************************** */
+/* TX queue lock / unlock */
+
+static inline void cw1200_tx_queues_lock(struct cw1200_common *priv)
+{
+ int i;
+ for (i = 0; i < 4; ++i)
+ cw1200_queue_lock(&priv->tx_queue[i]);
+}
+
+static inline void cw1200_tx_queues_unlock(struct cw1200_common *priv)
+{
+ int i;
+ for (i = 0; i < 4; ++i)
+ cw1200_queue_unlock(&priv->tx_queue[i]);
+}
+
+/* ******************************************************************** */
+/* TX policy cache implementation */
+
+static void tx_policy_dump(struct tx_policy *policy)
+{
+ pr_debug("[TX policy] %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X: %d\n",
+ policy->raw[0] & 0x0F, policy->raw[0] >> 4,
+ policy->raw[1] & 0x0F, policy->raw[1] >> 4,
+ policy->raw[2] & 0x0F, policy->raw[2] >> 4,
+ policy->raw[3] & 0x0F, policy->raw[3] >> 4,
+ policy->raw[4] & 0x0F, policy->raw[4] >> 4,
+ policy->raw[5] & 0x0F, policy->raw[5] >> 4,
+ policy->raw[6] & 0x0F, policy->raw[6] >> 4,
+ policy->raw[7] & 0x0F, policy->raw[7] >> 4,
+ policy->raw[8] & 0x0F, policy->raw[8] >> 4,
+ policy->raw[9] & 0x0F, policy->raw[9] >> 4,
+ policy->raw[10] & 0x0F, policy->raw[10] >> 4,
+ policy->raw[11] & 0x0F, policy->raw[11] >> 4,
+ policy->defined);
+}
+
+static void tx_policy_build(const struct cw1200_common *priv,
+ /* [out] */ struct tx_policy *policy,
+ struct ieee80211_tx_rate *rates, size_t count)
+{
+ int i, j;
+ unsigned limit = priv->short_frame_max_tx_count;
+ unsigned total = 0;
+ BUG_ON(rates[0].idx < 0);
+ memset(policy, 0, sizeof(*policy));
+
+ /* minstrel is buggy a little bit, so distille
+ * incoming rates first. */
+
+ /* Sort rates in descending order. */
+ for (i = 1; i < count; ++i) {
+ if (rates[i].idx < 0) {
+ count = i;
+ break;
+ }
+ if (rates[i].idx > rates[i - 1].idx) {
+ struct ieee80211_tx_rate tmp = rates[i - 1];
+ rates[i - 1] = rates[i];
+ rates[i] = tmp;
+ }
+ }
+
+ /* Eliminate duplicates. */
+ total = rates[0].count;
+ for (i = 0, j = 1; j < count; ++j) {
+ if (rates[j].idx == rates[i].idx) {
+ rates[i].count += rates[j].count;
+ } else if (rates[j].idx > rates[i].idx) {
+ break;
+ } else {
+ ++i;
+ if (i != j)
+ rates[i] = rates[j];
+ }
+ total += rates[j].count;
+ }
+ count = i + 1;
+
+ /* Re-fill policy trying to keep every requested rate and with
+ * respect to the global max tx retransmission count. */
+ if (limit < count)
+ limit = count;
+ if (total > limit) {
+ for (i = 0; i < count; ++i) {
+ int left = count - i - 1;
+ if (rates[i].count > limit - left)
+ rates[i].count = limit - left;
+ limit -= rates[i].count;
+ }
+ }
+
+ /* HACK!!! Device has problems (at least) switching from
+ * 54Mbps CTS to 1Mbps. This switch takes enormous amount
+ * of time (100-200 ms), leading to valuable throughput drop.
+ * As a workaround, additional g-rates are injected to the
+ * policy.
+ */
+ if (count == 2 && !(rates[0].flags & IEEE80211_TX_RC_MCS) &&
+ rates[0].idx > 4 && rates[0].count > 2 &&
+ rates[1].idx < 2) {
+ /* ">> 1" is an equivalent of "/ 2", but faster */
+ int mid_rate = (rates[0].idx + 4) >> 1;
+
+ /* Decrease number of retries for the initial rate */
+ rates[0].count -= 2;
+
+ if (mid_rate != 4) {
+ /* Keep fallback rate at 1Mbps. */
+ rates[3] = rates[1];
+
+ /* Inject 1 transmission on lowest g-rate */
+ rates[2].idx = 4;
+ rates[2].count = 1;
+ rates[2].flags = rates[1].flags;
+
+ /* Inject 1 transmission on mid-rate */
+ rates[1].idx = mid_rate;
+ rates[1].count = 1;
+
+ /* Fallback to 1 Mbps is a really bad thing,
+ * so let's try to increase probability of
+ * successful transmission on the lowest g rate
+ * even more */
+ if (rates[0].count >= 3) {
+ --rates[0].count;
+ ++rates[2].count;
+ }
+
+ /* Adjust amount of rates defined */
+ count += 2;
+ } else {
+ /* Keep fallback rate at 1Mbps. */
+ rates[2] = rates[1];
+
+ /* Inject 2 transmissions on lowest g-rate */
+ rates[1].idx = 4;
+ rates[1].count = 2;
+
+ /* Adjust amount of rates defined */
+ count += 1;
+ }
+ }
+
+ policy->defined = cw1200_get_tx_rate(priv, &rates[0])->hw_value + 1;
+
+ for (i = 0; i < count; ++i) {
+ register unsigned rateid, off, shift, retries;
+
+ rateid = cw1200_get_tx_rate(priv, &rates[i])->hw_value;
+ off = rateid >> 3; /* eq. rateid / 8 */
+ shift = (rateid & 0x07) << 2; /* eq. (rateid % 8) * 4 */
+
+ retries = rates[i].count;
+ if (retries > 0x0F) {
+ rates[i].count = 0x0f;
+ retries = 0x0F;
+ }
+ policy->tbl[off] |= __cpu_to_le32(retries << shift);
+ policy->retry_count += retries;
+ }
+
+ pr_debug("[TX policy] Policy (%zu): %d:%d, %d:%d, %d:%d, %d:%d, %d:%d\n",
+ count,
+ rates[0].idx, rates[0].count,
+ rates[1].idx, rates[1].count,
+ rates[2].idx, rates[2].count,
+ rates[3].idx, rates[3].count,
+ rates[4].idx, rates[4].count);
+}
+
+static inline bool tx_policy_is_equal(const struct tx_policy *wanted,
+ const struct tx_policy *cached)
+{
+ size_t count = wanted->defined >> 1;
+ if (wanted->defined > cached->defined)
+ return false;
+ if (count) {
+ if (memcmp(wanted->raw, cached->raw, count))
+ return false;
+ }
+ if (wanted->defined & 1) {
+ if ((wanted->raw[count] & 0x0F) != (cached->raw[count] & 0x0F))
+ return false;
+ }
+ return true;
+}
+
+static int tx_policy_find(struct tx_policy_cache *cache,
+ const struct tx_policy *wanted)
+{
+ /* O(n) complexity. Not so good, but there's only 8 entries in
+ * the cache.
+ * Also lru helps to reduce search time. */
+ struct tx_policy_cache_entry *it;
+ /* First search for policy in "used" list */
+ list_for_each_entry(it, &cache->used, link) {
+ if (tx_policy_is_equal(wanted, &it->policy))
+ return it - cache->cache;
+ }
+ /* Then - in "free list" */
+ list_for_each_entry(it, &cache->free, link) {
+ if (tx_policy_is_equal(wanted, &it->policy))
+ return it - cache->cache;
+ }
+ return -1;
+}
+
+static inline void tx_policy_use(struct tx_policy_cache *cache,
+ struct tx_policy_cache_entry *entry)
+{
+ ++entry->policy.usage_count;
+ list_move(&entry->link, &cache->used);
+}
+
+static inline int tx_policy_release(struct tx_policy_cache *cache,
+ struct tx_policy_cache_entry *entry)
+{
+ int ret = --entry->policy.usage_count;
+ if (!ret)
+ list_move(&entry->link, &cache->free);
+ return ret;
+}
+
+void tx_policy_clean(struct cw1200_common *priv)
+{
+ int idx, locked;
+ struct tx_policy_cache *cache = &priv->tx_policy_cache;
+ struct tx_policy_cache_entry *entry;
+
+ cw1200_tx_queues_lock(priv);
+ spin_lock_bh(&cache->lock);
+ locked = list_empty(&cache->free);
+
+ for (idx = 0; idx < TX_POLICY_CACHE_SIZE; idx++) {
+ entry = &cache->cache[idx];
+ /* Policy usage count should be 0 at this time as all queues
+ should be empty */
+ if (WARN_ON(entry->policy.usage_count)) {
+ entry->policy.usage_count = 0;
+ list_move(&entry->link, &cache->free);
+ }
+ memset(&entry->policy, 0, sizeof(entry->policy));
+ }
+ if (locked)
+ cw1200_tx_queues_unlock(priv);
+
+ cw1200_tx_queues_unlock(priv);
+ spin_unlock_bh(&cache->lock);
+}
+
+/* ******************************************************************** */
+/* External TX policy cache API */
+
+void tx_policy_init(struct cw1200_common *priv)
+{
+ struct tx_policy_cache *cache = &priv->tx_policy_cache;
+ int i;
+
+ memset(cache, 0, sizeof(*cache));
+
+ spin_lock_init(&cache->lock);
+ INIT_LIST_HEAD(&cache->used);
+ INIT_LIST_HEAD(&cache->free);
+
+ for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i)
+ list_add(&cache->cache[i].link, &cache->free);
+}
+
+static int tx_policy_get(struct cw1200_common *priv,
+ struct ieee80211_tx_rate *rates,
+ size_t count, bool *renew)
+{
+ int idx;
+ struct tx_policy_cache *cache = &priv->tx_policy_cache;
+ struct tx_policy wanted;
+
+ tx_policy_build(priv, &wanted, rates, count);
+
+ spin_lock_bh(&cache->lock);
+ if (WARN_ON_ONCE(list_empty(&cache->free))) {
+ spin_unlock_bh(&cache->lock);
+ return CW1200_INVALID_RATE_ID;
+ }
+ idx = tx_policy_find(cache, &wanted);
+ if (idx >= 0) {
+ pr_debug("[TX policy] Used TX policy: %d\n", idx);
+ *renew = false;
+ } else {
+ struct tx_policy_cache_entry *entry;
+ *renew = true;
+ /* If policy is not found create a new one
+ * using the oldest entry in "free" list */
+ entry = list_entry(cache->free.prev,
+ struct tx_policy_cache_entry, link);
+ entry->policy = wanted;
+ idx = entry - cache->cache;
+ pr_debug("[TX policy] New TX policy: %d\n", idx);
+ tx_policy_dump(&entry->policy);
+ }
+ tx_policy_use(cache, &cache->cache[idx]);
+ if (list_empty(&cache->free)) {
+ /* Lock TX queues. */
+ cw1200_tx_queues_lock(priv);
+ }
+ spin_unlock_bh(&cache->lock);
+ return idx;
+}
+
+static void tx_policy_put(struct cw1200_common *priv, int idx)
+{
+ int usage, locked;
+ struct tx_policy_cache *cache = &priv->tx_policy_cache;
+
+ spin_lock_bh(&cache->lock);
+ locked = list_empty(&cache->free);
+ usage = tx_policy_release(cache, &cache->cache[idx]);
+ if (locked && !usage) {
+ /* Unlock TX queues. */
+ cw1200_tx_queues_unlock(priv);
+ }
+ spin_unlock_bh(&cache->lock);
+}
+
+static int tx_policy_upload(struct cw1200_common *priv)
+{
+ struct tx_policy_cache *cache = &priv->tx_policy_cache;
+ int i;
+ struct wsm_set_tx_rate_retry_policy arg = {
+ .num = 0,
+ };
+ spin_lock_bh(&cache->lock);
+
+ /* Upload only modified entries. */
+ for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) {
+ struct tx_policy *src = &cache->cache[i].policy;
+ if (src->retry_count && !src->uploaded) {
+ struct wsm_tx_rate_retry_policy *dst =
+ &arg.tbl[arg.num];
+ dst->index = i;
+ dst->short_retries = priv->short_frame_max_tx_count;
+ dst->long_retries = priv->long_frame_max_tx_count;
+
+ dst->flags = WSM_TX_RATE_POLICY_FLAG_TERMINATE_WHEN_FINISHED |
+ WSM_TX_RATE_POLICY_FLAG_COUNT_INITIAL_TRANSMIT;
+ memcpy(dst->rate_count_indices, src->tbl,
+ sizeof(dst->rate_count_indices));
+ src->uploaded = 1;
+ ++arg.num;
+ }
+ }
+ spin_unlock_bh(&cache->lock);
+ cw1200_debug_tx_cache_miss(priv);
+ pr_debug("[TX policy] Upload %d policies\n", arg.num);
+ return wsm_set_tx_rate_retry_policy(priv, &arg);
+}
+
+void tx_policy_upload_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, tx_policy_upload_work);
+
+ pr_debug("[TX] TX policy upload.\n");
+ tx_policy_upload(priv);
+
+ wsm_unlock_tx(priv);
+ cw1200_tx_queues_unlock(priv);
+}
+
+/* ******************************************************************** */
+/* cw1200 TX implementation */
+
+struct cw1200_txinfo {
+ struct sk_buff *skb;
+ unsigned queue;
+ struct ieee80211_tx_info *tx_info;
+ const struct ieee80211_rate *rate;
+ struct ieee80211_hdr *hdr;
+ size_t hdrlen;
+ const u8 *da;
+ struct cw1200_sta_priv *sta_priv;
+ struct ieee80211_sta *sta;
+ struct cw1200_txpriv txpriv;
+};
+
+u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, u32 rates)
+{
+ u32 ret = 0;
+ int i;
+ for (i = 0; i < 32; ++i) {
+ if (rates & BIT(i))
+ ret |= BIT(priv->rates[i].hw_value);
+ }
+ return ret;
+}
+
+static const struct ieee80211_rate *
+cw1200_get_tx_rate(const struct cw1200_common *priv,
+ const struct ieee80211_tx_rate *rate)
+{
+ if (rate->idx < 0)
+ return NULL;
+ if (rate->flags & IEEE80211_TX_RC_MCS)
+ return &priv->mcs_rates[rate->idx];
+ return &priv->hw->wiphy->bands[priv->channel->band]->
+ bitrates[rate->idx];
+}
+
+static int
+cw1200_tx_h_calc_link_ids(struct cw1200_common *priv,
+ struct cw1200_txinfo *t)
+{
+ if (t->sta && t->sta_priv->link_id)
+ t->txpriv.raw_link_id =
+ t->txpriv.link_id =
+ t->sta_priv->link_id;
+ else if (priv->mode != NL80211_IFTYPE_AP)
+ t->txpriv.raw_link_id =
+ t->txpriv.link_id = 0;
+ else if (is_multicast_ether_addr(t->da)) {
+ if (priv->enable_beacon) {
+ t->txpriv.raw_link_id = 0;
+ t->txpriv.link_id = CW1200_LINK_ID_AFTER_DTIM;
+ } else {
+ t->txpriv.raw_link_id = 0;
+ t->txpriv.link_id = 0;
+ }
+ } else {
+ t->txpriv.link_id = cw1200_find_link_id(priv, t->da);
+ if (!t->txpriv.link_id)
+ t->txpriv.link_id = cw1200_alloc_link_id(priv, t->da);
+ if (!t->txpriv.link_id) {
+ wiphy_err(priv->hw->wiphy,
+ "No more link IDs available.\n");
+ return -ENOENT;
+ }
+ t->txpriv.raw_link_id = t->txpriv.link_id;
+ }
+ if (t->txpriv.raw_link_id)
+ priv->link_id_db[t->txpriv.raw_link_id - 1].timestamp =
+ jiffies;
+ if (t->sta && (t->sta->uapsd_queues & BIT(t->queue)))
+ t->txpriv.link_id = CW1200_LINK_ID_UAPSD;
+ return 0;
+}
+
+static void
+cw1200_tx_h_pm(struct cw1200_common *priv,
+ struct cw1200_txinfo *t)
+{
+ if (ieee80211_is_auth(t->hdr->frame_control)) {
+ u32 mask = ~BIT(t->txpriv.raw_link_id);
+ spin_lock_bh(&priv->ps_state_lock);
+ priv->sta_asleep_mask &= mask;
+ priv->pspoll_mask &= mask;
+ spin_unlock_bh(&priv->ps_state_lock);
+ }
+}
+
+static void
+cw1200_tx_h_calc_tid(struct cw1200_common *priv,
+ struct cw1200_txinfo *t)
+{
+ if (ieee80211_is_data_qos(t->hdr->frame_control)) {
+ u8 *qos = ieee80211_get_qos_ctl(t->hdr);
+ t->txpriv.tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK;
+ } else if (ieee80211_is_data(t->hdr->frame_control)) {
+ t->txpriv.tid = 0;
+ }
+}
+
+static int
+cw1200_tx_h_crypt(struct cw1200_common *priv,
+ struct cw1200_txinfo *t)
+{
+ if (!t->tx_info->control.hw_key ||
+ !ieee80211_has_protected(t->hdr->frame_control))
+ return 0;
+
+ t->hdrlen += t->tx_info->control.hw_key->iv_len;
+ skb_put(t->skb, t->tx_info->control.hw_key->icv_len);
+
+ if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
+ skb_put(t->skb, 8); /* MIC space */
+
+ return 0;
+}
+
+static int
+cw1200_tx_h_align(struct cw1200_common *priv,
+ struct cw1200_txinfo *t,
+ u8 *flags)
+{
+ size_t offset = (size_t)t->skb->data & 3;
+
+ if (!offset)
+ return 0;
+
+ if (offset & 1) {
+ wiphy_err(priv->hw->wiphy,
+ "Bug: attempt to transmit a frame with wrong alignment: %zu\n",
+ offset);
+ return -EINVAL;
+ }
+
+ if (skb_headroom(t->skb) < offset) {
+ wiphy_err(priv->hw->wiphy,
+ "Bug: no space allocated for DMA alignment. headroom: %d\n",
+ skb_headroom(t->skb));
+ return -ENOMEM;
+ }
+ skb_push(t->skb, offset);
+ t->hdrlen += offset;
+ t->txpriv.offset += offset;
+ *flags |= WSM_TX_2BYTES_SHIFT;
+ cw1200_debug_tx_align(priv);
+ return 0;
+}
+
+static int
+cw1200_tx_h_action(struct cw1200_common *priv,
+ struct cw1200_txinfo *t)
+{
+ struct ieee80211_mgmt *mgmt =
+ (struct ieee80211_mgmt *)t->hdr;
+ if (ieee80211_is_action(t->hdr->frame_control) &&
+ mgmt->u.action.category == WLAN_CATEGORY_BACK)
+ return 1;
+ else
+ return 0;
+}
+
+/* Add WSM header */
+static struct wsm_tx *
+cw1200_tx_h_wsm(struct cw1200_common *priv,
+ struct cw1200_txinfo *t)
+{
+ struct wsm_tx *wsm;
+
+ if (skb_headroom(t->skb) < sizeof(struct wsm_tx)) {
+ wiphy_err(priv->hw->wiphy,
+ "Bug: no space allocated for WSM header. headroom: %d\n",
+ skb_headroom(t->skb));
+ return NULL;
+ }
+
+ wsm = (struct wsm_tx *)skb_push(t->skb, sizeof(struct wsm_tx));
+ t->txpriv.offset += sizeof(struct wsm_tx);
+ memset(wsm, 0, sizeof(*wsm));
+ wsm->hdr.len = __cpu_to_le16(t->skb->len);
+ wsm->hdr.id = __cpu_to_le16(0x0004);
+ wsm->queue_id = wsm_queue_id_to_wsm(t->queue);
+ return wsm;
+}
+
+/* BT Coex specific handling */
+static void
+cw1200_tx_h_bt(struct cw1200_common *priv,
+ struct cw1200_txinfo *t,
+ struct wsm_tx *wsm)
+{
+ u8 priority = 0;
+
+ if (!priv->bt_present)
+ return;
+
+ if (ieee80211_is_nullfunc(t->hdr->frame_control)) {
+ priority = WSM_EPTA_PRIORITY_MGT;
+ } else if (ieee80211_is_data(t->hdr->frame_control)) {
+ /* Skip LLC SNAP header (+6) */
+ u8 *payload = &t->skb->data[t->hdrlen];
+ u16 *ethertype = (u16 *)&payload[6];
+ if (*ethertype == __be16_to_cpu(ETH_P_PAE))
+ priority = WSM_EPTA_PRIORITY_EAPOL;
+ } else if (ieee80211_is_assoc_req(t->hdr->frame_control) ||
+ ieee80211_is_reassoc_req(t->hdr->frame_control)) {
+ struct ieee80211_mgmt *mgt_frame =
+ (struct ieee80211_mgmt *)t->hdr;
+
+ if (mgt_frame->u.assoc_req.listen_interval <
+ priv->listen_interval) {
+ pr_debug("Modified Listen Interval to %d from %d\n",
+ priv->listen_interval,
+ mgt_frame->u.assoc_req.listen_interval);
+ /* Replace listen interval derieved from
+ * the one read from SDD */
+ mgt_frame->u.assoc_req.listen_interval =
+ priv->listen_interval;
+ }
+ }
+
+ if (!priority) {
+ if (ieee80211_is_action(t->hdr->frame_control))
+ priority = WSM_EPTA_PRIORITY_ACTION;
+ else if (ieee80211_is_mgmt(t->hdr->frame_control))
+ priority = WSM_EPTA_PRIORITY_MGT;
+ else if ((wsm->queue_id == WSM_QUEUE_VOICE))
+ priority = WSM_EPTA_PRIORITY_VOICE;
+ else if ((wsm->queue_id == WSM_QUEUE_VIDEO))
+ priority = WSM_EPTA_PRIORITY_VIDEO;
+ else
+ priority = WSM_EPTA_PRIORITY_DATA;
+ }
+
+ pr_debug("[TX] EPTA priority %d.\n", priority);
+
+ wsm->flags |= priority << 1;
+}
+
+static int
+cw1200_tx_h_rate_policy(struct cw1200_common *priv,
+ struct cw1200_txinfo *t,
+ struct wsm_tx *wsm)
+{
+ bool tx_policy_renew = false;
+
+ t->txpriv.rate_id = tx_policy_get(priv,
+ t->tx_info->control.rates, IEEE80211_TX_MAX_RATES,
+ &tx_policy_renew);
+ if (t->txpriv.rate_id == CW1200_INVALID_RATE_ID)
+ return -EFAULT;
+
+ wsm->flags |= t->txpriv.rate_id << 4;
+
+ t->rate = cw1200_get_tx_rate(priv,
+ &t->tx_info->control.rates[0]),
+ wsm->max_tx_rate = t->rate->hw_value;
+ if (t->rate->flags & IEEE80211_TX_RC_MCS) {
+ if (cw1200_ht_greenfield(&priv->ht_info))
+ wsm->ht_tx_parameters |=
+ __cpu_to_le32(WSM_HT_TX_GREENFIELD);
+ else
+ wsm->ht_tx_parameters |=
+ __cpu_to_le32(WSM_HT_TX_MIXED);
+ }
+
+ if (tx_policy_renew) {
+ pr_debug("[TX] TX policy renew.\n");
+ /* It's not so optimal to stop TX queues every now and then.
+ * Better to reimplement task scheduling with
+ * a counter. TODO. */
+ wsm_lock_tx_async(priv);
+ cw1200_tx_queues_lock(priv);
+ if (queue_work(priv->workqueue,
+ &priv->tx_policy_upload_work) <= 0) {
+ cw1200_tx_queues_unlock(priv);
+ wsm_unlock_tx(priv);
+ }
+ }
+ return 0;
+}
+
+static bool
+cw1200_tx_h_pm_state(struct cw1200_common *priv,
+ struct cw1200_txinfo *t)
+{
+ int was_buffered = 1;
+
+ if (t->txpriv.link_id == CW1200_LINK_ID_AFTER_DTIM &&
+ !priv->buffered_multicasts) {
+ priv->buffered_multicasts = true;
+ if (priv->sta_asleep_mask)
+ queue_work(priv->workqueue,
+ &priv->multicast_start_work);
+ }
+
+ if (t->txpriv.raw_link_id && t->txpriv.tid < CW1200_MAX_TID)
+ was_buffered = priv->link_id_db[t->txpriv.raw_link_id - 1].buffered[t->txpriv.tid]++;
+
+ return !was_buffered;
+}
+
+/* ******************************************************************** */
+
+void cw1200_tx(struct ieee80211_hw *dev,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb)
+{
+ struct cw1200_common *priv = dev->priv;
+ struct cw1200_txinfo t = {
+ .skb = skb,
+ .queue = skb_get_queue_mapping(skb),
+ .tx_info = IEEE80211_SKB_CB(skb),
+ .hdr = (struct ieee80211_hdr *)skb->data,
+ .txpriv.tid = CW1200_MAX_TID,
+ .txpriv.rate_id = CW1200_INVALID_RATE_ID,
+ };
+ struct ieee80211_sta *sta;
+ struct wsm_tx *wsm;
+ bool tid_update = 0;
+ u8 flags = 0;
+ int ret;
+
+ if (priv->bh_error)
+ goto drop;
+
+ t.hdrlen = ieee80211_hdrlen(t.hdr->frame_control);
+ t.da = ieee80211_get_DA(t.hdr);
+ if (control) {
+ t.sta = control->sta;
+ t.sta_priv = (struct cw1200_sta_priv *)&t.sta->drv_priv;
+ }
+
+ if (WARN_ON(t.queue >= 4))
+ goto drop;
+
+ ret = cw1200_tx_h_calc_link_ids(priv, &t);
+ if (ret)
+ goto drop;
+
+ pr_debug("[TX] TX %d bytes (queue: %d, link_id: %d (%d)).\n",
+ skb->len, t.queue, t.txpriv.link_id,
+ t.txpriv.raw_link_id);
+
+ cw1200_tx_h_pm(priv, &t);
+ cw1200_tx_h_calc_tid(priv, &t);
+ ret = cw1200_tx_h_crypt(priv, &t);
+ if (ret)
+ goto drop;
+ ret = cw1200_tx_h_align(priv, &t, &flags);
+ if (ret)
+ goto drop;
+ ret = cw1200_tx_h_action(priv, &t);
+ if (ret)
+ goto drop;
+ wsm = cw1200_tx_h_wsm(priv, &t);
+ if (!wsm) {
+ ret = -ENOMEM;
+ goto drop;
+ }
+ wsm->flags |= flags;
+ cw1200_tx_h_bt(priv, &t, wsm);
+ ret = cw1200_tx_h_rate_policy(priv, &t, wsm);
+ if (ret)
+ goto drop;
+
+ rcu_read_lock();
+ sta = rcu_dereference(t.sta);
+
+ spin_lock_bh(&priv->ps_state_lock);
+ {
+ tid_update = cw1200_tx_h_pm_state(priv, &t);
+ BUG_ON(cw1200_queue_put(&priv->tx_queue[t.queue],
+ t.skb, &t.txpriv));
+ }
+ spin_unlock_bh(&priv->ps_state_lock);
+
+ if (tid_update && sta)
+ ieee80211_sta_set_buffered(sta, t.txpriv.tid, true);
+
+ rcu_read_unlock();
+
+ cw1200_bh_wakeup(priv);
+
+ return;
+
+drop:
+ cw1200_skb_dtor(priv, skb, &t.txpriv);
+ return;
+}
+
+/* ******************************************************************** */
+
+static int cw1200_handle_action_rx(struct cw1200_common *priv,
+ struct sk_buff *skb)
+{
+ struct ieee80211_mgmt *mgmt = (void *)skb->data;
+
+ /* Filter block ACK negotiation: fully controlled by firmware */
+ if (mgmt->u.action.category == WLAN_CATEGORY_BACK)
+ return 1;
+
+ return 0;
+}
+
+static int cw1200_handle_pspoll(struct cw1200_common *priv,
+ struct sk_buff *skb)
+{
+ struct ieee80211_sta *sta;
+ struct ieee80211_pspoll *pspoll = (struct ieee80211_pspoll *)skb->data;
+ int link_id = 0;
+ u32 pspoll_mask = 0;
+ int drop = 1;
+ int i;
+
+ if (priv->join_status != CW1200_JOIN_STATUS_AP)
+ goto done;
+ if (memcmp(priv->vif->addr, pspoll->bssid, ETH_ALEN))
+ goto done;
+
+ rcu_read_lock();
+ sta = ieee80211_find_sta(priv->vif, pspoll->ta);
+ if (sta) {
+ struct cw1200_sta_priv *sta_priv;
+ sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv;
+ link_id = sta_priv->link_id;
+ pspoll_mask = BIT(sta_priv->link_id);
+ }
+ rcu_read_unlock();
+ if (!link_id)
+ goto done;
+
+ priv->pspoll_mask |= pspoll_mask;
+ drop = 0;
+
+ /* Do not report pspols if data for given link id is
+ * queued already. */
+ for (i = 0; i < 4; ++i) {
+ if (cw1200_queue_get_num_queued(&priv->tx_queue[i],
+ pspoll_mask)) {
+ cw1200_bh_wakeup(priv);
+ drop = 1;
+ break;
+ }
+ }
+ pr_debug("[RX] PSPOLL: %s\n", drop ? "local" : "fwd");
+done:
+ return drop;
+}
+
+/* ******************************************************************** */
+
+void cw1200_tx_confirm_cb(struct cw1200_common *priv,
+ int link_id,
+ struct wsm_tx_confirm *arg)
+{
+ u8 queue_id = cw1200_queue_get_queue_id(arg->packet_id);
+ struct cw1200_queue *queue = &priv->tx_queue[queue_id];
+ struct sk_buff *skb;
+ const struct cw1200_txpriv *txpriv;
+
+ pr_debug("[TX] TX confirm: %d, %d.\n",
+ arg->status, arg->ack_failures);
+
+ if (cw1200_itp_tx_running(priv))
+ return;
+
+ if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
+ /* STA is stopped. */
+ return;
+ }
+
+ if (WARN_ON(queue_id >= 4))
+ return;
+
+ if (arg->status)
+ pr_debug("TX failed: %d.\n", arg->status);
+
+ if ((arg->status == WSM_REQUEUE) &&
+ (arg->flags & WSM_TX_STATUS_REQUEUE)) {
+ /* "Requeue" means "implicit suspend" */
+ struct wsm_suspend_resume suspend = {
+ .link_id = link_id,
+ .stop = 1,
+ .multicast = !link_id,
+ };
+ cw1200_suspend_resume(priv, &suspend);
+ wiphy_warn(priv->hw->wiphy, "Requeue for link_id %d (try %d). STAs asleep: 0x%.8X\n",
+ link_id,
+ cw1200_queue_get_generation(arg->packet_id) + 1,
+ priv->sta_asleep_mask);
+ cw1200_queue_requeue(queue, arg->packet_id);
+ spin_lock_bh(&priv->ps_state_lock);
+ if (!link_id) {
+ priv->buffered_multicasts = true;
+ if (priv->sta_asleep_mask) {
+ queue_work(priv->workqueue,
+ &priv->multicast_start_work);
+ }
+ }
+ spin_unlock_bh(&priv->ps_state_lock);
+ } else if (!cw1200_queue_get_skb(queue, arg->packet_id,
+ &skb, &txpriv)) {
+ struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb);
+ int tx_count = arg->ack_failures;
+ u8 ht_flags = 0;
+ int i;
+
+ if (cw1200_ht_greenfield(&priv->ht_info))
+ ht_flags |= IEEE80211_TX_RC_GREEN_FIELD;
+
+ spin_lock(&priv->bss_loss_lock);
+ if (priv->bss_loss_state &&
+ arg->packet_id == priv->bss_loss_confirm_id) {
+ if (arg->status) {
+ /* Recovery failed */
+ __cw1200_cqm_bssloss_sm(priv, 0, 0, 1);
+ } else {
+ /* Recovery succeeded */
+ __cw1200_cqm_bssloss_sm(priv, 0, 1, 0);
+ }
+ }
+ spin_unlock(&priv->bss_loss_lock);
+
+ if (!arg->status) {
+ tx->flags |= IEEE80211_TX_STAT_ACK;
+ ++tx_count;
+ cw1200_debug_txed(priv);
+ if (arg->flags & WSM_TX_STATUS_AGGREGATION) {
+ /* Do not report aggregation to mac80211:
+ * it confuses minstrel a lot. */
+ /* tx->flags |= IEEE80211_TX_STAT_AMPDU; */
+ cw1200_debug_txed_agg(priv);
+ }
+ } else {
+ if (tx_count)
+ ++tx_count;
+ }
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) {
+ if (tx->status.rates[i].count >= tx_count) {
+ tx->status.rates[i].count = tx_count;
+ break;
+ }
+ tx_count -= tx->status.rates[i].count;
+ if (tx->status.rates[i].flags & IEEE80211_TX_RC_MCS)
+ tx->status.rates[i].flags |= ht_flags;
+ }
+
+ for (++i; i < IEEE80211_TX_MAX_RATES; ++i) {
+ tx->status.rates[i].count = 0;
+ tx->status.rates[i].idx = -1;
+ }
+
+ /* Pull off any crypto trailers that we added on */
+ if (tx->control.hw_key) {
+ skb_trim(skb, skb->len - tx->control.hw_key->icv_len);
+ if (tx->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
+ skb_trim(skb, skb->len - 8); /* MIC space */
+ }
+ cw1200_queue_remove(queue, arg->packet_id);
+ }
+ /* XXX TODO: Only wake if there are pending transmits.. */
+ cw1200_bh_wakeup(priv);
+}
+
+static void cw1200_notify_buffered_tx(struct cw1200_common *priv,
+ struct sk_buff *skb, int link_id, int tid)
+{
+ struct ieee80211_sta *sta;
+ struct ieee80211_hdr *hdr;
+ u8 *buffered;
+ u8 still_buffered = 0;
+
+ if (link_id && tid < CW1200_MAX_TID) {
+ buffered = priv->link_id_db
+ [link_id - 1].buffered;
+
+ spin_lock_bh(&priv->ps_state_lock);
+ if (!WARN_ON(!buffered[tid]))
+ still_buffered = --buffered[tid];
+ spin_unlock_bh(&priv->ps_state_lock);
+
+ if (!still_buffered && tid < CW1200_MAX_TID) {
+ hdr = (struct ieee80211_hdr *)skb->data;
+ rcu_read_lock();
+ sta = ieee80211_find_sta(priv->vif, hdr->addr1);
+ if (sta)
+ ieee80211_sta_set_buffered(sta, tid, false);
+ rcu_read_unlock();
+ }
+ }
+}
+
+void cw1200_skb_dtor(struct cw1200_common *priv,
+ struct sk_buff *skb,
+ const struct cw1200_txpriv *txpriv)
+{
+ skb_pull(skb, txpriv->offset);
+ if (txpriv->rate_id != CW1200_INVALID_RATE_ID) {
+ cw1200_notify_buffered_tx(priv, skb,
+ txpriv->raw_link_id, txpriv->tid);
+ tx_policy_put(priv, txpriv->rate_id);
+ }
+ if (!cw1200_is_itp(priv))
+ ieee80211_tx_status(priv->hw, skb);
+}
+
+void cw1200_rx_cb(struct cw1200_common *priv,
+ struct wsm_rx *arg,
+ int link_id,
+ struct sk_buff **skb_p)
+{
+ struct sk_buff *skb = *skb_p;
+ struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb);
+ struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+ struct cw1200_link_entry *entry = NULL;
+ unsigned long grace_period;
+
+ bool early_data = false;
+ bool p2p = priv->vif && priv->vif->p2p;
+ size_t hdrlen;
+ hdr->flag = 0;
+
+ if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
+ /* STA is stopped. */
+ goto drop;
+ }
+
+ if (link_id && link_id <= CW1200_MAX_STA_IN_AP_MODE) {
+ entry = &priv->link_id_db[link_id - 1];
+ if (entry->status == CW1200_LINK_SOFT &&
+ ieee80211_is_data(frame->frame_control))
+ early_data = true;
+ entry->timestamp = jiffies;
+ } else if (p2p &&
+ ieee80211_is_action(frame->frame_control) &&
+ (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) {
+ pr_debug("[RX] Going to MAP&RESET link ID\n");
+ WARN_ON(work_pending(&priv->linkid_reset_work));
+ memcpy(&priv->action_frame_sa[0],
+ ieee80211_get_SA(frame), ETH_ALEN);
+ priv->action_linkid = 0;
+ schedule_work(&priv->linkid_reset_work);
+ }
+
+ if (link_id && p2p &&
+ ieee80211_is_action(frame->frame_control) &&
+ (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) {
+ /* Link ID already exists for the ACTION frame.
+ * Reset and Remap */
+ WARN_ON(work_pending(&priv->linkid_reset_work));
+ memcpy(&priv->action_frame_sa[0],
+ ieee80211_get_SA(frame), ETH_ALEN);
+ priv->action_linkid = link_id;
+ schedule_work(&priv->linkid_reset_work);
+ }
+ if (arg->status) {
+ if (arg->status == WSM_STATUS_MICFAILURE) {
+ pr_debug("[RX] MIC failure.\n");
+ hdr->flag |= RX_FLAG_MMIC_ERROR;
+ } else if (arg->status == WSM_STATUS_NO_KEY_FOUND) {
+ pr_debug("[RX] No key found.\n");
+ goto drop;
+ } else {
+ pr_debug("[RX] Receive failure: %d.\n",
+ arg->status);
+ goto drop;
+ }
+ }
+
+ if (skb->len < sizeof(struct ieee80211_pspoll)) {
+ wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed. Size is lesser than IEEE header.\n");
+ goto drop;
+ }
+
+ if (ieee80211_is_pspoll(frame->frame_control))
+ if (cw1200_handle_pspoll(priv, skb))
+ goto drop;
+
+ hdr->mactime = 0; /* Not supported by WSM */
+ hdr->band = ((arg->channel_number & 0xff00) ||
+ (arg->channel_number > 14)) ?
+ IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ;
+ hdr->freq = ieee80211_channel_to_frequency(
+ arg->channel_number,
+ hdr->band);
+
+ if (arg->rx_rate >= 14) {
+ hdr->flag |= RX_FLAG_HT;
+ hdr->rate_idx = arg->rx_rate - 14;
+ } else if (arg->rx_rate >= 4) {
+ hdr->rate_idx = arg->rx_rate - 2;
+ } else {
+ hdr->rate_idx = arg->rx_rate;
+ }
+
+ hdr->signal = (s8)arg->rcpi_rssi;
+ hdr->antenna = 0;
+
+ hdrlen = ieee80211_hdrlen(frame->frame_control);
+
+ if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) {
+ size_t iv_len = 0, icv_len = 0;
+
+ hdr->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED;
+
+ /* Oops... There is no fast way to ask mac80211 about
+ * IV/ICV lengths. Even defineas are not exposed.*/
+ switch (WSM_RX_STATUS_ENCRYPTION(arg->flags)) {
+ case WSM_RX_STATUS_WEP:
+ iv_len = 4 /* WEP_IV_LEN */;
+ icv_len = 4 /* WEP_ICV_LEN */;
+ break;
+ case WSM_RX_STATUS_TKIP:
+ iv_len = 8 /* TKIP_IV_LEN */;
+ icv_len = 4 /* TKIP_ICV_LEN */
+ + 8 /*MICHAEL_MIC_LEN*/;
+ hdr->flag |= RX_FLAG_MMIC_STRIPPED;
+ break;
+ case WSM_RX_STATUS_AES:
+ iv_len = 8 /* CCMP_HDR_LEN */;
+ icv_len = 8 /* CCMP_MIC_LEN */;
+ break;
+ case WSM_RX_STATUS_WAPI:
+ iv_len = 18 /* WAPI_HDR_LEN */;
+ icv_len = 16 /* WAPI_MIC_LEN */;
+ break;
+ default:
+ pr_warn("Unknown encryption type %d\n",
+ WSM_RX_STATUS_ENCRYPTION(arg->flags));
+ goto drop;
+ }
+
+ /* Firmware strips ICV in case of MIC failure. */
+ if (arg->status == WSM_STATUS_MICFAILURE)
+ icv_len = 0;
+
+ if (skb->len < hdrlen + iv_len + icv_len) {
+ wiphy_warn(priv->hw->wiphy, "Malformed SDU rx'ed. Size is lesser than crypto headers.\n");
+ goto drop;
+ }
+
+ /* Remove IV, ICV and MIC */
+ skb_trim(skb, skb->len - icv_len);
+ memmove(skb->data + iv_len, skb->data, hdrlen);
+ skb_pull(skb, iv_len);
+ }
+
+ /* Remove TSF from the end of frame */
+ if (arg->flags & WSM_RX_STATUS_TSF_INCLUDED) {
+ memcpy(&hdr->mactime, skb->data + skb->len - 8, 8);
+ hdr->mactime = le64_to_cpu(hdr->mactime);
+ if (skb->len >= 8)
+ skb_trim(skb, skb->len - 8);
+ }
+
+ cw1200_debug_rxed(priv);
+ if (arg->flags & WSM_RX_STATUS_AGGREGATE)
+ cw1200_debug_rxed_agg(priv);
+
+ if (ieee80211_is_action(frame->frame_control) &&
+ (arg->flags & WSM_RX_STATUS_ADDRESS1)) {
+ if (cw1200_handle_action_rx(priv, skb))
+ return;
+ } else if (ieee80211_is_beacon(frame->frame_control) &&
+ !arg->status &&
+ !memcmp(ieee80211_get_SA(frame), priv->vif->bss_conf.bssid,
+ ETH_ALEN)) {
+ const u8 *tim_ie;
+ u8 *ies = ((struct ieee80211_mgmt *)
+ (skb->data))->u.beacon.variable;
+ size_t ies_len = skb->len - (ies - (u8 *)(skb->data));
+
+ tim_ie = cfg80211_find_ie(WLAN_EID_TIM, ies, ies_len);
+ if (tim_ie) {
+ struct ieee80211_tim_ie *tim =
+ (struct ieee80211_tim_ie *)&tim_ie[2];
+
+ if (priv->join_dtim_period != tim->dtim_period) {
+ priv->join_dtim_period = tim->dtim_period;
+ queue_work(priv->workqueue,
+ &priv->set_beacon_wakeup_period_work);
+ }
+ }
+
+ /* Disable beacon filter once we're associated... */
+ if (priv->disable_beacon_filter &&
+ (priv->vif->bss_conf.assoc ||
+ priv->vif->bss_conf.ibss_joined)) {
+ priv->disable_beacon_filter = false;
+ queue_work(priv->workqueue,
+ &priv->update_filtering_work);
+ }
+ }
+
+ /* Stay awake after frame is received to give
+ * userspace chance to react and acquire appropriate
+ * wakelock. */
+ if (ieee80211_is_auth(frame->frame_control))
+ grace_period = 5 * HZ;
+ else if (ieee80211_is_deauth(frame->frame_control))
+ grace_period = 5 * HZ;
+ else
+ grace_period = 1 * HZ;
+ cw1200_pm_stay_awake(&priv->pm_state, grace_period);
+
+ if (cw1200_itp_rxed(priv, skb)) {
+ consume_skb(skb);
+ } else if (early_data) {
+ spin_lock_bh(&priv->ps_state_lock);
+ /* Double-check status with lock held */
+ if (entry->status == CW1200_LINK_SOFT)
+ skb_queue_tail(&entry->rx_queue, skb);
+ else
+ ieee80211_rx_irqsafe(priv->hw, skb);
+ spin_unlock_bh(&priv->ps_state_lock);
+ } else {
+ ieee80211_rx_irqsafe(priv->hw, skb);
+ }
+ *skb_p = NULL;
+
+ return;
+
+drop:
+ /* TODO: update failure counters */
+ return;
+}
+
+/* ******************************************************************** */
+/* Security */
+
+int cw1200_alloc_key(struct cw1200_common *priv)
+{
+ int idx;
+
+ idx = ffs(~priv->key_map) - 1;
+ if (idx < 0 || idx > WSM_KEY_MAX_INDEX)
+ return -1;
+
+ priv->key_map |= BIT(idx);
+ priv->keys[idx].index = idx;
+ return idx;
+}
+
+void cw1200_free_key(struct cw1200_common *priv, int idx)
+{
+ BUG_ON(!(priv->key_map & BIT(idx)));
+ memset(&priv->keys[idx], 0, sizeof(priv->keys[idx]));
+ priv->key_map &= ~BIT(idx);
+}
+
+void cw1200_free_keys(struct cw1200_common *priv)
+{
+ memset(&priv->keys, 0, sizeof(priv->keys));
+ priv->key_map = 0;
+}
+
+int cw1200_upload_keys(struct cw1200_common *priv)
+{
+ int idx, ret = 0;
+ for (idx = 0; idx <= WSM_KEY_MAX_INDEX; ++idx)
+ if (priv->key_map & BIT(idx)) {
+ ret = wsm_add_key(priv, &priv->keys[idx]);
+ if (ret < 0)
+ break;
+ }
+ return ret;
+}
+
+/* Workaround for WFD test case 6.1.10 */
+void cw1200_link_id_reset(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, linkid_reset_work);
+ int temp_linkid;
+
+ if (!priv->action_linkid) {
+ /* In GO mode we can receive ACTION frames without a linkID */
+ temp_linkid = cw1200_alloc_link_id(priv,
+ &priv->action_frame_sa[0]);
+ WARN_ON(!temp_linkid);
+ if (temp_linkid) {
+ /* Make sure we execute the WQ */
+ flush_workqueue(priv->workqueue);
+ /* Release the link ID */
+ spin_lock_bh(&priv->ps_state_lock);
+ priv->link_id_db[temp_linkid - 1].prev_status =
+ priv->link_id_db[temp_linkid - 1].status;
+ priv->link_id_db[temp_linkid - 1].status =
+ CW1200_LINK_RESET;
+ spin_unlock_bh(&priv->ps_state_lock);
+ wsm_lock_tx_async(priv);
+ if (queue_work(priv->workqueue,
+ &priv->link_id_work) <= 0)
+ wsm_unlock_tx(priv);
+ }
+ } else {
+ spin_lock_bh(&priv->ps_state_lock);
+ priv->link_id_db[priv->action_linkid - 1].prev_status =
+ priv->link_id_db[priv->action_linkid - 1].status;
+ priv->link_id_db[priv->action_linkid - 1].status =
+ CW1200_LINK_RESET_REMAP;
+ spin_unlock_bh(&priv->ps_state_lock);
+ wsm_lock_tx_async(priv);
+ if (queue_work(priv->workqueue, &priv->link_id_work) <= 0)
+ wsm_unlock_tx(priv);
+ flush_workqueue(priv->workqueue);
+ }
+}
+
+int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac)
+{
+ int i, ret = 0;
+ spin_lock_bh(&priv->ps_state_lock);
+ for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
+ if (!memcmp(mac, priv->link_id_db[i].mac, ETH_ALEN) &&
+ priv->link_id_db[i].status) {
+ priv->link_id_db[i].timestamp = jiffies;
+ ret = i + 1;
+ break;
+ }
+ }
+ spin_unlock_bh(&priv->ps_state_lock);
+ return ret;
+}
+
+int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac)
+{
+ int i, ret = 0;
+ unsigned long max_inactivity = 0;
+ unsigned long now = jiffies;
+
+ spin_lock_bh(&priv->ps_state_lock);
+ for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
+ if (!priv->link_id_db[i].status) {
+ ret = i + 1;
+ break;
+ } else if (priv->link_id_db[i].status != CW1200_LINK_HARD &&
+ !priv->tx_queue_stats.link_map_cache[i + 1]) {
+ unsigned long inactivity =
+ now - priv->link_id_db[i].timestamp;
+ if (inactivity < max_inactivity)
+ continue;
+ max_inactivity = inactivity;
+ ret = i + 1;
+ }
+ }
+ if (ret) {
+ struct cw1200_link_entry *entry = &priv->link_id_db[ret - 1];
+ pr_debug("[AP] STA added, link_id: %d\n", ret);
+ entry->status = CW1200_LINK_RESERVE;
+ memcpy(&entry->mac, mac, ETH_ALEN);
+ memset(&entry->buffered, 0, CW1200_MAX_TID);
+ skb_queue_head_init(&entry->rx_queue);
+ wsm_lock_tx_async(priv);
+ if (queue_work(priv->workqueue, &priv->link_id_work) <= 0)
+ wsm_unlock_tx(priv);
+ } else {
+ wiphy_info(priv->hw->wiphy,
+ "[AP] Early: no more link IDs available.\n");
+ }
+
+ spin_unlock_bh(&priv->ps_state_lock);
+ return ret;
+}
+
+void cw1200_link_id_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, link_id_work);
+ wsm_flush_tx(priv);
+ cw1200_link_id_gc_work(&priv->link_id_gc_work.work);
+ wsm_unlock_tx(priv);
+}
+
+void cw1200_link_id_gc_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, link_id_gc_work.work);
+ struct wsm_reset reset = {
+ .reset_statistics = false,
+ };
+ struct wsm_map_link map_link = {
+ .link_id = 0,
+ };
+ unsigned long now = jiffies;
+ unsigned long next_gc = -1;
+ long ttl;
+ bool need_reset;
+ u32 mask;
+ int i;
+
+ if (priv->join_status != CW1200_JOIN_STATUS_AP)
+ return;
+
+ wsm_lock_tx(priv);
+ spin_lock_bh(&priv->ps_state_lock);
+ for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
+ need_reset = false;
+ mask = BIT(i + 1);
+ if (priv->link_id_db[i].status == CW1200_LINK_RESERVE ||
+ (priv->link_id_db[i].status == CW1200_LINK_HARD &&
+ !(priv->link_id_map & mask))) {
+ if (priv->link_id_map & mask) {
+ priv->sta_asleep_mask &= ~mask;
+ priv->pspoll_mask &= ~mask;
+ need_reset = true;
+ }
+ priv->link_id_map |= mask;
+ if (priv->link_id_db[i].status != CW1200_LINK_HARD)
+ priv->link_id_db[i].status = CW1200_LINK_SOFT;
+ memcpy(map_link.mac_addr, priv->link_id_db[i].mac,
+ ETH_ALEN);
+ spin_unlock_bh(&priv->ps_state_lock);
+ if (need_reset) {
+ reset.link_id = i + 1;
+ wsm_reset(priv, &reset);
+ }
+ map_link.link_id = i + 1;
+ wsm_map_link(priv, &map_link);
+ next_gc = min(next_gc, CW1200_LINK_ID_GC_TIMEOUT);
+ spin_lock_bh(&priv->ps_state_lock);
+ } else if (priv->link_id_db[i].status == CW1200_LINK_SOFT) {
+ ttl = priv->link_id_db[i].timestamp - now +
+ CW1200_LINK_ID_GC_TIMEOUT;
+ if (ttl <= 0) {
+ need_reset = true;
+ priv->link_id_db[i].status = CW1200_LINK_OFF;
+ priv->link_id_map &= ~mask;
+ priv->sta_asleep_mask &= ~mask;
+ priv->pspoll_mask &= ~mask;
+ memset(map_link.mac_addr, 0, ETH_ALEN);
+ spin_unlock_bh(&priv->ps_state_lock);
+ reset.link_id = i + 1;
+ wsm_reset(priv, &reset);
+ spin_lock_bh(&priv->ps_state_lock);
+ } else {
+ next_gc = min_t(unsigned long, next_gc, ttl);
+ }
+ } else if (priv->link_id_db[i].status == CW1200_LINK_RESET ||
+ priv->link_id_db[i].status ==
+ CW1200_LINK_RESET_REMAP) {
+ int status = priv->link_id_db[i].status;
+ priv->link_id_db[i].status =
+ priv->link_id_db[i].prev_status;
+ priv->link_id_db[i].timestamp = now;
+ reset.link_id = i + 1;
+ spin_unlock_bh(&priv->ps_state_lock);
+ wsm_reset(priv, &reset);
+ if (status == CW1200_LINK_RESET_REMAP) {
+ memcpy(map_link.mac_addr,
+ priv->link_id_db[i].mac,
+ ETH_ALEN);
+ map_link.link_id = i + 1;
+ wsm_map_link(priv, &map_link);
+ next_gc = min(next_gc,
+ CW1200_LINK_ID_GC_TIMEOUT);
+ }
+ spin_lock_bh(&priv->ps_state_lock);
+ }
+ if (need_reset) {
+ skb_queue_purge(&priv->link_id_db[i].rx_queue);
+ pr_debug("[AP] STA removed, link_id: %d\n",
+ reset.link_id);
+ }
+ }
+ spin_unlock_bh(&priv->ps_state_lock);
+ if (next_gc != -1)
+ queue_delayed_work(priv->workqueue,
+ &priv->link_id_gc_work, next_gc);
+ wsm_unlock_tx(priv);
+}
diff --git a/drivers/net/wireless/cw1200/txrx.h b/drivers/net/wireless/cw1200/txrx.h
new file mode 100644
index 00000000000..492a4e14213
--- /dev/null
+++ b/drivers/net/wireless/cw1200/txrx.h
@@ -0,0 +1,106 @@
+/*
+ * Datapath interface for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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.
+ */
+
+#ifndef CW1200_TXRX_H
+#define CW1200_TXRX_H
+
+#include <linux/list.h>
+
+/* extern */ struct ieee80211_hw;
+/* extern */ struct sk_buff;
+/* extern */ struct wsm_tx;
+/* extern */ struct wsm_rx;
+/* extern */ struct wsm_tx_confirm;
+/* extern */ struct cw1200_txpriv;
+
+struct tx_policy {
+ union {
+ __le32 tbl[3];
+ u8 raw[12];
+ };
+ u8 defined;
+ u8 usage_count;
+ u8 retry_count;
+ u8 uploaded;
+};
+
+struct tx_policy_cache_entry {
+ struct tx_policy policy;
+ struct list_head link;
+};
+
+#define TX_POLICY_CACHE_SIZE (8)
+struct tx_policy_cache {
+ struct tx_policy_cache_entry cache[TX_POLICY_CACHE_SIZE];
+ struct list_head used;
+ struct list_head free;
+ spinlock_t lock; /* Protect policy cache */
+};
+
+/* ******************************************************************** */
+/* TX policy cache */
+/* Intention of TX policy cache is an overcomplicated WSM API.
+ * Device does not accept per-PDU tx retry sequence.
+ * It uses "tx retry policy id" instead, so driver code has to sync
+ * linux tx retry sequences with a retry policy table in the device.
+ */
+void tx_policy_init(struct cw1200_common *priv);
+void tx_policy_upload_work(struct work_struct *work);
+void tx_policy_clean(struct cw1200_common *priv);
+
+/* ******************************************************************** */
+/* TX implementation */
+
+u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv,
+ u32 rates);
+void cw1200_tx(struct ieee80211_hw *dev,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb);
+void cw1200_skb_dtor(struct cw1200_common *priv,
+ struct sk_buff *skb,
+ const struct cw1200_txpriv *txpriv);
+
+/* ******************************************************************** */
+/* WSM callbacks */
+
+void cw1200_tx_confirm_cb(struct cw1200_common *priv,
+ int link_id,
+ struct wsm_tx_confirm *arg);
+void cw1200_rx_cb(struct cw1200_common *priv,
+ struct wsm_rx *arg,
+ int link_id,
+ struct sk_buff **skb_p);
+
+/* ******************************************************************** */
+/* Timeout */
+
+void cw1200_tx_timeout(struct work_struct *work);
+
+/* ******************************************************************** */
+/* Security */
+int cw1200_alloc_key(struct cw1200_common *priv);
+void cw1200_free_key(struct cw1200_common *priv, int idx);
+void cw1200_free_keys(struct cw1200_common *priv);
+int cw1200_upload_keys(struct cw1200_common *priv);
+
+/* ******************************************************************** */
+/* Workaround for WFD test case 6.1.10 */
+void cw1200_link_id_reset(struct work_struct *work);
+
+#define CW1200_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ))
+
+int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac);
+int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac);
+void cw1200_link_id_work(struct work_struct *work);
+void cw1200_link_id_gc_work(struct work_struct *work);
+
+
+#endif /* CW1200_TXRX_H */
diff --git a/drivers/net/wireless/cw1200/wsm.c b/drivers/net/wireless/cw1200/wsm.c
new file mode 100644
index 00000000000..4db6cc16879
--- /dev/null
+++ b/drivers/net/wireless/cw1200/wsm.c
@@ -0,0 +1,1884 @@
+/*
+ * WSM host interface (HI) implementation for
+ * ST-Ericsson CW1200 mac80211 drivers.
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * 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/skbuff.h>
+#include <linux/wait.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/random.h>
+
+#include "cw1200.h"
+#include "wsm.h"
+#include "bh.h"
+#include "sta.h"
+#include "debug.h"
+#include "itp.h"
+
+#define WSM_CMD_TIMEOUT (2 * HZ) /* With respect to interrupt loss */
+#define WSM_CMD_START_TIMEOUT (7 * HZ)
+#define WSM_CMD_RESET_TIMEOUT (3 * HZ) /* 2 sec. timeout was observed. */
+#define WSM_CMD_MAX_TIMEOUT (3 * HZ)
+
+#define WSM_SKIP(buf, size) \
+ do { \
+ if ((buf)->data + size > (buf)->end) \
+ goto underflow; \
+ (buf)->data += size; \
+ } while (0)
+
+#define WSM_GET(buf, ptr, size) \
+ do { \
+ if ((buf)->data + size > (buf)->end) \
+ goto underflow; \
+ memcpy(ptr, (buf)->data, size); \
+ (buf)->data += size; \
+ } while (0)
+
+#define __WSM_GET(buf, type, cvt) \
+ ({ \
+ type val; \
+ if ((buf)->data + sizeof(type) > (buf)->end) \
+ goto underflow; \
+ val = cvt(*(type *)(buf)->data); \
+ (buf)->data += sizeof(type); \
+ val; \
+ })
+
+#define WSM_GET8(buf) __WSM_GET(buf, u8, (u8))
+#define WSM_GET16(buf) __WSM_GET(buf, u16, __le16_to_cpu)
+#define WSM_GET32(buf) __WSM_GET(buf, u32, __le32_to_cpu)
+
+#define WSM_PUT(buf, ptr, size) \
+ do { \
+ if ((buf)->data + size > (buf)->end) \
+ if (wsm_buf_reserve((buf), size)) \
+ goto nomem; \
+ memcpy((buf)->data, ptr, size); \
+ (buf)->data += size; \
+ } while (0)
+
+#define __WSM_PUT(buf, val, type, cvt) \
+ do { \
+ if ((buf)->data + sizeof(type) > (buf)->end) \
+ if (wsm_buf_reserve((buf), sizeof(type))) \
+ goto nomem; \
+ *(type *)(buf)->data = cvt(val); \
+ (buf)->data += sizeof(type); \
+ } while (0)
+
+#define WSM_PUT8(buf, val) __WSM_PUT(buf, val, u8, (u8))
+#define WSM_PUT16(buf, val) __WSM_PUT(buf, val, u16, __cpu_to_le16)
+#define WSM_PUT32(buf, val) __WSM_PUT(buf, val, u32, __cpu_to_le32)
+
+static void wsm_buf_reset(struct wsm_buf *buf);
+static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size);
+
+static int wsm_cmd_send(struct cw1200_common *priv,
+ struct wsm_buf *buf,
+ void *arg, u16 cmd, long tmo);
+
+#define wsm_cmd_lock(__priv) mutex_lock(&((__priv)->wsm_cmd_mux))
+#define wsm_cmd_unlock(__priv) mutex_unlock(&((__priv)->wsm_cmd_mux))
+
+/* ******************************************************************** */
+/* WSM API implementation */
+
+static int wsm_generic_confirm(struct cw1200_common *priv,
+ void *arg,
+ struct wsm_buf *buf)
+{
+ u32 status = WSM_GET32(buf);
+ if (status != WSM_STATUS_SUCCESS)
+ return -EINVAL;
+ return 0;
+
+underflow:
+ WARN_ON(1);
+ return -EINVAL;
+}
+
+int wsm_configuration(struct cw1200_common *priv, struct wsm_configuration *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT32(buf, arg->dot11MaxTransmitMsduLifeTime);
+ WSM_PUT32(buf, arg->dot11MaxReceiveLifeTime);
+ WSM_PUT32(buf, arg->dot11RtsThreshold);
+
+ /* DPD block. */
+ WSM_PUT16(buf, arg->dpdData_size + 12);
+ WSM_PUT16(buf, 1); /* DPD version */
+ WSM_PUT(buf, arg->dot11StationId, ETH_ALEN);
+ WSM_PUT16(buf, 5); /* DPD flags */
+ WSM_PUT(buf, arg->dpdData, arg->dpdData_size);
+
+ ret = wsm_cmd_send(priv, buf, arg,
+ WSM_CONFIGURATION_REQ_ID, WSM_CMD_TIMEOUT);
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+static int wsm_configuration_confirm(struct cw1200_common *priv,
+ struct wsm_configuration *arg,
+ struct wsm_buf *buf)
+{
+ int i;
+ int status;
+
+ status = WSM_GET32(buf);
+ if (WARN_ON(status != WSM_STATUS_SUCCESS))
+ return -EINVAL;
+
+ WSM_GET(buf, arg->dot11StationId, ETH_ALEN);
+ arg->dot11FrequencyBandsSupported = WSM_GET8(buf);
+ WSM_SKIP(buf, 1);
+ arg->supportedRateMask = WSM_GET32(buf);
+ for (i = 0; i < 2; ++i) {
+ arg->txPowerRange[i].min_power_level = WSM_GET32(buf);
+ arg->txPowerRange[i].max_power_level = WSM_GET32(buf);
+ arg->txPowerRange[i].stepping = WSM_GET32(buf);
+ }
+ return 0;
+
+underflow:
+ WARN_ON(1);
+ return -EINVAL;
+}
+
+/* ******************************************************************** */
+
+int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+ u16 cmd = WSM_RESET_REQ_ID | WSM_TX_LINK_ID(arg->link_id);
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT32(buf, arg->reset_statistics ? 0 : 1);
+ ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_RESET_TIMEOUT);
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+struct wsm_mib {
+ u16 mib_id;
+ void *buf;
+ size_t buf_size;
+};
+
+int wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *_buf,
+ size_t buf_size)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+ struct wsm_mib mib_buf = {
+ .mib_id = mib_id,
+ .buf = _buf,
+ .buf_size = buf_size,
+ };
+ wsm_cmd_lock(priv);
+
+ WSM_PUT16(buf, mib_id);
+ WSM_PUT16(buf, 0);
+
+ ret = wsm_cmd_send(priv, buf, &mib_buf,
+ WSM_READ_MIB_REQ_ID, WSM_CMD_TIMEOUT);
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+static int wsm_read_mib_confirm(struct cw1200_common *priv,
+ struct wsm_mib *arg,
+ struct wsm_buf *buf)
+{
+ u16 size;
+ if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS))
+ return -EINVAL;
+
+ if (WARN_ON(WSM_GET16(buf) != arg->mib_id))
+ return -EINVAL;
+
+ size = WSM_GET16(buf);
+ if (size > arg->buf_size)
+ size = arg->buf_size;
+
+ WSM_GET(buf, arg->buf, size);
+ arg->buf_size = size;
+ return 0;
+
+underflow:
+ WARN_ON(1);
+ return -EINVAL;
+}
+
+/* ******************************************************************** */
+
+int wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *_buf,
+ size_t buf_size)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+ struct wsm_mib mib_buf = {
+ .mib_id = mib_id,
+ .buf = _buf,
+ .buf_size = buf_size,
+ };
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT16(buf, mib_id);
+ WSM_PUT16(buf, buf_size);
+ WSM_PUT(buf, _buf, buf_size);
+
+ ret = wsm_cmd_send(priv, buf, &mib_buf,
+ WSM_WRITE_MIB_REQ_ID, WSM_CMD_TIMEOUT);
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+static int wsm_write_mib_confirm(struct cw1200_common *priv,
+ struct wsm_mib *arg,
+ struct wsm_buf *buf)
+{
+ int ret;
+
+ ret = wsm_generic_confirm(priv, arg, buf);
+ if (ret)
+ return ret;
+
+ if (arg->mib_id == WSM_MIB_ID_OPERATIONAL_POWER_MODE) {
+ /* OperationalMode: update PM status. */
+ const char *p = arg->buf;
+ cw1200_enable_powersave(priv, (p[0] & 0x0F) ? true : false);
+ }
+ return 0;
+}
+
+/* ******************************************************************** */
+
+int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg)
+{
+ int i;
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ if (arg->num_channels > 48)
+ return -EINVAL;
+
+ if (arg->num_ssids > 2)
+ return -EINVAL;
+
+ if (arg->band > 1)
+ return -EINVAL;
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT8(buf, arg->band);
+ WSM_PUT8(buf, arg->type);
+ WSM_PUT8(buf, arg->flags);
+ WSM_PUT8(buf, arg->max_tx_rate);
+ WSM_PUT32(buf, arg->auto_scan_interval);
+ WSM_PUT8(buf, arg->num_probes);
+ WSM_PUT8(buf, arg->num_channels);
+ WSM_PUT8(buf, arg->num_ssids);
+ WSM_PUT8(buf, arg->probe_delay);
+
+ for (i = 0; i < arg->num_channels; ++i) {
+ WSM_PUT16(buf, arg->ch[i].number);
+ WSM_PUT16(buf, 0);
+ WSM_PUT32(buf, arg->ch[i].min_chan_time);
+ WSM_PUT32(buf, arg->ch[i].max_chan_time);
+ WSM_PUT32(buf, 0);
+ }
+
+ for (i = 0; i < arg->num_ssids; ++i) {
+ WSM_PUT32(buf, arg->ssids[i].length);
+ WSM_PUT(buf, &arg->ssids[i].ssid[0],
+ sizeof(arg->ssids[i].ssid));
+ }
+
+ ret = wsm_cmd_send(priv, buf, NULL,
+ WSM_START_SCAN_REQ_ID, WSM_CMD_TIMEOUT);
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_stop_scan(struct cw1200_common *priv)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+ wsm_cmd_lock(priv);
+ ret = wsm_cmd_send(priv, buf, NULL,
+ WSM_STOP_SCAN_REQ_ID, WSM_CMD_TIMEOUT);
+ wsm_cmd_unlock(priv);
+ return ret;
+}
+
+
+static int wsm_tx_confirm(struct cw1200_common *priv,
+ struct wsm_buf *buf,
+ int link_id)
+{
+ struct wsm_tx_confirm tx_confirm;
+
+ tx_confirm.packet_id = WSM_GET32(buf);
+ tx_confirm.status = WSM_GET32(buf);
+ tx_confirm.tx_rate = WSM_GET8(buf);
+ tx_confirm.ack_failures = WSM_GET8(buf);
+ tx_confirm.flags = WSM_GET16(buf);
+ tx_confirm.media_delay = WSM_GET32(buf);
+ tx_confirm.tx_queue_delay = WSM_GET32(buf);
+
+ cw1200_tx_confirm_cb(priv, link_id, &tx_confirm);
+ return 0;
+
+underflow:
+ WARN_ON(1);
+ return -EINVAL;
+}
+
+static int wsm_multi_tx_confirm(struct cw1200_common *priv,
+ struct wsm_buf *buf, int link_id)
+{
+ int ret;
+ int count;
+ int i;
+
+ count = WSM_GET32(buf);
+ if (WARN_ON(count <= 0))
+ return -EINVAL;
+
+ if (count > 1) {
+ /* We already released one buffer, now for the rest */
+ ret = wsm_release_tx_buffer(priv, count - 1);
+ if (ret < 0)
+ return ret;
+ else if (ret > 0)
+ cw1200_bh_wakeup(priv);
+ }
+
+ cw1200_debug_txed_multi(priv, count);
+ for (i = 0; i < count; ++i) {
+ ret = wsm_tx_confirm(priv, buf, link_id);
+ if (ret)
+ return ret;
+ }
+ return ret;
+
+underflow:
+ WARN_ON(1);
+ return -EINVAL;
+}
+
+/* ******************************************************************** */
+
+static int wsm_join_confirm(struct cw1200_common *priv,
+ struct wsm_join_cnf *arg,
+ struct wsm_buf *buf)
+{
+ arg->status = WSM_GET32(buf);
+ if (WARN_ON(arg->status) != WSM_STATUS_SUCCESS)
+ return -EINVAL;
+
+ arg->min_power_level = WSM_GET32(buf);
+ arg->max_power_level = WSM_GET32(buf);
+
+ return 0;
+
+underflow:
+ WARN_ON(1);
+ return -EINVAL;
+}
+
+int wsm_join(struct cw1200_common *priv, struct wsm_join *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+ struct wsm_join_cnf resp;
+ wsm_cmd_lock(priv);
+
+ WSM_PUT8(buf, arg->mode);
+ WSM_PUT8(buf, arg->band);
+ WSM_PUT16(buf, arg->channel_number);
+ WSM_PUT(buf, &arg->bssid[0], sizeof(arg->bssid));
+ WSM_PUT16(buf, arg->atim_window);
+ WSM_PUT8(buf, arg->preamble_type);
+ WSM_PUT8(buf, arg->probe_for_join);
+ WSM_PUT8(buf, arg->dtim_period);
+ WSM_PUT8(buf, arg->flags);
+ WSM_PUT32(buf, arg->ssid_len);
+ WSM_PUT(buf, &arg->ssid[0], sizeof(arg->ssid));
+ WSM_PUT32(buf, arg->beacon_interval);
+ WSM_PUT32(buf, arg->basic_rate_set);
+
+ priv->tx_burst_idx = -1;
+ ret = wsm_cmd_send(priv, buf, &resp,
+ WSM_JOIN_REQ_ID, WSM_CMD_TIMEOUT);
+ /* TODO: Update state based on resp.min|max_power_level */
+
+ priv->join_complete_status = resp.status;
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_bss_params(struct cw1200_common *priv,
+ const struct wsm_set_bss_params *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT8(buf, (arg->reset_beacon_loss ? 0x1 : 0));
+ WSM_PUT8(buf, arg->beacon_lost_count);
+ WSM_PUT16(buf, arg->aid);
+ WSM_PUT32(buf, arg->operational_rate_set);
+
+ ret = wsm_cmd_send(priv, buf, NULL,
+ WSM_SET_BSS_PARAMS_REQ_ID, WSM_CMD_TIMEOUT);
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT(buf, arg, sizeof(*arg));
+
+ ret = wsm_cmd_send(priv, buf, NULL,
+ WSM_ADD_KEY_REQ_ID, WSM_CMD_TIMEOUT);
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_remove_key(struct cw1200_common *priv, const struct wsm_remove_key *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT8(buf, arg->index);
+ WSM_PUT8(buf, 0);
+ WSM_PUT16(buf, 0);
+
+ ret = wsm_cmd_send(priv, buf, NULL,
+ WSM_REMOVE_KEY_REQ_ID, WSM_CMD_TIMEOUT);
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_tx_queue_params(struct cw1200_common *priv,
+ const struct wsm_set_tx_queue_params *arg, u8 id)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+ u8 queue_id_to_wmm_aci[] = {3, 2, 0, 1};
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT8(buf, queue_id_to_wmm_aci[id]);
+ WSM_PUT8(buf, 0);
+ WSM_PUT8(buf, arg->ackPolicy);
+ WSM_PUT8(buf, 0);
+ WSM_PUT32(buf, arg->maxTransmitLifetime);
+ WSM_PUT16(buf, arg->allowedMediumTime);
+ WSM_PUT16(buf, 0);
+
+ ret = wsm_cmd_send(priv, buf, NULL, 0x0012, WSM_CMD_TIMEOUT);
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_edca_params(struct cw1200_common *priv,
+ const struct wsm_edca_params *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+
+ /* Implemented according to specification. */
+
+ WSM_PUT16(buf, arg->params[3].cwmin);
+ WSM_PUT16(buf, arg->params[2].cwmin);
+ WSM_PUT16(buf, arg->params[1].cwmin);
+ WSM_PUT16(buf, arg->params[0].cwmin);
+
+ WSM_PUT16(buf, arg->params[3].cwmax);
+ WSM_PUT16(buf, arg->params[2].cwmax);
+ WSM_PUT16(buf, arg->params[1].cwmax);
+ WSM_PUT16(buf, arg->params[0].cwmax);
+
+ WSM_PUT8(buf, arg->params[3].aifns);
+ WSM_PUT8(buf, arg->params[2].aifns);
+ WSM_PUT8(buf, arg->params[1].aifns);
+ WSM_PUT8(buf, arg->params[0].aifns);
+
+ WSM_PUT16(buf, arg->params[3].txop_limit);
+ WSM_PUT16(buf, arg->params[2].txop_limit);
+ WSM_PUT16(buf, arg->params[1].txop_limit);
+ WSM_PUT16(buf, arg->params[0].txop_limit);
+
+ WSM_PUT32(buf, arg->params[3].max_rx_lifetime);
+ WSM_PUT32(buf, arg->params[2].max_rx_lifetime);
+ WSM_PUT32(buf, arg->params[1].max_rx_lifetime);
+ WSM_PUT32(buf, arg->params[0].max_rx_lifetime);
+
+ ret = wsm_cmd_send(priv, buf, NULL,
+ WSM_EDCA_PARAMS_REQ_ID, WSM_CMD_TIMEOUT);
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_switch_channel(struct cw1200_common *priv,
+ const struct wsm_switch_channel *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT8(buf, arg->mode);
+ WSM_PUT8(buf, arg->switch_count);
+ WSM_PUT16(buf, arg->channel_number);
+
+ priv->channel_switch_in_progress = 1;
+
+ ret = wsm_cmd_send(priv, buf, NULL,
+ WSM_SWITCH_CHANNEL_REQ_ID, WSM_CMD_TIMEOUT);
+ if (ret)
+ priv->channel_switch_in_progress = 0;
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+ priv->ps_mode_switch_in_progress = 1;
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT8(buf, arg->mode);
+ WSM_PUT8(buf, arg->fast_psm_idle_period);
+ WSM_PUT8(buf, arg->ap_psm_change_period);
+ WSM_PUT8(buf, arg->min_auto_pspoll_period);
+
+ ret = wsm_cmd_send(priv, buf, NULL,
+ WSM_SET_PM_REQ_ID, WSM_CMD_TIMEOUT);
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT8(buf, arg->mode);
+ WSM_PUT8(buf, arg->band);
+ WSM_PUT16(buf, arg->channel_number);
+ WSM_PUT32(buf, arg->ct_window);
+ WSM_PUT32(buf, arg->beacon_interval);
+ WSM_PUT8(buf, arg->dtim_period);
+ WSM_PUT8(buf, arg->preamble);
+ WSM_PUT8(buf, arg->probe_delay);
+ WSM_PUT8(buf, arg->ssid_len);
+ WSM_PUT(buf, arg->ssid, sizeof(arg->ssid));
+ WSM_PUT32(buf, arg->basic_rate_set);
+
+ priv->tx_burst_idx = -1;
+ ret = wsm_cmd_send(priv, buf, NULL,
+ WSM_START_REQ_ID, WSM_CMD_START_TIMEOUT);
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_beacon_transmit(struct cw1200_common *priv,
+ const struct wsm_beacon_transmit *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT32(buf, arg->enable_beaconing ? 1 : 0);
+
+ ret = wsm_cmd_send(priv, buf, NULL,
+ WSM_BEACON_TRANSMIT_REQ_ID, WSM_CMD_TIMEOUT);
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_start_find(struct cw1200_common *priv)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+ ret = wsm_cmd_send(priv, buf, NULL, 0x0019, WSM_CMD_TIMEOUT);
+ wsm_cmd_unlock(priv);
+ return ret;
+}
+
+/* ******************************************************************** */
+
+int wsm_stop_find(struct cw1200_common *priv)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+ ret = wsm_cmd_send(priv, buf, NULL, 0x001A, WSM_CMD_TIMEOUT);
+ wsm_cmd_unlock(priv);
+ return ret;
+}
+
+/* ******************************************************************** */
+
+int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+ u16 cmd = 0x001C | WSM_TX_LINK_ID(arg->link_id);
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT(buf, &arg->mac_addr[0], sizeof(arg->mac_addr));
+ WSM_PUT16(buf, 0);
+
+ ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_TIMEOUT);
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_update_ie(struct cw1200_common *priv,
+ const struct wsm_update_ie *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT16(buf, arg->what);
+ WSM_PUT16(buf, arg->count);
+ WSM_PUT(buf, arg->ies, arg->length);
+
+ ret = wsm_cmd_send(priv, buf, NULL, 0x001B, WSM_CMD_TIMEOUT);
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+int wsm_set_probe_responder(struct cw1200_common *priv, bool enable)
+{
+ priv->rx_filter.probeResponder = enable;
+ return wsm_set_rx_filter(priv, &priv->rx_filter);
+}
+
+/* ******************************************************************** */
+/* WSM indication events implementation */
+const char * const cw1200_fw_types[] = {
+ "ETF",
+ "WFM",
+ "WSM",
+ "HI test",
+ "Platform test"
+};
+
+static int wsm_startup_indication(struct cw1200_common *priv,
+ struct wsm_buf *buf)
+{
+ priv->wsm_caps.input_buffers = WSM_GET16(buf);
+ priv->wsm_caps.input_buffer_size = WSM_GET16(buf);
+ priv->wsm_caps.hw_id = WSM_GET16(buf);
+ priv->wsm_caps.hw_subid = WSM_GET16(buf);
+ priv->wsm_caps.status = WSM_GET16(buf);
+ priv->wsm_caps.fw_cap = WSM_GET16(buf);
+ priv->wsm_caps.fw_type = WSM_GET16(buf);
+ priv->wsm_caps.fw_api = WSM_GET16(buf);
+ priv->wsm_caps.fw_build = WSM_GET16(buf);
+ priv->wsm_caps.fw_ver = WSM_GET16(buf);
+ WSM_GET(buf, priv->wsm_caps.fw_label, sizeof(priv->wsm_caps.fw_label));
+ priv->wsm_caps.fw_label[sizeof(priv->wsm_caps.fw_label) - 1] = 0; /* Do not trust FW too much... */
+
+ if (WARN_ON(priv->wsm_caps.status))
+ return -EINVAL;
+
+ if (WARN_ON(priv->wsm_caps.fw_type > 4))
+ return -EINVAL;
+
+ pr_info("CW1200 WSM init done.\n"
+ " Input buffers: %d x %d bytes\n"
+ " Hardware: %d.%d\n"
+ " %s firmware [%s], ver: %d, build: %d,"
+ " api: %d, cap: 0x%.4X\n",
+ priv->wsm_caps.input_buffers,
+ priv->wsm_caps.input_buffer_size,
+ priv->wsm_caps.hw_id, priv->wsm_caps.hw_subid,
+ cw1200_fw_types[priv->wsm_caps.fw_type],
+ priv->wsm_caps.fw_label, priv->wsm_caps.fw_ver,
+ priv->wsm_caps.fw_build,
+ priv->wsm_caps.fw_api, priv->wsm_caps.fw_cap);
+
+ /* Disable unsupported frequency bands */
+ if (!(priv->wsm_caps.fw_cap & 0x1))
+ priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL;
+ if (!(priv->wsm_caps.fw_cap & 0x2))
+ priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL;
+
+ priv->firmware_ready = 1;
+ wake_up(&priv->wsm_startup_done);
+ return 0;
+
+underflow:
+ WARN_ON(1);
+ return -EINVAL;
+}
+
+static int wsm_receive_indication(struct cw1200_common *priv,
+ int link_id,
+ struct wsm_buf *buf,
+ struct sk_buff **skb_p)
+{
+ struct wsm_rx rx;
+ struct ieee80211_hdr *hdr;
+ size_t hdr_len;
+ __le16 fctl;
+
+ rx.status = WSM_GET32(buf);
+ rx.channel_number = WSM_GET16(buf);
+ rx.rx_rate = WSM_GET8(buf);
+ rx.rcpi_rssi = WSM_GET8(buf);
+ rx.flags = WSM_GET32(buf);
+
+ /* FW Workaround: Drop probe resp or
+ beacon when RSSI is 0
+ */
+ hdr = (struct ieee80211_hdr *)(*skb_p)->data;
+
+ if (!rx.rcpi_rssi &&
+ (ieee80211_is_probe_resp(hdr->frame_control) ||
+ ieee80211_is_beacon(hdr->frame_control)))
+ return 0;
+
+ /* If no RSSI subscription has been made,
+ * convert RCPI to RSSI here
+ */
+ if (!priv->cqm_use_rssi)
+ rx.rcpi_rssi = rx.rcpi_rssi / 2 - 110;
+
+ fctl = *(__le16 *)buf->data;
+ hdr_len = buf->data - buf->begin;
+ skb_pull(*skb_p, hdr_len);
+ if (!rx.status && ieee80211_is_deauth(fctl)) {
+ if (priv->join_status == CW1200_JOIN_STATUS_STA) {
+ /* Shedule unjoin work */
+ pr_debug("[WSM] Issue unjoin command (RX).\n");
+ wsm_lock_tx_async(priv);
+ if (queue_work(priv->workqueue,
+ &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(priv);
+ }
+ }
+ cw1200_rx_cb(priv, &rx, link_id, skb_p);
+ if (*skb_p)
+ skb_push(*skb_p, hdr_len);
+
+ return 0;
+
+underflow:
+ return -EINVAL;
+}
+
+static int wsm_event_indication(struct cw1200_common *priv, struct wsm_buf *buf)
+{
+ int first;
+ struct cw1200_wsm_event *event;
+
+ if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) {
+ /* STA is stopped. */
+ return 0;
+ }
+
+ event = kzalloc(sizeof(struct cw1200_wsm_event), GFP_KERNEL);
+
+ event->evt.id = __le32_to_cpu(WSM_GET32(buf));
+ event->evt.data = __le32_to_cpu(WSM_GET32(buf));
+
+ pr_debug("[WSM] Event: %d(%d)\n",
+ event->evt.id, event->evt.data);
+
+ spin_lock(&priv->event_queue_lock);
+ first = list_empty(&priv->event_queue);
+ list_add_tail(&event->link, &priv->event_queue);
+ spin_unlock(&priv->event_queue_lock);
+
+ if (first)
+ queue_work(priv->workqueue, &priv->event_handler);
+
+ return 0;
+
+underflow:
+ kfree(event);
+ return -EINVAL;
+}
+
+static int wsm_channel_switch_indication(struct cw1200_common *priv,
+ struct wsm_buf *buf)
+{
+ WARN_ON(WSM_GET32(buf));
+
+ priv->channel_switch_in_progress = 0;
+ wake_up(&priv->channel_switch_done);
+
+ wsm_unlock_tx(priv);
+
+ return 0;
+
+underflow:
+ return -EINVAL;
+}
+
+static int wsm_set_pm_indication(struct cw1200_common *priv,
+ struct wsm_buf *buf)
+{
+ /* TODO: Check buf (struct wsm_set_pm_complete) for validity */
+ if (priv->ps_mode_switch_in_progress) {
+ priv->ps_mode_switch_in_progress = 0;
+ wake_up(&priv->ps_mode_switch_done);
+ }
+ return 0;
+}
+
+static int wsm_scan_started(struct cw1200_common *priv, void *arg,
+ struct wsm_buf *buf)
+{
+ u32 status = WSM_GET32(buf);
+ if (status != WSM_STATUS_SUCCESS) {
+ cw1200_scan_failed_cb(priv);
+ return -EINVAL;
+ }
+ return 0;
+
+underflow:
+ WARN_ON(1);
+ return -EINVAL;
+}
+
+static int wsm_scan_complete_indication(struct cw1200_common *priv,
+ struct wsm_buf *buf)
+{
+ struct wsm_scan_complete arg;
+ arg.status = WSM_GET32(buf);
+ arg.psm = WSM_GET8(buf);
+ arg.num_channels = WSM_GET8(buf);
+ cw1200_scan_complete_cb(priv, &arg);
+
+ return 0;
+
+underflow:
+ return -EINVAL;
+}
+
+static int wsm_join_complete_indication(struct cw1200_common *priv,
+ struct wsm_buf *buf)
+{
+ struct wsm_join_complete arg;
+ arg.status = WSM_GET32(buf);
+ pr_debug("[WSM] Join complete indication, status: %d\n", arg.status);
+ cw1200_join_complete_cb(priv, &arg);
+
+ return 0;
+
+underflow:
+ return -EINVAL;
+}
+
+static int wsm_find_complete_indication(struct cw1200_common *priv,
+ struct wsm_buf *buf)
+{
+ pr_warn("Implement find_complete_indication\n");
+ return 0;
+}
+
+static int wsm_ba_timeout_indication(struct cw1200_common *priv,
+ struct wsm_buf *buf)
+{
+ u32 dummy;
+ u8 tid;
+ u8 dummy2;
+ u8 addr[ETH_ALEN];
+
+ dummy = WSM_GET32(buf);
+ tid = WSM_GET8(buf);
+ dummy2 = WSM_GET8(buf);
+ WSM_GET(buf, addr, ETH_ALEN);
+
+ pr_info("BlockACK timeout, tid %d, addr %pM\n",
+ tid, addr);
+
+ return 0;
+
+underflow:
+ return -EINVAL;
+}
+
+static int wsm_suspend_resume_indication(struct cw1200_common *priv,
+ int link_id, struct wsm_buf *buf)
+{
+ u32 flags;
+ struct wsm_suspend_resume arg;
+
+ flags = WSM_GET32(buf);
+ arg.link_id = link_id;
+ arg.stop = !(flags & 1);
+ arg.multicast = !!(flags & 8);
+ arg.queue = (flags >> 1) & 3;
+
+ cw1200_suspend_resume(priv, &arg);
+
+ return 0;
+
+underflow:
+ return -EINVAL;
+}
+
+
+/* ******************************************************************** */
+/* WSM TX */
+
+static int wsm_cmd_send(struct cw1200_common *priv,
+ struct wsm_buf *buf,
+ void *arg, u16 cmd, long tmo)
+{
+ size_t buf_len = buf->data - buf->begin;
+ int ret;
+
+ /* Don't bother if we're dead. */
+ if (priv->bh_error) {
+ ret = 0;
+ goto done;
+ }
+
+ /* Block until the cmd buffer is completed. Tortuous. */
+ spin_lock(&priv->wsm_cmd.lock);
+ while (!priv->wsm_cmd.done) {
+ spin_unlock(&priv->wsm_cmd.lock);
+ spin_lock(&priv->wsm_cmd.lock);
+ }
+ priv->wsm_cmd.done = 0;
+ spin_unlock(&priv->wsm_cmd.lock);
+
+ if (cmd == WSM_WRITE_MIB_REQ_ID ||
+ cmd == WSM_READ_MIB_REQ_ID)
+ pr_debug("[WSM] >>> 0x%.4X [MIB: 0x%.4X] (%zu)\n",
+ cmd, __le16_to_cpu(((__le16 *)buf->begin)[2]),
+ buf_len);
+ else
+ pr_debug("[WSM] >>> 0x%.4X (%zu)\n", cmd, buf_len);
+
+ /*
+ * Due to buggy SPI on CW1200, we need to
+ * pad the message by a few bytes to ensure
+ * that it's completely received.
+ */
+#ifdef CONFIG_CW1200_ETF
+ if (!etf_mode)
+#endif
+ buf_len += 4;
+
+ /* Fill HI message header */
+ /* BH will add sequence number */
+ ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len);
+ ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd);
+
+ spin_lock(&priv->wsm_cmd.lock);
+ BUG_ON(priv->wsm_cmd.ptr);
+ priv->wsm_cmd.ptr = buf->begin;
+ priv->wsm_cmd.len = buf_len;
+ priv->wsm_cmd.arg = arg;
+ priv->wsm_cmd.cmd = cmd;
+ spin_unlock(&priv->wsm_cmd.lock);
+
+ cw1200_bh_wakeup(priv);
+
+ /* Wait for command completion */
+ ret = wait_event_timeout(priv->wsm_cmd_wq,
+ priv->wsm_cmd.done, tmo);
+
+ if (!ret && !priv->wsm_cmd.done) {
+ spin_lock(&priv->wsm_cmd.lock);
+ priv->wsm_cmd.done = 1;
+ priv->wsm_cmd.ptr = NULL;
+ spin_unlock(&priv->wsm_cmd.lock);
+ if (priv->bh_error) {
+ /* Return ok to help system cleanup */
+ ret = 0;
+ } else {
+ pr_err("CMD req (0x%04x) stuck in firmware, killing BH\n", priv->wsm_cmd.cmd);
+ print_hex_dump_bytes("REQDUMP: ", DUMP_PREFIX_NONE,
+ buf->begin, buf_len);
+ pr_err("Outstanding outgoing frames: %d\n", priv->hw_bufs_used);
+
+ /* Kill BH thread to report the error to the top layer. */
+ atomic_add(1, &priv->bh_term);
+ wake_up(&priv->bh_wq);
+ ret = -ETIMEDOUT;
+ }
+ } else {
+ spin_lock(&priv->wsm_cmd.lock);
+ BUG_ON(!priv->wsm_cmd.done);
+ ret = priv->wsm_cmd.ret;
+ spin_unlock(&priv->wsm_cmd.lock);
+ }
+done:
+ wsm_buf_reset(buf);
+ return ret;
+}
+
+#ifdef CONFIG_CW1200_ETF
+int wsm_raw_cmd(struct cw1200_common *priv, u8 *data, size_t len)
+{
+ struct wsm_buf *buf = &priv->wsm_cmd_buf;
+ int ret;
+
+ u16 *cmd = (u16 *)(data + 2);
+
+ wsm_cmd_lock(priv);
+
+ WSM_PUT(buf, data + 4, len - 4); /* Skip over header (u16+u16) */
+
+ ret = wsm_cmd_send(priv, buf, NULL, __le16_to_cpu(*cmd), WSM_CMD_TIMEOUT);
+
+ wsm_cmd_unlock(priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv);
+ return -ENOMEM;
+}
+#endif /* CONFIG_CW1200_ETF */
+
+/* ******************************************************************** */
+/* WSM TX port control */
+
+void wsm_lock_tx(struct cw1200_common *priv)
+{
+ wsm_cmd_lock(priv);
+ if (atomic_add_return(1, &priv->tx_lock) == 1) {
+ if (wsm_flush_tx(priv))
+ pr_debug("[WSM] TX is locked.\n");
+ }
+ wsm_cmd_unlock(priv);
+}
+
+void wsm_lock_tx_async(struct cw1200_common *priv)
+{
+ if (atomic_add_return(1, &priv->tx_lock) == 1)
+ pr_debug("[WSM] TX is locked (async).\n");
+}
+
+bool wsm_flush_tx(struct cw1200_common *priv)
+{
+ unsigned long timestamp = jiffies;
+ bool pending = false;
+ long timeout;
+ int i;
+
+ /* Flush must be called with TX lock held. */
+ BUG_ON(!atomic_read(&priv->tx_lock));
+
+ /* First check if we really need to do something.
+ * It is safe to use unprotected access, as hw_bufs_used
+ * can only decrements.
+ */
+ if (!priv->hw_bufs_used)
+ return true;
+
+ if (priv->bh_error) {
+ /* In case of failure do not wait for magic. */
+ pr_err("[WSM] Fatal error occured, will not flush TX.\n");
+ return false;
+ } else {
+ /* Get a timestamp of "oldest" frame */
+ for (i = 0; i < 4; ++i)
+ pending |= cw1200_queue_get_xmit_timestamp(
+ &priv->tx_queue[i],
+ &timestamp, 0xffffffff);
+ /* If there's nothing pending, we're good */
+ if (!pending)
+ return true;
+
+ timeout = timestamp + WSM_CMD_LAST_CHANCE_TIMEOUT - jiffies;
+ if (timeout < 0 || wait_event_timeout(priv->bh_evt_wq,
+ !priv->hw_bufs_used,
+ timeout) <= 0) {
+ /* Hmmm... Not good. Frame had stuck in firmware. */
+ priv->bh_error = 1;
+ wiphy_err(priv->hw->wiphy, "[WSM] TX Frames (%d) stuck in firmware, killing BH\n", priv->hw_bufs_used);
+ wake_up(&priv->bh_wq);
+ return false;
+ }
+
+ /* Ok, everything is flushed. */
+ return true;
+ }
+}
+
+void wsm_unlock_tx(struct cw1200_common *priv)
+{
+ int tx_lock;
+ tx_lock = atomic_sub_return(1, &priv->tx_lock);
+ BUG_ON(tx_lock < 0);
+
+ if (tx_lock == 0) {
+ if (!priv->bh_error)
+ cw1200_bh_wakeup(priv);
+ pr_debug("[WSM] TX is unlocked.\n");
+ }
+}
+
+/* ******************************************************************** */
+/* WSM RX */
+
+int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len)
+{
+ struct wsm_buf buf;
+ u32 reason;
+ u32 reg[18];
+ char fname[48];
+ unsigned int i;
+
+ static const char * const reason_str[] = {
+ "undefined instruction",
+ "prefetch abort",
+ "data abort",
+ "unknown error",
+ };
+
+ buf.begin = buf.data = data;
+ buf.end = &buf.begin[len];
+
+ reason = WSM_GET32(&buf);
+ for (i = 0; i < ARRAY_SIZE(reg); ++i)
+ reg[i] = WSM_GET32(&buf);
+ WSM_GET(&buf, fname, sizeof(fname));
+
+ if (reason < 4)
+ wiphy_err(priv->hw->wiphy,
+ "Firmware exception: %s.\n",
+ reason_str[reason]);
+ else
+ wiphy_err(priv->hw->wiphy,
+ "Firmware assert at %.*s, line %d\n",
+ (int) sizeof(fname), fname, reg[1]);
+
+ for (i = 0; i < 12; i += 4)
+ wiphy_err(priv->hw->wiphy,
+ "R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X,\n",
+ i + 0, reg[i + 0], i + 1, reg[i + 1],
+ i + 2, reg[i + 2], i + 3, reg[i + 3]);
+ wiphy_err(priv->hw->wiphy,
+ "R12: 0x%.8X, SP: 0x%.8X, LR: 0x%.8X, PC: 0x%.8X,\n",
+ reg[i + 0], reg[i + 1], reg[i + 2], reg[i + 3]);
+ i += 4;
+ wiphy_err(priv->hw->wiphy,
+ "CPSR: 0x%.8X, SPSR: 0x%.8X\n",
+ reg[i + 0], reg[i + 1]);
+
+ print_hex_dump_bytes("R1: ", DUMP_PREFIX_NONE,
+ fname, sizeof(fname));
+ return 0;
+
+underflow:
+ wiphy_err(priv->hw->wiphy, "Firmware exception.\n");
+ print_hex_dump_bytes("Exception: ", DUMP_PREFIX_NONE,
+ data, len);
+ return -EINVAL;
+}
+
+int wsm_handle_rx(struct cw1200_common *priv, u16 id,
+ struct wsm_hdr *wsm, struct sk_buff **skb_p)
+{
+ int ret = 0;
+ struct wsm_buf wsm_buf;
+ int link_id = (id >> 6) & 0x0F;
+
+ /* Strip link id. */
+ id &= ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX);
+
+ wsm_buf.begin = (u8 *)&wsm[0];
+ wsm_buf.data = (u8 *)&wsm[1];
+ wsm_buf.end = &wsm_buf.begin[__le32_to_cpu(wsm->len)];
+
+ pr_debug("[WSM] <<< 0x%.4X (%td)\n", id,
+ wsm_buf.end - wsm_buf.begin);
+
+#ifdef CONFIG_CW1200_ETF
+ if (etf_mode) {
+ struct sk_buff *skb = alloc_skb(wsm_buf.end - wsm_buf.begin, GFP_KERNEL);
+
+ /* Strip out Sequence num before passing up */
+ wsm->id = __le16_to_cpu(wsm->id);
+ wsm->id &= 0x0FFF;
+ wsm->id = __cpu_to_le16(wsm->id);
+
+ memcpy(skb_put(skb, wsm_buf.end - wsm_buf.begin),
+ wsm_buf.begin,
+ wsm_buf.end - wsm_buf.begin);
+ skb_queue_tail(&priv->etf_q, skb);
+
+ /* Special case for startup */
+ if (id == WSM_STARTUP_IND_ID) {
+ wsm_startup_indication(priv, &wsm_buf);
+ } else if (id & 0x0400) {
+ spin_lock(&priv->wsm_cmd.lock);
+ priv->wsm_cmd.done = 1;
+ spin_unlock(&priv->wsm_cmd.lock);
+ wake_up(&priv->wsm_cmd_wq);
+ }
+
+ goto out;
+ }
+#endif
+
+ if (id == WSM_TX_CONFIRM_IND_ID) {
+ ret = wsm_tx_confirm(priv, &wsm_buf, link_id);
+ } else if (id == WSM_MULTI_TX_CONFIRM_ID) {
+ ret = wsm_multi_tx_confirm(priv, &wsm_buf, link_id);
+ } else if (id & 0x0400) {
+ void *wsm_arg;
+ u16 wsm_cmd;
+
+ /* Do not trust FW too much. Protection against repeated
+ * response and race condition removal (see above).
+ */
+ spin_lock(&priv->wsm_cmd.lock);
+ wsm_arg = priv->wsm_cmd.arg;
+ wsm_cmd = priv->wsm_cmd.cmd &
+ ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX);
+ priv->wsm_cmd.cmd = 0xFFFF;
+ spin_unlock(&priv->wsm_cmd.lock);
+
+ if (WARN_ON((id & ~0x0400) != wsm_cmd)) {
+ /* Note that any non-zero is a fatal retcode. */
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Note that wsm_arg can be NULL in case of timeout in
+ * wsm_cmd_send().
+ */
+
+ switch (id) {
+ case WSM_READ_MIB_RESP_ID:
+ if (wsm_arg)
+ ret = wsm_read_mib_confirm(priv, wsm_arg,
+ &wsm_buf);
+ break;
+ case WSM_WRITE_MIB_RESP_ID:
+ if (wsm_arg)
+ ret = wsm_write_mib_confirm(priv, wsm_arg,
+ &wsm_buf);
+ break;
+ case WSM_START_SCAN_RESP_ID:
+ if (wsm_arg)
+ ret = wsm_scan_started(priv, wsm_arg, &wsm_buf);
+ break;
+ case WSM_CONFIGURATION_RESP_ID:
+ if (wsm_arg)
+ ret = wsm_configuration_confirm(priv, wsm_arg,
+ &wsm_buf);
+ break;
+ case WSM_JOIN_RESP_ID:
+ if (wsm_arg)
+ ret = wsm_join_confirm(priv, wsm_arg, &wsm_buf);
+ break;
+ case WSM_STOP_SCAN_RESP_ID:
+ case WSM_RESET_RESP_ID:
+ case WSM_ADD_KEY_RESP_ID:
+ case WSM_REMOVE_KEY_RESP_ID:
+ case WSM_SET_PM_RESP_ID:
+ case WSM_SET_BSS_PARAMS_RESP_ID:
+ case 0x0412: /* set_tx_queue_params */
+ case WSM_EDCA_PARAMS_RESP_ID:
+ case WSM_SWITCH_CHANNEL_RESP_ID:
+ case WSM_START_RESP_ID:
+ case WSM_BEACON_TRANSMIT_RESP_ID:
+ case 0x0419: /* start_find */
+ case 0x041A: /* stop_find */
+ case 0x041B: /* update_ie */
+ case 0x041C: /* map_link */
+ WARN_ON(wsm_arg != NULL);
+ ret = wsm_generic_confirm(priv, wsm_arg, &wsm_buf);
+ if (ret) {
+ wiphy_warn(priv->hw->wiphy,
+ "wsm_generic_confirm failed for request 0x%04x.\n",
+ id & ~0x0400);
+
+ /* often 0x407 and 0x410 occur, this means we're dead.. */
+ if (priv->join_status >= CW1200_JOIN_STATUS_JOINING) {
+ wsm_lock_tx(priv);
+ if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(priv);
+ }
+ }
+ break;
+ default:
+ wiphy_warn(priv->hw->wiphy,
+ "Unrecognized confirmation 0x%04x\n",
+ id & ~0x0400);
+ }
+
+ spin_lock(&priv->wsm_cmd.lock);
+ priv->wsm_cmd.ret = ret;
+ priv->wsm_cmd.done = 1;
+ spin_unlock(&priv->wsm_cmd.lock);
+
+ ret = 0; /* Error response from device should ne stop BH. */
+
+ wake_up(&priv->wsm_cmd_wq);
+ } else if (id & 0x0800) {
+ switch (id) {
+ case WSM_STARTUP_IND_ID:
+ ret = wsm_startup_indication(priv, &wsm_buf);
+ break;
+ case WSM_RECEIVE_IND_ID:
+ ret = wsm_receive_indication(priv, link_id,
+ &wsm_buf, skb_p);
+ break;
+ case 0x0805:
+ ret = wsm_event_indication(priv, &wsm_buf);
+ break;
+ case WSM_SCAN_COMPLETE_IND_ID:
+ ret = wsm_scan_complete_indication(priv, &wsm_buf);
+ break;
+ case 0x0808:
+ ret = wsm_ba_timeout_indication(priv, &wsm_buf);
+ break;
+ case 0x0809:
+ ret = wsm_set_pm_indication(priv, &wsm_buf);
+ break;
+ case 0x080A:
+ ret = wsm_channel_switch_indication(priv, &wsm_buf);
+ break;
+ case 0x080B:
+ ret = wsm_find_complete_indication(priv, &wsm_buf);
+ break;
+ case 0x080C:
+ ret = wsm_suspend_resume_indication(priv,
+ link_id, &wsm_buf);
+ break;
+ case 0x080F:
+ ret = wsm_join_complete_indication(priv, &wsm_buf);
+ break;
+ default:
+ pr_warn("Unrecognised WSM ID %04x\n", id);
+ }
+ } else {
+ WARN_ON(1);
+ ret = -EINVAL;
+ }
+out:
+ return ret;
+}
+
+static bool wsm_handle_tx_data(struct cw1200_common *priv,
+ struct wsm_tx *wsm,
+ const struct ieee80211_tx_info *tx_info,
+ const struct cw1200_txpriv *txpriv,
+ struct cw1200_queue *queue)
+{
+ bool handled = false;
+ const struct ieee80211_hdr *frame =
+ (struct ieee80211_hdr *)&((u8 *)wsm)[txpriv->offset];
+ __le16 fctl = frame->frame_control;
+ enum {
+ do_probe,
+ do_drop,
+ do_wep,
+ do_tx,
+ } action = do_tx;
+
+ switch (priv->mode) {
+ case NL80211_IFTYPE_STATION:
+ if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
+ action = do_tx;
+ else if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA)
+ action = do_drop;
+ break;
+ case NL80211_IFTYPE_AP:
+ if (!priv->join_status) {
+ action = do_drop;
+ } else if (!(BIT(txpriv->raw_link_id) &
+ (BIT(0) | priv->link_id_map))) {
+ wiphy_warn(priv->hw->wiphy,
+ "A frame with expired link id is dropped.\n");
+ action = do_drop;
+ }
+ if (cw1200_queue_get_generation(wsm->packet_id) >
+ CW1200_MAX_REQUEUE_ATTEMPTS) {
+ /* HACK!!! WSM324 firmware has tendency to requeue
+ * multicast frames in a loop, causing performance
+ * drop and high power consumption of the driver.
+ * In this situation it is better just to drop
+ * the problematic frame.
+ */
+ wiphy_warn(priv->hw->wiphy,
+ "Too many attempts to requeue a frame; dropped.\n");
+ action = do_drop;
+ }
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ if (priv->join_status != CW1200_JOIN_STATUS_IBSS)
+ action = do_drop;
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ action = do_tx; /* TODO: Test me! */
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ default:
+ action = do_drop;
+ break;
+ }
+
+ if (action == do_tx) {
+ if (ieee80211_is_nullfunc(fctl)) {
+ spin_lock(&priv->bss_loss_lock);
+ if (priv->bss_loss_state) {
+ priv->bss_loss_confirm_id = wsm->packet_id;
+ wsm->queue_id = WSM_QUEUE_VOICE;
+ }
+ spin_unlock(&priv->bss_loss_lock);
+ } else if (ieee80211_is_probe_req(fctl)) {
+ action = do_probe;
+ } else if (ieee80211_is_deauth(fctl) &&
+ priv->mode != NL80211_IFTYPE_AP) {
+ pr_debug("[WSM] Issue unjoin command due to tx deauth.\n");
+ wsm_lock_tx_async(priv);
+ if (queue_work(priv->workqueue,
+ &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(priv);
+ } else if (ieee80211_has_protected(fctl) &&
+ tx_info->control.hw_key &&
+ tx_info->control.hw_key->keyidx != priv->wep_default_key_id &&
+ (tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP104)) {
+ action = do_wep;
+ }
+ }
+
+ switch (action) {
+ case do_probe:
+ /* An interesting FW "feature". Device filters probe responses.
+ * The easiest way to get it back is to convert
+ * probe request into WSM start_scan command.
+ */
+ pr_debug("[WSM] Convert probe request to scan.\n");
+ wsm_lock_tx_async(priv);
+ priv->pending_frame_id = __le32_to_cpu(wsm->packet_id);
+ if (queue_delayed_work(priv->workqueue,
+ &priv->scan.probe_work, 0) <= 0)
+ wsm_unlock_tx(priv);
+ handled = true;
+ break;
+ case do_drop:
+ pr_debug("[WSM] Drop frame (0x%.4X).\n", fctl);
+ BUG_ON(cw1200_queue_remove(queue,
+ __le32_to_cpu(wsm->packet_id)));
+ handled = true;
+ break;
+ case do_wep:
+ pr_debug("[WSM] Issue set_default_wep_key.\n");
+ wsm_lock_tx_async(priv);
+ priv->wep_default_key_id = tx_info->control.hw_key->keyidx;
+ priv->pending_frame_id = __le32_to_cpu(wsm->packet_id);
+ if (queue_work(priv->workqueue, &priv->wep_key_work) <= 0)
+ wsm_unlock_tx(priv);
+ handled = true;
+ break;
+ case do_tx:
+ pr_debug("[WSM] Transmit frame.\n");
+ break;
+ default:
+ /* Do nothing */
+ break;
+ }
+ return handled;
+}
+
+static int cw1200_get_prio_queue(struct cw1200_common *priv,
+ u32 link_id_map, int *total)
+{
+ static const int urgent = BIT(CW1200_LINK_ID_AFTER_DTIM) |
+ BIT(CW1200_LINK_ID_UAPSD);
+ struct wsm_edca_queue_params *edca;
+ unsigned score, best = -1;
+ int winner = -1;
+ int queued;
+ int i;
+
+ /* search for a winner using edca params */
+ for (i = 0; i < 4; ++i) {
+ queued = cw1200_queue_get_num_queued(&priv->tx_queue[i],
+ link_id_map);
+ if (!queued)
+ continue;
+ *total += queued;
+ edca = &priv->edca.params[i];
+ score = ((edca->aifns + edca->cwmin) << 16) +
+ ((edca->cwmax - edca->cwmin) *
+ (get_random_int() & 0xFFFF));
+ if (score < best && (winner < 0 || i != 3)) {
+ best = score;
+ winner = i;
+ }
+ }
+
+ /* override winner if bursting */
+ if (winner >= 0 && priv->tx_burst_idx >= 0 &&
+ winner != priv->tx_burst_idx &&
+ !cw1200_queue_get_num_queued(
+ &priv->tx_queue[winner],
+ link_id_map & urgent) &&
+ cw1200_queue_get_num_queued(
+ &priv->tx_queue[priv->tx_burst_idx],
+ link_id_map))
+ winner = priv->tx_burst_idx;
+
+ return winner;
+}
+
+static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv,
+ struct cw1200_queue **queue_p,
+ u32 *tx_allowed_mask_p,
+ bool *more)
+{
+ int idx;
+ u32 tx_allowed_mask;
+ int total = 0;
+
+ /* Search for a queue with multicast frames buffered */
+ if (priv->tx_multicast) {
+ tx_allowed_mask = BIT(CW1200_LINK_ID_AFTER_DTIM);
+ idx = cw1200_get_prio_queue(priv,
+ tx_allowed_mask, &total);
+ if (idx >= 0) {
+ *more = total > 1;
+ goto found;
+ }
+ }
+
+ /* Search for unicast traffic */
+ tx_allowed_mask = ~priv->sta_asleep_mask;
+ tx_allowed_mask |= BIT(CW1200_LINK_ID_UAPSD);
+ if (priv->sta_asleep_mask) {
+ tx_allowed_mask |= priv->pspoll_mask;
+ tx_allowed_mask &= ~BIT(CW1200_LINK_ID_AFTER_DTIM);
+ } else {
+ tx_allowed_mask |= BIT(CW1200_LINK_ID_AFTER_DTIM);
+ }
+ idx = cw1200_get_prio_queue(priv,
+ tx_allowed_mask, &total);
+ if (idx < 0)
+ return -ENOENT;
+
+found:
+ *queue_p = &priv->tx_queue[idx];
+ *tx_allowed_mask_p = tx_allowed_mask;
+ return 0;
+}
+
+int wsm_get_tx(struct cw1200_common *priv, u8 **data,
+ size_t *tx_len, int *burst)
+{
+ struct wsm_tx *wsm = NULL;
+ struct ieee80211_tx_info *tx_info;
+ struct cw1200_queue *queue = NULL;
+ int queue_num;
+ u32 tx_allowed_mask = 0;
+ const struct cw1200_txpriv *txpriv = NULL;
+ int count = 0;
+
+ /* More is used only for broadcasts. */
+ bool more = false;
+
+#ifdef CONFIG_CW1200_ITP
+ count = cw1200_itp_get_tx(priv, data, tx_len, burst);
+ if (count)
+ return count;
+#endif
+
+ if (priv->wsm_cmd.ptr) { /* CMD request */
+ ++count;
+ spin_lock(&priv->wsm_cmd.lock);
+ BUG_ON(!priv->wsm_cmd.ptr);
+ *data = priv->wsm_cmd.ptr;
+ *tx_len = priv->wsm_cmd.len;
+ *burst = 1;
+ spin_unlock(&priv->wsm_cmd.lock);
+ } else {
+ for (;;) {
+ int ret;
+
+ if (atomic_add_return(0, &priv->tx_lock))
+ break;
+
+ spin_lock_bh(&priv->ps_state_lock);
+
+ ret = wsm_get_tx_queue_and_mask(priv, &queue,
+ &tx_allowed_mask, &more);
+ queue_num = queue - priv->tx_queue;
+
+ if (priv->buffered_multicasts &&
+ (ret || !more) &&
+ (priv->tx_multicast || !priv->sta_asleep_mask)) {
+ priv->buffered_multicasts = false;
+ if (priv->tx_multicast) {
+ priv->tx_multicast = false;
+ queue_work(priv->workqueue,
+ &priv->multicast_stop_work);
+ }
+ }
+
+ spin_unlock_bh(&priv->ps_state_lock);
+
+ if (ret)
+ break;
+
+ if (cw1200_queue_get(queue,
+ tx_allowed_mask,
+ &wsm, &tx_info, &txpriv))
+ continue;
+
+ if (wsm_handle_tx_data(priv, wsm,
+ tx_info, txpriv, queue))
+ continue; /* Handled by WSM */
+
+ wsm->hdr.id &= __cpu_to_le16(
+ ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX));
+ wsm->hdr.id |= cpu_to_le16(
+ WSM_TX_LINK_ID(txpriv->raw_link_id));
+ priv->pspoll_mask &= ~BIT(txpriv->raw_link_id);
+
+ *data = (u8 *)wsm;
+ *tx_len = __le16_to_cpu(wsm->hdr.len);
+
+ /* allow bursting if txop is set */
+ if (priv->edca.params[queue_num].txop_limit)
+ *burst = min(*burst,
+ (int)cw1200_queue_get_num_queued(queue, tx_allowed_mask) + 1);
+ else
+ *burst = 1;
+
+ /* store index of bursting queue */
+ if (*burst > 1)
+ priv->tx_burst_idx = queue_num;
+ else
+ priv->tx_burst_idx = -1;
+
+ if (more) {
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *)
+ &((u8 *)wsm)[txpriv->offset];
+ /* more buffered multicast/broadcast frames
+ * ==> set MoreData flag in IEEE 802.11 header
+ * to inform PS STAs
+ */
+ hdr->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ }
+
+ pr_debug("[WSM] >>> 0x%.4X (%zu) %p %c\n",
+ 0x0004, *tx_len, *data,
+ wsm->more ? 'M' : ' ');
+ ++count;
+ break;
+ }
+ }
+
+ return count;
+}
+
+void wsm_txed(struct cw1200_common *priv, u8 *data)
+{
+ if (data == priv->wsm_cmd.ptr) {
+ spin_lock(&priv->wsm_cmd.lock);
+ priv->wsm_cmd.ptr = NULL;
+ spin_unlock(&priv->wsm_cmd.lock);
+ }
+}
+
+/* ******************************************************************** */
+/* WSM buffer */
+
+void wsm_buf_init(struct wsm_buf *buf)
+{
+ BUG_ON(buf->begin);
+ buf->begin = kmalloc(FWLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA);
+ buf->end = buf->begin ? &buf->begin[FWLOAD_BLOCK_SIZE] : buf->begin;
+ wsm_buf_reset(buf);
+}
+
+void wsm_buf_deinit(struct wsm_buf *buf)
+{
+ kfree(buf->begin);
+ buf->begin = buf->data = buf->end = NULL;
+}
+
+static void wsm_buf_reset(struct wsm_buf *buf)
+{
+ if (buf->begin) {
+ buf->data = &buf->begin[4];
+ *(u32 *)buf->begin = 0;
+ } else {
+ buf->data = buf->begin;
+ }
+}
+
+static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size)
+{
+ size_t pos = buf->data - buf->begin;
+ size_t size = pos + extra_size;
+
+ size = round_up(size, FWLOAD_BLOCK_SIZE);
+
+ buf->begin = krealloc(buf->begin, size, GFP_KERNEL | GFP_DMA);
+ if (buf->begin) {
+ buf->data = &buf->begin[pos];
+ buf->end = &buf->begin[size];
+ return 0;
+ } else {
+ buf->end = buf->data = buf->begin;
+ return -ENOMEM;
+ }
+}
diff --git a/drivers/net/wireless/cw1200/wsm.h b/drivers/net/wireless/cw1200/wsm.h
new file mode 100644
index 00000000000..8d902d6a7dc
--- /dev/null
+++ b/drivers/net/wireless/cw1200/wsm.h
@@ -0,0 +1,1879 @@
+/*
+ * WSM host interface (HI) interface for ST-Ericsson CW1200 mac80211 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
+ *
+ * Based on CW1200 UMAC WSM API, which is
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Stewart Mathers <stewart.mathers@stericsson.com>
+ *
+ * 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.
+ */
+
+#ifndef CW1200_WSM_H_INCLUDED
+#define CW1200_WSM_H_INCLUDED
+
+#include <linux/spinlock.h>
+
+struct cw1200_common;
+
+/* Bands */
+/* Radio band 2.412 -2.484 GHz. */
+#define WSM_PHY_BAND_2_4G (0)
+
+/* Radio band 4.9375-5.8250 GHz. */
+#define WSM_PHY_BAND_5G (1)
+
+/* Transmit rates */
+/* 1 Mbps ERP-DSSS */
+#define WSM_TRANSMIT_RATE_1 (0)
+
+/* 2 Mbps ERP-DSSS */
+#define WSM_TRANSMIT_RATE_2 (1)
+
+/* 5.5 Mbps ERP-CCK */
+#define WSM_TRANSMIT_RATE_5 (2)
+
+/* 11 Mbps ERP-CCK */
+#define WSM_TRANSMIT_RATE_11 (3)
+
+/* 22 Mbps ERP-PBCC (Not supported) */
+/* #define WSM_TRANSMIT_RATE_22 (4) */
+
+/* 33 Mbps ERP-PBCC (Not supported) */
+/* #define WSM_TRANSMIT_RATE_33 (5) */
+
+/* 6 Mbps (3 Mbps) ERP-OFDM, BPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_6 (6)
+
+/* 9 Mbps (4.5 Mbps) ERP-OFDM, BPSK coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_9 (7)
+
+/* 12 Mbps (6 Mbps) ERP-OFDM, QPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_12 (8)
+
+/* 18 Mbps (9 Mbps) ERP-OFDM, QPSK coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_18 (9)
+
+/* 24 Mbps (12 Mbps) ERP-OFDM, 16QAM coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_24 (10)
+
+/* 36 Mbps (18 Mbps) ERP-OFDM, 16QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_36 (11)
+
+/* 48 Mbps (24 Mbps) ERP-OFDM, 64QAM coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_48 (12)
+
+/* 54 Mbps (27 Mbps) ERP-OFDM, 64QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_54 (13)
+
+/* 6.5 Mbps HT-OFDM, BPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_HT_6 (14)
+
+/* 13 Mbps HT-OFDM, QPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_HT_13 (15)
+
+/* 19.5 Mbps HT-OFDM, QPSK coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_HT_19 (16)
+
+/* 26 Mbps HT-OFDM, 16QAM coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_HT_26 (17)
+
+/* 39 Mbps HT-OFDM, 16QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_HT_39 (18)
+
+/* 52 Mbps HT-OFDM, 64QAM coding rate 2/3 */
+#define WSM_TRANSMIT_RATE_HT_52 (19)
+
+/* 58.5 Mbps HT-OFDM, 64QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_HT_58 (20)
+
+/* 65 Mbps HT-OFDM, 64QAM coding rate 5/6 */
+#define WSM_TRANSMIT_RATE_HT_65 (21)
+
+/* Scan types */
+/* Foreground scan */
+#define WSM_SCAN_TYPE_FOREGROUND (0)
+
+/* Background scan */
+#define WSM_SCAN_TYPE_BACKGROUND (1)
+
+/* Auto scan */
+#define WSM_SCAN_TYPE_AUTO (2)
+
+/* Scan flags */
+/* Forced background scan means if the station cannot */
+/* enter the power-save mode, it shall force to perform a */
+/* background scan. Only valid when ScanType is */
+/* background scan. */
+#define WSM_SCAN_FLAG_FORCE_BACKGROUND (BIT(0))
+
+/* The WLAN device scans one channel at a time so */
+/* that disturbance to the data traffic is minimized. */
+#define WSM_SCAN_FLAG_SPLIT_METHOD (BIT(1))
+
+/* Preamble Type. Long if not set. */
+#define WSM_SCAN_FLAG_SHORT_PREAMBLE (BIT(2))
+
+/* 11n Tx Mode. Mixed if not set. */
+#define WSM_SCAN_FLAG_11N_GREENFIELD (BIT(3))
+
+/* Scan constraints */
+/* Maximum number of channels to be scanned. */
+#define WSM_SCAN_MAX_NUM_OF_CHANNELS (48)
+
+/* The maximum number of SSIDs that the device can scan for. */
+#define WSM_SCAN_MAX_NUM_OF_SSIDS (2)
+
+/* Power management modes */
+/* 802.11 Active mode */
+#define WSM_PSM_ACTIVE (0)
+
+/* 802.11 PS mode */
+#define WSM_PSM_PS BIT(0)
+
+/* Fast Power Save bit */
+#define WSM_PSM_FAST_PS_FLAG BIT(7)
+
+/* Dynamic aka Fast power save */
+#define WSM_PSM_FAST_PS (BIT(0) | BIT(7))
+
+/* Undetermined */
+/* Note : Undetermined status is reported when the */
+/* NULL data frame used to advertise the PM mode to */
+/* the AP at Pre or Post Background Scan is not Acknowledged */
+#define WSM_PSM_UNKNOWN BIT(1)
+
+/* Queue IDs */
+/* best effort/legacy */
+#define WSM_QUEUE_BEST_EFFORT (0)
+
+/* background */
+#define WSM_QUEUE_BACKGROUND (1)
+
+/* video */
+#define WSM_QUEUE_VIDEO (2)
+
+/* voice */
+#define WSM_QUEUE_VOICE (3)
+
+/* HT TX parameters */
+/* Non-HT */
+#define WSM_HT_TX_NON_HT (0)
+
+/* Mixed format */
+#define WSM_HT_TX_MIXED (1)
+
+/* Greenfield format */
+#define WSM_HT_TX_GREENFIELD (2)
+
+/* STBC allowed */
+#define WSM_HT_TX_STBC (BIT(7))
+
+/* EPTA prioirty flags for BT Coex */
+/* default epta priority */
+#define WSM_EPTA_PRIORITY_DEFAULT 4
+/* use for normal data */
+#define WSM_EPTA_PRIORITY_DATA 4
+/* use for connect/disconnect/roaming*/
+#define WSM_EPTA_PRIORITY_MGT 5
+/* use for action frames */
+#define WSM_EPTA_PRIORITY_ACTION 5
+/* use for AC_VI data */
+#define WSM_EPTA_PRIORITY_VIDEO 5
+/* use for AC_VO data */
+#define WSM_EPTA_PRIORITY_VOICE 6
+/* use for EAPOL exchange */
+#define WSM_EPTA_PRIORITY_EAPOL 7
+
+/* TX status */
+/* Frame was sent aggregated */
+/* Only valid for WSM_SUCCESS status. */
+#define WSM_TX_STATUS_AGGREGATION (BIT(0))
+
+/* Host should requeue this frame later. */
+/* Valid only when status is WSM_REQUEUE. */
+#define WSM_TX_STATUS_REQUEUE (BIT(1))
+
+/* Normal Ack */
+#define WSM_TX_STATUS_NORMAL_ACK (0<<2)
+
+/* No Ack */
+#define WSM_TX_STATUS_NO_ACK (1<<2)
+
+/* No explicit acknowledgement */
+#define WSM_TX_STATUS_NO_EXPLICIT_ACK (2<<2)
+
+/* Block Ack */
+/* Only valid for WSM_SUCCESS status. */
+#define WSM_TX_STATUS_BLOCK_ACK (3<<2)
+
+/* RX status */
+/* Unencrypted */
+#define WSM_RX_STATUS_UNENCRYPTED (0<<0)
+
+/* WEP */
+#define WSM_RX_STATUS_WEP (1<<0)
+
+/* TKIP */
+#define WSM_RX_STATUS_TKIP (2<<0)
+
+/* AES */
+#define WSM_RX_STATUS_AES (3<<0)
+
+/* WAPI */
+#define WSM_RX_STATUS_WAPI (4<<0)
+
+/* Macro to fetch encryption subfield. */
+#define WSM_RX_STATUS_ENCRYPTION(status) ((status) & 0x07)
+
+/* Frame was part of an aggregation */
+#define WSM_RX_STATUS_AGGREGATE (BIT(3))
+
+/* Frame was first in the aggregation */
+#define WSM_RX_STATUS_AGGREGATE_FIRST (BIT(4))
+
+/* Frame was last in the aggregation */
+#define WSM_RX_STATUS_AGGREGATE_LAST (BIT(5))
+
+/* Indicates a defragmented frame */
+#define WSM_RX_STATUS_DEFRAGMENTED (BIT(6))
+
+/* Indicates a Beacon frame */
+#define WSM_RX_STATUS_BEACON (BIT(7))
+
+/* Indicates STA bit beacon TIM field */
+#define WSM_RX_STATUS_TIM (BIT(8))
+
+/* Indicates Beacon frame's virtual bitmap contains multicast bit */
+#define WSM_RX_STATUS_MULTICAST (BIT(9))
+
+/* Indicates frame contains a matching SSID */
+#define WSM_RX_STATUS_MATCHING_SSID (BIT(10))
+
+/* Indicates frame contains a matching BSSI */
+#define WSM_RX_STATUS_MATCHING_BSSI (BIT(11))
+
+/* Indicates More bit set in Framectl field */
+#define WSM_RX_STATUS_MORE_DATA (BIT(12))
+
+/* Indicates frame received during a measurement process */
+#define WSM_RX_STATUS_MEASUREMENT (BIT(13))
+
+/* Indicates frame received as an HT packet */
+#define WSM_RX_STATUS_HT (BIT(14))
+
+/* Indicates frame received with STBC */
+#define WSM_RX_STATUS_STBC (BIT(15))
+
+/* Indicates Address 1 field matches dot11StationId */
+#define WSM_RX_STATUS_ADDRESS1 (BIT(16))
+
+/* Indicates Group address present in the Address 1 field */
+#define WSM_RX_STATUS_GROUP (BIT(17))
+
+/* Indicates Broadcast address present in the Address 1 field */
+#define WSM_RX_STATUS_BROADCAST (BIT(18))
+
+/* Indicates group key used with encrypted frames */
+#define WSM_RX_STATUS_GROUP_KEY (BIT(19))
+
+/* Macro to fetch encryption key index. */
+#define WSM_RX_STATUS_KEY_IDX(status) (((status >> 20)) & 0x0F)
+
+/* Indicates TSF inclusion after 802.11 frame body */
+#define WSM_RX_STATUS_TSF_INCLUDED (BIT(24))
+
+/* Frame Control field starts at Frame offset + 2 */
+#define WSM_TX_2BYTES_SHIFT (BIT(7))
+
+/* Join mode */
+/* IBSS */
+#define WSM_JOIN_MODE_IBSS (0)
+
+/* BSS */
+#define WSM_JOIN_MODE_BSS (1)
+
+/* PLCP preamble type */
+/* For long preamble */
+#define WSM_JOIN_PREAMBLE_LONG (0)
+
+/* For short preamble (Long for 1Mbps) */
+#define WSM_JOIN_PREAMBLE_SHORT (1)
+
+/* For short preamble (Long for 1 and 2Mbps) */
+#define WSM_JOIN_PREAMBLE_SHORT_2 (2)
+
+/* Join flags */
+/* Unsynchronized */
+#define WSM_JOIN_FLAGS_UNSYNCRONIZED BIT(0)
+/* The BSS owner is a P2P GO */
+#define WSM_JOIN_FLAGS_P2P_GO BIT(1)
+/* Force to join BSS with the BSSID and the
+ * SSID specified without waiting for beacons. The
+ * ProbeForJoin parameter is ignored. */
+#define WSM_JOIN_FLAGS_FORCE BIT(2)
+/* Give probe request/response higher
+ * priority over the BT traffic */
+#define WSM_JOIN_FLAGS_PRIO BIT(3)
+/* Issue immediate join confirmation and use
+ * join complete to notify about completion */
+#define WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND BIT(5)
+
+/* Key types */
+#define WSM_KEY_TYPE_WEP_DEFAULT (0)
+#define WSM_KEY_TYPE_WEP_PAIRWISE (1)
+#define WSM_KEY_TYPE_TKIP_GROUP (2)
+#define WSM_KEY_TYPE_TKIP_PAIRWISE (3)
+#define WSM_KEY_TYPE_AES_GROUP (4)
+#define WSM_KEY_TYPE_AES_PAIRWISE (5)
+#define WSM_KEY_TYPE_WAPI_GROUP (6)
+#define WSM_KEY_TYPE_WAPI_PAIRWISE (7)
+
+/* Key indexes */
+#define WSM_KEY_MAX_INDEX (10)
+
+/* ACK policy */
+#define WSM_ACK_POLICY_NORMAL (0)
+#define WSM_ACK_POLICY_NO_ACK (1)
+
+/* Start modes */
+#define WSM_START_MODE_AP (0) /* Mini AP */
+#define WSM_START_MODE_P2P_GO (1) /* P2P GO */
+#define WSM_START_MODE_P2P_DEV (2) /* P2P device */
+
+/* SetAssociationMode MIB flags */
+#define WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE (BIT(0))
+#define WSM_ASSOCIATION_MODE_USE_HT_MODE (BIT(1))
+#define WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET (BIT(2))
+#define WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING (BIT(3))
+#define WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES (BIT(4))
+
+/* RcpiRssiThreshold MIB flags */
+#define WSM_RCPI_RSSI_THRESHOLD_ENABLE (BIT(0))
+#define WSM_RCPI_RSSI_USE_RSSI (BIT(1))
+#define WSM_RCPI_RSSI_DONT_USE_UPPER (BIT(2))
+#define WSM_RCPI_RSSI_DONT_USE_LOWER (BIT(3))
+
+/* Update-ie constants */
+#define WSM_UPDATE_IE_BEACON (BIT(0))
+#define WSM_UPDATE_IE_PROBE_RESP (BIT(1))
+#define WSM_UPDATE_IE_PROBE_REQ (BIT(2))
+
+/* WSM events */
+/* Error */
+#define WSM_EVENT_ERROR (0)
+
+/* BSS lost */
+#define WSM_EVENT_BSS_LOST (1)
+
+/* BSS regained */
+#define WSM_EVENT_BSS_REGAINED (2)
+
+/* Radar detected */
+#define WSM_EVENT_RADAR_DETECTED (3)
+
+/* RCPI or RSSI threshold triggered */
+#define WSM_EVENT_RCPI_RSSI (4)
+
+/* BT inactive */
+#define WSM_EVENT_BT_INACTIVE (5)
+
+/* BT active */
+#define WSM_EVENT_BT_ACTIVE (6)
+
+/* MIB IDs */
+/* 4.1 dot11StationId */
+#define WSM_MIB_ID_DOT11_STATION_ID 0x0000
+
+/* 4.2 dot11MaxtransmitMsduLifeTime */
+#define WSM_MIB_ID_DOT11_MAX_TRANSMIT_LIFTIME 0x0001
+
+/* 4.3 dot11MaxReceiveLifeTime */
+#define WSM_MIB_ID_DOT11_MAX_RECEIVE_LIFETIME 0x0002
+
+/* 4.4 dot11SlotTime */
+#define WSM_MIB_ID_DOT11_SLOT_TIME 0x0003
+
+/* 4.5 dot11GroupAddressesTable */
+#define WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE 0x0004
+#define WSM_MAX_GRP_ADDRTABLE_ENTRIES 8
+
+/* 4.6 dot11WepDefaultKeyId */
+#define WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID 0x0005
+
+/* 4.7 dot11CurrentTxPowerLevel */
+#define WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL 0x0006
+
+/* 4.8 dot11RTSThreshold */
+#define WSM_MIB_ID_DOT11_RTS_THRESHOLD 0x0007
+
+/* 4.9 NonErpProtection */
+#define WSM_MIB_ID_NON_ERP_PROTECTION 0x1000
+
+/* 4.10 ArpIpAddressesTable */
+#define WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE 0x1001
+#define WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES 1
+
+/* 4.11 TemplateFrame */
+#define WSM_MIB_ID_TEMPLATE_FRAME 0x1002
+
+/* 4.12 RxFilter */
+#define WSM_MIB_ID_RX_FILTER 0x1003
+
+/* 4.13 BeaconFilterTable */
+#define WSM_MIB_ID_BEACON_FILTER_TABLE 0x1004
+
+/* 4.14 BeaconFilterEnable */
+#define WSM_MIB_ID_BEACON_FILTER_ENABLE 0x1005
+
+/* 4.15 OperationalPowerMode */
+#define WSM_MIB_ID_OPERATIONAL_POWER_MODE 0x1006
+
+/* 4.16 BeaconWakeUpPeriod */
+#define WSM_MIB_ID_BEACON_WAKEUP_PERIOD 0x1007
+
+/* 4.17 RcpiRssiThreshold */
+#define WSM_MIB_ID_RCPI_RSSI_THRESHOLD 0x1009
+
+/* 4.18 StatisticsTable */
+#define WSM_MIB_ID_STATISTICS_TABLE 0x100A
+
+/* 4.19 IbssPsConfig */
+#define WSM_MIB_ID_IBSS_PS_CONFIG 0x100B
+
+/* 4.20 CountersTable */
+#define WSM_MIB_ID_COUNTERS_TABLE 0x100C
+
+/* 4.21 BlockAckPolicy */
+#define WSM_MIB_ID_BLOCK_ACK_POLICY 0x100E
+
+/* 4.22 OverrideInternalTxRate */
+#define WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE 0x100F
+
+/* 4.23 SetAssociationMode */
+#define WSM_MIB_ID_SET_ASSOCIATION_MODE 0x1010
+
+/* 4.24 UpdateEptaConfigData */
+#define WSM_MIB_ID_UPDATE_EPTA_CONFIG_DATA 0x1011
+
+/* 4.25 SelectCcaMethod */
+#define WSM_MIB_ID_SELECT_CCA_METHOD 0x1012
+
+/* 4.26 SetUpasdInformation */
+#define WSM_MIB_ID_SET_UAPSD_INFORMATION 0x1013
+
+/* 4.27 SetAutoCalibrationMode WBF00004073 */
+#define WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE 0x1015
+
+/* 4.28 SetTxRateRetryPolicy */
+#define WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY 0x1016
+
+/* 4.29 SetHostMessageTypeFilter */
+#define WSM_MIB_ID_SET_HOST_MSG_TYPE_FILTER 0x1017
+
+/* 4.30 P2PFindInfo */
+#define WSM_MIB_ID_P2P_FIND_INFO 0x1018
+
+/* 4.31 P2PPsModeInfo */
+#define WSM_MIB_ID_P2P_PS_MODE_INFO 0x1019
+
+/* 4.32 SetEtherTypeDataFrameFilter */
+#define WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER 0x101A
+
+/* 4.33 SetUDPPortDataFrameFilter */
+#define WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER 0x101B
+
+/* 4.34 SetMagicDataFrameFilter */
+#define WSM_MIB_ID_SET_MAGIC_DATAFRAME_FILTER 0x101C
+
+/* 4.35 P2PDeviceInfo */
+#define WSM_MIB_ID_P2P_DEVICE_INFO 0x101D
+
+/* 4.36 SetWCDMABand */
+#define WSM_MIB_ID_SET_WCDMA_BAND 0x101E
+
+/* 4.37 GroupTxSequenceCounter */
+#define WSM_MIB_ID_GRP_SEQ_COUNTER 0x101F
+
+/* 4.38 ProtectedMgmtPolicy */
+#define WSM_MIB_ID_PROTECTED_MGMT_POLICY 0x1020
+
+/* 4.39 SetHtProtection */
+#define WSM_MIB_ID_SET_HT_PROTECTION 0x1021
+
+/* 4.40 GPIO Command */
+#define WSM_MIB_ID_GPIO_COMMAND 0x1022
+
+/* 4.41 TSF Counter Value */
+#define WSM_MIB_ID_TSF_COUNTER 0x1023
+
+/* Test Purposes Only */
+#define WSM_MIB_ID_BLOCK_ACK_INFO 0x100D
+
+/* 4.42 UseMultiTxConfMessage */
+#define WSM_MIB_USE_MULTI_TX_CONF 0x1024
+
+/* 4.43 Keep-alive period */
+#define WSM_MIB_ID_KEEP_ALIVE_PERIOD 0x1025
+
+/* 4.44 Disable BSSID filter */
+#define WSM_MIB_ID_DISABLE_BSSID_FILTER 0x1026
+
+/* Frame template types */
+#define WSM_FRAME_TYPE_PROBE_REQUEST (0)
+#define WSM_FRAME_TYPE_BEACON (1)
+#define WSM_FRAME_TYPE_NULL (2)
+#define WSM_FRAME_TYPE_QOS_NULL (3)
+#define WSM_FRAME_TYPE_PS_POLL (4)
+#define WSM_FRAME_TYPE_PROBE_RESPONSE (5)
+
+#define WSM_FRAME_GREENFIELD (0x80) /* See 4.11 */
+
+/* Status */
+/* The WSM firmware has completed a request */
+/* successfully. */
+#define WSM_STATUS_SUCCESS (0)
+
+/* This is a generic failure code if other error codes do */
+/* not apply. */
+#define WSM_STATUS_FAILURE (1)
+
+/* A request contains one or more invalid parameters. */
+#define WSM_INVALID_PARAMETER (2)
+
+/* The request cannot perform because the device is in */
+/* an inappropriate mode. */
+#define WSM_ACCESS_DENIED (3)
+
+/* The frame received includes a decryption error. */
+#define WSM_STATUS_DECRYPTFAILURE (4)
+
+/* A MIC failure is detected in the received packets. */
+#define WSM_STATUS_MICFAILURE (5)
+
+/* The transmit request failed due to retry limit being */
+/* exceeded. */
+#define WSM_STATUS_RETRY_EXCEEDED (6)
+
+/* The transmit request failed due to MSDU life time */
+/* being exceeded. */
+#define WSM_STATUS_TX_LIFETIME_EXCEEDED (7)
+
+/* The link to the AP is lost. */
+#define WSM_STATUS_LINK_LOST (8)
+
+/* No key was found for the encrypted frame */
+#define WSM_STATUS_NO_KEY_FOUND (9)
+
+/* Jammer was detected when transmitting this frame */
+#define WSM_STATUS_JAMMER_DETECTED (10)
+
+/* The message should be requeued later. */
+/* This is applicable only to Transmit */
+#define WSM_REQUEUE (11)
+
+/* Advanced filtering options */
+#define WSM_MAX_FILTER_ELEMENTS (4)
+
+#define WSM_FILTER_ACTION_IGNORE (0)
+#define WSM_FILTER_ACTION_FILTER_IN (1)
+#define WSM_FILTER_ACTION_FILTER_OUT (2)
+
+#define WSM_FILTER_PORT_TYPE_DST (0)
+#define WSM_FILTER_PORT_TYPE_SRC (1)
+
+/* Actual header of WSM messages */
+struct wsm_hdr {
+ __le16 len;
+ __le16 id;
+};
+
+#define WSM_TX_SEQ_MAX (7)
+#define WSM_TX_SEQ(seq) \
+ ((seq & WSM_TX_SEQ_MAX) << 13)
+#define WSM_TX_LINK_ID_MAX (0x0F)
+#define WSM_TX_LINK_ID(link_id) \
+ ((link_id & WSM_TX_LINK_ID_MAX) << 6)
+
+#define MAX_BEACON_SKIP_TIME_MS 1000
+
+#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 3 / 2)
+
+/* ******************************************************************** */
+/* WSM capability */
+
+#define WSM_STARTUP_IND_ID 0x0801
+
+struct wsm_startup_ind {
+ u16 input_buffers;
+ u16 input_buffer_size;
+ u16 status;
+ u16 hw_id;
+ u16 hw_subid;
+ u16 fw_cap;
+ u16 fw_type;
+ u16 fw_api;
+ u16 fw_build;
+ u16 fw_ver;
+ char fw_label[128];
+ u32 config[4];
+};
+
+/* ******************************************************************** */
+/* WSM commands */
+
+/* 3.1 */
+#define WSM_CONFIGURATION_REQ_ID 0x0009
+#define WSM_CONFIGURATION_RESP_ID 0x0409
+
+struct wsm_tx_power_range {
+ int min_power_level;
+ int max_power_level;
+ u32 stepping;
+};
+
+struct wsm_configuration {
+ /* [in] */ u32 dot11MaxTransmitMsduLifeTime;
+ /* [in] */ u32 dot11MaxReceiveLifeTime;
+ /* [in] */ u32 dot11RtsThreshold;
+ /* [in, out] */ u8 *dot11StationId;
+ /* [in] */ const void *dpdData;
+ /* [in] */ size_t dpdData_size;
+ /* [out] */ u8 dot11FrequencyBandsSupported;
+ /* [out] */ u32 supportedRateMask;
+ /* [out] */ struct wsm_tx_power_range txPowerRange[2];
+};
+
+int wsm_configuration(struct cw1200_common *priv,
+ struct wsm_configuration *arg);
+
+/* 3.3 */
+#define WSM_RESET_REQ_ID 0x000A
+#define WSM_RESET_RESP_ID 0x040A
+struct wsm_reset {
+ /* [in] */ int link_id;
+ /* [in] */ bool reset_statistics;
+};
+
+int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg);
+
+/* 3.5 */
+#define WSM_READ_MIB_REQ_ID 0x0005
+#define WSM_READ_MIB_RESP_ID 0x0405
+int wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *buf,
+ size_t buf_size);
+
+/* 3.7 */
+#define WSM_WRITE_MIB_REQ_ID 0x0006
+#define WSM_WRITE_MIB_RESP_ID 0x0406
+int wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *buf,
+ size_t buf_size);
+
+/* 3.9 */
+#define WSM_START_SCAN_REQ_ID 0x0007
+#define WSM_START_SCAN_RESP_ID 0x0407
+
+struct wsm_ssid {
+ u8 ssid[32];
+ u32 length;
+};
+
+struct wsm_scan_ch {
+ u16 number;
+ u32 min_chan_time;
+ u32 max_chan_time;
+ u32 tx_power_level;
+};
+
+struct wsm_scan {
+ /* WSM_PHY_BAND_... */
+ u8 band;
+
+ /* WSM_SCAN_TYPE_... */
+ u8 type;
+
+ /* WSM_SCAN_FLAG_... */
+ u8 flags;
+
+ /* WSM_TRANSMIT_RATE_... */
+ u8 max_tx_rate;
+
+ /* Interval period in TUs that the device shall the re- */
+ /* execute the requested scan. Max value supported by the device */
+ /* is 256s. */
+ u32 auto_scan_interval;
+
+ /* Number of probe requests (per SSID) sent to one (1) */
+ /* channel. Zero (0) means that none is send, which */
+ /* means that a passive scan is to be done. Value */
+ /* greater than zero (0) means that an active scan is to */
+ /* be done. */
+ u32 num_probes;
+
+ /* Number of channels to be scanned. */
+ /* Maximum value is WSM_SCAN_MAX_NUM_OF_CHANNELS. */
+ u8 num_channels;
+
+ /* Number of SSID provided in the scan command (this */
+ /* is zero (0) in broadcast scan) */
+ /* The maximum number of SSIDs is WSM_SCAN_MAX_NUM_OF_SSIDS. */
+ u8 num_ssids;
+
+ /* The delay time (in microseconds) period */
+ /* before sending a probe-request. */
+ u8 probe_delay;
+
+ /* SSIDs to be scanned [numOfSSIDs]; */
+ struct wsm_ssid *ssids;
+
+ /* Channels to be scanned [numOfChannels]; */
+ struct wsm_scan_ch *ch;
+};
+
+int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg);
+
+/* 3.11 */
+#define WSM_STOP_SCAN_REQ_ID 0x0008
+#define WSM_STOP_SCAN_RESP_ID 0x0408
+int wsm_stop_scan(struct cw1200_common *priv);
+
+/* 3.13 */
+#define WSM_SCAN_COMPLETE_IND_ID 0x0806
+struct wsm_scan_complete {
+ /* WSM_STATUS_... */
+ u32 status;
+
+ /* WSM_PSM_... */
+ u8 psm;
+
+ /* Number of channels that the scan operation completed. */
+ u8 num_channels;
+};
+
+/* 3.14 */
+#define WSM_TX_CONFIRM_IND_ID 0x0404
+#define WSM_MULTI_TX_CONFIRM_ID 0x041E
+
+struct wsm_tx_confirm {
+ /* Packet identifier used in wsm_tx. */
+ u32 packet_id;
+
+ /* WSM_STATUS_... */
+ u32 status;
+
+ /* WSM_TRANSMIT_RATE_... */
+ u8 tx_rate;
+
+ /* The number of times the frame was transmitted */
+ /* without receiving an acknowledgement. */
+ u8 ack_failures;
+
+ /* WSM_TX_STATUS_... */
+ u16 flags;
+
+ /* The total time in microseconds that the frame spent in */
+ /* the WLAN device before transmission as completed. */
+ u32 media_delay;
+
+ /* The total time in microseconds that the frame spent in */
+ /* the WLAN device before transmission was started. */
+ u32 tx_queue_delay;
+};
+
+/* 3.15 */
+typedef void (*wsm_tx_confirm_cb) (struct cw1200_common *priv,
+ struct wsm_tx_confirm *arg);
+
+/* Note that ideology of wsm_tx struct is different against the rest of
+ * WSM API. wsm_hdr is /not/ a caller-adapted struct to be used as an input
+ * argument for WSM call, but a prepared bytestream to be sent to firmware.
+ * It is filled partly in cw1200_tx, partly in low-level WSM code.
+ * Please pay attention once again: ideology is different.
+ *
+ * Legend:
+ * - [in]: cw1200_tx must fill this field.
+ * - [wsm]: the field is filled by low-level WSM.
+ */
+struct wsm_tx {
+ /* common WSM header */
+ struct wsm_hdr hdr;
+
+ /* Packet identifier that meant to be used in completion. */
+ __le32 packet_id;
+
+ /* WSM_TRANSMIT_RATE_... */
+ u8 max_tx_rate;
+
+ /* WSM_QUEUE_... */
+ u8 queue_id;
+
+ /* True: another packet is pending on the host for transmission. */
+ u8 more;
+
+ /* Bit 0 = 0 - Start expiry time from first Tx attempt (default) */
+ /* Bit 0 = 1 - Start expiry time from receipt of Tx Request */
+ /* Bits 3:1 - PTA Priority */
+ /* Bits 6:4 - Tx Rate Retry Policy */
+ /* Bit 7 - Reserved */
+ u8 flags;
+
+ /* Should be 0. */
+ __le32 reserved;
+
+ /* The elapsed time in TUs, after the initial transmission */
+ /* of an MSDU, after which further attempts to transmit */
+ /* the MSDU shall be terminated. Overrides the global */
+ /* dot11MaxTransmitMsduLifeTime setting [optional] */
+ /* Device will set the default value if this is 0. */
+ __le32 expire_time;
+
+ /* WSM_HT_TX_... */
+ __le32 ht_tx_parameters;
+};
+
+/* = sizeof(generic hi hdr) + sizeof(wsm hdr) + sizeof(alignment) */
+#define WSM_TX_EXTRA_HEADROOM (28)
+
+/* 3.16 */
+#define WSM_RECEIVE_IND_ID 0x0804
+
+struct wsm_rx {
+ /* WSM_STATUS_... */
+ __le32 status;
+
+ /* Specifies the channel of the received packet. */
+ __le16 channel_number;
+
+ /* WSM_TRANSMIT_RATE_... */
+ u8 rx_rate;
+
+ /* This value is expressed in signed Q8.0 format for */
+ /* RSSI and unsigned Q7.1 format for RCPI. */
+ u8 rcpi_rssi;
+
+ /* WSM_RX_STATUS_... */
+ __le32 flags;
+
+ /* Payload */
+ u8 data[0];
+} __packed;
+
+/* = sizeof(generic hi hdr) + sizeof(wsm hdr) */
+#define WSM_RX_EXTRA_HEADROOM (16)
+
+typedef void (*wsm_rx_cb) (struct cw1200_common *priv, struct wsm_rx *arg,
+ struct sk_buff **skb_p);
+
+/* 3.17 */
+struct wsm_event {
+ /* WSM_STATUS_... */
+ /* [out] */ u32 id;
+
+ /* Indication parameters. */
+ /* For error indication, this shall be a 32-bit WSM status. */
+ /* For RCPI or RSSI indication, this should be an 8-bit */
+ /* RCPI or RSSI value. */
+ /* [out] */ u32 data;
+};
+
+struct cw1200_wsm_event {
+ struct list_head link;
+ struct wsm_event evt;
+};
+
+/* 3.18 - 3.22 */
+/* Measurement. Skipped for now. Irrelevent. */
+
+typedef void (*wsm_event_cb) (struct cw1200_common *priv,
+ struct wsm_event *arg);
+
+/* 3.23 */
+#define WSM_JOIN_REQ_ID 0x000B
+#define WSM_JOIN_RESP_ID 0x040B
+
+struct wsm_join {
+ /* WSM_JOIN_MODE_... */
+ u8 mode;
+
+ /* WSM_PHY_BAND_... */
+ u8 band;
+
+ /* Specifies the channel number to join. The channel */
+ /* number will be mapped to an actual frequency */
+ /* according to the band */
+ u16 channel_number;
+
+ /* Specifies the BSSID of the BSS or IBSS to be joined */
+ /* or the IBSS to be started. */
+ u8 bssid[6];
+
+ /* ATIM window of IBSS */
+ /* When ATIM window is zero the initiated IBSS does */
+ /* not support power saving. */
+ u16 atim_window;
+
+ /* WSM_JOIN_PREAMBLE_... */
+ u8 preamble_type;
+
+ /* Specifies if a probe request should be send with the */
+ /* specified SSID when joining to the network. */
+ u8 probe_for_join;
+
+ /* DTIM Period (In multiples of beacon interval) */
+ u8 dtim_period;
+
+ /* WSM_JOIN_FLAGS_... */
+ u8 flags;
+
+ /* Length of the SSID */
+ u32 ssid_len;
+
+ /* Specifies the SSID of the IBSS to join or start */
+ u8 ssid[32];
+
+ /* Specifies the time between TBTTs in TUs */
+ u32 beacon_interval;
+
+ /* A bit mask that defines the BSS basic rate set. */
+ u32 basic_rate_set;
+};
+
+struct wsm_join_cnf {
+ u32 status;
+
+ /* Minimum transmission power level in units of 0.1dBm */
+ u32 min_power_level;
+
+ /* Maximum transmission power level in units of 0.1dBm */
+ u32 max_power_level;
+};
+
+int wsm_join(struct cw1200_common *priv, struct wsm_join *arg);
+
+/* 3.24 */
+struct wsm_join_complete {
+ /* WSM_STATUS_... */
+ u32 status;
+};
+
+/* 3.25 */
+#define WSM_SET_PM_REQ_ID 0x0010
+#define WSM_SET_PM_RESP_ID 0x0410
+struct wsm_set_pm {
+ /* WSM_PSM_... */
+ u8 mode;
+
+ /* in unit of 500us; 0 to use default */
+ u8 fast_psm_idle_period;
+
+ /* in unit of 500us; 0 to use default */
+ u8 ap_psm_change_period;
+
+ /* in unit of 500us; 0 to disable auto-pspoll */
+ u8 min_auto_pspoll_period;
+};
+
+int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg);
+
+/* 3.27 */
+struct wsm_set_pm_complete {
+ u8 psm; /* WSM_PSM_... */
+};
+
+/* 3.28 */
+#define WSM_SET_BSS_PARAMS_REQ_ID 0x0011
+#define WSM_SET_BSS_PARAMS_RESP_ID 0x0411
+struct wsm_set_bss_params {
+ /* This resets the beacon loss counters only */
+ u8 reset_beacon_loss;
+
+ /* The number of lost consecutive beacons after which */
+ /* the WLAN device should indicate the BSS-Lost event */
+ /* to the WLAN host driver. */
+ u8 beacon_lost_count;
+
+ /* The AID received during the association process. */
+ u16 aid;
+
+ /* The operational rate set mask */
+ u32 operational_rate_set;
+};
+
+int wsm_set_bss_params(struct cw1200_common *priv,
+ const struct wsm_set_bss_params *arg);
+
+/* 3.30 */
+#define WSM_ADD_KEY_REQ_ID 0x000C
+#define WSM_ADD_KEY_RESP_ID 0x040C
+struct wsm_add_key {
+ u8 type; /* WSM_KEY_TYPE_... */
+ u8 index; /* Key entry index: 0 -- WSM_KEY_MAX_INDEX */
+ u16 reserved;
+ union {
+ struct {
+ u8 peer[6]; /* MAC address of the
+ * peer station */
+ u8 reserved;
+ u8 keylen; /* Key length in bytes */
+ u8 keydata[16]; /* Key data */
+ } __packed wep_pairwise;
+ struct {
+ u8 keyid; /* Unique per key identifier
+ * (0..3) */
+ u8 keylen; /* Key length in bytes */
+ u16 reserved;
+ u8 keydata[16]; /* Key data */
+ } __packed wep_group;
+ struct {
+ u8 peer[6]; /* MAC address of the
+ * peer station */
+ u16 reserved;
+ u8 keydata[16]; /* TKIP key data */
+ u8 rx_mic_key[8]; /* Rx MIC key */
+ u8 tx_mic_key[8]; /* Tx MIC key */
+ } __packed tkip_pairwise;
+ struct {
+ u8 keydata[16]; /* TKIP key data */
+ u8 rx_mic_key[8]; /* Rx MIC key */
+ u8 keyid; /* Key ID */
+ u8 reserved[3];
+ u8 rx_seqnum[8]; /* Receive Sequence Counter */
+ } __packed tkip_group;
+ struct {
+ u8 peer[6]; /* MAC address of the
+ * peer station */
+ u16 reserved;
+ u8 keydata[16]; /* AES key data */
+ } __packed aes_pairwise;
+ struct {
+ u8 keydata[16]; /* AES key data */
+ u8 keyid; /* Key ID */
+ u8 reserved[3];
+ u8 rx_seqnum[8]; /* Receive Sequence Counter */
+ } __packed aes_group;
+ struct {
+ u8 peer[6]; /* MAC address of the
+ * peer station */
+ u8 keyid; /* Key ID */
+ u8 reserved;
+ u8 keydata[16]; /* WAPI key data */
+ u8 mic_key[16]; /* MIC key data */
+ } __packed wapi_pairwise;
+ struct {
+ u8 keydata[16]; /* WAPI key data */
+ u8 mic_key[16]; /* MIC key data */
+ u8 keyid; /* Key ID */
+ u8 reserved[3];
+ } __packed wapi_group;
+ } __packed;
+} __packed;
+
+int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg);
+
+/* 3.32 */
+#define WSM_REMOVE_KEY_REQ_ID 0x000D
+#define WSM_REMOVE_KEY_RESP_ID 0x040D
+struct wsm_remove_key {
+ u8 index; /* Key entry index : 0-10 */
+};
+
+int wsm_remove_key(struct cw1200_common *priv,
+ const struct wsm_remove_key *arg);
+
+/* 3.34 */
+struct wsm_set_tx_queue_params {
+ /* WSM_ACK_POLICY_... */
+ u8 ackPolicy;
+
+ /* Medium Time of TSPEC (in 32us units) allowed per */
+ /* One Second Averaging Period for this queue. */
+ u16 allowedMediumTime;
+
+ /* dot11MaxTransmitMsduLifetime to be used for the */
+ /* specified queue. */
+ u32 maxTransmitLifetime;
+};
+
+struct wsm_tx_queue_params {
+ /* NOTE: index is a linux queue id. */
+ struct wsm_set_tx_queue_params params[4];
+};
+
+
+#define WSM_TX_QUEUE_SET(queue_params, queue, ack_policy, allowed_time,\
+ max_life_time) \
+do { \
+ struct wsm_set_tx_queue_params *p = &(queue_params)->params[queue]; \
+ p->ackPolicy = (ack_policy); \
+ p->allowedMediumTime = (allowed_time); \
+ p->maxTransmitLifetime = (max_life_time); \
+} while (0)
+
+int wsm_set_tx_queue_params(struct cw1200_common *priv,
+ const struct wsm_set_tx_queue_params *arg, u8 id);
+
+/* 3.36 */
+#define WSM_EDCA_PARAMS_REQ_ID 0x0013
+#define WSM_EDCA_PARAMS_RESP_ID 0x0413
+struct wsm_edca_queue_params {
+ /* CWmin (in slots) for the access class. */
+ __le16 cwmin;
+
+ /* CWmax (in slots) for the access class. */
+ __le16 cwmax;
+
+ /* AIFS (in slots) for the access class. */
+ __le16 aifns;
+
+ /* TX OP Limit (in microseconds) for the access class. */
+ __le16 txop_limit;
+
+ /* dot11MaxReceiveLifetime to be used for the specified */
+ /* the access class. Overrides the global */
+ /* dot11MaxReceiveLifetime value */
+ __le32 max_rx_lifetime;
+} __packed;
+
+struct wsm_edca_params {
+ /* NOTE: index is a linux queue id. */
+ struct wsm_edca_queue_params params[4];
+ bool uapsd_enable[4];
+};
+
+#define TXOP_UNIT 32
+#define WSM_EDCA_SET(__edca, __queue, __aifs, __cw_min, __cw_max, __txop, __lifetime,\
+ __uapsd) \
+ do { \
+ struct wsm_edca_queue_params *p = &(__edca)->params[__queue]; \
+ p->cwmin = (__cw_min); \
+ p->cwmax = (__cw_max); \
+ p->aifns = (__aifs); \
+ p->txop_limit = ((__txop) * TXOP_UNIT); \
+ p->max_rx_lifetime = (__lifetime); \
+ (__edca)->uapsd_enable[__queue] = (__uapsd); \
+ } while (0)
+
+int wsm_set_edca_params(struct cw1200_common *priv,
+ const struct wsm_edca_params *arg);
+
+int wsm_set_uapsd_param(struct cw1200_common *priv,
+ const struct wsm_edca_params *arg);
+
+/* 3.38 */
+/* Set-System info. Skipped for now. Irrelevent. */
+
+/* 3.40 */
+#define WSM_SWITCH_CHANNEL_REQ_ID 0x0016
+#define WSM_SWITCH_CHANNEL_RESP_ID 0x0416
+
+struct wsm_switch_channel {
+ /* 1 - means the STA shall not transmit any further */
+ /* frames until the channel switch has completed */
+ u8 mode;
+
+ /* Number of TBTTs until channel switch occurs. */
+ /* 0 - indicates switch shall occur at any time */
+ /* 1 - occurs immediately before the next TBTT */
+ u8 switch_count;
+
+ /* The new channel number to switch to. */
+ /* Note this is defined as per section 2.7. */
+ u16 channel_number;
+};
+
+int wsm_switch_channel(struct cw1200_common *priv,
+ const struct wsm_switch_channel *arg);
+
+typedef void (*wsm_channel_switch_cb) (struct cw1200_common *priv);
+
+#define WSM_START_REQ_ID 0x0017
+#define WSM_START_RESP_ID 0x0417
+
+struct wsm_start {
+ /* WSM_START_MODE_... */
+ /* [in] */ u8 mode;
+
+ /* WSM_PHY_BAND_... */
+ /* [in] */ u8 band;
+
+ /* Channel number */
+ /* [in] */ u16 channel_number;
+
+ /* Client Traffic window in units of TU */
+ /* Valid only when mode == ..._P2P */
+ /* [in] */ u32 ct_window;
+
+ /* Interval between two consecutive */
+ /* beacon transmissions in TU. */
+ /* [in] */ u32 beacon_interval;
+
+ /* DTIM period in terms of beacon intervals */
+ /* [in] */ u8 dtim_period;
+
+ /* WSM_JOIN_PREAMBLE_... */
+ /* [in] */ u8 preamble;
+
+ /* The delay time (in microseconds) period */
+ /* before sending a probe-request. */
+ /* [in] */ u8 probe_delay;
+
+ /* Length of the SSID */
+ /* [in] */ u8 ssid_len;
+
+ /* SSID of the BSS or P2P_GO to be started now. */
+ /* [in] */ u8 ssid[32];
+
+ /* The basic supported rates for the MiniAP. */
+ /* [in] */ u32 basic_rate_set;
+};
+
+int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg);
+
+#define WSM_BEACON_TRANSMIT_REQ_ID 0x0018
+#define WSM_BEACON_TRANSMIT_RESP_ID 0x0418
+
+struct wsm_beacon_transmit {
+ /* 1: enable; 0: disable */
+ /* [in] */ u8 enable_beaconing;
+};
+
+int wsm_beacon_transmit(struct cw1200_common *priv,
+ const struct wsm_beacon_transmit *arg);
+
+int wsm_start_find(struct cw1200_common *priv);
+
+int wsm_stop_find(struct cw1200_common *priv);
+
+typedef void (*wsm_find_complete_cb) (struct cw1200_common *priv, u32 status);
+
+struct wsm_suspend_resume {
+ /* See 3.52 */
+ /* Link ID */
+ /* [out] */ int link_id;
+ /* Stop sending further Tx requests down to device for this link */
+ /* [out] */ bool stop;
+ /* Transmit multicast Frames */
+ /* [out] */ bool multicast;
+ /* The AC on which Tx to be suspended /resumed. */
+ /* This is applicable only for U-APSD */
+ /* WSM_QUEUE_... */
+ /* [out] */ int queue;
+};
+
+typedef void (*wsm_suspend_resume_cb) (struct cw1200_common *priv,
+ struct wsm_suspend_resume *arg);
+
+/* 3.54 Update-IE request. */
+struct wsm_update_ie {
+ /* WSM_UPDATE_IE_... */
+ /* [in] */ u16 what;
+ /* [in] */ u16 count;
+ /* [in] */ u8 *ies;
+ /* [in] */ size_t length;
+};
+
+int wsm_update_ie(struct cw1200_common *priv,
+ const struct wsm_update_ie *arg);
+
+/* 3.56 */
+struct wsm_map_link {
+ /* MAC address of the remote device */
+ /* [in] */ u8 mac_addr[6];
+ /* [in] */ u8 link_id;
+};
+
+int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg);
+
+/* ******************************************************************** */
+/* MIB shortcats */
+
+static inline int wsm_set_output_power(struct cw1200_common *priv,
+ int power_level)
+{
+ __le32 val = __cpu_to_le32(power_level);
+ return wsm_write_mib(priv, WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL,
+ &val, sizeof(val));
+}
+
+static inline int wsm_set_beacon_wakeup_period(struct cw1200_common *priv,
+ unsigned dtim_interval,
+ unsigned listen_interval)
+{
+ struct {
+ u8 numBeaconPeriods;
+ u8 reserved;
+ __le16 listenInterval;
+ } val = {
+ dtim_interval, 0, __cpu_to_le16(listen_interval)
+ };
+
+ if (dtim_interval > 0xFF || listen_interval > 0xFFFF)
+ return -EINVAL;
+ else
+ return wsm_write_mib(priv, WSM_MIB_ID_BEACON_WAKEUP_PERIOD,
+ &val, sizeof(val));
+}
+
+struct wsm_rcpi_rssi_threshold {
+ u8 rssiRcpiMode; /* WSM_RCPI_RSSI_... */
+ u8 lowerThreshold;
+ u8 upperThreshold;
+ u8 rollingAverageCount;
+};
+
+static inline int wsm_set_rcpi_rssi_threshold(struct cw1200_common *priv,
+ struct wsm_rcpi_rssi_threshold *arg)
+{
+ return wsm_write_mib(priv, WSM_MIB_ID_RCPI_RSSI_THRESHOLD, arg,
+ sizeof(*arg));
+}
+
+struct wsm_mib_counters_table {
+ __le32 plcp_errors;
+ __le32 fcs_errors;
+ __le32 tx_packets;
+ __le32 rx_packets;
+ __le32 rx_packet_errors;
+ __le32 rx_decryption_failures;
+ __le32 rx_mic_failures;
+ __le32 rx_no_key_failures;
+ __le32 tx_multicast_frames;
+ __le32 tx_frames_success;
+ __le32 tx_frame_failures;
+ __le32 tx_frames_retried;
+ __le32 tx_frames_multi_retried;
+ __le32 rx_frame_duplicates;
+ __le32 rts_success;
+ __le32 rts_failures;
+ __le32 ack_failures;
+ __le32 rx_multicast_frames;
+ __le32 rx_frames_success;
+ __le32 rx_cmac_icv_errors;
+ __le32 rx_cmac_replays;
+ __le32 rx_mgmt_ccmp_replays;
+} __packed;
+
+static inline int wsm_get_counters_table(struct cw1200_common *priv,
+ struct wsm_mib_counters_table *arg)
+{
+ return wsm_read_mib(priv, WSM_MIB_ID_COUNTERS_TABLE,
+ arg, sizeof(*arg));
+}
+
+static inline int wsm_get_station_id(struct cw1200_common *priv, u8 *mac)
+{
+ return wsm_read_mib(priv, WSM_MIB_ID_DOT11_STATION_ID, mac, ETH_ALEN);
+}
+
+struct wsm_rx_filter {
+ bool promiscuous;
+ bool bssid;
+ bool fcs;
+ bool probeResponder;
+};
+
+static inline int wsm_set_rx_filter(struct cw1200_common *priv,
+ const struct wsm_rx_filter *arg)
+{
+ __le32 val = 0;
+ if (arg->promiscuous)
+ val |= __cpu_to_le32(BIT(0));
+ if (arg->bssid)
+ val |= __cpu_to_le32(BIT(1));
+ if (arg->fcs)
+ val |= __cpu_to_le32(BIT(2));
+ if (arg->probeResponder)
+ val |= __cpu_to_le32(BIT(3));
+ return wsm_write_mib(priv, WSM_MIB_ID_RX_FILTER, &val, sizeof(val));
+}
+
+int wsm_set_probe_responder(struct cw1200_common *priv, bool enable);
+
+#define WSM_BEACON_FILTER_IE_HAS_CHANGED BIT(0)
+#define WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT BIT(1)
+#define WSM_BEACON_FILTER_IE_HAS_APPEARED BIT(2)
+
+struct wsm_beacon_filter_table_entry {
+ u8 ie_id;
+ u8 flags;
+ u8 oui[3];
+ u8 match_data[3];
+} __packed;
+
+struct wsm_mib_beacon_filter_table {
+ __le32 num;
+ struct wsm_beacon_filter_table_entry entry[10];
+} __packed;
+
+static inline int wsm_set_beacon_filter_table(struct cw1200_common *priv,
+ struct wsm_mib_beacon_filter_table *ft)
+{
+ size_t size = __le32_to_cpu(ft->num) *
+ sizeof(struct wsm_beacon_filter_table_entry) +
+ sizeof(__le32);
+
+ return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_TABLE, ft, size);
+}
+
+#define WSM_BEACON_FILTER_ENABLE BIT(0) /* Enable/disable beacon filtering */
+#define WSM_BEACON_FILTER_AUTO_ERP BIT(1) /* If 1 FW will handle ERP IE changes internally */
+
+struct wsm_beacon_filter_control {
+ int enabled;
+ int bcn_count;
+};
+
+static inline int wsm_beacon_filter_control(struct cw1200_common *priv,
+ struct wsm_beacon_filter_control *arg)
+{
+ struct {
+ __le32 enabled;
+ __le32 bcn_count;
+ } val;
+ val.enabled = __cpu_to_le32(arg->enabled);
+ val.bcn_count = __cpu_to_le32(arg->bcn_count);
+ return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_ENABLE, &val,
+ sizeof(val));
+}
+
+enum wsm_power_mode {
+ wsm_power_mode_active = 0,
+ wsm_power_mode_doze = 1,
+ wsm_power_mode_quiescent = 2,
+};
+
+struct wsm_operational_mode {
+ enum wsm_power_mode power_mode;
+ int disable_more_flag_usage;
+ int perform_ant_diversity;
+};
+
+static inline int wsm_set_operational_mode(struct cw1200_common *priv,
+ const struct wsm_operational_mode *arg)
+{
+ u8 val = arg->power_mode;
+ if (arg->disable_more_flag_usage)
+ val |= BIT(4);
+ if (arg->perform_ant_diversity)
+ val |= BIT(5);
+ return wsm_write_mib(priv, WSM_MIB_ID_OPERATIONAL_POWER_MODE, &val,
+ sizeof(val));
+}
+
+struct wsm_template_frame {
+ u8 frame_type;
+ u8 rate;
+ struct sk_buff *skb;
+};
+
+static inline int wsm_set_template_frame(struct cw1200_common *priv,
+ struct wsm_template_frame *arg)
+{
+ int ret;
+ u8 *p = skb_push(arg->skb, 4);
+ p[0] = arg->frame_type;
+ p[1] = arg->rate;
+ ((u16 *)p)[1] = __cpu_to_le16(arg->skb->len - 4);
+ ret = wsm_write_mib(priv, WSM_MIB_ID_TEMPLATE_FRAME, p, arg->skb->len);
+ skb_pull(arg->skb, 4);
+ return ret;
+}
+
+
+struct wsm_protected_mgmt_policy {
+ bool protectedMgmtEnable;
+ bool unprotectedMgmtFramesAllowed;
+ bool encryptionForAuthFrame;
+};
+
+static inline int wsm_set_protected_mgmt_policy(struct cw1200_common *priv,
+ struct wsm_protected_mgmt_policy *arg)
+{
+ __le32 val = 0;
+ int ret;
+ if (arg->protectedMgmtEnable)
+ val |= __cpu_to_le32(BIT(0));
+ if (arg->unprotectedMgmtFramesAllowed)
+ val |= __cpu_to_le32(BIT(1));
+ if (arg->encryptionForAuthFrame)
+ val |= __cpu_to_le32(BIT(2));
+ ret = wsm_write_mib(priv, WSM_MIB_ID_PROTECTED_MGMT_POLICY,
+ &val, sizeof(val));
+ return ret;
+}
+
+struct wsm_mib_block_ack_policy {
+ u8 tx_tid;
+ u8 reserved1;
+ u8 rx_tid;
+ u8 reserved2;
+} __packed;
+
+static inline int wsm_set_block_ack_policy(struct cw1200_common *priv,
+ u8 tx_tid_policy,
+ u8 rx_tid_policy)
+{
+ struct wsm_mib_block_ack_policy val = {
+ .tx_tid = tx_tid_policy,
+ .rx_tid = rx_tid_policy,
+ };
+ return wsm_write_mib(priv, WSM_MIB_ID_BLOCK_ACK_POLICY, &val,
+ sizeof(val));
+}
+
+struct wsm_mib_association_mode {
+ u8 flags; /* WSM_ASSOCIATION_MODE_... */
+ u8 preamble; /* WSM_JOIN_PREAMBLE_... */
+ u8 greenfield; /* 1 for greenfield */
+ u8 mpdu_start_spacing;
+ __le32 basic_rate_set;
+} __packed;
+
+static inline int wsm_set_association_mode(struct cw1200_common *priv,
+ struct wsm_mib_association_mode *arg)
+{
+ return wsm_write_mib(priv, WSM_MIB_ID_SET_ASSOCIATION_MODE, arg,
+ sizeof(*arg));
+}
+
+#define WSM_TX_RATE_POLICY_FLAG_TERMINATE_WHEN_FINISHED BIT(2)
+#define WSM_TX_RATE_POLICY_FLAG_COUNT_INITIAL_TRANSMIT BIT(3)
+struct wsm_tx_rate_retry_policy {
+ u8 index;
+ u8 short_retries;
+ u8 long_retries;
+ /* BIT(2) - Terminate retries when Tx rate retry policy
+ * finishes.
+ * BIT(3) - Count initial frame transmission as part of
+ * rate retry counting but not as a retry
+ * attempt */
+ u8 flags;
+ u8 rate_recoveries;
+ u8 reserved[3];
+ __le32 rate_count_indices[3];
+} __packed;
+
+struct wsm_set_tx_rate_retry_policy {
+ u8 num;
+ u8 reserved[3];
+ struct wsm_tx_rate_retry_policy tbl[8];
+} __packed;
+
+static inline int wsm_set_tx_rate_retry_policy(struct cw1200_common *priv,
+ struct wsm_set_tx_rate_retry_policy *arg)
+{
+ size_t size = 4 + arg->num * sizeof(struct wsm_tx_rate_retry_policy);
+ return wsm_write_mib(priv, WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY, arg,
+ size);
+}
+
+/* 4.32 SetEtherTypeDataFrameFilter */
+struct wsm_ether_type_filter_hdr {
+ u8 num; /* Up to WSM_MAX_FILTER_ELEMENTS */
+ u8 reserved[3];
+} __packed;
+
+struct wsm_ether_type_filter {
+ u8 action; /* WSM_FILTER_ACTION_XXX */
+ u8 reserved;
+ __le16 type; /* Type of ethernet frame */
+} __packed;
+
+static inline int wsm_set_ether_type_filter(struct cw1200_common *priv,
+ struct wsm_ether_type_filter_hdr *arg)
+{
+ size_t size = sizeof(struct wsm_ether_type_filter_hdr) +
+ arg->num * sizeof(struct wsm_ether_type_filter);
+ return wsm_write_mib(priv, WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER,
+ arg, size);
+}
+
+/* 4.33 SetUDPPortDataFrameFilter */
+struct wsm_udp_port_filter_hdr {
+ u8 num; /* Up to WSM_MAX_FILTER_ELEMENTS */
+ u8 reserved[3];
+} __packed;
+
+struct wsm_udp_port_filter {
+ u8 action; /* WSM_FILTER_ACTION_XXX */
+ u8 type; /* WSM_FILTER_PORT_TYPE_XXX */
+ __le16 port; /* Port number */
+} __packed;
+
+static inline int wsm_set_udp_port_filter(struct cw1200_common *priv,
+ struct wsm_udp_port_filter_hdr *arg)
+{
+ size_t size = sizeof(struct wsm_udp_port_filter_hdr) +
+ arg->num * sizeof(struct wsm_udp_port_filter);
+ return wsm_write_mib(priv, WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER,
+ arg, size);
+}
+
+/* Undocumented MIBs: */
+/* 4.35 P2PDeviceInfo */
+#define D11_MAX_SSID_LEN (32)
+
+struct wsm_p2p_device_type {
+ __le16 categoryId;
+ u8 oui[4];
+ __le16 subCategoryId;
+} __packed;
+
+struct wsm_p2p_device_info {
+ struct wsm_p2p_device_type primaryDevice;
+ u8 reserved1[3];
+ u8 devNameSize;
+ u8 localDevName[D11_MAX_SSID_LEN];
+ u8 reserved2[3];
+ u8 numSecDevSupported;
+ struct wsm_p2p_device_type secondaryDevices[0];
+} __packed;
+
+/* 4.36 SetWCDMABand - WO */
+struct wsm_cdma_band {
+ u8 WCDMA_Band;
+ u8 reserved[3];
+} __packed;
+
+/* 4.37 GroupTxSequenceCounter - RO */
+struct wsm_group_tx_seq {
+ __le32 bits_47_16;
+ __le16 bits_15_00;
+ __le16 reserved;
+} __packed;
+
+/* 4.39 SetHtProtection - WO */
+#define WSM_DUAL_CTS_PROT_ENB (1 << 0)
+#define WSM_NON_GREENFIELD_STA_PRESENT (1 << 1)
+#define WSM_HT_PROT_MODE__NO_PROT (0 << 2)
+#define WSM_HT_PROT_MODE__NON_MEMBER (1 << 2)
+#define WSM_HT_PROT_MODE__20_MHZ (2 << 2)
+#define WSM_HT_PROT_MODE__NON_HT_MIXED (3 << 2)
+#define WSM_LSIG_TXOP_PROT_FULL (1 << 4)
+#define WSM_LARGE_L_LENGTH_PROT (1 << 5)
+
+struct wsm_ht_protection {
+ __le32 flags;
+} __packed;
+
+/* 4.40 GPIO Command - R/W */
+#define WSM_GPIO_COMMAND_SETUP 0
+#define WSM_GPIO_COMMAND_READ 1
+#define WSM_GPIO_COMMAND_WRITE 2
+#define WSM_GPIO_COMMAND_RESET 3
+#define WSM_GPIO_ALL_PINS 0xFF
+
+struct wsm_gpio_command {
+ u8 GPIO_Command;
+ u8 pin;
+ __le16 config;
+} __packed;
+
+/* 4.41 TSFCounter - RO */
+struct wsm_tsf_counter {
+ __le64 TSF_Counter;
+} __packed;
+
+/* 4.43 Keep alive period */
+struct wsm_keep_alive_period {
+ __le16 keepAlivePeriod;
+ u8 reserved[2];
+} __packed;
+
+static inline int wsm_keep_alive_period(struct cw1200_common *priv,
+ int period)
+{
+ struct wsm_keep_alive_period arg = {
+ .keepAlivePeriod = __cpu_to_le16(period),
+ };
+ return wsm_write_mib(priv, WSM_MIB_ID_KEEP_ALIVE_PERIOD,
+ &arg, sizeof(arg));
+};
+
+/* BSSID filtering */
+struct wsm_set_bssid_filtering {
+ u8 filter;
+ u8 reserved[3];
+} __packed;
+
+static inline int wsm_set_bssid_filtering(struct cw1200_common *priv,
+ bool enabled)
+{
+ struct wsm_set_bssid_filtering arg = {
+ .filter = !enabled,
+ };
+ return wsm_write_mib(priv, WSM_MIB_ID_DISABLE_BSSID_FILTER,
+ &arg, sizeof(arg));
+}
+
+/* Multicast filtering - 4.5 */
+struct wsm_mib_multicast_filter {
+ __le32 enable;
+ __le32 num_addrs;
+ u8 macaddrs[WSM_MAX_GRP_ADDRTABLE_ENTRIES][ETH_ALEN];
+} __packed;
+
+static inline int wsm_set_multicast_filter(struct cw1200_common *priv,
+ struct wsm_mib_multicast_filter *fp)
+{
+ return wsm_write_mib(priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE,
+ fp, sizeof(*fp));
+}
+
+/* ARP IPv4 filtering - 4.10 */
+struct wsm_mib_arp_ipv4_filter {
+ __le32 enable;
+ __be32 ipv4addrs[WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES];
+} __packed;
+
+static inline int wsm_set_arp_ipv4_filter(struct cw1200_common *priv,
+ struct wsm_mib_arp_ipv4_filter *fp)
+{
+ return wsm_write_mib(priv, WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE,
+ fp, sizeof(*fp));
+}
+
+/* P2P Power Save Mode Info - 4.31 */
+struct wsm_p2p_ps_modeinfo {
+ u8 oppPsCTWindow;
+ u8 count;
+ u8 reserved;
+ u8 dtimCount;
+ __le32 duration;
+ __le32 interval;
+ __le32 startTime;
+} __packed;
+
+static inline int wsm_set_p2p_ps_modeinfo(struct cw1200_common *priv,
+ struct wsm_p2p_ps_modeinfo *mi)
+{
+ return wsm_write_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO,
+ mi, sizeof(*mi));
+}
+
+static inline int wsm_get_p2p_ps_modeinfo(struct cw1200_common *priv,
+ struct wsm_p2p_ps_modeinfo *mi)
+{
+ return wsm_read_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO,
+ mi, sizeof(*mi));
+}
+
+/* UseMultiTxConfMessage */
+
+static inline int wsm_use_multi_tx_conf(struct cw1200_common *priv,
+ bool enabled)
+{
+ __le32 arg = enabled ? __cpu_to_le32(1) : 0;
+
+ return wsm_write_mib(priv, WSM_MIB_USE_MULTI_TX_CONF,
+ &arg, sizeof(arg));
+}
+
+
+/* 4.26 SetUpasdInformation */
+struct wsm_uapsd_info {
+ __le16 uapsd_flags;
+ __le16 min_auto_trigger_interval;
+ __le16 max_auto_trigger_interval;
+ __le16 auto_trigger_step;
+};
+
+static inline int wsm_set_uapsd_info(struct cw1200_common *priv,
+ struct wsm_uapsd_info *arg)
+{
+ return wsm_write_mib(priv, WSM_MIB_ID_SET_UAPSD_INFORMATION,
+ arg, sizeof(*arg));
+}
+
+/* 4.22 OverrideInternalTxRate */
+struct wsm_override_internal_txrate {
+ u8 internalTxRate;
+ u8 nonErpInternalTxRate;
+ u8 reserved[2];
+} __packed;
+
+static inline int wsm_set_override_internal_txrate(struct cw1200_common *priv,
+ struct wsm_override_internal_txrate *arg)
+{
+ return wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
+ arg, sizeof(*arg));
+}
+
+/* ******************************************************************** */
+/* WSM TX port control */
+
+void wsm_lock_tx(struct cw1200_common *priv);
+void wsm_lock_tx_async(struct cw1200_common *priv);
+bool wsm_flush_tx(struct cw1200_common *priv);
+void wsm_unlock_tx(struct cw1200_common *priv);
+
+/* ******************************************************************** */
+/* WSM / BH API */
+
+int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len);
+int wsm_handle_rx(struct cw1200_common *priv, u16 id, struct wsm_hdr *wsm,
+ struct sk_buff **skb_p);
+
+/* ******************************************************************** */
+/* wsm_buf API */
+
+struct wsm_buf {
+ u8 *begin;
+ u8 *data;
+ u8 *end;
+};
+
+void wsm_buf_init(struct wsm_buf *buf);
+void wsm_buf_deinit(struct wsm_buf *buf);
+
+/* ******************************************************************** */
+/* wsm_cmd API */
+
+struct wsm_cmd {
+ spinlock_t lock; /* Protect structure from multiple access */
+ int done;
+ u8 *ptr;
+ size_t len;
+ void *arg;
+ int ret;
+ u16 cmd;
+};
+
+/* ******************************************************************** */
+/* WSM TX buffer access */
+
+int wsm_get_tx(struct cw1200_common *priv, u8 **data,
+ size_t *tx_len, int *burst);
+void wsm_txed(struct cw1200_common *priv, u8 *data);
+
+/* ******************************************************************** */
+/* Queue mapping: WSM <---> linux */
+/* Linux: VO VI BE BK */
+/* WSM: BE BK VI VO */
+
+static inline u8 wsm_queue_id_to_linux(u8 queue_id)
+{
+ static const u8 queue_mapping[] = {
+ 2, 3, 1, 0
+ };
+ return queue_mapping[queue_id];
+}
+
+static inline u8 wsm_queue_id_to_wsm(u8 queue_id)
+{
+ static const u8 queue_mapping[] = {
+ 3, 2, 0, 1
+ };
+ return queue_mapping[queue_id];
+}
+
+
+#ifdef CONFIG_CW1200_ETF
+int wsm_raw_cmd(struct cw1200_common *priv, u8 *data, size_t len);
+#endif
+
+#endif /* CW1200_HWIO_H_INCLUDED */
diff --git a/drivers/net/wireless/iwlegacy/commands.h b/drivers/net/wireless/iwlegacy/commands.h
index 3b6c9940089..04842151198 100644
--- a/drivers/net/wireless/iwlegacy/commands.h
+++ b/drivers/net/wireless/iwlegacy/commands.h
@@ -1348,14 +1348,6 @@ struct il_rx_mpdu_res_start {
#define TX_CMD_SEC_KEY128 0x08
/*
- * security overhead sizes
- */
-#define WEP_IV_LEN 4
-#define WEP_ICV_LEN 4
-#define CCMP_MIC_LEN 8
-#define TKIP_ICV_LEN 4
-
-/*
* C_TX = 0x1c (command)
*/
diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/iwlwifi/dvm/commands.h
index 19f3ce7646a..ebdac909f0c 100644
--- a/drivers/net/wireless/iwlwifi/dvm/commands.h
+++ b/drivers/net/wireless/iwlwifi/dvm/commands.h
@@ -1221,14 +1221,6 @@ struct iwl_rx_mpdu_res_start {
#define TX_CMD_SEC_KEY128 0x08
/*
- * security overhead sizes
- */
-#define WEP_IV_LEN 4
-#define WEP_ICV_LEN 4
-#define CCMP_MIC_LEN 8
-#define TKIP_ICV_LEN 4
-
-/*
* REPLY_TX = 0x1c (command)
*/
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index dc50020d746..12337aac294 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -84,15 +84,6 @@ static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
.types = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP),
},
- {
- .max = 1,
- .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
- BIT(NL80211_IFTYPE_P2P_GO),
- },
- {
- .max = 1,
- .types = BIT(NL80211_IFTYPE_P2P_DEVICE),
- },
};
static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = {
@@ -173,10 +164,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->chanctx_data_size = sizeof(u16);
hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
- BIT(NL80211_IFTYPE_P2P_CLIENT) |
- BIT(NL80211_IFTYPE_AP) |
- BIT(NL80211_IFTYPE_P2P_GO) |
- BIT(NL80211_IFTYPE_P2P_DEVICE);
+ BIT(NL80211_IFTYPE_AP);
hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY |
WIPHY_FLAG_DISABLE_BEACON_HINTS |
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c
index bf5f8246385..a35c6aefbc3 100644
--- a/drivers/net/wireless/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/tx.c
@@ -224,13 +224,13 @@ static void iwl_pcie_txq_update_byte_cnt_tbl(struct iwl_trans *trans,
switch (sec_ctl & TX_CMD_SEC_MSK) {
case TX_CMD_SEC_CCM:
- len += CCMP_MIC_LEN;
+ len += IEEE80211_CCMP_MIC_LEN;
break;
case TX_CMD_SEC_TKIP:
- len += TKIP_ICV_LEN;
+ len += IEEE80211_TKIP_ICV_LEN;
break;
case TX_CMD_SEC_WEP:
- len += WEP_IV_LEN + WEP_ICV_LEN;
+ len += IEEE80211_WEP_IV_LEN + IEEE80211_WEP_ICV_LEN;
break;
}
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index b878a32e7a9..69bbf6fdd2d 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -1723,11 +1723,11 @@ static void mac80211_hwsim_free(void)
class_destroy(hwsim_class);
}
-
-static struct device_driver mac80211_hwsim_driver = {
- .name = "mac80211_hwsim",
- .bus = &platform_bus_type,
- .owner = THIS_MODULE,
+static struct platform_driver mac80211_hwsim_driver = {
+ .driver = {
+ .name = "mac80211_hwsim",
+ .owner = THIS_MODULE,
+ },
};
static const struct net_device_ops hwsim_netdev_ops = {
@@ -2169,7 +2169,6 @@ static const struct ieee80211_iface_limit hwsim_if_limits[] = {
#endif
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_P2P_GO) },
- { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) },
};
static struct ieee80211_iface_combination hwsim_if_comb = {
@@ -2219,7 +2218,7 @@ static int __init init_mac80211_hwsim(void)
spin_lock_init(&hwsim_radio_lock);
INIT_LIST_HEAD(&hwsim_radios);
- err = driver_register(&mac80211_hwsim_driver);
+ err = platform_driver_register(&mac80211_hwsim_driver);
if (err)
return err;
@@ -2254,7 +2253,7 @@ static int __init init_mac80211_hwsim(void)
err = -ENOMEM;
goto failed_drvdata;
}
- data->dev->driver = &mac80211_hwsim_driver;
+ data->dev->driver = &mac80211_hwsim_driver.driver;
err = device_bind_driver(data->dev);
if (err != 0) {
printk(KERN_DEBUG
@@ -2295,8 +2294,7 @@ static int __init init_mac80211_hwsim(void)
BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_ADHOC) |
- BIT(NL80211_IFTYPE_MESH_POINT) |
- BIT(NL80211_IFTYPE_P2P_DEVICE);
+ BIT(NL80211_IFTYPE_MESH_POINT);
hw->flags = IEEE80211_HW_MFP_CAPABLE |
IEEE80211_HW_SIGNAL_DBM |
@@ -2564,7 +2562,7 @@ failed_drvdata:
failed:
mac80211_hwsim_free();
failed_unregister_driver:
- driver_unregister(&mac80211_hwsim_driver);
+ platform_driver_unregister(&mac80211_hwsim_driver);
return err;
}
module_init(init_mac80211_hwsim);
@@ -2577,6 +2575,6 @@ static void __exit exit_mac80211_hwsim(void)
mac80211_hwsim_free();
unregister_netdev(hwsim_mon);
- driver_unregister(&mac80211_hwsim_driver);
+ platform_driver_unregister(&mac80211_hwsim_driver);
}
module_exit(exit_mac80211_hwsim);
diff --git a/drivers/net/wireless/mwifiex/Kconfig b/drivers/net/wireless/mwifiex/Kconfig
index 4f614aad9de..f7ff4725506 100644
--- a/drivers/net/wireless/mwifiex/Kconfig
+++ b/drivers/net/wireless/mwifiex/Kconfig
@@ -3,13 +3,13 @@ config MWIFIEX
depends on CFG80211
---help---
This adds support for wireless adapters based on Marvell
- 802.11n chipsets.
+ 802.11n/ac chipsets.
If you choose to build it as a module, it will be called
mwifiex.
config MWIFIEX_SDIO
- tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797"
+ tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797/SD8897"
depends on MWIFIEX && MMC
select FW_LOADER
---help---
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
index e42b266a023..00a82817eb6 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/mwifiex/cfg80211.c
@@ -1231,6 +1231,51 @@ static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy,
return 0;
}
+/* cfg80211 operation handler for del_station.
+ * Function deauthenticates station which value is provided in mac parameter.
+ * If mac is NULL/broadcast, all stations in associated station list are
+ * deauthenticated. If bss is not started or there are no stations in
+ * associated stations list, no action is taken.
+ */
+static int
+mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
+ u8 *mac)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ struct mwifiex_sta_node *sta_node;
+ unsigned long flags;
+
+ if (list_empty(&priv->sta_list) || !priv->bss_started)
+ return 0;
+
+ if (!mac || is_broadcast_ether_addr(mac)) {
+ wiphy_dbg(wiphy, "%s: NULL/broadcast mac address\n", __func__);
+ list_for_each_entry(sta_node, &priv->sta_list, list) {
+ if (mwifiex_send_cmd_sync(priv,
+ HostCmd_CMD_UAP_STA_DEAUTH,
+ HostCmd_ACT_GEN_SET, 0,
+ sta_node->mac_addr))
+ return -1;
+ mwifiex_uap_del_sta_data(priv, sta_node);
+ }
+ } else {
+ wiphy_dbg(wiphy, "%s: mac address %pM\n", __func__, mac);
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+ sta_node = mwifiex_get_sta_entry(priv, mac);
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+ if (sta_node) {
+ if (mwifiex_send_cmd_sync(priv,
+ HostCmd_CMD_UAP_STA_DEAUTH,
+ HostCmd_ACT_GEN_SET, 0,
+ sta_node->mac_addr))
+ return -1;
+ mwifiex_uap_del_sta_data(priv, sta_node);
+ }
+ }
+
+ return 0;
+}
+
static int
mwifiex_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant)
{
@@ -1859,6 +1904,7 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy,
int i, offset, ret;
struct ieee80211_channel *chan;
struct ieee_types_header *ie;
+ struct mwifiex_user_scan_cfg *user_scan_cfg;
wiphy_dbg(wiphy, "info: received scan request on %s\n", dev->name);
@@ -1869,20 +1915,22 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy,
return -EBUSY;
}
- if (priv->user_scan_cfg) {
+ /* Block scan request if scan operation or scan cleanup when interface
+ * is disabled is in process
+ */
+ if (priv->scan_request || priv->scan_aborting) {
dev_err(priv->adapter->dev, "cmd: Scan already in process..\n");
return -EBUSY;
}
- priv->user_scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg),
- GFP_KERNEL);
- if (!priv->user_scan_cfg)
+ user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL);
+ if (!user_scan_cfg)
return -ENOMEM;
priv->scan_request = request;
- priv->user_scan_cfg->num_ssids = request->n_ssids;
- priv->user_scan_cfg->ssid_list = request->ssids;
+ user_scan_cfg->num_ssids = request->n_ssids;
+ user_scan_cfg->ssid_list = request->ssids;
if (request->ie && request->ie_len) {
offset = 0;
@@ -1902,25 +1950,25 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy,
for (i = 0; i < min_t(u32, request->n_channels,
MWIFIEX_USER_SCAN_CHAN_MAX); i++) {
chan = request->channels[i];
- priv->user_scan_cfg->chan_list[i].chan_number = chan->hw_value;
- priv->user_scan_cfg->chan_list[i].radio_type = chan->band;
+ user_scan_cfg->chan_list[i].chan_number = chan->hw_value;
+ user_scan_cfg->chan_list[i].radio_type = chan->band;
if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
- priv->user_scan_cfg->chan_list[i].scan_type =
+ user_scan_cfg->chan_list[i].scan_type =
MWIFIEX_SCAN_TYPE_PASSIVE;
else
- priv->user_scan_cfg->chan_list[i].scan_type =
+ user_scan_cfg->chan_list[i].scan_type =
MWIFIEX_SCAN_TYPE_ACTIVE;
- priv->user_scan_cfg->chan_list[i].scan_time = 0;
+ user_scan_cfg->chan_list[i].scan_time = 0;
}
- ret = mwifiex_scan_networks(priv, priv->user_scan_cfg);
+ ret = mwifiex_scan_networks(priv, user_scan_cfg);
+ kfree(user_scan_cfg);
if (ret) {
dev_err(priv->adapter->dev, "scan failed: %d\n", ret);
+ priv->scan_aborting = false;
priv->scan_request = NULL;
- kfree(priv->user_scan_cfg);
- priv->user_scan_cfg = NULL;
return ret;
}
@@ -2419,6 +2467,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
.change_beacon = mwifiex_cfg80211_change_beacon,
.set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config,
.set_antenna = mwifiex_cfg80211_set_antenna,
+ .del_station = mwifiex_cfg80211_del_station,
#ifdef CONFIG_PM
.suspend = mwifiex_cfg80211_suspend,
.resume = mwifiex_cfg80211_resume,
diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c
index 26755d9acb5..2d761477d15 100644
--- a/drivers/net/wireless/mwifiex/cmdevt.c
+++ b/drivers/net/wireless/mwifiex/cmdevt.c
@@ -570,6 +570,7 @@ int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no,
case HostCmd_CMD_UAP_SYS_CONFIG:
case HostCmd_CMD_UAP_BSS_START:
case HostCmd_CMD_UAP_BSS_STOP:
+ case HostCmd_CMD_UAP_STA_DEAUTH:
ret = mwifiex_uap_prepare_cmd(priv, cmd_no, cmd_action,
cmd_oid, data_buf,
cmd_ptr);
diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h
index 1f7578d553e..d6ada7354c1 100644
--- a/drivers/net/wireless/mwifiex/fw.h
+++ b/drivers/net/wireless/mwifiex/fw.h
@@ -271,6 +271,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define HostCmd_CMD_802_11_SUBSCRIBE_EVENT 0x0075
#define HostCmd_CMD_802_11_TX_RATE_QUERY 0x007f
#define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS 0x0083
+#define HostCmd_CMD_CFG_DATA 0x008f
#define HostCmd_CMD_VERSION_EXT 0x0097
#define HostCmd_CMD_MEF_CFG 0x009a
#define HostCmd_CMD_RSSI_INFO 0x00a4
@@ -279,6 +280,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define HostCmd_CMD_UAP_SYS_CONFIG 0x00b0
#define HostCmd_CMD_UAP_BSS_START 0x00b1
#define HostCmd_CMD_UAP_BSS_STOP 0x00b2
+#define HostCmd_CMD_UAP_STA_DEAUTH 0x00b5
#define HostCmd_CMD_11N_CFG 0x00cd
#define HostCmd_CMD_11N_ADDBA_REQ 0x00ce
#define HostCmd_CMD_11N_ADDBA_RSP 0x00cf
@@ -464,6 +466,8 @@ enum P2P_MODES {
#define MWIFIEX_CRITERIA_UNICAST BIT(1)
#define MWIFIEX_CRITERIA_MULTICAST BIT(3)
+#define CFG_DATA_TYPE_CAL 2
+
struct mwifiex_ie_types_header {
__le16 type;
__le16 len;
@@ -1197,6 +1201,11 @@ struct host_cmd_ds_amsdu_aggr_ctrl {
__le16 curr_buf_size;
} __packed;
+struct host_cmd_ds_sta_deauth {
+ u8 mac[ETH_ALEN];
+ __le16 reason;
+} __packed;
+
struct mwifiex_ie_types_wmm_param_set {
struct mwifiex_ie_types_header header;
u8 wmm_ie[1];
@@ -1573,6 +1582,12 @@ struct mwifiex_ie_list {
struct mwifiex_ie ie_list[MAX_MGMT_IE_INDEX];
} __packed;
+struct host_cmd_ds_802_11_cfg_data {
+ __le16 action;
+ __le16 type;
+ __le16 data_len;
+} __packed;
+
struct host_cmd_ds_command {
__le16 command;
__le16 size;
@@ -1630,7 +1645,9 @@ struct host_cmd_ds_command {
struct host_cmd_ds_802_11_eeprom_access eeprom;
struct host_cmd_ds_802_11_subsc_evt subsc_evt;
struct host_cmd_ds_sys_config uap_sys_config;
+ struct host_cmd_ds_sta_deauth sta_deauth;
struct host_cmd_11ac_vht_cfg vht_cfg;
+ struct host_cmd_ds_802_11_cfg_data cfg_data;
} params;
} __packed;
diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c
index 9f44fda19db..c7f11c0c3bb 100644
--- a/drivers/net/wireless/mwifiex/init.c
+++ b/drivers/net/wireless/mwifiex/init.c
@@ -59,6 +59,9 @@ static void scan_delay_timer_fn(unsigned long data)
struct cmd_ctrl_node *cmd_node, *tmp_node;
unsigned long flags;
+ if (adapter->surprise_removed)
+ return;
+
if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT) {
/*
* Abort scan operation by cancelling all pending scan
@@ -78,19 +81,13 @@ static void scan_delay_timer_fn(unsigned long data)
adapter->empty_tx_q_cnt = 0;
spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
- if (priv->user_scan_cfg) {
- if (priv->scan_request) {
- dev_dbg(priv->adapter->dev,
- "info: aborting scan\n");
- cfg80211_scan_done(priv->scan_request, 1);
- priv->scan_request = NULL;
- } else {
- dev_dbg(priv->adapter->dev,
- "info: scan already aborted\n");
- }
-
- kfree(priv->user_scan_cfg);
- priv->user_scan_cfg = NULL;
+ if (priv->scan_request) {
+ dev_dbg(adapter->dev, "info: aborting scan\n");
+ cfg80211_scan_done(priv->scan_request, 1);
+ priv->scan_request = NULL;
+ } else {
+ priv->scan_aborting = false;
+ dev_dbg(adapter->dev, "info: scan already aborted\n");
}
goto done;
}
@@ -447,23 +444,29 @@ static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter)
}
/*
- * This function frees the adapter structure.
+ * This function performs cleanup for adapter structure.
*
- * The freeing operation is done recursively, by canceling all
- * pending commands, freeing the member buffers previously
- * allocated (command buffers, scan table buffer, sleep confirm
- * command buffer), stopping the timers and calling the cleanup
- * routines for every interface, before the actual adapter
- * structure is freed.
+ * The cleanup is done recursively, by canceling all pending
+ * commands, freeing the member buffers previously allocated
+ * (command buffers, scan table buffer, sleep confirm command
+ * buffer), stopping the timers and calling the cleanup routines
+ * for every interface.
*/
static void
-mwifiex_free_adapter(struct mwifiex_adapter *adapter)
+mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter)
{
+ int i;
+
if (!adapter) {
pr_err("%s: adapter is NULL\n", __func__);
return;
}
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i])
+ del_timer_sync(&adapter->priv[i]->scan_delay_timer);
+ }
+
mwifiex_cancel_all_pending_cmd(adapter);
/* Free lock variables */
@@ -684,7 +687,6 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
int ret = -EINPROGRESS;
struct mwifiex_private *priv;
s32 i;
- unsigned long flags;
struct sk_buff *skb;
/* mwifiex already shutdown */
@@ -719,7 +721,7 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
}
}
- spin_lock_irqsave(&adapter->mwifiex_lock, flags);
+ spin_lock(&adapter->mwifiex_lock);
if (adapter->if_ops.data_complete) {
while ((skb = skb_dequeue(&adapter->usb_rx_data_q))) {
@@ -733,10 +735,9 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
}
}
- /* Free adapter structure */
- mwifiex_free_adapter(adapter);
+ mwifiex_adapter_cleanup(adapter);
- spin_unlock_irqrestore(&adapter->mwifiex_lock, flags);
+ spin_unlock(&adapter->mwifiex_lock);
/* Notify completion */
ret = mwifiex_shutdown_fw_complete(adapter);
diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c
index 6bcb66e6e97..122175af18c 100644
--- a/drivers/net/wireless/mwifiex/join.c
+++ b/drivers/net/wireless/mwifiex/join.c
@@ -919,9 +919,8 @@ mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv,
memcpy(&priv->curr_bss_params.data_rates,
&adhoc_start->data_rate, priv->curr_bss_params.num_of_rates);
- dev_dbg(adapter->dev, "info: ADHOC_S_CMD: rates=%02x %02x %02x %02x\n",
- adhoc_start->data_rate[0], adhoc_start->data_rate[1],
- adhoc_start->data_rate[2], adhoc_start->data_rate[3]);
+ dev_dbg(adapter->dev, "info: ADHOC_S_CMD: rates=%4ph\n",
+ adhoc_start->data_rate);
dev_dbg(adapter->dev, "info: ADHOC_S_CMD: AD-HOC Start command is ready\n");
diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c
index 2eb88ea9acf..5bc7ef8d04d 100644
--- a/drivers/net/wireless/mwifiex/main.c
+++ b/drivers/net/wireless/mwifiex/main.c
@@ -25,6 +25,8 @@
#define VERSION "1.0"
const char driver_version[] = "mwifiex " VERSION " (%s) ";
+static char *cal_data_cfg;
+module_param(cal_data_cfg, charp, 0);
/*
* This function registers the device and performs all the necessary
@@ -336,6 +338,13 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
dev_notice(adapter->dev, "WLAN FW is active\n");
+ if (cal_data_cfg) {
+ if ((request_firmware(&adapter->cal_data, cal_data_cfg,
+ adapter->dev)) < 0)
+ dev_err(adapter->dev,
+ "Cal data request_firmware() failed\n");
+ }
+
adapter->init_wait_q_woken = false;
ret = mwifiex_init_fw(adapter);
if (ret == -1) {
@@ -390,6 +399,10 @@ err_init_fw:
pr_debug("info: %s: unregister device\n", __func__);
adapter->if_ops.unregister_dev(adapter);
done:
+ if (adapter->cal_data) {
+ release_firmware(adapter->cal_data);
+ adapter->cal_data = NULL;
+ }
release_firmware(adapter->firmware);
complete(&adapter->fw_load);
return;
@@ -436,6 +449,7 @@ mwifiex_close(struct net_device *dev)
dev_dbg(priv->adapter->dev, "aborting scan on ndo_stop\n");
cfg80211_scan_done(priv->scan_request, 1);
priv->scan_request = NULL;
+ priv->scan_aborting = true;
}
return 0;
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h
index 4ef67fca06d..0832c2437da 100644
--- a/drivers/net/wireless/mwifiex/main.h
+++ b/drivers/net/wireless/mwifiex/main.h
@@ -492,7 +492,6 @@ struct mwifiex_private {
struct semaphore async_sem;
u8 report_scan_result;
struct cfg80211_scan_request *scan_request;
- struct mwifiex_user_scan_cfg *user_scan_cfg;
u8 cfg_bssid[6];
struct wps wps;
u8 scan_block;
@@ -510,6 +509,7 @@ struct mwifiex_private {
u8 ap_11ac_enabled;
u32 mgmt_frame_mask;
struct mwifiex_roc_cfg roc_cfg;
+ bool scan_aborting;
};
enum mwifiex_ba_status {
@@ -730,6 +730,7 @@ struct mwifiex_adapter {
u16 max_mgmt_ie_index;
u8 scan_delay_cnt;
u8 empty_tx_q_cnt;
+ const struct firmware *cal_data;
/* 11AC */
u32 is_hw_11ac_capable;
@@ -1115,6 +1116,8 @@ int mwifiex_set_mgmt_ies(struct mwifiex_private *priv,
struct cfg80211_beacon_data *data);
int mwifiex_del_mgmt_ies(struct mwifiex_private *priv);
u8 *mwifiex_11d_code_2_region(u8 code);
+void mwifiex_uap_del_sta_data(struct mwifiex_private *priv,
+ struct mwifiex_sta_node *node);
extern const struct ethtool_ops mwifiex_ethtool_ops;
diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c
index 9cf5d8f07df..801b6b72837 100644
--- a/drivers/net/wireless/mwifiex/scan.c
+++ b/drivers/net/wireless/mwifiex/scan.c
@@ -1784,22 +1784,17 @@ check_next_scan:
if (priv->report_scan_result)
priv->report_scan_result = false;
- if (priv->user_scan_cfg) {
- if (priv->scan_request) {
- dev_dbg(priv->adapter->dev,
- "info: notifying scan done\n");
- cfg80211_scan_done(priv->scan_request, 0);
- priv->scan_request = NULL;
- } else {
- dev_dbg(priv->adapter->dev,
- "info: scan already aborted\n");
- }
-
- kfree(priv->user_scan_cfg);
- priv->user_scan_cfg = NULL;
+ if (priv->scan_request) {
+ dev_dbg(adapter->dev, "info: notifying scan done\n");
+ cfg80211_scan_done(priv->scan_request, 0);
+ priv->scan_request = NULL;
+ } else {
+ priv->scan_aborting = false;
+ dev_dbg(adapter->dev, "info: scan already aborted\n");
}
} else {
- if (priv->user_scan_cfg && !priv->scan_request) {
+ if ((priv->scan_aborting && !priv->scan_request) ||
+ priv->scan_block) {
spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
flags);
adapter->scan_delay_cnt = MWIFIEX_MAX_SCAN_DELAY_CNT;
diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c
index 363ba31b58b..5ee5ed02ecc 100644
--- a/drivers/net/wireless/mwifiex/sdio.c
+++ b/drivers/net/wireless/mwifiex/sdio.c
@@ -77,6 +77,17 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
+ if (id->driver_data) {
+ struct mwifiex_sdio_device *data = (void *)id->driver_data;
+
+ card->firmware = data->firmware;
+ card->reg = data->reg;
+ card->max_ports = data->max_ports;
+ card->mp_agg_pkt_limit = data->mp_agg_pkt_limit;
+ card->supports_sdio_new_mode = data->supports_sdio_new_mode;
+ card->has_control_mask = data->has_control_mask;
+ }
+
sdio_claim_host(func);
ret = sdio_enable_func(func);
sdio_release_host(func);
@@ -251,12 +262,19 @@ static int mwifiex_sdio_resume(struct device *dev)
#define SDIO_DEVICE_ID_MARVELL_8787 (0x9119)
/* Device ID for SD8797 */
#define SDIO_DEVICE_ID_MARVELL_8797 (0x9129)
+/* Device ID for SD8897 */
+#define SDIO_DEVICE_ID_MARVELL_8897 (0x912d)
/* WLAN IDs */
static const struct sdio_device_id mwifiex_ids[] = {
- {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786)},
- {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787)},
- {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797)},
+ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786),
+ .driver_data = (unsigned long) &mwifiex_sdio_sd8786},
+ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787),
+ .driver_data = (unsigned long) &mwifiex_sdio_sd8787},
+ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797),
+ .driver_data = (unsigned long) &mwifiex_sdio_sd8797},
+ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897),
+ .driver_data = (unsigned long) &mwifiex_sdio_sd8897},
{},
};
@@ -282,13 +300,13 @@ static struct sdio_driver mwifiex_sdio = {
* This function writes data into SDIO card register.
*/
static int
-mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u32 data)
+mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u8 data)
{
struct sdio_mmc_card *card = adapter->card;
int ret = -1;
sdio_claim_host(card->func);
- sdio_writeb(card->func, (u8) data, reg, &ret);
+ sdio_writeb(card->func, data, reg, &ret);
sdio_release_host(card->func);
return ret;
@@ -298,7 +316,7 @@ mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u32 data)
* This function reads data from SDIO card register.
*/
static int
-mwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u32 *data)
+mwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u8 *data)
{
struct sdio_mmc_card *card = adapter->card;
int ret = -1;
@@ -400,7 +418,40 @@ static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter)
}
/*
- * This function initializes the IO ports.
+ * This function is used to initialize IO ports for the
+ * chipsets supporting SDIO new mode eg SD8897.
+ */
+static int mwifiex_init_sdio_new_mode(struct mwifiex_adapter *adapter)
+{
+ u8 reg;
+
+ adapter->ioport = MEM_PORT;
+
+ /* enable sdio new mode */
+ if (mwifiex_read_reg(adapter, CARD_CONFIG_2_1_REG, &reg))
+ return -1;
+ if (mwifiex_write_reg(adapter, CARD_CONFIG_2_1_REG,
+ reg | CMD53_NEW_MODE))
+ return -1;
+
+ /* Configure cmd port and enable reading rx length from the register */
+ if (mwifiex_read_reg(adapter, CMD_CONFIG_0, &reg))
+ return -1;
+ if (mwifiex_write_reg(adapter, CMD_CONFIG_0, reg | CMD_PORT_RD_LEN_EN))
+ return -1;
+
+ /* Enable Dnld/Upld ready auto reset for cmd port after cmd53 is
+ * completed
+ */
+ if (mwifiex_read_reg(adapter, CMD_CONFIG_1, &reg))
+ return -1;
+ if (mwifiex_write_reg(adapter, CMD_CONFIG_1, reg | CMD_PORT_AUTO_EN))
+ return -1;
+
+ return 0;
+}
+
+/* This function initializes the IO ports.
*
* The following operations are performed -
* - Read the IO ports (0, 1 and 2)
@@ -409,10 +460,17 @@ static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter)
*/
static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter)
{
- u32 reg;
+ u8 reg;
+ struct sdio_mmc_card *card = adapter->card;
adapter->ioport = 0;
+ if (card->supports_sdio_new_mode) {
+ if (mwifiex_init_sdio_new_mode(adapter))
+ return -1;
+ goto cont;
+ }
+
/* Read the IO port */
if (!mwifiex_read_reg(adapter, IO_PORT_0_REG, &reg))
adapter->ioport |= (reg & 0xff);
@@ -428,19 +486,19 @@ static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter)
adapter->ioport |= ((reg & 0xff) << 16);
else
return -1;
-
+cont:
pr_debug("info: SDIO FUNC1 IO port: %#x\n", adapter->ioport);
/* Set Host interrupt reset to read to clear */
if (!mwifiex_read_reg(adapter, HOST_INT_RSR_REG, &reg))
mwifiex_write_reg(adapter, HOST_INT_RSR_REG,
- reg | SDIO_INT_MASK);
+ reg | card->reg->sdio_int_mask);
else
return -1;
/* Dnld/Upld ready set to auto reset */
- if (!mwifiex_read_reg(adapter, CARD_MISC_CFG_REG, &reg))
- mwifiex_write_reg(adapter, CARD_MISC_CFG_REG,
+ if (!mwifiex_read_reg(adapter, card->reg->card_misc_cfg_reg, &reg))
+ mwifiex_write_reg(adapter, card->reg->card_misc_cfg_reg,
reg | AUTO_RE_ENABLE_INT);
else
return -1;
@@ -486,34 +544,42 @@ static int mwifiex_write_data_to_card(struct mwifiex_adapter *adapter,
static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port)
{
struct sdio_mmc_card *card = adapter->card;
- u16 rd_bitmap = card->mp_rd_bitmap;
+ const struct mwifiex_sdio_card_reg *reg = card->reg;
+ u32 rd_bitmap = card->mp_rd_bitmap;
- dev_dbg(adapter->dev, "data: mp_rd_bitmap=0x%04x\n", rd_bitmap);
+ dev_dbg(adapter->dev, "data: mp_rd_bitmap=0x%08x\n", rd_bitmap);
- if (!(rd_bitmap & (CTRL_PORT_MASK | DATA_PORT_MASK)))
- return -1;
+ if (card->supports_sdio_new_mode) {
+ if (!(rd_bitmap & reg->data_port_mask))
+ return -1;
+ } else {
+ if (!(rd_bitmap & (CTRL_PORT_MASK | reg->data_port_mask)))
+ return -1;
+ }
- if (card->mp_rd_bitmap & CTRL_PORT_MASK) {
- card->mp_rd_bitmap &= (u16) (~CTRL_PORT_MASK);
+ if ((card->has_control_mask) &&
+ (card->mp_rd_bitmap & CTRL_PORT_MASK)) {
+ card->mp_rd_bitmap &= (u32) (~CTRL_PORT_MASK);
*port = CTRL_PORT;
- dev_dbg(adapter->dev, "data: port=%d mp_rd_bitmap=0x%04x\n",
+ dev_dbg(adapter->dev, "data: port=%d mp_rd_bitmap=0x%08x\n",
*port, card->mp_rd_bitmap);
- } else {
- if (card->mp_rd_bitmap & (1 << card->curr_rd_port)) {
- card->mp_rd_bitmap &= (u16)
- (~(1 << card->curr_rd_port));
- *port = card->curr_rd_port;
+ return 0;
+ }
- if (++card->curr_rd_port == MAX_PORT)
- card->curr_rd_port = 1;
- } else {
- return -1;
- }
+ if (!(card->mp_rd_bitmap & (1 << card->curr_rd_port)))
+ return -1;
+
+ /* We are now handling the SDIO data ports */
+ card->mp_rd_bitmap &= (u32)(~(1 << card->curr_rd_port));
+ *port = card->curr_rd_port;
+
+ if (++card->curr_rd_port == card->max_ports)
+ card->curr_rd_port = reg->start_rd_port;
+
+ dev_dbg(adapter->dev,
+ "data: port=%d mp_rd_bitmap=0x%08x -> 0x%08x\n",
+ *port, rd_bitmap, card->mp_rd_bitmap);
- dev_dbg(adapter->dev,
- "data: port=%d mp_rd_bitmap=0x%04x -> 0x%04x\n",
- *port, rd_bitmap, card->mp_rd_bitmap);
- }
return 0;
}
@@ -524,35 +590,45 @@ static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port)
* increased (provided it does not reach the maximum limit, in which
* case it is reset to 1)
*/
-static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u8 *port)
+static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u32 *port)
{
struct sdio_mmc_card *card = adapter->card;
- u16 wr_bitmap = card->mp_wr_bitmap;
+ const struct mwifiex_sdio_card_reg *reg = card->reg;
+ u32 wr_bitmap = card->mp_wr_bitmap;
- dev_dbg(adapter->dev, "data: mp_wr_bitmap=0x%04x\n", wr_bitmap);
+ dev_dbg(adapter->dev, "data: mp_wr_bitmap=0x%08x\n", wr_bitmap);
- if (!(wr_bitmap & card->mp_data_port_mask))
+ if (card->supports_sdio_new_mode &&
+ !(wr_bitmap & reg->data_port_mask)) {
+ adapter->data_sent = true;
+ return -EBUSY;
+ } else if (!card->supports_sdio_new_mode &&
+ !(wr_bitmap & card->mp_data_port_mask)) {
return -1;
+ }
if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) {
- card->mp_wr_bitmap &= (u16) (~(1 << card->curr_wr_port));
+ card->mp_wr_bitmap &= (u32) (~(1 << card->curr_wr_port));
*port = card->curr_wr_port;
- if (++card->curr_wr_port == card->mp_end_port)
- card->curr_wr_port = 1;
+ if (((card->supports_sdio_new_mode) &&
+ (++card->curr_wr_port == card->max_ports)) ||
+ ((!card->supports_sdio_new_mode) &&
+ (++card->curr_wr_port == card->mp_end_port)))
+ card->curr_wr_port = reg->start_wr_port;
} else {
adapter->data_sent = true;
return -EBUSY;
}
- if (*port == CTRL_PORT) {
- dev_err(adapter->dev, "invalid data port=%d cur port=%d"
- " mp_wr_bitmap=0x%04x -> 0x%04x\n",
+ if ((card->has_control_mask) && (*port == CTRL_PORT)) {
+ dev_err(adapter->dev,
+ "invalid data port=%d cur port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n",
*port, card->curr_wr_port, wr_bitmap,
card->mp_wr_bitmap);
return -1;
}
- dev_dbg(adapter->dev, "data: port=%d mp_wr_bitmap=0x%04x -> 0x%04x\n",
+ dev_dbg(adapter->dev, "data: port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n",
*port, wr_bitmap, card->mp_wr_bitmap);
return 0;
@@ -564,11 +640,12 @@ static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u8 *port)
static int
mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits)
{
+ struct sdio_mmc_card *card = adapter->card;
u32 tries;
- u32 cs;
+ u8 cs;
for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
- if (mwifiex_read_reg(adapter, CARD_STATUS_REG, &cs))
+ if (mwifiex_read_reg(adapter, card->reg->poll_reg, &cs))
break;
else if ((cs & bits) == bits)
return 0;
@@ -587,12 +664,14 @@ mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits)
static int
mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat)
{
- u32 fws0, fws1;
+ struct sdio_mmc_card *card = adapter->card;
+ const struct mwifiex_sdio_card_reg *reg = card->reg;
+ u8 fws0, fws1;
- if (mwifiex_read_reg(adapter, CARD_FW_STATUS0_REG, &fws0))
+ if (mwifiex_read_reg(adapter, reg->status_reg_0, &fws0))
return -1;
- if (mwifiex_read_reg(adapter, CARD_FW_STATUS1_REG, &fws1))
+ if (mwifiex_read_reg(adapter, reg->status_reg_1, &fws1))
return -1;
*dat = (u16) ((fws1 << 8) | fws0);
@@ -608,14 +687,14 @@ mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat)
*/
static int mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter)
{
- u32 host_int_mask;
+ u8 host_int_mask, host_int_disable = HOST_INT_DISABLE;
/* Read back the host_int_mask register */
if (mwifiex_read_reg(adapter, HOST_INT_MASK_REG, &host_int_mask))
return -1;
/* Update with the mask and write back to the register */
- host_int_mask &= ~HOST_INT_DISABLE;
+ host_int_mask &= ~host_int_disable;
if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG, host_int_mask)) {
dev_err(adapter->dev, "disable host interrupt failed\n");
@@ -633,8 +712,11 @@ static int mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter)
*/
static int mwifiex_sdio_enable_host_int(struct mwifiex_adapter *adapter)
{
+ struct sdio_mmc_card *card = adapter->card;
+
/* Simply write the mask to the register */
- if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG, HOST_INT_ENABLE)) {
+ if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG,
+ card->reg->host_int_enable)) {
dev_err(adapter->dev, "enable host interrupt failed\n");
return -1;
}
@@ -686,11 +768,13 @@ static int mwifiex_sdio_card_to_host(struct mwifiex_adapter *adapter,
static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
struct mwifiex_fw_image *fw)
{
+ struct sdio_mmc_card *card = adapter->card;
+ const struct mwifiex_sdio_card_reg *reg = card->reg;
int ret;
u8 *firmware = fw->fw_buf;
u32 firmware_len = fw->fw_len;
u32 offset = 0;
- u32 base0, base1;
+ u8 base0, base1;
u8 *fwbuf;
u16 len = 0;
u32 txlen, tx_blocks = 0, tries;
@@ -727,7 +811,7 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
break;
for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
- ret = mwifiex_read_reg(adapter, HOST_F1_RD_BASE_0,
+ ret = mwifiex_read_reg(adapter, reg->base_0_reg,
&base0);
if (ret) {
dev_err(adapter->dev,
@@ -736,7 +820,7 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
base0, base0);
goto done;
}
- ret = mwifiex_read_reg(adapter, HOST_F1_RD_BASE_1,
+ ret = mwifiex_read_reg(adapter, reg->base_1_reg,
&base1);
if (ret) {
dev_err(adapter->dev,
@@ -828,10 +912,11 @@ done:
static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
u32 poll_num)
{
+ struct sdio_mmc_card *card = adapter->card;
int ret = 0;
u16 firmware_stat;
u32 tries;
- u32 winner_status;
+ u8 winner_status;
/* Wait for firmware initialization event */
for (tries = 0; tries < poll_num; tries++) {
@@ -849,7 +934,7 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
if (ret) {
if (mwifiex_read_reg
- (adapter, CARD_FW_STATUS0_REG, &winner_status))
+ (adapter, card->reg->status_reg_0, &winner_status))
winner_status = 0;
if (winner_status)
@@ -866,12 +951,12 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
{
struct sdio_mmc_card *card = adapter->card;
- u32 sdio_ireg;
+ u8 sdio_ireg;
unsigned long flags;
- if (mwifiex_read_data_sync(adapter, card->mp_regs, MAX_MP_REGS,
- REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK,
- 0)) {
+ if (mwifiex_read_data_sync(adapter, card->mp_regs,
+ card->reg->max_mp_regs,
+ REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) {
dev_err(adapter->dev, "read mp_regs failed\n");
return;
}
@@ -880,6 +965,9 @@ static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
if (sdio_ireg) {
/*
* DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
+ * For SDIO new mode CMD port interrupts
+ * DN_LD_CMD_PORT_HOST_INT_STATUS and/or
+ * UP_LD_CMD_PORT_HOST_INT_STATUS
* Clear the interrupt status register
*/
dev_dbg(adapter->dev, "int: sdio_ireg = %#x\n", sdio_ireg);
@@ -1003,11 +1091,11 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
s32 f_aggr_cur = 0;
struct sk_buff *skb_deaggr;
u32 pind;
- u32 pkt_len, pkt_type = 0;
+ u32 pkt_len, pkt_type, mport;
u8 *curr_ptr;
u32 rx_len = skb->len;
- if (port == CTRL_PORT) {
+ if ((card->has_control_mask) && (port == CTRL_PORT)) {
/* Read the command Resp without aggr */
dev_dbg(adapter->dev, "info: %s: no aggregation for cmd "
"response\n", __func__);
@@ -1024,7 +1112,10 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
goto rx_curr_single;
}
- if (card->mp_rd_bitmap & (~((u16) CTRL_PORT_MASK))) {
+ if ((!card->has_control_mask && (card->mp_rd_bitmap &
+ card->reg->data_port_mask)) ||
+ (card->has_control_mask && (card->mp_rd_bitmap &
+ (~((u32) CTRL_PORT_MASK))))) {
/* Some more data RX pending */
dev_dbg(adapter->dev, "info: %s: not last packet\n", __func__);
@@ -1060,10 +1151,10 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
if (f_aggr_cur) {
dev_dbg(adapter->dev, "info: current packet aggregation\n");
/* Curr pkt can be aggregated */
- MP_RX_AGGR_SETUP(card, skb, port);
+ mp_rx_aggr_setup(card, skb, port);
if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) ||
- MP_RX_AGGR_PORT_LIMIT_REACHED(card)) {
+ mp_rx_aggr_port_limit_reached(card)) {
dev_dbg(adapter->dev, "info: %s: aggregated packet "
"limit reached\n", __func__);
/* No more pkts allowed in Aggr buf, rx it */
@@ -1076,11 +1167,28 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
dev_dbg(adapter->dev, "info: do_rx_aggr: num of packets: %d\n",
card->mpa_rx.pkt_cnt);
+ if (card->supports_sdio_new_mode) {
+ int i;
+ u32 port_count;
+
+ for (i = 0, port_count = 0; i < card->max_ports; i++)
+ if (card->mpa_rx.ports & BIT(i))
+ port_count++;
+
+ /* Reading data from "start_port + 0" to "start_port +
+ * port_count -1", so decrease the count by 1
+ */
+ port_count--;
+ mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+ (port_count << 8)) + card->mpa_rx.start_port;
+ } else {
+ mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+ (card->mpa_rx.ports << 4)) +
+ card->mpa_rx.start_port;
+ }
+
if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf,
- card->mpa_rx.buf_len,
- (adapter->ioport | 0x1000 |
- (card->mpa_rx.ports << 4)) +
- card->mpa_rx.start_port, 1))
+ card->mpa_rx.buf_len, mport, 1))
goto error;
curr_ptr = card->mpa_rx.buf;
@@ -1167,6 +1275,7 @@ error:
static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
{
struct sdio_mmc_card *card = adapter->card;
+ const struct mwifiex_sdio_card_reg *reg = card->reg;
int ret = 0;
u8 sdio_ireg;
struct sk_buff *skb;
@@ -1175,6 +1284,8 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
u32 rx_blocks;
u16 rx_len;
unsigned long flags;
+ u32 bitmap;
+ u8 cr;
spin_lock_irqsave(&adapter->int_lock, flags);
sdio_ireg = adapter->int_status;
@@ -1184,10 +1295,60 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
if (!sdio_ireg)
return ret;
+ /* Following interrupt is only for SDIO new mode */
+ if (sdio_ireg & DN_LD_CMD_PORT_HOST_INT_STATUS && adapter->cmd_sent)
+ adapter->cmd_sent = false;
+
+ /* Following interrupt is only for SDIO new mode */
+ if (sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) {
+ u32 pkt_type;
+
+ /* read the len of control packet */
+ rx_len = card->mp_regs[CMD_RD_LEN_1] << 8;
+ rx_len |= (u16) card->mp_regs[CMD_RD_LEN_0];
+ rx_blocks = DIV_ROUND_UP(rx_len, MWIFIEX_SDIO_BLOCK_SIZE);
+ if (rx_len <= INTF_HEADER_LEN ||
+ (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) >
+ MWIFIEX_RX_DATA_BUF_SIZE)
+ return -1;
+ rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE);
+
+ skb = dev_alloc_skb(rx_len);
+ if (!skb)
+ return -1;
+
+ skb_put(skb, rx_len);
+
+ if (mwifiex_sdio_card_to_host(adapter, &pkt_type, skb->data,
+ skb->len, adapter->ioport |
+ CMD_PORT_SLCT)) {
+ dev_err(adapter->dev,
+ "%s: failed to card_to_host", __func__);
+ dev_kfree_skb_any(skb);
+ goto term_cmd;
+ }
+
+ if ((pkt_type != MWIFIEX_TYPE_CMD) &&
+ (pkt_type != MWIFIEX_TYPE_EVENT))
+ dev_err(adapter->dev,
+ "%s:Received wrong packet on cmd port",
+ __func__);
+
+ mwifiex_decode_rx_packet(adapter, skb, pkt_type);
+ }
+
if (sdio_ireg & DN_LD_HOST_INT_STATUS) {
- card->mp_wr_bitmap = ((u16) card->mp_regs[WR_BITMAP_U]) << 8;
- card->mp_wr_bitmap |= (u16) card->mp_regs[WR_BITMAP_L];
- dev_dbg(adapter->dev, "int: DNLD: wr_bitmap=0x%04x\n",
+ bitmap = (u32) card->mp_regs[reg->wr_bitmap_l];
+ bitmap |= ((u32) card->mp_regs[reg->wr_bitmap_u]) << 8;
+ if (card->supports_sdio_new_mode) {
+ bitmap |=
+ ((u32) card->mp_regs[reg->wr_bitmap_1l]) << 16;
+ bitmap |=
+ ((u32) card->mp_regs[reg->wr_bitmap_1u]) << 24;
+ }
+ card->mp_wr_bitmap = bitmap;
+
+ dev_dbg(adapter->dev, "int: DNLD: wr_bitmap=0x%x\n",
card->mp_wr_bitmap);
if (adapter->data_sent &&
(card->mp_wr_bitmap & card->mp_data_port_mask)) {
@@ -1200,11 +1361,11 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
/* As firmware will not generate download ready interrupt if the port
updated is command port only, cmd_sent should be done for any SDIO
interrupt. */
- if (adapter->cmd_sent) {
+ if (card->has_control_mask && adapter->cmd_sent) {
/* Check if firmware has attach buffer at command port and
update just that in wr_bit_map. */
card->mp_wr_bitmap |=
- (u16) card->mp_regs[WR_BITMAP_L] & CTRL_PORT_MASK;
+ (u32) card->mp_regs[reg->wr_bitmap_l] & CTRL_PORT_MASK;
if (card->mp_wr_bitmap & CTRL_PORT_MASK)
adapter->cmd_sent = false;
}
@@ -1212,9 +1373,16 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
dev_dbg(adapter->dev, "info: cmd_sent=%d data_sent=%d\n",
adapter->cmd_sent, adapter->data_sent);
if (sdio_ireg & UP_LD_HOST_INT_STATUS) {
- card->mp_rd_bitmap = ((u16) card->mp_regs[RD_BITMAP_U]) << 8;
- card->mp_rd_bitmap |= (u16) card->mp_regs[RD_BITMAP_L];
- dev_dbg(adapter->dev, "int: UPLD: rd_bitmap=0x%04x\n",
+ bitmap = (u32) card->mp_regs[reg->rd_bitmap_l];
+ bitmap |= ((u32) card->mp_regs[reg->rd_bitmap_u]) << 8;
+ if (card->supports_sdio_new_mode) {
+ bitmap |=
+ ((u32) card->mp_regs[reg->rd_bitmap_1l]) << 16;
+ bitmap |=
+ ((u32) card->mp_regs[reg->rd_bitmap_1u]) << 24;
+ }
+ card->mp_rd_bitmap = bitmap;
+ dev_dbg(adapter->dev, "int: UPLD: rd_bitmap=0x%x\n",
card->mp_rd_bitmap);
while (true) {
@@ -1224,8 +1392,8 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
"info: no more rd_port available\n");
break;
}
- len_reg_l = RD_LEN_P0_L + (port << 1);
- len_reg_u = RD_LEN_P0_U + (port << 1);
+ len_reg_l = reg->rd_len_p0_l + (port << 1);
+ len_reg_u = reg->rd_len_p0_u + (port << 1);
rx_len = ((u16) card->mp_regs[len_reg_u]) << 8;
rx_len |= (u16) card->mp_regs[len_reg_l];
dev_dbg(adapter->dev, "info: RX: port=%d rx_len=%u\n",
@@ -1257,37 +1425,33 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
if (mwifiex_sdio_card_to_host_mp_aggr(adapter, skb,
port)) {
- u32 cr = 0;
-
dev_err(adapter->dev, "card_to_host_mpa failed:"
" int status=%#x\n", sdio_ireg);
- if (mwifiex_read_reg(adapter,
- CONFIGURATION_REG, &cr))
- dev_err(adapter->dev,
- "read CFG reg failed\n");
-
- dev_dbg(adapter->dev,
- "info: CFG reg val = %d\n", cr);
- if (mwifiex_write_reg(adapter,
- CONFIGURATION_REG,
- (cr | 0x04)))
- dev_err(adapter->dev,
- "write CFG reg failed\n");
-
- dev_dbg(adapter->dev, "info: write success\n");
- if (mwifiex_read_reg(adapter,
- CONFIGURATION_REG, &cr))
- dev_err(adapter->dev,
- "read CFG reg failed\n");
-
- dev_dbg(adapter->dev,
- "info: CFG reg val =%x\n", cr);
- return -1;
+ goto term_cmd;
}
}
}
return 0;
+
+term_cmd:
+ /* terminate cmd */
+ if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr))
+ dev_err(adapter->dev, "read CFG reg failed\n");
+ else
+ dev_dbg(adapter->dev, "info: CFG reg val = %d\n", cr);
+
+ if (mwifiex_write_reg(adapter, CONFIGURATION_REG, (cr | 0x04)))
+ dev_err(adapter->dev, "write CFG reg failed\n");
+ else
+ dev_dbg(adapter->dev, "info: write success\n");
+
+ if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr))
+ dev_err(adapter->dev, "read CFG reg failed\n");
+ else
+ dev_dbg(adapter->dev, "info: CFG reg val =%x\n", cr);
+
+ return -1;
}
/*
@@ -1305,7 +1469,7 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
* and return.
*/
static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
- u8 *payload, u32 pkt_len, u8 port,
+ u8 *payload, u32 pkt_len, u32 port,
u32 next_pkt_len)
{
struct sdio_mmc_card *card = adapter->card;
@@ -1314,8 +1478,11 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
s32 f_send_cur_buf = 0;
s32 f_precopy_cur_buf = 0;
s32 f_postcopy_cur_buf = 0;
+ u32 mport;
- if ((!card->mpa_tx.enabled) || (port == CTRL_PORT)) {
+ if (!card->mpa_tx.enabled ||
+ (card->has_control_mask && (port == CTRL_PORT)) ||
+ (card->supports_sdio_new_mode && (port == CMD_PORT_SLCT))) {
dev_dbg(adapter->dev, "info: %s: tx aggregation disabled\n",
__func__);
@@ -1329,7 +1496,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
__func__);
if (MP_TX_AGGR_IN_PROGRESS(card)) {
- if (!MP_TX_AGGR_PORT_LIMIT_REACHED(card) &&
+ if (!mp_tx_aggr_port_limit_reached(card) &&
MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) {
f_precopy_cur_buf = 1;
@@ -1342,7 +1509,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
/* No room in Aggr buf, send it */
f_send_aggr_buf = 1;
- if (MP_TX_AGGR_PORT_LIMIT_REACHED(card) ||
+ if (mp_tx_aggr_port_limit_reached(card) ||
!(card->mp_wr_bitmap &
(1 << card->curr_wr_port)))
f_send_cur_buf = 1;
@@ -1381,7 +1548,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port);
if (MP_TX_AGGR_PKT_LIMIT_REACHED(card) ||
- MP_TX_AGGR_PORT_LIMIT_REACHED(card))
+ mp_tx_aggr_port_limit_reached(card))
/* No more pkts allowed in Aggr buf, send it */
f_send_aggr_buf = 1;
}
@@ -1390,11 +1557,28 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
dev_dbg(adapter->dev, "data: %s: send aggr buffer: %d %d\n",
__func__,
card->mpa_tx.start_port, card->mpa_tx.ports);
+ if (card->supports_sdio_new_mode) {
+ u32 port_count;
+ int i;
+
+ for (i = 0, port_count = 0; i < card->max_ports; i++)
+ if (card->mpa_tx.ports & BIT(i))
+ port_count++;
+
+ /* Writing data from "start_port + 0" to "start_port +
+ * port_count -1", so decrease the count by 1
+ */
+ port_count--;
+ mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+ (port_count << 8)) + card->mpa_tx.start_port;
+ } else {
+ mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+ (card->mpa_tx.ports << 4)) +
+ card->mpa_tx.start_port;
+ }
+
ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf,
- card->mpa_tx.buf_len,
- (adapter->ioport | 0x1000 |
- (card->mpa_tx.ports << 4)) +
- card->mpa_tx.start_port);
+ card->mpa_tx.buf_len, mport);
MP_TX_AGGR_BUF_RESET(card);
}
@@ -1434,7 +1618,7 @@ static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter,
int ret;
u32 buf_block_len;
u32 blk_size;
- u8 port = CTRL_PORT;
+ u32 port = CTRL_PORT;
u8 *payload = (u8 *)skb->data;
u32 pkt_len = skb->len;
@@ -1465,6 +1649,9 @@ static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter,
pkt_len > MWIFIEX_UPLD_SIZE)
dev_err(adapter->dev, "%s: payload=%p, nb=%d\n",
__func__, payload, pkt_len);
+
+ if (card->supports_sdio_new_mode)
+ port = CMD_PORT_SLCT;
}
/* Transfer data to card */
@@ -1586,18 +1773,7 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
adapter->dev = &func->dev;
- switch (func->device) {
- case SDIO_DEVICE_ID_MARVELL_8786:
- strcpy(adapter->fw_name, SD8786_DEFAULT_FW_NAME);
- break;
- case SDIO_DEVICE_ID_MARVELL_8797:
- strcpy(adapter->fw_name, SD8797_DEFAULT_FW_NAME);
- break;
- case SDIO_DEVICE_ID_MARVELL_8787:
- default:
- strcpy(adapter->fw_name, SD8787_DEFAULT_FW_NAME);
- break;
- }
+ strcpy(adapter->fw_name, card->firmware);
return 0;
@@ -1626,8 +1802,9 @@ disable_func:
static int mwifiex_init_sdio(struct mwifiex_adapter *adapter)
{
struct sdio_mmc_card *card = adapter->card;
+ const struct mwifiex_sdio_card_reg *reg = card->reg;
int ret;
- u32 sdio_ireg;
+ u8 sdio_ireg;
/*
* Read the HOST_INT_STATUS_REG for ACK the first interrupt got
@@ -1645,30 +1822,35 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter)
/* Initialize SDIO variables in card */
card->mp_rd_bitmap = 0;
card->mp_wr_bitmap = 0;
- card->curr_rd_port = 1;
- card->curr_wr_port = 1;
+ card->curr_rd_port = reg->start_rd_port;
+ card->curr_wr_port = reg->start_wr_port;
- card->mp_data_port_mask = DATA_PORT_MASK;
+ card->mp_data_port_mask = reg->data_port_mask;
card->mpa_tx.buf_len = 0;
card->mpa_tx.pkt_cnt = 0;
card->mpa_tx.start_port = 0;
card->mpa_tx.enabled = 1;
- card->mpa_tx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT;
+ card->mpa_tx.pkt_aggr_limit = card->mp_agg_pkt_limit;
card->mpa_rx.buf_len = 0;
card->mpa_rx.pkt_cnt = 0;
card->mpa_rx.start_port = 0;
card->mpa_rx.enabled = 1;
- card->mpa_rx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT;
+ card->mpa_rx.pkt_aggr_limit = card->mp_agg_pkt_limit;
/* Allocate buffers for SDIO MP-A */
- card->mp_regs = kzalloc(MAX_MP_REGS, GFP_KERNEL);
+ card->mp_regs = kzalloc(reg->max_mp_regs, GFP_KERNEL);
if (!card->mp_regs)
return -ENOMEM;
+ /* Allocate skb pointer buffers */
+ card->mpa_rx.skb_arr = kzalloc((sizeof(void *)) *
+ card->mp_agg_pkt_limit, GFP_KERNEL);
+ card->mpa_rx.len_arr = kzalloc(sizeof(*card->mpa_rx.len_arr) *
+ card->mp_agg_pkt_limit, GFP_KERNEL);
ret = mwifiex_alloc_sdio_mpa_buffers(adapter,
SDIO_MP_TX_AGGR_DEF_BUF_SIZE,
SDIO_MP_RX_AGGR_DEF_BUF_SIZE);
@@ -1705,6 +1887,8 @@ static void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter)
struct sdio_mmc_card *card = adapter->card;
kfree(card->mp_regs);
+ kfree(card->mpa_rx.skb_arr);
+ kfree(card->mpa_rx.len_arr);
kfree(card->mpa_tx.buf);
kfree(card->mpa_rx.buf);
}
@@ -1716,16 +1900,20 @@ static void
mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port)
{
struct sdio_mmc_card *card = adapter->card;
+ const struct mwifiex_sdio_card_reg *reg = card->reg;
int i;
card->mp_end_port = port;
- card->mp_data_port_mask = DATA_PORT_MASK;
+ card->mp_data_port_mask = reg->data_port_mask;
- for (i = 1; i <= MAX_PORT - card->mp_end_port; i++)
- card->mp_data_port_mask &= ~(1 << (MAX_PORT - i));
+ if (reg->start_wr_port) {
+ for (i = 1; i <= card->max_ports - card->mp_end_port; i++)
+ card->mp_data_port_mask &=
+ ~(1 << (card->max_ports - i));
+ }
- card->curr_wr_port = 1;
+ card->curr_wr_port = reg->start_wr_port;
dev_dbg(adapter->dev, "cmd: mp_end_port %d, data port mask 0x%x\n",
port, card->mp_data_port_mask);
@@ -1831,3 +2019,4 @@ MODULE_LICENSE("GPL v2");
MODULE_FIRMWARE(SD8786_DEFAULT_FW_NAME);
MODULE_FIRMWARE(SD8787_DEFAULT_FW_NAME);
MODULE_FIRMWARE(SD8797_DEFAULT_FW_NAME);
+MODULE_FIRMWARE(SD8897_DEFAULT_FW_NAME);
diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h
index 8cc5468654b..6d51dfdd825 100644
--- a/drivers/net/wireless/mwifiex/sdio.h
+++ b/drivers/net/wireless/mwifiex/sdio.h
@@ -32,30 +32,37 @@
#define SD8786_DEFAULT_FW_NAME "mrvl/sd8786_uapsta.bin"
#define SD8787_DEFAULT_FW_NAME "mrvl/sd8787_uapsta.bin"
#define SD8797_DEFAULT_FW_NAME "mrvl/sd8797_uapsta.bin"
+#define SD8897_DEFAULT_FW_NAME "mrvl/sd8897_uapsta.bin"
#define BLOCK_MODE 1
#define BYTE_MODE 0
#define REG_PORT 0
-#define RD_BITMAP_L 0x04
-#define RD_BITMAP_U 0x05
-#define WR_BITMAP_L 0x06
-#define WR_BITMAP_U 0x07
-#define RD_LEN_P0_L 0x08
-#define RD_LEN_P0_U 0x09
#define MWIFIEX_SDIO_IO_PORT_MASK 0xfffff
#define MWIFIEX_SDIO_BYTE_MODE_MASK 0x80000000
+#define SDIO_MPA_ADDR_BASE 0x1000
#define CTRL_PORT 0
#define CTRL_PORT_MASK 0x0001
-#define DATA_PORT_MASK 0xfffe
-#define MAX_MP_REGS 64
-#define MAX_PORT 16
-
-#define SDIO_MP_AGGR_DEF_PKT_LIMIT 8
+#define CMD_PORT_UPLD_INT_MASK (0x1U<<6)
+#define CMD_PORT_DNLD_INT_MASK (0x1U<<7)
+#define HOST_TERM_CMD53 (0x1U << 2)
+#define REG_PORT 0
+#define MEM_PORT 0x10000
+#define CMD_RD_LEN_0 0xB4
+#define CMD_RD_LEN_1 0xB5
+#define CARD_CONFIG_2_1_REG 0xCD
+#define CMD53_NEW_MODE (0x1U << 0)
+#define CMD_CONFIG_0 0xB8
+#define CMD_PORT_RD_LEN_EN (0x1U << 2)
+#define CMD_CONFIG_1 0xB9
+#define CMD_PORT_AUTO_EN (0x1U << 0)
+#define CMD_PORT_SLCT 0x8000
+#define UP_LD_CMD_PORT_HOST_INT_STATUS (0x40U)
+#define DN_LD_CMD_PORT_HOST_INT_STATUS (0x80U)
#define SDIO_MP_TX_AGGR_DEF_BUF_SIZE (8192) /* 8K */
@@ -75,14 +82,8 @@
/* Host Control Registers : Configuration */
#define CONFIGURATION_REG 0x00
-/* Host Control Registers : Host without Command 53 finish host*/
-#define HOST_TO_CARD_EVENT (0x1U << 3)
-/* Host Control Registers : Host without Command 53 finish host */
-#define HOST_WO_CMD53_FINISH_HOST (0x1U << 2)
/* Host Control Registers : Host power up */
#define HOST_POWER_UP (0x1U << 1)
-/* Host Control Registers : Host power down */
-#define HOST_POWER_DOWN (0x1U << 0)
/* Host Control Registers : Host interrupt mask */
#define HOST_INT_MASK_REG 0x02
@@ -90,8 +91,7 @@
#define UP_LD_HOST_INT_MASK (0x1U)
/* Host Control Registers : Download host interrupt mask */
#define DN_LD_HOST_INT_MASK (0x2U)
-/* Enable Host interrupt mask */
-#define HOST_INT_ENABLE (UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK)
+
/* Disable Host interrupt mask */
#define HOST_INT_DISABLE 0xff
@@ -104,74 +104,15 @@
/* Host Control Registers : Host interrupt RSR */
#define HOST_INT_RSR_REG 0x01
-/* Host Control Registers : Upload host interrupt RSR */
-#define UP_LD_HOST_INT_RSR (0x1U)
-#define SDIO_INT_MASK 0x3F
/* Host Control Registers : Host interrupt status */
#define HOST_INT_STATUS_REG 0x28
-/* Host Control Registers : Upload CRC error */
-#define UP_LD_CRC_ERR (0x1U << 2)
-/* Host Control Registers : Upload restart */
-#define UP_LD_RESTART (0x1U << 1)
-/* Host Control Registers : Download restart */
-#define DN_LD_RESTART (0x1U << 0)
-
-/* Card Control Registers : Card status register */
-#define CARD_STATUS_REG 0x30
+
/* Card Control Registers : Card I/O ready */
#define CARD_IO_READY (0x1U << 3)
-/* Card Control Registers : CIS card ready */
-#define CIS_CARD_RDY (0x1U << 2)
-/* Card Control Registers : Upload card ready */
-#define UP_LD_CARD_RDY (0x1U << 1)
/* Card Control Registers : Download card ready */
#define DN_LD_CARD_RDY (0x1U << 0)
-/* Card Control Registers : Host interrupt mask register */
-#define HOST_INTERRUPT_MASK_REG 0x34
-/* Card Control Registers : Host power interrupt mask */
-#define HOST_POWER_INT_MASK (0x1U << 3)
-/* Card Control Registers : Abort card interrupt mask */
-#define ABORT_CARD_INT_MASK (0x1U << 2)
-/* Card Control Registers : Upload card interrupt mask */
-#define UP_LD_CARD_INT_MASK (0x1U << 1)
-/* Card Control Registers : Download card interrupt mask */
-#define DN_LD_CARD_INT_MASK (0x1U << 0)
-
-/* Card Control Registers : Card interrupt status register */
-#define CARD_INTERRUPT_STATUS_REG 0x38
-/* Card Control Registers : Power up interrupt */
-#define POWER_UP_INT (0x1U << 4)
-/* Card Control Registers : Power down interrupt */
-#define POWER_DOWN_INT (0x1U << 3)
-
-/* Card Control Registers : Card interrupt RSR register */
-#define CARD_INTERRUPT_RSR_REG 0x3c
-/* Card Control Registers : Power up RSR */
-#define POWER_UP_RSR (0x1U << 4)
-/* Card Control Registers : Power down RSR */
-#define POWER_DOWN_RSR (0x1U << 3)
-
-/* Card Control Registers : Miscellaneous Configuration Register */
-#define CARD_MISC_CFG_REG 0x6C
-
-/* Host F1 read base 0 */
-#define HOST_F1_RD_BASE_0 0x0040
-/* Host F1 read base 1 */
-#define HOST_F1_RD_BASE_1 0x0041
-/* Host F1 card ready */
-#define HOST_F1_CARD_RDY 0x0020
-
-/* Firmware status 0 register */
-#define CARD_FW_STATUS0_REG 0x60
-/* Firmware status 1 register */
-#define CARD_FW_STATUS1_REG 0x61
-/* Rx length register */
-#define CARD_RX_LEN_REG 0x62
-/* Rx unit register */
-#define CARD_RX_UNIT_REG 0x63
-
/* Max retry number of CMD53 write */
#define MAX_WRITE_IOMEM_RETRY 2
@@ -192,7 +133,8 @@
if (a->mpa_tx.start_port <= port) \
a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt)); \
else \
- a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+(MAX_PORT - \
+ a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+ \
+ (a->max_ports - \
a->mp_end_port))); \
a->mpa_tx.pkt_cnt++; \
} while (0)
@@ -201,12 +143,6 @@
#define MP_TX_AGGR_PKT_LIMIT_REACHED(a) \
(a->mpa_tx.pkt_cnt == a->mpa_tx.pkt_aggr_limit)
-/* SDIO Tx aggregation port limit ? */
-#define MP_TX_AGGR_PORT_LIMIT_REACHED(a) ((a->curr_wr_port < \
- a->mpa_tx.start_port) && (((MAX_PORT - \
- a->mpa_tx.start_port) + a->curr_wr_port) >= \
- SDIO_MP_AGGR_DEF_PKT_LIMIT))
-
/* Reset SDIO Tx aggregation buffer parameters */
#define MP_TX_AGGR_BUF_RESET(a) do { \
a->mpa_tx.pkt_cnt = 0; \
@@ -219,12 +155,6 @@
#define MP_RX_AGGR_PKT_LIMIT_REACHED(a) \
(a->mpa_rx.pkt_cnt == a->mpa_rx.pkt_aggr_limit)
-/* SDIO Tx aggregation port limit ? */
-#define MP_RX_AGGR_PORT_LIMIT_REACHED(a) ((a->curr_rd_port < \
- a->mpa_rx.start_port) && (((MAX_PORT - \
- a->mpa_rx.start_port) + a->curr_rd_port) >= \
- SDIO_MP_AGGR_DEF_PKT_LIMIT))
-
/* SDIO Rx aggregation in progress ? */
#define MP_RX_AGGR_IN_PROGRESS(a) (a->mpa_rx.pkt_cnt > 0)
@@ -232,20 +162,6 @@
#define MP_RX_AGGR_BUF_HAS_ROOM(a, rx_len) \
((a->mpa_rx.buf_len+rx_len) <= a->mpa_rx.buf_size)
-/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */
-#define MP_RX_AGGR_SETUP(a, skb, port) do { \
- a->mpa_rx.buf_len += skb->len; \
- if (!a->mpa_rx.pkt_cnt) \
- a->mpa_rx.start_port = port; \
- if (a->mpa_rx.start_port <= port) \
- a->mpa_rx.ports |= (1<<(a->mpa_rx.pkt_cnt)); \
- else \
- a->mpa_rx.ports |= (1<<(a->mpa_rx.pkt_cnt+1)); \
- a->mpa_rx.skb_arr[a->mpa_rx.pkt_cnt] = skb; \
- a->mpa_rx.len_arr[a->mpa_rx.pkt_cnt] = skb->len; \
- a->mpa_rx.pkt_cnt++; \
-} while (0)
-
/* Reset SDIO Rx aggregation buffer parameters */
#define MP_RX_AGGR_BUF_RESET(a) do { \
a->mpa_rx.pkt_cnt = 0; \
@@ -254,14 +170,13 @@
a->mpa_rx.start_port = 0; \
} while (0)
-
/* data structure for SDIO MPA TX */
struct mwifiex_sdio_mpa_tx {
/* multiport tx aggregation buffer pointer */
u8 *buf;
u32 buf_len;
u32 pkt_cnt;
- u16 ports;
+ u32 ports;
u16 start_port;
u8 enabled;
u32 buf_size;
@@ -272,11 +187,11 @@ struct mwifiex_sdio_mpa_rx {
u8 *buf;
u32 buf_len;
u32 pkt_cnt;
- u16 ports;
+ u32 ports;
u16 start_port;
- struct sk_buff *skb_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT];
- u32 len_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT];
+ struct sk_buff **skb_arr;
+ u32 *len_arr;
u8 enabled;
u32 buf_size;
@@ -286,15 +201,47 @@ struct mwifiex_sdio_mpa_rx {
int mwifiex_bus_register(void);
void mwifiex_bus_unregister(void);
+struct mwifiex_sdio_card_reg {
+ u8 start_rd_port;
+ u8 start_wr_port;
+ u8 base_0_reg;
+ u8 base_1_reg;
+ u8 poll_reg;
+ u8 host_int_enable;
+ u8 status_reg_0;
+ u8 status_reg_1;
+ u8 sdio_int_mask;
+ u32 data_port_mask;
+ u8 max_mp_regs;
+ u8 rd_bitmap_l;
+ u8 rd_bitmap_u;
+ u8 rd_bitmap_1l;
+ u8 rd_bitmap_1u;
+ u8 wr_bitmap_l;
+ u8 wr_bitmap_u;
+ u8 wr_bitmap_1l;
+ u8 wr_bitmap_1u;
+ u8 rd_len_p0_l;
+ u8 rd_len_p0_u;
+ u8 card_misc_cfg_reg;
+};
+
struct sdio_mmc_card {
struct sdio_func *func;
struct mwifiex_adapter *adapter;
- u16 mp_rd_bitmap;
- u16 mp_wr_bitmap;
+ const char *firmware;
+ const struct mwifiex_sdio_card_reg *reg;
+ u8 max_ports;
+ u8 mp_agg_pkt_limit;
+ bool supports_sdio_new_mode;
+ bool has_control_mask;
+
+ u32 mp_rd_bitmap;
+ u32 mp_wr_bitmap;
u16 mp_end_port;
- u16 mp_data_port_mask;
+ u32 mp_data_port_mask;
u8 curr_rd_port;
u8 curr_wr_port;
@@ -305,6 +252,98 @@ struct sdio_mmc_card {
struct mwifiex_sdio_mpa_rx mpa_rx;
};
+struct mwifiex_sdio_device {
+ const char *firmware;
+ const struct mwifiex_sdio_card_reg *reg;
+ u8 max_ports;
+ u8 mp_agg_pkt_limit;
+ bool supports_sdio_new_mode;
+ bool has_control_mask;
+};
+
+static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = {
+ .start_rd_port = 1,
+ .start_wr_port = 1,
+ .base_0_reg = 0x0040,
+ .base_1_reg = 0x0041,
+ .poll_reg = 0x30,
+ .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK,
+ .status_reg_0 = 0x60,
+ .status_reg_1 = 0x61,
+ .sdio_int_mask = 0x3f,
+ .data_port_mask = 0x0000fffe,
+ .max_mp_regs = 64,
+ .rd_bitmap_l = 0x04,
+ .rd_bitmap_u = 0x05,
+ .wr_bitmap_l = 0x06,
+ .wr_bitmap_u = 0x07,
+ .rd_len_p0_l = 0x08,
+ .rd_len_p0_u = 0x09,
+ .card_misc_cfg_reg = 0x6c,
+};
+
+static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = {
+ .start_rd_port = 0,
+ .start_wr_port = 0,
+ .base_0_reg = 0x60,
+ .base_1_reg = 0x61,
+ .poll_reg = 0x50,
+ .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK |
+ CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK,
+ .status_reg_0 = 0xc0,
+ .status_reg_1 = 0xc1,
+ .sdio_int_mask = 0xff,
+ .data_port_mask = 0xffffffff,
+ .max_mp_regs = 184,
+ .rd_bitmap_l = 0x04,
+ .rd_bitmap_u = 0x05,
+ .rd_bitmap_1l = 0x06,
+ .rd_bitmap_1u = 0x07,
+ .wr_bitmap_l = 0x08,
+ .wr_bitmap_u = 0x09,
+ .wr_bitmap_1l = 0x0a,
+ .wr_bitmap_1u = 0x0b,
+ .rd_len_p0_l = 0x0c,
+ .rd_len_p0_u = 0x0d,
+ .card_misc_cfg_reg = 0xcc,
+};
+
+static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = {
+ .firmware = SD8786_DEFAULT_FW_NAME,
+ .reg = &mwifiex_reg_sd87xx,
+ .max_ports = 16,
+ .mp_agg_pkt_limit = 8,
+ .supports_sdio_new_mode = false,
+ .has_control_mask = true,
+};
+
+static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = {
+ .firmware = SD8787_DEFAULT_FW_NAME,
+ .reg = &mwifiex_reg_sd87xx,
+ .max_ports = 16,
+ .mp_agg_pkt_limit = 8,
+ .supports_sdio_new_mode = false,
+ .has_control_mask = true,
+};
+
+static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = {
+ .firmware = SD8797_DEFAULT_FW_NAME,
+ .reg = &mwifiex_reg_sd87xx,
+ .max_ports = 16,
+ .mp_agg_pkt_limit = 8,
+ .supports_sdio_new_mode = false,
+ .has_control_mask = true,
+};
+
+static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
+ .firmware = SD8897_DEFAULT_FW_NAME,
+ .reg = &mwifiex_reg_sd8897,
+ .max_ports = 32,
+ .mp_agg_pkt_limit = 16,
+ .supports_sdio_new_mode = true,
+ .has_control_mask = false,
+};
+
/*
* .cmdrsp_complete handler
*/
@@ -325,4 +364,77 @@ static inline int mwifiex_sdio_event_complete(struct mwifiex_adapter *adapter,
return 0;
}
+static inline bool
+mp_rx_aggr_port_limit_reached(struct sdio_mmc_card *card)
+{
+ u8 tmp;
+
+ if (card->curr_rd_port < card->mpa_rx.start_port) {
+ if (card->supports_sdio_new_mode)
+ tmp = card->mp_end_port >> 1;
+ else
+ tmp = card->mp_agg_pkt_limit;
+
+ if (((card->max_ports - card->mpa_rx.start_port) +
+ card->curr_rd_port) >= tmp)
+ return true;
+ }
+
+ if (!card->supports_sdio_new_mode)
+ return false;
+
+ if ((card->curr_rd_port - card->mpa_rx.start_port) >=
+ (card->mp_end_port >> 1))
+ return true;
+
+ return false;
+}
+
+static inline bool
+mp_tx_aggr_port_limit_reached(struct sdio_mmc_card *card)
+{
+ u16 tmp;
+
+ if (card->curr_wr_port < card->mpa_tx.start_port) {
+ if (card->supports_sdio_new_mode)
+ tmp = card->mp_end_port >> 1;
+ else
+ tmp = card->mp_agg_pkt_limit;
+
+ if (((card->max_ports - card->mpa_tx.start_port) +
+ card->curr_wr_port) >= tmp)
+ return true;
+ }
+
+ if (!card->supports_sdio_new_mode)
+ return false;
+
+ if ((card->curr_wr_port - card->mpa_tx.start_port) >=
+ (card->mp_end_port >> 1))
+ return true;
+
+ return false;
+}
+
+/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */
+static inline void mp_rx_aggr_setup(struct sdio_mmc_card *card,
+ struct sk_buff *skb, u8 port)
+{
+ card->mpa_rx.buf_len += skb->len;
+
+ if (!card->mpa_rx.pkt_cnt)
+ card->mpa_rx.start_port = port;
+
+ if (card->supports_sdio_new_mode) {
+ card->mpa_rx.ports |= (1 << port);
+ } else {
+ if (card->mpa_rx.start_port <= port)
+ card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt);
+ else
+ card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt + 1);
+ }
+ card->mpa_rx.skb_arr[card->mpa_rx.pkt_cnt] = skb;
+ card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] = skb->len;
+ card->mpa_rx.pkt_cnt++;
+}
#endif /* _MWIFIEX_SDIO_H */
diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c
index b193e25977d..8ece4858064 100644
--- a/drivers/net/wireless/mwifiex/sta_cmd.c
+++ b/drivers/net/wireless/mwifiex/sta_cmd.c
@@ -1134,6 +1134,55 @@ mwifiex_cmd_mef_cfg(struct mwifiex_private *priv,
return 0;
}
+/* This function parse cal data from ASCII to hex */
+static u32 mwifiex_parse_cal_cfg(u8 *src, size_t len, u8 *dst)
+{
+ u8 *s = src, *d = dst;
+
+ while (s - src < len) {
+ if (*s && (isspace(*s) || *s == '\t')) {
+ s++;
+ continue;
+ }
+ if (isxdigit(*s)) {
+ *d++ = simple_strtol(s, NULL, 16);
+ s += 2;
+ } else {
+ s++;
+ }
+ }
+
+ return d - dst;
+}
+
+/* This function prepares command of set_cfg_data. */
+static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_action)
+{
+ struct host_cmd_ds_802_11_cfg_data *cfg_data = &cmd->params.cfg_data;
+ struct mwifiex_adapter *adapter = priv->adapter;
+ u32 len, cal_data_offset;
+ u8 *tmp_cmd = (u8 *)cmd;
+
+ cal_data_offset = S_DS_GEN + sizeof(*cfg_data);
+ if ((adapter->cal_data->data) && (adapter->cal_data->size > 0))
+ len = mwifiex_parse_cal_cfg((u8 *)adapter->cal_data->data,
+ adapter->cal_data->size,
+ (u8 *)(tmp_cmd + cal_data_offset));
+ else
+ return -1;
+
+ cfg_data->action = cpu_to_le16(cmd_action);
+ cfg_data->type = cpu_to_le16(CFG_DATA_TYPE_CAL);
+ cfg_data->data_len = cpu_to_le16(len);
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_CFG_DATA);
+ cmd->size = cpu_to_le16(S_DS_GEN + sizeof(*cfg_data) + len);
+
+ return 0;
+}
+
/*
* This function prepares the commands before sending them to the firmware.
*
@@ -1152,6 +1201,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
case HostCmd_CMD_GET_HW_SPEC:
ret = mwifiex_cmd_get_hw_spec(priv, cmd_ptr);
break;
+ case HostCmd_CMD_CFG_DATA:
+ ret = mwifiex_cmd_cfg_data(priv, cmd_ptr, cmd_action);
+ break;
case HostCmd_CMD_MAC_CONTROL:
ret = mwifiex_cmd_mac_control(priv, cmd_ptr, cmd_action,
data_buf);
@@ -1384,6 +1436,7 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
*/
int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta)
{
+ struct mwifiex_adapter *adapter = priv->adapter;
int ret;
u16 enable = true;
struct mwifiex_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl;
@@ -1404,6 +1457,15 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta)
HostCmd_ACT_GEN_SET, 0, NULL);
if (ret)
return -1;
+
+ /* Download calibration data to firmware */
+ if (adapter->cal_data) {
+ ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_CFG_DATA,
+ HostCmd_ACT_GEN_SET, 0, NULL);
+ if (ret)
+ return -1;
+ }
+
/* Read MAC address from HW */
ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_GET_HW_SPEC,
HostCmd_ACT_GEN_GET, 0, NULL);
diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c
index 9f990e14966..d85df158cc6 100644
--- a/drivers/net/wireless/mwifiex/sta_cmdresp.c
+++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c
@@ -818,6 +818,18 @@ static int mwifiex_ret_subsc_evt(struct mwifiex_private *priv,
return 0;
}
+/* This function handles the command response of set_cfg_data */
+static int mwifiex_ret_cfg_data(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp)
+{
+ if (resp->result != HostCmd_RESULT_OK) {
+ dev_err(priv->adapter->dev, "Cal data cmd resp failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
/*
* This function handles the command responses.
*
@@ -841,6 +853,9 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
case HostCmd_CMD_GET_HW_SPEC:
ret = mwifiex_ret_get_hw_spec(priv, resp);
break;
+ case HostCmd_CMD_CFG_DATA:
+ ret = mwifiex_ret_cfg_data(priv, resp);
+ break;
case HostCmd_CMD_MAC_CONTROL:
break;
case HostCmd_CMD_802_11_MAC_ADDRESS:
@@ -978,6 +993,8 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
case HostCmd_CMD_UAP_BSS_STOP:
priv->bss_started = 0;
break;
+ case HostCmd_CMD_UAP_STA_DEAUTH:
+ break;
case HostCmd_CMD_MEF_CFG:
break;
default:
diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c
index b04b1db2910..2de882dead0 100644
--- a/drivers/net/wireless/mwifiex/uap_cmd.c
+++ b/drivers/net/wireless/mwifiex/uap_cmd.c
@@ -689,6 +689,23 @@ mwifiex_cmd_uap_sys_config(struct host_cmd_ds_command *cmd, u16 cmd_action,
return 0;
}
+/* This function prepares AP specific deauth command with mac supplied in
+ * function parameter.
+ */
+static int mwifiex_cmd_uap_sta_deauth(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd, u8 *mac)
+{
+ struct host_cmd_ds_sta_deauth *sta_deauth = &cmd->params.sta_deauth;
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_UAP_STA_DEAUTH);
+ memcpy(sta_deauth->mac, mac, ETH_ALEN);
+ sta_deauth->reason = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING);
+
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_sta_deauth) +
+ S_DS_GEN);
+ return 0;
+}
+
/* This function prepares the AP specific commands before sending them
* to the firmware.
* This is a generic function which calls specific command preparation
@@ -710,6 +727,10 @@ int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, u16 cmd_no,
cmd->command = cpu_to_le16(cmd_no);
cmd->size = cpu_to_le16(S_DS_GEN);
break;
+ case HostCmd_CMD_UAP_STA_DEAUTH:
+ if (mwifiex_cmd_uap_sta_deauth(priv, cmd, data_buf))
+ return -1;
+ break;
default:
dev_err(priv->adapter->dev,
"PREP_CMD: unknown cmd %#x\n", cmd_no);
diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c
index 21c640d3b57..718066577c6 100644
--- a/drivers/net/wireless/mwifiex/uap_event.c
+++ b/drivers/net/wireless/mwifiex/uap_event.c
@@ -107,18 +107,15 @@ mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies,
*/
static void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac)
{
- struct mwifiex_sta_node *node, *tmp;
+ struct mwifiex_sta_node *node;
unsigned long flags;
spin_lock_irqsave(&priv->sta_list_spinlock, flags);
node = mwifiex_get_sta_entry(priv, mac);
if (node) {
- list_for_each_entry_safe(node, tmp, &priv->sta_list,
- list) {
- list_del(&node->list);
- kfree(node);
- }
+ list_del(&node->list);
+ kfree(node);
}
spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
@@ -295,3 +292,19 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
return 0;
}
+
+/* This function deletes station entry from associated station list.
+ * Also if both AP and STA are 11n enabled, RxReorder tables and TxBA stream
+ * tables created for this station are deleted.
+ */
+void mwifiex_uap_del_sta_data(struct mwifiex_private *priv,
+ struct mwifiex_sta_node *node)
+{
+ if (priv->ap_11n_enabled && node->is_11n_enabled) {
+ mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, node->mac_addr);
+ mwifiex_del_tx_ba_stream_tbl_by_ra(priv, node->mac_addr);
+ }
+ mwifiex_del_sta_entry(priv, node->mac_addr);
+
+ return;
+}
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index 6820fce4016..a3707fd4ef6 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -1548,7 +1548,7 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
if (!priv->pending_tx_pkts)
return 0;
- retry = 0;
+ retry = 1;
rc = 0;
spin_lock_bh(&priv->tx_lock);
@@ -1572,13 +1572,19 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
spin_lock_bh(&priv->tx_lock);
- if (timeout) {
+ if (timeout || !priv->pending_tx_pkts) {
WARN_ON(priv->pending_tx_pkts);
if (retry)
wiphy_notice(hw->wiphy, "tx rings drained\n");
break;
}
+ if (retry) {
+ mwl8k_tx_start(priv);
+ retry = 0;
+ continue;
+ }
+
if (priv->pending_tx_pkts < oldcount) {
wiphy_notice(hw->wiphy,
"waiting for tx rings to drain (%d -> %d pkts)\n",
@@ -2055,6 +2061,7 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw,
mwl8k_remove_stream(hw, stream);
spin_unlock(&priv->stream_lock);
}
+ mwl8k_tx_start(priv);
spin_unlock_bh(&priv->tx_lock);
pci_unmap_single(priv->pdev, dma, skb->len,
PCI_DMA_TODEVICE);
diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c
index 978e7eb2656..7fc46f26cf2 100644
--- a/drivers/net/wireless/p54/p54spi.c
+++ b/drivers/net/wireless/p54/p54spi.c
@@ -42,8 +42,7 @@
MODULE_FIRMWARE("3826.arm");
-/*
- * gpios should be handled in board files and provided via platform data,
+/* gpios should be handled in board files and provided via platform data,
* but because it's currently impossible for p54spi to have a header file
* in include/linux, let's use module paramaters for now
*/
@@ -191,8 +190,7 @@ static int p54spi_request_eeprom(struct ieee80211_hw *dev)
const struct firmware *eeprom;
int ret;
- /*
- * allow users to customize their eeprom.
+ /* allow users to customize their eeprom.
*/
ret = request_firmware(&eeprom, "3826.eeprom", &priv->spi->dev);
@@ -285,8 +283,7 @@ static void p54spi_power_on(struct p54s_priv *priv)
gpio_set_value(p54spi_gpio_power, 1);
enable_irq(gpio_to_irq(p54spi_gpio_irq));
- /*
- * need to wait a while before device can be accessed, the length
+ /* need to wait a while before device can be accessed, the length
* is just a guess
*/
msleep(10);
@@ -365,7 +362,8 @@ static int p54spi_rx(struct p54s_priv *priv)
/* Firmware may insert up to 4 padding bytes after the lmac header,
* but it does not amend the size of SPI data transfer.
* Such packets has correct data size in header, thus referencing
- * past the end of allocated skb. Reserve extra 4 bytes for this case */
+ * past the end of allocated skb. Reserve extra 4 bytes for this case
+ */
skb = dev_alloc_skb(len + 4);
if (!skb) {
p54spi_sleep(priv);
@@ -383,7 +381,8 @@ static int p54spi_rx(struct p54s_priv *priv)
}
p54spi_sleep(priv);
/* Put additional bytes to compensate for the possible
- * alignment-caused truncation */
+ * alignment-caused truncation
+ */
skb_put(skb, 4);
if (p54_rx(priv->hw, skb) == 0)
@@ -713,27 +712,7 @@ static struct spi_driver p54spi_driver = {
.remove = p54spi_remove,
};
-static int __init p54spi_init(void)
-{
- int ret;
-
- ret = spi_register_driver(&p54spi_driver);
- if (ret < 0) {
- printk(KERN_ERR "failed to register SPI driver: %d", ret);
- goto out;
- }
-
-out:
- return ret;
-}
-
-static void __exit p54spi_exit(void)
-{
- spi_unregister_driver(&p54spi_driver);
-}
-
-module_init(p54spi_init);
-module_exit(p54spi_exit);
+module_spi_driver(p54spi_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Christian Lamparter <chunkeey@web.de>");
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index b52d70c75e1..ead3a3e746a 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -840,7 +840,7 @@ static inline void rt2800_clear_beacon_register(struct rt2x00_dev *rt2x00dev,
unsigned int beacon_base)
{
int i;
- const int txwi_desc_size = rt2x00dev->ops->bcn->winfo_size;
+ const int txwi_desc_size = rt2x00dev->bcn->winfo_size;
/*
* For the Beacon base registers we only need to clear
@@ -3953,379 +3953,577 @@ static void rt2800_init_bbp_early(struct rt2x00_dev *rt2x00dev)
rt2800_bbp_write(rt2x00dev, 106, 0x35);
}
-static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev)
+static void rt2800_disable_unused_dac_adc(struct rt2x00_dev *rt2x00dev)
{
- int ant, div_mode;
u16 eeprom;
u8 value;
- rt2800_init_bbp_early(rt2x00dev);
+ rt2800_bbp_read(rt2x00dev, 138, &value);
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
+ if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1)
+ value |= 0x20;
+ if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1)
+ value &= ~0x02;
+ rt2800_bbp_write(rt2x00dev, 138, value);
+}
- rt2800_bbp_read(rt2x00dev, 105, &value);
- rt2x00_set_field8(&value, BBP105_MLD,
- rt2x00dev->default_ant.rx_chain_num == 2);
- rt2800_bbp_write(rt2x00dev, 105, value);
+static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev)
+{
+ rt2800_bbp_write(rt2x00dev, 31, 0x08);
+
+ rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+ rt2800_bbp_write(rt2x00dev, 66, 0x38);
+
+ rt2800_bbp_write(rt2x00dev, 69, 0x12);
+ rt2800_bbp_write(rt2x00dev, 73, 0x10);
+
+ rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+
+ rt2800_bbp_write(rt2x00dev, 78, 0x0e);
+ rt2800_bbp_write(rt2x00dev, 80, 0x08);
+
+ rt2800_bbp_write(rt2x00dev, 82, 0x62);
+
+ rt2800_bbp_write(rt2x00dev, 83, 0x6a);
+
+ rt2800_bbp_write(rt2x00dev, 84, 0x99);
+
+ rt2800_bbp_write(rt2x00dev, 86, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 91, 0x04);
+
+ rt2800_bbp_write(rt2x00dev, 92, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+
+ rt2800_bbp_write(rt2x00dev, 105, 0x01);
+
+ rt2800_bbp_write(rt2x00dev, 106, 0x35);
+}
+
+static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev)
+{
+ rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+ rt2800_bbp_write(rt2x00dev, 66, 0x38);
+
+ if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) {
+ rt2800_bbp_write(rt2x00dev, 69, 0x16);
+ rt2800_bbp_write(rt2x00dev, 73, 0x12);
+ } else {
+ rt2800_bbp_write(rt2x00dev, 69, 0x12);
+ rt2800_bbp_write(rt2x00dev, 73, 0x10);
+ }
+
+ rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+
+ rt2800_bbp_write(rt2x00dev, 81, 0x37);
+
+ rt2800_bbp_write(rt2x00dev, 82, 0x62);
+
+ rt2800_bbp_write(rt2x00dev, 83, 0x6a);
+
+ if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860D))
+ rt2800_bbp_write(rt2x00dev, 84, 0x19);
+ else
+ rt2800_bbp_write(rt2x00dev, 84, 0x99);
+
+ rt2800_bbp_write(rt2x00dev, 86, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 91, 0x04);
+
+ rt2800_bbp_write(rt2x00dev, 92, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 103, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 105, 0x05);
+
+ rt2800_bbp_write(rt2x00dev, 106, 0x35);
+}
+
+static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev)
+{
+ rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+ rt2800_bbp_write(rt2x00dev, 66, 0x38);
+
+ rt2800_bbp_write(rt2x00dev, 69, 0x12);
+ rt2800_bbp_write(rt2x00dev, 73, 0x10);
+
+ rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+
+ rt2800_bbp_write(rt2x00dev, 79, 0x13);
+ rt2800_bbp_write(rt2x00dev, 80, 0x05);
+ rt2800_bbp_write(rt2x00dev, 81, 0x33);
+
+ rt2800_bbp_write(rt2x00dev, 82, 0x62);
+
+ rt2800_bbp_write(rt2x00dev, 83, 0x6a);
+
+ rt2800_bbp_write(rt2x00dev, 84, 0x99);
+
+ rt2800_bbp_write(rt2x00dev, 86, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 91, 0x04);
+
+ rt2800_bbp_write(rt2x00dev, 92, 0x00);
+
+ if (rt2x00_rt_rev_gte(rt2x00dev, RT3070, REV_RT3070F) ||
+ rt2x00_rt_rev_gte(rt2x00dev, RT3071, REV_RT3071E) ||
+ rt2x00_rt_rev_gte(rt2x00dev, RT3090, REV_RT3090E))
+ rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+ else
+ rt2800_bbp_write(rt2x00dev, 103, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 105, 0x05);
+
+ rt2800_bbp_write(rt2x00dev, 106, 0x35);
+
+ if (rt2x00_rt(rt2x00dev, RT3071) ||
+ rt2x00_rt(rt2x00dev, RT3090))
+ rt2800_disable_unused_dac_adc(rt2x00dev);
+}
+
+static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev)
+{
+ u8 value;
rt2800_bbp4_mac_if_ctrl(rt2x00dev);
- rt2800_bbp_write(rt2x00dev, 20, 0x06);
rt2800_bbp_write(rt2x00dev, 31, 0x08);
- rt2800_bbp_write(rt2x00dev, 65, 0x2C);
- rt2800_bbp_write(rt2x00dev, 68, 0xDD);
- rt2800_bbp_write(rt2x00dev, 69, 0x1A);
- rt2800_bbp_write(rt2x00dev, 70, 0x05);
+
+ rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+ rt2800_bbp_write(rt2x00dev, 66, 0x38);
+
+ rt2800_bbp_write(rt2x00dev, 68, 0x0b);
+
+ rt2800_bbp_write(rt2x00dev, 69, 0x12);
rt2800_bbp_write(rt2x00dev, 73, 0x13);
- rt2800_bbp_write(rt2x00dev, 74, 0x0F);
- rt2800_bbp_write(rt2x00dev, 75, 0x4F);
+ rt2800_bbp_write(rt2x00dev, 75, 0x46);
rt2800_bbp_write(rt2x00dev, 76, 0x28);
+
+ rt2800_bbp_write(rt2x00dev, 77, 0x58);
+
+ rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+
+ rt2800_bbp_write(rt2x00dev, 74, 0x0b);
+ rt2800_bbp_write(rt2x00dev, 79, 0x18);
+ rt2800_bbp_write(rt2x00dev, 80, 0x09);
+ rt2800_bbp_write(rt2x00dev, 81, 0x33);
+
+ rt2800_bbp_write(rt2x00dev, 82, 0x62);
+
+ rt2800_bbp_write(rt2x00dev, 83, 0x7a);
+
+ rt2800_bbp_write(rt2x00dev, 84, 0x9a);
+
+ rt2800_bbp_write(rt2x00dev, 86, 0x38);
+
+ rt2800_bbp_write(rt2x00dev, 91, 0x04);
+
+ rt2800_bbp_write(rt2x00dev, 92, 0x02);
+
+ rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+
+ rt2800_bbp_write(rt2x00dev, 104, 0x92);
+
+ rt2800_bbp_write(rt2x00dev, 105, 0x1c);
+
+ rt2800_bbp_write(rt2x00dev, 106, 0x03);
+
+ rt2800_bbp_write(rt2x00dev, 128, 0x12);
+
+ rt2800_bbp_write(rt2x00dev, 67, 0x24);
+ rt2800_bbp_write(rt2x00dev, 143, 0x04);
+ rt2800_bbp_write(rt2x00dev, 142, 0x99);
+ rt2800_bbp_write(rt2x00dev, 150, 0x30);
+ rt2800_bbp_write(rt2x00dev, 151, 0x2e);
+ rt2800_bbp_write(rt2x00dev, 152, 0x20);
+ rt2800_bbp_write(rt2x00dev, 153, 0x34);
+ rt2800_bbp_write(rt2x00dev, 154, 0x40);
+ rt2800_bbp_write(rt2x00dev, 155, 0x3b);
+ rt2800_bbp_write(rt2x00dev, 253, 0x04);
+
+ rt2800_bbp_read(rt2x00dev, 47, &value);
+ rt2x00_set_field8(&value, BBP47_TSSI_ADC6, 1);
+ rt2800_bbp_write(rt2x00dev, 47, value);
+
+ /* Use 5-bit ADC for Acquisition and 8-bit ADC for data */
+ rt2800_bbp_read(rt2x00dev, 3, &value);
+ rt2x00_set_field8(&value, BBP3_ADC_MODE_SWITCH, 1);
+ rt2x00_set_field8(&value, BBP3_ADC_INIT_MODE, 1);
+ rt2800_bbp_write(rt2x00dev, 3, value);
+}
+
+static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev)
+{
+ rt2800_bbp_write(rt2x00dev, 3, 0x00);
+ rt2800_bbp_write(rt2x00dev, 4, 0x50);
+
+ rt2800_bbp_write(rt2x00dev, 31, 0x08);
+
+ rt2800_bbp_write(rt2x00dev, 47, 0x48);
+
+ rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+ rt2800_bbp_write(rt2x00dev, 66, 0x38);
+
+ rt2800_bbp_write(rt2x00dev, 68, 0x0b);
+
+ rt2800_bbp_write(rt2x00dev, 69, 0x12);
+ rt2800_bbp_write(rt2x00dev, 73, 0x13);
+ rt2800_bbp_write(rt2x00dev, 75, 0x46);
+ rt2800_bbp_write(rt2x00dev, 76, 0x28);
+
rt2800_bbp_write(rt2x00dev, 77, 0x59);
- rt2800_bbp_write(rt2x00dev, 84, 0x9A);
+
+ rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+
+ rt2800_bbp_write(rt2x00dev, 78, 0x0e);
+ rt2800_bbp_write(rt2x00dev, 80, 0x08);
+ rt2800_bbp_write(rt2x00dev, 81, 0x37);
+
+ rt2800_bbp_write(rt2x00dev, 82, 0x62);
+
+ rt2800_bbp_write(rt2x00dev, 83, 0x6a);
+
+ rt2800_bbp_write(rt2x00dev, 84, 0x99);
+
rt2800_bbp_write(rt2x00dev, 86, 0x38);
+
rt2800_bbp_write(rt2x00dev, 88, 0x90);
+
rt2800_bbp_write(rt2x00dev, 91, 0x04);
+
rt2800_bbp_write(rt2x00dev, 92, 0x02);
- rt2800_bbp_write(rt2x00dev, 95, 0x9a);
- rt2800_bbp_write(rt2x00dev, 98, 0x12);
- rt2800_bbp_write(rt2x00dev, 103, 0xC0);
+
+ rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+
rt2800_bbp_write(rt2x00dev, 104, 0x92);
- /* FIXME BBP105 owerwrite */
- rt2800_bbp_write(rt2x00dev, 105, 0x3C);
- rt2800_bbp_write(rt2x00dev, 106, 0x35);
- rt2800_bbp_write(rt2x00dev, 128, 0x12);
- rt2800_bbp_write(rt2x00dev, 134, 0xD0);
- rt2800_bbp_write(rt2x00dev, 135, 0xF6);
- rt2800_bbp_write(rt2x00dev, 137, 0x0F);
- /* Initialize GLRT (Generalized Likehood Radio Test) */
- rt2800_init_bbp_5592_glrt(rt2x00dev);
+ rt2800_bbp_write(rt2x00dev, 105, 0x34);
+
+ rt2800_bbp_write(rt2x00dev, 106, 0x05);
+
+ rt2800_bbp_write(rt2x00dev, 120, 0x50);
+
+ rt2800_bbp_write(rt2x00dev, 137, 0x0f);
+
+ rt2800_bbp_write(rt2x00dev, 163, 0xbd);
+ /* Set ITxBF timeout to 0x9c40=1000msec */
+ rt2800_bbp_write(rt2x00dev, 179, 0x02);
+ rt2800_bbp_write(rt2x00dev, 180, 0x00);
+ rt2800_bbp_write(rt2x00dev, 182, 0x40);
+ rt2800_bbp_write(rt2x00dev, 180, 0x01);
+ rt2800_bbp_write(rt2x00dev, 182, 0x9c);
+ rt2800_bbp_write(rt2x00dev, 179, 0x00);
+ /* Reprogram the inband interface to put right values in RXWI */
+ rt2800_bbp_write(rt2x00dev, 142, 0x04);
+ rt2800_bbp_write(rt2x00dev, 143, 0x3b);
+ rt2800_bbp_write(rt2x00dev, 142, 0x06);
+ rt2800_bbp_write(rt2x00dev, 143, 0xa0);
+ rt2800_bbp_write(rt2x00dev, 142, 0x07);
+ rt2800_bbp_write(rt2x00dev, 143, 0xa1);
+ rt2800_bbp_write(rt2x00dev, 142, 0x08);
+ rt2800_bbp_write(rt2x00dev, 143, 0xa2);
+
+ rt2800_bbp_write(rt2x00dev, 148, 0xc8);
+}
- rt2800_bbp4_mac_if_ctrl(rt2x00dev);
+static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev)
+{
+ rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+ rt2800_bbp_write(rt2x00dev, 66, 0x38);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
- div_mode = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_ANT_DIVERSITY);
- ant = (div_mode == 3) ? 1 : 0;
- rt2800_bbp_read(rt2x00dev, 152, &value);
- if (ant == 0) {
- /* Main antenna */
- rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1);
- } else {
- /* Auxiliary antenna */
- rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0);
- }
- rt2800_bbp_write(rt2x00dev, 152, value);
+ rt2800_bbp_write(rt2x00dev, 69, 0x12);
+ rt2800_bbp_write(rt2x00dev, 73, 0x10);
- if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) {
- rt2800_bbp_read(rt2x00dev, 254, &value);
- rt2x00_set_field8(&value, BBP254_BIT7, 1);
- rt2800_bbp_write(rt2x00dev, 254, value);
- }
+ rt2800_bbp_write(rt2x00dev, 70, 0x0a);
- rt2800_init_freq_calibration(rt2x00dev);
+ rt2800_bbp_write(rt2x00dev, 79, 0x13);
+ rt2800_bbp_write(rt2x00dev, 80, 0x05);
+ rt2800_bbp_write(rt2x00dev, 81, 0x33);
- rt2800_bbp_write(rt2x00dev, 84, 0x19);
- if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C))
+ rt2800_bbp_write(rt2x00dev, 82, 0x62);
+
+ rt2800_bbp_write(rt2x00dev, 83, 0x6a);
+
+ rt2800_bbp_write(rt2x00dev, 84, 0x99);
+
+ rt2800_bbp_write(rt2x00dev, 86, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 91, 0x04);
+
+ rt2800_bbp_write(rt2x00dev, 92, 0x00);
+
+ if (rt2x00_rt_rev_gte(rt2x00dev, RT3390, REV_RT3390E))
rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+ else
+ rt2800_bbp_write(rt2x00dev, 103, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 105, 0x05);
+
+ rt2800_bbp_write(rt2x00dev, 106, 0x35);
+
+ rt2800_disable_unused_dac_adc(rt2x00dev);
}
-static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
+static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev)
{
- unsigned int i;
- u16 eeprom;
- u8 reg_id;
- u8 value;
+ rt2800_bbp_write(rt2x00dev, 31, 0x08);
- if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev) ||
- rt2800_wait_bbp_ready(rt2x00dev)))
- return -EACCES;
+ rt2800_bbp_write(rt2x00dev, 65, 0x2c);
+ rt2800_bbp_write(rt2x00dev, 66, 0x38);
- if (rt2x00_rt(rt2x00dev, RT5592)) {
- rt2800_init_bbp_5592(rt2x00dev);
- return 0;
- }
+ rt2800_bbp_write(rt2x00dev, 69, 0x12);
+ rt2800_bbp_write(rt2x00dev, 73, 0x10);
- if (rt2x00_rt(rt2x00dev, RT3352)) {
- rt2800_bbp_write(rt2x00dev, 3, 0x00);
- rt2800_bbp_write(rt2x00dev, 4, 0x50);
- }
+ rt2800_bbp_write(rt2x00dev, 70, 0x0a);
- if (rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392))
- rt2800_bbp4_mac_if_ctrl(rt2x00dev);
+ rt2800_bbp_write(rt2x00dev, 79, 0x13);
+ rt2800_bbp_write(rt2x00dev, 80, 0x05);
+ rt2800_bbp_write(rt2x00dev, 81, 0x33);
- if (rt2800_is_305x_soc(rt2x00dev) ||
- rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT3352) ||
- rt2x00_rt(rt2x00dev, RT3572) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392))
- rt2800_bbp_write(rt2x00dev, 31, 0x08);
+ rt2800_bbp_write(rt2x00dev, 82, 0x62);
- if (rt2x00_rt(rt2x00dev, RT3352))
- rt2800_bbp_write(rt2x00dev, 47, 0x48);
+ rt2800_bbp_write(rt2x00dev, 83, 0x6a);
+
+ rt2800_bbp_write(rt2x00dev, 84, 0x99);
+
+ rt2800_bbp_write(rt2x00dev, 86, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 91, 0x04);
+
+ rt2800_bbp_write(rt2x00dev, 92, 0x00);
+
+ rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+
+ rt2800_bbp_write(rt2x00dev, 105, 0x05);
+
+ rt2800_bbp_write(rt2x00dev, 106, 0x35);
+
+ rt2800_disable_unused_dac_adc(rt2x00dev);
+}
+
+static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev)
+{
+ int ant, div_mode;
+ u16 eeprom;
+ u8 value;
+
+ rt2800_bbp4_mac_if_ctrl(rt2x00dev);
+
+ rt2800_bbp_write(rt2x00dev, 31, 0x08);
rt2800_bbp_write(rt2x00dev, 65, 0x2c);
rt2800_bbp_write(rt2x00dev, 66, 0x38);
- if (rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT3352) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392))
- rt2800_bbp_write(rt2x00dev, 68, 0x0b);
+ rt2800_bbp_write(rt2x00dev, 68, 0x0b);
- if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) {
- rt2800_bbp_write(rt2x00dev, 69, 0x16);
- rt2800_bbp_write(rt2x00dev, 73, 0x12);
- } else if (rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT3352) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392)) {
- rt2800_bbp_write(rt2x00dev, 69, 0x12);
- rt2800_bbp_write(rt2x00dev, 73, 0x13);
- rt2800_bbp_write(rt2x00dev, 75, 0x46);
- rt2800_bbp_write(rt2x00dev, 76, 0x28);
+ rt2800_bbp_write(rt2x00dev, 69, 0x12);
+ rt2800_bbp_write(rt2x00dev, 73, 0x13);
+ rt2800_bbp_write(rt2x00dev, 75, 0x46);
+ rt2800_bbp_write(rt2x00dev, 76, 0x28);
- if (rt2x00_rt(rt2x00dev, RT3290))
- rt2800_bbp_write(rt2x00dev, 77, 0x58);
- else
- rt2800_bbp_write(rt2x00dev, 77, 0x59);
- } else {
- rt2800_bbp_write(rt2x00dev, 69, 0x12);
- rt2800_bbp_write(rt2x00dev, 73, 0x10);
- }
+ rt2800_bbp_write(rt2x00dev, 77, 0x59);
rt2800_bbp_write(rt2x00dev, 70, 0x0a);
- if (rt2x00_rt(rt2x00dev, RT3070) ||
- rt2x00_rt(rt2x00dev, RT3071) ||
- rt2x00_rt(rt2x00dev, RT3090) ||
- rt2x00_rt(rt2x00dev, RT3390) ||
- rt2x00_rt(rt2x00dev, RT3572) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392)) {
- rt2800_bbp_write(rt2x00dev, 79, 0x13);
- rt2800_bbp_write(rt2x00dev, 80, 0x05);
- rt2800_bbp_write(rt2x00dev, 81, 0x33);
- } else if (rt2800_is_305x_soc(rt2x00dev)) {
- rt2800_bbp_write(rt2x00dev, 78, 0x0e);
- rt2800_bbp_write(rt2x00dev, 80, 0x08);
- } else if (rt2x00_rt(rt2x00dev, RT3290)) {
- rt2800_bbp_write(rt2x00dev, 74, 0x0b);
- rt2800_bbp_write(rt2x00dev, 79, 0x18);
- rt2800_bbp_write(rt2x00dev, 80, 0x09);
- rt2800_bbp_write(rt2x00dev, 81, 0x33);
- } else if (rt2x00_rt(rt2x00dev, RT3352)) {
- rt2800_bbp_write(rt2x00dev, 78, 0x0e);
- rt2800_bbp_write(rt2x00dev, 80, 0x08);
- rt2800_bbp_write(rt2x00dev, 81, 0x37);
- } else {
- rt2800_bbp_write(rt2x00dev, 81, 0x37);
- }
+ rt2800_bbp_write(rt2x00dev, 79, 0x13);
+ rt2800_bbp_write(rt2x00dev, 80, 0x05);
+ rt2800_bbp_write(rt2x00dev, 81, 0x33);
rt2800_bbp_write(rt2x00dev, 82, 0x62);
- if (rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392))
- rt2800_bbp_write(rt2x00dev, 83, 0x7a);
- else
- rt2800_bbp_write(rt2x00dev, 83, 0x6a);
- if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860D))
- rt2800_bbp_write(rt2x00dev, 84, 0x19);
- else if (rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392))
- rt2800_bbp_write(rt2x00dev, 84, 0x9a);
- else
- rt2800_bbp_write(rt2x00dev, 84, 0x99);
+ rt2800_bbp_write(rt2x00dev, 83, 0x7a);
- if (rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT3352) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392))
- rt2800_bbp_write(rt2x00dev, 86, 0x38);
- else
- rt2800_bbp_write(rt2x00dev, 86, 0x00);
+ rt2800_bbp_write(rt2x00dev, 84, 0x9a);
- if (rt2x00_rt(rt2x00dev, RT3352) ||
- rt2x00_rt(rt2x00dev, RT5392))
+ rt2800_bbp_write(rt2x00dev, 86, 0x38);
+
+ if (rt2x00_rt(rt2x00dev, RT5392))
rt2800_bbp_write(rt2x00dev, 88, 0x90);
rt2800_bbp_write(rt2x00dev, 91, 0x04);
- if (rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT3352) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392))
- rt2800_bbp_write(rt2x00dev, 92, 0x02);
- else
- rt2800_bbp_write(rt2x00dev, 92, 0x00);
+ rt2800_bbp_write(rt2x00dev, 92, 0x02);
if (rt2x00_rt(rt2x00dev, RT5392)) {
rt2800_bbp_write(rt2x00dev, 95, 0x9a);
rt2800_bbp_write(rt2x00dev, 98, 0x12);
}
- if (rt2x00_rt_rev_gte(rt2x00dev, RT3070, REV_RT3070F) ||
- rt2x00_rt_rev_gte(rt2x00dev, RT3071, REV_RT3071E) ||
- rt2x00_rt_rev_gte(rt2x00dev, RT3090, REV_RT3090E) ||
- rt2x00_rt_rev_gte(rt2x00dev, RT3390, REV_RT3390E) ||
- rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT3352) ||
- rt2x00_rt(rt2x00dev, RT3572) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392) ||
- rt2800_is_305x_soc(rt2x00dev))
- rt2800_bbp_write(rt2x00dev, 103, 0xc0);
- else
- rt2800_bbp_write(rt2x00dev, 103, 0x00);
+ rt2800_bbp_write(rt2x00dev, 103, 0xc0);
- if (rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT3352) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392))
- rt2800_bbp_write(rt2x00dev, 104, 0x92);
+ rt2800_bbp_write(rt2x00dev, 104, 0x92);
- if (rt2800_is_305x_soc(rt2x00dev))
- rt2800_bbp_write(rt2x00dev, 105, 0x01);
- else if (rt2x00_rt(rt2x00dev, RT3290))
- rt2800_bbp_write(rt2x00dev, 105, 0x1c);
- else if (rt2x00_rt(rt2x00dev, RT3352))
- rt2800_bbp_write(rt2x00dev, 105, 0x34);
- else if (rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392))
- rt2800_bbp_write(rt2x00dev, 105, 0x3c);
- else
- rt2800_bbp_write(rt2x00dev, 105, 0x05);
+ rt2800_bbp_write(rt2x00dev, 105, 0x3c);
- if (rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT5390))
+ if (rt2x00_rt(rt2x00dev, RT5390))
rt2800_bbp_write(rt2x00dev, 106, 0x03);
- else if (rt2x00_rt(rt2x00dev, RT3352))
- rt2800_bbp_write(rt2x00dev, 106, 0x05);
else if (rt2x00_rt(rt2x00dev, RT5392))
rt2800_bbp_write(rt2x00dev, 106, 0x12);
else
- rt2800_bbp_write(rt2x00dev, 106, 0x35);
-
- if (rt2x00_rt(rt2x00dev, RT3352))
- rt2800_bbp_write(rt2x00dev, 120, 0x50);
+ WARN_ON(1);
- if (rt2x00_rt(rt2x00dev, RT3290) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392))
- rt2800_bbp_write(rt2x00dev, 128, 0x12);
+ rt2800_bbp_write(rt2x00dev, 128, 0x12);
if (rt2x00_rt(rt2x00dev, RT5392)) {
rt2800_bbp_write(rt2x00dev, 134, 0xd0);
rt2800_bbp_write(rt2x00dev, 135, 0xf6);
}
- if (rt2x00_rt(rt2x00dev, RT3352))
- rt2800_bbp_write(rt2x00dev, 137, 0x0f);
+ rt2800_disable_unused_dac_adc(rt2x00dev);
- if (rt2x00_rt(rt2x00dev, RT3071) ||
- rt2x00_rt(rt2x00dev, RT3090) ||
- rt2x00_rt(rt2x00dev, RT3390) ||
- rt2x00_rt(rt2x00dev, RT3572) ||
- rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392)) {
- rt2800_bbp_read(rt2x00dev, 138, &value);
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+ div_mode = rt2x00_get_field16(eeprom,
+ EEPROM_NIC_CONF1_ANT_DIVERSITY);
+ ant = (div_mode == 3) ? 1 : 0;
- rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
- if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1)
- value |= 0x20;
- if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1)
- value &= ~0x02;
+ /* check if this is a Bluetooth combo card */
+ if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
+ u32 reg;
- rt2800_bbp_write(rt2x00dev, 138, value);
+ rt2800_register_read(rt2x00dev, GPIO_CTRL, &reg);
+ rt2x00_set_field32(&reg, GPIO_CTRL_DIR3, 0);
+ rt2x00_set_field32(&reg, GPIO_CTRL_DIR6, 0);
+ rt2x00_set_field32(&reg, GPIO_CTRL_VAL3, 0);
+ rt2x00_set_field32(&reg, GPIO_CTRL_VAL6, 0);
+ if (ant == 0)
+ rt2x00_set_field32(&reg, GPIO_CTRL_VAL3, 1);
+ else if (ant == 1)
+ rt2x00_set_field32(&reg, GPIO_CTRL_VAL6, 1);
+ rt2800_register_write(rt2x00dev, GPIO_CTRL, reg);
}
- if (rt2x00_rt(rt2x00dev, RT3290)) {
- rt2800_bbp_write(rt2x00dev, 67, 0x24);
- rt2800_bbp_write(rt2x00dev, 143, 0x04);
- rt2800_bbp_write(rt2x00dev, 142, 0x99);
- rt2800_bbp_write(rt2x00dev, 150, 0x30);
- rt2800_bbp_write(rt2x00dev, 151, 0x2e);
- rt2800_bbp_write(rt2x00dev, 152, 0x20);
- rt2800_bbp_write(rt2x00dev, 153, 0x34);
- rt2800_bbp_write(rt2x00dev, 154, 0x40);
- rt2800_bbp_write(rt2x00dev, 155, 0x3b);
- rt2800_bbp_write(rt2x00dev, 253, 0x04);
-
- rt2800_bbp_read(rt2x00dev, 47, &value);
- rt2x00_set_field8(&value, BBP47_TSSI_ADC6, 1);
- rt2800_bbp_write(rt2x00dev, 47, value);
-
- /* Use 5-bit ADC for Acquisition and 8-bit ADC for data */
- rt2800_bbp_read(rt2x00dev, 3, &value);
- rt2x00_set_field8(&value, BBP3_ADC_MODE_SWITCH, 1);
- rt2x00_set_field8(&value, BBP3_ADC_INIT_MODE, 1);
- rt2800_bbp_write(rt2x00dev, 3, value);
+ /* This chip has hardware antenna diversity*/
+ if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) {
+ rt2800_bbp_write(rt2x00dev, 150, 0); /* Disable Antenna Software OFDM */
+ rt2800_bbp_write(rt2x00dev, 151, 0); /* Disable Antenna Software CCK */
+ rt2800_bbp_write(rt2x00dev, 154, 0); /* Clear previously selected antenna */
}
- if (rt2x00_rt(rt2x00dev, RT3352)) {
- rt2800_bbp_write(rt2x00dev, 163, 0xbd);
- /* Set ITxBF timeout to 0x9c40=1000msec */
- rt2800_bbp_write(rt2x00dev, 179, 0x02);
- rt2800_bbp_write(rt2x00dev, 180, 0x00);
- rt2800_bbp_write(rt2x00dev, 182, 0x40);
- rt2800_bbp_write(rt2x00dev, 180, 0x01);
- rt2800_bbp_write(rt2x00dev, 182, 0x9c);
- rt2800_bbp_write(rt2x00dev, 179, 0x00);
- /* Reprogram the inband interface to put right values in RXWI */
- rt2800_bbp_write(rt2x00dev, 142, 0x04);
- rt2800_bbp_write(rt2x00dev, 143, 0x3b);
- rt2800_bbp_write(rt2x00dev, 142, 0x06);
- rt2800_bbp_write(rt2x00dev, 143, 0xa0);
- rt2800_bbp_write(rt2x00dev, 142, 0x07);
- rt2800_bbp_write(rt2x00dev, 143, 0xa1);
- rt2800_bbp_write(rt2x00dev, 142, 0x08);
- rt2800_bbp_write(rt2x00dev, 143, 0xa2);
-
- rt2800_bbp_write(rt2x00dev, 148, 0xc8);
+ rt2800_bbp_read(rt2x00dev, 152, &value);
+ if (ant == 0)
+ rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1);
+ else
+ rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0);
+ rt2800_bbp_write(rt2x00dev, 152, value);
+
+ rt2800_init_freq_calibration(rt2x00dev);
+}
+
+static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev)
+{
+ int ant, div_mode;
+ u16 eeprom;
+ u8 value;
+
+ rt2800_init_bbp_early(rt2x00dev);
+
+ rt2800_bbp_read(rt2x00dev, 105, &value);
+ rt2x00_set_field8(&value, BBP105_MLD,
+ rt2x00dev->default_ant.rx_chain_num == 2);
+ rt2800_bbp_write(rt2x00dev, 105, value);
+
+ rt2800_bbp4_mac_if_ctrl(rt2x00dev);
+
+ rt2800_bbp_write(rt2x00dev, 20, 0x06);
+ rt2800_bbp_write(rt2x00dev, 31, 0x08);
+ rt2800_bbp_write(rt2x00dev, 65, 0x2C);
+ rt2800_bbp_write(rt2x00dev, 68, 0xDD);
+ rt2800_bbp_write(rt2x00dev, 69, 0x1A);
+ rt2800_bbp_write(rt2x00dev, 70, 0x05);
+ rt2800_bbp_write(rt2x00dev, 73, 0x13);
+ rt2800_bbp_write(rt2x00dev, 74, 0x0F);
+ rt2800_bbp_write(rt2x00dev, 75, 0x4F);
+ rt2800_bbp_write(rt2x00dev, 76, 0x28);
+ rt2800_bbp_write(rt2x00dev, 77, 0x59);
+ rt2800_bbp_write(rt2x00dev, 84, 0x9A);
+ rt2800_bbp_write(rt2x00dev, 86, 0x38);
+ rt2800_bbp_write(rt2x00dev, 88, 0x90);
+ rt2800_bbp_write(rt2x00dev, 91, 0x04);
+ rt2800_bbp_write(rt2x00dev, 92, 0x02);
+ rt2800_bbp_write(rt2x00dev, 95, 0x9a);
+ rt2800_bbp_write(rt2x00dev, 98, 0x12);
+ rt2800_bbp_write(rt2x00dev, 103, 0xC0);
+ rt2800_bbp_write(rt2x00dev, 104, 0x92);
+ /* FIXME BBP105 owerwrite */
+ rt2800_bbp_write(rt2x00dev, 105, 0x3C);
+ rt2800_bbp_write(rt2x00dev, 106, 0x35);
+ rt2800_bbp_write(rt2x00dev, 128, 0x12);
+ rt2800_bbp_write(rt2x00dev, 134, 0xD0);
+ rt2800_bbp_write(rt2x00dev, 135, 0xF6);
+ rt2800_bbp_write(rt2x00dev, 137, 0x0F);
+
+ /* Initialize GLRT (Generalized Likehood Radio Test) */
+ rt2800_init_bbp_5592_glrt(rt2x00dev);
+
+ rt2800_bbp4_mac_if_ctrl(rt2x00dev);
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+ div_mode = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_ANT_DIVERSITY);
+ ant = (div_mode == 3) ? 1 : 0;
+ rt2800_bbp_read(rt2x00dev, 152, &value);
+ if (ant == 0) {
+ /* Main antenna */
+ rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1);
+ } else {
+ /* Auxiliary antenna */
+ rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0);
}
+ rt2800_bbp_write(rt2x00dev, 152, value);
- if (rt2x00_rt(rt2x00dev, RT5390) ||
- rt2x00_rt(rt2x00dev, RT5392)) {
- int ant, div_mode;
+ if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C)) {
+ rt2800_bbp_read(rt2x00dev, 254, &value);
+ rt2x00_set_field8(&value, BBP254_BIT7, 1);
+ rt2800_bbp_write(rt2x00dev, 254, value);
+ }
- rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
- div_mode = rt2x00_get_field16(eeprom,
- EEPROM_NIC_CONF1_ANT_DIVERSITY);
- ant = (div_mode == 3) ? 1 : 0;
+ rt2800_init_freq_calibration(rt2x00dev);
- /* check if this is a Bluetooth combo card */
- if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
- u32 reg;
-
- rt2800_register_read(rt2x00dev, GPIO_CTRL, &reg);
- rt2x00_set_field32(&reg, GPIO_CTRL_DIR3, 0);
- rt2x00_set_field32(&reg, GPIO_CTRL_DIR6, 0);
- rt2x00_set_field32(&reg, GPIO_CTRL_VAL3, 0);
- rt2x00_set_field32(&reg, GPIO_CTRL_VAL6, 0);
- if (ant == 0)
- rt2x00_set_field32(&reg, GPIO_CTRL_VAL3, 1);
- else if (ant == 1)
- rt2x00_set_field32(&reg, GPIO_CTRL_VAL6, 1);
- rt2800_register_write(rt2x00dev, GPIO_CTRL, reg);
- }
+ rt2800_bbp_write(rt2x00dev, 84, 0x19);
+ if (rt2x00_rt_rev_gte(rt2x00dev, RT5592, REV_RT5592C))
+ rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+}
- /* This chip has hardware antenna diversity*/
- if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) {
- rt2800_bbp_write(rt2x00dev, 150, 0); /* Disable Antenna Software OFDM */
- rt2800_bbp_write(rt2x00dev, 151, 0); /* Disable Antenna Software CCK */
- rt2800_bbp_write(rt2x00dev, 154, 0); /* Clear previously selected antenna */
- }
+static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
+{
+ unsigned int i;
+ u16 eeprom;
+ u8 reg_id;
+ u8 value;
- rt2800_bbp_read(rt2x00dev, 152, &value);
- if (ant == 0)
- rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1);
- else
- rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0);
- rt2800_bbp_write(rt2x00dev, 152, value);
+ if (rt2800_is_305x_soc(rt2x00dev))
+ rt2800_init_bbp_305x_soc(rt2x00dev);
- rt2800_init_freq_calibration(rt2x00dev);
+ switch (rt2x00dev->chip.rt) {
+ case RT2860:
+ case RT2872:
+ case RT2883:
+ rt2800_init_bbp_28xx(rt2x00dev);
+ break;
+ case RT3070:
+ case RT3071:
+ case RT3090:
+ rt2800_init_bbp_30xx(rt2x00dev);
+ break;
+ case RT3290:
+ rt2800_init_bbp_3290(rt2x00dev);
+ break;
+ case RT3352:
+ rt2800_init_bbp_3352(rt2x00dev);
+ break;
+ case RT3390:
+ rt2800_init_bbp_3390(rt2x00dev);
+ break;
+ case RT3572:
+ rt2800_init_bbp_3572(rt2x00dev);
+ break;
+ case RT5390:
+ case RT5392:
+ rt2800_init_bbp_53xx(rt2x00dev);
+ break;
+ case RT5592:
+ rt2800_init_bbp_5592(rt2x00dev);
+ return;
}
for (i = 0; i < EEPROM_BBP_SIZE; i++) {
@@ -4337,8 +4535,6 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
rt2800_bbp_write(rt2x00dev, reg_id, value);
}
}
-
- return 0;
}
static void rt2800_led_open_drain_enable(struct rt2x00_dev *rt2x00dev)
@@ -5189,9 +5385,11 @@ int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev)
}
msleep(1);
- if (unlikely(rt2800_init_bbp(rt2x00dev)))
+ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev) ||
+ rt2800_wait_bbp_ready(rt2x00dev)))
return -EIO;
+ rt2800_init_bbp(rt2x00dev);
rt2800_init_rfcsr(rt2x00dev);
if (rt2x00_is_usb(rt2x00dev) &&
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c
index 6f4a861af33..330f1d25726 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/rt2x00/rt2800pci.c
@@ -1014,7 +1014,7 @@ static void rt2800pci_txstatus_interrupt(struct rt2x00_dev *rt2x00dev)
* Since we have only one producer and one consumer we don't
* need to lock the kfifo.
*/
- for (i = 0; i < rt2x00dev->ops->tx->entry_num; i++) {
+ for (i = 0; i < rt2x00dev->tx->limit; i++) {
rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status);
if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID))
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index ac854d75bd6..c71a48da9a3 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -327,7 +327,7 @@ static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev)
* this limit so reduce the number to prevent errors.
*/
rt2x00_set_field32(&reg, USB_DMA_CFG_RX_BULK_AGG_LIMIT,
- ((rt2x00dev->ops->rx->entry_num * DATA_FRAME_SIZE)
+ ((rt2x00dev->rx->limit * DATA_FRAME_SIZE)
/ 1024) - 3);
rt2x00_set_field32(&reg, USB_DMA_CFG_RX_BULK_EN, 1);
rt2x00_set_field32(&reg, USB_DMA_CFG_TX_BULK_EN, 1);
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index 90dc1433698..6a201725bc5 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -1077,7 +1077,7 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev)
*/
int kfifo_size =
roundup_pow_of_two(rt2x00dev->ops->tx_queues *
- rt2x00dev->ops->tx->entry_num *
+ rt2x00dev->tx->limit *
sizeof(u32));
status = kfifo_alloc(&rt2x00dev->txstatus_fifo, kfifo_size,
@@ -1301,23 +1301,6 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
(rt2x00dev->ops->max_ap_intf - 1);
/*
- * Determine which operating modes are supported, all modes
- * which require beaconing, depend on the availability of
- * beacon entries.
- */
- rt2x00dev->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
- if (rt2x00dev->ops->bcn->entry_num > 0)
- rt2x00dev->hw->wiphy->interface_modes |=
- BIT(NL80211_IFTYPE_ADHOC) |
- BIT(NL80211_IFTYPE_AP) |
-#ifdef CONFIG_MAC80211_MESH
- BIT(NL80211_IFTYPE_MESH_POINT) |
-#endif
- BIT(NL80211_IFTYPE_WDS);
-
- rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
-
- /*
* Initialize work.
*/
rt2x00dev->workqueue =
@@ -1348,6 +1331,23 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
goto exit;
/*
+ * Determine which operating modes are supported, all modes
+ * which require beaconing, depend on the availability of
+ * beacon entries.
+ */
+ rt2x00dev->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+ if (rt2x00dev->ops->bcn->entry_num > 0)
+ rt2x00dev->hw->wiphy->interface_modes |=
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_AP) |
+#ifdef CONFIG_MAC80211_MESH
+ BIT(NL80211_IFTYPE_MESH_POINT) |
+#endif
+ BIT(NL80211_IFTYPE_WDS);
+
+ rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
+
+ /*
* Initialize ieee80211 structure.
*/
retval = rt2x00lib_probe_hw(rt2x00dev);
diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.c b/drivers/net/wireless/rt2x00/rt2x00pci.c
index dc49e525ae5..76d95deb274 100644
--- a/drivers/net/wireless/rt2x00/rt2x00pci.c
+++ b/drivers/net/wireless/rt2x00/rt2x00pci.c
@@ -105,11 +105,13 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops)
goto exit_release_regions;
}
+ pci_enable_msi(pci_dev);
+
hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw);
if (!hw) {
rt2x00_probe_err("Failed to allocate hardware\n");
retval = -ENOMEM;
- goto exit_release_regions;
+ goto exit_disable_msi;
}
pci_set_drvdata(pci_dev, hw);
@@ -150,6 +152,9 @@ exit_free_reg:
exit_free_device:
ieee80211_free_hw(hw);
+exit_disable_msi:
+ pci_disable_msi(pci_dev);
+
exit_release_regions:
pci_release_regions(pci_dev);
@@ -174,6 +179,8 @@ void rt2x00pci_remove(struct pci_dev *pci_dev)
rt2x00pci_free_reg(rt2x00dev);
ieee80211_free_hw(hw);
+ pci_disable_msi(pci_dev);
+
/*
* Free the PCI device data.
*/
diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c
index 0dc8180e251..7e1759b3e49 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/rt2x00/rt61pci.c
@@ -2175,7 +2175,7 @@ static void rt61pci_txdone(struct rt2x00_dev *rt2x00dev)
* that the TX_STA_FIFO stack has a size of 16. We stick to our
* tx ring size for now.
*/
- for (i = 0; i < rt2x00dev->ops->tx->entry_num; i++) {
+ for (i = 0; i < rt2x00dev->tx->limit; i++) {
rt2x00mmio_register_read(rt2x00dev, STA_CSR4, &reg);
if (!rt2x00_get_field32(reg, STA_CSR4_VALID))
break;
diff --git a/include/linux/cw1200_platform.h b/include/linux/cw1200_platform.h
new file mode 100644
index 00000000000..c168fa51234
--- /dev/null
+++ b/include/linux/cw1200_platform.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef CW1200_PLAT_H_INCLUDED
+#define CW1200_PLAT_H_INCLUDED
+
+struct cw1200_platform_data_spi {
+ u8 spi_bits_per_word; /* REQUIRED */
+ u16 ref_clk; /* REQUIRED (in KHz) */
+
+ /* All others are optional */
+ bool have_5ghz;
+ const struct resource *reset; /* GPIO to RSTn signal */
+ const struct resource *powerup; /* GPIO to POWERUP signal */
+ int (*power_ctrl)(const struct cw1200_platform_data_spi *pdata,
+ bool enable); /* Control 3v3 / 1v8 supply */
+ int (*clk_ctrl)(const struct cw1200_platform_data_spi *pdata,
+ bool enable); /* Control CLK32K */
+ const u8 *macaddr; /* if NULL, use cw1200_mac_template module parameter */
+ const char *sdd_file; /* if NULL, will use default for detected hw type */
+};
+
+struct cw1200_platform_data_sdio {
+ u16 ref_clk; /* REQUIRED (in KHz) */
+
+ /* All others are optional */
+ const struct resource *irq; /* if using GPIO for IRQ */
+ bool have_5ghz;
+ bool no_nptb; /* SDIO hardware does not support non-power-of-2-blocksizes */
+ const struct resource *reset; /* GPIO to RSTn signal */
+ const struct resource *powerup; /* GPIO to POWERUP signal */
+ int (*power_ctrl)(const struct cw1200_platform_data_sdio *pdata,
+ bool enable); /* Control 3v3 / 1v8 supply */
+ int (*clk_ctrl)(const struct cw1200_platform_data_sdio *pdata,
+ bool enable); /* Control CLK32K */
+ const u8 *macaddr; /* if NULL, use cw1200_mac_template module parameter */
+ const char *sdd_file; /* if NULL, will use default for detected hw type */
+};
+
+const void *cw1200_get_platform_data(void);
+
+#endif /* CW1200_PLAT_H_INCLUDED */
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 06b0ed0154a..d826e5a84af 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1829,6 +1829,15 @@ enum ieee80211_key_len {
WLAN_KEY_LEN_AES_CMAC = 16,
};
+#define IEEE80211_WEP_IV_LEN 4
+#define IEEE80211_WEP_ICV_LEN 4
+#define IEEE80211_CCMP_HDR_LEN 8
+#define IEEE80211_CCMP_MIC_LEN 8
+#define IEEE80211_CCMP_PN_LEN 6
+#define IEEE80211_TKIP_IV_LEN 8
+#define IEEE80211_TKIP_ICV_LEN 4
+#define IEEE80211_CMAC_PN_LEN 6
+
/* Public action codes */
enum ieee80211_pub_actioncode {
WLAN_PUB_ACTION_EXT_CHANSW_ANN = 4,
diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h
index 3a7256955b1..f9f931c89e3 100644
--- a/include/linux/ssb/ssb_regs.h
+++ b/include/linux/ssb/ssb_regs.h
@@ -172,6 +172,7 @@
#define SSB_SPROMSIZE_WORDS_R4 220
#define SSB_SPROMSIZE_BYTES_R123 (SSB_SPROMSIZE_WORDS_R123 * sizeof(u16))
#define SSB_SPROMSIZE_BYTES_R4 (SSB_SPROMSIZE_WORDS_R4 * sizeof(u16))
+#define SSB_SPROMSIZE_WORDS_R10 230
#define SSB_SPROM_BASE1 0x1000
#define SSB_SPROM_BASE31 0x0800
#define SSB_SPROM_REVISION 0x007E
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 26b5b692c22..b3b076a46d5 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -753,6 +753,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
* @STATION_INFO_LOCAL_PM: @local_pm filled
* @STATION_INFO_PEER_PM: @peer_pm filled
* @STATION_INFO_NONPEER_PM: @nonpeer_pm filled
+ * @STATION_INFO_CHAIN_SIGNAL: @chain_signal filled
+ * @STATION_INFO_CHAIN_SIGNAL_AVG: @chain_signal_avg filled
*/
enum station_info_flags {
STATION_INFO_INACTIVE_TIME = 1<<0,
@@ -781,6 +783,8 @@ enum station_info_flags {
STATION_INFO_NONPEER_PM = 1<<23,
STATION_INFO_RX_BYTES64 = 1<<24,
STATION_INFO_TX_BYTES64 = 1<<25,
+ STATION_INFO_CHAIN_SIGNAL = 1<<26,
+ STATION_INFO_CHAIN_SIGNAL_AVG = 1<<27,
};
/**
@@ -857,6 +861,8 @@ struct sta_bss_parameters {
u16 beacon_interval;
};
+#define IEEE80211_MAX_CHAINS 4
+
/**
* struct station_info - station information
*
@@ -874,6 +880,9 @@ struct sta_bss_parameters {
* For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_.
* @signal_avg: Average signal strength, type depends on the wiphy's signal_type.
* For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_.
+ * @chains: bitmask for filled values in @chain_signal, @chain_signal_avg
+ * @chain_signal: per-chain signal strength of last received packet in dBm
+ * @chain_signal_avg: per-chain signal strength average in dBm
* @txrate: current unicast bitrate from this station
* @rxrate: current unicast bitrate to this station
* @rx_packets: packets received from this station
@@ -909,6 +918,11 @@ struct station_info {
u8 plink_state;
s8 signal;
s8 signal_avg;
+
+ u8 chains;
+ s8 chain_signal[IEEE80211_MAX_CHAINS];
+ s8 chain_signal_avg[IEEE80211_MAX_CHAINS];
+
struct rate_info txrate;
struct rate_info rxrate;
u32 rx_packets;
@@ -1147,6 +1161,7 @@ struct mesh_config {
* @sync_method: which synchronization method to use
* @path_sel_proto: which path selection protocol to use
* @path_metric: which metric to use
+ * @auth_id: which authentication method this mesh is using
* @ie: vendor information elements (optional)
* @ie_len: length of vendor information elements
* @is_authenticated: this mesh requires authentication
@@ -1165,6 +1180,7 @@ struct mesh_setup {
u8 sync_method;
u8 path_sel_proto;
u8 path_metric;
+ u8 auth_id;
const u8 *ie;
u8 ie_len;
bool is_authenticated;
@@ -1241,6 +1257,7 @@ struct cfg80211_ssid {
* @scan_start: time (in jiffies) when the scan started
* @wdev: the wireless device to scan for
* @aborted: (internal) scan request was notified as aborted
+ * @notified: (internal) scan request was notified as done or aborted
* @no_cck: used to send probe requests at non CCK rate in 2GHz band
*/
struct cfg80211_scan_request {
@@ -1258,7 +1275,7 @@ struct cfg80211_scan_request {
/* internal */
struct wiphy *wiphy;
unsigned long scan_start;
- bool aborted;
+ bool aborted, notified;
bool no_cck;
/* keep last */
@@ -1850,7 +1867,9 @@ struct cfg80211_update_ft_ies_params {
* @get_mpath: get a mesh path for the given parameters
* @dump_mpath: dump mesh path callback -- resume dump at index @idx
* @join_mesh: join the mesh network with the specified parameters
+ * (invoked with the wireless_dev mutex held)
* @leave_mesh: leave the current mesh network
+ * (invoked with the wireless_dev mutex held)
*
* @get_mesh_config: Get the current mesh configuration
*
@@ -1877,20 +1896,28 @@ struct cfg80211_update_ft_ies_params {
* the scan/scan_done bracket too.
*
* @auth: Request to authenticate with the specified peer
+ * (invoked with the wireless_dev mutex held)
* @assoc: Request to (re)associate with the specified peer
+ * (invoked with the wireless_dev mutex held)
* @deauth: Request to deauthenticate from the specified peer
+ * (invoked with the wireless_dev mutex held)
* @disassoc: Request to disassociate from the specified peer
+ * (invoked with the wireless_dev mutex held)
*
* @connect: Connect to the ESS with the specified parameters. When connected,
* call cfg80211_connect_result() with status code %WLAN_STATUS_SUCCESS.
* If the connection fails for some reason, call cfg80211_connect_result()
* with the status from the AP.
+ * (invoked with the wireless_dev mutex held)
* @disconnect: Disconnect from the BSS/ESS.
+ * (invoked with the wireless_dev mutex held)
*
* @join_ibss: Join the specified IBSS (or create if necessary). Once done, call
* cfg80211_ibss_joined(), also call that function when changing BSSID due
* to a merge.
+ * (invoked with the wireless_dev mutex held)
* @leave_ibss: Leave the IBSS.
+ * (invoked with the wireless_dev mutex held)
*
* @set_mcast_rate: Set the specified multicast rate (only if vif is in ADHOC or
* MESH mode)
@@ -2556,6 +2583,9 @@ struct wiphy_wowlan_support {
* may request, if implemented.
*
* @wowlan: WoWLAN support information
+ * @wowlan_config: current WoWLAN configuration; this should usually not be
+ * used since access to it is necessarily racy, use the parameter passed
+ * to the suspend() operation instead.
*
* @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features.
* @ht_capa_mod_mask: Specify what ht_cap values can be over-ridden.
@@ -2623,6 +2653,7 @@ struct wiphy {
#ifdef CONFIG_PM
struct wiphy_wowlan_support wowlan;
+ struct cfg80211_wowlan *wowlan_config;
#endif
u16 max_remain_on_channel_duration;
@@ -2834,7 +2865,8 @@ struct cfg80211_cached_keys;
* by cfg80211 on change_interface
* @mgmt_registrations: list of registrations for management frames
* @mgmt_registrations_lock: lock for the list
- * @mtx: mutex used to lock data in this struct
+ * @mtx: mutex used to lock data in this struct, may be used by drivers
+ * and some API functions require it held
* @cleanup_work: work struct used for cleanup that can't be done directly
* @beacon_interval: beacon interval used on this device for transmitting
* beacons, 0 when not valid
@@ -2858,8 +2890,6 @@ struct wireless_dev {
struct mutex mtx;
- struct work_struct cleanup_work;
-
bool use_4addr, p2p_started;
u8 address[ETH_ALEN] __aligned(sizeof(u16));
@@ -2989,6 +3019,15 @@ struct ieee80211_rate *
ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
u32 basic_rates, int bitrate);
+/**
+ * ieee80211_mandatory_rates - get mandatory rates for a given band
+ * @sband: the band to look for rates in
+ *
+ * This function returns a bitmap of the mandatory rates for the given
+ * band, bits are set according to the rate position in the bitrates array.
+ */
+u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband);
+
/*
* Radiotap parsing functions -- for controlled injection support
*
@@ -3400,7 +3439,8 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
* This function is called whenever an authentication has been processed in
* station mode. The driver is required to call either this function or
* cfg80211_send_auth_timeout() to indicate the result of cfg80211_ops::auth()
- * call. This function may sleep.
+ * call. This function may sleep. The caller must hold the corresponding wdev's
+ * mutex.
*/
void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len);
@@ -3409,7 +3449,8 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len);
* @dev: network device
* @addr: The MAC address of the device with which the authentication timed out
*
- * This function may sleep.
+ * This function may sleep. The caller must hold the corresponding wdev's
+ * mutex.
*/
void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr);
@@ -3424,7 +3465,8 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr);
* This function is called whenever a (re)association response has been
* processed in station mode. The driver is required to call either this
* function or cfg80211_send_assoc_timeout() to indicate the result of
- * cfg80211_ops::assoc() call. This function may sleep.
+ * cfg80211_ops::assoc() call. This function may sleep. The caller must hold
+ * the corresponding wdev's mutex.
*/
void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,
const u8 *buf, size_t len);
@@ -3434,7 +3476,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,
* @dev: network device
* @addr: The MAC address of the device with which the association timed out
*
- * This function may sleep.
+ * This function may sleep. The caller must hold the corresponding wdev's mutex.
*/
void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr);
@@ -3446,21 +3488,12 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr);
*
* This function is called whenever deauthentication has been processed in
* station mode. This includes both received deauthentication frames and
- * locally generated ones. This function may sleep.
+ * locally generated ones. This function may sleep. The caller must hold the
+ * corresponding wdev's mutex.
*/
void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len);
/**
- * __cfg80211_send_deauth - notification of processed deauthentication
- * @dev: network device
- * @buf: deauthentication frame (header + body)
- * @len: length of the frame data
- *
- * Like cfg80211_send_deauth(), but doesn't take the wdev lock.
- */
-void __cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len);
-
-/**
* cfg80211_send_disassoc - notification of processed disassociation
* @dev: network device
* @buf: disassociation response frame (header + body)
@@ -3468,22 +3501,12 @@ void __cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len);
*
* This function is called whenever disassociation has been processed in
* station mode. This includes both received disassociation frames and locally
- * generated ones. This function may sleep.
+ * generated ones. This function may sleep. The caller must hold the
+ * corresponding wdev's mutex.
*/
void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len);
/**
- * __cfg80211_send_disassoc - notification of processed disassociation
- * @dev: network device
- * @buf: disassociation response frame (header + body)
- * @len: length of the frame data
- *
- * Like cfg80211_send_disassoc(), but doesn't take the wdev lock.
- */
-void __cfg80211_send_disassoc(struct net_device *dev, const u8 *buf,
- size_t len);
-
-/**
* cfg80211_send_unprot_deauth - notification of unprotected deauthentication
* @dev: network device
* @buf: deauthentication frame (header + body)
@@ -4153,6 +4176,7 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
* cfg80211_crit_proto_stopped() - indicate critical protocol stopped by driver.
*
* @wdev: the wireless device for which critical protocol is stopped.
+ * @gfp: allocation flags
*
* This function can be called by the driver to indicate it has reverted
* operation back to normal. One reason could be that the duration given
diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h
index c3999632e61..c6d07cb074b 100644
--- a/include/net/ieee80211_radiotap.h
+++ b/include/net/ieee80211_radiotap.h
@@ -269,6 +269,7 @@ enum ieee80211_radiotap_type {
#define IEEE80211_RADIOTAP_MCS_HAVE_GI 0x04
#define IEEE80211_RADIOTAP_MCS_HAVE_FMT 0x08
#define IEEE80211_RADIOTAP_MCS_HAVE_FEC 0x10
+#define IEEE80211_RADIOTAP_MCS_HAVE_STBC 0x20
#define IEEE80211_RADIOTAP_MCS_BW_MASK 0x03
#define IEEE80211_RADIOTAP_MCS_BW_20 0
@@ -278,6 +279,12 @@ enum ieee80211_radiotap_type {
#define IEEE80211_RADIOTAP_MCS_SGI 0x04
#define IEEE80211_RADIOTAP_MCS_FMT_GF 0x08
#define IEEE80211_RADIOTAP_MCS_FEC_LDPC 0x10
+#define IEEE80211_RADIOTAP_MCS_STBC_MASK 0x60
+#define IEEE80211_RADIOTAP_MCS_STBC_1 1
+#define IEEE80211_RADIOTAP_MCS_STBC_2 2
+#define IEEE80211_RADIOTAP_MCS_STBC_3 3
+
+#define IEEE80211_RADIOTAP_MCS_STBC_SHIFT 5
/* For IEEE80211_RADIOTAP_AMPDU_STATUS */
#define IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN 0x0001
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 04c2d4670dc..1f0014bd4d8 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -805,6 +805,7 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
* on this subframe
* @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC
* is stored in the @ampdu_delimiter_crc field)
+ * @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3
*/
enum mac80211_rx_flags {
RX_FLAG_MMIC_ERROR = BIT(0),
@@ -832,8 +833,11 @@ enum mac80211_rx_flags {
RX_FLAG_80MHZ = BIT(23),
RX_FLAG_80P80MHZ = BIT(24),
RX_FLAG_160MHZ = BIT(25),
+ RX_FLAG_STBC_MASK = BIT(26) | BIT(27),
};
+#define RX_FLAG_STBC_SHIFT 26
+
/**
* struct ieee80211_rx_status - receive status
*
@@ -850,6 +854,10 @@ enum mac80211_rx_flags {
* @signal: signal strength when receiving this frame, either in dBm, in dB or
* unspecified depending on the hardware capabilities flags
* @IEEE80211_HW_SIGNAL_*
+ * @chains: bitmask of receive chains for which separate signal strength
+ * values were filled.
+ * @chain_signal: per-chain signal strength, in dBm (unlike @signal, doesn't
+ * support dB or unspecified units)
* @antenna: antenna used
* @rate_idx: index of data rate into band's supported rates or MCS index if
* HT or VHT is used (%RX_FLAG_HT/%RX_FLAG_VHT)
@@ -881,6 +889,8 @@ struct ieee80211_rx_status {
u8 band;
u8 antenna;
s8 signal;
+ u8 chains;
+ s8 chain_signal[IEEE80211_MAX_CHAINS];
u8 ampdu_delimiter_crc;
u8 vendor_radiotap_align;
u8 vendor_radiotap_oui[3];
@@ -1235,7 +1245,7 @@ enum ieee80211_sta_rx_bandwidth {
* struct ieee80211_sta_rates - station rate selection table
*
* @rcu_head: RCU head used for freeing the table on update
- * @rates: transmit rates/flags to be used by default.
+ * @rate: transmit rates/flags to be used by default.
* Overriding entries per-packet is possible by using cb tx control.
*/
struct ieee80211_sta_rates {
@@ -1276,7 +1286,7 @@ struct ieee80211_sta_rates {
* notifications and capabilities. The value is only valid after
* the station moves to associated state.
* @smps_mode: current SMPS mode (off, static or dynamic)
- * @tx_rates: rate control selection table
+ * @rates: rate control selection table
*/
struct ieee80211_sta {
u32 supp_rates[IEEE80211_NUM_BANDS];
@@ -3043,7 +3053,8 @@ void ieee80211_napi_complete(struct ieee80211_hw *hw);
* This function may not be called in IRQ context. Calls to this function
* for a single hardware must be synchronized against each other. Calls to
* this function, ieee80211_rx_ni() and ieee80211_rx_irqsafe() may not be
- * mixed for a single hardware.
+ * mixed for a single hardware. Must not run concurrently with
+ * ieee80211_tx_status() or ieee80211_tx_status_ni().
*
* In process context use instead ieee80211_rx_ni().
*
@@ -3059,7 +3070,8 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb);
* (internally defers to a tasklet.)
*
* Calls to this function, ieee80211_rx() or ieee80211_rx_ni() may not
- * be mixed for a single hardware.
+ * be mixed for a single hardware.Must not run concurrently with
+ * ieee80211_tx_status() or ieee80211_tx_status_ni().
*
* @hw: the hardware this frame came in on
* @skb: the buffer to receive, owned by mac80211 after this call
@@ -3073,7 +3085,8 @@ void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb);
* (internally disables bottom halves).
*
* Calls to this function, ieee80211_rx() and ieee80211_rx_irqsafe() may
- * not be mixed for a single hardware.
+ * not be mixed for a single hardware. Must not run concurrently with
+ * ieee80211_tx_status() or ieee80211_tx_status_ni().
*
* @hw: the hardware this frame came in on
* @skb: the buffer to receive, owned by mac80211 after this call
@@ -3196,7 +3209,8 @@ void ieee80211_get_tx_rates(struct ieee80211_vif *vif,
* This function may not be called in IRQ context. Calls to this function
* for a single hardware must be synchronized against each other. Calls
* to this function, ieee80211_tx_status_ni() and ieee80211_tx_status_irqsafe()
- * may not be mixed for a single hardware.
+ * may not be mixed for a single hardware. Must not run concurrently with
+ * ieee80211_rx() or ieee80211_rx_ni().
*
* @hw: the hardware the frame was transmitted by
* @skb: the frame that was transmitted, owned by mac80211 after this call
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index d1e48b5e348..32b060ea526 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -27,6 +27,8 @@
#include <linux/types.h>
+#define NL80211_GENL_NAME "nl80211"
+
/**
* DOC: Station handling
*
@@ -1429,6 +1431,11 @@ enum nl80211_commands {
* @NL80211_ATTR_MAX_CRIT_PROT_DURATION: duration in milliseconds in which
* the connection should have increased reliability (u16).
*
+ * @NL80211_ATTR_PEER_AID: Association ID for the peer TDLS station (u16).
+ * This is similar to @NL80211_ATTR_STA_AID but with a difference of being
+ * allowed to be used with the first @NL80211_CMD_SET_STATION command to
+ * update a TDLS peer STA entry.
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -1727,6 +1734,8 @@ enum nl80211_attrs {
NL80211_ATTR_CRIT_PROT_ID,
NL80211_ATTR_MAX_CRIT_PROT_DURATION,
+ NL80211_ATTR_PEER_AID,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -1991,6 +2000,10 @@ enum nl80211_sta_bss_param {
* @NL80211_STA_INFO_PEER_PM: peer mesh STA link-specific power mode
* @NL80211_STA_INFO_NONPEER_PM: neighbor mesh STA power save mode towards
* non-peer STA
+ * @NL80211_STA_INFO_CHAIN_SIGNAL: per-chain signal strength of last PPDU
+ * Contains a nested array of signal strength attributes (u8, dBm)
+ * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average
+ * Same format as NL80211_STA_INFO_CHAIN_SIGNAL.
* @__NL80211_STA_INFO_AFTER_LAST: internal
* @NL80211_STA_INFO_MAX: highest possible station info attribute
*/
@@ -2020,6 +2033,8 @@ enum nl80211_sta_info {
NL80211_STA_INFO_NONPEER_PM,
NL80211_STA_INFO_RX_BYTES64,
NL80211_STA_INFO_TX_BYTES64,
+ NL80211_STA_INFO_CHAIN_SIGNAL,
+ NL80211_STA_INFO_CHAIN_SIGNAL_AVG,
/* keep last */
__NL80211_STA_INFO_AFTER_LAST,
@@ -2637,6 +2652,10 @@ enum nl80211_meshconf_params {
* @NL80211_MESH_SETUP_USERSPACE_MPM: Enable this option if userspace will
* implement an MPM which handles peer allocation and state.
*
+ * @NL80211_MESH_SETUP_AUTH_PROTOCOL: Inform the kernel of the authentication
+ * method (u8, as defined in IEEE 8.4.2.100.6, e.g. 0x1 for SAE).
+ * Default is no authentication method required.
+ *
* @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number
*
* @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use
@@ -2650,6 +2669,7 @@ enum nl80211_mesh_setup_params {
NL80211_MESH_SETUP_USERSPACE_AMPE,
NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC,
NL80211_MESH_SETUP_USERSPACE_MPM,
+ NL80211_MESH_SETUP_AUTH_PROTOCOL,
/* keep last */
__NL80211_MESH_SETUP_ATTR_AFTER_LAST,
diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c
index 0785e95c992..be7614b9ed2 100644
--- a/net/mac80211/aes_ccm.c
+++ b/net/mac80211/aes_ccm.c
@@ -85,7 +85,7 @@ void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
*cpos++ = *pos++ ^ e[i];
}
- for (i = 0; i < CCMP_MIC_LEN; i++)
+ for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++)
mic[i] = b[i] ^ s_0[i];
}
@@ -123,7 +123,7 @@ int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
crypto_cipher_encrypt_one(tfm, a, a);
}
- for (i = 0; i < CCMP_MIC_LEN; i++) {
+ for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++) {
if ((mic[i] ^ s_0[i]) != a[i])
return -1;
}
@@ -138,7 +138,7 @@ struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[])
tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
if (!IS_ERR(tfm))
- crypto_cipher_setkey(tfm, key, ALG_CCMP_KEY_LEN);
+ crypto_cipher_setkey(tfm, key, WLAN_KEY_LEN_CCMP);
return tfm;
}
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 1a89c80e640..232edf78d5a 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -444,7 +444,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
struct ieee80211_local *local = sdata->local;
struct timespec uptime;
u64 packets = 0;
- int ac;
+ int i, ac;
sinfo->generation = sdata->local->sta_generation;
@@ -488,6 +488,17 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
sinfo->signal = (s8)sta->last_signal;
sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
}
+ if (sta->chains) {
+ sinfo->filled |= STATION_INFO_CHAIN_SIGNAL |
+ STATION_INFO_CHAIN_SIGNAL_AVG;
+
+ sinfo->chains = sta->chains;
+ for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
+ sinfo->chain_signal[i] = sta->chain_signal_last[i];
+ sinfo->chain_signal_avg[i] =
+ (s8) -ewma_read(&sta->chain_signal_avg[i]);
+ }
+ }
sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
sta_set_rate_info_rx(sta, &sinfo->rxrate);
@@ -728,7 +739,7 @@ static void ieee80211_get_et_strings(struct wiphy *wiphy,
if (sset == ETH_SS_STATS) {
sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats);
- memcpy(data, *ieee80211_gstrings_sta_stats, sz_sta_stats);
+ memcpy(data, ieee80211_gstrings_sta_stats, sz_sta_stats);
}
drv_get_et_strings(sdata, sset, &(data[sz_sta_stats]));
}
@@ -1735,6 +1746,7 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
ifmsh->mesh_pp_id = setup->path_sel_proto;
ifmsh->mesh_pm_id = setup->path_metric;
ifmsh->user_mpm = setup->user_mpm;
+ ifmsh->mesh_auth_id = setup->auth_id;
ifmsh->security = IEEE80211_MESH_SEC_NONE;
if (setup->is_authenticated)
ifmsh->security |= IEEE80211_MESH_SEC_AUTHED;
@@ -2306,7 +2318,7 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
enum ieee80211_smps_mode old_req;
int err;
- lockdep_assert_held(&sdata->u.mgd.mtx);
+ lockdep_assert_held(&sdata->wdev.mtx);
old_req = sdata->u.mgd.req_smps;
sdata->u.mgd.req_smps = smps_mode;
@@ -2363,9 +2375,7 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
local->dynamic_ps_forced_timeout = timeout;
/* no change, but if automatic follow powersave */
- mutex_lock(&sdata->u.mgd.mtx);
__ieee80211_request_smps(sdata, sdata->u.mgd.req_smps);
- mutex_unlock(&sdata->u.mgd.mtx);
if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index 14abcf44f97..cafe614ef93 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -228,9 +228,9 @@ static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,
if (sdata->vif.type != NL80211_IFTYPE_STATION)
return -EOPNOTSUPP;
- mutex_lock(&sdata->u.mgd.mtx);
+ sdata_lock(sdata);
err = __ieee80211_request_smps(sdata, smps_mode);
- mutex_unlock(&sdata->u.mgd.mtx);
+ sdata_unlock(sdata);
return err;
}
@@ -313,16 +313,16 @@ static ssize_t ieee80211_if_parse_tkip_mic_test(
case NL80211_IFTYPE_STATION:
fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
/* BSSID SA DA */
- mutex_lock(&sdata->u.mgd.mtx);
+ sdata_lock(sdata);
if (!sdata->u.mgd.associated) {
- mutex_unlock(&sdata->u.mgd.mtx);
+ sdata_unlock(sdata);
dev_kfree_skb(skb);
return -ENOTCONN;
}
memcpy(hdr->addr1, sdata->u.mgd.associated->bssid, ETH_ALEN);
memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN);
memcpy(hdr->addr3, addr, ETH_ALEN);
- mutex_unlock(&sdata->u.mgd.mtx);
+ sdata_unlock(sdata);
break;
default:
dev_kfree_skb(skb);
@@ -471,6 +471,8 @@ __IEEE80211_IF_FILE_W(tsf);
IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC);
#ifdef CONFIG_MAC80211_MESH
+IEEE80211_IF_FILE(estab_plinks, u.mesh.estab_plinks, ATOMIC);
+
/* Mesh stats attributes */
IEEE80211_IF_FILE(fwded_mcast, u.mesh.mshstats.fwded_mcast, DEC);
IEEE80211_IF_FILE(fwded_unicast, u.mesh.mshstats.fwded_unicast, DEC);
@@ -480,7 +482,6 @@ IEEE80211_IF_FILE(dropped_frames_congestion,
u.mesh.mshstats.dropped_frames_congestion, DEC);
IEEE80211_IF_FILE(dropped_frames_no_route,
u.mesh.mshstats.dropped_frames_no_route, DEC);
-IEEE80211_IF_FILE(estab_plinks, u.mesh.estab_plinks, ATOMIC);
/* Mesh parameters */
IEEE80211_IF_FILE(dot11MeshMaxRetries,
@@ -583,6 +584,7 @@ static void add_wds_files(struct ieee80211_sub_if_data *sdata)
static void add_mesh_files(struct ieee80211_sub_if_data *sdata)
{
DEBUGFS_ADD_MODE(tsf, 0600);
+ DEBUGFS_ADD_MODE(estab_plinks, 0400);
}
static void add_mesh_stats(struct ieee80211_sub_if_data *sdata)
@@ -598,7 +600,6 @@ static void add_mesh_stats(struct ieee80211_sub_if_data *sdata)
MESHSTATS_ADD(dropped_frames_ttl);
MESHSTATS_ADD(dropped_frames_no_route);
MESHSTATS_ADD(dropped_frames_congestion);
- MESHSTATS_ADD(estab_plinks);
#undef MESHSTATS_ADD
}
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index af8cee06e4f..75dff338f58 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -429,9 +429,9 @@ void ieee80211_request_smps_work(struct work_struct *work)
container_of(work, struct ieee80211_sub_if_data,
u.mgd.request_smps_work);
- mutex_lock(&sdata->u.mgd.mtx);
+ sdata_lock(sdata);
__ieee80211_request_smps(sdata, sdata->u.mgd.driver_smps_mode);
- mutex_unlock(&sdata->u.mgd.mtx);
+ sdata_unlock(sdata);
}
void ieee80211_request_smps(struct ieee80211_vif *vif,
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 170f9a7fa31..caa4b4f7f6e 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -54,7 +54,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
struct beacon_data *presp;
int frame_len;
- lockdep_assert_held(&ifibss->mtx);
+ sdata_assert_lock(sdata);
/* Reset own TSF to allow time synchronization work. */
drv_reset_tsf(local, sdata);
@@ -74,7 +74,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
}
presp = rcu_dereference_protected(ifibss->presp,
- lockdep_is_held(&ifibss->mtx));
+ lockdep_is_held(&sdata->wdev.mtx));
rcu_assign_pointer(ifibss->presp, NULL);
if (presp)
kfree_rcu(presp, rcu_head);
@@ -263,7 +263,7 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
const struct cfg80211_bss_ies *ies;
u64 tsf;
- lockdep_assert_held(&sdata->u.ibss.mtx);
+ sdata_assert_lock(sdata);
if (beacon_int < 10)
beacon_int = 10;
@@ -341,6 +341,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_supported_band *sband;
int band;
/*
@@ -380,8 +381,9 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
sta->last_rx = jiffies;
/* make sure mandatory rates are always added */
+ sband = local->hw.wiphy->bands[band];
sta->sta.supp_rates[band] = supp_rates |
- ieee80211_mandatory_rates(local, band);
+ ieee80211_mandatory_rates(sband);
return ieee80211_ibss_finish_sta(sta, auth);
}
@@ -408,7 +410,7 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta;
u8 deauth_frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
- lockdep_assert_held(&sdata->u.ibss.mtx);
+ sdata_assert_lock(sdata);
if (len < 24 + 6)
return;
@@ -492,7 +494,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
prev_rates = sta->sta.supp_rates[band];
/* make sure mandatory rates are always added */
sta->sta.supp_rates[band] = supp_rates |
- ieee80211_mandatory_rates(local, band);
+ ieee80211_mandatory_rates(sband);
if (sta->sta.supp_rates[band] != prev_rates) {
ibss_dbg(sdata,
@@ -624,6 +626,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_supported_band *sband;
int band;
/*
@@ -658,8 +661,9 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
sta->last_rx = jiffies;
/* make sure mandatory rates are always added */
+ sband = local->hw.wiphy->bands[band];
sta->sta.supp_rates[band] = supp_rates |
- ieee80211_mandatory_rates(local, band);
+ ieee80211_mandatory_rates(sband);
spin_lock(&ifibss->incomplete_lock);
list_add(&sta->list, &ifibss->incomplete_stations);
@@ -673,7 +677,7 @@ static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
int active = 0;
struct sta_info *sta;
- lockdep_assert_held(&sdata->u.ibss.mtx);
+ sdata_assert_lock(sdata);
rcu_read_lock();
@@ -699,7 +703,7 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
- lockdep_assert_held(&ifibss->mtx);
+ sdata_assert_lock(sdata);
mod_timer(&ifibss->timer,
round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
@@ -730,7 +734,7 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
u16 capability;
int i;
- lockdep_assert_held(&ifibss->mtx);
+ sdata_assert_lock(sdata);
if (ifibss->fixed_bssid) {
memcpy(bssid, ifibss->bssid, ETH_ALEN);
@@ -773,7 +777,7 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
int active_ibss;
u16 capability;
- lockdep_assert_held(&ifibss->mtx);
+ sdata_assert_lock(sdata);
active_ibss = ieee80211_sta_active_ibss(sdata);
ibss_dbg(sdata, "sta_find_ibss (active_ibss=%d)\n", active_ibss);
@@ -843,10 +847,10 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
struct beacon_data *presp;
u8 *pos, *end;
- lockdep_assert_held(&ifibss->mtx);
+ sdata_assert_lock(sdata);
presp = rcu_dereference_protected(ifibss->presp,
- lockdep_is_held(&ifibss->mtx));
+ lockdep_is_held(&sdata->wdev.mtx));
if (ifibss->state != IEEE80211_IBSS_MLME_JOINED ||
len < 24 + 2 || !presp)
@@ -930,7 +934,7 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
mgmt = (struct ieee80211_mgmt *) skb->data;
fc = le16_to_cpu(mgmt->frame_control);
- mutex_lock(&sdata->u.ibss.mtx);
+ sdata_lock(sdata);
if (!sdata->u.ibss.ssid_len)
goto mgmt_out; /* not ready to merge yet */
@@ -953,7 +957,7 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
}
mgmt_out:
- mutex_unlock(&sdata->u.ibss.mtx);
+ sdata_unlock(sdata);
}
void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)
@@ -961,7 +965,7 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct sta_info *sta;
- mutex_lock(&ifibss->mtx);
+ sdata_lock(sdata);
/*
* Work could be scheduled after scan or similar
@@ -997,7 +1001,7 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)
}
out:
- mutex_unlock(&ifibss->mtx);
+ sdata_unlock(sdata);
}
static void ieee80211_ibss_timer(unsigned long data)
@@ -1014,7 +1018,6 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
setup_timer(&ifibss->timer, ieee80211_ibss_timer,
(unsigned long) sdata);
- mutex_init(&ifibss->mtx);
INIT_LIST_HEAD(&ifibss->incomplete_stations);
spin_lock_init(&ifibss->incomplete_lock);
}
@@ -1041,8 +1044,6 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
{
u32 changed = 0;
- mutex_lock(&sdata->u.ibss.mtx);
-
if (params->bssid) {
memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN);
sdata->u.ibss.fixed_bssid = true;
@@ -1075,8 +1076,6 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
memcpy(sdata->u.ibss.ssid, params->ssid, params->ssid_len);
sdata->u.ibss.ssid_len = params->ssid_len;
- mutex_unlock(&sdata->u.ibss.mtx);
-
/*
* 802.11n-2009 9.13.3.1: In an IBSS, the HT Protection field is
* reserved, but an HT STA shall protect HT transmissions as though
@@ -1112,8 +1111,6 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
struct sta_info *sta;
struct beacon_data *presp;
- mutex_lock(&sdata->u.ibss.mtx);
-
active_ibss = ieee80211_sta_active_ibss(sdata);
if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) {
@@ -1157,7 +1154,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
/* remove beacon */
kfree(sdata->u.ibss.ie);
presp = rcu_dereference_protected(ifibss->presp,
- lockdep_is_held(&sdata->u.ibss.mtx));
+ lockdep_is_held(&sdata->wdev.mtx));
RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
sdata->vif.bss_conf.ibss_joined = false;
sdata->vif.bss_conf.ibss_creator = false;
@@ -1173,7 +1170,5 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
del_timer_sync(&sdata->u.ibss.timer);
- mutex_unlock(&sdata->u.ibss.mtx);
-
return 0;
}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 158e6eb188d..9eed6f1d161 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -394,7 +394,6 @@ struct ieee80211_if_managed {
bool nullfunc_failed;
bool connection_loss;
- struct mutex mtx;
struct cfg80211_bss *associated;
struct ieee80211_mgd_auth_data *auth_data;
struct ieee80211_mgd_assoc_data *assoc_data;
@@ -488,8 +487,6 @@ struct ieee80211_if_managed {
struct ieee80211_if_ibss {
struct timer_list timer;
- struct mutex mtx;
-
unsigned long last_scan_completed;
u32 basic_rates;
@@ -580,8 +577,6 @@ struct ieee80211_if_mesh {
bool accepting_plinks;
int num_gates;
struct beacon_data __rcu *beacon;
- /* just protects beacon updates for now */
- struct mutex mtx;
const u8 *ie;
u8 ie_len;
enum {
@@ -778,6 +773,26 @@ struct ieee80211_sub_if_data *vif_to_sdata(struct ieee80211_vif *p)
return container_of(p, struct ieee80211_sub_if_data, vif);
}
+static inline void sdata_lock(struct ieee80211_sub_if_data *sdata)
+ __acquires(&sdata->wdev.mtx)
+{
+ mutex_lock(&sdata->wdev.mtx);
+ __acquire(&sdata->wdev.mtx);
+}
+
+static inline void sdata_unlock(struct ieee80211_sub_if_data *sdata)
+ __releases(&sdata->wdev.mtx)
+{
+ mutex_unlock(&sdata->wdev.mtx);
+ __release(&sdata->wdev.mtx);
+}
+
+static inline void
+sdata_assert_lock(struct ieee80211_sub_if_data *sdata)
+{
+ lockdep_assert_held(&sdata->wdev.mtx);
+}
+
static inline enum ieee80211_band
ieee80211_get_sdata_band(struct ieee80211_sub_if_data *sdata)
{
@@ -1267,6 +1282,7 @@ void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata);
void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata);
void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata,
__le16 fc, bool acked);
+void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
/* IBSS code */
void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
@@ -1505,9 +1521,6 @@ static inline void ieee802_11_parse_elems(u8 *start, size_t len, bool action,
ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0);
}
-u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
- enum ieee80211_band band);
-
void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
void ieee80211_dynamic_ps_timer(unsigned long data);
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 60f1ce5e5e5..ceef64426a8 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -474,6 +474,9 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
master->control_port_protocol;
sdata->control_port_no_encrypt =
master->control_port_no_encrypt;
+ sdata->vif.cab_queue = master->vif.cab_queue;
+ memcpy(sdata->vif.hw_queue, master->vif.hw_queue,
+ sizeof(sdata->vif.hw_queue));
break;
}
case NL80211_IFTYPE_AP:
@@ -653,7 +656,11 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
ieee80211_recalc_ps(local, -1);
- if (dev) {
+ if (sdata->vif.type == NL80211_IFTYPE_MONITOR ||
+ sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+ /* XXX: for AP_VLAN, actually track AP queues */
+ netif_tx_start_all_queues(dev);
+ } else if (dev) {
unsigned long flags;
int n_acs = IEEE80211_NUM_ACS;
int ac;
@@ -1696,6 +1703,15 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local)
ASSERT_RTNL();
+ /*
+ * Close all AP_VLAN interfaces first, as otherwise they
+ * might be closed while the AP interface they belong to
+ * is closed, causing unregister_netdevice_many() to crash.
+ */
+ list_for_each_entry(sdata, &local->interfaces, list)
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ dev_close(sdata->dev);
+
mutex_lock(&local->iflist_mtx);
list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
list_del(&sdata->list);
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 67059b88fea..e39cc91d0cf 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -335,12 +335,12 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
switch (cipher) {
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
- key->conf.iv_len = WEP_IV_LEN;
- key->conf.icv_len = WEP_ICV_LEN;
+ key->conf.iv_len = IEEE80211_WEP_IV_LEN;
+ key->conf.icv_len = IEEE80211_WEP_ICV_LEN;
break;
case WLAN_CIPHER_SUITE_TKIP:
- key->conf.iv_len = TKIP_IV_LEN;
- key->conf.icv_len = TKIP_ICV_LEN;
+ key->conf.iv_len = IEEE80211_TKIP_IV_LEN;
+ key->conf.icv_len = IEEE80211_TKIP_ICV_LEN;
if (seq) {
for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
key->u.tkip.rx[i].iv32 =
@@ -352,13 +352,13 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
spin_lock_init(&key->u.tkip.txlock);
break;
case WLAN_CIPHER_SUITE_CCMP:
- key->conf.iv_len = CCMP_HDR_LEN;
- key->conf.icv_len = CCMP_MIC_LEN;
+ key->conf.iv_len = IEEE80211_CCMP_HDR_LEN;
+ key->conf.icv_len = IEEE80211_CCMP_MIC_LEN;
if (seq) {
for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++)
- for (j = 0; j < CCMP_PN_LEN; j++)
+ for (j = 0; j < IEEE80211_CCMP_PN_LEN; j++)
key->u.ccmp.rx_pn[i][j] =
- seq[CCMP_PN_LEN - j - 1];
+ seq[IEEE80211_CCMP_PN_LEN - j - 1];
}
/*
* Initialize AES key state here as an optimization so that
@@ -375,9 +375,9 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
key->conf.iv_len = 0;
key->conf.icv_len = sizeof(struct ieee80211_mmie);
if (seq)
- for (j = 0; j < CMAC_PN_LEN; j++)
+ for (j = 0; j < IEEE80211_CMAC_PN_LEN; j++)
key->u.aes_cmac.rx_pn[j] =
- seq[CMAC_PN_LEN - j - 1];
+ seq[IEEE80211_CMAC_PN_LEN - j - 1];
/*
* Initialize AES key state here as an optimization so that
* it does not need to be initialized for every packet.
@@ -740,13 +740,13 @@ void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf,
pn = key->u.ccmp.rx_pn[IEEE80211_NUM_TIDS];
else
pn = key->u.ccmp.rx_pn[tid];
- memcpy(seq->ccmp.pn, pn, CCMP_PN_LEN);
+ memcpy(seq->ccmp.pn, pn, IEEE80211_CCMP_PN_LEN);
break;
case WLAN_CIPHER_SUITE_AES_CMAC:
if (WARN_ON(tid != 0))
return;
pn = key->u.aes_cmac.rx_pn;
- memcpy(seq->aes_cmac.pn, pn, CMAC_PN_LEN);
+ memcpy(seq->aes_cmac.pn, pn, IEEE80211_CMAC_PN_LEN);
break;
}
}
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index e8de3e6d780..036d57e76a5 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -19,17 +19,6 @@
#define NUM_DEFAULT_KEYS 4
#define NUM_DEFAULT_MGMT_KEYS 2
-#define WEP_IV_LEN 4
-#define WEP_ICV_LEN 4
-#define ALG_CCMP_KEY_LEN 16
-#define CCMP_HDR_LEN 8
-#define CCMP_MIC_LEN 8
-#define CCMP_TK_LEN 16
-#define CCMP_PN_LEN 6
-#define TKIP_IV_LEN 8
-#define TKIP_ICV_LEN 4
-#define CMAC_PN_LEN 6
-
struct ieee80211_local;
struct ieee80211_sub_if_data;
struct sta_info;
@@ -93,13 +82,13 @@ struct ieee80211_key {
* frames and the last counter is used with Robust
* Management frames.
*/
- u8 rx_pn[IEEE80211_NUM_TIDS + 1][CCMP_PN_LEN];
+ u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN];
struct crypto_cipher *tfm;
u32 replays; /* dot11RSNAStatsCCMPReplays */
} ccmp;
struct {
atomic64_t tx_pn;
- u8 rx_pn[CMAC_PN_LEN];
+ u8 rx_pn[IEEE80211_CMAC_PN_LEN];
struct crypto_cipher *tfm;
u32 replays; /* dot11RSNAStatsCMACReplays */
u32 icverrors; /* dot11RSNAStatsCMACICVErrors */
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 8a7bfc47d57..1998f147526 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -331,7 +331,7 @@ static int ieee80211_ifa_changed(struct notifier_block *nb,
return NOTIFY_DONE;
ifmgd = &sdata->u.mgd;
- mutex_lock(&ifmgd->mtx);
+ sdata_lock(sdata);
/* Copy the addresses to the bss_conf list */
ifa = idev->ifa_list;
@@ -349,7 +349,7 @@ static int ieee80211_ifa_changed(struct notifier_block *nb,
ieee80211_bss_info_change_notify(sdata,
BSS_CHANGED_ARP_FILTER);
- mutex_unlock(&ifmgd->mtx);
+ sdata_unlock(sdata);
return NOTIFY_DONE;
}
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 6952760881c..b3d1fdd4636 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -161,8 +161,11 @@ void mesh_sta_cleanup(struct sta_info *sta)
del_timer_sync(&sta->plink_timer);
}
- if (changed)
+ if (changed) {
+ sdata_lock(sdata);
ieee80211_mbss_info_change_notify(sdata, changed);
+ sdata_unlock(sdata);
+ }
}
int mesh_rmc_init(struct ieee80211_sub_if_data *sdata)
@@ -577,7 +580,9 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata)
mesh_path_expire(sdata);
changed = mesh_accept_plinks_update(sdata);
+ sdata_lock(sdata);
ieee80211_mbss_info_change_notify(sdata, changed);
+ sdata_unlock(sdata);
mod_timer(&ifmsh->housekeeping_timer,
round_jiffies(jiffies +
@@ -697,25 +702,21 @@ out_free:
}
static int
-ieee80211_mesh_rebuild_beacon(struct ieee80211_if_mesh *ifmsh)
+ieee80211_mesh_rebuild_beacon(struct ieee80211_sub_if_data *sdata)
{
struct beacon_data *old_bcn;
int ret;
- mutex_lock(&ifmsh->mtx);
-
- old_bcn = rcu_dereference_protected(ifmsh->beacon,
- lockdep_is_held(&ifmsh->mtx));
- ret = ieee80211_mesh_build_beacon(ifmsh);
+ old_bcn = rcu_dereference_protected(sdata->u.mesh.beacon,
+ lockdep_is_held(&sdata->wdev.mtx));
+ ret = ieee80211_mesh_build_beacon(&sdata->u.mesh);
if (ret)
/* just reuse old beacon */
- goto out;
+ return ret;
if (old_bcn)
kfree_rcu(old_bcn, rcu_head);
-out:
- mutex_unlock(&ifmsh->mtx);
- return ret;
+ return 0;
}
void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata,
@@ -726,7 +727,7 @@ void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata,
BSS_CHANGED_HT |
BSS_CHANGED_BASIC_RATES |
BSS_CHANGED_BEACON_INT)))
- if (ieee80211_mesh_rebuild_beacon(&sdata->u.mesh))
+ if (ieee80211_mesh_rebuild_beacon(sdata))
return;
ieee80211_bss_info_change_notify(sdata, changed);
}
@@ -741,6 +742,8 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
BSS_CHANGED_BASIC_RATES |
BSS_CHANGED_BEACON_INT;
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
+ struct ieee80211_supported_band *sband =
+ sdata->local->hw.wiphy->bands[band];
local->fif_other_bss++;
/* mesh ifaces must set allmulti to forward mcast traffic */
@@ -748,7 +751,6 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
ieee80211_configure_filter(local);
ifmsh->mesh_cc_id = 0; /* Disabled */
- ifmsh->mesh_auth_id = 0; /* Disabled */
/* register sync ops from extensible synchronization framework */
ifmsh->sync_ops = ieee80211_mesh_sync_ops_get(ifmsh->mesh_sp_id);
ifmsh->adjusting_tbtt = false;
@@ -759,8 +761,7 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
sdata->vif.bss_conf.ht_operation_mode =
ifmsh->mshcfg.ht_opmode;
sdata->vif.bss_conf.enable_beacon = true;
- sdata->vif.bss_conf.basic_rates =
- ieee80211_mandatory_rates(local, band);
+ sdata->vif.bss_conf.basic_rates = ieee80211_mandatory_rates(sband);
changed |= ieee80211_mps_local_status_update(sdata);
@@ -788,12 +789,12 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
sdata->vif.bss_conf.enable_beacon = false;
clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
- mutex_lock(&ifmsh->mtx);
+ sdata_lock(sdata);
bcn = rcu_dereference_protected(ifmsh->beacon,
- lockdep_is_held(&ifmsh->mtx));
+ lockdep_is_held(&sdata->wdev.mtx));
rcu_assign_pointer(ifmsh->beacon, NULL);
kfree_rcu(bcn, rcu_head);
- mutex_unlock(&ifmsh->mtx);
+ sdata_unlock(sdata);
/* flush STAs and mpaths on this iface */
sta_info_flush(sdata);
@@ -1041,7 +1042,6 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
spin_lock_init(&ifmsh->mesh_preq_queue_lock);
spin_lock_init(&ifmsh->sync_offset_lock);
RCU_INIT_POINTER(ifmsh->beacon, NULL);
- mutex_init(&ifmsh->mtx);
sdata->vif.bss_conf.bssid = zero_addr;
}
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 09bebed9941..6c4da99bc4f 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -517,7 +517,9 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
ieee80211_mps_frame_release(sta, elems);
out:
rcu_read_unlock();
+ sdata_lock(sdata);
ieee80211_mbss_info_change_notify(sdata, changed);
+ sdata_unlock(sdata);
}
static void mesh_plink_timer(unsigned long data)
@@ -1068,6 +1070,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
rcu_read_unlock();
- if (changed)
+ if (changed) {
+ sdata_lock(sdata);
ieee80211_mbss_info_change_notify(sdata, changed);
+ sdata_unlock(sdata);
+ }
}
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 29620bfc7a6..f44f4caa69e 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -91,41 +91,6 @@ MODULE_PARM_DESC(probe_wait_ms,
#define IEEE80211_SIGNAL_AVE_MIN_COUNT 4
/*
- * All cfg80211 functions have to be called outside a locked
- * section so that they can acquire a lock themselves... This
- * is much simpler than queuing up things in cfg80211, but we
- * do need some indirection for that here.
- */
-enum rx_mgmt_action {
- /* no action required */
- RX_MGMT_NONE,
-
- /* caller must call cfg80211_send_deauth() */
- RX_MGMT_CFG80211_DEAUTH,
-
- /* caller must call cfg80211_send_disassoc() */
- RX_MGMT_CFG80211_DISASSOC,
-
- /* caller must call cfg80211_send_rx_auth() */
- RX_MGMT_CFG80211_RX_AUTH,
-
- /* caller must call cfg80211_send_rx_assoc() */
- RX_MGMT_CFG80211_RX_ASSOC,
-
- /* caller must call cfg80211_send_assoc_timeout() */
- RX_MGMT_CFG80211_ASSOC_TIMEOUT,
-
- /* used when a processed beacon causes a deauth */
- RX_MGMT_CFG80211_TX_DEAUTH,
-};
-
-/* utils */
-static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd)
-{
- lockdep_assert_held(&ifmgd->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
@@ -135,13 +100,14 @@ static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd)
* has happened -- the work that runs from this timer will
* do that.
*/
-static void run_again(struct ieee80211_if_managed *ifmgd, unsigned long timeout)
+static void run_again(struct ieee80211_sub_if_data *sdata,
+ unsigned long timeout)
{
- ASSERT_MGD_MTX(ifmgd);
+ sdata_assert_lock(sdata);
- if (!timer_pending(&ifmgd->timer) ||
- time_before(timeout, ifmgd->timer.expires))
- mod_timer(&ifmgd->timer, timeout);
+ if (!timer_pending(&sdata->u.mgd.timer) ||
+ time_before(timeout, sdata->u.mgd.timer.expires))
+ mod_timer(&sdata->u.mgd.timer, timeout);
}
void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata)
@@ -652,7 +618,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
struct ieee80211_channel *chan;
u32 rates = 0;
- lockdep_assert_held(&ifmgd->mtx);
+ sdata_assert_lock(sdata);
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
@@ -962,7 +928,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
if (!ieee80211_sdata_running(sdata))
return;
- mutex_lock(&ifmgd->mtx);
+ sdata_lock(sdata);
if (!ifmgd->associated)
goto out;
@@ -985,7 +951,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
IEEE80211_QUEUE_STOP_REASON_CSA);
out:
ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
- mutex_unlock(&ifmgd->mtx);
+ sdata_unlock(sdata);
}
void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
@@ -1015,7 +981,8 @@ static void ieee80211_chswitch_timer(unsigned long data)
static void
ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
- u64 timestamp, struct ieee802_11_elems *elems)
+ u64 timestamp, struct ieee802_11_elems *elems,
+ bool beacon)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -1032,9 +999,10 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
struct cfg80211_chan_def new_vht_chandef = {};
const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
+ const struct ieee80211_ht_operation *ht_oper;
int secondary_channel_offset = -1;
- ASSERT_MGD_MTX(ifmgd);
+ sdata_assert_lock(sdata);
if (!cbss)
return;
@@ -1048,11 +1016,14 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
sec_chan_offs = elems->sec_chan_offs;
wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
+ ht_oper = elems->ht_operation;
if (ifmgd->flags & (IEEE80211_STA_DISABLE_HT |
IEEE80211_STA_DISABLE_40MHZ)) {
sec_chan_offs = NULL;
wide_bw_chansw_ie = NULL;
+ /* only used for bandwidth here */
+ ht_oper = NULL;
}
if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
@@ -1094,10 +1065,20 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
return;
}
- if (sec_chan_offs) {
+ if (!beacon && sec_chan_offs) {
secondary_channel_offset = sec_chan_offs->sec_chan_offs;
+ } else if (beacon && ht_oper) {
+ secondary_channel_offset =
+ ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
} else if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
- /* if HT is enabled and the IE not present, it's still HT */
+ /*
+ * If it's not a beacon, HT is enabled and the IE not present,
+ * it's 20 MHz, 802.11-2012 8.5.2.6:
+ * This element [the Secondary Channel Offset Element] is
+ * present when switching to a 40 MHz channel. It may be
+ * present when switching to a 20 MHz channel (in which
+ * case the secondary channel offset is set to SCN).
+ */
secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
}
@@ -1375,6 +1356,9 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata)
IEEE80211_STA_CONNECTION_POLL))
return false;
+ if (!sdata->vif.bss_conf.dtim_period)
+ return false;
+
rcu_read_lock();
sta = sta_info_get(sdata, mgd->bssid);
if (sta)
@@ -1827,7 +1811,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
u32 changed = 0;
- ASSERT_MGD_MTX(ifmgd);
+ sdata_assert_lock(sdata);
if (WARN_ON_ONCE(tx && !frame_buf))
return;
@@ -2036,7 +2020,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
}
ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
- run_again(ifmgd, ifmgd->probe_timeout);
+ run_again(sdata, ifmgd->probe_timeout);
if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
ieee80211_flush_queues(sdata->local, sdata);
}
@@ -2050,7 +2034,7 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
if (!ieee80211_sdata_running(sdata))
return;
- mutex_lock(&ifmgd->mtx);
+ sdata_lock(sdata);
if (!ifmgd->associated)
goto out;
@@ -2104,7 +2088,7 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
ifmgd->probe_send_count = 0;
ieee80211_mgd_probe_ap_send(sdata);
out:
- mutex_unlock(&ifmgd->mtx);
+ sdata_unlock(sdata);
}
struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
@@ -2120,7 +2104,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
return NULL;
- ASSERT_MGD_MTX(ifmgd);
+ sdata_assert_lock(sdata);
if (ifmgd->associated)
cbss = ifmgd->associated;
@@ -2153,9 +2137,9 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
- mutex_lock(&ifmgd->mtx);
+ sdata_lock(sdata);
if (!ifmgd->associated) {
- mutex_unlock(&ifmgd->mtx);
+ sdata_unlock(sdata);
return;
}
@@ -2166,13 +2150,9 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
ieee80211_wake_queues_by_reason(&sdata->local->hw,
IEEE80211_MAX_QUEUE_MAP,
IEEE80211_QUEUE_STOP_REASON_CSA);
- mutex_unlock(&ifmgd->mtx);
- /*
- * must be outside lock due to cfg80211,
- * but that's not a problem.
- */
cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
+ sdata_unlock(sdata);
}
static void ieee80211_beacon_connection_loss_work(struct work_struct *work)
@@ -2239,7 +2219,7 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data;
- lockdep_assert_held(&sdata->u.mgd.mtx);
+ sdata_assert_lock(sdata);
if (!assoc) {
sta_info_destroy_addr(sdata, auth_data->bss->bssid);
@@ -2280,27 +2260,26 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
auth_data->key_idx, tx_flags);
}
-static enum rx_mgmt_action __must_check
-ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt, size_t len)
+static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt, size_t len)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u8 bssid[ETH_ALEN];
u16 auth_alg, auth_transaction, status_code;
struct sta_info *sta;
- lockdep_assert_held(&ifmgd->mtx);
+ sdata_assert_lock(sdata);
if (len < 24 + 6)
- return RX_MGMT_NONE;
+ return;
if (!ifmgd->auth_data || ifmgd->auth_data->done)
- return RX_MGMT_NONE;
+ return;
memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN);
if (!ether_addr_equal(bssid, mgmt->bssid))
- return RX_MGMT_NONE;
+ return;
auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
@@ -2312,14 +2291,15 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
mgmt->sa, auth_alg, ifmgd->auth_data->algorithm,
auth_transaction,
ifmgd->auth_data->expected_transaction);
- return RX_MGMT_NONE;
+ return;
}
if (status_code != WLAN_STATUS_SUCCESS) {
sdata_info(sdata, "%pM denied authentication (status %d)\n",
mgmt->sa, status_code);
ieee80211_destroy_auth_data(sdata, false);
- return RX_MGMT_CFG80211_RX_AUTH;
+ cfg80211_send_rx_auth(sdata->dev, (u8 *)mgmt, len);
+ return;
}
switch (ifmgd->auth_data->algorithm) {
@@ -2332,20 +2312,20 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
if (ifmgd->auth_data->expected_transaction != 4) {
ieee80211_auth_challenge(sdata, mgmt, len);
/* need another frame */
- return RX_MGMT_NONE;
+ return;
}
break;
default:
WARN_ONCE(1, "invalid auth alg %d",
ifmgd->auth_data->algorithm);
- return RX_MGMT_NONE;
+ return;
}
sdata_info(sdata, "authenticated\n");
ifmgd->auth_data->done = true;
ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC;
ifmgd->auth_data->timeout_started = true;
- run_again(ifmgd, ifmgd->auth_data->timeout);
+ run_again(sdata, ifmgd->auth_data->timeout);
if (ifmgd->auth_data->algorithm == WLAN_AUTH_SAE &&
ifmgd->auth_data->expected_transaction != 2) {
@@ -2353,7 +2333,8 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
* Report auth frame to user space for processing since another
* round of Authentication frames is still needed.
*/
- return RX_MGMT_CFG80211_RX_AUTH;
+ cfg80211_send_rx_auth(sdata->dev, (u8 *)mgmt, len);
+ return;
}
/* move station state to auth */
@@ -2369,30 +2350,29 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
}
mutex_unlock(&sdata->local->sta_mtx);
- return RX_MGMT_CFG80211_RX_AUTH;
+ cfg80211_send_rx_auth(sdata->dev, (u8 *)mgmt, len);
+ return;
out_err:
mutex_unlock(&sdata->local->sta_mtx);
/* ignore frame -- wait for timeout */
- return RX_MGMT_NONE;
}
-static enum rx_mgmt_action __must_check
-ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt, size_t len)
+static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt, size_t len)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
const u8 *bssid = NULL;
u16 reason_code;
- lockdep_assert_held(&ifmgd->mtx);
+ sdata_assert_lock(sdata);
if (len < 24 + 2)
- return RX_MGMT_NONE;
+ return;
if (!ifmgd->associated ||
!ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
- return RX_MGMT_NONE;
+ return;
bssid = ifmgd->associated->bssid;
@@ -2403,25 +2383,24 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
- return RX_MGMT_CFG80211_DEAUTH;
+ cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, len);
}
-static enum rx_mgmt_action __must_check
-ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt, size_t len)
+static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt, size_t len)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u16 reason_code;
- lockdep_assert_held(&ifmgd->mtx);
+ sdata_assert_lock(sdata);
if (len < 24 + 2)
- return RX_MGMT_NONE;
+ return;
if (!ifmgd->associated ||
!ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
- return RX_MGMT_NONE;
+ return;
reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
@@ -2430,7 +2409,7 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
- return RX_MGMT_CFG80211_DISASSOC;
+ cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, len);
}
static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
@@ -2480,7 +2459,7 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
- lockdep_assert_held(&sdata->u.mgd.mtx);
+ sdata_assert_lock(sdata);
if (!assoc) {
sta_info_destroy_addr(sdata, assoc_data->bss->bssid);
@@ -2661,10 +2640,9 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
return true;
}
-static enum rx_mgmt_action __must_check
-ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt, size_t len,
- struct cfg80211_bss **bss)
+static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt,
+ size_t len)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
@@ -2672,13 +2650,14 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems elems;
u8 *pos;
bool reassoc;
+ struct cfg80211_bss *bss;
- lockdep_assert_held(&ifmgd->mtx);
+ sdata_assert_lock(sdata);
if (!assoc_data)
- return RX_MGMT_NONE;
+ return;
if (!ether_addr_equal(assoc_data->bss->bssid, mgmt->bssid))
- return RX_MGMT_NONE;
+ return;
/*
* AssocResp and ReassocResp have identical structure, so process both
@@ -2686,7 +2665,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
*/
if (len < 24 + 6)
- return RX_MGMT_NONE;
+ return;
reassoc = ieee80211_is_reassoc_req(mgmt->frame_control);
capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
@@ -2713,22 +2692,23 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
assoc_data->timeout = jiffies + msecs_to_jiffies(ms);
assoc_data->timeout_started = true;
if (ms > IEEE80211_ASSOC_TIMEOUT)
- run_again(ifmgd, assoc_data->timeout);
- return RX_MGMT_NONE;
+ run_again(sdata, assoc_data->timeout);
+ return;
}
- *bss = assoc_data->bss;
+ bss = assoc_data->bss;
if (status_code != WLAN_STATUS_SUCCESS) {
sdata_info(sdata, "%pM denied association (code=%d)\n",
mgmt->sa, status_code);
ieee80211_destroy_assoc_data(sdata, false);
} else {
- if (!ieee80211_assoc_success(sdata, *bss, mgmt, len)) {
+ if (!ieee80211_assoc_success(sdata, bss, mgmt, len)) {
/* oops -- internal error -- send timeout for now */
ieee80211_destroy_assoc_data(sdata, false);
- cfg80211_put_bss(sdata->local->hw.wiphy, *bss);
- return RX_MGMT_CFG80211_ASSOC_TIMEOUT;
+ cfg80211_put_bss(sdata->local->hw.wiphy, bss);
+ cfg80211_send_assoc_timeout(sdata->dev, mgmt->bssid);
+ return;
}
sdata_info(sdata, "associated\n");
@@ -2740,7 +2720,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
ieee80211_destroy_assoc_data(sdata, true);
}
- return RX_MGMT_CFG80211_RX_ASSOC;
+ cfg80211_send_rx_assoc(sdata->dev, bss, (u8 *)mgmt, len);
}
static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
@@ -2754,7 +2734,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *channel;
bool need_ps = false;
- lockdep_assert_held(&sdata->u.mgd.mtx);
+ sdata_assert_lock(sdata);
if ((sdata->u.mgd.associated &&
ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) ||
@@ -2796,7 +2776,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
mutex_unlock(&local->iflist_mtx);
}
- ieee80211_sta_process_chanswitch(sdata, rx_status->mactime, elems);
+ ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
+ elems, true);
}
@@ -2812,7 +2793,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
ifmgd = &sdata->u.mgd;
- ASSERT_MGD_MTX(ifmgd);
+ sdata_assert_lock(sdata);
if (!ether_addr_equal(mgmt->da, sdata->vif.addr))
return; /* ignore ProbeResp to foreign address */
@@ -2837,7 +2818,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
ifmgd->auth_data->tries = 0;
ifmgd->auth_data->timeout = jiffies;
ifmgd->auth_data->timeout_started = true;
- run_again(ifmgd, ifmgd->auth_data->timeout);
+ run_again(sdata, ifmgd->auth_data->timeout);
}
}
@@ -2862,10 +2843,9 @@ static const u64 care_about_ies =
(1ULL << WLAN_EID_HT_CAPABILITY) |
(1ULL << WLAN_EID_HT_OPERATION);
-static enum rx_mgmt_action
-ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt, size_t len,
- u8 *deauth_buf, struct ieee80211_rx_status *rx_status)
+static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt, size_t len,
+ struct ieee80211_rx_status *rx_status)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
@@ -2880,24 +2860,25 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
u8 erp_value = 0;
u32 ncrc;
u8 *bssid;
+ u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN];
- lockdep_assert_held(&ifmgd->mtx);
+ sdata_assert_lock(sdata);
/* Process beacon from the current BSS */
baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
if (baselen > len)
- return RX_MGMT_NONE;
+ return;
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (!chanctx_conf) {
rcu_read_unlock();
- return RX_MGMT_NONE;
+ return;
}
if (rx_status->freq != chanctx_conf->def.chan->center_freq) {
rcu_read_unlock();
- return RX_MGMT_NONE;
+ return;
}
chan = chanctx_conf->def.chan;
rcu_read_unlock();
@@ -2924,13 +2905,13 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
/* continue assoc process */
ifmgd->assoc_data->timeout = jiffies;
ifmgd->assoc_data->timeout_started = true;
- run_again(ifmgd, ifmgd->assoc_data->timeout);
- return RX_MGMT_NONE;
+ run_again(sdata, ifmgd->assoc_data->timeout);
+ return;
}
if (!ifmgd->associated ||
!ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
- return RX_MGMT_NONE;
+ return;
bssid = ifmgd->associated->bssid;
/* Track average RSSI from the Beacon frames of the current AP */
@@ -3076,7 +3057,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
}
if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
- return RX_MGMT_NONE;
+ return;
ifmgd->beacon_crc = ncrc;
ifmgd->beacon_crc_valid = true;
@@ -3110,6 +3091,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
}
changed |= BSS_CHANGED_DTIM_PERIOD;
+ ieee80211_recalc_ps_vif(sdata);
}
if (elems.erp_info) {
@@ -3131,7 +3113,9 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
WLAN_REASON_DEAUTH_LEAVING,
true, deauth_buf);
- return RX_MGMT_CFG80211_TX_DEAUTH;
+ cfg80211_send_deauth(sdata->dev, deauth_buf,
+ sizeof(deauth_buf));
+ return;
}
if (sta && elems.opmode_notif)
@@ -3148,19 +3132,13 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
elems.pwr_constr_elem);
ieee80211_bss_info_change_notify(sdata, changed);
-
- return RX_MGMT_NONE;
}
void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_rx_status *rx_status;
struct ieee80211_mgmt *mgmt;
- struct cfg80211_bss *bss = NULL;
- enum rx_mgmt_action rma = RX_MGMT_NONE;
- u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN];
u16 fc;
struct ieee802_11_elems elems;
int ies_len;
@@ -3169,28 +3147,27 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
mgmt = (struct ieee80211_mgmt *) skb->data;
fc = le16_to_cpu(mgmt->frame_control);
- mutex_lock(&ifmgd->mtx);
+ sdata_lock(sdata);
switch (fc & IEEE80211_FCTL_STYPE) {
case IEEE80211_STYPE_BEACON:
- rma = ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len,
- deauth_buf, rx_status);
+ ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, rx_status);
break;
case IEEE80211_STYPE_PROBE_RESP:
ieee80211_rx_mgmt_probe_resp(sdata, skb);
break;
case IEEE80211_STYPE_AUTH:
- rma = ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len);
+ ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len);
break;
case IEEE80211_STYPE_DEAUTH:
- rma = ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len);
+ ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len);
break;
case IEEE80211_STYPE_DISASSOC:
- rma = ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
+ ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
break;
case IEEE80211_STYPE_ASSOC_RESP:
case IEEE80211_STYPE_REASSOC_RESP:
- rma = ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len, &bss);
+ ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len);
break;
case IEEE80211_STYPE_ACTION:
if (mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT) {
@@ -3210,7 +3187,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
ieee80211_sta_process_chanswitch(sdata,
rx_status->mactime,
- &elems);
+ &elems, false);
} else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
ies_len = skb->len -
offsetof(struct ieee80211_mgmt,
@@ -3232,38 +3209,11 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
ieee80211_sta_process_chanswitch(sdata,
rx_status->mactime,
- &elems);
+ &elems, false);
}
break;
}
- mutex_unlock(&ifmgd->mtx);
-
- switch (rma) {
- case RX_MGMT_NONE:
- /* no action */
- break;
- case RX_MGMT_CFG80211_DEAUTH:
- cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
- break;
- case RX_MGMT_CFG80211_DISASSOC:
- cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len);
- break;
- case RX_MGMT_CFG80211_RX_AUTH:
- cfg80211_send_rx_auth(sdata->dev, (u8 *)mgmt, skb->len);
- break;
- case RX_MGMT_CFG80211_RX_ASSOC:
- cfg80211_send_rx_assoc(sdata->dev, bss, (u8 *)mgmt, skb->len);
- break;
- case RX_MGMT_CFG80211_ASSOC_TIMEOUT:
- cfg80211_send_assoc_timeout(sdata->dev, mgmt->bssid);
- break;
- case RX_MGMT_CFG80211_TX_DEAUTH:
- cfg80211_send_deauth(sdata->dev, deauth_buf,
- sizeof(deauth_buf));
- break;
- default:
- WARN(1, "unexpected: %d", rma);
- }
+ sdata_unlock(sdata);
}
static void ieee80211_sta_timer(unsigned long data)
@@ -3277,20 +3227,12 @@ static void ieee80211_sta_timer(unsigned long data)
static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
u8 *bssid, u8 reason, bool tx)
{
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
tx, frame_buf);
- mutex_unlock(&ifmgd->mtx);
- /*
- * must be outside lock due to cfg80211,
- * but that's not a problem.
- */
cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
-
- mutex_lock(&ifmgd->mtx);
}
static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
@@ -3300,15 +3242,11 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
struct ieee80211_mgd_auth_data *auth_data = ifmgd->auth_data;
u32 tx_flags = 0;
- lockdep_assert_held(&ifmgd->mtx);
+ sdata_assert_lock(sdata);
if (WARN_ON_ONCE(!auth_data))
return -EINVAL;
- if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
- tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
- IEEE80211_TX_INTFL_MLME_CONN_TX;
-
auth_data->tries++;
if (auth_data->tries > IEEE80211_AUTH_MAX_TRIES) {
@@ -3342,6 +3280,10 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
auth_data->expected_transaction = trans;
}
+ if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
+ tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
+ IEEE80211_TX_INTFL_MLME_CONN_TX;
+
ieee80211_send_auth(sdata, trans, auth_data->algorithm, status,
auth_data->data, auth_data->data_len,
auth_data->bss->bssid,
@@ -3365,15 +3307,15 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
* will not answer to direct packet in unassociated state.
*/
ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1],
- NULL, 0, (u32) -1, true, tx_flags,
+ NULL, 0, (u32) -1, true, 0,
auth_data->bss->channel, false);
rcu_read_unlock();
}
- if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
+ if (tx_flags == 0) {
auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
ifmgd->auth_data->timeout_started = true;
- run_again(ifmgd, auth_data->timeout);
+ run_again(sdata, auth_data->timeout);
} else {
auth_data->timeout_started = false;
}
@@ -3386,7 +3328,7 @@ static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata)
struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
struct ieee80211_local *local = sdata->local;
- lockdep_assert_held(&sdata->u.mgd.mtx);
+ sdata_assert_lock(sdata);
assoc_data->tries++;
if (assoc_data->tries > IEEE80211_ASSOC_MAX_TRIES) {
@@ -3410,7 +3352,7 @@ static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata)
if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
assoc_data->timeout_started = true;
- run_again(&sdata->u.mgd, assoc_data->timeout);
+ run_again(sdata, assoc_data->timeout);
} else {
assoc_data->timeout_started = false;
}
@@ -3435,7 +3377,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- mutex_lock(&ifmgd->mtx);
+ sdata_lock(sdata);
if (ifmgd->status_received) {
__le16 fc = ifmgd->status_fc;
@@ -3447,7 +3389,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
if (status_acked) {
ifmgd->auth_data->timeout =
jiffies + IEEE80211_AUTH_TIMEOUT_SHORT;
- run_again(ifmgd, ifmgd->auth_data->timeout);
+ run_again(sdata, ifmgd->auth_data->timeout);
} else {
ifmgd->auth_data->timeout = jiffies - 1;
}
@@ -3458,7 +3400,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
if (status_acked) {
ifmgd->assoc_data->timeout =
jiffies + IEEE80211_ASSOC_TIMEOUT_SHORT;
- run_again(ifmgd, ifmgd->assoc_data->timeout);
+ run_again(sdata, ifmgd->assoc_data->timeout);
} else {
ifmgd->assoc_data->timeout = jiffies - 1;
}
@@ -3481,12 +3423,10 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
ieee80211_destroy_auth_data(sdata, false);
- mutex_unlock(&ifmgd->mtx);
cfg80211_send_auth_timeout(sdata->dev, bssid);
- mutex_lock(&ifmgd->mtx);
}
} else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started)
- run_again(ifmgd, ifmgd->auth_data->timeout);
+ run_again(sdata, ifmgd->auth_data->timeout);
if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started &&
time_after(jiffies, ifmgd->assoc_data->timeout)) {
@@ -3499,12 +3439,10 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
ieee80211_destroy_assoc_data(sdata, false);
- mutex_unlock(&ifmgd->mtx);
cfg80211_send_assoc_timeout(sdata->dev, bssid);
- mutex_lock(&ifmgd->mtx);
}
} else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
- run_again(ifmgd, ifmgd->assoc_data->timeout);
+ run_again(sdata, ifmgd->assoc_data->timeout);
if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
IEEE80211_STA_CONNECTION_POLL) &&
@@ -3538,7 +3476,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
false);
}
} else if (time_is_after_jiffies(ifmgd->probe_timeout))
- run_again(ifmgd, ifmgd->probe_timeout);
+ run_again(sdata, ifmgd->probe_timeout);
else if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) {
mlme_dbg(sdata,
"Failed to send nullfunc to AP %pM after %dms, disconnecting\n",
@@ -3567,7 +3505,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
}
}
- mutex_unlock(&ifmgd->mtx);
+ sdata_unlock(sdata);
}
static void ieee80211_sta_bcn_mon_timer(unsigned long data)
@@ -3623,6 +3561,31 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
}
}
+#ifdef CONFIG_PM
+void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+ sdata_lock(sdata);
+ if (!ifmgd->associated) {
+ sdata_unlock(sdata);
+ return;
+ }
+
+ if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) {
+ sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME;
+ mlme_dbg(sdata, "driver requested disconnect after resume\n");
+ ieee80211_sta_connection_lost(sdata,
+ ifmgd->associated->bssid,
+ WLAN_REASON_UNSPECIFIED,
+ true);
+ sdata_unlock(sdata);
+ return;
+ }
+ sdata_unlock(sdata);
+}
+#endif
+
/* interface setup */
void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
{
@@ -3651,8 +3614,6 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
ifmgd->uapsd_max_sp_len = sdata->local->hw.uapsd_max_sp_len;
ifmgd->p2p_noa_index = -1;
- mutex_init(&ifmgd->mtx);
-
if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)
ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC;
else
@@ -4008,8 +3969,6 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
/* try to authenticate/probe */
- mutex_lock(&ifmgd->mtx);
-
if ((ifmgd->auth_data && !ifmgd->auth_data->done) ||
ifmgd->assoc_data) {
err = -EBUSY;
@@ -4029,8 +3988,8 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
WLAN_REASON_UNSPECIFIED,
false, frame_buf);
- __cfg80211_send_deauth(sdata->dev, frame_buf,
- sizeof(frame_buf));
+ cfg80211_send_deauth(sdata->dev, frame_buf,
+ sizeof(frame_buf));
}
sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid);
@@ -4047,8 +4006,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
/* hold our own reference */
cfg80211_ref_bss(local->hw.wiphy, auth_data->bss);
- err = 0;
- goto out_unlock;
+ return 0;
err_clear:
memset(ifmgd->bssid, 0, ETH_ALEN);
@@ -4056,9 +4014,6 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
ifmgd->auth_data = NULL;
err_free:
kfree(auth_data);
- out_unlock:
- mutex_unlock(&ifmgd->mtx);
-
return err;
}
@@ -4089,8 +4044,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
assoc_data->ssid_len = ssidie[1];
rcu_read_unlock();
- mutex_lock(&ifmgd->mtx);
-
if (ifmgd->associated) {
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
@@ -4098,8 +4051,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
WLAN_REASON_UNSPECIFIED,
false, frame_buf);
- __cfg80211_send_deauth(sdata->dev, frame_buf,
- sizeof(frame_buf));
+ cfg80211_send_deauth(sdata->dev, frame_buf,
+ sizeof(frame_buf));
}
if (ifmgd->auth_data && !ifmgd->auth_data->done) {
@@ -4293,7 +4246,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
}
rcu_read_unlock();
- run_again(ifmgd, assoc_data->timeout);
+ run_again(sdata, assoc_data->timeout);
if (bss->corrupt_data) {
char *corrupt_type = "data";
@@ -4309,17 +4262,13 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
corrupt_type);
}
- err = 0;
- goto out;
+ return 0;
err_clear:
memset(ifmgd->bssid, 0, ETH_ALEN);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
ifmgd->assoc_data = NULL;
err_free:
kfree(assoc_data);
- out:
- mutex_unlock(&ifmgd->mtx);
-
return err;
}
@@ -4329,9 +4278,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
bool tx = !req->local_state_change;
- bool sent_frame = false;
-
- mutex_lock(&ifmgd->mtx);
+ bool report_frame = false;
sdata_info(sdata,
"deauthenticating from %pM by local choice (reason=%d)\n",
@@ -4344,9 +4291,8 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
req->reason_code, tx,
frame_buf);
ieee80211_destroy_auth_data(sdata, false);
- mutex_unlock(&ifmgd->mtx);
- sent_frame = tx;
+ report_frame = true;
goto out;
}
@@ -4354,14 +4300,13 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
ether_addr_equal(ifmgd->associated->bssid, req->bssid)) {
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
req->reason_code, tx, frame_buf);
- sent_frame = tx;
+ report_frame = true;
}
- mutex_unlock(&ifmgd->mtx);
out:
- if (sent_frame)
- __cfg80211_send_deauth(sdata->dev, frame_buf,
- IEEE80211_DEAUTH_FRAME_LEN);
+ if (report_frame)
+ cfg80211_send_deauth(sdata->dev, frame_buf,
+ IEEE80211_DEAUTH_FRAME_LEN);
return 0;
}
@@ -4373,18 +4318,14 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
u8 bssid[ETH_ALEN];
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
- mutex_lock(&ifmgd->mtx);
-
/*
* cfg80211 should catch this ... but it's racy since
* we can receive a disassoc frame, process it, hand it
* to cfg80211 while that's in a locked section already
* trying to tell us that the user wants to disconnect.
*/
- if (ifmgd->associated != req->bss) {
- mutex_unlock(&ifmgd->mtx);
+ if (ifmgd->associated != req->bss)
return -ENOLINK;
- }
sdata_info(sdata,
"disassociating from %pM by local choice (reason=%d)\n",
@@ -4394,10 +4335,9 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DISASSOC,
req->reason_code, !req->local_state_change,
frame_buf);
- mutex_unlock(&ifmgd->mtx);
- __cfg80211_send_disassoc(sdata->dev, frame_buf,
- IEEE80211_DEAUTH_FRAME_LEN);
+ cfg80211_send_disassoc(sdata->dev, frame_buf,
+ IEEE80211_DEAUTH_FRAME_LEN);
return 0;
}
@@ -4417,13 +4357,13 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
cancel_work_sync(&ifmgd->csa_connection_drop_work);
cancel_work_sync(&ifmgd->chswitch_work);
- mutex_lock(&ifmgd->mtx);
+ sdata_lock(sdata);
if (ifmgd->assoc_data)
ieee80211_destroy_assoc_data(sdata, false);
if (ifmgd->auth_data)
ieee80211_destroy_auth_data(sdata, false);
del_timer_sync(&ifmgd->timer);
- mutex_unlock(&ifmgd->mtx);
+ sdata_unlock(sdata);
}
void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 0d51877efdb..d3f414fe67e 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -688,8 +688,15 @@ int rate_control_set_rates(struct ieee80211_hw *hw,
struct ieee80211_sta *pubsta,
struct ieee80211_sta_rates *rates)
{
- struct ieee80211_sta_rates *old = rcu_dereference(pubsta->rates);
+ struct ieee80211_sta_rates *old;
+ /*
+ * mac80211 guarantees that this function will not be called
+ * concurrently, so the following RCU access is safe, even without
+ * extra locking. This can not be checked easily, so we just set
+ * the condition to true.
+ */
+ old = rcu_dereference_protected(pubsta->rates, true);
rcu_assign_pointer(pubsta->rates, rates);
if (old)
kfree_rcu(old, rcu_head);
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index c8447af76ea..bdd7b4a719e 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -258,6 +258,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
pos += 2;
if (status->flag & RX_FLAG_HT) {
+ unsigned int stbc;
+
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS);
*pos++ = local->hw.radiotap_mcs_details;
*pos = 0;
@@ -267,6 +269,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
*pos |= IEEE80211_RADIOTAP_MCS_BW_40;
if (status->flag & RX_FLAG_HT_GF)
*pos |= IEEE80211_RADIOTAP_MCS_FMT_GF;
+ stbc = (status->flag & RX_FLAG_STBC_MASK) >> RX_FLAG_STBC_SHIFT;
+ *pos |= stbc << IEEE80211_RADIOTAP_MCS_STBC_SHIFT;
pos++;
*pos++ = status->rate_idx;
}
@@ -1372,6 +1376,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
struct sk_buff *skb = rx->skb;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ int i;
if (!sta)
return RX_CONTINUE;
@@ -1422,6 +1427,19 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
ewma_add(&sta->avg_signal, -status->signal);
}
+ if (status->chains) {
+ sta->chains = status->chains;
+ for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) {
+ int signal = status->chain_signal[i];
+
+ if (!(status->chains & BIT(i)))
+ continue;
+
+ sta->chain_signal_last[i] = signal;
+ ewma_add(&sta->chain_signal_avg[i], -signal);
+ }
+ }
+
/*
* Change STA power saving mode only at the end of a frame
* exchange sequence.
@@ -1608,7 +1626,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
entry->ccmp = 1;
memcpy(entry->last_pn,
rx->key->u.ccmp.rx_pn[queue],
- CCMP_PN_LEN);
+ IEEE80211_CCMP_PN_LEN);
}
return RX_QUEUED;
}
@@ -1627,21 +1645,21 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
* (IEEE 802.11i, 8.3.3.4.5) */
if (entry->ccmp) {
int i;
- u8 pn[CCMP_PN_LEN], *rpn;
+ u8 pn[IEEE80211_CCMP_PN_LEN], *rpn;
int queue;
if (!rx->key || rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP)
return RX_DROP_UNUSABLE;
- memcpy(pn, entry->last_pn, CCMP_PN_LEN);
- for (i = CCMP_PN_LEN - 1; i >= 0; i--) {
+ memcpy(pn, entry->last_pn, IEEE80211_CCMP_PN_LEN);
+ for (i = IEEE80211_CCMP_PN_LEN - 1; i >= 0; i--) {
pn[i]++;
if (pn[i])
break;
}
queue = rx->security_idx;
rpn = rx->key->u.ccmp.rx_pn[queue];
- if (memcmp(pn, rpn, CCMP_PN_LEN))
+ if (memcmp(pn, rpn, IEEE80211_CCMP_PN_LEN))
return RX_DROP_UNUSABLE;
- memcpy(entry->last_pn, pn, CCMP_PN_LEN);
+ memcpy(entry->last_pn, pn, IEEE80211_CCMP_PN_LEN);
}
skb_pull(rx->skb, ieee80211_hdrlen(fc));
@@ -3036,6 +3054,9 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,
* and location updates. Note that mac80211
* itself never looks at these frames.
*/
+ if (!multicast &&
+ !ether_addr_equal(sdata->vif.addr, hdr->addr1))
+ return 0;
if (ieee80211_is_public_action(hdr, skb->len))
return 1;
if (!ieee80211_is_beacon(hdr->frame_control))
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 11216bc13b2..a04c5671d7f 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -358,6 +358,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
do_posix_clock_monotonic_gettime(&uptime);
sta->last_connected = uptime.tv_sec;
ewma_init(&sta->avg_signal, 1024, 8);
+ for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++)
+ ewma_init(&sta->chain_signal_avg[i], 1024, 8);
if (sta_prepare_rate_control(local, sta, gfp)) {
kfree(sta);
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index adc30045f99..41c28b977f7 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -344,6 +344,11 @@ struct sta_info {
int last_signal;
struct ewma avg_signal;
int last_ack_signal;
+
+ u8 chains;
+ s8 chain_signal_last[IEEE80211_MAX_CHAINS];
+ struct ewma chain_signal_avg[IEEE80211_MAX_CHAINS];
+
/* Plus 1 for non-QoS frames */
__le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1];
diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c
index 3ed801d90f1..124b1fdc20d 100644
--- a/net/mac80211/tkip.c
+++ b/net/mac80211/tkip.c
@@ -208,10 +208,10 @@ void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf,
u32 iv32 = get_unaligned_le32(&data[4]);
u16 iv16 = data[2] | (data[0] << 8);
- spin_lock_bh(&key->u.tkip.txlock);
+ spin_lock(&key->u.tkip.txlock);
ieee80211_compute_tkip_p1k(key, iv32);
tkip_mixing_phase2(tk, ctx, iv16, p2k);
- spin_unlock_bh(&key->u.tkip.txlock);
+ spin_unlock(&key->u.tkip.txlock);
}
EXPORT_SYMBOL(ieee80211_get_tkip_p2k);
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 3f87fa468b1..2a8d759324c 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1072,32 +1072,6 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
ieee80211_set_wmm_default(sdata, true);
}
-u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
- enum ieee80211_band band)
-{
- struct ieee80211_supported_band *sband;
- struct ieee80211_rate *bitrates;
- u32 mandatory_rates;
- enum ieee80211_rate_flags mandatory_flag;
- int i;
-
- sband = local->hw.wiphy->bands[band];
- if (WARN_ON(!sband))
- return 1;
-
- if (band == IEEE80211_BAND_2GHZ)
- mandatory_flag = IEEE80211_RATE_MANDATORY_B;
- else
- mandatory_flag = IEEE80211_RATE_MANDATORY_A;
-
- bitrates = sband->bitrates;
- mandatory_rates = 0;
- for (i = 0; i < sband->n_bitrates; i++)
- if (bitrates[i].flags & mandatory_flag)
- mandatory_rates |= BIT(i);
- return mandatory_rates;
-}
-
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
u16 transaction, u16 auth_alg, u16 status,
const u8 *extra, size_t extra_len, const u8 *da,
@@ -1607,9 +1581,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
if (sdata->u.mgd.dtim_period)
changed |= BSS_CHANGED_DTIM_PERIOD;
- mutex_lock(&sdata->u.mgd.mtx);
+ sdata_lock(sdata);
ieee80211_bss_info_change_notify(sdata, changed);
- mutex_unlock(&sdata->u.mgd.mtx);
+ sdata_unlock(sdata);
break;
case NL80211_IFTYPE_ADHOC:
changed |= BSS_CHANGED_IBSS;
@@ -1740,6 +1714,13 @@ int ieee80211_reconfig(struct ieee80211_local *local)
mb();
local->resuming = false;
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (!ieee80211_sdata_running(sdata))
+ continue;
+ if (sdata->vif.type == NL80211_IFTYPE_STATION)
+ ieee80211_sta_restart(sdata);
+ }
+
mod_timer(&local->sta_cleanup, jiffies + 1);
#else
WARN_ON(1);
diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c
index c04d401dae9..6ee2b586357 100644
--- a/net/mac80211/wep.c
+++ b/net/mac80211/wep.c
@@ -28,7 +28,7 @@
int ieee80211_wep_init(struct ieee80211_local *local)
{
/* start WEP IV from a random value */
- get_random_bytes(&local->wep_iv, WEP_IV_LEN);
+ get_random_bytes(&local->wep_iv, IEEE80211_WEP_IV_LEN);
local->wep_tx_tfm = crypto_alloc_cipher("arc4", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(local->wep_tx_tfm)) {
@@ -98,20 +98,21 @@ static u8 *ieee80211_wep_add_iv(struct ieee80211_local *local,
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
- if (WARN_ON(skb_tailroom(skb) < WEP_ICV_LEN ||
- skb_headroom(skb) < WEP_IV_LEN))
+ if (WARN_ON(skb_tailroom(skb) < IEEE80211_WEP_ICV_LEN ||
+ skb_headroom(skb) < IEEE80211_WEP_IV_LEN))
return NULL;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
- newhdr = skb_push(skb, WEP_IV_LEN);
- memmove(newhdr, newhdr + WEP_IV_LEN, hdrlen);
+ newhdr = skb_push(skb, IEEE80211_WEP_IV_LEN);
+ memmove(newhdr, newhdr + IEEE80211_WEP_IV_LEN, hdrlen);
/* the HW only needs room for the IV, but not the actual IV */
if (info->control.hw_key &&
(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))
return newhdr + hdrlen;
- skb_set_network_header(skb, skb_network_offset(skb) + WEP_IV_LEN);
+ skb_set_network_header(skb, skb_network_offset(skb) +
+ IEEE80211_WEP_IV_LEN);
ieee80211_wep_get_iv(local, keylen, keyidx, newhdr + hdrlen);
return newhdr + hdrlen;
}
@@ -125,8 +126,8 @@ static void ieee80211_wep_remove_iv(struct ieee80211_local *local,
unsigned int hdrlen;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
- memmove(skb->data + WEP_IV_LEN, skb->data, hdrlen);
- skb_pull(skb, WEP_IV_LEN);
+ memmove(skb->data + IEEE80211_WEP_IV_LEN, skb->data, hdrlen);
+ skb_pull(skb, IEEE80211_WEP_IV_LEN);
}
@@ -146,7 +147,7 @@ int ieee80211_wep_encrypt_data(struct crypto_cipher *tfm, u8 *rc4key,
put_unaligned(icv, (__le32 *)(data + data_len));
crypto_cipher_setkey(tfm, rc4key, klen);
- for (i = 0; i < data_len + WEP_ICV_LEN; i++)
+ for (i = 0; i < data_len + IEEE80211_WEP_ICV_LEN; i++)
crypto_cipher_encrypt_one(tfm, data + i, data + i);
return 0;
@@ -172,7 +173,7 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local,
if (!iv)
return -1;
- len = skb->len - (iv + WEP_IV_LEN - skb->data);
+ len = skb->len - (iv + IEEE80211_WEP_IV_LEN - skb->data);
/* Prepend 24-bit IV to RC4 key */
memcpy(rc4key, iv, 3);
@@ -181,10 +182,10 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local,
memcpy(rc4key + 3, key, keylen);
/* Add room for ICV */
- skb_put(skb, WEP_ICV_LEN);
+ skb_put(skb, IEEE80211_WEP_ICV_LEN);
return ieee80211_wep_encrypt_data(local->wep_tx_tfm, rc4key, keylen + 3,
- iv + WEP_IV_LEN, len);
+ iv + IEEE80211_WEP_IV_LEN, len);
}
@@ -201,11 +202,11 @@ int ieee80211_wep_decrypt_data(struct crypto_cipher *tfm, u8 *rc4key,
return -1;
crypto_cipher_setkey(tfm, rc4key, klen);
- for (i = 0; i < data_len + WEP_ICV_LEN; i++)
+ for (i = 0; i < data_len + IEEE80211_WEP_ICV_LEN; i++)
crypto_cipher_decrypt_one(tfm, data + i, data + i);
crc = cpu_to_le32(~crc32_le(~0, data, data_len));
- if (memcmp(&crc, data + data_len, WEP_ICV_LEN) != 0)
+ if (memcmp(&crc, data + data_len, IEEE80211_WEP_ICV_LEN) != 0)
/* ICV mismatch */
return -1;
@@ -237,10 +238,10 @@ static int ieee80211_wep_decrypt(struct ieee80211_local *local,
return -1;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
- if (skb->len < hdrlen + WEP_IV_LEN + WEP_ICV_LEN)
+ if (skb->len < hdrlen + IEEE80211_WEP_IV_LEN + IEEE80211_WEP_ICV_LEN)
return -1;
- len = skb->len - hdrlen - WEP_IV_LEN - WEP_ICV_LEN;
+ len = skb->len - hdrlen - IEEE80211_WEP_IV_LEN - IEEE80211_WEP_ICV_LEN;
keyidx = skb->data[hdrlen + 3] >> 6;
@@ -256,16 +257,16 @@ static int ieee80211_wep_decrypt(struct ieee80211_local *local,
memcpy(rc4key + 3, key->conf.key, key->conf.keylen);
if (ieee80211_wep_decrypt_data(local->wep_rx_tfm, rc4key, klen,
- skb->data + hdrlen + WEP_IV_LEN,
- len))
+ skb->data + hdrlen +
+ IEEE80211_WEP_IV_LEN, len))
ret = -1;
/* Trim ICV */
- skb_trim(skb, skb->len - WEP_ICV_LEN);
+ skb_trim(skb, skb->len - IEEE80211_WEP_ICV_LEN);
/* Remove IV */
- memmove(skb->data + WEP_IV_LEN, skb->data, hdrlen);
- skb_pull(skb, WEP_IV_LEN);
+ memmove(skb->data + IEEE80211_WEP_IV_LEN, skb->data, hdrlen);
+ skb_pull(skb, IEEE80211_WEP_IV_LEN);
return ret;
}
@@ -305,13 +306,14 @@ ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx)
if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key))
return RX_DROP_UNUSABLE;
} else if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
- if (!pskb_may_pull(rx->skb, ieee80211_hdrlen(fc) + WEP_IV_LEN))
+ if (!pskb_may_pull(rx->skb, ieee80211_hdrlen(fc) +
+ IEEE80211_WEP_IV_LEN))
return RX_DROP_UNUSABLE;
if (rx->sta && ieee80211_wep_is_weak_iv(rx->skb, rx->key))
rx->sta->wep_weak_iv_count++;
ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
/* remove ICV */
- if (pskb_trim(rx->skb, rx->skb->len - WEP_ICV_LEN))
+ if (pskb_trim(rx->skb, rx->skb->len - IEEE80211_WEP_ICV_LEN))
return RX_DROP_UNUSABLE;
}
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index c7c6d644486..c9edfcb7a13 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -62,10 +62,10 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)
tail = MICHAEL_MIC_LEN;
if (!info->control.hw_key)
- tail += TKIP_ICV_LEN;
+ tail += IEEE80211_TKIP_ICV_LEN;
if (WARN_ON(skb_tailroom(skb) < tail ||
- skb_headroom(skb) < TKIP_IV_LEN))
+ skb_headroom(skb) < IEEE80211_TKIP_IV_LEN))
return TX_DROP;
key = &tx->key->conf.key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY];
@@ -198,15 +198,16 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
if (info->control.hw_key)
tail = 0;
else
- tail = TKIP_ICV_LEN;
+ tail = IEEE80211_TKIP_ICV_LEN;
if (WARN_ON(skb_tailroom(skb) < tail ||
- skb_headroom(skb) < TKIP_IV_LEN))
+ skb_headroom(skb) < IEEE80211_TKIP_IV_LEN))
return -1;
- pos = skb_push(skb, TKIP_IV_LEN);
- memmove(pos, pos + TKIP_IV_LEN, hdrlen);
- skb_set_network_header(skb, skb_network_offset(skb) + TKIP_IV_LEN);
+ pos = skb_push(skb, IEEE80211_TKIP_IV_LEN);
+ memmove(pos, pos + IEEE80211_TKIP_IV_LEN, hdrlen);
+ skb_set_network_header(skb, skb_network_offset(skb) +
+ IEEE80211_TKIP_IV_LEN);
pos += hdrlen;
/* the HW only needs room for the IV, but not the actual IV */
@@ -227,7 +228,7 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
return 0;
/* Add room for ICV */
- skb_put(skb, TKIP_ICV_LEN);
+ skb_put(skb, IEEE80211_TKIP_ICV_LEN);
return ieee80211_tkip_encrypt_data(tx->local->wep_tx_tfm,
key, skb, pos, len);
@@ -290,11 +291,11 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
return RX_DROP_UNUSABLE;
/* Trim ICV */
- skb_trim(skb, skb->len - TKIP_ICV_LEN);
+ skb_trim(skb, skb->len - IEEE80211_TKIP_ICV_LEN);
/* Remove IV */
- memmove(skb->data + TKIP_IV_LEN, skb->data, hdrlen);
- skb_pull(skb, TKIP_IV_LEN);
+ memmove(skb->data + IEEE80211_TKIP_IV_LEN, skb->data, hdrlen);
+ skb_pull(skb, IEEE80211_TKIP_IV_LEN);
return RX_CONTINUE;
}
@@ -337,9 +338,9 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,
else
qos_tid = 0;
- data_len = skb->len - hdrlen - CCMP_HDR_LEN;
+ data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN;
if (encrypted)
- data_len -= CCMP_MIC_LEN;
+ data_len -= IEEE80211_CCMP_MIC_LEN;
/* First block, b_0 */
b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */
@@ -348,7 +349,7 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,
*/
b_0[1] = qos_tid | (mgmt << 4);
memcpy(&b_0[2], hdr->addr2, ETH_ALEN);
- memcpy(&b_0[8], pn, CCMP_PN_LEN);
+ memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN);
/* l(m) */
put_unaligned_be16(data_len, &b_0[14]);
@@ -424,15 +425,16 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
if (info->control.hw_key)
tail = 0;
else
- tail = CCMP_MIC_LEN;
+ tail = IEEE80211_CCMP_MIC_LEN;
if (WARN_ON(skb_tailroom(skb) < tail ||
- skb_headroom(skb) < CCMP_HDR_LEN))
+ skb_headroom(skb) < IEEE80211_CCMP_HDR_LEN))
return -1;
- pos = skb_push(skb, CCMP_HDR_LEN);
- memmove(pos, pos + CCMP_HDR_LEN, hdrlen);
- skb_set_network_header(skb, skb_network_offset(skb) + CCMP_HDR_LEN);
+ pos = skb_push(skb, IEEE80211_CCMP_HDR_LEN);
+ memmove(pos, pos + IEEE80211_CCMP_HDR_LEN, hdrlen);
+ skb_set_network_header(skb, skb_network_offset(skb) +
+ IEEE80211_CCMP_HDR_LEN);
/* the HW only needs room for the IV, but not the actual IV */
if (info->control.hw_key &&
@@ -457,10 +459,10 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
if (info->control.hw_key)
return 0;
- pos += CCMP_HDR_LEN;
+ pos += IEEE80211_CCMP_HDR_LEN;
ccmp_special_blocks(skb, pn, scratch, 0);
ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, pos, len,
- pos, skb_put(skb, CCMP_MIC_LEN));
+ pos, skb_put(skb, IEEE80211_CCMP_MIC_LEN));
return 0;
}
@@ -490,7 +492,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
struct ieee80211_key *key = rx->key;
struct sk_buff *skb = rx->skb;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
- u8 pn[CCMP_PN_LEN];
+ u8 pn[IEEE80211_CCMP_PN_LEN];
int data_len;
int queue;
@@ -500,12 +502,13 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
!ieee80211_is_robust_mgmt_frame(hdr))
return RX_CONTINUE;
- data_len = skb->len - hdrlen - CCMP_HDR_LEN - CCMP_MIC_LEN;
+ data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN -
+ IEEE80211_CCMP_MIC_LEN;
if (!rx->sta || data_len < 0)
return RX_DROP_UNUSABLE;
if (status->flag & RX_FLAG_DECRYPTED) {
- if (!pskb_may_pull(rx->skb, hdrlen + CCMP_HDR_LEN))
+ if (!pskb_may_pull(rx->skb, hdrlen + IEEE80211_CCMP_HDR_LEN))
return RX_DROP_UNUSABLE;
} else {
if (skb_linearize(rx->skb))
@@ -516,7 +519,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
queue = rx->security_idx;
- if (memcmp(pn, key->u.ccmp.rx_pn[queue], CCMP_PN_LEN) <= 0) {
+ if (memcmp(pn, key->u.ccmp.rx_pn[queue], IEEE80211_CCMP_PN_LEN) <= 0) {
key->u.ccmp.replays++;
return RX_DROP_UNUSABLE;
}
@@ -528,19 +531,20 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
if (ieee80211_aes_ccm_decrypt(
key->u.ccmp.tfm, scratch,
- skb->data + hdrlen + CCMP_HDR_LEN, data_len,
- skb->data + skb->len - CCMP_MIC_LEN,
- skb->data + hdrlen + CCMP_HDR_LEN))
+ skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
+ data_len,
+ skb->data + skb->len - IEEE80211_CCMP_MIC_LEN,
+ skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN))
return RX_DROP_UNUSABLE;
}
- memcpy(key->u.ccmp.rx_pn[queue], pn, CCMP_PN_LEN);
+ memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN);
/* Remove CCMP header and MIC */
- if (pskb_trim(skb, skb->len - CCMP_MIC_LEN))
+ if (pskb_trim(skb, skb->len - IEEE80211_CCMP_MIC_LEN))
return RX_DROP_UNUSABLE;
- memmove(skb->data + CCMP_HDR_LEN, skb->data, hdrlen);
- skb_pull(skb, CCMP_HDR_LEN);
+ memmove(skb->data + IEEE80211_CCMP_HDR_LEN, skb->data, hdrlen);
+ skb_pull(skb, IEEE80211_CCMP_HDR_LEN);
return RX_CONTINUE;
}
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 84c9ad7e1dc..41cec1776f4 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -34,13 +34,12 @@
MODULE_AUTHOR("Johannes Berg");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("wireless configuration support");
+MODULE_ALIAS_GENL_FAMILY(NL80211_GENL_NAME);
-/* RCU-protected (and cfg80211_mutex for writers) */
+/* RCU-protected (and RTNL for writers) */
LIST_HEAD(cfg80211_rdev_list);
int cfg80211_rdev_list_generation;
-DEFINE_MUTEX(cfg80211_mutex);
-
/* for debugfs */
static struct dentry *ieee80211_debugfs_dir;
@@ -52,12 +51,11 @@ module_param(cfg80211_disable_40mhz_24ghz, bool, 0644);
MODULE_PARM_DESC(cfg80211_disable_40mhz_24ghz,
"Disable 40MHz support in the 2.4GHz band");
-/* requires cfg80211_mutex to be held! */
struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx)
{
struct cfg80211_registered_device *result = NULL, *rdev;
- assert_cfg80211_lock();
+ ASSERT_RTNL();
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
if (rdev->wiphy_idx == wiphy_idx) {
@@ -76,12 +74,11 @@ int get_wiphy_idx(struct wiphy *wiphy)
return rdev->wiphy_idx;
}
-/* requires cfg80211_rdev_mutex to be held! */
struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx)
{
struct cfg80211_registered_device *rdev;
- assert_cfg80211_lock();
+ ASSERT_RTNL();
rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx);
if (!rdev)
@@ -89,35 +86,13 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx)
return &rdev->wiphy;
}
-struct cfg80211_registered_device *
-cfg80211_get_dev_from_ifindex(struct net *net, int ifindex)
-{
- struct cfg80211_registered_device *rdev = ERR_PTR(-ENODEV);
- struct net_device *dev;
-
- mutex_lock(&cfg80211_mutex);
- dev = dev_get_by_index(net, ifindex);
- if (!dev)
- goto out;
- if (dev->ieee80211_ptr) {
- rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy);
- mutex_lock(&rdev->mtx);
- } else
- rdev = ERR_PTR(-ENODEV);
- dev_put(dev);
- out:
- mutex_unlock(&cfg80211_mutex);
- return rdev;
-}
-
-/* requires cfg80211_mutex to be held */
int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
char *newname)
{
struct cfg80211_registered_device *rdev2;
int wiphy_idx, taken = -1, result, digits;
- assert_cfg80211_lock();
+ ASSERT_RTNL();
/* prohibit calling the thing phy%d when %d is not its number */
sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken);
@@ -215,8 +190,7 @@ static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data)
void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev)
{
- lockdep_assert_held(&rdev->devlist_mtx);
- lockdep_assert_held(&rdev->sched_scan_mtx);
+ ASSERT_RTNL();
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_P2P_DEVICE))
return;
@@ -230,18 +204,15 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
rdev->opencount--;
if (rdev->scan_req && rdev->scan_req->wdev == wdev) {
- bool busy = work_busy(&rdev->scan_done_wk);
-
/*
- * If the work isn't pending or running (in which case it would
- * be waiting for the lock we hold) the driver didn't properly
- * cancel the scan when the interface was removed. In this case
- * warn and leak the scan request object to not crash later.
+ * If the scan request wasn't notified as done, set it
+ * to aborted and leak it after a warning. The driver
+ * should have notified us that it ended at the latest
+ * during rdev_stop_p2p_device().
*/
- WARN_ON(!busy);
-
- rdev->scan_req->aborted = true;
- ___cfg80211_scan_done(rdev, !busy);
+ if (WARN_ON(!rdev->scan_req->notified))
+ rdev->scan_req->aborted = true;
+ ___cfg80211_scan_done(rdev, !rdev->scan_req->notified);
}
}
@@ -255,8 +226,6 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked)
rtnl_lock();
- /* read-only iteration need not hold the devlist_mtx */
-
list_for_each_entry(wdev, &rdev->wdev_list, list) {
if (wdev->netdev) {
dev_close(wdev->netdev);
@@ -265,12 +234,7 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked)
/* otherwise, check iftype */
switch (wdev->iftype) {
case NL80211_IFTYPE_P2P_DEVICE:
- /* but this requires it */
- mutex_lock(&rdev->devlist_mtx);
- mutex_lock(&rdev->sched_scan_mtx);
cfg80211_stop_p2p_device(rdev, wdev);
- mutex_unlock(&rdev->sched_scan_mtx);
- mutex_unlock(&rdev->devlist_mtx);
break;
default:
break;
@@ -298,10 +262,7 @@ static void cfg80211_event_work(struct work_struct *work)
event_work);
rtnl_lock();
- cfg80211_lock_rdev(rdev);
-
cfg80211_process_rdev_events(rdev);
- cfg80211_unlock_rdev(rdev);
rtnl_unlock();
}
@@ -309,7 +270,7 @@ static void cfg80211_event_work(struct work_struct *work)
struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
{
- static int wiphy_counter;
+ static atomic_t wiphy_counter = ATOMIC_INIT(0);
struct cfg80211_registered_device *rdev;
int alloc_size;
@@ -331,26 +292,18 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
rdev->ops = ops;
- mutex_lock(&cfg80211_mutex);
-
- rdev->wiphy_idx = wiphy_counter++;
+ rdev->wiphy_idx = atomic_inc_return(&wiphy_counter);
if (unlikely(rdev->wiphy_idx < 0)) {
- wiphy_counter--;
- mutex_unlock(&cfg80211_mutex);
/* ugh, wrapped! */
+ atomic_dec(&wiphy_counter);
kfree(rdev);
return NULL;
}
- mutex_unlock(&cfg80211_mutex);
-
/* give it a proper name */
dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx);
- mutex_init(&rdev->mtx);
- mutex_init(&rdev->devlist_mtx);
- mutex_init(&rdev->sched_scan_mtx);
INIT_LIST_HEAD(&rdev->wdev_list);
INIT_LIST_HEAD(&rdev->beacon_registrations);
spin_lock_init(&rdev->beacon_registrations_lock);
@@ -598,11 +551,11 @@ int wiphy_register(struct wiphy *wiphy)
/* check and set up bitrates */
ieee80211_set_bitrate_flags(wiphy);
- mutex_lock(&cfg80211_mutex);
+ rtnl_lock();
res = device_add(&rdev->wiphy.dev);
if (res) {
- mutex_unlock(&cfg80211_mutex);
+ rtnl_unlock();
return res;
}
@@ -631,24 +584,21 @@ int wiphy_register(struct wiphy *wiphy)
}
cfg80211_debugfs_rdev_add(rdev);
- mutex_unlock(&cfg80211_mutex);
- /*
- * due to a locking dependency this has to be outside of the
- * cfg80211_mutex lock
- */
res = rfkill_register(rdev->rfkill);
- if (res)
- goto out_rm_dev;
+ if (res) {
+ device_del(&rdev->wiphy.dev);
+
+ debugfs_remove_recursive(rdev->wiphy.debugfsdir);
+ list_del_rcu(&rdev->list);
+ wiphy_regulatory_deregister(wiphy);
+ rtnl_unlock();
+ return res;
+ }
- rtnl_lock();
rdev->wiphy.registered = true;
rtnl_unlock();
return 0;
-
-out_rm_dev:
- device_del(&rdev->wiphy.dev);
- return res;
}
EXPORT_SYMBOL(wiphy_register);
@@ -675,25 +625,19 @@ void wiphy_unregister(struct wiphy *wiphy)
{
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
- rtnl_lock();
- rdev->wiphy.registered = false;
- rtnl_unlock();
-
- rfkill_unregister(rdev->rfkill);
-
- /* protect the device list */
- mutex_lock(&cfg80211_mutex);
-
wait_event(rdev->dev_wait, ({
int __count;
- mutex_lock(&rdev->devlist_mtx);
+ rtnl_lock();
__count = rdev->opencount;
- mutex_unlock(&rdev->devlist_mtx);
+ rtnl_unlock();
__count == 0; }));
- mutex_lock(&rdev->devlist_mtx);
+ rtnl_lock();
+ rdev->wiphy.registered = false;
+
+ rfkill_unregister(rdev->rfkill);
+
BUG_ON(!list_empty(&rdev->wdev_list));
- mutex_unlock(&rdev->devlist_mtx);
/*
* First remove the hardware from everywhere, this makes
@@ -704,20 +648,6 @@ void wiphy_unregister(struct wiphy *wiphy)
synchronize_rcu();
/*
- * Try to grab rdev->mtx. If a command is still in progress,
- * hopefully the driver will refuse it since it's tearing
- * down the device already. We wait for this command to complete
- * before unlinking the item from the list.
- * Note: as codified by the BUG_ON above we cannot get here if
- * a virtual interface is still present. Hence, we can only get
- * to lock contention here if userspace issues a command that
- * identified the hardware by wiphy index.
- */
- cfg80211_lock_rdev(rdev);
- /* nothing */
- cfg80211_unlock_rdev(rdev);
-
- /*
* If this device got a regulatory hint tell core its
* free to listen now to a new shiny device regulatory hint
*/
@@ -726,15 +656,17 @@ void wiphy_unregister(struct wiphy *wiphy)
cfg80211_rdev_list_generation++;
device_del(&rdev->wiphy.dev);
- mutex_unlock(&cfg80211_mutex);
+ rtnl_unlock();
flush_work(&rdev->scan_done_wk);
cancel_work_sync(&rdev->conn_work);
flush_work(&rdev->event_work);
cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
- if (rdev->wowlan && rdev->ops->set_wakeup)
+#ifdef CONFIG_PM
+ if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
rdev_set_wakeup(rdev, false);
+#endif
cfg80211_rdev_free_wowlan(rdev);
}
EXPORT_SYMBOL(wiphy_unregister);
@@ -744,9 +676,6 @@ void cfg80211_dev_free(struct cfg80211_registered_device *rdev)
struct cfg80211_internal_bss *scan, *tmp;
struct cfg80211_beacon_registration *reg, *treg;
rfkill_destroy(rdev->rfkill);
- mutex_destroy(&rdev->mtx);
- mutex_destroy(&rdev->devlist_mtx);
- mutex_destroy(&rdev->sched_scan_mtx);
list_for_each_entry_safe(reg, treg, &rdev->beacon_registrations, list) {
list_del(&reg->list);
kfree(reg);
@@ -771,36 +700,6 @@ void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked)
}
EXPORT_SYMBOL(wiphy_rfkill_set_hw_state);
-static void wdev_cleanup_work(struct work_struct *work)
-{
- struct wireless_dev *wdev;
- struct cfg80211_registered_device *rdev;
-
- wdev = container_of(work, struct wireless_dev, cleanup_work);
- rdev = wiphy_to_dev(wdev->wiphy);
-
- mutex_lock(&rdev->sched_scan_mtx);
-
- if (WARN_ON(rdev->scan_req && rdev->scan_req->wdev == wdev)) {
- rdev->scan_req->aborted = true;
- ___cfg80211_scan_done(rdev, true);
- }
-
- if (WARN_ON(rdev->sched_scan_req &&
- rdev->sched_scan_req->dev == wdev->netdev)) {
- __cfg80211_stop_sched_scan(rdev, false);
- }
-
- mutex_unlock(&rdev->sched_scan_mtx);
-
- mutex_lock(&rdev->devlist_mtx);
- rdev->opencount--;
- mutex_unlock(&rdev->devlist_mtx);
- wake_up(&rdev->dev_wait);
-
- dev_put(wdev->netdev);
-}
-
void cfg80211_unregister_wdev(struct wireless_dev *wdev)
{
struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
@@ -810,8 +709,6 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev)
if (WARN_ON(wdev->netdev))
return;
- mutex_lock(&rdev->devlist_mtx);
- mutex_lock(&rdev->sched_scan_mtx);
list_del_rcu(&wdev->list);
rdev->devlist_generation++;
@@ -823,8 +720,6 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev)
WARN_ON_ONCE(1);
break;
}
- mutex_unlock(&rdev->sched_scan_mtx);
- mutex_unlock(&rdev->devlist_mtx);
}
EXPORT_SYMBOL(cfg80211_unregister_wdev);
@@ -843,7 +738,7 @@ void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
}
void cfg80211_leave(struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev)
+ struct wireless_dev *wdev)
{
struct net_device *dev = wdev->netdev;
@@ -853,9 +748,7 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev,
break;
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_STATION:
- mutex_lock(&rdev->sched_scan_mtx);
__cfg80211_stop_sched_scan(rdev, false);
- mutex_unlock(&rdev->sched_scan_mtx);
wdev_lock(wdev);
#ifdef CONFIG_CFG80211_WEXT
@@ -864,9 +757,8 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev,
wdev->wext.ie_len = 0;
wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
#endif
- __cfg80211_disconnect(rdev, dev,
- WLAN_REASON_DEAUTH_LEAVING, true);
- cfg80211_mlme_down(rdev, dev);
+ cfg80211_disconnect(rdev, dev,
+ WLAN_REASON_DEAUTH_LEAVING, true);
wdev_unlock(wdev);
break;
case NL80211_IFTYPE_MESH_POINT:
@@ -909,13 +801,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
* are added with nl80211.
*/
mutex_init(&wdev->mtx);
- INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work);
INIT_LIST_HEAD(&wdev->event_list);
spin_lock_init(&wdev->event_lock);
INIT_LIST_HEAD(&wdev->mgmt_registrations);
spin_lock_init(&wdev->mgmt_registrations_lock);
- mutex_lock(&rdev->devlist_mtx);
wdev->identifier = ++rdev->wdev_id;
list_add_rcu(&wdev->list, &rdev->wdev_list);
rdev->devlist_generation++;
@@ -928,7 +818,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
}
wdev->netdev = dev;
wdev->sme_state = CFG80211_SME_IDLE;
- mutex_unlock(&rdev->devlist_mtx);
#ifdef CONFIG_CFG80211_WEXT
wdev->wext.default_key = -1;
wdev->wext.default_mgmt_key = -1;
@@ -954,26 +843,22 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
break;
case NETDEV_DOWN:
cfg80211_update_iface_num(rdev, wdev->iftype, -1);
- dev_hold(dev);
- queue_work(cfg80211_wq, &wdev->cleanup_work);
+ if (rdev->scan_req && rdev->scan_req->wdev == wdev) {
+ if (WARN_ON(!rdev->scan_req->notified))
+ rdev->scan_req->aborted = true;
+ ___cfg80211_scan_done(rdev, true);
+ }
+
+ if (WARN_ON(rdev->sched_scan_req &&
+ rdev->sched_scan_req->dev == wdev->netdev)) {
+ __cfg80211_stop_sched_scan(rdev, false);
+ }
+
+ rdev->opencount--;
+ wake_up(&rdev->dev_wait);
break;
case NETDEV_UP:
- /*
- * If we have a really quick DOWN/UP succession we may
- * have this work still pending ... cancel it and see
- * if it was pending, in which case we need to account
- * for some of the work it would have done.
- */
- if (cancel_work_sync(&wdev->cleanup_work)) {
- mutex_lock(&rdev->devlist_mtx);
- rdev->opencount--;
- mutex_unlock(&rdev->devlist_mtx);
- dev_put(dev);
- }
cfg80211_update_iface_num(rdev, wdev->iftype, 1);
- cfg80211_lock_rdev(rdev);
- mutex_lock(&rdev->devlist_mtx);
- mutex_lock(&rdev->sched_scan_mtx);
wdev_lock(wdev);
switch (wdev->iftype) {
#ifdef CONFIG_CFG80211_WEXT
@@ -1005,10 +890,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
break;
}
wdev_unlock(wdev);
- mutex_unlock(&rdev->sched_scan_mtx);
rdev->opencount++;
- mutex_unlock(&rdev->devlist_mtx);
- cfg80211_unlock_rdev(rdev);
/*
* Configure power management to the driver here so that its
@@ -1025,12 +907,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
break;
case NETDEV_UNREGISTER:
/*
- * NB: cannot take rdev->mtx here because this may be
- * called within code protected by it when interfaces
- * are removed with nl80211.
- */
- mutex_lock(&rdev->devlist_mtx);
- /*
* It is possible to get NETDEV_UNREGISTER
* multiple times. To detect that, check
* that the interface is still on the list
@@ -1046,7 +922,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
kfree(wdev->wext.keys);
#endif
}
- mutex_unlock(&rdev->devlist_mtx);
/*
* synchronise (so that we won't find this netdev
* from other code any more) and then clear the list
@@ -1066,9 +941,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
return notifier_from_errno(-EOPNOTSUPP);
if (rfkill_blocked(rdev->rfkill))
return notifier_from_errno(-ERFKILL);
- mutex_lock(&rdev->devlist_mtx);
ret = cfg80211_can_add_interface(rdev, wdev->iftype);
- mutex_unlock(&rdev->devlist_mtx);
if (ret)
return notifier_from_errno(ret);
break;
@@ -1086,12 +959,10 @@ static void __net_exit cfg80211_pernet_exit(struct net *net)
struct cfg80211_registered_device *rdev;
rtnl_lock();
- mutex_lock(&cfg80211_mutex);
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
if (net_eq(wiphy_net(&rdev->wiphy), net))
WARN_ON(cfg80211_switch_netns(rdev, &init_net));
}
- mutex_unlock(&cfg80211_mutex);
rtnl_unlock();
}
diff --git a/net/wireless/core.h b/net/wireless/core.h
index fd35dae547c..a65eaf8a84c 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -5,7 +5,6 @@
*/
#ifndef __NET_WIRELESS_CORE_H
#define __NET_WIRELESS_CORE_H
-#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/rbtree.h>
@@ -23,11 +22,6 @@
struct cfg80211_registered_device {
const struct cfg80211_ops *ops;
struct list_head list;
- /* we hold this mutex during any call so that
- * we cannot do multiple calls at once, and also
- * to avoid the deregister call to proceed while
- * any call is in progress */
- struct mutex mtx;
/* rfkill support */
struct rfkill_ops rfkill_ops;
@@ -49,9 +43,7 @@ struct cfg80211_registered_device {
/* wiphy index, internal only */
int wiphy_idx;
- /* associated wireless interfaces */
- struct mutex devlist_mtx;
- /* protected by devlist_mtx or RCU */
+ /* associated wireless interfaces, protected by rtnl or RCU */
struct list_head wdev_list;
int devlist_generation, wdev_id;
int opencount; /* also protected by devlist_mtx */
@@ -75,8 +67,6 @@ struct cfg80211_registered_device {
struct work_struct scan_done_wk;
struct work_struct sched_scan_results_wk;
- struct mutex sched_scan_mtx;
-
#ifdef CONFIG_NL80211_TESTMODE
struct genl_info *testmode_info;
#endif
@@ -84,8 +74,6 @@ struct cfg80211_registered_device {
struct work_struct conn_work;
struct work_struct event_work;
- struct cfg80211_wowlan *wowlan;
-
struct delayed_work dfs_update_channels_wk;
/* netlink port which started critical protocol (0 means not started) */
@@ -106,29 +94,26 @@ struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy)
static inline void
cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev)
{
+#ifdef CONFIG_PM
int i;
- if (!rdev->wowlan)
+ if (!rdev->wiphy.wowlan_config)
return;
- for (i = 0; i < rdev->wowlan->n_patterns; i++)
- kfree(rdev->wowlan->patterns[i].mask);
- kfree(rdev->wowlan->patterns);
- if (rdev->wowlan->tcp && rdev->wowlan->tcp->sock)
- sock_release(rdev->wowlan->tcp->sock);
- kfree(rdev->wowlan->tcp);
- kfree(rdev->wowlan);
+ for (i = 0; i < rdev->wiphy.wowlan_config->n_patterns; i++)
+ kfree(rdev->wiphy.wowlan_config->patterns[i].mask);
+ kfree(rdev->wiphy.wowlan_config->patterns);
+ if (rdev->wiphy.wowlan_config->tcp &&
+ rdev->wiphy.wowlan_config->tcp->sock)
+ sock_release(rdev->wiphy.wowlan_config->tcp->sock);
+ kfree(rdev->wiphy.wowlan_config->tcp);
+ kfree(rdev->wiphy.wowlan_config);
+#endif
}
extern struct workqueue_struct *cfg80211_wq;
-extern struct mutex cfg80211_mutex;
extern struct list_head cfg80211_rdev_list;
extern int cfg80211_rdev_list_generation;
-static inline void assert_cfg80211_lock(void)
-{
- lockdep_assert_held(&cfg80211_mutex);
-}
-
struct cfg80211_internal_bss {
struct list_head list;
struct list_head hidden_list;
@@ -161,27 +146,11 @@ static inline void cfg80211_unhold_bss(struct cfg80211_internal_bss *bss)
struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx);
int get_wiphy_idx(struct wiphy *wiphy);
-/* requires cfg80211_rdev_mutex to be held! */
struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx);
-/* identical to cfg80211_get_dev_from_info but only operate on ifindex */
-extern struct cfg80211_registered_device *
-cfg80211_get_dev_from_ifindex(struct net *net, int ifindex);
-
int cfg80211_switch_netns(struct cfg80211_registered_device *rdev,
struct net *net);
-static inline void cfg80211_lock_rdev(struct cfg80211_registered_device *rdev)
-{
- mutex_lock(&rdev->mtx);
-}
-
-static inline void cfg80211_unlock_rdev(struct cfg80211_registered_device *rdev)
-{
- BUG_ON(IS_ERR(rdev) || !rdev);
- mutex_unlock(&rdev->mtx);
-}
-
static inline void wdev_lock(struct wireless_dev *wdev)
__acquires(wdev)
{
@@ -196,7 +165,7 @@ static inline void wdev_unlock(struct wireless_dev *wdev)
mutex_unlock(&wdev->mtx);
}
-#define ASSERT_RDEV_LOCK(rdev) lockdep_assert_held(&(rdev)->mtx)
+#define ASSERT_RDEV_LOCK(rdev) ASSERT_RTNL()
#define ASSERT_WDEV_LOCK(wdev) lockdep_assert_held(&(wdev)->mtx)
static inline bool cfg80211_has_monitors_only(struct cfg80211_registered_device *rdev)
@@ -314,38 +283,21 @@ int cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
struct net_device *dev);
/* MLME */
-int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
- struct net_device *dev,
- struct ieee80211_channel *chan,
- enum nl80211_auth_type auth_type,
- const u8 *bssid,
- const u8 *ssid, int ssid_len,
- const u8 *ie, int ie_len,
- const u8 *key, int key_len, int key_idx,
- const u8 *sae_data, int sae_data_len);
int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
- struct net_device *dev, struct ieee80211_channel *chan,
- enum nl80211_auth_type auth_type, const u8 *bssid,
+ struct net_device *dev,
+ struct ieee80211_channel *chan,
+ enum nl80211_auth_type auth_type,
+ const u8 *bssid,
const u8 *ssid, int ssid_len,
const u8 *ie, int ie_len,
const u8 *key, int key_len, int key_idx,
const u8 *sae_data, int sae_data_len);
-int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
- struct net_device *dev,
- struct ieee80211_channel *chan,
- const u8 *bssid,
- const u8 *ssid, int ssid_len,
- struct cfg80211_assoc_request *req);
int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct ieee80211_channel *chan,
const u8 *bssid,
const u8 *ssid, int ssid_len,
struct cfg80211_assoc_request *req);
-int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
- struct net_device *dev, const u8 *bssid,
- const u8 *ie, int ie_len, u16 reason,
- bool local_state_change);
int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
struct net_device *dev, const u8 *bssid,
const u8 *ie, int ie_len, u16 reason,
@@ -377,18 +329,11 @@ void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
const struct ieee80211_vht_cap *vht_capa_mask);
/* SME */
-int __cfg80211_connect(struct cfg80211_registered_device *rdev,
- struct net_device *dev,
- struct cfg80211_connect_params *connect,
- struct cfg80211_cached_keys *connkeys,
- const u8 *prev_bssid);
int cfg80211_connect(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_connect_params *connect,
- struct cfg80211_cached_keys *connkeys);
-int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
- struct net_device *dev, u16 reason,
- bool wextev);
+ struct cfg80211_cached_keys *connkeys,
+ const u8 *prev_bssid);
int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
struct net_device *dev, u16 reason,
bool wextev);
diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c
index 920cabe0461..90d05003662 100644
--- a/net/wireless/debugfs.c
+++ b/net/wireless/debugfs.c
@@ -74,7 +74,7 @@ static ssize_t ht40allow_map_read(struct file *file,
if (!buf)
return -ENOMEM;
- mutex_lock(&cfg80211_mutex);
+ rtnl_lock();
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
sband = wiphy->bands[band];
@@ -85,7 +85,7 @@ static ssize_t ht40allow_map_read(struct file *file,
buf, buf_size, offset);
}
- mutex_unlock(&cfg80211_mutex);
+ rtnl_unlock();
r = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index d80e47194d4..5449c5a6de8 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -152,11 +152,11 @@ int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
- mutex_lock(&rdev->devlist_mtx);
+ ASSERT_RTNL();
+
wdev_lock(wdev);
err = __cfg80211_join_ibss(rdev, dev, params, connkeys);
wdev_unlock(wdev);
- mutex_unlock(&rdev->devlist_mtx);
return err;
}
@@ -359,11 +359,9 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
wdev->wext.ibss.channel_fixed = false;
}
- mutex_lock(&rdev->devlist_mtx);
wdev_lock(wdev);
err = cfg80211_ibss_wext_join(rdev, wdev);
wdev_unlock(wdev);
- mutex_unlock(&rdev->devlist_mtx);
return err;
}
@@ -429,11 +427,9 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev,
memcpy(wdev->wext.ibss.ssid, ssid, len);
wdev->wext.ibss.ssid_len = len;
- mutex_lock(&rdev->devlist_mtx);
wdev_lock(wdev);
err = cfg80211_ibss_wext_join(rdev, wdev);
wdev_unlock(wdev);
- mutex_unlock(&rdev->devlist_mtx);
return err;
}
@@ -512,11 +508,9 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev,
} else
wdev->wext.ibss.bssid = NULL;
- mutex_lock(&rdev->devlist_mtx);
wdev_lock(wdev);
err = cfg80211_ibss_wext_join(rdev, wdev);
wdev_unlock(wdev);
- mutex_unlock(&rdev->devlist_mtx);
return err;
}
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index 0bb93f3061a..5dfb289ab76 100644
--- a/net/wireless/mesh.c
+++ b/net/wireless/mesh.c
@@ -82,6 +82,7 @@ const struct mesh_setup default_mesh_setup = {
.sync_method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET,
.path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP,
.path_metric = IEEE80211_PATH_METRIC_AIRTIME,
+ .auth_id = 0, /* open */
.ie = NULL,
.ie_len = 0,
.is_secure = false,
@@ -185,11 +186,9 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
- mutex_lock(&rdev->devlist_mtx);
wdev_lock(wdev);
err = __cfg80211_join_mesh(rdev, dev, setup, conf);
wdev_unlock(wdev);
- mutex_unlock(&rdev->devlist_mtx);
return err;
}
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 0c7b7dd855f..7bde5d9c000 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -25,12 +25,9 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len)
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
trace_cfg80211_send_rx_auth(dev);
- wdev_lock(wdev);
nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL);
cfg80211_sme_rx_auth(dev, buf, len);
-
- wdev_unlock(wdev);
}
EXPORT_SYMBOL(cfg80211_send_rx_auth);
@@ -46,7 +43,6 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,
int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
trace_cfg80211_send_rx_assoc(dev, bss);
- wdev_lock(wdev);
status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
@@ -59,7 +55,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,
if (status_code != WLAN_STATUS_SUCCESS && wdev->conn &&
cfg80211_sme_failed_reassoc(wdev)) {
cfg80211_put_bss(wiphy, bss);
- goto out;
+ return;
}
nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL);
@@ -71,7 +67,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,
* sme will schedule work that does it later.
*/
cfg80211_put_bss(wiphy, bss);
- goto out;
+ return;
}
if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) {
@@ -87,13 +83,11 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,
__cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
status_code,
status_code == WLAN_STATUS_SUCCESS, bss);
- out:
- wdev_unlock(wdev);
}
EXPORT_SYMBOL(cfg80211_send_rx_assoc);
-void __cfg80211_send_deauth(struct net_device *dev,
- const u8 *buf, size_t len)
+void cfg80211_send_deauth(struct net_device *dev,
+ const u8 *buf, size_t len)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
@@ -102,7 +96,7 @@ void __cfg80211_send_deauth(struct net_device *dev,
const u8 *bssid = mgmt->bssid;
bool was_current = false;
- trace___cfg80211_send_deauth(dev);
+ trace_cfg80211_send_deauth(dev);
ASSERT_WDEV_LOCK(wdev);
if (wdev->current_bss &&
@@ -129,20 +123,10 @@ void __cfg80211_send_deauth(struct net_device *dev,
false, NULL);
}
}
-EXPORT_SYMBOL(__cfg80211_send_deauth);
-
-void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
-
- wdev_lock(wdev);
- __cfg80211_send_deauth(dev, buf, len);
- wdev_unlock(wdev);
-}
EXPORT_SYMBOL(cfg80211_send_deauth);
-void __cfg80211_send_disassoc(struct net_device *dev,
- const u8 *buf, size_t len)
+void cfg80211_send_disassoc(struct net_device *dev,
+ const u8 *buf, size_t len)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
@@ -152,7 +136,7 @@ void __cfg80211_send_disassoc(struct net_device *dev,
u16 reason_code;
bool from_ap;
- trace___cfg80211_send_disassoc(dev);
+ trace_cfg80211_send_disassoc(dev);
ASSERT_WDEV_LOCK(wdev);
nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL);
@@ -175,16 +159,6 @@ void __cfg80211_send_disassoc(struct net_device *dev,
from_ap = !ether_addr_equal(mgmt->sa, dev->dev_addr);
__cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap);
}
-EXPORT_SYMBOL(__cfg80211_send_disassoc);
-
-void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
-
- wdev_lock(wdev);
- __cfg80211_send_disassoc(dev, buf, len);
- wdev_unlock(wdev);
-}
EXPORT_SYMBOL(cfg80211_send_disassoc);
void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
@@ -194,15 +168,12 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
trace_cfg80211_send_auth_timeout(dev, addr);
- wdev_lock(wdev);
nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
if (wdev->sme_state == CFG80211_SME_CONNECTING)
__cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE,
false, NULL);
-
- wdev_unlock(wdev);
}
EXPORT_SYMBOL(cfg80211_send_auth_timeout);
@@ -213,15 +184,12 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr)
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
trace_cfg80211_send_assoc_timeout(dev, addr);
- wdev_lock(wdev);
nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL);
if (wdev->sme_state == CFG80211_SME_CONNECTING)
__cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE,
false, NULL);
-
- wdev_unlock(wdev);
}
EXPORT_SYMBOL(cfg80211_send_assoc_timeout);
@@ -253,18 +221,27 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
EXPORT_SYMBOL(cfg80211_michael_mic_failure);
/* some MLME handling for userspace SME */
-int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
- struct net_device *dev,
- struct ieee80211_channel *chan,
- enum nl80211_auth_type auth_type,
- const u8 *bssid,
- const u8 *ssid, int ssid_len,
- const u8 *ie, int ie_len,
- const u8 *key, int key_len, int key_idx,
- const u8 *sae_data, int sae_data_len)
+int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ieee80211_channel *chan,
+ enum nl80211_auth_type auth_type,
+ const u8 *bssid,
+ const u8 *ssid, int ssid_len,
+ const u8 *ie, int ie_len,
+ const u8 *key, int key_len, int key_idx,
+ const u8 *sae_data, int sae_data_len)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
- struct cfg80211_auth_request req;
+ struct cfg80211_auth_request req = {
+ .ie = ie,
+ .ie_len = ie_len,
+ .sae_data = sae_data,
+ .sae_data_len = sae_data_len,
+ .auth_type = auth_type,
+ .key = key,
+ .key_len = key_len,
+ .key_idx = key_idx,
+ };
int err;
ASSERT_WDEV_LOCK(wdev);
@@ -277,18 +254,8 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
ether_addr_equal(bssid, wdev->current_bss->pub.bssid))
return -EALREADY;
- memset(&req, 0, sizeof(req));
-
- req.ie = ie;
- req.ie_len = ie_len;
- req.sae_data = sae_data;
- req.sae_data_len = sae_data_len;
- req.auth_type = auth_type;
req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
- req.key = key;
- req.key_len = key_len;
- req.key_idx = key_idx;
if (!req.bss)
return -ENOENT;
@@ -304,28 +271,6 @@ out:
return err;
}
-int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
- struct net_device *dev, struct ieee80211_channel *chan,
- enum nl80211_auth_type auth_type, const u8 *bssid,
- const u8 *ssid, int ssid_len,
- const u8 *ie, int ie_len,
- const u8 *key, int key_len, int key_idx,
- const u8 *sae_data, int sae_data_len)
-{
- int err;
-
- mutex_lock(&rdev->devlist_mtx);
- wdev_lock(dev->ieee80211_ptr);
- err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
- ssid, ssid_len, ie, ie_len,
- key, key_len, key_idx,
- sae_data, sae_data_len);
- wdev_unlock(dev->ieee80211_ptr);
- mutex_unlock(&rdev->devlist_mtx);
-
- return err;
-}
-
/* Do a logical ht_capa &= ht_capa_mask. */
void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
const struct ieee80211_ht_cap *ht_capa_mask)
@@ -360,12 +305,12 @@ void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
p1[i] &= p2[i];
}
-int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
- struct net_device *dev,
- struct ieee80211_channel *chan,
- const u8 *bssid,
- const u8 *ssid, int ssid_len,
- struct cfg80211_assoc_request *req)
+int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ieee80211_channel *chan,
+ const u8 *bssid,
+ const u8 *ssid, int ssid_len,
+ struct cfg80211_assoc_request *req)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
@@ -415,30 +360,10 @@ out:
return err;
}
-int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
- struct net_device *dev,
- struct ieee80211_channel *chan,
- const u8 *bssid,
- const u8 *ssid, int ssid_len,
- struct cfg80211_assoc_request *req)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
- int err;
-
- mutex_lock(&rdev->devlist_mtx);
- wdev_lock(wdev);
- err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid,
- ssid, ssid_len, req);
- wdev_unlock(wdev);
- mutex_unlock(&rdev->devlist_mtx);
-
- return err;
-}
-
-int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
- struct net_device *dev, const u8 *bssid,
- const u8 *ie, int ie_len, u16 reason,
- bool local_state_change)
+int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *bssid,
+ const u8 *ie, int ie_len, u16 reason,
+ bool local_state_change)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_deauth_request req = {
@@ -458,29 +383,18 @@ int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
return rdev_deauth(rdev, dev, &req);
}
-int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
- struct net_device *dev, const u8 *bssid,
- const u8 *ie, int ie_len, u16 reason,
- bool local_state_change)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
- int err;
-
- wdev_lock(wdev);
- err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason,
- local_state_change);
- wdev_unlock(wdev);
-
- return err;
-}
-
-static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
- struct net_device *dev, const u8 *bssid,
- const u8 *ie, int ie_len, u16 reason,
- bool local_state_change)
+int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *bssid,
+ const u8 *ie, int ie_len, u16 reason,
+ bool local_state_change)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
- struct cfg80211_disassoc_request req;
+ struct cfg80211_disassoc_request req = {
+ .reason_code = reason,
+ .local_state_change = local_state_change,
+ .ie = ie,
+ .ie_len = ie_len,
+ };
ASSERT_WDEV_LOCK(wdev);
@@ -490,11 +404,6 @@ static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
if (WARN(!wdev->current_bss, "sme_state=%d\n", wdev->sme_state))
return -ENOTCONN;
- memset(&req, 0, sizeof(req));
- req.reason_code = reason;
- req.local_state_change = local_state_change;
- req.ie = ie;
- req.ie_len = ie_len;
if (ether_addr_equal(wdev->current_bss->pub.bssid, bssid))
req.bss = &wdev->current_bss->pub;
else
@@ -503,44 +412,25 @@ static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
return rdev_disassoc(rdev, dev, &req);
}
-int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
- struct net_device *dev, const u8 *bssid,
- const u8 *ie, int ie_len, u16 reason,
- bool local_state_change)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
- int err;
-
- wdev_lock(wdev);
- err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason,
- local_state_change);
- wdev_unlock(wdev);
-
- return err;
-}
-
void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
struct net_device *dev)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
- struct cfg80211_deauth_request req;
u8 bssid[ETH_ALEN];
+ struct cfg80211_deauth_request req = {
+ .reason_code = WLAN_REASON_DEAUTH_LEAVING,
+ .bssid = bssid,
+ };
ASSERT_WDEV_LOCK(wdev);
if (!rdev->ops->deauth)
return;
- memset(&req, 0, sizeof(req));
- req.reason_code = WLAN_REASON_DEAUTH_LEAVING;
- req.ie = NULL;
- req.ie_len = 0;
-
if (!wdev->current_bss)
return;
memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN);
- req.bssid = bssid;
rdev_deauth(rdev, dev, &req);
if (wdev->current_bss) {
@@ -848,7 +738,7 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
dfs_update_channels_wk);
wiphy = &rdev->wiphy;
- mutex_lock(&cfg80211_mutex);
+ rtnl_lock();
for (bandid = 0; bandid < IEEE80211_NUM_BANDS; bandid++) {
sband = wiphy->bands[bandid];
if (!sband)
@@ -881,7 +771,7 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
check_again = true;
}
}
- mutex_unlock(&cfg80211_mutex);
+ rtnl_unlock();
/* reschedule if there are other channels waiting to be cleared again */
if (check_again)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index afa283841e8..fb6abcb359a 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -37,10 +37,10 @@ static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
/* the netlink family */
static struct genl_family nl80211_fam = {
- .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
- .name = "nl80211", /* have users key off the name instead */
- .hdrsize = 0, /* no private header */
- .version = 1, /* no particular meaning now */
+ .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
+ .name = NL80211_GENL_NAME, /* have users key off the name instead */
+ .hdrsize = 0, /* no private header */
+ .version = 1, /* no particular meaning now */
.maxattr = NL80211_ATTR_MAX,
.netnsok = true,
.pre_doit = nl80211_pre_doit,
@@ -59,7 +59,7 @@ __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
int wiphy_idx = -1;
int ifidx = -1;
- assert_cfg80211_lock();
+ ASSERT_RTNL();
if (!have_ifidx && !have_wdev_id)
return ERR_PTR(-EINVAL);
@@ -80,7 +80,6 @@ __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
if (have_wdev_id && rdev->wiphy_idx != wiphy_idx)
continue;
- mutex_lock(&rdev->devlist_mtx);
list_for_each_entry(wdev, &rdev->wdev_list, list) {
if (have_ifidx && wdev->netdev &&
wdev->netdev->ifindex == ifidx) {
@@ -92,7 +91,6 @@ __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
break;
}
}
- mutex_unlock(&rdev->devlist_mtx);
if (result)
break;
@@ -109,7 +107,7 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
struct cfg80211_registered_device *rdev = NULL, *tmp;
struct net_device *netdev;
- assert_cfg80211_lock();
+ ASSERT_RTNL();
if (!attrs[NL80211_ATTR_WIPHY] &&
!attrs[NL80211_ATTR_IFINDEX] &&
@@ -128,14 +126,12 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
tmp = cfg80211_rdev_by_wiphy_idx(wdev_id >> 32);
if (tmp) {
/* make sure wdev exists */
- mutex_lock(&tmp->devlist_mtx);
list_for_each_entry(wdev, &tmp->wdev_list, list) {
if (wdev->identifier != (u32)wdev_id)
continue;
found = true;
break;
}
- mutex_unlock(&tmp->devlist_mtx);
if (!found)
tmp = NULL;
@@ -182,19 +178,6 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
/*
* This function returns a pointer to the driver
* that the genl_info item that is passed refers to.
- * If successful, it returns non-NULL and also locks
- * the driver's mutex!
- *
- * This means that you need to call cfg80211_unlock_rdev()
- * before being allowed to acquire &cfg80211_mutex!
- *
- * This is necessary because we need to lock the global
- * mutex to get an item off the list safely, and then
- * we lock the rdev mutex so it doesn't go away under us.
- *
- * We don't want to keep cfg80211_mutex locked
- * for all the time in order to allow requests on
- * other interfaces to go through at the same time.
*
* The result of this can be a PTR_ERR and hence must
* be checked with IS_ERR() for errors.
@@ -202,20 +185,7 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
static struct cfg80211_registered_device *
cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info)
{
- struct cfg80211_registered_device *rdev;
-
- mutex_lock(&cfg80211_mutex);
- rdev = __cfg80211_rdev_from_attrs(netns, info->attrs);
-
- /* if it is not an error we grab the lock on
- * it to assure it won't be going away while
- * we operate on it */
- if (!IS_ERR(rdev))
- mutex_lock(&rdev->mtx);
-
- mutex_unlock(&cfg80211_mutex);
-
- return rdev;
+ return __cfg80211_rdev_from_attrs(netns, info->attrs);
}
/* policy for the attributes */
@@ -378,6 +348,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_MDID] = { .type = NLA_U16 },
[NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_DATA_LEN },
+ [NL80211_ATTR_PEER_AID] = { .type = NLA_U16 },
};
/* policy for the key attributes */
@@ -455,7 +426,6 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
int err;
rtnl_lock();
- mutex_lock(&cfg80211_mutex);
if (!cb->args[0]) {
err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
@@ -484,14 +454,12 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
*rdev = wiphy_to_dev(wiphy);
*wdev = NULL;
- mutex_lock(&(*rdev)->devlist_mtx);
list_for_each_entry(tmp, &(*rdev)->wdev_list, list) {
if (tmp->identifier == cb->args[1]) {
*wdev = tmp;
break;
}
}
- mutex_unlock(&(*rdev)->devlist_mtx);
if (!*wdev) {
err = -ENODEV;
@@ -499,19 +467,14 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
}
}
- cfg80211_lock_rdev(*rdev);
-
- mutex_unlock(&cfg80211_mutex);
return 0;
out_unlock:
- mutex_unlock(&cfg80211_mutex);
rtnl_unlock();
return err;
}
static void nl80211_finish_wdev_dump(struct cfg80211_registered_device *rdev)
{
- cfg80211_unlock_rdev(rdev);
rtnl_unlock();
}
@@ -1567,7 +1530,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
struct nlattr **tb = nl80211_fam.attrbuf;
int res;
- mutex_lock(&cfg80211_mutex);
+ rtnl_lock();
res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
tb, nl80211_fam.maxattr, nl80211_policy);
if (res == 0) {
@@ -1581,10 +1544,8 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
int ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
netdev = dev_get_by_index(sock_net(skb->sk), ifidx);
- if (!netdev) {
- mutex_unlock(&cfg80211_mutex);
+ if (!netdev)
return -ENODEV;
- }
if (netdev->ieee80211_ptr) {
dev = wiphy_to_dev(
netdev->ieee80211_ptr->wiphy);
@@ -1628,7 +1589,6 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
!skb->len &&
cb->min_dump_alloc < 4096) {
cb->min_dump_alloc = 4096;
- mutex_unlock(&cfg80211_mutex);
return 1;
}
idx--;
@@ -1637,7 +1597,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
} while (cb->args[1] > 0);
break;
}
- mutex_unlock(&cfg80211_mutex);
+ rtnl_unlock();
cb->args[0] = idx;
@@ -1792,7 +1752,6 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
if (result)
return result;
- mutex_lock(&rdev->devlist_mtx);
switch (iftype) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
@@ -1816,7 +1775,6 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
default:
result = -EINVAL;
}
- mutex_unlock(&rdev->devlist_mtx);
return result;
}
@@ -1865,6 +1823,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
u32 frag_threshold = 0, rts_threshold = 0;
u8 coverage_class = 0;
+ ASSERT_RTNL();
+
/*
* Try to find the wiphy and netdev. Normally this
* function shouldn't need the netdev, but this is
@@ -1874,31 +1834,25 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
* also passed a netdev to set_wiphy, so that it is
* possible to let that go to the right netdev!
*/
- mutex_lock(&cfg80211_mutex);
if (info->attrs[NL80211_ATTR_IFINDEX]) {
int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
netdev = dev_get_by_index(genl_info_net(info), ifindex);
- if (netdev && netdev->ieee80211_ptr) {
+ if (netdev && netdev->ieee80211_ptr)
rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
- mutex_lock(&rdev->mtx);
- } else
+ else
netdev = NULL;
}
if (!netdev) {
rdev = __cfg80211_rdev_from_attrs(genl_info_net(info),
info->attrs);
- if (IS_ERR(rdev)) {
- mutex_unlock(&cfg80211_mutex);
+ if (IS_ERR(rdev))
return PTR_ERR(rdev);
- }
wdev = NULL;
netdev = NULL;
result = 0;
-
- mutex_lock(&rdev->mtx);
} else
wdev = netdev->ieee80211_ptr;
@@ -1911,8 +1865,6 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
result = cfg80211_dev_rename(
rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
- mutex_unlock(&cfg80211_mutex);
-
if (result)
goto bad_res;
@@ -2119,7 +2071,6 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
}
bad_res:
- mutex_unlock(&rdev->mtx);
if (netdev)
dev_put(netdev);
return result;
@@ -2217,7 +2168,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
struct cfg80211_registered_device *rdev;
struct wireless_dev *wdev;
- mutex_lock(&cfg80211_mutex);
+ rtnl_lock();
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
continue;
@@ -2227,7 +2178,6 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
}
if_idx = 0;
- mutex_lock(&rdev->devlist_mtx);
list_for_each_entry(wdev, &rdev->wdev_list, list) {
if (if_idx < if_start) {
if_idx++;
@@ -2236,17 +2186,15 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
rdev, wdev) < 0) {
- mutex_unlock(&rdev->devlist_mtx);
goto out;
}
if_idx++;
}
- mutex_unlock(&rdev->devlist_mtx);
wp_idx++;
}
out:
- mutex_unlock(&cfg80211_mutex);
+ rtnl_unlock();
cb->args[0] = wp_idx;
cb->args[1] = if_idx;
@@ -2479,11 +2427,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
INIT_LIST_HEAD(&wdev->mgmt_registrations);
spin_lock_init(&wdev->mgmt_registrations_lock);
- mutex_lock(&rdev->devlist_mtx);
wdev->identifier = ++rdev->wdev_id;
list_add_rcu(&wdev->list, &rdev->wdev_list);
rdev->devlist_generation++;
- mutex_unlock(&rdev->devlist_mtx);
break;
default:
break;
@@ -2992,8 +2938,6 @@ static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev;
bool ret = false;
- mutex_lock(&rdev->devlist_mtx);
-
list_for_each_entry(wdev, &rdev->wdev_list, list) {
if (wdev->iftype != NL80211_IFTYPE_AP &&
wdev->iftype != NL80211_IFTYPE_P2P_GO)
@@ -3007,8 +2951,6 @@ static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev,
break;
}
- mutex_unlock(&rdev->devlist_mtx);
-
return ret;
}
@@ -3170,13 +3112,10 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
params.radar_required = true;
}
- mutex_lock(&rdev->devlist_mtx);
err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
params.chandef.chan,
CHAN_MODE_SHARED,
radar_detect_width);
- mutex_unlock(&rdev->devlist_mtx);
-
if (err)
return err;
@@ -3376,6 +3315,32 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
return true;
}
+static bool nl80211_put_signal(struct sk_buff *msg, u8 mask, s8 *signal,
+ int id)
+{
+ void *attr;
+ int i = 0;
+
+ if (!mask)
+ return true;
+
+ attr = nla_nest_start(msg, id);
+ if (!attr)
+ return false;
+
+ for (i = 0; i < IEEE80211_MAX_CHAINS; i++) {
+ if (!(mask & BIT(i)))
+ continue;
+
+ if (nla_put_u8(msg, i, signal[i]))
+ return false;
+ }
+
+ nla_nest_end(msg, attr);
+
+ return true;
+}
+
static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
int flags,
struct cfg80211_registered_device *rdev,
@@ -3411,7 +3376,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
(u32)sinfo->rx_bytes))
goto nla_put_failure;
if ((sinfo->filled & (STATION_INFO_TX_BYTES |
- NL80211_STA_INFO_TX_BYTES64)) &&
+ STATION_INFO_TX_BYTES64)) &&
nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES,
(u32)sinfo->tx_bytes))
goto nla_put_failure;
@@ -3447,6 +3412,18 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
default:
break;
}
+ if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL) {
+ if (!nl80211_put_signal(msg, sinfo->chains,
+ sinfo->chain_signal,
+ NL80211_STA_INFO_CHAIN_SIGNAL))
+ goto nla_put_failure;
+ }
+ if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL_AVG) {
+ if (!nl80211_put_signal(msg, sinfo->chains,
+ sinfo->chain_signal_avg,
+ NL80211_STA_INFO_CHAIN_SIGNAL_AVG))
+ goto nla_put_failure;
+ }
if (sinfo->filled & STATION_INFO_TX_BITRATE) {
if (!nl80211_put_sta_rate(msg, &sinfo->txrate,
NL80211_STA_INFO_TX_BITRATE))
@@ -3834,6 +3811,8 @@ static int nl80211_set_station_tdls(struct genl_info *info,
struct station_parameters *params)
{
/* Dummy STA entry gets updated once the peer capabilities are known */
+ if (info->attrs[NL80211_ATTR_PEER_AID])
+ params->aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
params->ht_capa =
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
@@ -3974,7 +3953,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
return -EINVAL;
- if (!info->attrs[NL80211_ATTR_STA_AID])
+ if (!info->attrs[NL80211_ATTR_STA_AID] &&
+ !info->attrs[NL80211_ATTR_PEER_AID])
return -EINVAL;
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
@@ -3985,7 +3965,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
params.listen_interval =
nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
- params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
+ if (info->attrs[NL80211_ATTR_STA_AID])
+ params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
+ else
+ params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
if (!params.aid || params.aid > IEEE80211_MAX_AID)
return -EINVAL;
@@ -4634,6 +4617,7 @@ static const struct nla_policy
[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
[NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
+ [NL80211_MESH_SETUP_AUTH_PROTOCOL] = { .type = NLA_U8 },
[NL80211_MESH_SETUP_USERSPACE_MPM] = { .type = NLA_FLAG },
[NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_DATA_LEN },
@@ -4819,6 +4803,13 @@ static int nl80211_parse_mesh_setup(struct genl_info *info,
if (setup->is_secure)
setup->user_mpm = true;
+ if (tb[NL80211_MESH_SETUP_AUTH_PROTOCOL]) {
+ if (!setup->user_mpm)
+ return -EINVAL;
+ setup->auth_id =
+ nla_get_u8(tb[NL80211_MESH_SETUP_AUTH_PROTOCOL]);
+ }
+
return 0;
}
@@ -4861,18 +4852,13 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
void *hdr = NULL;
struct nlattr *nl_reg_rules;
unsigned int i;
- int err = -EINVAL;
-
- mutex_lock(&cfg80211_mutex);
if (!cfg80211_regdomain)
- goto out;
+ return -EINVAL;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (!msg) {
- err = -ENOBUFS;
- goto out;
- }
+ if (!msg)
+ return -ENOBUFS;
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
NL80211_CMD_GET_REG);
@@ -4931,8 +4917,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
nla_nest_end(msg, nl_reg_rules);
genlmsg_end(msg, hdr);
- err = genlmsg_reply(msg, info);
- goto out;
+ return genlmsg_reply(msg, info);
nla_put_failure_rcu:
rcu_read_unlock();
@@ -4940,10 +4925,7 @@ nla_put_failure:
genlmsg_cancel(msg, hdr);
put_failure:
nlmsg_free(msg);
- err = -EMSGSIZE;
-out:
- mutex_unlock(&cfg80211_mutex);
- return err;
+ return -EMSGSIZE;
}
static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
@@ -5009,12 +4991,9 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
}
}
- mutex_lock(&cfg80211_mutex);
-
r = set_regdom(rd);
/* set_regdom took ownership */
rd = NULL;
- mutex_unlock(&cfg80211_mutex);
bad_reg:
kfree(rd);
@@ -5064,7 +5043,6 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
if (!rdev->ops->scan)
return -EOPNOTSUPP;
- mutex_lock(&rdev->sched_scan_mtx);
if (rdev->scan_req) {
err = -EBUSY;
goto unlock;
@@ -5250,7 +5228,6 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
}
unlock:
- mutex_unlock(&rdev->sched_scan_mtx);
return err;
}
@@ -5322,8 +5299,6 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
if (ie_len > wiphy->max_sched_scan_ie_len)
return -EINVAL;
- mutex_lock(&rdev->sched_scan_mtx);
-
if (rdev->sched_scan_req) {
err = -EINPROGRESS;
goto out;
@@ -5491,7 +5466,6 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
out_free:
kfree(request);
out:
- mutex_unlock(&rdev->sched_scan_mtx);
return err;
}
@@ -5499,17 +5473,12 @@ static int nl80211_stop_sched_scan(struct sk_buff *skb,
struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
- int err;
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
!rdev->ops->sched_scan_stop)
return -EOPNOTSUPP;
- mutex_lock(&rdev->sched_scan_mtx);
- err = __cfg80211_stop_sched_scan(rdev, false);
- mutex_unlock(&rdev->sched_scan_mtx);
-
- return err;
+ return __cfg80211_stop_sched_scan(rdev, false);
}
static int nl80211_start_radar_detection(struct sk_buff *skb,
@@ -5541,12 +5510,11 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
if (!rdev->ops->start_radar_detection)
return -EOPNOTSUPP;
- mutex_lock(&rdev->devlist_mtx);
err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
chandef.chan, CHAN_MODE_SHARED,
BIT(chandef.width));
if (err)
- goto err_locked;
+ return err;
err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef);
if (!err) {
@@ -5554,9 +5522,6 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
wdev->cac_started = true;
wdev->cac_start_time = jiffies;
}
-err_locked:
- mutex_unlock(&rdev->devlist_mtx);
-
return err;
}
@@ -5939,10 +5904,13 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
if (local_state_change)
return 0;
- return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
- ssid, ssid_len, ie, ie_len,
- key.p.key, key.p.key_len, key.idx,
- sae_data, sae_data_len);
+ wdev_lock(dev->ieee80211_ptr);
+ err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
+ ssid, ssid_len, ie, ie_len,
+ key.p.key, key.p.key_len, key.idx,
+ sae_data, sae_data_len);
+ wdev_unlock(dev->ieee80211_ptr);
+ return err;
}
static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
@@ -6109,9 +6077,12 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
}
err = nl80211_crypto_settings(rdev, info, &req.crypto, 1);
- if (!err)
+ if (!err) {
+ wdev_lock(dev->ieee80211_ptr);
err = cfg80211_mlme_assoc(rdev, dev, chan, bssid,
ssid, ssid_len, &req);
+ wdev_unlock(dev->ieee80211_ptr);
+ }
return err;
}
@@ -6121,7 +6092,7 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
const u8 *ie = NULL, *bssid;
- int ie_len = 0;
+ int ie_len = 0, err;
u16 reason_code;
bool local_state_change;
@@ -6156,8 +6127,11 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
- return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
- local_state_change);
+ wdev_lock(dev->ieee80211_ptr);
+ err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
+ local_state_change);
+ wdev_unlock(dev->ieee80211_ptr);
+ return err;
}
static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
@@ -6165,7 +6139,7 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
const u8 *ie = NULL, *bssid;
- int ie_len = 0;
+ int ie_len = 0, err;
u16 reason_code;
bool local_state_change;
@@ -6200,8 +6174,11 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
- return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
- local_state_change);
+ wdev_lock(dev->ieee80211_ptr);
+ err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
+ local_state_change);
+ wdev_unlock(dev->ieee80211_ptr);
+ return err;
}
static bool
@@ -6419,6 +6396,8 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
void *data = NULL;
int data_len = 0;
+ rtnl_lock();
+
if (cb->args[0]) {
/*
* 0 is a valid index, but not valid for args[0],
@@ -6430,18 +6409,16 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
nl80211_fam.attrbuf, nl80211_fam.maxattr,
nl80211_policy);
if (err)
- return err;
+ goto out_err;
- mutex_lock(&cfg80211_mutex);
rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk),
nl80211_fam.attrbuf);
if (IS_ERR(rdev)) {
- mutex_unlock(&cfg80211_mutex);
- return PTR_ERR(rdev);
+ err = PTR_ERR(rdev);
+ goto out_err;
}
phy_idx = rdev->wiphy_idx;
rdev = NULL;
- mutex_unlock(&cfg80211_mutex);
if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA])
cb->args[1] =
@@ -6453,14 +6430,11 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
data_len = nla_len((void *)cb->args[1]);
}
- mutex_lock(&cfg80211_mutex);
rdev = cfg80211_rdev_by_wiphy_idx(phy_idx);
if (!rdev) {
- mutex_unlock(&cfg80211_mutex);
- return -ENOENT;
+ err = -ENOENT;
+ goto out_err;
}
- cfg80211_lock_rdev(rdev);
- mutex_unlock(&cfg80211_mutex);
if (!rdev->ops->testmode_dump) {
err = -EOPNOTSUPP;
@@ -6501,7 +6475,7 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
/* see above */
cb->args[0] = phy_idx + 1;
out_err:
- cfg80211_unlock_rdev(rdev);
+ rtnl_unlock();
return err;
}
@@ -6709,7 +6683,9 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
sizeof(connect.vht_capa));
}
- err = cfg80211_connect(rdev, dev, &connect, connkeys);
+ wdev_lock(dev->ieee80211_ptr);
+ err = cfg80211_connect(rdev, dev, &connect, connkeys, NULL);
+ wdev_unlock(dev->ieee80211_ptr);
if (err)
kfree(connkeys);
return err;
@@ -6720,6 +6696,7 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
u16 reason;
+ int ret;
if (!info->attrs[NL80211_ATTR_REASON_CODE])
reason = WLAN_REASON_DEAUTH_LEAVING;
@@ -6733,7 +6710,10 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
return -EOPNOTSUPP;
- return cfg80211_disconnect(rdev, dev, reason, true);
+ wdev_lock(dev->ieee80211_ptr);
+ ret = cfg80211_disconnect(rdev, dev, reason, true);
+ wdev_unlock(dev->ieee80211_ptr);
+ return ret;
}
static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
@@ -7509,28 +7489,29 @@ static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)
static int nl80211_send_wowlan_patterns(struct sk_buff *msg,
struct cfg80211_registered_device *rdev)
{
+ struct cfg80211_wowlan *wowlan = rdev->wiphy.wowlan_config;
struct nlattr *nl_pats, *nl_pat;
int i, pat_len;
- if (!rdev->wowlan->n_patterns)
+ if (!wowlan->n_patterns)
return 0;
nl_pats = nla_nest_start(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN);
if (!nl_pats)
return -ENOBUFS;
- for (i = 0; i < rdev->wowlan->n_patterns; i++) {
+ for (i = 0; i < wowlan->n_patterns; i++) {
nl_pat = nla_nest_start(msg, i + 1);
if (!nl_pat)
return -ENOBUFS;
- pat_len = rdev->wowlan->patterns[i].pattern_len;
+ pat_len = wowlan->patterns[i].pattern_len;
if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK,
DIV_ROUND_UP(pat_len, 8),
- rdev->wowlan->patterns[i].mask) ||
+ wowlan->patterns[i].mask) ||
nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
- pat_len, rdev->wowlan->patterns[i].pattern) ||
+ pat_len, wowlan->patterns[i].pattern) ||
nla_put_u32(msg, NL80211_WOWLAN_PKTPAT_OFFSET,
- rdev->wowlan->patterns[i].pkt_offset))
+ wowlan->patterns[i].pkt_offset))
return -ENOBUFS;
nla_nest_end(msg, nl_pat);
}
@@ -7577,6 +7558,8 @@ static int nl80211_send_wowlan_tcp(struct sk_buff *msg,
&tcp->payload_tok))
return -ENOBUFS;
+ nla_nest_end(msg, nl_tcp);
+
return 0;
}
@@ -7591,12 +7574,12 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
!rdev->wiphy.wowlan.tcp)
return -EOPNOTSUPP;
- if (rdev->wowlan && rdev->wowlan->tcp) {
+ if (rdev->wiphy.wowlan_config && rdev->wiphy.wowlan_config->tcp) {
/* adjust size to have room for all the data */
- size += rdev->wowlan->tcp->tokens_size +
- rdev->wowlan->tcp->payload_len +
- rdev->wowlan->tcp->wake_len +
- rdev->wowlan->tcp->wake_len / 8;
+ size += rdev->wiphy.wowlan_config->tcp->tokens_size +
+ rdev->wiphy.wowlan_config->tcp->payload_len +
+ rdev->wiphy.wowlan_config->tcp->wake_len +
+ rdev->wiphy.wowlan_config->tcp->wake_len / 8;
}
msg = nlmsg_new(size, GFP_KERNEL);
@@ -7608,33 +7591,34 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
if (!hdr)
goto nla_put_failure;
- if (rdev->wowlan) {
+ if (rdev->wiphy.wowlan_config) {
struct nlattr *nl_wowlan;
nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
if (!nl_wowlan)
goto nla_put_failure;
- if ((rdev->wowlan->any &&
+ if ((rdev->wiphy.wowlan_config->any &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
- (rdev->wowlan->disconnect &&
+ (rdev->wiphy.wowlan_config->disconnect &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
- (rdev->wowlan->magic_pkt &&
+ (rdev->wiphy.wowlan_config->magic_pkt &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
- (rdev->wowlan->gtk_rekey_failure &&
+ (rdev->wiphy.wowlan_config->gtk_rekey_failure &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
- (rdev->wowlan->eap_identity_req &&
+ (rdev->wiphy.wowlan_config->eap_identity_req &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
- (rdev->wowlan->four_way_handshake &&
+ (rdev->wiphy.wowlan_config->four_way_handshake &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
- (rdev->wowlan->rfkill_release &&
+ (rdev->wiphy.wowlan_config->rfkill_release &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
goto nla_put_failure;
if (nl80211_send_wowlan_patterns(msg, rdev))
goto nla_put_failure;
- if (nl80211_send_wowlan_tcp(msg, rdev->wowlan->tcp))
+ if (nl80211_send_wowlan_tcp(msg,
+ rdev->wiphy.wowlan_config->tcp))
goto nla_put_failure;
nla_nest_end(msg, nl_wowlan);
@@ -7801,7 +7785,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
struct cfg80211_wowlan *ntrig;
struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
int err, i;
- bool prev_enabled = rdev->wowlan;
+ bool prev_enabled = rdev->wiphy.wowlan_config;
if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns &&
!rdev->wiphy.wowlan.tcp)
@@ -7809,7 +7793,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) {
cfg80211_rdev_free_wowlan(rdev);
- rdev->wowlan = NULL;
+ rdev->wiphy.wowlan_config = NULL;
goto set_wakeup;
}
@@ -7945,11 +7929,12 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
goto error;
}
cfg80211_rdev_free_wowlan(rdev);
- rdev->wowlan = ntrig;
+ rdev->wiphy.wowlan_config = ntrig;
set_wakeup:
- if (rdev->ops->set_wakeup && prev_enabled != !!rdev->wowlan)
- rdev_set_wakeup(rdev, rdev->wowlan);
+ if (rdev->ops->set_wakeup &&
+ prev_enabled != !!rdev->wiphy.wowlan_config)
+ rdev_set_wakeup(rdev, rdev->wiphy.wowlan_config);
return 0;
error:
@@ -8134,9 +8119,7 @@ static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)
if (wdev->p2p_started)
return 0;
- mutex_lock(&rdev->devlist_mtx);
err = cfg80211_can_add_interface(rdev, wdev->iftype);
- mutex_unlock(&rdev->devlist_mtx);
if (err)
return err;
@@ -8145,9 +8128,7 @@ static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)
return err;
wdev->p2p_started = true;
- mutex_lock(&rdev->devlist_mtx);
rdev->opencount++;
- mutex_unlock(&rdev->devlist_mtx);
return 0;
}
@@ -8163,11 +8144,7 @@ static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info)
if (!rdev->ops->stop_p2p_device)
return -EOPNOTSUPP;
- mutex_lock(&rdev->devlist_mtx);
- mutex_lock(&rdev->sched_scan_mtx);
cfg80211_stop_p2p_device(rdev, wdev);
- mutex_unlock(&rdev->sched_scan_mtx);
- mutex_unlock(&rdev->devlist_mtx);
return 0;
}
@@ -8310,11 +8287,11 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
info->user_ptr[0] = rdev;
} else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV ||
ops->internal_flags & NL80211_FLAG_NEED_WDEV) {
- mutex_lock(&cfg80211_mutex);
+ ASSERT_RTNL();
+
wdev = __cfg80211_wdev_from_attrs(genl_info_net(info),
info->attrs);
if (IS_ERR(wdev)) {
- mutex_unlock(&cfg80211_mutex);
if (rtnl)
rtnl_unlock();
return PTR_ERR(wdev);
@@ -8325,7 +8302,6 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
if (!dev) {
- mutex_unlock(&cfg80211_mutex);
if (rtnl)
rtnl_unlock();
return -EINVAL;
@@ -8339,7 +8315,6 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
if (dev) {
if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
!netif_running(dev)) {
- mutex_unlock(&cfg80211_mutex);
if (rtnl)
rtnl_unlock();
return -ENETDOWN;
@@ -8348,17 +8323,12 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
dev_hold(dev);
} else if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP) {
if (!wdev->p2p_started) {
- mutex_unlock(&cfg80211_mutex);
if (rtnl)
rtnl_unlock();
return -ENETDOWN;
}
}
- cfg80211_lock_rdev(rdev);
-
- mutex_unlock(&cfg80211_mutex);
-
info->user_ptr[0] = rdev;
}
@@ -8368,8 +8338,6 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
struct genl_info *info)
{
- if (info->user_ptr[0])
- cfg80211_unlock_rdev(info->user_ptr[0]);
if (info->user_ptr[1]) {
if (ops->internal_flags & NL80211_FLAG_NEED_WDEV) {
struct wireless_dev *wdev = info->user_ptr[1];
@@ -8391,7 +8359,8 @@ static struct genl_ops nl80211_ops[] = {
.dumpit = nl80211_dump_wiphy,
.policy = nl80211_policy,
/* can be retrieved by unprivileged users */
- .internal_flags = NL80211_FLAG_NEED_WIPHY,
+ .internal_flags = NL80211_FLAG_NEED_WIPHY |
+ NL80211_FLAG_NEED_RTNL,
},
{
.cmd = NL80211_CMD_SET_WIPHY,
@@ -8406,7 +8375,8 @@ static struct genl_ops nl80211_ops[] = {
.dumpit = nl80211_dump_interface,
.policy = nl80211_policy,
/* can be retrieved by unprivileged users */
- .internal_flags = NL80211_FLAG_NEED_WDEV,
+ .internal_flags = NL80211_FLAG_NEED_WDEV |
+ NL80211_FLAG_NEED_RTNL,
},
{
.cmd = NL80211_CMD_SET_INTERFACE,
@@ -8565,6 +8535,7 @@ static struct genl_ops nl80211_ops[] = {
.cmd = NL80211_CMD_GET_REG,
.doit = nl80211_get_reg,
.policy = nl80211_policy,
+ .internal_flags = NL80211_FLAG_NEED_RTNL,
/* can be retrieved by unprivileged users */
},
{
@@ -8572,6 +8543,7 @@ static struct genl_ops nl80211_ops[] = {
.doit = nl80211_set_reg,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_RTNL,
},
{
.cmd = NL80211_CMD_REQ_SET_REG,
@@ -9027,8 +8999,6 @@ static int nl80211_add_scan_req(struct sk_buff *msg,
struct nlattr *nest;
int i;
- lockdep_assert_held(&rdev->sched_scan_mtx);
-
if (WARN_ON(!req))
return 0;
@@ -9970,6 +9940,7 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
(netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
netdev->ifindex)) ||
+ nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
(sig_dbm &&
nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
@@ -10010,6 +9981,7 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
(netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
netdev->ifindex)) ||
+ nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) ||
nla_put(msg, NL80211_ATTR_FRAME, len, buf) ||
nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie) ||
(ack && nla_put_flag(msg, NL80211_ATTR_ACK)))
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index cc35fbaa457..e1d6749234c 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -81,7 +81,10 @@ static struct regulatory_request core_request_world = {
.country_ie_env = ENVIRON_ANY,
};
-/* Receipt of information from last regulatory request */
+/*
+ * Receipt of information from last regulatory request,
+ * protected by RTNL (and can be accessed with RCU protection)
+ */
static struct regulatory_request __rcu *last_request =
(void __rcu *)&core_request_world;
@@ -96,39 +99,25 @@ static struct device_type reg_device_type = {
* Central wireless core regulatory domains, we only need two,
* the current one and a world regulatory domain in case we have no
* information to give us an alpha2.
+ * (protected by RTNL, can be read under RCU)
*/
const struct ieee80211_regdomain __rcu *cfg80211_regdomain;
/*
- * Protects static reg.c components:
- * - cfg80211_regdomain (if not used with RCU)
- * - cfg80211_world_regdom
- * - last_request (if not used with RCU)
- * - reg_num_devs_support_basehint
- */
-static DEFINE_MUTEX(reg_mutex);
-
-/*
* Number of devices that registered to the core
* that support cellular base station regulatory hints
+ * (protected by RTNL)
*/
static int reg_num_devs_support_basehint;
-static inline void assert_reg_lock(void)
-{
- lockdep_assert_held(&reg_mutex);
-}
-
static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
{
- return rcu_dereference_protected(cfg80211_regdomain,
- lockdep_is_held(&reg_mutex));
+ return rtnl_dereference(cfg80211_regdomain);
}
static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)
{
- return rcu_dereference_protected(wiphy->regd,
- lockdep_is_held(&reg_mutex));
+ return rtnl_dereference(wiphy->regd);
}
static void rcu_free_regdom(const struct ieee80211_regdomain *r)
@@ -140,8 +129,7 @@ static void rcu_free_regdom(const struct ieee80211_regdomain *r)
static struct regulatory_request *get_last_request(void)
{
- return rcu_dereference_check(last_request,
- lockdep_is_held(&reg_mutex));
+ return rcu_dereference_rtnl(last_request);
}
/* Used to queue up regulatory hints */
@@ -200,6 +188,7 @@ static const struct ieee80211_regdomain world_regdom = {
}
};
+/* protected by RTNL */
static const struct ieee80211_regdomain *cfg80211_world_regdom =
&world_regdom;
@@ -215,7 +204,7 @@ static void reset_regdomains(bool full_reset,
const struct ieee80211_regdomain *r;
struct regulatory_request *lr;
- assert_reg_lock();
+ ASSERT_RTNL();
r = get_cfg80211_regdom();
@@ -377,7 +366,7 @@ static void reg_regdb_search(struct work_struct *work)
const struct ieee80211_regdomain *curdom, *regdom = NULL;
int i;
- mutex_lock(&cfg80211_mutex);
+ rtnl_lock();
mutex_lock(&reg_regdb_search_mutex);
while (!list_empty(&reg_regdb_search_list)) {
@@ -402,7 +391,7 @@ static void reg_regdb_search(struct work_struct *work)
if (!IS_ERR_OR_NULL(regdom))
set_regdom(regdom);
- mutex_unlock(&cfg80211_mutex);
+ rtnl_unlock();
}
static DECLARE_WORK(reg_regdb_work, reg_regdb_search);
@@ -936,13 +925,7 @@ static bool reg_request_cell_base(struct regulatory_request *request)
bool reg_last_request_cell_base(void)
{
- bool val;
-
- mutex_lock(&reg_mutex);
- val = reg_request_cell_base(get_last_request());
- mutex_unlock(&reg_mutex);
-
- return val;
+ return reg_request_cell_base(get_last_request());
}
#ifdef CONFIG_CFG80211_CERTIFICATION_ONUS
@@ -1225,7 +1208,7 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
struct cfg80211_registered_device *rdev;
struct wiphy *wiphy;
- assert_cfg80211_lock();
+ ASSERT_RTNL();
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
wiphy = &rdev->wiphy;
@@ -1444,8 +1427,6 @@ static void reg_set_request_processed(void)
* what it believes should be the current regulatory domain.
*
* Returns one of the different reg request treatment values.
- *
- * Caller must hold &reg_mutex
*/
static enum reg_request_treatment
__regulatory_hint(struct wiphy *wiphy,
@@ -1570,21 +1551,19 @@ static void reg_process_pending_hints(void)
{
struct regulatory_request *reg_request, *lr;
- mutex_lock(&cfg80211_mutex);
- mutex_lock(&reg_mutex);
lr = get_last_request();
/* When last_request->processed becomes true this will be rescheduled */
if (lr && !lr->processed) {
REG_DBG_PRINT("Pending regulatory request, waiting for it to be processed...\n");
- goto out;
+ return;
}
spin_lock(&reg_requests_lock);
if (list_empty(&reg_requests_list)) {
spin_unlock(&reg_requests_lock);
- goto out;
+ return;
}
reg_request = list_first_entry(&reg_requests_list,
@@ -1595,10 +1574,6 @@ static void reg_process_pending_hints(void)
spin_unlock(&reg_requests_lock);
reg_process_hint(reg_request, reg_request->initiator);
-
-out:
- mutex_unlock(&reg_mutex);
- mutex_unlock(&cfg80211_mutex);
}
/* Processes beacon hints -- this has nothing to do with country IEs */
@@ -1607,9 +1582,6 @@ static void reg_process_pending_beacon_hints(void)
struct cfg80211_registered_device *rdev;
struct reg_beacon *pending_beacon, *tmp;
- mutex_lock(&cfg80211_mutex);
- mutex_lock(&reg_mutex);
-
/* This goes through the _pending_ beacon list */
spin_lock_bh(&reg_pending_beacons_lock);
@@ -1626,14 +1598,14 @@ static void reg_process_pending_beacon_hints(void)
}
spin_unlock_bh(&reg_pending_beacons_lock);
- mutex_unlock(&reg_mutex);
- mutex_unlock(&cfg80211_mutex);
}
static void reg_todo(struct work_struct *work)
{
+ rtnl_lock();
reg_process_pending_hints();
reg_process_pending_beacon_hints();
+ rtnl_unlock();
}
static void queue_regulatory_request(struct regulatory_request *request)
@@ -1717,29 +1689,23 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
}
EXPORT_SYMBOL(regulatory_hint);
-/*
- * We hold wdev_lock() here so we cannot hold cfg80211_mutex() and
- * therefore cannot iterate over the rdev list here.
- */
void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band,
const u8 *country_ie, u8 country_ie_len)
{
char alpha2[2];
enum environment_cap env = ENVIRON_ANY;
- struct regulatory_request *request, *lr;
-
- mutex_lock(&reg_mutex);
- lr = get_last_request();
-
- if (unlikely(!lr))
- goto out;
+ struct regulatory_request *request = NULL, *lr;
/* IE len must be evenly divisible by 2 */
if (country_ie_len & 0x01)
- goto out;
+ return;
if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
- goto out;
+ return;
+
+ request = kzalloc(sizeof(*request), GFP_KERNEL);
+ if (!request)
+ return;
alpha2[0] = country_ie[0];
alpha2[1] = country_ie[1];
@@ -1749,19 +1715,21 @@ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band,
else if (country_ie[2] == 'O')
env = ENVIRON_OUTDOOR;
+ rcu_read_lock();
+ lr = get_last_request();
+
+ if (unlikely(!lr))
+ goto out;
+
/*
* We will run this only upon a successful connection on cfg80211.
* We leave conflict resolution to the workqueue, where can hold
- * cfg80211_mutex.
+ * the RTNL.
*/
if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
lr->wiphy_idx != WIPHY_IDX_INVALID)
goto out;
- request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
- if (!request)
- goto out;
-
request->wiphy_idx = get_wiphy_idx(wiphy);
request->alpha2[0] = alpha2[0];
request->alpha2[1] = alpha2[1];
@@ -1769,8 +1737,10 @@ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band,
request->country_ie_env = env;
queue_regulatory_request(request);
+ request = NULL;
out:
- mutex_unlock(&reg_mutex);
+ kfree(request);
+ rcu_read_unlock();
}
static void restore_alpha2(char *alpha2, bool reset_user)
@@ -1858,8 +1828,7 @@ static void restore_regulatory_settings(bool reset_user)
LIST_HEAD(tmp_reg_req_list);
struct cfg80211_registered_device *rdev;
- mutex_lock(&cfg80211_mutex);
- mutex_lock(&reg_mutex);
+ ASSERT_RTNL();
reset_regdomains(true, &world_regdom);
restore_alpha2(alpha2, reset_user);
@@ -1914,9 +1883,6 @@ static void restore_regulatory_settings(bool reset_user)
list_splice_tail_init(&tmp_reg_req_list, &reg_requests_list);
spin_unlock(&reg_requests_lock);
- mutex_unlock(&reg_mutex);
- mutex_unlock(&cfg80211_mutex);
-
REG_DBG_PRINT("Kicking the queue\n");
schedule_work(&reg_work);
@@ -2231,7 +2197,6 @@ int set_regdom(const struct ieee80211_regdomain *rd)
struct regulatory_request *lr;
int r;
- mutex_lock(&reg_mutex);
lr = get_last_request();
/* Note that this doesn't update the wiphys, this is done below */
@@ -2241,14 +2206,12 @@ int set_regdom(const struct ieee80211_regdomain *rd)
reg_set_request_processed();
kfree(rd);
- goto out;
+ return r;
}
/* This would make this whole thing pointless */
- if (WARN_ON(!lr->intersect && rd != get_cfg80211_regdom())) {
- r = -EINVAL;
- goto out;
- }
+ if (WARN_ON(!lr->intersect && rd != get_cfg80211_regdom()))
+ return -EINVAL;
/* update all wiphys now with the new established regulatory domain */
update_all_wiphy_regulatory(lr->initiator);
@@ -2259,10 +2222,7 @@ int set_regdom(const struct ieee80211_regdomain *rd)
reg_set_request_processed();
- out:
- mutex_unlock(&reg_mutex);
-
- return r;
+ return 0;
}
int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env)
@@ -2287,23 +2247,17 @@ int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env)
void wiphy_regulatory_register(struct wiphy *wiphy)
{
- mutex_lock(&reg_mutex);
-
if (!reg_dev_ignore_cell_hint(wiphy))
reg_num_devs_support_basehint++;
wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE);
-
- mutex_unlock(&reg_mutex);
}
-/* Caller must hold cfg80211_mutex */
void wiphy_regulatory_deregister(struct wiphy *wiphy)
{
struct wiphy *request_wiphy = NULL;
struct regulatory_request *lr;
- mutex_lock(&reg_mutex);
lr = get_last_request();
if (!reg_dev_ignore_cell_hint(wiphy))
@@ -2316,12 +2270,10 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy)
request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
if (!request_wiphy || request_wiphy != wiphy)
- goto out;
+ return;
lr->wiphy_idx = WIPHY_IDX_INVALID;
lr->country_ie_env = ENVIRON_ANY;
-out:
- mutex_unlock(&reg_mutex);
}
static void reg_timeout_work(struct work_struct *work)
@@ -2385,9 +2337,9 @@ void regulatory_exit(void)
cancel_delayed_work_sync(&reg_timeout);
/* Lock to suppress warnings */
- mutex_lock(&reg_mutex);
+ rtnl_lock();
reset_regdomains(true, NULL);
- mutex_unlock(&reg_mutex);
+ rtnl_unlock();
dev_set_uevent_suppress(&reg_pdev->dev, true);
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index fd99ea495b7..dd01b58fa78 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -169,7 +169,7 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)
union iwreq_data wrqu;
#endif
- lockdep_assert_held(&rdev->sched_scan_mtx);
+ ASSERT_RTNL();
request = rdev->scan_req;
@@ -230,9 +230,9 @@ void __cfg80211_scan_done(struct work_struct *wk)
rdev = container_of(wk, struct cfg80211_registered_device,
scan_done_wk);
- mutex_lock(&rdev->sched_scan_mtx);
+ rtnl_lock();
___cfg80211_scan_done(rdev, false);
- mutex_unlock(&rdev->sched_scan_mtx);
+ rtnl_unlock();
}
void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
@@ -241,6 +241,7 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req);
request->aborted = aborted;
+ request->notified = true;
queue_work(cfg80211_wq, &wiphy_to_dev(request->wiphy)->scan_done_wk);
}
EXPORT_SYMBOL(cfg80211_scan_done);
@@ -255,7 +256,7 @@ void __cfg80211_sched_scan_results(struct work_struct *wk)
request = rdev->sched_scan_req;
- mutex_lock(&rdev->sched_scan_mtx);
+ rtnl_lock();
/* we don't have sched_scan_req anymore if the scan is stopping */
if (request) {
@@ -270,7 +271,7 @@ void __cfg80211_sched_scan_results(struct work_struct *wk)
nl80211_send_sched_scan_results(rdev, request->dev);
}
- mutex_unlock(&rdev->sched_scan_mtx);
+ rtnl_unlock();
}
void cfg80211_sched_scan_results(struct wiphy *wiphy)
@@ -289,9 +290,9 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy)
trace_cfg80211_sched_scan_stopped(wiphy);
- mutex_lock(&rdev->sched_scan_mtx);
+ rtnl_lock();
__cfg80211_stop_sched_scan(rdev, true);
- mutex_unlock(&rdev->sched_scan_mtx);
+ rtnl_unlock();
}
EXPORT_SYMBOL(cfg80211_sched_scan_stopped);
@@ -300,7 +301,7 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
{
struct net_device *dev;
- lockdep_assert_held(&rdev->sched_scan_mtx);
+ ASSERT_RTNL();
if (!rdev->sched_scan_req)
return -ENOENT;
@@ -1040,6 +1041,25 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
EXPORT_SYMBOL(cfg80211_unlink_bss);
#ifdef CONFIG_CFG80211_WEXT
+static struct cfg80211_registered_device *
+cfg80211_get_dev_from_ifindex(struct net *net, int ifindex)
+{
+ struct cfg80211_registered_device *rdev;
+ struct net_device *dev;
+
+ ASSERT_RTNL();
+
+ dev = dev_get_by_index(net, ifindex);
+ if (!dev)
+ return ERR_PTR(-ENODEV);
+ if (dev->ieee80211_ptr)
+ rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy);
+ else
+ rdev = ERR_PTR(-ENODEV);
+ dev_put(dev);
+ return rdev;
+}
+
int cfg80211_wext_siwscan(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
@@ -1062,7 +1082,6 @@ int cfg80211_wext_siwscan(struct net_device *dev,
if (IS_ERR(rdev))
return PTR_ERR(rdev);
- mutex_lock(&rdev->sched_scan_mtx);
if (rdev->scan_req) {
err = -EBUSY;
goto out;
@@ -1169,9 +1188,7 @@ int cfg80211_wext_siwscan(struct net_device *dev,
dev_hold(dev);
}
out:
- mutex_unlock(&rdev->sched_scan_mtx);
kfree(creq);
- cfg80211_unlock_rdev(rdev);
return err;
}
EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan);
@@ -1470,10 +1487,8 @@ int cfg80211_wext_giwscan(struct net_device *dev,
if (IS_ERR(rdev))
return PTR_ERR(rdev);
- if (rdev->scan_req) {
- res = -EAGAIN;
- goto out;
- }
+ if (rdev->scan_req)
+ return -EAGAIN;
res = ieee80211_scan_results(rdev, info, extra, data->length);
data->length = 0;
@@ -1482,8 +1497,6 @@ int cfg80211_wext_giwscan(struct net_device *dev,
res = 0;
}
- out:
- cfg80211_unlock_rdev(rdev);
return res;
}
EXPORT_SYMBOL_GPL(cfg80211_wext_giwscan);
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index a9dc5c736df..81be95f3be7 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -43,35 +43,29 @@ static bool cfg80211_is_all_idle(void)
struct wireless_dev *wdev;
bool is_all_idle = true;
- mutex_lock(&cfg80211_mutex);
-
/*
* All devices must be idle as otherwise if you are actively
* scanning some new beacon hints could be learned and would
* count as new regulatory hints.
*/
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
- cfg80211_lock_rdev(rdev);
list_for_each_entry(wdev, &rdev->wdev_list, list) {
wdev_lock(wdev);
if (wdev->sme_state != CFG80211_SME_IDLE)
is_all_idle = false;
wdev_unlock(wdev);
}
- cfg80211_unlock_rdev(rdev);
}
- mutex_unlock(&cfg80211_mutex);
-
return is_all_idle;
}
static void disconnect_work(struct work_struct *work)
{
- if (!cfg80211_is_all_idle())
- return;
-
- regulatory_hint_disconnect();
+ rtnl_lock();
+ if (cfg80211_is_all_idle())
+ regulatory_hint_disconnect();
+ rtnl_unlock();
}
static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
@@ -85,7 +79,6 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
ASSERT_RTNL();
ASSERT_RDEV_LOCK(rdev);
ASSERT_WDEV_LOCK(wdev);
- lockdep_assert_held(&rdev->sched_scan_mtx);
if (rdev->scan_req)
return -EBUSY;
@@ -176,13 +169,13 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
case CFG80211_CONN_AUTHENTICATE_NEXT:
BUG_ON(!rdev->ops->auth);
wdev->conn->state = CFG80211_CONN_AUTHENTICATING;
- return __cfg80211_mlme_auth(rdev, wdev->netdev,
- params->channel, params->auth_type,
- params->bssid,
- params->ssid, params->ssid_len,
- NULL, 0,
- params->key, params->key_len,
- params->key_idx, NULL, 0);
+ return cfg80211_mlme_auth(rdev, wdev->netdev,
+ params->channel, params->auth_type,
+ params->bssid,
+ params->ssid, params->ssid_len,
+ NULL, 0,
+ params->key, params->key_len,
+ params->key_idx, NULL, 0);
case CFG80211_CONN_ASSOCIATE_NEXT:
BUG_ON(!rdev->ops->assoc);
wdev->conn->state = CFG80211_CONN_ASSOCIATING;
@@ -198,19 +191,19 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
req.vht_capa = params->vht_capa;
req.vht_capa_mask = params->vht_capa_mask;
- err = __cfg80211_mlme_assoc(rdev, wdev->netdev, params->channel,
- params->bssid, params->ssid,
- params->ssid_len, &req);
+ err = cfg80211_mlme_assoc(rdev, wdev->netdev, params->channel,
+ params->bssid, params->ssid,
+ params->ssid_len, &req);
if (err)
- __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
- NULL, 0,
- WLAN_REASON_DEAUTH_LEAVING,
- false);
+ cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
+ NULL, 0,
+ WLAN_REASON_DEAUTH_LEAVING,
+ false);
return err;
case CFG80211_CONN_DEAUTH_ASSOC_FAIL:
- __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
- NULL, 0,
- WLAN_REASON_DEAUTH_LEAVING, false);
+ cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
+ NULL, 0,
+ WLAN_REASON_DEAUTH_LEAVING, false);
/* return an error so that we call __cfg80211_connect_result() */
return -EINVAL;
default:
@@ -226,11 +219,11 @@ void cfg80211_conn_work(struct work_struct *work)
u8 bssid_buf[ETH_ALEN], *bssid = NULL;
rtnl_lock();
- cfg80211_lock_rdev(rdev);
- mutex_lock(&rdev->devlist_mtx);
- mutex_lock(&rdev->sched_scan_mtx);
list_for_each_entry(wdev, &rdev->wdev_list, list) {
+ if (!wdev->netdev)
+ continue;
+
wdev_lock(wdev);
if (!netif_running(wdev->netdev)) {
wdev_unlock(wdev);
@@ -253,9 +246,6 @@ void cfg80211_conn_work(struct work_struct *work)
wdev_unlock(wdev);
}
- mutex_unlock(&rdev->sched_scan_mtx);
- mutex_unlock(&rdev->devlist_mtx);
- cfg80211_unlock_rdev(rdev);
rtnl_unlock();
}
@@ -770,11 +760,11 @@ void cfg80211_disconnected(struct net_device *dev, u16 reason,
}
EXPORT_SYMBOL(cfg80211_disconnected);
-int __cfg80211_connect(struct cfg80211_registered_device *rdev,
- struct net_device *dev,
- struct cfg80211_connect_params *connect,
- struct cfg80211_cached_keys *connkeys,
- const u8 *prev_bssid)
+int cfg80211_connect(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_connect_params *connect,
+ struct cfg80211_cached_keys *connkeys,
+ const u8 *prev_bssid)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_bss *bss = NULL;
@@ -921,27 +911,8 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
}
}
-int cfg80211_connect(struct cfg80211_registered_device *rdev,
- struct net_device *dev,
- struct cfg80211_connect_params *connect,
- struct cfg80211_cached_keys *connkeys)
-{
- int err;
-
- mutex_lock(&rdev->devlist_mtx);
- /* might request scan - scan_mtx -> wdev_mtx dependency */
- mutex_lock(&rdev->sched_scan_mtx);
- wdev_lock(dev->ieee80211_ptr);
- err = __cfg80211_connect(rdev, dev, connect, connkeys, NULL);
- wdev_unlock(dev->ieee80211_ptr);
- mutex_unlock(&rdev->sched_scan_mtx);
- mutex_unlock(&rdev->devlist_mtx);
-
- return err;
-}
-
-int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
- struct net_device *dev, u16 reason, bool wextev)
+int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u16 reason, bool wextev)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
@@ -961,7 +932,7 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
/* was it connected by userspace SME? */
if (!wdev->conn) {
cfg80211_mlme_down(rdev, dev);
- return 0;
+ goto disconnect;
}
if (wdev->sme_state == CFG80211_SME_CONNECTING &&
@@ -976,7 +947,7 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
}
/* wdev->conn->params.bssid must be set if > SCANNING */
- err = __cfg80211_mlme_deauth(rdev, dev,
+ err = cfg80211_mlme_deauth(rdev, dev,
wdev->conn->params.bssid,
NULL, 0, reason, false);
if (err)
@@ -987,6 +958,7 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
return err;
}
+ disconnect:
if (wdev->sme_state == CFG80211_SME_CONNECTED)
__cfg80211_disconnected(dev, NULL, 0, 0, false);
else if (wdev->sme_state == CFG80211_SME_CONNECTING)
@@ -997,19 +969,6 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
return 0;
}
-int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
- struct net_device *dev,
- u16 reason, bool wextev)
-{
- int err;
-
- wdev_lock(dev->ieee80211_ptr);
- err = __cfg80211_disconnect(rdev, dev, reason, wextev);
- wdev_unlock(dev->ieee80211_ptr);
-
- return err;
-}
-
void cfg80211_sme_disassoc(struct net_device *dev,
struct cfg80211_internal_bss *bss)
{
@@ -1032,6 +991,6 @@ void cfg80211_sme_disassoc(struct net_device *dev,
memcpy(bssid, bss->pub.bssid, ETH_ALEN);
- __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0,
- WLAN_REASON_DEAUTH_LEAVING, false);
+ cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0,
+ WLAN_REASON_DEAUTH_LEAVING, false);
}
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
index 8f28b9f798d..360a42c6f69 100644
--- a/net/wireless/sysfs.c
+++ b/net/wireless/sysfs.c
@@ -91,6 +91,7 @@ static void cfg80211_leave_all(struct cfg80211_registered_device *rdev)
cfg80211_leave(rdev, wdev);
}
+#ifdef CONFIG_PM
static int wiphy_suspend(struct device *dev, pm_message_t state)
{
struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
@@ -100,10 +101,10 @@ static int wiphy_suspend(struct device *dev, pm_message_t state)
rtnl_lock();
if (rdev->wiphy.registered) {
- if (!rdev->wowlan)
+ if (!rdev->wiphy.wowlan_config)
cfg80211_leave_all(rdev);
if (rdev->ops->suspend)
- ret = rdev_suspend(rdev, rdev->wowlan);
+ ret = rdev_suspend(rdev, rdev->wiphy.wowlan_config);
if (ret == 1) {
/* Driver refuse to configure wowlan */
cfg80211_leave_all(rdev);
@@ -132,6 +133,7 @@ static int wiphy_resume(struct device *dev)
return ret;
}
+#endif
static const void *wiphy_namespace(struct device *d)
{
@@ -146,8 +148,10 @@ struct class ieee80211_class = {
.dev_release = wiphy_dev_release,
.dev_attrs = ieee80211_dev_attrs,
.dev_uevent = wiphy_uevent,
+#ifdef CONFIG_PM
.suspend = wiphy_suspend,
.resume = wiphy_resume,
+#endif
.ns_type = &net_ns_type_operations,
.namespace = wiphy_namespace,
};
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index ecd4fcec3c9..23fafeae8a1 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -1911,12 +1911,12 @@ TRACE_EVENT(cfg80211_send_rx_assoc,
NETDEV_PR_ARG, MAC_PR_ARG(bssid), CHAN_PR_ARG)
);
-DEFINE_EVENT(netdev_evt_only, __cfg80211_send_deauth,
+DEFINE_EVENT(netdev_evt_only, cfg80211_send_deauth,
TP_PROTO(struct net_device *netdev),
TP_ARGS(netdev)
);
-DEFINE_EVENT(netdev_evt_only, __cfg80211_send_disassoc,
+DEFINE_EVENT(netdev_evt_only, cfg80211_send_disassoc,
TP_PROTO(struct net_device *netdev),
TP_ARGS(netdev)
);
@@ -2441,6 +2441,7 @@ TRACE_EVENT(cfg80211_report_wowlan_wakeup,
TP_STRUCT__entry(
WIPHY_ENTRY
WDEV_ENTRY
+ __field(bool, non_wireless)
__field(bool, disconnect)
__field(bool, magic_pkt)
__field(bool, gtk_rekey_failure)
@@ -2449,20 +2450,22 @@ TRACE_EVENT(cfg80211_report_wowlan_wakeup,
__field(bool, rfkill_release)
__field(s32, pattern_idx)
__field(u32, packet_len)
- __dynamic_array(u8, packet, wakeup->packet_present_len)
+ __dynamic_array(u8, packet,
+ wakeup ? wakeup->packet_present_len : 0)
),
TP_fast_assign(
WIPHY_ASSIGN;
WDEV_ASSIGN;
- __entry->disconnect = wakeup->disconnect;
- __entry->magic_pkt = wakeup->magic_pkt;
- __entry->gtk_rekey_failure = wakeup->gtk_rekey_failure;
- __entry->eap_identity_req = wakeup->eap_identity_req;
- __entry->four_way_handshake = wakeup->four_way_handshake;
- __entry->rfkill_release = wakeup->rfkill_release;
- __entry->pattern_idx = wakeup->pattern_idx;
- __entry->packet_len = wakeup->packet_len;
- if (wakeup->packet && wakeup->packet_present_len)
+ __entry->non_wireless = !wakeup;
+ __entry->disconnect = wakeup ? wakeup->disconnect : false;
+ __entry->magic_pkt = wakeup ? wakeup->magic_pkt : false;
+ __entry->gtk_rekey_failure = wakeup ? wakeup->gtk_rekey_failure : false;
+ __entry->eap_identity_req = wakeup ? wakeup->eap_identity_req : false;
+ __entry->four_way_handshake = wakeup ? wakeup->four_way_handshake : false;
+ __entry->rfkill_release = wakeup ? wakeup->rfkill_release : false;
+ __entry->pattern_idx = wakeup ? wakeup->pattern_idx : false;
+ __entry->packet_len = wakeup ? wakeup->packet_len : false;
+ if (wakeup && wakeup->packet && wakeup->packet_present_len)
memcpy(__get_dynamic_array(packet), wakeup->packet,
wakeup->packet_present_len);
),
diff --git a/net/wireless/util.c b/net/wireless/util.c
index f5ad4d94ba8..74458b7f61e 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -33,6 +33,29 @@ ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
}
EXPORT_SYMBOL(ieee80211_get_response_rate);
+u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband)
+{
+ struct ieee80211_rate *bitrates;
+ u32 mandatory_rates = 0;
+ enum ieee80211_rate_flags mandatory_flag;
+ int i;
+
+ if (WARN_ON(!sband))
+ return 1;
+
+ if (sband->band == IEEE80211_BAND_2GHZ)
+ mandatory_flag = IEEE80211_RATE_MANDATORY_B;
+ else
+ mandatory_flag = IEEE80211_RATE_MANDATORY_A;
+
+ bitrates = sband->bitrates;
+ for (i = 0; i < sband->n_bitrates; i++)
+ if (bitrates[i].flags & mandatory_flag)
+ mandatory_rates |= BIT(i);
+ return mandatory_rates;
+}
+EXPORT_SYMBOL(ieee80211_mandatory_rates);
+
int ieee80211_channel_to_frequency(int chan, enum ieee80211_band band)
{
/* see 802.11 17.3.8.3.2 and Annex J
@@ -785,12 +808,8 @@ void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev)
ASSERT_RTNL();
ASSERT_RDEV_LOCK(rdev);
- mutex_lock(&rdev->devlist_mtx);
-
list_for_each_entry(wdev, &rdev->wdev_list, list)
cfg80211_process_wdev_events(wdev);
-
- mutex_unlock(&rdev->devlist_mtx);
}
int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
@@ -822,10 +841,8 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
return -EBUSY;
if (ntype != otype && netif_running(dev)) {
- mutex_lock(&rdev->devlist_mtx);
err = cfg80211_can_change_interface(rdev, dev->ieee80211_ptr,
ntype);
- mutex_unlock(&rdev->devlist_mtx);
if (err)
return err;
@@ -841,8 +858,10 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
break;
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
+ wdev_lock(dev->ieee80211_ptr);
cfg80211_disconnect(rdev, dev,
WLAN_REASON_DEAUTH_LEAVING, true);
+ wdev_unlock(dev->ieee80211_ptr);
break;
case NL80211_IFTYPE_MESH_POINT:
/* mesh should be handled? */
@@ -1169,6 +1188,9 @@ bool ieee80211_operating_class_to_band(u8 operating_class,
case 84:
*band = IEEE80211_BAND_2GHZ;
return true;
+ case 180:
+ *band = IEEE80211_BAND_60GHZ;
+ return true;
}
return false;
@@ -1184,8 +1206,6 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
if (!beacon_int)
return -EINVAL;
- mutex_lock(&rdev->devlist_mtx);
-
list_for_each_entry(wdev, &rdev->wdev_list, list) {
if (!wdev->beacon_interval)
continue;
@@ -1195,8 +1215,6 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
}
}
- mutex_unlock(&rdev->devlist_mtx);
-
return res;
}
@@ -1220,7 +1238,6 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
int i, j;
ASSERT_RTNL();
- lockdep_assert_held(&rdev->devlist_mtx);
if (WARN_ON(hweight32(radar_detect) > 1))
return -EINVAL;
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index d997d0f0c54..e7c6e862580 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -72,7 +72,6 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
struct cfg80211_registered_device *rdev;
struct vif_params vifparams;
enum nl80211_iftype type;
- int ret;
rdev = wiphy_to_dev(wdev->wiphy);
@@ -98,11 +97,7 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
memset(&vifparams, 0, sizeof(vifparams));
- cfg80211_lock_rdev(rdev);
- ret = cfg80211_change_iface(rdev, dev, type, NULL, &vifparams);
- cfg80211_unlock_rdev(rdev);
-
- return ret;
+ return cfg80211_change_iface(rdev, dev, type, NULL, &vifparams);
}
EXPORT_SYMBOL_GPL(cfg80211_wext_siwmode);
@@ -579,13 +574,10 @@ static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
{
int err;
- /* devlist mutex needed for possible IBSS re-join */
- mutex_lock(&rdev->devlist_mtx);
wdev_lock(dev->ieee80211_ptr);
err = __cfg80211_set_encryption(rdev, dev, pairwise, addr,
remove, tx_key, idx, params);
wdev_unlock(dev->ieee80211_ptr);
- mutex_unlock(&rdev->devlist_mtx);
return err;
}
@@ -787,7 +779,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
struct cfg80211_chan_def chandef = {
.width = NL80211_CHAN_WIDTH_20_NOHT,
};
- int freq, err;
+ int freq;
switch (wdev->iftype) {
case NL80211_IFTYPE_STATION:
@@ -804,10 +796,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
if (!chandef.chan)
return -EINVAL;
- mutex_lock(&rdev->devlist_mtx);
- err = cfg80211_set_monitor_channel(rdev, &chandef);
- mutex_unlock(&rdev->devlist_mtx);
- return err;
+ return cfg80211_set_monitor_channel(rdev, &chandef);
case NL80211_IFTYPE_MESH_POINT:
freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
if (freq < 0)
@@ -818,10 +807,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
if (!chandef.chan)
return -EINVAL;
- mutex_lock(&rdev->devlist_mtx);
- err = cfg80211_set_mesh_channel(rdev, wdev, &chandef);
- mutex_unlock(&rdev->devlist_mtx);
- return err;
+ return cfg80211_set_mesh_channel(rdev, wdev, &chandef);
default:
return -EOPNOTSUPP;
}
diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c
index e79cb5c0655..a53f8404f45 100644
--- a/net/wireless/wext-sme.c
+++ b/net/wireless/wext-sme.c
@@ -54,8 +54,8 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
if (wdev->wext.prev_bssid_valid)
prev_bssid = wdev->wext.prev_bssid;
- err = __cfg80211_connect(rdev, wdev->netdev,
- &wdev->wext.connect, ck, prev_bssid);
+ err = cfg80211_connect(rdev, wdev->netdev,
+ &wdev->wext.connect, ck, prev_bssid);
if (err)
kfree(ck);
@@ -87,9 +87,6 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
return -EINVAL;
}
- cfg80211_lock_rdev(rdev);
- mutex_lock(&rdev->devlist_mtx);
- mutex_lock(&rdev->sched_scan_mtx);
wdev_lock(wdev);
if (wdev->sme_state != CFG80211_SME_IDLE) {
@@ -103,8 +100,8 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
/* if SSID set, we'll try right again, avoid event */
if (wdev->wext.connect.ssid_len)
event = false;
- err = __cfg80211_disconnect(rdev, dev,
- WLAN_REASON_DEAUTH_LEAVING, event);
+ err = cfg80211_disconnect(rdev, dev,
+ WLAN_REASON_DEAUTH_LEAVING, event);
if (err)
goto out;
}
@@ -136,9 +133,6 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
err = cfg80211_mgd_wext_connect(rdev, wdev);
out:
wdev_unlock(wdev);
- mutex_unlock(&rdev->sched_scan_mtx);
- mutex_unlock(&rdev->devlist_mtx);
- cfg80211_unlock_rdev(rdev);
return err;
}
@@ -190,9 +184,6 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev,
if (len > 0 && ssid[len - 1] == '\0')
len--;
- cfg80211_lock_rdev(rdev);
- mutex_lock(&rdev->devlist_mtx);
- mutex_lock(&rdev->sched_scan_mtx);
wdev_lock(wdev);
err = 0;
@@ -208,8 +199,8 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev,
/* if SSID set now, we'll try to connect, avoid event */
if (len)
event = false;
- err = __cfg80211_disconnect(rdev, dev,
- WLAN_REASON_DEAUTH_LEAVING, event);
+ err = cfg80211_disconnect(rdev, dev,
+ WLAN_REASON_DEAUTH_LEAVING, event);
if (err)
goto out;
}
@@ -226,9 +217,6 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev,
err = cfg80211_mgd_wext_connect(rdev, wdev);
out:
wdev_unlock(wdev);
- mutex_unlock(&rdev->sched_scan_mtx);
- mutex_unlock(&rdev->devlist_mtx);
- cfg80211_unlock_rdev(rdev);
return err;
}
@@ -287,9 +275,6 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev,
if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
bssid = NULL;
- cfg80211_lock_rdev(rdev);
- mutex_lock(&rdev->devlist_mtx);
- mutex_lock(&rdev->sched_scan_mtx);
wdev_lock(wdev);
if (wdev->sme_state != CFG80211_SME_IDLE) {
@@ -303,8 +288,8 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev,
ether_addr_equal(bssid, wdev->wext.connect.bssid))
goto out;
- err = __cfg80211_disconnect(rdev, dev,
- WLAN_REASON_DEAUTH_LEAVING, false);
+ err = cfg80211_disconnect(rdev, dev,
+ WLAN_REASON_DEAUTH_LEAVING, false);
if (err)
goto out;
}
@@ -318,9 +303,6 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev,
err = cfg80211_mgd_wext_connect(rdev, wdev);
out:
wdev_unlock(wdev);
- mutex_unlock(&rdev->sched_scan_mtx);
- mutex_unlock(&rdev->devlist_mtx);
- cfg80211_unlock_rdev(rdev);
return err;
}
@@ -383,8 +365,8 @@ int cfg80211_wext_siwgenie(struct net_device *dev,
wdev->wext.ie_len = ie_len;
if (wdev->sme_state != CFG80211_SME_IDLE) {
- err = __cfg80211_disconnect(rdev, dev,
- WLAN_REASON_DEAUTH_LEAVING, false);
+ err = cfg80211_disconnect(rdev, dev,
+ WLAN_REASON_DEAUTH_LEAVING, false);
if (err)
goto out;
}
@@ -420,8 +402,7 @@ int cfg80211_wext_siwmlme(struct net_device *dev,
switch (mlme->cmd) {
case IW_MLME_DEAUTH:
case IW_MLME_DISASSOC:
- err = __cfg80211_disconnect(rdev, dev, mlme->reason_code,
- true);
+ err = cfg80211_disconnect(rdev, dev, mlme->reason_code, true);
break;
default:
err = -EOPNOTSUPP;