msm: ipa4: add VLAN mode to rndis_ipa and ecm_ipa

IPA core driver will receive at boot information about which LAN
interfaces should support VLAN. rndis_ipa and ecm_ipa will query
that info and will register the correct header accordingly.

Change-Id: I9cf0875e94373e06aa6a4200d629e5934199a345
CRs-Fixed: 2156924
Signed-off-by: Amir Levy <alevy@codeaurora.org>
diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c
index 7df312e..e20ddba 100644
--- a/drivers/platform/msm/ipa/ipa_api.c
+++ b/drivers/platform/msm/ipa/ipa_api.c
@@ -2728,6 +2728,26 @@
 EXPORT_SYMBOL(ipa_start_gsi_channel);
 
 /**
+* ipa_is_vlan_mode - check if a LAN driver should load in VLAN mode
+* @iface - type of vlan capable device
+* @res - query result: true for vlan mode, false for non vlan mode
+*
+* API must be called after ipa_is_ready() returns true, otherwise it will fail
+*
+* Returns: 0 on success, negative on failure
+*/
+int ipa_is_vlan_mode(enum ipa_vlan_ifaces iface, bool *res)
+{
+	int ret;
+
+	IPA_API_DISPATCH_RETURN(ipa_is_vlan_mode, iface, res);
+
+	return ret;
+
+}
+EXPORT_SYMBOL(ipa_is_vlan_mode);
+
+/**
  * ipa_get_version_string() - Get string representation of IPA version
  * @ver: IPA version
  *
diff --git a/drivers/platform/msm/ipa/ipa_api.h b/drivers/platform/msm/ipa/ipa_api.h
index 0779f34..f3e62b8 100644
--- a/drivers/platform/msm/ipa/ipa_api.h
+++ b/drivers/platform/msm/ipa/ipa_api.h
@@ -420,6 +420,7 @@
 
 	int (*ipa_get_smmu_params)(struct ipa_smmu_in_params *in,
 		struct ipa_smmu_out_params *out);
+	int (*ipa_is_vlan_mode)(enum ipa_vlan_ifaces iface, bool *res);
 };
 
 #ifdef CONFIG_IPA
diff --git a/drivers/platform/msm/ipa/ipa_clients/ecm_ipa.c b/drivers/platform/msm/ipa/ipa_clients/ecm_ipa.c
index 2975192..eadd58b 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ecm_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ecm_ipa.c
@@ -12,6 +12,7 @@
 #include <linux/debugfs.h>
 #include <linux/errno.h>
 #include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
 #include <linux/fs.h>
 #include <linux/module.h>
 #include <linux/netdevice.h>
@@ -123,6 +124,7 @@
  * @ipa_rm_resource_name_prod: IPA resource manager producer resource
  * @ipa_rm_resource_name_cons: IPA resource manager consumer resource
  * @pm_hdl: handle for IPA PM
+ * @is_vlan_mode: does the driver need to work in VLAN mode?
  */
 struct ecm_ipa_dev {
 	struct net_device *net;
@@ -141,6 +143,7 @@
 	enum ipa_rm_resource_name ipa_rm_resource_name_prod;
 	enum ipa_rm_resource_name ipa_rm_resource_name_cons;
 	u32 pm_hdl;
+	bool is_vlan_mode;
 };
 
 static int ecm_ipa_open(struct net_device *net);
@@ -173,7 +176,8 @@
 	(struct file *file, char __user *ubuf, size_t count, loff_t *ppos);
 static void ecm_ipa_debugfs_init(struct ecm_ipa_dev *ecm_ipa_ctx);
 static void ecm_ipa_debugfs_destroy(struct ecm_ipa_dev *ecm_ipa_ctx);
-static int ecm_ipa_ep_registers_cfg(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl);
+static int ecm_ipa_ep_registers_cfg(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl,
+	bool is_vlan_mode);
 static int ecm_ipa_set_device_ethernet_addr
 	(u8 *dev_ethaddr, u8 device_ethaddr[]);
 static enum ecm_ipa_state ecm_ipa_next_state
@@ -283,6 +287,12 @@
 	}
 	ECM_IPA_DEBUG("Device Ethernet address set %pM\n", net->dev_addr);
 
+	if (ipa_is_vlan_mode(IPA_VLAN_IF_ECM, &ecm_ipa_ctx->is_vlan_mode)) {
+		ECM_IPA_ERROR("couldn't acquire vlan mode, is ipa ready?\n");
+		goto fail_get_vlan_mode;
+	}
+	ECM_IPA_DEBUG("is vlan mode %d\n", ecm_ipa_ctx->is_vlan_mode);
+
 	result = ecm_ipa_rules_cfg
 		(ecm_ipa_ctx, params->host_ethaddr, params->device_ethaddr);
 	if (result) {
@@ -319,8 +329,9 @@
 
 fail_register_netdev:
 	ecm_ipa_rules_destroy(ecm_ipa_ctx);
-fail_set_device_ethernet:
 fail_rules_cfg:
+fail_get_vlan_mode:
+fail_set_device_ethernet:
 	ecm_ipa_debugfs_destroy(ecm_ipa_ctx);
 fail_netdev_priv:
 	free_netdev(net);
@@ -450,7 +461,8 @@
 	}
 	ECM_IPA_DEBUG("ecm_ipa 2 Tx and 2 Rx properties were registered\n");
 
