diff --git a/platform/linux-netmap/include/odp_packet_netmap.h b/platform/linux-netmap/include/odp_packet_netmap.h
index 78b2379..fae434f 100644
--- a/platform/linux-netmap/include/odp_packet_netmap.h
+++ b/platform/linux-netmap/include/odp_packet_netmap.h
@@ -30,12 +30,10 @@ typedef struct {
 	size_t frame_offset;	/**< Frame start offset from start of pkt buf */
 	size_t buf_size;	/**< Size of buffer payload in 'pool' */
 	struct nm_desc *desc;	/**< Netmap meta-data for the device */
-	uint32_t begin;		/**< First ring to poll */
-	uint32_t end;		/**< Last ring to poll */
-	struct netmap_ring *rxring; /**< First rx ring */
-	struct netmap_ring *txring; /**< First tx ring */
 	odp_queue_t tx_access;	/** Used for exclusive access to send packets */
 	uint32_t if_flags;
+	odp_packet_t *pkt_table;/** Used by recv_pkt_netmap */
+	int nb_rx;		/** Used by recv_pkt_netmap */
 	char ifname[IFNAMSIZ];
 } pkt_netmap_t;
 
diff --git a/platform/linux-netmap/odp_packet_netmap.c b/platform/linux-netmap/odp_packet_netmap.c
index 29e7862..37e574b 100644
--- a/platform/linux-netmap/odp_packet_netmap.c
+++ b/platform/linux-netmap/odp_packet_netmap.c
@@ -103,7 +103,9 @@ static int nm_do_ioctl(pkt_netmap_t * const pkt_nm, unsigned long cmd,
 	case SIOCGIFFLAGS:
 		pkt_nm->if_flags = (ifr.ifr_flags << 16) |
 			(0xffff & ifr.ifr_flags);
-		ODP_DBG("flags are 0x%x\n", pkt_nm->if_flags);
+		ODP_DBG("[%04d] flags are 0x%x\n",
+			odp_thread_id(),
+			pkt_nm->if_flags);
 		break;
 	default:
 		break;
@@ -170,20 +172,17 @@ int setup_pkt_netmap(pkt_netmap_t * const pkt_nm, const char *netdev,
 	}
 	odp_spinlock_unlock(&mmap_desc_lock);
 
-	ODP_DBG("thread %d mmap addr %p\n",
+	ODP_DBG("[%04d] mmap addr %p\n",
 		odp_thread_id(),
 		pkt_nm->desc->mem);
 
-	pkt_nm->begin  = 0;
-	pkt_nm->end    = pkt_nm->desc->req.nr_rx_rings;
-	pkt_nm->rxring = NETMAP_RXRING(pkt_nm->desc->nifp, 0);
-	pkt_nm->txring = NETMAP_TXRING(pkt_nm->desc->nifp, 0);
-
 	ret = nm_do_ioctl(pkt_nm, SIOCGIFFLAGS, 0);
 	if (ret)
 		return ret;
 	if ((pkt_nm->if_flags & IFF_UP) == 0) {
-		ODP_DBG("%s is down, bringing up...\n", pkt_nm->ifname);
+		ODP_DBG("[%04d] %s is down, bringing up...\n",
+			odp_thread_id(),
+			pkt_nm->ifname);
 		pkt_nm->if_flags |= IFF_UP;
 	}
 	if (ETH_PROMISC) {
@@ -192,11 +191,13 @@ int setup_pkt_netmap(pkt_netmap_t * const pkt_nm, const char *netdev,
 	}
 	ret = nm_do_ioctl(pkt_nm, SIOCETHTOOL, ETHTOOL_SGSO);
 	if (ret)
-		ODP_DBG("ETHTOOL_SGSO not supported\n");
+		ODP_DBG("[%04d] ETHTOOL_SGSO not supported\n",
+			odp_thread_id());
 
 	ret = nm_do_ioctl(pkt_nm, SIOCETHTOOL, ETHTOOL_STSO);
 	if (ret)
-		ODP_DBG("ETHTOOL_STSO not supported\n");
+		ODP_DBG("[%04d] ETHTOOL_STSO not supported\n",
+			odp_thread_id());
 	/* TODO: This seems to cause the app to not receive frames
 	 * first time it is launched after netmap driver is inserted.
 	 * Should be investigated further.
@@ -206,11 +207,14 @@ int setup_pkt_netmap(pkt_netmap_t * const pkt_nm, const char *netdev,
 	 */
 	ret = nm_do_ioctl(pkt_nm, SIOCETHTOOL, ETHTOOL_STXCSUM);
 	if (ret)
-		ODP_DBG("ETHTOOL_STXCSUM not supported\n");
+		ODP_DBG("[%04d] ETHTOOL_STXCSUM not supported\n",
+			odp_thread_id());
 
-	ODP_DBG("Wait for link to come up\n");
+	ODP_DBG("[%04d] Wait for link to come up\n",
+		odp_thread_id());
 	sleep(WAITLINK_TMO);
-	ODP_DBG("Done\n");
+	ODP_DBG("[%04d] Done\n",
+		odp_thread_id());
 
 	return 0;
 }
@@ -225,198 +229,110 @@ int close_pkt_netmap(pkt_netmap_t * const pkt_nm)
 	return 0;
 }
 
+static void nm_recv_cb(uint8_t *arg, const struct nm_pkthdr *hdr,
+		       const uint8_t *buf)
+{
+	pkt_netmap_t *pkt_nm = (pkt_netmap_t *)arg;
+	odp_packet_t pkt;
+	uint8_t *pkt_buf, *l2_hdr;
+	size_t frame_len;
+
+	frame_len = (size_t)hdr->len;
+
+	if (frame_len > pkt_nm->max_frame_len) {
+		ODP_ERR("[%04d] RX: frame too big %u %lu!\n",
+			odp_thread_id(),
+			(unsigned)frame_len, pkt_nm->max_frame_len);
+		return;
+	}
+
+	if (odp_unlikely(frame_len < ODPH_ETH_LEN_MIN)) {
+		ODP_ERR("[%04d] RX: Frame truncated: %u\n",
+			odp_thread_id(),
+			(unsigned)frame_len);
+		return;
+	}
+
+	pkt = odph_packet_alloc(pkt_nm->pool);
+
+	if (odp_unlikely(pkt == ODP_PACKET_INVALID)) {
+		ODP_DBG("[%04d] Could not allocate packet\n", odp_thread_id());
+		return;
+	}
+	pkt_buf = odp_packet_addr(pkt);
+	l2_hdr = pkt_buf + pkt_nm->frame_offset;
+	/* For now copy the data in the mbuf,
+	   worry about zero-copy later */
+	memcpy(l2_hdr, buf, frame_len);
+
+	/* Initialize, parse and set packet header data */
+	odp_packet_init(pkt);
+	odp_packet_parse(pkt, frame_len, pkt_nm->frame_offset);
+
+	pkt_nm->pkt_table[pkt_nm->nb_rx++] = pkt;
+}
+
 int recv_pkt_netmap(pkt_netmap_t * const pkt_nm, odp_packet_t pkt_table[],
 		    unsigned len)
 {
-	struct netmap_ring *rxring = pkt_nm->rxring;
-	int fd;
-	unsigned nb_rx = 0;
-	uint32_t limit, rx;
-	uint32_t ringid = pkt_nm->begin;
-	odp_packet_t pkt = ODP_PACKET_INVALID;
+	int fd = pkt_nm->desc->fd;
+
 #ifdef NETMAP_BLOCKING_IO
 	struct pollfd fds[1];
 	int ret;
-#endif
 
-	fd = pkt_nm->desc->fd;
-#ifdef NETMAP_BLOCKING_IO
 	fds[0].fd = fd;
 	fds[0].events = POLLIN;
-#endif
 
-	while (nb_rx < len) {
-#ifdef NETMAP_BLOCKING_IO
-		ret = poll(&fds[0], 1, POLL_TMO);
-		if (ret <= 0 || (fds[0].revents & POLLERR))
-			break;
+	ret = poll(&fds[0], 1, POLL_TMO);
+	if (ret <= 0 || (fds[0].revents & POLLERR))
+		return 0;
 #else
-		ioctl(fd, NIOCRXSYNC, NULL);
+	ioctl(fd, NIOCRXSYNC, NULL);
 #endif
 
-		/* Find first ring not empty */
-		while (nm_ring_empty(rxring)) {
-			ringid++;
-
-			/* Return to scheduler if no more data to meet the
-			   requested amount (len) */
-			if (ringid == pkt_nm->end) {
-				ODP_DBG("No more data on the wire\n");
-				break;
-			}
-
-			rxring = NETMAP_RXRING(pkt_nm->desc->nifp, ringid);
-		}
-
-		limit = len - nb_rx;
-		if (nm_ring_space(rxring) < limit)
-			limit = nm_ring_space(rxring);
-
-		ODP_DBG("receiving %d frames out of %u\n", limit, len);
-
-		for (rx = 0; rx < limit; rx++) {
-			struct netmap_slot *rslot;
-			char *p;
-			uint16_t frame_len;
-			uint8_t *pkt_buf;
-			uint8_t *l2_hdr;
-			uint32_t cur;
-
-			if (odp_likely(pkt == ODP_PACKET_INVALID)) {
-				pkt = odph_packet_alloc(pkt_nm->pool);
-				if (odp_unlikely(pkt == ODP_PACKET_INVALID))
-					break;
-			}
-
-			cur = rxring->cur;
-			rslot = &rxring->slot[cur];
-			p = NETMAP_BUF(rxring, rslot->buf_idx);
-			frame_len = rslot->len;
-
-			rxring->head = nm_ring_next(rxring, cur);
-			rxring->cur = rxring->head;
-
-			pkt_buf = odp_packet_addr(pkt);
-			l2_hdr = pkt_buf + pkt_nm->frame_offset;
-
-			if (frame_len > pkt_nm->max_frame_len) {
-				ODP_ERR("RX: frame too big %u %lu!\n",
-					frame_len, pkt_nm->max_frame_len);
-				/* drop the frame, reuse pkt next interation */
-				continue;
-			}
-			if (odp_unlikely(frame_len < ODPH_ETH_LEN_MIN)) {
-				ODP_ERR("RX: Frame truncated: %u\n",
-					(unsigned)frame_len);
-				continue;
-			}
-
-			/* For now copy the data in the mbuf,
-			   worry about zero-copy later */
-			memcpy(l2_hdr, p, frame_len);
-
-			/* Initialize, parse and set packet header data */
-			odp_packet_init(pkt);
-			odp_packet_parse(pkt, frame_len, pkt_nm->frame_offset);
-
-			pkt_table[nb_rx] = pkt;
-			pkt = ODP_PACKET_INVALID;
-			nb_rx++;
-		}
-
-		if (odp_unlikely(pkt == ODP_PACKET_INVALID))
-			break;
-	}
+	pkt_nm->pkt_table = pkt_table;
+	pkt_nm->nb_rx = 0;
+	nm_dispatch(pkt_nm->desc, len, nm_recv_cb, (uint8_t *)pkt_nm);
 
-	if (odp_unlikely(pkt != ODP_PACKET_INVALID))
-		odp_buffer_free((odp_buffer_t) pkt);
+	if (pkt_nm->nb_rx)
+		ODP_DBG("[%04d] <=== rcvd %03u frames from netmap adapter\n",
+			odp_thread_id(), pkt_nm->nb_rx);
 
-	if (nb_rx)
-		ODP_DBG("<=== rcvd %03u frames from netmap adapter\n", nb_rx);
-
-	return nb_rx;
+	return pkt_nm->nb_rx;
 }
 
 int send_pkt_netmap(pkt_netmap_t * const pkt_nm, odp_packet_t pkt_table[],
 		    unsigned len)
 {
-	struct netmap_ring *txring = pkt_nm->txring;
-	int                 fd;
-	unsigned            nb_tx = 0;
-	uint32_t            limit, tx;
-	uint32_t            ringid = pkt_nm->begin;
-	odp_packet_t        pkt;
+	int fd = pkt_nm->desc->fd;
+	unsigned tx, nb_tx;
 
 #ifdef NETMAP_BLOCKING_IO
 	struct pollfd fds[2];
 	int ret;
-#endif
 
-	fd = pkt_nm->desc->fd;
-#ifdef NETMAP_BLOCKING_IO
 	fds[0].fd = fd;
 	fds[0].events = POLLOUT;
-#endif
 
-	while (nb_tx < len) {
-#ifdef NETMAP_BLOCKING_IO
-		ret = poll(&fds[0], 1, POLL_TMO);
-		if (ret <= 0 || (fds[0].revents & POLLERR))
-			break;
+	ret = poll(&fds[0], 1, POLL_TMO);
+	if (ret <= 0 || (fds[0].revents & POLLERR))
+		return 0;
 #else
-		ioctl(fd, NIOCTXSYNC, NULL);
+	ioctl(fd, NIOCTXSYNC, NULL);
 #endif
 
-		/* Find first ring not empty */
-		while (nm_ring_empty(txring)) {
-			ringid++;
-
-			/* Return to scheduler if no more space to meet the
-			   requested amount (len) */
-			if (ringid == pkt_nm->end) {
-				ODP_DBG("No more space in TX rings\n");
-				break;
-			}
-
-			txring = NETMAP_TXRING(pkt_nm->desc->nifp, ringid);
-		}
-
-		limit = len - nb_tx;
-		if (nm_ring_space(txring) < limit)
-			limit = nm_ring_space(txring);
-
-		ODP_DBG("Sending %d packets out of %d to netmap %p %u\n",
-			limit, len, txring, txring->cur);
-
-		for (tx = 0; tx < limit; tx++) {
-			struct netmap_slot *tslot;
-			size_t frame_len;
-			uint32_t cur;
-			uint8_t *frame;
-			void *txbuf;
-
-			cur = txring->cur;
-			tslot = &txring->slot[cur];
-			txbuf = NETMAP_BUF(txring, tslot->buf_idx);
-
-			pkt = pkt_table[nb_tx];
-			frame = odp_packet_l2(pkt);
-			frame_len = odp_packet_get_len(pkt);
-
-			memcpy(txbuf, frame, frame_len);
-			tslot->len = frame_len;
-			txring->head = nm_ring_next(txring, cur);
-			txring->cur = txring->head;
-			nb_tx++;
-		}
+	for (nb_tx = 0; nb_tx < len; nb_tx++) {
+		odp_packet_t pkt = pkt_table[nb_tx];
+		uint8_t *frame = odp_packet_l2(pkt);
+		size_t frame_len = odp_packet_get_len(pkt);
+		if (nm_inject(pkt_nm->desc, frame, frame_len))
+			break;
 	}
 
-#ifndef NETMAP_BLOCKING_IO
-	ioctl(fd, NIOCTXSYNC, NULL);
-#endif
-
 	if (nb_tx)
-		ODP_DBG("===> sent %03u frames to netmap adapter\n", nb_tx);
+		ODP_DBG("[%04d] ===> sent %03u frames to netmap adapter\n",
+			odp_thread_id(), nb_tx);
 
 	for (tx = 0; tx < len; tx++)
 		odph_packet_free(pkt_table[tx]);
