ath10k: introduce DFS implementation
authorMarek Puzyniak <marek.puzyniak@tieto.com>
Wed, 20 Nov 2013 07:59:47 +0000 (09:59 +0200)
committerKalle Valo <kvalo@qca.qualcomm.com>
Wed, 20 Nov 2013 07:59:47 +0000 (09:59 +0200)
Configure interface combination for AP running on channels
where radar detection is required. It allows only one type
of interface - AP on DFS channel and limits number of AP
interfaces to 8. Setup WMI channel flags accordingly to mac
channel configuration. CAC based on additional monitor vdev
is started if required for current channel.

kvalo: dropped ATH10K_DFS_CERTIFIED config option as this
the DFS still depends on few mac80211 and cfg80211 patches
which are on mac80211-next.git right now. The config option
will be added later once all dependencies are available.

Signed-off-by: Marek Puzyniak <marek.puzyniak@tieto.com>
Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/htt_rx.c
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/wmi.c
drivers/net/wireless/ath/ath10k/wmi.h

index afbc744..0b88d55 100644 (file)
@@ -310,6 +310,11 @@ enum ath10k_fw_features {
        ATH10K_FW_FEATURE_COUNT,
 };
 
+enum ath10k_dev_flags {
+       /* Indicates that ath10k device is during CAC phase of DFS */
+       ATH10K_CAC_RUNNING,
+};
+
 struct ath10k {
        struct ath_common ath_common;
        struct ieee80211_hw *hw;
@@ -403,6 +408,7 @@ struct ath10k {
        bool monitor_enabled;
        bool monitor_present;
        unsigned int filter_flags;
+       unsigned long dev_flags;
 
        struct wmi_pdev_set_wmm_params_arg wmm_params;
        struct completion install_key_done;
index 0335218..fcb534f 100644 (file)
@@ -945,6 +945,11 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
                                continue;
                        }
 
+                       if (test_bit(ATH10K_CAC_RUNNING, &htt->ar->dev_flags)) {
+                               ath10k_htt_rx_free_msdu_chain(msdu_head);
+                               continue;
+                       }
+
                        /* FIXME: we do not support chaining yet.
                         * this needs investigation */
                        if (msdu_chaining) {
index c95a398..b70a3b2 100644 (file)
@@ -463,6 +463,10 @@ static int ath10k_vdev_start(struct ath10k_vif *arvif)
                arg.ssid = arvif->u.ap.ssid;
                arg.ssid_len = arvif->u.ap.ssid_len;
                arg.hidden_ssid = arvif->u.ap.hidden_ssid;
+
+               /* For now allow DFS for AP mode */
+               arg.channel.chan_radar =
+                       !!(channel->flags & IEEE80211_CHAN_RADAR);
        } else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
                arg.ssid = arvif->vif->bss_conf.ssid;
                arg.ssid_len = arvif->vif->bss_conf.ssid_len;
@@ -532,6 +536,8 @@ static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
        /* TODO setup this dynamically, what in case we
           don't have any vifs? */
        arg.channel.mode = chan_to_phymode(&ar->hw->conf.chandef);
+       arg.channel.chan_radar =
+                       !!(channel->flags & IEEE80211_CHAN_RADAR);
 
        arg.channel.min_power = 0;
        arg.channel.max_power = channel->max_power * 2;
@@ -666,6 +672,107 @@ static int ath10k_monitor_destroy(struct ath10k *ar)
        return ret;
 }
 
