examples/ethtool: add user-space ethtool sample application
authorRemy Horton <remy.horton@intel.com>
Mon, 7 Dec 2015 13:48:59 +0000 (13:48 +0000)
committerThomas Monjalon <thomas.monjalon@6wind.com>
Tue, 8 Dec 2015 02:00:42 +0000 (03:00 +0100)
Further enhancements to the userspace ethtool implementation that was
submitted in 2.1 and packaged as a self-contained sample application.
Implements an rte_ethtool shim layer based on rte_ethdev API, along
with a command prompt driven demonstration application.

Signed-off-by: Remy Horton <remy.horton@intel.com>
13 files changed:
MAINTAINERS
doc/guides/rel_notes/release_2_2.rst
doc/guides/sample_app_ug/ethtool.rst [new file with mode: 0644]
doc/guides/sample_app_ug/index.rst
examples/Makefile
examples/ethtool/Makefile [new file with mode: 0644]
examples/ethtool/ethtool-app/Makefile [new file with mode: 0644]
examples/ethtool/ethtool-app/ethapp.c [new file with mode: 0644]
examples/ethtool/ethtool-app/ethapp.h [new file with mode: 0644]
examples/ethtool/ethtool-app/main.c [new file with mode: 0644]
examples/ethtool/lib/Makefile [new file with mode: 0644]
examples/ethtool/lib/rte_ethtool.c [new file with mode: 0644]
examples/ethtool/lib/rte_ethtool.h [new file with mode: 0644]

index 8319ce1..9c8e767 100644 (file)
@@ -521,6 +521,10 @@ M: Pablo de Lara <pablo.de.lara.guarch@intel.com>
 F: examples/dpdk_qat/
 F: doc/guides/sample_app_ug/intel_quickassist.rst
 
+M: Remy Horton <remy.horton@intel.com>
+F: examples/ethtool/
+F: doc/guides/sample_app_ug/ethtool.rst
+
 F: examples/exception_path/
 F: doc/guides/sample_app_ug/exception_path.rst
 
index 34237b9..e0724d5 100644 (file)
@@ -125,6 +125,8 @@ New Features
 
 * **Added port hotplug support to xenvirt.**
 
+* **Added ethtool shim and sample application.**
+
 
 Resolved Issues
 ---------------
diff --git a/doc/guides/sample_app_ug/ethtool.rst b/doc/guides/sample_app_ug/ethtool.rst
new file mode 100644 (file)
index 0000000..4d1697e
--- /dev/null
@@ -0,0 +1,160 @@
+
+..  BSD LICENSE
+    Copyright(c) 2015 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.
+
+Ethtool Sample Application
+==========================
+
+The Ethtool sample application shows an implementation of an
+ethtool-like API and provides a console environment that allows
+its use to query and change Ethernet card parameters. The sample
+is based upon a simple L2 frame reflector.
+
+Compiling the Application
+-------------------------
+
+To compile the application:
+
+#.  Go to the sample application directory:
+
+    .. code-block:: console
+
+        export RTE_SDK=/path/to/rte_sdk
+        cd ${RTE_SD}/examples/ethtool
+
+#.  Set the target (a default target is used if not specified). For example:
+
+    .. code-block:: console
+
+        export RTE_TARGET=x86_64-native-linuxapp-gcc
+
+    See the *DPDK Getting Started Guide* for possible RTE_TARGET values.
+
+#.  Build the application:
+
+    .. code-block:: console
+
+        make
+
+Running the Application
+-----------------------
+
+The application requires an available core for each port, plus one.
+The only available options are the standard ones for the EAL:
+
+.. code-block:: console
+
+    ./ethtool-app/ethtool-app/${RTE_TARGET}/ethtool [EAL options]
+
+Refer to the *DPDK Getting Started Guide* for general information on
+running applications and the Environment Abstraction Layer (EAL)
+options.
+
+Using the application
+---------------------
+
+The application is console-driven using the cmdline DPDK interface:
+
+.. code-block:: console
+
+        EthApp>
+
+From this interface the available commands and descriptions of what
+they do as as follows:
+
+* ``drvinfo``: Print driver info
+* ``eeprom``: Dump EEPROM to file
+* ``link``: Print port link states
+* ``macaddr``: Gets/sets MAC address
+* ``mtu``: Set NIC MTU
+* ``open``: Open port
+* ``pause``: Get/set port pause state
+* ``portstats``: Print port statistics
+* ``regs``: Dump port register(s) to file
+* ``ringparam``: Get/set ring parameters
+* ``rxmode``: Toggle port Rx mode
+* ``stop``: Stop port
+* ``validate``: Check that given MAC address is valid unicast address
+* ``vlan``: Add/remove VLAN id
+* ``quit``: Exit program
+
+
+Explanation
+-----------
+
+The sample program has two parts: A background `packet reflector`_
+that runs on a slave core, and a foreground `Ethtool Shell`_ that
+runs on the master core. These are described below.
+
+Packet Reflector
+~~~~~~~~~~~~~~~~
+
+The background packet reflector is intended to demonstrate basic
+packet processing on NIC ports controlled by the Ethtool shim.
+Each incoming MAC frame is rewritten so that it is returned to
+the sender, using the port in question's own MAC address as the
+source address, and is then sent out on the same port.
+
+Ethtool Shell
+~~~~~~~~~~~~~
+
+The foreground part of the Ethtool sample is a console-based
+interface that accepts commands as described in `using the
+application`_. Individual call-back functions handle the detail
+associated with each command, which make use of the functions
+defined in the `Ethtool interface`_ to the DPDK functions.
+
+Ethtool interface
+-----------------
+
+The Ethtool interface is built as a separate library, and implements
+the following functions:
+
+- ``rte_ethtool_get_drvinfo()``
+- ``rte_ethtool_get_regs_len()``
+- ``rte_ethtool_get_regs()``
+- ``rte_ethtool_get_link()``
+- ``rte_ethtool_get_eeprom_len()``
+- ``rte_ethtool_get_eeprom()``
+- ``rte_ethtool_set_eeprom()``
+- ``rte_ethtool_get_pauseparam()``
+- ``rte_ethtool_set_pauseparam()``
+- ``rte_ethtool_net_open()``
+- ``rte_ethtool_net_stop()``
+- ``rte_ethtool_net_get_mac_addr()``
+- ``rte_ethtool_net_set_mac_addr()``
+- ``rte_ethtool_net_validate_addr()``
+- ``rte_ethtool_net_change_mtu()``
+- ``rte_ethtool_net_get_stats64()``
+- ``rte_ethtool_net_vlan_rx_add_vid()``
+- ``rte_ethtool_net_vlan_rx_kill_vid()``
+- ``rte_ethtool_net_set_rx_mode()``
+- ``rte_ethtool_get_ringparam()``
+- ``rte_ethtool_set_ringparam()``
index 1f62ef5..b8fca39 100644 (file)
@@ -41,6 +41,7 @@ Sample Applications User Guide
 
     intro
     cmd_line
+    ethtool
     exception_path
     hello_world
     skeleton
index 5dd2c53..1cb4785 100644 (file)
@@ -43,6 +43,7 @@ DIRS-$(CONFIG_RTE_LIBRTE_DISTRIBUTOR) += distributor
 ifneq ($(ICP_ROOT),)
 DIRS-y += dpdk_qat
 endif
+DIRS-y += ethtool
 DIRS-y += exception_path
 DIRS-y += helloworld
 DIRS-y += ip_pipeline
