developer | 05f3b2b | 2024-08-19 19:17:34 +0800 | [diff] [blame^] | 1 | From 330346c577b4623e52767ad0939cd735a83d9837 Mon Sep 17 00:00:00 2001 |
| 2 | From: Allen Ye <allen.ye@mediatek.com> |
| 3 | Date: Mon, 22 Jul 2024 20:51:18 +0800 |
| 4 | Subject: [PATCH 124/126] mtk: hostapd: Add AFC and lpi driver power support |
| 5 | |
| 6 | Add AFC and lpi driver power support |
| 7 | This patch parse the AFC response into mtk sku power table format and send |
| 8 | it to driver by vendor cmd. The table format is like below: |
| 9 | col\row bw20 bw40 ... ru26 ... ru3472 |
| 10 | chan 1 |
| 11 | chan 5 |
| 12 | ... |
| 13 | chan 233 |
| 14 | |
| 15 | - Once the afc procedure start failed, the device would set as lpi mode by |
| 16 | telling driver lpi_sku_index to use specify sku-index in dst. |
| 17 | - Add afc_max_timeout conf is use to limit the maximum interval between the |
| 18 | two afc requests. |
| 19 | - Set default use lpi & sp mode |
| 20 | |
| 21 | Signed-off-by: Allen Ye <allen.ye@mediatek.com> |
| 22 | --- |
| 23 | hostapd/config_file.c | 4 + |
| 24 | src/ap/afc.c | 365 +++++++++++++++++++++++++++++++++-- |
| 25 | src/ap/ap_config.c | 3 +- |
| 26 | src/ap/ap_config.h | 2 + |
| 27 | src/ap/ap_drv_ops.c | 34 +++- |
| 28 | src/ap/hostapd.h | 60 ++++++ |
| 29 | src/common/mtk_vendor.h | 2 + |
| 30 | src/drivers/driver.h | 4 +- |
| 31 | src/drivers/driver_nl80211.c | 36 +++- |
| 32 | 9 files changed, 477 insertions(+), 33 deletions(-) |
| 33 | |
| 34 | diff --git a/hostapd/config_file.c b/hostapd/config_file.c |
| 35 | index 944669270..11c5f8947 100644 |
| 36 | --- a/hostapd/config_file.c |
| 37 | +++ b/hostapd/config_file.c |
| 38 | @@ -4248,6 +4248,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, |
| 39 | } else if (os_strcmp(buf, "afc_op_class") == 0) { |
| 40 | if (hostapd_afc_parse_op_class(conf, pos)) |
| 41 | return 1; |
| 42 | + } else if (os_strcmp(buf, "afc_max_timeout") == 0) { |
| 43 | + conf->afc.max_timeout = atoi(pos); |
| 44 | #endif /* CONFIG_AFC */ |
| 45 | } else if (os_strcmp(buf, "mbssid") == 0) { |
| 46 | int mbssid = atoi(pos); |
| 47 | @@ -5575,6 +5577,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, |
| 48 | conf->lpi_psd = !!en; |
| 49 | } else if (os_strcmp(buf, "sku_idx") == 0) { |
| 50 | conf->sku_idx = strtol(pos, NULL, 10); |
| 51 | + } else if (os_strcmp(buf, "lpi_sku_idx") == 0) { |
| 52 | + conf->lpi_sku_idx = strtol(pos, NULL, 10); |
| 53 | } else if (os_strcmp(buf, "lpi_bcn_enhance") == 0) { |
| 54 | u8 en = strtol(pos, NULL, 10); |
| 55 | conf->lpi_bcn_enhance = !!en; |
| 56 | diff --git a/src/ap/afc.c b/src/ap/afc.c |
| 57 | index 361ecb575..d36ce00d7 100644 |
| 58 | --- a/src/ap/afc.c |
| 59 | +++ b/src/ap/afc.c |
| 60 | @@ -16,6 +16,7 @@ |
| 61 | #include "hostapd.h" |
| 62 | #include "acs.h" |
| 63 | #include "hw_features.h" |
| 64 | +#include "ap_drv_ops.h" |
| 65 | |
| 66 | #define HOSTAPD_AFC_RETRY_TIMEOUT 180 |
| 67 | #define HOSTAPD_AFC_TIMEOUT 86400 /* 24h */ |
| 68 | @@ -749,6 +750,9 @@ static int hostapd_afc_parse_reply(struct hostapd_iface *iface, char *reply) |
| 69 | iface->afc.timeout = request_timeout; |
| 70 | if (iface->afc.timeout < 0) |
| 71 | iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT; |
| 72 | + else if (iface->afc.timeout > iconf->afc.max_timeout && |
| 73 | + iconf->afc.max_timeout >= HOSTAPD_AFC_RETRY_TIMEOUT) |
| 74 | + iface->afc.timeout = iconf->afc.max_timeout; |
| 75 | |
| 76 | return ret; |
| 77 | } |
| 78 | @@ -772,6 +776,7 @@ static int hostapd_afc_send_receive(struct hostapd_iface *iface) |
| 79 | int sockfd, ret; |
| 80 | fd_set read_set; |
| 81 | |
| 82 | + iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT; |
| 83 | if (iface->afc.data_valid) { |
| 84 | /* AFC data already downloaded from the server */ |
| 85 | return 0; |
| 86 | @@ -782,7 +787,6 @@ static int hostapd_afc_send_receive(struct hostapd_iface *iface) |
| 87 | return -EINVAL; |
| 88 | } |
| 89 | |
| 90 | - iface->afc.timeout = HOSTAPD_AFC_RETRY_TIMEOUT; |
| 91 | if (os_strlen(iconf->afc.socket) >= sizeof(addr.sun_path)) { |
| 92 | wpa_printf(MSG_ERROR, "Malformed AFC socket string %s", |
| 93 | iconf->afc.socket); |
| 94 | @@ -880,7 +884,19 @@ static bool hostapd_afc_has_usable_chans(struct hostapd_iface *iface) |
| 95 | int hostapd_afc_handle_request(struct hostapd_iface *iface) |
| 96 | { |
| 97 | struct hostapd_config *iconf = iface->conf; |
| 98 | + bool lpi_mode; |
| 99 | int ret; |
| 100 | + int afc_status = AFC_CONTINUE; |
| 101 | + |
| 102 | + lpi_mode = he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type); |
| 103 | + if (lpi_mode && !he_reg_is_sp(iconf->he_6ghz_reg_pwr_type)) { |
| 104 | + iface->afc.lpi_mode = true; |
| 105 | + return 1; |
| 106 | + } |
| 107 | + |
| 108 | + if (strncmp(iconf->country, "US", 2) != 0 && |
| 109 | + strncmp(iconf->country, "CA", 2) != 0) |
| 110 | + return 1; |
| 111 | |
| 112 | /* AFC is required just for standard power AP */ |
| 113 | if (!he_reg_is_sp(iconf->he_6ghz_reg_pwr_type)) |
| 114 | @@ -894,34 +910,50 @@ int hostapd_afc_handle_request(struct hostapd_iface *iface) |
| 115 | |
| 116 | ret = hostapd_afc_send_receive(iface); |
| 117 | if (ret < 0) { |
| 118 | - /* |
| 119 | - * If the connection to the AFCD failed, resched for a |
| 120 | - * future attempt. |
| 121 | - */ |
| 122 | - wpa_printf(MSG_ERROR, "AFC connection failed: %d", ret); |
| 123 | - if (ret == -EIO) |
| 124 | - ret = 0; |
| 125 | + afc_status = lpi_mode ? AFC_LPI : AFC_DISABLE; |
| 126 | goto resched; |
| 127 | } |
| 128 | |
| 129 | hostap_afc_disable_channels(iface); |
| 130 | - if (!hostapd_afc_has_usable_chans(iface)) |
| 131 | + if (!hostapd_afc_has_usable_chans(iface)) { |
| 132 | + afc_status = lpi_mode ? AFC_LPI : AFC_DISABLE; |
| 133 | goto resched; |
| 134 | + } |
| 135 | |
| 136 | if (!hostapd_is_usable_chans(iface)) { |
| 137 | /* Trigger an ACS freq scan */ |
| 138 | + afc_status = AFC_RESTART_IFACE; |
| 139 | iconf->channel = 0; |
| 140 | iface->freq = 0; |
| 141 | |
| 142 | if (acs_init(iface) != HOSTAPD_CHAN_ACS) { |
| 143 | wpa_printf(MSG_ERROR, "Could not start ACS"); |
| 144 | + afc_status = AFC_DISABLE; |
| 145 | ret = -EINVAL; |
| 146 | } |
| 147 | } else { |
| 148 | + afc_status = AFC_CONTINUE; |
| 149 | ret = 1; |
| 150 | } |
| 151 | |
| 152 | resched: |
| 153 | + switch(afc_status) { |
| 154 | + case AFC_LPI: |
| 155 | + iface->afc.lpi_mode = true; |
| 156 | + hostapd_afc_enable_lpi_channels(iface); |
| 157 | + ret = 1; |
| 158 | + break; |
| 159 | + /* Disable and restart iface would be finished in hostapd setup flow. */ |
| 160 | + case AFC_RESTART_IFACE: |
| 161 | + ret = 0; |
| 162 | + fallthrough; |
| 163 | + case AFC_DISABLE: |
| 164 | + case AFC_CONTINUE: |
| 165 | + break; |
| 166 | + default: |
| 167 | + break; |
| 168 | + } |
| 169 | + |
| 170 | eloop_cancel_timeout(hostapd_afc_timeout_handler, iface, NULL); |
| 171 | eloop_register_timeout(iface->afc.timeout, 0, |
| 172 | hostapd_afc_timeout_handler, iface, NULL); |
| 173 | @@ -948,34 +980,58 @@ static void hostapd_afc_delete_data_from_server(struct hostapd_iface *iface) |
| 174 | static void hostapd_afc_timeout_handler(void *eloop_ctx, void *timeout_ctx) |
| 175 | { |
| 176 | struct hostapd_iface *iface = eloop_ctx; |
| 177 | - bool restart_iface = true; |
| 178 | + bool lpi_mode; |
| 179 | + int afc_status = AFC_CONTINUE, ret; |
| 180 | + |
| 181 | + lpi_mode = he_reg_is_indoor(iface->conf->he_6ghz_reg_pwr_type); |
| 182 | + iface->afc.lpi_mode = false; |
| 183 | |
| 184 | hostapd_afc_delete_data_from_server(iface); |
| 185 | if (iface->state != HAPD_IFACE_ENABLED) { |
| 186 | + afc_status = AFC_RESTART_IFACE; |
| 187 | /* Hostapd is not fully enabled yet, toggle the interface */ |
| 188 | goto restart_interface; |
| 189 | } |
| 190 | |
| 191 | if (hostapd_afc_send_receive(iface) < 0 || |
| 192 | hostapd_get_hw_features(iface)) { |
| 193 | - restart_iface = false; |
| 194 | + afc_status = lpi_mode ? AFC_LPI : AFC_DISABLE; |
| 195 | goto restart_interface; |
| 196 | } |
| 197 | |
| 198 | - if (hostapd_is_usable_chans(iface)) |
| 199 | - goto resched; |
| 200 | + ret = hostapd_is_usable_chans(iface); |
| 201 | + if (ret != 1) { |
| 202 | + afc_status = lpi_mode && ret == 0 ? AFC_LPI : AFC_DISABLE; |
| 203 | + goto restart_interface; |
| 204 | + } |
| 205 | |
| 206 | - restart_iface = hostapd_afc_has_usable_chans(iface); |
| 207 | - if (restart_iface) { |
| 208 | + ret = hostapd_afc_has_usable_chans(iface); |
| 209 | + if (ret) { |
| 210 | /* Trigger an ACS freq scan */ |
| 211 | + afc_status = AFC_RESTART_IFACE; |
| 212 | iface->conf->channel = 0; |
| 213 | iface->freq = 0; |
| 214 | } |
| 215 | |
| 216 | restart_interface: |
| 217 | - hostapd_disable_iface(iface); |
| 218 | - if (restart_iface) |
| 219 | + switch(afc_status) { |
| 220 | + case AFC_DISABLE: |
| 221 | + hostapd_disable_iface(iface); |
| 222 | + break; |
| 223 | + case AFC_RESTART_IFACE: |
| 224 | + hostapd_disable_iface(iface); |
| 225 | hostapd_enable_iface(iface); |
| 226 | + break; |
| 227 | + case AFC_LPI: |
| 228 | + iface->afc.lpi_mode = true; |
| 229 | + hostapd_afc_enable_lpi_channels(iface); |
| 230 | + hostapd_drv_txpower_ctrl(iface->bss[0]); |
| 231 | + break; |
| 232 | + case AFC_CONTINUE: |
| 233 | + break; |
| 234 | + default: |
| 235 | + break; |
| 236 | + } |
| 237 | resched: |
| 238 | eloop_register_timeout(iface->afc.timeout, 0, |
| 239 | hostapd_afc_timeout_handler, iface, NULL); |
| 240 | @@ -1040,6 +1096,37 @@ void hostap_afc_disable_channels(struct hostapd_iface *iface) |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | +void hostapd_afc_enable_lpi_channels(struct hostapd_iface *iface) |
| 245 | +{ |
| 246 | + struct hostapd_hw_modes *mode = NULL; |
| 247 | + int i; |
| 248 | + |
| 249 | + for (i = 0; i < iface->num_hw_features; i++) { |
| 250 | + mode = &iface->hw_features[i]; |
| 251 | + if (mode->mode == HOSTAPD_MODE_IEEE80211A && |
| 252 | + mode->is_6ghz) |
| 253 | + break; |
| 254 | + } |
| 255 | + |
| 256 | + if (i == iface->num_hw_features) |
| 257 | + return; |
| 258 | + |
| 259 | + if (!he_reg_is_indoor(iface->conf->he_6ghz_reg_pwr_type)) |
| 260 | + return; |
| 261 | + |
| 262 | + for (i = 0; i < mode->num_channels; i++) { |
| 263 | + struct hostapd_channel_data *chan = &mode->channels[i]; |
| 264 | + |
| 265 | + if (!is_6ghz_freq(chan->freq)) |
| 266 | + continue; |
| 267 | + |
| 268 | + chan->flag &= ~HOSTAPD_CHAN_DISABLED; |
| 269 | + wpa_printf(MSG_MSGDUMP, |
| 270 | + "Enabling freq=%d MHz for lpi mode", |
| 271 | + chan->freq); |
| 272 | + } |
| 273 | +} |
| 274 | + |
| 275 | |
| 276 | int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd, |
| 277 | int *power) |
| 278 | @@ -1076,3 +1163,247 @@ int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd, |
| 279 | } |
| 280 | return -EINVAL; |
| 281 | } |
| 282 | + |
| 283 | +void hostapd_afc_init_power_table(s8 ***power_table) |
| 284 | +{ |
| 285 | + int table_idx, bw; |
| 286 | + s8 *chan_power_list; |
| 287 | + |
| 288 | + /* init power table */ |
| 289 | + for (table_idx = 0; table_idx < MAX_CHANNEL_NUM_6G; table_idx++) { |
| 290 | + chan_power_list = (*power_table)[table_idx]; |
| 291 | + for (bw = 0; bw < afc_power_table_num; bw++) |
| 292 | + chan_power_list[bw] = AFC_INVALID_POWER; |
| 293 | + } |
| 294 | +} |
| 295 | + |
| 296 | +int hostapd_afc_parse_psd_to_dbm(struct hostapd_iface *iface, s8 ***power_table) |
| 297 | +{ |
| 298 | + int i, freq, channel, bw, table_idx, target_power; |
| 299 | + s8 *chan_power_list; |
| 300 | + |
| 301 | + for (i = 0; i < iface->afc.num_freq_range; i++) { |
| 302 | + struct afc_freq_range_elem *freq_range = &iface->afc.freq_range[i]; |
| 303 | + |
| 304 | + if (!freq_range) |
| 305 | + continue; |
| 306 | + |
| 307 | + freq = freq_range->low_freq + 10; |
| 308 | + channel = hostapd_hw_get_channel(iface->bss[0], freq); |
| 309 | + if (channel == 0) |
| 310 | + return -EINVAL; |
| 311 | + |
| 312 | + table_idx = channel / 4; |
| 313 | + |
| 314 | + if (table_idx >= MAX_CHANNEL_NUM_6G) |
| 315 | + return -EINVAL; |
| 316 | + |
| 317 | + chan_power_list = (*power_table)[table_idx]; |
| 318 | + for (bw = 0; bw < afc_power_bw320_2; bw++) { |
| 319 | + target_power = freq_range->max_psd * 2 + PSD_TO_DBM_OFFSET + |
| 320 | + bw * DOUBLE_BW_POWER; |
| 321 | + target_power = MIN(AFC_MAXIMUM_POWER, target_power); |
| 322 | + chan_power_list[bw] = MIN(chan_power_list[bw], |
| 323 | + target_power); |
| 324 | + } |
| 325 | + chan_power_list[afc_power_bw320_2] = chan_power_list[afc_power_bw320_1]; |
| 326 | + } |
| 327 | + return 0; |
| 328 | +} |
| 329 | + |
| 330 | +int hostapd_afc_parse_eirp_to_dbm(struct hostapd_iface *iface, s8 ***power_table) |
| 331 | +{ |
| 332 | + int i, bw, table_idx, target_power; |
| 333 | + s8 *chan_power_list; |
| 334 | + |
| 335 | + for (i = 0; i < iface->afc.num_chan_info; i++) { |
| 336 | + struct afc_chan_info_elem *chan_info = &iface->afc.chan_info_list[i]; |
| 337 | + |
| 338 | + if (!chan_info) |
| 339 | + continue; |
| 340 | + |
| 341 | + table_idx = chan_info->chan / 4; |
| 342 | + |
| 343 | + if (table_idx >= MAX_CHANNEL_NUM_6G) |
| 344 | + return -EINVAL; |
| 345 | + |
| 346 | + chan_power_list = (*power_table)[table_idx]; |
| 347 | + target_power = MIN(AFC_MAXIMUM_POWER, chan_info->power * 2); |
| 348 | + /* FIXME: wider bandwidth power is not stored. */ |
| 349 | + chan_power_list[afc_power_bw20] = MIN(chan_power_list[afc_power_bw20], |
| 350 | + target_power); |
| 351 | + } |
| 352 | + return 0; |
| 353 | +} |
| 354 | + |
| 355 | +int afc_get_ru_be_offset(int bw, int *target_bw, int *offset) |
| 356 | +{ |
| 357 | + switch (bw) { |
| 358 | + case afc_power_ru26: |
| 359 | + *target_bw = afc_power_bw20; |
| 360 | + *offset = RU26_OFFSET_20MHZ; |
| 361 | + break; |
| 362 | + case afc_power_ru52: |
| 363 | + *target_bw = afc_power_bw20; |
| 364 | + *offset = RU52_OFFSET_20MHZ; |
| 365 | + break; |
| 366 | + case afc_power_ru78: |
| 367 | + *target_bw = afc_power_bw20; |
| 368 | + *offset = RU78_OFFSET_20MHZ; |
| 369 | + break; |
| 370 | + case afc_power_ru106: |
| 371 | + *target_bw = afc_power_bw20; |
| 372 | + *offset = RU106_OFFSET_20MHZ; |
| 373 | + break; |
| 374 | + case afc_power_ru132: |
| 375 | + *target_bw = afc_power_bw20; |
| 376 | + *offset = RU132_OFFSET_20MHZ; |
| 377 | + break; |
| 378 | + case afc_power_ru726: |
| 379 | + *target_bw = afc_power_bw80; |
| 380 | + *offset = RU726_OFFSET_80MHZ; |
| 381 | + break; |
| 382 | + case afc_power_ru1480: |
| 383 | + *target_bw = afc_power_bw160; |
| 384 | + *offset = RU1480_OFFSET_160MHZ; |
| 385 | + break; |
| 386 | + case afc_power_ru1772: |
| 387 | + *target_bw = afc_power_bw160; |
| 388 | + *offset = RU1772_OFFSET_160MHZ; |
| 389 | + break; |
| 390 | + case afc_power_ru2476: |
| 391 | + *target_bw = afc_power_bw320_1; |
| 392 | + *offset = RU2476_OFFSET_320MHZ; |
| 393 | + break; |
| 394 | + case afc_power_ru2988: |
| 395 | + *target_bw = afc_power_bw320_1; |
| 396 | + *offset = RU2988_OFFSET_320MHZ; |
| 397 | + break; |
| 398 | + case afc_power_ru3472: |
| 399 | + *target_bw = afc_power_bw320_1; |
| 400 | + *offset = RU3472_OFFSET_320MHZ; |
| 401 | + break; |
| 402 | + default: |
| 403 | + return -EINVAL; |
| 404 | + } |
| 405 | + return 0; |
| 406 | +} |
| 407 | + |
| 408 | +int hostapd_afc_fill_wide_bandwidth_power(s8 ***power_table) |
| 409 | +{ |
| 410 | + int table_idx, bw; |
| 411 | + s8 *chan_power_list; |
| 412 | + |
| 413 | + for (table_idx = 0; table_idx < MAX_CHANNEL_NUM_6G; table_idx++) { |
| 414 | + int target_power, ru26_power; |
| 415 | + |
| 416 | + if ((*power_table)[table_idx][afc_power_bw20] == AFC_INVALID_POWER) |
| 417 | + continue; |
| 418 | + |
| 419 | + chan_power_list = (*power_table)[table_idx]; |
| 420 | + |
| 421 | + /* Check wide bandwidth power minimum or valid. */ |
| 422 | + for (bw = afc_power_bw40; bw <= afc_power_bw320_2; bw++) { |
| 423 | + int bw_ch_num, first_ch, last_ch; |
| 424 | + |
| 425 | + target_power = AFC_INVALID_POWER; |
| 426 | + switch (bw) { |
| 427 | + case afc_power_bw40: |
| 428 | + bw_ch_num = 2; |
| 429 | + break; |
| 430 | + case afc_power_bw80: |
| 431 | + bw_ch_num = 4; |
| 432 | + break; |
| 433 | + case afc_power_bw160: |
| 434 | + bw_ch_num = 8; |
| 435 | + break; |
| 436 | + case afc_power_bw320_1: |
| 437 | + case afc_power_bw320_2: |
| 438 | + bw_ch_num = 16; |
| 439 | + break; |
| 440 | + } |
| 441 | + if ((bw == afc_power_bw320_1 && table_idx > 47) || |
| 442 | + (bw == afc_power_bw320_2 && table_idx < 8)) |
| 443 | + continue; |
| 444 | + |
| 445 | + if (bw == afc_power_bw320_2) |
| 446 | + first_ch = table_idx - (table_idx + 8) % bw_ch_num; |
| 447 | + else |
| 448 | + first_ch = table_idx - table_idx % bw_ch_num; |
| 449 | + last_ch = first_ch + bw_ch_num; |
| 450 | + for (int ch = first_ch; ch < last_ch; ch++) { |
| 451 | + if ((*power_table)[ch][bw] == AFC_INVALID_POWER) { |
| 452 | + target_power = AFC_INVALID_POWER; |
| 453 | + break; |
| 454 | + } |
| 455 | + target_power = MIN((*power_table)[ch][bw], target_power); |
| 456 | + } |
| 457 | + chan_power_list[bw] = target_power; |
| 458 | + } |
| 459 | + |
| 460 | + /* Update remain ru */ |
| 461 | + for (bw = afc_power_ru26; bw < afc_power_table_num; bw++) { |
| 462 | + int target_bw, offset; |
| 463 | + |
| 464 | + if (afc_get_ru_be_offset(bw, &target_bw, &offset)) |
| 465 | + return -EINVAL; |
| 466 | + |
| 467 | + if (target_bw == afc_power_bw320_1 && |
| 468 | + chan_power_list[target_bw] == AFC_INVALID_POWER) |
| 469 | + target_bw++; |
| 470 | + |
| 471 | + if (chan_power_list[target_bw] == AFC_INVALID_POWER) { |
| 472 | + chan_power_list[bw] = AFC_INVALID_POWER; |
| 473 | + continue; |
| 474 | + } |
| 475 | + |
| 476 | + target_power = chan_power_list[target_bw] - offset; |
| 477 | + chan_power_list[bw] = target_power; |
| 478 | + } |
| 479 | + } |
| 480 | + return 0; |
| 481 | +} |
| 482 | + |
| 483 | + |
| 484 | +int hostapd_afc_translate_table(struct hostapd_iface *iface, |
| 485 | + s8 ***power_table) |
| 486 | +{ |
| 487 | + int i, ret, bw320_offset; |
| 488 | + |
| 489 | + if (!iface->afc.data_valid) |
| 490 | + return -EINVAL; |
| 491 | + |
| 492 | + *power_table = (s8**)os_zalloc(MAX_CHANNEL_NUM_6G * sizeof(s8*)); |
| 493 | + |
| 494 | + if (!(*power_table)) |
| 495 | + return -ENOMEM; |
| 496 | + |
| 497 | + for (i = 0; i < MAX_CHANNEL_NUM_6G; i++) { |
| 498 | + (*power_table)[i] = (s8*)os_zalloc(afc_power_table_num * sizeof(s8)); |
| 499 | + if (!(*power_table)[i]) |
| 500 | + goto out; |
| 501 | + } |
| 502 | + |
| 503 | + hostapd_afc_init_power_table(power_table); |
| 504 | + |
| 505 | + ret = hostapd_afc_parse_psd_to_dbm(iface, power_table); |
| 506 | + if (ret) |
| 507 | + goto out; |
| 508 | + |
| 509 | + ret = hostapd_afc_parse_eirp_to_dbm(iface, power_table); |
| 510 | + if (ret) |
| 511 | + goto out; |
| 512 | + |
| 513 | + ret = hostapd_afc_fill_wide_bandwidth_power(power_table); |
| 514 | + if (ret) |
| 515 | + goto out; |
| 516 | + |
| 517 | + return 0; |
| 518 | +out: |
| 519 | + for (i = 0; i < MAX_CHANNEL_NUM_6G; i++) |
| 520 | + os_free((*power_table)[i]); |
| 521 | + |
| 522 | + os_free(*power_table); |
| 523 | + power_table = NULL; |
| 524 | + return -ENOMEM; |
| 525 | +} |
| 526 | diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c |
| 527 | index bb5ec78a1..185dae089 100644 |
| 528 | --- a/src/ap/ap_config.c |
| 529 | +++ b/src/ap/ap_config.c |
| 530 | @@ -288,7 +288,7 @@ struct hostapd_config * hostapd_config_defaults(void) |
| 531 | conf->he_6ghz_max_ampdu_len_exp = 7; |
| 532 | conf->he_6ghz_rx_ant_pat = 1; |
| 533 | conf->he_6ghz_tx_ant_pat = 1; |
| 534 | - conf->he_6ghz_reg_pwr_type = HE_REG_INFO_6GHZ_AP_TYPE_VLP; |
| 535 | + conf->he_6ghz_reg_pwr_type = HE_REG_INFO_6GHZ_AP_TYPE_INDOOR; |
| 536 | conf->reg_def_cli_eirp_psd = -1; |
| 537 | conf->reg_sub_cli_eirp_psd = -1; |
| 538 | conf->reg_def_cli_eirp = -1; |
| 539 | @@ -317,6 +317,7 @@ struct hostapd_config * hostapd_config_defaults(void) |
| 540 | |
| 541 | conf->lpi_psd = 0; |
| 542 | conf->sku_idx = 0; |
| 543 | + conf->lpi_sku_idx = 0; |
| 544 | conf->lpi_bcn_enhance = 0; |
| 545 | |
| 546 | hostapd_set_and_check_bw320_offset(conf, 0); |
| 547 | diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h |
| 548 | index ea8507cea..966a02d6a 100644 |
| 549 | --- a/src/ap/ap_config.h |
| 550 | +++ b/src/ap/ap_config.h |
| 551 | @@ -1343,6 +1343,7 @@ struct hostapd_config { |
| 552 | unsigned int n_op_class; |
| 553 | unsigned int *op_class; |
| 554 | int min_power; |
| 555 | + int max_timeout; |
| 556 | } afc; |
| 557 | #endif /* CONFIG_AFC */ |
| 558 | |
| 559 | @@ -1358,6 +1359,7 @@ struct hostapd_config { |
| 560 | u8 band_idx; |
| 561 | u8 lpi_psd; |
| 562 | u8 sku_idx; |
| 563 | + u8 lpi_sku_idx; |
| 564 | u8 lpi_bcn_enhance; |
| 565 | }; |
| 566 | |
| 567 | diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c |
| 568 | index 64fd0c04c..bd710832a 100644 |
| 569 | --- a/src/ap/ap_drv_ops.c |
| 570 | +++ b/src/ap/ap_drv_ops.c |
| 571 | @@ -1397,7 +1397,8 @@ int hostapd_drv_get_aval_bss_color_bmp(struct hostapd_data *hapd, u64 *aval_colo |
| 572 | |
| 573 | int hostapd_drv_txpower_ctrl(struct hostapd_data *hapd) |
| 574 | { |
| 575 | - s8 link_id = -1; |
| 576 | + s8 link_id = -1, sku_idx = hapd->iconf->sku_idx, ret = 0, i; |
| 577 | + s8 **afc_power_table = NULL; |
| 578 | |
| 579 | if (!hapd->driver || !hapd->driver->txpower_ctrl) |
| 580 | return 0; |
| 581 | @@ -1405,10 +1406,33 @@ int hostapd_drv_txpower_ctrl(struct hostapd_data *hapd) |
| 582 | if (hapd->conf->mld_ap) |
| 583 | link_id = hapd->mld_link_id; |
| 584 | |
| 585 | - return hapd->driver->txpower_ctrl(hapd->drv_priv, hapd->iconf->lpi_psd, |
| 586 | - hapd->iconf->sku_idx, |
| 587 | - hapd->iconf->lpi_bcn_enhance, |
| 588 | - link_id); |
| 589 | +#ifdef CONFIG_AFC |
| 590 | + if (hapd->iface->current_mode->is_6ghz && |
| 591 | + he_reg_is_sp(hapd->iface->conf->he_6ghz_reg_pwr_type) && |
| 592 | + !hapd->iface->afc.lpi_mode) { |
| 593 | + ret = hostapd_afc_translate_table(hapd->iface, &afc_power_table); |
| 594 | + if (ret) |
| 595 | + goto out; |
| 596 | + } |
| 597 | + |
| 598 | + if (hapd->iface->afc.lpi_mode == true) |
| 599 | + sku_idx = hapd->iconf->lpi_sku_idx; |
| 600 | +#endif /* CONFIG_AFC */ |
| 601 | + |
| 602 | + ret = hapd->driver->txpower_ctrl(hapd->drv_priv, hapd->iconf->lpi_psd, |
| 603 | + sku_idx, |
| 604 | + hapd->iconf->lpi_bcn_enhance, |
| 605 | + link_id, |
| 606 | + afc_power_table, |
| 607 | + hapd->iface->afc.lpi_mode); |
| 608 | +#ifdef CONFIG_AFC |
| 609 | +out: |
| 610 | + if (afc_power_table) |
| 611 | + for (i = 0; i < MAX_CHANNEL_NUM_6G; i++) |
| 612 | + os_free(afc_power_table[i]); |
| 613 | + os_free(afc_power_table); |
| 614 | +#endif /* CONFIG_AFC */ |
| 615 | + return ret; |
| 616 | } |
| 617 | |
| 618 | int hostapd_drv_ap_wireless(struct hostapd_data *hapd, u8 sub_vendor_id, int value) |
| 619 | diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h |
| 620 | index f69fa0062..0625bb762 100644 |
| 621 | --- a/src/ap/hostapd.h |
| 622 | +++ b/src/ap/hostapd.h |
| 623 | @@ -783,17 +783,71 @@ struct hostapd_iface { |
| 624 | int power; |
| 625 | } *chan_info_list; |
| 626 | bool data_valid; |
| 627 | + bool lpi_mode; |
| 628 | } afc; |
| 629 | #endif /* CONFIG_AFC */ |
| 630 | }; |
| 631 | |
| 632 | /* hostapd.c */ |
| 633 | #ifdef CONFIG_AFC |
| 634 | + |
| 635 | +enum afc_state { |
| 636 | + AFC_DISABLE, |
| 637 | + AFC_RESTART_IFACE, |
| 638 | + AFC_LPI, |
| 639 | + AFC_CONTINUE, |
| 640 | +}; |
| 641 | + |
| 642 | +#define MAX_CHANNEL_NUM_6G 59 |
| 643 | + |
| 644 | +/* The power unit is 0.5 dBm */ |
| 645 | +#define AFC_MAXIMUM_POWER 72 |
| 646 | +#define AFC_INVALID_POWER 127 |
| 647 | +#define PSD_TO_DBM_OFFSET 26 |
| 648 | +#define BW20_TO_RU26_OFFSET 20 |
| 649 | +#define DOUBLE_BW_POWER 6 |
| 650 | + |
| 651 | +#define RU26_OFFSET_20MHZ 20 |
| 652 | +#define RU52_OFFSET_20MHZ 14 |
| 653 | +#define RU78_OFFSET_20MHZ 10 |
| 654 | +#define RU106_OFFSET_20MHZ 8 |
| 655 | +#define RU132_OFFSET_20MHZ 6 |
| 656 | + |
| 657 | +#define RU726_OFFSET_80MHZ 2 |
| 658 | +#define RU1480_OFFSET_160MHZ 2 |
| 659 | +#define RU1772_OFFSET_160MHZ 1 |
| 660 | +#define RU2476_OFFSET_320MHZ 4 |
| 661 | +#define RU2988_OFFSET_320MHZ 2 |
| 662 | +#define RU3472_OFFSET_320MHZ 1 |
| 663 | + |
| 664 | +enum afc_table_info { |
| 665 | + afc_power_bw20, |
| 666 | + afc_power_bw40, |
| 667 | + afc_power_bw80, |
| 668 | + afc_power_bw160, |
| 669 | + afc_power_bw320_1, |
| 670 | + afc_power_bw320_2, |
| 671 | + afc_power_ru26, |
| 672 | + afc_power_ru52, |
| 673 | + afc_power_ru78, |
| 674 | + afc_power_ru106, |
| 675 | + afc_power_ru132, |
| 676 | + afc_power_ru726, |
| 677 | + afc_power_ru1480, |
| 678 | + afc_power_ru1772, |
| 679 | + afc_power_ru2476, |
| 680 | + afc_power_ru2988, |
| 681 | + afc_power_ru3472, |
| 682 | + afc_power_table_num, |
| 683 | +}; |
| 684 | + |
| 685 | int hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd, |
| 686 | int *power); |
| 687 | int hostapd_afc_handle_request(struct hostapd_iface *iface); |
| 688 | void hostapd_afc_stop(struct hostapd_iface *iface); |
| 689 | void hostap_afc_disable_channels(struct hostapd_iface *iface); |
| 690 | +int hostapd_afc_translate_table(struct hostapd_iface *iface, |
| 691 | + s8 ***power_table); |
| 692 | #else |
| 693 | static inline int |
| 694 | hostap_afc_get_chan_max_eirp_power(struct hostapd_iface *iface, bool psd, |
| 695 | @@ -814,6 +868,12 @@ static inline void hostapd_afc_stop(struct hostapd_iface *iface) |
| 696 | static inline void hostap_afc_disable_channels(struct hostapd_iface *iface) |
| 697 | { |
| 698 | } |
| 699 | + |
| 700 | +int hostapd_afc_translate_table(struct hostapd_iface *iface, |
| 701 | + s8 ***power_table) |
| 702 | +{ |
| 703 | + return -EINVAL; |
| 704 | +} |
| 705 | #endif /* CONFIG_AFC */ |
| 706 | |
| 707 | int hostapd_for_each_interface(struct hapd_interfaces *interfaces, |
| 708 | diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h |
| 709 | index 1fe459126..4b900162b 100644 |
| 710 | --- a/src/common/mtk_vendor.h |
| 711 | +++ b/src/common/mtk_vendor.h |
| 712 | @@ -319,6 +319,8 @@ enum mtk_vendor_attr_txpower_ctrl { |
| 713 | MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX, |
| 714 | MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE, |
| 715 | MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID, |
| 716 | + MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE, |
| 717 | + MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI, |
| 718 | |
| 719 | /* keep last */ |
| 720 | NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL, |
| 721 | diff --git a/src/drivers/driver.h b/src/drivers/driver.h |
| 722 | index 829274bfe..6aac87ce1 100644 |
| 723 | --- a/src/drivers/driver.h |
| 724 | +++ b/src/drivers/driver.h |
| 725 | @@ -5481,9 +5481,11 @@ struct wpa_driver_ops { |
| 726 | * @lpi_bcn_enhance: 1 to enable beacon duplicate enhancement in 6G lpi mode, 0 to disable enhancement |
| 727 | * @sku_idx: index used to indicate which sku table should be used |
| 728 | * @link_id: MLD link id. -1 if this is an non-MLD AP |
| 729 | + * @power_table: power table generated from AFC response |
| 730 | + * @lpi_mode: specify the current mode is whether lpi |
| 731 | */ |
| 732 | int (*txpower_ctrl)(void *priv, u8 lpi_psd, u8 sku_idx, u8 lpi_bcn_enhance, |
| 733 | - u8 link_id); |
| 734 | + u8 link_id, s8 **power_table, u8 lpi_mode); |
| 735 | }; |
| 736 | |
| 737 | /** |
| 738 | diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c |
| 739 | index d2064fbd6..efee210b9 100644 |
| 740 | --- a/src/drivers/driver_nl80211.c |
| 741 | +++ b/src/drivers/driver_nl80211.c |
| 742 | @@ -41,6 +41,7 @@ |
| 743 | #include "driver_nl80211.h" |
| 744 | #include "common/mtk_vendor.h" |
| 745 | #include "ap/ap_config.h" |
| 746 | +#include "ap/hostapd.h" |
| 747 | |
| 748 | #ifdef CONFIG_IEEE80211BE |
| 749 | #include "ap/scs.h" |
| 750 | @@ -205,6 +206,8 @@ txpower_ctrl_policy[NUM_MTK_VENDOR_ATTRS_TXPOWER_CTRL] = { |
| 751 | [MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX] = { .type = NLA_U8 }, |
| 752 | [MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE] = { .type = NLA_U8 }, |
| 753 | [MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID] = { .type = NLA_U8 }, |
| 754 | + [MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE] = { .type = NLA_BINARY }, |
| 755 | + [MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI] = { .type = NLA_U8 }, |
| 756 | }; |
| 757 | |
| 758 | static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg) |
| 759 | @@ -15662,38 +15665,53 @@ fail: |
| 760 | } |
| 761 | |
| 762 | static int nl80211_txpower_ctrl(void *priv, u8 lpi_psd, u8 sku_idx, u8 lpi_bcn_enhance, |
| 763 | - u8 link_id) |
| 764 | + u8 link_id, s8 **power_table, u8 lpi_mode) |
| 765 | { |
| 766 | struct i802_bss *bss = priv; |
| 767 | struct wpa_driver_nl80211_data *drv = bss->drv; |
| 768 | struct nl_msg *msg; |
| 769 | struct nlattr *data; |
| 770 | - int ret; |
| 771 | + struct nlattr *table_attr, *channel_list; |
| 772 | + int ret = 0; |
| 773 | |
| 774 | if (!drv->mtk_txpower_vendor_cmd_avail) { |
| 775 | wpa_printf(MSG_INFO, |
| 776 | "nl80211: Driver does not support setting txpower control"); |
| 777 | - return 0; |
| 778 | + goto fail; |
| 779 | } |
| 780 | |
| 781 | msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR); |
| 782 | - if (!msg) |
| 783 | + if (!msg) { |
| 784 | + ret = -ENOBUFS; |
| 785 | goto fail; |
| 786 | + } |
| 787 | |
| 788 | if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) || |
| 789 | nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, |
| 790 | - MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL)) |
| 791 | + MTK_NL80211_VENDOR_SUBCMD_TXPOWER_CTRL)) { |
| 792 | + ret = -ENOBUFS; |
| 793 | goto fail; |
| 794 | + } |
| 795 | |
| 796 | - data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); |
| 797 | - if (!data) |
| 798 | + data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED); |
| 799 | + if (!data) { |
| 800 | + ret = -ENOBUFS; |
| 801 | goto fail; |
| 802 | + } |
| 803 | |
| 804 | nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_PSD, lpi_psd); |
| 805 | nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_SKU_IDX, sku_idx); |
| 806 | nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LPI_BCN_ENHANCE, lpi_bcn_enhance); |
| 807 | - nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID, link_id); |
| 808 | |
| 809 | + if (link_id > -1) |
| 810 | + nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_LINK_ID, link_id); |
| 811 | + |
| 812 | + if (power_table && *power_table) { |
| 813 | + nla_put(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_TABLE, |
| 814 | + MAX_CHANNEL_NUM_6G * afc_power_table_num, power_table); |
| 815 | + } |
| 816 | + |
| 817 | + nla_put_u8(msg, MTK_VENDOR_ATTR_TXPOWER_CTRL_AFC_LPI, lpi_mode); |
| 818 | nla_nest_end(msg, data); |
| 819 | ret = send_and_recv_cmd(drv, msg); |
| 820 | if (ret) |
| 821 | @@ -15704,7 +15722,7 @@ static int nl80211_txpower_ctrl(void *priv, u8 lpi_psd, u8 sku_idx, u8 lpi_bcn_e |
| 822 | |
| 823 | fail: |
| 824 | nlmsg_free(msg); |
| 825 | - return -ENOBUFS; |
| 826 | + return ret; |
| 827 | } |
| 828 | |
| 829 | const struct wpa_driver_ops wpa_driver_nl80211_ops = { |
| 830 | -- |
| 831 | 2.18.0 |
| 832 | |