All of lore.kernel.org
 help / color / mirror / Atom feed
* Qualcomm rmnet driver and qmi_wwan
@ 2018-02-21 11:38 Daniele Palmas
  2018-02-21 19:47 ` Subash Abhinov Kasiviswanathan
  0 siblings, 1 reply; 15+ messages in thread
From: Daniele Palmas @ 2018-02-21 11:38 UTC (permalink / raw)
  To: netdev

Hello,

in rmnet kernel documentation I read:

"This driver can be used to register onto any physical network device in
IP mode. Physical transports include USB, HSIC, PCIe and IP accelerator."

Does this mean that it can be used in association with the qmi_wwan driver?

If yes, can someone give me an hint on the steps to follow?

If not, does anyone know if it is possible to modify qmi_wwan in order
to take advantage of the features provided by the rmnet driver?

In this case hint on the changes for modifying qmi_wwan are welcome.

Thanks in advance,
Daniele

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Qualcomm rmnet driver and qmi_wwan
  2018-02-21 11:38 Qualcomm rmnet driver and qmi_wwan Daniele Palmas
@ 2018-02-21 19:47 ` Subash Abhinov Kasiviswanathan
  2018-02-22 10:44   ` Daniele Palmas
  2018-06-05  9:38   ` Daniele Palmas
  0 siblings, 2 replies; 15+ messages in thread
From: Subash Abhinov Kasiviswanathan @ 2018-02-21 19:47 UTC (permalink / raw)
  To: Daniele Palmas; +Cc: netdev

On 2018-02-21 04:38, Daniele Palmas wrote:
> Hello,
> 
> in rmnet kernel documentation I read:
> 
> "This driver can be used to register onto any physical network device 
> in
> IP mode. Physical transports include USB, HSIC, PCIe and IP 
> accelerator."
> 
> Does this mean that it can be used in association with the qmi_wwan 
> driver?
> 
> If yes, can someone give me an hint on the steps to follow?
> 
> If not, does anyone know if it is possible to modify qmi_wwan in order
> to take advantage of the features provided by the rmnet driver?
> 
> In this case hint on the changes for modifying qmi_wwan are welcome.
> 
> Thanks in advance,
> Daniele

Hi

I havent used qmi_wwan so the following comment is based on code 
inspection.
qmimux_register_device() is creating qmimux devices with usb net device 
as
real_dev. The Multiplexing and aggregation header (qmimux_hdr) is 
stripped off
in qmimux_rx_fixup() and the packet is passed on to stack.

You could instead create rmnet devices with the usb netdevice as real 
dev.
The packets from the usb net driver can be queued to network stack 
directly
as rmnet driver will setup a RX handler. rmnet driver will process the 
packets
further and then queue to network stack.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Qualcomm rmnet driver and qmi_wwan
  2018-02-21 19:47 ` Subash Abhinov Kasiviswanathan
@ 2018-02-22 10:44   ` Daniele Palmas
  2018-06-05  9:38   ` Daniele Palmas
  1 sibling, 0 replies; 15+ messages in thread
From: Daniele Palmas @ 2018-02-22 10:44 UTC (permalink / raw)
  To: Subash Abhinov Kasiviswanathan; +Cc: netdev

Hi Subash,

2018-02-21 20:47 GMT+01:00 Subash Abhinov Kasiviswanathan
<subashab@codeaurora.org>:
> On 2018-02-21 04:38, Daniele Palmas wrote:
>>
>> Hello,
>>
>> in rmnet kernel documentation I read:
>>
>> "This driver can be used to register onto any physical network device in
>> IP mode. Physical transports include USB, HSIC, PCIe and IP accelerator."
>>
>> Does this mean that it can be used in association with the qmi_wwan
>> driver?
>>
>> If yes, can someone give me an hint on the steps to follow?
>>
>> If not, does anyone know if it is possible to modify qmi_wwan in order
>> to take advantage of the features provided by the rmnet driver?
>>
>> In this case hint on the changes for modifying qmi_wwan are welcome.
>>
>> Thanks in advance,
>> Daniele
>
>
> Hi
>
> I havent used qmi_wwan so the following comment is based on code inspection.
> qmimux_register_device() is creating qmimux devices with usb net device as
> real_dev. The Multiplexing and aggregation header (qmimux_hdr) is stripped
> off
> in qmimux_rx_fixup() and the packet is passed on to stack.
>
> You could instead create rmnet devices with the usb netdevice as real dev.
> The packets from the usb net driver can be queued to network stack directly
> as rmnet driver will setup a RX handler. rmnet driver will process the
> packets
> further and then queue to network stack.
>

Thanks, I will try to do some testing and report my findings.

Regards,
Daniele

> --
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Qualcomm rmnet driver and qmi_wwan
  2018-02-21 19:47 ` Subash Abhinov Kasiviswanathan
  2018-02-22 10:44   ` Daniele Palmas
@ 2018-06-05  9:38   ` Daniele Palmas
  2018-06-05 14:54     ` Dan Williams
  1 sibling, 1 reply; 15+ messages in thread
From: Daniele Palmas @ 2018-06-05  9:38 UTC (permalink / raw)
  To: Subash Abhinov Kasiviswanathan; +Cc: netdev

Hi,

2018-02-21 20:47 GMT+01:00 Subash Abhinov Kasiviswanathan
<subashab@codeaurora.org>:
> On 2018-02-21 04:38, Daniele Palmas wrote:
>>
>> Hello,
>>
>> in rmnet kernel documentation I read:
>>
>> "This driver can be used to register onto any physical network device in
>> IP mode. Physical transports include USB, HSIC, PCIe and IP accelerator."
>>
>> Does this mean that it can be used in association with the qmi_wwan
>> driver?
>>
>> If yes, can someone give me an hint on the steps to follow?
>>
>> If not, does anyone know if it is possible to modify qmi_wwan in order
>> to take advantage of the features provided by the rmnet driver?
>>
>> In this case hint on the changes for modifying qmi_wwan are welcome.
>>
>> Thanks in advance,
>> Daniele
>
>
> Hi
>
> I havent used qmi_wwan so the following comment is based on code inspection.
> qmimux_register_device() is creating qmimux devices with usb net device as
> real_dev. The Multiplexing and aggregation header (qmimux_hdr) is stripped
> off
> in qmimux_rx_fixup() and the packet is passed on to stack.
>
> You could instead create rmnet devices with the usb netdevice as real dev.
> The packets from the usb net driver can be queued to network stack directly
> as rmnet driver will setup a RX handler. rmnet driver will process the
> packets
> further and then queue to network stack.
>

in kernel documentation I read that rmnet user space configuration is
done through librmnetctl available at

https://source.codeaurora.org/quic/la/platform/vendor/qcom-opensource/dataservices/tree/rmnetctl

However it seems to me that this is a bit outdated (e.g. it does not
properly build since it is looking for kernel header
linux/rmnet_data.h that, as far as I understand, is no more present).

Is there available a more recent version of the tool?

Thanks,
Daniele

> --
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Qualcomm rmnet driver and qmi_wwan
  2018-06-05  9:38   ` Daniele Palmas
@ 2018-06-05 14:54     ` Dan Williams
  2018-06-05 17:38       ` Subash Abhinov Kasiviswanathan
  0 siblings, 1 reply; 15+ messages in thread
From: Dan Williams @ 2018-06-05 14:54 UTC (permalink / raw)
  To: Daniele Palmas, Subash Abhinov Kasiviswanathan; +Cc: netdev

On Tue, 2018-06-05 at 11:38 +0200, Daniele Palmas wrote:
> Hi,
> 
> 2018-02-21 20:47 GMT+01:00 Subash Abhinov Kasiviswanathan
> <subashab@codeaurora.org>:
> > On 2018-02-21 04:38, Daniele Palmas wrote:
> > > 
> > > Hello,
> > > 
> > > in rmnet kernel documentation I read:
> > > 
> > > "This driver can be used to register onto any physical network
> > > device in
> > > IP mode. Physical transports include USB, HSIC, PCIe and IP
> > > accelerator."
> > > 
> > > Does this mean that it can be used in association with the
> > > qmi_wwan
> > > driver?
> > > 
> > > If yes, can someone give me an hint on the steps to follow?
> > > 
> > > If not, does anyone know if it is possible to modify qmi_wwan in
> > > order
> > > to take advantage of the features provided by the rmnet driver?
> > > 
> > > In this case hint on the changes for modifying qmi_wwan are
> > > welcome.
> > > 
> > > Thanks in advance,
> > > Daniele
> > 
> > 
> > Hi
> > 
> > I havent used qmi_wwan so the following comment is based on code
> > inspection.
> > qmimux_register_device() is creating qmimux devices with usb net
> > device as
> > real_dev. The Multiplexing and aggregation header (qmimux_hdr) is
> > stripped
> > off
> > in qmimux_rx_fixup() and the packet is passed on to stack.
> > 
> > You could instead create rmnet devices with the usb netdevice as
> > real dev.
> > The packets from the usb net driver can be queued to network stack
> > directly
> > as rmnet driver will setup a RX handler. rmnet driver will process
> > the
> > packets
> > further and then queue to network stack.
> > 
> 
> in kernel documentation I read that rmnet user space configuration is
> done through librmnetctl available at
> 
> https://source.codeaurora.org/quic/la/platform/vendor/qcom-opensource
> /dataservices/tree/rmnetctl
> 
> However it seems to me that this is a bit outdated (e.g. it does not
> properly build since it is looking for kernel header
> linux/rmnet_data.h that, as far as I understand, is no more present).
> 
> Is there available a more recent version of the tool?

I'd expect that somebody (Subash?) would add support for the
rmnet/qmimux options to iproute2 via 'ip link' like exists for most
other device types.

Dan

