Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
This commit is contained in:
commit
45ea4ea2af
|
@ -145,7 +145,6 @@ usage should require reading the full document.
|
|||
interface in STA mode at first!
|
||||
</para>
|
||||
!Finclude/net/mac80211.h ieee80211_if_init_conf
|
||||
!Finclude/net/mac80211.h ieee80211_if_conf
|
||||
</chapter>
|
||||
|
||||
<chapter id="rx-tx">
|
||||
|
|
|
@ -501,5 +501,6 @@ source "drivers/net/wireless/zd1211rw/Kconfig"
|
|||
source "drivers/net/wireless/rt2x00/Kconfig"
|
||||
source "drivers/net/wireless/orinoco/Kconfig"
|
||||
source "drivers/net/wireless/wl12xx/Kconfig"
|
||||
source "drivers/net/wireless/iwmc3200wifi/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -60,3 +60,5 @@ obj-$(CONFIG_ATH_COMMON) += ath/
|
|||
obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o
|
||||
|
||||
obj-$(CONFIG_WL12XX) += wl12xx/
|
||||
|
||||
obj-$(CONFIG_IWM) += iwmc3200wifi/
|
||||
|
|
|
@ -1555,7 +1555,7 @@ static int ar9170_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
|||
|
||||
switch (key->alg) {
|
||||
case ALG_WEP:
|
||||
if (key->keylen == LEN_WEP40)
|
||||
if (key->keylen == WLAN_KEY_LEN_WEP40)
|
||||
ktype = AR9170_ENC_ALG_WEP64;
|
||||
else
|
||||
ktype = AR9170_ENC_ALG_WEP128;
|
||||
|
|
|
@ -242,8 +242,8 @@ static int ath5k_get_tx_stats(struct ieee80211_hw *hw,
|
|||
static u64 ath5k_get_tsf(struct ieee80211_hw *hw);
|
||||
static void ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf);
|
||||
static void ath5k_reset_tsf(struct ieee80211_hw *hw);
|
||||
static int ath5k_beacon_update(struct ath5k_softc *sc,
|
||||
struct sk_buff *skb);
|
||||
static int ath5k_beacon_update(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif);
|
||||
static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *bss_conf,
|
||||
|
@ -2127,8 +2127,10 @@ ath5k_beacon_send(struct ath5k_softc *sc)
|
|||
/* NB: hw still stops DMA, so proceed */
|
||||
}
|
||||
|
||||
/* Note: Beacon buffer is updated on beacon_update when mac80211
|
||||
* calls config_interface */
|
||||
/* refresh the beacon for AP mode */
|
||||
if (sc->opmode == NL80211_IFTYPE_AP)
|
||||
ath5k_beacon_update(sc->hw, sc->vif);
|
||||
|
||||
ath5k_hw_set_txdp(ah, sc->bhalq, bf->daddr);
|
||||
ath5k_hw_start_tx_dma(ah, sc->bhalq);
|
||||
ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "TXDP[%u] = %llx (%p)\n",
|
||||
|
@ -3047,28 +3049,55 @@ ath5k_reset_tsf(struct ieee80211_hw *hw)
|
|||
ath5k_hw_reset_tsf(sc->ah);
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates the beacon that is sent by ath5k_beacon_send. For adhoc,
|
||||
* this is called only once at config_bss time, for AP we do it every
|
||||
* SWBA interrupt so that the TIM will reflect buffered frames.
|
||||
*
|
||||
* Called with the beacon lock.
|
||||
*/
|
||||
static int
|
||||
ath5k_beacon_update(struct ath5k_softc *sc, struct sk_buff *skb)
|
||||
ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
struct ath5k_softc *sc = hw->priv;
|
||||
struct sk_buff *skb = ieee80211_beacon_get(hw, vif);
|
||||
|
||||
if (!skb) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ath5k_debug_dump_skb(sc, skb, "BC ", 1);
|
||||
|
||||
spin_lock_irqsave(&sc->block, flags);
|
||||
ath5k_txbuf_free(sc, sc->bbuf);
|
||||
sc->bbuf->skb = skb;
|
||||
ret = ath5k_beacon_setup(sc, sc->bbuf);
|
||||
if (ret)
|
||||
sc->bbuf->skb = NULL;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the beacon and reconfigure the beacon queues.
|
||||
*/
|
||||
static void
|
||||
ath5k_beacon_reconfig(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
struct ath5k_softc *sc = hw->priv;
|
||||
|
||||
spin_lock_irqsave(&sc->block, flags);
|
||||
ret = ath5k_beacon_update(hw, vif);
|
||||
spin_unlock_irqrestore(&sc->block, flags);
|
||||
if (!ret) {
|
||||
if (ret == 0) {
|
||||
ath5k_beacon_config(sc);
|
||||
mmiowb();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
set_beacon_filter(struct ieee80211_hw *hw, bool enable)
|
||||
{
|
||||
|
@ -3118,10 +3147,7 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
|
|||
(vif->type == NL80211_IFTYPE_ADHOC ||
|
||||
vif->type == NL80211_IFTYPE_MESH_POINT ||
|
||||
vif->type == NL80211_IFTYPE_AP)) {
|
||||
struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
|
||||
|
||||
if (beacon)
|
||||
ath5k_beacon_update(sc, beacon);
|
||||
ath5k_beacon_reconfig(hw, vif);
|
||||
}
|
||||
|
||||
unlock:
|
||||
|
|
|
@ -1038,9 +1038,9 @@ int ath5k_keycache_type(const struct ieee80211_key_conf *key)
|
|||
case ALG_CCMP:
|
||||
return AR5K_KEYTABLE_TYPE_CCM;
|
||||
case ALG_WEP:
|
||||
if (key->keylen == LEN_WEP40)
|
||||
if (key->keylen == WLAN_KEY_LEN_WEP40)
|
||||
return AR5K_KEYTABLE_TYPE_40;
|
||||
else if (key->keylen == LEN_WEP104)
|
||||
else if (key->keylen == WLAN_KEY_LEN_WEP104)
|
||||
return AR5K_KEYTABLE_TYPE_104;
|
||||
return -EINVAL;
|
||||
default:
|
||||
|
|
|
@ -1897,6 +1897,9 @@ ath5k_get_linear_pcdac_min(const u8 *stepL, const u8 *stepR,
|
|||
s16 min_pwrL, min_pwrR;
|
||||
s16 pwr_i;
|
||||
|
||||
if (WARN_ON(stepL[0] == stepL[1] || stepR[0] == stepR[1]))
|
||||
return 0;
|
||||
|
||||
if (pwrL[0] == pwrL[1])
|
||||
min_pwrL = pwrL[0];
|
||||
else {
|
||||
|
|
|
@ -515,6 +515,10 @@ struct ath_rfkill {
|
|||
#define SC_OP_LED_ON BIT(13)
|
||||
#define SC_OP_SCANNING BIT(14)
|
||||
#define SC_OP_TSF_RESET BIT(15)
|
||||
#define SC_OP_WAIT_FOR_CAB BIT(16)
|
||||
#define SC_OP_WAIT_FOR_PSPOLL_DATA BIT(17)
|
||||
#define SC_OP_WAIT_FOR_TX_ACK BIT(18)
|
||||
#define SC_OP_BEACON_SYNC BIT(19)
|
||||
|
||||
struct ath_bus_ops {
|
||||
void (*read_cachesize)(struct ath_softc *sc, int *csz);
|
||||
|
@ -599,6 +603,7 @@ struct ath_softc {
|
|||
struct ath9k_debug debug;
|
||||
#endif
|
||||
struct ath_bus_ops *bus_ops;
|
||||
struct ath_beacon_config cur_beacon_conf;
|
||||
};
|
||||
|
||||
struct ath_wiphy {
|
||||
|
@ -676,7 +681,9 @@ static inline void ath9k_ps_restore(struct ath_softc *sc)
|
|||
{
|
||||
if (atomic_dec_and_test(&sc->ps_usecount))
|
||||
if ((sc->hw->conf.flags & IEEE80211_CONF_PS) &&
|
||||
!(sc->sc_flags & SC_OP_WAIT_FOR_BEACON))
|
||||
!(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON |
|
||||
SC_OP_WAIT_FOR_PSPOLL_DATA |
|
||||
SC_OP_WAIT_FOR_TX_ACK)))
|
||||
ath9k_hw_setpower(sc->sc_ah,
|
||||
sc->sc_ah->restore_mode);
|
||||
}
|
||||
|
|
|
@ -507,8 +507,7 @@ void ath_beacon_tasklet(unsigned long data)
|
|||
* slot. Slots that are not occupied will generate nothing.
|
||||
*/
|
||||
static void ath_beacon_config_ap(struct ath_softc *sc,
|
||||
struct ath_beacon_config *conf,
|
||||
struct ath_vif *avp)
|
||||
struct ath_beacon_config *conf)
|
||||
{
|
||||
u32 nexttbtt, intval;
|
||||
|
||||
|
@ -553,14 +552,14 @@ static void ath_beacon_config_ap(struct ath_softc *sc,
|
|||
* we've associated with.
|
||||
*/
|
||||
static void ath_beacon_config_sta(struct ath_softc *sc,
|
||||
struct ath_beacon_config *conf,
|
||||
struct ath_vif *avp)
|
||||
struct ath_beacon_config *conf)
|
||||
{
|
||||
struct ath9k_beacon_state bs;
|
||||
int dtimperiod, dtimcount, sleepduration;
|
||||
int cfpperiod, cfpcount;
|
||||
u32 nexttbtt = 0, intval, tsftu;
|
||||
u64 tsf;
|
||||
int num_beacons, offset, dtim_dec_count, cfp_dec_count;
|
||||
|
||||
memset(&bs, 0, sizeof(bs));
|
||||
intval = conf->beacon_interval & ATH9K_BEACON_PERIOD;
|
||||
|
@ -588,14 +587,27 @@ static void ath_beacon_config_sta(struct ath_softc *sc,
|
|||
*/
|
||||
tsf = ath9k_hw_gettsf64(sc->sc_ah);
|
||||
tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
|
||||
do {
|
||||
|
||||
num_beacons = tsftu / intval + 1;
|
||||
offset = tsftu % intval;
|
||||
nexttbtt = tsftu - offset;
|
||||
if (offset)
|
||||
nexttbtt += intval;
|
||||
if (--dtimcount < 0) {
|
||||
dtimcount = dtimperiod - 1;
|
||||
if (--cfpcount < 0)
|
||||
cfpcount = cfpperiod - 1;
|
||||
}
|
||||
} while (nexttbtt < tsftu);
|
||||
|
||||
/* DTIM Beacon every dtimperiod Beacon */
|
||||
dtim_dec_count = num_beacons % dtimperiod;
|
||||
/* CFP every cfpperiod DTIM Beacon */
|
||||
cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod;
|
||||
if (dtim_dec_count)
|
||||
cfp_dec_count++;
|
||||
|
||||
dtimcount -= dtim_dec_count;
|
||||
if (dtimcount < 0)
|
||||
dtimcount += dtimperiod;
|
||||
|
||||
cfpcount -= cfp_dec_count;
|
||||
if (cfpcount < 0)
|
||||
cfpcount += cfpperiod;
|
||||
|
||||
bs.bs_intval = intval;
|
||||
bs.bs_nexttbtt = nexttbtt;
|
||||
|
@ -654,7 +666,6 @@ static void ath_beacon_config_sta(struct ath_softc *sc,
|
|||
|
||||
static void ath_beacon_config_adhoc(struct ath_softc *sc,
|
||||
struct ath_beacon_config *conf,
|
||||
struct ath_vif *avp,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
u64 tsf;
|
||||
|
@ -698,43 +709,50 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc,
|
|||
sc->beacon.bmisscnt = 0;
|
||||
ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
|
||||
|
||||
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)
|
||||
/* FIXME: Handle properly when vif is NULL */
|
||||
if (vif && sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_VEOL)
|
||||
ath_beacon_start_adhoc(sc, vif);
|
||||
}
|
||||
|
||||
void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif)
|
||||
{
|
||||
struct ath_beacon_config conf;
|
||||
struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
|
||||
enum nl80211_iftype iftype;
|
||||
|
||||
/* Setup the beacon configuration parameters */
|
||||
|
||||
memset(&conf, 0, sizeof(struct ath_beacon_config));
|
||||
conf.beacon_interval = sc->beacon_interval ? : ATH_DEFAULT_BINTVAL;
|
||||
conf.listen_interval = 1;
|
||||
conf.dtim_period = conf.beacon_interval;
|
||||
conf.dtim_count = 1;
|
||||
conf.bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf.beacon_interval;
|
||||
|
||||
if (vif) {
|
||||
struct ath_vif *avp = (struct ath_vif *)vif->drv_priv;
|
||||
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
|
||||
|
||||
switch(avp->av_opmode) {
|
||||
case NL80211_IFTYPE_AP:
|
||||
ath_beacon_config_ap(sc, &conf, avp);
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
ath_beacon_config_adhoc(sc, &conf, avp, vif);
|
||||
break;
|
||||
case NL80211_IFTYPE_STATION:
|
||||
ath_beacon_config_sta(sc, &conf, avp);
|
||||
break;
|
||||
default:
|
||||
DPRINTF(sc, ATH_DBG_CONFIG,
|
||||
"Unsupported beaconing mode\n");
|
||||
return;
|
||||
}
|
||||
iftype = vif->type;
|
||||
|
||||
sc->sc_flags |= SC_OP_BEACONS;
|
||||
cur_conf->beacon_interval = bss_conf->beacon_int;
|
||||
cur_conf->dtim_period = bss_conf->dtim_period;
|
||||
cur_conf->listen_interval = 1;
|
||||
cur_conf->dtim_count = 1;
|
||||
cur_conf->bmiss_timeout =
|
||||
ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval;
|
||||
} else {
|
||||
iftype = sc->sc_ah->opmode;
|
||||
}
|
||||
|
||||
|
||||
switch (iftype) {
|
||||
case NL80211_IFTYPE_AP:
|
||||
ath_beacon_config_ap(sc, cur_conf);
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
ath_beacon_config_adhoc(sc, cur_conf, vif);
|
||||
break;
|
||||
case NL80211_IFTYPE_STATION:
|
||||
ath_beacon_config_sta(sc, cur_conf);
|
||||
break;
|
||||
default:
|
||||
DPRINTF(sc, ATH_DBG_CONFIG,
|
||||
"Unsupported beaconing mode\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sc->sc_flags |= SC_OP_BEACONS;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ enum ATH_DEBUG {
|
|||
ATH_DBG_BEACON = 0x00000100,
|
||||
ATH_DBG_CONFIG = 0x00000200,
|
||||
ATH_DBG_FATAL = 0x00000400,
|
||||
ATH_DBG_PS = 0x00000800,
|
||||
ATH_DBG_ANY = 0xffffffff
|
||||
};
|
||||
|
||||
|
|
|
@ -2472,14 +2472,14 @@ bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry,
|
|||
}
|
||||
break;
|
||||
case ATH9K_CIPHER_WEP:
|
||||
if (k->kv_len < LEN_WEP40) {
|
||||
if (k->kv_len < WLAN_KEY_LEN_WEP40) {
|
||||
DPRINTF(ah->ah_sc, ATH_DBG_ANY,
|
||||
"WEP key length %u too small\n", k->kv_len);
|
||||
return false;
|
||||
}
|
||||
if (k->kv_len <= LEN_WEP40)
|
||||
if (k->kv_len <= WLAN_KEY_LEN_WEP40)
|
||||
keyType = AR_KEYTABLE_TYPE_40;
|
||||
else if (k->kv_len <= LEN_WEP104)
|
||||
else if (k->kv_len <= WLAN_KEY_LEN_WEP104)
|
||||
keyType = AR_KEYTABLE_TYPE_104;
|
||||
else
|
||||
keyType = AR_KEYTABLE_TYPE_128;
|
||||
|
@ -2498,7 +2498,7 @@ bool ath9k_hw_set_keycache_entry(struct ath_hw *ah, u16 entry,
|
|||
key2 = get_unaligned_le32(k->kv_val + 6);
|
||||
key3 = get_unaligned_le16(k->kv_val + 10);
|
||||
key4 = get_unaligned_le32(k->kv_val + 12);
|
||||
if (k->kv_len <= LEN_WEP104)
|
||||
if (k->kv_len <= WLAN_KEY_LEN_WEP104)
|
||||
key4 &= 0xff;
|
||||
|
||||
/*
|
||||
|
|
|
@ -35,14 +35,14 @@ MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption");
|
|||
#define CHAN2G(_freq, _idx) { \
|
||||
.center_freq = (_freq), \
|
||||
.hw_value = (_idx), \
|
||||
.max_power = 30, \
|
||||
.max_power = 20, \
|
||||
}
|
||||
|
||||
#define CHAN5G(_freq, _idx) { \
|
||||
.band = IEEE80211_BAND_5GHZ, \
|
||||
.center_freq = (_freq), \
|
||||
.hw_value = (_idx), \
|
||||
.max_power = 30, \
|
||||
.max_power = 20, \
|
||||
}
|
||||
|
||||
/* Some 2 GHz radios are actually tunable on 2312-2732
|
||||
|
@ -280,7 +280,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
|
|||
if (r) {
|
||||
DPRINTF(sc, ATH_DBG_FATAL,
|
||||
"Unable to reset channel (%u Mhz) "
|
||||
"reset status %u\n",
|
||||
"reset status %d\n",
|
||||
channel->center_freq, r);
|
||||
spin_unlock_bh(&sc->sc_resetlock);
|
||||
return r;
|
||||
|
@ -329,6 +329,12 @@ static void ath_ani_calibrate(unsigned long data)
|
|||
if (sc->sc_flags & SC_OP_SCANNING)
|
||||
goto set_timer;
|
||||
|
||||
/* Only calibrate if awake */
|
||||
if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE)
|
||||
goto set_timer;
|
||||
|
||||
ath9k_ps_wakeup(sc);
|
||||
|
||||
/* Long calibration runs independently of short calibration. */
|
||||
if ((timestamp - sc->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
|
||||
longcal = true;
|
||||
|
@ -380,6 +386,8 @@ static void ath_ani_calibrate(unsigned long data)
|
|||
}
|
||||
}
|
||||
|
||||
ath9k_ps_restore(sc);
|
||||
|
||||
set_timer:
|
||||
/*
|
||||
* Set timer interval based on previous results.
|
||||
|
@ -455,8 +463,11 @@ static void ath9k_tasklet(unsigned long data)
|
|||
struct ath_softc *sc = (struct ath_softc *)data;
|
||||
u32 status = sc->intrstatus;
|
||||
|
||||
ath9k_ps_wakeup(sc);
|
||||
|
||||
if (status & ATH9K_INT_FATAL) {
|
||||
ath_reset(sc, false);
|
||||
ath9k_ps_restore(sc);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -469,8 +480,19 @@ static void ath9k_tasklet(unsigned long data)
|
|||
if (status & ATH9K_INT_TX)
|
||||
ath_tx_tasklet(sc);
|
||||
|
||||
if ((status & ATH9K_INT_TSFOOR) &&
|
||||
(sc->hw->conf.flags & IEEE80211_CONF_PS)) {
|
||||
/*
|
||||
* TSF sync does not look correct; remain awake to sync with
|
||||
* the next Beacon.
|
||||
*/
|
||||
DPRINTF(sc, ATH_DBG_PS, "TSFOOR - Sync with next Beacon\n");
|
||||
sc->sc_flags |= SC_OP_WAIT_FOR_BEACON | SC_OP_BEACON_SYNC;
|
||||
}
|
||||
|
||||
/* re-enable hardware interrupt */
|
||||
ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
|
||||
ath9k_ps_restore(sc);
|
||||
}
|
||||
|
||||
irqreturn_t ath_isr(int irq, void *dev)
|
||||
|
@ -498,14 +520,11 @@ irqreturn_t ath_isr(int irq, void *dev)
|
|||
if (sc->sc_flags & SC_OP_INVALID)
|
||||
return IRQ_NONE;
|
||||
|
||||
ath9k_ps_wakeup(sc);
|
||||
|
||||
/* shared irq, not for us */
|
||||
|
||||
if (!ath9k_hw_intrpend(ah)) {
|
||||
ath9k_ps_restore(sc);
|
||||
if (!ath9k_hw_intrpend(ah))
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Figure out the reason(s) for the interrupt. Note
|
||||
|
@ -520,10 +539,8 @@ irqreturn_t ath_isr(int irq, void *dev)
|
|||
* If there are no status bits set, then this interrupt was not
|
||||
* for me (should have been caught above).
|
||||
*/
|
||||
if (!status) {
|
||||
ath9k_ps_restore(sc);
|
||||
if (!status)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* Cache the status */
|
||||
sc->intrstatus = status;
|
||||
|
@ -560,20 +577,17 @@ irqreturn_t ath_isr(int irq, void *dev)
|
|||
ath9k_hw_set_interrupts(ah, sc->imask);
|
||||
}
|
||||
|
||||
if (status & ATH9K_INT_TIM_TIMER) {
|
||||
if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
|
||||
if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
|
||||
if (status & ATH9K_INT_TIM_TIMER) {
|
||||
/* Clear RxAbort bit so that we can
|
||||
* receive frames */
|
||||
ath9k_hw_setpower(ah, ATH9K_PM_AWAKE);
|
||||
ath9k_hw_setrxabort(ah, 0);
|
||||
sched = true;
|
||||
ath9k_hw_setrxabort(sc->sc_ah, 0);
|
||||
sc->sc_flags |= SC_OP_WAIT_FOR_BEACON;
|
||||
}
|
||||
}
|
||||
|
||||
chip_reset:
|
||||
|
||||
ath9k_ps_restore(sc);
|
||||
ath_debug_stat_interrupt(sc, status);
|
||||
|
||||
if (sched) {
|
||||
|
@ -900,6 +914,13 @@ static void ath9k_bss_assoc_info(struct ath_softc *sc,
|
|||
if (avp->av_opmode == NL80211_IFTYPE_STATION) {
|
||||
sc->curaid = bss_conf->aid;
|
||||
ath9k_hw_write_associd(sc);
|
||||
|
||||
/*
|
||||
* Request a re-configuration of Beacon related timers
|
||||
* on the receipt of the first Beacon frame (i.e.,
|
||||
* after time sync with the AP).
|
||||
*/
|
||||
sc->sc_flags |= SC_OP_BEACON_SYNC;
|
||||
}
|
||||
|
||||
/* Configure the beacon */
|
||||
|
@ -1094,7 +1115,7 @@ void ath_radio_enable(struct ath_softc *sc)
|
|||
if (r) {
|
||||
DPRINTF(sc, ATH_DBG_FATAL,
|
||||
"Unable to reset channel %u (%uMhz) ",
|
||||
"reset status %u\n",
|
||||
"reset status %d\n",
|
||||
channel->center_freq, r);
|
||||
}
|
||||
spin_unlock_bh(&sc->sc_resetlock);
|
||||
|
@ -1146,7 +1167,7 @@ void ath_radio_disable(struct ath_softc *sc)
|
|||
if (r) {
|
||||
DPRINTF(sc, ATH_DBG_FATAL,
|
||||
"Unable to reset channel %u (%uMhz) "
|
||||
"reset status %u\n",
|
||||
"reset status %d\n",
|
||||
channel->center_freq, r);
|
||||
}
|
||||
spin_unlock_bh(&sc->sc_resetlock);
|
||||
|
@ -1416,8 +1437,6 @@ static int ath_init(u16 devid, struct ath_softc *sc)
|
|||
for (i = 0; i < sc->keymax; i++)
|
||||
ath9k_hw_keyreset(ah, (u16) i);
|
||||
|
||||
error = ath_regd_init(&sc->sc_ah->regulatory, sc->hw->wiphy,
|
||||
ath9k_reg_notifier);
|
||||
if (error)
|
||||
goto bad;
|
||||
|
||||
|
@ -1630,14 +1649,19 @@ int ath_attach(u16 devid, struct ath_softc *sc)
|
|||
if (error != 0)
|
||||
return error;
|
||||
|
||||
reg = &sc->sc_ah->regulatory;
|
||||
|
||||
/* get mac address from hardware and set in mac80211 */
|
||||
|
||||
SET_IEEE80211_PERM_ADDR(hw, sc->sc_ah->macaddr);
|
||||
|
||||
ath_set_hw_capab(sc, hw);
|
||||
|
||||
error = ath_regd_init(&sc->sc_ah->regulatory, sc->hw->wiphy,
|
||||
ath9k_reg_notifier);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
reg = &sc->sc_ah->regulatory;
|
||||
|
||||
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
|
||||
setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap);
|
||||
if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes))
|
||||
|
@ -1709,7 +1733,7 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
|
|||
r = ath9k_hw_reset(ah, sc->sc_ah->curchan, false);
|
||||
if (r)
|
||||
DPRINTF(sc, ATH_DBG_FATAL,
|
||||
"Unable to reset hardware; reset status %u\n", r);
|
||||
"Unable to reset hardware; reset status %d\n", r);
|
||||
spin_unlock_bh(&sc->sc_resetlock);
|
||||
|
||||
if (ath_startrecv(sc) != 0)
|
||||
|
@ -2001,7 +2025,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
|
|||
r = ath9k_hw_reset(sc->sc_ah, init_channel, false);
|
||||
if (r) {
|
||||
DPRINTF(sc, ATH_DBG_FATAL,
|
||||
"Unable to reset hardware; reset status %u "
|
||||
"Unable to reset hardware; reset status %d "
|
||||
"(freq %u MHz)\n", r,
|
||||
curchan->center_freq);
|
||||
spin_unlock_bh(&sc->sc_resetlock);
|
||||
|
@ -2074,6 +2098,46 @@ static int ath9k_tx(struct ieee80211_hw *hw,
|
|||
goto exit;
|
||||
}
|
||||
|
||||
if (sc->hw->conf.flags & IEEE80211_CONF_PS) {
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
||||
/*
|
||||
* mac80211 does not set PM field for normal data frames, so we
|
||||
* need to update that based on the current PS mode.
|
||||
*/
|
||||
if (ieee80211_is_data(hdr->frame_control) &&
|
||||
!ieee80211_is_nullfunc(hdr->frame_control) &&
|
||||
!ieee80211_has_pm(hdr->frame_control)) {
|
||||
DPRINTF(sc, ATH_DBG_PS, "Add PM=1 for a TX frame "
|
||||
"while in PS mode\n");
|
||||
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(sc->sc_ah->power_mode != ATH9K_PM_AWAKE)) {
|
||||
/*
|
||||
* We are using PS-Poll and mac80211 can request TX while in
|
||||
* power save mode. Need to wake up hardware for the TX to be
|
||||
* completed and if needed, also for RX of buffered frames.
|
||||
*/
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
||||
ath9k_ps_wakeup(sc);
|
||||
ath9k_hw_setrxabort(sc->sc_ah, 0);
|
||||
if (ieee80211_is_pspoll(hdr->frame_control)) {
|
||||
DPRINTF(sc, ATH_DBG_PS, "Sending PS-Poll to pick a "
|
||||
"buffered frame\n");
|
||||
sc->sc_flags |= SC_OP_WAIT_FOR_PSPOLL_DATA;
|
||||
} else {
|
||||
DPRINTF(sc, ATH_DBG_PS, "Wake up to complete TX\n");
|
||||
sc->sc_flags |= SC_OP_WAIT_FOR_TX_ACK;
|
||||
}
|
||||
/*
|
||||
* The actual restore operation will happen only after
|
||||
* the sc_flags bit is cleared. We are just dropping
|
||||
* the ps_usecount here.
|
||||
*/
|
||||
ath9k_ps_restore(sc);
|
||||
}
|
||||
|
||||
memset(&txctl, 0, sizeof(struct ath_tx_control));
|
||||
|
||||
/*
|
||||
|
@ -2311,7 +2375,10 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
|
|||
if (!(ah->caps.hw_caps &
|
||||
ATH9K_HW_CAP_AUTOSLEEP)) {
|
||||
ath9k_hw_setrxabort(sc->sc_ah, 0);
|
||||
sc->sc_flags &= ~SC_OP_WAIT_FOR_BEACON;
|
||||
sc->sc_flags &= ~(SC_OP_WAIT_FOR_BEACON |
|
||||
SC_OP_WAIT_FOR_CAB |
|
||||
SC_OP_WAIT_FOR_PSPOLL_DATA |
|
||||
SC_OP_WAIT_FOR_TX_ACK);
|
||||
if (sc->imask & ATH9K_INT_TIM_TIMER) {
|
||||
sc->imask &= ~ATH9K_INT_TIM_TIMER;
|
||||
ath9k_hw_set_interrupts(sc->sc_ah,
|
||||
|
@ -2386,8 +2453,10 @@ static void ath9k_configure_filter(struct ieee80211_hw *hw,
|
|||
*total_flags &= SUPPORTED_FILTERS;
|
||||
|
||||
sc->rx.rxfilter = *total_flags;
|
||||
ath9k_ps_wakeup(sc);
|
||||
rfilt = ath_calcrxfilter(sc);
|
||||
ath9k_hw_setrxfilter(sc->sc_ah, rfilt);
|
||||
ath9k_ps_restore(sc);
|
||||
|
||||
DPRINTF(sc, ATH_DBG_CONFIG, "Set HW RX filter: 0x%x\n", sc->rx.rxfilter);
|
||||
}
|
||||
|
|
|
@ -473,6 +473,159 @@ void ath_flushrecv(struct ath_softc *sc)
|
|||
spin_unlock_bh(&sc->rx.rxflushlock);
|
||||
}
|
||||
|
||||
static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb)
|
||||
{
|
||||
/* Check whether the Beacon frame has DTIM indicating buffered bc/mc */
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
u8 *pos, *end, id, elen;
|
||||
struct ieee80211_tim_ie *tim;
|
||||
|
||||
mgmt = (struct ieee80211_mgmt *)skb->data;
|
||||
pos = mgmt->u.beacon.variable;
|
||||
end = skb->data + skb->len;
|
||||
|
||||
while (pos + 2 < end) {
|
||||
id = *pos++;
|
||||
elen = *pos++;
|
||||
if (pos + elen > end)
|
||||
break;
|
||||
|
||||
if (id == WLAN_EID_TIM) {
|
||||
if (elen < sizeof(*tim))
|
||||
break;
|
||||
tim = (struct ieee80211_tim_ie *) pos;
|
||||
if (tim->dtim_count != 0)
|
||||
break;
|
||||
return tim->bitmap_ctrl & 0x01;
|
||||
}
|
||||
|
||||
pos += elen;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ath_rx_ps_back_to_sleep(struct ath_softc *sc)
|
||||
{
|
||||
sc->sc_flags &= ~(SC_OP_WAIT_FOR_BEACON | SC_OP_WAIT_FOR_CAB);
|
||||
}
|
||||
|
||||
static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
|
||||
if (skb->len < 24 + 8 + 2 + 2)
|
||||
return;
|
||||
|
||||
mgmt = (struct ieee80211_mgmt *)skb->data;
|
||||
if (memcmp(sc->curbssid, mgmt->bssid, ETH_ALEN) != 0)
|
||||
return; /* not from our current AP */
|
||||
|
||||
if (sc->sc_flags & SC_OP_BEACON_SYNC) {
|
||||
sc->sc_flags &= ~SC_OP_BEACON_SYNC;
|
||||
DPRINTF(sc, ATH_DBG_PS, "Reconfigure Beacon timers based on "
|
||||
"timestamp from the AP\n");
|
||||
ath_beacon_config(sc, NULL);
|
||||
}
|
||||
|
||||
if (!(sc->hw->conf.flags & IEEE80211_CONF_PS)) {
|
||||
/* We are not in PS mode anymore; remain awake */
|
||||
DPRINTF(sc, ATH_DBG_PS, "Not in PS mode anymore, remain "
|
||||
"awake\n");
|
||||
sc->sc_flags &= ~(SC_OP_WAIT_FOR_BEACON | SC_OP_WAIT_FOR_CAB);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ath_beacon_dtim_pending_cab(skb)) {
|
||||
/*
|
||||
* Remain awake waiting for buffered broadcast/multicast
|
||||
* frames.
|
||||
*/
|
||||
DPRINTF(sc, ATH_DBG_PS, "Received DTIM beacon indicating "
|
||||
"buffered broadcast/multicast frame(s)\n");
|
||||
sc->sc_flags |= SC_OP_WAIT_FOR_CAB;
|
||||
return;
|
||||
}
|
||||
|
||||
if (sc->sc_flags & SC_OP_WAIT_FOR_CAB) {
|
||||
/*
|
||||
* This can happen if a broadcast frame is dropped or the AP
|
||||
* fails to send a frame indicating that all CAB frames have
|
||||
* been delivered.
|
||||
*/
|
||||
DPRINTF(sc, ATH_DBG_PS, "PS wait for CAB frames timed out\n");
|
||||
}
|
||||
|
||||
/* No more broadcast/multicast frames to be received at this point. */
|
||||
ath_rx_ps_back_to_sleep(sc);
|
||||
}
|
||||
|
||||
static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_hdr *hdr;
|
||||
|
||||
hdr = (struct ieee80211_hdr *)skb->data;
|
||||
|
||||
/* Process Beacon and CAB receive in PS state */
|
||||
if ((sc->sc_flags & SC_OP_WAIT_FOR_BEACON) &&
|
||||
ieee80211_is_beacon(hdr->frame_control))
|
||||
ath_rx_ps_beacon(sc, skb);
|
||||
else if ((sc->sc_flags & SC_OP_WAIT_FOR_CAB) &&
|
||||
(ieee80211_is_data(hdr->frame_control) ||
|
||||
ieee80211_is_action(hdr->frame_control)) &&
|
||||
is_multicast_ether_addr(hdr->addr1) &&
|
||||
!ieee80211_has_moredata(hdr->frame_control)) {
|
||||
DPRINTF(sc, ATH_DBG_PS, "All PS CAB frames received, back to "
|
||||
"sleep\n");
|
||||
/*
|
||||
* No more broadcast/multicast frames to be received at this
|
||||
* point.
|
||||
*/
|
||||
ath_rx_ps_back_to_sleep(sc);
|
||||
} else if ((sc->sc_flags & SC_OP_WAIT_FOR_PSPOLL_DATA) &&
|
||||
!is_multicast_ether_addr(hdr->addr1) &&
|
||||
!ieee80211_has_morefrags(hdr->frame_control)) {
|
||||
sc->sc_flags &= ~SC_OP_WAIT_FOR_PSPOLL_DATA;
|
||||
DPRINTF(sc, ATH_DBG_PS, "Going back to sleep after having "
|
||||
"received PS-Poll data (0x%x)\n",
|
||||
sc->sc_flags & (SC_OP_WAIT_FOR_BEACON |
|
||||
SC_OP_WAIT_FOR_CAB |
|
||||
SC_OP_WAIT_FOR_PSPOLL_DATA |
|
||||
SC_OP_WAIT_FOR_TX_ACK));
|
||||
}
|
||||
}
|
||||
|
||||
static void ath_rx_send_to_mac80211(struct ath_softc *sc, struct sk_buff *skb,
|
||||
struct ieee80211_rx_status *rx_status)
|
||||
{
|
||||
struct ieee80211_hdr *hdr;
|
||||
|
||||
hdr = (struct ieee80211_hdr *)skb->data;
|
||||
|
||||
/* Send the frame to mac80211 */
|
||||
if (is_multicast_ether_addr(hdr->addr1)) {
|
||||
int i;
|
||||
/*
|
||||
* Deliver broadcast/multicast frames to all suitable
|
||||
* virtual wiphys.
|
||||
*/
|
||||
/* TODO: filter based on channel configuration */
|
||||
for (i = 0; i < sc->num_sec_wiphy; i++) {
|
||||
struct ath_wiphy *aphy = sc->sec_wiphy[i];
|
||||
struct sk_buff *nskb;
|
||||
if (aphy == NULL)
|
||||
continue;
|
||||
nskb = skb_copy(skb, GFP_ATOMIC);
|
||||
if (nskb)
|
||||
__ieee80211_rx(aphy->hw, nskb, rx_status);
|
||||
}
|
||||
__ieee80211_rx(sc->hw, skb, rx_status);
|
||||
} else {
|
||||
/* Deliver unicast frames based on receiver address */
|
||||
__ieee80211_rx(ath_get_virt_hw(sc, hdr), skb, rx_status);
|
||||
}
|
||||
}
|
||||
|
||||
int ath_rx_tasklet(struct ath_softc *sc, int flush)
|
||||
{
|
||||
#define PA2DESC(_sc, _pa) \
|
||||
|
@ -622,7 +775,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush)
|
|||
|
||||
if (!(keyix == ATH9K_RXKEYIX_INVALID) && !decrypt_error) {
|
||||
rx_status.flag |= RX_FLAG_DECRYPTED;
|
||||
} else if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_PROTECTED)
|
||||
} else if (ieee80211_has_protected(fc)
|
||||
&& !decrypt_error && skb->len >= hdrlen + 4) {
|
||||
keyix = skb->data[hdrlen + 3] >> 6;
|
||||
|
||||
|
@ -631,36 +784,11 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush)
|
|||
}
|
||||
if (ah->sw_mgmt_crypto &&
|
||||
(rx_status.flag & RX_FLAG_DECRYPTED) &&
|
||||
ieee80211_is_mgmt(hdr->frame_control)) {
|
||||
ieee80211_is_mgmt(fc)) {
|
||||
/* Use software decrypt for management frames. */
|
||||
rx_status.flag &= ~RX_FLAG_DECRYPTED;
|
||||
}
|
||||
|
||||
/* Send the frame to mac80211 */
|
||||
if (hdr->addr1[5] & 0x01) {
|
||||
int i;
|
||||
/*
|
||||
* Deliver broadcast/multicast frames to all suitable
|
||||
* virtual wiphys.
|
||||
*/
|
||||
/* TODO: filter based on channel configuration */
|
||||
for (i = 0; i < sc->num_sec_wiphy; i++) {
|
||||
struct ath_wiphy *aphy = sc->sec_wiphy[i];
|
||||
struct sk_buff *nskb;
|
||||
if (aphy == NULL)
|
||||
continue;
|
||||
nskb = skb_copy(skb, GFP_ATOMIC);
|
||||
if (nskb)
|
||||
__ieee80211_rx(aphy->hw, nskb,
|
||||
&rx_status);
|
||||
}
|
||||
__ieee80211_rx(sc->hw, skb, &rx_status);
|
||||
} else {
|
||||
/* Deliver unicast frames based on receiver address */
|
||||
__ieee80211_rx(ath_get_virt_hw(sc, hdr), skb,
|
||||
&rx_status);
|
||||
}
|
||||
|
||||
/* We will now give hardware our shiny new allocated skb */
|
||||
bf->bf_mpdu = requeue_skb;
|
||||
bf->bf_buf_addr = dma_map_single(sc->dev, requeue_skb->data,
|
||||
|
@ -672,6 +800,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush)
|
|||
bf->bf_mpdu = NULL;
|
||||
DPRINTF(sc, ATH_DBG_FATAL,
|
||||
"dma_mapping_error() on RX\n");
|
||||
ath_rx_send_to_mac80211(sc, skb, &rx_status);
|
||||
break;
|
||||
}
|
||||
bf->bf_dmacontext = bf->bf_buf_addr;
|
||||
|
@ -687,11 +816,12 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush)
|
|||
sc->rx.rxotherant = 0;
|
||||
}
|
||||
|
||||
if (ieee80211_is_beacon(fc) &&
|
||||
(sc->sc_flags & SC_OP_WAIT_FOR_BEACON)) {
|
||||
sc->sc_flags &= ~SC_OP_WAIT_FOR_BEACON;
|
||||
ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP);
|
||||
}
|
||||
if (unlikely(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON |
|
||||
SC_OP_WAIT_FOR_PSPOLL_DATA)))
|
||||
ath_rx_ps(sc, skb);
|
||||
|
||||
ath_rx_send_to_mac80211(sc, skb, &rx_status);
|
||||
|
||||
requeue:
|
||||
list_move_tail(&bf->list, &sc->rx.rxbuf);
|
||||
ath_rx_buf_link(sc, bf);
|
||||
|
|
|
@ -1070,7 +1070,7 @@ void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
|
|||
r = ath9k_hw_reset(ah, sc->sc_ah->curchan, true);
|
||||
if (r)
|
||||
DPRINTF(sc, ATH_DBG_FATAL,
|
||||
"Unable to reset hardware; reset status %u\n",
|
||||
"Unable to reset hardware; reset status %d\n",
|
||||
r);
|
||||
spin_unlock_bh(&sc->sc_resetlock);
|
||||
}
|
||||
|
@ -1790,6 +1790,16 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
|
|||
skb_pull(skb, padsize);
|
||||
}
|
||||
|
||||
if (sc->sc_flags & SC_OP_WAIT_FOR_TX_ACK) {
|
||||
sc->sc_flags &= ~SC_OP_WAIT_FOR_TX_ACK;
|
||||
DPRINTF(sc, ATH_DBG_PS, "Going back to sleep after having "
|
||||
"received TX status (0x%x)\n",
|
||||
sc->sc_flags & (SC_OP_WAIT_FOR_BEACON |
|
||||
SC_OP_WAIT_FOR_CAB |
|
||||
SC_OP_WAIT_FOR_PSPOLL_DATA |
|
||||
SC_OP_WAIT_FOR_TX_ACK));
|
||||
}
|
||||
|
||||
if (frame_type == ATH9K_NOT_INTERNAL)
|
||||
ieee80211_tx_status(hw, skb);
|
||||
else
|
||||
|
|
|
@ -200,8 +200,10 @@ ath_reg_apply_beaconing_flags(struct wiphy *wiphy,
|
|||
continue;
|
||||
|
||||
if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
|
||||
r = freq_reg_info(wiphy, ch->center_freq,
|
||||
&bandwidth, ®_rule);
|
||||
r = freq_reg_info(wiphy,
|
||||
ch->center_freq,
|
||||
bandwidth,
|
||||
®_rule);
|
||||
if (r)
|
||||
continue;
|
||||
/*
|
||||
|
@ -265,7 +267,7 @@ ath_reg_apply_active_scan_flags(struct wiphy *wiphy,
|
|||
*/
|
||||
|
||||
ch = &sband->channels[11]; /* CH 12 */
|
||||
r = freq_reg_info(wiphy, ch->center_freq, &bandwidth, ®_rule);
|
||||
r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule);
|
||||
if (!r) {
|
||||
if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
|
||||
if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
|
||||
|
@ -273,7 +275,7 @@ ath_reg_apply_active_scan_flags(struct wiphy *wiphy,
|
|||
}
|
||||
|
||||
ch = &sband->channels[12]; /* CH 13 */
|
||||
r = freq_reg_info(wiphy, ch->center_freq, &bandwidth, ®_rule);
|
||||
r = freq_reg_info(wiphy, ch->center_freq, bandwidth, ®_rule);
|
||||
if (!r) {
|
||||
if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN))
|
||||
if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
|
||||
|
|
|
@ -3553,28 +3553,26 @@ static void b43_op_bss_info_changed(struct ieee80211_hw *hw,
|
|||
|
||||
B43_WARN_ON(wl->vif != vif);
|
||||
|
||||
spin_lock_irqsave(&wl->irq_lock, flags);
|
||||
if (changed & BSS_CHANGED_BSSID) {
|
||||
spin_lock_irqsave(&wl->irq_lock, flags);
|
||||
if (conf->bssid)
|
||||
memcpy(wl->bssid, conf->bssid, ETH_ALEN);
|
||||
else
|
||||
memset(wl->bssid, 0, ETH_ALEN);
|
||||
|
||||
if (b43_status(dev) >= B43_STAT_INITIALIZED) {
|
||||
if (b43_is_mode(wl, NL80211_IFTYPE_AP) ||
|
||||
b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT)) {
|
||||
B43_WARN_ON(vif->type != wl->if_type);
|
||||
if (changed & BSS_CHANGED_BEACON)
|
||||
b43_update_templates(wl);
|
||||
} else if (b43_is_mode(wl, NL80211_IFTYPE_ADHOC)) {
|
||||
if (changed & BSS_CHANGED_BEACON)
|
||||
b43_update_templates(wl);
|
||||
}
|
||||
b43_write_mac_bssid_templates(dev);
|
||||
}
|
||||
spin_unlock_irqrestore(&wl->irq_lock, flags);
|
||||
}
|
||||
|
||||
if (b43_status(dev) >= B43_STAT_INITIALIZED) {
|
||||
if (changed & BSS_CHANGED_BEACON &&
|
||||
(b43_is_mode(wl, NL80211_IFTYPE_AP) ||
|
||||
b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT) ||
|
||||
b43_is_mode(wl, NL80211_IFTYPE_ADHOC)))
|
||||
b43_update_templates(wl);
|
||||
|
||||
if (changed & BSS_CHANGED_BSSID)
|
||||
b43_write_mac_bssid_templates(dev);
|
||||
}
|
||||
spin_unlock_irqrestore(&wl->irq_lock, flags);
|
||||
|
||||
b43_mac_suspend(dev);
|
||||
|
||||
/* Update templates for AP/mesh mode. */
|
||||
|
@ -3639,7 +3637,7 @@ static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
|||
err = -EINVAL;
|
||||
switch (key->alg) {
|
||||
case ALG_WEP:
|
||||
if (key->keylen == LEN_WEP40)
|
||||
if (key->keylen == WLAN_KEY_LEN_WEP40)
|
||||
algorithm = B43_SEC_ALGO_WEP40;
|
||||
else
|
||||
algorithm = B43_SEC_ALGO_WEP104;
|
||||
|
|
|
@ -694,8 +694,8 @@ struct b43legacy_wldev {
|
|||
/* Reason code of the last interrupt. */
|
||||
u32 irq_reason;
|
||||
u32 dma_reason[6];
|
||||
/* saved irq enable/disable state bitfield. */
|
||||
u32 irq_savedstate;
|
||||
/* The currently active generic-interrupt mask. */
|
||||
u32 irq_mask;
|
||||
/* Link Quality calculation context. */
|
||||
struct b43legacy_noise_calculation noisecalc;
|
||||
/* if > 0 MAC is suspended. if == 0 MAC is enabled. */
|
||||
|
|
|
@ -583,35 +583,6 @@ static void b43legacy_short_slot_timing_disable(struct b43legacy_wldev *dev)
|
|||
b43legacy_set_slot_time(dev, 20);
|
||||
}
|
||||
|
||||
/* Enable a Generic IRQ. "mask" is the mask of which IRQs to enable.
|
||||
* Returns the _previously_ enabled IRQ mask.
|
||||
*/
|
||||
static inline u32 b43legacy_interrupt_enable(struct b43legacy_wldev *dev,
|
||||
u32 mask)
|
||||
{
|
||||
u32 old_mask;
|
||||
|
||||
old_mask = b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_MASK);
|
||||
b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, old_mask |
|
||||
mask);
|
||||
|
||||
return old_mask;
|
||||
}
|
||||
|
||||
/* Disable a Generic IRQ. "mask" is the mask of which IRQs to disable.
|
||||
* Returns the _previously_ enabled IRQ mask.
|
||||
*/
|
||||
static inline u32 b43legacy_interrupt_disable(struct b43legacy_wldev *dev,
|
||||
u32 mask)
|
||||
{
|
||||
u32 old_mask;
|
||||
|
||||
old_mask = b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_MASK);
|
||||
b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, old_mask & ~mask);
|
||||
|
||||
return old_mask;
|
||||
}
|
||||
|
||||
/* Synchronize IRQ top- and bottom-half.
|
||||
* IRQs must be masked before calling this.
|
||||
* This must not be called with the irq_lock held.
|
||||
|
@ -1200,7 +1171,7 @@ static void handle_irq_beacon(struct b43legacy_wldev *dev)
|
|||
/* This is the bottom half of the asynchronous beacon update. */
|
||||
|
||||
/* Ignore interrupt in the future. */
|
||||
dev->irq_savedstate &= ~B43legacy_IRQ_BEACON;
|
||||
dev->irq_mask &= ~B43legacy_IRQ_BEACON;
|
||||
|
||||
cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD);
|
||||
beacon0_valid = (cmd & B43legacy_MACCMD_BEACON0_VALID);
|
||||
|
@ -1209,7 +1180,7 @@ static void handle_irq_beacon(struct b43legacy_wldev *dev)
|
|||
/* Schedule interrupt manually, if busy. */
|
||||
if (beacon0_valid && beacon1_valid) {
|
||||
b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON, B43legacy_IRQ_BEACON);
|
||||
dev->irq_savedstate |= B43legacy_IRQ_BEACON;
|
||||
dev->irq_mask |= B43legacy_IRQ_BEACON;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1247,12 +1218,11 @@ static void b43legacy_beacon_update_trigger_work(struct work_struct *work)
|
|||
dev = wl->current_dev;
|
||||
if (likely(dev && (b43legacy_status(dev) >= B43legacy_STAT_INITIALIZED))) {
|
||||
spin_lock_irq(&wl->irq_lock);
|
||||
/* update beacon right away or defer to irq */
|
||||
dev->irq_savedstate = b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_MASK);
|
||||
/* Update beacon right away or defer to IRQ. */
|
||||
handle_irq_beacon(dev);
|
||||
/* The handler might have updated the IRQ mask. */
|
||||
b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK,
|
||||
dev->irq_savedstate);
|
||||
dev->irq_mask);
|
||||
mmiowb();
|
||||
spin_unlock_irq(&wl->irq_lock);
|
||||
}
|
||||
|
@ -1398,7 +1368,7 @@ static void b43legacy_interrupt_tasklet(struct b43legacy_wldev *dev)
|
|||
if (reason & B43legacy_IRQ_TX_OK)
|
||||
handle_irq_transmit_status(dev);
|
||||
|
||||
b43legacy_interrupt_enable(dev, dev->irq_savedstate);
|
||||
b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask);
|
||||
mmiowb();
|
||||
spin_unlock_irqrestore(&dev->wl->irq_lock, flags);
|
||||
}
|
||||
|
@ -1450,18 +1420,18 @@ static irqreturn_t b43legacy_interrupt_handler(int irq, void *dev_id)
|
|||
struct b43legacy_wldev *dev = dev_id;
|
||||
u32 reason;
|
||||
|
||||
if (!dev)
|
||||
return IRQ_NONE;
|
||||
B43legacy_WARN_ON(!dev);
|
||||
|
||||
spin_lock(&dev->wl->irq_lock);
|
||||
|
||||
if (b43legacy_status(dev) < B43legacy_STAT_STARTED)
|
||||
if (unlikely(b43legacy_status(dev) < B43legacy_STAT_STARTED))
|
||||
/* This can only happen on shared IRQ lines. */
|
||||
goto out;
|
||||
reason = b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON);
|
||||
if (reason == 0xffffffff) /* shared IRQ */
|
||||
goto out;
|
||||
ret = IRQ_HANDLED;
|
||||
reason &= b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_MASK);
|
||||
reason &= dev->irq_mask;
|
||||
if (!reason)
|
||||
goto out;
|
||||
|
||||
|
@ -1485,10 +1455,9 @@ static irqreturn_t b43legacy_interrupt_handler(int irq, void *dev_id)
|
|||
& 0x0000DC00;
|
||||
|
||||
b43legacy_interrupt_ack(dev, reason);
|
||||
/* disable all IRQs. They are enabled again in the bottom half. */
|
||||
dev->irq_savedstate = b43legacy_interrupt_disable(dev,
|
||||
B43legacy_IRQ_ALL);
|
||||
/* save the reason code and call our bottom half. */
|
||||
/* Disable all IRQs. They are enabled again in the bottom half. */
|
||||
b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0);
|
||||
/* Save the reason code and call our bottom half. */
|
||||
dev->irq_reason = reason;
|
||||
tasklet_schedule(&dev->isr_tasklet);
|
||||
out:
|
||||
|
@ -1948,7 +1917,8 @@ void b43legacy_mac_enable(struct b43legacy_wldev *dev)
|
|||
|
||||
/* Re-enable IRQs. */
|
||||
spin_lock_irq(&dev->wl->irq_lock);
|
||||
b43legacy_interrupt_enable(dev, dev->irq_savedstate);
|
||||
b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK,
|
||||
dev->irq_mask);
|
||||
spin_unlock_irq(&dev->wl->irq_lock);
|
||||
}
|
||||
}
|
||||
|
@ -1967,10 +1937,9 @@ void b43legacy_mac_suspend(struct b43legacy_wldev *dev)
|
|||
/* Mask IRQs before suspending MAC. Otherwise
|
||||
* the MAC stays busy and won't suspend. */
|
||||
spin_lock_irq(&dev->wl->irq_lock);
|
||||
tmp = b43legacy_interrupt_disable(dev, B43legacy_IRQ_ALL);
|
||||
b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0);
|
||||
spin_unlock_irq(&dev->wl->irq_lock);
|
||||
b43legacy_synchronize_irq(dev);
|
||||
dev->irq_savedstate = tmp;
|
||||
|
||||
b43legacy_power_saving_ctl_bits(dev, -1, 1);
|
||||
b43legacy_write32(dev, B43legacy_MMIO_MACCTL,
|
||||
|
@ -2659,7 +2628,6 @@ static int b43legacy_op_dev_config(struct ieee80211_hw *hw,
|
|||
int antenna_tx;
|
||||
int antenna_rx;
|
||||
int err = 0;
|
||||
u32 savedirqs;
|
||||
|
||||
antenna_tx = B43legacy_ANTENNA_DEFAULT;
|
||||
antenna_rx = B43legacy_ANTENNA_DEFAULT;
|
||||
|
@ -2699,7 +2667,7 @@ static int b43legacy_op_dev_config(struct ieee80211_hw *hw,
|
|||
spin_unlock_irqrestore(&wl->irq_lock, flags);
|
||||
goto out_unlock_mutex;
|
||||
}
|
||||
savedirqs = b43legacy_interrupt_disable(dev, B43legacy_IRQ_ALL);
|
||||
b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0);
|
||||
spin_unlock_irqrestore(&wl->irq_lock, flags);
|
||||
b43legacy_synchronize_irq(dev);
|
||||
|
||||
|
@ -2738,7 +2706,7 @@ static int b43legacy_op_dev_config(struct ieee80211_hw *hw,
|
|||
}
|
||||
|
||||
spin_lock_irqsave(&wl->irq_lock, flags);
|
||||
b43legacy_interrupt_enable(dev, savedirqs);
|
||||
b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask);
|
||||
mmiowb();
|
||||
spin_unlock_irqrestore(&wl->irq_lock, flags);
|
||||
out_unlock_mutex:
|
||||
|
@ -2801,7 +2769,6 @@ static void b43legacy_op_bss_info_changed(struct ieee80211_hw *hw,
|
|||
struct b43legacy_wldev *dev;
|
||||
struct b43legacy_phy *phy;
|
||||
unsigned long flags;
|
||||
u32 savedirqs;
|
||||
|
||||
mutex_lock(&wl->mutex);
|
||||
B43legacy_WARN_ON(wl->vif != vif);
|
||||
|
@ -2817,31 +2784,28 @@ static void b43legacy_op_bss_info_changed(struct ieee80211_hw *hw,
|
|||
spin_unlock_irqrestore(&wl->irq_lock, flags);
|
||||
goto out_unlock_mutex;
|
||||
}
|
||||
savedirqs = b43legacy_interrupt_disable(dev, B43legacy_IRQ_ALL);
|
||||
b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0);
|
||||
|
||||
if (changed & BSS_CHANGED_BSSID) {
|
||||
spin_unlock_irqrestore(&wl->irq_lock, flags);
|
||||
b43legacy_synchronize_irq(dev);
|
||||
|
||||
if (conf->bssid)
|
||||
memcpy(wl->bssid, conf->bssid, ETH_ALEN);
|
||||
else
|
||||
memset(wl->bssid, 0, ETH_ALEN);
|
||||
|
||||
if (b43legacy_status(dev) >= B43legacy_STAT_INITIALIZED) {
|
||||
if (b43legacy_is_mode(wl, NL80211_IFTYPE_AP)) {
|
||||
B43legacy_WARN_ON(vif->type != NL80211_IFTYPE_AP);
|
||||
if (changed & BSS_CHANGED_BEACON)
|
||||
b43legacy_update_templates(wl);
|
||||
} else if (b43legacy_is_mode(wl, NL80211_IFTYPE_ADHOC)) {
|
||||
if (changed & BSS_CHANGED_BEACON)
|
||||
b43legacy_update_templates(wl);
|
||||
}
|
||||
b43legacy_write_mac_bssid_templates(dev);
|
||||
}
|
||||
spin_unlock_irqrestore(&wl->irq_lock, flags);
|
||||
}
|
||||
|
||||
if (b43legacy_status(dev) >= B43legacy_STAT_INITIALIZED) {
|
||||
if (changed & BSS_CHANGED_BEACON &&
|
||||
(b43legacy_is_mode(wl, NL80211_IFTYPE_AP) ||
|
||||
b43legacy_is_mode(wl, NL80211_IFTYPE_ADHOC)))
|
||||
b43legacy_update_templates(wl);
|
||||
|
||||
if (changed & BSS_CHANGED_BSSID)
|
||||
b43legacy_write_mac_bssid_templates(dev);
|
||||
}
|
||||
spin_unlock_irqrestore(&wl->irq_lock, flags);
|
||||
|
||||
b43legacy_mac_suspend(dev);
|
||||
|
||||
if (changed & BSS_CHANGED_BEACON_INT &&
|
||||
|
@ -2862,7 +2826,7 @@ static void b43legacy_op_bss_info_changed(struct ieee80211_hw *hw,
|
|||
b43legacy_mac_enable(dev);
|
||||
|
||||
spin_lock_irqsave(&wl->irq_lock, flags);
|
||||
b43legacy_interrupt_enable(dev, savedirqs);
|
||||
b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask);
|
||||
/* XXX: why? */
|
||||
mmiowb();
|
||||
spin_unlock_irqrestore(&wl->irq_lock, flags);
|
||||
|
@ -2922,8 +2886,7 @@ static void b43legacy_wireless_core_stop(struct b43legacy_wldev *dev)
|
|||
* setting the status to INITIALIZED, as the interrupt handler
|
||||
* won't care about IRQs then. */
|
||||
spin_lock_irqsave(&wl->irq_lock, flags);
|
||||
dev->irq_savedstate = b43legacy_interrupt_disable(dev,
|
||||
B43legacy_IRQ_ALL);
|
||||
b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, 0);
|
||||
b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_MASK); /* flush */
|
||||
spin_unlock_irqrestore(&wl->irq_lock, flags);
|
||||
b43legacy_synchronize_irq(dev);
|
||||
|
@ -2963,7 +2926,7 @@ static int b43legacy_wireless_core_start(struct b43legacy_wldev *dev)
|
|||
|
||||
/* Start data flow (TX/RX) */
|
||||
b43legacy_mac_enable(dev);
|
||||
b43legacy_interrupt_enable(dev, dev->irq_savedstate);
|
||||
b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, dev->irq_mask);
|
||||
|
||||
/* Start maintenance work */
|
||||
b43legacy_periodic_tasks_setup(dev);
|
||||
|
@ -3126,7 +3089,7 @@ static void setup_struct_wldev_for_init(struct b43legacy_wldev *dev)
|
|||
/* IRQ related flags */
|
||||
dev->irq_reason = 0;
|
||||
memset(dev->dma_reason, 0, sizeof(dev->dma_reason));
|
||||
dev->irq_savedstate = B43legacy_IRQ_MASKTEMPLATE;
|
||||
dev->irq_mask = B43legacy_IRQ_MASKTEMPLATE;
|
||||
|
||||
dev->mac_suspended = 1;
|
||||
|
||||
|
|
|
@ -443,7 +443,7 @@ int b43legacy_pio_init(struct b43legacy_wldev *dev)
|
|||
pio->queue3 = queue;
|
||||
|
||||
if (dev->dev->id.revision < 3)
|
||||
dev->irq_savedstate |= B43legacy_IRQ_PIO_WORKAROUND;
|
||||
dev->irq_mask |= B43legacy_IRQ_PIO_WORKAROUND;
|
||||
|
||||
b43legacydbg(dev->wl, "PIO initialized\n");
|
||||
err = 0;
|
||||
|
|
|
@ -44,6 +44,15 @@
|
|||
#include "iwl-core.h"
|
||||
#include "iwl-dev.h"
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
static const char *led_type_str[] = {
|
||||
__stringify(IWL_LED_TRG_TX),
|
||||
__stringify(IWL_LED_TRG_RX),
|
||||
__stringify(IWL_LED_TRG_ASSOC),
|
||||
__stringify(IWL_LED_TRG_RADIO),
|
||||
NULL
|
||||
};
|
||||
#endif /* CONFIG_IWLWIFI_DEBUG */
|
||||
|
||||
static const struct {
|
||||
u16 brightness;
|
||||
|
@ -61,7 +70,7 @@ static const struct {
|
|||
{10, 110, 110},
|
||||
{5, 130, 130},
|
||||
{0, 167, 167},
|
||||
/*SOLID_ON*/
|
||||
/* SOLID_ON */
|
||||
{-1, IWL_LED_SOLID, 0}
|
||||
};
|
||||
|
||||
|
@ -142,6 +151,30 @@ static int iwl3945_led_off(struct iwl_priv *priv, int led_id)
|
|||
return iwl_send_led_cmd(priv, &led_cmd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set led on in case of association
|
||||
* */
|
||||
static int iwl3945_led_associate(struct iwl_priv *priv, int led_id)
|
||||
{
|
||||
IWL_DEBUG_LED(priv, "Associated\n");
|
||||
|
||||
priv->allow_blinking = 1;
|
||||
return iwl3945_led_on(priv, led_id);
|
||||
}
|
||||
/* Set Led off in case of disassociation */
|
||||
static int iwl3945_led_disassociate(struct iwl_priv *priv, int led_id)
|
||||
{
|
||||
IWL_DEBUG_LED(priv, "Disassociated\n");
|
||||
|
||||
priv->allow_blinking = 0;
|
||||
if (iwl_is_rfkill(priv))
|
||||
iwl3945_led_off(priv, led_id);
|
||||
else
|
||||
iwl3945_led_on(priv, led_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* brightness call back function for Tx/Rx LED
|
||||
*/
|
||||
|
@ -165,26 +198,21 @@ static void iwl3945_led_brightness_set(struct led_classdev *led_cdev,
|
|||
enum led_brightness brightness)
|
||||
{
|
||||
struct iwl_led *led = container_of(led_cdev,
|
||||
struct iwl_led, led_dev);
|
||||
struct iwl_led, led_dev);
|
||||
struct iwl_priv *priv = led->priv;
|
||||
|
||||
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||||
return;
|
||||
|
||||
IWL_DEBUG_LED(priv, "Led type = %s brightness = %d\n",
|
||||
led_type_str[led->type], brightness);
|
||||
|
||||
switch (brightness) {
|
||||
case LED_FULL:
|
||||
if (led->type == IWL_LED_TRG_ASSOC) {
|
||||
priv->allow_blinking = 1;
|
||||
IWL_DEBUG_LED(priv, "MAC is associated\n");
|
||||
}
|
||||
if (led->led_on)
|
||||
led->led_on(priv, IWL_LED_LINK);
|
||||
break;
|
||||
case LED_OFF:
|
||||
if (led->type == IWL_LED_TRG_ASSOC) {
|
||||
priv->allow_blinking = 0;
|
||||
IWL_DEBUG_LED(priv, "MAC is disassociated\n");
|
||||
}
|
||||
if (led->led_off)
|
||||
led->led_off(priv, IWL_LED_LINK);
|
||||
break;
|
||||
|
@ -197,8 +225,6 @@ static void iwl3945_led_brightness_set(struct led_classdev *led_cdev,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Register led class with the system
|
||||
*/
|
||||
|
@ -237,12 +263,12 @@ static int iwl3945_led_register_led(struct iwl_priv *priv,
|
|||
static inline u8 get_blink_rate(struct iwl_priv *priv)
|
||||
{
|
||||
int index;
|
||||
u64 current_tpt = priv->rxtxpackets;
|
||||
s64 tpt = current_tpt - priv->led_tpt;
|
||||
s64 tpt = priv->rxtxpackets;
|
||||
|
||||
if (tpt < 0)
|
||||
tpt = -tpt;
|
||||
priv->led_tpt = current_tpt;
|
||||
|
||||
IWL_DEBUG_LED(priv, "tpt %lld \n", (long long)tpt);
|
||||
|
||||
if (!priv->allow_blinking)
|
||||
index = IWL_MAX_BLINK_TBL;
|
||||
|
@ -250,13 +276,9 @@ static inline u8 get_blink_rate(struct iwl_priv *priv)
|
|||
for (index = 0; index < IWL_MAX_BLINK_TBL; index++)
|
||||
if (tpt > (blink_tbl[index].brightness * IWL_1MB_RATE))
|
||||
break;
|
||||
return index;
|
||||
}
|
||||
|
||||
static inline int is_rf_kill(struct iwl_priv *priv)
|
||||
{
|
||||
return test_bit(STATUS_RF_KILL_HW, &priv->status) ||
|
||||
test_bit(STATUS_RF_KILL_SW, &priv->status);
|
||||
IWL_DEBUG_LED(priv, "LED BLINK IDX=%d\n", index);
|
||||
return index;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -272,7 +294,7 @@ void iwl3945_led_background(struct iwl_priv *priv)
|
|||
priv->last_blink_time = 0;
|
||||
return;
|
||||
}
|
||||
if (is_rf_kill(priv)) {
|
||||
if (iwl_is_rfkill(priv)) {
|
||||
priv->last_blink_time = 0;
|
||||
return;
|
||||
}
|
||||
|
@ -341,8 +363,8 @@ int iwl3945_led_register(struct iwl_priv *priv)
|
|||
IWL_LED_TRG_ASSOC, 0, trigger);
|
||||
|
||||
/* for assoc always turn led on */
|
||||
priv->led[IWL_LED_TRG_ASSOC].led_on = iwl3945_led_on;
|
||||
priv->led[IWL_LED_TRG_ASSOC].led_off = iwl3945_led_on;
|
||||
priv->led[IWL_LED_TRG_ASSOC].led_on = iwl3945_led_associate;
|
||||
priv->led[IWL_LED_TRG_ASSOC].led_off = iwl3945_led_disassociate;
|
||||
priv->led[IWL_LED_TRG_ASSOC].led_pattern = NULL;
|
||||
|
||||
if (ret)
|
||||
|
|
|
@ -98,7 +98,6 @@ const struct iwl3945_rate_info iwl3945_rates[IWL_RATE_COUNT_3945] = {
|
|||
* ... and set IWL_EVT_DISABLE to 1. */
|
||||
void iwl3945_disable_events(struct iwl_priv *priv)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
u32 base; /* SRAM address of event log header */
|
||||
u32 disable_ptr; /* SRAM address of event-disable bitmap array */
|
||||
|
@ -159,26 +158,17 @@ void iwl3945_disable_events(struct iwl_priv *priv)
|
|||
return;
|
||||
}
|
||||
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret) {
|
||||
IWL_WARN(priv, "Can not read from adapter at this time.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
disable_ptr = iwl_read_targ_mem(priv, base + (4 * sizeof(u32)));
|
||||
array_size = iwl_read_targ_mem(priv, base + (5 * sizeof(u32)));
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
if (IWL_EVT_DISABLE && (array_size == IWL_EVT_DISABLE_SIZE)) {
|
||||
IWL_DEBUG_INFO(priv, "Disabling selected uCode log events at 0x%x\n",
|
||||
disable_ptr);
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
for (i = 0; i < IWL_EVT_DISABLE_SIZE; i++)
|
||||
iwl_write_targ_mem(priv,
|
||||
disable_ptr + (i * sizeof(u32)),
|
||||
evt_disable[i]);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
} else {
|
||||
IWL_DEBUG_INFO(priv, "Selected uCode log events may be disabled\n");
|
||||
IWL_DEBUG_INFO(priv, " by writing \"1\"s into disable bitmap\n");
|
||||
|
@ -908,55 +898,30 @@ u8 iwl3945_sync_sta(struct iwl_priv *priv, int sta_id, u16 tx_rate, u8 flags)
|
|||
|
||||
static int iwl3945_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret) {
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (src == IWL_PWR_SRC_VAUX) {
|
||||
if (pci_pme_capable(priv->pci_dev, PCI_D3cold)) {
|
||||
iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG,
|
||||
APMG_PS_CTRL_VAL_PWR_SRC_VAUX,
|
||||
~APMG_PS_CTRL_MSK_PWR_SRC);
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
iwl_poll_bit(priv, CSR_GPIO_IN,
|
||||
CSR_GPIO_IN_VAL_VAUX_PWR_SRC,
|
||||
CSR_GPIO_IN_BIT_AUX_POWER, 5000);
|
||||
} else {
|
||||
iwl_release_nic_access(priv);
|
||||
}
|
||||
} else {
|
||||
iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG,
|
||||
APMG_PS_CTRL_VAL_PWR_SRC_VMAIN,
|
||||
~APMG_PS_CTRL_MSK_PWR_SRC);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
iwl_poll_bit(priv, CSR_GPIO_IN, CSR_GPIO_IN_VAL_VMAIN_PWR_SRC,
|
||||
CSR_GPIO_IN_BIT_AUX_POWER, 5000); /* uS */
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwl3945_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
|
||||
{
|
||||
int rc;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
rc = iwl_grab_nic_access(priv);
|
||||
if (rc) {
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return rc;
|
||||
}
|
||||
|
||||
iwl_write_direct32(priv, FH39_RCSR_RBD_BASE(0), rxq->dma_addr);
|
||||
iwl_write_direct32(priv, FH39_RCSR_RPTR_ADDR(0), rxq->rb_stts_dma);
|
||||
iwl_write_direct32(priv, FH39_RCSR_WPTR(0), 0);
|
||||
|
@ -973,23 +938,11 @@ static int iwl3945_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
|
|||
/* fake read to flush all prev I/O */
|
||||
iwl_read_direct32(priv, FH39_RSSR_CTRL);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwl3945_tx_reset(struct iwl_priv *priv)
|
||||
{
|
||||
int rc;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
rc = iwl_grab_nic_access(priv);
|
||||
if (rc) {
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* bypass mode */
|
||||
iwl_write_prph(priv, ALM_SCD_MODE_REG, 0x2);
|
||||
|
@ -1017,8 +970,6 @@ static int iwl3945_tx_reset(struct iwl_priv *priv)
|
|||
FH39_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RSP_WAIT_TH |
|
||||
FH39_TSSR_TX_MSG_CONFIG_REG_VAL_RSP_WAIT_TH);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1061,7 +1012,7 @@ static int iwl3945_txq_ctx_reset(struct iwl_priv *priv)
|
|||
|
||||
static int iwl3945_apm_init(struct iwl_priv *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
iwl_power_initialize(priv);
|
||||
|
||||
|
@ -1083,10 +1034,6 @@ static int iwl3945_apm_init(struct iwl_priv *priv)
|
|||
goto out;
|
||||
}
|
||||
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* enable DMA */
|
||||
iwl_write_prph(priv, APMG_CLK_CTRL_REG, APMG_CLK_VAL_DMA_CLK_RQT |
|
||||
APMG_CLK_VAL_BSM_CLK_RQT);
|
||||
|
@ -1097,7 +1044,6 @@ static int iwl3945_apm_init(struct iwl_priv *priv)
|
|||
iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG,
|
||||
APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
@ -1196,22 +1142,13 @@ int iwl3945_hw_nic_init(struct iwl_priv *priv)
|
|||
|
||||
iwl3945_rx_init(priv, rxq);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
/* Look at using this instead:
|
||||
rxq->need_update = 1;
|
||||
iwl_rx_queue_update_write_ptr(priv, rxq);
|
||||
*/
|
||||
|
||||
rc = iwl_grab_nic_access(priv);
|
||||
if (rc) {
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return rc;
|
||||
}
|
||||
iwl_write_direct32(priv, FH39_RCSR_WPTR(0), rxq->write & ~7);
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
rc = iwl3945_txq_ctx_reset(priv);
|
||||
if (rc)
|
||||
|
@ -1243,14 +1180,6 @@ void iwl3945_hw_txq_ctx_free(struct iwl_priv *priv)
|
|||
void iwl3945_hw_txq_ctx_stop(struct iwl_priv *priv)
|
||||
{
|
||||
int txq_id;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
if (iwl_grab_nic_access(priv)) {
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
iwl3945_hw_txq_ctx_free(priv);
|
||||
return;
|
||||
}
|
||||
|
||||
/* stop SCD */
|
||||
iwl_write_prph(priv, ALM_SCD_MODE_REG, 0);
|
||||
|
@ -1263,9 +1192,6 @@ void iwl3945_hw_txq_ctx_stop(struct iwl_priv *priv)
|
|||
1000);
|
||||
}
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
iwl3945_hw_txq_ctx_free(priv);
|
||||
}
|
||||
|
||||
|
@ -1310,12 +1236,8 @@ static void iwl3945_apm_stop(struct iwl_priv *priv)
|
|||
|
||||
static int iwl3945_apm_reset(struct iwl_priv *priv)
|
||||
{
|
||||
int rc;
|
||||
unsigned long flags;
|
||||
|
||||
iwl3945_apm_stop_master(priv);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
|
||||
udelay(10);
|
||||
|
@ -1325,36 +1247,31 @@ static int iwl3945_apm_reset(struct iwl_priv *priv)
|
|||
iwl_poll_direct_bit(priv, CSR_GP_CNTRL,
|
||||
CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000);
|
||||
|
||||
rc = iwl_grab_nic_access(priv);
|
||||
if (!rc) {
|
||||
iwl_write_prph(priv, APMG_CLK_CTRL_REG,
|
||||
APMG_CLK_VAL_BSM_CLK_RQT);
|
||||
iwl_write_prph(priv, APMG_CLK_CTRL_REG,
|
||||
APMG_CLK_VAL_BSM_CLK_RQT);
|
||||
|
||||
iwl_write_prph(priv, APMG_RTC_INT_MSK_REG, 0x0);
|
||||
iwl_write_prph(priv, APMG_RTC_INT_STT_REG,
|
||||
iwl_write_prph(priv, APMG_RTC_INT_MSK_REG, 0x0);
|
||||
iwl_write_prph(priv, APMG_RTC_INT_STT_REG,
|
||||
0xFFFFFFFF);
|
||||
|
||||
/* enable DMA */
|
||||
iwl_write_prph(priv, APMG_CLK_EN_REG,
|
||||
APMG_CLK_VAL_DMA_CLK_RQT |
|
||||
APMG_CLK_VAL_BSM_CLK_RQT);
|
||||
udelay(10);
|
||||
/* enable DMA */
|
||||
iwl_write_prph(priv, APMG_CLK_EN_REG,
|
||||
APMG_CLK_VAL_DMA_CLK_RQT |
|
||||
APMG_CLK_VAL_BSM_CLK_RQT);
|
||||
udelay(10);
|
||||
|
||||
iwl_set_bits_prph(priv, APMG_PS_CTRL_REG,
|
||||
iwl_set_bits_prph(priv, APMG_PS_CTRL_REG,
|
||||
APMG_PS_CTRL_VAL_RESET_REQ);
|
||||
udelay(5);
|
||||
iwl_clear_bits_prph(priv, APMG_PS_CTRL_REG,
|
||||
udelay(5);
|
||||
iwl_clear_bits_prph(priv, APMG_PS_CTRL_REG,
|
||||
APMG_PS_CTRL_VAL_RESET_REQ);
|
||||
iwl_release_nic_access(priv);
|
||||
}
|
||||
|
||||
/* Clear the 'host command active' bit... */
|
||||
clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
|
||||
|
||||
wake_up_interruptible(&priv->wait_command_queue);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2500,14 +2417,6 @@ int iwl3945_txpower_set_from_eeprom(struct iwl_priv *priv)
|
|||
int iwl3945_hw_rxq_stop(struct iwl_priv *priv)
|
||||
{
|
||||
int rc;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
rc = iwl_grab_nic_access(priv);
|
||||
if (rc) {
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return rc;
|
||||
}
|
||||
|
||||
iwl_write_direct32(priv, FH39_RCSR_CONFIG(0), 0);
|
||||
rc = iwl_poll_direct_bit(priv, FH39_RSSR_STATUS,
|
||||
|
@ -2515,28 +2424,17 @@ int iwl3945_hw_rxq_stop(struct iwl_priv *priv)
|
|||
if (rc < 0)
|
||||
IWL_ERR(priv, "Can't stop Rx DMA.\n");
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iwl3945_hw_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq)
|
||||
{
|
||||
int rc;
|
||||
unsigned long flags;
|
||||
int txq_id = txq->q.id;
|
||||
|
||||
struct iwl3945_shared *shared_data = priv->shared_virt;
|
||||
|
||||
shared_data->tx_base_ptr[txq_id] = cpu_to_le32((u32)txq->q.dma_addr);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
rc = iwl_grab_nic_access(priv);
|
||||
if (rc) {
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return rc;
|
||||
}
|
||||
iwl_write_direct32(priv, FH39_CBCC_CTRL(txq_id), 0);
|
||||
iwl_write_direct32(priv, FH39_CBCC_BASE(txq_id), 0);
|
||||
|
||||
|
@ -2546,11 +2444,9 @@ int iwl3945_hw_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq)
|
|||
FH39_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD |
|
||||
FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL |
|
||||
FH39_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE);
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
/* fake read to flush all prev. writes */
|
||||
iwl_read32(priv, FH39_TSSR_CBB_BASE);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2858,10 +2754,6 @@ static int iwl3945_load_bsm(struct iwl_priv *priv)
|
|||
inst_len = priv->ucode_init.len;
|
||||
data_len = priv->ucode_init_data.len;
|
||||
|
||||
rc = iwl_grab_nic_access(priv);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
iwl_write_prph(priv, BSM_DRAM_INST_PTR_REG, pinst);
|
||||
iwl_write_prph(priv, BSM_DRAM_DATA_PTR_REG, pdata);
|
||||
iwl_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG, inst_len);
|
||||
|
@ -2875,10 +2767,8 @@ static int iwl3945_load_bsm(struct iwl_priv *priv)
|
|||
le32_to_cpu(*image));
|
||||
|
||||
rc = iwl3945_verify_bsm(priv);
|
||||
if (rc) {
|
||||
iwl_release_nic_access(priv);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Tell BSM to copy from BSM SRAM into instruction SRAM, when asked */
|
||||
iwl_write_prph(priv, BSM_WR_MEM_SRC_REG, 0x0);
|
||||
|
@ -2910,8 +2800,6 @@ static int iwl3945_load_bsm(struct iwl_priv *priv)
|
|||
iwl_write_prph(priv, BSM_WR_CTRL_REG,
|
||||
BSM_WR_CTRL_REG_BIT_START_EN);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2950,6 +2838,7 @@ static struct iwl_lib_ops iwl3945_lib = {
|
|||
.send_tx_power = iwl3945_send_tx_power,
|
||||
.is_valid_rtc_data_addr = iwl3945_hw_valid_rtc_data_addr,
|
||||
.post_associate = iwl3945_post_associate,
|
||||
.isr = iwl_isr_legacy,
|
||||
.config_ap = iwl3945_config_ap,
|
||||
};
|
||||
|
||||
|
@ -2983,7 +2872,8 @@ static struct iwl_cfg iwl3945_bg_cfg = {
|
|||
.eeprom_size = IWL3945_EEPROM_IMG_SIZE,
|
||||
.eeprom_ver = EEPROM_3945_EEPROM_VERSION,
|
||||
.ops = &iwl3945_ops,
|
||||
.mod_params = &iwl3945_mod_params
|
||||
.mod_params = &iwl3945_mod_params,
|
||||
.use_isr_legacy = true
|
||||
};
|
||||
|
||||
static struct iwl_cfg iwl3945_abg_cfg = {
|
||||
|
@ -2995,7 +2885,8 @@ static struct iwl_cfg iwl3945_abg_cfg = {
|
|||
.eeprom_size = IWL3945_EEPROM_IMG_SIZE,
|
||||
.eeprom_ver = EEPROM_3945_EEPROM_VERSION,
|
||||
.ops = &iwl3945_ops,
|
||||
.mod_params = &iwl3945_mod_params
|
||||
.mod_params = &iwl3945_mod_params,
|
||||
.use_isr_legacy = true
|
||||
};
|
||||
|
||||
struct pci_device_id iwl3945_hw_card_ids[] = {
|
||||
|
|
|
@ -163,10 +163,6 @@ static int iwl4965_load_bsm(struct iwl_priv *priv)
|
|||
inst_len = priv->ucode_init.len;
|
||||
data_len = priv->ucode_init_data.len;
|
||||
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
iwl_write_prph(priv, BSM_DRAM_INST_PTR_REG, pinst);
|
||||
iwl_write_prph(priv, BSM_DRAM_DATA_PTR_REG, pdata);
|
||||
iwl_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG, inst_len);
|
||||
|
@ -179,10 +175,8 @@ static int iwl4965_load_bsm(struct iwl_priv *priv)
|
|||
_iwl_write_prph(priv, reg_offset, le32_to_cpu(*image));
|
||||
|
||||
ret = iwl4965_verify_bsm(priv);
|
||||
if (ret) {
|
||||
iwl_release_nic_access(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Tell BSM to copy from BSM SRAM into instruction SRAM, when asked */
|
||||
iwl_write_prph(priv, BSM_WR_MEM_SRC_REG, 0x0);
|
||||
|
@ -211,7 +205,6 @@ static int iwl4965_load_bsm(struct iwl_priv *priv)
|
|||
* (e.g. when powering back up after power-save shutdown) */
|
||||
iwl_write_prph(priv, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START_EN);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -229,20 +222,12 @@ static int iwl4965_set_ucode_ptrs(struct iwl_priv *priv)
|
|||
{
|
||||
dma_addr_t pinst;
|
||||
dma_addr_t pdata;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
/* bits 35:4 for 4965 */
|
||||
pinst = priv->ucode_code.p_addr >> 4;
|
||||
pdata = priv->ucode_data_backup.p_addr >> 4;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret) {
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Tell bootstrap uCode where to find image to load */
|
||||
iwl_write_prph(priv, BSM_DRAM_INST_PTR_REG, pinst);
|
||||
iwl_write_prph(priv, BSM_DRAM_DATA_PTR_REG, pdata);
|
||||
|
@ -253,10 +238,6 @@ static int iwl4965_set_ucode_ptrs(struct iwl_priv *priv)
|
|||
* that all new ptr/size info is in place */
|
||||
iwl_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG,
|
||||
priv->ucode_code.len | BSM_DRAM_INST_LOAD);
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
IWL_DEBUG_INFO(priv, "Runtime uCode pointers are set.\n");
|
||||
|
||||
return ret;
|
||||
|
@ -312,10 +293,12 @@ static void iwl4965_init_alive_start(struct iwl_priv *priv)
|
|||
queue_work(priv->workqueue, &priv->restart);
|
||||
}
|
||||
|
||||
static int is_fat_channel(__le32 rxon_flags)
|
||||
static bool is_fat_channel(__le32 rxon_flags)
|
||||
{
|
||||
return (rxon_flags & RXON_FLG_CHANNEL_MODE_PURE_40_MSK) ||
|
||||
(rxon_flags & RXON_FLG_CHANNEL_MODE_MIXED_MSK);
|
||||
int chan_mod = le32_to_cpu(rxon_flags & RXON_FLG_CHANNEL_MODE_MSK)
|
||||
>> RXON_FLG_CHANNEL_MODE_POS;
|
||||
return ((chan_mod == CHANNEL_MODE_PURE_40) ||
|
||||
(chan_mod == CHANNEL_MODE_MIXED));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -358,10 +341,6 @@ static int iwl4965_apm_init(struct iwl_priv *priv)
|
|||
goto out;
|
||||
}
|
||||
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* enable DMA */
|
||||
iwl_write_prph(priv, APMG_CLK_CTRL_REG, APMG_CLK_VAL_DMA_CLK_RQT |
|
||||
APMG_CLK_VAL_BSM_CLK_RQT);
|
||||
|
@ -372,7 +351,6 @@ static int iwl4965_apm_init(struct iwl_priv *priv)
|
|||
iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG,
|
||||
APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
@ -454,11 +432,9 @@ static void iwl4965_apm_stop(struct iwl_priv *priv)
|
|||
static int iwl4965_apm_reset(struct iwl_priv *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
|
||||
iwl4965_apm_stop_master(priv);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
|
||||
|
||||
|
@ -475,9 +451,6 @@ static int iwl4965_apm_reset(struct iwl_priv *priv)
|
|||
|
||||
udelay(10);
|
||||
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret)
|
||||
goto out;
|
||||
/* Enable DMA and BSM Clock */
|
||||
iwl_write_prph(priv, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT |
|
||||
APMG_CLK_VAL_BSM_CLK_RQT);
|
||||
|
@ -488,14 +461,10 @@ static int iwl4965_apm_reset(struct iwl_priv *priv)
|
|||
iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG,
|
||||
APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
|
||||
wake_up_interruptible(&priv->wait_command_queue);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -681,18 +650,11 @@ static int iwl4965_alive_notify(struct iwl_priv *priv)
|
|||
{
|
||||
u32 a;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
int i, chan;
|
||||
u32 reg_val;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret) {
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Clear 4965's internal Tx Scheduler data base */
|
||||
priv->scd_base_addr = iwl_read_prph(priv, IWL49_SCD_SRAM_BASE_ADDR);
|
||||
a = priv->scd_base_addr + IWL49_SCD_CONTEXT_DATA_OFFSET;
|
||||
|
@ -759,10 +721,9 @@ static int iwl4965_alive_notify(struct iwl_priv *priv)
|
|||
iwl4965_tx_queue_set_status(priv, &priv->txq[i], ac, 0);
|
||||
}
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct iwl_sensitivity_ranges iwl4965_sensitivity = {
|
||||
|
@ -788,6 +749,12 @@ static struct iwl_sensitivity_ranges iwl4965_sensitivity = {
|
|||
.nrg_th_ofdm = 100,
|
||||
};
|
||||
|
||||
static void iwl4965_set_ct_threshold(struct iwl_priv *priv)
|
||||
{
|
||||
/* want Kelvin */
|
||||
priv->hw_params.ct_kill_threshold = CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD);
|
||||
}
|
||||
|
||||
/**
|
||||
* iwl4965_hw_set_hw_params
|
||||
*
|
||||
|
@ -822,7 +789,8 @@ static int iwl4965_hw_set_hw_params(struct iwl_priv *priv)
|
|||
priv->hw_params.rx_chains_num = 2;
|
||||
priv->hw_params.valid_tx_ant = ANT_A | ANT_B;
|
||||
priv->hw_params.valid_rx_ant = ANT_A | ANT_B;
|
||||
priv->hw_params.ct_kill_threshold = CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD);
|
||||
if (priv->cfg->ops->lib->temp_ops.set_ct_kill)
|
||||
priv->cfg->ops->lib->temp_ops.set_ct_kill(priv);
|
||||
|
||||
priv->hw_params.sens = &iwl4965_sensitivity;
|
||||
|
||||
|
@ -1524,7 +1492,7 @@ static int iwl4965_send_tx_power(struct iwl_priv *priv)
|
|||
struct iwl4965_txpowertable_cmd cmd = { 0 };
|
||||
int ret;
|
||||
u8 band = 0;
|
||||
u8 is_fat = 0;
|
||||
bool is_fat = false;
|
||||
u8 ctrl_chan_high = 0;
|
||||
|
||||
if (test_bit(STATUS_SCANNING, &priv->status)) {
|
||||
|
@ -1602,7 +1570,7 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel)
|
|||
{
|
||||
int rc;
|
||||
u8 band = 0;
|
||||
u8 is_fat = 0;
|
||||
bool is_fat = false;
|
||||
u8 ctrl_chan_high = 0;
|
||||
struct iwl4965_channel_switch_cmd cmd = { 0 };
|
||||
const struct iwl_channel_info *ch_info;
|
||||
|
@ -1833,8 +1801,6 @@ static void iwl4965_tx_queue_stop_scheduler(struct iwl_priv *priv,
|
|||
static int iwl4965_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
|
||||
u16 ssn_idx, u8 tx_fifo)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if ((IWL49_FIRST_AMPDU_QUEUE > txq_id) ||
|
||||
(IWL49_FIRST_AMPDU_QUEUE + IWL49_NUM_AMPDU_QUEUES <= txq_id)) {
|
||||
IWL_WARN(priv,
|
||||
|
@ -1844,10 +1810,6 @@ static int iwl4965_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
iwl4965_tx_queue_stop_scheduler(priv, txq_id);
|
||||
|
||||
iwl_clear_bits_prph(priv, IWL49_SCD_QUEUECHAIN_SEL, (1 << txq_id));
|
||||
|
@ -1861,8 +1823,6 @@ static int iwl4965_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
|
|||
iwl_txq_ctx_deactivate(priv, txq_id);
|
||||
iwl4965_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 0);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1904,7 +1864,6 @@ static int iwl4965_txq_agg_enable(struct iwl_priv *priv, int txq_id,
|
|||
int tx_fifo, int sta_id, int tid, u16 ssn_idx)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
u16 ra_tid;
|
||||
|
||||
if ((IWL49_FIRST_AMPDU_QUEUE > txq_id) ||
|
||||
|
@ -1922,11 +1881,6 @@ static int iwl4965_txq_agg_enable(struct iwl_priv *priv, int txq_id,
|
|||
iwl_sta_tx_modify_enable_tid(priv, sta_id, tid);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret) {
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Stop this Tx queue before configuring it */
|
||||
iwl4965_tx_queue_stop_scheduler(priv, txq_id);
|
||||
|
@ -1959,7 +1913,6 @@ static int iwl4965_txq_agg_enable(struct iwl_priv *priv, int txq_id,
|
|||
/* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */
|
||||
iwl4965_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 1);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
|
@ -2331,9 +2284,13 @@ static struct iwl_lib_ops iwl4965_lib = {
|
|||
},
|
||||
.send_tx_power = iwl4965_send_tx_power,
|
||||
.update_chain_flags = iwl_update_chain_flags,
|
||||
.temperature = iwl4965_temperature_calib,
|
||||
.post_associate = iwl_post_associate,
|
||||
.config_ap = iwl_config_ap,
|
||||
.isr = iwl_isr_legacy,
|
||||
.temp_ops = {
|
||||
.temperature = iwl4965_temperature_calib,
|
||||
.set_ct_kill = iwl4965_set_ct_threshold,
|
||||
},
|
||||
};
|
||||
|
||||
static struct iwl_ops iwl4965_ops = {
|
||||
|
@ -2354,6 +2311,7 @@ struct iwl_cfg iwl4965_agn_cfg = {
|
|||
.eeprom_calib_ver = EEPROM_4965_TX_POWER_VERSION,
|
||||
.ops = &iwl4965_ops,
|
||||
.mod_params = &iwl4965_mod_params,
|
||||
.use_isr_legacy = true
|
||||
};
|
||||
|
||||
/* Module firmware */
|
||||
|
|
|
@ -87,6 +87,18 @@
|
|||
#define IWL50_NUM_AMPDU_QUEUES 10
|
||||
#define IWL50_FIRST_AMPDU_QUEUE 10
|
||||
|
||||
/* 5150 only */
|
||||
#define IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF (-5)
|
||||
|
||||
static inline s32 iwl_temp_calib_to_offset(struct iwl_priv *priv)
|
||||
{
|
||||
u16 *temp_calib = (u16 *)iwl_eeprom_query_addr(priv,
|
||||
EEPROM_5000_TEMPERATURE);
|
||||
/* offset = temperature - voltage / coef */
|
||||
s32 offset = (s32)(temp_calib[0] - temp_calib[1] / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF);
|
||||
return offset;
|
||||
}
|
||||
|
||||
/* Fixed (non-configurable) rx data from phy */
|
||||
|
||||
/**
|
||||
|
|
|
@ -124,10 +124,6 @@ static int iwl5000_apm_init(struct iwl_priv *priv)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* enable DMA */
|
||||
iwl_write_prph(priv, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT);
|
||||
|
||||
|
@ -137,8 +133,6 @@ static int iwl5000_apm_init(struct iwl_priv *priv)
|
|||
iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG,
|
||||
APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -165,12 +159,9 @@ static void iwl5000_apm_stop(struct iwl_priv *priv)
|
|||
static int iwl5000_apm_reset(struct iwl_priv *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
|
||||
iwl5000_apm_stop_master(priv);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
|
||||
|
||||
udelay(10);
|
||||
|
@ -193,10 +184,6 @@ static int iwl5000_apm_reset(struct iwl_priv *priv)
|
|||
goto out;
|
||||
}
|
||||
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* enable DMA */
|
||||
iwl_write_prph(priv, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT);
|
||||
|
||||
|
@ -205,11 +192,7 @@ static int iwl5000_apm_reset(struct iwl_priv *priv)
|
|||
/* disable L1-Active */
|
||||
iwl_set_bits_prph(priv, APMG_PCIDEV_STT_REG,
|
||||
APMG_PCIDEV_STT_VAL_L1_ACT_DIS);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -252,11 +235,9 @@ static void iwl5000_nic_config(struct iwl_priv *priv)
|
|||
* (PCIe power is lost before PERST# is asserted),
|
||||
* causing ME FW to lose ownership and not being able to obtain it back.
|
||||
*/
|
||||
iwl_grab_nic_access(priv);
|
||||
iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG,
|
||||
APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS,
|
||||
~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS);
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
@ -434,15 +415,19 @@ static const u8 *iwl5000_eeprom_query_addr(const struct iwl_priv *priv,
|
|||
return &priv->eeprom[address];
|
||||
}
|
||||
|
||||
static s32 iwl5150_get_ct_threshold(struct iwl_priv *priv)
|
||||
static void iwl5150_set_ct_threshold(struct iwl_priv *priv)
|
||||
{
|
||||
const s32 volt2temp_coef = -5;
|
||||
u16 *temp_calib = (u16 *)iwl_eeprom_query_addr(priv,
|
||||
EEPROM_5000_TEMPERATURE);
|
||||
/* offset = temperate - voltage / coef */
|
||||
s32 offset = temp_calib[0] - temp_calib[1] / volt2temp_coef;
|
||||
s32 threshold = (s32)CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD) - offset;
|
||||
return threshold * volt2temp_coef;
|
||||
const s32 volt2temp_coef = IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF;
|
||||
s32 threshold = (s32)CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD) -
|
||||
iwl_temp_calib_to_offset(priv);
|
||||
|
||||
priv->hw_params.ct_kill_threshold = threshold * volt2temp_coef;
|
||||
}
|
||||
|
||||
static void iwl5000_set_ct_threshold(struct iwl_priv *priv)
|
||||
{
|
||||
/* want Celsius */
|
||||
priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -533,19 +518,9 @@ static int iwl5000_load_section(struct iwl_priv *priv,
|
|||
struct fw_desc *image,
|
||||
u32 dst_addr)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
|
||||
dma_addr_t phy_addr = image->p_addr;
|
||||
u32 byte_cnt = image->len;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret) {
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
iwl_write_direct32(priv,
|
||||
FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL),
|
||||
FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE);
|
||||
|
@ -574,8 +549,6 @@ static int iwl5000_load_section(struct iwl_priv *priv,
|
|||
FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE |
|
||||
FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -736,18 +709,11 @@ static int iwl5000_alive_notify(struct iwl_priv *priv)
|
|||
{
|
||||
u32 a;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
int i, chan;
|
||||
u32 reg_val;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret) {
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->scd_base_addr = iwl_read_prph(priv, IWL50_SCD_SRAM_BASE_ADDR);
|
||||
a = priv->scd_base_addr + IWL50_SCD_CONTEXT_DATA_OFFSET;
|
||||
for (; a < priv->scd_base_addr + IWL50_SCD_TX_STTS_BITMAP_OFFSET;
|
||||
|
@ -815,7 +781,6 @@ static int iwl5000_alive_notify(struct iwl_priv *priv)
|
|||
iwl_txq_ctx_activate(priv, 8);
|
||||
iwl_txq_ctx_activate(priv, 9);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
|
||||
|
@ -868,17 +833,8 @@ static int iwl5000_hw_set_hw_params(struct iwl_priv *priv)
|
|||
priv->hw_params.valid_tx_ant = priv->cfg->valid_tx_ant;
|
||||
priv->hw_params.valid_rx_ant = priv->cfg->valid_rx_ant;
|
||||
|
||||
switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) {
|
||||
case CSR_HW_REV_TYPE_5150:
|
||||
/* 5150 wants in Kelvin */
|
||||
priv->hw_params.ct_kill_threshold =
|
||||
iwl5150_get_ct_threshold(priv);
|
||||
break;
|
||||
default:
|
||||
/* all others want Celsius */
|
||||
priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD;
|
||||
break;
|
||||
}
|
||||
if (priv->cfg->ops->lib->temp_ops.set_ct_kill)
|
||||
priv->cfg->ops->lib->temp_ops.set_ct_kill(priv);
|
||||
|
||||
/* Set initial calibration set */
|
||||
switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) {
|
||||
|
@ -900,7 +856,6 @@ static int iwl5000_hw_set_hw_params(struct iwl_priv *priv)
|
|||
break;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1006,7 +961,6 @@ static int iwl5000_txq_agg_enable(struct iwl_priv *priv, int txq_id,
|
|||
int tx_fifo, int sta_id, int tid, u16 ssn_idx)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
u16 ra_tid;
|
||||
|
||||
if ((IWL50_FIRST_AMPDU_QUEUE > txq_id) ||
|
||||
|
@ -1024,11 +978,6 @@ static int iwl5000_txq_agg_enable(struct iwl_priv *priv, int txq_id,
|
|||
iwl_sta_tx_modify_enable_tid(priv, sta_id, tid);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret) {
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Stop this Tx queue before configuring it */
|
||||
iwl5000_tx_queue_stop_scheduler(priv, txq_id);
|
||||
|
@ -1064,7 +1013,6 @@ static int iwl5000_txq_agg_enable(struct iwl_priv *priv, int txq_id,
|
|||
/* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */
|
||||
iwl5000_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 1);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
|
@ -1073,8 +1021,6 @@ static int iwl5000_txq_agg_enable(struct iwl_priv *priv, int txq_id,
|
|||
static int iwl5000_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
|
||||
u16 ssn_idx, u8 tx_fifo)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((IWL50_FIRST_AMPDU_QUEUE > txq_id) ||
|
||||
(IWL50_FIRST_AMPDU_QUEUE + IWL50_NUM_AMPDU_QUEUES <= txq_id)) {
|
||||
IWL_ERR(priv,
|
||||
|
@ -1084,10 +1030,6 @@ static int iwl5000_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
iwl5000_tx_queue_stop_scheduler(priv, txq_id);
|
||||
|
||||
iwl_clear_bits_prph(priv, IWL50_SCD_AGGR_SEL, (1 << txq_id));
|
||||
|
@ -1101,8 +1043,6 @@ static int iwl5000_txq_agg_disable(struct iwl_priv *priv, u16 txq_id,
|
|||
iwl_txq_ctx_deactivate(priv, txq_id);
|
||||
iwl5000_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 0);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1434,6 +1374,17 @@ static void iwl5000_temperature(struct iwl_priv *priv)
|
|||
priv->temperature = le32_to_cpu(priv->statistics.general.temperature);
|
||||
}
|
||||
|
||||
static void iwl5150_temperature(struct iwl_priv *priv)
|
||||
{
|
||||
u32 vt = 0;
|
||||
s32 offset = iwl_temp_calib_to_offset(priv);
|
||||
|
||||
vt = le32_to_cpu(priv->statistics.general.temperature);
|
||||
vt = vt / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF + offset;
|
||||
/* now vt hold the temperature in Kelvin */
|
||||
priv->temperature = KELVIN_TO_CELSIUS(vt);
|
||||
}
|
||||
|
||||
/* Calc max signal level (dBm) among 3 possible receivers */
|
||||
int iwl5000_calc_rssi(struct iwl_priv *priv,
|
||||
struct iwl_rx_phy_res *rx_resp)
|
||||
|
@ -1511,7 +1462,6 @@ struct iwl_lib_ops iwl5000_lib = {
|
|||
.init_alive_start = iwl5000_init_alive_start,
|
||||
.alive_notify = iwl5000_alive_notify,
|
||||
.send_tx_power = iwl5000_send_tx_power,
|
||||
.temperature = iwl5000_temperature,
|
||||
.update_chain_flags = iwl_update_chain_flags,
|
||||
.apm_ops = {
|
||||
.init = iwl5000_apm_init,
|
||||
|
@ -1537,7 +1487,62 @@ struct iwl_lib_ops iwl5000_lib = {
|
|||
.query_addr = iwl5000_eeprom_query_addr,
|
||||
},
|
||||
.post_associate = iwl_post_associate,
|
||||
.isr = iwl_isr_ict,
|
||||
.config_ap = iwl_config_ap,
|
||||
.temp_ops = {
|
||||
.temperature = iwl5000_temperature,
|
||||
.set_ct_kill = iwl5000_set_ct_threshold,
|
||||
},
|
||||
};
|
||||
|
||||
static struct iwl_lib_ops iwl5150_lib = {
|
||||
.set_hw_params = iwl5000_hw_set_hw_params,
|
||||
.txq_update_byte_cnt_tbl = iwl5000_txq_update_byte_cnt_tbl,
|
||||
.txq_inval_byte_cnt_tbl = iwl5000_txq_inval_byte_cnt_tbl,
|
||||
.txq_set_sched = iwl5000_txq_set_sched,
|
||||
.txq_agg_enable = iwl5000_txq_agg_enable,
|
||||
.txq_agg_disable = iwl5000_txq_agg_disable,
|
||||
.txq_attach_buf_to_tfd = iwl_hw_txq_attach_buf_to_tfd,
|
||||
.txq_free_tfd = iwl_hw_txq_free_tfd,
|
||||
.txq_init = iwl_hw_tx_queue_init,
|
||||
.rx_handler_setup = iwl5000_rx_handler_setup,
|
||||
.setup_deferred_work = iwl5000_setup_deferred_work,
|
||||
.is_valid_rtc_data_addr = iwl5000_hw_valid_rtc_data_addr,
|
||||
.load_ucode = iwl5000_load_ucode,
|
||||
.init_alive_start = iwl5000_init_alive_start,
|
||||
.alive_notify = iwl5000_alive_notify,
|
||||
.send_tx_power = iwl5000_send_tx_power,
|
||||
.update_chain_flags = iwl_update_chain_flags,
|
||||
.apm_ops = {
|
||||
.init = iwl5000_apm_init,
|
||||
.reset = iwl5000_apm_reset,
|
||||
.stop = iwl5000_apm_stop,
|
||||
.config = iwl5000_nic_config,
|
||||
.set_pwr_src = iwl_set_pwr_src,
|
||||
},
|
||||
.eeprom_ops = {
|
||||
.regulatory_bands = {
|
||||
EEPROM_5000_REG_BAND_1_CHANNELS,
|
||||
EEPROM_5000_REG_BAND_2_CHANNELS,
|
||||
EEPROM_5000_REG_BAND_3_CHANNELS,
|
||||
EEPROM_5000_REG_BAND_4_CHANNELS,
|
||||
EEPROM_5000_REG_BAND_5_CHANNELS,
|
||||
EEPROM_5000_REG_BAND_24_FAT_CHANNELS,
|
||||
EEPROM_5000_REG_BAND_52_FAT_CHANNELS
|
||||
},
|
||||
.verify_signature = iwlcore_eeprom_verify_signature,
|
||||
.acquire_semaphore = iwlcore_eeprom_acquire_semaphore,
|
||||
.release_semaphore = iwlcore_eeprom_release_semaphore,
|
||||
.calib_version = iwl5000_eeprom_calib_version,
|
||||
.query_addr = iwl5000_eeprom_query_addr,
|
||||
},
|
||||
.post_associate = iwl_post_associate,
|
||||
.isr = iwl_isr_ict,
|
||||
.config_ap = iwl_config_ap,
|
||||
.temp_ops = {
|
||||
.temperature = iwl5150_temperature,
|
||||
.set_ct_kill = iwl5150_set_ct_threshold,
|
||||
},
|
||||
};
|
||||
|
||||
struct iwl_ops iwl5000_ops = {
|
||||
|
@ -1547,6 +1552,13 @@ struct iwl_ops iwl5000_ops = {
|
|||
.smgmt = &iwl5000_station_mgmt,
|
||||
};
|
||||
|
||||
static struct iwl_ops iwl5150_ops = {
|
||||
.lib = &iwl5150_lib,
|
||||
.hcmd = &iwl5000_hcmd,
|
||||
.utils = &iwl5000_hcmd_utils,
|
||||
.smgmt = &iwl5000_station_mgmt,
|
||||
};
|
||||
|
||||
struct iwl_mod_params iwl50_mod_params = {
|
||||
.num_of_queues = IWL50_NUM_QUEUES,
|
||||
.num_of_ampdu_queues = IWL50_NUM_AMPDU_QUEUES,
|
||||
|
@ -1642,7 +1654,7 @@ struct iwl_cfg iwl5150_agn_cfg = {
|
|||
.ucode_api_max = IWL5150_UCODE_API_MAX,
|
||||
.ucode_api_min = IWL5150_UCODE_API_MIN,
|
||||
.sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N,
|
||||
.ops = &iwl5000_ops,
|
||||
.ops = &iwl5150_ops,
|
||||
.eeprom_size = IWL_5000_EEPROM_IMG_SIZE,
|
||||
.eeprom_ver = EEPROM_5050_EEPROM_VERSION,
|
||||
.eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION,
|
||||
|
|
|
@ -270,6 +270,8 @@ const static struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = {
|
|||
{"60", "64QAM 5/6"}
|
||||
};
|
||||
|
||||
#define MCS_INDEX_PER_STREAM (8)
|
||||
|
||||
static inline u8 rs_extract_rate(u32 rate_n_flags)
|
||||
{
|
||||
return (u8)(rate_n_flags & 0xFF);
|
||||
|
@ -652,19 +654,19 @@ static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags,
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* FIXME:RS: in 4965 we don't use greenfield at all */
|
||||
/* FIXME:RS: don't use greenfield for now in TX */
|
||||
#if 0
|
||||
static inline u8 rs_use_green(struct iwl_priv *priv, struct ieee80211_conf *conf)
|
||||
/* in 4965 we don't use greenfield at all */
|
||||
static inline u8 rs_use_green(struct iwl_priv *priv,
|
||||
struct ieee80211_conf *conf)
|
||||
{
|
||||
return (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) &&
|
||||
priv->current_ht_config.is_green_field &&
|
||||
!priv->current_ht_config.non_GF_STA_present;
|
||||
}
|
||||
#endif
|
||||
static inline u8 rs_use_green(struct iwl_priv *priv, struct ieee80211_conf *conf)
|
||||
{
|
||||
return 0;
|
||||
u8 is_green;
|
||||
|
||||
if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_4965)
|
||||
is_green = 0;
|
||||
else
|
||||
is_green = (conf_is_ht(conf) &&
|
||||
priv->current_ht_config.is_green_field &&
|
||||
!priv->current_ht_config.non_GF_STA_present);
|
||||
return is_green;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2061,6 +2063,10 @@ static void rs_rate_scale_perform(struct iwl_priv *priv,
|
|||
active_tbl = 1 - lq_sta->active_tbl;
|
||||
|
||||
tbl = &(lq_sta->lq_info[active_tbl]);
|
||||
if (is_legacy(tbl->lq_type))
|
||||
lq_sta->is_green = 0;
|
||||
else
|
||||
lq_sta->is_green = rs_use_green(priv, conf);
|
||||
is_green = lq_sta->is_green;
|
||||
|
||||
/* current tx rate */
|
||||
|
@ -2514,12 +2520,33 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta,
|
|||
}
|
||||
}
|
||||
|
||||
if (rate_idx < 0 || rate_idx > IWL_RATE_COUNT)
|
||||
rate_idx = rate_lowest_index(sband, sta);
|
||||
else if (sband->band == IEEE80211_BAND_5GHZ)
|
||||
if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) {
|
||||
rate_idx -= IWL_FIRST_OFDM_RATE;
|
||||
|
||||
/* 6M and 9M shared same MCS index */
|
||||
rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0;
|
||||
if (rs_extract_rate(lq_sta->last_rate_n_flags) >=
|
||||
IWL_RATE_MIMO3_6M_PLCP)
|
||||
rate_idx = rate_idx + (2 * MCS_INDEX_PER_STREAM);
|
||||
else if (rs_extract_rate(lq_sta->last_rate_n_flags) >=
|
||||
IWL_RATE_MIMO2_6M_PLCP)
|
||||
rate_idx = rate_idx + MCS_INDEX_PER_STREAM;
|
||||
info->control.rates[0].flags = IEEE80211_TX_RC_MCS;
|
||||
if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK)
|
||||
info->control.rates[0].flags |= IEEE80211_TX_RC_SHORT_GI;
|
||||
if (lq_sta->last_rate_n_flags & RATE_MCS_DUP_MSK)
|
||||
info->control.rates[0].flags |= IEEE80211_TX_RC_DUP_DATA;
|
||||
if (lq_sta->last_rate_n_flags & RATE_MCS_FAT_MSK)
|
||||
info->control.rates[0].flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
|
||||
if (lq_sta->last_rate_n_flags & RATE_MCS_GF_MSK)
|
||||
info->control.rates[0].flags |= IEEE80211_TX_RC_GREEN_FIELD;
|
||||
} else {
|
||||
if (rate_idx < 0 || rate_idx > IWL_RATE_COUNT)
|
||||
rate_idx = rate_lowest_index(sband, sta);
|
||||
else if (sband->band == IEEE80211_BAND_5GHZ)
|
||||
rate_idx -= IWL_FIRST_OFDM_RATE;
|
||||
}
|
||||
info->control.rates[0].idx = rate_idx;
|
||||
|
||||
}
|
||||
|
||||
static void *rs_alloc_sta(void *priv_rate, struct ieee80211_sta *sta,
|
||||
|
@ -2896,7 +2923,8 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
|
|||
((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3"));
|
||||
desc += sprintf(buff+desc, " %s",
|
||||
(tbl->is_fat) ? "40MHz" : "20MHz");
|
||||
desc += sprintf(buff+desc, " %s\n", (tbl->is_SGI) ? "SGI" : "");
|
||||
desc += sprintf(buff+desc, " %s %s\n", (tbl->is_SGI) ? "SGI" : "",
|
||||
(lq_sta->is_green) ? "GF enabled" : "");
|
||||
}
|
||||
desc += sprintf(buff+desc, "last tx rate=0x%X\n",
|
||||
lq_sta->last_rate_n_flags);
|
||||
|
@ -2959,13 +2987,14 @@ static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file,
|
|||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < LQ_SIZE; i++) {
|
||||
desc += sprintf(buff+desc, "%s type=%d SGI=%d FAT=%d DUP=%d\n"
|
||||
desc += sprintf(buff+desc, "%s type=%d SGI=%d FAT=%d DUP=%d GF=%d\n"
|
||||
"rate=0x%X\n",
|
||||
lq_sta->active_tbl == i ? "*" : "x",
|
||||
lq_sta->lq_info[i].lq_type,
|
||||
lq_sta->lq_info[i].is_SGI,
|
||||
lq_sta->lq_info[i].is_fat,
|
||||
lq_sta->lq_info[i].is_dup,
|
||||
lq_sta->is_green,
|
||||
lq_sta->lq_info[i].current_rate);
|
||||
for (j = 0; j < IWL_RATE_COUNT; j++) {
|
||||
desc += sprintf(buff+desc,
|
||||
|
|
|
@ -503,24 +503,12 @@ int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv,
|
|||
int iwl_hw_tx_queue_init(struct iwl_priv *priv,
|
||||
struct iwl_tx_queue *txq)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
int txq_id = txq->q.id;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret) {
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Circular buffer (TFD queue in DRAM) physical base address */
|
||||
iwl_write_direct32(priv, FH_MEM_CBBC_QUEUE(txq_id),
|
||||
txq->q.dma_addr >> 8);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -709,6 +697,7 @@ static void iwl_rx_card_state_notif(struct iwl_priv *priv,
|
|||
struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
|
||||
u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags);
|
||||
unsigned long status = priv->status;
|
||||
unsigned long reg_flags;
|
||||
|
||||
IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s\n",
|
||||
(flags & HW_CARD_DISABLED) ? "Kill" : "On",
|
||||
|
@ -720,32 +709,25 @@ static void iwl_rx_card_state_notif(struct iwl_priv *priv,
|
|||
iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
|
||||
CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
|
||||
|
||||
if (!iwl_grab_nic_access(priv)) {
|
||||
iwl_write_direct32(
|
||||
priv, HBUS_TARG_MBX_C,
|
||||
HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
}
|
||||
iwl_write_direct32(priv, HBUS_TARG_MBX_C,
|
||||
HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
|
||||
|
||||
if (!(flags & RXON_CARD_DISABLED)) {
|
||||
iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
|
||||
CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED);
|
||||
if (!iwl_grab_nic_access(priv)) {
|
||||
iwl_write_direct32(
|
||||
priv, HBUS_TARG_MBX_C,
|
||||
iwl_write_direct32(priv, HBUS_TARG_MBX_C,
|
||||
HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & RF_CARD_DISABLED) {
|
||||
iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
|
||||
CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT);
|
||||
iwl_read32(priv, CSR_UCODE_DRV_GP1);
|
||||
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
||||
if (!iwl_grab_nic_access(priv))
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -774,14 +756,6 @@ static void iwl_rx_card_state_notif(struct iwl_priv *priv,
|
|||
|
||||
int iwl_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (src == IWL_PWR_SRC_VAUX) {
|
||||
if (pci_pme_capable(priv->pci_dev, PCI_D3cold))
|
||||
iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG,
|
||||
|
@ -793,10 +767,7 @@ int iwl_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src)
|
|||
~APMG_PS_CTRL_MSK_PWR_SRC);
|
||||
}
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
err:
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -860,6 +831,7 @@ void iwl_rx_handle(struct iwl_priv *priv)
|
|||
unsigned long flags;
|
||||
u8 fill_rx = 0;
|
||||
u32 count = 8;
|
||||
int total_empty;
|
||||
|
||||
/* uCode's read index (stored in shared DRAM) indicates the last Rx
|
||||
* buffer that the driver may process (last buffer filled by ucode). */
|
||||
|
@ -870,7 +842,12 @@ void iwl_rx_handle(struct iwl_priv *priv)
|
|||
if (i == r)
|
||||
IWL_DEBUG_RX(priv, "r = %d, i = %d\n", r, i);
|
||||
|
||||
if (iwl_rx_queue_space(rxq) > (RX_QUEUE_SIZE / 2))
|
||||
/* calculate total frames need to be restock after handling RX */
|
||||
total_empty = r - priv->rxq.write_actual;
|
||||
if (total_empty < 0)
|
||||
total_empty += RX_QUEUE_SIZE;
|
||||
|
||||
if (total_empty > (RX_QUEUE_SIZE / 2))
|
||||
fill_rx = 1;
|
||||
|
||||
while (i != r) {
|
||||
|
@ -947,7 +924,7 @@ void iwl_rx_handle(struct iwl_priv *priv)
|
|||
count++;
|
||||
if (count >= 8) {
|
||||
priv->rxq.read = i;
|
||||
iwl_rx_queue_restock(priv);
|
||||
iwl_rx_replenish_now(priv);
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
@ -955,7 +932,10 @@ void iwl_rx_handle(struct iwl_priv *priv)
|
|||
|
||||
/* Backtrack one entry */
|
||||
priv->rxq.read = i;
|
||||
iwl_rx_queue_restock(priv);
|
||||
if (fill_rx)
|
||||
iwl_rx_replenish_now(priv);
|
||||
else
|
||||
iwl_rx_queue_restock(priv);
|
||||
}
|
||||
|
||||
/* call this function to flush any scheduled tasklet */
|
||||
|
@ -966,7 +946,7 @@ static inline void iwl_synchronize_irq(struct iwl_priv *priv)
|
|||
tasklet_kill(&priv->irq_tasklet);
|
||||
}
|
||||
|
||||
static void iwl_irq_tasklet(struct iwl_priv *priv)
|
||||
static void iwl_irq_tasklet_legacy(struct iwl_priv *priv)
|
||||
{
|
||||
u32 inta, handled = 0;
|
||||
u32 inta_fh;
|
||||
|
@ -1127,9 +1107,9 @@ static void iwl_irq_tasklet(struct iwl_priv *priv)
|
|||
priv->isr_stats.unhandled++;
|
||||
}
|
||||
|
||||
if (inta & ~CSR_INI_SET_MASK) {
|
||||
if (inta & ~(priv->inta_mask)) {
|
||||
IWL_WARN(priv, "Disabled INTA bits 0x%08x were pending\n",
|
||||
inta & ~CSR_INI_SET_MASK);
|
||||
inta & ~priv->inta_mask);
|
||||
IWL_WARN(priv, " with FH_INT = 0x%08x\n", inta_fh);
|
||||
}
|
||||
|
||||
|
@ -1150,6 +1130,199 @@ static void iwl_irq_tasklet(struct iwl_priv *priv)
|
|||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
/* tasklet for iwlagn interrupt */
|
||||
static void iwl_irq_tasklet(struct iwl_priv *priv)
|
||||
{
|
||||
u32 inta = 0;
|
||||
u32 handled = 0;
|
||||
unsigned long flags;
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
u32 inta_mask;
|
||||
#endif
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
|
||||
/* Ack/clear/reset pending uCode interrupts.
|
||||
* Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS,
|
||||
*/
|
||||
iwl_write32(priv, CSR_INT, priv->inta);
|
||||
|
||||
inta = priv->inta;
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
if (priv->debug_level & IWL_DL_ISR) {
|
||||
/* just for debug */
|
||||
inta_mask = iwl_read32(priv, CSR_INT_MASK);
|
||||
IWL_DEBUG_ISR(priv, "inta 0x%08x, enabled 0x%08x\n ",
|
||||
inta, inta_mask);
|
||||
}
|
||||
#endif
|
||||
/* saved interrupt in inta variable now we can reset priv->inta */
|
||||
priv->inta = 0;
|
||||
|
||||
/* Now service all interrupt bits discovered above. */
|
||||
if (inta & CSR_INT_BIT_HW_ERR) {
|
||||
IWL_ERR(priv, "Microcode HW error detected. Restarting.\n");
|
||||
|
||||
/* Tell the device to stop sending interrupts */
|
||||
iwl_disable_interrupts(priv);
|
||||
|
||||
priv->isr_stats.hw++;
|
||||
iwl_irq_handle_error(priv);
|
||||
|
||||
handled |= CSR_INT_BIT_HW_ERR;
|
||||
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
if (priv->debug_level & (IWL_DL_ISR)) {
|
||||
/* NIC fires this, but we don't use it, redundant with WAKEUP */
|
||||
if (inta & CSR_INT_BIT_SCD) {
|
||||
IWL_DEBUG_ISR(priv, "Scheduler finished to transmit "
|
||||
"the frame/frames.\n");
|
||||
priv->isr_stats.sch++;
|
||||
}
|
||||
|
||||
/* Alive notification via Rx interrupt will do the real work */
|
||||
if (inta & CSR_INT_BIT_ALIVE) {
|
||||
IWL_DEBUG_ISR(priv, "Alive interrupt\n");
|
||||
priv->isr_stats.alive++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* Safely ignore these bits for debug checks below */
|
||||
inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE);
|
||||
|
||||
/* HW RF KILL switch toggled */
|
||||
if (inta & CSR_INT_BIT_RF_KILL) {
|
||||
int hw_rf_kill = 0;
|
||||
if (!(iwl_read32(priv, CSR_GP_CNTRL) &
|
||||
CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW))
|
||||
hw_rf_kill = 1;
|
||||
|
||||
IWL_DEBUG_RF_KILL(priv, "RF_KILL bit toggled to %s.\n",
|
||||
hw_rf_kill ? "disable radio" : "enable radio");
|
||||
|
||||
priv->isr_stats.rfkill++;
|
||||
|
||||
/* driver only loads ucode once setting the interface up.
|
||||
* the driver allows loading the ucode even if the radio
|
||||
* is killed. Hence update the killswitch state here. The
|
||||
* rfkill handler will care about restarting if needed.
|
||||
*/
|
||||
if (!test_bit(STATUS_ALIVE, &priv->status)) {
|
||||
if (hw_rf_kill)
|
||||
set_bit(STATUS_RF_KILL_HW, &priv->status);
|
||||
else
|
||||
clear_bit(STATUS_RF_KILL_HW, &priv->status);
|
||||
queue_work(priv->workqueue, &priv->rf_kill);
|
||||
}
|
||||
|
||||
handled |= CSR_INT_BIT_RF_KILL;
|
||||
}
|
||||
|
||||
/* Chip got too hot and stopped itself */
|
||||
if (inta & CSR_INT_BIT_CT_KILL) {
|
||||
IWL_ERR(priv, "Microcode CT kill error detected.\n");
|
||||
priv->isr_stats.ctkill++;
|
||||
handled |= CSR_INT_BIT_CT_KILL;
|
||||
}
|
||||
|
||||
/* Error detected by uCode */
|
||||
if (inta & CSR_INT_BIT_SW_ERR) {
|
||||
IWL_ERR(priv, "Microcode SW error detected. "
|
||||
" Restarting 0x%X.\n", inta);
|
||||
priv->isr_stats.sw++;
|
||||
priv->isr_stats.sw_err = inta;
|
||||
iwl_irq_handle_error(priv);
|
||||
handled |= CSR_INT_BIT_SW_ERR;
|
||||
}
|
||||
|
||||
/* uCode wakes up after power-down sleep */
|
||||
if (inta & CSR_INT_BIT_WAKEUP) {
|
||||
IWL_DEBUG_ISR(priv, "Wakeup interrupt\n");
|
||||
iwl_rx_queue_update_write_ptr(priv, &priv->rxq);
|
||||
iwl_txq_update_write_ptr(priv, &priv->txq[0]);
|
||||
iwl_txq_update_write_ptr(priv, &priv->txq[1]);
|
||||
iwl_txq_update_write_ptr(priv, &priv->txq[2]);
|
||||
iwl_txq_update_write_ptr(priv, &priv->txq[3]);
|
||||
iwl_txq_update_write_ptr(priv, &priv->txq[4]);
|
||||
iwl_txq_update_write_ptr(priv, &priv->txq[5]);
|
||||
|
||||
priv->isr_stats.wakeup++;
|
||||
|
||||
handled |= CSR_INT_BIT_WAKEUP;
|
||||
}
|
||||
|
||||
/* All uCode command responses, including Tx command responses,
|
||||
* Rx "responses" (frame-received notification), and other
|
||||
* notifications from uCode come through here*/
|
||||
if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX |
|
||||
CSR_INT_BIT_RX_PERIODIC)) {
|
||||
IWL_DEBUG_ISR(priv, "Rx interrupt\n");
|
||||
if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) {
|
||||
handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX);
|
||||
iwl_write32(priv, CSR_FH_INT_STATUS,
|
||||
CSR49_FH_INT_RX_MASK);
|
||||
}
|
||||
if (inta & CSR_INT_BIT_RX_PERIODIC) {
|
||||
handled |= CSR_INT_BIT_RX_PERIODIC;
|
||||
iwl_write32(priv, CSR_INT, CSR_INT_BIT_RX_PERIODIC);
|
||||
}
|
||||
/* Sending RX interrupt require many steps to be done in the
|
||||
* the device:
|
||||
* 1- write interrupt to current index in ICT table.
|
||||
* 2- dma RX frame.
|
||||
* 3- update RX shared data to indicate last write index.
|
||||
* 4- send interrupt.
|
||||
* This could lead to RX race, driver could receive RX interrupt
|
||||
* but the shared data changes does not reflect this.
|
||||
* this could lead to RX race, RX periodic will solve this race
|
||||
*/
|
||||
iwl_write32(priv, CSR_INT_PERIODIC_REG,
|
||||
CSR_INT_PERIODIC_DIS);
|
||||
iwl_rx_handle(priv);
|
||||
/* Only set RX periodic if real RX is received. */
|
||||
if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX))
|
||||
iwl_write32(priv, CSR_INT_PERIODIC_REG,
|
||||
CSR_INT_PERIODIC_ENA);
|
||||
|
||||
priv->isr_stats.rx++;
|
||||
}
|
||||
|
||||
if (inta & CSR_INT_BIT_FH_TX) {
|
||||
iwl_write32(priv, CSR_FH_INT_STATUS, CSR49_FH_INT_TX_MASK);
|
||||
IWL_DEBUG_ISR(priv, "Tx interrupt\n");
|
||||
priv->isr_stats.tx++;
|
||||
handled |= CSR_INT_BIT_FH_TX;
|
||||
/* FH finished to write, send event */
|
||||
priv->ucode_write_complete = 1;
|
||||
wake_up_interruptible(&priv->wait_command_queue);
|
||||
}
|
||||
|
||||
if (inta & ~handled) {
|
||||
IWL_ERR(priv, "Unhandled INTA bits 0x%08x\n", inta & ~handled);
|
||||
priv->isr_stats.unhandled++;
|
||||
}
|
||||
|
||||
if (inta & ~(priv->inta_mask)) {
|
||||
IWL_WARN(priv, "Disabled INTA bits 0x%08x were pending\n",
|
||||
inta & ~priv->inta_mask);
|
||||
}
|
||||
|
||||
|
||||
/* Re-enable all interrupts */
|
||||
/* only Re-enable if diabled by irq */
|
||||
if (test_bit(STATUS_INT_ENABLED, &priv->status))
|
||||
iwl_enable_interrupts(priv);
|
||||
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
|
@ -1579,6 +1752,8 @@ static void __iwl_down(struct iwl_priv *priv)
|
|||
test_bit(STATUS_EXIT_PENDING, &priv->status) <<
|
||||
STATUS_EXIT_PENDING;
|
||||
|
||||
/* device going down, Stop using ICT table */
|
||||
iwl_disable_ict(priv);
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
iwl_clear_bit(priv, CSR_GP_CNTRL,
|
||||
CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
|
||||
|
@ -1587,13 +1762,8 @@ static void __iwl_down(struct iwl_priv *priv)
|
|||
iwl_txq_ctx_stop(priv);
|
||||
iwl_rxq_stop(priv);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
if (!iwl_grab_nic_access(priv)) {
|
||||
iwl_write_prph(priv, APMG_CLK_DIS_REG,
|
||||
APMG_CLK_VAL_DMA_CLK_RQT);
|
||||
iwl_release_nic_access(priv);
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
iwl_write_prph(priv, APMG_CLK_DIS_REG,
|
||||
APMG_CLK_VAL_DMA_CLK_RQT);
|
||||
|
||||
udelay(5);
|
||||
|
||||
|
@ -1622,6 +1792,49 @@ static void iwl_down(struct iwl_priv *priv)
|
|||
iwl_cancel_deferred_work(priv);
|
||||
}
|
||||
|
||||
#define HW_READY_TIMEOUT (50)
|
||||
|
||||
static int iwl_set_hw_ready(struct iwl_priv *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
|
||||
CSR_HW_IF_CONFIG_REG_BIT_NIC_READY);
|
||||
|
||||
/* See if we got it */
|
||||
ret = iwl_poll_bit(priv, CSR_HW_IF_CONFIG_REG,
|
||||
CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
|
||||
CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
|
||||
HW_READY_TIMEOUT);
|
||||
if (ret != -ETIMEDOUT)
|
||||
priv->hw_ready = true;
|
||||
else
|
||||
priv->hw_ready = false;
|
||||
|
||||
IWL_DEBUG_INFO(priv, "hardware %s\n",
|
||||
(priv->hw_ready == 1) ? "ready" : "not ready");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iwl_prepare_card_hw(struct iwl_priv *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
IWL_DEBUG_INFO(priv, "iwl_prepare_card_hw enter \n");
|
||||
|
||||
iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG,
|
||||
CSR_HW_IF_CONFIG_REG_PREPARE);
|
||||
|
||||
ret = iwl_poll_bit(priv, CSR_HW_IF_CONFIG_REG,
|
||||
~CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE,
|
||||
CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 150000);
|
||||
|
||||
if (ret != -ETIMEDOUT)
|
||||
iwl_set_hw_ready(priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define MAX_HW_RESTARTS 5
|
||||
|
||||
static int __iwl_up(struct iwl_priv *priv)
|
||||
|
@ -1639,6 +1852,13 @@ static int __iwl_up(struct iwl_priv *priv)
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
iwl_prepare_card_hw(priv);
|
||||
|
||||
if (!priv->hw_ready) {
|
||||
IWL_WARN(priv, "Exit HW not ready\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* If platform's RF_KILL switch is NOT set to KILL */
|
||||
if (iwl_read32(priv, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)
|
||||
clear_bit(STATUS_RF_KILL_HW, &priv->status);
|
||||
|
@ -1667,6 +1887,8 @@ static int __iwl_up(struct iwl_priv *priv)
|
|||
|
||||
/* clear (again), then enable host interrupts */
|
||||
iwl_write32(priv, CSR_INT, 0xFFFFFFFF);
|
||||
/* enable dram interrupt */
|
||||
iwl_reset_ict(priv);
|
||||
iwl_enable_interrupts(priv);
|
||||
|
||||
/* really make sure rfkill handshake bits are cleared */
|
||||
|
@ -2298,8 +2520,10 @@ static ssize_t show_version(struct device *d,
|
|||
|
||||
if (priv->eeprom) {
|
||||
eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION);
|
||||
pos += sprintf(buf + pos, "EEPROM version: 0x%x\n",
|
||||
eeprom_ver);
|
||||
pos += sprintf(buf + pos, "NVM Type: %s, version: 0x%x\n",
|
||||
(priv->nvm_device_type == NVM_DEVICE_TYPE_OTP)
|
||||
? "OTP" : "EEPROM", eeprom_ver);
|
||||
|
||||
} else {
|
||||
pos += sprintf(buf + pos, "EEPROM not initialzed\n");
|
||||
}
|
||||
|
@ -2478,7 +2702,7 @@ static DEVICE_ATTR(power_level, S_IWUSR | S_IRUSR, show_power_level,
|
|||
static ssize_t show_qos(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iwl_priv *priv = (struct iwl_priv *)d->driver_data;
|
||||
struct iwl_priv *priv = dev_get_drvdata(d);
|
||||
char *p = buf;
|
||||
int q;
|
||||
|
||||
|
@ -2565,8 +2789,12 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
|
|||
priv->statistics_periodic.data = (unsigned long)priv;
|
||||
priv->statistics_periodic.function = iwl_bg_statistics_periodic;
|
||||
|
||||
tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
|
||||
iwl_irq_tasklet, (unsigned long)priv);
|
||||
if (!priv->cfg->use_isr_legacy)
|
||||
tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
|
||||
iwl_irq_tasklet, (unsigned long)priv);
|
||||
else
|
||||
tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
|
||||
iwl_irq_tasklet_legacy, (unsigned long)priv);
|
||||
}
|
||||
|
||||
static void iwl_cancel_deferred_work(struct iwl_priv *priv)
|
||||
|
@ -2655,6 +2883,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
IWL_DEBUG_INFO(priv, "*** LOAD DRIVER ***\n");
|
||||
priv->cfg = cfg;
|
||||
priv->pci_dev = pdev;
|
||||
priv->inta_mask = CSR_INI_SET_MASK;
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
priv->debug_level = priv->cfg->mod_params->debug;
|
||||
|
@ -2705,6 +2934,10 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
(unsigned long long) pci_resource_len(pdev, 0));
|
||||
IWL_DEBUG_INFO(priv, "pci_resource_base = %p\n", priv->hw_base);
|
||||
|
||||
/* this spin lock will be used in apm_ops.init and EEPROM access
|
||||
* we should init now
|
||||
*/
|
||||
spin_lock_init(&priv->reg_lock);
|
||||
iwl_hw_detect(priv);
|
||||
IWL_INFO(priv, "Detected Intel Wireless WiFi Link %s REV=0x%X\n",
|
||||
priv->cfg->name, priv->hw_rev);
|
||||
|
@ -2713,6 +2946,12 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
* PCI Tx retries from interfering with C3 CPU state */
|
||||
pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
|
||||
|
||||
iwl_prepare_card_hw(priv);
|
||||
if (!priv->hw_ready) {
|
||||
IWL_WARN(priv, "Failed, HW not ready\n");
|
||||
goto out_iounmap;
|
||||
}
|
||||
|
||||
/* amp init */
|
||||
err = priv->cfg->ops->lib->apm_ops.init(priv);
|
||||
if (err < 0) {
|
||||
|
@ -2763,8 +3002,9 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
|
||||
pci_enable_msi(priv->pci_dev);
|
||||
|
||||
err = request_irq(priv->pci_dev->irq, iwl_isr, IRQF_SHARED,
|
||||
DRV_NAME, priv);
|
||||
iwl_alloc_isr_ict(priv);
|
||||
err = request_irq(priv->pci_dev->irq, priv->cfg->ops->lib->isr,
|
||||
IRQF_SHARED, DRV_NAME, priv);
|
||||
if (err) {
|
||||
IWL_ERR(priv, "Error allocating IRQ %d\n", priv->pci_dev->irq);
|
||||
goto out_disable_msi;
|
||||
|
@ -2821,6 +3061,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
sysfs_remove_group(&pdev->dev.kobj, &iwl_attribute_group);
|
||||
out_free_irq:
|
||||
free_irq(priv->pci_dev->irq, priv);
|
||||
iwl_free_isr_ict(priv);
|
||||
out_disable_msi:
|
||||
pci_disable_msi(priv->pci_dev);
|
||||
iwl_uninit_drv(priv);
|
||||
|
@ -2902,6 +3143,8 @@ static void __devexit iwl_pci_remove(struct pci_dev *pdev)
|
|||
|
||||
iwl_uninit_drv(priv);
|
||||
|
||||
iwl_free_isr_ict(priv);
|
||||
|
||||
if (priv->ibss_beacon)
|
||||
dev_kfree_skb(priv->ibss_beacon);
|
||||
|
||||
|
|
|
@ -614,8 +614,18 @@ enum {
|
|||
|
||||
#define RXON_FLG_CHANNEL_MODE_POS (25)
|
||||
#define RXON_FLG_CHANNEL_MODE_MSK cpu_to_le32(0x3 << 25)
|
||||
#define RXON_FLG_CHANNEL_MODE_PURE_40_MSK cpu_to_le32(0x1 << 25)
|
||||
#define RXON_FLG_CHANNEL_MODE_MIXED_MSK cpu_to_le32(0x2 << 25)
|
||||
|
||||
/* channel mode */
|
||||
enum {
|
||||
CHANNEL_MODE_LEGACY = 0,
|
||||
CHANNEL_MODE_PURE_40 = 1,
|
||||
CHANNEL_MODE_MIXED = 2,
|
||||
CHANNEL_MODE_RESERVED = 3,
|
||||
};
|
||||
#define RXON_FLG_CHANNEL_MODE_LEGACY cpu_to_le32(CHANNEL_MODE_LEGACY << RXON_FLG_CHANNEL_MODE_POS)
|
||||
#define RXON_FLG_CHANNEL_MODE_PURE_40 cpu_to_le32(CHANNEL_MODE_PURE_40 << RXON_FLG_CHANNEL_MODE_POS)
|
||||
#define RXON_FLG_CHANNEL_MODE_MIXED cpu_to_le32(CHANNEL_MODE_MIXED << RXON_FLG_CHANNEL_MODE_POS)
|
||||
|
||||
/* CTS to self (if spec allows) flag */
|
||||
#define RXON_FLG_SELF_CTS_EN cpu_to_le32(0x1<<30)
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "iwl-rfkill.h"
|
||||
#include "iwl-power.h"
|
||||
#include "iwl-sta.h"
|
||||
#include "iwl-helpers.h"
|
||||
|
||||
|
||||
MODULE_DESCRIPTION("iwl core");
|
||||
|
@ -59,6 +60,8 @@ MODULE_LICENSE("GPL");
|
|||
IWL_RATE_##pp##M_INDEX, \
|
||||
IWL_RATE_##np##M_INDEX }
|
||||
|
||||
static irqreturn_t iwl_isr(int irq, void *data);
|
||||
|
||||
/*
|
||||
* Parameter order:
|
||||
* rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate
|
||||
|
@ -603,10 +606,10 @@ static u8 iwl_is_channel_extension(struct iwl_priv *priv,
|
|||
|
||||
if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE)
|
||||
return !(ch_info->fat_extension_channel &
|
||||
IEEE80211_CHAN_NO_FAT_ABOVE);
|
||||
IEEE80211_CHAN_NO_HT40PLUS);
|
||||
else if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW)
|
||||
return !(ch_info->fat_extension_channel &
|
||||
IEEE80211_CHAN_NO_FAT_BELOW);
|
||||
IEEE80211_CHAN_NO_HT40MINUS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -617,19 +620,23 @@ u8 iwl_is_fat_tx_allowed(struct iwl_priv *priv,
|
|||
struct iwl_ht_info *iwl_ht_conf = &priv->current_ht_config;
|
||||
|
||||
if ((!iwl_ht_conf->is_ht) ||
|
||||
(iwl_ht_conf->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ) ||
|
||||
(iwl_ht_conf->extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_NONE))
|
||||
(iwl_ht_conf->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ))
|
||||
return 0;
|
||||
|
||||
/* We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40
|
||||
* the bit will not set if it is pure 40MHz case
|
||||
*/
|
||||
if (sta_ht_inf) {
|
||||
if ((!sta_ht_inf->ht_supported) ||
|
||||
(!(sta_ht_inf->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)))
|
||||
if (!sta_ht_inf->ht_supported)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return iwl_is_channel_extension(priv, priv->band,
|
||||
le16_to_cpu(priv->staging_rxon.channel),
|
||||
iwl_ht_conf->extension_chan_offset);
|
||||
if (iwl_ht_conf->ht_protection & IEEE80211_HT_OP_MODE_PROTECTION_20MHZ)
|
||||
return 1;
|
||||
else
|
||||
return iwl_is_channel_extension(priv, priv->band,
|
||||
le16_to_cpu(priv->staging_rxon.channel),
|
||||
iwl_ht_conf->extension_chan_offset);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_is_fat_tx_allowed);
|
||||
|
||||
|
@ -799,42 +806,51 @@ EXPORT_SYMBOL(iwl_rate_get_lowest_plcp);
|
|||
void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info)
|
||||
{
|
||||
struct iwl_rxon_cmd *rxon = &priv->staging_rxon;
|
||||
u32 val;
|
||||
|
||||
if (!ht_info->is_ht) {
|
||||
rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED_MSK |
|
||||
RXON_FLG_CHANNEL_MODE_PURE_40_MSK |
|
||||
rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK |
|
||||
RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK |
|
||||
RXON_FLG_FAT_PROT_MSK |
|
||||
RXON_FLG_HT_PROT_MSK);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set up channel bandwidth: 20 MHz only, or 20/40 mixed if fat ok */
|
||||
if (iwl_is_fat_tx_allowed(priv, NULL))
|
||||
rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED_MSK;
|
||||
else
|
||||
rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED_MSK |
|
||||
RXON_FLG_CHANNEL_MODE_PURE_40_MSK);
|
||||
/* FIXME: if the definition of ht_protection changed, the "translation"
|
||||
* will be needed for rxon->flags
|
||||
*/
|
||||
rxon->flags |= cpu_to_le32(ht_info->ht_protection << RXON_FLG_HT_OPERATING_MODE_POS);
|
||||
|
||||
/* Note: control channel is opposite of extension channel */
|
||||
switch (ht_info->extension_chan_offset) {
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
||||
rxon->flags &= ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
|
||||
break;
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
||||
rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
|
||||
break;
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_NONE:
|
||||
default:
|
||||
rxon->flags &= ~RXON_FLG_CHANNEL_MODE_MIXED_MSK;
|
||||
break;
|
||||
/* Set up channel bandwidth:
|
||||
* 20 MHz only, 20/40 mixed or pure 40 if fat ok */
|
||||
/* clear the HT channel mode before set the mode */
|
||||
rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK |
|
||||
RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
|
||||
if (iwl_is_fat_tx_allowed(priv, NULL)) {
|
||||
/* pure 40 fat */
|
||||
if (rxon->flags & RXON_FLG_FAT_PROT_MSK)
|
||||
rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40;
|
||||
else {
|
||||
/* Note: control channel is opposite of extension channel */
|
||||
switch (ht_info->extension_chan_offset) {
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
||||
rxon->flags &= ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK);
|
||||
rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
|
||||
break;
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
||||
rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK;
|
||||
rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED;
|
||||
break;
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_NONE:
|
||||
default:
|
||||
/* channel location only valid if in Mixed mode */
|
||||
IWL_ERR(priv, "invalid extension channel offset\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY;
|
||||
}
|
||||
|
||||
val = ht_info->ht_protection;
|
||||
|
||||
rxon->flags |= cpu_to_le32(val << RXON_FLG_HT_OPERATING_MODE_POS);
|
||||
|
||||
if (priv->cfg->ops->hcmd->set_rxon_chain)
|
||||
priv->cfg->ops->hcmd->set_rxon_chain(priv);
|
||||
|
||||
|
@ -1122,8 +1138,9 @@ void iwl_connection_init_rx_config(struct iwl_priv *priv, int mode)
|
|||
priv->staging_rxon.cck_basic_rates =
|
||||
(IWL_CCK_RATES_MASK >> IWL_FIRST_CCK_RATE) & 0xF;
|
||||
|
||||
priv->staging_rxon.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED_MSK |
|
||||
RXON_FLG_CHANNEL_MODE_PURE_40_MSK);
|
||||
/* clear both MIX and PURE40 mode flag */
|
||||
priv->staging_rxon.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED |
|
||||
RXON_FLG_CHANNEL_MODE_PURE_40);
|
||||
memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN);
|
||||
memcpy(priv->staging_rxon.wlap_bssid_addr, priv->mac_addr, ETH_ALEN);
|
||||
priv->staging_rxon.ofdm_ht_single_stream_basic_rates = 0xff;
|
||||
|
@ -1483,11 +1500,272 @@ void iwl_enable_interrupts(struct iwl_priv *priv)
|
|||
{
|
||||
IWL_DEBUG_ISR(priv, "Enabling interrupts\n");
|
||||
set_bit(STATUS_INT_ENABLED, &priv->status);
|
||||
iwl_write32(priv, CSR_INT_MASK, CSR_INI_SET_MASK);
|
||||
iwl_write32(priv, CSR_INT_MASK, priv->inta_mask);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_enable_interrupts);
|
||||
|
||||
irqreturn_t iwl_isr(int irq, void *data)
|
||||
|
||||
#define ICT_COUNT (PAGE_SIZE/sizeof(u32))
|
||||
|
||||
/* Free dram table */
|
||||
void iwl_free_isr_ict(struct iwl_priv *priv)
|
||||
{
|
||||
if (priv->ict_tbl_vir) {
|
||||
pci_free_consistent(priv->pci_dev, (sizeof(u32) * ICT_COUNT) +
|
||||
PAGE_SIZE, priv->ict_tbl_vir,
|
||||
priv->ict_tbl_dma);
|
||||
priv->ict_tbl_vir = NULL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_free_isr_ict);
|
||||
|
||||
|
||||
/* allocate dram shared table it is a PAGE_SIZE aligned
|
||||
* also reset all data related to ICT table interrupt.
|
||||
*/
|
||||
int iwl_alloc_isr_ict(struct iwl_priv *priv)
|
||||
{
|
||||
|
||||
if (priv->cfg->use_isr_legacy)
|
||||
return 0;
|
||||
/* allocate shrared data table */
|
||||
priv->ict_tbl_vir = pci_alloc_consistent(priv->pci_dev, (sizeof(u32) *
|
||||
ICT_COUNT) + PAGE_SIZE,
|
||||
&priv->ict_tbl_dma);
|
||||
if (!priv->ict_tbl_vir)
|
||||
return -ENOMEM;
|
||||
|
||||
/* align table to PAGE_SIZE boundry */
|
||||
priv->aligned_ict_tbl_dma = ALIGN(priv->ict_tbl_dma, PAGE_SIZE);
|
||||
|
||||
IWL_DEBUG_ISR(priv, "ict dma addr %Lx dma aligned %Lx diff %d\n",
|
||||
(unsigned long long)priv->ict_tbl_dma,
|
||||
(unsigned long long)priv->aligned_ict_tbl_dma,
|
||||
(int)(priv->aligned_ict_tbl_dma - priv->ict_tbl_dma));
|
||||
|
||||
priv->ict_tbl = priv->ict_tbl_vir +
|
||||
(priv->aligned_ict_tbl_dma - priv->ict_tbl_dma);
|
||||
|
||||
IWL_DEBUG_ISR(priv, "ict vir addr %p vir aligned %p diff %d\n",
|
||||
priv->ict_tbl, priv->ict_tbl_vir,
|
||||
(int)(priv->aligned_ict_tbl_dma - priv->ict_tbl_dma));
|
||||
|
||||
/* reset table and index to all 0 */
|
||||
memset(priv->ict_tbl_vir,0, (sizeof(u32) * ICT_COUNT) + PAGE_SIZE);
|
||||
priv->ict_index = 0;
|
||||
|
||||
/* add periodic RX interrupt */
|
||||
priv->inta_mask |= CSR_INT_BIT_RX_PERIODIC;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_alloc_isr_ict);
|
||||
|
||||
/* Device is going up inform it about using ICT interrupt table,
|
||||
* also we need to tell the driver to start using ICT interrupt.
|
||||
*/
|
||||
int iwl_reset_ict(struct iwl_priv *priv)
|
||||
{
|
||||
u32 val;
|
||||
unsigned long flags;
|
||||
|
||||
if (!priv->ict_tbl_vir)
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
iwl_disable_interrupts(priv);
|
||||
|
||||
memset(&priv->ict_tbl[0],0, sizeof(u32) * ICT_COUNT);
|
||||
|
||||
val = priv->aligned_ict_tbl_dma >> PAGE_SHIFT;
|
||||
|
||||
val |= CSR_DRAM_INT_TBL_ENABLE;
|
||||
val |= CSR_DRAM_INIT_TBL_WRAP_CHECK;
|
||||
|
||||
IWL_DEBUG_ISR(priv, "CSR_DRAM_INT_TBL_REG =0x%X "
|
||||
"aligned dma address %Lx\n",
|
||||
val, (unsigned long long)priv->aligned_ict_tbl_dma);
|
||||
|
||||
iwl_write32(priv, CSR_DRAM_INT_TBL_REG, val);
|
||||
priv->use_ict = true;
|
||||
priv->ict_index = 0;
|
||||
iwl_write32(priv, CSR_INT, priv->inta_mask);
|
||||
iwl_enable_interrupts(priv);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_reset_ict);
|
||||
|
||||
/* Device is going down disable ict interrupt usage */
|
||||
void iwl_disable_ict(struct iwl_priv *priv)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
priv->use_ict = false;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_disable_ict);
|
||||
|
||||
/* interrupt handler using ict table, with this interrupt driver will
|
||||
* stop using INTA register to get device's interrupt, reading this register
|
||||
* is expensive, device will write interrupts in ICT dram table, increment
|
||||
* index then will fire interrupt to driver, driver will OR all ICT table
|
||||
* entries from current index up to table entry with 0 value. the result is
|
||||
* the interrupt we need to service, driver will set the entries back to 0 and
|
||||
* set index.
|
||||
*/
|
||||
irqreturn_t iwl_isr_ict(int irq, void *data)
|
||||
{
|
||||
struct iwl_priv *priv = data;
|
||||
u32 inta, inta_mask;
|
||||
u32 val = 0;
|
||||
|
||||
if (!priv)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* dram interrupt table not set yet,
|
||||
* use legacy interrupt.
|
||||
*/
|
||||
if (!priv->use_ict)
|
||||
return iwl_isr(irq, data);
|
||||
|
||||
spin_lock(&priv->lock);
|
||||
|
||||
/* Disable (but don't clear!) interrupts here to avoid
|
||||
* back-to-back ISRs and sporadic interrupts from our NIC.
|
||||
* If we have something to service, the tasklet will re-enable ints.
|
||||
* If we *don't* have something, we'll re-enable before leaving here.
|
||||
*/
|
||||
inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */
|
||||
iwl_write32(priv, CSR_INT_MASK, 0x00000000);
|
||||
|
||||
|
||||
/* Ignore interrupt if there's nothing in NIC to service.
|
||||
* This may be due to IRQ shared with another device,
|
||||
* or due to sporadic interrupts thrown from our NIC. */
|
||||
if (!priv->ict_tbl[priv->ict_index]) {
|
||||
IWL_DEBUG_ISR(priv, "Ignore interrupt, inta == 0\n");
|
||||
goto none;
|
||||
}
|
||||
|
||||
/* read all entries that not 0 start with ict_index */
|
||||
while (priv->ict_tbl[priv->ict_index]) {
|
||||
|
||||
val |= priv->ict_tbl[priv->ict_index];
|
||||
IWL_DEBUG_ISR(priv, "ICT index %d value 0x%08X\n",
|
||||
priv->ict_index,
|
||||
priv->ict_tbl[priv->ict_index]);
|
||||
priv->ict_tbl[priv->ict_index] = 0;
|
||||
priv->ict_index = iwl_queue_inc_wrap(priv->ict_index,
|
||||
ICT_COUNT);
|
||||
|
||||
}
|
||||
|
||||
/* We should not get this value, just ignore it. */
|
||||
if (val == 0xffffffff)
|
||||
val = 0;
|
||||
|
||||
inta = (0xff & val) | ((0xff00 & val) << 16);
|
||||
IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x ict 0x%08x\n",
|
||||
inta, inta_mask, val);
|
||||
|
||||
inta &= priv->inta_mask;
|
||||
priv->inta |= inta;
|
||||
|
||||
/* iwl_irq_tasklet() will service interrupts and re-enable them */
|
||||
if (likely(inta))
|
||||
tasklet_schedule(&priv->irq_tasklet);
|
||||
else if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->inta) {
|
||||
/* Allow interrupt if was disabled by this handler and
|
||||
* no tasklet was schedules, We should not enable interrupt,
|
||||
* tasklet will enable it.
|
||||
*/
|
||||
iwl_enable_interrupts(priv);
|
||||
}
|
||||
|
||||
spin_unlock(&priv->lock);
|
||||
return IRQ_HANDLED;
|
||||
|
||||
none:
|
||||
/* re-enable interrupts here since we don't have anything to service.
|
||||
* only Re-enable if disabled by irq.
|
||||
*/
|
||||
if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->inta)
|
||||
iwl_enable_interrupts(priv);
|
||||
|
||||
spin_unlock(&priv->lock);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_isr_ict);
|
||||
|
||||
|
||||
static irqreturn_t iwl_isr(int irq, void *data)
|
||||
{
|
||||
struct iwl_priv *priv = data;
|
||||
u32 inta, inta_mask;
|
||||
u32 inta_fh;
|
||||
|
||||
if (!priv)
|
||||
return IRQ_NONE;
|
||||
|
||||
spin_lock(&priv->lock);
|
||||
|
||||
/* Disable (but don't clear!) interrupts here to avoid
|
||||
* back-to-back ISRs and sporadic interrupts from our NIC.
|
||||
* If we have something to service, the tasklet will re-enable ints.
|
||||
* If we *don't* have something, we'll re-enable before leaving here. */
|
||||
inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */
|
||||
iwl_write32(priv, CSR_INT_MASK, 0x00000000);
|
||||
|
||||
/* Discover which interrupts are active/pending */
|
||||
inta = iwl_read32(priv, CSR_INT);
|
||||
|
||||
/* Ignore interrupt if there's nothing in NIC to service.
|
||||
* This may be due to IRQ shared with another device,
|
||||
* or due to sporadic interrupts thrown from our NIC. */
|
||||
if (!inta) {
|
||||
IWL_DEBUG_ISR(priv, "Ignore interrupt, inta == 0\n");
|
||||
goto none;
|
||||
}
|
||||
|
||||
if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) {
|
||||
/* Hardware disappeared. It might have already raised
|
||||
* an interrupt */
|
||||
IWL_WARN(priv, "HARDWARE GONE?? INTA == 0x%08x\n", inta);
|
||||
goto unplugged;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
if (priv->debug_level & (IWL_DL_ISR)) {
|
||||
inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS);
|
||||
IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x, "
|
||||
"fh 0x%08x\n", inta, inta_mask, inta_fh);
|
||||
}
|
||||
#endif
|
||||
|
||||
priv->inta |= inta;
|
||||
/* iwl_irq_tasklet() will service interrupts and re-enable them */
|
||||
if (likely(inta))
|
||||
tasklet_schedule(&priv->irq_tasklet);
|
||||
else if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->inta)
|
||||
iwl_enable_interrupts(priv);
|
||||
|
||||
unplugged:
|
||||
spin_unlock(&priv->lock);
|
||||
return IRQ_HANDLED;
|
||||
|
||||
none:
|
||||
/* re-enable interrupts here since we don't have anything to service. */
|
||||
/* only Re-enable if diabled by irq and no schedules tasklet. */
|
||||
if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->inta)
|
||||
iwl_enable_interrupts(priv);
|
||||
|
||||
spin_unlock(&priv->lock);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
irqreturn_t iwl_isr_legacy(int irq, void *data)
|
||||
{
|
||||
struct iwl_priv *priv = data;
|
||||
u32 inta, inta_mask;
|
||||
|
@ -1544,7 +1822,7 @@ irqreturn_t iwl_isr(int irq, void *data)
|
|||
spin_unlock(&priv->lock);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_isr);
|
||||
EXPORT_SYMBOL(iwl_isr_legacy);
|
||||
|
||||
int iwl_send_bt_config(struct iwl_priv *priv)
|
||||
{
|
||||
|
@ -1588,10 +1866,6 @@ static int iwlcore_verify_inst_sparse(struct iwl_priv *priv, __le32 *image, u32
|
|||
|
||||
IWL_DEBUG_INFO(priv, "ucode inst image size is %u\n", len);
|
||||
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < len; i += 100, image += 100/sizeof(u32)) {
|
||||
/* read data comes through single port, auto-incr addr */
|
||||
/* NOTE: Use the debugless read so we don't flood kernel log
|
||||
|
@ -1607,8 +1881,6 @@ static int iwlcore_verify_inst_sparse(struct iwl_priv *priv, __le32 *image, u32
|
|||
}
|
||||
}
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1626,10 +1898,6 @@ static int iwl_verify_inst_full(struct iwl_priv *priv, __le32 *image,
|
|||
|
||||
IWL_DEBUG_INFO(priv, "ucode inst image size is %u\n", len);
|
||||
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR,
|
||||
IWL49_RTC_INST_LOWER_BOUND);
|
||||
|
||||
|
@ -1650,8 +1918,6 @@ static int iwl_verify_inst_full(struct iwl_priv *priv, __le32 *image,
|
|||
}
|
||||
}
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
if (!errcnt)
|
||||
IWL_DEBUG_INFO(priv,
|
||||
"ucode image in INSTRUCTION memory is good\n");
|
||||
|
@ -1760,7 +2026,6 @@ void iwl_dump_nic_error_log(struct iwl_priv *priv)
|
|||
u32 data2, line;
|
||||
u32 desc, time, count, base, data1;
|
||||
u32 blink1, blink2, ilink1, ilink2;
|
||||
int ret;
|
||||
|
||||
if (priv->ucode_type == UCODE_INIT)
|
||||
base = le32_to_cpu(priv->card_alive_init.error_event_table_ptr);
|
||||
|
@ -1772,12 +2037,6 @@ void iwl_dump_nic_error_log(struct iwl_priv *priv)
|
|||
return;
|
||||
}
|
||||
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret) {
|
||||
IWL_WARN(priv, "Can not read from adapter at this time.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
count = iwl_read_targ_mem(priv, base);
|
||||
|
||||
if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) {
|
||||
|
@ -1804,7 +2063,6 @@ void iwl_dump_nic_error_log(struct iwl_priv *priv)
|
|||
IWL_ERR(priv, "0x%05X 0x%05X 0x%05X 0x%05X\n", blink1, blink2,
|
||||
ilink1, ilink2);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_dump_nic_error_log);
|
||||
|
||||
|
@ -1813,7 +2071,6 @@ EXPORT_SYMBOL(iwl_dump_nic_error_log);
|
|||
/**
|
||||
* iwl_print_event_log - Dump error event log to syslog
|
||||
*
|
||||
* NOTE: Must be called with iwl_grab_nic_access() already obtained!
|
||||
*/
|
||||
static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
|
||||
u32 num_events, u32 mode)
|
||||
|
@ -1859,7 +2116,6 @@ static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx,
|
|||
|
||||
void iwl_dump_nic_event_log(struct iwl_priv *priv)
|
||||
{
|
||||
int ret;
|
||||
u32 base; /* SRAM byte address of event log header */
|
||||
u32 capacity; /* event log capacity in # entries */
|
||||
u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */
|
||||
|
@ -1877,12 +2133,6 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv)
|
|||
return;
|
||||
}
|
||||
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret) {
|
||||
IWL_WARN(priv, "Can not read from adapter at this time.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* event log header */
|
||||
capacity = iwl_read_targ_mem(priv, base);
|
||||
mode = iwl_read_targ_mem(priv, base + (1 * sizeof(u32)));
|
||||
|
@ -1894,7 +2144,6 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv)
|
|||
/* bail out if nothing in log */
|
||||
if (size == 0) {
|
||||
IWL_ERR(priv, "Start IWL Event Log Dump: nothing in log\n");
|
||||
iwl_release_nic_access(priv);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1909,7 +2158,6 @@ void iwl_dump_nic_event_log(struct iwl_priv *priv)
|
|||
/* (then/else) start at top of log */
|
||||
iwl_print_event_log(priv, 0, next_entry, mode);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_dump_nic_event_log);
|
||||
|
||||
|
@ -2016,11 +2264,11 @@ int iwl_radio_kill_sw_enable_radio(struct iwl_priv *priv)
|
|||
/* wake up ucode */
|
||||
msleep(10);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
iwl_read32(priv, CSR_UCODE_DRV_GP1);
|
||||
spin_lock_irqsave(&priv->reg_lock, flags);
|
||||
if (!iwl_grab_nic_access(priv))
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
spin_unlock_irqrestore(&priv->reg_lock, flags);
|
||||
|
||||
if (test_bit(STATUS_RF_KILL_HW, &priv->status)) {
|
||||
IWL_DEBUG_RF_KILL(priv, "Can not turn radio back on - "
|
||||
|
|
|
@ -112,6 +112,19 @@ struct iwl_hcmd_utils_ops {
|
|||
struct iwl_rx_phy_res *rx_resp);
|
||||
};
|
||||
|
||||
struct iwl_apm_ops {
|
||||
int (*init)(struct iwl_priv *priv);
|
||||
int (*reset)(struct iwl_priv *priv);
|
||||
void (*stop)(struct iwl_priv *priv);
|
||||
void (*config)(struct iwl_priv *priv);
|
||||
int (*set_pwr_src)(struct iwl_priv *priv, enum iwl_pwr_src src);
|
||||
};
|
||||
|
||||
struct iwl_temp_ops {
|
||||
void (*temperature)(struct iwl_priv *priv);
|
||||
void (*set_ct_kill)(struct iwl_priv *priv);
|
||||
};
|
||||
|
||||
struct iwl_lib_ops {
|
||||
/* set hw dependent parameters */
|
||||
int (*set_hw_params)(struct iwl_priv *priv);
|
||||
|
@ -149,23 +162,21 @@ struct iwl_lib_ops {
|
|||
int (*is_valid_rtc_data_addr)(u32 addr);
|
||||
/* 1st ucode load */
|
||||
int (*load_ucode)(struct iwl_priv *priv);
|
||||
/* power management */
|
||||
struct {
|
||||
int (*init)(struct iwl_priv *priv);
|
||||
int (*reset)(struct iwl_priv *priv);
|
||||
void (*stop)(struct iwl_priv *priv);
|
||||
void (*config)(struct iwl_priv *priv);
|
||||
int (*set_pwr_src)(struct iwl_priv *priv, enum iwl_pwr_src src);
|
||||
} apm_ops;
|
||||
/* power management */
|
||||
struct iwl_apm_ops apm_ops;
|
||||
|
||||
/* power */
|
||||
int (*send_tx_power) (struct iwl_priv *priv);
|
||||
void (*update_chain_flags)(struct iwl_priv *priv);
|
||||
void (*temperature) (struct iwl_priv *priv);
|
||||
void (*post_associate) (struct iwl_priv *priv);
|
||||
void (*config_ap) (struct iwl_priv *priv);
|
||||
irqreturn_t (*isr) (int irq, void *data);
|
||||
|
||||
/* eeprom operations (as defined in iwl-eeprom.h) */
|
||||
struct iwl_eeprom_ops eeprom_ops;
|
||||
|
||||
/* temperature */
|
||||
struct iwl_temp_ops temp_ops;
|
||||
};
|
||||
|
||||
struct iwl_ops {
|
||||
|
@ -229,6 +240,7 @@ struct iwl_cfg {
|
|||
u8 valid_tx_ant;
|
||||
u8 valid_rx_ant;
|
||||
bool need_pll_cfg;
|
||||
bool use_isr_legacy;
|
||||
};
|
||||
|
||||
/***************************
|
||||
|
@ -306,10 +318,11 @@ int iwl_rx_queue_update_write_ptr(struct iwl_priv *priv,
|
|||
struct iwl_rx_queue *q);
|
||||
void iwl_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
|
||||
void iwl_rx_replenish(struct iwl_priv *priv);
|
||||
void iwl_rx_replenish_now(struct iwl_priv *priv);
|
||||
int iwl_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
|
||||
int iwl_rx_queue_restock(struct iwl_priv *priv);
|
||||
int iwl_rx_queue_space(const struct iwl_rx_queue *q);
|
||||
void iwl_rx_allocate(struct iwl_priv *priv);
|
||||
void iwl_rx_allocate(struct iwl_priv *priv, gfp_t priority);
|
||||
void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb);
|
||||
int iwl_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index);
|
||||
/* Handlers */
|
||||
|
@ -456,7 +469,13 @@ int iwl_send_card_state(struct iwl_priv *priv, u32 flags,
|
|||
*****************************************************/
|
||||
void iwl_disable_interrupts(struct iwl_priv *priv);
|
||||
void iwl_enable_interrupts(struct iwl_priv *priv);
|
||||
irqreturn_t iwl_isr(int irq, void *data);
|
||||
irqreturn_t iwl_isr_legacy(int irq, void *data);
|
||||
int iwl_reset_ict(struct iwl_priv *priv);
|
||||
void iwl_disable_ict(struct iwl_priv *priv);
|
||||
int iwl_alloc_isr_ict(struct iwl_priv *priv);
|
||||
void iwl_free_isr_ict(struct iwl_priv *priv);
|
||||
irqreturn_t iwl_isr_ict(int irq, void *data);
|
||||
|
||||
static inline u16 iwl_pcie_link_ctl(struct iwl_priv *priv)
|
||||
{
|
||||
int pos;
|
||||
|
|
|
@ -89,6 +89,7 @@
|
|||
/* EEPROM reads */
|
||||
#define CSR_EEPROM_REG (CSR_BASE+0x02c)
|
||||
#define CSR_EEPROM_GP (CSR_BASE+0x030)
|
||||
#define CSR_OTP_GP_REG (CSR_BASE+0x034)
|
||||
#define CSR_GIO_REG (CSR_BASE+0x03C)
|
||||
#define CSR_GP_UCODE (CSR_BASE+0x044)
|
||||
#define CSR_UCODE_DRV_GP1 (CSR_BASE+0x054)
|
||||
|
@ -96,8 +97,10 @@
|
|||
#define CSR_UCODE_DRV_GP1_CLR (CSR_BASE+0x05c)
|
||||
#define CSR_UCODE_DRV_GP2 (CSR_BASE+0x060)
|
||||
#define CSR_LED_REG (CSR_BASE+0x094)
|
||||
#define CSR_DRAM_INT_TBL_REG (CSR_BASE+0x0A0)
|
||||
#define CSR_GIO_CHICKEN_BITS (CSR_BASE+0x100)
|
||||
|
||||
#define CSR_INT_PERIODIC_REG (CSR_BASE+0x005)
|
||||
/* Analog phase-lock-loop configuration */
|
||||
#define CSR_ANA_PLL_CFG (CSR_BASE+0x20c)
|
||||
/*
|
||||
|
@ -123,16 +126,18 @@
|
|||
|
||||
#define CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A (0x00080000)
|
||||
#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000)
|
||||
#define CSR_HW_IF_CONFIG_REG_BIT_PCI_OWN_SEM (0x00400000)
|
||||
#define CSR_HW_IF_CONFIG_REG_BIT_ME_OWN (0x02000000)
|
||||
#define CSR_HW_IF_CONFIG_REG_BIT_WAKE_ME (0x08000000)
|
||||
#define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY (0x00400000)
|
||||
#define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000)
|
||||
#define CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000)
|
||||
|
||||
#define CSR_INT_PERIODIC_DIS (0x00)
|
||||
#define CSR_INT_PERIODIC_ENA (0xFF)
|
||||
|
||||
/* interrupt flags in INTA, set by uCode or hardware (e.g. dma),
|
||||
* acknowledged (reset) by host writing "1" to flagged bits. */
|
||||
#define CSR_INT_BIT_FH_RX (1 << 31) /* Rx DMA, cmd responses, FH_INT[17:16] */
|
||||
#define CSR_INT_BIT_HW_ERR (1 << 29) /* DMA hardware error FH_INT[31] */
|
||||
#define CSR_INT_BIT_DNLD (1 << 28) /* uCode Download */
|
||||
#define CSR_INT_BIT_RX_PERIODIC (1 << 28) /* Rx periodic */
|
||||
#define CSR_INT_BIT_FH_TX (1 << 27) /* Tx DMA FH_INT[1:0] */
|
||||
#define CSR_INT_BIT_SCD (1 << 26) /* TXQ pointer advanced */
|
||||
#define CSR_INT_BIT_SW_ERR (1 << 25) /* uCode error */
|
||||
|
@ -226,6 +231,10 @@
|
|||
#define CSR_EEPROM_GP_VALID_MSK (0x00000007)
|
||||
#define CSR_EEPROM_GP_BAD_SIGNATURE (0x00000000)
|
||||
#define CSR_EEPROM_GP_IF_OWNER_MSK (0x00000180)
|
||||
#define CSR_OTP_GP_REG_DEVICE_SELECT (0x00010000) /* 0 - EEPROM, 1 - OTP */
|
||||
#define CSR_OTP_GP_REG_OTP_ACCESS_MODE (0x00020000) /* 0 - absolute, 1 - relative */
|
||||
#define CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK (0x00100000) /* bit 20 */
|
||||
#define CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK (0x00200000) /* bit 21 */
|
||||
|
||||
/* CSR GIO */
|
||||
#define CSR_GIO_REG_VAL_L0S_ENABLED (0x00000002)
|
||||
|
@ -251,6 +260,11 @@
|
|||
|
||||
/* HPET MEM debug */
|
||||
#define CSR_DBG_HPET_MEM_REG_VAL (0xFFFF0000)
|
||||
|
||||
/* DRAM INT TABLE */
|
||||
#define CSR_DRAM_INT_TBL_ENABLE (1 << 31)
|
||||
#define CSR_DRAM_INIT_TBL_WRAP_CHECK (1 << 27)
|
||||
|
||||
/*=== HBUS (Host-side Bus) ===*/
|
||||
#define HBUS_BASE (0x400)
|
||||
/*
|
||||
|
|
|
@ -68,7 +68,7 @@ struct iwl_debugfs {
|
|||
struct dentry *dir_rf;
|
||||
struct dir_data_files {
|
||||
struct dentry *file_sram;
|
||||
struct dentry *file_eeprom;
|
||||
struct dentry *file_nvm;
|
||||
struct dentry *file_stations;
|
||||
struct dentry *file_rx_statistics;
|
||||
struct dentry *file_tx_statistics;
|
||||
|
|
|
@ -172,7 +172,6 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file,
|
|||
struct iwl_priv *priv = (struct iwl_priv *)file->private_data;
|
||||
const size_t bufsz = sizeof(buf);
|
||||
|
||||
iwl_grab_nic_access(priv);
|
||||
for (i = priv->dbgfs->sram_len; i > 0; i -= 4) {
|
||||
val = iwl_read_targ_mem(priv, priv->dbgfs->sram_offset + \
|
||||
priv->dbgfs->sram_len - i);
|
||||
|
@ -192,7 +191,6 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file,
|
|||
pos += scnprintf(buf + pos, bufsz - pos, "0x%08x ", val);
|
||||
}
|
||||
pos += scnprintf(buf + pos, bufsz - pos, "\n");
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
|
||||
return ret;
|
||||
|
@ -292,7 +290,7 @@ static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t iwl_dbgfs_eeprom_read(struct file *file,
|
||||
static ssize_t iwl_dbgfs_nvm_read(struct file *file,
|
||||
char __user *user_buf,
|
||||
size_t count,
|
||||
loff_t *ppos)
|
||||
|
@ -306,7 +304,7 @@ static ssize_t iwl_dbgfs_eeprom_read(struct file *file,
|
|||
buf_size = 4 * eeprom_len + 256;
|
||||
|
||||
if (eeprom_len % 16) {
|
||||
IWL_ERR(priv, "EEPROM size is not multiple of 16.\n");
|
||||
IWL_ERR(priv, "NVM size is not multiple of 16.\n");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
|
@ -318,6 +316,13 @@ static ssize_t iwl_dbgfs_eeprom_read(struct file *file,
|
|||
}
|
||||
|
||||
ptr = priv->eeprom;
|
||||
if (!ptr) {
|
||||
IWL_ERR(priv, "Invalid EEPROM/OTP memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
pos += scnprintf(buf + pos, buf_size - pos, "NVM Type: %s\n",
|
||||
(priv->nvm_device_type == NVM_DEVICE_TYPE_OTP)
|
||||
? "OTP" : "EEPROM");
|
||||
for (ofs = 0 ; ofs < eeprom_len ; ofs += 16) {
|
||||
pos += scnprintf(buf + pos, buf_size - pos, "0x%.4x ", ofs);
|
||||
hex_dump_to_buffer(ptr + ofs, 16 , 16, 2, buf + pos,
|
||||
|
@ -375,51 +380,53 @@ static ssize_t iwl_dbgfs_channels_read(struct file *file, char __user *user_buf,
|
|||
}
|
||||
|
||||
supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_2GHZ);
|
||||
channels = supp_band->channels;
|
||||
if (supp_band) {
|
||||
channels = supp_band->channels;
|
||||
|
||||
pos += scnprintf(buf + pos, bufsz - pos,
|
||||
"Displaying %d channels in 2.4GHz band 802.11bg):\n",
|
||||
supp_band->n_channels);
|
||||
|
||||
for (i = 0; i < supp_band->n_channels; i++)
|
||||
pos += scnprintf(buf + pos, bufsz - pos,
|
||||
"%d: %ddBm: BSS%s%s, %s.\n",
|
||||
ieee80211_frequency_to_channel(
|
||||
channels[i].center_freq),
|
||||
channels[i].max_power,
|
||||
channels[i].flags & IEEE80211_CHAN_RADAR ?
|
||||
" (IEEE 802.11h required)" : "",
|
||||
((channels[i].flags & IEEE80211_CHAN_NO_IBSS)
|
||||
|| (channels[i].flags &
|
||||
IEEE80211_CHAN_RADAR)) ? "" :
|
||||
", IBSS",
|
||||
channels[i].flags &
|
||||
IEEE80211_CHAN_PASSIVE_SCAN ?
|
||||
"passive only" : "active/passive");
|
||||
"Displaying %d channels in 2.4GHz band 802.11bg):\n",
|
||||
supp_band->n_channels);
|
||||
|
||||
for (i = 0; i < supp_band->n_channels; i++)
|
||||
pos += scnprintf(buf + pos, bufsz - pos,
|
||||
"%d: %ddBm: BSS%s%s, %s.\n",
|
||||
ieee80211_frequency_to_channel(
|
||||
channels[i].center_freq),
|
||||
channels[i].max_power,
|
||||
channels[i].flags & IEEE80211_CHAN_RADAR ?
|
||||
" (IEEE 802.11h required)" : "",
|
||||
((channels[i].flags & IEEE80211_CHAN_NO_IBSS)
|
||||
|| (channels[i].flags &
|
||||
IEEE80211_CHAN_RADAR)) ? "" :
|
||||
", IBSS",
|
||||
channels[i].flags &
|
||||
IEEE80211_CHAN_PASSIVE_SCAN ?
|
||||
"passive only" : "active/passive");
|
||||
}
|
||||
supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_5GHZ);
|
||||
channels = supp_band->channels;
|
||||
if (supp_band) {
|
||||
channels = supp_band->channels;
|
||||
|
||||
pos += scnprintf(buf + pos, bufsz - pos,
|
||||
"Displaying %d channels in 5.2GHz band (802.11a)\n",
|
||||
supp_band->n_channels);
|
||||
|
||||
for (i = 0; i < supp_band->n_channels; i++)
|
||||
pos += scnprintf(buf + pos, bufsz - pos,
|
||||
"%d: %ddBm: BSS%s%s, %s.\n",
|
||||
ieee80211_frequency_to_channel(
|
||||
channels[i].center_freq),
|
||||
channels[i].max_power,
|
||||
channels[i].flags & IEEE80211_CHAN_RADAR ?
|
||||
" (IEEE 802.11h required)" : "",
|
||||
((channels[i].flags & IEEE80211_CHAN_NO_IBSS)
|
||||
|| (channels[i].flags &
|
||||
IEEE80211_CHAN_RADAR)) ? "" :
|
||||
", IBSS",
|
||||
channels[i].flags &
|
||||
IEEE80211_CHAN_PASSIVE_SCAN ?
|
||||
"passive only" : "active/passive");
|
||||
"Displaying %d channels in 5.2GHz band (802.11a)\n",
|
||||
supp_band->n_channels);
|
||||
|
||||
for (i = 0; i < supp_band->n_channels; i++)
|
||||
pos += scnprintf(buf + pos, bufsz - pos,
|
||||
"%d: %ddBm: BSS%s%s, %s.\n",
|
||||
ieee80211_frequency_to_channel(
|
||||
channels[i].center_freq),
|
||||
channels[i].max_power,
|
||||
channels[i].flags & IEEE80211_CHAN_RADAR ?
|
||||
" (IEEE 802.11h required)" : "",
|
||||
((channels[i].flags & IEEE80211_CHAN_NO_IBSS)
|
||||
|| (channels[i].flags &
|
||||
IEEE80211_CHAN_RADAR)) ? "" :
|
||||
", IBSS",
|
||||
channels[i].flags &
|
||||
IEEE80211_CHAN_PASSIVE_SCAN ?
|
||||
"passive only" : "active/passive");
|
||||
}
|
||||
ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
|
||||
kfree(buf);
|
||||
return ret;
|
||||
|
@ -564,7 +571,7 @@ static ssize_t iwl_dbgfs_interrupt_write(struct file *file,
|
|||
|
||||
DEBUGFS_READ_WRITE_FILE_OPS(sram);
|
||||
DEBUGFS_WRITE_FILE_OPS(log_event);
|
||||
DEBUGFS_READ_FILE_OPS(eeprom);
|
||||
DEBUGFS_READ_FILE_OPS(nvm);
|
||||
DEBUGFS_READ_FILE_OPS(stations);
|
||||
DEBUGFS_READ_FILE_OPS(rx_statistics);
|
||||
DEBUGFS_READ_FILE_OPS(tx_statistics);
|
||||
|
@ -598,7 +605,7 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name)
|
|||
|
||||
DEBUGFS_ADD_DIR(data, dbgfs->dir_drv);
|
||||
DEBUGFS_ADD_DIR(rf, dbgfs->dir_drv);
|
||||
DEBUGFS_ADD_FILE(eeprom, data);
|
||||
DEBUGFS_ADD_FILE(nvm, data);
|
||||
DEBUGFS_ADD_FILE(sram, data);
|
||||
DEBUGFS_ADD_FILE(log_event, data);
|
||||
DEBUGFS_ADD_FILE(stations, data);
|
||||
|
@ -629,7 +636,7 @@ void iwl_dbgfs_unregister(struct iwl_priv *priv)
|
|||
if (!priv->dbgfs)
|
||||
return;
|
||||
|
||||
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_eeprom);
|
||||
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_nvm);
|
||||
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_rx_statistics);
|
||||
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_tx_statistics);
|
||||
DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_sram);
|
||||
|
|
|
@ -382,6 +382,7 @@ struct iwl_rx_queue {
|
|||
u32 read;
|
||||
u32 write;
|
||||
u32 free_count;
|
||||
u32 write_actual;
|
||||
struct list_head rx_free;
|
||||
struct list_head rx_used;
|
||||
int need_update;
|
||||
|
@ -499,22 +500,13 @@ struct iwl_qos_info {
|
|||
#define STA_PS_STATUS_WAKE 0
|
||||
#define STA_PS_STATUS_SLEEP 1
|
||||
|
||||
struct iwl3945_tid_data {
|
||||
u16 seq_number;
|
||||
};
|
||||
|
||||
struct iwl3945_hw_key {
|
||||
enum ieee80211_key_alg alg;
|
||||
int keylen;
|
||||
u8 key[32];
|
||||
};
|
||||
|
||||
struct iwl3945_station_entry {
|
||||
struct iwl3945_addsta_cmd sta;
|
||||
struct iwl3945_tid_data tid[MAX_TID_COUNT];
|
||||
struct iwl_tid_data tid[MAX_TID_COUNT];
|
||||
u8 used;
|
||||
u8 ps_status;
|
||||
struct iwl3945_hw_key keyinfo;
|
||||
struct iwl_hw_key keyinfo;
|
||||
};
|
||||
|
||||
struct iwl_station_entry {
|
||||
|
@ -823,6 +815,11 @@ enum {
|
|||
MEASUREMENT_ACTIVE = (1 << 1),
|
||||
};
|
||||
|
||||
enum iwl_nvm_type {
|
||||
NVM_DEVICE_TYPE_EEPROM = 0,
|
||||
NVM_DEVICE_TYPE_OTP,
|
||||
};
|
||||
|
||||
/* interrupt statistics */
|
||||
struct isr_statistics {
|
||||
u32 hw;
|
||||
|
@ -900,6 +897,7 @@ struct iwl_priv {
|
|||
/* spinlock */
|
||||
spinlock_t lock; /* protect general shared data */
|
||||
spinlock_t hcmd_lock; /* protect hcmd */
|
||||
spinlock_t reg_lock; /* protect hw register access */
|
||||
struct mutex mutex;
|
||||
|
||||
/* basic pci-network driver stuff */
|
||||
|
@ -1033,6 +1031,7 @@ struct iwl_priv {
|
|||
|
||||
/* eeprom */
|
||||
u8 *eeprom;
|
||||
int nvm_device_type;
|
||||
struct iwl_eeprom_calib_info *calib_info;
|
||||
|
||||
enum nl80211_iftype iw_mode;
|
||||
|
@ -1050,7 +1049,16 @@ struct iwl_priv {
|
|||
/*End*/
|
||||
struct iwl_hw_params hw_params;
|
||||
|
||||
/* INT ICT Table */
|
||||
u32 *ict_tbl;
|
||||
dma_addr_t ict_tbl_dma;
|
||||
dma_addr_t aligned_ict_tbl_dma;
|
||||
int ict_index;
|
||||
void *ict_tbl_vir;
|
||||
u32 inta;
|
||||
bool use_ict;
|
||||
|
||||
u32 inta_mask;
|
||||
/* Current association information needed to configure the
|
||||
* hardware */
|
||||
u16 assoc_id;
|
||||
|
@ -1105,7 +1113,7 @@ struct iwl_priv {
|
|||
u32 disable_tx_power_cal;
|
||||
struct work_struct run_time_calib_work;
|
||||
struct timer_list statistics_periodic;
|
||||
|
||||
bool hw_ready;
|
||||
/*For 3945*/
|
||||
#define IWL_DEFAULT_TX_POWER 0x0F
|
||||
|
||||
|
|
|
@ -152,6 +152,32 @@ int iwlcore_eeprom_verify_signature(struct iwl_priv *priv)
|
|||
}
|
||||
EXPORT_SYMBOL(iwlcore_eeprom_verify_signature);
|
||||
|
||||
static int iwlcore_get_nvm_type(struct iwl_priv *priv)
|
||||
{
|
||||
u32 otpgp;
|
||||
int nvm_type;
|
||||
|
||||
/* OTP only valid for CP/PP and after */
|
||||
switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) {
|
||||
case CSR_HW_REV_TYPE_3945:
|
||||
case CSR_HW_REV_TYPE_4965:
|
||||
case CSR_HW_REV_TYPE_5300:
|
||||
case CSR_HW_REV_TYPE_5350:
|
||||
case CSR_HW_REV_TYPE_5100:
|
||||
case CSR_HW_REV_TYPE_5150:
|
||||
nvm_type = NVM_DEVICE_TYPE_EEPROM;
|
||||
break;
|
||||
default:
|
||||
otpgp = iwl_read32(priv, CSR_OTP_GP_REG);
|
||||
if (otpgp & CSR_OTP_GP_REG_DEVICE_SELECT)
|
||||
nvm_type = NVM_DEVICE_TYPE_OTP;
|
||||
else
|
||||
nvm_type = NVM_DEVICE_TYPE_EEPROM;
|
||||
break;
|
||||
}
|
||||
return nvm_type;
|
||||
}
|
||||
|
||||
/*
|
||||
* The device's EEPROM semaphore prevents conflicts between driver and uCode
|
||||
* when accessing the EEPROM; each access is a series of pulses to/from the
|
||||
|
@ -198,6 +224,33 @@ const u8 *iwlcore_eeprom_query_addr(const struct iwl_priv *priv, size_t offset)
|
|||
}
|
||||
EXPORT_SYMBOL(iwlcore_eeprom_query_addr);
|
||||
|
||||
static int iwl_init_otp_access(struct iwl_priv *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Enable 40MHz radio clock */
|
||||
_iwl_write32(priv, CSR_GP_CNTRL,
|
||||
_iwl_read32(priv, CSR_GP_CNTRL) |
|
||||
CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
|
||||
|
||||
/* wait for clock to be ready */
|
||||
ret = iwl_poll_direct_bit(priv, CSR_GP_CNTRL,
|
||||
CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
|
||||
25000);
|
||||
if (ret < 0)
|
||||
IWL_ERR(priv, "Time out access OTP\n");
|
||||
else {
|
||||
if (!ret) {
|
||||
iwl_set_bits_prph(priv, APMG_PS_CTRL_REG,
|
||||
APMG_PS_CTRL_VAL_RESET_REQ);
|
||||
udelay(5);
|
||||
iwl_clear_bits_prph(priv, APMG_PS_CTRL_REG,
|
||||
APMG_PS_CTRL_VAL_RESET_REQ);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* iwl_eeprom_init - read EEPROM contents
|
||||
*
|
||||
|
@ -209,11 +262,18 @@ int iwl_eeprom_init(struct iwl_priv *priv)
|
|||
{
|
||||
u16 *e;
|
||||
u32 gp = iwl_read32(priv, CSR_EEPROM_GP);
|
||||
int sz = priv->cfg->eeprom_size;
|
||||
int sz;
|
||||
int ret;
|
||||
u16 addr;
|
||||
u32 otpgp;
|
||||
|
||||
priv->nvm_device_type = iwlcore_get_nvm_type(priv);
|
||||
|
||||
/* allocate eeprom */
|
||||
if (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP)
|
||||
priv->cfg->eeprom_size =
|
||||
OTP_BLOCK_SIZE * OTP_LOWER_BLOCKS_TOTAL;
|
||||
sz = priv->cfg->eeprom_size;
|
||||
priv->eeprom = kzalloc(sz, GFP_KERNEL);
|
||||
if (!priv->eeprom) {
|
||||
ret = -ENOMEM;
|
||||
|
@ -235,30 +295,77 @@ int iwl_eeprom_init(struct iwl_priv *priv)
|
|||
ret = -ENOENT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* eeprom is an array of 16bit values */
|
||||
for (addr = 0; addr < sz; addr += sizeof(u16)) {
|
||||
u32 r;
|
||||
|
||||
_iwl_write32(priv, CSR_EEPROM_REG,
|
||||
CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
|
||||
|
||||
ret = iwl_poll_direct_bit(priv, CSR_EEPROM_REG,
|
||||
CSR_EEPROM_REG_READ_VALID_MSK,
|
||||
IWL_EEPROM_ACCESS_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
IWL_ERR(priv, "Time out reading EEPROM[%d]\n", addr);
|
||||
goto done;
|
||||
if (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) {
|
||||
ret = iwl_init_otp_access(priv);
|
||||
if (ret) {
|
||||
IWL_ERR(priv, "Failed to initialize OTP access.\n");
|
||||
ret = -ENOENT;
|
||||
goto err;
|
||||
}
|
||||
_iwl_write32(priv, CSR_EEPROM_GP,
|
||||
iwl_read32(priv, CSR_EEPROM_GP) &
|
||||
~CSR_EEPROM_GP_IF_OWNER_MSK);
|
||||
/* clear */
|
||||
_iwl_write32(priv, CSR_OTP_GP_REG,
|
||||
iwl_read32(priv, CSR_OTP_GP_REG) |
|
||||
CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK |
|
||||
CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK);
|
||||
|
||||
for (addr = 0; addr < sz; addr += sizeof(u16)) {
|
||||
u32 r;
|
||||
|
||||
_iwl_write32(priv, CSR_EEPROM_REG,
|
||||
CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
|
||||
|
||||
ret = iwl_poll_direct_bit(priv, CSR_EEPROM_REG,
|
||||
CSR_EEPROM_REG_READ_VALID_MSK,
|
||||
IWL_EEPROM_ACCESS_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
IWL_ERR(priv, "Time out reading OTP[%d]\n", addr);
|
||||
goto done;
|
||||
}
|
||||
r = _iwl_read_direct32(priv, CSR_EEPROM_REG);
|
||||
/* check for ECC errors: */
|
||||
otpgp = iwl_read32(priv, CSR_OTP_GP_REG);
|
||||
if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) {
|
||||
/* stop in this case */
|
||||
IWL_ERR(priv, "Uncorrectable OTP ECC error, Abort OTP read\n");
|
||||
goto done;
|
||||
}
|
||||
if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) {
|
||||
/* continue in this case */
|
||||
_iwl_write32(priv, CSR_OTP_GP_REG,
|
||||
iwl_read32(priv, CSR_OTP_GP_REG) |
|
||||
CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK);
|
||||
IWL_ERR(priv, "Correctable OTP ECC error, continue read\n");
|
||||
}
|
||||
e[addr / 2] = le16_to_cpu((__force __le16)(r >> 16));
|
||||
}
|
||||
} else {
|
||||
/* eeprom is an array of 16bit values */
|
||||
for (addr = 0; addr < sz; addr += sizeof(u16)) {
|
||||
u32 r;
|
||||
|
||||
_iwl_write32(priv, CSR_EEPROM_REG,
|
||||
CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
|
||||
|
||||
ret = iwl_poll_direct_bit(priv, CSR_EEPROM_REG,
|
||||
CSR_EEPROM_REG_READ_VALID_MSK,
|
||||
IWL_EEPROM_ACCESS_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
IWL_ERR(priv, "Time out reading EEPROM[%d]\n", addr);
|
||||
goto done;
|
||||
}
|
||||
r = _iwl_read_direct32(priv, CSR_EEPROM_REG);
|
||||
e[addr / 2] = le16_to_cpu((__force __le16)(r >> 16));
|
||||
}
|
||||
r = _iwl_read_direct32(priv, CSR_EEPROM_REG);
|
||||
e[addr / 2] = le16_to_cpu((__force __le16)(r >> 16));
|
||||
}
|
||||
ret = 0;
|
||||
done:
|
||||
priv->cfg->ops->lib->eeprom_ops.release_semaphore(priv);
|
||||
err:
|
||||
if (ret)
|
||||
kfree(priv->eeprom);
|
||||
iwl_eeprom_free(priv);
|
||||
alloc_err:
|
||||
return ret;
|
||||
}
|
||||
|
@ -301,6 +408,8 @@ EXPORT_SYMBOL(iwl_eeprom_query_addr);
|
|||
|
||||
u16 iwl_eeprom_query16(const struct iwl_priv *priv, size_t offset)
|
||||
{
|
||||
if (!priv->eeprom)
|
||||
return 0;
|
||||
return (u16)priv->eeprom[offset] | ((u16)priv->eeprom[offset + 1] << 8);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_eeprom_query16);
|
||||
|
@ -481,8 +590,8 @@ int iwl_init_channel_map(struct iwl_priv *priv)
|
|||
/* First write that fat is not enabled, and then enable
|
||||
* one by one */
|
||||
ch_info->fat_extension_channel =
|
||||
(IEEE80211_CHAN_NO_FAT_ABOVE |
|
||||
IEEE80211_CHAN_NO_FAT_BELOW);
|
||||
(IEEE80211_CHAN_NO_HT40PLUS |
|
||||
IEEE80211_CHAN_NO_HT40MINUS);
|
||||
|
||||
if (!(is_channel_valid(ch_info))) {
|
||||
IWL_DEBUG_INFO(priv, "Ch. %d Flags %x [%sGHz] - "
|
||||
|
@ -561,7 +670,7 @@ int iwl_init_channel_map(struct iwl_priv *priv)
|
|||
fat_extension_chan = 0;
|
||||
else
|
||||
fat_extension_chan =
|
||||
IEEE80211_CHAN_NO_FAT_BELOW;
|
||||
IEEE80211_CHAN_NO_HT40MINUS;
|
||||
|
||||
/* Set up driver's info for lower half */
|
||||
iwl_set_fat_chan_info(priv, ieeeband,
|
||||
|
@ -573,7 +682,7 @@ int iwl_init_channel_map(struct iwl_priv *priv)
|
|||
iwl_set_fat_chan_info(priv, ieeeband,
|
||||
(eeprom_ch_index[ch] + 4),
|
||||
&(eeprom_ch_info[ch]),
|
||||
IEEE80211_CHAN_NO_FAT_ABOVE);
|
||||
IEEE80211_CHAN_NO_HT40PLUS);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -179,6 +179,10 @@ struct iwl_eeprom_channel {
|
|||
#define EEPROM_5050_TX_POWER_VERSION (4)
|
||||
#define EEPROM_5050_EEPROM_VERSION (0x21E)
|
||||
|
||||
/* OTP */
|
||||
#define OTP_LOWER_BLOCKS_TOTAL (3)
|
||||
#define OTP_BLOCK_SIZE (0x400)
|
||||
|
||||
/* 2.4 GHz */
|
||||
extern const u8 iwl_eeprom_band_1[14];
|
||||
|
||||
|
|
|
@ -131,9 +131,23 @@ static inline void __iwl_set_bit(const char *f, u32 l,
|
|||
IWL_DEBUG_IO(priv, "set_bit(0x%08X, 0x%08X) = 0x%08X\n", reg, mask, val);
|
||||
_iwl_write32(priv, reg, val);
|
||||
}
|
||||
#define iwl_set_bit(p, r, m) __iwl_set_bit(__FILE__, __LINE__, p, r, m)
|
||||
static inline void iwl_set_bit(struct iwl_priv *p, u32 r, u32 m)
|
||||
{
|
||||
unsigned long reg_flags;
|
||||
|
||||
spin_lock_irqsave(&p->reg_lock, reg_flags);
|
||||
__iwl_set_bit(__FILE__, __LINE__, p, r, m);
|
||||
spin_unlock_irqrestore(&p->reg_lock, reg_flags);
|
||||
}
|
||||
#else
|
||||
#define iwl_set_bit(p, r, m) _iwl_set_bit(p, r, m)
|
||||
static inline void iwl_set_bit(struct iwl_priv *p, u32 r, u32 m)
|
||||
{
|
||||
unsigned long reg_flags;
|
||||
|
||||
spin_lock_irqsave(&p->reg_lock, reg_flags);
|
||||
_iwl_set_bit(p, r, m);
|
||||
spin_unlock_irqrestore(&p->reg_lock, reg_flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void _iwl_clear_bit(struct iwl_priv *priv, u32 reg, u32 mask)
|
||||
|
@ -148,19 +162,30 @@ static inline void __iwl_clear_bit(const char *f, u32 l,
|
|||
IWL_DEBUG_IO(priv, "clear_bit(0x%08X, 0x%08X) = 0x%08X\n", reg, mask, val);
|
||||
_iwl_write32(priv, reg, val);
|
||||
}
|
||||
#define iwl_clear_bit(p, r, m) __iwl_clear_bit(__FILE__, __LINE__, p, r, m)
|
||||
static inline void iwl_clear_bit(struct iwl_priv *p, u32 r, u32 m)
|
||||
{
|
||||
unsigned long reg_flags;
|
||||
|
||||
spin_lock_irqsave(&p->reg_lock, reg_flags);
|
||||
__iwl_clear_bit(__FILE__, __LINE__, p, r, m);
|
||||
spin_unlock_irqrestore(&p->reg_lock, reg_flags);
|
||||
}
|
||||
#else
|
||||
#define iwl_clear_bit(p, r, m) _iwl_clear_bit(p, r, m)
|
||||
static inline void iwl_clear_bit(struct iwl_priv *p, u32 r, u32 m)
|
||||
{
|
||||
unsigned long reg_flags;
|
||||
|
||||
spin_lock_irqsave(&p->reg_lock, reg_flags);
|
||||
_iwl_clear_bit(p, r, m);
|
||||
spin_unlock_irqrestore(&p->reg_lock, reg_flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int _iwl_grab_nic_access(struct iwl_priv *priv)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
if (atomic_read(&priv->restrict_refcnt))
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
/* this bit wakes up the NIC */
|
||||
_iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
|
||||
ret = _iwl_poll_bit(priv, CSR_GP_CNTRL,
|
||||
|
@ -170,12 +195,10 @@ static inline int _iwl_grab_nic_access(struct iwl_priv *priv)
|
|||
if (ret < 0) {
|
||||
val = _iwl_read32(priv, CSR_GP_CNTRL);
|
||||
IWL_ERR(priv, "MAC is in deep sleep!. CSR_GP_CNTRL = 0x%08X\n", val);
|
||||
_iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_FORCE_NMI);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
atomic_inc(&priv->restrict_refcnt);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -183,9 +206,6 @@ static inline int _iwl_grab_nic_access(struct iwl_priv *priv)
|
|||
static inline int __iwl_grab_nic_access(const char *f, u32 l,
|
||||
struct iwl_priv *priv)
|
||||
{
|
||||
if (atomic_read(&priv->restrict_refcnt))
|
||||
IWL_ERR(priv, "Grabbing access while already held %s %d.\n", f, l);
|
||||
|
||||
IWL_DEBUG_IO(priv, "grabbing nic access - %s %d\n", f, l);
|
||||
return _iwl_grab_nic_access(priv);
|
||||
}
|
||||
|
@ -198,18 +218,13 @@ static inline int __iwl_grab_nic_access(const char *f, u32 l,
|
|||
|
||||
static inline void _iwl_release_nic_access(struct iwl_priv *priv)
|
||||
{
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
if (atomic_dec_and_test(&priv->restrict_refcnt))
|
||||
#endif
|
||||
_iwl_clear_bit(priv, CSR_GP_CNTRL,
|
||||
CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
|
||||
_iwl_clear_bit(priv, CSR_GP_CNTRL,
|
||||
CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
|
||||
}
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
static inline void __iwl_release_nic_access(const char *f, u32 l,
|
||||
struct iwl_priv *priv)
|
||||
{
|
||||
if (atomic_read(&priv->restrict_refcnt) <= 0)
|
||||
IWL_ERR(priv, "Release unheld nic access at line %s %d.\n", f, l);
|
||||
|
||||
IWL_DEBUG_IO(priv, "releasing nic access - %s %d\n", f, l);
|
||||
_iwl_release_nic_access(priv);
|
||||
|
@ -230,16 +245,37 @@ static inline u32 __iwl_read_direct32(const char *f, u32 l,
|
|||
struct iwl_priv *priv, u32 reg)
|
||||
{
|
||||
u32 value = _iwl_read_direct32(priv, reg);
|
||||
if (!atomic_read(&priv->restrict_refcnt))
|
||||
IWL_ERR(priv, "Nic access not held from %s %d\n", f, l);
|
||||
IWL_DEBUG_IO(priv, "read_direct32(0x%4X) = 0x%08x - %s %d \n", reg, value,
|
||||
f, l);
|
||||
return value;
|
||||
}
|
||||
#define iwl_read_direct32(priv, reg) \
|
||||
__iwl_read_direct32(__FILE__, __LINE__, priv, reg)
|
||||
static inline u32 iwl_read_direct32(struct iwl_priv *priv, u32 reg)
|
||||
{
|
||||
u32 value;
|
||||
unsigned long reg_flags;
|
||||
|
||||
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
||||
iwl_grab_nic_access(priv);
|
||||
value = __iwl_read_direct32(__FILE__, __LINE__, priv, reg);
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
||||
return value;
|
||||
}
|
||||
|
||||
#else
|
||||
#define iwl_read_direct32 _iwl_read_direct32
|
||||
static inline u32 iwl_read_direct32(struct iwl_priv *priv, u32 reg)
|
||||
{
|
||||
u32 value;
|
||||
unsigned long reg_flags;
|
||||
|
||||
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
||||
iwl_grab_nic_access(priv);
|
||||
value = _iwl_read_direct32(priv, reg);
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
||||
return value;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void _iwl_write_direct32(struct iwl_priv *priv,
|
||||
|
@ -247,19 +283,17 @@ static inline void _iwl_write_direct32(struct iwl_priv *priv,
|
|||
{
|
||||
_iwl_write32(priv, reg, value);
|
||||
}
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
static void __iwl_write_direct32(const char *f , u32 line,
|
||||
struct iwl_priv *priv, u32 reg, u32 value)
|
||||
static inline void iwl_write_direct32(struct iwl_priv *priv, u32 reg, u32 value)
|
||||
{
|
||||
if (!atomic_read(&priv->restrict_refcnt))
|
||||
IWL_ERR(priv, "Nic access not held from %s line %d\n", f, line);
|
||||
_iwl_write_direct32(priv, reg, value);
|
||||
unsigned long reg_flags;
|
||||
|
||||
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
||||
if (!iwl_grab_nic_access(priv)) {
|
||||
_iwl_write_direct32(priv, reg, value);
|
||||
iwl_release_nic_access(priv);
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
||||
}
|
||||
#define iwl_write_direct32(priv, reg, value) \
|
||||
__iwl_write_direct32(__func__, __LINE__, priv, reg, value)
|
||||
#else
|
||||
#define iwl_write_direct32 _iwl_write_direct32
|
||||
#endif
|
||||
|
||||
static inline void iwl_write_reg_buf(struct iwl_priv *priv,
|
||||
u32 reg, u32 len, u32 *values)
|
||||
|
@ -268,14 +302,23 @@ static inline void iwl_write_reg_buf(struct iwl_priv *priv,
|
|||
|
||||
if ((priv != NULL) && (values != NULL)) {
|
||||
for (; 0 < len; len -= count, reg += count, values++)
|
||||
_iwl_write_direct32(priv, reg, *values);
|
||||
iwl_write_direct32(priv, reg, *values);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int _iwl_poll_direct_bit(struct iwl_priv *priv, u32 addr,
|
||||
u32 mask, int timeout)
|
||||
{
|
||||
return _iwl_poll_bit(priv, addr, mask, mask, timeout);
|
||||
int t = 0;
|
||||
|
||||
do {
|
||||
if ((iwl_read_direct32(priv, addr) & mask) == mask)
|
||||
return t;
|
||||
udelay(IWL_POLL_INTERVAL);
|
||||
t += IWL_POLL_INTERVAL;
|
||||
} while (t < timeout);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
|
@ -305,20 +348,18 @@ static inline u32 _iwl_read_prph(struct iwl_priv *priv, u32 reg)
|
|||
rmb();
|
||||
return _iwl_read_direct32(priv, HBUS_TARG_PRPH_RDAT);
|
||||
}
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
static inline u32 __iwl_read_prph(const char *f, u32 line,
|
||||
struct iwl_priv *priv, u32 reg)
|
||||
static inline u32 iwl_read_prph(struct iwl_priv *priv, u32 reg)
|
||||
{
|
||||
if (!atomic_read(&priv->restrict_refcnt))
|
||||
IWL_ERR(priv, "Nic access not held from %s line %d\n", f, line);
|
||||
return _iwl_read_prph(priv, reg);
|
||||
}
|
||||
unsigned long reg_flags;
|
||||
u32 val;
|
||||
|
||||
#define iwl_read_prph(priv, reg) \
|
||||
__iwl_read_prph(__func__, __LINE__, priv, reg)
|
||||
#else
|
||||
#define iwl_read_prph _iwl_read_prph
|
||||
#endif
|
||||
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
||||
iwl_grab_nic_access(priv);
|
||||
val = _iwl_read_prph(priv, reg);
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void _iwl_write_prph(struct iwl_priv *priv,
|
||||
u32 addr, u32 val)
|
||||
|
@ -328,83 +369,107 @@ static inline void _iwl_write_prph(struct iwl_priv *priv,
|
|||
wmb();
|
||||
_iwl_write_direct32(priv, HBUS_TARG_PRPH_WDAT, val);
|
||||
}
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
static inline void __iwl_write_prph(const char *f, u32 line,
|
||||
struct iwl_priv *priv, u32 addr, u32 val)
|
||||
{
|
||||
if (!atomic_read(&priv->restrict_refcnt))
|
||||
IWL_ERR(priv, "Nic access not held from %s line %d\n", f, line);
|
||||
_iwl_write_prph(priv, addr, val);
|
||||
}
|
||||
|
||||
#define iwl_write_prph(priv, addr, val) \
|
||||
__iwl_write_prph(__func__, __LINE__, priv, addr, val);
|
||||
#else
|
||||
#define iwl_write_prph _iwl_write_prph
|
||||
#endif
|
||||
static inline void iwl_write_prph(struct iwl_priv *priv, u32 addr, u32 val)
|
||||
{
|
||||
unsigned long reg_flags;
|
||||
|
||||
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
||||
if (!iwl_grab_nic_access(priv)) {
|
||||
_iwl_write_prph(priv, addr, val);
|
||||
iwl_release_nic_access(priv);
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
||||
}
|
||||
|
||||
#define _iwl_set_bits_prph(priv, reg, mask) \
|
||||
_iwl_write_prph(priv, reg, (_iwl_read_prph(priv, reg) | mask))
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
static inline void __iwl_set_bits_prph(const char *f, u32 line,
|
||||
struct iwl_priv *priv,
|
||||
u32 reg, u32 mask)
|
||||
{
|
||||
if (!atomic_read(&priv->restrict_refcnt))
|
||||
IWL_ERR(priv, "Nic access not held from %s line %d\n", f, line);
|
||||
|
||||
static inline void iwl_set_bits_prph(struct iwl_priv *priv, u32 reg, u32 mask)
|
||||
{
|
||||
unsigned long reg_flags;
|
||||
|
||||
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
||||
iwl_grab_nic_access(priv);
|
||||
_iwl_set_bits_prph(priv, reg, mask);
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
||||
}
|
||||
#define iwl_set_bits_prph(priv, reg, mask) \
|
||||
__iwl_set_bits_prph(__func__, __LINE__, priv, reg, mask)
|
||||
#else
|
||||
#define iwl_set_bits_prph _iwl_set_bits_prph
|
||||
#endif
|
||||
|
||||
#define _iwl_set_bits_mask_prph(priv, reg, bits, mask) \
|
||||
_iwl_write_prph(priv, reg, ((_iwl_read_prph(priv, reg) & mask) | bits))
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
static inline void __iwl_set_bits_mask_prph(const char *f, u32 line,
|
||||
struct iwl_priv *priv, u32 reg, u32 bits, u32 mask)
|
||||
static inline void iwl_set_bits_mask_prph(struct iwl_priv *priv, u32 reg,
|
||||
u32 bits, u32 mask)
|
||||
{
|
||||
if (!atomic_read(&priv->restrict_refcnt))
|
||||
IWL_ERR(priv, "Nic access not held from %s line %d\n", f, line);
|
||||
unsigned long reg_flags;
|
||||
|
||||
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
||||
iwl_grab_nic_access(priv);
|
||||
_iwl_set_bits_mask_prph(priv, reg, bits, mask);
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
||||
}
|
||||
#define iwl_set_bits_mask_prph(priv, reg, bits, mask) \
|
||||
__iwl_set_bits_mask_prph(__func__, __LINE__, priv, reg, bits, mask)
|
||||
#else
|
||||
#define iwl_set_bits_mask_prph _iwl_set_bits_mask_prph
|
||||
#endif
|
||||
|
||||
static inline void iwl_clear_bits_prph(struct iwl_priv
|
||||
*priv, u32 reg, u32 mask)
|
||||
{
|
||||
u32 val = _iwl_read_prph(priv, reg);
|
||||
unsigned long reg_flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
||||
iwl_grab_nic_access(priv);
|
||||
val = _iwl_read_prph(priv, reg);
|
||||
_iwl_write_prph(priv, reg, (val & ~mask));
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
||||
}
|
||||
|
||||
static inline u32 iwl_read_targ_mem(struct iwl_priv *priv, u32 addr)
|
||||
{
|
||||
iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR, addr);
|
||||
unsigned long reg_flags;
|
||||
u32 value;
|
||||
|
||||
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
||||
iwl_grab_nic_access(priv);
|
||||
|
||||
_iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR, addr);
|
||||
rmb();
|
||||
return iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
|
||||
value = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline void iwl_write_targ_mem(struct iwl_priv *priv, u32 addr, u32 val)
|
||||
{
|
||||
iwl_write_direct32(priv, HBUS_TARG_MEM_WADDR, addr);
|
||||
wmb();
|
||||
iwl_write_direct32(priv, HBUS_TARG_MEM_WDAT, val);
|
||||
unsigned long reg_flags;
|
||||
|
||||
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
||||
if (!iwl_grab_nic_access(priv)) {
|
||||
_iwl_write_direct32(priv, HBUS_TARG_MEM_WADDR, addr);
|
||||
wmb();
|
||||
_iwl_write_direct32(priv, HBUS_TARG_MEM_WDAT, val);
|
||||
iwl_release_nic_access(priv);
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
||||
}
|
||||
|
||||
static inline void iwl_write_targ_mem_buf(struct iwl_priv *priv, u32 addr,
|
||||
u32 len, u32 *values)
|
||||
{
|
||||
iwl_write_direct32(priv, HBUS_TARG_MEM_WADDR, addr);
|
||||
wmb();
|
||||
for (; 0 < len; len -= sizeof(u32), values++)
|
||||
iwl_write_direct32(priv, HBUS_TARG_MEM_WDAT, *values);
|
||||
unsigned long reg_flags;
|
||||
|
||||
spin_lock_irqsave(&priv->reg_lock, reg_flags);
|
||||
if (!iwl_grab_nic_access(priv)) {
|
||||
_iwl_write_direct32(priv, HBUS_TARG_MEM_WADDR, addr);
|
||||
wmb();
|
||||
for (; 0 < len; len -= sizeof(u32), values++)
|
||||
_iwl_write_direct32(priv, HBUS_TARG_MEM_WDAT, *values);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->reg_lock, reg_flags);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -145,18 +145,14 @@ int iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, struct iwl_rx_queue *q)
|
|||
goto exit_unlock;
|
||||
}
|
||||
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret)
|
||||
goto exit_unlock;
|
||||
|
||||
/* Device expects a multiple of 8 */
|
||||
iwl_write_direct32(priv, rx_wrt_ptr_reg, q->write & ~0x7);
|
||||
iwl_release_nic_access(priv);
|
||||
q->write_actual = (q->write & ~0x7);
|
||||
iwl_write_direct32(priv, rx_wrt_ptr_reg, q->write_actual);
|
||||
|
||||
/* Else device is assumed to be awake */
|
||||
} else {
|
||||
/* Device expects a multiple of 8 */
|
||||
iwl_write32(priv, rx_wrt_ptr_reg, q->write & ~0x7);
|
||||
q->write_actual = (q->write & ~0x7);
|
||||
iwl_write_direct32(priv, rx_wrt_ptr_reg, q->write_actual);
|
||||
}
|
||||
|
||||
q->need_update = 0;
|
||||
|
@ -218,7 +214,7 @@ int iwl_rx_queue_restock(struct iwl_priv *priv)
|
|||
|
||||
/* If we've added more space for the firmware to place data, tell it.
|
||||
* Increment device's write pointer in multiples of 8. */
|
||||
if (write != (rxq->write & ~0x7)) {
|
||||
if (rxq->write_actual != (rxq->write & ~0x7)) {
|
||||
spin_lock_irqsave(&rxq->lock, flags);
|
||||
rxq->need_update = 1;
|
||||
spin_unlock_irqrestore(&rxq->lock, flags);
|
||||
|
@ -238,7 +234,7 @@ EXPORT_SYMBOL(iwl_rx_queue_restock);
|
|||
* Also restock the Rx queue via iwl_rx_queue_restock.
|
||||
* This is called as a scheduled work item (except for during initialization)
|
||||
*/
|
||||
void iwl_rx_allocate(struct iwl_priv *priv)
|
||||
void iwl_rx_allocate(struct iwl_priv *priv, gfp_t priority)
|
||||
{
|
||||
struct iwl_rx_queue *rxq = &priv->rxq;
|
||||
struct list_head *element;
|
||||
|
@ -260,7 +256,8 @@ void iwl_rx_allocate(struct iwl_priv *priv)
|
|||
|
||||
/* Alloc a new receive buffer */
|
||||
rxb->skb = alloc_skb(priv->hw_params.rx_buf_size + 256,
|
||||
GFP_KERNEL);
|
||||
priority);
|
||||
|
||||
if (!rxb->skb) {
|
||||
IWL_CRIT(priv, "Can not allocate SKB buffers\n");
|
||||
/* We don't reschedule replenish work here -- we will
|
||||
|
@ -295,7 +292,7 @@ void iwl_rx_replenish(struct iwl_priv *priv)
|
|||
{
|
||||
unsigned long flags;
|
||||
|
||||
iwl_rx_allocate(priv);
|
||||
iwl_rx_allocate(priv, GFP_KERNEL);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
iwl_rx_queue_restock(priv);
|
||||
|
@ -303,6 +300,14 @@ void iwl_rx_replenish(struct iwl_priv *priv)
|
|||
}
|
||||
EXPORT_SYMBOL(iwl_rx_replenish);
|
||||
|
||||
void iwl_rx_replenish_now(struct iwl_priv *priv)
|
||||
{
|
||||
iwl_rx_allocate(priv, GFP_ATOMIC);
|
||||
|
||||
iwl_rx_queue_restock(priv);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_rx_replenish_now);
|
||||
|
||||
|
||||
/* Assumes that the skb field of the buffers in 'pool' is kept accurate.
|
||||
* If an SKB has been detached, the POOL needs to have its SKB set to NULL
|
||||
|
@ -358,6 +363,7 @@ int iwl_rx_queue_alloc(struct iwl_priv *priv)
|
|||
/* Set us so that we have processed and used all buffers, but have
|
||||
* not restocked the Rx queue with fresh buffers */
|
||||
rxq->read = rxq->write = 0;
|
||||
rxq->write_actual = 0;
|
||||
rxq->free_count = 0;
|
||||
rxq->need_update = 0;
|
||||
return 0;
|
||||
|
@ -396,6 +402,7 @@ void iwl_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
|
|||
/* Set us so that we have processed and used all buffers, but have
|
||||
* not restocked the Rx queue with fresh buffers */
|
||||
rxq->read = rxq->write = 0;
|
||||
rxq->write_actual = 0;
|
||||
rxq->free_count = 0;
|
||||
spin_unlock_irqrestore(&rxq->lock, flags);
|
||||
}
|
||||
|
@ -403,18 +410,12 @@ EXPORT_SYMBOL(iwl_rx_queue_reset);
|
|||
|
||||
int iwl_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
u32 rb_size;
|
||||
const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */
|
||||
const u32 rb_timeout = 0; /* FIXME: RX_RB_TIMEOUT why this stalls RX */
|
||||
u32 rb_timeout = 0; /* FIXME: RX_RB_TIMEOUT for all devices? */
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret) {
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
if (!priv->cfg->use_isr_legacy)
|
||||
rb_timeout = RX_RB_TIMEOUT;
|
||||
|
||||
if (priv->cfg->mod_params->amsdu_size_8K)
|
||||
rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K;
|
||||
|
@ -452,35 +453,19 @@ int iwl_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
|
|||
(rb_timeout << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)|
|
||||
(rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS));
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
iwl_write32(priv, CSR_INT_COALESCING, 0x40);
|
||||
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iwl_rxq_stop(struct iwl_priv *priv)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (unlikely(ret)) {
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* stop Rx DMA */
|
||||
iwl_write_direct32(priv, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0);
|
||||
iwl_poll_direct_bit(priv, FH_MEM_RSSR_RX_STATUS_REG,
|
||||
FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_rxq_stop);
|
||||
|
@ -582,8 +567,8 @@ void iwl_rx_statistics(struct iwl_priv *priv,
|
|||
|
||||
iwl_leds_background(priv);
|
||||
|
||||
if (priv->cfg->ops->lib->temperature && change)
|
||||
priv->cfg->ops->lib->temperature(priv);
|
||||
if (priv->cfg->ops->lib->temp_ops.temperature && change)
|
||||
priv->cfg->ops->lib->temp_ops.temperature(priv);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_rx_statistics);
|
||||
|
||||
|
|
|
@ -586,6 +586,7 @@ static void iwl_bg_request_scan(struct work_struct *data)
|
|||
u8 rx_ant = priv->hw_params.valid_rx_ant;
|
||||
u8 rate;
|
||||
bool is_active = false;
|
||||
int chan_mod;
|
||||
|
||||
conf = ieee80211_get_hw_conf(priv->hw);
|
||||
|
||||
|
@ -703,7 +704,9 @@ static void iwl_bg_request_scan(struct work_struct *data)
|
|||
if (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) {
|
||||
band = IEEE80211_BAND_2GHZ;
|
||||
scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK;
|
||||
if (priv->active_rxon.flags & RXON_FLG_CHANNEL_MODE_PURE_40_MSK) {
|
||||
chan_mod = le32_to_cpu(priv->active_rxon.flags & RXON_FLG_CHANNEL_MODE_MSK)
|
||||
>> RXON_FLG_CHANNEL_MODE_POS;
|
||||
if (chan_mod == CHANNEL_MODE_PURE_40) {
|
||||
rate = IWL_RATE_6M_PLCP;
|
||||
} else {
|
||||
rate = IWL_RATE_1M_PLCP;
|
||||
|
|
|
@ -102,13 +102,8 @@ int iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* restore this queue's parameters in nic hardware. */
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
iwl_write_direct32(priv, HBUS_TARG_WRPTR,
|
||||
txq->q.write_ptr | (txq_id << 8));
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
/* else not in power-save mode, uCode will never sleep when we're
|
||||
* trying to tx (during RFKILL, we're not trying to tx). */
|
||||
|
@ -429,11 +424,6 @@ int iwl_txq_ctx_reset(struct iwl_priv *priv)
|
|||
goto error_kw;
|
||||
}
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
ret = iwl_grab_nic_access(priv);
|
||||
if (unlikely(ret)) {
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
goto error_reset;
|
||||
}
|
||||
|
||||
/* Turn off all Tx DMA fifos */
|
||||
priv->cfg->ops->lib->txq_set_sched(priv, 0);
|
||||
|
@ -441,7 +431,6 @@ int iwl_txq_ctx_reset(struct iwl_priv *priv)
|
|||
/* Tell NIC where to find the "keep warm" buffer */
|
||||
iwl_write_direct32(priv, FH_KW_MEM_ADDR_REG, priv->kw.dma >> 4);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
/* Alloc and init all Tx queues, including the command queue (#4) */
|
||||
|
@ -460,7 +449,6 @@ int iwl_txq_ctx_reset(struct iwl_priv *priv)
|
|||
|
||||
error:
|
||||
iwl_hw_txq_ctx_free(priv);
|
||||
error_reset:
|
||||
iwl_free_dma_ptr(priv, &priv->kw);
|
||||
error_kw:
|
||||
iwl_free_dma_ptr(priv, &priv->scd_bc_tbls);
|
||||
|
@ -478,10 +466,6 @@ void iwl_txq_ctx_stop(struct iwl_priv *priv)
|
|||
|
||||
/* Turn off all Tx DMA fifos */
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
if (iwl_grab_nic_access(priv)) {
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
priv->cfg->ops->lib->txq_set_sched(priv, 0);
|
||||
|
||||
|
@ -492,7 +476,6 @@ void iwl_txq_ctx_stop(struct iwl_priv *priv)
|
|||
FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch),
|
||||
1000);
|
||||
}
|
||||
iwl_release_nic_access(priv);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
/* Deallocate memory for all Tx queues */
|
||||
|
|
|
@ -340,7 +340,7 @@ static int iwl3945_clear_sta_key_info(struct iwl_priv *priv, u8 sta_id)
|
|||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->sta_lock, flags);
|
||||
memset(&priv->stations_39[sta_id].keyinfo, 0, sizeof(struct iwl3945_hw_key));
|
||||
memset(&priv->stations_39[sta_id].keyinfo, 0, sizeof(struct iwl_hw_key));
|
||||
memset(&priv->stations_39[sta_id].sta.key, 0,
|
||||
sizeof(struct iwl4965_keyinfo));
|
||||
priv->stations_39[sta_id].sta.key.key_flags = STA_KEY_FLG_NO_ENC;
|
||||
|
@ -578,8 +578,7 @@ static void iwl3945_build_tx_cmd_hwcrypto(struct iwl_priv *priv,
|
|||
int sta_id)
|
||||
{
|
||||
struct iwl3945_tx_cmd *tx = (struct iwl3945_tx_cmd *)cmd->cmd.payload;
|
||||
struct iwl3945_hw_key *keyinfo =
|
||||
&priv->stations_39[sta_id].keyinfo;
|
||||
struct iwl_hw_key *keyinfo = &priv->stations_39[sta_id].keyinfo;
|
||||
|
||||
switch (keyinfo->alg) {
|
||||
case ALG_CCMP:
|
||||
|
@ -1687,7 +1686,6 @@ static void iwl3945_dump_nic_error_log(struct iwl_priv *priv)
|
|||
u32 i;
|
||||
u32 desc, time, count, base, data1;
|
||||
u32 blink1, blink2, ilink1, ilink2;
|
||||
int rc;
|
||||
|
||||
base = le32_to_cpu(priv->card_alive.error_event_table_ptr);
|
||||
|
||||
|
@ -1696,11 +1694,6 @@ static void iwl3945_dump_nic_error_log(struct iwl_priv *priv)
|
|||
return;
|
||||
}
|
||||
|
||||
rc = iwl_grab_nic_access(priv);
|
||||
if (rc) {
|
||||
IWL_WARN(priv, "Can not read from adapter at this time.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
count = iwl_read_targ_mem(priv, base);
|
||||
|
||||
|
@ -1735,8 +1728,6 @@ static void iwl3945_dump_nic_error_log(struct iwl_priv *priv)
|
|||
ilink1, ilink2, data1);
|
||||
}
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
}
|
||||
|
||||
#define EVENT_START_OFFSET (6 * sizeof(u32))
|
||||
|
@ -1744,7 +1735,6 @@ static void iwl3945_dump_nic_error_log(struct iwl_priv *priv)
|
|||
/**
|
||||
* iwl3945_print_event_log - Dump error event log to syslog
|
||||
*
|
||||
* NOTE: Must be called with iwl_grab_nic_access() already obtained!
|
||||
*/
|
||||
static void iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx,
|
||||
u32 num_events, u32 mode)
|
||||
|
@ -1787,7 +1777,6 @@ static void iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx,
|
|||
|
||||
static void iwl3945_dump_nic_event_log(struct iwl_priv *priv)
|
||||
{
|
||||
int rc;
|
||||
u32 base; /* SRAM byte address of event log header */
|
||||
u32 capacity; /* event log capacity in # entries */
|
||||
u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */
|
||||
|
@ -1801,12 +1790,6 @@ static void iwl3945_dump_nic_event_log(struct iwl_priv *priv)
|
|||
return;
|
||||
}
|
||||
|
||||
rc = iwl_grab_nic_access(priv);
|
||||
if (rc) {
|
||||
IWL_WARN(priv, "Can not read from adapter at this time.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* event log header */
|
||||
capacity = iwl_read_targ_mem(priv, base);
|
||||
mode = iwl_read_targ_mem(priv, base + (1 * sizeof(u32)));
|
||||
|
@ -1818,7 +1801,6 @@ static void iwl3945_dump_nic_event_log(struct iwl_priv *priv)
|
|||
/* bail out if nothing in log */
|
||||
if (size == 0) {
|
||||
IWL_ERR(priv, "Start IWL Event Log Dump: nothing in log\n");
|
||||
iwl_release_nic_access(priv);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1834,7 +1816,6 @@ static void iwl3945_dump_nic_event_log(struct iwl_priv *priv)
|
|||
/* (then/else) start at top of log */
|
||||
iwl3945_print_event_log(priv, 0, next_entry, mode);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
}
|
||||
|
||||
static void iwl3945_irq_tasklet(struct iwl_priv *priv)
|
||||
|
@ -1953,11 +1934,8 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv)
|
|||
priv->isr_stats.tx++;
|
||||
|
||||
iwl_write32(priv, CSR_FH_INT_STATUS, (1 << 6));
|
||||
if (!iwl_grab_nic_access(priv)) {
|
||||
iwl_write_direct32(priv, FH39_TCSR_CREDIT
|
||||
(FH39_SRVC_CHNL), 0x0);
|
||||
iwl_release_nic_access(priv);
|
||||
}
|
||||
iwl_write_direct32(priv, FH39_TCSR_CREDIT
|
||||
(FH39_SRVC_CHNL), 0x0);
|
||||
handled |= CSR_INT_BIT_FH_TX;
|
||||
}
|
||||
|
||||
|
@ -1966,9 +1944,9 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv)
|
|||
priv->isr_stats.unhandled++;
|
||||
}
|
||||
|
||||
if (inta & ~CSR_INI_SET_MASK) {
|
||||
if (inta & ~priv->inta_mask) {
|
||||
IWL_WARN(priv, "Disabled INTA bits 0x%08x were pending\n",
|
||||
inta & ~CSR_INI_SET_MASK);
|
||||
inta & ~priv->inta_mask);
|
||||
IWL_WARN(priv, " with FH_INT = 0x%08x\n", inta_fh);
|
||||
}
|
||||
|
||||
|
@ -2132,10 +2110,6 @@ static int iwl3945_verify_inst_full(struct iwl_priv *priv, __le32 *image, u32 le
|
|||
|
||||
IWL_DEBUG_INFO(priv, "ucode inst image size is %u\n", len);
|
||||
|
||||
rc = iwl_grab_nic_access(priv);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR,
|
||||
IWL39_RTC_INST_LOWER_BOUND);
|
||||
|
||||
|
@ -2156,7 +2130,6 @@ static int iwl3945_verify_inst_full(struct iwl_priv *priv, __le32 *image, u32 le
|
|||
}
|
||||
}
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
if (!errcnt)
|
||||
IWL_DEBUG_INFO(priv,
|
||||
|
@ -2180,10 +2153,6 @@ static int iwl3945_verify_inst_sparse(struct iwl_priv *priv, __le32 *image, u32
|
|||
|
||||
IWL_DEBUG_INFO(priv, "ucode inst image size is %u\n", len);
|
||||
|
||||
rc = iwl_grab_nic_access(priv);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
for (i = 0; i < len; i += 100, image += 100/sizeof(u32)) {
|
||||
/* read data comes through single port, auto-incr addr */
|
||||
/* NOTE: Use the debugless read so we don't flood kernel log
|
||||
|
@ -2204,8 +2173,6 @@ static int iwl3945_verify_inst_sparse(struct iwl_priv *priv, __le32 *image, u32
|
|||
}
|
||||
}
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -2529,20 +2496,11 @@ static int iwl3945_set_ucode_ptrs(struct iwl_priv *priv)
|
|||
{
|
||||
dma_addr_t pinst;
|
||||
dma_addr_t pdata;
|
||||
int rc = 0;
|
||||
unsigned long flags;
|
||||
|
||||
/* bits 31:0 for 3945 */
|
||||
pinst = priv->ucode_code.p_addr;
|
||||
pdata = priv->ucode_data_backup.p_addr;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
rc = iwl_grab_nic_access(priv);
|
||||
if (rc) {
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Tell bootstrap uCode where to find image to load */
|
||||
iwl_write_prph(priv, BSM_DRAM_INST_PTR_REG, pinst);
|
||||
iwl_write_prph(priv, BSM_DRAM_DATA_PTR_REG, pdata);
|
||||
|
@ -2554,13 +2512,9 @@ static int iwl3945_set_ucode_ptrs(struct iwl_priv *priv)
|
|||
iwl_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG,
|
||||
priv->ucode_code.len | BSM_DRAM_INST_LOAD);
|
||||
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
IWL_DEBUG_INFO(priv, "Runtime uCode pointers are set.\n");
|
||||
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2613,7 +2567,6 @@ static void iwl3945_init_alive_start(struct iwl_priv *priv)
|
|||
*/
|
||||
static void iwl3945_alive_start(struct iwl_priv *priv)
|
||||
{
|
||||
int rc = 0;
|
||||
int thermal_spin = 0;
|
||||
u32 rfkill;
|
||||
|
||||
|
@ -2638,15 +2591,8 @@ static void iwl3945_alive_start(struct iwl_priv *priv)
|
|||
|
||||
priv->cfg->ops->smgmt->clear_station_table(priv);
|
||||
|
||||
rc = iwl_grab_nic_access(priv);
|
||||
if (rc) {
|
||||
IWL_WARN(priv, "Can not read RFKILL status from adapter\n");
|
||||
return;
|
||||
}
|
||||
|
||||
rfkill = iwl_read_prph(priv, APMG_RFKILL_REG);
|
||||
IWL_DEBUG_INFO(priv, "RFKILL status: 0x%x\n", rfkill);
|
||||
iwl_release_nic_access(priv);
|
||||
|
||||
if (rfkill & 0x1) {
|
||||
clear_bit(STATUS_RF_KILL_HW, &priv->status);
|
||||
|
@ -2792,13 +2738,8 @@ static void __iwl3945_down(struct iwl_priv *priv)
|
|||
iwl3945_hw_txq_ctx_stop(priv);
|
||||
iwl3945_hw_rxq_stop(priv);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
if (!iwl_grab_nic_access(priv)) {
|
||||
iwl_write_prph(priv, APMG_CLK_DIS_REG,
|
||||
APMG_CLK_VAL_DMA_CLK_RQT);
|
||||
iwl_release_nic_access(priv);
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
iwl_write_prph(priv, APMG_CLK_DIS_REG,
|
||||
APMG_CLK_VAL_DMA_CLK_RQT);
|
||||
|
||||
udelay(5);
|
||||
|
||||
|
@ -4243,6 +4184,7 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
|
|||
IWL_DEBUG_INFO(priv, "*** LOAD DRIVER ***\n");
|
||||
priv->cfg = cfg;
|
||||
priv->pci_dev = pdev;
|
||||
priv->inta_mask = CSR_INI_SET_MASK;
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
priv->debug_level = iwl3945_mod_params.debug;
|
||||
|
@ -4289,6 +4231,11 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
|
|||
* PCI Tx retries from interfering with C3 CPU state */
|
||||
pci_write_config_byte(pdev, 0x41, 0x00);
|
||||
|
||||
/* this spin lock will be used in apm_ops.init and EEPROM access
|
||||
* we should init now
|
||||
*/
|
||||
spin_lock_init(&priv->reg_lock);
|
||||
|
||||
/* amp init */
|
||||
err = priv->cfg->ops->lib->apm_ops.init(priv);
|
||||
if (err < 0) {
|
||||
|
@ -4344,8 +4291,8 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
|
|||
|
||||
pci_enable_msi(priv->pci_dev);
|
||||
|
||||
err = request_irq(priv->pci_dev->irq, iwl_isr, IRQF_SHARED,
|
||||
DRV_NAME, priv);
|
||||
err = request_irq(priv->pci_dev->irq, priv->cfg->ops->lib->isr,
|
||||
IRQF_SHARED, DRV_NAME, priv);
|
||||
if (err) {
|
||||
IWL_ERR(priv, "Error allocating IRQ %d\n", priv->pci_dev->irq);
|
||||
goto out_disable_msi;
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
config IWM
|
||||
tristate "Intel Wireless Multicomm 3200 WiFi driver"
|
||||
depends on MMC && WLAN_80211 && EXPERIMENTAL
|
||||
select LIB80211
|
||||
select FW_LOADER
|
||||
select RFKILL
|
||||
|
||||
config IWM_DEBUG
|
||||
bool "Enable full debugging output in iwmc3200wifi"
|
||||
depends on IWM && DEBUG_FS
|
||||
---help---
|
||||
This option will enable debug tracing and setting for iwm
|
||||
|
||||
You can set the debug level and module through debugfs. By
|
||||
default all modules are set to the IWL_DL_ERR level.
|
||||
To see the list of debug modules and levels, see iwm/debug.h
|
||||
|
||||
For example, if you want the full MLME debug output:
|
||||
echo 0xff > /debug/iwm/phyN/debug/mlme
|
||||
|
||||
Or, if you want the full debug, for all modules:
|
||||
echo 0xff > /debug/iwm/phyN/debug/level
|
||||
echo 0xff > /debug/iwm/phyN/debug/modules
|
|
@ -0,0 +1,5 @@
|
|||
obj-$(CONFIG_IWM) := iwmc3200wifi.o
|
||||
iwmc3200wifi-objs += main.o netdev.o rx.o tx.o sdio.o hal.o fw.o
|
||||
iwmc3200wifi-objs += commands.o wext.o cfg80211.o eeprom.o rfkill.o
|
||||
|
||||
iwmc3200wifi-$(CONFIG_IWM_DEBUG) += debugfs.o
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __IWM_BUS_H__
|
||||
#define __IWM_BUS_H__
|
||||
|
||||
#include "iwm.h"
|
||||
|
||||
struct iwm_if_ops {
|
||||
int (*enable)(struct iwm_priv *iwm);
|
||||
int (*disable)(struct iwm_priv *iwm);
|
||||
int (*send_chunk)(struct iwm_priv *iwm, u8* buf, int count);
|
||||
|
||||
int (*debugfs_init)(struct iwm_priv *iwm, struct dentry *parent_dir);
|
||||
void (*debugfs_exit)(struct iwm_priv *iwm);
|
||||
|
||||
const char *umac_name;
|
||||
const char *calib_lmac_name;
|
||||
const char *lmac_name;
|
||||
};
|
||||
|
||||
static inline int iwm_bus_send_chunk(struct iwm_priv *iwm, u8 *buf, int count)
|
||||
{
|
||||
return iwm->bus_ops->send_chunk(iwm, buf, count);
|
||||
}
|
||||
|
||||
static inline int iwm_bus_enable(struct iwm_priv *iwm)
|
||||
{
|
||||
return iwm->bus_ops->enable(iwm);
|
||||
}
|
||||
|
||||
static inline int iwm_bus_disable(struct iwm_priv *iwm)
|
||||
{
|
||||
return iwm->bus_ops->disable(iwm);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,409 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <linux/ieee80211.h>
|
||||
#include <net/cfg80211.h>
|
||||
|
||||
#include "iwm.h"
|
||||
#include "commands.h"
|
||||
#include "cfg80211.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define RATETAB_ENT(_rate, _rateid, _flags) \
|
||||
{ \
|
||||
.bitrate = (_rate), \
|
||||
.hw_value = (_rateid), \
|
||||
.flags = (_flags), \
|
||||
}
|
||||
|
||||
#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_rate iwm_rates[] = {
|
||||
RATETAB_ENT(10, 0x1, 0),
|
||||
RATETAB_ENT(20, 0x2, 0),
|
||||
RATETAB_ENT(55, 0x4, 0),
|
||||
RATETAB_ENT(110, 0x8, 0),
|
||||
RATETAB_ENT(60, 0x10, 0),
|
||||
RATETAB_ENT(90, 0x20, 0),
|
||||
RATETAB_ENT(120, 0x40, 0),
|
||||
RATETAB_ENT(180, 0x80, 0),
|
||||
RATETAB_ENT(240, 0x100, 0),
|
||||
RATETAB_ENT(360, 0x200, 0),
|
||||
RATETAB_ENT(480, 0x400, 0),
|
||||
RATETAB_ENT(540, 0x800, 0),
|
||||
};
|
||||
|
||||
#define iwm_a_rates (iwm_rates + 4)
|
||||
#define iwm_a_rates_size 8
|
||||
#define iwm_g_rates (iwm_rates + 0)
|
||||
#define iwm_g_rates_size 12
|
||||
|
||||
static struct ieee80211_channel iwm_2ghz_channels[] = {
|
||||
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 iwm_5ghz_a_channels[] = {
|
||||
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 iwm_band_2ghz = {
|
||||
.channels = iwm_2ghz_channels,
|
||||
.n_channels = ARRAY_SIZE(iwm_2ghz_channels),
|
||||
.bitrates = iwm_g_rates,
|
||||
.n_bitrates = iwm_g_rates_size,
|
||||
};
|
||||
|
||||
static struct ieee80211_supported_band iwm_band_5ghz = {
|
||||
.channels = iwm_5ghz_a_channels,
|
||||
.n_channels = ARRAY_SIZE(iwm_5ghz_a_channels),
|
||||
.bitrates = iwm_a_rates,
|
||||
.n_bitrates = iwm_a_rates_size,
|
||||
};
|
||||
|
||||
int iwm_cfg80211_inform_bss(struct iwm_priv *iwm)
|
||||
{
|
||||
struct wiphy *wiphy = iwm_to_wiphy(iwm);
|
||||
struct iwm_bss_info *bss, *next;
|
||||
struct iwm_umac_notif_bss_info *umac_bss;
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
struct ieee80211_channel *channel;
|
||||
struct ieee80211_supported_band *band;
|
||||
s32 signal;
|
||||
int freq;
|
||||
|
||||
list_for_each_entry_safe(bss, next, &iwm->bss_list, node) {
|
||||
umac_bss = bss->bss;
|
||||
mgmt = (struct ieee80211_mgmt *)(umac_bss->frame_buf);
|
||||
|
||||
if (umac_bss->band == UMAC_BAND_2GHZ)
|
||||
band = wiphy->bands[IEEE80211_BAND_2GHZ];
|
||||
else if (umac_bss->band == UMAC_BAND_5GHZ)
|
||||
band = wiphy->bands[IEEE80211_BAND_5GHZ];
|
||||
else {
|
||||
IWM_ERR(iwm, "Invalid band: %d\n", umac_bss->band);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
freq = ieee80211_channel_to_frequency(umac_bss->channel);
|
||||
channel = ieee80211_get_channel(wiphy, freq);
|
||||
signal = umac_bss->rssi * 100;
|
||||
|
||||
if (!cfg80211_inform_bss_frame(wiphy, channel, mgmt,
|
||||
le16_to_cpu(umac_bss->frame_len),
|
||||
signal, GFP_KERNEL))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwm_cfg80211_change_iface(struct wiphy *wiphy, int ifindex,
|
||||
enum nl80211_iftype type, u32 *flags,
|
||||
struct vif_params *params)
|
||||
{
|
||||
struct net_device *ndev;
|
||||
struct wireless_dev *wdev;
|
||||
struct iwm_priv *iwm;
|
||||
u32 old_mode;
|
||||
|
||||
/* we're under RTNL */
|
||||
ndev = __dev_get_by_index(&init_net, ifindex);
|
||||
if (!ndev)
|
||||
return -ENODEV;
|
||||
|
||||
wdev = ndev->ieee80211_ptr;
|
||||
iwm = ndev_to_iwm(ndev);
|
||||
old_mode = iwm->conf.mode;
|
||||
|
||||
switch (type) {
|
||||
case NL80211_IFTYPE_STATION:
|
||||
iwm->conf.mode = UMAC_MODE_BSS;
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
iwm->conf.mode = UMAC_MODE_IBSS;
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
wdev->iftype = type;
|
||||
|
||||
if ((old_mode == iwm->conf.mode) || !iwm->umac_profile)
|
||||
return 0;
|
||||
|
||||
iwm->umac_profile->mode = cpu_to_le32(iwm->conf.mode);
|
||||
|
||||
if (iwm->umac_profile_active) {
|
||||
int ret = iwm_invalidate_mlme_profile(iwm);
|
||||
if (ret < 0)
|
||||
IWM_ERR(iwm, "Couldn't invalidate profile\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwm_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
|
||||
struct cfg80211_scan_request *request)
|
||||
{
|
||||
struct iwm_priv *iwm = ndev_to_iwm(ndev);
|
||||
int ret;
|
||||
|
||||
if (!test_bit(IWM_STATUS_READY, &iwm->status)) {
|
||||
IWM_ERR(iwm, "Scan while device is not ready\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (test_bit(IWM_STATUS_SCANNING, &iwm->status)) {
|
||||
IWM_ERR(iwm, "Scanning already\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (test_bit(IWM_STATUS_SCAN_ABORTING, &iwm->status)) {
|
||||
IWM_ERR(iwm, "Scanning being aborted\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
set_bit(IWM_STATUS_SCANNING, &iwm->status);
|
||||
|
||||
ret = iwm_scan_ssids(iwm, request->ssids, request->n_ssids);
|
||||
if (ret) {
|
||||
clear_bit(IWM_STATUS_SCANNING, &iwm->status);
|
||||
return ret;
|
||||
}
|
||||
|
||||
iwm->scan_request = request;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwm_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
|
||||
{
|
||||
struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
|
||||
|
||||
if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
|
||||
(iwm->conf.rts_threshold != wiphy->rts_threshold)) {
|
||||
int ret;
|
||||
|
||||
iwm->conf.rts_threshold = wiphy->rts_threshold;
|
||||
|
||||
ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
|
||||
CFG_RTS_THRESHOLD,
|
||||
iwm->conf.rts_threshold);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
|
||||
(iwm->conf.frag_threshold != wiphy->frag_threshold)) {
|
||||
int ret;
|
||||
|
||||
iwm->conf.frag_threshold = wiphy->frag_threshold;
|
||||
|
||||
ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
|
||||
CFG_FRAG_THRESHOLD,
|
||||
iwm->conf.frag_threshold);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwm_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
|
||||
struct cfg80211_ibss_params *params)
|
||||
{
|
||||
struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
|
||||
struct ieee80211_channel *chan = params->channel;
|
||||
struct cfg80211_bss *bss;
|
||||
|
||||
if (!test_bit(IWM_STATUS_READY, &iwm->status))
|
||||
return -EIO;
|
||||
|
||||
/* UMAC doesn't support creating IBSS network with specified bssid.
|
||||
* This should be removed after we have join only mode supported. */
|
||||
if (params->bssid)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
bss = cfg80211_get_ibss(iwm_to_wiphy(iwm), NULL,
|
||||
params->ssid, params->ssid_len);
|
||||
if (!bss) {
|
||||
iwm_scan_one_ssid(iwm, params->ssid, params->ssid_len);
|
||||
schedule_timeout_interruptible(2 * HZ);
|
||||
bss = cfg80211_get_ibss(iwm_to_wiphy(iwm), NULL,
|
||||
params->ssid, params->ssid_len);
|
||||
}
|
||||
/* IBSS join only mode is not supported by UMAC ATM */
|
||||
if (bss) {
|
||||
cfg80211_put_bss(bss);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
iwm->channel = ieee80211_frequency_to_channel(chan->center_freq);
|
||||
iwm->umac_profile->ibss.band = chan->band;
|
||||
iwm->umac_profile->ibss.channel = iwm->channel;
|
||||
iwm->umac_profile->ssid.ssid_len = params->ssid_len;
|
||||
memcpy(iwm->umac_profile->ssid.ssid, params->ssid, params->ssid_len);
|
||||
|
||||
if (params->bssid)
|
||||
memcpy(&iwm->umac_profile->bssid[0], params->bssid, ETH_ALEN);
|
||||
|
||||
return iwm_send_mlme_profile(iwm);
|
||||
}
|
||||
|
||||
static int iwm_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
|
||||
{
|
||||
struct iwm_priv *iwm = wiphy_to_iwm(wiphy);
|
||||
|
||||
if (iwm->umac_profile_active)
|
||||
return iwm_invalidate_mlme_profile(iwm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cfg80211_ops iwm_cfg80211_ops = {
|
||||
.change_virtual_intf = iwm_cfg80211_change_iface,
|
||||
.scan = iwm_cfg80211_scan,
|
||||
.set_wiphy_params = iwm_cfg80211_set_wiphy_params,
|
||||
.join_ibss = iwm_cfg80211_join_ibss,
|
||||
.leave_ibss = iwm_cfg80211_leave_ibss,
|
||||
};
|
||||
|
||||
struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct wireless_dev *wdev;
|
||||
|
||||
/*
|
||||
* We're trying to have the following memory
|
||||
* layout:
|
||||
*
|
||||
* +-------------------------+
|
||||
* | struct wiphy |
|
||||
* +-------------------------+
|
||||
* | struct iwm_priv |
|
||||
* +-------------------------+
|
||||
* | bus private data |
|
||||
* | (e.g. iwm_priv_sdio) |
|
||||
* +-------------------------+
|
||||
*
|
||||
*/
|
||||
|
||||
wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
|
||||
if (!wdev) {
|
||||
dev_err(dev, "Couldn't allocate wireless device\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
wdev->wiphy = wiphy_new(&iwm_cfg80211_ops,
|
||||
sizeof(struct iwm_priv) + sizeof_bus);
|
||||
if (!wdev->wiphy) {
|
||||
dev_err(dev, "Couldn't allocate wiphy device\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_err_new;
|
||||
}
|
||||
|
||||
set_wiphy_dev(wdev->wiphy, dev);
|
||||
wdev->wiphy->max_scan_ssids = UMAC_WIFI_IF_PROBE_OPTION_MAX;
|
||||
wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
|
||||
BIT(NL80211_IFTYPE_ADHOC);
|
||||
wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &iwm_band_2ghz;
|
||||
wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &iwm_band_5ghz;
|
||||
wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
|
||||
|
||||
ret = wiphy_register(wdev->wiphy);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Couldn't register wiphy device\n");
|
||||
goto out_err_register;
|
||||
}
|
||||
|
||||
return wdev;
|
||||
|
||||
out_err_register:
|
||||
wiphy_free(wdev->wiphy);
|
||||
|
||||
out_err_new:
|
||||
kfree(wdev);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
void iwm_wdev_free(struct iwm_priv *iwm)
|
||||
{
|
||||
struct wireless_dev *wdev = iwm_to_wdev(iwm);
|
||||
|
||||
if (!wdev)
|
||||
return;
|
||||
|
||||
wiphy_unregister(wdev->wiphy);
|
||||
wiphy_free(wdev->wiphy);
|
||||
kfree(wdev);
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __IWM_CFG80211_H__
|
||||
#define __IWM_CFG80211_H__
|
||||
|
||||
int iwm_cfg80211_inform_bss(struct iwm_priv *iwm);
|
||||
struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev);
|
||||
void iwm_wdev_free(struct iwm_priv *iwm);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,920 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ieee80211.h>
|
||||
|
||||
#include "iwm.h"
|
||||
#include "bus.h"
|
||||
#include "hal.h"
|
||||
#include "umac.h"
|
||||
#include "commands.h"
|
||||
#include "debug.h"
|
||||
|
||||
static int iwm_send_lmac_ptrough_cmd(struct iwm_priv *iwm,
|
||||
u8 lmac_cmd_id,
|
||||
const void *lmac_payload,
|
||||
u16 lmac_payload_size,
|
||||
u8 resp)
|
||||
{
|
||||
struct iwm_udma_wifi_cmd udma_cmd = UDMA_LMAC_INIT;
|
||||
struct iwm_umac_cmd umac_cmd;
|
||||
struct iwm_lmac_cmd lmac_cmd;
|
||||
|
||||
lmac_cmd.id = lmac_cmd_id;
|
||||
|
||||
umac_cmd.id = UMAC_CMD_OPCODE_WIFI_PASS_THROUGH;
|
||||
umac_cmd.resp = resp;
|
||||
|
||||
return iwm_hal_send_host_cmd(iwm, &udma_cmd, &umac_cmd, &lmac_cmd,
|
||||
lmac_payload, lmac_payload_size);
|
||||
}
|
||||
|
||||
int iwm_send_wifi_if_cmd(struct iwm_priv *iwm, void *payload, u16 payload_size,
|
||||
bool resp)
|
||||
{
|
||||
struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
|
||||
struct iwm_umac_cmd umac_cmd;
|
||||
|
||||
umac_cmd.id = UMAC_CMD_OPCODE_WIFI_IF_WRAPPER;
|
||||
umac_cmd.resp = resp;
|
||||
|
||||
return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd,
|
||||
payload, payload_size);
|
||||
}
|
||||
|
||||
static struct coex_event iwm_sta_xor_prio_tbl[COEX_EVENTS_NUM] =
|
||||
{
|
||||
{4, 3, 0, COEX_UNASSOC_IDLE_FLAGS},
|
||||
{4, 3, 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS},
|
||||
{4, 3, 0, COEX_UNASSOC_AUTO_SCAN_FLAGS},
|
||||
{4, 3, 0, COEX_CALIBRATION_FLAGS},
|
||||
{4, 3, 0, COEX_PERIODIC_CALIBRATION_FLAGS},
|
||||
{4, 3, 0, COEX_CONNECTION_ESTAB_FLAGS},
|
||||
{4, 3, 0, COEX_ASSOCIATED_IDLE_FLAGS},
|
||||
{4, 3, 0, COEX_ASSOC_MANUAL_SCAN_FLAGS},
|
||||
{4, 3, 0, COEX_ASSOC_AUTO_SCAN_FLAGS},
|
||||
{4, 3, 0, COEX_ASSOC_ACTIVE_LEVEL_FLAGS},
|
||||
{6, 3, 0, COEX_XOR_RF_ON_FLAGS},
|
||||
{4, 3, 0, COEX_RF_OFF_FLAGS},
|
||||
{6, 6, 0, COEX_STAND_ALONE_DEBUG_FLAGS},
|
||||
{4, 3, 0, COEX_IPAN_ASSOC_LEVEL_FLAGS},
|
||||
{4, 3, 0, COEX_RSRVD1_FLAGS},
|
||||
{4, 3, 0, COEX_RSRVD2_FLAGS}
|
||||
};
|
||||
|
||||
static struct coex_event iwm_sta_cm_prio_tbl[COEX_EVENTS_NUM] =
|
||||
{
|
||||
{1, 1, 0, COEX_UNASSOC_IDLE_FLAGS},
|
||||
{4, 3, 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS},
|
||||
{3, 3, 0, COEX_UNASSOC_AUTO_SCAN_FLAGS},
|
||||
{5, 5, 0, COEX_CALIBRATION_FLAGS},
|
||||
{4, 4, 0, COEX_PERIODIC_CALIBRATION_FLAGS},
|
||||
{5, 4, 0, COEX_CONNECTION_ESTAB_FLAGS},
|
||||
{4, 4, 0, COEX_ASSOCIATED_IDLE_FLAGS},
|
||||
{4, 4, 0, COEX_ASSOC_MANUAL_SCAN_FLAGS},
|
||||
{4, 4, 0, COEX_ASSOC_AUTO_SCAN_FLAGS},
|
||||
{4, 4, 0, COEX_ASSOC_ACTIVE_LEVEL_FLAGS},
|
||||
{1, 1, 0, COEX_RF_ON_FLAGS},
|
||||
{1, 1, 0, COEX_RF_OFF_FLAGS},
|
||||
{6, 6, 0, COEX_STAND_ALONE_DEBUG_FLAGS},
|
||||
{5, 4, 0, COEX_IPAN_ASSOC_LEVEL_FLAGS},
|
||||
{1, 1, 0, COEX_RSRVD1_FLAGS},
|
||||
{1, 1, 0, COEX_RSRVD2_FLAGS}
|
||||
};
|
||||
|
||||
int iwm_send_prio_table(struct iwm_priv *iwm)
|
||||
{
|
||||
struct iwm_coex_prio_table_cmd coex_table_cmd;
|
||||
u32 coex_enabled, mode_enabled;
|
||||
|
||||
memset(&coex_table_cmd, 0, sizeof(struct iwm_coex_prio_table_cmd));
|
||||
|
||||
coex_table_cmd.flags = COEX_FLAGS_STA_TABLE_VALID_MSK;
|
||||
|
||||
switch (iwm->conf.coexist_mode) {
|
||||
case COEX_MODE_XOR:
|
||||
case COEX_MODE_CM:
|
||||
coex_enabled = 1;
|
||||
break;
|
||||
default:
|
||||
coex_enabled = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (iwm->conf.mode) {
|
||||
case UMAC_MODE_BSS:
|
||||
case UMAC_MODE_IBSS:
|
||||
mode_enabled = 1;
|
||||
break;
|
||||
default:
|
||||
mode_enabled = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (coex_enabled && mode_enabled) {
|
||||
coex_table_cmd.flags |= COEX_FLAGS_COEX_ENABLE_MSK |
|
||||
COEX_FLAGS_ASSOC_WAKEUP_UMASK_MSK |
|
||||
COEX_FLAGS_UNASSOC_WAKEUP_UMASK_MSK;
|
||||
|
||||
switch (iwm->conf.coexist_mode) {
|
||||
case COEX_MODE_XOR:
|
||||
memcpy(coex_table_cmd.sta_prio, iwm_sta_xor_prio_tbl,
|
||||
sizeof(iwm_sta_xor_prio_tbl));
|
||||
break;
|
||||
case COEX_MODE_CM:
|
||||
memcpy(coex_table_cmd.sta_prio, iwm_sta_cm_prio_tbl,
|
||||
sizeof(iwm_sta_cm_prio_tbl));
|
||||
break;
|
||||
default:
|
||||
IWM_ERR(iwm, "Invalid coex_mode 0x%x\n",
|
||||
iwm->conf.coexist_mode);
|
||||
break;
|
||||
}
|
||||
} else
|
||||
IWM_WARN(iwm, "coexistense disabled\n");
|
||||
|
||||
return iwm_send_lmac_ptrough_cmd(iwm, COEX_PRIORITY_TABLE_CMD,
|
||||
&coex_table_cmd,
|
||||
sizeof(struct iwm_coex_prio_table_cmd), 1);
|
||||
}
|
||||
|
||||
int iwm_send_init_calib_cfg(struct iwm_priv *iwm, u8 calib_requested)
|
||||
{
|
||||
struct iwm_lmac_cal_cfg_cmd cal_cfg_cmd;
|
||||
|
||||
memset(&cal_cfg_cmd, 0, sizeof(struct iwm_lmac_cal_cfg_cmd));
|
||||
|
||||
cal_cfg_cmd.ucode_cfg.init.enable = cpu_to_le32(calib_requested);
|
||||
cal_cfg_cmd.ucode_cfg.init.start = cpu_to_le32(calib_requested);
|
||||
cal_cfg_cmd.ucode_cfg.init.send_res = cpu_to_le32(calib_requested);
|
||||
cal_cfg_cmd.ucode_cfg.flags =
|
||||
cpu_to_le32(CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_AFTER_MSK);
|
||||
|
||||
return iwm_send_lmac_ptrough_cmd(iwm, CALIBRATION_CFG_CMD, &cal_cfg_cmd,
|
||||
sizeof(struct iwm_lmac_cal_cfg_cmd), 1);
|
||||
}
|
||||
|
||||
int iwm_send_periodic_calib_cfg(struct iwm_priv *iwm, u8 calib_requested)
|
||||
{
|
||||
struct iwm_lmac_cal_cfg_cmd cal_cfg_cmd;
|
||||
|
||||
memset(&cal_cfg_cmd, 0, sizeof(struct iwm_lmac_cal_cfg_cmd));
|
||||
|
||||
cal_cfg_cmd.ucode_cfg.periodic.enable = cpu_to_le32(calib_requested);
|
||||
cal_cfg_cmd.ucode_cfg.periodic.start = cpu_to_le32(calib_requested);
|
||||
|
||||
return iwm_send_lmac_ptrough_cmd(iwm, CALIBRATION_CFG_CMD, &cal_cfg_cmd,
|
||||
sizeof(struct iwm_lmac_cal_cfg_cmd), 0);
|
||||
}
|
||||
|
||||
int iwm_store_rxiq_calib_result(struct iwm_priv *iwm)
|
||||
{
|
||||
struct iwm_calib_rxiq *rxiq;
|
||||
u8 *eeprom_rxiq = iwm_eeprom_access(iwm, IWM_EEPROM_CALIB_RXIQ);
|
||||
int grplen = sizeof(struct iwm_calib_rxiq_group);
|
||||
|
||||
rxiq = kzalloc(sizeof(struct iwm_calib_rxiq), GFP_KERNEL);
|
||||
if (!rxiq) {
|
||||
IWM_ERR(iwm, "Couldn't alloc memory for RX IQ\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
eeprom_rxiq = iwm_eeprom_access(iwm, IWM_EEPROM_CALIB_RXIQ);
|
||||
if (IS_ERR(eeprom_rxiq)) {
|
||||
IWM_ERR(iwm, "Couldn't access EEPROM RX IQ entry\n");
|
||||
return PTR_ERR(eeprom_rxiq);
|
||||
}
|
||||
|
||||
iwm->calib_res[SHILOH_PHY_CALIBRATE_RX_IQ_CMD].buf = (u8 *)rxiq;
|
||||
iwm->calib_res[SHILOH_PHY_CALIBRATE_RX_IQ_CMD].size = sizeof(*rxiq);
|
||||
|
||||
rxiq->hdr.opcode = SHILOH_PHY_CALIBRATE_RX_IQ_CMD;
|
||||
rxiq->hdr.first_grp = 0;
|
||||
rxiq->hdr.grp_num = 1;
|
||||
rxiq->hdr.all_data_valid = 1;
|
||||
|
||||
memcpy(&rxiq->group[0], eeprom_rxiq, 4 * grplen);
|
||||
memcpy(&rxiq->group[4], eeprom_rxiq + 6 * grplen, grplen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iwm_send_calib_results(struct iwm_priv *iwm)
|
||||
{
|
||||
int i, ret = 0;
|
||||
|
||||
for (i = PHY_CALIBRATE_OPCODES_NUM; i < CALIBRATION_CMD_NUM; i++) {
|
||||
if (test_bit(i - PHY_CALIBRATE_OPCODES_NUM,
|
||||
&iwm->calib_done_map)) {
|
||||
IWM_DBG_CMD(iwm, DBG,
|
||||
"Send calibration %d result\n", i);
|
||||
ret |= iwm_send_lmac_ptrough_cmd(iwm,
|
||||
REPLY_PHY_CALIBRATION_CMD,
|
||||
iwm->calib_res[i].buf,
|
||||
iwm->calib_res[i].size, 0);
|
||||
|
||||
kfree(iwm->calib_res[i].buf);
|
||||
iwm->calib_res[i].buf = NULL;
|
||||
iwm->calib_res[i].size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int iwm_send_umac_reset(struct iwm_priv *iwm, __le32 reset_flags, bool resp)
|
||||
{
|
||||
struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
|
||||
struct iwm_umac_cmd umac_cmd;
|
||||
struct iwm_umac_cmd_reset reset;
|
||||
|
||||
reset.flags = reset_flags;
|
||||
|
||||
umac_cmd.id = UMAC_CMD_OPCODE_RESET;
|
||||
umac_cmd.resp = resp;
|
||||
|
||||
return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &reset,
|
||||
sizeof(struct iwm_umac_cmd_reset));
|
||||
}
|
||||
|
||||
int iwm_umac_set_config_fix(struct iwm_priv *iwm, u16 tbl, u16 key, u32 value)
|
||||
{
|
||||
struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
|
||||
struct iwm_umac_cmd umac_cmd;
|
||||
struct iwm_umac_cmd_set_param_fix param;
|
||||
|
||||
if ((tbl != UMAC_PARAM_TBL_CFG_FIX) &&
|
||||
(tbl != UMAC_PARAM_TBL_FA_CFG_FIX))
|
||||
return -EINVAL;
|
||||
|
||||
umac_cmd.id = UMAC_CMD_OPCODE_SET_PARAM_FIX;
|
||||
umac_cmd.resp = 0;
|
||||
|
||||
param.tbl = cpu_to_le16(tbl);
|
||||
param.key = cpu_to_le16(key);
|
||||
param.value = cpu_to_le32(value);
|
||||
|
||||
return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, ¶m,
|
||||
sizeof(struct iwm_umac_cmd_set_param_fix));
|
||||
}
|
||||
|
||||
int iwm_umac_set_config_var(struct iwm_priv *iwm, u16 key,
|
||||
void *payload, u16 payload_size)
|
||||
{
|
||||
struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
|
||||
struct iwm_umac_cmd umac_cmd;
|
||||
struct iwm_umac_cmd_set_param_var *param_hdr;
|
||||
u8 *param;
|
||||
int ret;
|
||||
|
||||
param = kzalloc(payload_size +
|
||||
sizeof(struct iwm_umac_cmd_set_param_var), GFP_KERNEL);
|
||||
if (!param) {
|
||||
IWM_ERR(iwm, "Couldn't allocate param\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
param_hdr = (struct iwm_umac_cmd_set_param_var *)param;
|
||||
|
||||
umac_cmd.id = UMAC_CMD_OPCODE_SET_PARAM_VAR;
|
||||
umac_cmd.resp = 0;
|
||||
|
||||
param_hdr->tbl = cpu_to_le16(UMAC_PARAM_TBL_CFG_VAR);
|
||||
param_hdr->key = cpu_to_le16(key);
|
||||
param_hdr->len = cpu_to_le16(payload_size);
|
||||
memcpy(param + sizeof(struct iwm_umac_cmd_set_param_var),
|
||||
payload, payload_size);
|
||||
|
||||
ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, param,
|
||||
sizeof(struct iwm_umac_cmd_set_param_var) +
|
||||
payload_size);
|
||||
kfree(param);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int iwm_send_umac_config(struct iwm_priv *iwm,
|
||||
__le32 reset_flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Use UMAC default values */
|
||||
ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
|
||||
CFG_POWER_INDEX, iwm->conf.power_index);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_FA_CFG_FIX,
|
||||
CFG_FRAG_THRESHOLD,
|
||||
iwm->conf.frag_threshold);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
|
||||
CFG_RTS_THRESHOLD,
|
||||
iwm->conf.rts_threshold);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
|
||||
CFG_CTS_TO_SELF, iwm->conf.cts_to_self);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
|
||||
CFG_COEX_MODE, iwm->conf.coexist_mode);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
|
||||
CFG_ASSOCIATION_TIMEOUT,
|
||||
iwm->conf.assoc_timeout);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
|
||||
CFG_ROAM_TIMEOUT,
|
||||
iwm->conf.roam_timeout);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
|
||||
CFG_WIRELESS_MODE,
|
||||
WIRELESS_MODE_11A | WIRELESS_MODE_11G);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*/
|
||||
|
||||
ret = iwm_umac_set_config_var(iwm, CFG_NET_ADDR,
|
||||
iwm_to_ndev(iwm)->dev_addr, ETH_ALEN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* UMAC PM static configurations */
|
||||
ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
|
||||
CFG_PM_LEGACY_RX_TIMEOUT, 0x12C);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
|
||||
CFG_PM_LEGACY_TX_TIMEOUT, 0x15E);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
|
||||
CFG_PM_CTRL_FLAGS, 0x30001);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
|
||||
CFG_PM_KEEP_ALIVE_IN_BEACONS, 0x80);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* reset UMAC */
|
||||
ret = iwm_send_umac_reset(iwm, reset_flags, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_RESET, IWM_SRC_UMAC,
|
||||
WAIT_NOTIF_TIMEOUT);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Wait for UMAC RESET timeout\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int iwm_send_packet(struct iwm_priv *iwm, struct sk_buff *skb, int pool_id)
|
||||
{
|
||||
struct iwm_udma_wifi_cmd udma_cmd;
|
||||
struct iwm_umac_cmd umac_cmd;
|
||||
struct iwm_tx_info *tx_info = skb_to_tx_info(skb);
|
||||
|
||||
udma_cmd.eop = 1; /* always set eop for non-concatenated Tx */
|
||||
udma_cmd.credit_group = pool_id;
|
||||
udma_cmd.ra_tid = tx_info->sta << 4 | tx_info->tid;
|
||||
udma_cmd.lmac_offset = 0;
|
||||
|
||||
umac_cmd.id = REPLY_TX;
|
||||
umac_cmd.color = tx_info->color;
|
||||
umac_cmd.resp = 0;
|
||||
|
||||
return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd,
|
||||
skb->data, skb->len);
|
||||
}
|
||||
|
||||
static int iwm_target_read(struct iwm_priv *iwm, __le32 address,
|
||||
u8 *response, u32 resp_size)
|
||||
{
|
||||
struct iwm_udma_nonwifi_cmd target_cmd;
|
||||
struct iwm_nonwifi_cmd *cmd;
|
||||
u16 seq_num;
|
||||
int ret = 0;
|
||||
|
||||
target_cmd.opcode = UMAC_HDI_OUT_OPCODE_READ;
|
||||
target_cmd.addr = address;
|
||||
target_cmd.op1_sz = cpu_to_le32(resp_size);
|
||||
target_cmd.op2 = 0;
|
||||
target_cmd.handle_by_hw = 0;
|
||||
target_cmd.resp = 1;
|
||||
target_cmd.eop = 1;
|
||||
|
||||
ret = iwm_hal_send_target_cmd(iwm, &target_cmd, NULL);
|
||||
if (ret < 0)
|
||||
IWM_ERR(iwm, "Couldn't send READ command\n");
|
||||
|
||||
/* When succeding, the send_target routine returns the seq number */
|
||||
seq_num = ret;
|
||||
|
||||
ret = wait_event_interruptible_timeout(iwm->nonwifi_queue,
|
||||
(cmd = iwm_get_pending_nonwifi_cmd(iwm, seq_num,
|
||||
UMAC_HDI_OUT_OPCODE_READ)) != NULL,
|
||||
2 * HZ);
|
||||
|
||||
if (!ret) {
|
||||
IWM_ERR(iwm, "Didn't receive a target READ answer\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
memcpy(response, cmd->buf.hdr + sizeof(struct iwm_udma_in_hdr),
|
||||
resp_size);
|
||||
|
||||
kfree(cmd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int iwm_read_mac(struct iwm_priv *iwm, u8 *mac)
|
||||
{
|
||||
int ret;
|
||||
u8 mac_align[ALIGN(ETH_ALEN, 8)];
|
||||
|
||||
ret = iwm_target_read(iwm, cpu_to_le32(WICO_MAC_ADDRESS_ADDR),
|
||||
mac_align, sizeof(mac_align));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (is_valid_ether_addr(mac_align))
|
||||
memcpy(mac, mac_align, ETH_ALEN);
|
||||
else {
|
||||
IWM_ERR(iwm, "Invalid EEPROM MAC\n");
|
||||
memcpy(mac, iwm->conf.mac_addr, ETH_ALEN);
|
||||
get_random_bytes(&mac[3], 3);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx)
|
||||
{
|
||||
struct iwm_umac_tx_key_id tx_key_id;
|
||||
|
||||
if (!iwm->default_key || !iwm->default_key->in_use)
|
||||
return -EINVAL;
|
||||
|
||||
tx_key_id.hdr.oid = UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID;
|
||||
tx_key_id.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_tx_key_id) -
|
||||
sizeof(struct iwm_umac_wifi_if));
|
||||
|
||||
tx_key_id.key_idx = key_idx;
|
||||
|
||||
return iwm_send_wifi_if_cmd(iwm, &tx_key_id, sizeof(tx_key_id), 1);
|
||||
}
|
||||
|
||||
static int iwm_check_profile(struct iwm_priv *iwm)
|
||||
{
|
||||
if (!iwm->umac_profile_active)
|
||||
return -EAGAIN;
|
||||
|
||||
if (iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_40 &&
|
||||
iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_104 &&
|
||||
iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_TKIP &&
|
||||
iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_CCMP) {
|
||||
IWM_ERR(iwm, "Wrong unicast cipher: 0x%x\n",
|
||||
iwm->umac_profile->sec.ucast_cipher);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_WEP_40 &&
|
||||
iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_WEP_104 &&
|
||||
iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_TKIP &&
|
||||
iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_CCMP) {
|
||||
IWM_ERR(iwm, "Wrong multicast cipher: 0x%x\n",
|
||||
iwm->umac_profile->sec.mcast_cipher);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if ((iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_40 ||
|
||||
iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_104) &&
|
||||
(iwm->umac_profile->sec.ucast_cipher !=
|
||||
iwm->umac_profile->sec.mcast_cipher)) {
|
||||
IWM_ERR(iwm, "Unicast and multicast ciphers differ for WEP\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
|
||||
struct iwm_key *key)
|
||||
{
|
||||
int ret;
|
||||
u8 cmd[64], *sta_addr, *key_data, key_len;
|
||||
s8 key_idx;
|
||||
u16 cmd_size = 0;
|
||||
struct iwm_umac_key_hdr *key_hdr = &key->hdr;
|
||||
struct iwm_umac_key_wep40 *wep40 = (struct iwm_umac_key_wep40 *)cmd;
|
||||
struct iwm_umac_key_wep104 *wep104 = (struct iwm_umac_key_wep104 *)cmd;
|
||||
struct iwm_umac_key_tkip *tkip = (struct iwm_umac_key_tkip *)cmd;
|
||||
struct iwm_umac_key_ccmp *ccmp = (struct iwm_umac_key_ccmp *)cmd;
|
||||
|
||||
if (set_tx_key)
|
||||
iwm->default_key = key;
|
||||
|
||||
/*
|
||||
* We check if our current profile is valid.
|
||||
* If not, we dont push the key, we just cache them,
|
||||
* so that with the next siwsessid call, the keys
|
||||
* will be actually pushed.
|
||||
*/
|
||||
if (!remove) {
|
||||
ret = iwm_check_profile(iwm);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
sta_addr = key->hdr.mac;
|
||||
key_data = key->key;
|
||||
key_len = key->key_len;
|
||||
key_idx = key->hdr.key_idx;
|
||||
|
||||
if (!remove) {
|
||||
IWM_DBG_WEXT(iwm, DBG, "key_idx:%d set tx key:%d\n",
|
||||
key_idx, set_tx_key);
|
||||
IWM_DBG_WEXT(iwm, DBG, "key_len:%d\n", key_len);
|
||||
IWM_DBG_WEXT(iwm, DBG, "MAC:%pM, idx:%d, multicast:%d\n",
|
||||
key_hdr->mac, key_hdr->key_idx, key_hdr->multicast);
|
||||
|
||||
IWM_DBG_WEXT(iwm, DBG, "profile: mcast:0x%x, ucast:0x%x\n",
|
||||
iwm->umac_profile->sec.mcast_cipher,
|
||||
iwm->umac_profile->sec.ucast_cipher);
|
||||
IWM_DBG_WEXT(iwm, DBG, "profile: auth_type:0x%x, flags:0x%x\n",
|
||||
iwm->umac_profile->sec.auth_type,
|
||||
iwm->umac_profile->sec.flags);
|
||||
|
||||
switch (key->alg) {
|
||||
case UMAC_CIPHER_TYPE_WEP_40:
|
||||
wep40->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP40_KEY;
|
||||
wep40->hdr.buf_size =
|
||||
cpu_to_le16(sizeof(struct iwm_umac_key_wep40) -
|
||||
sizeof(struct iwm_umac_wifi_if));
|
||||
|
||||
memcpy(&wep40->key_hdr, key_hdr,
|
||||
sizeof(struct iwm_umac_key_hdr));
|
||||
memcpy(wep40->key, key_data, key_len);
|
||||
wep40->static_key = 1;
|
||||
|
||||
cmd_size = sizeof(struct iwm_umac_key_wep40);
|
||||
break;
|
||||
|
||||
case UMAC_CIPHER_TYPE_WEP_104:
|
||||
wep104->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP104_KEY;
|
||||
wep104->hdr.buf_size =
|
||||
cpu_to_le16(sizeof(struct iwm_umac_key_wep104) -
|
||||
sizeof(struct iwm_umac_wifi_if));
|
||||
|
||||
memcpy(&wep104->key_hdr, key_hdr,
|
||||
sizeof(struct iwm_umac_key_hdr));
|
||||
memcpy(wep104->key, key_data, key_len);
|
||||
wep104->static_key = 1;
|
||||
|
||||
cmd_size = sizeof(struct iwm_umac_key_wep104);
|
||||
break;
|
||||
|
||||
case UMAC_CIPHER_TYPE_CCMP:
|
||||
key_hdr->key_idx++;
|
||||
ccmp->hdr.oid = UMAC_WIFI_IF_CMD_ADD_CCMP_KEY;
|
||||
ccmp->hdr.buf_size =
|
||||
cpu_to_le16(sizeof(struct iwm_umac_key_ccmp) -
|
||||
sizeof(struct iwm_umac_wifi_if));
|
||||
|
||||
memcpy(&ccmp->key_hdr, key_hdr,
|
||||
sizeof(struct iwm_umac_key_hdr));
|
||||
|
||||
memcpy(ccmp->key, key_data, key_len);
|
||||
|
||||
if (key->flags & IW_ENCODE_EXT_RX_SEQ_VALID)
|
||||
memcpy(ccmp->iv_count, key->rx_seq, 6);
|
||||
|
||||
cmd_size = sizeof(struct iwm_umac_key_ccmp);
|
||||
break;
|
||||
|
||||
case UMAC_CIPHER_TYPE_TKIP:
|
||||
key_hdr->key_idx++;
|
||||
tkip->hdr.oid = UMAC_WIFI_IF_CMD_ADD_TKIP_KEY;
|
||||
tkip->hdr.buf_size =
|
||||
cpu_to_le16(sizeof(struct iwm_umac_key_tkip) -
|
||||
sizeof(struct iwm_umac_wifi_if));
|
||||
|
||||
memcpy(&tkip->key_hdr, key_hdr,
|
||||
sizeof(struct iwm_umac_key_hdr));
|
||||
|
||||
memcpy(tkip->tkip_key, key_data, IWM_TKIP_KEY_SIZE);
|
||||
memcpy(tkip->mic_tx_key, key_data + IWM_TKIP_KEY_SIZE,
|
||||
IWM_TKIP_MIC_SIZE);
|
||||
memcpy(tkip->mic_rx_key,
|
||||
key_data + IWM_TKIP_KEY_SIZE + IWM_TKIP_MIC_SIZE,
|
||||
IWM_TKIP_MIC_SIZE);
|
||||
|
||||
if (key->flags & IW_ENCODE_EXT_RX_SEQ_VALID)
|
||||
memcpy(ccmp->iv_count, key->rx_seq, 6);
|
||||
|
||||
cmd_size = sizeof(struct iwm_umac_key_tkip);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
if ((key->alg == UMAC_CIPHER_TYPE_CCMP) ||
|
||||
(key->alg == UMAC_CIPHER_TYPE_TKIP))
|
||||
/*
|
||||
* UGLY_UGLY_UGLY
|
||||
* Copied HACK from the MWG driver.
|
||||
* Without it, the key is set before the second
|
||||
* EAPOL frame is sent, and the latter is thus
|
||||
* encrypted.
|
||||
*/
|
||||
schedule_timeout_interruptible(usecs_to_jiffies(300));
|
||||
|
||||
ret = iwm_send_wifi_if_cmd(iwm, cmd, cmd_size, 1);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* We need a default key only if it is set and
|
||||
* if we're doing WEP.
|
||||
*/
|
||||
if (iwm->default_key == key &&
|
||||
((key->alg == UMAC_CIPHER_TYPE_WEP_40) ||
|
||||
(key->alg == UMAC_CIPHER_TYPE_WEP_104))) {
|
||||
ret = iwm_set_tx_key(iwm, key_idx);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
struct iwm_umac_key_remove key_remove;
|
||||
|
||||
key_remove.hdr.oid = UMAC_WIFI_IF_CMD_REMOVE_KEY;
|
||||
key_remove.hdr.buf_size =
|
||||
cpu_to_le16(sizeof(struct iwm_umac_key_remove) -
|
||||
sizeof(struct iwm_umac_wifi_if));
|
||||
memcpy(&key_remove.key_hdr, key_hdr,
|
||||
sizeof(struct iwm_umac_key_hdr));
|
||||
|
||||
ret = iwm_send_wifi_if_cmd(iwm, &key_remove,
|
||||
sizeof(struct iwm_umac_key_remove),
|
||||
1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
iwm->keys[key_idx].in_use = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
kfree(key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int iwm_send_mlme_profile(struct iwm_priv *iwm)
|
||||
{
|
||||
int ret, i;
|
||||
struct iwm_umac_profile profile;
|
||||
|
||||
memcpy(&profile, iwm->umac_profile, sizeof(profile));
|
||||
|
||||
profile.hdr.oid = UMAC_WIFI_IF_CMD_SET_PROFILE;
|
||||
profile.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_profile) -
|
||||
sizeof(struct iwm_umac_wifi_if));
|
||||
|
||||
ret = iwm_send_wifi_if_cmd(iwm, &profile, sizeof(profile), 1);
|
||||
if (ret < 0) {
|
||||
IWM_ERR(iwm, "Send profile command failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Wait for the profile to be active */
|
||||
ret = wait_event_interruptible_timeout(iwm->mlme_queue,
|
||||
iwm->umac_profile_active == 1,
|
||||
3 * HZ);
|
||||
if (!ret)
|
||||
return -EBUSY;
|
||||
|
||||
|
||||
for (i = 0; i < IWM_NUM_KEYS; i++)
|
||||
if (iwm->keys[i].in_use) {
|
||||
int default_key = 0;
|
||||
struct iwm_key *key = &iwm->keys[i];
|
||||
|
||||
if (key == iwm->default_key)
|
||||
default_key = 1;
|
||||
|
||||
/* Wait for the profile before sending the keys */
|
||||
wait_event_interruptible_timeout(iwm->mlme_queue,
|
||||
(test_bit(IWM_STATUS_ASSOCIATING, &iwm->status) ||
|
||||
test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)),
|
||||
3 * HZ);
|
||||
|
||||
ret = iwm_set_key(iwm, 0, default_key, key);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iwm_invalidate_mlme_profile(struct iwm_priv *iwm)
|
||||
{
|
||||
int ret;
|
||||
struct iwm_umac_invalidate_profile invalid;
|
||||
|
||||
invalid.hdr.oid = UMAC_WIFI_IF_CMD_INVALIDATE_PROFILE;
|
||||
invalid.hdr.buf_size =
|
||||
cpu_to_le16(sizeof(struct iwm_umac_invalidate_profile) -
|
||||
sizeof(struct iwm_umac_wifi_if));
|
||||
|
||||
invalid.reason = WLAN_REASON_UNSPECIFIED;
|
||||
|
||||
ret = iwm_send_wifi_if_cmd(iwm, &invalid, sizeof(invalid), 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = wait_event_interruptible_timeout(iwm->mlme_queue,
|
||||
(iwm->umac_profile_active == 0),
|
||||
2 * HZ);
|
||||
if (!ret)
|
||||
return -EBUSY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iwm_send_umac_stats_req(struct iwm_priv *iwm, u32 flags)
|
||||
{
|
||||
struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
|
||||
struct iwm_umac_cmd umac_cmd;
|
||||
struct iwm_umac_cmd_stats_req stats_req;
|
||||
|
||||
stats_req.flags = cpu_to_le32(flags);
|
||||
|
||||
umac_cmd.id = UMAC_CMD_OPCODE_STATISTIC_REQUEST;
|
||||
umac_cmd.resp = 0;
|
||||
|
||||
return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &stats_req,
|
||||
sizeof(struct iwm_umac_cmd_stats_req));
|
||||
}
|
||||
|
||||
int iwm_send_umac_channel_list(struct iwm_priv *iwm)
|
||||
{
|
||||
struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
|
||||
struct iwm_umac_cmd umac_cmd;
|
||||
struct iwm_umac_cmd_get_channel_list *ch_list;
|
||||
int size = sizeof(struct iwm_umac_cmd_get_channel_list) +
|
||||
sizeof(struct iwm_umac_channel_info) * 4;
|
||||
int ret;
|
||||
|
||||
ch_list = kzalloc(size, GFP_KERNEL);
|
||||
if (!ch_list) {
|
||||
IWM_ERR(iwm, "Couldn't allocate channel list cmd\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ch_list->ch[0].band = UMAC_BAND_2GHZ;
|
||||
ch_list->ch[0].type = UMAC_CHANNEL_WIDTH_20MHZ;
|
||||
ch_list->ch[0].flags = UMAC_CHANNEL_FLAG_VALID;
|
||||
|
||||
ch_list->ch[1].band = UMAC_BAND_5GHZ;
|
||||
ch_list->ch[1].type = UMAC_CHANNEL_WIDTH_20MHZ;
|
||||
ch_list->ch[1].flags = UMAC_CHANNEL_FLAG_VALID;
|
||||
|
||||
ch_list->ch[2].band = UMAC_BAND_2GHZ;
|
||||
ch_list->ch[2].type = UMAC_CHANNEL_WIDTH_20MHZ;
|
||||
ch_list->ch[2].flags = UMAC_CHANNEL_FLAG_VALID | UMAC_CHANNEL_FLAG_IBSS;
|
||||
|
||||
ch_list->ch[3].band = UMAC_BAND_5GHZ;
|
||||
ch_list->ch[3].type = UMAC_CHANNEL_WIDTH_20MHZ;
|
||||
ch_list->ch[3].flags = UMAC_CHANNEL_FLAG_VALID | UMAC_CHANNEL_FLAG_IBSS;
|
||||
|
||||
ch_list->count = cpu_to_le16(4);
|
||||
|
||||
umac_cmd.id = UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST;
|
||||
umac_cmd.resp = 1;
|
||||
|
||||
ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, ch_list, size);
|
||||
|
||||
kfree(ch_list);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids,
|
||||
int ssid_num)
|
||||
{
|
||||
struct iwm_umac_cmd_scan_request req;
|
||||
int i, ret;
|
||||
|
||||
memset(&req, 0, sizeof(struct iwm_umac_cmd_scan_request));
|
||||
|
||||
req.hdr.oid = UMAC_WIFI_IF_CMD_SCAN_REQUEST;
|
||||
req.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_cmd_scan_request)
|
||||
- sizeof(struct iwm_umac_wifi_if));
|
||||
req.type = UMAC_WIFI_IF_SCAN_TYPE_USER;
|
||||
req.timeout = 2;
|
||||
req.seq_num = iwm->scan_id;
|
||||
req.ssid_num = min(ssid_num, UMAC_WIFI_IF_PROBE_OPTION_MAX);
|
||||
|
||||
for (i = 0; i < req.ssid_num; i++) {
|
||||
memcpy(req.ssids[i].ssid, ssids[i].ssid, ssids[i].ssid_len);
|
||||
req.ssids[i].ssid_len = ssids[i].ssid_len;
|
||||
}
|
||||
|
||||
ret = iwm_send_wifi_if_cmd(iwm, &req, sizeof(req), 0);
|
||||
if (ret < 0) {
|
||||
IWM_ERR(iwm, "Couldn't send scan request\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
iwm->scan_id = iwm->scan_id++ % IWM_SCAN_ID_MAX;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len)
|
||||
{
|
||||
struct cfg80211_ssid one_ssid;
|
||||
|
||||
if (test_and_set_bit(IWM_STATUS_SCANNING, &iwm->status))
|
||||
return 0;
|
||||
|
||||
one_ssid.ssid_len = min(ssid_len, IEEE80211_MAX_SSID_LEN);
|
||||
memcpy(&one_ssid.ssid, ssid, one_ssid.ssid_len);
|
||||
|
||||
return iwm_scan_ssids(iwm, &one_ssid, 1);
|
||||
}
|
||||
|
||||
int iwm_target_reset(struct iwm_priv *iwm)
|
||||
{
|
||||
struct iwm_udma_nonwifi_cmd target_cmd;
|
||||
|
||||
target_cmd.opcode = UMAC_HDI_OUT_OPCODE_REBOOT;
|
||||
target_cmd.addr = 0;
|
||||
target_cmd.op1_sz = 0;
|
||||
target_cmd.op2 = 0;
|
||||
target_cmd.handle_by_hw = 0;
|
||||
target_cmd.resp = 0;
|
||||
target_cmd.eop = 1;
|
||||
|
||||
return iwm_hal_send_target_cmd(iwm, &target_cmd, NULL);
|
||||
}
|
|
@ -0,0 +1,419 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __IWM_COMMANDS_H__
|
||||
#define __IWM_COMMANDS_H__
|
||||
|
||||
#include <linux/ieee80211.h>
|
||||
|
||||
#define IWM_BARKER_REBOOT_NOTIFICATION 0xF
|
||||
#define IWM_ACK_BARKER_NOTIFICATION 0x10
|
||||
|
||||
/* UMAC commands */
|
||||
#define UMAC_RST_CTRL_FLG_LARC_CLK_EN 0x0001
|
||||
#define UMAC_RST_CTRL_FLG_LARC_RESET 0x0002
|
||||
#define UMAC_RST_CTRL_FLG_FUNC_RESET 0x0004
|
||||
#define UMAC_RST_CTRL_FLG_DEV_RESET 0x0008
|
||||
#define UMAC_RST_CTRL_FLG_WIFI_CORE_EN 0x0010
|
||||
#define UMAC_RST_CTRL_FLG_WIFI_LINK_EN 0x0040
|
||||
#define UMAC_RST_CTRL_FLG_WIFI_MLME_EN 0x0080
|
||||
#define UMAC_RST_CTRL_FLG_NVM_RELOAD 0x0100
|
||||
|
||||
struct iwm_umac_cmd_reset {
|
||||
__le32 flags;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define UMAC_PARAM_TBL_ORD_FIX 0x0
|
||||
#define UMAC_PARAM_TBL_ORD_VAR 0x1
|
||||
#define UMAC_PARAM_TBL_CFG_FIX 0x2
|
||||
#define UMAC_PARAM_TBL_CFG_VAR 0x3
|
||||
#define UMAC_PARAM_TBL_BSS_TRK 0x4
|
||||
#define UMAC_PARAM_TBL_FA_CFG_FIX 0x5
|
||||
#define UMAC_PARAM_TBL_STA 0x6
|
||||
#define UMAC_PARAM_TBL_CHN 0x7
|
||||
#define UMAC_PARAM_TBL_STATISTICS 0x8
|
||||
|
||||
/* fast access table */
|
||||
enum {
|
||||
CFG_FRAG_THRESHOLD = 0,
|
||||
CFG_FRAME_RETRY_LIMIT,
|
||||
CFG_OS_QUEUE_UTIL_TH,
|
||||
CFG_RX_FILTER,
|
||||
/* <-- LAST --> */
|
||||
FAST_ACCESS_CFG_TBL_FIX_LAST
|
||||
};
|
||||
|
||||
/* fixed size table */
|
||||
enum {
|
||||
CFG_POWER_INDEX = 0,
|
||||
CFG_PM_LEGACY_RX_TIMEOUT,
|
||||
CFG_PM_LEGACY_TX_TIMEOUT,
|
||||
CFG_PM_CTRL_FLAGS,
|
||||
CFG_PM_KEEP_ALIVE_IN_BEACONS,
|
||||
CFG_BT_ON_THRESHOLD,
|
||||
CFG_RTS_THRESHOLD,
|
||||
CFG_CTS_TO_SELF,
|
||||
CFG_COEX_MODE,
|
||||
CFG_WIRELESS_MODE,
|
||||
CFG_ASSOCIATION_TIMEOUT,
|
||||
CFG_ROAM_TIMEOUT,
|
||||
CFG_CAPABILITY_SUPPORTED_RATES,
|
||||
CFG_SCAN_ALLOWED_UNASSOC_FLAGS,
|
||||
CFG_SCAN_ALLOWED_MAIN_ASSOC_FLAGS,
|
||||
CFG_SCAN_ALLOWED_PAN_ASSOC_FLAGS,
|
||||
CFG_SCAN_INTERNAL_PERIODIC_ENABLED,
|
||||
CFG_SCAN_IMM_INTERNAL_PERIODIC_SCAN_ON_INIT,
|
||||
CFG_SCAN_DEFAULT_PERIODIC_FREQ_SEC,
|
||||
CFG_SCAN_NUM_PASSIVE_CHAN_PER_PARTIAL_SCAN,
|
||||
CFG_TLC_SUPPORTED_TX_HT_RATES,
|
||||
CFG_TLC_SUPPORTED_TX_RATES,
|
||||
CFG_TLC_VALID_ANTENNA,
|
||||
CFG_TLC_SPATIAL_STREAM_SUPPORTED,
|
||||
CFG_TLC_RETRY_PER_RATE,
|
||||
CFG_TLC_RETRY_PER_HT_RATE,
|
||||
CFG_TLC_FIXED_RATE,
|
||||
CFG_TLC_FIXED_RATE_FLAGS,
|
||||
CFG_TLC_CONTROL_FLAGS,
|
||||
CFG_TLC_SR_MIN_FAIL,
|
||||
CFG_TLC_SR_MIN_PASS,
|
||||
CFG_TLC_HT_STAY_IN_COL_PASS_THRESH,
|
||||
CFG_TLC_HT_STAY_IN_COL_FAIL_THRESH,
|
||||
CFG_TLC_LEGACY_STAY_IN_COL_PASS_THRESH,
|
||||
CFG_TLC_LEGACY_STAY_IN_COL_FAIL_THRESH,
|
||||
CFG_TLC_HT_FLUSH_STATS_PACKETS,
|
||||
CFG_TLC_LEGACY_FLUSH_STATS_PACKETS,
|
||||
CFG_TLC_LEGACY_FLUSH_STATS_MS,
|
||||
CFG_TLC_HT_FLUSH_STATS_MS,
|
||||
CFG_TLC_STAY_IN_COL_TIME_OUT,
|
||||
CFG_TLC_AGG_SHORT_LIM,
|
||||
CFG_TLC_AGG_LONG_LIM,
|
||||
CFG_TLC_HT_SR_NO_DECREASE,
|
||||
CFG_TLC_LEGACY_SR_NO_DECREASE,
|
||||
CFG_TLC_SR_FORCE_DECREASE,
|
||||
CFG_TLC_SR_ALLOW_INCREASE,
|
||||
CFG_TLC_AGG_SET_LONG,
|
||||
CFG_TLC_AUTO_AGGREGATION,
|
||||
CFG_TLC_AGG_THRESHOLD,
|
||||
CFG_TLC_TID_LOAD_THRESHOLD,
|
||||
CFG_TLC_BLOCK_ACK_TIMEOUT,
|
||||
CFG_TLC_NO_BA_COUNTED_AS_ONE,
|
||||
CFG_TLC_NUM_BA_STREAMS_ALLOWED,
|
||||
CFG_TLC_NUM_BA_STREAMS_PRESENT,
|
||||
CFG_TLC_RENEW_ADDBA_DELAY,
|
||||
CFG_TLC_NUM_OF_MULTISEC_TO_COUN_LOAD,
|
||||
CFG_TLC_IS_STABLE_IN_HT,
|
||||
CFG_RLC_CHAIN_CTRL,
|
||||
CFG_TRK_TABLE_OP_MODE,
|
||||
CFG_TRK_TABLE_RSSI_THRESHOLD,
|
||||
CFG_TX_PWR_TARGET, /* Used By xVT */
|
||||
CFG_TX_PWR_LIMIT_USR,
|
||||
CFG_TX_PWR_LIMIT_BSS, /* 11d limit */
|
||||
CFG_TX_PWR_LIMIT_BSS_CONSTRAINT, /* 11h constraint */
|
||||
CFG_TX_PWR_MODE,
|
||||
CFG_MLME_DBG_NOTIF_BLOCK,
|
||||
CFG_BT_OFF_BECONS_INTERVALS,
|
||||
CFG_BT_FRAG_DURATION,
|
||||
|
||||
/* <-- LAST --> */
|
||||
CFG_TBL_FIX_LAST
|
||||
};
|
||||
|
||||
/* variable size table */
|
||||
enum {
|
||||
CFG_NET_ADDR = 0,
|
||||
CFG_PROFILE,
|
||||
/* <-- LAST --> */
|
||||
CFG_TBL_VAR_LAST
|
||||
};
|
||||
|
||||
struct iwm_umac_cmd_set_param_fix {
|
||||
__le16 tbl;
|
||||
__le16 key;
|
||||
__le32 value;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_cmd_set_param_var {
|
||||
__le16 tbl;
|
||||
__le16 key;
|
||||
__le16 len;
|
||||
__le16 reserved;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_cmd_get_param {
|
||||
__le16 tbl;
|
||||
__le16 key;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_cmd_get_param_resp {
|
||||
__le16 tbl;
|
||||
__le16 key;
|
||||
__le16 len;
|
||||
__le16 reserved;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_cmd_eeprom_proxy_hdr {
|
||||
__le32 type;
|
||||
__le32 offset;
|
||||
__le32 len;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_cmd_eeprom_proxy {
|
||||
struct iwm_umac_cmd_eeprom_proxy_hdr hdr;
|
||||
u8 buf[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define IWM_UMAC_CMD_EEPROM_TYPE_READ 0x1
|
||||
#define IWM_UMAC_CMD_EEPROM_TYPE_WRITE 0x2
|
||||
|
||||
#define UMAC_CHANNEL_FLAG_VALID BIT(0)
|
||||
#define UMAC_CHANNEL_FLAG_IBSS BIT(1)
|
||||
#define UMAC_CHANNEL_FLAG_ACTIVE BIT(3)
|
||||
#define UMAC_CHANNEL_FLAG_RADAR BIT(4)
|
||||
#define UMAC_CHANNEL_FLAG_DFS BIT(7)
|
||||
|
||||
struct iwm_umac_channel_info {
|
||||
u8 band;
|
||||
u8 type;
|
||||
u8 reserved;
|
||||
u8 flags;
|
||||
__le32 channels_mask;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_cmd_get_channel_list {
|
||||
__le16 count;
|
||||
__le16 reserved;
|
||||
struct iwm_umac_channel_info ch[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
/* UMAC WiFi interface commands */
|
||||
|
||||
/* Coexistence mode */
|
||||
#define COEX_MODE_SA 0x1
|
||||
#define COEX_MODE_XOR 0x2
|
||||
#define COEX_MODE_CM 0x3
|
||||
#define COEX_MODE_MAX 0x4
|
||||
|
||||
/* Wireless mode */
|
||||
#define WIRELESS_MODE_11A 0x1
|
||||
#define WIRELESS_MODE_11G 0x2
|
||||
|
||||
#define UMAC_PROFILE_EX_IE_REQUIRED 0x1
|
||||
#define UMAC_PROFILE_QOS_ALLOWED 0x2
|
||||
|
||||
/* Scanning */
|
||||
#define UMAC_WIFI_IF_PROBE_OPTION_MAX 10
|
||||
|
||||
#define UMAC_WIFI_IF_SCAN_TYPE_USER 0x0
|
||||
#define UMAC_WIFI_IF_SCAN_TYPE_UMAC_RESERVED 0x1
|
||||
#define UMAC_WIFI_IF_SCAN_TYPE_HOST_PERIODIC 0x2
|
||||
#define UMAC_WIFI_IF_SCAN_TYPE_MAX 0x3
|
||||
|
||||
struct iwm_umac_ssid {
|
||||
u8 ssid_len;
|
||||
u8 ssid[IEEE80211_MAX_SSID_LEN];
|
||||
u8 reserved[3];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_cmd_scan_request {
|
||||
struct iwm_umac_wifi_if hdr;
|
||||
__le32 type; /* UMAC_WIFI_IF_SCAN_TYPE_* */
|
||||
u8 ssid_num;
|
||||
u8 seq_num;
|
||||
u8 timeout; /* In seconds */
|
||||
u8 reserved;
|
||||
struct iwm_umac_ssid ssids[UMAC_WIFI_IF_PROBE_OPTION_MAX];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define UMAC_CIPHER_TYPE_NONE 0xFF
|
||||
#define UMAC_CIPHER_TYPE_USE_GROUPCAST 0x00
|
||||
#define UMAC_CIPHER_TYPE_WEP_40 0x01
|
||||
#define UMAC_CIPHER_TYPE_WEP_104 0x02
|
||||
#define UMAC_CIPHER_TYPE_TKIP 0x04
|
||||
#define UMAC_CIPHER_TYPE_CCMP 0x08
|
||||
|
||||
/* Supported authentication types - bitmap */
|
||||
#define UMAC_AUTH_TYPE_OPEN 0x00
|
||||
#define UMAC_AUTH_TYPE_LEGACY_PSK 0x01
|
||||
#define UMAC_AUTH_TYPE_8021X 0x02
|
||||
#define UMAC_AUTH_TYPE_RSNA_PSK 0x04
|
||||
|
||||
/* iwm_umac_security.flag is WPA supported -- bits[0:0] */
|
||||
#define UMAC_SEC_FLG_WPA_ON_POS 0
|
||||
#define UMAC_SEC_FLG_WPA_ON_SEED 1
|
||||
#define UMAC_SEC_FLG_WPA_ON_MSK (UMAC_SEC_FLG_WPA_ON_SEED << \
|
||||
UMAC_SEC_FLG_WPA_ON_POS)
|
||||
|
||||
/* iwm_umac_security.flag is WPA2 supported -- bits [1:1] */
|
||||
#define UMAC_SEC_FLG_RSNA_ON_POS 1
|
||||
#define UMAC_SEC_FLG_RSNA_ON_SEED 1
|
||||
#define UMAC_SEC_FLG_RSNA_ON_MSK (UMAC_SEC_FLG_RSNA_ON_SEED << \
|
||||
UMAC_SEC_FLG_RSNA_ON_POS)
|
||||
|
||||
/* iwm_umac_security.flag is WSC mode on -- bits [2:2] */
|
||||
#define UMAC_SEC_FLG_WSC_ON_POS 2
|
||||
#define UMAC_SEC_FLG_WSC_ON_SEED 1
|
||||
|
||||
/* Legacy profile can use only WEP40 and WEP104 for encryption and
|
||||
* OPEN or PSK for authentication */
|
||||
#define UMAC_SEC_FLG_LEGACY_PROFILE 0
|
||||
|
||||
struct iwm_umac_security {
|
||||
u8 auth_type;
|
||||
u8 ucast_cipher;
|
||||
u8 mcast_cipher;
|
||||
u8 flags;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_ibss {
|
||||
u8 beacon_interval; /* in millisecond */
|
||||
u8 atim; /* in millisecond */
|
||||
s8 join_only;
|
||||
u8 band;
|
||||
u8 channel;
|
||||
u8 reserved[3];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define UMAC_MODE_BSS 0
|
||||
#define UMAC_MODE_IBSS 1
|
||||
|
||||
#define UMAC_BSSID_MAX 4
|
||||
|
||||
struct iwm_umac_profile {
|
||||
struct iwm_umac_wifi_if hdr;
|
||||
__le32 mode;
|
||||
struct iwm_umac_ssid ssid;
|
||||
u8 bssid[UMAC_BSSID_MAX][ETH_ALEN];
|
||||
struct iwm_umac_security sec;
|
||||
struct iwm_umac_ibss ibss;
|
||||
__le32 channel_2ghz;
|
||||
__le32 channel_5ghz;
|
||||
__le16 flags;
|
||||
u8 wireless_mode;
|
||||
u8 bss_num;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_invalidate_profile {
|
||||
struct iwm_umac_wifi_if hdr;
|
||||
u8 reason;
|
||||
u8 reserved[3];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Encryption key commands */
|
||||
struct iwm_umac_key_wep40 {
|
||||
struct iwm_umac_wifi_if hdr;
|
||||
struct iwm_umac_key_hdr key_hdr;
|
||||
u8 key[WLAN_KEY_LEN_WEP40];
|
||||
u8 static_key;
|
||||
u8 reserved[2];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_key_wep104 {
|
||||
struct iwm_umac_wifi_if hdr;
|
||||
struct iwm_umac_key_hdr key_hdr;
|
||||
u8 key[WLAN_KEY_LEN_WEP104];
|
||||
u8 static_key;
|
||||
u8 reserved[2];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define IWM_TKIP_KEY_SIZE 16
|
||||
#define IWM_TKIP_MIC_SIZE 8
|
||||
struct iwm_umac_key_tkip {
|
||||
struct iwm_umac_wifi_if hdr;
|
||||
struct iwm_umac_key_hdr key_hdr;
|
||||
u8 iv_count[6];
|
||||
u8 reserved[2];
|
||||
u8 tkip_key[IWM_TKIP_KEY_SIZE];
|
||||
u8 mic_rx_key[IWM_TKIP_MIC_SIZE];
|
||||
u8 mic_tx_key[IWM_TKIP_MIC_SIZE];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_key_ccmp {
|
||||
struct iwm_umac_wifi_if hdr;
|
||||
struct iwm_umac_key_hdr key_hdr;
|
||||
u8 iv_count[6];
|
||||
u8 reserved[2];
|
||||
u8 key[WLAN_KEY_LEN_CCMP];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_key_remove {
|
||||
struct iwm_umac_wifi_if hdr;
|
||||
struct iwm_umac_key_hdr key_hdr;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_tx_key_id {
|
||||
struct iwm_umac_wifi_if hdr;
|
||||
u8 key_idx;
|
||||
u8 reserved[3];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_cmd_stats_req {
|
||||
__le32 flags;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* LMAC commands */
|
||||
int iwm_read_mac(struct iwm_priv *iwm, u8 *mac);
|
||||
int iwm_send_prio_table(struct iwm_priv *iwm);
|
||||
int iwm_send_init_calib_cfg(struct iwm_priv *iwm, u8 calib_requested);
|
||||
int iwm_send_periodic_calib_cfg(struct iwm_priv *iwm, u8 calib_requested);
|
||||
int iwm_send_calib_results(struct iwm_priv *iwm);
|
||||
int iwm_store_rxiq_calib_result(struct iwm_priv *iwm);
|
||||
|
||||
/* UMAC commands */
|
||||
int iwm_send_wifi_if_cmd(struct iwm_priv *iwm, void *payload, u16 payload_size,
|
||||
bool resp);
|
||||
int iwm_send_umac_reset(struct iwm_priv *iwm, __le32 reset_flags, bool resp);
|
||||
int iwm_umac_set_config_fix(struct iwm_priv *iwm, u16 tbl, u16 key, u32 value);
|
||||
int iwm_umac_set_config_var(struct iwm_priv *iwm, u16 key,
|
||||
void *payload, u16 payload_size);
|
||||
int iwm_send_umac_config(struct iwm_priv *iwm, __le32 reset_flags);
|
||||
int iwm_send_mlme_profile(struct iwm_priv *iwm);
|
||||
int iwm_invalidate_mlme_profile(struct iwm_priv *iwm);
|
||||
int iwm_send_packet(struct iwm_priv *iwm, struct sk_buff *skb, int pool_id);
|
||||
int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx);
|
||||
int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key,
|
||||
struct iwm_key *key);
|
||||
int iwm_send_umac_stats_req(struct iwm_priv *iwm, u32 flags);
|
||||
int iwm_send_umac_channel_list(struct iwm_priv *iwm);
|
||||
int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids,
|
||||
int ssid_num);
|
||||
int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len);
|
||||
|
||||
/* UDMA commands */
|
||||
int iwm_target_reset(struct iwm_priv *iwm);
|
||||
#endif
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __IWM_DEBUG_H__
|
||||
#define __IWM_DEBUG_H__
|
||||
|
||||
#define IWM_ERR(p, f, a...) dev_err(iwm_to_dev(p), f, ## a)
|
||||
#define IWM_WARN(p, f, a...) dev_warn(iwm_to_dev(p), f, ## a)
|
||||
#define IWM_INFO(p, f, a...) dev_info(iwm_to_dev(p), f, ## a)
|
||||
#define IWM_CRIT(p, f, a...) dev_crit(iwm_to_dev(p), f, ## a)
|
||||
|
||||
#ifdef CONFIG_IWM_DEBUG
|
||||
|
||||
#define IWM_DEBUG_MODULE(i, level, module, f, a...) \
|
||||
do { \
|
||||
if (unlikely(i->dbg.dbg_module[IWM_DM_##module] >= (IWM_DL_##level)))\
|
||||
dev_printk(KERN_INFO, (iwm_to_dev(i)), \
|
||||
"%s " f, __func__ , ## a); \
|
||||
} while (0)
|
||||
|
||||
#define IWM_HEXDUMP(i, level, module, pref, buf, len) \
|
||||
do { \
|
||||
if (unlikely(i->dbg.dbg_module[IWM_DM_##module] >= (IWM_DL_##level)))\
|
||||
print_hex_dump(KERN_INFO, pref, DUMP_PREFIX_OFFSET, \
|
||||
16, 1, buf, len, 1); \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
|
||||
#define IWM_DEBUG_MODULE(i, level, module, f, a...)
|
||||
#define IWM_HEXDUMP(i, level, module, pref, buf, len)
|
||||
|
||||
#endif /* CONFIG_IWM_DEBUG */
|
||||
|
||||
/* Debug modules */
|
||||
enum iwm_debug_module_id {
|
||||
IWM_DM_BOOT = 0,
|
||||
IWM_DM_FW,
|
||||
IWM_DM_SDIO,
|
||||
IWM_DM_NTF,
|
||||
IWM_DM_RX,
|
||||
IWM_DM_TX,
|
||||
IWM_DM_MLME,
|
||||
IWM_DM_CMD,
|
||||
IWM_DM_WEXT,
|
||||
__IWM_DM_NR,
|
||||
};
|
||||
#define IWM_DM_DEFAULT 0
|
||||
|
||||
#define IWM_DBG_BOOT(i, l, f, a...) IWM_DEBUG_MODULE(i, l, BOOT, f, ## a)
|
||||
#define IWM_DBG_FW(i, l, f, a...) IWM_DEBUG_MODULE(i, l, FW, f, ## a)
|
||||
#define IWM_DBG_SDIO(i, l, f, a...) IWM_DEBUG_MODULE(i, l, SDIO, f, ## a)
|
||||
#define IWM_DBG_NTF(i, l, f, a...) IWM_DEBUG_MODULE(i, l, NTF, f, ## a)
|
||||
#define IWM_DBG_RX(i, l, f, a...) IWM_DEBUG_MODULE(i, l, RX, f, ## a)
|
||||
#define IWM_DBG_TX(i, l, f, a...) IWM_DEBUG_MODULE(i, l, TX, f, ## a)
|
||||
#define IWM_DBG_MLME(i, l, f, a...) IWM_DEBUG_MODULE(i, l, MLME, f, ## a)
|
||||
#define IWM_DBG_CMD(i, l, f, a...) IWM_DEBUG_MODULE(i, l, CMD, f, ## a)
|
||||
#define IWM_DBG_WEXT(i, l, f, a...) IWM_DEBUG_MODULE(i, l, WEXT, f, ## a)
|
||||
|
||||
/* Debug levels */
|
||||
enum iwm_debug_level {
|
||||
IWM_DL_NONE = 0,
|
||||
IWM_DL_ERR,
|
||||
IWM_DL_WARN,
|
||||
IWM_DL_INFO,
|
||||
IWM_DL_DBG,
|
||||
};
|
||||
#define IWM_DL_DEFAULT IWM_DL_ERR
|
||||
|
||||
struct iwm_debugfs {
|
||||
struct iwm_priv *iwm;
|
||||
struct dentry *rootdir;
|
||||
struct dentry *devdir;
|
||||
struct dentry *dbgdir;
|
||||
struct dentry *txdir;
|
||||
struct dentry *rxdir;
|
||||
struct dentry *busdir;
|
||||
|
||||
u32 dbg_level;
|
||||
struct dentry *dbg_level_dentry;
|
||||
|
||||
unsigned long dbg_modules;
|
||||
struct dentry *dbg_modules_dentry;
|
||||
|
||||
u8 dbg_module[__IWM_DM_NR];
|
||||
struct dentry *dbg_module_dentries[__IWM_DM_NR];
|
||||
|
||||
struct dentry *txq_dentry;
|
||||
struct dentry *tx_credit_dentry;
|
||||
struct dentry *rx_ticket_dentry;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IWM_DEBUG
|
||||
int iwm_debugfs_init(struct iwm_priv *iwm);
|
||||
void iwm_debugfs_exit(struct iwm_priv *iwm);
|
||||
#else
|
||||
static inline int iwm_debugfs_init(struct iwm_priv *iwm)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void iwm_debugfs_exit(struct iwm_priv *iwm) {}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,453 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include "iwm.h"
|
||||
#include "bus.h"
|
||||
#include "rx.h"
|
||||
#include "debug.h"
|
||||
|
||||
static struct {
|
||||
u8 id;
|
||||
char *name;
|
||||
} iwm_debug_module[__IWM_DM_NR] = {
|
||||
{IWM_DM_BOOT, "boot"},
|
||||
{IWM_DM_FW, "fw"},
|
||||
{IWM_DM_SDIO, "sdio"},
|
||||
{IWM_DM_NTF, "ntf"},
|
||||
{IWM_DM_RX, "rx"},
|
||||
{IWM_DM_TX, "tx"},
|
||||
{IWM_DM_MLME, "mlme"},
|
||||
{IWM_DM_CMD, "cmd"},
|
||||
{IWM_DM_WEXT, "wext"},
|
||||
};
|
||||
|
||||
#define add_dbg_module(dbg, name, id, initlevel) \
|
||||
do { \
|
||||
struct dentry *d; \
|
||||
dbg.dbg_module[id] = (initlevel); \
|
||||
d = debugfs_create_x8(name, 0600, dbg.dbgdir, \
|
||||
&(dbg.dbg_module[id])); \
|
||||
if (!IS_ERR(d)) \
|
||||
dbg.dbg_module_dentries[id] = d; \
|
||||
} while (0)
|
||||
|
||||
static int iwm_debugfs_u32_read(void *data, u64 *val)
|
||||
{
|
||||
struct iwm_priv *iwm = data;
|
||||
|
||||
*val = iwm->dbg.dbg_level;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwm_debugfs_dbg_level_write(void *data, u64 val)
|
||||
{
|
||||
struct iwm_priv *iwm = data;
|
||||
int i;
|
||||
|
||||
iwm->dbg.dbg_level = val;
|
||||
|
||||
for (i = 0; i < __IWM_DM_NR; i++)
|
||||
iwm->dbg.dbg_module[i] = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_iwm_dbg_level,
|
||||
iwm_debugfs_u32_read, iwm_debugfs_dbg_level_write,
|
||||
"%llu\n");
|
||||
|
||||
static int iwm_debugfs_dbg_modules_write(void *data, u64 val)
|
||||
{
|
||||
struct iwm_priv *iwm = data;
|
||||
int i, bit;
|
||||
|
||||
iwm->dbg.dbg_modules = val;
|
||||
|
||||
for (i = 0; i < __IWM_DM_NR; i++)
|
||||
iwm->dbg.dbg_module[i] = 0;
|
||||
|
||||
for_each_bit(bit, &iwm->dbg.dbg_modules, __IWM_DM_NR)
|
||||
iwm->dbg.dbg_module[bit] = iwm->dbg.dbg_level;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(fops_iwm_dbg_modules,
|
||||
iwm_debugfs_u32_read, iwm_debugfs_dbg_modules_write,
|
||||
"%llu\n");
|
||||
|
||||
static int iwm_txrx_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
filp->private_data = inode->i_private;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t iwm_debugfs_txq_read(struct file *filp, char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct iwm_priv *iwm = filp->private_data;
|
||||
char *buf;
|
||||
int i, buf_len = 4096;
|
||||
size_t len = 0;
|
||||
ssize_t ret;
|
||||
|
||||
if (*ppos != 0)
|
||||
return 0;
|
||||
if (count < sizeof(buf))
|
||||
return -ENOSPC;
|
||||
|
||||
buf = kzalloc(buf_len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < IWM_TX_QUEUES; i++) {
|
||||
struct iwm_tx_queue *txq = &iwm->txq[i];
|
||||
struct sk_buff *skb;
|
||||
int j;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&txq->queue.lock, flags);
|
||||
|
||||
skb = (struct sk_buff *)&txq->queue;
|
||||
|
||||
len += snprintf(buf + len, buf_len - len, "TXQ #%d\n", i);
|
||||
len += snprintf(buf + len, buf_len - len, "\tStopped: %d\n",
|
||||
__netif_subqueue_stopped(iwm_to_ndev(iwm),
|
||||
txq->id));
|
||||
len += snprintf(buf + len, buf_len - len, "\tConcat count:%d\n",
|
||||
txq->concat_count);
|
||||
len += snprintf(buf + len, buf_len - len, "\tQueue len: %d\n",
|
||||
skb_queue_len(&txq->queue));
|
||||
for (j = 0; j < skb_queue_len(&txq->queue); j++) {
|
||||
struct iwm_tx_info *tx_info;
|
||||
|
||||
skb = skb->next;
|
||||
tx_info = skb_to_tx_info(skb);
|
||||
|
||||
len += snprintf(buf + len, buf_len - len,
|
||||
"\tSKB #%d\n", j);
|
||||
len += snprintf(buf + len, buf_len - len,
|
||||
"\t\tsta: %d\n", tx_info->sta);
|
||||
len += snprintf(buf + len, buf_len - len,
|
||||
"\t\tcolor: %d\n", tx_info->color);
|
||||
len += snprintf(buf + len, buf_len - len,
|
||||
"\t\ttid: %d\n", tx_info->tid);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&txq->queue.lock, flags);
|
||||
}
|
||||
|
||||
ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
|
||||
kfree(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t iwm_debugfs_tx_credit_read(struct file *filp,
|
||||
char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct iwm_priv *iwm = filp->private_data;
|
||||
struct iwm_tx_credit *credit = &iwm->tx_credit;
|
||||
char *buf;
|
||||
int i, buf_len = 4096;
|
||||
size_t len = 0;
|
||||
ssize_t ret;
|
||||
|
||||
if (*ppos != 0)
|
||||
return 0;
|
||||
if (count < sizeof(buf))
|
||||
return -ENOSPC;
|
||||
|
||||
buf = kzalloc(buf_len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
len += snprintf(buf + len, buf_len - len,
|
||||
"NR pools: %d\n", credit->pool_nr);
|
||||
len += snprintf(buf + len, buf_len - len,
|
||||
"pools map: 0x%lx\n", credit->full_pools_map);
|
||||
|
||||
len += snprintf(buf + len, buf_len - len, "\n### POOLS ###\n");
|
||||
for (i = 0; i < IWM_MACS_OUT_GROUPS; i++) {
|
||||
len += snprintf(buf + len, buf_len - len,
|
||||
"pools entry #%d\n", i);
|
||||
len += snprintf(buf + len, buf_len - len,
|
||||
"\tid: %d\n",
|
||||
credit->pools[i].id);
|
||||
len += snprintf(buf + len, buf_len - len,
|
||||
"\tsid: %d\n",
|
||||
credit->pools[i].sid);
|
||||
len += snprintf(buf + len, buf_len - len,
|
||||
"\tmin_pages: %d\n",
|
||||
credit->pools[i].min_pages);
|
||||
len += snprintf(buf + len, buf_len - len,
|
||||
"\tmax_pages: %d\n",
|
||||
credit->pools[i].max_pages);
|
||||
len += snprintf(buf + len, buf_len - len,
|
||||
"\talloc_pages: %d\n",
|
||||
credit->pools[i].alloc_pages);
|
||||
len += snprintf(buf + len, buf_len - len,
|
||||
"\tfreed_pages: %d\n",
|
||||
credit->pools[i].total_freed_pages);
|
||||
}
|
||||
|
||||
len += snprintf(buf + len, buf_len - len, "\n### SPOOLS ###\n");
|
||||
for (i = 0; i < IWM_MACS_OUT_SGROUPS; i++) {
|
||||
len += snprintf(buf + len, buf_len - len,
|
||||
"spools entry #%d\n", i);
|
||||
len += snprintf(buf + len, buf_len - len,
|
||||
"\tid: %d\n",
|
||||
credit->spools[i].id);
|
||||
len += snprintf(buf + len, buf_len - len,
|
||||
"\tmax_pages: %d\n",
|
||||
credit->spools[i].max_pages);
|
||||
len += snprintf(buf + len, buf_len - len,
|
||||
"\talloc_pages: %d\n",
|
||||
credit->spools[i].alloc_pages);
|
||||
|
||||
}
|
||||
|
||||
ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
|
||||
kfree(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t iwm_debugfs_rx_ticket_read(struct file *filp,
|
||||
char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct iwm_priv *iwm = filp->private_data;
|
||||
struct iwm_rx_ticket_node *ticket, *next;
|
||||
char *buf;
|
||||
int buf_len = 4096, i;
|
||||
size_t len = 0;
|
||||
ssize_t ret;
|
||||
|
||||
if (*ppos != 0)
|
||||
return 0;
|
||||
if (count < sizeof(buf))
|
||||
return -ENOSPC;
|
||||
|
||||
buf = kzalloc(buf_len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
list_for_each_entry_safe(ticket, next, &iwm->rx_tickets, node) {
|
||||
len += snprintf(buf + len, buf_len - len, "Ticket #%d\n",
|
||||
ticket->ticket->id);
|
||||
len += snprintf(buf + len, buf_len - len, "\taction: 0x%x\n",
|
||||
ticket->ticket->action);
|
||||
len += snprintf(buf + len, buf_len - len, "\tflags: 0x%x\n",
|
||||
ticket->ticket->flags);
|
||||
}
|
||||
|
||||
for (i = 0; i < IWM_RX_ID_HASH; i++) {
|
||||
struct iwm_rx_packet *packet, *nxt;
|
||||
struct list_head *pkt_list = &iwm->rx_packets[i];
|
||||
if (!list_empty(pkt_list)) {
|
||||
len += snprintf(buf + len, buf_len - len,
|
||||
"Packet hash #%d\n", i);
|
||||
list_for_each_entry_safe(packet, nxt, pkt_list, node) {
|
||||
len += snprintf(buf + len, buf_len - len,
|
||||
"\tPacket id: %d\n",
|
||||
packet->id);
|
||||
len += snprintf(buf + len, buf_len - len,
|
||||
"\tPacket length: %lu\n",
|
||||
packet->pkt_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
|
||||
kfree(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static const struct file_operations iwm_debugfs_txq_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = iwm_txrx_open,
|
||||
.read = iwm_debugfs_txq_read,
|
||||
};
|
||||
|
||||
static const struct file_operations iwm_debugfs_tx_credit_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = iwm_txrx_open,
|
||||
.read = iwm_debugfs_tx_credit_read,
|
||||
};
|
||||
|
||||
static const struct file_operations iwm_debugfs_rx_ticket_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = iwm_txrx_open,
|
||||
.read = iwm_debugfs_rx_ticket_read,
|
||||
};
|
||||
|
||||
int iwm_debugfs_init(struct iwm_priv *iwm)
|
||||
{
|
||||
int i, result;
|
||||
char devdir[16];
|
||||
|
||||
iwm->dbg.rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
|
||||
result = PTR_ERR(iwm->dbg.rootdir);
|
||||
if (!result || IS_ERR(iwm->dbg.rootdir)) {
|
||||
if (result == -ENODEV) {
|
||||
IWM_ERR(iwm, "DebugFS (CONFIG_DEBUG_FS) not "
|
||||
"enabled in kernel config\n");
|
||||
result = 0; /* No debugfs support */
|
||||
}
|
||||
IWM_ERR(iwm, "Couldn't create rootdir: %d\n", result);
|
||||
goto error;
|
||||
}
|
||||
|
||||
snprintf(devdir, sizeof(devdir), "%s", wiphy_name(iwm_to_wiphy(iwm)));
|
||||
|
||||
iwm->dbg.devdir = debugfs_create_dir(devdir, iwm->dbg.rootdir);
|
||||
result = PTR_ERR(iwm->dbg.devdir);
|
||||
if (IS_ERR(iwm->dbg.devdir) && (result != -ENODEV)) {
|
||||
IWM_ERR(iwm, "Couldn't create devdir: %d\n", result);
|
||||
goto error;
|
||||
}
|
||||
|
||||
iwm->dbg.dbgdir = debugfs_create_dir("debug", iwm->dbg.devdir);
|
||||
result = PTR_ERR(iwm->dbg.dbgdir);
|
||||
if (IS_ERR(iwm->dbg.dbgdir) && (result != -ENODEV)) {
|
||||
IWM_ERR(iwm, "Couldn't create dbgdir: %d\n", result);
|
||||
goto error;
|
||||
}
|
||||
|
||||
iwm->dbg.rxdir = debugfs_create_dir("rx", iwm->dbg.devdir);
|
||||
result = PTR_ERR(iwm->dbg.rxdir);
|
||||
if (IS_ERR(iwm->dbg.rxdir) && (result != -ENODEV)) {
|
||||
IWM_ERR(iwm, "Couldn't create rx dir: %d\n", result);
|
||||
goto error;
|
||||
}
|
||||
|
||||
iwm->dbg.txdir = debugfs_create_dir("tx", iwm->dbg.devdir);
|
||||
result = PTR_ERR(iwm->dbg.txdir);
|
||||
if (IS_ERR(iwm->dbg.txdir) && (result != -ENODEV)) {
|
||||
IWM_ERR(iwm, "Couldn't create tx dir: %d\n", result);
|
||||
goto error;
|
||||
}
|
||||
|
||||
iwm->dbg.busdir = debugfs_create_dir("bus", iwm->dbg.devdir);
|
||||
result = PTR_ERR(iwm->dbg.busdir);
|
||||
if (IS_ERR(iwm->dbg.busdir) && (result != -ENODEV)) {
|
||||
IWM_ERR(iwm, "Couldn't create bus dir: %d\n", result);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (iwm->bus_ops->debugfs_init) {
|
||||
result = iwm->bus_ops->debugfs_init(iwm, iwm->dbg.busdir);
|
||||
if (result < 0) {
|
||||
IWM_ERR(iwm, "Couldn't create bus entry: %d\n", result);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
iwm->dbg.dbg_level = IWM_DL_NONE;
|
||||
iwm->dbg.dbg_level_dentry =
|
||||
debugfs_create_file("level", 0200, iwm->dbg.dbgdir, iwm,
|
||||
&fops_iwm_dbg_level);
|
||||
result = PTR_ERR(iwm->dbg.dbg_level_dentry);
|
||||
if (IS_ERR(iwm->dbg.dbg_level_dentry) && (result != -ENODEV)) {
|
||||
IWM_ERR(iwm, "Couldn't create dbg_level: %d\n", result);
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
iwm->dbg.dbg_modules = IWM_DM_DEFAULT;
|
||||
iwm->dbg.dbg_modules_dentry =
|
||||
debugfs_create_file("modules", 0200, iwm->dbg.dbgdir, iwm,
|
||||
&fops_iwm_dbg_modules);
|
||||
result = PTR_ERR(iwm->dbg.dbg_modules_dentry);
|
||||
if (IS_ERR(iwm->dbg.dbg_modules_dentry) && (result != -ENODEV)) {
|
||||
IWM_ERR(iwm, "Couldn't create dbg_modules: %d\n", result);
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (i = 0; i < __IWM_DM_NR; i++)
|
||||
add_dbg_module(iwm->dbg, iwm_debug_module[i].name,
|
||||
iwm_debug_module[i].id, IWM_DL_DEFAULT);
|
||||
|
||||
iwm->dbg.txq_dentry = debugfs_create_file("queues", 0200,
|
||||
iwm->dbg.txdir, iwm,
|
||||
&iwm_debugfs_txq_fops);
|
||||
result = PTR_ERR(iwm->dbg.txq_dentry);
|
||||
if (IS_ERR(iwm->dbg.txq_dentry) && (result != -ENODEV)) {
|
||||
IWM_ERR(iwm, "Couldn't create tx queue: %d\n", result);
|
||||
goto error;
|
||||
}
|
||||
|
||||
iwm->dbg.tx_credit_dentry = debugfs_create_file("credits", 0200,
|
||||
iwm->dbg.txdir, iwm,
|
||||
&iwm_debugfs_tx_credit_fops);
|
||||
result = PTR_ERR(iwm->dbg.tx_credit_dentry);
|
||||
if (IS_ERR(iwm->dbg.tx_credit_dentry) && (result != -ENODEV)) {
|
||||
IWM_ERR(iwm, "Couldn't create tx credit: %d\n", result);
|
||||
goto error;
|
||||
}
|
||||
|
||||
iwm->dbg.rx_ticket_dentry = debugfs_create_file("tickets", 0200,
|
||||
iwm->dbg.rxdir, iwm,
|
||||
&iwm_debugfs_rx_ticket_fops);
|
||||
result = PTR_ERR(iwm->dbg.rx_ticket_dentry);
|
||||
if (IS_ERR(iwm->dbg.rx_ticket_dentry) && (result != -ENODEV)) {
|
||||
IWM_ERR(iwm, "Couldn't create rx ticket: %d\n", result);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
return result;
|
||||
}
|
||||
|
||||
void iwm_debugfs_exit(struct iwm_priv *iwm)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < __IWM_DM_NR; i++)
|
||||
debugfs_remove(iwm->dbg.dbg_module_dentries[i]);
|
||||
|
||||
debugfs_remove(iwm->dbg.dbg_modules_dentry);
|
||||
debugfs_remove(iwm->dbg.dbg_level_dentry);
|
||||
debugfs_remove(iwm->dbg.txq_dentry);
|
||||
debugfs_remove(iwm->dbg.tx_credit_dentry);
|
||||
debugfs_remove(iwm->dbg.rx_ticket_dentry);
|
||||
if (iwm->bus_ops->debugfs_exit)
|
||||
iwm->bus_ops->debugfs_exit(iwm);
|
||||
|
||||
debugfs_remove(iwm->dbg.busdir);
|
||||
debugfs_remove(iwm->dbg.dbgdir);
|
||||
debugfs_remove(iwm->dbg.txdir);
|
||||
debugfs_remove(iwm->dbg.rxdir);
|
||||
debugfs_remove(iwm->dbg.devdir);
|
||||
debugfs_remove(iwm->dbg.rootdir);
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "iwm.h"
|
||||
#include "umac.h"
|
||||
#include "commands.h"
|
||||
#include "eeprom.h"
|
||||
|
||||
static struct iwm_eeprom_entry eeprom_map[] = {
|
||||
[IWM_EEPROM_SIG] =
|
||||
{"Signature", IWM_EEPROM_SIG_OFF, IWM_EEPROM_SIG_LEN},
|
||||
|
||||
[IWM_EEPROM_VERSION] =
|
||||
{"Version", IWM_EEPROM_VERSION_OFF, IWM_EEPROM_VERSION_LEN},
|
||||
|
||||
[IWM_EEPROM_OEM_HW_VERSION] =
|
||||
{"OEM HW version", IWM_EEPROM_OEM_HW_VERSION_OFF,
|
||||
IWM_EEPROM_OEM_HW_VERSION_LEN},
|
||||
|
||||
[IWM_EEPROM_MAC_VERSION] =
|
||||
{"MAC version", IWM_EEPROM_MAC_VERSION_OFF, IWM_EEPROM_MAC_VERSION_LEN},
|
||||
|
||||
[IWM_EEPROM_CARD_ID] =
|
||||
{"Card ID", IWM_EEPROM_CARD_ID_OFF, IWM_EEPROM_CARD_ID_LEN},
|
||||
|
||||
[IWM_EEPROM_RADIO_CONF] =
|
||||
{"Radio config", IWM_EEPROM_RADIO_CONF_OFF, IWM_EEPROM_RADIO_CONF_LEN},
|
||||
|
||||
[IWM_EEPROM_SKU_CAP] =
|
||||
{"SKU capabilities", IWM_EEPROM_SKU_CAP_OFF, IWM_EEPROM_SKU_CAP_LEN},
|
||||
|
||||
[IWM_EEPROM_CALIB_RXIQ_OFFSET] =
|
||||
{"RX IQ offset", IWM_EEPROM_CALIB_RXIQ_OFF, IWM_EEPROM_INDIRECT_LEN},
|
||||
|
||||
[IWM_EEPROM_CALIB_RXIQ] =
|
||||
{"Calib RX IQ", 0, IWM_EEPROM_CALIB_RXIQ_LEN},
|
||||
};
|
||||
|
||||
|
||||
static int iwm_eeprom_read(struct iwm_priv *iwm, u8 eeprom_id)
|
||||
{
|
||||
int ret;
|
||||
u32 entry_size, chunk_size, data_offset = 0, addr_offset = 0;
|
||||
u32 addr;
|
||||
struct iwm_udma_wifi_cmd udma_cmd;
|
||||
struct iwm_umac_cmd umac_cmd;
|
||||
struct iwm_umac_cmd_eeprom_proxy eeprom_cmd;
|
||||
|
||||
if (eeprom_id > (IWM_EEPROM_LAST - 1))
|
||||
return -EINVAL;
|
||||
|
||||
entry_size = eeprom_map[eeprom_id].length;
|
||||
|
||||
if (eeprom_id >= IWM_EEPROM_INDIRECT_DATA) {
|
||||
/* indirect data */
|
||||
u32 off_id = eeprom_id - IWM_EEPROM_INDIRECT_DATA +
|
||||
IWM_EEPROM_INDIRECT_OFFSET;
|
||||
|
||||
eeprom_map[eeprom_id].offset =
|
||||
*(u16 *)(iwm->eeprom + eeprom_map[off_id].offset) << 1;
|
||||
}
|
||||
|
||||
addr = eeprom_map[eeprom_id].offset;
|
||||
|
||||
udma_cmd.eop = 1;
|
||||
udma_cmd.credit_group = 0x4;
|
||||
udma_cmd.ra_tid = UMAC_HDI_ACT_TBL_IDX_HOST_CMD;
|
||||
udma_cmd.lmac_offset = 0;
|
||||
|
||||
umac_cmd.id = UMAC_CMD_OPCODE_EEPROM_PROXY;
|
||||
umac_cmd.resp = 1;
|
||||
|
||||
while (entry_size > 0) {
|
||||
chunk_size = min_t(u32, entry_size, IWM_MAX_EEPROM_DATA_LEN);
|
||||
|
||||
eeprom_cmd.hdr.type =
|
||||
cpu_to_le32(IWM_UMAC_CMD_EEPROM_TYPE_READ);
|
||||
eeprom_cmd.hdr.offset = cpu_to_le32(addr + addr_offset);
|
||||
eeprom_cmd.hdr.len = cpu_to_le32(chunk_size);
|
||||
|
||||
ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd,
|
||||
&umac_cmd, &eeprom_cmd,
|
||||
sizeof(struct iwm_umac_cmd_eeprom_proxy));
|
||||
if (ret < 0) {
|
||||
IWM_ERR(iwm, "Couldn't read eeprom\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_EEPROM_PROXY,
|
||||
IWM_SRC_UMAC, 2*HZ);
|
||||
if (ret < 0) {
|
||||
IWM_ERR(iwm, "Did not get any eeprom answer\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data_offset += chunk_size;
|
||||
addr_offset += chunk_size;
|
||||
entry_size -= chunk_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 *iwm_eeprom_access(struct iwm_priv *iwm, u8 eeprom_id)
|
||||
{
|
||||
if (!iwm->eeprom)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return iwm->eeprom + eeprom_map[eeprom_id].offset;
|
||||
}
|
||||
|
||||
int iwm_eeprom_init(struct iwm_priv *iwm)
|
||||
{
|
||||
int i, ret = 0;
|
||||
char name[32];
|
||||
|
||||
iwm->eeprom = kzalloc(IWM_EEPROM_LEN, GFP_KERNEL);
|
||||
if (!iwm->eeprom)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = IWM_EEPROM_FIRST; i < IWM_EEPROM_LAST; i++) {
|
||||
#ifdef CONFIG_IWM_B0_HW_SUPPORT
|
||||
if (iwm->conf.hw_b0 && (i >= IWM_EEPROM_INDIRECT_OFFSET))
|
||||
break;
|
||||
#endif
|
||||
ret = iwm_eeprom_read(iwm, i);
|
||||
if (ret < 0) {
|
||||
IWM_ERR(iwm, "Couldn't read eeprom entry #%d: %s\n",
|
||||
i, eeprom_map[i].name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
IWM_DBG_BOOT(iwm, DBG, "EEPROM dump:\n");
|
||||
for (i = IWM_EEPROM_FIRST; i < IWM_EEPROM_LAST; i++) {
|
||||
memset(name, 0, 32);
|
||||
sprintf(name, "%s: ", eeprom_map[i].name);
|
||||
|
||||
IWM_HEXDUMP(iwm, DBG, BOOT, name,
|
||||
iwm->eeprom + eeprom_map[i].offset,
|
||||
eeprom_map[i].length);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void iwm_eeprom_exit(struct iwm_priv *iwm)
|
||||
{
|
||||
kfree(iwm->eeprom);
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __IWM_EEPROM_H__
|
||||
#define __IWM_EEPROM_H__
|
||||
|
||||
enum {
|
||||
IWM_EEPROM_SIG = 0,
|
||||
IWM_EEPROM_FIRST = IWM_EEPROM_SIG,
|
||||
IWM_EEPROM_VERSION,
|
||||
IWM_EEPROM_OEM_HW_VERSION,
|
||||
IWM_EEPROM_MAC_VERSION,
|
||||
IWM_EEPROM_CARD_ID,
|
||||
IWM_EEPROM_RADIO_CONF,
|
||||
IWM_EEPROM_SKU_CAP,
|
||||
|
||||
IWM_EEPROM_INDIRECT_OFFSET,
|
||||
IWM_EEPROM_CALIB_RXIQ_OFFSET = IWM_EEPROM_INDIRECT_OFFSET,
|
||||
|
||||
IWM_EEPROM_INDIRECT_DATA,
|
||||
IWM_EEPROM_CALIB_RXIQ = IWM_EEPROM_INDIRECT_DATA,
|
||||
|
||||
IWM_EEPROM_LAST,
|
||||
};
|
||||
|
||||
#define IWM_EEPROM_SIG_OFF 0x00
|
||||
#define IWM_EEPROM_VERSION_OFF (0x54 << 1)
|
||||
#define IWM_EEPROM_OEM_HW_VERSION_OFF (0x56 << 1)
|
||||
#define IWM_EEPROM_MAC_VERSION_OFF (0x30 << 1)
|
||||
#define IWM_EEPROM_CARD_ID_OFF (0x5d << 1)
|
||||
#define IWM_EEPROM_RADIO_CONF_OFF (0x58 << 1)
|
||||
#define IWM_EEPROM_SKU_CAP_OFF (0x55 << 1)
|
||||
#define IWM_EEPROM_CALIB_CONFIG_OFF (0x7c << 1)
|
||||
|
||||
#define IWM_EEPROM_SIG_LEN 4
|
||||
#define IWM_EEPROM_VERSION_LEN 2
|
||||
#define IWM_EEPROM_OEM_HW_VERSION_LEN 2
|
||||
#define IWM_EEPROM_MAC_VERSION_LEN 1
|
||||
#define IWM_EEPROM_CARD_ID_LEN 2
|
||||
#define IWM_EEPROM_RADIO_CONF_LEN 2
|
||||
#define IWM_EEPROM_SKU_CAP_LEN 2
|
||||
#define IWM_EEPROM_INDIRECT_LEN 2
|
||||
|
||||
#define IWM_MAX_EEPROM_DATA_LEN 240
|
||||
#define IWM_EEPROM_LEN 0x800
|
||||
|
||||
#define IWM_EEPROM_MIN_ALLOWED_VERSION 0x0610
|
||||
#define IWM_EEPROM_MAX_ALLOWED_VERSION 0x0700
|
||||
#define IWM_EEPROM_CURRENT_VERSION 0x0612
|
||||
|
||||
#define IWM_EEPROM_SKU_CAP_BAND_24GHZ (1 << 4)
|
||||
#define IWM_EEPROM_SKU_CAP_BAND_52GHZ (1 << 5)
|
||||
#define IWM_EEPROM_SKU_CAP_11N_ENABLE (1 << 6)
|
||||
|
||||
enum {
|
||||
IWM_EEPROM_CALIB_CAL_HDR,
|
||||
IWM_EEPROM_CALIB_TX_POWER,
|
||||
IWM_EEPROM_CALIB_XTAL,
|
||||
IWM_EEPROM_CALIB_TEMPERATURE,
|
||||
IWM_EEPROM_CALIB_RX_BB_FILTER,
|
||||
IWM_EEPROM_CALIB_RX_IQ,
|
||||
IWM_EEPROM_CALIB_MAX,
|
||||
};
|
||||
|
||||
#define IWM_EEPROM_CALIB_RXIQ_OFF (IWM_EEPROM_CALIB_CONFIG_OFF + \
|
||||
(IWM_EEPROM_CALIB_RX_IQ << 1))
|
||||
#define IWM_EEPROM_CALIB_RXIQ_LEN sizeof(struct iwm_lmac_calib_rxiq)
|
||||
|
||||
struct iwm_eeprom_entry {
|
||||
char *name;
|
||||
u32 offset;
|
||||
u32 length;
|
||||
};
|
||||
|
||||
int iwm_eeprom_init(struct iwm_priv *iwm);
|
||||
void iwm_eeprom_exit(struct iwm_priv *iwm);
|
||||
u8 *iwm_eeprom_access(struct iwm_priv *iwm, u8 eeprom_id);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,388 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include "iwm.h"
|
||||
#include "bus.h"
|
||||
#include "hal.h"
|
||||
#include "umac.h"
|
||||
#include "debug.h"
|
||||
#include "fw.h"
|
||||
#include "commands.h"
|
||||
|
||||
static const char fw_barker[] = "*WESTOPFORNOONE*";
|
||||
|
||||
/*
|
||||
* @op_code: Op code we're looking for.
|
||||
* @index: There can be several instances of the same opcode within
|
||||
* the firmware. Index specifies which one we're looking for.
|
||||
*/
|
||||
static int iwm_fw_op_offset(struct iwm_priv *iwm, const struct firmware *fw,
|
||||
u16 op_code, u32 index)
|
||||
{
|
||||
int offset = -EINVAL, fw_offset;
|
||||
u32 op_index = 0;
|
||||
const u8 *fw_ptr;
|
||||
struct iwm_fw_hdr_rec *rec;
|
||||
|
||||
fw_offset = 0;
|
||||
fw_ptr = fw->data;
|
||||
|
||||
/* We first need to look for the firmware barker */
|
||||
if (memcmp(fw_ptr, fw_barker, IWM_HDR_BARKER_LEN)) {
|
||||
IWM_ERR(iwm, "No barker string in this FW\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fw->size < IWM_HDR_LEN) {
|
||||
IWM_ERR(iwm, "FW is too small (%d)\n", fw->size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fw_offset += IWM_HDR_BARKER_LEN;
|
||||
|
||||
while (fw_offset < fw->size) {
|
||||
rec = (struct iwm_fw_hdr_rec *)(fw_ptr + fw_offset);
|
||||
|
||||
IWM_DBG_FW(iwm, DBG, "FW: op_code: 0x%x, len: %d @ 0x%x\n",
|
||||
rec->op_code, rec->len, fw_offset);
|
||||
|
||||
if (rec->op_code == IWM_HDR_REC_OP_INVALID) {
|
||||
IWM_DBG_FW(iwm, DBG, "Reached INVALID op code\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (rec->op_code == op_code) {
|
||||
if (op_index == index) {
|
||||
fw_offset += sizeof(struct iwm_fw_hdr_rec);
|
||||
offset = fw_offset;
|
||||
goto out;
|
||||
}
|
||||
op_index++;
|
||||
}
|
||||
|
||||
fw_offset += sizeof(struct iwm_fw_hdr_rec) + rec->len;
|
||||
}
|
||||
|
||||
out:
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int iwm_load_firmware_chunk(struct iwm_priv *iwm,
|
||||
const struct firmware *fw,
|
||||
struct iwm_fw_img_desc *img_desc)
|
||||
{
|
||||
struct iwm_udma_nonwifi_cmd target_cmd;
|
||||
u32 chunk_size;
|
||||
const u8 *chunk_ptr;
|
||||
int ret = 0;
|
||||
|
||||
IWM_DBG_FW(iwm, INFO, "Loading FW chunk: %d bytes @ 0x%x\n",
|
||||
img_desc->length, img_desc->address);
|
||||
|
||||
target_cmd.opcode = UMAC_HDI_OUT_OPCODE_WRITE;
|
||||
target_cmd.handle_by_hw = 1;
|
||||
target_cmd.op2 = 0;
|
||||
target_cmd.resp = 0;
|
||||
target_cmd.eop = 1;
|
||||
|
||||
chunk_size = img_desc->length;
|
||||
chunk_ptr = fw->data + img_desc->offset;
|
||||
|
||||
while (chunk_size > 0) {
|
||||
u32 tmp_chunk_size;
|
||||
|
||||
tmp_chunk_size = min_t(u32, chunk_size,
|
||||
IWM_MAX_NONWIFI_CMD_BUFF_SIZE);
|
||||
|
||||
target_cmd.addr = cpu_to_le32(img_desc->address +
|
||||
(chunk_ptr - fw->data - img_desc->offset));
|
||||
target_cmd.op1_sz = cpu_to_le32(tmp_chunk_size);
|
||||
|
||||
IWM_DBG_FW(iwm, DBG, "\t%d bytes @ 0x%x\n",
|
||||
tmp_chunk_size, target_cmd.addr);
|
||||
|
||||
ret = iwm_hal_send_target_cmd(iwm, &target_cmd, chunk_ptr);
|
||||
if (ret < 0) {
|
||||
IWM_ERR(iwm, "Couldn't load FW chunk\n");
|
||||
break;
|
||||
}
|
||||
|
||||
chunk_size -= tmp_chunk_size;
|
||||
chunk_ptr += tmp_chunk_size;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* To load a fw image to the target, we basically go through the
|
||||
* fw, looking for OP_MEM_DESC records. Once we found one, we
|
||||
* pass it to iwm_load_firmware_chunk().
|
||||
* The OP_MEM_DESC records contain the actuall memory chunk to be
|
||||
* sent, but also the destination address.
|
||||
*/
|
||||
static int iwm_load_img(struct iwm_priv *iwm, const char *img_name)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
struct iwm_fw_img_desc *img_desc;
|
||||
struct iwm_fw_img_ver *ver;
|
||||
int ret = 0, fw_offset;
|
||||
u32 opcode_idx = 0, build_date;
|
||||
char *build_tag;
|
||||
|
||||
ret = request_firmware(&fw, img_name, iwm_to_dev(iwm));
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Request firmware failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
IWM_DBG_FW(iwm, INFO, "Start to load FW %s\n", img_name);
|
||||
|
||||
while (1) {
|
||||
fw_offset = iwm_fw_op_offset(iwm, fw,
|
||||
IWM_HDR_REC_OP_MEM_DESC,
|
||||
opcode_idx);
|
||||
if (fw_offset < 0)
|
||||
break;
|
||||
|
||||
img_desc = (struct iwm_fw_img_desc *)(fw->data + fw_offset);
|
||||
ret = iwm_load_firmware_chunk(iwm, fw, img_desc);
|
||||
if (ret < 0)
|
||||
goto err_release_fw;
|
||||
opcode_idx++;
|
||||
};
|
||||
|
||||
/* Read firmware version */
|
||||
fw_offset = iwm_fw_op_offset(iwm, fw, IWM_HDR_REC_OP_SW_VER, 0);
|
||||
if (fw_offset < 0)
|
||||
goto err_release_fw;
|
||||
|
||||
ver = (struct iwm_fw_img_ver *)(fw->data + fw_offset);
|
||||
|
||||
/* Read build tag */
|
||||
fw_offset = iwm_fw_op_offset(iwm, fw, IWM_HDR_REC_OP_BUILD_TAG, 0);
|
||||
if (fw_offset < 0)
|
||||
goto err_release_fw;
|
||||
|
||||
build_tag = (char *)(fw->data + fw_offset);
|
||||
|
||||
/* Read build date */
|
||||
fw_offset = iwm_fw_op_offset(iwm, fw, IWM_HDR_REC_OP_BUILD_DATE, 0);
|
||||
if (fw_offset < 0)
|
||||
goto err_release_fw;
|
||||
|
||||
build_date = *(u32 *)(fw->data + fw_offset);
|
||||
|
||||
IWM_INFO(iwm, "%s:\n", img_name);
|
||||
IWM_INFO(iwm, "\tVersion: %02X.%02X\n", ver->major, ver->minor);
|
||||
IWM_INFO(iwm, "\tBuild tag: %s\n", build_tag);
|
||||
IWM_INFO(iwm, "\tBuild date: %x-%x-%x\n",
|
||||
IWM_BUILD_YEAR(build_date), IWM_BUILD_MONTH(build_date),
|
||||
IWM_BUILD_DAY(build_date));
|
||||
|
||||
|
||||
err_release_fw:
|
||||
release_firmware(fw);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iwm_load_umac(struct iwm_priv *iwm)
|
||||
{
|
||||
struct iwm_udma_nonwifi_cmd target_cmd;
|
||||
int ret;
|
||||
|
||||
ret = iwm_load_img(iwm, iwm->bus_ops->umac_name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* We've loaded the UMAC, we can tell the target to jump there */
|
||||
target_cmd.opcode = UMAC_HDI_OUT_OPCODE_JUMP;
|
||||
target_cmd.addr = cpu_to_le32(UMAC_MU_FW_INST_DATA_12_ADDR);
|
||||
target_cmd.op1_sz = 0;
|
||||
target_cmd.op2 = 0;
|
||||
target_cmd.handle_by_hw = 0;
|
||||
target_cmd.resp = 1 ;
|
||||
target_cmd.eop = 1;
|
||||
|
||||
ret = iwm_hal_send_target_cmd(iwm, &target_cmd, NULL);
|
||||
if (ret < 0)
|
||||
IWM_ERR(iwm, "Couldn't send JMP command\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iwm_load_lmac(struct iwm_priv *iwm, const char *img_name)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = iwm_load_img(iwm, img_name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return iwm_send_umac_reset(iwm,
|
||||
cpu_to_le32(UMAC_RST_CTRL_FLG_LARC_CLK_EN), 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* We currently have to load 3 FWs:
|
||||
* 1) The UMAC (Upper MAC).
|
||||
* 2) The calibration LMAC (Lower MAC).
|
||||
* We then send the calibration init command, so that the device can
|
||||
* run a first calibration round.
|
||||
* 3) The operational LMAC, which replaces the calibration one when it's
|
||||
* done with the first calibration round.
|
||||
*
|
||||
* Once those 3 FWs have been loaded, we send the periodic calibration
|
||||
* command, and then the device is available for regular 802.11 operations.
|
||||
*/
|
||||
int iwm_load_fw(struct iwm_priv *iwm)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* We first start downloading the UMAC */
|
||||
ret = iwm_load_umac(iwm);
|
||||
if (ret < 0) {
|
||||
IWM_ERR(iwm, "UMAC loading failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Handle UMAC_ALIVE notification */
|
||||
ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_ALIVE, IWM_SRC_UMAC,
|
||||
WAIT_NOTIF_TIMEOUT);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Handle UMAC_ALIVE failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* UMAC is alive, we can download the calibration LMAC */
|
||||
ret = iwm_load_lmac(iwm, iwm->bus_ops->calib_lmac_name);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Calibration LMAC loading failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Handle UMAC_INIT_COMPLETE notification */
|
||||
ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_INIT_COMPLETE,
|
||||
IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Handle INIT_COMPLETE failed for calibration "
|
||||
"LMAC: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Read EEPROM data */
|
||||
ret = iwm_eeprom_init(iwm);
|
||||
if (ret < 0) {
|
||||
IWM_ERR(iwm, "Couldn't init eeprom array\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IWM_B0_HW_SUPPORT
|
||||
if (iwm->conf.hw_b0) {
|
||||
clear_bit(PHY_CALIBRATE_RX_IQ_CMD, &iwm->conf.init_calib_map);
|
||||
clear_bit(PHY_CALIBRATE_RX_IQ_CMD,
|
||||
&iwm->conf.periodic_calib_map);
|
||||
}
|
||||
#endif
|
||||
/* Read RX IQ calibration result from EEPROM */
|
||||
if (test_bit(PHY_CALIBRATE_RX_IQ_CMD, &iwm->conf.init_calib_map)) {
|
||||
iwm_store_rxiq_calib_result(iwm);
|
||||
set_bit(PHY_CALIBRATE_RX_IQ_CMD, &iwm->calib_done_map);
|
||||
}
|
||||
|
||||
iwm_send_prio_table(iwm);
|
||||
iwm_send_init_calib_cfg(iwm, iwm->conf.init_calib_map);
|
||||
|
||||
while (iwm->calib_done_map != iwm->conf.init_calib_map) {
|
||||
ret = iwm_notif_handle(iwm, CALIBRATION_RES_NOTIFICATION,
|
||||
IWM_SRC_LMAC, WAIT_NOTIF_TIMEOUT);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Wait for calibration result timeout\n");
|
||||
goto out;
|
||||
}
|
||||
IWM_DBG_FW(iwm, DBG, "Got calibration result. calib_done_map: "
|
||||
"0x%lx, requested calibrations: 0x%lx\n",
|
||||
iwm->calib_done_map, iwm->conf.init_calib_map);
|
||||
}
|
||||
|
||||
/* Handle LMAC CALIBRATION_COMPLETE notification */
|
||||
ret = iwm_notif_handle(iwm, CALIBRATION_COMPLETE_NOTIFICATION,
|
||||
IWM_SRC_LMAC, WAIT_NOTIF_TIMEOUT);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Wait for CALIBRATION_COMPLETE timeout\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
IWM_INFO(iwm, "LMAC calibration done: 0x%lx\n", iwm->calib_done_map);
|
||||
|
||||
iwm_send_umac_reset(iwm, cpu_to_le32(UMAC_RST_CTRL_FLG_LARC_RESET), 1);
|
||||
|
||||
ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_RESET, IWM_SRC_UMAC,
|
||||
WAIT_NOTIF_TIMEOUT);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Wait for UMAC RESET timeout\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Download the operational LMAC */
|
||||
ret = iwm_load_lmac(iwm, iwm->bus_ops->lmac_name);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "LMAC loading failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_INIT_COMPLETE,
|
||||
IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Handle INIT_COMPLETE failed for LMAC: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
iwm_send_prio_table(iwm);
|
||||
iwm_send_calib_results(iwm);
|
||||
iwm_send_periodic_calib_cfg(iwm, iwm->conf.periodic_calib_map);
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
iwm_eeprom_exit(iwm);
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __IWM_FW_H__
|
||||
#define __IWM_FW_H__
|
||||
|
||||
/**
|
||||
* struct iwm_fw_hdr_rec - An iwm firmware image is a
|
||||
* concatenation of various records. Each of them is
|
||||
* defined by an ID (aka op code), a length, and the
|
||||
* actual data.
|
||||
* @op_code: The record ID, see IWM_HDR_REC_OP_*
|
||||
*
|
||||
* @len: The record payload length
|
||||
*
|
||||
* @buf: The record payload
|
||||
*/
|
||||
struct iwm_fw_hdr_rec {
|
||||
u16 op_code;
|
||||
u16 len;
|
||||
u8 buf[0];
|
||||
};
|
||||
|
||||
/* Header's definitions */
|
||||
#define IWM_HDR_LEN (512)
|
||||
#define IWM_HDR_BARKER_LEN (16)
|
||||
|
||||
/* Header's opcodes */
|
||||
#define IWM_HDR_REC_OP_INVALID (0x00)
|
||||
#define IWM_HDR_REC_OP_BUILD_DATE (0x01)
|
||||
#define IWM_HDR_REC_OP_BUILD_TAG (0x02)
|
||||
#define IWM_HDR_REC_OP_SW_VER (0x03)
|
||||
#define IWM_HDR_REC_OP_HW_SKU (0x04)
|
||||
#define IWM_HDR_REC_OP_BUILD_OPT (0x05)
|
||||
#define IWM_HDR_REC_OP_MEM_DESC (0x06)
|
||||
#define IWM_HDR_REC_USERDEFS (0x07)
|
||||
|
||||
/* Header's records length (in bytes) */
|
||||
#define IWM_HDR_REC_LEN_BUILD_DATE (4)
|
||||
#define IWM_HDR_REC_LEN_BUILD_TAG (64)
|
||||
#define IWM_HDR_REC_LEN_SW_VER (4)
|
||||
#define IWM_HDR_REC_LEN_HW_SKU (4)
|
||||
#define IWM_HDR_REC_LEN_BUILD_OPT (4)
|
||||
#define IWM_HDR_REC_LEN_MEM_DESC (12)
|
||||
#define IWM_HDR_REC_LEN_USERDEF (64)
|
||||
|
||||
#define IWM_BUILD_YEAR(date) ((date >> 16) & 0xffff)
|
||||
#define IWM_BUILD_MONTH(date) ((date >> 8) & 0xff)
|
||||
#define IWM_BUILD_DAY(date) (date & 0xff)
|
||||
|
||||
struct iwm_fw_img_desc {
|
||||
u32 offset;
|
||||
u32 address;
|
||||
u32 length;
|
||||
};
|
||||
|
||||
struct iwm_fw_img_ver {
|
||||
u8 minor;
|
||||
u8 major;
|
||||
u16 reserved;
|
||||
};
|
||||
|
||||
int iwm_load_fw(struct iwm_priv *iwm);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,464 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Hardware Abstraction Layer for iwm.
|
||||
*
|
||||
* This file mostly defines an abstraction API for
|
||||
* sending various commands to the target.
|
||||
*
|
||||
* We have 2 types of commands: wifi and non-wifi ones.
|
||||
*
|
||||
* - wifi commands:
|
||||
* They are used for sending LMAC and UMAC commands,
|
||||
* and thus are the most commonly used ones.
|
||||
* There are 2 different wifi command types, the regular
|
||||
* one and the LMAC one. The former is used to send
|
||||
* UMAC commands (see UMAC_CMD_OPCODE_* from umac.h)
|
||||
* while the latter is used for sending commands to the
|
||||
* LMAC. If you look at LMAC commands you'll se that they
|
||||
* are actually regular iwlwifi target commands encapsulated
|
||||
* into a special UMAC command called UMAC passthrough.
|
||||
* This is due to the fact the the host talks exclusively
|
||||
* to the UMAC and so there needs to be a special UMAC
|
||||
* command for talking to the LMAC.
|
||||
* This is how a wifi command is layed out:
|
||||
* ------------------------
|
||||
* | iwm_udma_out_wifi_hdr |
|
||||
* ------------------------
|
||||
* | SW meta_data (32 bits) |
|
||||
* ------------------------
|
||||
* | iwm_dev_cmd_hdr |
|
||||
* ------------------------
|
||||
* | payload |
|
||||
* | .... |
|
||||
*
|
||||
* - non-wifi, or general commands:
|
||||
* Those commands are handled by the device's bootrom,
|
||||
* and are typically sent when the UMAC and the LMAC
|
||||
* are not yet available.
|
||||
* * This is how a non-wifi command is layed out:
|
||||
* ---------------------------
|
||||
* | iwm_udma_out_nonwifi_hdr |
|
||||
* ---------------------------
|
||||
* | payload |
|
||||
* | .... |
|
||||
|
||||
*
|
||||
* All the commands start with a UDMA header, which is
|
||||
* basically a 32 bits field. The 4 LSB there define
|
||||
* an opcode that allows the target to differentiate
|
||||
* between wifi (opcode is 0xf) and non-wifi commands
|
||||
* (opcode is [0..0xe]).
|
||||
*
|
||||
* When a command (wifi or non-wifi) is supposed to receive
|
||||
* an answer, we queue the command buffer. When we do receive
|
||||
* a command response from the UMAC, we go through the list
|
||||
* of pending command, and pass both the command and the answer
|
||||
* to the rx handler. Each command is sent with a unique
|
||||
* sequence id, and the answer is sent with the same one. This
|
||||
* is how we're supposed to match an answer with its command.
|
||||
* See rx.c:iwm_rx_handle_[non]wifi() and iwm_get_pending_[non]wifi()
|
||||
* for the implementation details.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
#include "iwm.h"
|
||||
#include "bus.h"
|
||||
#include "hal.h"
|
||||
#include "umac.h"
|
||||
#include "debug.h"
|
||||
|
||||
static void iwm_nonwifi_cmd_init(struct iwm_priv *iwm,
|
||||
struct iwm_nonwifi_cmd *cmd,
|
||||
struct iwm_udma_nonwifi_cmd *udma_cmd)
|
||||
{
|
||||
INIT_LIST_HEAD(&cmd->pending);
|
||||
|
||||
spin_lock(&iwm->cmd_lock);
|
||||
|
||||
cmd->resp_received = 0;
|
||||
|
||||
cmd->seq_num = iwm->nonwifi_seq_num;
|
||||
udma_cmd->seq_num = cpu_to_le16(cmd->seq_num);
|
||||
|
||||
cmd->seq_num = iwm->nonwifi_seq_num++;
|
||||
iwm->nonwifi_seq_num %= UMAC_NONWIFI_SEQ_NUM_MAX;
|
||||
|
||||
if (udma_cmd->resp)
|
||||
list_add_tail(&cmd->pending, &iwm->nonwifi_pending_cmd);
|
||||
|
||||
spin_unlock(&iwm->cmd_lock);
|
||||
|
||||
cmd->buf.start = cmd->buf.payload;
|
||||
cmd->buf.len = 0;
|
||||
|
||||
memcpy(&cmd->udma_cmd, udma_cmd, sizeof(*udma_cmd));
|
||||
}
|
||||
|
||||
u16 iwm_alloc_wifi_cmd_seq(struct iwm_priv *iwm)
|
||||
{
|
||||
u16 seq_num = iwm->wifi_seq_num;
|
||||
|
||||
iwm->wifi_seq_num++;
|
||||
iwm->wifi_seq_num %= UMAC_WIFI_SEQ_NUM_MAX;
|
||||
|
||||
return seq_num;
|
||||
}
|
||||
|
||||
static void iwm_wifi_cmd_init(struct iwm_priv *iwm,
|
||||
struct iwm_wifi_cmd *cmd,
|
||||
struct iwm_udma_wifi_cmd *udma_cmd,
|
||||
struct iwm_umac_cmd *umac_cmd,
|
||||
struct iwm_lmac_cmd *lmac_cmd,
|
||||
u16 payload_size)
|
||||
{
|
||||
INIT_LIST_HEAD(&cmd->pending);
|
||||
|
||||
spin_lock(&iwm->cmd_lock);
|
||||
|
||||
cmd->seq_num = iwm_alloc_wifi_cmd_seq(iwm);
|
||||
umac_cmd->seq_num = cpu_to_le16(cmd->seq_num);
|
||||
|
||||
if (umac_cmd->resp)
|
||||
list_add_tail(&cmd->pending, &iwm->wifi_pending_cmd);
|
||||
|
||||
spin_unlock(&iwm->cmd_lock);
|
||||
|
||||
cmd->buf.start = cmd->buf.payload;
|
||||
cmd->buf.len = 0;
|
||||
|
||||
if (lmac_cmd) {
|
||||
cmd->buf.start -= sizeof(struct iwm_lmac_hdr);
|
||||
|
||||
lmac_cmd->seq_num = cpu_to_le16(cmd->seq_num);
|
||||
lmac_cmd->count = cpu_to_le16(payload_size);
|
||||
|
||||
memcpy(&cmd->lmac_cmd, lmac_cmd, sizeof(*lmac_cmd));
|
||||
|
||||
umac_cmd->count = cpu_to_le16(sizeof(struct iwm_lmac_hdr));
|
||||
} else
|
||||
umac_cmd->count = 0;
|
||||
|
||||
umac_cmd->count = cpu_to_le16(payload_size +
|
||||
le16_to_cpu(umac_cmd->count));
|
||||
udma_cmd->count = cpu_to_le16(sizeof(struct iwm_umac_fw_cmd_hdr) +
|
||||
le16_to_cpu(umac_cmd->count));
|
||||
|
||||
memcpy(&cmd->udma_cmd, udma_cmd, sizeof(*udma_cmd));
|
||||
memcpy(&cmd->umac_cmd, umac_cmd, sizeof(*umac_cmd));
|
||||
}
|
||||
|
||||
void iwm_cmd_flush(struct iwm_priv *iwm)
|
||||
{
|
||||
struct iwm_wifi_cmd *wcmd, *wnext;
|
||||
struct iwm_nonwifi_cmd *nwcmd, *nwnext;
|
||||
|
||||
list_for_each_entry_safe(wcmd, wnext, &iwm->wifi_pending_cmd, pending) {
|
||||
list_del(&wcmd->pending);
|
||||
kfree(wcmd);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(nwcmd, nwnext, &iwm->nonwifi_pending_cmd,
|
||||
pending) {
|
||||
list_del(&nwcmd->pending);
|
||||
kfree(nwcmd);
|
||||
}
|
||||
}
|
||||
|
||||
struct iwm_wifi_cmd *iwm_get_pending_wifi_cmd(struct iwm_priv *iwm, u16 seq_num)
|
||||
{
|
||||
struct iwm_wifi_cmd *cmd, *next;
|
||||
|
||||
list_for_each_entry_safe(cmd, next, &iwm->wifi_pending_cmd, pending)
|
||||
if (cmd->seq_num == seq_num) {
|
||||
list_del(&cmd->pending);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct iwm_nonwifi_cmd *
|
||||
iwm_get_pending_nonwifi_cmd(struct iwm_priv *iwm, u8 seq_num, u8 cmd_opcode)
|
||||
{
|
||||
struct iwm_nonwifi_cmd *cmd, *next;
|
||||
|
||||
list_for_each_entry_safe(cmd, next, &iwm->nonwifi_pending_cmd, pending)
|
||||
if ((cmd->seq_num == seq_num) &&
|
||||
(cmd->udma_cmd.opcode == cmd_opcode) &&
|
||||
(cmd->resp_received)) {
|
||||
list_del(&cmd->pending);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void iwm_build_udma_nonwifi_hdr(struct iwm_priv *iwm,
|
||||
struct iwm_udma_out_nonwifi_hdr *hdr,
|
||||
struct iwm_udma_nonwifi_cmd *cmd)
|
||||
{
|
||||
memset(hdr, 0, sizeof(*hdr));
|
||||
|
||||
SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_OPCODE, cmd->opcode);
|
||||
SET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_RESP, cmd->resp);
|
||||
SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, 1);
|
||||
SET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_HANDLE_BY_HW,
|
||||
cmd->handle_by_hw);
|
||||
SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_SIGNATURE, UMAC_HDI_OUT_SIGNATURE);
|
||||
SET_VAL32(hdr->cmd, UDMA_HDI_OUT_CMD_NON_WIFI_HW_SEQ_NUM,
|
||||
le16_to_cpu(cmd->seq_num));
|
||||
|
||||
hdr->addr = cmd->addr;
|
||||
hdr->op1_sz = cmd->op1_sz;
|
||||
hdr->op2 = cmd->op2;
|
||||
}
|
||||
|
||||
static int iwm_send_udma_nonwifi_cmd(struct iwm_priv *iwm,
|
||||
struct iwm_nonwifi_cmd *cmd)
|
||||
{
|
||||
struct iwm_udma_out_nonwifi_hdr *udma_hdr;
|
||||
struct iwm_nonwifi_cmd_buff *buf;
|
||||
struct iwm_udma_nonwifi_cmd *udma_cmd = &cmd->udma_cmd;
|
||||
|
||||
buf = &cmd->buf;
|
||||
|
||||
buf->start -= sizeof(struct iwm_umac_nonwifi_out_hdr);
|
||||
buf->len += sizeof(struct iwm_umac_nonwifi_out_hdr);
|
||||
|
||||
udma_hdr = (struct iwm_udma_out_nonwifi_hdr *)(buf->start);
|
||||
|
||||
iwm_build_udma_nonwifi_hdr(iwm, udma_hdr, udma_cmd);
|
||||
|
||||
IWM_DBG_CMD(iwm, DBG,
|
||||
"Send UDMA nonwifi cmd: opcode = 0x%x, resp = 0x%x, "
|
||||
"hw = 0x%x, seqnum = %d, addr = 0x%x, op1_sz = 0x%x, "
|
||||
"op2 = 0x%x\n", udma_cmd->opcode, udma_cmd->resp,
|
||||
udma_cmd->handle_by_hw, cmd->seq_num, udma_cmd->addr,
|
||||
udma_cmd->op1_sz, udma_cmd->op2);
|
||||
|
||||
return iwm_bus_send_chunk(iwm, buf->start, buf->len);
|
||||
}
|
||||
|
||||
void iwm_udma_wifi_hdr_set_eop(struct iwm_priv *iwm, u8 *buf, u8 eop)
|
||||
{
|
||||
struct iwm_udma_out_wifi_hdr *hdr = (struct iwm_udma_out_wifi_hdr *)buf;
|
||||
|
||||
SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, eop);
|
||||
}
|
||||
|
||||
void iwm_build_udma_wifi_hdr(struct iwm_priv *iwm,
|
||||
struct iwm_udma_out_wifi_hdr *hdr,
|
||||
struct iwm_udma_wifi_cmd *cmd)
|
||||
{
|
||||
memset(hdr, 0, sizeof(*hdr));
|
||||
|
||||
SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_OPCODE, UMAC_HDI_OUT_OPCODE_WIFI);
|
||||
SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, cmd->eop);
|
||||
SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_SIGNATURE, UMAC_HDI_OUT_SIGNATURE);
|
||||
|
||||
SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_BYTE_COUNT,
|
||||
le16_to_cpu(cmd->count));
|
||||
SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_CREDIT_GRP, cmd->credit_group);
|
||||
SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_RATID, cmd->ra_tid);
|
||||
SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_LMAC_OFFSET, cmd->lmac_offset);
|
||||
}
|
||||
|
||||
void iwm_build_umac_hdr(struct iwm_priv *iwm,
|
||||
struct iwm_umac_fw_cmd_hdr *hdr,
|
||||
struct iwm_umac_cmd *cmd)
|
||||
{
|
||||
memset(hdr, 0, sizeof(*hdr));
|
||||
|
||||
SET_VAL32(hdr->meta_data, UMAC_FW_CMD_BYTE_COUNT,
|
||||
le16_to_cpu(cmd->count));
|
||||
SET_VAL32(hdr->meta_data, UMAC_FW_CMD_TX_STA_COLOR, cmd->color);
|
||||
SET_VAL8(hdr->cmd.flags, UMAC_DEV_CMD_FLAGS_RESP_REQ, cmd->resp);
|
||||
|
||||
hdr->cmd.cmd = cmd->id;
|
||||
hdr->cmd.seq_num = cmd->seq_num;
|
||||
}
|
||||
|
||||
static int iwm_send_udma_wifi_cmd(struct iwm_priv *iwm,
|
||||
struct iwm_wifi_cmd *cmd)
|
||||
{
|
||||
struct iwm_umac_wifi_out_hdr *umac_hdr;
|
||||
struct iwm_wifi_cmd_buff *buf;
|
||||
struct iwm_udma_wifi_cmd *udma_cmd = &cmd->udma_cmd;
|
||||
struct iwm_umac_cmd *umac_cmd = &cmd->umac_cmd;
|
||||
int ret;
|
||||
|
||||
buf = &cmd->buf;
|
||||
|
||||
buf->start -= sizeof(struct iwm_umac_wifi_out_hdr);
|
||||
buf->len += sizeof(struct iwm_umac_wifi_out_hdr);
|
||||
|
||||
umac_hdr = (struct iwm_umac_wifi_out_hdr *)(buf->start);
|
||||
|
||||
iwm_build_udma_wifi_hdr(iwm, &umac_hdr->hw_hdr, udma_cmd);
|
||||
iwm_build_umac_hdr(iwm, &umac_hdr->sw_hdr, umac_cmd);
|
||||
|
||||
IWM_DBG_CMD(iwm, DBG,
|
||||
"Send UDMA wifi cmd: opcode = 0x%x, UMAC opcode = 0x%x, "
|
||||
"eop = 0x%x, count = 0x%x, credit_group = 0x%x, "
|
||||
"ra_tid = 0x%x, lmac_offset = 0x%x, seqnum = %d\n",
|
||||
UMAC_HDI_OUT_OPCODE_WIFI, umac_cmd->id,
|
||||
udma_cmd->eop, udma_cmd->count, udma_cmd->credit_group,
|
||||
udma_cmd->ra_tid, udma_cmd->lmac_offset, cmd->seq_num);
|
||||
|
||||
if (umac_cmd->id == UMAC_CMD_OPCODE_WIFI_PASS_THROUGH)
|
||||
IWM_DBG_CMD(iwm, DBG, "\tLMAC opcode: 0x%x\n",
|
||||
cmd->lmac_cmd.id);
|
||||
|
||||
ret = iwm_tx_credit_alloc(iwm, udma_cmd->credit_group, buf->len);
|
||||
|
||||
/* We keep sending UMAC reset regardless of the command credits.
|
||||
* The UMAC is supposed to be reset anyway and the Tx credits are
|
||||
* reinitialized afterwards. If we are lucky, the reset could
|
||||
* still be done even though we have run out of credits for the
|
||||
* command pool at this moment.*/
|
||||
if (ret && (umac_cmd->id != UMAC_CMD_OPCODE_RESET)) {
|
||||
IWM_DBG_TX(iwm, DBG, "Failed to alloc tx credit for cmd %d\n",
|
||||
umac_cmd->id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return iwm_bus_send_chunk(iwm, buf->start, buf->len);
|
||||
}
|
||||
|
||||
/* target_cmd a.k.a udma_nonwifi_cmd can be sent when UMAC is not available */
|
||||
int iwm_hal_send_target_cmd(struct iwm_priv *iwm,
|
||||
struct iwm_udma_nonwifi_cmd *udma_cmd,
|
||||
const void *payload)
|
||||
{
|
||||
struct iwm_nonwifi_cmd *cmd;
|
||||
int ret;
|
||||
|
||||
cmd = kzalloc(sizeof(struct iwm_nonwifi_cmd), GFP_KERNEL);
|
||||
if (!cmd) {
|
||||
IWM_ERR(iwm, "Couldn't alloc memory for hal cmd\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
iwm_nonwifi_cmd_init(iwm, cmd, udma_cmd);
|
||||
|
||||
if (cmd->udma_cmd.opcode == UMAC_HDI_OUT_OPCODE_WRITE ||
|
||||
cmd->udma_cmd.opcode == UMAC_HDI_OUT_OPCODE_WRITE_PERSISTENT) {
|
||||
cmd->buf.len = le32_to_cpu(cmd->udma_cmd.op1_sz);
|
||||
memcpy(&cmd->buf.payload, payload, cmd->buf.len);
|
||||
}
|
||||
|
||||
ret = iwm_send_udma_nonwifi_cmd(iwm, cmd);
|
||||
|
||||
if (!udma_cmd->resp)
|
||||
kfree(cmd);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return cmd->seq_num;
|
||||
}
|
||||
|
||||
static void iwm_build_lmac_hdr(struct iwm_priv *iwm, struct iwm_lmac_hdr *hdr,
|
||||
struct iwm_lmac_cmd *cmd)
|
||||
{
|
||||
memset(hdr, 0, sizeof(*hdr));
|
||||
|
||||
hdr->id = cmd->id;
|
||||
hdr->flags = 0; /* Is this ever used? */
|
||||
hdr->seq_num = cmd->seq_num;
|
||||
}
|
||||
|
||||
/*
|
||||
* iwm_hal_send_host_cmd(): sends commands to the UMAC or the LMAC.
|
||||
* Sending command to the LMAC is equivalent to sending a
|
||||
* regular UMAC command with the LMAC passtrough or the LMAC
|
||||
* wrapper UMAC command IDs.
|
||||
*/
|
||||
int iwm_hal_send_host_cmd(struct iwm_priv *iwm,
|
||||
struct iwm_udma_wifi_cmd *udma_cmd,
|
||||
struct iwm_umac_cmd *umac_cmd,
|
||||
struct iwm_lmac_cmd *lmac_cmd,
|
||||
const void *payload, u16 payload_size)
|
||||
{
|
||||
struct iwm_wifi_cmd *cmd;
|
||||
struct iwm_lmac_hdr *hdr;
|
||||
int lmac_hdr_len = 0;
|
||||
int ret;
|
||||
|
||||
cmd = kzalloc(sizeof(struct iwm_wifi_cmd), GFP_KERNEL);
|
||||
if (!cmd) {
|
||||
IWM_ERR(iwm, "Couldn't alloc memory for wifi hal cmd\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
iwm_wifi_cmd_init(iwm, cmd, udma_cmd, umac_cmd, lmac_cmd, payload_size);
|
||||
|
||||
if (lmac_cmd) {
|
||||
hdr = (struct iwm_lmac_hdr *)(cmd->buf.start);
|
||||
|
||||
iwm_build_lmac_hdr(iwm, hdr, &cmd->lmac_cmd);
|
||||
lmac_hdr_len = sizeof(struct iwm_lmac_hdr);
|
||||
}
|
||||
|
||||
memcpy(cmd->buf.payload, payload, payload_size);
|
||||
cmd->buf.len = le16_to_cpu(umac_cmd->count);
|
||||
|
||||
ret = iwm_send_udma_wifi_cmd(iwm, cmd);
|
||||
|
||||
/* We free the cmd if we're not expecting any response */
|
||||
if (!umac_cmd->resp)
|
||||
kfree(cmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* iwm_hal_send_umac_cmd(): This is a special case for
|
||||
* iwm_hal_send_host_cmd() to send direct UMAC cmd (without
|
||||
* LMAC involved).
|
||||
*/
|
||||
int iwm_hal_send_umac_cmd(struct iwm_priv *iwm,
|
||||
struct iwm_udma_wifi_cmd *udma_cmd,
|
||||
struct iwm_umac_cmd *umac_cmd,
|
||||
const void *payload, u16 payload_size)
|
||||
{
|
||||
return iwm_hal_send_host_cmd(iwm, udma_cmd, umac_cmd, NULL,
|
||||
payload, payload_size);
|
||||
}
|
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _IWM_HAL_H_
|
||||
#define _IWM_HAL_H_
|
||||
|
||||
#include "umac.h"
|
||||
|
||||
#define GET_VAL8(s, name) ((s >> name##_POS) & name##_SEED)
|
||||
#define GET_VAL16(s, name) ((le16_to_cpu(s) >> name##_POS) & name##_SEED)
|
||||
#define GET_VAL32(s, name) ((le32_to_cpu(s) >> name##_POS) & name##_SEED)
|
||||
|
||||
#define SET_VAL8(s, name, val) \
|
||||
do { \
|
||||
s = (s & ~(name##_SEED << name##_POS)) | \
|
||||
((val & name##_SEED) << name##_POS); \
|
||||
} while (0)
|
||||
|
||||
#define SET_VAL16(s, name, val) \
|
||||
do { \
|
||||
s = cpu_to_le16((le16_to_cpu(s) & ~(name##_SEED << name##_POS)) | \
|
||||
((val & name##_SEED) << name##_POS)); \
|
||||
} while (0)
|
||||
|
||||
#define SET_VAL32(s, name, val) \
|
||||
do { \
|
||||
s = cpu_to_le32((le32_to_cpu(s) & ~(name##_SEED << name##_POS)) | \
|
||||
((val & name##_SEED) << name##_POS)); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define UDMA_UMAC_INIT { .eop = 1, \
|
||||
.credit_group = 0x4, \
|
||||
.ra_tid = UMAC_HDI_ACT_TBL_IDX_HOST_CMD, \
|
||||
.lmac_offset = 0 }
|
||||
#define UDMA_LMAC_INIT { .eop = 1, \
|
||||
.credit_group = 0x4, \
|
||||
.ra_tid = UMAC_HDI_ACT_TBL_IDX_HOST_CMD, \
|
||||
.lmac_offset = 4 }
|
||||
|
||||
|
||||
/* UDMA IN OP CODE -- cmd bits [3:0] */
|
||||
#define UDMA_IN_OPCODE_MASK 0xF
|
||||
|
||||
#define UDMA_IN_OPCODE_GENERAL_RESP 0x0
|
||||
#define UDMA_IN_OPCODE_READ_RESP 0x1
|
||||
#define UDMA_IN_OPCODE_WRITE_RESP 0x2
|
||||
#define UDMA_IN_OPCODE_PERS_WRITE_RESP 0x5
|
||||
#define UDMA_IN_OPCODE_PERS_READ_RESP 0x6
|
||||
#define UDMA_IN_OPCODE_RD_MDFY_WR_RESP 0x7
|
||||
#define UDMA_IN_OPCODE_EP_MNGMT_MSG 0x8
|
||||
#define UDMA_IN_OPCODE_CRDT_CHNG_MSG 0x9
|
||||
#define UDMA_IN_OPCODE_CNTRL_DATABASE_MSG 0xA
|
||||
#define UDMA_IN_OPCODE_SW_MSG 0xB
|
||||
#define UDMA_IN_OPCODE_WIFI 0xF
|
||||
#define UDMA_IN_OPCODE_WIFI_LMAC 0x1F
|
||||
#define UDMA_IN_OPCODE_WIFI_UMAC 0x2F
|
||||
|
||||
/* HW API: udma_hdi_nonwifi API (OUT and IN) */
|
||||
|
||||
/* iwm_udma_nonwifi_cmd request response -- bits [9:9] */
|
||||
#define UDMA_HDI_OUT_NW_CMD_RESP_POS 9
|
||||
#define UDMA_HDI_OUT_NW_CMD_RESP_SEED 0x1
|
||||
|
||||
/* iwm_udma_nonwifi_cmd handle by HW -- bits [11:11] */
|
||||
#define UDMA_HDI_OUT_NW_CMD_HANDLE_BY_HW_POS 11
|
||||
#define UDMA_HDI_OUT_NW_CMD_HANDLE_BY_HW_SEED 0x1
|
||||
|
||||
/* iwm_udma_nonwifi_cmd sequence-number -- bits [12:15] */
|
||||
#define UDMA_HDI_OUT_NW_CMD_SEQ_NUM_POS 12
|
||||
#define UDMA_HDI_OUT_NW_CMD_SEQ_NUM_SEED 0xF
|
||||
|
||||
/* UDMA IN Non-WIFI HW sequence number -- bits [12:15] */
|
||||
#define UDMA_IN_NW_HW_SEQ_NUM_POS 12
|
||||
#define UDMA_IN_NW_HW_SEQ_NUM_SEED 0xF
|
||||
|
||||
/* UDMA IN Non-WIFI HW signature -- bits [16:31] */
|
||||
#define UDMA_IN_NW_HW_SIG_POS 16
|
||||
#define UDMA_IN_NW_HW_SIG_SEED 0xFFFF
|
||||
|
||||
/* fixed signature */
|
||||
#define UDMA_IN_NW_HW_SIG 0xCBBC
|
||||
|
||||
/* UDMA IN Non-WIFI HW block length -- bits [32:35] */
|
||||
#define UDMA_IN_NW_HW_LENGTH_SEED 0xF
|
||||
#define UDMA_IN_NW_HW_LENGTH_POS 32
|
||||
|
||||
/* End of HW API: udma_hdi_nonwifi API (OUT and IN) */
|
||||
|
||||
#define IWM_SDIO_FW_MAX_CHUNK_SIZE 2032
|
||||
#define IWM_MAX_WIFI_HEADERS_SIZE 32
|
||||
#define IWM_MAX_NONWIFI_HEADERS_SIZE 16
|
||||
#define IWM_MAX_NONWIFI_CMD_BUFF_SIZE (IWM_SDIO_FW_MAX_CHUNK_SIZE - \
|
||||
IWM_MAX_NONWIFI_HEADERS_SIZE)
|
||||
#define IWM_MAX_WIFI_CMD_BUFF_SIZE (IWM_SDIO_FW_MAX_CHUNK_SIZE - \
|
||||
IWM_MAX_WIFI_HEADERS_SIZE)
|
||||
|
||||
#define IWM_HAL_CONCATENATE_BUF_SIZE 8192
|
||||
|
||||
struct iwm_wifi_cmd_buff {
|
||||
u16 len;
|
||||
u8 *start;
|
||||
u8 hdr[IWM_MAX_WIFI_HEADERS_SIZE];
|
||||
u8 payload[IWM_MAX_WIFI_CMD_BUFF_SIZE];
|
||||
};
|
||||
|
||||
struct iwm_nonwifi_cmd_buff {
|
||||
u16 len;
|
||||
u8 *start;
|
||||
u8 hdr[IWM_MAX_NONWIFI_HEADERS_SIZE];
|
||||
u8 payload[IWM_MAX_NONWIFI_CMD_BUFF_SIZE];
|
||||
};
|
||||
|
||||
struct iwm_udma_nonwifi_cmd {
|
||||
u8 opcode;
|
||||
u8 eop;
|
||||
u8 resp;
|
||||
u8 handle_by_hw;
|
||||
__le32 addr;
|
||||
__le32 op1_sz;
|
||||
__le32 op2;
|
||||
__le16 seq_num;
|
||||
};
|
||||
|
||||
struct iwm_udma_wifi_cmd {
|
||||
__le16 count;
|
||||
u8 eop;
|
||||
u8 credit_group;
|
||||
u8 ra_tid;
|
||||
u8 lmac_offset;
|
||||
};
|
||||
|
||||
struct iwm_umac_cmd {
|
||||
u8 id;
|
||||
__le16 count;
|
||||
u8 resp;
|
||||
__le16 seq_num;
|
||||
u8 color;
|
||||
};
|
||||
|
||||
struct iwm_lmac_cmd {
|
||||
u8 id;
|
||||
__le16 count;
|
||||
u8 resp;
|
||||
__le16 seq_num;
|
||||
};
|
||||
|
||||
struct iwm_nonwifi_cmd {
|
||||
u16 seq_num;
|
||||
bool resp_received;
|
||||
struct list_head pending;
|
||||
struct iwm_udma_nonwifi_cmd udma_cmd;
|
||||
struct iwm_umac_cmd umac_cmd;
|
||||
struct iwm_lmac_cmd lmac_cmd;
|
||||
struct iwm_nonwifi_cmd_buff buf;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
struct iwm_wifi_cmd {
|
||||
u16 seq_num;
|
||||
struct list_head pending;
|
||||
struct iwm_udma_wifi_cmd udma_cmd;
|
||||
struct iwm_umac_cmd umac_cmd;
|
||||
struct iwm_lmac_cmd lmac_cmd;
|
||||
struct iwm_wifi_cmd_buff buf;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
void iwm_cmd_flush(struct iwm_priv *iwm);
|
||||
|
||||
struct iwm_wifi_cmd *iwm_get_pending_wifi_cmd(struct iwm_priv *iwm,
|
||||
u16 seq_num);
|
||||
struct iwm_nonwifi_cmd *iwm_get_pending_nonwifi_cmd(struct iwm_priv *iwm,
|
||||
u8 seq_num, u8 cmd_opcode);
|
||||
|
||||
|
||||
int iwm_hal_send_target_cmd(struct iwm_priv *iwm,
|
||||
struct iwm_udma_nonwifi_cmd *ucmd,
|
||||
const void *payload);
|
||||
|
||||
int iwm_hal_send_host_cmd(struct iwm_priv *iwm,
|
||||
struct iwm_udma_wifi_cmd *udma_cmd,
|
||||
struct iwm_umac_cmd *umac_cmd,
|
||||
struct iwm_lmac_cmd *lmac_cmd,
|
||||
const void *payload, u16 payload_size);
|
||||
|
||||
int iwm_hal_send_umac_cmd(struct iwm_priv *iwm,
|
||||
struct iwm_udma_wifi_cmd *udma_cmd,
|
||||
struct iwm_umac_cmd *umac_cmd,
|
||||
const void *payload, u16 payload_size);
|
||||
|
||||
u16 iwm_alloc_wifi_cmd_seq(struct iwm_priv *iwm);
|
||||
|
||||
void iwm_udma_wifi_hdr_set_eop(struct iwm_priv *iwm, u8 *buf, u8 eop);
|
||||
void iwm_build_udma_wifi_hdr(struct iwm_priv *iwm,
|
||||
struct iwm_udma_out_wifi_hdr *hdr,
|
||||
struct iwm_udma_wifi_cmd *cmd);
|
||||
void iwm_build_umac_hdr(struct iwm_priv *iwm,
|
||||
struct iwm_umac_fw_cmd_hdr *hdr,
|
||||
struct iwm_umac_cmd *cmd);
|
||||
#endif /* _IWM_HAL_H_ */
|
|
@ -0,0 +1,350 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __IWM_H__
|
||||
#define __IWM_H__
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <net/cfg80211.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "hal.h"
|
||||
#include "umac.h"
|
||||
#include "lmac.h"
|
||||
#include "eeprom.h"
|
||||
|
||||
#define IWM_COPYRIGHT "Copyright(c) 2009 Intel Corporation"
|
||||
#define IWM_AUTHOR "<ilw@linux.intel.com>"
|
||||
|
||||
#define CONFIG_IWM_B0_HW_SUPPORT 1
|
||||
|
||||
#define IWM_SRC_LMAC UMAC_HDI_IN_SOURCE_FHRX
|
||||
#define IWM_SRC_UDMA UMAC_HDI_IN_SOURCE_UDMA
|
||||
#define IWM_SRC_UMAC UMAC_HDI_IN_SOURCE_FW
|
||||
#define IWM_SRC_NUM 3
|
||||
|
||||
#define IWM_POWER_INDEX_MIN 0
|
||||
#define IWM_POWER_INDEX_MAX 5
|
||||
#define IWM_POWER_INDEX_DEFAULT 3
|
||||
|
||||
struct iwm_conf {
|
||||
u32 sdio_ior_timeout;
|
||||
unsigned long init_calib_map;
|
||||
unsigned long periodic_calib_map;
|
||||
bool reset_on_fatal_err;
|
||||
bool auto_connect;
|
||||
bool wimax_not_present;
|
||||
bool enable_qos;
|
||||
u32 mode;
|
||||
|
||||
u32 power_index;
|
||||
u32 frag_threshold;
|
||||
u32 rts_threshold;
|
||||
bool cts_to_self;
|
||||
|
||||
u32 assoc_timeout;
|
||||
u32 roam_timeout;
|
||||
u32 wireless_mode;
|
||||
u32 coexist_mode;
|
||||
|
||||
u8 ibss_band;
|
||||
u8 ibss_channel;
|
||||
|
||||
u8 mac_addr[ETH_ALEN];
|
||||
#ifdef CONFIG_IWM_B0_HW_SUPPORT
|
||||
bool hw_b0;
|
||||
#endif
|
||||
};
|
||||
|
||||
enum {
|
||||
COEX_MODE_SA = 1,
|
||||
COEX_MODE_XOR,
|
||||
COEX_MODE_CM,
|
||||
COEX_MODE_MAX,
|
||||
};
|
||||
|
||||
struct iwm_if_ops;
|
||||
struct iwm_wifi_cmd;
|
||||
|
||||
struct pool_entry {
|
||||
int id; /* group id */
|
||||
int sid; /* super group id */
|
||||
int min_pages; /* min capacity in pages */
|
||||
int max_pages; /* max capacity in pages */
|
||||
int alloc_pages; /* allocated # of pages. incresed by driver */
|
||||
int total_freed_pages; /* total freed # of pages. incresed by UMAC */
|
||||
};
|
||||
|
||||
struct spool_entry {
|
||||
int id;
|
||||
int max_pages;
|
||||
int alloc_pages;
|
||||
};
|
||||
|
||||
struct iwm_tx_credit {
|
||||
spinlock_t lock;
|
||||
int pool_nr;
|
||||
unsigned long full_pools_map; /* bitmap for # of filled tx pools */
|
||||
struct pool_entry pools[IWM_MACS_OUT_GROUPS];
|
||||
struct spool_entry spools[IWM_MACS_OUT_SGROUPS];
|
||||
};
|
||||
|
||||
struct iwm_notif {
|
||||
struct list_head pending;
|
||||
u32 cmd_id;
|
||||
void *cmd;
|
||||
u8 src;
|
||||
void *buf;
|
||||
unsigned long buf_size;
|
||||
};
|
||||
|
||||
struct iwm_sta_info {
|
||||
u8 addr[ETH_ALEN];
|
||||
bool valid;
|
||||
bool qos;
|
||||
u8 color;
|
||||
};
|
||||
|
||||
struct iwm_tx_info {
|
||||
u8 sta;
|
||||
u8 color;
|
||||
u8 tid;
|
||||
};
|
||||
|
||||
struct iwm_rx_info {
|
||||
unsigned long rx_size;
|
||||
unsigned long rx_buf_size;
|
||||
};
|
||||
|
||||
#define IWM_NUM_KEYS 4
|
||||
|
||||
struct iwm_umac_key_hdr {
|
||||
u8 mac[ETH_ALEN];
|
||||
u8 key_idx;
|
||||
u8 multicast; /* BCast encrypt & BCast decrypt of frames FROM mac */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_key {
|
||||
struct iwm_umac_key_hdr hdr;
|
||||
u8 in_use;
|
||||
u8 alg;
|
||||
u32 flags;
|
||||
u8 tx_seq[IW_ENCODE_SEQ_MAX_SIZE];
|
||||
u8 rx_seq[IW_ENCODE_SEQ_MAX_SIZE];
|
||||
u8 key_len;
|
||||
u8 key[32];
|
||||
};
|
||||
|
||||
#define IWM_RX_ID_HASH 0xff
|
||||
#define IWM_RX_ID_GET_HASH(id) ((id) % IWM_RX_ID_HASH)
|
||||
|
||||
#define IWM_STA_TABLE_NUM 16
|
||||
#define IWM_TX_LIST_SIZE 64
|
||||
#define IWM_RX_LIST_SIZE 256
|
||||
|
||||
#define IWM_SCAN_ID_MAX 0xff
|
||||
|
||||
#define IWM_STATUS_READY 0
|
||||
#define IWM_STATUS_SCANNING 1
|
||||
#define IWM_STATUS_SCAN_ABORTING 2
|
||||
#define IWM_STATUS_ASSOCIATING 3
|
||||
#define IWM_STATUS_ASSOCIATED 4
|
||||
|
||||
#define IWM_RADIO_RFKILL_OFF 0
|
||||
#define IWM_RADIO_RFKILL_HW 1
|
||||
#define IWM_RADIO_RFKILL_SW 2
|
||||
|
||||
struct iwm_tx_queue {
|
||||
int id;
|
||||
struct sk_buff_head queue;
|
||||
struct workqueue_struct *wq;
|
||||
struct work_struct worker;
|
||||
u8 concat_buf[IWM_HAL_CONCATENATE_BUF_SIZE];
|
||||
int concat_count;
|
||||
u8 *concat_ptr;
|
||||
};
|
||||
|
||||
/* Queues 0 ~ 3 for AC data, 5 for iPAN */
|
||||
#define IWM_TX_QUEUES 5
|
||||
#define IWM_TX_DATA_QUEUES 4
|
||||
#define IWM_TX_CMD_QUEUE 4
|
||||
|
||||
struct iwm_bss_info {
|
||||
struct list_head node;
|
||||
struct cfg80211_bss *cfg_bss;
|
||||
struct iwm_umac_notif_bss_info *bss;
|
||||
};
|
||||
|
||||
typedef int (*iwm_handler)(struct iwm_priv *priv, u8 *buf,
|
||||
unsigned long buf_size, struct iwm_wifi_cmd *cmd);
|
||||
|
||||
#define IWM_WATCHDOG_PERIOD (6 * HZ)
|
||||
|
||||
struct iwm_priv {
|
||||
struct wireless_dev *wdev;
|
||||
struct iwm_if_ops *bus_ops;
|
||||
|
||||
struct iwm_conf conf;
|
||||
|
||||
unsigned long status;
|
||||
unsigned long radio;
|
||||
|
||||
struct list_head pending_notif;
|
||||
wait_queue_head_t notif_queue;
|
||||
|
||||
wait_queue_head_t nonwifi_queue;
|
||||
|
||||
unsigned long calib_done_map;
|
||||
struct {
|
||||
u8 *buf;
|
||||
u32 size;
|
||||
} calib_res[CALIBRATION_CMD_NUM];
|
||||
|
||||
struct iwm_umac_profile *umac_profile;
|
||||
bool umac_profile_active;
|
||||
|
||||
u8 bssid[ETH_ALEN];
|
||||
u8 channel;
|
||||
u16 rate;
|
||||
|
||||
struct iwm_sta_info sta_table[IWM_STA_TABLE_NUM];
|
||||
struct list_head bss_list;
|
||||
|
||||
void (*nonwifi_rx_handlers[UMAC_HDI_IN_OPCODE_NONWIFI_MAX])
|
||||
(struct iwm_priv *priv, u8 *buf, unsigned long buf_size);
|
||||
|
||||
const iwm_handler *umac_handlers;
|
||||
const iwm_handler *lmac_handlers;
|
||||
DECLARE_BITMAP(lmac_handler_map, LMAC_COMMAND_ID_NUM);
|
||||
DECLARE_BITMAP(umac_handler_map, LMAC_COMMAND_ID_NUM);
|
||||
DECLARE_BITMAP(udma_handler_map, LMAC_COMMAND_ID_NUM);
|
||||
|
||||
struct list_head wifi_pending_cmd;
|
||||
struct list_head nonwifi_pending_cmd;
|
||||
u16 wifi_seq_num;
|
||||
u8 nonwifi_seq_num;
|
||||
spinlock_t cmd_lock;
|
||||
|
||||
u32 core_enabled;
|
||||
|
||||
u8 scan_id;
|
||||
struct cfg80211_scan_request *scan_request;
|
||||
|
||||
struct sk_buff_head rx_list;
|
||||
struct list_head rx_tickets;
|
||||
struct list_head rx_packets[IWM_RX_ID_HASH];
|
||||
struct workqueue_struct *rx_wq;
|
||||
struct work_struct rx_worker;
|
||||
|
||||
struct iwm_tx_credit tx_credit;
|
||||
struct iwm_tx_queue txq[IWM_TX_QUEUES];
|
||||
|
||||
struct iwm_key keys[IWM_NUM_KEYS];
|
||||
struct iwm_key *default_key;
|
||||
|
||||
wait_queue_head_t mlme_queue;
|
||||
|
||||
struct iw_statistics wstats;
|
||||
struct delayed_work stats_request;
|
||||
|
||||
struct iwm_debugfs dbg;
|
||||
|
||||
u8 *eeprom;
|
||||
struct timer_list watchdog;
|
||||
struct work_struct reset_worker;
|
||||
struct rfkill *rfkill;
|
||||
|
||||
char private[0] __attribute__((__aligned__(NETDEV_ALIGN)));
|
||||
};
|
||||
|
||||
static inline void *iwm_private(struct iwm_priv *iwm)
|
||||
{
|
||||
BUG_ON(!iwm);
|
||||
return &iwm->private;
|
||||
}
|
||||
|
||||
#define hw_to_iwm(h) (h->iwm)
|
||||
#define iwm_to_dev(i) (wiphy_dev(i->wdev->wiphy))
|
||||
#define iwm_to_wiphy(i) (i->wdev->wiphy)
|
||||
#define wiphy_to_iwm(w) (struct iwm_priv *)(wiphy_priv(w))
|
||||
#define iwm_to_wdev(i) (i->wdev)
|
||||
#define wdev_to_iwm(w) (struct iwm_priv *)(wdev_priv(w))
|
||||
#define iwm_to_ndev(i) (i->wdev->netdev)
|
||||
#define ndev_to_iwm(n) (wdev_to_iwm(n->ieee80211_ptr))
|
||||
#define skb_to_rx_info(s) ((struct iwm_rx_info *)(s->cb))
|
||||
#define skb_to_tx_info(s) ((struct iwm_tx_info *)s->cb)
|
||||
|
||||
extern const struct iw_handler_def iwm_iw_handler_def;
|
||||
|
||||
void *iwm_if_alloc(int sizeof_bus, struct device *dev,
|
||||
struct iwm_if_ops *if_ops);
|
||||
void iwm_if_free(struct iwm_priv *iwm);
|
||||
int iwm_mode_to_nl80211_iftype(int mode);
|
||||
int iwm_priv_init(struct iwm_priv *iwm);
|
||||
void iwm_reset(struct iwm_priv *iwm);
|
||||
void iwm_tx_credit_init_pools(struct iwm_priv *iwm,
|
||||
struct iwm_umac_notif_alive *alive);
|
||||
int iwm_tx_credit_alloc(struct iwm_priv *iwm, int id, int nb);
|
||||
int iwm_notif_send(struct iwm_priv *iwm, struct iwm_wifi_cmd *cmd,
|
||||
u8 cmd_id, u8 source, u8 *buf, unsigned long buf_size);
|
||||
int iwm_notif_handle(struct iwm_priv *iwm, u32 cmd, u8 source, long timeout);
|
||||
void iwm_init_default_profile(struct iwm_priv *iwm,
|
||||
struct iwm_umac_profile *profile);
|
||||
void iwm_link_on(struct iwm_priv *iwm);
|
||||
void iwm_link_off(struct iwm_priv *iwm);
|
||||
int iwm_up(struct iwm_priv *iwm);
|
||||
int iwm_down(struct iwm_priv *iwm);
|
||||
|
||||
/* TX API */
|
||||
void iwm_tx_credit_inc(struct iwm_priv *iwm, int id, int total_freed_pages);
|
||||
void iwm_tx_worker(struct work_struct *work);
|
||||
int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
|
||||
|
||||
/* RX API */
|
||||
void iwm_rx_setup_handlers(struct iwm_priv *iwm);
|
||||
int iwm_rx_handle(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size);
|
||||
int iwm_rx_handle_resp(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size,
|
||||
struct iwm_wifi_cmd *cmd);
|
||||
void iwm_rx_free(struct iwm_priv *iwm);
|
||||
|
||||
/* RF Kill API */
|
||||
int iwm_rfkill_init(struct iwm_priv *iwm);
|
||||
void iwm_rfkill_exit(struct iwm_priv *iwm);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,457 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __IWM_LMAC_H__
|
||||
#define __IWM_LMAC_H__
|
||||
|
||||
struct iwm_lmac_hdr {
|
||||
u8 id;
|
||||
u8 flags;
|
||||
__le16 seq_num;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* LMAC commands */
|
||||
#define CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_AFTER_MSK 0x1
|
||||
|
||||
struct iwm_lmac_cal_cfg_elt {
|
||||
__le32 enable; /* 1 means LMAC needs to do something */
|
||||
__le32 start; /* 1 to start calibration, 0 to stop */
|
||||
__le32 send_res; /* 1 for sending back results */
|
||||
__le32 apply_res; /* 1 for applying calibration results to HW */
|
||||
__le32 reserved;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_lmac_cal_cfg_status {
|
||||
struct iwm_lmac_cal_cfg_elt init;
|
||||
struct iwm_lmac_cal_cfg_elt periodic;
|
||||
__le32 flags; /* CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_AFTER_MSK */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_lmac_cal_cfg_cmd {
|
||||
struct iwm_lmac_cal_cfg_status ucode_cfg;
|
||||
struct iwm_lmac_cal_cfg_status driver_cfg;
|
||||
__le32 reserved;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_lmac_cal_cfg_resp {
|
||||
__le32 status;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define IWM_CARD_STATE_SW_HW_ENABLED 0x00
|
||||
#define IWM_CARD_STATE_HW_DISABLED 0x01
|
||||
#define IWM_CARD_STATE_SW_DISABLED 0x02
|
||||
#define IWM_CARD_STATE_CTKILL_DISABLED 0x04
|
||||
#define IWM_CARD_STATE_IS_RXON 0x10
|
||||
|
||||
struct iwm_lmac_card_state {
|
||||
__le32 flags;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/**
|
||||
* COEX_PRIORITY_TABLE_CMD
|
||||
*
|
||||
* Priority entry for each state
|
||||
* Will keep two tables, for STA and WIPAN
|
||||
*/
|
||||
enum {
|
||||
/* UN-ASSOCIATION PART */
|
||||
COEX_UNASSOC_IDLE = 0,
|
||||
COEX_UNASSOC_MANUAL_SCAN,
|
||||
COEX_UNASSOC_AUTO_SCAN,
|
||||
|
||||
/* CALIBRATION */
|
||||
COEX_CALIBRATION,
|
||||
COEX_PERIODIC_CALIBRATION,
|
||||
|
||||
/* CONNECTION */
|
||||
COEX_CONNECTION_ESTAB,
|
||||
|
||||
/* ASSOCIATION PART */
|
||||
COEX_ASSOCIATED_IDLE,
|
||||
COEX_ASSOC_MANUAL_SCAN,
|
||||
COEX_ASSOC_AUTO_SCAN,
|
||||
COEX_ASSOC_ACTIVE_LEVEL,
|
||||
|
||||
/* RF ON/OFF */
|
||||
COEX_RF_ON,
|
||||
COEX_RF_OFF,
|
||||
COEX_STAND_ALONE_DEBUG,
|
||||
|
||||
/* IPNN */
|
||||
COEX_IPAN_ASSOC_LEVEL,
|
||||
|
||||
/* RESERVED */
|
||||
COEX_RSRVD1,
|
||||
COEX_RSRVD2,
|
||||
|
||||
COEX_EVENTS_NUM
|
||||
};
|
||||
|
||||
#define COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK 0x1
|
||||
#define COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK 0x2
|
||||
#define COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK 0x4
|
||||
|
||||
struct coex_event {
|
||||
u8 req_prio;
|
||||
u8 win_med_prio;
|
||||
u8 reserved;
|
||||
u8 flags;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define COEX_FLAGS_STA_TABLE_VALID_MSK 0x1
|
||||
#define COEX_FLAGS_UNASSOC_WAKEUP_UMASK_MSK 0x4
|
||||
#define COEX_FLAGS_ASSOC_WAKEUP_UMASK_MSK 0x8
|
||||
#define COEX_FLAGS_COEX_ENABLE_MSK 0x80
|
||||
|
||||
struct iwm_coex_prio_table_cmd {
|
||||
u8 flags;
|
||||
u8 reserved[3];
|
||||
struct coex_event sta_prio[COEX_EVENTS_NUM];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Coexistence definitions
|
||||
*
|
||||
* Constants to fill in the Priorities' Tables
|
||||
* RP - Requested Priority
|
||||
* WP - Win Medium Priority: priority assigned when the contention has been won
|
||||
* FLAGS - Combination of COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK and
|
||||
* COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK
|
||||
*/
|
||||
|
||||
#define COEX_UNASSOC_IDLE_FLAGS 0
|
||||
#define COEX_UNASSOC_MANUAL_SCAN_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
|
||||
COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
|
||||
#define COEX_UNASSOC_AUTO_SCAN_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
|
||||
COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
|
||||
#define COEX_CALIBRATION_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
|
||||
COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
|
||||
#define COEX_PERIODIC_CALIBRATION_FLAGS 0
|
||||
/* COEX_CONNECTION_ESTAB: we need DELAY_MEDIUM_FREE_NTFY to let WiMAX
|
||||
* disconnect from network. */
|
||||
#define COEX_CONNECTION_ESTAB_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
|
||||
COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK | \
|
||||
COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK)
|
||||
#define COEX_ASSOCIATED_IDLE_FLAGS 0
|
||||
#define COEX_ASSOC_MANUAL_SCAN_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
|
||||
COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
|
||||
#define COEX_ASSOC_AUTO_SCAN_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
|
||||
COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
|
||||
#define COEX_ASSOC_ACTIVE_LEVEL_FLAGS 0
|
||||
#define COEX_RF_ON_FLAGS 0
|
||||
#define COEX_RF_OFF_FLAGS 0
|
||||
#define COEX_STAND_ALONE_DEBUG_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
|
||||
COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK)
|
||||
#define COEX_IPAN_ASSOC_LEVEL_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
|
||||
COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK | \
|
||||
COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK)
|
||||
#define COEX_RSRVD1_FLAGS 0
|
||||
#define COEX_RSRVD2_FLAGS 0
|
||||
/* XOR_RF_ON is the event wrapping all radio ownership. We need
|
||||
* DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network. */
|
||||
#define COEX_XOR_RF_ON_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \
|
||||
COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK | \
|
||||
COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK)
|
||||
|
||||
/* LMAC OP CODES */
|
||||
#define REPLY_PAD 0x0
|
||||
#define REPLY_ALIVE 0x1
|
||||
#define REPLY_ERROR 0x2
|
||||
#define REPLY_ECHO 0x3
|
||||
#define REPLY_HALT 0x6
|
||||
|
||||
/* RXON state commands */
|
||||
#define REPLY_RX_ON 0x10
|
||||
#define REPLY_RX_ON_ASSOC 0x11
|
||||
#define REPLY_RX_OFF 0x12
|
||||
#define REPLY_QOS_PARAM 0x13
|
||||
#define REPLY_RX_ON_TIMING 0x14
|
||||
#define REPLY_INTERNAL_QOS_PARAM 0x15
|
||||
#define REPLY_RX_INT_TIMEOUT_CNFG 0x16
|
||||
#define REPLY_NULL 0x17
|
||||
|
||||
/* Multi-Station support */
|
||||
#define REPLY_ADD_STA 0x18
|
||||
#define REPLY_REMOVE_STA 0x19
|
||||
#define REPLY_RESET_ALL_STA 0x1a
|
||||
|
||||
/* RX, TX */
|
||||
#define REPLY_ALM_RX 0x1b
|
||||
#define REPLY_TX 0x1c
|
||||
#define REPLY_TXFIFO_FLUSH 0x1e
|
||||
|
||||
/* MISC commands */
|
||||
#define REPLY_MGMT_MCAST_KEY 0x1f
|
||||
#define REPLY_WEPKEY 0x20
|
||||
#define REPLY_INIT_IV 0x21
|
||||
#define REPLY_WRITE_MIB 0x22
|
||||
#define REPLY_READ_MIB 0x23
|
||||
#define REPLY_RADIO_FE 0x24
|
||||
#define REPLY_TXFIFO_CFG 0x25
|
||||
#define REPLY_WRITE_READ 0x26
|
||||
#define REPLY_INSTALL_SEC_KEY 0x27
|
||||
|
||||
|
||||
#define REPLY_RATE_SCALE 0x47
|
||||
#define REPLY_LEDS_CMD 0x48
|
||||
#define REPLY_TX_LINK_QUALITY_CMD 0x4e
|
||||
#define REPLY_ANA_MIB_OVERRIDE_CMD 0x4f
|
||||
#define REPLY_WRITE2REG_CMD 0x50
|
||||
|
||||
/* winfi-wifi coexistence */
|
||||
#define COEX_PRIORITY_TABLE_CMD 0x5a
|
||||
#define COEX_MEDIUM_NOTIFICATION 0x5b
|
||||
#define COEX_EVENT_CMD 0x5c
|
||||
|
||||
/* more Protocol and Protocol-test commands */
|
||||
#define REPLY_MAX_SLEEP_TIME_CMD 0x61
|
||||
#define CALIBRATION_CFG_CMD 0x65
|
||||
#define CALIBRATION_RES_NOTIFICATION 0x66
|
||||
#define CALIBRATION_COMPLETE_NOTIFICATION 0x67
|
||||
|
||||
/* Measurements */
|
||||
#define REPLY_QUIET_CMD 0x71
|
||||
#define REPLY_CHANNEL_SWITCH 0x72
|
||||
#define CHANNEL_SWITCH_NOTIFICATION 0x73
|
||||
|
||||
#define REPLY_SPECTRUM_MEASUREMENT_CMD 0x74
|
||||
#define SPECTRUM_MEASURE_NOTIFICATION 0x75
|
||||
#define REPLY_MEASUREMENT_ABORT_CMD 0x76
|
||||
|
||||
/* Power Management */
|
||||
#define POWER_TABLE_CMD 0x77
|
||||
#define SAVE_RESTORE_ADRESS_CMD 0x78
|
||||
#define REPLY_WATERMARK_CMD 0x79
|
||||
#define PM_DEBUG_STATISTIC_NOTIFIC 0x7B
|
||||
#define PD_FLUSH_N_NOTIFICATION 0x7C
|
||||
|
||||
/* Scan commands and notifications */
|
||||
#define REPLY_SCAN_REQUEST_CMD 0x80
|
||||
#define REPLY_SCAN_ABORT_CMD 0x81
|
||||
#define SCAN_START_NOTIFICATION 0x82
|
||||
#define SCAN_RESULTS_NOTIFICATION 0x83
|
||||
#define SCAN_COMPLETE_NOTIFICATION 0x84
|
||||
|
||||
/* Continuous TX commands */
|
||||
#define REPLY_CONT_TX_CMD 0x85
|
||||
#define END_OF_CONT_TX_NOTIFICATION 0x86
|
||||
|
||||
/* Timer/Eeprom commands */
|
||||
#define TIMER_CMD 0x87
|
||||
#define EEPROM_WRITE_CMD 0x88
|
||||
|
||||
/* PAPD commands */
|
||||
#define FEEDBACK_REQUEST_NOTIFICATION 0x8b
|
||||
#define REPLY_CW_CMD 0x8c
|
||||
|
||||
/* IBSS/AP commands Continue */
|
||||
#define BEACON_NOTIFICATION 0x90
|
||||
#define REPLY_TX_BEACON 0x91
|
||||
#define REPLY_REQUEST_ATIM 0x93
|
||||
#define WHO_IS_AWAKE_NOTIFICATION 0x94
|
||||
#define TX_PWR_DBM_LIMIT_CMD 0x95
|
||||
#define QUIET_NOTIFICATION 0x96
|
||||
#define TX_PWR_TABLE_CMD 0x97
|
||||
#define TX_ANT_CONFIGURATION_CMD 0x98
|
||||
#define MEASURE_ABORT_NOTIFICATION 0x99
|
||||
#define REPLY_CALIBRATION_TUNE 0x9a
|
||||
|
||||
/* bt config command */
|
||||
#define REPLY_BT_CONFIG 0x9b
|
||||
#define REPLY_STATISTICS_CMD 0x9c
|
||||
#define STATISTICS_NOTIFICATION 0x9d
|
||||
|
||||
/* RF-KILL commands and notifications */
|
||||
#define REPLY_CARD_STATE_CMD 0xa0
|
||||
#define CARD_STATE_NOTIFICATION 0xa1
|
||||
|
||||
/* Missed beacons notification */
|
||||
#define MISSED_BEACONS_NOTIFICATION 0xa2
|
||||
#define MISSED_BEACONS_NOTIFICATION_TH_CMD 0xa3
|
||||
|
||||
#define REPLY_CT_KILL_CONFIG_CMD 0xa4
|
||||
|
||||
/* HD commands and notifications */
|
||||
#define REPLY_HD_PARAMS_CMD 0xa6
|
||||
#define HD_PARAMS_NOTIFICATION 0xa7
|
||||
#define SENSITIVITY_CMD 0xa8
|
||||
#define U_APSD_PARAMS_CMD 0xa9
|
||||
#define NOISY_PLATFORM_CMD 0xaa
|
||||
#define ILLEGAL_CMD 0xac
|
||||
#define REPLY_PHY_CALIBRATION_CMD 0xb0
|
||||
#define REPLAY_RX_GAIN_CALIB_CMD 0xb1
|
||||
|
||||
/* WiPAN commands */
|
||||
#define REPLY_WIPAN_PARAMS_CMD 0xb2
|
||||
#define REPLY_WIPAN_RX_ON_CMD 0xb3
|
||||
#define REPLY_WIPAN_RX_ON_TIMING 0xb4
|
||||
#define REPLY_WIPAN_TX_PWR_TABLE_CMD 0xb5
|
||||
#define REPLY_WIPAN_RXON_ASSOC_CMD 0xb6
|
||||
#define REPLY_WIPAN_QOS_PARAM 0xb7
|
||||
#define WIPAN_REPLY_WEPKEY 0xb8
|
||||
|
||||
/* BeamForming commands */
|
||||
#define BEAMFORMER_CFG_CMD 0xba
|
||||
#define BEAMFORMEE_NOTIFICATION 0xbb
|
||||
|
||||
/* TGn new Commands */
|
||||
#define REPLY_RX_PHY_CMD 0xc0
|
||||
#define REPLY_RX_MPDU_CMD 0xc1
|
||||
#define REPLY_MULTICAST_HASH 0xc2
|
||||
#define REPLY_KDR_RX 0xc3
|
||||
#define REPLY_RX_DSP_EXT_INFO 0xc4
|
||||
#define REPLY_COMPRESSED_BA 0xc5
|
||||
|
||||
/* PNC commands */
|
||||
#define PNC_CONFIG_CMD 0xc8
|
||||
#define PNC_UPDATE_TABLE_CMD 0xc9
|
||||
#define XVT_GENERAL_CTRL_CMD 0xca
|
||||
#define REPLY_LEGACY_RADIO_FE 0xdd
|
||||
|
||||
/* WoWLAN commands */
|
||||
#define WOWLAN_PATTERNS 0xe0
|
||||
#define WOWLAN_WAKEUP_FILTER 0xe1
|
||||
#define WOWLAN_TSC_RSC_PARAM 0xe2
|
||||
#define WOWLAN_TKIP_PARAM 0xe3
|
||||
#define WOWLAN_KEK_KCK_MATERIAL 0xe4
|
||||
#define WOWLAN_GET_STATUSES 0xe5
|
||||
#define WOWLAN_TX_POWER_PER_DB 0xe6
|
||||
#define REPLY_WOWLAN_GET_STATUSES WOWLAN_GET_STATUSES
|
||||
|
||||
#define REPLY_DEBUG_CMD 0xf0
|
||||
#define REPLY_DSP_DEBUG_CMD 0xf1
|
||||
#define REPLY_DEBUG_MONITOR_CMD 0xf2
|
||||
#define REPLY_DEBUG_XVT_CMD 0xf3
|
||||
#define REPLY_DEBUG_DC_CALIB 0xf4
|
||||
#define REPLY_DYNAMIC_BP 0xf5
|
||||
|
||||
/* General purpose Commands */
|
||||
#define REPLY_GP1_CMD 0xfa
|
||||
#define REPLY_GP2_CMD 0xfb
|
||||
#define REPLY_GP3_CMD 0xfc
|
||||
#define REPLY_GP4_CMD 0xfd
|
||||
#define REPLY_REPLAY_WRAPPER 0xfe
|
||||
#define REPLY_FRAME_DURATION_CALC_CMD 0xff
|
||||
|
||||
#define LMAC_COMMAND_ID_MAX 0xff
|
||||
#define LMAC_COMMAND_ID_NUM (LMAC_COMMAND_ID_MAX + 1)
|
||||
|
||||
|
||||
/* Calibration */
|
||||
|
||||
enum {
|
||||
PHY_CALIBRATE_DC_CMD = 0,
|
||||
PHY_CALIBRATE_LO_CMD = 1,
|
||||
PHY_CALIBRATE_RX_BB_CMD = 2,
|
||||
PHY_CALIBRATE_TX_IQ_CMD = 3,
|
||||
PHY_CALIBRATE_RX_IQ_CMD = 4,
|
||||
PHY_CALIBRATION_NOISE_CMD = 5,
|
||||
PHY_CALIBRATE_AGC_TABLE_CMD = 6,
|
||||
PHY_CALIBRATE_CRYSTAL_FRQ_CMD = 7,
|
||||
PHY_CALIBRATE_OPCODES_NUM,
|
||||
SHILOH_PHY_CALIBRATE_DC_CMD = 8,
|
||||
SHILOH_PHY_CALIBRATE_LO_CMD = 9,
|
||||
SHILOH_PHY_CALIBRATE_RX_BB_CMD = 10,
|
||||
SHILOH_PHY_CALIBRATE_TX_IQ_CMD = 11,
|
||||
SHILOH_PHY_CALIBRATE_RX_IQ_CMD = 12,
|
||||
SHILOH_PHY_CALIBRATION_NOISE_CMD = 13,
|
||||
SHILOH_PHY_CALIBRATE_AGC_TABLE_CMD = 14,
|
||||
SHILOH_PHY_CALIBRATE_CRYSTAL_FRQ_CMD = 15,
|
||||
SHILOH_PHY_CALIBRATE_BASE_BAND_CMD = 16,
|
||||
SHILOH_PHY_CALIBRATE_TXIQ_PERIODIC_CMD = 17,
|
||||
CALIBRATION_CMD_NUM,
|
||||
};
|
||||
|
||||
struct iwm_lmac_calib_hdr {
|
||||
u8 opcode;
|
||||
u8 first_grp;
|
||||
u8 grp_num;
|
||||
u8 all_data_valid;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define IWM_LMAC_CALIB_FREQ_GROUPS_NR 7
|
||||
#define IWM_CALIB_FREQ_GROUPS_NR 5
|
||||
#define IWM_CALIB_DC_MODES_NR 12
|
||||
|
||||
struct iwm_calib_rxiq_entry {
|
||||
u16 ptam_postdist_ars;
|
||||
u16 ptam_postdist_arc;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_calib_rxiq_group {
|
||||
struct iwm_calib_rxiq_entry mode[IWM_CALIB_DC_MODES_NR];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_lmac_calib_rxiq {
|
||||
struct iwm_calib_rxiq_group group[IWM_LMAC_CALIB_FREQ_GROUPS_NR];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_calib_rxiq {
|
||||
struct iwm_lmac_calib_hdr hdr;
|
||||
struct iwm_calib_rxiq_group group[IWM_CALIB_FREQ_GROUPS_NR];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define LMAC_STA_ID_SEED 0x0f
|
||||
#define LMAC_STA_ID_POS 0
|
||||
|
||||
#define LMAC_STA_COLOR_SEED 0x7
|
||||
#define LMAC_STA_COLOR_POS 4
|
||||
|
||||
struct iwm_lmac_power_report {
|
||||
u8 pa_status;
|
||||
u8 pa_integ_res_A[3];
|
||||
u8 pa_integ_res_B[3];
|
||||
u8 pa_integ_res_C[3];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_lmac_tx_resp {
|
||||
u8 frame_cnt; /* 1-no aggregation, greater then 1 - aggregation */
|
||||
u8 bt_kill_cnt;
|
||||
__le16 retry_cnt;
|
||||
__le32 initial_tx_rate;
|
||||
__le16 wireless_media_time;
|
||||
struct iwm_lmac_power_report power_report;
|
||||
__le32 tfd_info;
|
||||
__le16 seq_ctl;
|
||||
__le16 byte_cnt;
|
||||
u8 tlc_rate_info;
|
||||
u8 ra_tid;
|
||||
__le16 frame_ctl;
|
||||
__le32 status;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#endif
|
|
@ -0,0 +1,680 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/ieee80211.h>
|
||||
#include <linux/wireless.h>
|
||||
|
||||
#include "iwm.h"
|
||||
#include "debug.h"
|
||||
#include "bus.h"
|
||||
#include "umac.h"
|
||||
#include "commands.h"
|
||||
#include "hal.h"
|
||||
#include "fw.h"
|
||||
#include "rx.h"
|
||||
|
||||
static struct iwm_conf def_iwm_conf = {
|
||||
|
||||
.sdio_ior_timeout = 5000,
|
||||
.init_calib_map = BIT(PHY_CALIBRATE_DC_CMD) |
|
||||
BIT(PHY_CALIBRATE_LO_CMD) |
|
||||
BIT(PHY_CALIBRATE_TX_IQ_CMD) |
|
||||
BIT(PHY_CALIBRATE_RX_IQ_CMD),
|
||||
.periodic_calib_map = BIT(PHY_CALIBRATE_DC_CMD) |
|
||||
BIT(PHY_CALIBRATE_LO_CMD) |
|
||||
BIT(PHY_CALIBRATE_TX_IQ_CMD) |
|
||||
BIT(PHY_CALIBRATE_RX_IQ_CMD) |
|
||||
BIT(SHILOH_PHY_CALIBRATE_BASE_BAND_CMD),
|
||||
.reset_on_fatal_err = 1,
|
||||
.auto_connect = 1,
|
||||
.wimax_not_present = 0,
|
||||
.enable_qos = 1,
|
||||
.mode = UMAC_MODE_BSS,
|
||||
|
||||
/* UMAC configuration */
|
||||
.power_index = 0,
|
||||
.frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD,
|
||||
.rts_threshold = IEEE80211_MAX_RTS_THRESHOLD,
|
||||
.cts_to_self = 0,
|
||||
|
||||
.assoc_timeout = 2,
|
||||
.roam_timeout = 10,
|
||||
.wireless_mode = WIRELESS_MODE_11A | WIRELESS_MODE_11G,
|
||||
.coexist_mode = COEX_MODE_CM,
|
||||
|
||||
/* IBSS */
|
||||
.ibss_band = UMAC_BAND_2GHZ,
|
||||
.ibss_channel = 1,
|
||||
|
||||
.mac_addr = {0x00, 0x02, 0xb3, 0x01, 0x02, 0x03},
|
||||
};
|
||||
|
||||
static int modparam_reset;
|
||||
module_param_named(reset, modparam_reset, bool, 0644);
|
||||
MODULE_PARM_DESC(reset, "reset on firmware errors (default 0 [not reset])");
|
||||
|
||||
int iwm_mode_to_nl80211_iftype(int mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case UMAC_MODE_BSS:
|
||||
return NL80211_IFTYPE_STATION;
|
||||
case UMAC_MODE_IBSS:
|
||||
return NL80211_IFTYPE_ADHOC;
|
||||
default:
|
||||
return NL80211_IFTYPE_UNSPECIFIED;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iwm_statistics_request(struct work_struct *work)
|
||||
{
|
||||
struct iwm_priv *iwm =
|
||||
container_of(work, struct iwm_priv, stats_request.work);
|
||||
|
||||
iwm_send_umac_stats_req(iwm, 0);
|
||||
}
|
||||
|
||||
static void iwm_reset_worker(struct work_struct *work)
|
||||
{
|
||||
struct iwm_priv *iwm;
|
||||
struct iwm_umac_profile *profile = NULL;
|
||||
int uninitialized_var(ret), retry = 0;
|
||||
|
||||
iwm = container_of(work, struct iwm_priv, reset_worker);
|
||||
|
||||
if (iwm->umac_profile_active) {
|
||||
profile = kmalloc(sizeof(struct iwm_umac_profile), GFP_KERNEL);
|
||||
if (profile)
|
||||
memcpy(profile, iwm->umac_profile, sizeof(*profile));
|
||||
else
|
||||
IWM_ERR(iwm, "Couldn't alloc memory for profile\n");
|
||||
}
|
||||
|
||||
iwm_down(iwm);
|
||||
|
||||
while (retry++ < 3) {
|
||||
ret = iwm_up(iwm);
|
||||
if (!ret)
|
||||
break;
|
||||
|
||||
schedule_timeout_uninterruptible(10 * HZ);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
IWM_WARN(iwm, "iwm_up() failed: %d\n", ret);
|
||||
|
||||
kfree(profile);
|
||||
return;
|
||||
}
|
||||
|
||||
if (profile) {
|
||||
IWM_DBG_MLME(iwm, DBG, "Resend UMAC profile\n");
|
||||
memcpy(iwm->umac_profile, profile, sizeof(*profile));
|
||||
iwm_send_mlme_profile(iwm);
|
||||
kfree(profile);
|
||||
}
|
||||
}
|
||||
|
||||
static void iwm_watchdog(unsigned long data)
|
||||
{
|
||||
struct iwm_priv *iwm = (struct iwm_priv *)data;
|
||||
|
||||
IWM_WARN(iwm, "Watchdog expired: UMAC stalls!\n");
|
||||
|
||||
if (modparam_reset)
|
||||
schedule_work(&iwm->reset_worker);
|
||||
}
|
||||
|
||||
int iwm_priv_init(struct iwm_priv *iwm)
|
||||
{
|
||||
int i;
|
||||
char name[32];
|
||||
|
||||
iwm->status = 0;
|
||||
INIT_LIST_HEAD(&iwm->pending_notif);
|
||||
init_waitqueue_head(&iwm->notif_queue);
|
||||
init_waitqueue_head(&iwm->nonwifi_queue);
|
||||
init_waitqueue_head(&iwm->mlme_queue);
|
||||
memcpy(&iwm->conf, &def_iwm_conf, sizeof(struct iwm_conf));
|
||||
spin_lock_init(&iwm->tx_credit.lock);
|
||||
INIT_LIST_HEAD(&iwm->wifi_pending_cmd);
|
||||
INIT_LIST_HEAD(&iwm->nonwifi_pending_cmd);
|
||||
iwm->wifi_seq_num = UMAC_WIFI_SEQ_NUM_BASE;
|
||||
iwm->nonwifi_seq_num = UMAC_NONWIFI_SEQ_NUM_BASE;
|
||||
spin_lock_init(&iwm->cmd_lock);
|
||||
iwm->scan_id = 1;
|
||||
INIT_DELAYED_WORK(&iwm->stats_request, iwm_statistics_request);
|
||||
INIT_WORK(&iwm->reset_worker, iwm_reset_worker);
|
||||
INIT_LIST_HEAD(&iwm->bss_list);
|
||||
|
||||
skb_queue_head_init(&iwm->rx_list);
|
||||
INIT_LIST_HEAD(&iwm->rx_tickets);
|
||||
for (i = 0; i < IWM_RX_ID_HASH; i++)
|
||||
INIT_LIST_HEAD(&iwm->rx_packets[i]);
|
||||
|
||||
INIT_WORK(&iwm->rx_worker, iwm_rx_worker);
|
||||
|
||||
iwm->rx_wq = create_singlethread_workqueue(KBUILD_MODNAME "_rx");
|
||||
if (!iwm->rx_wq)
|
||||
return -EAGAIN;
|
||||
|
||||
for (i = 0; i < IWM_TX_QUEUES; i++) {
|
||||
INIT_WORK(&iwm->txq[i].worker, iwm_tx_worker);
|
||||
snprintf(name, 32, KBUILD_MODNAME "_tx_%d", i);
|
||||
iwm->txq[i].id = i;
|
||||
iwm->txq[i].wq = create_singlethread_workqueue(name);
|
||||
if (!iwm->txq[i].wq)
|
||||
return -EAGAIN;
|
||||
|
||||
skb_queue_head_init(&iwm->txq[i].queue);
|
||||
}
|
||||
|
||||
for (i = 0; i < IWM_NUM_KEYS; i++)
|
||||
memset(&iwm->keys[i], 0, sizeof(struct iwm_key));
|
||||
|
||||
iwm->default_key = NULL;
|
||||
|
||||
init_timer(&iwm->watchdog);
|
||||
iwm->watchdog.function = iwm_watchdog;
|
||||
iwm->watchdog.data = (unsigned long)iwm;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We reset all the structures, and we reset the UMAC.
|
||||
* After calling this routine, you're expected to reload
|
||||
* the firmware.
|
||||
*/
|
||||
void iwm_reset(struct iwm_priv *iwm)
|
||||
{
|
||||
struct iwm_notif *notif, *next;
|
||||
|
||||
if (test_bit(IWM_STATUS_READY, &iwm->status))
|
||||
iwm_target_reset(iwm);
|
||||
|
||||
iwm->status = 0;
|
||||
iwm->scan_id = 1;
|
||||
|
||||
list_for_each_entry_safe(notif, next, &iwm->pending_notif, pending) {
|
||||
list_del(¬if->pending);
|
||||
kfree(notif->buf);
|
||||
kfree(notif);
|
||||
}
|
||||
|
||||
iwm_cmd_flush(iwm);
|
||||
|
||||
flush_workqueue(iwm->rx_wq);
|
||||
|
||||
iwm_link_off(iwm);
|
||||
}
|
||||
|
||||
/*
|
||||
* Notification code:
|
||||
*
|
||||
* We're faced with the following issue: Any host command can
|
||||
* have an answer or not, and if there's an answer to expect,
|
||||
* it can be treated synchronously or asynchronously.
|
||||
* To work around the synchronous answer case, we implemented
|
||||
* our notification mechanism.
|
||||
* When a code path needs to wait for a command response
|
||||
* synchronously, it calls notif_handle(), which waits for the
|
||||
* right notification to show up, and then process it. Before
|
||||
* starting to wait, it registered as a waiter for this specific
|
||||
* answer (by toggling a bit in on of the handler_map), so that
|
||||
* the rx code knows that it needs to send a notification to the
|
||||
* waiting processes. It does so by calling iwm_notif_send(),
|
||||
* which adds the notification to the pending notifications list,
|
||||
* and then wakes the waiting processes up.
|
||||
*/
|
||||
int iwm_notif_send(struct iwm_priv *iwm, struct iwm_wifi_cmd *cmd,
|
||||
u8 cmd_id, u8 source, u8 *buf, unsigned long buf_size)
|
||||
{
|
||||
struct iwm_notif *notif;
|
||||
|
||||
notif = kzalloc(sizeof(struct iwm_notif), GFP_KERNEL);
|
||||
if (!notif) {
|
||||
IWM_ERR(iwm, "Couldn't alloc memory for notification\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(¬if->pending);
|
||||
notif->cmd = cmd;
|
||||
notif->cmd_id = cmd_id;
|
||||
notif->src = source;
|
||||
notif->buf = kzalloc(buf_size, GFP_KERNEL);
|
||||
if (!notif->buf) {
|
||||
IWM_ERR(iwm, "Couldn't alloc notification buffer\n");
|
||||
kfree(notif);
|
||||
return -ENOMEM;
|
||||
}
|
||||
notif->buf_size = buf_size;
|
||||
memcpy(notif->buf, buf, buf_size);
|
||||
list_add_tail(¬if->pending, &iwm->pending_notif);
|
||||
|
||||
wake_up_interruptible(&iwm->notif_queue);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct iwm_notif *iwm_notif_find(struct iwm_priv *iwm, u32 cmd,
|
||||
u8 source)
|
||||
{
|
||||
struct iwm_notif *notif, *next;
|
||||
|
||||
list_for_each_entry_safe(notif, next, &iwm->pending_notif, pending) {
|
||||
if ((notif->cmd_id == cmd) && (notif->src == source)) {
|
||||
list_del(¬if->pending);
|
||||
return notif;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct iwm_notif *iwm_notif_wait(struct iwm_priv *iwm, u32 cmd,
|
||||
u8 source, long timeout)
|
||||
{
|
||||
int ret;
|
||||
struct iwm_notif *notif;
|
||||
unsigned long *map = NULL;
|
||||
|
||||
switch (source) {
|
||||
case IWM_SRC_LMAC:
|
||||
map = &iwm->lmac_handler_map[0];
|
||||
break;
|
||||
case IWM_SRC_UMAC:
|
||||
map = &iwm->umac_handler_map[0];
|
||||
break;
|
||||
case IWM_SRC_UDMA:
|
||||
map = &iwm->udma_handler_map[0];
|
||||
break;
|
||||
}
|
||||
|
||||
set_bit(cmd, map);
|
||||
|
||||
ret = wait_event_interruptible_timeout(iwm->notif_queue,
|
||||
((notif = iwm_notif_find(iwm, cmd, source)) != NULL),
|
||||
timeout);
|
||||
clear_bit(cmd, map);
|
||||
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
return notif;
|
||||
}
|
||||
|
||||
int iwm_notif_handle(struct iwm_priv *iwm, u32 cmd, u8 source, long timeout)
|
||||
{
|
||||
int ret;
|
||||
struct iwm_notif *notif;
|
||||
|
||||
notif = iwm_notif_wait(iwm, cmd, source, timeout);
|
||||
if (!notif)
|
||||
return -ETIME;
|
||||
|
||||
ret = iwm_rx_handle_resp(iwm, notif->buf, notif->buf_size, notif->cmd);
|
||||
kfree(notif->buf);
|
||||
kfree(notif);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iwm_config_boot_params(struct iwm_priv *iwm)
|
||||
{
|
||||
struct iwm_udma_nonwifi_cmd target_cmd;
|
||||
int ret;
|
||||
|
||||
/* check Wimax is off and config debug monitor */
|
||||
if (iwm->conf.wimax_not_present) {
|
||||
u32 data1 = 0x1f;
|
||||
u32 addr1 = 0x606BE258;
|
||||
|
||||
u32 data2_set = 0x0;
|
||||
u32 data2_clr = 0x1;
|
||||
u32 addr2 = 0x606BE100;
|
||||
|
||||
u32 data3 = 0x1;
|
||||
u32 addr3 = 0x606BEC00;
|
||||
|
||||
target_cmd.resp = 0;
|
||||
target_cmd.handle_by_hw = 0;
|
||||
target_cmd.eop = 1;
|
||||
|
||||
target_cmd.opcode = UMAC_HDI_OUT_OPCODE_WRITE;
|
||||
target_cmd.addr = cpu_to_le32(addr1);
|
||||
target_cmd.op1_sz = cpu_to_le32(sizeof(u32));
|
||||
target_cmd.op2 = 0;
|
||||
|
||||
ret = iwm_hal_send_target_cmd(iwm, &target_cmd, &data1);
|
||||
if (ret < 0) {
|
||||
IWM_ERR(iwm, "iwm_hal_send_target_cmd failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
target_cmd.opcode = UMAC_HDI_OUT_OPCODE_READ_MODIFY_WRITE;
|
||||
target_cmd.addr = cpu_to_le32(addr2);
|
||||
target_cmd.op1_sz = cpu_to_le32(data2_set);
|
||||
target_cmd.op2 = cpu_to_le32(data2_clr);
|
||||
|
||||
ret = iwm_hal_send_target_cmd(iwm, &target_cmd, &data1);
|
||||
if (ret < 0) {
|
||||
IWM_ERR(iwm, "iwm_hal_send_target_cmd failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
target_cmd.opcode = UMAC_HDI_OUT_OPCODE_WRITE;
|
||||
target_cmd.addr = cpu_to_le32(addr3);
|
||||
target_cmd.op1_sz = cpu_to_le32(sizeof(u32));
|
||||
target_cmd.op2 = 0;
|
||||
|
||||
ret = iwm_hal_send_target_cmd(iwm, &target_cmd, &data3);
|
||||
if (ret < 0) {
|
||||
IWM_ERR(iwm, "iwm_hal_send_target_cmd failed\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void iwm_init_default_profile(struct iwm_priv *iwm,
|
||||
struct iwm_umac_profile *profile)
|
||||
{
|
||||
memset(profile, 0, sizeof(struct iwm_umac_profile));
|
||||
|
||||
profile->sec.auth_type = UMAC_AUTH_TYPE_OPEN;
|
||||
profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE;
|
||||
profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_NONE;
|
||||
profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_NONE;
|
||||
|
||||
if (iwm->conf.enable_qos)
|
||||
profile->flags |= cpu_to_le16(UMAC_PROFILE_QOS_ALLOWED);
|
||||
|
||||
profile->wireless_mode = iwm->conf.wireless_mode;
|
||||
profile->mode = cpu_to_le32(iwm->conf.mode);
|
||||
|
||||
profile->ibss.atim = 0;
|
||||
profile->ibss.beacon_interval = 100;
|
||||
profile->ibss.join_only = 0;
|
||||
profile->ibss.band = iwm->conf.ibss_band;
|
||||
profile->ibss.channel = iwm->conf.ibss_channel;
|
||||
}
|
||||
|
||||
void iwm_link_on(struct iwm_priv *iwm)
|
||||
{
|
||||
netif_carrier_on(iwm_to_ndev(iwm));
|
||||
netif_tx_wake_all_queues(iwm_to_ndev(iwm));
|
||||
|
||||
iwm_send_umac_stats_req(iwm, 0);
|
||||
}
|
||||
|
||||
void iwm_link_off(struct iwm_priv *iwm)
|
||||
{
|
||||
struct iw_statistics *wstats = &iwm->wstats;
|
||||
int i;
|
||||
|
||||
netif_tx_stop_all_queues(iwm_to_ndev(iwm));
|
||||
netif_carrier_off(iwm_to_ndev(iwm));
|
||||
|
||||
for (i = 0; i < IWM_TX_QUEUES; i++) {
|
||||
skb_queue_purge(&iwm->txq[i].queue);
|
||||
|
||||
iwm->txq[i].concat_count = 0;
|
||||
iwm->txq[i].concat_ptr = iwm->txq[i].concat_buf;
|
||||
|
||||
flush_workqueue(iwm->txq[i].wq);
|
||||
}
|
||||
|
||||
iwm_rx_free(iwm);
|
||||
|
||||
cancel_delayed_work(&iwm->stats_request);
|
||||
memset(wstats, 0, sizeof(struct iw_statistics));
|
||||
wstats->qual.updated = IW_QUAL_ALL_INVALID;
|
||||
|
||||
del_timer_sync(&iwm->watchdog);
|
||||
}
|
||||
|
||||
static void iwm_bss_list_clean(struct iwm_priv *iwm)
|
||||
{
|
||||
struct iwm_bss_info *bss, *next;
|
||||
|
||||
list_for_each_entry_safe(bss, next, &iwm->bss_list, node) {
|
||||
list_del(&bss->node);
|
||||
kfree(bss->bss);
|
||||
kfree(bss);
|
||||
}
|
||||
}
|
||||
|
||||
static int iwm_channels_init(struct iwm_priv *iwm)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_IWM_B0_HW_SUPPORT
|
||||
if (iwm->conf.hw_b0) {
|
||||
IWM_INFO(iwm, "Workaround EEPROM channels for B0 hardware\n");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = iwm_send_umac_channel_list(iwm);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Send channel list failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST,
|
||||
IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Didn't get a channel list notification\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iwm_up(struct iwm_priv *iwm)
|
||||
{
|
||||
int ret;
|
||||
struct iwm_notif *notif_reboot, *notif_ack = NULL;
|
||||
|
||||
ret = iwm_bus_enable(iwm);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Couldn't enable function\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
iwm_rx_setup_handlers(iwm);
|
||||
|
||||
/* Wait for initial BARKER_REBOOT from hardware */
|
||||
notif_reboot = iwm_notif_wait(iwm, IWM_BARKER_REBOOT_NOTIFICATION,
|
||||
IWM_SRC_UDMA, 2 * HZ);
|
||||
if (!notif_reboot) {
|
||||
IWM_ERR(iwm, "Wait for REBOOT_BARKER timeout\n");
|
||||
goto err_disable;
|
||||
}
|
||||
|
||||
/* We send the barker back */
|
||||
ret = iwm_bus_send_chunk(iwm, notif_reboot->buf, 16);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "REBOOT barker response failed\n");
|
||||
kfree(notif_reboot);
|
||||
goto err_disable;
|
||||
}
|
||||
|
||||
kfree(notif_reboot->buf);
|
||||
kfree(notif_reboot);
|
||||
|
||||
/* Wait for ACK_BARKER from hardware */
|
||||
notif_ack = iwm_notif_wait(iwm, IWM_ACK_BARKER_NOTIFICATION,
|
||||
IWM_SRC_UDMA, 2 * HZ);
|
||||
if (!notif_ack) {
|
||||
IWM_ERR(iwm, "Wait for ACK_BARKER timeout\n");
|
||||
goto err_disable;
|
||||
}
|
||||
|
||||
kfree(notif_ack->buf);
|
||||
kfree(notif_ack);
|
||||
|
||||
/* We start to config static boot parameters */
|
||||
ret = iwm_config_boot_params(iwm);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Config boot parameters failed\n");
|
||||
goto err_disable;
|
||||
}
|
||||
|
||||
ret = iwm_read_mac(iwm, iwm_to_ndev(iwm)->dev_addr);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "MAC reading failed\n");
|
||||
goto err_disable;
|
||||
}
|
||||
|
||||
/* We can load the FWs */
|
||||
ret = iwm_load_fw(iwm);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "FW loading failed\n");
|
||||
goto err_disable;
|
||||
}
|
||||
|
||||
/* We configure the UMAC and enable the wifi module */
|
||||
ret = iwm_send_umac_config(iwm,
|
||||
cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_CORE_EN) |
|
||||
cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_LINK_EN) |
|
||||
cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_MLME_EN));
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "UMAC config failed\n");
|
||||
goto err_fw;
|
||||
}
|
||||
|
||||
ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS,
|
||||
IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Didn't get a wifi core status notification\n");
|
||||
goto err_fw;
|
||||
}
|
||||
|
||||
if (iwm->core_enabled != (UMAC_NTFY_WIFI_CORE_STATUS_LINK_EN |
|
||||
UMAC_NTFY_WIFI_CORE_STATUS_MLME_EN)) {
|
||||
IWM_DBG_BOOT(iwm, DBG, "Not all cores enabled:0x%x\n",
|
||||
iwm->core_enabled);
|
||||
ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS,
|
||||
IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Didn't get a core status notification\n");
|
||||
goto err_fw;
|
||||
}
|
||||
|
||||
if (iwm->core_enabled != (UMAC_NTFY_WIFI_CORE_STATUS_LINK_EN |
|
||||
UMAC_NTFY_WIFI_CORE_STATUS_MLME_EN)) {
|
||||
IWM_ERR(iwm, "Not all cores enabled: 0x%x\n",
|
||||
iwm->core_enabled);
|
||||
goto err_fw;
|
||||
} else {
|
||||
IWM_INFO(iwm, "All cores enabled\n");
|
||||
}
|
||||
}
|
||||
|
||||
iwm->umac_profile = kmalloc(sizeof(struct iwm_umac_profile),
|
||||
GFP_KERNEL);
|
||||
if (!iwm->umac_profile) {
|
||||
IWM_ERR(iwm, "Couldn't alloc memory for profile\n");
|
||||
goto err_fw;
|
||||
}
|
||||
|
||||
iwm_init_default_profile(iwm, iwm->umac_profile);
|
||||
|
||||
ret = iwm_channels_init(iwm);
|
||||
if (ret < 0) {
|
||||
IWM_ERR(iwm, "Couldn't init channels\n");
|
||||
goto err_profile;
|
||||
}
|
||||
|
||||
/* Set the READY bit to indicate interface is brought up successfully */
|
||||
set_bit(IWM_STATUS_READY, &iwm->status);
|
||||
|
||||
return 0;
|
||||
|
||||
err_profile:
|
||||
kfree(iwm->umac_profile);
|
||||
iwm->umac_profile = NULL;
|
||||
|
||||
err_fw:
|
||||
iwm_eeprom_exit(iwm);
|
||||
|
||||
err_disable:
|
||||
ret = iwm_bus_disable(iwm);
|
||||
if (ret < 0)
|
||||
IWM_ERR(iwm, "Couldn't disable function\n");
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int iwm_down(struct iwm_priv *iwm)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* The interface is already down */
|
||||
if (!test_bit(IWM_STATUS_READY, &iwm->status))
|
||||
return 0;
|
||||
|
||||
if (iwm->scan_request) {
|
||||
cfg80211_scan_done(iwm->scan_request, true);
|
||||
iwm->scan_request = NULL;
|
||||
}
|
||||
|
||||
clear_bit(IWM_STATUS_READY, &iwm->status);
|
||||
|
||||
iwm_eeprom_exit(iwm);
|
||||
kfree(iwm->umac_profile);
|
||||
iwm->umac_profile = NULL;
|
||||
iwm_bss_list_clean(iwm);
|
||||
|
||||
iwm->default_key = NULL;
|
||||
iwm->core_enabled = 0;
|
||||
|
||||
ret = iwm_bus_disable(iwm);
|
||||
if (ret < 0) {
|
||||
IWM_ERR(iwm, "Couldn't disable function\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is the netdev related hooks for iwm.
|
||||
*
|
||||
* Some interesting code paths:
|
||||
*
|
||||
* iwm_open() (Called at netdev interface bringup time)
|
||||
* -> iwm_up() (main.c)
|
||||
* -> iwm_bus_enable()
|
||||
* -> if_sdio_enable() (In case of an SDIO bus)
|
||||
* -> sdio_enable_func()
|
||||
* -> iwm_notif_wait(BARKER_REBOOT) (wait for reboot barker)
|
||||
* -> iwm_notif_wait(ACK_BARKER) (wait for ACK barker)
|
||||
* -> iwm_load_fw() (fw.c)
|
||||
* -> iwm_load_umac()
|
||||
* -> iwm_load_lmac() (Calibration LMAC)
|
||||
* -> iwm_load_lmac() (Operational LMAC)
|
||||
* -> iwm_send_umac_config()
|
||||
*
|
||||
* iwm_stop() (Called at netdev interface bringdown time)
|
||||
* -> iwm_down()
|
||||
* -> iwm_bus_disable()
|
||||
* -> if_sdio_disable() (In case of an SDIO bus)
|
||||
* -> sdio_disable_func()
|
||||
*/
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
#include "iwm.h"
|
||||
#include "cfg80211.h"
|
||||
#include "debug.h"
|
||||
|
||||
static int iwm_open(struct net_device *ndev)
|
||||
{
|
||||
struct iwm_priv *iwm = ndev_to_iwm(ndev);
|
||||
int ret = 0;
|
||||
|
||||
if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio))
|
||||
ret = iwm_up(iwm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iwm_stop(struct net_device *ndev)
|
||||
{
|
||||
struct iwm_priv *iwm = ndev_to_iwm(ndev);
|
||||
int ret = 0;
|
||||
|
||||
if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio))
|
||||
ret = iwm_down(iwm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* iwm AC to queue mapping
|
||||
*
|
||||
* AC_VO -> queue 3
|
||||
* AC_VI -> queue 2
|
||||
* AC_BE -> queue 1
|
||||
* AC_BK -> queue 0
|
||||
*/
|
||||
static const u16 iwm_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
|
||||
|
||||
static u16 iwm_select_queue(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
skb->priority = cfg80211_classify8021d(skb);
|
||||
|
||||
return iwm_1d_to_queue[skb->priority];
|
||||
}
|
||||
|
||||
static const struct net_device_ops iwm_netdev_ops = {
|
||||
.ndo_open = iwm_open,
|
||||
.ndo_stop = iwm_stop,
|
||||
.ndo_start_xmit = iwm_xmit_frame,
|
||||
.ndo_select_queue = iwm_select_queue,
|
||||
};
|
||||
|
||||
void *iwm_if_alloc(int sizeof_bus, struct device *dev,
|
||||
struct iwm_if_ops *if_ops)
|
||||
{
|
||||
struct net_device *ndev;
|
||||
struct wireless_dev *wdev;
|
||||
struct iwm_priv *iwm;
|
||||
int ret = 0;
|
||||
|
||||
wdev = iwm_wdev_alloc(sizeof_bus, dev);
|
||||
if (!wdev) {
|
||||
dev_err(dev, "no memory for wireless device instance\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
iwm = wdev_to_iwm(wdev);
|
||||
iwm->bus_ops = if_ops;
|
||||
iwm->wdev = wdev;
|
||||
iwm_priv_init(iwm);
|
||||
wdev->iftype = iwm_mode_to_nl80211_iftype(iwm->conf.mode);
|
||||
|
||||
ndev = alloc_netdev_mq(0, "wlan%d", ether_setup,
|
||||
IWM_TX_QUEUES);
|
||||
if (!ndev) {
|
||||
dev_err(dev, "no memory for network device instance\n");
|
||||
goto out_wdev;
|
||||
}
|
||||
|
||||
ndev->netdev_ops = &iwm_netdev_ops;
|
||||
ndev->wireless_handlers = &iwm_iw_handler_def;
|
||||
ndev->ieee80211_ptr = wdev;
|
||||
SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
|
||||
ret = register_netdev(ndev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to register netdev: %d\n", ret);
|
||||
goto out_ndev;
|
||||
}
|
||||
|
||||
wdev->netdev = ndev;
|
||||
|
||||
ret = iwm_rfkill_init(iwm);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to init rfkill\n");
|
||||
goto out_rfkill;
|
||||
}
|
||||
|
||||
return iwm;
|
||||
|
||||
out_rfkill:
|
||||
unregister_netdev(ndev);
|
||||
|
||||
out_ndev:
|
||||
free_netdev(ndev);
|
||||
|
||||
out_wdev:
|
||||
iwm_wdev_free(iwm);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
void iwm_if_free(struct iwm_priv *iwm)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!iwm_to_ndev(iwm))
|
||||
return;
|
||||
|
||||
iwm_rfkill_exit(iwm);
|
||||
unregister_netdev(iwm_to_ndev(iwm));
|
||||
free_netdev(iwm_to_ndev(iwm));
|
||||
iwm_wdev_free(iwm);
|
||||
destroy_workqueue(iwm->rx_wq);
|
||||
for (i = 0; i < IWM_TX_QUEUES; i++)
|
||||
destroy_workqueue(iwm->txq[i].wq);
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/rfkill.h>
|
||||
|
||||
#include "iwm.h"
|
||||
|
||||
static int iwm_rfkill_soft_toggle(void *data, enum rfkill_state state)
|
||||
{
|
||||
struct iwm_priv *iwm = data;
|
||||
|
||||
switch (state) {
|
||||
case RFKILL_STATE_UNBLOCKED:
|
||||
if (test_bit(IWM_RADIO_RFKILL_HW, &iwm->radio))
|
||||
return -EBUSY;
|
||||
|
||||
if (test_and_clear_bit(IWM_RADIO_RFKILL_SW, &iwm->radio) &&
|
||||
(iwm_to_ndev(iwm)->flags & IFF_UP))
|
||||
iwm_up(iwm);
|
||||
|
||||
break;
|
||||
case RFKILL_STATE_SOFT_BLOCKED:
|
||||
if (!test_and_set_bit(IWM_RADIO_RFKILL_SW, &iwm->radio))
|
||||
iwm_down(iwm);
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iwm_rfkill_init(struct iwm_priv *iwm)
|
||||
{
|
||||
int ret;
|
||||
|
||||
iwm->rfkill = rfkill_allocate(iwm_to_dev(iwm), RFKILL_TYPE_WLAN);
|
||||
if (!iwm->rfkill) {
|
||||
IWM_ERR(iwm, "Unable to allocate rfkill device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
iwm->rfkill->name = KBUILD_MODNAME;
|
||||
iwm->rfkill->data = iwm;
|
||||
iwm->rfkill->state = RFKILL_STATE_UNBLOCKED;
|
||||
iwm->rfkill->toggle_radio = iwm_rfkill_soft_toggle;
|
||||
|
||||
ret = rfkill_register(iwm->rfkill);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Failed to register rfkill device\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
rfkill_free(iwm->rfkill);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void iwm_rfkill_exit(struct iwm_priv *iwm)
|
||||
{
|
||||
if (iwm->rfkill)
|
||||
rfkill_unregister(iwm->rfkill);
|
||||
|
||||
rfkill_free(iwm->rfkill);
|
||||
iwm->rfkill = NULL;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __IWM_RX_H__
|
||||
#define __IWM_RX_H__
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include "umac.h"
|
||||
|
||||
struct iwm_rx_ticket_node {
|
||||
struct list_head node;
|
||||
struct iwm_rx_ticket *ticket;
|
||||
};
|
||||
|
||||
struct iwm_rx_packet {
|
||||
struct list_head node;
|
||||
u16 id;
|
||||
struct sk_buff *skb;
|
||||
unsigned long pkt_size;
|
||||
};
|
||||
|
||||
void iwm_rx_worker(struct work_struct *work);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,516 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is the SDIO bus specific hooks for iwm.
|
||||
* It also is the module's entry point.
|
||||
*
|
||||
* Interesting code paths:
|
||||
* iwm_sdio_probe() (Called by an SDIO bus scan)
|
||||
* -> iwm_if_alloc() (netdev.c)
|
||||
* -> iwm_wdev_alloc() (cfg80211.c, allocates and register our wiphy)
|
||||
* -> wiphy_new()
|
||||
* -> wiphy_register()
|
||||
* -> alloc_netdev_mq()
|
||||
* -> register_netdev()
|
||||
*
|
||||
* iwm_sdio_remove()
|
||||
* -> iwm_if_free() (netdev.c)
|
||||
* -> unregister_netdev()
|
||||
* -> iwm_wdev_free() (cfg80211.c)
|
||||
* -> wiphy_unregister()
|
||||
* -> wiphy_free()
|
||||
*
|
||||
* iwm_sdio_isr() (called in process context from the SDIO core code)
|
||||
* -> queue_work(.., isr_worker)
|
||||
* -- [async] --> iwm_sdio_isr_worker()
|
||||
* -> iwm_rx_handle()
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/mmc/sdio.h>
|
||||
#include <linux/mmc/sdio_func.h>
|
||||
|
||||
#include "iwm.h"
|
||||
#include "debug.h"
|
||||
#include "bus.h"
|
||||
#include "sdio.h"
|
||||
|
||||
static void iwm_sdio_isr_worker(struct work_struct *work)
|
||||
{
|
||||
struct iwm_sdio_priv *hw;
|
||||
struct iwm_priv *iwm;
|
||||
struct iwm_rx_info *rx_info;
|
||||
struct sk_buff *skb;
|
||||
u8 *rx_buf;
|
||||
unsigned long rx_size;
|
||||
|
||||
hw = container_of(work, struct iwm_sdio_priv, isr_worker);
|
||||
iwm = hw_to_iwm(hw);
|
||||
|
||||
while (!skb_queue_empty(&iwm->rx_list)) {
|
||||
skb = skb_dequeue(&iwm->rx_list);
|
||||
rx_info = skb_to_rx_info(skb);
|
||||
rx_size = rx_info->rx_size;
|
||||
rx_buf = skb->data;
|
||||
|
||||
IWM_HEXDUMP(iwm, DBG, SDIO, "RX: ", rx_buf, rx_size);
|
||||
if (iwm_rx_handle(iwm, rx_buf, rx_size) < 0)
|
||||
IWM_WARN(iwm, "RX error\n");
|
||||
|
||||
kfree_skb(skb);
|
||||
}
|
||||
}
|
||||
|
||||
static void iwm_sdio_isr(struct sdio_func *func)
|
||||
{
|
||||
struct iwm_priv *iwm;
|
||||
struct iwm_sdio_priv *hw;
|
||||
struct iwm_rx_info *rx_info;
|
||||
struct sk_buff *skb;
|
||||
unsigned long buf_size, read_size;
|
||||
int ret;
|
||||
u8 val;
|
||||
|
||||
hw = sdio_get_drvdata(func);
|
||||
iwm = hw_to_iwm(hw);
|
||||
|
||||
buf_size = hw->blk_size;
|
||||
|
||||
/* We're checking the status */
|
||||
val = sdio_readb(func, IWM_SDIO_INTR_STATUS_ADDR, &ret);
|
||||
if (val == 0 || ret < 0) {
|
||||
IWM_ERR(iwm, "Wrong INTR_STATUS\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* See if we have free buffers */
|
||||
if (skb_queue_len(&iwm->rx_list) > IWM_RX_LIST_SIZE) {
|
||||
IWM_ERR(iwm, "No buffer for more Rx frames\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* We first read the transaction size */
|
||||
read_size = sdio_readb(func, IWM_SDIO_INTR_GET_SIZE_ADDR + 1, &ret);
|
||||
read_size = read_size << 8;
|
||||
|
||||
if (ret < 0) {
|
||||
IWM_ERR(iwm, "Couldn't read the xfer size\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* We need to clear the INT register */
|
||||
sdio_writeb(func, 1, IWM_SDIO_INTR_CLEAR_ADDR, &ret);
|
||||
if (ret < 0) {
|
||||
IWM_ERR(iwm, "Couldn't clear the INT register\n");
|
||||
return;
|
||||
}
|
||||
|
||||
while (buf_size < read_size)
|
||||
buf_size <<= 1;
|
||||
|
||||
skb = dev_alloc_skb(buf_size);
|
||||
if (!skb) {
|
||||
IWM_ERR(iwm, "Couldn't alloc RX skb\n");
|
||||
return;
|
||||
}
|
||||
rx_info = skb_to_rx_info(skb);
|
||||
rx_info->rx_size = read_size;
|
||||
rx_info->rx_buf_size = buf_size;
|
||||
|
||||
/* Now we can read the actual buffer */
|
||||
ret = sdio_memcpy_fromio(func, skb_put(skb, read_size),
|
||||
IWM_SDIO_DATA_ADDR, read_size);
|
||||
|
||||
/* The skb is put on a driver's specific Rx SKB list */
|
||||
skb_queue_tail(&iwm->rx_list, skb);
|
||||
|
||||
/* We can now schedule the actual worker */
|
||||
queue_work(hw->isr_wq, &hw->isr_worker);
|
||||
}
|
||||
|
||||
static void iwm_sdio_rx_free(struct iwm_sdio_priv *hw)
|
||||
{
|
||||
struct iwm_priv *iwm = hw_to_iwm(hw);
|
||||
|
||||
flush_workqueue(hw->isr_wq);
|
||||
|
||||
skb_queue_purge(&iwm->rx_list);
|
||||
}
|
||||
|
||||
/* Bus ops */
|
||||
static int if_sdio_enable(struct iwm_priv *iwm)
|
||||
{
|
||||
struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
|
||||
int ret;
|
||||
|
||||
sdio_claim_host(hw->func);
|
||||
|
||||
ret = sdio_enable_func(hw->func);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Couldn't enable the device: is TOP driver "
|
||||
"loaded and functional?\n");
|
||||
goto release_host;
|
||||
}
|
||||
|
||||
iwm_reset(iwm);
|
||||
|
||||
ret = sdio_claim_irq(hw->func, iwm_sdio_isr);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Failed to claim irq: %d\n", ret);
|
||||
goto release_host;
|
||||
}
|
||||
|
||||
sdio_writeb(hw->func, 1, IWM_SDIO_INTR_ENABLE_ADDR, &ret);
|
||||
if (ret < 0) {
|
||||
IWM_ERR(iwm, "Couldn't enable INTR: %d\n", ret);
|
||||
goto release_irq;
|
||||
}
|
||||
|
||||
sdio_release_host(hw->func);
|
||||
|
||||
IWM_DBG_SDIO(iwm, INFO, "IWM SDIO enable\n");
|
||||
|
||||
return 0;
|
||||
|
||||
release_irq:
|
||||
sdio_release_irq(hw->func);
|
||||
release_host:
|
||||
sdio_release_host(hw->func);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int if_sdio_disable(struct iwm_priv *iwm)
|
||||
{
|
||||
struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
|
||||
int ret;
|
||||
|
||||
iwm_reset(iwm);
|
||||
|
||||
sdio_claim_host(hw->func);
|
||||
sdio_writeb(hw->func, 0, IWM_SDIO_INTR_ENABLE_ADDR, &ret);
|
||||
if (ret < 0)
|
||||
IWM_WARN(iwm, "Couldn't disable INTR: %d\n", ret);
|
||||
|
||||
sdio_release_irq(hw->func);
|
||||
sdio_disable_func(hw->func);
|
||||
sdio_release_host(hw->func);
|
||||
|
||||
iwm_sdio_rx_free(hw);
|
||||
|
||||
IWM_DBG_SDIO(iwm, INFO, "IWM SDIO disable\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int if_sdio_send_chunk(struct iwm_priv *iwm, u8 *buf, int count)
|
||||
{
|
||||
struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
|
||||
int aligned_count = ALIGN(count, hw->blk_size);
|
||||
int ret;
|
||||
|
||||
if ((unsigned long)buf & 0x3) {
|
||||
IWM_ERR(iwm, "buf <%p> is not dword aligned\n", buf);
|
||||
/* TODO: Is this a hardware limitation? use get_unligned */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sdio_claim_host(hw->func);
|
||||
ret = sdio_memcpy_toio(hw->func, IWM_SDIO_DATA_ADDR, buf,
|
||||
aligned_count);
|
||||
sdio_release_host(hw->func);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* debugfs hooks */
|
||||
static int iwm_debugfs_sdio_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
filp->private_data = inode->i_private;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t iwm_debugfs_sdio_read(struct file *filp, char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct iwm_priv *iwm = filp->private_data;
|
||||
struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
|
||||
char *buf;
|
||||
u8 cccr;
|
||||
int buf_len = 4096, ret;
|
||||
size_t len = 0;
|
||||
|
||||
if (*ppos != 0)
|
||||
return 0;
|
||||
if (count < sizeof(buf))
|
||||
return -ENOSPC;
|
||||
|
||||
buf = kzalloc(buf_len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
sdio_claim_host(hw->func);
|
||||
|
||||
cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IOEx, &ret);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Could not read SDIO_CCCR_IOEx\n");
|
||||
goto err;
|
||||
}
|
||||
len += snprintf(buf + len, buf_len - len, "CCCR_IOEx: 0x%x\n", cccr);
|
||||
|
||||
cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IORx, &ret);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Could not read SDIO_CCCR_IORx\n");
|
||||
goto err;
|
||||
}
|
||||
len += snprintf(buf + len, buf_len - len, "CCCR_IORx: 0x%x\n", cccr);
|
||||
|
||||
|
||||
cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IENx, &ret);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Could not read SDIO_CCCR_IENx\n");
|
||||
goto err;
|
||||
}
|
||||
len += snprintf(buf + len, buf_len - len, "CCCR_IENx: 0x%x\n", cccr);
|
||||
|
||||
|
||||
cccr = sdio_f0_readb(hw->func, SDIO_CCCR_INTx, &ret);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Could not read SDIO_CCCR_INTx\n");
|
||||
goto err;
|
||||
}
|
||||
len += snprintf(buf + len, buf_len - len, "CCCR_INTx: 0x%x\n", cccr);
|
||||
|
||||
|
||||
cccr = sdio_f0_readb(hw->func, SDIO_CCCR_ABORT, &ret);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Could not read SDIO_CCCR_ABORTx\n");
|
||||
goto err;
|
||||
}
|
||||
len += snprintf(buf + len, buf_len - len, "CCCR_ABORT: 0x%x\n", cccr);
|
||||
|
||||
cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IF, &ret);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Could not read SDIO_CCCR_IF\n");
|
||||
goto err;
|
||||
}
|
||||
len += snprintf(buf + len, buf_len - len, "CCCR_IF: 0x%x\n", cccr);
|
||||
|
||||
|
||||
cccr = sdio_f0_readb(hw->func, SDIO_CCCR_CAPS, &ret);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Could not read SDIO_CCCR_CAPS\n");
|
||||
goto err;
|
||||
}
|
||||
len += snprintf(buf + len, buf_len - len, "CCCR_CAPS: 0x%x\n", cccr);
|
||||
|
||||
cccr = sdio_f0_readb(hw->func, SDIO_CCCR_CIS, &ret);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "Could not read SDIO_CCCR_CIS\n");
|
||||
goto err;
|
||||
}
|
||||
len += snprintf(buf + len, buf_len - len, "CCCR_CIS: 0x%x\n", cccr);
|
||||
|
||||
ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len);
|
||||
err:
|
||||
sdio_release_host(hw->func);
|
||||
|
||||
kfree(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations iwm_debugfs_sdio_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = iwm_debugfs_sdio_open,
|
||||
.read = iwm_debugfs_sdio_read,
|
||||
};
|
||||
|
||||
static int if_sdio_debugfs_init(struct iwm_priv *iwm, struct dentry *parent_dir)
|
||||
{
|
||||
int result;
|
||||
struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
|
||||
|
||||
hw->cccr_dentry = debugfs_create_file("cccr", 0200,
|
||||
parent_dir, iwm,
|
||||
&iwm_debugfs_sdio_fops);
|
||||
result = PTR_ERR(hw->cccr_dentry);
|
||||
if (IS_ERR(hw->cccr_dentry) && (result != -ENODEV)) {
|
||||
IWM_ERR(iwm, "Couldn't create CCCR entry: %d\n", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void if_sdio_debugfs_exit(struct iwm_priv *iwm)
|
||||
{
|
||||
struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm);
|
||||
|
||||
debugfs_remove(hw->cccr_dentry);
|
||||
}
|
||||
|
||||
static struct iwm_if_ops if_sdio_ops = {
|
||||
.enable = if_sdio_enable,
|
||||
.disable = if_sdio_disable,
|
||||
.send_chunk = if_sdio_send_chunk,
|
||||
.debugfs_init = if_sdio_debugfs_init,
|
||||
.debugfs_exit = if_sdio_debugfs_exit,
|
||||
.umac_name = "iwmc3200wifi-umac-sdio.bin",
|
||||
.calib_lmac_name = "iwmc3200wifi-lmac-calib-sdio.bin",
|
||||
.lmac_name = "iwmc3200wifi-lmac-sdio.bin",
|
||||
};
|
||||
|
||||
static int iwm_sdio_probe(struct sdio_func *func,
|
||||
const struct sdio_device_id *id)
|
||||
{
|
||||
struct iwm_priv *iwm;
|
||||
struct iwm_sdio_priv *hw;
|
||||
struct device *dev = &func->dev;
|
||||
int ret;
|
||||
|
||||
/* check if TOP has already initialized the card */
|
||||
sdio_claim_host(func);
|
||||
ret = sdio_enable_func(func);
|
||||
if (ret) {
|
||||
dev_err(dev, "wait for TOP to enable the device\n");
|
||||
sdio_release_host(func);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sdio_set_block_size(func, IWM_SDIO_BLK_SIZE);
|
||||
|
||||
sdio_disable_func(func);
|
||||
sdio_release_host(func);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to set block size: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
iwm = iwm_if_alloc(sizeof(struct iwm_sdio_priv), dev, &if_sdio_ops);
|
||||
if (IS_ERR(iwm)) {
|
||||
dev_err(dev, "allocate SDIO interface failed\n");
|
||||
return PTR_ERR(iwm);
|
||||
}
|
||||
|
||||
hw = iwm_private(iwm);
|
||||
hw->iwm = iwm;
|
||||
|
||||
ret = iwm_debugfs_init(iwm);
|
||||
if (ret < 0) {
|
||||
IWM_ERR(iwm, "Debugfs registration failed\n");
|
||||
goto if_free;
|
||||
}
|
||||
|
||||
sdio_set_drvdata(func, hw);
|
||||
|
||||
hw->func = func;
|
||||
hw->blk_size = IWM_SDIO_BLK_SIZE;
|
||||
|
||||
hw->isr_wq = create_singlethread_workqueue(KBUILD_MODNAME "_sdio");
|
||||
if (!hw->isr_wq) {
|
||||
ret = -ENOMEM;
|
||||
goto debugfs_exit;
|
||||
}
|
||||
|
||||
INIT_WORK(&hw->isr_worker, iwm_sdio_isr_worker);
|
||||
|
||||
dev_info(dev, "IWM SDIO probe\n");
|
||||
|
||||
return 0;
|
||||
|
||||
debugfs_exit:
|
||||
iwm_debugfs_exit(iwm);
|
||||
if_free:
|
||||
iwm_if_free(iwm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void iwm_sdio_remove(struct sdio_func *func)
|
||||
{
|
||||
struct iwm_sdio_priv *hw = sdio_get_drvdata(func);
|
||||
struct iwm_priv *iwm = hw_to_iwm(hw);
|
||||
struct device *dev = &func->dev;
|
||||
|
||||
iwm_debugfs_exit(iwm);
|
||||
iwm_if_free(iwm);
|
||||
destroy_workqueue(hw->isr_wq);
|
||||
|
||||
sdio_set_drvdata(func, NULL);
|
||||
|
||||
dev_info(dev, "IWM SDIO remove\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static const struct sdio_device_id iwm_sdio_ids[] = {
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, SDIO_DEVICE_ID_IWM) },
|
||||
{ /* end: all zeroes */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(sdio, iwm_sdio_ids);
|
||||
|
||||
static struct sdio_driver iwm_sdio_driver = {
|
||||
.name = "iwm_sdio",
|
||||
.id_table = iwm_sdio_ids,
|
||||
.probe = iwm_sdio_probe,
|
||||
.remove = iwm_sdio_remove,
|
||||
};
|
||||
|
||||
static int __init iwm_sdio_init_module(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sdio_register_driver(&iwm_sdio_driver);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit iwm_sdio_exit_module(void)
|
||||
{
|
||||
sdio_unregister_driver(&iwm_sdio_driver);
|
||||
}
|
||||
|
||||
module_init(iwm_sdio_init_module);
|
||||
module_exit(iwm_sdio_exit_module);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR(IWM_COPYRIGHT " " IWM_AUTHOR);
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __IWM_SDIO_H__
|
||||
#define __IWM_SDIO_H__
|
||||
|
||||
#define SDIO_VENDOR_ID_INTEL 0x89
|
||||
#define SDIO_DEVICE_ID_IWM 0x1403
|
||||
|
||||
#define IWM_SDIO_DATA_ADDR 0x0
|
||||
#define IWM_SDIO_INTR_ENABLE_ADDR 0x14
|
||||
#define IWM_SDIO_INTR_STATUS_ADDR 0x13
|
||||
#define IWM_SDIO_INTR_CLEAR_ADDR 0x13
|
||||
#define IWM_SDIO_INTR_GET_SIZE_ADDR 0x2C
|
||||
|
||||
#define IWM_SDIO_BLK_SIZE 256
|
||||
|
||||
#define iwm_to_if_sdio(i) (struct iwm_sdio_priv *)(iwm->private)
|
||||
|
||||
struct iwm_sdio_priv {
|
||||
struct sdio_func *func;
|
||||
struct iwm_priv *iwm;
|
||||
|
||||
struct workqueue_struct *isr_wq;
|
||||
struct work_struct isr_worker;
|
||||
|
||||
struct dentry *cccr_dentry;
|
||||
|
||||
unsigned int blk_size;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,492 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* iwm Tx theory of operation:
|
||||
*
|
||||
* 1) We receive a 802.3 frame from the stack
|
||||
* 2) We convert it to a 802.11 frame [iwm_xmit_frame]
|
||||
* 3) We queue it to its corresponding tx queue [iwm_xmit_frame]
|
||||
* 4) We schedule the tx worker. There is one worker per tx
|
||||
* queue. [iwm_xmit_frame]
|
||||
* 5) The tx worker is scheduled
|
||||
* 6) We go through every queued skb on the tx queue, and for each
|
||||
* and every one of them: [iwm_tx_worker]
|
||||
* a) We check if we have enough Tx credits (see below for a Tx
|
||||
* credits description) for the frame length. [iwm_tx_worker]
|
||||
* b) If we do, we aggregate the Tx frame into a UDMA one, by
|
||||
* concatenating one REPLY_TX command per Tx frame. [iwm_tx_worker]
|
||||
* c) When we run out of credits, or when we reach the maximum
|
||||
* concatenation size, we actually send the concatenated UDMA
|
||||
* frame. [iwm_tx_worker]
|
||||
*
|
||||
* When we run out of Tx credits, the skbs are filling the tx queue,
|
||||
* and eventually we will stop the netdev queue. [iwm_tx_worker]
|
||||
* The tx queue is emptied as we're getting new tx credits, by
|
||||
* scheduling the tx_worker. [iwm_tx_credit_inc]
|
||||
* The netdev queue is started again when we have enough tx credits,
|
||||
* and when our tx queue has some reasonable amout of space available
|
||||
* (i.e. half of the max size). [iwm_tx_worker]
|
||||
*/
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/ieee80211.h>
|
||||
|
||||
#include "iwm.h"
|
||||
#include "debug.h"
|
||||
#include "commands.h"
|
||||
#include "hal.h"
|
||||
#include "umac.h"
|
||||
#include "bus.h"
|
||||
|
||||
#define IWM_UMAC_PAGE_ALLOC_WRAP 0xffff
|
||||
|
||||
#define BYTES_TO_PAGES(n) (1 + ((n) >> ilog2(IWM_UMAC_PAGE_SIZE)) - \
|
||||
(((n) & (IWM_UMAC_PAGE_SIZE - 1)) == 0))
|
||||
|
||||
#define pool_id_to_queue(id) ((id < IWM_TX_CMD_QUEUE) ? id : id - 1)
|
||||
#define queue_to_pool_id(q) ((q < IWM_TX_CMD_QUEUE) ? q : q + 1)
|
||||
|
||||
/* require to hold tx_credit lock */
|
||||
static int iwm_tx_credit_get(struct iwm_tx_credit *tx_credit, int id)
|
||||
{
|
||||
struct pool_entry *pool = &tx_credit->pools[id];
|
||||
struct spool_entry *spool = &tx_credit->spools[pool->sid];
|
||||
int spool_pages;
|
||||
|
||||
/* number of pages can be taken from spool by this pool */
|
||||
spool_pages = spool->max_pages - spool->alloc_pages +
|
||||
max(pool->min_pages - pool->alloc_pages, 0);
|
||||
|
||||
return min(pool->max_pages - pool->alloc_pages, spool_pages);
|
||||
}
|
||||
|
||||
static bool iwm_tx_credit_ok(struct iwm_priv *iwm, int id, int nb)
|
||||
{
|
||||
u32 npages = BYTES_TO_PAGES(nb);
|
||||
|
||||
if (npages <= iwm_tx_credit_get(&iwm->tx_credit, id))
|
||||
return 1;
|
||||
|
||||
set_bit(id, &iwm->tx_credit.full_pools_map);
|
||||
|
||||
IWM_DBG_TX(iwm, DBG, "LINK: stop txq[%d], available credit: %d\n",
|
||||
pool_id_to_queue(id),
|
||||
iwm_tx_credit_get(&iwm->tx_credit, id));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void iwm_tx_credit_inc(struct iwm_priv *iwm, int id, int total_freed_pages)
|
||||
{
|
||||
struct pool_entry *pool;
|
||||
struct spool_entry *spool;
|
||||
int freed_pages;
|
||||
int queue;
|
||||
|
||||
BUG_ON(id >= IWM_MACS_OUT_GROUPS);
|
||||
|
||||
pool = &iwm->tx_credit.pools[id];
|
||||
spool = &iwm->tx_credit.spools[pool->sid];
|
||||
|
||||
freed_pages = total_freed_pages - pool->total_freed_pages;
|
||||
IWM_DBG_TX(iwm, DBG, "Free %d pages for pool[%d]\n", freed_pages, id);
|
||||
|
||||
if (!freed_pages) {
|
||||
IWM_DBG_TX(iwm, DBG, "No pages are freed by UMAC\n");
|
||||
return;
|
||||
} else if (freed_pages < 0)
|
||||
freed_pages += IWM_UMAC_PAGE_ALLOC_WRAP + 1;
|
||||
|
||||
if (pool->alloc_pages > pool->min_pages) {
|
||||
int spool_pages = pool->alloc_pages - pool->min_pages;
|
||||
spool_pages = min(spool_pages, freed_pages);
|
||||
spool->alloc_pages -= spool_pages;
|
||||
}
|
||||
|
||||
pool->alloc_pages -= freed_pages;
|
||||
pool->total_freed_pages = total_freed_pages;
|
||||
|
||||
IWM_DBG_TX(iwm, DBG, "Pool[%d] pages alloc: %d, total_freed: %d, "
|
||||
"Spool[%d] pages alloc: %d\n", id, pool->alloc_pages,
|
||||
pool->total_freed_pages, pool->sid, spool->alloc_pages);
|
||||
|
||||
if (test_bit(id, &iwm->tx_credit.full_pools_map) &&
|
||||
(pool->alloc_pages < pool->max_pages / 2)) {
|
||||
clear_bit(id, &iwm->tx_credit.full_pools_map);
|
||||
|
||||
queue = pool_id_to_queue(id);
|
||||
|
||||
IWM_DBG_TX(iwm, DBG, "LINK: start txq[%d], available "
|
||||
"credit: %d\n", queue,
|
||||
iwm_tx_credit_get(&iwm->tx_credit, id));
|
||||
queue_work(iwm->txq[queue].wq, &iwm->txq[queue].worker);
|
||||
}
|
||||
}
|
||||
|
||||
static void iwm_tx_credit_dec(struct iwm_priv *iwm, int id, int alloc_pages)
|
||||
{
|
||||
struct pool_entry *pool;
|
||||
struct spool_entry *spool;
|
||||
int spool_pages;
|
||||
|
||||
IWM_DBG_TX(iwm, DBG, "Allocate %d pages for pool[%d]\n",
|
||||
alloc_pages, id);
|
||||
|
||||
BUG_ON(id >= IWM_MACS_OUT_GROUPS);
|
||||
|
||||
pool = &iwm->tx_credit.pools[id];
|
||||
spool = &iwm->tx_credit.spools[pool->sid];
|
||||
|
||||
spool_pages = pool->alloc_pages + alloc_pages - pool->min_pages;
|
||||
|
||||
if (pool->alloc_pages >= pool->min_pages)
|
||||
spool->alloc_pages += alloc_pages;
|
||||
else if (spool_pages > 0)
|
||||
spool->alloc_pages += spool_pages;
|
||||
|
||||
pool->alloc_pages += alloc_pages;
|
||||
|
||||
IWM_DBG_TX(iwm, DBG, "Pool[%d] pages alloc: %d, total_freed: %d, "
|
||||
"Spool[%d] pages alloc: %d\n", id, pool->alloc_pages,
|
||||
pool->total_freed_pages, pool->sid, spool->alloc_pages);
|
||||
}
|
||||
|
||||
int iwm_tx_credit_alloc(struct iwm_priv *iwm, int id, int nb)
|
||||
{
|
||||
u32 npages = BYTES_TO_PAGES(nb);
|
||||
int ret = 0;
|
||||
|
||||
spin_lock(&iwm->tx_credit.lock);
|
||||
|
||||
if (!iwm_tx_credit_ok(iwm, id, nb)) {
|
||||
IWM_DBG_TX(iwm, DBG, "No credit avaliable for pool[%d]\n", id);
|
||||
ret = -ENOSPC;
|
||||
goto out;
|
||||
}
|
||||
|
||||
iwm_tx_credit_dec(iwm, id, npages);
|
||||
|
||||
out:
|
||||
spin_unlock(&iwm->tx_credit.lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since we're on an SDIO or USB bus, we are not sharing memory
|
||||
* for storing to be transmitted frames. The host needs to push
|
||||
* them upstream. As a consequence there needs to be a way for
|
||||
* the target to let us know if it can actually take more TX frames
|
||||
* or not. This is what Tx credits are for.
|
||||
*
|
||||
* For each Tx HW queue, we have a Tx pool, and then we have one
|
||||
* unique super pool (spool), which is actually a global pool of
|
||||
* all the UMAC pages.
|
||||
* For each Tx pool we have a min_pages, a max_pages fields, and a
|
||||
* alloc_pages fields. The alloc_pages tracks the number of pages
|
||||
* currently allocated from the tx pool.
|
||||
* Here are the rules to check if given a tx frame we have enough
|
||||
* tx credits for it:
|
||||
* 1) We translate the frame length into a number of UMAC pages.
|
||||
* Let's call them n_pages.
|
||||
* 2) For the corresponding tx pool, we check if n_pages +
|
||||
* pool->alloc_pages is higher than pool->min_pages. min_pages
|
||||
* represent a set of pre-allocated pages on the tx pool. If
|
||||
* that's the case, then we need to allocate those pages from
|
||||
* the spool. We can do so until we reach spool->max_pages.
|
||||
* 3) Each tx pool is not allowed to allocate more than pool->max_pages
|
||||
* from the spool, so once we're over min_pages, we can allocate
|
||||
* pages from the spool, but not more than max_pages.
|
||||
*
|
||||
* When the tx code path needs to send a tx frame, it checks first
|
||||
* if it has enough tx credits, following those rules. [iwm_tx_credit_get]
|
||||
* If it does, it then updates the pool and spool counters and
|
||||
* then send the frame. [iwm_tx_credit_alloc and iwm_tx_credit_dec]
|
||||
* On the other side, when the UMAC is done transmitting frames, it
|
||||
* will send a credit update notification to the host. This is when
|
||||
* the pool and spool counters gets to be decreased. [iwm_tx_credit_inc,
|
||||
* called from rx.c:iwm_ntf_tx_credit_update]
|
||||
*
|
||||
*/
|
||||
void iwm_tx_credit_init_pools(struct iwm_priv *iwm,
|
||||
struct iwm_umac_notif_alive *alive)
|
||||
{
|
||||
int i, sid, pool_pages;
|
||||
|
||||
spin_lock(&iwm->tx_credit.lock);
|
||||
|
||||
iwm->tx_credit.pool_nr = le16_to_cpu(alive->page_grp_count);
|
||||
iwm->tx_credit.full_pools_map = 0;
|
||||
memset(&iwm->tx_credit.spools[0], 0, sizeof(struct spool_entry));
|
||||
|
||||
IWM_DBG_TX(iwm, DBG, "Pools number is %d\n", iwm->tx_credit.pool_nr);
|
||||
|
||||
for (i = 0; i < iwm->tx_credit.pool_nr; i++) {
|
||||
__le32 page_grp_state = alive->page_grp_state[i];
|
||||
|
||||
iwm->tx_credit.pools[i].id = GET_VAL32(page_grp_state,
|
||||
UMAC_ALIVE_PAGE_STS_GRP_NUM);
|
||||
iwm->tx_credit.pools[i].sid = GET_VAL32(page_grp_state,
|
||||
UMAC_ALIVE_PAGE_STS_SGRP_NUM);
|
||||
iwm->tx_credit.pools[i].min_pages = GET_VAL32(page_grp_state,
|
||||
UMAC_ALIVE_PAGE_STS_GRP_MIN_SIZE);
|
||||
iwm->tx_credit.pools[i].max_pages = GET_VAL32(page_grp_state,
|
||||
UMAC_ALIVE_PAGE_STS_GRP_MAX_SIZE);
|
||||
iwm->tx_credit.pools[i].alloc_pages = 0;
|
||||
iwm->tx_credit.pools[i].total_freed_pages = 0;
|
||||
|
||||
sid = iwm->tx_credit.pools[i].sid;
|
||||
pool_pages = iwm->tx_credit.pools[i].min_pages;
|
||||
|
||||
if (iwm->tx_credit.spools[sid].max_pages == 0) {
|
||||
iwm->tx_credit.spools[sid].id = sid;
|
||||
iwm->tx_credit.spools[sid].max_pages =
|
||||
GET_VAL32(page_grp_state,
|
||||
UMAC_ALIVE_PAGE_STS_SGRP_MAX_SIZE);
|
||||
iwm->tx_credit.spools[sid].alloc_pages = 0;
|
||||
}
|
||||
|
||||
iwm->tx_credit.spools[sid].alloc_pages += pool_pages;
|
||||
|
||||
IWM_DBG_TX(iwm, DBG, "Pool idx: %d, id: %d, sid: %d, capacity "
|
||||
"min: %d, max: %d, pool alloc: %d, total_free: %d, "
|
||||
"super poll alloc: %d\n",
|
||||
i, iwm->tx_credit.pools[i].id,
|
||||
iwm->tx_credit.pools[i].sid,
|
||||
iwm->tx_credit.pools[i].min_pages,
|
||||
iwm->tx_credit.pools[i].max_pages,
|
||||
iwm->tx_credit.pools[i].alloc_pages,
|
||||
iwm->tx_credit.pools[i].total_freed_pages,
|
||||
iwm->tx_credit.spools[sid].alloc_pages);
|
||||
}
|
||||
|
||||
spin_unlock(&iwm->tx_credit.lock);
|
||||
}
|
||||
|
||||
#define IWM_UDMA_HDR_LEN sizeof(struct iwm_umac_wifi_out_hdr)
|
||||
|
||||
static int iwm_tx_build_packet(struct iwm_priv *iwm, struct sk_buff *skb,
|
||||
int pool_id, u8 *buf)
|
||||
{
|
||||
struct iwm_umac_wifi_out_hdr *hdr = (struct iwm_umac_wifi_out_hdr *)buf;
|
||||
struct iwm_udma_wifi_cmd udma_cmd;
|
||||
struct iwm_umac_cmd umac_cmd;
|
||||
struct iwm_tx_info *tx_info = skb_to_tx_info(skb);
|
||||
|
||||
udma_cmd.count = cpu_to_le16(skb->len +
|
||||
sizeof(struct iwm_umac_fw_cmd_hdr));
|
||||
/* set EOP to 0 here. iwm_udma_wifi_hdr_set_eop() will be
|
||||
* called later to set EOP for the last packet. */
|
||||
udma_cmd.eop = 0;
|
||||
udma_cmd.credit_group = pool_id;
|
||||
udma_cmd.ra_tid = tx_info->sta << 4 | tx_info->tid;
|
||||
udma_cmd.lmac_offset = 0;
|
||||
|
||||
umac_cmd.id = REPLY_TX;
|
||||
umac_cmd.count = cpu_to_le16(skb->len);
|
||||
umac_cmd.color = tx_info->color;
|
||||
umac_cmd.resp = 0;
|
||||
umac_cmd.seq_num = cpu_to_le16(iwm_alloc_wifi_cmd_seq(iwm));
|
||||
|
||||
iwm_build_udma_wifi_hdr(iwm, &hdr->hw_hdr, &udma_cmd);
|
||||
iwm_build_umac_hdr(iwm, &hdr->sw_hdr, &umac_cmd);
|
||||
|
||||
memcpy(buf + sizeof(*hdr), skb->data, skb->len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwm_tx_send_concat_packets(struct iwm_priv *iwm,
|
||||
struct iwm_tx_queue *txq)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!txq->concat_count)
|
||||
return 0;
|
||||
|
||||
IWM_DBG_TX(iwm, DBG, "Send concatenated Tx: queue %d, %d bytes\n",
|
||||
txq->id, txq->concat_count);
|
||||
|
||||
/* mark EOP for the last packet */
|
||||
iwm_udma_wifi_hdr_set_eop(iwm, txq->concat_ptr, 1);
|
||||
|
||||
ret = iwm_bus_send_chunk(iwm, txq->concat_buf, txq->concat_count);
|
||||
|
||||
txq->concat_count = 0;
|
||||
txq->concat_ptr = txq->concat_buf;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define CONFIG_IWM_TX_CONCATENATED 1
|
||||
|
||||
void iwm_tx_worker(struct work_struct *work)
|
||||
{
|
||||
struct iwm_priv *iwm;
|
||||
struct iwm_tx_info *tx_info = NULL;
|
||||
struct sk_buff *skb;
|
||||
int cmdlen, ret;
|
||||
struct iwm_tx_queue *txq;
|
||||
int pool_id;
|
||||
|
||||
txq = container_of(work, struct iwm_tx_queue, worker);
|
||||
iwm = container_of(txq, struct iwm_priv, txq[txq->id]);
|
||||
|
||||
pool_id = queue_to_pool_id(txq->id);
|
||||
|
||||
while (!test_bit(pool_id, &iwm->tx_credit.full_pools_map) &&
|
||||
!skb_queue_empty(&txq->queue)) {
|
||||
|
||||
skb = skb_dequeue(&txq->queue);
|
||||
tx_info = skb_to_tx_info(skb);
|
||||
cmdlen = IWM_UDMA_HDR_LEN + skb->len;
|
||||
|
||||
IWM_DBG_TX(iwm, DBG, "Tx frame on queue %d: skb: 0x%p, sta: "
|
||||
"%d, color: %d\n", txq->id, skb, tx_info->sta,
|
||||
tx_info->color);
|
||||
|
||||
#if !CONFIG_IWM_TX_CONCATENATED
|
||||
/* temporarily keep this to comparing the performance */
|
||||
ret = iwm_send_packet(iwm, skb, pool_id);
|
||||
#else
|
||||
|
||||
if (txq->concat_count + cmdlen > IWM_HAL_CONCATENATE_BUF_SIZE)
|
||||
iwm_tx_send_concat_packets(iwm, txq);
|
||||
|
||||
ret = iwm_tx_credit_alloc(iwm, pool_id, cmdlen);
|
||||
if (ret) {
|
||||
IWM_DBG_TX(iwm, DBG, "not enough tx_credit for queue "
|
||||
"%d, Tx worker stopped\n", txq->id);
|
||||
skb_queue_head(&txq->queue, skb);
|
||||
break;
|
||||
}
|
||||
|
||||
txq->concat_ptr = txq->concat_buf + txq->concat_count;
|
||||
iwm_tx_build_packet(iwm, skb, pool_id, txq->concat_ptr);
|
||||
txq->concat_count += ALIGN(cmdlen, 16);
|
||||
#endif
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
iwm_tx_send_concat_packets(iwm, txq);
|
||||
|
||||
if (__netif_subqueue_stopped(iwm_to_ndev(iwm), txq->id) &&
|
||||
!test_bit(pool_id, &iwm->tx_credit.full_pools_map) &&
|
||||
(skb_queue_len(&txq->queue) < IWM_TX_LIST_SIZE / 2)) {
|
||||
IWM_DBG_TX(iwm, DBG, "LINK: start netif_subqueue[%d]", txq->id);
|
||||
netif_wake_subqueue(iwm_to_ndev(iwm), txq->id);
|
||||
}
|
||||
}
|
||||
|
||||
int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
|
||||
{
|
||||
struct iwm_priv *iwm = ndev_to_iwm(netdev);
|
||||
struct net_device *ndev = iwm_to_ndev(iwm);
|
||||
struct wireless_dev *wdev = iwm_to_wdev(iwm);
|
||||
u8 *dst_addr;
|
||||
struct iwm_tx_info *tx_info;
|
||||
struct iwm_tx_queue *txq;
|
||||
struct iwm_sta_info *sta_info;
|
||||
u8 sta_id;
|
||||
u16 queue;
|
||||
int ret;
|
||||
|
||||
if (!test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
|
||||
IWM_DBG_TX(iwm, DBG, "LINK: stop netif_all_queues: "
|
||||
"not associated\n");
|
||||
netif_tx_stop_all_queues(netdev);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
queue = skb_get_queue_mapping(skb);
|
||||
BUG_ON(queue >= IWM_TX_DATA_QUEUES); /* no iPAN yet */
|
||||
|
||||
txq = &iwm->txq[queue];
|
||||
|
||||
/* No free space for Tx, tx_worker is too slow */
|
||||
if (skb_queue_len(&txq->queue) > IWM_TX_LIST_SIZE) {
|
||||
IWM_DBG_TX(iwm, DBG, "LINK: stop netif_subqueue[%d]\n", queue);
|
||||
netif_stop_subqueue(netdev, queue);
|
||||
return NETDEV_TX_BUSY;
|
||||
}
|
||||
|
||||
ret = ieee80211_data_from_8023(skb, netdev->dev_addr, wdev->iftype,
|
||||
iwm->bssid, 0);
|
||||
if (ret) {
|
||||
IWM_ERR(iwm, "build wifi header failed\n");
|
||||
goto drop;
|
||||
}
|
||||
|
||||
dst_addr = ((struct ieee80211_hdr *)(skb->data))->addr1;
|
||||
|
||||
for (sta_id = 0; sta_id < IWM_STA_TABLE_NUM; sta_id++) {
|
||||
sta_info = &iwm->sta_table[sta_id];
|
||||
if (sta_info->valid &&
|
||||
!memcmp(dst_addr, sta_info->addr, ETH_ALEN))
|
||||
break;
|
||||
}
|
||||
|
||||
if (sta_id == IWM_STA_TABLE_NUM) {
|
||||
IWM_ERR(iwm, "STA %pM not found in sta_table, Tx ignored\n",
|
||||
dst_addr);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
tx_info = skb_to_tx_info(skb);
|
||||
tx_info->sta = sta_id;
|
||||
tx_info->color = sta_info->color;
|
||||
/* UMAC uses TID 8 (vs. 0) for non QoS packets */
|
||||
if (sta_info->qos)
|
||||
tx_info->tid = skb->priority;
|
||||
else
|
||||
tx_info->tid = IWM_UMAC_MGMT_TID;
|
||||
|
||||
skb_queue_tail(&iwm->txq[queue].queue, skb);
|
||||
|
||||
queue_work(iwm->txq[queue].wq, &iwm->txq[queue].worker);
|
||||
|
||||
ndev->stats.tx_packets++;
|
||||
ndev->stats.tx_bytes += skb->len;
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
drop:
|
||||
ndev->stats.tx_dropped++;
|
||||
dev_kfree_skb_any(skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
|
@ -0,0 +1,744 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __IWM_UMAC_H__
|
||||
#define __IWM_UMAC_H__
|
||||
|
||||
struct iwm_udma_in_hdr {
|
||||
__le32 cmd;
|
||||
__le32 size;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_udma_out_nonwifi_hdr {
|
||||
__le32 cmd;
|
||||
__le32 addr;
|
||||
__le32 op1_sz;
|
||||
__le32 op2;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_udma_out_wifi_hdr {
|
||||
__le32 cmd;
|
||||
__le32 meta_data;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Sequence numbering */
|
||||
#define UMAC_WIFI_SEQ_NUM_BASE 1
|
||||
#define UMAC_WIFI_SEQ_NUM_MAX 0x4000
|
||||
#define UMAC_NONWIFI_SEQ_NUM_BASE 1
|
||||
#define UMAC_NONWIFI_SEQ_NUM_MAX 0x10
|
||||
|
||||
/* MAC address address */
|
||||
#define WICO_MAC_ADDRESS_ADDR 0x604008F8
|
||||
|
||||
/* RA / TID */
|
||||
#define UMAC_HDI_ACT_TBL_IDX_TID_POS 0
|
||||
#define UMAC_HDI_ACT_TBL_IDX_TID_SEED 0xF
|
||||
|
||||
#define UMAC_HDI_ACT_TBL_IDX_RA_POS 4
|
||||
#define UMAC_HDI_ACT_TBL_IDX_RA_SEED 0xF
|
||||
|
||||
#define UMAC_HDI_ACT_TBL_IDX_RA_UMAC 0xF
|
||||
#define UMAC_HDI_ACT_TBL_IDX_TID_UMAC 0x9
|
||||
#define UMAC_HDI_ACT_TBL_IDX_TID_LMAC 0xA
|
||||
|
||||
#define UMAC_HDI_ACT_TBL_IDX_HOST_CMD \
|
||||
((UMAC_HDI_ACT_TBL_IDX_RA_UMAC << UMAC_HDI_ACT_TBL_IDX_RA_POS) |\
|
||||
(UMAC_HDI_ACT_TBL_IDX_TID_UMAC << UMAC_HDI_ACT_TBL_IDX_TID_POS))
|
||||
#define UMAC_HDI_ACT_TBL_IDX_UMAC_CMD \
|
||||
((UMAC_HDI_ACT_TBL_IDX_RA_UMAC << UMAC_HDI_ACT_TBL_IDX_RA_POS) |\
|
||||
(UMAC_HDI_ACT_TBL_IDX_TID_LMAC << UMAC_HDI_ACT_TBL_IDX_TID_POS))
|
||||
|
||||
/* iwm_umac_notif_alive.page_grp_state Group number -- bits [3:0] */
|
||||
#define UMAC_ALIVE_PAGE_STS_GRP_NUM_POS 0
|
||||
#define UMAC_ALIVE_PAGE_STS_GRP_NUM_SEED 0xF
|
||||
|
||||
/* iwm_umac_notif_alive.page_grp_state Super group number -- bits [7:4] */
|
||||
#define UMAC_ALIVE_PAGE_STS_SGRP_NUM_POS 4
|
||||
#define UMAC_ALIVE_PAGE_STS_SGRP_NUM_SEED 0xF
|
||||
|
||||
/* iwm_umac_notif_alive.page_grp_state Group min size -- bits [15:8] */
|
||||
#define UMAC_ALIVE_PAGE_STS_GRP_MIN_SIZE_POS 8
|
||||
#define UMAC_ALIVE_PAGE_STS_GRP_MIN_SIZE_SEED 0xFF
|
||||
|
||||
/* iwm_umac_notif_alive.page_grp_state Group max size -- bits [23:16] */
|
||||
#define UMAC_ALIVE_PAGE_STS_GRP_MAX_SIZE_POS 16
|
||||
#define UMAC_ALIVE_PAGE_STS_GRP_MAX_SIZE_SEED 0xFF
|
||||
|
||||
/* iwm_umac_notif_alive.page_grp_state Super group max size -- bits [31:24] */
|
||||
#define UMAC_ALIVE_PAGE_STS_SGRP_MAX_SIZE_POS 24
|
||||
#define UMAC_ALIVE_PAGE_STS_SGRP_MAX_SIZE_SEED 0xFF
|
||||
|
||||
/* Barkers */
|
||||
#define UMAC_REBOOT_BARKER 0xdeadbeef
|
||||
#define UMAC_ACK_BARKER 0xfeedbabe
|
||||
#define UMAC_PAD_TERMINAL 0xadadadad
|
||||
|
||||
/* UMAC JMP address */
|
||||
#define UMAC_MU_FW_INST_DATA_12_ADDR 0xBF0000
|
||||
|
||||
/* iwm_umac_hdi_out_hdr.cmd OP code -- bits [3:0] */
|
||||
#define UMAC_HDI_OUT_CMD_OPCODE_POS 0
|
||||
#define UMAC_HDI_OUT_CMD_OPCODE_SEED 0xF
|
||||
|
||||
/* iwm_umac_hdi_out_hdr.cmd End-Of-Transfer -- bits [10:10] */
|
||||
#define UMAC_HDI_OUT_CMD_EOT_POS 10
|
||||
#define UMAC_HDI_OUT_CMD_EOT_SEED 0x1
|
||||
|
||||
/* iwm_umac_hdi_out_hdr.cmd UTFD only usage -- bits [11:11] */
|
||||
#define UMAC_HDI_OUT_CMD_UTFD_ONLY_POS 11
|
||||
#define UMAC_HDI_OUT_CMD_UTFD_ONLY_SEED 0x1
|
||||
|
||||
/* iwm_umac_hdi_out_hdr.cmd Non-WiFi HW sequence number -- bits [12:15] */
|
||||
#define UDMA_HDI_OUT_CMD_NON_WIFI_HW_SEQ_NUM_POS 12
|
||||
#define UDMA_HDI_OUT_CMD_NON_WIFI_HW_SEQ_NUM_SEED 0xF
|
||||
|
||||
/* iwm_umac_hdi_out_hdr.cmd Signature -- bits [31:16] */
|
||||
#define UMAC_HDI_OUT_CMD_SIGNATURE_POS 16
|
||||
#define UMAC_HDI_OUT_CMD_SIGNATURE_SEED 0xFFFF
|
||||
|
||||
/* iwm_umac_hdi_out_hdr.meta_data Byte count -- bits [11:0] */
|
||||
#define UMAC_HDI_OUT_BYTE_COUNT_POS 0
|
||||
#define UMAC_HDI_OUT_BYTE_COUNT_SEED 0xFFF
|
||||
|
||||
/* iwm_umac_hdi_out_hdr.meta_data Credit group -- bits [15:12] */
|
||||
#define UMAC_HDI_OUT_CREDIT_GRP_POS 12
|
||||
#define UMAC_HDI_OUT_CREDIT_GRP_SEED 0xF
|
||||
|
||||
/* iwm_umac_hdi_out_hdr.meta_data RA/TID -- bits [23:16] */
|
||||
#define UMAC_HDI_OUT_RATID_POS 16
|
||||
#define UMAC_HDI_OUT_RATID_SEED 0xFF
|
||||
|
||||
/* iwm_umac_hdi_out_hdr.meta_data LMAC offset -- bits [31:24] */
|
||||
#define UMAC_HDI_OUT_LMAC_OFFSET_POS 24
|
||||
#define UMAC_HDI_OUT_LMAC_OFFSET_SEED 0xFF
|
||||
|
||||
/* Signature */
|
||||
#define UMAC_HDI_OUT_SIGNATURE 0xCBBC
|
||||
|
||||
/* buffer alignment */
|
||||
#define UMAC_HDI_BUF_ALIGN_MSK 0xF
|
||||
|
||||
/* iwm_umac_hdi_in_hdr.cmd OP code -- bits [3:0] */
|
||||
#define UMAC_HDI_IN_CMD_OPCODE_POS 0
|
||||
#define UMAC_HDI_IN_CMD_OPCODE_SEED 0xF
|
||||
|
||||
/* iwm_umac_hdi_in_hdr.cmd Non-WiFi API response -- bits [6:4] */
|
||||
#define UMAC_HDI_IN_CMD_NON_WIFI_RESP_POS 4
|
||||
#define UMAC_HDI_IN_CMD_NON_WIFI_RESP_SEED 0x7
|
||||
|
||||
/* iwm_umac_hdi_in_hdr.cmd WiFi API source -- bits [5:4] */
|
||||
#define UMAC_HDI_IN_CMD_SOURCE_POS 4
|
||||
#define UMAC_HDI_IN_CMD_SOURCE_SEED 0x3
|
||||
|
||||
/* iwm_umac_hdi_in_hdr.cmd WiFi API EOT -- bits [6:6] */
|
||||
#define UMAC_HDI_IN_CMD_EOT_POS 6
|
||||
#define UMAC_HDI_IN_CMD_EOT_SEED 0x1
|
||||
|
||||
/* iwm_umac_hdi_in_hdr.cmd timestamp present -- bits [7:7] */
|
||||
#define UMAC_HDI_IN_CMD_TIME_STAMP_PRESENT_POS 7
|
||||
#define UMAC_HDI_IN_CMD_TIME_STAMP_PRESENT_SEED 0x1
|
||||
|
||||
/* iwm_umac_hdi_in_hdr.cmd WiFi Non-last AMSDU -- bits [8:8] */
|
||||
#define UMAC_HDI_IN_CMD_NON_LAST_AMSDU_POS 8
|
||||
#define UMAC_HDI_IN_CMD_NON_LAST_AMSDU_SEED 0x1
|
||||
|
||||
/* iwm_umac_hdi_in_hdr.cmd WiFi HW sequence number -- bits [31:9] */
|
||||
#define UMAC_HDI_IN_CMD_HW_SEQ_NUM_POS 9
|
||||
#define UMAC_HDI_IN_CMD_HW_SEQ_NUM_SEED 0x7FFFFF
|
||||
|
||||
/* iwm_umac_hdi_in_hdr.cmd Non-WiFi HW sequence number -- bits [12:15] */
|
||||
#define UDMA_HDI_IN_CMD_NON_WIFI_HW_SEQ_NUM_POS 12
|
||||
#define UDMA_HDI_IN_CMD_NON_WIFI_HW_SEQ_NUM_SEED 0xF
|
||||
|
||||
/* iwm_umac_hdi_in_hdr.cmd Non-WiFi HW signature -- bits [16:31] */
|
||||
#define UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG_POS 16
|
||||
#define UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG_SEED 0xFFFF
|
||||
|
||||
/* Fixed Non-WiFi signature */
|
||||
#define UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG 0xCBBC
|
||||
|
||||
/* IN NTFY op-codes */
|
||||
#define UMAC_NOTIFY_OPCODE_ALIVE 0xA1
|
||||
#define UMAC_NOTIFY_OPCODE_INIT_COMPLETE 0xA2
|
||||
#define UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS 0xA3
|
||||
#define UMAC_NOTIFY_OPCODE_ERROR 0xA4
|
||||
#define UMAC_NOTIFY_OPCODE_DEBUG 0xA5
|
||||
#define UMAC_NOTIFY_OPCODE_WIFI_IF_WRAPPER 0xB0
|
||||
#define UMAC_NOTIFY_OPCODE_STATS 0xB1
|
||||
#define UMAC_NOTIFY_OPCODE_PAGE_DEALLOC 0xB3
|
||||
#define UMAC_NOTIFY_OPCODE_RX_TICKET 0xB4
|
||||
#define UMAC_NOTIFY_OPCODE_MAX (UMAC_NOTIFY_OPCODE_RX_TICKET -\
|
||||
UMAC_NOTIFY_OPCODE_ALIVE + 1)
|
||||
#define UMAC_NOTIFY_OPCODE_FIRST (UMAC_NOTIFY_OPCODE_ALIVE)
|
||||
|
||||
/* HDI OUT OP CODE */
|
||||
#define UMAC_HDI_OUT_OPCODE_PING 0x0
|
||||
#define UMAC_HDI_OUT_OPCODE_READ 0x1
|
||||
#define UMAC_HDI_OUT_OPCODE_WRITE 0x2
|
||||
#define UMAC_HDI_OUT_OPCODE_JUMP 0x3
|
||||
#define UMAC_HDI_OUT_OPCODE_REBOOT 0x4
|
||||
#define UMAC_HDI_OUT_OPCODE_WRITE_PERSISTENT 0x5
|
||||
#define UMAC_HDI_OUT_OPCODE_READ_PERSISTENT 0x6
|
||||
#define UMAC_HDI_OUT_OPCODE_READ_MODIFY_WRITE 0x7
|
||||
/* #define UMAC_HDI_OUT_OPCODE_RESERVED 0x8..0xA */
|
||||
#define UMAC_HDI_OUT_OPCODE_WRITE_AUX_REG 0xB
|
||||
#define UMAC_HDI_OUT_OPCODE_WIFI 0xF
|
||||
|
||||
/* HDI IN OP CODE -- Non WiFi*/
|
||||
#define UMAC_HDI_IN_OPCODE_PING 0x0
|
||||
#define UMAC_HDI_IN_OPCODE_READ 0x1
|
||||
#define UMAC_HDI_IN_OPCODE_WRITE 0x2
|
||||
#define UMAC_HDI_IN_OPCODE_WRITE_PERSISTENT 0x5
|
||||
#define UMAC_HDI_IN_OPCODE_READ_PERSISTENT 0x6
|
||||
#define UMAC_HDI_IN_OPCODE_READ_MODIFY_WRITE 0x7
|
||||
#define UMAC_HDI_IN_OPCODE_EP_MGMT 0x8
|
||||
#define UMAC_HDI_IN_OPCODE_CREDIT_CHANGE 0x9
|
||||
#define UMAC_HDI_IN_OPCODE_CTRL_DATABASE 0xA
|
||||
#define UMAC_HDI_IN_OPCODE_WRITE_AUX_REG 0xB
|
||||
#define UMAC_HDI_IN_OPCODE_NONWIFI_MAX \
|
||||
(UMAC_HDI_IN_OPCODE_WRITE_AUX_REG + 1)
|
||||
#define UMAC_HDI_IN_OPCODE_WIFI 0xF
|
||||
|
||||
/* HDI IN SOURCE */
|
||||
#define UMAC_HDI_IN_SOURCE_FHRX 0x0
|
||||
#define UMAC_HDI_IN_SOURCE_UDMA 0x1
|
||||
#define UMAC_HDI_IN_SOURCE_FW 0x2
|
||||
#define UMAC_HDI_IN_SOURCE_RESERVED 0x3
|
||||
|
||||
/* OUT CMD op-codes */
|
||||
#define UMAC_CMD_OPCODE_ECHO 0x01
|
||||
#define UMAC_CMD_OPCODE_HALT 0x02
|
||||
#define UMAC_CMD_OPCODE_RESET 0x03
|
||||
#define UMAC_CMD_OPCODE_BULK_EP_INACT_TIMEOUT 0x09
|
||||
#define UMAC_CMD_OPCODE_URB_CANCEL_ACK 0x0A
|
||||
#define UMAC_CMD_OPCODE_DCACHE_FLUSH 0x0B
|
||||
#define UMAC_CMD_OPCODE_EEPROM_PROXY 0x0C
|
||||
#define UMAC_CMD_OPCODE_TX_ECHO 0x0D
|
||||
#define UMAC_CMD_OPCODE_DBG_MON 0x0E
|
||||
#define UMAC_CMD_OPCODE_INTERNAL_TX 0x0F
|
||||
#define UMAC_CMD_OPCODE_SET_PARAM_FIX 0x10
|
||||
#define UMAC_CMD_OPCODE_SET_PARAM_VAR 0x11
|
||||
#define UMAC_CMD_OPCODE_GET_PARAM 0x12
|
||||
#define UMAC_CMD_OPCODE_DBG_EVENT_WRAPPER 0x13
|
||||
#define UMAC_CMD_OPCODE_TARGET 0x14
|
||||
#define UMAC_CMD_OPCODE_STATISTIC_REQUEST 0x15
|
||||
#define UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST 0x16
|
||||
#define UMAC_CMD_OPCODE_SET_PARAM_LIST 0x17
|
||||
#define UMAC_CMD_OPCODE_GET_PARAM_LIST 0x18
|
||||
#define UMAC_CMD_OPCODE_BASE_WRAPPER 0xFA
|
||||
#define UMAC_CMD_OPCODE_LMAC_WRAPPER 0xFB
|
||||
#define UMAC_CMD_OPCODE_HW_TEST_WRAPPER 0xFC
|
||||
#define UMAC_CMD_OPCODE_WIFI_IF_WRAPPER 0xFD
|
||||
#define UMAC_CMD_OPCODE_WIFI_WRAPPER 0xFE
|
||||
#define UMAC_CMD_OPCODE_WIFI_PASS_THROUGH 0xFF
|
||||
|
||||
/* UMAC WiFi interface op-codes */
|
||||
#define UMAC_WIFI_IF_CMD_SET_PROFILE 0x11
|
||||
#define UMAC_WIFI_IF_CMD_INVALIDATE_PROFILE 0x12
|
||||
#define UMAC_WIFI_IF_CMD_SET_EXCLUDE_LIST 0x13
|
||||
#define UMAC_WIFI_IF_CMD_SCAN_REQUEST 0x14
|
||||
#define UMAC_WIFI_IF_CMD_SCAN_CONFIG 0x15
|
||||
#define UMAC_WIFI_IF_CMD_ADD_WEP40_KEY 0x16
|
||||
#define UMAC_WIFI_IF_CMD_ADD_WEP104_KEY 0x17
|
||||
#define UMAC_WIFI_IF_CMD_ADD_TKIP_KEY 0x18
|
||||
#define UMAC_WIFI_IF_CMD_ADD_CCMP_KEY 0x19
|
||||
#define UMAC_WIFI_IF_CMD_REMOVE_KEY 0x1A
|
||||
#define UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID 0x1B
|
||||
#define UMAC_WIFI_IF_CMD_SET_HOST_EXTENDED_IE 0x1C
|
||||
#define UMAC_WIFI_IF_CMD_GET_SUPPORTED_CHANNELS 0x1E
|
||||
#define UMAC_WIFI_IF_CMD_TX_PWR_TRIGGER 0x20
|
||||
|
||||
/* UMAC WiFi interface ports */
|
||||
#define UMAC_WIFI_IF_FLG_PORT_DEF 0x00
|
||||
#define UMAC_WIFI_IF_FLG_PORT_PAN 0x01
|
||||
#define UMAC_WIFI_IF_FLG_PORT_PAN_INVALID WIFI_IF_FLG_PORT_DEF
|
||||
|
||||
/* UMAC WiFi interface actions */
|
||||
#define UMAC_WIFI_IF_FLG_ACT_GET 0x10
|
||||
#define UMAC_WIFI_IF_FLG_ACT_SET 0x20
|
||||
|
||||
/* iwm_umac_fw_cmd_hdr.meta_data byte count -- bits [11:0] */
|
||||
#define UMAC_FW_CMD_BYTE_COUNT_POS 0
|
||||
#define UMAC_FW_CMD_BYTE_COUNT_SEED 0xFFF
|
||||
|
||||
/* iwm_umac_fw_cmd_hdr.meta_data status -- bits [15:12] */
|
||||
#define UMAC_FW_CMD_STATUS_POS 12
|
||||
#define UMAC_FW_CMD_STATUS_SEED 0xF
|
||||
|
||||
/* iwm_umac_fw_cmd_hdr.meta_data full TX command by Driver -- bits [16:16] */
|
||||
#define UMAC_FW_CMD_TX_DRV_FULL_CMD_POS 16
|
||||
#define UMAC_FW_CMD_TX_DRV_FULL_CMD_SEED 0x1
|
||||
|
||||
/* iwm_umac_fw_cmd_hdr.meta_data TX command by FW -- bits [17:17] */
|
||||
#define UMAC_FW_CMD_TX_FW_CMD_POS 17
|
||||
#define UMAC_FW_CMD_TX_FW_CMD_SEED 0x1
|
||||
|
||||
/* iwm_umac_fw_cmd_hdr.meta_data TX plaintext mode -- bits [18:18] */
|
||||
#define UMAC_FW_CMD_TX_PLAINTEXT_POS 18
|
||||
#define UMAC_FW_CMD_TX_PLAINTEXT_SEED 0x1
|
||||
|
||||
/* iwm_umac_fw_cmd_hdr.meta_data STA color -- bits [22:20] */
|
||||
#define UMAC_FW_CMD_TX_STA_COLOR_POS 20
|
||||
#define UMAC_FW_CMD_TX_STA_COLOR_SEED 0x7
|
||||
|
||||
/* iwm_umac_fw_cmd_hdr.meta_data TX life time (TU) -- bits [31:24] */
|
||||
#define UMAC_FW_CMD_TX_LIFETIME_TU_POS 24
|
||||
#define UMAC_FW_CMD_TX_LIFETIME_TU_SEED 0xFF
|
||||
|
||||
/* iwm_dev_cmd_hdr.flags Response required -- bits [5:5] */
|
||||
#define UMAC_DEV_CMD_FLAGS_RESP_REQ_POS 5
|
||||
#define UMAC_DEV_CMD_FLAGS_RESP_REQ_SEED 0x1
|
||||
|
||||
/* iwm_dev_cmd_hdr.flags Aborted command -- bits [6:6] */
|
||||
#define UMAC_DEV_CMD_FLAGS_ABORT_POS 6
|
||||
#define UMAC_DEV_CMD_FLAGS_ABORT_SEED 0x1
|
||||
|
||||
/* iwm_dev_cmd_hdr.flags Internal command -- bits [7:7] */
|
||||
#define DEV_CMD_FLAGS_FLD_INTERNAL_POS 7
|
||||
#define DEV_CMD_FLAGS_FLD_INTERNAL_SEED 0x1
|
||||
|
||||
/* Rx */
|
||||
/* Rx actions */
|
||||
#define IWM_RX_TICKET_DROP 0x0
|
||||
#define IWM_RX_TICKET_RELEASE 0x1
|
||||
#define IWM_RX_TICKET_SNIFFER 0x2
|
||||
#define IWM_RX_TICKET_ENQUEUE 0x3
|
||||
|
||||
/* Rx flags */
|
||||
#define IWM_RX_TICKET_PAD_SIZE_MSK 0x2
|
||||
#define IWM_RX_TICKET_SPECIAL_SNAP_MSK 0x4
|
||||
#define IWM_RX_TICKET_AMSDU_MSK 0x8
|
||||
#define IWM_RX_TICKET_DROP_REASON_POS 4
|
||||
#define IWM_RX_TICKET_DROP_REASON_MSK (0x1F << RX_TICKET_FLAGS_DROP_REASON_POS)
|
||||
|
||||
#define IWM_RX_DROP_NO_DROP 0x0
|
||||
#define IWM_RX_DROP_BAD_CRC 0x1
|
||||
/* L2P no address match */
|
||||
#define IWM_RX_DROP_LMAC_ADDR_FILTER 0x2
|
||||
/* Multicast address not in list */
|
||||
#define IWM_RX_DROP_MCAST_ADDR_FILTER 0x3
|
||||
/* Control frames are not sent to the driver */
|
||||
#define IWM_RX_DROP_CTL_FRAME 0x4
|
||||
/* Our frame is back */
|
||||
#define IWM_RX_DROP_OUR_TX 0x5
|
||||
/* Association class filtering */
|
||||
#define IWM_RX_DROP_CLASS_FILTER 0x6
|
||||
/* Duplicated frame */
|
||||
#define IWM_RX_DROP_DUPLICATE_FILTER 0x7
|
||||
/* Decryption error */
|
||||
#define IWM_RX_DROP_SEC_ERR 0x8
|
||||
/* Unencrypted frame while encryption is on */
|
||||
#define IWM_RX_DROP_SEC_NO_ENCRYPTION 0x9
|
||||
/* Replay check failure */
|
||||
#define IWM_RX_DROP_SEC_REPLAY_ERR 0xa
|
||||
/* uCode and FW key color mismatch, check before replay */
|
||||
#define IWM_RX_DROP_SEC_KEY_COLOR_MISMATCH 0xb
|
||||
#define IWM_RX_DROP_SEC_TKIP_COUNTER_MEASURE 0xc
|
||||
/* No fragmentations Db is found */
|
||||
#define IWM_RX_DROP_FRAG_NO_RESOURCE 0xd
|
||||
/* Fragmention Db has seqCtl mismatch Vs. non-1st frag */
|
||||
#define IWM_RX_DROP_FRAG_ERR 0xe
|
||||
#define IWM_RX_DROP_FRAG_LOST 0xf
|
||||
#define IWM_RX_DROP_FRAG_COMPLETE 0x10
|
||||
/* Should be handled by UMAC */
|
||||
#define IWM_RX_DROP_MANAGEMENT 0x11
|
||||
/* STA not found by UMAC */
|
||||
#define IWM_RX_DROP_NO_STATION 0x12
|
||||
/* NULL or QoS NULL */
|
||||
#define IWM_RX_DROP_NULL_DATA 0x13
|
||||
#define IWM_RX_DROP_BA_REORDER_OLD_SEQCTL 0x14
|
||||
#define IWM_RX_DROP_BA_REORDER_DUPLICATE 0x15
|
||||
|
||||
struct iwm_rx_ticket {
|
||||
__le16 action;
|
||||
__le16 id;
|
||||
__le16 flags;
|
||||
u8 payload_offset; /* includes: MAC header, pad, IV */
|
||||
u8 tail_len; /* includes: MIC, ICV, CRC (w/o STATUS) */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_rx_mpdu_hdr {
|
||||
__le16 len;
|
||||
__le16 reserved;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* UMAC SW WIFI API */
|
||||
|
||||
struct iwm_dev_cmd_hdr {
|
||||
u8 cmd;
|
||||
u8 flags;
|
||||
__le16 seq_num;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_fw_cmd_hdr {
|
||||
__le32 meta_data;
|
||||
struct iwm_dev_cmd_hdr cmd;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_wifi_out_hdr {
|
||||
struct iwm_udma_out_wifi_hdr hw_hdr;
|
||||
struct iwm_umac_fw_cmd_hdr sw_hdr;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_nonwifi_out_hdr {
|
||||
struct iwm_udma_out_nonwifi_hdr hw_hdr;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_wifi_in_hdr {
|
||||
struct iwm_udma_in_hdr hw_hdr;
|
||||
struct iwm_umac_fw_cmd_hdr sw_hdr;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_nonwifi_in_hdr {
|
||||
struct iwm_udma_in_hdr hw_hdr;
|
||||
__le32 time_stamp;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define IWM_UMAC_PAGE_SIZE 0x200
|
||||
|
||||
/* Notify structures */
|
||||
struct iwm_fw_version {
|
||||
u8 minor;
|
||||
u8 major;
|
||||
__le16 id;
|
||||
};
|
||||
|
||||
struct iwm_fw_build {
|
||||
u8 type;
|
||||
u8 subtype;
|
||||
u8 platform;
|
||||
u8 opt;
|
||||
};
|
||||
|
||||
struct iwm_fw_alive_hdr {
|
||||
struct iwm_fw_version ver;
|
||||
struct iwm_fw_build build;
|
||||
__le32 os_build;
|
||||
__le32 log_hdr_addr;
|
||||
__le32 log_buf_addr;
|
||||
__le32 sys_timer_addr;
|
||||
};
|
||||
|
||||
#define WAIT_NOTIF_TIMEOUT (2 * HZ)
|
||||
#define SCAN_COMPLETE_TIMEOUT (3 * HZ)
|
||||
|
||||
#define UMAC_NTFY_ALIVE_STATUS_ERR 0xDEAD
|
||||
#define UMAC_NTFY_ALIVE_STATUS_OK 0xCAFE
|
||||
|
||||
#define UMAC_NTFY_INIT_COMPLETE_STATUS_ERR 0xDEAD
|
||||
#define UMAC_NTFY_INIT_COMPLETE_STATUS_OK 0xCAFE
|
||||
|
||||
#define UMAC_NTFY_WIFI_CORE_STATUS_LINK_EN 0x40
|
||||
#define UMAC_NTFY_WIFI_CORE_STATUS_MLME_EN 0x80
|
||||
|
||||
#define IWM_MACS_OUT_GROUPS 6
|
||||
#define IWM_MACS_OUT_SGROUPS 1
|
||||
|
||||
|
||||
#define WIFI_IF_NTFY_ASSOC_START 0x80
|
||||
#define WIFI_IF_NTFY_ASSOC_COMPLETE 0x81
|
||||
#define WIFI_IF_NTFY_PROFILE_INVALIDATE_COMPLETE 0x82
|
||||
#define WIFI_IF_NTFY_CONNECTION_TERMINATED 0x83
|
||||
#define WIFI_IF_NTFY_SCAN_COMPLETE 0x84
|
||||
#define WIFI_IF_NTFY_STA_TABLE_CHANGE 0x85
|
||||
#define WIFI_IF_NTFY_EXTENDED_IE_REQUIRED 0x86
|
||||
#define WIFI_IF_NTFY_RADIO_PREEMPTION 0x87
|
||||
#define WIFI_IF_NTFY_BSS_TRK_TABLE_CHANGED 0x88
|
||||
#define WIFI_IF_NTFY_BSS_TRK_ENTRIES_REMOVED 0x89
|
||||
#define WIFI_IF_NTFY_LINK_QUALITY_STATISTICS 0x8A
|
||||
#define WIFI_IF_NTFY_MGMT_FRAME 0x8B
|
||||
|
||||
/* DEBUG INDICATIONS */
|
||||
#define WIFI_DBG_IF_NTFY_SCAN_SUPER_JOB_START 0xE0
|
||||
#define WIFI_DBG_IF_NTFY_SCAN_SUPER_JOB_COMPLETE 0xE1
|
||||
#define WIFI_DBG_IF_NTFY_SCAN_CHANNEL_START 0xE2
|
||||
#define WIFI_DBG_IF_NTFY_SCAN_CHANNEL_RESULT 0xE3
|
||||
#define WIFI_DBG_IF_NTFY_SCAN_MINI_JOB_START 0xE4
|
||||
#define WIFI_DBG_IF_NTFY_SCAN_MINI_JOB_COMPLETE 0xE5
|
||||
#define WIFI_DBG_IF_NTFY_CNCT_ATC_START 0xE6
|
||||
#define WIFI_DBG_IF_NTFY_COEX_NOTIFICATION 0xE7
|
||||
#define WIFI_DBG_IF_NTFY_COEX_HANDLE_ENVELOP 0xE8
|
||||
#define WIFI_DBG_IF_NTFY_COEX_HANDLE_RELEASE_ENVELOP 0xE9
|
||||
|
||||
/* Notification structures */
|
||||
struct iwm_umac_notif_wifi_if {
|
||||
struct iwm_umac_wifi_in_hdr hdr;
|
||||
u8 status;
|
||||
u8 flags;
|
||||
__le16 buf_size;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define UMAC_ROAM_REASON_FIRST_SELECTION 0x1
|
||||
#define UMAC_ROAM_REASON_AP_DEAUTH 0x2
|
||||
#define UMAC_ROAM_REASON_AP_CONNECT_LOST 0x3
|
||||
#define UMAC_ROAM_REASON_RSSI 0x4
|
||||
#define UMAC_ROAM_REASON_AP_ASSISTED_ROAM 0x5
|
||||
#define UMAC_ROAM_REASON_IBSS_COALESCING 0x6
|
||||
|
||||
struct iwm_umac_notif_assoc_start {
|
||||
struct iwm_umac_notif_wifi_if mlme_hdr;
|
||||
__le32 roam_reason;
|
||||
u8 bssid[ETH_ALEN];
|
||||
u8 reserved[2];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define UMAC_ASSOC_COMPLETE_SUCCESS 0x0
|
||||
#define UMAC_ASSOC_COMPLETE_FAILURE 0x1
|
||||
|
||||
struct iwm_umac_notif_assoc_complete {
|
||||
struct iwm_umac_notif_wifi_if mlme_hdr;
|
||||
__le32 status;
|
||||
u8 bssid[ETH_ALEN];
|
||||
u8 band;
|
||||
u8 channel;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define UMAC_PROFILE_INVALID_ASSOC_TIMEOUT 0x0
|
||||
#define UMAC_PROFILE_INVALID_ROAM_TIMEOUT 0x1
|
||||
#define UMAC_PROFILE_INVALID_REQUEST 0x2
|
||||
#define UMAC_PROFILE_INVALID_RF_PREEMPTED 0x3
|
||||
|
||||
struct iwm_umac_notif_profile_invalidate {
|
||||
struct iwm_umac_notif_wifi_if mlme_hdr;
|
||||
__le32 reason;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define UMAC_SCAN_RESULT_SUCCESS 0x0
|
||||
#define UMAC_SCAN_RESULT_ABORTED 0x1
|
||||
#define UMAC_SCAN_RESULT_REJECTED 0x2
|
||||
#define UMAC_SCAN_RESULT_FAILED 0x3
|
||||
|
||||
struct iwm_umac_notif_scan_complete {
|
||||
struct iwm_umac_notif_wifi_if mlme_hdr;
|
||||
__le32 type;
|
||||
__le32 result;
|
||||
u8 seq_num;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define UMAC_OPCODE_ADD_MODIFY 0x0
|
||||
#define UMAC_OPCODE_REMOVE 0x1
|
||||
#define UMAC_OPCODE_CLEAR_ALL 0x2
|
||||
|
||||
#define UMAC_STA_FLAG_QOS 0x1
|
||||
|
||||
struct iwm_umac_notif_sta_info {
|
||||
struct iwm_umac_notif_wifi_if mlme_hdr;
|
||||
__le32 opcode;
|
||||
u8 mac_addr[ETH_ALEN];
|
||||
u8 sta_id; /* bits 0-3: station ID, bits 4-7: station color */
|
||||
u8 flags;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define UMAC_BAND_2GHZ 0
|
||||
#define UMAC_BAND_5GHZ 1
|
||||
|
||||
#define UMAC_CHANNEL_WIDTH_20MHZ 0
|
||||
#define UMAC_CHANNEL_WIDTH_40MHZ 1
|
||||
|
||||
struct iwm_umac_notif_bss_info {
|
||||
struct iwm_umac_notif_wifi_if mlme_hdr;
|
||||
__le32 type;
|
||||
__le32 timestamp;
|
||||
__le16 table_idx;
|
||||
__le16 frame_len;
|
||||
u8 band;
|
||||
u8 channel;
|
||||
s8 rssi;
|
||||
u8 reserved;
|
||||
u8 frame_buf[1];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define IWM_BSS_REMOVE_INDEX_MSK 0x0fff
|
||||
#define IWM_BSS_REMOVE_FLAGS_MSK 0xfc00
|
||||
|
||||
#define IWM_BSS_REMOVE_FLG_AGE 0x1000
|
||||
#define IWM_BSS_REMOVE_FLG_TIMEOUT 0x2000
|
||||
#define IWM_BSS_REMOVE_FLG_TABLE_FULL 0x4000
|
||||
|
||||
struct iwm_umac_notif_bss_removed {
|
||||
struct iwm_umac_notif_wifi_if mlme_hdr;
|
||||
__le32 count;
|
||||
__le16 entries[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_notif_mgt_frame {
|
||||
struct iwm_umac_notif_wifi_if mlme_hdr;
|
||||
__le16 len;
|
||||
u8 frame[1];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_notif_alive {
|
||||
struct iwm_umac_wifi_in_hdr hdr;
|
||||
__le16 status;
|
||||
__le16 reserved1;
|
||||
struct iwm_fw_alive_hdr alive_data;
|
||||
__le16 reserved2;
|
||||
__le16 page_grp_count;
|
||||
__le32 page_grp_state[IWM_MACS_OUT_GROUPS];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_notif_init_complete {
|
||||
__le16 status;
|
||||
__le16 reserved;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* error categories */
|
||||
enum {
|
||||
UMAC_SYS_ERR_CAT_NONE = 0,
|
||||
UMAC_SYS_ERR_CAT_BOOT,
|
||||
UMAC_SYS_ERR_CAT_UMAC,
|
||||
UMAC_SYS_ERR_CAT_UAXM,
|
||||
UMAC_SYS_ERR_CAT_LMAC,
|
||||
UMAC_SYS_ERR_CAT_MAX
|
||||
};
|
||||
|
||||
struct iwm_fw_error_hdr {
|
||||
__le32 category;
|
||||
__le32 status;
|
||||
__le32 pc;
|
||||
__le32 blink1;
|
||||
__le32 blink2;
|
||||
__le32 ilink1;
|
||||
__le32 ilink2;
|
||||
__le32 data1;
|
||||
__le32 data2;
|
||||
__le32 line_num;
|
||||
__le32 umac_status;
|
||||
__le32 lmac_status;
|
||||
__le32 sdio_status;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_notif_error {
|
||||
struct iwm_umac_wifi_in_hdr hdr;
|
||||
struct iwm_fw_error_hdr err;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define UMAC_DEALLOC_NTFY_CHANGES_CNT_POS 0
|
||||
#define UMAC_DEALLOC_NTFY_CHANGES_CNT_SEED 0xff
|
||||
#define UMAC_DEALLOC_NTFY_CHANGES_MSK_POS 8
|
||||
#define UMAC_DEALLOC_NTFY_CHANGES_MSK_SEED 0xffffff
|
||||
#define UMAC_DEALLOC_NTFY_PAGE_CNT_POS 0
|
||||
#define UMAC_DEALLOC_NTFY_PAGE_CNT_SEED 0xffffff
|
||||
#define UMAC_DEALLOC_NTFY_GROUP_NUM_POS 24
|
||||
#define UMAC_DEALLOC_NTFY_GROUP_NUM_SEED 0xf
|
||||
|
||||
struct iwm_umac_notif_page_dealloc {
|
||||
struct iwm_umac_wifi_in_hdr hdr;
|
||||
__le32 changes;
|
||||
__le32 grp_info[IWM_MACS_OUT_GROUPS];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_notif_wifi_status {
|
||||
struct iwm_umac_wifi_in_hdr hdr;
|
||||
__le16 status;
|
||||
__le16 reserved;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct iwm_umac_notif_rx_ticket {
|
||||
struct iwm_umac_wifi_in_hdr hdr;
|
||||
u8 num_tickets;
|
||||
u8 reserved[3];
|
||||
struct iwm_rx_ticket tickets[1];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Tx/Rx rates window (number of max of last update window per second) */
|
||||
#define UMAC_NTF_RATE_SAMPLE_NR 4
|
||||
|
||||
#define IWM_UMAC_MGMT_TID 8
|
||||
#define IWM_UMAC_TID_NR 8
|
||||
|
||||
struct iwm_umac_notif_stats {
|
||||
struct iwm_umac_wifi_in_hdr hdr;
|
||||
__le32 flags;
|
||||
__le32 timestamp;
|
||||
__le16 tid_load[IWM_UMAC_TID_NR + 2]; /* 1 non-QoS + 1 dword align */
|
||||
__le16 tx_rate[UMAC_NTF_RATE_SAMPLE_NR];
|
||||
__le16 rx_rate[UMAC_NTF_RATE_SAMPLE_NR];
|
||||
s32 rssi_dbm;
|
||||
s32 noise_dbm;
|
||||
__le32 supp_rates;
|
||||
__le32 missed_beacons;
|
||||
__le32 rx_beacons;
|
||||
__le32 rx_dir_pkts;
|
||||
__le32 rx_nondir_pkts;
|
||||
__le32 rx_multicast;
|
||||
__le32 rx_errors;
|
||||
__le32 rx_drop_other_bssid;
|
||||
__le32 rx_drop_decode;
|
||||
__le32 rx_drop_reassembly;
|
||||
__le32 rx_drop_bad_len;
|
||||
__le32 rx_drop_overflow;
|
||||
__le32 rx_drop_crc;
|
||||
__le32 rx_drop_missed;
|
||||
__le32 tx_dir_pkts;
|
||||
__le32 tx_nondir_pkts;
|
||||
__le32 tx_failure;
|
||||
__le32 tx_errors;
|
||||
__le32 tx_drop_max_retry;
|
||||
__le32 tx_err_abort;
|
||||
__le32 tx_err_carrier;
|
||||
__le32 rx_bytes;
|
||||
__le32 tx_bytes;
|
||||
__le32 tx_power;
|
||||
__le32 tx_max_power;
|
||||
__le32 roam_threshold;
|
||||
__le32 ap_assoc_nr;
|
||||
__le32 scan_full;
|
||||
__le32 scan_abort;
|
||||
__le32 ap_nr;
|
||||
__le32 roam_nr;
|
||||
__le32 roam_missed_beacons;
|
||||
__le32 roam_rssi;
|
||||
__le32 roam_unassoc;
|
||||
__le32 roam_deauth;
|
||||
__le32 roam_ap_loadblance;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* WiFi interface wrapper header */
|
||||
struct iwm_umac_wifi_if {
|
||||
u8 oid;
|
||||
u8 flags;
|
||||
__le16 buf_size;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define IWM_SEQ_NUM_HOST_MSK 0x0000
|
||||
#define IWM_SEQ_NUM_UMAC_MSK 0x4000
|
||||
#define IWM_SEQ_NUM_LMAC_MSK 0x8000
|
||||
#define IWM_SEQ_NUM_MSK 0xC000
|
||||
|
||||
#endif
|
|
@ -0,0 +1,723 @@
|
|||
/*
|
||||
* Intel Wireless Multicomm 3200 WiFi driver
|
||||
*
|
||||
* Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com>
|
||||
* Samuel Ortiz <samuel.ortiz@intel.com>
|
||||
* Zhu Yi <yi.zhu@intel.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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include <net/iw_handler.h>
|
||||
|
||||
#include "iwm.h"
|
||||
#include "umac.h"
|
||||
#include "commands.h"
|
||||
#include "debug.h"
|
||||
|
||||
static struct iw_statistics *iwm_get_wireless_stats(struct net_device *dev)
|
||||
{
|
||||
struct iwm_priv *iwm = ndev_to_iwm(dev);
|
||||
struct iw_statistics *wstats = &iwm->wstats;
|
||||
|
||||
if (!test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
|
||||
memset(wstats, 0, sizeof(struct iw_statistics));
|
||||
wstats->qual.updated = IW_QUAL_ALL_INVALID;
|
||||
}
|
||||
|
||||
return wstats;
|
||||
}
|
||||
|
||||
static int iwm_wext_siwfreq(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_freq *freq, char *extra)
|
||||
{
|
||||
struct iwm_priv *iwm = ndev_to_iwm(dev);
|
||||
|
||||
if (freq->flags == IW_FREQ_AUTO)
|
||||
return 0;
|
||||
|
||||
/* frequency/channel can only be set in IBSS mode */
|
||||
if (iwm->conf.mode != UMAC_MODE_IBSS)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra);
|
||||
}
|
||||
|
||||
static int iwm_wext_giwfreq(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_freq *freq, char *extra)
|
||||
{
|
||||
struct iwm_priv *iwm = ndev_to_iwm(dev);
|
||||
|
||||
if (iwm->conf.mode == UMAC_MODE_IBSS)
|
||||
return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra);
|
||||
|
||||
freq->e = 0;
|
||||
freq->m = iwm->channel;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwm_wext_siwap(struct net_device *dev, struct iw_request_info *info,
|
||||
struct sockaddr *ap_addr, char *extra)
|
||||
{
|
||||
struct iwm_priv *iwm = ndev_to_iwm(dev);
|
||||
|
||||
if (iwm->conf.mode == UMAC_MODE_IBSS)
|
||||
return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra);
|
||||
|
||||
if (!test_bit(IWM_STATUS_READY, &iwm->status))
|
||||
return -EIO;
|
||||
|
||||
if (is_zero_ether_addr(ap_addr->sa_data) ||
|
||||
is_broadcast_ether_addr(ap_addr->sa_data)) {
|
||||
IWM_DBG_WEXT(iwm, DBG, "clear mandatory bssid %pM\n",
|
||||
iwm->umac_profile->bssid[0]);
|
||||
memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN);
|
||||
iwm->umac_profile->bss_num = 0;
|
||||
} else {
|
||||
IWM_DBG_WEXT(iwm, DBG, "add mandatory bssid %pM\n",
|
||||
ap_addr->sa_data);
|
||||
memcpy(&iwm->umac_profile->bssid[0], ap_addr->sa_data,
|
||||
ETH_ALEN);
|
||||
iwm->umac_profile->bss_num = 1;
|
||||
}
|
||||
|
||||
if (iwm->umac_profile_active) {
|
||||
if (!memcmp(&iwm->umac_profile->bssid[0], iwm->bssid, ETH_ALEN))
|
||||
return 0;
|
||||
|
||||
iwm_invalidate_mlme_profile(iwm);
|
||||
}
|
||||
|
||||
if (iwm->umac_profile->ssid.ssid_len)
|
||||
return iwm_send_mlme_profile(iwm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwm_wext_giwap(struct net_device *dev, struct iw_request_info *info,
|
||||
struct sockaddr *ap_addr, char *extra)
|
||||
{
|
||||
struct iwm_priv *iwm = ndev_to_iwm(dev);
|
||||
|
||||
switch (iwm->conf.mode) {
|
||||
case UMAC_MODE_IBSS:
|
||||
return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra);
|
||||
case UMAC_MODE_BSS:
|
||||
if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
|
||||
ap_addr->sa_family = ARPHRD_ETHER;
|
||||
memcpy(&ap_addr->sa_data, iwm->bssid, ETH_ALEN);
|
||||
} else
|
||||
memset(&ap_addr->sa_data, 0, ETH_ALEN);
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwm_wext_siwessid(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *data, char *ssid)
|
||||
{
|
||||
struct iwm_priv *iwm = ndev_to_iwm(dev);
|
||||
size_t len = data->length;
|
||||
int ret;
|
||||
|
||||
if (iwm->conf.mode == UMAC_MODE_IBSS)
|
||||
return cfg80211_ibss_wext_siwessid(dev, info, data, ssid);
|
||||
|
||||
if (!test_bit(IWM_STATUS_READY, &iwm->status))
|
||||
return -EIO;
|
||||
|
||||
if (len > 0 && ssid[len - 1] == '\0')
|
||||
len--;
|
||||
|
||||
if (iwm->umac_profile_active) {
|
||||
if (iwm->umac_profile->ssid.ssid_len == len &&
|
||||
!memcmp(iwm->umac_profile->ssid.ssid, ssid, len))
|
||||
return 0;
|
||||
|
||||
ret = iwm_invalidate_mlme_profile(iwm);
|
||||
if (ret < 0) {
|
||||
IWM_ERR(iwm, "Couldn't invalidate profile\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
iwm->umac_profile->ssid.ssid_len = len;
|
||||
memcpy(iwm->umac_profile->ssid.ssid, ssid, len);
|
||||
|
||||
return iwm_send_mlme_profile(iwm);
|
||||
}
|
||||
|
||||
static int iwm_wext_giwessid(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *data, char *ssid)
|
||||
{
|
||||
struct iwm_priv *iwm = ndev_to_iwm(dev);
|
||||
|
||||
if (iwm->conf.mode == UMAC_MODE_IBSS)
|
||||
return cfg80211_ibss_wext_giwessid(dev, info, data, ssid);
|
||||
|
||||
if (!test_bit(IWM_STATUS_READY, &iwm->status))
|
||||
return -EIO;
|
||||
|
||||
data->length = iwm->umac_profile->ssid.ssid_len;
|
||||
if (data->length) {
|
||||
memcpy(ssid, iwm->umac_profile->ssid.ssid, data->length);
|
||||
data->flags = 1;
|
||||
} else
|
||||
data->flags = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct iwm_key *
|
||||
iwm_key_init(struct iwm_priv *iwm, u8 key_idx, bool in_use,
|
||||
struct iw_encode_ext *ext, u8 alg)
|
||||
{
|
||||
struct iwm_key *key = &iwm->keys[key_idx];
|
||||
|
||||
memset(key, 0, sizeof(struct iwm_key));
|
||||
memcpy(key->hdr.mac, ext->addr.sa_data, ETH_ALEN);
|
||||
key->hdr.key_idx = key_idx;
|
||||
if (is_broadcast_ether_addr(ext->addr.sa_data))
|
||||
key->hdr.multicast = 1;
|
||||
|
||||
key->in_use = in_use;
|
||||
key->flags = ext->ext_flags;
|
||||
key->alg = alg;
|
||||
key->key_len = ext->key_len;
|
||||
memcpy(key->key, ext->key, ext->key_len);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
static int iwm_wext_giwrate(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_param *rate, char *extra)
|
||||
{
|
||||
struct iwm_priv *iwm = ndev_to_iwm(dev);
|
||||
|
||||
rate->value = iwm->rate * 1000000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwm_wext_siwencode(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *erq, char *key_buf)
|
||||
{
|
||||
struct iwm_priv *iwm = ndev_to_iwm(dev);
|
||||
struct iwm_key *uninitialized_var(key);
|
||||
int idx, i, uninitialized_var(alg), remove = 0, ret;
|
||||
|
||||
IWM_DBG_WEXT(iwm, DBG, "key len: %d\n", erq->length);
|
||||
IWM_DBG_WEXT(iwm, DBG, "flags: 0x%x\n", erq->flags);
|
||||
|
||||
if (!iwm->umac_profile) {
|
||||
IWM_ERR(iwm, "UMAC profile not allocated yet\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (erq->length == WLAN_KEY_LEN_WEP40) {
|
||||
alg = UMAC_CIPHER_TYPE_WEP_40;
|
||||
iwm->umac_profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_WEP_40;
|
||||
iwm->umac_profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_WEP_40;
|
||||
} else if (erq->length == WLAN_KEY_LEN_WEP104) {
|
||||
alg = UMAC_CIPHER_TYPE_WEP_104;
|
||||
iwm->umac_profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_WEP_104;
|
||||
iwm->umac_profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_WEP_104;
|
||||
}
|
||||
|
||||
if (erq->flags & IW_ENCODE_RESTRICTED)
|
||||
iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
|
||||
else
|
||||
iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_OPEN;
|
||||
|
||||
idx = erq->flags & IW_ENCODE_INDEX;
|
||||
if (idx == 0) {
|
||||
if (iwm->default_key)
|
||||
for (i = 0; i < IWM_NUM_KEYS; i++) {
|
||||
if (iwm->default_key == &iwm->keys[i]) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
iwm->default_key = &iwm->keys[idx];
|
||||
} else if (idx < 1 || idx > 4) {
|
||||
return -EINVAL;
|
||||
} else
|
||||
idx--;
|
||||
|
||||
if (erq->flags & IW_ENCODE_DISABLED)
|
||||
remove = 1;
|
||||
else if (erq->length == 0) {
|
||||
if (!iwm->keys[idx].in_use)
|
||||
return -EINVAL;
|
||||
iwm->default_key = &iwm->keys[idx];
|
||||
}
|
||||
|
||||
if (erq->length) {
|
||||
key = &iwm->keys[idx];
|
||||
memset(key, 0, sizeof(struct iwm_key));
|
||||
memset(key->hdr.mac, 0xff, ETH_ALEN);
|
||||
key->hdr.key_idx = idx;
|
||||
key->hdr.multicast = 1;
|
||||
key->in_use = !remove;
|
||||
key->alg = alg;
|
||||
key->key_len = erq->length;
|
||||
memcpy(key->key, key_buf, erq->length);
|
||||
|
||||
IWM_DBG_WEXT(iwm, DBG, "Setting key %d, default: %d\n",
|
||||
idx, !!iwm->default_key);
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
if ((erq->flags & IW_ENCODE_NOKEY) || (erq->length == 0)) {
|
||||
int j;
|
||||
for (j = 0; j < IWM_NUM_KEYS; j++)
|
||||
if (iwm->keys[j].in_use) {
|
||||
struct iwm_key *k = &iwm->keys[j];
|
||||
|
||||
k->in_use = 0;
|
||||
ret = iwm_set_key(iwm, remove, 0, k);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
iwm->umac_profile->sec.ucast_cipher =
|
||||
UMAC_CIPHER_TYPE_NONE;
|
||||
iwm->umac_profile->sec.mcast_cipher =
|
||||
UMAC_CIPHER_TYPE_NONE;
|
||||
iwm->umac_profile->sec.auth_type =
|
||||
UMAC_AUTH_TYPE_OPEN;
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
key->in_use = 0;
|
||||
return iwm_set_key(iwm, remove, 0, key);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we havent set a profile yet, we cant set keys.
|
||||
* Keys will be pushed after we're associated.
|
||||
*/
|
||||
if (!iwm->umac_profile_active)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If there is a current active profile, but no
|
||||
* default key, it's not worth trying to associate again.
|
||||
*/
|
||||
if (!iwm->default_key)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Here we have an active profile, but a key setting changed.
|
||||
* We thus have to invalidate the current profile, and push the
|
||||
* new one. Keys will be pushed when association takes place.
|
||||
*/
|
||||
ret = iwm_invalidate_mlme_profile(iwm);
|
||||
if (ret < 0) {
|
||||
IWM_ERR(iwm, "Couldn't invalidate profile\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return iwm_send_mlme_profile(iwm);
|
||||
}
|
||||
|
||||
static int iwm_wext_giwencode(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *erq, char *key)
|
||||
{
|
||||
struct iwm_priv *iwm = ndev_to_iwm(dev);
|
||||
int idx, i;
|
||||
|
||||
idx = erq->flags & IW_ENCODE_INDEX;
|
||||
if (idx < 1 || idx > 4) {
|
||||
idx = -1;
|
||||
if (!iwm->default_key) {
|
||||
erq->length = 0;
|
||||
erq->flags |= IW_ENCODE_NOKEY;
|
||||
return 0;
|
||||
} else
|
||||
for (i = 0; i < IWM_NUM_KEYS; i++) {
|
||||
if (iwm->default_key == &iwm->keys[i]) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (idx < 0)
|
||||
return -EINVAL;
|
||||
} else
|
||||
idx--;
|
||||
|
||||
erq->flags = idx + 1;
|
||||
|
||||
if (!iwm->keys[idx].in_use) {
|
||||
erq->length = 0;
|
||||
erq->flags |= IW_ENCODE_DISABLED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(key, iwm->keys[idx].key,
|
||||
min_t(int, erq->length, iwm->keys[idx].key_len));
|
||||
erq->length = iwm->keys[idx].key_len;
|
||||
erq->flags |= IW_ENCODE_ENABLED;
|
||||
|
||||
if (iwm->umac_profile->mode == UMAC_MODE_BSS) {
|
||||
switch (iwm->umac_profile->sec.auth_type) {
|
||||
case UMAC_AUTH_TYPE_OPEN:
|
||||
erq->flags |= IW_ENCODE_OPEN;
|
||||
break;
|
||||
default:
|
||||
erq->flags |= IW_ENCODE_RESTRICTED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwm_set_wpa_version(struct iwm_priv *iwm, u8 wpa_version)
|
||||
{
|
||||
if (wpa_version & IW_AUTH_WPA_VERSION_WPA2)
|
||||
iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK;
|
||||
else if (wpa_version & IW_AUTH_WPA_VERSION_WPA)
|
||||
iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WPA_ON_MSK;
|
||||
else
|
||||
iwm->umac_profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwm_wext_siwpower(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_param *wrq, char *extra)
|
||||
{
|
||||
struct iwm_priv *iwm = ndev_to_iwm(dev);
|
||||
u32 power_index;
|
||||
|
||||
if (wrq->disabled) {
|
||||
power_index = IWM_POWER_INDEX_MIN;
|
||||
goto set;
|
||||
} else
|
||||
power_index = IWM_POWER_INDEX_DEFAULT;
|
||||
|
||||
switch (wrq->flags & IW_POWER_MODE) {
|
||||
case IW_POWER_ON:
|
||||
case IW_POWER_MODE:
|
||||
case IW_POWER_ALL_R:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
set:
|
||||
if (power_index == iwm->conf.power_index)
|
||||
return 0;
|
||||
|
||||
iwm->conf.power_index = power_index;
|
||||
|
||||
return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX,
|
||||
CFG_POWER_INDEX, iwm->conf.power_index);
|
||||
}
|
||||
|
||||
static int iwm_wext_giwpower(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
struct iwm_priv *iwm = ndev_to_iwm(dev);
|
||||
|
||||
wrqu->power.disabled = (iwm->conf.power_index == IWM_POWER_INDEX_MIN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwm_set_key_mgt(struct iwm_priv *iwm, u8 key_mgt)
|
||||
{
|
||||
u8 *auth_type = &iwm->umac_profile->sec.auth_type;
|
||||
|
||||
if (key_mgt == IW_AUTH_KEY_MGMT_802_1X)
|
||||
*auth_type = UMAC_AUTH_TYPE_8021X;
|
||||
else if (key_mgt == IW_AUTH_KEY_MGMT_PSK) {
|
||||
if (iwm->umac_profile->sec.flags &
|
||||
(UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK))
|
||||
*auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
|
||||
else
|
||||
*auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
|
||||
} else {
|
||||
IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwm_set_cipher(struct iwm_priv *iwm, u8 cipher, u8 ucast)
|
||||
{
|
||||
u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher :
|
||||
&iwm->umac_profile->sec.mcast_cipher;
|
||||
|
||||
switch (cipher) {
|
||||
case IW_AUTH_CIPHER_NONE:
|
||||
*profile_cipher = UMAC_CIPHER_TYPE_NONE;
|
||||
break;
|
||||
case IW_AUTH_CIPHER_WEP40:
|
||||
*profile_cipher = UMAC_CIPHER_TYPE_WEP_40;
|
||||
break;
|
||||
case IW_AUTH_CIPHER_TKIP:
|
||||
*profile_cipher = UMAC_CIPHER_TYPE_TKIP;
|
||||
break;
|
||||
case IW_AUTH_CIPHER_CCMP:
|
||||
*profile_cipher = UMAC_CIPHER_TYPE_CCMP;
|
||||
break;
|
||||
case IW_AUTH_CIPHER_WEP104:
|
||||
*profile_cipher = UMAC_CIPHER_TYPE_WEP_104;
|
||||
break;
|
||||
default:
|
||||
IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwm_set_auth_alg(struct iwm_priv *iwm, u8 auth_alg)
|
||||
{
|
||||
u8 *auth_type = &iwm->umac_profile->sec.auth_type;
|
||||
|
||||
switch (auth_alg) {
|
||||
case IW_AUTH_ALG_OPEN_SYSTEM:
|
||||
*auth_type = UMAC_AUTH_TYPE_OPEN;
|
||||
break;
|
||||
case IW_AUTH_ALG_SHARED_KEY:
|
||||
if (iwm->umac_profile->sec.flags &
|
||||
(UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) {
|
||||
if (*auth_type == UMAC_AUTH_TYPE_8021X)
|
||||
return -EINVAL;
|
||||
*auth_type = UMAC_AUTH_TYPE_RSNA_PSK;
|
||||
} else {
|
||||
*auth_type = UMAC_AUTH_TYPE_LEGACY_PSK;
|
||||
}
|
||||
break;
|
||||
case IW_AUTH_ALG_LEAP:
|
||||
default:
|
||||
IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", auth_alg);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwm_wext_siwauth(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_param *data, char *extra)
|
||||
{
|
||||
struct iwm_priv *iwm = ndev_to_iwm(dev);
|
||||
int ret;
|
||||
|
||||
if ((data->flags) &
|
||||
(IW_AUTH_WPA_VERSION | IW_AUTH_KEY_MGMT |
|
||||
IW_AUTH_WPA_ENABLED | IW_AUTH_80211_AUTH_ALG)) {
|
||||
/* We need to invalidate the current profile */
|
||||
if (iwm->umac_profile_active) {
|
||||
ret = iwm_invalidate_mlme_profile(iwm);
|
||||
if (ret < 0) {
|
||||
IWM_ERR(iwm, "Couldn't invalidate profile\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (data->flags & IW_AUTH_INDEX) {
|
||||
case IW_AUTH_WPA_VERSION:
|
||||
return iwm_set_wpa_version(iwm, data->value);
|
||||
break;
|
||||
case IW_AUTH_CIPHER_PAIRWISE:
|
||||
return iwm_set_cipher(iwm, data->value, 1);
|
||||
break;
|
||||
case IW_AUTH_CIPHER_GROUP:
|
||||
return iwm_set_cipher(iwm, data->value, 0);
|
||||
break;
|
||||
case IW_AUTH_KEY_MGMT:
|
||||
return iwm_set_key_mgt(iwm, data->value);
|
||||
break;
|
||||
case IW_AUTH_80211_AUTH_ALG:
|
||||
return iwm_set_auth_alg(iwm, data->value);
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwm_wext_giwauth(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_param *data, char *extra)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iwm_wext_siwencodeext(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *erq, char *extra)
|
||||
{
|
||||
struct iwm_priv *iwm = ndev_to_iwm(dev);
|
||||
struct iwm_key *key;
|
||||
struct iw_encode_ext *ext = (struct iw_encode_ext *) extra;
|
||||
int uninitialized_var(alg), idx, i, remove = 0;
|
||||
|
||||
IWM_DBG_WEXT(iwm, DBG, "alg: 0x%x\n", ext->alg);
|
||||
IWM_DBG_WEXT(iwm, DBG, "key len: %d\n", ext->key_len);
|
||||
IWM_DBG_WEXT(iwm, DBG, "ext_flags: 0x%x\n", ext->ext_flags);
|
||||
IWM_DBG_WEXT(iwm, DBG, "flags: 0x%x\n", erq->flags);
|
||||
IWM_DBG_WEXT(iwm, DBG, "length: 0x%x\n", erq->length);
|
||||
|
||||
switch (ext->alg) {
|
||||
case IW_ENCODE_ALG_NONE:
|
||||
remove = 1;
|
||||
break;
|
||||
case IW_ENCODE_ALG_WEP:
|
||||
if (ext->key_len == WLAN_KEY_LEN_WEP40)
|
||||
alg = UMAC_CIPHER_TYPE_WEP_40;
|
||||
else if (ext->key_len == WLAN_KEY_LEN_WEP104)
|
||||
alg = UMAC_CIPHER_TYPE_WEP_104;
|
||||
else {
|
||||
IWM_ERR(iwm, "Invalid key length: %d\n", ext->key_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
break;
|
||||
case IW_ENCODE_ALG_TKIP:
|
||||
alg = UMAC_CIPHER_TYPE_TKIP;
|
||||
break;
|
||||
case IW_ENCODE_ALG_CCMP:
|
||||
alg = UMAC_CIPHER_TYPE_CCMP;
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
idx = erq->flags & IW_ENCODE_INDEX;
|
||||
|
||||
if (idx == 0) {
|
||||
if (iwm->default_key)
|
||||
for (i = 0; i < IWM_NUM_KEYS; i++) {
|
||||
if (iwm->default_key == &iwm->keys[i]) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (idx < 1 || idx > 4) {
|
||||
return -EINVAL;
|
||||
} else
|
||||
idx--;
|
||||
|
||||
if (erq->flags & IW_ENCODE_DISABLED)
|
||||
remove = 1;
|
||||
else if ((erq->length == 0) ||
|
||||
(ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) {
|
||||
iwm->default_key = &iwm->keys[idx];
|
||||
if (iwm->umac_profile_active && ext->alg == IW_ENCODE_ALG_WEP)
|
||||
return iwm_set_tx_key(iwm, idx);
|
||||
}
|
||||
|
||||
key = iwm_key_init(iwm, idx, !remove, ext, alg);
|
||||
|
||||
return iwm_set_key(iwm, remove, !iwm->default_key, key);
|
||||
}
|
||||
|
||||
static const iw_handler iwm_handlers[] =
|
||||
{
|
||||
(iw_handler) NULL, /* SIOCSIWCOMMIT */
|
||||
(iw_handler) cfg80211_wext_giwname, /* SIOCGIWNAME */
|
||||
(iw_handler) NULL, /* SIOCSIWNWID */
|
||||
(iw_handler) NULL, /* SIOCGIWNWID */
|
||||
(iw_handler) iwm_wext_siwfreq, /* SIOCSIWFREQ */
|
||||
(iw_handler) iwm_wext_giwfreq, /* SIOCGIWFREQ */
|
||||
(iw_handler) cfg80211_wext_siwmode, /* SIOCSIWMODE */
|
||||
(iw_handler) cfg80211_wext_giwmode, /* SIOCGIWMODE */
|
||||
(iw_handler) NULL, /* SIOCSIWSENS */
|
||||
(iw_handler) NULL, /* SIOCGIWSENS */
|
||||
(iw_handler) NULL /* not used */, /* SIOCSIWRANGE */
|
||||
(iw_handler) cfg80211_wext_giwrange, /* SIOCGIWRANGE */
|
||||
(iw_handler) NULL /* not used */, /* SIOCSIWPRIV */
|
||||
(iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */
|
||||
(iw_handler) NULL /* not used */, /* SIOCSIWSTATS */
|
||||
(iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */
|
||||
(iw_handler) NULL, /* SIOCSIWSPY */
|
||||
(iw_handler) NULL, /* SIOCGIWSPY */
|
||||
(iw_handler) NULL, /* SIOCSIWTHRSPY */
|
||||
(iw_handler) NULL, /* SIOCGIWTHRSPY */
|
||||
(iw_handler) iwm_wext_siwap, /* SIOCSIWAP */
|
||||
(iw_handler) iwm_wext_giwap, /* SIOCGIWAP */
|
||||
(iw_handler) NULL, /* SIOCSIWMLME */
|
||||
(iw_handler) NULL, /* SIOCGIWAPLIST */
|
||||
(iw_handler) cfg80211_wext_siwscan, /* SIOCSIWSCAN */
|
||||
(iw_handler) cfg80211_wext_giwscan, /* SIOCGIWSCAN */
|
||||
(iw_handler) iwm_wext_siwessid, /* SIOCSIWESSID */
|
||||
(iw_handler) iwm_wext_giwessid, /* SIOCGIWESSID */
|
||||
(iw_handler) NULL, /* SIOCSIWNICKN */
|
||||
(iw_handler) NULL, /* SIOCGIWNICKN */
|
||||
(iw_handler) NULL, /* -- hole -- */
|
||||
(iw_handler) NULL, /* -- hole -- */
|
||||
(iw_handler) NULL, /* SIOCSIWRATE */
|
||||
(iw_handler) iwm_wext_giwrate, /* SIOCGIWRATE */
|
||||
(iw_handler) cfg80211_wext_siwrts, /* SIOCSIWRTS */
|
||||
(iw_handler) cfg80211_wext_giwrts, /* SIOCGIWRTS */
|
||||
(iw_handler) cfg80211_wext_siwfrag, /* SIOCSIWFRAG */
|
||||
(iw_handler) cfg80211_wext_giwfrag, /* SIOCGIWFRAG */
|
||||
(iw_handler) NULL, /* SIOCSIWTXPOW */
|
||||
(iw_handler) NULL, /* SIOCGIWTXPOW */
|
||||
(iw_handler) NULL, /* SIOCSIWRETRY */
|
||||
(iw_handler) NULL, /* SIOCGIWRETRY */
|
||||
(iw_handler) iwm_wext_siwencode, /* SIOCSIWENCODE */
|
||||
(iw_handler) iwm_wext_giwencode, /* SIOCGIWENCODE */
|
||||
(iw_handler) iwm_wext_siwpower, /* SIOCSIWPOWER */
|
||||
(iw_handler) iwm_wext_giwpower, /* SIOCGIWPOWER */
|
||||
(iw_handler) NULL, /* -- hole -- */
|
||||
(iw_handler) NULL, /* -- hole -- */
|
||||
(iw_handler) NULL, /* SIOCSIWGENIE */
|
||||
(iw_handler) NULL, /* SIOCGIWGENIE */
|
||||
(iw_handler) iwm_wext_siwauth, /* SIOCSIWAUTH */
|
||||
(iw_handler) iwm_wext_giwauth, /* SIOCGIWAUTH */
|
||||
(iw_handler) iwm_wext_siwencodeext, /* SIOCSIWENCODEEXT */
|
||||
(iw_handler) NULL, /* SIOCGIWENCODEEXT */
|
||||
(iw_handler) NULL, /* SIOCSIWPMKSA */
|
||||
(iw_handler) NULL, /* -- hole -- */
|
||||
};
|
||||
|
||||
const struct iw_handler_def iwm_iw_handler_def = {
|
||||
.num_standard = ARRAY_SIZE(iwm_handlers),
|
||||
.standard = (iw_handler *) iwm_handlers,
|
||||
.get_wireless_stats = iwm_get_wireless_stats,
|
||||
};
|
||||
|
|
@ -321,6 +321,8 @@ struct lbs_private {
|
|||
|
||||
u32 monitormode;
|
||||
u8 fw_ready;
|
||||
u8 fn_init_required;
|
||||
u8 fn_shutdown_required;
|
||||
};
|
||||
|
||||
extern struct cmd_confirm_sleep confirm_sleep;
|
||||
|
|
|
@ -86,6 +86,8 @@
|
|||
#define CMD_MESH_CONFIG_OLD 0x00a3
|
||||
#define CMD_MESH_CONFIG 0x00ac
|
||||
#define CMD_SET_BOOT2_VER 0x00a5
|
||||
#define CMD_FUNC_INIT 0x00a9
|
||||
#define CMD_FUNC_SHUTDOWN 0x00aa
|
||||
#define CMD_802_11_BEACON_CTRL 0x00b0
|
||||
|
||||
/* For the IEEE Power Save */
|
||||
|
|
|
@ -61,26 +61,30 @@ struct if_sdio_model {
|
|||
int model;
|
||||
const char *helper;
|
||||
const char *firmware;
|
||||
struct if_sdio_card *card;
|
||||
};
|
||||
|
||||
static struct if_sdio_model if_sdio_models[] = {
|
||||
{
|
||||
/* 8385 */
|
||||
.model = 0x04,
|
||||
.model = IF_SDIO_MODEL_8385,
|
||||
.helper = "sd8385_helper.bin",
|
||||
.firmware = "sd8385.bin",
|
||||
.card = NULL,
|
||||
},
|
||||
{
|
||||
/* 8686 */
|
||||
.model = 0x0B,
|
||||
.model = IF_SDIO_MODEL_8686,
|
||||
.helper = "sd8686_helper.bin",
|
||||
.firmware = "sd8686.bin",
|
||||
.card = NULL,
|
||||
},
|
||||
{
|
||||
/* 8688 */
|
||||
.model = 0x10,
|
||||
.model = IF_SDIO_MODEL_8688,
|
||||
.helper = "sd8688_helper.bin",
|
||||
.firmware = "sd8688.bin",
|
||||
.card = NULL,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -96,6 +100,7 @@ struct if_sdio_card {
|
|||
|
||||
int model;
|
||||
unsigned long ioport;
|
||||
unsigned int scratch_reg;
|
||||
|
||||
const char *helper;
|
||||
const char *firmware;
|
||||
|
@ -107,25 +112,29 @@ struct if_sdio_card {
|
|||
|
||||
struct workqueue_struct *workqueue;
|
||||
struct work_struct packet_worker;
|
||||
|
||||
u8 rx_unit;
|
||||
};
|
||||
|
||||
/********************************************************************/
|
||||
/* I/O */
|
||||
/********************************************************************/
|
||||
|
||||
/*
|
||||
* For SD8385/SD8686, this function reads firmware status after
|
||||
* the image is downloaded, or reads RX packet length when
|
||||
* interrupt (with IF_SDIO_H_INT_UPLD bit set) is received.
|
||||
* For SD8688, this function reads firmware status only.
|
||||
*/
|
||||
static u16 if_sdio_read_scratch(struct if_sdio_card *card, int *err)
|
||||
{
|
||||
int ret, reg;
|
||||
int ret;
|
||||
u16 scratch;
|
||||
|
||||
if (card->model == 0x04)
|
||||
reg = IF_SDIO_SCRATCH_OLD;
|
||||
else
|
||||
reg = IF_SDIO_SCRATCH;
|
||||
|
||||
scratch = sdio_readb(card->func, reg, &ret);
|
||||
scratch = sdio_readb(card->func, card->scratch_reg, &ret);
|
||||
if (!ret)
|
||||
scratch |= sdio_readb(card->func, reg + 1, &ret) << 8;
|
||||
scratch |= sdio_readb(card->func, card->scratch_reg + 1,
|
||||
&ret) << 8;
|
||||
|
||||
if (err)
|
||||
*err = ret;
|
||||
|
@ -136,6 +145,46 @@ static u16 if_sdio_read_scratch(struct if_sdio_card *card, int *err)
|
|||
return scratch;
|
||||
}
|
||||
|
||||
static u8 if_sdio_read_rx_unit(struct if_sdio_card *card)
|
||||
{
|
||||
int ret;
|
||||
u8 rx_unit;
|
||||
|
||||
rx_unit = sdio_readb(card->func, IF_SDIO_RX_UNIT, &ret);
|
||||
|
||||
if (ret)
|
||||
rx_unit = 0;
|
||||
|
||||
return rx_unit;
|
||||
}
|
||||
|
||||
static u16 if_sdio_read_rx_len(struct if_sdio_card *card, int *err)
|
||||
{
|
||||
int ret;
|
||||
u16 rx_len;
|
||||
|
||||
switch (card->model) {
|
||||
case IF_SDIO_MODEL_8385:
|
||||
case IF_SDIO_MODEL_8686:
|
||||
rx_len = if_sdio_read_scratch(card, &ret);
|
||||
break;
|
||||
case IF_SDIO_MODEL_8688:
|
||||
default: /* for newer chipsets */
|
||||
rx_len = sdio_readb(card->func, IF_SDIO_RX_LEN, &ret);
|
||||
if (!ret)
|
||||
rx_len <<= card->rx_unit;
|
||||
else
|
||||
rx_len = 0xffff; /* invalid length */
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (err)
|
||||
*err = ret;
|
||||
|
||||
return rx_len;
|
||||
}
|
||||
|
||||
static int if_sdio_handle_cmd(struct if_sdio_card *card,
|
||||
u8 *buffer, unsigned size)
|
||||
{
|
||||
|
@ -216,7 +265,7 @@ static int if_sdio_handle_event(struct if_sdio_card *card,
|
|||
|
||||
lbs_deb_enter(LBS_DEB_SDIO);
|
||||
|
||||
if (card->model == 0x04) {
|
||||
if (card->model == IF_SDIO_MODEL_8385) {
|
||||
event = sdio_readb(card->func, IF_SDIO_EVENT, &ret);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
@ -254,7 +303,7 @@ static int if_sdio_card_to_host(struct if_sdio_card *card)
|
|||
|
||||
lbs_deb_enter(LBS_DEB_SDIO);
|
||||
|
||||
size = if_sdio_read_scratch(card, &ret);
|
||||
size = if_sdio_read_rx_len(card, &ret);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
@ -497,7 +546,6 @@ static int if_sdio_prog_helper(struct if_sdio_card *card)
|
|||
ret = 0;
|
||||
|
||||
release:
|
||||
sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE);
|
||||
sdio_release_host(card->func);
|
||||
kfree(chunk_buffer);
|
||||
release_fw:
|
||||
|
@ -633,7 +681,6 @@ static int if_sdio_prog_real(struct if_sdio_card *card)
|
|||
ret = 0;
|
||||
|
||||
release:
|
||||
sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE);
|
||||
sdio_release_host(card->func);
|
||||
kfree(chunk_buffer);
|
||||
release_fw:
|
||||
|
@ -662,6 +709,8 @@ static int if_sdio_prog_firmware(struct if_sdio_card *card)
|
|||
if (ret)
|
||||
goto out;
|
||||
|
||||
lbs_deb_sdio("firmware status = %#x\n", scratch);
|
||||
|
||||
if (scratch == IF_SDIO_FIRMWARE_OK) {
|
||||
lbs_deb_sdio("firmware already loaded\n");
|
||||
goto success;
|
||||
|
@ -676,6 +725,9 @@ static int if_sdio_prog_firmware(struct if_sdio_card *card)
|
|||
goto out;
|
||||
|
||||
success:
|
||||
sdio_claim_host(card->func);
|
||||
sdio_set_block_size(card->func, IF_SDIO_BLOCK_SIZE);
|
||||
sdio_release_host(card->func);
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
|
@ -829,10 +881,10 @@ static int if_sdio_probe(struct sdio_func *func,
|
|||
if (sscanf(func->card->info[i],
|
||||
"ID: %x", &model) == 1)
|
||||
break;
|
||||
if (!strcmp(func->card->info[i], "IBIS Wireless SDIO Card")) {
|
||||
model = 4;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(func->card->info[i], "IBIS Wireless SDIO Card")) {
|
||||
model = IF_SDIO_MODEL_8385;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == func->card->num_info) {
|
||||
|
@ -846,6 +898,20 @@ static int if_sdio_probe(struct sdio_func *func,
|
|||
|
||||
card->func = func;
|
||||
card->model = model;
|
||||
|
||||
switch (card->model) {
|
||||
case IF_SDIO_MODEL_8385:
|
||||
card->scratch_reg = IF_SDIO_SCRATCH_OLD;
|
||||
break;
|
||||
case IF_SDIO_MODEL_8686:
|
||||
card->scratch_reg = IF_SDIO_SCRATCH;
|
||||
break;
|
||||
case IF_SDIO_MODEL_8688:
|
||||
default: /* for newer chipsets */
|
||||
card->scratch_reg = IF_SDIO_FW_STATUS;
|
||||
break;
|
||||
}
|
||||
|
||||
spin_lock_init(&card->lock);
|
||||
card->workqueue = create_workqueue("libertas_sdio");
|
||||
INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker);
|
||||
|
@ -861,6 +927,8 @@ static int if_sdio_probe(struct sdio_func *func,
|
|||
goto free;
|
||||
}
|
||||
|
||||
if_sdio_models[i].card = card;
|
||||
|
||||
card->helper = if_sdio_models[i].helper;
|
||||
card->firmware = if_sdio_models[i].firmware;
|
||||
|
||||
|
@ -923,15 +991,32 @@ static int if_sdio_probe(struct sdio_func *func,
|
|||
|
||||
priv->fw_ready = 1;
|
||||
|
||||
sdio_claim_host(func);
|
||||
|
||||
/*
|
||||
* Get rx_unit if the chip is SD8688 or newer.
|
||||
* SD8385 & SD8686 do not have rx_unit.
|
||||
*/
|
||||
if ((card->model != IF_SDIO_MODEL_8385)
|
||||
&& (card->model != IF_SDIO_MODEL_8686))
|
||||
card->rx_unit = if_sdio_read_rx_unit(card);
|
||||
else
|
||||
card->rx_unit = 0;
|
||||
|
||||
/*
|
||||
* Enable interrupts now that everything is set up
|
||||
*/
|
||||
sdio_claim_host(func);
|
||||
sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret);
|
||||
sdio_release_host(func);
|
||||
if (ret)
|
||||
goto reclaim;
|
||||
|
||||
/*
|
||||
* FUNC_INIT is required for SD8688 WLAN/BT multiple functions
|
||||
*/
|
||||
priv->fn_init_required =
|
||||
(card->model == IF_SDIO_MODEL_8688) ? 1 : 0;
|
||||
|
||||
ret = lbs_start_card(priv);
|
||||
if (ret)
|
||||
goto err_activate_card;
|
||||
|
@ -972,23 +1057,30 @@ static void if_sdio_remove(struct sdio_func *func)
|
|||
{
|
||||
struct if_sdio_card *card;
|
||||
struct if_sdio_packet *packet;
|
||||
int ret;
|
||||
|
||||
lbs_deb_enter(LBS_DEB_SDIO);
|
||||
|
||||
card = sdio_get_drvdata(func);
|
||||
|
||||
lbs_stop_card(card->priv);
|
||||
|
||||
card->priv->surpriseremoved = 1;
|
||||
|
||||
lbs_deb_sdio("call remove card\n");
|
||||
lbs_stop_card(card->priv);
|
||||
lbs_remove_card(card->priv);
|
||||
|
||||
flush_workqueue(card->workqueue);
|
||||
destroy_workqueue(card->workqueue);
|
||||
|
||||
sdio_claim_host(func);
|
||||
|
||||
/* Disable interrupts */
|
||||
sdio_writeb(func, 0x00, IF_SDIO_H_INT_MASK, &ret);
|
||||
|
||||
sdio_release_irq(func);
|
||||
sdio_disable_func(func);
|
||||
|
||||
sdio_release_host(func);
|
||||
|
||||
while (card->packets) {
|
||||
|
@ -1031,8 +1123,23 @@ static int __init if_sdio_init_module(void)
|
|||
|
||||
static void __exit if_sdio_exit_module(void)
|
||||
{
|
||||
int i;
|
||||
struct if_sdio_card *card;
|
||||
|
||||
lbs_deb_enter(LBS_DEB_SDIO);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(if_sdio_models); i++) {
|
||||
card = if_sdio_models[i].card;
|
||||
|
||||
/*
|
||||
* FUNC_SHUTDOWN is required for SD8688 WLAN/BT
|
||||
* multiple functions
|
||||
*/
|
||||
if (card && card->priv)
|
||||
card->priv->fn_shutdown_required =
|
||||
(card->model == IF_SDIO_MODEL_8688) ? 1 : 0;
|
||||
}
|
||||
|
||||
sdio_unregister_driver(&if_sdio_driver);
|
||||
|
||||
lbs_deb_leave(LBS_DEB_SDIO);
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
#ifndef _LBS_IF_SDIO_H
|
||||
#define _LBS_IF_SDIO_H
|
||||
|
||||
#define IF_SDIO_MODEL_8385 0x04
|
||||
#define IF_SDIO_MODEL_8686 0x0b
|
||||
#define IF_SDIO_MODEL_8688 0x10
|
||||
|
||||
#define IF_SDIO_IOPORT 0x00
|
||||
|
||||
#define IF_SDIO_H_INT_MASK 0x04
|
||||
|
@ -38,8 +42,12 @@
|
|||
|
||||
#define IF_SDIO_SCRATCH 0x34
|
||||
#define IF_SDIO_SCRATCH_OLD 0x80fe
|
||||
#define IF_SDIO_FW_STATUS 0x40
|
||||
#define IF_SDIO_FIRMWARE_OK 0xfedc
|
||||
|
||||
#define IF_SDIO_RX_LEN 0x42
|
||||
#define IF_SDIO_RX_UNIT 0x43
|
||||
|
||||
#define IF_SDIO_EVENT 0x80fc
|
||||
|
||||
#define IF_SDIO_BLOCK_SIZE 256
|
||||
|
|
|
@ -814,6 +814,13 @@ static void if_spi_e2h(struct if_spi_card *card)
|
|||
if (err)
|
||||
goto out;
|
||||
|
||||
/* re-enable the card event interrupt */
|
||||
spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG,
|
||||
~IF_SPI_HICU_CARD_EVENT);
|
||||
|
||||
/* generate a card interrupt */
|
||||
spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, IF_SPI_CIC_HOST_EVENT);
|
||||
|
||||
spin_lock_irqsave(&priv->driver_lock, flags);
|
||||
lbs_queue_event(priv, cause & 0xff);
|
||||
spin_unlock_irqrestore(&priv->driver_lock, flags);
|
||||
|
|
|
@ -1002,9 +1002,17 @@ static int lbs_setup_firmware(struct lbs_private *priv)
|
|||
{
|
||||
int ret = -1;
|
||||
s16 curlevel = 0, minlevel = 0, maxlevel = 0;
|
||||
struct cmd_header cmd;
|
||||
|
||||
lbs_deb_enter(LBS_DEB_FW);
|
||||
|
||||
if (priv->fn_init_required) {
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd),
|
||||
lbs_cmd_copyback, (unsigned long) &cmd))
|
||||
lbs_pr_alert("CMD_FUNC_INIT command failed\n");
|
||||
}
|
||||
|
||||
/* Read MAC address from firmware */
|
||||
memset(priv->current_addr, 0xff, ETH_ALEN);
|
||||
ret = lbs_update_hw_spec(priv);
|
||||
|
@ -1192,6 +1200,9 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev)
|
|||
priv->mesh_open = 0;
|
||||
priv->infra_open = 0;
|
||||
|
||||
priv->fn_init_required = 0;
|
||||
priv->fn_shutdown_required = 0;
|
||||
|
||||
/* Setup the OS Interface to our functions */
|
||||
dev->netdev_ops = &lbs_netdev_ops;
|
||||
dev->watchdog_timeo = 5 * HZ;
|
||||
|
@ -1373,11 +1384,20 @@ void lbs_stop_card(struct lbs_private *priv)
|
|||
struct net_device *dev;
|
||||
struct cmd_ctrl_node *cmdnode;
|
||||
unsigned long flags;
|
||||
struct cmd_header cmd;
|
||||
|
||||
lbs_deb_enter(LBS_DEB_MAIN);
|
||||
|
||||
if (!priv)
|
||||
goto out;
|
||||
|
||||
if (priv->fn_shutdown_required) {
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
if (__lbs_cmd(priv, CMD_FUNC_SHUTDOWN, &cmd, sizeof(cmd),
|
||||
lbs_cmd_copyback, (unsigned long) &cmd))
|
||||
lbs_pr_alert("CMD_FUNC_SHUTDOWN command failed\n");
|
||||
}
|
||||
|
||||
dev = priv->dev;
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
|
|
@ -291,6 +291,14 @@ struct mac80211_hwsim_data {
|
|||
bool ps_poll_pending;
|
||||
struct dentry *debugfs;
|
||||
struct dentry *debugfs_ps;
|
||||
|
||||
/*
|
||||
* Only radios in the same group can communicate together (the
|
||||
* channel has to match too). Each bit represents a group. A
|
||||
* radio can be in more then one group.
|
||||
*/
|
||||
u64 group;
|
||||
struct dentry *debugfs_group;
|
||||
};
|
||||
|
||||
|
||||
|
@ -412,7 +420,8 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
|
|||
|
||||
if (!data2->started || !data2->radio_enabled ||
|
||||
!hwsim_ps_rx_ok(data2, skb) ||
|
||||
data->channel->center_freq != data2->channel->center_freq)
|
||||
data->channel->center_freq != data2->channel->center_freq ||
|
||||
!(data->group & data2->group))
|
||||
continue;
|
||||
|
||||
nskb = skb_copy(skb, GFP_ATOMIC);
|
||||
|
@ -720,6 +729,7 @@ static void mac80211_hwsim_free(void)
|
|||
spin_unlock_bh(&hwsim_radio_lock);
|
||||
|
||||
list_for_each_entry(data, &tmplist, list) {
|
||||
debugfs_remove(data->debugfs_group);
|
||||
debugfs_remove(data->debugfs_ps);
|
||||
debugfs_remove(data->debugfs);
|
||||
ieee80211_unregister_hw(data->hw);
|
||||
|
@ -872,6 +882,24 @@ DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_ps, hwsim_fops_ps_read, hwsim_fops_ps_write,
|
|||
"%llu\n");
|
||||
|
||||
|
||||
static int hwsim_fops_group_read(void *dat, u64 *val)
|
||||
{
|
||||
struct mac80211_hwsim_data *data = dat;
|
||||
*val = data->group;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hwsim_fops_group_write(void *dat, u64 val)
|
||||
{
|
||||
struct mac80211_hwsim_data *data = dat;
|
||||
data->group = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group,
|
||||
hwsim_fops_group_read, hwsim_fops_group_write,
|
||||
"%llx\n");
|
||||
|
||||
static int __init init_mac80211_hwsim(void)
|
||||
{
|
||||
int i, err = 0;
|
||||
|
@ -976,6 +1004,8 @@ static int __init init_mac80211_hwsim(void)
|
|||
|
||||
hw->wiphy->bands[band] = sband;
|
||||
}
|
||||
/* By default all radios are belonging to the first group */
|
||||
data->group = 1;
|
||||
|
||||
/* Work to be done prior to ieee80211_register_hw() */
|
||||
switch (regtest) {
|
||||
|
@ -1100,6 +1130,9 @@ static int __init init_mac80211_hwsim(void)
|
|||
data->debugfs_ps = debugfs_create_file("ps", 0666,
|
||||
data->debugfs, data,
|
||||
&hwsim_fops_ps);
|
||||
data->debugfs_group = debugfs_create_file("group", 0666,
|
||||
data->debugfs, data,
|
||||
&hwsim_fops_group);
|
||||
|
||||
setup_timer(&data->beacon_timer, mac80211_hwsim_beacon,
|
||||
(unsigned long) hw);
|
||||
|
|
|
@ -96,7 +96,7 @@ static void p54spi_spi_write(struct p54s_priv *priv, u8 address,
|
|||
spi_message_add_tail(&t[0], &m);
|
||||
|
||||
t[1].tx_buf = buf;
|
||||
t[1].len = len;
|
||||
t[1].len = len & ~1;
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
if (len % 2) {
|
||||
|
@ -172,8 +172,6 @@ static int p54spi_wait_bit(struct p54s_priv *priv, u16 reg, __le32 bits)
|
|||
__le32 buffer = p54spi_read32(priv, reg);
|
||||
if ((buffer & bits) == bits)
|
||||
return 1;
|
||||
|
||||
msleep(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -181,9 +179,6 @@ static int p54spi_wait_bit(struct p54s_priv *priv, u16 reg, __le32 bits)
|
|||
static int p54spi_spi_write_dma(struct p54s_priv *priv, __le32 base,
|
||||
const void *buf, size_t len)
|
||||
{
|
||||
p54spi_write16(priv, SPI_ADRS_DMA_WRITE_CTRL,
|
||||
cpu_to_le16(SPI_DMA_WRITE_CTRL_ENABLE));
|
||||
|
||||
if (!p54spi_wait_bit(priv, SPI_ADRS_DMA_WRITE_CTRL,
|
||||
cpu_to_le32(HOST_ALLOWED))) {
|
||||
dev_err(&priv->spi->dev, "spi_write_dma not allowed "
|
||||
|
@ -191,6 +186,9 @@ static int p54spi_spi_write_dma(struct p54s_priv *priv, __le32 base,
|
|||
return -EAGAIN;
|
||||
}
|
||||
|
||||
p54spi_write16(priv, SPI_ADRS_DMA_WRITE_CTRL,
|
||||
cpu_to_le16(SPI_DMA_WRITE_CTRL_ENABLE));
|
||||
|
||||
p54spi_write16(priv, SPI_ADRS_DMA_WRITE_LEN, cpu_to_le16(len));
|
||||
p54spi_write32(priv, SPI_ADRS_DMA_WRITE_BASE, base);
|
||||
p54spi_spi_write(priv, SPI_ADRS_DMA_DATA, buf, len);
|
||||
|
@ -327,7 +325,7 @@ static inline void p54spi_int_ack(struct p54s_priv *priv, u32 val)
|
|||
p54spi_write32(priv, SPI_ADRS_HOST_INT_ACK, cpu_to_le32(val));
|
||||
}
|
||||
|
||||
static void p54spi_wakeup(struct p54s_priv *priv)
|
||||
static int p54spi_wakeup(struct p54s_priv *priv)
|
||||
{
|
||||
/* wake the chip */
|
||||
p54spi_write32(priv, SPI_ADRS_ARM_INTERRUPTS,
|
||||
|
@ -337,13 +335,11 @@ static void p54spi_wakeup(struct p54s_priv *priv)
|
|||
if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS,
|
||||
cpu_to_le32(SPI_HOST_INT_READY))) {
|
||||
dev_err(&priv->spi->dev, "INT_READY timeout\n");
|
||||
goto out;
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
p54spi_int_ack(priv, SPI_HOST_INT_READY);
|
||||
|
||||
out:
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void p54spi_sleep(struct p54s_priv *priv)
|
||||
|
@ -375,31 +371,44 @@ static int p54spi_rx(struct p54s_priv *priv)
|
|||
{
|
||||
struct sk_buff *skb;
|
||||
u16 len;
|
||||
u16 rx_head[2];
|
||||
#define READAHEAD_SZ (sizeof(rx_head)-sizeof(u16))
|
||||
|
||||
p54spi_wakeup(priv);
|
||||
if (p54spi_wakeup(priv) < 0)
|
||||
return -EBUSY;
|
||||
|
||||
/* dummy read to flush SPI DMA controller bug */
|
||||
p54spi_read16(priv, SPI_ADRS_GEN_PURP_1);
|
||||
|
||||
len = p54spi_read16(priv, SPI_ADRS_DMA_DATA);
|
||||
/* Read data size and first data word in one SPI transaction
|
||||
* This is workaround for firmware/DMA bug,
|
||||
* when first data word gets lost under high load.
|
||||
*/
|
||||
p54spi_spi_read(priv, SPI_ADRS_DMA_DATA, rx_head, sizeof(rx_head));
|
||||
len = rx_head[0];
|
||||
|
||||
if (len == 0) {
|
||||
dev_err(&priv->spi->dev, "rx request of zero bytes");
|
||||
p54spi_sleep(priv);
|
||||
dev_err(&priv->spi->dev, "rx request of zero bytes\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Firmware may insert up to 4 padding bytes after the lmac header,
|
||||
* but it does not amend the size of SPI data transfer.
|
||||
* Such packets has correct data size in header, thus referencing
|
||||
* past the end of allocated skb. Reserve extra 4 bytes for this case */
|
||||
skb = dev_alloc_skb(len + 4);
|
||||
if (!skb) {
|
||||
p54spi_sleep(priv);
|
||||
dev_err(&priv->spi->dev, "could not alloc skb");
|
||||
return 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
p54spi_spi_read(priv, SPI_ADRS_DMA_DATA, skb_put(skb, len), len);
|
||||
if (len <= READAHEAD_SZ) {
|
||||
memcpy(skb_put(skb, len), rx_head + 1, len);
|
||||
} else {
|
||||
memcpy(skb_put(skb, READAHEAD_SZ), rx_head + 1, READAHEAD_SZ);
|
||||
p54spi_spi_read(priv, SPI_ADRS_DMA_DATA,
|
||||
skb_put(skb, len - READAHEAD_SZ),
|
||||
len - READAHEAD_SZ);
|
||||
}
|
||||
p54spi_sleep(priv);
|
||||
/* Put additional bytes to compensate for the possible
|
||||
* alignment-caused truncation */
|
||||
|
@ -427,7 +436,8 @@ static int p54spi_tx_frame(struct p54s_priv *priv, struct sk_buff *skb)
|
|||
struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
|
||||
int ret = 0;
|
||||
|
||||
p54spi_wakeup(priv);
|
||||
if (p54spi_wakeup(priv) < 0)
|
||||
return -EBUSY;
|
||||
|
||||
ret = p54spi_spi_write_dma(priv, hdr->req_id, skb->data, skb->len);
|
||||
if (ret < 0)
|
||||
|
@ -436,16 +446,16 @@ static int p54spi_tx_frame(struct p54s_priv *priv, struct sk_buff *skb)
|
|||
if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS,
|
||||
cpu_to_le32(SPI_HOST_INT_WR_READY))) {
|
||||
dev_err(&priv->spi->dev, "WR_READY timeout\n");
|
||||
ret = -1;
|
||||
ret = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
p54spi_int_ack(priv, SPI_HOST_INT_WR_READY);
|
||||
p54spi_sleep(priv);
|
||||
|
||||
if (FREE_AFTER_TX(skb))
|
||||
p54_free_skb(priv->hw, skb);
|
||||
out:
|
||||
p54spi_sleep(priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -515,8 +525,7 @@ static void p54spi_work(struct work_struct *work)
|
|||
|
||||
mutex_lock(&priv->mutex);
|
||||
|
||||
if (priv->fw_state == FW_STATE_OFF &&
|
||||
priv->fw_state == FW_STATE_RESET)
|
||||
if (priv->fw_state == FW_STATE_OFF)
|
||||
goto out;
|
||||
|
||||
ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS);
|
||||
|
@ -543,11 +552,6 @@ static void p54spi_work(struct work_struct *work)
|
|||
}
|
||||
|
||||
ret = p54spi_wq_tx(priv);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS);
|
||||
|
||||
out:
|
||||
mutex_unlock(&priv->mutex);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* Driver for RNDIS based wireless USB devices.
|
||||
*
|
||||
* Copyright (C) 2007 by Bjorge Dijkstra <bjd@jooz.net>
|
||||
* Copyright (C) 2008 by Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
|
||||
* Copyright (C) 2008-2009 by Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -196,6 +196,18 @@ enum ndis_80211_priv_filter {
|
|||
ndis_80211_priv_8021x_wep
|
||||
};
|
||||
|
||||
enum ndis_80211_addkey_bits {
|
||||
ndis_80211_addkey_8021x_auth = cpu_to_le32(1 << 28),
|
||||
ndis_80211_addkey_set_init_recv_seq = cpu_to_le32(1 << 29),
|
||||
ndis_80211_addkey_pairwise_key = cpu_to_le32(1 << 30),
|
||||
ndis_80211_addkey_transmit_key = cpu_to_le32(1 << 31),
|
||||
};
|
||||
|
||||
enum ndis_80211_addwep_bits {
|
||||
ndis_80211_addwep_perclient_key = cpu_to_le32(1 << 30),
|
||||
ndis_80211_addwep_transmit_key = cpu_to_le32(1 << 31),
|
||||
};
|
||||
|
||||
struct ndis_80211_ssid {
|
||||
__le32 length;
|
||||
u8 essid[NDIS_802_11_LENGTH_SSID];
|
||||
|
@ -309,7 +321,6 @@ enum wpa_key_mgmt { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE,
|
|||
#define CAP_MODE_80211B 2
|
||||
#define CAP_MODE_80211G 4
|
||||
#define CAP_MODE_MASK 7
|
||||
#define CAP_SUPPORT_TXPOWER 8
|
||||
|
||||
#define WORK_LINK_UP (1<<0)
|
||||
#define WORK_LINK_DOWN (1<<1)
|
||||
|
@ -394,6 +405,7 @@ struct rndis_wext_private {
|
|||
int encr_tx_key_index;
|
||||
char encr_keys[4][32];
|
||||
int encr_key_len[4];
|
||||
char encr_key_wpa[4];
|
||||
int wpa_version;
|
||||
int wpa_keymgmt;
|
||||
int wpa_authalg;
|
||||
|
@ -945,7 +957,7 @@ static int set_infra_mode(struct usbnet *usbdev, int mode)
|
|||
if (priv->wpa_keymgmt == 0 ||
|
||||
priv->wpa_keymgmt == IW_AUTH_KEY_MGMT_802_1X) {
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (priv->encr_key_len[i] > 0)
|
||||
if (priv->encr_key_len[i] > 0 && !priv->encr_key_wpa[i])
|
||||
add_wep_key(usbdev, priv->encr_keys[i],
|
||||
priv->encr_key_len[i], i);
|
||||
}
|
||||
|
@ -999,7 +1011,7 @@ static int add_wep_key(struct usbnet *usbdev, char *key, int key_len, int index)
|
|||
memcpy(&ndis_key.material, key, key_len);
|
||||
|
||||
if (index == priv->encr_tx_key_index) {
|
||||
ndis_key.index |= cpu_to_le32(1 << 31);
|
||||
ndis_key.index |= ndis_80211_addwep_transmit_key;
|
||||
ret = set_encr_mode(usbdev, IW_AUTH_CIPHER_WEP104,
|
||||
IW_AUTH_CIPHER_NONE);
|
||||
if (ret)
|
||||
|
@ -1016,12 +1028,81 @@ static int add_wep_key(struct usbnet *usbdev, char *key, int key_len, int index)
|
|||
}
|
||||
|
||||
priv->encr_key_len[index] = key_len;
|
||||
priv->encr_key_wpa[index] = 0;
|
||||
memcpy(&priv->encr_keys[index], key, key_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len,
|
||||
int index, const struct sockaddr *addr,
|
||||
const u8 *rx_seq, int alg, int flags)
|
||||
{
|
||||
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
|
||||
struct ndis_80211_key ndis_key;
|
||||
int ret;
|
||||
|
||||
if (index < 0 || index >= 4)
|
||||
return -EINVAL;
|
||||
if (key_len > sizeof(ndis_key.material) || key_len < 0)
|
||||
return -EINVAL;
|
||||
if ((flags & ndis_80211_addkey_set_init_recv_seq) && !rx_seq)
|
||||
return -EINVAL;
|
||||
if ((flags & ndis_80211_addkey_pairwise_key) && !addr)
|
||||
return -EINVAL;
|
||||
|
||||
devdbg(usbdev, "add_wpa_key(%i): flags:%i%i%i", index,
|
||||
!!(flags & ndis_80211_addkey_transmit_key),
|
||||
!!(flags & ndis_80211_addkey_pairwise_key),
|
||||
!!(flags & ndis_80211_addkey_set_init_recv_seq));
|
||||
|
||||
memset(&ndis_key, 0, sizeof(ndis_key));
|
||||
|
||||
ndis_key.size = cpu_to_le32(sizeof(ndis_key) -
|
||||
sizeof(ndis_key.material) + key_len);
|
||||
ndis_key.length = cpu_to_le32(key_len);
|
||||
ndis_key.index = cpu_to_le32(index) | flags;
|
||||
|
||||
if (alg == IW_ENCODE_ALG_TKIP && key_len == 32) {
|
||||
/* wpa_supplicant gives us the Michael MIC RX/TX keys in
|
||||
* different order than NDIS spec, so swap the order here. */
|
||||
memcpy(ndis_key.material, key, 16);
|
||||
memcpy(ndis_key.material + 16, key + 24, 8);
|
||||
memcpy(ndis_key.material + 24, key + 16, 8);
|
||||
} else
|
||||
memcpy(ndis_key.material, key, key_len);
|
||||
|
||||
if (flags & ndis_80211_addkey_set_init_recv_seq)
|
||||
memcpy(ndis_key.rsc, rx_seq, 6);
|
||||
|
||||
if (flags & ndis_80211_addkey_pairwise_key) {
|
||||
/* pairwise key */
|
||||
memcpy(ndis_key.bssid, addr->sa_data, ETH_ALEN);
|
||||
} else {
|
||||
/* group key */
|
||||
if (priv->infra_mode == ndis_80211_infra_adhoc)
|
||||
memset(ndis_key.bssid, 0xff, ETH_ALEN);
|
||||
else
|
||||
get_bssid(usbdev, ndis_key.bssid);
|
||||
}
|
||||
|
||||
ret = rndis_set_oid(usbdev, OID_802_11_ADD_KEY, &ndis_key,
|
||||
le32_to_cpu(ndis_key.size));
|
||||
devdbg(usbdev, "add_wpa_key: OID_802_11_ADD_KEY -> %08X", ret);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
priv->encr_key_len[index] = key_len;
|
||||
priv->encr_key_wpa[index] = 1;
|
||||
|
||||
if (flags & ndis_80211_addkey_transmit_key)
|
||||
priv->encr_tx_key_index = index;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* remove_key is for both wep and wpa */
|
||||
static int remove_key(struct usbnet *usbdev, int index, u8 bssid[ETH_ALEN])
|
||||
{
|
||||
|
@ -1034,6 +1115,7 @@ static int remove_key(struct usbnet *usbdev, int index, u8 bssid[ETH_ALEN])
|
|||
return 0;
|
||||
|
||||
priv->encr_key_len[index] = 0;
|
||||
priv->encr_key_wpa[index] = 0;
|
||||
memset(&priv->encr_keys[index], 0, sizeof(priv->encr_keys[index]));
|
||||
|
||||
if (priv->wpa_cipher_pair == IW_AUTH_CIPHER_TKIP ||
|
||||
|
@ -1045,7 +1127,8 @@ static int remove_key(struct usbnet *usbdev, int index, u8 bssid[ETH_ALEN])
|
|||
if (bssid) {
|
||||
/* pairwise key */
|
||||
if (memcmp(bssid, ffff_bssid, ETH_ALEN) != 0)
|
||||
remove_key.index |= cpu_to_le32(1 << 30);
|
||||
remove_key.index |=
|
||||
ndis_80211_addkey_pairwise_key;
|
||||
memcpy(remove_key.bssid, bssid,
|
||||
sizeof(remove_key.bssid));
|
||||
} else
|
||||
|
@ -1590,9 +1673,7 @@ static int rndis_iw_set_encode_ext(struct net_device *dev,
|
|||
struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
|
||||
struct usbnet *usbdev = netdev_priv(dev);
|
||||
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
|
||||
struct ndis_80211_key ndis_key;
|
||||
int keyidx, ret;
|
||||
u8 *addr;
|
||||
int keyidx, flags;
|
||||
|
||||
keyidx = wrqu->encoding.flags & IW_ENCODE_INDEX;
|
||||
|
||||
|
@ -1615,58 +1696,16 @@ static int rndis_iw_set_encode_ext(struct net_device *dev,
|
|||
ext->alg == IW_ENCODE_ALG_NONE || ext->key_len == 0)
|
||||
return remove_key(usbdev, keyidx, NULL);
|
||||
|
||||
if (ext->key_len > sizeof(ndis_key.material))
|
||||
return -1;
|
||||
|
||||
memset(&ndis_key, 0, sizeof(ndis_key));
|
||||
|
||||
ndis_key.size = cpu_to_le32(sizeof(ndis_key) -
|
||||
sizeof(ndis_key.material) + ext->key_len);
|
||||
ndis_key.length = cpu_to_le32(ext->key_len);
|
||||
ndis_key.index = cpu_to_le32(keyidx);
|
||||
|
||||
if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) {
|
||||
memcpy(ndis_key.rsc, ext->rx_seq, 6);
|
||||
ndis_key.index |= cpu_to_le32(1 << 29);
|
||||
}
|
||||
|
||||
addr = ext->addr.sa_data;
|
||||
if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
|
||||
/* group key */
|
||||
if (priv->infra_mode == ndis_80211_infra_adhoc)
|
||||
memset(ndis_key.bssid, 0xff, ETH_ALEN);
|
||||
else
|
||||
get_bssid(usbdev, ndis_key.bssid);
|
||||
} else {
|
||||
/* pairwise key */
|
||||
ndis_key.index |= cpu_to_le32(1 << 30);
|
||||
memcpy(ndis_key.bssid, addr, ETH_ALEN);
|
||||
}
|
||||
|
||||
flags = 0;
|
||||
if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID)
|
||||
flags |= ndis_80211_addkey_set_init_recv_seq;
|
||||
if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY))
|
||||
flags |= ndis_80211_addkey_pairwise_key;
|
||||
if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)
|
||||
ndis_key.index |= cpu_to_le32(1 << 31);
|
||||
flags |= ndis_80211_addkey_transmit_key;
|
||||
|
||||
if (ext->alg == IW_ENCODE_ALG_TKIP && ext->key_len == 32) {
|
||||
/* wpa_supplicant gives us the Michael MIC RX/TX keys in
|
||||
* different order than NDIS spec, so swap the order here. */
|
||||
memcpy(ndis_key.material, ext->key, 16);
|
||||
memcpy(ndis_key.material + 16, ext->key + 24, 8);
|
||||
memcpy(ndis_key.material + 24, ext->key + 16, 8);
|
||||
} else
|
||||
memcpy(ndis_key.material, ext->key, ext->key_len);
|
||||
|
||||
ret = rndis_set_oid(usbdev, OID_802_11_ADD_KEY, &ndis_key,
|
||||
le32_to_cpu(ndis_key.size));
|
||||
devdbg(usbdev, "SIOCSIWENCODEEXT: OID_802_11_ADD_KEY -> %08X", ret);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
priv->encr_key_len[keyidx] = ext->key_len;
|
||||
memcpy(&priv->encr_keys[keyidx], ndis_key.material, ext->key_len);
|
||||
if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)
|
||||
priv->encr_tx_key_index = keyidx;
|
||||
|
||||
return 0;
|
||||
return add_wpa_key(usbdev, ext->key, ext->key_len, keyidx, &ext->addr,
|
||||
ext->rx_seq, ext->alg, flags);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1849,18 +1888,10 @@ static int rndis_iw_get_txpower(struct net_device *dev,
|
|||
struct usbnet *usbdev = netdev_priv(dev);
|
||||
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
|
||||
__le32 tx_power;
|
||||
int ret = 0, len;
|
||||
|
||||
if (priv->radio_on) {
|
||||
if (priv->caps & CAP_SUPPORT_TXPOWER) {
|
||||
len = sizeof(tx_power);
|
||||
ret = rndis_query_oid(usbdev, OID_802_11_TX_POWER_LEVEL,
|
||||
&tx_power, &len);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
} else
|
||||
/* fake incase not supported */
|
||||
tx_power = cpu_to_le32(get_bcm4320_power(priv));
|
||||
/* fake since changing tx_power (by userlevel) not supported */
|
||||
tx_power = cpu_to_le32(get_bcm4320_power(priv));
|
||||
|
||||
wrqu->txpower.flags = IW_TXPOW_MWATT;
|
||||
wrqu->txpower.value = le32_to_cpu(tx_power);
|
||||
|
@ -1873,7 +1904,7 @@ static int rndis_iw_get_txpower(struct net_device *dev,
|
|||
|
||||
devdbg(usbdev, "SIOCGIWTXPOW: %d", wrqu->txpower.value);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1883,7 +1914,6 @@ static int rndis_iw_set_txpower(struct net_device *dev,
|
|||
struct usbnet *usbdev = netdev_priv(dev);
|
||||
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
|
||||
__le32 tx_power = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (!wrqu->txpower.disabled) {
|
||||
if (wrqu->txpower.flags == IW_TXPOW_MWATT)
|
||||
|
@ -1906,22 +1936,10 @@ static int rndis_iw_set_txpower(struct net_device *dev,
|
|||
devdbg(usbdev, "SIOCSIWTXPOW: %d", le32_to_cpu(tx_power));
|
||||
|
||||
if (le32_to_cpu(tx_power) != 0) {
|
||||
if (priv->caps & CAP_SUPPORT_TXPOWER) {
|
||||
/* turn radio on first */
|
||||
if (!priv->radio_on)
|
||||
disassociate(usbdev, 1);
|
||||
|
||||
ret = rndis_set_oid(usbdev, OID_802_11_TX_POWER_LEVEL,
|
||||
&tx_power, sizeof(tx_power));
|
||||
if (ret != 0)
|
||||
ret = -EOPNOTSUPP;
|
||||
return ret;
|
||||
} else {
|
||||
/* txpower unsupported, just turn radio on */
|
||||
if (!priv->radio_on)
|
||||
return disassociate(usbdev, 1);
|
||||
return 0; /* all ready on */
|
||||
}
|
||||
/* txpower unsupported, just turn radio on */
|
||||
if (!priv->radio_on)
|
||||
return disassociate(usbdev, 1);
|
||||
return 0; /* all ready on */
|
||||
}
|
||||
|
||||
/* tx_power == 0, turn off radio */
|
||||
|
@ -2130,16 +2148,8 @@ static int rndis_wext_get_caps(struct usbnet *usbdev)
|
|||
__le32 items[8];
|
||||
} networks_supported;
|
||||
int len, retval, i, n;
|
||||
__le32 tx_power;
|
||||
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
|
||||
|
||||
/* determine if supports setting txpower */
|
||||
len = sizeof(tx_power);
|
||||
retval = rndis_query_oid(usbdev, OID_802_11_TX_POWER_LEVEL, &tx_power,
|
||||
&len);
|
||||
if (retval == 0 && le32_to_cpu(tx_power) != 0xFF)
|
||||
priv->caps |= CAP_SUPPORT_TXPOWER;
|
||||
|
||||
/* determine supported modes */
|
||||
len = sizeof(networks_supported);
|
||||
retval = rndis_query_oid(usbdev, OID_802_11_NETWORK_TYPES_SUPPORTED,
|
||||
|
@ -2275,7 +2285,17 @@ static void rndis_update_wireless_stats(struct work_struct *work)
|
|||
}
|
||||
|
||||
|
||||
static int bcm4320_early_init(struct usbnet *usbdev)
|
||||
static int bcm4320a_early_init(struct usbnet *usbdev)
|
||||
{
|
||||
/* bcm4320a doesn't handle configuration parameters well. Try
|
||||
* set any and you get partially zeroed mac and broken device.
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int bcm4320b_early_init(struct usbnet *usbdev)
|
||||
{
|
||||
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
|
||||
char buf[8];
|
||||
|
@ -2515,7 +2535,7 @@ static const struct driver_info bcm4320b_info = {
|
|||
.rx_fixup = rndis_rx_fixup,
|
||||
.tx_fixup = rndis_tx_fixup,
|
||||
.reset = rndis_wext_reset,
|
||||
.early_init = bcm4320_early_init,
|
||||
.early_init = bcm4320b_early_init,
|
||||
.link_change = rndis_wext_link_change,
|
||||
};
|
||||
|
||||
|
@ -2528,7 +2548,7 @@ static const struct driver_info bcm4320a_info = {
|
|||
.rx_fixup = rndis_rx_fixup,
|
||||
.tx_fixup = rndis_tx_fixup,
|
||||
.reset = rndis_wext_reset,
|
||||
.early_init = bcm4320_early_init,
|
||||
.early_init = bcm4320a_early_init,
|
||||
.link_change = rndis_wext_link_change,
|
||||
};
|
||||
|
||||
|
@ -2541,7 +2561,7 @@ static const struct driver_info rndis_wext_info = {
|
|||
.rx_fixup = rndis_rx_fixup,
|
||||
.tx_fixup = rndis_tx_fixup,
|
||||
.reset = rndis_wext_reset,
|
||||
.early_init = bcm4320_early_init,
|
||||
.early_init = bcm4320a_early_init,
|
||||
.link_change = rndis_wext_link_change,
|
||||
};
|
||||
|
||||
|
|
|
@ -335,10 +335,11 @@ static void rt2400pci_config_erp(struct rt2x00_dev *rt2x00dev,
|
|||
preamble_mask = erp->short_preamble << 3;
|
||||
|
||||
rt2x00pci_register_read(rt2x00dev, TXCSR1, ®);
|
||||
rt2x00_set_field32(®, TXCSR1_ACK_TIMEOUT,
|
||||
erp->ack_timeout);
|
||||
rt2x00_set_field32(®, TXCSR1_ACK_TIMEOUT, erp->ack_timeout);
|
||||
rt2x00_set_field32(®, TXCSR1_ACK_CONSUME_TIME,
|
||||
erp->ack_consume_time);
|
||||
rt2x00_set_field32(®, TXCSR1_TSF_OFFSET, IEEE80211_HEADER);
|
||||
rt2x00_set_field32(®, TXCSR1_AUTORESPONDER, 1);
|
||||
rt2x00pci_register_write(rt2x00dev, TXCSR1, reg);
|
||||
|
||||
rt2x00pci_register_read(rt2x00dev, ARCSR2, ®);
|
||||
|
@ -371,6 +372,11 @@ static void rt2400pci_config_erp(struct rt2x00_dev *rt2x00dev,
|
|||
rt2x00_set_field32(®, CSR11_SLOT_TIME, erp->slot_time);
|
||||
rt2x00pci_register_write(rt2x00dev, CSR11, reg);
|
||||
|
||||
rt2x00pci_register_read(rt2x00dev, CSR12, ®);
|
||||
rt2x00_set_field32(®, CSR12_BEACON_INTERVAL, erp->beacon_int * 16);
|
||||
rt2x00_set_field32(®, CSR12_CFP_MAX_DURATION, erp->beacon_int * 16);
|
||||
rt2x00pci_register_write(rt2x00dev, CSR12, reg);
|
||||
|
||||
rt2x00pci_register_read(rt2x00dev, CSR18, ®);
|
||||
rt2x00_set_field32(®, CSR18_SIFS, erp->sifs);
|
||||
rt2x00_set_field32(®, CSR18_PIFS, erp->pifs);
|
||||
|
@ -503,24 +509,6 @@ static void rt2400pci_config_retry_limit(struct rt2x00_dev *rt2x00dev,
|
|||
rt2x00pci_register_write(rt2x00dev, CSR11, reg);
|
||||
}
|
||||
|
||||
static void rt2400pci_config_duration(struct rt2x00_dev *rt2x00dev,
|
||||
struct rt2x00lib_conf *libconf)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
rt2x00pci_register_read(rt2x00dev, TXCSR1, ®);
|
||||
rt2x00_set_field32(®, TXCSR1_TSF_OFFSET, IEEE80211_HEADER);
|
||||
rt2x00_set_field32(®, TXCSR1_AUTORESPONDER, 1);
|
||||
rt2x00pci_register_write(rt2x00dev, TXCSR1, reg);
|
||||
|
||||
rt2x00pci_register_read(rt2x00dev, CSR12, ®);
|
||||
rt2x00_set_field32(®, CSR12_BEACON_INTERVAL,
|
||||
libconf->conf->beacon_int * 16);
|
||||
rt2x00_set_field32(®, CSR12_CFP_MAX_DURATION,
|
||||
libconf->conf->beacon_int * 16);
|
||||
rt2x00pci_register_write(rt2x00dev, CSR12, reg);
|
||||
}
|
||||
|
||||
static void rt2400pci_config_ps(struct rt2x00_dev *rt2x00dev,
|
||||
struct rt2x00lib_conf *libconf)
|
||||
{
|
||||
|
@ -558,8 +546,6 @@ static void rt2400pci_config(struct rt2x00_dev *rt2x00dev,
|
|||
libconf->conf->power_level);
|
||||
if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
|
||||
rt2400pci_config_retry_limit(rt2x00dev, libconf);
|
||||
if (flags & IEEE80211_CONF_CHANGE_BEACON_INTERVAL)
|
||||
rt2400pci_config_duration(rt2x00dev, libconf);
|
||||
if (flags & IEEE80211_CONF_CHANGE_PS)
|
||||
rt2400pci_config_ps(rt2x00dev, libconf);
|
||||
}
|
||||
|
|
|
@ -341,10 +341,11 @@ static void rt2500pci_config_erp(struct rt2x00_dev *rt2x00dev,
|
|||
preamble_mask = erp->short_preamble << 3;
|
||||
|
||||
rt2x00pci_register_read(rt2x00dev, TXCSR1, ®);
|
||||
rt2x00_set_field32(®, TXCSR1_ACK_TIMEOUT,
|
||||
erp->ack_timeout);
|
||||
rt2x00_set_field32(®, TXCSR1_ACK_TIMEOUT, erp->ack_timeout);
|
||||
rt2x00_set_field32(®, TXCSR1_ACK_CONSUME_TIME,
|
||||
erp->ack_consume_time);
|
||||
rt2x00_set_field32(®, TXCSR1_TSF_OFFSET, IEEE80211_HEADER);
|
||||
rt2x00_set_field32(®, TXCSR1_AUTORESPONDER, 1);
|
||||
rt2x00pci_register_write(rt2x00dev, TXCSR1, reg);
|
||||
|
||||
rt2x00pci_register_read(rt2x00dev, ARCSR2, ®);
|
||||
|
@ -377,6 +378,11 @@ static void rt2500pci_config_erp(struct rt2x00_dev *rt2x00dev,
|
|||
rt2x00_set_field32(®, CSR11_SLOT_TIME, erp->slot_time);
|
||||
rt2x00pci_register_write(rt2x00dev, CSR11, reg);
|
||||
|
||||
rt2x00pci_register_read(rt2x00dev, CSR12, ®);
|
||||
rt2x00_set_field32(®, CSR12_BEACON_INTERVAL, erp->beacon_int * 16);
|
||||
rt2x00_set_field32(®, CSR12_CFP_MAX_DURATION, erp->beacon_int * 16);
|
||||
rt2x00pci_register_write(rt2x00dev, CSR12, reg);
|
||||
|
||||
rt2x00pci_register_read(rt2x00dev, CSR18, ®);
|
||||
rt2x00_set_field32(®, CSR18_SIFS, erp->sifs);
|
||||
rt2x00_set_field32(®, CSR18_PIFS, erp->pifs);
|
||||
|
@ -552,24 +558,6 @@ static void rt2500pci_config_retry_limit(struct rt2x00_dev *rt2x00dev,
|
|||
rt2x00pci_register_write(rt2x00dev, CSR11, reg);
|
||||
}
|
||||
|
||||
static void rt2500pci_config_duration(struct rt2x00_dev *rt2x00dev,
|
||||
struct rt2x00lib_conf *libconf)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
rt2x00pci_register_read(rt2x00dev, TXCSR1, ®);
|
||||
rt2x00_set_field32(®, TXCSR1_TSF_OFFSET, IEEE80211_HEADER);
|
||||
rt2x00_set_field32(®, TXCSR1_AUTORESPONDER, 1);
|
||||
rt2x00pci_register_write(rt2x00dev, TXCSR1, reg);
|
||||
|
||||
rt2x00pci_register_read(rt2x00dev, CSR12, ®);
|
||||
rt2x00_set_field32(®, CSR12_BEACON_INTERVAL,
|
||||
libconf->conf->beacon_int * 16);
|
||||
rt2x00_set_field32(®, CSR12_CFP_MAX_DURATION,
|
||||
libconf->conf->beacon_int * 16);
|
||||
rt2x00pci_register_write(rt2x00dev, CSR12, reg);
|
||||
}
|
||||
|
||||
static void rt2500pci_config_ps(struct rt2x00_dev *rt2x00dev,
|
||||
struct rt2x00lib_conf *libconf)
|
||||
{
|
||||
|
@ -609,8 +597,6 @@ static void rt2500pci_config(struct rt2x00_dev *rt2x00dev,
|
|||
libconf->conf->power_level);
|
||||
if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
|
||||
rt2500pci_config_retry_limit(rt2x00dev, libconf);
|
||||
if (flags & IEEE80211_CONF_CHANGE_BEACON_INTERVAL)
|
||||
rt2500pci_config_duration(rt2x00dev, libconf);
|
||||
if (flags & IEEE80211_CONF_CHANGE_PS)
|
||||
rt2500pci_config_ps(rt2x00dev, libconf);
|
||||
}
|
||||
|
|
|
@ -503,6 +503,10 @@ static void rt2500usb_config_erp(struct rt2x00_dev *rt2x00dev,
|
|||
|
||||
rt2500usb_register_write(rt2x00dev, TXRX_CSR11, erp->basic_rates);
|
||||
|
||||
rt2500usb_register_read(rt2x00dev, TXRX_CSR18, ®);
|
||||
rt2x00_set_field16(®, TXRX_CSR18_INTERVAL, erp->beacon_int * 4);
|
||||
rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg);
|
||||
|
||||
rt2500usb_register_write(rt2x00dev, MAC_CSR10, erp->slot_time);
|
||||
rt2500usb_register_write(rt2x00dev, MAC_CSR11, erp->sifs);
|
||||
rt2500usb_register_write(rt2x00dev, MAC_CSR12, erp->eifs);
|
||||
|
@ -632,17 +636,6 @@ static void rt2500usb_config_txpower(struct rt2x00_dev *rt2x00dev,
|
|||
rt2500usb_rf_write(rt2x00dev, 3, rf3);
|
||||
}
|
||||
|
||||
static void rt2500usb_config_duration(struct rt2x00_dev *rt2x00dev,
|
||||
struct rt2x00lib_conf *libconf)
|
||||
{
|
||||
u16 reg;
|
||||
|
||||
rt2500usb_register_read(rt2x00dev, TXRX_CSR18, ®);
|
||||
rt2x00_set_field16(®, TXRX_CSR18_INTERVAL,
|
||||
libconf->conf->beacon_int * 4);
|
||||
rt2500usb_register_write(rt2x00dev, TXRX_CSR18, reg);
|
||||
}
|
||||
|
||||
static void rt2500usb_config_ps(struct rt2x00_dev *rt2x00dev,
|
||||
struct rt2x00lib_conf *libconf)
|
||||
{
|
||||
|
@ -680,8 +673,6 @@ static void rt2500usb_config(struct rt2x00_dev *rt2x00dev,
|
|||
!(flags & IEEE80211_CONF_CHANGE_CHANNEL))
|
||||
rt2500usb_config_txpower(rt2x00dev,
|
||||
libconf->conf->power_level);
|
||||
if (flags & IEEE80211_CONF_CHANGE_BEACON_INTERVAL)
|
||||
rt2500usb_config_duration(rt2x00dev, libconf);
|
||||
if (flags & IEEE80211_CONF_CHANGE_PS)
|
||||
rt2500usb_config_ps(rt2x00dev, libconf);
|
||||
}
|
||||
|
|
|
@ -616,6 +616,11 @@ static void rt2800usb_config_erp(struct rt2x00_dev *rt2x00dev,
|
|||
rt2x00_set_field32(®, XIFS_TIME_CFG_EIFS, erp->eifs);
|
||||
rt2x00_set_field32(®, XIFS_TIME_CFG_BB_RXEND_ENABLE, 1);
|
||||
rt2x00usb_register_write(rt2x00dev, XIFS_TIME_CFG, reg);
|
||||
|
||||
rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, ®);
|
||||
rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL,
|
||||
erp->beacon_int * 16);
|
||||
rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg);
|
||||
}
|
||||
|
||||
static void rt2800usb_config_ant(struct rt2x00_dev *rt2x00dev,
|
||||
|
@ -955,17 +960,6 @@ static void rt2800usb_config_retry_limit(struct rt2x00_dev *rt2x00dev,
|
|||
rt2x00usb_register_write(rt2x00dev, TX_RTY_CFG, reg);
|
||||
}
|
||||
|
||||
static void rt2800usb_config_duration(struct rt2x00_dev *rt2x00dev,
|
||||
struct rt2x00lib_conf *libconf)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, ®);
|
||||
rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL,
|
||||
libconf->conf->beacon_int * 16);
|
||||
rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg);
|
||||
}
|
||||
|
||||
static void rt2800usb_config_ps(struct rt2x00_dev *rt2x00dev,
|
||||
struct rt2x00lib_conf *libconf)
|
||||
{
|
||||
|
@ -1010,8 +1004,6 @@ static void rt2800usb_config(struct rt2x00_dev *rt2x00dev,
|
|||
rt2800usb_config_txpower(rt2x00dev, libconf->conf->power_level);
|
||||
if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
|
||||
rt2800usb_config_retry_limit(rt2x00dev, libconf);
|
||||
if (flags & IEEE80211_CONF_CHANGE_BEACON_INTERVAL)
|
||||
rt2800usb_config_duration(rt2x00dev, libconf);
|
||||
if (flags & IEEE80211_CONF_CHANGE_PS)
|
||||
rt2800usb_config_ps(rt2x00dev, libconf);
|
||||
}
|
||||
|
@ -2881,8 +2873,6 @@ static const struct rt2x00_ops rt2800usb_ops = {
|
|||
* rt2800usb module information.
|
||||
*/
|
||||
static struct usb_device_id rt2800usb_device_table[] = {
|
||||
/* ??? */
|
||||
{ USB_DEVICE(0x177f, 0x0302), USB_DEVICE_DATA(&rt2800usb_ops) },
|
||||
/* Abocom */
|
||||
{ USB_DEVICE(0x07b8, 0x2870), USB_DEVICE_DATA(&rt2800usb_ops) },
|
||||
{ USB_DEVICE(0x07b8, 0x2770), USB_DEVICE_DATA(&rt2800usb_ops) },
|
||||
|
@ -2912,6 +2902,7 @@ static struct usb_device_id rt2800usb_device_table[] = {
|
|||
{ USB_DEVICE(0x050d, 0x8053), USB_DEVICE_DATA(&rt2800usb_ops) },
|
||||
{ USB_DEVICE(0x050d, 0x805c), USB_DEVICE_DATA(&rt2800usb_ops) },
|
||||
{ USB_DEVICE(0x050d, 0x815c), USB_DEVICE_DATA(&rt2800usb_ops) },
|
||||
{ USB_DEVICE(0x050d, 0x825a), USB_DEVICE_DATA(&rt2800usb_ops) },
|
||||
/* Buffalo */
|
||||
{ USB_DEVICE(0x0411, 0x00e8), USB_DEVICE_DATA(&rt2800usb_ops) },
|
||||
{ USB_DEVICE(0x0411, 0x012e), USB_DEVICE_DATA(&rt2800usb_ops) },
|
||||
|
@ -3026,6 +3017,10 @@ static struct usb_device_id rt2800usb_device_table[] = {
|
|||
{ USB_DEVICE(0x083a, 0xc522), USB_DEVICE_DATA(&rt2800usb_ops) },
|
||||
/* Sparklan */
|
||||
{ USB_DEVICE(0x15a9, 0x0006), USB_DEVICE_DATA(&rt2800usb_ops) },
|
||||
/* Sweex */
|
||||
{ USB_DEVICE(0x177f, 0x0153), USB_DEVICE_DATA(&rt2800usb_ops) },
|
||||
{ USB_DEVICE(0x177f, 0x0302), USB_DEVICE_DATA(&rt2800usb_ops) },
|
||||
{ USB_DEVICE(0x177f, 0x0313), USB_DEVICE_DATA(&rt2800usb_ops) },
|
||||
/* U-Media*/
|
||||
{ USB_DEVICE(0x157e, 0x300e), USB_DEVICE_DATA(&rt2800usb_ops) },
|
||||
/* ZCOM */
|
||||
|
|
|
@ -417,6 +417,8 @@ struct rt2x00lib_erp {
|
|||
short pifs;
|
||||
short difs;
|
||||
short eifs;
|
||||
|
||||
u16 beacon_int;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -106,6 +106,7 @@ void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev,
|
|||
}
|
||||
|
||||
erp.basic_rates = bss_conf->basic_rates;
|
||||
erp.beacon_int = bss_conf->beacon_int;
|
||||
|
||||
rt2x00dev->ops->lib->config_erp(rt2x00dev, &erp);
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key)
|
|||
{
|
||||
switch (key->alg) {
|
||||
case ALG_WEP:
|
||||
if (key->keylen == LEN_WEP40)
|
||||
if (key->keylen == WLAN_KEY_LEN_WEP40)
|
||||
return CIPHER_WEP64;
|
||||
else
|
||||
return CIPHER_WEP128;
|
||||
|
|
|
@ -260,7 +260,8 @@ void rt2x00lib_txdone(struct queue_entry *entry,
|
|||
* Update TX statistics.
|
||||
*/
|
||||
rt2x00dev->link.qual.tx_success +=
|
||||
test_bit(TXDONE_SUCCESS, &txdesc->flags);
|
||||
test_bit(TXDONE_SUCCESS, &txdesc->flags) ||
|
||||
test_bit(TXDONE_UNKNOWN, &txdesc->flags);
|
||||
rt2x00dev->link.qual.tx_failed +=
|
||||
test_bit(TXDONE_FAILURE, &txdesc->flags);
|
||||
|
||||
|
@ -278,14 +279,16 @@ void rt2x00lib_txdone(struct queue_entry *entry,
|
|||
tx_info->status.rates[1].idx = -1; /* terminate */
|
||||
|
||||
if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) {
|
||||
if (test_bit(TXDONE_SUCCESS, &txdesc->flags))
|
||||
if (test_bit(TXDONE_SUCCESS, &txdesc->flags) ||
|
||||
test_bit(TXDONE_UNKNOWN, &txdesc->flags))
|
||||
tx_info->flags |= IEEE80211_TX_STAT_ACK;
|
||||
else if (test_bit(TXDONE_FAILURE, &txdesc->flags))
|
||||
rt2x00dev->low_level_stats.dot11ACKFailureCount++;
|
||||
}
|
||||
|
||||
if (rate_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
|
||||
if (test_bit(TXDONE_SUCCESS, &txdesc->flags))
|
||||
if (test_bit(TXDONE_SUCCESS, &txdesc->flags) ||
|
||||
test_bit(TXDONE_UNKNOWN, &txdesc->flags))
|
||||
rt2x00dev->low_level_stats.dot11RTSSuccessCount++;
|
||||
else if (test_bit(TXDONE_FAILURE, &txdesc->flags))
|
||||
rt2x00dev->low_level_stats.dot11RTSFailureCount++;
|
||||
|
|
|
@ -603,15 +603,22 @@ static void rt61pci_config_erp(struct rt2x00_dev *rt2x00dev,
|
|||
|
||||
rt2x00pci_register_read(rt2x00dev, TXRX_CSR0, ®);
|
||||
rt2x00_set_field32(®, TXRX_CSR0_RX_ACK_TIMEOUT, erp->ack_timeout);
|
||||
rt2x00_set_field32(®, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER);
|
||||
rt2x00pci_register_write(rt2x00dev, TXRX_CSR0, reg);
|
||||
|
||||
rt2x00pci_register_read(rt2x00dev, TXRX_CSR4, ®);
|
||||
rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_ENABLE, 1);
|
||||
rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_PREAMBLE,
|
||||
!!erp->short_preamble);
|
||||
rt2x00pci_register_write(rt2x00dev, TXRX_CSR4, reg);
|
||||
|
||||
rt2x00pci_register_write(rt2x00dev, TXRX_CSR5, erp->basic_rates);
|
||||
|
||||
rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, ®);
|
||||
rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL,
|
||||
erp->beacon_int * 16);
|
||||
rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg);
|
||||
|
||||
rt2x00pci_register_read(rt2x00dev, MAC_CSR9, ®);
|
||||
rt2x00_set_field32(®, MAC_CSR9_SLOT_TIME, erp->slot_time);
|
||||
rt2x00pci_register_write(rt2x00dev, MAC_CSR9, reg);
|
||||
|
@ -938,25 +945,6 @@ static void rt61pci_config_retry_limit(struct rt2x00_dev *rt2x00dev,
|
|||
rt2x00pci_register_write(rt2x00dev, TXRX_CSR4, reg);
|
||||
}
|
||||
|
||||
static void rt61pci_config_duration(struct rt2x00_dev *rt2x00dev,
|
||||
struct rt2x00lib_conf *libconf)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
rt2x00pci_register_read(rt2x00dev, TXRX_CSR0, ®);
|
||||
rt2x00_set_field32(®, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER);
|
||||
rt2x00pci_register_write(rt2x00dev, TXRX_CSR0, reg);
|
||||
|
||||
rt2x00pci_register_read(rt2x00dev, TXRX_CSR4, ®);
|
||||
rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_ENABLE, 1);
|
||||
rt2x00pci_register_write(rt2x00dev, TXRX_CSR4, reg);
|
||||
|
||||
rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, ®);
|
||||
rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL,
|
||||
libconf->conf->beacon_int * 16);
|
||||
rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg);
|
||||
}
|
||||
|
||||
static void rt61pci_config_ps(struct rt2x00_dev *rt2x00dev,
|
||||
struct rt2x00lib_conf *libconf)
|
||||
{
|
||||
|
@ -1016,8 +1004,6 @@ static void rt61pci_config(struct rt2x00_dev *rt2x00dev,
|
|||
rt61pci_config_txpower(rt2x00dev, libconf->conf->power_level);
|
||||
if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
|
||||
rt61pci_config_retry_limit(rt2x00dev, libconf);
|
||||
if (flags & IEEE80211_CONF_CHANGE_BEACON_INTERVAL)
|
||||
rt61pci_config_duration(rt2x00dev, libconf);
|
||||
if (flags & IEEE80211_CONF_CHANGE_PS)
|
||||
rt61pci_config_ps(rt2x00dev, libconf);
|
||||
}
|
||||
|
|
|
@ -566,15 +566,22 @@ static void rt73usb_config_erp(struct rt2x00_dev *rt2x00dev,
|
|||
|
||||
rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®);
|
||||
rt2x00_set_field32(®, TXRX_CSR0_RX_ACK_TIMEOUT, erp->ack_timeout);
|
||||
rt2x00_set_field32(®, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER);
|
||||
rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg);
|
||||
|
||||
rt2x00usb_register_read(rt2x00dev, TXRX_CSR4, ®);
|
||||
rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_ENABLE, 1);
|
||||
rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_PREAMBLE,
|
||||
!!erp->short_preamble);
|
||||
rt2x00usb_register_write(rt2x00dev, TXRX_CSR4, reg);
|
||||
|
||||
rt2x00usb_register_write(rt2x00dev, TXRX_CSR5, erp->basic_rates);
|
||||
|
||||
rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®);
|
||||
rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL,
|
||||
erp->beacon_int * 16);
|
||||
rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg);
|
||||
|
||||
rt2x00usb_register_read(rt2x00dev, MAC_CSR9, ®);
|
||||
rt2x00_set_field32(®, MAC_CSR9_SLOT_TIME, erp->slot_time);
|
||||
rt2x00usb_register_write(rt2x00dev, MAC_CSR9, reg);
|
||||
|
@ -834,25 +841,6 @@ static void rt73usb_config_retry_limit(struct rt2x00_dev *rt2x00dev,
|
|||
rt2x00usb_register_write(rt2x00dev, TXRX_CSR4, reg);
|
||||
}
|
||||
|
||||
static void rt73usb_config_duration(struct rt2x00_dev *rt2x00dev,
|
||||
struct rt2x00lib_conf *libconf)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
rt2x00usb_register_read(rt2x00dev, TXRX_CSR0, ®);
|
||||
rt2x00_set_field32(®, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER);
|
||||
rt2x00usb_register_write(rt2x00dev, TXRX_CSR0, reg);
|
||||
|
||||
rt2x00usb_register_read(rt2x00dev, TXRX_CSR4, ®);
|
||||
rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_ENABLE, 1);
|
||||
rt2x00usb_register_write(rt2x00dev, TXRX_CSR4, reg);
|
||||
|
||||
rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®);
|
||||
rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL,
|
||||
libconf->conf->beacon_int * 16);
|
||||
rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg);
|
||||
}
|
||||
|
||||
static void rt73usb_config_ps(struct rt2x00_dev *rt2x00dev,
|
||||
struct rt2x00lib_conf *libconf)
|
||||
{
|
||||
|
@ -906,8 +894,6 @@ static void rt73usb_config(struct rt2x00_dev *rt2x00dev,
|
|||
rt73usb_config_txpower(rt2x00dev, libconf->conf->power_level);
|
||||
if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
|
||||
rt73usb_config_retry_limit(rt2x00dev, libconf);
|
||||
if (flags & IEEE80211_CONF_CHANGE_BEACON_INTERVAL)
|
||||
rt73usb_config_duration(rt2x00dev, libconf);
|
||||
if (flags & IEEE80211_CONF_CHANGE_PS)
|
||||
rt73usb_config_ps(rt2x00dev, libconf);
|
||||
}
|
||||
|
|
|
@ -2509,7 +2509,7 @@ static void strip_dev_setup(struct net_device *dev)
|
|||
* netdev_priv(dev) Already holds a pointer to our struct strip
|
||||
*/
|
||||
|
||||
*(MetricomAddress *) & dev->broadcast = broadcast_address;
|
||||
*(MetricomAddress *)dev->broadcast = broadcast_address;
|
||||
dev->dev_addr[0] = 0;
|
||||
dev->addr_len = sizeof(MetricomAddress);
|
||||
|
||||
|
|
|
@ -420,9 +420,9 @@ static void cs_set_control(struct zd_mac *mac, struct zd_ctrlset *cs,
|
|||
if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
|
||||
cs->control |= ZD_CS_NEED_RANDOM_BACKOFF;
|
||||
|
||||
/* Multicast */
|
||||
if (is_multicast_ether_addr(header->addr1))
|
||||
cs->control |= ZD_CS_MULTICAST;
|
||||
/* No ACK expected (multicast, etc.) */
|
||||
if (info->flags & IEEE80211_TX_CTL_NO_ACK)
|
||||
cs->control |= ZD_CS_NO_ACK;
|
||||
|
||||
/* PS-POLL */
|
||||
if (ieee80211_is_pspoll(header->frame_control))
|
||||
|
|
|
@ -87,7 +87,7 @@ struct zd_ctrlset {
|
|||
|
||||
/* zd_ctrlset control field */
|
||||
#define ZD_CS_NEED_RANDOM_BACKOFF 0x01
|
||||
#define ZD_CS_MULTICAST 0x02
|
||||
#define ZD_CS_NO_ACK 0x02
|
||||
|
||||
#define ZD_CS_FRAME_TYPE_MASK 0x0c
|
||||
#define ZD_CS_DATA_FRAME 0x00
|
||||
|
|
|
@ -303,14 +303,18 @@ static int agnx_config(struct ieee80211_hw *dev, u32 changed)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int agnx_config_interface(struct ieee80211_hw *dev,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_if_conf *conf)
|
||||
static void agnx_bss_info_changed(struct ieee80211_hw *dev,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *conf,
|
||||
u32 changed)
|
||||
{
|
||||
struct agnx_priv *priv = dev->priv;
|
||||
void __iomem *ctl = priv->ctl;
|
||||
AGNX_TRACE;
|
||||
|
||||
if (!(changed & BSS_CHANGED_BSSID))
|
||||
return;
|
||||
|
||||
spin_lock(&priv->lock);
|
||||
|
||||
if (memcmp(conf->bssid, priv->bssid, ETH_ALEN)) {
|
||||
|
@ -323,8 +327,7 @@ static int agnx_config_interface(struct ieee80211_hw *dev,
|
|||
agnx_write32(ctl, AGNX_BM_MTSM, 0xff & ~0x1);
|
||||
}
|
||||
spin_unlock(&priv->lock);
|
||||
return 0;
|
||||
} /* agnx_config_interface */
|
||||
} /* agnx_bss_info_changed */
|
||||
|
||||
|
||||
static void agnx_configure_filter(struct ieee80211_hw *dev,
|
||||
|
@ -422,7 +425,7 @@ static struct ieee80211_ops agnx_ops = {
|
|||
.add_interface = agnx_add_interface,
|
||||
.remove_interface = agnx_remove_interface,
|
||||
.config = agnx_config,
|
||||
.config_interface = agnx_config_interface,
|
||||
.bss_info_changed = agnx_bss_info_changed,
|
||||
.configure_filter = agnx_configure_filter,
|
||||
.get_stats = agnx_get_stats,
|
||||
.get_tx_stats = agnx_get_tx_stats,
|
||||
|
|
|
@ -493,6 +493,7 @@ struct ieee80211s_hdr {
|
|||
/* Mesh flags */
|
||||
#define MESH_FLAGS_AE_A4 0x1
|
||||
#define MESH_FLAGS_AE_A5_A6 0x2
|
||||
#define MESH_FLAGS_AE 0x3
|
||||
#define MESH_FLAGS_PS_DEEP 0x4
|
||||
|
||||
/**
|
||||
|
@ -1085,6 +1086,14 @@ enum ieee80211_spectrum_mgmt_actioncode {
|
|||
WLAN_ACTION_SPCT_CHL_SWITCH = 4,
|
||||
};
|
||||
|
||||
/* Security key length */
|
||||
enum ieee80211_key_len {
|
||||
WLAN_KEY_LEN_WEP40 = 5,
|
||||
WLAN_KEY_LEN_WEP104 = 13,
|
||||
WLAN_KEY_LEN_CCMP = 16,
|
||||
WLAN_KEY_LEN_TKIP = 32,
|
||||
};
|
||||
|
||||
/*
|
||||
* IEEE 802.11-2007 7.3.2.9 Country information element
|
||||
*
|
||||
|
|
|
@ -56,9 +56,9 @@ enum ieee80211_band {
|
|||
* on this channel.
|
||||
* @IEEE80211_CHAN_NO_IBSS: IBSS is not allowed on this channel.
|
||||
* @IEEE80211_CHAN_RADAR: Radar detection is required on this channel.
|
||||
* @IEEE80211_CHAN_NO_FAT_ABOVE: extension channel above this channel
|
||||
* @IEEE80211_CHAN_NO_HT40PLUS: extension channel above this channel
|
||||
* is not permitted.
|
||||
* @IEEE80211_CHAN_NO_FAT_BELOW: extension channel below this channel
|
||||
* @IEEE80211_CHAN_NO_HT40MINUS: extension channel below this channel
|
||||
* is not permitted.
|
||||
*/
|
||||
enum ieee80211_channel_flags {
|
||||
|
@ -66,10 +66,13 @@ enum ieee80211_channel_flags {
|
|||
IEEE80211_CHAN_PASSIVE_SCAN = 1<<1,
|
||||
IEEE80211_CHAN_NO_IBSS = 1<<2,
|
||||
IEEE80211_CHAN_RADAR = 1<<3,
|
||||
IEEE80211_CHAN_NO_FAT_ABOVE = 1<<4,
|
||||
IEEE80211_CHAN_NO_FAT_BELOW = 1<<5,
|
||||
IEEE80211_CHAN_NO_HT40PLUS = 1<<4,
|
||||
IEEE80211_CHAN_NO_HT40MINUS = 1<<5,
|
||||
};
|
||||
|
||||
#define IEEE80211_CHAN_NO_HT40 \
|
||||
(IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
|
||||
|
||||
/**
|
||||
* struct ieee80211_channel - channel definition
|
||||
*
|
||||
|
@ -778,10 +781,11 @@ enum wiphy_params_flags {
|
|||
* @get_key: get information about the key with the given parameters.
|
||||
* @mac_addr will be %NULL when requesting information for a group
|
||||
* key. All pointers given to the @callback function need not be valid
|
||||
* after it returns.
|
||||
* after it returns. This function should return an error if it is
|
||||
* not possible to retrieve the key, -ENOENT if it doesn't exist.
|
||||
*
|
||||
* @del_key: remove a key given the @mac_addr (%NULL for a group key)
|
||||
* and @key_index
|
||||
* and @key_index, return -ENOENT if the key doesn't exist.
|
||||
*
|
||||
* @set_default_key: set the default key on an interface
|
||||
*
|
||||
|
@ -994,7 +998,7 @@ struct wiphy {
|
|||
* know whether it points to a wiphy your driver has registered
|
||||
* or not. Assign this to something global to your driver to
|
||||
* help determine whether you own this wiphy or not. */
|
||||
void *privid;
|
||||
const void *privid;
|
||||
|
||||
struct ieee80211_supported_band *bands[IEEE80211_NUM_BANDS];
|
||||
|
||||
|
@ -1070,7 +1074,7 @@ static inline const char *wiphy_name(struct wiphy *wiphy)
|
|||
* The returned pointer must be assigned to each netdev's
|
||||
* ieee80211_ptr for proper operation.
|
||||
*/
|
||||
struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv);
|
||||
struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv);
|
||||
|
||||
/**
|
||||
* wiphy_register - register a wiphy with cfg80211
|
||||
|
@ -1240,6 +1244,53 @@ extern int ieee80211_radiotap_iterator_init(
|
|||
extern int ieee80211_radiotap_iterator_next(
|
||||
struct ieee80211_radiotap_iterator *iterator);
|
||||
|
||||
extern const unsigned char rfc1042_header[6];
|
||||
extern const unsigned char bridge_tunnel_header[6];
|
||||
|
||||
/**
|
||||
* ieee80211_get_hdrlen_from_skb - get header length from data
|
||||
*
|
||||
* Given an skb with a raw 802.11 header at the data pointer this function
|
||||
* returns the 802.11 header length in bytes (not including encryption
|
||||
* headers). If the data in the sk_buff is too short to contain a valid 802.11
|
||||
* header the function returns 0.
|
||||
*
|
||||
* @skb: the frame
|
||||
*/
|
||||
unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb);
|
||||
|
||||
/**
|
||||
* ieee80211_hdrlen - get header length in bytes from frame control
|
||||
* @fc: frame control field in little-endian format
|
||||
*/
|
||||
unsigned int ieee80211_hdrlen(__le16 fc);
|
||||
|
||||
/**
|
||||
* ieee80211_data_to_8023 - convert an 802.11 data frame to 802.3
|
||||
* @skb: the 802.11 data frame
|
||||
* @addr: the device MAC address
|
||||
* @iftype: the virtual interface type
|
||||
*/
|
||||
int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr,
|
||||
enum nl80211_iftype iftype);
|
||||
|
||||
/**
|
||||
* ieee80211_data_from_8023 - convert an 802.3 frame to 802.11
|
||||
* @skb: the 802.3 frame
|
||||
* @addr: the device MAC address
|
||||
* @iftype: the virtual interface type
|
||||
* @bssid: the network bssid (used only for iftype STATION and ADHOC)
|
||||
* @qos: build 802.11 QoS data frame
|
||||
*/
|
||||
int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr,
|
||||
enum nl80211_iftype iftype, u8 *bssid, bool qos);
|
||||
|
||||
/**
|
||||
* cfg80211_classify8021d - determine the 802.1p/1d tag for a data frame
|
||||
* @skb: the data frame
|
||||
*/
|
||||
unsigned int cfg80211_classify8021d(struct sk_buff *skb);
|
||||
|
||||
/*
|
||||
* Regulatory helper functions for wiphys
|
||||
*/
|
||||
|
@ -1303,9 +1354,10 @@ extern void wiphy_apply_custom_regulatory(
|
|||
* freq_reg_info - get regulatory information for the given frequency
|
||||
* @wiphy: the wiphy for which we want to process this rule for
|
||||
* @center_freq: Frequency in KHz for which we want regulatory information for
|
||||
* @bandwidth: the bandwidth requirement you have in KHz, if you do not have one
|
||||
* you can set this to 0. If this frequency is allowed we then set
|
||||
* this value to the maximum allowed bandwidth.
|
||||
* @desired_bw_khz: the desired max bandwidth you want to use per
|
||||
* channel. Note that this is still 20 MHz if you want to use HT40
|
||||
* as HT40 makes use of two channels for its 40 MHz width bandwidth.
|
||||
* If set to 0 we'll assume you want the standard 20 MHz.
|
||||
* @reg_rule: the regulatory rule which we have for this frequency
|
||||
*
|
||||
* Use this function to get the regulatory rule for a specific frequency on
|
||||
|
@ -1320,7 +1372,9 @@ extern void wiphy_apply_custom_regulatory(
|
|||
* freq_in_rule_band() for our current definition of a band -- this is purely
|
||||
* subjective and right now its 802.11 specific.
|
||||
*/
|
||||
extern int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 *bandwidth,
|
||||
extern int freq_reg_info(struct wiphy *wiphy,
|
||||
u32 center_freq,
|
||||
u32 desired_bw_khz,
|
||||
const struct ieee80211_reg_rule **reg_rule);
|
||||
|
||||
/*
|
||||
|
|
|
@ -173,7 +173,6 @@ enum ieee80211_bss_change {
|
|||
* @timestamp: beacon timestamp
|
||||
* @beacon_int: beacon interval
|
||||
* @assoc_capability: capabilities taken from assoc resp
|
||||
* @ht: BSS's HT configuration
|
||||
* @basic_rates: bitmap of basic rates, each bit stands for an
|
||||
* index into the rate table configured by the driver in
|
||||
* the current band.
|
||||
|
@ -672,16 +671,6 @@ enum ieee80211_key_alg {
|
|||
ALG_AES_CMAC,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum ieee80211_key_len - key length
|
||||
* @LEN_WEP40: WEP 5-byte long key
|
||||
* @LEN_WEP104: WEP 13-byte long key
|
||||
*/
|
||||
enum ieee80211_key_len {
|
||||
LEN_WEP40 = 5,
|
||||
LEN_WEP104 = 13,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum ieee80211_key_flags - key flags
|
||||
*
|
||||
|
@ -1812,24 +1801,6 @@ __le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw,
|
|||
struct sk_buff *
|
||||
ieee80211_get_buffered_bc(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
|
||||
|
||||
/**
|
||||
* ieee80211_get_hdrlen_from_skb - get header length from data
|
||||
*
|
||||
* Given an skb with a raw 802.11 header at the data pointer this function
|
||||
* returns the 802.11 header length in bytes (not including encryption
|
||||
* headers). If the data in the sk_buff is too short to contain a valid 802.11
|
||||
* header the function returns 0.
|
||||
*
|
||||
* @skb: the frame
|
||||
*/
|
||||
unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb);
|
||||
|
||||
/**
|
||||
* ieee80211_hdrlen - get header length in bytes from frame control
|
||||
* @fc: frame control field in little-endian format
|
||||
*/
|
||||
unsigned int ieee80211_hdrlen(__le16 fc);
|
||||
|
||||
/**
|
||||
* ieee80211_get_tkip_key - get a TKIP rc4 for skb
|
||||
*
|
||||
|
|
|
@ -52,14 +52,6 @@ static const struct file_operations name## _ops = { \
|
|||
|
||||
DEBUGFS_READONLY_FILE(frequency, 20, "%d",
|
||||
local->hw.conf.channel->center_freq);
|
||||
DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d",
|
||||
local->hw.wiphy->rts_threshold);
|
||||
DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d",
|
||||
local->hw.wiphy->frag_threshold);
|
||||
DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d",
|
||||
local->hw.wiphy->retry_short);
|
||||
DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d",
|
||||
local->hw.wiphy->retry_long);
|
||||
DEBUGFS_READONLY_FILE(total_ps_buffered, 20, "%d",
|
||||
local->total_ps_buffered);
|
||||
DEBUGFS_READONLY_FILE(wep_iv, 20, "%#08x",
|
||||
|
@ -303,10 +295,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
|
|||
local->debugfs.keys = debugfs_create_dir("keys", phyd);
|
||||
|
||||
DEBUGFS_ADD(frequency);
|
||||
DEBUGFS_ADD(rts_threshold);
|
||||
DEBUGFS_ADD(fragmentation_threshold);
|
||||
DEBUGFS_ADD(short_retry_limit);
|
||||
DEBUGFS_ADD(long_retry_limit);
|
||||
DEBUGFS_ADD(total_ps_buffered);
|
||||
DEBUGFS_ADD(wep_iv);
|
||||
DEBUGFS_ADD(tsf);
|
||||
|
@ -359,10 +347,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
|
|||
void debugfs_hw_del(struct ieee80211_local *local)
|
||||
{
|
||||
DEBUGFS_DEL(frequency);
|
||||
DEBUGFS_DEL(rts_threshold);
|
||||
DEBUGFS_DEL(fragmentation_threshold);
|
||||
DEBUGFS_DEL(short_retry_limit);
|
||||
DEBUGFS_DEL(long_retry_limit);
|
||||
DEBUGFS_DEL(total_ps_buffered);
|
||||
DEBUGFS_DEL(wep_iv);
|
||||
DEBUGFS_DEL(tsf);
|
||||
|
|
|
@ -535,9 +535,9 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
|
|||
bssid = ifibss->bssid;
|
||||
bss = (void *)cfg80211_get_bss(local->hw.wiphy, chan, bssid,
|
||||
ifibss->ssid, ifibss->ssid_len,
|
||||
capability,
|
||||
WLAN_CAPABILITY_IBSS |
|
||||
WLAN_CAPABILITY_PRIVACY);
|
||||
WLAN_CAPABILITY_PRIVACY,
|
||||
capability);
|
||||
|
||||
#ifdef CONFIG_MAC80211_IBSS_DEBUG
|
||||
if (bss)
|
||||
|
@ -737,6 +737,9 @@ static void ieee80211_ibss_work(struct work_struct *work)
|
|||
struct ieee80211_if_ibss *ifibss;
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (WARN_ON(local->suspended))
|
||||
return;
|
||||
|
||||
if (!netif_running(sdata->dev))
|
||||
return;
|
||||
|
||||
|
@ -773,10 +776,36 @@ static void ieee80211_ibss_timer(unsigned long data)
|
|||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
|
||||
if (local->quiescing) {
|
||||
ifibss->timer_running = true;
|
||||
return;
|
||||
}
|
||||
|
||||
set_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request);
|
||||
queue_work(local->hw.workqueue, &ifibss->work);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
|
||||
cancel_work_sync(&ifibss->work);
|
||||
if (del_timer_sync(&ifibss->timer))
|
||||
ifibss->timer_running = true;
|
||||
}
|
||||
|
||||
void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
|
||||
if (ifibss->timer_running) {
|
||||
add_timer(&ifibss->timer);
|
||||
ifibss->timer_running = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
|
|
|
@ -293,6 +293,7 @@ struct ieee80211_if_managed {
|
|||
int auth_tries; /* retries for auth req */
|
||||
int assoc_tries; /* retries for assoc req */
|
||||
|
||||
unsigned long timers_running; /* used for quiesce/restart */
|
||||
bool powersave; /* powersave requested for this iface */
|
||||
|
||||
unsigned long request;
|
||||
|
@ -333,6 +334,9 @@ struct ieee80211_if_ibss {
|
|||
|
||||
unsigned long request;
|
||||
unsigned long last_scan_completed;
|
||||
|
||||
bool timer_running;
|
||||
|
||||
bool fixed_bssid;
|
||||
bool fixed_channel;
|
||||
|
||||
|
@ -358,6 +362,8 @@ struct ieee80211_if_mesh {
|
|||
struct timer_list mesh_path_timer;
|
||||
struct sk_buff_head skb_queue;
|
||||
|
||||
unsigned long timers_running;
|
||||
|
||||
bool housekeeping;
|
||||
|
||||
u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN];
|
||||
|
@ -609,6 +615,21 @@ struct ieee80211_local {
|
|||
unsigned int filter_flags; /* FIF_* */
|
||||
struct iw_statistics wstats;
|
||||
bool tim_in_locked_section; /* see ieee80211_beacon_get() */
|
||||
|
||||
/*
|
||||
* suspended is true if we finished all the suspend _and_ we have
|
||||
* not yet come up from resume. This is to be used by mac80211
|
||||
* to ensure driver sanity during suspend and mac80211's own
|
||||
* sanity. It can eventually be used for WoW as well.
|
||||
*/
|
||||
bool suspended;
|
||||
|
||||
/*
|
||||
* quiescing is true during the suspend process _only_ to
|
||||
* ease timer cancelling etc.
|
||||
*/
|
||||
bool quiescing;
|
||||
|
||||
int tx_headroom; /* required headroom for hardware/radiotap */
|
||||
|
||||
/* Tasklet and skb queue to process calls from IRQ mode. All frames
|
||||
|
@ -758,10 +779,6 @@ struct ieee80211_local {
|
|||
struct dentry *rcdir;
|
||||
struct dentry *rcname;
|
||||
struct dentry *frequency;
|
||||
struct dentry *rts_threshold;
|
||||
struct dentry *fragmentation_threshold;
|
||||
struct dentry *short_retry_limit;
|
||||
struct dentry *long_retry_limit;
|
||||
struct dentry *total_ps_buffered;
|
||||
struct dentry *wep_iv;
|
||||
struct dentry *tsf;
|
||||
|
@ -938,6 +955,11 @@ void ieee80211_send_pspoll(struct ieee80211_local *local,
|
|||
void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency);
|
||||
int ieee80211_max_network_latency(struct notifier_block *nb,
|
||||
unsigned long data, void *dummy);
|
||||
void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_channel_sw_ie *sw_elem,
|
||||
struct ieee80211_bss *bss);
|
||||
void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
|
||||
|
||||
/* IBSS code */
|
||||
void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
|
||||
|
@ -950,6 +972,8 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
|
|||
int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_ibss_params *params);
|
||||
int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_ibss_quiesce(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_ibss_restart(struct ieee80211_sub_if_data *sdata);
|
||||
|
||||
/* scan/BSS handling */
|
||||
void ieee80211_scan_work(struct work_struct *work);
|
||||
|
@ -960,6 +984,7 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
|
|||
int ieee80211_scan_results(struct ieee80211_local *local,
|
||||
struct iw_request_info *info,
|
||||
char *buf, size_t len);
|
||||
void ieee80211_scan_cancel(struct ieee80211_local *local);
|
||||
ieee80211_rx_result
|
||||
ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb,
|
||||
|
@ -1035,14 +1060,6 @@ int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
|
|||
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
size_t len);
|
||||
void ieee80211_chswitch_timer(unsigned long data);
|
||||
void ieee80211_chswitch_work(struct work_struct *work);
|
||||
void ieee80211_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_channel_sw_ie *sw_elem,
|
||||
struct ieee80211_bss *bss);
|
||||
void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
|
||||
u16 capab_info, u8 *pwr_constr_elem,
|
||||
u8 pwr_constr_elem_len);
|
||||
|
||||
/* Suspend/resume and hw reconfiguration */
|
||||
int ieee80211_reconfig(struct ieee80211_local *local);
|
||||
|
@ -1068,8 +1085,6 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw)
|
|||
|
||||
/* utility functions/constants */
|
||||
extern void *mac80211_wiphy_privid; /* for wiphy privid */
|
||||
extern const unsigned char rfc1042_header[6];
|
||||
extern const unsigned char bridge_tunnel_header[6];
|
||||
u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
|
||||
enum nl80211_iftype type);
|
||||
int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,
|
||||
|
|
|
@ -320,7 +320,7 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg,
|
|||
case ALG_TKIP:
|
||||
key->conf.iv_len = TKIP_IV_LEN;
|
||||
key->conf.icv_len = TKIP_ICV_LEN;
|
||||
if (seq && seq_len == 6) {
|
||||
if (seq) {
|
||||
for (i = 0; i < NUM_RX_DATA_QUEUES; i++) {
|
||||
key->u.tkip.rx[i].iv32 =
|
||||
get_unaligned_le32(&seq[2]);
|
||||
|
@ -332,7 +332,7 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg,
|
|||
case ALG_CCMP:
|
||||
key->conf.iv_len = CCMP_HDR_LEN;
|
||||
key->conf.icv_len = CCMP_MIC_LEN;
|
||||
if (seq && seq_len == CCMP_PN_LEN) {
|
||||
if (seq) {
|
||||
for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
|
||||
for (j = 0; j < CCMP_PN_LEN; j++)
|
||||
key->u.ccmp.rx_pn[i][j] =
|
||||
|
@ -342,7 +342,7 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg,
|
|||
case ALG_AES_CMAC:
|
||||
key->conf.iv_len = 0;
|
||||
key->conf.icv_len = sizeof(struct ieee80211_mmie);
|
||||
if (seq && seq_len == 6)
|
||||
if (seq)
|
||||
for (j = 0; j < 6; j++)
|
||||
key->u.aes_cmac.rx_pn[j] = seq[6 - j - 1];
|
||||
break;
|
||||
|
|
|
@ -219,18 +219,26 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
|
|||
u32 changed)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
static const u8 zero[ETH_ALEN] = { 0 };
|
||||
|
||||
if (!changed)
|
||||
return;
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION)
|
||||
sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
|
||||
else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
|
||||
/*
|
||||
* While not associated, claim a BSSID of all-zeroes
|
||||
* so that drivers don't do any weird things with the
|
||||
* BSSID at that time.
|
||||
*/
|
||||
if (sdata->vif.bss_conf.assoc)
|
||||
sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
|
||||
else
|
||||
sdata->vif.bss_conf.bssid = zero;
|
||||
} else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
|
||||
sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;
|
||||
else if (sdata->vif.type == NL80211_IFTYPE_AP)
|
||||
sdata->vif.bss_conf.bssid = sdata->dev->dev_addr;
|
||||
else if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
||||
static const u8 zero[ETH_ALEN] = { 0 };
|
||||
sdata->vif.bss_conf.bssid = zero;
|
||||
} else {
|
||||
WARN_ON(1);
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
#define CAPAB_OFFSET 17
|
||||
#define ACCEPT_PLINKS 0x80
|
||||
|
||||
#define TMR_RUNNING_HK 0
|
||||
#define TMR_RUNNING_MP 1
|
||||
|
||||
int mesh_allocated;
|
||||
static struct kmem_cache *rm_cache;
|
||||
|
||||
|
@ -45,6 +48,12 @@ static void ieee80211_mesh_housekeeping_timer(unsigned long data)
|
|||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
|
||||
ifmsh->housekeeping = true;
|
||||
|
||||
if (local->quiescing) {
|
||||
set_bit(TMR_RUNNING_HK, &ifmsh->timers_running);
|
||||
return;
|
||||
}
|
||||
|
||||
queue_work(local->hw.workqueue, &ifmsh->work);
|
||||
}
|
||||
|
||||
|
@ -343,6 +352,11 @@ static void ieee80211_mesh_path_timer(unsigned long data)
|
|||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
|
||||
if (local->quiescing) {
|
||||
set_bit(TMR_RUNNING_MP, &ifmsh->timers_running);
|
||||
return;
|
||||
}
|
||||
|
||||
queue_work(local->hw.workqueue, &ifmsh->work);
|
||||
}
|
||||
|
||||
|
@ -424,6 +438,32 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata,
|
|||
round_jiffies(jiffies + IEEE80211_MESH_HOUSEKEEPING_INTERVAL));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
|
||||
/* might restart the timer but that doesn't matter */
|
||||
cancel_work_sync(&ifmsh->work);
|
||||
|
||||
/* use atomic bitops in case both timers fire at the same time */
|
||||
|
||||
if (del_timer_sync(&ifmsh->housekeeping_timer))
|
||||
set_bit(TMR_RUNNING_HK, &ifmsh->timers_running);
|
||||
if (del_timer_sync(&ifmsh->mesh_path_timer))
|
||||
set_bit(TMR_RUNNING_MP, &ifmsh->timers_running);
|
||||
}
|
||||
|
||||
void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
|
||||
if (test_and_clear_bit(TMR_RUNNING_HK, &ifmsh->timers_running))
|
||||
add_timer(&ifmsh->housekeeping_timer);
|
||||
if (test_and_clear_bit(TMR_RUNNING_MP, &ifmsh->timers_running))
|
||||
add_timer(&ifmsh->mesh_path_timer);
|
||||
}
|
||||
#endif
|
||||
|
||||
void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
|
|
|
@ -191,12 +191,8 @@ struct mesh_rmc {
|
|||
#define PLINK_CATEGORY 30
|
||||
#define MESH_PATH_SEL_CATEGORY 32
|
||||
|
||||
/* Mesh Header Flags */
|
||||
#define IEEE80211S_FLAGS_AE 0x3
|
||||
|
||||
/* Public interfaces */
|
||||
/* Various */
|
||||
int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr);
|
||||
int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
|
||||
struct ieee80211_sub_if_data *sdata);
|
||||
int mesh_rmc_check(u8 *addr, struct ieee80211s_hdr *mesh_hdr,
|
||||
|
@ -267,6 +263,8 @@ void mesh_path_timer(unsigned long data);
|
|||
void mesh_path_flush_by_nexthop(struct sta_info *sta);
|
||||
void mesh_path_discard_frame(struct sk_buff *skb,
|
||||
struct ieee80211_sub_if_data *sdata);
|
||||
void mesh_path_quiesce(struct ieee80211_sub_if_data *sdata);
|
||||
void mesh_path_restart(struct ieee80211_sub_if_data *sdata);
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
extern int mesh_allocated;
|
||||
|
@ -294,10 +292,20 @@ static inline void mesh_path_activate(struct mesh_path *mpath)
|
|||
|
||||
void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local);
|
||||
|
||||
void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata);
|
||||
void mesh_plink_quiesce(struct sta_info *sta);
|
||||
void mesh_plink_restart(struct sta_info *sta);
|
||||
#else
|
||||
#define mesh_allocated 0
|
||||
static inline void
|
||||
ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) {}
|
||||
static inline void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata)
|
||||
{}
|
||||
static inline void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata)
|
||||
{}
|
||||
static inline void mesh_plink_quiesce(struct sta_info *sta) {}
|
||||
static inline void mesh_plink_restart(struct sta_info *sta) {}
|
||||
#endif
|
||||
|
||||
#endif /* IEEE80211S_H */
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue