#include <linux/nl80211.h>
#include "ath9k.h"
-#define ATH_PCI_VERSION "0.1"
-
static char *dev_info = "ath9k";
MODULE_AUTHOR("Atheros Communications");
#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
static void ath_setup_rates(struct ath_softc *sc, enum ieee80211_band band)
{
- struct ath_rate_table *rate_table = NULL;
+ const struct ath_rate_table *rate_table = NULL;
struct ieee80211_supported_band *sband;
struct ieee80211_rate *rate;
int i, maxrates;
}
}
+static struct ath9k_channel *ath_get_curchannel(struct ath_softc *sc,
+ struct ieee80211_hw *hw)
+{
+ struct ieee80211_channel *curchan = hw->conf.channel;
+ struct ath9k_channel *channel;
+ u8 chan_idx;
+
+ chan_idx = curchan->hw_value;
+ channel = &sc->sc_ah->channels[chan_idx];
+ ath9k_update_ichannel(sc, hw, channel);
+ return channel;
+}
+
/*
* Set/change channels. If the channel is really being changed, it's done
* by reseting the chip. To accomplish this we must first cleanup any pending
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;
+ goto ps_restore;
}
spin_unlock_bh(&sc->sc_resetlock);
if (ath_startrecv(sc) != 0) {
DPRINTF(sc, ATH_DBG_FATAL,
"Unable to restart recv logic\n");
- return -EIO;
+ r = -EIO;
+ goto ps_restore;
}
ath_cache_conf_rate(sc, &hw->conf);
ath_update_txpow(sc);
ath9k_hw_set_interrupts(ah, sc->imask);
+
+ ps_restore:
ath9k_ps_restore(sc);
- return 0;
+ return r;
}
/*
* don't calibrate when we're scanning.
* we are most likely not on our home channel.
*/
+ spin_lock(&sc->ani_lock);
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;
if (longcal || shortcal || aniflag) {
/* Call ANI routine if necessary */
if (aniflag)
- ath9k_hw_ani_monitor(ah, &sc->nodestats, ah->curchan);
+ ath9k_hw_ani_monitor(ah, ah->curchan);
/* Perform calibration if necessary */
if (longcal || shortcal) {
- bool iscaldone = false;
-
- if (ath9k_hw_calibrate(ah, ah->curchan,
- sc->rx_chainmask, longcal,
- &iscaldone)) {
- if (longcal)
- sc->ani.noise_floor =
- ath9k_hw_getchan_noise(ah,
- ah->curchan);
-
- DPRINTF(sc, ATH_DBG_ANI,
- "calibrate chan %u/%x nf: %d\n",
- ah->curchan->channel,
- ah->curchan->channelFlags,
- sc->ani.noise_floor);
- } else {
- DPRINTF(sc, ATH_DBG_ANY,
- "calibrate chan %u/%x failed\n",
- ah->curchan->channel,
- ah->curchan->channelFlags);
- }
- sc->ani.caldone = iscaldone;
+ sc->ani.caldone = ath9k_hw_calibrate(ah, ah->curchan,
+ sc->rx_chainmask, longcal);
+
+ if (longcal)
+ sc->ani.noise_floor = ath9k_hw_getchan_noise(ah,
+ ah->curchan);
+
+ DPRINTF(sc, ATH_DBG_ANI," calibrate chan %u/%x nf: %d\n",
+ ah->curchan->channel, ah->curchan->channelFlags,
+ sc->ani.noise_floor);
}
}
+ ath9k_ps_restore(sc);
+
set_timer:
+ spin_unlock(&sc->ani_lock);
/*
* Set timer interval based on previous results.
* The interval must be the shortest necessary to satisfy ANI,
mod_timer(&sc->ani.timer, jiffies + msecs_to_jiffies(cal_interval));
}
+static void ath_start_ani(struct ath_softc *sc)
+{
+ unsigned long timestamp = jiffies_to_msecs(jiffies);
+
+ sc->ani.longcal_timer = timestamp;
+ sc->ani.shortcal_timer = timestamp;
+ sc->ani.checkani_timer = timestamp;
+
+ mod_timer(&sc->ani.timer,
+ jiffies + msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
+}
+
/*
* Update tx/rx chainmask. For legacy association,
* hard code chainmask to 1x1, for 11n association, use
*/
void ath_update_chainmask(struct ath_softc *sc, int is_ht)
{
- if (is_ht ||
- (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BT_COEX)) {
+ if ((sc->sc_flags & SC_OP_SCANNING) || is_ht ||
+ (sc->btcoex_info.btcoex_scheme != ATH_BTCOEX_CFG_NONE)) {
sc->tx_chainmask = sc->sc_ah->caps.tx_chainmask;
sc->rx_chainmask = sc->sc_ah->caps.rx_chainmask;
} else {
if (sc->sc_flags & SC_OP_TXAGGR) {
ath_tx_node_init(sc, an);
- an->maxampdu = 1 << (IEEE80211_HTCAP_MAXRXAMPDU_FACTOR +
+ an->maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
sta->ht_cap.ampdu_factor);
an->mpdudensity = parse_mpdudensity(sta->ht_cap.ampdu_density);
+ an->last_rssi = ATH_RSSI_DUMMY_MARKER;
}
}
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;
}
if (status & ATH9K_INT_TX)
ath_tx_tasklet(sc);
+ if ((status & ATH9K_INT_TSFOOR) && sc->ps_enabled) {
+ /*
+ * 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;
+ }
+
+ if (sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_3WIRE)
+ if (status & ATH9K_INT_GENTIMER)
+ ath_gen_timer_isr(sc->sc_ah);
+
/* 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)
ATH9K_INT_TX | \
ATH9K_INT_BMISS | \
ATH9K_INT_CST | \
- ATH9K_INT_TSFOOR)
+ ATH9K_INT_TSFOOR | \
+ ATH9K_INT_GENTIMER)
struct ath_softc *sc = dev;
struct ath_hw *ah = sc->sc_ah;
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
* 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;
* it will clear whatever condition caused
* the interrupt.
*/
- ath9k_hw_procmibevent(ah, &sc->nodestats);
+ ath9k_hw_procmibevent(ah);
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) {
static void setup_ht_cap(struct ath_softc *sc,
struct ieee80211_sta_ht_cap *ht_info)
{
-#define ATH9K_HT_CAP_MAXRXAMPDU_65536 0x3 /* 2 ^ 16 */
-#define ATH9K_HT_CAP_MPDUDENSITY_8 0x6 /* 8 usec */
+ u8 tx_streams, rx_streams;
ht_info->ht_supported = true;
ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_SGI_40 |
IEEE80211_HT_CAP_DSSSCCK40;
- ht_info->ampdu_factor = ATH9K_HT_CAP_MAXRXAMPDU_65536;
- ht_info->ampdu_density = ATH9K_HT_CAP_MPDUDENSITY_8;
+ ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+ ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8;
/* set up supported mcs set */
memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
+ tx_streams = !(sc->tx_chainmask & (sc->tx_chainmask - 1)) ? 1 : 2;
+ rx_streams = !(sc->rx_chainmask & (sc->rx_chainmask - 1)) ? 1 : 2;
- switch(sc->rx_chainmask) {
- case 1:
- ht_info->mcs.rx_mask[0] = 0xff;
- break;
- case 3:
- case 5:
- case 7:
- default:
- ht_info->mcs.rx_mask[0] = 0xff;
- ht_info->mcs.rx_mask[1] = 0xff;
- break;
+ if (tx_streams != rx_streams) {
+ DPRINTF(sc, ATH_DBG_CONFIG, "TX streams %d, RX streams: %d\n",
+ tx_streams, rx_streams);
+ ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
+ ht_info->mcs.tx_params |= ((tx_streams - 1) <<
+ IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
}
- ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+ ht_info->mcs.rx_mask[0] = 0xff;
+ if (rx_streams >= 2)
+ ht_info->mcs.rx_mask[1] = 0xff;
+
+ ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
}
static void ath9k_bss_assoc_info(struct ath_softc *sc,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf)
{
- struct ath_vif *avp = (void *)vif->drv_priv;
if (bss_conf->assoc) {
DPRINTF(sc, ATH_DBG_CONFIG, "Bss Info ASSOC %d, bssid: %pM\n",
bss_conf->aid, sc->curbssid);
/* New association, store aid */
- if (avp->av_opmode == NL80211_IFTYPE_STATION) {
- sc->curaid = bss_conf->aid;
- ath9k_hw_write_associd(sc);
- }
+ 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 */
ath_beacon_config(sc, vif);
/* Reset rssi stats */
- sc->nodestats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER;
- sc->nodestats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER;
- sc->nodestats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER;
- sc->nodestats.ns_avgtxrate = ATH_RATE_DUMMY_MARKER;
-
- /* Start ANI */
- mod_timer(&sc->ani.timer,
- jiffies + msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
+ sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
+
+ ath_start_ani(sc);
} else {
DPRINTF(sc, ATH_DBG_CONFIG, "Bss Info DISASSOC\n");
sc->curaid = 0;
+ /* Stop ANI */
+ del_timer_sync(&sc->ani.timer);
}
}
if ((sc->led_on_duration == ATH_LED_ON_DURATION_IDLE) ||
(sc->led_off_duration == ATH_LED_OFF_DURATION_IDLE))
- ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 0);
+ ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0);
else
- ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN,
+ ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin,
(sc->sc_flags & SC_OP_LED_ON) ? 1 : 0);
- queue_delayed_work(sc->hw->workqueue, &sc->ath_led_blink_work,
- (sc->sc_flags & SC_OP_LED_ON) ?
- msecs_to_jiffies(sc->led_off_duration) :
- msecs_to_jiffies(sc->led_on_duration));
+ ieee80211_queue_delayed_work(sc->hw,
+ &sc->ath_led_blink_work,
+ (sc->sc_flags & SC_OP_LED_ON) ?
+ msecs_to_jiffies(sc->led_off_duration) :
+ msecs_to_jiffies(sc->led_on_duration));
sc->led_on_duration = sc->led_on_cnt ?
max((ATH_LED_ON_DURATION_IDLE - sc->led_on_cnt), 25) :
case LED_OFF:
if (led->led_type == ATH_LED_ASSOC ||
led->led_type == ATH_LED_RADIO) {
- ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN,
+ ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin,
(led->led_type == ATH_LED_RADIO));
sc->sc_flags &= ~SC_OP_LED_ASSOCIATED;
if (led->led_type == ATH_LED_RADIO)
case LED_FULL:
if (led->led_type == ATH_LED_ASSOC) {
sc->sc_flags |= SC_OP_LED_ASSOCIATED;
- queue_delayed_work(sc->hw->workqueue,
- &sc->ath_led_blink_work, 0);
+ ieee80211_queue_delayed_work(sc->hw,
+ &sc->ath_led_blink_work, 0);
} else if (led->led_type == ATH_LED_RADIO) {
- ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 0);
+ ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0);
sc->sc_flags |= SC_OP_LED_ON;
} else {
sc->led_on_cnt++;
static void ath_deinit_leds(struct ath_softc *sc)
{
- cancel_delayed_work_sync(&sc->ath_led_blink_work);
ath_unregister_led(&sc->assoc_led);
sc->sc_flags &= ~SC_OP_LED_ASSOCIATED;
ath_unregister_led(&sc->tx_led);
ath_unregister_led(&sc->rx_led);
ath_unregister_led(&sc->radio_led);
- ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
+ ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1);
}
static void ath_init_leds(struct ath_softc *sc)
char *trigger;
int ret;
+ if (AR_SREV_9287(sc->sc_ah))
+ sc->sc_ah->led_pin = ATH_LED_PIN_9287;
+ else
+ sc->sc_ah->led_pin = ATH_LED_PIN_DEF;
+
/* Configure gpio 1 for output */
- ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN,
+ ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
/* LED off, active low */
- ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
+ ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1);
INIT_DELAYED_WORK(&sc->ath_led_blink_work, ath_led_blink_work);
return;
fail:
+ cancel_delayed_work_sync(&sc->ath_led_blink_work);
ath_deinit_leds(sc);
}
int r;
ath9k_ps_wakeup(sc);
- spin_lock_bh(&sc->sc_resetlock);
+ ath9k_hw_configpcipowersave(ah, 0, 0);
- r = ath9k_hw_reset(ah, ah->curchan, false);
+ if (!ah->curchan)
+ ah->curchan = ath_get_curchannel(sc, sc->hw);
+ spin_lock_bh(&sc->sc_resetlock);
+ r = ath9k_hw_reset(ah, ah->curchan, false);
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);
ath9k_hw_set_interrupts(ah, sc->imask);
/* Enable LED */
- ath9k_hw_cfg_output(ah, ATH_LED_PIN,
+ ath9k_hw_cfg_output(ah, ah->led_pin,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
- ath9k_hw_set_gpio(ah, ATH_LED_PIN, 0);
+ ath9k_hw_set_gpio(ah, ah->led_pin, 0);
ieee80211_wake_queues(sc->hw);
ath9k_ps_restore(sc);
ieee80211_stop_queues(sc->hw);
/* Disable LED */
- ath9k_hw_set_gpio(ah, ATH_LED_PIN, 1);
- ath9k_hw_cfg_gpio_input(ah, ATH_LED_PIN);
+ ath9k_hw_set_gpio(ah, ah->led_pin, 1);
+ ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
/* Disable interrupts */
ath9k_hw_set_interrupts(ah, 0);
ath_stoprecv(sc); /* turn off frame recv */
ath_flushrecv(sc); /* flush recv queue */
+ if (!ah->curchan)
+ ah->curchan = ath_get_curchannel(sc, sc->hw);
+
spin_lock_bh(&sc->sc_resetlock);
r = ath9k_hw_reset(ah, ah->curchan, false);
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);
ath9k_hw_phy_disable(ah);
- ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP);
+ ath9k_hw_configpcipowersave(ah, 1, 1);
ath9k_ps_restore(sc);
+ ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP);
}
-#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
-
/*******************/
/* Rfkill */
/*******************/
ah->rfkill_polarity;
}
-/* h/w rfkill poll function */
-static void ath_rfkill_poll(struct work_struct *work)
-{
- struct ath_softc *sc = container_of(work, struct ath_softc,
- rf_kill.rfkill_poll.work);
- bool radio_on;
-
- if (sc->sc_flags & SC_OP_INVALID)
- return;
-
- radio_on = !ath_is_rfkill_set(sc);
-
- /*
- * enable/disable radio only when there is a
- * state change in RF switch
- */
- if (radio_on == !!(sc->sc_flags & SC_OP_RFKILL_HW_BLOCKED)) {
- enum rfkill_state state;
-
- if (sc->sc_flags & SC_OP_RFKILL_SW_BLOCKED) {
- state = radio_on ? RFKILL_STATE_SOFT_BLOCKED
- : RFKILL_STATE_HARD_BLOCKED;
- } else if (radio_on) {
- ath_radio_enable(sc);
- state = RFKILL_STATE_UNBLOCKED;
- } else {
- ath_radio_disable(sc);
- state = RFKILL_STATE_HARD_BLOCKED;
- }
-
- if (state == RFKILL_STATE_HARD_BLOCKED)
- sc->sc_flags |= SC_OP_RFKILL_HW_BLOCKED;
- else
- sc->sc_flags &= ~SC_OP_RFKILL_HW_BLOCKED;
-
- rfkill_force_state(sc->rf_kill.rfkill, state);
- }
-
- queue_delayed_work(sc->hw->workqueue, &sc->rf_kill.rfkill_poll,
- msecs_to_jiffies(ATH_RFKILL_POLL_INTERVAL));
-}
-
-/* s/w rfkill handler */
-static int ath_sw_toggle_radio(void *data, enum rfkill_state state)
-{
- struct ath_softc *sc = data;
-
- switch (state) {
- case RFKILL_STATE_SOFT_BLOCKED:
- if (!(sc->sc_flags & (SC_OP_RFKILL_HW_BLOCKED |
- SC_OP_RFKILL_SW_BLOCKED)))
- ath_radio_disable(sc);
- sc->sc_flags |= SC_OP_RFKILL_SW_BLOCKED;
- return 0;
- case RFKILL_STATE_UNBLOCKED:
- if ((sc->sc_flags & SC_OP_RFKILL_SW_BLOCKED)) {
- sc->sc_flags &= ~SC_OP_RFKILL_SW_BLOCKED;
- if (sc->sc_flags & SC_OP_RFKILL_HW_BLOCKED) {
- DPRINTF(sc, ATH_DBG_FATAL, "Can't turn on the"
- "radio as it is disabled by h/w\n");
- return -EPERM;
- }
- ath_radio_enable(sc);
- }
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-/* Init s/w rfkill */
-static int ath_init_sw_rfkill(struct ath_softc *sc)
-{
- sc->rf_kill.rfkill = rfkill_allocate(wiphy_dev(sc->hw->wiphy),
- RFKILL_TYPE_WLAN);
- if (!sc->rf_kill.rfkill) {
- DPRINTF(sc, ATH_DBG_FATAL, "Failed to allocate rfkill\n");
- return -ENOMEM;
- }
-
- snprintf(sc->rf_kill.rfkill_name, sizeof(sc->rf_kill.rfkill_name),
- "ath9k-%s::rfkill", wiphy_name(sc->hw->wiphy));
- sc->rf_kill.rfkill->name = sc->rf_kill.rfkill_name;
- sc->rf_kill.rfkill->data = sc;
- sc->rf_kill.rfkill->toggle_radio = ath_sw_toggle_radio;
- sc->rf_kill.rfkill->state = RFKILL_STATE_UNBLOCKED;
-
- return 0;
-}
-
-/* Deinitialize rfkill */
-static void ath_deinit_rfkill(struct ath_softc *sc)
+static void ath9k_rfkill_poll_state(struct ieee80211_hw *hw)
{
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
- cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll);
+ struct ath_wiphy *aphy = hw->priv;
+ struct ath_softc *sc = aphy->sc;
+ bool blocked = !!ath_is_rfkill_set(sc);
- if (sc->sc_flags & SC_OP_RFKILL_REGISTERED) {
- rfkill_unregister(sc->rf_kill.rfkill);
- sc->sc_flags &= ~SC_OP_RFKILL_REGISTERED;
- sc->rf_kill.rfkill = NULL;
- }
+ wiphy_rfkill_set_hw_state(hw->wiphy, blocked);
}
-static int ath_start_rfkill_poll(struct ath_softc *sc)
+static void ath_start_rfkill_poll(struct ath_softc *sc)
{
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
- queue_delayed_work(sc->hw->workqueue,
- &sc->rf_kill.rfkill_poll, 0);
-
- if (!(sc->sc_flags & SC_OP_RFKILL_REGISTERED)) {
- if (rfkill_register(sc->rf_kill.rfkill)) {
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to register rfkill\n");
- rfkill_free(sc->rf_kill.rfkill);
-
- /* Deinitialize the device */
- ath_cleanup(sc);
- return -EIO;
- } else {
- sc->sc_flags |= SC_OP_RFKILL_REGISTERED;
- }
- }
+ struct ath_hw *ah = sc->sc_ah;
- return 0;
+ if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
+ wiphy_rfkill_start_polling(sc->hw->wiphy);
}
-#endif /* CONFIG_RFKILL */
void ath_cleanup(struct ath_softc *sc)
{
DPRINTF(sc, ATH_DBG_CONFIG, "Detach ATH hw\n");
-#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
- ath_deinit_rfkill(sc);
-#endif
ath_deinit_leds(sc);
- cancel_work_sync(&sc->chan_work);
- cancel_delayed_work_sync(&sc->wiphy_work);
+ wiphy_rfkill_stop_polling(sc->hw->wiphy);
for (i = 0; i < sc->num_sec_wiphy; i++) {
struct ath_wiphy *aphy = sc->sec_wiphy[i];
if (ATH_TXQ_SETUP(sc, i))
ath_tx_cleanupq(sc, &sc->tx.txq[i]);
+ if ((sc->btcoex_info.no_stomp_timer) &&
+ sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_3WIRE)
+ ath_gen_timer_free(sc->sc_ah, sc->btcoex_info.no_stomp_timer);
+
ath9k_hw_detach(sc->sc_ah);
+ sc->sc_ah = NULL;
ath9k_exit_debug(sc);
- ath9k_ps_restore(sc);
}
static int ath9k_reg_notifier(struct wiphy *wiphy,
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
- struct ath_regulatory *reg = &sc->sc_ah->regulatory;
+ struct ath_regulatory *reg = &sc->common.regulatory;
return ath_reg_notifier_apply(wiphy, request, reg);
}
-static int ath_init(u16 devid, struct ath_softc *sc)
+/*
+ * Initialize and fill ath_softc, ath_sofct is the
+ * "Software Carrier" struct. Historically it has existed
+ * to allow the separation between hardware specific
+ * variables (now in ath_hw) and driver specific variables.
+ */
+static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid)
{
struct ath_hw *ah = NULL;
- int status;
- int error = 0, i;
+ int r = 0, i;
int csz = 0;
/* XXX: hardware will not be ready until ath_open() being called */
spin_lock_init(&sc->wiphy_lock);
spin_lock_init(&sc->sc_resetlock);
spin_lock_init(&sc->sc_serial_rw);
+ spin_lock_init(&sc->ani_lock);
+ spin_lock_init(&sc->sc_pm_lock);
mutex_init(&sc->mutex);
tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet,
*/
ath_read_cachesize(sc, &csz);
/* XXX assert csz is non-zero */
- sc->cachelsz = csz << 2; /* convert to bytes */
+ sc->common.cachelsz = csz << 2; /* convert to bytes */
+
+ ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL);
+ if (!ah) {
+ r = -ENOMEM;
+ goto bad_no_ah;
+ }
+
+ ah->ah_sc = sc;
+ ah->hw_version.devid = devid;
+ ah->hw_version.subsysid = subsysid;
+ sc->sc_ah = ah;
- ah = ath9k_hw_attach(devid, sc, &status);
- if (ah == NULL) {
+ r = ath9k_hw_init(ah);
+ if (r) {
DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to attach hardware; HAL status %d\n", status);
- error = -ENXIO;
+ "Unable to initialize hardware; "
+ "initialization status: %d\n", r);
goto bad;
}
- sc->sc_ah = ah;
/* Get the hardware key cache size. */
sc->keymax = ah->caps.keycache_size;
for (i = 0; i < sc->keymax; i++)
ath9k_hw_keyreset(ah, (u16) i);
- if (ath_regd_init(&sc->sc_ah->regulatory, sc->hw->wiphy,
- ath9k_reg_notifier))
- goto bad;
-
/* default to MONITOR mode */
sc->sc_ah->opmode = NL80211_IFTYPE_MONITOR;
if (sc->beacon.beaconq == -1) {
DPRINTF(sc, ATH_DBG_FATAL,
"Unable to setup a beacon xmit queue\n");
- error = -EIO;
+ r = -EIO;
goto bad2;
}
sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0);
if (sc->beacon.cabq == NULL) {
DPRINTF(sc, ATH_DBG_FATAL,
"Unable to setup CAB xmit queue\n");
- error = -EIO;
+ r = -EIO;
goto bad2;
}
if (!ath_tx_setup(sc, ATH9K_WME_AC_BK)) {
DPRINTF(sc, ATH_DBG_FATAL,
"Unable to setup xmit queue for BK traffic\n");
- error = -EIO;
+ r = -EIO;
goto bad2;
}
if (!ath_tx_setup(sc, ATH9K_WME_AC_BE)) {
DPRINTF(sc, ATH_DBG_FATAL,
"Unable to setup xmit queue for BE traffic\n");
- error = -EIO;
+ r = -EIO;
goto bad2;
}
if (!ath_tx_setup(sc, ATH9K_WME_AC_VI)) {
DPRINTF(sc, ATH_DBG_FATAL,
"Unable to setup xmit queue for VI traffic\n");
- error = -EIO;
+ r = -EIO;
goto bad2;
}
if (!ath_tx_setup(sc, ATH9K_WME_AC_VO)) {
DPRINTF(sc, ATH_DBG_FATAL,
"Unable to setup xmit queue for VO traffic\n");
- error = -EIO;
+ r = -EIO;
goto bad2;
}
ARRAY_SIZE(ath9k_5ghz_chantable);
}
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BT_COEX)
- ath9k_hw_btcoex_enable(sc->sc_ah);
+ if (sc->btcoex_info.btcoex_scheme != ATH_BTCOEX_CFG_NONE) {
+ r = ath9k_hw_btcoex_init(ah);
+ if (r)
+ goto bad2;
+ }
return 0;
bad2:
if (ATH_TXQ_SETUP(sc, i))
ath_tx_cleanupq(sc, &sc->tx.txq[i]);
bad:
- if (ah)
- ath9k_hw_detach(ah);
+ ath9k_hw_detach(ah);
+ sc->sc_ah = NULL;
+bad_no_ah:
ath9k_exit_debug(sc);
- return error;
+ return r;
}
void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_MESH_POINT);
+ hw->wiphy->ps_default = false;
+
hw->queues = 4;
hw->max_rates = 4;
hw->channel_change_time = 5000;
hw->max_listen_interval = 10;
- hw->max_rate_tries = ATH_11N_TXMAXTRY;
+ /* Hardware supports 10 but we use 4 */
+ hw->max_rate_tries = 4;
hw->sta_data_size = sizeof(struct ath_node);
hw->vif_data_size = sizeof(struct ath_vif);
&sc->sbands[IEEE80211_BAND_5GHZ];
}
-int ath_attach(u16 devid, struct ath_softc *sc)
+/* Device driver core initialization */
+int ath_init_device(u16 devid, struct ath_softc *sc, u16 subsysid)
{
struct ieee80211_hw *hw = sc->hw;
int error = 0, i;
DPRINTF(sc, ATH_DBG_CONFIG, "Attach ATH hw\n");
- error = ath_init(devid, sc);
+ error = ath_init_softc(devid, sc, subsysid);
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->common.regulatory, sc->hw->wiphy,
+ ath9k_reg_notifier);
+ if (error)
+ return error;
+
+ reg = &sc->common.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))
if (error != 0)
goto error_attach;
-#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
- /* Initialze h/w Rfkill */
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
- INIT_DELAYED_WORK(&sc->rf_kill.rfkill_poll, ath_rfkill_poll);
-
- /* Initialize s/w rfkill */
- error = ath_init_sw_rfkill(sc);
- if (error)
- goto error_attach;
-#endif
-
INIT_WORK(&sc->chan_work, ath9k_wiphy_chan_work);
INIT_DELAYED_WORK(&sc->wiphy_work, ath9k_wiphy_work);
sc->wiphy_scheduler_int = msecs_to_jiffies(500);
/* Initialize LED control */
ath_init_leds(sc);
+ ath_start_rfkill_poll(sc);
return 0;
ath_tx_cleanupq(sc, &sc->tx.txq[i]);
ath9k_hw_detach(sc->sc_ah);
+ sc->sc_ah = NULL;
ath9k_exit_debug(sc);
return error;
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)
if (chan->band == IEEE80211_BAND_2GHZ) {
ichan->chanmode = CHANNEL_G;
- ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM;
+ ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM | CHANNEL_G;
} else {
ichan->chanmode = CHANNEL_A;
ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM;
struct ath_softc *sc = aphy->sc;
struct ieee80211_channel *curchan = hw->conf.channel;
struct ath9k_channel *init_channel;
- int r, pos;
+ int r;
DPRINTF(sc, ATH_DBG_CONFIG, "Starting driver with "
"initial channel: %d MHz\n", curchan->center_freq);
/* setup initial channel */
- pos = curchan->hw_value;
+ sc->chan_idx = curchan->hw_value;
- sc->chan_idx = pos;
- init_channel = &sc->sc_ah->channels[pos];
- ath9k_update_ichannel(sc, hw, init_channel);
+ init_channel = ath_get_curchannel(sc, hw);
/* Reset SERDES registers */
- ath9k_hw_configpcipowersave(sc->sc_ah, 0);
+ ath9k_hw_configpcipowersave(sc->sc_ah, 0, 0);
/*
* The basic interface to setting the hardware in a good
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);
ieee80211_wake_queues(hw);
-#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
- r = ath_start_rfkill_poll(sc);
-#endif
+ ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
+
+ if ((sc->btcoex_info.btcoex_scheme != ATH_BTCOEX_CFG_NONE) &&
+ !(sc->sc_flags & SC_OP_BTCOEX_ENABLED)) {
+ ath_btcoex_set_weight(&sc->btcoex_info, AR_BT_COEX_WGHT,
+ AR_STOMP_LOW_WLAN_WGHT);
+ ath9k_hw_btcoex_enable(sc->sc_ah);
+
+ ath_pcie_aspm_disable(sc);
+ if (sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_3WIRE)
+ ath_btcoex_timer_resume(sc, &sc->btcoex_info);
+ }
mutex_unlock:
mutex_unlock(&sc->mutex);
goto exit;
}
+ if (sc->ps_enabled) {
+ 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));
/*
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
+ mutex_lock(&sc->mutex);
+
aphy->state = ATH_WIPHY_INACTIVE;
+ cancel_delayed_work_sync(&sc->ath_led_blink_work);
+ cancel_delayed_work_sync(&sc->tx_complete_work);
+
+ if (!sc->num_sec_wiphy) {
+ cancel_delayed_work_sync(&sc->wiphy_work);
+ cancel_work_sync(&sc->chan_work);
+ }
+
if (sc->sc_flags & SC_OP_INVALID) {
DPRINTF(sc, ATH_DBG_ANY, "Device not present\n");
+ mutex_unlock(&sc->mutex);
return;
}
- mutex_lock(&sc->mutex);
-
- ieee80211_stop_queues(hw);
-
if (ath9k_wiphy_started(sc)) {
mutex_unlock(&sc->mutex);
return; /* another wiphy still in use */
}
+ if (sc->sc_flags & SC_OP_BTCOEX_ENABLED) {
+ ath9k_hw_btcoex_disable(sc->sc_ah);
+ if (sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_3WIRE)
+ ath_btcoex_timer_pause(sc, &sc->btcoex_info);
+ }
+
/* make sure h/w will not generate any interrupt
* before setting the invalid flag. */
ath9k_hw_set_interrupts(sc->sc_ah, 0);
} else
sc->rx.rxlink = NULL;
-#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
- cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll);
-#endif
/* disable HAL and put h/w to sleep */
ath9k_hw_disable(sc->sc_ah);
- ath9k_hw_configpcipowersave(sc->sc_ah, 1);
+ ath9k_hw_configpcipowersave(sc->sc_ah, 1, 1);
+ ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP);
sc->sc_flags |= SC_OP_INVALID;
if ((conf->type == NL80211_IFTYPE_STATION) ||
(conf->type == NL80211_IFTYPE_ADHOC) ||
(conf->type == NL80211_IFTYPE_MESH_POINT)) {
- if (ath9k_hw_phycounters(sc->sc_ah))
- sc->imask |= ATH9K_INT_MIB;
+ sc->imask |= ATH9K_INT_MIB;
sc->imask |= ATH9K_INT_TSFOOR;
}
ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
- if (conf->type == NL80211_IFTYPE_AP) {
- /* TODO: is this a suitable place to start ANI for AP mode? */
- /* Start ANI */
- mod_timer(&sc->ani.timer,
- jiffies + msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
- }
+ if (conf->type == NL80211_IFTYPE_AP ||
+ conf->type == NL80211_IFTYPE_ADHOC ||
+ conf->type == NL80211_IFTYPE_MONITOR)
+ ath_start_ani(sc);
out:
mutex_unlock(&sc->mutex);
struct ath_softc *sc = aphy->sc;
struct ieee80211_conf *conf = &hw->conf;
struct ath_hw *ah = sc->sc_ah;
+ bool all_wiphys_idle = false, disable_radio = false;
mutex_lock(&sc->mutex);
+ /* Leave this as the first check */
+ if (changed & IEEE80211_CONF_CHANGE_IDLE) {
+
+ spin_lock_bh(&sc->wiphy_lock);
+ all_wiphys_idle = ath9k_all_wiphys_idle(sc);
+ spin_unlock_bh(&sc->wiphy_lock);
+
+ if (conf->flags & IEEE80211_CONF_IDLE){
+ if (all_wiphys_idle)
+ disable_radio = true;
+ }
+ else if (all_wiphys_idle) {
+ ath_radio_enable(sc);
+ DPRINTF(sc, ATH_DBG_CONFIG,
+ "not-idle: enabling radio\n");
+ }
+ }
+
if (changed & IEEE80211_CONF_CHANGE_PS) {
if (conf->flags & IEEE80211_CONF_PS) {
if (!(ah->caps.hw_caps &
}
ath9k_hw_setrxabort(sc->sc_ah, 1);
}
- ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP);
+ sc->ps_enabled = true;
} else {
+ sc->ps_enabled = false;
ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
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,
if (changed & IEEE80211_CONF_CHANGE_POWER)
sc->config.txpowlimit = 2 * conf->power_level;
- /*
- * The HW TSF has to be reset when the beacon interval changes.
- * We set the flag here, and ath_beacon_config_ap() would take this
- * into account when it gets called through the subsequent
- * config_interface() call - with IFCC_BEACON in the changed field.
- */
-
- if (changed & IEEE80211_CONF_CHANGE_BEACON_INTERVAL)
- sc->sc_flags |= SC_OP_TSF_RESET;
-
- mutex_unlock(&sc->mutex);
-
- return 0;
-}
-
-static int ath9k_config_interface(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_if_conf *conf)
-{
- struct ath_wiphy *aphy = hw->priv;
- struct ath_softc *sc = aphy->sc;
- struct ath_hw *ah = sc->sc_ah;
- struct ath_vif *avp = (void *)vif->drv_priv;
- u32 rfilt = 0;
- int error, i;
-
- mutex_lock(&sc->mutex);
-
- /* TODO: Need to decide which hw opmode to use for multi-interface
- * cases */
- if (vif->type == NL80211_IFTYPE_AP &&
- ah->opmode != NL80211_IFTYPE_AP) {
- ah->opmode = NL80211_IFTYPE_STATION;
- ath9k_hw_setopmode(ah);
- memcpy(sc->curbssid, sc->sc_ah->macaddr, ETH_ALEN);
- sc->curaid = 0;
- ath9k_hw_write_associd(sc);
- /* Request full reset to get hw opmode changed properly */
- sc->sc_flags |= SC_OP_FULL_RESET;
- }
-
- if ((conf->changed & IEEE80211_IFCC_BSSID) &&
- !is_zero_ether_addr(conf->bssid)) {
- switch (vif->type) {
- case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_MESH_POINT:
- /* Set BSSID */
- memcpy(sc->curbssid, conf->bssid, ETH_ALEN);
- memcpy(avp->bssid, conf->bssid, ETH_ALEN);
- sc->curaid = 0;
- ath9k_hw_write_associd(sc);
-
- /* Set aggregation protection mode parameters */
- sc->config.ath_aggr_prot = 0;
-
- DPRINTF(sc, ATH_DBG_CONFIG,
- "RX filter 0x%x bssid %pM aid 0x%x\n",
- rfilt, sc->curbssid, sc->curaid);
-
- /* need to reconfigure the beacon */
- sc->sc_flags &= ~SC_OP_BEACONS ;
-
- break;
- default:
- break;
- }
- }
-
- if ((vif->type == NL80211_IFTYPE_ADHOC) ||
- (vif->type == NL80211_IFTYPE_AP) ||
- (vif->type == NL80211_IFTYPE_MESH_POINT)) {
- if ((conf->changed & IEEE80211_IFCC_BEACON) ||
- (conf->changed & IEEE80211_IFCC_BEACON_ENABLED &&
- conf->enable_beacon)) {
- /*
- * Allocate and setup the beacon frame.
- *
- * Stop any previous beacon DMA. This may be
- * necessary, for example, when an ibss merge
- * causes reconfiguration; we may be called
- * with beacon transmission active.
- */
- ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
-
- error = ath_beacon_alloc(aphy, vif);
- if (error != 0) {
- mutex_unlock(&sc->mutex);
- return error;
- }
-
- ath_beacon_config(sc, vif);
- }
- }
-
- /* Check for WLAN_CAPABILITY_PRIVACY ? */
- if ((avp->av_opmode != NL80211_IFTYPE_STATION)) {
- for (i = 0; i < IEEE80211_WEP_NKID; i++)
- if (ath9k_hw_keyisvalid(sc->sc_ah, (u16)i))
- ath9k_hw_keysetmac(sc->sc_ah,
- (u16)i,
- sc->curbssid);
+ if (disable_radio) {
+ DPRINTF(sc, ATH_DBG_CONFIG, "idle: disabling radio\n");
+ ath_radio_disable(sc);
}
- /* Only legacy IBSS for now */
- if (vif->type == NL80211_IFTYPE_ADHOC)
- ath_update_chainmask(sc, 0);
-
mutex_unlock(&sc->mutex);
return 0;
(FIF_PROMISC_IN_BSS | \
FIF_ALLMULTI | \
FIF_CONTROL | \
+ FIF_PSPOLL | \
FIF_OTHER_BSS | \
FIF_BCN_PRBRESP_PROMISC | \
FIF_FCSFAIL)
static void ath9k_configure_filter(struct ieee80211_hw *hw,
unsigned int changed_flags,
unsigned int *total_flags,
- int mc_count,
- struct dev_mc_list *mclist)
+ u64 multicast)
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
*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);
+ DPRINTF(sc, ATH_DBG_CONFIG, "Set HW RX filter: 0x%x\n", rfilt);
}
static void ath9k_sta_notify(struct ieee80211_hw *hw,
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_vif *avp = (void *)vif->drv_priv;
+ u32 rfilt = 0;
+ int error, i;
mutex_lock(&sc->mutex);
+ /*
+ * TODO: Need to decide which hw opmode to use for
+ * multi-interface cases
+ * XXX: This belongs into add_interface!
+ */
+ if (vif->type == NL80211_IFTYPE_AP &&
+ ah->opmode != NL80211_IFTYPE_AP) {
+ ah->opmode = NL80211_IFTYPE_STATION;
+ ath9k_hw_setopmode(ah);
+ memcpy(sc->curbssid, sc->sc_ah->macaddr, ETH_ALEN);
+ sc->curaid = 0;
+ ath9k_hw_write_associd(sc);
+ /* Request full reset to get hw opmode changed properly */
+ sc->sc_flags |= SC_OP_FULL_RESET;
+ }
+
+ if ((changed & BSS_CHANGED_BSSID) &&
+ !is_zero_ether_addr(bss_conf->bssid)) {
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
+ /* Set BSSID */
+ memcpy(sc->curbssid, bss_conf->bssid, ETH_ALEN);
+ memcpy(avp->bssid, bss_conf->bssid, ETH_ALEN);
+ sc->curaid = 0;
+ ath9k_hw_write_associd(sc);
+
+ /* Set aggregation protection mode parameters */
+ sc->config.ath_aggr_prot = 0;
+
+ DPRINTF(sc, ATH_DBG_CONFIG,
+ "RX filter 0x%x bssid %pM aid 0x%x\n",
+ rfilt, sc->curbssid, sc->curaid);
+
+ /* need to reconfigure the beacon */
+ sc->sc_flags &= ~SC_OP_BEACONS ;
+
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ((vif->type == NL80211_IFTYPE_ADHOC) ||
+ (vif->type == NL80211_IFTYPE_AP) ||
+ (vif->type == NL80211_IFTYPE_MESH_POINT)) {
+ if ((changed & BSS_CHANGED_BEACON) ||
+ (changed & BSS_CHANGED_BEACON_ENABLED &&
+ bss_conf->enable_beacon)) {
+ /*
+ * Allocate and setup the beacon frame.
+ *
+ * Stop any previous beacon DMA. This may be
+ * necessary, for example, when an ibss merge
+ * causes reconfiguration; we may be called
+ * with beacon transmission active.
+ */
+ ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
+
+ error = ath_beacon_alloc(aphy, vif);
+ if (!error)
+ ath_beacon_config(sc, vif);
+ }
+ }
+
+ /* Check for WLAN_CAPABILITY_PRIVACY ? */
+ if ((avp->av_opmode != NL80211_IFTYPE_STATION)) {
+ for (i = 0; i < IEEE80211_WEP_NKID; i++)
+ if (ath9k_hw_keyisvalid(sc->sc_ah, (u16)i))
+ ath9k_hw_keysetmac(sc->sc_ah,
+ (u16)i,
+ sc->curbssid);
+ }
+
+ /* Only legacy IBSS for now */
+ if (vif->type == NL80211_IFTYPE_ADHOC)
+ ath_update_chainmask(sc, 0);
+
if (changed & BSS_CHANGED_ERP_PREAMBLE) {
DPRINTF(sc, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n",
bss_conf->use_short_preamble);
ath9k_bss_assoc_info(sc, vif, bss_conf);
}
+ /*
+ * The HW TSF has to be reset when the beacon interval changes.
+ * We set the flag here, and ath_beacon_config_ap() would take this
+ * into account when it gets called through the subsequent
+ * config_interface() call - with IFCC_BEACON in the changed field.
+ */
+
+ if (changed & BSS_CHANGED_BEACON_INT) {
+ sc->sc_flags |= SC_OP_TSF_RESET;
+ sc->beacon_interval = bss_conf->beacon_int;
+ }
+
mutex_unlock(&sc->mutex);
}
case IEEE80211_AMPDU_RX_STOP:
break;
case IEEE80211_AMPDU_TX_START:
- ret = ath_tx_aggr_start(sc, sta, tid, ssn);
- if (ret < 0)
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to start TX aggregation\n");
- else
- ieee80211_start_tx_ba_cb_irqsafe(hw, sta->addr, tid);
+ ath_tx_aggr_start(sc, sta, tid, ssn);
+ ieee80211_start_tx_ba_cb_irqsafe(hw, sta->addr, tid);
break;
case IEEE80211_AMPDU_TX_STOP:
- ret = ath_tx_aggr_stop(sc, sta, tid);
- if (ret < 0)
- DPRINTF(sc, ATH_DBG_FATAL,
- "Unable to stop TX aggregation\n");
-
+ ath_tx_aggr_stop(sc, sta, tid);
ieee80211_stop_tx_ba_cb_irqsafe(hw, sta->addr, tid);
break;
case IEEE80211_AMPDU_TX_OPERATIONAL:
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
+ mutex_lock(&sc->mutex);
if (ath9k_wiphy_scanning(sc)) {
printk(KERN_DEBUG "ath9k: Two wiphys trying to scan at the "
"same time\n");
* Do not allow the concurrent scanning state for now. This
* could be improved with scanning control moved into ath9k.
*/
+ mutex_unlock(&sc->mutex);
return;
}
aphy->state = ATH_WIPHY_SCAN;
ath9k_wiphy_pause_all_forced(sc, aphy);
- mutex_lock(&sc->mutex);
+ spin_lock_bh(&sc->ani_lock);
sc->sc_flags |= SC_OP_SCANNING;
+ spin_unlock_bh(&sc->ani_lock);
mutex_unlock(&sc->mutex);
}
struct ath_softc *sc = aphy->sc;
mutex_lock(&sc->mutex);
+ spin_lock_bh(&sc->ani_lock);
aphy->state = ATH_WIPHY_ACTIVE;
sc->sc_flags &= ~SC_OP_SCANNING;
+ sc->sc_flags |= SC_OP_FULL_RESET;
+ spin_unlock_bh(&sc->ani_lock);
+ ath_beacon_config(sc, NULL);
mutex_unlock(&sc->mutex);
}
.add_interface = ath9k_add_interface,
.remove_interface = ath9k_remove_interface,
.config = ath9k_config,
- .config_interface = ath9k_config_interface,
.configure_filter = ath9k_configure_filter,
.sta_notify = ath9k_sta_notify,
.conf_tx = ath9k_conf_tx,
.ampdu_action = ath9k_ampdu_action,
.sw_scan_start = ath9k_sw_scan_start,
.sw_scan_complete = ath9k_sw_scan_complete,
+ .rfkill_poll = ath9k_rfkill_poll_state,
};
static struct {
{ AR_SREV_VERSION_9100, "9100" },
{ AR_SREV_VERSION_9160, "9160" },
{ AR_SREV_VERSION_9280, "9280" },
- { AR_SREV_VERSION_9285, "9285" }
+ { AR_SREV_VERSION_9285, "9285" },
+ { AR_SREV_VERSION_9287, "9287" }
};
static struct {