+static int ath10k_start_cac(struct ath10k *ar)
+{
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       set_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+
+       ret = ath10k_monitor_create(ar);
+       if (ret) {
+               clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+               return ret;
+       }
+
+       ret = ath10k_monitor_start(ar, ar->monitor_vdev_id);
+       if (ret) {
+               clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+               ath10k_monitor_destroy(ar);
+               return ret;
+       }
+
+       ath10k_dbg(ATH10K_DBG_MAC, "mac cac start monitor vdev %d\n",
+                  ar->monitor_vdev_id);
+
+       return 0;
+}
+
+static int ath10k_stop_cac(struct ath10k *ar)
+{
+       lockdep_assert_held(&ar->conf_mutex);
+
+       /* CAC is not running - do nothing */
+       if (!test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags))
+               return 0;
+
+       ath10k_monitor_stop(ar);
+       ath10k_monitor_destroy(ar);
+       clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags);
+
+       ath10k_dbg(ATH10K_DBG_MAC, "mac cac finished\n");
+
+       return 0;
+}
+
+static const char *ath10k_dfs_state(enum nl80211_dfs_state dfs_state)
+{
+       switch (dfs_state) {
+       case NL80211_DFS_USABLE:
+               return "USABLE";
+       case NL80211_DFS_UNAVAILABLE:
+               return "UNAVAILABLE";
+       case NL80211_DFS_AVAILABLE:
+               return "AVAILABLE";
+       default:
+               WARN_ON(1);
+               return "bug";
+       }
+}
+
+static void ath10k_config_radar_detection(struct ath10k *ar)
+{
+       struct ieee80211_channel *chan = ar->hw->conf.chandef.chan;
+       bool radar = ar->hw->conf.radar_enabled;
+       bool chan_radar = !!(chan->flags & IEEE80211_CHAN_RADAR);
+       enum nl80211_dfs_state dfs_state = chan->dfs_state;
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       ath10k_dbg(ATH10K_DBG_MAC,
+                  "mac radar config update: chan %dMHz radar %d chan radar %d chan state %s\n",
+                  chan->center_freq, radar, chan_radar,
+                  ath10k_dfs_state(dfs_state));
+
+       /*
+        * It's safe to call it even if CAC is not started.
+        * This call here guarantees changing channel, etc. will stop CAC.
+        */
+       ath10k_stop_cac(ar);
+
+       if (!radar)
+               return;
+
+       if (!chan_radar)
+               return;
+
+       if (dfs_state != NL80211_DFS_USABLE)
+               return;
+
+       ret = ath10k_start_cac(ar);
+       if (ret) {
+               /*
+                * Not possible to start CAC on current channel so starting
+                * radiation is not allowed, make this channel DFS_UNAVAILABLE
+                * by indicating that radar was detected.
+                */
+               ath10k_warn("failed to start CAC (%d)\n", ret);
+               ieee80211_radar_detected(ar->hw);
+       }
+}
+
 static void ath10k_control_beaconing(struct ath10k_vif *arvif,
                                struct ieee80211_bss_conf *info)
 {
@@ -1375,6 +1482,9 @@ static int ath10k_update_channel_list(struct ath10k *ar)
                        ch->ht40plus =
                                !(channel->flags & IEEE80211_CHAN_NO_HT40PLUS);
 
+                       ch->chan_radar =
+                               !!(channel->flags & IEEE80211_CHAN_RADAR);
+
                        passive = channel->flags & IEEE80211_CHAN_PASSIVE_SCAN;
                        ch->passive = passive;
 
@@ -1921,6 +2031,7 @@ void ath10k_halt(struct ath10k *ar)
 {
        lockdep_assert_held(&ar->conf_mutex);
 
+       ath10k_stop_cac(ar);
        del_timer_sync(&ar->scan.timeout);
        ath10k_offchan_tx_purge(ar);
        ath10k_mgmt_over_wmi_tx_purge(ar);
@@ -2035,11 +2146,16 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
        mutex_lock(&ar->conf_mutex);
 
        if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
-               ath10k_dbg(ATH10K_DBG_MAC, "mac config channel %d mhz\n",
-                          conf->chandef.chan->center_freq);
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "mac config channel %d mhz flags 0x%x\n",
+                          conf->chandef.chan->center_freq,
+                          conf->chandef.chan->flags);
+
                spin_lock_bh(&ar->data_lock);
                ar->rx_channel = conf->chandef.chan;
                spin_unlock_bh(&ar->data_lock);
+
+               ath10k_config_radar_detection(ar);
        }
 
        if (changed & IEEE80211_CONF_CHANGE_POWER) {
@@ -3308,12 +3424,36 @@ static const struct ieee80211_iface_limit ath10k_if_limits[] = {
        },
 };
 
-static const struct ieee80211_iface_combination ath10k_if_comb = {
-       .limits = ath10k_if_limits,
-       .n_limits = ARRAY_SIZE(ath10k_if_limits),
-       .max_interfaces = 8,
-       .num_different_channels = 1,
-       .beacon_int_infra_match = true,
+#ifdef CONFIG_ATH10K_DFS_CERTIFIED
+static const struct ieee80211_iface_limit ath10k_if_dfs_limits[] = {
+       {
+       .max    = 8,
+       .types  = BIT(NL80211_IFTYPE_AP)
+       },
+};
+#endif
+
+static const struct ieee80211_iface_combination ath10k_if_comb[] = {
+       {
+               .limits = ath10k_if_limits,
+               .n_limits = ARRAY_SIZE(ath10k_if_limits),
+               .max_interfaces = 8,
+               .num_different_channels = 1,
+               .beacon_int_infra_match = true,
+       },
+#ifdef CONFIG_ATH10K_DFS_CERTIFIED
+       {
+               .limits = ath10k_if_dfs_limits,
+               .n_limits = ARRAY_SIZE(ath10k_if_dfs_limits),
+               .max_interfaces = 8,
+               .num_different_channels = 1,
+               .beacon_int_infra_match = true,
+               .radar_detect_widths =  BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+                                       BIT(NL80211_CHAN_WIDTH_20) |
+                                       BIT(NL80211_CHAN_WIDTH_40) |
+                                       BIT(NL80211_CHAN_WIDTH_80),
+       }
+#endif
 };
 
 static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar)
