From 58187386689f5d87f7f0a9ccf495d095e6e80b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Pouiller?= Date: Thu, 22 May 2025 11:18:32 +0200 Subject: [PATCH] drivers: wifi: siwx91x: Extract Power Save related functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit siwx91x_wifi.c starts to contains to much code. Let's simplify it by grouping all the power-save related functions in a separated file. Signed-off-by: Jérôme Pouiller --- drivers/wifi/siwx91x/CMakeLists.txt | 6 +- drivers/wifi/siwx91x/siwx91x_wifi.c | 340 +----------------------- drivers/wifi/siwx91x/siwx91x_wifi_ps.c | 346 +++++++++++++++++++++++++ drivers/wifi/siwx91x/siwx91x_wifi_ps.h | 20 ++ 4 files changed, 374 insertions(+), 338 deletions(-) create mode 100644 drivers/wifi/siwx91x/siwx91x_wifi_ps.c create mode 100644 drivers/wifi/siwx91x/siwx91x_wifi_ps.h diff --git a/drivers/wifi/siwx91x/CMakeLists.txt b/drivers/wifi/siwx91x/CMakeLists.txt index c0e46476cf2..70405800f25 100644 --- a/drivers/wifi/siwx91x/CMakeLists.txt +++ b/drivers/wifi/siwx91x/CMakeLists.txt @@ -2,7 +2,11 @@ # Copyright (c) 2024 Silicon Laboratories Inc. if(CONFIG_WIFI_SILABS_SIWX91X) - zephyr_library_sources(siwx91x_wifi.c siwx91x_wifi_ap.c siwx91x_wifi_scan.c) + zephyr_library_sources( + siwx91x_wifi.c + siwx91x_wifi_ap.c + siwx91x_wifi_scan.c + siwx91x_wifi_ps.c) if(CONFIG_WIFI_SILABS_SIWX91X_NET_STACK_OFFLOAD) zephyr_library_sources(siwx91x_wifi_socket.c) diff --git a/drivers/wifi/siwx91x/siwx91x_wifi.c b/drivers/wifi/siwx91x/siwx91x_wifi.c index 226468f6371..6d88ec5da68 100644 --- a/drivers/wifi/siwx91x/siwx91x_wifi.c +++ b/drivers/wifi/siwx91x/siwx91x_wifi.c @@ -12,6 +12,7 @@ #include #include "siwx91x_wifi.h" #include "siwx91x_wifi_ap.h" +#include "siwx91x_wifi_ps.h" #include "siwx91x_wifi_scan.h" #include "siwx91x_wifi_socket.h" @@ -30,12 +31,6 @@ LOG_MODULE_REGISTER(siwx91x_wifi); NET_BUF_POOL_FIXED_DEFINE(siwx91x_tx_pool, 1, _NET_ETH_MAX_FRAME_SIZE, 0, NULL); -enum { - REQUEST_TWT = 0, - SUGGEST_TWT = 1, - DEMAND_TWT = 2, -}; - enum { STATE_IDLE = 0x00, /* Failover Roam */ @@ -191,203 +186,6 @@ static enum wifi_mfp_options siwx91x_set_sta_mfp_option(sl_wifi_security_t secur return WIFI_MFP_UNKNOWN; } -static int siwx91x_get_connected_ap_beacon_interval_ms(void) -{ - sl_wifi_operational_statistics_t sl_stat; - sl_wifi_interface_t interface; - int status; - - interface = sl_wifi_get_default_interface(); - if (FIELD_GET(SIWX91X_INTERFACE_MASK, interface) != SL_WIFI_CLIENT_INTERFACE) { - return 0; - } - - status = sl_wifi_get_operational_statistics(SL_WIFI_CLIENT_INTERFACE, &sl_stat); - if (status) { - return 0; - } - - return sys_get_le16(sl_stat.beacon_interval) * 1024 / 1000; -} - -static int siwx91x_apply_power_save(struct siwx91x_dev *sidev) -{ - sl_wifi_performance_profile_t sl_ps_profile; - sl_wifi_interface_t interface; - int beacon_interval; - int status; - - interface = sl_wifi_get_default_interface(); - if (FIELD_GET(SIWX91X_INTERFACE_MASK, interface) != SL_WIFI_CLIENT_INTERFACE) { - LOG_ERR("Wi-Fi not in station mode"); - return -EINVAL; - } - - if (sidev->state == WIFI_STATE_INTERFACE_DISABLED) { - LOG_ERR("Command given in invalid state"); - return -EINVAL; - } - - sl_wifi_get_performance_profile(&sl_ps_profile); - - if (sidev->ps_params.enabled == WIFI_PS_DISABLED) { - sl_ps_profile.profile = HIGH_PERFORMANCE; - goto out; - } - if (sidev->ps_params.exit_strategy == WIFI_PS_EXIT_EVERY_TIM) { - sl_ps_profile.profile = ASSOCIATED_POWER_SAVE_LOW_LATENCY; - } else if (sidev->ps_params.exit_strategy == WIFI_PS_EXIT_CUSTOM_ALGO) { - sl_ps_profile.profile = ASSOCIATED_POWER_SAVE; - } else { - /* Already sanitized by siwx91x_set_power_save() */ - return -EINVAL; - } - - sl_ps_profile.monitor_interval = sidev->ps_params.timeout_ms; - - beacon_interval = siwx91x_get_connected_ap_beacon_interval_ms(); - /* 1000ms is arbitrary sane value */ - sl_ps_profile.listen_interval = MIN(beacon_interval * sidev->ps_params.listen_interval, - 1000); - - if (sidev->ps_params.wakeup_mode == WIFI_PS_WAKEUP_MODE_LISTEN_INTERVAL && - !sidev->ps_params.listen_interval) { - LOG_INF("Disabling listen interval based wakeup until connection establishes"); - } - if (sidev->ps_params.wakeup_mode == WIFI_PS_WAKEUP_MODE_DTIM || - !sidev->ps_params.listen_interval) { - sl_ps_profile.dtim_aligned_type = 1; - } else if (sidev->ps_params.wakeup_mode == WIFI_PS_WAKEUP_MODE_LISTEN_INTERVAL) { - sl_ps_profile.dtim_aligned_type = 0; - } else { - /* Already sanitized by siwx91x_set_power_save() */ - return -EINVAL; - } - -out: - status = sl_wifi_set_performance_profile(&sl_ps_profile); - return status ? -EIO : 0; -} - -static int siwx91x_set_power_save(const struct device *dev, struct wifi_ps_params *params) -{ - struct siwx91x_dev *sidev = dev->data; - int status; - - __ASSERT(params, "params cannot be NULL"); - - switch (params->type) { - case WIFI_PS_PARAM_STATE: - sidev->ps_params.enabled = params->enabled; - break; - case WIFI_PS_PARAM_MODE: - if (params->mode != WIFI_PS_MODE_LEGACY) { - params->fail_reason = WIFI_PS_PARAM_FAIL_OPERATION_NOT_SUPPORTED; - return -ENOTSUP; - } - break; - case WIFI_PS_PARAM_LISTEN_INTERVAL: - sidev->ps_params.listen_interval = params->listen_interval; - break; - case WIFI_PS_PARAM_WAKEUP_MODE: - if (params->wakeup_mode != WIFI_PS_WAKEUP_MODE_LISTEN_INTERVAL && - params->wakeup_mode != WIFI_PS_WAKEUP_MODE_DTIM) { - params->fail_reason = WIFI_PS_PARAM_FAIL_OPERATION_NOT_SUPPORTED; - return -ENOTSUP; - } - sidev->ps_params.wakeup_mode = params->wakeup_mode; - break; - case WIFI_PS_PARAM_TIMEOUT: - /* 1000ms is arbitrary sane value */ - if (params->timeout_ms < SLI_DEFAULT_MONITOR_INTERVAL || - params->timeout_ms > 1000) { - params->fail_reason = WIFI_PS_PARAM_FAIL_CMD_EXEC_FAIL; - return -EINVAL; - } - sidev->ps_params.timeout_ms = params->timeout_ms; - break; - case WIFI_PS_PARAM_EXIT_STRATEGY: - if (params->exit_strategy != WIFI_PS_EXIT_EVERY_TIM && - params->exit_strategy != WIFI_PS_EXIT_CUSTOM_ALGO) { - params->fail_reason = WIFI_PS_PARAM_FAIL_OPERATION_NOT_SUPPORTED; - return -ENOTSUP; - } - sidev->ps_params.exit_strategy = params->exit_strategy; - break; - default: - params->fail_reason = WIFI_PS_PARAM_FAIL_CMD_EXEC_FAIL; - return -EINVAL; - } - status = siwx91x_apply_power_save(sidev); - if (status) { - params->fail_reason = WIFI_PS_PARAM_FAIL_CMD_EXEC_FAIL; - return status; - } - return 0; -} - -static int siwx91x_get_power_save_config(const struct device *dev, struct wifi_ps_config *config) -{ - sl_wifi_performance_profile_t sl_ps_profile; - struct siwx91x_dev *sidev = dev->data; - sl_wifi_interface_t interface; - uint16_t beacon_interval; - sl_status_t status; - - __ASSERT(config, "config cannot be NULL"); - - interface = sl_wifi_get_default_interface(); - if (FIELD_GET(SIWX91X_INTERFACE_MASK, interface) != SL_WIFI_CLIENT_INTERFACE) { - LOG_ERR("Wi-Fi not in station mode"); - return -EINVAL; - } - - if (sidev->state == WIFI_STATE_INTERFACE_DISABLED) { - LOG_ERR("Command given in invalid state"); - return -EINVAL; - } - - status = sl_wifi_get_performance_profile(&sl_ps_profile); - if (status != SL_STATUS_OK) { - LOG_ERR("Failed to get power save profile: 0x%x", status); - return -EIO; - } - - switch (sl_ps_profile.profile) { - case HIGH_PERFORMANCE: - config->ps_params.enabled = WIFI_PS_DISABLED; - break; - case ASSOCIATED_POWER_SAVE_LOW_LATENCY: - config->ps_params.enabled = WIFI_PS_ENABLED; - config->ps_params.exit_strategy = WIFI_PS_EXIT_EVERY_TIM; - break; - case ASSOCIATED_POWER_SAVE: - config->ps_params.enabled = WIFI_PS_ENABLED; - config->ps_params.exit_strategy = WIFI_PS_EXIT_CUSTOM_ALGO; - break; - default: - break; - } - - if (sl_ps_profile.dtim_aligned_type) { - config->ps_params.wakeup_mode = WIFI_PS_WAKEUP_MODE_DTIM; - } else { - config->ps_params.wakeup_mode = WIFI_PS_WAKEUP_MODE_LISTEN_INTERVAL; - - beacon_interval = siwx91x_get_connected_ap_beacon_interval_ms(); - if (beacon_interval > 0) { - config->ps_params.listen_interval = - sl_ps_profile.listen_interval / beacon_interval; - } - } - - /* Device supports only legacy power-save mode */ - config->ps_params.mode = WIFI_PS_MODE_LEGACY; - config->ps_params.timeout_ms = sl_ps_profile.monitor_interval; - - return 0; -} - static unsigned int siwx91x_on_join(sl_wifi_event_t event, char *result, uint32_t result_size, void *arg) { @@ -929,138 +727,6 @@ static int siwx91x_dev_init(const struct device *dev) return 0; } -static int siwx91x_convert_z_sl_twt_req_type(enum wifi_twt_setup_cmd z_req_cmd) -{ - switch (z_req_cmd) { - case WIFI_TWT_SETUP_CMD_REQUEST: - return REQUEST_TWT; - case WIFI_TWT_SETUP_CMD_SUGGEST: - return SUGGEST_TWT; - case WIFI_TWT_SETUP_CMD_DEMAND: - return DEMAND_TWT; - default: - return -EINVAL; - } -} - -static int siwx91x_set_twt_setup(struct wifi_twt_params *params) -{ - sl_status_t status; - int twt_req_type = siwx91x_convert_z_sl_twt_req_type(params->setup_cmd); - - sl_wifi_twt_request_t twt_req = { - .twt_retry_interval = 5, - .wake_duration_unit = 0, - .wake_int_mantissa = params->setup.twt_mantissa, - .un_announced_twt = !params->setup.announce, - .wake_duration = params->setup.twt_wake_interval, - .triggered_twt = params->setup.trigger, - .wake_int_exp = params->setup.twt_exponent, - .implicit_twt = 1, - .twt_flow_id = params->flow_id, - .twt_enable = 1, - .req_type = twt_req_type, - }; - - if (twt_req_type < 0) { - params->fail_reason = WIFI_TWT_FAIL_CMD_EXEC_FAIL; - return -EINVAL; - } - - if (!params->setup.twt_info_disable) { - params->fail_reason = WIFI_TWT_FAIL_OPERATION_NOT_SUPPORTED; - return -ENOTSUP; - } - - if (params->setup.responder) { - params->fail_reason = WIFI_TWT_FAIL_OPERATION_NOT_SUPPORTED; - return -ENOTSUP; - } - - /* implicit -> won't do renegotiation - * explicit -> must do renegotiation for each session - */ - if (!params->setup.implicit) { - /* explicit twt is not supported */ - params->fail_reason = WIFI_TWT_FAIL_OPERATION_NOT_SUPPORTED; - return -ENOTSUP; - } - - if (params->setup.twt_wake_interval > 255 * 256) { - twt_req.wake_duration_unit = 1; - twt_req.wake_duration = params->setup.twt_wake_interval / 1024; - } else { - twt_req.wake_duration_unit = 0; - twt_req.wake_duration = params->setup.twt_wake_interval / 256; - } - - status = sl_wifi_enable_target_wake_time(&twt_req); - if (status != SL_STATUS_OK) { - params->fail_reason = WIFI_TWT_FAIL_CMD_EXEC_FAIL; - params->resp_status = WIFI_TWT_RESP_NOT_RECEIVED; - return -EINVAL; - } - - return 0; -} - -static int siwx91x_set_twt_teardown(struct wifi_twt_params *params) -{ - sl_status_t status; - sl_wifi_twt_request_t twt_req = { }; - - twt_req.twt_enable = 0; - - if (params->teardown.teardown_all) { - twt_req.twt_flow_id = 0xFF; - } else { - twt_req.twt_flow_id = params->flow_id; - } - - status = sl_wifi_disable_target_wake_time(&twt_req); - if (status != SL_STATUS_OK) { - params->fail_reason = WIFI_TWT_FAIL_CMD_EXEC_FAIL; - params->teardown_status = WIFI_TWT_TEARDOWN_FAILED; - return -EINVAL; - } - - params->teardown_status = WIFI_TWT_TEARDOWN_SUCCESS; - - return 0; -} - -static int siwx91x_set_twt(const struct device *dev, struct wifi_twt_params *params) -{ - sl_wifi_interface_t interface = sl_wifi_get_default_interface(); - struct siwx91x_dev *sidev = dev->data; - - __ASSERT(params, "params cannot be a NULL"); - - if (FIELD_GET(SIWX91X_INTERFACE_MASK, interface) != SL_WIFI_CLIENT_INTERFACE) { - params->fail_reason = WIFI_TWT_FAIL_OPERATION_NOT_SUPPORTED; - return -ENOTSUP; - } - - if (sidev->state != WIFI_STATE_DISCONNECTED && sidev->state != WIFI_STATE_INACTIVE && - sidev->state != WIFI_STATE_COMPLETED) { - LOG_ERR("Command given in invalid state"); - return -EBUSY; - } - - if (params->negotiation_type != WIFI_TWT_INDIVIDUAL) { - params->fail_reason = WIFI_TWT_FAIL_OPERATION_NOT_SUPPORTED; - return -ENOTSUP; - } - - if (params->operation == WIFI_TWT_SETUP) { - return siwx91x_set_twt_setup(params); - } else if (params->operation == WIFI_TWT_TEARDOWN) { - return siwx91x_set_twt_teardown(params); - } - params->fail_reason = WIFI_TWT_FAIL_OPERATION_NOT_SUPPORTED; - return -ENOTSUP; -} - static const struct wifi_mgmt_ops siwx91x_mgmt = { .scan = siwx91x_scan, .connect = siwx91x_connect, @@ -1072,12 +738,12 @@ static const struct wifi_mgmt_ops siwx91x_mgmt = { .iface_status = siwx91x_status, .mode = siwx91x_mode, .set_twt = siwx91x_set_twt, + .set_power_save = siwx91x_set_power_save, + .get_power_save_config = siwx91x_get_power_save_config, #if defined(CONFIG_NET_STATISTICS_WIFI) .get_stats = siwx91x_stats, #endif .get_version = siwx91x_get_version, - .set_power_save = siwx91x_set_power_save, - .get_power_save_config = siwx91x_get_power_save_config, }; static const struct net_wifi_mgmt_offload siwx91x_api = { diff --git a/drivers/wifi/siwx91x/siwx91x_wifi_ps.c b/drivers/wifi/siwx91x/siwx91x_wifi_ps.c new file mode 100644 index 00000000000..223d30f5d4d --- /dev/null +++ b/drivers/wifi/siwx91x/siwx91x_wifi_ps.c @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2023 Antmicro + * Copyright (c) 2024-2025 Silicon Laboratories Inc. + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "siwx91x_wifi.h" +#include "siwx91x_wifi_ps.h" + +LOG_MODULE_DECLARE(siwx91x_wifi); + +enum { + REQUEST_TWT = 0, + SUGGEST_TWT = 1, + DEMAND_TWT = 2, +}; + + +static int siwx91x_get_connected_ap_beacon_interval_ms(void) +{ + sl_wifi_operational_statistics_t sl_stat; + sl_wifi_interface_t interface; + int status; + + interface = sl_wifi_get_default_interface(); + if (FIELD_GET(SIWX91X_INTERFACE_MASK, interface) != SL_WIFI_CLIENT_INTERFACE) { + return 0; + } + + status = sl_wifi_get_operational_statistics(SL_WIFI_CLIENT_INTERFACE, &sl_stat); + if (status) { + return 0; + } + + return sys_get_le16(sl_stat.beacon_interval) * 1024 / 1000; +} + +int siwx91x_apply_power_save(struct siwx91x_dev *sidev) +{ + sl_wifi_performance_profile_t sl_ps_profile; + sl_wifi_interface_t interface; + int beacon_interval; + int status; + + interface = sl_wifi_get_default_interface(); + if (FIELD_GET(SIWX91X_INTERFACE_MASK, interface) != SL_WIFI_CLIENT_INTERFACE) { + LOG_ERR("Wi-Fi not in station mode"); + return -EINVAL; + } + + if (sidev->state == WIFI_STATE_INTERFACE_DISABLED) { + LOG_ERR("Command given in invalid state"); + return -EINVAL; + } + + sl_wifi_get_performance_profile(&sl_ps_profile); + + if (sidev->ps_params.enabled == WIFI_PS_DISABLED) { + sl_ps_profile.profile = HIGH_PERFORMANCE; + goto out; + } + if (sidev->ps_params.exit_strategy == WIFI_PS_EXIT_EVERY_TIM) { + sl_ps_profile.profile = ASSOCIATED_POWER_SAVE_LOW_LATENCY; + } else if (sidev->ps_params.exit_strategy == WIFI_PS_EXIT_CUSTOM_ALGO) { + sl_ps_profile.profile = ASSOCIATED_POWER_SAVE; + } else { + /* Already sanitized by siwx91x_set_power_save() */ + return -EINVAL; + } + + sl_ps_profile.monitor_interval = sidev->ps_params.timeout_ms; + + beacon_interval = siwx91x_get_connected_ap_beacon_interval_ms(); + /* 1000ms is arbitrary sane value */ + sl_ps_profile.listen_interval = MIN(beacon_interval * sidev->ps_params.listen_interval, + 1000); + + if (sidev->ps_params.wakeup_mode == WIFI_PS_WAKEUP_MODE_LISTEN_INTERVAL && + !sidev->ps_params.listen_interval) { + LOG_INF("Disabling listen interval based wakeup until connection establishes"); + } + if (sidev->ps_params.wakeup_mode == WIFI_PS_WAKEUP_MODE_DTIM || + !sidev->ps_params.listen_interval) { + sl_ps_profile.dtim_aligned_type = 1; + } else if (sidev->ps_params.wakeup_mode == WIFI_PS_WAKEUP_MODE_LISTEN_INTERVAL) { + sl_ps_profile.dtim_aligned_type = 0; + } else { + /* Already sanitized by siwx91x_set_power_save() */ + return -EINVAL; + } + +out: + status = sl_wifi_set_performance_profile(&sl_ps_profile); + return status ? -EIO : 0; +} + +int siwx91x_set_power_save(const struct device *dev, struct wifi_ps_params *params) +{ + struct siwx91x_dev *sidev = dev->data; + int status; + + __ASSERT(params, "params cannot be NULL"); + + switch (params->type) { + case WIFI_PS_PARAM_STATE: + sidev->ps_params.enabled = params->enabled; + break; + case WIFI_PS_PARAM_MODE: + if (params->mode != WIFI_PS_MODE_LEGACY) { + params->fail_reason = WIFI_PS_PARAM_FAIL_OPERATION_NOT_SUPPORTED; + return -ENOTSUP; + } + break; + case WIFI_PS_PARAM_LISTEN_INTERVAL: + sidev->ps_params.listen_interval = params->listen_interval; + break; + case WIFI_PS_PARAM_WAKEUP_MODE: + if (params->wakeup_mode != WIFI_PS_WAKEUP_MODE_LISTEN_INTERVAL && + params->wakeup_mode != WIFI_PS_WAKEUP_MODE_DTIM) { + params->fail_reason = WIFI_PS_PARAM_FAIL_OPERATION_NOT_SUPPORTED; + return -ENOTSUP; + } + sidev->ps_params.wakeup_mode = params->wakeup_mode; + break; + case WIFI_PS_PARAM_TIMEOUT: + /* 1000ms is arbitrary sane value */ + if (params->timeout_ms < SLI_DEFAULT_MONITOR_INTERVAL || + params->timeout_ms > 1000) { + params->fail_reason = WIFI_PS_PARAM_FAIL_CMD_EXEC_FAIL; + return -EINVAL; + } + sidev->ps_params.timeout_ms = params->timeout_ms; + break; + case WIFI_PS_PARAM_EXIT_STRATEGY: + if (params->exit_strategy != WIFI_PS_EXIT_EVERY_TIM && + params->exit_strategy != WIFI_PS_EXIT_CUSTOM_ALGO) { + params->fail_reason = WIFI_PS_PARAM_FAIL_OPERATION_NOT_SUPPORTED; + return -ENOTSUP; + } + sidev->ps_params.exit_strategy = params->exit_strategy; + break; + default: + params->fail_reason = WIFI_PS_PARAM_FAIL_CMD_EXEC_FAIL; + return -EINVAL; + } + status = siwx91x_apply_power_save(sidev); + if (status) { + params->fail_reason = WIFI_PS_PARAM_FAIL_CMD_EXEC_FAIL; + return status; + } + return 0; +} + +int siwx91x_get_power_save_config(const struct device *dev, struct wifi_ps_config *config) +{ + sl_wifi_performance_profile_t sl_ps_profile; + struct siwx91x_dev *sidev = dev->data; + sl_wifi_interface_t interface; + uint16_t beacon_interval; + sl_status_t status; + + __ASSERT(config, "config cannot be NULL"); + + interface = sl_wifi_get_default_interface(); + if (FIELD_GET(SIWX91X_INTERFACE_MASK, interface) != SL_WIFI_CLIENT_INTERFACE) { + LOG_ERR("Wi-Fi not in station mode"); + return -EINVAL; + } + + if (sidev->state == WIFI_STATE_INTERFACE_DISABLED) { + LOG_ERR("Command given in invalid state"); + return -EINVAL; + } + + status = sl_wifi_get_performance_profile(&sl_ps_profile); + if (status != SL_STATUS_OK) { + LOG_ERR("Failed to get power save profile: 0x%x", status); + return -EIO; + } + + switch (sl_ps_profile.profile) { + case HIGH_PERFORMANCE: + config->ps_params.enabled = WIFI_PS_DISABLED; + break; + case ASSOCIATED_POWER_SAVE_LOW_LATENCY: + config->ps_params.enabled = WIFI_PS_ENABLED; + config->ps_params.exit_strategy = WIFI_PS_EXIT_EVERY_TIM; + break; + case ASSOCIATED_POWER_SAVE: + config->ps_params.enabled = WIFI_PS_ENABLED; + config->ps_params.exit_strategy = WIFI_PS_EXIT_CUSTOM_ALGO; + break; + default: + break; + } + + if (sl_ps_profile.dtim_aligned_type) { + config->ps_params.wakeup_mode = WIFI_PS_WAKEUP_MODE_DTIM; + } else { + config->ps_params.wakeup_mode = WIFI_PS_WAKEUP_MODE_LISTEN_INTERVAL; + + beacon_interval = siwx91x_get_connected_ap_beacon_interval_ms(); + if (beacon_interval > 0) { + config->ps_params.listen_interval = + sl_ps_profile.listen_interval / beacon_interval; + } + } + + /* Device supports only legacy power-save mode */ + config->ps_params.mode = WIFI_PS_MODE_LEGACY; + config->ps_params.timeout_ms = sl_ps_profile.monitor_interval; + + return 0; +} + +static int siwx91x_convert_z_sl_twt_req_type(enum wifi_twt_setup_cmd z_req_cmd) +{ + switch (z_req_cmd) { + case WIFI_TWT_SETUP_CMD_REQUEST: + return REQUEST_TWT; + case WIFI_TWT_SETUP_CMD_SUGGEST: + return SUGGEST_TWT; + case WIFI_TWT_SETUP_CMD_DEMAND: + return DEMAND_TWT; + default: + return -EINVAL; + } +} + +static int siwx91x_set_twt_setup(struct wifi_twt_params *params) +{ + sl_status_t status; + int twt_req_type = siwx91x_convert_z_sl_twt_req_type(params->setup_cmd); + + sl_wifi_twt_request_t twt_req = { + .twt_retry_interval = 5, + .wake_duration_unit = 0, + .wake_int_mantissa = params->setup.twt_mantissa, + .un_announced_twt = !params->setup.announce, + .wake_duration = params->setup.twt_wake_interval, + .triggered_twt = params->setup.trigger, + .wake_int_exp = params->setup.twt_exponent, + .implicit_twt = 1, + .twt_flow_id = params->flow_id, + .twt_enable = 1, + .req_type = twt_req_type, + }; + + if (twt_req_type < 0) { + params->fail_reason = WIFI_TWT_FAIL_CMD_EXEC_FAIL; + return -EINVAL; + } + + if (!params->setup.twt_info_disable) { + params->fail_reason = WIFI_TWT_FAIL_OPERATION_NOT_SUPPORTED; + return -ENOTSUP; + } + + if (params->setup.responder) { + params->fail_reason = WIFI_TWT_FAIL_OPERATION_NOT_SUPPORTED; + return -ENOTSUP; + } + + /* implicit -> won't do renegotiation + * explicit -> must do renegotiation for each session + */ + if (!params->setup.implicit) { + /* explicit twt is not supported */ + params->fail_reason = WIFI_TWT_FAIL_OPERATION_NOT_SUPPORTED; + return -ENOTSUP; + } + + if (params->setup.twt_wake_interval > 255 * 256) { + twt_req.wake_duration_unit = 1; + twt_req.wake_duration = params->setup.twt_wake_interval / 1024; + } else { + twt_req.wake_duration_unit = 0; + twt_req.wake_duration = params->setup.twt_wake_interval / 256; + } + + status = sl_wifi_enable_target_wake_time(&twt_req); + if (status != SL_STATUS_OK) { + params->fail_reason = WIFI_TWT_FAIL_CMD_EXEC_FAIL; + params->resp_status = WIFI_TWT_RESP_NOT_RECEIVED; + return -EINVAL; + } + + return 0; +} + +static int siwx91x_set_twt_teardown(struct wifi_twt_params *params) +{ + sl_status_t status; + sl_wifi_twt_request_t twt_req = { }; + + twt_req.twt_enable = 0; + + if (params->teardown.teardown_all) { + twt_req.twt_flow_id = 0xFF; + } else { + twt_req.twt_flow_id = params->flow_id; + } + + status = sl_wifi_disable_target_wake_time(&twt_req); + if (status != SL_STATUS_OK) { + params->fail_reason = WIFI_TWT_FAIL_CMD_EXEC_FAIL; + params->teardown_status = WIFI_TWT_TEARDOWN_FAILED; + return -EINVAL; + } + + params->teardown_status = WIFI_TWT_TEARDOWN_SUCCESS; + + return 0; +} + +int siwx91x_set_twt(const struct device *dev, struct wifi_twt_params *params) +{ + sl_wifi_interface_t interface = sl_wifi_get_default_interface(); + struct siwx91x_dev *sidev = dev->data; + + __ASSERT(params, "params cannot be a NULL"); + + if (FIELD_GET(SIWX91X_INTERFACE_MASK, interface) != SL_WIFI_CLIENT_INTERFACE) { + params->fail_reason = WIFI_TWT_FAIL_OPERATION_NOT_SUPPORTED; + return -ENOTSUP; + } + + if (sidev->state != WIFI_STATE_DISCONNECTED && sidev->state != WIFI_STATE_INACTIVE && + sidev->state != WIFI_STATE_COMPLETED) { + LOG_ERR("Command given in invalid state"); + return -EBUSY; + } + + if (params->negotiation_type != WIFI_TWT_INDIVIDUAL) { + params->fail_reason = WIFI_TWT_FAIL_OPERATION_NOT_SUPPORTED; + return -ENOTSUP; + } + + if (params->operation == WIFI_TWT_SETUP) { + return siwx91x_set_twt_setup(params); + } else if (params->operation == WIFI_TWT_TEARDOWN) { + return siwx91x_set_twt_teardown(params); + } + params->fail_reason = WIFI_TWT_FAIL_OPERATION_NOT_SUPPORTED; + return -ENOTSUP; +} diff --git a/drivers/wifi/siwx91x/siwx91x_wifi_ps.h b/drivers/wifi/siwx91x/siwx91x_wifi_ps.h new file mode 100644 index 00000000000..21eac284d50 --- /dev/null +++ b/drivers/wifi/siwx91x/siwx91x_wifi_ps.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2024 Silicon Laboratories Inc. + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef SIWX91X_WIFI_PS_H +#define SIWX91X_WIFI_PS_H + +struct device; +struct siwx91x_dev; +struct wifi_twt_params; +struct wifi_ps_config; +struct wifi_ps_params; + +int siwx91x_set_twt(const struct device *dev, struct wifi_twt_params *params); +int siwx91x_set_power_save(const struct device *dev, struct wifi_ps_params *params); +int siwx91x_get_power_save_config(const struct device *dev, struct wifi_ps_config *config); + +int siwx91x_apply_power_save(struct siwx91x_dev *sidev); + +#endif