diff --git a/app/test/Makefile b/app/test/Makefile
index 3c777bf..77e48c1 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -58,6 +58,10 @@ SRCS-y += test_ring.c
 SRCS-y += test_ring_perf.c
 SRCS-y += test_pmd_perf.c
 
+ifeq ($(CONFIG_RTE_LIBRTE_PKTDEV),y)
+SRCS-y += test_pktdev.c
+endif
+
 ifeq ($(CONFIG_RTE_LIBRTE_TABLE),y)
 SRCS-y += test_table.c
 SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += test_table_pipeline.c
diff --git a/app/test/test_pktdev.c b/app/test/test_pktdev.c
new file mode 100644
index 0000000..e24fd78
--- /dev/null
+++ b/app/test/test_pktdev.c
@@ -0,0 +1,440 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in
+ *       the documentation and/or other materials provided with the
+ *       distribution.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <unistd.h>
+#include <rte_cycles.h>
+#include <rte_errno.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_kni.h>
+#include <rte_byteorder.h>
+#include <rte_atomic.h>
+#include <rte_malloc.h>
+#include <rte_pktdev.h>
+
+#include "packet_burst_generator.h"
+#include "test.h"
+
+/* General test constants */
+#define MAX_PKTDEVS 4
+#define NB_MBUF 8192
+#define SOCKET 0
+#define MAX_PACKET_SZ 2048
+#define MBUF_DATA_SZ (MAX_PACKET_SZ + RTE_PKTMBUF_HEADROOM)
+#define PKT_BURST_SZ 32
+#define MEMPOOL_CACHE_SZ PKT_BURST_SZ
+#define RING_SIZE 128
+
+#define RING_NAME "test-pktdev"
+#define KNI_NAME "kni-pktdev"
+#define MEMPOOL_NAME "pkdev-mempool"
+
+/* Shared mempool */
+struct rte_mempool* mp;
+
+/* We use port_id 0 */
+static uint8_t port_id = 0;
+
+/* Device specific contexts*/
+static struct rte_ring* r = NULL;
+#ifdef RTE_LIBRTE_KNI
+static struct rte_kni* kni = NULL;
+#endif
+
+/* pktdev handles */
+static struct rte_pktdev* ring_dev = NULL;
+#ifdef RTE_LIBRTE_KNI
+static struct rte_pktdev* kni_dev = NULL;
+#endif
+static struct rte_pktdev* eth_dev = NULL;
+
+static int
+setup_mempool(void)
+{
+	mp = rte_mempool_lookup(MEMPOOL_NAME);
+	if (!mp)
+		mp = rte_pktmbuf_pool_create(MEMPOOL_NAME,
+				NB_MBUF,
+				MEMPOOL_CACHE_SZ, 0, MBUF_DATA_SZ,
+				SOCKET);
+
+	if (!mp){
+		printf( "Could not create mempool!\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+setup_ring(void)
+{
+	r = rte_ring_lookup(RING_NAME);
+
+	if (r == NULL)
+		r = rte_ring_create(RING_NAME, RING_SIZE, SOCKET_ID_ANY, 0);
+
+	if (r == NULL) {
+		printf( "ERROR: unable to create rte_ring '" RING_NAME "' required for the pktdev device!\n");
+		return -1;
+	}
+
+	/* Check NULL ring */
+	if (rte_pktdev_from_ring(NULL) != NULL) {
+		printf( "ERROR: invalid behaviour of rte_pktdev_from_ring() for NULL rings!\n");
+		return -1;
+	}
+
+	/* Create the pktdev device */
+	ring_dev = rte_pktdev_from_ring(r);
+
+	if (ring_dev == NULL) {
+		printf( "ERROR: could not create pktdev from rte_ring '" RING_NAME "'!\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+#ifdef RTE_LIBRTE_KNI
+static int
+setup_kni(void)
+{
+	struct rte_kni_conf conf;
+	struct rte_kni_ops ops;
+
+	memset(&conf, 0, sizeof(conf));
+	memset(&ops, 0, sizeof(ops));
+	sprintf(conf.name,"%s", KNI_NAME);
+	conf.mbuf_size = MAX_PACKET_SZ;
+
+	/* Initialize KNI subsystem */
+	rte_kni_init(1);
+
+	/* Allocate KNI interface */
+	kni = rte_kni_get(KNI_NAME);
+
+	if (kni == NULL)
+		kni = rte_kni_alloc(mp, &conf, &ops);
+
+	if (kni == NULL) {
+		printf( "ERROR: could not allocate KNI interface '" KNI_NAME "'!\n");
+		return -1;
+	}
+
+	/* Check NULL CTX */
+	if (rte_pktdev_from_kni(NULL) != NULL) {
+		printf( "ERROR: invalid behaviour of rte_pktdev_from_kni() for NULL KNI context!\n");
+		return -1;
+	}
+
+	kni_dev = rte_pktdev_from_kni(kni);
+
+	if (kni_dev == NULL) {
+		printf( "ERROR: could not create pktdev from KNI interface '" KNI_NAME "'\n");
+		return -1;
+	}
+
+	return 0;
+}
+#endif /* RTE_LIBRTE_KNI */
+
+
+static struct rte_eth_conf port_conf = {
+	.rxmode = {
+		.mq_mode = ETH_MQ_RX_NONE,
+		.max_rx_pkt_len = MAX_PACKET_SZ,
+		.split_hdr_size = 0,
+		.header_split   = 0, /**< Header Split disabled */
+		.hw_ip_checksum = 0, /**< IP checksum offload enabled */
+		.hw_vlan_filter = 0, /**< VLAN filtering disabled */
+		.hw_vlan_strip  = 0, /**< VLAN strip enabled. */
+		.hw_vlan_extend = 0, /**< Extended VLAN disabled. */
+		.jumbo_frame    = 0, /**< Jumbo Frame Support disabled */
+		.hw_strip_crc   = 0, /**< CRC stripped by hardware */
+		.enable_scatter = 0, /**< scatter rx disabled */
+	},
+	.txmode = {
+		.mq_mode = ETH_MQ_TX_NONE,
+	},
+	.lpbk_mode = 1,  /* enable loopback */
+};
+
+static int
+setup_ethdev(void)
+{
+	uint16_t nb_rx_queue = 1, nb_tx_queue = 1;
+	int ret;
+
+	if (rte_eth_dev_count() == 0 ) {
+		printf( "ERROR: pktdev test requires at least one ethdev!\n");
+		return -1;
+	}
+
+	/* Check NULL CTX */
+	if (rte_pktdev_from_ethport(255, 0, 0) != NULL) {
+		printf( "ERROR: invalid behaviour of rte_pktdev_from_ethport() for invalid port id!\n");
+		return -1;
+	}
+
+	/* port configure */
+	ret = rte_eth_dev_configure(port_id, nb_rx_queue, nb_tx_queue,
+								&port_conf);
+	if (ret < 0) {
+		printf( "ERROR: unable to configure port 0 '%s'.\n",
+							rte_strerror(ret));
+		return -1;
+	}
+
+	/* tx queue setup */
+	ret = rte_eth_tx_queue_setup(port_id, 0, PKT_BURST_SZ, 0, NULL);
+	if (ret < 0) {
+		printf( "ERROR: unable to setup TX queue '%s'.\n",
+							rte_strerror(ret));
+		return -1;
+	}
+
+	/* rx queue steup */
+	ret = rte_eth_rx_queue_setup(port_id, 0, PKT_BURST_SZ, 0, NULL, mp);
+	if (ret < 0) {
+		printf( "ERROR: unable to setup RX queue '%s'.\n",
+							rte_strerror(ret));
+		return -1;
+	}
+
+	/* Start device */
+	ret = rte_eth_dev_start(port_id);
+	if (ret < 0) {
+		printf( "ERROR: unable to start eth_dev '%s'.\n",
+							rte_strerror(ret));
+		return -1;
+	}
+
+	/* Enable promiscuous */
+	rte_eth_promiscuous_enable(port_id);
+
+	/* Create eth_dev */
+	eth_dev = rte_pktdev_from_ethport(0, 0, 0);
+	if (eth_dev == NULL) {
+		printf( "ERROR: could not create pktdev from ethdev'\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static void
+stop_ethdev(void)
+{
+	rte_eth_dev_stop(port_id);
+}
+
+/* I/O loop stop flag */
+static bool keep_running = true;
+
+/* Intialize as an ARP pkt */
+static void
+init_pkt(struct rte_mbuf *mbuf)
+{
+	struct ether_hdr *eth;
+
+	/* Set length */
+	rte_pktmbuf_reset(mbuf);
+	rte_ctrlmbuf_len(mbuf) = ETHER_MIN_LEN;
+	eth = (struct ether_hdr*)rte_ctrlmbuf_data(mbuf);
+
+	/* Set mac addresses & ethtype */
+	memset(eth, 0, sizeof(*eth));
+	eth->d_addr.addr_bytes[0] = 0x2;
+	eth->d_addr.addr_bytes[0] = 0x1;
+	eth->ether_type = ETHER_TYPE_ARP;
+}
+
+/*
+ * Injects a pkt through the rte_ring pkt dev, and the packet
+ * has to travel back and forth through the chain (eth_dev in loopback)
+ */
+static int
+io_loop(void* not_used)
+{
+	(void)not_used;
+	unsigned int len;
+	struct rte_mbuf *burst[PKT_BURST_SZ];
+	struct rte_mbuf *mbuf = NULL;
+
+	/* Get an mbuf */
+	rte_mempool_get(mp, (void**)&mbuf);
+	if (mbuf == NULL) {
+		printf("Unable to allocate an mbuf\n");
+		return -1;
+	}
+
+	/* Prepare packet */
+	init_pkt(mbuf);
+
+	/* Test ring dev */
+	printf("Testing ring pktdev...\n");
+	burst[0] = mbuf;
+	len = rte_pkt_tx_burst(ring_dev, burst, 1);
+
+	if (len != 1) {
+		printf("TX through ring pktdev failed (len:%u)\n", len);
+		return -1;
+	}
+	if (ring_dev->tx_count != 1) {
+		printf("TX through ring pktdev stats check failed (len:%u)\n",										len);
+		return -1;
+	}
+
+	len = rte_pkt_rx_burst(ring_dev, burst, PKT_BURST_SZ);
+	if (len != 1) {
+		printf("RX through ring pktdev failed (len:%u), ring count: %u\n",
+							len,
+							rte_ring_count(r));
+		return -1;
+	}
+	if (ring_dev->rx_count != 1) {
+		printf("TX through ring pktdev stats check failed (len:%u)\n",										len);
+		return -1;
+	}
+
+
+
+#ifdef RTE_LIBRTE_KNI
+	/* Test KNI dev. TODO: check stats and RX */
+	printf("Testing KNI pktdev...\n");
+	rte_mempool_get(mp, (void**)&mbuf);
+	if (mbuf == NULL) {
+		printf("Unable to allocate an mbuf (len:%u)\n", len);
+		return -1;
+	}
+
+	init_pkt(mbuf);
+	burst[0] = mbuf;
+
+	len = rte_pkt_tx_burst(kni_dev, burst, 1);
+	if (len != 1) {
+		printf("TX through kni pktdev failed (len:%u)\n", len);
+		return -1;
+	}
+	if (kni_dev->tx_count != 1) {
+		printf("TX through KNI pktdev stats check failed (len:%u)\n",										len);
+		return -1;
+	}
+#endif /* RTE_LIBRTE_KNI */
+
+	/* Test eth dev. TODO: check RX */
+	printf("Testing ethdev pktdev...\n");
+	rte_mempool_get(mp, (void**)&mbuf);
+	if (mbuf == NULL) {
+		printf("Unable to allocate an mbuf (len:%u)\n", len);
+		return -1;
+	}
+
+	init_pkt(mbuf);
+	burst[0] = mbuf;
+
+	len = rte_pkt_tx_burst(eth_dev, burst, 1);
+	if (len != 1) {
+		printf("TX through eth pktdev failed\n");
+		return -1;
+	}
+	if (eth_dev->tx_count != 1) {
+		printf("TX through eth pktdev stats check failed (len:%u)\n",										len);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+test_pktdev(void)
+{
+	unsigned int lcore_id;
+
+	/* Get lcore id */
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		if (lcore_id == rte_get_master_lcore())
+			continue;
+		if ( rte_lcore_is_enabled(lcore_id) == 1)
+			break;
+	}
+
+	if (lcore_id == RTE_MAX_LCORE) {
+		printf("No available lcores to run I/O loop. This test requires at least 2 lcores\n");
+		return -1;
+	}
+
+	printf("Initializing devices...\n");
+
+	if(setup_mempool() < 0)
+		return -1;
+	if(setup_ring() < 0)
+		return -1;
+#ifdef RTE_LIBRTE_KNI
+	if(setup_kni() < 0)
+		return -1;
+#endif /* RTE_LIBRTE_KNI */
+
+	if(setup_ethdev() < 0)
+		return -1;
+
+	printf("Launching I/O core and testing devs...\n");
+	rte_eal_remote_launch(io_loop, NULL, lcore_id);
+
+	/* Wait */
+	sleep(1);
+
+	/* Stop lcore */
+	keep_running = false;
+	rte_eal_wait_lcore(lcore_id);
+
+	printf("Cleaning the house...\n");
+
+	stop_ethdev();
+
+	return 0;
+}
+
+
+static struct test_command pktdev_cmd = {
+	.command = "pktdev_autotest",
+	.callback = test_pktdev,
+};
+
+REGISTER_TEST_COMMAND(pktdev_cmd);
