Merge "[qca-nss-drv] Fix tailroom for ipsec data"
diff --git a/Makefile b/Makefile
index 5de395e..8a43073 100644
--- a/Makefile
+++ b/Makefile
@@ -42,6 +42,7 @@
 			nss_sjack.o \
 			nss_stats.o \
 			nss_tun6rd.o \
+			nss_pptp.o \
 			nss_l2tpv2.o \
 			nss_tunipip6.o \
 			nss_virt_if.o \
diff --git a/exports/nss_api_if.h b/exports/nss_api_if.h
index 186d296..95b3d05 100644
--- a/exports/nss_api_if.h
+++ b/exports/nss_api_if.h
@@ -40,6 +40,7 @@
 #include "nss_cmn.h"
 #include "nss_tun6rd.h"
 #include "nss_l2tpv2.h"
+#include "nss_pptp.h"
 #include "nss_tunipip6.h"
 #include "nss_lag.h"
 #include "nss_ipv4.h"
@@ -136,6 +137,7 @@
 #define NSS_LAG3_INTERFACE_NUM (NSS_SPECIAL_IF_START + 33) /* Special IF number for LAG3 */
 #define NSS_L2TPV2_INTERFACE (NSS_SPECIAL_IF_START + 34) /* Special L2TPv2 UDP encap interface */
 #define NSS_TSTAMP_INTERFACE (NSS_SPECIAL_IF_START + 35) /* Special IF number for Tstamp interface */
+#define NSS_PPTP_INTERFACE (NSS_SPECIAL_IF_START + 36) /* Special PPTP-Decap interface */
 
 /**
  * This macro converts format for IPv6 address (from Linux to NSS)
diff --git a/exports/nss_dynamic_interface.h b/exports/nss_dynamic_interface.h
index 1724976..b93ed2a 100644
--- a/exports/nss_dynamic_interface.h
+++ b/exports/nss_dynamic_interface.h
@@ -39,6 +39,7 @@
 	NSS_DYNAMIC_INTERFACE_TYPE_RADIO_2,		/* WIFI radio type2 */
 	NSS_DYNAMIC_INTERFACE_TYPE_VIRTIF_DEPRECATED,
 	NSS_DYNAMIC_INTERFACE_TYPE_L2TPV2,		/* L2TPV2 Interface Type */
+	NSS_DYNAMIC_INTERFACE_TYPE_PPTP,		/* PPTP VPN Interface Type */
 	NSS_DYNAMIC_INTERFACE_TYPE_MAX
 };
 
