blob: 3237aa343b5b2f42e0482e529ec20660bd6cac78 [file] [log] [blame]
From 4e7c861c971ca49ea162bd908fef6021a62b9018 Mon Sep 17 00:00:00 2001
From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
Date: Fri, 7 Oct 2022 10:46:29 +0800
Subject: [PATCH 10/15] hostapd: mtk: Add DFS and ZWDFS support
Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
---
hostapd/config_file.c | 4 ++
hostapd/ctrl_iface.c | 95 ++++++++++++++++++++++++++++++++++++
src/ap/ap_config.h | 13 +++++
src/ap/dfs.c | 35 +++++++------
src/ap/dfs.h | 15 ++++++
src/ap/hostapd.c | 4 +-
src/drivers/driver.h | 7 +++
src/drivers/driver_nl80211.c | 29 +++++++++++
src/drivers/nl80211_copy.h | 1 +
9 files changed, 186 insertions(+), 17 deletions(-)
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 63c9d40be..cd1534952 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -4803,6 +4803,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "ibf_enable") == 0) { /*ibf setting is per device*/
int val = atoi(pos);
conf->ibf_enable = !!val;
+ } else if (os_strcmp(buf, "dfs_detect_mode") == 0) { /*bypass channel switch*/
+ u8 en = strtol(pos, NULL, 10);
+
+ conf->dfs_detect_mode = en;
} else {
wpa_printf(MSG_ERROR,
"Line %d: unknown configuration item '%s'",
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index c881d3717..6ea1573b8 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -3522,6 +3522,96 @@ hostapd_ctrl_iface_get_ibf(struct hostapd_data *hapd, char *buf,
}
+static int
+hostapd_ctrl_iface_set_dfs_detect_mode(struct hostapd_data *hapd, char *value,
+ char *buf, size_t buflen)
+{
+ u8 dfs_detect_mode;
+
+ if (!value)
+ return -1;
+
+ dfs_detect_mode = strtol(value, NULL, 10);
+ if (dfs_detect_mode > DFS_DETECT_MODE_MAX) {
+ wpa_printf(MSG_ERROR, "Invalid value for dfs detect mode");
+ return -1;
+ }
+ hapd->iconf->dfs_detect_mode = dfs_detect_mode;
+
+ return os_snprintf(buf, buflen, "OK\n");
+}
+
+
+static int
+hostapd_ctrl_iface_set_offchan_ctrl(struct hostapd_data *hapd, char *cmd,
+ char *buf, size_t buflen)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ char *pos, *param;
+ enum hostapd_hw_mode hw_mode;
+ bool chan_found = false;
+ int i, num_available_chandefs, channel, chan_width, sec = 0;
+ int sec_chan_idx_80p80 = -1;
+ u8 oper_centr_freq_seg0_idx, oper_centr_freq_seg1_idx;
+ struct hostapd_channel_data *chan;
+ enum dfs_channel_type type = DFS_NO_CAC_YET;
+
+ param = os_strchr(cmd, ' ');
+ if (!param)
+ return -1;
+ *param++ = '\0';
+
+ pos = os_strstr(param, "chan=");
+ if (pos)
+ channel = strtol(pos + 5, NULL, 10);
+ else
+ return -1;
+
+ num_available_chandefs = dfs_find_channel(iface, NULL, 0, type);
+ for (i = 0; i < num_available_chandefs; i++) {
+ dfs_find_channel(iface, &chan, i, type);
+ if (chan->chan == channel) {
+ chan_found = true;
+ break;
+ }
+ }
+
+ if (!chan_found)
+ return -1;
+
+ if (iface->conf->secondary_channel)
+ sec = 1;
+
+ dfs_adjust_center_freq(iface, chan,
+ sec,
+ sec_chan_idx_80p80,
+ &oper_centr_freq_seg0_idx,
+ &oper_centr_freq_seg1_idx);
+
+ if (hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
+ chan->freq, chan->chan,
+ iface->conf->ieee80211n,
+ iface->conf->ieee80211ac,
+ iface->conf->ieee80211ax,
+ iface->conf->ieee80211be,
+ sec, hostapd_get_oper_chwidth(iface->conf),
+ oper_centr_freq_seg0_idx,
+ oper_centr_freq_seg1_idx, true)) {
+ wpa_printf(MSG_ERROR, "DFS failed to start CAC offchannel");
+ iface->radar_background.channel = -1;
+ return -1;
+ }
+
+ iface->radar_background.channel = chan->chan;
+ iface->radar_background.freq = chan->freq;
+ iface->radar_background.secondary_channel = sec;
+ iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+ iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
+
+ return os_snprintf(buf, buflen, "OK\n");
+}
+
+
static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
char *buf, char *reply,
int reply_size,
@@ -4081,6 +4171,11 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
reply_len = hostapd_ctrl_iface_get_mu(hapd, reply, reply_size);
} else if (os_strncmp(buf, "GET_IBF", 7) == 0) {
reply_len = hostapd_ctrl_iface_get_ibf(hapd, reply, reply_size);
+ } else if (os_strncmp(buf, "DFS_DETECT_MODE ", 16) == 0) {
+ reply_len = hostapd_ctrl_iface_set_dfs_detect_mode(hapd, buf + 16,
+ reply, reply_size);
+ } else if (os_strncmp(buf, "SET_OFFCHAN_CTRL", 16) == 0) {
+ reply_len = hostapd_ctrl_iface_set_offchan_ctrl(hapd, buf + 16, reply, reply_size);
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index a9e3d2aeb..77f6169d2 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -1160,6 +1160,7 @@ struct hostapd_config {
int *edcca_threshold;
u8 three_wire_enable;
u8 ibf_enable;
+ u8 dfs_detect_mode;
};
enum three_wire_mode {
@@ -1174,6 +1175,18 @@ enum three_wire_mode {
NUM_THREE_WIRE_MODE - 1
};
+enum dfs_mode {
+ DFS_DETECT_MODE_DISABLE,
+ DFS_DETECT_MODE_AP_ENABLE,
+ DFS_DETECT_MODE_BACKGROUND_ENABLE,
+ DFS_DETECT_MODE_ALL_ENABLE,
+
+ /* keep last */
+ NUM_DFS_DETECT_MODE,
+ DFS_DETECT_MODE_MAX =
+ NUM_DFS_DETECT_MODE - 1
+};
+
enum edcca_mode {
EDCCA_MODE_FORCE_DISABLE = 0,
EDCCA_MODE_AUTO = 1,
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index b5d105d6a..1c3f6785b 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -19,13 +19,6 @@
#include "dfs.h"
#include "crypto/crypto.h"
-
-enum dfs_channel_type {
- DFS_ANY_CHANNEL,
- DFS_AVAILABLE, /* non-radar or radar-available */
- DFS_NO_CAC_YET, /* radar-not-yet-available */
-};
-
static struct hostapd_channel_data *
dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
u8 *oper_centr_freq_seg0_idx,
@@ -238,9 +231,9 @@ static int is_in_chanlist(struct hostapd_iface *iface,
* - hapd->vht/he_oper_centr_freq_seg0_idx
* - hapd->vht/he_oper_centr_freq_seg1_idx
*/
-static int dfs_find_channel(struct hostapd_iface *iface,
- struct hostapd_channel_data **ret_chan,
- int idx, enum dfs_channel_type type)
+int dfs_find_channel(struct hostapd_iface *iface,
+ struct hostapd_channel_data **ret_chan,
+ int idx, enum dfs_channel_type type)
{
struct hostapd_hw_modes *mode;
struct hostapd_channel_data *chan;
@@ -299,12 +292,12 @@ static int dfs_find_channel(struct hostapd_iface *iface,
}
-static void dfs_adjust_center_freq(struct hostapd_iface *iface,
- struct hostapd_channel_data *chan,
- int secondary_channel,
- int sec_chan_idx_80p80,
- u8 *oper_centr_freq_seg0_idx,
- u8 *oper_centr_freq_seg1_idx)
+void dfs_adjust_center_freq(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan,
+ int secondary_channel,
+ int sec_chan_idx_80p80,
+ u8 *oper_centr_freq_seg0_idx,
+ u8 *oper_centr_freq_seg1_idx)
{
if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax)
return;
@@ -1317,6 +1310,11 @@ hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface,
__func__, iface->radar_background.cac_started ? "yes" : "no",
hostapd_csa_in_progress(iface) ? "yes" : "no");
+ /* Skip channel switch when background dfs detect mode is on */
+ if (iface->conf->dfs_detect_mode == DFS_DETECT_MODE_BACKGROUND_ENABLE ||
+ iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
+ return 0;
+
/* Check if CSA in progress */
if (hostapd_csa_in_progress(iface))
return 0;
@@ -1365,6 +1363,11 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
__func__, iface->cac_started ? "yes" : "no",
hostapd_csa_in_progress(iface) ? "yes" : "no");
+ /* Skip channel switch when dfs detect mode is on */
+ if (iface->conf->dfs_detect_mode == DFS_DETECT_MODE_AP_ENABLE ||
+ iface->conf->dfs_detect_mode == DFS_DETECT_MODE_ALL_ENABLE)
+ return 0;
+
/* Check if CSA in progress */
if (hostapd_csa_in_progress(iface))
return 0;
diff --git a/src/ap/dfs.h b/src/ap/dfs.h
index 606c1b393..c2556d2d9 100644
--- a/src/ap/dfs.h
+++ b/src/ap/dfs.h
@@ -9,6 +9,12 @@
#ifndef DFS_H
#define DFS_H
+enum dfs_channel_type {
+ DFS_ANY_CHANNEL,
+ DFS_AVAILABLE, /* non-radar or radar-available */
+ DFS_NO_CAC_YET, /* radar-not-yet-available */
+};
+
int hostapd_handle_dfs(struct hostapd_iface *iface);
int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
@@ -32,5 +38,14 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
int hostapd_handle_dfs_offload(struct hostapd_iface *iface);
int hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
int center_freq);
+int dfs_find_channel(struct hostapd_iface *iface,
+ struct hostapd_channel_data **ret_chan,
+ int idx, enum dfs_channel_type type);
+void dfs_adjust_center_freq(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan,
+ int secondary_channel,
+ int sec_chan_idx_80p80,
+ u8 *oper_centr_freq_seg0_idx,
+ u8 *oper_centr_freq_seg1_idx);
#endif /* DFS_H */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index da7f7d87b..a0229c9ca 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -1464,7 +1464,9 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
return -1;
}
- if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
+ if (conf->start_disabled)
+ hapd->driver->start_disabled(hapd->drv_priv);
+ else if (ieee802_11_set_beacon(hapd) < 0)
return -1;
if (flush_old_stations && !conf->start_disabled &&
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 71ded617f..aa23fbdb3 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -4720,6 +4720,13 @@ struct wpa_driver_ops {
*
*/
int (*ibf_dump)(void *priv, u8 *ibf_enable);
+
+ /**
+ * start_disabled - set start_disabled to cfg80211
+ * @priv: Private driver interface data
+ *
+ */
+ int (*start_disabled)(void *priv);
};
/**
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 5c2a291ca..7472542cc 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -12776,6 +12776,34 @@ fail:
return -ENOBUFS;
}
+static int nl80211_start_disabled(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ struct nlattr *data;
+ int ret;
+
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_NEW_BEACON);
+ if (!msg)
+ goto fail;
+
+ if (nla_put_flag(msg, NL80211_ATTR_START_DISABLED))
+ goto fail;
+
+ ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 1);
+
+ if (ret)
+ wpa_printf(MSG_ERROR, "Failed to set start_disabled. ret=%d (%s)",
+ ret, strerror(-ret));
+
+ return ret;
+
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
@@ -12930,4 +12958,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.three_wire_ctrl = nl80211_enable_three_wire,
.ibf_ctrl = nl80211_ibf_enable,
.ibf_dump = nl80211_ibf_dump,
+ .start_disabled = nl80211_start_disabled,
};
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index c4bf3ad35..79bc76c57 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -3176,6 +3176,7 @@ enum nl80211_attrs {
NL80211_ATTR_EHT_CAPABILITY,
/* add attributes here, update the policy in nl80211.c */
+ NL80211_ATTR_START_DISABLED = 999,
__NL80211_ATTR_AFTER_LAST,
NUM_NL80211_ATTR = __NL80211_ATTR_AFTER_LAST,
--
2.25.1