Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH v2 6/9] wil6210: add support for headroom configuration
From: Maya Erez @ 2017-12-14 16:53 UTC (permalink / raw)
  To: Kalle Valo; +Cc: Lazar Alexei, linux-wireless, wil6210, Maya Erez
In-Reply-To: <1513270393-919-1-git-send-email-qca_merez@qca.qualcomm.com>

From: Lazar Alexei <qca_ailizaro@qca.qualcomm.com>

Add module parameter for configuring the headroom size
in the skb allocation.

Signed-off-by: Lazar Alexei <qca_ailizaro@qca.qualcomm.com>
Signed-off-by: Maya Erez <qca_merez@qca.qualcomm.com>
---
 drivers/net/wireless/ath/wil6210/txrx.c | 28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 62c04f0..89967ce 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -41,6 +41,32 @@
 module_param(rx_large_buf, bool, 0444);
 MODULE_PARM_DESC(rx_large_buf, " allocate 8KB RX buffers, default - no");
 
+#define WIL6210_MAX_HEADROOM_SIZE	(256)
+
+static ushort headroom_size; /* = 0; */
+static int headroom_size_set(const char *val, const struct kernel_param *kp)
+{
+	int ret;
+
+	ret = param_set_uint(val, kp);
+	if (ret)
+		return ret;
+
+	if (headroom_size > WIL6210_MAX_HEADROOM_SIZE)
+		return -EINVAL;
+
+	return 0;
+}
+
+static const struct kernel_param_ops headroom_ops = {
+	.set = headroom_size_set,
+	.get = param_get_ushort,
+};
+
+module_param_cb(headroom_size, &headroom_ops, &headroom_size, 0644);
+MODULE_PARM_DESC(headroom_size,
+		 " headroom size for rx skb allocation, default - 0");
+
 static inline uint wil_rx_snaplen(void)
 {
 	return rx_align_2 ? 6 : 0;
@@ -630,7 +656,7 @@ static int wil_rx_refill(struct wil6210_priv *wil, int count)
 	u32 next_tail;
 	int rc = 0;
 	int headroom = ndev->type == ARPHRD_IEEE80211_RADIOTAP ?
-			WIL6210_RTAP_SIZE : 0;
+			WIL6210_RTAP_SIZE : headroom_size;
 
 	for (; next_tail = wil_vring_next_tail(v),
 			(next_tail != v->swhead) && (count-- > 0);
-- 
1.9.1

^ permalink raw reply related

* [PATCH v2 5/9] wil6210: prevent parallel suspend and dump collection
From: Maya Erez @ 2017-12-14 16:53 UTC (permalink / raw)
  To: Kalle Valo; +Cc: Maya Erez, linux-wireless, wil6210
In-Reply-To: <1513270393-919-1-git-send-email-qca_merez@qca.qualcomm.com>

Suspend and crash dump operations can happen simultaneously
in case there is a FW assert during the suspend procedure
or when SSR calls all the devices crashdump callbacks.

To prevent that, a new flag is added, indicating that the
dumps collection is in progress, in order to allow the
suspend/reset decline if the dumps collection already started.

Signed-off-by: Maya Erez <qca_merez@qca.qualcomm.com>
---
 drivers/net/wireless/ath/wil6210/main.c           | 33 +++++++++++++++++------
 drivers/net/wireless/ath/wil6210/pm.c             | 17 ++++++++++++
 drivers/net/wireless/ath/wil6210/wil6210.h        |  1 +
 drivers/net/wireless/ath/wil6210/wil_crash_dump.c | 11 ++++++++
 4 files changed, 54 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 7a8f8c2..aa6f9c4 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -998,6 +998,7 @@ static void wil_pre_fw_config(struct wil6210_priv *wil)
 int wil_reset(struct wil6210_priv *wil, bool load_fw)
 {
 	int rc;
+	unsigned long status_flags = BIT(wil_status_resetting);
 
 	wil_dbg_misc(wil, "reset\n");
 
@@ -1037,6 +1038,14 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
 	}
 
 	set_bit(wil_status_resetting, wil->status);
+	if (test_bit(wil_status_collecting_dumps, wil->status)) {
+		/* Device collects crash dump, cancel the reset.
+		 * following crash dump collection, reset would take place.
+		 */
+		wil_dbg_misc(wil, "reject reset while collecting crash dump\n");
+		rc = -EBUSY;
+		goto out;
+	}
 
 	cancel_work_sync(&wil->disconnect_worker);
 	wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
@@ -1051,7 +1060,11 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
 
 	/* prevent NAPI from being scheduled and prevent wmi commands */
 	mutex_lock(&wil->wmi_mutex);
-	bitmap_zero(wil->status, wil_status_last);
+	if (test_bit(wil_status_suspending, wil->status))
+		status_flags |= BIT(wil_status_suspending);
+	bitmap_and(wil->status, wil->status, &status_flags,
+		   wil_status_last);
+	wil_dbg_misc(wil, "wil->status (0x%lx)\n", *wil->status);
 	mutex_unlock(&wil->wmi_mutex);
 
 	wil_mask_irq(wil);
@@ -1069,14 +1082,14 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
 	wil_rx_fini(wil);
 	if (rc) {
 		wil_bl_crash_info(wil, true);
-		return rc;
+		goto out;
 	}
 
 	rc = wil_get_bl_info(wil);
 	if (rc == -EAGAIN && !load_fw) /* ignore RF error if not going up */
 		rc = 0;
 	if (rc)
-		return rc;
+		goto out;
 
 	wil_set_oob_mode(wil, oob_mode);
 	if (load_fw) {
@@ -1088,10 +1101,10 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
 		/* Loading f/w from the file */
 		rc = wil_request_firmware(wil, wil->wil_fw_name, true);
 		if (rc)
-			return rc;
+			goto out;
 		rc = wil_request_firmware(wil, WIL_BOARD_FILE_NAME, true);
 		if (rc)
-			return rc;
+			goto out;
 
 		wil_pre_fw_config(wil);
 		wil_release_cpu(wil);
@@ -1103,6 +1116,8 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
 	reinit_completion(&wil->wmi_call);
 	reinit_completion(&wil->halp.comp);
 
+	clear_bit(wil_status_resetting, wil->status);
+
 	if (load_fw) {
 		wil_configure_interrupt_moderation(wil);
 		wil_unmask_irq(wil);
@@ -1136,6 +1151,10 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
 	}
 
 	return rc;
+
+out:
+	clear_bit(wil_status_resetting, wil->status);
+	return rc;
 }
 
 void wil_fw_error_recovery(struct wil6210_priv *wil)
@@ -1241,9 +1260,7 @@ int __wil_down(struct wil6210_priv *wil)
 	wil_abort_scan(wil, false);
 	mutex_unlock(&wil->p2p_wdev_mutex);
 
-	wil_reset(wil, false);
-
-	return 0;
+	return wil_reset(wil, false);
 }
 
 int wil_down(struct wil6210_priv *wil)
diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c
index 056b180..0a96518 100644
--- a/drivers/net/wireless/ath/wil6210/pm.c
+++ b/drivers/net/wireless/ath/wil6210/pm.c
@@ -145,6 +145,13 @@ static int wil_suspend_keep_radio_on(struct wil6210_priv *wil)
 
 	/* Prevent handling of new tx and wmi commands */
 	set_bit(wil_status_suspending, wil->status);
+	if (test_bit(wil_status_collecting_dumps, wil->status)) {
+		/* Device collects crash dump, cancel the suspend */
+		wil_dbg_pm(wil, "reject suspend while collecting crash dump\n");
+		clear_bit(wil_status_suspending, wil->status);
+		wil->suspend_stats.rejected_by_host++;
+		return -EBUSY;
+	}
 	wil_update_net_queues_bh(wil, NULL, true);
 
 	if (!wil_is_tx_idle(wil)) {
@@ -255,6 +262,15 @@ static int wil_suspend_radio_off(struct wil6210_priv *wil)
 
 	wil_dbg_pm(wil, "suspend radio off\n");
 
+	set_bit(wil_status_suspending, wil->status);
+	if (test_bit(wil_status_collecting_dumps, wil->status)) {
+		/* Device collects crash dump, cancel the suspend */
+		wil_dbg_pm(wil, "reject suspend while collecting crash dump\n");
+		clear_bit(wil_status_suspending, wil->status);
+		wil->suspend_stats.rejected_by_host++;
+		return -EBUSY;
+	}
+
 	/* if netif up, hardware is alive, shut it down */
 	if (ndev->flags & IFF_UP) {
 		rc = wil_down(wil);
@@ -281,6 +297,7 @@ static int wil_suspend_radio_off(struct wil6210_priv *wil)
 	set_bit(wil_status_suspended, wil->status);
 
 out:
+	clear_bit(wil_status_suspending, wil->status);
 	wil_dbg_pm(wil, "suspend radio off: %d\n", rc);
 
 	return rc;
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index f2bb55e..ca5abbd 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -445,6 +445,7 @@ enum { /* for wil6210_priv.status */
 	wil_status_suspending, /* suspend in progress */
 	wil_status_suspended, /* suspend completed, device is suspended */
 	wil_status_resuming, /* resume in progress */
+	wil_status_collecting_dumps, /* crashdump collection in progress */
 	wil_status_last /* keep last */
 };
 
diff --git a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c
index e53cf0c..1ed3306 100644
--- a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c
+++ b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c
@@ -72,6 +72,15 @@ int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size)
 		return -EINVAL;
 	}
 
+	set_bit(wil_status_collecting_dumps, wil->status);
+	if (test_bit(wil_status_suspending, wil->status) ||
+	    test_bit(wil_status_suspended, wil->status) ||
+	    test_bit(wil_status_resetting, wil->status)) {
+		wil_err(wil, "cannot collect fw dump during suspend/reset\n");
+		clear_bit(wil_status_collecting_dumps, wil->status);
+		return -EINVAL;
+	}
+
 	/* copy to crash dump area */
 	for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) {
 		map = &fw_mapping[i];
@@ -91,6 +100,8 @@ int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size)
 				     (const void __iomem * __force)data, len);
 	}
 
+	clear_bit(wil_status_collecting_dumps, wil->status);
+
 	return 0;
 }
 
-- 
1.9.1

^ permalink raw reply related

* [PATCH v2 4/9] wil6210: set platform features based on FW capabilities
From: Maya Erez @ 2017-12-14 16:53 UTC (permalink / raw)
  To: Kalle Valo; +Cc: Maya Erez, linux-wireless, wil6210
In-Reply-To: <1513270393-919-1-git-send-email-qca_merez@qca.qualcomm.com>

In some cases the platform should be aware of the FW capabilities
to decide which feature to enable.
For example, FW can control the external REF clock for power saving.
Driver should notify the platform about that, to allow platform
power management optimization.

Signed-off-by: Maya Erez <qca_merez@qca.qualcomm.com>
---
 drivers/net/wireless/ath/wil6210/main.c         | 11 +++++++++++
 drivers/net/wireless/ath/wil6210/wil_platform.h |  6 ++++++
 drivers/net/wireless/ath/wil6210/wmi.h          |  1 +
 3 files changed, 18 insertions(+)

diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index bafd8d5..7a8f8c2 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -771,6 +771,7 @@ static void wil_collect_fw_info(struct wil6210_priv *wil)
 void wil_refresh_fw_capabilities(struct wil6210_priv *wil)
 {
 	struct wiphy *wiphy = wil_to_wiphy(wil);
+	int features;
 
 	wil->keep_radio_on_during_sleep =
 		test_bit(WIL_PLATFORM_CAPA_RADIO_ON_IN_SUSPEND,
@@ -792,6 +793,16 @@ void wil_refresh_fw_capabilities(struct wil6210_priv *wil)
 		wiphy->max_sched_scan_ie_len = WMI_MAX_IE_LEN;
 		wiphy->max_sched_scan_plans = WMI_MAX_PLANS_NUM;
 	}
+
+	if (wil->platform_ops.set_features) {
+		features = (test_bit(WMI_FW_CAPABILITY_REF_CLOCK_CONTROL,
+				     wil->fw_capabilities) &&
+			    test_bit(WIL_PLATFORM_CAPA_EXT_CLK,
+				     wil->platform_capa)) ?
+			BIT(WIL_PLATFORM_FEATURE_FW_EXT_CLK_CONTROL) : 0;
+
+		wil->platform_ops.set_features(wil->platform_handle, features);
+	}
 }
 
 void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r)
diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.h b/drivers/net/wireless/ath/wil6210/wil_platform.h
index 5cfb946..177026e 100644
--- a/drivers/net/wireless/ath/wil6210/wil_platform.h
+++ b/drivers/net/wireless/ath/wil6210/wil_platform.h
@@ -27,6 +27,11 @@ enum wil_platform_event {
 	WIL_PLATFORM_EVT_POST_SUSPEND = 4,
 };
 
