Search Linux Wireless

[PATCH v2 4/4] qtnfmac: enable networked standby mode on device inactivity

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Sergei Maksimenko <smaksimenko@xxxxxxxxxxxxx>

Enable support of networked standby mode (NSM) on qsr10g devices.
Networked standby is a power saving mode when the device keeps
all existing network connections and returns to full power mode
on a network activity. When enabled, device enters standby mode
after 15 min of inactivity (no associated stations or no trattic).
This period can be changed by setting sysfs attribute standby_timeout
(0 disables NSM support). A module parameter auto_standby
(defaults to 1) controls enabling NSM support on module loading.

Signed-off-by: Sergei Maksimenko <smaksimenko@xxxxxxxxxxxxx>
---
 drivers/net/wireless/quantenna/qtnfmac/bus.h      |  1 +
 drivers/net/wireless/quantenna/qtnfmac/commands.c | 33 ++++++++++
 drivers/net/wireless/quantenna/qtnfmac/commands.h |  1 +
 drivers/net/wireless/quantenna/qtnfmac/core.c     | 77 +++++++++++++++++++++++
 drivers/net/wireless/quantenna/qtnfmac/core.h     |  2 +
 drivers/net/wireless/quantenna/qtnfmac/qlink.h    | 30 +++++++++
 6 files changed, 144 insertions(+)

diff --git a/drivers/net/wireless/quantenna/qtnfmac/bus.h b/drivers/net/wireless/quantenna/qtnfmac/bus.h
index 0a1604683bab..7a27ffc6c7a7 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/bus.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/bus.h
@@ -65,6 +65,7 @@ struct qtnf_bus {
 	struct work_struct event_work;
 	struct mutex bus_lock; /* lock during command/event processing */
 	struct dentry *dbg_dir;
+	u32 standby_timeout;
 	/* bus private data */
 	char bus_priv[0] __aligned(sizeof(void *));
 };
diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c
index deca0060eb27..1e730c9fa371 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/commands.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c
@@ -2753,3 +2753,36 @@ int qtnf_cmd_set_mac_acl(const struct qtnf_vif *vif,
 
 	return ret;
 }
+
+int qtnf_cmd_send_pm_set(struct qtnf_bus *bus, u8 pm_mode, u32 timeout)
+{
+	struct sk_buff *cmd_skb;
+	u16 res_code = QLINK_CMD_RESULT_OK;
+	struct qlink_cmd_pm_set *cmd;
+	int ret = 0;
+
+	cmd_skb = qtnf_cmd_alloc_new_cmdskb(QLINK_MACID_RSVD, QLINK_VIFID_RSVD,
+					    QLINK_CMD_PM_SET, sizeof(*cmd));
+	if (!cmd_skb)
+		return -ENOMEM;
+
+	cmd = (struct qlink_cmd_pm_set *)cmd_skb->data;
+	cmd->pm_mode = pm_mode;
+	cmd->pm_standby_timer = cpu_to_le32(timeout);
+
+	qtnf_bus_lock(bus);
+
+	ret = qtnf_cmd_send(bus, cmd_skb, &res_code);
+
+	if (unlikely(ret))
+		goto out;
+
+	if (unlikely(res_code != QLINK_CMD_RESULT_OK)) {
+		pr_err("cmd exec failed: 0x%.4X\n", res_code);
+		ret = -EFAULT;
+	}
+
+out:
+	qtnf_bus_unlock(bus);
+	return ret;
+}
diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.h b/drivers/net/wireless/quantenna/qtnfmac/commands.h
index 69a7d56f7e58..a06e6a96c35d 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/commands.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/commands.h
@@ -81,5 +81,6 @@ int qtnf_cmd_start_cac(const struct qtnf_vif *vif,
 		       u32 cac_time_ms);
 int qtnf_cmd_set_mac_acl(const struct qtnf_vif *vif,
 			 const struct cfg80211_acl_data *params);
+int qtnf_cmd_send_pm_set(struct qtnf_bus *bus, u8 pm_mode, u32 timeout);
 
 #endif /* QLINK_COMMANDS_H_ */
diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.c b/drivers/net/wireless/quantenna/qtnfmac/core.c
index cf26c15a84f8..10c4e3ea2404 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/core.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/core.c
@@ -26,6 +26,10 @@
 #include "event.h"
 #include "util.h"
 
+static bool auto_standby = true;
+module_param(auto_standby, bool, 0644);
+MODULE_PARM_DESC(auto_standby, "set to 0 to disable auto standby mode");
+
 #define QTNF_DMP_MAX_LEN 48
 #define QTNF_PRIMARY_VIF_IDX	0
 
@@ -552,6 +556,53 @@ static int qtnf_core_mac_attach(struct qtnf_bus *bus, unsigned int macid)
 	return ret;
 }
 
