diff options
author | Felix Fietkau <nbd@openwrt.org> | 2014-06-11 06:47:56 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2014-06-19 15:49:17 -0400 |
commit | 405393cfde07781c21cdee28b145919d6dfe382e (patch) | |
tree | c840e3690eb6b988d320d4bb44a75b6cdc15c220 /drivers/net/wireless/ath/ath9k/main.c | |
parent | 78b21949711ee3c877f1aab5b51abe1981e1161d (diff) |
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>
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/main.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/main.c | 126 |
1 files changed, 112 insertions, 14 deletions
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 5da62ef1fc26..9e10434dbfa5 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c | |||
@@ -2176,22 +2176,48 @@ ath_scan_next_channel(struct ath_softc *sc) | |||
2176 | ath_chanctx_offchan_switch(sc, chan); | 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 | static void ath_scan_complete(struct ath_softc *sc, bool abort) | 2210 | static void ath_scan_complete(struct ath_softc *sc, bool abort) |
2180 | { | 2211 | { |
2181 | struct ath_common *common = ath9k_hw_common(sc->sc_ah); | 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 | sc->offchannel.scan_req = NULL; | 2214 | sc->offchannel.scan_req = NULL; |
2185 | sc->offchannel.scan_vif = NULL; | 2215 | sc->offchannel.scan_vif = NULL; |
2186 | sc->offchannel.state = ATH_OFFCHANNEL_IDLE; | 2216 | sc->offchannel.state = ATH_OFFCHANNEL_IDLE; |
2187 | ieee80211_scan_completed(sc->hw, abort); | 2217 | ieee80211_scan_completed(sc->hw, abort); |
2188 | clear_bit(ATH_OP_SCANNING, &common->op_flags); | 2218 | clear_bit(ATH_OP_SCANNING, &common->op_flags); |
2219 | ath_offchannel_next(sc); | ||
2189 | ath9k_ps_restore(sc); | 2220 | ath9k_ps_restore(sc); |
2190 | |||
2191 | if (!sc->ps_idle) | ||
2192 | return; | ||
2193 | |||
2194 | ath_cancel_work(sc); | ||
2195 | } | 2221 | } |
2196 | 2222 | ||
2197 | static void ath_scan_send_probe(struct ath_softc *sc, | 2223 | static void ath_scan_send_probe(struct ath_softc *sc, |
@@ -2253,11 +2279,11 @@ static void ath_scan_channel_start(struct ath_softc *sc) | |||
2253 | 2279 | ||
2254 | void ath_offchannel_channel_change(struct ath_softc *sc) | 2280 | void ath_offchannel_channel_change(struct ath_softc *sc) |
2255 | { | 2281 | { |
2256 | if (!sc->offchannel.scan_req) | ||
2257 | return; | ||
2258 | |||
2259 | switch (sc->offchannel.state) { | 2282 | switch (sc->offchannel.state) { |
2260 | case ATH_OFFCHANNEL_PROBE_SEND: | 2283 | case ATH_OFFCHANNEL_PROBE_SEND: |
2284 | if (!sc->offchannel.scan_req) | ||
2285 | return; | ||
2286 | |||
2261 | if (sc->cur_chan->chandef.chan != | 2287 | if (sc->cur_chan->chandef.chan != |
2262 | sc->offchannel.chan.chandef.chan) | 2288 | sc->offchannel.chan.chandef.chan) |
2263 | return; | 2289 | return; |
@@ -2265,8 +2291,23 @@ void ath_offchannel_channel_change(struct ath_softc *sc) | |||
2265 | ath_scan_channel_start(sc); | 2291 | ath_scan_channel_start(sc); |
2266 | break; | 2292 | break; |
2267 | case ATH_OFFCHANNEL_IDLE: | 2293 | case ATH_OFFCHANNEL_IDLE: |
2294 | if (!sc->offchannel.scan_req) | ||
2295 | return; | ||
2296 | |||
2268 | ath_scan_complete(sc, false); | 2297 | ath_scan_complete(sc, false); |
2269 | break; | 2298 | break; |
2299 | case ATH_OFFCHANNEL_ROC_START: | ||
2300 | if (sc->cur_chan != &sc->offchannel.chan) | ||
2301 | break; | ||
2302 | |||
2303 | sc->offchannel.state = ATH_OFFCHANNEL_ROC_WAIT; | ||
2304 | mod_timer(&sc->offchannel.timer, jiffies + | ||
2305 | msecs_to_jiffies(sc->offchannel.roc_duration)); | ||
2306 | ieee80211_ready_on_channel(sc->hw); | ||
2307 | break; | ||
2308 | case ATH_OFFCHANNEL_ROC_DONE: | ||
2309 | ath_roc_complete(sc, false); | ||
2310 | break; | ||
2270 | default: | 2311 | default: |
2271 | break; | 2312 | break; |
2272 | } | 2313 | } |
@@ -2277,11 +2318,11 @@ void ath_offchannel_timer(unsigned long data) | |||
2277 | struct ath_softc *sc = (struct ath_softc *)data; | 2318 | struct ath_softc *sc = (struct ath_softc *)data; |
2278 | struct ath_chanctx *ctx = ath_chanctx_get_oper_chan(sc); | 2319 | struct ath_chanctx *ctx = ath_chanctx_get_oper_chan(sc); |
2279 | 2320 | ||
2280 | if (!sc->offchannel.scan_req) | ||
2281 | return; | ||
2282 | |||
2283 | switch (sc->offchannel.state) { | 2321 | switch (sc->offchannel.state) { |
2284 | case ATH_OFFCHANNEL_PROBE_WAIT: | 2322 | case ATH_OFFCHANNEL_PROBE_WAIT: |
2323 | if (!sc->offchannel.scan_req) | ||
2324 | return; | ||
2325 | |||
2285 | if (ctx->active) { | 2326 | if (ctx->active) { |
2286 | sc->offchannel.state = ATH_OFFCHANNEL_SUSPEND; | 2327 | sc->offchannel.state = ATH_OFFCHANNEL_SUSPEND; |
2287 | ath_chanctx_switch(sc, ctx, NULL); | 2328 | ath_chanctx_switch(sc, ctx, NULL); |
@@ -2290,8 +2331,16 @@ void ath_offchannel_timer(unsigned long data) | |||
2290 | } | 2331 | } |
2291 | /* fall through */ | 2332 | /* fall through */ |
2292 | case ATH_OFFCHANNEL_SUSPEND: | 2333 | case ATH_OFFCHANNEL_SUSPEND: |
2334 | if (!sc->offchannel.scan_req) | ||
2335 | return; | ||
2336 | |||
2293 | ath_scan_next_channel(sc); | 2337 | ath_scan_next_channel(sc); |
2294 | break; | 2338 | break; |
2339 | case ATH_OFFCHANNEL_ROC_START: | ||
2340 | case ATH_OFFCHANNEL_ROC_WAIT: | ||
2341 | sc->offchannel.state = ATH_OFFCHANNEL_ROC_DONE; | ||
2342 | ath_chanctx_switch(sc, ctx, NULL); | ||
2343 | break; | ||
2295 | default: | 2344 | default: |
2296 | break; | 2345 | break; |
2297 | } | 2346 | } |
@@ -2316,9 +2365,9 @@ static int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | |||
2316 | sc->offchannel.scan_vif = vif; | 2365 | sc->offchannel.scan_vif = vif; |
2317 | sc->offchannel.scan_req = req; | 2366 | sc->offchannel.scan_req = req; |
2318 | sc->offchannel.scan_idx = 0; | 2367 | sc->offchannel.scan_idx = 0; |
2319 | sc->offchannel.chan.txpower = vif->bss_conf.txpower; | ||
2320 | 2368 | ||
2321 | ath_scan_next_channel(sc); | 2369 | if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE) |
2370 | ath_offchannel_next(sc); | ||
2322 | 2371 | ||
2323 | out: | 2372 | out: |
2324 | mutex_unlock(&sc->mutex); | 2373 | mutex_unlock(&sc->mutex); |
@@ -2337,6 +2386,53 @@ static void ath9k_cancel_hw_scan(struct ieee80211_hw *hw, | |||
2337 | mutex_unlock(&sc->mutex); | 2386 | mutex_unlock(&sc->mutex); |
2338 | } | 2387 | } |
2339 | 2388 | ||
2389 | static int ath9k_remain_on_channel(struct ieee80211_hw *hw, | ||
2390 | struct ieee80211_vif *vif, | ||
2391 | struct ieee80211_channel *chan, int duration, | ||
2392 | enum ieee80211_roc_type type) | ||
2393 | { | ||
2394 | struct ath_softc *sc = hw->priv; | ||
2395 | int ret = 0; | ||
2396 | |||
2397 | mutex_lock(&sc->mutex); | ||
2398 | |||
2399 | if (WARN_ON(sc->offchannel.roc_vif)) { | ||
2400 | ret = -EBUSY; | ||
2401 | goto out; | ||
2402 | } | ||
2403 | |||
2404 | ath9k_ps_wakeup(sc); | ||
2405 | sc->offchannel.roc_vif = vif; | ||
2406 | sc->offchannel.roc_chan = chan; | ||
2407 | sc->offchannel.roc_duration = duration; | ||
2408 | |||
2409 | if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE) | ||
2410 | ath_offchannel_next(sc); | ||
2411 | |||
2412 | out: | ||
2413 | mutex_unlock(&sc->mutex); | ||
2414 | |||
2415 | return ret; | ||
2416 | } | ||
2417 | |||
2418 | static int ath9k_cancel_remain_on_channel(struct ieee80211_hw *hw) | ||
2419 | { | ||
2420 | struct ath_softc *sc = hw->priv; | ||
2421 | |||
2422 | mutex_lock(&sc->mutex); | ||
2423 | |||
2424 | del_timer_sync(&sc->offchannel.timer); | ||
2425 | |||
2426 | if (sc->offchannel.roc_vif) { | ||
2427 | if (sc->offchannel.state >= ATH_OFFCHANNEL_ROC_START) | ||
2428 | ath_roc_complete(sc, true); | ||
2429 | } | ||
2430 | |||
2431 | mutex_unlock(&sc->mutex); | ||
2432 | |||
2433 | return 0; | ||
2434 | } | ||
2435 | |||
2340 | void ath9k_fill_chanctx_ops(void) | 2436 | void ath9k_fill_chanctx_ops(void) |
2341 | { | 2437 | { |
2342 | if (!ath9k_use_chanctx) | 2438 | if (!ath9k_use_chanctx) |
@@ -2344,6 +2440,8 @@ void ath9k_fill_chanctx_ops(void) | |||
2344 | 2440 | ||
2345 | ath9k_ops.hw_scan = ath9k_hw_scan; | 2441 | ath9k_ops.hw_scan = ath9k_hw_scan; |
2346 | ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan; | 2442 | ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan; |
2443 | ath9k_ops.remain_on_channel = ath9k_remain_on_channel; | ||
2444 | ath9k_ops.cancel_remain_on_channel = ath9k_cancel_remain_on_channel; | ||
2347 | } | 2445 | } |
2348 | 2446 | ||
2349 | struct ieee80211_ops ath9k_ops = { | 2447 | struct ieee80211_ops ath9k_ops = { |