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