+enum wil_platform_features {
+	WIL_PLATFORM_FEATURE_FW_EXT_CLK_CONTROL = 0,
+	WIL_PLATFORM_FEATURE_MAX,
+};
+
 enum wil_platform_capa {
 	WIL_PLATFORM_CAPA_RADIO_ON_IN_SUSPEND = 0,
 	WIL_PLATFORM_CAPA_T_PWR_ON_0 = 1,
@@ -45,6 +50,7 @@ struct wil_platform_ops {
 	void (*uninit)(void *handle);
 	int (*notify)(void *handle, enum wil_platform_event evt);
 	int (*get_capa)(void *handle);
+	void (*set_features)(void *handle, int features);
 };
 
 /**
diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h
index c7b84d1..d3e75f0 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.h
+++ b/drivers/net/wireless/ath/wil6210/wmi.h
@@ -72,6 +72,7 @@ enum wmi_fw_capability {
 	WMI_FW_CAPABILITY_SET_SILENT_RSSI_TABLE		= 13,
 	WMI_FW_CAPABILITY_LO_POWER_CALIB_FROM_OTP	= 14,
 	WMI_FW_CAPABILITY_PNO				= 15,
+	WMI_FW_CAPABILITY_REF_CLOCK_CONTROL		= 18,
 	WMI_FW_CAPABILITY_MAX,
 };
 
-- 
1.9.1

^ permalink raw reply related

* [PATCH v2 3/9] wil6210: add platform capabilities bitmap
From: Maya Erez @ 2017-12-14 16:53 UTC (permalink / raw)
  To: Kalle Valo; +Cc: Maya Erez, linux-wireless, wil6210
In-Reply-To: <1513270393-919-1-git-send-email-qca_merez@qca.qualcomm.com>

Add get_capa callback to platform ops to allow reading the platform
capabilities.
Supported capabilities:
- Keeping 11ad connection during suspend
- T_POWER_ON 0 support
- Usage of external clock

Signed-off-by: Maya Erez <qca_merez@qca.qualcomm.com>
---
 drivers/net/wireless/ath/wil6210/main.c         | 15 ++++++++++++---
 drivers/net/wireless/ath/wil6210/pcie_bus.c     | 10 ++++++++++
 drivers/net/wireless/ath/wil6210/wil6210.h      |  5 +++++
 drivers/net/wireless/ath/wil6210/wil_platform.h |  9 ++++++++-
 4 files changed, 35 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 5d69796..bafd8d5 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -773,9 +773,8 @@ void wil_refresh_fw_capabilities(struct wil6210_priv *wil)
 	struct wiphy *wiphy = wil_to_wiphy(wil);
 
 	wil->keep_radio_on_during_sleep =
-		wil->platform_ops.keep_radio_on_during_sleep &&
-		wil->platform_ops.keep_radio_on_during_sleep(
-			wil->platform_handle) &&
+		test_bit(WIL_PLATFORM_CAPA_RADIO_ON_IN_SUSPEND,
+			 wil->platform_capa) &&
 		test_bit(WMI_FW_CAPABILITY_D3_SUSPEND, wil->fw_capabilities);
 
 	wil_info(wil, "keep_radio_on_during_sleep (%d)\n",
@@ -1008,6 +1007,16 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
 	if (wil->hw_version == HW_VER_UNKNOWN)
 		return -ENODEV;
 
+	if (test_bit(WIL_PLATFORM_CAPA_T_PWR_ON_0, wil->platform_capa)) {
+		wil_dbg_misc(wil, "Notify FW to set T_POWER_ON=0\n");
+		wil_s(wil, RGF_USER_USAGE_8, BIT_USER_SUPPORT_T_POWER_ON_0);
+	}
+
+	if (test_bit(WIL_PLATFORM_CAPA_EXT_CLK, wil->platform_capa)) {
+		wil_dbg_misc(wil, "Notify FW on ext clock configuration\n");
+		wil_s(wil, RGF_USER_USAGE_8, BIT_USER_EXT_CLK);
+	}
+
 	if (wil->platform_ops.notify) {
 		rc = wil->platform_ops.notify(wil->platform_handle,
 					      WIL_PLATFORM_EVT_PRE_RESET);
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index d4bb0bd..dc84001 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -43,9 +43,11 @@ void wil_set_capabilities(struct wil6210_priv *wil)
 	u32 jtag_id = wil_r(wil, RGF_USER_JTAG_DEV_ID);
 	u8 chip_revision = (wil_r(wil, RGF_USER_REVISION_ID) &
 			    RGF_USER_REVISION_ID_MASK);
+	int platform_capa;
 
 	bitmap_zero(wil->hw_capabilities, hw_capability_last);
 	bitmap_zero(wil->fw_capabilities, WMI_FW_CAPABILITY_MAX);
+	bitmap_zero(wil->platform_capa, WIL_PLATFORM_CAPA_MAX);
 	wil->wil_fw_name = ftm_mode ? WIL_FW_NAME_FTM_DEFAULT :
 			   WIL_FW_NAME_DEFAULT;
 	wil->chip_revision = chip_revision;
@@ -81,6 +83,14 @@ void wil_set_capabilities(struct wil6210_priv *wil)
 
 	wil_info(wil, "Board hardware is %s\n", wil->hw_name);
 
+	/* Get platform capabilities */
+	if (wil->platform_ops.get_capa) {
+		platform_capa =
+			wil->platform_ops.get_capa(wil->platform_handle);
+		memcpy(wil->platform_capa, &platform_capa,
+		       min(sizeof(wil->platform_capa), sizeof(platform_capa)));
+	}
+
 	/* extract FW capabilities from file without loading the FW */
 	wil_request_firmware(wil, wil->wil_fw_name, false);
 	wil_refresh_fw_capabilities(wil);
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index db68209..f2bb55e 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -161,6 +161,10 @@ struct RGF_ICR {
 #define RGF_USER_USAGE_6		(0x880018)
 	#define BIT_USER_OOB_MODE		BIT(31)
 	#define BIT_USER_OOB_R2_MODE		BIT(30)
+#define RGF_USER_USAGE_8		(0x880020)
+	#define BIT_USER_PREVENT_DEEP_SLEEP	BIT(0)
+	#define BIT_USER_SUPPORT_T_POWER_ON_0	BIT(1)
+	#define BIT_USER_EXT_CLK		BIT(2)
 #define RGF_USER_HW_MACHINE_STATE	(0x8801dc)
 	#define HW_MACHINE_BOOT_DONE	(0x3fffffd)
 #define RGF_USER_USER_CPU_0		(0x8801e0)
@@ -643,6 +647,7 @@ struct wil6210_priv {
 	const char *wil_fw_name;
 	DECLARE_BITMAP(hw_capabilities, hw_capability_last);
 	DECLARE_BITMAP(fw_capabilities, WMI_FW_CAPABILITY_MAX);
+	DECLARE_BITMAP(platform_capa, WIL_PLATFORM_CAPA_MAX);
 	u8 n_mids; /* number of additional MIDs as reported by FW */
 	u32 recovery_count; /* num of FW recovery attempts in a short time */
 	u32 recovery_state; /* FW recovery state machine */
diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.h b/drivers/net/wireless/ath/wil6210/wil_platform.h
index 5d9e4bf..5cfb946 100644
--- a/drivers/net/wireless/ath/wil6210/wil_platform.h
+++ b/drivers/net/wireless/ath/wil6210/wil_platform.h
@@ -27,6 +27,13 @@ enum wil_platform_event {
 	WIL_PLATFORM_EVT_POST_SUSPEND = 4,
 };
 
+enum wil_platform_capa {
+	WIL_PLATFORM_CAPA_RADIO_ON_IN_SUSPEND = 0,
+	WIL_PLATFORM_CAPA_T_PWR_ON_0 = 1,
+	WIL_PLATFORM_CAPA_EXT_CLK = 2,
+	WIL_PLATFORM_CAPA_MAX,
+};
+
 /**
  * struct wil_platform_ops - wil platform module calls from this
  * driver to platform driver
@@ -37,7 +44,7 @@ struct wil_platform_ops {
 	int (*resume)(void *handle, bool device_powered_on);
 	void (*uninit)(void *handle);
 	int (*notify)(void *handle, enum wil_platform_event evt);
-	bool (*keep_radio_on_during_sleep)(void *handle);
+	int (*get_capa)(void *handle);
 };
 
 /**
-- 
1.9.1

^ permalink raw reply related

* [PATCH v2 2/9] wil6210: support 40bit DMA addresses
From: Maya Erez @ 2017-12-14 16:53 UTC (permalink / raw)
  To: Kalle Valo; +Cc: Lazar Alexei, linux-wireless, wil6210, Maya Erez
In-Reply-To: <1513270393-919-1-git-send-email-qca_merez@qca.qualcomm.com>

From: Lazar Alexei <qca_ailizaro@qca.qualcomm.com>

Add the option to support 40bit addresses since some platforms
may not support 48bits but support 40bits

Signed-off-by: Lazar Alexei <qca_ailizaro@qca.qualcomm.com>
Signed-off-by: Maya Erez <qca_merez@qca.qualcomm.com>
---
 drivers/net/wireless/ath/wil6210/pcie_bus.c | 26 +++++++++++++++-----------
 drivers/net/wireless/ath/wil6210/pmc.c      | 11 ++++++-----
 drivers/net/wireless/ath/wil6210/txrx.c     | 11 ++++++-----
 drivers/net/wireless/ath/wil6210/wil6210.h  |  2 +-
 4 files changed, 28 insertions(+), 22 deletions(-)

diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index 42a5480..d4bb0bd 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -206,6 +206,8 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 		.fw_recovery = wil_platform_rop_fw_recovery,
 	};
 	u32 bar_size = pci_resource_len(pdev, 0);
+	int dma_addr_size[] = {48, 40, 32}; /* keep descending order */
+	int i;
 
 	/* check HW */
 	dev_info(&pdev->dev, WIL_NAME
@@ -241,21 +243,23 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	}
 	/* rollback to err_plat */
 
-	/* device supports 48bit addresses */
-	rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));
-	if (rc) {
-		dev_err(dev, "dma_set_mask_and_coherent(48) failed: %d\n", rc);
-		rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+	/* device supports >32bit addresses */
+	for (i = 0; i < ARRAY_SIZE(dma_addr_size); i++) {
+		rc = dma_set_mask_and_coherent(dev,
+					       DMA_BIT_MASK(dma_addr_size[i]));
 		if (rc) {
-			dev_err(dev,
-				"dma_set_mask_and_coherent(32) failed: %d\n",
-				rc);
-			goto err_plat;
+			dev_err(dev, "dma_set_mask_and_coherent(%d) failed: %d\n",
+				dma_addr_size[i], rc);
+			continue;
 		}
-	} else {
-		wil->use_extended_dma_addr = 1;
+		dev_info(dev, "using dma mask %d", dma_addr_size[i]);
+		wil->dma_addr_size = dma_addr_size[i];
+		break;
 	}
 
+	if (wil->dma_addr_size == 0)
+		goto err_plat;
+
 	rc = pci_enable_device(pdev);
 	if (rc && pdev->msi_enabled == 0) {
 		wil_err(wil,
diff --git a/drivers/net/wireless/ath/wil6210/pmc.c b/drivers/net/wireless/ath/wil6210/pmc.c
index 2e301b6..4ea27b0 100644
--- a/drivers/net/wireless/ath/wil6210/pmc.c
+++ b/drivers/net/wireless/ath/wil6210/pmc.c
@@ -111,14 +111,14 @@ void wil_pmc_alloc(struct wil6210_priv *wil,
 	 *
 	 * HW has limitation that all vrings addresses must share the same
 	 * upper 16 msb bits part of 48 bits address. To workaround that,
-	 * if we are using 48 bit addresses switch to 32 bit allocation
-	 * before allocating vring memory.
+	 * if we are using more than 32 bit addresses switch to 32 bit
+	 * allocation before allocating vring memory.
 	 *
 	 * There's no check for the return value of dma_set_mask_and_coherent,
 	 * since we assume if we were able to set the mask during
 	 * initialization in this system it will not fail if we set it again
 	 */
-	if (wil->use_extended_dma_addr)
+	if (wil->dma_addr_size > 32)
 		dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
 
 	pmc->pring_va = dma_alloc_coherent(dev,
@@ -126,8 +126,9 @@ void wil_pmc_alloc(struct wil6210_priv *wil,
 			&pmc->pring_pa,
 			GFP_KERNEL);
 
-	if (wil->use_extended_dma_addr)
-		dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));
+	if (wil->dma_addr_size > 32)
+		dma_set_mask_and_coherent(dev,
+					  DMA_BIT_MASK(wil->dma_addr_size));
 
 	wil_dbg_misc(wil,
 		     "pmc_alloc: allocated pring %p => %pad. %zd x %d = total %zd bytes\n",
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 389c718..62c04f0 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -178,14 +178,14 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
 	 *
 	 * HW has limitation that all vrings addresses must share the same
 	 * upper 16 msb bits part of 48 bits address. To workaround that,
-	 * if we are using 48 bit addresses switch to 32 bit allocation
-	 * before allocating vring memory.
+	 * if we are using more than 32 bit addresses switch to 32 bit
+	 * allocation before allocating vring memory.
 	 *
 	 * There's no check for the return value of dma_set_mask_and_coherent,
 	 * since we assume if we were able to set the mask during
 	 * initialization in this system it will not fail if we set it again
 	 */
-	if (wil->use_extended_dma_addr)
+	if (wil->dma_addr_size > 32)
 		dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
 
 	vring->va = dma_alloc_coherent(dev, sz, &vring->pa, GFP_KERNEL);
@@ -195,8 +195,9 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring)
 		return -ENOMEM;
 	}
 
-	if (wil->use_extended_dma_addr)
-		dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));
+	if (wil->dma_addr_size > 32)
+		dma_set_mask_and_coherent(dev,
+					  DMA_BIT_MASK(wil->dma_addr_size));
 
 	/* initially, all descriptors are SW owned
 	 * For Tx and Rx, ownership bit is at the same location, thus
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index aa85e9c..db68209 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -704,7 +704,7 @@ struct wil6210_priv {
 	struct wil_sta_info sta[WIL6210_MAX_CID];
 	int bcast_vring;
 	u32 vring_idle_trsh; /* HW fetches up to 16 descriptors at once  */
-	bool use_extended_dma_addr; /* indicates whether we are using 48 bits */
+	u32 dma_addr_size; /* indicates dma addr size */
 	/* scan */
 	struct cfg80211_scan_request *scan_request;
 
-- 
1.9.1

^ permalink raw reply related

* [PATCH v2 1/9] wil6210: support Scheduled scan
From: Maya Erez @ 2017-12-14 16:53 UTC (permalink / raw)
  To: Kalle Valo; +Cc: Dedy Lansky, linux-wireless, wil6210, Maya Erez
In-Reply-To: <1513270393-919-1-git-send-email-qca_merez@qca.qualcomm.com>

From: Dedy Lansky <qca_dlansky@qca.qualcomm.com>

Add support for sched_scan_start/stop by sending PNO commands to FW.
Driver reports max_sched_scan_reqs and invokes
cfg80211_sched_scan_results upon receiving WMI_SCHED_SCAN_RESULT_EVENTID
from FW.

Signed-off-by: Dedy Lansky <qca_dlansky@qca.qualcomm.com>
Signed-off-by: Maya Erez <qca_merez@qca.qualcomm.com>
---
 drivers/net/wireless/ath/wil6210/cfg80211.c |  65 ++++++++
 drivers/net/wireless/ath/wil6210/main.c     |   8 +
 drivers/net/wireless/ath/wil6210/wil6210.h  |   4 +
 drivers/net/wireless/ath/wil6210/wmi.c      | 237 ++++++++++++++++++++++++++++
 drivers/net/wireless/ath/wil6210/wmi.h      |  99 +++++++++---
 5 files changed, 395 insertions(+), 18 deletions(-)

diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 771a534..39509d0 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -1751,6 +1751,69 @@ static int wil_cfg80211_resume(struct wiphy *wiphy)
 	return 0;
 }
 