-	retval = ecm_ipa_ep_registers_cfg(usb_to_ipa_hdl, ipa_to_usb_hdl);
+	retval = ecm_ipa_ep_registers_cfg(usb_to_ipa_hdl, ipa_to_usb_hdl,
+		ecm_ipa_ctx->is_vlan_mode);
 	if (retval) {
 		ECM_IPA_ERROR("fail on ep cfg\n");
 		goto fail;
@@ -606,6 +618,10 @@
 		goto out;
 	}
 
+	if (ecm_ipa_ctx->is_vlan_mode)
+		if (unlikely(skb->protocol != ETH_P_8021Q))
+			ECM_IPA_DEBUG("ether_type != ETH_P_8021Q && vlan\n");
+
 	ret = ipa_tx_dp(ecm_ipa_ctx->ipa_to_usb_client, skb, NULL);
 	if (ret) {
 		ECM_IPA_ERROR("ipa transmit failed (%d)\n", ret);
@@ -843,6 +859,41 @@
 	ECM_IPA_DEBUG("queue started\n");
 }
 
+static void ecm_ipa_prepare_header_insertion(
+	int eth_type,
+	const char *hdr_name, struct ipa_hdr_add *add_hdr,
+	const void *dst_mac, const void *src_mac, bool is_vlan_mode)
+{
+	struct ethhdr *eth_hdr;
+	struct vlan_ethhdr *eth_vlan_hdr;
+
+	ECM_IPA_LOG_ENTRY();
+
+	add_hdr->is_partial = 0;
+	strlcpy(add_hdr->name, hdr_name, IPA_RESOURCE_NAME_MAX);
+	add_hdr->is_eth2_ofst_valid = true;
+	add_hdr->eth2_ofst = 0;
+
+	if (is_vlan_mode) {
+		eth_vlan_hdr = (struct vlan_ethhdr *)add_hdr->hdr;
+		memcpy(eth_vlan_hdr->h_dest, dst_mac, ETH_ALEN);
+		memcpy(eth_vlan_hdr->h_source, src_mac, ETH_ALEN);
+		eth_vlan_hdr->h_vlan_encapsulated_proto =
+			htons(eth_type);
+		eth_vlan_hdr->h_vlan_proto = htons(ETH_P_8021Q);
+		add_hdr->hdr_len = VLAN_ETH_HLEN;
+		add_hdr->type = IPA_HDR_L2_802_1Q;
+	} else {
+		eth_hdr = (struct ethhdr *)add_hdr->hdr;
+		memcpy(eth_hdr->h_dest, dst_mac, ETH_ALEN);
+		memcpy(eth_hdr->h_source, src_mac, ETH_ALEN);
+		eth_hdr->h_proto = htons(eth_type);
+		add_hdr->hdr_len = ETH_HLEN;
+		add_hdr->type = IPA_HDR_L2_ETHERNET_II;
+	}
+	ECM_IPA_LOG_EXIT();
+}
+
 /**
  * ecm_ipa_rules_cfg() - set header insertion and register Tx/Rx properties
  *				Headers will be committed to HW
@@ -859,8 +910,6 @@
 	struct ipa_ioc_add_hdr *hdrs;
 	struct ipa_hdr_add *ipv4_hdr;
 	struct ipa_hdr_add *ipv6_hdr;
-	struct ethhdr *eth_ipv4;
-	struct ethhdr *eth_ipv6;
 	int result = 0;
 
 	ECM_IPA_LOG_ENTRY();
@@ -871,28 +920,17 @@
 		result = -ENOMEM;
 		goto out;
 	}
+
 	ipv4_hdr = &hdrs->hdr[0];
-	eth_ipv4 = (struct ethhdr *)ipv4_hdr->hdr;
+	ecm_ipa_prepare_header_insertion(
+		ETH_P_IP, ECM_IPA_IPV4_HDR_NAME,
+		ipv4_hdr, dst_mac, src_mac, ecm_ipa_ctx->is_vlan_mode);
+
 	ipv6_hdr = &hdrs->hdr[1];
-	eth_ipv6 = (struct ethhdr *)ipv6_hdr->hdr;
-	strlcpy(ipv4_hdr->name, ECM_IPA_IPV4_HDR_NAME, IPA_RESOURCE_NAME_MAX);
-	memcpy(eth_ipv4->h_dest, dst_mac, ETH_ALEN);
-	memcpy(eth_ipv4->h_source, src_mac, ETH_ALEN);
-	eth_ipv4->h_proto = htons(ETH_P_IP);
-	ipv4_hdr->hdr_len = ETH_HLEN;
-	ipv4_hdr->is_partial = 0;
-	ipv4_hdr->is_eth2_ofst_valid = true;
-	ipv4_hdr->eth2_ofst = 0;
-	ipv4_hdr->type = IPA_HDR_L2_ETHERNET_II;
-	strlcpy(ipv6_hdr->name, ECM_IPA_IPV6_HDR_NAME, IPA_RESOURCE_NAME_MAX);
-	memcpy(eth_ipv6->h_dest, dst_mac, ETH_ALEN);
-	memcpy(eth_ipv6->h_source, src_mac, ETH_ALEN);
-	eth_ipv6->h_proto = htons(ETH_P_IPV6);
-	ipv6_hdr->hdr_len = ETH_HLEN;
-	ipv6_hdr->is_partial = 0;
-	ipv6_hdr->is_eth2_ofst_valid = true;
-	ipv6_hdr->eth2_ofst = 0;
-	ipv6_hdr->type = IPA_HDR_L2_ETHERNET_II;
+	ecm_ipa_prepare_header_insertion(
+		ETH_P_IPV6, ECM_IPA_IPV6_HDR_NAME,
+		ipv6_hdr, dst_mac, src_mac, ecm_ipa_ctx->is_vlan_mode);
+
 	hdrs->commit = 1;
 	hdrs->num_hdrs = 2;
 	result = ipa_add_hdr(hdrs);
@@ -972,10 +1010,14 @@
 	struct ipa_rx_intf rx_properties = {0};
 	struct ipa_ioc_rx_intf_prop *rx_ipv4_property;
 	struct ipa_ioc_rx_intf_prop *rx_ipv6_property;
+	enum ipa_hdr_l2_type hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
 	int result = 0;
 
 	ECM_IPA_LOG_ENTRY();
 
+	if (ecm_ipa_ctx->is_vlan_mode)
+		hdr_l2_type = IPA_HDR_L2_802_1Q;
+
 	tx_properties.prop = properties;
 	ipv4_property = &tx_properties.prop[0];
 	ipv4_property->ip = IPA_IP_v4;
@@ -983,11 +1025,11 @@
 	strlcpy
 		(ipv4_property->hdr_name, ECM_IPA_IPV4_HDR_NAME,
 		IPA_RESOURCE_NAME_MAX);
-	ipv4_property->hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
+	ipv4_property->hdr_l2_type = hdr_l2_type;
 	ipv6_property = &tx_properties.prop[1];
 	ipv6_property->ip = IPA_IP_v6;
 	ipv6_property->dst_pipe = ecm_ipa_ctx->ipa_to_usb_client;
-	ipv6_property->hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
+	ipv6_property->hdr_l2_type = hdr_l2_type;
 	strlcpy
 		(ipv6_property->hdr_name, ECM_IPA_IPV6_HDR_NAME,
 		IPA_RESOURCE_NAME_MAX);
@@ -998,12 +1040,12 @@
 	rx_ipv4_property->ip = IPA_IP_v4;
 	rx_ipv4_property->attrib.attrib_mask = 0;
 	rx_ipv4_property->src_pipe = ecm_ipa_ctx->usb_to_ipa_client;
-	rx_ipv4_property->hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
+	rx_ipv4_property->hdr_l2_type = hdr_l2_type;
 	rx_ipv6_property = &rx_properties.prop[1];
 	rx_ipv6_property->ip = IPA_IP_v6;
 	rx_ipv6_property->attrib.attrib_mask = 0;
 	rx_ipv6_property->src_pipe = ecm_ipa_ctx->usb_to_ipa_client;
-	rx_ipv6_property->hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
+	rx_ipv6_property->hdr_l2_type = hdr_l2_type;
 	rx_properties.num_props = 2;
 
 	result = ipa_register_intf("ecm0", &tx_properties, &rx_properties);
@@ -1336,6 +1378,13 @@
 		goto fail_file;
 	}
 
+	file = debugfs_create_bool("is_vlan_mode", flags_read_only,
+		ecm_ipa_ctx->directory, &ecm_ipa_ctx->is_vlan_mode);
+	if (!file) {
+		ECM_IPA_ERROR("could not create is_vlan_mode file\n");
+		goto fail_file;
+	}
+
 	ECM_IPA_DEBUG("debugfs entries were created\n");
 	ECM_IPA_LOG_EXIT();
 
@@ -1362,8 +1411,9 @@
 /**
  * ecm_ipa_ep_cfg() - configure the USB endpoints for ECM
  *
- *usb_to_ipa_hdl: handle received from ipa_connect
- *ipa_to_usb_hdl: handle received from ipa_connect
+ * @usb_to_ipa_hdl: handle received from ipa_connect
+ * @ipa_to_usb_hdl: handle received from ipa_connect
+ * @is_vlan_mode - should driver work in vlan mode?
  *
  * USB to IPA pipe:
  *  - No de-aggregation
@@ -1374,16 +1424,21 @@
  *  - No aggregation
  *  - Add Ethernet header
  */
