aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/mac80211/ibss.c60
1 files changed, 60 insertions, 0 deletions
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index c0042136f1d8..5ea9b3ae303e 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -428,6 +428,60 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
428 tsf, false); 428 tsf, false);
429} 429}
430 430
431static int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
432 struct cfg80211_csa_settings *csa_settings)
433{
434 struct sk_buff *skb;
435 struct ieee80211_mgmt *mgmt;
436 struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
437 struct ieee80211_local *local = sdata->local;
438 int freq;
439 int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) +
440 sizeof(mgmt->u.action.u.chan_switch);
441 u8 *pos;
442
443 skb = dev_alloc_skb(local->tx_headroom + hdr_len +
444 5 + /* channel switch announcement element */
445 3); /* secondary channel offset element */
446 if (!skb)
447 return -1;
448
449 skb_reserve(skb, local->tx_headroom);
450 mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len);
451 memset(mgmt, 0, hdr_len);
452 mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
453 IEEE80211_STYPE_ACTION);
454
455 eth_broadcast_addr(mgmt->da);
456 memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
457 memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
458 mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
459 mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
460 pos = skb_put(skb, 5);
461 *pos++ = WLAN_EID_CHANNEL_SWITCH; /* EID */
462 *pos++ = 3; /* IE length */
463 *pos++ = csa_settings->block_tx ? 1 : 0; /* CSA mode */
464 freq = csa_settings->chandef.chan->center_freq;
465 *pos++ = ieee80211_frequency_to_channel(freq); /* channel */
466 *pos++ = csa_settings->count; /* count */
467
468 if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
469 enum nl80211_channel_type ch_type;
470
471 skb_put(skb, 3);
472 *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; /* EID */
473 *pos++ = 1; /* IE length */
474 ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
475 if (ch_type == NL80211_CHAN_HT40PLUS)
476 *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
477 else
478 *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
479 }
480
481 ieee80211_tx_skb(sdata, skb);
482 return 0;
483}
484
431int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata, 485int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
432 struct cfg80211_csa_settings *csa_settings) 486 struct cfg80211_csa_settings *csa_settings)
433{ 487{
@@ -480,6 +534,12 @@ int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
480 if (old_presp) 534 if (old_presp)
481 kfree_rcu(old_presp, rcu_head); 535 kfree_rcu(old_presp, rcu_head);
482 536
537 /* it might not send the beacon for a while. send an action frame
538 * immediately to announce the channel switch.
539 */
540 if (csa_settings)
541 ieee80211_send_action_csa(sdata, csa_settings);
542
483 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); 543 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
484 out: 544 out:
485 return ret; 545 return ret;