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

This commit is contained in:
Johannes Berg 2013-05-29 21:56:46 +02:00
commit 774439518a
136 changed files with 18102 additions and 2610 deletions

View File

@ -132,9 +132,7 @@
!Finclude/net/cfg80211.h cfg80211_send_rx_assoc
!Finclude/net/cfg80211.h cfg80211_send_assoc_timeout
!Finclude/net/cfg80211.h cfg80211_send_deauth
!Finclude/net/cfg80211.h __cfg80211_send_deauth
!Finclude/net/cfg80211.h cfg80211_send_disassoc
!Finclude/net/cfg80211.h __cfg80211_send_disassoc
!Finclude/net/cfg80211.h cfg80211_ibss_joined
!Finclude/net/cfg80211.h cfg80211_connect_result
!Finclude/net/cfg80211.h cfg80211_roamed

View File

@ -2299,6 +2299,11 @@ M: Jaya Kumar <jayakumar.alsa@gmail.com>
S: Maintained
F: sound/pci/cs5535audio/
CW1200 WLAN driver
M: Solomon Peachy <pizza@shaftnet.org>
S: Maintained
F: drivers/net/wireless/cw1200/
CX18 VIDEO4LINUX DRIVER
M: Andy Walls <awalls@md.metrocast.net>
L: ivtv-devel@ivtvdriver.org (moderated for non-subscribers)

View File

@ -72,12 +72,12 @@ static int bcma_fill_sprom_with_fallback(struct bcma_bus *bus,
* R/W ops.
**************************************************/
static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom)
static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom,
size_t words)
{
int i;
for (i = 0; i < SSB_SPROMSIZE_WORDS_R4; i++)
sprom[i] = bcma_read16(bus->drv_cc.core,
offset + (i * 2));
for (i = 0; i < words; i++)
sprom[i] = bcma_read16(bus->drv_cc.core, offset + (i * 2));
}
/**************************************************
@ -124,29 +124,29 @@ static inline u8 bcma_crc8(u8 crc, u8 data)
return t[crc ^ data];
}
static u8 bcma_sprom_crc(const u16 *sprom)
static u8 bcma_sprom_crc(const u16 *sprom, size_t words)
{
int word;
u8 crc = 0xFF;
for (word = 0; word < SSB_SPROMSIZE_WORDS_R4 - 1; word++) {
for (word = 0; word < words - 1; word++) {
crc = bcma_crc8(crc, sprom[word] & 0x00FF);
crc = bcma_crc8(crc, (sprom[word] & 0xFF00) >> 8);
}
crc = bcma_crc8(crc, sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & 0x00FF);
crc = bcma_crc8(crc, sprom[words - 1] & 0x00FF);
crc ^= 0xFF;
return crc;
}
static int bcma_sprom_check_crc(const u16 *sprom)
static int bcma_sprom_check_crc(const u16 *sprom, size_t words)
{
u8 crc;
u8 expected_crc;
u16 tmp;
crc = bcma_sprom_crc(sprom);
tmp = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_CRC;
crc = bcma_sprom_crc(sprom, words);
tmp = sprom[words - 1] & SSB_SPROM_REVISION_CRC;
expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT;
if (crc != expected_crc)
return -EPROTO;
@ -154,21 +154,25 @@ static int bcma_sprom_check_crc(const u16 *sprom)
return 0;
}
static int bcma_sprom_valid(const u16 *sprom)
static int bcma_sprom_valid(struct bcma_bus *bus, const u16 *sprom,
size_t words)
{
u16 revision;
int err;
err = bcma_sprom_check_crc(sprom);
err = bcma_sprom_check_crc(sprom, words);
if (err)
return err;
revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_REV;
if (revision != 8 && revision != 9) {
revision = sprom[words - 1] & SSB_SPROM_REVISION_REV;
if (revision != 8 && revision != 9 && revision != 10) {
pr_err("Unsupported SPROM revision: %d\n", revision);
return -ENOENT;
}
bus->sprom.revision = revision;
bcma_debug(bus, "Found SPROM revision %d\n", revision);
return 0;
}
@ -208,9 +212,6 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
ARRAY_SIZE(bus->sprom.core_pwr_info));
bus->sprom.revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] &
SSB_SPROM_REVISION_REV;
for (i = 0; i < 3; i++) {
v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i];
*(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v);
@ -502,7 +503,6 @@ static bool bcma_sprom_onchip_available(struct bcma_bus *bus)
case BCMA_CHIP_ID_BCM4331:
present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT;
break;
case BCMA_CHIP_ID_BCM43224:
case BCMA_CHIP_ID_BCM43225:
/* for these chips OTP is always available */
@ -550,7 +550,9 @@ int bcma_sprom_get(struct bcma_bus *bus)
{
u16 offset = BCMA_CC_SPROM;
u16 *sprom;
int err = 0;
size_t sprom_sizes[] = { SSB_SPROMSIZE_WORDS_R4,
SSB_SPROMSIZE_WORDS_R10, };
int i, err = 0;
if (!bus->drv_cc.core)
return -EOPNOTSUPP;
@ -579,32 +581,37 @@ int bcma_sprom_get(struct bcma_bus *bus)
}
}
sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
GFP_KERNEL);
if (!sprom)
return -ENOMEM;
if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 ||
bus->chipinfo.id == BCMA_CHIP_ID_BCM43431)
bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false);
bcma_debug(bus, "SPROM offset 0x%x\n", offset);
bcma_sprom_read(bus, offset, sprom);
for (i = 0; i < ARRAY_SIZE(sprom_sizes); i++) {
size_t words = sprom_sizes[i];
sprom = kcalloc(words, sizeof(u16), GFP_KERNEL);
if (!sprom)
return -ENOMEM;
bcma_sprom_read(bus, offset, sprom, words);
err = bcma_sprom_valid(bus, sprom, words);
if (!err)
break;
kfree(sprom);
}
if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 ||
bus->chipinfo.id == BCMA_CHIP_ID_BCM43431)
bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true);
err = bcma_sprom_valid(sprom);
if (err) {
bcma_warn(bus, "invalid sprom read from the PCIe card, try to use fallback sprom\n");
bcma_warn(bus, "Invalid SPROM read from the PCIe card, trying to use fallback SPROM\n");
err = bcma_fill_sprom_with_fallback(bus, &bus->sprom);
goto out;
} else {
bcma_sprom_extract_r8(bus, sprom);
kfree(sprom);
}
bcma_sprom_extract_r8(bus, sprom);
out:
kfree(sprom);
return err;
}

View File

@ -280,5 +280,6 @@ source "drivers/net/wireless/rtlwifi/Kconfig"
source "drivers/net/wireless/ti/Kconfig"
source "drivers/net/wireless/zd1211rw/Kconfig"
source "drivers/net/wireless/mwifiex/Kconfig"
source "drivers/net/wireless/cw1200/Kconfig"
endif # WLAN

View File

@ -57,3 +57,5 @@ obj-$(CONFIG_MWIFIEX) += mwifiex/
obj-$(CONFIG_BRCMFMAC) += brcm80211/
obj-$(CONFIG_BRCMSMAC) += brcm80211/
obj-$(CONFIG_CW1200) += cw1200/

View File

@ -84,14 +84,6 @@ config ATH9K_DFS_CERTIFIED
developed. At this point enabling this option won't do anything
except increase code size.
config ATH9K_MAC_DEBUG
bool "Atheros MAC statistics"
depends on ATH9K_DEBUGFS
default y
---help---
This option enables collection of statistics for Rx/Tx status
data and some other MAC related statistics
config ATH9K_RATE_CONTROL
bool "Atheros ath9k rate control"
depends on ATH9K

View File

@ -454,6 +454,8 @@ static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain)
if (accum_cnt <= thresh_accum_cnt)
continue;
max_index++;
/* sum(tx amplitude) */
accum_tx = ((data_L[i] >> 16) & 0xffff) |
((data_U[i] & 0x7ff) << 16);
@ -468,20 +470,21 @@ static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain)
accum_tx <<= scale_factor;
accum_rx <<= scale_factor;
x_est[i + 1] = (((accum_tx + accum_cnt) / accum_cnt) + 32) >>
scale_factor;
x_est[max_index] =
(((accum_tx + accum_cnt) / accum_cnt) + 32) >>
scale_factor;
Y[i + 1] = ((((accum_rx + accum_cnt) / accum_cnt) + 32) >>
Y[max_index] =
((((accum_rx + accum_cnt) / accum_cnt) + 32) >>
scale_factor) +
(1 << scale_factor) * max_index + 16;
(1 << scale_factor) * i + 16;
if (accum_ang >= (1 << 26))
accum_ang -= 1 << 27;
theta[i + 1] = ((accum_ang * (1 << scale_factor)) + accum_cnt) /
accum_cnt;
max_index++;
theta[max_index] =
((accum_ang * (1 << scale_factor)) + accum_cnt) /
accum_cnt;
}
/*

View File

@ -646,6 +646,7 @@ enum sc_op_flags {
SC_OP_ANI_RUN,
SC_OP_PRIM_STA_VIF,
SC_OP_HW_RESET,
SC_OP_SCANNING,
};
/* Powersave flags */
@ -759,7 +760,6 @@ struct ath_softc {
struct rchan *rfs_chan_spec_scan;
enum spectral_mode spectral_mode;
struct ath_spec_scan spec_config;
int scanning;
#ifdef CONFIG_PM_SLEEP
atomic_t wow_got_bmiss_intr;

View File

@ -39,7 +39,8 @@ static void ath9k_beaconq_config(struct ath_softc *sc)
ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi);
if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
if (sc->sc_ah->opmode == NL80211_IFTYPE_AP ||
sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) {
/* Always burst out beacon and CAB traffic. */
qi.tqi_aifs = 1;
qi.tqi_cwmin = 0;
@ -273,7 +274,8 @@ static int ath9k_beacon_choose_slot(struct ath_softc *sc)
u64 tsf;
int slot;
if (sc->sc_ah->opmode != NL80211_IFTYPE_AP) {
if (sc->sc_ah->opmode != NL80211_IFTYPE_AP &&
sc->sc_ah->opmode != NL80211_IFTYPE_MESH_POINT) {
ath_dbg(common, BEACON, "slot 0, tsf: %llu\n",
ath9k_hw_gettsf64(sc->sc_ah));
return 0;
@ -765,10 +767,10 @@ void ath9k_set_beacon(struct ath_softc *sc)
switch (sc->sc_ah->opmode) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_MESH_POINT:
ath9k_beacon_config_ap(sc, cur_conf);
break;
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_MESH_POINT:
ath9k_beacon_config_adhoc(sc, cur_conf);
break;
case NL80211_IFTYPE_STATION:

View File

@ -738,8 +738,6 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf,
struct ath_tx_status *ts, struct ath_txq *txq,
unsigned int flags)
{
#define TX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].ts\
[sc->debug.tsidx].c)
int qnum = txq->axq_qnum;
TX_STAT_INC(qnum, tx_pkts_all);
@ -771,37 +769,6 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf,
TX_STAT_INC(qnum, data_underrun);
if (ts->ts_flags & ATH9K_TX_DELIM_UNDERRUN)
TX_STAT_INC(qnum, delim_underrun);
#ifdef CONFIG_ATH9K_MAC_DEBUG
spin_lock(&sc->debug.samp_lock);
TX_SAMP_DBG(jiffies) = jiffies;
TX_SAMP_DBG(rssi_ctl0) = ts->ts_rssi_ctl0;
TX_SAMP_DBG(rssi_ctl1) = ts->ts_rssi_ctl1;
TX_SAMP_DBG(rssi_ctl2) = ts->ts_rssi_ctl2;
TX_SAMP_DBG(rssi_ext0) = ts->ts_rssi_ext0;
TX_SAMP_DBG(rssi_ext1) = ts->ts_rssi_ext1;
TX_SAMP_DBG(rssi_ext2) = ts->ts_rssi_ext2;
TX_SAMP_DBG(rateindex) = ts->ts_rateindex;
TX_SAMP_DBG(isok) = !!(ts->ts_status & ATH9K_TXERR_MASK);
TX_SAMP_DBG(rts_fail_cnt) = ts->ts_shortretry;
TX_SAMP_DBG(data_fail_cnt) = ts->ts_longretry;
TX_SAMP_DBG(rssi) = ts->ts_rssi;
TX_SAMP_DBG(tid) = ts->tid;
TX_SAMP_DBG(qid) = ts->qid;
if (ts->ts_flags & ATH9K_TX_BA) {
TX_SAMP_DBG(ba_low) = ts->ba_low;
TX_SAMP_DBG(ba_high) = ts->ba_high;
} else {
TX_SAMP_DBG(ba_low) = 0;
TX_SAMP_DBG(ba_high) = 0;
}
sc->debug.tsidx = (sc->debug.tsidx + 1) % ATH_DBG_MAX_SAMPLES;
spin_unlock(&sc->debug.samp_lock);
#endif
#undef TX_SAMP_DBG
}
static const struct file_operations fops_xmit = {
@ -915,8 +882,6 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf,
void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs)
{
#define RX_PHY_ERR_INC(c) sc->debug.stats.rxstats.phy_err_stats[c]++
#define RX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].rs\
[sc->debug.rsidx].c)
RX_STAT_INC(rx_pkts_all);
sc->debug.stats.rxstats.rx_bytes_all += rs->rs_datalen;
@ -940,27 +905,7 @@ void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs)
RX_PHY_ERR_INC(rs->rs_phyerr);
}
#ifdef CONFIG_ATH9K_MAC_DEBUG
spin_lock(&sc->debug.samp_lock);
RX_SAMP_DBG(jiffies) = jiffies;
RX_SAMP_DBG(rssi_ctl0) = rs->rs_rssi_ctl0;
RX_SAMP_DBG(rssi_ctl1) = rs->rs_rssi_ctl1;
RX_SAMP_DBG(rssi_ctl2) = rs->rs_rssi_ctl2;
RX_SAMP_DBG(rssi_ext0) = rs->rs_rssi_ext0;
RX_SAMP_DBG(rssi_ext1) = rs->rs_rssi_ext1;
RX_SAMP_DBG(rssi_ext2) = rs->rs_rssi_ext2;
RX_SAMP_DBG(antenna) = rs->rs_antenna;
RX_SAMP_DBG(rssi) = rs->rs_rssi;
RX_SAMP_DBG(rate) = rs->rs_rate;
RX_SAMP_DBG(is_mybeacon) = rs->is_mybeacon;
sc->debug.rsidx = (sc->debug.rsidx + 1) % ATH_DBG_MAX_SAMPLES;
spin_unlock(&sc->debug.samp_lock);
#endif
#undef RX_PHY_ERR_INC
#undef RX_SAMP_DBG
}
static const struct file_operations fops_recv = {
@ -1485,283 +1430,6 @@ static const struct file_operations fops_modal_eeprom = {
.llseek = default_llseek,
};
#ifdef CONFIG_ATH9K_MAC_DEBUG
void ath9k_debug_samp_bb_mac(struct ath_softc *sc)
{
#define ATH_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].c)
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
unsigned long flags;
int i;
ath9k_ps_wakeup(sc);
spin_lock_bh(&sc->debug.samp_lock);
spin_lock_irqsave(&common->cc_lock, flags);
ath_hw_cycle_counters_update(common);
ATH_SAMP_DBG(cc.cycles) = common->cc_ani.cycles;
ATH_SAMP_DBG(cc.rx_busy) = common->cc_ani.rx_busy;
ATH_SAMP_DBG(cc.rx_frame) = common->cc_ani.rx_frame;
ATH_SAMP_DBG(cc.tx_frame) = common->cc_ani.tx_frame;
spin_unlock_irqrestore(&common->cc_lock, flags);
ATH_SAMP_DBG(noise) = ah->noise;
REG_WRITE_D(ah, AR_MACMISC,
((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) |
(AR_MACMISC_MISC_OBS_BUS_1 <<
AR_MACMISC_MISC_OBS_BUS_MSB_S)));
for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++)
ATH_SAMP_DBG(dma_dbg_reg_vals[i]) = REG_READ_D(ah,
AR_DMADBG_0 + (i * sizeof(u32)));
ATH_SAMP_DBG(pcu_obs) = REG_READ_D(ah, AR_OBS_BUS_1);
ATH_SAMP_DBG(pcu_cr) = REG_READ_D(ah, AR_CR);
memcpy(ATH_SAMP_DBG(nfCalHist), sc->caldata.nfCalHist,
sizeof(ATH_SAMP_DBG(nfCalHist)));
sc->debug.sampidx = (sc->debug.sampidx + 1) % ATH_DBG_MAX_SAMPLES;
spin_unlock_bh(&sc->debug.samp_lock);
ath9k_ps_restore(sc);
#undef ATH_SAMP_DBG
}
static int open_file_bb_mac_samps(struct inode *inode, struct file *file)
{
#define ATH_SAMP_DBG(c) bb_mac_samp[sampidx].c
struct ath_softc *sc = inode->i_private;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_conf *conf = &common->hw->conf;
struct ath_dbg_bb_mac_samp *bb_mac_samp;
struct ath9k_nfcal_hist *h;
int i, j, qcuOffset = 0, dcuOffset = 0;
u32 *qcuBase, *dcuBase, size = 30000, len = 0;
u32 sampidx = 0;
u8 *buf;
u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
u8 nread;
if (test_bit(SC_OP_INVALID, &sc->sc_flags))
return -EAGAIN;
buf = vmalloc(size);
if (!buf)
return -ENOMEM;
bb_mac_samp = vmalloc(sizeof(*bb_mac_samp) * ATH_DBG_MAX_SAMPLES);
if (!bb_mac_samp) {
vfree(buf);
return -ENOMEM;
}
/* Account the current state too */
ath9k_debug_samp_bb_mac(sc);
spin_lock_bh(&sc->debug.samp_lock);
memcpy(bb_mac_samp, sc->debug.bb_mac_samp,
sizeof(*bb_mac_samp) * ATH_DBG_MAX_SAMPLES);
len += snprintf(buf + len, size - len,
"Current Sample Index: %d\n", sc->debug.sampidx);
spin_unlock_bh(&sc->debug.samp_lock);
len += snprintf(buf + len, size - len,
"Raw DMA Debug Dump:\n");
len += snprintf(buf + len, size - len, "Sample |\t");
for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++)
len += snprintf(buf + len, size - len, " DMA Reg%d |\t", i);
len += snprintf(buf + len, size - len, "\n");
for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
len += snprintf(buf + len, size - len, "%d\t", sampidx);
for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++)
len += snprintf(buf + len, size - len, " %08x\t",
ATH_SAMP_DBG(dma_dbg_reg_vals[i]));
len += snprintf(buf + len, size - len, "\n");
}
len += snprintf(buf + len, size - len, "\n");
len += snprintf(buf + len, size - len,
"Sample Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n");
for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
qcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[0]);
dcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[4]);
for (i = 0; i < ATH9K_NUM_QUEUES; i++,
qcuOffset += 4, dcuOffset += 5) {
if (i == 8) {
qcuOffset = 0;
qcuBase++;
}
if (i == 6) {
dcuOffset = 0;
dcuBase++;
}
if (!sc->debug.stats.txstats[i].queued)
continue;
len += snprintf(buf + len, size - len,
"%4d %7d %2x %1x %2x %2x\n",
sampidx, i,
(*qcuBase & (0x7 << qcuOffset)) >> qcuOffset,
(*qcuBase & (0x8 << qcuOffset)) >>
(qcuOffset + 3),
ATH_SAMP_DBG(dma_dbg_reg_vals[2]) &
(0x7 << (i * 3)) >> (i * 3),
(*dcuBase & (0x1f << dcuOffset)) >> dcuOffset);
}
len += snprintf(buf + len, size - len, "\n");
}
len += snprintf(buf + len, size - len,
"samp qcu_sh qcu_fh qcu_comp dcu_comp dcu_arb dcu_fp "
"ch_idle_dur ch_idle_dur_val txfifo_val0 txfifo_val1 "
"txfifo_dcu0 txfifo_dcu1 pcu_obs AR_CR\n");
for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
qcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[0]);
dcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[4]);
len += snprintf(buf + len, size - len, "%4d %5x %5x ", sampidx,
(ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x003c0000) >> 18,
(ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x03c00000) >> 22);
len += snprintf(buf + len, size - len, "%7x %8x ",
(ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x1c000000) >> 26,
(ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x3));
len += snprintf(buf + len, size - len, "%7x %7x ",
(ATH_SAMP_DBG(dma_dbg_reg_vals[5]) & 0x06000000) >> 25,
(ATH_SAMP_DBG(dma_dbg_reg_vals[5]) & 0x38000000) >> 27);
len += snprintf(buf + len, size - len, "%7d %12d ",
(ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x000003fc) >> 2,
(ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00000400) >> 10);
len += snprintf(buf + len, size - len, "%12d %12d ",
(ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00000800) >> 11,
(ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00001000) >> 12);
len += snprintf(buf + len, size - len, "%12d %12d ",
(ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x0001e000) >> 13,
(ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x001e0000) >> 17);
len += snprintf(buf + len, size - len, "0x%07x 0x%07x\n",
ATH_SAMP_DBG(pcu_obs), ATH_SAMP_DBG(pcu_cr));
}
len += snprintf(buf + len, size - len,
"Sample ChNoise Chain privNF #Reading Readings\n");
for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
h = ATH_SAMP_DBG(nfCalHist);
if (!ATH_SAMP_DBG(noise))
continue;
for (i = 0; i < NUM_NF_READINGS; i++) {
if (!(chainmask & (1 << i)) ||
((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)))
continue;
nread = AR_PHY_CCA_FILTERWINDOW_LENGTH -
h[i].invalidNFcount;
len += snprintf(buf + len, size - len,
"%4d %5d %4d\t %d\t %d\t",
sampidx, ATH_SAMP_DBG(noise),
i, h[i].privNF, nread);
for (j = 0; j < nread; j++)
len += snprintf(buf + len, size - len,
" %d", h[i].nfCalBuffer[j]);
len += snprintf(buf + len, size - len, "\n");
}
}
len += snprintf(buf + len, size - len, "\nCycle counters:\n"
"Sample Total Rxbusy Rxframes Txframes\n");
for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
if (!ATH_SAMP_DBG(cc.cycles))
continue;
len += snprintf(buf + len, size - len,
"%4d %08x %08x %08x %08x\n",
sampidx, ATH_SAMP_DBG(cc.cycles),
ATH_SAMP_DBG(cc.rx_busy),
ATH_SAMP_DBG(cc.rx_frame),
ATH_SAMP_DBG(cc.tx_frame));
}
len += snprintf(buf + len, size - len, "Tx status Dump :\n");
len += snprintf(buf + len, size - len,
"Sample rssi:- ctl0 ctl1 ctl2 ext0 ext1 ext2 comb "
"isok rts_fail data_fail rate tid qid "
"ba_low ba_high tx_before(ms)\n");
for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
for (i = 0; i < ATH_DBG_MAX_SAMPLES; i++) {
if (!ATH_SAMP_DBG(ts[i].jiffies))
continue;
len += snprintf(buf + len, size - len, "%-14d"
"%-4d %-4d %-4d %-4d %-4d %-4d %-4d %-4d %-8d "
"%-9d %-4d %-3d %-3d %08x %08x %-11d\n",
sampidx,
ATH_SAMP_DBG(ts[i].rssi_ctl0),
ATH_SAMP_DBG(ts[i].rssi_ctl1),
ATH_SAMP_DBG(ts[i].rssi_ctl2),
ATH_SAMP_DBG(ts[i].rssi_ext0),
ATH_SAMP_DBG(ts[i].rssi_ext1),
ATH_SAMP_DBG(ts[i].rssi_ext2),
ATH_SAMP_DBG(ts[i].rssi),
ATH_SAMP_DBG(ts[i].isok),
ATH_SAMP_DBG(ts[i].rts_fail_cnt),
ATH_SAMP_DBG(ts[i].data_fail_cnt),
ATH_SAMP_DBG(ts[i].rateindex),
ATH_SAMP_DBG(ts[i].tid),
ATH_SAMP_DBG(ts[i].qid),
ATH_SAMP_DBG(ts[i].ba_low),
ATH_SAMP_DBG(ts[i].ba_high),
jiffies_to_msecs(jiffies -
ATH_SAMP_DBG(ts[i].jiffies)));
}
}
len += snprintf(buf + len, size - len, "Rx status Dump :\n");
len += snprintf(buf + len, size - len, "Sample rssi:- ctl0 ctl1 ctl2 "
"ext0 ext1 ext2 comb beacon ant rate rx_before(ms)\n");
for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) {
for (i = 0; i < ATH_DBG_MAX_SAMPLES; i++) {
if (!ATH_SAMP_DBG(rs[i].jiffies))
continue;
len += snprintf(buf + len, size - len, "%-14d"
"%-4d %-4d %-4d %-4d %-4d %-4d %-4d %-9s %-2d %02x %-13d\n",
sampidx,
ATH_SAMP_DBG(rs[i].rssi_ctl0),
ATH_SAMP_DBG(rs[i].rssi_ctl1),
ATH_SAMP_DBG(rs[i].rssi_ctl2),
ATH_SAMP_DBG(rs[i].rssi_ext0),
ATH_SAMP_DBG(rs[i].rssi_ext1),
ATH_SAMP_DBG(rs[i].rssi_ext2),
ATH_SAMP_DBG(rs[i].rssi),
ATH_SAMP_DBG(rs[i].is_mybeacon) ?
"True" : "False",
ATH_SAMP_DBG(rs[i].antenna),
ATH_SAMP_DBG(rs[i].rate),
jiffies_to_msecs(jiffies -
ATH_SAMP_DBG(rs[i].jiffies)));
}
}
vfree(bb_mac_samp);
file->private_data = buf;
return 0;
#undef ATH_SAMP_DBG
}
static const struct file_operations fops_samps = {
.open = open_file_bb_mac_samps,
.read = ath9k_debugfs_read_buf,
.release = ath9k_debugfs_release_buf,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
#endif
#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
static ssize_t read_file_btcoex(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
@ -2087,11 +1755,6 @@ int ath9k_init_debug(struct ath_hw *ah)
debugfs_create_file("spectral_fft_period", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, sc,
&fops_spectral_fft_period);
#ifdef CONFIG_ATH9K_MAC_DEBUG
debugfs_create_file("samples", S_IRUSR, sc->debug.debugfs_phy, sc,
&fops_samps);
#endif
debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask);
debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR,

View File

@ -294,13 +294,6 @@ struct ath9k_debug {
struct dentry *debugfs_phy;
u32 regidx;
struct ath_stats stats;
#ifdef CONFIG_ATH9K_MAC_DEBUG
spinlock_t samp_lock;
struct ath_dbg_bb_mac_samp bb_mac_samp[ATH_DBG_MAX_SAMPLES];
u8 sampidx;
u8 tsidx;
u8 rsidx;
#endif
};
int ath9k_init_debug(struct ath_hw *ah);
@ -359,17 +352,4 @@ static inline void ath_debug_stat_rx(struct ath_softc *sc,
#endif /* CONFIG_ATH9K_DEBUGFS */
#ifdef CONFIG_ATH9K_MAC_DEBUG
void ath9k_debug_samp_bb_mac(struct ath_softc *sc);
#else
static inline void ath9k_debug_samp_bb_mac(struct ath_softc *sc)
{
}
#endif
#endif /* DEBUG_H */

View File

@ -208,6 +208,9 @@ struct ath9k_htc_target_rx_stats {
case NL80211_IFTYPE_AP: \
_priv->num_ap_vif++; \
break; \
case NL80211_IFTYPE_MESH_POINT: \
_priv->num_mbss_vif++; \
break; \
default: \
break; \
} \
@ -224,6 +227,9 @@ struct ath9k_htc_target_rx_stats {
case NL80211_IFTYPE_AP: \
_priv->num_ap_vif--; \
break; \
case NL80211_IFTYPE_MESH_POINT: \
_priv->num_mbss_vif--; \
break; \
default: \
break; \
} \
@ -450,6 +456,7 @@ struct ath9k_htc_priv {
u8 sta_slot;
u8 vif_sta_pos[ATH9K_HTC_MAX_VIF];
u8 num_ibss_vif;
u8 num_mbss_vif;
u8 num_sta_vif;
u8 num_sta_assoc_vif;
u8 num_ap_vif;

View File

@ -28,7 +28,8 @@ void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv)
ath9k_hw_get_txq_props(ah, priv->beaconq, &qi);
if (priv->ah->opmode == NL80211_IFTYPE_AP) {
if (priv->ah->opmode == NL80211_IFTYPE_AP ||
priv->ah->opmode == NL80211_IFTYPE_MESH_POINT) {
qi.tqi_aifs = 1;
qi.tqi_cwmin = 0;
qi.tqi_cwmax = 0;
@ -628,6 +629,7 @@ void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv,
case NL80211_IFTYPE_ADHOC:
ath9k_htc_beacon_config_adhoc(priv, cur_conf);
break;
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_AP:
ath9k_htc_beacon_config_ap(priv, cur_conf);
break;
@ -649,6 +651,7 @@ void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv)
case NL80211_IFTYPE_ADHOC:
ath9k_htc_beacon_config_adhoc(priv, cur_conf);
break;
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_AP:
ath9k_htc_beacon_config_ap(priv, cur_conf);
break;

View File

@ -698,7 +698,8 @@ static const struct ieee80211_iface_limit if_limits[] = {
{ .max = 2, .types = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_P2P_CLIENT) },
{ .max = 2, .types = BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_P2P_GO) },
BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_MESH_POINT) },
};
static const struct ieee80211_iface_combination if_comb = {
@ -721,6 +722,7 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK |
IEEE80211_HW_REPORTS_TX_ACK_STATUS |
IEEE80211_HW_MFP_CAPABLE |
IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING;
hw->wiphy->interface_modes =
@ -728,7 +730,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_P2P_CLIENT);
BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_MESH_POINT);
hw->wiphy->iface_combinations = &if_comb;
hw->wiphy->n_iface_combinations = 1;

View File

@ -113,7 +113,9 @@ static void ath9k_htc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
struct ath9k_htc_priv *priv = data;
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
if ((vif->type == NL80211_IFTYPE_AP) && bss_conf->enable_beacon)
if ((vif->type == NL80211_IFTYPE_AP ||
vif->type == NL80211_IFTYPE_MESH_POINT) &&
bss_conf->enable_beacon)
priv->reconfig_beacon = true;
if (bss_conf->assoc) {
@ -180,6 +182,8 @@ static void ath9k_htc_set_opmode(struct ath9k_htc_priv *priv)
priv->ah->opmode = NL80211_IFTYPE_ADHOC;
else if (priv->num_ap_vif)
priv->ah->opmode = NL80211_IFTYPE_AP;
else if (priv->num_mbss_vif)
priv->ah->opmode = NL80211_IFTYPE_MESH_POINT;
else
priv->ah->opmode = NL80211_IFTYPE_STATION;
@ -1052,6 +1056,9 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
case NL80211_IFTYPE_AP:
hvif.opmode = HTC_M_HOSTAP;
break;
case NL80211_IFTYPE_MESH_POINT:
hvif.opmode = HTC_M_WDS; /* close enough */
break;
default:
ath_err(common,
"Interface type %d not yet supported\n", vif->type);
@ -1084,6 +1091,7 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
INC_VIF(priv, vif->type);
if ((vif->type == NL80211_IFTYPE_AP) ||
(vif->type == NL80211_IFTYPE_MESH_POINT) ||
(vif->type == NL80211_IFTYPE_ADHOC))
ath9k_htc_assign_bslot(priv, vif);
@ -1134,6 +1142,7 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
DEC_VIF(priv, vif->type);
if ((vif->type == NL80211_IFTYPE_AP) ||
vif->type == NL80211_IFTYPE_MESH_POINT ||
(vif->type == NL80211_IFTYPE_ADHOC))
ath9k_htc_remove_bslot(priv, vif);
@ -1525,9 +1534,10 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) {
/*
* Disable SWBA interrupt only if there are no
* AP/IBSS interfaces.
* concurrent AP/mesh or IBSS interfaces.
*/
if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) {
if ((priv->num_ap_vif + priv->num_mbss_vif <= 1) ||
priv->num_ibss_vif) {
ath_dbg(common, CONFIG,
"Beacon disabled for BSS: %pM\n",
bss_conf->bssid);
@ -1538,12 +1548,15 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_BEACON_INT) {
/*
* Reset the HW TSF for the first AP interface.
* Reset the HW TSF for the first AP or mesh interface.
*/
if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
(priv->nvifs == 1) &&
(priv->num_ap_vif == 1) &&
(vif->type == NL80211_IFTYPE_AP)) {
if (priv->nvifs == 1 &&
((priv->ah->opmode == NL80211_IFTYPE_AP &&
vif->type == NL80211_IFTYPE_AP &&
priv->num_ap_vif == 1) ||
(priv->ah->opmode == NL80211_IFTYPE_MESH_POINT &&
vif->type == NL80211_IFTYPE_MESH_POINT &&
priv->num_mbss_vif == 1))) {
set_bit(OP_TSF_RESET, &priv->op_flags);
}
ath_dbg(common, CONFIG,

View File

@ -887,7 +887,7 @@ u32 ath9k_htc_calcrxfilter(struct ath9k_htc_priv *priv)
if (priv->rxfilter & FIF_PSPOLL)
rfilt |= ATH9K_RX_FILTER_PSPOLL;
if (priv->nvifs > 1)
if (priv->nvifs > 1 || priv->rxfilter & FIF_OTHER_BSS)
rfilt |= ATH9K_RX_FILTER_MCAST_BCAST_ALL;
return rfilt;

View File

@ -1245,10 +1245,10 @@ static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode)
switch (opmode) {
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_MESH_POINT:
set |= AR_STA_ID1_ADHOC;
REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION);
break;
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_AP:
set |= AR_STA_ID1_STA_AP;
/* fall through */
@ -2246,12 +2246,12 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period)
switch (ah->opmode) {
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_MESH_POINT:
REG_SET_BIT(ah, AR_TXCFG,
AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY);
REG_WRITE(ah, AR_NEXT_NDP_TIMER, next_beacon +
TU_TO_USEC(ah->atim_window ? ah->atim_window : 1));
flags |= AR_NDP_TIMER_EN;
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_AP:
REG_WRITE(ah, AR_NEXT_TBTT_TIMER, next_beacon);
REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, next_beacon -

View File

@ -613,9 +613,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
spin_lock_init(&sc->sc_serial_rw);
spin_lock_init(&sc->sc_pm_lock);
mutex_init(&sc->mutex);
#ifdef CONFIG_ATH9K_MAC_DEBUG
spin_lock_init(&sc->debug.samp_lock);
#endif
tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet,
(unsigned long)sc);

View File

@ -418,7 +418,6 @@ void ath_ani_calibrate(unsigned long data)
longcal ? "long" : "", shortcal ? "short" : "",
aniflag ? "ani" : "", common->ani.caldone ? "true" : "false");
ath9k_debug_samp_bb_mac(sc);
ath9k_ps_restore(sc);
set_timer:

View File

@ -193,7 +193,6 @@ static bool ath_prepare_reset(struct ath_softc *sc)
ath_stop_ani(sc);
del_timer_sync(&sc->rx_poll_timer);
ath9k_debug_samp_bb_mac(sc);
ath9k_hw_disable_interrupts(ah);
if (!ath_drain_all_txq(sc))
@ -1273,7 +1272,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
curchan->center_freq);
} else {
/* perform spectral scan if requested. */
if (sc->scanning &&
if (test_bit(SC_OP_SCANNING, &sc->sc_flags) &&
sc->spectral_mode == SPECTRAL_CHANSCAN)
ath9k_spectral_scan_trigger(hw);
}
@ -1689,7 +1688,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
struct ath_softc *sc = hw->priv;
int ret = 0;
local_bh_disable();
mutex_lock(&sc->mutex);
switch (action) {
case IEEE80211_AMPDU_RX_START:
@ -1720,7 +1719,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
ath_err(ath9k_hw_common(sc->sc_ah), "Unknown AMPDU action\n");
}
local_bh_enable();
mutex_unlock(&sc->mutex);
return ret;
}
@ -2339,15 +2338,13 @@ static void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled)
static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
{
struct ath_softc *sc = hw->priv;
sc->scanning = 1;
set_bit(SC_OP_SCANNING, &sc->sc_flags);
}
static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
{
struct ath_softc *sc = hw->priv;
sc->scanning = 0;
clear_bit(SC_OP_SCANNING, &sc->sc_flags);
}
struct ieee80211_ops ath9k_ops = {

View File

@ -27,3 +27,15 @@ config WIL6210_ISR_COR
self-clear when accessed for debug purposes, it makes
such monitoring impossible.
Say y unless you debug interrupts
config ATH6KL_TRACING
bool "wil6210 tracing support"
depends on WIL6210
depends on EVENT_TRACING
default y
---help---
Say Y here to enable tracepoints for the wil6210 driver
using the kernel tracing infrastructure. Select this
option if you are interested in debugging the driver.
If unsure, say Y to make it easier to debug problems.

View File

@ -1,15 +1,20 @@
obj-$(CONFIG_WIL6210) += wil6210.o
wil6210-objs := main.o
wil6210-objs += netdev.o
wil6210-objs += cfg80211.o
wil6210-objs += pcie_bus.o
wil6210-objs += debugfs.o
wil6210-objs += wmi.o
wil6210-objs += interrupt.o
wil6210-objs += txrx.o
wil6210-y := main.o
wil6210-y += netdev.o
wil6210-y += cfg80211.o
wil6210-y += pcie_bus.o
wil6210-y += debugfs.o
wil6210-y += wmi.o
wil6210-y += interrupt.o
wil6210-y += txrx.o
wil6210-y += debug.o
wil6210-$(CONFIG_WIL6210_TRACING) += trace.o
ifeq (, $(findstring -W,$(EXTRA_CFLAGS)))
subdir-ccflags-y += -Werror
endif
# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)
subdir-ccflags-y += -D__CHECK_ENDIAN__

View File

@ -322,12 +322,16 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
* FW don't support scan after connection attempt
*/
set_bit(wil_status_dontscan, &wil->status);
set_bit(wil_status_fwconnecting, &wil->status);
rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn));
if (rc == 0) {
/* Connect can take lots of time */
mod_timer(&wil->connect_timer,
jiffies + msecs_to_jiffies(2000));
} else {
clear_bit(wil_status_dontscan, &wil->status);
clear_bit(wil_status_fwconnecting, &wil->status);
}
out:

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2013 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "wil6210.h"
#include "trace.h"
int wil_err(struct wil6210_priv *wil, const char *fmt, ...)
{
struct net_device *ndev = wil_to_ndev(wil);
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
int ret;
va_start(args, fmt);
vaf.va = &args;
ret = netdev_err(ndev, "%pV", &vaf);
trace_wil6210_log_err(&vaf);
va_end(args);
return ret;
}
int wil_info(struct wil6210_priv *wil, const char *fmt, ...)
{
struct net_device *ndev = wil_to_ndev(wil);
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
int ret;
va_start(args, fmt);
vaf.va = &args;
ret = netdev_info(ndev, "%pV", &vaf);
trace_wil6210_log_info(&vaf);
va_end(args);
return ret;
}
int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
va_start(args, fmt);
vaf.va = &args;
trace_wil6210_log_dbg(&vaf);
va_end(args);
return 0;
}

View File

@ -418,9 +418,15 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
if (skb) {
unsigned char printbuf[16 * 3 + 2];
int i = 0;
int len = skb_headlen(skb);
int len = le16_to_cpu(d->dma.length);
void *p = skb->data;
if (len != skb_headlen(skb)) {
seq_printf(s, "!!! len: desc = %d skb = %d\n",
len, skb_headlen(skb));
len = min_t(int, len, skb_headlen(skb));
}
seq_printf(s, " len = %d\n", len);
while (i < len) {

View File

@ -17,6 +17,7 @@
#include <linux/interrupt.h>
#include "wil6210.h"
#include "trace.h"
/**
* Theory of operation:
@ -103,14 +104,14 @@ static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil)
clear_bit(wil_status_irqen, &wil->status);
}
static void wil6210_unmask_irq_tx(struct wil6210_priv *wil)
void wil6210_unmask_irq_tx(struct wil6210_priv *wil)
{
iowrite32(WIL6210_IMC_TX, wil->csr +
HOSTADDR(RGF_DMA_EP_TX_ICR) +
offsetof(struct RGF_ICR, IMC));
}
static void wil6210_unmask_irq_rx(struct wil6210_priv *wil)
void wil6210_unmask_irq_rx(struct wil6210_priv *wil)
{
iowrite32(WIL6210_IMC_RX, wil->csr +
HOSTADDR(RGF_DMA_EP_RX_ICR) +
@ -168,6 +169,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
HOSTADDR(RGF_DMA_EP_RX_ICR) +
offsetof(struct RGF_ICR, ICR));
trace_wil6210_irq_rx(isr);
wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr);
if (!isr) {
@ -180,13 +182,14 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) {
wil_dbg_irq(wil, "RX done\n");
isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE;
wil_rx_handle(wil);
wil_dbg_txrx(wil, "NAPI schedule\n");
napi_schedule(&wil->napi_rx);
}
if (isr)
wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr);
wil6210_unmask_irq_rx(wil);
/* Rx IRQ will be enabled when NAPI processing finished */
return IRQ_HANDLED;
}
@ -198,6 +201,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
HOSTADDR(RGF_DMA_EP_TX_ICR) +
offsetof(struct RGF_ICR, ICR));
trace_wil6210_irq_tx(isr);
wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr);
if (!isr) {
@ -208,23 +212,17 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
wil6210_mask_irq_tx(wil);
if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) {
uint i;
wil_dbg_irq(wil, "TX done\n");
napi_schedule(&wil->napi_tx);
isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE;
for (i = 0; i < 24; i++) {
u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i);
if (isr & mask) {
isr &= ~mask;
wil_dbg_irq(wil, "TX done(%i)\n", i);
wil_tx_complete(wil, i);
}
}
/* clear also all VRING interrupts */
isr &= ~(BIT(25) - 1UL);
}
if (isr)
wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr);
wil6210_unmask_irq_tx(wil);
/* Tx IRQ will be enabled when NAPI processing finished */
return IRQ_HANDLED;
}
@ -256,6 +254,7 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
HOSTADDR(RGF_DMA_EP_MISC_ICR) +
offsetof(struct RGF_ICR, ICR));
trace_wil6210_irq_misc(isr);
wil_dbg_irq(wil, "ISR MISC 0x%08x\n", isr);
if (!isr) {
@ -301,6 +300,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
struct wil6210_priv *wil = cookie;
u32 isr = wil->isr_misc;
trace_wil6210_irq_misc_thread(isr);
wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr);
if (isr & ISR_MISC_FW_ERROR) {
@ -408,6 +408,7 @@ static irqreturn_t wil6210_hardirq(int irq, void *cookie)
if (wil6210_debug_irq_mask(wil, pseudo_cause))
return IRQ_NONE;
trace_wil6210_irq_pseudo(pseudo_cause);
wil_dbg_irq(wil, "Pseudo IRQ 0x%08x\n", pseudo_cause);
wil6210_mask_irq_pseudo(wil);

View File

@ -56,27 +56,21 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
{
uint i;
struct net_device *ndev = wil_to_ndev(wil);
struct wireless_dev *wdev = wil->wdev;
wil_dbg_misc(wil, "%s()\n", __func__);
wil_link_off(wil);
clear_bit(wil_status_fwconnected, &wil->status);
switch (wdev->sme_state) {
case CFG80211_SME_CONNECTED:
cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE,
if (test_bit(wil_status_fwconnected, &wil->status)) {
clear_bit(wil_status_fwconnected, &wil->status);
cfg80211_disconnected(ndev,
WLAN_STATUS_UNSPECIFIED_FAILURE,
NULL, 0, GFP_KERNEL);
break;
case CFG80211_SME_CONNECTING:
} else if (test_bit(wil_status_fwconnecting, &wil->status)) {
cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE,
GFP_KERNEL);
break;
default:
break;
}
clear_bit(wil_status_fwconnecting, &wil->status);
for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++)
wil_vring_fini_tx(wil, i);
@ -365,6 +359,9 @@ static int __wil_up(struct wil6210_priv *wil)
/* Rx VRING. After MAC and beacon */
wil_rx_init(wil);
napi_enable(&wil->napi_rx);
napi_enable(&wil->napi_tx);
return 0;
}
@ -381,6 +378,9 @@ int wil_up(struct wil6210_priv *wil)
static int __wil_down(struct wil6210_priv *wil)
{
napi_disable(&wil->napi_rx);
napi_disable(&wil->napi_tx);
if (wil->scan_request) {
cfg80211_scan_done(wil->scan_request, true);
wil->scan_request = NULL;

View File

@ -40,6 +40,55 @@ static const struct net_device_ops wil_netdev_ops = {
.ndo_validate_addr = eth_validate_addr,
};
static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget)
{
struct wil6210_priv *wil = container_of(napi, struct wil6210_priv,
napi_rx);
int quota = budget;
int done;
wil_rx_handle(wil, &quota);
done = budget - quota;
if (done <= 1) { /* burst ends - only one packet processed */
napi_complete(napi);
wil6210_unmask_irq_rx(wil);
wil_dbg_txrx(wil, "NAPI RX complete\n");
}
wil_dbg_txrx(wil, "NAPI RX poll(%d) done %d\n", budget, done);
return done;
}
static int wil6210_netdev_poll_tx(struct napi_struct *napi, int budget)
{
struct wil6210_priv *wil = container_of(napi, struct wil6210_priv,
napi_tx);
int tx_done = 0;
uint i;
/* always process ALL Tx complete, regardless budget - it is fast */
for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
struct vring *vring = &wil->vring_tx[i];
if (!vring->va)
continue;
tx_done += wil_tx_complete(wil, i);
}
if (tx_done <= 1) { /* burst ends - only one packet processed */
napi_complete(napi);
wil6210_unmask_irq_tx(wil);
wil_dbg_txrx(wil, "NAPI TX complete\n");
}
wil_dbg_txrx(wil, "NAPI TX poll(%d) done %d\n", budget, tx_done);
return min(tx_done, budget);
}
void *wil_if_alloc(struct device *dev, void __iomem *csr)
{
struct net_device *ndev;
@ -81,6 +130,11 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr)
SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
wdev->netdev = ndev;
netif_napi_add(ndev, &wil->napi_rx, wil6210_netdev_poll_rx,
WIL6210_NAPI_BUDGET);
netif_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx,
WIL6210_NAPI_BUDGET);
wil_link_off(wil);
return wil;

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) 2013 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/module.h>
#define CREATE_TRACE_POINTS
#include "trace.h"

View File

@ -0,0 +1,235 @@
/*
* Copyright (c) 2013 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM wil6210
#if !defined(WIL6210_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
#define WIL6210_TRACE_H
#include <linux/tracepoint.h>
#include "wil6210.h"
#include "txrx.h"
/* create empty functions when tracing is disabled */
#if !defined(CONFIG_WIL6210_TRACING) || defined(__CHECKER__)
#undef TRACE_EVENT
#define TRACE_EVENT(name, proto, ...) \
static inline void trace_ ## name(proto) {}
#undef DECLARE_EVENT_CLASS
#define DECLARE_EVENT_CLASS(...)
#undef DEFINE_EVENT
#define DEFINE_EVENT(evt_class, name, proto, ...) \
static inline void trace_ ## name(proto) {}
#endif /* !CONFIG_WIL6210_TRACING || defined(__CHECKER__) */
DECLARE_EVENT_CLASS(wil6210_wmi,
TP_PROTO(u16 id, void *buf, u16 buf_len),
TP_ARGS(id, buf, buf_len),
TP_STRUCT__entry(
__field(u16, id)
__field(u16, buf_len)
__dynamic_array(u8, buf, buf_len)
),
TP_fast_assign(
__entry->id = id;
__entry->buf_len = buf_len;
memcpy(__get_dynamic_array(buf), buf, buf_len);
),
TP_printk(
"id 0x%04x len %d",
__entry->id, __entry->buf_len
)
);
DEFINE_EVENT(wil6210_wmi, wil6210_wmi_cmd,
TP_PROTO(u16 id, void *buf, u16 buf_len),
TP_ARGS(id, buf, buf_len)
);
DEFINE_EVENT(wil6210_wmi, wil6210_wmi_event,
TP_PROTO(u16 id, void *buf, u16 buf_len),
TP_ARGS(id, buf, buf_len)
);
#define WIL6210_MSG_MAX (200)
DECLARE_EVENT_CLASS(wil6210_log_event,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf),
TP_STRUCT__entry(
__dynamic_array(char, msg, WIL6210_MSG_MAX)
),
TP_fast_assign(
WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg),
WIL6210_MSG_MAX,
vaf->fmt,
*vaf->va) >= WIL6210_MSG_MAX);
),
TP_printk("%s", __get_str(msg))
);
DEFINE_EVENT(wil6210_log_event, wil6210_log_err,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf)
);
DEFINE_EVENT(wil6210_log_event, wil6210_log_info,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf)
);
DEFINE_EVENT(wil6210_log_event, wil6210_log_dbg,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf)
);
#define wil_pseudo_irq_cause(x) __print_flags(x, "|", \
{BIT_DMA_PSEUDO_CAUSE_RX, "Rx" }, \
{BIT_DMA_PSEUDO_CAUSE_TX, "Tx" }, \
{BIT_DMA_PSEUDO_CAUSE_MISC, "Misc" })
TRACE_EVENT(wil6210_irq_pseudo,
TP_PROTO(u32 x),
TP_ARGS(x),
TP_STRUCT__entry(
__field(u32, x)
),
TP_fast_assign(
__entry->x = x;
),
TP_printk("cause 0x%08x : %s", __entry->x,
wil_pseudo_irq_cause(__entry->x))
);
DECLARE_EVENT_CLASS(wil6210_irq,
TP_PROTO(u32 x),
TP_ARGS(x),
TP_STRUCT__entry(
__field(u32, x)
),
TP_fast_assign(
__entry->x = x;
),
TP_printk("cause 0x%08x", __entry->x)
);
DEFINE_EVENT(wil6210_irq, wil6210_irq_rx,
TP_PROTO(u32 x),
TP_ARGS(x)
);
DEFINE_EVENT(wil6210_irq, wil6210_irq_tx,
TP_PROTO(u32 x),
TP_ARGS(x)
);
DEFINE_EVENT(wil6210_irq, wil6210_irq_misc,
TP_PROTO(u32 x),
TP_ARGS(x)
);
DEFINE_EVENT(wil6210_irq, wil6210_irq_misc_thread,
TP_PROTO(u32 x),
TP_ARGS(x)
);
TRACE_EVENT(wil6210_rx,
TP_PROTO(u16 index, struct vring_rx_desc *d),
TP_ARGS(index, d),
TP_STRUCT__entry(
__field(u16, index)
__field(unsigned int, len)
__field(u8, mid)
__field(u8, cid)
__field(u8, tid)
__field(u8, type)
__field(u8, subtype)
__field(u16, seq)
__field(u8, mcs)
),
TP_fast_assign(
__entry->index = index;
__entry->len = d->dma.length;
__entry->mid = wil_rxdesc_mid(d);
__entry->cid = wil_rxdesc_cid(d);
__entry->tid = wil_rxdesc_tid(d);
__entry->type = wil_rxdesc_ftype(d);
__entry->subtype = wil_rxdesc_subtype(d);
__entry->seq = wil_rxdesc_seq(d);
__entry->mcs = wil_rxdesc_mcs(d);
),
TP_printk("index %d len %d mid %d cid %d tid %d mcs %d seq 0x%03x"
" type 0x%1x subtype 0x%1x", __entry->index, __entry->len,
__entry->mid, __entry->cid, __entry->tid, __entry->mcs,
__entry->seq, __entry->type, __entry->subtype)
);
TRACE_EVENT(wil6210_tx,
TP_PROTO(u8 vring, u16 index, unsigned int len, u8 frags),
TP_ARGS(vring, index, len, frags),
TP_STRUCT__entry(
__field(u8, vring)
__field(u8, frags)
__field(u16, index)
__field(unsigned int, len)
),
TP_fast_assign(
__entry->vring = vring;
__entry->frags = frags;
__entry->index = index;
__entry->len = len;
),
TP_printk("vring %d index %d len %d frags %d",
__entry->vring, __entry->index, __entry->len, __entry->frags)
);
TRACE_EVENT(wil6210_tx_done,
TP_PROTO(u8 vring, u16 index, unsigned int len, u8 err),
TP_ARGS(vring, index, len, err),
TP_STRUCT__entry(
__field(u8, vring)
__field(u8, err)
__field(u16, index)
__field(unsigned int, len)
),
TP_fast_assign(
__entry->vring = vring;
__entry->index = index;
__entry->len = len;
__entry->err = err;
),
TP_printk("vring %d index %d len %d err 0x%02x",
__entry->vring, __entry->index, __entry->len,
__entry->err)
);
#endif /* WIL6210_TRACE_H || TRACE_HEADER_MULTI_READ*/
#if defined(CONFIG_WIL6210_TRACING) && !defined(__CHECKER__)
/* we don't want to use include/trace/events */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace
/* This part must be outside protection */
#include <trace/define_trace.h>
#endif /* defined(CONFIG_WIL6210_TRACING) && !defined(__CHECKER__) */

View File

@ -22,6 +22,7 @@
#include "wil6210.h"
#include "wmi.h"
#include "txrx.h"
#include "trace.h"
static bool rtap_include_phy_info;
module_param(rtap_include_phy_info, bool, S_IRUGO);
@ -89,8 +90,8 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
* we can use any
*/
for (i = 0; i < vring->size; i++) {
volatile struct vring_tx_desc *d = &(vring->va[i].tx);
d->dma.status = TX_DMA_STATUS_DU;
volatile struct vring_tx_desc *_d = &(vring->va[i].tx);
_d->dma.status = TX_DMA_STATUS_DU;
}
wil_dbg_misc(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size,
@ -106,30 +107,39 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring,
size_t sz = vring->size * sizeof(vring->va[0]);
while (!wil_vring_is_empty(vring)) {
dma_addr_t pa;
struct sk_buff *skb;
u16 dmalen;
if (tx) {
volatile struct vring_tx_desc *d =
struct vring_tx_desc dd, *d = &dd;
volatile struct vring_tx_desc *_d =
&vring->va[vring->swtail].tx;
dma_addr_t pa = d->dma.addr_low |
((u64)d->dma.addr_high << 32);
struct sk_buff *skb = vring->ctx[vring->swtail];
*d = *_d;
pa = wil_desc_addr(&d->dma.addr);
dmalen = le16_to_cpu(d->dma.length);
skb = vring->ctx[vring->swtail];
if (skb) {
dma_unmap_single(dev, pa, d->dma.length,
dma_unmap_single(dev, pa, dmalen,
DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
vring->ctx[vring->swtail] = NULL;
} else {
dma_unmap_page(dev, pa, d->dma.length,
dma_unmap_page(dev, pa, dmalen,
DMA_TO_DEVICE);
}
vring->swtail = wil_vring_next_tail(vring);
} else { /* rx */
volatile struct vring_rx_desc *d =
struct vring_rx_desc dd, *d = &dd;
volatile struct vring_rx_desc *_d =
&vring->va[vring->swtail].rx;
dma_addr_t pa = d->dma.addr_low |
((u64)d->dma.addr_high << 32);
struct sk_buff *skb = vring->ctx[vring->swhead];
dma_unmap_single(dev, pa, d->dma.length,
DMA_FROM_DEVICE);
*d = *_d;
pa = wil_desc_addr(&d->dma.addr);
dmalen = le16_to_cpu(d->dma.length);
skb = vring->ctx[vring->swhead];
dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE);
kfree_skb(skb);
wil_vring_advance_head(vring, 1);
}
@ -151,7 +161,8 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
{
struct device *dev = wil_to_dev(wil);
unsigned int sz = RX_BUF_LEN;
volatile struct vring_rx_desc *d = &(vring->va[i].rx);
struct vring_rx_desc dd, *d = &dd;
volatile struct vring_rx_desc *_d = &(vring->va[i].rx);
dma_addr_t pa;
/* TODO align */
@ -169,13 +180,13 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring,
}
d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT;
d->dma.addr_low = lower_32_bits(pa);
d->dma.addr_high = (u16)upper_32_bits(pa);
wil_desc_addr_set(&d->dma.addr, pa);
/* ip_length don't care */
/* b11 don't care */
/* error don't care */
d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
d->dma.length = sz;
d->dma.length = cpu_to_le16(sz);
*_d = *d;
vring->ctx[i] = skb;
return 0;
@ -321,11 +332,12 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
{
struct device *dev = wil_to_dev(wil);
struct net_device *ndev = wil_to_ndev(wil);
volatile struct vring_rx_desc *d;
struct vring_rx_desc *d1;
volatile struct vring_rx_desc *_d;
struct vring_rx_desc *d;
struct sk_buff *skb;
dma_addr_t pa;
unsigned int sz = RX_BUF_LEN;
u16 dmalen;
u8 ftype;
u8 ds_bits;
@ -334,32 +346,44 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
if (wil_vring_is_empty(vring))
return NULL;
d = &(vring->va[vring->swhead].rx);
if (!(d->dma.status & RX_DMA_STATUS_DU)) {
_d = &(vring->va[vring->swhead].rx);
if (!(_d->dma.status & RX_DMA_STATUS_DU)) {
/* it is not error, we just reached end of Rx done area */
return NULL;
}
pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
skb = vring->ctx[vring->swhead];
d = wil_skb_rxdesc(skb);
*d = *_d;
pa = wil_desc_addr(&d->dma.addr);
vring->ctx[vring->swhead] = NULL;
wil_vring_advance_head(vring, 1);
dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE);
skb_trim(skb, d->dma.length);
dmalen = le16_to_cpu(d->dma.length);
d1 = wil_skb_rxdesc(skb);
*d1 = *d;
trace_wil6210_rx(vring->swhead, d);
wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, dmalen);
wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
wil->stats.last_mcs_rx = wil_rxdesc_mcs(d1);
if (dmalen > sz) {
wil_err(wil, "Rx size too large: %d bytes!\n", dmalen);
kfree_skb(skb);
return NULL;
}
skb_trim(skb, dmalen);
wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
skb->data, skb_headlen(skb), false);
wil->stats.last_mcs_rx = wil_rxdesc_mcs(d);
/* use radiotap header only if required */
if (ndev->type == ARPHRD_IEEE80211_RADIOTAP)
wil_rx_add_radiotap_header(wil, skb);
wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length);
wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
wil_vring_advance_head(vring, 1);
/* no extra checks if in sniffer mode */
if (ndev->type != ARPHRD_ETHER)
return skb;
@ -368,7 +392,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
* Driver should recognize it by frame type, that is found
* in Rx descriptor. If type is not data, it is 802.11 frame as is
*/
ftype = wil_rxdesc_ftype(d1) << 2;
ftype = wil_rxdesc_ftype(d) << 2;
if (ftype != IEEE80211_FTYPE_DATA) {
wil_dbg_txrx(wil, "Non-data frame ftype 0x%08x\n", ftype);
/* TODO: process it */
@ -383,7 +407,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
return NULL;
}
ds_bits = wil_rxdesc_ds_bits(d1);
ds_bits = wil_rxdesc_ds_bits(d);
if (ds_bits == 1) {
/*
* HW bug - in ToDS mode, i.e. Rx on AP side,
@ -425,6 +449,7 @@ static int wil_rx_refill(struct wil6210_priv *wil, int count)
/*
* Pass Rx packet to the netif. Update statistics.
* Called in softirq context (NAPI poll).
*/
static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
{
@ -433,10 +458,7 @@ static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
skb_orphan(skb);
if (in_interrupt())
rc = netif_rx(skb);
else
rc = netif_rx_ni(skb);
rc = netif_receive_skb(skb);
if (likely(rc == NET_RX_SUCCESS)) {
ndev->stats.rx_packets++;
@ -450,9 +472,9 @@ static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
/**
* Proceed all completed skb's from Rx VRING
*
* Safe to call from IRQ
* Safe to call from NAPI poll, i.e. softirq with interrupts enabled
*/
void wil_rx_handle(struct wil6210_priv *wil)
void wil_rx_handle(struct wil6210_priv *wil, int *quota)
{
struct net_device *ndev = wil_to_ndev(wil);
struct vring *v = &wil->vring_rx;
@ -463,9 +485,8 @@ void wil_rx_handle(struct wil6210_priv *wil)
return;
}
wil_dbg_txrx(wil, "%s()\n", __func__);
while (NULL != (skb = wil_vring_reap_rx(wil, v))) {
wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
skb->data, skb_headlen(skb), false);
while ((*quota > 0) && (NULL != (skb = wil_vring_reap_rx(wil, v)))) {
(*quota)--;
if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) {
skb->dev = ndev;
@ -600,17 +621,15 @@ static struct vring *wil_find_tx_vring(struct wil6210_priv *wil,
return NULL;
}
static int wil_tx_desc_map(volatile struct vring_tx_desc *d,
dma_addr_t pa, u32 len)
static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len)
{
d->dma.addr_low = lower_32_bits(pa);
d->dma.addr_high = (u16)upper_32_bits(pa);
wil_desc_addr_set(&d->dma.addr, pa);
d->dma.ip_length = 0;
/* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/
d->dma.b11 = 0/*14 | BIT(7)*/;
d->dma.error = 0;
d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
d->dma.length = len;
d->dma.length = cpu_to_le16((u16)len);
d->dma.d0 = 0;
d->mac.d[0] = 0;
d->mac.d[1] = 0;
@ -630,7 +649,8 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
struct sk_buff *skb)
{
struct device *dev = wil_to_dev(wil);
volatile struct vring_tx_desc *d;
struct vring_tx_desc dd, *d = &dd;
volatile struct vring_tx_desc *_d;
u32 swhead = vring->swhead;
int avail = wil_vring_avail_tx(vring);
int nr_frags = skb_shinfo(skb)->nr_frags;
@ -648,7 +668,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
1 + nr_frags);
return -ENOMEM;
}
d = &(vring->va[i].tx);
_d = &(vring->va[i].tx);
/* FIXME FW can accept only unicast frames for the peer */
memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN);
@ -667,25 +687,30 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
wil_tx_desc_map(d, pa, skb_headlen(skb));
d->mac.d[2] |= ((nr_frags + 1) <<
MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS);
if (nr_frags)
*_d = *d;
/* middle segments */
for (f = 0; f < nr_frags; f++) {
const struct skb_frag_struct *frag =
&skb_shinfo(skb)->frags[f];
int len = skb_frag_size(frag);
i = (swhead + f + 1) % vring->size;
d = &(vring->va[i].tx);
_d = &(vring->va[i].tx);
pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag),
DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(dev, pa)))
goto dma_error;
wil_tx_desc_map(d, pa, len);
vring->ctx[i] = NULL;
*_d = *d;
}
/* for the last seg only */
d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS);
d->dma.d0 |= BIT(9); /* BUG: undocumented bit */
d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS);
d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_0_QID_POS);
*_d = *d;
wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
@ -693,6 +718,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
/* advance swhead */
wil_vring_advance_head(vring, nr_frags + 1);
wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
trace_wil6210_tx(vring_index, swhead, skb->len, nr_frags);
iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail));
/* hold reference to skb
* to prevent skb release before accounting
@ -705,14 +731,18 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
/* unmap what we have mapped */
/* Note: increment @f to operate with positive index */
for (f++; f > 0; f--) {
u16 dmalen;
i = (swhead + f) % vring->size;
d = &(vring->va[i].tx);
d->dma.status = TX_DMA_STATUS_DU;
pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
_d = &(vring->va[i].tx);
*d = *_d;
_d->dma.status = TX_DMA_STATUS_DU;
pa = wil_desc_addr(&d->dma.addr);
dmalen = le16_to_cpu(d->dma.length);
if (vring->ctx[i])
dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE);
dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
else
dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE);
dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
}
return -EINVAL;
@ -761,7 +791,6 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
break; /* goto drop; */
}
drop:
netif_tx_stop_all_queues(ndev);
ndev->stats.tx_dropped++;
dev_kfree_skb_any(skb);
@ -771,41 +800,48 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
/**
* Clean up transmitted skb's from the Tx VRING
*
* Return number of descriptors cleared
*
* Safe to call from IRQ
*/
void wil_tx_complete(struct wil6210_priv *wil, int ringid)
int wil_tx_complete(struct wil6210_priv *wil, int ringid)
{
struct net_device *ndev = wil_to_ndev(wil);
struct device *dev = wil_to_dev(wil);
struct vring *vring = &wil->vring_tx[ringid];
int done = 0;
if (!vring->va) {
wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid);
return;
return 0;
}
wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid);
while (!wil_vring_is_empty(vring)) {
volatile struct vring_tx_desc *d1 =
volatile struct vring_tx_desc *_d =
&vring->va[vring->swtail].tx;
struct vring_tx_desc dd, *d = &dd;
dma_addr_t pa;
struct sk_buff *skb;
u16 dmalen;
dd = *d1;
*d = *_d;
if (!(d->dma.status & TX_DMA_STATUS_DU))
break;
dmalen = le16_to_cpu(d->dma.length);
trace_wil6210_tx_done(ringid, vring->swtail, dmalen,
d->dma.error);
wil_dbg_txrx(wil,
"Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n",
vring->swtail, d->dma.length, d->dma.status,
vring->swtail, dmalen, d->dma.status,
d->dma.error);
wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32);
pa = wil_desc_addr(&d->dma.addr);
skb = vring->ctx[vring->swtail];
if (skb) {
if (d->dma.error == 0) {
@ -815,18 +851,21 @@ void wil_tx_complete(struct wil6210_priv *wil, int ringid)
ndev->stats.tx_errors++;
}
dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE);
dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
vring->ctx[vring->swtail] = NULL;
} else {
dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE);
dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
}
d->dma.addr_low = 0;
d->dma.addr_high = 0;
d->dma.addr.addr_low = 0;
d->dma.addr.addr_high = 0;
d->dma.length = 0;
d->dma.status = TX_DMA_STATUS_DU;
vring->swtail = wil_vring_next_tail(vring);
done++;
}
if (wil_vring_avail_tx(vring) > vring->size/4)
netif_tx_wake_all_queues(wil_to_ndev(wil));
return done;
}

View File

@ -27,6 +27,28 @@
#define WIL6210_RTAP_SIZE (128)
/* Tx/Rx path */
/*
* Common representation of physical address in Vring
*/
struct vring_dma_addr {
__le32 addr_low;
__le16 addr_high;
} __packed;
static inline dma_addr_t wil_desc_addr(struct vring_dma_addr *addr)
{
return le32_to_cpu(addr->addr_low) |
((u64)le16_to_cpu(addr->addr_high) << 32);
}
static inline void wil_desc_addr_set(struct vring_dma_addr *addr,
dma_addr_t pa)
{
addr->addr_low = cpu_to_le32(lower_32_bits(pa));
addr->addr_high = cpu_to_le16((u16)upper_32_bits(pa));
}
/*
* Tx descriptor - MAC part
* [dword 0]
@ -216,13 +238,12 @@ struct vring_tx_mac {
struct vring_tx_dma {
u32 d0;
u32 addr_low;
u16 addr_high;
struct vring_dma_addr addr;
u8 ip_length;
u8 b11; /* 0..6: mac_length; 7:ip_version */
u8 error; /* 0..2: err; 3..7: reserved; */
u8 status; /* 0: used; 1..7; reserved */
u16 length;
__le16 length;
} __packed;
/*
@ -315,13 +336,12 @@ struct vring_rx_mac {
struct vring_rx_dma {
u32 d0;
u32 addr_low;
u16 addr_high;
struct vring_dma_addr addr;
u8 ip_length;
u8 b11;
u8 error;
u8 status;
u16 length;
__le16 length;
} __packed;
struct vring_tx_desc {

View File

@ -34,9 +34,11 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
#define WIL6210_MEM_SIZE (2*1024*1024UL)
#define WIL6210_RX_RING_SIZE (128)
#define WIL6210_TX_RING_SIZE (128)
#define WIL6210_MAX_TX_RINGS (24)
#define WIL6210_RX_RING_SIZE (128)
#define WIL6210_TX_RING_SIZE (128)
#define WIL6210_MAX_TX_RINGS (24) /* HW limit */
#define WIL6210_MAX_CID (8) /* HW limit */
#define WIL6210_NAPI_BUDGET (16) /* arbitrary */
/* Hardware definitions begin */
@ -184,6 +186,7 @@ struct vring {
enum { /* for wil6210_priv.status */
wil_status_fwready = 0,
wil_status_fwconnecting,
wil_status_fwconnected,
wil_status_dontscan,
wil_status_reset_done,
@ -239,6 +242,8 @@ struct wil6210_priv {
* - consumed in thread by wmi_event_worker
*/
spinlock_t wmi_ev_lock;
struct napi_struct napi_rx;
struct napi_struct napi_tx;
/* DMA related */
struct vring vring_rx;
struct vring vring_tx[WIL6210_MAX_TX_RINGS];
@ -267,9 +272,13 @@ struct wil6210_priv {
#define wil_to_ndev(i) (wil_to_wdev(i)->netdev)
#define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr))
#define wil_dbg(wil, fmt, arg...) netdev_dbg(wil_to_ndev(wil), fmt, ##arg)
#define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg)
#define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg)
int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...);
int wil_err(struct wil6210_priv *wil, const char *fmt, ...);
int wil_info(struct wil6210_priv *wil, const char *fmt, ...);
#define wil_dbg(wil, fmt, arg...) do { \
netdev_dbg(wil_to_ndev(wil), fmt, ##arg); \
wil_dbg_trace(wil, fmt, ##arg); \
} while (0)
#define wil_dbg_irq(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg)
#define wil_dbg_txrx(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg)
@ -356,10 +365,12 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
void wil_vring_fini_tx(struct wil6210_priv *wil, int id);
netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev);
void wil_tx_complete(struct wil6210_priv *wil, int ringid);
int wil_tx_complete(struct wil6210_priv *wil, int ringid);
void wil6210_unmask_irq_tx(struct wil6210_priv *wil);
/* RX API */
void wil_rx_handle(struct wil6210_priv *wil);
void wil_rx_handle(struct wil6210_priv *wil, int *quota);
void wil6210_unmask_irq_rx(struct wil6210_priv *wil);
int wil_iftype_nl2wmi(enum nl80211_iftype type);

View File

@ -20,6 +20,7 @@
#include "wil6210.h"
#include "txrx.h"
#include "wmi.h"
#include "trace.h"
/**
* WMI event receiving - theory of operations
@ -246,6 +247,8 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len)
iowrite32(r->head = next_head, wil->csr + HOST_MBOX +
offsetof(struct wil6210_mbox_ctl, tx.head));
trace_wil6210_wmi_cmd(cmdid, buf, len);
/* interrupt to FW */
iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT);
@ -406,7 +409,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
(wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
if (wdev->sme_state != CFG80211_SME_CONNECTING) {
if (!test_bit(wil_status_fwconnecting, &wil->status)) {
wil_err(wil, "Not in connecting state\n");
return;
}
@ -430,6 +433,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL);
}
clear_bit(wil_status_fwconnecting, &wil->status);
set_bit(wil_status_fwconnected, &wil->status);
/* FIXME FW can transmit only ucast frames to peer */
@ -635,8 +639,9 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
hdr.flags);
if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) &&
(len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
wil_dbg_wmi(wil, "WMI event 0x%04x\n",
evt->event.wmi.id);
u16 id = le16_to_cpu(evt->event.wmi.id);
wil_dbg_wmi(wil, "WMI event 0x%04x\n", id);
trace_wil6210_wmi_event(id, &evt->event.wmi, len);
}
wil_hex_dump_wmi("evt ", DUMP_PREFIX_OFFSET, 16, 1,
&evt->event.hdr, sizeof(hdr) + len, true);
@ -724,7 +729,7 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan)
.bcon_interval = cpu_to_le16(bi),
.network_type = wmi_nettype,
.disable_sec_offload = 1,
.channel = chan,
.channel = chan - 1,
};
struct {
struct wil6210_mbox_hdr_wmi wmi;

View File

@ -606,7 +606,8 @@ static int brcmf_sdio_pd_remove(struct platform_device *pdev)
static struct platform_driver brcmf_sdio_pd = {
.remove = brcmf_sdio_pd_remove,
.driver = {
.name = BRCMFMAC_SDIO_PDATA_NAME
.name = BRCMFMAC_SDIO_PDATA_NAME,
.owner = THIS_MODULE,
}
};

View File

@ -0,0 +1,46 @@
config CW1200
tristate "CW1200 WLAN support"
depends on MAC80211 && CFG80211
help
This is a driver for the ST-E CW1100 & CW1200 WLAN chipsets.
This option just enables the driver core, see below for
specific bus support.
if CW1200
config CW1200_WLAN_SDIO
tristate "Support SDIO platforms"
depends on CW1200 && MMC
help
Enable support for the CW1200 connected via an SDIO bus.
config CW1200_WLAN_SPI
tristate "Support SPI platforms"
depends on CW1200 && SPI
help
Enables support for the CW1200 connected via a SPI bus.
config CW1200_WLAN_SAGRAD
tristate "Support Sagrad SG901-1091/1098 modules"
depends on CW1200_WLAN_SDIO
help
This provides the platform data glue to support the
Sagrad SG901-1091/1098 modules in their standard SDIO EVK.
It also includes example SPI platform data.
menu "Driver debug features"
depends on CW1200 && DEBUG_FS
config CW1200_ETF
bool "Enable CW1200 Engineering Test Framework hooks"
help
If you don't know what this is, just say N.
config CW1200_ITP
bool "Enable ITP access"
help
If you don't know what this is, just say N.
endmenu
endif

View File

@ -0,0 +1,24 @@
cw1200_core-y := \
fwio.o \
txrx.o \
main.o \
queue.o \
hwio.o \
bh.o \
wsm.o \
sta.o \
scan.o \
pm.o \
debug.o
cw1200_core-$(CONFIG_CW1200_ITP) += itp.o
# CFLAGS_sta.o += -DDEBUG
cw1200_wlan_sdio-y := cw1200_sdio.o
cw1200_wlan_spi-y := cw1200_spi.o
cw1200_wlan_sagrad-y := cw1200_sagrad.o
obj-$(CONFIG_CW1200) += cw1200_core.o
obj-$(CONFIG_CW1200_WLAN_SDIO) += cw1200_wlan_sdio.o
obj-$(CONFIG_CW1200_WLAN_SPI) += cw1200_wlan_spi.o
obj-$(CONFIG_CW1200_WLAN_SAGRAD) += cw1200_wlan_sagrad.o

View File

@ -0,0 +1,616 @@
/*
* Device handling thread implementation for mac80211 ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* Based on:
* ST-Ericsson UMAC CW1200 driver, which is
* Copyright (c) 2010, ST-Ericsson
* Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <net/mac80211.h>
#include <linux/kthread.h>
#include <linux/timer.h>
#include "cw1200.h"
#include "bh.h"
#include "hwio.h"
#include "wsm.h"
#include "sbus.h"
#include "debug.h"
#include "fwio.h"
static int cw1200_bh(void *arg);
#define DOWNLOAD_BLOCK_SIZE_WR (0x1000 - 4)
/* an SPI message cannot be bigger than (2"12-1)*2 bytes
* "*2" to cvt to bytes */
#define MAX_SZ_RD_WR_BUFFERS (DOWNLOAD_BLOCK_SIZE_WR*2)
#define PIGGYBACK_CTRL_REG (2)
#define EFFECTIVE_BUF_SIZE (MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG)
/* Suspend state privates */
enum cw1200_bh_pm_state {
CW1200_BH_RESUMED = 0,
CW1200_BH_SUSPEND,
CW1200_BH_SUSPENDED,
CW1200_BH_RESUME,
};
typedef int (*cw1200_wsm_handler)(struct cw1200_common *priv,
u8 *data, size_t size);
static void cw1200_bh_work(struct work_struct *work)
{
struct cw1200_common *priv =
container_of(work, struct cw1200_common, bh_work);
cw1200_bh(priv);
}
int cw1200_register_bh(struct cw1200_common *priv)
{
int err = 0;
/* Realtime workqueue */
priv->bh_workqueue = alloc_workqueue("cw1200_bh",
WQ_MEM_RECLAIM | WQ_HIGHPRI
| WQ_CPU_INTENSIVE, 1);
if (!priv->bh_workqueue)
return -ENOMEM;
INIT_WORK(&priv->bh_work, cw1200_bh_work);
pr_debug("[BH] register.\n");
atomic_set(&priv->bh_rx, 0);
atomic_set(&priv->bh_tx, 0);
atomic_set(&priv->bh_term, 0);
atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED);
priv->bh_error = 0;
priv->hw_bufs_used = 0;
priv->buf_id_tx = 0;
priv->buf_id_rx = 0;
init_waitqueue_head(&priv->bh_wq);
init_waitqueue_head(&priv->bh_evt_wq);
err = !queue_work(priv->bh_workqueue, &priv->bh_work);
WARN_ON(err);
return err;
}
void cw1200_unregister_bh(struct cw1200_common *priv)
{
atomic_add(1, &priv->bh_term);
wake_up(&priv->bh_wq);
flush_workqueue(priv->bh_workqueue);
destroy_workqueue(priv->bh_workqueue);
priv->bh_workqueue = NULL;
pr_debug("[BH] unregistered.\n");
}
void cw1200_irq_handler(struct cw1200_common *priv)
{
pr_debug("[BH] irq.\n");
/* Disable Interrupts! */
/* NOTE: sbus_ops->lock already held */
__cw1200_irq_enable(priv, 0);
if (/* WARN_ON */(priv->bh_error))
return;
if (atomic_add_return(1, &priv->bh_rx) == 1)
wake_up(&priv->bh_wq);
}
EXPORT_SYMBOL_GPL(cw1200_irq_handler);
void cw1200_bh_wakeup(struct cw1200_common *priv)
{
pr_debug("[BH] wakeup.\n");
if (priv->bh_error) {
pr_err("[BH] wakeup failed (BH error)\n");
return;
}
if (atomic_add_return(1, &priv->bh_tx) == 1)
wake_up(&priv->bh_wq);
}
int cw1200_bh_suspend(struct cw1200_common *priv)
{
pr_debug("[BH] suspend.\n");
if (priv->bh_error) {
wiphy_warn(priv->hw->wiphy, "BH error -- can't suspend\n");
return -EINVAL;
}
atomic_set(&priv->bh_suspend, CW1200_BH_SUSPEND);
wake_up(&priv->bh_wq);
return wait_event_timeout(priv->bh_evt_wq, priv->bh_error ||
(CW1200_BH_SUSPENDED == atomic_read(&priv->bh_suspend)),
1 * HZ) ? 0 : -ETIMEDOUT;
}
int cw1200_bh_resume(struct cw1200_common *priv)
{
pr_debug("[BH] resume.\n");
if (priv->bh_error) {
wiphy_warn(priv->hw->wiphy, "BH error -- can't resume\n");
return -EINVAL;
}
atomic_set(&priv->bh_suspend, CW1200_BH_RESUME);
wake_up(&priv->bh_wq);
return wait_event_timeout(priv->bh_evt_wq, priv->bh_error ||
(CW1200_BH_RESUMED == atomic_read(&priv->bh_suspend)),
1 * HZ) ? 0 : -ETIMEDOUT;
}
static inline void wsm_alloc_tx_buffer(struct cw1200_common *priv)
{
++priv->hw_bufs_used;
}
int wsm_release_tx_buffer(struct cw1200_common *priv, int count)
{
int ret = 0;
int hw_bufs_used = priv->hw_bufs_used;
priv->hw_bufs_used -= count;
if (WARN_ON(priv->hw_bufs_used < 0))
ret = -1;
else if (hw_bufs_used >= priv->wsm_caps.input_buffers)
ret = 1;
if (!priv->hw_bufs_used)
wake_up(&priv->bh_evt_wq);
return ret;
}
static int cw1200_bh_read_ctrl_reg(struct cw1200_common *priv,
u16 *ctrl_reg)
{
int ret;
ret = cw1200_reg_read_16(priv,
ST90TDS_CONTROL_REG_ID, ctrl_reg);
if (ret) {
ret = cw1200_reg_read_16(priv,
ST90TDS_CONTROL_REG_ID, ctrl_reg);
if (ret)
pr_err("[BH] Failed to read control register.\n");
}
return ret;
}
static int cw1200_device_wakeup(struct cw1200_common *priv)
{
u16 ctrl_reg;
int ret;
pr_debug("[BH] Device wakeup.\n");
/* First, set the dpll register */
ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID,
cw1200_dpll_from_clk(priv->hw_refclk));
if (WARN_ON(ret))
return ret;
/* To force the device to be always-on, the host sets WLAN_UP to 1 */
ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID,
ST90TDS_CONT_WUP_BIT);
if (WARN_ON(ret))
return ret;
ret = cw1200_bh_read_ctrl_reg(priv, &ctrl_reg);
if (WARN_ON(ret))
return ret;
/* If the device returns WLAN_RDY as 1, the device is active and will
* remain active. */
if (ctrl_reg & ST90TDS_CONT_RDY_BIT) {
pr_debug("[BH] Device awake.\n");
return 1;
}
return 0;
}
/* Must be called from BH thraed. */
void cw1200_enable_powersave(struct cw1200_common *priv,
bool enable)
{
pr_debug("[BH] Powerave is %s.\n",
enable ? "enabled" : "disabled");
priv->powersave_enabled = enable;
}
static int cw1200_bh_rx_helper(struct cw1200_common *priv,
uint16_t *ctrl_reg,
int *tx)
{
size_t read_len = 0;
struct sk_buff *skb_rx = NULL;
struct wsm_hdr *wsm;
size_t wsm_len;
u16 wsm_id;
u8 wsm_seq;
int rx_resync = 1;
size_t alloc_len;
u8 *data;
read_len = (*ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2;
if (!read_len)
return 0; /* No more work */
if (WARN_ON((read_len < sizeof(struct wsm_hdr)) ||
(read_len > EFFECTIVE_BUF_SIZE))) {
pr_debug("Invalid read len: %zu (%04x)",
read_len, *ctrl_reg);
goto err;
}
/* Add SIZE of PIGGYBACK reg (CONTROL Reg)
* to the NEXT Message length + 2 Bytes for SKB */
read_len = read_len + 2;
alloc_len = priv->sbus_ops->align_size(
priv->sbus_priv, read_len);
/* Check if not exceeding CW1200 capabilities */
if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) {
pr_debug("Read aligned len: %zu\n",
alloc_len);
}
skb_rx = dev_alloc_skb(alloc_len);
if (WARN_ON(!skb_rx))
goto err;
skb_trim(skb_rx, 0);
skb_put(skb_rx, read_len);
data = skb_rx->data;
if (WARN_ON(!data))
goto err;
if (WARN_ON(cw1200_data_read(priv, data, alloc_len))) {
pr_err("rx blew up, len %zu\n", alloc_len);
goto err;
}
/* Piggyback */
*ctrl_reg = __le16_to_cpu(
((__le16 *)data)[alloc_len / 2 - 1]);
wsm = (struct wsm_hdr *)data;
wsm_len = __le16_to_cpu(wsm->len);
if (WARN_ON(wsm_len > read_len))
goto err;
if (priv->wsm_enable_wsm_dumps)
print_hex_dump_bytes("<-- ",
DUMP_PREFIX_NONE,
data, wsm_len);
wsm_id = __le16_to_cpu(wsm->id) & 0xFFF;
wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7;
skb_trim(skb_rx, wsm_len);
if (wsm_id == 0x0800) {
wsm_handle_exception(priv,
&data[sizeof(*wsm)],
wsm_len - sizeof(*wsm));
goto err;
} else if (!rx_resync) {
if (WARN_ON(wsm_seq != priv->wsm_rx_seq))
goto err;
}
priv->wsm_rx_seq = (wsm_seq + 1) & 7;
rx_resync = 0;
if (wsm_id & 0x0400) {
int rc = wsm_release_tx_buffer(priv, 1);
if (WARN_ON(rc < 0))
return rc;
else if (rc > 0)
*tx = 1;
}
/* cw1200_wsm_rx takes care on SKB livetime */
if (WARN_ON(wsm_handle_rx(priv, wsm_id, wsm, &skb_rx)))
goto err;
if (skb_rx) {
dev_kfree_skb(skb_rx);
skb_rx = NULL;
}
return 0;
err:
if (skb_rx) {
dev_kfree_skb(skb_rx);
skb_rx = NULL;
}
return -1;
}
static int cw1200_bh_tx_helper(struct cw1200_common *priv,
int *pending_tx,
int *tx_burst)
{
size_t tx_len;
u8 *data;
int ret;
struct wsm_hdr *wsm;
if (priv->device_can_sleep) {
ret = cw1200_device_wakeup(priv);
if (WARN_ON(ret < 0)) { /* Error in wakeup */
*pending_tx = 1;
return 0;
} else if (ret) { /* Woke up */
priv->device_can_sleep = false;
} else { /* Did not awake */
*pending_tx = 1;
return 0;
}
}
wsm_alloc_tx_buffer(priv);
ret = wsm_get_tx(priv, &data, &tx_len, tx_burst);
if (ret <= 0) {
wsm_release_tx_buffer(priv, 1);
if (WARN_ON(ret < 0))
return ret; /* Error */
return 0; /* No work */
}
wsm = (struct wsm_hdr *)data;
BUG_ON(tx_len < sizeof(*wsm));
BUG_ON(__le16_to_cpu(wsm->len) != tx_len);
atomic_add(1, &priv->bh_tx);
tx_len = priv->sbus_ops->align_size(
priv->sbus_priv, tx_len);
/* Check if not exceeding CW1200 capabilities */
if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE))
pr_debug("Write aligned len: %zu\n", tx_len);
wsm->id &= __cpu_to_le16(0xffff ^ WSM_TX_SEQ(WSM_TX_SEQ_MAX));
wsm->id |= __cpu_to_le16(WSM_TX_SEQ(priv->wsm_tx_seq));
if (WARN_ON(cw1200_data_write(priv, data, tx_len))) {
pr_err("tx blew up, len %zu\n", tx_len);
wsm_release_tx_buffer(priv, 1);
return -1; /* Error */
}
if (priv->wsm_enable_wsm_dumps)
print_hex_dump_bytes("--> ",
DUMP_PREFIX_NONE,
data,
__le16_to_cpu(wsm->len));
wsm_txed(priv, data);
priv->wsm_tx_seq = (priv->wsm_tx_seq + 1) & WSM_TX_SEQ_MAX;
if (*tx_burst > 1) {
cw1200_debug_tx_burst(priv);
return 1; /* Work remains */
}
return 0;
}
static int cw1200_bh(void *arg)
{
struct cw1200_common *priv = arg;
int rx, tx, term, suspend;
u16 ctrl_reg = 0;
int tx_allowed;
int pending_tx = 0;
int tx_burst;
long status;
u32 dummy;
int ret;
for (;;) {
if (!priv->hw_bufs_used &&
priv->powersave_enabled &&
!priv->device_can_sleep &&
!atomic_read(&priv->recent_scan)) {
status = 1 * HZ;
pr_debug("[BH] Device wakedown. No data.\n");
cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, 0);
priv->device_can_sleep = true;
} else if (priv->hw_bufs_used) {
/* Interrupt loss detection */
status = 1 * HZ;
} else {
status = MAX_SCHEDULE_TIMEOUT;
}
/* Dummy Read for SDIO retry mechanism*/
if ((priv->hw_type != -1) &&
(atomic_read(&priv->bh_rx) == 0) &&
(atomic_read(&priv->bh_tx) == 0))
cw1200_reg_read(priv, ST90TDS_CONFIG_REG_ID,
&dummy, sizeof(dummy));
pr_debug("[BH] waiting ...\n");
status = wait_event_interruptible_timeout(priv->bh_wq, ({
rx = atomic_xchg(&priv->bh_rx, 0);
tx = atomic_xchg(&priv->bh_tx, 0);
term = atomic_xchg(&priv->bh_term, 0);
suspend = pending_tx ?
0 : atomic_read(&priv->bh_suspend);
(rx || tx || term || suspend || priv->bh_error);
}), status);
pr_debug("[BH] - rx: %d, tx: %d, term: %d, suspend: %d, status: %ld\n",
rx, tx, term, suspend, status);
/* Did an error occur? */
if ((status < 0 && status != -ERESTARTSYS) ||
term || priv->bh_error) {
break;
}
if (!status) { /* wait_event timed out */
unsigned long timestamp = jiffies;
long timeout;
int pending = 0;
int i;
/* Check to see if we have any outstanding frames */
if (priv->hw_bufs_used && (!rx || !tx)) {
wiphy_warn(priv->hw->wiphy,
"Missed interrupt? (%d frames outstanding)\n",
priv->hw_bufs_used);
rx = 1;
/* Get a timestamp of "oldest" frame */
for (i = 0; i < 4; ++i)
pending += cw1200_queue_get_xmit_timestamp(
&priv->tx_queue[i],
&timestamp,
priv->pending_frame_id);
/* Check if frame transmission is timed out.
* Add an extra second with respect to possible
* interrupt loss.
*/
timeout = timestamp +
WSM_CMD_LAST_CHANCE_TIMEOUT +
1 * HZ -
jiffies;
/* And terminate BH thread if the frame is "stuck" */
if (pending && timeout < 0) {
wiphy_warn(priv->hw->wiphy,
"Timeout waiting for TX confirm (%d/%d pending, %ld vs %lu).\n",
priv->hw_bufs_used, pending,
timestamp, jiffies);
break;
}
} else if (!priv->device_can_sleep &&
!atomic_read(&priv->recent_scan)) {
pr_debug("[BH] Device wakedown. Timeout.\n");
cw1200_reg_write_16(priv,
ST90TDS_CONTROL_REG_ID, 0);
priv->device_can_sleep = true;
}
goto done;
} else if (suspend) {
pr_debug("[BH] Device suspend.\n");
if (priv->powersave_enabled) {
pr_debug("[BH] Device wakedown. Suspend.\n");
cw1200_reg_write_16(priv,
ST90TDS_CONTROL_REG_ID, 0);
priv->device_can_sleep = true;
}
atomic_set(&priv->bh_suspend, CW1200_BH_SUSPENDED);
wake_up(&priv->bh_evt_wq);
status = wait_event_interruptible(priv->bh_wq,
CW1200_BH_RESUME == atomic_read(&priv->bh_suspend));
if (status < 0) {
wiphy_err(priv->hw->wiphy,
"Failed to wait for resume: %ld.\n",
status);
break;
}
pr_debug("[BH] Device resume.\n");
atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED);
wake_up(&priv->bh_evt_wq);
atomic_add(1, &priv->bh_rx);
goto done;
}
rx:
tx += pending_tx;
pending_tx = 0;
if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg))
break;
/* Don't bother trying to rx unless we have data to read */
if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) {
ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx);
if (ret < 0)
break;
/* Double up here if there's more data.. */
if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) {
ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx);
if (ret < 0)
break;
}
}
tx:
if (tx) {
tx = 0;
BUG_ON(priv->hw_bufs_used > priv->wsm_caps.input_buffers);
tx_burst = priv->wsm_caps.input_buffers - priv->hw_bufs_used;
tx_allowed = tx_burst > 0;
if (!tx_allowed) {
/* Buffers full. Ensure we process tx
* after we handle rx..
*/
pending_tx = tx;
goto done_rx;
}
ret = cw1200_bh_tx_helper(priv, &pending_tx, &tx_burst);
if (ret < 0)
break;
if (ret > 0) /* More to transmit */
tx = ret;
/* Re-read ctrl reg */
if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg))
break;
}
done_rx:
if (priv->bh_error)
break;
if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)
goto rx;
if (tx)
goto tx;
done:
/* Re-enable device interrupts */
priv->sbus_ops->lock(priv->sbus_priv);
__cw1200_irq_enable(priv, 1);
priv->sbus_ops->unlock(priv->sbus_priv);
}
/* Explicitly disable device interrupts */
priv->sbus_ops->lock(priv->sbus_priv);
__cw1200_irq_enable(priv, 0);
priv->sbus_ops->unlock(priv->sbus_priv);
if (!term) {
pr_err("[BH] Fatal error, exiting.\n");
priv->bh_error = 1;
/* TODO: schedule_work(recovery) */
}
return 0;
}

View File

@ -0,0 +1,28 @@
/*
* Device handling thread interface for mac80211 ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef CW1200_BH_H
#define CW1200_BH_H
/* extern */ struct cw1200_common;
int cw1200_register_bh(struct cw1200_common *priv);
void cw1200_unregister_bh(struct cw1200_common *priv);
void cw1200_irq_handler(struct cw1200_common *priv);
void cw1200_bh_wakeup(struct cw1200_common *priv);
int cw1200_bh_suspend(struct cw1200_common *priv);
int cw1200_bh_resume(struct cw1200_common *priv);
/* Must be called from BH thread. */
void cw1200_enable_powersave(struct cw1200_common *priv,
bool enable);
int wsm_release_tx_buffer(struct cw1200_common *priv, int count);
#endif /* CW1200_BH_H */

View File

@ -0,0 +1,332 @@
/*
* Common private data for ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* Based on the mac80211 Prism54 code, which is
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
*
* Based on the islsm (softmac prism54) driver, which is:
* Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef CW1200_H
#define CW1200_H
#include <linux/wait.h>
#include <linux/version.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include <net/mac80211.h>
#include "queue.h"
#include "wsm.h"
#include "scan.h"
#include "txrx.h"
#include "pm.h"
/* Forward declarations */
struct sbus_ops;
struct task_struct;
struct cw1200_debug_priv;
struct firmware;
#ifdef CONFIG_CW1200_ETF
extern int etf_mode;
extern char *etf_firmware;
#endif
#define CW1200_MAX_CTRL_FRAME_LEN (0x1000)
#define CW1200_MAX_STA_IN_AP_MODE (5)
#define CW1200_LINK_ID_AFTER_DTIM (CW1200_MAX_STA_IN_AP_MODE + 1)
#define CW1200_LINK_ID_UAPSD (CW1200_MAX_STA_IN_AP_MODE + 2)
#define CW1200_LINK_ID_MAX (CW1200_MAX_STA_IN_AP_MODE + 3)
#define CW1200_MAX_REQUEUE_ATTEMPTS (5)
#define CW1200_MAX_TID (8)
#define CW1200_BLOCK_ACK_CNT (30)
#define CW1200_BLOCK_ACK_THLD (800)
#define CW1200_BLOCK_ACK_HIST (3)
#define CW1200_BLOCK_ACK_INTERVAL (1 * HZ / CW1200_BLOCK_ACK_HIST)
#define CW1200_JOIN_TIMEOUT (1 * HZ)
#define CW1200_AUTH_TIMEOUT (5 * HZ)
struct cw1200_ht_info {
struct ieee80211_sta_ht_cap ht_cap;
enum nl80211_channel_type channel_type;
u16 operation_mode;
};
/* Please keep order */
enum cw1200_join_status {
CW1200_JOIN_STATUS_PASSIVE = 0,
CW1200_JOIN_STATUS_MONITOR,
CW1200_JOIN_STATUS_JOINING,
CW1200_JOIN_STATUS_PRE_STA,
CW1200_JOIN_STATUS_STA,
CW1200_JOIN_STATUS_IBSS,
CW1200_JOIN_STATUS_AP,
};
enum cw1200_link_status {
CW1200_LINK_OFF,
CW1200_LINK_RESERVE,
CW1200_LINK_SOFT,
CW1200_LINK_HARD,
CW1200_LINK_RESET,
CW1200_LINK_RESET_REMAP,
};
extern int cw1200_power_mode;
extern const char * const cw1200_fw_types[];
struct cw1200_link_entry {
unsigned long timestamp;
enum cw1200_link_status status;
enum cw1200_link_status prev_status;
u8 mac[ETH_ALEN];
u8 buffered[CW1200_MAX_TID];
struct sk_buff_head rx_queue;
};
struct cw1200_common {
/* interfaces to the rest of the stack */
struct ieee80211_hw *hw;
struct ieee80211_vif *vif;
struct device *pdev;
/* Statistics */
struct ieee80211_low_level_stats stats;
/* Our macaddr */
u8 mac_addr[ETH_ALEN];
/* Hardware interface */
const struct sbus_ops *sbus_ops;
struct sbus_priv *sbus_priv;
/* Hardware information */
enum {
HIF_9000_SILICON_VERSATILE = 0,
HIF_8601_VERSATILE,
HIF_8601_SILICON,
} hw_type;
enum {
CW1200_HW_REV_CUT10 = 10,
CW1200_HW_REV_CUT11 = 11,
CW1200_HW_REV_CUT20 = 20,
CW1200_HW_REV_CUT22 = 22,
CW1X60_HW_REV = 40,
} hw_revision;
int hw_refclk;
bool hw_have_5ghz;
const struct firmware *sdd;
char *sdd_path;
struct cw1200_debug_priv *debug;
struct workqueue_struct *workqueue;
struct mutex conf_mutex;
struct cw1200_queue tx_queue[4];
struct cw1200_queue_stats tx_queue_stats;
int tx_burst_idx;
/* firmware/hardware info */
unsigned int tx_hdr_len;
/* Radio data */
int output_power;
/* BBP/MAC state */
struct ieee80211_rate *rates;
struct ieee80211_rate *mcs_rates;
struct ieee80211_channel *channel;
struct wsm_edca_params edca;
struct wsm_tx_queue_params tx_queue_params;
struct wsm_mib_association_mode association_mode;
struct wsm_set_bss_params bss_params;
struct cw1200_ht_info ht_info;
struct wsm_set_pm powersave_mode;
struct wsm_set_pm firmware_ps_mode;
int cqm_rssi_thold;
unsigned cqm_rssi_hyst;
bool cqm_use_rssi;
int cqm_beacon_loss_count;
int channel_switch_in_progress;
wait_queue_head_t channel_switch_done;
u8 long_frame_max_tx_count;
u8 short_frame_max_tx_count;
int mode;
bool enable_beacon;
int beacon_int;
bool listening;
struct wsm_rx_filter rx_filter;
struct wsm_mib_multicast_filter multicast_filter;
bool has_multicast_subscription;
bool disable_beacon_filter;
struct work_struct update_filtering_work;
struct work_struct set_beacon_wakeup_period_work;
u8 ba_rx_tid_mask;
u8 ba_tx_tid_mask;
struct cw1200_pm_state pm_state;
struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo;
struct wsm_uapsd_info uapsd_info;
bool setbssparams_done;
bool bt_present;
u8 conf_listen_interval;
u32 listen_interval;
u32 erp_info;
u32 rts_threshold;
/* BH */
atomic_t bh_rx;
atomic_t bh_tx;
atomic_t bh_term;
atomic_t bh_suspend;
struct workqueue_struct *bh_workqueue;
struct work_struct bh_work;
int bh_error;
wait_queue_head_t bh_wq;
wait_queue_head_t bh_evt_wq;
u8 buf_id_tx;
u8 buf_id_rx;
u8 wsm_rx_seq;
u8 wsm_tx_seq;
int hw_bufs_used;
bool powersave_enabled;
bool device_can_sleep;
/* Scan status */
struct cw1200_scan scan;
/* Keep cw1200 awake (WUP = 1) 1 second after each scan to avoid
* FW issue with sleeping/waking up. */
atomic_t recent_scan;
struct delayed_work clear_recent_scan_work;
/* WSM */
struct wsm_startup_ind wsm_caps;
struct mutex wsm_cmd_mux;
struct wsm_buf wsm_cmd_buf;
struct wsm_cmd wsm_cmd;
wait_queue_head_t wsm_cmd_wq;
wait_queue_head_t wsm_startup_done;
int firmware_ready;
atomic_t tx_lock;
/* WSM debug */
int wsm_enable_wsm_dumps;
/* WSM Join */
enum cw1200_join_status join_status;
u32 pending_frame_id;
bool join_pending;
struct delayed_work join_timeout;
struct work_struct unjoin_work;
struct work_struct join_complete_work;
int join_complete_status;
int join_dtim_period;
bool delayed_unjoin;
/* TX/RX and security */
s8 wep_default_key_id;
struct work_struct wep_key_work;
u32 key_map;
struct wsm_add_key keys[WSM_KEY_MAX_INDEX + 1];
/* AP powersave */
u32 link_id_map;
struct cw1200_link_entry link_id_db[CW1200_MAX_STA_IN_AP_MODE];
struct work_struct link_id_work;
struct delayed_work link_id_gc_work;
u32 sta_asleep_mask;
u32 pspoll_mask;
bool aid0_bit_set;
spinlock_t ps_state_lock; /* Protect power save state */
bool buffered_multicasts;
bool tx_multicast;
struct work_struct set_tim_work;
struct work_struct set_cts_work;
struct work_struct multicast_start_work;
struct work_struct multicast_stop_work;
struct timer_list mcast_timeout;
/* WSM events and CQM implementation */
spinlock_t event_queue_lock; /* Protect event queue */
struct list_head event_queue;
struct work_struct event_handler;
struct delayed_work bss_loss_work;
spinlock_t bss_loss_lock; /* Protect BSS loss state */
int bss_loss_state;
int bss_loss_confirm_id;
int delayed_link_loss;
struct work_struct bss_params_work;
/* TX rate policy cache */
struct tx_policy_cache tx_policy_cache;
struct work_struct tx_policy_upload_work;
/* legacy PS mode switch in suspend */
int ps_mode_switch_in_progress;
wait_queue_head_t ps_mode_switch_done;
/* Workaround for WFD testcase 6.1.10*/
struct work_struct linkid_reset_work;
u8 action_frame_sa[ETH_ALEN];
u8 action_linkid;
#ifdef CONFIG_CW1200_ETF
struct sk_buff_head etf_q;
#endif
};
struct cw1200_sta_priv {
int link_id;
};
/* interfaces for the drivers */
int cw1200_core_probe(const struct sbus_ops *sbus_ops,
struct sbus_priv *sbus,
struct device *pdev,
struct cw1200_common **pself,
int ref_clk, const u8 *macaddr,
const char *sdd_path, bool have_5ghz);
void cw1200_core_release(struct cw1200_common *self);
#define FWLOAD_BLOCK_SIZE (1024)
static inline int cw1200_is_ht(const struct cw1200_ht_info *ht_info)
{
return ht_info->channel_type != NL80211_CHAN_NO_HT;
}
static inline int cw1200_ht_greenfield(const struct cw1200_ht_info *ht_info)
{
return cw1200_is_ht(ht_info) &&
(ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) &&
!(ht_info->operation_mode &
IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
}
static inline int cw1200_ht_ampdu_density(const struct cw1200_ht_info *ht_info)
{
if (!cw1200_is_ht(ht_info))
return 0;
return ht_info->ht_cap.ampdu_density;
}
#endif /* CW1200_H */

View File

@ -0,0 +1,145 @@
/*
* Platform glue data for ST-Ericsson CW1200 driver
*
* Copyright (c) 2013, Sagrad, Inc
* Author: Solomon Peachy <speachy@sagrad.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/cw1200_platform.h>
MODULE_AUTHOR("Solomon Peachy <speachy@sagrad.com>");
MODULE_DESCRIPTION("ST-Ericsson CW1200 Platform glue driver");
MODULE_LICENSE("GPL");
/* Define just one of these. Feel free to customize as needed */
#define SAGRAD_1091_1098_EVK_SDIO
/* #define SAGRAD_1091_1098_EVK_SPI */
#ifdef SAGRAD_1091_1098_EVK_SDIO
#if 0
static struct resource cw1200_href_resources[] = {
{
.start = 215, /* fix me as appropriate */
.end = 215, /* ditto */
.flags = IORESOURCE_IO,
.name = "cw1200_wlan_reset",
},
{
.start = 216, /* fix me as appropriate */
.end = 216, /* ditto */
.flags = IORESOURCE_IO,
.name = "cw1200_wlan_powerup",
},
{
.start = NOMADIK_GPIO_TO_IRQ(216), /* fix me as appropriate */
.end = NOMADIK_GPIO_TO_IRQ(216), /* ditto */
.flags = IORESOURCE_IRQ,
.name = "cw1200_wlan_irq",
},
};
#endif
static int cw1200_power_ctrl(const struct cw1200_platform_data_sdio *pdata,
bool enable)
{
/* Control 3v3 and 1v8 to hardware as appropriate */
/* Note this is not needed if it's controlled elsewhere or always on */
/* May require delay for power to stabilize */
return 0;
}
static int cw1200_clk_ctrl(const struct cw1200_platform_data_sdio *pdata,
bool enable)
{
/* Turn CLK_32K off and on as appropriate. */
/* Note this is not needed if it's always on */
/* May require delay for clock to stabilize */
return 0;
}
static struct cw1200_platform_data_sdio cw1200_platform_data = {
.ref_clk = 38400,
.have_5ghz = false,
#if 0
.reset = &cw1200_href_resources[0],
.powerup = &cw1200_href_resources[1],
.irq = &cw1200_href_resources[2],
#endif
.power_ctrl = cw1200_power_ctrl,
.clk_ctrl = cw1200_clk_ctrl,
/* .macaddr = ??? */
.sdd_file = "sdd_sagrad_1091_1098.bin",
};
#endif
#ifdef SAGRAD_1091_1098_EVK_SPI
/* Note that this is an example of integrating into your board support file */
static struct resource cw1200_href_resources[] = {
{
.start = GPIO_RF_RESET,
.end = GPIO_RF_RESET,
.flags = IORESOURCE_IO,
.name = "cw1200_wlan_reset",
},
{
.start = GPIO_RF_POWERUP,
.end = GPIO_RF_POWERUP,
.flags = IORESOURCE_IO,
.name = "cw1200_wlan_powerup",
},
};
static int cw1200_power_ctrl(const struct cw1200_platform_data_spi *pdata,
bool enable)
{
/* Control 3v3 and 1v8 to hardware as appropriate */
/* Note this is not needed if it's controlled elsewhere or always on */
/* May require delay for power to stabilize */
return 0;
}
static int cw1200_clk_ctrl(const struct cw1200_platform_data_spi *pdata,
bool enable)
{
/* Turn CLK_32K off and on as appropriate. */
/* Note this is not needed if it's always on */
/* May require delay for clock to stabilize */
return 0;
}
static struct cw1200_platform_data_spi cw1200_platform_data = {
.ref_clk = 38400,
.spi_bits_per_word = 16,
.reset = &cw1200_href_resources[0],
.powerup = &cw1200_href_resources[1],
.power_ctrl = cw1200_power_ctrl,
.clk_ctrl = cw1200_clk_ctrl,
/* .macaddr = ??? */
.sdd_file = "sdd_sagrad_1091_1098.bin",
};
static struct spi_board_info myboard_spi_devices[] __initdata = {
{
.modalias = "cw1200_wlan_spi",
.max_speed_hz = 10000000, /* 52MHz Max */
.bus_num = 0,
.irq = WIFI_IRQ,
.platform_data = &cw1200_platform_data,
.chip_select = 0,
},
};
#endif
const void *cw1200_get_platform_data(void)
{
return &cw1200_platform_data;
}
EXPORT_SYMBOL_GPL(cw1200_get_platform_data);

View File

@ -0,0 +1,409 @@
/*
* Mac80211 SDIO driver for ST-Ericsson CW1200 device
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio.h>
#include <net/mac80211.h>
#include "cw1200.h"
#include "sbus.h"
#include <linux/cw1200_platform.h>
#include "hwio.h"
MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>");
MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SDIO driver");
MODULE_LICENSE("GPL");
#define SDIO_BLOCK_SIZE (512)
struct sbus_priv {
struct sdio_func *func;
struct cw1200_common *core;
const struct cw1200_platform_data_sdio *pdata;
};
#ifndef SDIO_VENDOR_ID_STE
#define SDIO_VENDOR_ID_STE 0x0020
#endif
#ifndef SDIO_DEVICE_ID_STE_CW1200
#define SDIO_DEVICE_ID_STE_CW1200 0x2280
#endif
static const struct sdio_device_id cw1200_sdio_ids[] = {
{ SDIO_DEVICE(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200) },
{ /* end: all zeroes */ },
};
/* sbus_ops implemetation */
static int cw1200_sdio_memcpy_fromio(struct sbus_priv *self,
unsigned int addr,
void *dst, int count)
{
return sdio_memcpy_fromio(self->func, dst, addr, count);
}
static int cw1200_sdio_memcpy_toio(struct sbus_priv *self,
unsigned int addr,
const void *src, int count)
{
return sdio_memcpy_toio(self->func, addr, (void *)src, count);
}
static void cw1200_sdio_lock(struct sbus_priv *self)
{
sdio_claim_host(self->func);
}
static void cw1200_sdio_unlock(struct sbus_priv *self)
{
sdio_release_host(self->func);
}
static void cw1200_sdio_irq_handler(struct sdio_func *func)
{
struct sbus_priv *self = sdio_get_drvdata(func);
/* note: sdio_host already claimed here. */
if (self->core)
cw1200_irq_handler(self->core);
}
static irqreturn_t cw1200_gpio_hardirq(int irq, void *dev_id)
{
return IRQ_WAKE_THREAD;
}
static irqreturn_t cw1200_gpio_irq(int irq, void *dev_id)
{
struct sbus_priv *self = dev_id;
if (self->core) {
sdio_claim_host(self->func);
cw1200_irq_handler(self->core);
sdio_release_host(self->func);
return IRQ_HANDLED;
} else {
return IRQ_NONE;
}
}
static int cw1200_request_irq(struct sbus_priv *self)
{
int ret;
const struct resource *irq = self->pdata->irq;
u8 cccr;
cccr = sdio_f0_readb(self->func, SDIO_CCCR_IENx, &ret);
if (WARN_ON(ret))
goto err;
/* Master interrupt enable ... */
cccr |= BIT(0);
/* ... for our function */
cccr |= BIT(self->func->num);
sdio_f0_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret);
if (WARN_ON(ret))
goto err;
ret = enable_irq_wake(irq->start);
if (WARN_ON(ret))
goto err;
/* Request the IRQ */
ret = request_threaded_irq(irq->start, cw1200_gpio_hardirq,
cw1200_gpio_irq,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
irq->name, self);
if (WARN_ON(ret))
goto err;
return 0;
err:
return ret;
}
static int cw1200_sdio_irq_subscribe(struct sbus_priv *self)
{
int ret = 0;
pr_debug("SW IRQ subscribe\n");
sdio_claim_host(self->func);
if (self->pdata->irq)
ret = cw1200_request_irq(self);
else
ret = sdio_claim_irq(self->func, cw1200_sdio_irq_handler);
sdio_release_host(self->func);
return ret;
}
static int cw1200_sdio_irq_unsubscribe(struct sbus_priv *self)
{
int ret = 0;
pr_debug("SW IRQ unsubscribe\n");
if (self->pdata->irq) {
disable_irq_wake(self->pdata->irq->start);
free_irq(self->pdata->irq->start, self);
} else {
sdio_claim_host(self->func);
ret = sdio_release_irq(self->func);
sdio_release_host(self->func);
}
return ret;
}
static int cw1200_sdio_off(const struct cw1200_platform_data_sdio *pdata)
{
const struct resource *reset = pdata->reset;
if (reset) {
gpio_set_value(reset->start, 0);
msleep(30); /* Min is 2 * CLK32K cycles */
gpio_free(reset->start);
}
if (pdata->power_ctrl)
pdata->power_ctrl(pdata, false);
if (pdata->clk_ctrl)
pdata->clk_ctrl(pdata, false);
return 0;
}
static int cw1200_sdio_on(const struct cw1200_platform_data_sdio *pdata)
{
const struct resource *reset = pdata->reset;
const struct resource *powerup = pdata->reset;
/* Ensure I/Os are pulled low */
if (reset) {
gpio_request(reset->start, reset->name);
gpio_direction_output(reset->start, 0);
}
if (powerup) {
gpio_request(powerup->start, powerup->name);
gpio_direction_output(powerup->start, 0);
}
if (reset || powerup)
msleep(50); /* Settle time */
/* Enable 3v3 and 1v8 to hardware */
if (pdata->power_ctrl) {
if (pdata->power_ctrl(pdata, true)) {
pr_err("power_ctrl() failed!\n");
return -1;
}
}
/* Enable CLK32K */
if (pdata->clk_ctrl) {
if (pdata->clk_ctrl(pdata, true)) {
pr_err("clk_ctrl() failed!\n");
return -1;
}
msleep(10); /* Delay until clock is stable for 2 cycles */
}
/* Enable POWERUP signal */
if (powerup) {
gpio_set_value(powerup->start, 1);
msleep(250); /* or more..? */
}
/* Enable RSTn signal */
if (reset) {
gpio_set_value(reset->start, 1);
msleep(50); /* Or more..? */
}
return 0;
}
static size_t cw1200_sdio_align_size(struct sbus_priv *self, size_t size)
{
if (self->pdata->no_nptb)
size = round_up(size, SDIO_BLOCK_SIZE);
else
size = sdio_align_size(self->func, size);
return size;
}
static int cw1200_sdio_pm(struct sbus_priv *self, bool suspend)
{
int ret = 0;
if (self->pdata->irq)
ret = irq_set_irq_wake(self->pdata->irq->start, suspend);
return ret;
}
static struct sbus_ops cw1200_sdio_sbus_ops = {
.sbus_memcpy_fromio = cw1200_sdio_memcpy_fromio,
.sbus_memcpy_toio = cw1200_sdio_memcpy_toio,
.lock = cw1200_sdio_lock,
.unlock = cw1200_sdio_unlock,
.align_size = cw1200_sdio_align_size,
.power_mgmt = cw1200_sdio_pm,
};
/* Probe Function to be called by SDIO stack when device is discovered */
static int cw1200_sdio_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
struct sbus_priv *self;
int status;
pr_info("cw1200_wlan_sdio: Probe called\n");
/* We are only able to handle the wlan function */
if (func->num != 0x01)
return -ENODEV;
self = kzalloc(sizeof(*self), GFP_KERNEL);
if (!self) {
pr_err("Can't allocate SDIO sbus_priv.\n");
return -ENOMEM;
}
func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
self->pdata = cw1200_get_platform_data();
self->func = func;
sdio_set_drvdata(func, self);
sdio_claim_host(func);
sdio_enable_func(func);
sdio_release_host(func);
status = cw1200_sdio_irq_subscribe(self);
status = cw1200_core_probe(&cw1200_sdio_sbus_ops,
self, &func->dev, &self->core,
self->pdata->ref_clk,
self->pdata->macaddr,
self->pdata->sdd_file,
self->pdata->have_5ghz);
if (status) {
cw1200_sdio_irq_unsubscribe(self);
sdio_claim_host(func);
sdio_disable_func(func);
sdio_release_host(func);
sdio_set_drvdata(func, NULL);
kfree(self);
}
return status;
}
/* Disconnect Function to be called by SDIO stack when
* device is disconnected */
static void cw1200_sdio_disconnect(struct sdio_func *func)
{
struct sbus_priv *self = sdio_get_drvdata(func);
if (self) {
cw1200_sdio_irq_unsubscribe(self);
if (self->core) {
cw1200_core_release(self->core);
self->core = NULL;
}
sdio_claim_host(func);
sdio_disable_func(func);
sdio_release_host(func);
sdio_set_drvdata(func, NULL);
kfree(self);
}
}
static int cw1200_sdio_suspend(struct device *dev)
{
int ret;
struct sdio_func *func = dev_to_sdio_func(dev);
struct sbus_priv *self = sdio_get_drvdata(func);
if (!cw1200_can_suspend(self->core))
return -EAGAIN;
/* Notify SDIO that CW1200 will remain powered during suspend */
ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
if (ret)
pr_err("Error setting SDIO pm flags: %i\n", ret);
return ret;
}
static int cw1200_sdio_resume(struct device *dev)
{
return 0;
}
static const struct dev_pm_ops cw1200_pm_ops = {
.suspend = cw1200_sdio_suspend,
.resume = cw1200_sdio_resume,
};
static struct sdio_driver sdio_driver = {
.name = "cw1200_wlan_sdio",
.id_table = cw1200_sdio_ids,
.probe = cw1200_sdio_probe,
.remove = cw1200_sdio_disconnect,
.drv = {
.pm = &cw1200_pm_ops,
}
};
/* Init Module function -> Called by insmod */
static int __init cw1200_sdio_init(void)
{
const struct cw1200_platform_data_sdio *pdata;
int ret;
pdata = cw1200_get_platform_data();
if (cw1200_sdio_on(pdata)) {
ret = -1;
goto err;
}
ret = sdio_register_driver(&sdio_driver);
if (ret)
goto err;
return 0;
err:
cw1200_sdio_off(pdata);
return ret;
}
/* Called at Driver Unloading */
static void __exit cw1200_sdio_exit(void)
{
const struct cw1200_platform_data_sdio *pdata;
pdata = cw1200_get_platform_data();
sdio_unregister_driver(&sdio_driver);
cw1200_sdio_off(pdata);
}
module_init(cw1200_sdio_init);
module_exit(cw1200_sdio_exit);

View File

@ -0,0 +1,480 @@
/*
* Mac80211 SPI driver for ST-Ericsson CW1200 device
*
* Copyright (c) 2011, Sagrad Inc.
* Author: Solomon Peachy <speachy@sagrad.com>
*
* Based on cw1200_sdio.c
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <net/mac80211.h>
#include <linux/spi/spi.h>
#include <linux/device.h>
#include "cw1200.h"
#include "sbus.h"
#include <linux/cw1200_platform.h>
#include "hwio.h"
MODULE_AUTHOR("Solomon Peachy <speachy@sagrad.com>");
MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SPI driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:cw1200_wlan_spi");
/* #define SPI_DEBUG */
struct sbus_priv {
struct spi_device *func;
struct cw1200_common *core;
const struct cw1200_platform_data_spi *pdata;
spinlock_t lock; /* Serialize all bus operations */
int claimed;
};
#define SDIO_TO_SPI_ADDR(addr) ((addr & 0x1f)>>2)
#define SET_WRITE 0x7FFF /* usage: and operation */
#define SET_READ 0x8000 /* usage: or operation */
/*
Notes on byte ordering:
LE: B0 B1 B2 B3
BE: B3 B2 B1 B0
Hardware expects 32-bit data to be written as 16-bit BE words:
B1 B0 B3 B2
*/
static int cw1200_spi_memcpy_fromio(struct sbus_priv *self,
unsigned int addr,
void *dst, int count)
{
int ret, i;
uint16_t regaddr;
struct spi_message m;
struct spi_transfer t_addr = {
.tx_buf = &regaddr,
.len = sizeof(regaddr),
};
struct spi_transfer t_msg = {
.rx_buf = dst,
.len = count,
};
regaddr = (SDIO_TO_SPI_ADDR(addr))<<12;
regaddr |= SET_READ;
regaddr |= (count>>1);
regaddr = cpu_to_le16(regaddr);
#ifdef SPI_DEBUG
pr_info("READ : %04d from 0x%02x (%04x)\n", count, addr,
le16_to_cpu(regaddr));
#endif
#if defined(__LITTLE_ENDIAN)
/* We have to byteswap if the SPI bus is limited to 8b operation */
if (self->func->bits_per_word == 8)
#endif
regaddr = swab16(regaddr);
spi_message_init(&m);
spi_message_add_tail(&t_addr, &m);
spi_message_add_tail(&t_msg, &m);
ret = spi_sync(self->func, &m);
#ifdef SPI_DEBUG
pr_info("READ : ");
for (i = 0; i < t_addr.len; i++)
printk("%02x ", ((u8 *)t_addr.tx_buf)[i]);
printk(" : ");
for (i = 0; i < t_msg.len; i++)
printk("%02x ", ((u8 *)t_msg.rx_buf)[i]);
printk("\n");
#endif
#if defined(__LITTLE_ENDIAN)
/* We have to byteswap if the SPI bus is limited to 8b operation */
if (self->func->bits_per_word == 8)
#endif
{
uint16_t *buf = (uint16_t *)dst;
for (i = 0; i < ((count + 1) >> 1); i++)
buf[i] = swab16(buf[i]);
}
return ret;
}
static int cw1200_spi_memcpy_toio(struct sbus_priv *self,
unsigned int addr,
const void *src, int count)
{
int rval, i;
uint16_t regaddr;
struct spi_transfer t_addr = {
.tx_buf = &regaddr,
.len = sizeof(regaddr),
};
struct spi_transfer t_msg = {
.tx_buf = src,
.len = count,
};
struct spi_message m;
regaddr = (SDIO_TO_SPI_ADDR(addr))<<12;
regaddr &= SET_WRITE;
regaddr |= (count>>1);
regaddr = cpu_to_le16(regaddr);
#ifdef SPI_DEBUG
pr_info("WRITE: %04d to 0x%02x (%04x)\n", count, addr,
le16_to_cpu(regaddr));
#endif
#if defined(__LITTLE_ENDIAN)
/* We have to byteswap if the SPI bus is limited to 8b operation */
if (self->func->bits_per_word == 8)
#endif
{
uint16_t *buf = (uint16_t *)src;
regaddr = swab16(regaddr);
for (i = 0; i < ((count + 1) >> 1); i++)
buf[i] = swab16(buf[i]);
}
#ifdef SPI_DEBUG
pr_info("WRITE: ");
for (i = 0; i < t_addr.len; i++)
printk("%02x ", ((u8 *)t_addr.tx_buf)[i]);
printk(" : ");
for (i = 0; i < t_msg.len; i++)
printk("%02x ", ((u8 *)t_msg.tx_buf)[i]);
printk("\n");
#endif
spi_message_init(&m);
spi_message_add_tail(&t_addr, &m);
spi_message_add_tail(&t_msg, &m);
rval = spi_sync(self->func, &m);
#ifdef SPI_DEBUG
pr_info("WROTE: %d\n", m.actual_length);
#endif
#if defined(__LITTLE_ENDIAN)
/* We have to byteswap if the SPI bus is limited to 8b operation */
if (self->func->bits_per_word == 8)
#endif
{
uint16_t *buf = (uint16_t *)src;
for (i = 0; i < ((count + 1) >> 1); i++)
buf[i] = swab16(buf[i]);
}
return rval;
}
static void cw1200_spi_lock(struct sbus_priv *self)
{
unsigned long flags;
might_sleep();
spin_lock_irqsave(&self->lock, flags);
while (1) {
set_current_state(TASK_UNINTERRUPTIBLE);
if (!self->claimed)
break;
spin_unlock_irqrestore(&self->lock, flags);
schedule();
spin_lock_irqsave(&self->lock, flags);
}
set_current_state(TASK_RUNNING);
self->claimed = 1;
spin_unlock_irqrestore(&self->lock, flags);
return;
}
static void cw1200_spi_unlock(struct sbus_priv *self)
{
unsigned long flags;
spin_lock_irqsave(&self->lock, flags);
self->claimed = 0;
spin_unlock_irqrestore(&self->lock, flags);
return;
}
static irqreturn_t cw1200_spi_irq_handler(int irq, void *dev_id)
{
struct sbus_priv *self = dev_id;
if (self->core) {
cw1200_irq_handler(self->core);
return IRQ_HANDLED;
} else {
return IRQ_NONE;
}
}
static int cw1200_spi_irq_subscribe(struct sbus_priv *self)
{
int ret;
pr_debug("SW IRQ subscribe\n");
ret = request_any_context_irq(self->func->irq, cw1200_spi_irq_handler,
IRQF_TRIGGER_HIGH,
"cw1200_wlan_irq", self);
if (WARN_ON(ret < 0))
goto exit;
ret = enable_irq_wake(self->func->irq);
if (WARN_ON(ret))
goto free_irq;
return 0;
free_irq:
free_irq(self->func->irq, self);
exit:
return ret;
}
static int cw1200_spi_irq_unsubscribe(struct sbus_priv *self)
{
int ret = 0;
pr_debug("SW IRQ unsubscribe\n");
disable_irq_wake(self->func->irq);
free_irq(self->func->irq, self);
return ret;
}
static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata)
{
const struct resource *reset = pdata->reset;
if (reset) {
gpio_set_value(reset->start, 0);
msleep(30); /* Min is 2 * CLK32K cycles */
gpio_free(reset->start);
}
if (pdata->power_ctrl)
pdata->power_ctrl(pdata, false);
if (pdata->clk_ctrl)
pdata->clk_ctrl(pdata, false);
return 0;
}
static int cw1200_spi_on(const struct cw1200_platform_data_spi *pdata)
{
const struct resource *reset = pdata->reset;
const struct resource *powerup = pdata->reset;
/* Ensure I/Os are pulled low */
if (reset) {
gpio_request(reset->start, reset->name);
gpio_direction_output(reset->start, 0);
}
if (powerup) {
gpio_request(powerup->start, powerup->name);
gpio_direction_output(powerup->start, 0);
}
if (reset || powerup)
msleep(10); /* Settle time? */
/* Enable 3v3 and 1v8 to hardware */
if (pdata->power_ctrl) {
if (pdata->power_ctrl(pdata, true)) {
pr_err("power_ctrl() failed!\n");
return -1;
}
}
/* Enable CLK32K */
if (pdata->clk_ctrl) {
if (pdata->clk_ctrl(pdata, true)) {
pr_err("clk_ctrl() failed!\n");
return -1;
}
msleep(10); /* Delay until clock is stable for 2 cycles */
}
/* Enable POWERUP signal */
if (powerup) {
gpio_set_value(powerup->start, 1);
msleep(250); /* or more..? */
}
/* Enable RSTn signal */
if (reset) {
gpio_set_value(reset->start, 1);
msleep(50); /* Or more..? */
}
return 0;
}
static size_t cw1200_spi_align_size(struct sbus_priv *self, size_t size)
{
return size & 1 ? size + 1 : size;
}
static int cw1200_spi_pm(struct sbus_priv *self, bool suspend)
{
return irq_set_irq_wake(self->func->irq, suspend);
}
static struct sbus_ops cw1200_spi_sbus_ops = {
.sbus_memcpy_fromio = cw1200_spi_memcpy_fromio,
.sbus_memcpy_toio = cw1200_spi_memcpy_toio,
.lock = cw1200_spi_lock,
.unlock = cw1200_spi_unlock,
.align_size = cw1200_spi_align_size,
.power_mgmt = cw1200_spi_pm,
};
/* Probe Function to be called by SPI stack when device is discovered */
static int cw1200_spi_probe(struct spi_device *func)
{
const struct cw1200_platform_data_spi *plat_data =
func->dev.platform_data;
struct sbus_priv *self;
int status;
/* Sanity check speed */
if (func->max_speed_hz > 52000000)
func->max_speed_hz = 52000000;
if (func->max_speed_hz < 1000000)
func->max_speed_hz = 1000000;
/* Fix up transfer size */
if (plat_data->spi_bits_per_word)
func->bits_per_word = plat_data->spi_bits_per_word;
if (!func->bits_per_word)
func->bits_per_word = 16;
/* And finally.. */
func->mode = SPI_MODE_0;
pr_info("cw1200_wlan_spi: Probe called (CS %d M %d BPW %d CLK %d)\n",
func->chip_select, func->mode, func->bits_per_word,
func->max_speed_hz);
if (cw1200_spi_on(plat_data)) {
pr_err("spi_on() failed!\n");
return -1;
}
if (spi_setup(func)) {
pr_err("spi_setup() failed!\n");
return -1;
}
self = kzalloc(sizeof(*self), GFP_KERNEL);
if (!self) {
pr_err("Can't allocate SPI sbus_priv.");
return -ENOMEM;
}
self->pdata = plat_data;
self->func = func;
spin_lock_init(&self->lock);
spi_set_drvdata(func, self);
status = cw1200_spi_irq_subscribe(self);
status = cw1200_core_probe(&cw1200_spi_sbus_ops,
self, &func->dev, &self->core,
self->pdata->ref_clk,
self->pdata->macaddr,
self->pdata->sdd_file,
self->pdata->have_5ghz);
if (status) {
cw1200_spi_irq_unsubscribe(self);
cw1200_spi_off(plat_data);
kfree(self);
}
return status;
}
/* Disconnect Function to be called by SPI stack when device is disconnected */
static int cw1200_spi_disconnect(struct spi_device *func)
{
struct sbus_priv *self = spi_get_drvdata(func);
if (self) {
cw1200_spi_irq_unsubscribe(self);
if (self->core) {
cw1200_core_release(self->core);
self->core = NULL;
}
kfree(self);
}
cw1200_spi_off(func->dev.platform_data);
return 0;
}
static int cw1200_spi_suspend(struct device *dev, pm_message_t state)
{
struct sbus_priv *self = spi_get_drvdata(to_spi_device(dev));
if (!cw1200_can_suspend(self->core))
return -EAGAIN;
/* XXX notify host that we have to keep CW1200 powered on? */
return 0;
}
static int cw1200_spi_resume(struct device *dev)
{
return 0;
}
static struct spi_driver spi_driver = {
.probe = cw1200_spi_probe,
.remove = cw1200_spi_disconnect,
.driver = {
.name = "cw1200_wlan_spi",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
.suspend = cw1200_spi_suspend,
.resume = cw1200_spi_resume,
},
};
/* Init Module function -> Called by insmod */
static int __init cw1200_spi_init(void)
{
return spi_register_driver(&spi_driver);
}
/* Called at Driver Unloading */
static void __exit cw1200_spi_exit(void)
{
spi_unregister_driver(&spi_driver);
}
module_init(cw1200_spi_init);
module_exit(cw1200_spi_exit);

View File

@ -0,0 +1,664 @@
/*
* mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers
* DebugFS code
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include "cw1200.h"
#include "debug.h"
#include "fwio.h"
/* join_status */
static const char * const cw1200_debug_join_status[] = {
"passive",
"monitor",
"station (joining)",
"station (not authenticated yet)",
"station",
"adhoc",
"access point",
};
/* WSM_JOIN_PREAMBLE_... */
static const char * const cw1200_debug_preamble[] = {
"long",
"short",
"long on 1 and 2 Mbps",
};
static const char * const cw1200_debug_link_id[] = {
"OFF",
"REQ",
"SOFT",
"HARD",
};
static const char *cw1200_debug_mode(int mode)
{
switch (mode) {
case NL80211_IFTYPE_UNSPECIFIED:
return "unspecified";
case NL80211_IFTYPE_MONITOR:
return "monitor";
case NL80211_IFTYPE_STATION:
return "station";
case NL80211_IFTYPE_ADHOC:
return "adhoc";
case NL80211_IFTYPE_MESH_POINT:
return "mesh point";
case NL80211_IFTYPE_AP:
return "access point";
case NL80211_IFTYPE_P2P_CLIENT:
return "p2p client";
case NL80211_IFTYPE_P2P_GO:
return "p2p go";
default:
return "unsupported";
}
}
static void cw1200_queue_status_show(struct seq_file *seq,
struct cw1200_queue *q)
{
int i;
seq_printf(seq, "Queue %d:\n", q->queue_id);
seq_printf(seq, " capacity: %zu\n", q->capacity);
seq_printf(seq, " queued: %zu\n", q->num_queued);
seq_printf(seq, " pending: %zu\n", q->num_pending);
seq_printf(seq, " sent: %zu\n", q->num_sent);
seq_printf(seq, " locked: %s\n", q->tx_locked_cnt ? "yes" : "no");
seq_printf(seq, " overfull: %s\n", q->overfull ? "yes" : "no");
seq_puts(seq, " link map: 0-> ");
for (i = 0; i < q->stats->map_capacity; ++i)
seq_printf(seq, "%.2d ", q->link_map_cache[i]);
seq_printf(seq, "<-%zu\n", q->stats->map_capacity);
}
static void cw1200_debug_print_map(struct seq_file *seq,
struct cw1200_common *priv,
const char *label,
u32 map)
{
int i;
seq_printf(seq, "%s0-> ", label);
for (i = 0; i < priv->tx_queue_stats.map_capacity; ++i)
seq_printf(seq, "%s ", (map & BIT(i)) ? "**" : "..");
seq_printf(seq, "<-%zu\n", priv->tx_queue_stats.map_capacity - 1);
}
static int cw1200_status_show(struct seq_file *seq, void *v)
{
int i;
struct list_head *item;
struct cw1200_common *priv = seq->private;
struct cw1200_debug_priv *d = priv->debug;
seq_puts(seq, "CW1200 Wireless LAN driver status\n");
seq_printf(seq, "Hardware: %d.%d\n",
priv->wsm_caps.hw_id,
priv->wsm_caps.hw_subid);
seq_printf(seq, "Firmware: %s %d.%d\n",
cw1200_fw_types[priv->wsm_caps.fw_type],
priv->wsm_caps.fw_ver,
priv->wsm_caps.fw_build);
seq_printf(seq, "FW API: %d\n",
priv->wsm_caps.fw_api);
seq_printf(seq, "FW caps: 0x%.4X\n",
priv->wsm_caps.fw_cap);
seq_printf(seq, "FW label: '%s'\n",
priv->wsm_caps.fw_label);
seq_printf(seq, "Mode: %s%s\n",
cw1200_debug_mode(priv->mode),
priv->listening ? " (listening)" : "");
seq_printf(seq, "Join state: %s\n",
cw1200_debug_join_status[priv->join_status]);
if (priv->channel)
seq_printf(seq, "Channel: %d%s\n",
priv->channel->hw_value,
priv->channel_switch_in_progress ?
" (switching)" : "");
if (priv->rx_filter.promiscuous)
seq_puts(seq, "Filter: promisc\n");
else if (priv->rx_filter.fcs)
seq_puts(seq, "Filter: fcs\n");
if (priv->rx_filter.bssid)
seq_puts(seq, "Filter: bssid\n");
if (!priv->disable_beacon_filter)
seq_puts(seq, "Filter: beacons\n");
if (priv->enable_beacon ||
priv->mode == NL80211_IFTYPE_AP ||
priv->mode == NL80211_IFTYPE_ADHOC ||
priv->mode == NL80211_IFTYPE_MESH_POINT ||
priv->mode == NL80211_IFTYPE_P2P_GO)
seq_printf(seq, "Beaconing: %s\n",
priv->enable_beacon ?
"enabled" : "disabled");
for (i = 0; i < 4; ++i)
seq_printf(seq, "EDCA(%d): %d, %d, %d, %d, %d\n", i,
priv->edca.params[i].cwmin,
priv->edca.params[i].cwmax,
priv->edca.params[i].aifns,
priv->edca.params[i].txop_limit,
priv->edca.params[i].max_rx_lifetime);
if (priv->join_status == CW1200_JOIN_STATUS_STA) {
static const char *pm_mode = "unknown";
switch (priv->powersave_mode.mode) {
case WSM_PSM_ACTIVE:
pm_mode = "off";
break;
case WSM_PSM_PS:
pm_mode = "on";
break;
case WSM_PSM_FAST_PS:
pm_mode = "dynamic";
break;
}
seq_printf(seq, "Preamble: %s\n",
cw1200_debug_preamble[priv->association_mode.preamble]);
seq_printf(seq, "AMPDU spcn: %d\n",
priv->association_mode.mpdu_start_spacing);
seq_printf(seq, "Basic rate: 0x%.8X\n",
le32_to_cpu(priv->association_mode.basic_rate_set));
seq_printf(seq, "Bss lost: %d beacons\n",
priv->bss_params.beacon_lost_count);
seq_printf(seq, "AID: %d\n",
priv->bss_params.aid);
seq_printf(seq, "Rates: 0x%.8X\n",
priv->bss_params.operational_rate_set);
seq_printf(seq, "Powersave: %s\n", pm_mode);
}
seq_printf(seq, "HT: %s\n",
cw1200_is_ht(&priv->ht_info) ? "on" : "off");
if (cw1200_is_ht(&priv->ht_info)) {
seq_printf(seq, "Greenfield: %s\n",
cw1200_ht_greenfield(&priv->ht_info) ? "yes" : "no");
seq_printf(seq, "AMPDU dens: %d\n",
cw1200_ht_ampdu_density(&priv->ht_info));
}
seq_printf(seq, "RSSI thold: %d\n",
priv->cqm_rssi_thold);
seq_printf(seq, "RSSI hyst: %d\n",
priv->cqm_rssi_hyst);
seq_printf(seq, "Long retr: %d\n",
priv->long_frame_max_tx_count);
seq_printf(seq, "Short retr: %d\n",
priv->short_frame_max_tx_count);
spin_lock_bh(&priv->tx_policy_cache.lock);
i = 0;
list_for_each(item, &priv->tx_policy_cache.used)
++i;
spin_unlock_bh(&priv->tx_policy_cache.lock);
seq_printf(seq, "RC in use: %d\n", i);
seq_puts(seq, "\n");
for (i = 0; i < 4; ++i) {
cw1200_queue_status_show(seq, &priv->tx_queue[i]);
seq_puts(seq, "\n");
}
cw1200_debug_print_map(seq, priv, "Link map: ",
priv->link_id_map);
cw1200_debug_print_map(seq, priv, "Asleep map: ",
priv->sta_asleep_mask);
cw1200_debug_print_map(seq, priv, "PSPOLL map: ",
priv->pspoll_mask);
seq_puts(seq, "\n");
for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) {
if (priv->link_id_db[i].status) {
seq_printf(seq, "Link %d: %s, %pM\n",
i + 1,
cw1200_debug_link_id[priv->link_id_db[i].status],
priv->link_id_db[i].mac);
}
}
seq_puts(seq, "\n");
seq_printf(seq, "BH status: %s\n",
atomic_read(&priv->bh_term) ? "terminated" : "alive");
seq_printf(seq, "Pending RX: %d\n",
atomic_read(&priv->bh_rx));
seq_printf(seq, "Pending TX: %d\n",
atomic_read(&priv->bh_tx));
if (priv->bh_error)
seq_printf(seq, "BH errcode: %d\n",
priv->bh_error);
seq_printf(seq, "TX bufs: %d x %d bytes\n",
priv->wsm_caps.input_buffers,
priv->wsm_caps.input_buffer_size);
seq_printf(seq, "Used bufs: %d\n",
priv->hw_bufs_used);
seq_printf(seq, "Powermgmt: %s\n",
priv->powersave_enabled ? "on" : "off");
seq_printf(seq, "Device: %s\n",
priv->device_can_sleep ? "asleep" : "awake");
spin_lock(&priv->wsm_cmd.lock);
seq_printf(seq, "WSM status: %s\n",
priv->wsm_cmd.done ? "idle" : "active");
seq_printf(seq, "WSM cmd: 0x%.4X (%td bytes)\n",
priv->wsm_cmd.cmd, priv->wsm_cmd.len);
seq_printf(seq, "WSM retval: %d\n",
priv->wsm_cmd.ret);
spin_unlock(&priv->wsm_cmd.lock);
seq_printf(seq, "Datapath: %s\n",
atomic_read(&priv->tx_lock) ? "locked" : "unlocked");
if (atomic_read(&priv->tx_lock))
seq_printf(seq, "TXlock cnt: %d\n",
atomic_read(&priv->tx_lock));
seq_printf(seq, "TXed: %d\n",
d->tx);
seq_printf(seq, "AGG TXed: %d\n",
d->tx_agg);
seq_printf(seq, "MULTI TXed: %d (%d)\n",
d->tx_multi, d->tx_multi_frames);
seq_printf(seq, "RXed: %d\n",
d->rx);
seq_printf(seq, "AGG RXed: %d\n",
d->rx_agg);
seq_printf(seq, "TX miss: %d\n",
d->tx_cache_miss);
seq_printf(seq, "TX align: %d\n",
d->tx_align);
seq_printf(seq, "TX burst: %d\n",
d->tx_burst);
seq_printf(seq, "TX TTL: %d\n",
d->tx_ttl);
seq_printf(seq, "Scan: %s\n",
atomic_read(&priv->scan.in_progress) ? "active" : "idle");
return 0;
}
static int cw1200_status_open(struct inode *inode, struct file *file)
{
return single_open(file, &cw1200_status_show,
inode->i_private);
}
static const struct file_operations fops_status = {
.open = cw1200_status_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int cw1200_counters_show(struct seq_file *seq, void *v)
{
int ret;
struct cw1200_common *priv = seq->private;
struct wsm_mib_counters_table counters;
ret = wsm_get_counters_table(priv, &counters);
if (ret)
return ret;
#define PUT_COUNTER(tab, name) \
seq_printf(seq, "%s:" tab "%d\n", #name, \
__le32_to_cpu(counters.name))
PUT_COUNTER("\t\t", plcp_errors);
PUT_COUNTER("\t\t", fcs_errors);
PUT_COUNTER("\t\t", tx_packets);
PUT_COUNTER("\t\t", rx_packets);
PUT_COUNTER("\t\t", rx_packet_errors);
PUT_COUNTER("\t", rx_decryption_failures);
PUT_COUNTER("\t\t", rx_mic_failures);
PUT_COUNTER("\t", rx_no_key_failures);
PUT_COUNTER("\t", tx_multicast_frames);
PUT_COUNTER("\t", tx_frames_success);
PUT_COUNTER("\t", tx_frame_failures);
PUT_COUNTER("\t", tx_frames_retried);
PUT_COUNTER("\t", tx_frames_multi_retried);
PUT_COUNTER("\t", rx_frame_duplicates);
PUT_COUNTER("\t\t", rts_success);
PUT_COUNTER("\t\t", rts_failures);
PUT_COUNTER("\t\t", ack_failures);
PUT_COUNTER("\t", rx_multicast_frames);
PUT_COUNTER("\t", rx_frames_success);
PUT_COUNTER("\t", rx_cmac_icv_errors);
PUT_COUNTER("\t\t", rx_cmac_replays);
PUT_COUNTER("\t", rx_mgmt_ccmp_replays);
#undef PUT_COUNTER
return 0;
}
static int cw1200_counters_open(struct inode *inode, struct file *file)
{
return single_open(file, &cw1200_counters_show,
inode->i_private);
}
static const struct file_operations fops_counters = {
.open = cw1200_counters_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int cw1200_generic_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return 0;
}
#ifdef CONFIG_CW1200_ETF
static int cw1200_etf_out_show(struct seq_file *seq, void *v)
{
struct cw1200_common *priv = seq->private;
struct sk_buff *skb;
u32 len = 0;
skb = skb_dequeue(&priv->etf_q);
if (skb)
len = skb->len;
seq_write(seq, &len, sizeof(len));
if (skb) {
seq_write(seq, skb->data, len);
kfree_skb(skb);
}
return 0;
}
static int cw1200_etf_out_open(struct inode *inode, struct file *file)
{
return single_open(file, &cw1200_etf_out_show,
inode->i_private);
}
static const struct file_operations fops_etf_out = {
.open = cw1200_etf_out_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
struct etf_req_msg;
static int etf_request(struct cw1200_common *priv,
struct etf_req_msg *msg, u32 len);
#define MAX_RX_SZE 2600
struct etf_in_state {
struct cw1200_common *priv;
u32 total_len;
u8 buf[MAX_RX_SZE];
u32 written;
};
static int cw1200_etf_in_open(struct inode *inode, struct file *file)
{
struct etf_in_state *etf = kmalloc(sizeof(struct etf_in_state),
GFP_KERNEL);
if (!etf)
return -ENOMEM;
etf->written = 0;
etf->total_len = 0;
etf->priv = inode->i_private;
file->private_data = etf;
return 0;
}
static int cw1200_etf_in_release(struct inode *inode, struct file *file)
{
kfree(file->private_data);
return 0;
}
static ssize_t cw1200_etf_in_write(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
struct etf_in_state *etf = file->private_data;
ssize_t written = 0;
if (!etf->total_len) {
if (count < sizeof(etf->total_len)) {
pr_err("count < sizeof(total_len)\n");
return -EINVAL;
}
if (copy_from_user(&etf->total_len, user_buf,
sizeof(etf->total_len))) {
pr_err("copy_from_user (len) failed\n");
return -EFAULT;
}
written += sizeof(etf->total_len);
count -= sizeof(etf->total_len);
}
if (!count)
goto done;
if (copy_from_user(etf->buf + etf->written, user_buf + written,
count)) {
pr_err("copy_from_user (payload %zu) failed\n", count);
return -EFAULT;
}
written += count;
etf->written += count;
if (etf->written >= etf->total_len) {
if (etf_request(etf->priv, (struct etf_req_msg *)etf->buf,
etf->total_len)) {
pr_err("etf_request failed\n");
return -EIO;
}
}
done:
return written;
}
static const struct file_operations fops_etf_in = {
.open = cw1200_etf_in_open,
.release = cw1200_etf_in_release,
.write = cw1200_etf_in_write,
.llseek = default_llseek,
.owner = THIS_MODULE,
};
#endif /* CONFIG_CW1200_ETF */
static ssize_t cw1200_wsm_dumps(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
struct cw1200_common *priv = file->private_data;
char buf[1];
if (!count)
return -EINVAL;
if (copy_from_user(buf, user_buf, 1))
return -EFAULT;
if (buf[0] == '1')
priv->wsm_enable_wsm_dumps = 1;
else
priv->wsm_enable_wsm_dumps = 0;
return count;
}
static const struct file_operations fops_wsm_dumps = {
.open = cw1200_generic_open,
.write = cw1200_wsm_dumps,
.llseek = default_llseek,
};
int cw1200_debug_init(struct cw1200_common *priv)
{
int ret = -ENOMEM;
struct cw1200_debug_priv *d = kzalloc(sizeof(struct cw1200_debug_priv),
GFP_KERNEL);
priv->debug = d;
if (!d)
return ret;
d->debugfs_phy = debugfs_create_dir("cw1200",
priv->hw->wiphy->debugfsdir);
if (!d->debugfs_phy)
goto err;
if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy,
priv, &fops_status))
goto err;
if (!debugfs_create_file("counters", S_IRUSR, d->debugfs_phy,
priv, &fops_counters))
goto err;
#ifdef CONFIG_CW1200_ETF
if (etf_mode) {
skb_queue_head_init(&priv->etf_q);
if (!debugfs_create_file("etf_out", S_IRUSR, d->debugfs_phy,
priv, &fops_etf_out))
goto err;
if (!debugfs_create_file("etf_in", S_IWUSR, d->debugfs_phy,
priv, &fops_etf_in))
goto err;
}
#endif /* CONFIG_CW1200_ETF */
if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy,
priv, &fops_wsm_dumps))
goto err;
ret = cw1200_itp_init(priv);
if (ret)
goto err;
return 0;
err:
priv->debug = NULL;
debugfs_remove_recursive(d->debugfs_phy);
kfree(d);
return ret;
}
void cw1200_debug_release(struct cw1200_common *priv)
{
struct cw1200_debug_priv *d = priv->debug;
if (d) {
cw1200_itp_release(priv);
priv->debug = NULL;
kfree(d);
}
}
#ifdef CONFIG_CW1200_ETF
struct cw1200_sdd {
u8 id;
u8 len;
u8 data[];
};
struct etf_req_msg {
u32 id;
u32 len;
u8 data[];
};
static int parse_sdd_file(struct cw1200_common *priv, u8 *data, u32 length)
{
struct cw1200_sdd *ie;
while (length > 0) {
ie = (struct cw1200_sdd *)data;
if (ie->id == SDD_REFERENCE_FREQUENCY_ELT_ID) {
priv->hw_refclk = cpu_to_le16(*((u16 *)ie->data));
pr_info("Using Reference clock frequency %d KHz\n",
priv->hw_refclk);
break;
}
length -= ie->len + sizeof(*ie);
data += ie->len + sizeof(*ie);
}
return 0;
}
char *etf_firmware;
#define ST90TDS_START_ADAPTER 0x09 /* Loads firmware too */
#define ST90TDS_STOP_ADAPTER 0x0A
#define ST90TDS_CONFIG_ADAPTER 0x0E /* Send configuration params */
#define ST90TDS_SBUS_READ 0x13
#define ST90TDS_SBUS_WRITE 0x14
#define ST90TDS_GET_DEVICE_OPTION 0x19
#define ST90TDS_SET_DEVICE_OPTION 0x1A
#define ST90TDS_SEND_SDD 0x1D /* SDD File used to find DPLL */
#include "fwio.h"
static int etf_request(struct cw1200_common *priv,
struct etf_req_msg *msg,
u32 len)
{
int rval = -1;
switch (msg->id) {
case ST90TDS_START_ADAPTER:
etf_firmware = "cw1200_etf.bin";
pr_info("ETF_START (len %d, '%s')\n", len, etf_firmware);
rval = cw1200_load_firmware(priv);
break;
case ST90TDS_STOP_ADAPTER:
pr_info("ETF_STOP (unhandled)\n");
break;
case ST90TDS_SEND_SDD:
pr_info("ETF_SDD\n");
rval = parse_sdd_file(priv, msg->data, msg->len);
break;
case ST90TDS_CONFIG_ADAPTER:
pr_info("ETF_CONFIG_ADAP (unhandled)\n");
break;
case ST90TDS_SBUS_READ:
pr_info("ETF_SBUS_READ (unhandled)\n");
break;
case ST90TDS_SBUS_WRITE:
pr_info("ETF_SBUS_WRITE (unhandled)\n");
break;
case ST90TDS_SET_DEVICE_OPTION:
pr_info("ETF_SET_DEV_OPT (unhandled)\n");
break;
default:
pr_info("ETF_PASSTHRU (0x%08x)\n", msg->id);
rval = wsm_raw_cmd(priv, (u8 *)msg, len);
break;
}
return rval;
}
#endif /* CONFIG_CW1200_ETF */

View File

@ -0,0 +1,98 @@
/*
* DebugFS code for ST-Ericsson CW1200 mac80211 driver
*
* Copyright (c) 2011, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef CW1200_DEBUG_H_INCLUDED
#define CW1200_DEBUG_H_INCLUDED
#include "itp.h"
struct cw1200_debug_priv {
struct dentry *debugfs_phy;
int tx;
int tx_agg;
int rx;
int rx_agg;
int tx_multi;
int tx_multi_frames;
int tx_cache_miss;
int tx_align;
int tx_ttl;
int tx_burst;
int ba_cnt;
int ba_acc;
int ba_cnt_rx;
int ba_acc_rx;
#ifdef CONFIG_CW1200_ITP
struct cw1200_itp itp;
#endif /* CONFIG_CW1200_ITP */
};
int cw1200_debug_init(struct cw1200_common *priv);
void cw1200_debug_release(struct cw1200_common *priv);
static inline void cw1200_debug_txed(struct cw1200_common *priv)
{
++priv->debug->tx;
}
static inline void cw1200_debug_txed_agg(struct cw1200_common *priv)
{
++priv->debug->tx_agg;
}
static inline void cw1200_debug_txed_multi(struct cw1200_common *priv,
int count)
{
++priv->debug->tx_multi;
priv->debug->tx_multi_frames += count;
}
static inline void cw1200_debug_rxed(struct cw1200_common *priv)
{
++priv->debug->rx;
}
static inline void cw1200_debug_rxed_agg(struct cw1200_common *priv)
{
++priv->debug->rx_agg;
}
static inline void cw1200_debug_tx_cache_miss(struct cw1200_common *priv)
{
++priv->debug->tx_cache_miss;
}
static inline void cw1200_debug_tx_align(struct cw1200_common *priv)
{
++priv->debug->tx_align;
}
static inline void cw1200_debug_tx_ttl(struct cw1200_common *priv)
{
++priv->debug->tx_ttl;
}
static inline void cw1200_debug_tx_burst(struct cw1200_common *priv)
{
++priv->debug->tx_burst;
}
static inline void cw1200_debug_ba(struct cw1200_common *priv,
int ba_cnt, int ba_acc,
int ba_cnt_rx, int ba_acc_rx)
{
priv->debug->ba_cnt = ba_cnt;
priv->debug->ba_acc = ba_acc;
priv->debug->ba_cnt_rx = ba_cnt_rx;
priv->debug->ba_acc_rx = ba_acc_rx;
}
#endif /* CW1200_DEBUG_H_INCLUDED */

View File

@ -0,0 +1,525 @@
/*
* Firmware I/O code for mac80211 ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* Based on:
* ST-Ericsson UMAC CW1200 driver which is
* Copyright (c) 2010, ST-Ericsson
* Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/vmalloc.h>
#include <linux/sched.h>
#include <linux/firmware.h>
#include "cw1200.h"
#include "fwio.h"
#include "hwio.h"
#include "sbus.h"
#include "bh.h"
static int cw1200_get_hw_type(u32 config_reg_val, int *major_revision)
{
int hw_type = -1;
u32 silicon_type = (config_reg_val >> 24) & 0x7;
u32 silicon_vers = (config_reg_val >> 31) & 0x1;
switch (silicon_type) {
case 0x00:
*major_revision = 1;
hw_type = HIF_9000_SILICON_VERSATILE;
break;
case 0x01:
case 0x02: /* CW1x00 */
case 0x04: /* CW1x60 */
*major_revision = silicon_type;
if (silicon_vers)
hw_type = HIF_8601_VERSATILE;
else
hw_type = HIF_8601_SILICON;
break;
default:
break;
}
return hw_type;
}
static int cw1200_load_firmware_cw1200(struct cw1200_common *priv)
{
int ret, block, num_blocks;
unsigned i;
u32 val32;
u32 put = 0, get = 0;
u8 *buf = NULL;
const char *fw_path;
const struct firmware *firmware = NULL;
/* Macroses are local. */
#define APB_WRITE(reg, val) \
do { \
ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \
if (ret < 0) \
goto error; \
} while (0)
#define APB_READ(reg, val) \
do { \
ret = cw1200_apb_read_32(priv, CW1200_APB(reg), &(val)); \
if (ret < 0) \
goto error; \
} while (0)
#define REG_WRITE(reg, val) \
do { \
ret = cw1200_reg_write_32(priv, (reg), (val)); \
if (ret < 0) \
goto error; \
} while (0)
#define REG_READ(reg, val) \
do { \
ret = cw1200_reg_read_32(priv, (reg), &(val)); \
if (ret < 0) \
goto error; \
} while (0)
switch (priv->hw_revision) {
case CW1200_HW_REV_CUT10:
fw_path = FIRMWARE_CUT10;
if (!priv->sdd_path)
priv->sdd_path = SDD_FILE_10;
break;
case CW1200_HW_REV_CUT11:
fw_path = FIRMWARE_CUT11;
if (!priv->sdd_path)
priv->sdd_path = SDD_FILE_11;
break;
case CW1200_HW_REV_CUT20:
fw_path = FIRMWARE_CUT20;
if (!priv->sdd_path)
priv->sdd_path = SDD_FILE_20;
break;
case CW1200_HW_REV_CUT22:
fw_path = FIRMWARE_CUT22;
if (!priv->sdd_path)
priv->sdd_path = SDD_FILE_22;
break;
case CW1X60_HW_REV:
fw_path = FIRMWARE_CW1X60;
if (!priv->sdd_path)
priv->sdd_path = SDD_FILE_CW1X60;
break;
default:
pr_err("Invalid silicon revision %d.\n", priv->hw_revision);
return -EINVAL;
}
/* Initialize common registers */
APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, DOWNLOAD_ARE_YOU_HERE);
APB_WRITE(DOWNLOAD_PUT_REG, 0);
APB_WRITE(DOWNLOAD_GET_REG, 0);
APB_WRITE(DOWNLOAD_STATUS_REG, DOWNLOAD_PENDING);
APB_WRITE(DOWNLOAD_FLAGS_REG, 0);
/* Write the NOP Instruction */
REG_WRITE(ST90TDS_SRAM_BASE_ADDR_REG_ID, 0xFFF20000);
REG_WRITE(ST90TDS_AHB_DPORT_REG_ID, 0xEAFFFFFE);
/* Release CPU from RESET */
REG_READ(ST90TDS_CONFIG_REG_ID, val32);
val32 &= ~ST90TDS_CONFIG_CPU_RESET_BIT;
REG_WRITE(ST90TDS_CONFIG_REG_ID, val32);
/* Enable Clock */
val32 &= ~ST90TDS_CONFIG_CPU_CLK_DIS_BIT;
REG_WRITE(ST90TDS_CONFIG_REG_ID, val32);
#ifdef CONFIG_CW1200_ETF
if (etf_mode)
fw_path = etf_firmware;
#endif
/* Load a firmware file */
ret = request_firmware(&firmware, fw_path, priv->pdev);
if (ret) {
pr_err("Can't load firmware file %s.\n", fw_path);
goto error;
}
buf = kmalloc(DOWNLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA);
if (!buf) {
pr_err("Can't allocate firmware load buffer.\n");
ret = -ENOMEM;
goto error;
}
/* Check if the bootloader is ready */
for (i = 0; i < 100; i += 1 + i / 2) {
APB_READ(DOWNLOAD_IMAGE_SIZE_REG, val32);
if (val32 == DOWNLOAD_I_AM_HERE)
break;
mdelay(i);
} /* End of for loop */
if (val32 != DOWNLOAD_I_AM_HERE) {
pr_err("Bootloader is not ready.\n");
ret = -ETIMEDOUT;
goto error;
}
/* Calculcate number of download blocks */
num_blocks = (firmware->size - 1) / DOWNLOAD_BLOCK_SIZE + 1;
/* Updating the length in Download Ctrl Area */
val32 = firmware->size; /* Explicit cast from size_t to u32 */
APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, val32);
/* Firmware downloading loop */
for (block = 0; block < num_blocks; block++) {
size_t tx_size;
size_t block_size;
/* check the download status */
APB_READ(DOWNLOAD_STATUS_REG, val32);
if (val32 != DOWNLOAD_PENDING) {
pr_err("Bootloader reported error %d.\n", val32);
ret = -EIO;
goto error;
}
/* loop until put - get <= 24K */
for (i = 0; i < 100; i++) {
APB_READ(DOWNLOAD_GET_REG, get);
if ((put - get) <=
(DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE))
break;
mdelay(i);
}
if ((put - get) > (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) {
pr_err("Timeout waiting for FIFO.\n");
ret = -ETIMEDOUT;
goto error;
}
/* calculate the block size */
tx_size = block_size = min((size_t)(firmware->size - put),
(size_t)DOWNLOAD_BLOCK_SIZE);
memcpy(buf, &firmware->data[put], block_size);
if (block_size < DOWNLOAD_BLOCK_SIZE) {
memset(&buf[block_size], 0,
DOWNLOAD_BLOCK_SIZE - block_size);
tx_size = DOWNLOAD_BLOCK_SIZE;
}
/* send the block to sram */
ret = cw1200_apb_write(priv,
CW1200_APB(DOWNLOAD_FIFO_OFFSET +
(put & (DOWNLOAD_FIFO_SIZE - 1))),
buf, tx_size);
if (ret < 0) {
pr_err("Can't write firmware block @ %d!\n",
put & (DOWNLOAD_FIFO_SIZE - 1));
goto error;
}
/* update the put register */
put += block_size;
APB_WRITE(DOWNLOAD_PUT_REG, put);
} /* End of firmware download loop */
/* Wait for the download completion */
for (i = 0; i < 300; i += 1 + i / 2) {
APB_READ(DOWNLOAD_STATUS_REG, val32);
if (val32 != DOWNLOAD_PENDING)
break;
mdelay(i);
}
if (val32 != DOWNLOAD_SUCCESS) {
pr_err("Wait for download completion failed: 0x%.8X\n", val32);
ret = -ETIMEDOUT;
goto error;
} else {
pr_info("Firmware download completed.\n");
ret = 0;
}
error:
kfree(buf);
if (firmware)
release_firmware(firmware);
return ret;
#undef APB_WRITE
#undef APB_READ
#undef REG_WRITE
#undef REG_READ
}
static int config_reg_read(struct cw1200_common *priv, u32 *val)
{
switch (priv->hw_type) {
case HIF_9000_SILICON_VERSATILE: {
u16 val16;
int ret = cw1200_reg_read_16(priv,
ST90TDS_CONFIG_REG_ID,
&val16);
if (ret < 0)
return ret;
*val = val16;
return 0;
}
case HIF_8601_VERSATILE:
case HIF_8601_SILICON:
default:
cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, val);
break;
}
return 0;
}
static int config_reg_write(struct cw1200_common *priv, u32 val)
{
switch (priv->hw_type) {
case HIF_9000_SILICON_VERSATILE:
return cw1200_reg_write_16(priv,
ST90TDS_CONFIG_REG_ID,
(u16)val);
case HIF_8601_VERSATILE:
case HIF_8601_SILICON:
default:
return cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val);
break;
}
return 0;
}
int cw1200_load_firmware(struct cw1200_common *priv)
{
int ret;
int i;
u32 val32;
u16 val16;
int major_revision = -1;
/* Read CONFIG Register */
ret = cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
if (ret < 0) {
pr_err("Can't read config register.\n");
goto out;
}
if (val32 == 0 || val32 == 0xffffffff) {
pr_err("Bad config register value (0x%08x)\n", val32);
ret = -EIO;
goto out;
}
priv->hw_type = cw1200_get_hw_type(val32, &major_revision);
if (priv->hw_type < 0) {
pr_err("Can't deduce hardware type.\n");
ret = -ENOTSUPP;
goto out;
}
/* Set DPLL Reg value, and read back to confirm writes work */
ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID,
cw1200_dpll_from_clk(priv->hw_refclk));
if (ret < 0) {
pr_err("Can't write DPLL register.\n");
goto out;
}
msleep(20);
ret = cw1200_reg_read_32(priv,
ST90TDS_TSET_GEN_R_W_REG_ID, &val32);
if (ret < 0) {
pr_err("Can't read DPLL register.\n");
goto out;
}
if (val32 != cw1200_dpll_from_clk(priv->hw_refclk)) {
pr_err("Unable to initialise DPLL register. Wrote 0x%.8X, Read 0x%.8X.\n",
cw1200_dpll_from_clk(priv->hw_refclk), val32);
ret = -EIO;
goto out;
}
/* Set wakeup bit in device */
ret = cw1200_reg_read_16(priv, ST90TDS_CONTROL_REG_ID, &val16);
if (ret < 0) {
pr_err("set_wakeup: can't read control register.\n");
goto out;
}
ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID,
val16 | ST90TDS_CONT_WUP_BIT);
if (ret < 0) {
pr_err("set_wakeup: can't write control register.\n");
goto out;
}
/* Wait for wakeup */
for (i = 0; i < 300; i += (1 + i / 2)) {
ret = cw1200_reg_read_16(priv,
ST90TDS_CONTROL_REG_ID, &val16);
if (ret < 0) {
pr_err("wait_for_wakeup: can't read control register.\n");
goto out;
}
if (val16 & ST90TDS_CONT_RDY_BIT)
break;
msleep(i);
}
if ((val16 & ST90TDS_CONT_RDY_BIT) == 0) {
pr_err("wait_for_wakeup: device is not responding.\n");
ret = -ETIMEDOUT;
goto out;
}
switch (major_revision) {
case 1:
/* CW1200 Hardware detection logic : Check for CUT1.1 */
ret = cw1200_ahb_read_32(priv, CW1200_CUT_ID_ADDR, &val32);
if (ret) {
pr_err("HW detection: can't read CUT ID.\n");
goto out;
}
switch (val32) {
case CW1200_CUT_11_ID_STR:
pr_info("CW1x00 Cut 1.1 silicon detected.\n");
priv->hw_revision = CW1200_HW_REV_CUT11;
break;
default:
pr_info("CW1x00 Cut 1.0 silicon detected.\n");
priv->hw_revision = CW1200_HW_REV_CUT10;
break;
}
/* According to ST-E, CUT<2.0 has busted BA TID0-3.
Just disable it entirely...
*/
priv->ba_rx_tid_mask = 0;
priv->ba_tx_tid_mask = 0;
break;
case 2: {
u32 ar1, ar2, ar3;
ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR, &ar1);
if (ret) {
pr_err("(1) HW detection: can't read CUT ID\n");
goto out;
}
ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 4, &ar2);
if (ret) {
pr_err("(2) HW detection: can't read CUT ID.\n");
goto out;
}
ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 8, &ar3);
if (ret) {
pr_err("(3) HW detection: can't read CUT ID.\n");
goto out;
}
if (ar1 == CW1200_CUT_22_ID_STR1 &&
ar2 == CW1200_CUT_22_ID_STR2 &&
ar3 == CW1200_CUT_22_ID_STR3) {
pr_info("CW1x00 Cut 2.2 silicon detected.\n");
priv->hw_revision = CW1200_HW_REV_CUT22;
} else {
pr_info("CW1x00 Cut 2.0 silicon detected.\n");
priv->hw_revision = CW1200_HW_REV_CUT20;
}
break;
}
case 4:
pr_info("CW1x60 silicon detected.\n");
priv->hw_revision = CW1X60_HW_REV;
break;
default:
pr_err("Unsupported silicon major revision %d.\n",
major_revision);
ret = -ENOTSUPP;
goto out;
}
/* Checking for access mode */
ret = config_reg_read(priv, &val32);
if (ret < 0) {
pr_err("Can't read config register.\n");
goto out;
}
if (!(val32 & ST90TDS_CONFIG_ACCESS_MODE_BIT)) {
pr_err("Device is already in QUEUE mode!\n");
ret = -EINVAL;
goto out;
}
switch (priv->hw_type) {
case HIF_8601_SILICON:
if (priv->hw_revision == CW1X60_HW_REV) {
pr_err("Can't handle CW1160/1260 firmware load yet.\n");
ret = -ENOTSUPP;
goto out;
}
ret = cw1200_load_firmware_cw1200(priv);
break;
default:
pr_err("Can't perform firmware load for hw type %d.\n",
priv->hw_type);
ret = -ENOTSUPP;
goto out;
}
if (ret < 0) {
pr_err("Firmware load error.\n");
goto out;
}
/* Enable interrupt signalling */
priv->sbus_ops->lock(priv->sbus_priv);
ret = __cw1200_irq_enable(priv, 1);
priv->sbus_ops->unlock(priv->sbus_priv);
if (ret < 0)
goto unsubscribe;
/* Configure device for MESSSAGE MODE */
ret = config_reg_read(priv, &val32);
if (ret < 0) {
pr_err("Can't read config register.\n");
goto unsubscribe;
}
ret = config_reg_write(priv, val32 & ~ST90TDS_CONFIG_ACCESS_MODE_BIT);
if (ret < 0) {
pr_err("Can't write config register.\n");
goto unsubscribe;
}
/* Unless we read the CONFIG Register we are
* not able to get an interrupt
*/
mdelay(10);
config_reg_read(priv, &val32);
out:
return ret;
unsubscribe:
/* Disable interrupt signalling */
priv->sbus_ops->lock(priv->sbus_priv);
ret = __cw1200_irq_enable(priv, 0);
priv->sbus_ops->unlock(priv->sbus_priv);
return ret;
}

View File

@ -0,0 +1,39 @@
/*
* Firmware API for mac80211 ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* Based on:
* ST-Ericsson UMAC CW1200 driver which is
* Copyright (c) 2010, ST-Ericsson
* Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef FWIO_H_INCLUDED
#define FWIO_H_INCLUDED
#define BOOTLOADER_CW1X60 "boot_cw1x60.bin"
#define FIRMWARE_CW1X60 "wsm_cw1x60.bin"
#define FIRMWARE_CUT22 "wsm_22.bin"
#define FIRMWARE_CUT20 "wsm_20.bin"
#define FIRMWARE_CUT11 "wsm_11.bin"
#define FIRMWARE_CUT10 "wsm_10.bin"
#define SDD_FILE_CW1X60 "sdd_cw1x60.bin"
#define SDD_FILE_22 "sdd_22.bin"
#define SDD_FILE_20 "sdd_20.bin"
#define SDD_FILE_11 "sdd_11.bin"
#define SDD_FILE_10 "sdd_10.bin"
int cw1200_load_firmware(struct cw1200_common *priv);
/* SDD definitions */
#define SDD_PTA_CFG_ELT_ID 0xEB
#define SDD_REFERENCE_FREQUENCY_ELT_ID 0xc5
u32 cw1200_dpll_from_clk(u16 clk);
#endif

View File

@ -0,0 +1,311 @@
/*
* Low-level device IO routines for ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* Based on:
* ST-Ericsson UMAC CW1200 driver, which is
* Copyright (c) 2010, ST-Ericsson
* Author: Ajitpal Singh <ajitpal.singh@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/types.h>
#include "cw1200.h"
#include "hwio.h"
#include "sbus.h"
/* Sdio addr is 4*spi_addr */
#define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2)
#define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \
((((buf_id) & 0x1F) << 7) \
| (((mpf) & 1) << 6) \
| (((rfu) & 1) << 5) \
| (((reg_id_ofs) & 0x1F) << 0))
#define MAX_RETRY 3
static int __cw1200_reg_read(struct cw1200_common *priv, u16 addr,
void *buf, size_t buf_len, int buf_id)
{
u16 addr_sdio;
u32 sdio_reg_addr_17bit;
/* Check if buffer is aligned to 4 byte boundary */
if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) {
pr_err("buffer is not aligned.\n");
return -EINVAL;
}
/* Convert to SDIO Register Address */
addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
return priv->sbus_ops->sbus_memcpy_fromio(priv->sbus_priv,
sdio_reg_addr_17bit,
buf, buf_len);
}
static int __cw1200_reg_write(struct cw1200_common *priv, u16 addr,
const void *buf, size_t buf_len, int buf_id)
{
u16 addr_sdio;
u32 sdio_reg_addr_17bit;
/* Convert to SDIO Register Address */
addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
return priv->sbus_ops->sbus_memcpy_toio(priv->sbus_priv,
sdio_reg_addr_17bit,
buf, buf_len);
}
static inline int __cw1200_reg_read_32(struct cw1200_common *priv,
u16 addr, u32 *val)
{
int i = __cw1200_reg_read(priv, addr, val, sizeof(*val), 0);
*val = le32_to_cpu(*val);
return i;
}
static inline int __cw1200_reg_write_32(struct cw1200_common *priv,
u16 addr, u32 val)
{
val = cpu_to_le32(val);
return __cw1200_reg_write(priv, addr, &val, sizeof(val), 0);
}
static inline int __cw1200_reg_read_16(struct cw1200_common *priv,
u16 addr, u16 *val)
{
int i = __cw1200_reg_read(priv, addr, val, sizeof(*val), 0);
*val = le16_to_cpu(*val);
return i;
}
static inline int __cw1200_reg_write_16(struct cw1200_common *priv,
u16 addr, u16 val)
{
val = cpu_to_le16(val);
return __cw1200_reg_write(priv, addr, &val, sizeof(val), 0);
}
int cw1200_reg_read(struct cw1200_common *priv, u16 addr, void *buf,
size_t buf_len)
{
int ret;
priv->sbus_ops->lock(priv->sbus_priv);
ret = __cw1200_reg_read(priv, addr, buf, buf_len, 0);
priv->sbus_ops->unlock(priv->sbus_priv);
return ret;
}
int cw1200_reg_write(struct cw1200_common *priv, u16 addr, const void *buf,
size_t buf_len)
{
int ret;
priv->sbus_ops->lock(priv->sbus_priv);
ret = __cw1200_reg_write(priv, addr, buf, buf_len, 0);
priv->sbus_ops->unlock(priv->sbus_priv);
return ret;
}
int cw1200_data_read(struct cw1200_common *priv, void *buf, size_t buf_len)
{
int ret, retry = 1;
int buf_id_rx = priv->buf_id_rx;
priv->sbus_ops->lock(priv->sbus_priv);
while (retry <= MAX_RETRY) {
ret = __cw1200_reg_read(priv,
ST90TDS_IN_OUT_QUEUE_REG_ID, buf,
buf_len, buf_id_rx + 1);
if (!ret) {
buf_id_rx = (buf_id_rx + 1) & 3;
priv->buf_id_rx = buf_id_rx;
break;
} else {
retry++;
mdelay(1);
pr_err("error :[%d]\n", ret);
}
}
priv->sbus_ops->unlock(priv->sbus_priv);
return ret;
}
int cw1200_data_write(struct cw1200_common *priv, const void *buf,
size_t buf_len)
{
int ret, retry = 1;
int buf_id_tx = priv->buf_id_tx;
priv->sbus_ops->lock(priv->sbus_priv);
while (retry <= MAX_RETRY) {
ret = __cw1200_reg_write(priv,
ST90TDS_IN_OUT_QUEUE_REG_ID, buf,
buf_len, buf_id_tx);
if (!ret) {
buf_id_tx = (buf_id_tx + 1) & 31;
priv->buf_id_tx = buf_id_tx;
break;
} else {
retry++;
mdelay(1);
pr_err("error :[%d]\n", ret);
}
}
priv->sbus_ops->unlock(priv->sbus_priv);
return ret;
}
int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf,
size_t buf_len, u32 prefetch, u16 port_addr)
{
u32 val32 = 0;
int i, ret;
if ((buf_len / 2) >= 0x1000) {
pr_err("Can't read more than 0xfff words.\n");
return -EINVAL;
goto out;
}
priv->sbus_ops->lock(priv->sbus_priv);
/* Write address */
ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
if (ret < 0) {
pr_err("Can't write address register.\n");
goto out;
}
/* Read CONFIG Register Value - We will read 32 bits */
ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
if (ret < 0) {
pr_err("Can't read config register.\n");
goto out;
}
/* Set PREFETCH bit */
ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID,
val32 | prefetch);
if (ret < 0) {
pr_err("Can't write prefetch bit.\n");
goto out;
}
/* Check for PRE-FETCH bit to be cleared */
for (i = 0; i < 20; i++) {
ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
if (ret < 0) {
pr_err("Can't check prefetch bit.\n");
goto out;
}
if (!(val32 & prefetch))
break;
mdelay(i);
}
if (val32 & prefetch) {
pr_err("Prefetch bit is not cleared.\n");
goto out;
}
/* Read data port */
ret = __cw1200_reg_read(priv, port_addr, buf, buf_len, 0);
if (ret < 0) {
pr_err("Can't read data port.\n");
goto out;
}
out:
priv->sbus_ops->unlock(priv->sbus_priv);
return ret;
}
int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf,
size_t buf_len)
{
int ret;
if ((buf_len / 2) >= 0x1000) {
pr_err("Can't write more than 0xfff words.\n");
return -EINVAL;
}
priv->sbus_ops->lock(priv->sbus_priv);
/* Write address */
ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
if (ret < 0) {
pr_err("Can't write address register.\n");
goto out;
}
/* Write data port */
ret = __cw1200_reg_write(priv, ST90TDS_SRAM_DPORT_REG_ID,
buf, buf_len, 0);
if (ret < 0) {
pr_err("Can't write data port.\n");
goto out;
}
out:
priv->sbus_ops->unlock(priv->sbus_priv);
return ret;
}
int __cw1200_irq_enable(struct cw1200_common *priv, int enable)
{
u32 val32;
u16 val16;
int ret;
if (HIF_8601_SILICON == priv->hw_type) {
ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32);
if (ret < 0) {
pr_err("Can't read config register.\n");
return ret;
}
if (enable)
val32 |= ST90TDS_CONF_IRQ_RDY_ENABLE;
else
val32 &= ~ST90TDS_CONF_IRQ_RDY_ENABLE;
ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val32);
if (ret < 0) {
pr_err("Can't write config register.\n");
return ret;
}
} else {
ret = __cw1200_reg_read_16(priv, ST90TDS_CONFIG_REG_ID, &val16);
if (ret < 0) {
pr_err("Can't read control register.\n");
return ret;
}
if (enable)
val16 |= ST90TDS_CONT_IRQ_RDY_ENABLE;
else
val16 &= ~ST90TDS_CONT_IRQ_RDY_ENABLE;
ret = __cw1200_reg_write_16(priv, ST90TDS_CONFIG_REG_ID, val16);
if (ret < 0) {
pr_err("Can't write control register.\n");
return ret;
}
}
return 0;
}

View File

@ -0,0 +1,247 @@
/*
* Low-level API for mac80211 ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* Based on:
* ST-Ericsson UMAC CW1200 driver which is
* Copyright (c) 2010, ST-Ericsson
* Author: Ajitpal Singh <ajitpal.singh@stericsson.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef CW1200_HWIO_H_INCLUDED
#define CW1200_HWIO_H_INCLUDED
/* extern */ struct cw1200_common;
#define CW1200_CUT_11_ID_STR (0x302E3830)
#define CW1200_CUT_22_ID_STR1 (0x302e3132)
#define CW1200_CUT_22_ID_STR2 (0x32302e30)
#define CW1200_CUT_22_ID_STR3 (0x3335)
#define CW1200_CUT_ID_ADDR (0xFFF17F90)
#define CW1200_CUT2_ID_ADDR (0xFFF1FF90)
/* Download control area */
/* boot loader start address in SRAM */
#define DOWNLOAD_BOOT_LOADER_OFFSET (0x00000000)
/* 32K, 0x4000 to 0xDFFF */
#define DOWNLOAD_FIFO_OFFSET (0x00004000)
/* 32K */
#define DOWNLOAD_FIFO_SIZE (0x00008000)
/* 128 bytes, 0xFF80 to 0xFFFF */
#define DOWNLOAD_CTRL_OFFSET (0x0000FF80)
#define DOWNLOAD_CTRL_DATA_DWORDS (32-6)
struct download_cntl_t {
/* size of whole firmware file (including Cheksum), host init */
u32 image_size;
/* downloading flags */
u32 flags;
/* No. of bytes put into the download, init & updated by host */
u32 put;
/* last traced program counter, last ARM reg_pc */
u32 trace_pc;
/* No. of bytes read from the download, host init, device updates */
u32 get;
/* r0, boot losader status, host init to pending, device updates */
u32 status;
/* Extra debug info, r1 to r14 if status=r0=DOWNLOAD_EXCEPTION */
u32 debug_data[DOWNLOAD_CTRL_DATA_DWORDS];
};
#define DOWNLOAD_IMAGE_SIZE_REG \
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, image_size))
#define DOWNLOAD_FLAGS_REG \
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, flags))
#define DOWNLOAD_PUT_REG \
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, put))
#define DOWNLOAD_TRACE_PC_REG \
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, trace_pc))
#define DOWNLOAD_GET_REG \
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, get))
#define DOWNLOAD_STATUS_REG \
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, status))
#define DOWNLOAD_DEBUG_DATA_REG \
(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, debug_data))
#define DOWNLOAD_DEBUG_DATA_LEN (108)
#define DOWNLOAD_BLOCK_SIZE (1024)
/* For boot loader detection */
#define DOWNLOAD_ARE_YOU_HERE (0x87654321)
#define DOWNLOAD_I_AM_HERE (0x12345678)
/* Download error code */
#define DOWNLOAD_PENDING (0xFFFFFFFF)
#define DOWNLOAD_SUCCESS (0)
#define DOWNLOAD_EXCEPTION (1)
#define DOWNLOAD_ERR_MEM_1 (2)
#define DOWNLOAD_ERR_MEM_2 (3)
#define DOWNLOAD_ERR_SOFTWARE (4)
#define DOWNLOAD_ERR_FILE_SIZE (5)
#define DOWNLOAD_ERR_CHECKSUM (6)
#define DOWNLOAD_ERR_OVERFLOW (7)
#define DOWNLOAD_ERR_IMAGE (8)
#define DOWNLOAD_ERR_HOST (9)
#define DOWNLOAD_ERR_ABORT (10)
#define SYS_BASE_ADDR_SILICON (0)
#define PAC_BASE_ADDRESS_SILICON (SYS_BASE_ADDR_SILICON + 0x09000000)
#define PAC_SHARED_MEMORY_SILICON (PAC_BASE_ADDRESS_SILICON)
#define CW1200_APB(addr) (PAC_SHARED_MEMORY_SILICON + (addr))
/* ***************************************************************
*Device register definitions
*************************************************************** */
/* WBF - SPI Register Addresses */
#define ST90TDS_ADDR_ID_BASE (0x0000)
/* 16/32 bits */
#define ST90TDS_CONFIG_REG_ID (0x0000)
/* 16/32 bits */
#define ST90TDS_CONTROL_REG_ID (0x0001)
/* 16 bits, Q mode W/R */
#define ST90TDS_IN_OUT_QUEUE_REG_ID (0x0002)
/* 32 bits, AHB bus R/W */
#define ST90TDS_AHB_DPORT_REG_ID (0x0003)
/* 16/32 bits */
#define ST90TDS_SRAM_BASE_ADDR_REG_ID (0x0004)
/* 32 bits, APB bus R/W */
#define ST90TDS_SRAM_DPORT_REG_ID (0x0005)
/* 32 bits, t_settle/general */
#define ST90TDS_TSET_GEN_R_W_REG_ID (0x0006)
/* 16 bits, Q mode read, no length */
#define ST90TDS_FRAME_OUT_REG_ID (0x0007)
#define ST90TDS_ADDR_ID_MAX (ST90TDS_FRAME_OUT_REG_ID)
/* WBF - Control register bit set */
/* next o/p length, bit 11 to 0 */
#define ST90TDS_CONT_NEXT_LEN_MASK (0x0FFF)
#define ST90TDS_CONT_WUP_BIT (BIT(12))
#define ST90TDS_CONT_RDY_BIT (BIT(13))
#define ST90TDS_CONT_IRQ_ENABLE (BIT(14))
#define ST90TDS_CONT_RDY_ENABLE (BIT(15))
#define ST90TDS_CONT_IRQ_RDY_ENABLE (BIT(14)|BIT(15))
/* SPI Config register bit set */
#define ST90TDS_CONFIG_FRAME_BIT (BIT(2))
#define ST90TDS_CONFIG_WORD_MODE_BITS (BIT(3)|BIT(4))
#define ST90TDS_CONFIG_WORD_MODE_1 (BIT(3))
#define ST90TDS_CONFIG_WORD_MODE_2 (BIT(4))
#define ST90TDS_CONFIG_ERROR_0_BIT (BIT(5))
#define ST90TDS_CONFIG_ERROR_1_BIT (BIT(6))
#define ST90TDS_CONFIG_ERROR_2_BIT (BIT(7))
/* TBD: Sure??? */
#define ST90TDS_CONFIG_CSN_FRAME_BIT (BIT(7))
#define ST90TDS_CONFIG_ERROR_3_BIT (BIT(8))
#define ST90TDS_CONFIG_ERROR_4_BIT (BIT(9))
/* QueueM */
#define ST90TDS_CONFIG_ACCESS_MODE_BIT (BIT(10))
/* AHB bus */
#define ST90TDS_CONFIG_AHB_PRFETCH_BIT (BIT(11))
#define ST90TDS_CONFIG_CPU_CLK_DIS_BIT (BIT(12))
/* APB bus */
#define ST90TDS_CONFIG_PRFETCH_BIT (BIT(13))
/* cpu reset */
#define ST90TDS_CONFIG_CPU_RESET_BIT (BIT(14))
#define ST90TDS_CONFIG_CLEAR_INT_BIT (BIT(15))
/* For CW1200 the IRQ Enable and Ready Bits are in CONFIG register */
#define ST90TDS_CONF_IRQ_ENABLE (BIT(16))
#define ST90TDS_CONF_RDY_ENABLE (BIT(17))
#define ST90TDS_CONF_IRQ_RDY_ENABLE (BIT(16)|BIT(17))
int cw1200_data_read(struct cw1200_common *priv,
void *buf, size_t buf_len);
int cw1200_data_write(struct cw1200_common *priv,
const void *buf, size_t buf_len);
int cw1200_reg_read(struct cw1200_common *priv, u16 addr,
void *buf, size_t buf_len);
int cw1200_reg_write(struct cw1200_common *priv, u16 addr,
const void *buf, size_t buf_len);
static inline int cw1200_reg_read_16(struct cw1200_common *priv,
u16 addr, u16 *val)
{
u32 tmp;
int i;
i = cw1200_reg_read(priv, addr, &tmp, sizeof(tmp));
tmp = le32_to_cpu(tmp);
*val = tmp & 0xffff;
return i;
}
static inline int cw1200_reg_write_16(struct cw1200_common *priv,
u16 addr, u16 val)
{
u32 tmp = val;
tmp = cpu_to_le32(tmp);
return cw1200_reg_write(priv, addr, &tmp, sizeof(tmp));
}
static inline int cw1200_reg_read_32(struct cw1200_common *priv,
u16 addr, u32 *val)
{
int i = cw1200_reg_read(priv, addr, val, sizeof(*val));
*val = le32_to_cpu(*val);
return i;
}
static inline int cw1200_reg_write_32(struct cw1200_common *priv,
u16 addr, u32 val)
{
val = cpu_to_le32(val);
return cw1200_reg_write(priv, addr, &val, sizeof(val));
}
int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf,
size_t buf_len, u32 prefetch, u16 port_addr);
int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf,
size_t buf_len);
static inline int cw1200_apb_read(struct cw1200_common *priv, u32 addr,
void *buf, size_t buf_len)
{
return cw1200_indirect_read(priv, addr, buf, buf_len,
ST90TDS_CONFIG_PRFETCH_BIT,
ST90TDS_SRAM_DPORT_REG_ID);
}
static inline int cw1200_ahb_read(struct cw1200_common *priv, u32 addr,
void *buf, size_t buf_len)
{
return cw1200_indirect_read(priv, addr, buf, buf_len,
ST90TDS_CONFIG_AHB_PRFETCH_BIT,
ST90TDS_AHB_DPORT_REG_ID);
}
static inline int cw1200_apb_read_32(struct cw1200_common *priv,
u32 addr, u32 *val)
{
int i = cw1200_apb_read(priv, addr, val, sizeof(*val));
*val = le32_to_cpu(*val);
return i;
}
static inline int cw1200_apb_write_32(struct cw1200_common *priv,
u32 addr, u32 val)
{
val = cpu_to_le32(val);
return cw1200_apb_write(priv, addr, &val, sizeof(val));
}
static inline int cw1200_ahb_read_32(struct cw1200_common *priv,
u32 addr, u32 *val)
{
int i = cw1200_ahb_read(priv, addr, val, sizeof(*val));
*val = le32_to_cpu(*val);
return i;
}
#endif /* CW1200_HWIO_H_INCLUDED */

View File

@ -0,0 +1,730 @@
/*
* mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers
* ITP code
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/poll.h>
#include <linux/time.h>
#include <linux/random.h>
#include <linux/kallsyms.h>
#include <net/mac80211.h>
#include "cw1200.h"
#include "debug.h"
#include "itp.h"
#include "sta.h"
static int __cw1200_itp_open(struct cw1200_common *priv);
static int __cw1200_itp_close(struct cw1200_common *priv);
static void cw1200_itp_rx_start(struct cw1200_common *priv);
static void cw1200_itp_rx_stop(struct cw1200_common *priv);
static void cw1200_itp_rx_stats(struct cw1200_common *priv);
static void cw1200_itp_rx_reset(struct cw1200_common *priv);
static void cw1200_itp_tx_stop(struct cw1200_common *priv);
static void cw1200_itp_handle(struct cw1200_common *priv,
struct sk_buff *skb);
static void cw1200_itp_err(struct cw1200_common *priv,
int err,
int arg);
static void __cw1200_itp_tx_stop(struct cw1200_common *priv);
static ssize_t cw1200_itp_read(struct file *file,
char __user *user_buf, size_t count, loff_t *ppos)
{
struct cw1200_common *priv = file->private_data;
struct cw1200_itp *itp = &priv->debug->itp;
struct sk_buff *skb;
int ret;
if (skb_queue_empty(&itp->log_queue))
return 0;
skb = skb_dequeue(&itp->log_queue);
ret = copy_to_user(user_buf, skb->data, skb->len);
*ppos += skb->len;
skb->data[skb->len] = 0;
pr_debug("[ITP] >>> %s", skb->data);
consume_skb(skb);
return skb->len - ret;
}
static ssize_t cw1200_itp_write(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
struct cw1200_common *priv = file->private_data;
struct sk_buff *skb;
if (!count || count > 1024)
return -EINVAL;
skb = dev_alloc_skb(count + 1);
if (!skb)
return -ENOMEM;
skb_trim(skb, 0);
skb_put(skb, count + 1);
if (copy_from_user(skb->data, user_buf, count)) {
kfree_skb(skb);
return -EFAULT;
}
skb->data[count] = 0;
cw1200_itp_handle(priv, skb);
consume_skb(skb);
return count;
}
static unsigned int cw1200_itp_poll(struct file *file, poll_table *wait)
{
struct cw1200_common *priv = file->private_data;
struct cw1200_itp *itp = &priv->debug->itp;
unsigned int mask = 0;
poll_wait(file, &itp->read_wait, wait);
if (!skb_queue_empty(&itp->log_queue))
mask |= POLLIN | POLLRDNORM;
mask |= POLLOUT | POLLWRNORM;
return mask;
}
static int cw1200_itp_open(struct inode *inode, struct file *file)
{
struct cw1200_common *priv = inode->i_private;
struct cw1200_itp *itp = &priv->debug->itp;
int ret = 0;
file->private_data = priv;
if (atomic_inc_return(&itp->open_count) == 1) {
ret = __cw1200_itp_open(priv);
if (ret && !atomic_dec_return(&itp->open_count))
__cw1200_itp_close(priv);
} else {
atomic_dec(&itp->open_count);
ret = -EBUSY;
}
return ret;
}
static int cw1200_itp_close(struct inode *inode, struct file *file)
{
struct cw1200_common *priv = file->private_data;
struct cw1200_itp *itp = &priv->debug->itp;
if (!atomic_dec_return(&itp->open_count)) {
__cw1200_itp_close(priv);
wake_up(&itp->close_wait);
}
return 0;
}
static const struct file_operations fops_itp = {
.open = cw1200_itp_open,
.read = cw1200_itp_read,
.write = cw1200_itp_write,
.poll = cw1200_itp_poll,
.release = cw1200_itp_close,
.llseek = default_llseek,
.owner = THIS_MODULE,
};
static void cw1200_itp_fill_pattern(u8 *data, int size,
enum cw1200_itp_data_modes mode)
{
if (size <= 0)
return;
switch (mode) {
default:
case ITP_DATA_ZEROS:
memset(data, 0x0, size);
break;
case ITP_DATA_ONES:
memset(data, 0xff, size);
break;
case ITP_DATA_ZERONES:
memset(data, 0x55, size);
break;
case ITP_DATA_RANDOM:
get_random_bytes(data, size);
break;
}
return;
}
static void cw1200_itp_tx_work(struct work_struct *work)
{
struct cw1200_itp *itp = container_of(work, struct cw1200_itp,
tx_work.work);
struct cw1200_common *priv = itp->priv;
atomic_set(&priv->bh_tx, 1);
wake_up(&priv->bh_wq);
}
static void cw1200_itp_tx_finish(struct work_struct *work)
{
struct cw1200_itp *itp = container_of(work, struct cw1200_itp,
tx_finish.work);
__cw1200_itp_tx_stop(itp->priv);
}
int cw1200_itp_init(struct cw1200_common *priv)
{
struct cw1200_itp *itp = &priv->debug->itp;
itp->priv = priv;
atomic_set(&itp->open_count, 0);
atomic_set(&itp->stop_tx, 0);
atomic_set(&itp->awaiting_confirm, 0);
skb_queue_head_init(&itp->log_queue);
spin_lock_init(&itp->tx_lock);
init_waitqueue_head(&itp->read_wait);
init_waitqueue_head(&itp->write_wait);
init_waitqueue_head(&itp->close_wait);
INIT_DELAYED_WORK(&itp->tx_work, cw1200_itp_tx_work);
INIT_DELAYED_WORK(&itp->tx_finish, cw1200_itp_tx_finish);
itp->data = NULL;
itp->hdr_len = WSM_TX_EXTRA_HEADROOM +
sizeof(struct ieee80211_hdr_3addr);
if (!debugfs_create_file("itp", S_IRUSR | S_IWUSR,
priv->debug->debugfs_phy, priv, &fops_itp))
return -ENOMEM;
return 0;
}
void cw1200_itp_release(struct cw1200_common *priv)
{
struct cw1200_itp *itp = &priv->debug->itp;
wait_event_interruptible(itp->close_wait,
!atomic_read(&itp->open_count));
WARN_ON(atomic_read(&itp->open_count));
skb_queue_purge(&itp->log_queue);
cw1200_itp_tx_stop(priv);
}
static int __cw1200_itp_open(struct cw1200_common *priv)
{
struct cw1200_itp *itp = &priv->debug->itp;
if (!priv->vif)
return -EINVAL;
if (priv->join_status)
return -EINVAL;
itp->saved_channel = priv->channel;
if (!priv->channel)
priv->channel = &priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0];
wsm_set_bssid_filtering(priv, false);
cw1200_itp_rx_reset(priv);
return 0;
}
static int __cw1200_itp_close(struct cw1200_common *priv)
{
struct cw1200_itp *itp = &priv->debug->itp;
if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST)
cw1200_itp_rx_stop(priv);
cw1200_itp_tx_stop(priv);
cw1200_disable_listening(priv);
cw1200_update_filtering(priv);
priv->channel = itp->saved_channel;
return 0;
}
bool cw1200_is_itp(struct cw1200_common *priv)
{
struct cw1200_itp *itp = &priv->debug->itp;
return atomic_read(&itp->open_count) != 0;
}
static void cw1200_itp_rx_reset(struct cw1200_common *priv)
{
struct cw1200_itp *itp = &priv->debug->itp;
itp->rx_cnt = 0;
itp->rx_rssi = 0;
itp->rx_rssi_max = -1000;
itp->rx_rssi_min = 1000;
}
static void cw1200_itp_rx_start(struct cw1200_common *priv)
{
struct cw1200_itp *itp = &priv->debug->itp;
pr_debug("[ITP] RX start, band = %d, ch = %d\n",
itp->band, itp->ch);
atomic_set(&itp->test_mode, TEST_MODE_RX_TEST);
cw1200_update_listening(priv, false);
priv->channel = &priv->hw->
wiphy->bands[itp->band]->channels[itp->ch];
cw1200_update_listening(priv, true);
wsm_set_bssid_filtering(priv, false);
}
static void cw1200_itp_rx_stop(struct cw1200_common *priv)
{
struct cw1200_itp *itp = &priv->debug->itp;
pr_debug("[ITP] RX stop\n");
atomic_set(&itp->test_mode, TEST_MODE_NO_TEST);
cw1200_itp_rx_reset(priv);
}
static void cw1200_itp_rx_stats(struct cw1200_common *priv)
{
struct cw1200_itp *itp = &priv->debug->itp;
struct sk_buff *skb;
char buf[128];
int len, ret;
struct wsm_mib_counters_table counters;
ret = wsm_get_counters_table(priv, &counters);
if (ret)
cw1200_itp_err(priv, -EBUSY, 20);
if (!itp->rx_cnt)
len = snprintf(buf, sizeof(buf), "1,0,0,0,0,%d\n",
counters.rx_packet_errors);
else
len = snprintf(buf, sizeof(buf), "1,%d,%ld,%d,%d,%d\n",
itp->rx_cnt,
itp->rx_cnt ? itp->rx_rssi / itp->rx_cnt : 0,
itp->rx_rssi_min, itp->rx_rssi_max,
counters.rx_packet_errors);
if (len <= 0) {
cw1200_itp_err(priv, -EBUSY, 21);
return;
}
skb = dev_alloc_skb(len);
if (!skb) {
cw1200_itp_err(priv, -ENOMEM, 22);
return;
}
itp->rx_cnt = 0;
itp->rx_rssi = 0;
itp->rx_rssi_max = -1000;
itp->rx_rssi_min = 1000;
skb_trim(skb, 0);
skb_put(skb, len);
memcpy(skb->data, buf, len);
skb_queue_tail(&itp->log_queue, skb);
wake_up(&itp->read_wait);
}
static void cw1200_itp_tx_start(struct cw1200_common *priv)
{
struct wsm_tx *tx;
struct ieee80211_hdr_3addr *hdr;
struct cw1200_itp *itp = &priv->debug->itp;
struct wsm_mib_association_mode assoc_mode = {
.flags = WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE,
.preamble = itp->preamble,
};
int len;
u8 da_addr[6] = ITP_DEFAULT_DA_ADDR;
/* Rates index 4 and 5 are not supported */
if (itp->rate > 3)
itp->rate += 2;
pr_debug("[ITP] TX start: band = %d, ch = %d, rate = %d, preamble = %d, number = %d, data_mode = %d, interval = %d, power = %d, data_len = %d\n",
itp->band, itp->ch, itp->rate, itp->preamble,
itp->number, itp->data_mode, itp->interval_us,
itp->power, itp->data_len);
len = itp->hdr_len + itp->data_len;
itp->data = kmalloc(len, GFP_KERNEL);
tx = (struct wsm_tx *)itp->data;
tx->hdr.len = itp->data_len + itp->hdr_len;
tx->hdr.id = __cpu_to_le16(0x0004 | 1 << 6);
tx->max_tx_rate = itp->rate;
tx->queue_id = 3;
tx->more = 0;
tx->flags = 0xc;
tx->packet_id = 0x55ff55;
tx->reserved = 0;
tx->expire_time = 1;
if (itp->preamble == ITP_PREAMBLE_GREENFIELD)
tx->ht_tx_parameters = WSM_HT_TX_GREENFIELD;
else if (itp->preamble == ITP_PREAMBLE_MIXED)
tx->ht_tx_parameters = WSM_HT_TX_MIXED;
hdr = (struct ieee80211_hdr_3addr *)&itp->data[sizeof(struct wsm_tx)];
memset(hdr, 0, sizeof(*hdr));
hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_FCTL_TODS);
memcpy(hdr->addr1, da_addr, ETH_ALEN);
memcpy(hdr->addr2, priv->vif->addr, ETH_ALEN);
memcpy(hdr->addr3, da_addr, ETH_ALEN);
cw1200_itp_fill_pattern(&itp->data[itp->hdr_len],
itp->data_len, itp->data_mode);
cw1200_update_listening(priv, false);
priv->channel = &priv->hw->wiphy->bands[itp->band]->channels[itp->ch];
WARN_ON(wsm_set_output_power(priv, itp->power));
if (itp->preamble == ITP_PREAMBLE_SHORT ||
itp->preamble == ITP_PREAMBLE_LONG)
WARN_ON(wsm_set_association_mode(priv,
&assoc_mode));
wsm_set_bssid_filtering(priv, false);
cw1200_update_listening(priv, true);
spin_lock_bh(&itp->tx_lock);
atomic_set(&itp->test_mode, TEST_MODE_TX_TEST);
atomic_set(&itp->awaiting_confirm, 0);
atomic_set(&itp->stop_tx, 0);
atomic_set(&priv->bh_tx, 1);
ktime_get_ts(&itp->last_sent);
wake_up(&priv->bh_wq);
spin_unlock_bh(&itp->tx_lock);
}
void __cw1200_itp_tx_stop(struct cw1200_common *priv)
{
struct cw1200_itp *itp = &priv->debug->itp;
spin_lock_bh(&itp->tx_lock);
kfree(itp->data);
itp->data = NULL;
atomic_set(&itp->test_mode, TEST_MODE_NO_TEST);
spin_unlock_bh(&itp->tx_lock);
}
static void cw1200_itp_tx_stop(struct cw1200_common *priv)
{
struct cw1200_itp *itp = &priv->debug->itp;
pr_debug("[ITP] TX stop\n");
atomic_set(&itp->stop_tx, 1);
flush_workqueue(priv->workqueue);
/* time for FW to confirm all tx requests */
msleep(500);
__cw1200_itp_tx_stop(priv);
}
static int cw1200_print_fw_version(struct cw1200_common *priv,
u8 *buf, size_t len)
{
return snprintf(buf, len, "%s %d.%d",
cw1200_fw_types[priv->wsm_caps.fw_type],
priv->wsm_caps.fw_ver,
priv->wsm_caps.fw_build);
}
static void cw1200_itp_get_version(struct cw1200_common *priv,
enum cw1200_itp_version_type type)
{
struct cw1200_itp *itp = &priv->debug->itp;
struct sk_buff *skb;
char buf[ITP_BUF_SIZE];
size_t size = 0;
int len;
pr_debug("[ITP] print %s version\n",
type == ITP_CHIP_ID ? "chip" : "firmware");
len = snprintf(buf, ITP_BUF_SIZE, "2,");
if (len <= 0) {
cw1200_itp_err(priv, -EINVAL, 40);
return;
}
size += len;
switch (type) {
case ITP_CHIP_ID:
len = cw1200_print_fw_version(priv, buf+size,
ITP_BUF_SIZE - size);
if (len <= 0) {
cw1200_itp_err(priv, -EINVAL, 41);
return;
}
size += len;
break;
case ITP_FW_VER:
len = snprintf(buf+size, ITP_BUF_SIZE - size,
"%d.%d", priv->wsm_caps.hw_id,
priv->wsm_caps.hw_subid);
if (len <= 0) {
cw1200_itp_err(priv, -EINVAL, 42);
return;
}
size += len;
break;
default:
cw1200_itp_err(priv, -EINVAL, 43);
break;
}
len = snprintf(buf+size, ITP_BUF_SIZE-size, "\n");
if (len <= 0) {
cw1200_itp_err(priv, -EINVAL, 44);
return;
}
size += len;
skb = dev_alloc_skb(size);
if (!skb) {
cw1200_itp_err(priv, -ENOMEM, 45);
return;
}
skb_trim(skb, 0);
skb_put(skb, size);
memcpy(skb->data, buf, size);
skb_queue_tail(&itp->log_queue, skb);
wake_up(&itp->read_wait);
}
int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data,
size_t *tx_len, int *burst)
{
struct cw1200_itp *itp;
struct timespec now;
int time_left_us;
if (!priv->debug)
return 0;
itp = &priv->debug->itp;
if (!itp)
return 0;
spin_lock_bh(&itp->tx_lock);
if (atomic_read(&itp->test_mode) != TEST_MODE_TX_TEST)
goto out;
if (atomic_read(&itp->stop_tx))
goto out;
if (itp->number == 0) {
atomic_set(&itp->stop_tx, 1);
queue_delayed_work(priv->workqueue, &itp->tx_finish, HZ/10);
goto out;
}
if (!itp->data)
goto out;
if (priv->hw_bufs_used >= 2) {
if (!atomic_read(&priv->bh_rx))
atomic_set(&priv->bh_rx, 1);
atomic_set(&priv->bh_tx, 1);
goto out;
}
ktime_get_ts(&now);
time_left_us = (itp->last_sent.tv_sec - now.tv_sec)*1000000 +
(itp->last_sent.tv_nsec - now.tv_nsec)/1000 +
itp->interval_us;
if (time_left_us > ITP_TIME_THRES_US) {
queue_delayed_work(priv->workqueue, &itp->tx_work,
ITP_US_TO_MS(time_left_us)*HZ/1000);
goto out;
}
if (time_left_us > 50)
udelay(time_left_us);
if (itp->number > 0)
itp->number--;
*data = itp->data;
*tx_len = itp->data_len + itp->hdr_len;
if (itp->data_mode == ITP_DATA_RANDOM)
cw1200_itp_fill_pattern(&itp->data[itp->hdr_len],
itp->data_len, itp->data_mode);
*burst = 2;
atomic_set(&priv->bh_tx, 1);
ktime_get_ts(&itp->last_sent);
atomic_add(1, &itp->awaiting_confirm);
spin_unlock_bh(&itp->tx_lock);
return 1;
out:
spin_unlock_bh(&itp->tx_lock);
return 0;
}
bool cw1200_itp_rxed(struct cw1200_common *priv, struct sk_buff *skb)
{
struct cw1200_itp *itp = &priv->debug->itp;
struct ieee80211_rx_status *rx = IEEE80211_SKB_RXCB(skb);
int signal;
if (atomic_read(&itp->test_mode) != TEST_MODE_RX_TEST)
return cw1200_is_itp(priv);
if (rx->freq != priv->channel->center_freq)
return true;
signal = rx->signal;
itp->rx_cnt++;
itp->rx_rssi += signal;
if (itp->rx_rssi_min > rx->signal)
itp->rx_rssi_min = rx->signal;
if (itp->rx_rssi_max < rx->signal)
itp->rx_rssi_max = rx->signal;
return true;
}
void cw1200_itp_wake_up_tx(struct cw1200_common *priv)
{
wake_up(&priv->debug->itp.write_wait);
}
bool cw1200_itp_tx_running(struct cw1200_common *priv)
{
if (atomic_read(&priv->debug->itp.awaiting_confirm) ||
atomic_read(&priv->debug->itp.test_mode) ==
TEST_MODE_TX_TEST) {
atomic_sub(1, &priv->debug->itp.awaiting_confirm);
return true;
}
return false;
}
static void cw1200_itp_handle(struct cw1200_common *priv,
struct sk_buff *skb)
{
struct cw1200_itp *itp = &priv->debug->itp;
const struct wiphy *wiphy = priv->hw->wiphy;
int cmd;
int ret;
pr_debug("[ITP] <<< %s", skb->data);
if (sscanf(skb->data, "%d", &cmd) != 1) {
cw1200_itp_err(priv, -EINVAL, 1);
return;
}
switch (cmd) {
case 1: /* RX test */
if (atomic_read(&itp->test_mode)) {
cw1200_itp_err(priv, -EBUSY, 0);
return;
}
ret = sscanf(skb->data, "%d,%d,%d",
&cmd, &itp->band, &itp->ch);
if (ret != 3) {
cw1200_itp_err(priv, -EINVAL, ret + 1);
return;
}
if (itp->band >= 2) {
cw1200_itp_err(priv, -EINVAL, 2);
} else if (!wiphy->bands[itp->band]) {
cw1200_itp_err(priv, -EINVAL, 2);
} else if (itp->ch >= wiphy->bands[itp->band]->n_channels) {
cw1200_itp_err(priv, -EINVAL, 3);
} else {
cw1200_itp_rx_stats(priv);
cw1200_itp_rx_start(priv);
}
break;
case 2: /* RX stat */
cw1200_itp_rx_stats(priv);
break;
case 3: /* RX/TX stop */
if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST) {
cw1200_itp_rx_stats(priv);
cw1200_itp_rx_stop(priv);
} else if (atomic_read(&itp->test_mode) == TEST_MODE_TX_TEST) {
cw1200_itp_tx_stop(priv);
} else {
cw1200_itp_err(priv, -EBUSY, 0);
}
break;
case 4: /* TX start */
if (atomic_read(&itp->test_mode) != TEST_MODE_NO_TEST) {
cw1200_itp_err(priv, -EBUSY, 0);
return;
}
ret = sscanf(skb->data, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
&cmd, &itp->band, &itp->ch, &itp->rate,
&itp->preamble, &itp->number, &itp->data_mode,
&itp->interval_us, &itp->power, &itp->data_len);
if (ret != 10) {
cw1200_itp_err(priv, -EINVAL, ret + 1);
return;
}
if (itp->band >= 2) {
cw1200_itp_err(priv, -EINVAL, 2);
} else if (!wiphy->bands[itp->band]) {
cw1200_itp_err(priv, -EINVAL, 2);
} else if (itp->ch >= wiphy->bands[itp->band]->n_channels) {
cw1200_itp_err(priv, -EINVAL, 3);
} else if (itp->rate >= 20) {
cw1200_itp_err(priv, -EINVAL, 4);
} else if (itp->preamble >= ITP_PREAMBLE_MAX) {
cw1200_itp_err(priv, -EINVAL, 5);
} else if (itp->data_mode >= ITP_DATA_MAX_MODE) {
cw1200_itp_err(priv, -EINVAL, 7);
} else if (itp->data_len < ITP_MIN_DATA_SIZE ||
itp->data_len > (priv->wsm_caps.input_buffer_size - itp->hdr_len)) {
cw1200_itp_err(priv, -EINVAL, 8);
} else {
cw1200_itp_tx_start(priv);
}
break;
case 5:
cw1200_itp_get_version(priv, ITP_CHIP_ID);
break;
case 6:
cw1200_itp_get_version(priv, ITP_FW_VER);
break;
}
}
static void cw1200_itp_err(struct cw1200_common *priv,
int err, int arg)
{
struct cw1200_itp *itp = &priv->debug->itp;
struct sk_buff *skb;
static char buf[255];
int len;
len = snprintf(buf, sizeof(buf), "%d,%d\n",
err, arg);
if (len <= 0)
return;
skb = dev_alloc_skb(len);
if (!skb)
return;
skb_trim(skb, 0);
skb_put(skb, len);
memcpy(skb->data, buf, len);
skb_queue_tail(&itp->log_queue, skb);
wake_up(&itp->read_wait);
len = sprint_symbol(buf,
(unsigned long)__builtin_return_address(0));
if (len <= 0)
return;
pr_debug("[ITP] error %d,%d from %s\n",
err, arg, buf);
}

View File

@ -0,0 +1,144 @@
/*
* ITP code for ST-Ericsson CW1200 mac80211 driver
*
* Copyright (c) 2011, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef CW1200_ITP_H_INCLUDED
#define CW1200_ITP_H_INCLUDED
struct cw200_common;
struct wsm_tx_confirm;
struct dentry;
#ifdef CONFIG_CW1200_ITP
/*extern*/ struct ieee80211_channel;
#define TEST_MODE_NO_TEST (0)
#define TEST_MODE_RX_TEST (1)
#define TEST_MODE_TX_TEST (2)
#define ITP_DEFAULT_DA_ADDR {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
#define ITP_MIN_DATA_SIZE 6
#define ITP_MAX_DATA_SIZE 1600
#define ITP_TIME_THRES_US 10000
#define ITP_US_TO_MS(x) ((x)/1000)
#define ITP_MS_TO_US(x) ((x)*1000)
#define ITP_BUF_SIZE 255
enum cw1200_itp_data_modes {
ITP_DATA_ZEROS,
ITP_DATA_ONES,
ITP_DATA_ZERONES,
ITP_DATA_RANDOM,
ITP_DATA_MAX_MODE,
};
enum cw1200_itp_version_type {
ITP_CHIP_ID,
ITP_FW_VER,
};
enum cw1200_itp_preamble_type {
ITP_PREAMBLE_LONG,
ITP_PREAMBLE_SHORT,
ITP_PREAMBLE_OFDM,
ITP_PREAMBLE_MIXED,
ITP_PREAMBLE_GREENFIELD,
ITP_PREAMBLE_MAX,
};
struct cw1200_itp {
struct cw1200_common *priv;
atomic_t open_count;
atomic_t awaiting_confirm;
struct sk_buff_head log_queue;
wait_queue_head_t read_wait;
wait_queue_head_t write_wait;
wait_queue_head_t close_wait;
struct ieee80211_channel *saved_channel;
atomic_t stop_tx;
struct delayed_work tx_work;
struct delayed_work tx_finish;
spinlock_t tx_lock;
struct timespec last_sent;
atomic_t test_mode;
int rx_cnt;
long rx_rssi;
int rx_rssi_max;
int rx_rssi_min;
unsigned band;
unsigned ch;
unsigned rate;
unsigned preamble;
unsigned int number;
unsigned data_mode;
int interval_us;
int power;
u8 *data;
int hdr_len;
int data_len;
};
int cw1200_itp_init(struct cw1200_common *priv);
void cw1200_itp_release(struct cw1200_common *priv);
bool cw1200_is_itp(struct cw1200_common *priv);
bool cw1200_itp_rxed(struct cw1200_common *priv, struct sk_buff *skb);
void cw1200_itp_wake_up_tx(struct cw1200_common *priv);
int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data,
size_t *tx_len, int *burst);
bool cw1200_itp_tx_running(struct cw1200_common *priv);
#else /* CONFIG_CW1200_ITP */
static inline int cw1200_itp_init(struct cw1200_common *priv)
{
return 0;
}
static inline void cw1200_itp_release(struct cw1200_common *priv)
{
}
static inline bool cw1200_is_itp(struct cw1200_common *priv)
{
return false;
}
static inline bool cw1200_itp_rxed(struct cw1200_common *priv,
struct sk_buff *skb)
{
return false;
}
static inline void cw1200_itp_consume_txed(struct cw1200_common *priv)
{
}
static inline void cw1200_itp_wake_up_tx(struct cw1200_common *priv)
{
}
static inline int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data,
size_t *tx_len, int *burst)
{
return 0;
}
static inline bool cw1200_itp_tx_running(struct cw1200_common *priv)
{
return false;
}
#endif /* CONFIG_CW1200_ITP */
#endif /* CW1200_ITP_H_INCLUDED */

View File

@ -0,0 +1,618 @@
/*
* mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* Based on:
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
* Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
*
* Based on:
* - the islsm (softmac prism54) driver, which is:
* Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
* - stlc45xx driver
* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/firmware.h>
#include <linux/etherdevice.h>
#include <linux/vmalloc.h>
#include <linux/random.h>
#include <linux/sched.h>
#include <net/mac80211.h>
#include "cw1200.h"
#include "txrx.h"
#include "sbus.h"
#include "fwio.h"
#include "hwio.h"
#include "bh.h"
#include "sta.h"
#include "scan.h"
#include "debug.h"
#include "pm.h"
MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>");
MODULE_DESCRIPTION("Softmac ST-Ericsson CW1200 common code");
MODULE_LICENSE("GPL");
MODULE_ALIAS("cw1200_core");
/* Accept MAC address of the form macaddr=0x00,0x80,0xE1,0x30,0x40,0x50 */
static u8 cw1200_mac_template[ETH_ALEN] = {0x02, 0x80, 0xe1, 0x00, 0x00, 0x00};
module_param_array_named(macaddr, cw1200_mac_template, byte, NULL, S_IRUGO);
MODULE_PARM_DESC(macaddr, "Override platform_data MAC address");
static char *cw1200_sdd_path;
module_param(cw1200_sdd_path, charp, 0644);
MODULE_PARM_DESC(cw1200_sdd_path, "Override platform_data SDD file");
static int cw1200_refclk;
module_param(cw1200_refclk, int, 0644);
MODULE_PARM_DESC(cw1200_refclk, "Override platform_data reference clock");
int cw1200_power_mode = wsm_power_mode_quiescent;
module_param(cw1200_power_mode, int, 0644);
MODULE_PARM_DESC(cw1200_power_mode, "WSM power mode. 0 == active, 1 == doze, 2 == quiescent (default)");
#ifdef CONFIG_CW1200_ETF
int etf_mode;
module_param(etf_mode, int, 0644);
MODULE_PARM_DESC(etf_mode, "Enable EngineeringTestingFramework operation");
#endif
#define RATETAB_ENT(_rate, _rateid, _flags) \
{ \
.bitrate = (_rate), \
.hw_value = (_rateid), \
.flags = (_flags), \
}
static struct ieee80211_rate cw1200_rates[] = {
RATETAB_ENT(10, 0, 0),
RATETAB_ENT(20, 1, 0),
RATETAB_ENT(55, 2, 0),
RATETAB_ENT(110, 3, 0),
RATETAB_ENT(60, 6, 0),
RATETAB_ENT(90, 7, 0),
RATETAB_ENT(120, 8, 0),
RATETAB_ENT(180, 9, 0),
RATETAB_ENT(240, 10, 0),
RATETAB_ENT(360, 11, 0),
RATETAB_ENT(480, 12, 0),
RATETAB_ENT(540, 13, 0),
};
static struct ieee80211_rate cw1200_mcs_rates[] = {
RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS),
RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS),
RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS),
RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS),
RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS),
RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS),
RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS),
RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS),
};
#define cw1200_a_rates (cw1200_rates + 4)
#define cw1200_a_rates_size (ARRAY_SIZE(cw1200_rates) - 4)
#define cw1200_g_rates (cw1200_rates + 0)
#define cw1200_g_rates_size (ARRAY_SIZE(cw1200_rates))
#define cw1200_n_rates (cw1200_mcs_rates)
#define cw1200_n_rates_size (ARRAY_SIZE(cw1200_mcs_rates))
#define CHAN2G(_channel, _freq, _flags) { \
.band = IEEE80211_BAND_2GHZ, \
.center_freq = (_freq), \
.hw_value = (_channel), \
.flags = (_flags), \
.max_antenna_gain = 0, \
.max_power = 30, \
}
#define CHAN5G(_channel, _flags) { \
.band = IEEE80211_BAND_5GHZ, \
.center_freq = 5000 + (5 * (_channel)), \
.hw_value = (_channel), \
.flags = (_flags), \
.max_antenna_gain = 0, \
.max_power = 30, \
}
static struct ieee80211_channel cw1200_2ghz_chantable[] = {
CHAN2G(1, 2412, 0),
CHAN2G(2, 2417, 0),
CHAN2G(3, 2422, 0),
CHAN2G(4, 2427, 0),
CHAN2G(5, 2432, 0),
CHAN2G(6, 2437, 0),
CHAN2G(7, 2442, 0),
CHAN2G(8, 2447, 0),
CHAN2G(9, 2452, 0),
CHAN2G(10, 2457, 0),
CHAN2G(11, 2462, 0),
CHAN2G(12, 2467, 0),
CHAN2G(13, 2472, 0),
CHAN2G(14, 2484, 0),
};
static struct ieee80211_channel cw1200_5ghz_chantable[] = {
CHAN5G(34, 0), CHAN5G(36, 0),
CHAN5G(38, 0), CHAN5G(40, 0),
CHAN5G(42, 0), CHAN5G(44, 0),
CHAN5G(46, 0), CHAN5G(48, 0),
CHAN5G(52, 0), CHAN5G(56, 0),
CHAN5G(60, 0), CHAN5G(64, 0),
CHAN5G(100, 0), CHAN5G(104, 0),
CHAN5G(108, 0), CHAN5G(112, 0),
CHAN5G(116, 0), CHAN5G(120, 0),
CHAN5G(124, 0), CHAN5G(128, 0),
CHAN5G(132, 0), CHAN5G(136, 0),
CHAN5G(140, 0), CHAN5G(149, 0),
CHAN5G(153, 0), CHAN5G(157, 0),
CHAN5G(161, 0), CHAN5G(165, 0),
CHAN5G(184, 0), CHAN5G(188, 0),
CHAN5G(192, 0), CHAN5G(196, 0),
CHAN5G(200, 0), CHAN5G(204, 0),
CHAN5G(208, 0), CHAN5G(212, 0),
CHAN5G(216, 0),
};
static struct ieee80211_supported_band cw1200_band_2ghz = {
.channels = cw1200_2ghz_chantable,
.n_channels = ARRAY_SIZE(cw1200_2ghz_chantable),
.bitrates = cw1200_g_rates,
.n_bitrates = cw1200_g_rates_size,
.ht_cap = {
.cap = IEEE80211_HT_CAP_GRN_FLD |
(1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
IEEE80211_HT_CAP_MAX_AMSDU,
.ht_supported = 1,
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
.mcs = {
.rx_mask[0] = 0xFF,
.rx_highest = __cpu_to_le16(0x41),
.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
},
},
};
static struct ieee80211_supported_band cw1200_band_5ghz = {
.channels = cw1200_5ghz_chantable,
.n_channels = ARRAY_SIZE(cw1200_5ghz_chantable),
.bitrates = cw1200_a_rates,
.n_bitrates = cw1200_a_rates_size,
.ht_cap = {
.cap = IEEE80211_HT_CAP_GRN_FLD |
(1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
IEEE80211_HT_CAP_MAX_AMSDU,
.ht_supported = 1,
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
.mcs = {
.rx_mask[0] = 0xFF,
.rx_highest = __cpu_to_le16(0x41),
.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
},
},
};
static const unsigned long cw1200_ttl[] = {
1 * HZ, /* VO */
2 * HZ, /* VI */
5 * HZ, /* BE */
10 * HZ /* BK */
};
static const struct ieee80211_ops cw1200_ops = {
.start = cw1200_start,
.stop = cw1200_stop,
.add_interface = cw1200_add_interface,
.remove_interface = cw1200_remove_interface,
.change_interface = cw1200_change_interface,
.tx = cw1200_tx,
.hw_scan = cw1200_hw_scan,
.set_tim = cw1200_set_tim,
.sta_notify = cw1200_sta_notify,
.sta_add = cw1200_sta_add,
.sta_remove = cw1200_sta_remove,
.set_key = cw1200_set_key,
.set_rts_threshold = cw1200_set_rts_threshold,
.config = cw1200_config,
.bss_info_changed = cw1200_bss_info_changed,
.prepare_multicast = cw1200_prepare_multicast,
.configure_filter = cw1200_configure_filter,
.conf_tx = cw1200_conf_tx,
.get_stats = cw1200_get_stats,
.ampdu_action = cw1200_ampdu_action,
.flush = cw1200_flush,
.suspend = cw1200_wow_suspend,
.resume = cw1200_wow_resume,
/* Intentionally not offloaded: */
/*.channel_switch = cw1200_channel_switch, */
/*.remain_on_channel = cw1200_remain_on_channel, */
/*.cancel_remain_on_channel = cw1200_cancel_remain_on_channel, */
};
int cw1200_ba_rx_tids = -1;
int cw1200_ba_tx_tids = -1;
module_param(cw1200_ba_rx_tids, int, 0644);
module_param(cw1200_ba_tx_tids, int, 0644);
MODULE_PARM_DESC(cw1200_ba_rx_tids, "Block ACK RX TIDs");
MODULE_PARM_DESC(cw1200_ba_tx_tids, "Block ACK TX TIDs");
static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr,
const bool have_5ghz)
{
int i, band;
struct ieee80211_hw *hw;
struct cw1200_common *priv;
hw = ieee80211_alloc_hw(sizeof(struct cw1200_common), &cw1200_ops);
if (!hw)
return NULL;
priv = hw->priv;
priv->hw = hw;
priv->hw_type = -1;
priv->mode = NL80211_IFTYPE_UNSPECIFIED;
priv->rates = cw1200_rates; /* TODO: fetch from FW */
priv->mcs_rates = cw1200_n_rates;
if (cw1200_ba_rx_tids != -1)
priv->ba_rx_tid_mask = cw1200_ba_rx_tids;
else
priv->ba_rx_tid_mask = 0xFF; /* Enable RX BLKACK for all TIDs */
if (cw1200_ba_tx_tids != -1)
priv->ba_tx_tid_mask = cw1200_ba_tx_tids;
else
priv->ba_tx_tid_mask = 0xff; /* Enable TX BLKACK for all TIDs */
hw->flags = IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
IEEE80211_HW_REPORTS_TX_ACK_STATUS |
IEEE80211_HW_SUPPORTS_UAPSD |
IEEE80211_HW_CONNECTION_MONITOR |
IEEE80211_HW_AMPDU_AGGREGATION |
IEEE80211_HW_TX_AMPDU_SETUP_IN_HW |
IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC;
hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_MESH_POINT) |
BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO);
/* Support only for limited wowlan functionalities */
hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY |
WIPHY_WOWLAN_DISCONNECT;
hw->wiphy->wowlan.n_patterns = 0;
hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
hw->channel_change_time = 1000; /* TODO: find actual value */
hw->queues = 4;
priv->rts_threshold = -1;
hw->max_rates = 8;
hw->max_rate_tries = 15;
hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM +
8; /* TKIP IV */
hw->sta_data_size = sizeof(struct cw1200_sta_priv);
hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &cw1200_band_2ghz;
if (have_5ghz)
hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &cw1200_band_5ghz;
/* Channel params have to be cleared before registering wiphy again */
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
struct ieee80211_supported_band *sband = hw->wiphy->bands[band];
if (!sband)
continue;
for (i = 0; i < sband->n_channels; i++) {
sband->channels[i].flags = 0;
sband->channels[i].max_antenna_gain = 0;
sband->channels[i].max_power = 30;
}
}
hw->wiphy->max_scan_ssids = 2;
hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
if (macaddr)
SET_IEEE80211_PERM_ADDR(hw, (u8 *)macaddr);
else
SET_IEEE80211_PERM_ADDR(hw, cw1200_mac_template);
/* Fix up mac address if necessary */
if (hw->wiphy->perm_addr[3] == 0 &&
hw->wiphy->perm_addr[4] == 0 &&
hw->wiphy->perm_addr[5] == 0) {
get_random_bytes(&hw->wiphy->perm_addr[3], 3);
}
mutex_init(&priv->wsm_cmd_mux);
mutex_init(&priv->conf_mutex);
priv->workqueue = create_singlethread_workqueue("cw1200_wq");
sema_init(&priv->scan.lock, 1);
INIT_WORK(&priv->scan.work, cw1200_scan_work);
INIT_DELAYED_WORK(&priv->scan.probe_work, cw1200_probe_work);
INIT_DELAYED_WORK(&priv->scan.timeout, cw1200_scan_timeout);
INIT_DELAYED_WORK(&priv->clear_recent_scan_work,
cw1200_clear_recent_scan_work);
INIT_DELAYED_WORK(&priv->join_timeout, cw1200_join_timeout);
INIT_WORK(&priv->unjoin_work, cw1200_unjoin_work);
INIT_WORK(&priv->join_complete_work, cw1200_join_complete_work);
INIT_WORK(&priv->wep_key_work, cw1200_wep_key_work);
INIT_WORK(&priv->tx_policy_upload_work, tx_policy_upload_work);
spin_lock_init(&priv->event_queue_lock);
INIT_LIST_HEAD(&priv->event_queue);
INIT_WORK(&priv->event_handler, cw1200_event_handler);
INIT_DELAYED_WORK(&priv->bss_loss_work, cw1200_bss_loss_work);
INIT_WORK(&priv->bss_params_work, cw1200_bss_params_work);
spin_lock_init(&priv->bss_loss_lock);
spin_lock_init(&priv->ps_state_lock);
INIT_WORK(&priv->set_cts_work, cw1200_set_cts_work);
INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work);
INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work);
INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work);
INIT_WORK(&priv->link_id_work, cw1200_link_id_work);
INIT_DELAYED_WORK(&priv->link_id_gc_work, cw1200_link_id_gc_work);
INIT_WORK(&priv->linkid_reset_work, cw1200_link_id_reset);
INIT_WORK(&priv->update_filtering_work, cw1200_update_filtering_work);
INIT_WORK(&priv->set_beacon_wakeup_period_work,
cw1200_set_beacon_wakeup_period_work);
init_timer(&priv->mcast_timeout);
priv->mcast_timeout.data = (unsigned long)priv;
priv->mcast_timeout.function = cw1200_mcast_timeout;
if (cw1200_queue_stats_init(&priv->tx_queue_stats,
CW1200_LINK_ID_MAX,
cw1200_skb_dtor,
priv)) {
ieee80211_free_hw(hw);
return NULL;
}
for (i = 0; i < 4; ++i) {
if (cw1200_queue_init(&priv->tx_queue[i],
&priv->tx_queue_stats, i, 16,
cw1200_ttl[i])) {
for (; i > 0; i--)
cw1200_queue_deinit(&priv->tx_queue[i - 1]);
cw1200_queue_stats_deinit(&priv->tx_queue_stats);
ieee80211_free_hw(hw);
return NULL;
}
}
init_waitqueue_head(&priv->channel_switch_done);
init_waitqueue_head(&priv->wsm_cmd_wq);
init_waitqueue_head(&priv->wsm_startup_done);
init_waitqueue_head(&priv->ps_mode_switch_done);
wsm_buf_init(&priv->wsm_cmd_buf);
spin_lock_init(&priv->wsm_cmd.lock);
priv->wsm_cmd.done = 1;
tx_policy_init(priv);
return hw;
}
static int cw1200_register_common(struct ieee80211_hw *dev)
{
struct cw1200_common *priv = dev->priv;
int err;
#ifdef CONFIG_CW1200_ETF
if (etf_mode)
goto done;
#endif
err = cw1200_pm_init(&priv->pm_state, priv);
if (err) {
pr_err("Cannot init PM. (%d).\n",
err);
return err;
}
err = ieee80211_register_hw(dev);
if (err) {
pr_err("Cannot register device (%d).\n",
err);
cw1200_pm_deinit(&priv->pm_state);
return err;
}
#ifdef CONFIG_CW1200_ETF
done:
#endif
cw1200_debug_init(priv);
pr_info("Registered as '%s'\n", wiphy_name(dev->wiphy));
return 0;
}
static void cw1200_free_common(struct ieee80211_hw *dev)
{
ieee80211_free_hw(dev);
}
static void cw1200_unregister_common(struct ieee80211_hw *dev)
{
struct cw1200_common *priv = dev->priv;
int i;
#ifdef CONFIG_CW1200_ETF
if (!etf_mode) {
#endif
ieee80211_unregister_hw(dev);
#ifdef CONFIG_CW1200_ETF
}
#endif
del_timer_sync(&priv->mcast_timeout);
cw1200_unregister_bh(priv);
cw1200_debug_release(priv);
mutex_destroy(&priv->conf_mutex);
wsm_buf_deinit(&priv->wsm_cmd_buf);
destroy_workqueue(priv->workqueue);
priv->workqueue = NULL;
if (priv->sdd) {
release_firmware(priv->sdd);
priv->sdd = NULL;
}
for (i = 0; i < 4; ++i)
cw1200_queue_deinit(&priv->tx_queue[i]);
cw1200_queue_stats_deinit(&priv->tx_queue_stats);
cw1200_pm_deinit(&priv->pm_state);
}
/* Clock is in KHz */
u32 cw1200_dpll_from_clk(u16 clk_khz)
{
switch (clk_khz) {
case 0x32C8: /* 13000 KHz */
return 0x1D89D241;
case 0x3E80: /* 16000 KHz */
return 0x000001E1;
case 0x41A0: /* 16800 KHz */
return 0x124931C1;
case 0x4B00: /* 19200 KHz */
return 0x00000191;
case 0x5DC0: /* 24000 KHz */
return 0x00000141;
case 0x6590: /* 26000 KHz */
return 0x0EC4F121;
case 0x8340: /* 33600 KHz */
return 0x092490E1;
case 0x9600: /* 38400 KHz */
return 0x100010C1;
case 0x9C40: /* 40000 KHz */
return 0x000000C1;
case 0xBB80: /* 48000 KHz */
return 0x000000A1;
case 0xCB20: /* 52000 KHz */
return 0x07627091;
default:
pr_err("Unknown Refclk freq (0x%04x), using 2600KHz\n",
clk_khz);
return 0x0EC4F121;
}
}
int cw1200_core_probe(const struct sbus_ops *sbus_ops,
struct sbus_priv *sbus,
struct device *pdev,
struct cw1200_common **core,
int ref_clk, const u8 *macaddr,
const char *sdd_path, bool have_5ghz)
{
int err = -EINVAL;
struct ieee80211_hw *dev;
struct cw1200_common *priv;
struct wsm_operational_mode mode = {
.power_mode = cw1200_power_mode,
.disable_more_flag_usage = true,
};
dev = cw1200_init_common(macaddr, have_5ghz);
if (!dev)
goto err;
priv = dev->priv;
priv->hw_refclk = ref_clk;
if (cw1200_refclk)
priv->hw_refclk = cw1200_refclk;
priv->sdd_path = (char *)sdd_path;
if (cw1200_sdd_path)
priv->sdd_path = cw1200_sdd_path;
priv->sbus_ops = sbus_ops;
priv->sbus_priv = sbus;
priv->pdev = pdev;
SET_IEEE80211_DEV(priv->hw, pdev);
/* Pass struct cw1200_common back up */
*core = priv;
err = cw1200_register_bh(priv);
if (err)
goto err1;
#ifdef CONFIG_CW1200_ETF
if (etf_mode)
goto skip_fw;
#endif
err = cw1200_load_firmware(priv);
if (err)
goto err2;
if (wait_event_interruptible_timeout(priv->wsm_startup_done,
priv->firmware_ready,
3*HZ) <= 0) {
/* TODO: Need to find how to reset device
in QUEUE mode properly.
*/
pr_err("Timeout waiting on device startup\n");
err = -ETIMEDOUT;
goto err2;
}
/* Set low-power mode. */
wsm_set_operational_mode(priv, &mode);
/* Enable multi-TX confirmation */
wsm_use_multi_tx_conf(priv, true);
#ifdef CONFIG_CW1200_ETF
skip_fw:
#endif
err = cw1200_register_common(dev);
if (err)
goto err2;
return err;
err2:
cw1200_unregister_bh(priv);
err1:
cw1200_free_common(dev);
err:
*core = NULL;
return err;
}
EXPORT_SYMBOL_GPL(cw1200_core_probe);
void cw1200_core_release(struct cw1200_common *self)
{
/* Disable device interrupts */
self->sbus_ops->lock(self->sbus_priv);
__cw1200_irq_enable(self, 0);
self->sbus_ops->unlock(self->sbus_priv);
/* And then clean up */
cw1200_unregister_common(self->hw);
cw1200_free_common(self->hw);
return;
}
EXPORT_SYMBOL_GPL(cw1200_core_release);

View File

@ -0,0 +1,367 @@
/*
* Mac80211 power management API for ST-Ericsson CW1200 drivers
*
* Copyright (c) 2011, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/if_ether.h>
#include "cw1200.h"
#include "pm.h"
#include "sta.h"
#include "bh.h"
#include "sbus.h"
#define CW1200_BEACON_SKIPPING_MULTIPLIER 3
struct cw1200_udp_port_filter {
struct wsm_udp_port_filter_hdr hdr;
/* Up to 4 filters are allowed. */
struct wsm_udp_port_filter filters[WSM_MAX_FILTER_ELEMENTS];
} __packed;
struct cw1200_ether_type_filter {
struct wsm_ether_type_filter_hdr hdr;
/* Up to 4 filters are allowed. */
struct wsm_ether_type_filter filters[WSM_MAX_FILTER_ELEMENTS];
} __packed;
static struct cw1200_udp_port_filter cw1200_udp_port_filter_on = {
.hdr.num = 2,
.filters = {
[0] = {
.action = WSM_FILTER_ACTION_FILTER_OUT,
.type = WSM_FILTER_PORT_TYPE_DST,
.port = __cpu_to_le16(67), /* DHCP Bootps */
},
[1] = {
.action = WSM_FILTER_ACTION_FILTER_OUT,
.type = WSM_FILTER_PORT_TYPE_DST,
.port = __cpu_to_le16(68), /* DHCP Bootpc */
},
}
};
static struct wsm_udp_port_filter_hdr cw1200_udp_port_filter_off = {
.num = 0,
};
#ifndef ETH_P_WAPI
#define ETH_P_WAPI 0x88B4
#endif
static struct cw1200_ether_type_filter cw1200_ether_type_filter_on = {
.hdr.num = 4,
.filters = {
[0] = {
.action = WSM_FILTER_ACTION_FILTER_IN,
.type = __cpu_to_le16(ETH_P_IP),
},
[1] = {
.action = WSM_FILTER_ACTION_FILTER_IN,
.type = __cpu_to_le16(ETH_P_PAE),
},
[2] = {
.action = WSM_FILTER_ACTION_FILTER_IN,
.type = __cpu_to_le16(ETH_P_WAPI),
},
[3] = {
.action = WSM_FILTER_ACTION_FILTER_IN,
.type = __cpu_to_le16(ETH_P_ARP),
},
},
};
static struct wsm_ether_type_filter_hdr cw1200_ether_type_filter_off = {
.num = 0,
};
/* private */
struct cw1200_suspend_state {
unsigned long bss_loss_tmo;
unsigned long join_tmo;
unsigned long direct_probe;
unsigned long link_id_gc;
bool beacon_skipping;
u8 prev_ps_mode;
};
static void cw1200_pm_stay_awake_tmo(unsigned long arg)
{
/* XXX what's the point of this ? */
}
int cw1200_pm_init(struct cw1200_pm_state *pm,
struct cw1200_common *priv)
{
spin_lock_init(&pm->lock);
init_timer(&pm->stay_awake);
pm->stay_awake.data = (unsigned long)pm;
pm->stay_awake.function = cw1200_pm_stay_awake_tmo;
return 0;
}
void cw1200_pm_deinit(struct cw1200_pm_state *pm)
{
del_timer_sync(&pm->stay_awake);
}
void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
unsigned long tmo)
{
long cur_tmo;
spin_lock_bh(&pm->lock);
cur_tmo = pm->stay_awake.expires - jiffies;
if (!timer_pending(&pm->stay_awake) || cur_tmo < (long)tmo)
mod_timer(&pm->stay_awake, jiffies + tmo);
spin_unlock_bh(&pm->lock);
}
static long cw1200_suspend_work(struct delayed_work *work)
{
int ret = cancel_delayed_work(work);
long tmo;
if (ret > 0) {
/* Timer is pending */
tmo = work->timer.expires - jiffies;
if (tmo < 0)
tmo = 0;
} else {
tmo = -1;
}
return tmo;
}
static int cw1200_resume_work(struct cw1200_common *priv,
struct delayed_work *work,
unsigned long tmo)
{
if ((long)tmo < 0)
return 1;
return queue_delayed_work(priv->workqueue, work, tmo);
}
int cw1200_can_suspend(struct cw1200_common *priv)
{
if (atomic_read(&priv->bh_rx)) {
wiphy_dbg(priv->hw->wiphy, "Suspend interrupted.\n");
return 0;
}
return 1;
}
EXPORT_SYMBOL_GPL(cw1200_can_suspend);
int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
{
struct cw1200_common *priv = hw->priv;
struct cw1200_pm_state *pm_state = &priv->pm_state;
struct cw1200_suspend_state *state;
int ret;
spin_lock_bh(&pm_state->lock);
ret = timer_pending(&pm_state->stay_awake);
spin_unlock_bh(&pm_state->lock);
if (ret)
return -EAGAIN;
/* Do not suspend when datapath is not idle */
if (priv->tx_queue_stats.num_queued)
return -EBUSY;
/* Make sure there is no configuration requests in progress. */
if (!mutex_trylock(&priv->conf_mutex))
return -EBUSY;
/* Ensure pending operations are done.
* Note also that wow_suspend must return in ~2.5sec, before
* watchdog is triggered.
*/
if (priv->channel_switch_in_progress)
goto revert1;
/* Do not suspend when join is pending */
if (priv->join_pending)
goto revert1;
/* Do not suspend when scanning */
if (down_trylock(&priv->scan.lock))
goto revert1;
/* Lock TX. */
wsm_lock_tx_async(priv);
/* Wait to avoid possible race with bh code.
* But do not wait too long...
*/
if (wait_event_timeout(priv->bh_evt_wq,
!priv->hw_bufs_used, HZ / 10) <= 0)
goto revert2;
/* Set UDP filter */
wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_on.hdr);
/* Set ethernet frame type filter */
wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_on.hdr);
/* Allocate state */
state = kzalloc(sizeof(struct cw1200_suspend_state), GFP_KERNEL);
if (!state)
goto revert3;
/* Change to legacy PS while going to suspend */
if (!priv->vif->p2p &&
priv->join_status == CW1200_JOIN_STATUS_STA &&
priv->powersave_mode.mode != WSM_PSM_PS) {
state->prev_ps_mode = priv->powersave_mode.mode;
priv->powersave_mode.mode = WSM_PSM_PS;
cw1200_set_pm(priv, &priv->powersave_mode);
if (wait_event_interruptible_timeout(priv->ps_mode_switch_done,
!priv->ps_mode_switch_in_progress, 1*HZ) <= 0) {
goto revert3;
}
}
/* Store delayed work states. */
state->bss_loss_tmo =
cw1200_suspend_work(&priv->bss_loss_work);
state->join_tmo =
cw1200_suspend_work(&priv->join_timeout);
state->direct_probe =
cw1200_suspend_work(&priv->scan.probe_work);
state->link_id_gc =
cw1200_suspend_work(&priv->link_id_gc_work);
cancel_delayed_work_sync(&priv->clear_recent_scan_work);
atomic_set(&priv->recent_scan, 0);
/* Enable beacon skipping */
if (priv->join_status == CW1200_JOIN_STATUS_STA &&
priv->join_dtim_period &&
!priv->has_multicast_subscription) {
state->beacon_skipping = true;
wsm_set_beacon_wakeup_period(priv,
priv->join_dtim_period,
CW1200_BEACON_SKIPPING_MULTIPLIER * priv->join_dtim_period);
}
/* Stop serving thread */
if (cw1200_bh_suspend(priv))
goto revert4;
ret = timer_pending(&priv->mcast_timeout);
if (ret)
goto revert5;
/* Store suspend state */
pm_state->suspend_state = state;
/* Enable IRQ wake */
ret = priv->sbus_ops->power_mgmt(priv->sbus_priv, true);
if (ret) {
wiphy_err(priv->hw->wiphy,
"PM request failed: %d. WoW is disabled.\n", ret);
cw1200_wow_resume(hw);
return -EBUSY;
}
/* Force resume if event is coming from the device. */
if (atomic_read(&priv->bh_rx)) {
cw1200_wow_resume(hw);
return -EAGAIN;
}
return 0;
revert5:
WARN_ON(cw1200_bh_resume(priv));
revert4:
cw1200_resume_work(priv, &priv->bss_loss_work,
state->bss_loss_tmo);
cw1200_resume_work(priv, &priv->join_timeout,
state->join_tmo);
cw1200_resume_work(priv, &priv->scan.probe_work,
state->direct_probe);
cw1200_resume_work(priv, &priv->link_id_gc_work,
state->link_id_gc);
kfree(state);
revert3:
wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off);
wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off);
revert2:
wsm_unlock_tx(priv);
up(&priv->scan.lock);
revert1:
mutex_unlock(&priv->conf_mutex);
return -EBUSY;
}
int cw1200_wow_resume(struct ieee80211_hw *hw)
{
struct cw1200_common *priv = hw->priv;
struct cw1200_pm_state *pm_state = &priv->pm_state;
struct cw1200_suspend_state *state;
state = pm_state->suspend_state;
pm_state->suspend_state = NULL;
/* Disable IRQ wake */
priv->sbus_ops->power_mgmt(priv->sbus_priv, false);
/* Scan.lock must be released before BH is resumed other way
* in case when BSS_LOST command arrived the processing of the
* command will be delayed.
*/
up(&priv->scan.lock);
/* Resume BH thread */
WARN_ON(cw1200_bh_resume(priv));
/* Restores previous PS mode */
if (!priv->vif->p2p && priv->join_status == CW1200_JOIN_STATUS_STA) {
priv->powersave_mode.mode = state->prev_ps_mode;
cw1200_set_pm(priv, &priv->powersave_mode);
}
if (state->beacon_skipping) {
wsm_set_beacon_wakeup_period(priv, priv->beacon_int *
priv->join_dtim_period >
MAX_BEACON_SKIP_TIME_MS ? 1 :
priv->join_dtim_period, 0);
state->beacon_skipping = false;
}
/* Resume delayed work */
cw1200_resume_work(priv, &priv->bss_loss_work,
state->bss_loss_tmo);
cw1200_resume_work(priv, &priv->join_timeout,
state->join_tmo);
cw1200_resume_work(priv, &priv->scan.probe_work,
state->direct_probe);
cw1200_resume_work(priv, &priv->link_id_gc_work,
state->link_id_gc);
/* Remove UDP port filter */
wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off);
/* Remove ethernet frame type filter */
wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off);
/* Unlock datapath */
wsm_unlock_tx(priv);
/* Unlock configuration mutex */
mutex_unlock(&priv->conf_mutex);
/* Free memory */
kfree(state);
return 0;
}

View File

@ -0,0 +1,38 @@
/*
* Mac80211 power management interface for ST-Ericsson CW1200 mac80211 drivers
*
* Copyright (c) 2011, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef PM_H_INCLUDED
#define PM_H_INCLUDED
/* ******************************************************************** */
/* mac80211 API */
/* extern */ struct cw1200_common;
/* private */ struct cw1200_suspend_state;
struct cw1200_pm_state {
struct cw1200_suspend_state *suspend_state;
struct timer_list stay_awake;
struct platform_device *pm_dev;
spinlock_t lock; /* Protect access */
};
int cw1200_pm_init(struct cw1200_pm_state *pm,
struct cw1200_common *priv);
void cw1200_pm_deinit(struct cw1200_pm_state *pm);
void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
unsigned long tmo);
int cw1200_wow_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan);
int cw1200_wow_resume(struct ieee80211_hw *hw);
int cw1200_can_suspend(struct cw1200_common *priv);
#endif

View File

@ -0,0 +1,583 @@
/*
* O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <net/mac80211.h>
#include <linux/sched.h>
#include "queue.h"
#include "cw1200.h"
#include "debug.h"
/* private */ struct cw1200_queue_item
{
struct list_head head;
struct sk_buff *skb;
u32 packet_id;
unsigned long queue_timestamp;
unsigned long xmit_timestamp;
struct cw1200_txpriv txpriv;
u8 generation;
};
static inline void __cw1200_queue_lock(struct cw1200_queue *queue)
{
struct cw1200_queue_stats *stats = queue->stats;
if (queue->tx_locked_cnt++ == 0) {
pr_debug("[TX] Queue %d is locked.\n",
queue->queue_id);
ieee80211_stop_queue(stats->priv->hw, queue->queue_id);
}
}
static inline void __cw1200_queue_unlock(struct cw1200_queue *queue)
{
struct cw1200_queue_stats *stats = queue->stats;
BUG_ON(!queue->tx_locked_cnt);
if (--queue->tx_locked_cnt == 0) {
pr_debug("[TX] Queue %d is unlocked.\n",
queue->queue_id);
ieee80211_wake_queue(stats->priv->hw, queue->queue_id);
}
}
static inline void cw1200_queue_parse_id(u32 packet_id, u8 *queue_generation,
u8 *queue_id, u8 *item_generation,
u8 *item_id)
{
*item_id = (packet_id >> 0) & 0xFF;
*item_generation = (packet_id >> 8) & 0xFF;
*queue_id = (packet_id >> 16) & 0xFF;
*queue_generation = (packet_id >> 24) & 0xFF;
}
static inline u32 cw1200_queue_mk_packet_id(u8 queue_generation, u8 queue_id,
u8 item_generation, u8 item_id)
{
return ((u32)item_id << 0) |
((u32)item_generation << 8) |
((u32)queue_id << 16) |
((u32)queue_generation << 24);
}
static void cw1200_queue_post_gc(struct cw1200_queue_stats *stats,
struct list_head *gc_list)
{
struct cw1200_queue_item *item, *tmp;
list_for_each_entry_safe(item, tmp, gc_list, head) {
list_del(&item->head);
stats->skb_dtor(stats->priv, item->skb, &item->txpriv);
kfree(item);
}
}
static void cw1200_queue_register_post_gc(struct list_head *gc_list,
struct cw1200_queue_item *item)
{
struct cw1200_queue_item *gc_item;
gc_item = kmalloc(sizeof(struct cw1200_queue_item),
GFP_ATOMIC);
BUG_ON(!gc_item);
memcpy(gc_item, item, sizeof(struct cw1200_queue_item));
list_add_tail(&gc_item->head, gc_list);
}
static void __cw1200_queue_gc(struct cw1200_queue *queue,
struct list_head *head,
bool unlock)
{
struct cw1200_queue_stats *stats = queue->stats;
struct cw1200_queue_item *item = NULL, *tmp;
bool wakeup_stats = false;
list_for_each_entry_safe(item, tmp, &queue->queue, head) {
if (jiffies - item->queue_timestamp < queue->ttl)
break;
--queue->num_queued;
--queue->link_map_cache[item->txpriv.link_id];
spin_lock_bh(&stats->lock);
--stats->num_queued;
if (!--stats->link_map_cache[item->txpriv.link_id])
wakeup_stats = true;
spin_unlock_bh(&stats->lock);
cw1200_debug_tx_ttl(stats->priv);
cw1200_queue_register_post_gc(head, item);
item->skb = NULL;
list_move_tail(&item->head, &queue->free_pool);
}
if (wakeup_stats)
wake_up(&stats->wait_link_id_empty);
if (queue->overfull) {
if (queue->num_queued <= (queue->capacity >> 1)) {
queue->overfull = false;
if (unlock)
__cw1200_queue_unlock(queue);
} else if (item) {
unsigned long tmo = item->queue_timestamp + queue->ttl;
mod_timer(&queue->gc, tmo);
cw1200_pm_stay_awake(&stats->priv->pm_state,
tmo - jiffies);
}
}
}
static void cw1200_queue_gc(unsigned long arg)
{
LIST_HEAD(list);
struct cw1200_queue *queue =
(struct cw1200_queue *)arg;
spin_lock_bh(&queue->lock);
__cw1200_queue_gc(queue, &list, true);
spin_unlock_bh(&queue->lock);
cw1200_queue_post_gc(queue->stats, &list);
}
int cw1200_queue_stats_init(struct cw1200_queue_stats *stats,
size_t map_capacity,
cw1200_queue_skb_dtor_t skb_dtor,
struct cw1200_common *priv)
{
memset(stats, 0, sizeof(*stats));
stats->map_capacity = map_capacity;
stats->skb_dtor = skb_dtor;
stats->priv = priv;
spin_lock_init(&stats->lock);
init_waitqueue_head(&stats->wait_link_id_empty);
stats->link_map_cache = kzalloc(sizeof(int) * map_capacity,
GFP_KERNEL);
if (!stats->link_map_cache)
return -ENOMEM;
return 0;
}
int cw1200_queue_init(struct cw1200_queue *queue,
struct cw1200_queue_stats *stats,
u8 queue_id,
size_t capacity,
unsigned long ttl)
{
size_t i;
memset(queue, 0, sizeof(*queue));
queue->stats = stats;
queue->capacity = capacity;
queue->queue_id = queue_id;
queue->ttl = ttl;
INIT_LIST_HEAD(&queue->queue);
INIT_LIST_HEAD(&queue->pending);
INIT_LIST_HEAD(&queue->free_pool);
spin_lock_init(&queue->lock);
init_timer(&queue->gc);
queue->gc.data = (unsigned long)queue;
queue->gc.function = cw1200_queue_gc;
queue->pool = kzalloc(sizeof(struct cw1200_queue_item) * capacity,
GFP_KERNEL);
if (!queue->pool)
return -ENOMEM;
queue->link_map_cache = kzalloc(sizeof(int) * stats->map_capacity,
GFP_KERNEL);
if (!queue->link_map_cache) {
kfree(queue->pool);
queue->pool = NULL;
return -ENOMEM;
}
for (i = 0; i < capacity; ++i)
list_add_tail(&queue->pool[i].head, &queue->free_pool);
return 0;
}
int cw1200_queue_clear(struct cw1200_queue *queue)
{
int i;
LIST_HEAD(gc_list);
struct cw1200_queue_stats *stats = queue->stats;
struct cw1200_queue_item *item, *tmp;
spin_lock_bh(&queue->lock);
queue->generation++;
list_splice_tail_init(&queue->queue, &queue->pending);
list_for_each_entry_safe(item, tmp, &queue->pending, head) {
WARN_ON(!item->skb);
cw1200_queue_register_post_gc(&gc_list, item);
item->skb = NULL;
list_move_tail(&item->head, &queue->free_pool);
}
queue->num_queued = 0;
queue->num_pending = 0;
spin_lock_bh(&stats->lock);
for (i = 0; i < stats->map_capacity; ++i) {
stats->num_queued -= queue->link_map_cache[i];
stats->link_map_cache[i] -= queue->link_map_cache[i];
queue->link_map_cache[i] = 0;
}
spin_unlock_bh(&stats->lock);
if (queue->overfull) {
queue->overfull = false;
__cw1200_queue_unlock(queue);
}
spin_unlock_bh(&queue->lock);
wake_up(&stats->wait_link_id_empty);
cw1200_queue_post_gc(stats, &gc_list);
return 0;
}
void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats)
{
kfree(stats->link_map_cache);
stats->link_map_cache = NULL;
}
void cw1200_queue_deinit(struct cw1200_queue *queue)
{
cw1200_queue_clear(queue);
del_timer_sync(&queue->gc);
INIT_LIST_HEAD(&queue->free_pool);
kfree(queue->pool);
kfree(queue->link_map_cache);
queue->pool = NULL;
queue->link_map_cache = NULL;
queue->capacity = 0;
}
size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue,
u32 link_id_map)
{
size_t ret;
int i, bit;
size_t map_capacity = queue->stats->map_capacity;
if (!link_id_map)
return 0;
spin_lock_bh(&queue->lock);
if (link_id_map == (u32)-1) {
ret = queue->num_queued - queue->num_pending;
} else {
ret = 0;
for (i = 0, bit = 1; i < map_capacity; ++i, bit <<= 1) {
if (link_id_map & bit)
ret += queue->link_map_cache[i];
}
}
spin_unlock_bh(&queue->lock);
return ret;
}
int cw1200_queue_put(struct cw1200_queue *queue,
struct sk_buff *skb,
struct cw1200_txpriv *txpriv)
{
int ret = 0;
LIST_HEAD(gc_list);
struct cw1200_queue_stats *stats = queue->stats;
if (txpriv->link_id >= queue->stats->map_capacity)
return -EINVAL;
spin_lock_bh(&queue->lock);
if (!WARN_ON(list_empty(&queue->free_pool))) {
struct cw1200_queue_item *item = list_first_entry(
&queue->free_pool, struct cw1200_queue_item, head);
BUG_ON(item->skb);
list_move_tail(&item->head, &queue->queue);
item->skb = skb;
item->txpriv = *txpriv;
item->generation = 0;
item->packet_id = cw1200_queue_mk_packet_id(queue->generation,
queue->queue_id,
item->generation,
item - queue->pool);
item->queue_timestamp = jiffies;
++queue->num_queued;
++queue->link_map_cache[txpriv->link_id];
spin_lock_bh(&stats->lock);
++stats->num_queued;
++stats->link_map_cache[txpriv->link_id];
spin_unlock_bh(&stats->lock);
/* TX may happen in parallel sometimes.
* Leave extra queue slots so we don't overflow.
*/
if (queue->overfull == false &&
queue->num_queued >=
(queue->capacity - (num_present_cpus() - 1))) {
queue->overfull = true;
__cw1200_queue_lock(queue);
mod_timer(&queue->gc, jiffies);
}
} else {
ret = -ENOENT;
}
spin_unlock_bh(&queue->lock);
return ret;
}
int cw1200_queue_get(struct cw1200_queue *queue,
u32 link_id_map,
struct wsm_tx **tx,
struct ieee80211_tx_info **tx_info,
const struct cw1200_txpriv **txpriv)
{
int ret = -ENOENT;
struct cw1200_queue_item *item;
struct cw1200_queue_stats *stats = queue->stats;
bool wakeup_stats = false;
spin_lock_bh(&queue->lock);
list_for_each_entry(item, &queue->queue, head) {
if (link_id_map & BIT(item->txpriv.link_id)) {
ret = 0;
break;
}
}
if (!WARN_ON(ret)) {
*tx = (struct wsm_tx *)item->skb->data;
*tx_info = IEEE80211_SKB_CB(item->skb);
*txpriv = &item->txpriv;
(*tx)->packet_id = __cpu_to_le32(item->packet_id);
list_move_tail(&item->head, &queue->pending);
++queue->num_pending;
--queue->link_map_cache[item->txpriv.link_id];
item->xmit_timestamp = jiffies;
spin_lock_bh(&stats->lock);
--stats->num_queued;
if (!--stats->link_map_cache[item->txpriv.link_id])
wakeup_stats = true;
spin_unlock_bh(&stats->lock);
}
spin_unlock_bh(&queue->lock);
if (wakeup_stats)
wake_up(&stats->wait_link_id_empty);
return ret;
}
int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id)
{
int ret = 0;
u8 queue_generation, queue_id, item_generation, item_id;
struct cw1200_queue_item *item;
struct cw1200_queue_stats *stats = queue->stats;
cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
&item_generation, &item_id);
item = &queue->pool[item_id];
spin_lock_bh(&queue->lock);
BUG_ON(queue_id != queue->queue_id);
if (queue_generation != queue->generation) {
ret = -ENOENT;
} else if (item_id >= (unsigned) queue->capacity) {
WARN_ON(1);
ret = -EINVAL;
} else if (item->generation != item_generation) {
WARN_ON(1);
ret = -ENOENT;
} else {
--queue->num_pending;
++queue->link_map_cache[item->txpriv.link_id];
spin_lock_bh(&stats->lock);
++stats->num_queued;
++stats->link_map_cache[item->txpriv.link_id];
spin_unlock_bh(&stats->lock);
item->generation = ++item_generation;
item->packet_id = cw1200_queue_mk_packet_id(queue_generation,
queue_id,
item_generation,
item_id);
list_move(&item->head, &queue->queue);
}
spin_unlock_bh(&queue->lock);
return ret;
}
int cw1200_queue_requeue_all(struct cw1200_queue *queue)
{
struct cw1200_queue_item *item, *tmp;
struct cw1200_queue_stats *stats = queue->stats;
spin_lock_bh(&queue->lock);
list_for_each_entry_safe_reverse(item, tmp, &queue->pending, head) {
--queue->num_pending;
++queue->link_map_cache[item->txpriv.link_id];
spin_lock_bh(&stats->lock);
++stats->num_queued;
++stats->link_map_cache[item->txpriv.link_id];
spin_unlock_bh(&stats->lock);
++item->generation;
item->packet_id = cw1200_queue_mk_packet_id(queue->generation,
queue->queue_id,
item->generation,
item - queue->pool);
list_move(&item->head, &queue->queue);
}
spin_unlock_bh(&queue->lock);
return 0;
}
int cw1200_queue_remove(struct cw1200_queue *queue, u32 packet_id)
{
int ret = 0;
u8 queue_generation, queue_id, item_generation, item_id;
struct cw1200_queue_item *item;
struct cw1200_queue_stats *stats = queue->stats;
struct sk_buff *gc_skb = NULL;
struct cw1200_txpriv gc_txpriv;
cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
&item_generation, &item_id);
item = &queue->pool[item_id];
spin_lock_bh(&queue->lock);
BUG_ON(queue_id != queue->queue_id);
if (queue_generation != queue->generation) {
ret = -ENOENT;
} else if (item_id >= (unsigned) queue->capacity) {
WARN_ON(1);
ret = -EINVAL;
} else if (item->generation != item_generation) {
WARN_ON(1);
ret = -ENOENT;
} else {
gc_txpriv = item->txpriv;
gc_skb = item->skb;
item->skb = NULL;
--queue->num_pending;
--queue->num_queued;
++queue->num_sent;
++item->generation;
/* Do not use list_move_tail here, but list_move:
* try to utilize cache row.
*/
list_move(&item->head, &queue->free_pool);
if (queue->overfull &&
(queue->num_queued <= (queue->capacity >> 1))) {
queue->overfull = false;
__cw1200_queue_unlock(queue);
}
}
spin_unlock_bh(&queue->lock);
if (gc_skb)
stats->skb_dtor(stats->priv, gc_skb, &gc_txpriv);
return ret;
}
int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id,
struct sk_buff **skb,
const struct cw1200_txpriv **txpriv)
{
int ret = 0;
u8 queue_generation, queue_id, item_generation, item_id;
struct cw1200_queue_item *item;
cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id,
&item_generation, &item_id);
item = &queue->pool[item_id];
spin_lock_bh(&queue->lock);
BUG_ON(queue_id != queue->queue_id);
if (queue_generation != queue->generation) {
ret = -ENOENT;
} else if (item_id >= (unsigned) queue->capacity) {
WARN_ON(1);
ret = -EINVAL;
} else if (item->generation != item_generation) {
WARN_ON(1);
ret = -ENOENT;
} else {
*skb = item->skb;
*txpriv = &item->txpriv;
}
spin_unlock_bh(&queue->lock);
return ret;
}
void cw1200_queue_lock(struct cw1200_queue *queue)
{
spin_lock_bh(&queue->lock);
__cw1200_queue_lock(queue);
spin_unlock_bh(&queue->lock);
}
void cw1200_queue_unlock(struct cw1200_queue *queue)
{
spin_lock_bh(&queue->lock);
__cw1200_queue_unlock(queue);
spin_unlock_bh(&queue->lock);
}
bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue,
unsigned long *timestamp,
u32 pending_frame_id)
{
struct cw1200_queue_item *item;
bool ret;
spin_lock_bh(&queue->lock);
ret = !list_empty(&queue->pending);
if (ret) {
list_for_each_entry(item, &queue->pending, head) {
if (item->packet_id != pending_frame_id)
if (time_before(item->xmit_timestamp,
*timestamp))
*timestamp = item->xmit_timestamp;
}
}
spin_unlock_bh(&queue->lock);
return ret;
}
bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats,
u32 link_id_map)
{
bool empty = true;
spin_lock_bh(&stats->lock);
if (link_id_map == (u32)-1) {
empty = stats->num_queued == 0;
} else {
int i;
for (i = 0; i < stats->map_capacity; ++i) {
if (link_id_map & BIT(i)) {
if (stats->link_map_cache[i]) {
empty = false;
break;
}
}
}
}
spin_unlock_bh(&stats->lock);
return empty;
}

View File

@ -0,0 +1,116 @@
/*
* O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef CW1200_QUEUE_H_INCLUDED
#define CW1200_QUEUE_H_INCLUDED
/* private */ struct cw1200_queue_item;
/* extern */ struct sk_buff;
/* extern */ struct wsm_tx;
/* extern */ struct cw1200_common;
/* extern */ struct ieee80211_tx_queue_stats;
/* extern */ struct cw1200_txpriv;
/* forward */ struct cw1200_queue_stats;
typedef void (*cw1200_queue_skb_dtor_t)(struct cw1200_common *priv,
struct sk_buff *skb,
const struct cw1200_txpriv *txpriv);
struct cw1200_queue {
struct cw1200_queue_stats *stats;
size_t capacity;
size_t num_queued;
size_t num_pending;
size_t num_sent;
struct cw1200_queue_item *pool;
struct list_head queue;
struct list_head free_pool;
struct list_head pending;
int tx_locked_cnt;
int *link_map_cache;
bool overfull;
spinlock_t lock; /* Protect queue entry */
u8 queue_id;
u8 generation;
struct timer_list gc;
unsigned long ttl;
};
struct cw1200_queue_stats {
spinlock_t lock; /* Protect stats entry */
int *link_map_cache;
int num_queued;
size_t map_capacity;
wait_queue_head_t wait_link_id_empty;
cw1200_queue_skb_dtor_t skb_dtor;
struct cw1200_common *priv;
};
struct cw1200_txpriv {
u8 link_id;
u8 raw_link_id;
u8 tid;
u8 rate_id;
u8 offset;
};
int cw1200_queue_stats_init(struct cw1200_queue_stats *stats,
size_t map_capacity,
cw1200_queue_skb_dtor_t skb_dtor,
struct cw1200_common *priv);
int cw1200_queue_init(struct cw1200_queue *queue,
struct cw1200_queue_stats *stats,
u8 queue_id,
size_t capacity,
unsigned long ttl);
int cw1200_queue_clear(struct cw1200_queue *queue);
void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats);
void cw1200_queue_deinit(struct cw1200_queue *queue);
size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue,
u32 link_id_map);
int cw1200_queue_put(struct cw1200_queue *queue,
struct sk_buff *skb,
struct cw1200_txpriv *txpriv);
int cw1200_queue_get(struct cw1200_queue *queue,
u32 link_id_map,
struct wsm_tx **tx,
struct ieee80211_tx_info **tx_info,
const struct cw1200_txpriv **txpriv);
int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id);
int cw1200_queue_requeue_all(struct cw1200_queue *queue);
int cw1200_queue_remove(struct cw1200_queue *queue,
u32 packet_id);
int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id,
struct sk_buff **skb,
const struct cw1200_txpriv **txpriv);
void cw1200_queue_lock(struct cw1200_queue *queue);
void cw1200_queue_unlock(struct cw1200_queue *queue);
bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue,
unsigned long *timestamp,
u32 pending_frame_id);
bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats,
u32 link_id_map);
static inline u8 cw1200_queue_get_queue_id(u32 packet_id)
{
return (packet_id >> 16) & 0xFF;
}
static inline u8 cw1200_queue_get_generation(u32 packet_id)
{
return (packet_id >> 8) & 0xFF;
}
#endif /* CW1200_QUEUE_H_INCLUDED */

View File

@ -0,0 +1,37 @@
/*
* Common sbus abstraction layer interface for cw1200 wireless driver
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef CW1200_SBUS_H
#define CW1200_SBUS_H
/*
* sbus priv forward definition.
* Implemented and instantiated in particular modules.
*/
struct sbus_priv;
void cw1200_irq_handler(struct cw1200_common *priv);
/* This MUST be wrapped with sbus_ops->lock/unlock! */
int __cw1200_irq_enable(struct cw1200_common *priv, int enable);
struct sbus_ops {
int (*sbus_memcpy_fromio)(struct sbus_priv *self, unsigned int addr,
void *dst, int count);
int (*sbus_memcpy_toio)(struct sbus_priv *self, unsigned int addr,
const void *src, int count);
void (*lock)(struct sbus_priv *self);
void (*unlock)(struct sbus_priv *self);
size_t (*align_size)(struct sbus_priv *self, size_t size);
int (*power_mgmt)(struct sbus_priv *self, bool suspend);
};
#endif /* CW1200_SBUS_H */

View File

@ -0,0 +1,461 @@
/*
* Scan implementation for ST-Ericsson CW1200 mac80211 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/sched.h>
#include "cw1200.h"
#include "scan.h"
#include "sta.h"
#include "pm.h"
static void cw1200_scan_restart_delayed(struct cw1200_common *priv);
static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan)
{
int ret, i;
int tmo = 2000;
switch (priv->join_status) {
case CW1200_JOIN_STATUS_PRE_STA:
case CW1200_JOIN_STATUS_JOINING:
return -EBUSY;
default:
break;
}
wiphy_dbg(priv->hw->wiphy, "[SCAN] hw req, type %d, %d channels, flags: 0x%x.\n",
scan->type, scan->num_channels, scan->flags);
for (i = 0; i < scan->num_channels; ++i)
tmo += scan->ch[i].max_chan_time + 10;
cancel_delayed_work_sync(&priv->clear_recent_scan_work);
atomic_set(&priv->scan.in_progress, 1);
atomic_set(&priv->recent_scan, 1);
cw1200_pm_stay_awake(&priv->pm_state, tmo * HZ / 1000);
queue_delayed_work(priv->workqueue, &priv->scan.timeout,
tmo * HZ / 1000);
ret = wsm_scan(priv, scan);
if (ret) {
atomic_set(&priv->scan.in_progress, 0);
cancel_delayed_work_sync(&priv->scan.timeout);
cw1200_scan_restart_delayed(priv);
}
return ret;
}
int cw1200_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_scan_request *req)
{
struct cw1200_common *priv = hw->priv;
struct wsm_template_frame frame = {
.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
};
int i, ret;
if (!priv->vif)
return -EINVAL;
/* Scan when P2P_GO corrupt firmware MiniAP mode */
if (priv->join_status == CW1200_JOIN_STATUS_AP)
return -EOPNOTSUPP;
if (req->n_ssids == 1 && !req->ssids[0].ssid_len)
req->n_ssids = 0;
wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n",
req->n_ssids);
if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS)
return -EINVAL;
frame.skb = ieee80211_probereq_get(hw, priv->vif, NULL, 0,
req->ie_len);
if (!frame.skb)
return -ENOMEM;
if (req->ie_len)
memcpy(skb_put(frame.skb, req->ie_len), req->ie, req->ie_len);
/* will be unlocked in cw1200_scan_work() */
down(&priv->scan.lock);
mutex_lock(&priv->conf_mutex);
ret = wsm_set_template_frame(priv, &frame);
if (!ret) {
/* Host want to be the probe responder. */
ret = wsm_set_probe_responder(priv, true);
}
if (ret) {
mutex_unlock(&priv->conf_mutex);
up(&priv->scan.lock);
dev_kfree_skb(frame.skb);
return ret;
}
wsm_lock_tx(priv);
BUG_ON(priv->scan.req);
priv->scan.req = req;
priv->scan.n_ssids = 0;
priv->scan.status = 0;
priv->scan.begin = &req->channels[0];
priv->scan.curr = priv->scan.begin;
priv->scan.end = &req->channels[req->n_channels];
priv->scan.output_power = priv->output_power;
for (i = 0; i < req->n_ssids; ++i) {
struct wsm_ssid *dst = &priv->scan.ssids[priv->scan.n_ssids];
memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid));
dst->length = req->ssids[i].ssid_len;
++priv->scan.n_ssids;
}
mutex_unlock(&priv->conf_mutex);
if (frame.skb)
dev_kfree_skb(frame.skb);
queue_work(priv->workqueue, &priv->scan.work);
return 0;
}
void cw1200_scan_work(struct work_struct *work)
{
struct cw1200_common *priv = container_of(work, struct cw1200_common,
scan.work);
struct ieee80211_channel **it;
struct wsm_scan scan = {
.type = WSM_SCAN_TYPE_FOREGROUND,
.flags = WSM_SCAN_FLAG_SPLIT_METHOD,
};
bool first_run = (priv->scan.begin == priv->scan.curr &&
priv->scan.begin != priv->scan.end);
int i;
if (first_run) {
/* Firmware gets crazy if scan request is sent
* when STA is joined but not yet associated.
* Force unjoin in this case.
*/
if (cancel_delayed_work_sync(&priv->join_timeout) > 0)
cw1200_join_timeout(&priv->join_timeout.work);
}
mutex_lock(&priv->conf_mutex);
if (first_run) {
if (priv->join_status == CW1200_JOIN_STATUS_STA &&
!(priv->powersave_mode.mode & WSM_PSM_PS)) {
struct wsm_set_pm pm = priv->powersave_mode;
pm.mode = WSM_PSM_PS;
cw1200_set_pm(priv, &pm);
} else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
/* FW bug: driver has to restart p2p-dev mode
* after scan
*/
cw1200_disable_listening(priv);
}
}
if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) {
if (priv->scan.output_power != priv->output_power)
wsm_set_output_power(priv, priv->output_power * 10);
if (priv->join_status == CW1200_JOIN_STATUS_STA &&
!(priv->powersave_mode.mode & WSM_PSM_PS))
cw1200_set_pm(priv, &priv->powersave_mode);
if (priv->scan.status < 0)
wiphy_dbg(priv->hw->wiphy, "[SCAN] Scan failed (%d).\n",
priv->scan.status);
else if (priv->scan.req)
wiphy_dbg(priv->hw->wiphy,
"[SCAN] Scan completed.\n");
else
wiphy_dbg(priv->hw->wiphy,
"[SCAN] Scan canceled.\n");
priv->scan.req = NULL;
cw1200_scan_restart_delayed(priv);
wsm_unlock_tx(priv);
mutex_unlock(&priv->conf_mutex);
ieee80211_scan_completed(priv->hw, priv->scan.status ? 1 : 0);
up(&priv->scan.lock);
return;
} else {
struct ieee80211_channel *first = *priv->scan.curr;
for (it = priv->scan.curr + 1, i = 1;
it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS;
++it, ++i) {
if ((*it)->band != first->band)
break;
if (((*it)->flags ^ first->flags) &
IEEE80211_CHAN_PASSIVE_SCAN)
break;
if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
(*it)->max_power != first->max_power)
break;
}
scan.band = first->band;
if (priv->scan.req->no_cck)
scan.max_tx_rate = WSM_TRANSMIT_RATE_6;
else
scan.max_tx_rate = WSM_TRANSMIT_RATE_1;
scan.num_probes =
(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) ? 0 : 2;
scan.num_ssids = priv->scan.n_ssids;
scan.ssids = &priv->scan.ssids[0];
scan.num_channels = it - priv->scan.curr;
/* TODO: Is it optimal? */
scan.probe_delay = 100;
/* It is not stated in WSM specification, however
* FW team says that driver may not use FG scan
* when joined.
*/
if (priv->join_status == CW1200_JOIN_STATUS_STA) {
scan.type = WSM_SCAN_TYPE_BACKGROUND;
scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
}
scan.ch = kzalloc(
sizeof(struct wsm_scan_ch) * (it - priv->scan.curr),
GFP_KERNEL);
if (!scan.ch) {
priv->scan.status = -ENOMEM;
goto fail;
}
for (i = 0; i < scan.num_channels; ++i) {
scan.ch[i].number = priv->scan.curr[i]->hw_value;
if (priv->scan.curr[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN) {
scan.ch[i].min_chan_time = 50;
scan.ch[i].max_chan_time = 100;
} else {
scan.ch[i].min_chan_time = 10;
scan.ch[i].max_chan_time = 25;
}
}
if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
priv->scan.output_power != first->max_power) {
priv->scan.output_power = first->max_power;
wsm_set_output_power(priv,
priv->scan.output_power * 10);
}
priv->scan.status = cw1200_scan_start(priv, &scan);
kfree(scan.ch);
if (priv->scan.status)
goto fail;
priv->scan.curr = it;
}
mutex_unlock(&priv->conf_mutex);
return;
fail:
priv->scan.curr = priv->scan.end;
mutex_unlock(&priv->conf_mutex);
queue_work(priv->workqueue, &priv->scan.work);
return;
}
static void cw1200_scan_restart_delayed(struct cw1200_common *priv)
{
/* FW bug: driver has to restart p2p-dev mode after scan. */
if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
cw1200_enable_listening(priv);
cw1200_update_filtering(priv);
}
if (priv->delayed_unjoin) {
priv->delayed_unjoin = false;
if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
wsm_unlock_tx(priv);
} else if (priv->delayed_link_loss) {
wiphy_dbg(priv->hw->wiphy, "[CQM] Requeue BSS loss.\n");
priv->delayed_link_loss = 0;
cw1200_cqm_bssloss_sm(priv, 1, 0, 0);
}
}
static void cw1200_scan_complete(struct cw1200_common *priv)
{
queue_delayed_work(priv->workqueue, &priv->clear_recent_scan_work, HZ);
if (priv->scan.direct_probe) {
wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe complete.\n");
cw1200_scan_restart_delayed(priv);
priv->scan.direct_probe = 0;
up(&priv->scan.lock);
wsm_unlock_tx(priv);
} else {
cw1200_scan_work(&priv->scan.work);
}
}
void cw1200_scan_failed_cb(struct cw1200_common *priv)
{
if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
/* STA is stopped. */
return;
if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) {
priv->scan.status = -EIO;
queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0);
}
}
void cw1200_scan_complete_cb(struct cw1200_common *priv,
struct wsm_scan_complete *arg)
{
if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
/* STA is stopped. */
return;
if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) {
priv->scan.status = 1;
queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0);
}
}
void cw1200_clear_recent_scan_work(struct work_struct *work)
{
struct cw1200_common *priv =
container_of(work, struct cw1200_common,
clear_recent_scan_work.work);
atomic_xchg(&priv->recent_scan, 0);
}
void cw1200_scan_timeout(struct work_struct *work)
{
struct cw1200_common *priv =
container_of(work, struct cw1200_common, scan.timeout.work);
if (atomic_xchg(&priv->scan.in_progress, 0)) {
if (priv->scan.status > 0) {
priv->scan.status = 0;
} else if (!priv->scan.status) {
wiphy_warn(priv->hw->wiphy,
"Timeout waiting for scan complete notification.\n");
priv->scan.status = -ETIMEDOUT;
priv->scan.curr = priv->scan.end;
wsm_stop_scan(priv);
}
cw1200_scan_complete(priv);
}
}
void cw1200_probe_work(struct work_struct *work)
{
struct cw1200_common *priv =
container_of(work, struct cw1200_common, scan.probe_work.work);
u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id);
struct cw1200_queue *queue = &priv->tx_queue[queue_id];
const struct cw1200_txpriv *txpriv;
struct wsm_tx *wsm;
struct wsm_template_frame frame = {
.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
};
struct wsm_ssid ssids[1] = {{
.length = 0,
} };
struct wsm_scan_ch ch[1] = {{
.min_chan_time = 0,
.max_chan_time = 10,
} };
struct wsm_scan scan = {
.type = WSM_SCAN_TYPE_FOREGROUND,
.num_probes = 1,
.probe_delay = 0,
.num_channels = 1,
.ssids = ssids,
.ch = ch,
};
u8 *ies;
size_t ies_len;
int ret;
wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe work.\n");
mutex_lock(&priv->conf_mutex);
if (down_trylock(&priv->scan.lock)) {
/* Scan is already in progress. Requeue self. */
schedule();
queue_delayed_work(priv->workqueue,
&priv->scan.probe_work, HZ / 10);
mutex_unlock(&priv->conf_mutex);
return;
}
/* Make sure we still have a pending probe req */
if (cw1200_queue_get_skb(queue, priv->pending_frame_id,
&frame.skb, &txpriv)) {
up(&priv->scan.lock);
mutex_unlock(&priv->conf_mutex);
wsm_unlock_tx(priv);
return;
}
wsm = (struct wsm_tx *)frame.skb->data;
scan.max_tx_rate = wsm->max_tx_rate;
scan.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
if (priv->join_status == CW1200_JOIN_STATUS_STA ||
priv->join_status == CW1200_JOIN_STATUS_IBSS) {
scan.type = WSM_SCAN_TYPE_BACKGROUND;
scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
}
ch[0].number = priv->channel->hw_value;
skb_pull(frame.skb, txpriv->offset);
ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)];
ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr);
if (ies_len) {
u8 *ssidie =
(u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len);
if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) {
u8 *nextie = &ssidie[2 + ssidie[1]];
/* Remove SSID from the IE list. It has to be provided
* as a separate argument in cw1200_scan_start call
*/
/* Store SSID localy */
ssids[0].length = ssidie[1];
memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length);
scan.num_ssids = 1;
/* Remove SSID from IE list */
ssidie[1] = 0;
memmove(&ssidie[2], nextie, &ies[ies_len] - nextie);
skb_trim(frame.skb, frame.skb->len - ssids[0].length);
}
}
/* FW bug: driver has to restart p2p-dev mode after scan */
if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
cw1200_disable_listening(priv);
ret = wsm_set_template_frame(priv, &frame);
priv->scan.direct_probe = 1;
if (!ret) {
wsm_flush_tx(priv);
ret = cw1200_scan_start(priv, &scan);
}
mutex_unlock(&priv->conf_mutex);
skb_push(frame.skb, txpriv->offset);
if (!ret)
IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK;
BUG_ON(cw1200_queue_remove(queue, priv->pending_frame_id));
if (ret) {
priv->scan.direct_probe = 0;
up(&priv->scan.lock);
wsm_unlock_tx(priv);
}
return;
}

View File

@ -0,0 +1,56 @@
/*
* Scan interface for ST-Ericsson CW1200 mac80211 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef SCAN_H_INCLUDED
#define SCAN_H_INCLUDED
#include <linux/semaphore.h>
#include "wsm.h"
/* external */ struct sk_buff;
/* external */ struct cfg80211_scan_request;
/* external */ struct ieee80211_channel;
/* external */ struct ieee80211_hw;
/* external */ struct work_struct;
struct cw1200_scan {
struct semaphore lock;
struct work_struct work;
struct delayed_work timeout;
struct cfg80211_scan_request *req;
struct ieee80211_channel **begin;
struct ieee80211_channel **curr;
struct ieee80211_channel **end;
struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS];
int output_power;
int n_ssids;
int status;
atomic_t in_progress;
/* Direct probe requests workaround */
struct delayed_work probe_work;
int direct_probe;
};
int cw1200_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_scan_request *req);
void cw1200_scan_work(struct work_struct *work);
void cw1200_scan_timeout(struct work_struct *work);
void cw1200_clear_recent_scan_work(struct work_struct *work);
void cw1200_scan_complete_cb(struct cw1200_common *priv,
struct wsm_scan_complete *arg);
void cw1200_scan_failed_cb(struct cw1200_common *priv);
/* ******************************************************************** */
/* Raw probe requests TX workaround */
void cw1200_probe_work(struct work_struct *work);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,123 @@
/*
* Mac80211 STA interface for ST-Ericsson CW1200 mac80211 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef STA_H_INCLUDED
#define STA_H_INCLUDED
/* ******************************************************************** */
/* mac80211 API */
int cw1200_start(struct ieee80211_hw *dev);
void cw1200_stop(struct ieee80211_hw *dev);
int cw1200_add_interface(struct ieee80211_hw *dev,
struct ieee80211_vif *vif);
void cw1200_remove_interface(struct ieee80211_hw *dev,
struct ieee80211_vif *vif);
int cw1200_change_interface(struct ieee80211_hw *dev,
struct ieee80211_vif *vif,
enum nl80211_iftype new_type,
bool p2p);
int cw1200_config(struct ieee80211_hw *dev, u32 changed);
void cw1200_configure_filter(struct ieee80211_hw *dev,
unsigned int changed_flags,
unsigned int *total_flags,
u64 multicast);
int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
u16 queue, const struct ieee80211_tx_queue_params *params);
int cw1200_get_stats(struct ieee80211_hw *dev,
struct ieee80211_low_level_stats *stats);
int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
struct ieee80211_vif *vif, struct ieee80211_sta *sta,
struct ieee80211_key_conf *key);
int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value);
void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop);
u64 cw1200_prepare_multicast(struct ieee80211_hw *hw,
struct netdev_hw_addr_list *mc_list);
int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg);
/* ******************************************************************** */
/* WSM callbacks */
void cw1200_join_complete_cb(struct cw1200_common *priv,
struct wsm_join_complete *arg);
/* ******************************************************************** */
/* WSM events */
void cw1200_free_event_queue(struct cw1200_common *priv);
void cw1200_event_handler(struct work_struct *work);
void cw1200_bss_loss_work(struct work_struct *work);
void cw1200_bss_params_work(struct work_struct *work);
void cw1200_keep_alive_work(struct work_struct *work);
void cw1200_tx_failure_work(struct work_struct *work);
void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv, int init, int good,
int bad);
static inline void cw1200_cqm_bssloss_sm(struct cw1200_common *priv,
int init, int good, int bad)
{
spin_lock(&priv->bss_loss_lock);
__cw1200_cqm_bssloss_sm(priv, init, good, bad);
spin_unlock(&priv->bss_loss_lock);
}
/* ******************************************************************** */
/* Internal API */
int cw1200_setup_mac(struct cw1200_common *priv);
void cw1200_join_timeout(struct work_struct *work);
void cw1200_unjoin_work(struct work_struct *work);
void cw1200_join_complete_work(struct work_struct *work);
void cw1200_wep_key_work(struct work_struct *work);
void cw1200_update_listening(struct cw1200_common *priv, bool enabled);
void cw1200_update_filtering(struct cw1200_common *priv);
void cw1200_update_filtering_work(struct work_struct *work);
void cw1200_set_beacon_wakeup_period_work(struct work_struct *work);
int cw1200_enable_listening(struct cw1200_common *priv);
int cw1200_disable_listening(struct cw1200_common *priv);
int cw1200_set_uapsd_param(struct cw1200_common *priv,
const struct wsm_edca_params *arg);
void cw1200_ba_work(struct work_struct *work);
void cw1200_ba_timer(unsigned long arg);
/* AP stuffs */
int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
bool set);
int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
enum sta_notify_cmd notify_cmd,
struct ieee80211_sta *sta);
void cw1200_bss_info_changed(struct ieee80211_hw *dev,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *info,
u32 changed);
int cw1200_ampdu_action(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
enum ieee80211_ampdu_mlme_action action,
struct ieee80211_sta *sta, u16 tid, u16 *ssn,
u8 buf_size);
void cw1200_suspend_resume(struct cw1200_common *priv,
struct wsm_suspend_resume *arg);
void cw1200_set_tim_work(struct work_struct *work);
void cw1200_set_cts_work(struct work_struct *work);
void cw1200_multicast_start_work(struct work_struct *work);
void cw1200_multicast_stop_work(struct work_struct *work);
void cw1200_mcast_timeout(unsigned long arg);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,106 @@
/*
* Datapath interface for ST-Ericsson CW1200 mac80211 drivers
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef CW1200_TXRX_H
#define CW1200_TXRX_H
#include <linux/list.h>
/* extern */ struct ieee80211_hw;
/* extern */ struct sk_buff;
/* extern */ struct wsm_tx;
/* extern */ struct wsm_rx;
/* extern */ struct wsm_tx_confirm;
/* extern */ struct cw1200_txpriv;
struct tx_policy {
union {
__le32 tbl[3];
u8 raw[12];
};
u8 defined;
u8 usage_count;
u8 retry_count;
u8 uploaded;
};
struct tx_policy_cache_entry {
struct tx_policy policy;
struct list_head link;
};
#define TX_POLICY_CACHE_SIZE (8)
struct tx_policy_cache {
struct tx_policy_cache_entry cache[TX_POLICY_CACHE_SIZE];
struct list_head used;
struct list_head free;
spinlock_t lock; /* Protect policy cache */
};
/* ******************************************************************** */
/* TX policy cache */
/* Intention of TX policy cache is an overcomplicated WSM API.
* Device does not accept per-PDU tx retry sequence.
* It uses "tx retry policy id" instead, so driver code has to sync
* linux tx retry sequences with a retry policy table in the device.
*/
void tx_policy_init(struct cw1200_common *priv);
void tx_policy_upload_work(struct work_struct *work);
void tx_policy_clean(struct cw1200_common *priv);
/* ******************************************************************** */
/* TX implementation */
u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv,
u32 rates);
void cw1200_tx(struct ieee80211_hw *dev,
struct ieee80211_tx_control *control,
struct sk_buff *skb);
void cw1200_skb_dtor(struct cw1200_common *priv,
struct sk_buff *skb,
const struct cw1200_txpriv *txpriv);
/* ******************************************************************** */
/* WSM callbacks */
void cw1200_tx_confirm_cb(struct cw1200_common *priv,
int link_id,
struct wsm_tx_confirm *arg);
void cw1200_rx_cb(struct cw1200_common *priv,
struct wsm_rx *arg,
int link_id,
struct sk_buff **skb_p);
/* ******************************************************************** */
/* Timeout */
void cw1200_tx_timeout(struct work_struct *work);
/* ******************************************************************** */
/* Security */
int cw1200_alloc_key(struct cw1200_common *priv);
void cw1200_free_key(struct cw1200_common *priv, int idx);
void cw1200_free_keys(struct cw1200_common *priv);
int cw1200_upload_keys(struct cw1200_common *priv);
/* ******************************************************************** */
/* Workaround for WFD test case 6.1.10 */
void cw1200_link_id_reset(struct work_struct *work);
#define CW1200_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ))
int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac);
int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac);
void cw1200_link_id_work(struct work_struct *work);
void cw1200_link_id_gc_work(struct work_struct *work);
#endif /* CW1200_TXRX_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1347,14 +1347,6 @@ struct il_rx_mpdu_res_start {
#define TX_CMD_SEC_SHIFT 6
#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)
*/

View File

@ -1220,14 +1220,6 @@ struct iwl_rx_mpdu_res_start {
#define TX_CMD_SEC_SHIFT 6
#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)
*/

View File

@ -84,15 +84,6 @@ static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
.types = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP),
},
{
.max = 1,
.types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO),
},
{
.max = 1,
.types = BIT(NL80211_IFTYPE_P2P_DEVICE),
},
};
static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = {
@ -173,10 +164,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->chanctx_data_size = sizeof(u16);
hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_P2P_DEVICE);
BIT(NL80211_IFTYPE_AP);
hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY |
WIPHY_FLAG_DISABLE_BEACON_HINTS |

View File

@ -224,13 +224,13 @@ static void iwl_pcie_txq_update_byte_cnt_tbl(struct iwl_trans *trans,
switch (sec_ctl & TX_CMD_SEC_MSK) {
case TX_CMD_SEC_CCM:
len += CCMP_MIC_LEN;
len += IEEE80211_CCMP_MIC_LEN;
break;
case TX_CMD_SEC_TKIP:
len += TKIP_ICV_LEN;
len += IEEE80211_TKIP_ICV_LEN;
break;
case TX_CMD_SEC_WEP:
len += WEP_IV_LEN + WEP_ICV_LEN;
len += IEEE80211_WEP_IV_LEN + IEEE80211_WEP_ICV_LEN;
break;
}

View File

@ -1723,11 +1723,11 @@ static void mac80211_hwsim_free(void)
class_destroy(hwsim_class);
}
static struct device_driver mac80211_hwsim_driver = {
.name = "mac80211_hwsim",
.bus = &platform_bus_type,
.owner = THIS_MODULE,
static struct platform_driver mac80211_hwsim_driver = {
.driver = {
.name = "mac80211_hwsim",
.owner = THIS_MODULE,
},
};
static const struct net_device_ops hwsim_netdev_ops = {
@ -2169,7 +2169,6 @@ static const struct ieee80211_iface_limit hwsim_if_limits[] = {
#endif
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_P2P_GO) },
{ .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) },
};
static struct ieee80211_iface_combination hwsim_if_comb = {
@ -2219,7 +2218,7 @@ static int __init init_mac80211_hwsim(void)
spin_lock_init(&hwsim_radio_lock);
INIT_LIST_HEAD(&hwsim_radios);
err = driver_register(&mac80211_hwsim_driver);
err = platform_driver_register(&mac80211_hwsim_driver);
if (err)
return err;
@ -2254,7 +2253,7 @@ static int __init init_mac80211_hwsim(void)
err = -ENOMEM;
goto failed_drvdata;
}
data->dev->driver = &mac80211_hwsim_driver;
data->dev->driver = &mac80211_hwsim_driver.driver;
err = device_bind_driver(data->dev);
if (err != 0) {
printk(KERN_DEBUG
@ -2295,8 +2294,7 @@ static int __init init_mac80211_hwsim(void)
BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_MESH_POINT) |
BIT(NL80211_IFTYPE_P2P_DEVICE);
BIT(NL80211_IFTYPE_MESH_POINT);
hw->flags = IEEE80211_HW_MFP_CAPABLE |
IEEE80211_HW_SIGNAL_DBM |
@ -2564,7 +2562,7 @@ static int __init init_mac80211_hwsim(void)
failed:
mac80211_hwsim_free();
failed_unregister_driver:
driver_unregister(&mac80211_hwsim_driver);
platform_driver_unregister(&mac80211_hwsim_driver);
return err;
}
module_init(init_mac80211_hwsim);
@ -2577,6 +2575,6 @@ static void __exit exit_mac80211_hwsim(void)
mac80211_hwsim_free();
unregister_netdev(hwsim_mon);
driver_unregister(&mac80211_hwsim_driver);
platform_driver_unregister(&mac80211_hwsim_driver);
}
module_exit(exit_mac80211_hwsim);

View File

@ -3,13 +3,13 @@ config MWIFIEX
depends on CFG80211
---help---
This adds support for wireless adapters based on Marvell
802.11n chipsets.
802.11n/ac chipsets.
If you choose to build it as a module, it will be called
mwifiex.
config MWIFIEX_SDIO
tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797"
tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797/SD8897"
depends on MWIFIEX && MMC
select FW_LOADER
---help---

View File

@ -1231,6 +1231,51 @@ static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy,
return 0;
}
/* cfg80211 operation handler for del_station.
* Function deauthenticates station which value is provided in mac parameter.
* If mac is NULL/broadcast, all stations in associated station list are
* deauthenticated. If bss is not started or there are no stations in
* associated stations list, no action is taken.
*/
static int
mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
u8 *mac)
{
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
struct mwifiex_sta_node *sta_node;
unsigned long flags;
if (list_empty(&priv->sta_list) || !priv->bss_started)
return 0;
if (!mac || is_broadcast_ether_addr(mac)) {
wiphy_dbg(wiphy, "%s: NULL/broadcast mac address\n", __func__);
list_for_each_entry(sta_node, &priv->sta_list, list) {
if (mwifiex_send_cmd_sync(priv,
HostCmd_CMD_UAP_STA_DEAUTH,
HostCmd_ACT_GEN_SET, 0,
sta_node->mac_addr))
return -1;
mwifiex_uap_del_sta_data(priv, sta_node);
}
} else {
wiphy_dbg(wiphy, "%s: mac address %pM\n", __func__, mac);
spin_lock_irqsave(&priv->sta_list_spinlock, flags);
sta_node = mwifiex_get_sta_entry(priv, mac);
spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
if (sta_node) {
if (mwifiex_send_cmd_sync(priv,
HostCmd_CMD_UAP_STA_DEAUTH,
HostCmd_ACT_GEN_SET, 0,
sta_node->mac_addr))
return -1;
mwifiex_uap_del_sta_data(priv, sta_node);
}
}
return 0;
}
static int
mwifiex_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant)
{
@ -1859,6 +1904,7 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy,
int i, offset, ret;
struct ieee80211_channel *chan;
struct ieee_types_header *ie;
struct mwifiex_user_scan_cfg *user_scan_cfg;
wiphy_dbg(wiphy, "info: received scan request on %s\n", dev->name);
@ -1869,20 +1915,22 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy,
return -EBUSY;
}
if (priv->user_scan_cfg) {
/* Block scan request if scan operation or scan cleanup when interface
* is disabled is in process
*/
if (priv->scan_request || priv->scan_aborting) {
dev_err(priv->adapter->dev, "cmd: Scan already in process..\n");
return -EBUSY;
}
priv->user_scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg),
GFP_KERNEL);
if (!priv->user_scan_cfg)
user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL);
if (!user_scan_cfg)
return -ENOMEM;
priv->scan_request = request;
priv->user_scan_cfg->num_ssids = request->n_ssids;
priv->user_scan_cfg->ssid_list = request->ssids;
user_scan_cfg->num_ssids = request->n_ssids;
user_scan_cfg->ssid_list = request->ssids;
if (request->ie && request->ie_len) {
offset = 0;
@ -1902,25 +1950,25 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy,
for (i = 0; i < min_t(u32, request->n_channels,
MWIFIEX_USER_SCAN_CHAN_MAX); i++) {
chan = request->channels[i];
priv->user_scan_cfg->chan_list[i].chan_number = chan->hw_value;
priv->user_scan_cfg->chan_list[i].radio_type = chan->band;
user_scan_cfg->chan_list[i].chan_number = chan->hw_value;
user_scan_cfg->chan_list[i].radio_type = chan->band;
if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
priv->user_scan_cfg->chan_list[i].scan_type =
user_scan_cfg->chan_list[i].scan_type =
MWIFIEX_SCAN_TYPE_PASSIVE;
else
priv->user_scan_cfg->chan_list[i].scan_type =
user_scan_cfg->chan_list[i].scan_type =
MWIFIEX_SCAN_TYPE_ACTIVE;
priv->user_scan_cfg->chan_list[i].scan_time = 0;
user_scan_cfg->chan_list[i].scan_time = 0;
}
ret = mwifiex_scan_networks(priv, priv->user_scan_cfg);
ret = mwifiex_scan_networks(priv, user_scan_cfg);
kfree(user_scan_cfg);
if (ret) {
dev_err(priv->adapter->dev, "scan failed: %d\n", ret);
priv->scan_aborting = false;
priv->scan_request = NULL;
kfree(priv->user_scan_cfg);
priv->user_scan_cfg = NULL;
return ret;
}
@ -2419,6 +2467,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
.change_beacon = mwifiex_cfg80211_change_beacon,
.set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config,
.set_antenna = mwifiex_cfg80211_set_antenna,
.del_station = mwifiex_cfg80211_del_station,
#ifdef CONFIG_PM
.suspend = mwifiex_cfg80211_suspend,
.resume = mwifiex_cfg80211_resume,

View File

@ -570,6 +570,7 @@ int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no,
case HostCmd_CMD_UAP_SYS_CONFIG:
case HostCmd_CMD_UAP_BSS_START:
case HostCmd_CMD_UAP_BSS_STOP:
case HostCmd_CMD_UAP_STA_DEAUTH:
ret = mwifiex_uap_prepare_cmd(priv, cmd_no, cmd_action,
cmd_oid, data_buf,
cmd_ptr);

View File

@ -271,6 +271,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define HostCmd_CMD_802_11_SUBSCRIBE_EVENT 0x0075
#define HostCmd_CMD_802_11_TX_RATE_QUERY 0x007f
#define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS 0x0083
#define HostCmd_CMD_CFG_DATA 0x008f
#define HostCmd_CMD_VERSION_EXT 0x0097
#define HostCmd_CMD_MEF_CFG 0x009a
#define HostCmd_CMD_RSSI_INFO 0x00a4
@ -279,6 +280,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define HostCmd_CMD_UAP_SYS_CONFIG 0x00b0
#define HostCmd_CMD_UAP_BSS_START 0x00b1
#define HostCmd_CMD_UAP_BSS_STOP 0x00b2
#define HostCmd_CMD_UAP_STA_DEAUTH 0x00b5
#define HostCmd_CMD_11N_CFG 0x00cd
#define HostCmd_CMD_11N_ADDBA_REQ 0x00ce
#define HostCmd_CMD_11N_ADDBA_RSP 0x00cf
@ -464,6 +466,8 @@ enum P2P_MODES {
#define MWIFIEX_CRITERIA_UNICAST BIT(1)
#define MWIFIEX_CRITERIA_MULTICAST BIT(3)
#define CFG_DATA_TYPE_CAL 2
struct mwifiex_ie_types_header {
__le16 type;
__le16 len;
@ -1197,6 +1201,11 @@ struct host_cmd_ds_amsdu_aggr_ctrl {
__le16 curr_buf_size;
} __packed;
struct host_cmd_ds_sta_deauth {
u8 mac[ETH_ALEN];
__le16 reason;
} __packed;
struct mwifiex_ie_types_wmm_param_set {
struct mwifiex_ie_types_header header;
u8 wmm_ie[1];
@ -1573,6 +1582,12 @@ struct mwifiex_ie_list {
struct mwifiex_ie ie_list[MAX_MGMT_IE_INDEX];
} __packed;
struct host_cmd_ds_802_11_cfg_data {
__le16 action;
__le16 type;
__le16 data_len;
} __packed;
struct host_cmd_ds_command {
__le16 command;
__le16 size;
@ -1630,7 +1645,9 @@ struct host_cmd_ds_command {
struct host_cmd_ds_802_11_eeprom_access eeprom;
struct host_cmd_ds_802_11_subsc_evt subsc_evt;
struct host_cmd_ds_sys_config uap_sys_config;
struct host_cmd_ds_sta_deauth sta_deauth;
struct host_cmd_11ac_vht_cfg vht_cfg;
struct host_cmd_ds_802_11_cfg_data cfg_data;
} params;
} __packed;

View File

@ -59,6 +59,9 @@ static void scan_delay_timer_fn(unsigned long data)
struct cmd_ctrl_node *cmd_node, *tmp_node;
unsigned long flags;
if (adapter->surprise_removed)
return;
if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT) {
/*
* Abort scan operation by cancelling all pending scan
@ -78,19 +81,13 @@ static void scan_delay_timer_fn(unsigned long data)
adapter->empty_tx_q_cnt = 0;
spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
if (priv->user_scan_cfg) {
if (priv->scan_request) {
dev_dbg(priv->adapter->dev,
"info: aborting scan\n");
cfg80211_scan_done(priv->scan_request, 1);
priv->scan_request = NULL;
} else {
dev_dbg(priv->adapter->dev,
"info: scan already aborted\n");
}
kfree(priv->user_scan_cfg);
priv->user_scan_cfg = NULL;
if (priv->scan_request) {
dev_dbg(adapter->dev, "info: aborting scan\n");
cfg80211_scan_done(priv->scan_request, 1);
priv->scan_request = NULL;
} else {
priv->scan_aborting = false;
dev_dbg(adapter->dev, "info: scan already aborted\n");
}
goto done;
}
@ -447,23 +444,29 @@ static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter)
}
/*
* This function frees the adapter structure.
* This function performs cleanup for adapter structure.
*
* The freeing operation is done recursively, by canceling all
* pending commands, freeing the member buffers previously
* allocated (command buffers, scan table buffer, sleep confirm
* command buffer), stopping the timers and calling the cleanup
* routines for every interface, before the actual adapter
* structure is freed.
* The cleanup is done recursively, by canceling all pending
* commands, freeing the member buffers previously allocated
* (command buffers, scan table buffer, sleep confirm command
* buffer), stopping the timers and calling the cleanup routines
* for every interface.
*/
static void
mwifiex_free_adapter(struct mwifiex_adapter *adapter)
mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter)
{
int i;
if (!adapter) {
pr_err("%s: adapter is NULL\n", __func__);
return;
}
for (i = 0; i < adapter->priv_num; i++) {
if (adapter->priv[i])
del_timer_sync(&adapter->priv[i]->scan_delay_timer);
}
mwifiex_cancel_all_pending_cmd(adapter);
/* Free lock variables */
@ -684,7 +687,6 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
int ret = -EINPROGRESS;
struct mwifiex_private *priv;
s32 i;
unsigned long flags;
struct sk_buff *skb;
/* mwifiex already shutdown */
@ -719,7 +721,7 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
}
}
spin_lock_irqsave(&adapter->mwifiex_lock, flags);
spin_lock(&adapter->mwifiex_lock);
if (adapter->if_ops.data_complete) {
while ((skb = skb_dequeue(&adapter->usb_rx_data_q))) {
@ -733,10 +735,9 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
}
}
/* Free adapter structure */
mwifiex_free_adapter(adapter);
mwifiex_adapter_cleanup(adapter);
spin_unlock_irqrestore(&adapter->mwifiex_lock, flags);
spin_unlock(&adapter->mwifiex_lock);
/* Notify completion */
ret = mwifiex_shutdown_fw_complete(adapter);

View File

@ -919,9 +919,8 @@ mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv,
memcpy(&priv->curr_bss_params.data_rates,
&adhoc_start->data_rate, priv->curr_bss_params.num_of_rates);
dev_dbg(adapter->dev, "info: ADHOC_S_CMD: rates=%02x %02x %02x %02x\n",
adhoc_start->data_rate[0], adhoc_start->data_rate[1],
adhoc_start->data_rate[2], adhoc_start->data_rate[3]);
dev_dbg(adapter->dev, "info: ADHOC_S_CMD: rates=%4ph\n",
adhoc_start->data_rate);
dev_dbg(adapter->dev, "info: ADHOC_S_CMD: AD-HOC Start command is ready\n");

View File

@ -25,6 +25,8 @@
#define VERSION "1.0"
const char driver_version[] = "mwifiex " VERSION " (%s) ";
static char *cal_data_cfg;
module_param(cal_data_cfg, charp, 0);
/*
* This function registers the device and performs all the necessary
@ -336,6 +338,13 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
dev_notice(adapter->dev, "WLAN FW is active\n");
if (cal_data_cfg) {
if ((request_firmware(&adapter->cal_data, cal_data_cfg,
adapter->dev)) < 0)
dev_err(adapter->dev,
"Cal data request_firmware() failed\n");
}
adapter->init_wait_q_woken = false;
ret = mwifiex_init_fw(adapter);
if (ret == -1) {
@ -390,6 +399,10 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
pr_debug("info: %s: unregister device\n", __func__);
adapter->if_ops.unregister_dev(adapter);
done:
if (adapter->cal_data) {
release_firmware(adapter->cal_data);
adapter->cal_data = NULL;
}
release_firmware(adapter->firmware);
complete(&adapter->fw_load);
return;
@ -436,6 +449,7 @@ mwifiex_close(struct net_device *dev)
dev_dbg(priv->adapter->dev, "aborting scan on ndo_stop\n");
cfg80211_scan_done(priv->scan_request, 1);
priv->scan_request = NULL;
priv->scan_aborting = true;
}
return 0;

View File

@ -492,7 +492,6 @@ struct mwifiex_private {
struct semaphore async_sem;
u8 report_scan_result;
struct cfg80211_scan_request *scan_request;
struct mwifiex_user_scan_cfg *user_scan_cfg;
u8 cfg_bssid[6];
struct wps wps;
u8 scan_block;
@ -510,6 +509,7 @@ struct mwifiex_private {
u8 ap_11ac_enabled;
u32 mgmt_frame_mask;
struct mwifiex_roc_cfg roc_cfg;
bool scan_aborting;
};
enum mwifiex_ba_status {
@ -730,6 +730,7 @@ struct mwifiex_adapter {
u16 max_mgmt_ie_index;
u8 scan_delay_cnt;
u8 empty_tx_q_cnt;
const struct firmware *cal_data;
/* 11AC */
u32 is_hw_11ac_capable;
@ -1115,6 +1116,8 @@ int mwifiex_set_mgmt_ies(struct mwifiex_private *priv,
struct cfg80211_beacon_data *data);
int mwifiex_del_mgmt_ies(struct mwifiex_private *priv);
u8 *mwifiex_11d_code_2_region(u8 code);
void mwifiex_uap_del_sta_data(struct mwifiex_private *priv,
struct mwifiex_sta_node *node);
extern const struct ethtool_ops mwifiex_ethtool_ops;

View File

@ -1784,22 +1784,17 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
if (priv->report_scan_result)
priv->report_scan_result = false;
if (priv->user_scan_cfg) {
if (priv->scan_request) {
dev_dbg(priv->adapter->dev,
"info: notifying scan done\n");
cfg80211_scan_done(priv->scan_request, 0);
priv->scan_request = NULL;
} else {
dev_dbg(priv->adapter->dev,
"info: scan already aborted\n");
}
kfree(priv->user_scan_cfg);
priv->user_scan_cfg = NULL;
if (priv->scan_request) {
dev_dbg(adapter->dev, "info: notifying scan done\n");
cfg80211_scan_done(priv->scan_request, 0);
priv->scan_request = NULL;
} else {
priv->scan_aborting = false;
dev_dbg(adapter->dev, "info: scan already aborted\n");
}
} else {
if (priv->user_scan_cfg && !priv->scan_request) {
if ((priv->scan_aborting && !priv->scan_request) ||
priv->scan_block) {
spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
flags);
adapter->scan_delay_cnt = MWIFIEX_MAX_SCAN_DELAY_CNT;

View File

@ -77,6 +77,17 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
if (id->driver_data) {
struct mwifiex_sdio_device *data = (void *)id->driver_data;
card->firmware = data->firmware;
card->reg = data->reg;
card->max_ports = data->max_ports;
card->mp_agg_pkt_limit = data->mp_agg_pkt_limit;
card->supports_sdio_new_mode = data->supports_sdio_new_mode;
card->has_control_mask = data->has_control_mask;
}
sdio_claim_host(func);
ret = sdio_enable_func(func);
sdio_release_host(func);
@ -251,12 +262,19 @@ static int mwifiex_sdio_resume(struct device *dev)
#define SDIO_DEVICE_ID_MARVELL_8787 (0x9119)
/* Device ID for SD8797 */
#define SDIO_DEVICE_ID_MARVELL_8797 (0x9129)
/* Device ID for SD8897 */
#define SDIO_DEVICE_ID_MARVELL_8897 (0x912d)
/* WLAN IDs */
static const struct sdio_device_id mwifiex_ids[] = {
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786)},
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787)},
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797)},
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786),
.driver_data = (unsigned long) &mwifiex_sdio_sd8786},
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787),
.driver_data = (unsigned long) &mwifiex_sdio_sd8787},
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797),
.driver_data = (unsigned long) &mwifiex_sdio_sd8797},
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897),
.driver_data = (unsigned long) &mwifiex_sdio_sd8897},
{},
};
@ -282,13 +300,13 @@ static struct sdio_driver mwifiex_sdio = {
* This function writes data into SDIO card register.
*/
static int
mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u32 data)
mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u8 data)
{
struct sdio_mmc_card *card = adapter->card;
int ret = -1;
sdio_claim_host(card->func);
sdio_writeb(card->func, (u8) data, reg, &ret);
sdio_writeb(card->func, data, reg, &ret);
sdio_release_host(card->func);
return ret;
@ -298,7 +316,7 @@ mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u32 data)
* This function reads data from SDIO card register.
*/
static int
mwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u32 *data)
mwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u8 *data)
{
struct sdio_mmc_card *card = adapter->card;
int ret = -1;
@ -400,7 +418,40 @@ static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter)
}
/*
* This function initializes the IO ports.
* This function is used to initialize IO ports for the
* chipsets supporting SDIO new mode eg SD8897.
*/
static int mwifiex_init_sdio_new_mode(struct mwifiex_adapter *adapter)
{
u8 reg;
adapter->ioport = MEM_PORT;
/* enable sdio new mode */
if (mwifiex_read_reg(adapter, CARD_CONFIG_2_1_REG, &reg))
return -1;
if (mwifiex_write_reg(adapter, CARD_CONFIG_2_1_REG,
reg | CMD53_NEW_MODE))
return -1;
/* Configure cmd port and enable reading rx length from the register */
if (mwifiex_read_reg(adapter, CMD_CONFIG_0, &reg))
return -1;
if (mwifiex_write_reg(adapter, CMD_CONFIG_0, reg | CMD_PORT_RD_LEN_EN))
return -1;
/* Enable Dnld/Upld ready auto reset for cmd port after cmd53 is
* completed
*/
if (mwifiex_read_reg(adapter, CMD_CONFIG_1, &reg))
return -1;
if (mwifiex_write_reg(adapter, CMD_CONFIG_1, reg | CMD_PORT_AUTO_EN))
return -1;
return 0;
}
/* This function initializes the IO ports.
*
* The following operations are performed -
* - Read the IO ports (0, 1 and 2)
@ -409,10 +460,17 @@ static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter)
*/
static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter)
{
u32 reg;
u8 reg;
struct sdio_mmc_card *card = adapter->card;
adapter->ioport = 0;
if (card->supports_sdio_new_mode) {
if (mwifiex_init_sdio_new_mode(adapter))
return -1;
goto cont;
}
/* Read the IO port */
if (!mwifiex_read_reg(adapter, IO_PORT_0_REG, &reg))
adapter->ioport |= (reg & 0xff);
@ -428,19 +486,19 @@ static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter)
adapter->ioport |= ((reg & 0xff) << 16);
else
return -1;
cont:
pr_debug("info: SDIO FUNC1 IO port: %#x\n", adapter->ioport);
/* Set Host interrupt reset to read to clear */
if (!mwifiex_read_reg(adapter, HOST_INT_RSR_REG, &reg))
mwifiex_write_reg(adapter, HOST_INT_RSR_REG,
reg | SDIO_INT_MASK);
reg | card->reg->sdio_int_mask);
else
return -1;
/* Dnld/Upld ready set to auto reset */
if (!mwifiex_read_reg(adapter, CARD_MISC_CFG_REG, &reg))
mwifiex_write_reg(adapter, CARD_MISC_CFG_REG,
if (!mwifiex_read_reg(adapter, card->reg->card_misc_cfg_reg, &reg))
mwifiex_write_reg(adapter, card->reg->card_misc_cfg_reg,
reg | AUTO_RE_ENABLE_INT);
else
return -1;
@ -486,34 +544,42 @@ static int mwifiex_write_data_to_card(struct mwifiex_adapter *adapter,
static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port)
{
struct sdio_mmc_card *card = adapter->card;
u16 rd_bitmap = card->mp_rd_bitmap;
const struct mwifiex_sdio_card_reg *reg = card->reg;
u32 rd_bitmap = card->mp_rd_bitmap;
dev_dbg(adapter->dev, "data: mp_rd_bitmap=0x%04x\n", rd_bitmap);
dev_dbg(adapter->dev, "data: mp_rd_bitmap=0x%08x\n", rd_bitmap);
if (!(rd_bitmap & (CTRL_PORT_MASK | DATA_PORT_MASK)))
if (card->supports_sdio_new_mode) {
if (!(rd_bitmap & reg->data_port_mask))
return -1;
} else {
if (!(rd_bitmap & (CTRL_PORT_MASK | reg->data_port_mask)))
return -1;
}
if ((card->has_control_mask) &&
(card->mp_rd_bitmap & CTRL_PORT_MASK)) {
card->mp_rd_bitmap &= (u32) (~CTRL_PORT_MASK);
*port = CTRL_PORT;
dev_dbg(adapter->dev, "data: port=%d mp_rd_bitmap=0x%08x\n",
*port, card->mp_rd_bitmap);
return 0;
}
if (!(card->mp_rd_bitmap & (1 << card->curr_rd_port)))
return -1;
if (card->mp_rd_bitmap & CTRL_PORT_MASK) {
card->mp_rd_bitmap &= (u16) (~CTRL_PORT_MASK);
*port = CTRL_PORT;
dev_dbg(adapter->dev, "data: port=%d mp_rd_bitmap=0x%04x\n",
*port, card->mp_rd_bitmap);
} else {
if (card->mp_rd_bitmap & (1 << card->curr_rd_port)) {
card->mp_rd_bitmap &= (u16)
(~(1 << card->curr_rd_port));
*port = card->curr_rd_port;
/* We are now handling the SDIO data ports */
card->mp_rd_bitmap &= (u32)(~(1 << card->curr_rd_port));
*port = card->curr_rd_port;
if (++card->curr_rd_port == MAX_PORT)
card->curr_rd_port = 1;
} else {
return -1;
}
if (++card->curr_rd_port == card->max_ports)
card->curr_rd_port = reg->start_rd_port;
dev_dbg(adapter->dev,
"data: port=%d mp_rd_bitmap=0x%08x -> 0x%08x\n",
*port, rd_bitmap, card->mp_rd_bitmap);
dev_dbg(adapter->dev,
"data: port=%d mp_rd_bitmap=0x%04x -> 0x%04x\n",
*port, rd_bitmap, card->mp_rd_bitmap);
}
return 0;
}
@ -524,35 +590,45 @@ static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port)
* increased (provided it does not reach the maximum limit, in which
* case it is reset to 1)
*/
static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u8 *port)
static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u32 *port)
{
struct sdio_mmc_card *card = adapter->card;
u16 wr_bitmap = card->mp_wr_bitmap;
const struct mwifiex_sdio_card_reg *reg = card->reg;
u32 wr_bitmap = card->mp_wr_bitmap;
dev_dbg(adapter->dev, "data: mp_wr_bitmap=0x%04x\n", wr_bitmap);
dev_dbg(adapter->dev, "data: mp_wr_bitmap=0x%08x\n", wr_bitmap);
if (!(wr_bitmap & card->mp_data_port_mask))
if (card->supports_sdio_new_mode &&
!(wr_bitmap & reg->data_port_mask)) {
adapter->data_sent = true;
return -EBUSY;
} else if (!card->supports_sdio_new_mode &&
!(wr_bitmap & card->mp_data_port_mask)) {
return -1;
}
if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) {
card->mp_wr_bitmap &= (u16) (~(1 << card->curr_wr_port));
card->mp_wr_bitmap &= (u32) (~(1 << card->curr_wr_port));
*port = card->curr_wr_port;
if (++card->curr_wr_port == card->mp_end_port)
card->curr_wr_port = 1;
if (((card->supports_sdio_new_mode) &&
(++card->curr_wr_port == card->max_ports)) ||
((!card->supports_sdio_new_mode) &&
(++card->curr_wr_port == card->mp_end_port)))
card->curr_wr_port = reg->start_wr_port;
} else {
adapter->data_sent = true;
return -EBUSY;
}
if (*port == CTRL_PORT) {
dev_err(adapter->dev, "invalid data port=%d cur port=%d"
" mp_wr_bitmap=0x%04x -> 0x%04x\n",
if ((card->has_control_mask) && (*port == CTRL_PORT)) {
dev_err(adapter->dev,
"invalid data port=%d cur port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n",
*port, card->curr_wr_port, wr_bitmap,
card->mp_wr_bitmap);
return -1;
}
dev_dbg(adapter->dev, "data: port=%d mp_wr_bitmap=0x%04x -> 0x%04x\n",
dev_dbg(adapter->dev, "data: port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n",
*port, wr_bitmap, card->mp_wr_bitmap);
return 0;
@ -564,11 +640,12 @@ static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u8 *port)
static int
mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits)
{
struct sdio_mmc_card *card = adapter->card;
u32 tries;
u32 cs;
u8 cs;
for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
if (mwifiex_read_reg(adapter, CARD_STATUS_REG, &cs))
if (mwifiex_read_reg(adapter, card->reg->poll_reg, &cs))
break;
else if ((cs & bits) == bits)
return 0;
@ -587,12 +664,14 @@ mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits)
static int
mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat)
{
u32 fws0, fws1;
struct sdio_mmc_card *card = adapter->card;
const struct mwifiex_sdio_card_reg *reg = card->reg;
u8 fws0, fws1;
if (mwifiex_read_reg(adapter, CARD_FW_STATUS0_REG, &fws0))
if (mwifiex_read_reg(adapter, reg->status_reg_0, &fws0))
return -1;
if (mwifiex_read_reg(adapter, CARD_FW_STATUS1_REG, &fws1))
if (mwifiex_read_reg(adapter, reg->status_reg_1, &fws1))
return -1;
*dat = (u16) ((fws1 << 8) | fws0);
@ -608,14 +687,14 @@ mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat)
*/
static int mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter)
{
u32 host_int_mask;
u8 host_int_mask, host_int_disable = HOST_INT_DISABLE;
/* Read back the host_int_mask register */
if (mwifiex_read_reg(adapter, HOST_INT_MASK_REG, &host_int_mask))
return -1;
/* Update with the mask and write back to the register */
host_int_mask &= ~HOST_INT_DISABLE;
host_int_mask &= ~host_int_disable;
if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG, host_int_mask)) {
dev_err(adapter->dev, "disable host interrupt failed\n");
@ -633,8 +712,11 @@ static int mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter)
*/
static int mwifiex_sdio_enable_host_int(struct mwifiex_adapter *adapter)
{
struct sdio_mmc_card *card = adapter->card;
/* Simply write the mask to the register */
if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG, HOST_INT_ENABLE)) {
if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG,
card->reg->host_int_enable)) {
dev_err(adapter->dev, "enable host interrupt failed\n");
return -1;
}
@ -686,11 +768,13 @@ static int mwifiex_sdio_card_to_host(struct mwifiex_adapter *adapter,
static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
struct mwifiex_fw_image *fw)
{
struct sdio_mmc_card *card = adapter->card;
const struct mwifiex_sdio_card_reg *reg = card->reg;
int ret;
u8 *firmware = fw->fw_buf;
u32 firmware_len = fw->fw_len;
u32 offset = 0;
u32 base0, base1;
u8 base0, base1;
u8 *fwbuf;
u16 len = 0;
u32 txlen, tx_blocks = 0, tries;
@ -727,7 +811,7 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
break;
for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
ret = mwifiex_read_reg(adapter, HOST_F1_RD_BASE_0,
ret = mwifiex_read_reg(adapter, reg->base_0_reg,
&base0);
if (ret) {
dev_err(adapter->dev,
@ -736,7 +820,7 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
base0, base0);
goto done;
}
ret = mwifiex_read_reg(adapter, HOST_F1_RD_BASE_1,
ret = mwifiex_read_reg(adapter, reg->base_1_reg,
&base1);
if (ret) {
dev_err(adapter->dev,
@ -828,10 +912,11 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
u32 poll_num)
{
struct sdio_mmc_card *card = adapter->card;
int ret = 0;
u16 firmware_stat;
u32 tries;
u32 winner_status;
u8 winner_status;
/* Wait for firmware initialization event */
for (tries = 0; tries < poll_num; tries++) {
@ -849,7 +934,7 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
if (ret) {
if (mwifiex_read_reg
(adapter, CARD_FW_STATUS0_REG, &winner_status))
(adapter, card->reg->status_reg_0, &winner_status))
winner_status = 0;
if (winner_status)
@ -866,12 +951,12 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
{
struct sdio_mmc_card *card = adapter->card;
u32 sdio_ireg;
u8 sdio_ireg;
unsigned long flags;
if (mwifiex_read_data_sync(adapter, card->mp_regs, MAX_MP_REGS,
REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK,
0)) {
if (mwifiex_read_data_sync(adapter, card->mp_regs,
card->reg->max_mp_regs,
REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) {
dev_err(adapter->dev, "read mp_regs failed\n");
return;
}
@ -880,6 +965,9 @@ static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
if (sdio_ireg) {
/*
* DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
* For SDIO new mode CMD port interrupts
* DN_LD_CMD_PORT_HOST_INT_STATUS and/or
* UP_LD_CMD_PORT_HOST_INT_STATUS
* Clear the interrupt status register
*/
dev_dbg(adapter->dev, "int: sdio_ireg = %#x\n", sdio_ireg);
@ -1003,11 +1091,11 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
s32 f_aggr_cur = 0;
struct sk_buff *skb_deaggr;
u32 pind;
u32 pkt_len, pkt_type = 0;
u32 pkt_len, pkt_type, mport;
u8 *curr_ptr;
u32 rx_len = skb->len;
if (port == CTRL_PORT) {
if ((card->has_control_mask) && (port == CTRL_PORT)) {
/* Read the command Resp without aggr */
dev_dbg(adapter->dev, "info: %s: no aggregation for cmd "
"response\n", __func__);
@ -1024,7 +1112,10 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
goto rx_curr_single;
}
if (card->mp_rd_bitmap & (~((u16) CTRL_PORT_MASK))) {
if ((!card->has_control_mask && (card->mp_rd_bitmap &
card->reg->data_port_mask)) ||
(card->has_control_mask && (card->mp_rd_bitmap &
(~((u32) CTRL_PORT_MASK))))) {
/* Some more data RX pending */
dev_dbg(adapter->dev, "info: %s: not last packet\n", __func__);
@ -1060,10 +1151,10 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
if (f_aggr_cur) {
dev_dbg(adapter->dev, "info: current packet aggregation\n");
/* Curr pkt can be aggregated */
MP_RX_AGGR_SETUP(card, skb, port);
mp_rx_aggr_setup(card, skb, port);
if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) ||
MP_RX_AGGR_PORT_LIMIT_REACHED(card)) {
mp_rx_aggr_port_limit_reached(card)) {
dev_dbg(adapter->dev, "info: %s: aggregated packet "
"limit reached\n", __func__);
/* No more pkts allowed in Aggr buf, rx it */
@ -1076,11 +1167,28 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
dev_dbg(adapter->dev, "info: do_rx_aggr: num of packets: %d\n",
card->mpa_rx.pkt_cnt);
if (card->supports_sdio_new_mode) {
int i;
u32 port_count;
for (i = 0, port_count = 0; i < card->max_ports; i++)
if (card->mpa_rx.ports & BIT(i))
port_count++;
/* Reading data from "start_port + 0" to "start_port +
* port_count -1", so decrease the count by 1
*/
port_count--;
mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
(port_count << 8)) + card->mpa_rx.start_port;
} else {
mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
(card->mpa_rx.ports << 4)) +
card->mpa_rx.start_port;
}
if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf,
card->mpa_rx.buf_len,
(adapter->ioport | 0x1000 |
(card->mpa_rx.ports << 4)) +
card->mpa_rx.start_port, 1))
card->mpa_rx.buf_len, mport, 1))
goto error;
curr_ptr = card->mpa_rx.buf;
@ -1167,6 +1275,7 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter,
static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
{
struct sdio_mmc_card *card = adapter->card;
const struct mwifiex_sdio_card_reg *reg = card->reg;
int ret = 0;
u8 sdio_ireg;
struct sk_buff *skb;
@ -1175,6 +1284,8 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
u32 rx_blocks;
u16 rx_len;
unsigned long flags;
u32 bitmap;
u8 cr;
spin_lock_irqsave(&adapter->int_lock, flags);
sdio_ireg = adapter->int_status;
@ -1184,10 +1295,60 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
if (!sdio_ireg)
return ret;
/* Following interrupt is only for SDIO new mode */
if (sdio_ireg & DN_LD_CMD_PORT_HOST_INT_STATUS && adapter->cmd_sent)
adapter->cmd_sent = false;
/* Following interrupt is only for SDIO new mode */
if (sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) {
u32 pkt_type;
/* read the len of control packet */
rx_len = card->mp_regs[CMD_RD_LEN_1] << 8;
rx_len |= (u16) card->mp_regs[CMD_RD_LEN_0];
rx_blocks = DIV_ROUND_UP(rx_len, MWIFIEX_SDIO_BLOCK_SIZE);
if (rx_len <= INTF_HEADER_LEN ||
(rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) >
MWIFIEX_RX_DATA_BUF_SIZE)
return -1;
rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE);
skb = dev_alloc_skb(rx_len);
if (!skb)
return -1;
skb_put(skb, rx_len);
if (mwifiex_sdio_card_to_host(adapter, &pkt_type, skb->data,
skb->len, adapter->ioport |
CMD_PORT_SLCT)) {
dev_err(adapter->dev,
"%s: failed to card_to_host", __func__);
dev_kfree_skb_any(skb);
goto term_cmd;
}
if ((pkt_type != MWIFIEX_TYPE_CMD) &&
(pkt_type != MWIFIEX_TYPE_EVENT))
dev_err(adapter->dev,
"%s:Received wrong packet on cmd port",
__func__);
mwifiex_decode_rx_packet(adapter, skb, pkt_type);
}
if (sdio_ireg & DN_LD_HOST_INT_STATUS) {
card->mp_wr_bitmap = ((u16) card->mp_regs[WR_BITMAP_U]) << 8;
card->mp_wr_bitmap |= (u16) card->mp_regs[WR_BITMAP_L];
dev_dbg(adapter->dev, "int: DNLD: wr_bitmap=0x%04x\n",
bitmap = (u32) card->mp_regs[reg->wr_bitmap_l];
bitmap |= ((u32) card->mp_regs[reg->wr_bitmap_u]) << 8;
if (card->supports_sdio_new_mode) {
bitmap |=
((u32) card->mp_regs[reg->wr_bitmap_1l]) << 16;
bitmap |=
((u32) card->mp_regs[reg->wr_bitmap_1u]) << 24;
}
card->mp_wr_bitmap = bitmap;
dev_dbg(adapter->dev, "int: DNLD: wr_bitmap=0x%x\n",
card->mp_wr_bitmap);
if (adapter->data_sent &&
(card->mp_wr_bitmap & card->mp_data_port_mask)) {
@ -1200,11 +1361,11 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
/* As firmware will not generate download ready interrupt if the port
updated is command port only, cmd_sent should be done for any SDIO
interrupt. */
if (adapter->cmd_sent) {
if (card->has_control_mask && adapter->cmd_sent) {
/* Check if firmware has attach buffer at command port and
update just that in wr_bit_map. */
card->mp_wr_bitmap |=
(u16) card->mp_regs[WR_BITMAP_L] & CTRL_PORT_MASK;
(u32) card->mp_regs[reg->wr_bitmap_l] & CTRL_PORT_MASK;
if (card->mp_wr_bitmap & CTRL_PORT_MASK)
adapter->cmd_sent = false;
}
@ -1212,9 +1373,16 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
dev_dbg(adapter->dev, "info: cmd_sent=%d data_sent=%d\n",
adapter->cmd_sent, adapter->data_sent);
if (sdio_ireg & UP_LD_HOST_INT_STATUS) {
card->mp_rd_bitmap = ((u16) card->mp_regs[RD_BITMAP_U]) << 8;
card->mp_rd_bitmap |= (u16) card->mp_regs[RD_BITMAP_L];
dev_dbg(adapter->dev, "int: UPLD: rd_bitmap=0x%04x\n",
bitmap = (u32) card->mp_regs[reg->rd_bitmap_l];
bitmap |= ((u32) card->mp_regs[reg->rd_bitmap_u]) << 8;
if (card->supports_sdio_new_mode) {
bitmap |=
((u32) card->mp_regs[reg->rd_bitmap_1l]) << 16;
bitmap |=
((u32) card->mp_regs[reg->rd_bitmap_1u]) << 24;
}
card->mp_rd_bitmap = bitmap;
dev_dbg(adapter->dev, "int: UPLD: rd_bitmap=0x%x\n",
card->mp_rd_bitmap);
while (true) {
@ -1224,8 +1392,8 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
"info: no more rd_port available\n");
break;
}
len_reg_l = RD_LEN_P0_L + (port << 1);
len_reg_u = RD_LEN_P0_U + (port << 1);
len_reg_l = reg->rd_len_p0_l + (port << 1);
len_reg_u = reg->rd_len_p0_u + (port << 1);
rx_len = ((u16) card->mp_regs[len_reg_u]) << 8;
rx_len |= (u16) card->mp_regs[len_reg_l];
dev_dbg(adapter->dev, "info: RX: port=%d rx_len=%u\n",
@ -1257,37 +1425,33 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
if (mwifiex_sdio_card_to_host_mp_aggr(adapter, skb,
port)) {
u32 cr = 0;
dev_err(adapter->dev, "card_to_host_mpa failed:"
" int status=%#x\n", sdio_ireg);
if (mwifiex_read_reg(adapter,
CONFIGURATION_REG, &cr))
dev_err(adapter->dev,
"read CFG reg failed\n");
dev_dbg(adapter->dev,
"info: CFG reg val = %d\n", cr);
if (mwifiex_write_reg(adapter,
CONFIGURATION_REG,
(cr | 0x04)))
dev_err(adapter->dev,
"write CFG reg failed\n");
dev_dbg(adapter->dev, "info: write success\n");
if (mwifiex_read_reg(adapter,
CONFIGURATION_REG, &cr))
dev_err(adapter->dev,
"read CFG reg failed\n");
dev_dbg(adapter->dev,
"info: CFG reg val =%x\n", cr);
return -1;
goto term_cmd;
}
}
}
return 0;
term_cmd:
/* terminate cmd */
if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr))
dev_err(adapter->dev, "read CFG reg failed\n");
else
dev_dbg(adapter->dev, "info: CFG reg val = %d\n", cr);
if (mwifiex_write_reg(adapter, CONFIGURATION_REG, (cr | 0x04)))
dev_err(adapter->dev, "write CFG reg failed\n");
else
dev_dbg(adapter->dev, "info: write success\n");
if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr))
dev_err(adapter->dev, "read CFG reg failed\n");
else
dev_dbg(adapter->dev, "info: CFG reg val =%x\n", cr);
return -1;
}
/*
@ -1305,7 +1469,7 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
* and return.
*/
static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
u8 *payload, u32 pkt_len, u8 port,
u8 *payload, u32 pkt_len, u32 port,
u32 next_pkt_len)
{
struct sdio_mmc_card *card = adapter->card;
@ -1314,8 +1478,11 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
s32 f_send_cur_buf = 0;
s32 f_precopy_cur_buf = 0;
s32 f_postcopy_cur_buf = 0;
u32 mport;
if ((!card->mpa_tx.enabled) || (port == CTRL_PORT)) {
if (!card->mpa_tx.enabled ||
(card->has_control_mask && (port == CTRL_PORT)) ||
(card->supports_sdio_new_mode && (port == CMD_PORT_SLCT))) {
dev_dbg(adapter->dev, "info: %s: tx aggregation disabled\n",
__func__);
@ -1329,7 +1496,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
__func__);
if (MP_TX_AGGR_IN_PROGRESS(card)) {
if (!MP_TX_AGGR_PORT_LIMIT_REACHED(card) &&
if (!mp_tx_aggr_port_limit_reached(card) &&
MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) {
f_precopy_cur_buf = 1;
@ -1342,7 +1509,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
/* No room in Aggr buf, send it */
f_send_aggr_buf = 1;
if (MP_TX_AGGR_PORT_LIMIT_REACHED(card) ||
if (mp_tx_aggr_port_limit_reached(card) ||
!(card->mp_wr_bitmap &
(1 << card->curr_wr_port)))
f_send_cur_buf = 1;
@ -1381,7 +1548,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port);
if (MP_TX_AGGR_PKT_LIMIT_REACHED(card) ||
MP_TX_AGGR_PORT_LIMIT_REACHED(card))
mp_tx_aggr_port_limit_reached(card))
/* No more pkts allowed in Aggr buf, send it */
f_send_aggr_buf = 1;
}
@ -1390,11 +1557,28 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter,
dev_dbg(adapter->dev, "data: %s: send aggr buffer: %d %d\n",
__func__,
card->mpa_tx.start_port, card->mpa_tx.ports);
if (card->supports_sdio_new_mode) {
u32 port_count;
int i;
for (i = 0, port_count = 0; i < card->max_ports; i++)
if (card->mpa_tx.ports & BIT(i))
port_count++;
/* Writing data from "start_port + 0" to "start_port +
* port_count -1", so decrease the count by 1
*/
port_count--;
mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
(port_count << 8)) + card->mpa_tx.start_port;
} else {
mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
(card->mpa_tx.ports << 4)) +
card->mpa_tx.start_port;
}
ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf,
card->mpa_tx.buf_len,
(adapter->ioport | 0x1000 |
(card->mpa_tx.ports << 4)) +
card->mpa_tx.start_port);
card->mpa_tx.buf_len, mport);
MP_TX_AGGR_BUF_RESET(card);
}
@ -1434,7 +1618,7 @@ static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter,
int ret;
u32 buf_block_len;
u32 blk_size;
u8 port = CTRL_PORT;
u32 port = CTRL_PORT;
u8 *payload = (u8 *)skb->data;
u32 pkt_len = skb->len;
@ -1465,6 +1649,9 @@ static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter,
pkt_len > MWIFIEX_UPLD_SIZE)
dev_err(adapter->dev, "%s: payload=%p, nb=%d\n",
__func__, payload, pkt_len);
if (card->supports_sdio_new_mode)
port = CMD_PORT_SLCT;
}
/* Transfer data to card */
@ -1586,18 +1773,7 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
adapter->dev = &func->dev;
switch (func->device) {
case SDIO_DEVICE_ID_MARVELL_8786:
strcpy(adapter->fw_name, SD8786_DEFAULT_FW_NAME);
break;
case SDIO_DEVICE_ID_MARVELL_8797:
strcpy(adapter->fw_name, SD8797_DEFAULT_FW_NAME);
break;
case SDIO_DEVICE_ID_MARVELL_8787:
default:
strcpy(adapter->fw_name, SD8787_DEFAULT_FW_NAME);
break;
}
strcpy(adapter->fw_name, card->firmware);
return 0;
@ -1626,8 +1802,9 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
static int mwifiex_init_sdio(struct mwifiex_adapter *adapter)
{
struct sdio_mmc_card *card = adapter->card;
const struct mwifiex_sdio_card_reg *reg = card->reg;
int ret;
u32 sdio_ireg;
u8 sdio_ireg;
/*
* Read the HOST_INT_STATUS_REG for ACK the first interrupt got
@ -1645,30 +1822,35 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter)
/* Initialize SDIO variables in card */
card->mp_rd_bitmap = 0;
card->mp_wr_bitmap = 0;
card->curr_rd_port = 1;
card->curr_wr_port = 1;
card->curr_rd_port = reg->start_rd_port;
card->curr_wr_port = reg->start_wr_port;
card->mp_data_port_mask = DATA_PORT_MASK;
card->mp_data_port_mask = reg->data_port_mask;
card->mpa_tx.buf_len = 0;
card->mpa_tx.pkt_cnt = 0;
card->mpa_tx.start_port = 0;
card->mpa_tx.enabled = 1;
card->mpa_tx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT;
card->mpa_tx.pkt_aggr_limit = card->mp_agg_pkt_limit;
card->mpa_rx.buf_len = 0;
card->mpa_rx.pkt_cnt = 0;
card->mpa_rx.start_port = 0;
card->mpa_rx.enabled = 1;
card->mpa_rx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT;
card->mpa_rx.pkt_aggr_limit = card->mp_agg_pkt_limit;
/* Allocate buffers for SDIO MP-A */
card->mp_regs = kzalloc(MAX_MP_REGS, GFP_KERNEL);
card->mp_regs = kzalloc(reg->max_mp_regs, GFP_KERNEL);
if (!card->mp_regs)
return -ENOMEM;
/* Allocate skb pointer buffers */
card->mpa_rx.skb_arr = kzalloc((sizeof(void *)) *
card->mp_agg_pkt_limit, GFP_KERNEL);
card->mpa_rx.len_arr = kzalloc(sizeof(*card->mpa_rx.len_arr) *
card->mp_agg_pkt_limit, GFP_KERNEL);
ret = mwifiex_alloc_sdio_mpa_buffers(adapter,
SDIO_MP_TX_AGGR_DEF_BUF_SIZE,
SDIO_MP_RX_AGGR_DEF_BUF_SIZE);
@ -1705,6 +1887,8 @@ static void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter)
struct sdio_mmc_card *card = adapter->card;
kfree(card->mp_regs);
kfree(card->mpa_rx.skb_arr);
kfree(card->mpa_rx.len_arr);
kfree(card->mpa_tx.buf);
kfree(card->mpa_rx.buf);
}
@ -1716,16 +1900,20 @@ static void
mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port)
{
struct sdio_mmc_card *card = adapter->card;
const struct mwifiex_sdio_card_reg *reg = card->reg;
int i;
card->mp_end_port = port;
card->mp_data_port_mask = DATA_PORT_MASK;
card->mp_data_port_mask = reg->data_port_mask;
for (i = 1; i <= MAX_PORT - card->mp_end_port; i++)
card->mp_data_port_mask &= ~(1 << (MAX_PORT - i));
if (reg->start_wr_port) {
for (i = 1; i <= card->max_ports - card->mp_end_port; i++)
card->mp_data_port_mask &=
~(1 << (card->max_ports - i));
}
card->curr_wr_port = 1;
card->curr_wr_port = reg->start_wr_port;
dev_dbg(adapter->dev, "cmd: mp_end_port %d, data port mask 0x%x\n",
port, card->mp_data_port_mask);
@ -1831,3 +2019,4 @@ MODULE_LICENSE("GPL v2");
MODULE_FIRMWARE(SD8786_DEFAULT_FW_NAME);
MODULE_FIRMWARE(SD8787_DEFAULT_FW_NAME);
MODULE_FIRMWARE(SD8797_DEFAULT_FW_NAME);
MODULE_FIRMWARE(SD8897_DEFAULT_FW_NAME);

View File

@ -32,30 +32,37 @@
#define SD8786_DEFAULT_FW_NAME "mrvl/sd8786_uapsta.bin"
#define SD8787_DEFAULT_FW_NAME "mrvl/sd8787_uapsta.bin"
#define SD8797_DEFAULT_FW_NAME "mrvl/sd8797_uapsta.bin"
#define SD8897_DEFAULT_FW_NAME "mrvl/sd8897_uapsta.bin"
#define BLOCK_MODE 1
#define BYTE_MODE 0
#define REG_PORT 0
#define RD_BITMAP_L 0x04
#define RD_BITMAP_U 0x05
#define WR_BITMAP_L 0x06
#define WR_BITMAP_U 0x07
#define RD_LEN_P0_L 0x08
#define RD_LEN_P0_U 0x09
#define MWIFIEX_SDIO_IO_PORT_MASK 0xfffff
#define MWIFIEX_SDIO_BYTE_MODE_MASK 0x80000000
#define SDIO_MPA_ADDR_BASE 0x1000
#define CTRL_PORT 0
#define CTRL_PORT_MASK 0x0001
#define DATA_PORT_MASK 0xfffe
#define MAX_MP_REGS 64
#define MAX_PORT 16
#define SDIO_MP_AGGR_DEF_PKT_LIMIT 8
#define CMD_PORT_UPLD_INT_MASK (0x1U<<6)
#define CMD_PORT_DNLD_INT_MASK (0x1U<<7)
#define HOST_TERM_CMD53 (0x1U << 2)
#define REG_PORT 0
#define MEM_PORT 0x10000
#define CMD_RD_LEN_0 0xB4
#define CMD_RD_LEN_1 0xB5
#define CARD_CONFIG_2_1_REG 0xCD
#define CMD53_NEW_MODE (0x1U << 0)
#define CMD_CONFIG_0 0xB8
#define CMD_PORT_RD_LEN_EN (0x1U << 2)
#define CMD_CONFIG_1 0xB9
#define CMD_PORT_AUTO_EN (0x1U << 0)
#define CMD_PORT_SLCT 0x8000
#define UP_LD_CMD_PORT_HOST_INT_STATUS (0x40U)
#define DN_LD_CMD_PORT_HOST_INT_STATUS (0x80U)
#define SDIO_MP_TX_AGGR_DEF_BUF_SIZE (8192) /* 8K */
@ -75,14 +82,8 @@
/* Host Control Registers : Configuration */
#define CONFIGURATION_REG 0x00
/* Host Control Registers : Host without Command 53 finish host*/
#define HOST_TO_CARD_EVENT (0x1U << 3)
/* Host Control Registers : Host without Command 53 finish host */
#define HOST_WO_CMD53_FINISH_HOST (0x1U << 2)
/* Host Control Registers : Host power up */
#define HOST_POWER_UP (0x1U << 1)
/* Host Control Registers : Host power down */
#define HOST_POWER_DOWN (0x1U << 0)
/* Host Control Registers : Host interrupt mask */
#define HOST_INT_MASK_REG 0x02
@ -90,8 +91,7 @@
#define UP_LD_HOST_INT_MASK (0x1U)
/* Host Control Registers : Download host interrupt mask */
#define DN_LD_HOST_INT_MASK (0x2U)
/* Enable Host interrupt mask */
#define HOST_INT_ENABLE (UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK)
/* Disable Host interrupt mask */
#define HOST_INT_DISABLE 0xff
@ -104,74 +104,15 @@
/* Host Control Registers : Host interrupt RSR */
#define HOST_INT_RSR_REG 0x01
/* Host Control Registers : Upload host interrupt RSR */
#define UP_LD_HOST_INT_RSR (0x1U)
#define SDIO_INT_MASK 0x3F
/* Host Control Registers : Host interrupt status */
#define HOST_INT_STATUS_REG 0x28
/* Host Control Registers : Upload CRC error */
#define UP_LD_CRC_ERR (0x1U << 2)
/* Host Control Registers : Upload restart */
#define UP_LD_RESTART (0x1U << 1)
/* Host Control Registers : Download restart */
#define DN_LD_RESTART (0x1U << 0)
/* Card Control Registers : Card status register */
#define CARD_STATUS_REG 0x30
/* Card Control Registers : Card I/O ready */
#define CARD_IO_READY (0x1U << 3)
/* Card Control Registers : CIS card ready */
#define CIS_CARD_RDY (0x1U << 2)
/* Card Control Registers : Upload card ready */
#define UP_LD_CARD_RDY (0x1U << 1)
/* Card Control Registers : Download card ready */
#define DN_LD_CARD_RDY (0x1U << 0)
/* Card Control Registers : Host interrupt mask register */
#define HOST_INTERRUPT_MASK_REG 0x34
/* Card Control Registers : Host power interrupt mask */
#define HOST_POWER_INT_MASK (0x1U << 3)
/* Card Control Registers : Abort card interrupt mask */
#define ABORT_CARD_INT_MASK (0x1U << 2)
/* Card Control Registers : Upload card interrupt mask */
#define UP_LD_CARD_INT_MASK (0x1U << 1)
/* Card Control Registers : Download card interrupt mask */
#define DN_LD_CARD_INT_MASK (0x1U << 0)
/* Card Control Registers : Card interrupt status register */
#define CARD_INTERRUPT_STATUS_REG 0x38
/* Card Control Registers : Power up interrupt */
#define POWER_UP_INT (0x1U << 4)
/* Card Control Registers : Power down interrupt */
#define POWER_DOWN_INT (0x1U << 3)
/* Card Control Registers : Card interrupt RSR register */
#define CARD_INTERRUPT_RSR_REG 0x3c
/* Card Control Registers : Power up RSR */
#define POWER_UP_RSR (0x1U << 4)
/* Card Control Registers : Power down RSR */
#define POWER_DOWN_RSR (0x1U << 3)
/* Card Control Registers : Miscellaneous Configuration Register */
#define CARD_MISC_CFG_REG 0x6C
/* Host F1 read base 0 */
#define HOST_F1_RD_BASE_0 0x0040
/* Host F1 read base 1 */
#define HOST_F1_RD_BASE_1 0x0041
/* Host F1 card ready */
#define HOST_F1_CARD_RDY 0x0020
/* Firmware status 0 register */
#define CARD_FW_STATUS0_REG 0x60
/* Firmware status 1 register */
#define CARD_FW_STATUS1_REG 0x61
/* Rx length register */
#define CARD_RX_LEN_REG 0x62
/* Rx unit register */
#define CARD_RX_UNIT_REG 0x63
/* Max retry number of CMD53 write */
#define MAX_WRITE_IOMEM_RETRY 2
@ -192,7 +133,8 @@
if (a->mpa_tx.start_port <= port) \
a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt)); \
else \
a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+(MAX_PORT - \
a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+ \
(a->max_ports - \
a->mp_end_port))); \
a->mpa_tx.pkt_cnt++; \
} while (0)
@ -201,12 +143,6 @@
#define MP_TX_AGGR_PKT_LIMIT_REACHED(a) \
(a->mpa_tx.pkt_cnt == a->mpa_tx.pkt_aggr_limit)
/* SDIO Tx aggregation port limit ? */
#define MP_TX_AGGR_PORT_LIMIT_REACHED(a) ((a->curr_wr_port < \
a->mpa_tx.start_port) && (((MAX_PORT - \
a->mpa_tx.start_port) + a->curr_wr_port) >= \
SDIO_MP_AGGR_DEF_PKT_LIMIT))
/* Reset SDIO Tx aggregation buffer parameters */
#define MP_TX_AGGR_BUF_RESET(a) do { \
a->mpa_tx.pkt_cnt = 0; \
@ -219,12 +155,6 @@
#define MP_RX_AGGR_PKT_LIMIT_REACHED(a) \
(a->mpa_rx.pkt_cnt == a->mpa_rx.pkt_aggr_limit)
/* SDIO Tx aggregation port limit ? */
#define MP_RX_AGGR_PORT_LIMIT_REACHED(a) ((a->curr_rd_port < \
a->mpa_rx.start_port) && (((MAX_PORT - \
a->mpa_rx.start_port) + a->curr_rd_port) >= \
SDIO_MP_AGGR_DEF_PKT_LIMIT))
/* SDIO Rx aggregation in progress ? */
#define MP_RX_AGGR_IN_PROGRESS(a) (a->mpa_rx.pkt_cnt > 0)
@ -232,20 +162,6 @@
#define MP_RX_AGGR_BUF_HAS_ROOM(a, rx_len) \
((a->mpa_rx.buf_len+rx_len) <= a->mpa_rx.buf_size)
/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */
#define MP_RX_AGGR_SETUP(a, skb, port) do { \
a->mpa_rx.buf_len += skb->len; \
if (!a->mpa_rx.pkt_cnt) \
a->mpa_rx.start_port = port; \
if (a->mpa_rx.start_port <= port) \
a->mpa_rx.ports |= (1<<(a->mpa_rx.pkt_cnt)); \
else \
a->mpa_rx.ports |= (1<<(a->mpa_rx.pkt_cnt+1)); \
a->mpa_rx.skb_arr[a->mpa_rx.pkt_cnt] = skb; \
a->mpa_rx.len_arr[a->mpa_rx.pkt_cnt] = skb->len; \
a->mpa_rx.pkt_cnt++; \
} while (0)
/* Reset SDIO Rx aggregation buffer parameters */
#define MP_RX_AGGR_BUF_RESET(a) do { \
a->mpa_rx.pkt_cnt = 0; \
@ -254,14 +170,13 @@
a->mpa_rx.start_port = 0; \
} while (0)
/* data structure for SDIO MPA TX */
struct mwifiex_sdio_mpa_tx {
/* multiport tx aggregation buffer pointer */
u8 *buf;
u32 buf_len;
u32 pkt_cnt;
u16 ports;
u32 ports;
u16 start_port;
u8 enabled;
u32 buf_size;
@ -272,11 +187,11 @@ struct mwifiex_sdio_mpa_rx {
u8 *buf;
u32 buf_len;
u32 pkt_cnt;
u16 ports;
u32 ports;
u16 start_port;
struct sk_buff *skb_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT];
u32 len_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT];
struct sk_buff **skb_arr;
u32 *len_arr;
u8 enabled;
u32 buf_size;
@ -286,15 +201,47 @@ struct mwifiex_sdio_mpa_rx {
int mwifiex_bus_register(void);
void mwifiex_bus_unregister(void);
struct mwifiex_sdio_card_reg {
u8 start_rd_port;
u8 start_wr_port;
u8 base_0_reg;
u8 base_1_reg;
u8 poll_reg;
u8 host_int_enable;
u8 status_reg_0;
u8 status_reg_1;
u8 sdio_int_mask;
u32 data_port_mask;
u8 max_mp_regs;
u8 rd_bitmap_l;
u8 rd_bitmap_u;
u8 rd_bitmap_1l;
u8 rd_bitmap_1u;
u8 wr_bitmap_l;
u8 wr_bitmap_u;
u8 wr_bitmap_1l;
u8 wr_bitmap_1u;
u8 rd_len_p0_l;
u8 rd_len_p0_u;
u8 card_misc_cfg_reg;
};
struct sdio_mmc_card {
struct sdio_func *func;
struct mwifiex_adapter *adapter;
u16 mp_rd_bitmap;
u16 mp_wr_bitmap;
const char *firmware;
const struct mwifiex_sdio_card_reg *reg;
u8 max_ports;
u8 mp_agg_pkt_limit;
bool supports_sdio_new_mode;
bool has_control_mask;
u32 mp_rd_bitmap;
u32 mp_wr_bitmap;
u16 mp_end_port;
u16 mp_data_port_mask;
u32 mp_data_port_mask;
u8 curr_rd_port;
u8 curr_wr_port;
@ -305,6 +252,98 @@ struct sdio_mmc_card {
struct mwifiex_sdio_mpa_rx mpa_rx;
};
struct mwifiex_sdio_device {
const char *firmware;
const struct mwifiex_sdio_card_reg *reg;
u8 max_ports;
u8 mp_agg_pkt_limit;
bool supports_sdio_new_mode;
bool has_control_mask;
};
static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = {
.start_rd_port = 1,
.start_wr_port = 1,
.base_0_reg = 0x0040,
.base_1_reg = 0x0041,
.poll_reg = 0x30,
.host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK,
.status_reg_0 = 0x60,
.status_reg_1 = 0x61,
.sdio_int_mask = 0x3f,
.data_port_mask = 0x0000fffe,
.max_mp_regs = 64,
.rd_bitmap_l = 0x04,
.rd_bitmap_u = 0x05,
.wr_bitmap_l = 0x06,
.wr_bitmap_u = 0x07,
.rd_len_p0_l = 0x08,
.rd_len_p0_u = 0x09,
.card_misc_cfg_reg = 0x6c,
};
static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = {
.start_rd_port = 0,
.start_wr_port = 0,
.base_0_reg = 0x60,
.base_1_reg = 0x61,
.poll_reg = 0x50,
.host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK |
CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK,
.status_reg_0 = 0xc0,
.status_reg_1 = 0xc1,
.sdio_int_mask = 0xff,
.data_port_mask = 0xffffffff,
.max_mp_regs = 184,
.rd_bitmap_l = 0x04,
.rd_bitmap_u = 0x05,
.rd_bitmap_1l = 0x06,
.rd_bitmap_1u = 0x07,
.wr_bitmap_l = 0x08,
.wr_bitmap_u = 0x09,
.wr_bitmap_1l = 0x0a,
.wr_bitmap_1u = 0x0b,
.rd_len_p0_l = 0x0c,
.rd_len_p0_u = 0x0d,
.card_misc_cfg_reg = 0xcc,
};
static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = {
.firmware = SD8786_DEFAULT_FW_NAME,
.reg = &mwifiex_reg_sd87xx,
.max_ports = 16,
.mp_agg_pkt_limit = 8,
.supports_sdio_new_mode = false,
.has_control_mask = true,
};
static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = {
.firmware = SD8787_DEFAULT_FW_NAME,
.reg = &mwifiex_reg_sd87xx,
.max_ports = 16,
.mp_agg_pkt_limit = 8,
.supports_sdio_new_mode = false,
.has_control_mask = true,
};
static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = {
.firmware = SD8797_DEFAULT_FW_NAME,
.reg = &mwifiex_reg_sd87xx,
.max_ports = 16,
.mp_agg_pkt_limit = 8,
.supports_sdio_new_mode = false,
.has_control_mask = true,
};
static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
.firmware = SD8897_DEFAULT_FW_NAME,
.reg = &mwifiex_reg_sd8897,
.max_ports = 32,
.mp_agg_pkt_limit = 16,
.supports_sdio_new_mode = true,
.has_control_mask = false,
};
/*
* .cmdrsp_complete handler
*/
@ -325,4 +364,77 @@ static inline int mwifiex_sdio_event_complete(struct mwifiex_adapter *adapter,
return 0;
}
static inline bool
mp_rx_aggr_port_limit_reached(struct sdio_mmc_card *card)
{
u8 tmp;
if (card->curr_rd_port < card->mpa_rx.start_port) {
if (card->supports_sdio_new_mode)
tmp = card->mp_end_port >> 1;
else
tmp = card->mp_agg_pkt_limit;
if (((card->max_ports - card->mpa_rx.start_port) +
card->curr_rd_port) >= tmp)
return true;
}
if (!card->supports_sdio_new_mode)
return false;
if ((card->curr_rd_port - card->mpa_rx.start_port) >=
(card->mp_end_port >> 1))
return true;
return false;
}
static inline bool
mp_tx_aggr_port_limit_reached(struct sdio_mmc_card *card)
{
u16 tmp;
if (card->curr_wr_port < card->mpa_tx.start_port) {
if (card->supports_sdio_new_mode)
tmp = card->mp_end_port >> 1;
else
tmp = card->mp_agg_pkt_limit;
if (((card->max_ports - card->mpa_tx.start_port) +
card->curr_wr_port) >= tmp)
return true;
}
if (!card->supports_sdio_new_mode)
return false;
if ((card->curr_wr_port - card->mpa_tx.start_port) >=
(card->mp_end_port >> 1))
return true;
return false;
}
/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */
static inline void mp_rx_aggr_setup(struct sdio_mmc_card *card,
struct sk_buff *skb, u8 port)
{
card->mpa_rx.buf_len += skb->len;
if (!card->mpa_rx.pkt_cnt)
card->mpa_rx.start_port = port;
if (card->supports_sdio_new_mode) {
card->mpa_rx.ports |= (1 << port);
} else {
if (card->mpa_rx.start_port <= port)
card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt);
else
card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt + 1);
}
card->mpa_rx.skb_arr[card->mpa_rx.pkt_cnt] = skb;
card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] = skb->len;
card->mpa_rx.pkt_cnt++;
}
#endif /* _MWIFIEX_SDIO_H */

View File

@ -1134,6 +1134,55 @@ mwifiex_cmd_mef_cfg(struct mwifiex_private *priv,
return 0;
}
/* This function parse cal data from ASCII to hex */
static u32 mwifiex_parse_cal_cfg(u8 *src, size_t len, u8 *dst)
{
u8 *s = src, *d = dst;
while (s - src < len) {
if (*s && (isspace(*s) || *s == '\t')) {
s++;
continue;
}
if (isxdigit(*s)) {
*d++ = simple_strtol(s, NULL, 16);
s += 2;
} else {
s++;
}
}
return d - dst;
}
/* This function prepares command of set_cfg_data. */
static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd,
u16 cmd_action)
{
struct host_cmd_ds_802_11_cfg_data *cfg_data = &cmd->params.cfg_data;
struct mwifiex_adapter *adapter = priv->adapter;
u32 len, cal_data_offset;
u8 *tmp_cmd = (u8 *)cmd;
cal_data_offset = S_DS_GEN + sizeof(*cfg_data);
if ((adapter->cal_data->data) && (adapter->cal_data->size > 0))
len = mwifiex_parse_cal_cfg((u8 *)adapter->cal_data->data,
adapter->cal_data->size,
(u8 *)(tmp_cmd + cal_data_offset));
else
return -1;
cfg_data->action = cpu_to_le16(cmd_action);
cfg_data->type = cpu_to_le16(CFG_DATA_TYPE_CAL);
cfg_data->data_len = cpu_to_le16(len);
cmd->command = cpu_to_le16(HostCmd_CMD_CFG_DATA);
cmd->size = cpu_to_le16(S_DS_GEN + sizeof(*cfg_data) + len);
return 0;
}
/*
* This function prepares the commands before sending them to the firmware.
*
@ -1152,6 +1201,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
case HostCmd_CMD_GET_HW_SPEC:
ret = mwifiex_cmd_get_hw_spec(priv, cmd_ptr);
break;
case HostCmd_CMD_CFG_DATA:
ret = mwifiex_cmd_cfg_data(priv, cmd_ptr, cmd_action);
break;
case HostCmd_CMD_MAC_CONTROL:
ret = mwifiex_cmd_mac_control(priv, cmd_ptr, cmd_action,
data_buf);
@ -1384,6 +1436,7 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
*/
int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta)
{
struct mwifiex_adapter *adapter = priv->adapter;
int ret;
u16 enable = true;
struct mwifiex_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl;
@ -1404,6 +1457,15 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta)
HostCmd_ACT_GEN_SET, 0, NULL);
if (ret)
return -1;
/* Download calibration data to firmware */
if (adapter->cal_data) {
ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_CFG_DATA,
HostCmd_ACT_GEN_SET, 0, NULL);
if (ret)
return -1;
}
/* Read MAC address from HW */
ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_GET_HW_SPEC,
HostCmd_ACT_GEN_GET, 0, NULL);

View File

@ -818,6 +818,18 @@ static int mwifiex_ret_subsc_evt(struct mwifiex_private *priv,
return 0;
}
/* This function handles the command response of set_cfg_data */
static int mwifiex_ret_cfg_data(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp)
{
if (resp->result != HostCmd_RESULT_OK) {
dev_err(priv->adapter->dev, "Cal data cmd resp failed\n");
return -1;
}
return 0;
}
/*
* This function handles the command responses.
*
@ -841,6 +853,9 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
case HostCmd_CMD_GET_HW_SPEC:
ret = mwifiex_ret_get_hw_spec(priv, resp);
break;
case HostCmd_CMD_CFG_DATA:
ret = mwifiex_ret_cfg_data(priv, resp);
break;
case HostCmd_CMD_MAC_CONTROL:
break;
case HostCmd_CMD_802_11_MAC_ADDRESS:
@ -978,6 +993,8 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
case HostCmd_CMD_UAP_BSS_STOP:
priv->bss_started = 0;
break;
case HostCmd_CMD_UAP_STA_DEAUTH:
break;
case HostCmd_CMD_MEF_CFG:
break;
default:

View File

@ -689,6 +689,23 @@ mwifiex_cmd_uap_sys_config(struct host_cmd_ds_command *cmd, u16 cmd_action,
return 0;
}
/* This function prepares AP specific deauth command with mac supplied in
* function parameter.
*/
static int mwifiex_cmd_uap_sta_deauth(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd, u8 *mac)
{
struct host_cmd_ds_sta_deauth *sta_deauth = &cmd->params.sta_deauth;
cmd->command = cpu_to_le16(HostCmd_CMD_UAP_STA_DEAUTH);
memcpy(sta_deauth->mac, mac, ETH_ALEN);
sta_deauth->reason = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING);
cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_sta_deauth) +
S_DS_GEN);
return 0;
}
/* This function prepares the AP specific commands before sending them
* to the firmware.
* This is a generic function which calls specific command preparation
@ -710,6 +727,10 @@ int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, u16 cmd_no,
cmd->command = cpu_to_le16(cmd_no);
cmd->size = cpu_to_le16(S_DS_GEN);
break;
case HostCmd_CMD_UAP_STA_DEAUTH:
if (mwifiex_cmd_uap_sta_deauth(priv, cmd, data_buf))
return -1;
break;
default:
dev_err(priv->adapter->dev,
"PREP_CMD: unknown cmd %#x\n", cmd_no);

View File

@ -107,18 +107,15 @@ mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies,
*/
static void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac)
{
struct mwifiex_sta_node *node, *tmp;
struct mwifiex_sta_node *node;
unsigned long flags;
spin_lock_irqsave(&priv->sta_list_spinlock, flags);
node = mwifiex_get_sta_entry(priv, mac);
if (node) {
list_for_each_entry_safe(node, tmp, &priv->sta_list,
list) {
list_del(&node->list);
kfree(node);
}
list_del(&node->list);
kfree(node);
}
spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
@ -295,3 +292,19 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
return 0;
}
/* This function deletes station entry from associated station list.
* Also if both AP and STA are 11n enabled, RxReorder tables and TxBA stream
* tables created for this station are deleted.
*/
void mwifiex_uap_del_sta_data(struct mwifiex_private *priv,
struct mwifiex_sta_node *node)
{
if (priv->ap_11n_enabled && node->is_11n_enabled) {
mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, node->mac_addr);
mwifiex_del_tx_ba_stream_tbl_by_ra(priv, node->mac_addr);
}
mwifiex_del_sta_entry(priv, node->mac_addr);
return;
}

View File

@ -1548,7 +1548,7 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
if (!priv->pending_tx_pkts)
return 0;
retry = 0;
retry = 1;
rc = 0;
spin_lock_bh(&priv->tx_lock);
@ -1572,13 +1572,19 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
spin_lock_bh(&priv->tx_lock);
if (timeout) {
if (timeout || !priv->pending_tx_pkts) {
WARN_ON(priv->pending_tx_pkts);
if (retry)
wiphy_notice(hw->wiphy, "tx rings drained\n");
break;
}
if (retry) {
mwl8k_tx_start(priv);
retry = 0;
continue;
}
if (priv->pending_tx_pkts < oldcount) {
wiphy_notice(hw->wiphy,
"waiting for tx rings to drain (%d -> %d pkts)\n",
@ -2055,6 +2061,7 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw,
mwl8k_remove_stream(hw, stream);
spin_unlock(&priv->stream_lock);
}
mwl8k_tx_start(priv);
spin_unlock_bh(&priv->tx_lock);
pci_unmap_single(priv->pdev, dma, skb->len,
PCI_DMA_TODEVICE);

View File

@ -42,8 +42,7 @@
MODULE_FIRMWARE("3826.arm");
/*
* gpios should be handled in board files and provided via platform data,
/* gpios should be handled in board files and provided via platform data,
* but because it's currently impossible for p54spi to have a header file
* in include/linux, let's use module paramaters for now
*/
@ -191,8 +190,7 @@ static int p54spi_request_eeprom(struct ieee80211_hw *dev)
const struct firmware *eeprom;
int ret;
/*
* allow users to customize their eeprom.
/* allow users to customize their eeprom.
*/
ret = request_firmware(&eeprom, "3826.eeprom", &priv->spi->dev);
@ -285,8 +283,7 @@ static void p54spi_power_on(struct p54s_priv *priv)
gpio_set_value(p54spi_gpio_power, 1);
enable_irq(gpio_to_irq(p54spi_gpio_irq));
/*
* need to wait a while before device can be accessed, the length
/* need to wait a while before device can be accessed, the length
* is just a guess
*/
msleep(10);
@ -365,7 +362,8 @@ static int p54spi_rx(struct p54s_priv *priv)
/* Firmware may insert up to 4 padding bytes after the lmac header,
* but it does not amend the size of SPI data transfer.
* Such packets has correct data size in header, thus referencing
* past the end of allocated skb. Reserve extra 4 bytes for this case */
* past the end of allocated skb. Reserve extra 4 bytes for this case
*/
skb = dev_alloc_skb(len + 4);
if (!skb) {
p54spi_sleep(priv);
@ -383,7 +381,8 @@ static int p54spi_rx(struct p54s_priv *priv)
}
p54spi_sleep(priv);
/* Put additional bytes to compensate for the possible
* alignment-caused truncation */
* alignment-caused truncation
*/
skb_put(skb, 4);
if (p54_rx(priv->hw, skb) == 0)
@ -713,27 +712,7 @@ static struct spi_driver p54spi_driver = {
.remove = p54spi_remove,
};
static int __init p54spi_init(void)
{
int ret;
ret = spi_register_driver(&p54spi_driver);
if (ret < 0) {
printk(KERN_ERR "failed to register SPI driver: %d", ret);
goto out;
}
out:
return ret;
}
static void __exit p54spi_exit(void)
{
spi_unregister_driver(&p54spi_driver);
}
module_init(p54spi_init);
module_exit(p54spi_exit);
module_spi_driver(p54spi_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Christian Lamparter <chunkeey@web.de>");

View File

@ -840,7 +840,7 @@ static inline void rt2800_clear_beacon_register(struct rt2x00_dev *rt2x00dev,
unsigned int beacon_base)
{
int i;
const int txwi_desc_size = rt2x00dev->ops->bcn->winfo_size;
const int txwi_desc_size = rt2x00dev->bcn->winfo_size;
/*
* For the Beacon base registers we only need to clear
@ -3953,6 +3953,463 @@ static void rt2800_init_bbp_early(struct rt2x00_dev *rt2x00dev)
rt2800_bbp_write(rt2x00dev, 106, 0x35);
}
static void rt2800_disable_unused_dac_adc(struct rt2x00_dev *rt2x00dev)
{
u16 eeprom;
u8 value;
rt2800_bbp_read(rt2x00dev, 138, &value);
rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1)
value |= 0x20;
if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1)
value &= ~0x02;
rt2800_bbp_write(rt2x00dev, 138, value);
}
static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev)
{
rt2800_bbp_write(rt2x00dev, 31, 0x08);
rt2800_bbp_write(rt2x00dev, 65, 0x2c);
rt2800_bbp_write(rt2x00dev, 66, 0x38);
rt2800_bbp_write(rt2x00dev, 69, 0x12);
rt2800_bbp_write(rt2x00dev, 73, 0x10);
rt2800_bbp_write(rt2x00dev, 70, 0x0a);
rt2800_bbp_write(rt2x00dev, 78, 0x0e);
rt2800_bbp_write(rt2x00dev, 80, 0x08);
rt2800_bbp_write(rt2x00dev, 82, 0x62);
rt2800_bbp_write(rt2x00dev, 83, 0x6a);
rt2800_bbp_write(rt2x00dev, 84, 0x99);
rt2800_bbp_write(rt2x00dev, 86, 0x00);
rt2800_bbp_write(rt2x00dev, 91, 0x04);
rt2800_bbp_write(rt2x00dev, 92, 0x00);
rt2800_bbp_write(rt2x00dev, 103, 0xc0);
rt2800_bbp_write(rt2x00dev, 105, 0x01);
rt2800_bbp_write(rt2x00dev, 106, 0x35);
}
static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev)
{
rt2800_bbp_write(rt2x00dev, 65, 0x2c);
rt2800_bbp_write(rt2x00dev, 66, 0x38);
if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) {
rt2800_bbp_write(rt2x00dev, 69, 0x16);
rt2800_bbp_write(rt2x00dev, 73, 0x12);
} else {
rt2800_bbp_write(rt2x00dev, 69, 0x12);
rt2800_bbp_write(rt2x00dev, 73, 0x10);
}
rt2800_bbp_write(rt2x00dev, 70, 0x0a);
rt2800_bbp_write(rt2x00dev, 81, 0x37);
rt2800_bbp_write(rt2x00dev, 82, 0x62);
rt2800_bbp_write(rt2x00dev, 83, 0x6a);
if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860D))
rt2800_bbp_write(rt2x00dev, 84, 0x19);
else
rt2800_bbp_write(rt2x00dev, 84, 0x99);
rt2800_bbp_write(rt2x00dev, 86, 0x00);
rt2800_bbp_write(rt2x00dev, 91, 0x04);
rt2800_bbp_write(rt2x00dev, 92, 0x00);
rt2800_bbp_write(rt2x00dev, 103, 0x00);
rt2800_bbp_write(rt2x00dev, 105, 0x05);
rt2800_bbp_write(rt2x00dev, 106, 0x35);
}
static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev)
{
rt2800_bbp_write(rt2x00dev, 65, 0x2c);
rt2800_bbp_write(rt2x00dev, 66, 0x38);
rt2800_bbp_write(rt2x00dev, 69, 0x12);
rt2800_bbp_write(rt2x00dev, 73, 0x10);
rt2800_bbp_write(rt2x00dev, 70, 0x0a);
rt2800_bbp_write(rt2x00dev, 79, 0x13);
rt2800_bbp_write(rt2x00dev, 80, 0x05);
rt2800_bbp_write(rt2x00dev, 81, 0x33);
rt2800_bbp_write(rt2x00dev, 82, 0x62);
rt2800_bbp_write(rt2x00dev, 83, 0x6a);
rt2800_bbp_write(rt2x00dev, 84, 0x99);
rt2800_bbp_write(rt2x00dev, 86, 0x00);
rt2800_bbp_write(rt2x00dev, 91, 0x04);
rt2800_bbp_write(rt2x00dev, 92, 0x00);
if (rt2x00_rt_rev_gte(rt2x00dev, RT3070, REV_RT3070F) ||
rt2x00_rt_rev_gte(rt2x00dev, RT3071, REV_RT3071E) ||
rt2x00_rt_rev_gte(rt2x00dev, RT3090, REV_RT3090E))
rt2800_bbp_write(rt2x00dev, 103, 0xc0);
else
rt2800_bbp_write(rt2x00dev, 103, 0x00);
rt2800_bbp_write(rt2x00dev, 105, 0x05);
rt2800_bbp_write(rt2x00dev, 106, 0x35);
if (rt2x00_rt(rt2x00dev, RT3071) ||
rt2x00_rt(rt2x00dev, RT3090))
rt2800_disable_unused_dac_adc(rt2x00dev);
}
static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev)
{
u8 value;
rt2800_bbp4_mac_if_ctrl(rt2x00dev);
rt2800_bbp_write(rt2x00dev, 31, 0x08);
rt2800_bbp_write(rt2x00dev, 65, 0x2c);
rt2800_bbp_write(rt2x00dev, 66, 0x38);
rt2800_bbp_write(rt2x00dev, 68, 0x0b);
rt2800_bbp_write(rt2x00dev, 69, 0x12);
rt2800_bbp_write(rt2x00dev, 73, 0x13);
rt2800_bbp_write(rt2x00dev, 75, 0x46);
rt2800_bbp_write(rt2x00dev, 76, 0x28);
rt2800_bbp_write(rt2x00dev, 77, 0x58);
rt2800_bbp_write(rt2x00dev, 70, 0x0a);
rt2800_bbp_write(rt2x00dev, 74, 0x0b);
rt2800_bbp_write(rt2x00dev, 79, 0x18);
rt2800_bbp_write(rt2x00dev, 80, 0x09);
rt2800_bbp_write(rt2x00dev, 81, 0x33);
rt2800_bbp_write(rt2x00dev, 82, 0x62);
rt2800_bbp_write(rt2x00dev, 83, 0x7a);
rt2800_bbp_write(rt2x00dev, 84, 0x9a);
rt2800_bbp_write(rt2x00dev, 86, 0x38);
rt2800_bbp_write(rt2x00dev, 91, 0x04);
rt2800_bbp_write(rt2x00dev, 92, 0x02);
rt2800_bbp_write(rt2x00dev, 103, 0xc0);
rt2800_bbp_write(rt2x00dev, 104, 0x92);
rt2800_bbp_write(rt2x00dev, 105, 0x1c);
rt2800_bbp_write(rt2x00dev, 106, 0x03);
rt2800_bbp_write(rt2x00dev, 128, 0x12);
rt2800_bbp_write(rt2x00dev, 67, 0x24);
rt2800_bbp_write(rt2x00dev, 143, 0x04);
rt2800_bbp_write(rt2x00dev, 142, 0x99);
rt2800_bbp_write(rt2x00dev, 150, 0x30);
rt2800_bbp_write(rt2x00dev, 151, 0x2e);
rt2800_bbp_write(rt2x00dev, 152, 0x20);
rt2800_bbp_write(rt2x00dev, 153, 0x34);
rt2800_bbp_write(rt2x00dev, 154, 0x40);
rt2800_bbp_write(rt2x00dev, 155, 0x3b);
rt2800_bbp_write(rt2x00dev, 253, 0x04);
rt2800_bbp_read(rt2x00dev, 47, &value);
rt2x00_set_field8(&value, BBP47_TSSI_ADC6, 1);
rt2800_bbp_write(rt2x00dev, 47, value);
/* Use 5-bit ADC for Acquisition and 8-bit ADC for data */
rt2800_bbp_read(rt2x00dev, 3, &value);
rt2x00_set_field8(&value, BBP3_ADC_MODE_SWITCH, 1);
rt2x00_set_field8(&value, BBP3_ADC_INIT_MODE, 1);
rt2800_bbp_write(rt2x00dev, 3, value);
}
static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev)
{
rt2800_bbp_write(rt2x00dev, 3, 0x00);
rt2800_bbp_write(rt2x00dev, 4, 0x50);
rt2800_bbp_write(rt2x00dev, 31, 0x08);
rt2800_bbp_write(rt2x00dev, 47, 0x48);
rt2800_bbp_write(rt2x00dev, 65, 0x2c);
rt2800_bbp_write(rt2x00dev, 66, 0x38);
rt2800_bbp_write(rt2x00dev, 68, 0x0b);
rt2800_bbp_write(rt2x00dev, 69, 0x12);
rt2800_bbp_write(rt2x00dev, 73, 0x13);
rt2800_bbp_write(rt2x00dev, 75, 0x46);
rt2800_bbp_write(rt2x00dev, 76, 0x28);
rt2800_bbp_write(rt2x00dev, 77, 0x59);
rt2800_bbp_write(rt2x00dev, 70, 0x0a);
rt2800_bbp_write(rt2x00dev, 78, 0x0e);
rt2800_bbp_write(rt2x00dev, 80, 0x08);
rt2800_bbp_write(rt2x00dev, 81, 0x37);
rt2800_bbp_write(rt2x00dev, 82, 0x62);
rt2800_bbp_write(rt2x00dev, 83, 0x6a);
rt2800_bbp_write(rt2x00dev, 84, 0x99);
rt2800_bbp_write(rt2x00dev, 86, 0x38);
rt2800_bbp_write(rt2x00dev, 88, 0x90);
rt2800_bbp_write(rt2x00dev, 91, 0x04);
rt2800_bbp_write(rt2x00dev, 92, 0x02);
rt2800_bbp_write(rt2x00dev, 103, 0xc0);
rt2800_bbp_write(rt2x00dev, 104, 0x92);
rt2800_bbp_write(rt2x00dev, 105, 0x34);
rt2800_bbp_write(rt2x00dev, 106, 0x05);
rt2800_bbp_write(rt2x00dev, 120, 0x50);
rt2800_bbp_write(rt2x00dev, 137, 0x0f);
rt2800_bbp_write(rt2x00dev, 163, 0xbd);
/* Set ITxBF timeout to 0x9c40=1000msec */
rt2800_bbp_write(rt2x00dev, 179, 0x02);
rt2800_bbp_write(rt2x00dev, 180, 0x00);
rt2800_bbp_write(rt2x00dev, 182, 0x40);
rt2800_bbp_write(rt2x00dev, 180, 0x01);
rt2800_bbp_write(rt2x00dev, 182, 0x9c);
rt2800_bbp_write(rt2x00dev, 179, 0x00);
/* Reprogram the inband interface to put right values in RXWI */
rt2800_bbp_write(rt2x00dev, 142, 0x04);
rt2800_bbp_write(rt2x00dev, 143, 0x3b);
rt2800_bbp_write(rt2x00dev, 142, 0x06);
rt2800_bbp_write(rt2x00dev, 143, 0xa0);
rt2800_bbp_write(rt2x00dev, 142, 0x07);
rt2800_bbp_write(rt2x00dev, 143, 0xa1);
rt2800_bbp_write(rt2x00dev, 142, 0x08);
rt2800_bbp_write(rt2x00dev, 143, 0xa2);
rt2800_bbp_write(rt2x00dev, 148, 0xc8);
}
static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev)
{
rt2800_bbp_write(rt2x00dev, 65, 0x2c);
rt2800_bbp_write(rt2x00dev, 66, 0x38);
rt2800_bbp_write(rt2x00dev, 69, 0x12);
rt2800_bbp_write(rt2x00dev, 73, 0x10);
rt2800_bbp_write(rt2x00dev, 70, 0x0a);
rt2800_bbp_write(rt2x00dev, 79, 0x13);
rt2800_bbp_write(rt2x00dev, 80, 0x05);
rt2800_bbp_write(rt2x00dev, 81, 0x33);
rt2800_bbp_write(rt2x00dev, 82, 0x62);
rt2800_bbp_write(rt2x00dev, 83, 0x6a);
rt2800_bbp_write(rt2x00dev, 84, 0x99);
rt2800_bbp_write(rt2x00dev, 86, 0x00);
rt2800_bbp_write(rt2x00dev, 91, 0x04);
rt2800_bbp_write(rt2x00dev, 92, 0x00);
if (rt2x00_rt_rev_gte(rt2x00dev, RT3390, REV_RT3390E))
rt2800_bbp_write(rt2x00dev, 103, 0xc0);
else
rt2800_bbp_write(rt2x00dev, 103, 0x00);
rt2800_bbp_write(rt2x00dev, 105, 0x05);
rt2800_bbp_write(rt2x00dev, 106, 0x35);
rt2800_disable_unused_dac_adc(rt2x00dev);
}
static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev)
{
rt2800_bbp_write(rt2x00dev, 31, 0x08);
rt2800_bbp_write(rt2x00dev, 65, 0x2c);
rt2800_bbp_write(rt2x00dev, 66, 0x38);
rt2800_bbp_write(rt2x00dev, 69, 0x12);
rt2800_bbp_write(rt2x00dev, 73, 0x10);
rt2800_bbp_write(rt2x00dev, 70, 0x0a);
rt2800_bbp_write(rt2x00dev, 79, 0x13);
rt2800_bbp_write(rt2x00dev, 80, 0x05);
rt2800_bbp_write(rt2x00dev, 81, 0x33);
rt2800_bbp_write(rt2x00dev, 82, 0x62);
rt2800_bbp_write(rt2x00dev, 83, 0x6a);
rt2800_bbp_write(rt2x00dev, 84, 0x99);
rt2800_bbp_write(rt2x00dev, 86, 0x00);
rt2800_bbp_write(rt2x00dev, 91, 0x04);
rt2800_bbp_write(rt2x00dev, 92, 0x00);
rt2800_bbp_write(rt2x00dev, 103, 0xc0);
rt2800_bbp_write(rt2x00dev, 105, 0x05);
rt2800_bbp_write(rt2x00dev, 106, 0x35);
rt2800_disable_unused_dac_adc(rt2x00dev);
}
static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev)
{
int ant, div_mode;
u16 eeprom;
u8 value;
rt2800_bbp4_mac_if_ctrl(rt2x00dev);
rt2800_bbp_write(rt2x00dev, 31, 0x08);
rt2800_bbp_write(rt2x00dev, 65, 0x2c);
rt2800_bbp_write(rt2x00dev, 66, 0x38);
rt2800_bbp_write(rt2x00dev, 68, 0x0b);
rt2800_bbp_write(rt2x00dev, 69, 0x12);
rt2800_bbp_write(rt2x00dev, 73, 0x13);
rt2800_bbp_write(rt2x00dev, 75, 0x46);
rt2800_bbp_write(rt2x00dev, 76, 0x28);
rt2800_bbp_write(rt2x00dev, 77, 0x59);
rt2800_bbp_write(rt2x00dev, 70, 0x0a);
rt2800_bbp_write(rt2x00dev, 79, 0x13);
rt2800_bbp_write(rt2x00dev, 80, 0x05);
rt2800_bbp_write(rt2x00dev, 81, 0x33);
rt2800_bbp_write(rt2x00dev, 82, 0x62);
rt2800_bbp_write(rt2x00dev, 83, 0x7a);
rt2800_bbp_write(rt2x00dev, 84, 0x9a);
rt2800_bbp_write(rt2x00dev, 86, 0x38);
if (rt2x00_rt(rt2x00dev, RT5392))
rt2800_bbp_write(rt2x00dev, 88, 0x90);
rt2800_bbp_write(rt2x00dev, 91, 0x04);
rt2800_bbp_write(rt2x00dev, 92, 0x02);
if (rt2x00_rt(rt2x00dev, RT5392)) {
rt2800_bbp_write(rt2x00dev, 95, 0x9a);
rt2800_bbp_write(rt2x00dev, 98, 0x12);
}
rt2800_bbp_write(rt2x00dev, 103, 0xc0);
rt2800_bbp_write(rt2x00dev, 104, 0x92);
rt2800_bbp_write(rt2x00dev, 105, 0x3c);
if (rt2x00_rt(rt2x00dev, RT5390))
rt2800_bbp_write(rt2x00dev, 106, 0x03);
else if (rt2x00_rt(rt2x00dev, RT5392))
rt2800_bbp_write(rt2x00dev, 106, 0x12);
else
WARN_ON(1);
rt2800_bbp_write(rt2x00dev, 128, 0x12);
if (rt2x00_rt(rt2x00dev, RT5392)) {
rt2800_bbp_write(rt2x00dev, 134, 0xd0);
rt2800_bbp_write(rt2x00dev, 135, 0xf6);
}
rt2800_disable_unused_dac_adc(rt2x00dev);
rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
div_mode = rt2x00_get_field16(eeprom,
EEPROM_NIC_CONF1_ANT_DIVERSITY);
ant = (div_mode == 3) ? 1 : 0;
/* check if this is a Bluetooth combo card */
if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
u32 reg;
rt2800_register_read(rt2x00dev, GPIO_CTRL, &reg);
rt2x00_set_field32(&reg, GPIO_CTRL_DIR3, 0);
rt2x00_set_field32(&reg, GPIO_CTRL_DIR6, 0);
rt2x00_set_field32(&reg, GPIO_CTRL_VAL3, 0);
rt2x00_set_field32(&reg, GPIO_CTRL_VAL6, 0);
if (ant == 0)
rt2x00_set_field32(&reg, GPIO_CTRL_VAL3, 1);
else if (ant == 1)
rt2x00_set_field32(&reg, GPIO_CTRL_VAL6, 1);
rt2800_register_write(rt2x00dev, GPIO_CTRL, reg);
}
/* This chip has hardware antenna diversity*/
if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) {
rt2800_bbp_write(rt2x00dev, 150, 0); /* Disable Antenna Software OFDM */
rt2800_bbp_write(rt2x00dev, 151, 0); /* Disable Antenna Software CCK */
rt2800_bbp_write(rt2x00dev, 154, 0); /* Clear previously selected antenna */
}
rt2800_bbp_read(rt2x00dev, 152, &value);
if (ant == 0)
rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1);
else
rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0);
rt2800_bbp_write(rt2x00dev, 152, value);
rt2800_init_freq_calibration(rt2x00dev);
}
static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev)
{
int ant, div_mode;
@ -4027,305 +4484,46 @@ static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev)
rt2800_bbp_write(rt2x00dev, 103, 0xc0);
}
static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
{
unsigned int i;
u16 eeprom;
u8 reg_id;
u8 value;
if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev) ||
rt2800_wait_bbp_ready(rt2x00dev)))
return -EACCES;
if (rt2x00_rt(rt2x00dev, RT5592)) {
rt2800_init_bbp_5592(rt2x00dev);
return 0;
}
if (rt2x00_rt(rt2x00dev, RT3352)) {
rt2800_bbp_write(rt2x00dev, 3, 0x00);
rt2800_bbp_write(rt2x00dev, 4, 0x50);
}
if (rt2x00_rt(rt2x00dev, RT3290) ||
rt2x00_rt(rt2x00dev, RT5390) ||
rt2x00_rt(rt2x00dev, RT5392))
rt2800_bbp4_mac_if_ctrl(rt2x00dev);
if (rt2800_is_305x_soc(rt2x00dev) ||
rt2x00_rt(rt2x00dev, RT3290) ||
rt2x00_rt(rt2x00dev, RT3352) ||
rt2x00_rt(rt2x00dev, RT3572) ||
rt2x00_rt(rt2x00dev, RT5390) ||
rt2x00_rt(rt2x00dev, RT5392))
rt2800_bbp_write(rt2x00dev, 31, 0x08);
if (rt2x00_rt(rt2x00dev, RT3352))
rt2800_bbp_write(rt2x00dev, 47, 0x48);
rt2800_bbp_write(rt2x00dev, 65, 0x2c);
rt2800_bbp_write(rt2x00dev, 66, 0x38);
if (rt2x00_rt(rt2x00dev, RT3290) ||
rt2x00_rt(rt2x00dev, RT3352) ||
rt2x00_rt(rt2x00dev, RT5390) ||
rt2x00_rt(rt2x00dev, RT5392))
rt2800_bbp_write(rt2x00dev, 68, 0x0b);
if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) {
rt2800_bbp_write(rt2x00dev, 69, 0x16);
rt2800_bbp_write(rt2x00dev, 73, 0x12);
} else if (rt2x00_rt(rt2x00dev, RT3290) ||
rt2x00_rt(rt2x00dev, RT3352) ||
rt2x00_rt(rt2x00dev, RT5390) ||
rt2x00_rt(rt2x00dev, RT5392)) {
rt2800_bbp_write(rt2x00dev, 69, 0x12);
rt2800_bbp_write(rt2x00dev, 73, 0x13);
rt2800_bbp_write(rt2x00dev, 75, 0x46);
rt2800_bbp_write(rt2x00dev, 76, 0x28);
if (rt2x00_rt(rt2x00dev, RT3290))
rt2800_bbp_write(rt2x00dev, 77, 0x58);
else
rt2800_bbp_write(rt2x00dev, 77, 0x59);
} else {
rt2800_bbp_write(rt2x00dev, 69, 0x12);
rt2800_bbp_write(rt2x00dev, 73, 0x10);
}
rt2800_bbp_write(rt2x00dev, 70, 0x0a);
if (rt2x00_rt(rt2x00dev, RT3070) ||
rt2x00_rt(rt2x00dev, RT3071) ||
rt2x00_rt(rt2x00dev, RT3090) ||
rt2x00_rt(rt2x00dev, RT3390) ||
rt2x00_rt(rt2x00dev, RT3572) ||
rt2x00_rt(rt2x00dev, RT5390) ||
rt2x00_rt(rt2x00dev, RT5392)) {
rt2800_bbp_write(rt2x00dev, 79, 0x13);
rt2800_bbp_write(rt2x00dev, 80, 0x05);
rt2800_bbp_write(rt2x00dev, 81, 0x33);
} else if (rt2800_is_305x_soc(rt2x00dev)) {
rt2800_bbp_write(rt2x00dev, 78, 0x0e);
rt2800_bbp_write(rt2x00dev, 80, 0x08);
} else if (rt2x00_rt(rt2x00dev, RT3290)) {
rt2800_bbp_write(rt2x00dev, 74, 0x0b);
rt2800_bbp_write(rt2x00dev, 79, 0x18);
rt2800_bbp_write(rt2x00dev, 80, 0x09);
rt2800_bbp_write(rt2x00dev, 81, 0x33);
} else if (rt2x00_rt(rt2x00dev, RT3352)) {
rt2800_bbp_write(rt2x00dev, 78, 0x0e);
rt2800_bbp_write(rt2x00dev, 80, 0x08);
rt2800_bbp_write(rt2x00dev, 81, 0x37);
} else {
rt2800_bbp_write(rt2x00dev, 81, 0x37);
}
rt2800_bbp_write(rt2x00dev, 82, 0x62);
if (rt2x00_rt(rt2x00dev, RT3290) ||
rt2x00_rt(rt2x00dev, RT5390) ||
rt2x00_rt(rt2x00dev, RT5392))
rt2800_bbp_write(rt2x00dev, 83, 0x7a);
else
rt2800_bbp_write(rt2x00dev, 83, 0x6a);
if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860D))
rt2800_bbp_write(rt2x00dev, 84, 0x19);
else if (rt2x00_rt(rt2x00dev, RT3290) ||
rt2x00_rt(rt2x00dev, RT5390) ||
rt2x00_rt(rt2x00dev, RT5392))
rt2800_bbp_write(rt2x00dev, 84, 0x9a);
else
rt2800_bbp_write(rt2x00dev, 84, 0x99);
if (rt2x00_rt(rt2x00dev, RT3290) ||
rt2x00_rt(rt2x00dev, RT3352) ||
rt2x00_rt(rt2x00dev, RT5390) ||
rt2x00_rt(rt2x00dev, RT5392))
rt2800_bbp_write(rt2x00dev, 86, 0x38);
else
rt2800_bbp_write(rt2x00dev, 86, 0x00);
if (rt2x00_rt(rt2x00dev, RT3352) ||
rt2x00_rt(rt2x00dev, RT5392))
rt2800_bbp_write(rt2x00dev, 88, 0x90);
rt2800_bbp_write(rt2x00dev, 91, 0x04);
if (rt2x00_rt(rt2x00dev, RT3290) ||
rt2x00_rt(rt2x00dev, RT3352) ||
rt2x00_rt(rt2x00dev, RT5390) ||
rt2x00_rt(rt2x00dev, RT5392))
rt2800_bbp_write(rt2x00dev, 92, 0x02);
else
rt2800_bbp_write(rt2x00dev, 92, 0x00);
if (rt2x00_rt(rt2x00dev, RT5392)) {
rt2800_bbp_write(rt2x00dev, 95, 0x9a);
rt2800_bbp_write(rt2x00dev, 98, 0x12);
}
if (rt2x00_rt_rev_gte(rt2x00dev, RT3070, REV_RT3070F) ||
rt2x00_rt_rev_gte(rt2x00dev, RT3071, REV_RT3071E) ||
rt2x00_rt_rev_gte(rt2x00dev, RT3090, REV_RT3090E) ||
rt2x00_rt_rev_gte(rt2x00dev, RT3390, REV_RT3390E) ||
rt2x00_rt(rt2x00dev, RT3290) ||
rt2x00_rt(rt2x00dev, RT3352) ||
rt2x00_rt(rt2x00dev, RT3572) ||
rt2x00_rt(rt2x00dev, RT5390) ||
rt2x00_rt(rt2x00dev, RT5392) ||
rt2800_is_305x_soc(rt2x00dev))
rt2800_bbp_write(rt2x00dev, 103, 0xc0);
else
rt2800_bbp_write(rt2x00dev, 103, 0x00);
if (rt2x00_rt(rt2x00dev, RT3290) ||
rt2x00_rt(rt2x00dev, RT3352) ||
rt2x00_rt(rt2x00dev, RT5390) ||
rt2x00_rt(rt2x00dev, RT5392))
rt2800_bbp_write(rt2x00dev, 104, 0x92);
if (rt2800_is_305x_soc(rt2x00dev))
rt2800_bbp_write(rt2x00dev, 105, 0x01);
else if (rt2x00_rt(rt2x00dev, RT3290))
rt2800_bbp_write(rt2x00dev, 105, 0x1c);
else if (rt2x00_rt(rt2x00dev, RT3352))
rt2800_bbp_write(rt2x00dev, 105, 0x34);
else if (rt2x00_rt(rt2x00dev, RT5390) ||
rt2x00_rt(rt2x00dev, RT5392))
rt2800_bbp_write(rt2x00dev, 105, 0x3c);
else
rt2800_bbp_write(rt2x00dev, 105, 0x05);
rt2800_init_bbp_305x_soc(rt2x00dev);
if (rt2x00_rt(rt2x00dev, RT3290) ||
rt2x00_rt(rt2x00dev, RT5390))
rt2800_bbp_write(rt2x00dev, 106, 0x03);
else if (rt2x00_rt(rt2x00dev, RT3352))
rt2800_bbp_write(rt2x00dev, 106, 0x05);
else if (rt2x00_rt(rt2x00dev, RT5392))
rt2800_bbp_write(rt2x00dev, 106, 0x12);
else
rt2800_bbp_write(rt2x00dev, 106, 0x35);
if (rt2x00_rt(rt2x00dev, RT3352))
rt2800_bbp_write(rt2x00dev, 120, 0x50);
if (rt2x00_rt(rt2x00dev, RT3290) ||
rt2x00_rt(rt2x00dev, RT5390) ||
rt2x00_rt(rt2x00dev, RT5392))
rt2800_bbp_write(rt2x00dev, 128, 0x12);
if (rt2x00_rt(rt2x00dev, RT5392)) {
rt2800_bbp_write(rt2x00dev, 134, 0xd0);
rt2800_bbp_write(rt2x00dev, 135, 0xf6);
}
if (rt2x00_rt(rt2x00dev, RT3352))
rt2800_bbp_write(rt2x00dev, 137, 0x0f);
if (rt2x00_rt(rt2x00dev, RT3071) ||
rt2x00_rt(rt2x00dev, RT3090) ||
rt2x00_rt(rt2x00dev, RT3390) ||
rt2x00_rt(rt2x00dev, RT3572) ||
rt2x00_rt(rt2x00dev, RT5390) ||
rt2x00_rt(rt2x00dev, RT5392)) {
rt2800_bbp_read(rt2x00dev, 138, &value);
rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1)
value |= 0x20;
if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1)
value &= ~0x02;
rt2800_bbp_write(rt2x00dev, 138, value);
}
if (rt2x00_rt(rt2x00dev, RT3290)) {
rt2800_bbp_write(rt2x00dev, 67, 0x24);
rt2800_bbp_write(rt2x00dev, 143, 0x04);
rt2800_bbp_write(rt2x00dev, 142, 0x99);
rt2800_bbp_write(rt2x00dev, 150, 0x30);
rt2800_bbp_write(rt2x00dev, 151, 0x2e);
rt2800_bbp_write(rt2x00dev, 152, 0x20);
rt2800_bbp_write(rt2x00dev, 153, 0x34);
rt2800_bbp_write(rt2x00dev, 154, 0x40);
rt2800_bbp_write(rt2x00dev, 155, 0x3b);
rt2800_bbp_write(rt2x00dev, 253, 0x04);
rt2800_bbp_read(rt2x00dev, 47, &value);
rt2x00_set_field8(&value, BBP47_TSSI_ADC6, 1);
rt2800_bbp_write(rt2x00dev, 47, value);
/* Use 5-bit ADC for Acquisition and 8-bit ADC for data */
rt2800_bbp_read(rt2x00dev, 3, &value);
rt2x00_set_field8(&value, BBP3_ADC_MODE_SWITCH, 1);
rt2x00_set_field8(&value, BBP3_ADC_INIT_MODE, 1);
rt2800_bbp_write(rt2x00dev, 3, value);
}
if (rt2x00_rt(rt2x00dev, RT3352)) {
rt2800_bbp_write(rt2x00dev, 163, 0xbd);
/* Set ITxBF timeout to 0x9c40=1000msec */
rt2800_bbp_write(rt2x00dev, 179, 0x02);
rt2800_bbp_write(rt2x00dev, 180, 0x00);
rt2800_bbp_write(rt2x00dev, 182, 0x40);
rt2800_bbp_write(rt2x00dev, 180, 0x01);
rt2800_bbp_write(rt2x00dev, 182, 0x9c);
rt2800_bbp_write(rt2x00dev, 179, 0x00);
/* Reprogram the inband interface to put right values in RXWI */
rt2800_bbp_write(rt2x00dev, 142, 0x04);
rt2800_bbp_write(rt2x00dev, 143, 0x3b);
rt2800_bbp_write(rt2x00dev, 142, 0x06);
rt2800_bbp_write(rt2x00dev, 143, 0xa0);
rt2800_bbp_write(rt2x00dev, 142, 0x07);
rt2800_bbp_write(rt2x00dev, 143, 0xa1);
rt2800_bbp_write(rt2x00dev, 142, 0x08);
rt2800_bbp_write(rt2x00dev, 143, 0xa2);
rt2800_bbp_write(rt2x00dev, 148, 0xc8);
}
if (rt2x00_rt(rt2x00dev, RT5390) ||
rt2x00_rt(rt2x00dev, RT5392)) {
int ant, div_mode;
rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
div_mode = rt2x00_get_field16(eeprom,
EEPROM_NIC_CONF1_ANT_DIVERSITY);
ant = (div_mode == 3) ? 1 : 0;
/* check if this is a Bluetooth combo card */
if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
u32 reg;
rt2800_register_read(rt2x00dev, GPIO_CTRL, &reg);
rt2x00_set_field32(&reg, GPIO_CTRL_DIR3, 0);
rt2x00_set_field32(&reg, GPIO_CTRL_DIR6, 0);
rt2x00_set_field32(&reg, GPIO_CTRL_VAL3, 0);
rt2x00_set_field32(&reg, GPIO_CTRL_VAL6, 0);
if (ant == 0)
rt2x00_set_field32(&reg, GPIO_CTRL_VAL3, 1);
else if (ant == 1)
rt2x00_set_field32(&reg, GPIO_CTRL_VAL6, 1);
rt2800_register_write(rt2x00dev, GPIO_CTRL, reg);
}
/* This chip has hardware antenna diversity*/
if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) {
rt2800_bbp_write(rt2x00dev, 150, 0); /* Disable Antenna Software OFDM */
rt2800_bbp_write(rt2x00dev, 151, 0); /* Disable Antenna Software CCK */
rt2800_bbp_write(rt2x00dev, 154, 0); /* Clear previously selected antenna */
}
rt2800_bbp_read(rt2x00dev, 152, &value);
if (ant == 0)
rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1);
else
rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0);
rt2800_bbp_write(rt2x00dev, 152, value);
rt2800_init_freq_calibration(rt2x00dev);
switch (rt2x00dev->chip.rt) {
case RT2860:
case RT2872:
case RT2883:
rt2800_init_bbp_28xx(rt2x00dev);
break;
case RT3070:
case RT3071:
case RT3090:
rt2800_init_bbp_30xx(rt2x00dev);
break;
case RT3290:
rt2800_init_bbp_3290(rt2x00dev);
break;
case RT3352:
rt2800_init_bbp_3352(rt2x00dev);
break;
case RT3390:
rt2800_init_bbp_3390(rt2x00dev);
break;
case RT3572:
rt2800_init_bbp_3572(rt2x00dev);
break;
case RT5390:
case RT5392:
rt2800_init_bbp_53xx(rt2x00dev);
break;
case RT5592:
rt2800_init_bbp_5592(rt2x00dev);
return;
}
for (i = 0; i < EEPROM_BBP_SIZE; i++) {
@ -4337,8 +4535,6 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
rt2800_bbp_write(rt2x00dev, reg_id, value);
}
}
return 0;
}
static void rt2800_led_open_drain_enable(struct rt2x00_dev *rt2x00dev)
@ -5189,9 +5385,11 @@ int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev)
}
msleep(1);
if (unlikely(rt2800_init_bbp(rt2x00dev)))
if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev) ||
rt2800_wait_bbp_ready(rt2x00dev)))
return -EIO;
rt2800_init_bbp(rt2x00dev);
rt2800_init_rfcsr(rt2x00dev);
if (rt2x00_is_usb(rt2x00dev) &&

View File

@ -1014,7 +1014,7 @@ static void rt2800pci_txstatus_interrupt(struct rt2x00_dev *rt2x00dev)
* Since we have only one producer and one consumer we don't
* need to lock the kfifo.
*/
for (i = 0; i < rt2x00dev->ops->tx->entry_num; i++) {
for (i = 0; i < rt2x00dev->tx->limit; i++) {
rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status);
if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID))

View File

@ -327,7 +327,7 @@ static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev)
* this limit so reduce the number to prevent errors.
*/
rt2x00_set_field32(&reg, USB_DMA_CFG_RX_BULK_AGG_LIMIT,
((rt2x00dev->ops->rx->entry_num * DATA_FRAME_SIZE)
((rt2x00dev->rx->limit * DATA_FRAME_SIZE)
/ 1024) - 3);
rt2x00_set_field32(&reg, USB_DMA_CFG_RX_BULK_EN, 1);
rt2x00_set_field32(&reg, USB_DMA_CFG_TX_BULK_EN, 1);

View File

@ -1077,7 +1077,7 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev)
*/
int kfifo_size =
roundup_pow_of_two(rt2x00dev->ops->tx_queues *
rt2x00dev->ops->tx->entry_num *
rt2x00dev->tx->limit *
sizeof(u32));
status = kfifo_alloc(&rt2x00dev->txstatus_fifo, kfifo_size,
@ -1300,23 +1300,6 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
rt2x00dev->hw->wiphy->addr_mask[ETH_ALEN - 1] =
(rt2x00dev->ops->max_ap_intf - 1);
/*
* Determine which operating modes are supported, all modes
* which require beaconing, depend on the availability of
* beacon entries.
*/
rt2x00dev->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
if (rt2x00dev->ops->bcn->entry_num > 0)
rt2x00dev->hw->wiphy->interface_modes |=
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_AP) |
#ifdef CONFIG_MAC80211_MESH
BIT(NL80211_IFTYPE_MESH_POINT) |
#endif
BIT(NL80211_IFTYPE_WDS);
rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
/*
* Initialize work.
*/
@ -1347,6 +1330,23 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)
if (retval)
goto exit;
/*
* Determine which operating modes are supported, all modes
* which require beaconing, depend on the availability of
* beacon entries.
*/
rt2x00dev->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
if (rt2x00dev->ops->bcn->entry_num > 0)
rt2x00dev->hw->wiphy->interface_modes |=
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_AP) |
#ifdef CONFIG_MAC80211_MESH
BIT(NL80211_IFTYPE_MESH_POINT) |
#endif
BIT(NL80211_IFTYPE_WDS);
rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
/*
* Initialize ieee80211 structure.
*/

View File

@ -105,11 +105,13 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops)
goto exit_release_regions;
}
pci_enable_msi(pci_dev);
hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw);
if (!hw) {
rt2x00_probe_err("Failed to allocate hardware\n");
retval = -ENOMEM;
goto exit_release_regions;
goto exit_disable_msi;
}
pci_set_drvdata(pci_dev, hw);
@ -150,6 +152,9 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops)
exit_free_device:
ieee80211_free_hw(hw);
exit_disable_msi:
pci_disable_msi(pci_dev);
exit_release_regions:
pci_release_regions(pci_dev);
@ -174,6 +179,8 @@ void rt2x00pci_remove(struct pci_dev *pci_dev)
rt2x00pci_free_reg(rt2x00dev);
ieee80211_free_hw(hw);
pci_disable_msi(pci_dev);
/*
* Free the PCI device data.
*/

View File

@ -2175,7 +2175,7 @@ static void rt61pci_txdone(struct rt2x00_dev *rt2x00dev)
* that the TX_STA_FIFO stack has a size of 16. We stick to our
* tx ring size for now.
*/
for (i = 0; i < rt2x00dev->ops->tx->entry_num; i++) {
for (i = 0; i < rt2x00dev->tx->limit; i++) {
rt2x00mmio_register_read(rt2x00dev, STA_CSR4, &reg);
if (!rt2x00_get_field32(reg, STA_CSR4_VALID))
break;

View File

@ -0,0 +1,46 @@
/*
* Copyright (C) ST-Ericsson SA 2011
*
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>
* License terms: GNU General Public License (GPL) version 2
*/
#ifndef CW1200_PLAT_H_INCLUDED
#define CW1200_PLAT_H_INCLUDED
struct cw1200_platform_data_spi {
u8 spi_bits_per_word; /* REQUIRED */
u16 ref_clk; /* REQUIRED (in KHz) */
/* All others are optional */
bool have_5ghz;
const struct resource *reset; /* GPIO to RSTn signal */
const struct resource *powerup; /* GPIO to POWERUP signal */
int (*power_ctrl)(const struct cw1200_platform_data_spi *pdata,
bool enable); /* Control 3v3 / 1v8 supply */
int (*clk_ctrl)(const struct cw1200_platform_data_spi *pdata,
bool enable); /* Control CLK32K */
const u8 *macaddr; /* if NULL, use cw1200_mac_template module parameter */
const char *sdd_file; /* if NULL, will use default for detected hw type */
};
struct cw1200_platform_data_sdio {
u16 ref_clk; /* REQUIRED (in KHz) */
/* All others are optional */
const struct resource *irq; /* if using GPIO for IRQ */
bool have_5ghz;
bool no_nptb; /* SDIO hardware does not support non-power-of-2-blocksizes */
const struct resource *reset; /* GPIO to RSTn signal */
const struct resource *powerup; /* GPIO to POWERUP signal */
int (*power_ctrl)(const struct cw1200_platform_data_sdio *pdata,
bool enable); /* Control 3v3 / 1v8 supply */
int (*clk_ctrl)(const struct cw1200_platform_data_sdio *pdata,
bool enable); /* Control CLK32K */
const u8 *macaddr; /* if NULL, use cw1200_mac_template module parameter */
const char *sdd_file; /* if NULL, will use default for detected hw type */
};
const void *cw1200_get_platform_data(void);
#endif /* CW1200_PLAT_H_INCLUDED */

View File

@ -1829,6 +1829,15 @@ enum ieee80211_key_len {
WLAN_KEY_LEN_AES_CMAC = 16,
};
#define IEEE80211_WEP_IV_LEN 4
#define IEEE80211_WEP_ICV_LEN 4
#define IEEE80211_CCMP_HDR_LEN 8
#define IEEE80211_CCMP_MIC_LEN 8
#define IEEE80211_CCMP_PN_LEN 6
#define IEEE80211_TKIP_IV_LEN 8
#define IEEE80211_TKIP_ICV_LEN 4
#define IEEE80211_CMAC_PN_LEN 6
/* Public action codes */
enum ieee80211_pub_actioncode {
WLAN_PUB_ACTION_EXT_CHANSW_ANN = 4,

View File

@ -172,6 +172,7 @@
#define SSB_SPROMSIZE_WORDS_R4 220
#define SSB_SPROMSIZE_BYTES_R123 (SSB_SPROMSIZE_WORDS_R123 * sizeof(u16))
#define SSB_SPROMSIZE_BYTES_R4 (SSB_SPROMSIZE_WORDS_R4 * sizeof(u16))
#define SSB_SPROMSIZE_WORDS_R10 230
#define SSB_SPROM_BASE1 0x1000
#define SSB_SPROM_BASE31 0x0800
#define SSB_SPROM_REVISION 0x007E

View File

@ -753,6 +753,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
* @STATION_INFO_LOCAL_PM: @local_pm filled
* @STATION_INFO_PEER_PM: @peer_pm filled
* @STATION_INFO_NONPEER_PM: @nonpeer_pm filled
* @STATION_INFO_CHAIN_SIGNAL: @chain_signal filled
* @STATION_INFO_CHAIN_SIGNAL_AVG: @chain_signal_avg filled
*/
enum station_info_flags {
STATION_INFO_INACTIVE_TIME = 1<<0,
@ -781,6 +783,8 @@ enum station_info_flags {
STATION_INFO_NONPEER_PM = 1<<23,
STATION_INFO_RX_BYTES64 = 1<<24,
STATION_INFO_TX_BYTES64 = 1<<25,
STATION_INFO_CHAIN_SIGNAL = 1<<26,
STATION_INFO_CHAIN_SIGNAL_AVG = 1<<27,
};
/**
@ -857,6 +861,8 @@ struct sta_bss_parameters {
u16 beacon_interval;
};
#define IEEE80211_MAX_CHAINS 4
/**
* struct station_info - station information
*
@ -874,6 +880,9 @@ struct sta_bss_parameters {
* For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_.
* @signal_avg: Average signal strength, type depends on the wiphy's signal_type.
* For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_.
* @chains: bitmask for filled values in @chain_signal, @chain_signal_avg
* @chain_signal: per-chain signal strength of last received packet in dBm
* @chain_signal_avg: per-chain signal strength average in dBm
* @txrate: current unicast bitrate from this station
* @rxrate: current unicast bitrate to this station
* @rx_packets: packets received from this station
@ -909,6 +918,11 @@ struct station_info {
u8 plink_state;
s8 signal;
s8 signal_avg;
u8 chains;
s8 chain_signal[IEEE80211_MAX_CHAINS];
s8 chain_signal_avg[IEEE80211_MAX_CHAINS];
struct rate_info txrate;
struct rate_info rxrate;
u32 rx_packets;
@ -1147,6 +1161,7 @@ struct mesh_config {
* @sync_method: which synchronization method to use
* @path_sel_proto: which path selection protocol to use
* @path_metric: which metric to use
* @auth_id: which authentication method this mesh is using
* @ie: vendor information elements (optional)
* @ie_len: length of vendor information elements
* @is_authenticated: this mesh requires authentication
@ -1165,6 +1180,7 @@ struct mesh_setup {
u8 sync_method;
u8 path_sel_proto;
u8 path_metric;
u8 auth_id;
const u8 *ie;
u8 ie_len;
bool is_authenticated;
@ -1241,6 +1257,7 @@ struct cfg80211_ssid {
* @scan_start: time (in jiffies) when the scan started
* @wdev: the wireless device to scan for
* @aborted: (internal) scan request was notified as aborted
* @notified: (internal) scan request was notified as done or aborted
* @no_cck: used to send probe requests at non CCK rate in 2GHz band
*/
struct cfg80211_scan_request {
@ -1258,7 +1275,7 @@ struct cfg80211_scan_request {
/* internal */
struct wiphy *wiphy;
unsigned long scan_start;
bool aborted;
bool aborted, notified;
bool no_cck;
/* keep last */
@ -1850,7 +1867,9 @@ struct cfg80211_update_ft_ies_params {
* @get_mpath: get a mesh path for the given parameters
* @dump_mpath: dump mesh path callback -- resume dump at index @idx
* @join_mesh: join the mesh network with the specified parameters
* (invoked with the wireless_dev mutex held)
* @leave_mesh: leave the current mesh network
* (invoked with the wireless_dev mutex held)
*
* @get_mesh_config: Get the current mesh configuration
*
@ -1877,20 +1896,28 @@ struct cfg80211_update_ft_ies_params {
* the scan/scan_done bracket too.
*
* @auth: Request to authenticate with the specified peer
* (invoked with the wireless_dev mutex held)
* @assoc: Request to (re)associate with the specified peer
* (invoked with the wireless_dev mutex held)
* @deauth: Request to deauthenticate from the specified peer
* (invoked with the wireless_dev mutex held)
* @disassoc: Request to disassociate from the specified peer
* (invoked with the wireless_dev mutex held)
*
* @connect: Connect to the ESS with the specified parameters. When connected,
* call cfg80211_connect_result() with status code %WLAN_STATUS_SUCCESS.
* If the connection fails for some reason, call cfg80211_connect_result()
* with the status from the AP.
* (invoked with the wireless_dev mutex held)
* @disconnect: Disconnect from the BSS/ESS.
* (invoked with the wireless_dev mutex held)
*
* @join_ibss: Join the specified IBSS (or create if necessary). Once done, call
* cfg80211_ibss_joined(), also call that function when changing BSSID due
* to a merge.
* (invoked with the wireless_dev mutex held)
* @leave_ibss: Leave the IBSS.
* (invoked with the wireless_dev mutex held)
*
* @set_mcast_rate: Set the specified multicast rate (only if vif is in ADHOC or
* MESH mode)
@ -2556,6 +2583,9 @@ struct wiphy_wowlan_support {
* may request, if implemented.
*
* @wowlan: WoWLAN support information
* @wowlan_config: current WoWLAN configuration; this should usually not be
* used since access to it is necessarily racy, use the parameter passed
* to the suspend() operation instead.
*
* @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features.
* @ht_capa_mod_mask: Specify what ht_cap values can be over-ridden.
@ -2623,6 +2653,7 @@ struct wiphy {
#ifdef CONFIG_PM
struct wiphy_wowlan_support wowlan;
struct cfg80211_wowlan *wowlan_config;
#endif
u16 max_remain_on_channel_duration;
@ -2834,7 +2865,8 @@ struct cfg80211_cached_keys;
* by cfg80211 on change_interface
* @mgmt_registrations: list of registrations for management frames
* @mgmt_registrations_lock: lock for the list
* @mtx: mutex used to lock data in this struct
* @mtx: mutex used to lock data in this struct, may be used by drivers
* and some API functions require it held
* @cleanup_work: work struct used for cleanup that can't be done directly
* @beacon_interval: beacon interval used on this device for transmitting
* beacons, 0 when not valid
@ -2858,8 +2890,6 @@ struct wireless_dev {
struct mutex mtx;
struct work_struct cleanup_work;
bool use_4addr, p2p_started;
u8 address[ETH_ALEN] __aligned(sizeof(u16));
@ -2989,6 +3019,15 @@ struct ieee80211_rate *
ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
u32 basic_rates, int bitrate);
/**
* ieee80211_mandatory_rates - get mandatory rates for a given band
* @sband: the band to look for rates in
*
* This function returns a bitmap of the mandatory rates for the given
* band, bits are set according to the rate position in the bitrates array.
*/
u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband);
/*
* Radiotap parsing functions -- for controlled injection support
*
@ -3400,7 +3439,8 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
* This function is called whenever an authentication has been processed in
* station mode. The driver is required to call either this function or
* cfg80211_send_auth_timeout() to indicate the result of cfg80211_ops::auth()
* call. This function may sleep.
* call. This function may sleep. The caller must hold the corresponding wdev's
* mutex.
*/
void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len);
@ -3409,7 +3449,8 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len);
* @dev: network device
* @addr: The MAC address of the device with which the authentication timed out
*
* This function may sleep.
* This function may sleep. The caller must hold the corresponding wdev's
* mutex.
*/
void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr);
@ -3424,7 +3465,8 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr);
* This function is called whenever a (re)association response has been
* processed in station mode. The driver is required to call either this
* function or cfg80211_send_assoc_timeout() to indicate the result of
* cfg80211_ops::assoc() call. This function may sleep.
* cfg80211_ops::assoc() call. This function may sleep. The caller must hold
* the corresponding wdev's mutex.
*/
void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,
const u8 *buf, size_t len);
@ -3434,7 +3476,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,
* @dev: network device
* @addr: The MAC address of the device with which the association timed out
*
* This function may sleep.
* This function may sleep. The caller must hold the corresponding wdev's mutex.
*/
void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr);
@ -3446,20 +3488,11 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr);
*
* This function is called whenever deauthentication has been processed in
* station mode. This includes both received deauthentication frames and
* locally generated ones. This function may sleep.
* locally generated ones. This function may sleep. The caller must hold the
* corresponding wdev's mutex.
*/
void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len);
/**
* __cfg80211_send_deauth - notification of processed deauthentication
* @dev: network device
* @buf: deauthentication frame (header + body)
* @len: length of the frame data
*
* Like cfg80211_send_deauth(), but doesn't take the wdev lock.
*/
void __cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len);
/**
* cfg80211_send_disassoc - notification of processed disassociation
* @dev: network device
@ -3468,21 +3501,11 @@ void __cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len);
*
* This function is called whenever disassociation has been processed in
* station mode. This includes both received disassociation frames and locally
* generated ones. This function may sleep.
* generated ones. This function may sleep. The caller must hold the
* corresponding wdev's mutex.
*/
void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len);
/**
* __cfg80211_send_disassoc - notification of processed disassociation
* @dev: network device
* @buf: disassociation response frame (header + body)
* @len: length of the frame data
*
* Like cfg80211_send_disassoc(), but doesn't take the wdev lock.
*/
void __cfg80211_send_disassoc(struct net_device *dev, const u8 *buf,
size_t len);
/**
* cfg80211_send_unprot_deauth - notification of unprotected deauthentication
* @dev: network device
@ -4153,6 +4176,7 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
* cfg80211_crit_proto_stopped() - indicate critical protocol stopped by driver.
*
* @wdev: the wireless device for which critical protocol is stopped.
* @gfp: allocation flags
*
* This function can be called by the driver to indicate it has reverted
* operation back to normal. One reason could be that the duration given

View File

@ -269,6 +269,7 @@ enum ieee80211_radiotap_type {
#define IEEE80211_RADIOTAP_MCS_HAVE_GI 0x04
#define IEEE80211_RADIOTAP_MCS_HAVE_FMT 0x08
#define IEEE80211_RADIOTAP_MCS_HAVE_FEC 0x10
#define IEEE80211_RADIOTAP_MCS_HAVE_STBC 0x20
#define IEEE80211_RADIOTAP_MCS_BW_MASK 0x03
#define IEEE80211_RADIOTAP_MCS_BW_20 0
@ -278,6 +279,12 @@ enum ieee80211_radiotap_type {
#define IEEE80211_RADIOTAP_MCS_SGI 0x04
#define IEEE80211_RADIOTAP_MCS_FMT_GF 0x08
#define IEEE80211_RADIOTAP_MCS_FEC_LDPC 0x10
#define IEEE80211_RADIOTAP_MCS_STBC_MASK 0x60
#define IEEE80211_RADIOTAP_MCS_STBC_1 1
#define IEEE80211_RADIOTAP_MCS_STBC_2 2
#define IEEE80211_RADIOTAP_MCS_STBC_3 3
#define IEEE80211_RADIOTAP_MCS_STBC_SHIFT 5
/* For IEEE80211_RADIOTAP_AMPDU_STATUS */
#define IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN 0x0001

View File

@ -805,6 +805,7 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
* on this subframe
* @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC
* is stored in the @ampdu_delimiter_crc field)
* @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3
*/
enum mac80211_rx_flags {
RX_FLAG_MMIC_ERROR = BIT(0),
@ -832,8 +833,11 @@ enum mac80211_rx_flags {
RX_FLAG_80MHZ = BIT(23),
RX_FLAG_80P80MHZ = BIT(24),
RX_FLAG_160MHZ = BIT(25),
RX_FLAG_STBC_MASK = BIT(26) | BIT(27),
};
#define RX_FLAG_STBC_SHIFT 26
/**
* struct ieee80211_rx_status - receive status
*
@ -850,6 +854,10 @@ enum mac80211_rx_flags {
* @signal: signal strength when receiving this frame, either in dBm, in dB or
* unspecified depending on the hardware capabilities flags
* @IEEE80211_HW_SIGNAL_*
* @chains: bitmask of receive chains for which separate signal strength
* values were filled.
* @chain_signal: per-chain signal strength, in dBm (unlike @signal, doesn't
* support dB or unspecified units)
* @antenna: antenna used
* @rate_idx: index of data rate into band's supported rates or MCS index if
* HT or VHT is used (%RX_FLAG_HT/%RX_FLAG_VHT)
@ -881,6 +889,8 @@ struct ieee80211_rx_status {
u8 band;
u8 antenna;
s8 signal;
u8 chains;
s8 chain_signal[IEEE80211_MAX_CHAINS];
u8 ampdu_delimiter_crc;
u8 vendor_radiotap_align;
u8 vendor_radiotap_oui[3];
@ -1235,7 +1245,7 @@ enum ieee80211_sta_rx_bandwidth {
* struct ieee80211_sta_rates - station rate selection table
*
* @rcu_head: RCU head used for freeing the table on update
* @rates: transmit rates/flags to be used by default.
* @rate: transmit rates/flags to be used by default.
* Overriding entries per-packet is possible by using cb tx control.
*/
struct ieee80211_sta_rates {
@ -1276,7 +1286,7 @@ struct ieee80211_sta_rates {
* notifications and capabilities. The value is only valid after
* the station moves to associated state.
* @smps_mode: current SMPS mode (off, static or dynamic)
* @tx_rates: rate control selection table
* @rates: rate control selection table
*/
struct ieee80211_sta {
u32 supp_rates[IEEE80211_NUM_BANDS];
@ -3043,7 +3053,8 @@ void ieee80211_napi_complete(struct ieee80211_hw *hw);
* This function may not be called in IRQ context. Calls to this function
* for a single hardware must be synchronized against each other. Calls to
* this function, ieee80211_rx_ni() and ieee80211_rx_irqsafe() may not be
* mixed for a single hardware.
* mixed for a single hardware. Must not run concurrently with
* ieee80211_tx_status() or ieee80211_tx_status_ni().
*
* In process context use instead ieee80211_rx_ni().
*
@ -3059,7 +3070,8 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb);
* (internally defers to a tasklet.)
*
* Calls to this function, ieee80211_rx() or ieee80211_rx_ni() may not
* be mixed for a single hardware.
* be mixed for a single hardware.Must not run concurrently with
* ieee80211_tx_status() or ieee80211_tx_status_ni().
*
* @hw: the hardware this frame came in on
* @skb: the buffer to receive, owned by mac80211 after this call
@ -3073,7 +3085,8 @@ void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb);
* (internally disables bottom halves).
*
* Calls to this function, ieee80211_rx() and ieee80211_rx_irqsafe() may
* not be mixed for a single hardware.
* not be mixed for a single hardware. Must not run concurrently with
* ieee80211_tx_status() or ieee80211_tx_status_ni().
*
* @hw: the hardware this frame came in on
* @skb: the buffer to receive, owned by mac80211 after this call
@ -3196,7 +3209,8 @@ void ieee80211_get_tx_rates(struct ieee80211_vif *vif,
* This function may not be called in IRQ context. Calls to this function
* for a single hardware must be synchronized against each other. Calls
* to this function, ieee80211_tx_status_ni() and ieee80211_tx_status_irqsafe()
* may not be mixed for a single hardware.
* may not be mixed for a single hardware. Must not run concurrently with
* ieee80211_rx() or ieee80211_rx_ni().
*
* @hw: the hardware the frame was transmitted by
* @skb: the frame that was transmitted, owned by mac80211 after this call

View File

@ -27,6 +27,8 @@
#include <linux/types.h>
#define NL80211_GENL_NAME "nl80211"
/**
* DOC: Station handling
*
@ -1429,6 +1431,11 @@ enum nl80211_commands {
* @NL80211_ATTR_MAX_CRIT_PROT_DURATION: duration in milliseconds in which
* the connection should have increased reliability (u16).
*
* @NL80211_ATTR_PEER_AID: Association ID for the peer TDLS station (u16).
* This is similar to @NL80211_ATTR_STA_AID but with a difference of being
* allowed to be used with the first @NL80211_CMD_SET_STATION command to
* update a TDLS peer STA entry.
*
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@ -1727,6 +1734,8 @@ enum nl80211_attrs {
NL80211_ATTR_CRIT_PROT_ID,
NL80211_ATTR_MAX_CRIT_PROT_DURATION,
NL80211_ATTR_PEER_AID,
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@ -1991,6 +2000,10 @@ enum nl80211_sta_bss_param {
* @NL80211_STA_INFO_PEER_PM: peer mesh STA link-specific power mode
* @NL80211_STA_INFO_NONPEER_PM: neighbor mesh STA power save mode towards
* non-peer STA
* @NL80211_STA_INFO_CHAIN_SIGNAL: per-chain signal strength of last PPDU
* Contains a nested array of signal strength attributes (u8, dBm)
* @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average
* Same format as NL80211_STA_INFO_CHAIN_SIGNAL.
* @__NL80211_STA_INFO_AFTER_LAST: internal
* @NL80211_STA_INFO_MAX: highest possible station info attribute
*/
@ -2020,6 +2033,8 @@ enum nl80211_sta_info {
NL80211_STA_INFO_NONPEER_PM,
NL80211_STA_INFO_RX_BYTES64,
NL80211_STA_INFO_TX_BYTES64,
NL80211_STA_INFO_CHAIN_SIGNAL,
NL80211_STA_INFO_CHAIN_SIGNAL_AVG,
/* keep last */
__NL80211_STA_INFO_AFTER_LAST,
@ -2637,6 +2652,10 @@ enum nl80211_meshconf_params {
* @NL80211_MESH_SETUP_USERSPACE_MPM: Enable this option if userspace will
* implement an MPM which handles peer allocation and state.
*
* @NL80211_MESH_SETUP_AUTH_PROTOCOL: Inform the kernel of the authentication
* method (u8, as defined in IEEE 8.4.2.100.6, e.g. 0x1 for SAE).
* Default is no authentication method required.
*
* @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number
*
* @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use
@ -2650,6 +2669,7 @@ enum nl80211_mesh_setup_params {
NL80211_MESH_SETUP_USERSPACE_AMPE,
NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC,
NL80211_MESH_SETUP_USERSPACE_MPM,
NL80211_MESH_SETUP_AUTH_PROTOCOL,
/* keep last */
__NL80211_MESH_SETUP_ATTR_AFTER_LAST,

Some files were not shown because too many files have changed in this diff Show More