diff --git a/drivers/wifi/nxp/nxp_wifi_drv.c b/drivers/wifi/nxp/nxp_wifi_drv.c index cdbd15df4749..c7c2a6e7f3de 100644 --- a/drivers/wifi/nxp/nxp_wifi_drv.c +++ b/drivers/wifi/nxp/nxp_wifi_drv.c @@ -1396,11 +1396,13 @@ static int nxp_wifi_set_twt(const struct device *dev, struct wifi_twt_params *pa twt_setup_conf.implicit = params->setup.implicit; twt_setup_conf.announced = params->setup.announce; twt_setup_conf.trigger_enabled = params->setup.trigger; + twt_setup_conf.twt_info_disabled = params->setup.twt_info_disable; twt_setup_conf.negotiation_type = params->negotiation_type; twt_setup_conf.twt_wakeup_duration = params->setup.twt_wake_interval; twt_setup_conf.flow_identifier = params->flow_id; twt_setup_conf.hard_constraint = 1; - twt_setup_conf.twt_mantissa = params->setup.twt_interval; + twt_setup_conf.twt_exponent = params->setup.twt_exponent; + twt_setup_conf.twt_mantissa = params->setup.twt_mantissa; twt_setup_conf.twt_request = params->setup.responder; ret = wlan_set_twt_setup_cfg(&twt_setup_conf); } else if (params->operation == WIFI_TWT_TEARDOWN) { @@ -1412,6 +1414,22 @@ static int nxp_wifi_set_twt(const struct device *dev, struct wifi_twt_params *pa return ret; } + +static int nxp_wifi_set_btwt(const struct device *dev, struct wifi_twt_params *params) +{ + wlan_btwt_config_t btwt_config; + + btwt_config.action = 1; + btwt_config.sub_id = params->btwt.sub_id; + btwt_config.nominal_wake = params->btwt.nominal_wake; + btwt_config.max_sta_support = params->btwt.max_sta_support; + btwt_config.twt_mantissa = params->btwt.twt_mantissa; + btwt_config.twt_offset = params->btwt.twt_offset; + btwt_config.twt_exponent = params->btwt.twt_exponent; + btwt_config.sp_gap = params->btwt.sp_gap; + + return wlan_set_btwt_cfg(&btwt_config); +} #endif static void nxp_wifi_sta_init(struct net_if *iface) @@ -1774,6 +1792,9 @@ static const struct wifi_mgmt_ops nxp_wifi_uap_mgmt = { .set_power_save = nxp_wifi_power_save, .get_power_save_config = nxp_wifi_get_power_save, .ap_config_params = nxp_wifi_ap_config_params, +#ifdef CONFIG_NXP_WIFI_11AX_TWT + .set_btwt = nxp_wifi_set_btwt, +#endif }; static const struct net_wifi_mgmt_offload nxp_wifi_uap_apis = { diff --git a/include/zephyr/net/wifi_mgmt.h b/include/zephyr/net/wifi_mgmt.h index 5ea11ef0ffa3..bbf799ba70eb 100644 --- a/include/zephyr/net/wifi_mgmt.h +++ b/include/zephyr/net/wifi_mgmt.h @@ -85,6 +85,8 @@ enum net_request_wifi_cmd { NET_REQUEST_WIFI_CMD_PS, /** Setup or teardown TWT flow */ NET_REQUEST_WIFI_CMD_TWT, + /** Setup BTWT flow */ + NET_REQUEST_WIFI_CMD_BTWT, /** Get power save config */ NET_REQUEST_WIFI_CMD_PS_CONFIG, /** Set or get regulatory domain */ @@ -194,6 +196,11 @@ NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_PS); NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_TWT); +#define NET_REQUEST_WIFI_BTWT \ + (_NET_WIFI_BASE | NET_REQUEST_WIFI_CMD_BTWT) + +NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_BTWT); + /** Request a Wi-Fi power save configuration */ #define NET_REQUEST_WIFI_PS_CONFIG \ (_NET_WIFI_BASE | NET_REQUEST_WIFI_CMD_PS_CONFIG) @@ -747,7 +754,30 @@ struct wifi_twt_params { * prepare the data before TWT SP starts. */ uint32_t twt_wake_ahead_duration; + /** TWT info enabled or disable */ + bool twt_info_disable; + /** TWT exponent */ + uint8_t twt_exponent; + /** TWT Mantissa Range: [0-sizeof(UINT16)] */ + uint16_t twt_mantissa; } setup; + /** Setup specific parameters */ + struct { + /** Broadcast TWT AP config */ + uint16_t sub_id; + /** Range 64-255 */ + uint8_t nominal_wake; + /** Max STA support */ + uint8_t max_sta_support; + /** TWT mantissa */ + uint16_t twt_mantissa; + /** TWT offset */ + uint16_t twt_offset; + /** TWT exponent */ + uint8_t twt_exponent; + /** SP gap */ + uint8_t sp_gap; + } btwt; /** Teardown specific parameters */ struct { /** Teardown all flows */ @@ -766,6 +796,7 @@ struct wifi_twt_params { /* 256 (u8) * 1TU */ #define WIFI_MAX_TWT_WAKE_INTERVAL_US 262144 #define WIFI_MAX_TWT_WAKE_AHEAD_DURATION_US (LONG_MAX - 1) +#define WIFI_MAX_TWT_EXPONENT 31 /** @endcond */ @@ -1362,6 +1393,14 @@ struct wifi_mgmt_ops { * @return 0 if ok, < 0 if error */ int (*set_twt)(const struct device *dev, struct wifi_twt_params *params); + /** Setup BTWT flow + * + * @param dev Pointer to the device structure for the driver instance. + * @param params BTWT parameters + * + * @return 0 if ok, < 0 if error + */ + int (*set_btwt)(const struct device *dev, struct wifi_twt_params *params); /** Get power save config * * @param dev Pointer to the device structure for the driver instance. diff --git a/modules/hostap/src/supp_api.c b/modules/hostap/src/supp_api.c index 952fb8a652cb..374b4cb22c53 100644 --- a/modules/hostap/src/supp_api.c +++ b/modules/hostap/src/supp_api.c @@ -1801,6 +1801,18 @@ int supplicant_set_twt(const struct device *dev, struct wifi_twt_params *params) return wifi_mgmt_api->set_twt(dev, params); } +int supplicant_set_btwt(const struct device *dev, struct wifi_twt_params *params) +{ + const struct wifi_mgmt_ops *const wifi_mgmt_api = get_wifi_mgmt_api(dev); + + if (!wifi_mgmt_api || !wifi_mgmt_api->set_btwt) { + wpa_printf(MSG_ERROR, "Set Broadcast TWT not supported"); + return -ENOTSUP; + } + + return wifi_mgmt_api->set_btwt(dev, params); +} + int supplicant_get_power_save_config(const struct device *dev, struct wifi_ps_config *config) { diff --git a/modules/hostap/src/supp_api.h b/modules/hostap/src/supp_api.h index ab1e6c014eb1..f817e2939a97 100644 --- a/modules/hostap/src/supp_api.h +++ b/modules/hostap/src/supp_api.h @@ -156,6 +156,15 @@ int supplicant_set_power_save(const struct device *dev, struct wifi_ps_params *p */ int supplicant_set_twt(const struct device *dev, struct wifi_twt_params *params); +/** + * @brief Set Wi-Fi BTWT parameters + * + * @param dev Wi-Fi interface name to use + * @param params BTWT parameters to set + * @return 0 for OK; -1 for ERROR + */ +int supplicant_set_btwt(const struct device *dev, struct wifi_twt_params *params); + /** * @brief Get Wi-Fi power save configuration * diff --git a/modules/hostap/src/supp_main.c b/modules/hostap/src/supp_main.c index da6c4f01c81e..0dd6509a4fcd 100644 --- a/modules/hostap/src/supp_main.c +++ b/modules/hostap/src/supp_main.c @@ -114,6 +114,7 @@ static const struct wifi_mgmt_ops mgmt_ap_ops = { #ifdef CONFIG_WIFI_NM_HOSTAPD_CRYPTO_ENTERPRISE .enterprise_creds = supplicant_add_enterprise_creds, #endif + .set_btwt = supplicant_set_btwt, }; DEFINE_WIFI_NM_INSTANCE(hostapd, &mgmt_ap_ops); diff --git a/subsys/net/l2/wifi/wifi_mgmt.c b/subsys/net/l2/wifi/wifi_mgmt.c index 28100693b012..db758cb2b6cf 100644 --- a/subsys/net/l2/wifi/wifi_mgmt.c +++ b/subsys/net/l2/wifi/wifi_mgmt.c @@ -916,6 +916,35 @@ static int wifi_set_twt(uint32_t mgmt_request, struct net_if *iface, NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_WIFI_TWT, wifi_set_twt); +static int wifi_set_btwt(uint32_t mgmt_request, struct net_if *iface, + void *data, size_t len) +{ + const struct device *dev = net_if_get_device(iface); + const struct wifi_mgmt_ops *const wifi_mgmt_api = get_wifi_api(iface); + struct wifi_twt_params *twt_params = data; + struct wifi_iface_status info = { 0 }; + + if (wifi_mgmt_api == NULL || wifi_mgmt_api->set_btwt == NULL) { + twt_params->fail_reason = + WIFI_TWT_FAIL_OPERATION_NOT_SUPPORTED; + return -ENOTSUP; + } + + if (net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, iface, &info, + sizeof(struct wifi_iface_status))) { + twt_params->fail_reason = + WIFI_TWT_FAIL_UNABLE_TO_GET_IFACE_STATUS; + goto fail; + } + + return wifi_mgmt_api->set_btwt(dev, twt_params); +fail: + return -ENOEXEC; + +} + +NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_WIFI_BTWT, wifi_set_btwt); + void wifi_mgmt_raise_twt_event(struct net_if *iface, struct wifi_twt_params *twt_params) { net_mgmt_event_notify_with_info(NET_EVENT_WIFI_TWT, diff --git a/subsys/net/l2/wifi/wifi_shell.c b/subsys/net/l2/wifi/wifi_shell.c index 207542e07796..6c5716c23630 100644 --- a/subsys/net/l2/wifi/wifi_shell.c +++ b/subsys/net/l2/wifi/wifi_shell.c @@ -29,6 +29,7 @@ LOG_MODULE_REGISTER(net_wifi_shell, LOG_LEVEL_INF); #include #include "net_shell_private.h" +#include #ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_CRYPTO_ENTERPRISE static const char ca_cert_test[] = { #include @@ -1524,6 +1525,10 @@ static int cmd_wifi_twt_setup_quick(const struct shell *sh, size_t argc, struct wifi_twt_params params = { 0 }; int idx = 1; long value; + double twt_mantissa_scale = 0.0; + double twt_interval_scale = 0.0; + uint16_t scale = 1000; + int exponent = 0; context.sh = sh; @@ -1548,6 +1553,13 @@ static int cmd_wifi_twt_setup_quick(const struct shell *sh, size_t argc, } params.setup.twt_interval = (uint64_t)value; + /* control the region of mantissa filed */ + twt_interval_scale = (double)(params.setup.twt_interval / scale); + /* derive mantissa and exponent from interval */ + twt_mantissa_scale = frexp(twt_interval_scale, &exponent); + params.setup.twt_mantissa = ceil(twt_mantissa_scale * scale); + params.setup.twt_exponent = exponent; + if (net_mgmt(NET_REQUEST_WIFI_TWT, iface, ¶ms, sizeof(params))) { PR_WARNING("%s with %s failed, reason : %s\n", wifi_twt_operation_txt(params.operation), @@ -1564,74 +1576,236 @@ static int cmd_wifi_twt_setup_quick(const struct shell *sh, size_t argc, return 0; } -static int cmd_wifi_twt_setup(const struct shell *sh, size_t argc, - char *argv[]) +static int cmd_wifi_btwt_setup(const struct shell *sh, size_t argc, char *argv[]) { - struct net_if *iface = net_if_get_wifi_sta(); - struct wifi_twt_params params = { 0 }; + struct net_if *iface = net_if_get_wifi_sap(); + struct wifi_twt_params params = {0}; int idx = 1; long value; + int ret = 0; context.sh = sh; - params.operation = WIFI_TWT_SETUP; + params.btwt.sub_id = (uint16_t)shell_strtol(argv[idx++], 10, &ret); + params.btwt.nominal_wake = (uint8_t)shell_strtol(argv[idx++], 10, &ret); + params.btwt.max_sta_support = (uint8_t)shell_strtol(argv[idx++], 10, &ret); - if (!parse_number(sh, &value, argv[idx++], NULL, WIFI_TWT_INDIVIDUAL, - WIFI_TWT_WAKE_TBTT)) { + if (!parse_number(sh, &value, argv[idx++], NULL, 1, 0xFFFF)) { return -EINVAL; } - params.negotiation_type = (enum wifi_twt_negotiation_type)value; + params.btwt.twt_mantissa = (uint16_t)value; - if (!parse_number(sh, &value, argv[idx++], NULL, WIFI_TWT_SETUP_CMD_REQUEST, - WIFI_TWT_SETUP_CMD_DEMAND)) { - return -EINVAL; - } - params.setup_cmd = (enum wifi_twt_setup_cmd)value; + params.btwt.twt_offset = (uint16_t)shell_strtol(argv[idx++], 10, &ret); - if (!parse_number(sh, &value, argv[idx++], NULL, 1, 255)) { + if (!parse_number(sh, &value, argv[idx++], NULL, 0, WIFI_MAX_TWT_EXPONENT)) { return -EINVAL; } - params.dialog_token = (uint8_t)value; + params.btwt.twt_exponent = (uint8_t)value; - if (!parse_number(sh, &value, argv[idx++], NULL, 0, (WIFI_MAX_TWT_FLOWS - 1))) { - return -EINVAL; - } - params.flow_id = (uint8_t)value; + params.btwt.sp_gap = (uint8_t)shell_strtol(argv[idx++], 10, &ret); - if (!parse_number(sh, &value, argv[idx++], NULL, 0, 1)) { + if (ret) { + PR_ERROR("Invalid argument (ret %d)\n", ret); return -EINVAL; } - params.setup.responder = (bool)value; - if (!parse_number(sh, &value, argv[idx++], NULL, 0, 1)) { - return -EINVAL; - } - params.setup.trigger = (bool)value; + if (net_mgmt(NET_REQUEST_WIFI_BTWT, iface, ¶ms, sizeof(params))) { + PR_WARNING("Failed reason : %s\n", + wifi_twt_get_err_code_str(params.fail_reason)); - if (!parse_number(sh, &value, argv[idx++], NULL, 0, 1)) { - return -EINVAL; + return -ENOEXEC; } - params.setup.implicit = (bool)value; - if (!parse_number(sh, &value, argv[idx++], NULL, 0, 1)) { - return -EINVAL; + PR("BTWT setup\n"); + + return 0; +} + +static int twt_args_to_params(const struct shell *sh, size_t argc, char *argv[], + struct wifi_twt_params *params) +{ + int opt; + int opt_index = 0; + struct getopt_state *state; + long value; + double twt_mantissa_scale = 0.0; + double twt_interval_scale = 0.0; + uint16_t scale = 1000; + int exponent = 0; + static const struct option long_options[] = { + {"negotiation-type", required_argument, 0, 'n'}, + {"setup-cmd", required_argument, 0, 'c'}, + {"dialog-token", required_argument, 0, 't'}, + {"flow-id", required_argument, 0, 'f'}, + {"responder", required_argument, 0, 'r'}, + {"trigger", required_argument, 0, 'T'}, + {"implicit", required_argument, 0, 'I'}, + {"announce", required_argument, 0, 'a'}, + {"wake-interval", required_argument, 0, 'w'}, + {"interval", required_argument, 0, 'i'}, + {"wake-ahead-duration", required_argument, 0, 'D'}, + {"info-disable", required_argument, 0, 'd'}, + {"exponent", required_argument, 0, 'e'}, + {"mantissa", required_argument, 0, 'm'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0}}; + + params->operation = WIFI_TWT_SETUP; + + while ((opt = getopt_long(argc, argv, "n:c:t:f:r:T:I:a:t:w:i:D:d:e:m:h", + long_options, &opt_index)) != -1) { + state = getopt_state_get(); + switch (opt) { + case 'n': + if (!parse_number(sh, &value, state->optarg, NULL, + WIFI_TWT_INDIVIDUAL, + WIFI_TWT_WAKE_TBTT)) { + return -EINVAL; + } + params->negotiation_type = (enum wifi_twt_negotiation_type)value; + break; + + case 'c': + if (!parse_number(sh, &value, state->optarg, NULL, + WIFI_TWT_SETUP_CMD_REQUEST, + WIFI_TWT_SETUP_CMD_DEMAND)) { + return -EINVAL; + } + params->setup_cmd = (enum wifi_twt_setup_cmd)value; + break; + + case 't': + if (!parse_number(sh, &value, state->optarg, NULL, 1, 255)) { + return -EINVAL; + } + params->dialog_token = (uint8_t)value; + break; + + case 'f': + if (!parse_number(sh, &value, state->optarg, NULL, 0, + (WIFI_MAX_TWT_FLOWS - 1))) { + return -EINVAL; + } + params->flow_id = (uint8_t)value; + break; + + case 'r': + if (!parse_number(sh, &value, state->optarg, NULL, 0, 1)) { + return -EINVAL; + } + params->setup.responder = (bool)value; + break; + + case 'T': + if (!parse_number(sh, &value, state->optarg, NULL, 0, 1)) { + return -EINVAL; + } + params->setup.trigger = (bool)value; + break; + + case 'I': + if (!parse_number(sh, &value, state->optarg, NULL, 0, 1)) { + return -EINVAL; + } + params->setup.implicit = (bool)value; + break; + + case 'a': + if (!parse_number(sh, &value, state->optarg, NULL, 0, 1)) { + return -EINVAL; + } + params->setup.announce = (bool)value; + break; + + case 'w': + if (!parse_number(sh, &value, state->optarg, NULL, 1, + WIFI_MAX_TWT_WAKE_INTERVAL_US)) { + return -EINVAL; + } + params->setup.twt_wake_interval = (uint32_t)value; + break; + + case 'i': + if (!parse_number(sh, &value, state->optarg, NULL, 1, + WIFI_MAX_TWT_INTERVAL_US)) { + return -EINVAL; + } + params->setup.twt_interval = (uint64_t)value; + break; + + case 'D': + if (!parse_number(sh, &value, state->optarg, NULL, 0, + WIFI_MAX_TWT_WAKE_AHEAD_DURATION_US)) { + return -EINVAL; + } + params->setup.twt_wake_ahead_duration = (uint32_t)value; + break; + + case 'd': + if (!parse_number(sh, &value, state->optarg, NULL, 0, 1)) { + return -EINVAL; + } + params->setup.twt_info_disable = (bool)value; + break; + + case 'e': + if (!parse_number(sh, &value, state->optarg, NULL, 0, + WIFI_MAX_TWT_EXPONENT)) { + return -EINVAL; + } + params->setup.twt_exponent = (uint8_t)value; + break; + + case 'm': + if (!parse_number(sh, &value, state->optarg, NULL, 0, 0xFFFF)) { + return -EINVAL; + } + params->setup.twt_mantissa = (uint16_t)value; + break; + + case 'h': + return -ENOEXEC; + } } - params.setup.announce = (bool)value; - if (!parse_number(sh, &value, argv[idx++], NULL, 1, WIFI_MAX_TWT_WAKE_INTERVAL_US)) { + if ((params->setup.twt_interval != 0) && + ((params->setup.twt_exponent != 0) || + (params->setup.twt_mantissa != 0))) { + PR_ERROR("Only one of TWT internal or (mantissa, exponent) should be used\n"); return -EINVAL; } - params.setup.twt_wake_interval = (uint32_t)value; - if (!parse_number(sh, &value, argv[idx++], NULL, 1, WIFI_MAX_TWT_INTERVAL_US)) { + if (params->setup.twt_interval) { + /* control the region of mantissa filed */ + twt_interval_scale = (double)(params->setup.twt_interval / scale); + /* derive mantissa and exponent from interval */ + twt_mantissa_scale = frexp(twt_interval_scale, &exponent); + params->setup.twt_mantissa = ceil(twt_mantissa_scale * scale); + params->setup.twt_exponent = exponent; + } else if ((params->setup.twt_exponent != 0) || + (params->setup.twt_mantissa != 0)) { + params->setup.twt_interval = floor(ldexp(params->setup.twt_mantissa, + params->setup.twt_exponent)); + } else { + PR_ERROR("Either TWT interval or (mantissa, exponent) is needed\n"); return -EINVAL; } - params.setup.twt_interval = (uint64_t)value; - if (!parse_number(sh, &value, argv[idx++], NULL, 0, WIFI_MAX_TWT_WAKE_AHEAD_DURATION_US)) { - return -EINVAL; + return 0; +} + +static int cmd_wifi_twt_setup(const struct shell *sh, size_t argc, + char *argv[]) +{ + struct net_if *iface = net_if_get_wifi_sta(); + struct wifi_twt_params params = { 0 }; + + context.sh = sh; + + if (twt_args_to_params(sh, argc, argv, ¶ms)) { + shell_help(sh); + return -ENOEXEC; } - params.setup.twt_wake_ahead_duration = (uint32_t)value; if (net_mgmt(NET_REQUEST_WIFI_TWT, iface, ¶ms, sizeof(params))) { PR_WARNING("%s with %s failed. reason : %s\n", @@ -3241,13 +3415,30 @@ SHELL_STATIC_SUBCMD_SET_CREATE(wifi_twt_ops, cmd_wifi_twt_setup_quick, 3, 0), SHELL_CMD_ARG(setup, NULL, " Start a TWT flow:\n" - "\n" - "\n" - " " - " .\n" - ": 0us-2^31us>\n", + "<-n --negotiation-type>: 0: Individual, 1: Broadcast, 2: Wake TBTT\n" + "<-c --setup-cmd>: 0: Request, 1: Suggest, 2: Demand\n" + "<-t --dialog-token>: 1-255\n" + "<-f --flow-id>: 0-7\n" + "<-r --responder>: 0/1\n" + "<-T --trigger>: 0/1\n" + "<-I --implicit>:0/1\n" + "<-a --announce>: 0/1\n" + "<-w --wake-interval>: 1-262144us\n" + "<-i --interval>: 1us-2^31us\n" + "<-D --wake-ahead-duration>: 0us-2^31us\n" + "<-d --info-disable>: 0/1\n" + "<-e --exponent>: 0-31\n" + "<-m --mantissa>: 1-2^16\n" + "[-h, --help]: Print out command usage.\n", cmd_wifi_twt_setup, - 12, 0), + 25, 5), + SHELL_CMD_ARG( + btwt_setup, NULL, + " Start a BTWT flow:\n" + " " + " .\n", + cmd_wifi_btwt_setup, + 8, 0), SHELL_CMD_ARG(teardown, NULL, " Teardown a TWT flow:\n" "\n" "\n"