-static int ecm_ipa_ep_registers_cfg(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl)
+static int ecm_ipa_ep_registers_cfg(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl,
+	bool is_vlan_mode)
 {
 	int result = 0;
 	struct ipa_ep_cfg usb_to_ipa_ep_cfg;
 	struct ipa_ep_cfg ipa_to_usb_ep_cfg;
+	uint8_t hdr_add = 0;
+
 
 	ECM_IPA_LOG_ENTRY();
+	if (is_vlan_mode)
+		hdr_add = VLAN_HLEN;
 	memset(&usb_to_ipa_ep_cfg, 0, sizeof(struct ipa_ep_cfg));
 	usb_to_ipa_ep_cfg.aggr.aggr_en = IPA_BYPASS_AGGR;
-	usb_to_ipa_ep_cfg.hdr.hdr_len = ETH_HLEN;
+	usb_to_ipa_ep_cfg.hdr.hdr_len = ETH_HLEN + hdr_add;
 	usb_to_ipa_ep_cfg.nat.nat_en = IPA_SRC_NAT;
 	usb_to_ipa_ep_cfg.route.rt_tbl_hdl = 0;
 	usb_to_ipa_ep_cfg.mode.dst = IPA_CLIENT_A5_LAN_WAN_CONS;
@@ -1395,7 +1450,7 @@
 	}
 	memset(&ipa_to_usb_ep_cfg, 0, sizeof(struct ipa_ep_cfg));
 	ipa_to_usb_ep_cfg.aggr.aggr_en = IPA_BYPASS_AGGR;
