Commit e8a50f8b authored by Marek Puzyniak's avatar Marek Puzyniak Committed by Kalle Valo

ath10k: introduce DFS implementation

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: default avatarMarek Puzyniak <marek.puzyniak@tieto.com>
Signed-off-by: default avatarMichal Kazior <michal.kazior@tieto.com>
Signed-off-by: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent 9702c686
...@@ -310,6 +310,11 @@ enum ath10k_fw_features { ...@@ -310,6 +310,11 @@ enum ath10k_fw_features {
ATH10K_FW_FEATURE_COUNT, ATH10K_FW_FEATURE_COUNT,
}; };
enum ath10k_dev_flags {
/* Indicates that ath10k device is during CAC phase of DFS */
ATH10K_CAC_RUNNING,
};
struct ath10k { struct ath10k {
struct ath_common ath_common; struct ath_common ath_common;
struct ieee80211_hw *hw; struct ieee80211_hw *hw;
...@@ -403,6 +408,7 @@ struct ath10k { ...@@ -403,6 +408,7 @@ struct ath10k {
bool monitor_enabled; bool monitor_enabled;
bool monitor_present; bool monitor_present;
unsigned int filter_flags; unsigned int filter_flags;
unsigned long dev_flags;
struct wmi_pdev_set_wmm_params_arg wmm_params; struct wmi_pdev_set_wmm_params_arg wmm_params;
struct completion install_key_done; struct completion install_key_done;
......
...@@ -945,6 +945,11 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, ...@@ -945,6 +945,11 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
continue; 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. /* FIXME: we do not support chaining yet.
* this needs investigation */ * this needs investigation */
if (msdu_chaining) { if (msdu_chaining) {
......
...@@ -463,6 +463,10 @@ static int ath10k_vdev_start(struct ath10k_vif *arvif) ...@@ -463,6 +463,10 @@ static int ath10k_vdev_start(struct ath10k_vif *arvif)
arg.ssid = arvif->u.ap.ssid; arg.ssid = arvif->u.ap.ssid;
arg.ssid_len = arvif->u.ap.ssid_len; arg.ssid_len = arvif->u.ap.ssid_len;
arg.hidden_ssid = arvif->u.ap.hidden_ssid; 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) { } else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
arg.ssid = arvif->vif->bss_conf.ssid; arg.ssid = arvif->vif->bss_conf.ssid;
arg.ssid_len = arvif->vif->bss_conf.ssid_len; arg.ssid_len = arvif->vif->bss_conf.ssid_len;
...@@ -532,6 +536,8 @@ static int ath10k_monitor_start(struct ath10k *ar, int vdev_id) ...@@ -532,6 +536,8 @@ static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
/* TODO setup this dynamically, what in case we /* TODO setup this dynamically, what in case we
don't have any vifs? */ don't have any vifs? */
arg.channel.mode = chan_to_phymode(&ar->hw->conf.chandef); 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.min_power = 0;
arg.channel.max_power = channel->max_power * 2; arg.channel.max_power = channel->max_power * 2;
...@@ -666,6 +672,107 @@ static int ath10k_monitor_destroy(struct ath10k *ar) ...@@ -666,6 +672,107 @@ static int ath10k_monitor_destroy(struct ath10k *ar)
return ret; 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, static void ath10k_control_beaconing(struct ath10k_vif *arvif,
struct ieee80211_bss_conf *info) struct ieee80211_bss_conf *info)
{ {
...@@ -1375,6 +1482,9 @@ static int ath10k_update_channel_list(struct ath10k *ar) ...@@ -1375,6 +1482,9 @@ static int ath10k_update_channel_list(struct ath10k *ar)
ch->ht40plus = ch->ht40plus =
!(channel->flags & IEEE80211_CHAN_NO_HT40PLUS); !(channel->flags & IEEE80211_CHAN_NO_HT40PLUS);
ch->chan_radar =
!!(channel->flags & IEEE80211_CHAN_RADAR);
passive = channel->flags & IEEE80211_CHAN_PASSIVE_SCAN; passive = channel->flags & IEEE80211_CHAN_PASSIVE_SCAN;
ch->passive = passive; ch->passive = passive;
...@@ -1921,6 +2031,7 @@ void ath10k_halt(struct ath10k *ar) ...@@ -1921,6 +2031,7 @@ void ath10k_halt(struct ath10k *ar)
{ {
lockdep_assert_held(&ar->conf_mutex); lockdep_assert_held(&ar->conf_mutex);
ath10k_stop_cac(ar);
del_timer_sync(&ar->scan.timeout); del_timer_sync(&ar->scan.timeout);
ath10k_offchan_tx_purge(ar); ath10k_offchan_tx_purge(ar);
ath10k_mgmt_over_wmi_tx_purge(ar); ath10k_mgmt_over_wmi_tx_purge(ar);
...@@ -2035,11 +2146,16 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed) ...@@ -2035,11 +2146,16 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
mutex_lock(&ar->conf_mutex); mutex_lock(&ar->conf_mutex);
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
ath10k_dbg(ATH10K_DBG_MAC, "mac config channel %d mhz\n", ath10k_dbg(ATH10K_DBG_MAC,
conf->chandef.chan->center_freq); "mac config channel %d mhz flags 0x%x\n",
conf->chandef.chan->center_freq,
conf->chandef.chan->flags);
spin_lock_bh(&ar->data_lock); spin_lock_bh(&ar->data_lock);
ar->rx_channel = conf->chandef.chan; ar->rx_channel = conf->chandef.chan;
spin_unlock_bh(&ar->data_lock); spin_unlock_bh(&ar->data_lock);
ath10k_config_radar_detection(ar);
} }
if (changed & IEEE80211_CONF_CHANGE_POWER) { if (changed & IEEE80211_CONF_CHANGE_POWER) {
...@@ -3308,12 +3424,36 @@ static const struct ieee80211_iface_limit ath10k_if_limits[] = { ...@@ -3308,12 +3424,36 @@ static const struct ieee80211_iface_limit ath10k_if_limits[] = {
}, },
}; };
static const struct ieee80211_iface_combination ath10k_if_comb = { #ifdef CONFIG_ATH10K_DFS_CERTIFIED
.limits = ath10k_if_limits, static const struct ieee80211_iface_limit ath10k_if_dfs_limits[] = {
.n_limits = ARRAY_SIZE(ath10k_if_limits), {
.max_interfaces = 8, .max = 8,
.num_different_channels = 1, .types = BIT(NL80211_IFTYPE_AP)
.beacon_int_infra_match = true, },
};
#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) static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar)
...@@ -3537,8 +3677,8 @@ int ath10k_mac_register(struct ath10k *ar) ...@@ -3537,8 +3677,8 @@ int ath10k_mac_register(struct ath10k *ar)
*/ */
ar->hw->queues = 4; ar->hw->queues = 4;
ar->hw->wiphy->iface_combinations = &ath10k_if_comb; ar->hw->wiphy->iface_combinations = ath10k_if_comb;
ar->hw->wiphy->n_iface_combinations = 1; ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ath10k_if_comb);
ar->hw->netdev_features = NETIF_F_HW_CSUM; ar->hw->netdev_features = NETIF_F_HW_CSUM;
......
...@@ -907,6 +907,11 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) ...@@ -907,6 +907,11 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
ath10k_dbg(ATH10K_DBG_MGMT, ath10k_dbg(ATH10K_DBG_MGMT,
"event mgmt rx status %08x\n", rx_status); "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) { if (rx_status & WMI_RX_STATUS_ERR_DECRYPT) {
dev_kfree_skb(skb); dev_kfree_skb(skb);
return 0; return 0;
...@@ -2302,6 +2307,7 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar, ...@@ -2302,6 +2307,7 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
{ {
struct wmi_set_channel_cmd *cmd; struct wmi_set_channel_cmd *cmd;
struct sk_buff *skb; struct sk_buff *skb;
u32 ch_flags = 0;
if (arg->passive) if (arg->passive)
return -EINVAL; return -EINVAL;
...@@ -2310,10 +2316,14 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar, ...@@ -2310,10 +2316,14 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
if (!skb) if (!skb)
return -ENOMEM; return -ENOMEM;
if (arg->chan_radar)
ch_flags |= WMI_CHAN_FLAG_DFS;
cmd = (struct wmi_set_channel_cmd *)skb->data; cmd = (struct wmi_set_channel_cmd *)skb->data;
cmd->chan.mhz = __cpu_to_le32(arg->freq); cmd->chan.mhz = __cpu_to_le32(arg->freq);
cmd->chan.band_center_freq1 = __cpu_to_le32(arg->freq); cmd->chan.band_center_freq1 = __cpu_to_le32(arg->freq);
cmd->chan.mode = arg->mode; cmd->chan.mode = arg->mode;
cmd->chan.flags |= __cpu_to_le32(ch_flags);
cmd->chan.min_power = arg->min_power; cmd->chan.min_power = arg->min_power;
cmd->chan.max_power = arg->max_power; cmd->chan.max_power = arg->max_power;
cmd->chan.reg_power = arg->max_reg_power; cmd->chan.reg_power = arg->max_reg_power;
...@@ -2862,6 +2872,7 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, ...@@ -2862,6 +2872,7 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
struct sk_buff *skb; struct sk_buff *skb;
const char *cmdname; const char *cmdname;
u32 flags = 0; u32 flags = 0;
u32 ch_flags = 0;
if (cmd_id != ar->wmi.cmd->vdev_start_request_cmdid && if (cmd_id != ar->wmi.cmd->vdev_start_request_cmdid &&
cmd_id != ar->wmi.cmd->vdev_restart_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, ...@@ -2888,6 +2899,8 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
flags |= WMI_VDEV_START_HIDDEN_SSID; flags |= WMI_VDEV_START_HIDDEN_SSID;
if (arg->pmf_enabled) if (arg->pmf_enabled)
flags |= WMI_VDEV_START_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 = (struct wmi_vdev_start_request_cmd *)skb->data;
cmd->vdev_id = __cpu_to_le32(arg->vdev_id); cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
...@@ -2909,6 +2922,7 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, ...@@ -2909,6 +2922,7 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
__cpu_to_le32(arg->channel.band_center_freq1); __cpu_to_le32(arg->channel.band_center_freq1);
cmd->chan.mode = arg->channel.mode; cmd->chan.mode = arg->channel.mode;
cmd->chan.flags |= __cpu_to_le32(ch_flags);
cmd->chan.min_power = arg->channel.min_power; cmd->chan.min_power = arg->channel.min_power;
cmd->chan.max_power = arg->channel.max_power; cmd->chan.max_power = arg->channel.max_power;
cmd->chan.reg_power = arg->channel.max_reg_power; cmd->chan.reg_power = arg->channel.max_reg_power;
...@@ -2916,9 +2930,10 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, ...@@ -2916,9 +2930,10 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar,
cmd->chan.antenna_max = arg->channel.max_antenna_gain; cmd->chan.antenna_max = arg->channel.max_antenna_gain;
ath10k_dbg(ATH10K_DBG_WMI, ath10k_dbg(ATH10K_DBG_WMI,
"wmi vdev %s id 0x%x freq %d, mode %d, ch_flags: 0x%0X," "wmi vdev %s id 0x%x flags: 0x%0X, freq %d, mode %d, "
"max_power: %d\n", cmdname, arg->vdev_id, arg->channel.freq, "ch_flags: 0x%0X, max_power: %d\n", cmdname, arg->vdev_id,
arg->channel.mode, flags, arg->channel.max_power); flags, arg->channel.freq, arg->channel.mode,
cmd->chan.flags, arg->channel.max_power);
return ath10k_wmi_cmd_send(ar, skb, cmd_id); return ath10k_wmi_cmd_send(ar, skb, cmd_id);
} }
...@@ -3252,6 +3267,8 @@ int ath10k_wmi_scan_chan_list(struct ath10k *ar, ...@@ -3252,6 +3267,8 @@ int ath10k_wmi_scan_chan_list(struct ath10k *ar,
flags |= WMI_CHAN_FLAG_ALLOW_VHT; flags |= WMI_CHAN_FLAG_ALLOW_VHT;
if (ch->ht40plus) if (ch->ht40plus)
flags |= WMI_CHAN_FLAG_HT40_PLUS; flags |= WMI_CHAN_FLAG_HT40_PLUS;
if (ch->chan_radar)
flags |= WMI_CHAN_FLAG_DFS;
ci->mhz = __cpu_to_le32(ch->freq); ci->mhz = __cpu_to_le32(ch->freq);
ci->band_center_freq1 = __cpu_to_le32(ch->freq); ci->band_center_freq1 = __cpu_to_le32(ch->freq);
......
...@@ -916,6 +916,7 @@ struct wmi_channel_arg { ...@@ -916,6 +916,7 @@ struct wmi_channel_arg {
bool allow_ht; bool allow_ht;
bool allow_vht; bool allow_vht;
bool ht40plus; bool ht40plus;
bool chan_radar;
/* note: power unit is 0.5 dBm */ /* note: power unit is 0.5 dBm */
u32 min_power; u32 min_power;
u32 max_power; u32 max_power;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment