aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
-rw-r--r--net/wireless/nl80211.c1
5 files changed, 111 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}
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 7502d33a3a70..b8d6f101378a 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -5700,6 +5700,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
5700 return -EINVAL; 5700 return -EINVAL;
5701 break; 5701 break;
5702 case NL80211_IFTYPE_ADHOC: 5702 case NL80211_IFTYPE_ADHOC:
5703 case NL80211_IFTYPE_MESH_POINT:
5703 break; 5704 break;
5704 default: 5705 default:
5705 return -EOPNOTSUPP; 5706 return -EOPNOTSUPP;