Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ath9k: Implement remain-on-channal support

Add remain on channel support in order to enable multi-channel
concurrency.

Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>

authored by

Felix Fietkau and committed by
John W. Linville
405393cf 78b21949

+124 -15
+6
drivers/net/wireless/ath/ath9k/ath9k.h
··· 338 338 ATH_OFFCHANNEL_PROBE_SEND, 339 339 ATH_OFFCHANNEL_PROBE_WAIT, 340 340 ATH_OFFCHANNEL_SUSPEND, 341 + ATH_OFFCHANNEL_ROC_START, 342 + ATH_OFFCHANNEL_ROC_WAIT, 343 + ATH_OFFCHANNEL_ROC_DONE, 341 344 }; 342 345 343 346 struct ath_offchannel { ··· 350 347 struct ieee80211_vif *scan_vif; 351 348 int scan_idx; 352 349 enum ath_offchannel_state state; 350 + struct ieee80211_channel *roc_chan; 351 + struct ieee80211_vif *roc_vif; 352 + int roc_duration; 353 353 }; 354 354 355 355 void ath9k_fill_chanctx_ops(void);
+1
drivers/net/wireless/ath/ath9k/init.c
··· 751 751 hw->wiphy->n_iface_combinations = 1; 752 752 hw->wiphy->max_scan_ssids = 255; 753 753 hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; 754 + hw->wiphy->max_remain_on_channel_duration = 10000; 754 755 } 755 756 } 756 757
+112 -14
drivers/net/wireless/ath/ath9k/main.c
··· 2176 2176 ath_chanctx_offchan_switch(sc, chan); 2177 2177 } 2178 2178 2179 + static void ath_offchannel_next(struct ath_softc *sc) 2180 + { 2181 + struct ieee80211_vif *vif; 2182 + 2183 + if (sc->offchannel.scan_req) { 2184 + vif = sc->offchannel.scan_vif; 2185 + sc->offchannel.chan.txpower = vif->bss_conf.txpower; 2186 + ath_scan_next_channel(sc); 2187 + } else if (sc->offchannel.roc_vif) { 2188 + vif = sc->offchannel.roc_vif; 2189 + sc->offchannel.chan.txpower = vif->bss_conf.txpower; 2190 + sc->offchannel.state = ATH_OFFCHANNEL_ROC_START; 2191 + ath_chanctx_offchan_switch(sc, sc->offchannel.roc_chan); 2192 + } else { 2193 + ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc), NULL); 2194 + sc->offchannel.state = ATH_OFFCHANNEL_IDLE; 2195 + if (sc->ps_idle) 2196 + ath_cancel_work(sc); 2197 + } 2198 + } 2199 + 2200 + static void ath_roc_complete(struct ath_softc *sc, bool abort) 2201 + { 2202 + sc->offchannel.roc_vif = NULL; 2203 + sc->offchannel.roc_chan = NULL; 2204 + if (!abort) 2205 + ieee80211_remain_on_channel_expired(sc->hw); 2206 + ath_offchannel_next(sc); 2207 + ath9k_ps_restore(sc); 2208 + } 2209 + 2179 2210 static void ath_scan_complete(struct ath_softc *sc, bool abort) 2180 2211 { 2181 2212 struct ath_common *common = ath9k_hw_common(sc->sc_ah); 2182 2213 2183 - ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc), NULL); 2184 2214 sc->offchannel.scan_req = NULL; 2185 2215 sc->offchannel.scan_vif = NULL; 2186 2216 sc->offchannel.state = ATH_OFFCHANNEL_IDLE; 2187 2217 ieee80211_scan_completed(sc->hw, abort); 2188 2218 clear_bit(ATH_OP_SCANNING, &common->op_flags); 2219 + ath_offchannel_next(sc); 2189 2220 ath9k_ps_restore(sc); 2190 - 2191 - if (!sc->ps_idle) 2192 - return; 2193 - 2194 - ath_cancel_work(sc); 2195 2221 } 2196 2222 2197 2223 static void ath_scan_send_probe(struct ath_softc *sc, ··· 2279 2253 2280 2254 void ath_offchannel_channel_change(struct ath_softc *sc) 2281 2255 { 2282 - if (!sc->offchannel.scan_req) 2283 - return; 2284 - 2285 2256 switch (sc->offchannel.state) { 2286 2257 case ATH_OFFCHANNEL_PROBE_SEND: 2258 + if (!sc->offchannel.scan_req) 2259 + return; 2260 + 2287 2261 if (sc->cur_chan->chandef.chan != 2288 2262 sc->offchannel.chan.chandef.chan) 2289 2263 return; ··· 2291 2265 ath_scan_channel_start(sc); 2292 2266 break; 2293 2267 case ATH_OFFCHANNEL_IDLE: 2268 + if (!sc->offchannel.scan_req) 2269 + return; 2270 + 2294 2271 ath_scan_complete(sc, false); 2272 + break; 2273 + case ATH_OFFCHANNEL_ROC_START: 2274 + if (sc->cur_chan != &sc->offchannel.chan) 2275 + break; 2276 + 2277 + sc->offchannel.state = ATH_OFFCHANNEL_ROC_WAIT; 2278 + mod_timer(&sc->offchannel.timer, jiffies + 2279 + msecs_to_jiffies(sc->offchannel.roc_duration)); 2280 + ieee80211_ready_on_channel(sc->hw); 2281 + break; 2282 + case ATH_OFFCHANNEL_ROC_DONE: 2283 + ath_roc_complete(sc, false); 2295 2284 break; 2296 2285 default: 2297 2286 break; ··· 2318 2277 struct ath_softc *sc = (struct ath_softc *)data; 2319 2278 struct ath_chanctx *ctx = ath_chanctx_get_oper_chan(sc); 2320 2279 2321 - if (!sc->offchannel.scan_req) 2322 - return; 2323 - 2324 2280 switch (sc->offchannel.state) { 2325 2281 case ATH_OFFCHANNEL_PROBE_WAIT: 2282 + if (!sc->offchannel.scan_req) 2283 + return; 2284 + 2326 2285 if (ctx->active) { 2327 2286 sc->offchannel.state = ATH_OFFCHANNEL_SUSPEND; 2328 2287 ath_chanctx_switch(sc, ctx, NULL); ··· 2331 2290 } 2332 2291 /* fall through */ 2333 2292 case ATH_OFFCHANNEL_SUSPEND: 2293 + if (!sc->offchannel.scan_req) 2294 + return; 2295 + 2334 2296 ath_scan_next_channel(sc); 2297 + break; 2298 + case ATH_OFFCHANNEL_ROC_START: 2299 + case ATH_OFFCHANNEL_ROC_WAIT: 2300 + sc->offchannel.state = ATH_OFFCHANNEL_ROC_DONE; 2301 + ath_chanctx_switch(sc, ctx, NULL); 2335 2302 break; 2336 2303 default: 2337 2304 break; ··· 2365 2316 sc->offchannel.scan_vif = vif; 2366 2317 sc->offchannel.scan_req = req; 2367 2318 sc->offchannel.scan_idx = 0; 2368 - sc->offchannel.chan.txpower = vif->bss_conf.txpower; 2369 2319 2370 - ath_scan_next_channel(sc); 2320 + if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE) 2321 + ath_offchannel_next(sc); 2371 2322 2372 2323 out: 2373 2324 mutex_unlock(&sc->mutex); ··· 2386 2337 mutex_unlock(&sc->mutex); 2387 2338 } 2388 2339 2340 + static int ath9k_remain_on_channel(struct ieee80211_hw *hw, 2341 + struct ieee80211_vif *vif, 2342 + struct ieee80211_channel *chan, int duration, 2343 + enum ieee80211_roc_type type) 2344 + { 2345 + struct ath_softc *sc = hw->priv; 2346 + int ret = 0; 2347 + 2348 + mutex_lock(&sc->mutex); 2349 + 2350 + if (WARN_ON(sc->offchannel.roc_vif)) { 2351 + ret = -EBUSY; 2352 + goto out; 2353 + } 2354 + 2355 + ath9k_ps_wakeup(sc); 2356 + sc->offchannel.roc_vif = vif; 2357 + sc->offchannel.roc_chan = chan; 2358 + sc->offchannel.roc_duration = duration; 2359 + 2360 + if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE) 2361 + ath_offchannel_next(sc); 2362 + 2363 + out: 2364 + mutex_unlock(&sc->mutex); 2365 + 2366 + return ret; 2367 + } 2368 + 2369 + static int ath9k_cancel_remain_on_channel(struct ieee80211_hw *hw) 2370 + { 2371 + struct ath_softc *sc = hw->priv; 2372 + 2373 + mutex_lock(&sc->mutex); 2374 + 2375 + del_timer_sync(&sc->offchannel.timer); 2376 + 2377 + if (sc->offchannel.roc_vif) { 2378 + if (sc->offchannel.state >= ATH_OFFCHANNEL_ROC_START) 2379 + ath_roc_complete(sc, true); 2380 + } 2381 + 2382 + mutex_unlock(&sc->mutex); 2383 + 2384 + return 0; 2385 + } 2386 + 2389 2387 void ath9k_fill_chanctx_ops(void) 2390 2388 { 2391 2389 if (!ath9k_use_chanctx) ··· 2440 2344 2441 2345 ath9k_ops.hw_scan = ath9k_hw_scan; 2442 2346 ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan; 2347 + ath9k_ops.remain_on_channel = ath9k_remain_on_channel; 2348 + ath9k_ops.cancel_remain_on_channel = ath9k_cancel_remain_on_channel; 2443 2349 } 2444 2350 2445 2351 struct ieee80211_ops ath9k_ops = {
+5 -1
drivers/net/wireless/ath/ath9k/xmit.c
··· 2197 2197 if (vif) 2198 2198 avp = (void *)vif->drv_priv; 2199 2199 2200 + if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) 2201 + txctl->force_channel = true; 2202 + 2200 2203 ret = ath_tx_prepare(hw, skb, txctl); 2201 2204 if (ret) 2202 2205 return ret; ··· 2237 2234 if (txctl->an && queue) 2238 2235 tid = ath_get_skb_tid(sc, txctl->an, skb); 2239 2236 2240 - if (info->flags & IEEE80211_TX_CTL_PS_RESPONSE) { 2237 + if (info->flags & (IEEE80211_TX_CTL_PS_RESPONSE | 2238 + IEEE80211_TX_CTL_TX_OFFCHAN)) { 2241 2239 ath_txq_unlock(sc, txq); 2242 2240 txq = sc->tx.uapsdq; 2243 2241 ath_txq_lock(sc, txq);