-	ipa_to_usb_ep_cfg.hdr.hdr_len = ETH_HLEN;
+	ipa_to_usb_ep_cfg.hdr.hdr_len = ETH_HLEN + hdr_add;
 	ipa_to_usb_ep_cfg.nat.nat_en = IPA_BYPASS_NAT;
 	result = ipa_cfg_ep(ipa_to_usb_hdl, &ipa_to_usb_ep_cfg);
 	if (result) {
diff --git a/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c b/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c
index 2e87bd2..4958c69 100644
--- a/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c
@@ -12,6 +12,7 @@
 #include <linux/atomic.h>
 #include <linux/errno.h>
 #include <linux/etherdevice.h>
+#include <linux/if_vlan.h>
 #include <linux/debugfs.h>
 #include <linux/in.h>
 #include <linux/stddef.h>
@@ -162,6 +163,7 @@
  * @xmit_error_delayed_work: work item for cases where IPA driver Tx fails
  * @state_lock: used to protect the state variable.
  * @pm_hdl: handle for IPA PM framework
+ * @is_vlan_mode: should driver work in vlan mode?
  */
 struct rndis_ipa_dev {
 	struct net_device *net;
@@ -191,6 +193,7 @@
 	struct delayed_work xmit_error_delayed_work;
 	spinlock_t state_lock; /* Spinlock for the state variable.*/
 	u32 pm_hdl;
+	bool is_vlan_mode;
 };
 
 /**
@@ -217,19 +220,20 @@
 static void rndis_ipa_tx_timeout(struct net_device *net);
 static int rndis_ipa_stop(struct net_device *net);
 static void rndis_ipa_enable_data_path(struct rndis_ipa_dev *rndis_ipa_ctx);
-static struct sk_buff *rndis_encapsulate_skb(struct sk_buff *skb);
+static struct sk_buff *rndis_encapsulate_skb(struct sk_buff *skb,
+	struct rndis_ipa_dev *rndis_ipa_ctx);
 static void rndis_ipa_xmit_error(struct sk_buff *skb);
 static void rndis_ipa_xmit_error_aftercare_wq(struct work_struct *work);
 static void rndis_ipa_prepare_header_insertion
 	(int eth_type,
 	const char *hdr_name, struct ipa_hdr_add *add_hdr,
-	const void *dst_mac, const void *src_mac);
+	const void *dst_mac, const void *src_mac, bool is_vlan_mode);
 static int rndis_ipa_hdrs_cfg
 	(struct rndis_ipa_dev *rndis_ipa_ctx,
 	const void *dst_mac, const void *src_mac);
 static int rndis_ipa_hdrs_destroy(struct rndis_ipa_dev *rndis_ipa_ctx);
 static struct net_device_stats *rndis_ipa_get_stats(struct net_device *net);
-static int rndis_ipa_register_properties(char *netdev_name);
+static int rndis_ipa_register_properties(char *netdev_name, bool is_vlan_mode);
 static int rndis_ipa_deregister_properties(char *netdev_name);
 static void rndis_ipa_rm_notify
 	(void *user_data, enum ipa_rm_event event,
@@ -262,7 +266,8 @@
 	(u32 usb_to_ipa_hdl,
 	u32 ipa_to_usb_hdl, u32 max_xfer_size_bytes_to_dev,
 	u32 max_xfer_size_bytes_to_host, u32 mtu,
-	bool deaggr_enable);
+	bool deaggr_enable,
+	bool is_vlan_mode);
 static int rndis_ipa_set_device_ethernet_addr
 	(u8 *dev_ethaddr,
 	u8 device_ethaddr[]);
@@ -566,6 +571,14 @@
 	}
 	RNDIS_IPA_DEBUG("Device Ethernet address set %pM\n", net->dev_addr);
 
+	if (ipa_is_vlan_mode(IPA_VLAN_IF_RNDIS,
+		&rndis_ipa_ctx->is_vlan_mode)) {
+		RNDIS_IPA_ERROR("couldn't acquire vlan mode, is ipa ready?\n");
+		goto fail_get_vlan_mode;
+	}
+
+	RNDIS_IPA_DEBUG("is_vlan_mode %d\n", rndis_ipa_ctx->is_vlan_mode);
+
 	result = rndis_ipa_hdrs_cfg
 			(rndis_ipa_ctx,
 			params->host_ethaddr,
@@ -576,7 +589,8 @@
 	}
 	RNDIS_IPA_DEBUG("IPA header-insertion configed for Ethernet+RNDIS\n");
 
-	result = rndis_ipa_register_properties(net->name);
+	result = rndis_ipa_register_properties(net->name,
+		rndis_ipa_ctx->is_vlan_mode);
 	if (result) {
 		RNDIS_IPA_ERROR("fail on properties set\n");
 		goto fail_register_tx;
@@ -612,8 +626,9 @@
 	rndis_ipa_deregister_properties(net->name);
 fail_register_tx:
 	rndis_ipa_hdrs_destroy(rndis_ipa_ctx);
-fail_set_device_ethernet:
 fail_hdrs_cfg:
+fail_get_vlan_mode:
+fail_set_device_ethernet:
 	rndis_ipa_debugfs_destroy(rndis_ipa_ctx);
 fail_netdev_priv:
 	free_netdev(net);
@@ -728,7 +743,8 @@
 		max_xfer_size_bytes_to_dev,
 		max_xfer_size_bytes_to_host,
 		rndis_ipa_ctx->net->mtu,
-		rndis_ipa_ctx->deaggregation_enable);
+		rndis_ipa_ctx->deaggregation_enable,
+		rndis_ipa_ctx->is_vlan_mode);
 	if (result) {
 		RNDIS_IPA_ERROR("fail on ep cfg\n");
 		goto fail;
@@ -910,7 +926,7 @@
 		goto out;
 	}
 
-	skb = rndis_encapsulate_skb(skb);
+	skb = rndis_encapsulate_skb(skb, rndis_ipa_ctx);
 	trace_rndis_tx_dp(skb->protocol);
 	ret = ipa_tx_dp(IPA_TO_USB_CLIENT, skb, NULL);
 	if (ret) {
@@ -1456,6 +1472,7 @@
  *  for IPA->USB pipe
  * src_mac: device MAC (Ethernet) address to be added to packets
  *  for IPA->USB pipe
+ * is_vlan_mode: should driver work in vlan mode?
  *
  * This function shall build the header-insertion block request for a
  * single Ethernet+RNDIS header)
@@ -1468,23 +1485,37 @@
 static void rndis_ipa_prepare_header_insertion(
 	int eth_type,
 	const char *hdr_name, struct ipa_hdr_add *add_hdr,
-	const void *dst_mac, const void *src_mac)
+	const void *dst_mac, const void *src_mac, bool is_vlan_mode)
 {
 	struct ethhdr *eth_hdr;
+	struct vlan_ethhdr *eth_vlan_hdr;
 
 	add_hdr->hdr_len = sizeof(rndis_template_hdr);
 	add_hdr->is_partial = false;
 	strlcpy(add_hdr->name, hdr_name, IPA_RESOURCE_NAME_MAX);
 
 	memcpy(add_hdr->hdr, &rndis_template_hdr, sizeof(rndis_template_hdr));
-	eth_hdr = (struct ethhdr *)(add_hdr->hdr + sizeof(rndis_template_hdr));
-	memcpy(eth_hdr->h_dest, dst_mac, ETH_ALEN);
-	memcpy(eth_hdr->h_source, src_mac, ETH_ALEN);
-	eth_hdr->h_proto = htons(eth_type);
-	add_hdr->hdr_len += ETH_HLEN;
 	add_hdr->is_eth2_ofst_valid = true;
 	add_hdr->eth2_ofst = sizeof(rndis_template_hdr);
-	add_hdr->type = IPA_HDR_L2_ETHERNET_II;
+
+	if (is_vlan_mode) {
+		eth_vlan_hdr = (struct vlan_ethhdr *)(add_hdr->hdr +
+			sizeof(rndis_template_hdr));
+		memcpy(eth_vlan_hdr->h_dest, dst_mac, ETH_ALEN);
+		memcpy(eth_vlan_hdr->h_source, src_mac, ETH_ALEN);
+		eth_vlan_hdr->h_vlan_encapsulated_proto = htons(eth_type);
+		eth_vlan_hdr->h_vlan_proto = htons(ETH_P_8021Q);
+		add_hdr->hdr_len += VLAN_ETH_HLEN;
+		add_hdr->type = IPA_HDR_L2_802_1Q;
+	} else {
+		eth_hdr = (struct ethhdr *)(add_hdr->hdr +
+			sizeof(rndis_template_hdr));
+		memcpy(eth_hdr->h_dest, dst_mac, ETH_ALEN);
+		memcpy(eth_hdr->h_source, src_mac, ETH_ALEN);
+		eth_hdr->h_proto = htons(eth_type);
+		add_hdr->hdr_len += ETH_HLEN;
+		add_hdr->type = IPA_HDR_L2_ETHERNET_II;
+	}
 }
 
 /**
@@ -1526,10 +1557,10 @@
 	ipv6_hdr = &hdrs->hdr[1];
 	rndis_ipa_prepare_header_insertion
 		(ETH_P_IP, IPV4_HDR_NAME,
-		ipv4_hdr, dst_mac, src_mac);
+		ipv4_hdr, dst_mac, src_mac, rndis_ipa_ctx->is_vlan_mode);
 	rndis_ipa_prepare_header_insertion
 		(ETH_P_IPV6, IPV6_HDR_NAME,
-		ipv6_hdr, dst_mac, src_mac);
+		ipv6_hdr, dst_mac, src_mac, rndis_ipa_ctx->is_vlan_mode);
 
 	hdrs->commit = 1;
 	hdrs->num_hdrs = 2;
@@ -1610,6 +1641,7 @@
  * rndis_ipa_register_properties() - set Tx/Rx properties needed
  *  by IPA configuration manager
  * @netdev_name: a string with the name of the network interface device
+ * @is_vlan_mode: should driver work in vlan mode?
  *
  * Register Tx/Rx properties to allow user space configuration (IPA
  * Configuration Manager):
@@ -1628,7 +1660,7 @@
  *   This rules shall be added based on the attribute mask supplied at
  *   this function, that is, always hit rule.
  */
-static int rndis_ipa_register_properties(char *netdev_name)
+static int rndis_ipa_register_properties(char *netdev_name, bool is_vlan_mode)
 {
 	struct ipa_tx_intf tx_properties = {0};
 	struct ipa_ioc_tx_intf_prop properties[2] = { {0}, {0} };
@@ -1638,10 +1670,14 @@
 	struct ipa_rx_intf rx_properties = {0};
 	struct ipa_ioc_rx_intf_prop *rx_ipv4_property;
 	struct ipa_ioc_rx_intf_prop *rx_ipv6_property;
+	enum ipa_hdr_l2_type hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
 	int result = 0;
 
 	RNDIS_IPA_LOG_ENTRY();
 
+	if (is_vlan_mode)
+		hdr_l2_type = IPA_HDR_L2_802_1Q;
+
 	tx_properties.prop = properties;
 	ipv4_property = &tx_properties.prop[0];
 	ipv4_property->ip = IPA_IP_v4;
@@ -1649,14 +1685,14 @@
 	strlcpy
 		(ipv4_property->hdr_name, IPV4_HDR_NAME,
 		IPA_RESOURCE_NAME_MAX);
-	ipv4_property->hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
+	ipv4_property->hdr_l2_type = hdr_l2_type;
 	ipv6_property = &tx_properties.prop[1];
 	ipv6_property->ip = IPA_IP_v6;
 	ipv6_property->dst_pipe = IPA_TO_USB_CLIENT;
 	strlcpy
 		(ipv6_property->hdr_name, IPV6_HDR_NAME,
 		IPA_RESOURCE_NAME_MAX);
-	ipv6_property->hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
+	ipv6_property->hdr_l2_type = hdr_l2_type;
 	tx_properties.num_props = 2;
 
 	rx_properties.prop = rx_ioc_properties;
@@ -1664,12 +1700,12 @@
 	rx_ipv4_property->ip = IPA_IP_v4;
 	rx_ipv4_property->attrib.attrib_mask = 0;
 	rx_ipv4_property->src_pipe = IPA_CLIENT_USB_PROD;
-	rx_ipv4_property->hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
+	rx_ipv4_property->hdr_l2_type = hdr_l2_type;
 	rx_ipv6_property = &rx_properties.prop[1];
 	rx_ipv6_property->ip = IPA_IP_v6;
 	rx_ipv6_property->attrib.attrib_mask = 0;
 	rx_ipv6_property->src_pipe = IPA_CLIENT_USB_PROD;
-	rx_ipv6_property->hdr_l2_type = IPA_HDR_L2_ETHERNET_II;
+	rx_ipv6_property->hdr_l2_type = hdr_l2_type;
 	rx_properties.num_props = 2;
 
 	result = ipa_register_intf("rndis0", &tx_properties, &rx_properties);
@@ -1948,12 +1984,14 @@
  * rndis_encapsulate_skb() - encapsulate the given Ethernet skb with
  *  an RNDIS header
  * @skb: packet to be encapsulated with the RNDIS header
+ * @rndis_ipa_ctx: main driver context
  *
  * Shall use a template header for RNDIS and update it with the given
  * skb values.
  * Ethernet is expected to be already encapsulate the packet.
  */
-static struct sk_buff *rndis_encapsulate_skb(struct sk_buff *skb)
+static struct sk_buff *rndis_encapsulate_skb(struct sk_buff *skb,
+	struct rndis_ipa_dev *rndis_ipa_ctx)
 {
 	struct rndis_pkt_hdr *rndis_hdr;
 	int payload_byte_len = skb->len;
@@ -1971,6 +2009,10 @@
 		skb = new_skb;
 	}
 
+	if (rndis_ipa_ctx->is_vlan_mode)
+		if (unlikely(skb->protocol != ETH_P_8021Q))
+			RNDIS_IPA_DEBUG("ether_type != ETH_P_8021Q && vlan\n");
+
 	/* make room at the head of the SKB to put the RNDIS header */
 	rndis_hdr = (struct rndis_pkt_hdr *)skb_push(skb,
 					sizeof(rndis_template_hdr));
@@ -2046,6 +2088,8 @@
  * @max_xfer_size_bytes_to_host: the maximum size, in bytes, that the host
  *  expects to receive from the device. supplied on REMOTE_NDIS_INITIALIZE_MSG.
  * @mtu: the netdev MTU size, in bytes
+ * @deaggr_enable: should deaggregation be enabled?
+ * @is_vlan_mode: should driver work in vlan mode?
  *
  * USB to IPA pipe:
  *  - de-aggregation
@@ -2064,7 +2108,8 @@
 	u32 max_xfer_size_bytes_to_dev,
 	u32 max_xfer_size_bytes_to_host,
 	u32 mtu,
-	bool deaggr_enable)
+	bool deaggr_enable,
+	bool is_vlan_mode)
 {
 	int result;
 	struct ipa_ep_cfg *usb_to_ipa_ep_cfg;
@@ -2077,6 +2122,20 @@
 		RNDIS_IPA_DEBUG("deaggregation disabled\n");
 	}
 