> Thanks,
> Daniele
> 
> > --
> > Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> > a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Qualcomm rmnet driver and qmi_wwan
  2018-06-05 14:54     ` Dan Williams
@ 2018-06-05 17:38       ` Subash Abhinov Kasiviswanathan
  2018-06-08 10:21         ` Daniele Palmas
  0 siblings, 1 reply; 15+ messages in thread
From: Subash Abhinov Kasiviswanathan @ 2018-06-05 17:38 UTC (permalink / raw)
  To: Dan Williams, Daniele Palmas; +Cc: netdev

[-- Attachment #1: Type: text/plain, Size: 2834 bytes --]

On 2018-06-05 08:54, Dan Williams wrote:
> On Tue, 2018-06-05 at 11:38 +0200, Daniele Palmas wrote:
>> Hi,
>> 
>> 2018-02-21 20:47 GMT+01:00 Subash Abhinov Kasiviswanathan
>> <subashab@codeaurora.org>:
>> > On 2018-02-21 04:38, Daniele Palmas wrote:
>> > >
>> > > Hello,
>> > >
>> > > in rmnet kernel documentation I read:
>> > >
>> > > "This driver can be used to register onto any physical network
>> > > device in
>> > > IP mode. Physical transports include USB, HSIC, PCIe and IP
>> > > accelerator."
>> > >
>> > > Does this mean that it can be used in association with the
>> > > qmi_wwan
>> > > driver?
>> > >
>> > > If yes, can someone give me an hint on the steps to follow?
>> > >
>> > > If not, does anyone know if it is possible to modify qmi_wwan in
>> > > order
>> > > to take advantage of the features provided by the rmnet driver?
>> > >
>> > > In this case hint on the changes for modifying qmi_wwan are
>> > > welcome.
>> > >
>> > > Thanks in advance,
>> > > Daniele
>> >
>> >
>> > Hi
>> >
>> > I havent used qmi_wwan so the following comment is based on code
>> > inspection.
>> > qmimux_register_device() is creating qmimux devices with usb net
>> > device as
>> > real_dev. The Multiplexing and aggregation header (qmimux_hdr) is
>> > stripped
>> > off
>> > in qmimux_rx_fixup() and the packet is passed on to stack.
>> >
>> > You could instead create rmnet devices with the usb netdevice as
>> > real dev.
>> > The packets from the usb net driver can be queued to network stack
>> > directly
>> > as rmnet driver will setup a RX handler. rmnet driver will process
>> > the
>> > packets
>> > further and then queue to network stack.
>> >
>> 
>> in kernel documentation I read that rmnet user space configuration is
>> done through librmnetctl available at
>> 
>> https://source.codeaurora.org/quic/la/platform/vendor/qcom-opensource
>> /dataservices/tree/rmnetctl
>> 
>> However it seems to me that this is a bit outdated (e.g. it does not
>> properly build since it is looking for kernel header
>> linux/rmnet_data.h that, as far as I understand, is no more present).
>> 
>> Is there available a more recent version of the tool?

Hi Daniele

The attached patch should have an updated version of the tool.
Usage -

rmnetcli -n newlink wwan0 rmnet0 1 1
where wwan0 is the physical device
rmnet0 is the virtual device to be created
1 is the mux id
the other 1 is the flag to configure DL de-aggregation by default

To delete a device -

ip link delete rmnet0

> 
> I'd expect that somebody (Subash?) would add support for the
> rmnet/qmimux options to iproute2 via 'ip link' like exists for most
> other device types.

Hi Dan

Yes, I can do that and update the documentation to point to using 
iproute2.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-librmnetctl-add-initial-library-code.patch --]
[-- Type: text/x-diff; name=0001-librmnetctl-add-initial-library-code.patch, Size: 97800 bytes --]

From 94abb589a8e60c49d9cc4598a27b50c9f757b623 Mon Sep 17 00:00:00 2001
From: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
Date: Tue, 5 Jun 2018 11:20:56 -0600
Subject: [PATCH] librmnetctl: add initial library code

Add initial snapshot of Rmnet Control library for
upstream rmnet driver.

---
 rmnetlib/Makefile.am            |    2 +
 rmnetlib/cli/Makefile.am        |   12 +
 rmnetlib/cli/rmnetcli.c         |  502 ++++++++++++++
 rmnetlib/cli/rmnetcli.h         |   61 ++
 rmnetlib/inc/librmnetctl.h      |  604 +++++++++++++++++
 rmnetlib/inc/librmnetctl_hndl.h |   65 ++
 rmnetlib/src/Makefile.am        |   14 +
 rmnetlib/src/librmnetctl.c      | 1369 +++++++++++++++++++++++++++++++++++++++
 8 files changed, 2629 insertions(+)
 create mode 100644 rmnetlib/Makefile.am
 create mode 100644 rmnetlib/cli/Makefile.am
 create mode 100644 rmnetlib/cli/rmnetcli.c
 create mode 100644 rmnetlib/cli/rmnetcli.h
 create mode 100644 rmnetlib/inc/librmnetctl.h
 create mode 100644 rmnetlib/inc/librmnetctl_hndl.h
 create mode 100644 rmnetlib/src/Makefile.am
 create mode 100644 rmnetlib/src/librmnetctl.c

diff --git a/rmnetlib/Makefile.am b/rmnetlib/Makefile.am
new file mode 100644
index 0000000..7cafa82
--- /dev/null
+++ b/rmnetlib/Makefile.am
@@ -0,0 +1,2 @@
+SUBDIRS=src cli
+
diff --git a/rmnetlib/cli/Makefile.am b/rmnetlib/cli/Makefile.am
new file mode 100644
index 0000000..499ef88
--- /dev/null
+++ b/rmnetlib/cli/Makefile.am
@@ -0,0 +1,12 @@
+AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
+AM_CFLAGS += -Wall -Werror -Wundef -Wstrict-prototypes -Wno-trigraphs -g
+AM_CFLAGS += -I./../inc
+AM_LDFLAGS = $(CODE_COVERAGE_LDFLAGS)
+
+rmnetcli_SOURCES = rmnetcli.c
+bin_PROGRAMS = rmnetcli
+requiredlibs = ../src/librmnetctl.la
+rmnetcli_LDADD = $(requiredlibs)
+LOCAL_MODULE := librmnetctl
+LOCAL_PRELINK_MODULE := false
+include $(BUILD_SHARED_LIBRARY)
diff --git a/rmnetlib/cli/rmnetcli.c b/rmnetlib/cli/rmnetcli.c
new file mode 100644
index 0000000..dc81054
--- /dev/null
+++ b/rmnetlib/cli/rmnetcli.c
@@ -0,0 +1,502 @@
+/******************************************************************************
+
+			R M N E T C L I . C
+
+Copyright (c) 2013-2015, 2017-2018 The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+	* Redistributions of source code must retain the above copyright
+	  notice, this list of conditions and the following disclaimer.
+	* Redistributions in binary form must reproduce the above
+	  copyright notice, this list of conditions and the following
+	  disclaimer in the documentation and/or other materials provided
+	  with the distribution.
+	* Neither the name of The Linux Foundation nor the names of its
+	  contributors may be used to endorse or promote products derived
+	  from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+/******************************************************************************
+
+  @file    rmnetcli.c
+  @brief   command line interface to expose rmnet control API's
+
+  DESCRIPTION
+  File containing implementation of the command line interface to expose the
+  rmnet control configuration .
+
+******************************************************************************/
+
+/*===========================================================================
+				INCLUDE FILES
+===========================================================================*/
+
+#include <sys/socket.h>
+#include <stdint.h>
+#include <linux/netlink.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "rmnetcli.h"
+#include "librmnetctl.h"
+
+#define RMNET_MAX_STR_LEN  16
+
+#define _RMNETCLI_CHECKNULL(X)		do { if (!X) {                         \
+print_rmnet_api_status(RMNETCTL_INVALID_ARG, RMNETCTL_CFG_FAILURE_NO_COMMAND); \
+				rmnetctl_cleanup(handle);                      \
+				return RMNETCTL_INVALID_ARG;                   \
+		} } while (0);
+#define _STRTOUI32(X)           (uint32_t)strtoul(X, NULL, 0)
+#define _STRTOUI16(X)           (uint16_t)strtoul(X, NULL, 0)
+#define _STRTOUI8(X)           (uint8_t)strtoul(X, NULL, 0)
+#define _STRTOI32(X)           (int32_t)strtol(X, NULL, 0)
+
+#define _5TABS 		"\n\t\t\t\t\t"
+#define _2TABS 		"\n\t\t"
+
+/*!
+* @brief Contains a list of error message from CLI
+*/
+char rmnetcfg_error_code_text
+[RMNETCFG_TOTAL_ERR_MSGS][RMNETCTL_ERR_MSG_SIZE] = {
+	"Help option Specified",
+	"ERROR: No\\Invalid command was specified\n",
+	"ERROR: Could not allocate buffer for Egress device\n"
+};
+
+/*!
+* @brief Method to display the syntax for the commands
+* @details Displays the syntax and usage for the commands
+* @param void
+* @return void
+*/
+static void rmnet_api_usage(void)
+{
+	printf("RmNet API Usage:\n\n");
+	printf("rmnetcli help                            Displays this help\n");
+	printf("\n");
+	printf("rmnetcli assocnetdev <dev_name>          Registers the RmNet");
+	printf(_5TABS" data driver on a particular");
+	printf(_5TABS" device.dev_name cannot");
+	printf(_5TABS" be larger than 15");
+	printf(_5TABS" characters. Returns");
+	printf(_5TABS" the status code.\n\n");
+	printf("rmnetcli unassocnetdev <dev_name>        Unregisters the");
+	printf(_5TABS" RmNet data driver on a particular");
+	printf(_5TABS" device. dev_name cannot");
+	printf(_5TABS" be larger than 15");
+	printf(_5TABS" characters. Returns");
+	printf(_5TABS" the status code.\n\n");
+	printf("rmnetcli getnetdevassoc <dev_name>       Get if the RmNet");
+	printf(_5TABS" data driver is registered on");
+	printf(_5TABS" a particular device.");
+	printf(_5TABS" dev_name cannot be");
+	printf(_5TABS" larger than 15");
+	printf(_5TABS" characters. Returns 1");
+	printf(_5TABS" if is registered and");
+	printf(_5TABS" 0 if it is not");
+	printf(_5TABS" registered\n\n");
+	printf("rmnetcli setledf <egress_flags>          Sets the egress data");
+	printf(_2TABS" <agg_size>              format for a particular link.");
+	printf(_2TABS" <agg_count>             dev_name cannot be larger");
+	printf(_2TABS" <dev_name>              than 15 characters.");
+	printf(_5TABS" Returns the status code\n\n");
+	printf("rmnetcli getledf <dev_name>              Gets the egress data");
+	printf(_5TABS" format for a particular link.");
+	printf(_5TABS" dev_name cannot be larger");
+	printf(_5TABS" than 15. Returns the 4");
+	printf(_5TABS" byte unsigned integer");
+	printf(_5TABS" egress_flags\n\n");
+	printf("rmnetcli setlidf <ingress_flags>         Sets the ingress");
+	printf(_2TABS" <tail_spacing>          data format for a particular");
+	printf(_2TABS" <dev_name>              link. ingress_flags is 4");
+	printf(_5TABS" byte unsigned integer.");
+	printf(_5TABS" tail_spacing is a one.");
+	printf(_5TABS" byte unsigned integer.");
+	printf(_5TABS" dev_name cannot be");
+	printf(_5TABS" larger than 15.");
+	printf(_5TABS" characters. Returns");
+	printf(_5TABS" the status code\n\n");
+	printf("rmnetcli getlidf <dev_name>              Gets the ingress");
+	printf(_5TABS" data format for a particular");
+	printf(_5TABS" link. dev_name cannot be");
+	printf(_5TABS" larger than 15. Returns");
+	printf(_5TABS" the 4 byte unsigned");
+	printf(_5TABS" integer ingress_flags\n\n");
+	printf("rmnetcli setlepc <logical_ep_id>         Sets the logical");
+	printf(_2TABS" <rmnet_mode>            endpoint configuration for");
+	printf(_2TABS" <dev_name>              a particular link.");
+	printf(_2TABS" <egress_dev_name>       logical_ep_id are 32bit");
+	printf(_5TABS" integers from -1 to 31.");
+	printf(_5TABS" rmnet_mode is a 1 byte");
+	printf(_5TABS" unsigned integer of");
+	printf(_5TABS" value none, vnd or");
+	printf(_5TABS" bridged. dev_name");
+	printf(_5TABS" and egress_dev_name");
+	printf(_5TABS" cannot be larger");
+	printf(_5TABS" than 15 characters");
+	printf(_5TABS" Returns the status code\n\n");
+	printf("rmnetcli unsetlepc <logical_ep_id>       Un-sets the logical");
+	printf(_2TABS"  <dev_name>              endpoint configuration for");
+	printf(_5TABS" a particular link.");
+	printf(_5TABS" integers from -1 to 31.");
+	printf(_5TABS" dev_name cannot be larger");
+	printf(_5TABS" than 15 characters");
+	printf(_5TABS" Returns the status code\n\n");
+	printf("rmnetcli getlepc <logical_ep_id>         Sets the logical");
+	printf(_2TABS" <dev_name>              endpoint configuration for a");
+	printf(_5TABS" particular link.");
+	printf(_5TABS" logical_ep_id are 32bit");
+	printf(_5TABS" integers from -1 to 31.");
+	printf(_5TABS" Returns the rmnet_mode");
+	printf(_5TABS" and egress_dev_name.");
+	printf(_5TABS" rmnet_mode is a 1");
+	printf(_5TABS" byte unsigned integer");
+	printf(_5TABS" of value none, vnd or");
+	printf(_5TABS" bridged. dev_name and");
+	printf(_5TABS" egress_dev_name cannot be");
+	printf(_5TABS" larger than 15 ");
+	printf(_5TABS" characters. Returns the");
+	printf(_5TABS" status code\n\n");
+	printf("rmnetcli newvnd <dev_id>                 Creates a new");
+	printf(_5TABS" virtual network device node.");
+	printf(_5TABS" dev_id is an int");
+	printf(_5TABS" less than 32. Returns");
+	printf(_5TABS" the status code\n\n");
+	printf("rmnetcli newvndprefix <dev_id> <name_prefix>   Creates");
+	printf(_5TABS" virtual network device node.");
+	printf(_5TABS" dev_id is an int");
+	printf(_5TABS" less than 32. Prefix");
+	printf(_5TABS" must be less than");
+	printf(_5TABS" 15 chars. Returns");
+	printf(_5TABS" the status code\n\n");
+	printf("rmnetcli newvndname <dev_id> <name_prefix>   Creates");
+	printf(_5TABS" virtual network device node.");
+	printf(_5TABS" dev_id is an int");
+	printf(_5TABS" less than 32. Name");
+	printf(_5TABS" must be less than");
+	printf(_5TABS" 15 chars. Returns");
+	printf(_5TABS" the status code\n\n");
+	printf("rmnetcli getvndname <dev_id>              Get name of");
+	printf(_5TABS" network device node from id\n\n");
+	printf("rmnetcli freevnd <dev_id>              Removes virtual");
+	printf(_5TABS" network device node. dev_name");
+	printf(_5TABS" cannot be larger than 15.");
+	printf(_5TABS" Returns the status code\n\n");
+	printf("rmnetcli addvnctcflow <dev_id>            Add a modem flow");
+	printf(_2TABS" <mdm_flow_hndl>         handle - tc flow handle");
+	printf(_2TABS" <tc_flow_hndl>          mapping for a virtual network");
+	printf(_2TABS" device node\n\n");
+	printf("rmnetcli delvnctcflow <dev_id>            Delete a modem flow");
+	printf(_2TABS" <mdm_flow_hndl>         handle - tc flow handle");
+	printf(_2TABS" <tc_flow_hndl>          mapping for a virtual network");
+	printf(_2TABS" device node\n\n");
+	printf("**************************\n");
+	printf("RmNet RTM_NETLINK API Usage:\n\n");
+	printf("rmnetcli -n newlink  <dev_id>            Add a vnd w/ newlink");
+	printf(_2TABS" <vnd>                   string - vnd device_name");
+	printf(_2TABS" <vnd id>                int - new vnd id");
+	printf(_2TABS" [flags]                 int - starting flag config\n\n");
+	printf("rmnetcli -n changelink  <dev_id>         Change a vnd's flags");
+	printf(_2TABS" <vnd>                   string - vnd device_name");
+	printf(_2TABS" <vnd id>                int - new vnd id");
+	printf(_2TABS" <flags>                 int - new flag config\n\n");
+	printf("rmnetcli -n dellink <dev_name>           Delete a vnd");
+	printf(_2TABS"                         by inputting dev name\n\n");
+	printf("rmnetcli -n bridgelink  <dev_name>       Bridge a vnd and a dev");
+	printf(_2TABS" <vnd id>                by specifying dev id and vnd id\n\n");
+
+}
+
+static void print_rmnetctl_lib_errors(uint16_t error_number)
+{
+	if ((error_number > RMNETCTL_API_SUCCESS) &&
+		(error_number < RMNETCTL_API_ERR_ENUM_LENGTH)) {
+		printf("%s", rmnetctl_error_code_text[error_number]);
+	}
+	if ((error_number >= RMNETCFG_ERR_NUM_START) &&
+	(error_number < RMNETCFG_ERR_NUM_START + RMNETCFG_TOTAL_ERR_MSGS)) {
+		printf("%s", rmnetcfg_error_code_text
+			[error_number - RMNETCFG_ERR_NUM_START]);
+		if ((error_number == RMNETCTL_CFG_SUCCESS_HELP_COMMAND) ||
+			(error_number == RMNETCTL_CFG_FAILURE_NO_COMMAND))
+			rmnet_api_usage();
+	}
+}
+
+/*!
+* @brief Method to check the error numbers generated from API calls
+* @details Displays the error messages based on each error code
+* @param error_number Error number returned from the API and the CLI
+* @return void
+*/
+static void print_rmnet_api_status(int return_code, uint16_t error_number)
+{
+	if (return_code == RMNETCTL_SUCCESS)
+		printf("SUCCESS\n");
+	else if (return_code == RMNETCTL_LIB_ERR) {
+		printf("LIBRARY ");
+		print_rmnetctl_lib_errors(error_number);
+	} else if (return_code == RMNETCTL_KERNEL_ERR) {
+		if (error_number < RMNETCTL_API_ERR_ENUM_LENGTH)
+			printf("KERNEL ERROR: System or rmnet error %d\n",
+			       error_number);
+	}
+	else if (return_code == RMNETCTL_INVALID_ARG)
+		printf("INVALID_ARG\n");
+}
+
+/*!
+* @brief Method to make the API calls
+* @details Checks for each type of parameter and calls the appropriate
+* function based on the number of parameters and parameter type
+* @param argc Number of arguments which vary based on the commands
+* @param argv Value of the arguments which vary based on the commands
+* @return RMNETCTL_SUCCESS if successful. Relevant data might be printed
+* based on the message type
+* @return RMNETCTL_LIB_ERR if there was a library error. Error code will be
+* printed
+* @return RMNETCTL_KERNEL_ERR if there was a error in the kernel. Error code will be
+* printed
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+
+static int rmnet_api_call(int argc, char *argv[])
+{
+	struct rmnetctl_hndl_s *handle = NULL;
+	uint16_t error_number = RMNETCTL_CFG_FAILURE_NO_COMMAND;
+	int return_code = RMNETCTL_LIB_ERR;
+	uint32_t flags = 0;
+	uint8_t rmnet_mode;
+	char *egress_dev_name;
+	if ((!argc) || (!*argv)) {
+		print_rmnet_api_status(RMNETCTL_LIB_ERR,
+		RMNETCTL_CFG_FAILURE_NO_COMMAND);
+		return RMNETCTL_LIB_ERR;
+	}
+	if (!strcmp(*argv, "help")) {
+		print_rmnet_api_status(RMNETCTL_LIB_ERR,
+		RMNETCTL_CFG_SUCCESS_HELP_COMMAND);
+		return RMNETCTL_LIB_ERR;
+	}
+
+	if (!strcmp(*argv, "-n")) {
+		return_code = rtrmnet_ctl_init(&handle, &error_number);
+		if (return_code != RMNETCTL_SUCCESS) {
+			print_rmnet_api_status(return_code, error_number);
+			return RMNETCTL_LIB_ERR;
+		}
+		error_number = RMNETCTL_CFG_FAILURE_NO_COMMAND;
+		return_code = RMNETCTL_LIB_ERR;
+		argv++;
+		argc--;
+		if ((!argc) || (!*argv)) {
+			print_rmnet_api_status(RMNETCTL_LIB_ERR,
+			RMNETCTL_CFG_FAILURE_NO_COMMAND);
+			return RMNETCTL_LIB_ERR;
+		}
+		if (!strcmp(*argv, "newlink")) {
+			_RMNETCLI_CHECKNULL(argv[1]);
+			_RMNETCLI_CHECKNULL(argv[2]);
+			_RMNETCLI_CHECKNULL(argv[3]);
+			/* If optional flag was used pass it on*/
+			if (argv[4])
+				flags = _STRTOI32(argv[4]);
+
+			return_code = rtrmnet_ctl_newvnd(handle, argv[1],
+							 argv[2],
+							 &error_number,
+							 _STRTOI32(argv[3]),
+							 flags);
+		} else if (!strcmp(*argv, "changelink")) {
+			_RMNETCLI_CHECKNULL(argv[1]);
+			_RMNETCLI_CHECKNULL(argv[2]);
+			_RMNETCLI_CHECKNULL(argv[3]);
+			_RMNETCLI_CHECKNULL(argv[4]);
+
+			return_code = rtrmnet_ctl_changevnd(handle, argv[1],
+							    argv[2],
+							    &error_number,
+							    _STRTOI32(argv[3]),
+							    _STRTOI32(argv[4]));
+		} else if (!strcmp(*argv, "dellink")) {
+			_RMNETCLI_CHECKNULL(argv[1]);
+				return_code = rtrmnet_ctl_delvnd(handle, argv[1],
+								 &error_number);
+		} else if (!strcmp(*argv, "bridge")) {
+			_RMNETCLI_CHECKNULL(argv[1]);
+			_RMNETCLI_CHECKNULL(argv[2]);
+			return_code = rtrmnet_ctl_bridgevnd(handle, argv[1],
+							    argv[2],
+							    &error_number);
+		}
+		goto end;
+	} else {
+		return_code = rmnetctl_init(&handle, &error_number);
+		if (return_code != RMNETCTL_SUCCESS) {
+			print_rmnet_api_status(return_code, error_number);
+			return RMNETCTL_LIB_ERR;
+		}
+
+	}
+	error_number = RMNETCTL_CFG_FAILURE_NO_COMMAND;
+	return_code = RMNETCTL_LIB_ERR;
+	if (!strcmp(*argv, "assocnetdev")) {
+		return_code = rmnet_associate_network_device(handle,
+		argv[1], &error_number, RMNETCTL_DEVICE_ASSOCIATE);
+	} else if (!strcmp(*argv, "unassocnetdev")) {
+		return_code = rmnet_associate_network_device(handle,
+		argv[1], &error_number, RMNETCTL_DEVICE_UNASSOCIATE);
+	} else if (!strcmp(*argv, "getnetdevassoc")) {
+		int register_status;
+		return_code = rmnet_get_network_device_associated(handle,
+		argv[1], &register_status, &error_number);
+		if (return_code == RMNETCTL_SUCCESS)
+			printf("register_status is %d\n", register_status);
+	} else if (!strcmp(*argv, "getledf")) {
+		uint32_t egress_flags;
+		uint16_t agg_size, agg_count;
+		return_code = rmnet_get_link_egress_data_format(handle,
+		argv[1], &egress_flags, &agg_size, &agg_count, &error_number);
+		if (return_code == RMNETCTL_SUCCESS) {
+			printf("egress_flags is %u\n", egress_flags);
+			printf("agg_size is %u\n", agg_size);
+			printf("agg_count is %u\n", agg_count);
+		}
+	} else if (!strcmp(*argv, "getlidf")) {
+		uint32_t ingress_flags;
+		uint8_t  tail_spacing;
+		return_code = rmnet_get_link_ingress_data_format_tailspace(
+		handle, argv[1], &ingress_flags, &tail_spacing, &error_number);
+		if (return_code == RMNETCTL_SUCCESS) {
+			printf("ingress_flags is %u\n", ingress_flags);
+			printf("tail_spacing is %u\n", tail_spacing);
+		}
+	} else if (!strcmp(*argv, "newvndprefix")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		_RMNETCLI_CHECKNULL(argv[2]);
+		return_code = rmnet_new_vnd_prefix(handle,
+		_STRTOUI32(argv[1]), &error_number, RMNETCTL_NEW_VND, argv[2]);
+	} else if (!strcmp(*argv, "newvndname")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		_RMNETCLI_CHECKNULL(argv[2]);
+		return_code = rmnet_new_vnd_name(handle,
+		_STRTOUI32(argv[1]), &error_number, argv[2]);
+	} else if (!strcmp(*argv, "newvnd")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		return_code = rmnet_new_vnd(handle,
+		_STRTOUI32(argv[1]), &error_number, RMNETCTL_NEW_VND);
+	} else if (!strcmp(*argv, "getvndname")) {
+		char buffer[32];
+		memset(buffer, 0, 32);
+		_RMNETCLI_CHECKNULL(argv[1]);
+		return_code = rmnet_get_vnd_name(handle, _STRTOUI32(argv[1]),
+			           &error_number, buffer, 32);
+		if (return_code == RMNETCTL_SUCCESS) {
+			printf("VND name: %s\n", buffer);
+		}
+	} else if (!strcmp(*argv, "freevnd")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		return_code = rmnet_new_vnd(handle,
+		_STRTOUI32(argv[1]), &error_number, RMNETCTL_FREE_VND);
+	} else if (!strcmp(*argv, "setlidf")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		_RMNETCLI_CHECKNULL(argv[2]);
+		_RMNETCLI_CHECKNULL(argv[3]);
+		return_code = rmnet_set_link_ingress_data_format_tailspace(
+		handle, _STRTOUI32(argv[1]), _STRTOUI8(argv[2]), argv[3],
+		&error_number);
+	} else if (!strcmp(*argv, "delvnctcflow")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		_RMNETCLI_CHECKNULL(argv[2]);
+		_RMNETCLI_CHECKNULL(argv[3]);
+		return_code = rmnet_add_del_vnd_tc_flow(handle,
+		_STRTOUI32(argv[1]), _STRTOUI32(argv[2]), _STRTOUI32(argv[3]),
+		RMNETCTL_DEL_FLOW, &error_number);
+	} else if (!strcmp(*argv, "getlepc")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		egress_dev_name = NULL;
+		egress_dev_name = (char *)malloc(RMNET_MAX_STR_LEN
+		* sizeof(char));
+		if (!egress_dev_name) {
+			print_rmnet_api_status(RMNETCTL_LIB_ERR,
+			RMNETCTL_CFG_FAILURE_EGRESS_DEV_NAME_NULL);
+			rmnetctl_cleanup(handle);
+			return RMNETCTL_LIB_ERR;
+		}
+		return_code = rmnet_get_logical_ep_config(handle,
+		_STRTOI32(argv[1]), argv[2], &rmnet_mode,
+		&egress_dev_name, RMNET_MAX_STR_LEN, &error_number);
+		if (return_code == RMNETCTL_SUCCESS) {
+			printf("rmnet_mode is %u\n", rmnet_mode);
+			printf("egress_dev_name is %s\n", egress_dev_name);
+		}
+		free(egress_dev_name);
+	} else if (!strcmp(*argv, "addvnctcflow")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		_RMNETCLI_CHECKNULL(argv[2]);
+		_RMNETCLI_CHECKNULL(argv[3]);
+		return_code = rmnet_add_del_vnd_tc_flow(handle,
+		_STRTOUI32(argv[1]), _STRTOUI32(argv[2]), _STRTOUI32(argv[3]),
+		RMNETCTL_ADD_FLOW, &error_number);
+	} else if (!strcmp(*argv, "setledf")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		_RMNETCLI_CHECKNULL(argv[2]);
+		_RMNETCLI_CHECKNULL(argv[3]);
+		return_code = rmnet_set_link_egress_data_format(handle,
+		_STRTOUI32(argv[1]), _STRTOUI16(argv[2]), _STRTOUI16(argv[3]),
+		argv[4], &error_number);
+	} else if (!strcmp(*argv, "setlepc")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		_RMNETCLI_CHECKNULL(argv[2]);
+		return_code = rmnet_set_logical_ep_config(handle,
+		_STRTOI32(argv[1]), _STRTOUI8(argv[2]), argv[3], argv[4],
+		&error_number);
+	} else if (!strcmp(*argv, "unsetlepc")) {
+		_RMNETCLI_CHECKNULL(argv[1]);
+		return_code = rmnet_unset_logical_ep_config(handle,
+		_STRTOI32(argv[1]), argv[2], &error_number);
+	}
+end:
+	print_rmnet_api_status(return_code, error_number);
+	(void)rtrmnet_ctl_deinit(handle);
+	return return_code;
+}
+
+/*!
+* @brief Method which serves as en entry point to the rmnetcli function
+* @details Entry point for the RmNet Netlink API. This is the command line
+* interface for the RmNet API
+* @param argc Number of arguments which vary based on the commands
+* @param argv Value of the arguments which vary based on the commands
+* @return RMNETCTL_SUCCESS if successful. Relevant data might be printed
+* based on the message type
+* @return RMNETCTL_LIB_ERR if there was a library error. Error code will be
+* printed
+* @return RMNETCTL_KERNEL_ERR if there was a error in the kernel. Error code will be
+* printed
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int main(int argc, char *argv[])
+{
+	argc--;
+	argv++;
+	return rmnet_api_call(argc, argv);
+}
diff --git a/rmnetlib/cli/rmnetcli.h b/rmnetlib/cli/rmnetcli.h
new file mode 100644
index 0000000..6375082
--- /dev/null
+++ b/rmnetlib/cli/rmnetcli.h
@@ -0,0 +1,61 @@
+/******************************************************************************
+
+			  R M N E T C L I . H
+
+Copyright (c) 2013, 2015 The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+	* Redistributions of source code must retain the above copyright
+	  notice, this list of conditions and the following disclaimer.
+	* Redistributions in binary form must reproduce the above
+	  copyright notice, this list of conditions and the following
+	  disclaimer in the documentation and/or other materials provided
+	  with the distribution.
+	* Neither the name of The Linux Foundation nor the names of its
+	  contributors may be used to endorse or promote products derived
+	  from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+/******************************************************************************
+
+  @file	   rmnetcli.h
+  @brief   headers for the command line interface to expose rmnet control API's
+
+  DESCRIPTION
+  Header file containing definition for the command line interface to expose
+  rmnet control API's
+
+******************************************************************************/
+
+#ifndef RMNETCLI_H
+#define RMNETCLI_H
+
+/* Print the help for the commands since the help flag was used. */
+#define RMNETCTL_CFG_SUCCESS_HELP_COMMAND 100
+/* No/invalid API call was specified. So return an error. */
+#define RMNETCTL_CFG_FAILURE_NO_COMMAND 101
+/* The buffer for egress device name was NULL */
+#define RMNETCTL_CFG_FAILURE_EGRESS_DEV_NAME_NULL 102
+
+/* This should always be the value of the starting element */
+#define RMNETCFG_ERR_NUM_START 100
+
+/* This should always be the total number of error message from CLI */
+#define RMNETCFG_TOTAL_ERR_MSGS 3
+
+#endif /* not defined RMNETCLI_H */
diff --git a/rmnetlib/inc/librmnetctl.h b/rmnetlib/inc/librmnetctl.h
new file mode 100644
index 0000000..3d622bf
--- /dev/null
+++ b/rmnetlib/inc/librmnetctl.h
@@ -0,0 +1,604 @@
+/******************************************************************************
+
+			  L I B R M N E T C T L . H
+
+Copyright (c) 2013-2015, 2017-2018 The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+	* Redistributions of source code must retain the above copyright
+	  notice, this list of conditions and the following disclaimer.
+	* Redistributions in binary form must reproduce the above
+	  copyright notice, this list of conditions and the following
+	  disclaimer in the documentation and/or other materials provided
+	  with the distribution.
+	* Neither the name of The Linux Foundation nor the names of its
+	  contributors may be used to endorse or promote products derived
+	  from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+/*!
+*  @file    librmnetctl.h
+*  @brief   rmnet control API's header file
+*/
+
+#ifndef LIBRMNETCTL_H
+#define LIBRMNETCTL_H
+
+/* RMNET API succeeded */
+#define RMNETCTL_SUCCESS 0
+/* RMNET API encountered an error while executing within the library. Check the
+* error code in this case */
+#define RMNETCTL_LIB_ERR 1
+/* RMNET API encountered an error while executing in the kernel. Check the
+* error code in this case */
+#define RMNETCTL_KERNEL_ERR 2
+/* RMNET API encountered an error because of invalid arguments*/
+#define RMNETCTL_INVALID_ARG 3
+
+/* Flag to associate a network device*/
+#define RMNETCTL_DEVICE_ASSOCIATE 1
+/* Flag to unassociate a network device*/
+#define RMNETCTL_DEVICE_UNASSOCIATE 0
+/* Flag to create a new virtual network device*/
+#define RMNETCTL_NEW_VND 1
+/* Flag to free a new virtual network device*/
+#define RMNETCTL_FREE_VND 0
+/* Flag to add a new flow*/
+#define RMNETCTL_ADD_FLOW 1
+/* Flag to delete an existing flow*/
+#define RMNETCTL_DEL_FLOW 0
+
+enum rmnetctl_error_codes_e {
+	/* API succeeded. This should always be the first element. */
+	RMNETCTL_API_SUCCESS = 0,
+
+	RMNETCTL_API_FIRST_ERR = 1,
+	/* API failed because not enough memory to create buffer to send
+	 * message */
+	RMNETCTL_API_ERR_REQUEST_INVALID = RMNETCTL_API_FIRST_ERR,
+	/* API failed because not enough memory to create buffer for the
+	 *  response message */
+	RMNETCTL_API_ERR_RESPONSE_INVALID = 2,
+	/* API failed because could not send the message to kernel */
+	RMNETCTL_API_ERR_MESSAGE_SEND = 3,
+	/* API failed because could not receive message from the kernel */
+	RMNETCTL_API_ERR_MESSAGE_RECEIVE = 4,
+
+	RMNETCTL_INIT_FIRST_ERR = 5,
+	/* Invalid process id. So return an error. */
+	RMNETCTL_INIT_ERR_PROCESS_ID = RMNETCTL_INIT_FIRST_ERR,
+	/* Invalid socket descriptor id. So return an error. */
+	RMNETCTL_INIT_ERR_NETLINK_FD = 6,
+	/* Could not bind the socket to the Netlink file descriptor */
+	RMNETCTL_INIT_ERR_BIND = 7,
+	/* Invalid user id. Only root has access to this function. (NA) */
+	RMNETCTL_INIT_ERR_INVALID_USER = 8,
+
+	RMNETCTL_API_SECOND_ERR = 9,
+	/* API failed because the RmNet handle for the transaction was NULL */
+	RMNETCTL_API_ERR_HNDL_INVALID = RMNETCTL_API_SECOND_ERR,
+	/* API failed because the request buffer for the transaction was NULL */
+	RMNETCTL_API_ERR_REQUEST_NULL = 10,
+	/* API failed because the response buffer for the transaction was NULL*/
+	RMNETCTL_API_ERR_RESPONSE_NULL = 11,
+	/* API failed because the request and response type do not match*/
+	RMNETCTL_API_ERR_MESSAGE_TYPE = 12,
+	/* API failed because the return type is invalid */
+	RMNETCTL_API_ERR_RETURN_TYPE = 13,
+	/* API failed because the string was truncated */
+	RMNETCTL_API_ERR_STRING_TRUNCATION = 14,
+
+	/* These error are 1-to-1 with rmnet_data config errors in rmnet_data.h
+	   for each conversion.
+	   please keep the enums synced.
+	*/
+	RMNETCTL_KERNEL_FIRST_ERR = 15,
+	/* No error */
+	RMNETCTL_KERNEL_ERROR_NO_ERR = RMNETCTL_KERNEL_FIRST_ERR,
+	/* Invalid / unsupported message */
+	RMNETCTL_KERNEL_ERR_UNKNOWN_MESSAGE = 16,
+	/* Internal problem in the kernel module */
+	RMNETCTL_KERNEL_ERR_INTERNAL = 17,
+	/* Kernel is temporarily out of memory */
+	RMNETCTL_KERNEL_ERR_OUT_OF_MEM = 18,
+	/* Device already exists / Still in use */
+	RMETNCTL_KERNEL_ERR_DEVICE_IN_USE = 19,
+	/* Invalid request / Unsupported scenario */
+	RMNETCTL_KERNEL_ERR_INVALID_REQUEST = 20,
+	/* Device doesn't exist */
+	RMNETCTL_KERNEL_ERR_NO_SUCH_DEVICE = 21,
+	/* One or more of the arguments is invalid */
+	RMNETCTL_KERNEL_ERR_BAD_ARGS = 22,
+	/* Egress device is invalid */
+	RMNETCTL_KERNEL_ERR_BAD_EGRESS_DEVICE = 23,
+	/* TC handle is full */
+	RMNETCTL_KERNEL_ERR_TC_HANDLE_FULL = 24,
+
+	/* This should always be the last element */
+	RMNETCTL_API_ERR_ENUM_LENGTH
+};
+
+#define RMNETCTL_ERR_MSG_SIZE 100
+
+/*!
+* @brief Contains a list of error message from API
+*/
+char rmnetctl_error_code_text
+[RMNETCTL_API_ERR_ENUM_LENGTH][RMNETCTL_ERR_MSG_SIZE] = {
+	"ERROR: API succeeded\n",
+	"ERROR: Unable to allocate the buffer to send message\n",
+	"ERROR: Unable to allocate the buffer to receive message\n",
+	"ERROR: Could not send the message to kernel\n",
+	"ERROR: Unable to receive message from the kernel\n",
+	"ERROR: Invalid process id\n",
+	"ERROR: Invalid socket descriptor id\n",
+	"ERROR: Could not bind to netlink socket\n",
+	"ERROR: Only root can access this API\n",
+	"ERROR: RmNet handle for the transaction was NULL\n",
+	"ERROR: Request buffer for the transaction was NULL\n",
+	"ERROR: Response buffer for the transaction was NULL\n",
+	"ERROR: Request and response type do not match\n",
+	"ERROR: Return type is invalid\n",
+	"ERROR: String was truncated\n",
+	/* Kernel errors */
+	"ERROR: Kernel call succeeded\n",
+	"ERROR: Invalid / Unsupported directive\n",
+	"ERROR: Internal problem in the kernel module\n",
+	"ERROR: The kernel is temporarily out of memory\n",
+	"ERROR: Device already exists / Still in use\n",
+	"ERROR: Invalid request / Unsupported scenario\n",
+	"ERROR: Device doesn't exist\n",
+	"ERROR: One or more of the arguments is invalid\n",
+	"ERROR: Egress device is invalid\n",
+	"ERROR: TC handle is full\n"
+};
+
+/*===========================================================================
+			 DEFINITIONS AND DECLARATIONS
+===========================================================================*/
+typedef struct rmnetctl_hndl_s rmnetctl_hndl_t;
+
+/*!
+* @brief Public API to initialize the RMNET control driver
+* @details Allocates memory for the RmNet handle. Creates and binds to a   and
+* netlink socket if successful
+* @param **rmnetctl_hndl_t_val RmNet handle to be initialized
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnetctl_init(rmnetctl_hndl_t **hndl, uint16_t *error_code);
+
+/*!
+* @brief Public API to clean up the RmNeT control handle
+* @details Close the socket and free the RmNet handle
+* @param *rmnetctl_hndl_t_val RmNet handle to be initialized
+* @return void
+*/
+void rmnetctl_cleanup(rmnetctl_hndl_t *hndl);
+
+/*!
+* @brief Public API to register/unregister a RMNET driver on a particular device
+* @details Message type is RMNET_NETLINK_ASSOCIATE_NETWORK_DEVICE or
+* RMNET_NETLINK_UNASSOCIATE_NETWORK_DEVICE based on the flag for assoc_dev
+* @param *rmnetctl_hndl_t_val RmNet handle for the Netlink message
+* @param dev_name Device on which to register the RmNet driver
+* @param error_code Status code of this operation
+* @param assoc_dev registers the device if RMNETCTL_DEVICE_ASSOCIATE or
+* unregisters the device if RMNETCTL_DEVICE_UNASSOCIATE
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_associate_network_device(rmnetctl_hndl_t *hndl,
+				   const char *dev_name,
+				   uint16_t *error_code,
+				   uint8_t assoc_dev);
+
+/*!
+* @brief Public API to get if a RMNET driver is registered on a particular
+* device
+* @details Message type is RMNET_NETLINK_GET_NETWORK_DEVICE_ASSOCIATED.
+* @param *rmnetctl_hndl_t_val RmNet handle for the Netlink message
+* @param dev_name Device on which to check if the RmNet driver is registered
+* @param register_status 1 if RmNet data driver is registered on a particular
+* device, 0 if not
+* @param error_code Status code of this operation
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_get_network_device_associated(rmnetctl_hndl_t *hndl,
+					const char *dev_name,
+					int *register_status,
+					uint16_t *error_code);
+
+/*!
+* @brief Public API to set the egress data format for a particular link.
+* @details Message type is RMNET_NETLINK_SET_LINK_EGRESS_DATA_FORMAT.
+* @param *rmnetctl_hndl_t_val RmNet handle for the Netlink message
+* @param egress_flags Egress flags to be set on the device
+* @param agg_size Max size of aggregated packets
+* @param agg_count Number of packets to be aggregated
+* @param dev_name Device on which to set the egress data format
+* @param error_code Status code of this operation returned from the kernel
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_set_link_egress_data_format(rmnetctl_hndl_t *hndl,
+				      uint32_t egress_flags,
+				      uint16_t agg_size,
+				      uint16_t agg_count,
+				      const char *dev_name,
+				      uint16_t *error_code);
+
+/*!
+* @brief Public API to get the egress data format for a particular link.
+* @details Message type is RMNET_NETLINK_GET_LINK_EGRESS_DATA_FORMAT.
+* @param *rmnetctl_hndl_t_val RmNet handle for the Netlink message
+* @param dev_name Device on which to get the egress data format
+* @param egress_flags Egress flags from the device
+* @param agg_count Number of packets to be aggregated
+* @param error_code Status code of this operation returned from the kernel
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_get_link_egress_data_format(rmnetctl_hndl_t *hndl,
+				      const char *dev_name,
+				      uint32_t *egress_flags,
+				      uint16_t *agg_size,
+				      uint16_t *agg_count,
+				      uint16_t *error_code);
+
+/*!
+* @brief Public API to set the ingress data format for a particular link.
+* @details Message type is RMNET_NETLINK_SET_LINK_INGRESS_DATA_FORMAT.
+* @param *rmnetctl_hndl_t_val RmNet handle for the Netlink message
+* @param ingress_flags Ingress flags from the device
+* @param tail_spacing Tail spacing needed for the packet
+* @param dev_name Device on which to set the ingress data format
+* @param error_code Status code of this operation returned from the kernel
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_set_link_ingress_data_format_tailspace(rmnetctl_hndl_t *hndl,
+						 uint32_t ingress_flags,
+						 uint8_t  tail_spacing,
+						 const char *dev_name,
+						 uint16_t *error_code);
+
+/*!
+* @brief Public API to get the ingress data format for a particular link.
+* @details Message type is RMNET_NETLINK_GET_LINK_INGRESS_DATA_FORMAT.
+* @param *rmnetctl_hndl_t_val RmNet handle for the Netlink message
+* @param dev_name Device on which to get the ingress data format
+* @param ingress_flags Ingress flags from the device
+* @param tail_spacing Tail spacing needed for the packet
+* @param error_code Status code of this operation returned from the kernel
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_get_link_ingress_data_format_tailspace(rmnetctl_hndl_t *hndl,
+						const char *dev_name,
+						uint32_t *ingress_flags,
+						uint8_t  *tail_spacing,
+						uint16_t *error_code);
+
+inline int rmnet_set_link_ingress_data_format(rmnetctl_hndl_t *hndl,
+					      uint32_t ingress_flags,
+					      const char *dev_name,
+					      uint16_t *error_code)
+{
+	return rmnet_set_link_ingress_data_format_tailspace(hndl,
+							    ingress_flags,
+							    0,
+							    dev_name,
+							    error_code);
+}
+
+inline int rmnet_get_link_ingress_data_format(rmnetctl_hndl_t *hndl,
+					      const char *dev_name,
+					      uint32_t *ingress_flags,
+					      uint16_t *error_code)
+{
+	return rmnet_get_link_ingress_data_format_tailspace(hndl,
+							    dev_name,
+							    ingress_flags,
+							    0,
+							    error_code);
+}
+
+/*!
+* @brief Public API to set the logical endpoint configuration for a
+* particular link.
+* @details Message type is RMNET_NETLINK_SET_LOGICAL_EP_CONFIG.
+* @param *rmnetctl_hndl_t_val RmNet handle for the Netlink message
+* @param logical_ep_id Logical end point id on which the configuration is to be
+* set
+* @param rmnet_mode RmNet mode to be set on the device
+* @param dev_name Device on which to set the logical end point configuration
+* @param egress_dev_name Egress Device if operating in bridge mode
+* @param error_code Status code of this operation returned from the kernel
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_set_logical_ep_config(rmnetctl_hndl_t *hndl,
+				int32_t ep_id,
+				uint8_t operating_mode,
+				const char *dev_name,
+				const char *next_dev,
+				uint16_t *error_code);
+
+/*!
+* @brief Public API to un-set the logical endpoint configuration for a
+* particular link.
+* @details Message type is RMNET_NETLINK_UNSET_LOGICAL_EP_CONFIG.
+* @param *rmnetctl_hndl_t_val RmNet handle for the Netlink message
+* @param logical_ep_id Logical end point id on which the configuration is to be
+* un-set
+* @param dev_name Device on which to un-set the logical end point configuration
+* @param error_code Status code of this operation returned from the kernel
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_unset_logical_ep_config(rmnetctl_hndl_t *hndl,
+				  int32_t ep_id,
+				  const char *dev_name,
+				  uint16_t *error_code);
+/*!
+* @brief Public API to get the logical endpoint configuration for a
+* particular link.
+* @details Message type is RMNET_NETLINK_GET_LOGICAL_EP_CONFIG.
+* @param *rmnetctl_hndl_t_val RmNet handle for the Netlink message
+* @param logical_ep_id Logical end point id from which to get the configuration
+* @param dev_name Device on which to get the logical end point configuration
+* @param rmnet_mode RmNet mode from the device
+* @param next_dev Egress Device name
+* @param next_dev_len Egress Device I/O string len
+* @param error_code Status code of this operation returned from the kernel
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_get_logical_ep_config(rmnetctl_hndl_t *hndl,
+				int32_t ep_id,
+				const char *dev_name,
+				uint8_t *operating_mode,
+				char **next_dev,
+				uint32_t next_dev_len,
+				uint16_t *error_code);
+
+/*!
+* @brief Public API to create a new virtual device node
+* @details Message type is RMNET_NETLINK_NEW_VND or
+* RMNETCTL_FREE_VND based on the flag for new_vnd
+* @param hndl RmNet handle for the Netlink message
+* @param id Node number to create the virtual network device node
+* @param error_code Status code of this operation returned from the kernel
+* @param new_vnd creates a new virtual network device if  RMNETCTL_NEW_VND or
+* frees the device if RMNETCTL_FREE_VND
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_new_vnd(rmnetctl_hndl_t *hndl,
+		  uint32_t id,
+		  uint16_t *error_code,
+		  uint8_t new_vnd);
+
+/*!
+ * @brief Public API to create a new virtual device node with a custom prefix
+ * @details Message type is RMNET_NETLINK_NEW_VND or
+ * RMNETCTL_FREE_VND based on the flag for new_vnd
+ * @param hndl RmNet handle for the Netlink message
+ * @param id Node number to create the virtual network device node
+ * @param error_code Status code of this operation returned from the kernel
+ * @param new_vnd creates a new virtual network device if  RMNETCTL_NEW_VND or
+ * frees the device if RMNETCTL_FREE_VND
+ * @param prefix Prefix to be used when naming the network interface
+ * @return RMNETCTL_SUCCESS if successful
+ * @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+ * @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+ * Check error_code
+ * @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+ */
+int rmnet_new_vnd_prefix(rmnetctl_hndl_t *hndl,
+			 uint32_t id,
+			 uint16_t *error_code,
+			 uint8_t new_vnd,
+			 const char *prefix);
+
+/*!
+ * @brief Public API to create a new virtual device node with a custom prefix
+ * @details Message type is RMNET_NETLINK_NEW_VND or
+ * RMNETCTL_FREE_VND based on the flag for new_vnd
+ * @param hndl RmNet handle for the Netlink message
+ * @param id Node number to create the virtual network device node
+ * @param error_code Status code of this operation returned from the kernel
+ * @param new_vnd creates a new virtual network device if  RMNETCTL_NEW_VND or
+ * frees the device if RMNETCTL_FREE_VND
+ * @param name Name to be used when naming the network interface
+ * @return RMNETCTL_SUCCESS if successful
+ * @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+ * @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+ * Check error_code
+ * @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+ */
+int rmnet_new_vnd_name(rmnetctl_hndl_t *hndl,
+			 uint32_t id,
+			 uint16_t *error_code,
+			 const char *name);
+
+/*!
+ * @brief API to get the ASCII name of a virtual network device from its ID
+ * @param hndl RmNet handle for the Netlink message
+ * @param id Node number to create the virtual network device node
+ * @param error_code Status code of this operation returned from the kernel
+ * @param buf Buffer to store ASCII representation of device name
+ * @param buflen Length of the buffer
+ * @param prefix Prefix to be used when naming the network interface
+ * @return RMNETCTL_SUCCESS if successful
+ * @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+ * @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+ * Check error_code
+ * @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+ */
+
+int rmnet_get_vnd_name(rmnetctl_hndl_t *hndl,
+                      uint32_t id,
+                      uint16_t *error_code,
+                      char *buf,
+                      uint32_t buflen);
+
+/*!
+* @brief Public API to set or clear a flow
+* @details Message type is RMNET_NETLINK_ADD_VND_TC_FLOW or
+* RMNET_NETLINK_DEL_VND_TC_FLOW based on the flag for set_flow
+* @param *rmnetctl_hndl_t_val RmNet handle for the Netlink message
+* @param id Node number to set or clear the flow on the virtual network
+* device node
+* @param map_flow_id Flow handle of the modem
+* @param tc_flow_id Software flow handle
+* @param set_flow sets the flow if  RMNET_NETLINK_SET_FLOW or
+* clears the flow if RMNET_NETLINK_CLEAR_FLOW
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+int rmnet_add_del_vnd_tc_flow(rmnetctl_hndl_t *hndl,
+			      uint32_t id,
+			      uint32_t map_flow_id,
+			      uint32_t tc_flow_id,
+			      uint8_t set_flow,
+			      uint16_t *error_code);
+
+/* @brief Public API to initialize the RTM_NETLINK RMNET control driver
+ * @details Allocates memory for the RmNet handle. Creates and binds to a
+ * netlink socket if successful
+ * @param **rmnetctl_hndl_t_val RmNet handle to be initialized
+ * @return RMNETCTL_SUCCESS if successful
+ * @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+ * @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+ * Check error_code
+ * @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+ */
+int rtrmnet_ctl_init(rmnetctl_hndl_t **hndl, uint16_t *error_code);
+
+/* @brief Public API to clean up the RTM_NETLINK RmNeT control handle
+ * @details Close the socket and free the RmNet handle
+ * @param *rmnetctl_hndl_t_val RmNet handle to be initialized
+ * @return void
+ */
+int rtrmnet_ctl_deinit(rmnetctl_hndl_t *hndl);
+
+/* @brief Public API to create a new virtual device node
+ * @details Message type is RTM_NEWLINK
+ * @param hndl RmNet handle for the Netlink message
+ * @param dev_name Device name new node will be connected to
+ * @param vnd_name Name of virtual device to be created
+ * @param error_code Status code of this operation returned from the kernel
+ * @param index Index node will have
+ * @param flagconfig Flag configuration device will have
+ * @return RMNETCTL_SUCCESS if successful
+ * @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+ * @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+ * Check error_code
+ * @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+ */
+int rtrmnet_ctl_newvnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname,
+		       uint16_t *error_code, uint8_t  index,
+		       uint32_t flagconfig);
+
+/* @brief Public API to delete a virtual device node
+ * @details Message type is RTM_DELLINK
+ * @param hndl RmNet handle for the Netlink message
+ * @param vnd_name Name of virtual device to be deleted
+ * @param error_code Status code of this operation returned from the kernel
+ * @return RMNETCTL_SUCCESS if successful
+ * @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+ * @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+ * Check error_code
+ * @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+ */
+int rtrmnet_ctl_delvnd(rmnetctl_hndl_t *hndl, char *vndname,
+		       uint16_t *error_code);
+
+/* @brief Public API to change flag's of a virtual device node
+ * @details Message type is RTM_NEWLINK
+ * @param hndl RmNet handle for the Netlink message
+ * @param dev_name Name of device node is connected to
+ * @param vnd_name Name of virtual device to be changed
+ * @param error_code Status code of this operation returned from the kernel
+ * @param flagconfig New flag config vnd should have
+ * @return RMNETCTL_SUCCESS if successful
+ * @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+ * @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+ * Check error_code
+ * @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+ */
+int rtrmnet_ctl_changevnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname,
+			  uint16_t *error_code, uint8_t  index,
+			  uint32_t flagconfig);
+
+/* @brief Public API to bridge a vnd and device
+ * @details Message type is RTM_NEWLINK
+ * @param hndl RmNet handle for the Netlink message
+ * @param dev_name device to bridge msg will be sent to
+ * @param vnd_name vnd name of device that will be dev_name's master
+ * @param error_code Status code of this operation returned from the kernel
+ * @return RMNETCTL_SUCCESS if successful
+ * @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+ * @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+ * Check error_code
+ * @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+ */
+int rtrmnet_ctl_bridgevnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname,
+			  uint16_t *error_code);
+
+#endif /* not defined LIBRMNETCTL_H */
+
diff --git a/rmnetlib/inc/librmnetctl_hndl.h b/rmnetlib/inc/librmnetctl_hndl.h
new file mode 100644
index 0000000..1a435ed
--- /dev/null
+++ b/rmnetlib/inc/librmnetctl_hndl.h
@@ -0,0 +1,65 @@
+/******************************************************************************
+
+			L I B R M N E T C T L _ H N D L. H
+
+Copyright (c) 2013, 2015 The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+	* Redistributions of source code must retain the above copyright
+	  notice, this list of conditions and the following disclaimer.
+	* Redistributions in binary form must reproduce the above
+	  copyright notice, this list of conditions and the following
+	  disclaimer in the documentation and/or other materials provided
+	  with the distribution.
+	* Neither the name of The Linux Foundation nor the names of its
+	  contributors may be used to endorse or promote products derived
+	  from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+/*!
+*  @file    librmnetctl_hndl.h
+*  @brief   rmnet control API's handle file
+*/
+
+#ifndef LIBRMNETCTL_HNDL_H
+#define LIBRMNETCTL_HNDL_H
+
+/*===========================================================================
+			 DEFINITIONS AND DECLARATIONS
+===========================================================================*/
+
+/*!
+* @brief Structure for RMNET control handles. A rmnet hndl contains the caller
+* process id, the transaction id which is initialized to 0 for each new
+* initialized handle and the netlink file descriptor for this handle.
+* @var pid process id to be used for the netlink message
+* @var transaction_id message number for debugging
+* @var netlink_fd netlink file descriptor to be used
+* @var src_addr source socket address properties for this message
+* @var dest_addr destination socket address properties for this message
+*/
+
+struct rmnetctl_hndl_s {
+	 uint32_t pid;
+	 uint32_t transaction_id;
+	 int netlink_fd;
+	 struct sockaddr_nl src_addr, dest_addr;
+};
+
+#endif /* not defined LIBRMNETCTL_HNDL_H */
+
diff --git a/rmnetlib/src/Makefile.am b/rmnetlib/src/Makefile.am
new file mode 100644
index 0000000..7bd53b7
--- /dev/null
+++ b/rmnetlib/src/Makefile.am
@@ -0,0 +1,14 @@
+AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
+AM_CFLAGS += -Wall -Werror -Wundef -Wstrict-prototypes -Wno-trigraphs -g
+AM_CFLAGS += -I./../inc
+AM_LDFLAGS = $(CODE_COVERAGE_LDFLAGS)
+
+librmnetctl_la_C = @C@
+librmnetctl_la_SOURCES = librmnetctl.c
+
+librmnetctl_la_CFLAGS := $(AM_CFLAGS)
+librmnetctl_la_LDFLAGS := $(AM_LDFLAGS) -lpthread
+library_includedir = $(pkgincludedir)
+library_include_HEADERS = ./../inc/librmnetctl.h
+
+lib_LTLIBRARIES = librmnetctl.la
diff --git a/rmnetlib/src/librmnetctl.c b/rmnetlib/src/librmnetctl.c
new file mode 100644
index 0000000..4e07376
--- /dev/null
+++ b/rmnetlib/src/librmnetctl.c
@@ -0,0 +1,1369 @@
+/******************************************************************************
+
+			L I B R M N E T C T L . C
+
+Copyright (c) 2013-2015, 2017-2018 The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+	* Redistributions of source code must retain the above copyright
+	  notice, this list of conditions and the following disclaimer.
+	* Redistributions in binary form must reproduce the above
+	  copyright notice, this list of conditions and the following
+	  disclaimer in the documentation and/or other materials provided
+	  with the distribution.
+	* Neither the name of The Linux Foundation nor the names of its
+	  contributors may be used to endorse or promote products derived
+	  from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+/*!
+* @file    librmnetctl.c
+* @brief   rmnet control API's implementation file
+*/
+
+/*===========================================================================
+			INCLUDE FILES
+===========================================================================*/
+
+#include <sys/socket.h>
+#include <stdint.h>
+#include <linux/netlink.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <linux/rtnetlink.h>
+#include <linux/gen_stats.h>
+#include <net/if.h>
+#include <asm/types.h>
+#include "rmnet_data.h"
+#include "librmnetctl_hndl.h"
+#include "librmnetctl.h"
+
+#ifdef USE_GLIB
+#include <glib.h>
+#define strlcpy g_strlcpy
+#endif
+
+#define RMNETCTL_SOCK_FLAG 0
+#define ROOT_USER_ID 0
+#define MIN_VALID_PROCESS_ID 0
+#define MIN_VALID_SOCKET_FD 0
+#define KERNEL_PROCESS_ID 0
+#define UNICAST 0
+#define MAX_BUF_SIZE sizeof(struct nlmsghdr) + sizeof(struct rmnet_nl_msg_s)
+#define INGRESS_FLAGS_MASK   (RMNET_INGRESS_FIX_ETHERNET | \
+			      RMNET_INGRESS_FORMAT_MAP | \
+			      RMNET_INGRESS_FORMAT_DEAGGREGATION | \
+			      RMNET_INGRESS_FORMAT_DEMUXING | \
+			      RMNET_INGRESS_FORMAT_MAP_COMMANDS | \
+			      RMNET_INGRESS_FORMAT_MAP_CKSUMV3 | \
+			      RMNET_INGRESS_FORMAT_MAP_CKSUMV4)
+#define EGRESS_FLAGS_MASK    (RMNET_EGRESS_FORMAT__RESERVED__ | \
+			      RMNET_EGRESS_FORMAT_MAP | \
+			      RMNET_EGRESS_FORMAT_AGGREGATION | \
+			      RMNET_EGRESS_FORMAT_MUXING | \
+			      RMNET_EGRESS_FORMAT_MAP_CKSUMV3 | \
+			      RMNET_EGRESS_FORMAT_MAP_CKSUMV4)
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define NLMSG_TAIL(nmsg) \
+    ((struct rtattr *) (((char *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+struct nlmsg {
+	struct nlmsghdr nl_addr;
+	struct ifinfomsg ifmsg;
+	char data[500];
+};
+
+extern size_t strlcpy(char *dst, const char *src, size_t dsize);
+
+/*===========================================================================
+			LOCAL FUNCTION DEFINITIONS
+===========================================================================*/
+/*!
+* @brief Synchronous method to send and receive messages to and from the kernel
+* using  netlink sockets
+* @details Increments the transaction id for each message sent to the kernel.
+* Sends the netlink message to the kernel and receives the response from the
+* kernel.
+* @param *hndl RmNet handle for this transaction
+* @param request Message to be sent to the kernel
+* @param response Message received from the kernel
+* @return RMNETCTL_API_SUCCESS if successfully able to send and receive message
+* from the kernel
+* @return RMNETCTL_API_ERR_HNDL_INVALID if RmNet handle for the transaction was
+* NULL
+* @return RMNETCTL_API_ERR_REQUEST_NULL not enough memory to create buffer for
+* sending the message
+* @return RMNETCTL_API_ERR_MESSAGE_SEND if could not send the message to kernel
+* @return RMNETCTL_API_ERR_MESSAGE_RECEIVE if could not receive message from the
+* kernel
+* @return RMNETCTL_API_ERR_MESSAGE_TYPE if the request and response type do not
+* match
+*/
+static uint16_t rmnetctl_transact(rmnetctl_hndl_t *hndl,
+			struct rmnet_nl_msg_s *request,
+			struct rmnet_nl_msg_s *response) {
+	uint8_t *request_buf, *response_buf;
+	struct nlmsghdr *nlmsghdr_val;
+	struct rmnet_nl_msg_s *rmnet_nl_msg_s_val;
+	ssize_t bytes_read = -1;
+	uint16_t return_code = RMNETCTL_API_ERR_HNDL_INVALID;
+	struct sockaddr_nl* __attribute__((__may_alias__)) saddr_ptr;
+	socklen_t addrlen = sizeof(struct sockaddr_nl);
+	request_buf = NULL;
+	response_buf = NULL;
+	nlmsghdr_val = NULL;
+	rmnet_nl_msg_s_val = NULL;
+	do {
+	if (!hndl){
+		break;
+	}
+	if (!request){
+		return_code = RMNETCTL_API_ERR_REQUEST_NULL;
+		break;
+	}
+	if (!response){
+		return_code = RMNETCTL_API_ERR_RESPONSE_NULL;
+		break;
+	}
+	request_buf = (uint8_t *)malloc(MAX_BUF_SIZE * sizeof(uint8_t));
+	if (!request_buf){
+		return_code = RMNETCTL_API_ERR_REQUEST_NULL;
+		break;
+	}
+
+	response_buf = (uint8_t *)malloc(MAX_BUF_SIZE * sizeof(uint8_t));
+	if (!response_buf) {
+		return_code = RMNETCTL_API_ERR_RESPONSE_NULL;
+		break;
+	}
+
+	nlmsghdr_val = (struct nlmsghdr *)request_buf;
+	rmnet_nl_msg_s_val = (struct rmnet_nl_msg_s *)NLMSG_DATA(request_buf);
+
+	memset(request_buf, 0, MAX_BUF_SIZE*sizeof(uint8_t));
+	memset(response_buf, 0, MAX_BUF_SIZE*sizeof(uint8_t));
+
+	nlmsghdr_val->nlmsg_seq = hndl->transaction_id;
+	nlmsghdr_val->nlmsg_pid = hndl->pid;
+	nlmsghdr_val->nlmsg_len = MAX_BUF_SIZE;
+
+	memcpy((void *)NLMSG_DATA(request_buf), request,
+	sizeof(struct rmnet_nl_msg_s));
+
+	rmnet_nl_msg_s_val->crd = RMNET_NETLINK_MSG_COMMAND;
+	hndl->transaction_id++;
+
+	saddr_ptr = &hndl->dest_addr;
+	if (sendto(hndl->netlink_fd,
+			request_buf,
+			MAX_BUF_SIZE,
+			RMNETCTL_SOCK_FLAG,
+			(struct sockaddr*)saddr_ptr,
+			sizeof(struct sockaddr_nl)) < 0) {
+		return_code = RMNETCTL_API_ERR_MESSAGE_SEND;
+		break;
+	}
+
+	saddr_ptr = &hndl->src_addr;
+	bytes_read = recvfrom(hndl->netlink_fd,
+			response_buf,
+			MAX_BUF_SIZE,
+			RMNETCTL_SOCK_FLAG,
+			(struct sockaddr*)saddr_ptr,
+			&addrlen);
+	if (bytes_read < 0) {
+		return_code = RMNETCTL_API_ERR_MESSAGE_RECEIVE;
+		break;
+	}
+
+	memcpy(response, (void *)NLMSG_DATA(response_buf),
+	sizeof(struct rmnet_nl_msg_s));
+	if (sizeof(*response) < sizeof(struct rmnet_nl_msg_s)) {
+		return_code = RMNETCTL_API_ERR_RESPONSE_NULL;
+		break;
+	}
+
+	if (request->message_type != response->message_type) {
+		return_code = RMNETCTL_API_ERR_MESSAGE_TYPE;
+		break;
+	}
+	return_code = RMNETCTL_SUCCESS;
+	} while(0);
+	free(request_buf);
+	free(response_buf);
+	return return_code;
+}
+
+/*!
+* @brief Static function to check the dev name
+* @details Checks if the name is not NULL and if the name is less than the
+* RMNET_MAX_STR_LEN
+* @param dev_name Name of the device
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+*/
+static inline int _rmnetctl_check_dev_name(const char *dev_name) {
+	int return_code = RMNETCTL_INVALID_ARG;
+	do {
+	if (!dev_name)
+		break;
+	if (strlen(dev_name) >= RMNET_MAX_STR_LEN)
+		break;
+	return_code = RMNETCTL_SUCCESS;
+	} while(0);
+	return return_code;
+}
+
+/*!
+* @brief Static function to check the string length after a copy
+* @details Checks if the string length is not lesser than zero and lesser than
+* RMNET_MAX_STR_LEN
+* @param str_len length of the string after a copy
+* @param error_code Status code of this operation
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+*/
+static inline int _rmnetctl_check_len(size_t str_len, uint16_t *error_code) {
+	int return_code = RMNETCTL_LIB_ERR;
+	do {
+	if (str_len > RMNET_MAX_STR_LEN) {
+		*error_code = RMNETCTL_API_ERR_STRING_TRUNCATION;
+		break;
+	}
+	return_code = RMNETCTL_SUCCESS;
+	} while(0);
+	return return_code;
+}
+
+/*!
+* @brief Static function to check the response type
+* @details Checks if the response type of this message was return code
+* @param crd The crd field passed
+* @param error_code Status code of this operation
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+*/
+static inline int _rmnetctl_check_code(int crd, uint16_t *error_code) {
+	int return_code = RMNETCTL_LIB_ERR;
+	do {
+	if (crd != RMNET_NETLINK_MSG_RETURNCODE) {
+		*error_code = RMNETCTL_API_ERR_RETURN_TYPE;
+		break;
+	}
+	return_code = RMNETCTL_SUCCESS;
+	} while(0);
+	return return_code;
+}
+
+/*!
+* @brief Static function to check the response type
+* @details Checks if the response type of this message was data
+* @param crd The crd field passed
+* @param error_code Status code of this operation
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_LIB_ERR if there was a library error. Check error_code
+*/
+static inline int _rmnetctl_check_data(int crd, uint16_t *error_code) {
+	int return_code = RMNETCTL_LIB_ERR;
+	do {
+	if (crd != RMNET_NETLINK_MSG_RETURNDATA) {
+		*error_code = RMNETCTL_API_ERR_RETURN_TYPE;
+		break;
+	}
+	return_code = RMNETCTL_SUCCESS;
+	} while(0);
+	return return_code;
+}
+
+/*!
+* @brief Static function to set the return value
+* @details Checks if the error_code from the transaction is zero for a return
+* code type message and sets the message type as RMNETCTL_SUCCESS
+* @param crd The crd field passed
+* @param error_code Status code of this operation
+* @return RMNETCTL_SUCCESS if successful
+* @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+* Check error_code
+*/
+static inline int _rmnetctl_set_codes(int error_val, uint16_t *error_code) {
+	int return_code = RMNETCTL_KERNEL_ERR;
+	if (error_val == RMNET_CONFIG_OK)
+		return_code = RMNETCTL_SUCCESS;
+	else
+		*error_code = (uint16_t)error_val + RMNETCTL_KERNEL_FIRST_ERR;
+	return return_code;
+}
+
+/*===========================================================================
+				EXPOSED API
+===========================================================================*/
+
+int rmnetctl_init(rmnetctl_hndl_t **hndl, uint16_t *error_code)
+{
+	pid_t pid = 0;
+	int netlink_fd = -1, return_code = RMNETCTL_LIB_ERR;
+	struct sockaddr_nl* __attribute__((__may_alias__)) saddr_ptr;
+	do {
+	if ((!hndl) || (!error_code)){
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	*hndl = (rmnetctl_hndl_t *)malloc(sizeof(rmnetctl_hndl_t));
+	if (!*hndl) {
+		*error_code = RMNETCTL_API_ERR_HNDL_INVALID;
+		break;
+	}
+
+	memset(*hndl, 0, sizeof(rmnetctl_hndl_t));
+
+	pid = getpid();
+	if (pid  < MIN_VALID_PROCESS_ID) {
+		free(*hndl);
+		*error_code = RMNETCTL_INIT_ERR_PROCESS_ID;
+		break;
+	}
+	(*hndl)->pid = (uint32_t)pid;
+	netlink_fd = socket(PF_NETLINK, SOCK_RAW, RMNET_NETLINK_PROTO);
+	if (netlink_fd < MIN_VALID_SOCKET_FD) {
+		free(*hndl);
+		*error_code = RMNETCTL_INIT_ERR_NETLINK_FD;
+		break;
+	}
+
+	(*hndl)->netlink_fd = netlink_fd;
+
+	memset(&(*hndl)->src_addr, 0, sizeof(struct sockaddr_nl));
+
+	(*hndl)->src_addr.nl_family = AF_NETLINK;
+	(*hndl)->src_addr.nl_pid = (*hndl)->pid;
+
+	saddr_ptr = &(*hndl)->src_addr;
+	if (bind((*hndl)->netlink_fd,
+		(struct sockaddr*)saddr_ptr,
+		sizeof(struct sockaddr_nl)) < 0) {
+		close((*hndl)->netlink_fd);
+		free(*hndl);
+		*error_code = RMNETCTL_INIT_ERR_BIND;
+		break;
+	}
+
+	memset(&(*hndl)->dest_addr, 0, sizeof(struct sockaddr_nl));
+
+	(*hndl)->dest_addr.nl_family = AF_NETLINK;
+	(*hndl)->dest_addr.nl_pid = KERNEL_PROCESS_ID;
+	(*hndl)->dest_addr.nl_groups = UNICAST;
+
+	return_code = RMNETCTL_SUCCESS;
+	} while(0);
+	return return_code;
+}
+
+void rmnetctl_cleanup(rmnetctl_hndl_t *hndl)
+{
+	if (!hndl)
+		return;
+	close(hndl->netlink_fd);
+	free(hndl);
+}
+
+int rmnet_associate_network_device(rmnetctl_hndl_t *hndl,
+				   const char *dev_name,
+				   uint16_t *error_code,
+				   uint8_t assoc_dev)
+{
+	struct rmnet_nl_msg_s request, response;
+	size_t str_len = 0;
+	int return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || (!error_code) || _rmnetctl_check_dev_name(dev_name) ||
+		((assoc_dev != RMNETCTL_DEVICE_ASSOCIATE) &&
+		(assoc_dev != RMNETCTL_DEVICE_UNASSOCIATE))) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	if (assoc_dev == RMNETCTL_DEVICE_ASSOCIATE)
+		request.message_type = RMNET_NETLINK_ASSOCIATE_NETWORK_DEVICE;
+	else
+		request.message_type = RMNET_NETLINK_UNASSOCIATE_NETWORK_DEVICE;
+
+	request.arg_length = RMNET_MAX_STR_LEN;
+	str_len = strlcpy((char *)(request.data), dev_name, (size_t)RMNET_MAX_STR_LEN);
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
+		break;
+	return_code = _rmnetctl_set_codes(response.return_code, error_code);
+	} while(0);
+	return return_code;
+}
+
+int rmnet_get_network_device_associated(rmnetctl_hndl_t *hndl,
+					const char *dev_name,
+					int *register_status,
+					uint16_t *error_code) {
+	struct rmnet_nl_msg_s request, response;
+	size_t str_len = 0;
+	int  return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || (!register_status) || (!error_code) ||
+	_rmnetctl_check_dev_name(dev_name)) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	request.message_type = RMNET_NETLINK_GET_NETWORK_DEVICE_ASSOCIATED;
+
+	request.arg_length = RMNET_MAX_STR_LEN;
+	str_len = strlcpy((char *)(request.data), dev_name, RMNET_MAX_STR_LEN);
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+
+	if (_rmnetctl_check_data(response.crd, error_code)
+		!= RMNETCTL_SUCCESS) {
+		if (_rmnetctl_check_code(response.crd, error_code)
+			== RMNETCTL_SUCCESS)
+			return_code = _rmnetctl_set_codes(response.return_code,
+							  error_code);
+		break;
+	}
+
+	*register_status = response.return_code;
+	return_code = RMNETCTL_SUCCESS;
+	} while(0);
+	return return_code;
+}
+
+int rmnet_set_link_egress_data_format(rmnetctl_hndl_t *hndl,
+				      uint32_t egress_flags,
+				      uint16_t agg_size,
+				      uint16_t agg_count,
+				      const char *dev_name,
+				      uint16_t *error_code) {
+	struct rmnet_nl_msg_s request, response;
+	size_t str_len = 0;
+	int  return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || (!error_code) || _rmnetctl_check_dev_name(dev_name) ||
+	    ((~EGRESS_FLAGS_MASK) & egress_flags)) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	request.message_type = RMNET_NETLINK_SET_LINK_EGRESS_DATA_FORMAT;
+
+	request.arg_length = RMNET_MAX_STR_LEN +
+			 sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t);
+	str_len = strlcpy((char *)(request.data_format.dev),
+			  dev_name,
+			  RMNET_MAX_STR_LEN);
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	request.data_format.flags = egress_flags;
+	request.data_format.agg_size = agg_size;
+	request.data_format.agg_count = agg_count;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+
+	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	return_code = _rmnetctl_set_codes(response.return_code, error_code);
+	} while(0);
+	return return_code;
+}
+
+int rmnet_get_link_egress_data_format(rmnetctl_hndl_t *hndl,
+				      const char *dev_name,
+				      uint32_t *egress_flags,
+				      uint16_t *agg_size,
+				      uint16_t *agg_count,
+				      uint16_t *error_code) {
+	struct rmnet_nl_msg_s request, response;
+	size_t str_len = 0;
+	int  return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || (!egress_flags) || (!agg_size) || (!agg_count) ||
+	(!error_code) || _rmnetctl_check_dev_name(dev_name)) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+	request.message_type = RMNET_NETLINK_GET_LINK_EGRESS_DATA_FORMAT;
+
+	request.arg_length = RMNET_MAX_STR_LEN;
+	str_len = strlcpy((char *)(request.data_format.dev),
+			  dev_name,
+			  RMNET_MAX_STR_LEN);
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+
+	if (_rmnetctl_check_data(response.crd, error_code)
+		!= RMNETCTL_SUCCESS) {
+		if (_rmnetctl_check_code(response.crd, error_code)
+			== RMNETCTL_SUCCESS)
+			return_code = _rmnetctl_set_codes(response.return_code,
+							  error_code);
+		break;
+	}
+
+	*egress_flags = response.data_format.flags;
+	*agg_size = response.data_format.agg_size;
+	*agg_count = response.data_format.agg_count;
+	return_code = RMNETCTL_SUCCESS;
+	} while(0);
+	return return_code;
+}
+
+int rmnet_set_link_ingress_data_format_tailspace(rmnetctl_hndl_t *hndl,
+						 uint32_t ingress_flags,
+						 uint8_t  tail_spacing,
+						 const char *dev_name,
+						 uint16_t *error_code) {
+	struct rmnet_nl_msg_s request, response;
+	size_t str_len = 0;
+	int  return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || (!error_code) || _rmnetctl_check_dev_name(dev_name) ||
+	    ((~INGRESS_FLAGS_MASK) & ingress_flags)) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	request.message_type = RMNET_NETLINK_SET_LINK_INGRESS_DATA_FORMAT;
+
+	request.arg_length = RMNET_MAX_STR_LEN +
+	sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t);
+	str_len = strlcpy((char *)(request.data_format.dev),
+			  dev_name,
+			  RMNET_MAX_STR_LEN);
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+	request.data_format.flags = ingress_flags;
+	request.data_format.tail_spacing = tail_spacing;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+
+	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	return_code = _rmnetctl_set_codes(response.return_code, error_code);
+	} while(0);
+	return return_code;
+}
+
+int rmnet_get_link_ingress_data_format_tailspace(rmnetctl_hndl_t *hndl,
+						 const char *dev_name,
+						 uint32_t *ingress_flags,
+						 uint8_t  *tail_spacing,
+						 uint16_t *error_code) {
+	struct rmnet_nl_msg_s request, response;
+	size_t str_len = 0;
+	int  return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || (!error_code) ||
+		_rmnetctl_check_dev_name(dev_name)) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	request.message_type = RMNET_NETLINK_GET_LINK_INGRESS_DATA_FORMAT;
+
+	request.arg_length = RMNET_MAX_STR_LEN;
+	str_len = strlcpy((char *)(request.data_format.dev),
+			  dev_name,
+			  RMNET_MAX_STR_LEN);
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+
+	if (_rmnetctl_check_data(response.crd, error_code)
+		!= RMNETCTL_SUCCESS) {
+		if (_rmnetctl_check_code(response.crd, error_code)
+			== RMNETCTL_SUCCESS)
+			return_code = _rmnetctl_set_codes(response.return_code,
+							  error_code);
+		break;
+	}
+
+	if (ingress_flags)
+		*ingress_flags = response.data_format.flags;
+
+	if (tail_spacing)
+		*tail_spacing = response.data_format.tail_spacing;
+
+	return_code = RMNETCTL_SUCCESS;
+	} while(0);
+	return return_code;
+}
+
+int rmnet_set_logical_ep_config(rmnetctl_hndl_t *hndl,
+				int32_t ep_id,
+				uint8_t operating_mode,
+				const char *dev_name,
+				const char *next_dev,
+				uint16_t *error_code) {
+	struct rmnet_nl_msg_s request, response;
+	size_t str_len = 0;
+	int return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || ((ep_id < -1) || (ep_id > 31)) || (!error_code) ||
+		_rmnetctl_check_dev_name(dev_name) ||
+		_rmnetctl_check_dev_name(next_dev) ||
+		operating_mode >= RMNET_EPMODE_LENGTH) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	request.message_type = RMNET_NETLINK_SET_LOGICAL_EP_CONFIG;
+
+	request.arg_length = RMNET_MAX_STR_LEN +
+	RMNET_MAX_STR_LEN + sizeof(int32_t) + sizeof(uint8_t);
+	str_len = strlcpy((char *)(request.local_ep_config.dev),
+			  dev_name,
+			  RMNET_MAX_STR_LEN);
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	str_len = strlcpy((char *)(request.local_ep_config.next_dev),
+			  next_dev,
+			  RMNET_MAX_STR_LEN);
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+	request.local_ep_config.ep_id = ep_id;
+	request.local_ep_config.operating_mode = operating_mode;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	return_code = _rmnetctl_set_codes(response.return_code, error_code);
+	} while(0);
+	return return_code;
+}
+
+int rmnet_unset_logical_ep_config(rmnetctl_hndl_t *hndl,
+				  int32_t ep_id,
+				  const char *dev_name,
+				  uint16_t *error_code) {
+	struct rmnet_nl_msg_s request, response;
+	size_t str_len = 0;
+	int return_code = RMNETCTL_LIB_ERR;
+	do {
+
+	if ((!hndl) || ((ep_id < -1) || (ep_id > 31)) || (!error_code) ||
+		_rmnetctl_check_dev_name(dev_name)) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	request.message_type = RMNET_NETLINK_UNSET_LOGICAL_EP_CONFIG;
+
+	request.arg_length = RMNET_MAX_STR_LEN + sizeof(int32_t);
+	str_len = strlcpy((char *)(request.local_ep_config.dev),
+			  dev_name,
+			  RMNET_MAX_STR_LEN);
+
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	request.local_ep_config.ep_id = ep_id;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	return_code = _rmnetctl_set_codes(response.return_code, error_code);
+	} while(0);
+
+	return return_code;
+}
+
+int rmnet_get_logical_ep_config(rmnetctl_hndl_t *hndl,
+				int32_t ep_id,
+				const char *dev_name,
+				uint8_t *operating_mode,
+				char **next_dev,
+				uint32_t next_dev_len,
+				uint16_t *error_code) {
+	struct rmnet_nl_msg_s request, response;
+	size_t str_len = 0;
+	int return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || (!operating_mode) || (!error_code) || ((ep_id < -1) ||
+	    (ep_id > 31)) || _rmnetctl_check_dev_name(dev_name) || (!next_dev)
+	    || (0 == next_dev_len)) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	request.message_type = RMNET_NETLINK_GET_LOGICAL_EP_CONFIG;
+
+	request.arg_length = RMNET_MAX_STR_LEN + sizeof(int32_t);
+	str_len = strlcpy((char *)(request.local_ep_config.dev),
+			  dev_name,
+			  RMNET_MAX_STR_LEN);
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	request.local_ep_config.ep_id = ep_id;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+
+	if (_rmnetctl_check_data(response.crd, error_code)
+		!= RMNETCTL_SUCCESS) {
+		if (_rmnetctl_check_code(response.crd, error_code)
+			== RMNETCTL_SUCCESS)
+			return_code = _rmnetctl_set_codes(response.return_code,
+							  error_code);
+		break;
+	}
+
+	str_len = strlcpy(*next_dev,
+			  (char *)(response.local_ep_config.next_dev),
+			  min(RMNET_MAX_STR_LEN, next_dev_len));
+	if (_rmnetctl_check_len(str_len, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	*operating_mode = response.local_ep_config.operating_mode;
+	return_code = RMNETCTL_SUCCESS;
+	} while(0);
+	return return_code;
+}
+
+int rmnet_new_vnd_prefix(rmnetctl_hndl_t *hndl,
+			 uint32_t id,
+			 uint16_t *error_code,
+			 uint8_t new_vnd,
+			 const char *prefix)
+{
+	struct rmnet_nl_msg_s request, response;
+	int return_code = RMNETCTL_LIB_ERR;
+	size_t str_len = 0;
+	do {
+	if ((!hndl) || (!error_code) ||
+	((new_vnd != RMNETCTL_NEW_VND) && (new_vnd != RMNETCTL_FREE_VND))) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	memset(request.vnd.vnd_name, 0, RMNET_MAX_STR_LEN);
+	if (new_vnd ==  RMNETCTL_NEW_VND) {
+		if (prefix) {
+			request.message_type =RMNET_NETLINK_NEW_VND_WITH_PREFIX;
+			str_len = strlcpy((char *)request.vnd.vnd_name,
+					  prefix, RMNET_MAX_STR_LEN);
+			if (_rmnetctl_check_len(str_len, error_code)
+						!= RMNETCTL_SUCCESS)
+				break;
+		} else {
+			request.message_type = RMNET_NETLINK_NEW_VND;
+		}
+	} else {
+		request.message_type = RMNET_NETLINK_FREE_VND;
+	}
+
+	request.arg_length = sizeof(uint32_t);
+	request.vnd.id = id;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	return_code = _rmnetctl_set_codes(response.return_code, error_code);
+	} while(0);
+	return return_code;
+}
+
+int rmnet_new_vnd_name(rmnetctl_hndl_t *hndl,
+			 uint32_t id,
+			 uint16_t *error_code,
+			 const char *prefix)
+{
+	struct rmnet_nl_msg_s request, response;
+	int return_code = RMNETCTL_LIB_ERR;
+	size_t str_len = 0;
+	do {
+	if ((!hndl) || (!error_code)) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	memset(request.vnd.vnd_name, 0, RMNET_MAX_STR_LEN);
+		if (prefix) {
+			request.message_type =RMNET_NETLINK_NEW_VND_WITH_NAME;
+			str_len = strlcpy((char *)request.vnd.vnd_name,
+					  prefix, RMNET_MAX_STR_LEN);
+			if (_rmnetctl_check_len(str_len, error_code)
+						!= RMNETCTL_SUCCESS)
+				break;
+		} else {
+			request.message_type = RMNET_NETLINK_NEW_VND;
+		}
+
+	request.arg_length = sizeof(uint32_t);
+	request.vnd.id = id;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
+		break;
+
+	return_code = _rmnetctl_set_codes(response.return_code, error_code);
+	} while(0);
+	return return_code;
+}
+
+int rmnet_new_vnd(rmnetctl_hndl_t *hndl,
+		  uint32_t id,
+		  uint16_t *error_code,
+		  uint8_t new_vnd)
+{
+	return rmnet_new_vnd_prefix(hndl, id, error_code, new_vnd, 0);
+}
+
+int rmnet_get_vnd_name(rmnetctl_hndl_t *hndl,
+		       uint32_t id,
+		       uint16_t *error_code,
+		       char *buf,
+		       uint32_t buflen)
+{
+	struct rmnet_nl_msg_s request, response;
+	uint32_t str_len;
+	int return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || (!error_code) || (!buf) || (0 == buflen)) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+
+	request.message_type = RMNET_NETLINK_GET_VND_NAME;
+	request.arg_length = sizeof(uint32_t);
+	request.vnd.id = id;
+
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+		!= RMNETCTL_SUCCESS)
+		break;
+
+	if (_rmnetctl_check_data(response.crd, error_code)
+		!= RMNETCTL_SUCCESS) {
+		if (_rmnetctl_check_code(response.crd, error_code)
+			== RMNETCTL_SUCCESS)
+			return_code = _rmnetctl_set_codes(response.return_code,
+							  error_code);
+		break;
+	}
+
+	str_len = (uint32_t)strlcpy(buf,
+			  (char *)(response.vnd.vnd_name),
+			  buflen);
+	if (str_len >= buflen) {
+		*error_code = RMNETCTL_API_ERR_STRING_TRUNCATION;
+		break;
+	}
+
+	return_code = RMNETCTL_SUCCESS;
+	} while (0);
+	return return_code;
+}
+
+int rmnet_add_del_vnd_tc_flow(rmnetctl_hndl_t *hndl,
+			      uint32_t id,
+			      uint32_t map_flow_id,
+			      uint32_t tc_flow_id,
+			      uint8_t set_flow,
+			      uint16_t *error_code) {
+	struct rmnet_nl_msg_s request, response;
+	int return_code = RMNETCTL_LIB_ERR;
+	do {
+	if ((!hndl) || (!error_code) || ((set_flow != RMNETCTL_ADD_FLOW) &&
+	    (set_flow != RMNETCTL_DEL_FLOW))) {
+		return_code = RMNETCTL_INVALID_ARG;
+		break;
+	}
+	if (set_flow ==  RMNETCTL_ADD_FLOW)
+		request.message_type = RMNET_NETLINK_ADD_VND_TC_FLOW;
+	else
+		request.message_type = RMNET_NETLINK_DEL_VND_TC_FLOW;
+
+	request.arg_length = (sizeof(uint32_t))*3;
+	request.flow_control.id = id;
+	request.flow_control.map_flow_id = map_flow_id;
+	request.flow_control.tc_flow_id = tc_flow_id;
+
+	if ((*error_code = rmnetctl_transact(hndl, &request, &response))
+	!= RMNETCTL_SUCCESS)
+		break;
+	if (_rmnetctl_check_code(response.crd, error_code) != RMNETCTL_SUCCESS)
+		break;
+	return_code = _rmnetctl_set_codes(response.return_code, error_code);
+	} while(0);
+	return return_code;
+}
+
+/*
+ *                       NEW DRIVER API
+ */
+/* @brief Synchronous method to receive messages to and from the kernel
+ * using netlink sockets
+ * @details Receives the ack response from the kernel.
+ * @param *hndl RmNet handle for this transaction
+ * @param *error_code Error code if transaction fails
+ * @return RMNETCTL_API_SUCCESS if successfully able to send and receive message
+ * from the kernel
+ * @return RMNETCTL_API_ERR_HNDL_INVALID if RmNet handle for the transaction was
+ * NULL
+ * @return RMNETCTL_API_ERR_MESSAGE_RECEIVE if could not receive message from
+ * the kernel
+ * @return RMNETCTL_API_ERR_MESSAGE_TYPE if the response type does not
+ * match
+ */
+static int rmnet_get_ack(rmnetctl_hndl_t *hndl, uint16_t *error_code)
+{
+	struct nlack {
+		struct nlmsghdr ackheader;
+		struct nlmsgerr ackdata;
+		char   data[256];
+
+	} ack;
+	int i;
+
+	if (!hndl || !error_code)
+		return RMNETCTL_INVALID_ARG;
+
+	if ((i = recv(hndl->netlink_fd, &ack, sizeof(ack), 0)) < 0) {
+		*error_code = errno;
+		return RMNETCTL_API_ERR_MESSAGE_RECEIVE;
+	}
+
+	/*Ack should always be NLMSG_ERROR type*/
+	if (ack.ackheader.nlmsg_type == NLMSG_ERROR) {
+		if (ack.ackdata.error == 0) {
+			*error_code = RMNETCTL_API_SUCCESS;
+			return RMNETCTL_SUCCESS;
+		} else {
+			*error_code = -ack.ackdata.error;
+			return RMNETCTL_KERNEL_ERR;
+		}
+	}
+
+	*error_code = RMNETCTL_API_ERR_RETURN_TYPE;
+	return RMNETCTL_API_FIRST_ERR;
+}
+
+/*
+ *                       EXPOSED NEW DRIVER API
+ */
+int rtrmnet_ctl_init(rmnetctl_hndl_t **hndl, uint16_t *error_code)
+{
+	struct sockaddr_nl __attribute__((__may_alias__)) *saddr_ptr;
+	int netlink_fd = -1;
+	pid_t pid = 0;
+
+	if (!hndl || !error_code)
+		return RMNETCTL_INVALID_ARG;
+
+	*hndl = (rmnetctl_hndl_t *)malloc(sizeof(rmnetctl_hndl_t));
+	if (!*hndl) {
+		*error_code = RMNETCTL_API_ERR_HNDL_INVALID;
+		return RMNETCTL_LIB_ERR;
+	}
+
+	memset(*hndl, 0, sizeof(rmnetctl_hndl_t));
+
+	pid = getpid();
+	if (pid  < MIN_VALID_PROCESS_ID) {
+		free(*hndl);
+		*error_code = RMNETCTL_INIT_ERR_PROCESS_ID;
+		return RMNETCTL_LIB_ERR;
+	}
+	(*hndl)->pid = KERNEL_PROCESS_ID;
+	netlink_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	if (netlink_fd < MIN_VALID_SOCKET_FD) {
+		free(*hndl);
+		*error_code = RMNETCTL_INIT_ERR_NETLINK_FD;
+		return RMNETCTL_LIB_ERR;
+	}
+
+	(*hndl)->netlink_fd = netlink_fd;
+
+	memset(&(*hndl)->src_addr, 0, sizeof(struct sockaddr_nl));
+
+	(*hndl)->src_addr.nl_family = AF_NETLINK;
+	(*hndl)->src_addr.nl_pid = (*hndl)->pid;
+
+	saddr_ptr = &(*hndl)->src_addr;
+	if (bind((*hndl)->netlink_fd,
+		(struct sockaddr *)saddr_ptr,
+		sizeof(struct sockaddr_nl)) < 0) {
+		close((*hndl)->netlink_fd);
+		free(*hndl);
+		*error_code = RMNETCTL_INIT_ERR_BIND;
+		return RMNETCTL_LIB_ERR;
+	}
+
+	memset(&(*hndl)->dest_addr, 0, sizeof(struct sockaddr_nl));
+
+	(*hndl)->dest_addr.nl_family = AF_NETLINK;
+	(*hndl)->dest_addr.nl_pid = KERNEL_PROCESS_ID;
+	(*hndl)->dest_addr.nl_groups = UNICAST;
+
+	return RMNETCTL_SUCCESS;
+}
+
+int rtrmnet_ctl_deinit(rmnetctl_hndl_t *hndl)
+{
+	if (!hndl)
+		return RMNETCTL_SUCCESS;
+
+	close(hndl->netlink_fd);
+	free(hndl);
+
+	return RMNETCTL_SUCCESS;
+}
+
+int rtrmnet_ctl_newvnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname,
+		       uint16_t *error_code, uint8_t  index,
+		       uint32_t flagconfig)
+{
+	struct rtattr *attrinfo, *datainfo, *linkinfo;
+	struct ifla_vlan_flags flags;
+	int devindex = 0, val = 0;
+	char *kind = "rmnet";
+	struct nlmsg req;
+	short id;
+
+	if (!hndl || !devname || !vndname || !error_code)
+		return RMNETCTL_INVALID_ARG;
+
+	memset(&req, 0, sizeof(req));
+	req.nl_addr.nlmsg_type = RTM_NEWLINK;
+	req.nl_addr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	req.nl_addr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL |
+				  NLM_F_ACK;
+	req.nl_addr.nlmsg_seq = hndl->transaction_id;
+	hndl->transaction_id++;
+
+	/* Get index of devname*/
+	devindex = if_nametoindex(devname);
+	if (devindex < 0) {
+		*error_code = errno;
+		return RMNETCTL_KERNEL_ERR;
+	}
+
+	/* Setup link attr with devindex as data */
+	val = devindex;
+	attrinfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+	attrinfo->rta_type = IFLA_LINK;
+	attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(sizeof(val)));
+	memcpy(RTA_DATA(attrinfo), &val, sizeof(val));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(sizeof(val)));
+
+	/* Set up IFLA info kind  RMNET that has linkinfo and type */
+	attrinfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+	attrinfo->rta_type =  IFLA_IFNAME;
+	attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(strlen(vndname) + 1));
+	memcpy(RTA_DATA(attrinfo), vndname, strlen(vndname) + 1);
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(strlen(vndname) + 1));
+
+	linkinfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+	linkinfo->rta_type = IFLA_LINKINFO;
+	linkinfo->rta_len = RTA_ALIGN(RTA_LENGTH(0));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(0));
+
+	attrinfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+	attrinfo->rta_type =  IFLA_INFO_KIND;
+	attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(strlen(kind)));
+	memcpy(RTA_DATA(attrinfo), kind, strlen(kind));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(strlen(kind)));
+
+	datainfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+	datainfo->rta_type =  IFLA_INFO_DATA;
+	datainfo->rta_len = RTA_ALIGN(RTA_LENGTH(0));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(0));
+
+	id = index;
+	attrinfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+	attrinfo->rta_type =  IFLA_VLAN_ID;
+	attrinfo->rta_len = RTA_LENGTH(sizeof(id));
+	memcpy(RTA_DATA(attrinfo), &id, sizeof(id));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(sizeof(id)));
+
+	if (flagconfig != 0) {
+		flags.mask  = flagconfig;
+		flags.flags = flagconfig;
+
+		attrinfo = (struct rtattr *)(((char *)&req) +
+					     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+		attrinfo->rta_type =  IFLA_VLAN_FLAGS;
+		attrinfo->rta_len = RTA_LENGTH(sizeof(flags));
+		memcpy(RTA_DATA(attrinfo), &flags, sizeof(flags));
+		req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+					RTA_ALIGN(RTA_LENGTH(sizeof(flags)));
+	}
+
+	datainfo->rta_len = (char *)NLMSG_TAIL(&req.nl_addr) - (char *)datainfo;
+
+	linkinfo->rta_len = (char *)NLMSG_TAIL(&req.nl_addr) - (char *)linkinfo;
+
+	if (send(hndl->netlink_fd, &req, req.nl_addr.nlmsg_len, 0) < 0) {
+		*error_code = RMNETCTL_API_ERR_MESSAGE_SEND;
+		return RMNETCTL_LIB_ERR;
+	}
+
+	return rmnet_get_ack(hndl, error_code);
+}
+
+int rtrmnet_ctl_delvnd(rmnetctl_hndl_t *hndl, char *vndname,
+		       uint16_t *error_code)
+{
+	int devindex = 0;
+	struct nlmsg req;
+
+	if (!hndl || !vndname || !error_code)
+		return RMNETCTL_INVALID_ARG;
+
+	memset(&req, 0, sizeof(req));
+	req.nl_addr.nlmsg_type = RTM_DELLINK;
+	req.nl_addr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	req.nl_addr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+	req.nl_addr.nlmsg_seq = hndl->transaction_id;
+	hndl->transaction_id++;
+
+	/* Get index of vndname*/
+	devindex = if_nametoindex(vndname);
+	if (devindex < 0) {
+		*error_code = errno;
+		return RMNETCTL_KERNEL_ERR;
+	}
+
+	/* Setup index attribute */
+	req.ifmsg.ifi_index = devindex;
+	if (send(hndl->netlink_fd, &req, req.nl_addr.nlmsg_len, 0) < 0) {
+		*error_code = RMNETCTL_API_ERR_MESSAGE_SEND;
+		return RMNETCTL_LIB_ERR;
+	}
+
+	return rmnet_get_ack(hndl, error_code);
+}
+
+
+int rtrmnet_ctl_changevnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname,
+			  uint16_t *error_code, uint8_t  index,
+			  uint32_t flagconfig)
+{
+	struct rtattr *attrinfo, *datainfo, *linkinfo;
+	struct ifla_vlan_flags flags;
+	char *kind = "rmnet";
+	struct nlmsg req;
+	int devindex = 0;
+	int val = 0;
+	short id;
+
+	memset(&req, 0, sizeof(req));
+
+	if (!hndl || !devname || !vndname || !error_code)
+		return RMNETCTL_INVALID_ARG;
+
+	req.nl_addr.nlmsg_type = RTM_NEWLINK;
+	req.nl_addr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	req.nl_addr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+	req.nl_addr.nlmsg_seq = hndl->transaction_id;
+	hndl->transaction_id++;
+
+	/* Get index of devname*/
+	devindex = if_nametoindex(devname);
+	if (devindex < 0) {
+		*error_code = errno;
+		return RMNETCTL_KERNEL_ERR;
+	}
+
+	/* Setup link attr with devindex as data */
+	val = devindex;
+	attrinfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+	attrinfo->rta_type = IFLA_LINK;
+	attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(sizeof(val)));
+	memcpy(RTA_DATA(attrinfo), &val, sizeof(val));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(sizeof(val)));
+
+	  /* Set up IFLA info kind  RMNET that has linkinfo and type */
+	attrinfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+	attrinfo->rta_type =  IFLA_IFNAME;
+	attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(strlen(vndname) + 1));
+	memcpy(RTA_DATA(attrinfo), vndname, strlen(vndname) + 1);
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(strlen(vndname) + 1));
+
+	linkinfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+	linkinfo->rta_type = IFLA_LINKINFO;
+	linkinfo->rta_len = RTA_ALIGN(RTA_LENGTH(0));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(0));
+
+
+	attrinfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+	attrinfo->rta_type =  IFLA_INFO_KIND;
+	attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(strlen(kind)));
+	memcpy(RTA_DATA(attrinfo), kind, strlen(kind));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(strlen(kind)));
+
+	datainfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+	datainfo->rta_type =  IFLA_INFO_DATA;
+	datainfo->rta_len = RTA_ALIGN(RTA_LENGTH(0));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(0));
+
+	id = index;
+	attrinfo = (struct rtattr *)(((char *)&req) +
+				     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+	attrinfo->rta_type =  IFLA_VLAN_ID;
+	attrinfo->rta_len = RTA_LENGTH(sizeof(id));
+	memcpy(RTA_DATA(attrinfo), &id, sizeof(id));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(sizeof(id)));
+
+	if (flagconfig != 0) {
+		flags.mask  = flagconfig;
+		flags.flags = flagconfig;
+
+		attrinfo = (struct rtattr *)(((char *)&req) +
+					     NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+		attrinfo->rta_type =  IFLA_VLAN_FLAGS;
+		attrinfo->rta_len = RTA_LENGTH(sizeof(flags));
+		memcpy(RTA_DATA(attrinfo), &flags, sizeof(flags));
+		req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+					RTA_ALIGN(RTA_LENGTH(sizeof(flags)));
+	}
+
+	datainfo->rta_len = (char *)NLMSG_TAIL(&req.nl_addr) - (char *)datainfo;
+
+	linkinfo->rta_len = (char *)NLMSG_TAIL(&req.nl_addr) - (char *)linkinfo;
+
+	if (send(hndl->netlink_fd, &req, req.nl_addr.nlmsg_len, 0) < 0) {
+		*error_code = RMNETCTL_API_ERR_MESSAGE_SEND;
+		return RMNETCTL_LIB_ERR;
+	}
+
+	return rmnet_get_ack(hndl, error_code);
+}
+
+int rtrmnet_ctl_bridgevnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname,
+			  uint16_t *error_code)
+{
+	int devindex = 0, vndindex = 0;
+	struct rtattr *masterinfo;
+	struct nlmsg req;
+
+	if (!hndl || !vndname || !devname || !error_code)
+		return RMNETCTL_INVALID_ARG;
+
+	memset(&req, 0, sizeof(req));
+	req.nl_addr.nlmsg_type = RTM_NEWLINK;
+	req.nl_addr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	req.nl_addr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+	req.nl_addr.nlmsg_seq = hndl->transaction_id;
+	hndl->transaction_id++;
+
+	/* Get index of vndname*/
+	devindex = if_nametoindex(devname);
+	if (devindex < 0) {
+		*error_code = errno;
+		return RMNETCTL_KERNEL_ERR;
+	}
+
+	vndindex = if_nametoindex(vndname);
+	if (vndindex < 0) {
+		*error_code = errno;
+		return RMNETCTL_KERNEL_ERR;
+	}
+
+	/* Setup index attribute */
+	req.ifmsg.ifi_index = devindex;
+	masterinfo = (struct rtattr *)(((char *)&req) +
+				       NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+	masterinfo->rta_type =  IFLA_MASTER;
+	masterinfo->rta_len = RTA_LENGTH(sizeof(vndindex));
+	memcpy(RTA_DATA(masterinfo), &vndindex, sizeof(vndindex));
+	req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+				RTA_ALIGN(RTA_LENGTH(sizeof(vndindex)));
+
+	if (send(hndl->netlink_fd, &req, req.nl_addr.nlmsg_len, 0) < 0) {
+		*error_code = RMNETCTL_API_ERR_MESSAGE_SEND;
+		return RMNETCTL_LIB_ERR;
+	}
+
+	return rmnet_get_ack(hndl, error_code);
+}
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: Qualcomm rmnet driver and qmi_wwan
  2018-06-05 17:38       ` Subash Abhinov Kasiviswanathan
