Merge remote-tracking branch 'wireless-next/master' into HEAD
Merge to get the wil6210 changes that a cfg80211 change needs. A conflict in drivers/net/wireless/ath/ath9k/init.c was just whitespace changes. Also fix a semantic conflict due to cw1200 using WoWLAN which I had modified in my tree. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
commit
51217cee3a
|
@ -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)
|
||||
|
|
|
@ -72,12 +72,12 @@ fail:
|
|||
* R/W ops.
|
||||
**************************************************/
|
||||
|
||||
static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom)
|
||||
static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom,
|
||||
size_t words)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < SSB_SPROMSIZE_WORDS_R4; i++)
|
||||
sprom[i] = bcma_read16(bus->drv_cc.core,
|
||||
offset + (i * 2));
|
||||
for (i = 0; i < words; i++)
|
||||
sprom[i] = bcma_read16(bus->drv_cc.core, offset + (i * 2));
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
|
@ -124,29 +124,29 @@ static inline u8 bcma_crc8(u8 crc, u8 data)
|
|||
return t[crc ^ data];
|
||||
}
|
||||
|
||||
static u8 bcma_sprom_crc(const u16 *sprom)
|
||||
static u8 bcma_sprom_crc(const u16 *sprom, size_t words)
|
||||
{
|
||||
int word;
|
||||
u8 crc = 0xFF;
|
||||
|
||||
for (word = 0; word < SSB_SPROMSIZE_WORDS_R4 - 1; word++) {
|
||||
for (word = 0; word < words - 1; word++) {
|
||||
crc = bcma_crc8(crc, sprom[word] & 0x00FF);
|
||||
crc = bcma_crc8(crc, (sprom[word] & 0xFF00) >> 8);
|
||||
}
|
||||
crc = bcma_crc8(crc, sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & 0x00FF);
|
||||
crc = bcma_crc8(crc, sprom[words - 1] & 0x00FF);
|
||||
crc ^= 0xFF;
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
static int bcma_sprom_check_crc(const u16 *sprom)
|
||||
static int bcma_sprom_check_crc(const u16 *sprom, size_t words)
|
||||
{
|
||||
u8 crc;
|
||||
u8 expected_crc;
|
||||
u16 tmp;
|
||||
|
||||
crc = bcma_sprom_crc(sprom);
|
||||
tmp = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_CRC;
|
||||
crc = bcma_sprom_crc(sprom, words);
|
||||
tmp = sprom[words - 1] & SSB_SPROM_REVISION_CRC;
|
||||
expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT;
|
||||
if (crc != expected_crc)
|
||||
return -EPROTO;
|
||||
|
@ -154,21 +154,25 @@ static int bcma_sprom_check_crc(const u16 *sprom)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int bcma_sprom_valid(const u16 *sprom)
|
||||
static int bcma_sprom_valid(struct bcma_bus *bus, const u16 *sprom,
|
||||
size_t words)
|
||||
{
|
||||
u16 revision;
|
||||
int err;
|
||||
|
||||
err = bcma_sprom_check_crc(sprom);
|
||||
err = bcma_sprom_check_crc(sprom, words);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_REV;
|
||||
if (revision != 8 && revision != 9) {
|
||||
revision = sprom[words - 1] & SSB_SPROM_REVISION_REV;
|
||||
if (revision != 8 && revision != 9 && revision != 10) {
|
||||
pr_err("Unsupported SPROM revision: %d\n", revision);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
bus->sprom.revision = revision;
|
||||
bcma_debug(bus, "Found SPROM revision %d\n", revision);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -208,9 +212,6 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
|
|||
BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
|
||||
ARRAY_SIZE(bus->sprom.core_pwr_info));
|
||||
|
||||
bus->sprom.revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] &
|
||||
SSB_SPROM_REVISION_REV;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i];
|
||||
*(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v);
|
||||
|
@ -502,7 +503,6 @@ static bool bcma_sprom_onchip_available(struct bcma_bus *bus)
|
|||
case BCMA_CHIP_ID_BCM4331:
|
||||
present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT;
|
||||
break;
|
||||
|
||||
case BCMA_CHIP_ID_BCM43224:
|
||||
case BCMA_CHIP_ID_BCM43225:
|
||||
/* for these chips OTP is always available */
|
||||
|
@ -550,7 +550,9 @@ int bcma_sprom_get(struct bcma_bus *bus)
|
|||
{
|
||||
u16 offset = BCMA_CC_SPROM;
|
||||
u16 *sprom;
|
||||
int err = 0;
|
||||
size_t sprom_sizes[] = { SSB_SPROMSIZE_WORDS_R4,
|
||||
SSB_SPROMSIZE_WORDS_R10, };
|
||||
int i, err = 0;
|
||||
|
||||
if (!bus->drv_cc.core)
|
||||
return -EOPNOTSUPP;
|
||||
|
@ -579,32 +581,37 @@ int bcma_sprom_get(struct bcma_bus *bus)
|
|||
}
|
||||
}
|
||||
|
||||
sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
|
||||
GFP_KERNEL);
|
||||
if (!sprom)
|
||||
return -ENOMEM;
|
||||
|
||||
if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 ||
|
||||
bus->chipinfo.id == BCMA_CHIP_ID_BCM43431)
|
||||
bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false);
|
||||
|
||||
bcma_debug(bus, "SPROM offset 0x%x\n", offset);
|
||||
bcma_sprom_read(bus, offset, sprom);
|
||||
for (i = 0; i < ARRAY_SIZE(sprom_sizes); i++) {
|
||||
size_t words = sprom_sizes[i];
|
||||
|
||||
sprom = kcalloc(words, sizeof(u16), GFP_KERNEL);
|
||||
if (!sprom)
|
||||
return -ENOMEM;
|
||||
|
||||
bcma_sprom_read(bus, offset, sprom, words);
|
||||
err = bcma_sprom_valid(bus, sprom, words);
|
||||
if (!err)
|
||||
break;
|
||||
|
||||
kfree(sprom);
|
||||
}
|
||||
|
||||
if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 ||
|
||||
bus->chipinfo.id == BCMA_CHIP_ID_BCM43431)
|
||||
bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true);
|
||||
|
||||
err = bcma_sprom_valid(sprom);
|
||||
if (err) {
|
||||
bcma_warn(bus, "invalid sprom read from the PCIe card, try to use fallback sprom\n");
|
||||
bcma_warn(bus, "Invalid SPROM read from the PCIe card, trying to use fallback SPROM\n");
|
||||
err = bcma_fill_sprom_with_fallback(bus, &bus->sprom);
|
||||
goto out;
|
||||
} else {
|
||||
bcma_sprom_extract_r8(bus, sprom);
|
||||
kfree(sprom);
|
||||
}
|
||||
|
||||
bcma_sprom_extract_r8(bus, sprom);
|
||||
|
||||
out:
|
||||
kfree(sprom);
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -57,3 +57,5 @@ obj-$(CONFIG_MWIFIEX) += mwifiex/
|
|||
|
||||
obj-$(CONFIG_BRCMFMAC) += brcm80211/
|
||||
obj-$(CONFIG_BRCMSMAC) += brcm80211/
|
||||
|
||||
obj-$(CONFIG_CW1200) += cw1200/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -118,10 +118,10 @@ static void ath9k_ani_restart(struct ath_hw *ah)
|
|||
{
|
||||
struct ar5416AniState *aniState;
|
||||
|
||||
if (!DO_ANI(ah))
|
||||
if (!ah->curchan)
|
||||
return;
|
||||
|
||||
aniState = &ah->curchan->ani;
|
||||
aniState = &ah->ani;
|
||||
aniState->listenTime = 0;
|
||||
|
||||
ENABLE_REGWRITE_BUFFER(ah);
|
||||
|
@ -143,7 +143,7 @@ static void ath9k_ani_restart(struct ath_hw *ah)
|
|||
static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel,
|
||||
bool scan)
|
||||
{
|
||||
struct ar5416AniState *aniState = &ah->curchan->ani;
|
||||
struct ar5416AniState *aniState = &ah->ani;
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
const struct ani_ofdm_level_entry *entry_ofdm;
|
||||
const struct ani_cck_level_entry *entry_cck;
|
||||
|
@ -195,10 +195,10 @@ static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah)
|
|||
{
|
||||
struct ar5416AniState *aniState;
|
||||
|
||||
if (!DO_ANI(ah))
|
||||
if (!ah->curchan)
|
||||
return;
|
||||
|
||||
aniState = &ah->curchan->ani;
|
||||
aniState = &ah->ani;
|
||||
|
||||
if (aniState->ofdmNoiseImmunityLevel < ATH9K_ANI_OFDM_MAX_LEVEL)
|
||||
ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel + 1, false);
|
||||
|
@ -210,7 +210,7 @@ static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah)
|
|||
static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel,
|
||||
bool scan)
|
||||
{
|
||||
struct ar5416AniState *aniState = &ah->curchan->ani;
|
||||
struct ar5416AniState *aniState = &ah->ani;
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
const struct ani_ofdm_level_entry *entry_ofdm;
|
||||
const struct ani_cck_level_entry *entry_cck;
|
||||
|
@ -251,10 +251,10 @@ static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah)
|
|||
{
|
||||
struct ar5416AniState *aniState;
|
||||
|
||||
if (!DO_ANI(ah))
|
||||
if (!ah->curchan)
|
||||
return;
|
||||
|
||||
aniState = &ah->curchan->ani;
|
||||
aniState = &ah->ani;
|
||||
|
||||
if (aniState->cckNoiseImmunityLevel < ATH9K_ANI_CCK_MAX_LEVEL)
|
||||
ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel + 1,
|
||||
|
@ -269,7 +269,7 @@ static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah)
|
|||
{
|
||||
struct ar5416AniState *aniState;
|
||||
|
||||
aniState = &ah->curchan->ani;
|
||||
aniState = &ah->ani;
|
||||
|
||||
/* lower OFDM noise immunity */
|
||||
if (aniState->ofdmNoiseImmunityLevel > 0 &&
|
||||
|
@ -292,12 +292,12 @@ static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah)
|
|||
*/
|
||||
void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
|
||||
{
|
||||
struct ar5416AniState *aniState = &ah->curchan->ani;
|
||||
struct ar5416AniState *aniState = &ah->ani;
|
||||
struct ath9k_channel *chan = ah->curchan;
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
int ofdm_nil, cck_nil;
|
||||
|
||||
if (!DO_ANI(ah))
|
||||
if (!ah->curchan)
|
||||
return;
|
||||
|
||||
BUG_ON(aniState == NULL);
|
||||
|
@ -380,7 +380,7 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
|
|||
static bool ath9k_hw_ani_read_counters(struct ath_hw *ah)
|
||||
{
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
struct ar5416AniState *aniState = &ah->curchan->ani;
|
||||
struct ar5416AniState *aniState = &ah->ani;
|
||||
u32 phyCnt1, phyCnt2;
|
||||
int32_t listenTime;
|
||||
|
||||
|
@ -415,10 +415,10 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan)
|
|||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
u32 ofdmPhyErrRate, cckPhyErrRate;
|
||||
|
||||
if (!DO_ANI(ah))
|
||||
if (!ah->curchan)
|
||||
return;
|
||||
|
||||
aniState = &ah->curchan->ani;
|
||||
aniState = &ah->ani;
|
||||
if (!ath9k_hw_ani_read_counters(ah))
|
||||
return;
|
||||
|
||||
|
@ -490,32 +490,22 @@ EXPORT_SYMBOL(ath9k_hw_disable_mib_counters);
|
|||
void ath9k_hw_ani_init(struct ath_hw *ah)
|
||||
{
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
int i;
|
||||
struct ar5416AniState *ani = &ah->ani;
|
||||
|
||||
ath_dbg(common, ANI, "Initialize ANI\n");
|
||||
|
||||
ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH;
|
||||
ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW;
|
||||
|
||||
ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH;
|
||||
ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ah->channels); i++) {
|
||||
struct ath9k_channel *chan = &ah->channels[i];
|
||||
struct ar5416AniState *ani = &chan->ani;
|
||||
|
||||
ani->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
|
||||
|
||||
ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
|
||||
|
||||
ani->mrcCCK = AR_SREV_9300_20_OR_LATER(ah) ? true : false;
|
||||
|
||||
ani->ofdmsTurn = true;
|
||||
|
||||
ani->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG;
|
||||
ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL;
|
||||
ani->ofdmNoiseImmunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL;
|
||||
}
|
||||
ani->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
|
||||
ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
|
||||
ani->mrcCCK = AR_SREV_9300_20_OR_LATER(ah) ? true : false;
|
||||
ani->ofdmsTurn = true;
|
||||
ani->ofdmWeakSigDetect = true;
|
||||
ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL;
|
||||
ani->ofdmNoiseImmunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL;
|
||||
|
||||
/*
|
||||
* since we expect some ongoing maintenance on the tables, let's sanity
|
||||
|
@ -524,9 +514,6 @@ void ath9k_hw_ani_init(struct ath_hw *ah)
|
|||
ah->aniperiod = ATH9K_ANI_PERIOD;
|
||||
ah->config.ani_poll_interval = ATH9K_ANI_POLLINTERVAL;
|
||||
|
||||
if (ah->config.enable_ani)
|
||||
ah->proc_phyerr |= HAL_PROCESS_ANI;
|
||||
|
||||
ath9k_ani_restart(ah);
|
||||
ath9k_enable_mib_counters(ah);
|
||||
}
|
||||
|
|
|
@ -17,10 +17,6 @@
|
|||
#ifndef ANI_H
|
||||
#define ANI_H
|
||||
|
||||
#define HAL_PROCESS_ANI 0x00000001
|
||||
|
||||
#define DO_ANI(ah) (((ah)->proc_phyerr & HAL_PROCESS_ANI) && ah->curchan)
|
||||
|
||||
#define BEACON_RSSI(ahp) (ahp->stats.avgbrssi)
|
||||
|
||||
/* units are errors per second */
|
||||
|
@ -38,11 +34,7 @@
|
|||
#define ATH9K_ANI_CCK_TRIG_LOW 300
|
||||
|
||||
#define ATH9K_ANI_NOISE_IMMUNE_LVL 4
|
||||
#define ATH9K_ANI_USE_OFDM_WEAK_SIG true
|
||||
#define ATH9K_ANI_CCK_WEAK_SIG_THR false
|
||||
|
||||
#define ATH9K_ANI_SPUR_IMMUNE_LVL 3
|
||||
|
||||
#define ATH9K_ANI_FIRSTEP_LVL 2
|
||||
|
||||
#define ATH9K_ANI_RSSI_THR_HIGH 40
|
||||
|
@ -111,7 +103,7 @@ struct ar5416AniState {
|
|||
u8 mrcCCK;
|
||||
u8 spurImmunityLevel;
|
||||
u8 firstepLevel;
|
||||
u8 ofdmWeakSigDetect;
|
||||
bool ofdmWeakSigDetect;
|
||||
u32 listenTime;
|
||||
u32 ofdmPhyErrCount;
|
||||
u32 cckPhyErrCount;
|
||||
|
@ -119,8 +111,6 @@ struct ar5416AniState {
|
|||
};
|
||||
|
||||
struct ar5416Stats {
|
||||
u32 ast_ani_niup;
|
||||
u32 ast_ani_nidown;
|
||||
u32 ast_ani_spurup;
|
||||
u32 ast_ani_spurdown;
|
||||
u32 ast_ani_ofdmon;
|
||||
|
|
|
@ -931,7 +931,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah,
|
|||
{
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
struct ath9k_channel *chan = ah->curchan;
|
||||
struct ar5416AniState *aniState = &chan->ani;
|
||||
struct ar5416AniState *aniState = &ah->ani;
|
||||
s32 value, value2;
|
||||
|
||||
switch (cmd & ah->ani_function) {
|
||||
|
@ -1207,7 +1207,7 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah)
|
|||
{
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
struct ath9k_channel *chan = ah->curchan;
|
||||
struct ar5416AniState *aniState = &chan->ani;
|
||||
struct ar5416AniState *aniState = &ah->ani;
|
||||
struct ath9k_ani_default *iniDef;
|
||||
u32 val;
|
||||
|
||||
|
@ -1251,7 +1251,7 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah)
|
|||
/* these levels just got reset to defaults by the INI */
|
||||
aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
|
||||
aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
|
||||
aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG;
|
||||
aniState->ofdmWeakSigDetect = true;
|
||||
aniState->mrcCCK = false; /* not available on pre AR9003 */
|
||||
}
|
||||
|
||||
|
|
|
@ -38,10 +38,6 @@ static int ar9002_hw_init_mode_regs(struct ath_hw *ah)
|
|||
else
|
||||
INIT_INI_ARRAY(&ah->iniPcieSerdes,
|
||||
ar9280PciePhy_clkreq_always_on_L1_9280);
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
INIT_INI_ARRAY(&ah->iniPcieSerdesWow,
|
||||
ar9280PciePhy_awow);
|
||||
#endif
|
||||
|
||||
if (AR_SREV_9287_11_OR_LATER(ah)) {
|
||||
INIT_INI_ARRAY(&ah->iniModes, ar9287Modes_9287_1_1);
|
||||
|
|
|
@ -925,20 +925,6 @@ static const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = {
|
|||
{0x00004044, 0x00000000},
|
||||
};
|
||||
|
||||
static const u32 ar9280PciePhy_awow[][2] = {
|
||||
/* Addr allmodes */
|
||||
{0x00004040, 0x9248fd00},
|
||||
{0x00004040, 0x24924924},
|
||||
{0x00004040, 0xa8000019},
|
||||
{0x00004040, 0x13160820},
|
||||
{0x00004040, 0xe5980560},
|
||||
{0x00004040, 0xc01dcffd},
|
||||
{0x00004040, 0x1aaabe41},
|
||||
{0x00004040, 0xbe105554},
|
||||
{0x00004040, 0x00043007},
|
||||
{0x00004044, 0x00000000},
|
||||
};
|
||||
|
||||
static const u32 ar9285Modes_9285_1_2[][5] = {
|
||||
/* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
|
||||
{0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160},
|
||||
|
|
|
@ -469,6 +469,7 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs,
|
|||
|
||||
rxs->rs_status = 0;
|
||||
rxs->rs_flags = 0;
|
||||
rxs->flag = 0;
|
||||
|
||||
rxs->rs_datalen = rxsp->status2 & AR_DataLen;
|
||||
rxs->rs_tstamp = rxsp->status3;
|
||||
|
@ -493,8 +494,8 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs,
|
|||
rxs->rs_isaggr = (rxsp->status11 & AR_RxAggr) ? 1 : 0;
|
||||
rxs->rs_moreaggr = (rxsp->status11 & AR_RxMoreAggr) ? 1 : 0;
|
||||
rxs->rs_antenna = (MS(rxsp->status4, AR_RxAntenna) & 0x7);
|
||||
rxs->rs_flags = (rxsp->status4 & AR_GI) ? ATH9K_RX_GI : 0;
|
||||
rxs->rs_flags |= (rxsp->status4 & AR_2040) ? ATH9K_RX_2040 : 0;
|
||||
rxs->flag |= (rxsp->status4 & AR_GI) ? RX_FLAG_SHORT_GI : 0;
|
||||
rxs->flag |= (rxsp->status4 & AR_2040) ? RX_FLAG_40MHZ : 0;
|
||||
|
||||
rxs->evm0 = rxsp->status6;
|
||||
rxs->evm1 = rxsp->status7;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -904,7 +904,7 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
|
|||
{
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
struct ath9k_channel *chan = ah->curchan;
|
||||
struct ar5416AniState *aniState = &chan->ani;
|
||||
struct ar5416AniState *aniState = &ah->ani;
|
||||
s32 value, value2;
|
||||
|
||||
switch (cmd & ah->ani_function) {
|
||||
|
@ -1172,7 +1172,7 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah)
|
|||
struct ath9k_ani_default *iniDef;
|
||||
u32 val;
|
||||
|
||||
aniState = &ah->curchan->ani;
|
||||
aniState = &ah->ani;
|
||||
iniDef = &aniState->iniDef;
|
||||
|
||||
ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n",
|
||||
|
@ -1213,7 +1213,7 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah)
|
|||
/* these levels just got reset to defaults by the INI */
|
||||
aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
|
||||
aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
|
||||
aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG;
|
||||
aniState->ofdmWeakSigDetect = true;
|
||||
aniState->mrcCCK = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -173,25 +173,69 @@ static const struct file_operations fops_rx_chainmask = {
|
|||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t read_file_disable_ani(struct file *file, char __user *user_buf,
|
||||
static ssize_t read_file_ani(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct ath_softc *sc = file->private_data;
|
||||
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
||||
char buf[32];
|
||||
unsigned int len;
|
||||
struct ath_hw *ah = sc->sc_ah;
|
||||
unsigned int len = 0, size = 1024;
|
||||
ssize_t retval = 0;
|
||||
char *buf;
|
||||
|
||||
len = sprintf(buf, "%d\n", common->disable_ani);
|
||||
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
||||
buf = kzalloc(size, GFP_KERNEL);
|
||||
if (buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (common->disable_ani) {
|
||||
len += snprintf(buf + len, size - len, "%s: %s\n",
|
||||
"ANI", "DISABLED");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
len += snprintf(buf + len, size - len, "%15s: %s\n",
|
||||
"ANI", "ENABLED");
|
||||
len += snprintf(buf + len, size - len, "%15s: %u\n",
|
||||
"ANI RESET", ah->stats.ast_ani_reset);
|
||||
len += snprintf(buf + len, size - len, "%15s: %u\n",
|
||||
"SPUR UP", ah->stats.ast_ani_spurup);
|
||||
len += snprintf(buf + len, size - len, "%15s: %u\n",
|
||||
"SPUR DOWN", ah->stats.ast_ani_spurup);
|
||||
len += snprintf(buf + len, size - len, "%15s: %u\n",
|
||||
"OFDM WS-DET ON", ah->stats.ast_ani_ofdmon);
|
||||
len += snprintf(buf + len, size - len, "%15s: %u\n",
|
||||
"OFDM WS-DET OFF", ah->stats.ast_ani_ofdmoff);
|
||||
len += snprintf(buf + len, size - len, "%15s: %u\n",
|
||||
"MRC-CCK ON", ah->stats.ast_ani_ccklow);
|
||||
len += snprintf(buf + len, size - len, "%15s: %u\n",
|
||||
"MRC-CCK OFF", ah->stats.ast_ani_cckhigh);
|
||||
len += snprintf(buf + len, size - len, "%15s: %u\n",
|
||||
"FIR-STEP UP", ah->stats.ast_ani_stepup);
|
||||
len += snprintf(buf + len, size - len, "%15s: %u\n",
|
||||
"FIR-STEP DOWN", ah->stats.ast_ani_stepdown);
|
||||
len += snprintf(buf + len, size - len, "%15s: %u\n",
|
||||
"INV LISTENTIME", ah->stats.ast_ani_lneg_or_lzero);
|
||||
len += snprintf(buf + len, size - len, "%15s: %u\n",
|
||||
"OFDM ERRORS", ah->stats.ast_ani_ofdmerrs);
|
||||
len += snprintf(buf + len, size - len, "%15s: %u\n",
|
||||
"CCK ERRORS", ah->stats.ast_ani_cckerrs);
|
||||
exit:
|
||||
if (len > size)
|
||||
len = size;
|
||||
|
||||
retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
||||
kfree(buf);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t write_file_disable_ani(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
static ssize_t write_file_ani(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct ath_softc *sc = file->private_data;
|
||||
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
||||
unsigned long disable_ani;
|
||||
unsigned long ani;
|
||||
char buf[32];
|
||||
ssize_t len;
|
||||
|
||||
|
@ -200,12 +244,15 @@ static ssize_t write_file_disable_ani(struct file *file,
|
|||
return -EFAULT;
|
||||
|
||||
buf[len] = '\0';
|
||||
if (strict_strtoul(buf, 0, &disable_ani))
|
||||
if (strict_strtoul(buf, 0, &ani))
|
||||
return -EINVAL;
|
||||
|
||||
common->disable_ani = !!disable_ani;
|
||||
if (ani < 0 || ani > 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (disable_ani) {
|
||||
common->disable_ani = !ani;
|
||||
|
||||
if (common->disable_ani) {
|
||||
clear_bit(SC_OP_ANI_RUN, &sc->sc_flags);
|
||||
ath_stop_ani(sc);
|
||||
} else {
|
||||
|
@ -215,9 +262,9 @@ static ssize_t write_file_disable_ani(struct file *file,
|
|||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations fops_disable_ani = {
|
||||
.read = read_file_disable_ani,
|
||||
.write = write_file_disable_ani,
|
||||
static const struct file_operations fops_ani = {
|
||||
.read = read_file_ani,
|
||||
.write = write_file_ani,
|
||||
.open = simple_open,
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = default_llseek,
|
||||
|
@ -738,8 +785,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 +816,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 +929,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 +952,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 +1477,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)
|
||||
|
@ -2051,8 +1766,8 @@ int ath9k_init_debug(struct ath_hw *ah)
|
|||
sc->debug.debugfs_phy, sc, &fops_rx_chainmask);
|
||||
debugfs_create_file("tx_chainmask", S_IRUSR | S_IWUSR,
|
||||
sc->debug.debugfs_phy, sc, &fops_tx_chainmask);
|
||||
debugfs_create_file("disable_ani", S_IRUSR | S_IWUSR,
|
||||
sc->debug.debugfs_phy, sc, &fops_disable_ani);
|
||||
debugfs_create_file("ani", S_IRUSR | S_IWUSR,
|
||||
sc->debug.debugfs_phy, sc, &fops_ani);
|
||||
debugfs_create_bool("paprd", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
|
||||
&sc->sc_ah->config.enable_paprd);
|
||||
debugfs_create_file("regidx", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
|
||||
|
@ -2087,11 +1802,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,
|
||||
|
|
|
@ -251,56 +251,10 @@ struct ath_stats {
|
|||
u32 reset[__RESET_TYPE_MAX];
|
||||
};
|
||||
|
||||
#define ATH_DBG_MAX_SAMPLES 10
|
||||
struct ath_dbg_bb_mac_samp {
|
||||
u32 dma_dbg_reg_vals[ATH9K_NUM_DMA_DEBUG_REGS];
|
||||
u32 pcu_obs, pcu_cr, noise;
|
||||
struct {
|
||||
u64 jiffies;
|
||||
int8_t rssi_ctl0;
|
||||
int8_t rssi_ctl1;
|
||||
int8_t rssi_ctl2;
|
||||
int8_t rssi_ext0;
|
||||
int8_t rssi_ext1;
|
||||
int8_t rssi_ext2;
|
||||
int8_t rssi;
|
||||
bool isok;
|
||||
u8 rts_fail_cnt;
|
||||
u8 data_fail_cnt;
|
||||
u8 rateindex;
|
||||
u8 qid;
|
||||
u8 tid;
|
||||
u32 ba_low;
|
||||
u32 ba_high;
|
||||
} ts[ATH_DBG_MAX_SAMPLES];
|
||||
struct {
|
||||
u64 jiffies;
|
||||
int8_t rssi_ctl0;
|
||||
int8_t rssi_ctl1;
|
||||
int8_t rssi_ctl2;
|
||||
int8_t rssi_ext0;
|
||||
int8_t rssi_ext1;
|
||||
int8_t rssi_ext2;
|
||||
int8_t rssi;
|
||||
bool is_mybeacon;
|
||||
u8 antenna;
|
||||
u8 rate;
|
||||
} rs[ATH_DBG_MAX_SAMPLES];
|
||||
struct ath_cycle_counters cc;
|
||||
struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS];
|
||||
};
|
||||
|
||||
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 +313,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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -810,8 +814,7 @@ void ath9k_htc_ani_work(struct work_struct *work)
|
|||
}
|
||||
|
||||
/* Verify whether we must check ANI */
|
||||
if (ah->config.enable_ani &&
|
||||
(timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
|
||||
if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
|
||||
aniflag = true;
|
||||
common->ani.checkani_timer = timestamp;
|
||||
}
|
||||
|
@ -841,8 +844,7 @@ set_timer:
|
|||
* short calibration and long calibration.
|
||||
*/
|
||||
cal_interval = ATH_LONG_CALINTERVAL;
|
||||
if (ah->config.enable_ani)
|
||||
cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
|
||||
cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
|
||||
if (!common->ani.caldone)
|
||||
cal_interval = min(cal_interval, (u32)short_cal_interval);
|
||||
|
||||
|
@ -1052,6 +1054,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 +1089,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 +1140,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 +1532,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 +1546,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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -452,7 +452,6 @@ static void ath9k_hw_init_config(struct ath_hw *ah)
|
|||
ah->config.pcie_clock_req = 0;
|
||||
ah->config.pcie_waen = 0;
|
||||
ah->config.analog_shiftreg = 1;
|
||||
ah->config.enable_ani = true;
|
||||
|
||||
for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
|
||||
ah->config.spurchans[i][0] = AR_NO_SPUR;
|
||||
|
@ -549,8 +548,7 @@ static int ath9k_hw_post_init(struct ath_hw *ah)
|
|||
ah->eep_ops->get_eeprom_ver(ah),
|
||||
ah->eep_ops->get_eeprom_rev(ah));
|
||||
|
||||
if (ah->config.enable_ani)
|
||||
ath9k_hw_ani_init(ah);
|
||||
ath9k_hw_ani_init(ah);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1245,10 +1243,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 +2244,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 -
|
||||
|
@ -2595,13 +2593,8 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
|
|||
pCap->hw_caps |= ATH9K_HW_CAP_RTT;
|
||||
}
|
||||
|
||||
if (AR_SREV_9280_20_OR_LATER(ah)) {
|
||||
pCap->hw_caps |= ATH9K_HW_WOW_DEVICE_CAPABLE |
|
||||
ATH9K_HW_WOW_PATTERN_MATCH_EXACT;
|
||||
|
||||
if (AR_SREV_9280(ah))
|
||||
pCap->hw_caps |= ATH9K_HW_WOW_PATTERN_MATCH_DWORD;
|
||||
}
|
||||
if (AR_SREV_9462(ah))
|
||||
pCap->hw_caps |= ATH9K_HW_WOW_DEVICE_CAPABLE;
|
||||
|
||||
if (AR_SREV_9300_20_OR_LATER(ah) &&
|
||||
ah->eep_ops->get_eeprom(ah, EEP_PAPRD))
|
||||
|
|
|
@ -246,9 +246,7 @@ enum ath9k_hw_caps {
|
|||
ATH9K_HW_CAP_MCI = BIT(15),
|
||||
ATH9K_HW_CAP_DFS = BIT(16),
|
||||
ATH9K_HW_WOW_DEVICE_CAPABLE = BIT(17),
|
||||
ATH9K_HW_WOW_PATTERN_MATCH_EXACT = BIT(18),
|
||||
ATH9K_HW_WOW_PATTERN_MATCH_DWORD = BIT(19),
|
||||
ATH9K_HW_CAP_PAPRD = BIT(20),
|
||||
ATH9K_HW_CAP_PAPRD = BIT(18),
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -291,7 +289,6 @@ struct ath9k_ops_config {
|
|||
u32 ofdm_trig_high;
|
||||
u32 cck_trig_high;
|
||||
u32 cck_trig_low;
|
||||
u32 enable_ani;
|
||||
u32 enable_paprd;
|
||||
int serialize_regmode;
|
||||
bool rx_intr_mitigation;
|
||||
|
@ -423,7 +420,6 @@ struct ath9k_hw_cal_data {
|
|||
|
||||
struct ath9k_channel {
|
||||
struct ieee80211_channel *chan;
|
||||
struct ar5416AniState ani;
|
||||
u16 channel;
|
||||
u32 channelFlags;
|
||||
u32 chanmode;
|
||||
|
@ -854,10 +850,10 @@ struct ath_hw {
|
|||
u32 globaltxtimeout;
|
||||
|
||||
/* ANI */
|
||||
u32 proc_phyerr;
|
||||
u32 aniperiod;
|
||||
enum ath9k_ani_cmd ani_function;
|
||||
u32 ani_skip_count;
|
||||
struct ar5416AniState ani;
|
||||
|
||||
#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
|
||||
struct ath_btcoex_hw btcoex_hw;
|
||||
|
@ -882,9 +878,6 @@ struct ath_hw {
|
|||
struct ar5416IniArray iniBank6;
|
||||
struct ar5416IniArray iniAddac;
|
||||
struct ar5416IniArray iniPcieSerdes;
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
struct ar5416IniArray iniPcieSerdesWow;
|
||||
#endif
|
||||
struct ar5416IniArray iniPcieSerdesLowPower;
|
||||
struct ar5416IniArray iniModesFastClock;
|
||||
struct ar5416IniArray iniAdditional;
|
||||
|
@ -1165,8 +1158,6 @@ static inline void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#define ATH9K_CLOCK_RATE_CCK 22
|
||||
#define ATH9K_CLOCK_RATE_5GHZ_OFDM 40
|
||||
#define ATH9K_CLOCK_RATE_2GHZ_OFDM 44
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/ath9k_platform.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/relay.h>
|
||||
#include <net/ieee80211_radiotap.h>
|
||||
|
||||
#include "ath9k.h"
|
||||
|
||||
|
@ -613,9 +614,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);
|
||||
|
@ -778,12 +776,19 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
|
|||
IEEE80211_HW_REPORTS_TX_ACK_STATUS |
|
||||
IEEE80211_HW_SUPPORTS_RC_TABLE;
|
||||
|
||||
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT)
|
||||
hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
|
||||
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
|
||||
hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
|
||||
|
||||
if (AR_SREV_9280_20_OR_LATER(ah))
|
||||
hw->radiotap_mcs_details |=
|
||||
IEEE80211_RADIOTAP_MCS_HAVE_STBC;
|
||||
}
|
||||
|
||||
if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || ath9k_modparam_nohwcrypt)
|
||||
hw->flags |= IEEE80211_HW_MFP_CAPABLE;
|
||||
|
||||
hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
|
||||
|
||||
hw->wiphy->interface_modes =
|
||||
BIT(NL80211_IFTYPE_P2P_GO) |
|
||||
BIT(NL80211_IFTYPE_P2P_CLIENT) |
|
||||
|
@ -804,14 +809,12 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
|
|||
hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
if ((ah->caps.hw_caps & ATH9K_HW_WOW_DEVICE_CAPABLE) &&
|
||||
device_can_wakeup(sc->dev))
|
||||
hw->wiphy->wowlan = &ath9k_wowlan_support;
|
||||
|
||||
atomic_set(&sc->wow_sleep_proc_intr, -1);
|
||||
atomic_set(&sc->wow_got_bmiss_intr, -1);
|
||||
|
||||
#endif
|
||||
|
||||
hw->queues = 4;
|
||||
|
|
|
@ -390,9 +390,7 @@ void ath_ani_calibrate(unsigned long data)
|
|||
}
|
||||
|
||||
/* Verify whether we must check ANI */
|
||||
if (sc->sc_ah->config.enable_ani
|
||||
&& (timestamp - common->ani.checkani_timer) >=
|
||||
ah->config.ani_poll_interval) {
|
||||
if ((timestamp - common->ani.checkani_timer) >= ah->config.ani_poll_interval) {
|
||||
aniflag = true;
|
||||
common->ani.checkani_timer = timestamp;
|
||||
}
|
||||
|
@ -418,7 +416,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:
|
||||
|
@ -428,9 +425,7 @@ set_timer:
|
|||
* short calibration and long calibration.
|
||||
*/
|
||||
cal_interval = ATH_LONG_CALINTERVAL;
|
||||
if (sc->sc_ah->config.enable_ani)
|
||||
cal_interval = min(cal_interval,
|
||||
(u32)ah->config.ani_poll_interval);
|
||||
cal_interval = min(cal_interval, (u32)ah->config.ani_poll_interval);
|
||||
if (!common->ani.caldone)
|
||||
cal_interval = min(cal_interval, (u32)short_cal_interval);
|
||||
|
||||
|
|
|
@ -547,6 +547,7 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
|
|||
|
||||
rs->rs_status = 0;
|
||||
rs->rs_flags = 0;
|
||||
rs->flag = 0;
|
||||
|
||||
rs->rs_datalen = ads.ds_rxstatus1 & AR_DataLen;
|
||||
rs->rs_tstamp = ads.AR_RcvTimestamp;
|
||||
|
@ -586,10 +587,17 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
|
|||
rs->rs_moreaggr =
|
||||
(ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0;
|
||||
rs->rs_antenna = MS(ads.ds_rxstatus3, AR_RxAntenna);
|
||||
rs->rs_flags =
|
||||
(ads.ds_rxstatus3 & AR_GI) ? ATH9K_RX_GI : 0;
|
||||
rs->rs_flags |=
|
||||
(ads.ds_rxstatus3 & AR_2040) ? ATH9K_RX_2040 : 0;
|
||||
|
||||
/* directly mapped flags for ieee80211_rx_status */
|
||||
rs->flag |=
|
||||
(ads.ds_rxstatus3 & AR_GI) ? RX_FLAG_SHORT_GI : 0;
|
||||
rs->flag |=
|
||||
(ads.ds_rxstatus3 & AR_2040) ? RX_FLAG_40MHZ : 0;
|
||||
if (AR_SREV_9280_20_OR_LATER(ah))
|
||||
rs->flag |=
|
||||
(ads.ds_rxstatus3 & AR_STBC) ?
|
||||
/* we can only Nss=1 STBC */
|
||||
(1 << RX_FLAG_STBC_SHIFT) : 0;
|
||||
|
||||
if (ads.ds_rxstatus8 & AR_PreDelimCRCErr)
|
||||
rs->rs_flags |= ATH9K_RX_DELIM_CRC_PRE;
|
||||
|
|
|
@ -149,6 +149,7 @@ struct ath_rx_status {
|
|||
u32 evm2;
|
||||
u32 evm3;
|
||||
u32 evm4;
|
||||
u32 flag; /* see enum mac80211_rx_flags */
|
||||
};
|
||||
|
||||
struct ath_htc_rx_status {
|
||||
|
@ -533,7 +534,8 @@ struct ar5416_desc {
|
|||
#define AR_2040 0x00000002
|
||||
#define AR_Parallel40 0x00000004
|
||||
#define AR_Parallel40_S 2
|
||||
#define AR_RxStatusRsvd30 0x000000f8
|
||||
#define AR_STBC 0x00000008 /* on ar9280 and later */
|
||||
#define AR_RxStatusRsvd30 0x000000f0
|
||||
#define AR_RxAntenna 0xffffff00
|
||||
#define AR_RxAntenna_S 8
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
@ -2004,7 +2003,6 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
|
|||
{
|
||||
struct ath_hw *ah = sc->sc_ah;
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
struct ath9k_hw_capabilities *pcaps = &ah->caps;
|
||||
int pattern_count = 0;
|
||||
int i, byte_cnt;
|
||||
u8 dis_deauth_pattern[MAX_PATTERN_SIZE];
|
||||
|
@ -2074,36 +2072,9 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
|
|||
|
||||
/* Create Disassociate pattern mask */
|
||||
|
||||
if (pcaps->hw_caps & ATH9K_HW_WOW_PATTERN_MATCH_EXACT) {
|
||||
|
||||
if (pcaps->hw_caps & ATH9K_HW_WOW_PATTERN_MATCH_DWORD) {
|
||||
/*
|
||||
* for AR9280, because of hardware limitation, the
|
||||
* first 4 bytes have to be matched for all patterns.
|
||||
* the mask for disassociation and de-auth pattern
|
||||
* matching need to enable the first 4 bytes.
|
||||
* also the duration field needs to be filled.
|
||||
*/
|
||||
dis_deauth_mask[0] = 0xf0;
|
||||
|
||||
/*
|
||||
* fill in duration field
|
||||
FIXME: what is the exact value ?
|
||||
*/
|
||||
dis_deauth_pattern[2] = 0xff;
|
||||
dis_deauth_pattern[3] = 0xff;
|
||||
} else {
|
||||
dis_deauth_mask[0] = 0xfe;
|
||||
}
|
||||
|
||||
dis_deauth_mask[1] = 0x03;
|
||||
dis_deauth_mask[2] = 0xc0;
|
||||
} else {
|
||||
dis_deauth_mask[0] = 0xef;
|
||||
dis_deauth_mask[1] = 0x3f;
|
||||
dis_deauth_mask[2] = 0x00;
|
||||
dis_deauth_mask[3] = 0xfc;
|
||||
}
|
||||
dis_deauth_mask[0] = 0xfe;
|
||||
dis_deauth_mask[1] = 0x03;
|
||||
dis_deauth_mask[2] = 0xc0;
|
||||
|
||||
ath_dbg(common, WOW, "Adding disassoc/deauth patterns for WoW\n");
|
||||
|
||||
|
@ -2339,15 +2310,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 = {
|
||||
|
|
|
@ -868,10 +868,7 @@ static int ath9k_process_rate(struct ath_common *common,
|
|||
if (rx_stats->rs_rate & 0x80) {
|
||||
/* HT rate */
|
||||
rxs->flag |= RX_FLAG_HT;
|
||||
if (rx_stats->rs_flags & ATH9K_RX_2040)
|
||||
rxs->flag |= RX_FLAG_40MHZ;
|
||||
if (rx_stats->rs_flags & ATH9K_RX_GI)
|
||||
rxs->flag |= RX_FLAG_SHORT_GI;
|
||||
rxs->flag |= rx_stats->flag;
|
||||
rxs->rate_idx = rx_stats->rs_rate & 0x7f;
|
||||
return 0;
|
||||
}
|
||||
|
@ -958,11 +955,11 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
|
|||
if (rx_stats->rs_more)
|
||||
return 0;
|
||||
|
||||
ath9k_process_rssi(common, hw, hdr, rx_stats);
|
||||
|
||||
if (ath9k_process_rate(common, hw, rx_stats, rx_status))
|
||||
return -EINVAL;
|
||||
|
||||
ath9k_process_rssi(common, hw, hdr, rx_stats);
|
||||
|
||||
rx_status->band = hw->conf.chandef.chan->band;
|
||||
rx_status->freq = hw->conf.chandef.chan->center_freq;
|
||||
rx_status->signal = ah->noise + rx_stats->rs_rssi;
|
||||
|
|
|
@ -34,17 +34,6 @@ const char *ath9k_hw_wow_event_to_string(u32 wow_event)
|
|||
}
|
||||
EXPORT_SYMBOL(ath9k_hw_wow_event_to_string);
|
||||
|
||||
static void ath9k_hw_config_serdes_wow_sleep(struct ath_hw *ah)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ah->iniPcieSerdesWow.ia_rows; i++)
|
||||
REG_WRITE(ah, INI_RA(&ah->iniPcieSerdesWow, i, 0),
|
||||
INI_RA(&ah->iniPcieSerdesWow, i, 1));
|
||||
|
||||
usleep_range(1000, 1500);
|
||||
}
|
||||
|
||||
static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah)
|
||||
{
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
|
@ -58,15 +47,8 @@ static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah)
|
|||
ath_err(common, "Failed to stop Rx DMA in 10ms AR_CR=0x%08x AR_DIAG_SW=0x%08x\n",
|
||||
REG_READ(ah, AR_CR), REG_READ(ah, AR_DIAG_SW));
|
||||
return;
|
||||
} else {
|
||||
if (!AR_SREV_9300_20_OR_LATER(ah))
|
||||
REG_WRITE(ah, AR_RXDP, 0x0);
|
||||
}
|
||||
|
||||
/* AR9280 WoW has sleep issue, do not set it to sleep */
|
||||
if (AR_SREV_9280_20(ah))
|
||||
return;
|
||||
|
||||
REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT);
|
||||
}
|
||||
|
||||
|
@ -84,27 +66,16 @@ static void ath9k_wow_create_keep_alive_pattern(struct ath_hw *ah)
|
|||
|
||||
/* set the transmit buffer */
|
||||
ctl[0] = (KAL_FRAME_LEN | (MAX_RATE_POWER << 16));
|
||||
|
||||
if (!(AR_SREV_9300_20_OR_LATER(ah)))
|
||||
ctl[0] += (KAL_ANTENNA_MODE << 25);
|
||||
|
||||
ctl[1] = 0;
|
||||
ctl[3] = 0xb; /* OFDM_6M hardware value for this rate */
|
||||
ctl[4] = 0;
|
||||
ctl[7] = (ah->txchainmask) << 2;
|
||||
|
||||
if (AR_SREV_9300_20_OR_LATER(ah))
|
||||
ctl[2] = 0xf << 16; /* tx_tries 0 */
|
||||
else
|
||||
ctl[2] = 0x7 << 16; /* tx_tries 0 */
|
||||
|
||||
ctl[2] = 0xf << 16; /* tx_tries 0 */
|
||||
|
||||
for (i = 0; i < KAL_NUM_DESC_WORDS; i++)
|
||||
REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
|
||||
|
||||
/* for AR9300 family 13 descriptor words */
|
||||
if (AR_SREV_9300_20_OR_LATER(ah))
|
||||
REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
|
||||
REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
|
||||
|
||||
data_word[0] = (KAL_FRAME_TYPE << 2) | (KAL_FRAME_SUB_TYPE << 4) |
|
||||
(KAL_TO_DS << 8) | (KAL_DURATION_ID << 16);
|
||||
|
@ -183,9 +154,6 @@ void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern,
|
|||
|
||||
ah->wow_event_mask |= BIT(pattern_count + AR_WOW_PAT_FOUND_SHIFT);
|
||||
|
||||
if (!AR_SREV_9285_12_OR_LATER(ah))
|
||||
return;
|
||||
|
||||
if (pattern_count < 4) {
|
||||
/* Pattern 0-3 uses AR_WOW_LENGTH1 register */
|
||||
set = (pattern_len & AR_WOW_LENGTH_MAX) <<
|
||||
|
@ -207,6 +175,7 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
|
|||
{
|
||||
u32 wow_status = 0;
|
||||
u32 val = 0, rval;
|
||||
|
||||
/*
|
||||
* read the WoW status register to know
|
||||
* the wakeup reason
|
||||
|
@ -223,19 +192,14 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
|
|||
val &= ah->wow_event_mask;
|
||||
|
||||
if (val) {
|
||||
|
||||
if (val & AR_WOW_MAGIC_PAT_FOUND)
|
||||
wow_status |= AH_WOW_MAGIC_PATTERN_EN;
|
||||
|
||||
if (AR_WOW_PATTERN_FOUND(val))
|
||||
wow_status |= AH_WOW_USER_PATTERN_EN;
|
||||
|
||||
if (val & AR_WOW_KEEP_ALIVE_FAIL)
|
||||
wow_status |= AH_WOW_LINK_CHANGE;
|
||||
|
||||
if (val & AR_WOW_BEACON_FAIL)
|
||||
wow_status |= AH_WOW_BEACON_MISS;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -254,17 +218,6 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
|
|||
REG_WRITE(ah, AR_WOW_PATTERN,
|
||||
AR_WOW_CLEAR_EVENTS(REG_READ(ah, AR_WOW_PATTERN)));
|
||||
|
||||
/*
|
||||
* tie reset register for AR9002 family of chipsets
|
||||
* NB: not tieing it back might have some repurcussions.
|
||||
*/
|
||||
|
||||
if (!AR_SREV_9300_20_OR_LATER(ah)) {
|
||||
REG_SET_BIT(ah, AR_WA, AR_WA_UNTIE_RESET_EN |
|
||||
AR_WA_POR_SHORT | AR_WA_RESET_EN);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* restore the beacon threshold to init value
|
||||
*/
|
||||
|
@ -277,8 +230,7 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
|
|||
* reset to our Chip's Power On Reset so that any PCI-E
|
||||
* reset from the bus will not reset our chip
|
||||
*/
|
||||
|
||||
if (AR_SREV_9280_20_OR_LATER(ah) && ah->is_pciexpress)
|
||||
if (ah->is_pciexpress)
|
||||
ath9k_hw_configpcipowersave(ah, false);
|
||||
|
||||
ah->wow_event_mask = 0;
|
||||
|
@ -298,7 +250,6 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
|
|||
* are from the 'pattern_enable' in this function and
|
||||
* 'pattern_count' of ath9k_hw_wow_apply_pattern()
|
||||
*/
|
||||
|
||||
wow_event_mask = ah->wow_event_mask;
|
||||
|
||||
/*
|
||||
|
@ -306,50 +257,15 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
|
|||
* WOW sleep, we do want the Reset from the PCI-E to disturb
|
||||
* our hw state
|
||||
*/
|
||||
|
||||
if (ah->is_pciexpress) {
|
||||
|
||||
/*
|
||||
* we need to untie the internal POR (power-on-reset)
|
||||
* to the external PCI-E reset. We also need to tie
|
||||
* the PCI-E Phy reset to the PCI-E reset.
|
||||
*/
|
||||
|
||||
if (AR_SREV_9300_20_OR_LATER(ah)) {
|
||||
set = AR_WA_RESET_EN | AR_WA_POR_SHORT;
|
||||
clr = AR_WA_UNTIE_RESET_EN | AR_WA_D3_L1_DISABLE;
|
||||
REG_RMW(ah, AR_WA, set, clr);
|
||||
} else {
|
||||
if (AR_SREV_9285(ah) || AR_SREV_9287(ah))
|
||||
set = AR9285_WA_DEFAULT;
|
||||
else
|
||||
set = AR9280_WA_DEFAULT;
|
||||
|
||||
/*
|
||||
* In AR9280 and AR9285, bit 14 in WA register
|
||||
* (disable L1) should only be set when device
|
||||
* enters D3 state and be cleared when device
|
||||
* comes back to D0
|
||||
*/
|
||||
|
||||
if (ah->config.pcie_waen & AR_WA_D3_L1_DISABLE)
|
||||
set |= AR_WA_D3_L1_DISABLE;
|
||||
|
||||
clr = AR_WA_UNTIE_RESET_EN;
|
||||
set |= AR_WA_RESET_EN | AR_WA_POR_SHORT;
|
||||
REG_RMW(ah, AR_WA, set, clr);
|
||||
|
||||
/*
|
||||
* for WoW sleep, we reprogram the SerDes so that the
|
||||
* PLL and CLK REQ are both enabled. This uses more
|
||||
* power but otherwise WoW sleep is unstable and the
|
||||
* chip may disappear.
|
||||
*/
|
||||
|
||||
if (AR_SREV_9285_12_OR_LATER(ah))
|
||||
ath9k_hw_config_serdes_wow_sleep(ah);
|
||||
|
||||
}
|
||||
set = AR_WA_RESET_EN | AR_WA_POR_SHORT;
|
||||
clr = AR_WA_UNTIE_RESET_EN | AR_WA_D3_L1_DISABLE;
|
||||
REG_RMW(ah, AR_WA, set, clr);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -378,7 +294,6 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
|
|||
* Program default values for pattern backoff, aifs/slot/KAL count,
|
||||
* beacon miss timeout, KAL timeout, etc.
|
||||
*/
|
||||
|
||||
set = AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF);
|
||||
REG_SET_BIT(ah, AR_WOW_PATTERN, set);
|
||||
|
||||
|
@ -398,7 +313,7 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
|
|||
/*
|
||||
* Keep alive timo in ms except AR9280
|
||||
*/
|
||||
if (!pattern_enable || AR_SREV_9280(ah))
|
||||
if (!pattern_enable)
|
||||
set = AR_WOW_KEEP_ALIVE_NEVER;
|
||||
else
|
||||
set = KAL_TIMEOUT * 32;
|
||||
|
@ -420,7 +335,6 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
|
|||
/*
|
||||
* Configure MAC WoW Registers
|
||||
*/
|
||||
|
||||
set = 0;
|
||||
/* Send keep alive timeouts anyway */
|
||||
clr = AR_WOW_KEEP_ALIVE_AUTO_DIS;
|
||||
|
@ -430,16 +344,9 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
|
|||
else
|
||||
set = AR_WOW_KEEP_ALIVE_FAIL_DIS;
|
||||
|
||||
/*
|
||||
* FIXME: For now disable keep alive frame
|
||||
* failure. This seems to sometimes trigger
|
||||
* unnecessary wake up with AR9485 chipsets.
|
||||
*/
|
||||
set = AR_WOW_KEEP_ALIVE_FAIL_DIS;
|
||||
|
||||
REG_RMW(ah, AR_WOW_KEEP_ALIVE, set, clr);
|
||||
|
||||
|
||||
/*
|
||||
* we are relying on a bmiss failure. ensure we have
|
||||
* enough threshold to prevent false positives
|
||||
|
@ -473,14 +380,8 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
|
|||
set |= AR_WOW_MAC_INTR_EN;
|
||||
REG_RMW(ah, AR_WOW_PATTERN, set, clr);
|
||||
|
||||
/*
|
||||
* For AR9285 and later version of chipsets
|
||||
* enable WoW pattern match for packets less
|
||||
* than 256 bytes for all patterns
|
||||
*/
|
||||
if (AR_SREV_9285_12_OR_LATER(ah))
|
||||
REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B,
|
||||
AR_WOW_PATTERN_SUPPORTED);
|
||||
REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B,
|
||||
AR_WOW_PATTERN_SUPPORTED);
|
||||
|
||||
/*
|
||||
* Set the power states appropriately and enable PME
|
||||
|
@ -488,43 +389,32 @@ void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
|
|||
clr = 0;
|
||||
set = AR_PMCTRL_PWR_STATE_D1D3 | AR_PMCTRL_HOST_PME_EN |
|
||||
AR_PMCTRL_PWR_PM_CTRL_ENA;
|
||||
/*
|
||||
* This is needed for AR9300 chipsets to wake-up
|
||||
* the host.
|
||||
*/
|
||||
if (AR_SREV_9300_20_OR_LATER(ah))
|
||||
clr = AR_PCIE_PM_CTRL_ENA;
|
||||
|
||||
clr = AR_PCIE_PM_CTRL_ENA;
|
||||
REG_RMW(ah, AR_PCIE_PM_CTRL, set, clr);
|
||||
|
||||
if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
|
||||
/*
|
||||
* this is needed to prevent the chip waking up
|
||||
* the host within 3-4 seconds with certain
|
||||
* platform/BIOS. The fix is to enable
|
||||
* D1 & D3 to match original definition and
|
||||
* also match the OTP value. Anyway this
|
||||
* is more related to SW WOW.
|
||||
*/
|
||||
clr = AR_PMCTRL_PWR_STATE_D1D3;
|
||||
REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr);
|
||||
|
||||
set = AR_PMCTRL_PWR_STATE_D1D3_REAL;
|
||||
REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set);
|
||||
}
|
||||
|
||||
/*
|
||||
* this is needed to prevent the chip waking up
|
||||
* the host within 3-4 seconds with certain
|
||||
* platform/BIOS. The fix is to enable
|
||||
* D1 & D3 to match original definition and
|
||||
* also match the OTP value. Anyway this
|
||||
* is more related to SW WOW.
|
||||
*/
|
||||
clr = AR_PMCTRL_PWR_STATE_D1D3;
|
||||
REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr);
|
||||
|
||||
set = AR_PMCTRL_PWR_STATE_D1D3_REAL;
|
||||
REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set);
|
||||
|
||||
REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PRESERVE_SEQNUM);
|
||||
|
||||
if (AR_SREV_9300_20_OR_LATER(ah)) {
|
||||
/* to bring down WOW power low margin */
|
||||
set = BIT(13);
|
||||
REG_SET_BIT(ah, AR_PCIE_PHY_REG3, set);
|
||||
/* HW WoW */
|
||||
clr = BIT(5);
|
||||
REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, clr);
|
||||
}
|
||||
/* to bring down WOW power low margin */
|
||||
set = BIT(13);
|
||||
REG_SET_BIT(ah, AR_PCIE_PHY_REG3, set);
|
||||
/* HW WoW */
|
||||
clr = BIT(5);
|
||||
REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, clr);
|
||||
|
||||
ath9k_hw_set_powermode_wow_sleep(ah);
|
||||
ah->wow_event_mask = wow_event_mask;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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, "a);
|
||||
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;
|
||||
|
|
|
@ -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"
|
|
@ -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__) */
|
|
@ -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 = ⅆ
|
||||
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 = ⅆ
|
||||
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 = ⅆ
|
||||
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 = ⅆ
|
||||
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 = ⅆ
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -900,7 +900,7 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
|
|||
if (supr_status) {
|
||||
update_rate = false;
|
||||
if (supr_status == TX_STATUS_SUPR_BADCH) {
|
||||
brcms_err(wlc->hw->d11core,
|
||||
brcms_dbg_ht(wlc->hw->d11core,
|
||||
"%s: Pkt tx suppressed, illegal channel possibly %d\n",
|
||||
__func__, CHSPEC_CHANNEL(
|
||||
wlc->default_bss->chanspec));
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
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.
|
||||
By default this driver only supports the Sagrad SG901-1091/1098 EVK
|
||||
and similar designs that utilize a hardware reset circuit. To
|
||||
support different CW1200 SDIO designs you will need to override
|
||||
the default platform data by calling cw1200_sdio_set_platform_data()
|
||||
in your board setup file.
|
||||
|
||||
config CW1200_WLAN_SPI
|
||||
tristate "Support SPI platforms"
|
||||
depends on CW1200 && SPI
|
||||
help
|
||||
Enables support for the CW1200 connected via a SPI bus. You will
|
||||
need to add appropriate platform data glue in your board setup
|
||||
file.
|
||||
|
||||
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
|
|
@ -0,0 +1,22 @@
|
|||
cw1200_core-y := \
|
||||
fwio.o \
|
||||
txrx.o \
|
||||
main.o \
|
||||
queue.o \
|
||||
hwio.o \
|
||||
bh.o \
|
||||
wsm.o \
|
||||
sta.o \
|
||||
scan.o \
|
||||
debug.o
|
||||
cw1200_core-$(CONFIG_CW1200_ITP) += itp.o
|
||||
cw1200_core-$(CONFIG_PM) += pm.o
|
||||
|
||||
# CFLAGS_sta.o += -DDEBUG
|
||||
|
||||
cw1200_wlan_sdio-y := cw1200_sdio.o
|
||||
cw1200_wlan_spi-y := cw1200_spi.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
|
|
@ -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 "hwbus.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: hwbus_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->hwbus_ops->align_size(
|
||||
priv->hwbus_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->hwbus_ops->align_size(
|
||||
priv->hwbus_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],
|
||||
×tamp,
|
||||
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->hwbus_ops->lock(priv->hwbus_priv);
|
||||
__cw1200_irq_enable(priv, 1);
|
||||
priv->hwbus_ops->unlock(priv->hwbus_priv);
|
||||
}
|
||||
|
||||
/* Explicitly disable device interrupts */
|
||||
priv->hwbus_ops->lock(priv->hwbus_priv);
|
||||
__cw1200_irq_enable(priv, 0);
|
||||
priv->hwbus_ops->unlock(priv->hwbus_priv);
|
||||
|
||||
if (!term) {
|
||||
pr_err("[BH] Fatal error, exiting.\n");
|
||||
priv->bh_error = 1;
|
||||
/* TODO: schedule_work(recovery) */
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -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 */
|
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
* 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/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 hwbus_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 hwbus_ops *hwbus_ops;
|
||||
struct hwbus_priv *hwbus_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 hwbus_ops *hwbus_ops,
|
||||
struct hwbus_priv *hwbus,
|
||||
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 */
|
|
@ -0,0 +1,424 @@
|
|||
/*
|
||||
* 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/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 "hwbus.h"
|
||||
#include <linux/platform_data/net-cw1200.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)
|
||||
|
||||
/* Default platform data for Sagrad modules */
|
||||
static struct cw1200_platform_data_sdio sagrad_109x_evk_platform_data = {
|
||||
.ref_clk = 38400,
|
||||
.have_5ghz = false,
|
||||
.sdd_file = "sdd_sagrad_1091_1098.bin",
|
||||
};
|
||||
|
||||
/* Allow platform data to be overridden */
|
||||
static struct cw1200_platform_data_sdio *global_plat_data = &sagrad_109x_evk_platform_data;
|
||||
|
||||
void __init cw1200_sdio_set_platform_data(struct cw1200_platform_data_sdio *pdata)
|
||||
{
|
||||
global_plat_data = pdata;
|
||||
}
|
||||
|
||||
struct hwbus_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 */ },
|
||||
};
|
||||
|
||||
/* hwbus_ops implemetation */
|
||||
|
||||
static int cw1200_sdio_memcpy_fromio(struct hwbus_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 hwbus_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 hwbus_priv *self)
|
||||
{
|
||||
sdio_claim_host(self->func);
|
||||
}
|
||||
|
||||
static void cw1200_sdio_unlock(struct hwbus_priv *self)
|
||||
{
|
||||
sdio_release_host(self->func);
|
||||
}
|
||||
|
||||
static void cw1200_sdio_irq_handler(struct sdio_func *func)
|
||||
{
|
||||
struct hwbus_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 hwbus_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 hwbus_priv *self)
|
||||
{
|
||||
int ret;
|
||||
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(self->pdata->irq);
|
||||
if (WARN_ON(ret))
|
||||
goto err;
|
||||
|
||||
/* Request the IRQ */
|
||||
ret = request_threaded_irq(self->pdata->irq, cw1200_gpio_hardirq,
|
||||
cw1200_gpio_irq,
|
||||
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||
"cw1200_wlan_irq", self);
|
||||
if (WARN_ON(ret))
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cw1200_sdio_irq_subscribe(struct hwbus_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 hwbus_priv *self)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("SW IRQ unsubscribe\n");
|
||||
|
||||
if (self->pdata->irq) {
|
||||
disable_irq_wake(self->pdata->irq);
|
||||
free_irq(self->pdata->irq, 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)
|
||||
{
|
||||
if (pdata->reset) {
|
||||
gpio_set_value(pdata->reset, 0);
|
||||
msleep(30); /* Min is 2 * CLK32K cycles */
|
||||
gpio_free(pdata->reset);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
/* Ensure I/Os are pulled low */
|
||||
if (pdata->reset) {
|
||||
gpio_request(pdata->reset, "cw1200_wlan_reset");
|
||||
gpio_direction_output(pdata->reset, 0);
|
||||
}
|
||||
if (pdata->powerup) {
|
||||
gpio_request(pdata->powerup, "cw1200_wlan_powerup");
|
||||
gpio_direction_output(pdata->powerup, 0);
|
||||
}
|
||||
if (pdata->reset || pdata->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 (pdata->powerup) {
|
||||
gpio_set_value(pdata->powerup, 1);
|
||||
msleep(250); /* or more..? */
|
||||
}
|
||||
/* Enable RSTn signal */
|
||||
if (pdata->reset) {
|
||||
gpio_set_value(pdata->reset, 1);
|
||||
msleep(50); /* Or more..? */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t cw1200_sdio_align_size(struct hwbus_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 hwbus_priv *self, bool suspend)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (self->pdata->irq)
|
||||
ret = irq_set_irq_wake(self->pdata->irq, suspend);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct hwbus_ops cw1200_sdio_hwbus_ops = {
|
||||
.hwbus_memcpy_fromio = cw1200_sdio_memcpy_fromio,
|
||||
.hwbus_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 hwbus_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 hwbus_priv.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
|
||||
|
||||
self->pdata = global_plat_data; /* FIXME */
|
||||
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_hwbus_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 hwbus_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);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int cw1200_sdio_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct sdio_func *func = dev_to_sdio_func(dev);
|
||||
struct hwbus_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,
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct sdio_driver sdio_driver = {
|
||||
.name = "cw1200_wlan_sdio",
|
||||
.id_table = cw1200_sdio_ids,
|
||||
.probe = cw1200_sdio_probe,
|
||||
.remove = cw1200_sdio_disconnect,
|
||||
#ifdef CONFIG_PM
|
||||
.drv = {
|
||||
.pm = &cw1200_pm_ops,
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Init Module function -> Called by insmod */
|
||||
static int __init cw1200_sdio_init(void)
|
||||
{
|
||||
const struct cw1200_platform_data_sdio *pdata;
|
||||
int ret;
|
||||
|
||||
/* FIXME -- this won't support multiple devices */
|
||||
pdata = global_plat_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;
|
||||
|
||||
/* FIXME -- this won't support multiple devices */
|
||||
pdata = global_plat_data;
|
||||
sdio_unregister_driver(&sdio_driver);
|
||||
cw1200_sdio_off(pdata);
|
||||
}
|
||||
|
||||
|
||||
module_init(cw1200_sdio_init);
|
||||
module_exit(cw1200_sdio_exit);
|
|
@ -0,0 +1,465 @@
|
|||
/*
|
||||
* 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/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 "hwbus.h"
|
||||
#include <linux/platform_data/net-cw1200.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 hwbus_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 hwbus_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 = ®addr,
|
||||
.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 hwbus_priv *self,
|
||||
unsigned int addr,
|
||||
const void *src, int count)
|
||||
{
|
||||
int rval, i;
|
||||
uint16_t regaddr;
|
||||
struct spi_transfer t_addr = {
|
||||
.tx_buf = ®addr,
|
||||
.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 hwbus_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 hwbus_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 hwbus_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 hwbus_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 hwbus_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)
|
||||
{
|
||||
if (pdata->reset) {
|
||||
gpio_set_value(pdata->reset, 0);
|
||||
msleep(30); /* Min is 2 * CLK32K cycles */
|
||||
gpio_free(pdata->reset);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
/* Ensure I/Os are pulled low */
|
||||
if (pdata->reset) {
|
||||
gpio_request(pdata->reset, "cw1200_wlan_reset");
|
||||
gpio_direction_output(pdata->reset, 0);
|
||||
}
|
||||
if (pdata->powerup) {
|
||||
gpio_request(pdata->powerup, "cw1200_wlan_powerup");
|
||||
gpio_direction_output(pdata->powerup, 0);
|
||||
}
|
||||
if (pdata->reset || pdata->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 (pdata->powerup) {
|
||||
gpio_set_value(pdata->powerup, 1);
|
||||
msleep(250); /* or more..? */
|
||||
}
|
||||
/* Enable RSTn signal */
|
||||
if (pdata->reset) {
|
||||
gpio_set_value(pdata->reset, 1);
|
||||
msleep(50); /* Or more..? */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t cw1200_spi_align_size(struct hwbus_priv *self, size_t size)
|
||||
{
|
||||
return size & 1 ? size + 1 : size;
|
||||
}
|
||||
|
||||
static int cw1200_spi_pm(struct hwbus_priv *self, bool suspend)
|
||||
{
|
||||
return irq_set_irq_wake(self->func->irq, suspend);
|
||||
}
|
||||
|
||||
static struct hwbus_ops cw1200_spi_hwbus_ops = {
|
||||
.hwbus_memcpy_fromio = cw1200_spi_memcpy_fromio,
|
||||
.hwbus_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 hwbus_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 hwbus_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_hwbus_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 hwbus_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;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int cw1200_spi_suspend(struct device *dev, pm_message_t state)
|
||||
{
|
||||
struct hwbus_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;
|
||||
}
|
||||
#endif
|
||||
|
||||
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,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = cw1200_spi_suspend,
|
||||
.resume = cw1200_spi_resume,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
module_spi_driver(spi_driver);
|
|
@ -0,0 +1,658 @@
|
|||
/*
|
||||
* 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,
|
||||
};
|
||||
|
||||
#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 = simple_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 */
|
|
@ -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 */
|
|
@ -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 "hwbus.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->hwbus_ops->lock(priv->hwbus_priv);
|
||||
ret = __cw1200_irq_enable(priv, 1);
|
||||
priv->hwbus_ops->unlock(priv->hwbus_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->hwbus_ops->lock(priv->hwbus_priv);
|
||||
ret = __cw1200_irq_enable(priv, 0);
|
||||
priv->hwbus_ops->unlock(priv->hwbus_priv);
|
||||
return ret;
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Common hwbus 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_HWBUS_H
|
||||
#define CW1200_HWBUS_H
|
||||
|
||||
struct hwbus_priv;
|
||||
|
||||
void cw1200_irq_handler(struct cw1200_common *priv);
|
||||
|
||||
/* This MUST be wrapped with hwbus_ops->lock/unlock! */
|
||||
int __cw1200_irq_enable(struct cw1200_common *priv, int enable);
|
||||
|
||||
struct hwbus_ops {
|
||||
int (*hwbus_memcpy_fromio)(struct hwbus_priv *self, unsigned int addr,
|
||||
void *dst, int count);
|
||||
int (*hwbus_memcpy_toio)(struct hwbus_priv *self, unsigned int addr,
|
||||
const void *src, int count);
|
||||
void (*lock)(struct hwbus_priv *self);
|
||||
void (*unlock)(struct hwbus_priv *self);
|
||||
size_t (*align_size)(struct hwbus_priv *self, size_t size);
|
||||
int (*power_mgmt)(struct hwbus_priv *self, bool suspend);
|
||||
};
|
||||
|
||||
#endif /* CW1200_HWBUS_H */
|
|
@ -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 "hwbus.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->hwbus_ops->hwbus_memcpy_fromio(priv->hwbus_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->hwbus_ops->hwbus_memcpy_toio(priv->hwbus_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->hwbus_ops->lock(priv->hwbus_priv);
|
||||
ret = __cw1200_reg_read(priv, addr, buf, buf_len, 0);
|
||||
priv->hwbus_ops->unlock(priv->hwbus_priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cw1200_reg_write(struct cw1200_common *priv, u16 addr, const void *buf,
|
||||
size_t buf_len)
|
||||
{
|
||||
int ret;
|
||||
priv->hwbus_ops->lock(priv->hwbus_priv);
|
||||
ret = __cw1200_reg_write(priv, addr, buf, buf_len, 0);
|
||||
priv->hwbus_ops->unlock(priv->hwbus_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->hwbus_ops->lock(priv->hwbus_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->hwbus_ops->unlock(priv->hwbus_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->hwbus_ops->lock(priv->hwbus_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->hwbus_ops->unlock(priv->hwbus_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->hwbus_ops->lock(priv->hwbus_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->hwbus_ops->unlock(priv->hwbus_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->hwbus_ops->lock(priv->hwbus_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->hwbus_ops->unlock(priv->hwbus_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;
|
||||
}
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -0,0 +1,633 @@
|
|||
/*
|
||||
* 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 "hwbus.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,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = cw1200_wow_suspend,
|
||||
.resume = cw1200_wow_resume,
|
||||
#endif
|
||||
/* 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");
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static const struct wiphy_wowlan_support cw1200_wowlan_support = {
|
||||
/* Support only for limited wowlan functionalities */
|
||||
.flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT,
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
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);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
hw->wiphy->wowlan = &cw1200_wowlan_support;
|
||||
#endif
|
||||
|
||||
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
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
err = cw1200_pm_init(&priv->pm_state, priv);
|
||||
if (err) {
|
||||
pr_err("Cannot init PM. (%d).\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
err = ieee80211_register_hw(dev);
|
||||
if (err) {
|
||||
pr_err("Cannot register device (%d).\n",
|
||||
err);
|
||||
#ifdef CONFIG_PM
|
||||
cw1200_pm_deinit(&priv->pm_state);
|
||||
#endif
|
||||
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);
|
||||
#ifdef CONFIG_PM
|
||||
cw1200_pm_deinit(&priv->pm_state);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* 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 hwbus_ops *hwbus_ops,
|
||||
struct hwbus_priv *hwbus,
|
||||
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->hwbus_ops = hwbus_ops;
|
||||
priv->hwbus_priv = hwbus;
|
||||
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->hwbus_ops->lock(self->hwbus_priv);
|
||||
__cw1200_irq_enable(self, 0);
|
||||
self->hwbus_ops->unlock(self->hwbus_priv);
|
||||
|
||||
/* And then clean up */
|
||||
cw1200_unregister_common(self->hw);
|
||||
cw1200_free_common(self->hw);
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cw1200_core_release);
|
|
@ -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 "hwbus.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->hwbus_ops->power_mgmt(priv->hwbus_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->hwbus_ops->power_mgmt(priv->hwbus_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;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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 */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int cw1200_pm_init(struct cw1200_pm_state *pm,
|
||||
struct cw1200_common *priv);
|
||||
void cw1200_pm_deinit(struct cw1200_pm_state *pm);
|
||||
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);
|
||||
void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
|
||||
unsigned long tmo);
|
||||
#else
|
||||
static inline void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
|
||||
unsigned long tmo) {
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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
|
@ -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
|
@ -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
|
@ -6242,8 +6242,6 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
|
|||
if ((val & 0x0000ff00) != 0)
|
||||
pci_write_config_dword(pci_dev, 0x40, val & 0xffff00ff);
|
||||
|
||||
pci_set_power_state(pci_dev, PCI_D0);
|
||||
|
||||
if (!ipw2100_hw_is_adapter_in_system(dev)) {
|
||||
printk(KERN_WARNING DRV_NAME
|
||||
"Device not found via register read.\n");
|
||||
|
|
|
@ -76,13 +76,16 @@
|
|||
#define IWL_INVALID_STATION 255
|
||||
|
||||
/* device operations */
|
||||
extern struct iwl_lib_ops iwl1000_lib;
|
||||
extern struct iwl_lib_ops iwl2000_lib;
|
||||
extern struct iwl_lib_ops iwl2030_lib;
|
||||
extern struct iwl_lib_ops iwl5000_lib;
|
||||
extern struct iwl_lib_ops iwl5150_lib;
|
||||
extern struct iwl_lib_ops iwl6000_lib;
|
||||
extern struct iwl_lib_ops iwl6030_lib;
|
||||
extern const struct iwl_dvm_cfg iwl_dvm_1000_cfg;
|
||||
extern const struct iwl_dvm_cfg iwl_dvm_2000_cfg;
|
||||
extern const struct iwl_dvm_cfg iwl_dvm_105_cfg;
|
||||
extern const struct iwl_dvm_cfg iwl_dvm_2030_cfg;
|
||||
extern const struct iwl_dvm_cfg iwl_dvm_5000_cfg;
|
||||
extern const struct iwl_dvm_cfg iwl_dvm_5150_cfg;
|
||||
extern const struct iwl_dvm_cfg iwl_dvm_6000_cfg;
|
||||
extern const struct iwl_dvm_cfg iwl_dvm_6005_cfg;
|
||||
extern const struct iwl_dvm_cfg iwl_dvm_6050_cfg;
|
||||
extern const struct iwl_dvm_cfg iwl_dvm_6030_cfg;
|
||||
|
||||
|
||||
#define TIME_UNIT 1024
|
||||
|
@ -291,8 +294,8 @@ void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena);
|
|||
|
||||
static inline bool iwl_advanced_bt_coexist(struct iwl_priv *priv)
|
||||
{
|
||||
return priv->cfg->bt_params &&
|
||||
priv->cfg->bt_params->advanced_bt_coexist;
|
||||
return priv->lib->bt_params &&
|
||||
priv->lib->bt_params->advanced_bt_coexist;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
|
|
|
@ -521,7 +521,7 @@ static int iwl_enhance_sensitivity_write(struct iwl_priv *priv)
|
|||
|
||||
iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]);
|
||||
|
||||
if (priv->cfg->base_params->hd_v2) {
|
||||
if (priv->lib->hd_v2) {
|
||||
cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] =
|
||||
HD_INA_NON_SQUARE_DET_OFDM_DATA_V2;
|
||||
cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] =
|
||||
|
@ -895,7 +895,7 @@ static void iwlagn_gain_computation(struct iwl_priv *priv,
|
|||
continue;
|
||||
}
|
||||
|
||||
delta_g = (priv->cfg->base_params->chain_noise_scale *
|
||||
delta_g = (priv->lib->chain_noise_scale *
|
||||
((s32)average_noise[default_chain] -
|
||||
(s32)average_noise[i])) / 1500;
|
||||
|
||||
|
@ -1051,8 +1051,8 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv)
|
|||
return;
|
||||
|
||||
/* Analyze signal for disconnected antenna */
|
||||
if (priv->cfg->bt_params &&
|
||||
priv->cfg->bt_params->advanced_bt_coexist) {
|
||||
if (priv->lib->bt_params &&
|
||||
priv->lib->bt_params->advanced_bt_coexist) {
|
||||
/* Disable disconnected antenna algorithm for advanced
|
||||
bt coex, assuming valid antennas are connected */
|
||||
data->active_chains = priv->nvm_data->valid_rx_ant;
|
||||
|
|
|
@ -838,10 +838,6 @@ struct iwl_qosparam_cmd {
|
|||
#define STA_MODIFY_DELBA_TID_MSK 0x10
|
||||
#define STA_MODIFY_SLEEP_TX_COUNT_MSK 0x20
|
||||
|
||||
/* Receiver address (actually, Rx station's index into station table),
|
||||
* combined with Traffic ID (QOS priority), in format used by Tx Scheduler */
|
||||
#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid))
|
||||
|
||||
/* agn */
|
||||
struct iwl_keyinfo {
|
||||
__le16 key_flags;
|
||||
|
|
|
@ -568,16 +568,61 @@ struct iwl_hw_params {
|
|||
const struct iwl_sensitivity_ranges *sens;
|
||||
};
|
||||
|
||||
struct iwl_lib_ops {
|
||||
/* set hw dependent parameters */
|
||||
/**
|
||||
* struct iwl_dvm_bt_params - DVM specific BT (coex) parameters
|
||||
* @advanced_bt_coexist: support advanced bt coexist
|
||||
* @bt_init_traffic_load: specify initial bt traffic load
|
||||
* @bt_prio_boost: default bt priority boost value
|
||||
* @agg_time_limit: maximum number of uSec in aggregation
|
||||
* @bt_sco_disable: uCode should not response to BT in SCO/ESCO mode
|
||||
*/
|
||||
struct iwl_dvm_bt_params {
|
||||
bool advanced_bt_coexist;
|
||||
u8 bt_init_traffic_load;
|
||||
u32 bt_prio_boost;
|
||||
u16 agg_time_limit;
|
||||
bool bt_sco_disable;
|
||||
bool bt_session_2;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_dvm_cfg - DVM firmware specific device configuration
|
||||
* @set_hw_params: set hardware parameters
|
||||
* @set_channel_switch: send channel switch command
|
||||
* @nic_config: apply device specific configuration
|
||||
* @temperature: read temperature
|
||||
* @adv_thermal_throttle: support advance thermal throttle
|
||||
* @support_ct_kill_exit: support ct kill exit condition
|
||||
* @plcp_delta_threshold: plcp error rate threshold used to trigger
|
||||
* radio tuning when there is a high receiving plcp error rate
|
||||
* @chain_noise_scale: default chain noise scale used for gain computation
|
||||
* @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up
|
||||
* @no_idle_support: do not support idle mode
|
||||
* @bt_params: pointer to BT parameters
|
||||
* @need_temp_offset_calib: need to perform temperature offset calibration
|
||||
* @no_xtal_calib: some devices do not need crystal calibration data,
|
||||
* don't send it to those
|
||||
* @temp_offset_v2: support v2 of temperature offset calibration
|
||||
* @adv_pm: advanced power management
|
||||
*/
|
||||
struct iwl_dvm_cfg {
|
||||
void (*set_hw_params)(struct iwl_priv *priv);
|
||||
int (*set_channel_switch)(struct iwl_priv *priv,
|
||||
struct ieee80211_channel_switch *ch_switch);
|
||||
/* device specific configuration */
|
||||
void (*nic_config)(struct iwl_priv *priv);
|
||||
|
||||
/* temperature */
|
||||
void (*temperature)(struct iwl_priv *priv);
|
||||
|
||||
const struct iwl_dvm_bt_params *bt_params;
|
||||
s32 chain_noise_scale;
|
||||
u8 plcp_delta_threshold;
|
||||
bool adv_thermal_throttle;
|
||||
bool support_ct_kill_exit;
|
||||
bool hd_v2;
|
||||
bool no_idle_support;
|
||||
bool need_temp_offset_calib;
|
||||
bool no_xtal_calib;
|
||||
bool temp_offset_v2;
|
||||
bool adv_pm;
|
||||
};
|
||||
|
||||
struct iwl_wipan_noa_data {
|
||||
|
@ -610,7 +655,7 @@ struct iwl_priv {
|
|||
struct device *dev; /* for debug prints only */
|
||||
const struct iwl_cfg *cfg;
|
||||
const struct iwl_fw *fw;
|
||||
const struct iwl_lib_ops *lib;
|
||||
const struct iwl_dvm_cfg *lib;
|
||||
unsigned long status;
|
||||
|
||||
spinlock_t sta_lock;
|
||||
|
|
|
@ -174,10 +174,13 @@ static void iwl1000_hw_set_hw_params(struct iwl_priv *priv)
|
|||
priv->hw_params.sens = &iwl1000_sensitivity;
|
||||
}
|
||||
|
||||
struct iwl_lib_ops iwl1000_lib = {
|
||||
const struct iwl_dvm_cfg iwl_dvm_1000_cfg = {
|
||||
.set_hw_params = iwl1000_hw_set_hw_params,
|
||||
.nic_config = iwl1000_nic_config,
|
||||
.temperature = iwlagn_temperature,
|
||||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
};
|
||||
|
||||
|
||||
|
@ -232,16 +235,56 @@ static void iwl2000_hw_set_hw_params(struct iwl_priv *priv)
|
|||
priv->hw_params.sens = &iwl2000_sensitivity;
|
||||
}
|
||||
|
||||
struct iwl_lib_ops iwl2000_lib = {
|
||||
const struct iwl_dvm_cfg iwl_dvm_2000_cfg = {
|
||||
.set_hw_params = iwl2000_hw_set_hw_params,
|
||||
.nic_config = iwl2000_nic_config,
|
||||
.temperature = iwlagn_temperature,
|
||||
.adv_thermal_throttle = true,
|
||||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.hd_v2 = true,
|
||||
.need_temp_offset_calib = true,
|
||||
.temp_offset_v2 = true,
|
||||
};
|
||||
|
||||
struct iwl_lib_ops iwl2030_lib = {
|
||||
const struct iwl_dvm_cfg iwl_dvm_105_cfg = {
|
||||
.set_hw_params = iwl2000_hw_set_hw_params,
|
||||
.nic_config = iwl2000_nic_config,
|
||||
.temperature = iwlagn_temperature,
|
||||
.adv_thermal_throttle = true,
|
||||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.hd_v2 = true,
|
||||
.need_temp_offset_calib = true,
|
||||
.temp_offset_v2 = true,
|
||||
.adv_pm = true,
|
||||
};
|
||||
|
||||
static const struct iwl_dvm_bt_params iwl2030_bt_params = {
|
||||
/* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */
|
||||
.advanced_bt_coexist = true,
|
||||
.agg_time_limit = BT_AGG_THRESHOLD_DEF,
|
||||
.bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE,
|
||||
.bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT32,
|
||||
.bt_sco_disable = true,
|
||||
.bt_session_2 = true,
|
||||
};
|
||||
|
||||
const struct iwl_dvm_cfg iwl_dvm_2030_cfg = {
|
||||
.set_hw_params = iwl2000_hw_set_hw_params,
|
||||
.nic_config = iwl2000_nic_config,
|
||||
.temperature = iwlagn_temperature,
|
||||
.adv_thermal_throttle = true,
|
||||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.hd_v2 = true,
|
||||
.bt_params = &iwl2030_bt_params,
|
||||
.need_temp_offset_calib = true,
|
||||
.temp_offset_v2 = true,
|
||||
.adv_pm = true,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -420,16 +463,23 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv,
|
|||
return iwl_dvm_send_cmd(priv, &hcmd);
|
||||
}
|
||||
|
||||
struct iwl_lib_ops iwl5000_lib = {
|
||||
const struct iwl_dvm_cfg iwl_dvm_5000_cfg = {
|
||||
.set_hw_params = iwl5000_hw_set_hw_params,
|
||||
.set_channel_switch = iwl5000_hw_channel_switch,
|
||||
.temperature = iwlagn_temperature,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.no_idle_support = true,
|
||||
};
|
||||
|
||||
struct iwl_lib_ops iwl5150_lib = {
|
||||
const struct iwl_dvm_cfg iwl_dvm_5150_cfg = {
|
||||
.set_hw_params = iwl5150_hw_set_hw_params,
|
||||
.set_channel_switch = iwl5000_hw_channel_switch,
|
||||
.temperature = iwl5150_temperature,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.no_idle_support = true,
|
||||
.no_xtal_calib = true,
|
||||
};
|
||||
|
||||
|
||||
|
@ -584,16 +634,59 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv,
|
|||
return err;
|
||||
}
|
||||
|
||||
struct iwl_lib_ops iwl6000_lib = {
|
||||
const struct iwl_dvm_cfg iwl_dvm_6000_cfg = {
|
||||
.set_hw_params = iwl6000_hw_set_hw_params,
|
||||
.set_channel_switch = iwl6000_hw_channel_switch,
|
||||
.nic_config = iwl6000_nic_config,
|
||||
.temperature = iwlagn_temperature,
|
||||
.adv_thermal_throttle = true,
|
||||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
};
|
||||
|
||||
struct iwl_lib_ops iwl6030_lib = {
|
||||
const struct iwl_dvm_cfg iwl_dvm_6005_cfg = {
|
||||
.set_hw_params = iwl6000_hw_set_hw_params,
|
||||
.set_channel_switch = iwl6000_hw_channel_switch,
|
||||
.nic_config = iwl6000_nic_config,
|
||||
.temperature = iwlagn_temperature,
|
||||
.adv_thermal_throttle = true,
|
||||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.need_temp_offset_calib = true,
|
||||
};
|
||||
|
||||
const struct iwl_dvm_cfg iwl_dvm_6050_cfg = {
|
||||
.set_hw_params = iwl6000_hw_set_hw_params,
|
||||
.set_channel_switch = iwl6000_hw_channel_switch,
|
||||
.nic_config = iwl6000_nic_config,
|
||||
.temperature = iwlagn_temperature,
|
||||
.adv_thermal_throttle = true,
|
||||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1500,
|
||||
};
|
||||
|
||||
static const struct iwl_dvm_bt_params iwl6000_bt_params = {
|
||||
/* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */
|
||||
.advanced_bt_coexist = true,
|
||||
.agg_time_limit = BT_AGG_THRESHOLD_DEF,
|
||||
.bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE,
|
||||
.bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT,
|
||||
.bt_sco_disable = true,
|
||||
};
|
||||
|
||||
const struct iwl_dvm_cfg iwl_dvm_6030_cfg = {
|
||||
.set_hw_params = iwl6000_hw_set_hw_params,
|
||||
.set_channel_switch = iwl6000_hw_channel_switch,
|
||||
.nic_config = iwl6000_nic_config,
|
||||
.temperature = iwlagn_temperature,
|
||||
.adv_thermal_throttle = true,
|
||||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.bt_params = &iwl6000_bt_params,
|
||||
.need_temp_offset_calib = true,
|
||||
.adv_pm = true,
|
||||
};
|
||||
|
|
|
@ -254,23 +254,23 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv)
|
|||
BUILD_BUG_ON(sizeof(iwlagn_def_3w_lookup) !=
|
||||
sizeof(basic.bt3_lookup_table));
|
||||
|
||||
if (priv->cfg->bt_params) {
|
||||
if (priv->lib->bt_params) {
|
||||
/*
|
||||
* newer generation of devices (2000 series and newer)
|
||||
* use the version 2 of the bt command
|
||||
* we need to make sure sending the host command
|
||||
* with correct data structure to avoid uCode assert
|
||||
*/
|
||||
if (priv->cfg->bt_params->bt_session_2) {
|
||||
if (priv->lib->bt_params->bt_session_2) {
|
||||
bt_cmd_v2.prio_boost = cpu_to_le32(
|
||||
priv->cfg->bt_params->bt_prio_boost);
|
||||
priv->lib->bt_params->bt_prio_boost);
|
||||
bt_cmd_v2.tx_prio_boost = 0;
|
||||
bt_cmd_v2.rx_prio_boost = 0;
|
||||
} else {
|
||||
/* older version only has 8 bits */
|
||||
WARN_ON(priv->cfg->bt_params->bt_prio_boost & ~0xFF);
|
||||
WARN_ON(priv->lib->bt_params->bt_prio_boost & ~0xFF);
|
||||
bt_cmd_v1.prio_boost =
|
||||
priv->cfg->bt_params->bt_prio_boost;
|
||||
priv->lib->bt_params->bt_prio_boost;
|
||||
bt_cmd_v1.tx_prio_boost = 0;
|
||||
bt_cmd_v1.rx_prio_boost = 0;
|
||||
}
|
||||
|
@ -330,7 +330,7 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv)
|
|||
priv->bt_full_concurrent ?
|
||||
"full concurrency" : "3-wire");
|
||||
|
||||
if (priv->cfg->bt_params->bt_session_2) {
|
||||
if (priv->lib->bt_params->bt_session_2) {
|
||||
memcpy(&bt_cmd_v2.basic, &basic,
|
||||
sizeof(basic));
|
||||
ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG,
|
||||
|
@ -758,8 +758,8 @@ static bool is_single_rx_stream(struct iwl_priv *priv)
|
|||
*/
|
||||
static int iwl_get_active_rx_chain_count(struct iwl_priv *priv)
|
||||
{
|
||||
if (priv->cfg->bt_params &&
|
||||
priv->cfg->bt_params->advanced_bt_coexist &&
|
||||
if (priv->lib->bt_params &&
|
||||
priv->lib->bt_params->advanced_bt_coexist &&
|
||||
(priv->bt_full_concurrent ||
|
||||
priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) {
|
||||
/*
|
||||
|
@ -830,8 +830,8 @@ void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
|
|||
else
|
||||
active_chains = priv->nvm_data->valid_rx_ant;
|
||||
|
||||
if (priv->cfg->bt_params &&
|
||||
priv->cfg->bt_params->advanced_bt_coexist &&
|
||||
if (priv->lib->bt_params &&
|
||||
priv->lib->bt_params->advanced_bt_coexist &&
|
||||
(priv->bt_full_concurrent ||
|
||||
priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) {
|
||||
/*
|
||||
|
|
|
@ -427,6 +427,10 @@ static int iwlagn_mac_suspend(struct ieee80211_hw *hw,
|
|||
if (ret)
|
||||
goto error;
|
||||
|
||||
/* let the ucode operate on its own */
|
||||
iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET,
|
||||
CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
|
||||
|
||||
iwl_trans_d3_suspend(priv->trans);
|
||||
|
||||
goto out;
|
||||
|
@ -510,6 +514,10 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw)
|
|||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* uCode is no longer operating by itself */
|
||||
iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR,
|
||||
CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
|
||||
|
||||
base = priv->device_pointers.error_event_table;
|
||||
if (!iwlagn_hw_valid_rtc_data_addr(base)) {
|
||||
IWL_WARN(priv, "Invalid error table during resume!\n");
|
||||
|
@ -1277,8 +1285,8 @@ static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw,
|
|||
IWL_DEBUG_MAC80211(priv, "enter\n");
|
||||
mutex_lock(&priv->mutex);
|
||||
|
||||
if (priv->cfg->bt_params &&
|
||||
priv->cfg->bt_params->advanced_bt_coexist) {
|
||||
if (priv->lib->bt_params &&
|
||||
priv->lib->bt_params->advanced_bt_coexist) {
|
||||
if (rssi_event == RSSI_EVENT_LOW)
|
||||
priv->bt_enable_pspoll = true;
|
||||
else if (rssi_event == RSSI_EVENT_HIGH)
|
||||
|
@ -1388,7 +1396,7 @@ static int iwl_setup_interface(struct iwl_priv *priv,
|
|||
return err;
|
||||
}
|
||||
|
||||
if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist &&
|
||||
if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist &&
|
||||
vif->type == NL80211_IFTYPE_ADHOC) {
|
||||
/*
|
||||
* pretend to have high BT traffic as long as we
|
||||
|
|
|
@ -615,7 +615,7 @@ static void iwl_rf_kill_ct_config(struct iwl_priv *priv)
|
|||
|
||||
priv->thermal_throttle.ct_kill_toggle = false;
|
||||
|
||||
if (priv->cfg->base_params->support_ct_kill_exit) {
|
||||
if (priv->lib->support_ct_kill_exit) {
|
||||
adv_cmd.critical_temperature_enter =
|
||||
cpu_to_le32(priv->hw_params.ct_kill_threshold);
|
||||
adv_cmd.critical_temperature_exit =
|
||||
|
@ -732,10 +732,10 @@ int iwl_alive_start(struct iwl_priv *priv)
|
|||
}
|
||||
|
||||
/* download priority table before any calibration request */
|
||||
if (priv->cfg->bt_params &&
|
||||
priv->cfg->bt_params->advanced_bt_coexist) {
|
||||
if (priv->lib->bt_params &&
|
||||
priv->lib->bt_params->advanced_bt_coexist) {
|
||||
/* Configure Bluetooth device coexistence support */
|
||||
if (priv->cfg->bt_params->bt_sco_disable)
|
||||
if (priv->lib->bt_params->bt_sco_disable)
|
||||
priv->bt_enable_pspoll = false;
|
||||
else
|
||||
priv->bt_enable_pspoll = true;
|
||||
|
@ -873,9 +873,9 @@ void iwl_down(struct iwl_priv *priv)
|
|||
priv->bt_status = 0;
|
||||
priv->cur_rssi_ctx = NULL;
|
||||
priv->bt_is_sco = 0;
|
||||
if (priv->cfg->bt_params)
|
||||
if (priv->lib->bt_params)
|
||||
priv->bt_traffic_load =
|
||||
priv->cfg->bt_params->bt_init_traffic_load;
|
||||
priv->lib->bt_params->bt_init_traffic_load;
|
||||
else
|
||||
priv->bt_traffic_load = 0;
|
||||
priv->bt_full_concurrent = false;
|
||||
|
@ -1058,7 +1058,7 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
|
|||
|
||||
iwl_setup_scan_deferred_work(priv);
|
||||
|
||||
if (priv->cfg->bt_params)
|
||||
if (priv->lib->bt_params)
|
||||
iwlagn_bt_setup_deferred_work(priv);
|
||||
|
||||
init_timer(&priv->statistics_periodic);
|
||||
|
@ -1072,7 +1072,7 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
|
|||
|
||||
void iwl_cancel_deferred_work(struct iwl_priv *priv)
|
||||
{
|
||||
if (priv->cfg->bt_params)
|
||||
if (priv->lib->bt_params)
|
||||
iwlagn_bt_cancel_deferred_work(priv);
|
||||
|
||||
cancel_work_sync(&priv->run_time_calib_work);
|
||||
|
@ -1098,8 +1098,7 @@ static int iwl_init_drv(struct iwl_priv *priv)
|
|||
|
||||
priv->band = IEEE80211_BAND_2GHZ;
|
||||
|
||||
priv->plcp_delta_threshold =
|
||||
priv->cfg->base_params->plcp_delta_threshold;
|
||||
priv->plcp_delta_threshold = priv->lib->plcp_delta_threshold;
|
||||
|
||||
priv->iw_mode = NL80211_IFTYPE_STATION;
|
||||
priv->current_ht_config.smps = IEEE80211_SMPS_STATIC;
|
||||
|
@ -1116,8 +1115,8 @@ static int iwl_init_drv(struct iwl_priv *priv)
|
|||
iwl_init_scan_params(priv);
|
||||
|
||||
/* init bt coex */
|
||||
if (priv->cfg->bt_params &&
|
||||
priv->cfg->bt_params->advanced_bt_coexist) {
|
||||
if (priv->lib->bt_params &&
|
||||
priv->lib->bt_params->advanced_bt_coexist) {
|
||||
priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT;
|
||||
priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT;
|
||||
priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK;
|
||||
|
@ -1264,31 +1263,37 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
|
|||
switch (priv->cfg->device_family) {
|
||||
case IWL_DEVICE_FAMILY_1000:
|
||||
case IWL_DEVICE_FAMILY_100:
|
||||
priv->lib = &iwl1000_lib;
|
||||
priv->lib = &iwl_dvm_1000_cfg;
|
||||
break;
|
||||
case IWL_DEVICE_FAMILY_2000:
|
||||
priv->lib = &iwl_dvm_2000_cfg;
|
||||
break;
|
||||
case IWL_DEVICE_FAMILY_105:
|
||||
priv->lib = &iwl2000_lib;
|
||||
priv->lib = &iwl_dvm_105_cfg;
|
||||
break;
|
||||
case IWL_DEVICE_FAMILY_2030:
|
||||
case IWL_DEVICE_FAMILY_135:
|
||||
priv->lib = &iwl2030_lib;
|
||||
priv->lib = &iwl_dvm_2030_cfg;
|
||||
break;
|
||||
case IWL_DEVICE_FAMILY_5000:
|
||||
priv->lib = &iwl5000_lib;
|
||||
priv->lib = &iwl_dvm_5000_cfg;
|
||||
break;
|
||||
case IWL_DEVICE_FAMILY_5150:
|
||||
priv->lib = &iwl5150_lib;
|
||||
priv->lib = &iwl_dvm_5150_cfg;
|
||||
break;
|
||||
case IWL_DEVICE_FAMILY_6000:
|
||||
case IWL_DEVICE_FAMILY_6005:
|
||||
case IWL_DEVICE_FAMILY_6000i:
|
||||
priv->lib = &iwl_dvm_6000_cfg;
|
||||
break;
|
||||
case IWL_DEVICE_FAMILY_6005:
|
||||
priv->lib = &iwl_dvm_6005_cfg;
|
||||
break;
|
||||
case IWL_DEVICE_FAMILY_6050:
|
||||
case IWL_DEVICE_FAMILY_6150:
|
||||
priv->lib = &iwl6000_lib;
|
||||
priv->lib = &iwl_dvm_6050_cfg;
|
||||
break;
|
||||
case IWL_DEVICE_FAMILY_6030:
|
||||
priv->lib = &iwl6030_lib;
|
||||
priv->lib = &iwl_dvm_6030_cfg;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -163,7 +163,7 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv,
|
|||
u8 skip;
|
||||
u32 slp_itrvl;
|
||||
|
||||
if (priv->cfg->adv_pm) {
|
||||
if (priv->lib->adv_pm) {
|
||||
table = apm_range_2;
|
||||
if (period <= IWL_DTIM_RANGE_1_MAX)
|
||||
table = apm_range_1;
|
||||
|
@ -217,7 +217,7 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv,
|
|||
cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA;
|
||||
|
||||
if (iwl_advanced_bt_coexist(priv)) {
|
||||
if (!priv->cfg->bt_params->bt_sco_disable)
|
||||
if (!priv->lib->bt_params->bt_sco_disable)
|
||||
cmd->flags |= IWL_POWER_BT_SCO_ENA;
|
||||
else
|
||||
cmd->flags &= ~IWL_POWER_BT_SCO_ENA;
|
||||
|
@ -293,7 +293,7 @@ static void iwl_power_build_cmd(struct iwl_priv *priv,
|
|||
|
||||
if (priv->wowlan)
|
||||
iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper);
|
||||
else if (!priv->cfg->base_params->no_idle_support &&
|
||||
else if (!priv->lib->no_idle_support &&
|
||||
priv->hw->conf.flags & IEEE80211_CONF_IDLE)
|
||||
iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20);
|
||||
else if (iwl_tt_is_low_power_state(priv)) {
|
||||
|
|
|
@ -1088,7 +1088,7 @@ done:
|
|||
(priv->tm_fixed_rate != lq_sta->dbg_fixed_rate))
|
||||
rs_program_fix_rate(priv, lq_sta);
|
||||
#endif
|
||||
if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist)
|
||||
if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist)
|
||||
rs_bt_update_lq(priv, ctx, lq_sta);
|
||||
}
|
||||
|
||||
|
@ -3064,11 +3064,11 @@ static void rs_fill_link_cmd(struct iwl_priv *priv,
|
|||
* overwrite if needed, pass aggregation time limit
|
||||
* to uCode in uSec
|
||||
*/
|
||||
if (priv && priv->cfg->bt_params &&
|
||||
priv->cfg->bt_params->agg_time_limit &&
|
||||
if (priv && priv->lib->bt_params &&
|
||||
priv->lib->bt_params->agg_time_limit &&
|
||||
priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)
|
||||
lq_cmd->agg_params.agg_time_limit =
|
||||
cpu_to_le16(priv->cfg->bt_params->agg_time_limit);
|
||||
cpu_to_le16(priv->lib->bt_params->agg_time_limit);
|
||||
}
|
||||
|
||||
static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
|
||||
|
|
|
@ -1102,7 +1102,7 @@ void iwl_setup_rx_handlers(struct iwl_priv *priv)
|
|||
iwl_notification_wait_init(&priv->notif_wait);
|
||||
|
||||
/* Set up BT Rx handlers */
|
||||
if (priv->cfg->bt_params)
|
||||
if (priv->lib->bt_params)
|
||||
iwlagn_bt_rx_handler_setup(priv);
|
||||
}
|
||||
|
||||
|
|
|
@ -801,8 +801,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
|
|||
* Internal scans are passive, so we can indiscriminately set
|
||||
* the BT ignore flag on 2.4 GHz since it applies to TX only.
|
||||
*/
|
||||
if (priv->cfg->bt_params &&
|
||||
priv->cfg->bt_params->advanced_bt_coexist)
|
||||
if (priv->lib->bt_params &&
|
||||
priv->lib->bt_params->advanced_bt_coexist)
|
||||
scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT;
|
||||
break;
|
||||
case IEEE80211_BAND_5GHZ:
|
||||
|
@ -844,8 +844,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
|
|||
band = priv->scan_band;
|
||||
|
||||
if (band == IEEE80211_BAND_2GHZ &&
|
||||
priv->cfg->bt_params &&
|
||||
priv->cfg->bt_params->advanced_bt_coexist) {
|
||||
priv->lib->bt_params &&
|
||||
priv->lib->bt_params->advanced_bt_coexist) {
|
||||
/* transmit 2.4 GHz probes only on first antenna */
|
||||
scan_tx_antennas = first_antenna(scan_tx_antennas);
|
||||
}
|
||||
|
@ -873,8 +873,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif)
|
|||
|
||||
rx_ant = first_antenna(active_chains);
|
||||
}
|
||||
if (priv->cfg->bt_params &&
|
||||
priv->cfg->bt_params->advanced_bt_coexist &&
|
||||
if (priv->lib->bt_params &&
|
||||
priv->lib->bt_params->advanced_bt_coexist &&
|
||||
priv->bt_full_concurrent) {
|
||||
/* operated as 1x1 in full concurrency mode */
|
||||
rx_ant = first_antenna(rx_ant);
|
||||
|
|
|
@ -735,7 +735,7 @@ void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
|
|||
memcpy(&lq, priv->stations[i].lq,
|
||||
sizeof(struct iwl_link_quality_cmd));
|
||||
|
||||
if (!memcmp(&lq, &zero_lq, sizeof(lq)))
|
||||
if (memcmp(&lq, &zero_lq, sizeof(lq)))
|
||||
send_lq = true;
|
||||
}
|
||||
spin_unlock_bh(&priv->sta_lock);
|
||||
|
|
|
@ -627,7 +627,7 @@ void iwl_tt_initialize(struct iwl_priv *priv)
|
|||
INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter);
|
||||
INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit);
|
||||
|
||||
if (priv->cfg->base_params->adv_thermal_throttle) {
|
||||
if (priv->lib->adv_thermal_throttle) {
|
||||
IWL_DEBUG_TEMP(priv, "Advanced Thermal Throttling\n");
|
||||
tt->restriction = kcalloc(IWL_TI_STATE_MAX,
|
||||
sizeof(struct iwl_tt_restriction),
|
||||
|
|
|
@ -83,8 +83,8 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv,
|
|||
else if (ieee80211_is_back_req(fc))
|
||||
tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK;
|
||||
else if (info->band == IEEE80211_BAND_2GHZ &&
|
||||
priv->cfg->bt_params &&
|
||||
priv->cfg->bt_params->advanced_bt_coexist &&
|
||||
priv->lib->bt_params &&
|
||||
priv->lib->bt_params->advanced_bt_coexist &&
|
||||
(ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) ||
|
||||
ieee80211_is_reassoc_req(fc) ||
|
||||
skb->protocol == cpu_to_be16(ETH_P_PAE)))
|
||||
|
@ -202,8 +202,8 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv,
|
|||
rate_flags |= RATE_MCS_CCK_MSK;
|
||||
|
||||
/* Set up antennas */
|
||||
if (priv->cfg->bt_params &&
|
||||
priv->cfg->bt_params->advanced_bt_coexist &&
|
||||
if (priv->lib->bt_params &&
|
||||
priv->lib->bt_params->advanced_bt_coexist &&
|
||||
priv->bt_full_concurrent) {
|
||||
/* operated as 1x1 in full concurrency mode */
|
||||
priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant,
|
||||
|
@ -986,8 +986,8 @@ static void iwl_rx_reply_tx_agg(struct iwl_priv *priv,
|
|||
* notification again.
|
||||
*/
|
||||
if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 &&
|
||||
priv->cfg->bt_params &&
|
||||
priv->cfg->bt_params->advanced_bt_coexist) {
|
||||
priv->lib->bt_params &&
|
||||
priv->lib->bt_params->advanced_bt_coexist) {
|
||||
IWL_DEBUG_COEX(priv, "receive reply tx w/ bt_kill\n");
|
||||
}
|
||||
|
||||
|
|
|
@ -132,8 +132,8 @@ int iwl_init_alive_start(struct iwl_priv *priv)
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (priv->cfg->bt_params &&
|
||||
priv->cfg->bt_params->advanced_bt_coexist) {
|
||||
if (priv->lib->bt_params &&
|
||||
priv->lib->bt_params->advanced_bt_coexist) {
|
||||
/*
|
||||
* Tell uCode we are ready to perform calibration
|
||||
* need to perform this before any calibration
|
||||
|
@ -155,8 +155,8 @@ int iwl_init_alive_start(struct iwl_priv *priv)
|
|||
* temperature offset calibration is only needed for runtime ucode,
|
||||
* so prepare the value now.
|
||||
*/
|
||||
if (priv->cfg->need_temp_offset_calib) {
|
||||
if (priv->cfg->temp_offset_v2)
|
||||
if (priv->lib->need_temp_offset_calib) {
|
||||
if (priv->lib->temp_offset_v2)
|
||||
return iwl_set_temperature_offset_calib_v2(priv);
|
||||
else
|
||||
return iwl_set_temperature_offset_calib(priv);
|
||||
|
@ -277,7 +277,7 @@ static int iwl_alive_notify(struct iwl_priv *priv)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!priv->cfg->no_xtal_calib) {
|
||||
if (!priv->lib->no_xtal_calib) {
|
||||
ret = iwl_set_Xtal_calib(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
@ -60,9 +60,6 @@ static const struct iwl_base_params iwl1000_base_params = {
|
|||
.max_ll_items = OTP_MAX_LL_ITEMS_1000,
|
||||
.shadow_ram_support = false,
|
||||
.led_compensation = 51,
|
||||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.wd_timeout = IWL_WATCHDOG_DISABLED,
|
||||
.max_event_log_size = 128,
|
||||
};
|
||||
|
|
|
@ -72,14 +72,9 @@ static const struct iwl_base_params iwl2000_base_params = {
|
|||
.max_ll_items = OTP_MAX_LL_ITEMS_2x00,
|
||||
.shadow_ram_support = true,
|
||||
.led_compensation = 51,
|
||||
.adv_thermal_throttle = true,
|
||||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.wd_timeout = IWL_DEF_WD_TIMEOUT,
|
||||
.max_event_log_size = 512,
|
||||
.shadow_reg_enable = false, /* TODO: fix bugs using this feature */
|
||||
.hd_v2 = true,
|
||||
};
|
||||
|
||||
|
||||
|
@ -90,14 +85,9 @@ static const struct iwl_base_params iwl2030_base_params = {
|
|||
.max_ll_items = OTP_MAX_LL_ITEMS_2x00,
|
||||
.shadow_ram_support = true,
|
||||
.led_compensation = 57,
|
||||
.adv_thermal_throttle = true,
|
||||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.wd_timeout = IWL_LONG_WD_TIMEOUT,
|
||||
.max_event_log_size = 512,
|
||||
.shadow_reg_enable = false, /* TODO: fix bugs using this feature */
|
||||
.hd_v2 = true,
|
||||
};
|
||||
|
||||
static const struct iwl_ht_params iwl2000_ht_params = {
|
||||
|
@ -106,16 +96,6 @@ static const struct iwl_ht_params iwl2000_ht_params = {
|
|||
.ht40_bands = BIT(IEEE80211_BAND_2GHZ),
|
||||
};
|
||||
|
||||
static const struct iwl_bt_params iwl2030_bt_params = {
|
||||
/* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */
|
||||
.advanced_bt_coexist = true,
|
||||
.agg_time_limit = BT_AGG_THRESHOLD_DEF,
|
||||
.bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE,
|
||||
.bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT32,
|
||||
.bt_sco_disable = true,
|
||||
.bt_session_2 = true,
|
||||
};
|
||||
|
||||
static const struct iwl_eeprom_params iwl20x0_eeprom_params = {
|
||||
.regulatory_bands = {
|
||||
EEPROM_REG_BAND_1_CHANNELS,
|
||||
|
@ -137,12 +117,10 @@ static const struct iwl_eeprom_params iwl20x0_eeprom_params = {
|
|||
.device_family = IWL_DEVICE_FAMILY_2000, \
|
||||
.max_inst_size = IWL60_RTC_INST_SIZE, \
|
||||
.max_data_size = IWL60_RTC_DATA_SIZE, \
|
||||
.nvm_ver = EEPROM_2000_EEPROM_VERSION, \
|
||||
.nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
|
||||
.nvm_ver = EEPROM_2000_EEPROM_VERSION, \
|
||||
.nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
|
||||
.base_params = &iwl2000_base_params, \
|
||||
.eeprom_params = &iwl20x0_eeprom_params, \
|
||||
.need_temp_offset_calib = true, \
|
||||
.temp_offset_v2 = true, \
|
||||
.led_mode = IWL_LED_RF_STATE
|
||||
|
||||
const struct iwl_cfg iwl2000_2bgn_cfg = {
|
||||
|
@ -168,12 +146,8 @@ const struct iwl_cfg iwl2000_2bgn_d_cfg = {
|
|||
.nvm_ver = EEPROM_2000_EEPROM_VERSION, \
|
||||
.nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
|
||||
.base_params = &iwl2030_base_params, \
|
||||
.bt_params = &iwl2030_bt_params, \
|
||||
.eeprom_params = &iwl20x0_eeprom_params, \
|
||||
.need_temp_offset_calib = true, \
|
||||
.temp_offset_v2 = true, \
|
||||
.led_mode = IWL_LED_RF_STATE, \
|
||||
.adv_pm = true
|
||||
.led_mode = IWL_LED_RF_STATE
|
||||
|
||||
const struct iwl_cfg iwl2030_2bgn_cfg = {
|
||||
.name = "Intel(R) Centrino(R) Wireless-N 2230 BGN",
|
||||
|
@ -193,10 +167,7 @@ const struct iwl_cfg iwl2030_2bgn_cfg = {
|
|||
.nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
|
||||
.base_params = &iwl2000_base_params, \
|
||||
.eeprom_params = &iwl20x0_eeprom_params, \
|
||||
.need_temp_offset_calib = true, \
|
||||
.temp_offset_v2 = true, \
|
||||
.led_mode = IWL_LED_RF_STATE, \
|
||||
.adv_pm = true, \
|
||||
.rx_with_siso_diversity = true
|
||||
|
||||
const struct iwl_cfg iwl105_bgn_cfg = {
|
||||
|
@ -222,12 +193,8 @@ const struct iwl_cfg iwl105_bgn_d_cfg = {
|
|||
.nvm_ver = EEPROM_2000_EEPROM_VERSION, \
|
||||
.nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
|
||||
.base_params = &iwl2030_base_params, \
|
||||
.bt_params = &iwl2030_bt_params, \
|
||||
.eeprom_params = &iwl20x0_eeprom_params, \
|
||||
.need_temp_offset_calib = true, \
|
||||
.temp_offset_v2 = true, \
|
||||
.led_mode = IWL_LED_RF_STATE, \
|
||||
.adv_pm = true, \
|
||||
.rx_with_siso_diversity = true
|
||||
|
||||
const struct iwl_cfg iwl135_bgn_cfg = {
|
||||
|
|
|
@ -59,11 +59,8 @@ static const struct iwl_base_params iwl5000_base_params = {
|
|||
.num_of_queues = IWLAGN_NUM_QUEUES,
|
||||
.pll_cfg_val = CSR50_ANA_PLL_CFG_VAL,
|
||||
.led_compensation = 51,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.wd_timeout = IWL_WATCHDOG_DISABLED,
|
||||
.max_event_log_size = 512,
|
||||
.no_idle_support = true,
|
||||
};
|
||||
|
||||
static const struct iwl_ht_params iwl5000_ht_params = {
|
||||
|
@ -159,7 +156,6 @@ const struct iwl_cfg iwl5350_agn_cfg = {
|
|||
.nvm_calib_ver = EEPROM_5050_TX_POWER_VERSION, \
|
||||
.base_params = &iwl5000_base_params, \
|
||||
.eeprom_params = &iwl5000_eeprom_params, \
|
||||
.no_xtal_calib = true, \
|
||||
.led_mode = IWL_LED_BLINK, \
|
||||
.internal_wimax_coex = true
|
||||
|
||||
|
|
|
@ -82,10 +82,6 @@ static const struct iwl_base_params iwl6000_base_params = {
|
|||
.max_ll_items = OTP_MAX_LL_ITEMS_6x00,
|
||||
.shadow_ram_support = true,
|
||||
.led_compensation = 51,
|
||||
.adv_thermal_throttle = true,
|
||||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.wd_timeout = IWL_DEF_WD_TIMEOUT,
|
||||
.max_event_log_size = 512,
|
||||
.shadow_reg_enable = false, /* TODO: fix bugs using this feature */
|
||||
|
@ -98,10 +94,6 @@ static const struct iwl_base_params iwl6050_base_params = {
|
|||
.max_ll_items = OTP_MAX_LL_ITEMS_6x50,
|
||||
.shadow_ram_support = true,
|
||||
.led_compensation = 51,
|
||||
.adv_thermal_throttle = true,
|
||||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1500,
|
||||
.wd_timeout = IWL_DEF_WD_TIMEOUT,
|
||||
.max_event_log_size = 1024,
|
||||
.shadow_reg_enable = false, /* TODO: fix bugs using this feature */
|
||||
|
@ -114,10 +106,6 @@ static const struct iwl_base_params iwl6000_g2_base_params = {
|
|||
.max_ll_items = OTP_MAX_LL_ITEMS_6x00,
|
||||
.shadow_ram_support = true,
|
||||
.led_compensation = 57,
|
||||
.adv_thermal_throttle = true,
|
||||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.wd_timeout = IWL_LONG_WD_TIMEOUT,
|
||||
.max_event_log_size = 512,
|
||||
.shadow_reg_enable = false, /* TODO: fix bugs using this feature */
|
||||
|
@ -129,15 +117,6 @@ static const struct iwl_ht_params iwl6000_ht_params = {
|
|||
.ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
|
||||
};
|
||||
|
||||
static const struct iwl_bt_params iwl6000_bt_params = {
|
||||
/* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */
|
||||
.advanced_bt_coexist = true,
|
||||
.agg_time_limit = BT_AGG_THRESHOLD_DEF,
|
||||
.bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE,
|
||||
.bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT,
|
||||
.bt_sco_disable = true,
|
||||
};
|
||||
|
||||
static const struct iwl_eeprom_params iwl6000_eeprom_params = {
|
||||
.regulatory_bands = {
|
||||
EEPROM_REG_BAND_1_CHANNELS,
|
||||
|
@ -163,7 +142,6 @@ static const struct iwl_eeprom_params iwl6000_eeprom_params = {
|
|||
.nvm_calib_ver = EEPROM_6005_TX_POWER_VERSION, \
|
||||
.base_params = &iwl6000_g2_base_params, \
|
||||
.eeprom_params = &iwl6000_eeprom_params, \
|
||||
.need_temp_offset_calib = true, \
|
||||
.led_mode = IWL_LED_RF_STATE
|
||||
|
||||
const struct iwl_cfg iwl6005_2agn_cfg = {
|
||||
|
@ -217,11 +195,8 @@ const struct iwl_cfg iwl6005_2agn_mow2_cfg = {
|
|||
.nvm_ver = EEPROM_6030_EEPROM_VERSION, \
|
||||
.nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \
|
||||
.base_params = &iwl6000_g2_base_params, \
|
||||
.bt_params = &iwl6000_bt_params, \
|
||||
.eeprom_params = &iwl6000_eeprom_params, \
|
||||
.need_temp_offset_calib = true, \
|
||||
.led_mode = IWL_LED_RF_STATE, \
|
||||
.adv_pm = true \
|
||||
.led_mode = IWL_LED_RF_STATE
|
||||
|
||||
const struct iwl_cfg iwl6030_2agn_cfg = {
|
||||
.name = "Intel(R) Centrino(R) Advanced-N 6230 AGN",
|
||||
|
@ -256,11 +231,8 @@ const struct iwl_cfg iwl6030_2bg_cfg = {
|
|||
.nvm_ver = EEPROM_6030_EEPROM_VERSION, \
|
||||
.nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \
|
||||
.base_params = &iwl6000_g2_base_params, \
|
||||
.bt_params = &iwl6000_bt_params, \
|
||||
.eeprom_params = &iwl6000_eeprom_params, \
|
||||
.need_temp_offset_calib = true, \
|
||||
.led_mode = IWL_LED_RF_STATE, \
|
||||
.adv_pm = true
|
||||
.led_mode = IWL_LED_RF_STATE
|
||||
|
||||
const struct iwl_cfg iwl6035_2agn_cfg = {
|
||||
.name = "Intel(R) Centrino(R) Advanced-N 6235 AGN",
|
||||
|
|
|
@ -96,13 +96,9 @@ static const struct iwl_base_params iwl7000_base_params = {
|
|||
.pll_cfg_val = 0,
|
||||
.shadow_ram_support = true,
|
||||
.led_compensation = 57,
|
||||
.adv_thermal_throttle = true,
|
||||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.wd_timeout = IWL_LONG_WD_TIMEOUT,
|
||||
.max_event_log_size = 512,
|
||||
.shadow_reg_enable = false, /* TODO: fix bugs using this feature */
|
||||
.shadow_reg_enable = true,
|
||||
};
|
||||
|
||||
static const struct iwl_ht_params iwl7000_ht_params = {
|
||||
|
@ -118,14 +114,11 @@ static const struct iwl_ht_params iwl7000_ht_params = {
|
|||
.max_inst_size = IWL60_RTC_INST_SIZE, \
|
||||
.max_data_size = IWL60_RTC_DATA_SIZE, \
|
||||
.base_params = &iwl7000_base_params, \
|
||||
/* TODO: .bt_params? */ \
|
||||
.need_temp_offset_calib = true, \
|
||||
.led_mode = IWL_LED_RF_STATE, \
|
||||
.adv_pm = true \
|
||||
.led_mode = IWL_LED_RF_STATE
|
||||
|
||||
|
||||
const struct iwl_cfg iwl7260_2ac_cfg = {
|
||||
.name = "Intel(R) Dual Band Wireless AC7260",
|
||||
.name = "Intel(R) Dual Band Wireless AC 7260",
|
||||
.fw_name_pre = IWL7260_FW_PRE,
|
||||
IWL_DEVICE_7000,
|
||||
.ht_params = &iwl7000_ht_params,
|
||||
|
@ -133,8 +126,44 @@ const struct iwl_cfg iwl7260_2ac_cfg = {
|
|||
.nvm_calib_ver = IWL7260_TX_POWER_VERSION,
|
||||
};
|
||||
|
||||
const struct iwl_cfg iwl3160_ac_cfg = {
|
||||
.name = "Intel(R) Dual Band Wireless AC3160",
|
||||
const struct iwl_cfg iwl7260_2n_cfg = {
|
||||
.name = "Intel(R) Dual Band Wireless N 7260",
|
||||
.fw_name_pre = IWL7260_FW_PRE,
|
||||
IWL_DEVICE_7000,
|
||||
.ht_params = &iwl7000_ht_params,
|
||||
.nvm_ver = IWL7260_NVM_VERSION,
|
||||
.nvm_calib_ver = IWL7260_TX_POWER_VERSION,
|
||||
};
|
||||
|
||||
const struct iwl_cfg iwl7260_n_cfg = {
|
||||
.name = "Intel(R) Wireless N 7260",
|
||||
.fw_name_pre = IWL7260_FW_PRE,
|
||||
IWL_DEVICE_7000,
|
||||
.ht_params = &iwl7000_ht_params,
|
||||
.nvm_ver = IWL7260_NVM_VERSION,
|
||||
.nvm_calib_ver = IWL7260_TX_POWER_VERSION,
|
||||
};
|
||||
|
||||
const struct iwl_cfg iwl3160_2ac_cfg = {
|
||||
.name = "Intel(R) Dual Band Wireless AC 3160",
|
||||
.fw_name_pre = IWL3160_FW_PRE,
|
||||
IWL_DEVICE_7000,
|
||||
.ht_params = &iwl7000_ht_params,
|
||||
.nvm_ver = IWL3160_NVM_VERSION,
|
||||
.nvm_calib_ver = IWL3160_TX_POWER_VERSION,
|
||||
};
|
||||
|
||||
const struct iwl_cfg iwl3160_2n_cfg = {
|
||||
.name = "Intel(R) Dual Band Wireless N 3160",
|
||||
.fw_name_pre = IWL3160_FW_PRE,
|
||||
IWL_DEVICE_7000,
|
||||
.ht_params = &iwl7000_ht_params,
|
||||
.nvm_ver = IWL3160_NVM_VERSION,
|
||||
.nvm_calib_ver = IWL3160_TX_POWER_VERSION,
|
||||
};
|
||||
|
||||
const struct iwl_cfg iwl3160_n_cfg = {
|
||||
.name = "Intel(R) Wireless N 3160",
|
||||
.fw_name_pre = IWL3160_FW_PRE,
|
||||
IWL_DEVICE_7000,
|
||||
.ht_params = &iwl7000_ht_params,
|
||||
|
|
|
@ -136,17 +136,9 @@ enum iwl_led_mode {
|
|||
* @led_compensation: compensate on the led on/off time per HW according
|
||||
* to the deviation to achieve the desired led frequency.
|
||||
* The detail algorithm is described in iwl-led.c
|
||||
* @chain_noise_num_beacons: number of beacons used to compute chain noise
|
||||
* @adv_thermal_throttle: support advance thermal throttle
|
||||
* @support_ct_kill_exit: support ct kill exit condition
|
||||
* @plcp_delta_threshold: plcp error rate threshold used to trigger
|
||||
* radio tuning when there is a high receiving plcp error rate
|
||||
* @chain_noise_scale: default chain noise scale used for gain computation
|
||||
* @wd_timeout: TX queues watchdog timeout
|
||||
* @max_event_log_size: size of event log buffer size for ucode event logging
|
||||
* @shadow_reg_enable: HW shadow register support
|
||||
* @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up
|
||||
* @no_idle_support: do not support idle mode
|
||||
*/
|
||||
struct iwl_base_params {
|
||||
int eeprom_size;
|
||||
|
@ -157,31 +149,9 @@ struct iwl_base_params {
|
|||
const u16 max_ll_items;
|
||||
const bool shadow_ram_support;
|
||||
u16 led_compensation;
|
||||
bool adv_thermal_throttle;
|
||||
bool support_ct_kill_exit;
|
||||
u8 plcp_delta_threshold;
|
||||
s32 chain_noise_scale;
|
||||
unsigned int wd_timeout;
|
||||
u32 max_event_log_size;
|
||||
const bool shadow_reg_enable;
|
||||
const bool hd_v2;
|
||||
const bool no_idle_support;
|
||||
};
|
||||
|
||||
/*
|
||||
* @advanced_bt_coexist: support advanced bt coexist
|
||||
* @bt_init_traffic_load: specify initial bt traffic load
|
||||
* @bt_prio_boost: default bt priority boost value
|
||||
* @agg_time_limit: maximum number of uSec in aggregation
|
||||
* @bt_sco_disable: uCode should not response to BT in SCO/ESCO mode
|
||||
*/
|
||||
struct iwl_bt_params {
|
||||
bool advanced_bt_coexist;
|
||||
u8 bt_init_traffic_load;
|
||||
u32 bt_prio_boost;
|
||||
u16 agg_time_limit;
|
||||
bool bt_sco_disable;
|
||||
bool bt_session_2;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -231,16 +201,10 @@ struct iwl_eeprom_params {
|
|||
* @nvm_calib_ver: NVM calibration version
|
||||
* @lib: pointer to the lib ops
|
||||
* @base_params: pointer to basic parameters
|
||||
* @ht_params: point to ht patameters
|
||||
* @bt_params: pointer to bt parameters
|
||||
* @need_temp_offset_calib: need to perform temperature offset calibration
|
||||
* @no_xtal_calib: some devices do not need crystal calibration data,
|
||||
* don't send it to those
|
||||
* @ht_params: point to ht parameters
|
||||
* @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off)
|
||||
* @adv_pm: advance power management
|
||||
* @rx_with_siso_diversity: 1x1 device with rx antenna diversity
|
||||
* @internal_wimax_coex: internal wifi/wimax combo device
|
||||
* @temp_offset_v2: support v2 of temperature offset calibration
|
||||
*
|
||||
* We enable the driver to be backward compatible wrt. hardware features.
|
||||
* API differences in uCode shouldn't be handled here but through TLVs
|
||||
|
@ -264,15 +228,10 @@ struct iwl_cfg {
|
|||
const struct iwl_base_params *base_params;
|
||||
/* params likely to change within a device family */
|
||||
const struct iwl_ht_params *ht_params;
|
||||
const struct iwl_bt_params *bt_params;
|
||||
const struct iwl_eeprom_params *eeprom_params;
|
||||
const bool need_temp_offset_calib; /* if used set to true */
|
||||
const bool no_xtal_calib;
|
||||
enum iwl_led_mode led_mode;
|
||||
const bool adv_pm;
|
||||
const bool rx_with_siso_diversity;
|
||||
const bool internal_wimax_coex;
|
||||
const bool temp_offset_v2;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -320,6 +279,10 @@ extern const struct iwl_cfg iwl105_bgn_cfg;
|
|||
extern const struct iwl_cfg iwl105_bgn_d_cfg;
|
||||
extern const struct iwl_cfg iwl135_bgn_cfg;
|
||||
extern const struct iwl_cfg iwl7260_2ac_cfg;
|
||||
extern const struct iwl_cfg iwl3160_ac_cfg;
|
||||
extern const struct iwl_cfg iwl7260_2n_cfg;
|
||||
extern const struct iwl_cfg iwl7260_n_cfg;
|
||||
extern const struct iwl_cfg iwl3160_2ac_cfg;
|
||||
extern const struct iwl_cfg iwl3160_2n_cfg;
|
||||
extern const struct iwl_cfg iwl3160_n_cfg;
|
||||
|
||||
#endif /* __IWL_CONFIG_H__ */
|
||||
|
|
|
@ -472,4 +472,23 @@
|
|||
#define IWL_HOST_INT_CALIB_TIMEOUT_DEF (0x10)
|
||||
#define IWL_HOST_INT_CALIB_TIMEOUT_MIN (0x0)
|
||||
|
||||
/*****************************************************************************
|
||||
* 7000/3000 series SHR DTS addresses *
|
||||
*****************************************************************************/
|
||||
|
||||
/* Diode Results Register Structure: */
|
||||
enum dtd_diode_reg {
|
||||
DTS_DIODE_REG_DIG_VAL = 0x000000FF, /* bits [7:0] */
|
||||
DTS_DIODE_REG_VREF_LOW = 0x0000FF00, /* bits [15:8] */
|
||||
DTS_DIODE_REG_VREF_HIGH = 0x00FF0000, /* bits [23:16] */
|
||||
DTS_DIODE_REG_VREF_ID = 0x03000000, /* bits [25:24] */
|
||||
DTS_DIODE_REG_PASS_ONCE = 0x80000000, /* bits [31:31] */
|
||||
DTS_DIODE_REG_FLAGS_MSK = 0xFF000000, /* bits [31:24] */
|
||||
/* Those are the masks INSIDE the flags bit-field: */
|
||||
DTS_DIODE_REG_FLAGS_VREFS_ID_POS = 0,
|
||||
DTS_DIODE_REG_FLAGS_VREFS_ID = 0x00000003, /* bits [1:0] */
|
||||
DTS_DIODE_REG_FLAGS_PASS_ONCE_POS = 7,
|
||||
DTS_DIODE_REG_FLAGS_PASS_ONCE = 0x00000080, /* bits [7:7] */
|
||||
};
|
||||
|
||||
#endif /* !__iwl_csr_h__ */
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue