Search Linux Wireless

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

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

 



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@xxxxxxxxxxxxx>

---

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(-)

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
 };
 
-- 
2.11.0




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

  Powered by Linux