Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/807375/?format=api
{ "id": 807375, "url": "http://patchwork.ozlabs.org/api/patches/807375/?format=api", "web_url": "http://patchwork.ozlabs.org/project/netdev/patch/1504054078-10173-4-git-send-email-subashab@codeaurora.org/", "project": { "id": 7, "url": "http://patchwork.ozlabs.org/api/projects/7/?format=api", "name": "Linux network development", "link_name": "netdev", "list_id": "netdev.vger.kernel.org", "list_email": "netdev@vger.kernel.org", "web_url": null, "scm_url": null, "webscm_url": null, "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<1504054078-10173-4-git-send-email-subashab@codeaurora.org>", "list_archive_url": null, "date": "2017-08-30T00:47:58", "name": "[net-next,3/3,v10] drivers: net: ethernet: qualcomm: rmnet: Initial implementation", "commit_ref": null, "pull_url": null, "state": "changes-requested", "archived": true, "hash": "2faf242a2474e498c36c4c34078412127fa8cfb1", "submitter": { "id": 65547, "url": "http://patchwork.ozlabs.org/api/people/65547/?format=api", "name": "Subash Abhinov Kasiviswanathan", "email": "subashab@codeaurora.org" }, "delegate": { "id": 34, "url": "http://patchwork.ozlabs.org/api/users/34/?format=api", "username": "davem", "first_name": "David", "last_name": "Miller", "email": "davem@davemloft.net" }, "mbox": "http://patchwork.ozlabs.org/project/netdev/patch/1504054078-10173-4-git-send-email-subashab@codeaurora.org/mbox/", "series": [ { "id": 511, "url": "http://patchwork.ozlabs.org/api/series/511/?format=api", "web_url": "http://patchwork.ozlabs.org/project/netdev/list/?series=511", "date": "2017-08-30T00:47:55", "name": "Add support for rmnet driver", "version": 10, "mbox": "http://patchwork.ozlabs.org/series/511/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/807375/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/807375/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<netdev-owner@vger.kernel.org>", "X-Original-To": "patchwork-incoming@ozlabs.org", "Delivered-To": "patchwork-incoming@ozlabs.org", "Authentication-Results": [ "ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=netdev-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)", "ozlabs.org; dkim=pass (1024-bit key;\n\tunprotected) header.d=codeaurora.org header.i=@codeaurora.org\n\theader.b=\"F/xoJvVc\"; \n\tdkim=pass (1024-bit key) header.d=codeaurora.org\n\theader.i=@codeaurora.org header.b=\"BoNCSgL3\"; \n\tdkim-atps=neutral", "pdx-caf-mail.web.codeaurora.org;\n\tdmarc=none (p=none dis=none)\n\theader.from=codeaurora.org", "pdx-caf-mail.web.codeaurora.org;\n\tspf=none smtp.mailfrom=subashab@codeaurora.org" ], "Received": [ "from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3xhn342bmSz9s9Y\n\tfor <patchwork-incoming@ozlabs.org>;\n\tWed, 30 Aug 2017 10:49:52 +1000 (AEST)", "(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1752033AbdH3Atu (ORCPT <rfc822;patchwork-incoming@ozlabs.org>);\n\tTue, 29 Aug 2017 20:49:50 -0400", "from smtp.codeaurora.org ([198.145.29.96]:46010 \"EHLO\n\tsmtp.codeaurora.org\" rhost-flags-OK-OK-OK-OK) by vger.kernel.org\n\twith ESMTP id S1751449AbdH3Atr (ORCPT\n\t<rfc822;netdev@vger.kernel.org>); Tue, 29 Aug 2017 20:49:47 -0400", "by smtp.codeaurora.org (Postfix, from userid 1000)\n\tid 242A0607B6; Wed, 30 Aug 2017 00:49:47 +0000 (UTC)", "from subashab-lnx.qualcomm.com (unknown [129.46.15.92])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits))\n\t(No client certificate requested)\n\t(Authenticated sender: subashab@codeaurora.org)\n\tby smtp.codeaurora.org (Postfix) with ESMTPSA id 6FDBB6079E;\n\tWed, 30 Aug 2017 00:49:40 +0000 (UTC)" ], "DKIM-Signature": [ "v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org;\n\ts=default; t=1504054187;\n\tbh=V7fBPmZeowpudabKy7IY3kLuUDRA5Sk4iQf6zJvDjIk=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=F/xoJvVceaEVPS0KEkSy6ieNNHt7jXhWrO1MjKP78NAyqZSAMR1XtxmTrk9461ArP\n\teC1pQlRewgAQ3oLp6CNkXCCTJF08Z6Jc43TT1ZJIc53tOPnhuEN8VO7EZasC4Uq2xa\n\tm95wUYpAQ8YbaUVndx6omO2YSyYfQmAxMx545UjI=", "v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org;\n\ts=default; t=1504054181;\n\tbh=V7fBPmZeowpudabKy7IY3kLuUDRA5Sk4iQf6zJvDjIk=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=BoNCSgL30DqVZlpt3hcjZRa5LUC+BB6KXWxpUp+lpuuse/ZabPkPFQTfRnIC9uAZl\n\tiYvp+xZ/DdKevOadHP0F2vv3202kgp0ubDipSXsU/HDjxBkkBx2mD1UQWaJN+g8uKj\n\tEF8TaobmiObbwOdwP/R254EwK5JZVQaI1ux1YgHQ=" ], "X-Spam-Checker-Version": "SpamAssassin 3.4.0 (2014-02-07) on\n\tpdx-caf-mail.web.codeaurora.org", "X-Spam-Level": "", "X-Spam-Status": "No, score=-2.8 required=2.0 tests=ALL_TRUSTED,BAYES_00,\n\tDKIM_SIGNED,\n\tT_DKIM_INVALID autolearn=no autolearn_force=no version=3.4.0", "DMARC-Filter": "OpenDMARC Filter v1.3.2 smtp.codeaurora.org 6FDBB6079E", "From": "Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>", "To": "netdev@vger.kernel.org, davem@davemloft.net,\n\tfengguang.wu@intel.com, dcbw@redhat.com, jiri@resnulli.us,\n\tstephen@networkplumber.org, David.Laight@ACULAB.COM,\n\tmarcel@holtmann.org, andrew@lunn.ch", "Cc": "Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>", "Subject": "[PATCH net-next 3/3 v10] drivers: net: ethernet: qualcomm: rmnet:\n\tInitial implementation", "Date": "Tue, 29 Aug 2017 18:47:58 -0600", "Message-Id": "<1504054078-10173-4-git-send-email-subashab@codeaurora.org>", "X-Mailer": "git-send-email 1.9.1", "In-Reply-To": "<1504054078-10173-1-git-send-email-subashab@codeaurora.org>", "References": "<1504054078-10173-1-git-send-email-subashab@codeaurora.org>", "Sender": "netdev-owner@vger.kernel.org", "Precedence": "bulk", "List-ID": "<netdev.vger.kernel.org>", "X-Mailing-List": "netdev@vger.kernel.org" }, "content": "RmNet driver provides a transport agnostic MAP (multiplexing and\naggregation protocol) support in embedded module. Module provides\nvirtual network devices which can be attached to any IP-mode\nphysical device. This will be used to provide all MAP functionality\non future hardware in a single consistent location.\n\nSigned-off-by: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>\n---\n Documentation/networking/rmnet.txt | 82 ++++\n drivers/net/ethernet/qualcomm/Kconfig | 2 +\n drivers/net/ethernet/qualcomm/Makefile | 2 +\n drivers/net/ethernet/qualcomm/rmnet/Kconfig | 12 +\n drivers/net/ethernet/qualcomm/rmnet/Makefile | 10 +\n drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c | 419 +++++++++++++++++++++\n drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h | 56 +++\n .../net/ethernet/qualcomm/rmnet/rmnet_handlers.c | 271 +++++++++++++\n .../net/ethernet/qualcomm/rmnet/rmnet_handlers.h | 26 ++\n drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h | 88 +++++\n .../ethernet/qualcomm/rmnet/rmnet_map_command.c | 107 ++++++\n .../net/ethernet/qualcomm/rmnet/rmnet_map_data.c | 105 ++++++\n .../net/ethernet/qualcomm/rmnet/rmnet_private.h | 45 +++\n drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c | 170 +++++++++\n drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h | 29 ++\n 15 files changed, 1424 insertions(+)\n create mode 100644 Documentation/networking/rmnet.txt\n create mode 100644 drivers/net/ethernet/qualcomm/rmnet/Kconfig\n create mode 100644 drivers/net/ethernet/qualcomm/rmnet/Makefile\n create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c\n create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h\n create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c\n create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h\n create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h\n create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c\n create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c\n create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h\n create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c\n create mode 100644 drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h", "diff": "diff --git a/Documentation/networking/rmnet.txt b/Documentation/networking/rmnet.txt\nnew file mode 100644\nindex 0000000..6b341ea\n--- /dev/null\n+++ b/Documentation/networking/rmnet.txt\n@@ -0,0 +1,82 @@\n+1. Introduction\n+\n+rmnet driver is used for supporting the Multiplexing and aggregation\n+Protocol (MAP). This protocol is used by all recent chipsets using Qualcomm\n+Technologies, Inc. modems.\n+\n+This driver can be used to register onto any physical network device in\n+IP mode. Physical transports include USB, HSIC, PCIe and IP accelerator.\n+\n+Multiplexing allows for creation of logical netdevices (rmnet devices) to\n+handle multiple private data networks (PDN) like a default internet, tethering,\n+multimedia messaging service (MMS) or IP media subsystem (IMS). Hardware sends\n+packets with MAP headers to rmnet. Based on the multiplexer id, rmnet\n+routes to the appropriate PDN after removing the MAP header.\n+\n+Aggregation is required to achieve high data rates. This involves hardware\n+sending aggregated bunch of MAP frames. rmnet driver will de-aggregate\n+these MAP frames and send them to appropriate PDN's.\n+\n+2. Packet format\n+\n+a. MAP packet (data / control)\n+\n+MAP header has the same endianness of the IP packet.\n+\n+Packet format -\n+\n+Bit 0 1 2-7 8 - 15 16 - 31\n+Function Command / Data Reserved Pad Multiplexer ID Payload length\n+Bit 32 - x\n+Function Raw Bytes\n+\n+Command (1)/ Data (0) bit value is to indicate if the packet is a MAP command\n+or data packet. Control packet is used for transport level flow control. Data\n+packets are standard IP packets.\n+\n+Reserved bits are usually zeroed out and to be ignored by receiver.\n+\n+Padding is number of bytes to be added for 4 byte alignment if required by\n+hardware.\n+\n+Multiplexer ID is to indicate the PDN on which data has to be sent.\n+\n+Payload length includes the padding length but does not include MAP header\n+length.\n+\n+b. MAP packet (command specific)\n+\n+Bit 0 1 2-7 8 - 15 16 - 31\n+Function Command Reserved Pad Multiplexer ID Payload length\n+Bit 32 - 39 40 - 45 46 - 47 48 - 63\n+Function Command name Reserved Command Type Reserved\n+Bit 64 - 95\n+Function Transaction ID\n+Bit 96 - 127\n+Function Command data\n+\n+Command 1 indicates disabling flow while 2 is enabling flow\n+\n+Command types -\n+0 for MAP command request\n+1 is to acknowledge the receipt of a command\n+2 is for unsupported commands\n+3 is for error during processing of commands\n+\n+c. Aggregation\n+\n+Aggregation is multiple MAP packets (can be data or command) delivered to\n+rmnet in a single linear skb. rmnet will process the individual\n+packets and either ACK the MAP command or deliver the IP packet to the\n+network stack as needed\n+\n+MAP header|IP Packet|Optional padding|MAP header|IP Packet|Optional padding....\n+MAP header|IP Packet|Optional padding|MAP header|Command Packet|Optional pad...\n+\n+3. Userspace configuration\n+\n+rmnet userspace configuration is done through netlink library librmnetctl\n+and command line utility rmnetcli. Utility is hosted in codeaurora forum git.\n+The driver uses rtnl_link_ops for communication.\n+\n+https://source.codeaurora.org/quic/la/platform/vendor/qcom-opensource/dataservices/tree/rmnetctl\ndiff --git a/drivers/net/ethernet/qualcomm/Kconfig b/drivers/net/ethernet/qualcomm/Kconfig\nindex 877675a..f520071 100644\n--- a/drivers/net/ethernet/qualcomm/Kconfig\n+++ b/drivers/net/ethernet/qualcomm/Kconfig\n@@ -59,4 +59,6 @@ config QCOM_EMAC\n \t low power, Receive-Side Scaling (RSS), and IEEE 1588-2008\n \t Precision Clock Synchronization Protocol.\n \n+source \"drivers/net/ethernet/qualcomm/rmnet/Kconfig\"\n+\n endif # NET_VENDOR_QUALCOMM\ndiff --git a/drivers/net/ethernet/qualcomm/Makefile b/drivers/net/ethernet/qualcomm/Makefile\nindex 92fa7c4..1847350 100644\n--- a/drivers/net/ethernet/qualcomm/Makefile\n+++ b/drivers/net/ethernet/qualcomm/Makefile\n@@ -9,3 +9,5 @@ obj-$(CONFIG_QCA7000_UART) += qcauart.o\n qcauart-objs := qca_uart.o\n \n obj-y += emac/\n+\n+obj-$(CONFIG_RMNET) += rmnet/\ndiff --git a/drivers/net/ethernet/qualcomm/rmnet/Kconfig b/drivers/net/ethernet/qualcomm/rmnet/Kconfig\nnew file mode 100644\nindex 0000000..4948f14\n--- /dev/null\n+++ b/drivers/net/ethernet/qualcomm/rmnet/Kconfig\n@@ -0,0 +1,12 @@\n+#\n+# RMNET MAP driver\n+#\n+\n+menuconfig RMNET\n+\tdepends on NETDEVICES\n+\tbool \"RmNet MAP driver\"\n+\tdefault n\n+\t---help---\n+\t If you say Y here, then the rmnet module will be statically\n+\t compiled into the kernel. The rmnet module provides MAP\n+\t functionality for embedded and bridged traffic.\ndiff --git a/drivers/net/ethernet/qualcomm/rmnet/Makefile b/drivers/net/ethernet/qualcomm/rmnet/Makefile\nnew file mode 100644\nindex 0000000..01bddf2\n--- /dev/null\n+++ b/drivers/net/ethernet/qualcomm/rmnet/Makefile\n@@ -0,0 +1,10 @@\n+#\n+# Makefile for the RMNET module\n+#\n+\n+rmnet-y\t\t := rmnet_config.o\n+rmnet-y\t\t += rmnet_vnd.o\n+rmnet-y\t\t += rmnet_handlers.o\n+rmnet-y\t\t += rmnet_map_data.o\n+rmnet-y\t\t += rmnet_map_command.o\n+obj-$(CONFIG_RMNET) += rmnet.o\ndiff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c\nnew file mode 100644\nindex 0000000..e836d26\n--- /dev/null\n+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c\n@@ -0,0 +1,419 @@\n+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.\n+ *\n+ * This program is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License version 2 and\n+ * only version 2 as published by the Free Software Foundation.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n+ * GNU General Public License for more details.\n+ *\n+ * RMNET configuration engine\n+ *\n+ */\n+\n+#include <net/sock.h>\n+#include <linux/module.h>\n+#include <linux/netlink.h>\n+#include <linux/netdevice.h>\n+#include \"rmnet_config.h\"\n+#include \"rmnet_handlers.h\"\n+#include \"rmnet_vnd.h\"\n+#include \"rmnet_private.h\"\n+\n+/* Locking scheme -\n+ * The shared resource which needs to be protected is realdev->rx_handler_data.\n+ * For the writer path, this is using rtnl_lock(). The writer paths are\n+ * rmnet_newlink(), rmnet_dellink() and rmnet_force_unassociate_device(). These\n+ * paths are already called with rtnl_lock() acquired in. There is also an\n+ * ASSERT_RTNL() to ensure that we are calling with rtnl acquired. For\n+ * dereference here, we will need to use rtnl_dereference(). Dev list writing\n+ * needs to happen with rtnl_lock() acquired for netdev_master_upper_dev_link().\n+ * For the reader path, the real_dev->rx_handler_data is called in the TX / RX\n+ * path. We only need rcu_read_lock() for these scenarios. In these cases,\n+ * the rcu_read_lock() is held in __dev_queue_xmit() and\n+ * netif_receive_skb_internal(), so readers need to use rcu_dereference_rtnl()\n+ * to get the relevant information. For dev list reading, we again acquire\n+ * rcu_read_lock() in rmnet_dellink() for netdev_master_upper_dev_get_rcu().\n+ * We also use unregister_netdevice_many() to free all rmnet devices in\n+ * rmnet_force_unassociate_device() so we dont lose the rtnl_lock() and free in\n+ * same context.\n+ */\n+\n+/* Local Definitions and Declarations */\n+#define RMNET_LOCAL_LOGICAL_ENDPOINT -1\n+\n+struct rmnet_walk_data {\n+\tstruct net_device *real_dev;\n+\tstruct list_head *head;\n+\tstruct rmnet_real_dev_info *real_dev_info;\n+};\n+\n+static int rmnet_is_real_dev_registered(const struct net_device *real_dev)\n+{\n+\trx_handler_func_t *rx_handler;\n+\n+\trx_handler = rcu_dereference(real_dev->rx_handler);\n+\treturn (rx_handler == rmnet_rx_handler);\n+}\n+\n+/* Needs either rcu_read_lock() or rtnl lock */\n+static struct rmnet_real_dev_info*\n+__rmnet_get_real_dev_info(const struct net_device *real_dev)\n+{\n+\tif (rmnet_is_real_dev_registered(real_dev))\n+\t\treturn rcu_dereference_rtnl(real_dev->rx_handler_data);\n+\telse\n+\t\treturn NULL;\n+}\n+\n+/* Needs rtnl lock */\n+static struct rmnet_real_dev_info*\n+rmnet_get_real_dev_info_rtnl(const struct net_device *real_dev)\n+{\n+\treturn rtnl_dereference(real_dev->rx_handler_data);\n+}\n+\n+static struct rmnet_endpoint*\n+rmnet_get_endpoint(struct net_device *dev, int config_id)\n+{\n+\tstruct rmnet_real_dev_info *r;\n+\tstruct rmnet_endpoint *ep;\n+\n+\tif (!rmnet_is_real_dev_registered(dev)) {\n+\t\tep = rmnet_vnd_get_endpoint(dev);\n+\t} else {\n+\t\tr = __rmnet_get_real_dev_info(dev);\n+\n+\t\tif (!r)\n+\t\t\treturn NULL;\n+\n+\t\tif (config_id == RMNET_LOCAL_LOGICAL_ENDPOINT)\n+\t\t\tep = &r->local_ep;\n+\t\telse\n+\t\t\tep = &r->muxed_ep[config_id];\n+\t}\n+\n+\treturn ep;\n+}\n+\n+static int rmnet_unregister_real_device(struct net_device *real_dev,\n+\t\t\t\t\tstruct rmnet_real_dev_info *r)\n+{\n+\tif (r->nr_rmnet_devs)\n+\t\treturn -EINVAL;\n+\n+\tkfree(r);\n+\n+\tnetdev_rx_handler_unregister(real_dev);\n+\n+\t/* release reference on real_dev */\n+\tdev_put(real_dev);\n+\n+\tnetdev_dbg(real_dev, \"Removed from rmnet\\n\");\n+\treturn 0;\n+}\n+\n+static int rmnet_register_real_device(struct net_device *real_dev)\n+{\n+\tstruct rmnet_real_dev_info *r;\n+\tint rc;\n+\n+\tASSERT_RTNL();\n+\n+\tif (rmnet_is_real_dev_registered(real_dev))\n+\t\treturn 0;\n+\n+\tr = kzalloc(sizeof(*r), GFP_ATOMIC);\n+\tif (!r)\n+\t\treturn -ENOMEM;\n+\n+\tr->dev = real_dev;\n+\trc = netdev_rx_handler_register(real_dev, rmnet_rx_handler, r);\n+\tif (rc) {\n+\t\tkfree(r);\n+\t\treturn -EBUSY;\n+\t}\n+\n+\t/* hold on to real dev for MAP data */\n+\tdev_hold(real_dev);\n+\n+\tnetdev_dbg(real_dev, \"registered with rmnet\\n\");\n+\treturn 0;\n+}\n+\n+static int rmnet_set_ingress_data_format(struct net_device *dev, u32 idf)\n+{\n+\tstruct rmnet_real_dev_info *r;\n+\n+\tnetdev_dbg(dev, \"Ingress format 0x%08X\\n\", idf);\n+\n+\tr = __rmnet_get_real_dev_info(dev);\n+\n+\tr->ingress_data_format = idf;\n+\n+\treturn 0;\n+}\n+\n+static int rmnet_set_egress_data_format(struct net_device *dev, u32 edf,\n+\t\t\t\t\tu16 agg_size, u16 agg_count)\n+{\n+\tstruct rmnet_real_dev_info *r;\n+\n+\tnetdev_dbg(dev, \"Egress format 0x%08X agg size %d cnt %d\\n\",\n+\t\t edf, agg_size, agg_count);\n+\n+\tr = __rmnet_get_real_dev_info(dev);\n+\n+\tr->egress_data_format = edf;\n+\n+\treturn 0;\n+}\n+\n+static int __rmnet_set_endpoint_config(struct net_device *dev, int config_id,\n+\t\t\t\t struct rmnet_endpoint *ep)\n+{\n+\tstruct rmnet_endpoint *dev_ep;\n+\n+\tdev_ep = rmnet_get_endpoint(dev, config_id);\n+\n+\tif (!dev_ep)\n+\t\treturn -EINVAL;\n+\n+\tmemcpy(dev_ep, ep, sizeof(struct rmnet_endpoint));\n+\tif (config_id == RMNET_LOCAL_LOGICAL_ENDPOINT)\n+\t\tdev_ep->mux_id = 0;\n+\telse\n+\t\tdev_ep->mux_id = config_id;\n+\n+\treturn 0;\n+}\n+\n+static int rmnet_set_endpoint_config(struct net_device *dev,\n+\t\t\t\t int config_id, u8 rmnet_mode,\n+\t\t\t\t struct net_device *egress_dev)\n+{\n+\tstruct rmnet_endpoint ep;\n+\n+\tnetdev_dbg(dev, \"id %d mode %d dev %s\\n\",\n+\t\t config_id, rmnet_mode, egress_dev->name);\n+\n+\tif (config_id < RMNET_LOCAL_LOGICAL_ENDPOINT ||\n+\t config_id >= RMNET_MAX_LOGICAL_EP)\n+\t\treturn -EINVAL;\n+\n+\t/* This config is cleared on every set, so its ok to not\n+\t * clear it on a device delete.\n+\t */\n+\tmemset(&ep, 0, sizeof(struct rmnet_endpoint));\n+\tep.rmnet_mode = rmnet_mode;\n+\tep.egress_dev = egress_dev;\n+\n+\treturn __rmnet_set_endpoint_config(dev, config_id, &ep);\n+}\n+\n+static int rmnet_newlink(struct net *src_net, struct net_device *dev,\n+\t\t\t struct nlattr *tb[], struct nlattr *data[],\n+\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tint ingress_format = RMNET_INGRESS_FORMAT_DEMUXING |\n+\t\t\t RMNET_INGRESS_FORMAT_DEAGGREGATION |\n+\t\t\t RMNET_INGRESS_FORMAT_MAP;\n+\tint egress_format = RMNET_EGRESS_FORMAT_MUXING |\n+\t\t\t RMNET_EGRESS_FORMAT_MAP;\n+\tstruct rmnet_real_dev_info *r;\n+\tstruct net_device *real_dev;\n+\tint mode = RMNET_EPMODE_VND;\n+\tint err = 0;\n+\tu16 mux_id;\n+\n+\treal_dev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));\n+\tif (!real_dev || !dev)\n+\t\treturn -ENODEV;\n+\n+\tif (!data[IFLA_VLAN_ID])\n+\t\treturn -EINVAL;\n+\n+\tmux_id = nla_get_u16(data[IFLA_VLAN_ID]);\n+\n+\terr = rmnet_register_real_device(real_dev);\n+\tif (err)\n+\t\tgoto err0;\n+\n+\tr = rmnet_get_real_dev_info_rtnl(real_dev);\n+\terr = rmnet_vnd_newlink(mux_id, dev, r);\n+\tif (err)\n+\t\tgoto err1;\n+\n+\terr = netdev_master_upper_dev_link(dev, real_dev, NULL, NULL);\n+\tif (err)\n+\t\tgoto err2;\n+\n+\trmnet_vnd_set_mux(dev, mux_id);\n+\trmnet_set_egress_data_format(real_dev, egress_format, 0, 0);\n+\trmnet_set_ingress_data_format(real_dev, ingress_format);\n+\trmnet_set_endpoint_config(real_dev, mux_id, mode, dev);\n+\trmnet_set_endpoint_config(dev, mux_id, mode, real_dev);\n+\treturn 0;\n+\n+err2:\n+\trmnet_vnd_dellink(mux_id, r);\n+err1:\n+\trmnet_unregister_real_device(real_dev, r);\n+err0:\n+\treturn err;\n+}\n+\n+static void rmnet_dellink(struct net_device *dev, struct list_head *head)\n+{\n+\tstruct rmnet_real_dev_info *r;\n+\tstruct net_device *real_dev;\n+\tu8 mux_id;\n+\n+\trcu_read_lock();\n+\treal_dev = netdev_master_upper_dev_get_rcu(dev);\n+\trcu_read_unlock();\n+\n+\tif (!real_dev || !rmnet_is_real_dev_registered(real_dev))\n+\t\treturn;\n+\n+\tr = rmnet_get_real_dev_info_rtnl(real_dev);\n+\n+\tmux_id = rmnet_vnd_get_mux(dev);\n+\trmnet_vnd_dellink(mux_id, r);\n+\tnetdev_upper_dev_unlink(dev, real_dev);\n+\trmnet_unregister_real_device(real_dev, r);\n+\n+\tunregister_netdevice_queue(dev, head);\n+}\n+\n+static int rmnet_dev_walk_unreg(struct net_device *rmnet_dev, void *data)\n+{\n+\tstruct rmnet_walk_data *d = data;\n+\tu8 mux_id;\n+\n+\tmux_id = rmnet_vnd_get_mux(rmnet_dev);\n+\n+\trmnet_vnd_dellink(mux_id, d->real_dev_info);\n+\tnetdev_upper_dev_unlink(rmnet_dev, d->real_dev);\n+\tunregister_netdevice_queue(rmnet_dev, d->head);\n+\n+\treturn 0;\n+}\n+\n+static void rmnet_force_unassociate_device(struct net_device *dev)\n+{\n+\tstruct net_device *real_dev = dev;\n+\tstruct rmnet_real_dev_info *r;\n+\tstruct rmnet_walk_data d;\n+\tLIST_HEAD(list);\n+\n+\tif (!rmnet_is_real_dev_registered(real_dev))\n+\t\treturn;\n+\n+\tASSERT_RTNL();\n+\n+\td.real_dev = real_dev;\n+\td.head = &list;\n+\n+\tr = rmnet_get_real_dev_info_rtnl(dev);\n+\td.real_dev_info = r;\n+\n+\trcu_read_lock();\n+\tnetdev_walk_all_lower_dev_rcu(real_dev, rmnet_dev_walk_unreg, &d);\n+\trcu_read_unlock();\n+\tunregister_netdevice_many(&list);\n+\n+\trmnet_unregister_real_device(real_dev, r);\n+}\n+\n+static int rmnet_config_notify_cb(struct notifier_block *nb,\n+\t\t\t\t unsigned long event, void *data)\n+{\n+\tstruct net_device *dev = netdev_notifier_info_to_dev(data);\n+\n+\tif (!dev)\n+\t\treturn NOTIFY_DONE;\n+\n+\tswitch (event) {\n+\tcase NETDEV_UNREGISTER:\n+\t\tnetdev_dbg(dev, \"Kernel unregister\\n\");\n+\t\trmnet_force_unassociate_device(dev);\n+\t\tbreak;\n+\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\n+\treturn NOTIFY_DONE;\n+}\n+\n+static struct notifier_block rmnet_dev_notifier __read_mostly = {\n+\t.notifier_call = rmnet_config_notify_cb,\n+};\n+\n+static int rmnet_rtnl_validate(struct nlattr *tb[], struct nlattr *data[],\n+\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tu16 mux_id;\n+\n+\tif (!data || !data[IFLA_VLAN_ID])\n+\t\treturn -EINVAL;\n+\n+\tmux_id = nla_get_u16(data[IFLA_VLAN_ID]);\n+\tif (mux_id > (RMNET_MAX_LOGICAL_EP - 1))\n+\t\treturn -ERANGE;\n+\n+\treturn 0;\n+}\n+\n+static size_t rmnet_get_size(const struct net_device *dev)\n+{\n+\treturn nla_total_size(2); /* IFLA_VLAN_ID */\n+}\n+\n+struct rtnl_link_ops rmnet_link_ops __read_mostly = {\n+\t.kind\t\t= \"rmnet\",\n+\t.maxtype\t= __IFLA_VLAN_MAX,\n+\t.priv_size\t= sizeof(struct rmnet_priv),\n+\t.setup\t\t= rmnet_vnd_setup,\n+\t.validate\t= rmnet_rtnl_validate,\n+\t.newlink\t= rmnet_newlink,\n+\t.dellink\t= rmnet_dellink,\n+\t.get_size\t= rmnet_get_size,\n+};\n+\n+struct rmnet_real_dev_info*\n+rmnet_get_real_dev_info(struct net_device *real_dev)\n+{\n+\treturn __rmnet_get_real_dev_info(real_dev);\n+}\n+\n+/* Startup/Shutdown */\n+\n+static int __init rmnet_init(void)\n+{\n+\tint rc;\n+\n+\trc = register_netdevice_notifier(&rmnet_dev_notifier);\n+\tif (rc != 0)\n+\t\treturn rc;\n+\n+\trc = rtnl_link_register(&rmnet_link_ops);\n+\tif (rc != 0) {\n+\t\tunregister_netdevice_notifier(&rmnet_dev_notifier);\n+\t\treturn rc;\n+\t}\n+\treturn rc;\n+}\n+\n+static void __exit rmnet_exit(void)\n+{\n+\tunregister_netdevice_notifier(&rmnet_dev_notifier);\n+\trtnl_link_unregister(&rmnet_link_ops);\n+}\n+\n+module_init(rmnet_init)\n+module_exit(rmnet_exit)\n+MODULE_LICENSE(\"GPL v2\");\ndiff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h\nnew file mode 100644\nindex 0000000..985d372\n--- /dev/null\n+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h\n@@ -0,0 +1,56 @@\n+/* Copyright (c) 2013-2014, 2016-2017 The Linux Foundation. All rights reserved.\n+ *\n+ * This program is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License version 2 and\n+ * only version 2 as published by the Free Software Foundation.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n+ * GNU General Public License for more details.\n+ *\n+ * RMNET Data configuration engine\n+ *\n+ */\n+\n+#include <linux/skbuff.h>\n+\n+#ifndef _RMNET_CONFIG_H_\n+#define _RMNET_CONFIG_H_\n+\n+#define RMNET_MAX_LOGICAL_EP 255\n+#define RMNET_MAX_VND 32\n+\n+/* Information about the next device to deliver the packet to.\n+ * Exact usage of this parameter depends on the rmnet_mode.\n+ */\n+struct rmnet_endpoint {\n+\tu8 rmnet_mode;\n+\tu8 mux_id;\n+\tstruct net_device *egress_dev;\n+};\n+\n+/* One instance of this structure is instantiated for each real_dev associated\n+ * with rmnet.\n+ */\n+struct rmnet_real_dev_info {\n+\tstruct net_device *dev;\n+\tstruct rmnet_endpoint local_ep;\n+\tstruct rmnet_endpoint muxed_ep[RMNET_MAX_LOGICAL_EP];\n+\tu32 ingress_data_format;\n+\tu32 egress_data_format;\n+\tstruct net_device *rmnet_devices[RMNET_MAX_VND];\n+\tu8 nr_rmnet_devs;\n+};\n+\n+extern struct rtnl_link_ops rmnet_link_ops;\n+\n+struct rmnet_priv {\n+\tstruct rmnet_endpoint local_ep;\n+\tu8 mux_id;\n+};\n+\n+struct rmnet_real_dev_info*\n+rmnet_get_real_dev_info(struct net_device *real_dev);\n+\n+#endif /* _RMNET_CONFIG_H_ */\ndiff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c\nnew file mode 100644\nindex 0000000..7dab3bb\n--- /dev/null\n+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c\n@@ -0,0 +1,271 @@\n+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.\n+ *\n+ * This program is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License version 2 and\n+ * only version 2 as published by the Free Software Foundation.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n+ * GNU General Public License for more details.\n+ *\n+ * RMNET Data ingress/egress handler\n+ *\n+ */\n+\n+#include <linux/netdevice.h>\n+#include <linux/netdev_features.h>\n+#include \"rmnet_private.h\"\n+#include \"rmnet_config.h\"\n+#include \"rmnet_vnd.h\"\n+#include \"rmnet_map.h\"\n+#include \"rmnet_handlers.h\"\n+\n+#define RMNET_IP_VERSION_4 0x40\n+#define RMNET_IP_VERSION_6 0x60\n+\n+/* Helper Functions */\n+\n+static void rmnet_set_skb_proto(struct sk_buff *skb)\n+{\n+\tswitch (skb->data[0] & 0xF0) {\n+\tcase RMNET_IP_VERSION_4:\n+\t\tskb->protocol = htons(ETH_P_IP);\n+\t\tbreak;\n+\tcase RMNET_IP_VERSION_6:\n+\t\tskb->protocol = htons(ETH_P_IPV6);\n+\t\tbreak;\n+\tdefault:\n+\t\tskb->protocol = htons(ETH_P_MAP);\n+\t\tbreak;\n+\t}\n+}\n+\n+/* Generic handler */\n+\n+static rx_handler_result_t\n+rmnet_bridge_handler(struct sk_buff *skb, struct rmnet_endpoint *ep)\n+{\n+\tif (!ep->egress_dev)\n+\t\tkfree_skb(skb);\n+\telse\n+\t\trmnet_egress_handler(skb, ep);\n+\n+\treturn RX_HANDLER_CONSUMED;\n+}\n+\n+static rx_handler_result_t\n+rmnet_deliver_skb(struct sk_buff *skb, struct rmnet_endpoint *ep)\n+{\n+\tswitch (ep->rmnet_mode) {\n+\tcase RMNET_EPMODE_NONE:\n+\t\treturn RX_HANDLER_PASS;\n+\n+\tcase RMNET_EPMODE_BRIDGE:\n+\t\treturn rmnet_bridge_handler(skb, ep);\n+\n+\tcase RMNET_EPMODE_VND:\n+\t\tskb_reset_transport_header(skb);\n+\t\tskb_reset_network_header(skb);\n+\t\trmnet_vnd_rx_fixup(skb, skb->dev);\n+\n+\t\tskb->pkt_type = PACKET_HOST;\n+\t\tskb_set_mac_header(skb, 0);\n+\t\tnetif_receive_skb(skb);\n+\t\treturn RX_HANDLER_CONSUMED;\n+\n+\tdefault:\n+\t\tkfree_skb(skb);\n+\t\treturn RX_HANDLER_CONSUMED;\n+\t}\n+}\n+\n+static rx_handler_result_t\n+rmnet_ingress_deliver_packet(struct sk_buff *skb,\n+\t\t\t struct rmnet_real_dev_info *r)\n+{\n+\tif (!r) {\n+\t\tkfree_skb(skb);\n+\t\treturn RX_HANDLER_CONSUMED;\n+\t}\n+\n+\tskb->dev = r->local_ep.egress_dev;\n+\n+\treturn rmnet_deliver_skb(skb, &r->local_ep);\n+}\n+\n+/* MAP handler */\n+\n+static rx_handler_result_t\n+__rmnet_map_ingress_handler(struct sk_buff *skb,\n+\t\t\t struct rmnet_real_dev_info *r)\n+{\n+\tstruct rmnet_endpoint *ep;\n+\tu8 mux_id;\n+\tu16 len;\n+\n+\tif (RMNET_MAP_GET_CD_BIT(skb)) {\n+\t\tif (r->ingress_data_format\n+\t\t & RMNET_INGRESS_FORMAT_MAP_COMMANDS)\n+\t\t\treturn rmnet_map_command(skb, r);\n+\n+\t\tkfree_skb(skb);\n+\t\treturn RX_HANDLER_CONSUMED;\n+\t}\n+\n+\tmux_id = RMNET_MAP_GET_MUX_ID(skb);\n+\tlen = RMNET_MAP_GET_LENGTH(skb) - RMNET_MAP_GET_PAD(skb);\n+\n+\tif (mux_id >= RMNET_MAX_LOGICAL_EP) {\n+\t\tkfree_skb(skb);\n+\t\treturn RX_HANDLER_CONSUMED;\n+\t}\n+\n+\tep = &r->muxed_ep[mux_id];\n+\n+\tif (r->ingress_data_format & RMNET_INGRESS_FORMAT_DEMUXING)\n+\t\tskb->dev = ep->egress_dev;\n+\n+\t/* Subtract MAP header */\n+\tskb_pull(skb, sizeof(struct rmnet_map_header));\n+\tskb_trim(skb, len);\n+\trmnet_set_skb_proto(skb);\n+\treturn rmnet_deliver_skb(skb, ep);\n+}\n+\n+static rx_handler_result_t\n+rmnet_map_ingress_handler(struct sk_buff *skb,\n+\t\t\t struct rmnet_real_dev_info *r)\n+{\n+\tstruct sk_buff *skbn;\n+\tint rc;\n+\n+\tif (r->ingress_data_format & RMNET_INGRESS_FORMAT_DEAGGREGATION) {\n+\t\twhile ((skbn = rmnet_map_deaggregate(skb, r)) != NULL)\n+\t\t\t__rmnet_map_ingress_handler(skbn, r);\n+\n+\t\tconsume_skb(skb);\n+\t\trc = RX_HANDLER_CONSUMED;\n+\t} else {\n+\t\trc = __rmnet_map_ingress_handler(skb, r);\n+\t}\n+\n+\treturn rc;\n+}\n+\n+static int rmnet_map_egress_handler(struct sk_buff *skb,\n+\t\t\t\t struct rmnet_real_dev_info *r,\n+\t\t\t\t struct rmnet_endpoint *ep,\n+\t\t\t\t struct net_device *orig_dev)\n+{\n+\tint required_headroom, additional_header_len;\n+\tstruct rmnet_map_header *map_header;\n+\n+\tadditional_header_len = 0;\n+\trequired_headroom = sizeof(struct rmnet_map_header);\n+\n+\tif (skb_headroom(skb) < required_headroom) {\n+\t\tif (pskb_expand_head(skb, required_headroom, 0, GFP_KERNEL))\n+\t\t\treturn RMNET_MAP_CONSUMED;\n+\t}\n+\n+\tmap_header = rmnet_map_add_map_header(skb, additional_header_len, 0);\n+\tif (!map_header)\n+\t\treturn RMNET_MAP_CONSUMED;\n+\n+\tif (r->egress_data_format & RMNET_EGRESS_FORMAT_MUXING) {\n+\t\tif (ep->mux_id == 0xff)\n+\t\t\tmap_header->mux_id = 0;\n+\t\telse\n+\t\t\tmap_header->mux_id = ep->mux_id;\n+\t}\n+\n+\tskb->protocol = htons(ETH_P_MAP);\n+\n+\treturn RMNET_MAP_SUCCESS;\n+}\n+\n+/* Ingress / Egress Entry Points */\n+\n+/* Processes packet as per ingress data format for receiving device. Logical\n+ * endpoint is determined from packet inspection. Packet is then sent to the\n+ * egress device listed in the logical endpoint configuration.\n+ */\n+rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb)\n+{\n+\tstruct rmnet_real_dev_info *r;\n+\tstruct sk_buff *skb = *pskb;\n+\tstruct net_device *dev;\n+\tint rc;\n+\n+\tif (!skb)\n+\t\treturn RX_HANDLER_CONSUMED;\n+\n+\tdev = skb->dev;\n+\tr = rmnet_get_real_dev_info(dev);\n+\n+\tif (r->ingress_data_format & RMNET_INGRESS_FORMAT_MAP) {\n+\t\trc = rmnet_map_ingress_handler(skb, r);\n+\t} else {\n+\t\tswitch (ntohs(skb->protocol)) {\n+\t\tcase ETH_P_MAP:\n+\t\t\tif (r->local_ep.rmnet_mode ==\n+\t\t\t\tRMNET_EPMODE_BRIDGE) {\n+\t\t\t\trc = rmnet_ingress_deliver_packet(skb, r);\n+\t\t\t} else {\n+\t\t\t\tkfree_skb(skb);\n+\t\t\t\trc = RX_HANDLER_CONSUMED;\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\tcase ETH_P_IP:\n+\t\tcase ETH_P_IPV6:\n+\t\t\trc = rmnet_ingress_deliver_packet(skb, r);\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\trc = RX_HANDLER_PASS;\n+\t\t}\n+\t}\n+\n+\treturn rc;\n+}\n+\n+/* Modifies packet as per logical endpoint configuration and egress data format\n+ * for egress device configured in logical endpoint. Packet is then transmitted\n+ * on the egress device.\n+ */\n+void rmnet_egress_handler(struct sk_buff *skb,\n+\t\t\t struct rmnet_endpoint *ep)\n+{\n+\tstruct rmnet_real_dev_info *r;\n+\tstruct net_device *orig_dev;\n+\n+\torig_dev = skb->dev;\n+\tskb->dev = ep->egress_dev;\n+\n+\tr = rmnet_get_real_dev_info(skb->dev);\n+\tif (!r) {\n+\t\tkfree_skb(skb);\n+\t\treturn;\n+\t}\n+\n+\tif (r->egress_data_format & RMNET_EGRESS_FORMAT_MAP) {\n+\t\tswitch (rmnet_map_egress_handler(skb, r, ep, orig_dev)) {\n+\t\tcase RMNET_MAP_CONSUMED:\n+\t\t\treturn;\n+\n+\t\tcase RMNET_MAP_SUCCESS:\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\tkfree_skb(skb);\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\n+\tif (ep->rmnet_mode == RMNET_EPMODE_VND)\n+\t\trmnet_vnd_tx_fixup(skb, orig_dev);\n+\n+\tdev_queue_xmit(skb);\n+}\ndiff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h\nnew file mode 100644\nindex 0000000..f2638cf\n--- /dev/null\n+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h\n@@ -0,0 +1,26 @@\n+/* Copyright (c) 2013, 2016-2017 The Linux Foundation. All rights reserved.\n+ *\n+ * This program is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License version 2 and\n+ * only version 2 as published by the Free Software Foundation.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n+ * GNU General Public License for more details.\n+ *\n+ * RMNET Data ingress/egress handler\n+ *\n+ */\n+\n+#ifndef _RMNET_HANDLERS_H_\n+#define _RMNET_HANDLERS_H_\n+\n+#include \"rmnet_config.h\"\n+\n+void rmnet_egress_handler(struct sk_buff *skb,\n+\t\t\t struct rmnet_endpoint *ep);\n+\n+rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb);\n+\n+#endif /* _RMNET_HANDLERS_H_ */\ndiff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h\nnew file mode 100644\nindex 0000000..2aabad2\n--- /dev/null\n+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h\n@@ -0,0 +1,88 @@\n+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.\n+ *\n+ * This program is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License version 2 and\n+ * only version 2 as published by the Free Software Foundation.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n+ * GNU General Public License for more details.\n+ */\n+\n+#ifndef _RMNET_MAP_H_\n+#define _RMNET_MAP_H_\n+\n+struct rmnet_map_control_command {\n+\tu8 command_name;\n+\tu8 cmd_type:2;\n+\tu8 reserved:6;\n+\tu16 reserved2;\n+\tu32 transaction_id;\n+\tunion {\n+\t\tstruct {\n+\t\t\tu16 ip_family:2;\n+\t\t\tu16 reserved:14;\n+\t\t\tu16 flow_control_seq_num;\n+\t\t\tu32 qos_id;\n+\t\t} flow_control;\n+\t\tu8 data[0];\n+\t};\n+} __aligned(1);\n+\n+enum rmnet_map_results {\n+\tRMNET_MAP_SUCCESS,\n+\tRMNET_MAP_CONSUMED,\n+\tRMNET_MAP_GENERAL_FAILURE,\n+\tRMNET_MAP_NOT_ENABLED,\n+\tRMNET_MAP_FAILED_AGGREGATION,\n+\tRMNET_MAP_FAILED_MUX\n+};\n+\n+enum rmnet_map_commands {\n+\tRMNET_MAP_COMMAND_NONE,\n+\tRMNET_MAP_COMMAND_FLOW_DISABLE,\n+\tRMNET_MAP_COMMAND_FLOW_ENABLE,\n+\t/* These should always be the last 2 elements */\n+\tRMNET_MAP_COMMAND_UNKNOWN,\n+\tRMNET_MAP_COMMAND_ENUM_LENGTH\n+};\n+\n+struct rmnet_map_header {\n+\tu8 pad_len:6;\n+\tu8 reserved_bit:1;\n+\tu8 cd_bit:1;\n+\tu8 mux_id;\n+\tu16 pkt_len;\n+} __aligned(1);\n+\n+#define RMNET_MAP_GET_MUX_ID(Y) (((struct rmnet_map_header *) \\\n+\t\t\t\t (Y)->data)->mux_id)\n+#define RMNET_MAP_GET_CD_BIT(Y) (((struct rmnet_map_header *) \\\n+\t\t\t\t(Y)->data)->cd_bit)\n+#define RMNET_MAP_GET_PAD(Y) (((struct rmnet_map_header *) \\\n+\t\t\t\t(Y)->data)->pad_len)\n+#define RMNET_MAP_GET_CMD_START(Y) ((struct rmnet_map_control_command *) \\\n+\t\t\t\t ((Y)->data + \\\n+\t\t\t\t sizeof(struct rmnet_map_header)))\n+#define RMNET_MAP_GET_LENGTH(Y) (ntohs(((struct rmnet_map_header *) \\\n+\t\t\t\t\t(Y)->data)->pkt_len))\n+\n+#define RMNET_MAP_COMMAND_REQUEST 0\n+#define RMNET_MAP_COMMAND_ACK 1\n+#define RMNET_MAP_COMMAND_UNSUPPORTED 2\n+#define RMNET_MAP_COMMAND_INVALID 3\n+\n+#define RMNET_MAP_NO_PAD_BYTES 0\n+#define RMNET_MAP_ADD_PAD_BYTES 1\n+\n+u8 rmnet_map_demultiplex(struct sk_buff *skb);\n+struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,\n+\t\t\t\t struct rmnet_real_dev_info *rdinfo);\n+\n+struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb,\n+\t\t\t\t\t\t int hdrlen, int pad);\n+rx_handler_result_t rmnet_map_command(struct sk_buff *skb,\n+\t\t\t\t struct rmnet_real_dev_info *rdinfo);\n+\n+#endif /* _RMNET_MAP_H_ */\ndiff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c\nnew file mode 100644\nindex 0000000..ccded40\n--- /dev/null\n+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c\n@@ -0,0 +1,107 @@\n+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.\n+ *\n+ * This program is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License version 2 and\n+ * only version 2 as published by the Free Software Foundation.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n+ * GNU General Public License for more details.\n+ */\n+\n+#include <linux/netdevice.h>\n+#include \"rmnet_config.h\"\n+#include \"rmnet_map.h\"\n+#include \"rmnet_private.h\"\n+#include \"rmnet_vnd.h\"\n+\n+static u8 rmnet_map_do_flow_control(struct sk_buff *skb,\n+\t\t\t\t struct rmnet_real_dev_info *rdinfo,\n+\t\t\t\t int enable)\n+{\n+\tstruct rmnet_map_control_command *cmd;\n+\tstruct rmnet_endpoint *ep;\n+\tstruct net_device *vnd;\n+\tu16 ip_family;\n+\tu16 fc_seq;\n+\tu32 qos_id;\n+\tu8 mux_id;\n+\tint r;\n+\n+\tmux_id = RMNET_MAP_GET_MUX_ID(skb);\n+\tcmd = RMNET_MAP_GET_CMD_START(skb);\n+\n+\tif (mux_id >= RMNET_MAX_LOGICAL_EP) {\n+\t\tkfree_skb(skb);\n+\t\treturn RX_HANDLER_CONSUMED;\n+\t}\n+\n+\tep = &rdinfo->muxed_ep[mux_id];\n+\tvnd = ep->egress_dev;\n+\n+\tip_family = cmd->flow_control.ip_family;\n+\tfc_seq = ntohs(cmd->flow_control.flow_control_seq_num);\n+\tqos_id = ntohl(cmd->flow_control.qos_id);\n+\n+\t/* Ignore the ip family and pass the sequence number for both v4 and v6\n+\t * sequence. User space does not support creating dedicated flows for\n+\t * the 2 protocols\n+\t */\n+\tr = rmnet_vnd_do_flow_control(vnd, enable);\n+\tif (r) {\n+\t\tkfree_skb(skb);\n+\t\treturn RMNET_MAP_COMMAND_UNSUPPORTED;\n+\t} else {\n+\t\treturn RMNET_MAP_COMMAND_ACK;\n+\t}\n+}\n+\n+static void rmnet_map_send_ack(struct sk_buff *skb,\n+\t\t\t unsigned char type,\n+\t\t\t struct rmnet_real_dev_info *rdinfo)\n+{\n+\tstruct rmnet_map_control_command *cmd;\n+\tint xmit_status;\n+\n+\tskb->protocol = htons(ETH_P_MAP);\n+\n+\tcmd = RMNET_MAP_GET_CMD_START(skb);\n+\tcmd->cmd_type = type & 0x03;\n+\n+\tnetif_tx_lock(skb->dev);\n+\txmit_status = skb->dev->netdev_ops->ndo_start_xmit(skb, skb->dev);\n+\tnetif_tx_unlock(skb->dev);\n+}\n+\n+/* Process MAP command frame and send N/ACK message as appropriate. Message cmd\n+ * name is decoded here and appropriate handler is called.\n+ */\n+rx_handler_result_t rmnet_map_command(struct sk_buff *skb,\n+\t\t\t\t struct rmnet_real_dev_info *rdinfo)\n+{\n+\tstruct rmnet_map_control_command *cmd;\n+\tunsigned char command_name;\n+\tunsigned char rc = 0;\n+\n+\tcmd = RMNET_MAP_GET_CMD_START(skb);\n+\tcommand_name = cmd->command_name;\n+\n+\tswitch (command_name) {\n+\tcase RMNET_MAP_COMMAND_FLOW_ENABLE:\n+\t\trc = rmnet_map_do_flow_control(skb, rdinfo, 1);\n+\t\tbreak;\n+\n+\tcase RMNET_MAP_COMMAND_FLOW_DISABLE:\n+\t\trc = rmnet_map_do_flow_control(skb, rdinfo, 0);\n+\t\tbreak;\n+\n+\tdefault:\n+\t\trc = RMNET_MAP_COMMAND_UNSUPPORTED;\n+\t\tkfree_skb(skb);\n+\t\tbreak;\n+\t}\n+\tif (rc == RMNET_MAP_COMMAND_ACK)\n+\t\trmnet_map_send_ack(skb, rc, rdinfo);\n+\treturn RX_HANDLER_CONSUMED;\n+}\ndiff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c\nnew file mode 100644\nindex 0000000..a29c476\n--- /dev/null\n+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c\n@@ -0,0 +1,105 @@\n+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.\n+ *\n+ * This program is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License version 2 and\n+ * only version 2 as published by the Free Software Foundation.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n+ * GNU General Public License for more details.\n+ *\n+ * RMNET Data MAP protocol\n+ *\n+ */\n+\n+#include <linux/netdevice.h>\n+#include \"rmnet_config.h\"\n+#include \"rmnet_map.h\"\n+#include \"rmnet_private.h\"\n+\n+#define RMNET_MAP_DEAGGR_SPACING 64\n+#define RMNET_MAP_DEAGGR_HEADROOM (RMNET_MAP_DEAGGR_SPACING / 2)\n+\n+/* Adds MAP header to front of skb->data\n+ * Padding is calculated and set appropriately in MAP header. Mux ID is\n+ * initialized to 0.\n+ */\n+struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb,\n+\t\t\t\t\t\t int hdrlen, int pad)\n+{\n+\tstruct rmnet_map_header *map_header;\n+\tu32 padding, map_datalen;\n+\tu8 *padbytes;\n+\n+\tif (skb_headroom(skb) < sizeof(struct rmnet_map_header))\n+\t\treturn NULL;\n+\n+\tmap_datalen = skb->len - hdrlen;\n+\tmap_header = (struct rmnet_map_header *)\n+\t\t\tskb_push(skb, sizeof(struct rmnet_map_header));\n+\tmemset(map_header, 0, sizeof(struct rmnet_map_header));\n+\n+\tif (pad == RMNET_MAP_NO_PAD_BYTES) {\n+\t\tmap_header->pkt_len = htons(map_datalen);\n+\t\treturn map_header;\n+\t}\n+\n+\tpadding = ALIGN(map_datalen, 4) - map_datalen;\n+\n+\tif (padding == 0)\n+\t\tgoto done;\n+\n+\tif (skb_tailroom(skb) < padding)\n+\t\treturn NULL;\n+\n+\tpadbytes = (u8 *)skb_put(skb, padding);\n+\tmemset(padbytes, 0, padding);\n+\n+done:\n+\tmap_header->pkt_len = htons(map_datalen + padding);\n+\tmap_header->pad_len = padding & 0x3F;\n+\n+\treturn map_header;\n+}\n+\n+/* Deaggregates a single packet\n+ * A whole new buffer is allocated for each portion of an aggregated frame.\n+ * Caller should keep calling deaggregate() on the source skb until 0 is\n+ * returned, indicating that there are no more packets to deaggregate. Caller\n+ * is responsible for freeing the original skb.\n+ */\n+struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,\n+\t\t\t\t struct rmnet_real_dev_info *rdinfo)\n+{\n+\tstruct rmnet_map_header *maph;\n+\tstruct sk_buff *skbn;\n+\tu32 packet_len;\n+\n+\tif (skb->len == 0)\n+\t\treturn NULL;\n+\n+\tmaph = (struct rmnet_map_header *)skb->data;\n+\tpacket_len = ntohs(maph->pkt_len) + sizeof(struct rmnet_map_header);\n+\n+\tif (((int)skb->len - (int)packet_len) < 0)\n+\t\treturn NULL;\n+\n+\tskbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING, GFP_ATOMIC);\n+\tif (!skbn)\n+\t\treturn NULL;\n+\n+\tskbn->dev = skb->dev;\n+\tskb_reserve(skbn, RMNET_MAP_DEAGGR_HEADROOM);\n+\tskb_put(skbn, packet_len);\n+\tmemcpy(skbn->data, skb->data, packet_len);\n+\tskb_pull(skb, packet_len);\n+\n+\t/* Some hardware can send us empty frames. Catch them */\n+\tif (ntohs(maph->pkt_len) == 0) {\n+\t\tkfree_skb(skb);\n+\t\treturn NULL;\n+\t}\n+\n+\treturn skbn;\n+}\ndiff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h\nnew file mode 100644\nindex 0000000..ed820b5\n--- /dev/null\n+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h\n@@ -0,0 +1,45 @@\n+/* Copyright (c) 2013-2014, 2016-2017 The Linux Foundation. All rights reserved.\n+ *\n+ * This program is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License version 2 and\n+ * only version 2 as published by the Free Software Foundation.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n+ * GNU General Public License for more details.\n+ */\n+\n+#ifndef _RMNET_PRIVATE_H_\n+#define _RMNET_PRIVATE_H_\n+\n+#define RMNET_MAX_VND 32\n+#define RMNET_MAX_PACKET_SIZE 16384\n+#define RMNET_DFLT_PACKET_SIZE 1500\n+#define RMNET_NEEDED_HEADROOM 16\n+#define RMNET_TX_QUEUE_LEN 1000\n+\n+/* Constants */\n+#define RMNET_EGRESS_FORMAT__RESERVED__ BIT(0)\n+#define RMNET_EGRESS_FORMAT_MAP BIT(1)\n+#define RMNET_EGRESS_FORMAT_AGGREGATION BIT(2)\n+#define RMNET_EGRESS_FORMAT_MUXING BIT(3)\n+#define RMNET_EGRESS_FORMAT_MAP_CKSUMV3 BIT(4)\n+#define RMNET_EGRESS_FORMAT_MAP_CKSUMV4 BIT(5)\n+\n+#define RMNET_INGRESS_FIX_ETHERNET BIT(0)\n+#define RMNET_INGRESS_FORMAT_MAP BIT(1)\n+#define RMNET_INGRESS_FORMAT_DEAGGREGATION BIT(2)\n+#define RMNET_INGRESS_FORMAT_DEMUXING BIT(3)\n+#define RMNET_INGRESS_FORMAT_MAP_COMMANDS BIT(4)\n+#define RMNET_INGRESS_FORMAT_MAP_CKSUMV3 BIT(5)\n+#define RMNET_INGRESS_FORMAT_MAP_CKSUMV4 BIT(6)\n+\n+/* Pass the frame up the stack with no modifications to skb->dev */\n+#define RMNET_EPMODE_NONE (0)\n+/* Replace skb->dev to a virtual rmnet device and pass up the stack */\n+#define RMNET_EPMODE_VND (1)\n+/* Pass the frame directly to another device with dev_queue_xmit() */\n+#define RMNET_EPMODE_BRIDGE (2)\n+\n+#endif /* _RMNET_PRIVATE_H_ */\ndiff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c\nnew file mode 100644\nindex 0000000..c8b573d\n--- /dev/null\n+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c\n@@ -0,0 +1,170 @@\n+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.\n+ *\n+ * This program is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License version 2 and\n+ * only version 2 as published by the Free Software Foundation.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n+ * GNU General Public License for more details.\n+ *\n+ *\n+ * RMNET Data virtual network driver\n+ *\n+ */\n+\n+#include <linux/etherdevice.h>\n+#include <linux/if_arp.h>\n+#include <net/pkt_sched.h>\n+#include \"rmnet_config.h\"\n+#include \"rmnet_handlers.h\"\n+#include \"rmnet_private.h\"\n+#include \"rmnet_map.h\"\n+#include \"rmnet_vnd.h\"\n+\n+/* RX/TX Fixup */\n+\n+void rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev)\n+{\n+\tdev->stats.rx_packets++;\n+\tdev->stats.rx_bytes += skb->len;\n+}\n+\n+void rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev)\n+{\n+\tdev->stats.tx_packets++;\n+\tdev->stats.tx_bytes += skb->len;\n+}\n+\n+/* Network Device Operations */\n+\n+static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb,\n+\t\t\t\t\tstruct net_device *dev)\n+{\n+\tstruct rmnet_priv *priv;\n+\n+\tpriv = netdev_priv(dev);\n+\tif (priv->local_ep.egress_dev) {\n+\t\trmnet_egress_handler(skb, &priv->local_ep);\n+\t} else {\n+\t\tdev->stats.tx_dropped++;\n+\t\tkfree_skb(skb);\n+\t}\n+\treturn NETDEV_TX_OK;\n+}\n+\n+static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu)\n+{\n+\tif (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE)\n+\t\treturn -EINVAL;\n+\n+\trmnet_dev->mtu = new_mtu;\n+\treturn 0;\n+}\n+\n+static const struct net_device_ops rmnet_vnd_ops = {\n+\t.ndo_start_xmit = rmnet_vnd_start_xmit,\n+\t.ndo_change_mtu = rmnet_vnd_change_mtu,\n+};\n+\n+/* Called by kernel whenever a new rmnet<n> device is created. Sets MTU,\n+ * flags, ARP type, needed headroom, etc...\n+ */\n+void rmnet_vnd_setup(struct net_device *rmnet_dev)\n+{\n+\tstruct rmnet_priv *priv;\n+\n+\tpriv = netdev_priv(rmnet_dev);\n+\tnetdev_dbg(rmnet_dev, \"Setting up device %s\\n\", rmnet_dev->name);\n+\n+\trmnet_dev->netdev_ops = &rmnet_vnd_ops;\n+\trmnet_dev->mtu = RMNET_DFLT_PACKET_SIZE;\n+\trmnet_dev->needed_headroom = RMNET_NEEDED_HEADROOM;\n+\trandom_ether_addr(rmnet_dev->dev_addr);\n+\trmnet_dev->tx_queue_len = RMNET_TX_QUEUE_LEN;\n+\n+\t/* Raw IP mode */\n+\trmnet_dev->header_ops = NULL; /* No header */\n+\trmnet_dev->type = ARPHRD_RAWIP;\n+\trmnet_dev->hard_header_len = 0;\n+\trmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);\n+\n+\trmnet_dev->needs_free_netdev = true;\n+}\n+\n+/* Exposed API */\n+\n+int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev,\n+\t\t struct rmnet_real_dev_info *r)\n+{\n+\tint rc;\n+\n+\tif (r->rmnet_devices[id])\n+\t\treturn -EINVAL;\n+\n+\trc = register_netdevice(rmnet_dev);\n+\tif (!rc) {\n+\t\tr->rmnet_devices[id] = rmnet_dev;\n+\t\tr->nr_rmnet_devs++;\n+\t\trmnet_dev->rtnl_link_ops = &rmnet_link_ops;\n+\t}\n+\n+\treturn rc;\n+}\n+\n+int rmnet_vnd_dellink(u8 id, struct rmnet_real_dev_info *r)\n+{\n+\tif (id >= RMNET_MAX_VND || !r->rmnet_devices[id])\n+\t\treturn -EINVAL;\n+\n+\tr->rmnet_devices[id] = NULL;\n+\tr->nr_rmnet_devs--;\n+\treturn 0;\n+}\n+\n+u8 rmnet_vnd_get_mux(struct net_device *rmnet_dev)\n+{\n+\tstruct rmnet_priv *priv;\n+\n+\tpriv = netdev_priv(rmnet_dev);\n+\treturn priv->mux_id;\n+}\n+\n+void rmnet_vnd_set_mux(struct net_device *rmnet_dev, u8 mux_id)\n+{\n+\tstruct rmnet_priv *priv;\n+\n+\tpriv = netdev_priv(rmnet_dev);\n+\tpriv->mux_id = mux_id;\n+}\n+\n+/* Gets the logical endpoint configuration for a RmNet virtual network device\n+ * node. Caller should confirm that devices is a RmNet VND before calling.\n+ */\n+struct rmnet_endpoint *rmnet_vnd_get_endpoint(struct net_device *rmnet_dev)\n+{\n+\tstruct rmnet_priv *priv;\n+\n+\tif (!rmnet_dev)\n+\t\treturn NULL;\n+\n+\tpriv = netdev_priv(rmnet_dev);\n+\n+\treturn &priv->local_ep;\n+}\n+\n+int rmnet_vnd_do_flow_control(struct net_device *rmnet_dev, int enable)\n+{\n+\tnetdev_dbg(rmnet_dev, \"Setting VND TX queue state to %d\\n\", enable);\n+\t/* Although we expect similar number of enable/disable\n+\t * commands, optimize for the disable. That is more\n+\t * latency sensitive than enable\n+\t */\n+\tif (unlikely(enable))\n+\t\tnetif_wake_queue(rmnet_dev);\n+\telse\n+\t\tnetif_stop_queue(rmnet_dev);\n+\n+\treturn 0;\n+}\ndiff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h\nnew file mode 100644\nindex 0000000..b102b42\n--- /dev/null\n+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h\n@@ -0,0 +1,29 @@\n+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.\n+ *\n+ * This program is free software; you can redistribute it and/or modify\n+ * it under the terms of the GNU General Public License version 2 and\n+ * only version 2 as published by the Free Software Foundation.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n+ * GNU General Public License for more details.\n+ *\n+ * RMNET Data Virtual Network Device APIs\n+ *\n+ */\n+\n+#ifndef _RMNET_VND_H_\n+#define _RMNET_VND_H_\n+\n+int rmnet_vnd_do_flow_control(struct net_device *dev, int enable);\n+struct rmnet_endpoint *rmnet_vnd_get_endpoint(struct net_device *dev);\n+int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev,\n+\t\t struct rmnet_real_dev_info *r);\n+int rmnet_vnd_dellink(u8 id, struct rmnet_real_dev_info *r);\n+void rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev);\n+void rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev);\n+u8 rmnet_vnd_get_mux(struct net_device *rmnet_dev);\n+void rmnet_vnd_set_mux(struct net_device *rmnet_dev, u8 mux_id);\n+void rmnet_vnd_setup(struct net_device *dev);\n+#endif /* _RMNET_VND_H_ */\n", "prefixes": [ "net-next", "3/3", "v10" ] }