All of lore.kernel.org
 help / color / mirror / Atom feed
From: M Chetan Kumar <m.chetan.kumar@intel.com>
To: netdev@vger.kernel.org, linux-wireless@vger.kernel.org
Cc: johannes@sipsolutions.net, krishna.c.sudi@intel.com, linuxwwan@intel.com
Subject: [PATCH V2 15/16] net: iosm: net driver
Date: Tue, 20 Apr 2021 21:43:09 +0530	[thread overview]
Message-ID: <20210420161310.16189-16-m.chetan.kumar@intel.com> (raw)
In-Reply-To: <20210420161310.16189-1-m.chetan.kumar@intel.com>

1) Create net device & implement net operations for data/IP communication.
2) Bind IP Link to mux IP session for simultaneous IP traffic.

Signed-off-by: M Chetan Kumar <m.chetan.kumar@intel.com>
---
v2:
* Removed Ethernet header & VLAN tag handling from wwan net driver.
* Implement rtnet_link interface for IP traffic handling.
---
 drivers/net/wwan/iosm/iosm_ipc_wwan.c | 445 ++++++++++++++++++++++++++
 drivers/net/wwan/iosm/iosm_ipc_wwan.h |  55 ++++
 2 files changed, 500 insertions(+)
 create mode 100644 drivers/net/wwan/iosm/iosm_ipc_wwan.c
 create mode 100644 drivers/net/wwan/iosm/iosm_ipc_wwan.h

diff --git a/drivers/net/wwan/iosm/iosm_ipc_wwan.c b/drivers/net/wwan/iosm/iosm_ipc_wwan.c
new file mode 100644
index 000000000000..cf721bcbddce
--- /dev/null
+++ b/drivers/net/wwan/iosm/iosm_ipc_wwan.c
@@ -0,0 +1,445 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020-21 Intel Corporation.
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_link.h>
+#include <net/rtnetlink.h>
+
+#include "iosm_ipc_chnl_cfg.h"
+#include "iosm_ipc_imem_ops.h"
+#include "iosm_ipc_wwan.h"
+
+#define IOSM_IP_TYPE_MASK 0xF0
+#define IOSM_IP_TYPE_IPV4 0x40
+#define IOSM_IP_TYPE_IPV6 0x60
+
+#define IOSM_IF_ID_PAYLOAD 2
+
+static struct device_type wwan_type = { .name = "wwan" };
+
+static const struct nla_policy iosm_wwan_policy[IFLA_IOSM_MAX + 1] = {
+	[IFLA_IOSM_IF_ID]	= { .type = NLA_U16 },
+};
+
+/**
+ * struct iosm_net_link - This structure includes information about interface dev.
+ * @if_id:	Interface id for device.
+ * @ch_id:	IPC channel number for which interface device is created.
+ * @netdev:	Pointer to network interface device structure
+ * @ipc_wwan:	Pointer to iosm_wwan struct
+ */
+
+struct iosm_net_link {
+	int if_id;
+	int ch_id;
+	struct net_device *netdev;
+	struct iosm_wwan *ipc_wwan;
+};
+
+/**
+ * struct iosm_wwan - This structure contains information about WWAN root device
+ *		     and interface to the IPC layer.
+ * @netdev:		Pointer to network interface device structure.
+ * @sub_netlist:	List of netlink interfaces
+ * @ipc_imem:		Pointer to imem data-struct
+ * @dev:		Pointer device structure
+ * @if_mutex:		Mutex used for add and remove interface id
+ * @is_registered:	Registration status with netdev
+ */
+struct iosm_wwan {
+	struct net_device *netdev;
+	struct iosm_net_link __rcu *sub_netlist[MAX_NET_SESSION];
+	struct iosm_imem *ipc_imem;
+	struct device *dev;
+	struct mutex if_mutex; /* Mutex used for add and remove interface id */
+	u8 is_registered:1;
+};
+
+/* Bring-up the wwan net link */
+static int iosm_wwan_link_open(struct net_device *netdev)
+{
+	struct iosm_net_link *netlink = netdev_priv(netdev);
+	struct iosm_wwan *ipc_wwan = netlink->ipc_wwan;
+	int if_id = netlink->if_id;
+	int ret = -EINVAL;
+
+	if ((if_id < IP_MUX_SESSION_START || if_id > IP_MUX_SESSION_END) &&
+	    (if_id < DSS_CHANNEL_START || if_id > DSS_CHANNEL_END))
+		return ret;
+
+	mutex_lock(&ipc_wwan->if_mutex);
+
+	/* get channel id */
+	netlink->ch_id =
+		imem_sys_wwan_open(ipc_wwan->ipc_imem, if_id);
+
+	if (netlink->ch_id < 0) {
+		dev_err(ipc_wwan->dev,
+			"cannot connect wwan0 & id %d to the IPC mem layer",
+			if_id);
+		ret = -ENODEV;
+		goto out;
+	}
+
+	/* enable tx path, DL data may follow */
+	netif_start_queue(netdev);
+
+	dev_dbg(ipc_wwan->dev, "Channel id %d allocated to if_id %d",
+		netlink->ch_id, netlink->if_id);
+
+	ret = 0;
+out:
+	mutex_unlock(&ipc_wwan->if_mutex);
+	return ret;
+}
+
+/* Bring-down the wwan net link */
+static int iosm_wwan_link_stop(struct net_device *netdev)
+{
+	struct iosm_net_link *netlink = netdev_priv(netdev);
+
+	netif_stop_queue(netdev);
+
+	mutex_lock(&netlink->ipc_wwan->if_mutex);
+	imem_sys_wwan_close(netlink->ipc_wwan->ipc_imem, netlink->if_id,
+			    netlink->ch_id);
+	mutex_unlock(&netlink->ipc_wwan->if_mutex);
+
+	return 0;
+}
+
+/* Transmit a packet */
+static int iosm_wwan_link_transmit(struct sk_buff *skb,
+				   struct net_device *netdev)
+{
+	struct iosm_net_link *netlink = netdev_priv(netdev);
+	struct iosm_wwan *ipc_wwan = netlink->ipc_wwan;
+	int if_id = netlink->if_id;
+	int ret = -EINVAL;
+
+	/* Interface IDs from 1 to 8 are for IP data
+	 * & from 257 to 261 are for non-IP data
+	 */
+	if ((if_id < IP_MUX_SESSION_START || if_id > IP_MUX_SESSION_END) &&
+	    (if_id < DSS_CHANNEL_START || if_id > DSS_CHANNEL_END))
+		goto exit;
+
+	/* Send the SKB to device for transmission */
+	ret = imem_sys_wwan_transmit(ipc_wwan->ipc_imem,
+				     if_id, netlink->ch_id, skb);
+
+	/* Return code of zero is success */
+	if (ret == 0) {
+		ret = NETDEV_TX_OK;
+	} else if (ret == -EBUSY) {
+		ret = NETDEV_TX_BUSY;
+		dev_err(ipc_wwan->dev, "unable to push packets");
+	} else {
+		goto exit;
+	}
+
+	return ret;
+
+exit:
+	/* Log any skb drop */
+	if (if_id)
+		dev_dbg(ipc_wwan->dev, "skb dropped. IF_ID: %d, ret: %d", if_id,
+			ret);
+
+	dev_kfree_skb_any(skb);
+	return ret;
+}
+
+/* Ops structure for wwan net link */
+static const struct net_device_ops iosm_inm_ops = {
+	.ndo_open = iosm_wwan_link_open,
+	.ndo_stop = iosm_wwan_link_stop,
+	.ndo_start_xmit = iosm_wwan_link_transmit,
+};
+
+/* Setup function for creating new net link */
+static void iosm_wwan_setup(struct net_device *iosm_dev)
+{
+	iosm_dev->header_ops = NULL;
+	iosm_dev->hard_header_len = 0;
+	iosm_dev->priv_flags |= IFF_NO_QUEUE;
+
+	iosm_dev->type = ARPHRD_NONE;
+	iosm_dev->min_mtu = ETH_MIN_MTU;
+	iosm_dev->max_mtu = ETH_MAX_MTU;
+
+	iosm_dev->flags = IFF_POINTOPOINT | IFF_NOARP;
+
+	iosm_dev->netdev_ops = &iosm_inm_ops;
+}
+
+static struct device_type inm_type = { .name = "iosmdev" };
+
+/* Create new wwan net link */
+static int iosm_wwan_newlink(struct net *src_net, struct net_device *dev,
+			     struct nlattr *tb[], struct nlattr *data[],
+			     struct netlink_ext_ack *extack)
+{
+	struct iosm_net_link *netlink = netdev_priv(dev);
+	struct iosm_wwan *ipc_wwan;
+	struct net_device *netdev;
+	int err = -EINVAL;
+	int index;
+
+	if (!data[IFLA_IOSM_IF_ID]) {
+		pr_err("Interface ID not specified");
+		goto out;
+	}
+
+	if (!tb[IFLA_LINK]) {
+		pr_err("Link not specified");
+		goto out;
+	}
+
+	netlink->netdev = dev;
+
+	netdev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
+
+	netlink->ipc_wwan = netdev_priv(netdev);
+
+	ipc_wwan = netlink->ipc_wwan;
+
+	if (ipc_wwan->netdev != netdev)
+		goto out;
+
+	netlink->if_id = nla_get_u16(data[IFLA_IOSM_IF_ID]);
+	index = netlink->if_id;
+
+	/* Complete all memory stores before this point */
+	smp_mb();
+	if (index >= DSS_SESSION_START && index <= DSS_SESSION_END)
+		index = (index - DSS_SESSION_START) + IP_MUX_SESSION_END + 1;
+	else if ((index < IP_MUX_SESSION_START || index > IP_MUX_SESSION_END) &&
+		 (index < DSS_SESSION_START || index > DSS_SESSION_END))
+		goto out;
+
+	rcu_read_lock();
+
+	if (rcu_access_pointer(ipc_wwan->sub_netlist[index - 1])) {
+		pr_err("IOSM interface ID already in use");
+		goto out_free_lock;
+	}
+
+	SET_NETDEV_DEVTYPE(dev, &inm_type);
+
+	eth_hw_addr_random(dev);
+	err = register_netdevice(dev);
+	if (err) {
+		dev_err(ipc_wwan->dev, "register netlink failed.\n");
+		goto out_free_lock;
+	}
+
+	err = netdev_upper_dev_link(ipc_wwan->netdev, dev, extack);
+
+	if (err) {
+		dev_err(ipc_wwan->dev, "netdev linking with parent failed.\n");
+		goto netlink_err;
+	}
+
+	rcu_assign_pointer(ipc_wwan->sub_netlist[index - 1], netlink);
+	netif_device_attach(dev);
+	rcu_read_unlock();
+
+	return 0;
+
+netlink_err:
+	unregister_netdevice(dev);
+out_free_lock:
+	rcu_read_unlock();
+out:
+	return err;
+}
+
+/* Delete new wwan net link */
+static void iosm_wwan_dellink(struct net_device *dev, struct list_head *head)
+{
+	struct iosm_net_link *netlink = netdev_priv(dev);
+	u16 index = netlink->if_id;
+
+	netdev_upper_dev_unlink(netlink->ipc_wwan->netdev, dev);
+	unregister_netdevice(dev);
+
+	if (index >= DSS_SESSION_START && index <= DSS_SESSION_END)
+		index = (index - DSS_SESSION_START) + IP_MUX_SESSION_END + 1;
+
+	mutex_lock(&netlink->ipc_wwan->if_mutex);
+	rcu_assign_pointer(netlink->ipc_wwan->sub_netlist[index - 1], NULL);
+	mutex_unlock(&netlink->ipc_wwan->if_mutex);
+}
+
+/* Get size for iosm net link payload*/
+static size_t iosm_wwan_get_size(const struct net_device *dev)
+{
+	return nla_total_size(IOSM_IF_ID_PAYLOAD);
+}
+
+/* Validate the input parameters for wwan net link */
+static int iosm_wwan_validate(struct nlattr *tb[], struct nlattr *data[],
+			      struct netlink_ext_ack *extack)
+{
+	u16 if_id;
+
+	if (!data || !data[IFLA_IOSM_IF_ID]) {
+		NL_SET_ERR_MSG_MOD(extack, "IF ID not specified");
+		return -EINVAL;
+	}
+
+	if_id = nla_get_u16(data[IFLA_IOSM_IF_ID]);
+
+	if ((if_id < IP_MUX_SESSION_START || if_id > IP_MUX_SESSION_END) &&
+	    (if_id < DSS_SESSION_START || if_id > DSS_SESSION_END)) {
+		NL_SET_ERR_MSG_MOD(extack, "Invalid Interface");
+		return -ERANGE;
+	}
+
+	return 0;
+}
+
+/* RT Net link ops structure for new wwan net link */
+struct rtnl_link_ops iosm_netlink __read_mostly = {
+	.kind           = "iosm",
+	.maxtype        = __IFLA_IOSM_MAX,
+	.priv_size      = sizeof(struct iosm_net_link),
+	.setup          = iosm_wwan_setup,
+	.validate       = iosm_wwan_validate,
+	.newlink        = iosm_wwan_newlink,
+	.dellink        = iosm_wwan_dellink,
+	.get_size       = iosm_wwan_get_size,
+	.policy         = iosm_wwan_policy,
+};
+
+int ipc_wwan_receive(struct iosm_wwan *ipc_wwan, struct sk_buff *skb_arg,
+		     bool dss, int if_id)
+{
+	struct sk_buff *skb = skb_arg;
+	struct net_device_stats stats;
+	int ret;
+
+	if ((skb->data[0] & IOSM_IP_TYPE_MASK) == IOSM_IP_TYPE_IPV4)
+		skb->protocol = htons(ETH_P_IP);
+	else if ((skb->data[0] & IOSM_IP_TYPE_MASK) ==
+		 IOSM_IP_TYPE_IPV6)
+		skb->protocol = htons(ETH_P_IPV6);
+
+	skb->pkt_type = PACKET_HOST;
+
+	if (if_id < (NET_SESSION_START - 1) || if_id > (MAX_NET_SESSION - 1)) {
+		dev_kfree_skb(skb);
+		return -EINVAL;
+	}
+
+	rcu_read_lock();
+	skb->dev = rcu_dereference(ipc_wwan->sub_netlist[if_id])->netdev;
+	stats = rcu_dereference(ipc_wwan->sub_netlist[if_id])->netdev->stats;
+	stats.rx_packets++;
+	stats.rx_bytes += skb->len;
+
+	ret = netif_rx(skb);
+	rcu_read_unlock();
+
+	return ret;
+}
+
+void ipc_wwan_tx_flowctrl(struct iosm_wwan *ipc_wwan, int if_id, bool on)
+{
+	struct net_device *netdev;
+	bool is_tx_blk;
+
+	rcu_read_lock();
+	netdev = rcu_dereference(ipc_wwan->sub_netlist[if_id])->netdev;
+
+	is_tx_blk = netif_queue_stopped(netdev);
+
+	if (on)
+		dev_dbg(ipc_wwan->dev, "session id[%d]: flowctrl enable",
+			if_id);
+
+	if (on && !is_tx_blk)
+		netif_stop_queue(netdev);
+	else if (!on && is_tx_blk)
+		netif_wake_queue(netdev);
+	rcu_read_unlock();
+}
+
+static void iosm_netdev_setup(struct net_device *dev) {}
+
+struct iosm_wwan *ipc_wwan_init(struct iosm_imem *ipc_imem, struct device *dev)
+{
+	static const struct net_device_ops iosm_wwandev_ops = {};
+	struct iosm_wwan *ipc_wwan;
+	struct net_device *netdev;
+
+	netdev = alloc_netdev(sizeof(*ipc_wwan), "wwan%d", NET_NAME_ENUM,
+			      iosm_netdev_setup);
+
+	if (!netdev)
+		return NULL;
+
+	ipc_wwan = netdev_priv(netdev);
+
+	ipc_wwan->dev = dev;
+	ipc_wwan->netdev = netdev;
+	ipc_wwan->is_registered = false;
+
+	ipc_wwan->ipc_imem = ipc_imem;
+
+	mutex_init(&ipc_wwan->if_mutex);
+
+	/* allocate random ethernet address */
+	eth_random_addr(netdev->dev_addr);
+	netdev->addr_assign_type = NET_ADDR_RANDOM;
+
+	netdev->netdev_ops = &iosm_wwandev_ops;
+	netdev->flags |= IFF_NOARP;
+
+	SET_NETDEV_DEVTYPE(netdev, &wwan_type);
+
+	if (register_netdev(netdev)) {
+		dev_err(ipc_wwan->dev, "register_netdev failed");
+		goto reg_fail;
+	}
+
+	ipc_wwan->is_registered = true;
+
+	netif_device_attach(netdev);
+
+	return ipc_wwan;
+
+reg_fail:
+	free_netdev(netdev);
+	return NULL;
+}
+
+void ipc_wwan_deinit(struct iosm_wwan *ipc_wwan)
+{
+	struct iosm_net_link *netlink;
+	int i;
+
+	if (ipc_wwan->is_registered) {
+		rcu_read_lock();
+		for (i = IP_MUX_SESSION_START; i <= IP_DSS_SESSION_END; i++) {
+			if (rcu_access_pointer(ipc_wwan->sub_netlist[i - 1])) {
+				netlink =
+				rcu_dereference(ipc_wwan->sub_netlist[i - 1]);
+				rtnl_lock();
+				netdev_upper_dev_unlink(ipc_wwan->netdev,
+							netlink->netdev);
+				unregister_netdevice(netlink->netdev);
+				rtnl_unlock();
+				rcu_assign_pointer(ipc_wwan->sub_netlist[i - 1], NULL);
+			}
+		}
+		rcu_read_unlock();
+
+		unregister_netdev(ipc_wwan->netdev);
+		free_netdev(ipc_wwan->netdev);
+	}
+}
diff --git a/drivers/net/wwan/iosm/iosm_ipc_wwan.h b/drivers/net/wwan/iosm/iosm_ipc_wwan.h
new file mode 100644
index 000000000000..4925f22dff0a
--- /dev/null
+++ b/drivers/net/wwan/iosm/iosm_ipc_wwan.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2020-21 Intel Corporation.
+ */
+
+#ifndef IOSM_IPC_WWAN_H
+#define IOSM_IPC_WWAN_H
+
+/**
+ * ipc_wwan_init - Allocate, Init and register WWAN device
+ * @ipc_imem:		Pointer to imem data-struct
+ * @dev:		Pointer to device structure
+ *
+ * Returns: Pointer to instance on success else NULL
+ */
+struct iosm_wwan *ipc_wwan_init(struct iosm_imem *ipc_imem, struct device *dev);
+
+/**
+ * ipc_wwan_deinit - Unregister and free WWAN device, clear pointer
+ * @ipc_wwan:	Pointer to wwan instance data
+ */
+void ipc_wwan_deinit(struct iosm_wwan *ipc_wwan);
+
+/**
+ * ipc_wwan_receive - Receive a downlink packet from CP.
+ * @ipc_wwan:	Pointer to wwan instance
+ * @skb_arg:	Pointer to struct sk_buff
+ * @dss:	Set to true if interafce id is from 257 to 261,
+ *		else false
+ * @if_id:	Interface ID
+ *
+ * Return: 0 on success and failure value on error
+ */
+int ipc_wwan_receive(struct iosm_wwan *ipc_wwan, struct sk_buff *skb_arg,
+		     bool dss, int if_id);
+
+/**
+ * ipc_wwan_tx_flowctrl - Enable/Disable TX flow control
+ * @ipc_wwan:	Pointer to wwan instance
+ * @id:		Ipc mux channel session id
+ * @on:		if true then flow ctrl would be enabled else disable
+ *
+ */
+void ipc_wwan_tx_flowctrl(struct iosm_wwan *ipc_wwan, int id, bool on);
+
+/**
+ * ipc_wwan_is_tx_stopped - Checks if Tx stopped for a Interface id.
+ * @ipc_wwan:	Pointer to wwan instance
+ * @id:		Ipc mux channel session id
+ *
+ * Return: true if stopped, false otherwise
+ */
+bool ipc_wwan_is_tx_stopped(struct iosm_wwan *ipc_wwan, int id);
+
+#endif
-- 
2.25.1


  parent reply	other threads:[~2021-04-20 16:15 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-04-20 16:12 [PATCH V2 00/16] net: iosm: PCIe Driver for Intel M.2 Modem M Chetan Kumar
2021-04-20 16:12 ` [PATCH V2 01/16] net: iosm: entry point M Chetan Kumar
2021-04-21  5:44   ` Leon Romanovsky
2021-04-21  6:51     ` Johannes Berg
2021-04-21  8:03       ` Leon Romanovsky
2021-04-20 16:12 ` [PATCH V2 02/16] net: iosm: irq handling M Chetan Kumar
2021-04-20 23:48   ` David Miller
2021-04-20 16:12 ` [PATCH V2 03/16] net: iosm: mmio scratchpad M Chetan Kumar
2021-04-20 16:12 ` [PATCH V2 04/16] net: iosm: shared memory IPC interface M Chetan Kumar
2021-04-20 16:12 ` [PATCH V2 05/16] net: iosm: shared memory I/O operations M Chetan Kumar
2021-04-20 16:13 ` [PATCH V2 06/16] net: iosm: channel configuration M Chetan Kumar
2021-04-20 16:13 ` [PATCH V2 07/16] net: iosm: mbim control device M Chetan Kumar
2021-05-06  9:25   ` Loic Poulain
2021-05-06 10:44     ` Kumar, M Chetan
2021-05-06 14:09       ` Dan Williams
2021-04-20 16:13 ` [PATCH V2 08/16] net: iosm: bottom half M Chetan Kumar
2021-04-20 16:13 ` [PATCH V2 09/16] net: iosm: multiplex IP sessions M Chetan Kumar
2021-04-20 16:13 ` [PATCH V2 10/16] net: iosm: encode or decode datagram M Chetan Kumar
2021-04-20 16:13 ` [PATCH V2 11/16] net: iosm: power management M Chetan Kumar
2021-04-20 16:13 ` [PATCH V2 12/16] net: iosm: shared memory protocol M Chetan Kumar
2021-04-20 16:13 ` [PATCH V2 13/16] net: iosm: protocol operations M Chetan Kumar
2021-04-20 16:13 ` [PATCH V2 14/16] net: iosm: uevent support M Chetan Kumar
2021-04-20 16:13 ` M Chetan Kumar [this message]
2021-04-20 16:13 ` [PATCH V2 16/16] net: iosm: infrastructure M Chetan Kumar
2021-04-20 18:08   ` Randy Dunlap
2021-05-06  8:54 ` [PATCH V2 00/16] net: iosm: PCIe Driver for Intel M.2 Modem Aleksander Morgado
2021-05-06 10:51   ` Kumar, M Chetan

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210420161310.16189-16-m.chetan.kumar@intel.com \
    --to=m.chetan.kumar@intel.com \
    --cc=johannes@sipsolutions.net \
    --cc=krishna.c.sudi@intel.com \
    --cc=linux-wireless@vger.kernel.org \
    --cc=linuxwwan@intel.com \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.