@ 2018-06-08 10:21         ` Daniele Palmas
  2018-06-08 17:19           ` Subash Abhinov Kasiviswanathan
  0 siblings, 1 reply; 15+ messages in thread
From: Daniele Palmas @ 2018-06-08 10:21 UTC (permalink / raw)
  To: Subash Abhinov Kasiviswanathan, Bjørn Mork; +Cc: Dan Williams, netdev

[-- Attachment #1: Type: text/plain, Size: 3868 bytes --]

Hi Dan and Subash,

2018-06-05 19:38 GMT+02:00 Subash Abhinov Kasiviswanathan
<subashab@codeaurora.org>:
> On 2018-06-05 08:54, Dan Williams wrote:
>>
>> On Tue, 2018-06-05 at 11:38 +0200, Daniele Palmas wrote:
>>>
>>> Hi,
>>>
>>> 2018-02-21 20:47 GMT+01:00 Subash Abhinov Kasiviswanathan
>>> <subashab@codeaurora.org>:
>>> > On 2018-02-21 04:38, Daniele Palmas wrote:
>>> > >
>>> > > Hello,
>>> > >
>>> > > in rmnet kernel documentation I read:
>>> > >
>>> > > "This driver can be used to register onto any physical network
>>> > > device in
>>> > > IP mode. Physical transports include USB, HSIC, PCIe and IP
>>> > > accelerator."
>>> > >
>>> > > Does this mean that it can be used in association with the
>>> > > qmi_wwan
>>> > > driver?
>>> > >
>>> > > If yes, can someone give me an hint on the steps to follow?
>>> > >
>>> > > If not, does anyone know if it is possible to modify qmi_wwan in
>>> > > order
>>> > > to take advantage of the features provided by the rmnet driver?
>>> > >
>>> > > In this case hint on the changes for modifying qmi_wwan are
>>> > > welcome.
>>> > >
>>> > > Thanks in advance,
>>> > > Daniele
>>> >
>>> >
>>> > Hi
>>> >
>>> > I havent used qmi_wwan so the following comment is based on code
>>> > inspection.
>>> > qmimux_register_device() is creating qmimux devices with usb net
>>> > device as
>>> > real_dev. The Multiplexing and aggregation header (qmimux_hdr) is
>>> > stripped
>>> > off
>>> > in qmimux_rx_fixup() and the packet is passed on to stack.
>>> >
>>> > You could instead create rmnet devices with the usb netdevice as
>>> > real dev.
>>> > The packets from the usb net driver can be queued to network stack
>>> > directly
>>> > as rmnet driver will setup a RX handler. rmnet driver will process
>>> > the
>>> > packets
>>> > further and then queue to network stack.
>>> >
>>>
>>> in kernel documentation I read that rmnet user space configuration is
>>> done through librmnetctl available at
>>>
>>> https://source.codeaurora.org/quic/la/platform/vendor/qcom-opensource
>>> /dataservices/tree/rmnetctl
>>>
>>> However it seems to me that this is a bit outdated (e.g. it does not
>>> properly build since it is looking for kernel header
>>> linux/rmnet_data.h that, as far as I understand, is no more present).
>>>
>>> Is there available a more recent version of the tool?
>
>
> Hi Daniele
>
> The attached patch should have an updated version of the tool.
> Usage -
>
> rmnetcli -n newlink wwan0 rmnet0 1 1
> where wwan0 is the physical device
> rmnet0 is the virtual device to be created
> 1 is the mux id
> the other 1 is the flag to configure DL de-aggregation by default
>
> To delete a device -
>
> ip link delete rmnet0
>
>>
>> I'd expect that somebody (Subash?) would add support for the
>> rmnet/qmimux options to iproute2 via 'ip link' like exists for most
>> other device types.
>
>
> Hi Dan
>
> Yes, I can do that and update the documentation to point to using iproute2.
>

I followed Dan's advice and prepared a very basic test patch
(attached) for testing it through ip link.

Basically things seem to be properly working with qmicli, but I needed
to modify a bit qmi_wwan, so I'm adding Bjørn that maybe can help.

Bjørn,

I'm trying to add support to rmnet in qmi_wwan: I had to modify the
code as in the attached test patch, but I'm not sure it is the right
way.

This is done under the assumption that the rmnet device would be the
only one to register an rx handler to qmi_wwan, but it is probably
wrong.

Basically I'm wondering if there is a more correct way to understand
if an rmnet device is linked to the real qmi_wwan device.

Thanks,
Daniele

>
> --
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> a Linux Foundation Collaborative Project

[-- Attachment #2: 0001-usb-net-qmi_wwan-add-support-for-rmnet-device.patch --]
[-- Type: text/x-patch, Size: 942 bytes --]

From 9c1777d4d93238703172c5e88aaeb9d8b3e372eb Mon Sep 17 00:00:00 2001
From: Daniele Palmas <dnlplm@gmail.com>
Date: Fri, 8 Jun 2018 12:02:49 +0200
Subject: [PATCH 1/1] usb: net: qmi_wwan: add support for rmnet device

This patch allows to use rmnet with qmi_wwan create network
interfaces.

Signed-off-by: Daniele Palmas <dnlplm@gmail.com>
---
 drivers/net/usb/qmi_wwan.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 0946808..dd5f278 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -479,6 +479,11 @@ static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
 	if (info->flags & QMI_WWAN_FLAG_MUX)
 		return qmimux_rx_fixup(dev, skb);
 
+	if (rcu_access_pointer(dev->net->rx_handler)) {
+		skb->protocol = htons(ETH_P_MAP);
+		return 1;
+	}
+
 	switch (skb->data[0] & 0xf0) {
 	case 0x40:
 		proto = htons(ETH_P_IP);
-- 
2.7.4


[-- Attachment #3: 0001-ip-add-rmnet-initial-support.patch --]
[-- Type: text/x-patch, Size: 3606 bytes --]

From 88bdb27b6d535600c10ef446391a51fc56691350 Mon Sep 17 00:00:00 2001
From: Daniele Palmas <dnlplm@gmail.com>
Date: Fri, 8 Jun 2018 11:43:49 +0200
Subject: [PATCH 1/1] ip: add rmnet initial support

This patch adds basic support for rmnet devices.

Currently the only possible actions are creating a new link
with a specific mux id and removing a link.

Signed-off-by: Daniele Palmas <dnlplm@gmail.com>
---
 ip/Makefile       |  2 +-
 ip/iplink.c       |  2 +-
 ip/iplink_rmnet.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 76 insertions(+), 2 deletions(-)
 create mode 100644 ip/iplink_rmnet.c

diff --git a/ip/Makefile b/ip/Makefile
index 77fadee..a88f936 100644
--- a/ip/Makefile
+++ b/ip/Makefile
@@ -10,7 +10,7 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o ipnetns.o \
     link_iptnl.o link_gre6.o iplink_bond.o iplink_bond_slave.o iplink_hsr.o \
     iplink_bridge.o iplink_bridge_slave.o ipfou.o iplink_ipvlan.o \
     iplink_geneve.o iplink_vrf.o iproute_lwtunnel.o ipmacsec.o ipila.o \
-    ipvrf.o iplink_xstats.o ipseg6.o iplink_netdevsim.o
+    ipvrf.o iplink_xstats.o ipseg6.o iplink_netdevsim.o iplink_rmnet.o
 
 RTMONOBJ=rtmon.o
 
diff --git a/ip/iplink.c b/ip/iplink.c
index 9ff5f69..d678301 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -121,7 +121,7 @@ void iplink_usage(void)
 			"          bridge | bond | team | ipoib | ip6tnl | ipip | sit | vxlan |\n"
 			"          gre | gretap | erspan | ip6gre | ip6gretap | ip6erspan |\n"
 			"          vti | nlmon | team_slave | bond_slave | ipvlan | geneve |\n"
-			"          bridge_slave | vrf | macsec | netdevsim }\n");
+			"          bridge_slave | vrf | macsec | netdevsim | rmnet}\n");
 	}
 	exit(-1);
 }
