| 1 | --- a/include/net/mac80211.h |
| 2 | +++ b/include/net/mac80211.h |
| 3 | @@ -684,6 +684,9 @@ enum mac80211_rx_flags { |
| 4 | * @mactime: value in microseconds of the 64-bit Time Synchronization Function |
| 5 | * (TSF) timer when the first data symbol (MPDU) arrived at the hardware. |
| 6 | * @band: the active band when this frame was received |
| 7 | + * @chains: bitmask of receive chains for which separate signal strength |
| 8 | + * values were filled. |
| 9 | + * @chain_signal: per-chain signal strength, same format as @signal |
| 10 | * @freq: frequency the radio was tuned to when receiving this frame, in MHz |
| 11 | * @signal: signal strength when receiving this frame, either in dBm, in dB or |
| 12 | * unspecified depending on the hardware capabilities flags |
| 13 | @@ -697,6 +700,10 @@ enum mac80211_rx_flags { |
| 14 | struct ieee80211_rx_status { |
| 15 | u64 mactime; |
| 16 | enum ieee80211_band band; |
| 17 | + |
| 18 | + u8 chains; |
| 19 | + s8 chain_signal[4]; |
| 20 | + |
| 21 | int freq; |
| 22 | int signal; |
| 23 | int antenna; |
| 24 | --- a/net/mac80211/sta_info.h |
| 25 | +++ b/net/mac80211/sta_info.h |
| 26 | @@ -313,6 +313,11 @@ struct sta_info { |
| 27 | unsigned long rx_dropped; |
| 28 | int last_signal; |
| 29 | struct ewma avg_signal; |
| 30 | + |
| 31 | + u8 chains; |
| 32 | + s8 chain_signal_last[4]; |
| 33 | + struct ewma chain_signal_avg[4]; |
| 34 | + |
| 35 | /* Plus 1 for non-QoS frames */ |
| 36 | __le16 last_seq_ctrl[NUM_RX_DATA_QUEUES + 1]; |
| 37 | |
| 38 | --- a/net/mac80211/rx.c |
| 39 | +++ b/net/mac80211/rx.c |
| 40 | @@ -1270,6 +1270,7 @@ ieee80211_rx_h_sta_process(struct ieee80 |
| 41 | struct sk_buff *skb = rx->skb; |
| 42 | struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); |
| 43 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; |
| 44 | + int i; |
| 45 | |
| 46 | if (!sta) |
| 47 | return RX_CONTINUE; |
| 48 | @@ -1312,6 +1313,19 @@ ieee80211_rx_h_sta_process(struct ieee80 |
| 49 | sta->last_signal = status->signal; |
| 50 | ewma_add(&sta->avg_signal, -status->signal); |
| 51 | |
| 52 | + if (status->chains) { |
| 53 | + sta->chains = status->chains; |
| 54 | + for (i = 0; i < 4; i++) { |
| 55 | + int signal = status->chain_signal[i]; |
| 56 | + |
| 57 | + if (!(status->chains & BIT(i))) |
| 58 | + continue; |
| 59 | + |
| 60 | + sta->chain_signal_last[i] = signal; |
| 61 | + ewma_add(&sta->chain_signal_avg[i], -signal); |
| 62 | + } |
| 63 | + } |
| 64 | + |
| 65 | /* |
| 66 | * Change STA power saving mode only at the end of a frame |
| 67 | * exchange sequence. |
| 68 | --- a/net/mac80211/sta_info.c |
| 69 | +++ b/net/mac80211/sta_info.c |
| 70 | @@ -256,6 +256,8 @@ struct sta_info *sta_info_alloc(struct i |
| 71 | do_posix_clock_monotonic_gettime(&uptime); |
| 72 | sta->last_connected = uptime.tv_sec; |
| 73 | ewma_init(&sta->avg_signal, 1024, 8); |
| 74 | + for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++) |
| 75 | + ewma_init(&sta->chain_signal_avg[i], 1024, 8); |
| 76 | |
| 77 | if (sta_prepare_rate_control(local, sta, gfp)) { |
| 78 | kfree(sta); |
| 79 | --- a/include/net/cfg80211.h |
| 80 | +++ b/include/net/cfg80211.h |
| 81 | @@ -518,6 +518,8 @@ struct station_parameters { |
| 82 | * @STATION_INFO_ASSOC_REQ_IES: @assoc_req_ies filled |
| 83 | * @STATION_INFO_STA_FLAGS: @sta_flags filled |
| 84 | * @STATION_INFO_BEACON_LOSS_COUNT: @beacon_loss_count filled |
| 85 | + * @STATION_INFO_CHAIN_SIGNAL: @chain_signal filled |
| 86 | + * @STATION_INFO_CHAIN_SIGNAL_AVG: @chain_signal_avg filled |
| 87 | */ |
| 88 | enum station_info_flags { |
| 89 | STATION_INFO_INACTIVE_TIME = 1<<0, |
| 90 | @@ -539,7 +541,9 @@ enum station_info_flags { |
| 91 | STATION_INFO_CONNECTED_TIME = 1<<16, |
| 92 | STATION_INFO_ASSOC_REQ_IES = 1<<17, |
| 93 | STATION_INFO_STA_FLAGS = 1<<18, |
| 94 | - STATION_INFO_BEACON_LOSS_COUNT = 1<<19 |
| 95 | + STATION_INFO_BEACON_LOSS_COUNT = 1<<19, |
| 96 | + STATION_INFO_CHAIN_SIGNAL = 1<<20, |
| 97 | + STATION_INFO_CHAIN_SIGNAL_AVG = 1<<21, |
| 98 | }; |
| 99 | |
| 100 | /** |
| 101 | @@ -619,6 +623,9 @@ struct sta_bss_parameters { |
| 102 | * @plink_state: mesh peer link state |
| 103 | * @signal: signal strength of last received packet in dBm |
| 104 | * @signal_avg: signal strength average in dBm |
| 105 | + * @chains: bitmask for filled values in @chain_signal, @chain_signal_avg |
| 106 | + * @chain_signal: per-chain signal strength of last received packet in dBm |
| 107 | + * @chain_signal_avg: per-chain signal strength average in dBm |
| 108 | * @txrate: current unicast bitrate from this station |
| 109 | * @rxrate: current unicast bitrate to this station |
| 110 | * @rx_packets: packets received from this station |
| 111 | @@ -650,6 +657,11 @@ struct station_info { |
| 112 | u8 plink_state; |
| 113 | s8 signal; |
| 114 | s8 signal_avg; |
| 115 | + |
| 116 | + u8 chains; |
| 117 | + s8 chain_signal[4]; |
| 118 | + s8 chain_signal_avg[4]; |
| 119 | + |
| 120 | struct rate_info txrate; |
| 121 | struct rate_info rxrate; |
| 122 | u32 rx_packets; |
| 123 | --- a/drivers/net/wireless/ath/ath9k/mac.h |
| 124 | +++ b/drivers/net/wireless/ath/ath9k/mac.h |
| 125 | @@ -133,12 +133,8 @@ struct ath_rx_status { |
| 126 | u8 rs_rate; |
| 127 | u8 rs_antenna; |
| 128 | u8 rs_more; |
| 129 | - int8_t rs_rssi_ctl0; |
| 130 | - int8_t rs_rssi_ctl1; |
| 131 | - int8_t rs_rssi_ctl2; |
| 132 | - int8_t rs_rssi_ext0; |
| 133 | - int8_t rs_rssi_ext1; |
| 134 | - int8_t rs_rssi_ext2; |
| 135 | + int8_t rs_rssi_ctl[3]; |
| 136 | + int8_t rs_rssi_ext[3]; |
| 137 | u8 rs_isaggr; |
| 138 | u8 rs_moreaggr; |
| 139 | u8 rs_num_delims; |
| 140 | --- a/drivers/net/wireless/ath/ath9k/recv.c |
| 141 | +++ b/drivers/net/wireless/ath/ath9k/recv.c |
| 142 | @@ -987,6 +987,7 @@ static int ath9k_rx_skb_preprocess(struc |
| 143 | bool *decrypt_error) |
| 144 | { |
| 145 | struct ath_hw *ah = common->ah; |
| 146 | + int i, j; |
| 147 | |
| 148 | memset(rx_status, 0, sizeof(struct ieee80211_rx_status)); |
| 149 | |
| 150 | @@ -1012,6 +1013,20 @@ static int ath9k_rx_skb_preprocess(struc |
| 151 | rx_status->antenna = rx_stats->rs_antenna; |
| 152 | rx_status->flag |= RX_FLAG_MACTIME_MPDU; |
| 153 | |
| 154 | + for (i = 0, j = 0; i < ARRAY_SIZE(rx_stats->rs_rssi_ctl); i++) { |
| 155 | + s8 rssi; |
| 156 | + |
| 157 | + if (!(ah->rxchainmask & BIT(i))) |
| 158 | + continue; |
| 159 | + |
| 160 | + rssi = rx_stats->rs_rssi_ctl[i]; |
| 161 | + if (rssi != ATH9K_RSSI_BAD) { |
| 162 | + rx_status->chains |= BIT(j); |
| 163 | + rx_status->chain_signal[j] = ah->noise + rssi; |
| 164 | + } |
| 165 | + j++; |
| 166 | + } |
| 167 | + |
| 168 | return 0; |
| 169 | } |
| 170 | |
| 171 | @@ -1542,14 +1557,14 @@ static void ath_ant_comb_scan(struct ath |
| 172 | struct ath_ant_comb *antcomb = &sc->ant_comb; |
| 173 | int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set; |
| 174 | int curr_main_set; |
| 175 | - int main_rssi = rs->rs_rssi_ctl0; |
| 176 | - int alt_rssi = rs->rs_rssi_ctl1; |
| 177 | + int main_rssi = rs->rs_rssi_ctl[0]; |
| 178 | + int alt_rssi = rs->rs_rssi_ctl[1]; |
| 179 | int rx_ant_conf, main_ant_conf; |
| 180 | bool short_scan = false; |
| 181 | |
| 182 | - rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) & |
| 183 | + rx_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_CURRENT_SHIFT) & |
| 184 | ATH_ANT_RX_MASK; |
| 185 | - main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) & |
| 186 | + main_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_MAIN_SHIFT) & |
| 187 | ATH_ANT_RX_MASK; |
| 188 | |
| 189 | /* Record packet only when both main_rssi and alt_rssi is positive */ |
| 190 | --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c |
| 191 | +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c |
| 192 | @@ -463,12 +463,12 @@ int ath9k_hw_process_rxdesc_edma(struct |
| 193 | |
| 194 | /* XXX: Keycache */ |
| 195 | rxs->rs_rssi = MS(rxsp->status5, AR_RxRSSICombined); |
| 196 | - rxs->rs_rssi_ctl0 = MS(rxsp->status1, AR_RxRSSIAnt00); |
| 197 | - rxs->rs_rssi_ctl1 = MS(rxsp->status1, AR_RxRSSIAnt01); |
| 198 | - rxs->rs_rssi_ctl2 = MS(rxsp->status1, AR_RxRSSIAnt02); |
| 199 | - rxs->rs_rssi_ext0 = MS(rxsp->status5, AR_RxRSSIAnt10); |
| 200 | - rxs->rs_rssi_ext1 = MS(rxsp->status5, AR_RxRSSIAnt11); |
| 201 | - rxs->rs_rssi_ext2 = MS(rxsp->status5, AR_RxRSSIAnt12); |
| 202 | + rxs->rs_rssi_ctl[0] = MS(rxsp->status1, AR_RxRSSIAnt00); |
| 203 | + rxs->rs_rssi_ctl[1] = MS(rxsp->status1, AR_RxRSSIAnt01); |
| 204 | + rxs->rs_rssi_ctl[2] = MS(rxsp->status1, AR_RxRSSIAnt02); |
| 205 | + rxs->rs_rssi_ext[0] = MS(rxsp->status5, AR_RxRSSIAnt10); |
| 206 | + rxs->rs_rssi_ext[1] = MS(rxsp->status5, AR_RxRSSIAnt11); |
| 207 | + rxs->rs_rssi_ext[2] = MS(rxsp->status5, AR_RxRSSIAnt12); |
| 208 | |
| 209 | if (rxsp->status11 & AR_RxKeyIdxValid) |
| 210 | rxs->rs_keyix = MS(rxsp->status11, AR_KeyIdx); |
| 211 | --- a/drivers/net/wireless/ath/ath9k/mac.c |
| 212 | +++ b/drivers/net/wireless/ath/ath9k/mac.c |
| 213 | @@ -556,25 +556,25 @@ int ath9k_hw_rxprocdesc(struct ath_hw *a |
| 214 | |
| 215 | if (ads.ds_rxstatus8 & AR_PostDelimCRCErr) { |
| 216 | rs->rs_rssi = ATH9K_RSSI_BAD; |
| 217 | - rs->rs_rssi_ctl0 = ATH9K_RSSI_BAD; |
| 218 | - rs->rs_rssi_ctl1 = ATH9K_RSSI_BAD; |
| 219 | - rs->rs_rssi_ctl2 = ATH9K_RSSI_BAD; |
| 220 | - rs->rs_rssi_ext0 = ATH9K_RSSI_BAD; |
| 221 | - rs->rs_rssi_ext1 = ATH9K_RSSI_BAD; |
| 222 | - rs->rs_rssi_ext2 = ATH9K_RSSI_BAD; |
| 223 | + rs->rs_rssi_ctl[0] = ATH9K_RSSI_BAD; |
| 224 | + rs->rs_rssi_ctl[1] = ATH9K_RSSI_BAD; |
| 225 | + rs->rs_rssi_ctl[2] = ATH9K_RSSI_BAD; |
| 226 | + rs->rs_rssi_ext[0] = ATH9K_RSSI_BAD; |
| 227 | + rs->rs_rssi_ext[1] = ATH9K_RSSI_BAD; |
| 228 | + rs->rs_rssi_ext[2] = ATH9K_RSSI_BAD; |
| 229 | } else { |
| 230 | rs->rs_rssi = MS(ads.ds_rxstatus4, AR_RxRSSICombined); |
| 231 | - rs->rs_rssi_ctl0 = MS(ads.ds_rxstatus0, |
| 232 | + rs->rs_rssi_ctl[0] = MS(ads.ds_rxstatus0, |
| 233 | AR_RxRSSIAnt00); |
| 234 | - rs->rs_rssi_ctl1 = MS(ads.ds_rxstatus0, |
| 235 | + rs->rs_rssi_ctl[1] = MS(ads.ds_rxstatus0, |
| 236 | AR_RxRSSIAnt01); |
| 237 | - rs->rs_rssi_ctl2 = MS(ads.ds_rxstatus0, |
| 238 | + rs->rs_rssi_ctl[2] = MS(ads.ds_rxstatus0, |
| 239 | AR_RxRSSIAnt02); |
| 240 | - rs->rs_rssi_ext0 = MS(ads.ds_rxstatus4, |
| 241 | + rs->rs_rssi_ext[0] = MS(ads.ds_rxstatus4, |
| 242 | AR_RxRSSIAnt10); |
| 243 | - rs->rs_rssi_ext1 = MS(ads.ds_rxstatus4, |
| 244 | + rs->rs_rssi_ext[1] = MS(ads.ds_rxstatus4, |
| 245 | AR_RxRSSIAnt11); |
| 246 | - rs->rs_rssi_ext2 = MS(ads.ds_rxstatus4, |
| 247 | + rs->rs_rssi_ext[2] = MS(ads.ds_rxstatus4, |
| 248 | AR_RxRSSIAnt12); |
| 249 | } |
| 250 | if (ads.ds_rxstatus8 & AR_RxKeyIdxValid) |
| 251 | --- a/drivers/net/wireless/ath/ath9k/debug.c |
| 252 | +++ b/drivers/net/wireless/ath/ath9k/debug.c |
| 253 | @@ -993,12 +993,12 @@ void ath_debug_stat_rx(struct ath_softc |
| 254 | #ifdef CONFIG_ATH9K_MAC_DEBUG |
| 255 | spin_lock(&sc->debug.samp_lock); |
| 256 | RX_SAMP_DBG(jiffies) = jiffies; |
| 257 | - RX_SAMP_DBG(rssi_ctl0) = rs->rs_rssi_ctl0; |
| 258 | - RX_SAMP_DBG(rssi_ctl1) = rs->rs_rssi_ctl1; |
| 259 | - RX_SAMP_DBG(rssi_ctl2) = rs->rs_rssi_ctl2; |
| 260 | - RX_SAMP_DBG(rssi_ext0) = rs->rs_rssi_ext0; |
| 261 | - RX_SAMP_DBG(rssi_ext1) = rs->rs_rssi_ext1; |
| 262 | - RX_SAMP_DBG(rssi_ext2) = rs->rs_rssi_ext2; |
| 263 | + RX_SAMP_DBG(rssi_ctl0) = rs->rs_rssi_ctl[0]; |
| 264 | + RX_SAMP_DBG(rssi_ctl1) = rs->rs_rssi_ctl[1]; |
| 265 | + RX_SAMP_DBG(rssi_ctl2) = rs->rs_rssi_ctl[2]; |
| 266 | + RX_SAMP_DBG(rssi_ext0) = rs->rs_rssi_ext[0]; |
| 267 | + RX_SAMP_DBG(rssi_ext1) = rs->rs_rssi_ext[1]; |
| 268 | + RX_SAMP_DBG(rssi_ext2) = rs->rs_rssi_ext[2]; |
| 269 | RX_SAMP_DBG(antenna) = rs->rs_antenna; |
| 270 | RX_SAMP_DBG(rssi) = rs->rs_rssi; |
| 271 | RX_SAMP_DBG(rate) = rs->rs_rate; |
| 272 | --- a/include/linux/nl80211.h |
| 273 | +++ b/include/linux/nl80211.h |
| 274 | @@ -1661,6 +1661,8 @@ enum nl80211_sta_bss_param { |
| 275 | * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected |
| 276 | * @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update. |
| 277 | * @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32) |
| 278 | + * @NL80211_STA_INFO_CHAIN_SIGNAL: per-chain signal strength of last PPDU |
| 279 | + * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average |
| 280 | * @__NL80211_STA_INFO_AFTER_LAST: internal |
| 281 | * @NL80211_STA_INFO_MAX: highest possible station info attribute |
| 282 | */ |
| 283 | @@ -1684,6 +1686,8 @@ enum nl80211_sta_info { |
| 284 | NL80211_STA_INFO_CONNECTED_TIME, |
| 285 | NL80211_STA_INFO_STA_FLAGS, |
| 286 | NL80211_STA_INFO_BEACON_LOSS, |
| 287 | + NL80211_STA_INFO_CHAIN_SIGNAL, |
| 288 | + NL80211_STA_INFO_CHAIN_SIGNAL_AVG, |
| 289 | |
| 290 | /* keep last */ |
| 291 | __NL80211_STA_INFO_AFTER_LAST, |
| 292 | --- a/net/wireless/nl80211.c |
| 293 | +++ b/net/wireless/nl80211.c |
| 294 | @@ -2376,6 +2376,33 @@ nla_put_failure: |
| 295 | return false; |
| 296 | } |
| 297 | |
| 298 | +static bool nl80211_put_signal(struct sk_buff *msg, u8 mask, s8 *signal, |
| 299 | + int id) |
| 300 | +{ |
| 301 | + void *attr; |
| 302 | + int i = 0; |
| 303 | + |
| 304 | + if (!mask) |
| 305 | + return true; |
| 306 | + |
| 307 | + attr = nla_nest_start(msg, id); |
| 308 | + if (!attr) |
| 309 | + goto nla_put_failure; |
| 310 | + |
| 311 | + for (i = 0; i < 4; i++) { |
| 312 | + if (!(mask & BIT(i))) |
| 313 | + continue; |
| 314 | + |
| 315 | + NLA_PUT_U8(msg, i, signal[i]); |
| 316 | + } |
| 317 | + |
| 318 | + nla_nest_end(msg, attr); |
| 319 | + |
| 320 | + return true; |
| 321 | +nla_put_failure: |
| 322 | + return false; |
| 323 | +} |
| 324 | + |
| 325 | static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, |
| 326 | int flags, struct net_device *dev, |
| 327 | const u8 *mac_addr, struct station_info *sinfo) |
| 328 | @@ -2422,6 +2449,18 @@ static int nl80211_send_station(struct s |
| 329 | if (sinfo->filled & STATION_INFO_SIGNAL_AVG) |
| 330 | NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL_AVG, |
| 331 | sinfo->signal_avg); |
| 332 | + if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL) { |
| 333 | + if (!nl80211_put_signal(msg, sinfo->chains, |
| 334 | + sinfo->chain_signal, |
| 335 | + NL80211_STA_INFO_CHAIN_SIGNAL)) |
| 336 | + goto nla_put_failure; |
| 337 | + } |
| 338 | + if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL_AVG) { |
| 339 | + if (!nl80211_put_signal(msg, sinfo->chains, |
| 340 | + sinfo->chain_signal_avg, |
| 341 | + NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) |
| 342 | + goto nla_put_failure; |
| 343 | + } |
| 344 | if (sinfo->filled & STATION_INFO_TX_BITRATE) { |
| 345 | if (!nl80211_put_sta_rate(msg, &sinfo->txrate, |
| 346 | NL80211_STA_INFO_TX_BITRATE)) |
| 347 | --- a/net/mac80211/cfg.c |
| 348 | +++ b/net/mac80211/cfg.c |
| 349 | @@ -340,6 +340,7 @@ static void sta_set_sinfo(struct sta_inf |
| 350 | { |
| 351 | struct ieee80211_sub_if_data *sdata = sta->sdata; |
| 352 | struct timespec uptime; |
| 353 | + int i; |
| 354 | |
| 355 | sinfo->generation = sdata->local->sta_generation; |
| 356 | |
| 357 | @@ -377,6 +378,17 @@ static void sta_set_sinfo(struct sta_inf |
| 358 | sinfo->signal = (s8)sta->last_signal; |
| 359 | sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal); |
| 360 | } |
| 361 | + if (sta->chains) { |
| 362 | + sinfo->filled |= STATION_INFO_CHAIN_SIGNAL | |
| 363 | + STATION_INFO_CHAIN_SIGNAL_AVG; |
| 364 | + |
| 365 | + sinfo->chains = sta->chains; |
| 366 | + for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) { |
| 367 | + sinfo->chain_signal[i] = sta->chain_signal_last[i]; |
| 368 | + sinfo->chain_signal_avg[i] = |
| 369 | + (s8) -ewma_read(&sta->chain_signal_avg[i]); |
| 370 | + } |
| 371 | + } |
| 372 | |
| 373 | sinfo->txrate.flags = 0; |
| 374 | if (sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS) |
| 375 | |