@@ -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 *));
@@ -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;
}
@@ -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));
@@ -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];
@@ -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;
@@ -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);
@@ -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
@@ -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
};
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(-)