diff --git a/exports/nss_pptp.h b/exports/nss_pptp.h
new file mode 100644
index 0000000..ab27550
--- /dev/null
+++ b/exports/nss_pptp.h
@@ -0,0 +1,167 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+/*
+ * nss_pptp.h
+ *	NSS TO HLOS interface definitions.
+ */
+#ifndef _NSS_PPTP_H_
+#define _NSS_PPTP_H_
+
+/*
+ * Maximum no of pptp sessions supported
+ */
+#define NSS_MAX_PPTP_DYNAMIC_INTERFACES 4
+
+/**
+ *  request/response types
+ */
+enum nss_pptp_metadata_types {
+	NSS_PPTP_MSG_SESSION_CONFIGURE,		/**< session create message */
+	NSS_PPTP_MSG_SESSION_DECONFIGURE,		/**< session delete message */
+	NSS_PPTP_MSG_SYNC_STATS,		/**< session stats sync message */
+	NSS_PPTP_MSG_MAX
+};
+
+/**
+ * PPTP session configuration message structure
+ */
+struct nss_pptp_session_configure_msg {
+	uint16_t src_call_id;	/**< local call id */
+	uint16_t dst_call_id;	/**< peer call id */
+	uint32_t sip;		/**< local tunnel end point */
+	uint32_t dip;		/**< remote tunnel end point */
+};
+
+/**
+ * PPTP session deconfiguration message structure
+ */
+struct nss_pptp_session_deconfigure_msg {
+	uint16_t src_call_id;	/**< local call id */
+};
+
+/**
+ * pptp statistics sync message structure.
+ */
+struct nss_pptp_sync_session_stats_msg {
+	struct nss_cmn_node_stats node_stats;	/**< common node stats */
+	uint32_t rx_dropped;		/**< rx dropped */
+	uint32_t tx_dropped;		/**< tx dropped */
+	uint32_t rx_ppp_lcp_pkts;	/**< PPP LCP packets received */
+	uint32_t rx_exception_data_pkts;/**< Data packets exceptioned to host */
+};
+
+/**
+ * Message structure to send/receive pptp messages
+ */
+struct nss_pptp_msg {
+	struct nss_cmn_msg cm;	/**< Message Header */
+	union {
+		struct nss_pptp_session_configure_msg session_configure_msg; /**< session configure message */
+		struct nss_pptp_session_deconfigure_msg session_deconfigure_msg; /**< session deconfigure message */
+		struct nss_pptp_sync_session_stats_msg stats;		/**< session stats message */
+	} msg;
+};
+
+/**
+ * @brief Callback to receive pptp  messages
+ *
+ * @return void
+ */
+typedef void (*nss_pptp_msg_callback_t)(void *app_data, struct nss_pptp_msg *msg);
+
+/**
+ * @brief Send pptp messages
+ *
+ * @param nss_ctx NSS context
+ * @param msg NSS pptp tunnel message
+ *
+ * @return nss_tx_status_t Tx status
+ */
+extern nss_tx_status_t nss_pptp_tx(struct nss_ctx_instance *nss_ctx, struct nss_pptp_msg *msg);
+
+/**
+ * @brief Get the pptp context used in the nss_pptp_tx
+ *
+ * @return struct nss_ctx_instance *NSS context
+ */
+extern struct nss_ctx_instance *nss_pptp_get_context(void);
+
+/**
+ * @brief Callback when pptp tunnel data is received
+ *
+ * @param netdevice of pptp session
+ * @param skb Pointer to data buffer
+ * @param napi pointer
+ *
+ * @return void
+ */
+typedef void (*nss_pptp_callback_t)(struct net_device *netdev, struct sk_buff *skb, struct napi_struct *napi);
+
+/**
+ * @brief Register to send/receive pptp tunnel messages to NSS
+ *
+ * @param if_num NSS interface number
+ * @param pptp_callback Callback for pptp tunnel data
+ * @param msg_callback Callback for pptp tunnel messages
+ * @param netdev netdevice associated with the pptp tunnel
+ * @param features denotes the skb types supported by this interface
+ *
+ * @return nss_ctx_instance* NSS context
+ */
+extern struct nss_ctx_instance *nss_register_pptp_if(uint32_t if_num, nss_pptp_callback_t pptp_callback,
+					nss_pptp_msg_callback_t msg_callback, struct net_device *netdev, uint32_t features);
+
+/**
+ * @brief Unregister pptp tunnel interface with NSS
+ *
+ * @param if_num NSS interface number
+ *
+ * @return void
+ */
+extern void nss_unregister_pptp_if(uint32_t if_num);
+
+/**
+ * @brief Initialize pptp msg
+ *
+ * @param nss_pptp_msg PPTP session info i.e Configure/Deconfigure
+ * @param if_num Interface number
+ * @param type Message type
+ * @param len Message length
+ * @param cb message callback
+ * @param app_data
+ *
+ * @return None
+ */
+extern void nss_pptp_msg_init(struct nss_pptp_msg *ncm, uint16_t if_num, uint32_t type,  uint32_t len, void *cb, void *app_data);
+
+/**
+ * @brief register pptp nss debug stats handler
+ *
+ * @return None
+ */
+extern void nss_pptp_register_handler(void);
+
+/**
+ * @brief get pptp nss session debug stats. stats_mem should be large enought to hold all stats.
+ *
+ * @param memory address to be copied to
+ *
+ * @return None
+ */
+extern void nss_pptp_session_debug_stats_get(void *stats_mem);
+
+#endif /* _NSS_PPTP_H_ */
diff --git a/nss_core.h b/nss_core.h
index beac10b..edec550 100755
--- a/nss_core.h
+++ b/nss_core.h
@@ -543,6 +543,24 @@
 };
 
 /*
+ * PPTP debug stats
+ */
+enum nss_stats_pptp_session {
+	NSS_STATS_PPTP_SESSION_RX_DROPPED,	/* Number of received packets dropped */
+	NSS_STATS_PPTP_SESSION_TX_DROPPED,	/* Number of packets dropped in trasmit direction */
+	NSS_STATS_PPTP_SESSION_RX_PPP_LCP_PKTS,	/* Number of ppp lcp packets received */
+	NSS_STATS_PPTP_SESSION_RX_EXP_DATA_PKTS,	/* Number of RX exceptioned packets */
+	NSS_STATS_PPTP_SESSION_MAX
+};
+
+struct nss_stats_pptp_session_debug {
+	uint64_t stats[NSS_STATS_PPTP_SESSION_MAX];
+	int32_t if_index;
+	uint32_t if_num; /* nss interface number */
+	bool valid;
+};
+
+/*
  * NSS core state
  */
 enum nss_core_state {
@@ -696,6 +714,7 @@
 	struct dentry *lso_rx_dentry;	/* LSO_RX stats dentry */
 	struct dentry *drv_dentry;	/* HLOS driver stats dentry */
 	struct dentry *pppoe_dentry;	/* PPPOE stats dentry */
+	struct dentry *pptp_dentry;	/* PPTP  stats dentry */
 	struct dentry *l2tpv2_dentry;	/* L2TPV2  stats dentry */
 	struct dentry *gmac_dentry;	/* GMAC ethnode stats dentry */
 	struct dentry *capwap_decap_dentry;     /* CAPWAP decap ethnode stats dentry */
@@ -726,6 +745,7 @@
 	uint8_t wlan_handler_id;
 	uint8_t tun6rd_handler_id;
 	uint8_t wifi_handler_id;
+	uint8_t pptp_handler_id;
 	uint8_t l2tpv2_handler_id;
 	uint8_t tunipip6_handler_id;
 	uint8_t frequency_handler_id;
@@ -765,6 +785,8 @@
 					/* l2tP tunnel interface event callback function */
 	nss_tunipip6_msg_callback_t tunipip6_msg_callback;
 					/* ipip6 tunnel interface event callback function */
+	nss_pptp_msg_callback_t pptp_msg_callback;
+					/* PPTP tunnel interface event callback function */
 	struct nss_shaper_bounce_registrant bounce_interface_registrants[NSS_MAX_NET_INTERFACES];
 					/* Registrants for interface shaper bounce operations */
 	struct nss_shaper_bounce_registrant bounce_bridge_registrants[NSS_MAX_NET_INTERFACES];
@@ -957,6 +979,7 @@
 	enum nss_feature_enabled ipsec_enabled;		/* Does this core handle IPsec? */
 	enum nss_feature_enabled wlanredirect_enabled;	/* Does this core handle WLAN redirect? */
 	enum nss_feature_enabled tun6rd_enabled;	/* Does this core handle 6rd Tunnel ? */
+	enum nss_feature_enabled pptp_enabled;		/* Does this core handle pptp Tunnel ? */
 	enum nss_feature_enabled l2tpv2_enabled;	/* Does this core handle l2tpv2 Tunnel ? */
 	enum nss_feature_enabled tunipip6_enabled;	/* Does this core handle ipip6 Tunnel ? */
 	enum nss_feature_enabled gre_redir_enabled;	/* Does this core handle gre_redir Tunnel ? */
diff --git a/nss_hal/ipq806x/nss_hal_pvt.c b/nss_hal/ipq806x/nss_hal_pvt.c
index dfa7420..8891e1b 100644
--- a/nss_hal/ipq806x/nss_hal_pvt.c
+++ b/nss_hal/ipq806x/nss_hal_pvt.c
@@ -883,7 +883,9 @@
 	    || of_property_read_u32(np, "qcom,ipsec_enabled", &npd->ipsec_enabled)
 	    || of_property_read_u32(np, "qcom,wlanredirect_enabled", &npd->wlanredirect_enabled)
 	    || of_property_read_u32(np, "qcom,tun6rd_enabled", &npd->tun6rd_enabled)
+	    || of_property_read_u32(np, "qcom,l2tpv2_enabled", &npd->l2tpv2_enabled)
 	    || of_property_read_u32(np, "qcom,tunipip6_enabled", &npd->tunipip6_enabled)
+	    || of_property_read_u32(np, "qcom,pptp_enabled", &npd->tunipip6_enabled)
 	    || of_property_read_u32(np, "qcom,shaping_enabled", &npd->shaping_enabled)) {
 		pr_warn("%s: error reading non-critical device node properties\n", np->name);
 	}
