@@ -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 */
@@ -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];