+	if (is_vlan_mode) {
+		usb_to_ipa_ep_cfg->hdr.hdr_len =
+			VLAN_ETH_HLEN + sizeof(struct rndis_pkt_hdr);
+		ipa_to_usb_ep_cfg.hdr.hdr_len =
+			VLAN_ETH_HLEN + sizeof(struct rndis_pkt_hdr);
+		ipa_to_usb_ep_cfg.hdr.hdr_additional_const_len = VLAN_ETH_HLEN;
+	} else {
+		usb_to_ipa_ep_cfg->hdr.hdr_len =
+			ETH_HLEN + sizeof(struct rndis_pkt_hdr);
+		ipa_to_usb_ep_cfg.hdr.hdr_len =
+			ETH_HLEN + sizeof(struct rndis_pkt_hdr);
+		ipa_to_usb_ep_cfg.hdr.hdr_additional_const_len = ETH_HLEN;
+	}
+
 	usb_to_ipa_ep_cfg->deaggr.max_packet_len = max_xfer_size_bytes_to_dev;
 	result = ipa_cfg_ep(usb_to_ipa_hdl, usb_to_ipa_ep_cfg);
 	if (result) {
@@ -2452,6 +2511,14 @@
 		goto fail_file;
 	}
 
+	file = debugfs_create_bool("is_vlan_mode", flags_read_only,
+		rndis_ipa_ctx->directory,
+		&rndis_ipa_ctx->is_vlan_mode);
+	if (!file) {
+		RNDIS_IPA_ERROR("fail to create is_vlan_mode file\n");
+		goto fail_file;
+	}
+
 	RNDIS_IPA_DEBUG("debugfs entries were created\n");
 	RNDIS_IPA_LOG_EXIT();
 
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index af4d448..09d2ca0 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -4633,7 +4633,7 @@
 {
 	unsigned long missing;
 
-	char dbg_buff[16] = { 0 };
+	char dbg_buff[32] = { 0 };
 
 	if (sizeof(dbg_buff) < count + 1)
 		return -EFAULT;
@@ -4648,19 +4648,44 @@
 	if (count > 0)
 		dbg_buff[count - 1] = '\0';
 
+	IPADBG("user input string %s\n", dbg_buff);
+
 	/* Prevent consequent calls from trying to load the FW again. */
 	if (ipa3_is_ready())
 		return count;
 
 	/* Check MHI configuration on MDM devices */
 	if (!ipa3_is_msm_device()) {
+
+		if (strnstr(dbg_buff, "vlan", strlen(dbg_buff))) {
+			if (strnstr(dbg_buff, "eth", strlen(dbg_buff)))
+				ipa3_ctx->vlan_mode_iface[IPA_VLAN_IF_EMAC] =
+				true;
+			if (strnstr(dbg_buff, "rndis", strlen(dbg_buff)))
+				ipa3_ctx->vlan_mode_iface[IPA_VLAN_IF_RNDIS] =
+				true;
+			if (strnstr(dbg_buff, "ecm", strlen(dbg_buff)))
+				ipa3_ctx->vlan_mode_iface[IPA_VLAN_IF_ECM] =
+				true;
+
+			/*
+			 * when vlan mode is passed to our dev we expect
+			 * another write
+			 */
+			return count;
+		}
+
 		if (!strcasecmp(dbg_buff, "MHI")) {
 			ipa3_ctx->ipa_config_is_mhi = true;
 			pr_info(
-			"IPA is loading with MHI configuration\n");
-		} else {
+				"IPA is loading with MHI configuration\n");
+		} else if (!strcmp(dbg_buff, "1\n")) {
 			pr_info(
-			"IPA is loading with non MHI configuration\n");
+				"IPA is loading with non MHI configuration\n");
+		} else {
+			IPAERR("got invalid string %s not loading FW\n",
+				dbg_buff);
+			return count;
 		}
 	}
 
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index adbd7b8..d7d74a3 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -1373,6 +1373,7 @@
 	int num_ipa_cne_evt_req;
 	struct mutex ipa_cne_evt_lock;
 	bool use_ipa_pm;