+static ssize_t qtnf_pm_standby_timeout_show(struct device *dev,
+					    struct device_attribute *attr,
+					    char *buf)
+{
+	struct qtnf_bus *bus = dev_get_drvdata(dev);
+
+	sprintf(buf, "%u\n", bus->standby_timeout);
+	return strlen(buf);
+}
+
+static ssize_t qtnf_pm_standby_timeout_store(struct device *dev,
+					     struct device_attribute *attr,
+					     const char *buf, size_t count)
+{
+	struct qtnf_bus *bus = dev_get_drvdata(dev);
+	int timeout;
+
+	if (count < 1)
+		goto out;
+
+	if (kstrtoint(buf, 0, &timeout))
+		goto out;
+
+	if (timeout < 0)
+		timeout = 0;
+	else if (timeout > S32_MAX)
+		timeout = S32_MAX;
+
+	if (timeout == bus->standby_timeout)
+		goto out;
+
+	if (timeout) {
+		if (!qtnf_cmd_send_pm_set(bus, QLINK_PM_AUTO_STANDBY,
+					  timeout))
+			bus->standby_timeout = timeout;
+	} else {
+		if (!qtnf_cmd_send_pm_set(bus, QLINK_PM_OFF, 0))
+			bus->standby_timeout = 0;
+	}
+
+out:
+	return (ssize_t)count;
+}
+
+static DEVICE_ATTR(standby_timeout, 0644, qtnf_pm_standby_timeout_show,
+		   qtnf_pm_standby_timeout_store);
+
 int qtnf_core_attach(struct qtnf_bus *bus)
 {
 	unsigned int i;
@@ -608,6 +659,25 @@ int qtnf_core_attach(struct qtnf_bus *bus)
 		}
 	}
 
+	if (auto_standby) {
+		bus->standby_timeout = QTNF_DEFAULT_STANDBY_TIMER;
+		ret = qtnf_cmd_send_pm_set(bus, QLINK_PM_AUTO_STANDBY,
+					   bus->standby_timeout);
+		if (ret)
+			bus->standby_timeout = 0;
+	} else {
+		bus->standby_timeout = 0;
+		ret = qtnf_cmd_send_pm_set(bus, QLINK_PM_OFF, 0);
+	}
+
+	if (ret) {
+		pr_err("failed to init PM auto standby: %d\n", ret);
+		/* Do not cancel init when PM mode not configured */
+	}
+
+	if (device_create_file(bus->dev, &dev_attr_standby_timeout))
+		pr_err("failed to init sysfs standby control file: %d\n", ret);
+
 	return 0;
 
 error:
@@ -620,6 +690,13 @@ EXPORT_SYMBOL_GPL(qtnf_core_attach);
 void qtnf_core_detach(struct qtnf_bus *bus)
 {
 	unsigned int macid;
+	int ret;
+
+	device_remove_file(bus->dev, &dev_attr_standby_timeout);
+
+	ret = qtnf_cmd_send_pm_set(bus, QLINK_PM_OFF, 0);
+	if (ret)
+		pr_err("failed to deinit NSM: %d\n", ret);
 
 	qtnf_bus_data_rx_stop(bus);
 
diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.h b/drivers/net/wireless/quantenna/qtnfmac/core.h
index 3b884c80b6ab..9386f09e1fab 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/core.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/core.h
@@ -52,6 +52,8 @@
 #define QTNF_DEF_WDOG_TIMEOUT		5
 #define QTNF_TX_TIMEOUT_TRSHLD		100
 
+#define QTNF_DEFAULT_STANDBY_TIMER	(15 * 60)
+
 extern const struct net_device_ops qtnf_netdev_ops;
 
 struct qtnf_bus;
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
index 9bf3ae4d1b3b..ee3c65ca7148 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
@@ -251,6 +251,7 @@ enum qlink_cmd_type {
 	QLINK_CMD_CHAN_STATS		= 0x0054,
 	QLINK_CMD_CONNECT		= 0x0060,
 	QLINK_CMD_DISCONNECT		= 0x0061,
+	QLINK_CMD_PM_SET		= 0x0062,
 };
 
 /**
@@ -663,6 +664,35 @@ struct qlink_acl_data {
 	struct qlink_mac_address mac_addrs[0];
 } __packed;
 
+/**
+ * enum qlink_pm_mode - Power Management mode
+ *
+ * @QLINK_PM_OFF: normal mode, no power saving enabled
+ * @QLINK_PM_AUTO_STANDBY: Automatic Network Standby Mode - when there is no
+ *	traffic for the certain period, device enters power saving mode without
+ *	disconnecting peers. Device will wake up automatically on a new
+ *	association or data frames to TX/RX. Standby mode is activated on each
+ *	radio interface individually, based on traffic on it. When all the
+ *	radios enter standby mode, device informs RC via MSI.
+ */
+enum qlink_pm_mode {
+	QLINK_PM_OFF		= 0,
+	QLINK_PM_AUTO_STANDBY	= 1,
+};
+
+/**
+ * struct qlink_cmd_pm_set - data for QLINK_CMD_PM_SET command
+ *
+ * @pm_standby timer: period of network inactivity in seconds before
+ *	putting a radio in standby mode
+ * @pm_mode: power management mode
+ */
+struct qlink_cmd_pm_set {
+	struct qlink_cmd chdr;
+	__le32 pm_standby_timer;
+	u8 pm_mode;
+} __packed;
+
 /* QLINK Command Responses messages related definitions
  */
 
-- 
2.11.0




[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Wireless Regulations]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux