diff --git a/platform/linux-keystone2/Makefile.am b/platform/linux-keystone2/Makefile.am
index f3d8471..38be9b6 100644
--- a/platform/linux-keystone2/Makefile.am
+++ b/platform/linux-keystone2/Makefile.am
@@ -64,6 +64,7 @@ odpplatinclude_HEADERS = \
 		  $(srcdir)/include/odp/plat/event_types.h \
 		  $(srcdir)/include/odp/plat/mcsdk_tune.h \
 		  $(srcdir)/include/odp/plat/osal.h \
+		  $(srcdir)/include/odp/plat/packet_io_types.h \
 		  $(srcdir)/include/odp/plat/packet_types.h \
 		  $(srcdir)/include/odp/plat/pool_types.h \
 		  $(srcdir)/include/odp/plat/queue_types.h \
@@ -75,7 +76,6 @@ odpplatinclude_HEADERS = \
 		  $(linux_generic_srcdir)/include/odp/plat/classification_types.h \
 		  $(linux_generic_srcdir)/include/odp/plat/cpumask_types.h \
 		  $(linux_generic_srcdir)/include/odp/plat/crypto_types.h \
-		  $(linux_generic_srcdir)/include/odp/plat/packet_io_types.h \
 		  $(linux_generic_srcdir)/include/odp/plat/schedule_types.h \
 		  $(linux_generic_srcdir)/include/odp/plat/shared_memory_types.h \
 		  $(linux_generic_srcdir)/include/odp/plat/strong_types.h \
@@ -137,6 +137,7 @@ __LIB__libodp_la_SOURCES = \
 			   odp_buffer.c \
 			   odp_packet.c \
 			   odp_queue.c \
+			   odp_packet_io.c \
 			   mcsdk/mcsdk_init.c \
 			   mcsdk/mcsdk_navig.c \
 			   mcsdk/mcsdk_rmclient.c \
diff --git a/platform/linux-keystone2/include/odp/packet_io.h b/platform/linux-keystone2/include/odp/packet_io.h
index 3faace9..a085781 100644
--- a/platform/linux-keystone2/include/odp/packet_io.h
+++ b/platform/linux-keystone2/include/odp/packet_io.h
@@ -19,112 +19,13 @@
 extern "C" {
 #endif
 
-#include <odp_std_types.h>
-#include <odp_buffer_pool.h>
-#include <odp_packet.h>
-#include <odp_queue.h>
+#include <odp/plat/pool_types.h>
+#include <odp/plat/packet_types.h>
+#include <odp/plat/queue_types.h>
+#include <odp/plat/classification_types.h>
+#include <odp/plat/packet_io_types.h>
 
-/** ODP packet IO handle */
-typedef uint32_t odp_pktio_t;
-
-/** Invalid packet IO handle */
-#define ODP_PKTIO_INVALID ((odp_pktio_t)-1)
-
-/**
- * Open an ODP packet IO instance
- *
- * @param dev    Packet IO device
- * @param pool   Pool to use for packet IO
- *
- * @return ODP packet IO handle or ODP_PKTIO_INVALID on error
- */
-odp_pktio_t odp_pktio_open(const char *dev, odp_buffer_pool_t pool);
-
-/**
- * Close an ODP packet IO instance
- *
- * @param id  ODP packet IO handle
- *
- * @return 0 on success or -1 on error
- */
-int odp_pktio_close(odp_pktio_t id);
-
-/**
- * Receive packets
- *
- * @param id          ODP packet IO handle
- * @param pkt_table[] Storage for received packets (filled by function)
- * @param len         Length of pkt_table[], i.e. max number of pkts to receive
- *
- * @return Number of packets received or -1 on error
- */
-int odp_pktio_recv(odp_pktio_t id, odp_packet_t pkt_table[], unsigned len);
-
-/**
- * Send packets
- *
- * @param id           ODP packet IO handle
- * @param pkt_table[]  Array of packets to send
- * @param len          length of pkt_table[]
- *
- * @return Number of packets sent or -1 on error
- */
-int odp_pktio_send(odp_pktio_t id, odp_packet_t pkt_table[], unsigned len);
-
-/**
- * Set the default input queue to be associated with a pktio handle
- *
- * @param id	ODP packet IO handle
- * @param queue default input queue set
- * @return  0 on success or -1 on error
- */
-int odp_pktio_inq_setdef(odp_pktio_t id, odp_queue_t queue);
-
-/**
- * Get default input queue associated with a pktio handle
- *
- * @param id  ODP packet IO handle
- *
- * @return Default input queue set or ODP_QUEUE_INVALID on error
- */
-odp_queue_t odp_pktio_inq_getdef(odp_pktio_t id);
-
-/**
- * Remove default input queue (if set)
- *
- * @param id  ODP packet IO handle
- *
- * @return 0 on success or -1 on error
- */
-int odp_pktio_inq_remdef(odp_pktio_t id);
-
-/**
- * Query default output queue
- *
- * @param id ODP packet IO handle
- *
- * @return Default out queue or ODP_QUEUE_INVALID on error
- */
-odp_queue_t odp_pktio_outq_getdef(odp_pktio_t id);
-
-/**
- * Store packet input handle into packet
- *
- * @param pkt  ODP packet buffer handle
- * @param id   ODP packet IO handle
- *
- * @return
- */
-void odp_pktio_set_input(odp_packet_t pkt, odp_pktio_t id);
-
-/**
- * Get stored packet input handle from packet
- *
- * @param pkt  ODP packet buffer handle
- *
- * @return Packet IO handle
- */
-odp_pktio_t odp_pktio_get_input(odp_packet_t pkt);
+#include <odp/api/packet_io.h>
 
 #ifdef __cplusplus
 }