+	bool vlan_mode_iface[IPA_VLAN_IF_MAX];
 };
 
 struct ipa3_plat_drv_res {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index fb29d00..e450ab6 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -4318,6 +4318,38 @@
 	ipa3_ctx->tag_process_before_gating = val;
 }
 
+/**
+ * ipa3_is_vlan_mode - check if a LAN driver should load in VLAN mode
+ * @iface - type of vlan capable device
+ * @res - query result: true for vlan mode, false for non vlan mode
+ *
+ * API must be called after ipa_is_ready() returns true, otherwise it will fail
+ *
+ * Returns: 0 on success, negative on failure
+ */
+static int ipa3_is_vlan_mode(enum ipa_vlan_ifaces iface, bool *res)
+{
+	if (!res) {
+		IPAERR("NULL out param\n");
+		return -EINVAL;
+	}
+
+	if (iface < 0 || iface > IPA_VLAN_IF_MAX) {
+		IPAERR("invalid iface %d\n", iface);
+		return -EINVAL;
+	}
+
+	if (!ipa3_is_ready()) {
+		IPAERR("IPA is not ready yet\n");
+		return -ENODEV;
+	}
+
+	*res = ipa3_ctx->vlan_mode_iface[iface];
+
+	IPADBG("Driver %d vlan mode is %d\n", iface, *res);
+	return 0;
+}
+
 int ipa3_bind_api_controller(enum ipa_hw_type ipa_hw_type,
 	struct ipa_api_controller *api_ctrl)
 {
@@ -4504,6 +4536,7 @@
 	api_ctrl->ipa_disable_wdi3_pipes = ipa3_disable_wdi3_pipes;
 	api_ctrl->ipa_tz_unlock_reg = ipa3_tz_unlock_reg;
 	api_ctrl->ipa_get_smmu_params = ipa3_get_smmu_params;
+	api_ctrl->ipa_is_vlan_mode = ipa3_is_vlan_mode;
 
 	return 0;
 }
