-
Notifications
You must be signed in to change notification settings - Fork 7.3k
drivers: wifi: siwx91x: Add power-save support #86537
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
drivers: wifi: siwx91x: Add power-save support #86537
Conversation
1830008
to
7e6b30a
Compare
7e6b30a
to
a5f1cb2
Compare
a5f1cb2
to
f9de086
Compare
c6f7a2e
to
bbd7677
Compare
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
sl_ps_profile.profile = req_ps_mode; | ||
} | ||
} else { | ||
return -EAGAIN; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did you mean -EALREADY
?
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
} | ||
} else if (params->enabled == WIFI_PS_DISABLED) { | ||
if (sl_ps_profile.profile == HIGH_PERFORMANCE) { | ||
return -EAGAIN; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did you mean -EALREADY?
a95a68a
to
51c0d0f
Compare
drivers/wifi/siwx91x/Kconfig.siwx91x
Outdated
|
||
config WIFI_SIWX91X_ENHANCED_MAX_PSP | ||
bool "Enhanced Max PSP Support" | ||
default n |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
n
is the "default default", so you can just remove this line
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
case WIFI_PS_PARAM_STATE: | ||
if (params->enabled == WIFI_PS_ENABLED) { | ||
if (sl_ps_profile.profile == HIGH_PERFORMANCE) { | ||
if (siwx91x_ps_cfg.req_ps_mode == HIGH_PERFORMANCE) { | ||
sl_ps_profile.profile = ASSOCIATED_POWER_SAVE_LOW_LATENCY; | ||
} else { | ||
sl_ps_profile.profile = siwx91x_ps_cfg.req_ps_mode; | ||
} | ||
} else { | ||
return -EALREADY; | ||
} | ||
|
||
if (sl_ps_profile.dtim_aligned_type == 0) { | ||
beacon_interval = siwx91x_get_beacon_interval(dev); | ||
if (beacon_interval < 0) { | ||
params->fail_reason = WIFI_PS_PARAM_FAIL_DEVICE_NOT_CONNECTED; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The compliance check is failing because of the long (> 100) lines. To me it looks like this function could use some refactoring into smaller helper functions, so that you don't need to do so deep indentations.
51c0d0f
to
5b5cfdc
Compare
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
return -EINVAL; | ||
} | ||
|
||
get_wifi_current_performance_profile(&sl_ps_profile); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a private function. Use sl_wifi_get_performance_profile()
instead.
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
if (status != SL_STATUS_OK) { | ||
params->fail_reason = WIFI_PS_PARAM_FAIL_CMD_EXEC_FAIL; | ||
LOG_ERR("Failed to set power save profile: 0x%x", status); | ||
return -EINVAL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EINVAL
is used when the caller didn't provide the right arguments. Here, I rather believe the error is unexpected. I would rather return EIO
.
drivers/wifi/siwx91x/Kconfig.siwx91x
Outdated
help | ||
Enable this option to allow the device to configure | ||
Enhanced Maximum Power Save (PSP) support during | ||
device initialization. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enhanced Maximum Power Save is not standard feature. It makes sense to explain what are the drawbacks and the advantage of this option.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This option only makes sense if WIFI_SILABS_SIWX91X
is selected.
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
/* HIGH_PERFORMANCE by default */ | ||
sl_si91x_performance_profile_t req_ps_mode; | ||
uint16_t listen_interval; | ||
} siwx91x_ps_cfg; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid global variable. These variables should be a part of struct siwx91x_dev
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW, avoid to declare struct/variables in the middle of the code.
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
@@ -709,6 +712,270 @@ static int siwx91x_stats(const struct device *dev, struct net_stats_wifi *stats) | |||
} | |||
#endif | |||
|
|||
struct siwx91x_ps_profile { | |||
/* HIGH_PERFORMANCE by default */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can fix the code rather than require a comment:
[...] siwx91x_ps_cfg = {
.req_ps_mode = HIGH_PERFORMANCE,
}
} | ||
} else { | ||
return -EALREADY; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to admit I don't understand what the above lines are doing. Maybe a comment could help.
break; | ||
case WIFI_PS_DISABLED: | ||
if (sl_ps_profile->profile == HIGH_PERFORMANCE) { | ||
return -EALREADY; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it make sense to report an error if the profile is already selected. EALREADY
exists for cases the function won't do what the user expect if it is called twice (eg. wifi_connect()).
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
return -EINVAL; | ||
} | ||
|
||
sl_ps_profile.monitor_interval = (uint16_t)params->timeout_ms; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the cast required?
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
if (beacon_interval > 0) { | ||
/* converts MSEC to beacon unit */ | ||
config->ps_params.listen_interval = | ||
(sl_ps_profile.listen_interval / beacon_interval); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Parentheses are useless.
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
|
||
if (sl_ps_profile.dtim_aligned_type == 1) { | ||
config->ps_params.wakeup_mode = WIFI_PS_WAKEUP_MODE_DTIM; | ||
} else if (sl_ps_profile.dtim_aligned_type == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you consider dtim_aligned_type
is a boolean, test it as a boolean:
if (sl_ps_profile.dtim_aligned_type) {
...
} else {
...
}
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
if (sidev->state != WIFI_STATE_COMPLETED) { | ||
/* fail reason */ | ||
LOG_ERR("Device not connected"); | ||
return -EIO; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably meant EINVAL
, EBUSY
or EAGAIN
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
sl_status_t status; | ||
|
||
if (sidev->state != WIFI_STATE_COMPLETED) { | ||
/* fail reason */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Useless comment.
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
return -EINVAL; | ||
} | ||
|
||
return sys_get_le16(sl_stat.beacon_interval); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Until now, we didn't considered the host CPU could be big endian. I believe it does not make sense to do it for sl_stat.beacon_interval
.
*listen_interval = SIWX91X_PS_MAX_LISTEN_INTERVAL_MS; | ||
} | ||
|
||
return 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest to return the result. Hence, you will be consistent with siwx91x_get_beacon_interval()
.
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
|
||
__ASSERT(sl_ps_profile, "sl_ps_profile cannot be NULL"); | ||
|
||
switch(state) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing space after switch
.
5b5cfdc
to
5a30b71
Compare
5a30b71
to
1c706a4
Compare
Rebased to reduce the CI failures |
fbecf7c
to
ea86695
Compare
@@ -61,7 +62,9 @@ int siwg91x_get_nwp_config(int wifi_oper_mode, sl_wifi_device_configuration_t *g | |||
|
|||
#ifdef CONFIG_WIFI_SILABS_SIWX91X | |||
boot_config->ext_tcp_ip_feature_bit_map = SL_SI91X_CONFIG_FEAT_EXTENSION_VALID; | |||
#ifdef CONFIG_WIFI_SIWX91X_ENHANCED_MAX_PSP |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use IS_ENABLED()
.
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
sl_status_t status; | ||
|
||
sidev->state = WIFI_STATE_INTERFACE_DISABLED; | ||
sidev->iface = iface; | ||
|
||
siwx91x_ps_cfg->req_ps_mode = HIGH_PERFORMANCE; | ||
siwx91x_ps_cfg->listen_interval = 0; | ||
siwx91x_ps_cfg->listen_interval_wakeup = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can statically assign these values during declaration:
static struct siwx91x_dev sidev = {
.siwx91x_ps_cfg.req_ps_mode = HIGH_PERFORMANCE,
};
Initialization of listen_interval
and listen_interval_wakeup
are not required because static variable are already filled with 0s.
BTW, do we need to introduce yet another struct for PS parameters? I feel the only information we need to store is req_ps_mode
(to restore it on join).
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
|
||
status = siwx91x_connected_ap_beacon_interval(&beacon_interval); | ||
if (status == SL_STATUS_OK && beacon_interval > 0) { | ||
/* converts MSEC to beacon unit */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MSEC
is not SI: Convert ms in number of beacons
. (BTW, it's obvious, the comment is not required)
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
__ASSERT(listen_interval, "listen_interval cannot be NULL"); | ||
|
||
if (sidev->state != WIFI_STATE_COMPLETED) { | ||
siwx91x_ps_cfg->listen_interval_wakeup = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This side effect is unexpected in function called get_xxx()
.
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
} | ||
|
||
params.enabled = WIFI_PS_ENABLED; | ||
params.type = WIFI_PS_PARAM_STATE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer assignment during declaration:
struct wifi_ps_params params = {
.type = WIFI_PS_PARAM_STATE,
.enabled = WIFI_PS_ENABLED,
};
ea86695
to
c29fab3
Compare
c29fab3
to
910e3b8
Compare
Rebased to resolve conflicts |
910e3b8
to
eef9d60
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is still super difficult to understand.
case WIFI_PS_PARAM_LISTEN_INTERVAL: | ||
sl_ps_profile->listen_interval = params->listen_interval; | ||
sl_ps_profile->dtim_aligned_type = 0; | ||
return 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this return
expected? I feel listen_interval will be never applied.
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
|
||
static int siwx91x_set_power_save(const struct device *dev, struct wifi_ps_params *params) | ||
{ | ||
static sl_si91x_performance_profile_t exit_strategy_mode = { HIGH_PERFORMANCE }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid global variables. This variable should probably go in siwx91x_dev
.
BTW, I feel this variable is redundant with sl_ps_profile->profile
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, we need the exit_strategy_mode
variable to store the power save profile in case the user configures the exit strategy before the power save enabled. If we directly use sl_ps_profile->profile
, power save would be enabled immediately even if the user hasn't explicitly enabled it.
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
static int siwx91x_convert_li_bcn_unit_to_ms(uint16_t bcn_li) | ||
{ | ||
uint16_t beacon_interval; | ||
uint16_t ms_li; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why uint16_t
? Unless you have good reason use int
.
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
return -EAGAIN; | ||
} | ||
|
||
ms_li = bcn_li * beacon_interval; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to use uint16_t
, you have to add a comment explaining why you guarantee there is no overflow here.
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
static int siwx91x_convert_li_bcn_unit_to_ms(uint16_t bcn_li) | ||
{ | ||
uint16_t beacon_interval; | ||
uint16_t ms_li; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable names lack of consistency in the function. I suggest:
listen_interval
beacon_interval
listen_interval_ms
static int siwx91x_configure_ps_mode(const struct device *dev, enum wifi_ps state, | ||
sl_si91x_performance_profile_t exit_strategy_mode) | ||
sl_si91x_performance_profile_t exit_strategy_mode, | ||
uint16_t bcn_li) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment than for siwx91x_schedule_listen_interval_wakeup()
.
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
@@ -795,6 +864,7 @@ static int siwx91x_set_power_save(const struct device *dev, struct wifi_ps_param | |||
struct siwx91x_dev *sidev = dev->data; | |||
sl_wifi_performance_profile_t *sl_ps_profile = &sidev->sl_ps_profile; | |||
sl_wifi_interface_t interface; | |||
static uint16_t bcn_li; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please (really) avoid global variables.
drivers/wifi/siwx91x/siwx91x_wifi.c
Outdated
@@ -816,7 +886,8 @@ static int siwx91x_set_power_save(const struct device *dev, struct wifi_ps_param | |||
|
|||
switch (params->type) { | |||
case WIFI_PS_PARAM_STATE: | |||
status = siwx91x_configure_ps_mode(dev, params->enabled, exit_strategy_mode); | |||
status = siwx91x_configure_ps_mode(dev, params->enabled, exit_strategy_mode, | |||
bcn_li); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Value of may be bcn_li
is undefined.
siwx91x_on_join_ipv6(sidev); | ||
|
||
return 0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you relocate this function in a separate commit so the review will be easier?
if (status < 0) { | ||
return status; | ||
if (status == -EINVAL) { | ||
sl_ps_profile->profile = HIGH_PERFORMANCE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Is it related to this commit?
EINVAL
means "Invalid argument". This is a bit weird to fix this error by changing the profile.- What happens now if
status < 0 && status != -EINVAL
?
eef9d60
to
5659337
Compare
Added power-save mode support to optimize station wake-up timing and reduce power consumption. The device currently supports only legacy power-save mode. Implemented power-save features: - ps ON/OFF : 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. Signed-off-by: Arunmani Alagarsamy <[email protected]>
Adds support to configure listen interval in power save mode. Zephyr sets listen interval based on beacon units, while SDK expects it in ms. The setting takes effect after connection. If set to 0, device switches to DTIM wakeup mode. Signed-off-by: Arunmani Alagarsamy <[email protected]>
Move the siwx91x_on_join() function to make it more accessible for power save feature integration. No functional changes. Signed-off-by: Arunmani Alagarsamy <[email protected]>
Allow users to enable listen interval wakeup mode before connection. Power save is automatically activated after connection if configured beforehand. Signed-off-by: Arunmani Alagarsamy <[email protected]>
5659337
to
e47663c
Compare
Rebased to resolve conflicts |
Added power-save mode support to optimize station wake-up timing
and reduce power consumption. The device currently supports
only legacy power-save mode.
Implemented power-save features:
ps ON/OFF
: By default, the device operates infast PSP mode.
ps_listen_interval
: Moves to DTIM wakeup if set to zero.ps_exit_strategy
: Updates mode if enabled; otherwise, followsthe 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.