mac80211: add radar detection command/event
authorSimon Wunderlich <simon.wunderlich@s2003.tu-chemnitz.de>
Fri, 8 Feb 2013 17:16:20 +0000 (18:16 +0100)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 15 Feb 2013 08:41:04 +0000 (09:41 +0100)
Add command to trigger radar detection in the driver/FW.
Once radar detection is started it should continuously
monitor for radars as long as the channel active.
If radar is detected usermode notified with 'radar
detected' event.

Scanning and remain on channel functionality must be disabled
while doing radar detection/scanning, and vice versa.

Based on original patch by Victor Goldenshtein <victorg@ti.com>

Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/mac80211.h
net/mac80211/cfg.c
net/mac80211/chan.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/main.c
net/mac80211/mlme.c
net/mac80211/pm.c
net/mac80211/scan.c
net/mac80211/trace.h
net/mac80211/util.c

index 0eaa9092364bfbe2b914347595549790ab6276b8..7241962f9f13040aae59d65b2fed4b6ca58b3cda 100644 (file)
@@ -147,10 +147,12 @@ struct ieee80211_low_level_stats {
  * enum ieee80211_chanctx_change - change flag for channel context
  * @IEEE80211_CHANCTX_CHANGE_WIDTH: The channel width changed
  * @IEEE80211_CHANCTX_CHANGE_RX_CHAINS: The number of RX chains changed
+ * @IEEE80211_CHANCTX_CHANGE_RADAR: radar detection flag changed
  */
 enum ieee80211_chanctx_change {
        IEEE80211_CHANCTX_CHANGE_WIDTH          = BIT(0),
        IEEE80211_CHANCTX_CHANGE_RX_CHAINS      = BIT(1),
+       IEEE80211_CHANCTX_CHANGE_RADAR          = BIT(2),
 };
 
 /**
@@ -165,6 +167,7 @@ enum ieee80211_chanctx_change {
  * @rx_chains_dynamic: The number of RX chains that must be enabled
  *     after RTS/CTS handshake to receive SMPS MIMO transmissions;
  *     this will always be >= @rx_chains_static.
+ * @radar_enabled: whether radar detection is enabled on this channel.
  * @drv_priv: data area for driver use, will always be aligned to
  *     sizeof(void *), size is determined in hw information.
  */
@@ -173,6 +176,8 @@ struct ieee80211_chanctx_conf {
 
        u8 rx_chains_static, rx_chains_dynamic;
 
+       bool radar_enabled;
+
        u8 drv_priv[0] __aligned(sizeof(void *));
 };
 
@@ -967,6 +972,7 @@ enum ieee80211_smps_mode {
  *
  * @channel: the channel to tune to
  * @channel_type: the channel (HT) type
+ * @radar_enabled: whether radar detection is enabled
  *
  * @long_frame_max_tx_count: Maximum number of transmissions for a "long" frame
  *    (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11,
@@ -993,6 +999,7 @@ struct ieee80211_conf {
 
        struct ieee80211_channel *channel;
        enum nl80211_channel_type channel_type;
+       bool radar_enabled;
        enum ieee80211_smps_mode smps_mode;
 };
 
@@ -3944,6 +3951,13 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
                               enum nl80211_cqm_rssi_threshold_event rssi_event,
                               gfp_t gfp);
 
+/**
+ * ieee80211_radar_detected - inform that a radar was detected
+ *
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ */
+void ieee80211_radar_detected(struct ieee80211_hw *hw);
+
 /**
  * ieee80211_chswitch_done - Complete channel switch process
  * @vif: &struct ieee80211_vif pointer from the add_interface callback.
index e3dec80cf617a0d1b5671d66b403a6a75fd29979..0969978c2d92f24a2499cbf3ad9636ca54eccd0e 100644 (file)
@@ -928,6 +928,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
        /* TODO: make hostapd tell us what it wants */
        sdata->smps_mode = IEEE80211_SMPS_OFF;
        sdata->needed_rx_chains = sdata->local->rx_chains;
+       sdata->radar_required = params->radar_required;
 
        err = ieee80211_vif_use_channel(sdata, &params->chandef,
                                        IEEE80211_CHANCTX_SHARED);
@@ -2395,7 +2396,8 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
        INIT_LIST_HEAD(&roc->dependents);
 
        /* if there's one pending or we're scanning, queue this one */
-       if (!list_empty(&local->roc_list) || local->scanning)
+       if (!list_empty(&local->roc_list) ||
+           local->scanning || local->radar_detect_enabled)
                goto out_check_combine;
 
        /* if not HW assist, just queue & schedule work */
@@ -2645,6 +2647,37 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
        return ieee80211_cancel_roc(local, cookie, false);
 }
 
+static int ieee80211_start_radar_detection(struct wiphy *wiphy,
+                                          struct net_device *dev,
+                                          struct cfg80211_chan_def *chandef)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+       unsigned long timeout;
+       int err;
+
+       if (!list_empty(&local->roc_list) || local->scanning)
+               return -EBUSY;
+
+       /* whatever, but channel contexts should not complain about that one */
+       sdata->smps_mode = IEEE80211_SMPS_OFF;
+       sdata->needed_rx_chains = local->rx_chains;
+       sdata->radar_required = true;
+
+       mutex_lock(&local->iflist_mtx);
+       err = ieee80211_vif_use_channel(sdata, chandef,
+                                       IEEE80211_CHANCTX_SHARED);
+       mutex_unlock(&local->iflist_mtx);
+       if (err)
+               return err;
+
+       timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS);
+       ieee80211_queue_delayed_work(&sdata->local->hw,
+                                    &sdata->dfs_cac_timer_work, timeout);
+
+       return 0;
+}
+
 static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                             struct ieee80211_channel *chan, bool offchan,
                             unsigned int wait, const u8 *buf, size_t len,
