aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorHelmut Schaa <helmut.schaa@googlemail.com>2009-07-23 07:18:01 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-07-27 15:24:17 -0400
commit142b9f5074dc0d09dc0025739ad437723d7bf527 (patch)
tree07173ed0ffae8956c1f8938bc41695b1d19cebb0 /net/mac80211
parentfbe9c429f195111bbf7f1630efa19aee295fd8e7 (diff)
mac80211: implement basic background scanning
Introduce a new scan flag "SCAN_OFF_CHANNEL" which basically tells us that we are currently on a different channel for scanning and cannot RX/TX. "SCAN_SW_SCANNING" tells us that we are currently running a software scan but we might as well be on the operating channel to RX/TX. While "SCAN_SW_SCANNING" is set during the whole scan "SCAN_OFF_CHANNEL" is set when leaving the operating channel and unset when coming back. Introduce two new scan states "SCAN_LEAVE_OPER_CHANNEL" and "SCAN_ENTER_OPER_CHANNEL" which basically implement the functionality we need to leave the operating channel (send a nullfunc to the AP and stop the queues) and enter it again (send a nullfunc to the AP and start the queues again). Enhance the scan state "SCAN_DECISION" to switch back to the operating channel after each scanned channel. In the future it sould be simple to enhance the decision state to scan as much channels in a row as the qos latency allows us. Signed-off-by: Helmut Schaa <helmut.schaa@googlemail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/ieee80211_i.h36
-rw-r--r--net/mac80211/rx.c6
-rw-r--r--net/mac80211/scan.c117
-rw-r--r--net/mac80211/tx.c2
4 files changed, 148 insertions, 13 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 783a125402b0..efda19ee0152 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -570,9 +570,41 @@ enum queue_stop_reason {
570 IEEE80211_QUEUE_STOP_REASON_SKB_ADD, 570 IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
571}; 571};
572 572
573/**
574 * mac80211 scan flags - currently active scan mode
575 *
576 * @SCAN_SW_SCANNING: We're currently in the process of scanning but may as
577 * well be on the operating channel
578 * @SCAN_HW_SCANNING: The hardware is scanning for us, we have no way to
579 * determine if we are on the operating channel or not
580 * @SCAN_OFF_CHANNEL: We're off our operating channel for scanning,
581 * gets only set in conjunction with SCAN_SW_SCANNING
582 */
573enum { 583enum {
574 SCAN_SW_SCANNING, 584 SCAN_SW_SCANNING,
575 SCAN_HW_SCANNING 585 SCAN_HW_SCANNING,
586 SCAN_OFF_CHANNEL,
587};
588
589/**
590 * enum mac80211_scan_state - scan state machine states
591 *
592 * @SCAN_DECISION: Main entry point to the scan state machine, this state
593 * determines if we should keep on scanning or switch back to the
594 * operating channel
595 * @SCAN_SET_CHANNEL: Set the next channel to be scanned
596 * @SCAN_SEND_PROBE: Send probe requests and wait for probe responses
597 * @SCAN_LEAVE_OPER_CHANNEL: Leave the operating channel, notify the AP
598 * about us leaving the channel and stop all associated STA interfaces
599 * @SCAN_ENTER_OPER_CHANNEL: Enter the operating channel again, notify the
600 * AP about us being back and restart all associated STA interfaces
601 */
602enum mac80211_scan_state {
603 SCAN_DECISION,
604 SCAN_SET_CHANNEL,
605 SCAN_SEND_PROBE,
606 SCAN_LEAVE_OPER_CHANNEL,
607 SCAN_ENTER_OPER_CHANNEL,
576}; 608};
577 609
578struct ieee80211_local { 610struct ieee80211_local {
@@ -683,7 +715,7 @@ struct ieee80211_local {
683 int scan_channel_idx; 715 int scan_channel_idx;
684 int scan_ies_len; 716 int scan_ies_len;
685 717
686 enum { SCAN_DECISION, SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state; 718 enum mac80211_scan_state scan_state;
687 struct delayed_work scan_work; 719 struct delayed_work scan_work;
688 struct ieee80211_sub_if_data *scan_sdata; 720 struct ieee80211_sub_if_data *scan_sdata;
689 enum nl80211_channel_type oper_channel_type; 721 enum nl80211_channel_type oper_channel_type;
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 9c1679d124ba..cb95a3116034 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -421,7 +421,8 @@ ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx)
421 if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning))) 421 if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning)))
422 return ieee80211_scan_rx(rx->sdata, skb); 422 return ieee80211_scan_rx(rx->sdata, skb);
423 423
424 if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning))) { 424 if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning) &&
425 (rx->flags & IEEE80211_RX_IN_SCAN))) {
425 /* drop all the other packets during a software scan anyway */ 426 /* drop all the other packets during a software scan anyway */
426 if (ieee80211_scan_rx(rx->sdata, skb) != RX_QUEUED) 427 if (ieee80211_scan_rx(rx->sdata, skb) != RX_QUEUED)
427 dev_kfree_skb(skb); 428 dev_kfree_skb(skb);
@@ -2136,7 +2137,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
2136 return; 2137 return;
2137 } 2138 }
2138 2139
2139 if (unlikely(local->scanning)) 2140 if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning) ||
2141 test_bit(SCAN_OFF_CHANNEL, &local->scanning)))
2140 rx.flags |= IEEE80211_RX_IN_SCAN; 2142 rx.flags |= IEEE80211_RX_IN_SCAN;
2141 2143
2142 ieee80211_parse_qos(&rx); 2144 ieee80211_parse_qos(&rx);
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 4233c3d700ce..d56b9da8b28a 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -365,12 +365,11 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
365 ieee80211_bss_info_change_notify( 365 ieee80211_bss_info_change_notify(
366 sdata, BSS_CHANGED_BEACON_ENABLED); 366 sdata, BSS_CHANGED_BEACON_ENABLED);
367 367
368 if (sdata->vif.type == NL80211_IFTYPE_STATION) { 368 /*
369 if (sdata->u.mgd.associated) { 369 * only handle non-STA interfaces here, STA interfaces
370 netif_tx_stop_all_queues(sdata->dev); 370 * are handled in the scan state machine
371 ieee80211_scan_ps_enable(sdata); 371 */
372 } 372 if (sdata->vif.type != NL80211_IFTYPE_STATION)
373 } else
374 netif_tx_stop_all_queues(sdata->dev); 373 netif_tx_stop_all_queues(sdata->dev);
375 } 374 }
376 mutex_unlock(&local->iflist_mtx); 375 mutex_unlock(&local->iflist_mtx);
@@ -474,17 +473,113 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
474static int ieee80211_scan_state_decision(struct ieee80211_local *local, 473static int ieee80211_scan_state_decision(struct ieee80211_local *local,
475 unsigned long *next_delay) 474 unsigned long *next_delay)
476{ 475{
477 /* if no more bands/channels left, complete scan */ 476 bool associated = false;
477 struct ieee80211_sub_if_data *sdata;
478
479 /* if no more bands/channels left, complete scan and advance to the idle state */
478 if (local->scan_channel_idx >= local->scan_req->n_channels) { 480 if (local->scan_channel_idx >= local->scan_req->n_channels) {
479 ieee80211_scan_completed(&local->hw, false); 481 ieee80211_scan_completed(&local->hw, false);
480 return 1; 482 return 1;
481 } 483 }
482 484
485 /* check if at least one STA interface is associated */
486 mutex_lock(&local->iflist_mtx);
487 list_for_each_entry(sdata, &local->interfaces, list) {
488 if (!netif_running(sdata->dev))
489 continue;
490
491 if (sdata->vif.type == NL80211_IFTYPE_STATION) {
492 if (sdata->u.mgd.associated) {
493 associated = true;
494 break;
495 }
496 }
497 }
498 mutex_unlock(&local->iflist_mtx);
499
500 if (local->scan_channel) {
501 /*
502 * we're currently scanning a different channel, let's
503 * switch back to the operating channel now if at least
504 * one interface is associated. Otherwise just scan the
505 * next channel
506 */
507 if (associated)
508 local->scan_state = SCAN_ENTER_OPER_CHANNEL;
509 else
510 local->scan_state = SCAN_SET_CHANNEL;
511 } else {
512 /*
513 * we're on the operating channel currently, let's
514 * leave that channel now to scan another one
515 */
516 local->scan_state = SCAN_LEAVE_OPER_CHANNEL;
517 }
518
483 *next_delay = 0; 519 *next_delay = 0;
484 local->scan_state = SCAN_SET_CHANNEL;
485 return 0; 520 return 0;
486} 521}
487 522
523static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local,
524 unsigned long *next_delay)
525{
526 struct ieee80211_sub_if_data *sdata;
527
528 /*
529 * notify the AP about us leaving the channel and stop all STA interfaces
530 */
531 mutex_lock(&local->iflist_mtx);
532 list_for_each_entry(sdata, &local->interfaces, list) {
533 if (!netif_running(sdata->dev))
534 continue;
535
536 if (sdata->vif.type == NL80211_IFTYPE_STATION) {
537 netif_tx_stop_all_queues(sdata->dev);
538 if (sdata->u.mgd.associated)
539 ieee80211_scan_ps_enable(sdata);
540 }
541 }
542 mutex_unlock(&local->iflist_mtx);
543
544 __set_bit(SCAN_OFF_CHANNEL, &local->scanning);
545
546 /* advance to the next channel to be scanned */
547 *next_delay = HZ / 10;
548 local->scan_state = SCAN_SET_CHANNEL;
549}
550
551static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *local,
552 unsigned long *next_delay)
553{
554 struct ieee80211_sub_if_data *sdata = local->scan_sdata;
555
556 /* switch back to the operating channel */
557 local->scan_channel = NULL;
558 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
559
560 /*
561 * notify the AP about us being back and restart all STA interfaces
562 */
563 mutex_lock(&local->iflist_mtx);
564 list_for_each_entry(sdata, &local->interfaces, list) {
565 if (!netif_running(sdata->dev))
566 continue;
567
568 /* Tell AP we're back */
569 if (sdata->vif.type == NL80211_IFTYPE_STATION) {
570 if (sdata->u.mgd.associated)
571 ieee80211_scan_ps_disable(sdata);
572 netif_tx_wake_all_queues(sdata->dev);
573 }
574 }
575 mutex_unlock(&local->iflist_mtx);
576
577 __clear_bit(SCAN_OFF_CHANNEL, &local->scanning);
578
579 *next_delay = HZ / 5;
580 local->scan_state = SCAN_DECISION;
581}
582
488static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, 583static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
489 unsigned long *next_delay) 584 unsigned long *next_delay)
490{ 585{
@@ -609,6 +704,12 @@ void ieee80211_scan_work(struct work_struct *work)
609 case SCAN_SEND_PROBE: 704 case SCAN_SEND_PROBE:
610 ieee80211_scan_state_send_probe(local, &next_delay); 705 ieee80211_scan_state_send_probe(local, &next_delay);
611 break; 706 break;
707 case SCAN_LEAVE_OPER_CHANNEL:
708 ieee80211_scan_state_leave_oper_channel(local, &next_delay);
709 break;
710 case SCAN_ENTER_OPER_CHANNEL:
711 ieee80211_scan_state_enter_oper_channel(local, &next_delay);
712 break;
612 } 713 }
613 } while (next_delay == 0); 714 } while (next_delay == 0);
614 715
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index d7491dc2a65c..70ff4f065665 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -192,7 +192,7 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
192 if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED)) 192 if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED))
193 return TX_CONTINUE; 193 return TX_CONTINUE;
194 194
195 if (unlikely(test_bit(SCAN_SW_SCANNING, &tx->local->scanning)) && 195 if (unlikely(test_bit(SCAN_OFF_CHANNEL, &tx->local->scanning)) &&
196 !ieee80211_is_probe_req(hdr->frame_control) && 196 !ieee80211_is_probe_req(hdr->frame_control) &&
197 !ieee80211_is_nullfunc(hdr->frame_control)) 197 !ieee80211_is_nullfunc(hdr->frame_control))
198 /* 198 /*