diff --git a/include/linux/ipa.h b/include/linux/ipa.h
index 405aed5..46ee6da 100644
--- a/include/linux/ipa.h
+++ b/include/linux/ipa.h
@@ -117,6 +117,19 @@
 };
 
 /**
+* enum ipa_vlan_ifaces - vlan interfaces types
+* @IPA_VLAN_IF_EMAC: used for EMAC ethernet device
+* @IPA_VLAN_IF_RNDIS: used for RNDIS USB device
+* @IPA_VLAN_IF_ECM: used for ECM USB device
+*/
+enum ipa_vlan_ifaces {
+	IPA_VLAN_IF_EMAC,
+	IPA_VLAN_IF_RNDIS,
+	IPA_VLAN_IF_ECM,
+	IPA_VLAN_IF_MAX
+};
+
+/**
  * struct ipa_ep_cfg_nat - NAT configuration in IPA end-point
  * @nat_en:	This defines the default NAT mode for the pipe: in case of
  *		filter miss - the default NAT mode defines the NATing operation
@@ -1585,10 +1598,18 @@
  * Returns: 0 on success, negative on failure
  */
 int ipa_tz_unlock_reg(struct ipa_tz_unlock_reg_info *reg_info, u16 num_regs);
-
 int ipa_get_smmu_params(struct ipa_smmu_in_params *in,
 	struct ipa_smmu_out_params *out);
