ath9k: implement rx/tx antenna control
authorFelix Fietkau <nbd@openwrt.org>
Sun, 28 Aug 2011 18:38:24 +0000 (18:38 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Sun, 28 Aug 2011 18:38:24 +0000 (18:38 +0000)
SVN-Revision: 28107

package/mac80211/patches/513-ath9k_channelbw_debugfs.patch
package/mac80211/patches/531-ath9k_cur_txpower.patch
package/mac80211/patches/580-ath9k_remove_common_chainmask.patch [new file with mode: 0644]
package/mac80211/patches/581-ath9k_merge_reset_functions.patch [new file with mode: 0644]
package/mac80211/patches/582-ath9k_antdiv_comb_cb.patch [new file with mode: 0644]
package/mac80211/patches/583-ath9k_antenna_control.patch [new file with mode: 0644]

index 6ae1323d2b4ceff60b0dbb739ae72597caae401e..fedd17f51dfae526ebf62ad25e3b2978034d150f 100644 (file)
  }
 --- a/drivers/net/wireless/ath/ath9k/main.c
 +++ b/drivers/net/wireless/ath/ath9k/main.c
-@@ -226,6 +226,7 @@ static int ath_set_channel(struct ath_so
-       bool fastcc = true, stopped;
-       struct ieee80211_channel *channel = hw->conf.channel;
-       struct ath9k_hw_cal_data *caldata = NULL;
-+      u32 oldflags;
-       int r;
+@@ -1654,9 +1654,10 @@ static int ath9k_config(struct ieee80211
  
-       if (sc->sc_flags & SC_OP_INVALID)
-@@ -268,6 +269,24 @@ static int ath_set_channel(struct ath_so
-       if (!stopped || !(sc->sc_flags & SC_OP_OFFCHANNEL))
-               fastcc = false;
+       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+               struct ieee80211_channel *curchan = hw->conf.channel;
+-              struct ath9k_channel old_chan;
++              struct ath9k_channel old_chan, *hchan;
+               int pos = curchan->hw_value;
+               int old_pos = -1;
++              u32 oldflags;
+               unsigned long flags;
  
-+      oldflags = hchan->channelFlags;
-+      switch (sc->chan_bw) {
-+      case 5:
-+              hchan->channelFlags &= ~CHANNEL_HALF;
-+              hchan->channelFlags |= CHANNEL_QUARTER;
-+              break;
-+      case 10:
-+              hchan->channelFlags &= ~CHANNEL_QUARTER;
-+              hchan->channelFlags |= CHANNEL_HALF;
-+              break;
-+      default:
-+              hchan->channelFlags &= ~(CHANNEL_HALF | CHANNEL_QUARTER);
-+              break;
-+      }
-+
-+      if (oldflags != hchan->channelFlags)
-+              fastcc = false;
+               if (ah->curchan)
+@@ -1709,7 +1710,23 @@ static int ath9k_config(struct ieee80211
+                       memset(&sc->survey[pos], 0, sizeof(struct survey_info));
+               }
+-              if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) {
++              hchan = &sc->sc_ah->channels[pos];
++              oldflags = hchan->channelFlags;
++              switch (sc->chan_bw) {
++              case 5:
++                      hchan->channelFlags &= ~CHANNEL_HALF;
++                      hchan->channelFlags |= CHANNEL_QUARTER;
++                      break;
++              case 10:
++                      hchan->channelFlags &= ~CHANNEL_QUARTER;
++                      hchan->channelFlags |= CHANNEL_HALF;
++                      break;
++              default:
++                      hchan->channelFlags &= ~(CHANNEL_HALF | CHANNEL_QUARTER);
++                      break;
++              }
 +
-       if (!(sc->sc_flags & SC_OP_OFFCHANNEL))
-               caldata = &sc->caldata;
++              if (ath_set_channel(sc, hw, hchan) < 0) {
+                       ath_err(common, "Unable to set channel\n");
+                       mutex_unlock(&sc->mutex);
+                       return -EINVAL;
+--- a/drivers/net/wireless/ath/ath9k/hw.c
++++ b/drivers/net/wireless/ath/ath9k/hw.c
+@@ -1504,6 +1504,10 @@ int ath9k_hw_reset(struct ath_hw *ah, st
+       }
+       ah->noise = ath9k_hw_getchan_noise(ah, chan);
  
++      if (!ah->curchan || ((ah->curchan->channelFlags ^ chan->channelFlags) &
++          (CHANNEL_HALF | CHANNEL_QUARTER)))
++              bChannelChange = false;
++
+       if (bChannelChange &&
+           (ah->chip_fullsleep != true) &&
+           (ah->curchan != NULL) &&
index f0de43eb47e649c0938d4cf47cc3f484f2e74123..8e104d9fee9eccf53da370e2b98bea9c52a5092c 100644 (file)
@@ -1,6 +1,6 @@
 --- a/drivers/net/wireless/ath/ath9k/main.c
 +++ b/drivers/net/wireless/ath/ath9k/main.c
-@@ -1734,6 +1734,8 @@ static int ath9k_config(struct ieee80211
+@@ -1732,6 +1732,8 @@ static int ath9k_config(struct ieee80211
                        return -EINVAL;
                }
  
@@ -9,7 +9,7 @@
                /*
                 * The most recent snapshot of channel->noisefloor for the old
                 * channel is only available after the hardware reset. Copy it to
-@@ -1751,6 +1753,7 @@ static int ath9k_config(struct ieee80211
+@@ -1749,6 +1751,7 @@ static int ath9k_config(struct ieee80211
                ath9k_cmn_update_txpow(ah, sc->curtxpow,
                                       sc->config.txpowlimit, &sc->curtxpow);
                ath9k_ps_restore(sc);
diff --git a/package/mac80211/patches/580-ath9k_remove_common_chainmask.patch b/package/mac80211/patches/580-ath9k_remove_common_chainmask.patch
new file mode 100644 (file)
index 0000000..1164fc2
--- /dev/null
@@ -0,0 +1,273 @@
+--- a/drivers/net/wireless/ath/ath.h
++++ b/drivers/net/wireless/ath/ath.h
+@@ -140,9 +140,6 @@ struct ath_common {
+       u8 curbssid[ETH_ALEN];
+       u8 bssidmask[ETH_ALEN];
+-      u8 tx_chainmask;
+-      u8 rx_chainmask;
+-
+       u32 rx_bufsize;
+       u32 keymax;
+--- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c
++++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c
+@@ -113,7 +113,7 @@ static int ar9003_get_training_power_5g(
+       if (delta > scale)
+               return -1;
+-      switch (get_streams(common->tx_chainmask)) {
++      switch (get_streams(ah->txchainmask)) {
+       case 1:
+               delta = 6;
+               break;
+@@ -126,7 +126,7 @@ static int ar9003_get_training_power_5g(
+       default:
+               delta = 0;
+               ath_dbg(common, ATH_DBG_CALIBRATE,
+-              "Invalid tx-chainmask: %u\n", common->tx_chainmask);
++              "Invalid tx-chainmask: %u\n", ah->txchainmask);
+       }
+       power += delta;
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -318,7 +318,6 @@ static void ath_paprd_activate(struct at
+ {
+       struct ath_hw *ah = sc->sc_ah;
+       struct ath9k_hw_cal_data *caldata = ah->caldata;
+-      struct ath_common *common = ath9k_hw_common(ah);
+       int chain;
+       if (!caldata || !caldata->paprd_done)
+@@ -327,7 +326,7 @@ static void ath_paprd_activate(struct at
+       ath9k_ps_wakeup(sc);
+       ar9003_paprd_enable(ah, false);
+       for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
+-              if (!(common->tx_chainmask & BIT(chain)))
++              if (!(ah->txchainmask & BIT(chain)))
+                       continue;
+               ar9003_paprd_populate_single_table(ah, caldata, chain);
+@@ -414,7 +413,7 @@ void ath_paprd_calibrate(struct work_str
+       memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN);
+       for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
+-              if (!(common->tx_chainmask & BIT(chain)))
++              if (!(ah->txchainmask & BIT(chain)))
+                       continue;
+               chain_ok = 0;
+@@ -535,7 +534,7 @@ void ath_ani_calibrate(unsigned long dat
+       if (longcal || shortcal) {
+               common->ani.caldone =
+                       ath9k_hw_calibrate(ah, ah->curchan,
+-                                              common->rx_chainmask, longcal);
++                                              ah->rxchainmask, longcal);
+       }
+       ath9k_ps_restore(sc);
+--- a/drivers/net/wireless/ath/ath9k/hw.c
++++ b/drivers/net/wireless/ath/ath9k/hw.c
+@@ -1485,9 +1485,6 @@ int ath9k_hw_reset(struct ath_hw *ah, st
+       u64 tsf = 0;
+       int i, r;
+-      ah->txchainmask = common->tx_chainmask;
+-      ah->rxchainmask = common->rx_chainmask;
+-
+       if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE))
+               return -EIO;
+@@ -2105,6 +2102,8 @@ int ath9k_hw_fill_cap_info(struct ath_hw
+       pCap->tx_chainmask = fixup_chainmask(chip_chainmask, pCap->tx_chainmask);
+       pCap->rx_chainmask = fixup_chainmask(chip_chainmask, pCap->rx_chainmask);
++      ah->txchainmask = pCap->tx_chainmask;
++      ah->rxchainmask = pCap->rx_chainmask;
+       ah->misc_mode |= AR_PCU_MIC_NEW_LOC_ENA;
+--- a/drivers/net/wireless/ath/ath9k/xmit.c
++++ b/drivers/net/wireless/ath/ath9k/xmit.c
+@@ -1655,7 +1655,7 @@ u8 ath_txchainmask_reduction(struct ath_
+ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf, int len)
+ {
+-      struct ath_common *common = ath9k_hw_common(sc->sc_ah);
++      struct ath_hw *ah = sc->sc_ah;
+       struct ath9k_11n_rate_series series[4];
+       struct sk_buff *skb;
+       struct ieee80211_tx_info *tx_info;
+@@ -1715,7 +1715,7 @@ static void ath_buf_set_rate(struct ath_
+                       /* MCS rates */
+                       series[i].Rate = rix | 0x80;
+                       series[i].ChSel = ath_txchainmask_reduction(sc,
+-                                      common->tx_chainmask, series[i].Rate);
++                                      ah->txchainmask, series[i].Rate);
+                       series[i].PktDuration = ath_pkt_duration(sc, rix, len,
+                                is_40, is_sgi, is_sp);
+                       if (rix < 8 && (tx_info->flags & IEEE80211_TX_CTL_STBC))
+@@ -1740,10 +1740,10 @@ static void ath_buf_set_rate(struct ath_
+               }
+               if (bf->bf_state.bfs_paprd)
+-                      series[i].ChSel = common->tx_chainmask;
++                      series[i].ChSel = ah->txchainmask;
+               else
+                       series[i].ChSel = ath_txchainmask_reduction(sc,
+-                                      common->tx_chainmask, series[i].Rate);
++                                      ah->txchainmask, series[i].Rate);
+               series[i].PktDuration = ath9k_hw_computetxtime(sc->sc_ah,
+                       phy, rate->bitrate * 100, len, rix, is_sp);
+--- a/drivers/net/wireless/ath/ath9k/beacon.c
++++ b/drivers/net/wireless/ath/ath9k/beacon.c
+@@ -107,7 +107,7 @@ static void ath_beacon_setup(struct ath_
+       series[0].Tries = 1;
+       series[0].Rate = rate;
+       series[0].ChSel = ath_txchainmask_reduction(sc,
+-                      common->tx_chainmask, series[0].Rate);
++                      ah->txchainmask, series[0].Rate);
+       series[0].RateFlags = (ctsrate) ? ATH9K_RATESERIES_RTS_CTS : 0;
+       ath9k_hw_set11n_ratescenario(ah, ds, ds, 0, ctsrate, ctsduration,
+                                    series, 4, 0);
+--- a/drivers/net/wireless/ath/ath9k/debug.c
++++ b/drivers/net/wireless/ath/ath9k/debug.c
+@@ -95,11 +95,11 @@ static ssize_t read_file_tx_chainmask(st
+                            size_t count, loff_t *ppos)
+ {
+       struct ath_softc *sc = file->private_data;
+-      struct ath_common *common = ath9k_hw_common(sc->sc_ah);
++      struct ath_hw *ah = sc->sc_ah;
+       char buf[32];
+       unsigned int len;
+-      len = sprintf(buf, "0x%08x\n", common->tx_chainmask);
++      len = sprintf(buf, "0x%08x\n", ah->txchainmask);
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ }
+@@ -107,7 +107,7 @@ static ssize_t write_file_tx_chainmask(s
+                            size_t count, loff_t *ppos)
+ {
+       struct ath_softc *sc = file->private_data;
+-      struct ath_common *common = ath9k_hw_common(sc->sc_ah);
++      struct ath_hw *ah = sc->sc_ah;
+       unsigned long mask;
+       char buf[32];
+       ssize_t len;
+@@ -120,8 +120,8 @@ static ssize_t write_file_tx_chainmask(s
+       if (strict_strtoul(buf, 0, &mask))
+               return -EINVAL;
+-      common->tx_chainmask = mask;
+-      sc->sc_ah->caps.tx_chainmask = mask;
++      ah->txchainmask = mask;
++      ah->caps.tx_chainmask = mask;
+       return count;
+ }
+@@ -138,11 +138,11 @@ static ssize_t read_file_rx_chainmask(st
+                            size_t count, loff_t *ppos)
+ {
+       struct ath_softc *sc = file->private_data;
+-      struct ath_common *common = ath9k_hw_common(sc->sc_ah);
++      struct ath_hw *ah = sc->sc_ah;
+       char buf[32];
+       unsigned int len;
+-      len = sprintf(buf, "0x%08x\n", common->rx_chainmask);
++      len = sprintf(buf, "0x%08x\n", ah->rxchainmask);
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ }
+@@ -150,7 +150,7 @@ static ssize_t write_file_rx_chainmask(s
+                            size_t count, loff_t *ppos)
+ {
+       struct ath_softc *sc = file->private_data;
+-      struct ath_common *common = ath9k_hw_common(sc->sc_ah);
++      struct ath_hw *ah = sc->sc_ah;
+       unsigned long mask;
+       char buf[32];
+       ssize_t len;
+@@ -163,8 +163,8 @@ static ssize_t write_file_rx_chainmask(s
+       if (strict_strtoul(buf, 0, &mask))
+               return -EINVAL;
+-      common->rx_chainmask = mask;
+-      sc->sc_ah->caps.rx_chainmask = mask;
++      ah->rxchainmask = mask;
++      ah->caps.rx_chainmask = mask;
+       return count;
+ }
+--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
++++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+@@ -509,8 +509,8 @@ static void setup_ht_cap(struct ath9k_ht
+       memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
+       /* ath9k_htc supports only 1 or 2 stream devices */
+-      tx_streams = ath9k_cmn_count_streams(common->tx_chainmask, 2);
+-      rx_streams = ath9k_cmn_count_streams(common->rx_chainmask, 2);
++      tx_streams = ath9k_cmn_count_streams(priv->ah->txchainmask, 2);
++      rx_streams = ath9k_cmn_count_streams(priv->ah->rxchainmask, 2);
+       ath_dbg(common, ATH_DBG_CONFIG,
+               "TX streams %d, RX streams: %d\n",
+@@ -601,9 +601,6 @@ static void ath9k_init_misc(struct ath9k
+ {
+       struct ath_common *common = ath9k_hw_common(priv->ah);
+-      common->tx_chainmask = priv->ah->caps.tx_chainmask;
+-      common->rx_chainmask = priv->ah->caps.rx_chainmask;
+-
+       memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
+       priv->ah->opmode = NL80211_IFTYPE_STATION;
+--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
++++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+@@ -826,8 +826,7 @@ void ath9k_htc_ani_work(struct work_stru
+               if (longcal || shortcal)
+                       common->ani.caldone =
+                               ath9k_hw_calibrate(ah, ah->curchan,
+-                                                 common->rx_chainmask,
+-                                                 longcal);
++                                                 ah->rxchainmask, longcal);
+               ath9k_htc_ps_restore(priv);
+       }
+--- a/drivers/net/wireless/ath/ath9k/init.c
++++ b/drivers/net/wireless/ath/ath9k/init.c
+@@ -270,8 +270,8 @@ static void setup_ht_cap(struct ath_soft
+       /* set up supported mcs set */
+       memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
+-      tx_streams = ath9k_cmn_count_streams(common->tx_chainmask, max_streams);
+-      rx_streams = ath9k_cmn_count_streams(common->rx_chainmask, max_streams);
++      tx_streams = ath9k_cmn_count_streams(ah->txchainmask, max_streams);
++      rx_streams = ath9k_cmn_count_streams(ah->rxchainmask, max_streams);
+       ath_dbg(common, ATH_DBG_CONFIG,
+               "TX streams %d, RX streams: %d\n",
+@@ -506,9 +506,6 @@ static void ath9k_init_misc(struct ath_s
+               sc->sc_flags |= SC_OP_RXAGGR;
+       }
+-      common->tx_chainmask = sc->sc_ah->caps.tx_chainmask;
+-      common->rx_chainmask = sc->sc_ah->caps.rx_chainmask;
+-
+       ath9k_hw_set_diversity(sc->sc_ah, true);
+       sc->rx.defant = ath9k_hw_getdefantenna(sc->sc_ah);
+@@ -645,10 +642,8 @@ static void ath9k_init_band_txpower(stru
+ static void ath9k_init_txpower_limits(struct ath_softc *sc)
+ {
+       struct ath_hw *ah = sc->sc_ah;
+-      struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       struct ath9k_channel *curchan = ah->curchan;
+-      ah->txchainmask = common->tx_chainmask;
+       if (ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
+               ath9k_init_band_txpower(sc, IEEE80211_BAND_2GHZ);
+       if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
diff --git a/package/mac80211/patches/581-ath9k_merge_reset_functions.patch b/package/mac80211/patches/581-ath9k_merge_reset_functions.patch
new file mode 100644 (file)
index 0000000..201994d
--- /dev/null
@@ -0,0 +1,343 @@
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -212,83 +212,39 @@ static int ath_update_survey_stats(struc
+       return ret;
+ }
+-/*
+- * 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.
+-*/
+-static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
+-                  struct ath9k_channel *hchan)
++static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx)
+ {
+       struct ath_hw *ah = sc->sc_ah;
+-      struct ath_common *common = ath9k_hw_common(ah);
+-      struct ieee80211_conf *conf = &common->hw->conf;
+-      bool fastcc = true, stopped;
+-      struct ieee80211_channel *channel = hw->conf.channel;
+-      struct ath9k_hw_cal_data *caldata = NULL;
+-      int r;
++      bool ret;
+-      if (sc->sc_flags & SC_OP_INVALID)
+-              return -EIO;
++      ieee80211_stop_queues(sc->hw);
+       sc->hw_busy_count = 0;
+-
+-      del_timer_sync(&common->ani.timer);
+       cancel_work_sync(&sc->paprd_work);
+       cancel_work_sync(&sc->hw_check_work);
+       cancel_delayed_work_sync(&sc->tx_complete_work);
+       cancel_delayed_work_sync(&sc->hw_pll_work);
+-      ath9k_ps_wakeup(sc);
+-
+-      spin_lock_bh(&sc->sc_pcu_lock);
+-
+-      /*
+-       * 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_disable_interrupts(ah);
+-      stopped = ath_drain_all_txq(sc, false);
+-      if (!ath_stoprecv(sc))
+-              stopped = false;
+-
+-      if (!ath9k_hw_check_alive(ah))
+-              stopped = false;
+-
+-      /* XXX: do not flush receive queue here. We don't want
+-       * to flush data frames already in queue because of
+-       * changing channel. */
++      ret = ath_drain_all_txq(sc, retry_tx);
+-      if (!stopped || !(sc->sc_flags & SC_OP_OFFCHANNEL))
+-              fastcc = false;
++      if (!ath_stoprecv(sc))
++              ret = false;
+-      if (!(sc->sc_flags & SC_OP_OFFCHANNEL))
+-              caldata = &sc->caldata;
++      ath_flushrecv(sc);
+-      ath_dbg(common, ATH_DBG_CONFIG,
+-              "(%u MHz) -> (%u MHz), conf_is_ht40: %d fastcc: %d\n",
+-              sc->sc_ah->curchan->channel,
+-              channel->center_freq, conf_is_ht40(conf),
+-              fastcc);
++      return ret;
++}
+-      r = ath9k_hw_reset(ah, hchan, caldata, fastcc);
+-      if (r) {
+-              ath_err(common,
+-                      "Unable to reset channel (%u MHz), reset status %d\n",
+-                      channel->center_freq, r);
+-              goto ps_restore;
+-      }
++static bool ath_complete_reset(struct ath_softc *sc)
++{
++      struct ath_hw *ah = sc->sc_ah;
++      struct ath_common *common = ath9k_hw_common(ah);
+       if (ath_startrecv(sc) != 0) {
+               ath_err(common, "Unable to restart recv logic\n");
+-              r = -EIO;
+-              goto ps_restore;
++              return false;
+       }
+       ath9k_cmn_update_txpow(ah, sc->curtxpow,
+@@ -299,18 +255,84 @@ static int ath_set_channel(struct ath_so
+       if (!(sc->sc_flags & (SC_OP_OFFCHANNEL))) {
+               if (sc->sc_flags & SC_OP_BEACONS)
+                       ath_set_beacon(sc);
++
+               ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
+               ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2);
+               if (!common->disable_ani)
+                       ath_start_ani(common);
+       }
+- ps_restore:
+-      ieee80211_wake_queues(hw);
++      ieee80211_wake_queues(sc->hw);
++
++      return true;
++}
++
++static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan,
++                            bool retry_tx)
++{
++      struct ath_hw *ah = sc->sc_ah;
++      struct ath_common *common = ath9k_hw_common(ah);
++      struct ath9k_hw_cal_data *caldata = NULL;
++      bool fastcc = true;
++      int r;
++
++      if (!(sc->sc_flags & SC_OP_OFFCHANNEL)) {
++              fastcc = false;
++              caldata = &sc->caldata;
++      }
++      if (!hchan) {
++              fastcc = false;
++              hchan = ah->curchan;
++      }
++
++      if (fastcc && !ath9k_hw_check_alive(ah))
++              fastcc = false;
++
++      if (!ath_prepare_reset(sc, retry_tx))
++              fastcc = false;
++
++      ath_dbg(common, ATH_DBG_CONFIG,
++              "Reset to %u MHz, HT40: %d fastcc: %d\n",
++              hchan->channel, !!(hchan->channelFlags & (CHANNEL_HT40MINUS |
++                                                        CHANNEL_HT40PLUS)),
++              fastcc);
++
++      r = ath9k_hw_reset(ah, hchan, caldata, fastcc);
++      if (r) {
++              ath_err(common,
++                      "Unable to reset channel, reset status %d\n", r);
++              return r;
++      }
++
++      if (!ath_complete_reset(sc))
++              return -EIO;
++
++      return 0;
++}
++
++
++/*
++ * 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.
++*/
++static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
++                  struct ath9k_channel *hchan)
++{
++      int r;
++
++      if (sc->sc_flags & SC_OP_INVALID)
++              return -EIO;
++
++      ath9k_ps_wakeup(sc);
++
++      spin_lock_bh(&sc->sc_pcu_lock);
++      r = ath_reset_internal(sc, hchan, false);
+       spin_unlock_bh(&sc->sc_pcu_lock);
+       ath9k_ps_restore(sc);
++
+       return r;
+ }
+@@ -893,28 +915,13 @@ static void ath_radio_enable(struct ath_
+                       channel->center_freq, r);
+       }
+-      ath9k_cmn_update_txpow(ah, sc->curtxpow,
+-                             sc->config.txpowlimit, &sc->curtxpow);
+-      if (ath_startrecv(sc) != 0) {
+-              ath_err(common, "Unable to restart recv logic\n");
+-              goto out;
+-      }
+-      if (sc->sc_flags & SC_OP_BEACONS)
+-              ath_set_beacon(sc);     /* restart beacons */
+-
+-      /* Re-Enable  interrupts */
+-      ath9k_hw_set_interrupts(ah, ah->imask);
+-      ath9k_hw_enable_interrupts(ah);
++      ath_complete_reset(sc);
+       /* Enable LED */
+       ath9k_hw_cfg_output(ah, ah->led_pin,
+                           AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+       ath9k_hw_set_gpio(ah, ah->led_pin, 0);
+-      ieee80211_wake_queues(hw);
+-      ieee80211_queue_delayed_work(hw, &sc->hw_pll_work, HZ/2);
+-
+-out:
+       spin_unlock_bh(&sc->sc_pcu_lock);
+       ath9k_ps_restore(sc);
+@@ -942,13 +949,7 @@ void ath_radio_disable(struct ath_softc 
+               ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
+       }
+-      /* Disable interrupts */
+-      ath9k_hw_disable_interrupts(ah);
+-
+-      ath_drain_all_txq(sc, false);   /* clear pending tx frames */
+-
+-      ath_stoprecv(sc);               /* turn off frame recv */
+-      ath_flushrecv(sc);              /* flush recv queue */
++      ath_prepare_reset(sc, false);
+       if (!ah->curchan)
+               ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
+@@ -970,48 +971,11 @@ void ath_radio_disable(struct ath_softc 
+ int ath_reset(struct ath_softc *sc, bool retry_tx)
+ {
+-      struct ath_hw *ah = sc->sc_ah;
+-      struct ath_common *common = ath9k_hw_common(ah);
+-      struct ieee80211_hw *hw = sc->hw;
+       int r;
+-      sc->hw_busy_count = 0;
+-
+-      /* Stop ANI */
+-
+-      del_timer_sync(&common->ani.timer);
+-
+       ath9k_ps_wakeup(sc);
+-      ieee80211_stop_queues(hw);
+-
+-      ath9k_hw_disable_interrupts(ah);
+-      ath_drain_all_txq(sc, retry_tx);
+-
+-      ath_stoprecv(sc);
+-      ath_flushrecv(sc);
+-
+-      r = ath9k_hw_reset(ah, sc->sc_ah->curchan, ah->caldata, false);
+-      if (r)
+-              ath_err(common,
+-                      "Unable to reset hardware; reset status %d\n", r);
+-
+-      if (ath_startrecv(sc) != 0)
+-              ath_err(common, "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.
+-       */
+-      ath9k_cmn_update_txpow(ah, sc->curtxpow,
+-                             sc->config.txpowlimit, &sc->curtxpow);
+-
+-      if ((sc->sc_flags & SC_OP_BEACONS) || !(sc->sc_flags & (SC_OP_OFFCHANNEL)))
+-              ath_set_beacon(sc);     /* restart beacons */
+-
+-      ath9k_hw_set_interrupts(ah, ah->imask);
+-      ath9k_hw_enable_interrupts(ah);
++      r = ath_reset_internal(sc, NULL, retry_tx);
+       if (retry_tx) {
+               int i;
+@@ -1024,12 +988,6 @@ int ath_reset(struct ath_softc *sc, bool
+               }
+       }
+-      ieee80211_wake_queues(hw);
+-
+-      /* Start ANI */
+-      if (!common->disable_ani)
+-              ath_start_ani(common);
+-
+       ath9k_ps_restore(sc);
+       return r;
+@@ -1081,28 +1039,6 @@ static int ath9k_start(struct ieee80211_
+               goto mutex_unlock;
+       }
+-      /*
+-       * This is needed only to setup initial state
+-       * but it's best done after a reset.
+-       */
+-      ath9k_cmn_update_txpow(ah, sc->curtxpow,
+-                      sc->config.txpowlimit, &sc->curtxpow);
+-
+-      /*
+-       * 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) {
+-              ath_err(common, "Unable to start recv logic\n");
+-              r = -EIO;
+-              spin_unlock_bh(&sc->sc_pcu_lock);
+-              goto mutex_unlock;
+-      }
+-      spin_unlock_bh(&sc->sc_pcu_lock);
+-
+       /* Setup our intr mask. */
+       ah->imask = ATH9K_INT_TX | ATH9K_INT_RXEOL |
+                   ATH9K_INT_RXORN | ATH9K_INT_FATAL |
+@@ -1125,12 +1061,14 @@ static int ath9k_start(struct ieee80211_
+       /* Disable BMISS interrupt when we're not associated */
+       ah->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
+-      ath9k_hw_set_interrupts(ah, ah->imask);
+-      ath9k_hw_enable_interrupts(ah);
+-      ieee80211_wake_queues(hw);
++      if (!ath_complete_reset(sc)) {
++              r = -EIO;
++              spin_unlock_bh(&sc->sc_pcu_lock);
++              goto mutex_unlock;
++      }
+-      ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
++      spin_unlock_bh(&sc->sc_pcu_lock);
+       if ((ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) &&
+           !ah->btcoex_hw.enabled) {
diff --git a/package/mac80211/patches/582-ath9k_antdiv_comb_cb.patch b/package/mac80211/patches/582-ath9k_antdiv_comb_cb.patch
new file mode 100644 (file)
index 0000000..92c8ea4
--- /dev/null
@@ -0,0 +1,50 @@
+--- a/drivers/net/wireless/ath/ath9k/hw-ops.h
++++ b/drivers/net/wireless/ath/ath9k/hw-ops.h
+@@ -121,13 +121,15 @@ static inline void ath9k_hw_set_clrdmask
+ static inline void ath9k_hw_antdiv_comb_conf_get(struct ath_hw *ah,
+               struct ath_hw_antcomb_conf *antconf)
+ {
+-      ath9k_hw_ops(ah)->antdiv_comb_conf_get(ah, antconf);
++      if (ath9k_hw_ops(ah)->antdiv_comb_conf_get)
++              ath9k_hw_ops(ah)->antdiv_comb_conf_get(ah, antconf);
+ }
+ static inline void ath9k_hw_antdiv_comb_conf_set(struct ath_hw *ah,
+               struct ath_hw_antcomb_conf *antconf)
+ {
+-      ath9k_hw_ops(ah)->antdiv_comb_conf_set(ah, antconf);
++      if (ath9k_hw_ops(ah)->antdiv_comb_conf_set)
++              ath9k_hw_ops(ah)->antdiv_comb_conf_set(ah, antconf);
+ }
+ /* Private hardware call ops */
+--- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c
++++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c
+@@ -570,8 +570,10 @@ void ar9002_hw_attach_phy_ops(struct ath
+       priv_ops->compute_pll_control = ar9002_hw_compute_pll_control;
+       priv_ops->do_getnf = ar9002_hw_do_getnf;
+-      ops->antdiv_comb_conf_get = ar9002_hw_antdiv_comb_conf_get;
+-      ops->antdiv_comb_conf_set = ar9002_hw_antdiv_comb_conf_set;
++      if (AR_SREV_9285(ah)) {
++              ops->antdiv_comb_conf_get = ar9002_hw_antdiv_comb_conf_get;
++              ops->antdiv_comb_conf_set = ar9002_hw_antdiv_comb_conf_set;
++      }
+       ar9002_hw_set_nf_limits(ah);
+ }
+--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
++++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+@@ -1283,8 +1283,10 @@ void ar9003_hw_attach_phy_ops(struct ath
+       priv_ops->ani_cache_ini_regs = ar9003_hw_ani_cache_ini_regs;
+       priv_ops->set_radar_params = ar9003_hw_set_radar_params;
+-      ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get;
+-      ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set;
++      if (AR_SREV_9330(ah) || AR_SREV_9485(ah)) {
++              ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get;
++              ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set;
++      }
+       ar9003_hw_set_nf_limits(ah);
+       ar9003_hw_set_radar_conf(ah);
diff --git a/package/mac80211/patches/583-ath9k_antenna_control.patch b/package/mac80211/patches/583-ath9k_antenna_control.patch
new file mode 100644 (file)
index 0000000..a1caa8a
--- /dev/null
@@ -0,0 +1,179 @@
+--- a/drivers/net/wireless/ath/ath9k/init.c
++++ b/drivers/net/wireless/ath/ath9k/init.c
+@@ -652,9 +652,22 @@ static void ath9k_init_txpower_limits(st
+       ah->curchan = curchan;
+ }
++void ath9k_reload_chainmask_settings(struct ath_softc *sc)
++{
++      if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT))
++              return;
++
++      if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
++              setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap);
++      if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
++              setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap);
++}
++
++
+ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
+ {
+-      struct ath_common *common = ath9k_hw_common(sc->sc_ah);
++      struct ath_hw *ah = sc->sc_ah;
++      struct ath_common *common = ath9k_hw_common(ah);
+       hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
+               IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
+@@ -692,6 +705,17 @@ void ath9k_set_hw_capab(struct ath_softc
+       hw->sta_data_size = sizeof(struct ath_node);
+       hw->vif_data_size = sizeof(struct ath_vif);
++      hw->wiphy->available_antennas_rx = BIT(ah->caps.max_rxchains) - 1;
++      hw->wiphy->available_antennas_tx = BIT(ah->caps.max_txchains) - 1;
++
++      /* single chain devices with rx diversity */
++      if (ah->caps.max_rxchains == 1 &&
++          ath9k_hw_ops(ah)->antdiv_comb_conf_get)
++              hw->wiphy->available_antennas_rx = 3;
++
++      sc->ant_rx = hw->wiphy->available_antennas_rx;
++      sc->ant_tx = hw->wiphy->available_antennas_tx;
++
+ #ifdef CONFIG_ATH9K_RATE_CONTROL
+       hw->rate_control_algorithm = "ath9k_rate_control";
+ #endif
+@@ -703,12 +727,7 @@ void ath9k_set_hw_capab(struct ath_softc
+               hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
+                       &sc->sbands[IEEE80211_BAND_5GHZ];
+-      if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
+-              if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
+-                      setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap);
+-              if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
+-                      setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap);
+-      }
++      ath9k_reload_chainmask_settings(sc);
+       SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
+ }
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -652,6 +652,7 @@ struct ath_softc {
+       struct ath_descdma txsdma;
+       struct ath_ant_comb ant_comb;
++      u32 ant_tx, ant_rx;
+ };
+ void ath9k_tasklet(unsigned long data);
+@@ -673,6 +674,7 @@ int ath9k_init_device(u16 devid, struct 
+                   const struct ath_bus_ops *bus_ops);
+ void ath9k_deinit_device(struct ath_softc *sc);
+ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw);
++void ath9k_reload_chainmask_settings(struct ath_softc *sc);
+ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw);
+ bool ath9k_uses_beacons(int type);
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -262,6 +262,22 @@ static bool ath_complete_reset(struct at
+                       ath_start_ani(common);
+       }
++      if (ath9k_hw_ops(ah)->antdiv_comb_conf_get && sc->ant_rx != 3) {
++              struct ath_hw_antcomb_conf div_ant_conf;
++              u8 lna_conf;
++
++              ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf);
++
++              if (sc->ant_rx == 1)
++                      lna_conf = ATH_ANT_DIV_COMB_LNA1;
++              else
++                      lna_conf = ATH_ANT_DIV_COMB_LNA2;
++              div_ant_conf.main_lna_conf = lna_conf;
++              div_ant_conf.alt_lna_conf = lna_conf;
++
++              ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf);
++      }
++
+       ieee80211_wake_queues(sc->hw);
+       return true;
+@@ -2360,6 +2376,59 @@ static int ath9k_get_stats(struct ieee80
+       return 0;
+ }
++static u32 fill_chainmask(u32 cap, u32 new)
++{
++      u32 filled = 0;
++      int i;
++
++      for (i = 0; cap && new; i++, cap >>= 1) {
++              if (!(cap & BIT(0)))
++                      continue;
++
++              if (new & BIT(0))
++                      filled |= BIT(i);
++
++              new >>= 1;
++      }
++
++      return filled;
++}
++
++static int ath9k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
++{
++      struct ath_softc *sc = hw->priv;
++      struct ath_hw *ah = sc->sc_ah;
++
++      if (!rx_ant || !tx_ant)
++              return -EINVAL;
++
++      sc->ant_rx = rx_ant;
++      sc->ant_tx = tx_ant;
++
++      if (ah->caps.rx_chainmask == 1)
++              return 0;
++
++      /* AR9100 runs into calibration issues if not all rx chains are enabled */
++      if (AR_SREV_9100(ah))
++              ah->rxchainmask = 0x7;
++      else
++              ah->rxchainmask = fill_chainmask(ah->caps.rx_chainmask, rx_ant);
++
++      ah->txchainmask = fill_chainmask(ah->caps.tx_chainmask, tx_ant);
++      ath9k_reload_chainmask_settings(sc);
++
++      return 0;
++}
++
++static int ath9k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
++{
++      struct ath_softc *sc = hw->priv;
++
++      *tx_ant = sc->ant_tx;
++      *rx_ant = sc->ant_rx;
++      return 0;
++}
++
+ struct ieee80211_ops ath9k_ops = {
+       .tx                 = ath9k_tx,
+       .start              = ath9k_start,
+@@ -2386,4 +2455,6 @@ struct ieee80211_ops ath9k_ops = {
+       .tx_frames_pending  = ath9k_tx_frames_pending,
+       .tx_last_beacon     = ath9k_tx_last_beacon,
+       .get_stats          = ath9k_get_stats,
++      .set_antenna        = ath9k_set_antenna,
++      .get_antenna        = ath9k_get_antenna,
+ };
+--- a/drivers/net/wireless/ath/ath9k/recv.c
++++ b/drivers/net/wireless/ath/ath9k/recv.c
+@@ -1956,7 +1956,7 @@ int ath_rx_tasklet(struct ath_softc *sc,
+                       ath_rx_ps(sc, skb);
+               spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
+-              if (ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)
++              if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx == 3)
+                       ath_ant_comb_scan(sc, &rs);
+               ieee80211_rx(hw, skb);