+static int
+wil_cfg80211_sched_scan_start(struct wiphy *wiphy,
+			      struct net_device *dev,
+			      struct cfg80211_sched_scan_request *request)
+{
+	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+	int i, rc;
+
+	wil_dbg_misc(wil,
+		     "sched scan start: n_ssids %d, ie_len %zu, flags 0x%x\n",
+		     request->n_ssids, request->ie_len, request->flags);
+	for (i = 0; i < request->n_ssids; i++) {
+		wil_dbg_misc(wil, "SSID[%d]:", i);
+		wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
+				  request->ssids[i].ssid,
+				  request->ssids[i].ssid_len, true);
+	}
+	wil_dbg_misc(wil, "channels:");
+	for (i = 0; i < request->n_channels; i++)
+		wil_dbg_misc(wil, " %d%s", request->channels[i]->hw_value,
+			     i == request->n_channels - 1 ? "\n" : "");
+	wil_dbg_misc(wil, "n_match_sets %d, min_rssi_thold %d, delay %d\n",
+		     request->n_match_sets, request->min_rssi_thold,
+		     request->delay);
+	for (i = 0; i < request->n_match_sets; i++) {
+		struct cfg80211_match_set *ms = &request->match_sets[i];
+
+		wil_dbg_misc(wil, "MATCHSET[%d]: rssi_thold %d\n",
+			     i, ms->rssi_thold);
+		wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1,
+				  ms->ssid.ssid,
+				  ms->ssid.ssid_len, true);
+	}
+	wil_dbg_misc(wil, "n_scan_plans %d\n", request->n_scan_plans);
+	for (i = 0; i < request->n_scan_plans; i++) {
+		struct cfg80211_sched_scan_plan *sp = &request->scan_plans[i];
+
+		wil_dbg_misc(wil, "SCAN PLAN[%d]: interval %d iterations %d\n",
+			     i, sp->interval, sp->iterations);
+	}
+
+	rc = wmi_set_ie(wil, WMI_FRAME_PROBE_REQ, request->ie_len, request->ie);
+	if (rc)
+		return rc;
+	return wmi_start_sched_scan(wil, request);
+}
+
+static int
+wil_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev,
+			     u64 reqid)
+{
+	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+	int rc;
+
+	rc = wmi_stop_sched_scan(wil);
+	/* device would return error if it thinks PNO is already stopped.
+	 * ignore the return code so user space and driver gets back in-sync
+	 */
+	wil_dbg_misc(wil, "sched scan stopped (%d)\n", rc);
+
+	return 0;
+}
+
 static const struct cfg80211_ops wil_cfg80211_ops = {
 	.add_virtual_intf = wil_cfg80211_add_iface,
 	.del_virtual_intf = wil_cfg80211_del_iface,
@@ -1784,6 +1847,8 @@ static int wil_cfg80211_resume(struct wiphy *wiphy)
 	.set_power_mgmt = wil_cfg80211_set_power_mgmt,
 	.suspend = wil_cfg80211_suspend,
 	.resume = wil_cfg80211_resume,
+	.sched_scan_start = wil_cfg80211_sched_scan_start,
+	.sched_scan_stop = wil_cfg80211_sched_scan_stop,
 };
 
 static void wil_wiphy_init(struct wiphy *wiphy)
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 1b53cd3..5d69796 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -785,6 +785,14 @@ void wil_refresh_fw_capabilities(struct wil6210_priv *wil)
 		wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
 	else
 		wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
+
+	if (test_bit(WMI_FW_CAPABILITY_PNO, wil->fw_capabilities)) {
+		wiphy->max_sched_scan_reqs = 1;
+		wiphy->max_sched_scan_ssids = WMI_MAX_PNO_SSID_NUM;
+		wiphy->max_match_sets = WMI_MAX_PNO_SSID_NUM;
+		wiphy->max_sched_scan_ie_len = WMI_MAX_IE_LEN;
+		wiphy->max_sched_scan_plans = WMI_MAX_PLANS_NUM;
+	}
 }
 
 void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r)
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index cf27d97..aa85e9c 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -1032,4 +1032,8 @@ int wil_request_firmware(struct wil6210_priv *wil, const char *name,
 void wil6210_set_halp(struct wil6210_priv *wil);
 void wil6210_clear_halp(struct wil6210_priv *wil);
 
+int wmi_start_sched_scan(struct wil6210_priv *wil,
+			 struct cfg80211_sched_scan_request *request);
+int wmi_stop_sched_scan(struct wil6210_priv *wil);
+
 #endif /* __WIL6210_H__ */
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 8ace618..d06090d 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -38,6 +38,7 @@
 		 " 60G device led enablement. Set the led ID (0-2) to enable");
 
 #define WIL_WAIT_FOR_SUSPEND_RESUME_COMP 200
+#define WIL_WMI_CALL_GENERAL_TO_MS 100
 
 /**
  * WMI event receiving - theory of operations
@@ -314,6 +315,10 @@ static const char *cmdid2name(u16 cmdid)
 		return "WMI_LINK_MAINTAIN_CFG_WRITE_CMD";
 	case WMI_LO_POWER_CALIB_FROM_OTP_CMDID:
 		return "WMI_LO_POWER_CALIB_FROM_OTP_CMD";
+	case WMI_START_SCHED_SCAN_CMDID:
+		return "WMI_START_SCHED_SCAN_CMD";
+	case WMI_STOP_SCHED_SCAN_CMDID:
+		return "WMI_STOP_SCHED_SCAN_CMD";
 	default:
 		return "Untracked CMD";
 	}
@@ -428,6 +433,12 @@ static const char *eventid2name(u16 eventid)
 		return "WMI_LINK_MAINTAIN_CFG_WRITE_DONE_EVENT";
 	case WMI_LO_POWER_CALIB_FROM_OTP_EVENTID:
 		return "WMI_LO_POWER_CALIB_FROM_OTP_EVENT";
+	case WMI_START_SCHED_SCAN_EVENTID:
+		return "WMI_START_SCHED_SCAN_EVENT";
+	case WMI_STOP_SCHED_SCAN_EVENTID:
+		return "WMI_STOP_SCHED_SCAN_EVENT";
+	case WMI_SCHED_SCAN_RESULT_EVENTID:
+		return "WMI_SCHED_SCAN_RESULT_EVENT";
 	default:
 		return "Untracked EVENT";
 	}
@@ -1066,6 +1077,75 @@ static void wmi_evt_delba(struct wil6210_priv *wil, int id, void *d, int len)
 	spin_unlock_bh(&sta->tid_rx_lock);
 }
 
+static void
+wmi_evt_sched_scan_result(struct wil6210_priv *wil, int id, void *d, int len)
+{
+	struct wmi_sched_scan_result_event *data = d;
+	struct wiphy *wiphy = wil_to_wiphy(wil);
+	struct ieee80211_mgmt *rx_mgmt_frame =
+		(struct ieee80211_mgmt *)data->payload;
+	int flen = len - offsetof(struct wmi_sched_scan_result_event, payload);
+	int ch_no;
+	u32 freq;
+	struct ieee80211_channel *channel;
+	s32 signal;
+	__le16 fc;
+	u32 d_len;
+	struct cfg80211_bss *bss;
+
+	if (flen < 0) {
+		wil_err(wil, "sched scan result event too short, len %d\n",
+			len);
+		return;
+	}
+
+	d_len = le32_to_cpu(data->info.len);
+	if (d_len != flen) {
+		wil_err(wil,
+			"sched scan result length mismatch, d_len %d should be %d\n",
+			d_len, flen);
+		return;
+	}
+
+	fc = rx_mgmt_frame->frame_control;
+	if (!ieee80211_is_probe_resp(fc)) {
+		wil_err(wil, "sched scan result invalid frame, fc 0x%04x\n",
+			fc);
+		return;
+	}
+
+	ch_no = data->info.channel + 1;
+	freq = ieee80211_channel_to_frequency(ch_no, NL80211_BAND_60GHZ);
+	channel = ieee80211_get_channel(wiphy, freq);
+	if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING, wil->fw_capabilities))
+		signal = 100 * data->info.rssi;
+	else
+		signal = data->info.sqi;
+
+	wil_dbg_wmi(wil, "sched scan result: channel %d MCS %d RSSI %d\n",
+		    data->info.channel, data->info.mcs, data->info.rssi);
+	wil_dbg_wmi(wil, "len %d qid %d mid %d cid %d\n",
+		    d_len, data->info.qid, data->info.mid, data->info.cid);
+	wil_hex_dump_wmi("PROBE ", DUMP_PREFIX_OFFSET, 16, 1, rx_mgmt_frame,
+			 d_len, true);
+
+	if (!channel) {
+		wil_err(wil, "Frame on unsupported channel\n");
+		return;
+	}
+
+	bss = cfg80211_inform_bss_frame(wiphy, channel, rx_mgmt_frame,
+					d_len, signal, GFP_KERNEL);
+	if (bss) {
+		wil_dbg_wmi(wil, "Added BSS %pM\n", rx_mgmt_frame->bssid);
+		cfg80211_put_bss(wiphy, bss);
+	} else {
+		wil_err(wil, "cfg80211_inform_bss_frame() failed\n");
+	}
+
+	cfg80211_sched_scan_results(wiphy, 0);
+}
+
 /**
  * Some events are ignored for purpose; and need not be interpreted as
  * "unhandled events"
@@ -1093,6 +1173,7 @@ static void wmi_evt_ignore(struct wil6210_priv *wil, int id, void *d, int len)
 	{WMI_DELBA_EVENTID,		wmi_evt_delba},
 	{WMI_VRING_EN_EVENTID,		wmi_evt_vring_en},
 	{WMI_DATA_PORT_OPEN_EVENTID,		wmi_evt_ignore},
+	{WMI_SCHED_SCAN_RESULT_EVENTID,		wmi_evt_sched_scan_result},
 };
 
 /*
@@ -2284,3 +2365,159 @@ bool wil_is_wmi_idle(struct wil6210_priv *wil)
 	spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
 	return rc;
 }
+
+static void
+wmi_sched_scan_set_ssids(struct wil6210_priv *wil,
+			 struct wmi_start_sched_scan_cmd *cmd,
+			 struct cfg80211_ssid *ssids, int n_ssids,
+			 struct cfg80211_match_set *match_sets,
+			 int n_match_sets)
+{
+	int i;
+
+	if (n_match_sets > WMI_MAX_PNO_SSID_NUM) {
+		wil_dbg_wmi(wil, "too many match sets (%d), use first %d\n",
+			    n_match_sets, WMI_MAX_PNO_SSID_NUM);
+		n_match_sets = WMI_MAX_PNO_SSID_NUM;
+	}
+	cmd->num_of_ssids = n_match_sets;
+
+	for (i = 0; i < n_match_sets; i++) {
+		struct wmi_sched_scan_ssid_match *wmi_match =
+			&cmd->ssid_for_match[i];
+		struct cfg80211_match_set *cfg_match = &match_sets[i];
+		int j;
+
+		wmi_match->ssid_len = cfg_match->ssid.ssid_len;
+		memcpy(wmi_match->ssid, cfg_match->ssid.ssid,
+		       min_t(u8, wmi_match->ssid_len, WMI_MAX_SSID_LEN));
+		wmi_match->rssi_threshold = S8_MIN;
+		if (cfg_match->rssi_thold >= S8_MIN &&
+		    cfg_match->rssi_thold <= S8_MAX)
+			wmi_match->rssi_threshold = cfg_match->rssi_thold;
+
+		for (j = 0; j < n_ssids; j++)
+			if (wmi_match->ssid_len == ssids[j].ssid_len &&
+			    memcmp(wmi_match->ssid, ssids[j].ssid,
+				   wmi_match->ssid_len) == 0)
+				wmi_match->add_ssid_to_probe = true;
+	}
+}
+
+static void
+wmi_sched_scan_set_channels(struct wil6210_priv *wil,
+			    struct wmi_start_sched_scan_cmd *cmd,
+			    u32 n_channels,
+			    struct ieee80211_channel **channels)
+{
+	int i;
+
+	if (n_channels > WMI_MAX_CHANNEL_NUM) {
+		wil_dbg_wmi(wil, "too many channels (%d), use first %d\n",
+			    n_channels, WMI_MAX_CHANNEL_NUM);
+		n_channels = WMI_MAX_CHANNEL_NUM;
+	}
+	cmd->num_of_channels = n_channels;
+
+	for (i = 0; i < n_channels; i++) {
+		struct ieee80211_channel *cfg_chan = channels[i];
+
+		cmd->channel_list[i] = cfg_chan->hw_value - 1;
+	}
+}
+
+static void
+wmi_sched_scan_set_plans(struct wil6210_priv *wil,
+			 struct wmi_start_sched_scan_cmd *cmd,
+			 struct cfg80211_sched_scan_plan *scan_plans,
+			 int n_scan_plans)
+{
+	int i;
+
+	if (n_scan_plans > WMI_MAX_PLANS_NUM) {
+		wil_dbg_wmi(wil, "too many plans (%d), use first %d\n",
+			    n_scan_plans, WMI_MAX_PLANS_NUM);
+		n_scan_plans = WMI_MAX_PLANS_NUM;
+	}
+
+	for (i = 0; i < n_scan_plans; i++) {
+		struct cfg80211_sched_scan_plan *cfg_plan = &scan_plans[i];
+
+		cmd->scan_plans[i].interval_sec =
+			cpu_to_le16(cfg_plan->interval);
+		cmd->scan_plans[i].num_of_iterations =
+			cpu_to_le16(cfg_plan->iterations);
+	}
+}
+
+int wmi_start_sched_scan(struct wil6210_priv *wil,
+			 struct cfg80211_sched_scan_request *request)
+{
+	int rc;
+	struct wmi_start_sched_scan_cmd cmd = {
+		.min_rssi_threshold = S8_MIN,
+		.initial_delay_sec = cpu_to_le16(request->delay),
+	};
+	struct {
+		struct wmi_cmd_hdr wmi;
+		struct wmi_start_sched_scan_event evt;
+	} __packed reply;
+
+	if (!test_bit(WMI_FW_CAPABILITY_PNO, wil->fw_capabilities))
+		return -ENOTSUPP;
+
+	if (request->min_rssi_thold >= S8_MIN &&
+	    request->min_rssi_thold <= S8_MAX)
+		cmd.min_rssi_threshold = request->min_rssi_thold;
+
+	wmi_sched_scan_set_ssids(wil, &cmd, request->ssids, request->n_ssids,
+				 request->match_sets, request->n_match_sets);
+	wmi_sched_scan_set_channels(wil, &cmd,
+				    request->n_channels, request->channels);
+	wmi_sched_scan_set_plans(wil, &cmd,
+				 request->scan_plans, request->n_scan_plans);
+
+	reply.evt.result = WMI_PNO_REJECT;
+
+	rc = wmi_call(wil, WMI_START_SCHED_SCAN_CMDID, &cmd, sizeof(cmd),
+		      WMI_START_SCHED_SCAN_EVENTID, &reply, sizeof(reply),
+		      WIL_WMI_CALL_GENERAL_TO_MS);
+	if (rc)
+		return rc;
+
+	if (reply.evt.result != WMI_PNO_SUCCESS) {
+		wil_err(wil, "start sched scan failed, result %d\n",
+			reply.evt.result);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int wmi_stop_sched_scan(struct wil6210_priv *wil)
+{
+	int rc;
+	struct {
+		struct wmi_cmd_hdr wmi;
+		struct wmi_stop_sched_scan_event evt;
+	} __packed reply;
+
+	if (!test_bit(WMI_FW_CAPABILITY_PNO, wil->fw_capabilities))
+		return -ENOTSUPP;
+
+	reply.evt.result = WMI_PNO_REJECT;
+
+	rc = wmi_call(wil, WMI_STOP_SCHED_SCAN_CMDID, NULL, 0,
+		      WMI_STOP_SCHED_SCAN_EVENTID, &reply, sizeof(reply),
+		      WIL_WMI_CALL_GENERAL_TO_MS);
+	if (rc)
+		return rc;
+
+	if (reply.evt.result != WMI_PNO_SUCCESS) {
+		wil_err(wil, "stop sched scan failed, result %d\n",
+			reply.evt.result);
+		return -EINVAL;
+	}
+
+	return 0;
+}
diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h
index d9e220a..c7b84d1 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.h
+++ b/drivers/net/wireless/ath/wil6210/wmi.h
@@ -71,6 +71,7 @@ enum wmi_fw_capability {
 	WMI_FW_CAPABILITY_RSSI_REPORTING		= 12,
 	WMI_FW_CAPABILITY_SET_SILENT_RSSI_TABLE		= 13,
 	WMI_FW_CAPABILITY_LO_POWER_CALIB_FROM_OTP	= 14,
+	WMI_FW_CAPABILITY_PNO				= 15,
 	WMI_FW_CAPABILITY_MAX,
 };
 
@@ -87,6 +88,8 @@ enum wmi_command_id {
 	WMI_CONNECT_CMDID				= 0x01,
 	WMI_DISCONNECT_CMDID				= 0x03,
 	WMI_DISCONNECT_STA_CMDID			= 0x04,
+	WMI_START_SCHED_SCAN_CMDID			= 0x05,
+	WMI_STOP_SCHED_SCAN_CMDID			= 0x06,
 	WMI_START_SCAN_CMDID				= 0x07,
 	WMI_SET_BSS_FILTER_CMDID			= 0x09,
 	WMI_SET_PROBED_SSID_CMDID			= 0x0A,
@@ -385,6 +388,38 @@ struct wmi_start_scan_cmd {
 	} channel_list[0];
 } __packed;
 
+#define WMI_MAX_PNO_SSID_NUM	(16)
+#define WMI_MAX_CHANNEL_NUM	(6)
+#define WMI_MAX_PLANS_NUM	(2)
+
+/* WMI_START_SCHED_SCAN_CMDID */
+struct wmi_sched_scan_ssid_match {
+	u8 ssid_len;
+	u8 ssid[WMI_MAX_SSID_LEN];
+	s8 rssi_threshold;
+	/* boolean */
+	u8 add_ssid_to_probe;
+	u8 reserved;
+} __packed;
+
+/* WMI_START_SCHED_SCAN_CMDID */
+struct wmi_sched_scan_plan {
+	__le16 interval_sec;
+	__le16 num_of_iterations;
+} __packed;
+
+/* WMI_START_SCHED_SCAN_CMDID */
+struct wmi_start_sched_scan_cmd {
+	struct wmi_sched_scan_ssid_match ssid_for_match[WMI_MAX_PNO_SSID_NUM];
+	u8 num_of_ssids;
+	s8 min_rssi_threshold;
+	u8 channel_list[WMI_MAX_CHANNEL_NUM];
+	u8 num_of_channels;
+	u8 reserved;
+	__le16 initial_delay_sec;
+	struct wmi_sched_scan_plan scan_plans[WMI_MAX_PLANS_NUM];
+} __packed;
+
 /* WMI_SET_PROBED_SSID_CMDID */
 #define MAX_PROBED_SSID_INDEX	(3)
 
