From 203c4805e91786f9a010bc7945a0fde70c9da28e Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 30 Mar 2009 22:30:33 -0400 Subject: atheros: put atheros wireless drivers into ath/ Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 2890 +++++++++++++++++++++++++++++++++ 1 file changed, 2890 insertions(+) create mode 100644 drivers/net/wireless/ath/ath9k/main.c (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c new file mode 100644 index 00000000000..8b6a7ea4e59 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -0,0 +1,2890 @@ +/* + * Copyright (c) 2008-2009 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include "ath9k.h" + +#define ATH_PCI_VERSION "0.1" + +static char *dev_info = "ath9k"; + +MODULE_AUTHOR("Atheros Communications"); +MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards."); +MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards"); +MODULE_LICENSE("Dual BSD/GPL"); + +static int modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption"); + +/* We use the hw_value as an index into our private channel structure */ + +#define CHAN2G(_freq, _idx) { \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 30, \ +} + +#define CHAN5G(_freq, _idx) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 30, \ +} + +/* Some 2 GHz radios are actually tunable on 2312-2732 + * on 5 MHz steps, we support the channels which we know + * we have calibration data for all cards though to make + * this static */ +static struct ieee80211_channel ath9k_2ghz_chantable[] = { + CHAN2G(2412, 0), /* Channel 1 */ + CHAN2G(2417, 1), /* Channel 2 */ + CHAN2G(2422, 2), /* Channel 3 */ + CHAN2G(2427, 3), /* Channel 4 */ + CHAN2G(2432, 4), /* Channel 5 */ + CHAN2G(2437, 5), /* Channel 6 */ + CHAN2G(2442, 6), /* Channel 7 */ + CHAN2G(2447, 7), /* Channel 8 */ + CHAN2G(2452, 8), /* Channel 9 */ + CHAN2G(2457, 9), /* Channel 10 */ + CHAN2G(2462, 10), /* Channel 11 */ + CHAN2G(2467, 11), /* Channel 12 */ + CHAN2G(2472, 12), /* Channel 13 */ + CHAN2G(2484, 13), /* Channel 14 */ +}; + +/* Some 5 GHz radios are actually tunable on XXXX-YYYY + * on 5 MHz steps, we support the channels which we know + * we have calibration data for all cards though to make + * this static */ +static struct ieee80211_channel ath9k_5ghz_chantable[] = { + /* _We_ call this UNII 1 */ + CHAN5G(5180, 14), /* Channel 36 */ + CHAN5G(5200, 15), /* Channel 40 */ + CHAN5G(5220, 16), /* Channel 44 */ + CHAN5G(5240, 17), /* Channel 48 */ + /* _We_ call this UNII 2 */ + CHAN5G(5260, 18), /* Channel 52 */ + CHAN5G(5280, 19), /* Channel 56 */ + CHAN5G(5300, 20), /* Channel 60 */ + CHAN5G(5320, 21), /* Channel 64 */ + /* _We_ call this "Middle band" */ + CHAN5G(5500, 22), /* Channel 100 */ + CHAN5G(5520, 23), /* Channel 104 */ + CHAN5G(5540, 24), /* Channel 108 */ + CHAN5G(5560, 25), /* Channel 112 */ + CHAN5G(5580, 26), /* Channel 116 */ + CHAN5G(5600, 27), /* Channel 120 */ + CHAN5G(5620, 28), /* Channel 124 */ + CHAN5G(5640, 29), /* Channel 128 */ + CHAN5G(5660, 30), /* Channel 132 */ + CHAN5G(5680, 31), /* Channel 136 */ + CHAN5G(5700, 32), /* Channel 140 */ + /* _We_ call this UNII 3 */ + CHAN5G(5745, 33), /* Channel 149 */ + CHAN5G(5765, 34), /* Channel 153 */ + CHAN5G(5785, 35), /* Channel 157 */ + CHAN5G(5805, 36), /* Channel 161 */ + CHAN5G(5825, 37), /* Channel 165 */ +}; + +static void ath_cache_conf_rate(struct ath_softc *sc, + struct ieee80211_conf *conf) +{ + switch (conf->channel->band) { + case IEEE80211_BAND_2GHZ: + if (conf_is_ht20(conf)) + sc->cur_rate_table = + sc->hw_rate_table[ATH9K_MODE_11NG_HT20]; + else if (conf_is_ht40_minus(conf)) + sc->cur_rate_table = + sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS]; + else if (conf_is_ht40_plus(conf)) + sc->cur_rate_table = + sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS]; + else + sc->cur_rate_table = + sc->hw_rate_table[ATH9K_MODE_11G]; + break; + case IEEE80211_BAND_5GHZ: + if (conf_is_ht20(conf)) + sc->cur_rate_table = + sc->hw_rate_table[ATH9K_MODE_11NA_HT20]; + else if (conf_is_ht40_minus(conf)) + sc->cur_rate_table = + sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS]; + else if (conf_is_ht40_plus(conf)) + sc->cur_rate_table = + sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS]; + else + sc->cur_rate_table = + sc->hw_rate_table[ATH9K_MODE_11A]; + break; + default: + BUG_ON(1); + break; + } +} + +static void ath_update_txpow(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + u32 txpow; + + if (sc->curtxpow != sc->config.txpowlimit) { + ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit); + /* read back in case value is clamped */ + ath9k_hw_getcapability(ah, ATH9K_CAP_TXPOW, 1, &txpow); + sc->curtxpow = txpow; + } +} + +static u8 parse_mpdudensity(u8 mpdudensity) +{ + /* + * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing": + * 0 for no restriction + * 1 for 1/4 us + * 2 for 1/2 us + * 3 for 1 us + * 4 for 2 us + * 5 for 4 us + * 6 for 8 us + * 7 for 16 us + */ + switch (mpdudensity) { + case 0: + return 0; + case 1: + case 2: + case 3: + /* Our lower layer calculations limit our precision to + 1 microsecond */ + return 1; + case 4: + return 2; + case 5: + return 4; + case 6: + return 8; + case 7: + return 16; + default: + return 0; + } +} + +static void ath_setup_rates(struct ath_softc *sc, enum ieee80211_band band) +{ + struct ath_rate_table *rate_table = NULL; + struct ieee80211_supported_band *sband; + struct ieee80211_rate *rate; + int i, maxrates; + + switch (band) { + case IEEE80211_BAND_2GHZ: + rate_table = sc->hw_rate_table[ATH9K_MODE_11G]; + break; + case IEEE80211_BAND_5GHZ: + rate_table = sc->hw_rate_table[ATH9K_MODE_11A]; + break; + default: + break; + } + + if (rate_table == NULL) + return; + + sband = &sc->sbands[band]; + rate = sc->rates[band]; + + if (rate_table->rate_cnt > ATH_RATE_MAX) + maxrates = ATH_RATE_MAX; + else + maxrates = rate_table->rate_cnt; + + for (i = 0; i < maxrates; i++) { + rate[i].bitrate = rate_table->info[i].ratekbps / 100; + rate[i].hw_value = rate_table->info[i].ratecode; + if (rate_table->info[i].short_preamble) { + rate[i].hw_value_short = rate_table->info[i].ratecode | + rate_table->info[i].short_preamble; + rate[i].flags = IEEE80211_RATE_SHORT_PREAMBLE; + } + sband->n_bitrates++; + + DPRINTF(sc, ATH_DBG_CONFIG, "Rate: %2dMbps, ratecode: %2d\n", + rate[i].bitrate / 10, rate[i].hw_value); + } +} + +/* + * 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 + * DMA, then restart stuff. +*/ +int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, + struct ath9k_channel *hchan) +{ + struct ath_hw *ah = sc->sc_ah; + bool fastcc = true, stopped; + struct ieee80211_channel *channel = hw->conf.channel; + int r; + + if (sc->sc_flags & SC_OP_INVALID) + return -EIO; + + ath9k_ps_wakeup(sc); + + /* + * This is only performed if the channel settings have + * actually changed. + * + * To switch channels clear any pending DMA operations; + * wait long enough for the RX fifo to drain, reset the + * hardware at the new frequency, and then re-enable + * the relevant bits of the h/w. + */ + ath9k_hw_set_interrupts(ah, 0); + ath_drain_all_txq(sc, false); + stopped = ath_stoprecv(sc); + + /* XXX: do not flush receive queue here. We don't want + * to flush data frames already in queue because of + * changing channel. */ + + if (!stopped || (sc->sc_flags & SC_OP_FULL_RESET)) + fastcc = false; + + DPRINTF(sc, ATH_DBG_CONFIG, + "(%u MHz) -> (%u MHz), chanwidth: %d\n", + sc->sc_ah->curchan->channel, + channel->center_freq, sc->tx_chan_width); + + spin_lock_bh(&sc->sc_resetlock); + + r = ath9k_hw_reset(ah, hchan, fastcc); + if (r) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to reset channel (%u Mhz) " + "reset status %u\n", + channel->center_freq, r); + spin_unlock_bh(&sc->sc_resetlock); + return r; + } + spin_unlock_bh(&sc->sc_resetlock); + + sc->sc_flags &= ~SC_OP_FULL_RESET; + + if (ath_startrecv(sc) != 0) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to restart recv logic\n"); + return -EIO; + } + + ath_cache_conf_rate(sc, &hw->conf); + ath_update_txpow(sc); + ath9k_hw_set_interrupts(ah, sc->imask); + ath9k_ps_restore(sc); + return 0; +} + +/* + * This routine performs the periodic noise floor calibration function + * that is used to adjust and optimize the chip performance. This + * takes environmental changes (location, temperature) into account. + * When the task is complete, it reschedules itself depending on the + * appropriate interval that was calculated. + */ +static void ath_ani_calibrate(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *)data; + struct ath_hw *ah = sc->sc_ah; + bool longcal = false; + bool shortcal = false; + bool aniflag = false; + unsigned int timestamp = jiffies_to_msecs(jiffies); + u32 cal_interval, short_cal_interval; + + short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ? + ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL; + + /* + * don't calibrate when we're scanning. + * we are most likely not on our home channel. + */ + if (sc->sc_flags & SC_OP_SCANNING) + goto set_timer; + + /* Long calibration runs independently of short calibration. */ + if ((timestamp - sc->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) { + longcal = true; + DPRINTF(sc, ATH_DBG_ANI, "longcal @%lu\n", jiffies); + sc->ani.longcal_timer = timestamp; + } + + /* Short calibration applies only while caldone is false */ + if (!sc->ani.caldone) { + if ((timestamp - sc->ani.shortcal_timer) >= short_cal_interval) { + shortcal = true; + DPRINTF(sc, ATH_DBG_ANI, "shortcal @%lu\n", jiffies); + sc->ani.shortcal_timer = timestamp; + sc->ani.resetcal_timer = timestamp; + } + } else { + if ((timestamp - sc->ani.resetcal_timer) >= + ATH_RESTART_CALINTERVAL) { + sc->ani.caldone = ath9k_hw_reset_calvalid(ah); + if (sc->ani.caldone) + sc->ani.resetcal_timer = timestamp; + } + } + + /* Verify whether we must check ANI */ + if ((timestamp - sc->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) { + aniflag = true; + sc->ani.checkani_timer = timestamp; + } + + /* Skip all processing if there's nothing to do. */ + if (longcal || shortcal || aniflag) { + /* Call ANI routine if necessary */ + if (aniflag) + ath9k_hw_ani_monitor(ah, &sc->nodestats, 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; + } + } + +set_timer: + /* + * Set timer interval based on previous results. + * The interval must be the shortest necessary to satisfy ANI, + * short calibration and long calibration. + */ + cal_interval = ATH_LONG_CALINTERVAL; + if (sc->sc_ah->config.enable_ani) + cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL); + if (!sc->ani.caldone) + cal_interval = min(cal_interval, (u32)short_cal_interval); + + mod_timer(&sc->ani.timer, jiffies + msecs_to_jiffies(cal_interval)); +} + +/* + * Update tx/rx chainmask. For legacy association, + * hard code chainmask to 1x1, for 11n association, use + * the chainmask configuration, for bt coexistence, use + * the chainmask configuration even in legacy mode. + */ +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)) { + sc->tx_chainmask = sc->sc_ah->caps.tx_chainmask; + sc->rx_chainmask = sc->sc_ah->caps.rx_chainmask; + } else { + sc->tx_chainmask = 1; + sc->rx_chainmask = 1; + } + + DPRINTF(sc, ATH_DBG_CONFIG, "tx chmask: %d, rx chmask: %d\n", + sc->tx_chainmask, sc->rx_chainmask); +} + +static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta) +{ + struct ath_node *an; + + an = (struct ath_node *)sta->drv_priv; + + if (sc->sc_flags & SC_OP_TXAGGR) { + ath_tx_node_init(sc, an); + an->maxampdu = 1 << (IEEE80211_HTCAP_MAXRXAMPDU_FACTOR + + sta->ht_cap.ampdu_factor); + an->mpdudensity = parse_mpdudensity(sta->ht_cap.ampdu_density); + } +} + +static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta) +{ + struct ath_node *an = (struct ath_node *)sta->drv_priv; + + if (sc->sc_flags & SC_OP_TXAGGR) + ath_tx_node_cleanup(sc, an); +} + +static void ath9k_tasklet(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *)data; + u32 status = sc->intrstatus; + + if (status & ATH9K_INT_FATAL) { + ath_reset(sc, false); + return; + } + + if (status & (ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN)) { + spin_lock_bh(&sc->rx.rxflushlock); + ath_rx_tasklet(sc, 0); + spin_unlock_bh(&sc->rx.rxflushlock); + } + + if (status & ATH9K_INT_TX) + ath_tx_tasklet(sc); + + /* re-enable hardware interrupt */ + ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); +} + +irqreturn_t ath_isr(int irq, void *dev) +{ +#define SCHED_INTR ( \ + ATH9K_INT_FATAL | \ + ATH9K_INT_RXORN | \ + ATH9K_INT_RXEOL | \ + ATH9K_INT_RX | \ + ATH9K_INT_TX | \ + ATH9K_INT_BMISS | \ + ATH9K_INT_CST | \ + ATH9K_INT_TSFOOR) + + struct ath_softc *sc = dev; + struct ath_hw *ah = sc->sc_ah; + enum ath9k_int status; + bool sched = false; + + /* + * The hardware is not ready/present, don't + * touch anything. Note this can happen early + * on if the IRQ is shared. + */ + 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); + return IRQ_NONE; + } + + /* + * Figure out the reason(s) for the interrupt. Note + * that the hal returns a pseudo-ISR that may include + * bits we haven't explicitly enabled so we mask the + * value to insure we only process bits we requested. + */ + ath9k_hw_getisr(ah, &status); /* NB: clears ISR too */ + status &= sc->imask; /* discard unasked-for bits */ + + /* + * 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); + return IRQ_NONE; + } + + /* Cache the status */ + sc->intrstatus = status; + + if (status & SCHED_INTR) + sched = true; + + /* + * If a FATAL or RXORN interrupt is received, we have to reset the + * chip immediately. + */ + if (status & (ATH9K_INT_FATAL | ATH9K_INT_RXORN)) + goto chip_reset; + + if (status & ATH9K_INT_SWBA) + tasklet_schedule(&sc->bcon_tasklet); + + if (status & ATH9K_INT_TXURN) + ath9k_hw_updatetxtriglevel(ah, true); + + if (status & ATH9K_INT_MIB) { + /* + * Disable interrupts until we service the MIB + * interrupt; otherwise it will continue to + * fire. + */ + ath9k_hw_set_interrupts(ah, 0); + /* + * Let the hal handle the event. We assume + * it will clear whatever condition caused + * the interrupt. + */ + ath9k_hw_procmibevent(ah, &sc->nodestats); + ath9k_hw_set_interrupts(ah, sc->imask); + } + + if (status & ATH9K_INT_TIM_TIMER) { + if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { + /* Clear RxAbort bit so that we can + * receive frames */ + ath9k_hw_setpower(ah, ATH9K_PM_AWAKE); + ath9k_hw_setrxabort(ah, 0); + sched = true; + sc->sc_flags |= SC_OP_WAIT_FOR_BEACON; + } + } + +chip_reset: + + ath9k_ps_restore(sc); + ath_debug_stat_interrupt(sc, status); + + if (sched) { + /* turn off every interrupt except SWBA */ + ath9k_hw_set_interrupts(ah, (sc->imask & ATH9K_INT_SWBA)); + tasklet_schedule(&sc->intr_tq); + } + + return IRQ_HANDLED; + +#undef SCHED_INTR +} + +static u32 ath_get_extchanmode(struct ath_softc *sc, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + u32 chanmode = 0; + + switch (chan->band) { + case IEEE80211_BAND_2GHZ: + switch(channel_type) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: + chanmode = CHANNEL_G_HT20; + break; + case NL80211_CHAN_HT40PLUS: + chanmode = CHANNEL_G_HT40PLUS; + break; + case NL80211_CHAN_HT40MINUS: + chanmode = CHANNEL_G_HT40MINUS; + break; + } + break; + case IEEE80211_BAND_5GHZ: + switch(channel_type) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: + chanmode = CHANNEL_A_HT20; + break; + case NL80211_CHAN_HT40PLUS: + chanmode = CHANNEL_A_HT40PLUS; + break; + case NL80211_CHAN_HT40MINUS: + chanmode = CHANNEL_A_HT40MINUS; + break; + } + break; + default: + break; + } + + return chanmode; +} + +static int ath_setkey_tkip(struct ath_softc *sc, u16 keyix, const u8 *key, + struct ath9k_keyval *hk, const u8 *addr, + bool authenticator) +{ + const u8 *key_rxmic; + const u8 *key_txmic; + + key_txmic = key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY; + key_rxmic = key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY; + + if (addr == NULL) { + /* + * Group key installation - only two key cache entries are used + * regardless of splitmic capability since group key is only + * used either for TX or RX. + */ + if (authenticator) { + memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic)); + memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_mic)); + } else { + memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); + memcpy(hk->kv_txmic, key_rxmic, sizeof(hk->kv_mic)); + } + return ath9k_hw_set_keycache_entry(sc->sc_ah, keyix, hk, addr); + } + if (!sc->splitmic) { + /* TX and RX keys share the same key cache entry. */ + memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); + memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic)); + return ath9k_hw_set_keycache_entry(sc->sc_ah, keyix, hk, addr); + } + + /* Separate key cache entries for TX and RX */ + + /* TX key goes at first index, RX key at +32. */ + memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic)); + if (!ath9k_hw_set_keycache_entry(sc->sc_ah, keyix, hk, NULL)) { + /* TX MIC entry failed. No need to proceed further */ + DPRINTF(sc, ATH_DBG_FATAL, + "Setting TX MIC Key Failed\n"); + return 0; + } + + memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); + /* XXX delete tx key on failure? */ + return ath9k_hw_set_keycache_entry(sc->sc_ah, keyix + 32, hk, addr); +} + +static int ath_reserve_key_cache_slot_tkip(struct ath_softc *sc) +{ + int i; + + for (i = IEEE80211_WEP_NKID; i < sc->keymax / 2; i++) { + if (test_bit(i, sc->keymap) || + test_bit(i + 64, sc->keymap)) + continue; /* At least one part of TKIP key allocated */ + if (sc->splitmic && + (test_bit(i + 32, sc->keymap) || + test_bit(i + 64 + 32, sc->keymap))) + continue; /* At least one part of TKIP key allocated */ + + /* Found a free slot for a TKIP key */ + return i; + } + return -1; +} + +static int ath_reserve_key_cache_slot(struct ath_softc *sc) +{ + int i; + + /* First, try to find slots that would not be available for TKIP. */ + if (sc->splitmic) { + for (i = IEEE80211_WEP_NKID; i < sc->keymax / 4; i++) { + if (!test_bit(i, sc->keymap) && + (test_bit(i + 32, sc->keymap) || + test_bit(i + 64, sc->keymap) || + test_bit(i + 64 + 32, sc->keymap))) + return i; + if (!test_bit(i + 32, sc->keymap) && + (test_bit(i, sc->keymap) || + test_bit(i + 64, sc->keymap) || + test_bit(i + 64 + 32, sc->keymap))) + return i + 32; + if (!test_bit(i + 64, sc->keymap) && + (test_bit(i , sc->keymap) || + test_bit(i + 32, sc->keymap) || + test_bit(i + 64 + 32, sc->keymap))) + return i + 64; + if (!test_bit(i + 64 + 32, sc->keymap) && + (test_bit(i, sc->keymap) || + test_bit(i + 32, sc->keymap) || + test_bit(i + 64, sc->keymap))) + return i + 64 + 32; + } + } else { + for (i = IEEE80211_WEP_NKID; i < sc->keymax / 2; i++) { + if (!test_bit(i, sc->keymap) && + test_bit(i + 64, sc->keymap)) + return i; + if (test_bit(i, sc->keymap) && + !test_bit(i + 64, sc->keymap)) + return i + 64; + } + } + + /* No partially used TKIP slots, pick any available slot */ + for (i = IEEE80211_WEP_NKID; i < sc->keymax; i++) { + /* Do not allow slots that could be needed for TKIP group keys + * to be used. This limitation could be removed if we know that + * TKIP will not be used. */ + if (i >= 64 && i < 64 + IEEE80211_WEP_NKID) + continue; + if (sc->splitmic) { + if (i >= 32 && i < 32 + IEEE80211_WEP_NKID) + continue; + if (i >= 64 + 32 && i < 64 + 32 + IEEE80211_WEP_NKID) + continue; + } + + if (!test_bit(i, sc->keymap)) + return i; /* Found a free slot for a key */ + } + + /* No free slot found */ + return -1; +} + +static int ath_key_config(struct ath_softc *sc, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct ath9k_keyval hk; + const u8 *mac = NULL; + int ret = 0; + int idx; + + memset(&hk, 0, sizeof(hk)); + + switch (key->alg) { + case ALG_WEP: + hk.kv_type = ATH9K_CIPHER_WEP; + break; + case ALG_TKIP: + hk.kv_type = ATH9K_CIPHER_TKIP; + break; + case ALG_CCMP: + hk.kv_type = ATH9K_CIPHER_AES_CCM; + break; + default: + return -EOPNOTSUPP; + } + + hk.kv_len = key->keylen; + memcpy(hk.kv_val, key->key, key->keylen); + + if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { + /* For now, use the default keys for broadcast keys. This may + * need to change with virtual interfaces. */ + idx = key->keyidx; + } else if (key->keyidx) { + if (WARN_ON(!sta)) + return -EOPNOTSUPP; + mac = sta->addr; + + if (vif->type != NL80211_IFTYPE_AP) { + /* Only keyidx 0 should be used with unicast key, but + * allow this for client mode for now. */ + idx = key->keyidx; + } else + return -EIO; + } else { + if (WARN_ON(!sta)) + return -EOPNOTSUPP; + mac = sta->addr; + + if (key->alg == ALG_TKIP) + idx = ath_reserve_key_cache_slot_tkip(sc); + else + idx = ath_reserve_key_cache_slot(sc); + if (idx < 0) + return -ENOSPC; /* no free key cache entries */ + } + + if (key->alg == ALG_TKIP) + ret = ath_setkey_tkip(sc, idx, key->key, &hk, mac, + vif->type == NL80211_IFTYPE_AP); + else + ret = ath9k_hw_set_keycache_entry(sc->sc_ah, idx, &hk, mac); + + if (!ret) + return -EIO; + + set_bit(idx, sc->keymap); + if (key->alg == ALG_TKIP) { + set_bit(idx + 64, sc->keymap); + if (sc->splitmic) { + set_bit(idx + 32, sc->keymap); + set_bit(idx + 64 + 32, sc->keymap); + } + } + + return idx; +} + +static void ath_key_delete(struct ath_softc *sc, struct ieee80211_key_conf *key) +{ + ath9k_hw_keyreset(sc->sc_ah, key->hw_key_idx); + if (key->hw_key_idx < IEEE80211_WEP_NKID) + return; + + clear_bit(key->hw_key_idx, sc->keymap); + if (key->alg != ALG_TKIP) + return; + + clear_bit(key->hw_key_idx + 64, sc->keymap); + if (sc->splitmic) { + clear_bit(key->hw_key_idx + 32, sc->keymap); + clear_bit(key->hw_key_idx + 64 + 32, sc->keymap); + } +} + +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 */ + + ht_info->ht_supported = true; + ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_SM_PS | + 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; + + /* set up supported mcs set */ + memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); + + 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; + } + + 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); + } + + /* 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)); + } else { + DPRINTF(sc, ATH_DBG_CONFIG, "Bss Info DISASSOC\n"); + sc->curaid = 0; + } +} + +/********************************/ +/* LED functions */ +/********************************/ + +static void ath_led_blink_work(struct work_struct *work) +{ + struct ath_softc *sc = container_of(work, struct ath_softc, + ath_led_blink_work.work); + + if (!(sc->sc_flags & SC_OP_LED_ASSOCIATED)) + return; + + 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); + else + ath9k_hw_set_gpio(sc->sc_ah, ATH_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)); + + sc->led_on_duration = sc->led_on_cnt ? + max((ATH_LED_ON_DURATION_IDLE - sc->led_on_cnt), 25) : + ATH_LED_ON_DURATION_IDLE; + sc->led_off_duration = sc->led_off_cnt ? + max((ATH_LED_OFF_DURATION_IDLE - sc->led_off_cnt), 10) : + ATH_LED_OFF_DURATION_IDLE; + sc->led_on_cnt = sc->led_off_cnt = 0; + if (sc->sc_flags & SC_OP_LED_ON) + sc->sc_flags &= ~SC_OP_LED_ON; + else + sc->sc_flags |= SC_OP_LED_ON; +} + +static void ath_led_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev); + struct ath_softc *sc = led->sc; + + switch (brightness) { + 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, + (led->led_type == ATH_LED_RADIO)); + sc->sc_flags &= ~SC_OP_LED_ASSOCIATED; + if (led->led_type == ATH_LED_RADIO) + sc->sc_flags &= ~SC_OP_LED_ON; + } else { + sc->led_off_cnt++; + } + break; + 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); + } else if (led->led_type == ATH_LED_RADIO) { + ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 0); + sc->sc_flags |= SC_OP_LED_ON; + } else { + sc->led_on_cnt++; + } + break; + default: + break; + } +} + +static int ath_register_led(struct ath_softc *sc, struct ath_led *led, + char *trigger) +{ + int ret; + + led->sc = sc; + led->led_cdev.name = led->name; + led->led_cdev.default_trigger = trigger; + led->led_cdev.brightness_set = ath_led_brightness; + + ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->led_cdev); + if (ret) + DPRINTF(sc, ATH_DBG_FATAL, + "Failed to register led:%s", led->name); + else + led->registered = 1; + return ret; +} + +static void ath_unregister_led(struct ath_led *led) +{ + if (led->registered) { + led_classdev_unregister(&led->led_cdev); + led->registered = 0; + } +} + +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); +} + +static void ath_init_leds(struct ath_softc *sc) +{ + char *trigger; + int ret; + + /* Configure gpio 1 for output */ + ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN, + AR_GPIO_OUTPUT_MUX_AS_OUTPUT); + /* LED off, active low */ + ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); + + INIT_DELAYED_WORK(&sc->ath_led_blink_work, ath_led_blink_work); + + trigger = ieee80211_get_radio_led_name(sc->hw); + snprintf(sc->radio_led.name, sizeof(sc->radio_led.name), + "ath9k-%s::radio", wiphy_name(sc->hw->wiphy)); + ret = ath_register_led(sc, &sc->radio_led, trigger); + sc->radio_led.led_type = ATH_LED_RADIO; + if (ret) + goto fail; + + trigger = ieee80211_get_assoc_led_name(sc->hw); + snprintf(sc->assoc_led.name, sizeof(sc->assoc_led.name), + "ath9k-%s::assoc", wiphy_name(sc->hw->wiphy)); + ret = ath_register_led(sc, &sc->assoc_led, trigger); + sc->assoc_led.led_type = ATH_LED_ASSOC; + if (ret) + goto fail; + + trigger = ieee80211_get_tx_led_name(sc->hw); + snprintf(sc->tx_led.name, sizeof(sc->tx_led.name), + "ath9k-%s::tx", wiphy_name(sc->hw->wiphy)); + ret = ath_register_led(sc, &sc->tx_led, trigger); + sc->tx_led.led_type = ATH_LED_TX; + if (ret) + goto fail; + + trigger = ieee80211_get_rx_led_name(sc->hw); + snprintf(sc->rx_led.name, sizeof(sc->rx_led.name), + "ath9k-%s::rx", wiphy_name(sc->hw->wiphy)); + ret = ath_register_led(sc, &sc->rx_led, trigger); + sc->rx_led.led_type = ATH_LED_RX; + if (ret) + goto fail; + + return; + +fail: + ath_deinit_leds(sc); +} + +void ath_radio_enable(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ieee80211_channel *channel = sc->hw->conf.channel; + int r; + + ath9k_ps_wakeup(sc); + 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", + channel->center_freq, r); + } + spin_unlock_bh(&sc->sc_resetlock); + + ath_update_txpow(sc); + if (ath_startrecv(sc) != 0) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to restart recv logic\n"); + return; + } + + if (sc->sc_flags & SC_OP_BEACONS) + ath_beacon_config(sc, NULL); /* restart beacons */ + + /* Re-Enable interrupts */ + ath9k_hw_set_interrupts(ah, sc->imask); + + /* Enable LED */ + ath9k_hw_cfg_output(ah, ATH_LED_PIN, + AR_GPIO_OUTPUT_MUX_AS_OUTPUT); + ath9k_hw_set_gpio(ah, ATH_LED_PIN, 0); + + ieee80211_wake_queues(sc->hw); + ath9k_ps_restore(sc); +} + +void ath_radio_disable(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ieee80211_channel *channel = sc->hw->conf.channel; + int r; + + ath9k_ps_wakeup(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); + + /* Disable interrupts */ + ath9k_hw_set_interrupts(ah, 0); + + ath_drain_all_txq(sc, false); /* clear pending tx frames */ + ath_stoprecv(sc); /* turn off frame recv */ + ath_flushrecv(sc); /* flush recv queue */ + + 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", + channel->center_freq, r); + } + spin_unlock_bh(&sc->sc_resetlock); + + ath9k_hw_phy_disable(ah); + ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); + ath9k_ps_restore(sc); +} + +#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) + +/*******************/ +/* Rfkill */ +/*******************/ + +static bool ath_is_rfkill_set(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + + return ath9k_hw_gpio_get(ah, ah->rfkill_gpio) == + 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) +{ + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) + cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll); + + 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; + } +} + +static int 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; + } + } + + return 0; +} +#endif /* CONFIG_RFKILL */ + +void ath_cleanup(struct ath_softc *sc) +{ + ath_detach(sc); + free_irq(sc->irq, sc); + ath_bus_cleanup(sc); + kfree(sc->sec_wiphy); + ieee80211_free_hw(sc->hw); +} + +void ath_detach(struct ath_softc *sc) +{ + struct ieee80211_hw *hw = sc->hw; + int i = 0; + + ath9k_ps_wakeup(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); + + for (i = 0; i < sc->num_sec_wiphy; i++) { + struct ath_wiphy *aphy = sc->sec_wiphy[i]; + if (aphy == NULL) + continue; + sc->sec_wiphy[i] = NULL; + ieee80211_unregister_hw(aphy->hw); + ieee80211_free_hw(aphy->hw); + } + ieee80211_unregister_hw(hw); + ath_rx_cleanup(sc); + ath_tx_cleanup(sc); + + tasklet_kill(&sc->intr_tq); + tasklet_kill(&sc->bcon_tasklet); + + if (!(sc->sc_flags & SC_OP_INVALID)) + ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); + + /* cleanup tx queues */ + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_cleanupq(sc, &sc->tx.txq[i]); + + ath9k_hw_detach(sc->sc_ah); + ath9k_exit_debug(sc); + ath9k_ps_restore(sc); +} + +static int ath9k_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + 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; + + return ath_reg_notifier_apply(wiphy, request, reg); +} + +static int ath_init(u16 devid, struct ath_softc *sc) +{ + struct ath_hw *ah = NULL; + int status; + int error = 0, i; + int csz = 0; + + /* XXX: hardware will not be ready until ath_open() being called */ + sc->sc_flags |= SC_OP_INVALID; + + if (ath9k_init_debug(sc) < 0) + printk(KERN_ERR "Unable to create debugfs files\n"); + + spin_lock_init(&sc->wiphy_lock); + spin_lock_init(&sc->sc_resetlock); + spin_lock_init(&sc->sc_serial_rw); + mutex_init(&sc->mutex); + tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc); + tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet, + (unsigned long)sc); + + /* + * Cache line size is used to size and align various + * structures used to communicate with the hardware. + */ + ath_read_cachesize(sc, &csz); + /* XXX assert csz is non-zero */ + sc->cachelsz = csz << 2; /* convert to bytes */ + + ah = ath9k_hw_attach(devid, sc, &status); + if (ah == NULL) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to attach hardware; HAL status %d\n", status); + error = -ENXIO; + goto bad; + } + sc->sc_ah = ah; + + /* Get the hardware key cache size. */ + sc->keymax = ah->caps.keycache_size; + if (sc->keymax > ATH_KEYMAX) { + DPRINTF(sc, ATH_DBG_ANY, + "Warning, using only %u entries in %u key cache\n", + ATH_KEYMAX, sc->keymax); + sc->keymax = ATH_KEYMAX; + } + + /* + * Reset the key cache since some parts do not + * reset the contents on initial power up. + */ + 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; + + /* Setup rate tables */ + + ath_rate_attach(sc); + ath_setup_rates(sc, IEEE80211_BAND_2GHZ); + ath_setup_rates(sc, IEEE80211_BAND_5GHZ); + + /* + * Allocate hardware transmit queues: one queue for + * beacon frames and one data queue for each QoS + * priority. Note that the hal handles reseting + * these queues at the needed time. + */ + sc->beacon.beaconq = ath_beaconq_setup(ah); + if (sc->beacon.beaconq == -1) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to setup a beacon xmit queue\n"); + error = -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; + goto bad2; + } + + sc->config.cabqReadytime = ATH_CABQ_READY_TIME; + ath_cabq_update(sc); + + for (i = 0; i < ARRAY_SIZE(sc->tx.hwq_map); i++) + sc->tx.hwq_map[i] = -1; + + /* Setup data queues */ + /* NB: ensure BK queue is the lowest priority h/w queue */ + 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; + 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; + 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; + 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; + goto bad2; + } + + /* Initializes the noise floor to a reasonable default value. + * Later on this will be updated during ANI processing. */ + + sc->ani.noise_floor = ATH_DEFAULT_NOISE_FLOOR; + setup_timer(&sc->ani.timer, ath_ani_calibrate, (unsigned long)sc); + + if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, + ATH9K_CIPHER_TKIP, NULL)) { + /* + * Whether we should enable h/w TKIP MIC. + * XXX: if we don't support WME TKIP MIC, then we wouldn't + * report WMM capable, so it's always safe to turn on + * TKIP MIC in this case. + */ + ath9k_hw_setcapability(sc->sc_ah, ATH9K_CAP_TKIP_MIC, + 0, 1, NULL); + } + + /* + * Check whether the separate key cache entries + * are required to handle both tx+rx MIC keys. + * With split mic keys the number of stations is limited + * to 27 otherwise 59. + */ + if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, + ATH9K_CIPHER_TKIP, NULL) + && ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, + ATH9K_CIPHER_MIC, NULL) + && ath9k_hw_getcapability(ah, ATH9K_CAP_TKIP_SPLIT, + 0, NULL)) + sc->splitmic = 1; + + /* turn on mcast key search if possible */ + if (!ath9k_hw_getcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL)) + (void)ath9k_hw_setcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 1, + 1, NULL); + + sc->config.txpowlimit = ATH_TXPOWER_MAX; + + /* 11n Capabilities */ + if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) { + sc->sc_flags |= SC_OP_TXAGGR; + sc->sc_flags |= SC_OP_RXAGGR; + } + + sc->tx_chainmask = ah->caps.tx_chainmask; + sc->rx_chainmask = ah->caps.rx_chainmask; + + ath9k_hw_setcapability(ah, ATH9K_CAP_DIVERSITY, 1, true, NULL); + sc->rx.defant = ath9k_hw_getdefantenna(ah); + + if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) + memcpy(sc->bssidmask, ath_bcast_mac, ETH_ALEN); + + sc->beacon.slottime = ATH9K_SLOT_TIME_9; /* default to short slot time */ + + /* initialize beacon slots */ + for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) { + sc->beacon.bslot[i] = NULL; + sc->beacon.bslot_aphy[i] = NULL; + } + + /* setup channels and rates */ + + sc->sbands[IEEE80211_BAND_2GHZ].channels = ath9k_2ghz_chantable; + sc->sbands[IEEE80211_BAND_2GHZ].bitrates = + sc->rates[IEEE80211_BAND_2GHZ]; + sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; + sc->sbands[IEEE80211_BAND_2GHZ].n_channels = + ARRAY_SIZE(ath9k_2ghz_chantable); + + if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) { + sc->sbands[IEEE80211_BAND_5GHZ].channels = ath9k_5ghz_chantable; + sc->sbands[IEEE80211_BAND_5GHZ].bitrates = + sc->rates[IEEE80211_BAND_5GHZ]; + sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ; + sc->sbands[IEEE80211_BAND_5GHZ].n_channels = + ARRAY_SIZE(ath9k_5ghz_chantable); + } + + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BT_COEX) + ath9k_hw_btcoex_enable(sc->sc_ah); + + return 0; +bad2: + /* cleanup tx queues */ + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_cleanupq(sc, &sc->tx.txq[i]); +bad: + if (ah) + ath9k_hw_detach(ah); + ath9k_exit_debug(sc); + + return error; +} + +void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) +{ + hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | + IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK | + IEEE80211_HW_SPECTRUM_MGMT; + + if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || modparam_nohwcrypt) + hw->flags |= IEEE80211_HW_MFP_CAPABLE; + + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_MESH_POINT); + + hw->queues = 4; + hw->max_rates = 4; + hw->channel_change_time = 5000; + hw->max_listen_interval = 10; + hw->max_rate_tries = ATH_11N_TXMAXTRY; + hw->sta_data_size = sizeof(struct ath_node); + hw->vif_data_size = sizeof(struct ath_vif); + + hw->rate_control_algorithm = "ath9k_rate_control"; + + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &sc->sbands[IEEE80211_BAND_2GHZ]; + if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = + &sc->sbands[IEEE80211_BAND_5GHZ]; +} + +int ath_attach(u16 devid, struct ath_softc *sc) +{ + struct ieee80211_hw *hw = sc->hw; + int error = 0, i; + struct ath_regulatory *reg; + + DPRINTF(sc, ATH_DBG_CONFIG, "Attach ATH hw\n"); + + error = ath_init(devid, 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); + + 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)) + setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap); + } + + /* initialize tx/rx engine */ + error = ath_tx_init(sc, ATH_TXBUF); + if (error != 0) + goto error_attach; + + error = ath_rx_init(sc, ATH_RXBUF); + 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); + + error = ieee80211_register_hw(hw); + + if (!ath_is_world_regd(reg)) { + error = regulatory_hint(hw->wiphy, reg->alpha2); + if (error) + goto error_attach; + } + + /* Initialize LED control */ + ath_init_leds(sc); + + + return 0; + +error_attach: + /* cleanup tx queues */ + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_cleanupq(sc, &sc->tx.txq[i]); + + ath9k_hw_detach(sc->sc_ah); + ath9k_exit_debug(sc); + + return error; +} + +int ath_reset(struct ath_softc *sc, bool retry_tx) +{ + struct ath_hw *ah = sc->sc_ah; + struct ieee80211_hw *hw = sc->hw; + int r; + + ath9k_hw_set_interrupts(ah, 0); + ath_drain_all_txq(sc, retry_tx); + ath_stoprecv(sc); + ath_flushrecv(sc); + + spin_lock_bh(&sc->sc_resetlock); + 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); + spin_unlock_bh(&sc->sc_resetlock); + + if (ath_startrecv(sc) != 0) + DPRINTF(sc, ATH_DBG_FATAL, "Unable to start recv logic\n"); + + /* + * We may be doing a reset in response to a request + * that changes the channel so update any state that + * might change as a result. + */ + ath_cache_conf_rate(sc, &hw->conf); + + ath_update_txpow(sc); + + if (sc->sc_flags & SC_OP_BEACONS) + ath_beacon_config(sc, NULL); /* restart beacons */ + + ath9k_hw_set_interrupts(ah, sc->imask); + + if (retry_tx) { + int i; + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { + if (ATH_TXQ_SETUP(sc, i)) { + spin_lock_bh(&sc->tx.txq[i].axq_lock); + ath_txq_schedule(sc, &sc->tx.txq[i]); + spin_unlock_bh(&sc->tx.txq[i].axq_lock); + } + } + } + + return r; +} + +/* + * This function will allocate both the DMA descriptor structure, and the + * buffers it contains. These are used to contain the descriptors used + * by the system. +*/ +int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, + struct list_head *head, const char *name, + int nbuf, int ndesc) +{ +#define DS2PHYS(_dd, _ds) \ + ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) +#define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0) +#define ATH_DESC_4KB_BOUND_NUM_SKIPPED(_len) ((_len) / 4096) + + struct ath_desc *ds; + struct ath_buf *bf; + int i, bsize, error; + + DPRINTF(sc, ATH_DBG_CONFIG, "%s DMA: %u buffers %u desc/buf\n", + name, nbuf, ndesc); + + INIT_LIST_HEAD(head); + /* ath_desc must be a multiple of DWORDs */ + if ((sizeof(struct ath_desc) % 4) != 0) { + DPRINTF(sc, ATH_DBG_FATAL, "ath_desc not DWORD aligned\n"); + ASSERT((sizeof(struct ath_desc) % 4) == 0); + error = -ENOMEM; + goto fail; + } + + dd->dd_desc_len = sizeof(struct ath_desc) * nbuf * ndesc; + + /* + * Need additional DMA memory because we can't use + * descriptors that cross the 4K page boundary. Assume + * one skipped descriptor per 4K page. + */ + if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_4KB_SPLITTRANS)) { + u32 ndesc_skipped = + ATH_DESC_4KB_BOUND_NUM_SKIPPED(dd->dd_desc_len); + u32 dma_len; + + while (ndesc_skipped) { + dma_len = ndesc_skipped * sizeof(struct ath_desc); + dd->dd_desc_len += dma_len; + + ndesc_skipped = ATH_DESC_4KB_BOUND_NUM_SKIPPED(dma_len); + }; + } + + /* allocate descriptors */ + dd->dd_desc = dma_alloc_coherent(sc->dev, dd->dd_desc_len, + &dd->dd_desc_paddr, GFP_KERNEL); + if (dd->dd_desc == NULL) { + error = -ENOMEM; + goto fail; + } + ds = dd->dd_desc; + DPRINTF(sc, ATH_DBG_CONFIG, "%s DMA map: %p (%u) -> %llx (%u)\n", + name, ds, (u32) dd->dd_desc_len, + ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len); + + /* allocate buffers */ + bsize = sizeof(struct ath_buf) * nbuf; + bf = kzalloc(bsize, GFP_KERNEL); + if (bf == NULL) { + error = -ENOMEM; + goto fail2; + } + dd->dd_bufptr = bf; + + for (i = 0; i < nbuf; i++, bf++, ds += ndesc) { + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(dd, ds); + + if (!(sc->sc_ah->caps.hw_caps & + ATH9K_HW_CAP_4KB_SPLITTRANS)) { + /* + * Skip descriptor addresses which can cause 4KB + * boundary crossing (addr + length) with a 32 dword + * descriptor fetch. + */ + while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) { + ASSERT((caddr_t) bf->bf_desc < + ((caddr_t) dd->dd_desc + + dd->dd_desc_len)); + + ds += ndesc; + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(dd, ds); + } + } + list_add_tail(&bf->list, head); + } + return 0; +fail2: + dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc, + dd->dd_desc_paddr); +fail: + memset(dd, 0, sizeof(*dd)); + return error; +#undef ATH_DESC_4KB_BOUND_CHECK +#undef ATH_DESC_4KB_BOUND_NUM_SKIPPED +#undef DS2PHYS +} + +void ath_descdma_cleanup(struct ath_softc *sc, + struct ath_descdma *dd, + struct list_head *head) +{ + dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc, + dd->dd_desc_paddr); + + INIT_LIST_HEAD(head); + kfree(dd->dd_bufptr); + memset(dd, 0, sizeof(*dd)); +} + +int ath_get_hal_qnum(u16 queue, struct ath_softc *sc) +{ + int qnum; + + switch (queue) { + case 0: + qnum = sc->tx.hwq_map[ATH9K_WME_AC_VO]; + break; + case 1: + qnum = sc->tx.hwq_map[ATH9K_WME_AC_VI]; + break; + case 2: + qnum = sc->tx.hwq_map[ATH9K_WME_AC_BE]; + break; + case 3: + qnum = sc->tx.hwq_map[ATH9K_WME_AC_BK]; + break; + default: + qnum = sc->tx.hwq_map[ATH9K_WME_AC_BE]; + break; + } + + return qnum; +} + +int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc) +{ + int qnum; + + switch (queue) { + case ATH9K_WME_AC_VO: + qnum = 0; + break; + case ATH9K_WME_AC_VI: + qnum = 1; + break; + case ATH9K_WME_AC_BE: + qnum = 2; + break; + case ATH9K_WME_AC_BK: + qnum = 3; + break; + default: + qnum = -1; + break; + } + + return qnum; +} + +/* XXX: Remove me once we don't depend on ath9k_channel for all + * this redundant data */ +void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw, + struct ath9k_channel *ichan) +{ + struct ieee80211_channel *chan = hw->conf.channel; + struct ieee80211_conf *conf = &hw->conf; + + ichan->channel = chan->center_freq; + ichan->chan = chan; + + if (chan->band == IEEE80211_BAND_2GHZ) { + ichan->chanmode = CHANNEL_G; + ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM; + } else { + ichan->chanmode = CHANNEL_A; + ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM; + } + + sc->tx_chan_width = ATH9K_HT_MACMODE_20; + + if (conf_is_ht(conf)) { + if (conf_is_ht40(conf)) + sc->tx_chan_width = ATH9K_HT_MACMODE_2040; + + ichan->chanmode = ath_get_extchanmode(sc, chan, + conf->channel_type); + } +} + +/**********************/ +/* mac80211 callbacks */ +/**********************/ + +static int ath9k_start(struct ieee80211_hw *hw) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ieee80211_channel *curchan = hw->conf.channel; + struct ath9k_channel *init_channel; + int r, pos; + + DPRINTF(sc, ATH_DBG_CONFIG, "Starting driver with " + "initial channel: %d MHz\n", curchan->center_freq); + + mutex_lock(&sc->mutex); + + if (ath9k_wiphy_started(sc)) { + if (sc->chan_idx == curchan->hw_value) { + /* + * Already on the operational channel, the new wiphy + * can be marked active. + */ + aphy->state = ATH_WIPHY_ACTIVE; + ieee80211_wake_queues(hw); + } else { + /* + * Another wiphy is on another channel, start the new + * wiphy in paused state. + */ + aphy->state = ATH_WIPHY_PAUSED; + ieee80211_stop_queues(hw); + } + mutex_unlock(&sc->mutex); + return 0; + } + aphy->state = ATH_WIPHY_ACTIVE; + + /* setup initial channel */ + + pos = curchan->hw_value; + + sc->chan_idx = pos; + init_channel = &sc->sc_ah->channels[pos]; + ath9k_update_ichannel(sc, hw, init_channel); + + /* Reset SERDES registers */ + ath9k_hw_configpcipowersave(sc->sc_ah, 0); + + /* + * The basic interface to setting the hardware in a good + * state is ``reset''. On return the hardware is known to + * be powered up and with interrupts disabled. This must + * be followed by initialization of the appropriate bits + * and then setup of the interrupt mask. + */ + spin_lock_bh(&sc->sc_resetlock); + r = ath9k_hw_reset(sc->sc_ah, init_channel, false); + if (r) { + DPRINTF(sc, ATH_DBG_FATAL, + "Unable to reset hardware; reset status %u " + "(freq %u MHz)\n", r, + curchan->center_freq); + spin_unlock_bh(&sc->sc_resetlock); + goto mutex_unlock; + } + spin_unlock_bh(&sc->sc_resetlock); + + /* + * This is needed only to setup initial state + * but it's best done after a reset. + */ + ath_update_txpow(sc); + + /* + * Setup the hardware after reset: + * The receive engine is set going. + * Frame transmit is handled entirely + * in the frame output path; there's nothing to do + * here except setup the interrupt mask. + */ + if (ath_startrecv(sc) != 0) { + DPRINTF(sc, ATH_DBG_FATAL, "Unable to start recv logic\n"); + r = -EIO; + goto mutex_unlock; + } + + /* Setup our intr mask. */ + sc->imask = ATH9K_INT_RX | ATH9K_INT_TX + | ATH9K_INT_RXEOL | ATH9K_INT_RXORN + | ATH9K_INT_FATAL | ATH9K_INT_GLOBAL; + + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_GTT) + sc->imask |= ATH9K_INT_GTT; + + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) + sc->imask |= ATH9K_INT_CST; + + ath_cache_conf_rate(sc, &hw->conf); + + sc->sc_flags &= ~SC_OP_INVALID; + + /* Disable BMISS interrupt when we're not associated */ + sc->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS); + ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); + + ieee80211_wake_queues(hw); + +#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) + r = ath_start_rfkill_poll(sc); +#endif + +mutex_unlock: + mutex_unlock(&sc->mutex); + + return r; +} + +static int ath9k_tx(struct ieee80211_hw *hw, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ath_tx_control txctl; + int hdrlen, padsize; + + if (aphy->state != ATH_WIPHY_ACTIVE && aphy->state != ATH_WIPHY_SCAN) { + printk(KERN_DEBUG "ath9k: %s: TX in unexpected wiphy state " + "%d\n", wiphy_name(hw->wiphy), aphy->state); + goto exit; + } + + memset(&txctl, 0, sizeof(struct ath_tx_control)); + + /* + * As a temporary workaround, assign seq# here; this will likely need + * to be cleaned up to work better with Beacon transmission and virtual + * BSSes. + */ + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) + sc->tx.seq_no += 0x10; + hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no); + } + + /* Add the padding after the header if this is not already done */ + hdrlen = ieee80211_get_hdrlen_from_skb(skb); + if (hdrlen & 3) { + padsize = hdrlen % 4; + if (skb_headroom(skb) < padsize) + return -1; + skb_push(skb, padsize); + memmove(skb->data, skb->data + padsize, hdrlen); + } + + /* Check if a tx queue is available */ + + txctl.txq = ath_test_get_txq(sc, skb); + if (!txctl.txq) + goto exit; + + DPRINTF(sc, ATH_DBG_XMIT, "transmitting packet, skb: %p\n", skb); + + if (ath_tx_start(hw, skb, &txctl) != 0) { + DPRINTF(sc, ATH_DBG_XMIT, "TX failed\n"); + goto exit; + } + + return 0; +exit: + dev_kfree_skb_any(skb); + return 0; +} + +static void ath9k_stop(struct ieee80211_hw *hw) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + + aphy->state = ATH_WIPHY_INACTIVE; + + if (sc->sc_flags & SC_OP_INVALID) { + DPRINTF(sc, ATH_DBG_ANY, "Device not present\n"); + return; + } + + mutex_lock(&sc->mutex); + + ieee80211_stop_queues(hw); + + if (ath9k_wiphy_started(sc)) { + mutex_unlock(&sc->mutex); + return; /* another wiphy still in use */ + } + + /* make sure h/w will not generate any interrupt + * before setting the invalid flag. */ + ath9k_hw_set_interrupts(sc->sc_ah, 0); + + if (!(sc->sc_flags & SC_OP_INVALID)) { + ath_drain_all_txq(sc, false); + ath_stoprecv(sc); + ath9k_hw_phy_disable(sc->sc_ah); + } 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); + + sc->sc_flags |= SC_OP_INVALID; + + mutex_unlock(&sc->mutex); + + DPRINTF(sc, ATH_DBG_CONFIG, "Driver halt\n"); +} + +static int ath9k_add_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ath_vif *avp = (void *)conf->vif->drv_priv; + enum nl80211_iftype ic_opmode = NL80211_IFTYPE_UNSPECIFIED; + int ret = 0; + + mutex_lock(&sc->mutex); + + if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) && + sc->nvifs > 0) { + ret = -ENOBUFS; + goto out; + } + + switch (conf->type) { + case NL80211_IFTYPE_STATION: + ic_opmode = NL80211_IFTYPE_STATION; + break; + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: + if (sc->nbcnvifs >= ATH_BCBUF) { + ret = -ENOBUFS; + goto out; + } + ic_opmode = conf->type; + break; + default: + DPRINTF(sc, ATH_DBG_FATAL, + "Interface type %d not yet supported\n", conf->type); + ret = -EOPNOTSUPP; + goto out; + } + + DPRINTF(sc, ATH_DBG_CONFIG, "Attach a VIF of type: %d\n", ic_opmode); + + /* Set the VIF opmode */ + avp->av_opmode = ic_opmode; + avp->av_bslot = -1; + + sc->nvifs++; + + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) + ath9k_set_bssid_mask(hw); + + if (sc->nvifs > 1) + goto out; /* skip global settings for secondary vif */ + + if (ic_opmode == NL80211_IFTYPE_AP) { + ath9k_hw_set_tsfadjust(sc->sc_ah, 1); + sc->sc_flags |= SC_OP_TSF_RESET; + } + + /* Set the device opmode */ + sc->sc_ah->opmode = ic_opmode; + + /* + * Enable MIB interrupts when there are hardware phy counters. + * Note we only do this (at the moment) for station mode. + */ + 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_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)); + } + +out: + mutex_unlock(&sc->mutex); + return ret; +} + +static void ath9k_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ath_vif *avp = (void *)conf->vif->drv_priv; + int i; + + DPRINTF(sc, ATH_DBG_CONFIG, "Detach Interface\n"); + + mutex_lock(&sc->mutex); + + /* Stop ANI */ + del_timer_sync(&sc->ani.timer); + + /* Reclaim beacon resources */ + if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) || + (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) || + (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT)) { + ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq); + ath_beacon_return(sc, avp); + } + + sc->sc_flags &= ~SC_OP_BEACONS; + + for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) { + if (sc->beacon.bslot[i] == conf->vif) { + printk(KERN_DEBUG "%s: vif had allocated beacon " + "slot\n", __func__); + sc->beacon.bslot[i] = NULL; + sc->beacon.bslot_aphy[i] = NULL; + } + } + + sc->nvifs--; + + mutex_unlock(&sc->mutex); +} + +static int ath9k_config(struct ieee80211_hw *hw, u32 changed) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ieee80211_conf *conf = &hw->conf; + struct ath_hw *ah = sc->sc_ah; + + mutex_lock(&sc->mutex); + + if (changed & IEEE80211_CONF_CHANGE_PS) { + if (conf->flags & IEEE80211_CONF_PS) { + if (!(ah->caps.hw_caps & + ATH9K_HW_CAP_AUTOSLEEP)) { + if ((sc->imask & ATH9K_INT_TIM_TIMER) == 0) { + sc->imask |= ATH9K_INT_TIM_TIMER; + ath9k_hw_set_interrupts(sc->sc_ah, + sc->imask); + } + ath9k_hw_setrxabort(sc->sc_ah, 1); + } + ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP); + } else { + 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; + if (sc->imask & ATH9K_INT_TIM_TIMER) { + sc->imask &= ~ATH9K_INT_TIM_TIMER; + ath9k_hw_set_interrupts(sc->sc_ah, + sc->imask); + } + } + } + } + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + struct ieee80211_channel *curchan = hw->conf.channel; + int pos = curchan->hw_value; + + aphy->chan_idx = pos; + aphy->chan_is_ht = conf_is_ht(conf); + + if (aphy->state == ATH_WIPHY_SCAN || + aphy->state == ATH_WIPHY_ACTIVE) + ath9k_wiphy_pause_all_forced(sc, aphy); + else { + /* + * Do not change operational channel based on a paused + * wiphy changes. + */ + goto skip_chan_change; + } + + DPRINTF(sc, ATH_DBG_CONFIG, "Set channel: %d MHz\n", + curchan->center_freq); + + /* XXX: remove me eventualy */ + ath9k_update_ichannel(sc, hw, &sc->sc_ah->channels[pos]); + + ath_update_chainmask(sc, conf_is_ht(conf)); + + if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) { + DPRINTF(sc, ATH_DBG_FATAL, "Unable to set channel\n"); + mutex_unlock(&sc->mutex); + return -EINVAL; + } + } + +skip_chan_change: + 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); + } + + /* Only legacy IBSS for now */ + if (vif->type == NL80211_IFTYPE_ADHOC) + ath_update_chainmask(sc, 0); + + mutex_unlock(&sc->mutex); + + return 0; +} + +#define SUPPORTED_FILTERS \ + (FIF_PROMISC_IN_BSS | \ + FIF_ALLMULTI | \ + FIF_CONTROL | \ + FIF_OTHER_BSS | \ + FIF_BCN_PRBRESP_PROMISC | \ + FIF_FCSFAIL) + +/* FIXME: sc->sc_full_reset ? */ +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) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + u32 rfilt; + + changed_flags &= SUPPORTED_FILTERS; + *total_flags &= SUPPORTED_FILTERS; + + sc->rx.rxfilter = *total_flags; + rfilt = ath_calcrxfilter(sc); + ath9k_hw_setrxfilter(sc->sc_ah, rfilt); + + DPRINTF(sc, ATH_DBG_CONFIG, "Set HW RX filter: 0x%x\n", sc->rx.rxfilter); +} + +static void ath9k_sta_notify(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, + struct ieee80211_sta *sta) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + + switch (cmd) { + case STA_NOTIFY_ADD: + ath_node_attach(sc, sta); + break; + case STA_NOTIFY_REMOVE: + ath_node_detach(sc, sta); + break; + default: + break; + } +} + +static int ath9k_conf_tx(struct ieee80211_hw *hw, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ath9k_tx_queue_info qi; + int ret = 0, qnum; + + if (queue >= WME_NUM_AC) + return 0; + + mutex_lock(&sc->mutex); + + memset(&qi, 0, sizeof(struct ath9k_tx_queue_info)); + + qi.tqi_aifs = params->aifs; + qi.tqi_cwmin = params->cw_min; + qi.tqi_cwmax = params->cw_max; + qi.tqi_burstTime = params->txop; + qnum = ath_get_hal_qnum(queue, sc); + + DPRINTF(sc, ATH_DBG_CONFIG, + "Configure tx [queue/halq] [%d/%d], " + "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n", + queue, qnum, params->aifs, params->cw_min, + params->cw_max, params->txop); + + ret = ath_txq_update(sc, qnum, &qi); + if (ret) + DPRINTF(sc, ATH_DBG_FATAL, "TXQ Update failed\n"); + + mutex_unlock(&sc->mutex); + + return ret; +} + +static int ath9k_set_key(struct ieee80211_hw *hw, + enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + int ret = 0; + + if (modparam_nohwcrypt) + return -ENOSPC; + + mutex_lock(&sc->mutex); + ath9k_ps_wakeup(sc); + DPRINTF(sc, ATH_DBG_CONFIG, "Set HW Key\n"); + + switch (cmd) { + case SET_KEY: + ret = ath_key_config(sc, vif, sta, key); + if (ret >= 0) { + key->hw_key_idx = ret; + /* push IV and Michael MIC generation to stack */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + if (key->alg == ALG_TKIP) + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + if (sc->sc_ah->sw_mgmt_crypto && key->alg == ALG_CCMP) + key->flags |= IEEE80211_KEY_FLAG_SW_MGMT; + ret = 0; + } + break; + case DISABLE_KEY: + ath_key_delete(sc, key); + break; + default: + ret = -EINVAL; + } + + ath9k_ps_restore(sc); + mutex_unlock(&sc->mutex); + + return ret; +} + +static void ath9k_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changed) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + + mutex_lock(&sc->mutex); + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + DPRINTF(sc, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n", + bss_conf->use_short_preamble); + if (bss_conf->use_short_preamble) + sc->sc_flags |= SC_OP_PREAMBLE_SHORT; + else + sc->sc_flags &= ~SC_OP_PREAMBLE_SHORT; + } + + if (changed & BSS_CHANGED_ERP_CTS_PROT) { + DPRINTF(sc, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n", + bss_conf->use_cts_prot); + if (bss_conf->use_cts_prot && + hw->conf.channel->band != IEEE80211_BAND_5GHZ) + sc->sc_flags |= SC_OP_PROTECT_ENABLE; + else + sc->sc_flags &= ~SC_OP_PROTECT_ENABLE; + } + + if (changed & BSS_CHANGED_ASSOC) { + DPRINTF(sc, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n", + bss_conf->assoc); + ath9k_bss_assoc_info(sc, vif, bss_conf); + } + + mutex_unlock(&sc->mutex); +} + +static u64 ath9k_get_tsf(struct ieee80211_hw *hw) +{ + u64 tsf; + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + + mutex_lock(&sc->mutex); + tsf = ath9k_hw_gettsf64(sc->sc_ah); + mutex_unlock(&sc->mutex); + + return tsf; +} + +static void ath9k_set_tsf(struct ieee80211_hw *hw, u64 tsf) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + + mutex_lock(&sc->mutex); + ath9k_hw_settsf64(sc->sc_ah, tsf); + mutex_unlock(&sc->mutex); +} + +static void ath9k_reset_tsf(struct ieee80211_hw *hw) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + + mutex_lock(&sc->mutex); + ath9k_hw_reset_tsf(sc->sc_ah); + mutex_unlock(&sc->mutex); +} + +static int ath9k_ampdu_action(struct ieee80211_hw *hw, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, + u16 tid, u16 *ssn) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + int ret = 0; + + switch (action) { + case IEEE80211_AMPDU_RX_START: + if (!(sc->sc_flags & SC_OP_RXAGGR)) + ret = -ENOTSUPP; + break; + 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); + 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"); + + ieee80211_stop_tx_ba_cb_irqsafe(hw, sta->addr, tid); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + ath_tx_aggr_resume(sc, sta, tid); + break; + default: + DPRINTF(sc, ATH_DBG_FATAL, "Unknown AMPDU action\n"); + } + + return ret; +} + +static void ath9k_sw_scan_start(struct ieee80211_hw *hw) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + + 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. + */ + return; + } + + aphy->state = ATH_WIPHY_SCAN; + ath9k_wiphy_pause_all_forced(sc, aphy); + + mutex_lock(&sc->mutex); + sc->sc_flags |= SC_OP_SCANNING; + mutex_unlock(&sc->mutex); +} + +static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + + mutex_lock(&sc->mutex); + aphy->state = ATH_WIPHY_ACTIVE; + sc->sc_flags &= ~SC_OP_SCANNING; + mutex_unlock(&sc->mutex); +} + +struct ieee80211_ops ath9k_ops = { + .tx = ath9k_tx, + .start = ath9k_start, + .stop = ath9k_stop, + .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, + .bss_info_changed = ath9k_bss_info_changed, + .set_key = ath9k_set_key, + .get_tsf = ath9k_get_tsf, + .set_tsf = ath9k_set_tsf, + .reset_tsf = ath9k_reset_tsf, + .ampdu_action = ath9k_ampdu_action, + .sw_scan_start = ath9k_sw_scan_start, + .sw_scan_complete = ath9k_sw_scan_complete, +}; + +static struct { + u32 version; + const char * name; +} ath_mac_bb_names[] = { + { AR_SREV_VERSION_5416_PCI, "5416" }, + { AR_SREV_VERSION_5416_PCIE, "5418" }, + { AR_SREV_VERSION_9100, "9100" }, + { AR_SREV_VERSION_9160, "9160" }, + { AR_SREV_VERSION_9280, "9280" }, + { AR_SREV_VERSION_9285, "9285" } +}; + +static struct { + u16 version; + const char * name; +} ath_rf_names[] = { + { 0, "5133" }, + { AR_RAD5133_SREV_MAJOR, "5133" }, + { AR_RAD5122_SREV_MAJOR, "5122" }, + { AR_RAD2133_SREV_MAJOR, "2133" }, + { AR_RAD2122_SREV_MAJOR, "2122" } +}; + +/* + * Return the MAC/BB name. "????" is returned if the MAC/BB is unknown. + */ +const char * +ath_mac_bb_name(u32 mac_bb_version) +{ + int i; + + for (i=0; i Date: Mon, 13 Apr 2009 21:56:25 +0530 Subject: ath9k: Handle ASPM properly for RFKILL Radio enable/disable have to handle ASPM state properly. This patch fixes it. Signed-off-by: Sujith Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 8b6a7ea4e59..1a9bf7ece4a 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1089,10 +1089,10 @@ void ath_radio_enable(struct ath_softc *sc) int r; ath9k_ps_wakeup(sc); - spin_lock_bh(&sc->sc_resetlock); + ath9k_hw_configpcipowersave(ah, 0); + 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) ", @@ -1154,6 +1154,7 @@ void ath_radio_disable(struct ath_softc *sc) spin_unlock_bh(&sc->sc_resetlock); ath9k_hw_phy_disable(ah); + ath9k_hw_configpcipowersave(ah, 1); ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); ath9k_ps_restore(sc); } -- cgit v1.2.3-70-g09d2 From 9c07a7777f44c7e39accec5ad8c4293d6a9b2a47 Mon Sep 17 00:00:00 2001 From: Sujith Date: Mon, 13 Apr 2009 21:56:36 +0530 Subject: ath9k: Fix bug in scan termination A full HW reset needs to be done on termination of a scan run. Not setting SC_OP_FULL_RESET resulted in doing a fast channel change. Cc: stable@kernel.org Signed-off-by: Sujith Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 1a9bf7ece4a..b2d129f5033 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2749,6 +2749,7 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) mutex_lock(&sc->mutex); aphy->state = ATH_WIPHY_ACTIVE; sc->sc_flags &= ~SC_OP_SCANNING; + sc->sc_flags |= SC_OP_FULL_RESET; mutex_unlock(&sc->mutex); } -- cgit v1.2.3-70-g09d2 From 415f738ecf41b427921b503ecfd427e26f89dc23 Mon Sep 17 00:00:00 2001 From: Sujith Date: Mon, 13 Apr 2009 21:56:46 +0530 Subject: ath9k: Initialize ANI timers The various ANI timers have to be initialized properly when starting the calibration timer. Cc: stable@kernel.org Signed-off-by: Sujith Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index b2d129f5033..9564b73ded5 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -407,6 +407,18 @@ set_timer: 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 @@ -911,9 +923,7 @@ static void ath9k_bss_assoc_info(struct ath_softc *sc, 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)); + ath_start_ani(sc); } else { DPRINTF(sc, ATH_DBG_CONFIG, "Bss Info DISASSOC\n"); sc->curaid = 0; @@ -2239,12 +2249,8 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, 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) + ath_start_ani(sc); out: mutex_unlock(&sc->mutex); -- cgit v1.2.3-70-g09d2 From 379f04407c92d84f2506385b66fb9fc89ecd96c3 Mon Sep 17 00:00:00 2001 From: Sujith Date: Mon, 13 Apr 2009 21:56:48 +0530 Subject: ath9k: Cleanup calibration interface This patch cleans up the functions dealing with calibration, using proper return values. ath9k_hw_per_calibration(), ath9k_hw_calibrate now return bool values instead of setting error values in the function arguments. Signed-off-by: Sujith Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/calib.c | 27 +++++++++++++-------------- drivers/net/wireless/ath/ath9k/calib.h | 3 +-- drivers/net/wireless/ath/ath9k/main.c | 32 ++++++++++---------------------- 3 files changed, 24 insertions(+), 38 deletions(-) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 3195c0b9a6a..a0661bb9672 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -238,13 +238,12 @@ static void ath9k_hw_reset_calibration(struct ath_hw *ah, ah->cal_samples = 0; } -static void ath9k_hw_per_calibration(struct ath_hw *ah, +static bool ath9k_hw_per_calibration(struct ath_hw *ah, struct ath9k_channel *ichan, u8 rxchainmask, - struct hal_cal_list *currCal, - bool *isCalDone) + struct hal_cal_list *currCal) { - *isCalDone = false; + bool iscaldone = false; if (currCal->calState == CAL_RUNNING) { if (!(REG_READ(ah, AR_PHY_TIMING_CTRL4(0)) & @@ -263,7 +262,7 @@ static void ath9k_hw_per_calibration(struct ath_hw *ah, currCal->calData->calPostProc(ah, numChains); ichan->CalValid |= currCal->calData->calType; currCal->calState = CAL_DONE; - *isCalDone = true; + iscaldone = true; } else { ath9k_hw_setup_calibration(ah, currCal); } @@ -271,6 +270,8 @@ static void ath9k_hw_per_calibration(struct ath_hw *ah, } else if (!(ichan->CalValid & currCal->calData->calType)) { ath9k_hw_reset_calibration(ah, currCal); } + + return iscaldone; } /* Assumes you are talking about the currently configured channel */ @@ -841,23 +842,21 @@ static inline void ath9k_hw_9285_pa_cal(struct ath_hw *ah) } bool ath9k_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan, - u8 rxchainmask, bool longcal, - bool *isCalDone) + u8 rxchainmask, bool longcal) { + bool iscaldone = true; struct hal_cal_list *currCal = ah->cal_list_curr; - *isCalDone = true; - if (currCal && (currCal->calState == CAL_RUNNING || currCal->calState == CAL_WAITING)) { - ath9k_hw_per_calibration(ah, chan, rxchainmask, currCal, - isCalDone); - if (*isCalDone) { + iscaldone = ath9k_hw_per_calibration(ah, chan, + rxchainmask, currCal); + if (iscaldone) { ah->cal_list_curr = currCal = currCal->calNext; if (currCal->calState == CAL_WAITING) { - *isCalDone = false; + iscaldone = false; ath9k_hw_reset_calibration(ah, currCal); } } @@ -877,7 +876,7 @@ bool ath9k_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan, chan->channelFlags &= ~CHANNEL_CW_INT; } - return true; + return iscaldone; } static bool ar9285_clc(struct ath_hw *ah, struct ath9k_channel *chan) diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h index 1c74bd50700..ac71ed966a1 100644 --- a/drivers/net/wireless/ath/ath9k/calib.h +++ b/drivers/net/wireless/ath/ath9k/calib.h @@ -116,8 +116,7 @@ int16_t ath9k_hw_getnf(struct ath_hw *ah, void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah); s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan); bool ath9k_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan, - u8 rxchainmask, bool longcal, - bool *isCalDone); + u8 rxchainmask, bool longcal); bool ath9k_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 9564b73ded5..8bf2bf36fd6 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -367,28 +367,16 @@ static void ath_ani_calibrate(unsigned long data) /* 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); } } -- cgit v1.2.3-70-g09d2 From 85efc86eb7c6cbb1c8ce8d99b10b948be033fbb9 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 13 Apr 2009 21:41:46 -0400 Subject: atheros: fix propagation of bad EEPROM on regulatory init When the EEPROM is not in good condition we cannot continue so we currently bail out but only ath5k is bailing out properly. Both ath9k and ar9170 were proceeding and if a user were to run into this they'd see an obscure panic. Lets propagate the error as intended and make sure we inform the user by lifting the error message from debug to a kernel error. Stable note: You can find a port of this page here: http://bombadil.infradead.org/~mcgrof/patches/ath9k/ath9k-fix-eeprom.patch.txt Cc: stable@kernel.org Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ar9170/main.c | 2 ++ drivers/net/wireless/ath/ath9k/main.c | 5 +++-- drivers/net/wireless/ath/regd.c | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c index 8de0ff9f580..857416c8019 100644 --- a/drivers/net/wireless/ath/ar9170/main.c +++ b/drivers/net/wireless/ath/ar9170/main.c @@ -1649,6 +1649,8 @@ int ar9170_register(struct ar9170 *ar, struct device *pdev) err = ath_regd_init(&ar->regulatory, ar->hw->wiphy, ar9170_reg_notifier); + if (err) + goto err_out; err = ieee80211_register_hw(ar->hw); if (err) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 8bf2bf36fd6..2398d4f45f2 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1416,8 +1416,9 @@ static int ath_init(u16 devid, struct ath_softc *sc) 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)) + error = ath_regd_init(&sc->sc_ah->regulatory, sc->hw->wiphy, + ath9k_reg_notifier); + if (error) goto bad; /* default to MONITOR mode */ diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 4b5c851b81f..526c7f1308d 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -477,7 +477,7 @@ ath_regd_init(struct ath_regulatory *reg, u16 regdmn; if (!ath_regd_is_eeprom_valid(reg)) { - printk(KERN_DEBUG "ath: Invalid EEPROM contents\n"); + printk(KERN_ERR "ath: Invalid EEPROM contents\n"); return -EINVAL; } -- cgit v1.2.3-70-g09d2 From 57c4d7b4c4986037be51476b8e3025d5ba18d8b8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 23 Apr 2009 16:10:04 +0200 Subject: mac80211: clean up beacon interval settings We currently have two beacon interval configuration knobs: hw.conf.beacon_int and vif.bss_info.beacon_int. This is rather confusing, even though the former is used when we beacon ourselves and the latter when we are associated to an AP. This just deprecates the hw.conf.beacon_int setting in favour of always using vif.bss_info.beacon_int. Since it touches all the beaconing IBSS code anyway, we can also add support for the cfg80211 IBSS beacon interval configuration easily. NOTE: The hw.conf.beacon_int setting is retained for now due to drivers still using it -- I couldn't untangle all drivers, some are updated in this patch. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ar9170/mac.c | 6 +++--- drivers/net/wireless/ath/ar9170/main.c | 5 ++++- drivers/net/wireless/ath/ath5k/base.c | 5 ++++- drivers/net/wireless/ath/ath9k/ath9k.h | 2 ++ drivers/net/wireless/ath/ath9k/beacon.c | 9 +++------ drivers/net/wireless/ath/ath9k/main.c | 22 ++++++++++++---------- drivers/net/wireless/ath/ath9k/xmit.c | 2 +- drivers/net/wireless/b43/main.c | 12 +++++++----- drivers/net/wireless/b43legacy/main.c | 10 +++++----- drivers/net/wireless/iwlwifi/iwl-agn.c | 3 ++- drivers/net/wireless/iwlwifi/iwl-core.c | 3 +-- drivers/net/wireless/iwlwifi/iwl3945-base.c | 5 ++--- drivers/net/wireless/libertas_tf/main.c | 2 +- drivers/net/wireless/mac80211_hwsim.c | 20 ++++++++++++-------- include/net/mac80211.h | 14 ++++++++++++-- net/mac80211/cfg.c | 17 +++++------------ net/mac80211/ibss.c | 25 +++++++++++++++++-------- net/mac80211/main.c | 17 +++++++++++------ net/mac80211/mlme.c | 1 + net/mac80211/sta_info.c | 12 ++++++------ net/mac80211/tx.c | 2 +- 21 files changed, 112 insertions(+), 82 deletions(-) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ar9170/mac.c b/drivers/net/wireless/ath/ar9170/mac.c index c8fa3073169..0e5967dd119 100644 --- a/drivers/net/wireless/ath/ar9170/mac.c +++ b/drivers/net/wireless/ath/ar9170/mac.c @@ -316,9 +316,9 @@ int ar9170_set_beacon_timers(struct ar9170 *ar) u32 v = 0; u32 pretbtt = 0; - v |= ar->hw->conf.beacon_int; - if (ar->vif) { + v |= ar->vif->bss_conf.beacon_int; + switch (ar->vif->type) { case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_ADHOC: @@ -326,7 +326,7 @@ int ar9170_set_beacon_timers(struct ar9170 *ar) break; case NL80211_IFTYPE_AP: v |= BIT(24); - pretbtt = (ar->hw->conf.beacon_int - 6) << 16; + pretbtt = (ar->vif->bss_conf.beacon_int - 6) << 16; break; default: break; diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c index 1b60906b80c..49c729bc714 100644 --- a/drivers/net/wireless/ath/ar9170/main.c +++ b/drivers/net/wireless/ath/ar9170/main.c @@ -1337,7 +1337,7 @@ static int ar9170_op_config(struct ieee80211_hw *hw, u32 changed) goto out; } - if (changed & IEEE80211_CONF_CHANGE_BEACON_INTERVAL) { + if (changed & BSS_CHANGED_BEACON_INT) { err = ar9170_set_beacon_timers(ar); if (err) goto out; @@ -1499,6 +1499,9 @@ static void ar9170_op_bss_info_changed(struct ieee80211_hw *hw, #endif /* CONFIG_AR9170_LEDS */ } + if (changed & BSS_CHANGED_BEACON_INT) + err = ar9170_set_beacon_timers(ar); + if (changed & BSS_CHANGED_HT) { /* TODO */ err = 0; diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index c8c658bfcf9..e4b1d9efa42 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -2756,7 +2756,6 @@ ath5k_config(struct ieee80211_hw *hw, u32 changed) mutex_lock(&sc->lock); - sc->bintval = conf->beacon_int; sc->power_level = conf->power_level; ret = ath5k_chan_set(sc, conf->channel); @@ -3083,6 +3082,10 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw, u32 changes) { struct ath5k_softc *sc = hw->priv; + + if (changes & BSS_CHANGED_BEACON_INT) + sc->bintval = bss_conf->beacon_int; + if (changes & BSS_CHANGED_ASSOC) { mutex_lock(&sc->lock); sc->assoc = bss_conf->assoc; diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index c92d46fa9d5..d5084ddf44f 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -590,6 +590,8 @@ struct ath_softc { int led_on_cnt; int led_off_cnt; + int beacon_interval; + struct ath_rfkill rf_kill; struct ath_ani ani; struct ath9k_node_stats nodestats; diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index eb4759fc6a0..c17b8382e32 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -320,8 +320,7 @@ int ath_beacon_alloc(struct ath_wiphy *aphy, struct ieee80211_vif *vif) u64 tsfadjust; int intval; - intval = sc->hw->conf.beacon_int ? - sc->hw->conf.beacon_int : ATH_DEFAULT_BINTVAL; + intval = sc->beacon_interval ? : ATH_DEFAULT_BINTVAL; /* * Calculate the TSF offset for this beacon slot, i.e., the @@ -431,8 +430,7 @@ void ath_beacon_tasklet(unsigned long data) * on the tsf to safeguard against missing an swba. */ - intval = sc->hw->conf.beacon_int ? - sc->hw->conf.beacon_int : ATH_DEFAULT_BINTVAL; + intval = sc->beacon_interval ? : ATH_DEFAULT_BINTVAL; tsf = ath9k_hw_gettsf64(ah); tsftu = TSF_TO_TU(tsf>>32, tsf); @@ -711,8 +709,7 @@ void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif) /* Setup the beacon configuration parameters */ memset(&conf, 0, sizeof(struct ath_beacon_config)); - conf.beacon_interval = sc->hw->conf.beacon_int ? - sc->hw->conf.beacon_int : ATH_DEFAULT_BINTVAL; + conf.beacon_interval = sc->beacon_interval ? : ATH_DEFAULT_BINTVAL; conf.listen_interval = 1; conf.dtim_period = conf.beacon_interval; conf.dtim_count = 1; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 2398d4f45f2..28cc9cd52f3 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2358,16 +2358,6 @@ skip_chan_change: 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; @@ -2635,6 +2625,18 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, 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); } diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index faf2cab49ea..501493ffc3a 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -971,7 +971,7 @@ int ath_cabq_update(struct ath_softc *sc) else if (sc->config.cabqReadytime > ATH9K_READY_TIME_HI_BOUND) sc->config.cabqReadytime = ATH9K_READY_TIME_HI_BOUND; - qi.tqi_readyTime = (sc->hw->conf.beacon_int * + qi.tqi_readyTime = (sc->beacon_interval * sc->config.cabqReadytime) / 100; ath_txq_update(sc, qnum, &qi); diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index a97c6ff0f12..3c693e6ec90 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3468,11 +3468,6 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) if (phy->ops->set_rx_antenna) phy->ops->set_rx_antenna(dev, antenna); - /* Update templates for AP/mesh mode. */ - if (b43_is_mode(wl, NL80211_IFTYPE_AP) || - b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT)) - b43_set_beacon_int(dev, conf->beacon_int); - if (!!conf->radio_enabled != phy->radio_on) { if (conf->radio_enabled) { b43_software_rfkill(dev, RFKILL_STATE_UNBLOCKED); @@ -3556,6 +3551,13 @@ static void b43_op_bss_info_changed(struct ieee80211_hw *hw, goto out_unlock_mutex; b43_mac_suspend(dev); + /* Update templates for AP/mesh mode. */ + if (changed & BSS_CHANGED_BEACON_INT && + (b43_is_mode(wl, NL80211_IFTYPE_AP) || + b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT) || + b43_is_mode(wl, NL80211_IFTYPE_ADHOC))) + b43_set_beacon_int(dev, conf->beacon_int); + if (changed & BSS_CHANGED_BASIC_RATES) b43_update_basic_rates(dev, conf->basic_rates); diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index ee202b4f77b..276f314688f 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -2721,11 +2721,6 @@ static int b43legacy_op_dev_config(struct ieee80211_hw *hw, /* Antennas for RX and management frame TX. */ b43legacy_mgmtframe_txantenna(dev, antenna_tx); - /* Update templates for AP mode. */ - if (b43legacy_is_mode(wl, NL80211_IFTYPE_AP)) - b43legacy_set_beacon_int(dev, conf->beacon_int); - - if (!!conf->radio_enabled != phy->radio_on) { if (conf->radio_enabled) { b43legacy_radio_turn_on(dev); @@ -2827,6 +2822,11 @@ static void b43legacy_op_bss_info_changed(struct ieee80211_hw *hw, b43legacy_mac_suspend(dev); + if (changed & BSS_CHANGED_BEACON_INT && + (b43legacy_is_mode(wl, NL80211_IFTYPE_AP) || + b43legacy_is_mode(wl, NL80211_IFTYPE_ADHOC))) + b43legacy_set_beacon_int(dev, conf->beacon_int); + if (changed & BSS_CHANGED_BASIC_RATES) b43legacy_update_basic_rates(dev, conf->basic_rates); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 277dfc57fde..4920dcf7d7b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -567,7 +567,8 @@ static void iwl_setup_rxon_timing(struct iwl_priv *priv) beacon_int = iwl_adjust_beacon_interval(priv->beacon_int); priv->rxon_timing.atim_window = 0; } else { - beacon_int = iwl_adjust_beacon_interval(conf->beacon_int); + beacon_int = iwl_adjust_beacon_interval( + priv->vif->bss_conf.beacon_int); /* TODO: we need to get atim_window from upper stack * for now we set to 0 */ diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 3dec2d25fa3..cd8a5ed97c4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -1313,7 +1313,6 @@ int iwl_setup_mac(struct iwl_priv *priv) /* Default value; 4 EDCA QOS priorities */ hw->queues = 4; - hw->conf.beacon_int = 100; hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; if (priv->bands[IEEE80211_BAND_2GHZ].n_channels) @@ -2751,7 +2750,7 @@ void iwl_mac_reset_tsf(struct ieee80211_hw *hw) priv->ibss_beacon = NULL; - priv->beacon_int = priv->hw->conf.beacon_int; + priv->beacon_int = priv->vif->bss_conf.beacon_int; priv->timestamp = 0; if ((priv->iw_mode == NL80211_IFTYPE_STATION)) priv->beacon_int = 0; diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index a782292ed43..f9d9b65ef30 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -551,7 +551,8 @@ static void iwl3945_setup_rxon_timing(struct iwl_priv *priv) priv->rxon_timing.atim_window = 0; } else { priv->rxon_timing.beacon_interval = - iwl3945_adjust_beacon_interval(conf->beacon_int); + iwl3945_adjust_beacon_interval( + priv->vif->bss_conf.beacon_int); /* TODO: we need to get atim_window from upper stack * for now we set to 0 */ priv->rxon_timing.atim_window = 0; @@ -4211,8 +4212,6 @@ static int iwl3945_setup_mac(struct iwl_priv *priv) /* Default value; 4 EDCA QOS priorities */ hw->queues = 4; - hw->conf.beacon_int = 100; - if (priv->bands[IEEE80211_BAND_2GHZ].n_channels) priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->bands[IEEE80211_BAND_2GHZ]; diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c index e7289e2e7f1..68e47fb47a1 100644 --- a/drivers/net/wireless/libertas_tf/main.c +++ b/drivers/net/wireless/libertas_tf/main.c @@ -380,7 +380,7 @@ static int lbtf_op_config_interface(struct ieee80211_hw *hw, if (beacon) { lbtf_beacon_set(priv, beacon); kfree_skb(beacon); - lbtf_beacon_ctrl(priv, 1, hw->conf.beacon_int); + lbtf_beacon_ctrl(priv, 1, vif->bss_conf.beacon_int); } break; default: diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index d4fdc8b7d7d..24c95a619e4 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -553,18 +553,13 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) struct mac80211_hwsim_data *data = hw->priv; struct ieee80211_conf *conf = &hw->conf; - printk(KERN_DEBUG "%s:%s (freq=%d radio_enabled=%d beacon_int=%d)\n", + printk(KERN_DEBUG "%s:%s (freq=%d radio_enabled=%d)\n", wiphy_name(hw->wiphy), __func__, - conf->channel->center_freq, conf->radio_enabled, - conf->beacon_int); + conf->channel->center_freq, conf->radio_enabled); data->channel = conf->channel; data->radio_enabled = conf->radio_enabled; - data->beacon_int = 1024 * conf->beacon_int / 1000 * HZ / 1000; - if (data->beacon_int < 1) - data->beacon_int = 1; - - if (!data->started || !data->radio_enabled) + if (!data->started || !data->radio_enabled || !data->beacon_int) del_timer(&data->beacon_timer); else mod_timer(&data->beacon_timer, jiffies + data->beacon_int); @@ -615,6 +610,7 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, u32 changed) { struct hwsim_vif_priv *vp = (void *)vif->drv_priv; + struct mac80211_hwsim_data *data = hw->priv; hwsim_check_magic(vif); @@ -628,6 +624,14 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, vp->aid = info->aid; } + if (changed & BSS_CHANGED_BEACON_INT) { + printk(KERN_DEBUG " %s: BCNINT: %d\n", + wiphy_name(hw->wiphy), info->beacon_int); + data->beacon_int = 1024 * info->beacon_int / 1000 * HZ / 1000; + if (WARN_ON(data->beacon_int)) + data->beacon_int = 1; + } + if (changed & BSS_CHANGED_ERP_CTS_PROT) { printk(KERN_DEBUG " %s: ERP_CTS_PROT: %d\n", wiphy_name(hw->wiphy), info->use_cts_prot); diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 81d706d8522..22c65e8cbb7 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -149,6 +149,7 @@ struct ieee80211_low_level_stats { * @BSS_CHANGED_ERP_SLOT: slot timing changed * @BSS_CHANGED_HT: 802.11n parameters changed * @BSS_CHANGED_BASIC_RATES: Basic rateset changed + * @BSS_CHANGED_BEACON_INT: Beacon interval changed */ enum ieee80211_bss_change { BSS_CHANGED_ASSOC = 1<<0, @@ -157,6 +158,7 @@ enum ieee80211_bss_change { BSS_CHANGED_ERP_SLOT = 1<<3, BSS_CHANGED_HT = 1<<4, BSS_CHANGED_BASIC_RATES = 1<<5, + BSS_CHANGED_BEACON_INT = 1<<6, }; /** @@ -529,7 +531,7 @@ enum ieee80211_conf_flags { * enum ieee80211_conf_changed - denotes which configuration changed * * @IEEE80211_CONF_CHANGE_RADIO_ENABLED: the value of radio_enabled changed - * @IEEE80211_CONF_CHANGE_BEACON_INTERVAL: the beacon interval changed + * @_IEEE80211_CONF_CHANGE_BEACON_INTERVAL: DEPRECATED * @IEEE80211_CONF_CHANGE_LISTEN_INTERVAL: the listen interval changed * @IEEE80211_CONF_CHANGE_RADIOTAP: the radiotap flag changed * @IEEE80211_CONF_CHANGE_PS: the PS flag or dynamic PS timeout changed @@ -539,7 +541,7 @@ enum ieee80211_conf_flags { */ enum ieee80211_conf_changed { IEEE80211_CONF_CHANGE_RADIO_ENABLED = BIT(0), - IEEE80211_CONF_CHANGE_BEACON_INTERVAL = BIT(1), + _IEEE80211_CONF_CHANGE_BEACON_INTERVAL = BIT(1), IEEE80211_CONF_CHANGE_LISTEN_INTERVAL = BIT(2), IEEE80211_CONF_CHANGE_RADIOTAP = BIT(3), IEEE80211_CONF_CHANGE_PS = BIT(4), @@ -548,6 +550,14 @@ enum ieee80211_conf_changed { IEEE80211_CONF_CHANGE_RETRY_LIMITS = BIT(7), }; +static inline __deprecated enum ieee80211_conf_changed +__IEEE80211_CONF_CHANGE_BEACON_INTERVAL(void) +{ + return _IEEE80211_CONF_CHANGE_BEACON_INTERVAL; +} +#define IEEE80211_CONF_CHANGE_BEACON_INTERVAL \ + __IEEE80211_CONF_CHANGE_BEACON_INTERVAL() + /** * struct ieee80211_conf - configuration of the device * diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 5e1c230744b..a898ccd3f2c 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -451,18 +451,11 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata, * This is a kludge. beacon interval should really be part * of the beacon information. */ - if (params->interval && (sdata->local->hw.conf.beacon_int != - params->interval)) { - sdata->local->hw.conf.beacon_int = params->interval; - err = ieee80211_hw_config(sdata->local, - IEEE80211_CONF_CHANGE_BEACON_INTERVAL); - if (err < 0) - return err; - /* - * We updated some parameter so if below bails out - * it's not an error. - */ - err = 0; + if (params->interval && + (sdata->vif.bss_conf.beacon_int != params->interval)) { + sdata->vif.bss_conf.beacon_int = params->interval; + ieee80211_bss_info_change_notify(sdata, + BSS_CHANGED_BEACON_INT); } /* Need to have a beacon head if we don't have one yet */ diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 25ff583612e..f4879dad3cd 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -73,6 +73,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt; u8 *pos; struct ieee80211_supported_band *sband; + u32 bss_change; if (local->ops->reset_tsf) { /* Reset own TSF to allow time synchronization work. */ @@ -92,8 +93,6 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, memcpy(ifibss->bssid, bssid, ETH_ALEN); - local->hw.conf.beacon_int = beacon_int >= 10 ? beacon_int : 10; - sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0; ieee80211_if_config(sdata, IEEE80211_IFCC_BSSID); @@ -101,6 +100,12 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, local->oper_channel = chan; local->oper_channel_type = NL80211_CHAN_NO_HT; ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); + + sdata->vif.bss_conf.beacon_int = beacon_int; + bss_change = BSS_CHANGED_BEACON_INT; + bss_change |= ieee80211_reset_erp_info(sdata); + ieee80211_bss_info_change_notify(sdata, bss_change); + sband = local->hw.wiphy->bands[chan->band]; /* Build IBSS probe response */ @@ -111,7 +116,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, memset(mgmt->da, 0xff, ETH_ALEN); memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN); memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN); - mgmt->u.beacon.beacon_int = cpu_to_le16(local->hw.conf.beacon_int); + mgmt->u.beacon.beacon_int = cpu_to_le16(beacon_int); mgmt->u.beacon.timestamp = cpu_to_le64(tsf); mgmt->u.beacon.capab_info = cpu_to_le16(capability); @@ -181,8 +186,13 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, struct ieee80211_bss *bss) { + u16 beacon_int = bss->cbss.beacon_interval; + + if (beacon_int < 10) + beacon_int = 10; + __ieee80211_sta_join_ibss(sdata, bss->cbss.bssid, - bss->cbss.beacon_interval, + beacon_int, bss->cbss.channel, bss->supp_rates_len, bss->supp_rates, bss->cbss.capability, @@ -464,9 +474,6 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) sband = local->hw.wiphy->bands[ifibss->channel->band]; - if (local->hw.conf.beacon_int == 0) - local->hw.conf.beacon_int = 100; - capability = WLAN_CAPABILITY_IBSS; if (sdata->default_key) @@ -480,7 +487,7 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) *pos++ = (u8) (rate / 5); } - __ieee80211_sta_join_ibss(sdata, bssid, local->hw.conf.beacon_int, + __ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int, ifibss->channel, sband->n_bitrates, supp_rates, capability, 0); } @@ -823,6 +830,8 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, } else sdata->u.ibss.fixed_bssid = false; + sdata->vif.bss_conf.beacon_int = params->beacon_interval; + sdata->u.ibss.channel = params->channel; sdata->u.ibss.fixed_channel = params->channel_fixed; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index e00d124e4ef..b254879d863 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -294,9 +294,6 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; - if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) - return; - if (!changed) return; @@ -305,6 +302,17 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, &sdata->vif, &sdata->vif.bss_conf, changed); + + /* + * DEPRECATED + * + * ~changed is just there to not do this at resume time + */ + if (changed & BSS_CHANGED_BEACON_INT && ~changed) { + local->hw.conf.beacon_int = sdata->vif.bss_conf.beacon_int; + ieee80211_hw_config(local, + _IEEE80211_CONF_CHANGE_BEACON_INTERVAL); + } } u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata) @@ -971,9 +979,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) debugfs_hw_add(local); - if (local->hw.conf.beacon_int < 10) - local->hw.conf.beacon_int = 100; - if (local->hw.max_listen_interval == 0) local->hw.max_listen_interval = 1; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index a2f5e622305..bfd571e6f22 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -842,6 +842,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, sdata->vif.bss_conf.timestamp = bss->cbss.tsf; sdata->vif.bss_conf.dtim_period = bss->dtim_period; + bss_info_changed |= BSS_CHANGED_BEACON_INT; bss_info_changed |= ieee80211_handle_bss_capability(sdata, bss->cbss.capability, bss->has_erp_value, bss->erp_value); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 654a8e963cc..7116220d06b 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -543,9 +543,8 @@ void sta_info_unlink(struct sta_info **sta) spin_unlock_irqrestore(&local->sta_lock, flags); } -static inline int sta_info_buffer_expired(struct ieee80211_local *local, - struct sta_info *sta, - struct sk_buff *skb) +static int sta_info_buffer_expired(struct sta_info *sta, + struct sk_buff *skb) { struct ieee80211_tx_info *info; int timeout; @@ -556,8 +555,9 @@ static inline int sta_info_buffer_expired(struct ieee80211_local *local, info = IEEE80211_SKB_CB(skb); /* Timeout: (2 * listen_interval * beacon_int * 1024 / 1000000) sec */ - timeout = (sta->listen_interval * local->hw.conf.beacon_int * 32 / - 15625) * HZ; + timeout = (sta->listen_interval * + sta->sdata->vif.bss_conf.beacon_int * + 32 / 15625) * HZ; if (timeout < STA_TX_BUFFER_EXPIRE) timeout = STA_TX_BUFFER_EXPIRE; return time_after(jiffies, info->control.jiffies + timeout); @@ -577,7 +577,7 @@ static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local, for (;;) { spin_lock_irqsave(&sta->ps_tx_buf.lock, flags); skb = skb_peek(&sta->ps_tx_buf); - if (sta_info_buffer_expired(local, sta, skb)) + if (sta_info_buffer_expired(sta, skb)) skb = __skb_dequeue(&sta->ps_tx_buf); else skb = NULL; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 1865622003c..29df65045fc 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2132,7 +2132,7 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN); /* BSSID is left zeroed, wildcard value */ mgmt->u.beacon.beacon_int = - cpu_to_le16(local->hw.conf.beacon_int); + cpu_to_le16(sdata->vif.bss_conf.beacon_int); mgmt->u.beacon.capab_info = 0x0; /* 0x0 for MPs */ pos = skb_put(skb, 2); -- cgit v1.2.3-70-g09d2 From 2d0ddec5b2b859f06116f631fc0ffe94fbceb556 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 23 Apr 2009 16:13:26 +0200 Subject: mac80211: unify config_interface and bss_info_changed The config_interface method is a little strange, it contains the BSSID and beacon updates, while bss_info_changed contains most other BSS information for each interface. This patch removes config_interface and rolls all the information it previously passed to drivers into bss_info_changed. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/adm8211.c | 14 ++- drivers/net/wireless/at76c50x-usb.c | 15 ++- drivers/net/wireless/ath/ar9170/main.c | 39 ++---- drivers/net/wireless/ath/ath5k/base.c | 71 +++++------ drivers/net/wireless/ath/ath9k/main.c | 182 +++++++++++++--------------- drivers/net/wireless/b43/main.c | 64 ++++------ drivers/net/wireless/b43legacy/main.c | 63 ++++------ drivers/net/wireless/iwlwifi/iwl-agn.c | 1 - drivers/net/wireless/iwlwifi/iwl-core.c | 162 +++++++++---------------- drivers/net/wireless/iwlwifi/iwl-core.h | 3 - drivers/net/wireless/iwlwifi/iwl3945-base.c | 1 - drivers/net/wireless/libertas_tf/main.c | 56 ++++----- drivers/net/wireless/mac80211_hwsim.c | 25 ++-- drivers/net/wireless/mwl8k.c | 18 +-- drivers/net/wireless/p54/p54common.c | 63 ++++------ drivers/net/wireless/rt2x00/rt2400pci.c | 1 - drivers/net/wireless/rt2x00/rt2500pci.c | 1 - drivers/net/wireless/rt2x00/rt2500usb.c | 1 - drivers/net/wireless/rt2x00/rt2x00.h | 3 - drivers/net/wireless/rt2x00/rt2x00mac.c | 88 ++++++-------- drivers/net/wireless/rt2x00/rt61pci.c | 1 - drivers/net/wireless/rt2x00/rt73usb.c | 1 - drivers/net/wireless/rtl818x/rtl8180_dev.c | 33 +++-- drivers/net/wireless/rtl818x/rtl8187_dev.c | 48 ++++---- drivers/net/wireless/zd1211rw/zd_mac.c | 80 +++++------- include/net/mac80211.h | 51 ++------ net/mac80211/cfg.c | 8 +- net/mac80211/ibss.c | 18 ++- net/mac80211/ieee80211_i.h | 1 - net/mac80211/main.c | 131 +++++++++----------- net/mac80211/mesh.c | 6 +- net/mac80211/mlme.c | 8 +- net/mac80211/scan.c | 8 +- net/mac80211/util.c | 17 +-- 34 files changed, 504 insertions(+), 778 deletions(-) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index f7182179501..2b9e379994a 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -1311,18 +1311,20 @@ static int adm8211_config(struct ieee80211_hw *dev, u32 changed) return 0; } -static int adm8211_config_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf) +static void adm8211_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *conf, + u32 changes) { struct adm8211_priv *priv = dev->priv; + if (!(changes & BSS_CHANGED_BSSID)) + return; + if (memcmp(conf->bssid, priv->bssid, ETH_ALEN)) { adm8211_set_bssid(dev, conf->bssid); memcpy(priv->bssid, conf->bssid, ETH_ALEN); } - - return 0; } static void adm8211_configure_filter(struct ieee80211_hw *dev, @@ -1753,7 +1755,7 @@ static const struct ieee80211_ops adm8211_ops = { .add_interface = adm8211_add_interface, .remove_interface = adm8211_remove_interface, .config = adm8211_config, - .config_interface = adm8211_config_interface, + .bss_info_changed = adm8211_bss_info_changed, .configure_filter = adm8211_configure_filter, .get_stats = adm8211_get_stats, .get_tx_stats = adm8211_get_tx_stats, diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index 55f947ac56d..cea7f1466c5 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -1965,13 +1965,18 @@ static int at76_config(struct ieee80211_hw *hw, u32 changed) return 0; } -static int at76_config_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf) +static void at76_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *conf, + u32 changed) { struct at76_priv *priv = hw->priv; at76_dbg(DBG_MAC80211, "%s():", __func__); + + if (!(changed & BSS_CHANGED_BSSID)) + return; + at76_dbg_dump(DBG_MAC80211, conf->bssid, ETH_ALEN, "bssid:"); mutex_lock(&priv->mtx); @@ -1983,8 +1988,6 @@ static int at76_config_interface(struct ieee80211_hw *hw, at76_join(priv); mutex_unlock(&priv->mtx); - - return 0; } /* must be atomic */ @@ -2076,7 +2079,7 @@ static const struct ieee80211_ops at76_ops = { .add_interface = at76_add_interface, .remove_interface = at76_remove_interface, .config = at76_config, - .config_interface = at76_config_interface, + .bss_info_changed = at76_bss_info_changed, .configure_filter = at76_configure_filter, .start = at76_mac80211_start, .stop = at76_mac80211_stop, diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c index 49c729bc714..3bd3a61225c 100644 --- a/drivers/net/wireless/ath/ar9170/main.c +++ b/drivers/net/wireless/ath/ar9170/main.c @@ -1360,33 +1360,6 @@ out: return err; } -static int ar9170_op_config_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf) -{ - struct ar9170 *ar = hw->priv; - int err = 0; - - mutex_lock(&ar->mutex); - - if (conf->changed & IEEE80211_IFCC_BSSID) { - memcpy(ar->bssid, conf->bssid, ETH_ALEN); - err = ar9170_set_operating_mode(ar); - } - - if (conf->changed & IEEE80211_IFCC_BEACON) { - err = ar9170_update_beacon(ar); - - if (err) - goto out; - err = ar9170_set_beacon_timers(ar); - } - -out: - mutex_unlock(&ar->mutex); - return err; -} - static void ar9170_set_filters(struct work_struct *work) { struct ar9170 *ar = container_of(work, struct ar9170, @@ -1488,6 +1461,17 @@ static void ar9170_op_bss_info_changed(struct ieee80211_hw *hw, mutex_lock(&ar->mutex); + if (changed & BSS_CHANGED_BSSID) { + memcpy(ar->bssid, bss_conf->bssid, ETH_ALEN); + err = ar9170_set_operating_mode(ar); + } + + if (changed & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED)) { + err = ar9170_update_beacon(ar); + if (!err) + ar9170_set_beacon_timers(ar); + } + ar9170_regwrite_begin(ar); if (changed & BSS_CHANGED_ASSOC) { @@ -1796,7 +1780,6 @@ static const struct ieee80211_ops ar9170_ops = { .add_interface = ar9170_op_add_interface, .remove_interface = ar9170_op_remove_interface, .config = ar9170_op_config, - .config_interface = ar9170_op_config_interface, .configure_filter = ar9170_op_configure_filter, .conf_tx = ar9170_conf_tx, .bss_info_changed = ar9170_op_bss_info_changed, diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index e4b1d9efa42..410fc4cfc4c 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -227,9 +227,6 @@ static int ath5k_add_interface(struct ieee80211_hw *hw, static void ath5k_remove_interface(struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf); static int ath5k_config(struct ieee80211_hw *hw, u32 changed); -static int ath5k_config_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf); static void ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, @@ -259,7 +256,6 @@ static const struct ieee80211_ops ath5k_hw_ops = { .add_interface = ath5k_add_interface, .remove_interface = ath5k_remove_interface, .config = ath5k_config, - .config_interface = ath5k_config_interface, .configure_filter = ath5k_configure_filter, .set_key = ath5k_set_key, .get_stats = ath5k_get_stats, @@ -2764,44 +2760,6 @@ ath5k_config(struct ieee80211_hw *hw, u32 changed) return ret; } -static int -ath5k_config_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf) -{ - struct ath5k_softc *sc = hw->priv; - struct ath5k_hw *ah = sc->ah; - int ret = 0; - - mutex_lock(&sc->lock); - if (sc->vif != vif) { - ret = -EIO; - goto unlock; - } - if (conf->changed & IEEE80211_IFCC_BSSID && conf->bssid) { - /* Cache for later use during resets */ - memcpy(ah->ah_bssid, conf->bssid, ETH_ALEN); - /* XXX: assoc id is set to 0 for now, mac80211 doesn't have - * a clean way of letting us retrieve this yet. */ - ath5k_hw_set_associd(ah, ah->ah_bssid, 0); - mmiowb(); - } - if (conf->changed & IEEE80211_IFCC_BEACON && - (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) { - ret = -ENOMEM; - goto unlock; - } - ath5k_beacon_update(sc, beacon); - } - -unlock: - mutex_unlock(&sc->lock); - return ret; -} - #define SUPPORTED_FIF_FLAGS \ FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_FCSFAIL | \ FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | \ @@ -3082,15 +3040,40 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw, u32 changes) { struct ath5k_softc *sc = hw->priv; + struct ath5k_hw *ah = sc->ah; + + mutex_lock(&sc->lock); + if (WARN_ON(sc->vif != vif)) + goto unlock; + + if (changes & BSS_CHANGED_BSSID) { + /* Cache for later use during resets */ + memcpy(ah->ah_bssid, bss_conf->bssid, ETH_ALEN); + /* XXX: assoc id is set to 0 for now, mac80211 doesn't have + * a clean way of letting us retrieve this yet. */ + ath5k_hw_set_associd(ah, ah->ah_bssid, 0); + mmiowb(); + } if (changes & BSS_CHANGED_BEACON_INT) sc->bintval = bss_conf->beacon_int; if (changes & BSS_CHANGED_ASSOC) { - mutex_lock(&sc->lock); sc->assoc = bss_conf->assoc; if (sc->opmode == NL80211_IFTYPE_STATION) set_beacon_filter(hw, sc->assoc); - mutex_unlock(&sc->lock); } + + if (changes & BSS_CHANGED_BEACON && + (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); + } + + unlock: + mutex_unlock(&sc->lock); } diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 28cc9cd52f3..d3dc8e2c77b 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2363,104 +2363,6 @@ skip_chan_change: 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); - } - - /* Only legacy IBSS for now */ - if (vif->type == NL80211_IFTYPE_ADHOC) - ath_update_chainmask(sc, 0); - - mutex_unlock(&sc->mutex); - - return 0; -} - #define SUPPORTED_FILTERS \ (FIF_PROMISC_IN_BSS | \ FIF_ALLMULTI | \ @@ -2597,9 +2499,92 @@ static void ath9k_bss_info_changed(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); @@ -2757,7 +2742,6 @@ struct ieee80211_ops ath9k_ops = { .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, diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 3c693e6ec90..2615aaf7df6 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3543,12 +3543,38 @@ static void b43_op_bss_info_changed(struct ieee80211_hw *hw, { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev; + unsigned long flags; mutex_lock(&wl->mutex); dev = wl->current_dev; if (!dev || b43_status(dev) < B43_STAT_STARTED) goto out_unlock_mutex; + + B43_WARN_ON(wl->vif != vif); + + 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); + } + b43_mac_suspend(dev); /* Update templates for AP/mesh mode. */ @@ -3571,8 +3597,6 @@ static void b43_op_bss_info_changed(struct ieee80211_hw *hw, b43_mac_enable(dev); out_unlock_mutex: mutex_unlock(&wl->mutex); - - return; } static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, @@ -3730,41 +3754,6 @@ static void b43_op_configure_filter(struct ieee80211_hw *hw, spin_unlock_irqrestore(&wl->irq_lock, flags); } -static int b43_op_config_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf) -{ - struct b43_wl *wl = hw_to_b43_wl(hw); - struct b43_wldev *dev = wl->current_dev; - unsigned long flags; - - if (!dev) - return -ENODEV; - mutex_lock(&wl->mutex); - spin_lock_irqsave(&wl->irq_lock, flags); - B43_WARN_ON(wl->vif != vif); - 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 (conf->changed & IEEE80211_IFCC_BEACON) - b43_update_templates(wl); - } else if (b43_is_mode(wl, NL80211_IFTYPE_ADHOC)) { - if (conf->changed & IEEE80211_IFCC_BEACON) - b43_update_templates(wl); - } - b43_write_mac_bssid_templates(dev); - } - spin_unlock_irqrestore(&wl->irq_lock, flags); - mutex_unlock(&wl->mutex); - - return 0; -} - /* Locking: wl->mutex */ static void b43_wireless_core_stop(struct b43_wldev *dev) { @@ -4434,7 +4423,6 @@ static const struct ieee80211_ops b43_hw_ops = { .remove_interface = b43_op_remove_interface, .config = b43_op_config, .bss_info_changed = b43_op_bss_info_changed, - .config_interface = b43_op_config_interface, .configure_filter = b43_op_configure_filter, .set_key = b43_op_set_key, .get_stats = b43_op_get_stats, diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index 276f314688f..07c7898c87a 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -2804,6 +2804,7 @@ static void b43legacy_op_bss_info_changed(struct ieee80211_hw *hw, u32 savedirqs; mutex_lock(&wl->mutex); + B43legacy_WARN_ON(wl->vif != vif); dev = wl->current_dev; phy = &dev->phy; @@ -2817,8 +2818,29 @@ static void b43legacy_op_bss_info_changed(struct ieee80211_hw *hw, goto out_unlock_mutex; } savedirqs = b43legacy_interrupt_disable(dev, B43legacy_IRQ_ALL); - spin_unlock_irqrestore(&wl->irq_lock, flags); - b43legacy_synchronize_irq(dev); + + 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); + } b43legacy_mac_suspend(dev); @@ -2846,8 +2868,6 @@ static void b43legacy_op_bss_info_changed(struct ieee80211_hw *hw, spin_unlock_irqrestore(&wl->irq_lock, flags); out_unlock_mutex: mutex_unlock(&wl->mutex); - - return; } static void b43legacy_op_configure_filter(struct ieee80211_hw *hw, @@ -2889,40 +2909,6 @@ static void b43legacy_op_configure_filter(struct ieee80211_hw *hw, spin_unlock_irqrestore(&wl->irq_lock, flags); } -static int b43legacy_op_config_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf) -{ - struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); - struct b43legacy_wldev *dev = wl->current_dev; - unsigned long flags; - - if (!dev) - return -ENODEV; - mutex_lock(&wl->mutex); - spin_lock_irqsave(&wl->irq_lock, flags); - B43legacy_WARN_ON(wl->vif != vif); - 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 (conf->changed & IEEE80211_IFCC_BEACON) - b43legacy_update_templates(wl); - } else if (b43legacy_is_mode(wl, NL80211_IFTYPE_ADHOC)) { - if (conf->changed & IEEE80211_IFCC_BEACON) - b43legacy_update_templates(wl); - } - b43legacy_write_mac_bssid_templates(dev); - } - spin_unlock_irqrestore(&wl->irq_lock, flags); - mutex_unlock(&wl->mutex); - - return 0; -} - /* Locking: wl->mutex */ static void b43legacy_wireless_core_stop(struct b43legacy_wldev *dev) { @@ -3563,7 +3549,6 @@ static const struct ieee80211_ops b43legacy_hw_ops = { .remove_interface = b43legacy_op_remove_interface, .config = b43legacy_op_dev_config, .bss_info_changed = b43legacy_op_bss_info_changed, - .config_interface = b43legacy_op_config_interface, .configure_filter = b43legacy_op_configure_filter, .get_stats = b43legacy_op_get_stats, .get_tx_stats = b43legacy_op_get_tx_stats, diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 4920dcf7d7b..dd8b15ed829 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -2623,7 +2623,6 @@ static struct ieee80211_ops iwl_hw_ops = { .add_interface = iwl_mac_add_interface, .remove_interface = iwl_mac_remove_interface, .config = iwl_mac_config, - .config_interface = iwl_mac_config_interface, .configure_filter = iwl_configure_filter, .set_key = iwl_mac_set_key, .update_tkip_key = iwl_mac_update_tkip_key, diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index cd8a5ed97c4..0f21fc136ca 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -2238,15 +2238,69 @@ static void iwl_ht_conf(struct iwl_priv *priv, #define IWL_DELAY_NEXT_SCAN_AFTER_ASSOC (HZ*6) void iwl_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes) + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes) { struct iwl_priv *priv = hw->priv; int ret; IWL_DEBUG_MAC80211(priv, "changes = 0x%X\n", changes); + if (!iwl_is_alive(priv)) + return; + + mutex_lock(&priv->mutex); + + if (changes & BSS_CHANGED_BEACON && + priv->iw_mode == NL80211_IFTYPE_AP) { + dev_kfree_skb(priv->ibss_beacon); + priv->ibss_beacon = ieee80211_beacon_get(hw, vif); + } + + if ((changes & BSS_CHANGED_BSSID) && !iwl_is_rfkill(priv)) { + /* If there is currently a HW scan going on in the background + * then we need to cancel it else the RXON below will fail. */ + if (iwl_scan_cancel_timeout(priv, 100)) { + IWL_WARN(priv, "Aborted scan still in progress " + "after 100ms\n"); + IWL_DEBUG_MAC80211(priv, "leaving - scan abort failed.\n"); + mutex_unlock(&priv->mutex); + return; + } + memcpy(priv->staging_rxon.bssid_addr, + bss_conf->bssid, ETH_ALEN); + + /* TODO: Audit driver for usage of these members and see + * if mac80211 deprecates them (priv->bssid looks like it + * shouldn't be there, but I haven't scanned the IBSS code + * to verify) - jpk */ + memcpy(priv->bssid, bss_conf->bssid, ETH_ALEN); + + if (priv->iw_mode == NL80211_IFTYPE_AP) + iwlcore_config_ap(priv); + else { + int rc = iwlcore_commit_rxon(priv); + if ((priv->iw_mode == NL80211_IFTYPE_STATION) && rc) + iwl_rxon_add_station( + priv, priv->active_rxon.bssid_addr, 1); + } + } else if (!iwl_is_rfkill(priv)) { + iwl_scan_cancel_timeout(priv, 100); + priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + iwlcore_commit_rxon(priv); + } + + if (priv->iw_mode == NL80211_IFTYPE_ADHOC && + changes & BSS_CHANGED_BEACON) { + struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); + + if (beacon) + iwl_mac_beacon_update(hw, beacon); + } + + mutex_unlock(&priv->mutex); + if (changes & BSS_CHANGED_ERP_PREAMBLE) { IWL_DEBUG_MAC80211(priv, "ERP_PREAMBLE %d\n", bss_conf->use_short_preamble); @@ -2305,7 +2359,7 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw, &priv->staging_rxon, sizeof(struct iwl_rxon_cmd)); } - + IWL_DEBUG_MAC80211(priv, "leave\n"); } EXPORT_SYMBOL(iwl_bss_info_changed); @@ -2589,106 +2643,6 @@ out: } EXPORT_SYMBOL(iwl_mac_config); -int iwl_mac_config_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf) -{ - struct iwl_priv *priv = hw->priv; - int rc; - - if (conf == NULL) - return -EIO; - - if (priv->vif != vif) { - IWL_DEBUG_MAC80211(priv, "leave - priv->vif != vif\n"); - return 0; - } - - if (priv->iw_mode == NL80211_IFTYPE_ADHOC && - conf->changed & IEEE80211_IFCC_BEACON) { - struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); - if (!beacon) - return -ENOMEM; - mutex_lock(&priv->mutex); - rc = iwl_mac_beacon_update(hw, beacon); - mutex_unlock(&priv->mutex); - if (rc) - return rc; - } - - if (!iwl_is_alive(priv)) - return -EAGAIN; - - mutex_lock(&priv->mutex); - - if (conf->bssid) - IWL_DEBUG_MAC80211(priv, "bssid: %pM\n", conf->bssid); - -/* - * very dubious code was here; the probe filtering flag is never set: - * - if (unlikely(test_bit(STATUS_SCANNING, &priv->status)) && - !(priv->hw->flags & IEEE80211_HW_NO_PROBE_FILTERING)) { - */ - - if (priv->iw_mode == NL80211_IFTYPE_AP) { - if (!conf->bssid) { - conf->bssid = priv->mac_addr; - memcpy(priv->bssid, priv->mac_addr, ETH_ALEN); - IWL_DEBUG_MAC80211(priv, "bssid was set to: %pM\n", - conf->bssid); - } - if (priv->ibss_beacon) - dev_kfree_skb(priv->ibss_beacon); - - priv->ibss_beacon = ieee80211_beacon_get(hw, vif); - } - - if (iwl_is_rfkill(priv)) - goto done; - - if (conf->bssid && !is_zero_ether_addr(conf->bssid) && - !is_multicast_ether_addr(conf->bssid)) { - /* If there is currently a HW scan going on in the background - * then we need to cancel it else the RXON below will fail. */ - if (iwl_scan_cancel_timeout(priv, 100)) { - IWL_WARN(priv, "Aborted scan still in progress " - "after 100ms\n"); - IWL_DEBUG_MAC80211(priv, "leaving - scan abort failed.\n"); - mutex_unlock(&priv->mutex); - return -EAGAIN; - } - memcpy(priv->staging_rxon.bssid_addr, conf->bssid, ETH_ALEN); - - /* TODO: Audit driver for usage of these members and see - * if mac80211 deprecates them (priv->bssid looks like it - * shouldn't be there, but I haven't scanned the IBSS code - * to verify) - jpk */ - memcpy(priv->bssid, conf->bssid, ETH_ALEN); - - if (priv->iw_mode == NL80211_IFTYPE_AP) - iwlcore_config_ap(priv); - else { - rc = iwlcore_commit_rxon(priv); - if ((priv->iw_mode == NL80211_IFTYPE_STATION) && rc) - iwl_rxon_add_station( - priv, priv->active_rxon.bssid_addr, 1); - } - - } else { - iwl_scan_cancel_timeout(priv, 100); - priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - iwlcore_commit_rxon(priv); - } - - done: - IWL_DEBUG_MAC80211(priv, "leave\n"); - mutex_unlock(&priv->mutex); - - return 0; -} -EXPORT_SYMBOL(iwl_mac_config_interface); - int iwl_mac_get_tx_stats(struct ieee80211_hw *hw, struct ieee80211_tx_queue_stats *stats) { diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index d4c60afa289..bd7f9d9616b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -281,9 +281,6 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf); int iwl_mac_config(struct ieee80211_hw *hw, u32 changed); void iwl_config_ap(struct iwl_priv *priv); -int iwl_mac_config_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf); int iwl_mac_get_tx_stats(struct ieee80211_hw *hw, struct ieee80211_tx_queue_stats *stats); void iwl_mac_reset_tsf(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index f9d9b65ef30..5cd4321d7cf 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -4106,7 +4106,6 @@ static struct ieee80211_ops iwl3945_hw_ops = { .add_interface = iwl_mac_add_interface, .remove_interface = iwl_mac_remove_interface, .config = iwl_mac_config, - .config_interface = iwl_mac_config_interface, .configure_filter = iwl_configure_filter, .set_key = iwl3945_mac_set_key, .get_tx_stats = iwl_mac_get_tx_stats, diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c index 68e47fb47a1..10a99e26d39 100644 --- a/drivers/net/wireless/libertas_tf/main.c +++ b/drivers/net/wireless/libertas_tf/main.c @@ -366,36 +366,6 @@ static int lbtf_op_config(struct ieee80211_hw *hw, u32 changed) return 0; } -static int lbtf_op_config_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf) -{ - struct lbtf_private *priv = hw->priv; - struct sk_buff *beacon; - - switch (priv->vif->type) { - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_MESH_POINT: - beacon = ieee80211_beacon_get(hw, vif); - if (beacon) { - lbtf_beacon_set(priv, beacon); - kfree_skb(beacon); - lbtf_beacon_ctrl(priv, 1, vif->bss_conf.beacon_int); - } - break; - default: - break; - } - - if (conf->bssid) { - u8 null_bssid[ETH_ALEN] = {0}; - bool activate = compare_ether_addr(conf->bssid, null_bssid); - lbtf_set_bssid(priv, activate, conf->bssid); - } - - return 0; -} - #define SUPPORTED_FIF_FLAGS (FIF_PROMISC_IN_BSS | FIF_ALLMULTI) static void lbtf_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, @@ -451,6 +421,29 @@ static void lbtf_op_bss_info_changed(struct ieee80211_hw *hw, u32 changes) { struct lbtf_private *priv = hw->priv; + struct sk_buff *beacon; + + if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_INT)) { + switch (priv->vif->type) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: + beacon = ieee80211_beacon_get(hw, vif); + if (beacon) { + lbtf_beacon_set(priv, beacon); + kfree_skb(beacon); + lbtf_beacon_ctrl(priv, 1, + bss_conf->beacon_int); + } + break; + default: + break; + } + } + + if (changes & BSS_CHANGED_BSSID) { + bool activate = !is_zero_ether_addr(bss_conf->bssid); + lbtf_set_bssid(priv, activate, bss_conf->bssid); + } if (changes & BSS_CHANGED_ERP_PREAMBLE) { if (bss_conf->use_short_preamble) @@ -459,8 +452,6 @@ static void lbtf_op_bss_info_changed(struct ieee80211_hw *hw, priv->preamble = CMD_TYPE_LONG_PREAMBLE; lbtf_set_radio_control(priv); } - - return; } static const struct ieee80211_ops lbtf_ops = { @@ -470,7 +461,6 @@ static const struct ieee80211_ops lbtf_ops = { .add_interface = lbtf_op_add_interface, .remove_interface = lbtf_op_remove_interface, .config = lbtf_op_config, - .config_interface = lbtf_op_config_interface, .configure_filter = lbtf_op_configure_filter, .bss_info_changed = lbtf_op_bss_info_changed, }; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 24c95a619e4..e67b424f925 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -587,23 +587,6 @@ static void mac80211_hwsim_configure_filter(struct ieee80211_hw *hw, *total_flags = data->rx_filter; } -static int mac80211_hwsim_config_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf) -{ - struct hwsim_vif_priv *vp = (void *)vif->drv_priv; - - hwsim_check_magic(vif); - if (conf->changed & IEEE80211_IFCC_BSSID) { - DECLARE_MAC_BUF(mac); - printk(KERN_DEBUG "%s:%s: BSSID changed: %pM\n", - wiphy_name(hw->wiphy), __func__, - conf->bssid); - memcpy(vp->bssid, conf->bssid, ETH_ALEN); - } - return 0; -} - static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, @@ -617,6 +600,13 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, printk(KERN_DEBUG "%s:%s(changed=0x%x)\n", wiphy_name(hw->wiphy), __func__, changed); + if (changed & BSS_CHANGED_BSSID) { + printk(KERN_DEBUG "%s:%s: BSSID changed: %pM\n", + wiphy_name(hw->wiphy), __func__, + info->bssid); + memcpy(vp->bssid, info->bssid, ETH_ALEN); + } + if (changed & BSS_CHANGED_ASSOC) { printk(KERN_DEBUG " %s: ASSOC: assoc=%d aid=%d\n", wiphy_name(hw->wiphy), info->assoc, info->aid); @@ -708,7 +698,6 @@ static const struct ieee80211_ops mac80211_hwsim_ops = .remove_interface = mac80211_hwsim_remove_interface, .config = mac80211_hwsim_config, .configure_filter = mac80211_hwsim_configure_filter, - .config_interface = mac80211_hwsim_config_interface, .bss_info_changed = mac80211_hwsim_bss_info_changed, .sta_notify = mac80211_hwsim_sta_notify, .set_tim = mac80211_hwsim_set_tim, diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index a9a970469c2..46b288dc8f4 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -3089,19 +3089,6 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) return rc ? -EINVAL : 0; } -static int mwl8k_config_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf) -{ - struct mwl8k_vif *mv_vif = MWL8K_VIF(vif); - u32 changed = conf->changed; - - if (changed & IEEE80211_IFCC_BSSID) - memcpy(mv_vif->bssid, conf->bssid, IEEE80211_ADDR_LEN); - - return 0; -} - struct mwl8k_bss_info_changed_worker { struct mwl8k_work_struct header; struct ieee80211_vif *vif; @@ -3183,8 +3170,12 @@ static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, { struct mwl8k_bss_info_changed_worker *worker; struct mwl8k_priv *priv = hw->priv; + struct mwl8k_vif *mv_vif = MWL8K_VIF(vif); int rc; + if (changed & BSS_CHANGED_BSSID) + memcpy(mv_vif->bssid, info->bssid, IEEE80211_ADDR_LEN); + if ((changed & BSS_CHANGED_ASSOC) == 0) return; @@ -3442,7 +3433,6 @@ static const struct ieee80211_ops mwl8k_ops = { .add_interface = mwl8k_add_interface, .remove_interface = mwl8k_remove_interface, .config = mwl8k_config, - .config_interface = mwl8k_config_interface, .bss_info_changed = mwl8k_bss_info_changed, .configure_filter = mwl8k_configure_filter, .set_rts_threshold = mwl8k_set_rts_threshold, diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c index 71394968d45..b8578529180 100644 --- a/drivers/net/wireless/p54/p54common.c +++ b/drivers/net/wireless/p54/p54common.c @@ -2204,41 +2204,6 @@ out: return ret; } -static int p54_config_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf) -{ - struct p54_common *priv = dev->priv; - int ret = 0; - - mutex_lock(&priv->conf_mutex); - if (conf->changed & IEEE80211_IFCC_BSSID) { - memcpy(priv->bssid, conf->bssid, ETH_ALEN); - ret = p54_setup_mac(dev); - if (ret) - goto out; - } - - if (conf->changed & IEEE80211_IFCC_BEACON) { - ret = p54_scan(dev, P54_SCAN_EXIT, 0); - if (ret) - goto out; - ret = p54_setup_mac(dev); - if (ret) - goto out; - ret = p54_beacon_update(dev, vif); - if (ret) - goto out; - ret = p54_set_edcf(dev); - if (ret) - goto out; - } - -out: - mutex_unlock(&priv->conf_mutex); - return ret; -} - static void p54_configure_filter(struct ieee80211_hw *dev, unsigned int changed_flags, unsigned int *total_flags, @@ -2342,8 +2307,32 @@ static void p54_bss_info_changed(struct ieee80211_hw *dev, u32 changed) { struct p54_common *priv = dev->priv; + int ret; + + mutex_lock(&priv->conf_mutex); + if (changed & BSS_CHANGED_BSSID) { + memcpy(priv->bssid, info->bssid, ETH_ALEN); + ret = p54_setup_mac(dev); + if (ret) + goto out; + } + + if (changed & BSS_CHANGED_BEACON) { + ret = p54_scan(dev, P54_SCAN_EXIT, 0); + if (ret) + goto out; + ret = p54_setup_mac(dev); + if (ret) + goto out; + ret = p54_beacon_update(dev, vif); + if (ret) + goto out; + } + /* XXX: this mimics having two callbacks... clean up */ + out: + mutex_unlock(&priv->conf_mutex); - if (changed & BSS_CHANGED_ERP_SLOT) { + if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_BEACON)) { priv->use_short_slot = info->use_short_slot; p54_set_edcf(dev); } @@ -2364,7 +2353,6 @@ static void p54_bss_info_changed(struct ieee80211_hw *dev, p54_setup_mac(dev); } } - } static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, @@ -2619,7 +2607,6 @@ static const struct ieee80211_ops p54_ops = { .sta_notify = p54_sta_notify, .set_key = p54_set_key, .config = p54_config, - .config_interface = p54_config_interface, .bss_info_changed = p54_bss_info_changed, .configure_filter = p54_configure_filter, .conf_tx = p54_conf_tx, diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c index 411eb9cbb4e..6f39ba66218 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/rt2x00/rt2400pci.c @@ -1580,7 +1580,6 @@ static const struct ieee80211_ops rt2400pci_mac80211_ops = { .add_interface = rt2x00mac_add_interface, .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, - .config_interface = rt2x00mac_config_interface, .configure_filter = rt2x00mac_configure_filter, .get_stats = rt2x00mac_get_stats, .bss_info_changed = rt2x00mac_bss_info_changed, diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c index e1be67ca23d..906960f67b6 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/rt2x00/rt2500pci.c @@ -1879,7 +1879,6 @@ static const struct ieee80211_ops rt2500pci_mac80211_ops = { .add_interface = rt2x00mac_add_interface, .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, - .config_interface = rt2x00mac_config_interface, .configure_filter = rt2x00mac_configure_filter, .get_stats = rt2x00mac_get_stats, .bss_info_changed = rt2x00mac_bss_info_changed, diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index 9e630e70fc9..08e88333b0f 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -1908,7 +1908,6 @@ static const struct ieee80211_ops rt2500usb_mac80211_ops = { .add_interface = rt2x00mac_add_interface, .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, - .config_interface = rt2x00mac_config_interface, .configure_filter = rt2x00mac_configure_filter, .set_key = rt2x00mac_set_key, .get_stats = rt2x00mac_get_stats, diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index e03d69975ea..0be39552826 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -943,9 +943,6 @@ int rt2x00mac_add_interface(struct ieee80211_hw *hw, void rt2x00mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf); int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed); -int rt2x00mac_config_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf); void rt2x00mac_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index c41a0b9e473..c4c06b4e1f0 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -390,56 +390,6 @@ int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed) } EXPORT_SYMBOL_GPL(rt2x00mac_config); -int rt2x00mac_config_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf) -{ - struct rt2x00_dev *rt2x00dev = hw->priv; - struct rt2x00_intf *intf = vif_to_intf(vif); - int update_bssid = 0; - int status = 0; - - /* - * Mac80211 might be calling this function while we are trying - * to remove the device or perhaps suspending it. - */ - if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) - return 0; - - spin_lock(&intf->lock); - - /* - * conf->bssid can be NULL if coming from the internal - * beacon update routine. - */ - if (conf->changed & IEEE80211_IFCC_BSSID && conf->bssid) { - update_bssid = 1; - memcpy(&intf->bssid, conf->bssid, ETH_ALEN); - } - - spin_unlock(&intf->lock); - - /* - * Call rt2x00_config_intf() outside of the spinlock context since - * the call will sleep for USB drivers. By using the ieee80211_if_conf - * values as arguments we make keep access to rt2x00_intf thread safe - * even without the lock. - */ - rt2x00lib_config_intf(rt2x00dev, intf, vif->type, NULL, - update_bssid ? conf->bssid : NULL); - - /* - * Update the beacon. - */ - if (conf->changed & (IEEE80211_IFCC_BEACON | - IEEE80211_IFCC_BEACON_ENABLED)) - status = rt2x00queue_update_beacon(rt2x00dev, vif, - conf->enable_beacon); - - return status; -} -EXPORT_SYMBOL_GPL(rt2x00mac_config_interface); - void rt2x00mac_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, @@ -623,6 +573,44 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, struct rt2x00_dev *rt2x00dev = hw->priv; struct rt2x00_intf *intf = vif_to_intf(vif); unsigned int delayed = 0; + int update_bssid = 0; + + /* + * Mac80211 might be calling this function while we are trying + * to remove the device or perhaps suspending it. + */ + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return; + + spin_lock(&intf->lock); + + /* + * conf->bssid can be NULL if coming from the internal + * beacon update routine. + */ + if (changes & BSS_CHANGED_BSSID) { + update_bssid = 1; + memcpy(&intf->bssid, bss_conf->bssid, ETH_ALEN); + } + + spin_unlock(&intf->lock); + + /* + * Call rt2x00_config_intf() outside of the spinlock context since + * the call will sleep for USB drivers. By using the ieee80211_if_conf + * values as arguments we make keep access to rt2x00_intf thread safe + * even without the lock. + */ + if (changes & BSS_CHANGED_BSSID) + rt2x00lib_config_intf(rt2x00dev, intf, vif->type, NULL, + update_bssid ? bss_conf->bssid : NULL); + + /* + * Update the beacon. + */ + if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED)) + rt2x00queue_update_beacon(rt2x00dev, vif, + bss_conf->enable_beacon); /* * When the association status has changed we must reset the link diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index 4346cd1494b..cb521ee7a8f 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -2735,7 +2735,6 @@ static const struct ieee80211_ops rt61pci_mac80211_ops = { .add_interface = rt2x00mac_add_interface, .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, - .config_interface = rt2x00mac_config_interface, .configure_filter = rt2x00mac_configure_filter, .set_key = rt2x00mac_set_key, .get_stats = rt2x00mac_get_stats, diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index 853b2b279b6..6bd28a6bcef 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -2259,7 +2259,6 @@ static const struct ieee80211_ops rt73usb_mac80211_ops = { .add_interface = rt2x00mac_add_interface, .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, - .config_interface = rt2x00mac_config_interface, .configure_filter = rt2x00mac_configure_filter, .set_key = rt2x00mac_set_key, .get_stats = rt2x00mac_get_stats, diff --git a/drivers/net/wireless/rtl818x/rtl8180_dev.c b/drivers/net/wireless/rtl818x/rtl8180_dev.c index 387c133ec0f..7e65d7c3180 100644 --- a/drivers/net/wireless/rtl818x/rtl8180_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180_dev.c @@ -702,30 +702,26 @@ static int rtl8180_config(struct ieee80211_hw *dev, u32 changed) return 0; } -static int rtl8180_config_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf) -{ - struct rtl8180_priv *priv = dev->priv; - int i; - - for (i = 0; i < ETH_ALEN; i++) - rtl818x_iowrite8(priv, &priv->map->BSSID[i], conf->bssid[i]); - - if (is_valid_ether_addr(conf->bssid)) - rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_INFRA); - else - rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_NO_LINK); - - return 0; -} - static void rtl8180_bss_info_changed(struct ieee80211_hw *dev, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, u32 changed) { struct rtl8180_priv *priv = dev->priv; + int i; + + if (changed & BSS_CHANGED_BSSID) { + for (i = 0; i < ETH_ALEN; i++) + rtl818x_iowrite8(priv, &priv->map->BSSID[i], + info->bssid[i]); + + if (is_valid_ether_addr(info->bssid)) + rtl818x_iowrite8(priv, &priv->map->MSR, + RTL818X_MSR_INFRA); + else + rtl818x_iowrite8(priv, &priv->map->MSR, + RTL818X_MSR_NO_LINK); + } if (changed & BSS_CHANGED_ERP_SLOT && priv->rf->conf_erp) priv->rf->conf_erp(dev, info); @@ -770,7 +766,6 @@ static const struct ieee80211_ops rtl8180_ops = { .add_interface = rtl8180_add_interface, .remove_interface = rtl8180_remove_interface, .config = rtl8180_config, - .config_interface = rtl8180_config_interface, .bss_info_changed = rtl8180_bss_info_changed, .configure_filter = rtl8180_configure_filter, }; diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c index ac558da92aa..158827e50c5 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c @@ -1090,32 +1090,6 @@ static int rtl8187_config(struct ieee80211_hw *dev, u32 changed) return 0; } -static int rtl8187_config_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf) -{ - struct rtl8187_priv *priv = dev->priv; - int i; - u8 reg; - - mutex_lock(&priv->conf_mutex); - for (i = 0; i < ETH_ALEN; i++) - rtl818x_iowrite8(priv, &priv->map->BSSID[i], conf->bssid[i]); - - if (is_valid_ether_addr(conf->bssid)) { - reg = RTL818X_MSR_INFRA; - if (priv->is_rtl8187b) - reg |= RTL818X_MSR_ENEDCA; - rtl818x_iowrite8(priv, &priv->map->MSR, reg); - } else { - reg = RTL818X_MSR_NO_LINK; - rtl818x_iowrite8(priv, &priv->map->MSR, reg); - } - - mutex_unlock(&priv->conf_mutex); - return 0; -} - /* * With 8187B, AC_*_PARAM clashes with FEMR definition in struct rtl818x_csr for * example. Thus we have to use raw values for AC_*_PARAM register addresses. @@ -1193,6 +1167,27 @@ static void rtl8187_bss_info_changed(struct ieee80211_hw *dev, u32 changed) { struct rtl8187_priv *priv = dev->priv; + int i; + u8 reg; + + if (changed & BSS_CHANGED_BSSID) { + mutex_lock(&priv->conf_mutex); + for (i = 0; i < ETH_ALEN; i++) + rtl818x_iowrite8(priv, &priv->map->BSSID[i], + info->bssid[i]); + + if (is_valid_ether_addr(info->bssid)) { + reg = RTL818X_MSR_INFRA; + if (priv->is_rtl8187b) + reg |= RTL818X_MSR_ENEDCA; + rtl818x_iowrite8(priv, &priv->map->MSR, reg); + } else { + reg = RTL818X_MSR_NO_LINK; + rtl818x_iowrite8(priv, &priv->map->MSR, reg); + } + + mutex_unlock(&priv->conf_mutex); + } if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_ERP_PREAMBLE)) rtl8187_conf_erp(priv, info->use_short_slot, @@ -1274,7 +1269,6 @@ static const struct ieee80211_ops rtl8187_ops = { .add_interface = rtl8187_add_interface, .remove_interface = rtl8187_remove_interface, .config = rtl8187_config, - .config_interface = rtl8187_config_interface, .bss_info_changed = rtl8187_bss_info_changed, .configure_filter = rtl8187_configure_filter, .conf_tx = rtl8187_conf_tx diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index c3a51266de2..6bdb1704083 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -755,52 +755,6 @@ static int zd_op_config(struct ieee80211_hw *hw, u32 changed) return zd_chip_set_channel(&mac->chip, conf->channel->hw_value); } -static int zd_op_config_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf) -{ - struct zd_mac *mac = zd_hw_mac(hw); - int associated; - int r; - - if (mac->type == NL80211_IFTYPE_MESH_POINT || - mac->type == NL80211_IFTYPE_ADHOC) { - associated = true; - if (conf->changed & IEEE80211_IFCC_BEACON) { - struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); - - if (!beacon) - return -ENOMEM; - r = zd_mac_config_beacon(hw, beacon); - kfree_skb(beacon); - - if (r < 0) - return r; - } - - if (conf->changed & IEEE80211_IFCC_BEACON_ENABLED) { - u32 interval; - - if (conf->enable_beacon) - interval = BCN_MODE_IBSS | hw->conf.beacon_int; - else - interval = 0; - - r = zd_set_beacon_interval(&mac->chip, interval); - if (r < 0) - return r; - } - } else - associated = is_valid_ether_addr(conf->bssid); - - spin_lock_irq(&mac->lock); - mac->associated = associated; - spin_unlock_irq(&mac->lock); - - /* TODO: do hardware bssid filtering */ - return 0; -} - static void zd_process_intr(struct work_struct *work) { u16 int_status; @@ -923,9 +877,42 @@ static void zd_op_bss_info_changed(struct ieee80211_hw *hw, { struct zd_mac *mac = zd_hw_mac(hw); unsigned long flags; + int associated; dev_dbg_f(zd_mac_dev(mac), "changes: %x\n", changes); + if (mac->type == NL80211_IFTYPE_MESH_POINT || + mac->type == NL80211_IFTYPE_ADHOC) { + associated = true; + if (changes & BSS_CHANGED_BEACON) { + struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); + + if (beacon) { + zd_mac_config_beacon(hw, beacon); + kfree_skb(beacon); + } + } + + if (changes & BSS_CHANGED_BEACON_ENABLED) { + u32 interval; + + if (bss_conf->enable_beacon) + interval = BCN_MODE_IBSS | + bss_conf->beacon_int; + else + interval = 0; + + zd_set_beacon_interval(&mac->chip, interval); + } + } else + associated = is_valid_ether_addr(bss_conf->bssid); + + spin_lock_irq(&mac->lock); + mac->associated = associated; + spin_unlock_irq(&mac->lock); + + /* TODO: do hardware bssid filtering */ + if (changes & BSS_CHANGED_ERP_PREAMBLE) { spin_lock_irqsave(&mac->lock, flags); mac->short_preamble = bss_conf->use_short_preamble; @@ -952,7 +939,6 @@ static const struct ieee80211_ops zd_ops = { .add_interface = zd_op_add_interface, .remove_interface = zd_op_remove_interface, .config = zd_op_config, - .config_interface = zd_op_config_interface, .configure_filter = zd_op_configure_filter, .bss_info_changed = zd_op_bss_info_changed, .get_tsf = zd_op_get_tsf, diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 22c65e8cbb7..7806e22f4ac 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -150,6 +150,12 @@ struct ieee80211_low_level_stats { * @BSS_CHANGED_HT: 802.11n parameters changed * @BSS_CHANGED_BASIC_RATES: Basic rateset changed * @BSS_CHANGED_BEACON_INT: Beacon interval changed + * @BSS_CHANGED_BSSID: BSSID changed, for whatever + * reason (IBSS and managed mode) + * @BSS_CHANGED_BEACON: Beacon data changed, retrieve + * new beacon (beaconing modes) + * @BSS_CHANGED_BEACON_ENABLED: Beaconing should be + * enabled/disabled (beaconing modes) */ enum ieee80211_bss_change { BSS_CHANGED_ASSOC = 1<<0, @@ -159,6 +165,9 @@ enum ieee80211_bss_change { BSS_CHANGED_HT = 1<<4, BSS_CHANGED_BASIC_RATES = 1<<5, BSS_CHANGED_BEACON_INT = 1<<6, + BSS_CHANGED_BSSID = 1<<7, + BSS_CHANGED_BEACON = 1<<8, + BSS_CHANGED_BEACON_ENABLED = 1<<9, }; /** @@ -192,8 +201,11 @@ struct ieee80211_bss_ht_conf { * @basic_rates: bitmap of basic rates, each bit stands for an * index into the rate table configured by the driver in * the current band. + * @bssid: The BSSID for this BSS + * @enable_beacon: whether beaconing should be enabled or not */ struct ieee80211_bss_conf { + const u8 *bssid; /* association related data */ bool assoc; u16 aid; @@ -201,6 +213,7 @@ struct ieee80211_bss_conf { bool use_cts_prot; bool use_short_preamble; bool use_short_slot; + bool enable_beacon; u8 dtim_period; u16 beacon_int; u16 assoc_capability; @@ -659,37 +672,6 @@ struct ieee80211_if_init_conf { void *mac_addr; }; -/** - * enum ieee80211_if_conf_change - interface config change flags - * - * @IEEE80211_IFCC_BSSID: The BSSID changed. - * @IEEE80211_IFCC_BEACON: The beacon for this interface changed - * (currently AP and MESH only), use ieee80211_beacon_get(). - * @IEEE80211_IFCC_BEACON_ENABLED: The enable_beacon value changed. - */ -enum ieee80211_if_conf_change { - IEEE80211_IFCC_BSSID = BIT(0), - IEEE80211_IFCC_BEACON = BIT(1), - IEEE80211_IFCC_BEACON_ENABLED = BIT(2), -}; - -/** - * struct ieee80211_if_conf - configuration of an interface - * - * @changed: parameters that have changed, see &enum ieee80211_if_conf_change. - * @bssid: BSSID of the network we are associated to/creating. - * @enable_beacon: Indicates whether beacons can be sent. - * This is valid only for AP/IBSS/MESH modes. - * - * This structure is passed to the config_interface() callback of - * &struct ieee80211_hw. - */ -struct ieee80211_if_conf { - u32 changed; - const u8 *bssid; - bool enable_beacon; -}; - /** * enum ieee80211_key_alg - key algorithm * @ALG_WEP: WEP40 or WEP104 @@ -1358,10 +1340,6 @@ enum ieee80211_ampdu_mlme_action { * This function should never fail but returns a negative error code * if it does. * - * @config_interface: Handler for configuration requests related to interfaces - * (e.g. BSSID changes.) - * Returns a negative error code which will be seen in userspace. - * * @bss_info_changed: Handler for configuration requests related to BSS * parameters that may vary during BSS's lifespan, and may affect low * level driver (e.g. assoc/disassoc status, erp parameters). @@ -1463,9 +1441,6 @@ struct ieee80211_ops { void (*remove_interface)(struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf); int (*config)(struct ieee80211_hw *hw, u32 changed); - int (*config_interface)(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf); void (*bss_info_changed)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index a898ccd3f2c..648bac1c850 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -521,8 +521,9 @@ static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata, kfree(old); - return ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON | - IEEE80211_IFCC_BEACON_ENABLED); + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | + BSS_CHANGED_BEACON); + return 0; } static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, @@ -573,7 +574,8 @@ static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev) synchronize_rcu(); kfree(old); - return ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON_ENABLED); + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); + return 0; } /* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */ diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index f4879dad3cd..c87caad383f 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -95,17 +95,10 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0; - ieee80211_if_config(sdata, IEEE80211_IFCC_BSSID); - local->oper_channel = chan; local->oper_channel_type = NL80211_CHAN_NO_HT; ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); - sdata->vif.bss_conf.beacon_int = beacon_int; - bss_change = BSS_CHANGED_BEACON_INT; - bss_change |= ieee80211_reset_erp_info(sdata); - ieee80211_bss_info_change_notify(sdata, bss_change); - sband = local->hw.wiphy->bands[chan->band]; /* Build IBSS probe response */ @@ -161,8 +154,13 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, rcu_assign_pointer(ifibss->presp, skb); - ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON | - IEEE80211_IFCC_BEACON_ENABLED); + sdata->vif.bss_conf.beacon_int = beacon_int; + bss_change = BSS_CHANGED_BEACON_INT; + bss_change |= ieee80211_reset_erp_info(sdata); + bss_change |= BSS_CHANGED_BSSID; + bss_change |= BSS_CHANGED_BEACON; + bss_change |= BSS_CHANGED_BEACON_ENABLED; + ieee80211_bss_info_change_notify(sdata, bss_change); rates = 0; for (i = 0; i < supp_rates_len; i++) { @@ -887,7 +885,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) kfree(sdata->u.ibss.ie); skb = sdata->u.ibss.presp; rcu_assign_pointer(sdata->u.ibss.presp, NULL); - ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON_ENABLED); + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); synchronize_rcu(); kfree_skb(skb); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index e6fd4bcf1c3..d8de1e159ee 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -906,7 +906,6 @@ static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr) int ieee80211_hw_config(struct ieee80211_local *local, u32 changed); -int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed); void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx); void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, u32 changed); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index b254879d863..c817c9ef215 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -152,82 +152,6 @@ static void ieee80211_master_set_multicast_list(struct net_device *dev) ieee80211_configure_filter(local); } -/* everything else */ - -int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed) -{ - struct ieee80211_local *local = sdata->local; - struct ieee80211_if_conf conf; - - if (WARN_ON(!netif_running(sdata->dev))) - return 0; - - memset(&conf, 0, sizeof(conf)); - - if (sdata->vif.type == NL80211_IFTYPE_STATION) - conf.bssid = sdata->u.mgd.bssid; - else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) - conf.bssid = sdata->u.ibss.bssid; - else if (sdata->vif.type == NL80211_IFTYPE_AP) - conf.bssid = sdata->dev->dev_addr; - else if (ieee80211_vif_is_mesh(&sdata->vif)) { - static const u8 zero[ETH_ALEN] = { 0 }; - conf.bssid = zero; - } else { - WARN_ON(1); - return -EINVAL; - } - - if (!local->ops->config_interface) - return 0; - - switch (sdata->vif.type) { - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_MESH_POINT: - break; - default: - /* do not warn to simplify caller in scan.c */ - changed &= ~IEEE80211_IFCC_BEACON_ENABLED; - if (WARN_ON(changed & IEEE80211_IFCC_BEACON)) - return -EINVAL; - changed &= ~IEEE80211_IFCC_BEACON; - break; - } - - if (changed & IEEE80211_IFCC_BEACON_ENABLED) { - if (local->sw_scanning) { - conf.enable_beacon = false; - } else { - /* - * Beacon should be enabled, but AP mode must - * check whether there is a beacon configured. - */ - switch (sdata->vif.type) { - case NL80211_IFTYPE_AP: - conf.enable_beacon = - !!rcu_dereference(sdata->u.ap.beacon); - break; - case NL80211_IFTYPE_ADHOC: - conf.enable_beacon = !!sdata->u.ibss.presp; - break; - case NL80211_IFTYPE_MESH_POINT: - conf.enable_beacon = true; - break; - default: - /* not reached */ - WARN_ON(1); - break; - } - } - } - - conf.changed = changed; - - return local->ops->config_interface(local_to_hw(local), - &sdata->vif, &conf); -} - int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) { struct ieee80211_channel *chan; @@ -297,6 +221,61 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, 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) + 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); + return; + } + + switch (sdata->vif.type) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + break; + default: + /* do not warn to simplify caller in scan.c */ + changed &= ~BSS_CHANGED_BEACON_ENABLED; + if (WARN_ON(changed & BSS_CHANGED_BEACON)) + return; + break; + } + + if (changed & BSS_CHANGED_BEACON_ENABLED) { + if (local->sw_scanning) { + sdata->vif.bss_conf.enable_beacon = false; + } else { + /* + * Beacon should be enabled, but AP mode must + * check whether there is a beacon configured. + */ + switch (sdata->vif.type) { + case NL80211_IFTYPE_AP: + sdata->vif.bss_conf.enable_beacon = + !!rcu_dereference(sdata->u.ap.beacon); + break; + case NL80211_IFTYPE_ADHOC: + sdata->vif.bss_conf.enable_beacon = + !!rcu_dereference(sdata->u.ibss.presp); + break; + case NL80211_IFTYPE_MESH_POINT: + sdata->vif.bss_conf.enable_beacon = true; + break; + default: + /* not reached */ + WARN_ON(1); + break; + } + } + } + if (local->ops->bss_info_changed) local->ops->bss_info_changed(local_to_hw(local), &sdata->vif, diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 9a3e5de0410..9000b01a167 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -417,7 +417,7 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata, free_plinks = mesh_plink_availables(sdata); if (free_plinks != sdata->u.mesh.accepting_plinks) - ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON); + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); ifmsh->housekeeping = false; mod_timer(&ifmsh->housekeeping_timer, @@ -432,8 +432,8 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) ifmsh->housekeeping = true; queue_work(local->hw.workqueue, &ifmsh->work); - ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON | - IEEE80211_IFCC_BEACON_ENABLED); + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED); } void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index bfd571e6f22..c7971196d9d 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2289,12 +2289,8 @@ int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid) ifmgd->flags &= ~IEEE80211_STA_BSSID_SET; } - if (netif_running(sdata->dev)) { - if (ieee80211_if_config(sdata, IEEE80211_IFCC_BSSID)) { - printk(KERN_DEBUG "%s: Failed to config new BSSID to " - "the low-level driver\n", sdata->dev->name); - } - } + if (netif_running(sdata->dev)) + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); return ieee80211_sta_commit(sdata); } diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 086d21645cb..04e270abdd2 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -346,8 +346,8 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) if (sdata->vif.type == NL80211_IFTYPE_AP || sdata->vif.type == NL80211_IFTYPE_ADHOC || sdata->vif.type == NL80211_IFTYPE_MESH_POINT) - ieee80211_if_config(sdata, - IEEE80211_IFCC_BEACON_ENABLED); + ieee80211_bss_info_change_notify( + sdata, BSS_CHANGED_BEACON_ENABLED); } mutex_unlock(&local->iflist_mtx); @@ -387,8 +387,8 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) if (sdata->vif.type == NL80211_IFTYPE_AP || sdata->vif.type == NL80211_IFTYPE_ADHOC || sdata->vif.type == NL80211_IFTYPE_MESH_POINT) - ieee80211_if_config(sdata, - IEEE80211_IFCC_BEACON_ENABLED); + ieee80211_bss_info_change_notify( + sdata, BSS_CHANGED_BEACON_ENABLED); if (sdata->vif.type == NL80211_IFTYPE_STATION) { if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) { diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 61876eb50b4..2cde9bbfe7d 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1063,24 +1063,13 @@ int ieee80211_reconfig(struct ieee80211_local *local) switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: /* disable beacon change bits */ - changed &= ~IEEE80211_IFCC_BEACON; + changed &= ~(BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED); /* fall through */ case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: - /* - * Driver's config_interface can fail if rfkill is - * enabled. Accommodate this return code. - * FIXME: When mac80211 has knowledge of rfkill - * state the code below can change back to: - * WARN(ieee80211_if_config(sdata, changed)); - * ieee80211_bss_info_change_notify(sdata, ~0); - */ - if (ieee80211_if_config(sdata, changed)) - printk(KERN_DEBUG "%s: failed to configure interface during resume\n", - sdata->dev->name); - else - ieee80211_bss_info_change_notify(sdata, ~0); + ieee80211_bss_info_change_notify(sdata, changed); break; case NL80211_IFTYPE_WDS: break; -- cgit v1.2.3-70-g09d2 From 4f0fc7c39f2a224b939f22d4dca552b266319525 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 6 May 2009 02:20:00 -0400 Subject: ath9k: make private driver rate tables const On x86 this allows us to do the following small savings: shave off 23 % off of the module's data, and shave off 6 % off of the module's text. We save 456 bytes, for those counting. $ size ath9k.ko text data bss dec hex filename 250794 3628 1600 256022 3e816 ath9k.ko $ size ath9k-old.ko text data bss dec hex filename 239114 15308 1600 256022 3e816 ath9k-old.ko $ du -b ath9k.ko 4034244 ath9k.ko $ du -b ath9k-old.ko 4033788 ath9k-old.ko Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 4 +-- drivers/net/wireless/ath/ath9k/beacon.c | 2 +- drivers/net/wireless/ath/ath9k/hw.c | 2 +- drivers/net/wireless/ath/ath9k/hw.h | 3 +- drivers/net/wireless/ath/ath9k/main.c | 2 +- drivers/net/wireless/ath/ath9k/rc.c | 61 +++++++++++++++++---------------- drivers/net/wireless/ath/ath9k/xmit.c | 8 ++--- 7 files changed, 43 insertions(+), 39 deletions(-) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 90b6314a816..10ffc944285 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -576,8 +576,8 @@ struct ath_softc { struct ath_tx tx; struct ath_beacon beacon; struct ieee80211_rate rates[IEEE80211_NUM_BANDS][ATH_RATE_MAX]; - struct ath_rate_table *hw_rate_table[ATH9K_MODE_MAX]; - struct ath_rate_table *cur_rate_table; + const struct ath_rate_table *hw_rate_table[ATH9K_MODE_MAX]; + const struct ath_rate_table *cur_rate_table; struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; struct ath_led radio_led; diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index c17b8382e32..3a7154beeae 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -63,7 +63,7 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ath_vif *avp, struct ath_hw *ah = sc->sc_ah; struct ath_desc *ds; struct ath9k_11n_rate_series series[4]; - struct ath_rate_table *rt; + const struct ath_rate_table *rt; int flags, antenna, ctsrate = 0, ctsduration = 0; u8 rate; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 02f40154e83..041c0f518ac 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -168,7 +168,7 @@ bool ath9k_get_channel_edges(struct ath_hw *ah, } u16 ath9k_hw_computetxtime(struct ath_hw *ah, - struct ath_rate_table *rates, + const struct ath_rate_table *rates, u32 frameLen, u16 rateix, bool shortPreamble) { diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index ab3412672e3..ddb24c47ebc 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -580,7 +580,8 @@ bool ath9k_hw_setantennaswitch(struct ath_hw *ah, bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout); u32 ath9k_hw_reverse_bits(u32 val, u32 n); bool ath9k_get_channel_edges(struct ath_hw *ah, u16 flags, u16 *low, u16 *high); -u16 ath9k_hw_computetxtime(struct ath_hw *ah, struct ath_rate_table *rates, +u16 ath9k_hw_computetxtime(struct ath_hw *ah, + const struct ath_rate_table *rates, u32 frameLen, u16 rateix, bool shortPreamble); void ath9k_hw_get_channel_centers(struct ath_hw *ah, struct ath9k_channel *chan, diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index d3dc8e2c77b..bbbfdcde272 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -189,7 +189,7 @@ static u8 parse_mpdudensity(u8 mpdudensity) 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; diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index e526dbce57d..ba06e78b2f5 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -17,7 +17,7 @@ #include "ath9k.h" -static struct ath_rate_table ar5416_11na_ratetable = { +static const struct ath_rate_table ar5416_11na_ratetable = { 42, { { VALID, VALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */ @@ -155,7 +155,7 @@ static struct ath_rate_table ar5416_11na_ratetable = { /* 4ms frame limit not used for NG mode. The values filled * for HT are the 64K max aggregate limit */ -static struct ath_rate_table ar5416_11ng_ratetable = { +static const struct ath_rate_table ar5416_11ng_ratetable = { 46, { { VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */ @@ -302,7 +302,7 @@ static struct ath_rate_table ar5416_11ng_ratetable = { WLAN_RC_HT_FLAG, /* Phy rates allowed initially */ }; -static struct ath_rate_table ar5416_11a_ratetable = { +static const struct ath_rate_table ar5416_11a_ratetable = { 8, { { VALID, VALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */ @@ -335,7 +335,7 @@ static struct ath_rate_table ar5416_11a_ratetable = { 0, /* Phy rates allowed initially */ }; -static struct ath_rate_table ar5416_11g_ratetable = { +static const struct ath_rate_table ar5416_11g_ratetable = { 12, { { VALID, VALID, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */ @@ -380,7 +380,7 @@ static struct ath_rate_table ar5416_11g_ratetable = { 0, /* Phy rates allowed initially */ }; -static struct ath_rate_table ar5416_11b_ratetable = { +static const struct ath_rate_table ar5416_11b_ratetable = { 4, { { VALID, VALID, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */ @@ -420,7 +420,7 @@ static inline int8_t median(int8_t a, int8_t b, int8_t c) } } -static void ath_rc_sort_validrates(struct ath_rate_table *rate_table, +static void ath_rc_sort_validrates(const struct ath_rate_table *rate_table, struct ath_rate_priv *ath_rc_priv) { u8 i, j, idx, idx_next; @@ -461,10 +461,11 @@ static inline int ath_rc_isvalid_txmask(struct ath_rate_priv *ath_rc_priv, return ath_rc_priv->valid_rate_index[index]; } -static inline int ath_rc_get_nextvalid_txrate(struct ath_rate_table *rate_table, - struct ath_rate_priv *ath_rc_priv, - u8 cur_valid_txrate, - u8 *next_idx) +static inline +int ath_rc_get_nextvalid_txrate(const struct ath_rate_table *rate_table, + struct ath_rate_priv *ath_rc_priv, + u8 cur_valid_txrate, + u8 *next_idx) { u8 i; @@ -500,7 +501,7 @@ static int ath_rc_valid_phyrate(u32 phy, u32 capflag, int ignore_cw) } static inline int -ath_rc_get_nextlowervalid_txrate(struct ath_rate_table *rate_table, +ath_rc_get_nextlowervalid_txrate(const struct ath_rate_table *rate_table, struct ath_rate_priv *ath_rc_priv, u8 cur_valid_txrate, u8 *next_idx) { @@ -517,7 +518,7 @@ ath_rc_get_nextlowervalid_txrate(struct ath_rate_table *rate_table, } static u8 ath_rc_init_validrates(struct ath_rate_priv *ath_rc_priv, - struct ath_rate_table *rate_table, + const struct ath_rate_table *rate_table, u32 capflag) { u8 i, hi = 0; @@ -547,7 +548,7 @@ static u8 ath_rc_init_validrates(struct ath_rate_priv *ath_rc_priv, } static u8 ath_rc_setvalid_rates(struct ath_rate_priv *ath_rc_priv, - struct ath_rate_table *rate_table, + const struct ath_rate_table *rate_table, struct ath_rateset *rateset, u32 capflag) { @@ -592,7 +593,7 @@ static u8 ath_rc_setvalid_rates(struct ath_rate_priv *ath_rc_priv, } static u8 ath_rc_setvalid_htrates(struct ath_rate_priv *ath_rc_priv, - struct ath_rate_table *rate_table, + const struct ath_rate_table *rate_table, u8 *mcs_set, u32 capflag) { struct ath_rateset *rateset = (struct ath_rateset *)mcs_set; @@ -630,7 +631,7 @@ static u8 ath_rc_setvalid_htrates(struct ath_rate_priv *ath_rc_priv, static u8 ath_rc_ratefind_ht(struct ath_softc *sc, struct ath_rate_priv *ath_rc_priv, - struct ath_rate_table *rate_table, + const struct ath_rate_table *rate_table, int *is_probing) { u32 dt, best_thruput, this_thruput, now_msec; @@ -748,7 +749,7 @@ static u8 ath_rc_ratefind_ht(struct ath_softc *sc, return rate; } -static void ath_rc_rate_set_series(struct ath_rate_table *rate_table, +static void ath_rc_rate_set_series(const struct ath_rate_table *rate_table, struct ieee80211_tx_rate *rate, struct ieee80211_tx_rate_control *txrc, u8 tries, u8 rix, int rtsctsenable) @@ -769,7 +770,7 @@ static void ath_rc_rate_set_series(struct ath_rate_table *rate_table, } static void ath_rc_rate_set_rtscts(struct ath_softc *sc, - struct ath_rate_table *rate_table, + const struct ath_rate_table *rate_table, struct ieee80211_tx_info *tx_info) { struct ieee80211_tx_rate *rates = tx_info->control.rates; @@ -807,7 +808,7 @@ static void ath_rc_rate_set_rtscts(struct ath_softc *sc, static u8 ath_rc_rate_getidx(struct ath_softc *sc, struct ath_rate_priv *ath_rc_priv, - struct ath_rate_table *rate_table, + const struct ath_rate_table *rate_table, u8 rix, u16 stepdown, u16 min_rate) { @@ -838,7 +839,7 @@ static void ath_rc_ratefind(struct ath_softc *sc, struct ath_rate_priv *ath_rc_priv, struct ieee80211_tx_rate_control *txrc) { - struct ath_rate_table *rate_table; + const struct ath_rate_table *rate_table; struct sk_buff *skb = txrc->skb; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_rate *rates = tx_info->control.rates; @@ -937,7 +938,7 @@ static void ath_rc_ratefind(struct ath_softc *sc, } static bool ath_rc_update_per(struct ath_softc *sc, - struct ath_rate_table *rate_table, + const struct ath_rate_table *rate_table, struct ath_rate_priv *ath_rc_priv, struct ath_tx_info_priv *tx_info_priv, int tx_rate, int xretries, int retries, @@ -1142,7 +1143,7 @@ static void ath_rc_update_ht(struct ath_softc *sc, int rate; u8 last_per; bool state_change = false; - struct ath_rate_table *rate_table = sc->cur_rate_table; + const struct ath_rate_table *rate_table = sc->cur_rate_table; int size = ath_rc_priv->rate_table_size; if ((tx_rate < 0) || (tx_rate > rate_table->rate_cnt)) @@ -1276,7 +1277,7 @@ static void ath_rc_update_ht(struct ath_softc *sc, #undef CHK_RSSI } -static int ath_rc_get_rateindex(struct ath_rate_table *rate_table, +static int ath_rc_get_rateindex(const struct ath_rate_table *rate_table, struct ieee80211_tx_rate *rate) { int rix; @@ -1300,7 +1301,7 @@ static void ath_rc_tx_status(struct ath_softc *sc, int final_ts_idx, int xretries, int long_retry) { struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info); - struct ath_rate_table *rate_table; + const struct ath_rate_table *rate_table; struct ieee80211_tx_rate *rates = tx_info->status.rates; u8 flags; u32 i = 0, rix; @@ -1354,9 +1355,11 @@ static void ath_rc_tx_status(struct ath_softc *sc, xretries, long_retry); } -static struct ath_rate_table *ath_choose_rate_table(struct ath_softc *sc, - enum ieee80211_band band, - bool is_ht, bool is_cw_40) +static const +struct ath_rate_table *ath_choose_rate_table(struct ath_softc *sc, + enum ieee80211_band band, + bool is_ht, + bool is_cw_40) { int mode = 0; @@ -1390,7 +1393,7 @@ static void ath_rc_init(struct ath_softc *sc, struct ath_rate_priv *ath_rc_priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, - struct ath_rate_table *rate_table) + const struct ath_rate_table *rate_table) { struct ath_rateset *rateset = &ath_rc_priv->neg_rates; u8 *ht_mcs = (u8 *)&ath_rc_priv->neg_ht_rates; @@ -1587,7 +1590,7 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband, { struct ath_softc *sc = priv; struct ath_rate_priv *ath_rc_priv = priv_sta; - struct ath_rate_table *rate_table = NULL; + const struct ath_rate_table *rate_table = NULL; bool is_cw40, is_sgi40; int i, j = 0; @@ -1636,7 +1639,7 @@ static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, { struct ath_softc *sc = priv; struct ath_rate_priv *ath_rc_priv = priv_sta; - struct ath_rate_table *rate_table = NULL; + const struct ath_rate_table *rate_table = NULL; bool oper_cw40 = false, oper_sgi40; bool local_cw40 = (ath_rc_priv->ht_cap & WLAN_RC_40_FLAG) ? true : false; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 501493ffc3a..41c42824a5c 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -434,7 +434,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, struct ath_atx_tid *tid) { - struct ath_rate_table *rate_table = sc->cur_rate_table; + const struct ath_rate_table *rate_table = sc->cur_rate_table; struct sk_buff *skb; struct ieee80211_tx_info *tx_info; struct ieee80211_tx_rate *rates; @@ -497,7 +497,7 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid, struct ath_buf *bf, u16 frmlen) { - struct ath_rate_table *rt = sc->cur_rate_table; + const struct ath_rate_table *rt = sc->cur_rate_table; struct sk_buff *skb = bf->bf_mpdu; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); u32 nsymbits, nsymbols, mpdudensity; @@ -1407,7 +1407,7 @@ static int setup_tx_flags(struct ath_softc *sc, struct sk_buff *skb, static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, struct ath_buf *bf, int width, int half_gi, bool shortPreamble) { - struct ath_rate_table *rate_table = sc->cur_rate_table; + const struct ath_rate_table *rate_table = sc->cur_rate_table; u32 nbits, nsymbits, duration, nsymbols; u8 rc; int streams, pktlen; @@ -1439,7 +1439,7 @@ static u32 ath_pkt_duration(struct ath_softc *sc, u8 rix, struct ath_buf *bf, static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf) { - struct ath_rate_table *rt = sc->cur_rate_table; + const struct ath_rate_table *rt = sc->cur_rate_table; struct ath9k_11n_rate_series series[4]; struct sk_buff *skb; struct ieee80211_tx_info *tx_info; -- cgit v1.2.3-70-g09d2 From 6b45784fbe9b31c287ab7f2717e4bd4cdcdd340c Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Fri, 15 May 2009 18:59:20 +0530 Subject: ath9k: Print hw reset failure status as signed int Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 10 +++++----- drivers/net/wireless/ath/ath9k/xmit.c | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index bbbfdcde272..0e8f6d41a37 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -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; @@ -1094,7 +1094,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 +1146,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); @@ -1709,7 +1709,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 +2001,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); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 41c42824a5c..3f2bd79f945 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -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); } -- cgit v1.2.3-70-g09d2 From 153e080da6a07ed888a0a59c45e28bc7351407ff Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Fri, 15 May 2009 02:47:16 -0400 Subject: ath9k: Move PS wakeup/restore calls from isr to tasklet We do not need to do this in ath_isr() and it looks like the modified version ends up being more stable as far as being able receive beacon frames is concerned. Furthermore, this reduces need to move between AWAKE and NETWORK SLEEP states when processing some unrelated interrupts. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 22 +++++++++------------- drivers/net/wireless/ath/ath9k/recv.c | 2 -- 2 files changed, 9 insertions(+), 15 deletions(-) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 0e8f6d41a37..c161b75cded 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -455,8 +455,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; } @@ -471,6 +474,7 @@ static void ath9k_tasklet(unsigned long data) /* 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 +502,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 +521,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 +559,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) { diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 72e9283bcf7..5567517aa64 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -508,8 +508,6 @@ static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb) 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); - if (sc->hw->conf.flags & IEEE80211_CONF_PS) - ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP); } static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) -- cgit v1.2.3-70-g09d2 From 9a23f9ca50943c1b4535d22b3acda3c31b4ad072 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 19 May 2009 17:01:38 +0300 Subject: ath9k: Wake up for TX in mac80211 timeout=0 sleep mode When using timeout=0 (PS-Poll) with mac80211, the driver will need to wake up for TX requests and remain awake until the TX has been completed (ACK received or timeout) or until the buffer frame(s) have been received (in case the TX is for a PS-Poll frame). Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 6 +++++- drivers/net/wireless/ath/ath9k/main.c | 30 +++++++++++++++++++++++++++++- drivers/net/wireless/ath/ath9k/recv.c | 16 ++++++++++++++-- drivers/net/wireless/ath/ath9k/xmit.c | 10 ++++++++++ 4 files changed, 58 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index bd2363f075c..0d4ac43f6d9 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -516,6 +516,8 @@ struct ath_rfkill { #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) struct ath_bus_ops { void (*read_cachesize)(struct ath_softc *sc, int *csz); @@ -678,7 +680,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); } diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index c161b75cded..7c3a98b1957 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2070,6 +2070,31 @@ static int ath9k_tx(struct ieee80211_hw *hw, goto exit; } + 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)); /* @@ -2307,7 +2332,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, diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 5567517aa64..5e046b58ad9 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -560,7 +560,8 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb) hdr = (struct ieee80211_hdr *)skb->data; /* Process Beacon and CAB receive in PS state */ - if (ieee80211_is_beacon(hdr->frame_control)) + 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) || @@ -574,6 +575,16 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb) * 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)); } } @@ -798,7 +809,8 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) sc->rx.rxotherant = 0; } - if (unlikely(sc->sc_flags & SC_OP_WAIT_FOR_BEACON)) + 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); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 3f2bd79f945..a8def4fa449 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -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 -- cgit v1.2.3-70-g09d2 From 1ffc1c61bdbe31105d99d65043bbf7ae6d24dbc7 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 19 May 2009 17:01:39 +0300 Subject: ath9k: Do not try to calibrate radio when in sleep mode When the chip is in sleep mode, there is no point trying to calibrate the radio since it will just results in incorrect values being read from registers and other potential issues. In addition, if we actually start processing calibrate, do not allow the chip to be put into sleep until we have completed the calibration step. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 7c3a98b1957..dd54fa727a6 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -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. -- cgit v1.2.3-70-g09d2 From 54ce846e2c5ade77bc6650d8e8c7e775e01ad859 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 19 May 2009 17:01:40 +0300 Subject: ath9k: Use TSFOOR interrupt to trigger TSF sync with next Beacon If the chip complains about TSF sync, make sure we remain awake to sync with the next Beacon frame. In theory, this should not be needed since we are currently trying to receive all Beacon frames, anyway, better have this code ready should we ever change that. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index dd54fa727a6..d2ef9732c1d 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -480,6 +480,16 @@ 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; + } + /* re-enable hardware interrupt */ ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); ath9k_ps_restore(sc); -- cgit v1.2.3-70-g09d2 From aa68aeaaff8bbf58e355eb83b7d0c14ce82b2f12 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 19 May 2009 17:01:41 +0300 Subject: ath9k: Wake up for RX filter changes We must make sure the chip is awake when changing the RX filter parameters. This could have caused problems, e.g., when changing the interface to promiscuous mode while in sleep mode. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index d2ef9732c1d..d6545b3538b 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2428,8 +2428,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); } -- cgit v1.2.3-70-g09d2 From dc8c4585d2e6f3bf5c9d8c0a6036b591bd3baf2e Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 19 May 2009 17:01:42 +0300 Subject: ath9k: Set PM field in frame control when in PS mode mac80211 does not set PM field for normal data frames, so we need to update that based on the current PS mode when using PS-Poll (timeout=0) power save mode. This allows the AP to remain in sync with our PS state. However, there is still a potential race condition between PS state changes when multiple TX queues are used and nullfunc and PS-Poll frames use different queue. That corner case may need to be handled separately by changing which queue is used either in ath9k or mac80211. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index d6545b3538b..77fbc46624b 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2088,6 +2088,21 @@ 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 -- cgit v1.2.3-70-g09d2 From c26c2e576dc05d90e8eadc0c6ca273ed7aa12535 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 19 May 2009 18:27:11 -0400 Subject: ath9k: fix custom regulatory call position ath_regd_init() needs to be called with the wiphy already properly set with the bands. Without this the custom regulatory settings were not taking effect, and the device would get the default channel settings from ath9k_[25]ghz_chantable. Reported-by: Johannes Berg Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 77fbc46624b..1839b559bdc 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1430,8 +1430,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; @@ -1644,14 +1642,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)) -- cgit v1.2.3-70-g09d2 From eeddfd9db3b1c50d49202d0be35aae187fa90129 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 19 May 2009 17:49:46 -0400 Subject: ath9k: set max default eirp to 20 dBm This is always discarded anyway but lets just set this to our safest lowest. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 1839b559bdc..5759c946502 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -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 -- cgit v1.2.3-70-g09d2 From ccdfeab6536ae55d43436ffaae949afde6e962ca Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 20 May 2009 21:59:08 +0300 Subject: ath9k: Update Beacon timers based on timestamp from the AP Some APs seem to drift away from the expected TBTT (timestamp % beacon_int_in_usec differs quite a bit from zero) which can result in us waking up way too early to receive a Beacon frame. In order to work around this, re-configure the Beacon timers after having received a Beacon frame from the AP (i.e., when we know the offset between the expected TBTT and the actual time the AP is sending out the Beacon frame). Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 + drivers/net/wireless/ath/ath9k/main.c | 9 ++++++++- drivers/net/wireless/ath/ath9k/recv.c | 7 +++++++ 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 0d4ac43f6d9..796a3adffea 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -518,6 +518,7 @@ struct ath_rfkill { #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); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 5759c946502..61da08a1648 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -487,7 +487,7 @@ static void ath9k_tasklet(unsigned long data) * the next Beacon. */ DPRINTF(sc, ATH_DBG_PS, "TSFOOR - Sync with next Beacon\n"); - sc->sc_flags |= SC_OP_WAIT_FOR_BEACON; + sc->sc_flags |= SC_OP_WAIT_FOR_BEACON | SC_OP_BEACON_SYNC; } /* re-enable hardware interrupt */ @@ -914,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 */ diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 5e046b58ad9..5014a19b0f7 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -521,6 +521,13 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) 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 " -- cgit v1.2.3-70-g09d2 From 19d337dff95cbf76edd3ad95c0cee2732c3e1ec5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 2 Jun 2009 13:01:37 +0200 Subject: rfkill: rewrite This patch completely rewrites the rfkill core to address the following deficiencies: * all rfkill drivers need to implement polling where necessary rather than having one central implementation * updating the rfkill state cannot be done from arbitrary contexts, forcing drivers to use schedule_work and requiring lots of code * rfkill drivers need to keep track of soft/hard blocked internally -- the core should do this * the rfkill API has many unexpected quirks, for example being asymmetric wrt. alloc/free and register/unregister * rfkill can call back into a driver from within a function the driver called -- this is prone to deadlocks and generally should be avoided * rfkill-input pointlessly is a separate module * drivers need to #ifdef rfkill functions (unless they want to depend on or select RFKILL) -- rfkill should provide inlines that do nothing if it isn't compiled in * the rfkill structure is not opaque -- drivers need to initialise it correctly (lots of sanity checking code required) -- instead force drivers to pass the right variables to rfkill_alloc() * the documentation is hard to read because it always assumes the reader is completely clueless and contains way TOO MANY CAPS * the rfkill code needlessly uses a lot of locks and atomic operations in locked sections * fix LED trigger to actually change the LED when the radio state changes -- this wasn't done before Tested-by: Alan Jenkins Signed-off-by: Henrique de Moraes Holschuh [thinkpad] Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- Documentation/rfkill.txt | 597 +++---------------- MAINTAINERS | 6 +- arch/arm/mach-pxa/tosa-bt.c | 30 +- arch/arm/mach-pxa/tosa.c | 1 - drivers/net/usb/hso.c | 42 +- drivers/net/wireless/ath/ath9k/ath9k.h | 7 +- drivers/net/wireless/ath/ath9k/main.c | 115 +--- drivers/net/wireless/ath/ath9k/pci.c | 15 - drivers/net/wireless/b43/Kconfig | 2 +- drivers/net/wireless/b43/leds.c | 2 +- drivers/net/wireless/b43/main.c | 4 +- drivers/net/wireless/b43/phy_a.c | 4 +- drivers/net/wireless/b43/phy_common.c | 17 +- drivers/net/wireless/b43/phy_common.h | 4 +- drivers/net/wireless/b43/phy_g.c | 4 +- drivers/net/wireless/b43/phy_lp.c | 2 +- drivers/net/wireless/b43/phy_n.c | 2 +- drivers/net/wireless/b43/rfkill.c | 123 ++-- drivers/net/wireless/b43/rfkill.h | 5 +- drivers/net/wireless/b43legacy/Kconfig | 2 +- drivers/net/wireless/b43legacy/leds.c | 3 +- drivers/net/wireless/b43legacy/rfkill.c | 123 ++-- drivers/net/wireless/b43legacy/rfkill.h | 6 +- drivers/net/wireless/iwlwifi/Kconfig | 5 +- drivers/net/wireless/iwlwifi/iwl-rfkill.c | 69 +-- drivers/net/wireless/iwmc3200wifi/rfkill.c | 39 +- drivers/platform/x86/Kconfig | 14 +- drivers/platform/x86/acer-wmi.c | 50 +- drivers/platform/x86/dell-laptop.c | 101 ++-- drivers/platform/x86/eeepc-laptop.c | 99 +--- drivers/platform/x86/hp-wmi.c | 103 ++-- drivers/platform/x86/sony-laptop.c | 191 +++--- drivers/platform/x86/thinkpad_acpi.c | 873 ++++++++++++++-------------- drivers/platform/x86/toshiba_acpi.c | 159 ++--- include/linux/Kbuild | 1 + include/linux/rfkill.h | 325 ++++++++--- include/net/wimax.h | 8 +- net/rfkill/Kconfig | 21 +- net/rfkill/Makefile | 5 +- net/rfkill/core.c | 896 +++++++++++++++++++++++++++++ net/rfkill/input.c | 342 +++++++++++ net/rfkill/rfkill-input.c | 390 ------------- net/rfkill/rfkill-input.h | 21 - net/rfkill/rfkill.c | 855 --------------------------- net/rfkill/rfkill.h | 27 + net/wimax/Kconfig | 14 - net/wimax/op-rfkill.c | 123 +--- 47 files changed, 2555 insertions(+), 3292 deletions(-) create mode 100644 net/rfkill/core.c create mode 100644 net/rfkill/input.c delete mode 100644 net/rfkill/rfkill-input.c delete mode 100644 net/rfkill/rfkill-input.h delete mode 100644 net/rfkill/rfkill.c create mode 100644 net/rfkill/rfkill.h (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/Documentation/rfkill.txt b/Documentation/rfkill.txt index 40c3a3f1081..de941e309d4 100644 --- a/Documentation/rfkill.txt +++ b/Documentation/rfkill.txt @@ -1,571 +1,130 @@ -rfkill - RF switch subsystem support -==================================== +rfkill - RF kill switch support +=============================== -1 Introduction -2 Implementation details -3 Kernel driver guidelines -3.1 wireless device drivers -3.2 platform/switch drivers -3.3 input device drivers -4 Kernel API -5 Userspace support +1. Introduction +2. Implementation details +3. Kernel driver guidelines +4. Kernel API +5. Userspace support -1. Introduction: +1. Introduction -The rfkill switch subsystem exists to add a generic interface to circuitry that -can enable or disable the signal output of a wireless *transmitter* of any -type. By far, the most common use is to disable radio-frequency transmitters. +The rfkill subsystem provides a generic interface to disabling any radio +transmitter in the system. When a transmitter is blocked, it shall not +radiate any power. -Note that disabling the signal output means that the the transmitter is to be -made to not emit any energy when "blocked". rfkill is not about blocking data -transmissions, it is about blocking energy emission. +The subsystem also provides the ability to react on button presses and +disable all transmitters of a certain type (or all). This is intended for +situations where transmitters need to be turned off, for example on +aircraft. -The rfkill subsystem offers support for keys and switches often found on -laptops to enable wireless devices like WiFi and Bluetooth, so that these keys -and switches actually perform an action in all wireless devices of a given type -attached to the system. -The buttons to enable and disable the wireless transmitters are important in -situations where the user is for example using his laptop on a location where -radio-frequency transmitters _must_ be disabled (e.g. airplanes). -Because of this requirement, userspace support for the keys should not be made -mandatory. Because userspace might want to perform some additional smarter -tasks when the key is pressed, rfkill provides userspace the possibility to -take over the task to handle the key events. - -=============================================================================== -2: Implementation details +2. Implementation details The rfkill subsystem is composed of various components: the rfkill class, the rfkill-input module (an input layer handler), and some specific input layer events. -The rfkill class provides kernel drivers with an interface that allows them to -know when they should enable or disable a wireless network device transmitter. -This is enabled by the CONFIG_RFKILL Kconfig option. - -The rfkill class support makes sure userspace will be notified of all state -changes on rfkill devices through uevents. It provides a notification chain -for interested parties in the kernel to also get notified of rfkill state -changes in other drivers. It creates several sysfs entries which can be used -by userspace. See section "Userspace support". - -The rfkill-input module provides the kernel with the ability to implement a -basic response when the user presses a key or button (or toggles a switch) -related to rfkill functionality. It is an in-kernel implementation of default -policy of reacting to rfkill-related input events and neither mandatory nor -required for wireless drivers to operate. It is enabled by the -CONFIG_RFKILL_INPUT Kconfig option. - -rfkill-input is a rfkill-related events input layer handler. This handler will -listen to all rfkill key events and will change the rfkill state of the -wireless devices accordingly. With this option enabled userspace could either -do nothing or simply perform monitoring tasks. - -The rfkill-input module also provides EPO (emergency power-off) functionality -for all wireless transmitters. This function cannot be overridden, and it is -always active. rfkill EPO is related to *_RFKILL_ALL input layer events. - - -Important terms for the rfkill subsystem: - -In order to avoid confusion, we avoid the term "switch" in rfkill when it is -referring to an electronic control circuit that enables or disables a -transmitter. We reserve it for the physical device a human manipulates -(which is an input device, by the way): - -rfkill switch: - - A physical device a human manipulates. Its state can be perceived by - the kernel either directly (through a GPIO pin, ACPI GPE) or by its - effect on a rfkill line of a wireless device. - -rfkill controller: - - A hardware circuit that controls the state of a rfkill line, which a - kernel driver can interact with *to modify* that state (i.e. it has - either write-only or read/write access). - -rfkill line: - - An input channel (hardware or software) of a wireless device, which - causes a wireless transmitter to stop emitting energy (BLOCK) when it - is active. Point of view is extremely important here: rfkill lines are - always seen from the PoV of a wireless device (and its driver). - -soft rfkill line/software rfkill line: - - A rfkill line the wireless device driver can directly change the state - of. Related to rfkill_state RFKILL_STATE_SOFT_BLOCKED. - -hard rfkill line/hardware rfkill line: - - A rfkill line that works fully in hardware or firmware, and that cannot - be overridden by the kernel driver. The hardware device or the - firmware just exports its status to the driver, but it is read-only. - Related to rfkill_state RFKILL_STATE_HARD_BLOCKED. - -The enum rfkill_state describes the rfkill state of a transmitter: - -When a rfkill line or rfkill controller is in the RFKILL_STATE_UNBLOCKED state, -the wireless transmitter (radio TX circuit for example) is *enabled*. When the -it is in the RFKILL_STATE_SOFT_BLOCKED or RFKILL_STATE_HARD_BLOCKED, the -wireless transmitter is to be *blocked* from operating. - -RFKILL_STATE_SOFT_BLOCKED indicates that a call to toggle_radio() can change -that state. RFKILL_STATE_HARD_BLOCKED indicates that a call to toggle_radio() -will not be able to change the state and will return with a suitable error if -attempts are made to set the state to RFKILL_STATE_UNBLOCKED. - -RFKILL_STATE_HARD_BLOCKED is used by drivers to signal that the device is -locked in the BLOCKED state by a hardwire rfkill line (typically an input pin -that, when active, forces the transmitter to be disabled) which the driver -CANNOT override. - -Full rfkill functionality requires two different subsystems to cooperate: the -input layer and the rfkill class. The input layer issues *commands* to the -entire system requesting that devices registered to the rfkill class change -state. The way this interaction happens is not complex, but it is not obvious -either: - -Kernel Input layer: - - * Generates KEY_WWAN, KEY_WLAN, KEY_BLUETOOTH, SW_RFKILL_ALL, and - other such events when the user presses certain keys, buttons, or - toggles certain physical switches. - - THE INPUT LAYER IS NEVER USED TO PROPAGATE STATUS, NOTIFICATIONS OR THE - KIND OF STUFF AN ON-SCREEN-DISPLAY APPLICATION WOULD REPORT. It is - used to issue *commands* for the system to change behaviour, and these - commands may or may not be carried out by some kernel driver or - userspace application. It follows that doing user feedback based only - on input events is broken, as there is no guarantee that an input event - will be acted upon. - - Most wireless communication device drivers implementing rfkill - functionality MUST NOT generate these events, and have no reason to - register themselves with the input layer. Doing otherwise is a common - misconception. There is an API to propagate rfkill status change - information, and it is NOT the input layer. - -rfkill class: - - * Calls a hook in a driver to effectively change the wireless - transmitter state; - * Keeps track of the wireless transmitter state (with help from - the driver); - * Generates userspace notifications (uevents) and a call to a - notification chain (kernel) when there is a wireless transmitter - state change; - * Connects a wireless communications driver with the common rfkill - control system, which, for example, allows actions such as - "switch all bluetooth devices offline" to be carried out by - userspace or by rfkill-input. - - THE RFKILL CLASS NEVER ISSUES INPUT EVENTS. THE RFKILL CLASS DOES - NOT LISTEN TO INPUT EVENTS. NO DRIVER USING THE RFKILL CLASS SHALL - EVER LISTEN TO, OR ACT ON RFKILL INPUT EVENTS. Doing otherwise is - a layering violation. - - Most wireless data communication drivers in the kernel have just to - implement the rfkill class API to work properly. Interfacing to the - input layer is not often required (and is very often a *bug*) on - wireless drivers. - - Platform drivers often have to attach to the input layer to *issue* - (but never to listen to) rfkill events for rfkill switches, and also to - the rfkill class to export a control interface for the platform rfkill - controllers to the rfkill subsystem. This does NOT mean the rfkill - switch is attached to a rfkill class (doing so is almost always wrong). - It just means the same kernel module is the driver for different - devices (rfkill switches and rfkill controllers). - - -Userspace input handlers (uevents) or kernel input handlers (rfkill-input): - - * Implements the policy of what should happen when one of the input - layer events related to rfkill operation is received. - * Uses the sysfs interface (userspace) or private rfkill API calls - to tell the devices registered with the rfkill class to change - their state (i.e. translates the input layer event into real - action). - - * rfkill-input implements EPO by handling EV_SW SW_RFKILL_ALL 0 - (power off all transmitters) in a special way: it ignores any - overrides and local state cache and forces all transmitters to the - RFKILL_STATE_SOFT_BLOCKED state (including those which are already - supposed to be BLOCKED). - * rfkill EPO will remain active until rfkill-input receives an - EV_SW SW_RFKILL_ALL 1 event. While the EPO is active, transmitters - are locked in the blocked state (rfkill will refuse to unblock them). - * rfkill-input implements different policies that the user can - select for handling EV_SW SW_RFKILL_ALL 1. It will unlock rfkill, - and either do nothing (leave transmitters blocked, but now unlocked), - restore the transmitters to their state before the EPO, or unblock - them all. - -Userspace uevent handler or kernel platform-specific drivers hooked to the -rfkill notifier chain: - - * Taps into the rfkill notifier chain or to KOBJ_CHANGE uevents, - in order to know when a device that is registered with the rfkill - class changes state; - * Issues feedback notifications to the user; - * In the rare platforms where this is required, synthesizes an input - event to command all *OTHER* rfkill devices to also change their - statues when a specific rfkill device changes state. - - -=============================================================================== -3: Kernel driver guidelines - -Remember: point-of-view is everything for a driver that connects to the rfkill -subsystem. All the details below must be measured/perceived from the point of -view of the specific driver being modified. - -The first thing one needs to know is whether his driver should be talking to -the rfkill class or to the input layer. In rare cases (platform drivers), it -could happen that you need to do both, as platform drivers often handle a -variety of devices in the same driver. - -Do not mistake input devices for rfkill controllers. The only type of "rfkill -switch" device that is to be registered with the rfkill class are those -directly controlling the circuits that cause a wireless transmitter to stop -working (or the software equivalent of them), i.e. what we call a rfkill -controller. Every other kind of "rfkill switch" is just an input device and -MUST NOT be registered with the rfkill class. - -A driver should register a device with the rfkill class when ALL of the -following conditions are met (they define a rfkill controller): - -1. The device is/controls a data communications wireless transmitter; - -2. The kernel can interact with the hardware/firmware to CHANGE the wireless - transmitter state (block/unblock TX operation); - -3. The transmitter can be made to not emit any energy when "blocked": - rfkill is not about blocking data transmissions, it is about blocking - energy emission; - -A driver should register a device with the input subsystem to issue -rfkill-related events (KEY_WLAN, KEY_BLUETOOTH, KEY_WWAN, KEY_WIMAX, -SW_RFKILL_ALL, etc) when ALL of the folowing conditions are met: - -1. It is directly related to some physical device the user interacts with, to - command the O.S./firmware/hardware to enable/disable a data communications - wireless transmitter. - - Examples of the physical device are: buttons, keys and switches the user - will press/touch/slide/switch to enable or disable the wireless - communication device. - -2. It is NOT slaved to another device, i.e. there is no other device that - issues rfkill-related input events in preference to this one. - - Please refer to the corner cases and examples section for more details. - -When in doubt, do not issue input events. For drivers that should generate -input events in some platforms, but not in others (e.g. b43), the best solution -is to NEVER generate input events in the first place. That work should be -deferred to a platform-specific kernel module (which will know when to generate -events through the rfkill notifier chain) or to userspace. This avoids the -usual maintenance problems with DMI whitelisting. - - -Corner cases and examples: -==================================== - -1. If the device is an input device that, because of hardware or firmware, -causes wireless transmitters to be blocked regardless of the kernel's will, it -is still just an input device, and NOT to be registered with the rfkill class. - -2. If the wireless transmitter switch control is read-only, it is an input -device and not to be registered with the rfkill class (and maybe not to be made -an input layer event source either, see below). - -3. If there is some other device driver *closer* to the actual hardware the -user interacted with (the button/switch/key) to issue an input event, THAT is -the device driver that should be issuing input events. - -E.g: - [RFKILL slider switch] -- [GPIO hardware] -- [WLAN card rf-kill input] - (platform driver) (wireless card driver) - -The user is closer to the RFKILL slide switch plaform driver, so the driver -which must issue input events is the platform driver looking at the GPIO -hardware, and NEVER the wireless card driver (which is just a slave). It is -very likely that there are other leaves than just the WLAN card rf-kill input -(e.g. a bluetooth card, etc)... - -On the other hand, some embedded devices do this: - - [RFKILL slider switch] -- [WLAN card rf-kill input] - (wireless card driver) - -In this situation, the wireless card driver *could* register itself as an input -device and issue rf-kill related input events... but in order to AVOID the need -for DMI whitelisting, the wireless card driver does NOT do it. Userspace (HAL) -or a platform driver (that exists only on these embedded devices) will do the -dirty job of issuing the input events. - - -COMMON MISTAKES in kernel drivers, related to rfkill: -==================================== - -1. NEVER confuse input device keys and buttons with input device switches. - - 1a. Switches are always set or reset. They report the current state - (on position or off position). - - 1b. Keys and buttons are either in the pressed or not-pressed state, and - that's it. A "button" that latches down when you press it, and - unlatches when you press it again is in fact a switch as far as input - devices go. - -Add the SW_* events you need for switches, do NOT try to emulate a button using -KEY_* events just because there is no such SW_* event yet. Do NOT try to use, -for example, KEY_BLUETOOTH when you should be using SW_BLUETOOTH instead. - -2. Input device switches (sources of EV_SW events) DO store their current state -(so you *must* initialize it by issuing a gratuitous input layer event on -driver start-up and also when resuming from sleep), and that state CAN be -queried from userspace through IOCTLs. There is no sysfs interface for this, -but that doesn't mean you should break things trying to hook it to the rfkill -class to get a sysfs interface :-) - -3. Do not issue *_RFKILL_ALL events by default, unless you are sure it is the -correct event for your switch/button. These events are emergency power-off -events when they are trying to turn the transmitters off. An example of an -input device which SHOULD generate *_RFKILL_ALL events is the wireless-kill -switch in a laptop which is NOT a hotkey, but a real sliding/rocker switch. -An example of an input device which SHOULD NOT generate *_RFKILL_ALL events by -default, is any sort of hot key that is type-specific (e.g. the one for WLAN). - - -3.1 Guidelines for wireless device drivers ------------------------------------------- - -(in this text, rfkill->foo means the foo field of struct rfkill). - -1. Each independent transmitter in a wireless device (usually there is only one -transmitter per device) should have a SINGLE rfkill class attached to it. - -2. If the device does not have any sort of hardware assistance to allow the -driver to rfkill the device, the driver should emulate it by taking all actions -required to silence the transmitter. - -3. If it is impossible to silence the transmitter (i.e. it still emits energy, -even if it is just in brief pulses, when there is no data to transmit and there -is no hardware support to turn it off) do NOT lie to the users. Do not attach -it to a rfkill class. The rfkill subsystem does not deal with data -transmission, it deals with energy emission. If the transmitter is emitting -energy, it is not blocked in rfkill terms. - -4. It doesn't matter if the device has multiple rfkill input lines affecting -the same transmitter, their combined state is to be exported as a single state -per transmitter (see rule 1). - -This rule exists because users of the rfkill subsystem expect to get (and set, -when possible) the overall transmitter rfkill state, not of a particular rfkill -line. +The rfkill class is provided for kernel drivers to register their radio +transmitter with the kernel, provide methods for turning it on and off and, +optionally, letting the system know about hardware-disabled states that may +be implemented on the device. This code is enabled with the CONFIG_RFKILL +Kconfig option, which drivers can "select". -5. The wireless device driver MUST NOT leave the transmitter enabled during -suspend and hibernation unless: +The rfkill class code also notifies userspace of state changes, this is +achieved via uevents. It also provides some sysfs files for userspace to +check the status of radio transmitters. See the "Userspace support" section +below. - 5.1. The transmitter has to be enabled for some sort of functionality - like wake-on-wireless-packet or autonomous packed forwarding in a mesh - network, and that functionality is enabled for this suspend/hibernation - cycle. -AND +The rfkill-input code implements a basic response to rfkill buttons -- it +implements turning on/off all devices of a certain class (or all). - 5.2. The device was not on a user-requested BLOCKED state before - the suspend (i.e. the driver must NOT unblock a device, not even - to support wake-on-wireless-packet or remain in the mesh). +When the device is hard-blocked (either by a call to rfkill_set_hw_state() +or from query_hw_block) set_block() will be invoked but drivers can well +ignore the method call since they can use the return value of the function +rfkill_set_hw_state() to sync the software state instead of keeping track +of calls to set_block(). -In other words, there is absolutely no allowed scenario where a driver can -automatically take action to unblock a rfkill controller (obviously, this deals -with scenarios where soft-blocking or both soft and hard blocking is happening. -Scenarios where hardware rfkill lines are the only ones blocking the -transmitter are outside of this rule, since the wireless device driver does not -control its input hardware rfkill lines in the first place). -6. During resume, rfkill will try to restore its previous state. +The entire functionality is spread over more than one subsystem: -7. After a rfkill class is suspended, it will *not* call rfkill->toggle_radio -until it is resumed. + * The kernel input layer generates KEY_WWAN, KEY_WLAN etc. and + SW_RFKILL_ALL -- when the user presses a button. Drivers for radio + transmitters generally do not register to the input layer, unless the + device really provides an input device (i.e. a button that has no + effect other than generating a button press event) + * The rfkill-input code hooks up to these events and switches the soft-block + of the various radio transmitters, depending on the button type. -Example of a WLAN wireless driver connected to the rfkill subsystem: --------------------------------------------------------------------- + * The rfkill drivers turn off/on their transmitters as requested. -A certain WLAN card has one input pin that causes it to block the transmitter -and makes the status of that input pin available (only for reading!) to the -kernel driver. This is a hard rfkill input line (it cannot be overridden by -the kernel driver). + * The rfkill class will generate userspace notifications (uevents) to tell + userspace what the current state is. -The card also has one PCI register that, if manipulated by the driver, causes -it to block the transmitter. This is a soft rfkill input line. -It has also a thermal protection circuitry that shuts down its transmitter if -the card overheats, and makes the status of that protection available (only for -reading!) to the kernel driver. This is also a hard rfkill input line. -If either one of these rfkill lines are active, the transmitter is blocked by -the hardware and forced offline. +3. Kernel driver guidelines -The driver should allocate and attach to its struct device *ONE* instance of -the rfkill class (there is only one transmitter). -It can implement the get_state() hook, and return RFKILL_STATE_HARD_BLOCKED if -either one of its two hard rfkill input lines are active. If the two hard -rfkill lines are inactive, it must return RFKILL_STATE_SOFT_BLOCKED if its soft -rfkill input line is active. Only if none of the rfkill input lines are -active, will it return RFKILL_STATE_UNBLOCKED. +Drivers for radio transmitters normally implement only the rfkill class. +These drivers may not unblock the transmitter based on own decisions, they +should act on information provided by the rfkill class only. -Since the device has a hardware rfkill line, it IS subject to state changes -external to rfkill. Therefore, the driver must make sure that it calls -rfkill_force_state() to keep the status always up-to-date, and it must do a -rfkill_force_state() on resume from sleep. +Platform drivers might implement input devices if the rfkill button is just +that, a button. If that button influences the hardware then you need to +implement an rfkill class instead. This also applies if the platform provides +a way to turn on/off the transmitter(s). -Every time the driver gets a notification from the card that one of its rfkill -lines changed state (polling might be needed on badly designed cards that don't -generate interrupts for such events), it recomputes the rfkill state as per -above, and calls rfkill_force_state() to update it. +During suspend/hibernation, transmitters should only be left enabled when +wake-on wlan or similar functionality requires it and the device wasn't +blocked before suspend/hibernate. Note that it may be necessary to update +the rfkill subsystem's idea of what the current state is at resume time if +the state may have changed over suspend. -The driver should implement the toggle_radio() hook, that: -1. Returns an error if one of the hardware rfkill lines are active, and the -caller asked for RFKILL_STATE_UNBLOCKED. -2. Activates the soft rfkill line if the caller asked for state -RFKILL_STATE_SOFT_BLOCKED. It should do this even if one of the hard rfkill -lines are active, effectively double-blocking the transmitter. - -3. Deactivates the soft rfkill line if none of the hardware rfkill lines are -active and the caller asked for RFKILL_STATE_UNBLOCKED. - -=============================================================================== -4: Kernel API +4. Kernel API To build a driver with rfkill subsystem support, the driver should depend on -(or select) the Kconfig symbol RFKILL; it should _not_ depend on RKFILL_INPUT. +(or select) the Kconfig symbol RFKILL. The hardware the driver talks to may be write-only (where the current state of the hardware is unknown), or read-write (where the hardware can be queried about its current state). -The rfkill class will call the get_state hook of a device every time it needs -to know the *real* current state of the hardware. This can happen often, but -it does not do any polling, so it is not enough on hardware that is subject -to state changes outside of the rfkill subsystem. - -Therefore, calling rfkill_force_state() when a state change happens is -mandatory when the device has a hardware rfkill line, or when something else -like the firmware could cause its state to be changed without going through the -rfkill class. - -Some hardware provides events when its status changes. In these cases, it is -best for the driver to not provide a get_state hook, and instead register the -rfkill class *already* with the correct status, and keep it updated using -rfkill_force_state() when it gets an event from the hardware. - -rfkill_force_state() must be used on the device resume handlers to update the -rfkill status, should there be any chance of the device status changing during -the sleep. - -There is no provision for a statically-allocated rfkill struct. You must -use rfkill_allocate() to allocate one. - -You should: - - rfkill_allocate() - - modify rfkill fields (flags, name) - - modify state to the current hardware state (THIS IS THE ONLY TIME - YOU CAN ACCESS state DIRECTLY) - - rfkill_register() +Calling rfkill_set_hw_state() when a state change happens is required from +rfkill drivers that control devices that can be hard-blocked unless they also +assign the poll_hw_block() callback (then the rfkill core will poll the +device). Don't do this unless you cannot get the event in any other way. -The only way to set a device to the RFKILL_STATE_HARD_BLOCKED state is through -a suitable return of get_state() or through rfkill_force_state(). -When a device is in the RFKILL_STATE_HARD_BLOCKED state, the only way to switch -it to a different state is through a suitable return of get_state() or through -rfkill_force_state(). -If toggle_radio() is called to set a device to state RFKILL_STATE_SOFT_BLOCKED -when that device is already at the RFKILL_STATE_HARD_BLOCKED state, it should -not return an error. Instead, it should try to double-block the transmitter, -so that its state will change from RFKILL_STATE_HARD_BLOCKED to -RFKILL_STATE_SOFT_BLOCKED should the hardware blocking cease. - -Please refer to the source for more documentation. - -=============================================================================== -5: Userspace support - -rfkill devices issue uevents (with an action of "change"), with the following -environment variables set: - -RFKILL_NAME -RFKILL_STATE -RFKILL_TYPE +5. Userspace support -The ABI for these variables is defined by the sysfs attributes. It is best -to take a quick look at the source to make sure of the possible values. - -It is expected that HAL will trap those, and bridge them to DBUS, etc. These -events CAN and SHOULD be used to give feedback to the user about the rfkill -status of the system. - -Input devices may issue events that are related to rfkill. These are the -various KEY_* events and SW_* events supported by rfkill-input.c. - -Userspace may not change the state of an rfkill switch in response to an -input event, it should refrain from changing states entirely. - -Userspace cannot assume it is the only source of control for rfkill switches. -Their state can change due to firmware actions, direct user actions, and the -rfkill-input EPO override for *_RFKILL_ALL. - -When rfkill-input is not active, userspace must initiate a rfkill status -change by writing to the "state" attribute in order for anything to happen. - -Take particular care to implement EV_SW SW_RFKILL_ALL properly. When that -switch is set to OFF, *every* rfkill device *MUST* be immediately put into the -RFKILL_STATE_SOFT_BLOCKED state, no questions asked. - -The following sysfs entries will be created: +The following sysfs entries exist for every rfkill device: name: Name assigned by driver to this key (interface or driver name). type: Name of the key type ("wlan", "bluetooth", etc). state: Current state of the transmitter 0: RFKILL_STATE_SOFT_BLOCKED - transmitter is forced off, but one can override it - by a write to the state attribute; + transmitter is turned off by software 1: RFKILL_STATE_UNBLOCKED - transmiter is NOT forced off, and may operate if - all other conditions for such operation are met - (such as interface is up and configured, etc); + transmiter is (potentially) active 2: RFKILL_STATE_HARD_BLOCKED transmitter is forced off by something outside of - the driver's control. One cannot set a device to - this state through writes to the state attribute; - claim: 1: Userspace handles events, 0: Kernel handles events - -Both the "state" and "claim" entries are also writable. For the "state" entry -this means that when 1 or 0 is written, the device rfkill state (if not yet in -the requested state), will be will be toggled accordingly. - -For the "claim" entry writing 1 to it means that the kernel no longer handles -key events even though RFKILL_INPUT input was enabled. When "claim" has been -set to 0, userspace should make sure that it listens for the input events or -check the sysfs "state" entry regularly to correctly perform the required tasks -when the rkfill key is pressed. - -A note about input devices and EV_SW events: - -In order to know the current state of an input device switch (like -SW_RFKILL_ALL), you will need to use an IOCTL. That information is not -available through sysfs in a generic way at this time, and it is not available -through the rfkill class AT ALL. + the driver's control. + claim: 0: Kernel handles events (currently always reads that value) + +rfkill devices also issue uevents (with an action of "change"), with the +following environment variables set: + +RFKILL_NAME +RFKILL_STATE +RFKILL_TYPE + +The contents of these variables corresponds to the "name", "state" and +"type" sysfs files explained above. diff --git a/MAINTAINERS b/MAINTAINERS index e18baa410b5..2f6a8fcfb1f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4753,9 +4753,9 @@ S: Supported F: fs/reiserfs/ RFKILL -P: Ivo van Doorn -M: IvDoorn@gmail.com -L: netdev@vger.kernel.org +P: Johannes Berg +M: johannes@sipsolutions.net +L: linux-wireless@vger.kernel.org S: Maintained F Documentation/rfkill.txt F: net/rfkill/ diff --git a/arch/arm/mach-pxa/tosa-bt.c b/arch/arm/mach-pxa/tosa-bt.c index bde42aa2937..c31e601eb49 100644 --- a/arch/arm/mach-pxa/tosa-bt.c +++ b/arch/arm/mach-pxa/tosa-bt.c @@ -35,21 +35,25 @@ static void tosa_bt_off(struct tosa_bt_data *data) gpio_set_value(data->gpio_reset, 0); } -static int tosa_bt_toggle_radio(void *data, enum rfkill_state state) +static int tosa_bt_set_block(void *data, bool blocked) { - pr_info("BT_RADIO going: %s\n", - state == RFKILL_STATE_UNBLOCKED ? "on" : "off"); + pr_info("BT_RADIO going: %s\n", blocked ? "off" : "on"); - if (state == RFKILL_STATE_UNBLOCKED) { + if (!blocked) { pr_info("TOSA_BT: going ON\n"); tosa_bt_on(data); } else { pr_info("TOSA_BT: going OFF\n"); tosa_bt_off(data); } + return 0; } +static const struct rfkill_ops tosa_bt_rfkill_ops = { + .set_block = tosa_bt_set_block, +}; + static int tosa_bt_probe(struct platform_device *dev) { int rc; @@ -70,18 +74,14 @@ static int tosa_bt_probe(struct platform_device *dev) if (rc) goto err_pwr_dir; - rfk = rfkill_allocate(&dev->dev, RFKILL_TYPE_BLUETOOTH); + rfk = rfkill_alloc("tosa-bt", &dev->dev, RFKILL_TYPE_BLUETOOTH, + &tosa_bt_rfkill_ops, data); if (!rfk) { rc = -ENOMEM; goto err_rfk_alloc; } - rfk->name = "tosa-bt"; - rfk->toggle_radio = tosa_bt_toggle_radio; - rfk->data = data; -#ifdef CONFIG_RFKILL_LEDS - rfk->led_trigger.name = "tosa-bt"; -#endif + rfkill_set_led_trigger_name(rfk, "tosa-bt"); rc = rfkill_register(rfk); if (rc) @@ -92,9 +92,7 @@ static int tosa_bt_probe(struct platform_device *dev) return 0; err_rfkill: - if (rfk) - rfkill_free(rfk); - rfk = NULL; + rfkill_destroy(rfk); err_rfk_alloc: tosa_bt_off(data); err_pwr_dir: @@ -113,8 +111,10 @@ static int __devexit tosa_bt_remove(struct platform_device *dev) platform_set_drvdata(dev, NULL); - if (rfk) + if (rfk) { rfkill_unregister(rfk); + rfkill_destroy(rfk); + } rfk = NULL; tosa_bt_off(data); diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c index afac5b6d3d7..58ce807fe44 100644 --- a/arch/arm/mach-pxa/tosa.c +++ b/arch/arm/mach-pxa/tosa.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 837135f0390..5ddd8c4f901 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -2481,10 +2481,10 @@ static int add_net_device(struct hso_device *hso_dev) return 0; } -static int hso_radio_toggle(void *data, enum rfkill_state state) +static int hso_rfkill_set_block(void *data, bool blocked) { struct hso_device *hso_dev = data; - int enabled = (state == RFKILL_STATE_UNBLOCKED); + int enabled = !blocked; int rv; mutex_lock(&hso_dev->mutex); @@ -2498,6 +2498,10 @@ static int hso_radio_toggle(void *data, enum rfkill_state state) return rv; } +static const struct rfkill_ops hso_rfkill_ops = { + .set_block = hso_rfkill_set_block, +}; + /* Creates and sets up everything for rfkill */ static void hso_create_rfkill(struct hso_device *hso_dev, struct usb_interface *interface) @@ -2506,29 +2510,25 @@ static void hso_create_rfkill(struct hso_device *hso_dev, struct device *dev = &hso_net->net->dev; char *rfkn; - hso_net->rfkill = rfkill_allocate(&interface_to_usbdev(interface)->dev, - RFKILL_TYPE_WWAN); - if (!hso_net->rfkill) { - dev_err(dev, "%s - Out of memory\n", __func__); - return; - } rfkn = kzalloc(20, GFP_KERNEL); - if (!rfkn) { - rfkill_free(hso_net->rfkill); - hso_net->rfkill = NULL; + if (!rfkn) dev_err(dev, "%s - Out of memory\n", __func__); - return; - } + snprintf(rfkn, 20, "hso-%d", interface->altsetting->desc.bInterfaceNumber); - hso_net->rfkill->name = rfkn; - hso_net->rfkill->state = RFKILL_STATE_UNBLOCKED; - hso_net->rfkill->data = hso_dev; - hso_net->rfkill->toggle_radio = hso_radio_toggle; + + hso_net->rfkill = rfkill_alloc(rfkn, + &interface_to_usbdev(interface)->dev, + RFKILL_TYPE_WWAN, + &hso_rfkill_ops, hso_dev); + if (!hso_net->rfkill) { + dev_err(dev, "%s - Out of memory\n", __func__); + kfree(rfkn); + return; + } if (rfkill_register(hso_net->rfkill) < 0) { + rfkill_destroy(hso_net->rfkill); kfree(rfkn); - hso_net->rfkill->name = NULL; - rfkill_free(hso_net->rfkill); hso_net->rfkill = NULL; dev_err(dev, "%s - Failed to register rfkill\n", __func__); return; @@ -3165,8 +3165,10 @@ static void hso_free_interface(struct usb_interface *interface) hso_stop_net_device(network_table[i]); cancel_work_sync(&network_table[i]->async_put_intf); cancel_work_sync(&network_table[i]->async_get_intf); - if (rfk) + if (rfk) { rfkill_unregister(rfk); + rfkill_destroy(rfk); + } hso_free_net_device(network_table[i]); } } diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 796a3adffea..515880aa211 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -460,12 +460,9 @@ struct ath_led { bool registered; }; -/* Rfkill */ -#define ATH_RFKILL_POLL_INTERVAL 2000 /* msecs */ - struct ath_rfkill { struct rfkill *rfkill; - struct delayed_work rfkill_poll; + struct rfkill_ops ops; char rfkill_name[32]; }; @@ -509,8 +506,6 @@ struct ath_rfkill { #define SC_OP_RXFLUSH BIT(7) #define SC_OP_LED_ASSOCIATED BIT(8) #define SC_OP_RFKILL_REGISTERED BIT(9) -#define SC_OP_RFKILL_SW_BLOCKED BIT(10) -#define SC_OP_RFKILL_HW_BLOCKED BIT(11) #define SC_OP_WAIT_FOR_BEACON BIT(12) #define SC_OP_LED_ON BIT(13) #define SC_OP_SCANNING BIT(14) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 61da08a1648..f7baa406918 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1192,120 +1192,69 @@ static bool ath_is_rfkill_set(struct ath_softc *sc) ah->rfkill_polarity; } -/* h/w rfkill poll function */ -static void ath_rfkill_poll(struct work_struct *work) +/* s/w rfkill handlers */ +static int ath_rfkill_set_block(void *data, bool blocked) { - 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; + struct ath_softc *sc = data; - rfkill_force_state(sc->rf_kill.rfkill, state); - } + if (blocked) + ath_radio_disable(sc); + else + ath_radio_enable(sc); - queue_delayed_work(sc->hw->workqueue, &sc->rf_kill.rfkill_poll, - msecs_to_jiffies(ATH_RFKILL_POLL_INTERVAL)); + return 0; } -/* s/w rfkill handler */ -static int ath_sw_toggle_radio(void *data, enum rfkill_state state) +static void ath_rfkill_poll_state(struct rfkill *rfkill, void *data) { struct ath_softc *sc = data; + bool blocked = !!ath_is_rfkill_set(sc); - 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; - } + if (rfkill_set_hw_state(rfkill, blocked)) + ath_radio_disable(sc); + else + ath_radio_enable(sc); } /* 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); + sc->rf_kill.ops.set_block = ath_rfkill_set_block; + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) + sc->rf_kill.ops.poll = ath_rfkill_poll_state; + + snprintf(sc->rf_kill.rfkill_name, sizeof(sc->rf_kill.rfkill_name), + "ath9k-%s::rfkill", wiphy_name(sc->hw->wiphy)); + + sc->rf_kill.rfkill = rfkill_alloc(sc->rf_kill.rfkill_name, + wiphy_dev(sc->hw->wiphy), + RFKILL_TYPE_WLAN, + &sc->rf_kill.ops, sc); 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) { - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) - cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll); - if (sc->sc_flags & SC_OP_RFKILL_REGISTERED) { rfkill_unregister(sc->rf_kill.rfkill); + rfkill_destroy(sc->rf_kill.rfkill); sc->sc_flags &= ~SC_OP_RFKILL_REGISTERED; - sc->rf_kill.rfkill = NULL; } } static int 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); + rfkill_destroy(sc->rf_kill.rfkill); /* Deinitialize the device */ ath_cleanup(sc); @@ -1678,10 +1627,6 @@ int ath_attach(u16 devid, struct ath_softc *sc) 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) @@ -2214,10 +2159,8 @@ static void ath9k_stop(struct ieee80211_hw *hw) } 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 + rfkill_pause_polling(sc->rf_kill.rfkill); + /* disable HAL and put h/w to sleep */ ath9k_hw_disable(sc->sc_ah); ath9k_hw_configpcipowersave(sc->sc_ah, 1); diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 168411d322a..ccdf20a2e9b 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -227,11 +227,6 @@ static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state) ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); -#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 - pci_save_state(pdev); pci_disable_device(pdev); pci_set_power_state(pdev, PCI_D3hot); @@ -256,16 +251,6 @@ static int ath_pci_resume(struct pci_dev *pdev) AR_GPIO_OUTPUT_MUX_AS_OUTPUT); ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); -#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) - /* - * check the h/w rfkill state on resume - * and start the rfkill poll timer - */ - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) - queue_delayed_work(sc->hw->workqueue, - &sc->rf_kill.rfkill_poll, 0); -#endif - return 0; } diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig index 21572e40b79..07a99e3faf9 100644 --- a/drivers/net/wireless/b43/Kconfig +++ b/drivers/net/wireless/b43/Kconfig @@ -102,7 +102,7 @@ config B43_LEDS # if it's possible. config B43_RFKILL bool - depends on B43 && (RFKILL = y || RFKILL = B43) && RFKILL_INPUT && (INPUT_POLLDEV = y || INPUT_POLLDEV = B43) + depends on B43 && (RFKILL = y || RFKILL = B43) default y # This config option automatically enables b43 HW-RNG support, diff --git a/drivers/net/wireless/b43/leds.c b/drivers/net/wireless/b43/leds.c index 76f4c7bad8b..9a498d3fc65 100644 --- a/drivers/net/wireless/b43/leds.c +++ b/drivers/net/wireless/b43/leds.c @@ -87,7 +87,7 @@ static void b43_led_brightness_set(struct led_classdev *led_dev, } static int b43_register_led(struct b43_wldev *dev, struct b43_led *led, - const char *name, char *default_trigger, + const char *name, const char *default_trigger, u8 led_index, bool activelow) { int err; diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index cb4a8712946..1d3e40095ad 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3470,7 +3470,7 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) if (!!conf->radio_enabled != phy->radio_on) { if (conf->radio_enabled) { - b43_software_rfkill(dev, RFKILL_STATE_UNBLOCKED); + b43_software_rfkill(dev, false); b43info(dev->wl, "Radio turned on by software\n"); if (!dev->radio_hw_enable) { b43info(dev->wl, "The hardware RF-kill button " @@ -3478,7 +3478,7 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) "Press the button to turn it on.\n"); } } else { - b43_software_rfkill(dev, RFKILL_STATE_SOFT_BLOCKED); + b43_software_rfkill(dev, true); b43info(dev->wl, "Radio turned off by software\n"); } } diff --git a/drivers/net/wireless/b43/phy_a.c b/drivers/net/wireless/b43/phy_a.c index c836c077d51..816e028a262 100644 --- a/drivers/net/wireless/b43/phy_a.c +++ b/drivers/net/wireless/b43/phy_a.c @@ -480,11 +480,11 @@ static bool b43_aphy_op_supports_hwpctl(struct b43_wldev *dev) } static void b43_aphy_op_software_rfkill(struct b43_wldev *dev, - enum rfkill_state state) + bool blocked) { struct b43_phy *phy = &dev->phy; - if (state == RFKILL_STATE_UNBLOCKED) { + if (!blocked) { if (phy->radio_on) return; b43_radio_write16(dev, 0x0004, 0x00C0); diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c index e176b6e0d9c..6d241622210 100644 --- a/drivers/net/wireless/b43/phy_common.c +++ b/drivers/net/wireless/b43/phy_common.c @@ -84,7 +84,7 @@ int b43_phy_init(struct b43_wldev *dev) phy->channel = ops->get_default_chan(dev); - ops->software_rfkill(dev, RFKILL_STATE_UNBLOCKED); + ops->software_rfkill(dev, false); err = ops->init(dev); if (err) { b43err(dev->wl, "PHY init failed\n"); @@ -104,7 +104,7 @@ err_phy_exit: if (ops->exit) ops->exit(dev); err_block_rf: - ops->software_rfkill(dev, RFKILL_STATE_SOFT_BLOCKED); + ops->software_rfkill(dev, true); return err; } @@ -113,7 +113,7 @@ void b43_phy_exit(struct b43_wldev *dev) { const struct b43_phy_operations *ops = dev->phy.ops; - ops->software_rfkill(dev, RFKILL_STATE_SOFT_BLOCKED); + ops->software_rfkill(dev, true); if (ops->exit) ops->exit(dev); } @@ -295,18 +295,13 @@ err_restore_cookie: return err; } -void b43_software_rfkill(struct b43_wldev *dev, enum rfkill_state state) +void b43_software_rfkill(struct b43_wldev *dev, bool blocked) { struct b43_phy *phy = &dev->phy; - if (state == RFKILL_STATE_HARD_BLOCKED) { - /* We cannot hardware-block the device */ - state = RFKILL_STATE_SOFT_BLOCKED; - } - b43_mac_suspend(dev); - phy->ops->software_rfkill(dev, state); - phy->radio_on = (state == RFKILL_STATE_UNBLOCKED); + phy->ops->software_rfkill(dev, blocked); + phy->radio_on = !blocked; b43_mac_enable(dev); } diff --git a/drivers/net/wireless/b43/phy_common.h b/drivers/net/wireless/b43/phy_common.h index b2d99101947..f4c2d79cbc8 100644 --- a/drivers/net/wireless/b43/phy_common.h +++ b/drivers/net/wireless/b43/phy_common.h @@ -159,7 +159,7 @@ struct b43_phy_operations { /* Radio */ bool (*supports_hwpctl)(struct b43_wldev *dev); - void (*software_rfkill)(struct b43_wldev *dev, enum rfkill_state state); + void (*software_rfkill)(struct b43_wldev *dev, bool blocked); void (*switch_analog)(struct b43_wldev *dev, bool on); int (*switch_channel)(struct b43_wldev *dev, unsigned int new_channel); unsigned int (*get_default_chan)(struct b43_wldev *dev); @@ -364,7 +364,7 @@ int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel); /** * b43_software_rfkill - Turn the radio ON or OFF in software. */ -void b43_software_rfkill(struct b43_wldev *dev, enum rfkill_state state); +void b43_software_rfkill(struct b43_wldev *dev, bool blocked); /** * b43_phy_txpower_check - Check TX power output. diff --git a/drivers/net/wireless/b43/phy_g.c b/drivers/net/wireless/b43/phy_g.c index e7b98f013b0..5300232449f 100644 --- a/drivers/net/wireless/b43/phy_g.c +++ b/drivers/net/wireless/b43/phy_g.c @@ -2592,7 +2592,7 @@ static bool b43_gphy_op_supports_hwpctl(struct b43_wldev *dev) } static void b43_gphy_op_software_rfkill(struct b43_wldev *dev, - enum rfkill_state state) + bool blocked) { struct b43_phy *phy = &dev->phy; struct b43_phy_g *gphy = phy->g; @@ -2600,7 +2600,7 @@ static void b43_gphy_op_software_rfkill(struct b43_wldev *dev, might_sleep(); - if (state == RFKILL_STATE_UNBLOCKED) { + if (!blocked) { /* Turn radio ON */ if (phy->radio_on) return; diff --git a/drivers/net/wireless/b43/phy_lp.c b/drivers/net/wireless/b43/phy_lp.c index 58e319d6b1e..ea0d3a3a6a6 100644 --- a/drivers/net/wireless/b43/phy_lp.c +++ b/drivers/net/wireless/b43/phy_lp.c @@ -488,7 +488,7 @@ static void b43_lpphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) } static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev, - enum rfkill_state state) + bool blocked) { //TODO } diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 8bcfda5f3f0..be7b5604947 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -579,7 +579,7 @@ static void b43_nphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) } static void b43_nphy_op_software_rfkill(struct b43_wldev *dev, - enum rfkill_state state) + bool blocked) {//TODO } diff --git a/drivers/net/wireless/b43/rfkill.c b/drivers/net/wireless/b43/rfkill.c index 9e1d00bc24d..96047843cd5 100644 --- a/drivers/net/wireless/b43/rfkill.c +++ b/drivers/net/wireless/b43/rfkill.c @@ -45,12 +45,11 @@ static bool b43_is_hw_radio_enabled(struct b43_wldev *dev) } /* The poll callback for the hardware button. */ -static void b43_rfkill_poll(struct input_polled_dev *poll_dev) +static void b43_rfkill_poll(struct rfkill *rfkill, void *data) { - struct b43_wldev *dev = poll_dev->private; + struct b43_wldev *dev = data; struct b43_wl *wl = dev->wl; bool enabled; - bool report_change = 0; mutex_lock(&wl->mutex); if (unlikely(b43_status(dev) < B43_STAT_INITIALIZED)) { @@ -60,68 +59,55 @@ static void b43_rfkill_poll(struct input_polled_dev *poll_dev) enabled = b43_is_hw_radio_enabled(dev); if (unlikely(enabled != dev->radio_hw_enable)) { dev->radio_hw_enable = enabled; - report_change = 1; b43info(wl, "Radio hardware status changed to %s\n", enabled ? "ENABLED" : "DISABLED"); + enabled = !rfkill_set_hw_state(rfkill, !enabled); + if (enabled != dev->phy.radio_on) + b43_software_rfkill(dev, !enabled); } mutex_unlock(&wl->mutex); - - /* send the radio switch event to the system - note both a key press - * and a release are required */ - if (unlikely(report_change)) { - input_report_key(poll_dev->input, KEY_WLAN, 1); - input_report_key(poll_dev->input, KEY_WLAN, 0); - } } /* Called when the RFKILL toggled in software. */ -static int b43_rfkill_soft_toggle(void *data, enum rfkill_state state) +static int b43_rfkill_soft_set(void *data, bool blocked) { struct b43_wldev *dev = data; struct b43_wl *wl = dev->wl; - int err = -EBUSY; + int err = -EINVAL; - if (!wl->rfkill.registered) - return 0; + if (WARN_ON(!wl->rfkill.registered)) + return -EINVAL; mutex_lock(&wl->mutex); + if (b43_status(dev) < B43_STAT_INITIALIZED) goto out_unlock; + + if (!dev->radio_hw_enable) + goto out_unlock; + + if (!blocked != dev->phy.radio_on) + b43_software_rfkill(dev, blocked); err = 0; - switch (state) { - case RFKILL_STATE_UNBLOCKED: - if (!dev->radio_hw_enable) { - /* No luck. We can't toggle the hardware RF-kill - * button from software. */ - err = -EBUSY; - goto out_unlock; - } - if (!dev->phy.radio_on) - b43_software_rfkill(dev, state); - break; - case RFKILL_STATE_SOFT_BLOCKED: - if (dev->phy.radio_on) - b43_software_rfkill(dev, state); - break; - default: - b43warn(wl, "Received unexpected rfkill state %d.\n", state); - break; - } out_unlock: mutex_unlock(&wl->mutex); - return err; } -char *b43_rfkill_led_name(struct b43_wldev *dev) +const char *b43_rfkill_led_name(struct b43_wldev *dev) { struct b43_rfkill *rfk = &(dev->wl->rfkill); if (!rfk->registered) return NULL; - return rfkill_get_led_name(rfk->rfkill); + return rfkill_get_led_trigger_name(rfk->rfkill); } +static const struct rfkill_ops b43_rfkill_ops = { + .set_block = b43_rfkill_soft_set, + .poll = b43_rfkill_poll, +}; + void b43_rfkill_init(struct b43_wldev *dev) { struct b43_wl *wl = dev->wl; @@ -130,65 +116,26 @@ void b43_rfkill_init(struct b43_wldev *dev) rfk->registered = 0; - rfk->rfkill = rfkill_allocate(dev->dev->dev, RFKILL_TYPE_WLAN); - if (!rfk->rfkill) - goto out_error; snprintf(rfk->name, sizeof(rfk->name), "b43-%s", wiphy_name(wl->hw->wiphy)); - rfk->rfkill->name = rfk->name; - rfk->rfkill->state = RFKILL_STATE_UNBLOCKED; - rfk->rfkill->data = dev; - rfk->rfkill->toggle_radio = b43_rfkill_soft_toggle; - - rfk->poll_dev = input_allocate_polled_device(); - if (!rfk->poll_dev) { - rfkill_free(rfk->rfkill); - goto err_freed_rfk; - } - - rfk->poll_dev->private = dev; - rfk->poll_dev->poll = b43_rfkill_poll; - rfk->poll_dev->poll_interval = 1000; /* msecs */ - rfk->poll_dev->input->name = rfk->name; - rfk->poll_dev->input->id.bustype = BUS_HOST; - rfk->poll_dev->input->id.vendor = dev->dev->bus->boardinfo.vendor; - rfk->poll_dev->input->evbit[0] = BIT(EV_KEY); - set_bit(KEY_WLAN, rfk->poll_dev->input->keybit); + rfk->rfkill = rfkill_alloc(rfk->name, + dev->dev->dev, + RFKILL_TYPE_WLAN, + &b43_rfkill_ops, dev); + if (!rfk->rfkill) + goto out_error; err = rfkill_register(rfk->rfkill); if (err) - goto err_free_polldev; - -#ifdef CONFIG_RFKILL_INPUT_MODULE - /* B43 RF-kill isn't useful without the rfkill-input subsystem. - * Try to load the module. */ - err = request_module("rfkill-input"); - if (err) - b43warn(wl, "Failed to load the rfkill-input module. " - "The built-in radio LED will not work.\n"); -#endif /* CONFIG_RFKILL_INPUT */ - -#if !defined(CONFIG_RFKILL_INPUT) && !defined(CONFIG_RFKILL_INPUT_MODULE) - b43warn(wl, "The rfkill-input subsystem is not available. " - "The built-in radio LED will not work.\n"); -#endif - - err = input_register_polled_device(rfk->poll_dev); - if (err) - goto err_unreg_rfk; + goto err_free; rfk->registered = 1; return; -err_unreg_rfk: - rfkill_unregister(rfk->rfkill); -err_free_polldev: - input_free_polled_device(rfk->poll_dev); - rfk->poll_dev = NULL; -err_freed_rfk: - rfk->rfkill = NULL; -out_error: + err_free: + rfkill_destroy(rfk->rfkill); + out_error: rfk->registered = 0; b43warn(wl, "RF-kill button init failed\n"); } @@ -201,9 +148,7 @@ void b43_rfkill_exit(struct b43_wldev *dev) return; rfk->registered = 0; - input_unregister_polled_device(rfk->poll_dev); rfkill_unregister(rfk->rfkill); - input_free_polled_device(rfk->poll_dev); - rfk->poll_dev = NULL; + rfkill_destroy(rfk->rfkill); rfk->rfkill = NULL; } diff --git a/drivers/net/wireless/b43/rfkill.h b/drivers/net/wireless/b43/rfkill.h index adacf936d81..da497e01bbb 100644 --- a/drivers/net/wireless/b43/rfkill.h +++ b/drivers/net/wireless/b43/rfkill.h @@ -7,14 +7,11 @@ struct b43_wldev; #ifdef CONFIG_B43_RFKILL #include -#include struct b43_rfkill { /* The RFKILL subsystem data structure */ struct rfkill *rfkill; - /* The poll device for the RFKILL input button */ - struct input_polled_dev *poll_dev; /* Did initialization succeed? Used for freeing. */ bool registered; /* The unique name of this rfkill switch */ @@ -26,7 +23,7 @@ struct b43_rfkill { void b43_rfkill_init(struct b43_wldev *dev); void b43_rfkill_exit(struct b43_wldev *dev); -char * b43_rfkill_led_name(struct b43_wldev *dev); +const char *b43_rfkill_led_name(struct b43_wldev *dev); #else /* CONFIG_B43_RFKILL */ diff --git a/drivers/net/wireless/b43legacy/Kconfig b/drivers/net/wireless/b43legacy/Kconfig index d4f628a74bb..6893f439df7 100644 --- a/drivers/net/wireless/b43legacy/Kconfig +++ b/drivers/net/wireless/b43legacy/Kconfig @@ -47,7 +47,7 @@ config B43LEGACY_LEDS # if it's possible. config B43LEGACY_RFKILL bool - depends on B43LEGACY && (RFKILL = y || RFKILL = B43LEGACY) && RFKILL_INPUT && (INPUT_POLLDEV = y || INPUT_POLLDEV = B43LEGACY) + depends on B43LEGACY && (RFKILL = y || RFKILL = B43LEGACY) default y # This config option automatically enables b43 HW-RNG support, diff --git a/drivers/net/wireless/b43legacy/leds.c b/drivers/net/wireless/b43legacy/leds.c index 3ea55b18c70..538d3117594 100644 --- a/drivers/net/wireless/b43legacy/leds.c +++ b/drivers/net/wireless/b43legacy/leds.c @@ -86,7 +86,8 @@ static void b43legacy_led_brightness_set(struct led_classdev *led_dev, static int b43legacy_register_led(struct b43legacy_wldev *dev, struct b43legacy_led *led, - const char *name, char *default_trigger, + const char *name, + const char *default_trigger, u8 led_index, bool activelow) { int err; diff --git a/drivers/net/wireless/b43legacy/rfkill.c b/drivers/net/wireless/b43legacy/rfkill.c index 4b0c7d27a51..c6230a64505 100644 --- a/drivers/net/wireless/b43legacy/rfkill.c +++ b/drivers/net/wireless/b43legacy/rfkill.c @@ -45,12 +45,11 @@ static bool b43legacy_is_hw_radio_enabled(struct b43legacy_wldev *dev) } /* The poll callback for the hardware button. */ -static void b43legacy_rfkill_poll(struct input_polled_dev *poll_dev) +static void b43legacy_rfkill_poll(struct rfkill *rfkill, void *data) { - struct b43legacy_wldev *dev = poll_dev->private; + struct b43legacy_wldev *dev = data; struct b43legacy_wl *wl = dev->wl; bool enabled; - bool report_change = 0; mutex_lock(&wl->mutex); if (unlikely(b43legacy_status(dev) < B43legacy_STAT_INITIALIZED)) { @@ -60,71 +59,64 @@ static void b43legacy_rfkill_poll(struct input_polled_dev *poll_dev) enabled = b43legacy_is_hw_radio_enabled(dev); if (unlikely(enabled != dev->radio_hw_enable)) { dev->radio_hw_enable = enabled; - report_change = 1; b43legacyinfo(wl, "Radio hardware status changed to %s\n", enabled ? "ENABLED" : "DISABLED"); + enabled = !rfkill_set_hw_state(rfkill, !enabled); + if (enabled != dev->phy.radio_on) { + if (enabled) + b43legacy_radio_turn_on(dev); + else + b43legacy_radio_turn_off(dev, 0); + } } mutex_unlock(&wl->mutex); - - /* send the radio switch event to the system - note both a key press - * and a release are required */ - if (unlikely(report_change)) { - input_report_key(poll_dev->input, KEY_WLAN, 1); - input_report_key(poll_dev->input, KEY_WLAN, 0); - } } /* Called when the RFKILL toggled in software. * This is called without locking. */ -static int b43legacy_rfkill_soft_toggle(void *data, enum rfkill_state state) +static int b43legacy_rfkill_soft_set(void *data, bool blocked) { struct b43legacy_wldev *dev = data; struct b43legacy_wl *wl = dev->wl; - int err = -EBUSY; + int ret = -EINVAL; if (!wl->rfkill.registered) - return 0; + return -EINVAL; mutex_lock(&wl->mutex); if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) goto out_unlock; - err = 0; - switch (state) { - case RFKILL_STATE_UNBLOCKED: - if (!dev->radio_hw_enable) { - /* No luck. We can't toggle the hardware RF-kill - * button from software. */ - err = -EBUSY; - goto out_unlock; - } - if (!dev->phy.radio_on) + + if (!dev->radio_hw_enable) + goto out_unlock; + + if (!blocked != dev->phy.radio_on) { + if (!blocked) b43legacy_radio_turn_on(dev); - break; - case RFKILL_STATE_SOFT_BLOCKED: - if (dev->phy.radio_on) + else b43legacy_radio_turn_off(dev, 0); - break; - default: - b43legacywarn(wl, "Received unexpected rfkill state %d.\n", - state); - break; } + ret = 0; out_unlock: mutex_unlock(&wl->mutex); - - return err; + return ret; } -char *b43legacy_rfkill_led_name(struct b43legacy_wldev *dev) +const char *b43legacy_rfkill_led_name(struct b43legacy_wldev *dev) { struct b43legacy_rfkill *rfk = &(dev->wl->rfkill); if (!rfk->registered) return NULL; - return rfkill_get_led_name(rfk->rfkill); + return rfkill_get_led_trigger_name(rfk->rfkill); } +static const struct rfkill_ops b43legacy_rfkill_ops = { + .set_block = b43legacy_rfkill_soft_set, + .poll = b43legacy_rfkill_poll, +}; + void b43legacy_rfkill_init(struct b43legacy_wldev *dev) { struct b43legacy_wl *wl = dev->wl; @@ -133,60 +125,25 @@ void b43legacy_rfkill_init(struct b43legacy_wldev *dev) rfk->registered = 0; - rfk->rfkill = rfkill_allocate(dev->dev->dev, RFKILL_TYPE_WLAN); - if (!rfk->rfkill) - goto out_error; snprintf(rfk->name, sizeof(rfk->name), "b43legacy-%s", wiphy_name(wl->hw->wiphy)); - rfk->rfkill->name = rfk->name; - rfk->rfkill->state = RFKILL_STATE_UNBLOCKED; - rfk->rfkill->data = dev; - rfk->rfkill->toggle_radio = b43legacy_rfkill_soft_toggle; - - rfk->poll_dev = input_allocate_polled_device(); - if (!rfk->poll_dev) { - rfkill_free(rfk->rfkill); - goto err_freed_rfk; - } - - rfk->poll_dev->private = dev; - rfk->poll_dev->poll = b43legacy_rfkill_poll; - rfk->poll_dev->poll_interval = 1000; /* msecs */ - - rfk->poll_dev->input->name = rfk->name; - rfk->poll_dev->input->id.bustype = BUS_HOST; - rfk->poll_dev->input->id.vendor = dev->dev->bus->boardinfo.vendor; - rfk->poll_dev->input->evbit[0] = BIT(EV_KEY); - set_bit(KEY_WLAN, rfk->poll_dev->input->keybit); + rfk->rfkill = rfkill_alloc(rfk->name, + dev->dev->dev, + RFKILL_TYPE_WLAN, + &b43legacy_rfkill_ops, dev); + if (!rfk->rfkill) + goto out_error; err = rfkill_register(rfk->rfkill); if (err) - goto err_free_polldev; - -#ifdef CONFIG_RFKILL_INPUT_MODULE - /* B43legacy RF-kill isn't useful without the rfkill-input subsystem. - * Try to load the module. */ - err = request_module("rfkill-input"); - if (err) - b43legacywarn(wl, "Failed to load the rfkill-input module." - "The built-in radio LED will not work.\n"); -#endif /* CONFIG_RFKILL_INPUT */ - - err = input_register_polled_device(rfk->poll_dev); - if (err) - goto err_unreg_rfk; + goto err_free; rfk->registered = 1; return; -err_unreg_rfk: - rfkill_unregister(rfk->rfkill); -err_free_polldev: - input_free_polled_device(rfk->poll_dev); - rfk->poll_dev = NULL; -err_freed_rfk: - rfk->rfkill = NULL; -out_error: + err_free: + rfkill_destroy(rfk->rfkill); + out_error: rfk->registered = 0; b43legacywarn(wl, "RF-kill button init failed\n"); } @@ -199,10 +156,8 @@ void b43legacy_rfkill_exit(struct b43legacy_wldev *dev) return; rfk->registered = 0; - input_unregister_polled_device(rfk->poll_dev); rfkill_unregister(rfk->rfkill); - input_free_polled_device(rfk->poll_dev); - rfk->poll_dev = NULL; + rfkill_destroy(rfk->rfkill); rfk->rfkill = NULL; } diff --git a/drivers/net/wireless/b43legacy/rfkill.h b/drivers/net/wireless/b43legacy/rfkill.h index 11150a8032f..adffc503a6a 100644 --- a/drivers/net/wireless/b43legacy/rfkill.h +++ b/drivers/net/wireless/b43legacy/rfkill.h @@ -6,16 +6,12 @@ struct b43legacy_wldev; #ifdef CONFIG_B43LEGACY_RFKILL #include -#include -#include struct b43legacy_rfkill { /* The RFKILL subsystem data structure */ struct rfkill *rfkill; - /* The poll device for the RFKILL input button */ - struct input_polled_dev *poll_dev; /* Did initialization succeed? Used for freeing. */ bool registered; /* The unique name of this rfkill switch */ @@ -27,7 +23,7 @@ struct b43legacy_rfkill { void b43legacy_rfkill_init(struct b43legacy_wldev *dev); void b43legacy_rfkill_exit(struct b43legacy_wldev *dev); -char *b43legacy_rfkill_led_name(struct b43legacy_wldev *dev); +const char *b43legacy_rfkill_led_name(struct b43legacy_wldev *dev); #else /* CONFIG_B43LEGACY_RFKILL */ diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index 8304f6406a1..6fe259fcfb8 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -5,15 +5,14 @@ config IWLWIFI select FW_LOADER select MAC80211_LEDS if IWLWIFI_LEDS select LEDS_CLASS if IWLWIFI_LEDS - select RFKILL if IWLWIFI_RFKILL config IWLWIFI_LEDS bool "Enable LED support in iwlagn and iwl3945 drivers" depends on IWLWIFI config IWLWIFI_RFKILL - bool "Enable RF kill support in iwlagn and iwl3945 drivers" - depends on IWLWIFI + def_bool y + depends on IWLWIFI && RFKILL config IWLWIFI_SPECTRUM_MEASUREMENT bool "Enable Spectrum Measurement in iwlagn driver" diff --git a/drivers/net/wireless/iwlwifi/iwl-rfkill.c b/drivers/net/wireless/iwlwifi/iwl-rfkill.c index 65605ad44e4..13149936fd2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rfkill.c +++ b/drivers/net/wireless/iwlwifi/iwl-rfkill.c @@ -36,42 +36,37 @@ #include "iwl-core.h" /* software rf-kill from user */ -static int iwl_rfkill_soft_rf_kill(void *data, enum rfkill_state state) +static int iwl_rfkill_soft_rf_kill(void *data, bool blocked) { struct iwl_priv *priv = data; - int err = 0; if (!priv->rfkill) - return 0; + return -EINVAL; if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return 0; - IWL_DEBUG_RF_KILL(priv, "we received soft RFKILL set to state %d\n", state); + IWL_DEBUG_RF_KILL(priv, "received soft RFKILL: block=%d\n", blocked); + mutex_lock(&priv->mutex); - switch (state) { - case RFKILL_STATE_UNBLOCKED: - if (iwl_is_rfkill_hw(priv)) { - err = -EBUSY; - goto out_unlock; - } + if (iwl_is_rfkill_hw(priv)) + goto out_unlock; + + if (!blocked) iwl_radio_kill_sw_enable_radio(priv); - break; - case RFKILL_STATE_SOFT_BLOCKED: + else iwl_radio_kill_sw_disable_radio(priv); - break; - default: - IWL_WARN(priv, "we received unexpected RFKILL state %d\n", - state); - break; - } + out_unlock: mutex_unlock(&priv->mutex); - - return err; + return 0; } +static const struct rfkill_ops iwl_rfkill_ops = { + .set_block = iwl_rfkill_soft_rf_kill, +}; + int iwl_rfkill_init(struct iwl_priv *priv) { struct device *device = wiphy_dev(priv->hw->wiphy); @@ -80,21 +75,16 @@ int iwl_rfkill_init(struct iwl_priv *priv) BUG_ON(device == NULL); IWL_DEBUG_RF_KILL(priv, "Initializing RFKILL.\n"); - priv->rfkill = rfkill_allocate(device, RFKILL_TYPE_WLAN); + priv->rfkill = rfkill_alloc(priv->cfg->name, + device, + RFKILL_TYPE_WLAN, + &iwl_rfkill_ops, priv); if (!priv->rfkill) { IWL_ERR(priv, "Unable to allocate RFKILL device.\n"); ret = -ENOMEM; goto error; } - priv->rfkill->name = priv->cfg->name; - priv->rfkill->data = priv; - priv->rfkill->state = RFKILL_STATE_UNBLOCKED; - priv->rfkill->toggle_radio = iwl_rfkill_soft_rf_kill; - - priv->rfkill->dev.class->suspend = NULL; - priv->rfkill->dev.class->resume = NULL; - ret = rfkill_register(priv->rfkill); if (ret) { IWL_ERR(priv, "Unable to register RFKILL: %d\n", ret); @@ -102,11 +92,10 @@ int iwl_rfkill_init(struct iwl_priv *priv) } IWL_DEBUG_RF_KILL(priv, "RFKILL initialization complete.\n"); - return ret; + return 0; free_rfkill: - if (priv->rfkill != NULL) - rfkill_free(priv->rfkill); + rfkill_destroy(priv->rfkill); priv->rfkill = NULL; error: @@ -118,8 +107,10 @@ EXPORT_SYMBOL(iwl_rfkill_init); void iwl_rfkill_unregister(struct iwl_priv *priv) { - if (priv->rfkill) + if (priv->rfkill) { rfkill_unregister(priv->rfkill); + rfkill_destroy(priv->rfkill); + } priv->rfkill = NULL; } @@ -131,14 +122,10 @@ void iwl_rfkill_set_hw_state(struct iwl_priv *priv) if (!priv->rfkill) return; - if (iwl_is_rfkill_hw(priv)) { - rfkill_force_state(priv->rfkill, RFKILL_STATE_HARD_BLOCKED); - return; - } - - if (!iwl_is_rfkill_sw(priv)) - rfkill_force_state(priv->rfkill, RFKILL_STATE_UNBLOCKED); + if (rfkill_set_hw_state(priv->rfkill, + !!iwl_is_rfkill_hw(priv))) + iwl_radio_kill_sw_disable_radio(priv); else - rfkill_force_state(priv->rfkill, RFKILL_STATE_SOFT_BLOCKED); + iwl_radio_kill_sw_enable_radio(priv); } EXPORT_SYMBOL(iwl_rfkill_set_hw_state); diff --git a/drivers/net/wireless/iwmc3200wifi/rfkill.c b/drivers/net/wireless/iwmc3200wifi/rfkill.c index 4ca8b495f82..8ee2c3c09a0 100644 --- a/drivers/net/wireless/iwmc3200wifi/rfkill.c +++ b/drivers/net/wireless/iwmc3200wifi/rfkill.c @@ -25,47 +25,42 @@ #include "iwm.h" -static int iwm_rfkill_soft_toggle(void *data, enum rfkill_state state) +static int iwm_rfkill_set_block(void *data, bool blocked) { struct iwm_priv *iwm = data; - switch (state) { - case RFKILL_STATE_UNBLOCKED: + if (!blocked) { 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: + return iwm_up(iwm); + } else { if (!test_and_set_bit(IWM_RADIO_RFKILL_SW, &iwm->radio)) - iwm_down(iwm); - - break; - default: - break; + return iwm_down(iwm); } return 0; } +static const struct rfkill_ops iwm_rfkill_ops = { + .set_block = iwm_rfkill_set_block, +}; + int iwm_rfkill_init(struct iwm_priv *iwm) { int ret; - iwm->rfkill = rfkill_allocate(iwm_to_dev(iwm), RFKILL_TYPE_WLAN); + iwm->rfkill = rfkill_alloc(KBUILD_MODNAME, + iwm_to_dev(iwm), + RFKILL_TYPE_WLAN, + &iwm_rfkill_ops, iwm); 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"); @@ -74,15 +69,15 @@ int iwm_rfkill_init(struct iwm_priv *iwm) return 0; fail: - rfkill_free(iwm->rfkill); + rfkill_destroy(iwm->rfkill); return ret; } void iwm_rfkill_exit(struct iwm_priv *iwm) { - if (iwm->rfkill) + if (iwm->rfkill) { rfkill_unregister(iwm->rfkill); - - rfkill_free(iwm->rfkill); + rfkill_destroy(iwm->rfkill); + } iwm->rfkill = NULL; } diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 284ebaca6e4..c682ac53641 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -21,7 +21,7 @@ config ACER_WMI depends on NEW_LEDS depends on BACKLIGHT_CLASS_DEVICE depends on SERIO_I8042 - depends on RFKILL + depends on RFKILL || RFKILL = n select ACPI_WMI ---help--- This is a driver for newer Acer (and Wistron) laptops. It adds @@ -60,7 +60,7 @@ config DELL_LAPTOP depends on DCDBAS depends on EXPERIMENTAL depends on BACKLIGHT_CLASS_DEVICE - depends on RFKILL + depends on RFKILL || RFKILL = n depends on POWER_SUPPLY default n ---help--- @@ -117,7 +117,7 @@ config HP_WMI tristate "HP WMI extras" depends on ACPI_WMI depends on INPUT - depends on RFKILL + depends on RFKILL || RFKILL = n help Say Y here if you want to support WMI-based hotkeys on HP laptops and to read data from WMI such as docking or ambient light sensor state. @@ -196,14 +196,13 @@ config THINKPAD_ACPI tristate "ThinkPad ACPI Laptop Extras" depends on ACPI depends on INPUT + depends on RFKILL || RFKILL = n select BACKLIGHT_LCD_SUPPORT select BACKLIGHT_CLASS_DEVICE select HWMON select NVRAM select NEW_LEDS select LEDS_CLASS - select NET - select RFKILL ---help--- This is a driver for the IBM and Lenovo ThinkPad laptops. It adds support for Fn-Fx key combinations, Bluetooth control, video @@ -338,9 +337,9 @@ config EEEPC_LAPTOP depends on ACPI depends on INPUT depends on EXPERIMENTAL + depends on RFKILL || RFKILL = n select BACKLIGHT_CLASS_DEVICE select HWMON - select RFKILL ---help--- This driver supports the Fn-Fx keys on Eee PC laptops. It also adds the ability to switch camera/wlan on/off. @@ -405,9 +404,8 @@ config ACPI_TOSHIBA tristate "Toshiba Laptop Extras" depends on ACPI depends on INPUT + depends on RFKILL || RFKILL = n select INPUT_POLLDEV - select NET - select RFKILL select BACKLIGHT_CLASS_DEVICE ---help--- This driver adds support for access to certain system settings diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 62d02b3c998..b618fa51db2 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -958,58 +958,50 @@ static void acer_rfkill_update(struct work_struct *ignored) status = get_u32(&state, ACER_CAP_WIRELESS); if (ACPI_SUCCESS(status)) - rfkill_force_state(wireless_rfkill, state ? - RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED); + rfkill_set_sw_state(wireless_rfkill, !!state); if (has_cap(ACER_CAP_BLUETOOTH)) { status = get_u32(&state, ACER_CAP_BLUETOOTH); if (ACPI_SUCCESS(status)) - rfkill_force_state(bluetooth_rfkill, state ? - RFKILL_STATE_UNBLOCKED : - RFKILL_STATE_SOFT_BLOCKED); + rfkill_set_sw_state(bluetooth_rfkill, !!state); } schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); } -static int acer_rfkill_set(void *data, enum rfkill_state state) +static int acer_rfkill_set(void *data, bool blocked) { acpi_status status; - u32 *cap = data; - status = set_u32((u32) (state == RFKILL_STATE_UNBLOCKED), *cap); + u32 cap = (unsigned long)data; + status = set_u32(!!blocked, cap); if (ACPI_FAILURE(status)) return -ENODEV; return 0; } -static struct rfkill * acer_rfkill_register(struct device *dev, -enum rfkill_type type, char *name, u32 cap) +static const struct rfkill_ops acer_rfkill_ops = { + .set_block = acer_rfkill_set, +}; + +static struct rfkill *acer_rfkill_register(struct device *dev, + enum rfkill_type type, + char *name, u32 cap) { int err; u32 state; - u32 *data; struct rfkill *rfkill_dev; - rfkill_dev = rfkill_allocate(dev, type); + rfkill_dev = rfkill_alloc(name, dev, type, + &acer_rfkill_ops, + (void *)(unsigned long)cap); if (!rfkill_dev) return ERR_PTR(-ENOMEM); - rfkill_dev->name = name; get_u32(&state, cap); - rfkill_dev->state = state ? RFKILL_STATE_UNBLOCKED : - RFKILL_STATE_SOFT_BLOCKED; - data = kzalloc(sizeof(u32), GFP_KERNEL); - if (!data) { - rfkill_free(rfkill_dev); - return ERR_PTR(-ENOMEM); - } - *data = cap; - rfkill_dev->data = data; - rfkill_dev->toggle_radio = acer_rfkill_set; + rfkill_set_sw_state(rfkill_dev, !state); err = rfkill_register(rfkill_dev); if (err) { - kfree(rfkill_dev->data); - rfkill_free(rfkill_dev); + rfkill_destroy(rfkill_dev); return ERR_PTR(err); } return rfkill_dev; @@ -1027,8 +1019,8 @@ static int acer_rfkill_init(struct device *dev) RFKILL_TYPE_BLUETOOTH, "acer-bluetooth", ACER_CAP_BLUETOOTH); if (IS_ERR(bluetooth_rfkill)) { - kfree(wireless_rfkill->data); rfkill_unregister(wireless_rfkill); + rfkill_destroy(wireless_rfkill); return PTR_ERR(bluetooth_rfkill); } } @@ -1041,11 +1033,13 @@ static int acer_rfkill_init(struct device *dev) static void acer_rfkill_exit(void) { cancel_delayed_work_sync(&acer_rfkill_work); - kfree(wireless_rfkill->data); + rfkill_unregister(wireless_rfkill); + rfkill_destroy(wireless_rfkill); + if (has_cap(ACER_CAP_BLUETOOTH)) { - kfree(bluetooth_rfkill->data); rfkill_unregister(bluetooth_rfkill); + rfkill_destroy(bluetooth_rfkill); } return; } diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index af9f4302117..2faf0e14f05 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -174,10 +174,11 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, result[3]: NVRAM format version number */ -static int dell_rfkill_set(int radio, enum rfkill_state state) +static int dell_rfkill_set(void *data, bool blocked) { struct calling_interface_buffer buffer; - int disable = (state == RFKILL_STATE_UNBLOCKED) ? 0 : 1; + int disable = blocked ? 0 : 1; + unsigned long radio = (unsigned long)data; memset(&buffer, 0, sizeof(struct calling_interface_buffer)); buffer.input[0] = (1 | (radio<<8) | (disable << 16)); @@ -186,56 +187,24 @@ static int dell_rfkill_set(int radio, enum rfkill_state state) return 0; } -static int dell_wifi_set(void *data, enum rfkill_state state) -{ - return dell_rfkill_set(1, state); -} - -static int dell_bluetooth_set(void *data, enum rfkill_state state) -{ - return dell_rfkill_set(2, state); -} - -static int dell_wwan_set(void *data, enum rfkill_state state) -{ - return dell_rfkill_set(3, state); -} - -static int dell_rfkill_get(int bit, enum rfkill_state *state) +static void dell_rfkill_query(struct rfkill *rfkill, void *data) { struct calling_interface_buffer buffer; int status; - int new_state = RFKILL_STATE_HARD_BLOCKED; + int bit = (unsigned long)data + 16; memset(&buffer, 0, sizeof(struct calling_interface_buffer)); dell_send_request(&buffer, 17, 11); status = buffer.output[1]; - if (status & (1<<16)) - new_state = RFKILL_STATE_SOFT_BLOCKED; - - if (status & (1<name = "dell-wifi"; - wifi_rfkill->toggle_radio = dell_wifi_set; - wifi_rfkill->get_state = dell_wifi_get; + } ret = rfkill_register(wifi_rfkill); if (ret) goto err_wifi; } if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) { - bluetooth_rfkill = rfkill_allocate(NULL, RFKILL_TYPE_BLUETOOTH); - if (!bluetooth_rfkill) + bluetooth_rfkill = rfkill_alloc("dell-bluetooth", NULL, + RFKILL_TYPE_BLUETOOTH, + &dell_rfkill_ops, (void *) 2); + if (!bluetooth_rfkill) { + ret = -ENOMEM; goto err_bluetooth; - bluetooth_rfkill->name = "dell-bluetooth"; - bluetooth_rfkill->toggle_radio = dell_bluetooth_set; - bluetooth_rfkill->get_state = dell_bluetooth_get; + } ret = rfkill_register(bluetooth_rfkill); if (ret) goto err_bluetooth; } if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) { - wwan_rfkill = rfkill_allocate(NULL, RFKILL_TYPE_WWAN); - if (!wwan_rfkill) + wwan_rfkill = rfkill_alloc("dell-wwan", NULL, RFKILL_TYPE_WWAN, + &dell_rfkill_ops, (void *) 3); + if (!wwan_rfkill) { + ret = -ENOMEM; goto err_wwan; - wwan_rfkill->name = "dell-wwan"; - wwan_rfkill->toggle_radio = dell_wwan_set; - wwan_rfkill->get_state = dell_wwan_get; + } ret = rfkill_register(wwan_rfkill); if (ret) goto err_wwan; @@ -285,22 +255,15 @@ static int dell_setup_rfkill(void) return 0; err_wwan: - if (wwan_rfkill) - rfkill_free(wwan_rfkill); - if (bluetooth_rfkill) { + rfkill_destroy(wwan_rfkill); + if (bluetooth_rfkill) rfkill_unregister(bluetooth_rfkill); - bluetooth_rfkill = NULL; - } err_bluetooth: - if (bluetooth_rfkill) - rfkill_free(bluetooth_rfkill); - if (wifi_rfkill) { + rfkill_destroy(bluetooth_rfkill); + if (wifi_rfkill) rfkill_unregister(wifi_rfkill); - wifi_rfkill = NULL; - } err_wifi: - if (wifi_rfkill) - rfkill_free(wifi_rfkill); + rfkill_destroy(wifi_rfkill); return ret; } diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 353a898c369..1208d0cedd1 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -299,39 +299,22 @@ static int update_bl_status(struct backlight_device *bd) * Rfkill helpers */ -static int eeepc_wlan_rfkill_set(void *data, enum rfkill_state state) -{ - if (state == RFKILL_STATE_SOFT_BLOCKED) - return set_acpi(CM_ASL_WLAN, 0); - else - return set_acpi(CM_ASL_WLAN, 1); -} - -static int eeepc_wlan_rfkill_state(void *data, enum rfkill_state *state) +static bool eeepc_wlan_rfkill_blocked(void) { if (get_acpi(CM_ASL_WLAN) == 1) - *state = RFKILL_STATE_UNBLOCKED; - else - *state = RFKILL_STATE_SOFT_BLOCKED; - return 0; + return false; + return true; } -static int eeepc_bluetooth_rfkill_set(void *data, enum rfkill_state state) +static int eeepc_rfkill_set(void *data, bool blocked) { - if (state == RFKILL_STATE_SOFT_BLOCKED) - return set_acpi(CM_ASL_BLUETOOTH, 0); - else - return set_acpi(CM_ASL_BLUETOOTH, 1); + unsigned long asl = (unsigned long)data; + return set_acpi(asl, !blocked); } -static int eeepc_bluetooth_rfkill_state(void *data, enum rfkill_state *state) -{ - if (get_acpi(CM_ASL_BLUETOOTH) == 1) - *state = RFKILL_STATE_UNBLOCKED; - else - *state = RFKILL_STATE_SOFT_BLOCKED; - return 0; -} +static const struct rfkill_ops eeepc_rfkill_ops = { + .set_block = eeepc_rfkill_set, +}; /* * Sys helpers @@ -531,9 +514,9 @@ static int notify_brn(void) static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) { - enum rfkill_state state; struct pci_dev *dev; struct pci_bus *bus = pci_find_bus(0, 1); + bool blocked; if (event != ACPI_NOTIFY_BUS_CHECK) return; @@ -543,9 +526,8 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) return; } - eeepc_wlan_rfkill_state(ehotk->eeepc_wlan_rfkill, &state); - - if (state == RFKILL_STATE_UNBLOCKED) { + blocked = eeepc_wlan_rfkill_blocked(); + if (!blocked) { dev = pci_get_slot(bus, 0); if (dev) { /* Device already present */ @@ -566,7 +548,7 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) } } - rfkill_force_state(ehotk->eeepc_wlan_rfkill, state); + rfkill_set_sw_state(ehotk->eeepc_wlan_rfkill, blocked); } static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data) @@ -684,26 +666,17 @@ static int eeepc_hotk_add(struct acpi_device *device) eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7"); if (get_acpi(CM_ASL_WLAN) != -1) { - ehotk->eeepc_wlan_rfkill = rfkill_allocate(&device->dev, - RFKILL_TYPE_WLAN); + ehotk->eeepc_wlan_rfkill = rfkill_alloc("eeepc-wlan", + &device->dev, + RFKILL_TYPE_WLAN, + &eeepc_rfkill_ops, + (void *)CM_ASL_WLAN); if (!ehotk->eeepc_wlan_rfkill) goto wlan_fail; - ehotk->eeepc_wlan_rfkill->name = "eeepc-wlan"; - ehotk->eeepc_wlan_rfkill->toggle_radio = eeepc_wlan_rfkill_set; - ehotk->eeepc_wlan_rfkill->get_state = eeepc_wlan_rfkill_state; - if (get_acpi(CM_ASL_WLAN) == 1) { - ehotk->eeepc_wlan_rfkill->state = - RFKILL_STATE_UNBLOCKED; - rfkill_set_default(RFKILL_TYPE_WLAN, - RFKILL_STATE_UNBLOCKED); - } else { - ehotk->eeepc_wlan_rfkill->state = - RFKILL_STATE_SOFT_BLOCKED; - rfkill_set_default(RFKILL_TYPE_WLAN, - RFKILL_STATE_SOFT_BLOCKED); - } + rfkill_set_global_sw_state(RFKILL_TYPE_WLAN, + get_acpi(CM_ASL_WLAN) != 1); result = rfkill_register(ehotk->eeepc_wlan_rfkill); if (result) goto wlan_fail; @@ -711,28 +684,17 @@ static int eeepc_hotk_add(struct acpi_device *device) if (get_acpi(CM_ASL_BLUETOOTH) != -1) { ehotk->eeepc_bluetooth_rfkill = - rfkill_allocate(&device->dev, RFKILL_TYPE_BLUETOOTH); + rfkill_alloc("eeepc-bluetooth", + &device->dev, + RFKILL_TYPE_BLUETOOTH, + &eeepc_rfkill_ops, + (void *)CM_ASL_BLUETOOTH); if (!ehotk->eeepc_bluetooth_rfkill) goto bluetooth_fail; - ehotk->eeepc_bluetooth_rfkill->name = "eeepc-bluetooth"; - ehotk->eeepc_bluetooth_rfkill->toggle_radio = - eeepc_bluetooth_rfkill_set; - ehotk->eeepc_bluetooth_rfkill->get_state = - eeepc_bluetooth_rfkill_state; - if (get_acpi(CM_ASL_BLUETOOTH) == 1) { - ehotk->eeepc_bluetooth_rfkill->state = - RFKILL_STATE_UNBLOCKED; - rfkill_set_default(RFKILL_TYPE_BLUETOOTH, - RFKILL_STATE_UNBLOCKED); - } else { - ehotk->eeepc_bluetooth_rfkill->state = - RFKILL_STATE_SOFT_BLOCKED; - rfkill_set_default(RFKILL_TYPE_BLUETOOTH, - RFKILL_STATE_SOFT_BLOCKED); - } - + rfkill_set_global_sw_state(RFKILL_TYPE_BLUETOOTH, + get_acpi(CM_ASL_BLUETOOTH) != 1); result = rfkill_register(ehotk->eeepc_bluetooth_rfkill); if (result) goto bluetooth_fail; @@ -741,13 +703,10 @@ static int eeepc_hotk_add(struct acpi_device *device) return 0; bluetooth_fail: - if (ehotk->eeepc_bluetooth_rfkill) - rfkill_free(ehotk->eeepc_bluetooth_rfkill); + rfkill_destroy(ehotk->eeepc_bluetooth_rfkill); rfkill_unregister(ehotk->eeepc_wlan_rfkill); - ehotk->eeepc_wlan_rfkill = NULL; wlan_fail: - if (ehotk->eeepc_wlan_rfkill) - rfkill_free(ehotk->eeepc_wlan_rfkill); + rfkill_destroy(ehotk->eeepc_wlan_rfkill); eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6"); eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7"); ehotk_fail: diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index fe171fad12c..8d931145cbf 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -154,58 +154,46 @@ static int hp_wmi_dock_state(void) return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0); } -static int hp_wmi_wifi_set(void *data, enum rfkill_state state) +static int hp_wmi_set_block(void *data, bool blocked) { - if (state) - return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x101); - else - return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x100); -} + unsigned long b = (unsigned long) data; + int query = BIT(b + 8) | ((!!blocked) << b); -static int hp_wmi_bluetooth_set(void *data, enum rfkill_state state) -{ - if (state) - return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x202); - else - return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x200); + return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, query); } -static int hp_wmi_wwan_set(void *data, enum rfkill_state state) -{ - if (state) - return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x404); - else - return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x400); -} +static const struct rfkill_ops hp_wmi_rfkill_ops = { + .set_block = hp_wmi_set_block, +}; -static int hp_wmi_wifi_state(void) +static bool hp_wmi_wifi_state(void) { int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); if (wireless & 0x100) - return RFKILL_STATE_UNBLOCKED; + return false; else - return RFKILL_STATE_SOFT_BLOCKED; + return true; } -static int hp_wmi_bluetooth_state(void) +static bool hp_wmi_bluetooth_state(void) { int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); if (wireless & 0x10000) - return RFKILL_STATE_UNBLOCKED; + return false; else - return RFKILL_STATE_SOFT_BLOCKED; + return true; } -static int hp_wmi_wwan_state(void) +static bool hp_wmi_wwan_state(void) { int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); if (wireless & 0x1000000) - return RFKILL_STATE_UNBLOCKED; + return false; else - return RFKILL_STATE_SOFT_BLOCKED; + return true; } static ssize_t show_display(struct device *dev, struct device_attribute *attr, @@ -347,14 +335,14 @@ static void hp_wmi_notify(u32 value, void *context) } } else if (eventcode == 0x5) { if (wifi_rfkill) - rfkill_force_state(wifi_rfkill, - hp_wmi_wifi_state()); + rfkill_set_sw_state(wifi_rfkill, + hp_wmi_wifi_state()); if (bluetooth_rfkill) - rfkill_force_state(bluetooth_rfkill, - hp_wmi_bluetooth_state()); + rfkill_set_sw_state(bluetooth_rfkill, + hp_wmi_bluetooth_state()); if (wwan_rfkill) - rfkill_force_state(wwan_rfkill, - hp_wmi_wwan_state()); + rfkill_set_sw_state(wwan_rfkill, + hp_wmi_wwan_state()); } else printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n", eventcode); @@ -430,31 +418,34 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) goto add_sysfs_error; if (wireless & 0x1) { - wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN); - wifi_rfkill->name = "hp-wifi"; - wifi_rfkill->state = hp_wmi_wifi_state(); - wifi_rfkill->toggle_radio = hp_wmi_wifi_set; + wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, + RFKILL_TYPE_WLAN, + &hp_wmi_rfkill_ops, + (void *) 0); + rfkill_set_sw_state(wifi_rfkill, hp_wmi_wifi_state()); err = rfkill_register(wifi_rfkill); if (err) - goto add_sysfs_error; + goto register_wifi_error; } if (wireless & 0x2) { - bluetooth_rfkill = rfkill_allocate(&device->dev, - RFKILL_TYPE_BLUETOOTH); - bluetooth_rfkill->name = "hp-bluetooth"; - bluetooth_rfkill->state = hp_wmi_bluetooth_state(); - bluetooth_rfkill->toggle_radio = hp_wmi_bluetooth_set; + bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev, + RFKILL_TYPE_BLUETOOTH, + &hp_wmi_rfkill_ops, + (void *) 1); + rfkill_set_sw_state(bluetooth_rfkill, + hp_wmi_bluetooth_state()); err = rfkill_register(bluetooth_rfkill); if (err) goto register_bluetooth_error; } if (wireless & 0x4) { - wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN); - wwan_rfkill->name = "hp-wwan"; - wwan_rfkill->state = hp_wmi_wwan_state(); - wwan_rfkill->toggle_radio = hp_wmi_wwan_set; + wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev, + RFKILL_TYPE_WWAN, + &hp_wmi_rfkill_ops, + (void *) 2); + rfkill_set_sw_state(wwan_rfkill, hp_wmi_wwan_state()); err = rfkill_register(wwan_rfkill); if (err) goto register_wwan_err; @@ -462,11 +453,15 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) return 0; register_wwan_err: + rfkill_destroy(wwan_rfkill); if (bluetooth_rfkill) rfkill_unregister(bluetooth_rfkill); register_bluetooth_error: + rfkill_destroy(bluetooth_rfkill); if (wifi_rfkill) rfkill_unregister(wifi_rfkill); +register_wifi_error: + rfkill_destroy(wifi_rfkill); add_sysfs_error: cleanup_sysfs(device); return err; @@ -476,12 +471,18 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device) { cleanup_sysfs(device); - if (wifi_rfkill) + if (wifi_rfkill) { rfkill_unregister(wifi_rfkill); - if (bluetooth_rfkill) + rfkill_destroy(wifi_rfkill); + } + if (bluetooth_rfkill) { rfkill_unregister(bluetooth_rfkill); - if (wwan_rfkill) + rfkill_destroy(wifi_rfkill); + } + if (wwan_rfkill) { rfkill_unregister(wwan_rfkill); + rfkill_destroy(wwan_rfkill); + } return 0; } diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index f1963b05175..aec0b27fd77 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -128,11 +128,11 @@ enum sony_nc_rfkill { SONY_BLUETOOTH, SONY_WWAN, SONY_WIMAX, - SONY_RFKILL_MAX, + N_SONY_RFKILL, }; -static struct rfkill *sony_rfkill_devices[SONY_RFKILL_MAX]; -static int sony_rfkill_address[SONY_RFKILL_MAX] = {0x300, 0x500, 0x700, 0x900}; +static struct rfkill *sony_rfkill_devices[N_SONY_RFKILL]; +static int sony_rfkill_address[N_SONY_RFKILL] = {0x300, 0x500, 0x700, 0x900}; static void sony_nc_rfkill_update(void); /*********** Input Devices ***********/ @@ -1051,147 +1051,98 @@ static void sony_nc_rfkill_cleanup(void) { int i; - for (i = 0; i < SONY_RFKILL_MAX; i++) { - if (sony_rfkill_devices[i]) + for (i = 0; i < N_SONY_RFKILL; i++) { + if (sony_rfkill_devices[i]) { rfkill_unregister(sony_rfkill_devices[i]); + rfkill_destroy(sony_rfkill_devices[i]); + } } } -static int sony_nc_rfkill_get(void *data, enum rfkill_state *state) -{ - int result; - int argument = sony_rfkill_address[(long) data]; - - sony_call_snc_handle(0x124, 0x200, &result); - if (result & 0x1) { - sony_call_snc_handle(0x124, argument, &result); - if (result & 0xf) - *state = RFKILL_STATE_UNBLOCKED; - else - *state = RFKILL_STATE_SOFT_BLOCKED; - } else { - *state = RFKILL_STATE_HARD_BLOCKED; - } - - return 0; -} - -static int sony_nc_rfkill_set(void *data, enum rfkill_state state) +static int sony_nc_rfkill_set(void *data, bool blocked) { int result; int argument = sony_rfkill_address[(long) data] + 0x100; - if (state == RFKILL_STATE_UNBLOCKED) + if (!blocked) argument |= 0xff0000; return sony_call_snc_handle(0x124, argument, &result); } -static int sony_nc_setup_wifi_rfkill(struct acpi_device *device) -{ - int err = 0; - struct rfkill *sony_wifi_rfkill; - - sony_wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN); - if (!sony_wifi_rfkill) - return -1; - sony_wifi_rfkill->name = "sony-wifi"; - sony_wifi_rfkill->toggle_radio = sony_nc_rfkill_set; - sony_wifi_rfkill->get_state = sony_nc_rfkill_get; - sony_wifi_rfkill->data = (void *)SONY_WIFI; - err = rfkill_register(sony_wifi_rfkill); - if (err) - rfkill_free(sony_wifi_rfkill); - else { - sony_rfkill_devices[SONY_WIFI] = sony_wifi_rfkill; - sony_nc_rfkill_set(sony_wifi_rfkill->data, - RFKILL_STATE_UNBLOCKED); - } - return err; -} +static const struct rfkill_ops sony_rfkill_ops = { + .set_block = sony_nc_rfkill_set, +}; -static int sony_nc_setup_bluetooth_rfkill(struct acpi_device *device) +static int sony_nc_setup_rfkill(struct acpi_device *device, + enum sony_nc_rfkill nc_type) { int err = 0; - struct rfkill *sony_bluetooth_rfkill; - - sony_bluetooth_rfkill = rfkill_allocate(&device->dev, - RFKILL_TYPE_BLUETOOTH); - if (!sony_bluetooth_rfkill) - return -1; - sony_bluetooth_rfkill->name = "sony-bluetooth"; - sony_bluetooth_rfkill->toggle_radio = sony_nc_rfkill_set; - sony_bluetooth_rfkill->get_state = sony_nc_rfkill_get; - sony_bluetooth_rfkill->data = (void *)SONY_BLUETOOTH; - err = rfkill_register(sony_bluetooth_rfkill); - if (err) - rfkill_free(sony_bluetooth_rfkill); - else { - sony_rfkill_devices[SONY_BLUETOOTH] = sony_bluetooth_rfkill; - sony_nc_rfkill_set(sony_bluetooth_rfkill->data, - RFKILL_STATE_UNBLOCKED); + struct rfkill *rfk; + enum rfkill_type type; + const char *name; + + switch (nc_type) { + case SONY_WIFI: + type = RFKILL_TYPE_WLAN; + name = "sony-wifi"; + break; + case SONY_BLUETOOTH: + type = RFKILL_TYPE_BLUETOOTH; + name = "sony-bluetooth"; + break; + case SONY_WWAN: + type = RFKILL_TYPE_WWAN; + name = "sony-wwan"; + break; + case SONY_WIMAX: + type = RFKILL_TYPE_WIMAX; + name = "sony-wimax"; + break; + default: + return -EINVAL; } - return err; -} -static int sony_nc_setup_wwan_rfkill(struct acpi_device *device) -{ - int err = 0; - struct rfkill *sony_wwan_rfkill; + rfk = rfkill_alloc(name, &device->dev, type, + &sony_rfkill_ops, (void *)nc_type); + if (!rfk) + return -ENOMEM; - sony_wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN); - if (!sony_wwan_rfkill) - return -1; - sony_wwan_rfkill->name = "sony-wwan"; - sony_wwan_rfkill->toggle_radio = sony_nc_rfkill_set; - sony_wwan_rfkill->get_state = sony_nc_rfkill_get; - sony_wwan_rfkill->data = (void *)SONY_WWAN; - err = rfkill_register(sony_wwan_rfkill); - if (err) - rfkill_free(sony_wwan_rfkill); - else { - sony_rfkill_devices[SONY_WWAN] = sony_wwan_rfkill; - sony_nc_rfkill_set(sony_wwan_rfkill->data, - RFKILL_STATE_UNBLOCKED); + err = rfkill_register(rfk); + if (err) { + rfkill_destroy(rfk); + return err; } + sony_rfkill_devices[nc_type] = rfk; + sony_nc_rfkill_set((void *)nc_type, false); return err; } -static int sony_nc_setup_wimax_rfkill(struct acpi_device *device) +static void sony_nc_rfkill_update() { - int err = 0; - struct rfkill *sony_wimax_rfkill; + enum sony_nc_rfkill i; + int result; + bool hwblock; - sony_wimax_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WIMAX); - if (!sony_wimax_rfkill) - return -1; - sony_wimax_rfkill->name = "sony-wimax"; - sony_wimax_rfkill->toggle_radio = sony_nc_rfkill_set; - sony_wimax_rfkill->get_state = sony_nc_rfkill_get; - sony_wimax_rfkill->data = (void *)SONY_WIMAX; - err = rfkill_register(sony_wimax_rfkill); - if (err) - rfkill_free(sony_wimax_rfkill); - else { - sony_rfkill_devices[SONY_WIMAX] = sony_wimax_rfkill; - sony_nc_rfkill_set(sony_wimax_rfkill->data, - RFKILL_STATE_UNBLOCKED); - } - return err; -} + sony_call_snc_handle(0x124, 0x200, &result); + hwblock = !(result & 0x1); -static void sony_nc_rfkill_update() -{ - int i; - enum rfkill_state state; + for (i = 0; i < N_SONY_RFKILL; i++) { + int argument = sony_rfkill_address[i]; - for (i = 0; i < SONY_RFKILL_MAX; i++) { - if (sony_rfkill_devices[i]) { - sony_rfkill_devices[i]-> - get_state(sony_rfkill_devices[i]->data, - &state); - rfkill_force_state(sony_rfkill_devices[i], state); + if (!sony_rfkill_devices[i]) + continue; + + if (hwblock) { + if (rfkill_set_hw_state(sony_rfkill_devices[i], true)) + sony_nc_rfkill_set(sony_rfkill_devices[i], + true); + continue; } + + sony_call_snc_handle(0x124, argument, &result); + rfkill_set_states(sony_rfkill_devices[i], + !(result & 0xf), false); } } @@ -1210,13 +1161,13 @@ static int sony_nc_rfkill_setup(struct acpi_device *device) } if (result & 0x1) - sony_nc_setup_wifi_rfkill(device); + sony_nc_setup_rfkill(device, SONY_WIFI); if (result & 0x2) - sony_nc_setup_bluetooth_rfkill(device); + sony_nc_setup_rfkill(device, SONY_BLUETOOTH); if (result & 0x1c) - sony_nc_setup_wwan_rfkill(device); + sony_nc_setup_rfkill(device, SONY_WWAN); if (result & 0x20) - sony_nc_setup_wimax_rfkill(device); + sony_nc_setup_rfkill(device, SONY_WIMAX); return 0; } diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 912be65b626..cfcafa4e947 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -166,13 +166,6 @@ enum { #define TPACPI_MAX_ACPI_ARGS 3 -/* rfkill switches */ -enum { - TPACPI_RFK_BLUETOOTH_SW_ID = 0, - TPACPI_RFK_WWAN_SW_ID, - TPACPI_RFK_UWB_SW_ID, -}; - /* printk headers */ #define TPACPI_LOG TPACPI_FILE ": " #define TPACPI_EMERG KERN_EMERG TPACPI_LOG @@ -1005,67 +998,237 @@ static int __init tpacpi_check_std_acpi_brightness_support(void) return 0; } -static int __init tpacpi_new_rfkill(const unsigned int id, - struct rfkill **rfk, +static void printk_deprecated_attribute(const char * const what, + const char * const details) +{ + tpacpi_log_usertask("deprecated sysfs attribute"); + printk(TPACPI_WARN "WARNING: sysfs attribute %s is deprecated and " + "will be removed. %s\n", + what, details); +} + +/************************************************************************* + * rfkill and radio control support helpers + */ + +/* + * ThinkPad-ACPI firmware handling model: + * + * WLSW (master wireless switch) is event-driven, and is common to all + * firmware-controlled radios. It cannot be controlled, just monitored, + * as expected. It overrides all radio state in firmware + * + * The kernel, a masked-off hotkey, and WLSW can change the radio state + * (TODO: verify how WLSW interacts with the returned radio state). + * + * The only time there are shadow radio state changes, is when + * masked-off hotkeys are used. + */ + +/* + * Internal driver API for radio state: + * + * int: < 0 = error, otherwise enum tpacpi_rfkill_state + * bool: true means radio blocked (off) + */ +enum tpacpi_rfkill_state { + TPACPI_RFK_RADIO_OFF = 0, + TPACPI_RFK_RADIO_ON +}; + +/* rfkill switches */ +enum tpacpi_rfk_id { + TPACPI_RFK_BLUETOOTH_SW_ID = 0, + TPACPI_RFK_WWAN_SW_ID, + TPACPI_RFK_UWB_SW_ID, + TPACPI_RFK_SW_MAX +}; + +static const char *tpacpi_rfkill_names[] = { + [TPACPI_RFK_BLUETOOTH_SW_ID] = "bluetooth", + [TPACPI_RFK_WWAN_SW_ID] = "wwan", + [TPACPI_RFK_UWB_SW_ID] = "uwb", + [TPACPI_RFK_SW_MAX] = NULL +}; + +/* ThinkPad-ACPI rfkill subdriver */ +struct tpacpi_rfk { + struct rfkill *rfkill; + enum tpacpi_rfk_id id; + const struct tpacpi_rfk_ops *ops; +}; + +struct tpacpi_rfk_ops { + /* firmware interface */ + int (*get_status)(void); + int (*set_status)(const enum tpacpi_rfkill_state); +}; + +static struct tpacpi_rfk *tpacpi_rfkill_switches[TPACPI_RFK_SW_MAX]; + +/* Query FW and update rfkill sw state for a given rfkill switch */ +static int tpacpi_rfk_update_swstate(const struct tpacpi_rfk *tp_rfk) +{ + int status; + + if (!tp_rfk) + return -ENODEV; + + status = (tp_rfk->ops->get_status)(); + if (status < 0) + return status; + + rfkill_set_sw_state(tp_rfk->rfkill, + (status == TPACPI_RFK_RADIO_OFF)); + + return status; +} + +/* Query FW and update rfkill sw state for all rfkill switches */ +static void tpacpi_rfk_update_swstate_all(void) +{ + unsigned int i; + + for (i = 0; i < TPACPI_RFK_SW_MAX; i++) + tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[i]); +} + +/* + * Sync the HW-blocking state of all rfkill switches, + * do notice it causes the rfkill core to schedule uevents + */ +static void tpacpi_rfk_update_hwblock_state(bool blocked) +{ + unsigned int i; + struct tpacpi_rfk *tp_rfk; + + for (i = 0; i < TPACPI_RFK_SW_MAX; i++) { + tp_rfk = tpacpi_rfkill_switches[i]; + if (tp_rfk) { + if (rfkill_set_hw_state(tp_rfk->rfkill, + blocked)) { + /* ignore -- we track sw block */ + } + } + } +} + +/* Call to get the WLSW state from the firmware */ +static int hotkey_get_wlsw(void); + +/* Call to query WLSW state and update all rfkill switches */ +static bool tpacpi_rfk_check_hwblock_state(void) +{ + int res = hotkey_get_wlsw(); + int hw_blocked; + + /* When unknown or unsupported, we have to assume it is unblocked */ + if (res < 0) + return false; + + hw_blocked = (res == TPACPI_RFK_RADIO_OFF); + tpacpi_rfk_update_hwblock_state(hw_blocked); + + return hw_blocked; +} + +static int tpacpi_rfk_hook_set_block(void *data, bool blocked) +{ + struct tpacpi_rfk *tp_rfk = data; + int res; + + dbg_printk(TPACPI_DBG_RFKILL, + "request to change radio state to %s\n", + blocked ? "blocked" : "unblocked"); + + /* try to set radio state */ + res = (tp_rfk->ops->set_status)(blocked ? + TPACPI_RFK_RADIO_OFF : TPACPI_RFK_RADIO_ON); + + /* and update the rfkill core with whatever the FW really did */ + tpacpi_rfk_update_swstate(tp_rfk); + + return (res < 0) ? res : 0; +} + +static const struct rfkill_ops tpacpi_rfk_rfkill_ops = { + .set_block = tpacpi_rfk_hook_set_block, +}; + +static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, + const struct tpacpi_rfk_ops *tp_rfkops, const enum rfkill_type rfktype, const char *name, - const bool set_default, - int (*toggle_radio)(void *, enum rfkill_state), - int (*get_state)(void *, enum rfkill_state *)) + const bool set_default) { + struct tpacpi_rfk *atp_rfk; int res; - enum rfkill_state initial_state = RFKILL_STATE_SOFT_BLOCKED; + bool initial_sw_state = false; + int initial_sw_status; - res = get_state(NULL, &initial_state); - if (res < 0) { + BUG_ON(id >= TPACPI_RFK_SW_MAX || tpacpi_rfkill_switches[id]); + + initial_sw_status = (tp_rfkops->get_status)(); + if (initial_sw_status < 0) { printk(TPACPI_ERR "failed to read initial state for %s, error %d; " - "will turn radio off\n", name, res); - } else if (set_default) { - /* try to set the initial state as the default for the rfkill - * type, since we ask the firmware to preserve it across S5 in - * NVRAM */ - if (rfkill_set_default(rfktype, - (initial_state == RFKILL_STATE_UNBLOCKED) ? - RFKILL_STATE_UNBLOCKED : - RFKILL_STATE_SOFT_BLOCKED) == -EPERM) - vdbg_printk(TPACPI_DBG_RFKILL, - "Default state for %s cannot be changed\n", - name); - } - - *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype); - if (!*rfk) { + "will turn radio off\n", name, initial_sw_status); + } else { + initial_sw_state = (initial_sw_status == TPACPI_RFK_RADIO_OFF); + if (set_default) { + /* try to set the initial state as the default for the + * rfkill type, since we ask the firmware to preserve + * it across S5 in NVRAM */ + rfkill_set_global_sw_state(rfktype, initial_sw_state); + } + } + + atp_rfk = kzalloc(sizeof(struct tpacpi_rfk), GFP_KERNEL); + if (atp_rfk) + atp_rfk->rfkill = rfkill_alloc(name, + &tpacpi_pdev->dev, + rfktype, + &tpacpi_rfk_rfkill_ops, + atp_rfk); + if (!atp_rfk || !atp_rfk->rfkill) { printk(TPACPI_ERR "failed to allocate memory for rfkill class\n"); + kfree(atp_rfk); return -ENOMEM; } - (*rfk)->name = name; - (*rfk)->get_state = get_state; - (*rfk)->toggle_radio = toggle_radio; - (*rfk)->state = initial_state; + atp_rfk->id = id; + atp_rfk->ops = tp_rfkops; + + rfkill_set_states(atp_rfk->rfkill, initial_sw_state, + tpacpi_rfk_check_hwblock_state()); - res = rfkill_register(*rfk); + res = rfkill_register(atp_rfk->rfkill); if (res < 0) { printk(TPACPI_ERR "failed to register %s rfkill switch: %d\n", name, res); - rfkill_free(*rfk); - *rfk = NULL; + rfkill_destroy(atp_rfk->rfkill); + kfree(atp_rfk); return res; } + tpacpi_rfkill_switches[id] = atp_rfk; return 0; } -static void printk_deprecated_attribute(const char * const what, - const char * const details) +static void tpacpi_destroy_rfkill(const enum tpacpi_rfk_id id) { - tpacpi_log_usertask("deprecated sysfs attribute"); - printk(TPACPI_WARN "WARNING: sysfs attribute %s is deprecated and " - "will be removed. %s\n", - what, details); + struct tpacpi_rfk *tp_rfk; + + BUG_ON(id >= TPACPI_RFK_SW_MAX); + + tp_rfk = tpacpi_rfkill_switches[id]; + if (tp_rfk) { + rfkill_unregister(tp_rfk->rfkill); + tpacpi_rfkill_switches[id] = NULL; + kfree(tp_rfk); + } } static void printk_deprecated_rfkill_attribute(const char * const what) @@ -1074,6 +1237,112 @@ static void printk_deprecated_rfkill_attribute(const char * const what) "Please switch to generic rfkill before year 2010"); } +/* sysfs enable ------------------------------------------------ */ +static ssize_t tpacpi_rfk_sysfs_enable_show(const enum tpacpi_rfk_id id, + struct device_attribute *attr, + char *buf) +{ + int status; + + printk_deprecated_rfkill_attribute(attr->attr.name); + + /* This is in the ABI... */ + if (tpacpi_rfk_check_hwblock_state()) { + status = TPACPI_RFK_RADIO_OFF; + } else { + status = tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[id]); + if (status < 0) + return status; + } + + return snprintf(buf, PAGE_SIZE, "%d\n", + (status == TPACPI_RFK_RADIO_ON) ? 1 : 0); +} + +static ssize_t tpacpi_rfk_sysfs_enable_store(const enum tpacpi_rfk_id id, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long t; + int res; + + printk_deprecated_rfkill_attribute(attr->attr.name); + + if (parse_strtoul(buf, 1, &t)) + return -EINVAL; + + tpacpi_disclose_usertask(attr->attr.name, "set to %ld\n", t); + + /* This is in the ABI... */ + if (tpacpi_rfk_check_hwblock_state() && !!t) + return -EPERM; + + res = tpacpi_rfkill_switches[id]->ops->set_status((!!t) ? + TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF); + tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[id]); + + return (res < 0) ? res : count; +} + +/* procfs -------------------------------------------------------------- */ +static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, char *p) +{ + int len = 0; + + if (id >= TPACPI_RFK_SW_MAX) + len += sprintf(p + len, "status:\t\tnot supported\n"); + else { + int status; + + /* This is in the ABI... */ + if (tpacpi_rfk_check_hwblock_state()) { + status = TPACPI_RFK_RADIO_OFF; + } else { + status = tpacpi_rfk_update_swstate( + tpacpi_rfkill_switches[id]); + if (status < 0) + return status; + } + + len += sprintf(p + len, "status:\t\t%s\n", + (status == TPACPI_RFK_RADIO_ON) ? + "enabled" : "disabled"); + len += sprintf(p + len, "commands:\tenable, disable\n"); + } + + return len; +} + +static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf) +{ + char *cmd; + int status = -1; + int res = 0; + + if (id >= TPACPI_RFK_SW_MAX) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "enable") == 0) + status = TPACPI_RFK_RADIO_ON; + else if (strlencmp(cmd, "disable") == 0) + status = TPACPI_RFK_RADIO_OFF; + else + return -EINVAL; + } + + if (status != -1) { + tpacpi_disclose_usertask("procfs", "attempt to %s %s\n", + (status == TPACPI_RFK_RADIO_ON) ? + "enable" : "disable", + tpacpi_rfkill_names[id]); + res = (tpacpi_rfkill_switches[id]->ops->set_status)(status); + tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[id]); + } + + return res; +} + /************************************************************************* * thinkpad-acpi driver attributes */ @@ -1127,8 +1396,6 @@ static DRIVER_ATTR(version, S_IRUGO, #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES -static void tpacpi_send_radiosw_update(void); - /* wlsw_emulstate ------------------------------------------------------ */ static ssize_t tpacpi_driver_wlsw_emulstate_show(struct device_driver *drv, char *buf) @@ -1144,11 +1411,10 @@ static ssize_t tpacpi_driver_wlsw_emulstate_store(struct device_driver *drv, if (parse_strtoul(buf, 1, &t)) return -EINVAL; - if (tpacpi_wlsw_emulstate != t) { - tpacpi_wlsw_emulstate = !!t; - tpacpi_send_radiosw_update(); - } else + if (tpacpi_wlsw_emulstate != !!t) { tpacpi_wlsw_emulstate = !!t; + tpacpi_rfk_update_hwblock_state(!t); /* negative logic */ + } return count; } @@ -1463,17 +1729,23 @@ static struct attribute_set *hotkey_dev_attributes; /* HKEY.MHKG() return bits */ #define TP_HOTKEY_TABLET_MASK (1 << 3) -static int hotkey_get_wlsw(int *status) +static int hotkey_get_wlsw(void) { + int status; + + if (!tp_features.hotkey_wlsw) + return -ENODEV; + #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES - if (dbg_wlswemul) { - *status = !!tpacpi_wlsw_emulstate; - return 0; - } + if (dbg_wlswemul) + return (tpacpi_wlsw_emulstate) ? + TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; #endif - if (!acpi_evalf(hkey_handle, status, "WLSW", "d")) + + if (!acpi_evalf(hkey_handle, &status, "WLSW", "d")) return -EIO; - return 0; + + return (status) ? TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; } static int hotkey_get_tablet_mode(int *status) @@ -2107,12 +2379,16 @@ static ssize_t hotkey_radio_sw_show(struct device *dev, struct device_attribute *attr, char *buf) { - int res, s; - res = hotkey_get_wlsw(&s); + int res; + res = hotkey_get_wlsw(); if (res < 0) return res; - return snprintf(buf, PAGE_SIZE, "%d\n", !!s); + /* Opportunistic update */ + tpacpi_rfk_update_hwblock_state((res == TPACPI_RFK_RADIO_OFF)); + + return snprintf(buf, PAGE_SIZE, "%d\n", + (res == TPACPI_RFK_RADIO_OFF) ? 0 : 1); } static struct device_attribute dev_attr_hotkey_radio_sw = @@ -2223,30 +2499,52 @@ static struct attribute *hotkey_mask_attributes[] __initdata = { &dev_attr_hotkey_wakeup_hotunplug_complete.attr, }; -static void bluetooth_update_rfk(void); -static void wan_update_rfk(void); -static void uwb_update_rfk(void); +/* + * Sync both the hw and sw blocking state of all switches + */ static void tpacpi_send_radiosw_update(void) { int wlsw; - /* Sync these BEFORE sending any rfkill events */ - if (tp_features.bluetooth) - bluetooth_update_rfk(); - if (tp_features.wan) - wan_update_rfk(); - if (tp_features.uwb) - uwb_update_rfk(); + /* + * We must sync all rfkill controllers *before* issuing any + * rfkill input events, or we will race the rfkill core input + * handler. + * + * tpacpi_inputdev_send_mutex works as a syncronization point + * for the above. + * + * We optimize to avoid numerous calls to hotkey_get_wlsw. + */ - if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { + wlsw = hotkey_get_wlsw(); + + /* Sync hw blocking state first if it is hw-blocked */ + if (wlsw == TPACPI_RFK_RADIO_OFF) + tpacpi_rfk_update_hwblock_state(true); + + /* Sync sw blocking state */ + tpacpi_rfk_update_swstate_all(); + + /* Sync hw blocking state last if it is hw-unblocked */ + if (wlsw == TPACPI_RFK_RADIO_ON) + tpacpi_rfk_update_hwblock_state(false); + + /* Issue rfkill input event for WLSW switch */ + if (!(wlsw < 0)) { mutex_lock(&tpacpi_inputdev_send_mutex); input_report_switch(tpacpi_inputdev, - SW_RFKILL_ALL, !!wlsw); + SW_RFKILL_ALL, (wlsw > 0)); input_sync(tpacpi_inputdev); mutex_unlock(&tpacpi_inputdev_send_mutex); } + + /* + * this can be unconditional, as we will poll state again + * if userspace uses the notify to read data + */ hotkey_radio_sw_notify_change(); } @@ -3056,8 +3354,6 @@ enum { #define TPACPI_RFK_BLUETOOTH_SW_NAME "tpacpi_bluetooth_sw" -static struct rfkill *tpacpi_bluetooth_rfkill; - static void bluetooth_suspend(pm_message_t state) { /* Try to make sure radio will resume powered off */ @@ -3067,83 +3363,47 @@ static void bluetooth_suspend(pm_message_t state) "bluetooth power down on resume request failed\n"); } -static int bluetooth_get_radiosw(void) +static int bluetooth_get_status(void) { int status; - if (!tp_features.bluetooth) - return -ENODEV; - - /* WLSW overrides bluetooth in firmware/hardware, reflect that */ - if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) - return RFKILL_STATE_HARD_BLOCKED; - #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES if (dbg_bluetoothemul) return (tpacpi_bluetooth_emulstate) ? - RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; + TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; #endif if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) return -EIO; return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ? - RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; + TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; } -static void bluetooth_update_rfk(void) +static int bluetooth_set_status(enum tpacpi_rfkill_state state) { int status; - if (!tpacpi_bluetooth_rfkill) - return; - - status = bluetooth_get_radiosw(); - if (status < 0) - return; - rfkill_force_state(tpacpi_bluetooth_rfkill, status); - vdbg_printk(TPACPI_DBG_RFKILL, - "forced rfkill state to %d\n", - status); -} - -static int bluetooth_set_radiosw(int radio_on, int update_rfk) -{ - int status; - - if (!tp_features.bluetooth) - return -ENODEV; - - /* WLSW overrides bluetooth in firmware/hardware, but there is no - * reason to risk weird behaviour. */ - if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status - && radio_on) - return -EPERM; - - vdbg_printk(TPACPI_DBG_RFKILL, - "will %s bluetooth\n", radio_on ? "enable" : "disable"); + "will attempt to %s bluetooth\n", + (state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable"); #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES if (dbg_bluetoothemul) { - tpacpi_bluetooth_emulstate = !!radio_on; - if (update_rfk) - bluetooth_update_rfk(); + tpacpi_bluetooth_emulstate = (state == TPACPI_RFK_RADIO_ON); return 0; } #endif /* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */ - if (radio_on) + if (state == TPACPI_RFK_RADIO_ON) status = TP_ACPI_BLUETOOTH_RADIOSSW; else status = 0; + if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) return -EIO; - if (update_rfk) - bluetooth_update_rfk(); - return 0; } @@ -3152,35 +3412,16 @@ static ssize_t bluetooth_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { - int status; - - printk_deprecated_rfkill_attribute("bluetooth_enable"); - - status = bluetooth_get_radiosw(); - if (status < 0) - return status; - - return snprintf(buf, PAGE_SIZE, "%d\n", - (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0); + return tpacpi_rfk_sysfs_enable_show(TPACPI_RFK_BLUETOOTH_SW_ID, + attr, buf); } static ssize_t bluetooth_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long t; - int res; - - printk_deprecated_rfkill_attribute("bluetooth_enable"); - - if (parse_strtoul(buf, 1, &t)) - return -EINVAL; - - tpacpi_disclose_usertask("bluetooth_enable", "set to %ld\n", t); - - res = bluetooth_set_radiosw(t, 1); - - return (res) ? res : count; + return tpacpi_rfk_sysfs_enable_store(TPACPI_RFK_BLUETOOTH_SW_ID, + attr, buf, count); } static struct device_attribute dev_attr_bluetooth_enable = @@ -3198,23 +3439,10 @@ static const struct attribute_group bluetooth_attr_group = { .attrs = bluetooth_attributes, }; -static int tpacpi_bluetooth_rfk_get(void *data, enum rfkill_state *state) -{ - int bts = bluetooth_get_radiosw(); - - if (bts < 0) - return bts; - - *state = bts; - return 0; -} - -static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state) -{ - dbg_printk(TPACPI_DBG_RFKILL, - "request to change radio state to %d\n", state); - return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); -} +static const struct tpacpi_rfk_ops bluetooth_tprfk_ops = { + .get_status = bluetooth_get_status, + .set_status = bluetooth_set_status, +}; static void bluetooth_shutdown(void) { @@ -3230,13 +3458,12 @@ static void bluetooth_shutdown(void) static void bluetooth_exit(void) { - bluetooth_shutdown(); - - if (tpacpi_bluetooth_rfkill) - rfkill_unregister(tpacpi_bluetooth_rfkill); - sysfs_remove_group(&tpacpi_pdev->dev.kobj, &bluetooth_attr_group); + + tpacpi_destroy_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID); + + bluetooth_shutdown(); } static int __init bluetooth_init(struct ibm_init_struct *iibm) @@ -3277,20 +3504,18 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) if (!tp_features.bluetooth) return 1; - res = sysfs_create_group(&tpacpi_pdev->dev.kobj, - &bluetooth_attr_group); - if (res) - return res; - res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID, - &tpacpi_bluetooth_rfkill, + &bluetooth_tprfk_ops, RFKILL_TYPE_BLUETOOTH, TPACPI_RFK_BLUETOOTH_SW_NAME, - true, - tpacpi_bluetooth_rfk_set, - tpacpi_bluetooth_rfk_get); + true); + if (res) + return res; + + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &bluetooth_attr_group); if (res) { - bluetooth_exit(); + tpacpi_destroy_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID); return res; } @@ -3300,46 +3525,12 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) /* procfs -------------------------------------------------------------- */ static int bluetooth_read(char *p) { - int len = 0; - int status = bluetooth_get_radiosw(); - - if (!tp_features.bluetooth) - len += sprintf(p + len, "status:\t\tnot supported\n"); - else { - len += sprintf(p + len, "status:\t\t%s\n", - (status == RFKILL_STATE_UNBLOCKED) ? - "enabled" : "disabled"); - len += sprintf(p + len, "commands:\tenable, disable\n"); - } - - return len; + return tpacpi_rfk_procfs_read(TPACPI_RFK_BLUETOOTH_SW_ID, p); } static int bluetooth_write(char *buf) { - char *cmd; - int state = -1; - - if (!tp_features.bluetooth) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "enable") == 0) { - state = 1; - } else if (strlencmp(cmd, "disable") == 0) { - state = 0; - } else - return -EINVAL; - } - - if (state != -1) { - tpacpi_disclose_usertask("procfs bluetooth", - "attempt to %s\n", - state ? "enable" : "disable"); - bluetooth_set_radiosw(state, 1); - } - - return 0; + return tpacpi_rfk_procfs_write(TPACPI_RFK_BLUETOOTH_SW_ID, buf); } static struct ibm_struct bluetooth_driver_data = { @@ -3365,8 +3556,6 @@ enum { #define TPACPI_RFK_WWAN_SW_NAME "tpacpi_wwan_sw" -static struct rfkill *tpacpi_wan_rfkill; - static void wan_suspend(pm_message_t state) { /* Try to make sure radio will resume powered off */ @@ -3376,83 +3565,47 @@ static void wan_suspend(pm_message_t state) "WWAN power down on resume request failed\n"); } -static int wan_get_radiosw(void) +static int wan_get_status(void) { int status; - if (!tp_features.wan) - return -ENODEV; - - /* WLSW overrides WWAN in firmware/hardware, reflect that */ - if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) - return RFKILL_STATE_HARD_BLOCKED; - #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES if (dbg_wwanemul) return (tpacpi_wwan_emulstate) ? - RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; + TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; #endif if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) return -EIO; return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ? - RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; -} - -static void wan_update_rfk(void) -{ - int status; - - if (!tpacpi_wan_rfkill) - return; - - status = wan_get_radiosw(); - if (status < 0) - return; - rfkill_force_state(tpacpi_wan_rfkill, status); - - vdbg_printk(TPACPI_DBG_RFKILL, - "forced rfkill state to %d\n", - status); + TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; } -static int wan_set_radiosw(int radio_on, int update_rfk) +static int wan_set_status(enum tpacpi_rfkill_state state) { int status; - if (!tp_features.wan) - return -ENODEV; - - /* WLSW overrides bluetooth in firmware/hardware, but there is no - * reason to risk weird behaviour. */ - if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status - && radio_on) - return -EPERM; - vdbg_printk(TPACPI_DBG_RFKILL, - "will %s WWAN\n", radio_on ? "enable" : "disable"); + "will attempt to %s wwan\n", + (state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable"); #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES if (dbg_wwanemul) { - tpacpi_wwan_emulstate = !!radio_on; - if (update_rfk) - wan_update_rfk(); + tpacpi_wwan_emulstate = (state == TPACPI_RFK_RADIO_ON); return 0; } #endif /* We make sure to keep TP_ACPI_WANCARD_RESUMECTRL off */ - if (radio_on) + if (state == TPACPI_RFK_RADIO_ON) status = TP_ACPI_WANCARD_RADIOSSW; else status = 0; + if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) return -EIO; - if (update_rfk) - wan_update_rfk(); - return 0; } @@ -3461,35 +3614,16 @@ static ssize_t wan_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { - int status; - - printk_deprecated_rfkill_attribute("wwan_enable"); - - status = wan_get_radiosw(); - if (status < 0) - return status; - - return snprintf(buf, PAGE_SIZE, "%d\n", - (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0); + return tpacpi_rfk_sysfs_enable_show(TPACPI_RFK_WWAN_SW_ID, + attr, buf); } static ssize_t wan_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - unsigned long t; - int res; - - printk_deprecated_rfkill_attribute("wwan_enable"); - - if (parse_strtoul(buf, 1, &t)) - return -EINVAL; - - tpacpi_disclose_usertask("wwan_enable", "set to %ld\n", t); - - res = wan_set_radiosw(t, 1); - - return (res) ? res : count; + return tpacpi_rfk_sysfs_enable_store(TPACPI_RFK_WWAN_SW_ID, + attr, buf, count); } static struct device_attribute dev_attr_wan_enable = @@ -3507,23 +3641,10 @@ static const struct attribute_group wan_attr_group = { .attrs = wan_attributes, }; -static int tpacpi_wan_rfk_get(void *data, enum rfkill_state *state) -{ - int wans = wan_get_radiosw(); - - if (wans < 0) - return wans; - - *state = wans; - return 0; -} - -static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state) -{ - dbg_printk(TPACPI_DBG_RFKILL, - "request to change radio state to %d\n", state); - return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); -} +static const struct tpacpi_rfk_ops wan_tprfk_ops = { + .get_status = wan_get_status, + .set_status = wan_set_status, +}; static void wan_shutdown(void) { @@ -3539,13 +3660,12 @@ static void wan_shutdown(void) static void wan_exit(void) { - wan_shutdown(); - - if (tpacpi_wan_rfkill) - rfkill_unregister(tpacpi_wan_rfkill); - sysfs_remove_group(&tpacpi_pdev->dev.kobj, &wan_attr_group); + + tpacpi_destroy_rfkill(TPACPI_RFK_WWAN_SW_ID); + + wan_shutdown(); } static int __init wan_init(struct ibm_init_struct *iibm) @@ -3584,20 +3704,19 @@ static int __init wan_init(struct ibm_init_struct *iibm) if (!tp_features.wan) return 1; - res = sysfs_create_group(&tpacpi_pdev->dev.kobj, - &wan_attr_group); - if (res) - return res; - res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID, - &tpacpi_wan_rfkill, + &wan_tprfk_ops, RFKILL_TYPE_WWAN, TPACPI_RFK_WWAN_SW_NAME, - true, - tpacpi_wan_rfk_set, - tpacpi_wan_rfk_get); + true); + if (res) + return res; + + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &wan_attr_group); + if (res) { - wan_exit(); + tpacpi_destroy_rfkill(TPACPI_RFK_WWAN_SW_ID); return res; } @@ -3607,48 +3726,12 @@ static int __init wan_init(struct ibm_init_struct *iibm) /* procfs -------------------------------------------------------------- */ static int wan_read(char *p) { - int len = 0; - int status = wan_get_radiosw(); - - tpacpi_disclose_usertask("procfs wan", "read"); - - if (!tp_features.wan) - len += sprintf(p + len, "status:\t\tnot supported\n"); - else { - len += sprintf(p + len, "status:\t\t%s\n", - (status == RFKILL_STATE_UNBLOCKED) ? - "enabled" : "disabled"); - len += sprintf(p + len, "commands:\tenable, disable\n"); - } - - return len; + return tpacpi_rfk_procfs_read(TPACPI_RFK_WWAN_SW_ID, p); } static int wan_write(char *buf) { - char *cmd; - int state = -1; - - if (!tp_features.wan) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "enable") == 0) { - state = 1; - } else if (strlencmp(cmd, "disable") == 0) { - state = 0; - } else - return -EINVAL; - } - - if (state != -1) { - tpacpi_disclose_usertask("procfs wan", - "attempt to %s\n", - state ? "enable" : "disable"); - wan_set_radiosw(state, 1); - } - - return 0; + return tpacpi_rfk_procfs_write(TPACPI_RFK_WWAN_SW_ID, buf); } static struct ibm_struct wan_driver_data = { @@ -3672,108 +3755,59 @@ enum { #define TPACPI_RFK_UWB_SW_NAME "tpacpi_uwb_sw" -static struct rfkill *tpacpi_uwb_rfkill; - -static int uwb_get_radiosw(void) +static int uwb_get_status(void) { int status; - if (!tp_features.uwb) - return -ENODEV; - - /* WLSW overrides UWB in firmware/hardware, reflect that */ - if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) - return RFKILL_STATE_HARD_BLOCKED; - #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES if (dbg_uwbemul) return (tpacpi_uwb_emulstate) ? - RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; + TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; #endif if (!acpi_evalf(hkey_handle, &status, "GUWB", "d")) return -EIO; return ((status & TP_ACPI_UWB_RADIOSSW) != 0) ? - RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; + TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; } -static void uwb_update_rfk(void) +static int uwb_set_status(enum tpacpi_rfkill_state state) { int status; - if (!tpacpi_uwb_rfkill) - return; - - status = uwb_get_radiosw(); - if (status < 0) - return; - rfkill_force_state(tpacpi_uwb_rfkill, status); - vdbg_printk(TPACPI_DBG_RFKILL, - "forced rfkill state to %d\n", - status); -} - -static int uwb_set_radiosw(int radio_on, int update_rfk) -{ - int status; - - if (!tp_features.uwb) - return -ENODEV; - - /* WLSW overrides UWB in firmware/hardware, but there is no - * reason to risk weird behaviour. */ - if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status - && radio_on) - return -EPERM; - - vdbg_printk(TPACPI_DBG_RFKILL, - "will %s UWB\n", radio_on ? "enable" : "disable"); + "will attempt to %s UWB\n", + (state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable"); #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES if (dbg_uwbemul) { - tpacpi_uwb_emulstate = !!radio_on; - if (update_rfk) - uwb_update_rfk(); + tpacpi_uwb_emulstate = (state == TPACPI_RFK_RADIO_ON); return 0; } #endif - status = (radio_on) ? TP_ACPI_UWB_RADIOSSW : 0; + if (state == TPACPI_RFK_RADIO_ON) + status = TP_ACPI_UWB_RADIOSSW; + else + status = 0; + if (!acpi_evalf(hkey_handle, NULL, "SUWB", "vd", status)) return -EIO; - if (update_rfk) - uwb_update_rfk(); - return 0; } /* --------------------------------------------------------------------- */ -static int tpacpi_uwb_rfk_get(void *data, enum rfkill_state *state) -{ - int uwbs = uwb_get_radiosw(); - - if (uwbs < 0) - return uwbs; - - *state = uwbs; - return 0; -} - -static int tpacpi_uwb_rfk_set(void *data, enum rfkill_state state) -{ - dbg_printk(TPACPI_DBG_RFKILL, - "request to change radio state to %d\n", state); - return uwb_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); -} +static const struct tpacpi_rfk_ops uwb_tprfk_ops = { + .get_status = uwb_get_status, + .set_status = uwb_set_status, +}; static void uwb_exit(void) { - if (tpacpi_uwb_rfkill) - rfkill_unregister(tpacpi_uwb_rfkill); + tpacpi_destroy_rfkill(TPACPI_RFK_UWB_SW_ID); } static int __init uwb_init(struct ibm_init_struct *iibm) @@ -3813,13 +3847,10 @@ static int __init uwb_init(struct ibm_init_struct *iibm) return 1; res = tpacpi_new_rfkill(TPACPI_RFK_UWB_SW_ID, - &tpacpi_uwb_rfkill, + &uwb_tprfk_ops, RFKILL_TYPE_UWB, TPACPI_RFK_UWB_SW_NAME, - false, - tpacpi_uwb_rfk_set, - tpacpi_uwb_rfk_get); - + false); return res; } diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 4345089f517..81d31ea507d 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -45,7 +45,6 @@ #include #include #include -#include #include @@ -250,21 +249,15 @@ static acpi_status hci_read2(u32 reg, u32 *out1, u32 *out2, u32 *result) struct toshiba_acpi_dev { struct platform_device *p_dev; - struct rfkill *rfk_dev; - struct input_polled_dev *poll_dev; + struct rfkill *bt_rfk; const char *bt_name; - const char *rfk_name; - - bool last_rfk_state; struct mutex mutex; }; static struct toshiba_acpi_dev toshiba_acpi = { .bt_name = "Toshiba Bluetooth", - .rfk_name = "Toshiba RFKill Switch", - .last_rfk_state = false, }; /* Bluetooth rfkill handlers */ @@ -283,21 +276,6 @@ static u32 hci_get_bt_present(bool *present) return hci_result; } -static u32 hci_get_bt_on(bool *on) -{ - u32 hci_result; - u32 value, value2; - - value = 0; - value2 = 0x0001; - hci_read2(HCI_WIRELESS, &value, &value2, &hci_result); - if (hci_result == HCI_SUCCESS) - *on = (value & HCI_WIRELESS_BT_POWER) && - (value & HCI_WIRELESS_BT_ATTACH); - - return hci_result; -} - static u32 hci_get_radio_state(bool *radio_state) { u32 hci_result; @@ -311,70 +289,67 @@ static u32 hci_get_radio_state(bool *radio_state) return hci_result; } -static int bt_rfkill_toggle_radio(void *data, enum rfkill_state state) +static int bt_rfkill_set_block(void *data, bool blocked) { + struct toshiba_acpi_dev *dev = data; u32 result1, result2; u32 value; + int err; bool radio_state; - struct toshiba_acpi_dev *dev = data; - value = (state == RFKILL_STATE_UNBLOCKED); + value = (blocked == false); - if (hci_get_radio_state(&radio_state) != HCI_SUCCESS) - return -EFAULT; + mutex_lock(&dev->mutex); + if (hci_get_radio_state(&radio_state) != HCI_SUCCESS) { + err = -EBUSY; + goto out; + } - switch (state) { - case RFKILL_STATE_UNBLOCKED: - if (!radio_state) - return -EPERM; - break; - case RFKILL_STATE_SOFT_BLOCKED: - break; - default: - return -EINVAL; + if (!radio_state) { + err = 0; + goto out; } - mutex_lock(&dev->mutex); hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1); hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2); - mutex_unlock(&dev->mutex); if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS) - return -EFAULT; - - return 0; + err = -EBUSY; + else + err = 0; + out: + mutex_unlock(&dev->mutex); + return err; } -static void bt_poll_rfkill(struct input_polled_dev *poll_dev) +static void bt_rfkill_poll(struct rfkill *rfkill, void *data) { - bool state_changed; bool new_rfk_state; bool value; u32 hci_result; - struct toshiba_acpi_dev *dev = poll_dev->private; + struct toshiba_acpi_dev *dev = data; + + mutex_lock(&dev->mutex); hci_result = hci_get_radio_state(&value); - if (hci_result != HCI_SUCCESS) - return; /* Can't do anything useful */ + if (hci_result != HCI_SUCCESS) { + /* Can't do anything useful */ + mutex_unlock(&dev->mutex); + } new_rfk_state = value; - mutex_lock(&dev->mutex); - state_changed = new_rfk_state != dev->last_rfk_state; - dev->last_rfk_state = new_rfk_state; mutex_unlock(&dev->mutex); - if (unlikely(state_changed)) { - rfkill_force_state(dev->rfk_dev, - new_rfk_state ? - RFKILL_STATE_SOFT_BLOCKED : - RFKILL_STATE_HARD_BLOCKED); - input_report_switch(poll_dev->input, SW_RFKILL_ALL, - new_rfk_state); - input_sync(poll_dev->input); - } + if (rfkill_set_hw_state(rfkill, !new_rfk_state)) + bt_rfkill_set_block(data, true); } +static const struct rfkill_ops toshiba_rfk_ops = { + .set_block = bt_rfkill_set_block, + .poll = bt_rfkill_poll, +}; + static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ; static struct backlight_device *toshiba_backlight_device; static int force_fan; @@ -702,14 +677,11 @@ static struct backlight_ops toshiba_backlight_data = { static void toshiba_acpi_exit(void) { - if (toshiba_acpi.poll_dev) { - input_unregister_polled_device(toshiba_acpi.poll_dev); - input_free_polled_device(toshiba_acpi.poll_dev); + if (toshiba_acpi.bt_rfk) { + rfkill_unregister(toshiba_acpi.bt_rfk); + rfkill_destroy(toshiba_acpi.bt_rfk); } - if (toshiba_acpi.rfk_dev) - rfkill_unregister(toshiba_acpi.rfk_dev); - if (toshiba_backlight_device) backlight_device_unregister(toshiba_backlight_device); @@ -728,8 +700,6 @@ static int __init toshiba_acpi_init(void) acpi_status status = AE_OK; u32 hci_result; bool bt_present; - bool bt_on; - bool radio_on; int ret = 0; if (acpi_disabled) @@ -793,60 +763,21 @@ static int __init toshiba_acpi_init(void) /* Register rfkill switch for Bluetooth */ if (hci_get_bt_present(&bt_present) == HCI_SUCCESS && bt_present) { - toshiba_acpi.rfk_dev = rfkill_allocate(&toshiba_acpi.p_dev->dev, - RFKILL_TYPE_BLUETOOTH); - if (!toshiba_acpi.rfk_dev) { + toshiba_acpi.bt_rfk = rfkill_alloc(toshiba_acpi.bt_name, + &toshiba_acpi.p_dev->dev, + RFKILL_TYPE_BLUETOOTH, + &toshiba_rfk_ops, + &toshiba_acpi); + if (!toshiba_acpi.bt_rfk) { printk(MY_ERR "unable to allocate rfkill device\n"); toshiba_acpi_exit(); return -ENOMEM; } - toshiba_acpi.rfk_dev->name = toshiba_acpi.bt_name; - toshiba_acpi.rfk_dev->toggle_radio = bt_rfkill_toggle_radio; - toshiba_acpi.rfk_dev->data = &toshiba_acpi; - - if (hci_get_bt_on(&bt_on) == HCI_SUCCESS && bt_on) { - toshiba_acpi.rfk_dev->state = RFKILL_STATE_UNBLOCKED; - } else if (hci_get_radio_state(&radio_on) == HCI_SUCCESS && - radio_on) { - toshiba_acpi.rfk_dev->state = RFKILL_STATE_SOFT_BLOCKED; - } else { - toshiba_acpi.rfk_dev->state = RFKILL_STATE_HARD_BLOCKED; - } - - ret = rfkill_register(toshiba_acpi.rfk_dev); + ret = rfkill_register(toshiba_acpi.bt_rfk); if (ret) { printk(MY_ERR "unable to register rfkill device\n"); - toshiba_acpi_exit(); - return -ENOMEM; - } - - /* Register input device for kill switch */ - toshiba_acpi.poll_dev = input_allocate_polled_device(); - if (!toshiba_acpi.poll_dev) { - printk(MY_ERR - "unable to allocate kill-switch input device\n"); - toshiba_acpi_exit(); - return -ENOMEM; - } - toshiba_acpi.poll_dev->private = &toshiba_acpi; - toshiba_acpi.poll_dev->poll = bt_poll_rfkill; - toshiba_acpi.poll_dev->poll_interval = 1000; /* msecs */ - - toshiba_acpi.poll_dev->input->name = toshiba_acpi.rfk_name; - toshiba_acpi.poll_dev->input->id.bustype = BUS_HOST; - /* Toshiba USB ID */ - toshiba_acpi.poll_dev->input->id.vendor = 0x0930; - set_bit(EV_SW, toshiba_acpi.poll_dev->input->evbit); - set_bit(SW_RFKILL_ALL, toshiba_acpi.poll_dev->input->swbit); - input_report_switch(toshiba_acpi.poll_dev->input, - SW_RFKILL_ALL, TRUE); - input_sync(toshiba_acpi.poll_dev->input); - - ret = input_register_polled_device(toshiba_acpi.poll_dev); - if (ret) { - printk(MY_ERR - "unable to register kill-switch input device\n"); + rfkill_destroy(toshiba_acpi.bt_rfk); toshiba_acpi_exit(); return ret; } diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 3f0eaa397ef..7e09c5c1ed0 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -311,6 +311,7 @@ unifdef-y += ptrace.h unifdef-y += qnx4_fs.h unifdef-y += quota.h unifdef-y += random.h +unifdef-y += rfkill.h unifdef-y += irqnr.h unifdef-y += reboot.h unifdef-y += reiserfs_fs.h diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index de18ef227e0..090852c8de7 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -4,6 +4,7 @@ /* * Copyright (C) 2006 - 2007 Ivo van Doorn * Copyright (C) 2007 Dmitry Torokhov + * Copyright 2009 Johannes Berg * * 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 @@ -21,6 +22,24 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* define userspace visible states */ +#define RFKILL_STATE_SOFT_BLOCKED 0 +#define RFKILL_STATE_UNBLOCKED 1 +#define RFKILL_STATE_HARD_BLOCKED 2 + +/* and that's all userspace gets */ +#ifdef __KERNEL__ +/* don't allow anyone to use these in the kernel */ +enum rfkill_user_states { + RFKILL_USER_STATE_SOFT_BLOCKED = RFKILL_STATE_SOFT_BLOCKED, + RFKILL_USER_STATE_UNBLOCKED = RFKILL_STATE_UNBLOCKED, + RFKILL_USER_STATE_HARD_BLOCKED = RFKILL_STATE_HARD_BLOCKED, +}; +#undef RFKILL_STATE_SOFT_BLOCKED +#undef RFKILL_STATE_UNBLOCKED +#undef RFKILL_STATE_HARD_BLOCKED + #include #include #include @@ -30,109 +49,267 @@ /** * enum rfkill_type - type of rfkill switch. - * RFKILL_TYPE_WLAN: switch is on a 802.11 wireless network device. - * RFKILL_TYPE_BLUETOOTH: switch is on a bluetooth device. - * RFKILL_TYPE_UWB: switch is on a ultra wideband device. - * RFKILL_TYPE_WIMAX: switch is on a WiMAX device. - * RFKILL_TYPE_WWAN: switch is on a wireless WAN device. + * + * @RFKILL_TYPE_WLAN: switch is on a 802.11 wireless network device. + * @RFKILL_TYPE_BLUETOOTH: switch is on a bluetooth device. + * @RFKILL_TYPE_UWB: switch is on a ultra wideband device. + * @RFKILL_TYPE_WIMAX: switch is on a WiMAX device. + * @RFKILL_TYPE_WWAN: switch is on a wireless WAN device. + * @NUM_RFKILL_TYPES: number of defined rfkill types */ enum rfkill_type { - RFKILL_TYPE_WLAN , + RFKILL_TYPE_WLAN, RFKILL_TYPE_BLUETOOTH, RFKILL_TYPE_UWB, RFKILL_TYPE_WIMAX, RFKILL_TYPE_WWAN, - RFKILL_TYPE_MAX, + NUM_RFKILL_TYPES, }; -enum rfkill_state { - RFKILL_STATE_SOFT_BLOCKED = 0, /* Radio output blocked */ - RFKILL_STATE_UNBLOCKED = 1, /* Radio output allowed */ - RFKILL_STATE_HARD_BLOCKED = 2, /* Output blocked, non-overrideable */ - RFKILL_STATE_MAX, /* marker for last valid state */ +/* this is opaque */ +struct rfkill; + +/** + * struct rfkill_ops - rfkill driver methods + * + * @poll: poll the rfkill block state(s) -- only assign this method + * when you need polling. When called, simply call one of the + * rfkill_set{,_hw,_sw}_state family of functions. If the hw + * is getting unblocked you need to take into account the return + * value of those functions to make sure the software block is + * properly used. + * @query: query the rfkill block state(s) and call exactly one of the + * rfkill_set{,_hw,_sw}_state family of functions. Assign this + * method if input events can cause hardware state changes to make + * the rfkill core query your driver before setting a requested + * block. + * @set_block: turn the transmitter on (blocked == false) or off + * (blocked == true) -- this is called only while the transmitter + * is not hard-blocked, but note that the core's view of whether + * the transmitter is hard-blocked might differ from your driver's + * view due to race conditions, so it is possible that it is still + * called at the same time as you are calling rfkill_set_hw_state(). + * This callback must be assigned. + */ +struct rfkill_ops { + void (*poll)(struct rfkill *rfkill, void *data); + void (*query)(struct rfkill *rfkill, void *data); + int (*set_block)(void *data, bool blocked); }; +#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) /** - * struct rfkill - rfkill control structure. - * @name: Name of the switch. - * @type: Radio type which the button controls, the value stored - * here should be a value from enum rfkill_type. - * @state: State of the switch, "UNBLOCKED" means radio can operate. - * @mutex: Guards switch state transitions. It serializes callbacks - * and also protects the state. - * @data: Pointer to the RF button drivers private data which will be - * passed along when toggling radio state. - * @toggle_radio(): Mandatory handler to control state of the radio. - * only RFKILL_STATE_SOFT_BLOCKED and RFKILL_STATE_UNBLOCKED are - * valid parameters. - * @get_state(): handler to read current radio state from hardware, - * may be called from atomic context, should return 0 on success. - * Either this handler OR judicious use of rfkill_force_state() is - * MANDATORY for any driver capable of RFKILL_STATE_HARD_BLOCKED. - * @led_trigger: A LED trigger for this button's LED. - * @dev: Device structure integrating the switch into device tree. - * @node: Used to place switch into list of all switches known to the - * the system. - * - * This structure represents a RF switch located on a network device. + * rfkill_alloc - allocate rfkill structure + * @name: name of the struct -- the string is not copied internally + * @parent: device that has rf switch on it + * @type: type of the switch (RFKILL_TYPE_*) + * @ops: rfkill methods + * @ops_data: data passed to each method + * + * This function should be called by the transmitter driver to allocate an + * rfkill structure. Returns %NULL on failure. */ -struct rfkill { - const char *name; - enum rfkill_type type; - - /* the mutex serializes callbacks and also protects - * the state */ - struct mutex mutex; - enum rfkill_state state; - void *data; - int (*toggle_radio)(void *data, enum rfkill_state state); - int (*get_state)(void *data, enum rfkill_state *state); +struct rfkill * __must_check rfkill_alloc(const char *name, + struct device *parent, + const enum rfkill_type type, + const struct rfkill_ops *ops, + void *ops_data); -#ifdef CONFIG_RFKILL_LEDS - struct led_trigger led_trigger; -#endif +/** + * rfkill_register - Register a rfkill structure. + * @rfkill: rfkill structure to be registered + * + * This function should be called by the transmitter driver to register + * the rfkill structure needs to be registered. Before calling this function + * the driver needs to be ready to service method calls from rfkill. + */ +int __must_check rfkill_register(struct rfkill *rfkill); - struct device dev; - struct list_head node; - enum rfkill_state state_for_resume; -}; -#define to_rfkill(d) container_of(d, struct rfkill, dev) +/** + * rfkill_pause_polling(struct rfkill *rfkill) + * + * Pause polling -- say transmitter is off for other reasons. + * NOTE: not necessary for suspend/resume -- in that case the + * core stops polling anyway + */ +void rfkill_pause_polling(struct rfkill *rfkill); -struct rfkill * __must_check rfkill_allocate(struct device *parent, - enum rfkill_type type); -void rfkill_free(struct rfkill *rfkill); -int __must_check rfkill_register(struct rfkill *rfkill); +/** + * rfkill_resume_polling(struct rfkill *rfkill) + * + * Pause polling -- say transmitter is off for other reasons. + * NOTE: not necessary for suspend/resume -- in that case the + * core stops polling anyway + */ +void rfkill_resume_polling(struct rfkill *rfkill); + + +/** + * rfkill_unregister - Unregister a rfkill structure. + * @rfkill: rfkill structure to be unregistered + * + * This function should be called by the network driver during device + * teardown to destroy rfkill structure. Until it returns, the driver + * needs to be able to service method calls. + */ void rfkill_unregister(struct rfkill *rfkill); -int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state); -int rfkill_set_default(enum rfkill_type type, enum rfkill_state state); +/** + * rfkill_destroy - free rfkill structure + * @rfkill: rfkill structure to be destroyed + * + * Destroys the rfkill structure. + */ +void rfkill_destroy(struct rfkill *rfkill); + +/** + * rfkill_set_hw_state - Set the internal rfkill hardware block state + * @rfkill: pointer to the rfkill class to modify. + * @state: the current hardware block state to set + * + * rfkill drivers that get events when the hard-blocked state changes + * use this function to notify the rfkill core (and through that also + * userspace) of the current state -- they should also use this after + * resume if the state could have changed. + * + * You need not (but may) call this function if poll_state is assigned. + * + * This function can be called in any context, even from within rfkill + * callbacks. + * + * The function returns the combined block state (true if transmitter + * should be blocked) so that drivers need not keep track of the soft + * block state -- which they might not be able to. + */ +bool __must_check rfkill_set_hw_state(struct rfkill *rfkill, bool blocked); + +/** + * rfkill_set_sw_state - Set the internal rfkill software block state + * @rfkill: pointer to the rfkill class to modify. + * @state: the current software block state to set + * + * rfkill drivers that get events when the soft-blocked state changes + * (yes, some platforms directly act on input but allow changing again) + * use this function to notify the rfkill core (and through that also + * userspace) of the current state -- they should also use this after + * resume if the state could have changed. + * + * This function can be called in any context, even from within rfkill + * callbacks. + * + * The function returns the combined block state (true if transmitter + * should be blocked). + */ +bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked); + +/** + * rfkill_set_states - Set the internal rfkill block states + * @rfkill: pointer to the rfkill class to modify. + * @sw: the current software block state to set + * @hw: the current hardware block state to set + * + * This function can be called in any context, even from within rfkill + * callbacks. + */ +void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw); /** - * rfkill_state_complement - return complementar state - * @state: state to return the complement of + * rfkill_set_global_sw_state - set global sw block default + * @type: rfkill type to set default for + * @blocked: default to set * - * Returns RFKILL_STATE_SOFT_BLOCKED if @state is RFKILL_STATE_UNBLOCKED, - * returns RFKILL_STATE_UNBLOCKED otherwise. + * This function sets the global default -- use at boot if your platform has + * an rfkill switch. If not early enough this call may be ignored. + * + * XXX: instead of ignoring -- how about just updating all currently + * registered drivers? */ -static inline enum rfkill_state rfkill_state_complement(enum rfkill_state state) +void rfkill_set_global_sw_state(const enum rfkill_type type, bool blocked); +#else /* !RFKILL */ +static inline struct rfkill * __must_check +rfkill_alloc(const char *name, + struct device *parent, + const enum rfkill_type type, + const struct rfkill_ops *ops, + void *ops_data) +{ + return ERR_PTR(-ENODEV); +} + +static inline int __must_check rfkill_register(struct rfkill *rfkill) +{ + if (rfkill == ERR_PTR(-ENODEV)) + return 0; + return -EINVAL; +} + +static inline void rfkill_pause_polling(struct rfkill *rfkill) +{ +} + +static inline void rfkill_resume_polling(struct rfkill *rfkill) +{ +} + +static inline void rfkill_unregister(struct rfkill *rfkill) +{ +} + +static inline void rfkill_destroy(struct rfkill *rfkill) +{ +} + +static inline bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked) +{ + return blocked; +} + +static inline bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked) +{ + return blocked; +} + +static inline void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw) +{ +} + +static inline void rfkill_set_global_sw_state(const enum rfkill_type type, + bool blocked) { - return (state == RFKILL_STATE_UNBLOCKED) ? - RFKILL_STATE_SOFT_BLOCKED : RFKILL_STATE_UNBLOCKED; } +#endif /* RFKILL || RFKILL_MODULE */ + +#ifdef CONFIG_RFKILL_LEDS /** - * rfkill_get_led_name - Get the LED trigger name for the button's LED. + * rfkill_get_led_trigger_name - Get the LED trigger name for the button's LED. * This function might return a NULL pointer if registering of the - * LED trigger failed. - * Use this as "default_trigger" for the LED. + * LED trigger failed. Use this as "default_trigger" for the LED. */ -static inline char *rfkill_get_led_name(struct rfkill *rfkill) -{ -#ifdef CONFIG_RFKILL_LEDS - return (char *)(rfkill->led_trigger.name); +const char *rfkill_get_led_trigger_name(struct rfkill *rfkill); + +/** + * rfkill_set_led_trigger_name -- set the LED trigger name + * @rfkill: rfkill struct + * @name: LED trigger name + * + * This function sets the LED trigger name of the radio LED + * trigger that rfkill creates. It is optional, but if called + * must be called before rfkill_register() to be effective. + */ +void rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name); #else +static inline const char *rfkill_get_led_trigger_name(struct rfkill *rfkill) +{ return NULL; -#endif } +static inline void +rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name) +{ +} +#endif + +#endif /* __KERNEL__ */ + #endif /* RFKILL_H */ diff --git a/include/net/wimax.h b/include/net/wimax.h index 6b3824edb39..2af7bf839f2 100644 --- a/include/net/wimax.h +++ b/include/net/wimax.h @@ -253,7 +253,6 @@ struct net_device; struct genl_info; struct wimax_dev; -struct input_dev; /** * struct wimax_dev - Generic WiMAX device @@ -293,8 +292,8 @@ struct input_dev; * See wimax_reset()'s documentation. * * @name: [fill] A way to identify this device. We need to register a - * name with many subsystems (input for RFKILL, workqueue - * creation, etc). We can't use the network device name as that + * name with many subsystems (rfkill, workqueue creation, etc). + * We can't use the network device name as that * might change and in some instances we don't know it yet (until * we don't call register_netdev()). So we generate an unique one * using the driver name and device bus id, place it here and use @@ -316,9 +315,6 @@ struct input_dev; * * @rfkill: [private] integration into the RF-Kill infrastructure. * - * @rfkill_input: [private] virtual input device to process the - * hardware RF Kill switches. - * * @rf_sw: [private] State of the software radio switch (OFF/ON) * * @rf_hw: [private] State of the hardware radio switch (OFF/ON) diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig index 7f807b30cfb..b47f72fae05 100644 --- a/net/rfkill/Kconfig +++ b/net/rfkill/Kconfig @@ -10,22 +10,15 @@ menuconfig RFKILL To compile this driver as a module, choose M here: the module will be called rfkill. -config RFKILL_INPUT - tristate "Input layer to RF switch connector" - depends on RFKILL && INPUT - help - Say Y here if you want kernel automatically toggle state - of RF switches on and off when user presses appropriate - button or a key on the keyboard. Without this module you - need a some kind of userspace application to control - state of the switches. - - To compile this driver as a module, choose M here: the - module will be called rfkill-input. - # LED trigger support config RFKILL_LEDS bool - depends on RFKILL && LEDS_TRIGGERS + depends on RFKILL + depends on LEDS_TRIGGERS = y || RFKILL = LEDS_TRIGGERS default y +config RFKILL_INPUT + bool + depends on RFKILL + depends on INPUT = y || RFKILL = INPUT + default y diff --git a/net/rfkill/Makefile b/net/rfkill/Makefile index b38c430be05..66210535269 100644 --- a/net/rfkill/Makefile +++ b/net/rfkill/Makefile @@ -2,5 +2,6 @@ # Makefile for the RF switch subsystem. # -obj-$(CONFIG_RFKILL) += rfkill.o -obj-$(CONFIG_RFKILL_INPUT) += rfkill-input.o +rfkill-y += core.o +rfkill-$(CONFIG_RFKILL_INPUT) += input.o +obj-$(CONFIG_RFKILL) += rfkill.o diff --git a/net/rfkill/core.c b/net/rfkill/core.c new file mode 100644 index 00000000000..30a6f8d819b --- /dev/null +++ b/net/rfkill/core.c @@ -0,0 +1,896 @@ +/* + * Copyright (C) 2006 - 2007 Ivo van Doorn + * Copyright (C) 2007 Dmitry Torokhov + * Copyright 2009 Johannes Berg + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rfkill.h" + +#define POLL_INTERVAL (5 * HZ) + +#define RFKILL_BLOCK_HW BIT(0) +#define RFKILL_BLOCK_SW BIT(1) +#define RFKILL_BLOCK_SW_PREV BIT(2) +#define RFKILL_BLOCK_ANY (RFKILL_BLOCK_HW |\ + RFKILL_BLOCK_SW |\ + RFKILL_BLOCK_SW_PREV) +#define RFKILL_BLOCK_SW_SETCALL BIT(31) + +struct rfkill { + spinlock_t lock; + + const char *name; + enum rfkill_type type; + + unsigned long state; + + bool registered; + bool suspended; + + const struct rfkill_ops *ops; + void *data; + +#ifdef CONFIG_RFKILL_LEDS + struct led_trigger led_trigger; + const char *ledtrigname; +#endif + + struct device dev; + struct list_head node; + + struct delayed_work poll_work; + struct work_struct uevent_work; + struct work_struct sync_work; +}; +#define to_rfkill(d) container_of(d, struct rfkill, dev) + + + +MODULE_AUTHOR("Ivo van Doorn "); +MODULE_AUTHOR("Johannes Berg "); +MODULE_DESCRIPTION("RF switch support"); +MODULE_LICENSE("GPL"); + + +/* + * The locking here should be made much smarter, we currently have + * a bit of a stupid situation because drivers might want to register + * the rfkill struct under their own lock, and take this lock during + * rfkill method calls -- which will cause an AB-BA deadlock situation. + * + * To fix that, we need to rework this code here to be mostly lock-free + * and only use the mutex for list manipulations, not to protect the + * various other global variables. Then we can avoid holding the mutex + * around driver operations, and all is happy. + */ +static LIST_HEAD(rfkill_list); /* list of registered rf switches */ +static DEFINE_MUTEX(rfkill_global_mutex); + +static unsigned int rfkill_default_state = 1; +module_param_named(default_state, rfkill_default_state, uint, 0444); +MODULE_PARM_DESC(default_state, + "Default initial state for all radio types, 0 = radio off"); + +static struct { + bool cur, def; +} rfkill_global_states[NUM_RFKILL_TYPES]; + +static unsigned long rfkill_states_default_locked; + +static bool rfkill_epo_lock_active; + + +#ifdef CONFIG_RFKILL_LEDS +static void rfkill_led_trigger_event(struct rfkill *rfkill) +{ + struct led_trigger *trigger; + + if (!rfkill->registered) + return; + + trigger = &rfkill->led_trigger; + + if (rfkill->state & RFKILL_BLOCK_ANY) + led_trigger_event(trigger, LED_OFF); + else + led_trigger_event(trigger, LED_FULL); +} + +static void rfkill_led_trigger_activate(struct led_classdev *led) +{ + struct rfkill *rfkill; + + rfkill = container_of(led->trigger, struct rfkill, led_trigger); + + rfkill_led_trigger_event(rfkill); +} + +const char *rfkill_get_led_trigger_name(struct rfkill *rfkill) +{ + return rfkill->led_trigger.name; +} +EXPORT_SYMBOL(rfkill_get_led_trigger_name); + +void rfkill_set_led_trigger_name(struct rfkill *rfkill, const char *name) +{ + BUG_ON(!rfkill); + + rfkill->ledtrigname = name; +} +EXPORT_SYMBOL(rfkill_set_led_trigger_name); + +static int rfkill_led_trigger_register(struct rfkill *rfkill) +{ + rfkill->led_trigger.name = rfkill->ledtrigname + ? : dev_name(&rfkill->dev); + rfkill->led_trigger.activate = rfkill_led_trigger_activate; + return led_trigger_register(&rfkill->led_trigger); +} + +static void rfkill_led_trigger_unregister(struct rfkill *rfkill) +{ + led_trigger_unregister(&rfkill->led_trigger); +} +#else +static void rfkill_led_trigger_event(struct rfkill *rfkill) +{ +} + +static inline int rfkill_led_trigger_register(struct rfkill *rfkill) +{ + return 0; +} + +static inline void rfkill_led_trigger_unregister(struct rfkill *rfkill) +{ +} +#endif /* CONFIG_RFKILL_LEDS */ + +static void rfkill_uevent(struct rfkill *rfkill) +{ + if (!rfkill->registered || rfkill->suspended) + return; + + kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE); +} + +static bool __rfkill_set_hw_state(struct rfkill *rfkill, + bool blocked, bool *change) +{ + unsigned long flags; + bool prev, any; + + BUG_ON(!rfkill); + + spin_lock_irqsave(&rfkill->lock, flags); + prev = !!(rfkill->state & RFKILL_BLOCK_HW); + if (blocked) + rfkill->state |= RFKILL_BLOCK_HW; + else + rfkill->state &= ~RFKILL_BLOCK_HW; + *change = prev != blocked; + any = rfkill->state & RFKILL_BLOCK_ANY; + spin_unlock_irqrestore(&rfkill->lock, flags); + + rfkill_led_trigger_event(rfkill); + + return any; +} + +/** + * rfkill_set_block - wrapper for set_block method + * + * @rfkill: the rfkill struct to use + * @blocked: the new software state + * + * Calls the set_block method (when applicable) and handles notifications + * etc. as well. + */ +static void rfkill_set_block(struct rfkill *rfkill, bool blocked) +{ + unsigned long flags; + int err; + + /* + * Some platforms (...!) generate input events which affect the + * _hard_ kill state -- whenever something tries to change the + * current software state query the hardware state too. + */ + if (rfkill->ops->query) + rfkill->ops->query(rfkill, rfkill->data); + + spin_lock_irqsave(&rfkill->lock, flags); + if (rfkill->state & RFKILL_BLOCK_SW) + rfkill->state |= RFKILL_BLOCK_SW_PREV; + else + rfkill->state &= ~RFKILL_BLOCK_SW_PREV; + + if (blocked) + rfkill->state |= RFKILL_BLOCK_SW; + else + rfkill->state &= ~RFKILL_BLOCK_SW; + + rfkill->state |= RFKILL_BLOCK_SW_SETCALL; + spin_unlock_irqrestore(&rfkill->lock, flags); + + if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP)) + return; + + err = rfkill->ops->set_block(rfkill->data, blocked); + + spin_lock_irqsave(&rfkill->lock, flags); + if (err) { + /* + * Failed -- reset status to _prev, this may be different + * from what set set _PREV to earlier in this function + * if rfkill_set_sw_state was invoked. + */ + if (rfkill->state & RFKILL_BLOCK_SW_PREV) + rfkill->state |= RFKILL_BLOCK_SW; + else + rfkill->state &= ~RFKILL_BLOCK_SW; + } + rfkill->state &= ~RFKILL_BLOCK_SW_SETCALL; + rfkill->state &= ~RFKILL_BLOCK_SW_PREV; + spin_unlock_irqrestore(&rfkill->lock, flags); + + rfkill_led_trigger_event(rfkill); + rfkill_uevent(rfkill); +} + +/** + * __rfkill_switch_all - Toggle state of all switches of given type + * @type: type of interfaces to be affected + * @state: the new state + * + * This function sets the state of all switches of given type, + * unless a specific switch is claimed by userspace (in which case, + * that switch is left alone) or suspended. + * + * Caller must have acquired rfkill_global_mutex. + */ +static void __rfkill_switch_all(const enum rfkill_type type, bool blocked) +{ + struct rfkill *rfkill; + + rfkill_global_states[type].cur = blocked; + list_for_each_entry(rfkill, &rfkill_list, node) { + if (rfkill->type != type) + continue; + + rfkill_set_block(rfkill, blocked); + } +} + +/** + * rfkill_switch_all - Toggle state of all switches of given type + * @type: type of interfaces to be affected + * @state: the new state + * + * Acquires rfkill_global_mutex and calls __rfkill_switch_all(@type, @state). + * Please refer to __rfkill_switch_all() for details. + * + * Does nothing if the EPO lock is active. + */ +void rfkill_switch_all(enum rfkill_type type, bool blocked) +{ + mutex_lock(&rfkill_global_mutex); + + if (!rfkill_epo_lock_active) + __rfkill_switch_all(type, blocked); + + mutex_unlock(&rfkill_global_mutex); +} + +/** + * rfkill_epo - emergency power off all transmitters + * + * This kicks all non-suspended rfkill devices to RFKILL_STATE_SOFT_BLOCKED, + * ignoring everything in its path but rfkill_global_mutex and rfkill->mutex. + * + * The global state before the EPO is saved and can be restored later + * using rfkill_restore_states(). + */ +void rfkill_epo(void) +{ + struct rfkill *rfkill; + int i; + + mutex_lock(&rfkill_global_mutex); + + rfkill_epo_lock_active = true; + list_for_each_entry(rfkill, &rfkill_list, node) + rfkill_set_block(rfkill, true); + + for (i = 0; i < NUM_RFKILL_TYPES; i++) { + rfkill_global_states[i].def = rfkill_global_states[i].cur; + rfkill_global_states[i].cur = true; + } + mutex_unlock(&rfkill_global_mutex); +} + +/** + * rfkill_restore_states - restore global states + * + * Restore (and sync switches to) the global state from the + * states in rfkill_default_states. This can undo the effects of + * a call to rfkill_epo(). + */ +void rfkill_restore_states(void) +{ + int i; + + mutex_lock(&rfkill_global_mutex); + + rfkill_epo_lock_active = false; + for (i = 0; i < NUM_RFKILL_TYPES; i++) + __rfkill_switch_all(i, rfkill_global_states[i].def); + mutex_unlock(&rfkill_global_mutex); +} + +/** + * rfkill_remove_epo_lock - unlock state changes + * + * Used by rfkill-input manually unlock state changes, when + * the EPO switch is deactivated. + */ +void rfkill_remove_epo_lock(void) +{ + mutex_lock(&rfkill_global_mutex); + rfkill_epo_lock_active = false; + mutex_unlock(&rfkill_global_mutex); +} + +/** + * rfkill_is_epo_lock_active - returns true EPO is active + * + * Returns 0 (false) if there is NOT an active EPO contidion, + * and 1 (true) if there is an active EPO contition, which + * locks all radios in one of the BLOCKED states. + * + * Can be called in atomic context. + */ +bool rfkill_is_epo_lock_active(void) +{ + return rfkill_epo_lock_active; +} + +/** + * rfkill_get_global_sw_state - returns global state for a type + * @type: the type to get the global state of + * + * Returns the current global state for a given wireless + * device type. + */ +bool rfkill_get_global_sw_state(const enum rfkill_type type) +{ + return rfkill_global_states[type].cur; +} + +void rfkill_set_global_sw_state(const enum rfkill_type type, bool blocked) +{ + mutex_lock(&rfkill_global_mutex); + + /* don't allow unblock when epo */ + if (rfkill_epo_lock_active && !blocked) + goto out; + + /* too late */ + if (rfkill_states_default_locked & BIT(type)) + goto out; + + rfkill_states_default_locked |= BIT(type); + + rfkill_global_states[type].cur = blocked; + rfkill_global_states[type].def = blocked; + out: + mutex_unlock(&rfkill_global_mutex); +} +EXPORT_SYMBOL(rfkill_set_global_sw_state); + + +bool rfkill_set_hw_state(struct rfkill *rfkill, bool blocked) +{ + bool ret, change; + + ret = __rfkill_set_hw_state(rfkill, blocked, &change); + + if (!rfkill->registered) + return ret; + + if (change) + schedule_work(&rfkill->uevent_work); + + return ret; +} +EXPORT_SYMBOL(rfkill_set_hw_state); + +static void __rfkill_set_sw_state(struct rfkill *rfkill, bool blocked) +{ + u32 bit = RFKILL_BLOCK_SW; + + /* if in a ops->set_block right now, use other bit */ + if (rfkill->state & RFKILL_BLOCK_SW_SETCALL) + bit = RFKILL_BLOCK_SW_PREV; + + if (blocked) + rfkill->state |= bit; + else + rfkill->state &= ~bit; +} + +bool rfkill_set_sw_state(struct rfkill *rfkill, bool blocked) +{ + unsigned long flags; + bool prev, hwblock; + + BUG_ON(!rfkill); + + spin_lock_irqsave(&rfkill->lock, flags); + prev = !!(rfkill->state & RFKILL_BLOCK_SW); + __rfkill_set_sw_state(rfkill, blocked); + hwblock = !!(rfkill->state & RFKILL_BLOCK_HW); + blocked = blocked || hwblock; + spin_unlock_irqrestore(&rfkill->lock, flags); + + if (!rfkill->registered) + return blocked; + + if (prev != blocked && !hwblock) + schedule_work(&rfkill->uevent_work); + + rfkill_led_trigger_event(rfkill); + + return blocked; +} +EXPORT_SYMBOL(rfkill_set_sw_state); + +void rfkill_set_states(struct rfkill *rfkill, bool sw, bool hw) +{ + unsigned long flags; + bool swprev, hwprev; + + BUG_ON(!rfkill); + + spin_lock_irqsave(&rfkill->lock, flags); + + /* + * No need to care about prev/setblock ... this is for uevent only + * and that will get triggered by rfkill_set_block anyway. + */ + swprev = !!(rfkill->state & RFKILL_BLOCK_SW); + hwprev = !!(rfkill->state & RFKILL_BLOCK_HW); + __rfkill_set_sw_state(rfkill, sw); + + spin_unlock_irqrestore(&rfkill->lock, flags); + + if (!rfkill->registered) + return; + + if (swprev != sw || hwprev != hw) + schedule_work(&rfkill->uevent_work); + + rfkill_led_trigger_event(rfkill); +} +EXPORT_SYMBOL(rfkill_set_states); + +static ssize_t rfkill_name_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rfkill *rfkill = to_rfkill(dev); + + return sprintf(buf, "%s\n", rfkill->name); +} + +static const char *rfkill_get_type_str(enum rfkill_type type) +{ + switch (type) { + case RFKILL_TYPE_WLAN: + return "wlan"; + case RFKILL_TYPE_BLUETOOTH: + return "bluetooth"; + case RFKILL_TYPE_UWB: + return "ultrawideband"; + case RFKILL_TYPE_WIMAX: + return "wimax"; + case RFKILL_TYPE_WWAN: + return "wwan"; + default: + BUG(); + } + + BUILD_BUG_ON(NUM_RFKILL_TYPES != RFKILL_TYPE_WWAN + 1); +} + +static ssize_t rfkill_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rfkill *rfkill = to_rfkill(dev); + + return sprintf(buf, "%s\n", rfkill_get_type_str(rfkill->type)); +} + +static u8 user_state_from_blocked(unsigned long state) +{ + if (state & RFKILL_BLOCK_HW) + return RFKILL_USER_STATE_HARD_BLOCKED; + if (state & RFKILL_BLOCK_SW) + return RFKILL_USER_STATE_SOFT_BLOCKED; + + return RFKILL_USER_STATE_UNBLOCKED; +} + +static ssize_t rfkill_state_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rfkill *rfkill = to_rfkill(dev); + unsigned long flags; + u32 state; + + spin_lock_irqsave(&rfkill->lock, flags); + state = rfkill->state; + spin_unlock_irqrestore(&rfkill->lock, flags); + + return sprintf(buf, "%d\n", user_state_from_blocked(state)); +} + +static ssize_t rfkill_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + /* + * The intention was that userspace can only take control over + * a given device when/if rfkill-input doesn't control it due + * to user_claim. Since user_claim is currently unsupported, + * we never support changing the state from userspace -- this + * can be implemented again later. + */ + + return -EPERM; +} + +static ssize_t rfkill_claim_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", 0); +} + +static ssize_t rfkill_claim_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return -EOPNOTSUPP; +} + +static struct device_attribute rfkill_dev_attrs[] = { + __ATTR(name, S_IRUGO, rfkill_name_show, NULL), + __ATTR(type, S_IRUGO, rfkill_type_show, NULL), + __ATTR(state, S_IRUGO|S_IWUSR, rfkill_state_show, rfkill_state_store), + __ATTR(claim, S_IRUGO|S_IWUSR, rfkill_claim_show, rfkill_claim_store), + __ATTR_NULL +}; + +static void rfkill_release(struct device *dev) +{ + struct rfkill *rfkill = to_rfkill(dev); + + kfree(rfkill); +} + +static int rfkill_dev_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct rfkill *rfkill = to_rfkill(dev); + unsigned long flags; + u32 state; + int error; + + error = add_uevent_var(env, "RFKILL_NAME=%s", rfkill->name); + if (error) + return error; + error = add_uevent_var(env, "RFKILL_TYPE=%s", + rfkill_get_type_str(rfkill->type)); + if (error) + return error; + spin_lock_irqsave(&rfkill->lock, flags); + state = rfkill->state; + spin_unlock_irqrestore(&rfkill->lock, flags); + error = add_uevent_var(env, "RFKILL_STATE=%d", + user_state_from_blocked(state)); + return error; +} + +void rfkill_pause_polling(struct rfkill *rfkill) +{ + BUG_ON(!rfkill); + + if (!rfkill->ops->poll) + return; + + cancel_delayed_work_sync(&rfkill->poll_work); +} +EXPORT_SYMBOL(rfkill_pause_polling); + +void rfkill_resume_polling(struct rfkill *rfkill) +{ + BUG_ON(!rfkill); + + if (!rfkill->ops->poll) + return; + + schedule_work(&rfkill->poll_work.work); +} +EXPORT_SYMBOL(rfkill_resume_polling); + +static int rfkill_suspend(struct device *dev, pm_message_t state) +{ + struct rfkill *rfkill = to_rfkill(dev); + + rfkill_pause_polling(rfkill); + + rfkill->suspended = true; + + return 0; +} + +static int rfkill_resume(struct device *dev) +{ + struct rfkill *rfkill = to_rfkill(dev); + bool cur; + + mutex_lock(&rfkill_global_mutex); + cur = rfkill_global_states[rfkill->type].cur; + rfkill_set_block(rfkill, cur); + mutex_unlock(&rfkill_global_mutex); + + rfkill->suspended = false; + + schedule_work(&rfkill->uevent_work); + + rfkill_resume_polling(rfkill); + + return 0; +} + +static struct class rfkill_class = { + .name = "rfkill", + .dev_release = rfkill_release, + .dev_attrs = rfkill_dev_attrs, + .dev_uevent = rfkill_dev_uevent, + .suspend = rfkill_suspend, + .resume = rfkill_resume, +}; + + +struct rfkill * __must_check rfkill_alloc(const char *name, + struct device *parent, + const enum rfkill_type type, + const struct rfkill_ops *ops, + void *ops_data) +{ + struct rfkill *rfkill; + struct device *dev; + + if (WARN_ON(!ops)) + return NULL; + + if (WARN_ON(!ops->set_block)) + return NULL; + + if (WARN_ON(!name)) + return NULL; + + if (WARN_ON(type >= NUM_RFKILL_TYPES)) + return NULL; + + rfkill = kzalloc(sizeof(*rfkill), GFP_KERNEL); + if (!rfkill) + return NULL; + + spin_lock_init(&rfkill->lock); + INIT_LIST_HEAD(&rfkill->node); + rfkill->type = type; + rfkill->name = name; + rfkill->ops = ops; + rfkill->data = ops_data; + + dev = &rfkill->dev; + dev->class = &rfkill_class; + dev->parent = parent; + device_initialize(dev); + + return rfkill; +} +EXPORT_SYMBOL(rfkill_alloc); + +static void rfkill_poll(struct work_struct *work) +{ + struct rfkill *rfkill; + + rfkill = container_of(work, struct rfkill, poll_work.work); + + /* + * Poll hardware state -- driver will use one of the + * rfkill_set{,_hw,_sw}_state functions and use its + * return value to update the current status. + */ + rfkill->ops->poll(rfkill, rfkill->data); + + schedule_delayed_work(&rfkill->poll_work, + round_jiffies_relative(POLL_INTERVAL)); +} + +static void rfkill_uevent_work(struct work_struct *work) +{ + struct rfkill *rfkill; + + rfkill = container_of(work, struct rfkill, uevent_work); + + rfkill_uevent(rfkill); +} + +static void rfkill_sync_work(struct work_struct *work) +{ + struct rfkill *rfkill; + bool cur; + + rfkill = container_of(work, struct rfkill, sync_work); + + mutex_lock(&rfkill_global_mutex); + cur = rfkill_global_states[rfkill->type].cur; + rfkill_set_block(rfkill, cur); + mutex_unlock(&rfkill_global_mutex); +} + +int __must_check rfkill_register(struct rfkill *rfkill) +{ + static unsigned long rfkill_no; + struct device *dev = &rfkill->dev; + int error; + + BUG_ON(!rfkill); + + mutex_lock(&rfkill_global_mutex); + + if (rfkill->registered) { + error = -EALREADY; + goto unlock; + } + + dev_set_name(dev, "rfkill%lu", rfkill_no); + rfkill_no++; + + if (!(rfkill_states_default_locked & BIT(rfkill->type))) { + /* first of its kind */ + BUILD_BUG_ON(NUM_RFKILL_TYPES > + sizeof(rfkill_states_default_locked) * 8); + rfkill_states_default_locked |= BIT(rfkill->type); + rfkill_global_states[rfkill->type].cur = + rfkill_global_states[rfkill->type].def; + } + + list_add_tail(&rfkill->node, &rfkill_list); + + error = device_add(dev); + if (error) + goto remove; + + error = rfkill_led_trigger_register(rfkill); + if (error) + goto devdel; + + rfkill->registered = true; + + if (rfkill->ops->poll) { + INIT_DELAYED_WORK(&rfkill->poll_work, rfkill_poll); + schedule_delayed_work(&rfkill->poll_work, + round_jiffies_relative(POLL_INTERVAL)); + } + + INIT_WORK(&rfkill->uevent_work, rfkill_uevent_work); + + INIT_WORK(&rfkill->sync_work, rfkill_sync_work); + schedule_work(&rfkill->sync_work); + + mutex_unlock(&rfkill_global_mutex); + return 0; + + devdel: + device_del(&rfkill->dev); + remove: + list_del_init(&rfkill->node); + unlock: + mutex_unlock(&rfkill_global_mutex); + return error; +} +EXPORT_SYMBOL(rfkill_register); + +void rfkill_unregister(struct rfkill *rfkill) +{ + BUG_ON(!rfkill); + + if (rfkill->ops->poll) + cancel_delayed_work_sync(&rfkill->poll_work); + + cancel_work_sync(&rfkill->uevent_work); + cancel_work_sync(&rfkill->sync_work); + + rfkill->registered = false; + + device_del(&rfkill->dev); + + mutex_lock(&rfkill_global_mutex); + list_del_init(&rfkill->node); + mutex_unlock(&rfkill_global_mutex); + + rfkill_led_trigger_unregister(rfkill); +} +EXPORT_SYMBOL(rfkill_unregister); + +void rfkill_destroy(struct rfkill *rfkill) +{ + if (rfkill) + put_device(&rfkill->dev); +} +EXPORT_SYMBOL(rfkill_destroy); + + +static int __init rfkill_init(void) +{ + int error; + int i; + + for (i = 0; i < NUM_RFKILL_TYPES; i++) + rfkill_global_states[i].def = !rfkill_default_state; + + error = class_register(&rfkill_class); + if (error) + goto out; + +#ifdef CONFIG_RFKILL_INPUT + error = rfkill_handler_init(); + if (error) + class_unregister(&rfkill_class); +#endif + + out: + return error; +} +subsys_initcall(rfkill_init); + +static void __exit rfkill_exit(void) +{ +#ifdef CONFIG_RFKILL_INPUT + rfkill_handler_exit(); +#endif + class_unregister(&rfkill_class); +} +module_exit(rfkill_exit); diff --git a/net/rfkill/input.c b/net/rfkill/input.c new file mode 100644 index 00000000000..a7295ad5f9c --- /dev/null +++ b/net/rfkill/input.c @@ -0,0 +1,342 @@ +/* + * Input layer to RF Kill interface connector + * + * Copyright (c) 2007 Dmitry Torokhov + * Copyright 2009 Johannes Berg + * + * 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. + * + * If you ever run into a situation in which you have a SW_ type rfkill + * input device, then you can revive code that was removed in the patch + * "rfkill-input: remove unused code". + */ + +#include +#include +#include +#include +#include +#include + +#include "rfkill.h" + +enum rfkill_input_master_mode { + RFKILL_INPUT_MASTER_UNLOCK = 0, + RFKILL_INPUT_MASTER_RESTORE = 1, + RFKILL_INPUT_MASTER_UNBLOCKALL = 2, + NUM_RFKILL_INPUT_MASTER_MODES +}; + +/* Delay (in ms) between consecutive switch ops */ +#define RFKILL_OPS_DELAY 200 + +static enum rfkill_input_master_mode rfkill_master_switch_mode = + RFKILL_INPUT_MASTER_UNBLOCKALL; +module_param_named(master_switch_mode, rfkill_master_switch_mode, uint, 0); +MODULE_PARM_DESC(master_switch_mode, + "SW_RFKILL_ALL ON should: 0=do nothing (only unlock); 1=restore; 2=unblock all"); + +static spinlock_t rfkill_op_lock; +static bool rfkill_op_pending; +static unsigned long rfkill_sw_pending[BITS_TO_LONGS(NUM_RFKILL_TYPES)]; +static unsigned long rfkill_sw_state[BITS_TO_LONGS(NUM_RFKILL_TYPES)]; + +enum rfkill_sched_op { + RFKILL_GLOBAL_OP_EPO = 0, + RFKILL_GLOBAL_OP_RESTORE, + RFKILL_GLOBAL_OP_UNLOCK, + RFKILL_GLOBAL_OP_UNBLOCK, +}; + +static enum rfkill_sched_op rfkill_master_switch_op; +static enum rfkill_sched_op rfkill_op; + +static void __rfkill_handle_global_op(enum rfkill_sched_op op) +{ + unsigned int i; + + switch (op) { + case RFKILL_GLOBAL_OP_EPO: + rfkill_epo(); + break; + case RFKILL_GLOBAL_OP_RESTORE: + rfkill_restore_states(); + break; + case RFKILL_GLOBAL_OP_UNLOCK: + rfkill_remove_epo_lock(); + break; + case RFKILL_GLOBAL_OP_UNBLOCK: + rfkill_remove_epo_lock(); + for (i = 0; i < NUM_RFKILL_TYPES; i++) + rfkill_switch_all(i, false); + break; + default: + /* memory corruption or bug, fail safely */ + rfkill_epo(); + WARN(1, "Unknown requested operation %d! " + "rfkill Emergency Power Off activated\n", + op); + } +} + +static void __rfkill_handle_normal_op(const enum rfkill_type type, + const bool complement) +{ + bool blocked; + + blocked = rfkill_get_global_sw_state(type); + if (complement) + blocked = !blocked; + + rfkill_switch_all(type, blocked); +} + +static void rfkill_op_handler(struct work_struct *work) +{ + unsigned int i; + bool c; + + spin_lock_irq(&rfkill_op_lock); + do { + if (rfkill_op_pending) { + enum rfkill_sched_op op = rfkill_op; + rfkill_op_pending = false; + memset(rfkill_sw_pending, 0, + sizeof(rfkill_sw_pending)); + spin_unlock_irq(&rfkill_op_lock); + + __rfkill_handle_global_op(op); + + spin_lock_irq(&rfkill_op_lock); + + /* + * handle global ops first -- during unlocked period + * we might have gotten a new global op. + */ + if (rfkill_op_pending) + continue; + } + + if (rfkill_is_epo_lock_active()) + continue; + + for (i = 0; i < NUM_RFKILL_TYPES; i++) { + if (__test_and_clear_bit(i, rfkill_sw_pending)) { + c = __test_and_clear_bit(i, rfkill_sw_state); + spin_unlock_irq(&rfkill_op_lock); + + __rfkill_handle_normal_op(i, c); + + spin_lock_irq(&rfkill_op_lock); + } + } + } while (rfkill_op_pending); + spin_unlock_irq(&rfkill_op_lock); +} + +static DECLARE_DELAYED_WORK(rfkill_op_work, rfkill_op_handler); +static unsigned long rfkill_last_scheduled; + +static unsigned long rfkill_ratelimit(const unsigned long last) +{ + const unsigned long delay = msecs_to_jiffies(RFKILL_OPS_DELAY); + return (time_after(jiffies, last + delay)) ? 0 : delay; +} + +static void rfkill_schedule_ratelimited(void) +{ + if (delayed_work_pending(&rfkill_op_work)) + return; + schedule_delayed_work(&rfkill_op_work, + rfkill_ratelimit(rfkill_last_scheduled)); + rfkill_last_scheduled = jiffies; +} + +static void rfkill_schedule_global_op(enum rfkill_sched_op op) +{ + unsigned long flags; + + spin_lock_irqsave(&rfkill_op_lock, flags); + rfkill_op = op; + rfkill_op_pending = true; + if (op == RFKILL_GLOBAL_OP_EPO && !rfkill_is_epo_lock_active()) { + /* bypass the limiter for EPO */ + cancel_delayed_work(&rfkill_op_work); + schedule_delayed_work(&rfkill_op_work, 0); + rfkill_last_scheduled = jiffies; + } else + rfkill_schedule_ratelimited(); + spin_unlock_irqrestore(&rfkill_op_lock, flags); +} + +static void rfkill_schedule_toggle(enum rfkill_type type) +{ + unsigned long flags; + + if (rfkill_is_epo_lock_active()) + return; + + spin_lock_irqsave(&rfkill_op_lock, flags); + if (!rfkill_op_pending) { + __set_bit(type, rfkill_sw_pending); + __change_bit(type, rfkill_sw_state); + rfkill_schedule_ratelimited(); + } + spin_unlock_irqrestore(&rfkill_op_lock, flags); +} + +static void rfkill_schedule_evsw_rfkillall(int state) +{ + if (state) + rfkill_schedule_global_op(rfkill_master_switch_op); + else + rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO); +} + +static void rfkill_event(struct input_handle *handle, unsigned int type, + unsigned int code, int data) +{ + if (type == EV_KEY && data == 1) { + switch (code) { + case KEY_WLAN: + rfkill_schedule_toggle(RFKILL_TYPE_WLAN); + break; + case KEY_BLUETOOTH: + rfkill_schedule_toggle(RFKILL_TYPE_BLUETOOTH); + break; + case KEY_UWB: + rfkill_schedule_toggle(RFKILL_TYPE_UWB); + break; + case KEY_WIMAX: + rfkill_schedule_toggle(RFKILL_TYPE_WIMAX); + break; + } + } else if (type == EV_SW && code == SW_RFKILL_ALL) + rfkill_schedule_evsw_rfkillall(data); +} + +static int rfkill_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) +{ + struct input_handle *handle; + int error; + + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "rfkill"; + + /* causes rfkill_start() to be called */ + error = input_register_handle(handle); + if (error) + goto err_free_handle; + + error = input_open_device(handle); + if (error) + goto err_unregister_handle; + + return 0; + + err_unregister_handle: + input_unregister_handle(handle); + err_free_handle: + kfree(handle); + return error; +} + +static void rfkill_start(struct input_handle *handle) +{ + /* + * Take event_lock to guard against configuration changes, we + * should be able to deal with concurrency with rfkill_event() + * just fine (which event_lock will also avoid). + */ + spin_lock_irq(&handle->dev->event_lock); + + if (test_bit(EV_SW, handle->dev->evbit) && + test_bit(SW_RFKILL_ALL, handle->dev->swbit)) + rfkill_schedule_evsw_rfkillall(test_bit(SW_RFKILL_ALL, + handle->dev->sw)); + + spin_unlock_irq(&handle->dev->event_lock); +} + +static void rfkill_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id rfkill_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, + .evbit = { BIT_MASK(EV_KEY) }, + .keybit = { [BIT_WORD(KEY_WLAN)] = BIT_MASK(KEY_WLAN) }, + }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, + .evbit = { BIT_MASK(EV_KEY) }, + .keybit = { [BIT_WORD(KEY_BLUETOOTH)] = BIT_MASK(KEY_BLUETOOTH) }, + }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, + .evbit = { BIT_MASK(EV_KEY) }, + .keybit = { [BIT_WORD(KEY_UWB)] = BIT_MASK(KEY_UWB) }, + }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, + .evbit = { BIT_MASK(EV_KEY) }, + .keybit = { [BIT_WORD(KEY_WIMAX)] = BIT_MASK(KEY_WIMAX) }, + }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_SWBIT, + .evbit = { BIT(EV_SW) }, + .swbit = { [BIT_WORD(SW_RFKILL_ALL)] = BIT_MASK(SW_RFKILL_ALL) }, + }, + { } +}; + +static struct input_handler rfkill_handler = { + .name = "rfkill", + .event = rfkill_event, + .connect = rfkill_connect, + .start = rfkill_start, + .disconnect = rfkill_disconnect, + .id_table = rfkill_ids, +}; + +int __init rfkill_handler_init(void) +{ + switch (rfkill_master_switch_mode) { + case RFKILL_INPUT_MASTER_UNBLOCKALL: + rfkill_master_switch_op = RFKILL_GLOBAL_OP_UNBLOCK; + break; + case RFKILL_INPUT_MASTER_RESTORE: + rfkill_master_switch_op = RFKILL_GLOBAL_OP_RESTORE; + break; + case RFKILL_INPUT_MASTER_UNLOCK: + rfkill_master_switch_op = RFKILL_GLOBAL_OP_UNLOCK; + break; + default: + return -EINVAL; + } + + spin_lock_init(&rfkill_op_lock); + + /* Avoid delay at first schedule */ + rfkill_last_scheduled = + jiffies - msecs_to_jiffies(RFKILL_OPS_DELAY) - 1; + return input_register_handler(&rfkill_handler); +} + +void __exit rfkill_handler_exit(void) +{ + input_unregister_handler(&rfkill_handler); + cancel_delayed_work_sync(&rfkill_op_work); +} diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c deleted file mode 100644 index 60a34f3b5f6..00000000000 --- a/net/rfkill/rfkill-input.c +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Input layer to RF Kill interface connector - * - * Copyright (c) 2007 Dmitry Torokhov - */ - -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "rfkill-input.h" - -MODULE_AUTHOR("Dmitry Torokhov "); -MODULE_DESCRIPTION("Input layer to RF switch connector"); -MODULE_LICENSE("GPL"); - -enum rfkill_input_master_mode { - RFKILL_INPUT_MASTER_DONOTHING = 0, - RFKILL_INPUT_MASTER_RESTORE = 1, - RFKILL_INPUT_MASTER_UNBLOCKALL = 2, - RFKILL_INPUT_MASTER_MAX, /* marker */ -}; - -/* Delay (in ms) between consecutive switch ops */ -#define RFKILL_OPS_DELAY 200 - -static enum rfkill_input_master_mode rfkill_master_switch_mode = - RFKILL_INPUT_MASTER_UNBLOCKALL; -module_param_named(master_switch_mode, rfkill_master_switch_mode, uint, 0); -MODULE_PARM_DESC(master_switch_mode, - "SW_RFKILL_ALL ON should: 0=do nothing; 1=restore; 2=unblock all"); - -enum rfkill_global_sched_op { - RFKILL_GLOBAL_OP_EPO = 0, - RFKILL_GLOBAL_OP_RESTORE, - RFKILL_GLOBAL_OP_UNLOCK, - RFKILL_GLOBAL_OP_UNBLOCK, -}; - -struct rfkill_task { - struct delayed_work dwork; - - /* ensures that task is serialized */ - struct mutex mutex; - - /* protects everything below */ - spinlock_t lock; - - /* pending regular switch operations (1=pending) */ - unsigned long sw_pending[BITS_TO_LONGS(RFKILL_TYPE_MAX)]; - - /* should the state be complemented (1=yes) */ - unsigned long sw_togglestate[BITS_TO_LONGS(RFKILL_TYPE_MAX)]; - - bool global_op_pending; - enum rfkill_global_sched_op op; - - /* last time it was scheduled */ - unsigned long last_scheduled; -}; - -static void __rfkill_handle_global_op(enum rfkill_global_sched_op op) -{ - unsigned int i; - - switch (op) { - case RFKILL_GLOBAL_OP_EPO: - rfkill_epo(); - break; - case RFKILL_GLOBAL_OP_RESTORE: - rfkill_restore_states(); - break; - case RFKILL_GLOBAL_OP_UNLOCK: - rfkill_remove_epo_lock(); - break; - case RFKILL_GLOBAL_OP_UNBLOCK: - rfkill_remove_epo_lock(); - for (i = 0; i < RFKILL_TYPE_MAX; i++) - rfkill_switch_all(i, RFKILL_STATE_UNBLOCKED); - break; - default: - /* memory corruption or bug, fail safely */ - rfkill_epo(); - WARN(1, "Unknown requested operation %d! " - "rfkill Emergency Power Off activated\n", - op); - } -} - -static void __rfkill_handle_normal_op(const enum rfkill_type type, - const bool c) -{ - enum rfkill_state state; - - state = rfkill_get_global_state(type); - if (c) - state = rfkill_state_complement(state); - - rfkill_switch_all(type, state); -} - -static void rfkill_task_handler(struct work_struct *work) -{ - struct rfkill_task *task = container_of(work, - struct rfkill_task, dwork.work); - bool doit = true; - - mutex_lock(&task->mutex); - - spin_lock_irq(&task->lock); - while (doit) { - if (task->global_op_pending) { - enum rfkill_global_sched_op op = task->op; - task->global_op_pending = false; - memset(task->sw_pending, 0, sizeof(task->sw_pending)); - spin_unlock_irq(&task->lock); - - __rfkill_handle_global_op(op); - - /* make sure we do at least one pass with - * !task->global_op_pending */ - spin_lock_irq(&task->lock); - continue; - } else if (!rfkill_is_epo_lock_active()) { - unsigned int i = 0; - - while (!task->global_op_pending && - i < RFKILL_TYPE_MAX) { - if (test_and_clear_bit(i, task->sw_pending)) { - bool c; - c = test_and_clear_bit(i, - task->sw_togglestate); - spin_unlock_irq(&task->lock); - - __rfkill_handle_normal_op(i, c); - - spin_lock_irq(&task->lock); - } - i++; - } - } - doit = task->global_op_pending; - } - spin_unlock_irq(&task->lock); - - mutex_unlock(&task->mutex); -} - -static struct rfkill_task rfkill_task = { - .dwork = __DELAYED_WORK_INITIALIZER(rfkill_task.dwork, - rfkill_task_handler), - .mutex = __MUTEX_INITIALIZER(rfkill_task.mutex), - .lock = __SPIN_LOCK_UNLOCKED(rfkill_task.lock), -}; - -static unsigned long rfkill_ratelimit(const unsigned long last) -{ - const unsigned long delay = msecs_to_jiffies(RFKILL_OPS_DELAY); - return (time_after(jiffies, last + delay)) ? 0 : delay; -} - -static void rfkill_schedule_ratelimited(void) -{ - if (!delayed_work_pending(&rfkill_task.dwork)) { - schedule_delayed_work(&rfkill_task.dwork, - rfkill_ratelimit(rfkill_task.last_scheduled)); - rfkill_task.last_scheduled = jiffies; - } -} - -static void rfkill_schedule_global_op(enum rfkill_global_sched_op op) -{ - unsigned long flags; - - spin_lock_irqsave(&rfkill_task.lock, flags); - rfkill_task.op = op; - rfkill_task.global_op_pending = true; - if (op == RFKILL_GLOBAL_OP_EPO && !rfkill_is_epo_lock_active()) { - /* bypass the limiter for EPO */ - cancel_delayed_work(&rfkill_task.dwork); - schedule_delayed_work(&rfkill_task.dwork, 0); - rfkill_task.last_scheduled = jiffies; - } else - rfkill_schedule_ratelimited(); - spin_unlock_irqrestore(&rfkill_task.lock, flags); -} - -static void rfkill_schedule_toggle(enum rfkill_type type) -{ - unsigned long flags; - - if (rfkill_is_epo_lock_active()) - return; - - spin_lock_irqsave(&rfkill_task.lock, flags); - if (!rfkill_task.global_op_pending) { - set_bit(type, rfkill_task.sw_pending); - change_bit(type, rfkill_task.sw_togglestate); - rfkill_schedule_ratelimited(); - } - spin_unlock_irqrestore(&rfkill_task.lock, flags); -} - -static void rfkill_schedule_evsw_rfkillall(int state) -{ - if (state) { - switch (rfkill_master_switch_mode) { - case RFKILL_INPUT_MASTER_UNBLOCKALL: - rfkill_schedule_global_op(RFKILL_GLOBAL_OP_UNBLOCK); - break; - case RFKILL_INPUT_MASTER_RESTORE: - rfkill_schedule_global_op(RFKILL_GLOBAL_OP_RESTORE); - break; - case RFKILL_INPUT_MASTER_DONOTHING: - rfkill_schedule_global_op(RFKILL_GLOBAL_OP_UNLOCK); - break; - default: - /* memory corruption or driver bug! fail safely */ - rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO); - WARN(1, "Unknown rfkill_master_switch_mode (%d), " - "driver bug or memory corruption detected!\n", - rfkill_master_switch_mode); - break; - } - } else - rfkill_schedule_global_op(RFKILL_GLOBAL_OP_EPO); -} - -static void rfkill_event(struct input_handle *handle, unsigned int type, - unsigned int code, int data) -{ - if (type == EV_KEY && data == 1) { - enum rfkill_type t; - - switch (code) { - case KEY_WLAN: - t = RFKILL_TYPE_WLAN; - break; - case KEY_BLUETOOTH: - t = RFKILL_TYPE_BLUETOOTH; - break; - case KEY_UWB: - t = RFKILL_TYPE_UWB; - break; - case KEY_WIMAX: - t = RFKILL_TYPE_WIMAX; - break; - default: - return; - } - rfkill_schedule_toggle(t); - return; - } else if (type == EV_SW) { - switch (code) { - case SW_RFKILL_ALL: - rfkill_schedule_evsw_rfkillall(data); - return; - default: - return; - } - } -} - -static int rfkill_connect(struct input_handler *handler, struct input_dev *dev, - const struct input_device_id *id) -{ - struct input_handle *handle; - int error; - - handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); - if (!handle) - return -ENOMEM; - - handle->dev = dev; - handle->handler = handler; - handle->name = "rfkill"; - - /* causes rfkill_start() to be called */ - error = input_register_handle(handle); - if (error) - goto err_free_handle; - - error = input_open_device(handle); - if (error) - goto err_unregister_handle; - - return 0; - - err_unregister_handle: - input_unregister_handle(handle); - err_free_handle: - kfree(handle); - return error; -} - -static void rfkill_start(struct input_handle *handle) -{ - /* Take event_lock to guard against configuration changes, we - * should be able to deal with concurrency with rfkill_event() - * just fine (which event_lock will also avoid). */ - spin_lock_irq(&handle->dev->event_lock); - - if (test_bit(EV_SW, handle->dev->evbit)) { - if (test_bit(SW_RFKILL_ALL, handle->dev->swbit)) - rfkill_schedule_evsw_rfkillall(test_bit(SW_RFKILL_ALL, - handle->dev->sw)); - /* add resync for further EV_SW events here */ - } - - spin_unlock_irq(&handle->dev->event_lock); -} - -static void rfkill_disconnect(struct input_handle *handle) -{ - input_close_device(handle); - input_unregister_handle(handle); - kfree(handle); -} - -static const struct input_device_id rfkill_ids[] = { - { - .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, - .evbit = { BIT_MASK(EV_KEY) }, - .keybit = { [BIT_WORD(KEY_WLAN)] = BIT_MASK(KEY_WLAN) }, - }, - { - .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, - .evbit = { BIT_MASK(EV_KEY) }, - .keybit = { [BIT_WORD(KEY_BLUETOOTH)] = BIT_MASK(KEY_BLUETOOTH) }, - }, - { - .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, - .evbit = { BIT_MASK(EV_KEY) }, - .keybit = { [BIT_WORD(KEY_UWB)] = BIT_MASK(KEY_UWB) }, - }, - { - .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT, - .evbit = { BIT_MASK(EV_KEY) }, - .keybit = { [BIT_WORD(KEY_WIMAX)] = BIT_MASK(KEY_WIMAX) }, - }, - { - .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_SWBIT, - .evbit = { BIT(EV_SW) }, - .swbit = { [BIT_WORD(SW_RFKILL_ALL)] = BIT_MASK(SW_RFKILL_ALL) }, - }, - { } -}; - -static struct input_handler rfkill_handler = { - .event = rfkill_event, - .connect = rfkill_connect, - .disconnect = rfkill_disconnect, - .start = rfkill_start, - .name = "rfkill", - .id_table = rfkill_ids, -}; - -static int __init rfkill_handler_init(void) -{ - if (rfkill_master_switch_mode >= RFKILL_INPUT_MASTER_MAX) - return -EINVAL; - - /* - * The penalty to not doing this is a possible RFKILL_OPS_DELAY delay - * at the first use. Acceptable, but if we can avoid it, why not? - */ - rfkill_task.last_scheduled = - jiffies - msecs_to_jiffies(RFKILL_OPS_DELAY) - 1; - return input_register_handler(&rfkill_handler); -} - -static void __exit rfkill_handler_exit(void) -{ - input_unregister_handler(&rfkill_handler); - cancel_delayed_work_sync(&rfkill_task.dwork); - rfkill_remove_epo_lock(); -} - -module_init(rfkill_handler_init); -module_exit(rfkill_handler_exit); diff --git a/net/rfkill/rfkill-input.h b/net/rfkill/rfkill-input.h deleted file mode 100644 index fe8df6b5b93..00000000000 --- a/net/rfkill/rfkill-input.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2007 Ivo van Doorn - */ - -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - */ - -#ifndef __RFKILL_INPUT_H -#define __RFKILL_INPUT_H - -void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state); -void rfkill_epo(void); -void rfkill_restore_states(void); -void rfkill_remove_epo_lock(void); -bool rfkill_is_epo_lock_active(void); -enum rfkill_state rfkill_get_global_state(const enum rfkill_type type); - -#endif /* __RFKILL_INPUT_H */ diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c deleted file mode 100644 index 4f5a83183c9..00000000000 --- a/net/rfkill/rfkill.c +++ /dev/null @@ -1,855 +0,0 @@ -/* - * Copyright (C) 2006 - 2007 Ivo van Doorn - * Copyright (C) 2007 Dmitry Torokhov - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* Get declaration of rfkill_switch_all() to shut up sparse. */ -#include "rfkill-input.h" - - -MODULE_AUTHOR("Ivo van Doorn "); -MODULE_VERSION("1.0"); -MODULE_DESCRIPTION("RF switch support"); -MODULE_LICENSE("GPL"); - -static LIST_HEAD(rfkill_list); /* list of registered rf switches */ -static DEFINE_MUTEX(rfkill_global_mutex); - -static unsigned int rfkill_default_state = RFKILL_STATE_UNBLOCKED; -module_param_named(default_state, rfkill_default_state, uint, 0444); -MODULE_PARM_DESC(default_state, - "Default initial state for all radio types, 0 = radio off"); - -struct rfkill_gsw_state { - enum rfkill_state current_state; - enum rfkill_state default_state; -}; - -static struct rfkill_gsw_state rfkill_global_states[RFKILL_TYPE_MAX]; -static unsigned long rfkill_states_lockdflt[BITS_TO_LONGS(RFKILL_TYPE_MAX)]; -static bool rfkill_epo_lock_active; - - -#ifdef CONFIG_RFKILL_LEDS -static void rfkill_led_trigger(struct rfkill *rfkill, - enum rfkill_state state) -{ - struct led_trigger *led = &rfkill->led_trigger; - - if (!led->name) - return; - if (state != RFKILL_STATE_UNBLOCKED) - led_trigger_event(led, LED_OFF); - else - led_trigger_event(led, LED_FULL); -} - -static void rfkill_led_trigger_activate(struct led_classdev *led) -{ - struct rfkill *rfkill = container_of(led->trigger, - struct rfkill, led_trigger); - - rfkill_led_trigger(rfkill, rfkill->state); -} -#else -static inline void rfkill_led_trigger(struct rfkill *rfkill, - enum rfkill_state state) -{ -} -#endif /* CONFIG_RFKILL_LEDS */ - -static void rfkill_uevent(struct rfkill *rfkill) -{ - kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE); -} - -static void update_rfkill_state(struct rfkill *rfkill) -{ - enum rfkill_state newstate, oldstate; - - if (rfkill->get_state) { - mutex_lock(&rfkill->mutex); - if (!rfkill->get_state(rfkill->data, &newstate)) { - oldstate = rfkill->state; - rfkill->state = newstate; - if (oldstate != newstate) - rfkill_uevent(rfkill); - } - mutex_unlock(&rfkill->mutex); - } - rfkill_led_trigger(rfkill, rfkill->state); -} - -/** - * rfkill_toggle_radio - wrapper for toggle_radio hook - * @rfkill: the rfkill struct to use - * @force: calls toggle_radio even if cache says it is not needed, - * and also makes sure notifications of the state will be - * sent even if it didn't change - * @state: the new state to call toggle_radio() with - * - * Calls rfkill->toggle_radio, enforcing the API for toggle_radio - * calls and handling all the red tape such as issuing notifications - * if the call is successful. - * - * Suspended devices are not touched at all, and -EAGAIN is returned. - * - * Note that the @force parameter cannot override a (possibly cached) - * state of RFKILL_STATE_HARD_BLOCKED. Any device making use of - * RFKILL_STATE_HARD_BLOCKED implements either get_state() or - * rfkill_force_state(), so the cache either is bypassed or valid. - * - * Note that we do call toggle_radio for RFKILL_STATE_SOFT_BLOCKED - * even if the radio is in RFKILL_STATE_HARD_BLOCKED state, so as to - * give the driver a hint that it should double-BLOCK the transmitter. - * - * Caller must have acquired rfkill->mutex. - */ -static int rfkill_toggle_radio(struct rfkill *rfkill, - enum rfkill_state state, - int force) -{ - int retval = 0; - enum rfkill_state oldstate, newstate; - - if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP)) - return -EBUSY; - - oldstate = rfkill->state; - - if (rfkill->get_state && !force && - !rfkill->get_state(rfkill->data, &newstate)) { - rfkill->state = newstate; - } - - switch (state) { - case RFKILL_STATE_HARD_BLOCKED: - /* typically happens when refreshing hardware state, - * such as on resume */ - state = RFKILL_STATE_SOFT_BLOCKED; - break; - case RFKILL_STATE_UNBLOCKED: - /* force can't override this, only rfkill_force_state() can */ - if (rfkill->state == RFKILL_STATE_HARD_BLOCKED) - return -EPERM; - break; - case RFKILL_STATE_SOFT_BLOCKED: - /* nothing to do, we want to give drivers the hint to double - * BLOCK even a transmitter that is already in state - * RFKILL_STATE_HARD_BLOCKED */ - break; - default: - WARN(1, KERN_WARNING - "rfkill: illegal state %d passed as parameter " - "to rfkill_toggle_radio\n", state); - return -EINVAL; - } - - if (force || state != rfkill->state) { - retval = rfkill->toggle_radio(rfkill->data, state); - /* never allow a HARD->SOFT downgrade! */ - if (!retval && rfkill->state != RFKILL_STATE_HARD_BLOCKED) - rfkill->state = state; - } - - if (force || rfkill->state != oldstate) - rfkill_uevent(rfkill); - - rfkill_led_trigger(rfkill, rfkill->state); - return retval; -} - -/** - * __rfkill_switch_all - Toggle state of all switches of given type - * @type: type of interfaces to be affected - * @state: the new state - * - * This function toggles the state of all switches of given type, - * unless a specific switch is claimed by userspace (in which case, - * that switch is left alone) or suspended. - * - * Caller must have acquired rfkill_global_mutex. - */ -static void __rfkill_switch_all(const enum rfkill_type type, - const enum rfkill_state state) -{ - struct rfkill *rfkill; - - if (WARN((state >= RFKILL_STATE_MAX || type >= RFKILL_TYPE_MAX), - KERN_WARNING - "rfkill: illegal state %d or type %d " - "passed as parameter to __rfkill_switch_all\n", - state, type)) - return; - - rfkill_global_states[type].current_state = state; - list_for_each_entry(rfkill, &rfkill_list, node) { - if (rfkill->type == type) { - mutex_lock(&rfkill->mutex); - rfkill_toggle_radio(rfkill, state, 0); - mutex_unlock(&rfkill->mutex); - rfkill_led_trigger(rfkill, rfkill->state); - } - } -} - -/** - * rfkill_switch_all - Toggle state of all switches of given type - * @type: type of interfaces to be affected - * @state: the new state - * - * Acquires rfkill_global_mutex and calls __rfkill_switch_all(@type, @state). - * Please refer to __rfkill_switch_all() for details. - * - * Does nothing if the EPO lock is active. - */ -void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) -{ - mutex_lock(&rfkill_global_mutex); - if (!rfkill_epo_lock_active) - __rfkill_switch_all(type, state); - mutex_unlock(&rfkill_global_mutex); -} -EXPORT_SYMBOL(rfkill_switch_all); - -/** - * rfkill_epo - emergency power off all transmitters - * - * This kicks all non-suspended rfkill devices to RFKILL_STATE_SOFT_BLOCKED, - * ignoring everything in its path but rfkill_global_mutex and rfkill->mutex. - * - * The global state before the EPO is saved and can be restored later - * using rfkill_restore_states(). - */ -void rfkill_epo(void) -{ - struct rfkill *rfkill; - int i; - - mutex_lock(&rfkill_global_mutex); - - rfkill_epo_lock_active = true; - list_for_each_entry(rfkill, &rfkill_list, node) { - mutex_lock(&rfkill->mutex); - rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); - mutex_unlock(&rfkill->mutex); - } - for (i = 0; i < RFKILL_TYPE_MAX; i++) { - rfkill_global_states[i].default_state = - rfkill_global_states[i].current_state; - rfkill_global_states[i].current_state = - RFKILL_STATE_SOFT_BLOCKED; - } - mutex_unlock(&rfkill_global_mutex); - rfkill_led_trigger(rfkill, rfkill->state); -} -EXPORT_SYMBOL_GPL(rfkill_epo); - -/** - * rfkill_restore_states - restore global states - * - * Restore (and sync switches to) the global state from the - * states in rfkill_default_states. This can undo the effects of - * a call to rfkill_epo(). - */ -void rfkill_restore_states(void) -{ - int i; - - mutex_lock(&rfkill_global_mutex); - - rfkill_epo_lock_active = false; - for (i = 0; i < RFKILL_TYPE_MAX; i++) - __rfkill_switch_all(i, rfkill_global_states[i].default_state); - mutex_unlock(&rfkill_global_mutex); -} -EXPORT_SYMBOL_GPL(rfkill_restore_states); - -/** - * rfkill_remove_epo_lock - unlock state changes - * - * Used by rfkill-input manually unlock state changes, when - * the EPO switch is deactivated. - */ -void rfkill_remove_epo_lock(void) -{ - mutex_lock(&rfkill_global_mutex); - rfkill_epo_lock_active = false; - mutex_unlock(&rfkill_global_mutex); -} -EXPORT_SYMBOL_GPL(rfkill_remove_epo_lock); - -/** - * rfkill_is_epo_lock_active - returns true EPO is active - * - * Returns 0 (false) if there is NOT an active EPO contidion, - * and 1 (true) if there is an active EPO contition, which - * locks all radios in one of the BLOCKED states. - * - * Can be called in atomic context. - */ -bool rfkill_is_epo_lock_active(void) -{ - return rfkill_epo_lock_active; -} -EXPORT_SYMBOL_GPL(rfkill_is_epo_lock_active); - -/** - * rfkill_get_global_state - returns global state for a type - * @type: the type to get the global state of - * - * Returns the current global state for a given wireless - * device type. - */ -enum rfkill_state rfkill_get_global_state(const enum rfkill_type type) -{ - return rfkill_global_states[type].current_state; -} -EXPORT_SYMBOL_GPL(rfkill_get_global_state); - -/** - * rfkill_force_state - Force the internal rfkill radio state - * @rfkill: pointer to the rfkill class to modify. - * @state: the current radio state the class should be forced to. - * - * This function updates the internal state of the radio cached - * by the rfkill class. It should be used when the driver gets - * a notification by the firmware/hardware of the current *real* - * state of the radio rfkill switch. - * - * Devices which are subject to external changes on their rfkill - * state (such as those caused by a hardware rfkill line) MUST - * have their driver arrange to call rfkill_force_state() as soon - * as possible after such a change. - * - * This function may not be called from an atomic context. - */ -int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) -{ - enum rfkill_state oldstate; - - BUG_ON(!rfkill); - if (WARN((state >= RFKILL_STATE_MAX), - KERN_WARNING - "rfkill: illegal state %d passed as parameter " - "to rfkill_force_state\n", state)) - return -EINVAL; - - mutex_lock(&rfkill->mutex); - - oldstate = rfkill->state; - rfkill->state = state; - - if (state != oldstate) - rfkill_uevent(rfkill); - - mutex_unlock(&rfkill->mutex); - rfkill_led_trigger(rfkill, rfkill->state); - - return 0; -} -EXPORT_SYMBOL(rfkill_force_state); - -static ssize_t rfkill_name_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct rfkill *rfkill = to_rfkill(dev); - - return sprintf(buf, "%s\n", rfkill->name); -} - -static const char *rfkill_get_type_str(enum rfkill_type type) -{ - switch (type) { - case RFKILL_TYPE_WLAN: - return "wlan"; - case RFKILL_TYPE_BLUETOOTH: - return "bluetooth"; - case RFKILL_TYPE_UWB: - return "ultrawideband"; - case RFKILL_TYPE_WIMAX: - return "wimax"; - case RFKILL_TYPE_WWAN: - return "wwan"; - default: - BUG(); - } -} - -static ssize_t rfkill_type_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct rfkill *rfkill = to_rfkill(dev); - - return sprintf(buf, "%s\n", rfkill_get_type_str(rfkill->type)); -} - -static ssize_t rfkill_state_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct rfkill *rfkill = to_rfkill(dev); - - update_rfkill_state(rfkill); - return sprintf(buf, "%d\n", rfkill->state); -} - -static ssize_t rfkill_state_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct rfkill *rfkill = to_rfkill(dev); - unsigned long state; - int error; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - error = strict_strtoul(buf, 0, &state); - if (error) - return error; - - /* RFKILL_STATE_HARD_BLOCKED is illegal here... */ - if (state != RFKILL_STATE_UNBLOCKED && - state != RFKILL_STATE_SOFT_BLOCKED) - return -EINVAL; - - error = mutex_lock_killable(&rfkill->mutex); - if (error) - return error; - - if (!rfkill_epo_lock_active) - error = rfkill_toggle_radio(rfkill, state, 0); - else - error = -EPERM; - - mutex_unlock(&rfkill->mutex); - - return error ? error : count; -} - -static ssize_t rfkill_claim_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - return sprintf(buf, "%d\n", 0); -} - -static ssize_t rfkill_claim_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - return -EOPNOTSUPP; -} - -static struct device_attribute rfkill_dev_attrs[] = { - __ATTR(name, S_IRUGO, rfkill_name_show, NULL), - __ATTR(type, S_IRUGO, rfkill_type_show, NULL), - __ATTR(state, S_IRUGO|S_IWUSR, rfkill_state_show, rfkill_state_store), - __ATTR(claim, S_IRUGO|S_IWUSR, rfkill_claim_show, rfkill_claim_store), - __ATTR_NULL -}; - -static void rfkill_release(struct device *dev) -{ - struct rfkill *rfkill = to_rfkill(dev); - - kfree(rfkill); - module_put(THIS_MODULE); -} - -#ifdef CONFIG_PM -static int rfkill_suspend(struct device *dev, pm_message_t state) -{ - struct rfkill *rfkill = to_rfkill(dev); - - /* mark class device as suspended */ - if (dev->power.power_state.event != state.event) - dev->power.power_state = state; - - /* store state for the resume handler */ - rfkill->state_for_resume = rfkill->state; - - return 0; -} - -static int rfkill_resume(struct device *dev) -{ - struct rfkill *rfkill = to_rfkill(dev); - enum rfkill_state newstate; - - if (dev->power.power_state.event != PM_EVENT_ON) { - mutex_lock(&rfkill->mutex); - - dev->power.power_state.event = PM_EVENT_ON; - - /* - * rfkill->state could have been modified before we got - * called, and won't be updated by rfkill_toggle_radio() - * in force mode. Sync it FIRST. - */ - if (rfkill->get_state && - !rfkill->get_state(rfkill->data, &newstate)) - rfkill->state = newstate; - - /* - * If we are under EPO, kick transmitter offline, - * otherwise restore to pre-suspend state. - * - * Issue a notification in any case - */ - rfkill_toggle_radio(rfkill, - rfkill_epo_lock_active ? - RFKILL_STATE_SOFT_BLOCKED : - rfkill->state_for_resume, - 1); - - mutex_unlock(&rfkill->mutex); - rfkill_led_trigger(rfkill, rfkill->state); - } - - return 0; -} -#else -#define rfkill_suspend NULL -#define rfkill_resume NULL -#endif - -static int rfkill_dev_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - struct rfkill *rfkill = to_rfkill(dev); - int error; - - error = add_uevent_var(env, "RFKILL_NAME=%s", rfkill->name); - if (error) - return error; - error = add_uevent_var(env, "RFKILL_TYPE=%s", - rfkill_get_type_str(rfkill->type)); - if (error) - return error; - error = add_uevent_var(env, "RFKILL_STATE=%d", rfkill->state); - return error; -} - -static struct class rfkill_class = { - .name = "rfkill", - .dev_release = rfkill_release, - .dev_attrs = rfkill_dev_attrs, - .suspend = rfkill_suspend, - .resume = rfkill_resume, - .dev_uevent = rfkill_dev_uevent, -}; - -static int rfkill_check_duplicity(const struct rfkill *rfkill) -{ - struct rfkill *p; - unsigned long seen[BITS_TO_LONGS(RFKILL_TYPE_MAX)]; - - memset(seen, 0, sizeof(seen)); - - list_for_each_entry(p, &rfkill_list, node) { - if (WARN((p == rfkill), KERN_WARNING - "rfkill: illegal attempt to register " - "an already registered rfkill struct\n")) - return -EEXIST; - set_bit(p->type, seen); - } - - /* 0: first switch of its kind */ - return (test_bit(rfkill->type, seen)) ? 1 : 0; -} - -static int rfkill_add_switch(struct rfkill *rfkill) -{ - int error; - - mutex_lock(&rfkill_global_mutex); - - error = rfkill_check_duplicity(rfkill); - if (error < 0) - goto unlock_out; - - if (!error) { - /* lock default after first use */ - set_bit(rfkill->type, rfkill_states_lockdflt); - rfkill_global_states[rfkill->type].current_state = - rfkill_global_states[rfkill->type].default_state; - } - - rfkill_toggle_radio(rfkill, - rfkill_global_states[rfkill->type].current_state, - 0); - - list_add_tail(&rfkill->node, &rfkill_list); - - error = 0; -unlock_out: - mutex_unlock(&rfkill_global_mutex); - - return error; -} - -static void rfkill_remove_switch(struct rfkill *rfkill) -{ - mutex_lock(&rfkill_global_mutex); - list_del_init(&rfkill->node); - mutex_unlock(&rfkill_global_mutex); - - mutex_lock(&rfkill->mutex); - rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); - mutex_unlock(&rfkill->mutex); -} - -/** - * rfkill_allocate - allocate memory for rfkill structure. - * @parent: device that has rf switch on it - * @type: type of the switch (RFKILL_TYPE_*) - * - * This function should be called by the network driver when it needs - * rfkill structure. Once the structure is allocated the driver should - * finish its initialization by setting the name, private data, enable_radio - * and disable_radio methods and then register it with rfkill_register(). - * - * NOTE: If registration fails the structure shoudl be freed by calling - * rfkill_free() otherwise rfkill_unregister() should be used. - */ -struct rfkill * __must_check rfkill_allocate(struct device *parent, - enum rfkill_type type) -{ - struct rfkill *rfkill; - struct device *dev; - - if (WARN((type >= RFKILL_TYPE_MAX), - KERN_WARNING - "rfkill: illegal type %d passed as parameter " - "to rfkill_allocate\n", type)) - return NULL; - - rfkill = kzalloc(sizeof(struct rfkill), GFP_KERNEL); - if (!rfkill) - return NULL; - - mutex_init(&rfkill->mutex); - INIT_LIST_HEAD(&rfkill->node); - rfkill->type = type; - - dev = &rfkill->dev; - dev->class = &rfkill_class; - dev->parent = parent; - device_initialize(dev); - - __module_get(THIS_MODULE); - - return rfkill; -} -EXPORT_SYMBOL(rfkill_allocate); - -/** - * rfkill_free - Mark rfkill structure for deletion - * @rfkill: rfkill structure to be destroyed - * - * Decrements reference count of the rfkill structure so it is destroyed. - * Note that rfkill_free() should _not_ be called after rfkill_unregister(). - */ -void rfkill_free(struct rfkill *rfkill) -{ - if (rfkill) - put_device(&rfkill->dev); -} -EXPORT_SYMBOL(rfkill_free); - -static void rfkill_led_trigger_register(struct rfkill *rfkill) -{ -#ifdef CONFIG_RFKILL_LEDS - int error; - - if (!rfkill->led_trigger.name) - rfkill->led_trigger.name = dev_name(&rfkill->dev); - if (!rfkill->led_trigger.activate) - rfkill->led_trigger.activate = rfkill_led_trigger_activate; - error = led_trigger_register(&rfkill->led_trigger); - if (error) - rfkill->led_trigger.name = NULL; -#endif /* CONFIG_RFKILL_LEDS */ -} - -static void rfkill_led_trigger_unregister(struct rfkill *rfkill) -{ -#ifdef CONFIG_RFKILL_LEDS - if (rfkill->led_trigger.name) { - led_trigger_unregister(&rfkill->led_trigger); - rfkill->led_trigger.name = NULL; - } -#endif -} - -/** - * rfkill_register - Register a rfkill structure. - * @rfkill: rfkill structure to be registered - * - * This function should be called by the network driver when the rfkill - * structure needs to be registered. Immediately from registration the - * switch driver should be able to service calls to toggle_radio. - */ -int __must_check rfkill_register(struct rfkill *rfkill) -{ - static atomic_t rfkill_no = ATOMIC_INIT(0); - struct device *dev = &rfkill->dev; - int error; - - if (WARN((!rfkill || !rfkill->toggle_radio || - rfkill->type >= RFKILL_TYPE_MAX || - rfkill->state >= RFKILL_STATE_MAX), - KERN_WARNING - "rfkill: attempt to register a " - "badly initialized rfkill struct\n")) - return -EINVAL; - - dev_set_name(dev, "rfkill%ld", (long)atomic_inc_return(&rfkill_no) - 1); - - rfkill_led_trigger_register(rfkill); - - error = rfkill_add_switch(rfkill); - if (error) { - rfkill_led_trigger_unregister(rfkill); - return error; - } - - error = device_add(dev); - if (error) { - rfkill_remove_switch(rfkill); - rfkill_led_trigger_unregister(rfkill); - return error; - } - - return 0; -} -EXPORT_SYMBOL(rfkill_register); - -/** - * rfkill_unregister - Unregister a rfkill structure. - * @rfkill: rfkill structure to be unregistered - * - * This function should be called by the network driver during device - * teardown to destroy rfkill structure. Note that rfkill_free() should - * _not_ be called after rfkill_unregister(). - */ -void rfkill_unregister(struct rfkill *rfkill) -{ - BUG_ON(!rfkill); - device_del(&rfkill->dev); - rfkill_remove_switch(rfkill); - rfkill_led_trigger_unregister(rfkill); - put_device(&rfkill->dev); -} -EXPORT_SYMBOL(rfkill_unregister); - -/** - * rfkill_set_default - set initial value for a switch type - * @type - the type of switch to set the default state of - * @state - the new default state for that group of switches - * - * Sets the initial state rfkill should use for a given type. - * The following initial states are allowed: RFKILL_STATE_SOFT_BLOCKED - * and RFKILL_STATE_UNBLOCKED. - * - * This function is meant to be used by platform drivers for platforms - * that can save switch state across power down/reboot. - * - * The default state for each switch type can be changed exactly once. - * After a switch of that type is registered, the default state cannot - * be changed anymore. This guards against multiple drivers it the - * same platform trying to set the initial switch default state, which - * is not allowed. - * - * Returns -EPERM if the state has already been set once or is in use, - * so drivers likely want to either ignore or at most printk(KERN_NOTICE) - * if this function returns -EPERM. - * - * Returns 0 if the new default state was set, or an error if it - * could not be set. - */ -int rfkill_set_default(enum rfkill_type type, enum rfkill_state state) -{ - int error; - - if (WARN((type >= RFKILL_TYPE_MAX || - (state != RFKILL_STATE_SOFT_BLOCKED && - state != RFKILL_STATE_UNBLOCKED)), - KERN_WARNING - "rfkill: illegal state %d or type %d passed as " - "parameter to rfkill_set_default\n", state, type)) - return -EINVAL; - - mutex_lock(&rfkill_global_mutex); - - if (!test_and_set_bit(type, rfkill_states_lockdflt)) { - rfkill_global_states[type].default_state = state; - rfkill_global_states[type].current_state = state; - error = 0; - } else - error = -EPERM; - - mutex_unlock(&rfkill_global_mutex); - return error; -} -EXPORT_SYMBOL_GPL(rfkill_set_default); - -/* - * Rfkill module initialization/deinitialization. - */ -static int __init rfkill_init(void) -{ - int error; - int i; - - /* RFKILL_STATE_HARD_BLOCKED is illegal here... */ - if (rfkill_default_state != RFKILL_STATE_SOFT_BLOCKED && - rfkill_default_state != RFKILL_STATE_UNBLOCKED) - return -EINVAL; - - for (i = 0; i < RFKILL_TYPE_MAX; i++) - rfkill_global_states[i].default_state = rfkill_default_state; - - error = class_register(&rfkill_class); - if (error) { - printk(KERN_ERR "rfkill: unable to register rfkill class\n"); - return error; - } - - return 0; -} - -static void __exit rfkill_exit(void) -{ - class_unregister(&rfkill_class); -} - -subsys_initcall(rfkill_init); -module_exit(rfkill_exit); diff --git a/net/rfkill/rfkill.h b/net/rfkill/rfkill.h new file mode 100644 index 00000000000..d1117cb6e4d --- /dev/null +++ b/net/rfkill/rfkill.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2007 Ivo van Doorn + * Copyright 2009 Johannes Berg + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#ifndef __RFKILL_INPUT_H +#define __RFKILL_INPUT_H + +/* core code */ +void rfkill_switch_all(const enum rfkill_type type, bool blocked); +void rfkill_epo(void); +void rfkill_restore_states(void); +void rfkill_remove_epo_lock(void); +bool rfkill_is_epo_lock_active(void); +bool rfkill_get_global_sw_state(const enum rfkill_type type); + +/* input handler */ +int rfkill_handler_init(void); +void rfkill_handler_exit(void); + +#endif /* __RFKILL_INPUT_H */ diff --git a/net/wimax/Kconfig b/net/wimax/Kconfig index 1b46747a5f5..0bdbb692820 100644 --- a/net/wimax/Kconfig +++ b/net/wimax/Kconfig @@ -1,23 +1,9 @@ # # WiMAX LAN device configuration # -# Note the ugly 'depends on' on WIMAX: that disallows RFKILL to be a -# module if WIMAX is to be linked in. The WiMAX code is done in such a -# way that it doesn't require and explicit dependency on RFKILL in -# case an embedded system wants to rip it out. -# -# As well, enablement of the RFKILL code means we need the INPUT layer -# support to inject events coming from hw rfkill switches. That -# dependency could be killed if input.h provided appropriate means to -# work when input is disabled. - -comment "WiMAX Wireless Broadband support requires CONFIG_INPUT enabled" - depends on INPUT = n && RFKILL != n menuconfig WIMAX tristate "WiMAX Wireless Broadband support" - depends on (y && RFKILL != m) || m - depends on (INPUT && RFKILL != n) || RFKILL = n help Select to configure support for devices that provide diff --git a/net/wimax/op-rfkill.c b/net/wimax/op-rfkill.c index a3616e2ccb8..bb102e4aa3e 100644 --- a/net/wimax/op-rfkill.c +++ b/net/wimax/op-rfkill.c @@ -29,8 +29,8 @@ * A non-polled generic rfkill device is embedded into the WiMAX * subsystem's representation of a device. * - * FIXME: Need polled support? use a timer or add the implementation - * to the stack. + * FIXME: Need polled support? Let drivers provide a poll routine + * and hand it to rfkill ops then? * * All device drivers have to do is after wimax_dev_init(), call * wimax_report_rfkill_hw() and wimax_report_rfkill_sw() to update @@ -43,7 +43,7 @@ * wimax_rfkill() Kernel calling wimax_rfkill() * __wimax_rf_toggle_radio() * - * wimax_rfkill_toggle_radio() RF-Kill subsytem calling + * wimax_rfkill_set_radio_block() RF-Kill subsytem calling * __wimax_rf_toggle_radio() * * __wimax_rf_toggle_radio() @@ -65,15 +65,11 @@ #include #include #include -#include #include "wimax-internal.h" #define D_SUBMODULE op_rfkill #include "debug-levels.h" -#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) - - /** * wimax_report_rfkill_hw - Reports changes in the hardware RF switch * @@ -99,7 +95,6 @@ void wimax_report_rfkill_hw(struct wimax_dev *wimax_dev, int result; struct device *dev = wimax_dev_to_dev(wimax_dev); enum wimax_st wimax_state; - enum rfkill_state rfkill_state; d_fnstart(3, dev, "(wimax_dev %p state %u)\n", wimax_dev, state); BUG_ON(state == WIMAX_RF_QUERY); @@ -112,16 +107,15 @@ void wimax_report_rfkill_hw(struct wimax_dev *wimax_dev, if (state != wimax_dev->rf_hw) { wimax_dev->rf_hw = state; - rfkill_state = state == WIMAX_RF_ON ? - RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; if (wimax_dev->rf_hw == WIMAX_RF_ON && wimax_dev->rf_sw == WIMAX_RF_ON) wimax_state = WIMAX_ST_READY; else wimax_state = WIMAX_ST_RADIO_OFF; + + rfkill_set_hw_state(wimax_dev->rfkill, state == WIMAX_RF_OFF); + __wimax_state_change(wimax_dev, wimax_state); - input_report_key(wimax_dev->rfkill_input, KEY_WIMAX, - rfkill_state); } error_not_ready: mutex_unlock(&wimax_dev->mutex); @@ -174,6 +168,7 @@ void wimax_report_rfkill_sw(struct wimax_dev *wimax_dev, else wimax_state = WIMAX_ST_RADIO_OFF; __wimax_state_change(wimax_dev, wimax_state); + rfkill_set_sw_state(wimax_dev->rfkill, state == WIMAX_RF_OFF); } error_not_ready: mutex_unlock(&wimax_dev->mutex); @@ -249,36 +244,31 @@ out_no_change: * * NOTE: This call will block until the operation is completed. */ -static -int wimax_rfkill_toggle_radio(void *data, enum rfkill_state state) +static int wimax_rfkill_set_radio_block(void *data, bool blocked) { int result; struct wimax_dev *wimax_dev = data; struct device *dev = wimax_dev_to_dev(wimax_dev); enum wimax_rf_state rf_state; - d_fnstart(3, dev, "(wimax_dev %p state %u)\n", wimax_dev, state); - switch (state) { - case RFKILL_STATE_SOFT_BLOCKED: + d_fnstart(3, dev, "(wimax_dev %p blocked %u)\n", wimax_dev, blocked); + rf_state = WIMAX_RF_ON; + if (blocked) rf_state = WIMAX_RF_OFF; - break; - case RFKILL_STATE_UNBLOCKED: - rf_state = WIMAX_RF_ON; - break; - default: - BUG(); - } mutex_lock(&wimax_dev->mutex); if (wimax_dev->state <= __WIMAX_ST_QUIESCING) - result = 0; /* just pretend it didn't happen */ + result = 0; else result = __wimax_rf_toggle_radio(wimax_dev, rf_state); mutex_unlock(&wimax_dev->mutex); - d_fnend(3, dev, "(wimax_dev %p state %u) = %d\n", - wimax_dev, state, result); + d_fnend(3, dev, "(wimax_dev %p blocked %u) = %d\n", + wimax_dev, blocked, result); return result; } +static const struct rfkill_ops wimax_rfkill_ops = { + .set_block = wimax_rfkill_set_radio_block, +}; /** * wimax_rfkill - Set the software RF switch state for a WiMAX device @@ -322,6 +312,7 @@ int wimax_rfkill(struct wimax_dev *wimax_dev, enum wimax_rf_state state) result = __wimax_rf_toggle_radio(wimax_dev, state); if (result < 0) goto error; + rfkill_set_sw_state(wimax_dev->rfkill, state == WIMAX_RF_OFF); break; case WIMAX_RF_QUERY: break; @@ -349,40 +340,20 @@ int wimax_rfkill_add(struct wimax_dev *wimax_dev) { int result; struct rfkill *rfkill; - struct input_dev *input_dev; struct device *dev = wimax_dev_to_dev(wimax_dev); d_fnstart(3, dev, "(wimax_dev %p)\n", wimax_dev); /* Initialize RF Kill */ result = -ENOMEM; - rfkill = rfkill_allocate(dev, RFKILL_TYPE_WIMAX); + rfkill = rfkill_alloc(wimax_dev->name, dev, RFKILL_TYPE_WIMAX, + &wimax_rfkill_ops, wimax_dev); if (rfkill == NULL) goto error_rfkill_allocate; + + d_printf(1, dev, "rfkill %p\n", rfkill); + wimax_dev->rfkill = rfkill; - rfkill->name = wimax_dev->name; - rfkill->state = RFKILL_STATE_UNBLOCKED; - rfkill->data = wimax_dev; - rfkill->toggle_radio = wimax_rfkill_toggle_radio; - - /* Initialize the input device for the hw key */ - input_dev = input_allocate_device(); - if (input_dev == NULL) - goto error_input_allocate; - wimax_dev->rfkill_input = input_dev; - d_printf(1, dev, "rfkill %p input %p\n", rfkill, input_dev); - - input_dev->name = wimax_dev->name; - /* FIXME: get a real device bus ID and stuff? do we care? */ - input_dev->id.bustype = BUS_HOST; - input_dev->id.vendor = 0xffff; - input_dev->evbit[0] = BIT(EV_KEY); - set_bit(KEY_WIMAX, input_dev->keybit); - - /* Register both */ - result = input_register_device(wimax_dev->rfkill_input); - if (result < 0) - goto error_input_register; result = rfkill_register(wimax_dev->rfkill); if (result < 0) goto error_rfkill_register; @@ -394,17 +365,8 @@ int wimax_rfkill_add(struct wimax_dev *wimax_dev) d_fnend(3, dev, "(wimax_dev %p) = 0\n", wimax_dev); return 0; - /* if rfkill_register() suceeds, can't use rfkill_free() any - * more, only rfkill_unregister() [it owns the refcount]; with - * the input device we have the same issue--hence the if. */ error_rfkill_register: - input_unregister_device(wimax_dev->rfkill_input); - wimax_dev->rfkill_input = NULL; -error_input_register: - if (wimax_dev->rfkill_input) - input_free_device(wimax_dev->rfkill_input); -error_input_allocate: - rfkill_free(wimax_dev->rfkill); + rfkill_destroy(wimax_dev->rfkill); error_rfkill_allocate: d_fnend(3, dev, "(wimax_dev %p) = %d\n", wimax_dev, result); return result; @@ -423,45 +385,12 @@ void wimax_rfkill_rm(struct wimax_dev *wimax_dev) { struct device *dev = wimax_dev_to_dev(wimax_dev); d_fnstart(3, dev, "(wimax_dev %p)\n", wimax_dev); - rfkill_unregister(wimax_dev->rfkill); /* frees */ - input_unregister_device(wimax_dev->rfkill_input); + rfkill_unregister(wimax_dev->rfkill); + rfkill_destroy(wimax_dev->rfkill); d_fnend(3, dev, "(wimax_dev %p)\n", wimax_dev); } -#else /* #ifdef CONFIG_RFKILL */ - -void wimax_report_rfkill_hw(struct wimax_dev *wimax_dev, - enum wimax_rf_state state) -{ -} -EXPORT_SYMBOL_GPL(wimax_report_rfkill_hw); - -void wimax_report_rfkill_sw(struct wimax_dev *wimax_dev, - enum wimax_rf_state state) -{ -} -EXPORT_SYMBOL_GPL(wimax_report_rfkill_sw); - -int wimax_rfkill(struct wimax_dev *wimax_dev, - enum wimax_rf_state state) -{ - return WIMAX_RF_ON << 1 | WIMAX_RF_ON; -} -EXPORT_SYMBOL_GPL(wimax_rfkill); - -int wimax_rfkill_add(struct wimax_dev *wimax_dev) -{ - return 0; -} - -void wimax_rfkill_rm(struct wimax_dev *wimax_dev) -{ -} - -#endif /* #ifdef CONFIG_RFKILL */ - - /* * Exporting to user space over generic netlink * -- cgit v1.2.3-70-g09d2 From 82880a7cf9c059c11cd6ad1868c48969956f966b Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Sat, 13 Jun 2009 14:50:24 +0530 Subject: ath9k: Add helper to get ath9k specific current channel Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index f7baa406918..89bf07e66d1 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -231,6 +231,19 @@ static void ath_setup_rates(struct ath_softc *sc, enum ieee80211_band band) } } +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 @@ -1920,7 +1933,7 @@ static int ath9k_start(struct ieee80211_hw *hw) 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); @@ -1950,11 +1963,9 @@ static int ath9k_start(struct ieee80211_hw *hw) /* 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); -- cgit v1.2.3-70-g09d2 From 159cd468bc885f7a2fbc75bbfe782782e52ef9c9 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Sat, 13 Jun 2009 14:50:25 +0530 Subject: ath9k: Make sure we have current channel in ah_curchan before rf disable/enable Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 89bf07e66d1..6516a426f6c 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1123,6 +1123,9 @@ void ath_radio_enable(struct ath_softc *sc) ath9k_ps_wakeup(sc); ath9k_hw_configpcipowersave(ah, 0); + 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) { @@ -1175,6 +1178,9 @@ void ath_radio_disable(struct ath_softc *sc) 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) { -- cgit v1.2.3-70-g09d2 From 3b319aae4244f9b4758212605f67cf63207a4fa1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 13 Jun 2009 14:50:26 +0530 Subject: ath9k: port to cfg80211 rfkill This ports the ath9k rfkill code to the new API offered by cfg80211 and thus removes a lot of useless stuff. ("With this series a kernel panic, which is a regression, during module unload disappears." -- Vasanthakumar Thiagarajan Other patches in the series: ath9k: Add helper to get ath9k specific current channel ath9k: Make sure we have current channel in ah_curchan before rf disable/enable -- JWL) Signed-off-by: Johannes Berg Cc: Luis Rodriguez Tested-by: Vasanthakumar Thiagarajan Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/Kconfig | 1 - drivers/net/wireless/ath/ath9k/ath9k.h | 9 ---- drivers/net/wireless/ath/ath9k/hw.c | 29 +++++------ drivers/net/wireless/ath/ath9k/hw.h | 3 -- drivers/net/wireless/ath/ath9k/main.c | 94 +++++----------------------------- 5 files changed, 26 insertions(+), 110 deletions(-) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 0ed1ac312aa..2d79610bce1 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -1,7 +1,6 @@ config ATH9K tristate "Atheros 802.11n wireless cards support" depends on PCI && MAC80211 && WLAN_80211 - depends on RFKILL || RFKILL=n select ATH_COMMON select MAC80211_LEDS select LEDS_CLASS diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 515880aa211..a6e65ff4bb2 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -21,7 +21,6 @@ #include #include #include -#include #include "hw.h" #include "rc.h" @@ -460,12 +459,6 @@ struct ath_led { bool registered; }; -struct ath_rfkill { - struct rfkill *rfkill; - struct rfkill_ops ops; - char rfkill_name[32]; -}; - /********************/ /* Main driver core */ /********************/ @@ -505,7 +498,6 @@ struct ath_rfkill { #define SC_OP_PROTECT_ENABLE BIT(6) #define SC_OP_RXFLUSH BIT(7) #define SC_OP_LED_ASSOCIATED BIT(8) -#define SC_OP_RFKILL_REGISTERED BIT(9) #define SC_OP_WAIT_FOR_BEACON BIT(12) #define SC_OP_LED_ON BIT(13) #define SC_OP_SCANNING BIT(14) @@ -591,7 +583,6 @@ struct ath_softc { int beacon_interval; - struct ath_rfkill rf_kill; struct ath_ani ani; struct ath9k_node_stats nodestats; #ifdef CONFIG_ATH9K_DEBUG diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 1579c9407ed..34935a8ee59 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2186,6 +2186,18 @@ static void ath9k_hw_spur_mitigate(struct ath_hw *ah, struct ath9k_channel *chan REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask); } +static void ath9k_enable_rfkill(struct ath_hw *ah) +{ + REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, + AR_GPIO_INPUT_EN_VAL_RFSILENT_BB); + + REG_CLR_BIT(ah, AR_GPIO_INPUT_MUX2, + AR_GPIO_INPUT_MUX2_RFSILENT); + + ath9k_hw_cfg_gpio_input(ah, ah->rfkill_gpio); + REG_SET_BIT(ah, AR_PHY_TEST, RFSILENT_BB); +} + int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, bool bChannelChange) { @@ -2313,10 +2325,9 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ath9k_hw_init_interrupt_masks(ah, ah->opmode); ath9k_hw_init_qos(ah); -#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) ath9k_enable_rfkill(ah); -#endif + ath9k_hw_init_user_settings(ah); REG_WRITE(ah, AR_STA_ID1, @@ -3613,20 +3624,6 @@ void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val) AR_GPIO_BIT(gpio)); } -#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) -void ath9k_enable_rfkill(struct ath_hw *ah) -{ - REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, - AR_GPIO_INPUT_EN_VAL_RFSILENT_BB); - - REG_CLR_BIT(ah, AR_GPIO_INPUT_MUX2, - AR_GPIO_INPUT_MUX2_RFSILENT); - - ath9k_hw_cfg_gpio_input(ah, ah->rfkill_gpio); - REG_SET_BIT(ah, AR_PHY_TEST, RFSILENT_BB); -} -#endif - u32 ath9k_hw_getdefantenna(struct ath_hw *ah) { return REG_READ(ah, AR_DEF_ANTENNA) & 0x7; diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index dd8508ef6e0..9d0b31ad460 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -565,9 +565,6 @@ u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio); void ath9k_hw_cfg_output(struct ath_hw *ah, u32 gpio, u32 ah_signal_type); void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val); -#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) -void ath9k_enable_rfkill(struct ath_hw *ah); -#endif u32 ath9k_hw_getdefantenna(struct ath_hw *ah); void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna); bool ath9k_hw_setantennaswitch(struct ath_hw *ah, diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 6516a426f6c..91c00486b2a 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1197,8 +1197,6 @@ void ath_radio_disable(struct ath_softc *sc) ath9k_ps_restore(sc); } -#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) - /*******************/ /* Rfkill */ /*******************/ @@ -1211,81 +1209,27 @@ static bool ath_is_rfkill_set(struct ath_softc *sc) ah->rfkill_polarity; } -/* s/w rfkill handlers */ -static int ath_rfkill_set_block(void *data, bool blocked) -{ - struct ath_softc *sc = data; - - if (blocked) - ath_radio_disable(sc); - else - ath_radio_enable(sc); - - return 0; -} - -static void ath_rfkill_poll_state(struct rfkill *rfkill, void *data) +static void ath9k_rfkill_poll_state(struct ieee80211_hw *hw) { - struct ath_softc *sc = data; + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; bool blocked = !!ath_is_rfkill_set(sc); - if (rfkill_set_hw_state(rfkill, blocked)) + wiphy_rfkill_set_hw_state(hw->wiphy, blocked); + + if (blocked) ath_radio_disable(sc); else ath_radio_enable(sc); } -/* Init s/w rfkill */ -static int ath_init_sw_rfkill(struct ath_softc *sc) +static void ath_start_rfkill_poll(struct ath_softc *sc) { - sc->rf_kill.ops.set_block = ath_rfkill_set_block; - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) - sc->rf_kill.ops.poll = ath_rfkill_poll_state; - - snprintf(sc->rf_kill.rfkill_name, sizeof(sc->rf_kill.rfkill_name), - "ath9k-%s::rfkill", wiphy_name(sc->hw->wiphy)); - - sc->rf_kill.rfkill = rfkill_alloc(sc->rf_kill.rfkill_name, - wiphy_dev(sc->hw->wiphy), - RFKILL_TYPE_WLAN, - &sc->rf_kill.ops, sc); - if (!sc->rf_kill.rfkill) { - DPRINTF(sc, ATH_DBG_FATAL, "Failed to allocate rfkill\n"); - return -ENOMEM; - } - - return 0; -} - -/* Deinitialize rfkill */ -static void ath_deinit_rfkill(struct ath_softc *sc) -{ - if (sc->sc_flags & SC_OP_RFKILL_REGISTERED) { - rfkill_unregister(sc->rf_kill.rfkill); - rfkill_destroy(sc->rf_kill.rfkill); - sc->sc_flags &= ~SC_OP_RFKILL_REGISTERED; - } -} - -static int ath_start_rfkill_poll(struct ath_softc *sc) -{ - 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_destroy(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) { @@ -1305,9 +1249,6 @@ void ath_detach(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); @@ -1645,13 +1586,6 @@ int ath_attach(u16 devid, struct ath_softc *sc) if (error != 0) goto error_attach; -#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) - /* 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); @@ -1667,6 +1601,7 @@ int ath_attach(u16 devid, struct ath_softc *sc) /* Initialize LED control */ ath_init_leds(sc); + ath_start_rfkill_poll(sc); return 0; @@ -2035,10 +1970,6 @@ static int ath9k_start(struct ieee80211_hw *hw) ieee80211_wake_queues(hw); -#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE) - r = ath_start_rfkill_poll(sc); -#endif - mutex_unlock: mutex_unlock(&sc->mutex); @@ -2176,7 +2107,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) } else sc->rx.rxlink = NULL; - rfkill_pause_polling(sc->rf_kill.rfkill); + wiphy_rfkill_stop_polling(sc->hw->wiphy); /* disable HAL and put h/w to sleep */ ath9k_hw_disable(sc->sc_ah); @@ -2782,6 +2713,7 @@ struct ieee80211_ops ath9k_ops = { .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 { -- cgit v1.2.3-70-g09d2 From 3989279ccc838c7357c75af6090f56f5cb555158 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Mon, 15 Jun 2009 17:49:09 +0200 Subject: ath9k: restore power mode on error path The ath9k_ps_{wakeup,restore} calls must be in balance. Changes-licensed-under: ISC Signed-off-by: Gabor Juhos Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 91c00486b2a..9f49a3251d4 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -296,7 +296,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, "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); @@ -305,14 +305,17 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, 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; } /* -- cgit v1.2.3-70-g09d2 From 38ab422e64d991472ce21ffdd7a37e8a02279697 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Wed, 17 Jun 2009 20:53:21 +0200 Subject: ath9k: restore PS mode, before we put the chip into FULL SLEEP state. We want to put the chip into FULL SLEEP state, when we are disabling the radio, but the the current code always change it to AWAKE/NETWORK SLEEP. Changes-licensed-under: ISC Signed-off-by: Gabor Juhos Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath/ath9k/main.c') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 9f49a3251d4..66a6c1f5022 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1196,8 +1196,8 @@ void ath_radio_disable(struct ath_softc *sc) ath9k_hw_phy_disable(ah); ath9k_hw_configpcipowersave(ah, 1); - ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); ath9k_ps_restore(sc); + ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); } /*******************/ -- cgit v1.2.3-70-g09d2