diff --git a/examples/ethtool/Makefile b/examples/ethtool/Makefile
new file mode 100644 (file)
index 0000000..94f8ee3
--- /dev/null
@@ -0,0 +1,48 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2015 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.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overwritten by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp")
+$(error This application can only operate in a linuxapp environment, \
+please change the definition of the RTE_TARGET environment variable)
+endif
+
+DIRS-y += lib ethtool-app
+
+include $(RTE_SDK)/mk/rte.extsubdir.mk
diff --git a/examples/ethtool/ethtool-app/Makefile b/examples/ethtool/ethtool-app/Makefile
new file mode 100644 (file)
index 0000000..09c66ad
--- /dev/null
@@ -0,0 +1,54 @@
+#   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.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overridden by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# binary name
+APP = ethtool
+
+# all source are stored in SRCS-y
+SRCS-y := main.c ethapp.c
+
+CFLAGS += -O3 -D_GNU_SOURCE -pthread -I$(SRCDIR)/../lib
+CFLAGS += $(WERROR_FLAGS)
+
+LDLIBS += -L$(subst ethtool-app,lib,$(RTE_OUTPUT))/lib
+LDLIBS += -lrte_ethtool
+
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/ethtool/ethtool-app/ethapp.c b/examples/ethtool/ethtool-app/ethapp.c
new file mode 100644 (file)
index 0000000..57c584e
--- /dev/null
@@ -0,0 +1,873 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 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 <cmdline_parse.h>
+#include <cmdline_parse_num.h>
+#include <cmdline_parse_string.h>
+#include <cmdline_parse_etheraddr.h>
+#include <cmdline_socket.h>
+#include <cmdline.h>
+
+#include "rte_ethtool.h"
+#include "ethapp.h"
+
+#define EEPROM_DUMP_CHUNKSIZE 1024
+
+
+struct pcmd_get_params {
+       cmdline_fixed_string_t cmd;
+};
+struct pcmd_int_params {
+       cmdline_fixed_string_t cmd;
+       uint16_t port;
+};
+struct pcmd_intstr_params {
+       cmdline_fixed_string_t cmd;
+       uint16_t port;
+       cmdline_fixed_string_t opt;
+};
+struct pcmd_intmac_params {
+       cmdline_fixed_string_t cmd;
+       uint16_t port;
+       struct ether_addr mac;
+};
+struct pcmd_str_params {
+       cmdline_fixed_string_t cmd;
+       cmdline_fixed_string_t opt;
+};
+struct pcmd_vlan_params {
+       cmdline_fixed_string_t cmd;
+       uint16_t port;
+       cmdline_fixed_string_t mode;
+       uint16_t vid;
+};
+struct pcmd_intintint_params {
+       cmdline_fixed_string_t cmd;
+       uint16_t port;
+       uint16_t tx;
+       uint16_t rx;
+};
+
+
+/* Parameter-less commands */
+cmdline_parse_token_string_t pcmd_quit_token_cmd =
+       TOKEN_STRING_INITIALIZER(struct pcmd_get_params, cmd, "quit");
+cmdline_parse_token_string_t pcmd_stats_token_cmd =
+       TOKEN_STRING_INITIALIZER(struct pcmd_get_params, cmd, "stats");
+cmdline_parse_token_string_t pcmd_drvinfo_token_cmd =
+       TOKEN_STRING_INITIALIZER(struct pcmd_get_params, cmd, "drvinfo");
+cmdline_parse_token_string_t pcmd_link_token_cmd =
+       TOKEN_STRING_INITIALIZER(struct pcmd_get_params, cmd, "link");
+
+/* Commands taking just port id */
+cmdline_parse_token_string_t pcmd_open_token_cmd =
+       TOKEN_STRING_INITIALIZER(struct pcmd_int_params, cmd, "open");
+cmdline_parse_token_string_t pcmd_stop_token_cmd =
+       TOKEN_STRING_INITIALIZER(struct pcmd_int_params, cmd, "stop");
+cmdline_parse_token_string_t pcmd_rxmode_token_cmd =
+       TOKEN_STRING_INITIALIZER(struct pcmd_int_params, cmd, "rxmode");
+cmdline_parse_token_string_t pcmd_portstats_token_cmd =
+       TOKEN_STRING_INITIALIZER(struct pcmd_int_params, cmd, "portstats");
+cmdline_parse_token_num_t pcmd_int_token_port =
+       TOKEN_NUM_INITIALIZER(struct pcmd_int_params, port, UINT16);
+
+/* Commands taking port id and string */
+cmdline_parse_token_string_t pcmd_eeprom_token_cmd =
+       TOKEN_STRING_INITIALIZER(struct pcmd_intstr_params, cmd, "eeprom");
+cmdline_parse_token_string_t pcmd_mtu_token_cmd =
+       TOKEN_STRING_INITIALIZER(struct pcmd_intstr_params, cmd, "mtu");
+cmdline_parse_token_string_t pcmd_regs_token_cmd =
+       TOKEN_STRING_INITIALIZER(struct pcmd_intstr_params, cmd, "regs");
+
+cmdline_parse_token_num_t pcmd_intstr_token_port =
+       TOKEN_NUM_INITIALIZER(struct pcmd_intstr_params, port, UINT16);
+cmdline_parse_token_string_t pcmd_intstr_token_opt =
+       TOKEN_STRING_INITIALIZER(struct pcmd_intstr_params, opt, NULL);
+
+/* Commands taking port id and a MAC address string */
+cmdline_parse_token_string_t pcmd_macaddr_token_cmd =
+       TOKEN_STRING_INITIALIZER(struct pcmd_intmac_params, cmd, "macaddr");
+cmdline_parse_token_num_t pcmd_intmac_token_port =
+       TOKEN_NUM_INITIALIZER(struct pcmd_intmac_params, port, UINT16);
+cmdline_parse_token_etheraddr_t pcmd_intmac_token_mac =
+       TOKEN_ETHERADDR_INITIALIZER(struct pcmd_intmac_params, mac);
+
+/* Command taking just a MAC address */
+cmdline_parse_token_string_t pcmd_validate_token_cmd =
+       TOKEN_STRING_INITIALIZER(struct pcmd_intmac_params, cmd, "validate");
+
+
+/* Commands taking port id and two integers */
+cmdline_parse_token_string_t pcmd_ringparam_token_cmd =
+       TOKEN_STRING_INITIALIZER(struct pcmd_intintint_params, cmd,
+               "ringparam");
+cmdline_parse_token_num_t pcmd_intintint_token_port =
+       TOKEN_NUM_INITIALIZER(struct pcmd_intintint_params, port, UINT16);
+cmdline_parse_token_num_t pcmd_intintint_token_tx =
+       TOKEN_NUM_INITIALIZER(struct pcmd_intintint_params, tx, UINT16);
+cmdline_parse_token_num_t pcmd_intintint_token_rx =
+       TOKEN_NUM_INITIALIZER(struct pcmd_intintint_params, rx, UINT16);
+
+
+/* Pause commands */
+cmdline_parse_token_string_t pcmd_pause_token_cmd =
+       TOKEN_STRING_INITIALIZER(struct pcmd_intstr_params, cmd, "pause");
+cmdline_parse_token_num_t pcmd_pause_token_port =
+       TOKEN_NUM_INITIALIZER(struct pcmd_intstr_params, port, UINT16);
+cmdline_parse_token_string_t pcmd_pause_token_opt =
+       TOKEN_STRING_INITIALIZER(struct pcmd_intstr_params,
+               opt, "all#tx#rx#none");
+
+/* VLAN commands */
+cmdline_parse_token_string_t pcmd_vlan_token_cmd =
+       TOKEN_STRING_INITIALIZER(struct pcmd_vlan_params, cmd, "vlan");
+cmdline_parse_token_num_t pcmd_vlan_token_port =
+       TOKEN_NUM_INITIALIZER(struct pcmd_vlan_params, port, UINT16);
+cmdline_parse_token_string_t pcmd_vlan_token_mode =
+       TOKEN_STRING_INITIALIZER(struct pcmd_vlan_params, mode, "add#del");
+cmdline_parse_token_num_t pcmd_vlan_token_vid =
+       TOKEN_NUM_INITIALIZER(struct pcmd_vlan_params, vid, UINT16);
+
+
+static void
+pcmd_quit_callback(__rte_unused void *ptr_params,
+       struct cmdline *ctx,
+       __rte_unused void *ptr_data)
+{
+       cmdline_quit(ctx);
+}
+
+
+static void
+pcmd_drvinfo_callback(__rte_unused void *ptr_params,
+       __rte_unused struct cmdline *ctx,
+       __rte_unused void *ptr_data)
+{
+       struct ethtool_drvinfo info;
+       int id_port;
+
+       for (id_port = 0; id_port < rte_eth_dev_count(); id_port++) {
+               if (rte_ethtool_get_drvinfo(id_port, &info)) {
+                       printf("Error getting info for port %i\n", id_port);
+                       return;
+               }
+               printf("Port %i driver: %s (ver: %s)\n",
+                       id_port, info.driver, info.version
+                     );
+       }
+}
+
+
+static void
+pcmd_link_callback(__rte_unused void *ptr_params,
+       __rte_unused struct cmdline *ctx,
+       __rte_unused void *ptr_data)
+{
+       int num_ports = rte_eth_dev_count();
+       int id_port, stat_port;
+
+       for (id_port = 0; id_port < num_ports; id_port++) {
+               if (!rte_eth_dev_is_valid_port(id_port))
+                       continue;
+               stat_port = rte_ethtool_get_link(id_port);
+               switch (stat_port) {
+               case 0:
+                       printf("Port %i: Down\n", id_port);
+                       break;
+               case 1:
+                       printf("Port %i: Up\n", id_port);
+                       break;
+               default:
+                       printf("Port %i: Error getting link status\n",
+                               id_port
+                               );
+                       break;
+               }
+       }
+       printf("\n");
+}
+
+
+static void
+pcmd_regs_callback(void *ptr_params,
+       __rte_unused struct cmdline *ctx,
+       __rte_unused void *ptr_data)
+{
+       struct pcmd_intstr_params *params = ptr_params;
+       int len_regs;
+       struct ethtool_regs regs;
+       unsigned char *buf_data;
+       FILE *fp_regs;
+
+       if (!rte_eth_dev_is_valid_port(params->port)) {
+               printf("Error: Invalid port number %i\n", params->port);
+               return;
+       }
+       len_regs = rte_ethtool_get_regs_len(params->port);
+       if (len_regs > 0) {
+               printf("Port %i: %i bytes\n", params->port, len_regs);
+               buf_data = malloc(len_regs);
+               if (buf_data == NULL) {
+                       printf("Error allocating %i bytes for buffer\n",
+                               len_regs);
+                       return;
+               }
+               if (!rte_ethtool_get_regs(params->port, &regs, buf_data)) {
+                       fp_regs = fopen(params->opt, "wb");
+                       if (fp_regs == NULL) {
+                               printf("Error opening '%s' for writing\n",
+                                       params->opt);
+                       } else {
+                               if ((int)fwrite(buf_data,
+                                               1, len_regs,
+                                               fp_regs) != len_regs)
+                                       printf("Error writing '%s'\n",
+                                               params->opt);
+                               fclose(fp_regs);
+                       }
+               }
+               free(buf_data);
+       } else if (len_regs == -ENOTSUP)
+               printf("Port %i: Operation not supported\n", params->port);
+       else
+               printf("Port %i: Error getting registers\n", params->port);
+}
+
+
+static void
+pcmd_eeprom_callback(void *ptr_params,
+       __rte_unused struct cmdline *ctx,
+       __rte_unused void *ptr_data)
+{
+       struct pcmd_intstr_params *params = ptr_params;
+       struct ethtool_eeprom info_eeprom;
+       int len_eeprom;
+       int pos_eeprom;
+       int stat;
+       unsigned char bytes_eeprom[EEPROM_DUMP_CHUNKSIZE];
+       FILE *fp_eeprom;
+
+       if (!rte_eth_dev_is_valid_port(params->port)) {
+               printf("Error: Invalid port number %i\n", params->port);
+               return;
+       }
+       len_eeprom = rte_ethtool_get_eeprom_len(params->port);
+       if (len_eeprom > 0) {
+               fp_eeprom = fopen(params->opt, "wb");
+               if (fp_eeprom == NULL) {
+                       printf("Error opening '%s' for writing\n",
+                               params->opt);
+                       return;
+               }
+               printf("Total EEPROM length: %i bytes\n", len_eeprom);
+               info_eeprom.len = EEPROM_DUMP_CHUNKSIZE;
+               for (pos_eeprom = 0;
+                               pos_eeprom < len_eeprom;
+                               pos_eeprom += EEPROM_DUMP_CHUNKSIZE) {
+                       info_eeprom.offset = pos_eeprom;
+                       if (pos_eeprom + EEPROM_DUMP_CHUNKSIZE > len_eeprom)
+                               info_eeprom.len = len_eeprom - pos_eeprom;
+                       else
+                               info_eeprom.len = EEPROM_DUMP_CHUNKSIZE;
+                       stat = rte_ethtool_get_eeprom(
+                               params->port, &info_eeprom, bytes_eeprom
+                               );
+                       if (stat != 0) {
+                               printf("EEPROM read error %i\n", stat);
+                               break;
+                       }
+                       if (fwrite(bytes_eeprom,
+                                       1, info_eeprom.len,
+                                       fp_eeprom) != info_eeprom.len) {
+                               printf("Error writing '%s'\n", params->opt);
+                               break;
+                       }
+               }
+               fclose(fp_eeprom);
+       } else if (len_eeprom == 0)
+               printf("Port %i: Device does not have EEPROM\n", params->port);
+       else if (len_eeprom == -ENOTSUP)
+               printf("Port %i: Operation not supported\n", params->port);
+       else
+               printf("Port %i: Error getting EEPROM\n", params->port);
+}
+
+
+static void
+pcmd_pause_callback(void *ptr_params,
+       __rte_unused struct cmdline *ctx,
+       void *ptr_data)
+{
+       struct pcmd_intstr_params *params = ptr_params;
+       struct ethtool_pauseparam info;
+       int stat;
+
+       if (!rte_eth_dev_is_valid_port(params->port)) {
+               printf("Error: Invalid port number %i\n", params->port);
+               return;
+       }
+       if (ptr_data != NULL) {
+               stat = rte_ethtool_get_pauseparam(params->port, &info);
+       } else {
+               if (strcasecmp("all", params->opt) == 0) {
+                       info.tx_pause = 1;
+                       info.rx_pause = 1;
+               } else if (strcasecmp("tx", params->opt) == 0) {
+                       info.tx_pause = 1;
+                       info.rx_pause = 0;
+               } else if (strcasecmp("rx", params->opt) == 0) {
+                       info.tx_pause = 0;
+                       info.rx_pause = 1;
+               } else {
+                       info.tx_pause = 0;
+                       info.rx_pause = 0;
+               }
+               stat = rte_ethtool_set_pauseparam(params->port, &info);
+       }
+       if (stat == 0) {
+               if (info.rx_pause && info.tx_pause)
+                       printf("Port %i: Tx & Rx Paused\n", params->port);
+               else if (info.rx_pause)
+                       printf("Port %i: Rx Paused\n", params->port);
+               else if (info.tx_pause)
+                       printf("Port %i: Tx Paused\n", params->port);
+               else
+                       printf("Port %i: Tx & Rx not paused\n", params->port);
+       } else if (stat == -ENOTSUP)
+               printf("Port %i: Operation not supported\n", params->port);
+       else
+               printf("Port %i: Error %i\n", params->port, stat);
+}
+
+
+static void
+pcmd_open_callback(__rte_unused void *ptr_params,
+       __rte_unused struct cmdline *ctx,
+       __rte_unused void *ptr_data)
+{
+       struct pcmd_int_params *params = ptr_params;
+       int stat;
+
+       if (!rte_eth_dev_is_valid_port(params->port)) {
+               printf("Error: Invalid port number %i\n", params->port);
+               return;
+       }
+       lock_port(params->port);
+       stat = rte_ethtool_net_open(params->port);
+       mark_port_active(params->port);
+       unlock_port(params->port);
+       if (stat == 0)
+               return;
+       else if (stat == -ENOTSUP)
+               printf("Port %i: Operation not supported\n", params->port);
+       else
+               printf("Port %i: Error opening device\n", params->port);
+}
+
+static void
+pcmd_stop_callback(__rte_unused void *ptr_params,
+       __rte_unused struct cmdline *ctx,
+       __rte_unused void *ptr_data)
+{
+       struct pcmd_int_params *params = ptr_params;
+       int stat;
+
+       if (!rte_eth_dev_is_valid_port(params->port)) {
+               printf("Error: Invalid port number %i\n", params->port);
+               return;
+       }
+       lock_port(params->port);
+       stat = rte_ethtool_net_stop(params->port);
+       mark_port_inactive(params->port);
+       unlock_port(params->port);
+       if (stat == 0)
+               return;
+       else if (stat == -ENOTSUP)
+               printf("Port %i: Operation not supported\n", params->port);
+       else
+               printf("Port %i: Error stopping device\n", params->port);
+}
+
+
+static void
+pcmd_rxmode_callback(void *ptr_params,
+       __rte_unused struct cmdline *ctx,
+       __rte_unused void *ptr_data)
+{
+       struct pcmd_intstr_params *params = ptr_params;
+       int stat;
+
+       if (!rte_eth_dev_is_valid_port(params->port)) {
+               printf("Error: Invalid port number %i\n", params->port);
+               return;
+       }
+       stat = rte_ethtool_net_set_rx_mode(params->port);
+       if (stat == 0)
+               return;
+       else if (stat == -ENOTSUP)
+               printf("Port %i: Operation not supported\n", params->port);
+       else
+               printf("Port %i: Error setting rx mode\n", params->port);
+}
+
+
+static void
+pcmd_macaddr_callback(void *ptr_params,
+       __rte_unused struct cmdline *ctx,
+       void *ptr_data)
+{
+       struct pcmd_intmac_params *params = ptr_params;
+       struct ether_addr mac_addr;
+       int stat;
+
+       stat = 0;
+       if (!rte_eth_dev_is_valid_port(params->port)) {
+               printf("Error: Invalid port number %i\n", params->port);
+               return;
+       }
+       if (ptr_data != NULL) {
+               lock_port(params->port);
+               stat = rte_ethtool_net_set_mac_addr(params->port,
+                       &params->mac);
+               mark_port_newmac(params->port);
+               unlock_port(params->port);
+               if (stat == 0) {
+                       printf("MAC address changed\n");
+                       return;
+               }
+       } else {
+               stat = rte_ethtool_net_get_mac_addr(params->port, &mac_addr);
+               if (stat == 0) {
+                       printf(
+                               "Port %i MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
+                               params->port,
+                               mac_addr.addr_bytes[0],
+                               mac_addr.addr_bytes[1],
+                               mac_addr.addr_bytes[2],
+                               mac_addr.addr_bytes[3],
+                               mac_addr.addr_bytes[4],
+                               mac_addr.addr_bytes[5]);
+                       return;
+               }
+       }
+       if (stat == 0)
+               return;
+       else if (stat == -ENOTSUP)
+               printf("Port %i: Operation not supported\n", params->port);
+       else
+               printf("Port %i: Error %i\n", params->port, stat);
+}
+
+static void
+pcmd_mtu_callback(void *ptr_params,
+       __rte_unused struct cmdline *ctx,
+       __rte_unused void *ptr_data)
+{
+       struct pcmd_intstr_params *params = ptr_params;
+       int stat;
+       int new_mtu;
+       char *ptr_parse_end;
+
+       if (!rte_eth_dev_is_valid_port(params->port)) {
+               printf("Error: Invalid port number %i\n", params->port);
+               return;
+       }
+       new_mtu = atoi(params->opt);
+       new_mtu = strtoul(params->opt, &ptr_parse_end, 10);
+       if (*ptr_parse_end != '\0' ||
+                       new_mtu < ETHER_MIN_MTU ||
+                       new_mtu > ETHER_MAX_JUMBO_FRAME_LEN) {
+               printf("Port %i: Invalid MTU value\n", params->port);
+               return;
+       }
+       stat = rte_ethtool_net_change_mtu(params->port, new_mtu);
+       if (stat == 0)
+               printf("Port %i: MTU set to %i\n", params->port, new_mtu);
+       else if (stat == -ENOTSUP)
+               printf("Port %i: Operation not supported\n", params->port);
+       else
+               printf("Port %i: Error setting MTU\n", params->port);
+}
+
+
+
+static void pcmd_portstats_callback(__rte_unused void *ptr_params,
+       __rte_unused struct cmdline *ctx,
+       __rte_unused void *ptr_data)
+{
+       struct pcmd_int_params *params = ptr_params;
+       struct rte_eth_stats stat_info;
+       int stat;
+
+       if (!rte_eth_dev_is_valid_port(params->port)) {
+               printf("Error: Invalid port number %i\n", params->port);
+               return;
+       }
+       stat = rte_ethtool_net_get_stats64(params->port, &stat_info);
+       if (stat == 0) {
+               /* Most of rte_eth_stats is deprecated.. */
+               printf("Port %i stats\n", params->port);
+               printf("   In: %" PRIu64 " (%" PRIu64 " bytes)\n"
+                       "  Out: %"PRIu64" (%"PRIu64 " bytes)\n"
+                       "  Err: %"PRIu64"\n",
+                       stat_info.ipackets,
+                       stat_info.ibytes,
+                       stat_info.opackets,
+                       stat_info.obytes,
+                       stat_info.ierrors+stat_info.oerrors
+                     );
+       } else if (stat == -ENOTSUP)
+               printf("Port %i: Operation not supported\n", params->port);
+       else
+               printf("Port %i: Error fetching statistics\n", params->port);
+}
+
+static void pcmd_ringparam_callback(__rte_unused void *ptr_params,
+       __rte_unused struct cmdline *ctx,
+       void *ptr_data)
+{
+       struct pcmd_intintint_params *params = ptr_params;
+       struct ethtool_ringparam ring_data;
+       struct ethtool_ringparam ring_params;
+       int stat;
+
+       if (!rte_eth_dev_is_valid_port(params->port)) {
+               printf("Error: Invalid port number %i\n", params->port);
+               return;
+       }
+       if (ptr_data == NULL) {
+               stat = rte_ethtool_get_ringparam(params->port, &ring_data);
+               if (stat == 0) {
+                       printf("Port %i ring parameters\n"
+                               "  Rx Pending: %i (%i max)\n"
+                               "  Tx Pending: %i (%i max)\n",
+                               params->port,
+                               ring_data.rx_pending,
+                               ring_data.rx_max_pending,
+                               ring_data.tx_pending,
+                               ring_data.tx_max_pending);
+               }
+       } else {
+               if (params->tx < 1 || params->rx < 1) {
+                       printf("Error: Invalid parameters\n");
+                       return;
+               }
+               memset(&ring_params, 0, sizeof(struct ethtool_ringparam));
+               ring_params.tx_pending = params->tx;
+               ring_params.rx_pending = params->rx;
+               lock_port(params->port);
+               stat = rte_ethtool_set_ringparam(params->port, &ring_params);
+               unlock_port(params->port);
+       }
+       if (stat == 0)
+               return;
+       else if (stat == -ENOTSUP)
+               printf("Port %i: Operation not supported\n", params->port);
+       else
+               printf("Port %i: Error fetching statistics\n", params->port);
+}
+
+static void pcmd_validate_callback(void *ptr_params,
+       __rte_unused struct cmdline *ctx,
+       __rte_unused void *ptr_data)
+{
+       struct pcmd_intmac_params *params = ptr_params;
+
+       if (rte_ethtool_net_validate_addr(0, &params->mac))
+               printf("Address is unicast\n");
+       else
+               printf("Address is not unicast\n");
+}
+
+
+static void pcmd_vlan_callback(__rte_unused void *ptr_params,
+       __rte_unused struct cmdline *ctx,
+       __rte_unused void *ptr_data)
+{
+       struct pcmd_vlan_params *params = ptr_params;
+       int stat;
+
+       if (!rte_eth_dev_is_valid_port(params->port)) {
+               printf("Error: Invalid port number %i\n", params->port);
+               return;
+       }
+       stat = 0;
+
+       if (strcasecmp("add", params->mode) == 0) {
+               stat = rte_ethtool_net_vlan_rx_add_vid(
+                       params->port, params->vid
+                       );
+               if (stat == 0)
+                       printf("VLAN vid %i added\n", params->vid);
+
+       } else if (strcasecmp("del", params->mode) == 0) {
+               stat = rte_ethtool_net_vlan_rx_kill_vid(
+                       params->port, params->vid
+                       );
+               if (stat == 0)
+                       printf("VLAN vid %i removed\n", params->vid);
+       } else {
+               /* Should not happen! */
+               printf("Error: Bad mode %s\n", params->mode);
+       }
+       if (stat == -ENOTSUP)
+               printf("Port %i: Operation not supported\n", params->port);
+       else if (stat == -ENOSYS)
+               printf("Port %i: VLAN filtering disabled\n", params->port);
+       else if (stat != 0)
+               printf("Port %i: Error changing VLAN setup (code %i)\n",
+                       params->port, -stat);
+}
+
+
+cmdline_parse_inst_t pcmd_quit = {
+       .f = pcmd_quit_callback,
+       .data = NULL,
+       .help_str = "quit\n     Exit program",
+       .tokens = {(void *)&pcmd_quit_token_cmd, NULL},
+};
+cmdline_parse_inst_t pcmd_drvinfo = {
+       .f = pcmd_drvinfo_callback,
+       .data = NULL,
+       .help_str = "drvinfo\n     Print driver info",
+       .tokens = {(void *)&pcmd_drvinfo_token_cmd, NULL},
+};
+cmdline_parse_inst_t pcmd_link = {
+       .f = pcmd_link_callback,
+       .data = NULL,
+       .help_str = "link\n     Print port link states",
+       .tokens = {(void *)&pcmd_link_token_cmd, NULL},
+};
+cmdline_parse_inst_t pcmd_regs = {
+       .f = pcmd_regs_callback,
+       .data = NULL,
+       .help_str = "regs <port_id> <filename>\n"
+               "     Dump port register(s) to file",
+       .tokens = {
+               (void *)&pcmd_regs_token_cmd,
+               (void *)&pcmd_intstr_token_port,
+               (void *)&pcmd_intstr_token_opt,
+               NULL
+       },
+};
+cmdline_parse_inst_t pcmd_eeprom = {
+       .f = pcmd_eeprom_callback,
+       .data = NULL,
+       .help_str = "eeprom <port_id> <filename>\n    Dump EEPROM to file",
+       .tokens = {
+               (void *)&pcmd_eeprom_token_cmd,
+               (void *)&pcmd_intstr_token_port,
+               (void *)&pcmd_intstr_token_opt,
+               NULL
+       },
+};
+cmdline_parse_inst_t pcmd_pause_noopt = {
+       .f = pcmd_pause_callback,
+       .data = (void *)0x01,
+       .help_str = "pause <port_id>\n     Print port pause state",
+       .tokens = {
+               (void *)&pcmd_pause_token_cmd,
+               (void *)&pcmd_pause_token_port,
+               NULL
+       },
+};
+cmdline_parse_inst_t pcmd_pause = {
+       .f = pcmd_pause_callback,
+       .data = NULL,
+       .help_str =
+               "pause <port_id> <all|tx|rx|none>\n     Pause/unpause port",
+       .tokens = {
+               (void *)&pcmd_pause_token_cmd,
+               (void *)&pcmd_pause_token_port,
+               (void *)&pcmd_pause_token_opt,
+               NULL
+       },
+};
+cmdline_parse_inst_t pcmd_open = {
+       .f = pcmd_open_callback,
+       .data = NULL,
+       .help_str = "open <port_id>\n     Open port",
+       .tokens = {
+               (void *)&pcmd_open_token_cmd,
+               (void *)&pcmd_int_token_port,
+               NULL
+       },
+};
+cmdline_parse_inst_t pcmd_stop = {
+       .f = pcmd_stop_callback,
+       .data = NULL,
+       .help_str = "stop <port_id>\n     Stop port",
+       .tokens = {
+               (void *)&pcmd_stop_token_cmd,
+               (void *)&pcmd_int_token_port,
+               NULL
+       },
+};
+cmdline_parse_inst_t pcmd_rxmode = {
+       .f = pcmd_rxmode_callback,
+       .data = NULL,
+       .help_str = "rxmode <port_id>\n     Toggle port Rx mode",
+       .tokens = {
+               (void *)&pcmd_rxmode_token_cmd,
+               (void *)&pcmd_int_token_port,
+               NULL
+       },
+};
+cmdline_parse_inst_t pcmd_macaddr_get = {
+       .f = pcmd_macaddr_callback,
+       .data = NULL,
+       .help_str = "macaddr <port_id>\n"
+               "     Get MAC address",
+       .tokens = {
+               (void *)&pcmd_macaddr_token_cmd,
+               (void *)&pcmd_intstr_token_port,
+               NULL
+       },
+};
+cmdline_parse_inst_t pcmd_macaddr = {
+       .f = pcmd_macaddr_callback,
+       .data = (void *)0x01,
+       .help_str =
+               "macaddr <port_id> <mac_addr>\n"
+               "     Set MAC address",
+       .tokens = {
+               (void *)&pcmd_macaddr_token_cmd,
+               (void *)&pcmd_intmac_token_port,
+               (void *)&pcmd_intmac_token_mac,
+               NULL
+       },
+};
+cmdline_parse_inst_t pcmd_mtu = {
+       .f = pcmd_mtu_callback,
+       .data = NULL,
+       .help_str = "mtu <port_id> <mtu_value>\n"
+               "     Change MTU",
+       .tokens = {
+               (void *)&pcmd_mtu_token_cmd,
+               (void *)&pcmd_intstr_token_port,
+               (void *)&pcmd_intstr_token_opt,
+               NULL
+       },
+};
+cmdline_parse_inst_t pcmd_portstats = {
+       .f = pcmd_portstats_callback,
+       .data = NULL,
+       .help_str = "portstats <port_id>\n"
+               "     Print port eth statistics",
+       .tokens = {
+               (void *)&pcmd_portstats_token_cmd,
+               (void *)&pcmd_int_token_port,
+               NULL
+       },
+};
+cmdline_parse_inst_t pcmd_ringparam = {
+       .f = pcmd_ringparam_callback,
+       .data = NULL,
+       .help_str = "ringparam <port_id>\n"
+               "     Print ring parameters",
+       .tokens = {
+               (void *)&pcmd_ringparam_token_cmd,
+               (void *)&pcmd_intintint_token_port,
+               NULL
+       },
+};
+cmdline_parse_inst_t pcmd_ringparam_set = {
+       .f = pcmd_ringparam_callback,
+       .data = (void *)1,
+       .help_str = "ringparam <port_id> <tx_param> <rx_param>\n"
+               "     Set ring parameters",
+       .tokens = {
+               (void *)&pcmd_ringparam_token_cmd,
+               (void *)&pcmd_intintint_token_port,
+               (void *)&pcmd_intintint_token_tx,
+               (void *)&pcmd_intintint_token_rx,
+               NULL
+       },
+};
+cmdline_parse_inst_t pcmd_validate = {
+       .f = pcmd_validate_callback,
+       .data = NULL,
+       .help_str = "validate <mac_addr>\n"
+               "     Check that MAC address is valid unicast address",
+       .tokens = {
+               (void *)&pcmd_validate_token_cmd,
+               (void *)&pcmd_intmac_token_mac,
+               NULL
+       },
+};
+cmdline_parse_inst_t pcmd_vlan = {
+       .f = pcmd_vlan_callback,
+       .data = NULL,
+       .help_str = "vlan <port_id> <add|del> <vlan_id>\n"
+               "     Add/remove VLAN id",
+       .tokens = {
+               (void *)&pcmd_vlan_token_cmd,
+               (void *)&pcmd_vlan_token_port,
+               (void *)&pcmd_vlan_token_mode,
+               (void *)&pcmd_vlan_token_vid,
+               NULL
+       },
+};
+
+
+cmdline_parse_ctx_t list_prompt_commands[] = {
+       (cmdline_parse_inst_t *)&pcmd_drvinfo,
+       (cmdline_parse_inst_t *)&pcmd_eeprom,
+       (cmdline_parse_inst_t *)&pcmd_link,
+       (cmdline_parse_inst_t *)&pcmd_macaddr_get,
+       (cmdline_parse_inst_t *)&pcmd_macaddr,
+       (cmdline_parse_inst_t *)&pcmd_mtu,
+       (cmdline_parse_inst_t *)&pcmd_open,
+       (cmdline_parse_inst_t *)&pcmd_pause_noopt,
+       (cmdline_parse_inst_t *)&pcmd_pause,
+       (cmdline_parse_inst_t *)&pcmd_portstats,
+       (cmdline_parse_inst_t *)&pcmd_regs,
+       (cmdline_parse_inst_t *)&pcmd_ringparam,
+       (cmdline_parse_inst_t *)&pcmd_ringparam_set,
+       (cmdline_parse_inst_t *)&pcmd_rxmode,
+       (cmdline_parse_inst_t *)&pcmd_stop,
+       (cmdline_parse_inst_t *)&pcmd_validate,
+       (cmdline_parse_inst_t *)&pcmd_vlan,
+       (cmdline_parse_inst_t *)&pcmd_quit,
+       NULL
+};
+
+
+void ethapp_main(void)
+{
+       struct cmdline *ctx_cmdline;
+
+       ctx_cmdline = cmdline_stdin_new(list_prompt_commands, "EthApp> ");
+       cmdline_interact(ctx_cmdline);
+       cmdline_stdin_exit(ctx_cmdline);
+}
diff --git a/examples/ethtool/ethtool-app/ethapp.h b/examples/ethtool/ethtool-app/ethapp.h
new file mode 100644 (file)
index 0000000..ba438ee
--- /dev/null
@@ -0,0 +1,41 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 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.
+ */
+
+
+void ethapp_main(void);
+void print_stats(void);
+void lock_port(int idx_port);
+void unlock_port(int idx_port);
+void mark_port_inactive(int idx_port);
+void mark_port_active(int idx_port);
+void mark_port_newmac(int idx_port);
diff --git a/examples/ethtool/ethtool-app/main.c b/examples/ethtool/ethtool-app/main.c
new file mode 100644 (file)
index 0000000..e21abcd
--- /dev/null
@@ -0,0 +1,305 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 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 <stdlib.h>
+
+#include <rte_common.h>
+#include <rte_rwlock.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include <rte_ip.h>
+#include <rte_memory.h>
+#include <rte_mempool.h>
+#include <rte_mbuf.h>
+
+#include "ethapp.h"
+
+#define MAX_PORTS RTE_MAX_ETHPORTS
+#define MAX_BURST_LENGTH 32
+#define PORT_RX_QUEUE_SIZE 128
+#define PORT_TX_QUEUE_SIZE 256
+#define PKTPOOL_EXTRA_SIZE 512
+#define PKTPOOL_CACHE 32
+
+
+struct txq_port {
+       uint16_t cnt_unsent;
+       struct rte_mbuf *buf_frames[MAX_BURST_LENGTH];
+};
+
+struct app_port {
+       struct ether_addr mac_addr;
+       struct txq_port txq;
+       rte_spinlock_t lock;
+       int port_active;
+       int port_dirty;
+       int idx_port;
+       struct rte_mempool *pkt_pool;
+};
+
+struct app_config {
+       struct app_port ports[MAX_PORTS];
+       int cnt_ports;
+       int exit_now;
+};
+
+
+struct app_config app_cfg;
+
+
+void lock_port(int idx_port)
+{
+       struct app_port *ptr_port = &app_cfg.ports[idx_port];
+
+       rte_spinlock_lock(&ptr_port->lock);
+}
+
+void unlock_port(int idx_port)
+{
+       struct app_port *ptr_port = &app_cfg.ports[idx_port];
+
+       rte_spinlock_unlock(&ptr_port->lock);
+}
+
+void mark_port_active(int idx_port)
+{
+       struct app_port *ptr_port = &app_cfg.ports[idx_port];
+
+       ptr_port->port_active = 1;
+}
+
+void mark_port_inactive(int idx_port)
+{
+       struct app_port *ptr_port = &app_cfg.ports[idx_port];
+
+       ptr_port->port_active = 0;
+}
+
+void mark_port_newmac(int idx_port)
+{
+       struct app_port *ptr_port = &app_cfg.ports[idx_port];
+
+       ptr_port->port_dirty = 1;
+}
+
+static void setup_ports(struct app_config *app_cfg, int cnt_ports)
+{
+       int idx_port;
+       int size_pktpool;
+       struct rte_eth_conf cfg_port;
+       struct rte_eth_dev_info dev_info;
+       char str_name[16];
+
+       memset(&cfg_port, 0, sizeof(cfg_port));
+       cfg_port.txmode.mq_mode = ETH_MQ_TX_NONE;
+
+       for (idx_port = 0; idx_port < cnt_ports; idx_port++) {
+               struct app_port *ptr_port = &app_cfg->ports[idx_port];
+
+               rte_eth_dev_info_get(idx_port, &dev_info);
+               size_pktpool = dev_info.rx_desc_lim.nb_max +
+                       dev_info.tx_desc_lim.nb_max + PKTPOOL_EXTRA_SIZE;
+
+               snprintf(str_name, 16, "pkt_pool%i", idx_port);
+               ptr_port->pkt_pool = rte_pktmbuf_pool_create(
+                       str_name,
+                       size_pktpool, PKTPOOL_CACHE,
+                       0,
+                       RTE_MBUF_DEFAULT_BUF_SIZE,
+                       rte_socket_id()
+                       );
+               if (ptr_port->pkt_pool == NULL)
+                       rte_exit(EXIT_FAILURE,
+                               "rte_pktmbuf_pool_create failed"
+                               );
+
+               printf("Init port %i..\n", idx_port);
+               ptr_port->port_active = 1;
+               ptr_port->port_dirty = 0;
+               ptr_port->idx_port = idx_port;
+
+               if (rte_eth_dev_configure(idx_port, 1, 1, &cfg_port) < 0)
+                       rte_exit(EXIT_FAILURE,
+                                "rte_eth_dev_configure failed");
+               if (rte_eth_rx_queue_setup(
+                           idx_port, 0, PORT_RX_QUEUE_SIZE,
+                           rte_eth_dev_socket_id(idx_port), NULL,
+                           ptr_port->pkt_pool) < 0)
+                       rte_exit(EXIT_FAILURE,
+                                "rte_eth_rx_queue_setup failed"
+                               );
+               if (rte_eth_tx_queue_setup(
+                           idx_port, 0, PORT_TX_QUEUE_SIZE,
+                           rte_eth_dev_socket_id(idx_port), NULL) < 0)
+                       rte_exit(EXIT_FAILURE,
+                                "rte_eth_tx_queue_setup failed"
+                               );
+               if (rte_eth_dev_start(idx_port) < 0)
+                       rte_exit(EXIT_FAILURE,
+                                "%s:%i: rte_eth_dev_start failed",
+                                __FILE__, __LINE__
+                               );
+               rte_eth_promiscuous_enable(idx_port);
+               rte_eth_macaddr_get(idx_port, &ptr_port->mac_addr);
+               rte_spinlock_init(&ptr_port->lock);
+       }
+}
+
+static void process_frame(struct app_port *ptr_port,
+       struct rte_mbuf *ptr_frame)
+{
+       struct ether_hdr *ptr_mac_hdr;
+
+       ptr_mac_hdr = rte_pktmbuf_mtod(ptr_frame, struct ether_hdr *);
+       ether_addr_copy(&ptr_mac_hdr->s_addr, &ptr_mac_hdr->d_addr);
+       ether_addr_copy(&ptr_port->mac_addr, &ptr_mac_hdr->s_addr);
+}
+
+static int slave_main(__attribute__((unused)) void *ptr_data)
+{
+       struct app_port *ptr_port;
+       struct rte_mbuf *ptr_frame;
+       struct txq_port *txq;
+
+       uint16_t cnt_recv_frames;
+       uint16_t idx_frame;
+       uint16_t cnt_sent;
+       uint16_t idx_port;
+       uint16_t lock_result;
+
+       while (app_cfg.exit_now == 0) {
+               for (idx_port = 0; idx_port < app_cfg.cnt_ports; idx_port++) {
+                       /* Check that port is active and unlocked */
+                       ptr_port = &app_cfg.ports[idx_port];
+                       lock_result = rte_spinlock_trylock(&ptr_port->lock);
+                       if (lock_result == 0)
+                               continue;
+                       if (ptr_port->port_active == 0) {
+                               rte_spinlock_unlock(&ptr_port->lock);
+                               continue;
+                       }
+                       txq = &ptr_port->txq;
+
+                       /* MAC address was updated */
+                       if (ptr_port->port_dirty == 1) {
+                               rte_eth_macaddr_get(ptr_port->idx_port,
+                                       &ptr_port->mac_addr);
+                               ptr_port->port_dirty = 0;
+                       }
+
+                       /* Incoming frames */
+                       cnt_recv_frames = rte_eth_rx_burst(
+                               ptr_port->idx_port, 0,
+                               &txq->buf_frames[txq->cnt_unsent],
+                               RTE_DIM(txq->buf_frames) - txq->cnt_unsent
+                               );
+                       if (cnt_recv_frames > 0) {
+                               for (idx_frame = 0;
+                                       idx_frame < cnt_recv_frames;
+                                       idx_frame++) {
+                                       ptr_frame = txq->buf_frames[
+                                               idx_frame + txq->cnt_unsent];
+                                       process_frame(ptr_port, ptr_frame);
+                               }
+                               txq->cnt_unsent += cnt_recv_frames;
+                       }
+
+                       /* Outgoing frames */
+                       if (txq->cnt_unsent > 0) {
+                               cnt_sent = rte_eth_tx_burst(
+                                       ptr_port->idx_port, 0,
+                                       txq->buf_frames,
+                                       txq->cnt_unsent
+                                       );
+                               /* Shuffle up unsent frame pointers */
+                               for (idx_frame = cnt_sent;
+                                       idx_frame < txq->cnt_unsent;
+                                       idx_frame++)
+                                       txq->buf_frames[idx_frame - cnt_sent] =
+                                               txq->buf_frames[idx_frame];
+                               txq->cnt_unsent -= cnt_sent;
+                       }
+                       rte_spinlock_unlock(&ptr_port->lock);
+               } /* end for( idx_port ) */
+       } /* end for(;;) */
+
+       return 0;
+}
+
+int main(int argc, char **argv)
+{
+       int cnt_args_parsed;
+       uint32_t id_core;
+       uint32_t cnt_ports;
+
+       /* Init runtime enviornment */
+       cnt_args_parsed = rte_eal_init(argc, argv);
+       if (cnt_args_parsed < 0)
+               rte_exit(EXIT_FAILURE, "rte_eal_init(): Failed");
+
+       cnt_ports = rte_eth_dev_count();
+       printf("Number of NICs: %i\n", cnt_ports);
+       if (cnt_ports == 0)
+               rte_exit(EXIT_FAILURE, "No available NIC ports!\n");
+       if (cnt_ports > MAX_PORTS) {
+               printf("Info: Using only %i of %i ports\n",
+                       cnt_ports, MAX_PORTS
+                       );
+               cnt_ports = MAX_PORTS;
+       }
+
+       setup_ports(&app_cfg, cnt_ports);
+
+       app_cfg.exit_now = 0;
+       app_cfg.cnt_ports = cnt_ports;
+
+       if (rte_lcore_count() < 2)
+               rte_exit(EXIT_FAILURE, "No available slave core!\n");
+       /* Assume there is an available slave.. */
+       id_core = rte_lcore_id();
+       id_core = rte_get_next_lcore(id_core, 1, 1);
+       rte_eal_remote_launch(slave_main, NULL, id_core);
+
+       ethapp_main();
+
+       app_cfg.exit_now = 1;
+       RTE_LCORE_FOREACH_SLAVE(id_core) {
+               if (rte_eal_wait_lcore(id_core) < 0)
+                       return -1;
+       }
+
+       return 0;
+}
diff --git a/examples/ethtool/lib/Makefile b/examples/ethtool/lib/Makefile
new file mode 100644 (file)
index 0000000..d7ee955
--- /dev/null
@@ -0,0 +1,57 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2015 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.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overwritten by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifneq ($(CONFIG_RTE_EXEC_ENV),"linuxapp")
+$(error This application can only operate in a linuxapp environment, \
+please change the definition of the RTE_TARGET environment variable)
+endif
+
+# library name
+LIB = librte_ethtool.a
+
+LIBABIVER := 1
+
+# all source are stored in SRC-Y
+SRCS-y := rte_ethtool.c
+
+CFLAGS += -O3
+CFLAGS += $(WERROR_FLAGS)
+
+include $(RTE_SDK)/mk/rte.extlib.mk
diff --git a/examples/ethtool/lib/rte_ethtool.c b/examples/ethtool/lib/rte_ethtool.c
new file mode 100644 (file)
index 0000000..42e05f1
--- /dev/null
@@ -0,0 +1,423 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 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 <string.h>
+#include <stdint.h>
+#include <rte_version.h>
+#include <rte_ethdev.h>
+#include <rte_ether.h>
+#include "rte_ethtool.h"
+
+#define PKTPOOL_SIZE 512
+#define PKTPOOL_CACHE 32
+
+
+int
+rte_ethtool_get_drvinfo(uint8_t port_id, struct ethtool_drvinfo *drvinfo)
+{
+       struct rte_eth_dev_info dev_info;
+       int n;
+
+       if (drvinfo == NULL)
+               return -EINVAL;
+
+       if (!rte_eth_dev_is_valid_port(port_id))
+               return -ENODEV;
+
+       memset(&dev_info, 0, sizeof(dev_info));
+       rte_eth_dev_info_get(port_id, &dev_info);
+
+       snprintf(drvinfo->driver, sizeof(drvinfo->driver), "%s",
+               dev_info.driver_name);
+       snprintf(drvinfo->version, sizeof(drvinfo->version), "%s",
+               rte_version());
+       snprintf(drvinfo->bus_info, sizeof(drvinfo->bus_info),
+               "%04x:%02x:%02x.%x",
+               dev_info.pci_dev->addr.domain, dev_info.pci_dev->addr.bus,
+               dev_info.pci_dev->addr.devid, dev_info.pci_dev->addr.function);
+
+       n = rte_eth_dev_get_reg_length(port_id);
+       if (n > 0)
+               drvinfo->regdump_len = n;
+       else
+               drvinfo->regdump_len = 0;
+
+       n = rte_eth_dev_get_eeprom_length(port_id);
+       if (n > 0)
+               drvinfo->eedump_len = n;
+       else
+               drvinfo->eedump_len = 0;
+
+       drvinfo->n_stats = sizeof(struct rte_eth_stats) / sizeof(uint64_t);
+       drvinfo->testinfo_len = 0;
+
+       return 0;
+}
+
+int
+rte_ethtool_get_regs_len(uint8_t port_id)
+{
+       int count_regs;
+
+       count_regs = rte_eth_dev_get_reg_length(port_id);
+       if (count_regs > 0)
+               return count_regs * sizeof(uint32_t);
+       return count_regs;
+}
+
+int
+rte_ethtool_get_regs(uint8_t port_id, struct ethtool_regs *regs, void *data)
+{
+       struct rte_dev_reg_info reg_info;
+       int status;
+
+       if (regs == NULL || data == NULL)
+               return -EINVAL;
+
+       reg_info.data = data;
+       reg_info.length = 0;
+
+       status = rte_eth_dev_get_reg_info(port_id, &reg_info);
+       if (status)
+               return status;
+       regs->version = reg_info.version;
+
+       return 0;
+}
+
+int
+rte_ethtool_get_link(uint8_t port_id)
+{
+       struct rte_eth_link link;
+
+       if (!rte_eth_dev_is_valid_port(port_id))
+               return -ENODEV;
+       rte_eth_link_get(port_id, &link);
+       return link.link_status;
+}
+
+int
+rte_ethtool_get_eeprom_len(uint8_t port_id)
+{
+       return rte_eth_dev_get_eeprom_length(port_id);
+}
+
+int
+rte_ethtool_get_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom,
+       void *words)
+{
+       struct rte_dev_eeprom_info eeprom_info;
+       int status;
+
+       if (eeprom == NULL || words == NULL)
+               return -EINVAL;
+
+       eeprom_info.offset = eeprom->offset;
+       eeprom_info.length = eeprom->len;
+       eeprom_info.data = words;
+
+       status = rte_eth_dev_get_eeprom(port_id, &eeprom_info);
+       if (status)
+               return status;
+
+       eeprom->magic = eeprom_info.magic;
+
+       return 0;
+}
+
+int
+rte_ethtool_set_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom,
+       void *words)
+{
+       struct rte_dev_eeprom_info eeprom_info;
+       int status;
+
+       if (eeprom == NULL || words == NULL || eeprom->offset >= eeprom->len)
+               return -EINVAL;
+
+       eeprom_info.offset = eeprom->offset;
+       eeprom_info.length = eeprom->len;
+       eeprom_info.data = words;
+
+       status = rte_eth_dev_set_eeprom(port_id, &eeprom_info);
+       if (status)
+               return status;
+
+       eeprom->magic = eeprom_info.magic;
+
+       return 0;
+}
+
+int
+rte_ethtool_get_pauseparam(uint8_t port_id,
+       struct ethtool_pauseparam *pause_param)
+{
+       struct rte_eth_fc_conf fc_conf;
+       int status;
+
+       if (pause_param == NULL)
+               return -EINVAL;
+
+       status = rte_eth_dev_flow_ctrl_get(port_id, &fc_conf);
+       if (status)
+               return status;
+
+       pause_param->tx_pause = 0;
+       pause_param->rx_pause = 0;
+       switch (fc_conf.mode) {
+       case RTE_FC_RX_PAUSE:
+               pause_param->rx_pause = 1;
+               break;
+       case RTE_FC_TX_PAUSE:
+               pause_param->tx_pause = 1;
+               break;
+       case RTE_FC_FULL:
+               pause_param->rx_pause = 1;
+               pause_param->tx_pause = 1;
+       default:
+               /* dummy block to avoid compiler warning */
+               break;
+       }
+       pause_param->autoneg = (uint32_t)fc_conf.autoneg;
+
+       return 0;
+}
+
+int
+rte_ethtool_set_pauseparam(uint8_t port_id,
+       struct ethtool_pauseparam *pause_param)
+{
+       struct rte_eth_fc_conf fc_conf;
+       int status;
+
+       if (pause_param == NULL)
+               return -EINVAL;
+
+       /*
+        * Read device flow control parameter first since
+        * ethtool set_pauseparam op doesn't have all the information.
+        * as defined in struct rte_eth_fc_conf.
+        * This API requires the device to support both
+        * rte_eth_dev_flow_ctrl_get and rte_eth_dev_flow_ctrl_set, otherwise
+        * return -ENOTSUP
+        */
+       status = rte_eth_dev_flow_ctrl_get(port_id, &fc_conf);
+       if (status)
+               return status;
+
+       fc_conf.autoneg = (uint8_t)pause_param->autoneg;
+
+       if (pause_param->tx_pause) {
+               if (pause_param->rx_pause)
+                       fc_conf.mode = RTE_FC_FULL;
+               else
+                       fc_conf.mode = RTE_FC_TX_PAUSE;
+       } else {
+               if (pause_param->rx_pause)
+                       fc_conf.mode = RTE_FC_RX_PAUSE;
+               else
+                       fc_conf.mode = RTE_FC_NONE;
+       }
+
+       status = rte_eth_dev_flow_ctrl_set(port_id, &fc_conf);
+       if (status)
+               return status;
+
+       return 0;
+}
+
+int
+rte_ethtool_net_open(uint8_t port_id)
+{
+       rte_eth_dev_stop(port_id);
+
+       return rte_eth_dev_start(port_id);
+}
+
+int
+rte_ethtool_net_stop(uint8_t port_id)
+{
+       if (!rte_eth_dev_is_valid_port(port_id))
+               return -ENODEV;
+       rte_eth_dev_stop(port_id);
+
+       return 0;
+}
+
+int
+rte_ethtool_net_get_mac_addr(uint8_t port_id, struct ether_addr *addr)
+{
+       if (!rte_eth_dev_is_valid_port(port_id))
+               return -ENODEV;
+       if (addr == NULL)
+               return -EINVAL;
+       rte_eth_macaddr_get(port_id, addr);
+
+       return 0;
+}
+
+int
+rte_ethtool_net_set_mac_addr(uint8_t port_id, struct ether_addr *addr)
+{
+       if (addr == NULL)
+               return -EINVAL;
+       return rte_eth_dev_default_mac_addr_set(port_id, addr);
+}
+
+int
+rte_ethtool_net_validate_addr(uint8_t port_id __rte_unused,
+       struct ether_addr *addr)
+{
+       if (addr == NULL)
+               return -EINVAL;
+       return is_valid_assigned_ether_addr(addr);
+}
+
+int
+rte_ethtool_net_change_mtu(uint8_t port_id, int mtu)
+{
+       if (mtu < 0 || mtu > UINT16_MAX)
+               return -EINVAL;
+       return rte_eth_dev_set_mtu(port_id, (uint16_t)mtu);
+}
+
+int
+rte_ethtool_net_get_stats64(uint8_t port_id, struct rte_eth_stats *stats)
+{
+       if (stats == NULL)
+               return -EINVAL;
+       return rte_eth_stats_get(port_id, stats);
+}
+
+int
+rte_ethtool_net_vlan_rx_add_vid(uint8_t port_id, uint16_t vid)
+{
+       return rte_eth_dev_vlan_filter(port_id, vid, 1);
+}
+
+int
+rte_ethtool_net_vlan_rx_kill_vid(uint8_t port_id, uint16_t vid)
+{
+       return rte_eth_dev_vlan_filter(port_id, vid, 0);
+}
+
+/*
+ * The set_rx_mode provides driver-specific rx mode setting.
+ * This implementation implements rx mode setting based upon
+ * ixgbe/igb drivers. Further improvement is to provide a
+ * callback op field over struct rte_eth_dev::dev_ops so each
+ * driver can register device-specific implementation
+ */
+int
+rte_ethtool_net_set_rx_mode(uint8_t port_id)
+{
+       uint16_t num_vfs;
+       struct rte_eth_dev_info dev_info;
+       uint16_t vf;
+
+       memset(&dev_info, 0, sizeof(dev_info));
+       rte_eth_dev_info_get(port_id, &dev_info);
+       num_vfs = dev_info.max_vfs;
+
+       /* Set VF vf_rx_mode, VF unsupport status is discard */
+       for (vf = 0; vf < num_vfs; vf++)
+               rte_eth_dev_set_vf_rxmode(port_id, vf,
+                       ETH_VMDQ_ACCEPT_UNTAG, 0);
+
+       /* Enable Rx vlan filter, VF unspport status is discard */
+       rte_eth_dev_set_vlan_offload(port_id, ETH_VLAN_FILTER_MASK);
+
+       return 0;
+}
+
+
+int
+rte_ethtool_get_ringparam(uint8_t port_id,
+       struct ethtool_ringparam *ring_param)
+{
+       struct rte_eth_dev_info dev_info;
+       struct rte_eth_rxq_info rx_qinfo;
+       struct rte_eth_txq_info tx_qinfo;
+       int stat;
+
+       if (ring_param == NULL)
+               return -EINVAL;
+
+       rte_eth_dev_info_get(port_id, &dev_info);
+
+       stat = rte_eth_rx_queue_info_get(port_id, 0, &rx_qinfo);
+       if (stat != 0)
+               return stat;
+
+       stat = rte_eth_tx_queue_info_get(port_id, 0, &tx_qinfo);
+       if (stat != 0)
+               return stat;
+
+       memset(ring_param, 0, sizeof(*ring_param));
+       ring_param->rx_pending = rx_qinfo.nb_desc;
+       ring_param->rx_max_pending = dev_info.rx_desc_lim.nb_max;
+       ring_param->tx_pending = tx_qinfo.nb_desc;
+       ring_param->tx_max_pending = dev_info.tx_desc_lim.nb_max;
+
+       return 0;
+}
+
+
+int
+rte_ethtool_set_ringparam(uint8_t port_id,
+       struct ethtool_ringparam *ring_param)
+{
+       struct rte_eth_rxq_info rx_qinfo;
+       int stat;
+
+       if (ring_param == NULL)
+               return -EINVAL;
+
+       stat = rte_eth_rx_queue_info_get(port_id, 0, &rx_qinfo);
+       if (stat != 0)
+               return stat;
+
+       rte_eth_dev_stop(port_id);
+
+       stat = rte_eth_tx_queue_setup(port_id, 0, ring_param->tx_pending,
+               rte_socket_id(), NULL);
+       if (stat != 0)
+               return stat;
+
+       stat = rte_eth_rx_queue_setup(port_id, 0, ring_param->rx_pending,
+               rte_socket_id(), NULL, rx_qinfo.mp);
+       if (stat != 0)
+               return stat;
+
+       return rte_eth_dev_start(port_id);
+}
diff --git a/examples/ethtool/lib/rte_ethtool.h b/examples/ethtool/lib/rte_ethtool.h
new file mode 100644 (file)
index 0000000..2e79d45
--- /dev/null
@@ -0,0 +1,410 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2015 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.
+ */
+
+#ifndef _RTE_ETHTOOL_H_
+#define _RTE_ETHTOOL_H_
+
+/*
+ * This new interface is designed to provide a user-space shim layer for
+ * Ethtool and Netdevice op API.
+ *
+ * rte_ethtool_get_driver:          ethtool_ops::get_driverinfo
+ * rte_ethtool_get_link:            ethtool_ops::get_link
+ * rte_ethtool_get_regs_len:        ethtool_ops::get_regs_len
+ * rte_ethtool_get_regs:            ethtool_ops::get_regs
+ * rte_ethtool_get_eeprom_len:      ethtool_ops::get_eeprom_len
+ * rte_ethtool_get_eeprom:          ethtool_ops::get_eeprom
+ * rte_ethtool_set_eeprom:          ethtool_ops::set_eeprom
+ * rte_ethtool_get_pauseparam:      ethtool_ops::get_pauseparam
+ * rte_ethtool_set_pauseparam:      ethtool_ops::set_pauseparam
+ *
+ * rte_ethtool_net_open:            net_device_ops::ndo_open
+ * rte_ethtool_net_stop:            net_device_ops::ndo_stop
+ * rte_ethtool_net_set_mac_addr:    net_device_ops::ndo_set_mac_address
+ * rte_ethtool_net_validate_addr:   net_device_ops::ndo_validate_addr
+ * rte_ethtool_net_change_mtu:      net_device_ops::rte_net_change_mtu
+ * rte_ethtool_net_get_stats64:     net_device_ops::ndo_get_stats64
+ * rte_ethtool_net_vlan_rx_add_vid  net_device_ops::ndo_vlan_rx_add_vid
+ * rte_ethtool_net_vlan_rx_kill_vid net_device_ops::ndo_vlan_rx_kill_vid
+ * rte_ethtool_net_set_rx_mode      net_device_ops::ndo_set_rx_mode
+ *
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <rte_ethdev.h>
+#include <linux/ethtool.h>
+
+/**
+ * Retrieve the Ethernet device driver information according to
+ * attributes described by ethtool data structure, ethtool_drvinfo.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param drvinfo
+ *   A pointer to get driver information
+ * @return
+ *   - (0) if successful.
+ *   - (-ENODEV) if *port_id* invalid.
+ */
+int rte_ethtool_get_drvinfo(uint8_t port_id, struct ethtool_drvinfo *drvinfo);
+
+/**
+ * Retrieve the Ethernet device register length in bytes.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @return
+ *   - (> 0) # of device registers (in bytes) available for dump
+ *   - (0) no registers available for dump.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_get_regs_len(uint8_t port_id);
+
+/**
+ * Retrieve the Ethernet device register information according to
+ * attributes described by ethtool data structure, ethtool_regs
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param reg
+ *   A pointer to ethtool_regs that has register information
+ * @param data
+ *   A pointer to a buffer that is used to retrieve device register content
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_get_regs(uint8_t port_id, struct ethtool_regs *regs,
+                           void *data);
+
+/**
+ * Retrieve the Ethernet device link status
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @return
+ *   - (1) if link up.
+ *   - (0) if link down.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-EINVAL) if parameters invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_get_link(uint8_t port_id);
+
+/**
+ * Retrieve the Ethernet device EEPROM size
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @return
+ *      - (> 0) device EEPROM size in bytes
+ *   - (0) device has NO EEPROM
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_get_eeprom_len(uint8_t port_id);
+
+/**
+ * Retrieve EEPROM content based upon eeprom range described in ethtool
+ * data structure, ethtool_eeprom
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param eeprom
+ *      The pointer of ethtool_eeprom that provides eeprom range
+ * @param words
+ *      A buffer that holds data read from eeprom
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_get_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom,
+                             void *words);
+
+/**
+ * Setting EEPROM content based upon eeprom range described in ethtool
+ * data structure, ethtool_eeprom
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param eeprom
+ *      The pointer of ethtool_eeprom that provides eeprom range
+ * @param words
+ *      A buffer that holds data to be written into eeprom
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-EINVAL) if parameters invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_set_eeprom(uint8_t port_id, struct ethtool_eeprom *eeprom,
+                             void *words);
+
+/**
+ * Retrieve the Ethernet device pause frame configuration according to
+ * parameter attributes desribed by ethtool data structure,
+ * ethtool_pauseparam.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param pause_param
+ *      The pointer of ethtool_coalesce that gets pause frame
+ *      configuration parameters
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-EINVAL) if parameters invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_get_pauseparam(uint8_t port_id,
+                                  struct ethtool_pauseparam *pause_param);
+
+/**
+ * Setting the Ethernet device pause frame configuration according to
+ * parameter attributes desribed by ethtool data structure, ethtool_pauseparam.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param pause_param
+ *      The pointer of ethtool_coalesce that gets ring configuration parameters
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-EINVAL) if parameters invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_set_pauseparam(uint8_t port_id,
+                                  struct ethtool_pauseparam *param);
+
+/**
+ * Start the Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_open(uint8_t port_id);
+
+/**
+ * Stop the Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENODEV) if *port_id* invalid.
+ */
+int rte_ethtool_net_stop(uint8_t port_id);
+
+/**
+ * Get the Ethernet device MAC address.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param addr
+ *      MAC address of the Ethernet device.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENODEV) if *port_id* invalid.
+ */
+int rte_ethtool_net_get_mac_addr(uint8_t port_id, struct ether_addr *addr);
+
+/**
+ * Setting the Ethernet device MAC address.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param addr
+ *      The new MAC addr.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-EINVAL) if parameters invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_set_mac_addr(uint8_t port_id, struct ether_addr *addr);
+
+/**
+ * Validate if the provided MAC address is valid unicast address
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param addr
+ *      A pointer to a buffer (6-byte, 48bit) for the target MAC address
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-EINVAL) if parameters invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_validate_addr(uint8_t port_id, struct ether_addr *addr);
+
+/**
+ * Setting the Ethernet device maximum Tx unit.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param mtu
+ *      New MTU
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-EINVAL) if parameters invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_change_mtu(uint8_t port_id, int mtu);
+
+/**
+ * Retrieve the Ethernet device traffic statistics
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param stats
+ *      A pointer to struct rte_eth_stats for statistics parameters
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-EINVAL) if parameters invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_get_stats64(uint8_t port_id, struct rte_eth_stats *stats);
+
+/**
+ * Update the Ethernet device VLAN filter with new vid
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param vid
+ *      A new VLAN id
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_vlan_rx_add_vid(uint8_t port_id, uint16_t vid);
+
+/**
+ * Remove VLAN id from Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param vid
+ *      A new VLAN id
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_vlan_rx_kill_vid(uint8_t port_id, uint16_t vid);
+
+/**
+ * Setting the Ethernet device rx mode.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ */
+int rte_ethtool_net_set_rx_mode(uint8_t port_id);
+
+/**
+ * Getting ring paramaters for Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param ring_param
+ *   Pointer to struct ethrool_ringparam to receive parameters.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ * @note
+ *   Only the tx_pending and rx_pending fields of struct ethtool_ringparam
+ *   are used, and the function only gets parameters for queue 0.
+ */
+int rte_ethtool_get_ringparam(uint8_t port_id,
+       struct ethtool_ringparam *ring_param);
+
+/**
+ * Setting ring paramaters for Ethernet device.
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param ring_param
+ *   Pointer to struct ethrool_ringparam with parameters to set.
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if hardware doesn't support.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - others depends on the specific operations implementation.
+ * @note
+ *   Only the tx_pending and rx_pending fields of struct ethtool_ringparam
+ *   are used, and the function only sets parameters for queue 0.
+ */
+int rte_ethtool_set_ringparam(uint8_t port_id,
+       struct ethtool_ringparam *ring_param);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_ETHTOOL_H_ */