@@ -1238,6 +1273,9 @@ enum wmi_event_id {
 	WMI_READY_EVENTID				= 0x1001,
 	WMI_CONNECT_EVENTID				= 0x1002,
 	WMI_DISCONNECT_EVENTID				= 0x1003,
+	WMI_START_SCHED_SCAN_EVENTID			= 0x1005,
+	WMI_STOP_SCHED_SCAN_EVENTID			= 0x1006,
+	WMI_SCHED_SCAN_RESULT_EVENTID			= 0x1007,
 	WMI_SCAN_COMPLETE_EVENTID			= 0x100A,
 	WMI_REPORT_STATISTICS_EVENTID			= 0x100B,
 	WMI_RD_MEM_RSP_EVENTID				= 0x1800,
@@ -1600,6 +1638,49 @@ struct wmi_scan_complete_event {
 	__le32 status;
 } __packed;
 
+/* wmi_rx_mgmt_info */
+struct wmi_rx_mgmt_info {
+	u8 mcs;
+	s8 rssi;
+	u8 range;
+	u8 sqi;
+	__le16 stype;
+	__le16 status;
+	__le32 len;
+	/* Not resolved when == 0xFFFFFFFF == > Broadcast to all MIDS */
+	u8 qid;
+	/* Not resolved when == 0xFFFFFFFF == > Broadcast to all MIDS */
+	u8 mid;
+	u8 cid;
+	/* From Radio MNGR */
+	u8 channel;
+} __packed;
+
+/* WMI_START_SCHED_SCAN_EVENTID */
+enum wmi_pno_result {
+	WMI_PNO_SUCCESS			= 0x00,
+	WMI_PNO_REJECT			= 0x01,
+	WMI_PNO_INVALID_PARAMETERS	= 0x02,
+	WMI_PNO_NOT_ENABLED		= 0x03,
+};
+
+struct wmi_start_sched_scan_event {
+	/* pno_result */
+	u8 result;
+	u8 reserved[3];
+} __packed;
+
+struct wmi_stop_sched_scan_event {
+	/* pno_result */
+	u8 result;
+	u8 reserved[3];
+} __packed;
+
+struct wmi_sched_scan_result_event {
+	struct wmi_rx_mgmt_info info;
+	u8 payload[0];
+} __packed;
+
 /* WMI_ACS_PASSIVE_SCAN_COMPLETE_EVENT */
 enum wmi_acs_info_bitmask {
 	WMI_ACS_INFO_BITMASK_BEACON_FOUND	= 0x01,
@@ -1814,24 +1895,6 @@ struct wmi_get_ssid_event {
 	u8 ssid[WMI_MAX_SSID_LEN];
 } __packed;
 
-/* wmi_rx_mgmt_info */
-struct wmi_rx_mgmt_info {
-	u8 mcs;
-	s8 rssi;
-	u8 range;
-	u8 sqi;
-	__le16 stype;
-	__le16 status;
-	__le32 len;
-	/* Not resolved when == 0xFFFFFFFF == > Broadcast to all MIDS */
-	u8 qid;
-	/* Not resolved when == 0xFFFFFFFF == > Broadcast to all MIDS */
-	u8 mid;
-	u8 cid;
-	/* From Radio MNGR */
-	u8 channel;
-} __packed;
-
 /* EVENT: WMI_RF_XPM_READ_RESULT_EVENTID */
 struct wmi_rf_xpm_read_result_event {
 	/* enum wmi_fw_status_e - success=0 or fail=1 */
-- 
1.9.1

^ permalink raw reply related

* [PATCH v2 0/9] wil6210 patches
From: Maya Erez @ 2017-12-14 16:53 UTC (permalink / raw)
  To: Kalle Valo; +Cc: Maya Erez, linux-wireless, wil6210

Changes from v1:
- Fix code review comments on "wil6210: support Scheduled scan"

The following patches include several wil6210 fixes.

Dedy Lansky (2):
  wil6210: support Scheduled scan
  wil6210: remove leftover "FIXME"s

Lazar Alexei (2):
  wil6210: support 40bit DMA addresses
  wil6210: add support for headroom configuration

Lior David (2):
  wil6210: configurable broadcast TX MCS
  wil6210: remove reference to preset_chandef

Maya Erez (3):
  wil6210: add platform capabilities bitmap
  wil6210: set platform features based on FW capabilities
  wil6210: prevent parallel suspend and dump collection

 drivers/net/wireless/ath/wil6210/cfg80211.c       |  68 +++++-
 drivers/net/wireless/ath/wil6210/debugfs.c        |   1 -
 drivers/net/wireless/ath/wil6210/interrupt.c      |   2 +-
 drivers/net/wireless/ath/wil6210/main.c           |  67 +++++-
 drivers/net/wireless/ath/wil6210/netdev.c         |   2 +-
 drivers/net/wireless/ath/wil6210/pcie_bus.c       |  36 +++-
 drivers/net/wireless/ath/wil6210/pm.c             |  17 ++
 drivers/net/wireless/ath/wil6210/pmc.c            |  11 +-
 drivers/net/wireless/ath/wil6210/txrx.c           | 101 ++++++++-
 drivers/net/wireless/ath/wil6210/wil6210.h        |  15 +-
 drivers/net/wireless/ath/wil6210/wil_crash_dump.c |  11 +
 drivers/net/wireless/ath/wil6210/wil_platform.h   |  15 +-
 drivers/net/wireless/ath/wil6210/wmi.c            | 241 +++++++++++++++++++++-
 drivers/net/wireless/ath/wil6210/wmi.h            | 100 +++++++--
 14 files changed, 621 insertions(+), 66 deletions(-)

-- 
1.9.1

^ permalink raw reply

* Re: [PATCH] mt76x2: eeprom: fix typo in mt76x2_get_power_info_5g()
From: Kalle Valo @ 2017-12-14 16:20 UTC (permalink / raw)
  To: Colin Ian King; +Cc: Lorenzo Bianconi, nbd, linux-wireless
In-Reply-To: <68bc67c3-9c9a-e059-2a2a-1e8ccd5b92f5@canonical.com>

Colin Ian King <colin.king@canonical.com> writes:

> On 14/12/17 10:46, Lorenzo Bianconi wrote:
>> Fix typo in 5GHz power vs channel eeprom parsing
>> 
>> Fixes: 7bc04215a66b ("mt76: add driver code for MT76x2e")
>> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
>> ---
>>  drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c | 2 +-
>>  1 file changed, 1 insertion(+), 1 deletion(-)
>> 
>> diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c
>> index 440b7e7d73a9..29dc52ef629d 100644
>> --- a/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c
>> +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c
>> @@ -524,7 +524,7 @@ mt76x2_get_power_info_5g(struct mt76x2_dev *dev, struct mt76x2_tx_power_info *t,
>>  
>>  	if (channel >= 192)
>>  		delta_idx = 4;
>> -	else if (channel >= 484)
>> +	else if (channel >= 184)
>>  		delta_idx = 3;
>>  	else if (channel < 44)
>>  		delta_idx = 3;
>> 
> any possibility of adding:
>
> Reported-by: Colin Ian King <colin.king@canonical.com>

Sure, I'll add that during commit.

-- 
Kalle Valo

^ permalink raw reply

* Re: [PATCH 2/3] rtlwifi: Add beacon check mechanism to check if AP settings changed.
From: Larry Finger @ 2017-12-14 16:06 UTC (permalink / raw)
  To: Johannes Berg
  Cc: Kalle Valo, linux-wireless, Tsang-Shian Lin, Ping-Ke Shih,
	Yan-Hsuan Chuang, Birming Chiu, Shaofu, Steven Ting
In-Reply-To: <87fu8dcwsf.fsf@purkki.adurom.net>

On 12/14/2017 06:35 AM, Kalle Valo wrote:
> Larry Finger <Larry.Finger@lwfinger.net> writes:
> 
>> From: Tsang-Shian Lin <thlin@realtek.com>
>>
>> +bool rtl_check_beacon_key(struct ieee80211_hw *hw, void *data, unsigned int len)
>> +{
>> +	struct rtl_priv *rtlpriv = rtl_priv(hw);
>> +	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
>> +	struct rtl_phy *rtlphy = &rtlpriv->phy;
>> +	struct ieee80211_hdr *hdr = data;
>> +	struct ieee80211_ht_cap *ht_cap_ie;
>> +	struct ieee80211_ht_operation *ht_oper_ie = NULL;
>> +	struct rtl_beacon_keys bcn_key = {0};
>> +	struct rtl_beacon_keys *cur_bcn_key;
>> +	u8 *ht_cap;
>> +	u8 ht_cap_len;
>> +	u8 *ht_oper;
>> +	u8 ht_oper_len;
>> +	u8 *ds_param;
>> +	u8 ds_param_len;
>> +
>> +	if (mac->opmode != NL80211_IFTYPE_STATION)
>> +		return false;
>> +
>> +	/* check if this really is a beacon*/
>> +	if (!ieee80211_is_beacon(hdr->frame_control))
>> +		return false;
>> +
>> +	/* min. beacon length + FCS_LEN */
>> +	if (len <= 40 + FCS_LEN)
>> +		return false;
>> +
>> +	cur_bcn_key = &mac->cur_beacon_keys;
>> +
>> +	if (rtlpriv->mac80211.link_state == MAC80211_NOLINK) {
>> +		if (cur_bcn_key->valid) {
>> +			cur_bcn_key->valid = false;
>> +			RT_TRACE(rtlpriv, COMP_BEACON, DBG_LOUD,
>> +				 "Reset cur_beacon_keys.valid to false!\n");
>> +		}
>> +		return false;
>> +	}
>> +
>> +	/* and only beacons from the associated BSSID, please */
>> +	if (!ether_addr_equal(hdr->addr3, rtlpriv->mac80211.bssid))
>> +		return false;
>> +
>> +	/***** Parsing DS Param IE ******/
>> +	ds_param = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_DS_PARAMS);
>> +
>> +	if (ds_param && !(ds_param[1] < sizeof(*ds_param)))
>> +		ds_param_len = ds_param[1];
>> +	else
>> +		ds_param = NULL;
>> +
>> +	/***** Parsing HT Cap. IE ******/
>> +	ht_cap = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_HT_CAPABILITY);
>> +
>> +	if (ht_cap && !(ht_cap[1] < sizeof(*ht_cap))) {
>> +		ht_cap_len = ht_cap[1];
>> +		ht_cap_ie = (struct ieee80211_ht_cap *)&ht_cap[2];
>> +	} else  {
>> +		ht_cap = NULL;
>> +		ht_cap_ie = NULL;
>> +	}
>> +
>> +	/***** Parsing HT Info. IE ******/
>> +	ht_oper = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_HT_OPERATION);
>> +
>> +	if (ht_oper && !(ht_oper[1] < sizeof(*ht_oper))) {
>> +		ht_oper_len = ht_oper[1];
>> +		ht_oper_ie = (struct ieee80211_ht_operation *)&ht_oper[2];
>> +	} else {
>> +		ht_oper = NULL;
>> +	}
>> +
>> +	/* update bcn_key */
>> +	memset(&bcn_key, 0, sizeof(bcn_key));
>> +
>> +	if (ds_param)
>> +		bcn_key.bcn_channel = ds_param[2];
>> +	else if (ht_oper && ht_oper_ie)
>> +		bcn_key.bcn_channel = ht_oper_ie->primary_chan;
>> +
>> +	if (ht_cap_ie)
>> +		bcn_key.ht_cap_info = ht_cap_ie->cap_info;
>> +
>> +	if (ht_oper && ht_oper_ie)
>> +		bcn_key.ht_info_infos_0_sco = ht_oper_ie->ht_param & 0x03;
>> +
>> +	bcn_key.valid = true;
>> +
>> +	/* update cur_beacon_keys or compare beacon key */
>> +	if (rtlpriv->mac80211.link_state != MAC80211_LINKED &&
>> +	    rtlpriv->mac80211.link_state != MAC80211_LINKED_SCANNING)
>> +		return true;
>> +
>> +	if (!cur_bcn_key->valid) {
>> +		/* update cur_beacon_keys */
>> +		memset(cur_bcn_key, 0, sizeof(bcn_key));
>> +		memcpy(cur_bcn_key, &bcn_key, sizeof(bcn_key));
>> +		cur_bcn_key->valid = true;
>> +
>> +		RT_TRACE(rtlpriv, COMP_BEACON, DBG_LOUD,
>> +			 "Beacon key update!ch=%d, ht_cap_info=0x%x, sco=0x%x\n",
>> +			 cur_bcn_key->bcn_channel,
>> +			 cur_bcn_key->ht_cap_info,
>> +			 cur_bcn_key->ht_info_infos_0_sco);
>> +
>> +		return true;
>> +	}
>> +
>> +	/* compare beacon key */
>> +	if (!memcmp(cur_bcn_key, &bcn_key, sizeof(bcn_key))) {
>> +		/* same beacon key */
>> +		mac->new_beacon_cnt = 0;
>> +		goto chk_exit;
>> +	}
>> +
>> +	if (cur_bcn_key->bcn_channel == bcn_key.bcn_channel &&
>> +	    cur_bcn_key->ht_cap_info == bcn_key.ht_cap_info) {
>> +		/* Beacon HT info IE, secondary channel offset check */
>> +		/* 40M -> 20M */
>> +		if (cur_bcn_key->ht_info_infos_0_sco >
>> +		    bcn_key.ht_info_infos_0_sco) {
>> +			/* Not a new beacon */
>> +			RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
>> +				 "Beacon BW change! sco:0x%x -> 0x%x\n",
>> +				 cur_bcn_key->ht_info_infos_0_sco,
>> +				 bcn_key.ht_info_infos_0_sco);
>> +
>> +			cur_bcn_key->ht_info_infos_0_sco =
>> +					bcn_key.ht_info_infos_0_sco;
>> +		} else {
>> +			/* 20M -> 40M */
>> +			if (rtlphy->max_ht_chan_bw >= HT_CHANNEL_WIDTH_20_40) {
>> +				/* Not a new beacon */
>> +				RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
>> +					 "Beacon BW change! sco:0x%x -> 0x%x\n",
>> +					 cur_bcn_key->ht_info_infos_0_sco,
>> +					 bcn_key.ht_info_infos_0_sco);
>> +
>> +				cur_bcn_key->ht_info_infos_0_sco =
>> +					bcn_key.ht_info_infos_0_sco;
>> +			} else {
>> +				mac->new_beacon_cnt++;
>> +			}
>> +		}
>> +	} else {
>> +		mac->new_beacon_cnt++;
>> +	}
>> +
>> +	if (mac->new_beacon_cnt == 1) {
>> +		RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
>> +			 "Get new beacon.\n");
>> +		RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
>> +			 "Cur : ch=%d, ht_cap=0x%x, sco=0x%x\n",
>> +			 cur_bcn_key->bcn_channel,
>> +			 cur_bcn_key->ht_cap_info,
>> +			 cur_bcn_key->ht_info_infos_0_sco);
>> +		RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
>> +			 "New RX : ch=%d, ht_cap=0x%x, sco=0x%x\n",
>> +			 bcn_key.bcn_channel,
>> +			 bcn_key.ht_cap_info,
>> +			 bcn_key.ht_info_infos_0_sco);
>> +
>> +	} else if (mac->new_beacon_cnt > 1) {
>> +		RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
>> +			 "new beacon cnt: %d\n",
>> +			 mac->new_beacon_cnt);
>> +	}
>> +
>> +	if (mac->new_beacon_cnt > 3) {
>> +		ieee80211_connection_loss(rtlpriv->mac80211.vif);
>> +		RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG,
>> +			 "new beacon cnt >3, disconnect !\n");
>> +	}
>> +
>> +chk_exit:
>> +
>> +	return true;
>> +}
>> +EXPORT_SYMBOL_GPL(rtl_check_beacon_key);
> 
> Why do all this in the driver? I would expect something like this to be
> done in mac80211, not in the driver.