diff --git a/ip/iplink_rmnet.c b/ip/iplink_rmnet.c
new file mode 100644
index 0000000..d3b0672
--- /dev/null
+++ b/ip/iplink_rmnet.c
@@ -0,0 +1,74 @@
+/*
+ * iplink_rmnet.c	RMNET device support
+ *
+ *              This program is free software; you can redistribute it and/or
+ *              modify it under the terms of the GNU General Public License
+ *              as published by the Free Software Foundation; either version
+ *              2 of the License, or (at your option) any later version.
+ *
+ * Authors:     Daniele Palmas <dnlplm@gmail.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+static void print_explain(FILE *f)
+{
+	fprintf(f,
+		"Usage: ... rmnet mux_id MUXID\n"
+		"\n"
+		"MUXID := 1-127\n"
+	);
+}
+
+static void explain(void)
+{
+	print_explain(stderr);
+}
+
+static int rmnet_parse_opt(struct link_util *lu, int argc, char **argv,
+			   struct nlmsghdr *n)
+{
+	struct ifla_rmnet_flags flags = { 0 };
+	__u16 mux_id;
+
+	while (argc > 0) {
+		if (matches(*argv, "mux_id") == 0) {
+                        NEXT_ARG();
+			if (get_u16(&mux_id, *argv, 0))
+				invarg("mux_id is invalid", *argv);
+			addattr_l(n, 1024, IFLA_RMNET_MUX_ID, &mux_id, 2);
+		} else if (matches(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr, "rmnet: unknown command \"%s\"?\n", *argv);
+			explain();
+			return -1;
+		}
+		argc--, argv++;
+	}
+
+	if (flags.mask)
+		addattr_l(n, 1024, IFLA_RMNET_FLAGS, &flags, sizeof(flags));
+
+	return 0;
+}
+
+static void rmnet_print_help(struct link_util *lu, int argc, char **argv,
+			     FILE *f)
+{
+	print_explain(f);
+}
+
+struct link_util rmnet_link_util = {
+	.id		= "rmnet",
+	.maxattr	= IFLA_RMNET_MAX,
+	.parse_opt	= rmnet_parse_opt,
+	.print_help	= rmnet_print_help,
+};
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: Qualcomm rmnet driver and qmi_wwan
  2018-06-08 10:21         ` Daniele Palmas
@ 2018-06-08 17:19           ` Subash Abhinov Kasiviswanathan
  2018-06-08 19:10             ` Bjørn Mork
  0 siblings, 1 reply; 15+ messages in thread
From: Subash Abhinov Kasiviswanathan @ 2018-06-08 17:19 UTC (permalink / raw)
  To: Daniele Palmas; +Cc: Bjørn Mork, Dan Williams, netdev

> I followed Dan's advice and prepared a very basic test patch
> (attached) for testing it through ip link.
> 
> Basically things seem to be properly working with qmicli, but I needed
> to modify a bit qmi_wwan, so I'm adding Bjørn that maybe can help.
> 
> Bjørn,
> 
> I'm trying to add support to rmnet in qmi_wwan: I had to modify the
> code as in the attached test patch, but I'm not sure it is the right
> way.
> 
> This is done under the assumption that the rmnet device would be the
> only one to register an rx handler to qmi_wwan, but it is probably
> wrong.
> 
> Basically I'm wondering if there is a more correct way to understand
> if an rmnet device is linked to the real qmi_wwan device.
> 
> Thanks,
> Daniele


Hi Daniele / Bjørn

Is it possible to define a pass through mode in qmi_wwan. This is to
ensure that all packets in MAP format are passed through instead of
processing in qmi_wwan layer. The pass through mode would just call
netif_receive_skb() on all these packets.

That would allow all the packets to be intercepted by the rx_handler
attached by rmnet which would subsequently de-multiplex and process
the packets.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Qualcomm rmnet driver and qmi_wwan
  2018-06-08 17:19           ` Subash Abhinov Kasiviswanathan
@ 2018-06-08 19:10             ` Bjørn Mork
  2018-06-09  2:19               ` Subash Abhinov Kasiviswanathan
  0 siblings, 1 reply; 15+ messages in thread
From: Bjørn Mork @ 2018-06-08 19:10 UTC (permalink / raw)
  To: Subash Abhinov Kasiviswanathan; +Cc: Daniele Palmas, Dan Williams, netdev

Subash Abhinov Kasiviswanathan <subashab@codeaurora.org> writes:

>> I followed Dan's advice and prepared a very basic test patch
>> (attached) for testing it through ip link.
>>
>> Basically things seem to be properly working with qmicli, but I needed
>> to modify a bit qmi_wwan, so I'm adding Bjørn that maybe can help.
>>
>> Bjørn,
>>
>> I'm trying to add support to rmnet in qmi_wwan: I had to modify the
>> code as in the attached test patch, but I'm not sure it is the right
>> way.
>>
>> This is done under the assumption that the rmnet device would be the
>> only one to register an rx handler to qmi_wwan, but it is probably
>> wrong.
>>
>> Basically I'm wondering if there is a more correct way to understand
>> if an rmnet device is linked to the real qmi_wwan device.
>>
>> Thanks,
>> Daniele
>
>
> Hi Daniele / Bjørn
>
> Is it possible to define a pass through mode in qmi_wwan. This is to
> ensure that all packets in MAP format are passed through instead of
> processing in qmi_wwan layer. The pass through mode would just call
> netif_receive_skb() on all these packets.
>
> That would allow all the packets to be intercepted by the rx_handler
> attached by rmnet which would subsequently de-multiplex and process
> the packets.

This sounds like a good idea. I probably won't have any time to look at
this in the near future, though.  Sorry about that. Extremely overloaded
both at work and private right now...

But I trust that you and Daniele can work out something. Please keep me
CCed, but don't expect timely replies.


Bjørn

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Qualcomm rmnet driver and qmi_wwan
  2018-06-08 19:10             ` Bjørn Mork
@ 2018-06-09  2:19               ` Subash Abhinov Kasiviswanathan
  2018-06-09  7:22                 ` Daniele Palmas
  0 siblings, 1 reply; 15+ messages in thread
From: Subash Abhinov Kasiviswanathan @ 2018-06-09  2:19 UTC (permalink / raw)
  To: Bjørn Mork, Daniele Palmas; +Cc: Dan Williams, netdev

[-- Attachment #1: Type: text/plain, Size: 664 bytes --]

> This sounds like a good idea. I probably won't have any time to look at
> this in the near future, though.  Sorry about that. Extremely 
> overloaded
> both at work and private right now...
> 
> But I trust that you and Daniele can work out something. Please keep me
> CCed, but don't expect timely replies.
> 

Hi Daniele

Can you try out the attached patch.
I have added a new sysfs attribute pass_through to be used in raw_ip 
mode
only. Once you attach rmnet devices on it, the rx_handler will be setup
and the packet will be processed by rmnet.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-net-qmi_wwan-Add-pass-through-mode.patch --]
[-- Type: text/x-diff; name=0001-net-qmi_wwan-Add-pass-through-mode.patch, Size: 3444 bytes --]

From bccfae3707af1be671fe55ea63123438f2dc38a8 Mon Sep 17 00:00:00 2001
From: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
Date: Fri, 8 Jun 2018 19:53:08 -0600
Subject: [PATCH] net: qmi_wwan: Add pass through mode

Pass through mode is to allow packets in MAP format to be passed
on to the stack. rmnet driver can be used to process and demultiplex
these packets. Note that pass through mode can be enabled when the
device is in raw ip mode only.

Signed-off-by: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
---
 drivers/net/usb/qmi_wwan.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 72 insertions(+)

diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 8e8b51f..f52a9be 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -59,6 +59,7 @@ struct qmi_wwan_state {
 enum qmi_wwan_flags {
 	QMI_WWAN_FLAG_RAWIP = 1 << 0,
 	QMI_WWAN_FLAG_MUX = 1 << 1,
+	QMI_WWAN_FLAG_PASS_THROUGH = 1 << 2,
 };
 
 enum qmi_wwan_quirks {
@@ -425,14 +426,80 @@ static ssize_t del_mux_store(struct device *d,  struct device_attribute *attr, c
 	return ret;
 }
 
+static ssize_t pass_through_show(struct device *d,
+				 struct device_attribute *attr,
+				 char *buf)
+{
+	struct usbnet *dev = netdev_priv(to_net_dev(d));
+	struct qmi_wwan_state *info;
+
+	info = (void *)&dev->data;
+	return sprintf(buf, "%c\n",
+		       info->flags & QMI_WWAN_FLAG_PASS_THROUGH ? 'Y' : 'N');
+}
+
+static ssize_t pass_through_store(struct device *d,
+				  struct device_attribute *attr,
+				  const char *buf, size_t len)
+{
+	struct usbnet *dev = netdev_priv(to_net_dev(d));
+	struct qmi_wwan_state *info;
+	bool enable;
+	int ret;
+
+	if (strtobool(buf, &enable))
+		return -EINVAL;
+
+	info = (void *)&dev->data;
+
+	/* no change? */
+	if (enable == (info->flags & QMI_WWAN_FLAG_PASS_THROUGH))
+		return len;
+
+	/* pass through mode can be set for raw ip devices only */
+	if (!(info->flags & QMI_WWAN_FLAG_RAWIP))
+		return -EINVAL;
+
+	if (!rtnl_trylock())
+		return restart_syscall();
+
+	/* we don't want to modify a running netdev */
+	if (netif_running(dev->net)) {
+		netdev_err(dev->net, "Cannot change a running device\n");
+		ret = -EBUSY;
+		goto err;
+	}
+
+	/* let other drivers deny the change */
+	ret = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev->net);
+	ret = notifier_to_errno(ret);
+	if (ret) {
+		netdev_err(dev->net, "Type change was refused\n");
+		goto err;
+	}
+
+	if (enable)
+		info->flags |= QMI_WWAN_FLAG_PASS_THROUGH;
+	else
+		info->flags &= ~QMI_WWAN_FLAG_PASS_THROUGH;
+	qmi_wwan_netdev_setup(dev->net);
+	call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev->net);
+	ret = len;
+err:
+	rtnl_unlock();
+	return ret;
+}
+
 static DEVICE_ATTR_RW(raw_ip);
 static DEVICE_ATTR_RW(add_mux);
 static DEVICE_ATTR_RW(del_mux);
