aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorChun-Yeow Yeoh <yeohchunyeow@cozybit.com>2013-10-14 22:08:28 -0400
committerJohannes Berg <johannes.berg@intel.com>2013-10-28 10:05:29 -0400
commitc6da674aff9425dc41255bcb7f7586a656843f2d (patch)
treeb711d3f6149e66ee579efc544c3391e4b0a48e7c /net/mac80211
parent8f2535b92d685c68db4bc699dd78462a646f6ef9 (diff)
{nl,cfg,mac}80211: enable the triggering of CSA frame in mesh
Allow the triggering of CSA frame using mesh interface. The rules are more or less same with IBSS, such as not allowed to change between the band and channel width has to be same from the previous mode. Also, move the ieee80211_send_action_csa to a common space so that it can be re-used by mesh interface. Signed-off-by: Chun-Yeow Yeoh <yeohchunyeow@cozybit.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/cfg.c21
-rw-r--r--net/mac80211/ibss.c54
-rw-r--r--net/mac80211/ieee80211_i.h2
-rw-r--r--net/mac80211/util.c87
4 files changed, 110 insertions, 54 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 67f62dac54f5..8cdbd29cbc45 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -3014,6 +3014,7 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
3014 struct ieee80211_local *local = sdata->local; 3014 struct ieee80211_local *local = sdata->local;
3015 struct ieee80211_chanctx_conf *chanctx_conf; 3015 struct ieee80211_chanctx_conf *chanctx_conf;
3016 struct ieee80211_chanctx *chanctx; 3016 struct ieee80211_chanctx *chanctx;
3017 struct ieee80211_if_mesh __maybe_unused *ifmsh;
3017 int err, num_chanctx; 3018 int err, num_chanctx;
3018 3019
3019 if (!list_empty(&local->roc_list) || local->scanning) 3020 if (!list_empty(&local->roc_list) || local->scanning)
@@ -3097,6 +3098,26 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
3097 if (err < 0) 3098 if (err < 0)
3098 return err; 3099 return err;
3099 break; 3100 break;
3101#ifdef CONFIG_MAC80211_MESH
3102 case NL80211_IFTYPE_MESH_POINT:
3103 ifmsh = &sdata->u.mesh;
3104
3105 if (!ifmsh->mesh_id)
3106 return -EINVAL;
3107
3108 if (params->chandef.width != sdata->vif.bss_conf.chandef.width)
3109 return -EINVAL;
3110
3111 /* changes into another band are not supported */
3112 if (sdata->vif.bss_conf.chandef.chan->band !=
3113 params->chandef.chan->band)
3114 return -EINVAL;
3115
3116 err = ieee80211_send_action_csa(sdata, params);
3117 if (err < 0)
3118 return err;
3119 break;
3120#endif
3100 default: 3121 default:
3101 return -EOPNOTSUPP; 3122 return -EOPNOTSUPP;
3102 } 3123 }
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index a0ae02760139..531be040b9ae 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -464,60 +464,6 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
464 tsf, false); 464 tsf, false);
465} 465}
466 466
467static int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
468 struct cfg80211_csa_settings *csa_settings)
469{
470 struct sk_buff *skb;
471 struct ieee80211_mgmt *mgmt;
472 struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
473 struct ieee80211_local *local = sdata->local;
474 int freq;
475 int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) +
476 sizeof(mgmt->u.action.u.chan_switch);
477 u8 *pos;
478
479 skb = dev_alloc_skb(local->tx_headroom + hdr_len +
480 5 + /* channel switch announcement element */
481 3); /* secondary channel offset element */
482 if (!skb)
483 return -1;
484
485 skb_reserve(skb, local->tx_headroom);
486 mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len);
487 memset(mgmt, 0, hdr_len);
488 mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
489 IEEE80211_STYPE_ACTION);
490
491 eth_broadcast_addr(mgmt->da);
492 memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
493 memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
494 mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
495 mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
496 pos = skb_put(skb, 5);
497 *pos++ = WLAN_EID_CHANNEL_SWITCH; /* EID */
498 *pos++ = 3; /* IE length */
499 *pos++ = csa_settings->block_tx ? 1 : 0; /* CSA mode */
500 freq = csa_settings->chandef.chan->center_freq;
501 *pos++ = ieee80211_frequency_to_channel(freq); /* channel */
502 *pos++ = csa_settings->count; /* count */
503
504 if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
505 enum nl80211_channel_type ch_type;
506
507 skb_put(skb, 3);
508 *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; /* EID */
509 *pos++ = 1; /* IE length */
510 ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
511 if (ch_type == NL80211_CHAN_HT40PLUS)
512 *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
513 else
514 *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
515 }
516
517 ieee80211_tx_skb(sdata, skb);
518 return 0;
519}
520
521int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata, 467int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
522 struct cfg80211_csa_settings *csa_settings) 468 struct cfg80211_csa_settings *csa_settings)
523{ 469{
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 4ebbcc6f67e0..9aad167e2ebc 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1731,6 +1731,8 @@ void ieee80211_dfs_cac_timer(unsigned long data);
1731void ieee80211_dfs_cac_timer_work(struct work_struct *work); 1731void ieee80211_dfs_cac_timer_work(struct work_struct *work);
1732void ieee80211_dfs_cac_cancel(struct ieee80211_local *local); 1732void ieee80211_dfs_cac_cancel(struct ieee80211_local *local);
1733void ieee80211_dfs_radar_detected_work(struct work_struct *work); 1733void ieee80211_dfs_radar_detected_work(struct work_struct *work);
1734int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
1735 struct cfg80211_csa_settings *csa_settings);
1734 1736
1735#ifdef CONFIG_MAC80211_NOINLINE 1737#ifdef CONFIG_MAC80211_NOINLINE
1736#define debug_noinline noinline 1738#define debug_noinline noinline
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 523783cedf6e..a38d58231af8 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -2384,3 +2384,90 @@ bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old,
2384 2384
2385 return false; 2385 return false;
2386} 2386}
2387
2388int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
2389 struct cfg80211_csa_settings *csa_settings)
2390{
2391 struct sk_buff *skb;
2392 struct ieee80211_mgmt *mgmt;
2393 struct ieee80211_local *local = sdata->local;
2394 int freq;
2395 int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) +
2396 sizeof(mgmt->u.action.u.chan_switch);
2397 u8 *pos;
2398
2399 if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
2400 sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
2401 return -EOPNOTSUPP;
2402
2403 skb = dev_alloc_skb(local->tx_headroom + hdr_len +
2404 5 + /* channel switch announcement element */
2405 3 + /* secondary channel offset element */
2406 8); /* mesh channel switch parameters element */
2407 if (!skb)
2408 return -ENOMEM;
2409
2410 skb_reserve(skb, local->tx_headroom);
2411 mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len);
2412 memset(mgmt, 0, hdr_len);
2413 mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
2414 IEEE80211_STYPE_ACTION);
2415
2416 eth_broadcast_addr(mgmt->da);
2417 memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
2418 if (ieee80211_vif_is_mesh(&sdata->vif)) {
2419 memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
2420 } else {
2421 struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
2422 memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
2423 }
2424 mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
2425 mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
2426 pos = skb_put(skb, 5);
2427 *pos++ = WLAN_EID_CHANNEL_SWITCH; /* EID */
2428 *pos++ = 3; /* IE length */
2429 *pos++ = csa_settings->block_tx ? 1 : 0; /* CSA mode */
2430 freq = csa_settings->chandef.chan->center_freq;
2431 *pos++ = ieee80211_frequency_to_channel(freq); /* channel */
2432 *pos++ = csa_settings->count; /* count */
2433
2434 if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
2435 enum nl80211_channel_type ch_type;
2436
2437 skb_put(skb, 3);
2438 *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; /* EID */
2439 *pos++ = 1; /* IE length */
2440 ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
2441 if (ch_type == NL80211_CHAN_HT40PLUS)
2442 *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
2443 else
2444 *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
2445 }
2446
2447 if (ieee80211_vif_is_mesh(&sdata->vif)) {
2448 struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
2449 __le16 pre_value;
2450
2451 skb_put(skb, 8);
2452 *pos++ = WLAN_EID_CHAN_SWITCH_PARAM; /* EID */
2453 *pos++ = 6; /* IE length */
2454 *pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL; /* Mesh TTL */
2455 *pos = 0x00; /* Mesh Flag: Tx Restrict, Initiator, Reason */
2456 *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
2457 *pos++ |= csa_settings->block_tx ?
2458 WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
2459 put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */
2460 pos += 2;
2461 if (!ifmsh->pre_value)
2462 ifmsh->pre_value = 1;
2463 else
2464 ifmsh->pre_value++;
2465 pre_value = cpu_to_le16(ifmsh->pre_value);
2466 memcpy(pos, &pre_value, 2); /* Precedence Value */
2467 pos += 2;
2468 ifmsh->chsw_init = true;
2469 }
2470
2471 ieee80211_tx_skb(sdata, skb);
2472 return 0;
2473}