All of lore.kernel.org
 help / color / mirror / Atom feed
* [v2 1/3] rsi: sdio: add WOWLAN support for S3 suspend state
@ 2017-10-25 13:44 Amitkumar Karwar
  0 siblings, 0 replies; only message in thread
From: Amitkumar Karwar @ 2017-10-25 13:44 UTC (permalink / raw)
  To: Kalle Valo
  Cc: linux-wireless, Amitkumar Karwar, Prameela Rani Garnepudi,
	Karun Eagalapati

From: Karun Eagalapati <karun256@gmail.com>

WoWLAN is supported in RS9113 device through GPIO pin2.
wowlan config frame is internally sent to firmware in mac80211
suspend handler. Also beacon miss threshold and keep-alive time
values are increased to avoid un-necessary disconnection with AP.

Signed-off-by: Karun Eagalapati <karun256@gmail.com>
Signed-off-by: Amitkumar Karwar <amit.karwar@redpinesignals.com>
---
 drivers/net/wireless/rsi/rsi_91x_core.c     |   6 ++
 drivers/net/wireless/rsi/rsi_91x_mac80211.c | 118 ++++++++++++++++++++++++++++
 drivers/net/wireless/rsi/rsi_91x_mgmt.c     |  60 +++++++++++---
 drivers/net/wireless/rsi/rsi_91x_sdio.c     |   7 ++
 drivers/net/wireless/rsi/rsi_common.h       |   1 +
 drivers/net/wireless/rsi/rsi_main.h         |   8 +-
 drivers/net/wireless/rsi/rsi_mgmt.h         |  31 +++++++-
 7 files changed, 220 insertions(+), 11 deletions(-)

diff --git a/drivers/net/wireless/rsi/rsi_91x_core.c b/drivers/net/wireless/rsi/rsi_91x_core.c
index bc18a19..87e023d 100644
--- a/drivers/net/wireless/rsi/rsi_91x_core.c
+++ b/drivers/net/wireless/rsi/rsi_91x_core.c
@@ -379,6 +379,12 @@ void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb)
 		rsi_dbg(ERR_ZONE, "%s: FSM state not open\n", __func__);
 		goto xmit_fail;
 	}
+	if (common->wow_flags & RSI_WOW_ENABLED) {
+		rsi_dbg(ERR_ZONE,
+			"%s: Blocking Tx_packets when WOWLAN is enabled\n",
+			__func__);
+		goto xmit_fail;
+	}
 
 	info = IEEE80211_SKB_CB(skb);
 	tx_params = (struct skb_info *)info->driver_data;
diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
index b1f5dbb..2bcf412 100644
--- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c
+++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
@@ -1746,6 +1746,119 @@ static int rsi_mac80211_cancel_roc(struct ieee80211_hw *hw)
 	return 0;
 }
 
