blob: 357b17ef3fe50146a12d809a8f3154ba75f983e0 [file] [log] [blame]
From 330346c577b4623e52767ad0939cd735a83d9837 Mon Sep 17 00:00:00 2001
From: Allen Ye <allen.ye@mediatek.com>
Date: Mon, 22 Jul 2024 20:51:18 +0800
Subject: [PATCH 124/126] mtk: hostapd: Add AFC and lpi driver power support
Add AFC and lpi driver power support
This patch parse the AFC response into mtk sku power table format and send
it to driver by vendor cmd. The table format is like below:
col\row bw20 bw40 ... ru26 ... ru3472
chan 1
chan 5
...
chan 233
- Once the afc procedure start failed, the device would set as lpi mode by
telling driver lpi_sku_index to use specify sku-index in dst.
- Add afc_max_timeout conf is use to limit the maximum interval between the
two afc requests.
- Set default use lpi & sp mode
Signed-off-by: Allen Ye <allen.ye@mediatek.com>
---
hostapd/config_file.c | 4 +
src/ap/afc.c | 365 +++++++++++++++++++++++++++++++++--
src/ap/ap_config.c | 3 +-
src/ap/ap_config.h | 2 +
src/ap/ap_drv_ops.c | 34 +++-
src/ap/hostapd.h | 60 ++++++
src/common/mtk_vendor.h | 2 +
src/drivers/driver.h | 4 +-
src/drivers/driver_nl80211.c | 36 +++-
9 files changed, 477 insertions(+), 33 deletions(-)
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 944669270..11c5f8947 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -4248,6 +4248,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "afc_op_class") == 0) {
if (hostapd_afc_parse_op_class(conf, pos))
return 1;
+ } else if (os_strcmp(buf, "afc_max_timeout") == 0) {
+ conf->afc.max_timeout = atoi(pos);
#endif /* CONFIG_AFC */
} else if (os_strcmp(buf, "mbssid") == 0) {
int mbssid = atoi(pos);
@@ -5575,6 +5577,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
conf->lpi_psd = !!en;
} else if (os_strcmp(buf, "sku_idx") == 0) {
conf->sku_idx = strtol(pos, NULL, 10);
+ } else if (os_strcmp(buf, "lpi_sku_idx") == 0) {
+ conf->lpi_sku_idx = strtol(pos, NULL, 10);
} else if (os_strcmp(buf, "lpi_bcn_enhance") == 0) {
u8 en = strtol(pos, NULL, 10);
conf->lpi_bcn_enhance = !!en;
diff --git a/src/ap/afc.c b/src/ap/afc.c
index 361ecb575..d36ce00d7 100644
--- a/src/ap/afc.c
+++ b/src/ap/afc.c
@@ -16,6 +16,7 @@
#include "hostapd.h"
#include "acs.h"
#include "hw_features.h"
+#include "ap_drv_ops.h"
#define HOSTAPD_AFC_RETRY_TIMEOUT 180
#define HOSTAPD_AFC_TIMEOUT 86400 /* 24h */
@@ -749,6 +750,9 @@ static int hostapd_afc_parse_reply(struct hostapd_iface *iface, char *reply)
iface->afc.timeout = request_timeout;
if (iface->afc.timeout < 0)
iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
+ else if (iface->afc.timeout > iconf->afc.max_timeout &&
+ iconf->afc.max_timeout >= HOSTAPD_AFC_RETRY_TIMEOUT)
+ iface->afc.timeout = iconf->afc.max_timeout;
return ret;
}
@@ -772,6 +776,7 @@ static int hostapd_afc_send_receive(struct hostapd_iface *iface)
int sockfd, ret;
fd_set read_set;
+ iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
if (iface->afc.data_valid) {
/* AFC data already downloaded from the server */
return 0;
@@ -782,7 +787,6 @@ static int hostapd_afc_send_receive(struct hostapd_iface *iface)
return -EINVAL;
}
- iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT;
if (os_strlen(iconf->afc.socket) >= sizeof(addr.sun_path)) {
wpa_printf(MSG_ERROR, "Malformed AFC socket string %s",
iconf->afc.socket);
@@ -880,7 +884,19 @@ static bool hostapd_afc_has_usable_chans(struct hostapd_iface *iface)
int hostapd_afc_handle_request(struct hostapd_iface *iface)
{
struct hostapd_config *iconf = iface->conf;
+ bool lpi_mode;
int ret;
+ int afc_status = AFC_CONTINUE;
+
+ lpi_mode = he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type);
+ if (lpi_mode && !he_reg_is_sp(iconf->he_6ghz_reg_pwr_type)) {
+ iface->afc.lpi_mode = true;
+ return 1;
+ }
+
+ if (strncmp(iconf->country, "US", 2) != 0 &&
+ strncmp(iconf->country, "CA", 2) != 0)
+ return 1;
/* AFC is required just for standard power AP */
if (!he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
@@ -894,34 +910,50 @@ int hostapd_afc_handle_request(struct hostapd_iface *iface)
ret = hostapd_afc_send_receive(iface);
if (ret < 0) {
- /*
- * If the connection to the AFCD failed, resched for a
- * future attempt.
- */
- wpa_printf(MSG_ERROR, "AFC connection failed: %d", ret);
- if (ret == -EIO)
- ret = 0;
+ afc_status = lpi_mode ? AFC_LPI : AFC_DISABLE;
goto resched;
}
hostap_afc_disable_channels(iface);
- if (!hostapd_afc_has_usable_chans(iface))
+ if (!hostapd_afc_has_usable_chans(iface)) {
+ afc_status = lpi_mode ? AFC_LPI : AFC_DISABLE;
goto resched;
+ }
if (!hostapd_is_usable_chans(iface)) {
/* Trigger an ACS freq scan */
+ afc_status = AFC_RESTART_IFACE;
iconf->channel = 0;
iface->freq = 0;
if (acs_init(iface) != HOSTAPD_CHAN_ACS) {
wpa_printf(MSG_ERROR, "Could not start ACS");
+ afc_status = AFC_DISABLE;
ret = -EINVAL;
}
} else {
+ afc_status = AFC_CONTINUE;
ret = 1;
}
resched:
+ switch(afc_status) {
+ case AFC_LPI:
+ iface->afc.lpi_mode = true;
+ hostapd_afc_enable_lpi_channels(iface);
+ ret = 1;
+ break;
+ /* Disable and restart iface would be finished in hostapd setup flow. */
+ case AFC_RESTART_IFACE:
+ ret = 0;
+ fallthrough;
+ case AFC_DISABLE:
+ case AFC_CONTINUE:
+ break;
+ default:
+ break;
+ }
+
eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL);
eloop_register_timeout(iface->afc.timeout, 0,
hostapd_afc_timeout_handler, iface, NULL);
@@ -948,34 +980,58 @@ static void hostapd_afc_delete_data_from_server(struct hostapd_iface *iface)
static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx)
{
struct hostapd_iface *iface = eloop_ctx;
- bool restart_iface = true;
+ bool lpi_mode;
+ int afc_status = AFC_CONTINUE, ret;
+
+ lpi_mode = he_reg_is_indoor(iface->conf->he_6ghz_reg_pwr_type);
+ iface->afc.lpi_mode = false;
hostapd_afc_delete_data_from_server(iface);
if (iface->state != HAPD_IFACE_ENABLED) {
+ afc_status = AFC_RESTART_IFACE;
/* Hostapd is not fully enabled yet, toggle the interface */
goto restart_interface;
}
if (hostapd_afc_send_receive(iface) < 0 ||
hostapd_get_hw_features(iface)) {
- restart_iface = false;
+ afc_status = lpi_mode ? AFC_LPI : AFC_DISABLE;
goto restart_interface;
}
- if (hostapd_is_usable_chans(iface))
- goto resched;
+ ret = hostapd_is_usable_chans(iface);
+ if (ret != 1) {
+ afc_status = lpi_mode && ret == 0 ? AFC_LPI : AFC_DISABLE;
+ goto restart_interface;
+ }
- restart_iface = hostapd_afc_has_usable_chans(iface);
- if (restart_iface) {
+ ret = hostapd_afc_has_usable_chans(iface);
+ if (ret) {
/* Trigger an ACS freq scan */
+ afc_status = AFC_RESTART_IFACE;
iface->conf->channel = 0;
iface->freq = 0;
}
restart_interface:
- hostapd_disable_iface(iface);
- if (restart_iface)
+ switch(afc_status) {
+ case AFC_DISABLE:
+ hostapd_disable_iface(iface);
+ break;
+ case AFC_RESTART_IFACE:
+ hostapd_disable_iface(iface);
hostapd_enable_iface(iface);
+ break;
+ case AFC_LPI:
+ iface->afc.lpi_mode = true;
+ hostapd_afc_enable_lpi_channels(iface);
+ hostapd_drv_txpower_ctrl(iface->bss[0]);
+ break;
+ case AFC_CONTINUE:
+ break;
+ default:
+ break;
+ }
resched:
eloop_register_timeout(iface->afc.timeout, 0,
hostapd_afc_timeout_handler, iface, NULL);
@@ -1040,6 +1096,37 @@ void hostap_afc_disable_channels(struct hostapd_iface *iface)
}
}
+void hostapd_afc_enable_lpi_channels(struct hostapd_iface *iface)
+{
+ struct hostapd_hw_modes *mode = NULL;
+ int i;
+
+ for (i = 0; i < iface->num_hw_features; i++) {
+ mode = &iface->hw_features[i];
+ if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
+ mode->is_6ghz)
+ break;
+ }
+
+ if (i == iface->num_hw_features)
+ return;
+
+ if (!he_reg_is_indoor(iface->conf->he_6ghz_reg_pwr_type))
+ return;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ struct hostapd_channel_data *chan = &mode->channels[i];
+
+ if (!is_6ghz_freq(chan->freq))
+ continue;
+
+ chan->flag &= ~HOSTAPD_CHAN_DISABLED;
+ wpa_printf(MSG_MSGDUMP,
+ "Enabling freq=%d MHz for lpi mode",
+ chan->freq);
+ }
+}
+
int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
int *power)
@@ -1076,3 +1163,247 @@ int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
}
return -EINVAL;
}
+
+void hostapd_afc_init_power_table(s8 ***power_table)
+{
+ int table_idx, bw;
+ s8 *chan_power_list;
+
+ /* init power table */
+ for (table_idx = 0; table_idx < MAX_CHANNEL_NUM_6G; table_idx++) {
+ chan_power_list = (*power_table)[table_idx];
+ for (bw = 0; bw < afc_power_table_num; bw++)
+ chan_power_list[bw] = AFC_INVALID_POWER;
+ }
+}
+
+int hostapd_afc_parse_psd_to_dbm(struct hostapd_iface *iface, s8 ***power_table)
+{
+ int i, freq, channel, bw, table_idx, target_power;
+ s8 *chan_power_list;
+
+ for (i = 0; i < iface->afc.num_freq_range; i++) {
+ struct afc_freq_range_elem *freq_range = &iface->afc.freq_range[i];
+
+ if (!freq_range)
+ continue;
+
+ freq = freq_range->low_freq + 10;
+ channel = hostapd_hw_get_channel(iface->bss[0], freq);
+ if (channel == 0)
+ return -EINVAL;
+
+ table_idx = channel / 4;
+
+ if (table_idx >= MAX_CHANNEL_NUM_6G)
+ return -EINVAL;
+
+ chan_power_list = (*power_table)[table_idx];
+ for (bw = 0; bw < afc_power_bw320_2; bw++) {
+ target_power = freq_range->max_psd * 2 + PSD_TO_DBM_OFFSET +
+ bw * DOUBLE_BW_POWER;
+ target_power = MIN(AFC_MAXIMUM_POWER, target_power);
+ chan_power_list[bw] = MIN(chan_power_list[bw],
+ target_power);
+ }
+ chan_power_list[afc_power_bw320_2] = chan_power_list[afc_power_bw320_1];
+ }
+ return 0;
+}
+
+int hostapd_afc_parse_eirp_to_dbm(struct hostapd_iface *iface, s8 ***power_table)
+{
+ int i, bw, table_idx, target_power;
+ s8 *chan_power_list;
+
+ for (i = 0; i < iface->afc.num_chan_info; i++) {
+ struct afc_chan_info_elem *chan_info = &iface->afc.chan_info_list[i];
+
+ if (!chan_info)
+ continue;
+
+ table_idx = chan_info->chan / 4;
+
+ if (table_idx >= MAX_CHANNEL_NUM_6G)
+ return -EINVAL;
+
+ chan_power_list = (*power_table)[table_idx];
+ target_power = MIN(AFC_MAXIMUM_POWER, chan_info->power * 2);
+ /* FIXME: wider bandwidth power is not stored. */
+ chan_power_list[afc_power_bw20] = MIN(chan_power_list[afc_power_bw20],
+ target_power);
+ }
+ return 0;
+}
+
+int afc_get_ru_be_offset(int bw, int *target_bw, int *offset)
+{
+ switch (bw) {
+ case afc_power_ru26:
+ *target_bw = afc_power_bw20;
+ *offset = RU26_OFFSET_20MHZ;
+ break;
+ case afc_power_ru52:
+ *target_bw = afc_power_bw20;
+ *offset = RU52_OFFSET_20MHZ;
+ break;
+ case afc_power_ru78:
+ *target_bw = afc_power_bw20;
+ *offset = RU78_OFFSET_20MHZ;
+ break;
+ case afc_power_ru106:
+ *target_bw = afc_power_bw20;
+ *offset = RU106_OFFSET_20MHZ;
+ break;
+ case afc_power_ru132:
+ *target_bw = afc_power_bw20;
+ *offset = RU132_OFFSET_20MHZ;
+ break;
+ case afc_power_ru726:
+ *target_bw = afc_power_bw80;
+ *offset = RU726_OFFSET_80MHZ;
+ break;
+ case afc_power_ru1480:
+ *target_bw = afc_power_bw160;
+ *offset = RU1480_OFFSET_160MHZ;
+ break;
+ case afc_power_ru1772:
+ *target_bw = afc_power_bw160;
+ *offset = RU1772_OFFSET_160MHZ;
+ break;
+ case afc_power_ru2476:
+ *target_bw = afc_power_bw320_1;
+ *offset = RU2476_OFFSET_320MHZ;
+ break;
+ case afc_power_ru2988:
+ *target_bw = afc_power_bw320_1;
+ *offset = RU2988_OFFSET_320MHZ;
+ break;
+ case afc_power_ru3472:
+ *target_bw = afc_power_bw320_1;
+ *offset = RU3472_OFFSET_320MHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int hostapd_afc_fill_wide_bandwidth_power(s8 ***power_table)
+{
+ int table_idx, bw;
+ s8 *chan_power_list;
+
+ for (table_idx = 0; table_idx < MAX_CHANNEL_NUM_6G; table_idx++) {
+ int target_power, ru26_power;
+
+ if ((*power_table)[table_idx][afc_power_bw20] == AFC_INVALID_POWER)
+ continue;
+
+ chan_power_list = (*power_table)[table_idx];
+
+ /* Check wide bandwidth power minimum or valid. */
+ for (bw = afc_power_bw40; bw <= afc_power_bw320_2; bw++) {
+ int bw_ch_num, first_ch, last_ch;
+
+ target_power = AFC_INVALID_POWER;
+ switch (bw) {
+ case afc_power_bw40:
+ bw_ch_num = 2;
+ break;
+ case afc_power_bw80:
+ bw_ch_num = 4;
+ break;
+ case afc_power_bw160:
+ bw_ch_num = 8;
+ break;
+ case afc_power_bw320_1:
+ case afc_power_bw320_2:
+ bw_ch_num = 16;
+ break;
+ }
+ if ((bw == afc_power_bw320_1 && table_idx > 47) ||
+ (bw == afc_power_bw320_2 && table_idx < 8))
+ continue;
+
+ if (bw == afc_power_bw320_2)
+ first_ch = table_idx - (table_idx + 8) % bw_ch_num;
+ else
+ first_ch = table_idx - table_idx % bw_ch_num;
+ last_ch = first_ch + bw_ch_num;
+ for (int ch = first_ch; ch < last_ch; ch++) {
+ if ((*power_table)[ch][bw] == AFC_INVALID_POWER) {
+ target_power = AFC_INVALID_POWER;
+ break;
+ }
+ target_power = MIN((*power_table)[ch][bw], target_power);
+ }
+ chan_power_list[bw] = target_power;
+ }
+
+ /* Update remain ru */
+ for (bw = afc_power_ru26; bw < afc_power_table_num; bw++) {
+ int target_bw, offset;
+
+ if (afc_get_ru_be_offset(bw, &target_bw, &offset))
+ return -EINVAL;
+
+ if (target_bw == afc_power_bw320_1 &&
+ chan_power_list[target_bw] == AFC_INVALID_POWER)
+ target_bw++;
+
+ if (chan_power_list[target_bw] == AFC_INVALID_POWER) {
+ chan_power_list[bw] = AFC_INVALID_POWER;
+ continue;
+ }
+
+ target_power = chan_power_list[target_bw] - offset;
+ chan_power_list[bw] = target_power;
+ }
+ }
+ return 0;
+}
+
+
+int hostapd_afc_translate_table(struct hostapd_iface *iface,
+ s8 ***power_table)
+{
+ int i, ret, bw320_offset;
+
+ if (!iface->afc.data_valid)
+ return -EINVAL;
+
+ *power_table = (s8**)os_zalloc(MAX_CHANNEL_NUM_6G * sizeof(s8*));
+
+ if (!(*power_table))
+ return -ENOMEM;
+
+ for (i = 0; i < MAX_CHANNEL_NUM_6G; i++) {
+ (*power_table)[i] = (s8*)os_zalloc(afc_power_table_num * sizeof(s8));
+ if (!(*power_table)[i])
+ goto out;
+ }
+
+ hostapd_afc_init_power_table(power_table);
+
+ ret = hostapd_afc_parse_psd_to_dbm(iface, power_table);
+ if (ret)
+ goto out;
+
+ ret = hostapd_afc_parse_eirp_to_dbm(iface, power_table);
+ if (ret)
+ goto out;
+
+ ret = hostapd_afc_fill_wide_bandwidth_power(power_table);
+ if (ret)
+ goto out;
+
+ return 0;
+out:
+ for (i = 0; i < MAX_CHANNEL_NUM_6G; i++)
+ os_free((*power_table)[i]);
+
+ os_free(*power_table);
+ power_table = NULL;
+ return -ENOMEM;
+}
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index bb5ec78a1..185dae089 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -288,7 +288,7 @@ struct hostapd_config * hostapd_config_defaults(void)
conf->he_6ghz_max_ampdu_len_exp = 7;
conf->he_6ghz_rx_ant_pat = 1;
conf->he_6ghz_tx_ant_pat = 1;
- conf->he_6ghz_reg_pwr_type = HE_REG_INFO_6GHZ_AP_TYPE_VLP;
+ conf->he_6ghz_reg_pwr_type = HE_REG_INFO_6GHZ_AP_TYPE_INDOOR;
conf->reg_def_cli_eirp_psd = -1;
conf->reg_sub_cli_eirp_psd = -1;
conf->reg_def_cli_eirp = -1;
@@ -317,6 +317,7 @@ struct hostapd_config * hostapd_config_defaults(void)
conf->lpi_psd = 0;
conf->sku_idx = 0;
+ conf->lpi_sku_idx = 0;
conf->lpi_bcn_enhance = 0;
hostapd_set_and_check_bw320_offset(conf, 0);
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index ea8507cea..966a02d6a 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -1343,6 +1343,7 @@ struct hostapd_config {
unsigned int n_op_class;
unsigned int *op_class;
int min_power;
+ int max_timeout;
} afc;
#endif /* CONFIG_AFC */
@@ -1358,6 +1359,7 @@ struct hostapd_config {
u8 band_idx;
u8 lpi_psd;
u8 sku_idx;
+ u8 lpi_sku_idx;
u8 lpi_bcn_enhance;
};
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 64fd0c04c..bd710832a 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -1397,7 +1397,8 @@ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_colo
int hostapd_drv_txpower_ctrl(struct hostapd_data *hapd)
{
- s8 link_id = -1;
+ s8 link_id = -1, sku_idx = hapd->iconf->sku_idx, ret = 0, i;
+ s8 **afc_power_table = NULL;
if (!hapd->driver || !hapd->driver->txpower_ctrl)
return 0;
@@ -1405,10 +1406,33 @@ int hostapd_drv_txpower_ctrl(struct hostapd_data *hapd)
if (hapd->conf->mld_ap)
link_id = hapd->mld_link_id;
- return hapd->driver->txpower_ctrl(hapd->drv_priv, hapd->iconf->lpi_psd,
- hapd->iconf->sku_idx,
- hapd->iconf->lpi_bcn_enhance,
- link_id);
+#ifdef CONFIG_AFC
+ if (hapd->iface->current_mode->is_6ghz &&
+ he_reg_is_sp(hapd->iface->conf->he_6ghz_reg_pwr_type) &&
+ !hapd->iface->afc.lpi_mode) {
+ ret = hostapd_afc_translate_table(hapd->iface, &afc_power_table);
+ if (ret)
+ goto out;
+ }
+
+ if (hapd->iface->afc.lpi_mode == true)
+ sku_idx = hapd->iconf->lpi_sku_idx;
+#endif /* CONFIG_AFC */
+
+ ret = hapd->driver->txpower_ctrl(hapd->drv_priv, hapd->iconf->lpi_psd,
+ sku_idx,
+ hapd->iconf->lpi_bcn_enhance,
+ link_id,
+ afc_power_table,
+ hapd->iface->afc.lpi_mode);
+#ifdef CONFIG_AFC
+out:
+ if (afc_power_table)
+ for (i = 0; i < MAX_CHANNEL_NUM_6G; i++)
+ os_free(afc_power_table[i]);
+ os_free(afc_power_table);
+#endif /* CONFIG_AFC */
+ return ret;
}
int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value)
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index f69fa0062..0625bb762 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -783,17 +783,71 @@ struct hostapd_iface {
int power;
} *chan_info_list;
bool data_valid;
+ bool lpi_mode;
} afc;
#endif /* CONFIG_AFC */
};
/* hostapd.c */
#ifdef CONFIG_AFC
+
+enum afc_state {
+ AFC_DISABLE,
+ AFC_RESTART_IFACE,
+ AFC_LPI,
+ AFC_CONTINUE,
+};
+
+#define MAX_CHANNEL_NUM_6G 59
+
+/* The power unit is 0.5 dBm */
+#define AFC_MAXIMUM_POWER 72
+#define AFC_INVALID_POWER 127
+#define PSD_TO_DBM_OFFSET 26
+#define BW20_TO_RU26_OFFSET 20
+#define DOUBLE_BW_POWER 6
+
+#define RU26_OFFSET_20MHZ 20
+#define RU52_OFFSET_20MHZ 14
+#define RU78_OFFSET_20MHZ 10
+#define RU106_OFFSET_20MHZ 8
+#define RU132_OFFSET_20MHZ 6
+
+#define RU726_OFFSET_80MHZ 2
+#define RU1480_OFFSET_160MHZ 2
+#define RU1772_OFFSET_160MHZ 1
+#define RU2476_OFFSET_320MHZ 4
+#define RU2988_OFFSET_320MHZ 2
+#define RU3472_OFFSET_320MHZ 1
+
+enum afc_table_info {
+ afc_power_bw20,
+ afc_power_bw40,
+ afc_power_bw80,
+ afc_power_bw160,
+ afc_power_bw320_1,
+ afc_power_bw320_2,
+ afc_power_ru26,
+ afc_power_ru52,
+ afc_power_ru78,
+ afc_power_ru106,
+ afc_power_ru132,
+ afc_power_ru726,
+ afc_power_ru1480,
+ afc_power_ru1772,
+ afc_power_ru2476,
+ afc_power_ru2988,
+ afc_power_ru3472,
+ afc_power_table_num,
+};
+
int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
int *power);
int hostapd_afc_handle_request(struct hostapd_iface *iface);
void hostapd_afc_stop(struct hostapd_iface *iface);
void hostap_afc_disable_channels(struct hostapd_iface *iface);
+int hostapd_afc_translate_table(struct hostapd_iface *iface,
+ s8 ***power_table);
#else
static inline int
hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd,
@@ -814,6 +868,12 @@ static inline void hostapd_afc_stop(struct hostapd_iface *iface)
static inline void hostap_afc_disable_channels(struct hostapd_iface *iface)
{
}
+
+int hostapd_afc_translate_table(struct hostapd_iface *iface,
+ s8 ***power_table)
+{
+ return -EINVAL;
+}
#endif /* CONFIG_AFC */
int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h
index 1fe459126..4b900162b 100644
--- a/src/common/mtk_vendor.h
+++ b/src/common/mtk_vendor.h
@@ -319,6 +319,8 @@ enum mtk_vendor_attr_txpower_ctrl {
MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX,
MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE,
MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID,
+ MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE,
+ MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI,
/* keep last */
NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL,
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 829274bfe..6aac87ce1 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -5481,9 +5481,11 @@ struct wpa_driver_ops {
* @lpi_bcn_enhance: 1 to enable beacon duplicate enhancement in 6G lpi mode, 0 to disable enhancement
* @sku_idx: index used to indicate which sku table should be used
* @link_id: MLD link id. -1 if this is an non-MLD AP
+ * @power_table: power table generated from AFC response
+ * @lpi_mode: specify the current mode is whether lpi
*/
int (*txpower_ctrl)(void *priv, u8 lpi_psd, u8 sku_idx, u8 lpi_bcn_enhance,
- u8 link_id);
+ u8 link_id, s8 **power_table, u8 lpi_mode);
};
/**
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index d2064fbd6..efee210b9 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -41,6 +41,7 @@
#include "driver_nl80211.h"
#include "common/mtk_vendor.h"
#include "ap/ap_config.h"
+#include "ap/hostapd.h"
#ifdef CONFIG_IEEE80211BE
#include "ap/scs.h"
@@ -205,6 +206,8 @@ txpower_ctrl_policy[NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL] = {
[MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX] = { .type = NLA_U8 },
[MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE] = { .type = NLA_U8 },
[MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID] = { .type = NLA_U8 },
+ [MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE] = { .type = NLA_BINARY },
+ [MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI] = { .type = NLA_U8 },
};
static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
@@ -15662,38 +15665,53 @@ fail:
}
static int nl80211_txpower_ctrl(void *priv, u8 lpi_psd, u8 sku_idx, u8 lpi_bcn_enhance,
- u8 link_id)
+ u8 link_id, s8 **power_table, u8 lpi_mode)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
struct nlattr *data;
- int ret;
+ struct nlattr *table_attr, *channel_list;
+ int ret = 0;
if (!drv->mtk_txpower_vendor_cmd_avail) {
wpa_printf(MSG_INFO,
"nl80211: Driver does not support setting txpower control");
- return 0;
+ goto fail;
}
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
- if (!msg)
+ if (!msg) {
+ ret = -ENOBUFS;
goto fail;
+ }
if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
- MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL))
+ MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL)) {
+ ret = -ENOBUFS;
goto fail;
+ }
- data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
- if (!data)
+ data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
+ if (!data) {
+ ret = -ENOBUFS;
goto fail;
+ }
nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_PSD, lpi_psd);
nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX, sku_idx);
nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE, lpi_bcn_enhance);
- nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID, link_id);
+ if (link_id > -1)
+ nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID, link_id);
+
+ if (power_table && *power_table) {
+ nla_put(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE,
+ MAX_CHANNEL_NUM_6G * afc_power_table_num, power_table);
+ }
+
+ nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI, lpi_mode);
nla_nest_end(msg, data);
ret = send_and_recv_cmd(drv, msg);
if (ret)
@@ -15704,7 +15722,7 @@ static int nl80211_txpower_ctrl(void *priv, u8 lpi_psd, u8 sku_idx, u8 lpi_bcn_e
fail:
nlmsg_free(msg);
- return -ENOBUFS;
+ return ret;
}
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
--
2.18.0