-
+/**
+ * ipa_is_vlan_mode - check if a LAN driver should load in VLAN mode
+ * @iface - type of vlan capable device
+ * @res - query result: true for vlan mode, false for non vlan mode
+ *
+ * API must be called after ipa_is_ready() returns true, otherwise it will fail
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_is_vlan_mode(enum ipa_vlan_ifaces iface, bool *res);
 #else /* (CONFIG_IPA || CONFIG_IPA3) */
 
 /*
@@ -2382,6 +2403,11 @@
 {
 	return -EPERM;
 }
+
+static inline int ipa_is_vlan_mode(enum ipa_vlan_ifaces iface, bool *res)
+{
+	return -EPERM;
+}
 #endif /* (CONFIG_IPA || CONFIG_IPA3) */
 
 #endif /* _IPA_H_ */
diff --git a/include/uapi/linux/msm_ipa.h b/include/uapi/linux/msm_ipa.h
index d3b9a33..ef07f78 100644
--- a/include/uapi/linux/msm_ipa.h
+++ b/include/uapi/linux/msm_ipa.h
@@ -819,13 +819,17 @@
  * IPA_HDR_L2_NONE: L2 header which isn't Ethernet II and isn't 802_3
  * IPA_HDR_L2_ETHERNET_II: L2 header of type Ethernet II
  * IPA_HDR_L2_802_3: L2 header of type 802_3
+ * IPA_HDR_L2_802_1Q: L2 header of type 802_1Q
  */
 enum ipa_hdr_l2_type {
 	IPA_HDR_L2_NONE,
 	IPA_HDR_L2_ETHERNET_II,
 	IPA_HDR_L2_802_3,
+	IPA_HDR_L2_802_1Q,
 };
-#define IPA_HDR_L2_MAX (IPA_HDR_L2_802_3 + 1)
+#define IPA_HDR_L2_MAX (IPA_HDR_L2_802_1Q + 1)
+
+#define IPA_HDR_L2_802_1Q IPA_HDR_L2_802_1Q
 
 /**
  * enum ipa_hdr_l2_type - Processing context type