diff --git a/platform/linux-keystone2/include/odp/plat/packet_io_types.h b/platform/linux-keystone2/include/odp/plat/packet_io_types.h
new file mode 100644
index 0000000..d038536
--- /dev/null
+++ b/platform/linux-keystone2/include/odp/plat/packet_io_types.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2014, Linaro Limited
+ * Copyright (c) 2014, Texas Instruments Incorporated
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+/**
+ * @file
+ *
+ * ODP Packet IO types
+ */
+
+#ifndef ODP_PLAT_PACKET_IO_TYPES_H_
+#define ODP_PLAT_PACKET_IO_TYPES_H_
+
+#include <odp/std_types.h>
+#include <odp/plat/strong_types.h>
+
+/** @addtogroup odp_packet_io ODP PACKET IO
+ *  Operations on a packet.
+ *  @{
+ */
+
+#define ODP_PKTIO_NAME_LEN  32
+
+typedef odp_handle_t odp_pktio_t;
+
+#define ODP_PKTIO_INVALID ((odp_pktio_t)NULL)
+
+/** Get printable format of odp_pktio_t */
+static inline uint64_t odp_pktio_to_u64(odp_pktio_t hdl)
+{
+	return _odp_pri(hdl);
+}
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/platform/linux-keystone2/include/odp/queue.h b/platform/linux-keystone2/include/odp/queue.h
index 89a703a..36eaf55 100644
--- a/platform/linux-keystone2/include/odp/queue.h
+++ b/platform/linux-keystone2/include/odp/queue.h
@@ -28,6 +28,7 @@ extern "C" {
 #include <odp/ticketlock.h>
 #include <odp/align.h>
 #include <odp/plat/queue_types.h>
+#include <odp/packet_io.h>
 #include <odp/plat/debug.h>
 
 /**
@@ -47,6 +48,8 @@ typedef struct queue_entry_s {
 	odp_ticketlock_t  lock;
 	odp_queue_t       handle;
 
+	odp_pktio_t       pktin;
+	struct pktio_entry_s    *pktout_entry;
 	odp_buffer_t      sched_buf;
 	struct {
 		odp_schedule_prio_t  prio;
diff --git a/platform/linux-keystone2/include/odp_packet_io_internal.h b/platform/linux-keystone2/include/odp_packet_io_internal.h
index 28bbeb8..5b8f2b0 100644
--- a/platform/linux-keystone2/include/odp_packet_io_internal.h
+++ b/platform/linux-keystone2/include/odp_packet_io_internal.h
@@ -15,33 +15,6 @@
 #ifndef ODP_PACKET_IO_INTERNAL_H_
 #define ODP_PACKET_IO_INTERNAL_H_
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <odp_spinlock.h>
-#include <odp_align.h>
-#include <ti/drv/nwal/nwal.h>
-#include <ti/drv/nwal/nwal_util.h>
-
-struct pktio_entry {
-	odp_spinlock_t lock;		 /**< entry spinlock */
-	int taken;			 /**< is entry taken(1) or free(0) */
-	odp_queue_t inq_default;	 /**< default input queue, if set */
-	odp_queue_t outq_default;	 /**< default out queue */
-	odp_buffer_pool_t in_pool;       /**< pool for incoming packets */
-	odp_pktio_t id;                  /**< pktio handle */
-	nwalTxPSCmdInfo_t tx_ps_cmdinfo; /**< saved Command Label */
-	int port;                        /**< netcp port number */
-};
-
-typedef union {
-	struct pktio_entry s;
-	uint8_t pad[ODP_CACHE_LINE_SIZE_ROUNDUP(sizeof(struct pktio_entry))];
-} pktio_entry_t;
-
-#ifdef __cplusplus
-}
-#endif
+#include <odp/plat/packet_io.h>
 
 #endif
diff --git a/platform/linux-keystone2/include/odp_packet_io_queue.h b/platform/linux-keystone2/include/odp_packet_io_queue.h
index 4d3a79d..df7e949 100644
--- a/platform/linux-keystone2/include/odp_packet_io_queue.h
+++ b/platform/linux-keystone2/include/odp_packet_io_queue.h
@@ -22,10 +22,10 @@ extern "C" {
 #include <odp_queue_internal.h>
 #include <odp_buffer_internal.h>
 
-odp_buffer_t pktin_dequeue(queue_entry_t *queue);
-int pktin_deq_multi(queue_entry_t *queue, odp_buffer_t buf[], int num);
-int pktout_enqueue(queue_entry_t *queue, odp_buffer_t buf);
-int pktout_enq_multi(queue_entry_t *queue, odp_buffer_t buf[], int num);
+odp_event_t pktin_dequeue(queue_entry_t *queue);
+int pktin_deq_multi(queue_entry_t *queue, odp_event_t ev[], int num);
+int pktout_enqueue(queue_entry_t *queue, odp_event_t ev);
+int pktout_enq_multi(queue_entry_t *queue, const odp_event_t ev[], int num);
 
 #ifdef __cplusplus
 }