+static DEVICE_ATTR_RW(pass_through);
 
 static struct attribute *qmi_wwan_sysfs_attrs[] = {
 	&dev_attr_raw_ip.attr,
 	&dev_attr_add_mux.attr,
 	&dev_attr_del_mux.attr,
+	&dev_attr_pass_through.attr,
 	NULL,
 };
 
@@ -479,6 +546,11 @@ static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
 	if (info->flags & QMI_WWAN_FLAG_MUX)
 		return qmimux_rx_fixup(dev, skb);
 
+	if (rawip && (info->flags & QMI_WWAN_FLAG_PASS_THROUGH)) {
+		skb->protocol = htons(ETH_P_MAP);
+		return (netif_rx(skb) == NET_RX_SUCCESS);
+	}
+
 	switch (skb->data[0] & 0xf0) {
 	case 0x40:
 		proto = htons(ETH_P_IP);
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: Qualcomm rmnet driver and qmi_wwan
  2018-06-09  2:19               ` Subash Abhinov Kasiviswanathan
@ 2018-06-09  7:22                 ` Daniele Palmas
  2018-06-09 17:55                   ` Subash Abhinov Kasiviswanathan
  0 siblings, 1 reply; 15+ messages in thread
From: Daniele Palmas @ 2018-06-09  7:22 UTC (permalink / raw)
  To: Subash Abhinov Kasiviswanathan; +Cc: Bjørn Mork, Dan Williams, netdev

Hi Subash,

2018-06-09 4:19 GMT+02:00 Subash Abhinov Kasiviswanathan
<subashab@codeaurora.org>:
>> This sounds like a good idea. I probably won't have any time to look at
>> this in the near future, though.  Sorry about that. Extremely overloaded
>> both at work and private right now...
>>
>> But I trust that you and Daniele can work out something. Please keep me
>> CCed, but don't expect timely replies.
>>
>
> Hi Daniele
>
> Can you try out the attached patch.
> I have added a new sysfs attribute pass_through to be used in raw_ip mode
> only. Once you attach rmnet devices on it, the rx_handler will be setup
> and the packet will be processed by rmnet.
>

thanks, I will test it on Monday.

Just a question for my knowledge: is the new sysfs attribute really
needed? I mean, is there not any other way to understand from qmi_wwan
without user intervention that there is the rmnet device attached?

Regards,
Daniele

>
> --
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Qualcomm rmnet driver and qmi_wwan
  2018-06-09  7:22                 ` Daniele Palmas
@ 2018-06-09 17:55                   ` Subash Abhinov Kasiviswanathan
  2018-06-11 14:30                     ` Daniele Palmas
  2018-06-11 17:43                     ` Bjørn Mork
  0 siblings, 2 replies; 15+ messages in thread
From: Subash Abhinov Kasiviswanathan @ 2018-06-09 17:55 UTC (permalink / raw)
  To: Daniele Palmas; +Cc: Bjørn Mork, Dan Williams, netdev

[-- Attachment #1: Type: text/plain, Size: 582 bytes --]

> thanks, I will test it on Monday.
> 
> Just a question for my knowledge: is the new sysfs attribute really
> needed? I mean, is there not any other way to understand from qmi_wwan
> without user intervention that there is the rmnet device attached?
> 
> Regards,
> Daniele
> 

Hi Daniele

You can check for the rx_handler attached to qmi_wwan dev and see if it
belongs to rmnet. You can use the attached patch for it but it think the
sysfs way might be a bit cleaner.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-net-qmi_wwan-Allow-packets-to-pass-through-to-rmnet.patch --]
[-- Type: text/x-diff; name=0001-net-qmi_wwan-Allow-packets-to-pass-through-to-rmnet.patch, Size: 3460 bytes --]

From f7a2b90948da47ade1b345eddb37b721f5ab65f4 Mon Sep 17 00:00:00 2001
From: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
Date: Sat, 9 Jun 2018 11:14:22 -0600
Subject: [PATCH] net: qmi_wwan: Allow packets to pass through to rmnet

Pass through mode is to allow packets in MAP format to be passed
on to rmnet if the rmnet rx handler is attached to it.

Signed-off-by: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
---
 drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c |  4 +++-
 drivers/net/usb/qmi_wwan.c                         | 10 ++++++++++
 include/linux/if_rmnet.h                           | 20 ++++++++++++++++++++
 3 files changed, 33 insertions(+), 1 deletion(-)
 create mode 100644 include/linux/if_rmnet.h

diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
index 5f4e447..164a18f 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
@@ -17,6 +17,7 @@
 #include <linux/module.h>
 #include <linux/netlink.h>
 #include <linux/netdevice.h>
+#include <linux/if_rmnet.h>
 #include "rmnet_config.h"
 #include "rmnet_handlers.h"
 #include "rmnet_vnd.h"
@@ -48,10 +49,11 @@
 	[IFLA_RMNET_FLAGS]	= { .len = sizeof(struct ifla_rmnet_flags) },
 };
 
-static int rmnet_is_real_dev_registered(const struct net_device *real_dev)
+int rmnet_is_real_dev_registered(const struct net_device *real_dev)
 {
 	return rcu_access_pointer(real_dev->rx_handler) == rmnet_rx_handler;
 }
+EXPORT_SYMBOL(rmnet_is_real_dev_registered);
 
 /* Needs rtnl lock */
 static struct rmnet_port*
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index f52a9be..abdae63 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -22,6 +22,7 @@
 #include <linux/usb/cdc.h>
 #include <linux/usb/usbnet.h>
 #include <linux/usb/cdc-wdm.h>
+#include <linux/if_rmnet.h>
 
 /* This driver supports wwan (3G/LTE/?) devices using a vendor
  * specific management protocol called Qualcomm MSM Interface (QMI) -
@@ -354,6 +355,10 @@ static ssize_t add_mux_store(struct device *d,  struct device_attribute *attr, c
 	if (kstrtou8(buf, 0, &mux_id))
 		return -EINVAL;
 
+	/* rmnet is already attached here */
+	if (rmnet_is_real_dev_registered(to_net_dev(d)))
+		return -EINVAL;
+
 	/* mux_id [1 - 0x7f] range empirically found */
 	if (mux_id < 1 || mux_id > 0x7f)
 		return -EINVAL;
@@ -543,6 +548,11 @@ static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
 	if (skb->len < dev->net->hard_header_len)
 		return 0;
 
+	if (rawip && rmnet_is_real_dev_registered(skb->dev)) {
+		skb->protocol = htons(ETH_P_MAP);
+		return (netif_rx(skb) == NET_RX_SUCCESS);
+	}
+
 	if (info->flags & QMI_WWAN_FLAG_MUX)
 		return qmimux_rx_fixup(dev, skb);
 
diff --git a/include/linux/if_rmnet.h b/include/linux/if_rmnet.h
new file mode 100644
index 0000000..7a7fb96
--- /dev/null
+++ b/include/linux/if_rmnet.h
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ *
+ */
+#ifndef _LINUX_IF_RMNET_H_
+#define _LINUX_IF_RMNET_H_
+#include <linux/netdevice.h>
+
+#if defined(CONFIG_RMNET)
+extern int rmnet_is_real_dev_registered(const struct net_device *real_dev);
+#else
+static inline
+int rmnet_is_real_dev_registered(const struct net_device *real_dev)
+{
+	return 0;
+}
+#endif
+
+#endif /* !(_LINUX_IF_RMNET_H_) */
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: Qualcomm rmnet driver and qmi_wwan
  2018-06-09 17:55                   ` Subash Abhinov Kasiviswanathan
@ 2018-06-11 14:30                     ` Daniele Palmas
  2018-06-11 17:43                     ` Bjørn Mork
  1 sibling, 0 replies; 15+ messages in thread
From: Daniele Palmas @ 2018-06-11 14:30 UTC (permalink / raw)
  To: Subash Abhinov Kasiviswanathan; +Cc: Bjørn Mork, Dan Williams, netdev

Hi Subash,

2018-06-09 19:55 GMT+02:00 Subash Abhinov Kasiviswanathan
<subashab@codeaurora.org>:
>> thanks, I will test it on Monday.
>>
>> Just a question for my knowledge: is the new sysfs attribute really
>> needed? I mean, is there not any other way to understand from qmi_wwan
>> without user intervention that there is the rmnet device attached?
>>
>> Regards,
>> Daniele
>>
>
> Hi Daniele
>
> You can check for the rx_handler attached to qmi_wwan dev and see if it
> belongs to rmnet. You can use the attached patch for it but it think the
> sysfs way might be a bit cleaner.
>

both patches work properly for me.

Maybe it could be helpful in the first patch to add a print when
pass-through setting fails if raw-ip has not been set, just to let the
user know what is happening.

Thanks,
Daniele

>
> --
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Qualcomm rmnet driver and qmi_wwan
  2018-06-09 17:55                   ` Subash Abhinov Kasiviswanathan
  2018-06-11 14:30                     ` Daniele Palmas
@ 2018-06-11 17:43                     ` Bjørn Mork
  2018-06-11 23:00                       ` Subash Abhinov Kasiviswanathan
  1 sibling, 1 reply; 15+ messages in thread
From: Bjørn Mork @ 2018-06-11 17:43 UTC (permalink / raw)
  To: Subash Abhinov Kasiviswanathan; +Cc: Daniele Palmas, Dan Williams, netdev

Subash Abhinov Kasiviswanathan <subashab@codeaurora.org> writes:

>> thanks, I will test it on Monday.
>>
>> Just a question for my knowledge: is the new sysfs attribute really
>> needed? I mean, is there not any other way to understand from qmi_wwan
>> without user intervention that there is the rmnet device attached?
>>
>> Regards,
>> Daniele
>>
>
> Hi Daniele
>
> You can check for the rx_handler attached to qmi_wwan dev and see if it
> belongs to rmnet. You can use the attached patch for it but it think the
> sysfs way might be a bit cleaner.
>
> -- 
> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
> a Linux Foundation Collaborative Project
>
> From f7a2b90948da47ade1b345eddb37b721f5ab65f4 Mon Sep 17 00:00:00 2001
> From: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
> Date: Sat, 9 Jun 2018 11:14:22 -0600
> Subject: [PATCH] net: qmi_wwan: Allow packets to pass through to rmnet
>
> Pass through mode is to allow packets in MAP format to be passed
> on to rmnet if the rmnet rx handler is attached to it.
>
> Signed-off-by: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
> ---
>  drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c |  4 +++-
>  drivers/net/usb/qmi_wwan.c                         | 10 ++++++++++
>  include/linux/if_rmnet.h                           | 20 ++++++++++++++++++++
>  3 files changed, 33 insertions(+), 1 deletion(-)
>  create mode 100644 include/linux/if_rmnet.h
>
> diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
> index 5f4e447..164a18f 100644
> --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
> +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c
> @@ -17,6 +17,7 @@
>  #include <linux/module.h>
>  #include <linux/netlink.h>
>  #include <linux/netdevice.h>
> +#include <linux/if_rmnet.h>
>  #include "rmnet_config.h"
>  #include "rmnet_handlers.h"
>  #include "rmnet_vnd.h"
> @@ -48,10 +49,11 @@
>  	[IFLA_RMNET_FLAGS]	= { .len = sizeof(struct ifla_rmnet_flags) },
>  };
>  
> -static int rmnet_is_real_dev_registered(const struct net_device *real_dev)
> +int rmnet_is_real_dev_registered(const struct net_device *real_dev)
>  {
>  	return rcu_access_pointer(real_dev->rx_handler) == rmnet_rx_handler;
>  }
> +EXPORT_SYMBOL(rmnet_is_real_dev_registered);
>  
>  /* Needs rtnl lock */
>  static struct rmnet_port*
> diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
> index f52a9be..abdae63 100644
> --- a/drivers/net/usb/qmi_wwan.c
> +++ b/drivers/net/usb/qmi_wwan.c
> @@ -22,6 +22,7 @@
>  #include <linux/usb/cdc.h>
>  #include <linux/usb/usbnet.h>
>  #include <linux/usb/cdc-wdm.h>
> +#include <linux/if_rmnet.h>
>  
>  /* This driver supports wwan (3G/LTE/?) devices using a vendor
>   * specific management protocol called Qualcomm MSM Interface (QMI) -
> @@ -354,6 +355,10 @@ static ssize_t add_mux_store(struct device *d,  struct device_attribute *attr, c
>  	if (kstrtou8(buf, 0, &mux_id))
>  		return -EINVAL;
>  
> +	/* rmnet is already attached here */
> +	if (rmnet_is_real_dev_registered(to_net_dev(d)))
> +		return -EINVAL;
> +