@@ -3350,4 +3383,5 @@ struct cfg80211_ops mac80211_config_ops = {
        .get_et_stats = ieee80211_get_et_stats,
        .get_et_strings = ieee80211_get_et_strings,
        .get_channel = ieee80211_cfg_get_channel,
+       .start_radar_detection = ieee80211_start_radar_detection,
 };
index 038f249966d6ec8a5f49a40d50e7ce6ded602b78..2e6faeda22ad3447ef613686a90d930e67dcdf1e 100644 (file)
@@ -193,6 +193,7 @@ static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
        if (ctx->refcount > 0) {
                ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
                ieee80211_recalc_smps_chanctx(local, ctx);
+               ieee80211_recalc_radar_chanctx(local, ctx);
        }
 }
 
@@ -216,6 +217,37 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
                ieee80211_free_chanctx(local, ctx);
 }
 
+void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
+                                   struct ieee80211_chanctx *chanctx)
+{
+       struct ieee80211_sub_if_data *sdata;
+       bool radar_enabled = false;
+
+       lockdep_assert_held(&local->chanctx_mtx);
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+               if (sdata->radar_required) {
+                       radar_enabled = true;
+                       break;
+               }
+       }
+       rcu_read_unlock();
+
+       if (radar_enabled == chanctx->conf.radar_enabled)
+               return;
+
+       chanctx->conf.radar_enabled = radar_enabled;
+       local->radar_detect_enabled = chanctx->conf.radar_enabled;
+
+       if (!local->use_chanctx) {
+               local->hw.conf.radar_enabled = chanctx->conf.radar_enabled;
+               ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+       }
+
+       drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR);
+}
+
 void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
                                   struct ieee80211_chanctx *chanctx)
 {
@@ -331,6 +363,7 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
        }
 
        ieee80211_recalc_smps_chanctx(local, ctx);
+       ieee80211_recalc_radar_chanctx(local, ctx);
  out:
        mutex_unlock(&local->chanctx_mtx);
        return ret;
