@@ -48,6 +48,8 @@
#define WMTWIFI_DEVICE "/dev/wmtWifi"
#define TESTMODE_CMD_ID_SUSPEND 101
+#define WMTWIFI_SUSPEND_VALUE (1)
+#define WMTWIFI_RESUME_VALUE (0)
#define PRIV_CMD_SIZE 512
typedef struct android_wifi_priv_cmd {
@@ -236,11 +238,72 @@
return err;
}
+/**
+ * @brief Set powersave state (on/off), same as calling `iw dev %ifname% set power_save %is_enable%`
+ *
+ * @param ifname interface name (e.g. wlan0)
+ * @param is_enable enable/disable state
+ */
static
void
-suspend_handle_display_on_off_iface(
+suspend_set_powersave(
+ const char *ifname, gboolean is_enable)
+{
+ int err = 0;
+ struct nl_msg *msg;
+
+ enum nl80211_ps_state ps_state;
+
+ int ifindex = 0;
+
+ ifindex = if_nametoindex(ifname);
+
+ if (ifindex == 0) {
+ if (!strcmp(ifname, "wlan0")) {
+ connman_error("iface %s is not active/present (set_powersave).", ifname);
+ } else {
+ DBG("iface %s is not active/present (set_powersave).", ifname);
+ }
+ return;
+ }
+
+ DBG("iface %s, setting powersave.", ifname);
+
+ msg = nlmsg_alloc();
+
+ genlmsg_put(msg, 0, 0, driver_id, 0, 0, NL80211_CMD_SET_POWER_SAVE, 0);
+
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifindex);
+
+ if (is_enable)
+ ps_state = NL80211_PS_ENABLED;
+ else
+ ps_state = NL80211_PS_DISABLED;
+
+ nla_put_u32(msg, NL80211_ATTR_PS_STATE, ps_state);
+
+ if ((err = nl_send_auto(nl_socket, msg)) < 0) {
+ connman_error("Failed to send powersave command.\n");
+ } else {
+ if ((err = suspend_plugin_netlink_handler()) != 0) {
+ connman_error("%s: setting powersave failed for %s with error %d\n", __func__, ifname, err);
+ }
+ }
+
+ nlmsg_free(msg);
+}
+
+/**
+ * @brief Suspend or resume wmtWifi device with gen2 or gen3 driver
+ *
+ * @param ifname interface name (e.g. wlan0)
+ * @param suspend_value suspend value as uint8_t (usually 1 to suspend, 0 to resume)
+ */
+static
+void
+suspend_set_wmtwifi(
const char *ifname,
- int on_off)
+ uint8_t suspend_value)
{
// first try the vendor specific TESTMODE command
struct nl_msg *msg = NULL;
@@ -256,7 +319,7 @@
return;
}
- DBG("iface: %s suspend: %d\n", ifname, (int)on_off ? 0 : 1);
+ DBG("iface: %s suspend value: %d\n", ifname, (int)suspend_value);
msg = nlmsg_alloc();
@@ -265,7 +328,7 @@
memset(&susp_cmd, 0, sizeof susp_cmd);
susp_cmd.header.idx = TESTMODE_CMD_ID_SUSPEND;
susp_cmd.header.buflen = 0; // unused
- susp_cmd.suspend = (int)(on_off) ? 0 : 1;
+ susp_cmd.suspend = suspend_value;
nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifindex);
nla_put(
@@ -308,7 +371,7 @@
priv_cmd.buf,
sizeof(priv_cmd.buf),
"SETSUSPENDMODE %d",
- on_off);
+ (int)suspend_value);
priv_cmd.used_len = cmd_len + 1;
priv_cmd.total_len = PRIV_CMD_SIZE;
@@ -350,7 +413,8 @@
if (access(WMTWIFI_DEVICE, F_OK) == 0) {
// only suspend/unsuspend if we are not in ap mode
if ((if_nametoindex("ap0")) == 0) {
- suspend_handle_display_on_off_iface("wlan0", 1);
+ //resume wmtwifi
+ suspend_set_wmtwifi("wlan0", WMTWIFI_RESUME_VALUE);
} else {
// also enable wowlan for ap0 if it is present.
SET_WOWLAN(ap0);
@@ -375,7 +439,16 @@
if (access(WMTWIFI_DEVICE, F_OK) == 0) {
// only suspend/unsuspend if we are not in ap mode
if ((if_nametoindex("ap0")) == 0) {
- suspend_handle_display_on_off_iface("wlan0", 0);
+ /*
+ wmtWifi (with gen3 driver at least) in Android requires to set power_save
+ to `off` and after that power_save to `on` to correctly suspend wmtWifi device.
+ We are doing that just before calling suspend_set_wmtwifi() after display is turned off.
+ This solution doesn't give us any disadvantages in comparison with Android behaviour.
+ */
+ suspend_set_powersave("wlan0", FALSE);
+ suspend_set_powersave("wlan0", TRUE);
+ //suspend wmtWifi
+ suspend_set_wmtwifi("wlan0", WMTWIFI_SUSPEND_VALUE);
} else {
// also enable wowlan for ap0 if it is present.
SET_WOWLAN(ap0);
|