Allow changing to DFS channels if the channel is available for beaconing and a control program is available. Channel switch announcement on DFS channels will trigger a radar event and mark these channels as unavailable. Signed-off-by: Simon Wunderlich <siwu@xxxxxxxxxxxxxxxxxx> Signed-off-by: Mathias Kretschmer <mathias.kretschmer@xxxxxxxxxxxxxxxxxxx> --- net/mac80211/ibss.c | 50 +++++++++++++++++++++++++++++++++++++++----- net/mac80211/ieee80211_i.h | 1 + 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 5ea9b3a..c3bf369 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -229,6 +229,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, struct beacon_data *presp; enum nl80211_bss_scan_width scan_width; bool have_higher_than_11mbit; + bool radar_required = false; + int err; sdata_assert_lock(sdata); @@ -271,6 +273,23 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, } chandef.width = NL80211_CHAN_WIDTH_20; chandef.center_freq1 = chan->center_freq; + /* check again for downgraded chandef */ + if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) { + sdata_info(sdata, + "Failed to join IBSS, beacons forbidden\n"); + return; + } + } + + err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, + &chandef); + if (err > 0) { + if (!ifibss->control_port_dfs) { + sdata_info(sdata, + "Failed to join IBSS, DFS channel without control program\n"); + return; + } + radar_required = true; } ieee80211_vif_release_channel(sdata); @@ -295,6 +314,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, rcu_assign_pointer(ifibss->presp, presp); mgmt = (void *)presp->head; + sdata->radar_required = radar_required; sdata->vif.bss_conf.enable_beacon = true; sdata->vif.bss_conf.beacon_int = beacon_int; sdata->vif.bss_conf.basic_rates = basic_rates; @@ -778,6 +798,21 @@ static void ieee80211_csa_connection_drop_work(struct work_struct *work) ieee80211_queue_work(&sdata->local->hw, &sdata->work); } +static void ieee80211_ibss_csa_mark_radar(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + int err; + + /* if the current channel is a DFS channel, mark the channel as + * unavailable. + */ + err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, + &ifibss->chandef); + if (err > 0) + cfg80211_radar_event(sdata->local->hw.wiphy, &ifibss->chandef, + GFP_ATOMIC); +} + static bool ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, struct ieee802_11_elems *elems, @@ -862,8 +897,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, goto disconnect; } - if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, ¶ms.chandef, - IEEE80211_CHAN_DISABLED)) { + if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, ¶ms.chandef)) { sdata_info(sdata, "IBSS %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", ifibss->bssid, @@ -879,10 +913,11 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, if (err < 0) goto disconnect; if (err) { + /* IBSS-DFS only allowed with a control program */ + if (!ifibss->control_port_dfs) + goto disconnect; + params.radar_required = true; - - /* TODO: IBSS-DFS not (yet) supported, disconnect. */ - goto disconnect; } rcu_read_lock(); @@ -929,12 +964,16 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, ieee80211_bss_info_change_notify(sdata, err); drv_channel_switch_beacon(sdata, ¶ms.chandef); + ieee80211_ibss_csa_mark_radar(sdata); + return true; disconnect: ibss_dbg(sdata, "Can't handle channel switch, disconnect\n"); ieee80211_queue_work(&sdata->local->hw, &ifibss->csa_connection_drop_work); + ieee80211_ibss_csa_mark_radar(sdata); + return true; } @@ -1670,6 +1709,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, sdata->u.ibss.privacy = params->privacy; sdata->u.ibss.control_port = params->control_port; + sdata->u.ibss.control_port_dfs = params->control_port_dfs; sdata->u.ibss.basic_rates = params->basic_rates; /* fix basic_rates if channel does not support these rates */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 3a87c89..2bb01b6 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -497,6 +497,7 @@ struct ieee80211_if_ibss { bool privacy; bool control_port; + bool control_port_dfs; u8 bssid[ETH_ALEN] __aligned(2); u8 ssid[IEEE80211_MAX_SSID_LEN]; -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html