+static const struct wiphy_wowlan_support rsi_wowlan_support = {
+	.flags = WIPHY_WOWLAN_ANY |
+		 WIPHY_WOWLAN_MAGIC_PKT |
+		 WIPHY_WOWLAN_DISCONNECT |
+		 WIPHY_WOWLAN_GTK_REKEY_FAILURE  |
+		 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
+		 WIPHY_WOWLAN_EAP_IDENTITY_REQ   |
+		 WIPHY_WOWLAN_4WAY_HANDSHAKE,
+};
+
+static u16 rsi_wow_map_triggers(struct rsi_common *common,
+				struct cfg80211_wowlan *wowlan)
+{
+	u16 wow_triggers = 0;
+
+	rsi_dbg(INFO_ZONE, "Mapping wowlan triggers\n");
+
+	if (wowlan->any)
+		wow_triggers |= RSI_WOW_ANY;
+	if (wowlan->magic_pkt)
+		wow_triggers |= RSI_WOW_MAGIC_PKT;
+	if (wowlan->disconnect)
+		wow_triggers |= RSI_WOW_DISCONNECT;
+	if (wowlan->gtk_rekey_failure || wowlan->eap_identity_req ||
+	    wowlan->four_way_handshake)
+		wow_triggers |= RSI_WOW_GTK_REKEY;
+
+	return wow_triggers;
+}
+
+int rsi_config_wowlan(struct rsi_hw *adapter, struct cfg80211_wowlan *wowlan)
+{
+	struct rsi_common *common = adapter->priv;
+	u16 triggers = 0;
+	u16 rx_filter_word = 0;
+	struct ieee80211_bss_conf *bss = &adapter->vifs[0]->bss_conf;
+
+	rsi_dbg(INFO_ZONE, "Config WoWLAN to device\n");
+
+	if (WARN_ON(!wowlan)) {
+		rsi_dbg(ERR_ZONE, "WoW triggers not enabled\n");
+		return -EINVAL;
+	}
+
+	triggers = rsi_wow_map_triggers(common, wowlan);
+	if (!triggers) {
+		rsi_dbg(ERR_ZONE, "%s:No valid WoW triggers\n", __func__);
+		return -EINVAL;
+	}
+	if (!bss->assoc) {
+		rsi_dbg(ERR_ZONE,
+			"Cannot configure WoWLAN (Station not connected)\n");
+		common->wow_flags |= RSI_WOW_NO_CONNECTION;
+		return 0;
+	}
+	rsi_dbg(INFO_ZONE, "TRIGGERS %x\n", triggers);
+	rsi_send_wowlan_request(common, triggers, 1);
+
+	/**
+	 * Increase the beacon_miss threshold & keep-alive timers in
+	 * vap_update frame
+	 */
+	rsi_send_vap_dynamic_update(common);
+
+	rx_filter_word = (ALLOW_DATA_ASSOC_PEER | DISALLOW_BEACONS);
+	rsi_send_rx_filter_frame(common, rx_filter_word);
+	common->wow_flags |= RSI_WOW_ENABLED;
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int rsi_mac80211_suspend(struct ieee80211_hw *hw,
+				struct cfg80211_wowlan *wowlan)
+{
+	struct rsi_hw *adapter = hw->priv;
+	struct rsi_common *common = adapter->priv;
+
+	rsi_dbg(INFO_ZONE, "%s: mac80211 suspend\n", __func__);
+	mutex_lock(&common->mutex);
+	if (rsi_config_wowlan(adapter, wowlan)) {
+		rsi_dbg(ERR_ZONE, "Failed to configure WoWLAN\n");
+		mutex_unlock(&common->mutex);
+		return 1;
+	}
+	mutex_unlock(&common->mutex);
+
+	return 0;
+}
+
+static int rsi_mac80211_resume(struct ieee80211_hw *hw)
+{
+	u16 rx_filter_word = 0;
+	struct rsi_hw *adapter = hw->priv;
+	struct rsi_common *common = adapter->priv;
+
+	common->wow_flags = 0;
+
+	rsi_dbg(INFO_ZONE, "%s: mac80211 resume\n", __func__);
+
+	mutex_lock(&common->mutex);
+	rsi_send_wowlan_request(common, 0, 0);
+
+	rx_filter_word = (ALLOW_DATA_ASSOC_PEER | ALLOW_CTRL_ASSOC_PEER |
+			  ALLOW_MGMT_ASSOC_PEER);
+	rsi_send_rx_filter_frame(common, rx_filter_word);
+	mutex_unlock(&common->mutex);
+
+	return 0;
+}
+
+#endif
+
 static const struct ieee80211_ops mac80211_ops = {
 	.tx = rsi_mac80211_tx,
 	.start = rsi_mac80211_start,
@@ -1767,6 +1880,10 @@ static const struct ieee80211_ops mac80211_ops = {
 	.rfkill_poll = rsi_mac80211_rfkill_poll,
 	.remain_on_channel = rsi_mac80211_roc,
 	.cancel_remain_on_channel = rsi_mac80211_cancel_roc,
+#ifdef CONFIG_PM
+	.suspend = rsi_mac80211_suspend,
+	.resume  = rsi_mac80211_resume,
+#endif
 };
 
 /**
@@ -1850,6 +1967,7 @@ int rsi_mac80211_attach(struct rsi_common *common)
 	wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER;
 	wiphy->reg_notifier = rsi_reg_notify;
 
+	wiphy->wowlan = &rsi_wowlan_support;
 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
 
 	/* Wi-Fi direct parameters */
diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
index 4b94190..1446ee3 100644
--- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c
+++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
@@ -1094,9 +1094,18 @@ int rsi_send_vap_dynamic_update(struct rsi_common *common)
 	dynamic_frame->desc_dword0.frame_type = VAP_DYNAMIC_UPDATE;
 	dynamic_frame->desc_dword2.pkt_info =
 					cpu_to_le32(common->rts_threshold);
-	/* Beacon miss threshold */
-	dynamic_frame->frame_body.keep_alive_period =
+
+	if (common->wow_flags & RSI_WOW_ENABLED) {
+		/* Beacon miss threshold */
+		dynamic_frame->desc_dword3.token =
+					cpu_to_le16(RSI_BCN_MISS_THRESHOLD);
+		dynamic_frame->frame_body.keep_alive_period =
+					cpu_to_le16(RSI_WOW_KEEPALIVE);
+	} else {
+		dynamic_frame->frame_body.keep_alive_period =
 					cpu_to_le16(RSI_DEF_KEEPALIVE);
+	}
+
 	dynamic_frame->desc_dword3.sta_id = 0; /* vap id */
 
 	skb_put(skb, sizeof(struct rsi_dynamic_s));
@@ -1340,13 +1349,12 @@ void rsi_inform_bss_status(struct rsi_common *common,
 	} else {
 		if (opmode == RSI_OPMODE_STA)
 			common->hw_data_qs_blocked = true;
-		rsi_hal_send_sta_notify_frame(common,
-					      opmode,
-					      STA_DISCONNECTED,
-					      addr,
-					      qos_enable,
-					      aid, sta_id,
-					      vif);
+
+		if (!(common->wow_flags & RSI_WOW_ENABLED))
+			rsi_hal_send_sta_notify_frame(common, opmode,
+						      STA_DISCONNECTED, addr,
+						      qos_enable, aid, sta_id,
+						      vif);
 		if (opmode == RSI_OPMODE_STA)
 			rsi_send_block_unblock_frame(common, true);
 	}
@@ -1589,6 +1597,40 @@ static int rsi_send_beacon(struct rsi_common *common)
 	return 0;
 }
 
+int rsi_send_wowlan_request(struct rsi_common *common, u16 flags,
+			    u16 sleep_status)
+{
+	struct rsi_wowlan_req *cmd_frame;
+	struct sk_buff *skb;
+	u8 length;
+
+	rsi_dbg(ERR_ZONE, "%s: Sending wowlan request frame\n", __func__);
+
+	length = sizeof(*cmd_frame);
+	skb = dev_alloc_skb(length);
+	if (!skb)
+		return -ENOMEM;
+	memset(skb->data, 0, length);
+	cmd_frame = (struct rsi_wowlan_req *)skb->data;
+
+	rsi_set_len_qno(&cmd_frame->desc.desc_dword0.len_qno,
+			(length - FRAME_DESC_SZ),
+			RSI_WIFI_MGMT_Q);
+	cmd_frame->desc.desc_dword0.frame_type = WOWLAN_CONFIG_PARAMS;
+	cmd_frame->host_sleep_status = sleep_status;
+	if (common->secinfo.security_enable &&
+	    common->secinfo.gtk_cipher)
+		flags |= RSI_WOW_GTK_REKEY;
+	if (sleep_status)
+		cmd_frame->wow_flags = flags;
+	rsi_dbg(INFO_ZONE, "Host_Sleep_Status : %d Flags : %d\n",
+		cmd_frame->host_sleep_status, cmd_frame->wow_flags);
+
+	skb_put(skb, length);
+
+	return rsi_send_internal_mgmt_frame(common, skb);
+}
+
 /**
  * rsi_handle_ta_confirm_type() - This function handles the confirm frames.
  * @common: Pointer to the driver private structure.
diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio.c b/drivers/net/wireless/rsi/rsi_91x_sdio.c
index b3f8006..fa6af7b 100644
--- a/drivers/net/wireless/rsi/rsi_91x_sdio.c
+++ b/drivers/net/wireless/rsi/rsi_91x_sdio.c
@@ -1124,6 +1124,8 @@ static int rsi_sdio_enable_interrupts(struct sdio_func *pfunc)
 {
 	u8 data;
 	int ret;
+	struct rsi_hw *adapter = sdio_get_drvdata(pfunc);
+	struct rsi_common *common = adapter->priv;
 
 	sdio_claim_host(pfunc);
 	ret = rsi_cmd52readbyte(pfunc->card, RSI_INT_ENABLE_REGISTER, &data);
@@ -1143,6 +1145,11 @@ static int rsi_sdio_enable_interrupts(struct sdio_func *pfunc)
 		goto done;
 	}
 
+	if ((common->wow_flags & RSI_WOW_ENABLED) &&
+	    (common->wow_flags & RSI_WOW_NO_CONNECTION))
+		rsi_dbg(ERR_ZONE,
+			"##### Device can not wake up through WLAN\n");
+
 	ret = rsi_cmd52readbyte(pfunc->card, RSI_INT_ENABLE_REGISTER, &data);
 	if (ret < 0) {
 		rsi_dbg(ERR_ZONE,
diff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h
index 272e18d..a03fa7f 100644
--- a/drivers/net/wireless/rsi/rsi_common.h
+++ b/drivers/net/wireless/rsi/rsi_common.h
@@ -83,6 +83,7 @@ u16 rsi_get_connected_channel(struct ieee80211_vif *vif);
 struct rsi_hw *rsi_91x_init(void);
 void rsi_91x_deinit(struct rsi_hw *adapter);
 int rsi_read_pkt(struct rsi_common *common, s32 rcv_pkt_len);
+int rsi_config_wowlan(struct rsi_hw *adapter, struct cfg80211_wowlan *wowlan);
 struct rsi_sta *rsi_find_sta(struct rsi_common *common, u8 *mac_addr);
 struct ieee80211_vif *rsi_get_vif(struct rsi_hw *adapter, u8 *mac);
 void rsi_roc_timeout(unsigned long data);
diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h
index a118b7a..44a199f 100644
--- a/drivers/net/wireless/rsi/rsi_main.h
+++ b/drivers/net/wireless/rsi/rsi_main.h
@@ -66,6 +66,8 @@ extern __printf(2, 3) void rsi_dbg(u32 zone, const char *fmt, ...);
 #define FRAME_DESC_SZ                   16
 #define MIN_802_11_HDR_LEN              24
 #define RSI_DEF_KEEPALIVE               90
+#define RSI_WOW_KEEPALIVE                5
+#define RSI_BCN_MISS_THRESHOLD           24
 
 #define DATA_QUEUE_WATER_MARK           400
 #define MIN_DATA_QUEUE_WATER_MARK       300
@@ -108,6 +110,10 @@ extern __printf(2, 3) void rsi_dbg(u32 zone, const char *fmt, ...);
 	((_q) == VI_Q) ? IEEE80211_AC_VI : \
 	IEEE80211_AC_VO)
 
+/* WoWLAN flags */
+#define RSI_WOW_ENABLED			BIT(0)
+#define RSI_WOW_NO_CONNECTION		BIT(1)
+
 #define RSI_DEV_9113		1
 
 struct version_info {
@@ -266,7 +272,7 @@ struct rsi_common {
 	u8 obm_ant_sel_val;
 	int tx_power;
 	u8 ant_in_use;
-
+	u8 wow_flags;
 	u16 beacon_interval;
 	u8 dtim_cnt;
 
diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h
index e217230..76337ce 100644
--- a/drivers/net/wireless/rsi/rsi_mgmt.h
+++ b/drivers/net/wireless/rsi/rsi_mgmt.h
@@ -45,6 +45,17 @@
 #define MAGIC_WORD                      0x5A
 #define WLAN_EEPROM_RFTYPE_ADDR		424
 
+/*WOWLAN RESUME WAKEUP TYPES*/
+#define RSI_UNICAST_MAGIC_PKT		BIT(0)
+#define RSI_BROADCAST_MAGICPKT		BIT(1)
+#define RSI_EAPOL_PKT			BIT(2)
+#define RSI_DISCONNECT_PKT		BIT(3)
+#define RSI_HW_BMISS_PKT		BIT(4)
+#define RSI_INSERT_SEQ_IN_FW		BIT(2)
+
+#define WOW_MAX_FILTERS_PER_LIST 16
+#define WOW_PATTERN_SIZE 256
+
 /* Receive Frame Types */
 #define TA_CONFIRM_TYPE                 0x01
 #define RX_DOT11_MGMT                   0x02
@@ -201,6 +212,13 @@
 #define RSI_DATA_DESC_INSERT_TSF	BIT(15)
 #define RSI_DATA_DESC_INSERT_SEQ_NO	BIT(2)
 
+#ifdef CONFIG_PM
+#define RSI_WOW_ANY			BIT(1)
+#define RSI_WOW_GTK_REKEY		BIT(3)
+#define RSI_WOW_MAGIC_PKT		BIT(4)
+#define RSI_WOW_DISCONNECT		BIT(5)
+#endif
+
 enum opmode {
 	RSI_OPMODE_UNSUPPORTED = -1,
 	RSI_OPMODE_AP = 0,
@@ -262,7 +280,9 @@ enum cmd_frame_type {
 	ANT_SEL_FRAME = 0x20,
 	VAP_DYNAMIC_UPDATE = 0x27,
 	COMMON_DEV_CONFIG = 0x28,
-	RADIO_PARAMS_UPDATE = 0x29
+	RADIO_PARAMS_UPDATE = 0x29,
+	WOWLAN_CONFIG_PARAMS = 0x2B,
+	WOWLAN_WAKEUP_REASON = 0xc5
 };
 
 struct rsi_mac_frame {
@@ -581,6 +601,13 @@ struct rsi_request_ps {
 	__le16 ps_num_dtim_intervals;
 } __packed;
 
+struct rsi_wowlan_req {
+	struct rsi_cmd_desc desc;
+	u8 sourceid[ETH_ALEN];
+	u16 wow_flags;
+	u16 host_sleep_status;
+} __packed;
+
 static inline u32 rsi_get_queueno(u8 *addr, u16 offset)
 {
 	return (le16_to_cpu(*(__le16 *)&addr[offset]) & 0x7000) >> 12;
@@ -641,6 +668,8 @@ int rsi_band_check(struct rsi_common *common, struct ieee80211_channel *chan);
 int rsi_send_rx_filter_frame(struct rsi_common *common, u16 rx_filter_word);
 int rsi_send_radio_params_update(struct rsi_common *common);
 int rsi_set_antenna(struct rsi_common *common, u8 antenna);
+int rsi_send_wowlan_request(struct rsi_common *common, u16 flags,
+			    u16 sleep_status);
 int rsi_send_ps_request(struct rsi_hw *adapter, bool enable,
 			struct ieee80211_vif *vif);
 #endif
-- 
2.7.4

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2017-10-25 13:50 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-10-25 13:44 [v2 1/3] rsi: sdio: add WOWLAN support for S3 suspend state Amitkumar Karwar

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.