Signed-off-by: Ciprian Barbu <[email protected]>
---
 configure.ac                                       |   3 +
 platform/linux-netmap/Makefile.am                  |  85 ++++
 platform/linux-netmap/Makefile.inc                 |   0
 platform/linux-netmap/README                       |  90 ++++
 .../linux-netmap/include/api/odp_pktio_netmap.h    |  39 ++
 .../linux-netmap/include/api/odp_pktio_types.h     |  51 ++
 .../linux-netmap/include/odp_packet_io_internal.h  |  45 ++
 platform/linux-netmap/odp_packet_io.c              | 523 +++++++++++++++++++++
 platform/linux-netmap/odp_packet_netmap.c          | 453 ++++++++++++++++++
 platform/linux-netmap/odp_packet_netmap.h          |  67 +++
 10 files changed, 1356 insertions(+)
 create mode 100644 platform/linux-netmap/Makefile.am
 create mode 100644 platform/linux-netmap/Makefile.inc
 create mode 100644 platform/linux-netmap/README
 create mode 100644 platform/linux-netmap/include/api/odp_pktio_netmap.h
 create mode 100644 platform/linux-netmap/include/api/odp_pktio_types.h
 create mode 100644 platform/linux-netmap/include/odp_packet_io_internal.h
 create mode 100644 platform/linux-netmap/odp_packet_io.c
 create mode 100644 platform/linux-netmap/odp_packet_netmap.c
 create mode 100644 platform/linux-netmap/odp_packet_netmap.h

