diff options
author | Helmut Schaa <helmut.schaa@googlemail.com> | 2009-07-23 07:18:01 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-07-27 15:24:17 -0400 |
commit | 142b9f5074dc0d09dc0025739ad437723d7bf527 (patch) | |
tree | 07173ed0ffae8956c1f8938bc41695b1d19cebb0 | |
parent | fbe9c429f195111bbf7f1630efa19aee295fd8e7 (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>
-rw-r--r-- | net/mac80211/ieee80211_i.h | 36 | ||||
-rw-r--r-- | net/mac80211/rx.c | 6 | ||||
-rw-r--r-- | net/mac80211/scan.c | 117 | ||||
-rw-r--r-- | net/mac80211/tx.c | 2 |
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 | */ | ||
573 | enum { | 583 | enum { |
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 | */ | ||
602 | enum 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 | ||
578 | struct ieee80211_local { | 610 | struct 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, | |||
474 | static int ieee80211_scan_state_decision(struct ieee80211_local *local, | 473 | static 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 | ||
523 | static 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 | |||
551 | static 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 | |||
488 | static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, | 583 | static 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 | /* |