ts->desc_id = MS(ads->status1, AR_TxDescId);
ts->ts_tstamp = ads->status4;
ts->ts_status = 0;
-@@ -510,7 +506,11 @@ int ath9k_hw_process_rxdesc_edma(struct
+@@ -440,20 +436,14 @@ int ath9k_hw_process_rxdesc_edma(struct
+ struct ar9003_rxs *rxsp = (struct ar9003_rxs *) buf_addr;
+ unsigned int phyerr;
+
+- /* TODO: byte swap on big endian for ar9300_10 */
+-
+- if (!rxs) {
+- if ((rxsp->status11 & AR_RxDone) == 0)
+- return -EINPROGRESS;
+-
+- if (MS(rxsp->ds_info, AR_DescId) != 0x168c)
+- return -EINVAL;
++ if ((rxsp->status11 & AR_RxDone) == 0)
++ return -EINPROGRESS;
+
+- if ((rxsp->ds_info & (AR_TxRxDesc | AR_CtrlStat)) != 0)
+- return -EINPROGRESS;
++ if (MS(rxsp->ds_info, AR_DescId) != 0x168c)
++ return -EINVAL;
+
+- return 0;
+- }
++ if ((rxsp->ds_info & (AR_TxRxDesc | AR_CtrlStat)) != 0)
++ return -EINPROGRESS;
+
+ rxs->rs_status = 0;
+ rxs->rs_flags = 0;
+@@ -510,7 +500,11 @@ int ath9k_hw_process_rxdesc_edma(struct
*/
if (rxsp->status11 & AR_CRCErr)
rxs->rs_status |= ATH9K_RXERR_CRC;
phyerr = MS(rxsp->status11, AR_PHYErrCode);
/*
* If we reach a point here where AR_PostDelimCRCErr is
-@@ -532,11 +532,7 @@ int ath9k_hw_process_rxdesc_edma(struct
+@@ -532,11 +526,7 @@ int ath9k_hw_process_rxdesc_edma(struct
rxs->rs_status |= ATH9K_RXERR_PHY;
rxs->rs_phyerr = phyerr;
}
#undef TX_SAMP_DBG
}
-@@ -1019,6 +1021,7 @@ void ath_debug_stat_rx(struct ath_softc
-
- sc->debug.stats.rxstats.rs_antenna = rs->rs_antenna;
+@@ -942,27 +944,6 @@ static ssize_t read_file_recv(struct fil
+ PHY_ERR("HT-RATE ERR", ATH9K_PHYERR_HT_RATE_ILLEGAL);
+
+ len += snprintf(buf + len, size - len,
+- "%22s : %10d\n", "RSSI-CTL0",
+- sc->debug.stats.rxstats.rs_rssi_ctl0);
+- len += snprintf(buf + len, size - len,
+- "%22s : %10d\n", "RSSI-CTL1",
+- sc->debug.stats.rxstats.rs_rssi_ctl1);
+- len += snprintf(buf + len, size - len,
+- "%22s : %10d\n", "RSSI-CTL2",
+- sc->debug.stats.rxstats.rs_rssi_ctl2);
+- len += snprintf(buf + len, size - len,
+- "%22s : %10d\n", "RSSI-EXT0",
+- sc->debug.stats.rxstats.rs_rssi_ext0);
+- len += snprintf(buf + len, size - len,
+- "%22s : %10d\n", "RSSI-EXT1",
+- sc->debug.stats.rxstats.rs_rssi_ext1);
+- len += snprintf(buf + len, size - len,
+- "%22s : %10d\n", "RSSI-EXT2",
+- sc->debug.stats.rxstats.rs_rssi_ext2);
+- len += snprintf(buf + len, size - len,
+- "%22s : %10d\n", "Rx Antenna",
+- sc->debug.stats.rxstats.rs_antenna);
+- len += snprintf(buf + len, size - len,
+ "%22s : %10u\n", "RX-Pkts-All",
+ sc->debug.stats.rxstats.rx_pkts_all);
+ len += snprintf(buf + len, size - len,
+@@ -1009,16 +990,7 @@ void ath_debug_stat_rx(struct ath_softc
+ RX_PHY_ERR_INC(rs->rs_phyerr);
+ }
+- sc->debug.stats.rxstats.rs_rssi_ctl0 = rs->rs_rssi_ctl0;
+- sc->debug.stats.rxstats.rs_rssi_ctl1 = rs->rs_rssi_ctl1;
+- sc->debug.stats.rxstats.rs_rssi_ctl2 = rs->rs_rssi_ctl2;
+-
+- sc->debug.stats.rxstats.rs_rssi_ext0 = rs->rs_rssi_ext0;
+- sc->debug.stats.rxstats.rs_rssi_ext1 = rs->rs_rssi_ext1;
+- sc->debug.stats.rxstats.rs_rssi_ext2 = rs->rs_rssi_ext2;
+-
+- sc->debug.stats.rxstats.rs_antenna = rs->rs_antenna;
+-
+#ifdef CONFIG_ATH9K_MAC_DEBUG
spin_lock(&sc->debug.samp_lock);
RX_SAMP_DBG(jiffies) = jiffies;
RX_SAMP_DBG(rssi_ctl0) = rs->rs_rssi_ctl0;
-@@ -1035,6 +1038,8 @@ void ath_debug_stat_rx(struct ath_softc
+@@ -1035,6 +1007,8 @@ void ath_debug_stat_rx(struct ath_softc
sc->debug.rsidx = (sc->debug.rsidx + 1) % ATH_DBG_MAX_SAMPLES;
spin_unlock(&sc->debug.samp_lock);
#undef RX_STAT_INC
#undef RX_PHY_ERR_INC
#undef RX_SAMP_DBG
-@@ -1278,6 +1283,8 @@ static const struct file_operations fops
+@@ -1278,6 +1252,8 @@ static const struct file_operations fops
.llseek = default_llseek,
};
void ath9k_debug_samp_bb_mac(struct ath_softc *sc)
{
#define ATH_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].c)
-@@ -1551,6 +1558,7 @@ static const struct file_operations fops
+@@ -1551,6 +1527,7 @@ static const struct file_operations fops
.llseek = default_llseek,
};
int ath9k_init_debug(struct ath_hw *ah)
{
-@@ -1604,8 +1612,10 @@ int ath9k_init_debug(struct ath_hw *ah)
+@@ -1604,8 +1581,10 @@ int ath9k_init_debug(struct ath_hw *ah)
&fops_base_eeprom);
debugfs_create_file("modal_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc,
&fops_modal_eeprom);
sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask);
--- a/drivers/net/wireless/ath/ath9k/debug.h
+++ b/drivers/net/wireless/ath/ath9k/debug.h
-@@ -235,16 +235,17 @@ struct ath9k_debug {
+@@ -165,13 +165,6 @@ struct ath_rx_stats {
+ u32 post_delim_crc_err;
+ u32 decrypt_busy_err;
+ u32 phy_err_stats[ATH9K_PHYERR_MAX];
+- int8_t rs_rssi_ctl0;
+- int8_t rs_rssi_ctl1;
+- int8_t rs_rssi_ctl2;
+- int8_t rs_rssi_ext0;
+- int8_t rs_rssi_ext1;
+- int8_t rs_rssi_ext2;
+- u8 rs_antenna;
+ };
+
+ enum ath_reset_type {
+@@ -235,16 +228,17 @@ struct ath9k_debug {
struct dentry *debugfs_phy;
u32 regidx;
struct ath_stats stats;
void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status);
void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf,
struct ath_tx_status *ts, struct ath_txq *txq,
-@@ -258,10 +259,6 @@ static inline int ath9k_init_debug(struc
+@@ -258,10 +252,6 @@ static inline int ath9k_init_debug(struc
return 0;
}
static inline void ath_debug_stat_interrupt(struct ath_softc *sc,
enum ath9k_int status)
{
-@@ -282,4 +279,17 @@ static inline void ath_debug_stat_rx(str
+@@ -282,4 +272,17 @@ static inline void ath_debug_stat_rx(str
#endif /* CONFIG_ATH9K_DEBUGFS */
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
-@@ -954,6 +954,7 @@ static void ath9k_process_rssi(struct at
+@@ -232,7 +232,6 @@ static void ath_rx_edma_cleanup(struct a
+ static void ath_rx_edma_init_queue(struct ath_rx_edma *rx_edma, int size)
+ {
+ skb_queue_head_init(&rx_edma->rx_fifo);
+- skb_queue_head_init(&rx_edma->rx_buffers);
+ rx_edma->rx_fifo_hwsize = size;
+ }
+
+@@ -658,7 +657,9 @@ static void ath_rx_ps(struct ath_softc *
+ }
+
+ static bool ath_edma_get_buffers(struct ath_softc *sc,
+- enum ath9k_rx_qtype qtype)
++ enum ath9k_rx_qtype qtype,
++ struct ath_rx_status *rs,
++ struct ath_buf **dest)
+ {
+ struct ath_rx_edma *rx_edma = &sc->rx.rx_edma[qtype];
+ struct ath_hw *ah = sc->sc_ah;
+@@ -677,7 +678,7 @@ static bool ath_edma_get_buffers(struct
+ dma_sync_single_for_cpu(sc->dev, bf->bf_buf_addr,
+ common->rx_bufsize, DMA_FROM_DEVICE);
+
+- ret = ath9k_hw_process_rxdesc_edma(ah, NULL, skb->data);
++ ret = ath9k_hw_process_rxdesc_edma(ah, rs, skb->data);
+ if (ret == -EINPROGRESS) {
+ /*let device gain the buffer again*/
+ dma_sync_single_for_device(sc->dev, bf->bf_buf_addr,
+@@ -690,20 +691,21 @@ static bool ath_edma_get_buffers(struct
+ /* corrupt descriptor, skip this one and the following one */
+ list_add_tail(&bf->list, &sc->rx.rxbuf);
+ ath_rx_edma_buf_link(sc, qtype);
+- skb = skb_peek(&rx_edma->rx_fifo);
+- if (!skb)
+- return true;
+
+- bf = SKB_CB_ATHBUF(skb);
+- BUG_ON(!bf);
++ skb = skb_peek(&rx_edma->rx_fifo);
++ if (skb) {
++ bf = SKB_CB_ATHBUF(skb);
++ BUG_ON(!bf);
+
+- __skb_unlink(skb, &rx_edma->rx_fifo);
+- list_add_tail(&bf->list, &sc->rx.rxbuf);
+- ath_rx_edma_buf_link(sc, qtype);
+- return true;
++ __skb_unlink(skb, &rx_edma->rx_fifo);
++ list_add_tail(&bf->list, &sc->rx.rxbuf);
++ ath_rx_edma_buf_link(sc, qtype);
++ } else {
++ bf = NULL;
++ }
+ }
+- skb_queue_tail(&rx_edma->rx_buffers, skb);
+
++ *dest = bf;
+ return true;
+ }
+
+@@ -711,18 +713,15 @@ static struct ath_buf *ath_edma_get_next
+ struct ath_rx_status *rs,
+ enum ath9k_rx_qtype qtype)
+ {
+- struct ath_rx_edma *rx_edma = &sc->rx.rx_edma[qtype];
+- struct sk_buff *skb;
+- struct ath_buf *bf;
++ struct ath_buf *bf = NULL;
+
+- while (ath_edma_get_buffers(sc, qtype));
+- skb = __skb_dequeue(&rx_edma->rx_buffers);
+- if (!skb)
+- return NULL;
++ while (ath_edma_get_buffers(sc, qtype, rs, &bf)) {
++ if (!bf)
++ continue;
+
+- bf = SKB_CB_ATHBUF(skb);
+- ath9k_hw_process_rxdesc_edma(sc->sc_ah, rs, skb->data);
+- return bf;
++ return bf;
++ }
++ return NULL;
+ }
+
+ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
+@@ -954,6 +953,7 @@ static void ath9k_process_rssi(struct at
struct ath_softc *sc = hw->priv;
struct ath_hw *ah = common->ah;
int last_rssi;
if (!rx_stats->is_mybeacon ||
((ah->opmode != NL80211_IFTYPE_STATION) &&
-@@ -965,13 +966,12 @@ static void ath9k_process_rssi(struct at
+@@ -965,13 +965,12 @@ static void ath9k_process_rssi(struct at
last_rssi = sc->last_rssi;
if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER))
}
/*
-@@ -1011,6 +1011,8 @@ static int ath9k_rx_skb_preprocess(struc
+@@ -1011,6 +1010,8 @@ static int ath9k_rx_skb_preprocess(struc
rx_status->signal = ah->noise + rx_stats->rs_rssi;
rx_status->antenna = rx_stats->rs_antenna;
rx_status->flag |= RX_FLAG_MACTIME_MPDU;
txq = &sc->tx.txq[ts.qid];
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -299,7 +299,6 @@ struct ath_tx {
+
+ struct ath_rx_edma {
+ struct sk_buff_head rx_fifo;
+- struct sk_buff_head rx_buffers;
+ u32 rx_fifo_hwsize;
+ };
+
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
-@@ -1560,6 +1560,53 @@ static const struct file_operations fops
+@@ -1529,6 +1529,53 @@ static const struct file_operations fops
#endif
int ath9k_init_debug(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
-@@ -1623,5 +1670,8 @@ int ath9k_init_debug(struct ath_hw *ah)
+@@ -1592,5 +1639,8 @@ int ath9k_init_debug(struct ath_hw *ah)
debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, &sc->sc_ah->gpio_val);
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
-@@ -370,7 +370,7 @@ struct ath_vif {
+@@ -369,7 +369,7 @@ struct ath_vif {
* number of beacon intervals, the game's up.
*/
#define BSTUCK_THRESH 9
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
-@@ -625,6 +625,7 @@ struct ath_softc {
+@@ -624,6 +624,7 @@ struct ath_softc {
struct ieee80211_hw *hw;
struct device *dev;
int chan_idx;
int chan_is_ht;
struct survey_info *cur_survey;
-@@ -692,6 +693,7 @@ struct ath_softc {
+@@ -691,6 +692,7 @@ struct ath_softc {
u8 ant_tx, ant_rx;
};
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
-@@ -1607,6 +1607,50 @@ static const struct file_operations fops
+@@ -1576,6 +1576,50 @@ static const struct file_operations fops
.owner = THIS_MODULE
};
int ath9k_init_debug(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
-@@ -1673,5 +1717,8 @@ int ath9k_init_debug(struct ath_hw *ah)
+@@ -1642,5 +1686,8 @@ int ath9k_init_debug(struct ath_hw *ah)
debugfs_create_file("eeprom", S_IRUSR, sc->debug.debugfs_phy, sc,
&fops_eeprom);
spinlock_t txbuflock;
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
-@@ -1674,6 +1674,10 @@ int ath9k_init_debug(struct ath_hw *ah)
+@@ -1643,6 +1643,10 @@ int ath9k_init_debug(struct ath_hw *ah)
&fops_interrupt);
debugfs_create_file("xmit", S_IRUSR, sc->debug.debugfs_phy, sc,
&fops_xmit);
--- /dev/null
+--- a/drivers/net/wireless/ath/ath9k/hw.c
++++ b/drivers/net/wireless/ath/ath9k/hw.c
+@@ -1006,16 +1006,26 @@ void ath9k_hw_init_global_settings(struc
+
+ if (IS_CHAN_HALF_RATE(chan)) {
+ eifs = 175;
+- rx_lat *= 2;
++
++ if (!AR_SREV_9300_20_OR_LATER(ah))
++ rx_lat = 10;
++ else
++ rx_lat *= 2;
++
+ tx_lat *= 2;
+ if (IS_CHAN_A_FAST_CLOCK(ah, chan))
+- tx_lat += 11;
++ tx_lat += 11;
+
+ slottime = 13;
+ sifstime = 32;
+ } else if (IS_CHAN_QUARTER_RATE(chan)) {
+ eifs = 340;
+- rx_lat = (rx_lat * 4) - 1;
++
++ if (!AR_SREV_9300_20_OR_LATER(ah))
++ rx_lat = 20;
++ else
++ rx_lat = (rx_lat * 4) - 1;
++
+ tx_lat *= 4;
+ if (IS_CHAN_A_FAST_CLOCK(ah, chan))
+ tx_lat += 22;
+++ /dev/null
---- a/drivers/net/wireless/ath/ath9k/hw.c
-+++ b/drivers/net/wireless/ath/ath9k/hw.c
-@@ -1006,16 +1006,26 @@ void ath9k_hw_init_global_settings(struc
-
- if (IS_CHAN_HALF_RATE(chan)) {
- eifs = 175;
-- rx_lat *= 2;
-+
-+ if (!AR_SREV_9300_20_OR_LATER(ah))
-+ rx_lat = 10;
-+ else
-+ rx_lat *= 2;
-+
- tx_lat *= 2;
- if (IS_CHAN_A_FAST_CLOCK(ah, chan))
-- tx_lat += 11;
-+ tx_lat += 11;
-
- slottime = 13;
- sifstime = 32;
- } else if (IS_CHAN_QUARTER_RATE(chan)) {
- eifs = 340;
-- rx_lat = (rx_lat * 4) - 1;
-+
-+ if (!AR_SREV_9300_20_OR_LATER(ah))
-+ rx_lat = 20;
-+ else
-+ rx_lat = (rx_lat * 4) - 1;
-+
- tx_lat *= 4;
- if (IS_CHAN_A_FAST_CLOCK(ah, chan))
- tx_lat += 22;
--- /dev/null
+--- a/drivers/net/wireless/ath/ath9k/hw.c
++++ b/drivers/net/wireless/ath/ath9k/hw.c
+@@ -1007,31 +1007,35 @@ void ath9k_hw_init_global_settings(struc
+ if (IS_CHAN_HALF_RATE(chan)) {
+ eifs = 175;
+
+- if (!AR_SREV_9300_20_OR_LATER(ah))
++ if (!AR_SREV_9300_20_OR_LATER(ah)) {
+ rx_lat = 10;
+- else
++ sifstime = 8;
++ } else {
+ rx_lat *= 2;
++ sifstime = 32;
++ }
+
+ tx_lat *= 2;
+ if (IS_CHAN_A_FAST_CLOCK(ah, chan))
+ tx_lat += 11;
+
+ slottime = 13;
+- sifstime = 32;
+ } else if (IS_CHAN_QUARTER_RATE(chan)) {
+ eifs = 340;
+
+- if (!AR_SREV_9300_20_OR_LATER(ah))
++ if (!AR_SREV_9300_20_OR_LATER(ah)) {
+ rx_lat = 20;
+- else
++ sifstime = 8;
++ } else {
+ rx_lat = (rx_lat * 4) - 1;
++ sifstime = 64;
++ }
+
+ tx_lat *= 4;
+ if (IS_CHAN_A_FAST_CLOCK(ah, chan))
+ tx_lat += 22;
+
+ slottime = 21;
+- sifstime = 64;
+ } else {
+ if (AR_SREV_9287(ah) && AR_SREV_9287_13_OR_LATER(ah)) {
+ eifs = AR_D_GBL_IFS_EIFS_ASYNC_FIFO;
+++ /dev/null
---- a/drivers/net/wireless/ath/ath9k/hw.c
-+++ b/drivers/net/wireless/ath/ath9k/hw.c
-@@ -1007,31 +1007,35 @@ void ath9k_hw_init_global_settings(struc
- if (IS_CHAN_HALF_RATE(chan)) {
- eifs = 175;
-
-- if (!AR_SREV_9300_20_OR_LATER(ah))
-+ if (!AR_SREV_9300_20_OR_LATER(ah)) {
- rx_lat = 10;
-- else
-+ sifstime = 8;
-+ } else {
- rx_lat *= 2;
-+ sifstime = 32;
-+ }
-
- tx_lat *= 2;
- if (IS_CHAN_A_FAST_CLOCK(ah, chan))
- tx_lat += 11;
-
- slottime = 13;
-- sifstime = 32;
- } else if (IS_CHAN_QUARTER_RATE(chan)) {
- eifs = 340;
-
-- if (!AR_SREV_9300_20_OR_LATER(ah))
-+ if (!AR_SREV_9300_20_OR_LATER(ah)) {
- rx_lat = 20;
-- else
-+ sifstime = 8;
-+ } else {
- rx_lat = (rx_lat * 4) - 1;
-+ sifstime = 64;
-+ }
-
- tx_lat *= 4;
- if (IS_CHAN_A_FAST_CLOCK(ah, chan))
- tx_lat += 22;
-
- slottime = 21;
-- sifstime = 64;
- } else {
- if (AR_SREV_9287(ah) && AR_SREV_9287_13_OR_LATER(ah)) {
- eifs = AR_D_GBL_IFS_EIFS_ASYNC_FIFO;
--- /dev/null
+--- a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h
++++ b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h
+@@ -835,108 +835,108 @@ static const u32 ar9300_2p2_baseband_cor
+
+ static const u32 ar9300Modes_high_power_tx_gain_table_2p2[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+- {0x0000a2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352},
+- {0x0000a2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584},
+- {0x0000a2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800},
++ {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
++ {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
++ {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+- {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
+- {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+- {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002},
+- {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004},
+- {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200},
+- {0x0000a510, 0x15000028, 0x15000028, 0x0f000202, 0x0f000202},
+- {0x0000a514, 0x1b00002b, 0x1b00002b, 0x12000400, 0x12000400},
+- {0x0000a518, 0x1f020028, 0x1f020028, 0x16000402, 0x16000402},
+- {0x0000a51c, 0x2502002b, 0x2502002b, 0x19000404, 0x19000404},
+- {0x0000a520, 0x2a04002a, 0x2a04002a, 0x1c000603, 0x1c000603},
+- {0x0000a524, 0x2e06002a, 0x2e06002a, 0x21000a02, 0x21000a02},
+- {0x0000a528, 0x3302202d, 0x3302202d, 0x25000a04, 0x25000a04},
+- {0x0000a52c, 0x3804202c, 0x3804202c, 0x28000a20, 0x28000a20},
+- {0x0000a530, 0x3c06202c, 0x3c06202c, 0x2c000e20, 0x2c000e20},
+- {0x0000a534, 0x4108202d, 0x4108202d, 0x30000e22, 0x30000e22},
+- {0x0000a538, 0x4506402d, 0x4506402d, 0x34000e24, 0x34000e24},
+- {0x0000a53c, 0x4906222d, 0x4906222d, 0x38001640, 0x38001640},
+- {0x0000a540, 0x4d062231, 0x4d062231, 0x3c001660, 0x3c001660},
+- {0x0000a544, 0x50082231, 0x50082231, 0x3f001861, 0x3f001861},
+- {0x0000a548, 0x5608422e, 0x5608422e, 0x43001a81, 0x43001a81},
+- {0x0000a54c, 0x5a08442e, 0x5a08442e, 0x47001a83, 0x47001a83},
+- {0x0000a550, 0x5e0a4431, 0x5e0a4431, 0x4a001c84, 0x4a001c84},
+- {0x0000a554, 0x640a4432, 0x640a4432, 0x4e001ce3, 0x4e001ce3},
+- {0x0000a558, 0x680a4434, 0x680a4434, 0x52001ce5, 0x52001ce5},
+- {0x0000a55c, 0x6c0a6434, 0x6c0a6434, 0x56001ce9, 0x56001ce9},
+- {0x0000a560, 0x6f0a6633, 0x6f0a6633, 0x5a001ceb, 0x5a001ceb},
+- {0x0000a564, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+- {0x0000a568, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+- {0x0000a56c, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+- {0x0000a570, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+- {0x0000a574, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+- {0x0000a578, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+- {0x0000a57c, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
+- {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000},
+- {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002},
+- {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004},
+- {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200},
+- {0x0000a590, 0x15800028, 0x15800028, 0x0f800202, 0x0f800202},
+- {0x0000a594, 0x1b80002b, 0x1b80002b, 0x12800400, 0x12800400},
+- {0x0000a598, 0x1f820028, 0x1f820028, 0x16800402, 0x16800402},
+- {0x0000a59c, 0x2582002b, 0x2582002b, 0x19800404, 0x19800404},
+- {0x0000a5a0, 0x2a84002a, 0x2a84002a, 0x1c800603, 0x1c800603},
+- {0x0000a5a4, 0x2e86002a, 0x2e86002a, 0x21800a02, 0x21800a02},
+- {0x0000a5a8, 0x3382202d, 0x3382202d, 0x25800a04, 0x25800a04},
+- {0x0000a5ac, 0x3884202c, 0x3884202c, 0x28800a20, 0x28800a20},
+- {0x0000a5b0, 0x3c86202c, 0x3c86202c, 0x2c800e20, 0x2c800e20},
+- {0x0000a5b4, 0x4188202d, 0x4188202d, 0x30800e22, 0x30800e22},
+- {0x0000a5b8, 0x4586402d, 0x4586402d, 0x34800e24, 0x34800e24},
+- {0x0000a5bc, 0x4986222d, 0x4986222d, 0x38801640, 0x38801640},
+- {0x0000a5c0, 0x4d862231, 0x4d862231, 0x3c801660, 0x3c801660},
+- {0x0000a5c4, 0x50882231, 0x50882231, 0x3f801861, 0x3f801861},
+- {0x0000a5c8, 0x5688422e, 0x5688422e, 0x43801a81, 0x43801a81},
+- {0x0000a5cc, 0x5a88442e, 0x5a88442e, 0x47801a83, 0x47801a83},
+- {0x0000a5d0, 0x5e8a4431, 0x5e8a4431, 0x4a801c84, 0x4a801c84},
+- {0x0000a5d4, 0x648a4432, 0x648a4432, 0x4e801ce3, 0x4e801ce3},
+- {0x0000a5d8, 0x688a4434, 0x688a4434, 0x52801ce5, 0x52801ce5},
+- {0x0000a5dc, 0x6c8a6434, 0x6c8a6434, 0x56801ce9, 0x56801ce9},
+- {0x0000a5e0, 0x6f8a6633, 0x6f8a6633, 0x5a801ceb, 0x5a801ceb},
+- {0x0000a5e4, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec},
+- {0x0000a5e8, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec},
+- {0x0000a5ec, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec},
+- {0x0000a5f0, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec},
+- {0x0000a5f4, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec},
+- {0x0000a5f8, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec},
+- {0x0000a5fc, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec},
++ {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9},
++ {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000},
++ {0x0000a504, 0x04002222, 0x04002222, 0x04000002, 0x04000002},
++ {0x0000a508, 0x09002421, 0x09002421, 0x08000004, 0x08000004},
++ {0x0000a50c, 0x0d002621, 0x0d002621, 0x0b000200, 0x0b000200},
++ {0x0000a510, 0x13004620, 0x13004620, 0x0f000202, 0x0f000202},
++ {0x0000a514, 0x19004a20, 0x19004a20, 0x11000400, 0x11000400},
++ {0x0000a518, 0x1d004e20, 0x1d004e20, 0x15000402, 0x15000402},
++ {0x0000a51c, 0x21005420, 0x21005420, 0x19000404, 0x19000404},
++ {0x0000a520, 0x26005e20, 0x26005e20, 0x1b000603, 0x1b000603},
++ {0x0000a524, 0x2b005e40, 0x2b005e40, 0x1f000a02, 0x1f000a02},
++ {0x0000a528, 0x2f005e42, 0x2f005e42, 0x23000a04, 0x23000a04},
++ {0x0000a52c, 0x33005e44, 0x33005e44, 0x26000a20, 0x26000a20},
++ {0x0000a530, 0x38005e65, 0x38005e65, 0x2a000e20, 0x2a000e20},
++ {0x0000a534, 0x3c005e69, 0x3c005e69, 0x2e000e22, 0x2e000e22},
++ {0x0000a538, 0x40005e6b, 0x40005e6b, 0x31000e24, 0x31000e24},
++ {0x0000a53c, 0x44005e6d, 0x44005e6d, 0x34001640, 0x34001640},
++ {0x0000a540, 0x49005e72, 0x49005e72, 0x38001660, 0x38001660},
++ {0x0000a544, 0x4e005eb2, 0x4e005eb2, 0x3b001861, 0x3b001861},
++ {0x0000a548, 0x53005f12, 0x53005f12, 0x3e001a81, 0x3e001a81},
++ {0x0000a54c, 0x59025eb2, 0x59025eb2, 0x42001a83, 0x42001a83},
++ {0x0000a550, 0x5e025f12, 0x5e025f12, 0x44001c84, 0x44001c84},
++ {0x0000a554, 0x61027f12, 0x61027f12, 0x48001ce3, 0x48001ce3},
++ {0x0000a558, 0x6702bf12, 0x6702bf12, 0x4c001ce5, 0x4c001ce5},
++ {0x0000a55c, 0x6b02bf14, 0x6b02bf14, 0x50001ce9, 0x50001ce9},
++ {0x0000a560, 0x6f02bf16, 0x6f02bf16, 0x54001ceb, 0x54001ceb},
++ {0x0000a564, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
++ {0x0000a568, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
++ {0x0000a56c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
++ {0x0000a570, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
++ {0x0000a574, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
++ {0x0000a578, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
++ {0x0000a57c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
++ {0x0000a580, 0x00802220, 0x00802220, 0x00800000, 0x00800000},
++ {0x0000a584, 0x04802222, 0x04802222, 0x04800002, 0x04800002},
++ {0x0000a588, 0x09802421, 0x09802421, 0x08800004, 0x08800004},
++ {0x0000a58c, 0x0d802621, 0x0d802621, 0x0b800200, 0x0b800200},
++ {0x0000a590, 0x13804620, 0x13804620, 0x0f800202, 0x0f800202},
++ {0x0000a594, 0x19804a20, 0x19804a20, 0x11800400, 0x11800400},
++ {0x0000a598, 0x1d804e20, 0x1d804e20, 0x15800402, 0x15800402},
++ {0x0000a59c, 0x21805420, 0x21805420, 0x19800404, 0x19800404},
++ {0x0000a5a0, 0x26805e20, 0x26805e20, 0x1b800603, 0x1b800603},
++ {0x0000a5a4, 0x2b805e40, 0x2b805e40, 0x1f800a02, 0x1f800a02},
++ {0x0000a5a8, 0x2f805e42, 0x2f805e42, 0x23800a04, 0x23800a04},
++ {0x0000a5ac, 0x33805e44, 0x33805e44, 0x26800a20, 0x26800a20},
++ {0x0000a5b0, 0x38805e65, 0x38805e65, 0x2a800e20, 0x2a800e20},
++ {0x0000a5b4, 0x3c805e69, 0x3c805e69, 0x2e800e22, 0x2e800e22},
++ {0x0000a5b8, 0x40805e6b, 0x40805e6b, 0x31800e24, 0x31800e24},
++ {0x0000a5bc, 0x44805e6d, 0x44805e6d, 0x34801640, 0x34801640},
++ {0x0000a5c0, 0x49805e72, 0x49805e72, 0x38801660, 0x38801660},
++ {0x0000a5c4, 0x4e805eb2, 0x4e805eb2, 0x3b801861, 0x3b801861},
++ {0x0000a5c8, 0x53805f12, 0x53805f12, 0x3e801a81, 0x3e801a81},
++ {0x0000a5cc, 0x59825eb2, 0x59825eb2, 0x42801a83, 0x42801a83},
++ {0x0000a5d0, 0x5e825f12, 0x5e825f12, 0x44801c84, 0x44801c84},
++ {0x0000a5d4, 0x61827f12, 0x61827f12, 0x48801ce3, 0x48801ce3},
++ {0x0000a5d8, 0x6782bf12, 0x6782bf12, 0x4c801ce5, 0x4c801ce5},
++ {0x0000a5dc, 0x6b82bf14, 0x6b82bf14, 0x50801ce9, 0x50801ce9},
++ {0x0000a5e0, 0x6f82bf16, 0x6f82bf16, 0x54801ceb, 0x54801ceb},
++ {0x0000a5e4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
++ {0x0000a5e8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
++ {0x0000a5ec, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
++ {0x0000a5f0, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
++ {0x0000a5f4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
++ {0x0000a5f8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
++ {0x0000a5fc, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
+ {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+- {0x0000a608, 0x01804601, 0x01804601, 0x00000000, 0x00000000},
+- {0x0000a60c, 0x01804601, 0x01804601, 0x00000000, 0x00000000},
+- {0x0000a610, 0x01804601, 0x01804601, 0x00000000, 0x00000000},
+- {0x0000a614, 0x01804601, 0x01804601, 0x01404000, 0x01404000},
+- {0x0000a618, 0x01804601, 0x01804601, 0x01404501, 0x01404501},
+- {0x0000a61c, 0x01804601, 0x01804601, 0x02008501, 0x02008501},
+- {0x0000a620, 0x03408d02, 0x03408d02, 0x0280ca03, 0x0280ca03},
+- {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04},
+- {0x0000a628, 0x03410d04, 0x03410d04, 0x04014c04, 0x04014c04},
+- {0x0000a62c, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
+- {0x0000a630, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
+- {0x0000a634, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
+- {0x0000a638, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
+- {0x0000a63c, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
+- {0x0000b2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352},
+- {0x0000b2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584},
+- {0x0000b2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800},
++ {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
++ {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
++ {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000},
++ {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000},
++ {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501},
++ {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501},
++ {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03},
++ {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04},
++ {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04},
++ {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
++ {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
++ {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
++ {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
++ {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
++ {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
++ {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
++ {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+- {0x0000c2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352},
+- {0x0000c2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584},
+- {0x0000c2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800},
++ {0x0000c2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
++ {0x0000c2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
++ {0x0000c2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
+ {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
+- {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+- {0x00016048, 0x61200001, 0x61200001, 0x66480001, 0x66480001},
+- {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+- {0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+- {0x00016448, 0x61200001, 0x61200001, 0x66480001, 0x66480001},
+- {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
+- {0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
+- {0x00016848, 0x61200001, 0x61200001, 0x66480001, 0x66480001},
+- {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
++ {0x00016044, 0x056db2e6, 0x056db2e6, 0x056db2e6, 0x056db2e6},
++ {0x00016048, 0xae480001, 0xae480001, 0xae480001, 0xae480001},
++ {0x00016068, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c},
++ {0x00016444, 0x056db2e6, 0x056db2e6, 0x056db2e6, 0x056db2e6},
++ {0x00016448, 0xae480001, 0xae480001, 0xae480001, 0xae480001},
++ {0x00016468, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c},
++ {0x00016844, 0x056db2e6, 0x056db2e6, 0x056db2e6, 0x056db2e6},
++ {0x00016848, 0xae480001, 0xae480001, 0xae480001, 0xae480001},
++ {0x00016868, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c},
+ };
+
+ static const u32 ar9300Modes_high_ob_db_tx_gain_table_2p2[][5] = {
+++ /dev/null
---- a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h
-+++ b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h
-@@ -835,108 +835,108 @@ static const u32 ar9300_2p2_baseband_cor
-
- static const u32 ar9300Modes_high_power_tx_gain_table_2p2[][5] = {
- /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
-- {0x0000a2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352},
-- {0x0000a2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584},
-- {0x0000a2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800},
-+ {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
-+ {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
-+ {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
- {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
-- {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9},
-- {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
-- {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002},
-- {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004},
-- {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200},
-- {0x0000a510, 0x15000028, 0x15000028, 0x0f000202, 0x0f000202},
-- {0x0000a514, 0x1b00002b, 0x1b00002b, 0x12000400, 0x12000400},
-- {0x0000a518, 0x1f020028, 0x1f020028, 0x16000402, 0x16000402},
-- {0x0000a51c, 0x2502002b, 0x2502002b, 0x19000404, 0x19000404},
-- {0x0000a520, 0x2a04002a, 0x2a04002a, 0x1c000603, 0x1c000603},
-- {0x0000a524, 0x2e06002a, 0x2e06002a, 0x21000a02, 0x21000a02},
-- {0x0000a528, 0x3302202d, 0x3302202d, 0x25000a04, 0x25000a04},
-- {0x0000a52c, 0x3804202c, 0x3804202c, 0x28000a20, 0x28000a20},
-- {0x0000a530, 0x3c06202c, 0x3c06202c, 0x2c000e20, 0x2c000e20},
-- {0x0000a534, 0x4108202d, 0x4108202d, 0x30000e22, 0x30000e22},
-- {0x0000a538, 0x4506402d, 0x4506402d, 0x34000e24, 0x34000e24},
-- {0x0000a53c, 0x4906222d, 0x4906222d, 0x38001640, 0x38001640},
-- {0x0000a540, 0x4d062231, 0x4d062231, 0x3c001660, 0x3c001660},
-- {0x0000a544, 0x50082231, 0x50082231, 0x3f001861, 0x3f001861},
-- {0x0000a548, 0x5608422e, 0x5608422e, 0x43001a81, 0x43001a81},
-- {0x0000a54c, 0x5a08442e, 0x5a08442e, 0x47001a83, 0x47001a83},
-- {0x0000a550, 0x5e0a4431, 0x5e0a4431, 0x4a001c84, 0x4a001c84},
-- {0x0000a554, 0x640a4432, 0x640a4432, 0x4e001ce3, 0x4e001ce3},
-- {0x0000a558, 0x680a4434, 0x680a4434, 0x52001ce5, 0x52001ce5},
-- {0x0000a55c, 0x6c0a6434, 0x6c0a6434, 0x56001ce9, 0x56001ce9},
-- {0x0000a560, 0x6f0a6633, 0x6f0a6633, 0x5a001ceb, 0x5a001ceb},
-- {0x0000a564, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
-- {0x0000a568, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
-- {0x0000a56c, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
-- {0x0000a570, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
-- {0x0000a574, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
-- {0x0000a578, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
-- {0x0000a57c, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec},
-- {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000},
-- {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002},
-- {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004},
-- {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200},
-- {0x0000a590, 0x15800028, 0x15800028, 0x0f800202, 0x0f800202},
-- {0x0000a594, 0x1b80002b, 0x1b80002b, 0x12800400, 0x12800400},
-- {0x0000a598, 0x1f820028, 0x1f820028, 0x16800402, 0x16800402},
-- {0x0000a59c, 0x2582002b, 0x2582002b, 0x19800404, 0x19800404},
-- {0x0000a5a0, 0x2a84002a, 0x2a84002a, 0x1c800603, 0x1c800603},
-- {0x0000a5a4, 0x2e86002a, 0x2e86002a, 0x21800a02, 0x21800a02},
-- {0x0000a5a8, 0x3382202d, 0x3382202d, 0x25800a04, 0x25800a04},
-- {0x0000a5ac, 0x3884202c, 0x3884202c, 0x28800a20, 0x28800a20},
-- {0x0000a5b0, 0x3c86202c, 0x3c86202c, 0x2c800e20, 0x2c800e20},
-- {0x0000a5b4, 0x4188202d, 0x4188202d, 0x30800e22, 0x30800e22},
-- {0x0000a5b8, 0x4586402d, 0x4586402d, 0x34800e24, 0x34800e24},
-- {0x0000a5bc, 0x4986222d, 0x4986222d, 0x38801640, 0x38801640},
-- {0x0000a5c0, 0x4d862231, 0x4d862231, 0x3c801660, 0x3c801660},
-- {0x0000a5c4, 0x50882231, 0x50882231, 0x3f801861, 0x3f801861},
-- {0x0000a5c8, 0x5688422e, 0x5688422e, 0x43801a81, 0x43801a81},
-- {0x0000a5cc, 0x5a88442e, 0x5a88442e, 0x47801a83, 0x47801a83},
-- {0x0000a5d0, 0x5e8a4431, 0x5e8a4431, 0x4a801c84, 0x4a801c84},
-- {0x0000a5d4, 0x648a4432, 0x648a4432, 0x4e801ce3, 0x4e801ce3},
-- {0x0000a5d8, 0x688a4434, 0x688a4434, 0x52801ce5, 0x52801ce5},
-- {0x0000a5dc, 0x6c8a6434, 0x6c8a6434, 0x56801ce9, 0x56801ce9},
-- {0x0000a5e0, 0x6f8a6633, 0x6f8a6633, 0x5a801ceb, 0x5a801ceb},
-- {0x0000a5e4, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec},
-- {0x0000a5e8, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec},
-- {0x0000a5ec, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec},
-- {0x0000a5f0, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec},
-- {0x0000a5f4, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec},
-- {0x0000a5f8, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec},
-- {0x0000a5fc, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec},
-+ {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9},
-+ {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000},
-+ {0x0000a504, 0x04002222, 0x04002222, 0x04000002, 0x04000002},
-+ {0x0000a508, 0x09002421, 0x09002421, 0x08000004, 0x08000004},
-+ {0x0000a50c, 0x0d002621, 0x0d002621, 0x0b000200, 0x0b000200},
-+ {0x0000a510, 0x13004620, 0x13004620, 0x0f000202, 0x0f000202},
-+ {0x0000a514, 0x19004a20, 0x19004a20, 0x11000400, 0x11000400},
-+ {0x0000a518, 0x1d004e20, 0x1d004e20, 0x15000402, 0x15000402},
-+ {0x0000a51c, 0x21005420, 0x21005420, 0x19000404, 0x19000404},
-+ {0x0000a520, 0x26005e20, 0x26005e20, 0x1b000603, 0x1b000603},
-+ {0x0000a524, 0x2b005e40, 0x2b005e40, 0x1f000a02, 0x1f000a02},
-+ {0x0000a528, 0x2f005e42, 0x2f005e42, 0x23000a04, 0x23000a04},
-+ {0x0000a52c, 0x33005e44, 0x33005e44, 0x26000a20, 0x26000a20},
-+ {0x0000a530, 0x38005e65, 0x38005e65, 0x2a000e20, 0x2a000e20},
-+ {0x0000a534, 0x3c005e69, 0x3c005e69, 0x2e000e22, 0x2e000e22},
-+ {0x0000a538, 0x40005e6b, 0x40005e6b, 0x31000e24, 0x31000e24},
-+ {0x0000a53c, 0x44005e6d, 0x44005e6d, 0x34001640, 0x34001640},
-+ {0x0000a540, 0x49005e72, 0x49005e72, 0x38001660, 0x38001660},
-+ {0x0000a544, 0x4e005eb2, 0x4e005eb2, 0x3b001861, 0x3b001861},
-+ {0x0000a548, 0x53005f12, 0x53005f12, 0x3e001a81, 0x3e001a81},
-+ {0x0000a54c, 0x59025eb2, 0x59025eb2, 0x42001a83, 0x42001a83},
-+ {0x0000a550, 0x5e025f12, 0x5e025f12, 0x44001c84, 0x44001c84},
-+ {0x0000a554, 0x61027f12, 0x61027f12, 0x48001ce3, 0x48001ce3},
-+ {0x0000a558, 0x6702bf12, 0x6702bf12, 0x4c001ce5, 0x4c001ce5},
-+ {0x0000a55c, 0x6b02bf14, 0x6b02bf14, 0x50001ce9, 0x50001ce9},
-+ {0x0000a560, 0x6f02bf16, 0x6f02bf16, 0x54001ceb, 0x54001ceb},
-+ {0x0000a564, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
-+ {0x0000a568, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
-+ {0x0000a56c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
-+ {0x0000a570, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
-+ {0x0000a574, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
-+ {0x0000a578, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
-+ {0x0000a57c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec},
-+ {0x0000a580, 0x00802220, 0x00802220, 0x00800000, 0x00800000},
-+ {0x0000a584, 0x04802222, 0x04802222, 0x04800002, 0x04800002},
-+ {0x0000a588, 0x09802421, 0x09802421, 0x08800004, 0x08800004},
-+ {0x0000a58c, 0x0d802621, 0x0d802621, 0x0b800200, 0x0b800200},
-+ {0x0000a590, 0x13804620, 0x13804620, 0x0f800202, 0x0f800202},
-+ {0x0000a594, 0x19804a20, 0x19804a20, 0x11800400, 0x11800400},
-+ {0x0000a598, 0x1d804e20, 0x1d804e20, 0x15800402, 0x15800402},
-+ {0x0000a59c, 0x21805420, 0x21805420, 0x19800404, 0x19800404},
-+ {0x0000a5a0, 0x26805e20, 0x26805e20, 0x1b800603, 0x1b800603},
-+ {0x0000a5a4, 0x2b805e40, 0x2b805e40, 0x1f800a02, 0x1f800a02},
-+ {0x0000a5a8, 0x2f805e42, 0x2f805e42, 0x23800a04, 0x23800a04},
-+ {0x0000a5ac, 0x33805e44, 0x33805e44, 0x26800a20, 0x26800a20},
-+ {0x0000a5b0, 0x38805e65, 0x38805e65, 0x2a800e20, 0x2a800e20},
-+ {0x0000a5b4, 0x3c805e69, 0x3c805e69, 0x2e800e22, 0x2e800e22},
-+ {0x0000a5b8, 0x40805e6b, 0x40805e6b, 0x31800e24, 0x31800e24},
-+ {0x0000a5bc, 0x44805e6d, 0x44805e6d, 0x34801640, 0x34801640},
-+ {0x0000a5c0, 0x49805e72, 0x49805e72, 0x38801660, 0x38801660},
-+ {0x0000a5c4, 0x4e805eb2, 0x4e805eb2, 0x3b801861, 0x3b801861},
-+ {0x0000a5c8, 0x53805f12, 0x53805f12, 0x3e801a81, 0x3e801a81},
-+ {0x0000a5cc, 0x59825eb2, 0x59825eb2, 0x42801a83, 0x42801a83},
-+ {0x0000a5d0, 0x5e825f12, 0x5e825f12, 0x44801c84, 0x44801c84},
-+ {0x0000a5d4, 0x61827f12, 0x61827f12, 0x48801ce3, 0x48801ce3},
-+ {0x0000a5d8, 0x6782bf12, 0x6782bf12, 0x4c801ce5, 0x4c801ce5},
-+ {0x0000a5dc, 0x6b82bf14, 0x6b82bf14, 0x50801ce9, 0x50801ce9},
-+ {0x0000a5e0, 0x6f82bf16, 0x6f82bf16, 0x54801ceb, 0x54801ceb},
-+ {0x0000a5e4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
-+ {0x0000a5e8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
-+ {0x0000a5ec, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
-+ {0x0000a5f0, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
-+ {0x0000a5f4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
-+ {0x0000a5f8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
-+ {0x0000a5fc, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec},
- {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
- {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
-- {0x0000a608, 0x01804601, 0x01804601, 0x00000000, 0x00000000},
-- {0x0000a60c, 0x01804601, 0x01804601, 0x00000000, 0x00000000},
-- {0x0000a610, 0x01804601, 0x01804601, 0x00000000, 0x00000000},
-- {0x0000a614, 0x01804601, 0x01804601, 0x01404000, 0x01404000},
-- {0x0000a618, 0x01804601, 0x01804601, 0x01404501, 0x01404501},
-- {0x0000a61c, 0x01804601, 0x01804601, 0x02008501, 0x02008501},
-- {0x0000a620, 0x03408d02, 0x03408d02, 0x0280ca03, 0x0280ca03},
-- {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04},
-- {0x0000a628, 0x03410d04, 0x03410d04, 0x04014c04, 0x04014c04},
-- {0x0000a62c, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
-- {0x0000a630, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
-- {0x0000a634, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
-- {0x0000a638, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
-- {0x0000a63c, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005},
-- {0x0000b2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352},
-- {0x0000b2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584},
-- {0x0000b2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800},
-+ {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
-+ {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
-+ {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000},
-+ {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000},
-+ {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501},
-+ {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501},
-+ {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03},
-+ {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04},
-+ {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04},
-+ {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
-+ {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
-+ {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
-+ {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
-+ {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005},
-+ {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
-+ {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
-+ {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
- {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
-- {0x0000c2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352},
-- {0x0000c2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584},
-- {0x0000c2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800},
-+ {0x0000c2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352},
-+ {0x0000c2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584},
-+ {0x0000c2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800},
- {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000},
-- {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
-- {0x00016048, 0x61200001, 0x61200001, 0x66480001, 0x66480001},
-- {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
-- {0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
-- {0x00016448, 0x61200001, 0x61200001, 0x66480001, 0x66480001},
-- {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
-- {0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4},
-- {0x00016848, 0x61200001, 0x61200001, 0x66480001, 0x66480001},
-- {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c},
-+ {0x00016044, 0x056db2e6, 0x056db2e6, 0x056db2e6, 0x056db2e6},
-+ {0x00016048, 0xae480001, 0xae480001, 0xae480001, 0xae480001},
-+ {0x00016068, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c},
-+ {0x00016444, 0x056db2e6, 0x056db2e6, 0x056db2e6, 0x056db2e6},
-+ {0x00016448, 0xae480001, 0xae480001, 0xae480001, 0xae480001},
-+ {0x00016468, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c},
-+ {0x00016844, 0x056db2e6, 0x056db2e6, 0x056db2e6, 0x056db2e6},
-+ {0x00016848, 0xae480001, 0xae480001, 0xae480001, 0xae480001},
-+ {0x00016868, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c, 0x6eb6db6c},
- };
-
- static const u32 ar9300Modes_high_ob_db_tx_gain_table_2p2[][5] = {
+++ /dev/null
---- a/drivers/net/wireless/ath/ath9k/debug.c
-+++ b/drivers/net/wireless/ath/ath9k/debug.c
-@@ -944,27 +944,6 @@ static ssize_t read_file_recv(struct fil
- PHY_ERR("HT-RATE ERR", ATH9K_PHYERR_HT_RATE_ILLEGAL);
-
- len += snprintf(buf + len, size - len,
-- "%22s : %10d\n", "RSSI-CTL0",
-- sc->debug.stats.rxstats.rs_rssi_ctl0);
-- len += snprintf(buf + len, size - len,
-- "%22s : %10d\n", "RSSI-CTL1",
-- sc->debug.stats.rxstats.rs_rssi_ctl1);
-- len += snprintf(buf + len, size - len,
-- "%22s : %10d\n", "RSSI-CTL2",
-- sc->debug.stats.rxstats.rs_rssi_ctl2);
-- len += snprintf(buf + len, size - len,
-- "%22s : %10d\n", "RSSI-EXT0",
-- sc->debug.stats.rxstats.rs_rssi_ext0);
-- len += snprintf(buf + len, size - len,
-- "%22s : %10d\n", "RSSI-EXT1",
-- sc->debug.stats.rxstats.rs_rssi_ext1);
-- len += snprintf(buf + len, size - len,
-- "%22s : %10d\n", "RSSI-EXT2",
-- sc->debug.stats.rxstats.rs_rssi_ext2);
-- len += snprintf(buf + len, size - len,
-- "%22s : %10d\n", "Rx Antenna",
-- sc->debug.stats.rxstats.rs_antenna);
-- len += snprintf(buf + len, size - len,
- "%22s : %10u\n", "RX-Pkts-All",
- sc->debug.stats.rxstats.rx_pkts_all);
- len += snprintf(buf + len, size - len,
-@@ -1011,16 +990,6 @@ void ath_debug_stat_rx(struct ath_softc
- RX_PHY_ERR_INC(rs->rs_phyerr);
- }
-
-- sc->debug.stats.rxstats.rs_rssi_ctl0 = rs->rs_rssi_ctl0;
-- sc->debug.stats.rxstats.rs_rssi_ctl1 = rs->rs_rssi_ctl1;
-- sc->debug.stats.rxstats.rs_rssi_ctl2 = rs->rs_rssi_ctl2;
--
-- sc->debug.stats.rxstats.rs_rssi_ext0 = rs->rs_rssi_ext0;
-- sc->debug.stats.rxstats.rs_rssi_ext1 = rs->rs_rssi_ext1;
-- sc->debug.stats.rxstats.rs_rssi_ext2 = rs->rs_rssi_ext2;
--
-- sc->debug.stats.rxstats.rs_antenna = rs->rs_antenna;
--
- #ifdef CONFIG_ATH9K_MAC_DEBUG
- spin_lock(&sc->debug.samp_lock);
- RX_SAMP_DBG(jiffies) = jiffies;
---- a/drivers/net/wireless/ath/ath9k/debug.h
-+++ b/drivers/net/wireless/ath/ath9k/debug.h
-@@ -165,13 +165,6 @@ struct ath_rx_stats {
- u32 post_delim_crc_err;
- u32 decrypt_busy_err;
- u32 phy_err_stats[ATH9K_PHYERR_MAX];
-- int8_t rs_rssi_ctl0;
-- int8_t rs_rssi_ctl1;
-- int8_t rs_rssi_ctl2;
-- int8_t rs_rssi_ext0;
-- int8_t rs_rssi_ext1;
-- int8_t rs_rssi_ext2;
-- u8 rs_antenna;
- };
-
- enum ath_reset_type {
--- /dev/null
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -687,6 +687,9 @@ enum mac80211_rx_flags {
+ * @mactime: value in microseconds of the 64-bit Time Synchronization Function
+ * (TSF) timer when the first data symbol (MPDU) arrived at the hardware.
+ * @band: the active band when this frame was received
++ * @chains: bitmask of receive chains for which separate signal strength
++ * values were filled.
++ * @chain_signal: per-chain signal strength, same format as @signal
+ * @freq: frequency the radio was tuned to when receiving this frame, in MHz
+ * @signal: signal strength when receiving this frame, either in dBm, in dB or
+ * unspecified depending on the hardware capabilities flags
+@@ -700,6 +703,10 @@ enum mac80211_rx_flags {
+ struct ieee80211_rx_status {
+ u64 mactime;
+ enum ieee80211_band band;
++
++ u8 chains;
++ s8 chain_signal[4];
++
+ int freq;
+ int signal;
+ int antenna;
+--- a/net/mac80211/sta_info.h
++++ b/net/mac80211/sta_info.h
+@@ -313,6 +313,11 @@ struct sta_info {
+ unsigned long rx_dropped;
+ int last_signal;
+ struct ewma avg_signal;
++
++ u8 chains;
++ s8 chain_signal_last[4];
++ struct ewma chain_signal_avg[4];
++
+ /* Plus 1 for non-QoS frames */
+ __le16 last_seq_ctrl[NUM_RX_DATA_QUEUES + 1];
+
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -1271,6 +1271,7 @@ ieee80211_rx_h_sta_process(struct ieee80
+ struct sk_buff *skb = rx->skb;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
++ int i;
+
+ if (!sta)
+ return RX_CONTINUE;
+@@ -1315,6 +1316,19 @@ ieee80211_rx_h_sta_process(struct ieee80
+ ewma_add(&sta->avg_signal, -status->signal);
+ }
+
++ if (status->chains) {
++ sta->chains = status->chains;
++ for (i = 0; i < 4; i++) {
++ int signal = status->chain_signal[i];
++
++ if (!(status->chains & BIT(i)))
++ continue;
++
++ sta->chain_signal_last[i] = signal;
++ ewma_add(&sta->chain_signal_avg[i], -signal);
++ }
++ }
++
+ /*
+ * Change STA power saving mode only at the end of a frame
+ * exchange sequence.
+--- a/net/mac80211/sta_info.c
++++ b/net/mac80211/sta_info.c
+@@ -256,6 +256,8 @@ struct sta_info *sta_info_alloc(struct i
+ do_posix_clock_monotonic_gettime(&uptime);
+ sta->last_connected = uptime.tv_sec;
+ ewma_init(&sta->avg_signal, 1024, 8);
++ for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++)
++ ewma_init(&sta->chain_signal_avg[i], 1024, 8);
+
+ if (sta_prepare_rate_control(local, sta, gfp)) {
+ kfree(sta);
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -518,6 +518,8 @@ struct station_parameters {
+ * @STATION_INFO_ASSOC_REQ_IES: @assoc_req_ies filled
+ * @STATION_INFO_STA_FLAGS: @sta_flags filled
+ * @STATION_INFO_BEACON_LOSS_COUNT: @beacon_loss_count filled
++ * @STATION_INFO_CHAIN_SIGNAL: @chain_signal filled
++ * @STATION_INFO_CHAIN_SIGNAL_AVG: @chain_signal_avg filled
+ */
+ enum station_info_flags {
+ STATION_INFO_INACTIVE_TIME = 1<<0,
+@@ -539,7 +541,9 @@ enum station_info_flags {
+ STATION_INFO_CONNECTED_TIME = 1<<16,
+ STATION_INFO_ASSOC_REQ_IES = 1<<17,
+ STATION_INFO_STA_FLAGS = 1<<18,
+- STATION_INFO_BEACON_LOSS_COUNT = 1<<19
++ STATION_INFO_BEACON_LOSS_COUNT = 1<<19,
++ STATION_INFO_CHAIN_SIGNAL = 1<<20,
++ STATION_INFO_CHAIN_SIGNAL_AVG = 1<<21,
+ };
+
+ /**
+@@ -619,6 +623,9 @@ struct sta_bss_parameters {
+ * @plink_state: mesh peer link state
+ * @signal: signal strength of last received packet in dBm
+ * @signal_avg: signal strength average in dBm
++ * @chains: bitmask for filled values in @chain_signal, @chain_signal_avg
++ * @chain_signal: per-chain signal strength of last received packet in dBm
++ * @chain_signal_avg: per-chain signal strength average in dBm
+ * @txrate: current unicast bitrate from this station
+ * @rxrate: current unicast bitrate to this station
+ * @rx_packets: packets received from this station
+@@ -650,6 +657,11 @@ struct station_info {
+ u8 plink_state;
+ s8 signal;
+ s8 signal_avg;
++
++ u8 chains;
++ s8 chain_signal[4];
++ s8 chain_signal_avg[4];
++
+ struct rate_info txrate;
+ struct rate_info rxrate;
+ u32 rx_packets;
+--- a/drivers/net/wireless/ath/ath9k/mac.h
++++ b/drivers/net/wireless/ath/ath9k/mac.h
+@@ -133,12 +133,8 @@ struct ath_rx_status {
+ u8 rs_rate;
+ u8 rs_antenna;
+ u8 rs_more;
+- int8_t rs_rssi_ctl0;
+- int8_t rs_rssi_ctl1;
+- int8_t rs_rssi_ctl2;
+- int8_t rs_rssi_ext0;
+- int8_t rs_rssi_ext1;
+- int8_t rs_rssi_ext2;
++ int8_t rs_rssi_ctl[3];
++ int8_t rs_rssi_ext[3];
+ u8 rs_isaggr;
+ u8 rs_moreaggr;
+ u8 rs_num_delims;
+--- a/drivers/net/wireless/ath/ath9k/recv.c
++++ b/drivers/net/wireless/ath/ath9k/recv.c
+@@ -986,6 +986,7 @@ static int ath9k_rx_skb_preprocess(struc
+ bool *decrypt_error)
+ {
+ struct ath_hw *ah = common->ah;
++ int i, j;
+
+ memset(rx_status, 0, sizeof(struct ieee80211_rx_status));
+
+@@ -1013,6 +1014,20 @@ static int ath9k_rx_skb_preprocess(struc
+ if (rx_stats->rs_moreaggr)
+ rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
+
++ for (i = 0, j = 0; i < ARRAY_SIZE(rx_stats->rs_rssi_ctl); i++) {
++ s8 rssi;
++
++ if (!(ah->rxchainmask & BIT(i)))
++ continue;
++
++ rssi = rx_stats->rs_rssi_ctl[i];
++ if (rssi != ATH9K_RSSI_BAD) {
++ rx_status->chains |= BIT(j);
++ rx_status->chain_signal[j] = ah->noise + rssi;
++ }
++ j++;
++ }
++
+ return 0;
+ }
+
+@@ -1543,14 +1558,14 @@ static void ath_ant_comb_scan(struct ath
+ struct ath_ant_comb *antcomb = &sc->ant_comb;
+ int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
+ int curr_main_set;
+- int main_rssi = rs->rs_rssi_ctl0;
+- int alt_rssi = rs->rs_rssi_ctl1;
++ int main_rssi = rs->rs_rssi_ctl[0];
++ int alt_rssi = rs->rs_rssi_ctl[1];
+ int rx_ant_conf, main_ant_conf;
+ bool short_scan = false;
+
+- rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) &
++ rx_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_CURRENT_SHIFT) &
+ ATH_ANT_RX_MASK;
+- main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) &
++ main_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_MAIN_SHIFT) &
+ ATH_ANT_RX_MASK;
+
+ /* Record packet only when both main_rssi and alt_rssi is positive */
+--- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c
++++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
+@@ -453,12 +453,12 @@ int ath9k_hw_process_rxdesc_edma(struct
+
+ /* XXX: Keycache */
+ rxs->rs_rssi = MS(rxsp->status5, AR_RxRSSICombined);
+- rxs->rs_rssi_ctl0 = MS(rxsp->status1, AR_RxRSSIAnt00);
+- rxs->rs_rssi_ctl1 = MS(rxsp->status1, AR_RxRSSIAnt01);
+- rxs->rs_rssi_ctl2 = MS(rxsp->status1, AR_RxRSSIAnt02);
+- rxs->rs_rssi_ext0 = MS(rxsp->status5, AR_RxRSSIAnt10);
+- rxs->rs_rssi_ext1 = MS(rxsp->status5, AR_RxRSSIAnt11);
+- rxs->rs_rssi_ext2 = MS(rxsp->status5, AR_RxRSSIAnt12);
++ rxs->rs_rssi_ctl[0] = MS(rxsp->status1, AR_RxRSSIAnt00);
++ rxs->rs_rssi_ctl[1] = MS(rxsp->status1, AR_RxRSSIAnt01);
++ rxs->rs_rssi_ctl[2] = MS(rxsp->status1, AR_RxRSSIAnt02);
++ rxs->rs_rssi_ext[0] = MS(rxsp->status5, AR_RxRSSIAnt10);
++ rxs->rs_rssi_ext[1] = MS(rxsp->status5, AR_RxRSSIAnt11);
++ rxs->rs_rssi_ext[2] = MS(rxsp->status5, AR_RxRSSIAnt12);
+
+ if (rxsp->status11 & AR_RxKeyIdxValid)
+ rxs->rs_keyix = MS(rxsp->status11, AR_KeyIdx);
+--- a/drivers/net/wireless/ath/ath9k/mac.c
++++ b/drivers/net/wireless/ath/ath9k/mac.c
+@@ -556,25 +556,25 @@ int ath9k_hw_rxprocdesc(struct ath_hw *a
+
+ if (ads.ds_rxstatus8 & AR_PostDelimCRCErr) {
+ rs->rs_rssi = ATH9K_RSSI_BAD;
+- rs->rs_rssi_ctl0 = ATH9K_RSSI_BAD;
+- rs->rs_rssi_ctl1 = ATH9K_RSSI_BAD;
+- rs->rs_rssi_ctl2 = ATH9K_RSSI_BAD;
+- rs->rs_rssi_ext0 = ATH9K_RSSI_BAD;
+- rs->rs_rssi_ext1 = ATH9K_RSSI_BAD;
+- rs->rs_rssi_ext2 = ATH9K_RSSI_BAD;
++ rs->rs_rssi_ctl[0] = ATH9K_RSSI_BAD;
++ rs->rs_rssi_ctl[1] = ATH9K_RSSI_BAD;
++ rs->rs_rssi_ctl[2] = ATH9K_RSSI_BAD;
++ rs->rs_rssi_ext[0] = ATH9K_RSSI_BAD;
++ rs->rs_rssi_ext[1] = ATH9K_RSSI_BAD;
++ rs->rs_rssi_ext[2] = ATH9K_RSSI_BAD;
+ } else {
+ rs->rs_rssi = MS(ads.ds_rxstatus4, AR_RxRSSICombined);
+- rs->rs_rssi_ctl0 = MS(ads.ds_rxstatus0,
++ rs->rs_rssi_ctl[0] = MS(ads.ds_rxstatus0,
+ AR_RxRSSIAnt00);
+- rs->rs_rssi_ctl1 = MS(ads.ds_rxstatus0,
++ rs->rs_rssi_ctl[1] = MS(ads.ds_rxstatus0,
+ AR_RxRSSIAnt01);
+- rs->rs_rssi_ctl2 = MS(ads.ds_rxstatus0,
++ rs->rs_rssi_ctl[2] = MS(ads.ds_rxstatus0,
+ AR_RxRSSIAnt02);
+- rs->rs_rssi_ext0 = MS(ads.ds_rxstatus4,
++ rs->rs_rssi_ext[0] = MS(ads.ds_rxstatus4,
+ AR_RxRSSIAnt10);
+- rs->rs_rssi_ext1 = MS(ads.ds_rxstatus4,
++ rs->rs_rssi_ext[1] = MS(ads.ds_rxstatus4,
+ AR_RxRSSIAnt11);
+- rs->rs_rssi_ext2 = MS(ads.ds_rxstatus4,
++ rs->rs_rssi_ext[2] = MS(ads.ds_rxstatus4,
+ AR_RxRSSIAnt12);
+ }
+ if (ads.ds_rxstatus8 & AR_RxKeyIdxValid)
+--- a/drivers/net/wireless/ath/ath9k/debug.c
++++ b/drivers/net/wireless/ath/ath9k/debug.c
+@@ -993,12 +993,12 @@ void ath_debug_stat_rx(struct ath_softc
+ #ifdef CONFIG_ATH9K_MAC_DEBUG
+ spin_lock(&sc->debug.samp_lock);
+ RX_SAMP_DBG(jiffies) = jiffies;
+- RX_SAMP_DBG(rssi_ctl0) = rs->rs_rssi_ctl0;
+- RX_SAMP_DBG(rssi_ctl1) = rs->rs_rssi_ctl1;
+- RX_SAMP_DBG(rssi_ctl2) = rs->rs_rssi_ctl2;
+- RX_SAMP_DBG(rssi_ext0) = rs->rs_rssi_ext0;
+- RX_SAMP_DBG(rssi_ext1) = rs->rs_rssi_ext1;
+- RX_SAMP_DBG(rssi_ext2) = rs->rs_rssi_ext2;
++ RX_SAMP_DBG(rssi_ctl0) = rs->rs_rssi_ctl[0];
++ RX_SAMP_DBG(rssi_ctl1) = rs->rs_rssi_ctl[1];
++ RX_SAMP_DBG(rssi_ctl2) = rs->rs_rssi_ctl[2];
++ RX_SAMP_DBG(rssi_ext0) = rs->rs_rssi_ext[0];
++ RX_SAMP_DBG(rssi_ext1) = rs->rs_rssi_ext[1];
++ RX_SAMP_DBG(rssi_ext2) = rs->rs_rssi_ext[2];
+ RX_SAMP_DBG(antenna) = rs->rs_antenna;
+ RX_SAMP_DBG(rssi) = rs->rs_rssi;
+ RX_SAMP_DBG(rate) = rs->rs_rate;
+--- a/include/linux/nl80211.h
++++ b/include/linux/nl80211.h
+@@ -1661,6 +1661,8 @@ enum nl80211_sta_bss_param {
+ * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected
+ * @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update.
+ * @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32)
++ * @NL80211_STA_INFO_CHAIN_SIGNAL: per-chain signal strength of last PPDU
++ * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average
+ * @__NL80211_STA_INFO_AFTER_LAST: internal
+ * @NL80211_STA_INFO_MAX: highest possible station info attribute
+ */
+@@ -1684,6 +1686,8 @@ enum nl80211_sta_info {
+ NL80211_STA_INFO_CONNECTED_TIME,
+ NL80211_STA_INFO_STA_FLAGS,
+ NL80211_STA_INFO_BEACON_LOSS,
++ NL80211_STA_INFO_CHAIN_SIGNAL,
++ NL80211_STA_INFO_CHAIN_SIGNAL_AVG,
+
+ /* keep last */
+ __NL80211_STA_INFO_AFTER_LAST,
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -2376,6 +2376,33 @@ nla_put_failure:
+ return false;
+ }
+
++static bool nl80211_put_signal(struct sk_buff *msg, u8 mask, s8 *signal,
++ int id)
++{
++ void *attr;
++ int i = 0;
++
++ if (!mask)
++ return true;
++
++ attr = nla_nest_start(msg, id);
++ if (!attr)
++ goto nla_put_failure;
++
++ for (i = 0; i < 4; i++) {
++ if (!(mask & BIT(i)))
++ continue;
++
++ NLA_PUT_U8(msg, i, signal[i]);
++ }
++
++ nla_nest_end(msg, attr);
++
++ return true;
++nla_put_failure:
++ return false;
++}
++
+ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
+ int flags, struct net_device *dev,
+ const u8 *mac_addr, struct station_info *sinfo)
+@@ -2422,6 +2449,18 @@ static int nl80211_send_station(struct s
+ if (sinfo->filled & STATION_INFO_SIGNAL_AVG)
+ NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL_AVG,
+ sinfo->signal_avg);
++ if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL) {
++ if (!nl80211_put_signal(msg, sinfo->chains,
++ sinfo->chain_signal,
++ NL80211_STA_INFO_CHAIN_SIGNAL))
++ goto nla_put_failure;
++ }
++ if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL_AVG) {
++ if (!nl80211_put_signal(msg, sinfo->chains,
++ sinfo->chain_signal_avg,
++ NL80211_STA_INFO_CHAIN_SIGNAL_AVG))
++ goto nla_put_failure;
++ }
+ if (sinfo->filled & STATION_INFO_TX_BITRATE) {
+ if (!nl80211_put_sta_rate(msg, &sinfo->txrate,
+ NL80211_STA_INFO_TX_BITRATE))
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -340,6 +340,7 @@ static void sta_set_sinfo(struct sta_inf
+ {
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct timespec uptime;
++ int i;
+
+ sinfo->generation = sdata->local->sta_generation;
+
+@@ -377,6 +378,17 @@ static void sta_set_sinfo(struct sta_inf
+ sinfo->signal = (s8)sta->last_signal;
+ sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
+ }
++ if (sta->chains) {
++ sinfo->filled |= STATION_INFO_CHAIN_SIGNAL |
++ STATION_INFO_CHAIN_SIGNAL_AVG;
++
++ sinfo->chains = sta->chains;
++ for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
++ sinfo->chain_signal[i] = sta->chain_signal_last[i];
++ sinfo->chain_signal_avg[i] =
++ (s8) -ewma_read(&sta->chain_signal_avg[i]);
++ }
++ }
+
+ sinfo->txrate.flags = 0;
+ if (sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)
--- /dev/null
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -504,6 +504,9 @@ static inline u16 ath9k_btcoex_aggr_limi
+ #ifdef CONFIG_MAC80211_LEDS
+ void ath_init_leds(struct ath_softc *sc);
+ void ath_deinit_leds(struct ath_softc *sc);
++int ath_create_gpio_led(struct ath_softc *sc, int gpio, const char *name,
++ const char *trigger, bool active_low);
++
+ #else
+ static inline void ath_init_leds(struct ath_softc *sc)
+ {
+@@ -624,6 +627,13 @@ struct ath9k_vif_iter_data {
+ int nothers; /* number of vifs not specified above. */
+ };
+
++struct ath_led {
++ struct list_head list;
++ struct ath_softc *sc;
++ const struct gpio_led *gpio;
++ struct led_classdev cdev;
++};
++
+ struct ath_softc {
+ struct ieee80211_hw *hw;
+ struct device *dev;
+@@ -667,9 +677,8 @@ struct ath_softc {
+ struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
+
+ #ifdef CONFIG_MAC80211_LEDS
+- bool led_registered;
+- char led_name[32];
+- struct led_classdev led_cdev;
++ const char *led_default_trigger;
++ struct list_head leds;
+ #endif
+
+ struct ath9k_hw_cal_data caldata;
+--- a/drivers/net/wireless/ath/ath9k/gpio.c
++++ b/drivers/net/wireless/ath/ath9k/gpio.c
+@@ -24,22 +24,89 @@
+ static void ath_led_brightness(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+ {
+- struct ath_softc *sc = container_of(led_cdev, struct ath_softc, led_cdev);
+- ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, (brightness == LED_OFF));
++ struct ath_led *led = container_of(led_cdev, struct ath_led, cdev);
++ struct ath_softc *sc = led->sc;
++
++ ath9k_ps_wakeup(sc);
++ ath9k_hw_set_gpio(sc->sc_ah, led->gpio->gpio,
++ (brightness != LED_OFF) ^ led->gpio->active_low);
++ ath9k_ps_restore(sc);
++}
++
++static int ath_add_led(struct ath_softc *sc, struct ath_led *led)
++{
++ const struct gpio_led *gpio = led->gpio;
++ int ret;
++
++ led->cdev.name = gpio->name;
++ led->cdev.default_trigger = gpio->default_trigger;
++ led->cdev.brightness_set = ath_led_brightness;
++
++ ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->cdev);
++ if (ret < 0)
++ return ret;
++
++ led->sc = sc;
++ list_add(&led->list, &sc->leds);
++
++ /* Configure gpio for output */
++ ath9k_hw_cfg_output(sc->sc_ah, gpio->gpio,
++ AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
++
++ /* LED off */
++ ath9k_hw_set_gpio(sc->sc_ah, gpio->gpio, gpio->active_low);
++
++ return 0;
++}
++
++int ath_create_gpio_led(struct ath_softc *sc, int gpio_num, const char *name,
++ const char *trigger, bool active_low)
++{
++ struct ath_led *led;
++ struct gpio_led *gpio;
++ char *_name;
++ int ret;
++
++ led = kzalloc(sizeof(*led) + sizeof(*gpio) + strlen(name) + 1,
++ GFP_KERNEL);
++ if (!led)
++ return -ENOMEM;
++
++ led->gpio = gpio = (struct gpio_led *) (led + 1);
++ _name = (char *) (led->gpio + 1);
++
++ strcpy(_name, name);
++ gpio->name = _name;
++ gpio->gpio = gpio_num;
++ gpio->active_low = active_low;
++ gpio->default_trigger = trigger;
++
++ ret = ath_add_led(sc, led);
++ if (unlikely(ret < 0))
++ kfree(led);
++
++ return ret;
+ }
+
+ void ath_deinit_leds(struct ath_softc *sc)
+ {
+- if (!sc->led_registered)
+- return;
++ struct ath_led *led;
+
+- ath_led_brightness(&sc->led_cdev, LED_OFF);
+- led_classdev_unregister(&sc->led_cdev);
++ while (!list_empty(&sc->leds)) {
++ led = list_first_entry(&sc->leds, struct ath_led, list);
++ list_del(&led->list);
++ ath_led_brightness(&led->cdev, LED_OFF);
++ led_classdev_unregister(&led->cdev);
++ kfree(led);
++ }
+ }
+
+ void ath_init_leds(struct ath_softc *sc)
+ {
+- int ret;
++ char led_name[32];
++ const char *trigger;
++
++ INIT_LIST_HEAD(&sc->leds);
+
+ if (AR_SREV_9100(sc->sc_ah))
+ return;
+@@ -57,26 +124,15 @@ void ath_init_leds(struct ath_softc *sc)
+ sc->sc_ah->led_pin = ATH_LED_PIN_DEF;
+ }
+
+- /* Configure gpio 1 for output */
+- ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin,
+- AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+- /* LED off, active low */
+- ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1);
+-
+- if (!led_blink)
+- sc->led_cdev.default_trigger =
+- ieee80211_get_radio_led_name(sc->hw);
+-
+- snprintf(sc->led_name, sizeof(sc->led_name),
+- "ath9k-%s", wiphy_name(sc->hw->wiphy));
+- sc->led_cdev.name = sc->led_name;
+- sc->led_cdev.brightness_set = ath_led_brightness;
++ snprintf(led_name, sizeof(led_name), "ath9k-%s",
++ wiphy_name(sc->hw->wiphy));
+
+- ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &sc->led_cdev);
+- if (ret < 0)
+- return;
++ if (led_blink)
++ trigger = sc->led_default_trigger;
++ else
++ trigger = ieee80211_get_radio_led_name(sc->hw);
+
+- sc->led_registered = true;
++ ath_create_gpio_led(sc, sc->sc_ah->led_pin, led_name, trigger, 1);
+ }
+ #endif
+
+--- a/drivers/net/wireless/ath/ath9k/init.c
++++ b/drivers/net/wireless/ath/ath9k/init.c
+@@ -759,7 +759,7 @@ int ath9k_init_device(u16 devid, struct
+
+ #ifdef CONFIG_MAC80211_LEDS
+ /* must be initialized before ieee80211_register_hw */
+- sc->led_cdev.default_trigger = ieee80211_create_tpt_led_trigger(sc->hw,
++ sc->led_default_trigger = ieee80211_create_tpt_led_trigger(sc->hw,
+ IEEE80211_TPT_LEDTRIG_FL_RADIO, ath9k_tpt_blink,
+ ARRAY_SIZE(ath9k_tpt_blink));
+ #endif
+--- a/drivers/net/wireless/ath/ath9k/debug.c
++++ b/drivers/net/wireless/ath/ath9k/debug.c
+@@ -1252,6 +1252,61 @@ static const struct file_operations fops
+ .llseek = default_llseek,
+ };
+
++#ifdef CONFIG_MAC80211_LEDS
++
++static ssize_t write_file_gpio_led(struct file *file, const char __user *ubuf,
++ size_t count, loff_t *ppos)
++{
++ struct ath_softc *sc = file->private_data;
++ char buf[32], *str, *name, *c;
++ ssize_t len;
++ unsigned int gpio;
++ bool active_low = false;
++
++ len = min(count, sizeof(buf) - 1);
++ if (copy_from_user(buf, ubuf, len))
++ return -EFAULT;
++
++ buf[len] = '\0';
++ name = strchr(buf, ',');
++ if (!name)
++ return -EINVAL;
++
++ *(name++) = 0;
++ if (!*name)
++ return -EINVAL;
++
++ c = strchr(name, '\n');
++ if (c)
++ *c = 0;
++
++ str = buf;
++ if (*str == '!') {
++ str++;
++ active_low = true;
++ }
++
++ if (kstrtouint(str, 0, &gpio) < 0)
++ return -EINVAL;
++
++ if (gpio >= sc->sc_ah->caps.num_gpio_pins)
++ return -EINVAL;
++
++ if (ath_create_gpio_led(sc, gpio, name, NULL, active_low) < 0)
++ return -EINVAL;
++
++ return count;
++}
++
++static const struct file_operations fops_gpio_led = {
++ .write = write_file_gpio_led,
++ .open = ath9k_debugfs_open,
++ .owner = THIS_MODULE,
++ .llseek = default_llseek,
++};
++
++#endif
++
+ #ifdef CONFIG_ATH9K_MAC_DEBUG
+
+ void ath9k_debug_samp_bb_mac(struct ath_softc *sc)
+@@ -1681,6 +1736,11 @@ int ath9k_init_debug(struct ath_hw *ah)
+ &fops_samps);
+ #endif
+
++#ifdef CONFIG_MAC80211_LEDS
++ debugfs_create_file("gpio_led", S_IWUSR,
++ sc->debug.debugfs_phy, sc, &fops_gpio_led);
++#endif
++
+ debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR,
+ sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask);
+
+++ /dev/null
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -687,6 +687,9 @@ enum mac80211_rx_flags {
- * @mactime: value in microseconds of the 64-bit Time Synchronization Function
- * (TSF) timer when the first data symbol (MPDU) arrived at the hardware.
- * @band: the active band when this frame was received
-+ * @chains: bitmask of receive chains for which separate signal strength
-+ * values were filled.
-+ * @chain_signal: per-chain signal strength, same format as @signal
- * @freq: frequency the radio was tuned to when receiving this frame, in MHz
- * @signal: signal strength when receiving this frame, either in dBm, in dB or
- * unspecified depending on the hardware capabilities flags
-@@ -700,6 +703,10 @@ enum mac80211_rx_flags {
- struct ieee80211_rx_status {
- u64 mactime;
- enum ieee80211_band band;
-+
-+ u8 chains;
-+ s8 chain_signal[4];
-+
- int freq;
- int signal;
- int antenna;
---- a/net/mac80211/sta_info.h
-+++ b/net/mac80211/sta_info.h
-@@ -313,6 +313,11 @@ struct sta_info {
- unsigned long rx_dropped;
- int last_signal;
- struct ewma avg_signal;
-+
-+ u8 chains;
-+ s8 chain_signal_last[4];
-+ struct ewma chain_signal_avg[4];
-+
- /* Plus 1 for non-QoS frames */
- __le16 last_seq_ctrl[NUM_RX_DATA_QUEUES + 1];
-
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -1271,6 +1271,7 @@ ieee80211_rx_h_sta_process(struct ieee80
- struct sk_buff *skb = rx->skb;
- struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-+ int i;
-
- if (!sta)
- return RX_CONTINUE;
-@@ -1315,6 +1316,19 @@ ieee80211_rx_h_sta_process(struct ieee80
- ewma_add(&sta->avg_signal, -status->signal);
- }
-
-+ if (status->chains) {
-+ sta->chains = status->chains;
-+ for (i = 0; i < 4; i++) {
-+ int signal = status->chain_signal[i];
-+
-+ if (!(status->chains & BIT(i)))
-+ continue;
-+
-+ sta->chain_signal_last[i] = signal;
-+ ewma_add(&sta->chain_signal_avg[i], -signal);
-+ }
-+ }
-+
- /*
- * Change STA power saving mode only at the end of a frame
- * exchange sequence.
---- a/net/mac80211/sta_info.c
-+++ b/net/mac80211/sta_info.c
-@@ -256,6 +256,8 @@ struct sta_info *sta_info_alloc(struct i
- do_posix_clock_monotonic_gettime(&uptime);
- sta->last_connected = uptime.tv_sec;
- ewma_init(&sta->avg_signal, 1024, 8);
-+ for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++)
-+ ewma_init(&sta->chain_signal_avg[i], 1024, 8);
-
- if (sta_prepare_rate_control(local, sta, gfp)) {
- kfree(sta);
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -518,6 +518,8 @@ struct station_parameters {
- * @STATION_INFO_ASSOC_REQ_IES: @assoc_req_ies filled
- * @STATION_INFO_STA_FLAGS: @sta_flags filled
- * @STATION_INFO_BEACON_LOSS_COUNT: @beacon_loss_count filled
-+ * @STATION_INFO_CHAIN_SIGNAL: @chain_signal filled
-+ * @STATION_INFO_CHAIN_SIGNAL_AVG: @chain_signal_avg filled
- */
- enum station_info_flags {
- STATION_INFO_INACTIVE_TIME = 1<<0,
-@@ -539,7 +541,9 @@ enum station_info_flags {
- STATION_INFO_CONNECTED_TIME = 1<<16,
- STATION_INFO_ASSOC_REQ_IES = 1<<17,
- STATION_INFO_STA_FLAGS = 1<<18,
-- STATION_INFO_BEACON_LOSS_COUNT = 1<<19
-+ STATION_INFO_BEACON_LOSS_COUNT = 1<<19,
-+ STATION_INFO_CHAIN_SIGNAL = 1<<20,
-+ STATION_INFO_CHAIN_SIGNAL_AVG = 1<<21,
- };
-
- /**
-@@ -619,6 +623,9 @@ struct sta_bss_parameters {
- * @plink_state: mesh peer link state
- * @signal: signal strength of last received packet in dBm
- * @signal_avg: signal strength average in dBm
-+ * @chains: bitmask for filled values in @chain_signal, @chain_signal_avg
-+ * @chain_signal: per-chain signal strength of last received packet in dBm
-+ * @chain_signal_avg: per-chain signal strength average in dBm
- * @txrate: current unicast bitrate from this station
- * @rxrate: current unicast bitrate to this station
- * @rx_packets: packets received from this station
-@@ -650,6 +657,11 @@ struct station_info {
- u8 plink_state;
- s8 signal;
- s8 signal_avg;
-+
-+ u8 chains;
-+ s8 chain_signal[4];
-+ s8 chain_signal_avg[4];
-+
- struct rate_info txrate;
- struct rate_info rxrate;
- u32 rx_packets;
---- a/drivers/net/wireless/ath/ath9k/mac.h
-+++ b/drivers/net/wireless/ath/ath9k/mac.h
-@@ -133,12 +133,8 @@ struct ath_rx_status {
- u8 rs_rate;
- u8 rs_antenna;
- u8 rs_more;
-- int8_t rs_rssi_ctl0;
-- int8_t rs_rssi_ctl1;
-- int8_t rs_rssi_ctl2;
-- int8_t rs_rssi_ext0;
-- int8_t rs_rssi_ext1;
-- int8_t rs_rssi_ext2;
-+ int8_t rs_rssi_ctl[3];
-+ int8_t rs_rssi_ext[3];
- u8 rs_isaggr;
- u8 rs_moreaggr;
- u8 rs_num_delims;
---- a/drivers/net/wireless/ath/ath9k/recv.c
-+++ b/drivers/net/wireless/ath/ath9k/recv.c
-@@ -987,6 +987,7 @@ static int ath9k_rx_skb_preprocess(struc
- bool *decrypt_error)
- {
- struct ath_hw *ah = common->ah;
-+ int i, j;
-
- memset(rx_status, 0, sizeof(struct ieee80211_rx_status));
-
-@@ -1014,6 +1015,20 @@ static int ath9k_rx_skb_preprocess(struc
- if (rx_stats->rs_moreaggr)
- rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
-
-+ for (i = 0, j = 0; i < ARRAY_SIZE(rx_stats->rs_rssi_ctl); i++) {
-+ s8 rssi;
-+
-+ if (!(ah->rxchainmask & BIT(i)))
-+ continue;
-+
-+ rssi = rx_stats->rs_rssi_ctl[i];
-+ if (rssi != ATH9K_RSSI_BAD) {
-+ rx_status->chains |= BIT(j);
-+ rx_status->chain_signal[j] = ah->noise + rssi;
-+ }
-+ j++;
-+ }
-+
- return 0;
- }
-
-@@ -1544,14 +1559,14 @@ static void ath_ant_comb_scan(struct ath
- struct ath_ant_comb *antcomb = &sc->ant_comb;
- int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
- int curr_main_set;
-- int main_rssi = rs->rs_rssi_ctl0;
-- int alt_rssi = rs->rs_rssi_ctl1;
-+ int main_rssi = rs->rs_rssi_ctl[0];
-+ int alt_rssi = rs->rs_rssi_ctl[1];
- int rx_ant_conf, main_ant_conf;
- bool short_scan = false;
-
-- rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) &
-+ rx_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_CURRENT_SHIFT) &
- ATH_ANT_RX_MASK;
-- main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) &
-+ main_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_MAIN_SHIFT) &
- ATH_ANT_RX_MASK;
-
- /* Record packet only when both main_rssi and alt_rssi is positive */
---- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c
-+++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
-@@ -459,12 +459,12 @@ int ath9k_hw_process_rxdesc_edma(struct
-
- /* XXX: Keycache */
- rxs->rs_rssi = MS(rxsp->status5, AR_RxRSSICombined);
-- rxs->rs_rssi_ctl0 = MS(rxsp->status1, AR_RxRSSIAnt00);
-- rxs->rs_rssi_ctl1 = MS(rxsp->status1, AR_RxRSSIAnt01);
-- rxs->rs_rssi_ctl2 = MS(rxsp->status1, AR_RxRSSIAnt02);
-- rxs->rs_rssi_ext0 = MS(rxsp->status5, AR_RxRSSIAnt10);
-- rxs->rs_rssi_ext1 = MS(rxsp->status5, AR_RxRSSIAnt11);
-- rxs->rs_rssi_ext2 = MS(rxsp->status5, AR_RxRSSIAnt12);
-+ rxs->rs_rssi_ctl[0] = MS(rxsp->status1, AR_RxRSSIAnt00);
-+ rxs->rs_rssi_ctl[1] = MS(rxsp->status1, AR_RxRSSIAnt01);
-+ rxs->rs_rssi_ctl[2] = MS(rxsp->status1, AR_RxRSSIAnt02);
-+ rxs->rs_rssi_ext[0] = MS(rxsp->status5, AR_RxRSSIAnt10);
-+ rxs->rs_rssi_ext[1] = MS(rxsp->status5, AR_RxRSSIAnt11);
-+ rxs->rs_rssi_ext[2] = MS(rxsp->status5, AR_RxRSSIAnt12);
-
- if (rxsp->status11 & AR_RxKeyIdxValid)
- rxs->rs_keyix = MS(rxsp->status11, AR_KeyIdx);
---- a/drivers/net/wireless/ath/ath9k/mac.c
-+++ b/drivers/net/wireless/ath/ath9k/mac.c
-@@ -556,25 +556,25 @@ int ath9k_hw_rxprocdesc(struct ath_hw *a
-
- if (ads.ds_rxstatus8 & AR_PostDelimCRCErr) {
- rs->rs_rssi = ATH9K_RSSI_BAD;
-- rs->rs_rssi_ctl0 = ATH9K_RSSI_BAD;
-- rs->rs_rssi_ctl1 = ATH9K_RSSI_BAD;
-- rs->rs_rssi_ctl2 = ATH9K_RSSI_BAD;
-- rs->rs_rssi_ext0 = ATH9K_RSSI_BAD;
-- rs->rs_rssi_ext1 = ATH9K_RSSI_BAD;
-- rs->rs_rssi_ext2 = ATH9K_RSSI_BAD;
-+ rs->rs_rssi_ctl[0] = ATH9K_RSSI_BAD;
-+ rs->rs_rssi_ctl[1] = ATH9K_RSSI_BAD;
-+ rs->rs_rssi_ctl[2] = ATH9K_RSSI_BAD;
-+ rs->rs_rssi_ext[0] = ATH9K_RSSI_BAD;
-+ rs->rs_rssi_ext[1] = ATH9K_RSSI_BAD;
-+ rs->rs_rssi_ext[2] = ATH9K_RSSI_BAD;
- } else {
- rs->rs_rssi = MS(ads.ds_rxstatus4, AR_RxRSSICombined);
-- rs->rs_rssi_ctl0 = MS(ads.ds_rxstatus0,
-+ rs->rs_rssi_ctl[0] = MS(ads.ds_rxstatus0,
- AR_RxRSSIAnt00);
-- rs->rs_rssi_ctl1 = MS(ads.ds_rxstatus0,
-+ rs->rs_rssi_ctl[1] = MS(ads.ds_rxstatus0,
- AR_RxRSSIAnt01);
-- rs->rs_rssi_ctl2 = MS(ads.ds_rxstatus0,
-+ rs->rs_rssi_ctl[2] = MS(ads.ds_rxstatus0,
- AR_RxRSSIAnt02);
-- rs->rs_rssi_ext0 = MS(ads.ds_rxstatus4,
-+ rs->rs_rssi_ext[0] = MS(ads.ds_rxstatus4,
- AR_RxRSSIAnt10);
-- rs->rs_rssi_ext1 = MS(ads.ds_rxstatus4,
-+ rs->rs_rssi_ext[1] = MS(ads.ds_rxstatus4,
- AR_RxRSSIAnt11);
-- rs->rs_rssi_ext2 = MS(ads.ds_rxstatus4,
-+ rs->rs_rssi_ext[2] = MS(ads.ds_rxstatus4,
- AR_RxRSSIAnt12);
- }
- if (ads.ds_rxstatus8 & AR_RxKeyIdxValid)
---- a/drivers/net/wireless/ath/ath9k/debug.c
-+++ b/drivers/net/wireless/ath/ath9k/debug.c
-@@ -993,12 +993,12 @@ void ath_debug_stat_rx(struct ath_softc
- #ifdef CONFIG_ATH9K_MAC_DEBUG
- spin_lock(&sc->debug.samp_lock);
- RX_SAMP_DBG(jiffies) = jiffies;
-- RX_SAMP_DBG(rssi_ctl0) = rs->rs_rssi_ctl0;
-- RX_SAMP_DBG(rssi_ctl1) = rs->rs_rssi_ctl1;
-- RX_SAMP_DBG(rssi_ctl2) = rs->rs_rssi_ctl2;
-- RX_SAMP_DBG(rssi_ext0) = rs->rs_rssi_ext0;
-- RX_SAMP_DBG(rssi_ext1) = rs->rs_rssi_ext1;
-- RX_SAMP_DBG(rssi_ext2) = rs->rs_rssi_ext2;
-+ RX_SAMP_DBG(rssi_ctl0) = rs->rs_rssi_ctl[0];
-+ RX_SAMP_DBG(rssi_ctl1) = rs->rs_rssi_ctl[1];
-+ RX_SAMP_DBG(rssi_ctl2) = rs->rs_rssi_ctl[2];
-+ RX_SAMP_DBG(rssi_ext0) = rs->rs_rssi_ext[0];
-+ RX_SAMP_DBG(rssi_ext1) = rs->rs_rssi_ext[1];
-+ RX_SAMP_DBG(rssi_ext2) = rs->rs_rssi_ext[2];
- RX_SAMP_DBG(antenna) = rs->rs_antenna;
- RX_SAMP_DBG(rssi) = rs->rs_rssi;
- RX_SAMP_DBG(rate) = rs->rs_rate;
---- a/include/linux/nl80211.h
-+++ b/include/linux/nl80211.h
-@@ -1661,6 +1661,8 @@ enum nl80211_sta_bss_param {
- * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected
- * @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update.
- * @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32)
-+ * @NL80211_STA_INFO_CHAIN_SIGNAL: per-chain signal strength of last PPDU
-+ * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average
- * @__NL80211_STA_INFO_AFTER_LAST: internal
- * @NL80211_STA_INFO_MAX: highest possible station info attribute
- */
-@@ -1684,6 +1686,8 @@ enum nl80211_sta_info {
- NL80211_STA_INFO_CONNECTED_TIME,
- NL80211_STA_INFO_STA_FLAGS,
- NL80211_STA_INFO_BEACON_LOSS,
-+ NL80211_STA_INFO_CHAIN_SIGNAL,
-+ NL80211_STA_INFO_CHAIN_SIGNAL_AVG,
-
- /* keep last */
- __NL80211_STA_INFO_AFTER_LAST,
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -2376,6 +2376,33 @@ nla_put_failure:
- return false;
- }
-
-+static bool nl80211_put_signal(struct sk_buff *msg, u8 mask, s8 *signal,
-+ int id)
-+{
-+ void *attr;
-+ int i = 0;
-+
-+ if (!mask)
-+ return true;
-+
-+ attr = nla_nest_start(msg, id);
-+ if (!attr)
-+ goto nla_put_failure;
-+
-+ for (i = 0; i < 4; i++) {
-+ if (!(mask & BIT(i)))
-+ continue;
-+
-+ NLA_PUT_U8(msg, i, signal[i]);
-+ }
-+
-+ nla_nest_end(msg, attr);
-+
-+ return true;
-+nla_put_failure:
-+ return false;
-+}
-+
- static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
- int flags, struct net_device *dev,
- const u8 *mac_addr, struct station_info *sinfo)
-@@ -2422,6 +2449,18 @@ static int nl80211_send_station(struct s
- if (sinfo->filled & STATION_INFO_SIGNAL_AVG)
- NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL_AVG,
- sinfo->signal_avg);
-+ if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL) {
-+ if (!nl80211_put_signal(msg, sinfo->chains,
-+ sinfo->chain_signal,
-+ NL80211_STA_INFO_CHAIN_SIGNAL))
-+ goto nla_put_failure;
-+ }
-+ if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL_AVG) {
-+ if (!nl80211_put_signal(msg, sinfo->chains,
-+ sinfo->chain_signal_avg,
-+ NL80211_STA_INFO_CHAIN_SIGNAL_AVG))
-+ goto nla_put_failure;
-+ }
- if (sinfo->filled & STATION_INFO_TX_BITRATE) {
- if (!nl80211_put_sta_rate(msg, &sinfo->txrate,
- NL80211_STA_INFO_TX_BITRATE))
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -340,6 +340,7 @@ static void sta_set_sinfo(struct sta_inf
- {
- struct ieee80211_sub_if_data *sdata = sta->sdata;
- struct timespec uptime;
-+ int i;
-
- sinfo->generation = sdata->local->sta_generation;
-
-@@ -377,6 +378,17 @@ static void sta_set_sinfo(struct sta_inf
- sinfo->signal = (s8)sta->last_signal;
- sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
- }
-+ if (sta->chains) {
-+ sinfo->filled |= STATION_INFO_CHAIN_SIGNAL |
-+ STATION_INFO_CHAIN_SIGNAL_AVG;
-+
-+ sinfo->chains = sta->chains;
-+ for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
-+ sinfo->chain_signal[i] = sta->chain_signal_last[i];
-+ sinfo->chain_signal_avg[i] =
-+ (s8) -ewma_read(&sta->chain_signal_avg[i]);
-+ }
-+ }
-
- sinfo->txrate.flags = 0;
- if (sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)
--- /dev/null
+--- a/include/linux/ath9k_platform.h
++++ b/include/linux/ath9k_platform.h
+@@ -32,6 +32,9 @@ struct ath9k_platform_data {
+ bool is_clk_25mhz;
+ int (*get_mac_revision)(void);
+ int (*external_reset)(void);
++
++ int num_leds;
++ const struct gpio_led *leds;
+ };
+
+ #endif /* _LINUX_ATH9K_PLATFORM_H */
+--- a/drivers/net/wireless/ath/ath9k/gpio.c
++++ b/drivers/net/wireless/ath/ath9k/gpio.c
+@@ -14,6 +14,7 @@
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
++#include <linux/ath9k_platform.h>
+ #include "ath9k.h"
+
+ /********************************/
+@@ -88,6 +89,24 @@ int ath_create_gpio_led(struct ath_softc
+ return ret;
+ }
+
++static int ath_create_platform_led(struct ath_softc *sc,
++ const struct gpio_led *gpio)
++{
++ struct ath_led *led;
++ int ret;
++
++ led = kzalloc(sizeof(*led), GFP_KERNEL);
++ if (!led)
++ return -ENOMEM;
++
++ led->gpio = gpio;
++ ret = ath_add_led(sc, led);
++ if (ret < 0)
++ kfree(led);
++
++ return ret;
++}
++
+ void ath_deinit_leds(struct ath_softc *sc)
+ {
+ struct ath_led *led;
+@@ -103,8 +122,10 @@ void ath_deinit_leds(struct ath_softc *s
+
+ void ath_init_leds(struct ath_softc *sc)
+ {
++ struct ath9k_platform_data *pdata = sc->dev->platform_data;
+ char led_name[32];
+ const char *trigger;
++ int i;
+
+ INIT_LIST_HEAD(&sc->leds);
+
+@@ -133,6 +154,12 @@ void ath_init_leds(struct ath_softc *sc)
+ trigger = ieee80211_get_radio_led_name(sc->hw);
+
+ ath_create_gpio_led(sc, sc->sc_ah->led_pin, led_name, trigger, 1);
++
++ if (!pdata)
++ return;
++
++ for (i = 0; i < pdata->num_leds; i++)
++ ath_create_platform_led(sc, &pdata->leds[i]);
+ }
+ #endif
+
+++ /dev/null
---- a/drivers/net/wireless/ath/ath9k/ath9k.h
-+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
-@@ -505,6 +505,9 @@ static inline u16 ath9k_btcoex_aggr_limi
- #ifdef CONFIG_MAC80211_LEDS
- void ath_init_leds(struct ath_softc *sc);
- void ath_deinit_leds(struct ath_softc *sc);
-+int ath_create_gpio_led(struct ath_softc *sc, int gpio, const char *name,
-+ const char *trigger, bool active_low);
-+
- #else
- static inline void ath_init_leds(struct ath_softc *sc)
- {
-@@ -625,6 +628,13 @@ struct ath9k_vif_iter_data {
- int nothers; /* number of vifs not specified above. */
- };
-
-+struct ath_led {
-+ struct list_head list;
-+ struct ath_softc *sc;
-+ const struct gpio_led *gpio;
-+ struct led_classdev cdev;
-+};
-+
- struct ath_softc {
- struct ieee80211_hw *hw;
- struct device *dev;
-@@ -668,9 +678,8 @@ struct ath_softc {
- struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
-
- #ifdef CONFIG_MAC80211_LEDS
-- bool led_registered;
-- char led_name[32];
-- struct led_classdev led_cdev;
-+ const char *led_default_trigger;
-+ struct list_head leds;
- #endif
-
- struct ath9k_hw_cal_data caldata;
---- a/drivers/net/wireless/ath/ath9k/gpio.c
-+++ b/drivers/net/wireless/ath/ath9k/gpio.c
-@@ -24,22 +24,89 @@
- static void ath_led_brightness(struct led_classdev *led_cdev,
- enum led_brightness brightness)
- {
-- struct ath_softc *sc = container_of(led_cdev, struct ath_softc, led_cdev);
-- ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, (brightness == LED_OFF));
-+ struct ath_led *led = container_of(led_cdev, struct ath_led, cdev);
-+ struct ath_softc *sc = led->sc;
-+
-+ ath9k_ps_wakeup(sc);
-+ ath9k_hw_set_gpio(sc->sc_ah, led->gpio->gpio,
-+ (brightness != LED_OFF) ^ led->gpio->active_low);
-+ ath9k_ps_restore(sc);
-+}
-+
-+static int ath_add_led(struct ath_softc *sc, struct ath_led *led)
-+{
-+ const struct gpio_led *gpio = led->gpio;
-+ int ret;
-+
-+ led->cdev.name = gpio->name;
-+ led->cdev.default_trigger = gpio->default_trigger;
-+ led->cdev.brightness_set = ath_led_brightness;
-+
-+ ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->cdev);
-+ if (ret < 0)
-+ return ret;
-+
-+ led->sc = sc;
-+ list_add(&led->list, &sc->leds);
-+
-+ /* Configure gpio for output */
-+ ath9k_hw_cfg_output(sc->sc_ah, gpio->gpio,
-+ AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
-+
-+ /* LED off */
-+ ath9k_hw_set_gpio(sc->sc_ah, gpio->gpio, gpio->active_low);
-+
-+ return 0;
-+}
-+
-+int ath_create_gpio_led(struct ath_softc *sc, int gpio_num, const char *name,
-+ const char *trigger, bool active_low)
-+{
-+ struct ath_led *led;
-+ struct gpio_led *gpio;
-+ char *_name;
-+ int ret;
-+
-+ led = kzalloc(sizeof(*led) + sizeof(*gpio) + strlen(name) + 1,
-+ GFP_KERNEL);
-+ if (!led)
-+ return -ENOMEM;
-+
-+ led->gpio = gpio = (struct gpio_led *) (led + 1);
-+ _name = (char *) (led->gpio + 1);
-+
-+ strcpy(_name, name);
-+ gpio->name = _name;
-+ gpio->gpio = gpio_num;
-+ gpio->active_low = active_low;
-+ gpio->default_trigger = trigger;
-+
-+ ret = ath_add_led(sc, led);
-+ if (unlikely(ret < 0))
-+ kfree(led);
-+
-+ return ret;
- }
-
- void ath_deinit_leds(struct ath_softc *sc)
- {
-- if (!sc->led_registered)
-- return;
-+ struct ath_led *led;
-
-- ath_led_brightness(&sc->led_cdev, LED_OFF);
-- led_classdev_unregister(&sc->led_cdev);
-+ while (!list_empty(&sc->leds)) {
-+ led = list_first_entry(&sc->leds, struct ath_led, list);
-+ list_del(&led->list);
-+ ath_led_brightness(&led->cdev, LED_OFF);
-+ led_classdev_unregister(&led->cdev);
-+ kfree(led);
-+ }
- }
-
- void ath_init_leds(struct ath_softc *sc)
- {
-- int ret;
-+ char led_name[32];
-+ const char *trigger;
-+
-+ INIT_LIST_HEAD(&sc->leds);
-
- if (AR_SREV_9100(sc->sc_ah))
- return;
-@@ -57,26 +124,15 @@ void ath_init_leds(struct ath_softc *sc)
- sc->sc_ah->led_pin = ATH_LED_PIN_DEF;
- }
-
-- /* Configure gpio 1 for output */
-- ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin,
-- AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
-- /* LED off, active low */
-- ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1);
--
-- if (!led_blink)
-- sc->led_cdev.default_trigger =
-- ieee80211_get_radio_led_name(sc->hw);
--
-- snprintf(sc->led_name, sizeof(sc->led_name),
-- "ath9k-%s", wiphy_name(sc->hw->wiphy));
-- sc->led_cdev.name = sc->led_name;
-- sc->led_cdev.brightness_set = ath_led_brightness;
-+ snprintf(led_name, sizeof(led_name), "ath9k-%s",
-+ wiphy_name(sc->hw->wiphy));
-
-- ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &sc->led_cdev);
-- if (ret < 0)
-- return;
-+ if (led_blink)
-+ trigger = sc->led_default_trigger;
-+ else
-+ trigger = ieee80211_get_radio_led_name(sc->hw);
-
-- sc->led_registered = true;
-+ ath_create_gpio_led(sc, sc->sc_ah->led_pin, led_name, trigger, 1);
- }
- #endif
-
---- a/drivers/net/wireless/ath/ath9k/init.c
-+++ b/drivers/net/wireless/ath/ath9k/init.c
-@@ -759,7 +759,7 @@ int ath9k_init_device(u16 devid, struct
-
- #ifdef CONFIG_MAC80211_LEDS
- /* must be initialized before ieee80211_register_hw */
-- sc->led_cdev.default_trigger = ieee80211_create_tpt_led_trigger(sc->hw,
-+ sc->led_default_trigger = ieee80211_create_tpt_led_trigger(sc->hw,
- IEEE80211_TPT_LEDTRIG_FL_RADIO, ath9k_tpt_blink,
- ARRAY_SIZE(ath9k_tpt_blink));
- #endif
---- a/drivers/net/wireless/ath/ath9k/debug.c
-+++ b/drivers/net/wireless/ath/ath9k/debug.c
-@@ -1252,6 +1252,61 @@ static const struct file_operations fops
- .llseek = default_llseek,
- };
-
-+#ifdef CONFIG_MAC80211_LEDS
-+
-+static ssize_t write_file_gpio_led(struct file *file, const char __user *ubuf,
-+ size_t count, loff_t *ppos)
-+{
-+ struct ath_softc *sc = file->private_data;
-+ char buf[32], *str, *name, *c;
-+ ssize_t len;
-+ unsigned int gpio;
-+ bool active_low = false;
-+
-+ len = min(count, sizeof(buf) - 1);
-+ if (copy_from_user(buf, ubuf, len))
-+ return -EFAULT;
-+
-+ buf[len] = '\0';
-+ name = strchr(buf, ',');
-+ if (!name)
-+ return -EINVAL;
-+
-+ *(name++) = 0;
-+ if (!*name)
-+ return -EINVAL;
-+
-+ c = strchr(name, '\n');
-+ if (c)
-+ *c = 0;
-+
-+ str = buf;
-+ if (*str == '!') {
-+ str++;
-+ active_low = true;
-+ }
-+
-+ if (kstrtouint(str, 0, &gpio) < 0)
-+ return -EINVAL;
-+
-+ if (gpio >= sc->sc_ah->caps.num_gpio_pins)
-+ return -EINVAL;
-+
-+ if (ath_create_gpio_led(sc, gpio, name, NULL, active_low) < 0)
-+ return -EINVAL;
-+
-+ return count;
-+}
-+
-+static const struct file_operations fops_gpio_led = {
-+ .write = write_file_gpio_led,
-+ .open = ath9k_debugfs_open,
-+ .owner = THIS_MODULE,
-+ .llseek = default_llseek,
-+};
-+
-+#endif
-+
- #ifdef CONFIG_ATH9K_MAC_DEBUG
-
- void ath9k_debug_samp_bb_mac(struct ath_softc *sc)
-@@ -1681,6 +1736,11 @@ int ath9k_init_debug(struct ath_hw *ah)
- &fops_samps);
- #endif
-
-+#ifdef CONFIG_MAC80211_LEDS
-+ debugfs_create_file("gpio_led", S_IWUSR,
-+ sc->debug.debugfs_phy, sc, &fops_gpio_led);
-+#endif
-+
- debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR,
- sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask);
-
+++ /dev/null
---- a/include/linux/ath9k_platform.h
-+++ b/include/linux/ath9k_platform.h
-@@ -32,6 +32,9 @@ struct ath9k_platform_data {
- bool is_clk_25mhz;
- int (*get_mac_revision)(void);
- int (*external_reset)(void);
-+
-+ int num_leds;
-+ const struct gpio_led *leds;
- };
-
- #endif /* _LINUX_ATH9K_PLATFORM_H */
---- a/drivers/net/wireless/ath/ath9k/gpio.c
-+++ b/drivers/net/wireless/ath/ath9k/gpio.c
-@@ -14,6 +14,7 @@
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-+#include <linux/ath9k_platform.h>
- #include "ath9k.h"
-
- /********************************/
-@@ -88,6 +89,24 @@ int ath_create_gpio_led(struct ath_softc
- return ret;
- }
-
-+static int ath_create_platform_led(struct ath_softc *sc,
-+ const struct gpio_led *gpio)
-+{
-+ struct ath_led *led;
-+ int ret;
-+
-+ led = kzalloc(sizeof(*led), GFP_KERNEL);
-+ if (!led)
-+ return -ENOMEM;
-+
-+ led->gpio = gpio;
-+ ret = ath_add_led(sc, led);
-+ if (ret < 0)
-+ kfree(led);
-+
-+ return ret;
-+}
-+
- void ath_deinit_leds(struct ath_softc *sc)
- {
- struct ath_led *led;
-@@ -103,8 +122,10 @@ void ath_deinit_leds(struct ath_softc *s
-
- void ath_init_leds(struct ath_softc *sc)
- {
-+ struct ath9k_platform_data *pdata = sc->dev->platform_data;
- char led_name[32];
- const char *trigger;
-+ int i;
-
- INIT_LIST_HEAD(&sc->leds);
-
-@@ -133,6 +154,12 @@ void ath_init_leds(struct ath_softc *sc)
- trigger = ieee80211_get_radio_led_name(sc->hw);
-
- ath_create_gpio_led(sc, sc->sc_ah->led_pin, led_name, trigger, 1);
-+
-+ if (!pdata)
-+ return;
-+
-+ for (i = 0; i < pdata->num_leds; i++)
-+ ath_create_platform_led(sc, &pdata->leds[i]);
- }
- #endif
-