Johannes,

Does mac80211 have this facility? If so, how would we tap into it? If this 
capability does not exist in mac80211, how would one add it? I have never 
devoted much effort to looking at the internals of mac80211.
> 
>> +struct rtl_beacon_keys {
>> +	/*u8 ssid[32];*/
>> +	/*u32 ssid_len;*/
> 
> Commented out fields, please drop.

Yes, those will be dropped.

Larry

^ permalink raw reply

* Re: [PATCH] mt76x2: eeprom: fix typo in mt76x2_get_power_info_5g()
From: Colin Ian King @ 2017-12-14 15:42 UTC (permalink / raw)
  To: Lorenzo Bianconi, nbd; +Cc: linux-wireless
In-Reply-To: <2ae397aff0b058615a558a2352067fad2c9201e6.1513248071.git.lorenzo.bianconi@redhat.com>

On 14/12/17 10:46, Lorenzo Bianconi wrote:
> Fix typo in 5GHz power vs channel eeprom parsing
> 
> Fixes: 7bc04215a66b ("mt76: add driver code for MT76x2e")
> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
> ---
>  drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c
> index 440b7e7d73a9..29dc52ef629d 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c
> @@ -524,7 +524,7 @@ mt76x2_get_power_info_5g(struct mt76x2_dev *dev, struct mt76x2_tx_power_info *t,
>  
>  	if (channel >= 192)
>  		delta_idx = 4;
> -	else if (channel >= 484)
> +	else if (channel >= 184)
>  		delta_idx = 3;
>  	else if (channel < 44)
>  		delta_idx = 3;
> 
any possibility of adding:

Reported-by: Colin Ian King <colin.king@canonical.com>

^ permalink raw reply

* [PATCH 06/14] mt76x2: add channel argument to eeprom tx power functions
From: Felix Fietkau @ 2017-12-14 15:39 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo
In-Reply-To: <20171214153918.43774-1-nbd@nbd.name>

Preparation for exposing maximum power to mac80211

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c | 30 +++++++++++++---------
 drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h |  6 +++--
 drivers/net/wireless/mediatek/mt76/mt76x2_phy.c    |  7 ++---
 3 files changed, 26 insertions(+), 17 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c
index 29dc52ef629d..8fef400cb58e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c
@@ -425,12 +425,13 @@ mt76x2_rate_power_val(u8 val)
 	return mt76x2_sign_extend_optional(val, 7);
 }
 
-void mt76x2_get_rate_power(struct mt76x2_dev *dev, struct mt76_rate_power *t)
+void mt76x2_get_rate_power(struct mt76x2_dev *dev, struct mt76_rate_power *t,
+			   struct ieee80211_channel *chan)
 {
 	bool is_5ghz;
 	u16 val;
 
-	is_5ghz = dev->mt76.chandef.chan->band == NL80211_BAND_5GHZ;
+	is_5ghz = chan->band == NL80211_BAND_5GHZ;
 
 	memset(t, 0, sizeof(*t));
 
@@ -484,9 +485,9 @@ void mt76x2_get_rate_power(struct mt76x2_dev *dev, struct mt76_rate_power *t)
 
 static void
 mt76x2_get_power_info_2g(struct mt76x2_dev *dev, struct mt76x2_tx_power_info *t,
-		       int chain, int offset)
+		         struct ieee80211_channel *chan, int chain, int offset)
 {
-	int channel = dev->mt76.chandef.chan->hw_value;
+	int channel = chan->hw_value;
 	int delta_idx;
 	u8 data[6];
 	u16 val;
@@ -511,9 +512,9 @@ mt76x2_get_power_info_2g(struct mt76x2_dev *dev, struct mt76x2_tx_power_info *t,
 
 static void
 mt76x2_get_power_info_5g(struct mt76x2_dev *dev, struct mt76x2_tx_power_info *t,
-		       int chain, int offset)
+		         struct ieee80211_channel *chan, int chain, int offset)
 {
-	int channel = dev->mt76.chandef.chan->hw_value;
+	int channel = chan->hw_value;
 	enum mt76x2_cal_channel_group group;
 	int delta_idx;
 	u16 val;
@@ -559,7 +560,8 @@ mt76x2_get_power_info_5g(struct mt76x2_dev *dev, struct mt76x2_tx_power_info *t,
 }
 
 void mt76x2_get_power_info(struct mt76x2_dev *dev,
-			   struct mt76x2_tx_power_info *t)
+			   struct mt76x2_tx_power_info *t,
+			   struct ieee80211_channel *chan)
 {
 	u16 bw40, bw80;
 
@@ -568,13 +570,17 @@ void mt76x2_get_power_info(struct mt76x2_dev *dev,
 	bw40 = mt76x2_eeprom_get(dev, MT_EE_TX_POWER_DELTA_BW40);
 	bw80 = mt76x2_eeprom_get(dev, MT_EE_TX_POWER_DELTA_BW80);
 
-	if (dev->mt76.chandef.chan->band == NL80211_BAND_5GHZ) {
+	if (chan->band == NL80211_BAND_5GHZ) {
 		bw40 >>= 8;
-		mt76x2_get_power_info_5g(dev, t, 0, MT_EE_TX_POWER_0_START_5G);
-		mt76x2_get_power_info_5g(dev, t, 1, MT_EE_TX_POWER_1_START_5G);
+		mt76x2_get_power_info_5g(dev, t, chan, 0,
+					 MT_EE_TX_POWER_0_START_5G);
+		mt76x2_get_power_info_5g(dev, t, chan, 1,
+					 MT_EE_TX_POWER_1_START_5G);
 	} else {
-		mt76x2_get_power_info_2g(dev, t, 0, MT_EE_TX_POWER_0_START_2G);
-		mt76x2_get_power_info_2g(dev, t, 1, MT_EE_TX_POWER_1_START_2G);
+		mt76x2_get_power_info_2g(dev, t, chan, 0,
+					 MT_EE_TX_POWER_0_START_2G);
+		mt76x2_get_power_info_2g(dev, t, chan, 1,
+					 MT_EE_TX_POWER_1_START_2G);
 	}
 
 	if (mt76x2_tssi_enabled(dev) || !field_valid(t->target_power))
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h b/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h
index 063d6c8451c9..6db2c6e47569 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h
@@ -146,9 +146,11 @@ mt76x2_eeprom_get(struct mt76x2_dev *dev, enum mt76x2_eeprom_field field)
 	return get_unaligned_le16(dev->mt76.eeprom.data + field);
 }
 
-void mt76x2_get_rate_power(struct mt76x2_dev *dev, struct mt76_rate_power *t);
+void mt76x2_get_rate_power(struct mt76x2_dev *dev, struct mt76_rate_power *t,
+			   struct ieee80211_channel *chan);
 void mt76x2_get_power_info(struct mt76x2_dev *dev,
-			   struct mt76x2_tx_power_info *t);
+			   struct mt76x2_tx_power_info *t,
+			   struct ieee80211_channel *chan);
 int mt76x2_get_temp_comp(struct mt76x2_dev *dev, struct mt76x2_temp_comp *t);
 bool mt76x2_ext_pa_enabled(struct mt76x2_dev *dev, enum nl80211_band band);
 void mt76x2_read_rx_gain(struct mt76x2_dev *dev);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
index fe3a4b6a19cc..81cea7e8414e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
@@ -117,11 +117,12 @@ mt76x2_get_max_power(struct mt76_rate_power *r)
 void mt76x2_phy_set_txpower(struct mt76x2_dev *dev)
 {
 	enum nl80211_chan_width width = dev->mt76.chandef.width;
+	struct ieee80211_channel *chan = dev->mt76.chandef.chan;
 	struct mt76x2_tx_power_info txp;
 	int txp_0, txp_1, delta = 0;
 	struct mt76_rate_power t = {};
 
-	mt76x2_get_power_info(dev, &txp);
+	mt76x2_get_power_info(dev, &txp, chan);
 
 	if (width == NL80211_CHAN_WIDTH_40)
 		delta = txp.delta_bw40;
@@ -131,7 +132,7 @@ void mt76x2_phy_set_txpower(struct mt76x2_dev *dev)
 	if (txp.target_power > dev->txpower_conf)
 		delta -= txp.target_power - dev->txpower_conf;
 
-	mt76x2_get_rate_power(dev, &t);
+	mt76x2_get_rate_power(dev, &t, chan);
 	mt76x2_add_rate_power_offset(&t, txp.chain[0].target_power +
 				   txp.chain[0].delta);
 	mt76x2_limit_rate_power(&t, dev->txpower_conf);
@@ -675,7 +676,7 @@ mt76x2_phy_tssi_compensate(struct mt76x2_dev *dev)
 			return;
 
 		dev->cal.tssi_comp_pending = false;
-		mt76x2_get_power_info(dev, &txp);
+		mt76x2_get_power_info(dev, &txp, chan);
 
 		if (mt76x2_ext_pa_enabled(dev, chan->band))
 			t.pa_mode = 1;
-- 
2.14.2

^ permalink raw reply related

* [PATCH 10/14] mt76x2: clean up MAC/BSSID address initialization
From: Felix Fietkau @ 2017-12-14 15:39 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo
In-Reply-To: <20171214153918.43774-1-nbd@nbd.name>

Drop the use of the EEPROM address entirely, rely on interface address
only

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/mt76x2_init.c | 11 ++++-------
 drivers/net/wireless/mediatek/mt76/mt76x2_mac.c  | 14 ++++++++++++++
 2 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
index ac4eeaf2c993..f54dc67a13d0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
@@ -231,7 +231,6 @@ mt76x2_init_beacon_offsets(struct mt76x2_dev *dev)
 int mt76x2_mac_reset(struct mt76x2_dev *dev, bool hard)
 {
 	static const u8 null_addr[ETH_ALEN] = {};
-	const u8 *macaddr = dev->mt76.macaddr;
 	u32 val;
 	int i, k;
 
@@ -271,13 +270,11 @@ int mt76x2_mac_reset(struct mt76x2_dev *dev, bool hard)
 	mt76_wr(dev, MT_MCU_CLOCK_CTL, 0x1401);
 	mt76_clear(dev, MT_FCE_L2_STUFF, MT_FCE_L2_STUFF_WR_MPDU_LEN_EN);
 
-	mt76_wr(dev, MT_MAC_ADDR_DW0, get_unaligned_le32(macaddr));
-	mt76_wr(dev, MT_MAC_ADDR_DW1, get_unaligned_le16(macaddr + 4));
+	mt76_wr(dev, MT_MAC_ADDR_DW0, 0);
+	mt76_wr(dev, MT_MAC_ADDR_DW1, 0);
 
-	mt76_wr(dev, MT_MAC_BSSID_DW0, get_unaligned_le32(macaddr));
-	mt76_wr(dev, MT_MAC_BSSID_DW1, get_unaligned_le16(macaddr + 4) |
-		FIELD_PREP(MT_MAC_BSSID_DW1_MBSS_MODE, 3) | /* 8 beacons */
-		MT_MAC_BSSID_DW1_MBSS_LOCAL_BIT);
+	mt76_wr(dev, MT_MAC_BSSID_DW0, 0);
+	mt76_wr(dev, MT_MAC_BSSID_DW1, 0);
 
 	mt76_set(dev, MT_MAC_ADDR_EXT_CTL, MT_MAC_ADDR_EXT_CTL_EN);
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
index 60cee4abed7c..d2ae093b7092 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
@@ -27,6 +27,15 @@ void mt76x2_mac_set_bssid(struct mt76x2_dev *dev, u8 idx, const u8 *addr)
 	if (addr) {
 		lo = get_unaligned_le32(addr);
 		hi = get_unaligned_le16(addr + 4);
+
+		if (!idx) {
+			mt76_wr(dev, MT_MAC_BSSID_DW0, lo);
+
+			mt76_rmw_field(dev, MT_MAC_BSSID_DW1,
+				       MT_MAC_BSSID_DW1_ADDR,
+				       hi);
+		}
+
 		hi |= MT_MAC_APC_BSSID0_H_EN;
 	}
 
@@ -41,6 +50,11 @@ void mt76x2_mac_set_ext_mac(struct mt76x2_dev *dev, u8 idx, const u8 *addr)
 	if (addr) {
 		lo = get_unaligned_le32(addr);
 		hi = get_unaligned_le16(addr + 4);
+
+		if (!idx) {
+			mt76_wr(dev, MT_MAC_ADDR_DW0, lo);
+			mt76_wr(dev, MT_MAC_ADDR_DW1, hi);
+		}
 	}
 
 	mt76_wr(dev, MT_MAC_ADDR_EXT_L(idx), lo);
-- 
2.14.2

^ permalink raw reply related

* [PATCH 11/14] mt76x2: drop wiphy->addresses
From: Felix Fietkau @ 2017-12-14 15:39 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo
In-Reply-To: <20171214153918.43774-1-nbd@nbd.name>

Now that the MAC address limitation is resolved, we no longer need a MAC
address list

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/mt76x2.h      |  2 --
 drivers/net/wireless/mediatek/mt76/mt76x2_init.c | 16 +---------------
 2 files changed, 1 insertion(+), 17 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2.h b/drivers/net/wireless/mediatek/mt76/mt76x2.h
index a993cc09cd1b..7f961cc4504a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2.h
@@ -84,8 +84,6 @@ struct mt76x2_calibration {
 struct mt76x2_dev {
 	struct mt76_dev mt76; /* must be first */
 
-	struct mac_address macaddr_list[8];
-
 	struct mutex mutex;
 
 	const u16 *beacon_offsets;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
index f54dc67a13d0..818164d1ed7a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
@@ -796,7 +796,7 @@ int mt76x2_register_device(struct mt76x2_dev *dev)
 	struct wiphy *wiphy = hw->wiphy;
 	void *status_fifo;
 	int fifo_size;
-	int i, ret;
+	int ret;
 
 	fifo_size = roundup_pow_of_two(32 * sizeof(struct mt76x2_tx_status));
 	status_fifo = devm_kzalloc(dev->mt76.dev, fifo_size, GFP_KERNEL);
@@ -818,20 +818,6 @@ int mt76x2_register_device(struct mt76x2_dev *dev)
 	hw->sta_data_size = sizeof(struct mt76x2_sta);
 	hw->vif_data_size = sizeof(struct mt76x2_vif);
 
-	for (i = 0; i < ARRAY_SIZE(dev->macaddr_list); i++) {
-		u8 *addr = dev->macaddr_list[i].addr;
-
-		memcpy(addr, dev->mt76.macaddr, ETH_ALEN);
-
-		if (!i)
-			continue;
-
-		addr[0] |= BIT(1);
-		addr[0] ^= ((i - 1) << 2);
-	}
-	wiphy->addresses = dev->macaddr_list;
-	wiphy->n_addresses = ARRAY_SIZE(dev->macaddr_list);
-
 	wiphy->iface_combinations = if_comb;
 	wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
 
-- 
2.14.2

^ permalink raw reply related

* [PATCH 09/14] mt76x2: remove MAC address limitation for multi-vif setups
From: Felix Fietkau @ 2017-12-14 15:39 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo
In-Reply-To: <20171214153918.43774-1-nbd@nbd.name>

The hardware has a separate set of registers to configure a
per-interface MAC address.

Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/mt76x2.h      |  1 +
 drivers/net/wireless/mediatek/mt76/mt76x2_init.c |  2 ++
 drivers/net/wireless/mediatek/mt76/mt76x2_mac.c  | 27 +++++++++++++++++----
 drivers/net/wireless/mediatek/mt76/mt76x2_mac.h  |  1 +
 drivers/net/wireless/mediatek/mt76/mt76x2_main.c | 30 ++++++++++--------------
 drivers/net/wireless/mediatek/mt76/mt76x2_regs.h |  8 +++++++
 6 files changed, 47 insertions(+), 22 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2.h b/drivers/net/wireless/mediatek/mt76/mt76x2.h
index a12dfce8c0d1..a993cc09cd1b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2.h
@@ -90,6 +90,7 @@ struct mt76x2_dev {
 
 	const u16 *beacon_offsets;
 	unsigned long wcid_mask[128 / BITS_PER_LONG];
+	unsigned long vif_mask;
 
 	int txpower_conf;
 	int txpower_cur;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
index 3e3a01b6f2ce..ac4eeaf2c993 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
@@ -279,6 +279,8 @@ int mt76x2_mac_reset(struct mt76x2_dev *dev, bool hard)
 		FIELD_PREP(MT_MAC_BSSID_DW1_MBSS_MODE, 3) | /* 8 beacons */
 		MT_MAC_BSSID_DW1_MBSS_LOCAL_BIT);
 
+	mt76_set(dev, MT_MAC_ADDR_EXT_CTL, MT_MAC_ADDR_EXT_CTL_EN);
+
 	/* Fire a pre-TBTT interrupt 8 ms before TBTT */
 	mt76_rmw_field(dev, MT_INT_TIMER_CFG, MT_INT_TIMER_CFG_PRE_TBTT,
 		       8 << 4);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
index a7416a01baa4..60cee4abed7c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
@@ -22,10 +22,29 @@
 
 void mt76x2_mac_set_bssid(struct mt76x2_dev *dev, u8 idx, const u8 *addr)
 {
-	idx &= 7;
-	mt76_wr(dev, MT_MAC_APC_BSSID_L(idx), get_unaligned_le32(addr));
-	mt76_rmw_field(dev, MT_MAC_APC_BSSID_H(idx), MT_MAC_APC_BSSID_H_ADDR,
-		       get_unaligned_le16(addr + 4));
+	u32 lo = 0, hi = 0;
+
+	if (addr) {
+		lo = get_unaligned_le32(addr);
+		hi = get_unaligned_le16(addr + 4);
+		hi |= MT_MAC_APC_BSSID0_H_EN;
+	}
+
+	mt76_wr(dev, MT_MAC_APC_BSSID_L(idx), lo);
+	mt76_wr(dev, MT_MAC_APC_BSSID_H(idx), hi);
+}
+
+void mt76x2_mac_set_ext_mac(struct mt76x2_dev *dev, u8 idx, const u8 *addr)
+{
+	u32 lo = 0, hi = 0;
+
+	if (addr) {
+		lo = get_unaligned_le32(addr);
+		hi = get_unaligned_le16(addr + 4);
+	}
+
+	mt76_wr(dev, MT_MAC_ADDR_EXT_L(idx), lo);
+	mt76_wr(dev, MT_MAC_ADDR_EXT_H(idx), hi);
 }
 
 static int
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.h b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.h
index 8a8a25e32d5f..a993c383d9e7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.h
@@ -162,6 +162,7 @@ int mt76x2_mac_start(struct mt76x2_dev *dev);
 void mt76x2_mac_stop(struct mt76x2_dev *dev, bool force);
 void mt76x2_mac_resume(struct mt76x2_dev *dev);
 void mt76x2_mac_set_bssid(struct mt76x2_dev *dev, u8 idx, const u8 *addr);
+void mt76x2_mac_set_ext_mac(struct mt76x2_dev *dev, u8 idx, const u8 *addr);
 
 int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb,
 			  void *rxi);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
index 8098a2b82c5b..240e11508ecd 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
@@ -82,26 +82,15 @@ mt76x2_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 {
 	struct mt76x2_dev *dev = hw->priv;
 	struct mt76x2_vif *mvif = (struct mt76x2_vif *) vif->drv_priv;
-	unsigned int idx = 0;
+	unsigned int idx;
 
-	if (vif->addr[0] & BIT(1))
-		idx = 1 + (((dev->mt76.macaddr[0] ^ vif->addr[0]) >> 2) & 7);
+	idx = ffs(~dev->vif_mask);
+	if (!idx || idx > 8)
+		return -ENOSPC;
 
-	/*
-	 * Client mode typically only has one configurable BSSID register,
-	 * which is used for bssidx=0. This is linked to the MAC address.
-	 * Since mac80211 allows changing interface types, and we cannot
-	 * force the use of the primary MAC address for a station mode
-	 * interface, we need some other way of configuring a per-interface
-	 * remote BSSID.
-	 * The hardware provides an AP-Client feature, where bssidx 0-7 are
-	 * used for AP mode and bssidx 8-15 for client mode.
-	 * We shift the station interface bss index by 8 to force the
-	 * hardware to recognize the BSSID.
-	 * The resulting bssidx mismatch for unicast frames is ignored by hw.
-	 */
-	if (vif->type == NL80211_IFTYPE_STATION)
-		idx += 8;
+	idx--;
+	dev->vif_mask |= BIT(idx);
+	mt76x2_mac_set_ext_mac(dev, idx, vif->addr);
 
 	mvif->idx = idx;
 	mvif->group_wcid.idx = 254 - idx;
@@ -114,8 +103,13 @@ mt76x2_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 static void
 mt76x2_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 {
+	struct mt76x2_vif *mvif = (struct mt76x2_vif *)vif->drv_priv;
 	struct mt76x2_dev *dev = hw->priv;
+	int idx = mvif->idx;
 
+	mt76x2_mac_set_bssid(dev, mvif->idx, NULL);
+	mt76x2_mac_set_ext_mac(dev, idx, NULL);
+	dev->vif_mask &= ~BIT(idx);
 	mt76_txq_remove(&dev->mt76, vif->txq);
 }
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_regs.h b/drivers/net/wireless/mediatek/mt76/mt76x2_regs.h
index ce3ab85c8b0f..95c1e9559f1c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_regs.h
@@ -438,6 +438,14 @@
 
 #define MT_TX_SW_CFG3			0x1478
 
+#define MT_MAC_ADDR_EXT_CTL		0x147c
+#define MT_MAC_ADDR_EXT_CTL_EN		BIT(0)
+
+#define MT_MAC_ADDR_EXT_BASE		0x1480
+#define MT_MAC_ADDR_EXT_L(_n)		(MT_MAC_ADDR_EXT_BASE + ((_n) * 8))
+#define MT_MAC_ADDR_EXT_H(_n)		(MT_MAC_ADDR_EXT_BASE + ((_n) * 8 + 4))
+#define MT_MAC_ADDR_EXT_H_MASK		GENMASK(15, 0)
+
 #define MT_PN_PAD_MODE			0x150c
 
 #define MT_TXOP_HLDR_ET			0x1608
-- 
2.14.2

^ permalink raw reply related

* [PATCH 13/14] mt76x2: configure rx filter based on monitor mode setting
From: Felix Fietkau @ 2017-12-14 15:39 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo
In-Reply-To: <20171214153918.43774-1-nbd@nbd.name>

Due to an unrelated issue, the MT_RX_FILTR_CFG_PROMISC flag is currently
unset, which means that monitor mode is unconditionally enabled.
Toggle this flag based on the mac80211 monitor mode setting instead

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/mt76x2_main.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
index 240e11508ecd..a69d441d9a47 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
@@ -146,6 +146,15 @@ mt76x2_config(struct ieee80211_hw *hw, u32 changed)
 
 	mutex_lock(&dev->mutex);
 
+	if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
+		if (!(hw->conf.flags & IEEE80211_CONF_MONITOR))
+			dev->rxfilter |= MT_RX_FILTR_CFG_PROMISC;
+		else
+			dev->rxfilter &= ~MT_RX_FILTR_CFG_PROMISC;
+
+		mt76_wr(dev, MT_RX_FILTR_CFG, dev->rxfilter);
+	}
+
 	if (changed & IEEE80211_CONF_CHANGE_POWER) {
 		dev->txpower_conf = hw->conf.power_level * 2;
 
-- 
2.14.2

^ permalink raw reply related

* [PATCH 12/14] mt76x2: init: disable APCLI by default
From: Felix Fietkau @ 2017-12-14 15:39 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo
In-Reply-To: <20171214153918.43774-1-nbd@nbd.name>

From: Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>

It is no longer necessary for client mode operation, vif index entries
8-16 are no longer used

Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/mt76x2_init.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
index 818164d1ed7a..ad17f9462358 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
@@ -307,8 +307,6 @@ int mt76x2_mac_reset(struct mt76x2_dev *dev, bool hard)
 	for (i = 0; i < 16; i++)
 		mt76_rr(dev, MT_TX_STAT_FIFO);
 
-	mt76_set(dev, MT_MAC_APC_BSSID_H(0), MT_MAC_APC_BSSID0_H_EN);
-
 	mt76_wr(dev, MT_CH_TIME_CFG,
 		MT_CH_TIME_CFG_TIMER_EN |
 		MT_CH_TIME_CFG_TX_AS_BUSY |
-- 
2.14.2

^ permalink raw reply related

* [PATCH 08/14] mt76x2: convert between per-chain tx power and combined output
From: Felix Fietkau @ 2017-12-14 15:39 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo
In-Reply-To: <20171214153918.43774-1-nbd@nbd.name>

Using both chains adds max. 3 dBm. A similar worst-case calculation is
being used in ath9k as well to ensure that the hardware stays within
regulatory limits

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/mt76x2_init.c | 3 +++
 drivers/net/wireless/mediatek/mt76/mt76x2_main.c | 7 +++++++
 2 files changed, 10 insertions(+)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
index 0755b451829e..3e3a01b6f2ce 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
@@ -785,6 +785,9 @@ mt76x2_init_txpower(struct mt76x2_dev *dev,
 		chan->max_power = mt76x2_get_max_rate_power(&t) +
 				  target_power;
 		chan->max_power /= 2;
+
+		/* convert to combined output power on 2x2 devices */
+		chan->max_power += 3;
 	}
 }
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
index 02dd2cafa52f..8098a2b82c5b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
@@ -155,6 +155,9 @@ mt76x2_config(struct ieee80211_hw *hw, u32 changed)
 	if (changed & IEEE80211_CONF_CHANGE_POWER) {
 		dev->txpower_conf = hw->conf.power_level * 2;
 
+		/* convert to per-chain power for 2x2 devices */
+		dev->txpower_conf -= 6;
+
 		if (test_bit(MT76_STATE_RUNNING, &dev->mt76.state)) {
 			mt76x2_phy_set_txpower(dev);
 			mt76x2_tx_set_txpwr_auto(dev, dev->txpower_conf);
@@ -443,6 +446,10 @@ mt76x2_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int *dbm)
 	struct mt76x2_dev *dev = hw->priv;
 
 	*dbm = dev->txpower_cur / 2;
+
+	/* convert from per-chain power to combined output on 2x2 devices */
+	*dbm += 3;
+
 	return 0;
 }
 
-- 
2.14.2

^ permalink raw reply related

* [PATCH 14/14] mt76x2: init: fix rx filter default value during init
From: Felix Fietkau @ 2017-12-14 15:39 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo
In-Reply-To: <20171214153918.43774-1-nbd@nbd.name>

From: Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>

mt76x2_mac_start writes dev->rxfilter to the hardware. It also happens
during init, before dev->rxfilter is filled with the initval register
value, leading to issues like promisc mode being enabled
unconditionally.

Fix this by reading the default value into dev->rxfilter earlier

Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/mt76x2_init.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
index ad17f9462358..62d702164dfe 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
@@ -583,6 +583,8 @@ int mt76x2_init_hardware(struct mt76x2_dev *dev)
 	if (ret)
 		return ret;
 
+	dev->rxfilter = mt76_rr(dev, MT_RX_FILTR_CFG);
+
 	ret = mt76x2_dma_init(dev);
 	if (ret)
 		return ret;
@@ -597,7 +599,6 @@ int mt76x2_init_hardware(struct mt76x2_dev *dev)
 		return ret;
 
 	mt76x2_mac_stop(dev, false);
-	dev->rxfilter = mt76_rr(dev, MT_RX_FILTR_CFG);
 
 	return 0;
 }
-- 
2.14.2

^ permalink raw reply related

* [PATCH 00/14] mt76 fixes
From: Felix Fietkau @ 2017-12-14 15:39 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo

Felix Fietkau (10):
  mt76x2: remove some harmless WARN_ONs in tx status and rx path
  mt76x2: fix transmission of encrypted management frames
  mt76x2: increase OFDM SIFS time
  mt76x2: add channel argument to eeprom tx power functions
  mt76x2: initialize channel power limits at probe time
  mt76x2: convert between per-chain tx power and combined output
  mt76x2: remove MAC address limitation for multi-vif setups
  mt76x2: clean up MAC/BSSID address initialization
  mt76x2: drop wiphy->addresses
  mt76x2: configure rx filter based on monitor mode setting

Fengguang Wu (2):
  mt76: fix debugfs_simple_attr.cocci warnings
  mt76: fix returnvar.cocci warnings

Lorenzo Bianconi (2):
  mt76x2: init: disable APCLI by default
  mt76x2: init: fix rx filter default value during init

 drivers/net/wireless/mediatek/mt76/debugfs.c       |  7 +-
 drivers/net/wireless/mediatek/mt76/mt76.h          |  1 +
 drivers/net/wireless/mediatek/mt76/mt76x2.h        |  3 +-
 drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c | 41 ++++++++---
 drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h |  7 +-
 drivers/net/wireless/mediatek/mt76/mt76x2_init.c   | 67 ++++++++++-------
 drivers/net/wireless/mediatek/mt76/mt76x2_mac.c    | 85 +++++++++++++++++-----
 drivers/net/wireless/mediatek/mt76/mt76x2_mac.h    |  1 +
 drivers/net/wireless/mediatek/mt76/mt76x2_main.c   | 57 +++++++++------
 drivers/net/wireless/mediatek/mt76/mt76x2_phy.c    | 30 ++------
 drivers/net/wireless/mediatek/mt76/mt76x2_regs.h   |  8 ++
 drivers/net/wireless/mediatek/mt76/mt76x2_tx.c     |  6 +-
 12 files changed, 204 insertions(+), 109 deletions(-)

-- 
2.14.2

^ permalink raw reply

* [PATCH 07/14] mt76x2: initialize channel power limits at probe time
From: Felix Fietkau @ 2017-12-14 15:39 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo
In-Reply-To: <20171214153918.43774-1-nbd@nbd.name>

This allows user space to query the real hardware limits directly

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c | 11 ++++++++
 drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h |  1 +
 drivers/net/wireless/mediatek/mt76/mt76x2_init.c   | 30 ++++++++++++++++++++++
 drivers/net/wireless/mediatek/mt76/mt76x2_phy.c    | 14 +---------
 4 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c
index 8fef400cb58e..9c9bf3e785ba 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.c
@@ -483,6 +483,17 @@ void mt76x2_get_rate_power(struct mt76x2_dev *dev, struct mt76_rate_power *t,
 	t->vht[8] = t->vht[9] = mt76x2_rate_power_val(val >> 8);
 }
 
+int mt76x2_get_max_rate_power(struct mt76_rate_power *r)
+{
+	int i;
+	s8 ret = 0;
+
+	for (i = 0; i < sizeof(r->all); i++)
+		ret = max(ret, r->all[i]);
+
+	return ret;
+}
+
 static void
 mt76x2_get_power_info_2g(struct mt76x2_dev *dev, struct mt76x2_tx_power_info *t,
 		         struct ieee80211_channel *chan, int chain, int offset)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h b/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h
index 6db2c6e47569..d79122728dca 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_eeprom.h
@@ -148,6 +148,7 @@ mt76x2_eeprom_get(struct mt76x2_dev *dev, enum mt76x2_eeprom_field field)
 
 void mt76x2_get_rate_power(struct mt76x2_dev *dev, struct mt76_rate_power *t,
 			   struct ieee80211_channel *chan);
+int mt76x2_get_max_rate_power(struct mt76_rate_power *r);
 void mt76x2_get_power_info(struct mt76x2_dev *dev,
 			   struct mt76x2_tx_power_info *t,
 			   struct ieee80211_channel *chan);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
index d3f03a8aee90..0755b451829e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_init.c
@@ -760,6 +760,34 @@ static void mt76x2_led_set_brightness(struct led_classdev *led_cdev,
 		mt76x2_led_set_config(mt76, 0xff, 0);
 }
 
+static void
+mt76x2_init_txpower(struct mt76x2_dev *dev,
+		    struct ieee80211_supported_band *sband)
+{
+	struct ieee80211_channel *chan;
+	struct mt76x2_tx_power_info txp;
+	struct mt76_rate_power t = {};
+	int target_power;
+	int i;
+
+	for (i = 0; i < sband->n_channels; i++) {
+		chan = &sband->channels[i];
+
+		mt76x2_get_power_info(dev, &txp, chan);
+
+		target_power = max_t(int, (txp.chain[0].target_power +
+					   txp.chain[0].delta),
+					  (txp.chain[1].target_power +
+					   txp.chain[1].delta));
+
+		mt76x2_get_rate_power(dev, &t, chan);
+
+		chan->max_power = mt76x2_get_max_rate_power(&t) +
+				  target_power;
+		chan->max_power /= 2;
+	}
+}
+
 int mt76x2_register_device(struct mt76x2_dev *dev)
 {
 	struct ieee80211_hw *hw = mt76_hw(dev);
@@ -828,6 +856,8 @@ int mt76x2_register_device(struct mt76x2_dev *dev)
 		goto fail;
 
 	mt76x2_init_debugfs(dev);
+	mt76x2_init_txpower(dev, &dev->mt76.sband_2g.sband);
+	mt76x2_init_txpower(dev, &dev->mt76.sband_5g.sband);
 
 	return 0;
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
index 81cea7e8414e..5b742749d5de 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
@@ -102,18 +102,6 @@ mt76x2_limit_rate_power(struct mt76_rate_power *r, int limit)
 			r->all[i] = limit;
 }
 
-static int
-mt76x2_get_max_power(struct mt76_rate_power *r)
-{
-	int i;
-	s8 ret = 0;
-
-	for (i = 0; i < sizeof(r->all); i++)
-		ret = max(ret, r->all[i]);
-
-	return ret;
-}
-
 void mt76x2_phy_set_txpower(struct mt76x2_dev *dev)
 {
 	enum nl80211_chan_width width = dev->mt76.chandef.width;
@@ -136,7 +124,7 @@ void mt76x2_phy_set_txpower(struct mt76x2_dev *dev)
 	mt76x2_add_rate_power_offset(&t, txp.chain[0].target_power +
 				   txp.chain[0].delta);
 	mt76x2_limit_rate_power(&t, dev->txpower_conf);
-	dev->txpower_cur = mt76x2_get_max_power(&t);
+	dev->txpower_cur = mt76x2_get_max_rate_power(&t);
 	mt76x2_add_rate_power_offset(&t, -(txp.chain[0].target_power +
 					 txp.chain[0].delta + delta));
 	dev->target_power = txp.chain[0].target_power;
-- 
2.14.2

^ permalink raw reply related

* [PATCH 01/14] mt76: fix debugfs_simple_attr.cocci warnings
From: Felix Fietkau @ 2017-12-14 15:39 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo
In-Reply-To: <20171214153918.43774-1-nbd@nbd.name>

From: Fengguang Wu <fengguang.wu@intel.com>

drivers/net/wireless/mediatek/mt76/debugfs.c:36:0-23: WARNING: fops_regval should be defined with DEFINE_DEBUGFS_ATTRIBUTE

 Use DEFINE_DEBUGFS_ATTRIBUTE rather than DEFINE_SIMPLE_ATTRIBUTE
 for debugfs files.

Semantic patch information:
 Rationale: DEFINE_SIMPLE_ATTRIBUTE + debugfs_create_file()
 imposes some significant overhead as compared to
 DEFINE_DEBUGFS_ATTRIBUTE + debugfs_create_file_unsafe().

Generated by: scripts/coccinelle/api/debugfs/debugfs_simple_attr.cocci

Fixes: a5f6039c8f9c ("mt76: add driver code for MT76x2e")
CC: Felix Fietkau <nbd@nbd.name>
Signed-off-by: Fengguang Wu <fengguang.wu@intel.com>
---
 drivers/net/wireless/mediatek/mt76/debugfs.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/debugfs.c b/drivers/net/wireless/mediatek/mt76/debugfs.c
index 7c3612aaa8c4..c121b502a462 100644
--- a/drivers/net/wireless/mediatek/mt76/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/debugfs.c
@@ -33,7 +33,8 @@ mt76_reg_get(void *data, u64 *val)
 	return 0;
 }
 
-DEFINE_SIMPLE_ATTRIBUTE(fops_regval, mt76_reg_get, mt76_reg_set, "0x%08llx\n");
+DEFINE_DEBUGFS_ATTRIBUTE(fops_regval, mt76_reg_get, mt76_reg_set,
+			 "0x%08llx\n");
 
 static int
 mt76_queues_read(struct seq_file *s, void *data)
@@ -65,8 +66,8 @@ struct dentry *mt76_register_debugfs(struct mt76_dev *dev)
 
 	debugfs_create_u8("led_pin", S_IRUSR | S_IWUSR, dir, &dev->led_pin);
 	debugfs_create_u32("regidx", S_IRUSR | S_IWUSR, dir, &dev->debugfs_reg);
-	debugfs_create_file("regval", S_IRUSR | S_IWUSR, dir, dev,
-			    &fops_regval);
+	debugfs_create_file_unsafe("regval", S_IRUSR | S_IWUSR, dir, dev,
+				   &fops_regval);
 	debugfs_create_blob("eeprom", S_IRUSR, dir, &dev->eeprom);
 	if (dev->otp.data)
 		debugfs_create_blob("otp", S_IRUSR, dir, &dev->otp);
-- 
2.14.2

^ permalink raw reply related

* [PATCH 05/14] mt76x2: increase OFDM SIFS time
From: Felix Fietkau @ 2017-12-14 15:39 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo
In-Reply-To: <20171214153918.43774-1-nbd@nbd.name>

Fixes throughput issues in combination with LDPC

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/mt76x2_phy.c | 9 +--------
 1 file changed, 1 insertion(+), 8 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
index 126497172284..fe3a4b6a19cc 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_phy.c
@@ -325,8 +325,7 @@ mt76x2_configure_tx_delay(struct mt76x2_dev *dev, enum nl80211_band band, u8 bw)
 	mt76_wr(dev, MT_TX_SW_CFG0, cfg0);
 	mt76_wr(dev, MT_TX_SW_CFG1, cfg1);
 
-	mt76_rmw_field(dev, MT_XIFS_TIME_CFG, MT_XIFS_TIME_CFG_CCK_SIFS,
-		       13 + (bw ? 1 : 0));
+	mt76_rmw_field(dev, MT_XIFS_TIME_CFG, MT_XIFS_TIME_CFG_OFDM_SIFS, 15);
 }
 
 static void
@@ -559,7 +558,6 @@ int mt76x2_phy_set_channel(struct mt76x2_dev *dev,
 	u8 bw, bw_index;
 	int freq, freq1;
 	int ret;
-	u8 sifs = 13;
 
 	dev->cal.channel_cal_done = false;
 	freq = chandef->chan->center_freq;
@@ -611,11 +609,6 @@ int mt76x2_phy_set_channel(struct mt76x2_dev *dev,
 		  MT_EXT_CCA_CFG_CCA_MASK),
 		 ext_cca_chan[ch_group_index]);
 
-	if (chandef->width >= NL80211_CHAN_WIDTH_40)
-		sifs++;
-
-	mt76_rmw_field(dev, MT_XIFS_TIME_CFG, MT_XIFS_TIME_CFG_OFDM_SIFS, sifs);
-
 	ret = mt76x2_mcu_set_channel(dev, channel, bw, bw_index, scan);
 	if (ret)
 		return ret;
-- 
2.14.2

^ permalink raw reply related

* [PATCH 03/14] mt76x2: remove some harmless WARN_ONs in tx status and rx path
From: Felix Fietkau @ 2017-12-14 15:39 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo
In-Reply-To: <20171214153918.43774-1-nbd@nbd.name>

Discard affected packets instead. Should reduce the frequency of bogus
bug reports

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/mt76x2_mac.c | 28 ++++++++++++-------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
index a1f695e9b51c..f7c0df0759f7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
@@ -28,7 +28,7 @@ void mt76x2_mac_set_bssid(struct mt76x2_dev *dev, u8 idx, const u8 *addr)
 		       get_unaligned_le16(addr + 4));
 }
 
-static void
+static int
 mt76x2_mac_process_rate(struct ieee80211_rx_status *status, u16 rate)
 {
 	u8 idx = FIELD_GET(MT_RXWI_RATE_INDEX, rate);
@@ -42,7 +42,7 @@ mt76x2_mac_process_rate(struct ieee80211_rx_status *status, u16 rate)
 			idx += 4;
 
 		status->rate_idx = idx;
-		return;
+		return 0;
 	case MT_PHY_TYPE_CCK:
 		if (idx >= 8) {
 			idx -= 8;
@@ -53,7 +53,7 @@ mt76x2_mac_process_rate(struct ieee80211_rx_status *status, u16 rate)
 			idx = 0;
 
 		status->rate_idx = idx;
-		return;
+		return 0;
 	case MT_PHY_TYPE_HT_GF:
 		status->enc_flags |= RX_ENC_FLAG_HT_GF;
 		/* fall through */
@@ -67,8 +67,7 @@ mt76x2_mac_process_rate(struct ieee80211_rx_status *status, u16 rate)
 		status->nss = FIELD_GET(MT_RATE_INDEX_VHT_NSS, idx) + 1;
 		break;
 	default:
-		WARN_ON(1);
-		return;
+		return -EINVAL;
 	}
 
 	if (rate & MT_RXWI_RATE_LDPC)
@@ -92,6 +91,8 @@ mt76x2_mac_process_rate(struct ieee80211_rx_status *status, u16 rate)
 	default:
 		break;
 	}
+
+	return 0;
 }
 
 static __le16
@@ -272,12 +273,10 @@ int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb,
 	status->freq = dev->mt76.chandef.chan->center_freq;
 	status->band = dev->mt76.chandef.chan->band;
 
-	mt76x2_mac_process_rate(status, rate);
-
-	return 0;
+	return mt76x2_mac_process_rate(status, rate);
 }
 
-static void
+static int
 mt76x2_mac_process_tx_rate(struct ieee80211_tx_rate *txrate, u16 rate,
 			   enum nl80211_band band)
 {
@@ -293,13 +292,13 @@ mt76x2_mac_process_tx_rate(struct ieee80211_tx_rate *txrate, u16 rate,
 			idx += 4;
 
 		txrate->idx = idx;
-		return;
+		return 0;
 	case MT_PHY_TYPE_CCK:
 		if (idx >= 8)
 			idx -= 8;
 
 		txrate->idx = idx;
-		return;
+		return 0;
 	case MT_PHY_TYPE_HT_GF:
 		txrate->flags |= IEEE80211_TX_RC_GREEN_FIELD;
 		/* fall through */
@@ -312,8 +311,7 @@ mt76x2_mac_process_tx_rate(struct ieee80211_tx_rate *txrate, u16 rate,
 		txrate->idx = idx;
 		break;
 	default:
-		WARN_ON(1);
-		return;
+		return -EINVAL;
 	}
 
 	switch (FIELD_GET(MT_RXWI_RATE_BW, rate)) {
@@ -326,12 +324,14 @@ mt76x2_mac_process_tx_rate(struct ieee80211_tx_rate *txrate, u16 rate,
 		txrate->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH;
 		break;
 	default:
-		WARN_ON(1);
+		return -EINVAL;
 		break;
 	}
 
 	if (rate & MT_RXWI_RATE_SGI)
 		txrate->flags |= IEEE80211_TX_RC_SHORT_GI;
+
+	return 0;
 }
 
 static void
-- 
2.14.2

^ permalink raw reply related

* [PATCH 04/14] mt76x2: fix transmission of encrypted management frames
From: Felix Fietkau @ 2017-12-14 15:39 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo
In-Reply-To: <20171214153918.43774-1-nbd@nbd.name>

Hardware encryption seems to break encrypted unicast mgmt tx.
Unfortunately the hardware TXWI header does not have a bit to indicate
that a frame is software encrypted, so sw-encrypted frames need to use a
different WCID. For that to work, the CCMP PN needs to be generated in
software, which makes things a bit slower, so only do it for keys that
also need to tx management frames.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/mediatek/mt76/mt76.h        |  1 +
 drivers/net/wireless/mediatek/mt76/mt76x2_mac.c  | 16 ++++++++++++++++
 drivers/net/wireless/mediatek/mt76/mt76x2_main.c |  8 +++++++-
 drivers/net/wireless/mediatek/mt76/mt76x2_tx.c   |  6 ++++--
 4 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index aa0880bbea7f..f88d9a15210a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -129,6 +129,7 @@ struct mt76_wcid {
 	bool tx_rate_set;
 	u8 tx_rate_nss;
 	s8 max_txpwr_adj;
+	bool sw_iv;
 };
 
 struct mt76_txq {
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
index f7c0df0759f7..a7416a01baa4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_mac.c
@@ -171,10 +171,12 @@ void mt76x2_mac_write_txwi(struct mt76x2_dev *dev, struct mt76x2_txwi *txwi,
 {
 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 	struct ieee80211_tx_rate *rate = &info->control.rates[0];
+	struct ieee80211_key_conf *key = info->control.hw_key;
 	u16 rate_ht_mask = FIELD_PREP(MT_RXWI_RATE_PHY, BIT(1) | BIT(2));
 	u16 txwi_flags = 0;
 	u8 nss;
 	s8 txpwr_adj, max_txpwr_adj;
+	u8 ccmp_pn[8];
 
 	memset(txwi, 0, sizeof(*txwi));
 
@@ -185,6 +187,20 @@ void mt76x2_mac_write_txwi(struct mt76x2_dev *dev, struct mt76x2_txwi *txwi,
 
 	txwi->pktid = 1;
 
+	if (wcid && wcid->sw_iv && key) {
+		u64 pn = atomic64_inc_return(&key->tx_pn);
+		ccmp_pn[0] = pn;
+		ccmp_pn[1] = pn >> 8;
+		ccmp_pn[2] = 0;
+		ccmp_pn[3] = 0x20 | (key->keyidx << 6);
+		ccmp_pn[4] = pn >> 16;
+		ccmp_pn[5] = pn >> 24;
+		ccmp_pn[6] = pn >> 32;
+		ccmp_pn[7] = pn >> 40;
+		txwi->iv = *((u32 *) &ccmp_pn[0]);
+		txwi->eiv = *((u32 *) &ccmp_pn[1]);
+	}
+
 	spin_lock_bh(&dev->mt76.lock);
 	if (wcid && (rate->idx < 0 || !rate->count)) {
 		txwi->rate = wcid->tx_rate;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
index 88fe5e0de0b9..02dd2cafa52f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
@@ -343,9 +343,15 @@ mt76x2_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 	if (cmd == SET_KEY) {
 		key->hw_key_idx = wcid->idx;
 		wcid->hw_key_idx = idx;
+		if (key->flags & IEEE80211_KEY_FLAG_RX_MGMT) {
+			key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
+			wcid->sw_iv = true;
+		}
 	} else {
-		if (idx == wcid->hw_key_idx)
+		if (idx == wcid->hw_key_idx) {
 			wcid->hw_key_idx = -1;
+			wcid->sw_iv = true;
+		}
 
 		key = NULL;
 	}
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_tx.c b/drivers/net/wireless/mediatek/mt76/mt76x2_tx.c
index 1a32e1fb8743..534e4bf9a34c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_tx.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_tx.c
@@ -36,7 +36,9 @@ void mt76x2_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
 
 		msta = (struct mt76x2_sta *) control->sta->drv_priv;
 		wcid = &msta->wcid;
-	} else if (vif) {
+	}
+
+	if (vif || (!info->control.hw_key && wcid->hw_key_idx != -1)) {
 		struct mt76x2_vif *mvif;
 
 		mvif = (struct mt76x2_vif *) vif->drv_priv;
@@ -166,7 +168,7 @@ int mt76x2_tx_prepare_skb(struct mt76_dev *mdev, void *txwi,
 	*tx_info = FIELD_PREP(MT_TXD_INFO_QSEL, qsel) |
 		   MT_TXD_INFO_80211;
 
-	if (!wcid || wcid->hw_key_idx == 0xff)
+	if (!wcid || wcid->hw_key_idx == 0xff || wcid->sw_iv)
 		*tx_info |= MT_TXD_INFO_WIV;
 
 	return 0;
-- 
2.14.2

^ permalink raw reply related

* [PATCH 02/14] mt76: fix returnvar.cocci warnings
From: Felix Fietkau @ 2017-12-14 15:39 UTC (permalink / raw)
  To: linux-wireless; +Cc: kvalo
In-Reply-To: <20171214153918.43774-1-nbd@nbd.name>

From: Fengguang Wu <fengguang.wu@intel.com>

drivers/net/wireless/mediatek/mt76/mt76x2_main.c:86:5-8: Unneeded variable: "ret". Return "0" on line 112

 Remove unneeded variable used to store return value.

Generated by: scripts/coccinelle/misc/returnvar.cocci

Fixes: a5f6039c8f9c ("mt76: add driver code for MT76x2e")
CC: Felix Fietkau <nbd@nbd.name>
Signed-off-by: Fengguang Wu <fengguang.wu@intel.com>
---
 drivers/net/wireless/mediatek/mt76/mt76x2_main.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
index 33469e32567b..88fe5e0de0b9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2_main.c
@@ -83,7 +83,6 @@ mt76x2_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 	struct mt76x2_dev *dev = hw->priv;
 	struct mt76x2_vif *mvif = (struct mt76x2_vif *) vif->drv_priv;
 	unsigned int idx = 0;
-	int ret = 0;
 
 	if (vif->addr[0] & BIT(1))
 		idx = 1 + (((dev->mt76.macaddr[0] ^ vif->addr[0]) >> 2) & 7);
@@ -109,7 +108,7 @@ mt76x2_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 	mvif->group_wcid.hw_key_idx = -1;
 	mt76x2_txq_init(dev, vif->txq);
 
-	return ret;
+	return 0;
 }
 
 static void
-- 
2.14.2

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox