Commit b8456a14 authored by Chun-Yeow Yeoh's avatar Chun-Yeow Yeoh Committed by Johannes Berg

{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: default avatarChun-Yeow Yeoh <yeohchunyeow@cozybit.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent c6da674a
...@@ -2994,6 +2994,13 @@ void ieee80211_csa_finalize_work(struct work_struct *work) ...@@ -2994,6 +2994,13 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
ieee80211_ibss_finish_csa(sdata); ieee80211_ibss_finish_csa(sdata);
break; break;
#ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT:
err = ieee80211_mesh_finish_csa(sdata);
if (err < 0)
return;
break;
#endif
default: default:
WARN_ON(1); WARN_ON(1);
return; return;
...@@ -3113,7 +3120,7 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, ...@@ -3113,7 +3120,7 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
params->chandef.chan->band) params->chandef.chan->band)
return -EINVAL; return -EINVAL;
err = ieee80211_send_action_csa(sdata, params); err = ieee80211_mesh_csa_beacon(sdata, params, true);
if (err < 0) if (err < 0)
return err; return err;
break; break;
......
...@@ -543,6 +543,11 @@ struct ieee80211_mesh_sync_ops { ...@@ -543,6 +543,11 @@ struct ieee80211_mesh_sync_ops {
/* add other framework functions here */ /* add other framework functions here */
}; };
struct mesh_csa_settings {
struct rcu_head rcu_head;
struct cfg80211_csa_settings settings;
};
struct ieee80211_if_mesh { struct ieee80211_if_mesh {
struct timer_list housekeeping_timer; struct timer_list housekeeping_timer;
struct timer_list mesh_path_timer; struct timer_list mesh_path_timer;
...@@ -604,7 +609,9 @@ struct ieee80211_if_mesh { ...@@ -604,7 +609,9 @@ struct ieee80211_if_mesh {
int ps_peers_deep_sleep; int ps_peers_deep_sleep;
struct ps_data ps; struct ps_data ps;
/* Channel Switching Support */ /* Channel Switching Support */
struct mesh_csa_settings __rcu *csa;
bool chsw_init; bool chsw_init;
u8 chsw_ttl;
u16 pre_value; u16 pre_value;
}; };
...@@ -1356,6 +1363,10 @@ void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata); ...@@ -1356,6 +1363,10 @@ void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);
void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata); void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb); struct sk_buff *skb);
int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
struct cfg80211_csa_settings *csa_settings,
bool csa_action);
int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata);
/* scan/BSS handling */ /* scan/BSS handling */
void ieee80211_scan_work(struct work_struct *work); void ieee80211_scan_work(struct work_struct *work);
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include "ieee80211_i.h" #include "ieee80211_i.h"
#include "mesh.h" #include "mesh.h"
#include "driver-ops.h"
static int mesh_allocated; static int mesh_allocated;
static struct kmem_cache *rm_cache; static struct kmem_cache *rm_cache;
...@@ -610,6 +611,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) ...@@ -610,6 +611,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
struct sk_buff *skb; struct sk_buff *skb;
struct ieee80211_mgmt *mgmt; struct ieee80211_mgmt *mgmt;
struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_chanctx_conf *chanctx_conf;
struct mesh_csa_settings *csa;
enum ieee80211_band band; enum ieee80211_band band;
u8 *pos; u8 *pos;
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
...@@ -624,6 +626,10 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) ...@@ -624,6 +626,10 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
head_len = hdr_len + head_len = hdr_len +
2 + /* NULL SSID */ 2 + /* NULL SSID */
/* Channel Switch Announcement */
2 + sizeof(struct ieee80211_channel_sw_ie) +
/* Mesh Channel Swith Parameters */
2 + sizeof(struct ieee80211_mesh_chansw_params_ie) +
2 + 8 + /* supported rates */ 2 + 8 + /* supported rates */
2 + 3; /* DS params */ 2 + 3; /* DS params */
tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) + tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) +
...@@ -665,6 +671,38 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) ...@@ -665,6 +671,38 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
*pos++ = WLAN_EID_SSID; *pos++ = WLAN_EID_SSID;
*pos++ = 0x0; *pos++ = 0x0;
rcu_read_lock();
csa = rcu_dereference(ifmsh->csa);
if (csa) {
__le16 pre_value;
pos = skb_put(skb, 13);
memset(pos, 0, 13);
*pos++ = WLAN_EID_CHANNEL_SWITCH;
*pos++ = 3;
*pos++ = 0x0;
*pos++ = ieee80211_frequency_to_channel(
csa->settings.chandef.chan->center_freq);
sdata->csa_counter_offset_beacon = hdr_len + 6;
*pos++ = csa->settings.count;
*pos++ = WLAN_EID_CHAN_SWITCH_PARAM;
*pos++ = 6;
if (ifmsh->chsw_init) {
*pos++ = ifmsh->mshcfg.dot11MeshTTL;
*pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
} else {
*pos++ = ifmsh->chsw_ttl;
}
*pos++ |= csa->settings.block_tx ?
WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos);
pos += 2;
pre_value = cpu_to_le16(ifmsh->pre_value);
memcpy(pos, &pre_value, 2);
pos += 2;
}
rcu_read_unlock();
if (ieee80211_add_srates_ie(sdata, skb, true, band) || if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
mesh_add_ds_params_ie(sdata, skb)) mesh_add_ds_params_ie(sdata, skb))
goto out_free; goto out_free;
...@@ -920,6 +958,65 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, ...@@ -920,6 +958,65 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
stype, mgmt, &elems, rx_status); stype, mgmt, &elems, rx_status);
} }
int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct mesh_csa_settings *tmp_csa_settings;
int ret = 0;
/* Reset the TTL value and Initiator flag */
ifmsh->chsw_init = false;
ifmsh->chsw_ttl = 0;
/* Remove the CSA and MCSP elements from the beacon */
tmp_csa_settings = rcu_dereference(ifmsh->csa);
rcu_assign_pointer(ifmsh->csa, NULL);
kfree_rcu(tmp_csa_settings, rcu_head);
ret = ieee80211_mesh_rebuild_beacon(sdata);
if (ret)
return -EINVAL;
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
mcsa_dbg(sdata, "complete switching to center freq %d MHz",
sdata->vif.bss_conf.chandef.chan->center_freq);
return 0;
}
int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
struct cfg80211_csa_settings *csa_settings,
bool csa_action)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct mesh_csa_settings *tmp_csa_settings;
int ret = 0;
tmp_csa_settings = kmalloc(sizeof(*tmp_csa_settings),
GFP_ATOMIC);
if (!tmp_csa_settings)
return -ENOMEM;
memcpy(&tmp_csa_settings->settings, csa_settings,
sizeof(struct cfg80211_csa_settings));
rcu_assign_pointer(ifmsh->csa, tmp_csa_settings);
ret = ieee80211_mesh_rebuild_beacon(sdata);
if (ret) {
tmp_csa_settings = rcu_dereference(ifmsh->csa);
rcu_assign_pointer(ifmsh->csa, NULL);
kfree_rcu(tmp_csa_settings, rcu_head);
return ret;
}
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
if (csa_action)
ieee80211_send_action_csa(sdata, csa_settings);
return 0;
}
static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata, static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, size_t len) struct ieee80211_mgmt *mgmt, size_t len)
{ {
...@@ -942,6 +1039,7 @@ static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata, ...@@ -942,6 +1039,7 @@ static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata,
offset_ttl = (len < 42) ? 7 : 10; offset_ttl = (len < 42) ? 7 : 10;
*(pos + offset_ttl) -= 1; *(pos + offset_ttl) -= 1;
*(pos + offset_ttl + 1) &= ~WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; *(pos + offset_ttl + 1) &= ~WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
sdata->u.mesh.chsw_ttl = *(pos + offset_ttl);
memcpy(mgmt_fwd, mgmt, len); memcpy(mgmt_fwd, mgmt, len);
eth_broadcast_addr(mgmt_fwd->da); eth_broadcast_addr(mgmt_fwd->da);
......
...@@ -2593,13 +2593,16 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) ...@@ -2593,13 +2593,16 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
break; break;
if (sdata->vif.type != NL80211_IFTYPE_STATION && if (sdata->vif.type != NL80211_IFTYPE_STATION &&
sdata->vif.type != NL80211_IFTYPE_ADHOC) sdata->vif.type != NL80211_IFTYPE_ADHOC &&
sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
break; break;
if (sdata->vif.type == NL80211_IFTYPE_STATION) if (sdata->vif.type == NL80211_IFTYPE_STATION)
bssid = sdata->u.mgd.bssid; bssid = sdata->u.mgd.bssid;
else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
bssid = sdata->u.ibss.bssid; bssid = sdata->u.ibss.bssid;
else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
bssid = mgmt->sa;
else else
break; break;
......
...@@ -2398,6 +2398,10 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata, ...@@ -2398,6 +2398,10 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
beacon_data = beacon->head; beacon_data = beacon->head;
beacon_data_len = beacon->head_len; beacon_data_len = beacon->head_len;
break; break;
case NL80211_IFTYPE_MESH_POINT:
beacon_data = beacon->head;
beacon_data_len = beacon->head_len;
break;
default: default:
return; return;
} }
...@@ -2452,6 +2456,15 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif) ...@@ -2452,6 +2456,15 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
if (!beacon) if (!beacon)
goto out; goto out;
beacon_data = beacon->head;
beacon_data_len = beacon->head_len;
} else if (vif->type == NL80211_IFTYPE_MESH_POINT) {
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
beacon = rcu_dereference(ifmsh->beacon);
if (!beacon)
goto out;
beacon_data = beacon->head; beacon_data = beacon->head;
beacon_data_len = beacon->head_len; beacon_data_len = beacon->head_len;
} else { } else {
...@@ -2559,6 +2572,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, ...@@ -2559,6 +2572,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
if (!bcn) if (!bcn)
goto out; goto out;
if (sdata->vif.csa_active)
ieee80211_update_csa(sdata, bcn);
if (ifmsh->sync_ops) if (ifmsh->sync_ops)
ifmsh->sync_ops->adjust_tbtt( ifmsh->sync_ops->adjust_tbtt(
sdata); sdata);
......
...@@ -10813,7 +10813,8 @@ void cfg80211_ch_switch_notify(struct net_device *dev, ...@@ -10813,7 +10813,8 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
wdev->iftype != NL80211_IFTYPE_P2P_GO && wdev->iftype != NL80211_IFTYPE_P2P_GO &&
wdev->iftype != NL80211_IFTYPE_ADHOC)) wdev->iftype != NL80211_IFTYPE_ADHOC &&
wdev->iftype != NL80211_IFTYPE_MESH_POINT))
goto out; goto out;
wdev->channel = chandef->chan; wdev->channel = chandef->chan;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment