diff mbox series

[RFC] qtnfmac: enable access to the card in calibration mode

Message ID 20200416210025.1096-1-sergey.matyukevich.os@quantenna.com (mailing list archive)
State RFC
Delegated to: Kalle Valo
Headers show
Series [RFC] qtnfmac: enable access to the card in calibration mode | expand

Commit Message

Sergey Matyukevich April 16, 2020, 9 p.m. UTC
Enable access to the wireless card in calibration mode using service
ethernet port. Wireless functionality is not available in calibration
mode. Service ethernet port can be used for various maintenance tasks
including calibration, configuration, troubleshooting. Add new kernel
module parameter force_svcmode. Set this parameter to one in order
to boot wireless card into the calibration mode.

Signed-off-by: Sergey Matyukevich <sergey.matyukevich.os@quantenna.com>

---

Hello Kalle and all,

I would like to add support for a simple service mode for calibration.
This patch includes some controversial bits, that is why it is marked
as RFC. Could you please take a closer look and tell me whether it is
acceptable for mainlining.

Regards,
Sergey

---
 drivers/net/wireless/quantenna/qtnfmac/bus.h       |  1 +
 drivers/net/wireless/quantenna/qtnfmac/commands.c  |  1 +
 drivers/net/wireless/quantenna/qtnfmac/core.c      | 83 ++++++++++++++++++++++
 drivers/net/wireless/quantenna/qtnfmac/core.h      |  1 +
 drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c |  5 ++
 .../wireless/quantenna/qtnfmac/pcie/pcie_priv.h    |  3 +-
 .../wireless/quantenna/qtnfmac/pcie/topaz_pcie.c   |  3 +
 drivers/net/wireless/quantenna/qtnfmac/qlink.h     |  2 +
 8 files changed, 98 insertions(+), 1 deletion(-)

Comments

Kalle Valo April 21, 2020, 12:47 p.m. UTC | #1
Sergey Matyukevich <sergey.matyukevich.os@quantenna.com> writes:

> Enable access to the wireless card in calibration mode using service
> ethernet port. Wireless functionality is not available in calibration
> mode. Service ethernet port can be used for various maintenance tasks
> including calibration, configuration, troubleshooting. Add new kernel
> module parameter force_svcmode. Set this parameter to one in order
> to boot wireless card into the calibration mode.
>
> Signed-off-by: Sergey Matyukevich <sergey.matyukevich.os@quantenna.com>
>
> ---
>
> Hello Kalle and all,
>
> I would like to add support for a simple service mode for calibration.
> This patch includes some controversial bits, that is why it is marked
> as RFC. Could you please take a closer look and tell me whether it is
> acceptable for mainlining.

Is there a reason why you can't use nl80211 testmode? The use cases you
list were exactly the reasons why we added testmode to nl80211 in the
first place.
Sergey Matyukevich April 24, 2020, 9:57 a.m. UTC | #2
> > Enable access to the wireless card in calibration mode using service
> > ethernet port. Wireless functionality is not available in calibration
> > mode. Service ethernet port can be used for various maintenance tasks
> > including calibration, configuration, troubleshooting. Add new kernel
> > module parameter force_svcmode. Set this parameter to one in order
> > to boot wireless card into the calibration mode.
> >
> > Signed-off-by: Sergey Matyukevich <sergey.matyukevich.os@quantenna.com>
> >
> > ---
> >
> > Hello Kalle and all,
> >
> > I would like to add support for a simple service mode for calibration.
> > This patch includes some controversial bits, that is why it is marked
> > as RFC. Could you please take a closer look and tell me whether it is
> > acceptable for mainlining.
> 
> Is there a reason why you can't use nl80211 testmode? The use cases you
> list were exactly the reasons why we added testmode to nl80211 in the
> first place.

Hello Kalle,

Thanks for reminding me of testmode. I took a closer look at this feature.
This is what I would be using if I had to implement calibration mode and
tools from the scratch. In my case I have to deal with legacy behavior in
calibration mode: special firmware mode and tools that expect simple
network access to firmware. One possible option would be to provide a
dummy wireless interface. However additional manipulations with carrier
would be required to bring-up dummy wireless interface. So I ended up
with a simple ethernet based solution.

Regards,
Sergey
diff mbox series

Patch

diff --git a/drivers/net/wireless/quantenna/qtnfmac/bus.h b/drivers/net/wireless/quantenna/qtnfmac/bus.h
index 87d048df09d1..7811833a84c9 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/bus.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/bus.h
@@ -67,6 +67,7 @@  struct qtnf_bus {
 	struct mutex bus_lock; /* lock during command/event processing */
 	struct dentry *dbg_dir;
 	struct notifier_block netdev_nb;
+	struct net_device *svc_ndev;
 	u8 hw_id[ETH_ALEN];
 	/* bus private data */
 	char bus_priv[0] __aligned(sizeof(void *));
diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c
index f40d8c3c3d9e..984300ac2f30 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/commands.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c
@@ -980,6 +980,7 @@  qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus,
 
 	strlcpy(hwinfo->fw_version, bld_label, sizeof(hwinfo->fw_version));
 	hwinfo->hw_version = hw_ver;
+	hwinfo->svc_mode = qtnf_hwcap_is_set(hwinfo, QLINK_HW_CAPAB_SVC_MODE);
 
 	return 0;
 }
diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.c b/drivers/net/wireless/quantenna/qtnfmac/core.c
index eea777f8acea..0b93ab7cca61 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/core.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/core.c
@@ -244,6 +244,49 @@  const struct net_device_ops qtnf_netdev_ops = {
 	.ndo_get_port_parent_id = qtnf_netdev_port_parent_id,
 };
 
+static netdev_tx_t
+qtnf_svc_ndev_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+	struct qtnf_bus *bus = *((void **)netdev_priv(ndev));
+
+	if (unlikely(!bus)) {
+		pr_err_ratelimited("invalid ndev settings");
+		dev_kfree_skb_any(skb);
+		return 0;
+	}
+
+	if (unlikely(skb->dev != ndev)) {
+		pr_err_ratelimited("invalid skb->dev");
+		dev_kfree_skb_any(skb);
+		return 0;
+	}
+
+	if (!skb->len || skb->len > ETH_FRAME_LEN) {
+		pr_err_ratelimited("%s: invalid skb len %d\n", ndev->name,
+				   skb->len);
+		dev_kfree_skb_any(skb);
+		ndev->stats.tx_dropped++;
+		return 0;
+	}
+
+	return qtnf_bus_data_tx(bus, skb, 0, 0);
+}
+
+static const struct net_device_ops svc_netdev_ops = {
+	.ndo_start_xmit = qtnf_svc_ndev_xmit,
+	.ndo_set_mac_address = eth_mac_addr,
+	.ndo_validate_addr = eth_validate_addr,
+};
+
+static void svc_ndev_setup(struct net_device *dev)
+{
+	dev->netdev_ops = &svc_netdev_ops;
+	dev->needs_free_netdev = true;
+	ether_setup(dev);
+	dev->priv_flags |= IFF_NO_QUEUE;
+	eth_hw_addr_random(dev);
+}
+
 static int qtnf_mac_init_single_band(struct wiphy *wiphy,
 				     struct qtnf_wmac *mac,
 				     enum nl80211_band band)
@@ -736,6 +779,7 @@  static int qtnf_core_netdevice_event(struct notifier_block *nb,
 int qtnf_core_attach(struct qtnf_bus *bus)
 {
 	unsigned int i;
+	void *priv;
 	int ret;
 
 	qtnf_trans_init(bus);
@@ -779,6 +823,38 @@  int qtnf_core_attach(struct qtnf_bus *bus)
 		goto error;
 	}
 
+	if (bus->hw_info.svc_mode) {
+		bus->svc_ndev = alloc_netdev(sizeof(struct qtnf_bus *),
+					     "eth%d", NET_NAME_UNKNOWN,
+					     svc_ndev_setup);
+		if (!bus->svc_ndev) {
+			ret = -ENOMEM;
+			goto error;
+		}
+
+		rtnl_lock();
+		ret = dev_alloc_name(bus->svc_ndev, bus->svc_ndev->name);
+		if (ret < 0) {
+			rtnl_unlock();
+			free_netdev(bus->svc_ndev);
+			bus->svc_ndev = NULL;
+			goto error;
+		}
+
+		priv = netdev_priv(bus->svc_ndev);
+		*((void **)priv) = bus;
+
+		ret = register_netdevice(bus->svc_ndev);
+		if (ret < 0) {
+			rtnl_unlock();
+			free_netdev(bus->svc_ndev);
+			bus->svc_ndev = NULL;
+			goto error;
+		}
+		rtnl_unlock();
+		goto done;
+	}
+
 	if (qtnf_hwcap_is_set(&bus->hw_info, QLINK_HW_CAPAB_HW_BRIDGE) &&
 	    bus->bus_ops->data_tx_use_meta_set)
 		bus->bus_ops->data_tx_use_meta_set(bus, true);
@@ -806,6 +882,7 @@  int qtnf_core_attach(struct qtnf_bus *bus)
 		goto error;
 	}
 
+done:
 	bus->fw_state = QTNF_FW_STATE_RUNNING;
 	return 0;
 
@@ -822,6 +899,9 @@  void qtnf_core_detach(struct qtnf_bus *bus)
 	unregister_netdevice_notifier(&bus->netdev_nb);
 	qtnf_bus_data_rx_stop(bus);
 
+	if (bus->svc_ndev)
+		unregister_netdev(bus->svc_ndev);
+
 	for (macid = 0; macid < QTNF_MAX_MAC; macid++)
 		qtnf_core_mac_detach(bus, macid);
 
@@ -862,6 +942,9 @@  struct net_device *qtnf_classify_skb(struct qtnf_bus *bus, struct sk_buff *skb)
 	if (unlikely(bus->fw_state != QTNF_FW_STATE_RUNNING))
 		return NULL;
 
+	if (unlikely(bus->svc_ndev))
+		return bus->svc_ndev;
+
 	meta = (struct qtnf_frame_meta_info *)
 		(skb_tail_pointer(skb) - sizeof(*meta));
 
diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.h b/drivers/net/wireless/quantenna/qtnfmac/core.h
index 269ce12cf8bf..9745b27379ba 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/core.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/core.h
@@ -116,6 +116,7 @@  struct qtnf_hw_info {
 	u8 num_mac;
 	u8 mac_bitmap;
 	u32 fw_ver;
+	u8 svc_mode;
 	u8 total_tx_chain;
 	u8 total_rx_chain;
 	char fw_version[ETHTOOL_FWVERS_LEN];
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c
index 5337e67092ca..8e025a27a4df 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie.c
@@ -41,6 +41,10 @@  static u8 flashboot = 1;
 module_param(flashboot, byte, 0644);
 MODULE_PARM_DESC(flashboot, "set to 0 to use FW binary file on FS");
 
+static u8 force_svcmode;
+module_param(force_svcmode, byte, 0644);
+MODULE_PARM_DESC(force_svcmode, "set to 1 to force boot into service mode");
+
 static unsigned int fw_blksize_param = QTN_PCIE_MAX_FW_BUFSZ;
 module_param(fw_blksize_param, uint, 0644);
 MODULE_PARM_DESC(fw_blksize_param, "firmware loading block size in bytes");
@@ -342,6 +346,7 @@  static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	pcie_priv->pdev = pdev;
 	pcie_priv->tx_stopped = 0;
 	pcie_priv->flashboot = flashboot;
+	pcie_priv->force_svcmode = force_svcmode;
 
 	if (fw_blksize_param > QTN_PCIE_MAX_FW_BUFSZ)
 		pcie_priv->fw_blksize =  QTN_PCIE_MAX_FW_BUFSZ;
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie_priv.h b/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie_priv.h
index 2a6a928e13bd..93cb651a3236 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie_priv.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/pcie_priv.h
@@ -66,7 +66,8 @@  struct qtnf_pcie_bus_priv {
 
 	u8 msi_enabled;
 	u8 tx_stopped;
-	bool flashboot;
+	u8 flashboot;
+	u8 force_svcmode;
 };
 
 int qtnf_pcie_control_tx(struct qtnf_bus *bus, struct sk_buff *skb);
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
index d1b850aa4657..f3f517733f99 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/pcie/topaz_pcie.c
@@ -866,6 +866,9 @@  static int qtnf_pre_init_ep(struct qtnf_bus *bus)
 	/* notify card about driver type and boot mode */
 	flags = readl(&bda->bda_flags) | QTN_BDA_HOST_QLINK_DRV;
 
+	if (ts->base.force_svcmode)
+		flags |= QTN_BDA_HOST_CALCMD;
+
 	if (ts->base.flashboot)
 		flags |= QTN_BDA_FLASH_BOOT;
 	else
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
index 4d22a54c034f..3abbe0b6e32b 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
@@ -73,6 +73,7 @@  struct qlink_msg_header {
  *	Randomization in probe requests.
  * @QLINK_HW_CAPAB_OBSS_SCAN: device can perform OBSS scanning.
  * @QLINK_HW_CAPAB_HW_BRIDGE: device has hardware switch capabilities.
+ * @QLINK_HW_CAPAB_SVC_MODE: device is in service mode (used for calibration).
  */
 enum qlink_hw_capab {
 	QLINK_HW_CAPAB_REG_UPDATE = 0,
@@ -84,6 +85,7 @@  enum qlink_hw_capab {
 	QLINK_HW_CAPAB_SCAN_DWELL,
 	QLINK_HW_CAPAB_SAE,
 	QLINK_HW_CAPAB_HW_BRIDGE,
+	QLINK_HW_CAPAB_SVC_MODE,
 	QLINK_HW_CAPAB_NUM
 };