Maybe rmnet_is_real_dev_registered(dev->net) instead, since we use that
elsewhere in this function?


>  	/* mux_id [1 - 0x7f] range empirically found */
>  	if (mux_id < 1 || mux_id > 0x7f)
>  		return -EINVAL;
> @@ -543,6 +548,11 @@ static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
>  	if (skb->len < dev->net->hard_header_len)
>  		return 0;
>  
> +	if (rawip && rmnet_is_real_dev_registered(skb->dev)) {
> +		skb->protocol = htons(ETH_P_MAP);
> +		return (netif_rx(skb) == NET_RX_SUCCESS);
> +	}

Like Daniele said: It would be good to have some way to know when the
rawip condition fails.  Or even better: Automatically force rawip mode
when the rmnet driver attaches.  But that doesn't seem possible?  No
notifications or anything when an rx handler is registered?

Hmm, looking at this I wonder: Is the rawip check really necessary?  You
skip all the extra rawip code in the driver anyway, so I don't see how
it matters.  But maybe the ethernet header_ops are a problem?

And I wonder about using skb->dev here.  Does that really work?  I
didn't think we set that until later.  Why not use dev->net instead?



Bjørn

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Qualcomm rmnet driver and qmi_wwan
  2018-06-11 17:43                     ` Bjørn Mork
@ 2018-06-11 23:00                       ` Subash Abhinov Kasiviswanathan
  0 siblings, 0 replies; 15+ messages in thread
