From d796f23e0ee7b720f76577a0a99bbd298f5fcaf8 Mon Sep 17 00:00:00 2001 From: Krishna T Date: Thu, 3 Nov 2022 17:19:53 +0530 Subject: [PATCH] net: wifi_mgmt: Add support for power save configuration Add support for configuring power-save in Wi-Fi chipsets, supports Legacy, WMM and TWT. Signed-off-by: Krishna T --- include/zephyr/net/wifi.h | 71 +++++++ include/zephyr/net/wifi_mgmt.h | 93 +++++++++ subsys/net/l2/wifi/wifi_mgmt.c | 79 ++++++++ subsys/net/l2/wifi/wifi_shell.c | 337 +++++++++++++++++++++++++++++++- 4 files changed, 579 insertions(+), 1 deletion(-) diff --git a/include/zephyr/net/wifi.h b/include/zephyr/net/wifi.h index e6107bef556..44e8f39eb9f 100644 --- a/include/zephyr/net/wifi.h +++ b/include/zephyr/net/wifi.h @@ -245,4 +245,75 @@ static inline const char *wifi_link_mode_txt(enum wifi_link_mode link_mode) } } +enum wifi_ps { + WIFI_PS_DISABLED = 0, + WIFI_PS_ENABLED, +}; + +static const char * const wifi_ps2str[] = { + [WIFI_PS_DISABLED] = "Power save disabled", + [WIFI_PS_ENABLED] = "Power save enabled", +}; + +enum wifi_ps_mode { + WIFI_PS_MODE_LEGACY = 0, + /* This has to be configured before connecting to the AP, + * as support for ADDTS action frames is not available. + */ + WIFI_PS_MODE_WMM, +}; + +static const char * const wifi_ps_mode2str[] = { + [WIFI_PS_MODE_LEGACY] = "Legacy power save", + [WIFI_PS_MODE_WMM] = "WMM power save", +}; + +enum wifi_twt_operation { + WIFI_TWT_SETUP = 0, + WIFI_TWT_TEARDOWN, +}; + +static const char * const wifi_twt_operation2str[] = { + [WIFI_TWT_SETUP] = "TWT setup", + [WIFI_TWT_TEARDOWN] = "TWT teardown", +}; + +enum wifi_twt_negotiation_type { + WIFI_TWT_INDIVIDUAL = 0, + WIFI_TWT_BROADCAST, + WIFI_TWT_WAKE_TBTT +}; + +static const char * const wifi_twt_negotiation_type2str[] = { + [WIFI_TWT_INDIVIDUAL] = "TWT individual negotiation", + [WIFI_TWT_BROADCAST] = "TWT broadcast negotiation", + [WIFI_TWT_WAKE_TBTT] = "TWT wake TBTT negotiation", +}; + +enum wifi_twt_setup_cmd { + /* TWT Requests */ + WIFI_TWT_SETUP_CMD_REQUEST = 0, + WIFI_TWT_SETUP_CMD_SUGGEST, + WIFI_TWT_SETUP_CMD_DEMAND, + /* TWT Responses */ + WIFI_TWT_SETUP_CMD_GROUPING, + WIFI_TWT_SETUP_CMD_ACCEPT, + WIFI_TWT_SETUP_CMD_ALTERNATE, + WIFI_TWT_SETUP_CMD_DICTATE, + WIFI_TWT_SETUP_CMD_REJECT, +}; + +static const char * const wifi_twt_setup_cmd2str[] = { + /* TWT Requests */ + [WIFI_TWT_SETUP_CMD_REQUEST] = "TWT request", + [WIFI_TWT_SETUP_CMD_SUGGEST] = "TWT suggest", + [WIFI_TWT_SETUP_CMD_DEMAND] = "TWT demand", + /* TWT Responses */ + [WIFI_TWT_SETUP_CMD_GROUPING] = "TWT grouping", + [WIFI_TWT_SETUP_CMD_ACCEPT] = "TWT accept", + [WIFI_TWT_SETUP_CMD_ALTERNATE] = "TWT alternate", + [WIFI_TWT_SETUP_CMD_DICTATE] = "TWT dictate", + [WIFI_TWT_SETUP_CMD_REJECT] = "TWT reject", +}; + #endif /* ZEPHYR_INCLUDE_NET_WIFI_H_ */ diff --git a/include/zephyr/net/wifi_mgmt.h b/include/zephyr/net/wifi_mgmt.h index ff42d7bfbf3..924f4630d30 100644 --- a/include/zephyr/net/wifi_mgmt.h +++ b/include/zephyr/net/wifi_mgmt.h @@ -36,6 +36,10 @@ enum net_request_wifi_cmd { NET_REQUEST_WIFI_CMD_AP_ENABLE, NET_REQUEST_WIFI_CMD_AP_DISABLE, NET_REQUEST_WIFI_CMD_IFACE_STATUS, + NET_REQUEST_WIFI_CMD_PS, + NET_REQUEST_WIFI_CMD_PS_MODE, + NET_REQUEST_WIFI_CMD_TWT, + NET_REQUEST_WIFI_CMD_PS_CONFIG, }; #define NET_REQUEST_WIFI_SCAN \ @@ -68,12 +72,33 @@ NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_AP_DISABLE); NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_IFACE_STATUS); +#define NET_REQUEST_WIFI_PS \ + (_NET_WIFI_BASE | NET_REQUEST_WIFI_CMD_PS) + +NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_PS); + +#define NET_REQUEST_WIFI_PS_MODE \ + (_NET_WIFI_BASE | NET_REQUEST_WIFI_CMD_PS_MODE) + +NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_PS_MODE); + +#define NET_REQUEST_WIFI_TWT \ + (_NET_WIFI_BASE | NET_REQUEST_WIFI_CMD_TWT) + +NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_TWT); + +#define NET_REQUEST_WIFI_PS_CONFIG \ + (_NET_WIFI_BASE | NET_REQUEST_WIFI_CMD_PS_CONFIG) + +NET_MGMT_DEFINE_REQUEST_HANDLER(NET_REQUEST_WIFI_PS_CONFIG); + enum net_event_wifi_cmd { NET_EVENT_WIFI_CMD_SCAN_RESULT = 1, NET_EVENT_WIFI_CMD_SCAN_DONE, NET_EVENT_WIFI_CMD_CONNECT_RESULT, NET_EVENT_WIFI_CMD_DISCONNECT_RESULT, NET_EVENT_WIFI_CMD_IFACE_STATUS, + NET_EVENT_WIFI_CMD_TWT, }; #define NET_EVENT_WIFI_SCAN_RESULT \ @@ -91,6 +116,8 @@ enum net_event_wifi_cmd { #define NET_EVENT_WIFI_IFACE_STATUS \ (_NET_WIFI_EVENT | NET_EVENT_WIFI_CMD_IFACE_STATUS) +#define NET_EVENT_WIFI_TWT \ + (_NET_WIFI_EVENT | NET_EVENT_WIFI_CMD_TWT) /* Each result is provided to the net_mgmt_event_callback * via its info attribute (see net_mgmt.h) @@ -144,6 +171,66 @@ struct wifi_iface_status { int rssi; }; +struct wifi_ps_params { + enum wifi_ps enabled; +}; + +struct wifi_ps_mode_params { + enum wifi_ps_mode mode; +}; + +struct wifi_twt_params { + enum wifi_twt_operation operation; + enum wifi_twt_negotiation_type negotiation_type; + enum wifi_twt_setup_cmd setup_cmd; + /* Map requests to responses */ + uint8_t dialog_token; + /* Map setup with teardown */ + uint8_t flow_id; + union { + struct { + /* Interval = Wake up time + Sleeping time */ + uint32_t twt_interval_ms; + bool responder; + bool trigger; + bool implicit; + bool announce; + /* Wake up time */ + uint8_t twt_wake_interval_ms; + } setup; + struct { + /* Only for Teardown */ + bool teardown_all; + } teardown; + }; +}; + +/* Flow ID is only 3 bits */ +#define WIFI_MAX_TWT_FLOWS 8 +#define WIFI_MAX_TWT_INTERVAL_MS 0x7FFFFFFF +struct wifi_twt_flow_info { + /* Interval = Wake up time + Sleeping time */ + uint32_t twt_interval_ms; + /* Map requests to responses */ + uint8_t dialog_token; + /* Map setup with teardown */ + uint8_t flow_id; + enum wifi_twt_negotiation_type negotiation_type; + bool responder; + bool trigger; + bool implicit; + bool announce; + /* Wake up time */ + uint8_t twt_wake_interval_ms; +}; + +struct wifi_ps_config { + struct wifi_twt_flow_info twt_flows[WIFI_MAX_TWT_FLOWS]; + bool enabled; + enum wifi_ps_mode mode; + char num_twt_flows; +}; + #include typedef void (*scan_result_cb_t)(struct net_if *iface, int status, @@ -177,6 +264,10 @@ struct net_wifi_mgmt_offload { #ifdef CONFIG_NET_STATISTICS_WIFI int (*get_stats)(const struct device *dev, struct net_stats_wifi *stats); #endif /* CONFIG_NET_STATISTICS_WIFI */ + int (*set_power_save)(const struct device *dev, struct wifi_ps_params *params); + int (*set_power_save_mode)(const struct device *dev, struct wifi_ps_mode_params *params); + int (*set_twt)(const struct device *dev, struct wifi_twt_params *params); + int (*get_power_save_config)(const struct device *dev, struct wifi_ps_config *config); }; /* Make sure that the network interface API is properly setup inside @@ -188,6 +279,8 @@ void wifi_mgmt_raise_connect_result_event(struct net_if *iface, int status); void wifi_mgmt_raise_disconnect_result_event(struct net_if *iface, int status); void wifi_mgmt_raise_iface_status_event(struct net_if *iface, struct wifi_iface_status *iface_status); +void wifi_mgmt_raise_twt_event(struct net_if *iface, + struct wifi_twt_params *twt_params); #ifdef __cplusplus } #endif diff --git a/subsys/net/l2/wifi/wifi_mgmt.c b/subsys/net/l2/wifi/wifi_mgmt.c index 92aa47a9bbd..d76892c7823 100644 --- a/subsys/net/l2/wifi/wifi_mgmt.c +++ b/subsys/net/l2/wifi/wifi_mgmt.c @@ -228,3 +228,82 @@ static int wifi_iface_stats(uint32_t mgmt_request, struct net_if *iface, } NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_STATS_GET_WIFI, wifi_iface_stats); #endif /* CONFIG_NET_STATISTICS_WIFI */ + +static int wifi_set_power_save(uint32_t mgmt_request, struct net_if *iface, + void *data, size_t len) +{ + const struct device *dev = net_if_get_device(iface); + struct net_wifi_mgmt_offload *off_api = + (struct net_wifi_mgmt_offload *) dev->api; + struct wifi_ps_params *ps_params = data; + + if (off_api == NULL || off_api->set_power_save == NULL) { + return -ENOTSUP; + } + + return off_api->set_power_save(dev, ps_params); +} + +NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_WIFI_PS, wifi_set_power_save); + +static int wifi_get_power_save_config(uint32_t mgmt_request, struct net_if *iface, + void *data, size_t len) +{ + const struct device *dev = net_if_get_device(iface); + struct net_wifi_mgmt_offload *off_api = + (struct net_wifi_mgmt_offload *) dev->api; + struct wifi_ps_config *ps_config = data; + + if (off_api == NULL || off_api->get_power_save_config == NULL) { + return -ENOTSUP; + } + + if (!data || len != sizeof(*ps_config)) { + return -EINVAL; + } + + return off_api->get_power_save_config(dev, ps_config); +} + +NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_WIFI_PS_CONFIG, wifi_get_power_save_config); + +static int wifi_set_power_save_mode(uint32_t mgmt_request, struct net_if *iface, + void *data, size_t len) +{ + const struct device *dev = net_if_get_device(iface); + struct net_wifi_mgmt_offload *off_api = + (struct net_wifi_mgmt_offload *) dev->api; + struct wifi_ps_mode_params *ps_mode_params = data; + + if (off_api == NULL || off_api->set_power_save_mode == NULL) { + return -ENOTSUP; + } + + return off_api->set_power_save_mode(dev, ps_mode_params); +} + +NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_WIFI_PS_MODE, wifi_set_power_save_mode); + +static int wifi_set_twt(uint32_t mgmt_request, struct net_if *iface, + void *data, size_t len) +{ + const struct device *dev = net_if_get_device(iface); + struct net_wifi_mgmt_offload *off_api = + (struct net_wifi_mgmt_offload *) dev->api; + struct wifi_twt_params *twt_params = data; + + if (off_api == NULL || off_api->set_twt == NULL) { + return -ENOTSUP; + } + + return off_api->set_twt(dev, twt_params); +} + +NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_WIFI_TWT, wifi_set_twt); + +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, + iface, twt_params, + sizeof(struct wifi_twt_params)); +} diff --git a/subsys/net/l2/wifi/wifi_shell.c b/subsys/net/l2/wifi/wifi_shell.c index 66afab8eec7..3e6a61529b6 100644 --- a/subsys/net/l2/wifi/wifi_shell.c +++ b/subsys/net/l2/wifi/wifi_shell.c @@ -29,7 +29,8 @@ LOG_MODULE_REGISTER(net_wifi_shell, LOG_LEVEL_INF); #define WIFI_SHELL_MGMT_EVENTS (NET_EVENT_WIFI_SCAN_RESULT | \ NET_EVENT_WIFI_SCAN_DONE | \ NET_EVENT_WIFI_CONNECT_RESULT | \ - NET_EVENT_WIFI_DISCONNECT_RESULT) + NET_EVENT_WIFI_DISCONNECT_RESULT | \ + NET_EVENT_WIFI_TWT) static struct { const struct shell *sh; @@ -58,6 +59,24 @@ static struct net_mgmt_event_callback wifi_shell_mgmt_cb; } \ } while (false) +static bool parse_number(const struct shell *sh, long *param, char *str, int min, int max) +{ + char *endptr; + char *str_tmp = str; + long num = strtol(str_tmp, &endptr, 10); + + if (*endptr != '\0') { + print(sh, SHELL_ERROR, "Invalid number: %s", str_tmp); + return false; + } + if ((num) < (min) || (num) > (max)) { + print(sh, SHELL_WARNING, "Value out of range: %s, (%d-%d)", str_tmp, min, max); + return false; + } + *param = num; + return true; +} + static void handle_wifi_scan_result(struct net_mgmt_event_callback *cb) { const struct wifi_scan_result *entry = @@ -129,6 +148,24 @@ static void handle_wifi_disconnect_result(struct net_mgmt_event_callback *cb) } } +static void handle_wifi_twt_event(struct net_mgmt_event_callback *cb) +{ + const struct wifi_twt_params *resp = + (const struct wifi_twt_params *)cb->info; + + print(context.sh, SHELL_NORMAL, "TWT response: %s for dialog: %d and flow: %d\n", + wifi_twt_setup_cmd2str[resp->setup_cmd], resp->dialog_token, resp->flow_id); + + /* If accepted, then no need to print TWT params */ + if (resp->setup_cmd != WIFI_TWT_SETUP_CMD_ACCEPT) { + print(context.sh, SHELL_NORMAL, + "TWT parameters: trigger: %s wake_interval_ms: %d, interval_ms: %d\n", + resp->setup.trigger ? "trigger" : "no_trigger", + resp->setup.twt_wake_interval_ms, + resp->setup.twt_interval_ms); + } +} + static void wifi_mgmt_event_handler(struct net_mgmt_event_callback *cb, uint32_t mgmt_event, struct net_if *iface) { @@ -145,6 +182,9 @@ static void wifi_mgmt_event_handler(struct net_mgmt_event_callback *cb, case NET_EVENT_WIFI_DISCONNECT_RESULT: handle_wifi_disconnect_result(cb); break; + case NET_EVENT_WIFI_TWT: + handle_wifi_twt_event(cb); + break; default: break; } @@ -383,6 +423,277 @@ static int cmd_wifi_stats(const struct shell *sh, size_t argc, char *argv[]) return 0; } +static int cmd_wifi_ps(const struct shell *sh, size_t argc, char *argv[]) +{ + struct net_if *iface = net_if_get_default(); + struct wifi_ps_params params = { 0 }; + + context.sh = sh; + + if (argc > 2) { + shell_fprintf(sh, SHELL_WARNING, "Invalid number of arguments\n"); + return -ENOEXEC; + } + + if (argc == 1) { + struct wifi_ps_config config = { 0 }; + + if (net_mgmt(NET_REQUEST_WIFI_PS_CONFIG, iface, + &config, sizeof(config))) { + shell_fprintf(sh, SHELL_WARNING, "Failed to get PS config\n"); + return -ENOEXEC; + } + + shell_fprintf(sh, SHELL_NORMAL, "PS status: %s\n", + wifi_ps_mode2str[config.enabled]); + if (config.enabled) { + shell_fprintf(sh, SHELL_NORMAL, "PS mode: %s\n", + wifi_ps_mode2str[config.mode]); + } + + if (config.num_twt_flows == 0) { + shell_fprintf(sh, SHELL_NORMAL, "No TWT flows\n"); + } else { + for (int i = 0; i < config.num_twt_flows; i++) { + shell_fprintf(sh, SHELL_NORMAL, "TWT Dialog token: %d\n", + config.twt_flows[i].dialog_token); + shell_fprintf(sh, SHELL_NORMAL, "TWT flow ID: %d\n", + config.twt_flows[i].flow_id); + shell_fprintf(sh, SHELL_NORMAL, "TWT negotiation type: %s\n", + wifi_twt_negotiation_type2str[ + config.twt_flows[i].negotiation_type]); + shell_fprintf(sh, SHELL_NORMAL, "TWT responder: %s\n", + config.twt_flows[i].responder ? "true" : "false"); + shell_fprintf(sh, SHELL_NORMAL, "TWT implicit: %s\n", + config.twt_flows[i].implicit ? "true" : "false"); + shell_fprintf(sh, SHELL_NORMAL, "TWT trigger: %s\n", + config.twt_flows[i].trigger ? "true" : "false"); + shell_fprintf(sh, SHELL_NORMAL, "TWT announce: %s\n", + config.twt_flows[i].announce ? "true" : "false"); + shell_fprintf(sh, SHELL_NORMAL, "TWT wake interval: %d ms\n", + config.twt_flows[i].twt_wake_interval_ms); + shell_fprintf(sh, SHELL_NORMAL, "TWT interval: %d ms\n", + config.twt_flows[i].twt_interval_ms); + shell_fprintf(sh, SHELL_NORMAL, "========================\n"); + } + } + return 0; + } + + if (!strncmp(argv[1], "on", 2)) { + params.enabled = WIFI_PS_ENABLED; + } else if (!strncmp(argv[1], "off", 3)) { + params.enabled = WIFI_PS_DISABLED; + } else { + shell_fprintf(sh, SHELL_WARNING, "Invalid argument\n"); + return -ENOEXEC; + } + + if (net_mgmt(NET_REQUEST_WIFI_PS, iface, ¶ms, sizeof(params))) { + shell_fprintf(sh, SHELL_WARNING, "Power save %s failed\n", + params.enabled ? "enable" : "disable"); + return -ENOEXEC; + } + + shell_fprintf(sh, SHELL_NORMAL, "%s\n", wifi_ps2str[params.enabled]); + + return 0; +} + +static int cmd_wifi_ps_mode(const struct shell *sh, size_t argc, char *argv[]) +{ + struct net_if *iface = net_if_get_default(); + struct wifi_ps_mode_params params = { 0 }; + + context.sh = sh; + + if (argc != 2) { + shell_fprintf(sh, SHELL_WARNING, "Invalid number of arguments\n"); + return -ENOEXEC; + } + + if (!strncmp(argv[1], "legacy", 6)) { + params.mode = WIFI_PS_MODE_LEGACY; + } else if (!strncmp(argv[1], "wmm", 3)) { + params.mode = WIFI_PS_MODE_WMM; + } else { + shell_fprintf(sh, SHELL_WARNING, "Invalid power save mode\n"); + return -ENOEXEC; + } + + if (net_mgmt(NET_REQUEST_WIFI_PS_MODE, iface, ¶ms, sizeof(params))) { + shell_fprintf(sh, SHELL_WARNING, "%s failed\n", wifi_ps_mode2str[params.mode]); + return -ENOEXEC; + } + + shell_fprintf(sh, SHELL_NORMAL, "%s\n", wifi_ps_mode2str[params.mode]); + + return 0; +} + +static int cmd_wifi_twt_setup_quick(const struct shell *sh, size_t argc, + char *argv[]) +{ + struct net_if *iface = net_if_get_default(); + struct wifi_twt_params params = { 0 }; + int idx = 1; + + context.sh = sh; + + if (argc != 3) { + shell_fprintf(sh, SHELL_WARNING, "Invalid number of arguments\n"); + shell_help(sh); + return -ENOEXEC; + } + + /* Sensible defaults */ + params.operation = WIFI_TWT_SETUP; + params.negotiation_type = WIFI_TWT_INDIVIDUAL; + params.setup_cmd = WIFI_TWT_SETUP_CMD_REQUEST; + params.dialog_token = 1; + params.flow_id = 1; + params.setup.responder = 0; + params.setup.implicit = 1; + params.setup.trigger = 1; + params.setup.announce = 0; + + if (!parse_number(sh, (long *)¶ms.setup.twt_wake_interval_ms, argv[idx++], 1, 255) || + !parse_number(sh, (long *)¶ms.setup.twt_interval_ms, argv[idx++], 1, + WIFI_MAX_TWT_INTERVAL_MS)) + return -EINVAL; + + if (net_mgmt(NET_REQUEST_WIFI_TWT, iface, ¶ms, sizeof(params))) { + shell_fprintf(sh, SHELL_WARNING, "%s with %s failed\n", + wifi_twt_operation2str[params.operation], + wifi_twt_negotiation_type2str[params.negotiation_type]); + return -ENOEXEC; + } + + shell_fprintf(sh, SHELL_NORMAL, "TWT operation %s with dg: %d, flow_id: %d requested\n", + wifi_twt_operation2str[params.operation], + params.dialog_token, params.flow_id); + + return 0; +} + + +static int cmd_wifi_twt_setup(const struct shell *sh, size_t argc, + char *argv[]) +{ + struct net_if *iface = net_if_get_default(); + struct wifi_twt_params params = { 0 }; + int idx = 1; + long neg_type; + long setup_cmd; + + context.sh = sh; + + if (argc != 11) { + shell_fprintf(sh, SHELL_WARNING, "Invalid number of arguments\n"); + shell_help(sh); + return -ENOEXEC; + } + + params.operation = WIFI_TWT_SETUP; + + if (!parse_number(sh, &neg_type, argv[idx++], WIFI_TWT_INDIVIDUAL, + WIFI_TWT_WAKE_TBTT) || + !parse_number(sh, &setup_cmd, argv[idx++], WIFI_TWT_SETUP_CMD_REQUEST, + WIFI_TWT_SETUP_CMD_DEMAND) || + !parse_number(sh, (long *)¶ms.dialog_token, argv[idx++], 1, 255) || + !parse_number(sh, (long *)¶ms.flow_id, argv[idx++], 1, WIFI_MAX_TWT_FLOWS) || + !parse_number(sh, (long *)¶ms.setup.responder, argv[idx++], 0, 1) || + !parse_number(sh, (long *)¶ms.setup.trigger, argv[idx++], 0, 1) || + !parse_number(sh, (long *)¶ms.setup.implicit, argv[idx++], 0, 1) || + !parse_number(sh, (long *)¶ms.setup.announce, argv[idx++], 0, 1) || + !parse_number(sh, (long *)¶ms.setup.twt_wake_interval_ms, argv[idx++], 1, 255) || + !parse_number(sh, (long *)¶ms.setup.twt_interval_ms, argv[idx++], 1, + WIFI_MAX_TWT_INTERVAL_MS)) + return -EINVAL; + + params.negotiation_type = neg_type; + params.setup_cmd = setup_cmd; + + if (net_mgmt(NET_REQUEST_WIFI_TWT, iface, ¶ms, sizeof(params))) { + shell_fprintf(sh, SHELL_WARNING, "%s with %s failed\n", + wifi_twt_operation2str[params.operation], + wifi_twt_negotiation_type2str[params.negotiation_type]); + return -ENOEXEC; + } + + shell_fprintf(sh, SHELL_NORMAL, "TWT operation %s with dg: %d, flow_id: %d requested\n", + wifi_twt_operation2str[params.operation], + params.dialog_token, params.flow_id); + + return 0; +} + +static int cmd_wifi_twt_teardown(const struct shell *sh, size_t argc, + char *argv[]) +{ + struct net_if *iface = net_if_get_default(); + struct wifi_twt_params params = { 0 }; + long neg_type = 0; + long setup_cmd = 0; + + context.sh = sh; + int idx = 1; + + if (argc != 5) { + shell_fprintf(sh, SHELL_WARNING, "Invalid number of arguments\n"); + shell_help(sh); + return -ENOEXEC; + } + + params.operation = WIFI_TWT_TEARDOWN; + neg_type = params.negotiation_type; + setup_cmd = params.setup_cmd; + + if (!parse_number(sh, &neg_type, argv[idx++], WIFI_TWT_INDIVIDUAL, + WIFI_TWT_WAKE_TBTT) || + !parse_number(sh, &setup_cmd, argv[idx++], WIFI_TWT_SETUP_CMD_REQUEST, + WIFI_TWT_SETUP_CMD_DEMAND) || + !parse_number(sh, (long *)¶ms.dialog_token, argv[idx++], 1, 255) || + !parse_number(sh, (long *)¶ms.flow_id, argv[idx++], 1, WIFI_MAX_TWT_FLOWS)) + return -EINVAL; + + if (net_mgmt(NET_REQUEST_WIFI_TWT, iface, ¶ms, sizeof(params))) { + shell_fprintf(sh, SHELL_WARNING, "%s with %s failed\n", + wifi_twt_operation2str[params.operation], + wifi_twt_negotiation_type2str[params.negotiation_type]); + return -ENOEXEC; + } + + shell_fprintf(sh, SHELL_NORMAL, "TWT operation %s with dg: %d, flow_id: %d requested\n", + wifi_twt_operation2str[params.operation], + params.dialog_token, params.flow_id); + + return 0; +} + +static int cmd_wifi_twt_teardown_all(const struct shell *sh, size_t argc, + char *argv[]) +{ + struct net_if *iface = net_if_get_default(); + struct wifi_twt_params params = { 0 }; + + context.sh = sh; + + params.operation = WIFI_TWT_TEARDOWN; + params.teardown.teardown_all = 1; + + if (net_mgmt(NET_REQUEST_WIFI_TWT, iface, ¶ms, sizeof(params))) { + shell_fprintf(sh, SHELL_WARNING, "%s with %s failed\n", + wifi_twt_operation2str[params.operation], + wifi_twt_negotiation_type2str[params.negotiation_type]); + return -ENOEXEC; + } + + shell_fprintf(sh, SHELL_NORMAL, "TWT operation %s all flows\n", + wifi_twt_operation2str[params.operation]); + + return 0; +} static int cmd_wifi_ap_enable(const struct shell *sh, size_t argc, char *argv[]) @@ -433,6 +744,26 @@ SHELL_STATIC_SUBCMD_SET_CREATE(wifi_cmd_ap, SHELL_SUBCMD_SET_END ); +SHELL_STATIC_SUBCMD_SET_CREATE(wifi_twt_ops, + SHELL_CMD(quick_setup, NULL, " Start a TWT flow with defaults:\n" + " \n", + cmd_wifi_twt_setup_quick), + SHELL_CMD(setup, NULL, " Start a TWT flow:\n" + "\n" + "\n" + " " + " \n", + cmd_wifi_twt_setup), + SHELL_CMD(teardown, NULL, " Teardown a TWT flow:\n" + "\n" + "\n" + " \n", + cmd_wifi_twt_teardown), + SHELL_CMD(teardown_all, NULL, " Teardown all TWT flows\n", + cmd_wifi_twt_teardown_all), + SHELL_SUBCMD_SET_END +); + SHELL_STATIC_SUBCMD_SET_CREATE(wifi_commands, SHELL_CMD(ap, &wifi_cmd_ap, "Access Point mode commands", NULL), SHELL_CMD(connect, NULL, @@ -447,9 +778,13 @@ SHELL_STATIC_SUBCMD_SET_CREATE(wifi_commands, cmd_wifi_connect), SHELL_CMD(disconnect, NULL, "Disconnect from the Wi-Fi AP", cmd_wifi_disconnect), + SHELL_CMD(ps, NULL, "Configure Wi-Fi power save on/off, no arguments will dump config", + cmd_wifi_ps), + SHELL_CMD(ps_mode, NULL, "Configure Wi-Fi power save mode legacy/wmm", cmd_wifi_ps_mode), SHELL_CMD(scan, NULL, "Scan for Wi-Fi APs", cmd_wifi_scan), SHELL_CMD(statistics, NULL, "Wi-Fi interface statistics", cmd_wifi_stats), SHELL_CMD(status, NULL, "Status of the Wi-Fi interface", cmd_wifi_status), + SHELL_CMD(twt, &wifi_twt_ops, "Manage TWT flows", NULL), SHELL_SUBCMD_SET_END );