index 76cdcfcd614cea37ac6adfbdd3b7185a109ea9ac..0e0a9776be39307bcc9539d8875b0d7f719cfbc7 100644 (file)
@@ -722,6 +722,9 @@ struct ieee80211_sub_if_data {
        int user_power_level; /* in dBm */
        int ap_power_level; /* in dBm */
 
+       bool radar_required;
+       struct delayed_work dfs_cac_timer_work;
+
        /*
         * AP this belongs to: self in AP mode and
         * corresponding AP in VLAN mode, NULL for
@@ -942,6 +945,10 @@ struct ieee80211_local {
        /* wowlan is enabled -- don't reconfig on resume */
        bool wowlan;
 
+       /* DFS/radar detection is enabled */
+       bool radar_detect_enabled;
+       struct work_struct radar_detected_work;
+
        /* number of RX chains the hardware has */
        u8 rx_chains;
 
@@ -1606,6 +1613,13 @@ void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
 
 void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
                                   struct ieee80211_chanctx *chanctx);
+void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
+                                   struct ieee80211_chanctx *chanctx);
+
+void ieee80211_dfs_cac_timer(unsigned long data);
+void ieee80211_dfs_cac_timer_work(struct work_struct *work);
+void ieee80211_dfs_cac_cancel(struct ieee80211_local *local);
+void ieee80211_dfs_radar_detected_work(struct work_struct *work);
 
 #ifdef CONFIG_MAC80211_NOINLINE
 #define debug_noinline noinline
index 40ff0307d08927d766d04d8a8e7a3d81433e4435..e9223ce2bf994a4a9b4db60bcf15219eb5390528 100644 (file)
@@ -749,6 +749,16 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
 
        cancel_work_sync(&sdata->recalc_smps);
 
+       cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
+
+       if (sdata->wdev.cac_started) {
+               mutex_lock(&local->iflist_mtx);
+               ieee80211_vif_release_channel(sdata);
+               mutex_unlock(&local->iflist_mtx);
+               cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_ABORTED,
+                                  GFP_KERNEL);
+       }
+
        /* APs need special treatment */
        if (sdata->vif.type == NL80211_IFTYPE_AP) {
                struct ieee80211_sub_if_data *vlan, *tmpsdata;
@@ -1513,6 +1523,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
        spin_lock_init(&sdata->cleanup_stations_lock);
        INIT_LIST_HEAD(&sdata->cleanup_stations);
        INIT_WORK(&sdata->cleanup_stations_wk, ieee80211_cleanup_sdata_stas_wk);
+       INIT_DELAYED_WORK(&sdata->dfs_cac_timer_work,
+                         ieee80211_dfs_cac_timer_work);
 
        for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
                struct ieee80211_supported_band *sband;
index 38b3468bc515dd0ab915910fe3bf1ffeb61be523..9cdbc774cfd7f310b2ab3056c0587de3db8fddac 100644 (file)
@@ -621,6 +621,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
 
        INIT_WORK(&local->restart_work, ieee80211_restart_work);
 
+       INIT_WORK(&local->radar_detected_work,
+                 ieee80211_dfs_radar_detected_work);
+
        INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter);
        local->smps_mode = IEEE80211_SMPS_OFF;
 
@@ -713,6 +716,16 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                 */
                if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS))
                        return -EINVAL;
+
+               /* DFS currently not supported with channel context drivers */
+               for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) {
+                       const struct ieee80211_iface_combination *comb;
+
+                       comb = &local->hw.wiphy->iface_combinations[i];
+
+                       if (comb->radar_detect_widths)
+                               return -EINVAL;
+               }
        }
 
        /* Only HW csum features are currently compatible with mac80211 */
index efb22763d56df39808d71c6196feb3d564566c99..7d4cde7af98e558ada66e9e595fce9a1a2eb54cf 100644 (file)
@@ -1212,6 +1212,19 @@ void ieee80211_dynamic_ps_timer(unsigned long data)
        ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work);
 }
 
