提交 77443951 编写于 作者: J Johannes Berg

Merge remote-tracking branch 'wireless-next/master' into iwlwifi-next

...@@ -132,9 +132,7 @@ ...@@ -132,9 +132,7 @@
!Finclude/net/cfg80211.h cfg80211_send_rx_assoc !Finclude/net/cfg80211.h cfg80211_send_rx_assoc
!Finclude/net/cfg80211.h cfg80211_send_assoc_timeout !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_deauth
!Finclude/net/cfg80211.h cfg80211_send_disassoc !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_ibss_joined
!Finclude/net/cfg80211.h cfg80211_connect_result !Finclude/net/cfg80211.h cfg80211_connect_result
!Finclude/net/cfg80211.h cfg80211_roamed !Finclude/net/cfg80211.h cfg80211_roamed
......
...@@ -2299,6 +2299,11 @@ M: Jaya Kumar <jayakumar.alsa@gmail.com> ...@@ -2299,6 +2299,11 @@ M: Jaya Kumar <jayakumar.alsa@gmail.com>
S: Maintained S: Maintained
F: sound/pci/cs5535audio/ F: sound/pci/cs5535audio/
CW1200 WLAN driver
M: Solomon Peachy <pizza@shaftnet.org>
S: Maintained
F: drivers/net/wireless/cw1200/
CX18 VIDEO4LINUX DRIVER CX18 VIDEO4LINUX DRIVER
M: Andy Walls <awalls@md.metrocast.net> M: Andy Walls <awalls@md.metrocast.net>
L: ivtv-devel@ivtvdriver.org (moderated for non-subscribers) L: ivtv-devel@ivtvdriver.org (moderated for non-subscribers)
......
...@@ -72,12 +72,12 @@ static int bcma_fill_sprom_with_fallback(struct bcma_bus *bus, ...@@ -72,12 +72,12 @@ static int bcma_fill_sprom_with_fallback(struct bcma_bus *bus,
* R/W ops. * 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; int i;
for (i = 0; i < SSB_SPROMSIZE_WORDS_R4; i++) for (i = 0; i < words; i++)
sprom[i] = bcma_read16(bus->drv_cc.core, sprom[i] = bcma_read16(bus->drv_cc.core, offset + (i * 2));
offset + (i * 2));
} }
/************************************************** /**************************************************
...@@ -124,29 +124,29 @@ static inline u8 bcma_crc8(u8 crc, u8 data) ...@@ -124,29 +124,29 @@ static inline u8 bcma_crc8(u8 crc, u8 data)
return t[crc ^ 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; int word;
u8 crc = 0xFF; 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] & 0x00FF);
crc = bcma_crc8(crc, (sprom[word] & 0xFF00) >> 8); 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; crc ^= 0xFF;
return crc; 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 crc;
u8 expected_crc; u8 expected_crc;
u16 tmp; u16 tmp;
crc = bcma_sprom_crc(sprom); crc = bcma_sprom_crc(sprom, words);
tmp = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_CRC; tmp = sprom[words - 1] & SSB_SPROM_REVISION_CRC;
expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT; expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT;
if (crc != expected_crc) if (crc != expected_crc)
return -EPROTO; return -EPROTO;
...@@ -154,21 +154,25 @@ static int bcma_sprom_check_crc(const u16 *sprom) ...@@ -154,21 +154,25 @@ static int bcma_sprom_check_crc(const u16 *sprom)
return 0; 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; u16 revision;
int err; int err;
err = bcma_sprom_check_crc(sprom); err = bcma_sprom_check_crc(sprom, words);
if (err) if (err)
return err; return err;
revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_REV; revision = sprom[words - 1] & SSB_SPROM_REVISION_REV;
if (revision != 8 && revision != 9) { if (revision != 8 && revision != 9 && revision != 10) {
pr_err("Unsupported SPROM revision: %d\n", revision); pr_err("Unsupported SPROM revision: %d\n", revision);
return -ENOENT; return -ENOENT;
} }
bus->sprom.revision = revision;
bcma_debug(bus, "Found SPROM revision %d\n", revision);
return 0; return 0;
} }
...@@ -208,9 +212,6 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom) ...@@ -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) != BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
ARRAY_SIZE(bus->sprom.core_pwr_info)); 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++) { for (i = 0; i < 3; i++) {
v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i]; v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i];
*(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v); *(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v);
...@@ -502,7 +503,6 @@ static bool bcma_sprom_onchip_available(struct bcma_bus *bus) ...@@ -502,7 +503,6 @@ static bool bcma_sprom_onchip_available(struct bcma_bus *bus)
case BCMA_CHIP_ID_BCM4331: case BCMA_CHIP_ID_BCM4331:
present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT; present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT;
break; break;
case BCMA_CHIP_ID_BCM43224: case BCMA_CHIP_ID_BCM43224:
case BCMA_CHIP_ID_BCM43225: case BCMA_CHIP_ID_BCM43225:
/* for these chips OTP is always available */ /* for these chips OTP is always available */
...@@ -550,7 +550,9 @@ int bcma_sprom_get(struct bcma_bus *bus) ...@@ -550,7 +550,9 @@ int bcma_sprom_get(struct bcma_bus *bus)
{ {
u16 offset = BCMA_CC_SPROM; u16 offset = BCMA_CC_SPROM;
u16 *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) if (!bus->drv_cc.core)
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -579,32 +581,37 @@ int bcma_sprom_get(struct bcma_bus *bus) ...@@ -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 || if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 ||
bus->chipinfo.id == BCMA_CHIP_ID_BCM43431) bus->chipinfo.id == BCMA_CHIP_ID_BCM43431)
bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false); bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false);
bcma_debug(bus, "SPROM offset 0x%x\n", offset); 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 || if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 ||
bus->chipinfo.id == BCMA_CHIP_ID_BCM43431) bus->chipinfo.id == BCMA_CHIP_ID_BCM43431)
bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true); bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true);
err = bcma_sprom_valid(sprom);
if (err) { 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); 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; return err;
} }
...@@ -280,5 +280,6 @@ source "drivers/net/wireless/rtlwifi/Kconfig" ...@@ -280,5 +280,6 @@ source "drivers/net/wireless/rtlwifi/Kconfig"
source "drivers/net/wireless/ti/Kconfig" source "drivers/net/wireless/ti/Kconfig"
source "drivers/net/wireless/zd1211rw/Kconfig" source "drivers/net/wireless/zd1211rw/Kconfig"
source "drivers/net/wireless/mwifiex/Kconfig" source "drivers/net/wireless/mwifiex/Kconfig"
source "drivers/net/wireless/cw1200/Kconfig"
endif # WLAN endif # WLAN
...@@ -57,3 +57,5 @@ obj-$(CONFIG_MWIFIEX) += mwifiex/ ...@@ -57,3 +57,5 @@ obj-$(CONFIG_MWIFIEX) += mwifiex/
obj-$(CONFIG_BRCMFMAC) += brcm80211/ obj-$(CONFIG_BRCMFMAC) += brcm80211/
obj-$(CONFIG_BRCMSMAC) += brcm80211/ obj-$(CONFIG_BRCMSMAC) += brcm80211/
obj-$(CONFIG_CW1200) += cw1200/
...@@ -84,14 +84,6 @@ config ATH9K_DFS_CERTIFIED ...@@ -84,14 +84,6 @@ config ATH9K_DFS_CERTIFIED
developed. At this point enabling this option won't do anything developed. At this point enabling this option won't do anything
except increase code size. 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 config ATH9K_RATE_CONTROL
bool "Atheros ath9k rate control" bool "Atheros ath9k rate control"
depends on ATH9K depends on ATH9K
......
...@@ -454,6 +454,8 @@ static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain) ...@@ -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) if (accum_cnt <= thresh_accum_cnt)
continue; continue;
max_index++;
/* sum(tx amplitude) */ /* sum(tx amplitude) */
accum_tx = ((data_L[i] >> 16) & 0xffff) | accum_tx = ((data_L[i] >> 16) & 0xffff) |
((data_U[i] & 0x7ff) << 16); ((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) ...@@ -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_tx <<= scale_factor;
accum_rx <<= scale_factor; accum_rx <<= scale_factor;
x_est[i + 1] = (((accum_tx + accum_cnt) / accum_cnt) + 32) >> x_est[max_index] =
scale_factor; (((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) + scale_factor) +
(1 << scale_factor) * max_index + 16; (1 << scale_factor) * i + 16;
if (accum_ang >= (1 << 26)) if (accum_ang >= (1 << 26))
accum_ang -= 1 << 27; accum_ang -= 1 << 27;
theta[i + 1] = ((accum_ang * (1 << scale_factor)) + accum_cnt) / theta[max_index] =
accum_cnt; ((accum_ang * (1 << scale_factor)) + accum_cnt) /
accum_cnt;
max_index++;
} }
/* /*
......
...@@ -646,6 +646,7 @@ enum sc_op_flags { ...@@ -646,6 +646,7 @@ enum sc_op_flags {
SC_OP_ANI_RUN, SC_OP_ANI_RUN,
SC_OP_PRIM_STA_VIF, SC_OP_PRIM_STA_VIF,
SC_OP_HW_RESET, SC_OP_HW_RESET,
SC_OP_SCANNING,
}; };
/* Powersave flags */ /* Powersave flags */
...@@ -759,7 +760,6 @@ struct ath_softc { ...@@ -759,7 +760,6 @@ struct ath_softc {
struct rchan *rfs_chan_spec_scan; struct rchan *rfs_chan_spec_scan;
enum spectral_mode spectral_mode; enum spectral_mode spectral_mode;
struct ath_spec_scan spec_config; struct ath_spec_scan spec_config;
int scanning;
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
atomic_t wow_got_bmiss_intr; atomic_t wow_got_bmiss_intr;
......
...@@ -39,7 +39,8 @@ static void ath9k_beaconq_config(struct ath_softc *sc) ...@@ -39,7 +39,8 @@ static void ath9k_beaconq_config(struct ath_softc *sc)
ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi); 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. */ /* Always burst out beacon and CAB traffic. */
qi.tqi_aifs = 1; qi.tqi_aifs = 1;
qi.tqi_cwmin = 0; qi.tqi_cwmin = 0;
...@@ -273,7 +274,8 @@ static int ath9k_beacon_choose_slot(struct ath_softc *sc) ...@@ -273,7 +274,8 @@ static int ath9k_beacon_choose_slot(struct ath_softc *sc)
u64 tsf; u64 tsf;
int slot; 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", ath_dbg(common, BEACON, "slot 0, tsf: %llu\n",
ath9k_hw_gettsf64(sc->sc_ah)); ath9k_hw_gettsf64(sc->sc_ah));
return 0; return 0;
...@@ -765,10 +767,10 @@ void ath9k_set_beacon(struct ath_softc *sc) ...@@ -765,10 +767,10 @@ void ath9k_set_beacon(struct ath_softc *sc)
switch (sc->sc_ah->opmode) { switch (sc->sc_ah->opmode) {
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_MESH_POINT:
ath9k_beacon_config_ap(sc, cur_conf); ath9k_beacon_config_ap(sc, cur_conf);
break; break;
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_MESH_POINT:
ath9k_beacon_config_adhoc(sc, cur_conf); ath9k_beacon_config_adhoc(sc, cur_conf);
break; break;
case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_STATION:
......
...@@ -738,8 +738,6 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, ...@@ -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, struct ath_tx_status *ts, struct ath_txq *txq,
unsigned int flags) 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; int qnum = txq->axq_qnum;
TX_STAT_INC(qnum, tx_pkts_all); TX_STAT_INC(qnum, tx_pkts_all);
...@@ -771,37 +769,6 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, ...@@ -771,37 +769,6 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf,
TX_STAT_INC(qnum, data_underrun); TX_STAT_INC(qnum, data_underrun);
if (ts->ts_flags & ATH9K_TX_DELIM_UNDERRUN) if (ts->ts_flags & ATH9K_TX_DELIM_UNDERRUN)
TX_STAT_INC(qnum, 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 = { static const struct file_operations fops_xmit = {
...@@ -915,8 +882,6 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, ...@@ -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) 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_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); RX_STAT_INC(rx_pkts_all);
sc->debug.stats.rxstats.rx_bytes_all += rs->rs_datalen; 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) ...@@ -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); 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_PHY_ERR_INC
#undef RX_SAMP_DBG
} }
static const struct file_operations fops_recv = { static const struct file_operations fops_recv = {
...@@ -1485,283 +1430,6 @@ static const struct file_operations fops_modal_eeprom = { ...@@ -1485,283 +1430,6 @@ static const struct file_operations fops_modal_eeprom = {
.llseek = default_llseek, .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 #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
static ssize_t read_file_btcoex(struct file *file, char __user *user_buf, static ssize_t read_file_btcoex(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
...@@ -2087,11 +1755,6 @@ int ath9k_init_debug(struct ath_hw *ah) ...@@ -2087,11 +1755,6 @@ int ath9k_init_debug(struct ath_hw *ah)
debugfs_create_file("spectral_fft_period", S_IRUSR | S_IWUSR, debugfs_create_file("spectral_fft_period", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, sc, sc->debug.debugfs_phy, sc,
&fops_spectral_fft_period); &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, debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask); sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask);
debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR, debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR,
......
...@@ -294,13 +294,6 @@ struct ath9k_debug { ...@@ -294,13 +294,6 @@ struct ath9k_debug {
struct dentry *debugfs_phy; struct dentry *debugfs_phy;
u32 regidx; u32 regidx;
struct ath_stats stats; 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); int ath9k_init_debug(struct ath_hw *ah);
...@@ -359,17 +352,4 @@ static inline void ath_debug_stat_rx(struct ath_softc *sc, ...@@ -359,17 +352,4 @@ static inline void ath_debug_stat_rx(struct ath_softc *sc,
#endif /* CONFIG_ATH9K_DEBUGFS */ #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 */ #endif /* DEBUG_H */
...@@ -208,6 +208,9 @@ struct ath9k_htc_target_rx_stats { ...@@ -208,6 +208,9 @@ struct ath9k_htc_target_rx_stats {
case NL80211_IFTYPE_AP: \ case NL80211_IFTYPE_AP: \
_priv->num_ap_vif++; \ _priv->num_ap_vif++; \
break; \ break; \
case NL80211_IFTYPE_MESH_POINT: \
_priv->num_mbss_vif++; \
break; \
default: \ default: \
break; \ break; \
} \ } \
...@@ -224,6 +227,9 @@ struct ath9k_htc_target_rx_stats { ...@@ -224,6 +227,9 @@ struct ath9k_htc_target_rx_stats {
case NL80211_IFTYPE_AP: \ case NL80211_IFTYPE_AP: \
_priv->num_ap_vif--; \ _priv->num_ap_vif--; \
break; \ break; \
case NL80211_IFTYPE_MESH_POINT: \
_priv->num_mbss_vif--; \
break; \
default: \ default: \
break; \ break; \
} \ } \
...@@ -450,6 +456,7 @@ struct ath9k_htc_priv { ...@@ -450,6 +456,7 @@ struct ath9k_htc_priv {
u8 sta_slot; u8 sta_slot;
u8 vif_sta_pos[ATH9K_HTC_MAX_VIF]; u8 vif_sta_pos[ATH9K_HTC_MAX_VIF];
u8 num_ibss_vif; u8 num_ibss_vif;
u8 num_mbss_vif;
u8 num_sta_vif; u8 num_sta_vif;
u8 num_sta_assoc_vif; u8 num_sta_assoc_vif;
u8 num_ap_vif; u8 num_ap_vif;
......
...@@ -28,7 +28,8 @@ void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv) ...@@ -28,7 +28,8 @@ void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv)
ath9k_hw_get_txq_props(ah, priv->beaconq, &qi); 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_aifs = 1;
qi.tqi_cwmin = 0; qi.tqi_cwmin = 0;
qi.tqi_cwmax = 0; qi.tqi_cwmax = 0;
...@@ -628,6 +629,7 @@ void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, ...@@ -628,6 +629,7 @@ void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv,
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
ath9k_htc_beacon_config_adhoc(priv, cur_conf); ath9k_htc_beacon_config_adhoc(priv, cur_conf);
break; break;
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
ath9k_htc_beacon_config_ap(priv, cur_conf); ath9k_htc_beacon_config_ap(priv, cur_conf);
break; break;
...@@ -649,6 +651,7 @@ void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv) ...@@ -649,6 +651,7 @@ void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv)
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
ath9k_htc_beacon_config_adhoc(priv, cur_conf); ath9k_htc_beacon_config_adhoc(priv, cur_conf);
break; break;
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
ath9k_htc_beacon_config_ap(priv, cur_conf); ath9k_htc_beacon_config_ap(priv, cur_conf);
break; break;
......
...@@ -698,7 +698,8 @@ static const struct ieee80211_iface_limit if_limits[] = { ...@@ -698,7 +698,8 @@ static const struct ieee80211_iface_limit if_limits[] = {
{ .max = 2, .types = BIT(NL80211_IFTYPE_STATION) | { .max = 2, .types = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_P2P_CLIENT) }, BIT(NL80211_IFTYPE_P2P_CLIENT) },
{ .max = 2, .types = BIT(NL80211_IFTYPE_AP) | { .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 = { static const struct ieee80211_iface_combination if_comb = {
...@@ -721,6 +722,7 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, ...@@ -721,6 +722,7 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK | IEEE80211_HW_PS_NULLFUNC_STACK |
IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_REPORTS_TX_ACK_STATUS |
IEEE80211_HW_MFP_CAPABLE |
IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING; IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING;
hw->wiphy->interface_modes = hw->wiphy->interface_modes =
...@@ -728,7 +730,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, ...@@ -728,7 +730,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_P2P_GO) | 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->iface_combinations = &if_comb;
hw->wiphy->n_iface_combinations = 1; hw->wiphy->n_iface_combinations = 1;
......
...@@ -113,7 +113,9 @@ static void ath9k_htc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) ...@@ -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 ath9k_htc_priv *priv = data;
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; 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; priv->reconfig_beacon = true;
if (bss_conf->assoc) { if (bss_conf->assoc) {
...@@ -180,6 +182,8 @@ static void ath9k_htc_set_opmode(struct ath9k_htc_priv *priv) ...@@ -180,6 +182,8 @@ static void ath9k_htc_set_opmode(struct ath9k_htc_priv *priv)
priv->ah->opmode = NL80211_IFTYPE_ADHOC; priv->ah->opmode = NL80211_IFTYPE_ADHOC;
else if (priv->num_ap_vif) else if (priv->num_ap_vif)
priv->ah->opmode = NL80211_IFTYPE_AP; priv->ah->opmode = NL80211_IFTYPE_AP;
else if (priv->num_mbss_vif)
priv->ah->opmode = NL80211_IFTYPE_MESH_POINT;
else else
priv->ah->opmode = NL80211_IFTYPE_STATION; priv->ah->opmode = NL80211_IFTYPE_STATION;
...@@ -1052,6 +1056,9 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw, ...@@ -1052,6 +1056,9 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
hvif.opmode = HTC_M_HOSTAP; hvif.opmode = HTC_M_HOSTAP;
break; break;
case NL80211_IFTYPE_MESH_POINT:
hvif.opmode = HTC_M_WDS; /* close enough */
break;
default: default:
ath_err(common, ath_err(common,
"Interface type %d not yet supported\n", vif->type); "Interface type %d not yet supported\n", vif->type);
...@@ -1084,6 +1091,7 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw, ...@@ -1084,6 +1091,7 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
INC_VIF(priv, vif->type); INC_VIF(priv, vif->type);
if ((vif->type == NL80211_IFTYPE_AP) || if ((vif->type == NL80211_IFTYPE_AP) ||
(vif->type == NL80211_IFTYPE_MESH_POINT) ||
(vif->type == NL80211_IFTYPE_ADHOC)) (vif->type == NL80211_IFTYPE_ADHOC))
ath9k_htc_assign_bslot(priv, vif); ath9k_htc_assign_bslot(priv, vif);
...@@ -1134,6 +1142,7 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw, ...@@ -1134,6 +1142,7 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
DEC_VIF(priv, vif->type); DEC_VIF(priv, vif->type);
if ((vif->type == NL80211_IFTYPE_AP) || if ((vif->type == NL80211_IFTYPE_AP) ||
vif->type == NL80211_IFTYPE_MESH_POINT ||
(vif->type == NL80211_IFTYPE_ADHOC)) (vif->type == NL80211_IFTYPE_ADHOC))
ath9k_htc_remove_bslot(priv, vif); ath9k_htc_remove_bslot(priv, vif);
...@@ -1525,9 +1534,10 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, ...@@ -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) { if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) {
/* /*
* Disable SWBA interrupt only if there are no * 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, ath_dbg(common, CONFIG,
"Beacon disabled for BSS: %pM\n", "Beacon disabled for BSS: %pM\n",
bss_conf->bssid); bss_conf->bssid);
...@@ -1538,12 +1548,15 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, ...@@ -1538,12 +1548,15 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_BEACON_INT) { 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) && if (priv->nvifs == 1 &&
(priv->nvifs == 1) && ((priv->ah->opmode == NL80211_IFTYPE_AP &&
(priv->num_ap_vif == 1) && vif->type == 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); set_bit(OP_TSF_RESET, &priv->op_flags);
} }
ath_dbg(common, CONFIG, ath_dbg(common, CONFIG,
......
...@@ -887,7 +887,7 @@ u32 ath9k_htc_calcrxfilter(struct ath9k_htc_priv *priv) ...@@ -887,7 +887,7 @@ u32 ath9k_htc_calcrxfilter(struct ath9k_htc_priv *priv)
if (priv->rxfilter & FIF_PSPOLL) if (priv->rxfilter & FIF_PSPOLL)
rfilt |= ATH9K_RX_FILTER_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; rfilt |= ATH9K_RX_FILTER_MCAST_BCAST_ALL;
return rfilt; return rfilt;
......
...@@ -1245,10 +1245,10 @@ static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode) ...@@ -1245,10 +1245,10 @@ static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode)
switch (opmode) { switch (opmode) {
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_MESH_POINT:
set |= AR_STA_ID1_ADHOC; set |= AR_STA_ID1_ADHOC;
REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION);
break; break;
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
set |= AR_STA_ID1_STA_AP; set |= AR_STA_ID1_STA_AP;
/* fall through */ /* fall through */
...@@ -2246,12 +2246,12 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period) ...@@ -2246,12 +2246,12 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period)
switch (ah->opmode) { switch (ah->opmode) {
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_MESH_POINT:
REG_SET_BIT(ah, AR_TXCFG, REG_SET_BIT(ah, AR_TXCFG,
AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY); AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY);
REG_WRITE(ah, AR_NEXT_NDP_TIMER, next_beacon + REG_WRITE(ah, AR_NEXT_NDP_TIMER, next_beacon +
TU_TO_USEC(ah->atim_window ? ah->atim_window : 1)); TU_TO_USEC(ah->atim_window ? ah->atim_window : 1));
flags |= AR_NDP_TIMER_EN; flags |= AR_NDP_TIMER_EN;
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
REG_WRITE(ah, AR_NEXT_TBTT_TIMER, next_beacon); REG_WRITE(ah, AR_NEXT_TBTT_TIMER, next_beacon);
REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, next_beacon - REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, next_beacon -
......
...@@ -613,9 +613,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, ...@@ -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_serial_rw);
spin_lock_init(&sc->sc_pm_lock); spin_lock_init(&sc->sc_pm_lock);
mutex_init(&sc->mutex); 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->intr_tq, ath9k_tasklet, (unsigned long)sc);
tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet, tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet,
(unsigned long)sc); (unsigned long)sc);
......
...@@ -418,7 +418,6 @@ void ath_ani_calibrate(unsigned long data) ...@@ -418,7 +418,6 @@ void ath_ani_calibrate(unsigned long data)
longcal ? "long" : "", shortcal ? "short" : "", longcal ? "long" : "", shortcal ? "short" : "",
aniflag ? "ani" : "", common->ani.caldone ? "true" : "false"); aniflag ? "ani" : "", common->ani.caldone ? "true" : "false");
ath9k_debug_samp_bb_mac(sc);
ath9k_ps_restore(sc); ath9k_ps_restore(sc);
set_timer: set_timer:
......
...@@ -193,7 +193,6 @@ static bool ath_prepare_reset(struct ath_softc *sc) ...@@ -193,7 +193,6 @@ static bool ath_prepare_reset(struct ath_softc *sc)
ath_stop_ani(sc); ath_stop_ani(sc);
del_timer_sync(&sc->rx_poll_timer); del_timer_sync(&sc->rx_poll_timer);
ath9k_debug_samp_bb_mac(sc);
ath9k_hw_disable_interrupts(ah); ath9k_hw_disable_interrupts(ah);
if (!ath_drain_all_txq(sc)) if (!ath_drain_all_txq(sc))
...@@ -1273,7 +1272,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) ...@@ -1273,7 +1272,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
curchan->center_freq); curchan->center_freq);
} else { } else {
/* perform spectral scan if requested. */ /* perform spectral scan if requested. */
if (sc->scanning && if (test_bit(SC_OP_SCANNING, &sc->sc_flags) &&
sc->spectral_mode == SPECTRAL_CHANSCAN) sc->spectral_mode == SPECTRAL_CHANSCAN)
ath9k_spectral_scan_trigger(hw); ath9k_spectral_scan_trigger(hw);
} }
...@@ -1689,7 +1688,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, ...@@ -1689,7 +1688,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
struct ath_softc *sc = hw->priv; struct ath_softc *sc = hw->priv;
int ret = 0; int ret = 0;
local_bh_disable(); mutex_lock(&sc->mutex);
switch (action) { switch (action) {
case IEEE80211_AMPDU_RX_START: case IEEE80211_AMPDU_RX_START:
...@@ -1720,7 +1719,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, ...@@ -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"); ath_err(ath9k_hw_common(sc->sc_ah), "Unknown AMPDU action\n");
} }
local_bh_enable(); mutex_unlock(&sc->mutex);
return ret; return ret;
} }
...@@ -2339,15 +2338,13 @@ static void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled) ...@@ -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) static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
{ {
struct ath_softc *sc = hw->priv; struct ath_softc *sc = hw->priv;
set_bit(SC_OP_SCANNING, &sc->sc_flags);
sc->scanning = 1;
} }
static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
{ {
struct ath_softc *sc = hw->priv; struct ath_softc *sc = hw->priv;
clear_bit(SC_OP_SCANNING, &sc->sc_flags);
sc->scanning = 0;
} }
struct ieee80211_ops ath9k_ops = { struct ieee80211_ops ath9k_ops = {
......
...@@ -27,3 +27,15 @@ config WIL6210_ISR_COR ...@@ -27,3 +27,15 @@ config WIL6210_ISR_COR
self-clear when accessed for debug purposes, it makes self-clear when accessed for debug purposes, it makes
such monitoring impossible. such monitoring impossible.
Say y unless you debug interrupts 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.
obj-$(CONFIG_WIL6210) += wil6210.o obj-$(CONFIG_WIL6210) += wil6210.o
wil6210-objs := main.o wil6210-y := main.o
wil6210-objs += netdev.o wil6210-y += netdev.o
wil6210-objs += cfg80211.o wil6210-y += cfg80211.o
wil6210-objs += pcie_bus.o wil6210-y += pcie_bus.o
wil6210-objs += debugfs.o wil6210-y += debugfs.o
wil6210-objs += wmi.o wil6210-y += wmi.o
wil6210-objs += interrupt.o wil6210-y += interrupt.o
wil6210-objs += txrx.o wil6210-y += txrx.o
wil6210-y += debug.o
wil6210-$(CONFIG_WIL6210_TRACING) += trace.o
ifeq (, $(findstring -W,$(EXTRA_CFLAGS))) ifeq (, $(findstring -W,$(EXTRA_CFLAGS)))
subdir-ccflags-y += -Werror subdir-ccflags-y += -Werror
endif endif
# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)
subdir-ccflags-y += -D__CHECK_ENDIAN__ subdir-ccflags-y += -D__CHECK_ENDIAN__
...@@ -322,12 +322,16 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, ...@@ -322,12 +322,16 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
* FW don't support scan after connection attempt * FW don't support scan after connection attempt
*/ */
set_bit(wil_status_dontscan, &wil->status); set_bit(wil_status_dontscan, &wil->status);
set_bit(wil_status_fwconnecting, &wil->status);
rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn)); rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn));
if (rc == 0) { if (rc == 0) {
/* Connect can take lots of time */ /* Connect can take lots of time */
mod_timer(&wil->connect_timer, mod_timer(&wil->connect_timer,
jiffies + msecs_to_jiffies(2000)); jiffies + msecs_to_jiffies(2000));
} else {
clear_bit(wil_status_dontscan, &wil->status);
clear_bit(wil_status_fwconnecting, &wil->status);
} }
out: out:
......
/*
* 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;
}
...@@ -418,9 +418,15 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data) ...@@ -418,9 +418,15 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
if (skb) { if (skb) {
unsigned char printbuf[16 * 3 + 2]; unsigned char printbuf[16 * 3 + 2];
int i = 0; int i = 0;
int len = skb_headlen(skb); int len = le16_to_cpu(d->dma.length);
void *p = skb->data; 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); seq_printf(s, " len = %d\n", len);
while (i < len) { while (i < len) {
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include "wil6210.h" #include "wil6210.h"
#include "trace.h"
/** /**
* Theory of operation: * Theory of operation:
...@@ -103,14 +104,14 @@ static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil) ...@@ -103,14 +104,14 @@ static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil)
clear_bit(wil_status_irqen, &wil->status); 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 + iowrite32(WIL6210_IMC_TX, wil->csr +
HOSTADDR(RGF_DMA_EP_TX_ICR) + HOSTADDR(RGF_DMA_EP_TX_ICR) +
offsetof(struct RGF_ICR, IMC)); 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 + iowrite32(WIL6210_IMC_RX, wil->csr +
HOSTADDR(RGF_DMA_EP_RX_ICR) + HOSTADDR(RGF_DMA_EP_RX_ICR) +
...@@ -168,6 +169,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie) ...@@ -168,6 +169,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
HOSTADDR(RGF_DMA_EP_RX_ICR) + HOSTADDR(RGF_DMA_EP_RX_ICR) +
offsetof(struct RGF_ICR, ICR)); offsetof(struct RGF_ICR, ICR));
trace_wil6210_irq_rx(isr);
wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr); wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr);
if (!isr) { if (!isr) {
...@@ -180,13 +182,14 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie) ...@@ -180,13 +182,14 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) { if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) {
wil_dbg_irq(wil, "RX done\n"); wil_dbg_irq(wil, "RX done\n");
isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE; 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) if (isr)
wil_err(wil, "un-handled RX ISR bits 0x%08x\n", 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; return IRQ_HANDLED;
} }
...@@ -198,6 +201,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie) ...@@ -198,6 +201,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
HOSTADDR(RGF_DMA_EP_TX_ICR) + HOSTADDR(RGF_DMA_EP_TX_ICR) +
offsetof(struct RGF_ICR, ICR)); offsetof(struct RGF_ICR, ICR));
trace_wil6210_irq_tx(isr);
wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr); wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr);
if (!isr) { if (!isr) {
...@@ -208,23 +212,17 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie) ...@@ -208,23 +212,17 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
wil6210_mask_irq_tx(wil); wil6210_mask_irq_tx(wil);
if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) { if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) {
uint i;
wil_dbg_irq(wil, "TX done\n"); wil_dbg_irq(wil, "TX done\n");
napi_schedule(&wil->napi_tx);
isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE; isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE;
for (i = 0; i < 24; i++) { /* clear also all VRING interrupts */
u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i); isr &= ~(BIT(25) - 1UL);
if (isr & mask) {
isr &= ~mask;
wil_dbg_irq(wil, "TX done(%i)\n", i);
wil_tx_complete(wil, i);
}
}
} }
if (isr) if (isr)
wil_err(wil, "un-handled TX ISR bits 0x%08x\n", 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; return IRQ_HANDLED;
} }
...@@ -256,6 +254,7 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie) ...@@ -256,6 +254,7 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
HOSTADDR(RGF_DMA_EP_MISC_ICR) + HOSTADDR(RGF_DMA_EP_MISC_ICR) +
offsetof(struct RGF_ICR, ICR)); offsetof(struct RGF_ICR, ICR));
trace_wil6210_irq_misc(isr);
wil_dbg_irq(wil, "ISR MISC 0x%08x\n", isr); wil_dbg_irq(wil, "ISR MISC 0x%08x\n", isr);
if (!isr) { if (!isr) {
...@@ -301,6 +300,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) ...@@ -301,6 +300,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
struct wil6210_priv *wil = cookie; struct wil6210_priv *wil = cookie;
u32 isr = wil->isr_misc; u32 isr = wil->isr_misc;
trace_wil6210_irq_misc_thread(isr);
wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr); wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr);
if (isr & ISR_MISC_FW_ERROR) { if (isr & ISR_MISC_FW_ERROR) {
...@@ -408,6 +408,7 @@ static irqreturn_t wil6210_hardirq(int irq, void *cookie) ...@@ -408,6 +408,7 @@ static irqreturn_t wil6210_hardirq(int irq, void *cookie)
if (wil6210_debug_irq_mask(wil, pseudo_cause)) if (wil6210_debug_irq_mask(wil, pseudo_cause))
return IRQ_NONE; return IRQ_NONE;
trace_wil6210_irq_pseudo(pseudo_cause);
wil_dbg_irq(wil, "Pseudo IRQ 0x%08x\n", pseudo_cause); wil_dbg_irq(wil, "Pseudo IRQ 0x%08x\n", pseudo_cause);
wil6210_mask_irq_pseudo(wil); wil6210_mask_irq_pseudo(wil);
......
...@@ -56,27 +56,21 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) ...@@ -56,27 +56,21 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
{ {
uint i; uint i;
struct net_device *ndev = wil_to_ndev(wil); struct net_device *ndev = wil_to_ndev(wil);
struct wireless_dev *wdev = wil->wdev;
wil_dbg_misc(wil, "%s()\n", __func__); wil_dbg_misc(wil, "%s()\n", __func__);
wil_link_off(wil); wil_link_off(wil);
clear_bit(wil_status_fwconnected, &wil->status); if (test_bit(wil_status_fwconnected, &wil->status)) {
clear_bit(wil_status_fwconnected, &wil->status);
switch (wdev->sme_state) { cfg80211_disconnected(ndev,
case CFG80211_SME_CONNECTED: WLAN_STATUS_UNSPECIFIED_FAILURE,
cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE,
NULL, 0, GFP_KERNEL); NULL, 0, GFP_KERNEL);
break; } else if (test_bit(wil_status_fwconnecting, &wil->status)) {
case CFG80211_SME_CONNECTING:
cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE, WLAN_STATUS_UNSPECIFIED_FAILURE,
GFP_KERNEL); GFP_KERNEL);
break;
default:
break;
} }
clear_bit(wil_status_fwconnecting, &wil->status);
for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++)
wil_vring_fini_tx(wil, i); wil_vring_fini_tx(wil, i);
...@@ -365,6 +359,9 @@ static int __wil_up(struct wil6210_priv *wil) ...@@ -365,6 +359,9 @@ static int __wil_up(struct wil6210_priv *wil)
/* Rx VRING. After MAC and beacon */ /* Rx VRING. After MAC and beacon */
wil_rx_init(wil); wil_rx_init(wil);
napi_enable(&wil->napi_rx);
napi_enable(&wil->napi_tx);
return 0; return 0;
} }
...@@ -381,6 +378,9 @@ int wil_up(struct wil6210_priv *wil) ...@@ -381,6 +378,9 @@ int wil_up(struct wil6210_priv *wil)
static int __wil_down(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) { if (wil->scan_request) {
cfg80211_scan_done(wil->scan_request, true); cfg80211_scan_done(wil->scan_request, true);
wil->scan_request = NULL; wil->scan_request = NULL;
......
...@@ -40,6 +40,55 @@ static const struct net_device_ops wil_netdev_ops = { ...@@ -40,6 +40,55 @@ static const struct net_device_ops wil_netdev_ops = {
.ndo_validate_addr = eth_validate_addr, .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) void *wil_if_alloc(struct device *dev, void __iomem *csr)
{ {
struct net_device *ndev; struct net_device *ndev;
...@@ -81,6 +130,11 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr) ...@@ -81,6 +130,11 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)
SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
wdev->netdev = ndev; 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); wil_link_off(wil);
return wil; return wil;
......
/*
* 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"
/*
* 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__) */
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "wil6210.h" #include "wil6210.h"
#include "wmi.h" #include "wmi.h"
#include "txrx.h" #include "txrx.h"
#include "trace.h"
static bool rtap_include_phy_info; static bool rtap_include_phy_info;
module_param(rtap_include_phy_info, bool, S_IRUGO); 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) ...@@ -89,8 +90,8 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
* we can use any * we can use any
*/ */
for (i = 0; i < vring->size; i++) { for (i = 0; i < vring->size; i++) {
volatile struct vring_tx_desc *d = &(vring->va[i].tx); volatile struct vring_tx_desc *_d = &(vring->va[i].tx);
d->dma.status = TX_DMA_STATUS_DU; _d->dma.status = TX_DMA_STATUS_DU;
} }
wil_dbg_misc(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size, 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, ...@@ -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]); size_t sz = vring->size * sizeof(vring->va[0]);
while (!wil_vring_is_empty(vring)) { while (!wil_vring_is_empty(vring)) {
dma_addr_t pa;
struct sk_buff *skb;
u16 dmalen;
if (tx) { 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; &vring->va[vring->swtail].tx;
dma_addr_t pa = d->dma.addr_low |
((u64)d->dma.addr_high << 32); *d = *_d;
struct sk_buff *skb = vring->ctx[vring->swtail]; pa = wil_desc_addr(&d->dma.addr);
dmalen = le16_to_cpu(d->dma.length);
skb = vring->ctx[vring->swtail];
if (skb) { if (skb) {
dma_unmap_single(dev, pa, d->dma.length, dma_unmap_single(dev, pa, dmalen,
DMA_TO_DEVICE); DMA_TO_DEVICE);
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
vring->ctx[vring->swtail] = NULL; vring->ctx[vring->swtail] = NULL;
} else { } else {
dma_unmap_page(dev, pa, d->dma.length, dma_unmap_page(dev, pa, dmalen,
DMA_TO_DEVICE); DMA_TO_DEVICE);
} }
vring->swtail = wil_vring_next_tail(vring); vring->swtail = wil_vring_next_tail(vring);
} else { /* rx */ } 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; &vring->va[vring->swtail].rx;
dma_addr_t pa = d->dma.addr_low |
((u64)d->dma.addr_high << 32); *d = *_d;
struct sk_buff *skb = vring->ctx[vring->swhead]; pa = wil_desc_addr(&d->dma.addr);
dma_unmap_single(dev, pa, d->dma.length, dmalen = le16_to_cpu(d->dma.length);
DMA_FROM_DEVICE); skb = vring->ctx[vring->swhead];
dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE);
kfree_skb(skb); kfree_skb(skb);
wil_vring_advance_head(vring, 1); wil_vring_advance_head(vring, 1);
} }
...@@ -151,7 +161,8 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring, ...@@ -151,7 +161,8 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
{ {
struct device *dev = wil_to_dev(wil); struct device *dev = wil_to_dev(wil);
unsigned int sz = RX_BUF_LEN; 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; dma_addr_t pa;
/* TODO align */ /* TODO align */
...@@ -169,13 +180,13 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring, ...@@ -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.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT;
d->dma.addr_low = lower_32_bits(pa); wil_desc_addr_set(&d->dma.addr, pa);
d->dma.addr_high = (u16)upper_32_bits(pa);
/* ip_length don't care */ /* ip_length don't care */
/* b11 don't care */ /* b11 don't care */
/* error don't care */ /* error don't care */
d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ 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; vring->ctx[i] = skb;
return 0; return 0;
...@@ -321,11 +332,12 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, ...@@ -321,11 +332,12 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
{ {
struct device *dev = wil_to_dev(wil); struct device *dev = wil_to_dev(wil);
struct net_device *ndev = wil_to_ndev(wil); struct net_device *ndev = wil_to_ndev(wil);
volatile struct vring_rx_desc *d; volatile struct vring_rx_desc *_d;
struct vring_rx_desc *d1; struct vring_rx_desc *d;
struct sk_buff *skb; struct sk_buff *skb;
dma_addr_t pa; dma_addr_t pa;
unsigned int sz = RX_BUF_LEN; unsigned int sz = RX_BUF_LEN;
u16 dmalen;
u8 ftype; u8 ftype;
u8 ds_bits; u8 ds_bits;
...@@ -334,32 +346,44 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, ...@@ -334,32 +346,44 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
if (wil_vring_is_empty(vring)) if (wil_vring_is_empty(vring))
return NULL; return NULL;
d = &(vring->va[vring->swhead].rx); _d = &(vring->va[vring->swhead].rx);
if (!(d->dma.status & RX_DMA_STATUS_DU)) { if (!(_d->dma.status & RX_DMA_STATUS_DU)) {
/* it is not error, we just reached end of Rx done area */ /* it is not error, we just reached end of Rx done area */
return NULL; return NULL;
} }
pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
skb = vring->ctx[vring->swhead]; 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); 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); trace_wil6210_rx(vring->swhead, d);
*d1 = *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 */ /* use radiotap header only if required */
if (ndev->type == ARPHRD_IEEE80211_RADIOTAP) if (ndev->type == ARPHRD_IEEE80211_RADIOTAP)
wil_rx_add_radiotap_header(wil, skb); 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 */ /* no extra checks if in sniffer mode */
if (ndev->type != ARPHRD_ETHER) if (ndev->type != ARPHRD_ETHER)
return skb; return skb;
...@@ -368,7 +392,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, ...@@ -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 * 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 * 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) { if (ftype != IEEE80211_FTYPE_DATA) {
wil_dbg_txrx(wil, "Non-data frame ftype 0x%08x\n", ftype); wil_dbg_txrx(wil, "Non-data frame ftype 0x%08x\n", ftype);
/* TODO: process it */ /* TODO: process it */
...@@ -383,7 +407,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, ...@@ -383,7 +407,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
return NULL; return NULL;
} }
ds_bits = wil_rxdesc_ds_bits(d1); ds_bits = wil_rxdesc_ds_bits(d);
if (ds_bits == 1) { if (ds_bits == 1) {
/* /*
* HW bug - in ToDS mode, i.e. Rx on AP side, * 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) ...@@ -425,6 +449,7 @@ static int wil_rx_refill(struct wil6210_priv *wil, int count)
/* /*
* Pass Rx packet to the netif. Update statistics. * 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) 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) ...@@ -433,10 +458,7 @@ static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
skb_orphan(skb); skb_orphan(skb);
if (in_interrupt()) rc = netif_receive_skb(skb);
rc = netif_rx(skb);
else
rc = netif_rx_ni(skb);
if (likely(rc == NET_RX_SUCCESS)) { if (likely(rc == NET_RX_SUCCESS)) {
ndev->stats.rx_packets++; ndev->stats.rx_packets++;
...@@ -450,9 +472,9 @@ static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) ...@@ -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 * 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 net_device *ndev = wil_to_ndev(wil);
struct vring *v = &wil->vring_rx; struct vring *v = &wil->vring_rx;
...@@ -463,9 +485,8 @@ void wil_rx_handle(struct wil6210_priv *wil) ...@@ -463,9 +485,8 @@ void wil_rx_handle(struct wil6210_priv *wil)
return; return;
} }
wil_dbg_txrx(wil, "%s()\n", __func__); wil_dbg_txrx(wil, "%s()\n", __func__);
while (NULL != (skb = wil_vring_reap_rx(wil, v))) { while ((*quota > 0) && (NULL != (skb = wil_vring_reap_rx(wil, v)))) {
wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1, (*quota)--;
skb->data, skb_headlen(skb), false);
if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) {
skb->dev = ndev; skb->dev = ndev;
...@@ -600,17 +621,15 @@ static struct vring *wil_find_tx_vring(struct wil6210_priv *wil, ...@@ -600,17 +621,15 @@ static struct vring *wil_find_tx_vring(struct wil6210_priv *wil,
return NULL; return NULL;
} }
static int wil_tx_desc_map(volatile struct vring_tx_desc *d, static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len)
dma_addr_t pa, u32 len)
{ {
d->dma.addr_low = lower_32_bits(pa); wil_desc_addr_set(&d->dma.addr, pa);
d->dma.addr_high = (u16)upper_32_bits(pa);
d->dma.ip_length = 0; d->dma.ip_length = 0;
/* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/ /* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/
d->dma.b11 = 0/*14 | BIT(7)*/; d->dma.b11 = 0/*14 | BIT(7)*/;
d->dma.error = 0; d->dma.error = 0;
d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ 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->dma.d0 = 0;
d->mac.d[0] = 0; d->mac.d[0] = 0;
d->mac.d[1] = 0; d->mac.d[1] = 0;
...@@ -630,7 +649,8 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, ...@@ -630,7 +649,8 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
struct sk_buff *skb) struct sk_buff *skb)
{ {
struct device *dev = wil_to_dev(wil); 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; u32 swhead = vring->swhead;
int avail = wil_vring_avail_tx(vring); int avail = wil_vring_avail_tx(vring);
int nr_frags = skb_shinfo(skb)->nr_frags; int nr_frags = skb_shinfo(skb)->nr_frags;
...@@ -648,7 +668,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, ...@@ -648,7 +668,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
1 + nr_frags); 1 + nr_frags);
return -ENOMEM; return -ENOMEM;
} }
d = &(vring->va[i].tx); _d = &(vring->va[i].tx);
/* FIXME FW can accept only unicast frames for the peer */ /* FIXME FW can accept only unicast frames for the peer */
memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN); 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, ...@@ -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)); wil_tx_desc_map(d, pa, skb_headlen(skb));
d->mac.d[2] |= ((nr_frags + 1) << d->mac.d[2] |= ((nr_frags + 1) <<
MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS); MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS);
if (nr_frags)
*_d = *d;
/* middle segments */ /* middle segments */
for (f = 0; f < nr_frags; f++) { for (f = 0; f < nr_frags; f++) {
const struct skb_frag_struct *frag = const struct skb_frag_struct *frag =
&skb_shinfo(skb)->frags[f]; &skb_shinfo(skb)->frags[f];
int len = skb_frag_size(frag); int len = skb_frag_size(frag);
i = (swhead + f + 1) % vring->size; 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), pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag),
DMA_TO_DEVICE); DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(dev, pa))) if (unlikely(dma_mapping_error(dev, pa)))
goto dma_error; goto dma_error;
wil_tx_desc_map(d, pa, len); wil_tx_desc_map(d, pa, len);
vring->ctx[i] = NULL; vring->ctx[i] = NULL;
*_d = *d;
} }
/* for the last seg only */ /* for the last seg only */
d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS); d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS);
d->dma.d0 |= BIT(9); /* BUG: undocumented bit */ d->dma.d0 |= BIT(9); /* BUG: undocumented bit */
d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS); 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->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_0_QID_POS);
*_d = *d;
wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4, wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false); (const void *)d, sizeof(*d), false);
...@@ -693,6 +718,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, ...@@ -693,6 +718,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
/* advance swhead */ /* advance swhead */
wil_vring_advance_head(vring, nr_frags + 1); wil_vring_advance_head(vring, nr_frags + 1);
wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead); 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)); iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail));
/* hold reference to skb /* hold reference to skb
* to prevent skb release before accounting * to prevent skb release before accounting
...@@ -705,14 +731,18 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, ...@@ -705,14 +731,18 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
/* unmap what we have mapped */ /* unmap what we have mapped */
/* Note: increment @f to operate with positive index */ /* Note: increment @f to operate with positive index */
for (f++; f > 0; f--) { for (f++; f > 0; f--) {
u16 dmalen;
i = (swhead + f) % vring->size; i = (swhead + f) % vring->size;
d = &(vring->va[i].tx); _d = &(vring->va[i].tx);
d->dma.status = TX_DMA_STATUS_DU; *d = *_d;
pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); _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]) 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 else
dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
} }
return -EINVAL; return -EINVAL;
...@@ -761,7 +791,6 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) ...@@ -761,7 +791,6 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
break; /* goto drop; */ break; /* goto drop; */
} }
drop: drop:
netif_tx_stop_all_queues(ndev);
ndev->stats.tx_dropped++; ndev->stats.tx_dropped++;
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
...@@ -771,41 +800,48 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) ...@@ -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 * Clean up transmitted skb's from the Tx VRING
* *
* Return number of descriptors cleared
*
* Safe to call from IRQ * 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 net_device *ndev = wil_to_ndev(wil);
struct device *dev = wil_to_dev(wil); struct device *dev = wil_to_dev(wil);
struct vring *vring = &wil->vring_tx[ringid]; struct vring *vring = &wil->vring_tx[ringid];
int done = 0;
if (!vring->va) { if (!vring->va) {
wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid); wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid);
return; return 0;
} }
wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid); wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid);
while (!wil_vring_is_empty(vring)) { while (!wil_vring_is_empty(vring)) {
volatile struct vring_tx_desc *d1 = volatile struct vring_tx_desc *_d =
&vring->va[vring->swtail].tx; &vring->va[vring->swtail].tx;
struct vring_tx_desc dd, *d = &dd; struct vring_tx_desc dd, *d = &dd;
dma_addr_t pa; dma_addr_t pa;
struct sk_buff *skb; struct sk_buff *skb;
u16 dmalen;
dd = *d1; *d = *_d;
if (!(d->dma.status & TX_DMA_STATUS_DU)) if (!(d->dma.status & TX_DMA_STATUS_DU))
break; break;
dmalen = le16_to_cpu(d->dma.length);
trace_wil6210_tx_done(ringid, vring->swtail, dmalen,
d->dma.error);
wil_dbg_txrx(wil, wil_dbg_txrx(wil,
"Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n", "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); d->dma.error);
wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4, wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false); (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]; skb = vring->ctx[vring->swtail];
if (skb) { if (skb) {
if (d->dma.error == 0) { if (d->dma.error == 0) {
...@@ -815,18 +851,21 @@ void wil_tx_complete(struct wil6210_priv *wil, int ringid) ...@@ -815,18 +851,21 @@ void wil_tx_complete(struct wil6210_priv *wil, int ringid)
ndev->stats.tx_errors++; 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); dev_kfree_skb_any(skb);
vring->ctx[vring->swtail] = NULL; vring->ctx[vring->swtail] = NULL;
} else { } 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.addr_low = 0;
d->dma.addr_high = 0; d->dma.addr.addr_high = 0;
d->dma.length = 0; d->dma.length = 0;
d->dma.status = TX_DMA_STATUS_DU; d->dma.status = TX_DMA_STATUS_DU;
vring->swtail = wil_vring_next_tail(vring); vring->swtail = wil_vring_next_tail(vring);
done++;
} }
if (wil_vring_avail_tx(vring) > vring->size/4) if (wil_vring_avail_tx(vring) > vring->size/4)
netif_tx_wake_all_queues(wil_to_ndev(wil)); netif_tx_wake_all_queues(wil_to_ndev(wil));
return done;
} }
...@@ -27,6 +27,28 @@ ...@@ -27,6 +27,28 @@
#define WIL6210_RTAP_SIZE (128) #define WIL6210_RTAP_SIZE (128)
/* Tx/Rx path */ /* 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 * Tx descriptor - MAC part
* [dword 0] * [dword 0]
...@@ -216,13 +238,12 @@ struct vring_tx_mac { ...@@ -216,13 +238,12 @@ struct vring_tx_mac {
struct vring_tx_dma { struct vring_tx_dma {
u32 d0; u32 d0;
u32 addr_low; struct vring_dma_addr addr;
u16 addr_high;
u8 ip_length; u8 ip_length;
u8 b11; /* 0..6: mac_length; 7:ip_version */ u8 b11; /* 0..6: mac_length; 7:ip_version */
u8 error; /* 0..2: err; 3..7: reserved; */ u8 error; /* 0..2: err; 3..7: reserved; */
u8 status; /* 0: used; 1..7; reserved */ u8 status; /* 0: used; 1..7; reserved */
u16 length; __le16 length;
} __packed; } __packed;
/* /*
...@@ -315,13 +336,12 @@ struct vring_rx_mac { ...@@ -315,13 +336,12 @@ struct vring_rx_mac {
struct vring_rx_dma { struct vring_rx_dma {
u32 d0; u32 d0;
u32 addr_low; struct vring_dma_addr addr;
u16 addr_high;
u8 ip_length; u8 ip_length;
u8 b11; u8 b11;
u8 error; u8 error;
u8 status; u8 status;
u16 length; __le16 length;
} __packed; } __packed;
struct vring_tx_desc { struct vring_tx_desc {
......
...@@ -34,9 +34,11 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1) ...@@ -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_MEM_SIZE (2*1024*1024UL)
#define WIL6210_RX_RING_SIZE (128) #define WIL6210_RX_RING_SIZE (128)
#define WIL6210_TX_RING_SIZE (128) #define WIL6210_TX_RING_SIZE (128)
#define WIL6210_MAX_TX_RINGS (24) #define WIL6210_MAX_TX_RINGS (24) /* HW limit */
#define WIL6210_MAX_CID (8) /* HW limit */
#define WIL6210_NAPI_BUDGET (16) /* arbitrary */
/* Hardware definitions begin */ /* Hardware definitions begin */
...@@ -184,6 +186,7 @@ struct vring { ...@@ -184,6 +186,7 @@ struct vring {
enum { /* for wil6210_priv.status */ enum { /* for wil6210_priv.status */
wil_status_fwready = 0, wil_status_fwready = 0,
wil_status_fwconnecting,
wil_status_fwconnected, wil_status_fwconnected,
wil_status_dontscan, wil_status_dontscan,
wil_status_reset_done, wil_status_reset_done,
...@@ -239,6 +242,8 @@ struct wil6210_priv { ...@@ -239,6 +242,8 @@ struct wil6210_priv {
* - consumed in thread by wmi_event_worker * - consumed in thread by wmi_event_worker
*/ */
spinlock_t wmi_ev_lock; spinlock_t wmi_ev_lock;
struct napi_struct napi_rx;
struct napi_struct napi_tx;
/* DMA related */ /* DMA related */
struct vring vring_rx; struct vring vring_rx;
struct vring vring_tx[WIL6210_MAX_TX_RINGS]; struct vring vring_tx[WIL6210_MAX_TX_RINGS];
...@@ -267,9 +272,13 @@ struct wil6210_priv { ...@@ -267,9 +272,13 @@ struct wil6210_priv {
#define wil_to_ndev(i) (wil_to_wdev(i)->netdev) #define wil_to_ndev(i) (wil_to_wdev(i)->netdev)
#define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr)) #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) int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...);
#define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg) int wil_err(struct wil6210_priv *wil, const char *fmt, ...);
#define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg) 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_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) #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, ...@@ -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); 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); 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 */ /* 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); int wil_iftype_nl2wmi(enum nl80211_iftype type);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "wil6210.h" #include "wil6210.h"
#include "txrx.h" #include "txrx.h"
#include "wmi.h" #include "wmi.h"
#include "trace.h"
/** /**
* WMI event receiving - theory of operations * WMI event receiving - theory of operations
...@@ -246,6 +247,8 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) ...@@ -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 + iowrite32(r->head = next_head, wil->csr + HOST_MBOX +
offsetof(struct wil6210_mbox_ctl, tx.head)); offsetof(struct wil6210_mbox_ctl, tx.head));
trace_wil6210_wmi_cmd(cmdid, buf, len);
/* interrupt to FW */ /* interrupt to FW */
iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT); 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) ...@@ -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) || if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
(wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { (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"); wil_err(wil, "Not in connecting state\n");
return; return;
} }
...@@ -430,6 +433,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) ...@@ -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); cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL);
} }
clear_bit(wil_status_fwconnecting, &wil->status);
set_bit(wil_status_fwconnected, &wil->status); set_bit(wil_status_fwconnected, &wil->status);
/* FIXME FW can transmit only ucast frames to peer */ /* FIXME FW can transmit only ucast frames to peer */
...@@ -635,8 +639,9 @@ void wmi_recv_cmd(struct wil6210_priv *wil) ...@@ -635,8 +639,9 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
hdr.flags); hdr.flags);
if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) && if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) &&
(len >= sizeof(struct wil6210_mbox_hdr_wmi))) { (len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
wil_dbg_wmi(wil, "WMI event 0x%04x\n", u16 id = le16_to_cpu(evt->event.wmi.id);
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, wil_hex_dump_wmi("evt ", DUMP_PREFIX_OFFSET, 16, 1,
&evt->event.hdr, sizeof(hdr) + len, true); &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) ...@@ -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), .bcon_interval = cpu_to_le16(bi),
.network_type = wmi_nettype, .network_type = wmi_nettype,
.disable_sec_offload = 1, .disable_sec_offload = 1,
.channel = chan, .channel = chan - 1,
}; };
struct { struct {
struct wil6210_mbox_hdr_wmi wmi; struct wil6210_mbox_hdr_wmi wmi;
......
...@@ -606,7 +606,8 @@ static int brcmf_sdio_pd_remove(struct platform_device *pdev) ...@@ -606,7 +606,8 @@ static int brcmf_sdio_pd_remove(struct platform_device *pdev)
static struct platform_driver brcmf_sdio_pd = { static struct platform_driver brcmf_sdio_pd = {
.remove = brcmf_sdio_pd_remove, .remove = brcmf_sdio_pd_remove,
.driver = { .driver = {
.name = BRCMFMAC_SDIO_PDATA_NAME .name = BRCMFMAC_SDIO_PDATA_NAME,
.owner = THIS_MODULE,
} }
}; };
......
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
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
/*
* 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;
}
/*
* 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 */
/*
* 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 */
/*
* 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);
/*
* 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);
/*
* 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);
/*
* 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 */
/*
* 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 */
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
...@@ -1347,14 +1347,6 @@ struct il_rx_mpdu_res_start { ...@@ -1347,14 +1347,6 @@ struct il_rx_mpdu_res_start {
#define TX_CMD_SEC_SHIFT 6 #define TX_CMD_SEC_SHIFT 6
#define TX_CMD_SEC_KEY128 0x08 #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) * C_TX = 0x1c (command)
*/ */
......
...@@ -1220,14 +1220,6 @@ struct iwl_rx_mpdu_res_start { ...@@ -1220,14 +1220,6 @@ struct iwl_rx_mpdu_res_start {
#define TX_CMD_SEC_SHIFT 6 #define TX_CMD_SEC_SHIFT 6
#define TX_CMD_SEC_KEY128 0x08 #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) * REPLY_TX = 0x1c (command)
*/ */
......
...@@ -570,6 +570,7 @@ int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no, ...@@ -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_SYS_CONFIG:
case HostCmd_CMD_UAP_BSS_START: case HostCmd_CMD_UAP_BSS_START:
case HostCmd_CMD_UAP_BSS_STOP: case HostCmd_CMD_UAP_BSS_STOP:
case HostCmd_CMD_UAP_STA_DEAUTH:
ret = mwifiex_uap_prepare_cmd(priv, cmd_no, cmd_action, ret = mwifiex_uap_prepare_cmd(priv, cmd_no, cmd_action,
cmd_oid, data_buf, cmd_oid, data_buf,
cmd_ptr); cmd_ptr);
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册