diff --git a/configure.ac b/configure.ac
index 296c9c5..220f89a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -51,6 +51,8 @@ AC_ARG_WITH([platform],
 
 AC_SUBST([with_platform])
 
+AM_CONDITIONAL([ODP_NETMAP_ENABLED], [test x$with_platform = xlinux-netmap ])
+
 AC_ARG_WITH([sdk-install-path],
 AC_HELP_STRING([--with-sdk-install-path=DIR Path to external libs and headers],
                [(or in the default path if not specified).]),
@@ -150,6 +152,7 @@ AC_CHECK_FUNCS([bzero clock_gettime gethostbyname 
getpagesize gettimeofday memse
 AC_CONFIG_FILES([Makefile
                 platform/Makefile
                 platform/linux-generic/Makefile
+                platform/linux-netmap/Makefile
                 example/Makefile
                 example/generator/Makefile
                 example/ipsec/Makefile
diff --git a/platform/linux-netmap/Makefile.am 
b/platform/linux-netmap/Makefile.am
new file mode 100644
index 0000000..b783521
--- /dev/null
+++ b/platform/linux-netmap/Makefile.am
@@ -0,0 +1,85 @@
+include $(top_srcdir)/Makefile.inc
+include $(top_srcdir)/platform/Makefile.inc
+
+PLAT_CFLAGS =
+if SDK_INSTALL_PATH_
+PLAT_CFLAGS += -I$(SDK_INSTALL_PATH)/sys
+endif
+
+AM_CFLAGS +=  $(PLAT_CFLAGS)
+AM_CFLAGS +=  -I$(srcdir)/include
+AM_CFLAGS +=  -I$(srcdir)/include/api
+AM_CFLAGS +=  -I$(top_srcdir)/platform/linux-generic/include
+AM_CFLAGS +=  -I$(top_srcdir)/platform/linux-generic/include/api
+AM_CFLAGS +=  -I$(top_srcdir)/helper/include
+
+include_HEADERS = \
+                 $(top_srcdir)/platform/linux-generic/include/api/odp.h \
+                 $(top_srcdir)/platform/linux-generic/include/api/odp_align.h \
+                 $(top_srcdir)/platform/linux-generic/include/api/odp_atomic.h 
\
+                 
$(top_srcdir)/platform/linux-generic/include/api/odp_barrier.h \
+                 $(top_srcdir)/platform/linux-generic/include/api/odp_buffer.h 
\
+                 
$(top_srcdir)/platform/linux-generic/include/api/odp_buffer_pool.h \
+                 
$(top_srcdir)/platform/linux-generic/include/api/odp_byteorder.h \
+                 
$(top_srcdir)/platform/linux-generic/include/api/odp_compiler.h \
+                 $(top_srcdir)/platform/linux-generic/include/api/odp_config.h 
\
+                 
$(top_srcdir)/platform/linux-generic/include/api/odp_coremask.h \
+                 $(top_srcdir)/platform/linux-generic/include/api/odp_crypto.h 
\
+                 $(top_srcdir)/platform/linux-generic/include/api/odp_debug.h \
+                 $(top_srcdir)/platform/linux-generic/include/api/odp_hints.h \
+                 $(top_srcdir)/platform/linux-generic/include/api/odp_init.h \
+                 
$(top_srcdir)/platform/linux-generic/include/api/odp_packet_flags.h \
+                 $(top_srcdir)/platform/linux-generic/include/api/odp_packet.h 
\
+                 
$(top_srcdir)/platform/linux-generic/include/api/odp_packet_io.h \
+                 $(top_srcdir)/platform/linux-generic/include/api/odp_queue.h \
+                 $(top_srcdir)/platform/linux-generic/include/api/odp_rwlock.h 
\
+                 
$(top_srcdir)/platform/linux-generic/include/api/odp_schedule.h \
+                 
$(top_srcdir)/platform/linux-generic/include/api/odp_shared_memory.h \
+                 
$(top_srcdir)/platform/linux-generic/include/api/odp_spinlock.h \
+                 
$(top_srcdir)/platform/linux-generic/include/api/odp_std_types.h \
+                 $(top_srcdir)/platform/linux-generic/include/api/odp_sync.h \
+                 
$(top_srcdir)/platform/linux-generic/include/api/odp_system_info.h \
+                 $(top_srcdir)/platform/linux-generic/include/api/odp_thread.h 
\
+                 
$(top_srcdir)/platform/linux-generic/include/api/odp_ticketlock.h \
+                 $(top_srcdir)/platform/linux-generic/include/api/odp_time.h \
+                 $(top_srcdir)/platform/linux-generic/include/api/odp_timer.h \
+                 
$(top_srcdir)/platform/linux-generic/include/api/odp_version.h \
+                 $(srcdir)/include/api/odp_pktio_types.h \
+                 $(srcdir)/include/api/odp_pktio_netmap.h \
+                 
$(top_srcdir)/platform/linux-generic/include/api/odp_pktio_socket.h
+
+subdirheadersdir = $(includedir)/helper
+subdirheaders_HEADERS = \
+                       $(top_srcdir)/helper/include/odph_chksum.h \
+                       $(top_srcdir)/helper/include/odph_eth.h \
+                       $(top_srcdir)/helper/include/odph_ip.h \
+                       $(top_srcdir)/helper/include/odph_linux.h \
+                       $(top_srcdir)/helper/include/odph_packet.h \
+                       $(top_srcdir)/helper/include/odph_ring.h \
+                       $(top_srcdir)/helper/include/odph_udp.h
+
+__LIB__libodp_la_SOURCES = \
+                          ../linux-generic/odp_barrier.c \
+                          ../linux-generic/odp_buffer.c \
+                          ../linux-generic/odp_buffer_pool.c \
+                          ../linux-generic/odp_coremask.c \
+                          ../linux-generic/odp_crypto.c \
+                          ../linux-generic/odp_init.c \
+                          ../linux-generic/odp_linux.c \
+                          ../linux-generic/odp_packet.c \
+                          ../linux-generic/odp_packet_flags.c \
+                          odp_packet_io.c \
+                          ../linux-generic/odp_packet_socket.c \
+                          ../linux-generic/odp_queue.c \
+                          ../linux-generic/odp_ring.c \
+                          ../linux-generic/odp_rwlock.c \
+                          ../linux-generic/odp_schedule.c \
+                          ../linux-generic/odp_shared_memory.c \
+                          ../linux-generic/odp_spinlock.c \
+                          ../linux-generic/odp_system_info.c \
+                          ../linux-generic/odp_thread.c \
+                          ../linux-generic/odp_ticketlock.c \
+                          ../linux-generic/odp_time.c \
+                          ../linux-generic/odp_timer.c \
+                          odp_packet_netmap.c
+
diff --git a/platform/linux-netmap/Makefile.inc 
b/platform/linux-netmap/Makefile.inc
new file mode 100644
index 0000000..e69de29
diff --git a/platform/linux-netmap/README b/platform/linux-netmap/README
new file mode 100644
index 0000000..6021445
--- /dev/null
+++ b/platform/linux-netmap/README
@@ -0,0 +1,90 @@
+Copyright (c) 2014, Linaro Limited
+All rights reserved.
+
+SPDX-License-Identifier:        BSD-3-Clause
+
+1. Intro
+==================================
+
+OpenDataPlane implementation using netmap accelerated packet I/O.
+The current implementation does not yet take advantage of the full
+performance of netmap I/O, the data is copied between netmap slots and
+ODP buffers.
+
+The implementation is based almost entirely on the linux-generic platform
+implementation, only the packet I/O differs to work with netmap enabled
+interfaces.
+
+Also the linux-netmap implementation allows hybrid pktio instances, which
+means that the applications can decide to use either sockets or netmap I/O
+at runtime.
+
+2. Build
+==================================
+
+2.1 Building netmap
+--------------------
+
+Netmap is currently hosted on https://code.google.com/p/netmap/ but
+ODP only works at this point with netmap API version 10 so you will need
+a specific revision of netmap.
+
+    git clone https://code.google.com/p/netmap/
+    cd netmap
+    git reset --hard 1f462ef
+
+Netmap consists of a core kernel module (netmap_lin.ko) and optional modified
+device drivers.
+
+Netmap builds as an out-of-tree kernel module, you need matching kernel sources
+to compile it. General build instructions can be found in the packet README:
+http://code.google.com/p/netmap/source/browse/README
+
+2.1.1 Building netmap on Ubuntu with stock kernel
+
+If you are running Ubuntu/Debian with the stock kernel and you want to compile
+both netmap_lin.ko and modified drivers, these steps will guide you through it.
+
+You will need the linux-headers and linux-source packages for this.
+
+    sudo apt-get install linux-headers
+    sudo apt-get install linux-source
+
+The source archive will be placed in /usr/src/linux-source-<kernel-version>
+You will need to locate it and extract it to a convenient place.
+
+Now compile netmap:
+
+    cd LINUX
+    make SRC=<path_to_kernel_source>
+
+2.1.2 Building netmap for kernel built from sources
+
+    cd LINUX
+    make KSRC=<path_to_kernel_source>
+
+2.2 Building ODP
+----------------
+
+    ./bootstrap
+    ./configure --with-platform=linux-netmap --with-sdk-install-path=<netmap>
+    make
+
+3. Running the example application
+==================================
+
+The example application for netmap-linux is odp_pktio_netmap. The main purpose
+of the example application is to show how to implement a simple packet loopback
+application using ODP. The example also implements a bridge between the Linux
+IP stack and the physical interfaces since netmap disrupts the communication
+between the two. The bridging is achieved by passing packets between the
+"software" ring attached to the host stack and the physical rings of the NIC.
+More information on netmap architecture and software rings can be found in the
+"netmap: a novel framework for fast packet I/O" paper by Luigi Rizzo on his
+research page: http://info.iet.unipi.it/~luigi/research.html
+
+    sudo ./odp_pktio_netmap -i eth0 -m 1
+
+From another machine you can simply run ping and observe the duplicate ICMP 
+replies. Also the host running the example application should still have 
network
+connectivity due to the bridging performed as explained above.
diff --git a/platform/linux-netmap/include/api/odp_pktio_netmap.h 
b/platform/linux-netmap/include/api/odp_pktio_netmap.h
new file mode 100644
index 0000000..294a13d
--- /dev/null
+++ b/platform/linux-netmap/include/api/odp_pktio_netmap.h
@@ -0,0 +1,39 @@
+
+/* Copyright (c) 2013, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+/**
+ * @file
+ *
+ * ODP packet input/output netmap
+ */
+
+#ifndef ODP_PKTIO_NETMAP_H
+#define ODP_PKTIO_NETMAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_pktio_types.h>
+
+#define ODP_NETMAP_MODE_HW     0 /**< Netmap mode in hardware */
+#define ODP_NETMAP_MODE_SW     1 /**< Netmap mode in software */
+
+/**
+ * Netmap parameters
+ */
+typedef struct {
+       odp_pktio_type_t type;  /**< Packet IO type */
+       int netmap_mode;        /**< Netmap Mode */
+       uint16_t ringid;        /**< Ring identifiers */
+} netmap_params_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-netmap/include/api/odp_pktio_types.h 
b/platform/linux-netmap/include/api/odp_pktio_types.h
new file mode 100644
index 0000000..b7c6341
--- /dev/null
+++ b/platform/linux-netmap/include/api/odp_pktio_types.h
@@ -0,0 +1,51 @@
+/* Copyright (c) 2013, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+/**
+ * @file
+ *
+ * ODP packet input/output types
+ */
+
+#ifndef ODP_PKTIO_TYPES_H
+#define ODP_PKTIO_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* We should ensure that future enum values will never overlap, otherwise
+ * applications that want netmap suport might get in trouble if the odp lib
+ * was not built with netmap support and there are more types define below
+ */
+
+/**
+ * Packet IO types
+ */
+typedef enum {
+       ODP_PKTIO_TYPE_SOCKET_BASIC = 0x1,
+       ODP_PKTIO_TYPE_SOCKET_MMSG,
+       ODP_PKTIO_TYPE_SOCKET_MMAP,
+       ODP_PKTIO_TYPE_NETMAP,
+} odp_pktio_type_t;
+
+#include <odp_pktio_socket.h>
+#include <odp_pktio_netmap.h>
+
+/**
+ * Packet IO parameters
+ */
+typedef union odp_pktio_params_t {
+       odp_pktio_type_t type;          /**< Packet IO type */
+       socket_params_t sock_params;    /**< Socket parameter */
+       netmap_params_t nm_params;
+} odp_pktio_params_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-netmap/include/odp_packet_io_internal.h 
b/platform/linux-netmap/include/odp_packet_io_internal.h
new file mode 100644
index 0000000..a11ca31
--- /dev/null
+++ b/platform/linux-netmap/include/odp_packet_io_internal.h
@@ -0,0 +1,45 @@
+/* Copyright (c) 2013, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+
+/**
+ * @file
+ *
+ * ODP packet IO - implementation internal
+ */
+
+#ifndef ODP_PACKET_IO_INTERNAL_H_
+#define ODP_PACKET_IO_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_spinlock.h>
+#include <odp_packet_socket.h>
+#include <odp_packet_netmap.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_pktio_params_t params;      /**< pktio parameters */
+       pkt_sock_t pkt_sock;            /**< using socket API for IO */
+       pkt_sock_mmap_t pkt_sock_mmap;  /**< using socket mmap API for IO */
+       pkt_netmap_t pkt_nm;            /**< using netmap API for IO */
+};
+
+typedef union {
+       struct pktio_entry s;
+       uint8_t pad[ODP_CACHE_LINE_SIZE_ROUNDUP(sizeof(struct pktio_entry))];
+} pktio_entry_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-netmap/odp_packet_io.c 
b/platform/linux-netmap/odp_packet_io.c
new file mode 100644
index 0000000..8d6e7d6
--- /dev/null
+++ b/platform/linux-netmap/odp_packet_io.c
@@ -0,0 +1,523 @@
+/* Copyright (c) 2013, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#include <odp_packet_io.h>
+#include <odp_packet_io_internal.h>
+#include <odp_packet_io_queue.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_packet_socket.h>
+#include <odp_packet_netmap.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_pktio_socket.h>
+
+#include <string.h>
+
+typedef struct {
+       pktio_entry_t entries[ODP_CONFIG_PKTIO_ENTRIES];
+} pktio_table_t;
+
+static pktio_table_t *pktio_tbl;
+
+
+static pktio_entry_t *get_entry(odp_pktio_t id)
+{
+       if (odp_unlikely(id == ODP_PKTIO_INVALID ||
+                        id > ODP_CONFIG_PKTIO_ENTRIES))
+               return NULL;
+
+       return &pktio_tbl->entries[id - 1];
+}
+
+int odp_pktio_init_global(void)
+{
+       char name[ODP_QUEUE_NAME_LEN];
+       pktio_entry_t *pktio_entry;
+       queue_entry_t *queue_entry;
+       odp_queue_t qid;
+       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);
+
+       if (pktio_tbl == NULL)
+               return -1;
+
+       memset(pktio_tbl, 0, sizeof(pktio_table_t));
+
+       for (id = 1; id <= ODP_CONFIG_PKTIO_ENTRIES; ++id) {
+               pktio_entry = get_entry(id);
+
+               odp_spinlock_init(&pktio_entry->s.lock);
+
+               /* Create a default output queue for each pktio resource */
+               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);
+               if (qid == ODP_QUEUE_INVALID)
+                       return -1;
+               pktio_entry->s.outq_default = qid;
+
+               queue_entry = queue_to_qentry(qid);
+               queue_entry->s.pktout = id;
+       }
+
+       return 0;
+}
+
+int odp_pktio_init_local(void)
+{
+       return 0;
+}
+
+static int is_free(pktio_entry_t *entry)
+{
+       return (entry->s.taken == 0);
+}
+
+static void set_free(pktio_entry_t *entry)
+{
+       entry->s.taken = 0;
+}
+
+static void set_taken(pktio_entry_t *entry)
+{
+       entry->s.taken = 1;
+}
+
+static void lock_entry(pktio_entry_t *entry)
+{
+       odp_spinlock_lock(&entry->s.lock);
+}
+
+static void unlock_entry(pktio_entry_t *entry)
+{
+       odp_spinlock_unlock(&entry->s.lock);
+}
+
+static void init_pktio_entry(pktio_entry_t *entry, odp_pktio_params_t *params)
+{
+       set_taken(entry);
+       entry->s.inq_default = ODP_QUEUE_INVALID;
+       switch (params->type) {
+       case ODP_PKTIO_TYPE_SOCKET_BASIC:
+       case ODP_PKTIO_TYPE_SOCKET_MMSG:
+       case ODP_PKTIO_TYPE_SOCKET_MMAP:
+               memset(&entry->s.pkt_sock, 0, sizeof(entry->s.pkt_sock));
+               memset(&entry->s.pkt_sock_mmap, 0,
+                     sizeof(entry->s.pkt_sock_mmap));
+               break;
+       case ODP_PKTIO_TYPE_NETMAP:
+               memset(&entry->s.pkt_nm, 0, sizeof(entry->s.pkt_nm));
+               break;
+       default:
+               ODP_ERR("Packet I/O type not supported. Please recompile\n");
+               break;
+       }
+       /* Save pktio parameters, type is the most useful */
+       memcpy(&entry->s.params, params, sizeof(*params));
+}
+
+static odp_pktio_t alloc_lock_pktio_entry(odp_pktio_params_t *params)
+{
+       odp_pktio_t id;
+       pktio_entry_t *entry;
+       int i;
+
+       for (i = 0; i < ODP_CONFIG_PKTIO_ENTRIES; ++i) {
+               entry = &pktio_tbl->entries[i];
+               if (is_free(entry)) {
+                       lock_entry(entry);
+                       if (is_free(entry)) {
+                               init_pktio_entry(entry, params);
+                               id = i + 1;
+                               return id; /* return with entry locked! */
+                       }
+                       unlock_entry(entry);
+               }
+       }
+
+       return ODP_PKTIO_INVALID;
+}
+
+static int free_pktio_entry(odp_pktio_t id)
+{
+       pktio_entry_t *entry = get_entry(id);
+
+       if (entry == NULL)
+               return -1;
+
+       set_free(entry);
+
+       return 0;
+}
+
+odp_pktio_t odp_pktio_open(const char *dev, odp_buffer_pool_t pool,
+                          odp_pktio_params_t *params)
+{
+       odp_pktio_t id;
+       pktio_entry_t *pktio_entry;
+       int res;
+
+       if (params == NULL) {
+               ODP_ERR("Invalid pktio params\n");
+               return ODP_PKTIO_INVALID;
+       }
+
+       switch (params->type) {
+       case ODP_PKTIO_TYPE_SOCKET_BASIC:
+       case ODP_PKTIO_TYPE_SOCKET_MMSG:
+       case ODP_PKTIO_TYPE_SOCKET_MMAP:
+               ODP_DBG("Allocating socket pktio\n");
+               break;
+       case ODP_PKTIO_TYPE_NETMAP:
+               ODP_DBG("Allocating netmap pktio\n");
+               break;
+       default:
+               ODP_ERR("Invalid pktio type: %02x\n", params->type);
+               return ODP_PKTIO_INVALID;
+       }
+
+       id = alloc_lock_pktio_entry(params);
+       if (id == ODP_PKTIO_INVALID) {
+               ODP_ERR("No resources available.\n");
+               return ODP_PKTIO_INVALID;
+       }
+       /* if successful, alloc_pktio_entry() returns with the entry locked */
+
+       pktio_entry = get_entry(id);
+
+       switch (params->type) {
+       case ODP_PKTIO_TYPE_SOCKET_BASIC:
+       case ODP_PKTIO_TYPE_SOCKET_MMSG:
+               res = setup_pkt_sock(&pktio_entry->s.pkt_sock, dev, pool);
+               if (res == -1) {
+                       close_pkt_sock(&pktio_entry->s.pkt_sock);
+                       free_pktio_entry(id);
+                       id = ODP_PKTIO_INVALID;
+               }
+               break;
+       case ODP_PKTIO_TYPE_SOCKET_MMAP:
+               res = setup_pkt_sock_mmap(&pktio_entry->s.pkt_sock_mmap, dev,
+                               pool, params->sock_params.fanout);
+               if (res == -1) {
+                       close_pkt_sock_mmap(&pktio_entry->s.pkt_sock_mmap);
+                       free_pktio_entry(id);
+                       id = ODP_PKTIO_INVALID;
+               }
+               break;
+       case ODP_PKTIO_TYPE_NETMAP:
+
+               res = setup_pkt_netmap(&pktio_entry->s.pkt_nm, dev,
+                               pool, &params->nm_params);
+               if (res == -1) {
+                       close_pkt_netmap(&pktio_entry->s.pkt_nm);
+                       free_pktio_entry(id);
+                       id = ODP_PKTIO_INVALID;
+               }
+               break;
+       default:
+               free_pktio_entry(id);
+               id = ODP_PKTIO_INVALID;
+               ODP_ERR("This type of I/O is not supported. Please 
recompile.\n");
+               break;
+       }
+
+       unlock_entry(pktio_entry);
+       return id;
+}
+
+int odp_pktio_close(odp_pktio_t id)
+{
+       pktio_entry_t *entry;
+       int res = -1;
+
+       entry = get_entry(id);
+       if (entry == NULL)
+               return -1;
+
+       lock_entry(entry);
+       if (!is_free(entry)) {
+               switch (entry->s.params.type) {
+               case ODP_PKTIO_TYPE_SOCKET_BASIC:
+               case ODP_PKTIO_TYPE_SOCKET_MMSG:
+                       res  = close_pkt_sock(&entry->s.pkt_sock);
+                       break;
+               case ODP_PKTIO_TYPE_SOCKET_MMAP:
+                       res  = close_pkt_sock_mmap(&entry->s.pkt_sock_mmap);
+                       break;
+               case ODP_PKTIO_TYPE_NETMAP:
+                       res  = close_pkt_netmap(&entry->s.pkt_nm);
+                       break;
+               default:
+                       break;
+               res |= free_pktio_entry(id);
+               }
+       }
+       unlock_entry(entry);
+
+       if (res != 0)
+               return -1;
+
+       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;
+}
+
+int odp_pktio_recv(odp_pktio_t id, odp_packet_t pkt_table[], unsigned len)
+{
+       pktio_entry_t *pktio_entry = get_entry(id);
+       int pkts;
+       int i;
+
+       if (pktio_entry == NULL)
+               return -1;
+
+       lock_entry(pktio_entry);
+       switch (pktio_entry->s.params.type) {
+       case ODP_PKTIO_TYPE_SOCKET_BASIC:
+               pkts = recv_pkt_sock_basic(&pktio_entry->s.pkt_sock,
+                               pkt_table, len);
+               break;
+       case ODP_PKTIO_TYPE_SOCKET_MMSG:
+               pkts = recv_pkt_sock_mmsg(&pktio_entry->s.pkt_sock,
+                               pkt_table, len);
+               break;
+       case ODP_PKTIO_TYPE_SOCKET_MMAP:
+               pkts = recv_pkt_sock_mmap(&pktio_entry->s.pkt_sock_mmap,
+                               pkt_table, len);
+               break;
+       case ODP_PKTIO_TYPE_NETMAP:
+               pkts = recv_pkt_netmap(&pktio_entry->s.pkt_nm, pkt_table, len);
+               break;
+       default:
+               pkts = -1;
+               break;
+       }
+
+       unlock_entry(pktio_entry);
+       if (pkts < 0)
+               return pkts;
+
+       for (i = 0; i < pkts; ++i)
+               odp_pktio_set_input(pkt_table[i], id);
+
+       return pkts;
+}
+
+int odp_pktio_send(odp_pktio_t id, odp_packet_t pkt_table[], unsigned len)
+{
+       pktio_entry_t *pktio_entry = get_entry(id);
+       int pkts;
+
+       if (pktio_entry == NULL)
+               return -1;
+
+       lock_entry(pktio_entry);
+       switch (pktio_entry->s.params.type) {
+       case ODP_PKTIO_TYPE_SOCKET_BASIC:
+               pkts = send_pkt_sock_basic(&pktio_entry->s.pkt_sock,
+                               pkt_table, len);
+               break;
+       case ODP_PKTIO_TYPE_SOCKET_MMSG:
+               pkts = send_pkt_sock_mmsg(&pktio_entry->s.pkt_sock,
+                               pkt_table, len);
+               break;
+       case ODP_PKTIO_TYPE_SOCKET_MMAP:
+               pkts = send_pkt_sock_mmap(&pktio_entry->s.pkt_sock_mmap,
+                               pkt_table, len);
+               break;
+       case ODP_PKTIO_TYPE_NETMAP:
+               pkts = send_pkt_netmap(&pktio_entry->s.pkt_nm,
+                               pkt_table, len);
+               break;
+       default:
+               pkts = -1;
+       }
+       unlock_entry(pktio_entry);
+
+       return pkts;
+}
+
+int odp_pktio_inq_setdef(odp_pktio_t id, odp_queue_t queue)
+{
+       pktio_entry_t *pktio_entry = get_entry(id);
+       queue_entry_t *qentry = queue_to_qentry(queue);
+
+       if (pktio_entry == NULL || qentry == NULL)
+               return -1;
+
+       if (qentry->s.type != ODP_QUEUE_TYPE_PKTIN)
+               return -1;
+
+       lock_entry(pktio_entry);
+       pktio_entry->s.inq_default = queue;
+       unlock_entry(pktio_entry);
+
+       queue_lock(qentry);
+       qentry->s.pktin = id;
+       qentry->s.status = QUEUE_STATUS_SCHED;
+       queue_unlock(qentry);
+
+       odp_schedule_queue(queue, qentry->s.param.sched.prio);
+
+       return 0;
+}
+
+int odp_pktio_inq_remdef(odp_pktio_t id)
+{
+       return odp_pktio_inq_setdef(id, ODP_QUEUE_INVALID);
+}
+
+odp_queue_t odp_pktio_inq_getdef(odp_pktio_t id)
+{
+       pktio_entry_t *pktio_entry = get_entry(id);
+
+       if (pktio_entry == NULL)
+               return ODP_QUEUE_INVALID;
+
+       return pktio_entry->s.inq_default;
+}
+
+odp_queue_t odp_pktio_outq_getdef(odp_pktio_t id)
+{
+       pktio_entry_t *pktio_entry = get_entry(id);
+
+       if (pktio_entry == NULL)
+               return ODP_QUEUE_INVALID;
+
+       return pktio_entry->s.outq_default;
+}
+
+int pktout_enqueue(queue_entry_t *qentry, odp_buffer_hdr_t *buf_hdr)
+{
+       odp_packet_t pkt = odp_packet_from_buffer(buf_hdr->handle.handle);
+       int len = 1;
+       int nbr;
+
+       nbr = odp_pktio_send(qentry->s.pktout, &pkt, len);
+       return (nbr == len ? 0 : -1);
+}
+
+odp_buffer_hdr_t *pktout_dequeue(queue_entry_t *qentry)
+{
+       (void)qentry;
+       return NULL;
+}
+
+int pktout_enq_multi(queue_entry_t *qentry, odp_buffer_hdr_t *buf_hdr[],
+                    int num)
+{
+       odp_packet_t pkt_tbl[QUEUE_MULTI_MAX];
+       int nbr;
+       int i;
+
+       for (i = 0; i < num; ++i)
+               pkt_tbl[i] = odp_packet_from_buffer(buf_hdr[i]->handle.handle);
+
+       nbr = odp_pktio_send(qentry->s.pktout, pkt_tbl, num);
+       return (nbr == num ? 0 : -1);
+}
+
+int pktout_deq_multi(queue_entry_t *qentry, odp_buffer_hdr_t *buf_hdr[],
+                    int num)
+{
+       (void)qentry;
+       (void)buf_hdr;
+       (void)num;
+
+       return 0;
+}
+
+int pktin_enqueue(queue_entry_t *qentry, odp_buffer_hdr_t *buf_hdr)
+{
+       /* Use default action */
+       return queue_enq(qentry, buf_hdr);
+}
+
+odp_buffer_hdr_t *pktin_dequeue(queue_entry_t *qentry)
+{
+       odp_buffer_hdr_t *buf_hdr;
+
+       buf_hdr = queue_deq(qentry);
+
+       if (buf_hdr == NULL) {
+               odp_packet_t pkt;
+               odp_buffer_t buf;
+               odp_packet_t pkt_tbl[QUEUE_MULTI_MAX];
+               odp_buffer_hdr_t *tmp_hdr_tbl[QUEUE_MULTI_MAX];
+               int pkts, i, j;
+
+               pkts = odp_pktio_recv(qentry->s.pktin, pkt_tbl,
+                                     QUEUE_MULTI_MAX);
+
+               if (pkts > 0) {
+                       pkt = pkt_tbl[0];
+                       buf = odp_buffer_from_packet(pkt);
+                       buf_hdr = odp_buf_to_hdr(buf);
+
+                       for (i = 1, j = 0; i < pkts; ++i) {
+                               buf = odp_buffer_from_packet(pkt_tbl[i]);
+                               tmp_hdr_tbl[j++] = odp_buf_to_hdr(buf);
+                       }
+                       queue_enq_multi(qentry, tmp_hdr_tbl, j);
+               }
+       }
+
+       return buf_hdr;
+}
+
+int pktin_enq_multi(queue_entry_t *qentry, odp_buffer_hdr_t *buf_hdr[], int 
num)
+{
+       /* Use default action */
+       return queue_enq_multi(qentry, buf_hdr, num);
+}
+
+int pktin_deq_multi(queue_entry_t *qentry, odp_buffer_hdr_t *buf_hdr[], int 
num)
+{
+       int nbr;
+
+       nbr = queue_deq_multi(qentry, buf_hdr, num);
+
+       if (nbr < num) {
+               odp_packet_t pkt_tbl[QUEUE_MULTI_MAX];
+               odp_buffer_hdr_t *tmp_hdr_tbl[QUEUE_MULTI_MAX];
+               odp_buffer_t buf;
+               int pkts, i;
+
+               pkts = odp_pktio_recv(qentry->s.pktin, pkt_tbl,
+                                     QUEUE_MULTI_MAX);
+               if (pkts > 0) {
+                       for (i = 0; i < pkts; ++i) {
+                               buf = odp_buffer_from_packet(pkt_tbl[i]);
+                               tmp_hdr_tbl[i] = odp_buf_to_hdr(buf);
+                       }
+                       queue_enq_multi(qentry, tmp_hdr_tbl, pkts);
+               }
+       }
+
+       return nbr;
+}
diff --git a/platform/linux-netmap/odp_packet_netmap.c 
b/platform/linux-netmap/odp_packet_netmap.c
new file mode 100644
index 0000000..524492d
--- /dev/null
+++ b/platform/linux-netmap/odp_packet_netmap.c
@@ -0,0 +1,453 @@
+/* Copyright (c) 2013, Linaro Limited
+ * Copyright (c) 2013, Nokia Solutions and Networks
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+/*
+ * NETMAP I/O code inspired by the pkt-gen example application in netmap by:
+ * Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo. All rights reserved.
+ * Copyright (C) 2013-2014 Universita` di Pisa. All rights reserved.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <poll.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <linux/ethtool.h>
+#include <linux/sockios.h>
+
+#include <odp_packet_internal.h>
+#include <odp_hints.h>
+#include <odp_thread.h>
+
+#include <odph_eth.h>
+#include <odph_ip.h>
+#include <odph_packet.h>
+
+#define NETMAP_WITH_LIBS
+#include <odp_packet_netmap.h>
+
+/** Eth buffer start offset from u32-aligned address to make sure the following
+ * header (e.g. IP) starts at a 32-bit aligned address.
+ */
+#define ETHBUF_OFFSET (ODP_ALIGN_ROUNDUP(ODPH_ETHHDR_LEN, sizeof(uint32_t)) \
+                               - ODPH_ETHHDR_LEN)
+
+/** Round up buffer address to get a properly aliged eth buffer, i.e. aligned
+ * so that the next header always starts at a 32bit aligned address.
+ */
+#define ETHBUF_ALIGN(buf_ptr) ((uint8_t *)ODP_ALIGN_ROUNDUP_PTR((buf_ptr), \
+                               sizeof(uint32_t)) + ETHBUF_OFFSET)
+
+#define ETH_PROMISC  1 /* TODO: maybe this should be exported to the user */
+#define WAITLINK_TMO 2
+#define POLL_TMO     50
+
+static int nm_do_ioctl(pkt_netmap_t * const pkt_nm, unsigned long cmd,
+                      int subcmd)
+{
+       struct ethtool_value eval;
+       struct ifreq ifr;
+       int error;
+       int fd;
+
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+       if (fd < 0) {
+               ODP_ERR("Error: cannot get device control socket\n");
+               return -1;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, pkt_nm->ifname, sizeof(ifr.ifr_name));
+
+       switch (cmd) {
+       case SIOCSIFFLAGS:
+               ifr.ifr_flags = pkt_nm->if_flags & 0xffff;
+               break;
+       case SIOCETHTOOL:
+               eval.cmd = subcmd;
+               eval.data = 0;
+               ifr.ifr_data = (caddr_t)&eval;
+               break;
+       default:
+               break;
+       }
+       error = ioctl(fd, cmd, &ifr);
+       if (error)
+               goto done;
+
+       switch (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);
+               break;
+       default:
+               break;
+       }
+done:
+       close(fd);
+       if (error)
+               ODP_ERR("ioctl err %d %lu: %s\n", error, cmd, strerror(errno));
+
+       return error;
+}
+
+int setup_pkt_netmap(pkt_netmap_t * const pkt_nm, const char *netdev,
+                    odp_buffer_pool_t pool, netmap_params_t *nm_params)
+{
+       char qname[ODP_QUEUE_NAME_LEN];
+       char ifname[32];
+       odp_packet_t pkt;
+       odp_buffer_t token;
+       uint8_t *pkt_buf;
+       uint16_t ringid;
+       uint8_t *l2_hdr;
+       int ret;
+
+       if (pool == ODP_BUFFER_POOL_INVALID)
+               return -1;
+       pkt_nm->pool = pool;
+
+       pkt = odph_packet_alloc(pool);
+       if (!odph_packet_is_valid(pkt))
+               return -1;
+
+       pkt_buf = odp_packet_buf_addr(pkt);
+       l2_hdr = ETHBUF_ALIGN(pkt_buf);
+       /* Store eth buffer offset for buffers from this pool */
+       pkt_nm->frame_offset = (uintptr_t)l2_hdr - (uintptr_t)pkt_buf;
+       /* pkt buffer size */
+       pkt_nm->buf_size = odph_packet_buf_size(pkt);
+       /* max frame len taking into account the l2-offset */
+       pkt_nm->max_frame_len = pkt_nm->buf_size - pkt_nm->frame_offset;
+       /* save netmap_mode for later use */
+       pkt_nm->netmap_mode = nm_params->netmap_mode;
+
+       odph_packet_free(pkt);
+
+       if (nm_params->netmap_mode == ODP_NETMAP_MODE_SW)
+               ringid = NETMAP_SW_RING;
+       else
+               ringid = nm_params->ringid;
+
+       strncpy(pkt_nm->ifname, netdev, sizeof(pkt_nm->ifname));
+       snprintf(ifname, sizeof(ifname), "netmap:%s", netdev);
+       pkt_nm->nm_desc = nm_open(ifname, NULL, ringid, 0);
+
+       if (pkt_nm->nm_desc == NULL) {
+               ODP_ERR("Error opening nm interface: %s\n", strerror(errno));
+               return -1;
+       }
+
+       ODP_DBG("thread %d mode %s mmap addr %p\n",
+               odp_thread_id(),
+               nm_params->netmap_mode == ODP_NETMAP_MODE_SW ? "SW" : "HW",
+               pkt_nm->nm_desc->mem);
+
+       if (nm_params->netmap_mode == ODP_NETMAP_MODE_SW) {
+               pkt_nm->begin  = pkt_nm->nm_desc->req.nr_rx_rings;
+               pkt_nm->end    = pkt_nm->begin + 1;
+               pkt_nm->rxring = NETMAP_RXRING(pkt_nm->nm_desc->nifp,
+                                   pkt_nm->nm_desc->req.nr_rx_rings);
+               pkt_nm->txring = NETMAP_TXRING(pkt_nm->nm_desc->nifp,
+                                    pkt_nm->nm_desc->req.nr_tx_rings);
+       } else if (nm_params->ringid & NETMAP_HW_RING) {
+               pkt_nm->begin  = nm_params->ringid & NETMAP_RING_MASK;
+               pkt_nm->end    = pkt_nm->begin + 1;
+               pkt_nm->rxring = NETMAP_RXRING(pkt_nm->nm_desc->nifp,
+                                    pkt_nm->begin);
+               pkt_nm->txring = NETMAP_TXRING(pkt_nm->nm_desc->nifp,
+                                    pkt_nm->begin);
+       } else {
+               pkt_nm->begin  = 0;
+               pkt_nm->end    = pkt_nm->nm_desc->req.nr_rx_rings;
+               pkt_nm->rxring = NETMAP_RXRING(pkt_nm->nm_desc->nifp, 0);
+               pkt_nm->txring = NETMAP_TXRING(pkt_nm->nm_desc->nifp, 0);
+       }
+
+       /* Set TX checksumming if hardware rings */
+       if (nm_params->netmap_mode == ODP_NETMAP_MODE_HW) {
+               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);
+                       pkt_nm->if_flags |= IFF_UP;
+               }
+               if (ETH_PROMISC) {
+                       pkt_nm->if_flags |= IFF_PROMISC;
+                       nm_do_ioctl(pkt_nm, SIOCSIFFLAGS, 0);
+               }
+               ret = nm_do_ioctl(pkt_nm, SIOCETHTOOL, ETHTOOL_SGSO);
+               if (ret)
+                       ODP_DBG("ETHTOOL_SGSO not supported\n");
+
+               ret = nm_do_ioctl(pkt_nm, SIOCETHTOOL, ETHTOOL_STSO);
+               if (ret)
+                       ODP_DBG("ETHTOOL_STSO not supported\n");
+               /* 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.
+                */
+               /*
+               nm_do_ioctl(pkt_nm, SIOCETHTOOL, ETHTOOL_SRXCSUM);
+               */
+               ret = nm_do_ioctl(pkt_nm, SIOCETHTOOL, ETHTOOL_STXCSUM);
+               if (ret)
+                       ODP_DBG("ETHTOOL_STXCSUM not supported\n");
+       }
+
+       /* Set up the TX access queue */
+       snprintf(qname, sizeof(qname), "%s:%s-pktio_tx_access", netdev,
+                nm_params->netmap_mode == ODP_NETMAP_MODE_SW ? "SW" : "HW");
+       pkt_nm->tx_access = odp_queue_create(qname, ODP_QUEUE_TYPE_POLL, NULL);
+       if (pkt_nm->tx_access == ODP_QUEUE_INVALID) {
+               ODP_ERR("Error: pktio queue creation failed\n");
+               return -1;
+       }
+       token = odp_buffer_alloc(pool);
+       if (!odp_buffer_is_valid(token)) {
+               ODP_ERR("Error: token creation failed\n");
+               return -1;
+       }
+
+       odp_queue_enq(pkt_nm->tx_access, token);
+
+       ODP_DBG("Wait for link to come up\n");
+       sleep(WAITLINK_TMO);
+       ODP_DBG("Done\n");
+
+       return 0;
+}
+
+int close_pkt_netmap(pkt_netmap_t * const pkt_nm)
+{
+       if (pkt_nm->nm_desc != NULL) {
+               nm_close(pkt_nm->nm_desc);
+               pkt_nm->nm_desc = NULL;
+       }
+
+       return 0;
+}
+
+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;
+#ifdef NETMAP_BLOCKING_IO
+       struct pollfd fds[1];
+       int ret;
+#endif
+
+       fd = pkt_nm->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;
+#else
+               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->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_buf_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)) {
+                               if (odp_unlikely(pkt_nm->netmap_mode !=
+                                                ODP_NETMAP_MODE_SW)) {
+                                       ODP_ERR("RX: Frame truncated: %u\n",
+                                               (unsigned)frame_len);
+                                       continue;
+                               }
+                               memset(l2_hdr + frame_len, 0,
+                                      ODPH_ETH_LEN_MIN - frame_len);
+                               frame_len = ODPH_ETH_LEN_MIN;
+                       }
+
+                       /* 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;
+       }
+
+       if (odp_unlikely(pkt != ODP_PACKET_INVALID))
+               odp_buffer_free((odp_buffer_t) pkt);
+
+       if (nb_rx)
+               ODP_DBG("<=== rcvd %03u frames from netmap adapter\n", nb_rx);
+
+       return 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;
+       odp_buffer_t        token;
+
+#ifdef NETMAP_BLOCKING_IO
+       struct pollfd fds[2];
+       int ret;
+#endif
+
+       fd = pkt_nm->nm_desc->fd;
+#ifdef NETMAP_BLOCKING_IO
+       fds[0].fd = fd;
+       fds[0].events = POLLOUT;
+#endif
+
+       token = odp_queue_deq(pkt_nm->tx_access);
+
+       while (nb_tx < len) {
+#ifdef NETMAP_BLOCKING_IO
+               ret = poll(&fds[0], 1, POLL_TMO);
+               if (ret <= 0 || (fds[0].revents & POLLERR))
+                       break;
+#else
+               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->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_start(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++;
+               }
+       }
+
+       odp_queue_enq(pkt_nm->tx_access, token);
+
+#ifndef NETMAP_BLOCKING_IO
+       ioctl(fd, NIOCTXSYNC, NULL);
+#endif
+
+       if (nb_tx)
+               ODP_DBG("===> sent %03u frames to netmap adapter\n", nb_tx);
+
+       for (tx = 0; tx < len; tx++)
+               odph_packet_free(pkt_table[tx]);
+
+       return nb_tx;
+}
diff --git a/platform/linux-netmap/odp_packet_netmap.h 
b/platform/linux-netmap/odp_packet_netmap.h
new file mode 100644
index 0000000..1ab50d0
--- /dev/null
+++ b/platform/linux-netmap/odp_packet_netmap.h
@@ -0,0 +1,67 @@
+/* Copyright (c) 2013, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#ifndef ODP_PACKET_NETMAP_H
+#define ODP_PACKET_NETMAP_H
+
+#include <stdint.h>
+
+#include <net/if.h>
+#include <net/netmap.h>
+#include <net/netmap_user.h>
+
+#include <odp_align.h>
+#include <odp_debug.h>
+#include <odp_buffer_pool.h>
+#include <odp_packet.h>
+
+#include <odp_pktio_netmap.h>
+
+#define ODP_NETMAP_MODE_HW     0
+#define ODP_NETMAP_MODE_SW     1
+
+#define NETMAP_BLOCKING_IO
+
+/** Packet socket using netmap mmaped rings for both Rx and Tx */
+typedef struct {
+       odp_buffer_pool_t pool;
+       size_t max_frame_len; /**< max frame len = buf_size - sizeof(pkt_hdr) */
+       size_t frame_offset; /**< frame start offset from start of pkt buf */
+       size_t buf_size; /**< size of buffer payload in 'pool' */
+       int netmap_mode;
+       struct nm_desc_t *nm_desc;
+       uint32_t begin;
+       uint32_t end;
+       struct netmap_ring *rxring;
+       struct netmap_ring *txring;
+       odp_queue_t tx_access; /* Used for exclusive access to send packets */
+       uint32_t if_flags;
+       char ifname[32];
+} pkt_netmap_t;
+
+/**
+ * Configure an interface to work in netmap mode
+ */
+int setup_pkt_netmap(pkt_netmap_t * const pkt_nm, const char *netdev,
+                    odp_buffer_pool_t pool, netmap_params_t *nm_params);
+
+/**
+ * Switch interface from netmap mode to normal mode
+ */
+int close_pkt_netmap(pkt_netmap_t * const pkt_nm);
+
+/**
+ * Receive packets using netmap
+ */
+int recv_pkt_netmap(pkt_netmap_t * const pkt_nm, odp_packet_t pkt_table[],
+                   unsigned len);
+
+/**
+ * Send packets using netmap
+ */
+int send_pkt_netmap(pkt_netmap_t * const pkt_nm, odp_packet_t pkt_table[],
+                   unsigned len);
+#endif
-- 
1.8.3.2


_______________________________________________
lng-odp mailing list
[email protected]
http://lists.linaro.org/mailman/listinfo/lng-odp

Reply via email to