+void ieee80211_dfs_cac_timer_work(struct work_struct *work)
+{
+       struct delayed_work *delayed_work =
+               container_of(work, struct delayed_work, work);
+       struct ieee80211_sub_if_data *sdata =
+               container_of(delayed_work, struct ieee80211_sub_if_data,
+                            dfs_cac_timer_work);
+
+       ieee80211_vif_release_channel(sdata);
+
+       cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
+}
+
 /* MLME */
 static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
                                     struct ieee80211_sub_if_data *sdata,
index 53801d20176de64e64f4a550c053aefc4652c6a4..d0275f34bf700397702f0f752d15388cc515d281 100644 (file)
@@ -38,6 +38,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
 
        ieee80211_scan_cancel(local);
 
+       ieee80211_dfs_cac_cancel(local);
+
        if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
                mutex_lock(&local->sta_mtx);
                list_for_each_entry(sta, &local->sta_list, list) {
index 6d0b89e4aa31a0f1004ec005db2115370d8d2c4f..43a45cf00e06803b091a350694621d87150ee46f 100644 (file)
@@ -351,6 +351,9 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
 static bool ieee80211_can_scan(struct ieee80211_local *local,
                               struct ieee80211_sub_if_data *sdata)
 {
+       if (local->radar_detect_enabled)
+               return false;
+
        if (!list_empty(&local->roc_list))
                return false;
 
index 0bdd7aeb8958ce2c2ca57a0945bfc56c1d11c43f..1183c4a4fee55abb8f9c6d8577d6983e03ab5114 100644 (file)
@@ -1862,6 +1862,25 @@ TRACE_EVENT(drv_set_default_unicast_key,
                  LOCAL_PR_ARG, VIF_PR_ARG, __entry->key_idx)
 );
 
+TRACE_EVENT(api_radar_detected,
+       TP_PROTO(struct ieee80211_local *local),
+
+       TP_ARGS(local),
+
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+       ),
+
+       TP_printk(
+               LOCAL_PR_FMT " radar detected",
+               LOCAL_PR_ARG
+       )
+);
+
 #ifdef CONFIG_MAC80211_MESSAGE_TRACING
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM mac80211_msg
index 6cb71a350eddd5913a674a319409177638e69238..218cb52f2b59dff9996f85f47dd79a327a2d305e 100644 (file)
@@ -2133,3 +2133,49 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
 
        return ts;
 }
+
+void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
+{
+       struct ieee80211_sub_if_data *sdata;
+
+       mutex_lock(&local->iflist_mtx);
+       list_for_each_entry(sdata, &local->interfaces, list) {
+               cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
+
+               if (sdata->wdev.cac_started) {
+                       ieee80211_vif_release_channel(sdata);
+                       cfg80211_cac_event(sdata->dev,
+                                          NL80211_RADAR_CAC_ABORTED,
+                                          GFP_KERNEL);
+               }
+       }
+       mutex_unlock(&local->iflist_mtx);
+}
+
+void ieee80211_dfs_radar_detected_work(struct work_struct *work)
+{
+       struct ieee80211_local *local =
+               container_of(work, struct ieee80211_local, radar_detected_work);
+       struct cfg80211_chan_def chandef;
+
+       ieee80211_dfs_cac_cancel(local);
+
+       if (local->use_chanctx)
+               /* currently not handled */
+               WARN_ON(1);
+       else {
+               cfg80211_chandef_create(&chandef, local->hw.conf.channel,
+                                       local->hw.conf.channel_type);
+               cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL);
+       }
+}
+
+void ieee80211_radar_detected(struct ieee80211_hw *hw)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+
+       trace_api_radar_detected(local);
+
+       ieee80211_queue_work(hw, &local->radar_detected_work);
+}
+EXPORT_SYMBOL(ieee80211_radar_detected);