aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@openwrt.org>2014-06-11 06:47:55 -0400
committerJohn W. Linville <linville@tuxdriver.com>2014-06-19 15:49:17 -0400
commit78b21949711ee3c877f1aab5b51abe1981e1161d (patch)
tree317ab473cd8a8ca5a96fdd000a5fc70a644868be /drivers
parentc083ce9980109065297aa2171d18980a0ac92bb9 (diff)
ath9k: Implement hw_scan support
Implement hw_scan support for enabling multi-channel cuncurrency. 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')
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h24
-rw-r--r--drivers/net/wireless/ath/ath9k/channel.c31
-rw-r--r--drivers/net/wireless/ath/ath9k/init.c9
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c187
-rw-r--r--drivers/net/wireless/ath/ath9k/pci.c1
-rw-r--r--drivers/net/wireless/ath/ath9k/recv.c5
6 files changed, 255 insertions, 2 deletions
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index d5a586bbab7c..d6b0c4e55c95 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -35,6 +35,7 @@ extern struct ieee80211_ops ath9k_ops;
35extern int ath9k_modparam_nohwcrypt; 35extern int ath9k_modparam_nohwcrypt;
36extern int led_blink; 36extern int led_blink;
37extern bool is_ath9k_unloaded; 37extern bool is_ath9k_unloaded;
38extern int ath9k_use_chanctx;
38 39
39/*************************/ 40/*************************/
40/* Descriptor Management */ 41/* Descriptor Management */
@@ -332,12 +333,34 @@ struct ath_chanctx {
332 bool active; 333 bool active;
333}; 334};
334 335
336enum ath_offchannel_state {
337 ATH_OFFCHANNEL_IDLE,
338 ATH_OFFCHANNEL_PROBE_SEND,
339 ATH_OFFCHANNEL_PROBE_WAIT,
340 ATH_OFFCHANNEL_SUSPEND,
341};
342
343struct ath_offchannel {
344 struct ath_chanctx chan;
345 struct timer_list timer;
346 struct cfg80211_scan_request *scan_req;
347 struct ieee80211_vif *scan_vif;
348 int scan_idx;
349 enum ath_offchannel_state state;
350};
351
352void ath9k_fill_chanctx_ops(void);
335void ath_chanctx_init(struct ath_softc *sc); 353void ath_chanctx_init(struct ath_softc *sc);
336void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx, 354void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
337 struct cfg80211_chan_def *chandef); 355 struct cfg80211_chan_def *chandef);
338void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx, 356void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx,
339 struct cfg80211_chan_def *chandef); 357 struct cfg80211_chan_def *chandef);
340void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx); 358void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx);
359void ath_offchannel_timer(unsigned long data);
360void ath_offchannel_channel_change(struct ath_softc *sc);
361void ath_chanctx_offchan_switch(struct ath_softc *sc,
362 struct ieee80211_channel *chan);
363struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc);
341 364
342int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan); 365int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan);
343int ath_startrecv(struct ath_softc *sc); 366int ath_startrecv(struct ath_softc *sc);
@@ -771,6 +794,7 @@ struct ath_softc {
771 struct ath_chanctx *cur_chan; 794 struct ath_chanctx *cur_chan;
772 struct ath_chanctx *next_chan; 795 struct ath_chanctx *next_chan;
773 spinlock_t chan_lock; 796 spinlock_t chan_lock;
797 struct ath_offchannel offchannel;
774 798
775#ifdef CONFIG_MAC80211_LEDS 799#ifdef CONFIG_MAC80211_LEDS
776 bool led_registered; 800 bool led_registered;
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index 26fc98b3495b..c679a26045ac 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -228,6 +228,7 @@ void ath_chanctx_work(struct work_struct *work)
228 if (send_ps) 228 if (send_ps)
229 ath_chanctx_send_ps_frame(sc, false); 229 ath_chanctx_send_ps_frame(sc, false);
230 230
231 ath_offchannel_channel_change(sc);
231 mutex_unlock(&sc->mutex); 232 mutex_unlock(&sc->mutex);
232} 233}
233 234
@@ -253,6 +254,14 @@ void ath_chanctx_init(struct ath_softc *sc)
253 INIT_LIST_HEAD(&ctx->acq[j]); 254 INIT_LIST_HEAD(&ctx->acq[j]);
254 } 255 }
255 sc->cur_chan = &sc->chanctx[0]; 256 sc->cur_chan = &sc->chanctx[0];
257 ctx = &sc->offchannel.chan;
258 cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20);
259 INIT_LIST_HEAD(&ctx->vifs);
260 ctx->txpower = ATH_TXPOWER_MAX;
261 for (j = 0; j < ARRAY_SIZE(ctx->acq); j++)
262 INIT_LIST_HEAD(&ctx->acq[j]);
263 sc->offchannel.chan.offchannel = true;
264
256} 265}
257 266
258void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx, 267void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx,
@@ -283,3 +292,25 @@ void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
283 292
284 ath_set_channel(sc); 293 ath_set_channel(sc);
285} 294}
295
296struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc)
297{
298 u8 i;
299
300 for (i = 0; i < ARRAY_SIZE(sc->chanctx); i++) {
301 if (!list_empty(&sc->chanctx[i].vifs))
302 return &sc->chanctx[i];
303 }
304
305 return &sc->chanctx[0];
306}
307
308void ath_chanctx_offchan_switch(struct ath_softc *sc,
309 struct ieee80211_channel *chan)
310{
311 struct cfg80211_chan_def chandef;
312
313 cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
314
315 ath_chanctx_switch(sc, &sc->offchannel.chan, &chandef);
316}
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 8bd3e422b82b..9f5e1e4931af 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -61,7 +61,7 @@ static int ath9k_ps_enable;
61module_param_named(ps_enable, ath9k_ps_enable, int, 0444); 61module_param_named(ps_enable, ath9k_ps_enable, int, 0444);
62MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave"); 62MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave");
63 63
64static int ath9k_use_chanctx; 64int ath9k_use_chanctx;
65module_param_named(use_chanctx, ath9k_use_chanctx, int, 0444); 65module_param_named(use_chanctx, ath9k_use_chanctx, int, 0444);
66MODULE_PARM_DESC(use_chanctx, "Enable channel context for concurrency"); 66MODULE_PARM_DESC(use_chanctx, "Enable channel context for concurrency");
67 67
@@ -566,6 +566,8 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
566 INIT_WORK(&sc->paprd_work, ath_paprd_calibrate); 566 INIT_WORK(&sc->paprd_work, ath_paprd_calibrate);
567 INIT_WORK(&sc->chanctx_work, ath_chanctx_work); 567 INIT_WORK(&sc->chanctx_work, ath_chanctx_work);
568 INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work); 568 INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work);
569 setup_timer(&sc->offchannel.timer, ath_offchannel_timer,
570 (unsigned long)sc);
569 571
570 /* 572 /*
571 * Cache line size is used to size and align various 573 * Cache line size is used to size and align various
@@ -745,8 +747,11 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
745 if (!ath9k_use_chanctx) { 747 if (!ath9k_use_chanctx) {
746 hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); 748 hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
747 hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_WDS); 749 hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_WDS);
748 } else 750 } else {
749 hw->wiphy->n_iface_combinations = 1; 751 hw->wiphy->n_iface_combinations = 1;
752 hw->wiphy->max_scan_ssids = 255;
753 hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
754 }
750 } 755 }
751 756
752 hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; 757 hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index d8a4510f963a..5da62ef1fc26 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -2159,6 +2159,193 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
2159 clear_bit(ATH_OP_SCANNING, &common->op_flags); 2159 clear_bit(ATH_OP_SCANNING, &common->op_flags);
2160} 2160}
2161 2161
2162static void
2163ath_scan_next_channel(struct ath_softc *sc)
2164{
2165 struct cfg80211_scan_request *req = sc->offchannel.scan_req;
2166 struct ieee80211_channel *chan;
2167
2168 if (sc->offchannel.scan_idx >= req->n_channels) {
2169 sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
2170 ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc), NULL);
2171 return;
2172 }
2173
2174 chan = req->channels[sc->offchannel.scan_idx++];
2175 sc->offchannel.state = ATH_OFFCHANNEL_PROBE_SEND;
2176 ath_chanctx_offchan_switch(sc, chan);
2177}
2178
2179static void ath_scan_complete(struct ath_softc *sc, bool abort)
2180{
2181 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
2182
2183 ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc), NULL);
2184 sc->offchannel.scan_req = NULL;
2185 sc->offchannel.scan_vif = NULL;
2186 sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
2187 ieee80211_scan_completed(sc->hw, abort);
2188 clear_bit(ATH_OP_SCANNING, &common->op_flags);
2189 ath9k_ps_restore(sc);
2190
2191 if (!sc->ps_idle)
2192 return;
2193
2194 ath_cancel_work(sc);
2195}
2196
2197static void ath_scan_send_probe(struct ath_softc *sc,
2198 struct cfg80211_ssid *ssid)
2199{
2200 struct cfg80211_scan_request *req = sc->offchannel.scan_req;
2201 struct ieee80211_vif *vif = sc->offchannel.scan_vif;
2202 struct ath_tx_control txctl = {};
2203 struct sk_buff *skb;
2204 struct ieee80211_tx_info *info;
2205 int band = sc->offchannel.chan.chandef.chan->band;
2206
2207 skb = ieee80211_probereq_get(sc->hw, vif,
2208 ssid->ssid, ssid->ssid_len, req->ie_len);
2209 if (!skb)
2210 return;
2211
2212 info = IEEE80211_SKB_CB(skb);
2213 if (req->no_cck)
2214 info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
2215
2216 if (req->ie_len)
2217 memcpy(skb_put(skb, req->ie_len), req->ie, req->ie_len);
2218
2219 skb_set_queue_mapping(skb, IEEE80211_AC_VO);
2220
2221 if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, NULL))
2222 goto error;
2223
2224 txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
2225 txctl.force_channel = true;
2226 if (ath_tx_start(sc->hw, skb, &txctl))
2227 goto error;
2228
2229 return;
2230
2231error:
2232 ieee80211_free_txskb(sc->hw, skb);
2233}
2234
2235static void ath_scan_channel_start(struct ath_softc *sc)
2236{
2237 struct cfg80211_scan_request *req = sc->offchannel.scan_req;
2238 int i, dwell;
2239
2240 if ((sc->cur_chan->chandef.chan->flags & IEEE80211_CHAN_NO_IR) ||
2241 !req->n_ssids) {
2242 dwell = HZ / 9; /* ~110 ms */
2243 } else {
2244 dwell = HZ / 16; /* ~60 ms */
2245
2246 for (i = 0; i < req->n_ssids; i++)
2247 ath_scan_send_probe(sc, &req->ssids[i]);
2248 }
2249
2250 sc->offchannel.state = ATH_OFFCHANNEL_PROBE_WAIT;
2251 mod_timer(&sc->offchannel.timer, jiffies + dwell);
2252}
2253
2254void ath_offchannel_channel_change(struct ath_softc *sc)
2255{
2256 if (!sc->offchannel.scan_req)
2257 return;
2258
2259 switch (sc->offchannel.state) {
2260 case ATH_OFFCHANNEL_PROBE_SEND:
2261 if (sc->cur_chan->chandef.chan !=
2262 sc->offchannel.chan.chandef.chan)
2263 return;
2264
2265 ath_scan_channel_start(sc);
2266 break;
2267 case ATH_OFFCHANNEL_IDLE:
2268 ath_scan_complete(sc, false);
2269 break;
2270 default:
2271 break;
2272 }
2273}
2274
2275void ath_offchannel_timer(unsigned long data)
2276{
2277 struct ath_softc *sc = (struct ath_softc *)data;
2278 struct ath_chanctx *ctx = ath_chanctx_get_oper_chan(sc);
2279
2280 if (!sc->offchannel.scan_req)
2281 return;
2282
2283 switch (sc->offchannel.state) {
2284 case ATH_OFFCHANNEL_PROBE_WAIT:
2285 if (ctx->active) {
2286 sc->offchannel.state = ATH_OFFCHANNEL_SUSPEND;
2287 ath_chanctx_switch(sc, ctx, NULL);
2288 mod_timer(&sc->offchannel.timer, jiffies + HZ / 10);
2289 break;
2290 }
2291 /* fall through */
2292 case ATH_OFFCHANNEL_SUSPEND:
2293 ath_scan_next_channel(sc);
2294 break;
2295 default:
2296 break;
2297 }
2298}
2299
2300static int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
2301 struct cfg80211_scan_request *req)
2302{
2303 struct ath_softc *sc = hw->priv;
2304 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
2305 int ret = 0;
2306
2307 mutex_lock(&sc->mutex);
2308
2309 if (WARN_ON(sc->offchannel.scan_req)) {
2310 ret = -EBUSY;
2311 goto out;
2312 }
2313
2314 ath9k_ps_wakeup(sc);
2315 set_bit(ATH_OP_SCANNING, &common->op_flags);
2316 sc->offchannel.scan_vif = vif;
2317 sc->offchannel.scan_req = req;
2318 sc->offchannel.scan_idx = 0;
2319 sc->offchannel.chan.txpower = vif->bss_conf.txpower;
2320
2321 ath_scan_next_channel(sc);
2322
2323out:
2324 mutex_unlock(&sc->mutex);
2325
2326 return ret;
2327}
2328
2329static void ath9k_cancel_hw_scan(struct ieee80211_hw *hw,
2330 struct ieee80211_vif *vif)
2331{
2332 struct ath_softc *sc = hw->priv;
2333
2334 mutex_lock(&sc->mutex);
2335 del_timer_sync(&sc->offchannel.timer);
2336 ath_scan_complete(sc, true);
2337 mutex_unlock(&sc->mutex);
2338}
2339
2340void ath9k_fill_chanctx_ops(void)
2341{
2342 if (!ath9k_use_chanctx)
2343 return;
2344
2345 ath9k_ops.hw_scan = ath9k_hw_scan;
2346 ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan;
2347}
2348
2162struct ieee80211_ops ath9k_ops = { 2349struct ieee80211_ops ath9k_ops = {
2163 .tx = ath9k_tx, 2350 .tx = ath9k_tx,
2164 .start = ath9k_start, 2351 .start = ath9k_start,
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 4dec09e565ed..7a2b2c5caced 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -843,6 +843,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
843 return -ENODEV; 843 return -ENODEV;
844 } 844 }
845 845
846 ath9k_fill_chanctx_ops();
846 hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops); 847 hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops);
847 if (!hw) { 848 if (!hw) {
848 dev_err(&pdev->dev, "No memory for ieee80211_hw\n"); 849 dev_err(&pdev->dev, "No memory for ieee80211_hw\n");
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index de5684a33dd7..fec9e0b42f5d 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -374,6 +374,7 @@ void ath_rx_cleanup(struct ath_softc *sc)
374 374
375u32 ath_calcrxfilter(struct ath_softc *sc) 375u32 ath_calcrxfilter(struct ath_softc *sc)
376{ 376{
377 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
377 u32 rfilt; 378 u32 rfilt;
378 379
379 if (config_enabled(CONFIG_ATH9K_TX99)) 380 if (config_enabled(CONFIG_ATH9K_TX99))
@@ -424,6 +425,10 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
424 if (AR_SREV_9550(sc->sc_ah) || AR_SREV_9531(sc->sc_ah)) 425 if (AR_SREV_9550(sc->sc_ah) || AR_SREV_9531(sc->sc_ah))
425 rfilt |= ATH9K_RX_FILTER_4ADDRESS; 426 rfilt |= ATH9K_RX_FILTER_4ADDRESS;
426 427
428 if (ath9k_use_chanctx &&
429 test_bit(ATH_OP_SCANNING, &common->op_flags))
430 rfilt |= ATH9K_RX_FILTER_BEACON;
431
427 return rfilt; 432 return rfilt;
428 433
429} 434}