diff --git a/platform/linux-keystone2/include/odp_queue_internal.h b/platform/linux-keystone2/include/odp_queue_internal.h
index 38a59d4..befbcce 100644
--- a/platform/linux-keystone2/include/odp_queue_internal.h
+++ b/platform/linux-keystone2/include/odp_queue_internal.h
@@ -20,6 +20,7 @@ extern "C" {
 #endif
 
 #include <odp/queue.h>
+#include <odp/packet_io.h>
 #include <odp/align.h>
 
 
diff --git a/platform/linux-keystone2/odp_init.c b/platform/linux-keystone2/odp_init.c
index d6ac135..6d78324 100644
--- a/platform/linux-keystone2/odp_init.c
+++ b/platform/linux-keystone2/odp_init.c
@@ -59,12 +59,12 @@ int odp_init_global(odp_init_t *params ODP_UNUSED,
 		odp_pr_err("ODP schedule init failed.\n");
 		return -1;
 	}
-
+#endif
 	if (odp_pktio_init_global()) {
 		odp_pr_err("ODP packet io init failed.\n");
 		return -1;
 	}
-
+#if 0
 	if (odp_crypto_init_global()) {
 		odp_pr_err("ODP crypto init failed.\n");
 		return -1;
diff --git a/platform/linux-keystone2/odp_packet_io.c b/platform/linux-keystone2/odp_packet_io.c
index 4241a3b..65c6b14 100644
--- a/platform/linux-keystone2/odp_packet_io.c
+++ b/platform/linux-keystone2/odp_packet_io.c
@@ -6,236 +6,297 @@
  * SPDX-License-Identifier:     BSD-3-Clause
  */
 
-#include <odp_packet_io.h>
-#include <odp_packet_io_internal.h>
+#include <odp/packet_io.h>
 #include <odp_packet_io_queue.h>
-#include <odp_packet.h>
+#include <odp/packet.h>
 #include <odp_packet_internal.h>
 #include <odp_internal.h>
-#include <odp_spinlock.h>
-#include <odp_shared_memory.h>
-#include <odp_hints.h>
-#include <odp_config.h>
+#include <odp/spinlock.h>
+#include <odp/shared_memory.h>
+#include <odp/hints.h>
+#include <odp/config.h>
 #include <odp_queue_internal.h>
 #include <odp_schedule_internal.h>
-#include <odp_debug.h>
-#include <odp_buffer_pool_internal.h>
-#include <odp_sync.h>
+#include <odp/debug.h>
+#include <odp_pool_internal.h>
+#include <odp/sync.h>
+#include <odp/plat/shared_resource.h>
 
 #include <string.h>
+#include <linux/if_ether.h>
 
-#define DUMMY_PKTIO
+#include <odp_align_internal.h>
+#include <ti/drv/nwal/nwal.h>
+#include <ti/drv/nwal/nwal_util.h>
 
-typedef struct {
-	pktio_entry_t entries[ODP_CONFIG_PKTIO_ENTRIES];
-} pktio_table_t;
-
-static pktio_table_t *pktio_tbl;
+/*
+ * Internal structures and accessor functions
+ */
 
-#define MAX_PORT_INDEX 4
-static int port_index(const char *interface)
+typedef uint8_t pktio_id_t;
+
+typedef struct if_mac {
+#define MAX_MAC_LEN    8
+	uint8_t mac[MAX_MAC_LEN];
+	uint8_t mac_len;
+} _odp_if_mac_t;
+
+typedef struct pktio_entry_s {
+	odp_queue_t inq_default ODP_ALIGNED_CACHE; /**< default input queue */
+	odp_queue_t outq_default;	 /**< default out queue */
+	odp_pool_t in_pool;              /**< pool for incoming packets */
+	nwalTxPSCmdInfo_t tx_ps_cmdinfo; /**< saved Command Label */
+	nwal_Handle mac_handle;          /**< NWAL MAC entry */
+	nwal_enetPort_t port;            /**< netcp port number */
+	odp_shr_head_t entry_head;       /**< odp_shr head */
+	char name[ODP_PKTIO_NAME_LEN];
+	_odp_if_mac_t mac;
+
+	union {
+		uint32_t all;
+		struct {
+			uint32_t promisc_mode:1;
+			uint32_t inq_created:1;
+		};
+	} flags;
+} pktio_entry_t;
+
+static odp_shr_table_t pktio_tbl;
+
+static pktio_entry_t *_odp_pktio_entry(odp_pktio_t pktio)
 {
-	int ret, port;
-
-	ret = sscanf(interface, "eth%d", &port);
-	if (1 != ret)
-		return -1;
-	port++;
-	if (port > MAX_PORT_INDEX)
-		return -1;
-	return port;
+	ODP_ASSERT(pktio != ODP_PKTIO_INVALID, "Wrong pktio handle");
+	ODP_ASSERT(ODP_ALIGNED_CHECK(pktio, ODP_ALIGNOF(pktio_entry_t)),
+		   "Wrong packet IO handle alignment");
+	return (pktio_entry_t *)(void *)pktio;
 }
 
-static pktio_entry_t *get_entry(odp_pktio_t id)
+static odp_pktio_t _odp_pktio_from_entry(pktio_entry_t *entry)
 {
-	if (odp_unlikely(id == ODP_PKTIO_INVALID ||
-			 id > ODP_CONFIG_PKTIO_ENTRIES))
-		return NULL;
-
-	return &pktio_tbl->entries[id];
+	return (odp_pktio_t)entry;
 }
 
-int odp_pktio_init_global(void)
+static pktio_id_t _odp_pktio_id(odp_pktio_t pktio)
 {
-	pktio_entry_t *pktio_entry;
-	int id;
-	odp_shm_t shm;
-
-	shm = odp_shm_reserve("odp_pktio_entries",
-			      sizeof(pktio_table_t),
-			      sizeof(pktio_entry_t), 0);
-
-	pktio_tbl = odp_shm_addr(shm);
+	ODP_ASSERT(pktio != ODP_PKTIO_INVALID, "Wrong pktio handle");
+	return odp_shr_id(_odp_pktio_entry(pktio));
+}
 
-	if (pktio_tbl == NULL)
-		return -1;
 
-	memset(pktio_tbl, 0, sizeof(pktio_table_t));
+static ODP_UNUSED pktio_entry_t *pktio_id_to_entry(pktio_id_t pktio_id)
+{
+	return odp_shr_from_id(pktio_tbl, pktio_id, pktio_entry_t);
+}
 
-	for (id = 1; id <= ODP_CONFIG_PKTIO_ENTRIES; ++id) {
-		pktio_entry = get_entry(id);
+/*
+ * Interface helper functions
+ */
 
-		odp_spinlock_init(&pktio_entry->s.lock);
-	}
-	return 0;
+#define MAX_PORT_INDEX 4
+static _odp_if_mac_t enetport_mac(nwal_enetPort_t id)
+{
+	_odp_if_mac_t mac = {.mac = {0x11, 0x22, 0x33, 0x44, 0x01, 0x00},
+			     .mac_len = ETH_ALEN};
+	mac.mac[mac.mac_len - 1] = id;
+	return mac;
 }
 
-static int is_free(pktio_entry_t *entry)
+static _odp_if_mac_t loopback_mac(uint8_t tag)
 {
-	return (entry->s.taken == 0);
+	_odp_if_mac_t mac = {.mac = {0x11, 0x22, 0x33, 0x44, 0x02, 0x00},
+			     .mac_len = ETH_ALEN};
+	mac.mac[mac.mac_len - 1] = tag;
+	return mac;
 }
 
-static void set_free(pktio_entry_t *entry)
+static bool is_loopback(const char *interface)
 {
-	entry->s.taken = 0;
+	char loopback_dev[] = "loop";
+
+	return strncmp(interface, loopback_dev, sizeof(loopback_dev) - 1) ?
+		false : true;
 }
 
-static void set_taken(pktio_entry_t *entry)
+static nwal_enetPort_t enetport_id(const char *interface)
 {
-	entry->s.taken = 1;
+	int ret, port;
+
+	ret = sscanf(interface, "eth%d", &port);
+	if (1 != ret)
+		return NWAL_ENET_PORT_UNKNOWN;
+	port++;
+	if (port > MAX_PORT_INDEX)
+		return NWAL_ENET_PORT_UNKNOWN;
+	return port;
 }
 
-static void lock_entry(pktio_entry_t *entry)
+int odp_pktio_init_global(void)
 {
-	odp_spinlock_lock(&entry->s.lock);
+	pktio_tbl = odp_shr_table_create("odp_pktio_entries",
+					 pktio_entry_t,
+					 ODP_CONFIG_PKTIO_ENTRIES);
+	if (pktio_tbl == ODP_SHR_TABLE_INVALID)
+		return -1;
+
+	return 0;
 }
 
-static void unlock_entry(pktio_entry_t *entry)
+static pktio_entry_t *lookup_pktio_entry(const char *name)
 {
-	odp_spinlock_unlock(&entry->s.lock);
+	pktio_entry_t *entry;
+
+	odp_shr_table_for_each_allocated_entry(pktio_tbl, entry) {
+		odp_shr_lock(entry);
+		if (strcmp(name, entry->name) == 0) {
+			/* found it */
+			odp_shr_unlock(entry);
+			return entry;
+		}
+		odp_shr_unlock(entry);
+	}
+	return NULL;
 }
 
-static int free_pktio_entry(odp_pktio_t id)
+static pktio_entry_t *alloc_pktio_entry(const char *name)
 {
-	pktio_entry_t *entry = get_entry(id);
+	pktio_entry_t *entry;
 
-	if (entry == NULL)
-		return -1;
+	odp_shr_table_lock(pktio_tbl);
+	/* Device name should be unique */
+	if (lookup_pktio_entry(name)) {
+		odp_shr_table_unlock(pktio_tbl);
+		__odp_errno = EEXIST;
+		return NULL;
+	}
 
-	set_free(entry);
+	entry = odp_shr_alloc_nolock(pktio_tbl, typeof(*entry));
+	if (!entry) {
+		odp_shr_table_unlock(pktio_tbl);
+		return NULL;
+	}
 
-	return 0;
+	strncpy(entry->name, name, ODP_PKTIO_NAME_LEN - 1);
+	entry->name[ODP_PKTIO_NAME_LEN - 1] = 0;
+	odp_shr_table_unlock(pktio_tbl);
+	return entry;
+}
+
+static void free_pktio_entry(pktio_entry_t *entry)
+{
+	odp_shr_free(pktio_tbl, entry);
 }
 
-static nwalTxPktInfo_t tx_pkt_info = {
-		.pPkt = NULL,
-		.txFlag1 = NWAL_TX_FLAG1_META_DATA_VALID,
-		.lpbackPass = 0,
-		.enetPort = 0,
-		.mtuSize = 0,
-		.startOffset = 0,
-		.saOffBytes = 0,
-		.saPayloadLen = 0,
-		.saAhIcvOffBytes = 0,
-		.saAhMacSize = 0,
-		.etherLenOffBytes = 0,
-		.ipOffBytes = 0,
-		.l4OffBytes = 0,
-		.l4HdrLen = 0,
-		.pseudoHdrChecksum = 0,
-		.ploadLen = 0,
-};
-
-odp_pktio_t odp_pktio_open(const char *dev, odp_buffer_pool_t pool)
-{
-	odp_pktio_t id;
-	pktio_entry_t *pktio_entry;
+odp_pktio_t odp_pktio_open(const char *dev, odp_pool_t pool)
+{
+	odp_pktio_t pktio;
+	pktio_entry_t *entry;
 	char name[ODP_QUEUE_NAME_LEN];
 	queue_entry_t *queue_entry;
-	odp_queue_t qid = ODP_QUEUE_INVALID;
+	odp_queue_t queue;
 	nwal_RetValue ret_nwal;
-	int port;
+	nwal_enetPort_t port;
+	bool loopback;
+
+	nwalTxPktInfo_t tx_pkt_info = {
+			.pPkt = NULL,
+			.txFlag1 = NWAL_TX_FLAG1_META_DATA_VALID,
+			.lpbackPass = nwal_FALSE,
+			.enetPort = NWAL_ENET_PORT_UNKNOWN,
+			.mtuSize = 0,
+			.startOffset = 0,
+			.saOffBytes = 0,
+			.saPayloadLen = 0,
+			.saAhIcvOffBytes = 0,
+			.saAhMacSize = 0,
+			.etherLenOffBytes = 0,
+			.ipOffBytes = 0,
+			.l4OffBytes = 0,
+			.l4HdrLen = 0,
+			.pseudoHdrChecksum = 0,
+			.ploadLen = 0,
+	};
 
 	odp_pr_dbg("Allocating HW pktio\n");
 
-	/* Create a default output queue for each pktio resource */
-	port = port_index(dev);
-	if (port < 0) {
-		odp_pr_err("Wrong pktio name: %s\n", dev);
-		return ODP_PKTIO_INVALID;
-	}
-
-	/**
-	 * Until classification API is in place there is no criteria to
-	 * differentiate pktio except a port number. So map port directly
-	 * to pktio entry.
-	 */
-	id = port;
-
-	pktio_entry = get_entry(id);
-	lock_entry(pktio_entry);
-	if (!is_free(pktio_entry)) {
-		/* Entry already initialized */
-		odp_pr_dbg("PktIO %d is already initialized\n", id);
-		goto unlock;
+	loopback = is_loopback(dev);
+	if (loopback) {
+		tx_pkt_info.lpbackPass = nwal_TRUE;
+		tx_pkt_info.enetPort = NWAL_ENET_PORT_UNKNOWN;
+		port = NWAL_ENET_PORT_UNKNOWN;
+	} else {
+		port = enetport_id(dev);
+		if (port == NWAL_ENET_PORT_UNKNOWN) {
+			odp_pr_err("Wrong pktio name: %s\n", dev);
+			return ODP_PKTIO_INVALID;
+		}
 	}
 
-	set_taken(pktio_entry);
-	pktio_entry->s.inq_default = ODP_QUEUE_INVALID;
-	pktio_entry->s.outq_default = ODP_QUEUE_INVALID;
-	pktio_entry->s.port = port;
-
-	snprintf(name, sizeof(name), "%i-pktio_outq_default", (int)id);
-	name[ODP_QUEUE_NAME_LEN-1] = '\0';
-
-	qid = odp_queue_create(name, ODP_QUEUE_TYPE_PKTOUT, NULL);
-	odp_pr_dbg("Created queue %u\n", (uint32_t)qid);
-	if (qid == ODP_QUEUE_INVALID) {
-		free_pktio_entry(id);
-		id = ODP_PKTIO_INVALID;
+	entry = alloc_pktio_entry(dev);
+	if (!entry)
+		return ODP_PKTIO_INVALID;
+	pktio = _odp_pktio_from_entry(entry);
+	entry->inq_default = ODP_QUEUE_INVALID;
+	entry->outq_default = ODP_QUEUE_INVALID;
+	entry->port = port;
+	entry->mac = (loopback) ? loopback_mac(_odp_pktio_id(pktio)) :
+				  enetport_mac(port);
+
+	snprintf(name, sizeof(name), "%s-pktio_outq_default", dev);
+	name[ODP_QUEUE_NAME_LEN - 1] = '\0';
+
+	queue = odp_queue_create(name, ODP_QUEUE_TYPE_PKTOUT, NULL);
+	odp_pr_dbg("Created queue %u\n", (uint32_t)queue);
+	if (queue == ODP_QUEUE_INVALID) {
 		odp_pr_err("Couldn't create queue: %s\n", name);
-		goto unlock;
+		goto free_entry;
 	}
 
 	ret_nwal = nwal_initPSCmdInfo(odp_global->nwal.handle,
 			&tx_pkt_info,
-			&pktio_entry->s.tx_ps_cmdinfo);
-
+			&entry->tx_ps_cmdinfo);
 	if (ret_nwal != nwal_OK) {
 		odp_pr_err("Couldn't create PSCmdInfo\n");
-		goto unlock;
+		goto queue_destroy;
 	}
 
-	pktio_entry->s.in_pool = pool;
-	pktio_entry->s.outq_default = qid;
-	pktio_entry->s.id = id;
+	entry->in_pool = pool;
+	entry->outq_default = queue;
 
-	queue_entry = queue_to_qentry(qid);
-	queue_entry->s.pktout_entry = pktio_entry;
-unlock:
-	unlock_entry(pktio_entry);
-	return id;
+	queue_entry = _odp_queue_to_qentry(queue);
+	queue_entry->pktout_entry = entry;
+	return pktio;
+
+queue_destroy:
+	odp_queue_destroy(queue);
+free_entry:
+	free_pktio_entry(entry);
+	return ODP_PKTIO_INVALID;
 }
 
-int odp_pktio_close(odp_pktio_t id)
+int odp_pktio_close(odp_pktio_t pktio)
 {
-	pktio_entry_t *entry;
+	pktio_entry_t *entry = _odp_pktio_entry(pktio);
+	int ret;
 
-	entry = get_entry(id);
-	if (entry == NULL)
+	/* At this point pktio should not be used anymore. So no locking */
+	ret = odp_pktio_inq_remdef(pktio);
+	if (ret)
 		return -1;
 
-	/* Only one entry per port exists, so no need to delete it */
+	if (entry->outq_default != ODP_QUEUE_INVALID) {
+		if (odp_queue_destroy(entry->outq_default))
+			return -1;
+		entry->outq_default = ODP_QUEUE_INVALID;
+	}
+	/** @todo: close default in queue and remove MAC rules */
+	free_pktio_entry(entry);
 
 	return 0;
 }
 
-void odp_pktio_set_input(odp_packet_t pkt, odp_pktio_t pktio)
-{
-	odp_packet_hdr(pkt)->input = pktio;
-}
-
-odp_pktio_t odp_pktio_get_input(odp_packet_t pkt)
-{
-	return odp_packet_hdr(pkt)->input;
-}
-
-static int pktio_inq_setdef_locked(odp_pktio_t id, odp_queue_t queue)
+static int pktio_update_mac_entry(pktio_entry_t *entry)
 {
 	nwal_RetValue nwal_ret;
-	nwal_Handle handle;
-	pktio_entry_t *pktio_entry = get_entry(id);
-	queue_entry_t *queue_entry = queue_to_qentry(queue);
 	nwalMacParam_t mac_info = {
 			.validParams = NWAL_SET_MAC_VALID_PARAM_IFNUM,
 			.ifNum = 0,
@@ -249,22 +310,37 @@ static int pktio_inq_setdef_locked(odp_pktio_t id, odp_queue_t queue)
 			.routeType = 0,
 	};
 
-	ODP_ASSERT(pktio_entry && queue_entry, "Not valid entries");
-	ODP_ASSERT(queue_entry->s.type == ODP_QUEUE_TYPE_PKTIN,
-		   "Not PKTIN queue");
+	ODP_ASSERT(entry->inq_default != ODP_QUEUE_INVALID,
+		   "In queue is not set");
 
-	pktio_entry->s.inq_default = queue;
-	odp_sync_stores();
-	mac_info.appRxPktQueue = _odp_queue_to_qmss_queue(queue);
-	/** @todo: Specify flow corresponding to the pool */
-	mac_info.appRxPktFlowId = QMSS_PARAM_NOT_SPECIFIED;
-	mac_info.ifNum = pktio_entry->s.port;
+	if (entry->mac_handle != nwal_HANDLE_INVALID) {
+		nwal_ret = nwal_delMacIface(odp_global->nwal.handle,
+				NWAL_TRANSID_SPIN_WAIT,
+				entry->mac_handle);
+		entry->mac_handle = nwal_HANDLE_INVALID;
+		ODP_ASSERT(nwal_ret == nwal_OK,
+			   "nwal_setMacIface returned Error Code: %d\n",
+			   nwal_ret);
+	}
+
+	mac_info.ifNum = entry->port;
+	mac_info.appRxPktQueue = _odp_queue_to_qmss_queue(entry->inq_default);
+	mac_info.appRxPktFlowId = _odp_pool_cppi_flow_id(entry->in_pool);
+	if (mac_info.appRxPktFlowId < 0) {
+		odp_pr_err("couldn't get pool flow id\n");
+		return -1;
+	}
+	if (!entry->flags.promisc_mode) {
+		ODP_ASSERT(entry->mac.mac_len == sizeof(mac_info.macAddr),
+			   "Wrong MAC address length");
+		memcpy(&mac_info.macAddr, entry->mac.mac, entry->mac.mac_len);
+	}
 
 	nwal_ret = nwal_setMacIface(odp_global->nwal.handle,
 			NWAL_TRANSID_SPIN_WAIT,
 			(nwal_AppId) (0x12345678),
 			&mac_info,
-			&handle);
+			&entry->mac_handle);
 	if (nwal_ret != nwal_OK) {
 		odp_pr_err("nwal_setMacIface returned Error Code %d\n",
 			   nwal_ret);
@@ -272,31 +348,48 @@ static int pktio_inq_setdef_locked(odp_pktio_t id, odp_queue_t queue)
 	}
 
 	odp_pr_info("MAC i/f added\n");
+	return 0;
+}
+
+static int pktio_inq_setdef_locked(odp_pktio_t pktio, odp_queue_t queue)
+{
+	pktio_entry_t *pktio_entry = _odp_pktio_entry(pktio);
+	queue_entry_t *queue_entry = _odp_queue_to_qentry(queue);
+	int ret;
+
+	ODP_ASSERT(pktio_entry && queue_entry, "Not valid entries");
+	ODP_ASSERT(queue_entry->type == ODP_QUEUE_TYPE_PKTIN,
+		   "Not PKTIN queue");
+
+	pktio_entry->inq_default = queue;
+	ret = pktio_update_mac_entry(pktio_entry);
+	if (ret)
+		return ret;
 
 	queue_lock(queue_entry);
-	queue_entry->s.pktin = id;
-	queue_entry->s.status = QUEUE_STATUS_SCHED;
+	queue_entry->pktin = pktio;
+	queue_entry->status = QUEUE_STATUS_SCHED;
 	queue_unlock(queue_entry);
 
-	odp_schedule_queue(queue, queue_entry->s.param.sched.prio);
+	odp_schedule_queue(queue, queue_entry->sched.prio);
 
 	return 0;
 }
 
-static int pktio_inq_create_setdef(odp_pktio_t id)
+static int pktio_inq_create_setdef(odp_pktio_t pktio)
 {
 	char name[ODP_QUEUE_NAME_LEN];
 	odp_queue_param_t qparam;
 	odp_queue_t inq_def;
-	pktio_entry_t *pktio_entry = get_entry(id);
+	pktio_entry_t *entry = _odp_pktio_entry(pktio);
 	int ret = 0;
 
-	ODP_ASSERT(pktio_entry, "Not valid entry");
-	lock_entry(pktio_entry);
-	if (pktio_entry->s.inq_default != ODP_QUEUE_INVALID) {
+	ODP_ASSERT(entry, "Not valid entry");
+	odp_shr_lock(entry);
+	if (entry->inq_default != ODP_QUEUE_INVALID) {
 		ret = 0;
 		odp_pr_dbg("default input queue is already set: %u\n",
-			   pktio_entry->s.inq_default);
+			   entry->inq_default);
 		goto unlock;
 	}
 
@@ -304,7 +397,7 @@ static int pktio_inq_create_setdef(odp_pktio_t id)
 	qparam.sched.prio  = ODP_SCHED_PRIO_DEFAULT;
 	qparam.sched.sync  = ODP_SCHED_SYNC_NONE;
 	qparam.sched.group = ODP_SCHED_GROUP_DEFAULT;
-	snprintf(name, sizeof(name), "%i-pktio_inq_default", (int)id);
+	snprintf(name, sizeof(name), "%s-pktio_inq_default", entry->name);
 	name[ODP_QUEUE_NAME_LEN-1] = '\0';
 	inq_def = odp_queue_create(name, ODP_QUEUE_TYPE_PKTIN, &qparam);
 	if (inq_def == ODP_QUEUE_INVALID) {
@@ -312,163 +405,267 @@ static int pktio_inq_create_setdef(odp_pktio_t id)
 		ret = -1;
 		goto unlock;
 	}
+	entry->flags.inq_created = 1;
 
-	if (pktio_inq_setdef_locked(id, inq_def)) {
+	if (pktio_inq_setdef_locked(pktio, inq_def)) {
 		odp_pr_err("default input-Q setup\n");
 		ret = -1;
 		goto unlock;
 	}
 unlock:
-	unlock_entry(pktio_entry);
+	odp_shr_unlock(entry);
 	return ret;
 }
 
-int odp_pktio_recv(odp_pktio_t id, odp_packet_t pkt_table[], unsigned len)
+int odp_pktio_recv(odp_pktio_t pktio, odp_packet_t pkt_table[], int len)
 {
-	pktio_entry_t *pktio_entry = get_entry(id);
-	unsigned pkts = 0;
-	odp_buffer_t buf;
+	pktio_entry_t *entry = _odp_pktio_entry(pktio);
+	int pkts = 0;
+	odp_event_t event;
 
-	ODP_ASSERT(pktio_entry, "Not valid entry");
+	ODP_ASSERT(entry, "Not valid entry");
 
-	if (pktio_entry->s.inq_default == ODP_QUEUE_INVALID) {
+	if (entry->inq_default == ODP_QUEUE_INVALID) {
 		/**
 		 * Create a default input queue.
 		 * @todo: It is a kind of WA for current ODP API usage.
 		 * It should be revised.
 		 */
-		if (pktio_inq_create_setdef(id))
+		if (pktio_inq_create_setdef(pktio))
 			return -1;
 	}
 
 	for (pkts = 0; pkts < len; pkts++) {
-		buf = odp_queue_deq(pktio_entry->s.inq_default);
-		if (!odp_buffer_is_valid(buf))
+		event = odp_queue_deq(entry->inq_default);
+		if (event == ODP_EVENT_INVALID)
 			break;
 
-		pkt_table[pkts] = odp_packet_from_buffer(buf);
+		pkt_table[pkts] = odp_packet_from_event(event);
 	}
 	return pkts;
 }
 
-static inline void pktio_buffer_send(pktio_entry_t *pktio, odp_buffer_t buf)
+static inline void pktio_event_send(pktio_entry_t *pktio, odp_event_t event)
 {
-	nwal_mCmdSetPort(_odp_buf_to_ti_pkt(buf),
-			 &(pktio->s.tx_ps_cmdinfo),
-			 pktio->s.port);
+	Cppi_HostDesc *desc = _odp_ev_to_cppi_desc(event);
+	nwal_mCmdSetPort(Pktlib_getPacketFromDesc(desc),
+			 &(pktio->tx_ps_cmdinfo),
+			 pktio->port);
 
-	Qmss_queuePushDescSize(pktio->s.tx_ps_cmdinfo.txQueue,
-			       _odp_buf_to_cppi_desc(buf),
+	Qmss_queuePushDescSize(pktio->tx_ps_cmdinfo.txQueue,
+			       desc,
 			       NWAL_DESC_SIZE);
 }
 
-int odp_pktio_send(odp_pktio_t id, odp_packet_t pkt_table[], unsigned len)
+int odp_pktio_send(odp_pktio_t pktio, odp_packet_t pkt_table[], int len)
 {
-	pktio_entry_t *pktio_entry = get_entry(id);
-	unsigned pkts;
+	pktio_entry_t *entry = _odp_pktio_entry(pktio);
+	int pkts;
 
-	if (pktio_entry == NULL)
+	if (entry == NULL)
 		return -1;
 
 	for (pkts = 0; pkts < len; pkts++) {
-		pktio_buffer_send(pktio_entry,
-				  odp_packet_to_buffer(pkt_table[pkts]));
+		pktio_event_send(entry,
+				 odp_packet_to_event(pkt_table[pkts]));
 	}
 	return pkts;
 }
 
-int odp_pktio_inq_setdef(odp_pktio_t id, odp_queue_t queue)
+int odp_pktio_inq_setdef(odp_pktio_t pktio, odp_queue_t queue)
 {
-	pktio_entry_t *pktio_entry = get_entry(id);
+	pktio_entry_t *entry = _odp_pktio_entry(pktio);
 	int ret = 0;
 
-	ODP_ASSERT(pktio_entry, "Not valid entry");
+	ODP_ASSERT(entry, "Not valid entry");
 
-	lock_entry(pktio_entry);
-	if (pktio_entry->s.inq_default == ODP_QUEUE_INVALID) {
-		ret = pktio_inq_setdef_locked(id, queue);
+	odp_shr_lock(entry);
+	if (entry->inq_default == ODP_QUEUE_INVALID) {
+		ret = pktio_inq_setdef_locked(pktio, queue);
 	} else {
 		 /* Default queue can be assigned only once */
 		odp_pr_err("pktio %u: default input queue %s is already set\n",
-			   id,
-			   odp_queue_name(pktio_entry->s.inq_default));
+			   pktio,
+			   odp_queue_name(entry->inq_default));
 		ret = -1;
 	}
-	unlock_entry(pktio_entry);
+	odp_shr_unlock(entry);
 	return ret;
 }
 
-int odp_pktio_inq_remdef(odp_pktio_t id)
+int odp_pktio_inq_remdef(odp_pktio_t pktio)
 {
-	return odp_pktio_inq_setdef(id, ODP_QUEUE_INVALID);
+	pktio_entry_t *pktio_entry = _odp_pktio_entry(pktio);
+	queue_entry_t *queue_entry;
+	nwal_RetValue nwal_ret;
+
+	if (pktio_entry->inq_default == ODP_QUEUE_INVALID)
+		return 0;
+
+	odp_shr_lock(pktio_entry);
+
+	nwal_ret = nwal_delMacIface(odp_global->nwal.handle,
+			NWAL_TRANSID_SPIN_WAIT,
+			pktio_entry->mac_handle);
+	pktio_entry->mac_handle = nwal_HANDLE_INVALID;
+	ODP_ASSERT(nwal_ret == nwal_OK,
+		   "nwal_setMacIface returned Error Code: %d\n", nwal_ret);
+
+	if (pktio_entry->flags.inq_created) {
+		/* destroy queue*/
+		if (odp_queue_destroy(pktio_entry->inq_default)) {
+			odp_shr_unlock(pktio_entry);
+			return -1;
+		}
+	} else {
+		/* Detach pktio from a queue */
+		queue_entry = _odp_queue_to_qentry(pktio_entry->inq_default);
+		queue_lock(queue_entry);
+		queue_entry->pktin = ODP_PKTIO_INVALID;
+		queue_unlock(queue_entry);
+	}
+
+	pktio_entry->inq_default = ODP_QUEUE_INVALID;
+	odp_shr_unlock(pktio_entry);
+	return 0;
 }
 
-odp_queue_t odp_pktio_inq_getdef(odp_pktio_t id)
+odp_queue_t odp_pktio_inq_getdef(odp_pktio_t pktio)
 {
-	pktio_entry_t *pktio_entry = get_entry(id);
+	pktio_entry_t *entry = _odp_pktio_entry(pktio);
 
-	if (pktio_entry == NULL)
+	if (entry == NULL)
 		return ODP_QUEUE_INVALID;
 
-	return pktio_entry->s.inq_default;
+	return entry->inq_default;
 }
 
-odp_queue_t odp_pktio_outq_getdef(odp_pktio_t id)
+odp_queue_t odp_pktio_outq_getdef(odp_pktio_t pktio)
 {
-	pktio_entry_t *pktio_entry = get_entry(id);
+	pktio_entry_t *entry = _odp_pktio_entry(pktio);
 
-	if (pktio_entry == NULL)
+	if (entry == NULL)
 		return ODP_QUEUE_INVALID;
 
-	return pktio_entry->s.outq_default;
+	return entry->outq_default;
 }
 
-int pktout_enqueue(queue_entry_t *queue, odp_buffer_t buf)
+int pktout_enqueue(queue_entry_t *qentry, odp_event_t ev)
 {
-	pktio_entry_t *pktio = queue->s.pktout_entry;
+	pktio_entry_t *pktio_entry = qentry->pktout_entry;
 	odp_pr_vdbg("sending packet\n");
-	odp_pr_vdbg_packet(odp_packet_from_buffer(buf));
-	pktio_buffer_send(pktio, buf);
+	odp_pr_vdbg_packet(odp_packet_from_event(ev));
+	pktio_event_send(pktio_entry, ev);
 	return 0;
 }
 
-int pktout_enq_multi(queue_entry_t *queue, odp_buffer_t buf[], int num)
+int pktout_enq_multi(queue_entry_t *qentry, const odp_event_t ev[], int num)
 {
 	int i;
-	pktio_entry_t *pktio = queue->s.pktout_entry;
+	pktio_entry_t *pktio_entry = qentry->pktout_entry;
 	for (i = 0; i < num; i++)
-		pktio_buffer_send(pktio, buf[i]);
-	return 0;
+		pktio_event_send(pktio_entry, ev[i]);
+	return num;
 }
 
-static inline void update_in_packet(odp_buffer_t buf,
+#define SW_PARSING
+
+static inline void update_in_packet(odp_event_t ev,
 				    odp_pktio_t pktin)
 {
-	if (!odp_buffer_is_valid(buf))
+	if (ev == ODP_EVENT_INVALID)
 		return;
 
-	odp_packet_t pkt = odp_packet_from_buffer(buf);
-	struct odp_pkthdr *pkt_hdr = odp_packet_hdr(pkt);
-	size_t len = odp_packet_get_len(pkt);
-	pkt_hdr->input = pktin;
-	odp_packet_parse(pkt, len, 0);
+	odp_packet_t pkt = odp_packet_from_event(ev);
+	struct odp_pkthdr *hdr = _odp_pkt_hdr(pkt);
+	hdr->input = pktin;
+	ODP_DBG("Received packet ---------\n");
+#ifdef SW_PARSING
+	_odp_packet_parse(pkt);
+#else
+	pasahoLongInfo_t *pasa_info =
+			nwal_mGetProtoInfo(_odp_buf_to_ti_pkt(buf));
+	ODP_ASSERT(pasa_info, "Invalid info from NetCP");
+	hdr->l2_offset = 0;
+	hdr->l3_offset = PASAHO_LINFO_READ_L3_OFFSET(pasa_info);
+	hdr->l4_offset = PASAHO_LINFO_READ_L4_OFFSET(pasa_info);
+#endif
+	odp_packet_print(pkt);
 }
 
-odp_buffer_t pktin_dequeue(queue_entry_t *queue)
+odp_event_t pktin_dequeue(queue_entry_t *qentry)
 {
-	odp_buffer_t buf;
-	buf = queue_deq(queue);
+	odp_event_t event;
+	event = queue_deq(qentry);
 
-	update_in_packet(buf, queue->s.pktin);
-	return buf;
+	update_in_packet(event, qentry->pktin);
+	return event;
 }
 
-int pktin_deq_multi(queue_entry_t *queue, odp_buffer_t buf[], int num)
+int pktin_deq_multi(queue_entry_t *qentry, odp_event_t ev[], int num)
 {
 	int i;
-	num = queue_deq_multi(queue, buf, num);
+	num = queue_deq_multi(qentry, ev, num);
 
 	for (i = 0; i < num; i++)
-		update_in_packet(buf[i], queue->s.pktin);
+		update_in_packet(ev[i], qentry->pktin);
 	return num;
 }
+
+ssize_t odp_pktio_mac_addr(odp_pktio_t pktio, void *mac_addr, ssize_t addr_size)
+{
+	pktio_entry_t *entry = _odp_pktio_entry(pktio);
+	ssize_t size;
+
+	/* MAC address is set only once on pktio open. So no locking. */
+	size = entry->mac.mac_len;
+	if (addr_size < size)
+		return -1;
+
+	memcpy(mac_addr, entry->mac.mac, size);
+
+	return size;
+}
+
+odp_pktio_t odp_pktio_lookup(const char *dev)
+{
+	pktio_entry_t *entry = lookup_pktio_entry(dev);
+	if (!entry)
+		return ODP_PKTIO_INVALID;
+
+	return _odp_pktio_from_entry(entry);
+}
+
+int odp_pktio_mtu(odp_pktio_t pktio)
+{
+	(void)pktio;
+	ODP_UNIMPLEMENTED();
+	return -1;
+}
+
+
+int odp_pktio_promisc_mode_set(odp_pktio_t pktio, odp_bool_t enable)
+{
+	pktio_entry_t *entry = _odp_pktio_entry(pktio);
+	int ret = 0;
+	odp_shr_lock(entry);
+
+	if (!enable == !entry->flags.promisc_mode) {
+		odp_shr_unlock(entry);
+		return 0;
+	}
+
+	entry->flags.promisc_mode = enable;
+	/* If default queue is already set, then MAC entry should be updated */
+	if (entry->inq_default != ODP_QUEUE_INVALID)
+		ret = pktio_update_mac_entry(entry);
+
+	odp_shr_unlock(entry);
+	return ret;
+}
+
+int odp_pktio_promisc_mode(odp_pktio_t pktio)
+{
+	pktio_entry_t *entry = _odp_pktio_entry(pktio);
+	return entry->flags.promisc_mode;
+}
diff --git a/platform/linux-keystone2/odp_queue.c b/platform/linux-keystone2/odp_queue.c
index f59a884..2ecc973 100644
--- a/platform/linux-keystone2/odp_queue.c
+++ b/platform/linux-keystone2/odp_queue.c
@@ -65,7 +65,6 @@ static int queue_init(queue_entry_t *qentry, const char *name,
 	}
 
 	switch (type) {
-#if 0
 	case ODP_QUEUE_TYPE_PKTIN:
 		qentry->enqueue = NULL;
 		qentry->dequeue = pktin_dequeue;
@@ -78,7 +77,6 @@ static int queue_init(queue_entry_t *qentry, const char *name,
 		qentry->enqueue_multi = pktout_enq_multi;
 		qentry->dequeue_multi = NULL;
 		break;
-#endif
 	default:
 		qentry->enqueue = queue_enq;
 		qentry->dequeue = queue_deq;
