diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath5k/base.c')
-rw-r--r-- | drivers/net/wireless/ath/ath5k/base.c | 210 |
1 files changed, 121 insertions, 89 deletions
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index c8c658bfcf9d..6789c5dfcc76 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, | |||
227 | static void ath5k_remove_interface(struct ieee80211_hw *hw, | 227 | static void ath5k_remove_interface(struct ieee80211_hw *hw, |
228 | struct ieee80211_if_init_conf *conf); | 228 | struct ieee80211_if_init_conf *conf); |
229 | static int ath5k_config(struct ieee80211_hw *hw, u32 changed); | 229 | static int ath5k_config(struct ieee80211_hw *hw, u32 changed); |
230 | static int ath5k_config_interface(struct ieee80211_hw *hw, | ||
231 | struct ieee80211_vif *vif, | ||
232 | struct ieee80211_if_conf *conf); | ||
233 | static void ath5k_configure_filter(struct ieee80211_hw *hw, | 230 | static void ath5k_configure_filter(struct ieee80211_hw *hw, |
234 | unsigned int changed_flags, | 231 | unsigned int changed_flags, |
235 | unsigned int *new_flags, | 232 | unsigned int *new_flags, |
@@ -259,7 +256,6 @@ static const struct ieee80211_ops ath5k_hw_ops = { | |||
259 | .add_interface = ath5k_add_interface, | 256 | .add_interface = ath5k_add_interface, |
260 | .remove_interface = ath5k_remove_interface, | 257 | .remove_interface = ath5k_remove_interface, |
261 | .config = ath5k_config, | 258 | .config = ath5k_config, |
262 | .config_interface = ath5k_config_interface, | ||
263 | .configure_filter = ath5k_configure_filter, | 259 | .configure_filter = ath5k_configure_filter, |
264 | .set_key = ath5k_set_key, | 260 | .set_key = ath5k_set_key, |
265 | .get_stats = ath5k_get_stats, | 261 | .get_stats = ath5k_get_stats, |
@@ -520,6 +516,7 @@ ath5k_pci_probe(struct pci_dev *pdev, | |||
520 | IEEE80211_HW_NOISE_DBM; | 516 | IEEE80211_HW_NOISE_DBM; |
521 | 517 | ||
522 | hw->wiphy->interface_modes = | 518 | hw->wiphy->interface_modes = |
519 | BIT(NL80211_IFTYPE_AP) | | ||
523 | BIT(NL80211_IFTYPE_STATION) | | 520 | BIT(NL80211_IFTYPE_STATION) | |
524 | BIT(NL80211_IFTYPE_ADHOC) | | 521 | BIT(NL80211_IFTYPE_ADHOC) | |
525 | BIT(NL80211_IFTYPE_MESH_POINT); | 522 | BIT(NL80211_IFTYPE_MESH_POINT); |
@@ -1282,7 +1279,7 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) | |||
1282 | ieee80211_get_hdrlen_from_skb(skb), AR5K_PKT_TYPE_NORMAL, | 1279 | ieee80211_get_hdrlen_from_skb(skb), AR5K_PKT_TYPE_NORMAL, |
1283 | (sc->power_level * 2), | 1280 | (sc->power_level * 2), |
1284 | hw_rate, | 1281 | hw_rate, |
1285 | info->control.rates[0].count, keyidx, 0, flags, | 1282 | info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags, |
1286 | cts_rate, duration); | 1283 | cts_rate, duration); |
1287 | if (ret) | 1284 | if (ret) |
1288 | goto err_unmap; | 1285 | goto err_unmap; |
@@ -1742,35 +1739,6 @@ ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb, | |||
1742 | } | 1739 | } |
1743 | } | 1740 | } |
1744 | 1741 | ||
1745 | static void ath5k_tasklet_beacon(unsigned long data) | ||
1746 | { | ||
1747 | struct ath5k_softc *sc = (struct ath5k_softc *) data; | ||
1748 | |||
1749 | /* | ||
1750 | * Software beacon alert--time to send a beacon. | ||
1751 | * | ||
1752 | * In IBSS mode we use this interrupt just to | ||
1753 | * keep track of the next TBTT (target beacon | ||
1754 | * transmission time) in order to detect wether | ||
1755 | * automatic TSF updates happened. | ||
1756 | */ | ||
1757 | if (sc->opmode == NL80211_IFTYPE_ADHOC) { | ||
1758 | /* XXX: only if VEOL suppported */ | ||
1759 | u64 tsf = ath5k_hw_get_tsf64(sc->ah); | ||
1760 | sc->nexttbtt += sc->bintval; | ||
1761 | ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, | ||
1762 | "SWBA nexttbtt: %x hw_tu: %x " | ||
1763 | "TSF: %llx\n", | ||
1764 | sc->nexttbtt, | ||
1765 | TSF_TO_TU(tsf), | ||
1766 | (unsigned long long) tsf); | ||
1767 | } else { | ||
1768 | spin_lock(&sc->block); | ||
1769 | ath5k_beacon_send(sc); | ||
1770 | spin_unlock(&sc->block); | ||
1771 | } | ||
1772 | } | ||
1773 | |||
1774 | static void | 1742 | static void |
1775 | ath5k_tasklet_rx(unsigned long data) | 1743 | ath5k_tasklet_rx(unsigned long data) |
1776 | { | 1744 | { |
@@ -2041,7 +2009,8 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) | |||
2041 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | 2009 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); |
2042 | struct ath5k_hw *ah = sc->ah; | 2010 | struct ath5k_hw *ah = sc->ah; |
2043 | struct ath5k_desc *ds; | 2011 | struct ath5k_desc *ds; |
2044 | int ret, antenna = 0; | 2012 | int ret = 0; |
2013 | u8 antenna; | ||
2045 | u32 flags; | 2014 | u32 flags; |
2046 | 2015 | ||
2047 | bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len, | 2016 | bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len, |
@@ -2055,23 +2024,35 @@ ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) | |||
2055 | } | 2024 | } |
2056 | 2025 | ||
2057 | ds = bf->desc; | 2026 | ds = bf->desc; |
2027 | antenna = ah->ah_tx_ant; | ||
2058 | 2028 | ||
2059 | flags = AR5K_TXDESC_NOACK; | 2029 | flags = AR5K_TXDESC_NOACK; |
2060 | if (sc->opmode == NL80211_IFTYPE_ADHOC && ath5k_hw_hasveol(ah)) { | 2030 | if (sc->opmode == NL80211_IFTYPE_ADHOC && ath5k_hw_hasveol(ah)) { |
2061 | ds->ds_link = bf->daddr; /* self-linked */ | 2031 | ds->ds_link = bf->daddr; /* self-linked */ |
2062 | flags |= AR5K_TXDESC_VEOL; | 2032 | flags |= AR5K_TXDESC_VEOL; |
2063 | /* | 2033 | } else |
2064 | * Let hardware handle antenna switching if txantenna is not set | ||
2065 | */ | ||
2066 | } else { | ||
2067 | ds->ds_link = 0; | 2034 | ds->ds_link = 0; |
2068 | /* | 2035 | |
2069 | * Switch antenna every 4 beacons if txantenna is not set | 2036 | /* |
2070 | * XXX assumes two antennas | 2037 | * If we use multiple antennas on AP and use |
2071 | */ | 2038 | * the Sectored AP scenario, switch antenna every |
2072 | if (antenna == 0) | 2039 | * 4 beacons to make sure everybody hears our AP. |
2073 | antenna = sc->bsent & 4 ? 2 : 1; | 2040 | * When a client tries to associate, hw will keep |
2074 | } | 2041 | * track of the tx antenna to be used for this client |
2042 | * automaticaly, based on ACKed packets. | ||
2043 | * | ||
2044 | * Note: AP still listens and transmits RTS on the | ||
2045 | * default antenna which is supposed to be an omni. | ||
2046 | * | ||
2047 | * Note2: On sectored scenarios it's possible to have | ||
2048 | * multiple antennas (1omni -the default- and 14 sectors) | ||
2049 | * so if we choose to actually support this mode we need | ||
2050 | * to allow user to set how many antennas we have and tweak | ||
2051 | * the code below to send beacons on all of them. | ||
2052 | */ | ||
2053 | if (ah->ah_ant_mode == AR5K_ANTMODE_SECTOR_AP) | ||
2054 | antenna = sc->bsent & 4 ? 2 : 1; | ||
2055 | |||
2075 | 2056 | ||
2076 | /* FIXME: If we are in g mode and rate is a CCK rate | 2057 | /* FIXME: If we are in g mode and rate is a CCK rate |
2077 | * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta | 2058 | * subtract ah->ah_txpower.txp_cck_ofdm_pwr_delta |
@@ -2124,7 +2105,7 @@ ath5k_beacon_send(struct ath5k_softc *sc) | |||
2124 | sc->bmisscount++; | 2105 | sc->bmisscount++; |
2125 | ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, | 2106 | ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, |
2126 | "missed %u consecutive beacons\n", sc->bmisscount); | 2107 | "missed %u consecutive beacons\n", sc->bmisscount); |
2127 | if (sc->bmisscount > 3) { /* NB: 3 is a guess */ | 2108 | if (sc->bmisscount > 10) { /* NB: 10 is a guess */ |
2128 | ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, | 2109 | ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, |
2129 | "stuck beacon time (%u missed)\n", | 2110 | "stuck beacon time (%u missed)\n", |
2130 | sc->bmisscount); | 2111 | sc->bmisscount); |
@@ -2145,10 +2126,12 @@ ath5k_beacon_send(struct ath5k_softc *sc) | |||
2145 | * are still pending on the queue. | 2126 | * are still pending on the queue. |
2146 | */ | 2127 | */ |
2147 | if (unlikely(ath5k_hw_stop_tx_dma(ah, sc->bhalq))) { | 2128 | if (unlikely(ath5k_hw_stop_tx_dma(ah, sc->bhalq))) { |
2148 | ATH5K_WARN(sc, "beacon queue %u didn't stop?\n", sc->bhalq); | 2129 | ATH5K_WARN(sc, "beacon queue %u didn't start/stop ?\n", sc->bhalq); |
2149 | /* NB: hw still stops DMA, so proceed */ | 2130 | /* NB: hw still stops DMA, so proceed */ |
2150 | } | 2131 | } |
2151 | 2132 | ||
2133 | /* Note: Beacon buffer is updated on beacon_update when mac80211 | ||
2134 | * calls config_interface */ | ||
2152 | ath5k_hw_set_txdp(ah, sc->bhalq, bf->daddr); | 2135 | ath5k_hw_set_txdp(ah, sc->bhalq, bf->daddr); |
2153 | ath5k_hw_start_tx_dma(ah, sc->bhalq); | 2136 | ath5k_hw_start_tx_dma(ah, sc->bhalq); |
2154 | ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "TXDP[%u] = %llx (%p)\n", | 2137 | ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "TXDP[%u] = %llx (%p)\n", |
@@ -2305,6 +2288,35 @@ ath5k_beacon_config(struct ath5k_softc *sc) | |||
2305 | ath5k_hw_set_imr(ah, sc->imask); | 2288 | ath5k_hw_set_imr(ah, sc->imask); |
2306 | } | 2289 | } |
2307 | 2290 | ||
2291 | static void ath5k_tasklet_beacon(unsigned long data) | ||
2292 | { | ||
2293 | struct ath5k_softc *sc = (struct ath5k_softc *) data; | ||
2294 | |||
2295 | /* | ||
2296 | * Software beacon alert--time to send a beacon. | ||
2297 | * | ||
2298 | * In IBSS mode we use this interrupt just to | ||
2299 | * keep track of the next TBTT (target beacon | ||
2300 | * transmission time) in order to detect wether | ||
2301 | * automatic TSF updates happened. | ||
2302 | */ | ||
2303 | if (sc->opmode == NL80211_IFTYPE_ADHOC) { | ||
2304 | /* XXX: only if VEOL suppported */ | ||
2305 | u64 tsf = ath5k_hw_get_tsf64(sc->ah); | ||
2306 | sc->nexttbtt += sc->bintval; | ||
2307 | ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, | ||
2308 | "SWBA nexttbtt: %x hw_tu: %x " | ||
2309 | "TSF: %llx\n", | ||
2310 | sc->nexttbtt, | ||
2311 | TSF_TO_TU(tsf), | ||
2312 | (unsigned long long) tsf); | ||
2313 | } else { | ||
2314 | spin_lock(&sc->block); | ||
2315 | ath5k_beacon_send(sc); | ||
2316 | spin_unlock(&sc->block); | ||
2317 | } | ||
2318 | } | ||
2319 | |||
2308 | 2320 | ||
2309 | /********************\ | 2321 | /********************\ |
2310 | * Interrupt handling * | 2322 | * Interrupt handling * |
@@ -2509,7 +2521,7 @@ ath5k_intr(int irq, void *dev_id) | |||
2509 | ath5k_hw_update_mib_counters(ah, &sc->ll_stats); | 2521 | ath5k_hw_update_mib_counters(ah, &sc->ll_stats); |
2510 | } | 2522 | } |
2511 | } | 2523 | } |
2512 | } while (ath5k_hw_is_intr_pending(ah) && counter-- > 0); | 2524 | } while (ath5k_hw_is_intr_pending(ah) && --counter > 0); |
2513 | 2525 | ||
2514 | if (unlikely(!counter)) | 2526 | if (unlikely(!counter)) |
2515 | ATH5K_WARN(sc, "too many interrupts, giving up for now\n"); | 2527 | ATH5K_WARN(sc, "too many interrupts, giving up for now\n"); |
@@ -2751,56 +2763,47 @@ static int | |||
2751 | ath5k_config(struct ieee80211_hw *hw, u32 changed) | 2763 | ath5k_config(struct ieee80211_hw *hw, u32 changed) |
2752 | { | 2764 | { |
2753 | struct ath5k_softc *sc = hw->priv; | 2765 | struct ath5k_softc *sc = hw->priv; |
2766 | struct ath5k_hw *ah = sc->ah; | ||
2754 | struct ieee80211_conf *conf = &hw->conf; | 2767 | struct ieee80211_conf *conf = &hw->conf; |
2755 | int ret; | 2768 | int ret = 0; |
2756 | 2769 | ||
2757 | mutex_lock(&sc->lock); | 2770 | mutex_lock(&sc->lock); |
2758 | 2771 | ||
2759 | sc->bintval = conf->beacon_int; | 2772 | sc->bintval = conf->beacon_int; |
2760 | sc->power_level = conf->power_level; | ||
2761 | 2773 | ||
2762 | ret = ath5k_chan_set(sc, conf->channel); | 2774 | ret = ath5k_chan_set(sc, conf->channel); |
2775 | if (ret < 0) | ||
2776 | return ret; | ||
2763 | 2777 | ||
2764 | mutex_unlock(&sc->lock); | 2778 | if ((changed & IEEE80211_CONF_CHANGE_POWER) && |
2765 | return ret; | 2779 | (sc->power_level != conf->power_level)) { |
2766 | } | 2780 | sc->power_level = conf->power_level; |
2767 | |||
2768 | static int | ||
2769 | ath5k_config_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | ||
2770 | struct ieee80211_if_conf *conf) | ||
2771 | { | ||
2772 | struct ath5k_softc *sc = hw->priv; | ||
2773 | struct ath5k_hw *ah = sc->ah; | ||
2774 | int ret = 0; | ||
2775 | 2781 | ||
2776 | mutex_lock(&sc->lock); | 2782 | /* Half dB steps */ |
2777 | if (sc->vif != vif) { | 2783 | ath5k_hw_set_txpower_limit(ah, (conf->power_level * 2)); |
2778 | ret = -EIO; | ||
2779 | goto unlock; | ||
2780 | } | ||
2781 | if (conf->changed & IEEE80211_IFCC_BSSID && conf->bssid) { | ||
2782 | /* Cache for later use during resets */ | ||
2783 | memcpy(ah->ah_bssid, conf->bssid, ETH_ALEN); | ||
2784 | /* XXX: assoc id is set to 0 for now, mac80211 doesn't have | ||
2785 | * a clean way of letting us retrieve this yet. */ | ||
2786 | ath5k_hw_set_associd(ah, ah->ah_bssid, 0); | ||
2787 | mmiowb(); | ||
2788 | } | ||
2789 | if (conf->changed & IEEE80211_IFCC_BEACON && | ||
2790 | (vif->type == NL80211_IFTYPE_ADHOC || | ||
2791 | vif->type == NL80211_IFTYPE_MESH_POINT || | ||
2792 | vif->type == NL80211_IFTYPE_AP)) { | ||
2793 | struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); | ||
2794 | if (!beacon) { | ||
2795 | ret = -ENOMEM; | ||
2796 | goto unlock; | ||
2797 | } | ||
2798 | ath5k_beacon_update(sc, beacon); | ||
2799 | } | 2784 | } |
2800 | 2785 | ||
2801 | unlock: | 2786 | /* TODO: |
2787 | * 1) Move this on config_interface and handle each case | ||
2788 | * separately eg. when we have only one STA vif, use | ||
2789 | * AR5K_ANTMODE_SINGLE_AP | ||
2790 | * | ||
2791 | * 2) Allow the user to change antenna mode eg. when only | ||
2792 | * one antenna is present | ||
2793 | * | ||
2794 | * 3) Allow the user to set default/tx antenna when possible | ||
2795 | * | ||
2796 | * 4) Default mode should handle 90% of the cases, together | ||
2797 | * with fixed a/b and single AP modes we should be able to | ||
2798 | * handle 99%. Sectored modes are extreme cases and i still | ||
2799 | * haven't found a usage for them. If we decide to support them, | ||
2800 | * then we must allow the user to set how many tx antennas we | ||
2801 | * have available | ||
2802 | */ | ||
2803 | ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_DEFAULT); | ||
2804 | |||
2802 | mutex_unlock(&sc->lock); | 2805 | mutex_unlock(&sc->lock); |
2803 | return ret; | 2806 | return 0; |
2804 | } | 2807 | } |
2805 | 2808 | ||
2806 | #define SUPPORTED_FIF_FLAGS \ | 2809 | #define SUPPORTED_FIF_FLAGS \ |
@@ -3083,11 +3086,40 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw, | |||
3083 | u32 changes) | 3086 | u32 changes) |
3084 | { | 3087 | { |
3085 | struct ath5k_softc *sc = hw->priv; | 3088 | struct ath5k_softc *sc = hw->priv; |
3089 | struct ath5k_hw *ah = sc->ah; | ||
3090 | |||
3091 | mutex_lock(&sc->lock); | ||
3092 | if (WARN_ON(sc->vif != vif)) | ||
3093 | goto unlock; | ||
3094 | |||
3095 | if (changes & BSS_CHANGED_BSSID) { | ||
3096 | /* Cache for later use during resets */ | ||
3097 | memcpy(ah->ah_bssid, bss_conf->bssid, ETH_ALEN); | ||
3098 | /* XXX: assoc id is set to 0 for now, mac80211 doesn't have | ||
3099 | * a clean way of letting us retrieve this yet. */ | ||
3100 | ath5k_hw_set_associd(ah, ah->ah_bssid, 0); | ||
3101 | mmiowb(); | ||
3102 | } | ||
3103 | |||
3104 | if (changes & BSS_CHANGED_BEACON_INT) | ||
3105 | sc->bintval = bss_conf->beacon_int; | ||
3106 | |||
3086 | if (changes & BSS_CHANGED_ASSOC) { | 3107 | if (changes & BSS_CHANGED_ASSOC) { |
3087 | mutex_lock(&sc->lock); | ||
3088 | sc->assoc = bss_conf->assoc; | 3108 | sc->assoc = bss_conf->assoc; |
3089 | if (sc->opmode == NL80211_IFTYPE_STATION) | 3109 | if (sc->opmode == NL80211_IFTYPE_STATION) |
3090 | set_beacon_filter(hw, sc->assoc); | 3110 | set_beacon_filter(hw, sc->assoc); |
3091 | mutex_unlock(&sc->lock); | ||
3092 | } | 3111 | } |
3112 | |||
3113 | if (changes & BSS_CHANGED_BEACON && | ||
3114 | (vif->type == NL80211_IFTYPE_ADHOC || | ||
3115 | vif->type == NL80211_IFTYPE_MESH_POINT || | ||
3116 | vif->type == NL80211_IFTYPE_AP)) { | ||
3117 | struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); | ||
3118 | |||
3119 | if (beacon) | ||
3120 | ath5k_beacon_update(sc, beacon); | ||
3121 | } | ||
3122 | |||
3123 | unlock: | ||
3124 | mutex_unlock(&sc->lock); | ||
3093 | } | 3125 | } |