diff options
author | Sujith <Sujith.Manoharan@atheros.com> | 2009-01-05 22:58:37 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-01-29 15:59:50 -0500 |
commit | c481ec9705d4a5d566393bc17374cfd82c870715 (patch) | |
tree | 383b90aa8cf172ee81a7e91c49440cf75c8c0278 /net | |
parent | b522ed56ef90f5078a2a1253e390299723510a89 (diff) |
mac80211: Add 802.11h CSA support
Move to the advertised channel on reception of
a CSA element. This is needed for 802.11h compliance.
Signed-off-by: Sujith <Sujith.Manoharan@atheros.com>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/ieee80211_i.h | 11 | ||||
-rw-r--r-- | net/mac80211/iface.c | 2 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 13 | ||||
-rw-r--r-- | net/mac80211/rx.c | 20 | ||||
-rw-r--r-- | net/mac80211/spectmgmt.c | 77 |
5 files changed, 122 insertions, 1 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 117718bd96ec..d2a007aa8e73 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -259,6 +259,7 @@ struct mesh_preq_queue { | |||
259 | #define IEEE80211_STA_AUTO_CHANNEL_SEL BIT(12) | 259 | #define IEEE80211_STA_AUTO_CHANNEL_SEL BIT(12) |
260 | #define IEEE80211_STA_PRIVACY_INVOKED BIT(13) | 260 | #define IEEE80211_STA_PRIVACY_INVOKED BIT(13) |
261 | #define IEEE80211_STA_TKIP_WEP_USED BIT(14) | 261 | #define IEEE80211_STA_TKIP_WEP_USED BIT(14) |
262 | #define IEEE80211_STA_CSA_RECEIVED BIT(15) | ||
262 | /* flags for MLME request */ | 263 | /* flags for MLME request */ |
263 | #define IEEE80211_STA_REQ_SCAN 0 | 264 | #define IEEE80211_STA_REQ_SCAN 0 |
264 | #define IEEE80211_STA_REQ_DIRECT_PROBE 1 | 265 | #define IEEE80211_STA_REQ_DIRECT_PROBE 1 |
@@ -283,7 +284,9 @@ enum ieee80211_sta_mlme_state { | |||
283 | 284 | ||
284 | struct ieee80211_if_sta { | 285 | struct ieee80211_if_sta { |
285 | struct timer_list timer; | 286 | struct timer_list timer; |
287 | struct timer_list chswitch_timer; | ||
286 | struct work_struct work; | 288 | struct work_struct work; |
289 | struct work_struct chswitch_work; | ||
287 | u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; | 290 | u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; |
288 | u8 ssid[IEEE80211_MAX_SSID_LEN]; | 291 | u8 ssid[IEEE80211_MAX_SSID_LEN]; |
289 | enum ieee80211_sta_mlme_state state; | 292 | enum ieee80211_sta_mlme_state state; |
@@ -542,6 +545,7 @@ enum { | |||
542 | enum queue_stop_reason { | 545 | enum queue_stop_reason { |
543 | IEEE80211_QUEUE_STOP_REASON_DRIVER, | 546 | IEEE80211_QUEUE_STOP_REASON_DRIVER, |
544 | IEEE80211_QUEUE_STOP_REASON_PS, | 547 | IEEE80211_QUEUE_STOP_REASON_PS, |
548 | IEEE80211_QUEUE_STOP_REASON_CSA | ||
545 | }; | 549 | }; |
546 | 550 | ||
547 | /* maximum number of hardware queues we support. */ | 551 | /* maximum number of hardware queues we support. */ |
@@ -631,7 +635,7 @@ struct ieee80211_local { | |||
631 | unsigned long last_scan_completed; | 635 | unsigned long last_scan_completed; |
632 | struct delayed_work scan_work; | 636 | struct delayed_work scan_work; |
633 | struct ieee80211_sub_if_data *scan_sdata; | 637 | struct ieee80211_sub_if_data *scan_sdata; |
634 | struct ieee80211_channel *oper_channel, *scan_channel; | 638 | struct ieee80211_channel *oper_channel, *scan_channel, *csa_channel; |
635 | enum nl80211_channel_type oper_channel_type; | 639 | enum nl80211_channel_type oper_channel_type; |
636 | u8 scan_ssid[IEEE80211_MAX_SSID_LEN]; | 640 | u8 scan_ssid[IEEE80211_MAX_SSID_LEN]; |
637 | size_t scan_ssid_len; | 641 | size_t scan_ssid_len; |
@@ -964,6 +968,11 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, | |||
964 | void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, | 968 | void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, |
965 | struct ieee80211_mgmt *mgmt, | 969 | struct ieee80211_mgmt *mgmt, |
966 | size_t len); | 970 | size_t len); |
971 | void ieee80211_chswitch_timer(unsigned long data); | ||
972 | void ieee80211_chswitch_work(struct work_struct *work); | ||
973 | void ieee80211_process_chanswitch(struct ieee80211_sub_if_data *sdata, | ||
974 | struct ieee80211_channel_sw_ie *sw_elem, | ||
975 | struct ieee80211_bss *bss); | ||
967 | 976 | ||
968 | /* utility functions/constants */ | 977 | /* utility functions/constants */ |
969 | extern void *mac80211_wiphy_privid; /* for wiphy privid */ | 978 | extern void *mac80211_wiphy_privid; /* for wiphy privid */ |
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 8e0e3303ca8c..5d5a029228be 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -443,6 +443,7 @@ static int ieee80211_stop(struct net_device *dev) | |||
443 | WLAN_REASON_DEAUTH_LEAVING); | 443 | WLAN_REASON_DEAUTH_LEAVING); |
444 | 444 | ||
445 | memset(sdata->u.sta.bssid, 0, ETH_ALEN); | 445 | memset(sdata->u.sta.bssid, 0, ETH_ALEN); |
446 | del_timer_sync(&sdata->u.sta.chswitch_timer); | ||
446 | del_timer_sync(&sdata->u.sta.timer); | 447 | del_timer_sync(&sdata->u.sta.timer); |
447 | /* | 448 | /* |
448 | * If the timer fired while we waited for it, it will have | 449 | * If the timer fired while we waited for it, it will have |
@@ -452,6 +453,7 @@ static int ieee80211_stop(struct net_device *dev) | |||
452 | * it no longer is. | 453 | * it no longer is. |
453 | */ | 454 | */ |
454 | cancel_work_sync(&sdata->u.sta.work); | 455 | cancel_work_sync(&sdata->u.sta.work); |
456 | cancel_work_sync(&sdata->u.sta.chswitch_work); | ||
455 | /* | 457 | /* |
456 | * When we get here, the interface is marked down. | 458 | * When we get here, the interface is marked down. |
457 | * Call synchronize_rcu() to wait for the RX path | 459 | * Call synchronize_rcu() to wait for the RX path |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 2db56605a2b6..cac4f65d9e61 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -1629,6 +1629,13 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, | |||
1629 | if (!bss) | 1629 | if (!bss) |
1630 | return; | 1630 | return; |
1631 | 1631 | ||
1632 | if (elems->ch_switch_elem && (elems->ch_switch_elem_len == 3) && | ||
1633 | (memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0)) { | ||
1634 | struct ieee80211_channel_sw_ie *sw_elem = | ||
1635 | (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem; | ||
1636 | ieee80211_process_chanswitch(sdata, sw_elem, bss); | ||
1637 | } | ||
1638 | |||
1632 | /* was just updated in ieee80211_bss_info_update */ | 1639 | /* was just updated in ieee80211_bss_info_update */ |
1633 | beacon_timestamp = bss->timestamp; | 1640 | beacon_timestamp = bss->timestamp; |
1634 | 1641 | ||
@@ -1765,6 +1772,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, | |||
1765 | memcmp(ifsta->bssid, mgmt->bssid, ETH_ALEN) != 0) | 1772 | memcmp(ifsta->bssid, mgmt->bssid, ETH_ALEN) != 0) |
1766 | return; | 1773 | return; |
1767 | 1774 | ||
1775 | if (rx_status->freq != local->hw.conf.channel->center_freq) | ||
1776 | return; | ||
1777 | |||
1768 | ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param, | 1778 | ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param, |
1769 | elems.wmm_param_len); | 1779 | elems.wmm_param_len); |
1770 | 1780 | ||
@@ -2425,8 +2435,11 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) | |||
2425 | 2435 | ||
2426 | ifsta = &sdata->u.sta; | 2436 | ifsta = &sdata->u.sta; |
2427 | INIT_WORK(&ifsta->work, ieee80211_sta_work); | 2437 | INIT_WORK(&ifsta->work, ieee80211_sta_work); |
2438 | INIT_WORK(&ifsta->chswitch_work, ieee80211_chswitch_work); | ||
2428 | setup_timer(&ifsta->timer, ieee80211_sta_timer, | 2439 | setup_timer(&ifsta->timer, ieee80211_sta_timer, |
2429 | (unsigned long) sdata); | 2440 | (unsigned long) sdata); |
2441 | setup_timer(&ifsta->chswitch_timer, ieee80211_chswitch_timer, | ||
2442 | (unsigned long) sdata); | ||
2430 | skb_queue_head_init(&ifsta->skb_queue); | 2443 | skb_queue_head_init(&ifsta->skb_queue); |
2431 | 2444 | ||
2432 | ifsta->capab = WLAN_CAPABILITY_ESS; | 2445 | ifsta->capab = WLAN_CAPABILITY_ESS; |
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 7175ae80c36a..ddb966f58882 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c | |||
@@ -1552,7 +1552,9 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) | |||
1552 | { | 1552 | { |
1553 | struct ieee80211_local *local = rx->local; | 1553 | struct ieee80211_local *local = rx->local; |
1554 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev); | 1554 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev); |
1555 | struct ieee80211_if_sta *ifsta = &sdata->u.sta; | ||
1555 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data; | 1556 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data; |
1557 | struct ieee80211_bss *bss; | ||
1556 | int len = rx->skb->len; | 1558 | int len = rx->skb->len; |
1557 | 1559 | ||
1558 | if (!ieee80211_is_action(mgmt->frame_control)) | 1560 | if (!ieee80211_is_action(mgmt->frame_control)) |
@@ -1601,6 +1603,24 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) | |||
1601 | return RX_DROP_MONITOR; | 1603 | return RX_DROP_MONITOR; |
1602 | ieee80211_process_measurement_req(sdata, mgmt, len); | 1604 | ieee80211_process_measurement_req(sdata, mgmt, len); |
1603 | break; | 1605 | break; |
1606 | case WLAN_ACTION_SPCT_CHL_SWITCH: | ||
1607 | if (len < (IEEE80211_MIN_ACTION_SIZE + | ||
1608 | sizeof(mgmt->u.action.u.chan_switch))) | ||
1609 | return RX_DROP_MONITOR; | ||
1610 | |||
1611 | if (memcmp(mgmt->bssid, ifsta->bssid, ETH_ALEN) != 0) | ||
1612 | return RX_DROP_MONITOR; | ||
1613 | |||
1614 | bss = ieee80211_rx_bss_get(local, ifsta->bssid, | ||
1615 | local->hw.conf.channel->center_freq, | ||
1616 | ifsta->ssid, ifsta->ssid_len); | ||
1617 | if (!bss) | ||
1618 | return RX_DROP_MONITOR; | ||
1619 | |||
1620 | ieee80211_process_chanswitch(sdata, | ||
1621 | &mgmt->u.action.u.chan_switch.sw_elem, bss); | ||
1622 | ieee80211_rx_bss_put(local, bss); | ||
1623 | break; | ||
1604 | } | 1624 | } |
1605 | break; | 1625 | break; |
1606 | default: | 1626 | default: |
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index f72bad636d8e..22ad4808e01a 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c | |||
@@ -84,3 +84,80 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, | |||
84 | mgmt->sa, mgmt->bssid, | 84 | mgmt->sa, mgmt->bssid, |
85 | mgmt->u.action.u.measurement.dialog_token); | 85 | mgmt->u.action.u.measurement.dialog_token); |
86 | } | 86 | } |
87 | |||
88 | void ieee80211_chswitch_work(struct work_struct *work) | ||
89 | { | ||
90 | struct ieee80211_sub_if_data *sdata = | ||
91 | container_of(work, struct ieee80211_sub_if_data, u.sta.chswitch_work); | ||
92 | struct ieee80211_bss *bss; | ||
93 | struct ieee80211_if_sta *ifsta = &sdata->u.sta; | ||
94 | |||
95 | if (!netif_running(sdata->dev)) | ||
96 | return; | ||
97 | |||
98 | bss = ieee80211_rx_bss_get(sdata->local, ifsta->bssid, | ||
99 | sdata->local->hw.conf.channel->center_freq, | ||
100 | ifsta->ssid, ifsta->ssid_len); | ||
101 | if (!bss) | ||
102 | goto exit; | ||
103 | |||
104 | sdata->local->oper_channel = sdata->local->csa_channel; | ||
105 | if (!ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL)) | ||
106 | bss->freq = sdata->local->oper_channel->center_freq; | ||
107 | |||
108 | ieee80211_rx_bss_put(sdata->local, bss); | ||
109 | exit: | ||
110 | ifsta->flags &= ~IEEE80211_STA_CSA_RECEIVED; | ||
111 | ieee80211_wake_queues_by_reason(&sdata->local->hw, | ||
112 | IEEE80211_QUEUE_STOP_REASON_CSA); | ||
113 | } | ||
114 | |||
115 | void ieee80211_chswitch_timer(unsigned long data) | ||
116 | { | ||
117 | struct ieee80211_sub_if_data *sdata = | ||
118 | (struct ieee80211_sub_if_data *) data; | ||
119 | struct ieee80211_if_sta *ifsta = &sdata->u.sta; | ||
120 | |||
121 | queue_work(sdata->local->hw.workqueue, &ifsta->chswitch_work); | ||
122 | } | ||
123 | |||
124 | void ieee80211_process_chanswitch(struct ieee80211_sub_if_data *sdata, | ||
125 | struct ieee80211_channel_sw_ie *sw_elem, | ||
126 | struct ieee80211_bss *bss) | ||
127 | { | ||
128 | struct ieee80211_channel *new_ch; | ||
129 | struct ieee80211_if_sta *ifsta = &sdata->u.sta; | ||
130 | int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num); | ||
131 | |||
132 | /* FIXME: Handle ADHOC later */ | ||
133 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | ||
134 | return; | ||
135 | |||
136 | if (ifsta->state != IEEE80211_STA_MLME_ASSOCIATED) | ||
137 | return; | ||
138 | |||
139 | if (sdata->local->sw_scanning || sdata->local->hw_scanning) | ||
140 | return; | ||
141 | |||
142 | /* Disregard subsequent beacons if we are already running a timer | ||
143 | processing a CSA */ | ||
144 | |||
145 | if (ifsta->flags & IEEE80211_STA_CSA_RECEIVED) | ||
146 | return; | ||
147 | |||
148 | new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); | ||
149 | if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) | ||
150 | return; | ||
151 | |||
152 | sdata->local->csa_channel = new_ch; | ||
153 | |||
154 | if (sw_elem->count <= 1) { | ||
155 | queue_work(sdata->local->hw.workqueue, &ifsta->chswitch_work); | ||
156 | } else { | ||
157 | ieee80211_stop_queues_by_reason(&sdata->local->hw, | ||
158 | IEEE80211_QUEUE_STOP_REASON_CSA); | ||
159 | ifsta->flags |= IEEE80211_STA_CSA_RECEIVED; | ||
160 | mod_timer(&ifsta->chswitch_timer, | ||
161 | jiffies + msecs_to_jiffies(sw_elem->count * bss->beacon_int)); | ||
162 | } | ||
163 | } | ||