From: Subash Abhinov Kasiviswanathan @ 2018-06-11 23:00 UTC (permalink / raw)
  To: Bjørn Mork, Daniele Palmas; +Cc: Dan Williams, netdev

> both patches work properly for me.
> 
> Maybe it could be helpful in the first patch to add a print when
> pass-through setting fails if raw-ip has not been set, just to let the
> user know what is happening.
> 

Thanks for testing Daniele.
I can add an error message there when pass through mode setting fails.
Will you be submitting the patch for iproute2. Otherwise, I can do so
a bit later.

> Maybe rmnet_is_real_dev_registered(dev->net) instead, since we use that
> elsewhere in this function?
> 
> Like Daniele said: It would be good to have some way to know when the
> rawip condition fails.  Or even better: Automatically force rawip mode
> when the rmnet driver attaches.  But that doesn't seem possible?  No
> notifications or anything when an rx handler is registered?
> 
> Hmm, looking at this I wonder: Is the rawip check really necessary?  
> You
> skip all the extra rawip code in the driver anyway, so I don't see how
> it matters.  But maybe the ethernet header_ops are a problem?
> 
> And I wonder about using skb->dev here.  Does that really work?  I
> didn't think we set that until later.  Why not use dev->net instead?
> 
> 

Hi Bjørn

