diff options
author | Juuso Oikarinen <juuso.oikarinen@nokia.com> | 2010-03-19 01:14:53 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-03-23 16:51:42 -0400 |
commit | 1e4dcd012423b6a28f968a55886d2b27896a1586 (patch) | |
tree | c2eadaf6a33175840b2f5d8fb7dd6af1d5be76ed | |
parent | 43ead78ac48b75aaf47de96fcf10cbf5962f32a6 (diff) |
mac80211: Add support for connection monitor in hardware
This patch is based on a RFC patch by Kalle Valo.
The wl1271 has a feature which handles the connection monitor logic
in hardware, basically sending periodically nullfunc frames and reporting
to the host if AP is lost, after attempting to recover by sending
probe-requests to the AP.
Add support to mac80211 by adding a new flag IEEE80211_HW_CONNECTION_MONITOR
which prevents conn_mon_timer from triggering during idle periods, and
prevents sending probe-requests to the AP if beacon-loss is indicated by the
hardware.
Cc: Kalle Valo <kalle.valo@nokia.com>
Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
Reviewed-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | include/net/mac80211.h | 24 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 4 | ||||
-rw-r--r-- | net/mac80211/iface.c | 2 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 64 |
4 files changed, 82 insertions, 12 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 936bc410d061..d14226f29ffc 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h | |||
@@ -954,6 +954,11 @@ enum ieee80211_tkip_key_type { | |||
954 | * Hardware can provide ack status reports of Tx frames to | 954 | * Hardware can provide ack status reports of Tx frames to |
955 | * the stack. | 955 | * the stack. |
956 | * | 956 | * |
957 | * @IEEE80211_HW_CONNECTION_MONITOR: | ||
958 | * The hardware performs its own connection monitoring, including | ||
959 | * periodic keep-alives to the AP and probing the AP on beacon loss. | ||
960 | * When this flag is set, signaling beacon-loss will cause an immediate | ||
961 | * change to disassociated state. | ||
957 | */ | 962 | */ |
958 | enum ieee80211_hw_flags { | 963 | enum ieee80211_hw_flags { |
959 | IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, | 964 | IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, |
@@ -975,6 +980,7 @@ enum ieee80211_hw_flags { | |||
975 | IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16, | 980 | IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16, |
976 | IEEE80211_HW_SUPPORTS_UAPSD = 1<<17, | 981 | IEEE80211_HW_SUPPORTS_UAPSD = 1<<17, |
977 | IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18, | 982 | IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18, |
983 | IEEE80211_HW_CONNECTION_MONITOR = 1<<19, | ||
978 | }; | 984 | }; |
979 | 985 | ||
980 | /** | 986 | /** |
@@ -2364,12 +2370,26 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, | |||
2364 | * | 2370 | * |
2365 | * @vif: &struct ieee80211_vif pointer from the add_interface callback. | 2371 | * @vif: &struct ieee80211_vif pointer from the add_interface callback. |
2366 | * | 2372 | * |
2367 | * When beacon filtering is enabled with IEEE80211_HW_BEACON_FILTERING and | 2373 | * When beacon filtering is enabled with %IEEE80211_HW_BEACON_FILTERING and |
2368 | * IEEE80211_CONF_PS is set, the driver needs to inform whenever the | 2374 | * %IEEE80211_CONF_PS is set, the driver needs to inform whenever the |
2369 | * hardware is not receiving beacons with this function. | 2375 | * hardware is not receiving beacons with this function. |
2370 | */ | 2376 | */ |
2371 | void ieee80211_beacon_loss(struct ieee80211_vif *vif); | 2377 | void ieee80211_beacon_loss(struct ieee80211_vif *vif); |
2372 | 2378 | ||
2379 | /** | ||
2380 | * ieee80211_connection_loss - inform hardware has lost connection to the AP | ||
2381 | * | ||
2382 | * @vif: &struct ieee80211_vif pointer from the add_interface callback. | ||
2383 | * | ||
2384 | * When beacon filtering is enabled with %IEEE80211_HW_BEACON_FILTERING, and | ||
2385 | * %IEEE80211_CONF_PS and %IEEE80211_HW_CONNECTION_MONITOR are set, the driver | ||
2386 | * needs to inform if the connection to the AP has been lost. | ||
2387 | * | ||
2388 | * This function will cause immediate change to disassociated state, | ||
2389 | * without connection recovery attempts. | ||
2390 | */ | ||
2391 | void ieee80211_connection_loss(struct ieee80211_vif *vif); | ||
2392 | |||
2373 | /* Rate control API */ | 2393 | /* Rate control API */ |
2374 | 2394 | ||
2375 | /** | 2395 | /** |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index b84126491ab1..ab369e2a5282 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -327,7 +327,7 @@ struct ieee80211_if_managed { | |||
327 | struct work_struct work; | 327 | struct work_struct work; |
328 | struct work_struct monitor_work; | 328 | struct work_struct monitor_work; |
329 | struct work_struct chswitch_work; | 329 | struct work_struct chswitch_work; |
330 | struct work_struct beacon_loss_work; | 330 | struct work_struct beacon_connection_loss_work; |
331 | 331 | ||
332 | unsigned long probe_timeout; | 332 | unsigned long probe_timeout; |
333 | int probe_send_count; | 333 | int probe_send_count; |
@@ -1156,7 +1156,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, | |||
1156 | int powersave); | 1156 | int powersave); |
1157 | void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, | 1157 | void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, |
1158 | struct ieee80211_hdr *hdr); | 1158 | struct ieee80211_hdr *hdr); |
1159 | void ieee80211_beacon_loss_work(struct work_struct *work); | 1159 | void ieee80211_beacon_connection_loss_work(struct work_struct *work); |
1160 | 1160 | ||
1161 | void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, | 1161 | void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, |
1162 | enum queue_stop_reason reason); | 1162 | enum queue_stop_reason reason); |
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index d5571b9420cd..b4ec59a8dc03 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -486,7 +486,7 @@ static int ieee80211_stop(struct net_device *dev) | |||
486 | cancel_work_sync(&sdata->u.mgd.work); | 486 | cancel_work_sync(&sdata->u.mgd.work); |
487 | cancel_work_sync(&sdata->u.mgd.chswitch_work); | 487 | cancel_work_sync(&sdata->u.mgd.chswitch_work); |
488 | cancel_work_sync(&sdata->u.mgd.monitor_work); | 488 | cancel_work_sync(&sdata->u.mgd.monitor_work); |
489 | cancel_work_sync(&sdata->u.mgd.beacon_loss_work); | 489 | cancel_work_sync(&sdata->u.mgd.beacon_connection_loss_work); |
490 | 490 | ||
491 | /* | 491 | /* |
492 | * When we get here, the interface is marked down. | 492 | * When we get here, the interface is marked down. |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 0ab284c32135..865ea1cfb7bb 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -851,6 +851,9 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, | |||
851 | if (is_multicast_ether_addr(hdr->addr1)) | 851 | if (is_multicast_ether_addr(hdr->addr1)) |
852 | return; | 852 | return; |
853 | 853 | ||
854 | if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) | ||
855 | return; | ||
856 | |||
854 | mod_timer(&sdata->u.mgd.conn_mon_timer, | 857 | mod_timer(&sdata->u.mgd.conn_mon_timer, |
855 | round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME)); | 858 | round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME)); |
856 | } | 859 | } |
@@ -928,23 +931,68 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, | |||
928 | mutex_unlock(&ifmgd->mtx); | 931 | mutex_unlock(&ifmgd->mtx); |
929 | } | 932 | } |
930 | 933 | ||
931 | void ieee80211_beacon_loss_work(struct work_struct *work) | 934 | static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata) |
935 | { | ||
936 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | ||
937 | struct ieee80211_local *local = sdata->local; | ||
938 | u8 bssid[ETH_ALEN]; | ||
939 | |||
940 | mutex_lock(&ifmgd->mtx); | ||
941 | if (!ifmgd->associated) { | ||
942 | mutex_unlock(&ifmgd->mtx); | ||
943 | return; | ||
944 | } | ||
945 | |||
946 | memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN); | ||
947 | |||
948 | printk(KERN_DEBUG "Connection to AP %pM lost.\n", bssid); | ||
949 | |||
950 | ieee80211_set_disassoc(sdata); | ||
951 | ieee80211_recalc_idle(local); | ||
952 | mutex_unlock(&ifmgd->mtx); | ||
953 | /* | ||
954 | * must be outside lock due to cfg80211, | ||
955 | * but that's not a problem. | ||
956 | */ | ||
957 | ieee80211_send_deauth_disassoc(sdata, bssid, | ||
958 | IEEE80211_STYPE_DEAUTH, | ||
959 | WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, | ||
960 | NULL); | ||
961 | } | ||
962 | |||
963 | void ieee80211_beacon_connection_loss_work(struct work_struct *work) | ||
932 | { | 964 | { |
933 | struct ieee80211_sub_if_data *sdata = | 965 | struct ieee80211_sub_if_data *sdata = |
934 | container_of(work, struct ieee80211_sub_if_data, | 966 | container_of(work, struct ieee80211_sub_if_data, |
935 | u.mgd.beacon_loss_work); | 967 | u.mgd.beacon_connection_loss_work); |
936 | 968 | ||
937 | ieee80211_mgd_probe_ap(sdata, true); | 969 | if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) |
970 | __ieee80211_connection_loss(sdata); | ||
971 | else | ||
972 | ieee80211_mgd_probe_ap(sdata, true); | ||
938 | } | 973 | } |
939 | 974 | ||
940 | void ieee80211_beacon_loss(struct ieee80211_vif *vif) | 975 | void ieee80211_beacon_loss(struct ieee80211_vif *vif) |
941 | { | 976 | { |
942 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); | 977 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); |
978 | struct ieee80211_hw *hw = &sdata->local->hw; | ||
943 | 979 | ||
944 | ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work); | 980 | WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR); |
981 | ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work); | ||
945 | } | 982 | } |
946 | EXPORT_SYMBOL(ieee80211_beacon_loss); | 983 | EXPORT_SYMBOL(ieee80211_beacon_loss); |
947 | 984 | ||
985 | void ieee80211_connection_loss(struct ieee80211_vif *vif) | ||
986 | { | ||
987 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); | ||
988 | struct ieee80211_hw *hw = &sdata->local->hw; | ||
989 | |||
990 | WARN_ON(!(hw->flags & IEEE80211_HW_CONNECTION_MONITOR)); | ||
991 | ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work); | ||
992 | } | ||
993 | EXPORT_SYMBOL(ieee80211_connection_loss); | ||
994 | |||
995 | |||
948 | static enum rx_mgmt_action __must_check | 996 | static enum rx_mgmt_action __must_check |
949 | ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, | 997 | ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, |
950 | struct ieee80211_mgmt *mgmt, size_t len) | 998 | struct ieee80211_mgmt *mgmt, size_t len) |
@@ -1634,7 +1682,8 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data) | |||
1634 | if (local->quiescing) | 1682 | if (local->quiescing) |
1635 | return; | 1683 | return; |
1636 | 1684 | ||
1637 | ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work); | 1685 | ieee80211_queue_work(&sdata->local->hw, |
1686 | &sdata->u.mgd.beacon_connection_loss_work); | ||
1638 | } | 1687 | } |
1639 | 1688 | ||
1640 | static void ieee80211_sta_conn_mon_timer(unsigned long data) | 1689 | static void ieee80211_sta_conn_mon_timer(unsigned long data) |
@@ -1686,7 +1735,7 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata) | |||
1686 | */ | 1735 | */ |
1687 | 1736 | ||
1688 | cancel_work_sync(&ifmgd->work); | 1737 | cancel_work_sync(&ifmgd->work); |
1689 | cancel_work_sync(&ifmgd->beacon_loss_work); | 1738 | cancel_work_sync(&ifmgd->beacon_connection_loss_work); |
1690 | if (del_timer_sync(&ifmgd->timer)) | 1739 | if (del_timer_sync(&ifmgd->timer)) |
1691 | set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); | 1740 | set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); |
1692 | 1741 | ||
@@ -1720,7 +1769,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) | |||
1720 | INIT_WORK(&ifmgd->work, ieee80211_sta_work); | 1769 | INIT_WORK(&ifmgd->work, ieee80211_sta_work); |
1721 | INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work); | 1770 | INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work); |
1722 | INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work); | 1771 | INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work); |
1723 | INIT_WORK(&ifmgd->beacon_loss_work, ieee80211_beacon_loss_work); | 1772 | INIT_WORK(&ifmgd->beacon_connection_loss_work, |
1773 | ieee80211_beacon_connection_loss_work); | ||
1724 | setup_timer(&ifmgd->timer, ieee80211_sta_timer, | 1774 | setup_timer(&ifmgd->timer, ieee80211_sta_timer, |
1725 | (unsigned long) sdata); | 1775 | (unsigned long) sdata); |
1726 | setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, | 1776 | setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, |