@@ -1420,6 +1422,11 @@
 		nss_top->tun6rd_handler_id = nss_dev->id;
 	}
 
+	if (npd->pptp_enabled == NSS_FEATURE_ENABLED) {
+		nss_top->pptp_handler_id = nss_dev->id;
+		nss_pptp_register_handler();
+	}
+
 	if (npd->l2tpv2_enabled == NSS_FEATURE_ENABLED) {
 		nss_top->l2tpv2_handler_id = nss_dev->id;
 		nss_l2tpv2_register_handler();
diff --git a/nss_l2tpv2.c b/nss_l2tpv2.c
index a37ee92..5b991e1 100644
--- a/nss_l2tpv2.c
+++ b/nss_l2tpv2.c
@@ -36,7 +36,6 @@
 		if (nss_l2tpv2_session_debug_stats[i].if_num == if_num) {
 			nss_l2tpv2_session_debug_stats[i].stats[NSS_STATS_L2TPV2_SESSION_RX_PPP_LCP_PKTS] += stats_msg->debug_stats.rx_ppp_lcp_pkts;
 			nss_l2tpv2_session_debug_stats[i].stats[NSS_STATS_L2TPV2_SESSION_RX_EXP_DATA_PKTS] += stats_msg->debug_stats.rx_exception_data_pkts;
-			nss_l2tpv2_session_debug_stats[i].stats[NSS_STATS_L2TPV2_SESSION_RX_EXP_DATA_PKTS] += stats_msg->debug_stats.rx_exception_data_pkts;
 			nss_l2tpv2_session_debug_stats[i].stats[NSS_STATS_L2TPV2_SESSION_ENCAP_PBUF_ALLOC_FAIL_PKTS] += stats_msg->debug_stats.encap_pbuf_alloc_fail;
 			nss_l2tpv2_session_debug_stats[i].stats[NSS_STATS_L2TPV2_SESSION_DECAP_PBUF_ALLOC_FAIL_PKTS] += stats_msg->debug_stats.decap_pbuf_alloc_fail;
 			break;
diff --git a/nss_pptp.c b/nss_pptp.c
new file mode 100644
index 0000000..3a6fd98
--- /dev/null
+++ b/nss_pptp.c
@@ -0,0 +1,276 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+#include <net/sock.h>
+#include "nss_tx_rx_common.h"
+
+/*
+ * Data structures to store pptp nss debug stats
+ */
+static DEFINE_SPINLOCK(nss_pptp_session_debug_stats_lock);
+static struct nss_stats_pptp_session_debug nss_pptp_session_debug_stats[NSS_MAX_PPTP_DYNAMIC_INTERFACES];
+
+/*
+ * nss_pptp_session_debug_stats_sync
+ *	Per session debug stats for pptp
+ */
+void nss_pptp_session_debug_stats_sync(struct nss_ctx_instance *nss_ctx, struct nss_pptp_sync_session_stats_msg *stats_msg, uint16_t if_num)
+{
+	int i;
+	spin_lock_bh(&nss_pptp_session_debug_stats_lock);
+	for (i = 0; i < NSS_MAX_PPTP_DYNAMIC_INTERFACES; i++) {
+		if (nss_pptp_session_debug_stats[i].if_num == if_num) {
+			nss_pptp_session_debug_stats[i].stats[NSS_STATS_PPTP_SESSION_RX_DROPPED] += stats_msg->rx_dropped;
+			nss_pptp_session_debug_stats[i].stats[NSS_STATS_PPTP_SESSION_TX_DROPPED] += stats_msg->tx_dropped;
+			nss_pptp_session_debug_stats[i].stats[NSS_STATS_PPTP_SESSION_RX_PPP_LCP_PKTS] += stats_msg->rx_ppp_lcp_pkts;
+			nss_pptp_session_debug_stats[i].stats[NSS_STATS_PPTP_SESSION_RX_EXP_DATA_PKTS] += stats_msg->rx_exception_data_pkts;
+			break;
+		}
+	}
+	spin_unlock_bh(&nss_pptp_session_debug_stats_lock);
+}
+
+/*
+ * nss_pptp_global_session_stats_get()
+ *	Get session pptp statitics.
+ */
+void nss_pptp_session_debug_stats_get(void *stats_mem)
+{
+	struct nss_stats_pptp_session_debug *stats = (struct nss_stats_pptp_session_debug *)stats_mem;
+	int i;
+
+	if (!stats) {
+		nss_warning("No memory to copy pptp session stats");
+		return;
+	}
+
+	spin_lock_bh(&nss_pptp_session_debug_stats_lock);
+	for (i = 0; i < NSS_MAX_PPTP_DYNAMIC_INTERFACES; i++) {
+		if (nss_pptp_session_debug_stats[i].valid) {
+			memcpy(stats, &nss_pptp_session_debug_stats[i], sizeof(struct nss_stats_pptp_session_debug));
+			stats++;
+		}
+	}
+	spin_unlock_bh(&nss_pptp_session_debug_stats_lock);
+}
+
+/*
+ * nss_pptp_handler()
+ *	Handle NSS -> HLOS messages for pptp tunnel
+ */
+static void nss_pptp_handler(struct nss_ctx_instance *nss_ctx, struct nss_cmn_msg *ncm, __attribute__((unused))void *app_data)
+{
+	struct nss_pptp_msg *ntm = (struct nss_pptp_msg *)ncm;
+	void *ctx;
+
+	nss_pptp_msg_callback_t cb;
+
+	BUG_ON(!(nss_is_dynamic_interface(ncm->interface) || ncm->interface == NSS_PPTP_INTERFACE));
+
+	/*
+	 * Is this a valid request/response packet?
+	 */
+	if (ncm->type >= NSS_PPTP_MSG_MAX) {
+		nss_warning("%p: received invalid message %d for PPTP interface", nss_ctx, ncm->type);
+		return;
+	}
+
+	if (ncm->len > sizeof(struct nss_pptp_msg)) {
+		nss_warning("%p: tx request for another interface: %d", nss_ctx, ncm->interface);
+		return;
+	}
+
+	switch (ntm->cm.type) {
+
+	case NSS_PPTP_MSG_SYNC_STATS:
+		/*
+		 * session debug stats embeded in session stats msg
+		 */
+		nss_pptp_session_debug_stats_sync(nss_ctx, &ntm->msg.stats, ncm->interface);
+		break;
+	}
+
+	/*
+	 * Update the callback and app_data for NOTIFY messages, pptp sends all notify messages
+	 * to the same callback/app_data.
+	 */
+	if (ncm->response == NSS_CMM_RESPONSE_NOTIFY) {
+		ncm->cb = (uint32_t)nss_ctx->nss_top->pptp_msg_callback;
+	}
+
+	/*
+	 * Log failures
+	 */
+	nss_core_log_msg_failures(nss_ctx, ncm);
+
+	/*
+	 * Do we have a call back
+	 */
+	if (!ncm->cb) {
+		return;
+	}
+
+	/*
+	 * callback
+	 */
+	cb = (nss_pptp_msg_callback_t)ncm->cb;
+	ctx =  nss_ctx->nss_top->subsys_dp_register[ncm->interface].ndev;
+
+	/*
+	 * call pptp tunnel callback
+	 */
+	if (!ctx) {
+		nss_warning("%p: Event received for pptp tunnel interface %d before registration", nss_ctx, ncm->interface);
+		return;
+	}
+
+	cb(ctx, ntm);
+}
+
+/*
+ * nss_pptp_tx()
+ *	Transmit a pptp message to NSS firmware
+ */
+nss_tx_status_t nss_pptp_tx(struct nss_ctx_instance *nss_ctx, struct nss_pptp_msg *msg)
+{
+	struct nss_pptp_msg *nm;
+	struct nss_cmn_msg *ncm = &msg->cm;
+	struct sk_buff *nbuf;
+	int32_t status;
+
+	NSS_VERIFY_CTX_MAGIC(nss_ctx);
+	if (unlikely(nss_ctx->state != NSS_CORE_STATE_INITIALIZED)) {
+		nss_warning("%p: pptp msg dropped as core not ready", nss_ctx);
+		return NSS_TX_FAILURE_NOT_READY;
+	}
+
+	/*
+	 * Sanity check the message
+	 */
+	if (!nss_is_dynamic_interface(ncm->interface)) {
+		nss_warning("%p: tx request for non dynamic interface: %d", nss_ctx, ncm->interface);
+		return NSS_TX_FAILURE;
+	}
+
+	if (ncm->type > NSS_PPTP_MSG_MAX) {
+		nss_warning("%p: message type out of range: %d", nss_ctx, ncm->type);
+		return NSS_TX_FAILURE;
+	}
+
+	if (ncm->len > sizeof(struct nss_pptp_msg)) {
+		nss_warning("%p: message length is invalid: %d", nss_ctx, ncm->len);
+		return NSS_TX_FAILURE;
+	}
+
+	nbuf = dev_alloc_skb(NSS_NBUF_PAYLOAD_SIZE);
+	if (unlikely(!nbuf)) {
+		NSS_PKT_STATS_INCREMENT(nss_ctx, &nss_ctx->nss_top->stats_drv[NSS_STATS_DRV_NBUF_ALLOC_FAILS]);
+		nss_warning("%p: msg dropped as command allocation failed", nss_ctx);
+		return NSS_TX_FAILURE;
+	}
+
+	/*
+	 * Copy the message to our skb
+	 */
+	nm = (struct nss_pptp_msg *)skb_put(nbuf, sizeof(struct nss_pptp_msg));
+	memcpy(nm, msg, sizeof(struct nss_pptp_msg));
+
+	status = nss_core_send_buffer(nss_ctx, 0, nbuf, NSS_IF_CMD_QUEUE, H2N_BUFFER_CTRL, 0);
+	if (status != NSS_CORE_STATUS_SUCCESS) {
+		dev_kfree_skb_any(nbuf);
+		nss_warning("%p: Unable to enqueue 'pptp message'\n", nss_ctx);
+		if (status == NSS_CORE_STATUS_FAILURE_QUEUE) {
+			return NSS_TX_FAILURE_QUEUE;
+		}
+		return NSS_TX_FAILURE;
+	}
+
+	nss_hal_send_interrupt(nss_ctx->nmap, nss_ctx->h2n_desc_rings[NSS_IF_CMD_QUEUE].desc_ring.int_bit,
+				NSS_REGS_H2N_INTR_STATUS_DATA_COMMAND_QUEUE);
+
+	NSS_PKT_STATS_INCREMENT(nss_ctx, &nss_ctx->nss_top->stats_drv[NSS_STATS_DRV_TX_CMD_REQ]);
+	return NSS_TX_SUCCESS;
+}
+
+/*
+ * nss_register_pptp_if()
+ */
+struct nss_ctx_instance *nss_register_pptp_if(uint32_t if_num, nss_pptp_callback_t pptp_callback,
+			nss_pptp_msg_callback_t event_callback, struct net_device *netdev, uint32_t features)
+{
+
+	nss_assert(nss_is_dynamic_interface(if_num));
+
+	nss_top_main.subsys_dp_register[if_num].ndev = netdev;
+	nss_top_main.subsys_dp_register[if_num].cb = pptp_callback;
+	nss_top_main.subsys_dp_register[if_num].app_data = NULL;
+	nss_top_main.subsys_dp_register[if_num].features = features;
+
+	nss_top_main.pptp_msg_callback = event_callback;
+
+	nss_core_register_handler(if_num, nss_pptp_handler, NULL);
+
+	return (struct nss_ctx_instance *)&nss_top_main.nss[nss_top_main.pptp_handler_id];
+}
+
+/*
+ * nss_unregister_pptp_if()
+ */
+void nss_unregister_pptp_if(uint32_t if_num)
+{
+	nss_assert(nss_is_dynamic_interface(if_num));
+
+	nss_top_main.subsys_dp_register[if_num].ndev = NULL;
+	nss_top_main.subsys_dp_register[if_num].cb = NULL;
+	nss_top_main.subsys_dp_register[if_num].app_data = NULL;
+	nss_top_main.subsys_dp_register[if_num].features = 0;
+
+	nss_top_main.pptp_msg_callback = NULL;
+
+	nss_core_unregister_handler(if_num);
+}
+
+/*
+ * nss_get_pptp_context()
+ */
+struct nss_ctx_instance *nss_pptp_get_context()
+{
+	return (struct nss_ctx_instance *)&nss_top_main.nss[nss_top_main.pptp_handler_id];
+}
+
+/*
+ * nss_pptp_msg_init()
+ *      Initialize nss_pptp msg.
+ */
+void nss_pptp_msg_init(struct nss_pptp_msg *ncm, uint16_t if_num, uint32_t type,  uint32_t len, void *cb, void *app_data)
+{
+	nss_cmn_msg_init(&ncm->cm, if_num, type, len, cb, app_data);
+}
+
+/* nss_pptp_register_handler()
+ *   debugfs stats msg handler received on static pptp interface
+ */
+void nss_pptp_register_handler(void)
+{
+	nss_info("nss_pptp_register_handler");
+	nss_core_register_handler(NSS_PPTP_INTERFACE, nss_pptp_handler, NULL);
+}
+
+EXPORT_SYMBOL(nss_pptp_get_context);
+EXPORT_SYMBOL(nss_pptp_tx);
+EXPORT_SYMBOL(nss_unregister_pptp_if);
+EXPORT_SYMBOL(nss_pptp_msg_init);
+EXPORT_SYMBOL(nss_register_pptp_if);
diff --git a/nss_stats.c b/nss_stats.c
index 2558502..591294e 100644
--- a/nss_stats.c
+++ b/nss_stats.c
@@ -422,6 +422,17 @@
 };
 
 /*
+ * nss_stats_str_ppt_session_stats
+ *	PPTP statistics strings for nss session stats
+ */
+static int8_t *nss_stats_str_pptp_session_debug_stats[NSS_STATS_PPTP_SESSION_MAX] = {
+	"RX_DROPPED",
+	"TX_DROPPED",
+	"ENCAP_PBUF_ALLOC_FAIL",
+	"DECAP_PBUF_ALLOC_FAIL"
+};
+
+/*
  * nss_stats_ipv4_read()
  *	Read IPV4 stats
  */
@@ -1316,6 +1327,72 @@
 }
 
 /*
+ * nss_stats_pptp_read()
+ *	Read pptp statistics
+ */
+static ssize_t nss_stats_pptp_read(struct file *fp, char __user *ubuf, size_t sz, loff_t *ppos)
+{
+
+	uint32_t max_output_lines = 2 /* header & footer for session stats */
+					+ NSS_MAX_PPTP_DYNAMIC_INTERFACES * (NSS_STATS_PPTP_SESSION_MAX + 2) /*session stats */
+					+ 2;
+	size_t size_al = NSS_STATS_MAX_STR_LENGTH * max_output_lines ;
+	size_t size_wr = 0;
+	ssize_t bytes_read = 0;
+	struct net_device *dev;
+	struct nss_stats_pptp_session_debug pptp_session_stats[NSS_MAX_PPTP_DYNAMIC_INTERFACES];
+	int id, i;
+
+	char *lbuf = kzalloc(size_al, GFP_KERNEL);
+	if (unlikely(lbuf == NULL)) {
+		nss_warning("Could not allocate memory for local statistics buffer");
+		return 0;
+	}
+
+	memset(&pptp_session_stats, 0, sizeof(struct nss_stats_pptp_session_debug) * NSS_MAX_PPTP_DYNAMIC_INTERFACES);
+
+	/*
+	 * Get all stats
+	 */
+	nss_pptp_session_debug_stats_get((void *)&pptp_session_stats);
+
+	/*
+	 * Session stats
+	 */
+	size_wr += scnprintf(lbuf + size_wr, size_al - size_wr, "\npptp session stats start:\n\n");
+	for (id = 0; id < NSS_MAX_PPTP_DYNAMIC_INTERFACES; id++) {
+
+			if (!pptp_session_stats[id].valid) {
+				break;
+			}
+
+			dev = dev_get_by_index(&init_net, pptp_session_stats[id].if_index);
+			if (likely(dev)) {
+
+				size_wr += scnprintf(lbuf + size_wr, size_al - size_wr, "%d. nss interface id=%d, netdevice=%s\n", id,
+						pptp_session_stats[id].if_num, dev->name);
+				dev_put(dev);
+			} else {
+				size_wr += scnprintf(lbuf + size_wr, size_al - size_wr, "%d. nss interface id=%d\n", id,
+						pptp_session_stats[id].if_num);
+			}
+
+			for (i = 0; i < NSS_STATS_PPTP_SESSION_MAX; i++) {
+				size_wr += scnprintf(lbuf + size_wr, size_al - size_wr,
+						     "\t%s = %llu\n", nss_stats_str_pptp_session_debug_stats[i],
+						      pptp_session_stats[id].stats[i]);
+			}
+			size_wr += scnprintf(lbuf + size_wr, size_al - size_wr, "\n");
+	}
+
+	size_wr += scnprintf(lbuf + size_wr, size_al - size_wr, "\npptp session stats end\n");
+	bytes_read = simple_read_from_buffer(ubuf, sz, ppos, lbuf, size_wr);
+
+	kfree(lbuf);
+	return bytes_read;
+}
+
+/*
  * nss_stats_sjack_read()
  *	Read SJACK stats
  */
@@ -2031,6 +2108,11 @@
 NSS_STATS_DECLARE_FILE_OPERATIONS(l2tpv2)
 
 /*
+ * pptp_stats_ops
+ */
+NSS_STATS_DECLARE_FILE_OPERATIONS(pptp)
+
+/*
  * gmac_stats_ops
  */
 NSS_STATS_DECLARE_FILE_OPERATIONS(gmac)
@@ -2282,6 +2364,17 @@
 		nss_warning("Failed to create qca-nss-drv/stats/l2tpv2 file in debugfs");
 		return;
 	}
+
+	/*
+	 *  PPTP Stats
+	 */
+	nss_top_main.pptp_dentry = debugfs_create_file("pptp", 0400,
+						nss_top_main.stats_dentry, &nss_top_main, &nss_stats_pptp_ops);
+	if (unlikely(nss_top_main.pptp_dentry == NULL)) {
+		nss_warning("Failed to create qca-nss-drv/stats/pptp file in debugfs");
+		return;
+	}
+
 	nss_log_init();
 }