drivers: wifi: siwx91x: Add support for set_power_save()

Added power-save mode support to optimize station wake-up timing
and reduce power consumption. The device currently supports
only legacy power-save mode.

This feature can be tested using the Wifi shell:
  - ps: Enable the PS. By default, the device operates in fast
        PSP mode.
  - ps_exit_strategy: Updates mode if enabled; otherwise, follows
        the configured exit strategy when power-save is enabled.
  - ps_wakeup_mode: Configures the wake-up behavior.
  - ps_timeout: Defines the timeout duration for power-save mode.

Co-authored-by: Arunmani Alagarsamy <arunmani.a@silabs.com>
Signed-off-by: Arunmani Alagarsamy <arunmani.a@silabs.com>
Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com>
This commit is contained in:
Arunmani Alagarsamy 2025-04-08 17:14:58 +05:30 committed by Benjamin Cabé
commit adae16812e
2 changed files with 140 additions and 0 deletions

View file

@ -253,6 +253,139 @@ static enum wifi_mfp_options siwx91x_set_sta_mfp_option(sl_wifi_security_t secur
return WIFI_MFP_UNKNOWN; 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) {
return 0;
}
if (sidev->state == WIFI_STATE_INTERFACE_DISABLED) {
return 0;
}
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 unsigned int siwx91x_on_join(sl_wifi_event_t event, static unsigned int siwx91x_on_join(sl_wifi_event_t event,
char *result, uint32_t result_size, void *arg) char *result, uint32_t result_size, void *arg)
{ {
@ -275,6 +408,8 @@ static unsigned int siwx91x_on_join(sl_wifi_event_t event,
siwx91x_on_join_ipv4(sidev); siwx91x_on_join_ipv4(sidev);
siwx91x_on_join_ipv6(sidev); siwx91x_on_join_ipv6(sidev);
siwx91x_apply_power_save(sidev);
return 0; return 0;
} }
@ -1439,6 +1574,7 @@ static const struct wifi_mgmt_ops siwx91x_mgmt = {
.get_stats = siwx91x_stats, .get_stats = siwx91x_stats,
#endif #endif
.get_version = siwx91x_get_version, .get_version = siwx91x_get_version,
.set_power_save = siwx91x_set_power_save,
}; };
static const struct net_wifi_mgmt_offload siwx91x_api = { static const struct net_wifi_mgmt_offload siwx91x_api = {
@ -1452,6 +1588,9 @@ static const struct net_wifi_mgmt_offload siwx91x_api = {
}; };
static struct siwx91x_dev sidev = { static struct siwx91x_dev sidev = {
.ps_params.enabled = WIFI_PS_DISABLED,
.ps_params.exit_strategy = WIFI_PS_EXIT_EVERY_TIM,
.ps_params.wakeup_mode = WIFI_PS_WAKEUP_MODE_DTIM,
.max_num_sta = CONFIG_WIFI_MGMT_AP_MAX_NUM_STA, .max_num_sta = CONFIG_WIFI_MGMT_AP_MAX_NUM_STA,
}; };

View file

@ -21,6 +21,7 @@ struct siwx91x_dev {
enum wifi_iface_state scan_prev_state; enum wifi_iface_state scan_prev_state;
scan_result_cb_t scan_res_cb; scan_result_cb_t scan_res_cb;
uint16_t scan_max_bss_cnt; uint16_t scan_max_bss_cnt;
struct wifi_ps_params ps_params;
uint8_t max_num_sta; uint8_t max_num_sta;
bool reboot_needed; bool reboot_needed;
bool hidden_ssid; bool hidden_ssid;