Yes, I will switch to using dev->net.
There doesnt seem to be a net device notifier event when the rx 
registration
happens.

If the dev type is ethernet, rmnet driver will try to remove the first 
14
bytes since it assumes an ethernet header is present in the packet. 
Hence the
need for raw ip mode in qmi_wwan.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2018-06-11 23:00 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-02-21 11:38 Qualcomm rmnet driver and qmi_wwan Daniele Palmas
2018-02-21 19:47 ` Subash Abhinov Kasiviswanathan
2018-02-22 10:44   ` Daniele Palmas
2018-06-05  9:38   ` Daniele Palmas
2018-06-05 14:54     ` Dan Williams
2018-06-05 17:38       ` Subash Abhinov Kasiviswanathan
2018-06-08 10:21         ` Daniele Palmas
2018-06-08 17:19           ` Subash Abhinov Kasiviswanathan
2018-06-08 19:10             ` Bjørn Mork
2018-06-09  2:19               ` Subash Abhinov Kasiviswanathan
2018-06-09  7:22                 ` Daniele Palmas
2018-06-09 17:55                   ` Subash Abhinov Kasiviswanathan
2018-06-11 14:30                     ` Daniele Palmas
2018-06-11 17:43                     ` Bjørn Mork
2018-06-11 23:00                       ` Subash Abhinov Kasiviswanathan

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.