aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorChun-Yeow Yeoh <yeohchunyeow@cozybit.com>2013-10-17 18:55:02 -0400
committerJohannes Berg <johannes.berg@intel.com>2013-10-28 10:05:30 -0400
commitb8456a14e9d2770846fcf74de18ff95b676149a3 (patch)
tree858ae69cd2d695116afc582377984070f645ff01 /net
parentc6da674aff9425dc41255bcb7f7586a656843f2d (diff)
{nl,cfg,mac}80211: implement mesh channel switch userspace API
Implement the required procedures for mesh channel switching as defined in the IEEE Std 802.11-2012 section 10.9.8.4.3 and also handle the CSA and MCSP elements as followed: * Add the function for updating the beacon and probe response frames with CSA and MCSP elements during the period of switching to the new channel. Both CSA and MCSP elements must be included in beacon and probe response frames until the intended channel switch time. * The ifmsh->csa_settings is set to NULL and the CSA and MCSP elements will then be removed from the beacon or probe response frames once the new channel is switched to. Signed-off-by: Chun-Yeow Yeoh <yeohchunyeow@cozybit.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/cfg.c9
-rw-r--r--net/mac80211/ieee80211_i.h11
-rw-r--r--net/mac80211/mesh.c98
-rw-r--r--net/mac80211/rx.c5
-rw-r--r--net/mac80211/tx.c16
-rw-r--r--net/wireless/nl80211.c3
6 files changed, 139 insertions, 3 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 8cdbd29cbc45..5b1ccb4e0271 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2994,6 +2994,13 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
2994 case NL80211_IFTYPE_ADHOC: 2994 case NL80211_IFTYPE_ADHOC:
2995 ieee80211_ibss_finish_csa(sdata); 2995 ieee80211_ibss_finish_csa(sdata);
2996 break; 2996 break;
2997#ifdef CONFIG_MAC80211_MESH
2998 case NL80211_IFTYPE_MESH_POINT:
2999 err = ieee80211_mesh_finish_csa(sdata);
3000 if (err < 0)
3001 return;
3002 break;
3003#endif
2997 default: 3004 default:
2998 WARN_ON(1); 3005 WARN_ON(1);
2999 return; 3006 return;
@@ -3113,7 +3120,7 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
3113 params->chandef.chan->band) 3120 params->chandef.chan->band)
3114 return -EINVAL; 3121 return -EINVAL;
3115 3122
3116 err = ieee80211_send_action_csa(sdata, params); 3123 err = ieee80211_mesh_csa_beacon(sdata, params, true);
3117 if (err < 0) 3124 if (err < 0)
3118 return err; 3125 return err;
3119 break; 3126 break;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 9aad167e2ebc..5cfa160be05b 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -543,6 +543,11 @@ struct ieee80211_mesh_sync_ops {
543 /* add other framework functions here */ 543 /* add other framework functions here */
544}; 544};
545 545
546struct mesh_csa_settings {
547 struct rcu_head rcu_head;
548 struct cfg80211_csa_settings settings;
549};
550
546struct ieee80211_if_mesh { 551struct ieee80211_if_mesh {
547 struct timer_list housekeeping_timer; 552 struct timer_list housekeeping_timer;
548 struct timer_list mesh_path_timer; 553 struct timer_list mesh_path_timer;
@@ -604,7 +609,9 @@ struct ieee80211_if_mesh {
604 int ps_peers_deep_sleep; 609 int ps_peers_deep_sleep;
605 struct ps_data ps; 610 struct ps_data ps;
606 /* Channel Switching Support */ 611 /* Channel Switching Support */
612 struct mesh_csa_settings __rcu *csa;
607 bool chsw_init; 613 bool chsw_init;
614 u8 chsw_ttl;
608 u16 pre_value; 615 u16 pre_value;
609}; 616};
610 617
@@ -1356,6 +1363,10 @@ void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);
1356void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata); 1363void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
1357void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, 1364void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
1358 struct sk_buff *skb); 1365 struct sk_buff *skb);
1366int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
1367 struct cfg80211_csa_settings *csa_settings,
1368 bool csa_action);
1369int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata);
1359 1370
1360/* scan/BSS handling */ 1371/* scan/BSS handling */
1361void ieee80211_scan_work(struct work_struct *work); 1372void ieee80211_scan_work(struct work_struct *work);
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 0a3ccaa275f9..6eb31d6fd8e1 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -12,6 +12,7 @@
12#include <asm/unaligned.h> 12#include <asm/unaligned.h>
13#include "ieee80211_i.h" 13#include "ieee80211_i.h"
14#include "mesh.h" 14#include "mesh.h"
15#include "driver-ops.h"
15 16
16static int mesh_allocated; 17static int mesh_allocated;
17static struct kmem_cache *rm_cache; 18static struct kmem_cache *rm_cache;
@@ -610,6 +611,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
610 struct sk_buff *skb; 611 struct sk_buff *skb;
611 struct ieee80211_mgmt *mgmt; 612 struct ieee80211_mgmt *mgmt;
612 struct ieee80211_chanctx_conf *chanctx_conf; 613 struct ieee80211_chanctx_conf *chanctx_conf;
614 struct mesh_csa_settings *csa;
613 enum ieee80211_band band; 615 enum ieee80211_band band;
614 u8 *pos; 616 u8 *pos;
615 struct ieee80211_sub_if_data *sdata; 617 struct ieee80211_sub_if_data *sdata;
@@ -624,6 +626,10 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
624 626
625 head_len = hdr_len + 627 head_len = hdr_len +
626 2 + /* NULL SSID */ 628 2 + /* NULL SSID */
629 /* Channel Switch Announcement */
630 2 + sizeof(struct ieee80211_channel_sw_ie) +
631 /* Mesh Channel Swith Parameters */
632 2 + sizeof(struct ieee80211_mesh_chansw_params_ie) +
627 2 + 8 + /* supported rates */ 633 2 + 8 + /* supported rates */
628 2 + 3; /* DS params */ 634 2 + 3; /* DS params */
629 tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) + 635 tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) +
@@ -665,6 +671,38 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
665 *pos++ = WLAN_EID_SSID; 671 *pos++ = WLAN_EID_SSID;
666 *pos++ = 0x0; 672 *pos++ = 0x0;
667 673
674 rcu_read_lock();
675 csa = rcu_dereference(ifmsh->csa);
676 if (csa) {
677 __le16 pre_value;
678
679 pos = skb_put(skb, 13);
680 memset(pos, 0, 13);
681 *pos++ = WLAN_EID_CHANNEL_SWITCH;
682 *pos++ = 3;
683 *pos++ = 0x0;
684 *pos++ = ieee80211_frequency_to_channel(
685 csa->settings.chandef.chan->center_freq);
686 sdata->csa_counter_offset_beacon = hdr_len + 6;
687 *pos++ = csa->settings.count;
688 *pos++ = WLAN_EID_CHAN_SWITCH_PARAM;
689 *pos++ = 6;
690 if (ifmsh->chsw_init) {
691 *pos++ = ifmsh->mshcfg.dot11MeshTTL;
692 *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
693 } else {
694 *pos++ = ifmsh->chsw_ttl;
695 }
696 *pos++ |= csa->settings.block_tx ?
697 WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
698 put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos);
699 pos += 2;
700 pre_value = cpu_to_le16(ifmsh->pre_value);
701 memcpy(pos, &pre_value, 2);
702 pos += 2;
703 }
704 rcu_read_unlock();
705
668 if (ieee80211_add_srates_ie(sdata, skb, true, band) || 706 if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
669 mesh_add_ds_params_ie(sdata, skb)) 707 mesh_add_ds_params_ie(sdata, skb))
670 goto out_free; 708 goto out_free;
@@ -920,6 +958,65 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
920 stype, mgmt, &elems, rx_status); 958 stype, mgmt, &elems, rx_status);
921} 959}
922 960
961int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata)
962{
963 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
964 struct mesh_csa_settings *tmp_csa_settings;
965 int ret = 0;
966
967 /* Reset the TTL value and Initiator flag */
968 ifmsh->chsw_init = false;
969 ifmsh->chsw_ttl = 0;
970
971 /* Remove the CSA and MCSP elements from the beacon */
972 tmp_csa_settings = rcu_dereference(ifmsh->csa);
973 rcu_assign_pointer(ifmsh->csa, NULL);
974 kfree_rcu(tmp_csa_settings, rcu_head);
975 ret = ieee80211_mesh_rebuild_beacon(sdata);
976 if (ret)
977 return -EINVAL;
978
979 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
980
981 mcsa_dbg(sdata, "complete switching to center freq %d MHz",
982 sdata->vif.bss_conf.chandef.chan->center_freq);
983 return 0;
984}
985
986int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
987 struct cfg80211_csa_settings *csa_settings,
988 bool csa_action)
989{
990 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
991 struct mesh_csa_settings *tmp_csa_settings;
992 int ret = 0;
993
994 tmp_csa_settings = kmalloc(sizeof(*tmp_csa_settings),
995 GFP_ATOMIC);
996 if (!tmp_csa_settings)
997 return -ENOMEM;
998
999 memcpy(&tmp_csa_settings->settings, csa_settings,
1000 sizeof(struct cfg80211_csa_settings));
1001
1002 rcu_assign_pointer(ifmsh->csa, tmp_csa_settings);
1003
1004 ret = ieee80211_mesh_rebuild_beacon(sdata);
1005 if (ret) {
1006 tmp_csa_settings = rcu_dereference(ifmsh->csa);
1007 rcu_assign_pointer(ifmsh->csa, NULL);
1008 kfree_rcu(tmp_csa_settings, rcu_head);
1009 return ret;
1010 }
1011
1012 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
1013
1014 if (csa_action)
1015 ieee80211_send_action_csa(sdata, csa_settings);
1016
1017 return 0;
1018}
1019
923static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata, 1020static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata,
924 struct ieee80211_mgmt *mgmt, size_t len) 1021 struct ieee80211_mgmt *mgmt, size_t len)
925{ 1022{
@@ -942,6 +1039,7 @@ static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata,
942 offset_ttl = (len < 42) ? 7 : 10; 1039 offset_ttl = (len < 42) ? 7 : 10;
943 *(pos + offset_ttl) -= 1; 1040 *(pos + offset_ttl) -= 1;
944 *(pos + offset_ttl + 1) &= ~WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; 1041 *(pos + offset_ttl + 1) &= ~WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
1042 sdata->u.mesh.chsw_ttl = *(pos + offset_ttl);
945 1043
946 memcpy(mgmt_fwd, mgmt, len); 1044 memcpy(mgmt_fwd, mgmt, len);
947 eth_broadcast_addr(mgmt_fwd->da); 1045 eth_broadcast_addr(mgmt_fwd->da);
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index f0247a43a75c..23f49e8d14c1 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2593,13 +2593,16 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
2593 break; 2593 break;
2594 2594
2595 if (sdata->vif.type != NL80211_IFTYPE_STATION && 2595 if (sdata->vif.type != NL80211_IFTYPE_STATION &&
2596 sdata->vif.type != NL80211_IFTYPE_ADHOC) 2596 sdata->vif.type != NL80211_IFTYPE_ADHOC &&
2597 sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
2597 break; 2598 break;
2598 2599
2599 if (sdata->vif.type == NL80211_IFTYPE_STATION) 2600 if (sdata->vif.type == NL80211_IFTYPE_STATION)
2600 bssid = sdata->u.mgd.bssid; 2601 bssid = sdata->u.mgd.bssid;
2601 else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) 2602 else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
2602 bssid = sdata->u.ibss.bssid; 2603 bssid = sdata->u.ibss.bssid;
2604 else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
2605 bssid = mgmt->sa;
2603 else 2606 else
2604 break; 2607 break;
2605 2608
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index acd9b61fbc07..9868cb72054e 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2398,6 +2398,10 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
2398 beacon_data = beacon->head; 2398 beacon_data = beacon->head;
2399 beacon_data_len = beacon->head_len; 2399 beacon_data_len = beacon->head_len;
2400 break; 2400 break;
2401 case NL80211_IFTYPE_MESH_POINT:
2402 beacon_data = beacon->head;
2403 beacon_data_len = beacon->head_len;
2404 break;
2401 default: 2405 default:
2402 return; 2406 return;
2403 } 2407 }
@@ -2454,6 +2458,15 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
2454 2458
2455 beacon_data = beacon->head; 2459 beacon_data = beacon->head;
2456 beacon_data_len = beacon->head_len; 2460 beacon_data_len = beacon->head_len;
2461 } else if (vif->type == NL80211_IFTYPE_MESH_POINT) {
2462 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
2463
2464 beacon = rcu_dereference(ifmsh->beacon);
2465 if (!beacon)
2466 goto out;
2467
2468 beacon_data = beacon->head;
2469 beacon_data_len = beacon->head_len;
2457 } else { 2470 } else {
2458 WARN_ON(1); 2471 WARN_ON(1);
2459 goto out; 2472 goto out;
@@ -2559,6 +2572,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
2559 if (!bcn) 2572 if (!bcn)
2560 goto out; 2573 goto out;
2561 2574
2575 if (sdata->vif.csa_active)
2576 ieee80211_update_csa(sdata, bcn);
2577
2562 if (ifmsh->sync_ops) 2578 if (ifmsh->sync_ops)
2563 ifmsh->sync_ops->adjust_tbtt( 2579 ifmsh->sync_ops->adjust_tbtt(
2564 sdata); 2580 sdata);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index b8d6f101378a..c49f0af61d5e 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -10813,7 +10813,8 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
10813 10813
10814 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && 10814 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
10815 wdev->iftype != NL80211_IFTYPE_P2P_GO && 10815 wdev->iftype != NL80211_IFTYPE_P2P_GO &&
10816 wdev->iftype != NL80211_IFTYPE_ADHOC)) 10816 wdev->iftype != NL80211_IFTYPE_ADHOC &&
10817 wdev->iftype != NL80211_IFTYPE_MESH_POINT))
10817 goto out; 10818 goto out;
10818 10819
10819 wdev->channel = chandef->chan; 10820 wdev->channel = chandef->chan;