@@ -3537,8 +3677,8 @@ int ath10k_mac_register(struct ath10k *ar)
         */
        ar->hw->queues = 4;
 
-       ar->hw->wiphy->iface_combinations = &ath10k_if_comb;
-       ar->hw->wiphy->n_iface_combinations = 1;
+       ar->hw->wiphy->iface_combinations = ath10k_if_comb;
+       ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ath10k_if_comb);
 
        ar->hw->netdev_features = NETIF_F_HW_CSUM;
 
index f0bc94a..aea81d9 100644 (file)
@@ -907,6 +907,11 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
        ath10k_dbg(ATH10K_DBG_MGMT,
                   "event mgmt rx status %08x\n", rx_status);
 
+       if (test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) {
+               dev_kfree_skb(skb);
+               return 0;
+       }
+
        if (rx_status & WMI_RX_STATUS_ERR_DECRYPT) {
                dev_kfree_skb(skb);
                return 0;
@@ -2302,6 +2307,7 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
 {
        struct wmi_set_channel_cmd *cmd;
        struct sk_buff *skb;
+       u32 ch_flags = 0;
 
        if (arg->passive)
                return -EINVAL;
@@ -2310,10 +2316,14 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
        if (!skb)
                return -ENOMEM;
 
+       if (arg->chan_radar)
+               ch_flags |= WMI_CHAN_FLAG_DFS;
+
        cmd = (struct wmi_set_channel_cmd *)skb->data;
        cmd->chan.mhz               = __cpu_to_le32(arg->freq);
        cmd->chan.band_center_freq1 = __cpu_to_le32(arg->freq);
        cmd->chan.mode              = arg->mode;
+       cmd->chan.flags            |= __cpu_to_le32(ch_flags);
        cmd->chan.min_power         = arg->min_power;
        cmd->chan.max_power         = arg->max_power;
        cmd->chan.reg_power         = arg->max_reg_power;
@@ -2862,6 +2872,7 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
        struct sk_buff *skb;
        const char *cmdname;
        u32 flags = 0;
+       u32 ch_flags = 0;
 
        if (cmd_id != ar->wmi.cmd->vdev_start_request_cmdid &&
            cmd_id != ar->wmi.cmd->vdev_restart_request_cmdid)
@@ -2888,6 +2899,8 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
                flags |= WMI_VDEV_START_HIDDEN_SSID;
        if (arg->pmf_enabled)
                flags |= WMI_VDEV_START_PMF_ENABLED;
+       if (arg->channel.chan_radar)
+               ch_flags |= WMI_CHAN_FLAG_DFS;
 
        cmd = (struct wmi_vdev_start_request_cmd *)skb->data;
        cmd->vdev_id         = __cpu_to_le32(arg->vdev_id);
@@ -2909,6 +2922,7 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
                __cpu_to_le32(arg->channel.band_center_freq1);
 
        cmd->chan.mode = arg->channel.mode;
+       cmd->chan.flags |= __cpu_to_le32(ch_flags);
        cmd->chan.min_power = arg->channel.min_power;
        cmd->chan.max_power = arg->channel.max_power;
        cmd->chan.reg_power = arg->channel.max_reg_power;
@@ -2916,9 +2930,10 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
        cmd->chan.antenna_max = arg->channel.max_antenna_gain;
 
        ath10k_dbg(ATH10K_DBG_WMI,
-                  "wmi vdev %s id 0x%x freq %d, mode %d, ch_flags: 0x%0X,"
-                  "max_power: %d\n", cmdname, arg->vdev_id, arg->channel.freq,
-                  arg->channel.mode, flags, arg->channel.max_power);
+                  "wmi vdev %s id 0x%x flags: 0x%0X, freq %d, mode %d, "
+                  "ch_flags: 0x%0X, max_power: %d\n", cmdname, arg->vdev_id,
+                  flags, arg->channel.freq, arg->channel.mode,
+                  cmd->chan.flags, arg->channel.max_power);
 
        return ath10k_wmi_cmd_send(ar, skb, cmd_id);
 }
@@ -3252,6 +3267,8 @@ int ath10k_wmi_scan_chan_list(struct ath10k *ar,
                        flags |= WMI_CHAN_FLAG_ALLOW_VHT;
                if (ch->ht40plus)
                        flags |= WMI_CHAN_FLAG_HT40_PLUS;
+               if (ch->chan_radar)
+                       flags |= WMI_CHAN_FLAG_DFS;
 
                ci->mhz               = __cpu_to_le32(ch->freq);
                ci->band_center_freq1 = __cpu_to_le32(ch->freq);
index e71273e..0087d69 100644 (file)
@@ -916,6 +916,7 @@ struct wmi_channel_arg {
        bool allow_ht;
        bool allow_vht;
        bool ht40plus;
+       bool chan_radar;
        /* note: power unit is 0.5 dBm */
        u32 min_power;
        u32 max_power;