From: Mykola Kostenok <mko-plv@napatech.com>
To: dev@dpdk.org, mko-plv@napatech.com, thomas@monjalon.net
Cc: ckm@napatech.com
Subject: [PATCH v11 8/8] net/ntnic: adds socket connection to PMD
Date: Thu, 31 Aug 2023 14:23:23 +0200 [thread overview]
Message-ID: <20230831122323.967504-8-mko-plv@napatech.com> (raw)
In-Reply-To: <20230831122323.967504-1-mko-plv@napatech.com>
From: Christian Koue Muf <ckm@napatech.com>
The socket connection is used by Napatech's tools for monitoring
and rte_flow programming from other processes.
Signed-off-by: Christian Koue Muf <ckm@napatech.com>
Reviewed-by: Mykola Kostenok <mko-plv@napatech.com>
---
v2:
* Fixed WARNING:TYPO_SPELLING
v4:
* Fixed Alpine build
v8:
* Fixed token parser constant length.
v10:
* Fix uninitialized variables and build warnings.
---
drivers/net/ntnic/meson.build | 24 +
.../ntconnect/include/ntconn_mod_helper.h | 97 ++
.../net/ntnic/ntconnect/include/ntconnect.h | 96 ++
.../ntnic/ntconnect/include/ntconnect_api.h | 87 ++
.../ntconnect/include/ntconnect_api_adapter.h | 221 +++
.../ntconnect/include/ntconnect_api_flow.h | 168 +++
.../ntconnect/include/ntconnect_api_meter.h | 89 ++
.../include/ntconnect_api_statistic.h | 173 +++
.../ntconnect/include/ntconnect_api_test.h | 18 +
drivers/net/ntnic/ntconnect/ntconn_server.c | 97 ++
drivers/net/ntnic/ntconnect/ntconnect.c | 641 ++++++++
.../ntnic/ntconnect_modules/ntconn_adapter.c | 775 ++++++++++
.../net/ntnic/ntconnect_modules/ntconn_flow.c | 1312 +++++++++++++++++
.../ntnic/ntconnect_modules/ntconn_meter.c | 517 +++++++
.../ntnic/ntconnect_modules/ntconn_modules.h | 19 +
.../net/ntnic/ntconnect_modules/ntconn_stat.c | 877 +++++++++++
.../net/ntnic/ntconnect_modules/ntconn_test.c | 146 ++
17 files changed, 5357 insertions(+)
create mode 100644 drivers/net/ntnic/ntconnect/include/ntconn_mod_helper.h
create mode 100644 drivers/net/ntnic/ntconnect/include/ntconnect.h
create mode 100644 drivers/net/ntnic/ntconnect/include/ntconnect_api.h
create mode 100644 drivers/net/ntnic/ntconnect/include/ntconnect_api_adapter.h
create mode 100644 drivers/net/ntnic/ntconnect/include/ntconnect_api_flow.h
create mode 100644 drivers/net/ntnic/ntconnect/include/ntconnect_api_meter.h
create mode 100644 drivers/net/ntnic/ntconnect/include/ntconnect_api_statistic.h
create mode 100644 drivers/net/ntnic/ntconnect/include/ntconnect_api_test.h
create mode 100644 drivers/net/ntnic/ntconnect/ntconn_server.c
create mode 100644 drivers/net/ntnic/ntconnect/ntconnect.c
create mode 100644 drivers/net/ntnic/ntconnect_modules/ntconn_adapter.c
create mode 100644 drivers/net/ntnic/ntconnect_modules/ntconn_flow.c
create mode 100644 drivers/net/ntnic/ntconnect_modules/ntconn_meter.c
create mode 100644 drivers/net/ntnic/ntconnect_modules/ntconn_modules.h
create mode 100644 drivers/net/ntnic/ntconnect_modules/ntconn_stat.c
create mode 100644 drivers/net/ntnic/ntconnect_modules/ntconn_test.c
diff --git a/drivers/net/ntnic/meson.build b/drivers/net/ntnic/meson.build
index f7454ffb79..ee8cf982ef 100644
--- a/drivers/net/ntnic/meson.build
+++ b/drivers/net/ntnic/meson.build
@@ -7,6 +7,22 @@ if not is_linux or not dpdk_conf.has('RTE_ARCH_X86_64')
subdir_done()
endif
+# config object
+ntnic_conf = configuration_data()
+
+# transfer options into config object
+ntnic_conf.set('NT_TOOLS', true)
+
+# check option 'debug' (boolean flag derived from meson buildtype)
+if get_option('debug')
+ cflags += '-DDEBUG'
+endif
+
+# check nt_tools build option
+if ntnic_conf.get('NT_TOOLS')
+ cflags += '-DNT_TOOLS'
+endif
+
# includes
includes = [
include_directories('.'),
@@ -20,6 +36,7 @@ includes = [
include_directories('nthw/supported'),
include_directories('nthw/flow_api'),
include_directories('nthw/flow_filter'),
+ include_directories('ntconnect/include'),
include_directories('sensors'),
include_directories('sensors/avr_sensors'),
include_directories('sensors/board_sensors'),
@@ -41,6 +58,13 @@ sources = files(
'nim/nt_link_speed.c',
'nim/qsfp_sensors.c',
'nim/sfp_sensors.c',
+ 'ntconnect/ntconn_server.c',
+ 'ntconnect/ntconnect.c',
+ 'ntconnect_modules/ntconn_adapter.c',
+ 'ntconnect_modules/ntconn_flow.c',
+ 'ntconnect_modules/ntconn_meter.c',
+ 'ntconnect_modules/ntconn_stat.c',
+ 'ntconnect_modules/ntconn_test.c',
'nthw/core/nthw_clock_profiles.c',
'nthw/core/nthw_fpga.c',
'nthw/core/nthw_fpga_nt200a0x.c',
diff --git a/drivers/net/ntnic/ntconnect/include/ntconn_mod_helper.h b/drivers/net/ntnic/ntconnect/include/ntconn_mod_helper.h
new file mode 100644
index 0000000000..f55c4141cc
--- /dev/null
+++ b/drivers/net/ntnic/ntconnect/include/ntconn_mod_helper.h
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef _NTCONN_MOD_HELPER_H_
+#define _NTCONN_MOD_HELPER_H_
+
+#include "ntconnect.h"
+
+/*
+ * Module parameter function call tree structures
+ */
+struct func_s {
+ const char *param;
+ struct func_s *sub_funcs;
+ int (*func)(void *hdl, int client_fd, struct ntconn_header_s *hdr,
+ char **data, int *len);
+};
+
+static inline int ntconn_error(char **data, int *len, const char *module,
+ enum ntconn_err_e err_code)
+{
+ *len = 0;
+ if (data) {
+ const ntconn_err_t *ntcerr = get_ntconn_error(err_code);
+ *data = malloc(4 + strlen(module) + 1 +
+ strlen(ntcerr->err_text) + 1);
+ if (*data) {
+ sprintf(*data, "----%s:%s", module, ntcerr->err_text);
+ *len = strlen(*data) + 1; /* add 0 - terminator */
+ *(uint32_t *)*data = (uint32_t)ntcerr->err_code;
+ }
+ }
+ return REQUEST_ERR;
+}
+
+static inline int ntconn_reply_status(char **data, int *len,
+ enum ntconn_reply_code_e code)
+{
+ *len = 0;
+ if (data) {
+ *data = malloc(sizeof(uint32_t));
+ if (*data) {
+ *len = sizeof(uint32_t);
+ *(uint32_t *)*data = (uint32_t)code;
+ }
+ }
+ return REQUEST_OK;
+}
+
+static inline int execute_function(const char *module, void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char *function,
+ struct func_s *func_list, char **data,
+ int *len, int recur_depth)
+{
+ char *tok = strtok(function, ",");
+
+ if (!tok) {
+ if (recur_depth == 0)
+ return ntconn_error(data, len, module,
+ NTCONN_ERR_CODE_MISSING_INVALID_PARAM);
+ else
+ return ntconn_error(data, len, module,
+ NTCONN_ERR_CODE_FUNCTION_PARAM_INCOMPLETE);
+ }
+
+ hdr->len -= strlen(tok) + 1;
+ char *sub_funcs = function + strlen(tok) + 1;
+ int idx = 0;
+
+ while (func_list[idx].param) {
+ if (strcmp(func_list[idx].param, tok) == 0) {
+ /* hit */
+ if (func_list[idx].sub_funcs) {
+ return execute_function(module, hdl, client_id,
+ hdr, sub_funcs,
+ func_list[idx].sub_funcs,
+ data, len,
+ ++recur_depth);
+ } else if (func_list[idx].func) {
+ /* commands/parameters for function in text, zero-terminated */
+ *data = sub_funcs;
+ return func_list[idx].func(hdl, client_id, hdr,
+ data, len);
+ } else {
+ return ntconn_error(data, len, module,
+ NTCONN_ERR_CODE_INTERNAL_FUNC_ERROR);
+ }
+ }
+ idx++;
+ }
+ /* no hits */
+ return ntconn_error(data, len, module,
+ NTCONN_ERR_CODE_FUNC_PARAM_NOT_RECOGNIZED);
+}
+
+#endif /* _NTCONN_MOD_HELPER_H_ */
diff --git a/drivers/net/ntnic/ntconnect/include/ntconnect.h b/drivers/net/ntnic/ntconnect/include/ntconnect.h
new file mode 100644
index 0000000000..9dcf2ec0a1
--- /dev/null
+++ b/drivers/net/ntnic/ntconnect/include/ntconnect.h
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef _NTCONNECT_H_
+#define _NTCONNECT_H_
+
+#include <rte_pci.h>
+#include <sched.h>
+#include <stdint.h>
+
+#include "ntconnect_api.h"
+
+#define REQUEST_OK 0
+#define REQUEST_ERR -1
+
+typedef struct ntconn_api_s {
+ /*
+ * Name specifying this module. This name is used in the request string
+ */
+ const char *module;
+ /*
+ * The version of this module integration
+ */
+ uint32_t version_major;
+ uint32_t version_minor;
+ /*
+ * The request function:
+ * hdl : pointer to the context of this instance.
+ * client_id : identifying the client. To be able to manage client specific data/state.
+ * function : pointer to the remainder of the request command (Layer 3). May be modified.
+ * an example: <pci_id>;adapter;get,interface,port0,link_speed function will
+ * then be 'get,interface,port0,link_speed'.
+ * hdr : header for length of command string and length of binary blop.
+ * The command string will start at "*data" and will have the length hdr->len.
+ * The binary blob will start at "&(*data)[hdr->len]" and will have the length
+ * hdr->blob_len.
+ * data : pointer to the resulting data. Typically this will be allocated.
+ * len : length of the data in the reply.
+ *
+ * return : REQUEST_OK on success, REQUEST_ERR on failure. On failure, the data and len
+ * can contain an describing error text
+ */
+ int (*request)(void *hdl, int client_id, struct ntconn_header_s *hdr,
+ char *function, char **data, int *len);
+ /*
+ * After each request call, and when 'len' returns > 0, this function is called
+ * after sending reply to client.
+ * hdl : pointer to the context of this instance.
+ * data : the data pointer returned in the request function
+ */
+ void (*free_data)(void *hdl, char *data);
+ /*
+ * Clean up of client specific data allocations. Called when client disconnects from server
+ * hdl : pointer to the context of this instance.
+ * client_id : identifying the client.
+ */
+ void (*client_cleanup)(void *hdl, int client_id);
+} ntconnapi_t;
+
+/*
+ * ntconn error
+ */
+typedef struct ntconn_err_s {
+ uint32_t err_code;
+ const char *err_text;
+} ntconn_err_t;
+
+const ntconn_err_t *get_ntconn_error(enum ntconn_err_e err_code);
+
+typedef struct ntconn_mod_s {
+ void *hdl;
+ struct pci_id_s addr;
+ const ntconnapi_t *op;
+
+ pthread_mutex_t mutex;
+ struct ntconn_mod_s *next;
+} ntconn_mod_t;
+
+struct ntconn_server_s {
+ int serv_fd;
+ int running;
+ /*
+ * list of different pci_ids registered aka SmartNICs
+ */
+ struct pci_id_s pci_id_list[MAX_PCI_IDS]; /* 0 - terminates */
+ cpu_set_t cpuset;
+};
+
+int ntconn_server_register(void *server);
+
+int register_ntconn_mod(const struct rte_pci_addr *addr, void *hdl,
+ const ntconnapi_t *op);
+int ntconnect_init(const char *sockname, cpu_set_t cpuset);
+
+#endif /* _NTCONNECT_H_ */
diff --git a/drivers/net/ntnic/ntconnect/include/ntconnect_api.h b/drivers/net/ntnic/ntconnect/include/ntconnect_api.h
new file mode 100644
index 0000000000..14668bf2ee
--- /dev/null
+++ b/drivers/net/ntnic/ntconnect/include/ntconnect_api.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef _NTCONNECT_API_H_
+#define _NTCONNECT_API_H_
+
+#include "stdint.h"
+/*
+ * NtConnect API
+ */
+
+#define NTCONNECT_SOCKET "/var/run/ntconnect/ntconnect.sock"
+
+enum ntconn_err_e {
+ NTCONN_ERR_CODE_NONE = 0U,
+ NTCONN_ERR_CODE_INTERNAL_ERROR,
+ NTCONN_ERR_CODE_INVALID_REQUEST,
+ NTCONN_ERR_CODE_INTERNAL_REPLY_ERROR,
+ NTCONN_ERR_CODE_NO_DATA,
+ NTCONN_ERR_CODE_NOT_YET_IMPLEMENTED,
+ NTCONN_ERR_CODE_MISSING_INVALID_PARAM,
+ NTCONN_ERR_CODE_FUNCTION_PARAM_INCOMPLETE,
+ NTCONN_ERR_CODE_INTERNAL_FUNC_ERROR,
+ NTCONN_ERR_CODE_FUNC_PARAM_NOT_RECOGNIZED,
+};
+
+enum ntconn_reply_code_e {
+ NTCONN_ADAPTER_ERR_PORT_STATE_FAIL = 0U,
+ NTCONN_ADAPTER_ERR_WRONG_LINK_STATE,
+ NTCONN_ADAPTER_ERR_TX_POWER_FAIL,
+};
+
+enum {
+ NTCONN_TAG_NONE,
+ NTCONN_TAG_REQUEST,
+ NTCONN_TAG_REPLY,
+ NTCONN_TAG_ERROR
+};
+
+#define MESSAGE_BUFFER 256
+#define MAX_ERR_MESSAGE_LENGTH 256
+
+struct reply_err_s {
+ enum ntconn_err_e err_code;
+ char msg[MAX_ERR_MESSAGE_LENGTH];
+};
+
+#define NTCMOD_HDR_LEN sizeof(struct ntconn_header_s)
+struct ntconn_header_s {
+ uint16_t tag;
+ uint16_t len;
+ uint32_t blob_len;
+};
+
+struct pci_id_s {
+ union {
+ uint64_t pci_id;
+ struct {
+ uint32_t domain;
+ uint8_t bus;
+ uint8_t devid;
+ uint8_t function;
+ uint8_t pad;
+ };
+ };
+};
+
+#define VERSION_HI(version) ((unsigned int)((version) >> 32))
+#define VERSION_LO(version) ((unsigned int)((version) & 0xffffffff))
+
+/*
+ * Binary interface description for ntconnect module replies
+ */
+
+/*
+ * server get,nic_pci_ids
+ */
+#define MAX_PCI_IDS 16
+#define NICS_PCI_ID_LEN 12
+
+struct ntc_nic_pci_ids_s {
+ char nic_pci_id[MAX_PCI_IDS][NICS_PCI_ID_LEN + 1];
+ int num_nics;
+};
+
+#endif /* _NTCONNECT_API_H_ */
diff --git a/drivers/net/ntnic/ntconnect/include/ntconnect_api_adapter.h b/drivers/net/ntnic/ntconnect/include/ntconnect_api_adapter.h
new file mode 100644
index 0000000000..affe905027
--- /dev/null
+++ b/drivers/net/ntnic/ntconnect/include/ntconnect_api_adapter.h
@@ -0,0 +1,221 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef _NTCONNECT_API_ADAPTER_H_
+#define _NTCONNECT_API_ADAPTER_H_
+
+/*
+ * adapter get,interfaces
+ */
+enum port_speed {
+ PORT_LINK_SPEED_UNKNOWN,
+ PORT_LINK_SPEED_NONE_REPORTED,
+ PORT_LINK_SPEED_10M,
+ PORT_LINK_SPEED_100M,
+ PORT_LINK_SPEED_1G,
+ PORT_LINK_SPEED_10G,
+ PORT_LINK_SPEED_25G,
+ PORT_LINK_SPEED_40G,
+ PORT_LINK_SPEED_50G,
+ PORT_LINK_SPEED_100G,
+};
+
+enum port_states {
+ PORT_STATE_DISABLED,
+ PORT_STATE_NIM_PRESENT,
+ PORT_STATE_NIM_ABSENT,
+ PORT_STATE_VIRTUAL_UNATTACHED,
+ PORT_STATE_VIRTUAL_SPLIT,
+ PORT_STATE_VIRTUAL_PACKED,
+ PORT_STATE_VIRTUAL_RELAY,
+};
+
+enum port_link { PORT_LINK_UNKNOWN, PORT_LINK_UP, PORT_LINK_DOWN };
+
+enum port_type {
+ PORT_TYPE_PHY_NORMAL, /* Normal phy port (no LAG) */
+ /* Link aggregated phy port in active/active LAG configuration */
+ PORT_TYPE_PHY_LAG_ACTIVE_ACTIVE,
+ PORT_TYPE_PHY_LAG_PRIMARY, /* Primary phy port in active/backup LAG configuration */
+ PORT_TYPE_PHY_LAG_BACKUP, /* Backup phy port in active/backup LAG configuration */
+ PORT_TYPE_VIRT,
+ PORT_TYPE_LAST
+};
+
+enum nim_identifier_e {
+ NIM_UNKNOWN = 0x00, /* Nim type is unknown */
+ NIM_GBIC = 0x01, /* Nim type = GBIC */
+ NIM_FIXED = 0x02, /* Nim type = FIXED */
+ NIM_SFP_SFP_PLUS = 0x03, /* Nim type = SFP/SFP+ */
+ NIM_300_PIN_XBI = 0x04, /* Nim type = 300 pin XBI */
+ NIM_XEN_PAK = 0x05, /* Nim type = XEN-PAK */
+ NIM_XFP = 0x06, /* Nim type = XFP */
+ NIM_XFF = 0x07, /* Nim type = XFF */
+ NIM_XFP_E = 0x08, /* Nim type = XFP-E */
+ NIM_XPAK = 0x09, /* Nim type = XPAK */
+ NIM_X2 = 0x0A, /* Nim type = X2 */
+ NIM_DWDM = 0x0B, /* Nim type = DWDM */
+ NIM_QSFP = 0x0C, /* Nim type = QSFP */
+ NIM_QSFP_PLUS = 0x0D, /* Nim type = QSFP+ */
+ NIM_QSFP28 = 0x11, /* Nim type = QSFP28 */
+ NIM_CFP4 = 0x12, /* Nim type = CFP4 */
+};
+
+/*
+ * Port types
+ */
+enum port_type_e {
+ PORT_TYPE_NOT_AVAILABLE =
+ 0, /* The NIM/port type is not available (unknown) */
+ PORT_TYPE_NOT_RECOGNISED, /* The NIM/port type not recognized */
+ PORT_TYPE_RJ45, /* RJ45 type */
+ PORT_TYPE_SFP_NOT_PRESENT, /* SFP type but slot is empty */
+ PORT_TYPE_SFP_SX, /* SFP SX */
+ PORT_TYPE_SFP_SX_DD, /* SFP SX digital diagnostic */
+ PORT_TYPE_SFP_LX, /* SFP LX */
+ PORT_TYPE_SFP_LX_DD, /* SFP LX digital diagnostic */
+ PORT_TYPE_SFP_ZX, /* SFP ZX */
+ PORT_TYPE_SFP_ZX_DD, /* SFP ZX digital diagnostic */
+ PORT_TYPE_SFP_CU, /* SFP copper */
+ PORT_TYPE_SFP_CU_DD, /* SFP copper digital diagnostic */
+ PORT_TYPE_SFP_NOT_RECOGNISED, /* SFP unknown */
+ PORT_TYPE_XFP, /* XFP */
+ PORT_TYPE_XPAK, /* XPAK */
+ PORT_TYPE_SFP_CU_TRI_SPEED, /* SFP copper tri-speed */
+ PORT_TYPE_SFP_CU_TRI_SPEED_DD, /* SFP copper tri-speed digital diagnostic */
+ PORT_TYPE_SFP_PLUS, /* SFP+ type */
+ PORT_TYPE_SFP_PLUS_NOT_PRESENT, /* SFP+ type but slot is empty */
+ PORT_TYPE_XFP_NOT_PRESENT, /* XFP type but slot is empty */
+ PORT_TYPE_QSFP_PLUS_NOT_PRESENT, /* QSFP type but slot is empty */
+ PORT_TYPE_QSFP_PLUS, /* QSFP type */
+ PORT_TYPE_SFP_PLUS_PASSIVE_DAC, /* SFP+ Passive DAC */
+ PORT_TYPE_SFP_PLUS_ACTIVE_DAC, /* SFP+ Active DAC */
+ PORT_TYPE_CFP4, /* CFP4 type */
+ PORT_TYPE_CFP4_LR4 = PORT_TYPE_CFP4, /* CFP4 100G, LR4 type */
+ PORT_TYPE_CFP4_NOT_PRESENT, /* CFP4 type but slot is empty */
+ PORT_TYPE_INITIALIZE, /* The port type is not fully established yet */
+ PORT_TYPE_NIM_NOT_PRESENT, /* Generic "Not present" */
+ PORT_TYPE_HCB, /* Test mode: Host Compliance Board */
+ PORT_TYPE_NOT_SUPPORTED, /* The NIM type is not supported in this context */
+ PORT_TYPE_SFP_PLUS_DUAL_RATE, /* SFP+ supports 1G/10G */
+ PORT_TYPE_CFP4_SR4, /* CFP4 100G, SR4 type */
+ PORT_TYPE_QSFP28_NOT_PRESENT, /* QSFP28 type but slot is empty */
+ PORT_TYPE_QSFP28, /* QSFP28 type */
+ PORT_TYPE_QSFP28_SR4, /* QSFP28-SR4 type */
+ PORT_TYPE_QSFP28_LR4, /* QSFP28-LR4 type */
+ /* Deprecated. The port type should not mention speed eg 4x10 or 1x40 */
+ PORT_TYPE_QSFP_PLUS_4X10,
+ /* Deprecated. The port type should not mention speed eg 4x10 or 1x40 */
+ PORT_TYPE_QSFP_PASSIVE_DAC_4X10,
+ PORT_TYPE_QSFP_PASSIVE_DAC =
+ PORT_TYPE_QSFP_PASSIVE_DAC_4X10, /* QSFP passive DAC type */
+ /* Deprecated. The port type should not mention speed eg 4x10 or 1x40 */
+ PORT_TYPE_QSFP_ACTIVE_DAC_4X10,
+ PORT_TYPE_QSFP_ACTIVE_DAC =
+ PORT_TYPE_QSFP_ACTIVE_DAC_4X10, /* QSFP active DAC type */
+ PORT_TYPE_SFP_28, /* SFP28 type */
+ PORT_TYPE_SFP_28_SR, /* SFP28-SR type */
+ PORT_TYPE_SFP_28_LR, /* SFP28-LR type */
+ PORT_TYPE_SFP_28_CR_CA_L, /* SFP28-CR-CA-L type */
+ PORT_TYPE_SFP_28_CR_CA_S, /* SFP28-CR-CA-S type */
+ PORT_TYPE_SFP_28_CR_CA_N, /* SFP28-CR-CA-N type */
+ PORT_TYPE_QSFP28_CR_CA_L, /* QSFP28-CR-CA-L type */
+ PORT_TYPE_QSFP28_CR_CA_S, /* QSFP28-CR-CA-S type */
+ PORT_TYPE_QSFP28_CR_CA_N, /* QSFP28-CR-CA-N type */
+ PORT_TYPE_SFP_28_SR_DR, /* SFP28-SR-DR type */
+ PORT_TYPE_SFP_28_LR_DR, /* SFP28-LR-DR type */
+ PORT_TYPE_SFP_FX, /* SFP FX */
+ PORT_TYPE_SFP_PLUS_CU, /* SFP+ CU type */
+ PORT_TYPE_QSFP28_FR, /* QSFP28-FR type. Uses PAM4 modulation on one lane only */
+ PORT_TYPE_QSFP28_DR, /* QSFP28-DR type. Uses PAM4 modulation on one lane only */
+ PORT_TYPE_QSFP28_LR, /* QSFP28-LR type. Uses PAM4 modulation on one lane only */
+};
+
+struct mac_addr_s {
+ uint8_t addr_b[6];
+};
+
+struct nim_link_length_s {
+ /* NIM link length (in meters) supported SM (9um). A value of 0xFFFF indicates that the
+ * length is >= 65535 m
+ */
+ uint16_t sm;
+ uint16_t ebw; /* NIM link length (in meters) supported EBW (50um) */
+ uint16_t mm50; /* NIM link length (in meters) supported MM (50um) */
+ uint16_t mm62; /* NIM link length (in meters) supported MM (62.5um) */
+ uint16_t copper; /* NIM link length (in meters) supported copper */
+};
+
+struct nim_data_s {
+ uint8_t nim_id;
+ uint8_t port_type;
+ char vendor_name[17];
+ char prod_no[17];
+ char serial_no[17];
+ char date[9];
+ char rev[5];
+ uint8_t pwr_level_req;
+ uint8_t pwr_level_cur;
+ struct nim_link_length_s link_length;
+};
+
+struct sensor {
+ uint8_t sign;
+ uint8_t type;
+ uint32_t current_value;
+ uint32_t min_value;
+ uint32_t max_value;
+ char name[50];
+};
+
+struct ntc_sensors_s {
+ uint16_t adapter_sensors_cnt;
+ uint16_t ports_cnt;
+ uint16_t nim_sensors_cnt[8];
+ char adapter_name[24];
+};
+
+#define MAX_RSS_QUEUES 128
+
+enum queue_dir_e { QUEUE_INPUT, QUEUE_OUTPUT };
+
+struct queue_s {
+ enum queue_dir_e dir;
+ int idx;
+};
+
+struct ntc_interface_s {
+ uint8_t port_id;
+ enum port_type type;
+ enum port_link link;
+ enum port_states port_state;
+ enum port_speed port_speed;
+ struct pci_id_s pci_id;
+ struct mac_addr_s mac;
+ struct nim_data_s nim_data;
+ uint16_t mtu;
+ /* attached queues */
+ struct {
+ struct queue_s queue[MAX_RSS_QUEUES];
+ int num_queues;
+ };
+};
+
+/*
+ * adapter get,interfaces
+ */
+struct ntc_interfaces_s {
+ int final_list;
+ uint8_t nb_ports;
+ struct ntc_interface_s intf[];
+};
+
+/*
+ * adapter get,info
+ */
+struct ntc_adap_get_info_s {
+ char *fw_version[32];
+};
+
+#endif /* _NTCONNECT_API_ADAPTER_H_ */
diff --git a/drivers/net/ntnic/ntconnect/include/ntconnect_api_flow.h b/drivers/net/ntnic/ntconnect/include/ntconnect_api_flow.h
new file mode 100644
index 0000000000..4091d61d7d
--- /dev/null
+++ b/drivers/net/ntnic/ntconnect/include/ntconnect_api_flow.h
@@ -0,0 +1,168 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef _NTCONNECT_API_FILTER_H_
+#define _NTCONNECT_API_FILTER_H_
+
+#include "stream_binary_flow_api.h"
+
+/*
+ * Create structures allocating the space to carry through ntconnect interface
+ */
+#define MAX_FLOW_STREAM_ELEM 16
+#define MAX_FLOW_STREAM_QUERY_DATA 1024
+#define MAX_FLOW_STREAM_ERROR_MSG 128
+#define MAX_FLOW_STREAM_VXLAN_TUN_ELEM 8
+#define MAX_FLOW_STREAM_COUNT_ACTIONS 4
+
+#define MAX_PATH_LEN 128
+
+enum ntconn_flow_err_e {
+ NTCONN_FLOW_ERR_NONE = 0,
+ NTCONN_FLOW_ERR_INTERNAL_ERROR = 0x100,
+ NTCONN_FLOW_ERR_PORT_IS_NOT_INITIALIZED,
+ NTCONN_FLOW_ERR_INVALID_PORT,
+ NTCONN_FLOW_ERR_UNEXPECTED_VIRTIO_PATH,
+ NTCONN_FLOW_ERR_UNSUPPORTED_ADAPTER,
+ NTCONN_FLOW_ERR_TO_MANY_FLOWS,
+ NTCONN_FLOW_ERR_NOT_YET_IMPLEMENTED,
+ NTCONN_FLOW_ERR_NO_VF_QUEUES,
+};
+
+struct flow_elem_types_s {
+ int valid;
+ union {
+ int start_addr;
+ struct flow_elem_eth eth;
+ struct flow_elem_vlan vlan[2];
+ struct flow_elem_ipv4 ipv4;
+ struct flow_elem_ipv6 ipv6;
+ struct flow_elem_sctp sctp;
+ struct flow_elem_tcp tcp;
+ struct flow_elem_udp udp;
+ struct flow_elem_icmp icmp;
+ struct flow_elem_vxlan vxlan;
+ struct flow_elem_port_id port_id;
+ struct flow_elem_tag tag;
+ } u;
+};
+
+struct flow_elem_cpy {
+ enum flow_elem_type type; /* element type */
+ struct flow_elem_types_s spec_cpy;
+ struct flow_elem_types_s mask_cpy;
+};
+
+struct flow_action_vxlan_encap_cpy {
+ /* Encapsulating vxlan tunnel definition */
+ struct flow_elem_cpy vxlan_tunnel[MAX_FLOW_STREAM_VXLAN_TUN_ELEM];
+};
+
+struct flow_action_rss_cpy {
+ struct flow_action_rss rss;
+ uint16_t cpy_queue[FLOW_MAX_QUEUES];
+};
+
+#define MAX_ACTION_ENCAP_DATA 512
+struct flow_action_decap_cpy {
+ uint8_t data[MAX_ACTION_ENCAP_DATA];
+ size_t size;
+ struct flow_elem_cpy item_cpy
+ [RAW_ENCAP_DECAP_ELEMS_MAX]; /* Need room for end command */
+ int item_count;
+};
+
+struct flow_action_encap_cpy {
+ uint8_t data[MAX_ACTION_ENCAP_DATA];
+ size_t size;
+ struct flow_elem_cpy item_cpy
+ [RAW_ENCAP_DECAP_ELEMS_MAX]; /* Need room for end command */
+ int item_count;
+};
+
+struct flow_action_types_s {
+ int valid;
+ union {
+ int start_addr;
+ struct flow_action_rss_cpy rss;
+ struct flow_action_push_vlan vlan;
+ struct flow_action_set_vlan_vid vlan_vid;
+ struct flow_action_vxlan_encap_cpy vxlan;
+ struct flow_action_count count;
+ struct flow_action_mark mark;
+ struct flow_action_port_id port_id;
+ struct flow_action_tag tag;
+ struct flow_action_queue queue;
+ struct flow_action_decap_cpy decap;
+ struct flow_action_encap_cpy encap;
+ struct flow_action_jump jump;
+ struct flow_action_meter meter;
+ } u;
+};
+
+struct flow_action_cpy {
+ enum flow_action_type type;
+ struct flow_action_types_s conf_cpy;
+};
+
+struct query_flow_ntconnect {
+ uint8_t port;
+ struct flow_action_cpy action;
+ uint64_t flow;
+};
+
+struct create_flow_ntconnect {
+ uint8_t port;
+ uint8_t vport;
+ struct flow_attr attr;
+ struct flow_elem_cpy elem[MAX_FLOW_STREAM_ELEM];
+ struct flow_action_cpy action[MAX_FLOW_STREAM_ELEM];
+};
+
+struct destroy_flow_ntconnect {
+ uint8_t port;
+ uint64_t flow;
+};
+
+#define ERR_MSG_LEN 128LLU
+
+struct flow_setport_return {
+ struct flow_queue_id_s queues[FLOW_MAX_QUEUES];
+ uint8_t num_queues;
+};
+
+struct flow_error_return_s {
+ enum flow_error_e type;
+ char err_msg[ERR_MSG_LEN];
+ int status;
+};
+
+struct create_flow_return_s {
+ uint64_t flow;
+};
+
+struct validate_flow_return_s {
+ int status;
+};
+
+struct query_flow_return_s {
+ enum flow_error_e type;
+ char err_msg[ERR_MSG_LEN];
+ int status;
+ uint32_t data_length;
+ uint8_t data[];
+};
+
+struct flow_return_s {
+ enum flow_error_e type;
+ char err_msg[ERR_MSG_LEN];
+ int status;
+};
+
+struct flow_error_ntconn {
+ enum flow_error_e type;
+ char message[ERR_MSG_LEN];
+};
+
+#endif /* _NTCONNECT_API_FILTER_H_ */
diff --git a/drivers/net/ntnic/ntconnect/include/ntconnect_api_meter.h b/drivers/net/ntnic/ntconnect/include/ntconnect_api_meter.h
new file mode 100644
index 0000000000..901b0ccba1
--- /dev/null
+++ b/drivers/net/ntnic/ntconnect/include/ntconnect_api_meter.h
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef _NTCONNECT_METER_FILTER_H_
+#define _NTCONNECT_METER_FILTER_H_
+
+#define FLOW_COOKIE 0x12344321
+
+/*
+ * Create structures allocating the space to carry through ntconnect interface
+ */
+
+#define MAX_PATH_LEN 128
+
+enum ntconn_meter_err_e {
+ NTCONN_METER_ERR_NONE = 0,
+ NTCONN_METER_ERR_INTERNAL_ERROR = 0x100,
+ NTCONN_METER_ERR_INVALID_PORT,
+ NTCONN_METER_ERR_UNEXPECTED_VIRTIO_PATH,
+ NTCONN_METER_ERR_PROFILE_ID,
+ NTCONN_METER_ERR_POLICY_ID,
+ NTCONN_METER_ERR_METER_ID,
+};
+
+enum ntconn_meter_command_e {
+ UNKNOWN_CMD,
+ ADD_PROFILE,
+ DEL_PROFILE,
+ ADD_POLICY,
+ DEL_POLICY,
+ CREATE_MTR,
+ DEL_MTR
+};
+
+#define ERR_MSG_LEN 128LLU
+
+struct meter_error_return_s {
+ enum rte_mtr_error_type type;
+ int status;
+ char err_msg[ERR_MSG_LEN];
+};
+
+struct meter_setup_s {
+ uint8_t vport;
+ uint32_t id;
+ int shared;
+ union {
+ struct rte_mtr_meter_profile profile;
+ struct {
+ struct rte_mtr_meter_policy_params policy;
+ struct rte_flow_action actions_green[2];
+ struct rte_flow_action actions_yellow[2];
+ struct rte_flow_action actions_red[2];
+ } p;
+ struct rte_mtr_params mtr_params;
+ };
+};
+
+struct meter_get_stat_s {
+ uint8_t vport;
+ uint32_t mtr_id;
+ int clear;
+};
+
+struct meter_return_stat_s {
+ struct rte_mtr_stats stats;
+ uint64_t stats_mask;
+};
+
+struct meter_setup_ptr_s {
+ uint32_t id;
+ int shared;
+ union {
+ struct rte_mtr_meter_profile *profile;
+ struct rte_mtr_meter_policy_params *policy;
+ struct rte_mtr_params *mtr_params;
+ };
+};
+
+struct meter_return_s {
+ int status;
+};
+
+struct meter_capabilities_return_s {
+ struct rte_mtr_capabilities cap;
+};
+
+#endif /* _NTCONNECT_METER_FILTER_H_ */
diff --git a/drivers/net/ntnic/ntconnect/include/ntconnect_api_statistic.h b/drivers/net/ntnic/ntconnect/include/ntconnect_api_statistic.h
new file mode 100644
index 0000000000..1022bc2056
--- /dev/null
+++ b/drivers/net/ntnic/ntconnect/include/ntconnect_api_statistic.h
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef _NTCONNECT_API_STATISTIC_H_
+#define _NTCONNECT_API_STATISTIC_H_
+
+/*
+ * Supported defined statistic records for Stat layout version 6 - defined in nthw_stat module
+ */
+#define NUM_STAT_RECORD_TYPE_COLOR \
+ (sizeof(struct color_type_fields_s) / sizeof(uint64_t))
+struct color_type_fields_s {
+ uint64_t pkts;
+ uint64_t octets;
+ uint64_t tcp_flgs;
+};
+
+#define NUM_STAT_RECORD_TYPE_FLOWMATCHER \
+ (sizeof(struct flowmatcher_type_fields_s) / sizeof(uint64_t))
+struct flowmatcher_type_fields_s {
+ /* FLM 0.17 */
+ uint64_t current;
+ uint64_t learn_done;
+ uint64_t learn_ignore;
+ uint64_t learn_fail;
+ uint64_t unlearn_done;
+ uint64_t unlearn_ignore;
+ uint64_t auto_unlearn_done;
+ uint64_t auto_unlearn_ignore;
+ uint64_t auto_unlearn_fail;
+ uint64_t timeout_unlearn_done;
+ uint64_t rel_done;
+ uint64_t rel_ignore;
+ uint64_t prb_done;
+ uint64_t prb_ignore;
+ /* FLM 0.20 */
+ uint64_t sta_done;
+ uint64_t inf_done;
+ uint64_t inf_skip;
+ uint64_t pck_hit;
+ uint64_t pck_miss;
+ uint64_t pck_unh;
+ uint64_t pck_dis;
+ uint64_t csh_hit;
+ uint64_t csh_miss;
+ uint64_t csh_unh;
+ uint64_t cuc_start;
+ uint64_t cuc_move;
+};
+
+#define NUM_STAT_RECORD_TYPE_QUEUE \
+ (sizeof(struct queue_type_fields_s) / sizeof(uint64_t))
+struct queue_type_fields_s {
+ uint64_t flush_pkts;
+ uint64_t drop_pkts;
+ uint64_t fwd_pkts;
+ uint64_t dbs_drop_pkts;
+ uint64_t flush_octets;
+ uint64_t drop_octets;
+ uint64_t fwd_octets;
+ uint64_t dbs_drop_octets;
+};
+
+/*
+ * Port stat counters for virtualization NICS with virtual ports support
+ */
+#define NUM_STAT_RECORD_TYPE_RX_PORT_VIRT \
+ (sizeof(struct rtx_type_fields_virt_s) / sizeof(uint64_t))
+/* same for Rx and Tx counters on Virt */
+#define NUM_STAT_RECORD_TYPE_TX_PORT_VIRT NUM_STAT_RECORD_TYPE_RX_PORT_VIRT
+struct rtx_type_fields_virt_s {
+ uint64_t octets;
+ uint64_t pkts;
+ uint64_t drop_events;
+ uint64_t qos_drop_octets;
+ uint64_t qos_drop_pkts;
+};
+
+/*
+ * Port RMON counters for Cap devices
+ */
+struct stat_rmon_s {
+ /* Sums that are calculated by software */
+ uint64_t drop_events;
+ uint64_t pkts;
+ /* Read from FPGA */
+ uint64_t octets;
+ uint64_t broadcast_pkts;
+ uint64_t multicast_pkts;
+ uint64_t unicast_pkts;
+ uint64_t pkts_alignment;
+ uint64_t pkts_code_violation;
+ uint64_t pkts_crc;
+ uint64_t undersize_pkts;
+ uint64_t oversize_pkts;
+ uint64_t fragments;
+ uint64_t jabbers_not_truncated;
+ uint64_t jabbers_truncated;
+ uint64_t pkts_64_octets;
+ uint64_t pkts_65_to_127_octets;
+ uint64_t pkts_128_to_255_octets;
+ uint64_t pkts_256_to_511_octets;
+ uint64_t pkts_512_to_1023_octets;
+ uint64_t pkts_1024_to_1518_octets;
+ uint64_t pkts_1519_to_2047_octets;
+ uint64_t pkts_2048_to_4095_octets;
+ uint64_t pkts_4096_to_8191_octets;
+ uint64_t pkts_8192_to_max_octets;
+};
+
+#define NUM_STAT_RECORD_TYPE_RX_PORT_CAP \
+ (sizeof(struct rx_type_fields_cap_s) / sizeof(uint64_t))
+struct rx_type_fields_cap_s {
+ struct stat_rmon_s rmon;
+ uint64_t mac_drop_events;
+ uint64_t pkts_lr;
+ /* Rx only port counters */
+ uint64_t duplicate;
+ uint64_t pkts_ip_chksum_error;
+ uint64_t pkts_udp_chksum_error;
+ uint64_t pkts_tcp_chksum_error;
+ uint64_t pkts_giant_undersize;
+ uint64_t pkts_baby_giant;
+ uint64_t pkts_not_isl_vlan_mpls;
+ uint64_t pkts_isl;
+ uint64_t pkts_vlan;
+ uint64_t pkts_isl_vlan;
+ uint64_t pkts_mpls;
+ uint64_t pkts_isl_mpls;
+ uint64_t pkts_vlan_mpls;
+ uint64_t pkts_isl_vlan_mpls;
+ uint64_t pkts_no_filter;
+ uint64_t pkts_dedup_drop;
+ uint64_t pkts_filter_drop;
+ uint64_t pkts_overflow;
+ uint64_t pkts_dbs_drop;
+ uint64_t octets_no_filter;
+ uint64_t octets_dedup_drop;
+ uint64_t octets_filter_drop;
+ uint64_t octets_overflow;
+ uint64_t octets_dbs_drop;
+ uint64_t ipft_first_hit;
+ uint64_t ipft_first_not_hit;
+ uint64_t ipft_mid_hit;
+ uint64_t ipft_mid_not_hit;
+ uint64_t ipft_last_hit;
+ uint64_t ipft_last_not_hit;
+};
+
+#define NUM_STAT_RECORD_TYPE_TX_PORT_CAP \
+ (sizeof(struct tx_type_fields_cap_s) / sizeof(uint64_t))
+struct tx_type_fields_cap_s {
+ struct stat_rmon_s rmon;
+};
+
+/*
+ * stat get,colors
+ * stat get,queues
+ * stat get,rx_counters
+ * stat get,tx_counters
+ */
+#define STAT_INFO_ELEMENTS \
+ (sizeof(struct ntc_stat_get_data_s) / sizeof(uint64_t))
+
+struct ntc_stat_get_data_s {
+ uint64_t nb_counters;
+ uint64_t timestamp;
+ uint64_t is_virt;
+ uint64_t data[];
+};
+
+#endif /* _NTCONNECT_API_STATISTIC_H_ */
diff --git a/drivers/net/ntnic/ntconnect/include/ntconnect_api_test.h b/drivers/net/ntnic/ntconnect/include/ntconnect_api_test.h
new file mode 100644
index 0000000000..44cacbd931
--- /dev/null
+++ b/drivers/net/ntnic/ntconnect/include/ntconnect_api_test.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef _NTCONNECT_TEST_FILTER_H_
+#define _NTCONNECT_TEST_FILTER_H_
+
+/*
+ * Create structures allocating the space to carry through ntconnect interface
+ */
+
+struct test_s {
+ int number;
+ int status;
+ uint64_t test[];
+};
+
+#endif /* _NTCONNECT_TEST_FILTER_H_ */
diff --git a/drivers/net/ntnic/ntconnect/ntconn_server.c b/drivers/net/ntnic/ntconnect/ntconn_server.c
new file mode 100644
index 0000000000..34a3c19955
--- /dev/null
+++ b/drivers/net/ntnic/ntconnect/ntconn_server.c
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "ntconnect.h"
+#include "ntconn_mod_helper.h"
+#include "nt_util.h"
+#include "ntlog.h"
+
+/*
+ * Server module always registered on 0000:00:00.0
+ */
+#define this_module_name "server"
+
+#define NTCONNECT_SERVER_VERSION_MAJOR 0U
+#define NTCONNECT_SERVER_VERSION_MINOR 1U
+
+static int func_get_nic_pci(void *hdl, int client_fd,
+ struct ntconn_header_s *hdr, char **data, int *len);
+static struct func_s funcs_get_level1[] = {
+ { "nic_pci_ids", NULL, func_get_nic_pci },
+ { NULL, NULL, NULL },
+};
+
+/*
+ * Entry level
+ */
+static struct func_s server_entry_funcs[] = {
+ { "get", funcs_get_level1, NULL },
+ { NULL, NULL, NULL },
+};
+
+static int func_get_nic_pci(void *hdl, int client_fd _unused,
+ struct ntconn_header_s *hdr _unused, char **data,
+ int *len)
+{
+ struct ntconn_server_s *serv = (struct ntconn_server_s *)hdl;
+ struct ntc_nic_pci_ids_s *npci =
+ calloc(1, sizeof(struct ntc_nic_pci_ids_s));
+ if (!npci) {
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "memory allocation failed");
+ return REQUEST_ERR;
+ }
+ int i = 0;
+
+ while (i < MAX_PCI_IDS && serv->pci_id_list[i].pci_id) {
+ sprintf(npci->nic_pci_id[i], "%04x:%02x:%02x.%x",
+ serv->pci_id_list[i].domain & 0xffff,
+ serv->pci_id_list[i].bus, serv->pci_id_list[i].devid,
+ serv->pci_id_list[i].function);
+ i++;
+ }
+ npci->num_nics = i;
+ *data = (char *)npci;
+ *len = sizeof(struct ntc_nic_pci_ids_s);
+
+ return REQUEST_OK;
+}
+
+static int ntconn_server_request(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char *function,
+ char **data, int *len)
+{
+ return execute_function(this_module_name, hdl, client_id, hdr, function,
+ server_entry_funcs, data, len, 0);
+}
+
+static void ntconn_server_free_data(void *hdl _unused, char *data)
+{
+ if (data) {
+#ifdef DEBUG
+ NT_LOG(DBG, NTCONNECT, "server free data\n");
+#endif
+ free(data);
+ }
+}
+
+static const ntconnapi_t ntconn_server_op = { this_module_name,
+ NTCONNECT_SERVER_VERSION_MAJOR,
+ NTCONNECT_SERVER_VERSION_MINOR,
+ ntconn_server_request,
+ ntconn_server_free_data,
+ NULL
+ };
+
+int ntconn_server_register(void *server)
+{
+ const struct rte_pci_addr addr = {
+ .domain = 0, .bus = 0, .devid = 0, .function = 0
+ };
+
+ return register_ntconn_mod(&addr, server, &ntconn_server_op);
+}
diff --git a/drivers/net/ntnic/ntconnect/ntconnect.c b/drivers/net/ntnic/ntconnect/ntconnect.c
new file mode 100644
index 0000000000..697e101a03
--- /dev/null
+++ b/drivers/net/ntnic/ntconnect/ntconnect.c
@@ -0,0 +1,641 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <libgen.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "nt_util.h"
+#include "ntconnect.h"
+#include "ntconnect_api.h"
+#include "ntlog.h"
+
+/* clang-format off */
+ntconn_err_t ntconn_err[] = {
+ {NTCONN_ERR_CODE_NONE, "Success"},
+ {NTCONN_ERR_CODE_INTERNAL_ERROR, "Internal error"},
+ {NTCONN_ERR_CODE_INTERNAL_REPLY_ERROR, "Internal error in reply from module"},
+ {NTCONN_ERR_CODE_NO_DATA, "No data found"},
+ {NTCONN_ERR_CODE_INVALID_REQUEST, "Invalid request"},
+ {NTCONN_ERR_CODE_NOT_YET_IMPLEMENTED, "Function not yet implemented"},
+ {NTCONN_ERR_CODE_INTERNAL_FUNC_ERROR, "Internal error in function call list"},
+ {NTCONN_ERR_CODE_MISSING_INVALID_PARAM, "Missing or invalid parameter"},
+ {NTCONN_ERR_CODE_FUNCTION_PARAM_INCOMPLETE, "Function parameter is incomplete"},
+ {NTCONN_ERR_CODE_FUNC_PARAM_NOT_RECOGNIZED,
+ "Function or parameter not recognized/supported"},
+ {-1, NULL}
+};
+
+/* clang-format on */
+
+static ntconn_mod_t *ntcmod_base;
+static pthread_t tid;
+static pthread_t ctid;
+static struct ntconn_server_s ntconn_serv;
+
+const ntconn_err_t *get_ntconn_error(enum ntconn_err_e err_code)
+{
+ int idx = 0;
+
+ while (ntconn_err[idx].err_code != (uint32_t)-1 &&
+ ntconn_err[idx].err_code != err_code)
+ idx++;
+ if (ntconn_err[idx].err_code == (uint32_t)-1)
+ idx = 1;
+
+ return &ntconn_err[idx];
+}
+
+int register_ntconn_mod(const struct rte_pci_addr *addr, void *hdl,
+ const ntconnapi_t *op)
+{
+ /* Verify and check module name is unique */
+#ifdef DEBUG
+ NT_LOG(DBG, NTCONNECT,
+ "Registering pci: %04x:%02x:%02x.%x, module %s\n", addr->domain,
+ addr->bus, addr->devid, addr->function, op->module);
+#endif
+
+ ntconn_mod_t *ntcmod = (ntconn_mod_t *)malloc(sizeof(ntconn_mod_t));
+
+ if (!ntcmod) {
+ NT_LOG(ERR, NTCONNECT, "memory allocation failed");
+ return -1;
+ }
+ ntcmod->addr.domain = addr->domain;
+ ntcmod->addr.bus = addr->bus;
+ ntcmod->addr.devid = addr->devid;
+ ntcmod->addr.function = addr->function;
+ ntcmod->addr.pad = 0;
+
+ ntcmod->hdl = hdl;
+ ntcmod->op = op;
+ pthread_mutex_init(&ntcmod->mutex, NULL);
+
+ ntcmod->next = ntcmod_base;
+ ntcmod_base = ntcmod;
+
+ if (ntcmod->addr.pci_id) { /* Avoid server fake pci_id */
+ int i;
+
+ for (i = 0; i < MAX_PCI_IDS; i++) {
+ if (ntconn_serv.pci_id_list[i].pci_id == 0) {
+ NT_LOG(DBG, NTCONNECT,
+ "insert at index %i PCI ID %" PRIX64 "\n", i,
+ ntcmod->addr.pci_id);
+ ntconn_serv.pci_id_list[i].pci_id =
+ ntcmod->addr.pci_id;
+ break;
+ } else if (ntconn_serv.pci_id_list[i].pci_id ==
+ ntcmod->addr.pci_id)
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int unix_build_address(const char *path, struct sockaddr_un *addr)
+{
+ if (addr == NULL || path == NULL)
+ return -1;
+ memset(addr, 0, sizeof(struct sockaddr_un));
+ addr->sun_family = AF_UNIX;
+ if (strlen(path) < sizeof(addr->sun_path)) {
+ rte_strscpy(addr->sun_path, path, sizeof(addr->sun_path) - 1);
+ return 0;
+ }
+ return -1;
+}
+
+#define STATUS_OK 0
+#define STATUS_INTERNAL_ERROR -1
+#define STATUS_TRYAGAIN -2
+#define STATUS_INVALID_PARAMETER -3
+#define STATUS_CONNECTION_CLOSED -4
+#define STATUS_CONNECTION_INVALID -5
+#define STATUS_TIMEOUT -6
+
+static int read_data(int fd, size_t len, uint8_t *data, size_t *recv_len,
+ int timeout)
+{
+ struct pollfd pfd;
+ ssize_t ret;
+
+ pfd.fd = fd;
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+
+ ret = poll(&pfd, 1, timeout);
+ if (ret < 0) {
+ if (errno == EINTR)
+ return STATUS_TRYAGAIN; /* Caught signal before timeout */
+ if (errno == EINVAL)
+ return STATUS_INVALID_PARAMETER; /* Timeout is negative */
+ if (errno == EFAULT)
+ return STATUS_INVALID_PARAMETER; /* Fds argument is illegal */
+ /* else */
+ assert(0);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ if (ret == 0)
+ return STATUS_TIMEOUT;
+
+ if (pfd.revents == 0) {
+ assert(ret == 1);
+ assert(0); /* Revents cannot be zero when NtSocket_Poll returns 1 */
+ return STATUS_TRYAGAIN;
+ }
+
+ if ((pfd.revents & POLLIN) &&
+ ((pfd.revents & (POLLERR | POLLNVAL)) == 0)) {
+ ret = recv(pfd.fd, data, len, 0);
+ if (ret < 0) {
+ int lerrno = errno;
+
+ if (lerrno == EWOULDBLOCK || lerrno == EAGAIN) {
+ /*
+ * We have data but if the very first read turns out to return
+ * EWOULDBLOCK or EAGAIN it means that the remote end has dropped
+ * the connection
+ */
+ NT_LOG(DBG, NTCONNECT,
+ "The socket with fd %d has been closed by remote end. %d [%s]\n",
+ pfd.fd, lerrno, strerror(lerrno));
+ return STATUS_CONNECTION_CLOSED;
+ }
+ if (lerrno != EINTR) {
+ NT_LOG(ERR, NTCONNECT,
+ "recv() from fd %d received errno %d [%s]\n",
+ pfd.fd, lerrno, strerror(lerrno));
+ return STATUS_CONNECTION_INVALID;
+ }
+ /* EINTR */
+ return STATUS_TRYAGAIN;
+ }
+ if (ret == 0) {
+ if (pfd.revents & POLLHUP) {
+ /* This means that we have read all data and the remote end has
+ * HUP
+ */
+#ifdef DEBUG
+ NT_LOG(DBG, NTCONNECT,
+ "The remote end has terminated the session\n");
+#endif
+ return STATUS_CONNECTION_CLOSED;
+ }
+ return STATUS_TRYAGAIN;
+ }
+
+ /* Ret can only be positive at this point */
+ *recv_len = (size_t)ret;
+ return STATUS_OK;
+ }
+
+ if ((pfd.revents & POLLHUP) == POLLHUP) {
+ /* this means that the remote end has HUP */
+ NT_LOG(DBG, NTCONNECT,
+ "The remote end has terminated the session\n");
+ return STATUS_CONNECTION_CLOSED;
+ }
+
+ NT_LOG(ERR, NTCONNECT,
+ "poll() returned 0x%x. Invalidating the connection\n",
+ pfd.revents);
+ return STATUS_CONNECTION_INVALID;
+}
+
+static int read_all(int clfd, uint8_t *data, size_t length)
+{
+ size_t recv_len = 0;
+ size_t left = length;
+ size_t pos = 0;
+
+ while (left > 0) {
+ int ret = read_data(clfd, left, &data[pos], &recv_len, -1);
+
+ if (ret == STATUS_OK) {
+ pos += recv_len;
+ left -= recv_len;
+ } else {
+ if (ret == STATUS_CONNECTION_CLOSED || ret == STATUS_TIMEOUT) {
+ /* Silently return status */
+ return ret;
+ }
+ if (ret != STATUS_TRYAGAIN) {
+ NT_LOG(ERR, NTCONNECT,
+ "Failed getting packet. Error code: 0x%X\n",
+ ret);
+ return ret;
+ }
+ }
+ /* Try again */
+ }
+ return STATUS_OK;
+}
+
+static int write_all(int fd, const uint8_t *data, size_t size)
+{
+ size_t len = 0;
+
+ while (len < size) {
+ ssize_t res = write(fd, (const void *)&data[len], size - len);
+
+ if (res < 0) {
+ NT_LOG(ERR, NTCONNECT, "write to socket failed!");
+ return STATUS_INTERNAL_ERROR;
+ }
+ len += res;
+ }
+ return 0;
+}
+
+static int read_request(int clfd, char **rdata)
+{
+ uint8_t *data = malloc(MESSAGE_BUFFER * sizeof(uint8_t));
+
+ if (!data) {
+ NT_LOG(ERR, NTCONNECT, "memory allocation failed");
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ size_t recv_len = 0;
+ int ret = read_data(clfd, MESSAGE_BUFFER, data, &recv_len, -1);
+
+ if (ret) {
+ free(data);
+ return ret;
+ }
+
+ struct ntconn_header_s hdr;
+
+ memcpy(&hdr, data, NTCMOD_HDR_LEN);
+ size_t length = (hdr.len + hdr.blob_len) * sizeof(uint8_t);
+
+ if (length > MESSAGE_BUFFER) {
+ uint8_t *new_data = realloc(data, length);
+
+ if (!new_data) {
+ NT_LOG(ERR, NTCONNECT, "memory reallocation failed");
+ free(data);
+ return STATUS_INTERNAL_ERROR;
+ }
+ data = new_data;
+ ret = read_all(clfd, &data[recv_len], length - recv_len);
+ if (ret) {
+ free(data);
+ return ret;
+ }
+ }
+
+ *rdata = (char *)data;
+ return STATUS_OK;
+}
+
+static ntconn_mod_t *ntconnect_interpret_request(int clfd,
+ struct ntconn_header_s *hdr,
+ char **get_req _unused,
+ char **module_cmd, int *status)
+{
+ char pci_id[32];
+ char module[64];
+ ntconn_mod_t *result_ntcmod = NULL;
+ char *request = NULL;
+
+ int ret = read_request(clfd, &request);
+ *status = ret;
+ *get_req = request;
+
+ if (ret == STATUS_OK && request) {
+ *hdr = *(struct ntconn_header_s *)request;
+
+ if (!hdr) {
+ NT_LOG(ERR, NTCONNECT, "hdr returned NULL\n");
+ *status = STATUS_INTERNAL_ERROR;
+ return NULL;
+ }
+
+ switch (hdr->tag) {
+ case NTCONN_TAG_REQUEST: {
+ unsigned long idx = NTCMOD_HDR_LEN;
+ char *saveptr;
+ char *req = &request[idx];
+
+ uint32_t domain = 0xffffffff;
+ uint8_t bus = 0xff;
+ uint8_t devid = 0xff;
+ uint8_t function = 0xff;
+
+ char *tok = strtok_r(req, ";", &saveptr);
+
+ idx += strlen(tok) + 1;
+ if (!tok)
+ goto err_out;
+ rte_strscpy(pci_id, tok, 31);
+
+ tok = strtok_r(NULL, ";", &saveptr);
+ idx += strlen(tok) + 1;
+ if (!tok)
+ goto err_out;
+ rte_strscpy(module, tok, 63);
+
+ tok = strtok_r(NULL, "", &saveptr);
+ hdr->len -= idx;
+ if (tok)
+ *module_cmd = &request[idx];
+
+ tok = strtok_r(pci_id, ":.", &saveptr);
+ if (!tok)
+ goto err_out;
+ domain = (uint32_t)strtol(tok, NULL, 16);
+ tok = strtok_r(NULL, ":.", &saveptr);
+ if (!tok)
+ goto err_out;
+ bus = (uint8_t)strtol(tok, NULL, 16);
+
+ tok = strtok_r(NULL, ":.", &saveptr);
+ if (!tok)
+ goto err_out;
+ devid = (uint8_t)strtol(tok, NULL, 16);
+ tok = strtok_r(NULL, "", &saveptr);
+ if (!tok)
+ goto err_out;
+ function = (uint8_t)strtol(tok, NULL, 16);
+
+ /* Search for module registered as <pci_id:module> */
+ ntconn_mod_t *ntcmod = ntcmod_base;
+
+ while (ntcmod) {
+ if (domain == ntcmod->addr.domain &&
+ bus == ntcmod->addr.bus &&
+ devid == ntcmod->addr.devid &&
+ function == ntcmod->addr.function &&
+ strcmp(ntcmod->op->module, module) == 0) {
+ result_ntcmod = ntcmod;
+ break;
+ }
+ ntcmod = ntcmod->next;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+err_out:
+
+ return result_ntcmod;
+}
+
+static int send_reply(int clfd, uint16_t reply_tag, const void *data,
+ uint32_t size)
+{
+ struct ntconn_header_s hdr;
+
+ hdr.tag = reply_tag;
+ hdr.len = NTCMOD_HDR_LEN + size;
+ hdr.blob_len = 0;
+ uint8_t *message = malloc(hdr.len * sizeof(uint8_t));
+
+ if (!message) {
+ NT_LOG(ERR, NTCONNECT, "memory allocation failed");
+ return STATUS_INTERNAL_ERROR;
+ }
+ memcpy(message, (void *)&hdr, NTCMOD_HDR_LEN);
+ memcpy(&message[NTCMOD_HDR_LEN], data, size);
+ int res = write_all(clfd, message, hdr.len);
+
+ free(message);
+ if (res)
+ return res;
+
+ return 0;
+}
+
+static int send_reply_free_data(int clfd, ntconn_mod_t *cmod,
+ uint16_t reply_tag, void *data, uint32_t size)
+{
+ int res = send_reply(clfd, reply_tag, data, size);
+
+ if (size) {
+ pthread_mutex_lock(&cmod->mutex);
+ cmod->op->free_data(cmod->hdl, data);
+ pthread_mutex_unlock(&cmod->mutex);
+ }
+
+ return res;
+}
+
+static int ntconnect_send_error(int clfd, enum ntconn_err_e err_code)
+{
+ char err_buf[MAX_ERR_MESSAGE_LENGTH];
+ const ntconn_err_t *ntcerr = get_ntconn_error(err_code);
+
+ sprintf(err_buf, "----connect:%s", ntcerr->err_text);
+ unsigned int len = strlen(err_buf);
+ memcpy(err_buf, &ntcerr->err_code, sizeof(uint32_t));
+
+ return send_reply(clfd, NTCONN_TAG_ERROR, err_buf, len);
+}
+
+static void *ntconnect_worker(void *arg)
+{
+ int status;
+ int clfd = (int)(uint64_t)arg;
+ char *module_cmd = NULL;
+ char *request = NULL;
+ struct ntconn_header_s hdr;
+
+ do {
+ ntconn_mod_t *cmod = ntconnect_interpret_request(clfd, &hdr,
+ &request,
+ &module_cmd,
+ &status);
+
+ if (cmod && module_cmd && status == 0) {
+ int len;
+ char *data;
+
+ /*
+ * Handle general module commands
+ */
+ if (strcmp(module_cmd, "version") == 0) {
+ uint64_t version =
+ ((uint64_t)cmod->op->version_major
+ << 32) +
+ (cmod->op->version_minor);
+
+ if (send_reply(clfd, NTCONN_TAG_REPLY,
+ (void *)&version,
+ sizeof(uint64_t)))
+ break;
+
+ } else {
+ /*
+ * Call module for execution of command
+ */
+ data = NULL;
+ pthread_mutex_lock(&cmod->mutex);
+ int repl = cmod->op->request(cmod->hdl, clfd,
+ &hdr, module_cmd,
+ &data, &len);
+ pthread_mutex_unlock(&cmod->mutex);
+
+ if (repl == REQUEST_OK && len >= 0) {
+ if (send_reply_free_data(clfd, cmod,
+ NTCONN_TAG_REPLY,
+ (void *)data,
+ (uint32_t)len))
+ break;
+
+ } else if (repl == REQUEST_ERR && len >= 0) {
+ if (send_reply_free_data(clfd, cmod,
+ NTCONN_TAG_ERROR,
+ (void *)data,
+ (uint32_t)len))
+ break;
+ } else {
+ NT_LOG(ERR, NTCONNECT,
+ "Invalid result from module request function: module %s, result %i\n",
+ cmod->op->module, repl);
+ if (ntconnect_send_error(clfd,
+ NTCONN_ERR_CODE_INTERNAL_REPLY_ERROR))
+ break;
+ }
+ }
+
+ } else if (status == STATUS_TIMEOUT) {
+ /* Other end is dead */
+ NT_LOG(WRN, NTCONNECT,
+ "Client must be dead - timeout\n");
+ break;
+ } else if (status == STATUS_CONNECTION_CLOSED) {
+ break; /* silently break out */
+ }
+ /* Error - send error back */
+ if (ntconnect_send_error(clfd, NTCONN_ERR_CODE_INVALID_REQUEST))
+ break;
+ if (request)
+ free(request);
+ } while (1); /* while still connected */
+
+ close(clfd);
+
+ /* call module cleanup callback function for client_id */
+ ntconn_mod_t *ntcmod = ntcmod_base;
+
+ while (ntcmod) {
+ if (ntcmod->op->client_cleanup) {
+ pthread_mutex_lock(&ntcmod->mutex);
+ ntcmod->op->client_cleanup(ntcmod->hdl, clfd);
+ pthread_mutex_unlock(&ntcmod->mutex);
+ }
+
+ ntcmod = ntcmod->next;
+ }
+ pthread_exit(NULL);
+ return NULL;
+}
+
+static void *ntconnect_server(void *arg)
+{
+ struct ntconn_server_s *ntcserv = (struct ntconn_server_s *)arg;
+
+ ntcserv->running = 1;
+
+#ifdef DEBUG
+ NT_LOG(DBG, NTCONNECT, "Running NT Connection Server fd %i\n",
+ ntcserv->serv_fd);
+#endif
+
+ if (listen(ntcserv->serv_fd, 5) < 0) {
+ NT_LOG(ERR, NTCONNECT,
+ "Server failed on listen(), stopping thread. err: %s\n",
+ strerror(errno));
+ pthread_exit(NULL);
+ return NULL;
+ }
+
+ while (ntcserv->running) {
+ int clfd = accept(ntcserv->serv_fd, NULL, NULL);
+
+ if (clfd < 0) {
+ NT_LOG(ERR, NTCONNECT,
+ "ERROR from accept(), stopping thread. err: %s\n",
+ strerror(errno));
+ break;
+ }
+ pthread_create(&ctid, NULL, ntconnect_worker,
+ (void *)(uint64_t)clfd);
+ pthread_setaffinity_np(ctid, sizeof(cpu_set_t),
+ &ntcserv->cpuset);
+ /* Detach immediately. We will never join this thread */
+ pthread_detach(ctid);
+ }
+
+ pthread_exit(NULL);
+ return NULL;
+}
+
+int ntconnect_init(const char *sockname, cpu_set_t cpuset)
+{
+ if (ntcmod_base) {
+ /* Make sure the socket directory exists */
+ char *sockname_copy = strdup(sockname);
+ char *sockname_dir = dirname(sockname_copy);
+
+ if (mkdir(sockname_dir, 0755) < 0 && errno != EEXIST) {
+ NT_LOG(ERR, NTCONNECT,
+ "Can't create socket directory: %s",
+ sockname_dir);
+ free(sockname_copy);
+ return -1;
+ }
+ free(sockname_copy);
+
+ /* Add server to module list - cannot work without */
+ ntconn_server_register(&ntconn_serv);
+
+ /* Start named socket server */
+ struct sockaddr_un addr;
+
+ unix_build_address(sockname, &addr);
+
+ ntconn_serv.serv_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ ntconn_serv.cpuset = cpuset;
+ if (ntconn_serv.serv_fd == -1)
+ return -1;
+
+ /* Make sure the node in filesystem is deleted otherwise bind will fail */
+ unlink(sockname);
+
+ if (bind(ntconn_serv.serv_fd, (struct sockaddr *)&addr,
+ sizeof(struct sockaddr_un)) == -1) {
+ close(ntconn_serv.serv_fd);
+ return -1;
+ }
+
+ /* Run ntconnect service */
+ pthread_create(&tid, NULL, ntconnect_server, &ntconn_serv);
+ pthread_setaffinity_np(tid, sizeof(cpu_set_t),
+ &ntconn_serv.cpuset);
+ }
+
+ return 0;
+}
diff --git a/drivers/net/ntnic/ntconnect_modules/ntconn_adapter.c b/drivers/net/ntnic/ntconnect_modules/ntconn_adapter.c
new file mode 100644
index 0000000000..294b95846b
--- /dev/null
+++ b/drivers/net/ntnic/ntconnect_modules/ntconn_adapter.c
@@ -0,0 +1,775 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntnic_ethdev.h"
+#include "ntconnect.h"
+#include "ntconnect_api_adapter.h"
+#include "ntos_system.h"
+#include "ntconn_modules.h"
+#include "ntconn_mod_helper.h"
+#include "nt_util.h"
+#include "ntlog.h"
+
+#define NTCONN_ADAP_VERSION_MAJOR 0U
+#define NTCONN_ADAP_VERSION_MINOR 1U
+
+#define this_module_name "adapter"
+
+#define MAX_ADAPTERS 2
+
+static struct adap_hdl_s {
+ struct drv_s *drv;
+} adap_hdl[MAX_ADAPTERS];
+
+static int func_adapter_get_interfaces(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char **data,
+ int *len);
+static int func_adapter_get_info(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char **data,
+ int *len);
+static int func_adapter_get_sensors(void *hdl, int client_id _unused,
+ struct ntconn_header_s *hdr _unused,
+ char **data, int *len);
+static struct func_s funcs_get_level1[] = {
+ { "interfaces", NULL, func_adapter_get_interfaces },
+ { "info", NULL, func_adapter_get_info },
+ { "sensors", NULL, func_adapter_get_sensors },
+ { NULL, NULL, NULL },
+};
+
+static int func_adapter_set_interface(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char **data,
+ int *len);
+static int func_adapter_set_adapter(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char **data,
+ int *len);
+static struct func_s funcs_set_level1[] = {
+ { "interface", NULL, func_adapter_set_interface },
+ { "adapter", NULL, func_adapter_set_adapter },
+ { NULL, NULL, NULL },
+};
+
+/*
+ * Entry level
+ */
+static struct func_s adapter_entry_funcs[] = {
+ { "get", funcs_get_level1, NULL },
+ { "set", funcs_set_level1, NULL },
+ { NULL, NULL, NULL },
+};
+
+static int read_link_speed(enum nt_link_speed_e link_speed)
+{
+ switch (link_speed) {
+ case NT_LINK_SPEED_10M:
+ return PORT_LINK_SPEED_10M;
+ case NT_LINK_SPEED_100M:
+ return PORT_LINK_SPEED_100M;
+ case NT_LINK_SPEED_1G:
+ return PORT_LINK_SPEED_1G;
+ case NT_LINK_SPEED_10G:
+ return PORT_LINK_SPEED_10G;
+ case NT_LINK_SPEED_25G:
+ return PORT_LINK_SPEED_25G;
+ case NT_LINK_SPEED_40G:
+ return PORT_LINK_SPEED_40G;
+ case NT_LINK_SPEED_50G:
+ return PORT_LINK_SPEED_50G;
+ case NT_LINK_SPEED_100G:
+ return PORT_LINK_SPEED_100G;
+ default:
+ break;
+ }
+ return PORT_LINK_SPEED_UNKNOWN;
+}
+
+static nt_link_speed_t convert_link_speed(char *speed_str)
+{
+ if (strcmp(speed_str, "10M") == 0)
+ return NT_LINK_SPEED_10M;
+ else if (strcmp(speed_str, "100M") == 0)
+ return NT_LINK_SPEED_100M;
+ else if (strcmp(speed_str, "1G") == 0)
+ return NT_LINK_SPEED_1G;
+ else if (strcmp(speed_str, "10G") == 0)
+ return NT_LINK_SPEED_10G;
+ else if (strcmp(speed_str, "25G") == 0)
+ return NT_LINK_SPEED_25G;
+ else if (strcmp(speed_str, "40G") == 0)
+ return NT_LINK_SPEED_40G;
+ else if (strcmp(speed_str, "50G") == 0)
+ return NT_LINK_SPEED_50G;
+ else if (strcmp(speed_str, "100G") == 0)
+ return NT_LINK_SPEED_100G;
+ else
+ return NT_LINK_SPEED_UNKNOWN;
+}
+
+static int func_adapter_get_interfaces(void *hdl, int client_id _unused,
+ struct ntconn_header_s *hdr _unused,
+ char **data, int *len)
+{
+ struct ntc_interfaces_s *ifs;
+ struct adap_hdl_s *adap = (struct adap_hdl_s *)hdl;
+ fpga_info_t *fpga_info = &adap->drv->ntdrv.adapter_info.fpga_info;
+ int lag_active;
+ int final_list = adap->drv->probe_finished;
+ /* keep final_list set before nb_ports are called */
+ rte_compiler_barrier();
+ int nb_ports = rte_eth_dev_count_avail();
+
+ /* Get the "internals" structure of phy port 0 to find out if we're running LAG */
+ char phy0_name[128];
+
+ rte_eth_dev_get_name_by_port(0, phy0_name);
+ struct rte_eth_dev *phy0_eth_dev = rte_eth_dev_get_by_name(phy0_name);
+
+ if (phy0_eth_dev == NULL || phy0_eth_dev->data == NULL ||
+ phy0_eth_dev->data->dev_private == NULL) {
+ return ntconn_error(data, len, this_module_name,
+ NTCONN_ERR_CODE_INTERNAL_ERROR);
+ }
+ struct pmd_internals *phy0_internals =
+ (struct pmd_internals *)phy0_eth_dev->data->dev_private;
+ lag_active = (phy0_internals->lag_config == NULL) ? 0 : 1;
+ if (lag_active) {
+ /*
+ * Phy ports are link aggregated. I.e. number of ports is actually
+ * one bigger than what rte_eth_dev_count_avail() returned
+ */
+ nb_ports++;
+
+ /*
+ * Sanity check:
+ * For now we know about LAG with 2 ports only.
+ * If in the future we get HW with more ports, make assert to alert
+ * the developers that something needs to be looked at...
+ */
+ assert(fpga_info->n_phy_ports == 2);
+ }
+
+ *len = sizeof(struct ntc_interfaces_s) +
+ sizeof(struct ntc_interface_s) * nb_ports;
+ ifs = malloc(*len);
+ if (!ifs) {
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "memory allocation failed");
+ return REQUEST_ERR;
+ }
+ *data = (char *)ifs;
+
+ ifs->nb_ports = nb_ports;
+ ifs->final_list = final_list;
+
+ int i;
+
+ /* First set the "port type" of the physical ports */
+ if (lag_active) {
+ if (phy0_internals->lag_config->mode == BONDING_MODE_8023AD) {
+ /* Active/active LAG */
+ for (i = 0; i < fpga_info->n_phy_ports; i++) {
+ ifs->intf[i].type =
+ PORT_TYPE_PHY_LAG_ACTIVE_ACTIVE;
+ }
+ } else if (phy0_internals->lag_config->mode ==
+ BONDING_MODE_ACTIVE_BACKUP) {
+ /* Active/backup LAG */
+ ifs->intf[phy0_internals->lag_config->primary_port]
+ .type = PORT_TYPE_PHY_LAG_PRIMARY;
+ ifs->intf[phy0_internals->lag_config->backup_port].type =
+ PORT_TYPE_PHY_LAG_BACKUP;
+ } else {
+ /* Unknown LAG mode */
+ assert(0);
+ }
+ } else {
+ /* Normal phy ports (not link aggregated) */
+ for (i = 0; i < fpga_info->n_phy_ports; i++)
+ ifs->intf[i].type = PORT_TYPE_PHY_NORMAL;
+ }
+
+ /* Then set the remaining port values for the physical ports. */
+ for (i = 0; i < fpga_info->n_phy_ports; i++) {
+ char name[128];
+
+ if (i > 0 && lag_active) {
+ /*
+ * Secondary link aggregated port. Just display the "internals" values
+ * from port 0
+ */
+ rte_eth_dev_get_name_by_port(0, name);
+ } else {
+ rte_eth_dev_get_name_by_port(i, name);
+ }
+ struct rte_eth_dev *eth_dev = rte_eth_dev_get_by_name(name);
+
+ struct pmd_internals *internals =
+ (struct pmd_internals *)eth_dev->data->dev_private;
+ struct adapter_info_s *p_adapter_info =
+ &adap->drv->ntdrv.adapter_info;
+
+ ifs->intf[i].port_id = i;
+ ifs->intf[i].pci_id.domain = internals->pci_dev->addr.domain;
+ ifs->intf[i].pci_id.bus = internals->pci_dev->addr.bus;
+ ifs->intf[i].pci_id.devid = internals->pci_dev->addr.devid;
+ ifs->intf[i].pci_id.function =
+ internals->pci_dev->addr.function;
+ ifs->intf[i].pci_id.pad = 0;
+
+ const bool port_link_status =
+ nt4ga_port_get_link_status(p_adapter_info, i);
+ ifs->intf[i].link = port_link_status ? PORT_LINK_UP :
+ PORT_LINK_DOWN;
+
+ const nt_link_speed_t port_link_speed =
+ nt4ga_port_get_link_speed(p_adapter_info, i);
+ ifs->intf[i].port_speed = read_link_speed(port_link_speed);
+
+ const bool port_adm_state =
+ nt4ga_port_get_adm_state(p_adapter_info, i);
+ if (!port_adm_state) {
+ ifs->intf[i].port_state = PORT_STATE_DISABLED;
+ } else {
+ const bool port_nim_present =
+ nt4ga_port_get_nim_present(p_adapter_info, i);
+ if (port_nim_present) {
+ ifs->intf[i].port_state =
+ PORT_STATE_NIM_PRESENT;
+ } else {
+ ifs->intf[i].port_state = PORT_STATE_NIM_ABSENT;
+ }
+ }
+
+ /* MTU */
+ if (i > 0 && lag_active) {
+ /* Secondary link aggregated port. Display same MTU value as port 0 */
+ rte_eth_dev_get_mtu(0, &ifs->intf[i].mtu);
+ } else {
+ rte_eth_dev_get_mtu(i, &ifs->intf[i].mtu);
+ }
+
+ /* MAC */
+ const uint64_t mac =
+ fpga_info->nthw_hw_info.vpd_info.mn_mac_addr_value + i;
+ ifs->intf[i].mac.addr_b[0] = (mac >> 40) & 0xFFu;
+ ifs->intf[i].mac.addr_b[1] = (mac >> 32) & 0xFFu;
+ ifs->intf[i].mac.addr_b[2] = (mac >> 24) & 0xFFu;
+ ifs->intf[i].mac.addr_b[3] = (mac >> 16) & 0xFFu;
+ ifs->intf[i].mac.addr_b[4] = (mac >> 8) & 0xFFu;
+ ifs->intf[i].mac.addr_b[5] = (mac >> 0) & 0xFFu;
+
+ if (i > 0 && lag_active) {
+ /* Secondary link aggregated port. Queues not applicable */
+ ifs->intf[i].num_queues = 0;
+ } else {
+ /* attached hw queues to this interface */
+ unsigned int input_num = internals->nb_rx_queues;
+ /*
+ * These are the "input" queues, meaning these go to host and is attached
+ * to receiving from a port
+ */
+ for (unsigned int ii = 0; ii < input_num; ii++) {
+ ifs->intf[i].queue[ii].idx =
+ internals->rxq_scg[ii].queue.hw_id;
+ ifs->intf[i].queue[ii].dir = QUEUE_INPUT;
+ }
+
+ /*
+ * These are the "output" queues, meaning these go to a virtual port queue
+ * which typically is used by vDPA
+ */
+ for (unsigned int ii = 0; ii < internals->vpq_nb_vq;
+ ii++) {
+ ifs->intf[i].queue[ii + input_num].idx =
+ internals->vpq[ii].hw_id;
+ ifs->intf[i].queue[ii + input_num].dir =
+ QUEUE_OUTPUT;
+ }
+
+ ifs->intf[i].num_queues =
+ input_num + internals->vpq_nb_vq;
+ }
+
+ /* NIM information */
+ nim_i2c_ctx_t nim_ctx =
+ nt4ga_port_get_nim_capabilities(p_adapter_info, i);
+
+ strlcpy((char *)&ifs->intf[i].nim_data.vendor_name,
+ nim_ctx.vendor_name,
+ sizeof(ifs->intf[i].nim_data.vendor_name));
+ strlcpy((char *)&ifs->intf[i].nim_data.prod_no, nim_ctx.prod_no,
+ sizeof(ifs->intf[i].nim_data.prod_no));
+ strlcpy((char *)&ifs->intf[i].nim_data.serial_no,
+ nim_ctx.serial_no,
+ sizeof(ifs->intf[i].nim_data.serial_no));
+ strlcpy((char *)&ifs->intf[i].nim_data.date, nim_ctx.date,
+ sizeof(ifs->intf[i].nim_data.date));
+ strlcpy((char *)&ifs->intf[i].nim_data.rev, nim_ctx.rev,
+ sizeof(ifs->intf[i].nim_data.rev));
+
+ if (nim_ctx.len_info[0] >= 0xFFFF)
+ ifs->intf[i].nim_data.link_length.sm = 0xFFFF;
+ else
+ ifs->intf[i].nim_data.link_length.sm =
+ nim_ctx.len_info[0];
+
+ ifs->intf[i].nim_data.link_length.ebw = nim_ctx.len_info[1];
+ ifs->intf[i].nim_data.link_length.mm50 = nim_ctx.len_info[2];
+ ifs->intf[i].nim_data.link_length.mm62 = nim_ctx.len_info[3];
+ ifs->intf[i].nim_data.link_length.copper = nim_ctx.len_info[4];
+
+ ifs->intf[i].nim_data.pwr_level_req = nim_ctx.pwr_level_req;
+ ifs->intf[i].nim_data.pwr_level_cur = nim_ctx.pwr_level_cur;
+ ifs->intf[i].nim_data.nim_id = nim_ctx.nim_id;
+ ifs->intf[i].nim_data.port_type = nim_ctx.port_type;
+ }
+
+ /* And finally handle the virtual ports. */
+ int rte_eth_dev_virt_port_offset = lag_active ? 1 :
+ fpga_info->n_phy_ports;
+ for (; i < nb_ports; i++, rte_eth_dev_virt_port_offset++) {
+ /* Continue counting from the "i" value reached in the previous for loop */
+ char name[128];
+
+ rte_eth_dev_get_name_by_port(rte_eth_dev_virt_port_offset,
+ name);
+ struct rte_eth_dev *eth_dev = rte_eth_dev_get_by_name(name);
+
+ struct pmd_internals *internals =
+ (struct pmd_internals *)eth_dev->data->dev_private;
+
+ ifs->intf[i].port_id = i;
+ ifs->intf[i].type = PORT_TYPE_VIRT;
+ ifs->intf[i].pci_id.domain = internals->pci_dev->addr.domain;
+ ifs->intf[i].pci_id.bus = internals->pci_dev->addr.bus;
+ ifs->intf[i].pci_id.devid = internals->pci_dev->addr.devid;
+ ifs->intf[i].pci_id.function =
+ internals->pci_dev->addr.function;
+ ifs->intf[i].pci_id.pad = 0;
+
+ ifs->intf[i].port_speed = PORT_LINK_SPEED_NONE_REPORTED;
+ switch (internals->vport_comm) {
+ case VIRT_PORT_NEGOTIATED_NONE:
+ ifs->intf[i].port_state = PORT_STATE_VIRTUAL_UNATTACHED;
+ ifs->intf[i].link = PORT_LINK_DOWN;
+ break;
+ case VIRT_PORT_NEGOTIATED_SPLIT:
+ ifs->intf[i].port_state = PORT_STATE_VIRTUAL_SPLIT;
+ ifs->intf[i].link = PORT_LINK_UP;
+ break;
+ case VIRT_PORT_NEGOTIATED_PACKED:
+ ifs->intf[i].port_state = PORT_STATE_VIRTUAL_PACKED;
+ ifs->intf[i].link = PORT_LINK_UP;
+ break;
+ case VIRT_PORT_USE_RELAY:
+ ifs->intf[i].port_state = PORT_STATE_VIRTUAL_RELAY;
+ ifs->intf[i].link = PORT_LINK_UP;
+ break;
+ }
+
+ /* MTU */
+ rte_eth_dev_get_mtu(rte_eth_dev_virt_port_offset,
+ &ifs->intf[i].mtu);
+
+ /* MAC */
+ for (int ii = 0; ii < 6; ii++) {
+ ifs->intf[i].mac.addr_b[ii] =
+ internals->eth_addrs[0].addr_bytes[ii];
+ }
+
+ /* attached hw queues to this interface */
+ unsigned int input_num = internals->nb_rx_queues;
+
+ /*
+ * These are the "input" queues, meaning these go to host and is attached to
+ * receiving from a port
+ */
+ for (unsigned int ii = 0; ii < input_num; ii++) {
+ ifs->intf[i].queue[ii].idx =
+ internals->rxq_scg[ii].queue.hw_id;
+ ifs->intf[i].queue[ii].dir = QUEUE_INPUT;
+ }
+
+ /*
+ * These are the "output" queues, meaning these go to a virtual port queue
+ * which typically is used by vDPA
+ */
+ unsigned int numq =
+ ((internals->vpq_nb_vq + input_num) > MAX_RSS_QUEUES) ?
+ MAX_RSS_QUEUES - input_num :
+ internals->vpq_nb_vq;
+ for (unsigned int ii = 0; ii < numq; ii++) {
+ ifs->intf[i].queue[ii + input_num].idx =
+ internals->vpq[ii].hw_id;
+ ifs->intf[i].queue[ii + input_num].dir = QUEUE_OUTPUT;
+ }
+ ifs->intf[i].num_queues = input_num + numq;
+ }
+ return REQUEST_OK;
+}
+
+static int func_adapter_get_info(void *hdl, int client_id _unused,
+ struct ntconn_header_s *hdr _unused,
+ char **data, int *len)
+{
+ struct adap_hdl_s *adap = (struct adap_hdl_s *)hdl;
+ fpga_info_t *fpga_info = &adap->drv->ntdrv.adapter_info.fpga_info;
+
+ *len = sizeof(struct ntc_adap_get_info_s);
+ *data = malloc(*len);
+ if (!*data) {
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "memory allocation failed");
+ return REQUEST_ERR;
+ }
+
+ snprintf(*data, 31, "%03d-%04d-%02d-%02d", fpga_info->n_fpga_type_id,
+ fpga_info->n_fpga_prod_id, fpga_info->n_fpga_ver_id,
+ fpga_info->n_fpga_rev_id);
+
+ return REQUEST_OK;
+}
+
+static int func_adapter_get_sensors(void *hdl, int client_id _unused,
+ struct ntconn_header_s *hdr _unused,
+ char **data, int *len)
+{
+ struct adapter_info_s *adapter =
+ &(((struct adap_hdl_s *)hdl)->drv->ntdrv.adapter_info);
+ struct sensor *sensor_ptr = NULL;
+ uint16_t sensors_num = 0;
+ uint8_t *sensors = NULL;
+ struct ntc_sensors_s sensors_info = {
+ .adapter_sensors_cnt = adapter->adapter_sensors_cnt,
+ .ports_cnt = adapter->fpga_info.n_phy_ports
+ };
+ memcpy(sensors_info.adapter_name, adapter->p_dev_name, 24);
+
+ /* Set a sum of sensor`s counters */
+ sensors_num = adapter->adapter_sensors_cnt;
+ for (int i = 0; i < adapter->fpga_info.n_phy_ports; i++) {
+ sensors_num += adapter->nim_sensors_cnt[i];
+ sensors_info.nim_sensors_cnt[i] = adapter->nim_sensors_cnt[i];
+ }
+
+ *len = sizeof(struct ntc_sensors_s) +
+ sensors_num * sizeof(struct sensor);
+
+ /* Allocate memory for sensors array */
+ sensors = malloc(*len);
+ if (!sensors) {
+ NT_LOG(ERR, NTCONNECT, "memory allocation failed");
+ *len = 0;
+ return REQUEST_ERR;
+ }
+ memcpy(sensors, &sensors_info, sizeof(struct ntc_sensors_s));
+ sensor_ptr = (struct sensor *)(sensors + sizeof(struct ntc_sensors_s));
+
+ /* Fetch adapter sensors */
+ for (struct nt_sensor_group *ptr = adapter->adapter_sensors;
+ ptr != NULL; ptr = ptr->next) {
+ sensor_ptr->current_value = ptr->sensor->info.value;
+ sensor_ptr->min_value = ptr->sensor->info.value_lowest;
+ sensor_ptr->max_value = ptr->sensor->info.value_highest;
+ sensor_ptr->sign = ptr->sensor->si;
+ sensor_ptr->type = ptr->sensor->info.type;
+ memcpy(sensor_ptr->name, ptr->sensor->info.name, 50);
+ sensor_ptr++;
+ }
+
+ /* Fetch NIM sensors */
+ for (int i = 0; i < adapter->fpga_info.n_phy_ports; i++) {
+ for (struct nim_sensor_group *ptr = adapter->nim_sensors[i];
+ ptr != NULL; ptr = ptr->next) {
+ sensor_ptr->current_value = ptr->sensor->info.value;
+ sensor_ptr->min_value = ptr->sensor->info.value_lowest;
+ sensor_ptr->max_value = ptr->sensor->info.value_highest;
+ sensor_ptr->sign = ptr->sensor->si;
+ sensor_ptr->type = ptr->sensor->info.type;
+
+ memcpy(sensor_ptr->name, ptr->sensor->info.name,
+ (strlen(ptr->sensor->info.name) >= 50) ?
+ 50 :
+ strlen(ptr->sensor->info.name));
+ sensor_ptr++;
+ }
+ }
+
+ /* Send response */
+ *data = (char *)sensors;
+
+ return REQUEST_OK;
+}
+
+static int set_port_enable(struct adap_hdl_s *adap, int port_nr)
+{
+ adapter_info_t *p_adapter_info = &adap->drv->ntdrv.adapter_info;
+
+ nt4ga_port_set_adm_state(p_adapter_info, port_nr, true);
+
+ return REQUEST_OK;
+}
+
+static int set_port_disable(struct adap_hdl_s *adap, int port_nr)
+{
+ adapter_info_t *p_adapter_info = &adap->drv->ntdrv.adapter_info;
+
+ nt4ga_port_set_adm_state(p_adapter_info, port_nr, false);
+
+ return REQUEST_OK;
+}
+
+static int set_link_up(struct adap_hdl_s *adap, int portid)
+{
+ struct adapter_info_s *p_adapter_info = &adap->drv->ntdrv.adapter_info;
+
+ const bool link_status =
+ nt4ga_port_get_link_status(p_adapter_info, portid);
+
+ if (!link_status) {
+ nt4ga_port_set_link_status(p_adapter_info, portid, true);
+ NT_LOG(DBG, NTCONNECT, "Port %i: Link set to be up\n", portid);
+ } else {
+ NT_LOG(DBG, NTCONNECT,
+ "Port %i: Link is already set to be up\n", portid);
+ }
+
+ return REQUEST_OK;
+}
+
+static int set_link_down(struct adap_hdl_s *adap, int portid)
+{
+ struct adapter_info_s *p_adapter_info = &adap->drv->ntdrv.adapter_info;
+
+ const bool link_status =
+ nt4ga_port_get_link_status(p_adapter_info, portid);
+
+ if (!link_status) {
+ NT_LOG(DBG, NTCONNECT,
+ "Port %i: Link is already set to be down\n", portid);
+ } else {
+ nt4ga_port_set_link_status(p_adapter_info, portid, false);
+ NT_LOG(DBG, NTCONNECT, "Port %i: Link set to be down\n",
+ portid);
+ }
+
+ return REQUEST_OK;
+}
+
+static int set_link_speed(struct adap_hdl_s *adap, int portid, char *speed_str,
+ char **data, int *len)
+{
+ struct adapter_info_s *p_adapter_info = &adap->drv->ntdrv.adapter_info;
+
+ const bool port_adm_state =
+ nt4ga_port_get_adm_state(p_adapter_info, portid);
+ if (!port_adm_state) {
+ const nt_link_speed_t speed = convert_link_speed(speed_str);
+
+ if (speed != NT_LINK_SPEED_UNKNOWN) {
+ nt4ga_port_set_link_speed(p_adapter_info, portid, speed);
+ NT_LOG(DBG, NTCONNECT, "Port %i: set link speed - %s\n",
+ portid, speed_str);
+ } else {
+ return ntconn_error(data, len, this_module_name,
+ NTCONN_ERR_CODE_MISSING_INVALID_PARAM);
+ }
+ } else {
+ NT_LOG(DBG, NTCONNECT,
+ "Port %i: fail to set link speed, port is enabled\n",
+ portid);
+ return ntconn_reply_status(data, len,
+ NTCONN_ADAPTER_ERR_WRONG_LINK_STATE);
+ }
+
+ return REQUEST_OK;
+}
+
+static int set_loopback_mode(struct adap_hdl_s *adap, int portid, int mode)
+{
+ struct adapter_info_s *p_adapter_info = &adap->drv->ntdrv.adapter_info;
+
+ NT_LOG(DBG, NTCONNECT, "Port %i: set loopback mode %i\n", portid, mode);
+ nt4ga_port_set_loopback_mode(p_adapter_info, portid, mode);
+ return REQUEST_OK;
+}
+
+static int set_tx_power(struct adap_hdl_s *adap, int portid, bool disable,
+ char **data, int *len)
+{
+ struct adapter_info_s *p_adapter_info = &adap->drv->ntdrv.adapter_info;
+
+ NT_LOG(DBG, NTCONNECT, "Port %i: set tx_power %i\n", portid, disable);
+ if (nt4ga_port_tx_power(p_adapter_info, portid, disable)) {
+ NT_LOG(DBG, NTCONNECT,
+ "Port %i: ERROR while changing tx_power\n", portid);
+ return ntconn_reply_status(data, len,
+ NTCONN_ADAPTER_ERR_TX_POWER_FAIL);
+ }
+ return REQUEST_OK;
+}
+
+static int func_adapter_set_interface(void *hdl, int client_id _unused,
+ struct ntconn_header_s *hdr _unused,
+ char **data, int *len)
+{
+ struct adap_hdl_s *adap = (struct adap_hdl_s *)hdl;
+ char *saveptr;
+ int port_nr;
+ int length;
+ char *tok;
+
+ *len = 0;
+
+ /*
+ * This will receive the request strings starting with "adapter;set,interface,...."
+ * so in the situation of a request like: "adapter,set,interface,port0,link_speed=10G"
+ * the remainder of the command "port0,link_speed=10G" will be pointed to by *data,
+ * zero-terminated on entry
+ */
+
+ if (!(data && *data))
+ return ntconn_error(data, len, this_module_name,
+ NTCONN_ERR_CODE_INVALID_REQUEST);
+
+ /* OK to modify *data */
+ tok = strtok_r(*data, ",", &saveptr);
+ if (!tok)
+ return ntconn_error(data, len, this_module_name,
+ NTCONN_ERR_CODE_MISSING_INVALID_PARAM);
+
+ length = strlen(tok);
+
+ if (!(length > 4 && memcmp(tok, "port", 4) == 0))
+ return ntconn_error(data, len, this_module_name,
+ NTCONN_ERR_CODE_MISSING_INVALID_PARAM);
+
+ port_nr = atoi(tok + 4);
+
+ /* Only set on phy ports */
+ if (port_nr < adap->drv->ntdrv.adapter_info.fpga_info.n_phy_ports)
+ return ntconn_error(data, len, this_module_name,
+ NTCONN_ERR_CODE_MISSING_INVALID_PARAM);
+
+ tok = strtok_r(NULL, "=,", &saveptr);
+ if (!tok)
+ return ntconn_error(data, len, this_module_name,
+ NTCONN_ERR_CODE_MISSING_INVALID_PARAM);
+ if (strcmp(tok, "link_speed") == 0) {
+ tok = strtok_r(NULL, ",", &saveptr);
+ if (!tok)
+ return ntconn_error(data, len, this_module_name,
+ NTCONN_ERR_CODE_MISSING_INVALID_PARAM);
+ return set_link_speed(adap, port_nr, tok, data, len);
+ } else if (strcmp(tok, "enable") == 0) {
+ return set_port_enable(adap, port_nr);
+ } else if (strcmp(tok, "disable") == 0) {
+ return set_port_disable(adap, port_nr);
+ } else if (strcmp(tok, "link_state") == 0) {
+ tok = strtok_r(NULL, ",", &saveptr);
+ if (!tok)
+ return ntconn_error(data, len, this_module_name,
+ NTCONN_ERR_CODE_MISSING_INVALID_PARAM);
+ if (strcmp(tok, "up") == 0)
+ return set_link_up(adap, port_nr);
+ else if (strcmp(tok, "down") == 0)
+ return set_link_down(adap, port_nr);
+ } else if (strcmp(tok, "host_loopback") == 0) {
+ tok = strtok_r(NULL, ",", &saveptr);
+ if (!tok)
+ return ntconn_error(data, len, this_module_name,
+ NTCONN_ERR_CODE_MISSING_INVALID_PARAM);
+ if (strcmp(tok, "on") == 0)
+ return set_loopback_mode(adap, port_nr,
+ NT_LINK_LOOPBACK_HOST);
+ else if (strcmp(tok, "off") == 0)
+ return set_loopback_mode(adap, port_nr,
+ NT_LINK_LOOPBACK_OFF);
+ } else if (strcmp(tok, "line_loopback") == 0) {
+ tok = strtok_r(NULL, ",", &saveptr);
+ if (!tok)
+ return ntconn_error(data, len, this_module_name,
+ NTCONN_ERR_CODE_MISSING_INVALID_PARAM);
+ if (strcmp(tok, "on") == 0)
+ return set_loopback_mode(adap, port_nr,
+ NT_LINK_LOOPBACK_LINE);
+ else if (strcmp(tok, "off") == 0)
+ return set_loopback_mode(adap, port_nr,
+ NT_LINK_LOOPBACK_OFF);
+ } else if (strcmp(tok, "tx_power") == 0) {
+ tok = strtok_r(NULL, ",", &saveptr);
+ if (!tok)
+ return ntconn_error(data, len, this_module_name,
+ NTCONN_ERR_CODE_MISSING_INVALID_PARAM);
+ if (strcmp(tok, "on") == 0)
+ return set_tx_power(adap, port_nr, false, data, len);
+ else if (strcmp(tok, "off") == 0)
+ return set_tx_power(adap, port_nr, true, data, len);
+ }
+
+ /* Should return 0 on success */
+ return ntconn_error(data, len, this_module_name,
+ NTCONN_ERR_CODE_MISSING_INVALID_PARAM);
+}
+
+static int func_adapter_set_adapter(void *hdl _unused, int client_id _unused,
+ struct ntconn_header_s *hdr _unused,
+ char **data, int *len)
+{
+ if (data && *data) {
+ NT_LOG(DBG, NTCONNECT,
+ "Set adapter: Command: %s\n", *data);
+ }
+
+ *len = 0;
+
+ /* Should return 0 on success */
+ return ntconn_error(data, len, this_module_name,
+ NTCONN_ERR_CODE_NOT_YET_IMPLEMENTED);
+}
+
+static int adap_request(void *hdl, int client_id _unused,
+ struct ntconn_header_s *hdr, char *function,
+ char **data, int *len)
+{
+ return execute_function(this_module_name, hdl, client_id, hdr, function,
+ adapter_entry_funcs, data, len, 0);
+}
+
+static void adap_free_data(void *hdl _unused, char *data)
+{
+ free(data);
+}
+
+static void adap_client_cleanup(void *hdl _unused, int client_id _unused)
+{
+ /* Nothing to do */
+}
+
+static const ntconnapi_t ntconn_adap_op = { this_module_name,
+ NTCONN_ADAP_VERSION_MAJOR,
+ NTCONN_ADAP_VERSION_MINOR,
+ adap_request,
+ adap_free_data,
+ adap_client_cleanup
+ };
+
+int ntconn_adap_register(struct drv_s *drv)
+{
+ int i;
+
+ for (i = 0; i < MAX_ADAPTERS; i++) {
+ if (adap_hdl[i].drv == NULL)
+ break;
+ }
+ if (i == MAX_ADAPTERS) {
+ NT_LOG(ERR, NTCONNECT,
+ "Cannot register more adapters into NtConnect framework");
+ return -1;
+ }
+
+ adap_hdl[i].drv = drv;
+ return register_ntconn_mod(&drv->p_dev->addr, (void *)&adap_hdl[i],
+ &ntconn_adap_op);
+}
diff --git a/drivers/net/ntnic/ntconnect_modules/ntconn_flow.c b/drivers/net/ntnic/ntconnect_modules/ntconn_flow.c
new file mode 100644
index 0000000000..3d81242524
--- /dev/null
+++ b/drivers/net/ntnic/ntconnect_modules/ntconn_flow.c
@@ -0,0 +1,1312 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include <errno.h>
+#include "ntnic_ethdev.h"
+#include "ntconnect.h"
+#include "ntos_system.h"
+#include "ntconn_modules.h"
+#include "ntconn_mod_helper.h"
+#include "nt_util.h"
+#include "ntlog.h"
+#include "ntnic_vf_vdpa.h"
+
+#include "ntconnect_api_flow.h"
+#include "ntconnect_api_meter.h"
+#include "stream_binary_flow_api.h"
+
+#include <rte_errno.h>
+#include "flow_api.h"
+
+#define DEBUG_FLOW 1
+
+#define NTCONN_FLOW_VERSION_MAJOR 0U
+#define NTCONN_FLOW_VERSION_MINOR 1U
+
+#define this_module_name "filter"
+
+#define IN_PORT_TOK "in_port="
+#define VPATH_TOK "vpath="
+
+#define MAX_CLIENTS 32
+
+#define UNUSED __rte_unused
+
+static struct flow_hdl_s {
+ struct drv_s *drv;
+} flow_hdl[MAX_CLIENTS];
+
+#define MAX_PORTS 64
+static struct port_to_eth_s {
+ struct flow_eth_dev *flw_dev;
+ uint32_t forced_vlan_vid;
+ uint32_t caller_id;
+} port_eth[MAX_PORTS];
+
+static ntconn_err_t ntconn_err[] = {
+ { NTCONN_FLOW_ERR_NONE, "Success" },
+ { NTCONN_FLOW_ERR_INTERNAL_ERROR, "Internal error" },
+ { NTCONN_FLOW_ERR_PORT_IS_NOT_INITIALIZED, "Port is not initialized" },
+ { NTCONN_FLOW_ERR_UNEXPECTED_VIRTIO_PATH, "Unexpected virtio path" },
+ { NTCONN_FLOW_ERR_TO_MANY_FLOWS, "To many flows" },
+ { NTCONN_FLOW_ERR_INVALID_PORT, "Invalid port" },
+ { NTCONN_FLOW_ERR_NOT_YET_IMPLEMENTED, "Function not yet implemented" },
+ { NTCONN_FLOW_ERR_UNSUPPORTED_ADAPTER, "Adapter is not supported" },
+ { NTCONN_FLOW_ERR_NO_VF_QUEUES, "No queues for the VF is found" },
+ { -1, NULL }
+};
+
+static const char *get_error_msg(enum ntconn_flow_err_e err_code)
+{
+ int idx = 0;
+
+ while (ntconn_err[idx].err_code != (uint32_t)-1 &&
+ ntconn_err[idx].err_code != err_code)
+ idx++;
+ if (ntconn_err[idx].err_code == (uint32_t)-1)
+ idx = 1;
+
+ return ntconn_err[idx].err_text;
+}
+
+static inline int ntconn_flow_err_reply_status(char **data, int *len,
+ enum ntconn_flow_err_e code,
+ int err)
+{
+ *data = malloc(sizeof(struct flow_return_s));
+ if (*data) {
+ struct flow_return_s *return_value =
+ (struct flow_return_s *)*data;
+ *len = sizeof(struct flow_return_s);
+ return_value->status = err;
+ return_value->type = FLOW_ERROR_GENERAL;
+ const char *err_msg = get_error_msg(code);
+
+ memcpy(return_value->err_msg, err_msg,
+ RTE_MIN(strlen(err_msg), ERR_MSG_LEN));
+ return REQUEST_OK;
+ }
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "Not able to allocate memory");
+ return REQUEST_ERR;
+}
+
+static inline int ntconn_flow_err_status(char **data, int *len, int err)
+{
+ *data = malloc(sizeof(struct flow_return_s));
+ if (*data) {
+ struct flow_return_s *return_value =
+ (struct flow_return_s *)*data;
+ *len = sizeof(struct flow_return_s);
+ return_value->status = err;
+ return_value->type = FLOW_ERROR_GENERAL;
+ const char *err_msg =
+ get_error_msg(NTCONN_FLOW_ERR_INTERNAL_ERROR);
+ strlcpy(return_value->err_msg, err_msg, ERR_MSG_LEN);
+ return REQUEST_OK;
+ }
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "Not able to allocate memory");
+ return REQUEST_ERR;
+}
+
+/*
+ * Filter functions
+ */
+static int func_flow_create(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char **data, int *len);
+static int func_flow_validate(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char **data,
+ int *len);
+static int func_flow_destroy(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char **data,
+ int *len);
+static int func_flow_flush(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char **data, int *len);
+static int func_flow_query(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char **data, int *len);
+static int func_flow_setport(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char **data,
+ int *len);
+static struct func_s adapter_entry_funcs[] = {
+ { "setport", NULL, func_flow_setport },
+ { "create", NULL, func_flow_create },
+ { "validate", NULL, func_flow_validate },
+ { "destroy", NULL, func_flow_destroy },
+ { "flush", NULL, func_flow_flush },
+ { "query", NULL, func_flow_query },
+ { NULL, NULL, NULL },
+};
+
+static int copy_return_status(char **data, int *len, int status,
+ struct flow_error *error)
+{
+ *data = malloc(sizeof(struct flow_return_s));
+ if (*data) {
+ struct flow_return_s *return_value =
+ (struct flow_return_s *)*data;
+ *len = sizeof(struct flow_return_s);
+
+ return_value->status = status;
+ return_value->type = error->type;
+ strlcpy(return_value->err_msg, error->message, ERR_MSG_LEN);
+ return REQUEST_OK;
+ }
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "Not able to allocate memory %s",
+ __func__);
+ return REQUEST_ERR;
+}
+
+static void set_error(struct flow_error *error)
+{
+ error->type = FLOW_ERROR_SUCCESS;
+ error->message = "Operation successfully completed";
+}
+
+static int func_flow_setport(void *hdl _unused, int client_id _unused,
+ struct ntconn_header_s *hdr _unused, char **data,
+ int *len)
+{
+ uint32_t i;
+ struct flow_error error;
+ uint32_t nb_port;
+ uint8_t in_port = MAX_PORTS;
+ char vpath[MAX_PATH_LEN];
+ char *saveptr;
+
+ set_error(&error);
+
+ nb_port = rte_eth_dev_count_avail();
+
+#ifdef DEBUG_FLOW
+ NT_LOG(DBG, NTCONNECT, "%s: \"%s\"\n", __func__, *data);
+ NT_LOG(DBG, NTCONNECT, "Number of ports: %u\n", nb_port);
+#endif
+
+ char *tok = strtok_r(*data, ",", &saveptr);
+
+ if (tok) {
+ size_t length = strlen(tok);
+ if (length > strlen(IN_PORT_TOK) && memcmp(tok, IN_PORT_TOK,
+ strlen(IN_PORT_TOK)) == 0)
+ in_port = atoi(tok + strlen(IN_PORT_TOK));
+ }
+#ifdef DEBUG_FLOW
+ NT_LOG(DBG, NTCONNECT, "in_port: %u\n", in_port);
+#endif
+
+ tok = strtok_r(NULL, ",", &saveptr);
+ if (tok) {
+ size_t length = strlen(tok);
+ if (length > strlen(VPATH_TOK) && memcmp(tok, VPATH_TOK, strlen(VPATH_TOK)) == 0)
+ strlcpy(vpath, tok + strlen(VPATH_TOK), MAX_PATH_LEN);
+ }
+#ifdef DEBUG_FLOW
+ NT_LOG(DBG, NTCONNECT, "vpath: %s\n", vpath);
+#endif
+
+ /* Check that the wanted ports are valid ports */
+ if (in_port >= nb_port) {
+ NT_LOG(ERR, NTCONNECT, "port out of range");
+ return ntconn_flow_err_status(data, len, ENODEV);
+ }
+
+ struct pmd_internals *vp_internals = vp_path_instance_ready(vpath);
+
+ if (!vp_internals) {
+ NT_LOG(ERR, NTCONNECT, "Failed to get VF device");
+ return ntconn_flow_err_status(data, len, ENODEV);
+ }
+
+ /* Get flow device */
+ port_eth[in_port].flw_dev = vp_internals->flw_dev;
+
+ if (port_eth[in_port].flw_dev == NULL) {
+ NT_LOG(ERR, NTCONNECT, "Failed to get eth device");
+ return ntconn_flow_err_status(data, len, ENODEV);
+ }
+
+ /* Only INLINE is supported */
+ if (vp_internals->flw_dev->ndev->flow_profile !=
+ FLOW_ETH_DEV_PROFILE_INLINE) {
+ /* Only inline profile is supported */
+ NT_LOG(ERR, NTCONNECT, "Adapter is not supported");
+ return ntconn_flow_err_status(data, len, ENODEV);
+ }
+
+ if (vp_internals->vpq_nb_vq == 0) {
+ NT_LOG(ERR, NTCONNECT, "No queues for the VF is found");
+ return ntconn_flow_err_status(data, len, ENODEV);
+ }
+
+ /* Server and client must agree of the virtual port number */
+ if (vp_internals->port != (in_port + 4U)) {
+ NT_LOG(ERR, NTCONNECT,
+ "Internal error: Virtual port out of sync");
+ return ntconn_flow_err_status(data, len, ENODEV);
+ }
+
+#ifdef DEBUG_FLOW
+ NT_LOG(DBG, NTCONNECT, "vport: %u\n", vp_internals->port);
+ NT_LOG(DBG, NTCONNECT, "vlan (forced): %u\n", vp_internals->vlan);
+#endif
+
+ port_eth[in_port].caller_id = vp_internals->port;
+ port_eth[in_port].forced_vlan_vid = vp_internals->vlan;
+
+ *data = malloc(sizeof(struct flow_setport_return));
+ if (*data) {
+ struct flow_setport_return *return_value =
+ (struct flow_setport_return *)*data;
+ *len = sizeof(struct flow_setport_return);
+ return_value->num_queues = vp_internals->vpq_nb_vq;
+
+#ifdef DEBUG_FLOW
+ NT_LOG(DBG, NTCONNECT, "Number of queues: %u\n",
+ vp_internals->vpq_nb_vq);
+#endif
+ for (i = 0; i < vp_internals->vpq_nb_vq && i < MAX_QUEUES;
+ i++) {
+#ifdef DEBUG_FLOW
+ NT_LOG(DBG, NTCONNECT, "Queue: %u\n",
+ vp_internals->vpq[i].id);
+ NT_LOG(DBG, NTCONNECT, "HW ID: %u\n",
+ vp_internals->vpq[i].hw_id);
+#endif
+ return_value->queues[i].id = vp_internals->vpq[i].id;
+ return_value->queues[i].hw_id =
+ vp_internals->vpq[i].hw_id;
+#ifdef DEBUG_FLOW
+ NT_LOG(DBG, NTCONNECT,
+ "Setup output port: %u, %04x:%02x:%02x.%x\n",
+ in_port, vp_internals->pci_dev->addr.domain,
+ vp_internals->pci_dev->addr.bus,
+ vp_internals->pci_dev->addr.devid,
+ vp_internals->pci_dev->addr.function);
+#endif
+ }
+ return REQUEST_OK;
+ }
+ *len = 0;
+ return REQUEST_ERR;
+}
+
+static int func_flow_flush(void *hdl _unused, int client_id _unused,
+ struct ntconn_header_s *hdr _unused, char **data,
+ int *len)
+{
+ struct flow_error error;
+ int port = MAX_PORTS;
+ int status = -1;
+ char *saveptr;
+
+ set_error(&error);
+
+#ifdef DEBUG_FLOW
+ NT_LOG(DBG, NTCONNECT, "%s: [%s:%u] enter\n", __func__, __FILE__, __LINE__);
+#endif
+
+ char *tok = strtok_r(*data, ",", &saveptr);
+
+ if (tok) {
+ int length = strlen(tok);
+
+ if (length > 5 && memcmp(tok, "port=", 5) == 0)
+ port = atoi(tok + 5);
+ }
+#ifdef DEBUG_FLOW
+ NT_LOG(DBG, NTCONNECT, "Port id=%u\n", port);
+#endif
+
+ if (port >= MAX_PORTS) {
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "port id out of range");
+ return ntconn_flow_err_reply_status(data, len,
+ NTCONN_FLOW_ERR_INVALID_PORT,
+ ENODEV);
+ }
+
+ /* Call filter with data */
+ status = flow_flush(port_eth[port].flw_dev, &error);
+ return copy_return_status(data, len, status, &error);
+}
+
+static int func_flow_destroy(void *hdl _unused, int client_id _unused,
+ struct ntconn_header_s *hdr, char **data, int *len)
+{
+ struct flow_error error;
+ int port = MAX_PORTS;
+ uint64_t flow = 0;
+ int status = -1;
+
+ struct destroy_flow_ntconnect *flow_cpy =
+ (struct destroy_flow_ntconnect *)&(*data)[hdr->len];
+
+ if (hdr->blob_len != sizeof(struct destroy_flow_ntconnect)) {
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "Error in filter data");
+ return ntconn_error(data, len, this_module_name,
+ NTCONN_ERR_CODE_INVALID_REQUEST);
+ }
+
+#ifdef DEBUG_FLOW1
+ NT_LOG(DBG, NTCONNECT, "%s: [%s:%u] enter\n", __func__, __FILE__, __LINE__);
+#endif
+
+ port = flow_cpy->port;
+
+#ifdef DEBUG_FLOW1
+ NT_LOG(DBG, NTCONNECT, "Port id=%u\n", port);
+#endif
+
+ if (port >= MAX_PORTS) {
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "port id out of range");
+ return ntconn_flow_err_reply_status(data, len,
+ NTCONN_FLOW_ERR_INVALID_PORT,
+ ENODEV);
+ }
+
+ flow = flow_cpy->flow;
+
+#ifdef DEBUG_FLOW1
+ NT_LOG(DBG, NTCONNECT, "flow=0x%016llX\n",
+ (unsigned long long)flow);
+#endif
+
+ /* Call filter with data */
+ status = flow_destroy(port_eth[port].flw_dev,
+ (struct flow_handle *)flow, &error);
+
+ *data = malloc(sizeof(struct flow_return_s));
+ if (*data) {
+ struct flow_return_s *return_value =
+ (struct flow_return_s *)*data;
+ *len = sizeof(struct flow_return_s);
+
+ return_value->status = status;
+ return_value->type = error.type;
+ strlcpy(return_value->err_msg, error.message, ERR_MSG_LEN);
+ return REQUEST_OK;
+ }
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "Not able to allocate memory %s",
+ __func__);
+ return REQUEST_ERR;
+}
+
+enum {
+ FLOW_API_FUNC_CREATE,
+ FLOW_API_FUNC_VALIDATE,
+};
+
+static uint64_t make_flow_create(int func, int port,
+ struct create_flow_ntconnect *flow_cpy,
+ int *status, struct flow_error *error)
+{
+ struct flow_elem elem[MAX_FLOW_STREAM_ELEM];
+ struct flow_action action[MAX_FLOW_STREAM_ELEM];
+ struct flow_action_vxlan_encap vxlan_tun;
+ struct flow_action_raw_encap encap;
+ struct flow_action_raw_decap decap;
+ struct flow_elem elem_tun[MAX_FLOW_STREAM_VXLAN_TUN_ELEM];
+ int idx = -1;
+
+ struct flow_attr *attr = &flow_cpy->attr;
+ struct flow_elem_cpy *elem_cpy = flow_cpy->elem;
+ struct flow_action_cpy *action_cpy = flow_cpy->action;
+
+ error->type = FLOW_ERROR_GENERAL;
+ error->message = "To many flows";
+ *status = NTCONN_FLOW_ERR_TO_MANY_FLOWS;
+
+ attr->caller_id = port_eth[port].caller_id;
+ attr->forced_vlan_vid = port_eth[port].forced_vlan_vid;
+
+ do {
+ idx++;
+ if (idx > MAX_FLOW_STREAM_ELEM)
+ goto error;
+ elem[idx].type = elem_cpy[idx].type;
+ if (!elem_cpy[idx].spec_cpy.valid) {
+ elem[idx].spec = NULL;
+ } else {
+ elem[idx].spec =
+ (void *)&elem_cpy[idx].spec_cpy.u.start_addr;
+ }
+ if (!elem_cpy[idx].mask_cpy.valid) {
+ elem[idx].mask = NULL;
+ } else {
+ elem[idx].mask =
+ (void *)&elem_cpy[idx].mask_cpy.u.start_addr;
+ }
+ } while (elem_cpy[idx].type != FLOW_ELEM_TYPE_END);
+
+ idx = -1;
+ do {
+ idx++;
+ if (idx > MAX_FLOW_STREAM_ELEM)
+ goto error;
+ action[idx].type = action_cpy[idx].type;
+ if (!action_cpy[idx].conf_cpy.valid) {
+ action[idx].conf = NULL;
+ } else {
+ switch (action_cpy[idx].type) {
+ case FLOW_ACTION_TYPE_VXLAN_ENCAP: {
+ /*
+ * Special VXLAN ENCAP treatment create inner tunnel
+ * elements in action
+ */
+ struct flow_elem_cpy *tun_elem_cpy =
+ (struct flow_elem_cpy *)action_cpy[idx]
+ .conf_cpy.u.vxlan.vxlan_tunnel;
+ vxlan_tun.vxlan_tunnel = elem_tun;
+ int tun_idx = -1;
+
+ do {
+ tun_idx++;
+ if (tun_idx >
+ MAX_FLOW_STREAM_VXLAN_TUN_ELEM) {
+ error->message =
+ "To many VXLAN tunnels";
+ goto error;
+ }
+ elem_tun[tun_idx].type =
+ tun_elem_cpy[tun_idx].type;
+ if (!tun_elem_cpy[tun_idx]
+ .spec_cpy.valid) {
+ elem_tun[tun_idx].spec = NULL;
+ } else {
+ elem_tun[tun_idx].spec =
+ (void *)&tun_elem_cpy[tun_idx]
+ .spec_cpy.u
+ .start_addr;
+ }
+ if (!tun_elem_cpy[tun_idx]
+ .mask_cpy.valid) {
+ elem_tun[tun_idx].mask = NULL;
+ } else {
+ elem_tun[tun_idx].mask =
+ (void *)&tun_elem_cpy[tun_idx]
+ .mask_cpy.u
+ .start_addr;
+ }
+ } while (tun_elem_cpy[tun_idx].type !=
+ FLOW_ELEM_TYPE_END);
+ /* VXLAN ENCAP tunnel finished */
+ action[idx].conf = &vxlan_tun;
+ }
+ break;
+ case FLOW_ACTION_TYPE_RSS: {
+ /* Need to set queue pointer */
+ action_cpy[idx].conf_cpy.u.rss.rss.queue =
+ (const uint16_t *)&action_cpy[idx]
+ .conf_cpy.u.rss.cpy_queue;
+ action[idx].conf = (void *)&action_cpy[idx]
+ .conf_cpy.u.rss.rss;
+ }
+ break;
+ case FLOW_ACTION_TYPE_METER: {
+ /* Need to convert meter ID to uniq ID for the VF */
+ action_cpy[idx].conf_cpy.u.meter.mtr_id =
+ ((flow_mtr_meters_supported() /
+ (RTE_MAX_ETHPORTS - 2)) *
+ (flow_cpy->vport - 4)) +
+ action_cpy[idx].conf_cpy.u.meter.mtr_id;
+ action[idx].conf = (void *)&action_cpy[idx]
+ .conf_cpy.u.meter;
+ }
+ break;
+ case FLOW_ACTION_TYPE_RAW_ENCAP: {
+ encap.preserve = NULL;
+ encap.data =
+ action_cpy[idx].conf_cpy.u.encap.data;
+ encap.item_count =
+ action_cpy[idx]
+ .conf_cpy.u.encap.item_count;
+ encap.size =
+ action_cpy[idx].conf_cpy.u.encap.size;
+
+ for (int eidx = 0;
+ eidx <
+ action_cpy[idx].conf_cpy.u.encap.item_count;
+ eidx++) {
+ if (eidx > RAW_ENCAP_DECAP_ELEMS_MAX) {
+ error->message =
+ "To many encap items";
+ goto error;
+ }
+ encap.items[eidx].type =
+ action_cpy[idx]
+ .conf_cpy.u.encap
+ .item_cpy[eidx]
+ .type;
+ if (action_cpy[idx]
+ .conf_cpy.u.encap
+ .item_cpy[eidx]
+ .spec_cpy.valid) {
+ encap.items[eidx].spec =
+ (void *)&action_cpy[idx]
+ .conf_cpy.u
+ .encap
+ .item_cpy[eidx]
+ .spec_cpy.u
+ .start_addr;
+ } else {
+ encap.items[eidx].spec = NULL;
+ }
+ if (action_cpy[idx]
+ .conf_cpy.u.encap
+ .item_cpy[eidx]
+ .mask_cpy.valid) {
+ encap.items[eidx].mask =
+ (void *)&action_cpy[idx]
+ .conf_cpy.u
+ .encap
+ .item_cpy[eidx]
+ .mask_cpy.u
+ .start_addr;
+ } else {
+ encap.items[eidx].mask = NULL;
+ }
+ }
+ action[idx].conf = &encap;
+ }
+ break;
+ case FLOW_ACTION_TYPE_RAW_DECAP: {
+ decap.data =
+ action_cpy[idx].conf_cpy.u.decap.data;
+ decap.item_count =
+ action_cpy[idx]
+ .conf_cpy.u.decap.item_count;
+ decap.size =
+ action_cpy[idx].conf_cpy.u.decap.size;
+
+ for (int eidx = 0;
+ eidx <
+ action_cpy[idx].conf_cpy.u.decap.item_count;
+ eidx++) {
+ if (eidx > RAW_ENCAP_DECAP_ELEMS_MAX) {
+ error->message =
+ "To many decap items";
+ goto error;
+ }
+ decap.items[eidx].type =
+ action_cpy[idx]
+ .conf_cpy.u.decap
+ .item_cpy[eidx]
+ .type;
+ if (action_cpy[idx]
+ .conf_cpy.u.decap
+ .item_cpy[eidx]
+ .spec_cpy.valid) {
+ decap.items[eidx].spec =
+ (void *)&action_cpy[idx]
+ .conf_cpy.u
+ .decap
+ .item_cpy[eidx]
+ .spec_cpy.u
+ .start_addr;
+ } else {
+ decap.items[eidx].spec = NULL;
+ }
+ if (action_cpy[idx]
+ .conf_cpy.u.decap
+ .item_cpy[eidx]
+ .mask_cpy.valid) {
+ decap.items[eidx].mask =
+ (void *)&action_cpy[idx]
+ .conf_cpy.u
+ .decap
+ .item_cpy[eidx]
+ .mask_cpy.u
+ .start_addr;
+ } else {
+ decap.items[eidx].mask = NULL;
+ }
+ }
+ action[idx].conf = &decap;
+ }
+ break;
+ default: {
+ /* Move conf pointer into conf_cpy data field */
+ action[idx].conf =
+ (void *)&action_cpy[idx]
+ .conf_cpy.u.start_addr;
+ }
+ break;
+ }
+ }
+ } while (action_cpy[idx].type != FLOW_ACTION_TYPE_END);
+
+ *status = NTCONN_FLOW_ERR_NONE;
+ if (func == FLOW_API_FUNC_VALIDATE) {
+ *status = flow_validate(port_eth[port].flw_dev, elem, action,
+ error);
+ return 0ULL;
+ } else {
+ return (uint64_t)flow_create(port_eth[port].flw_dev, attr, elem,
+ action, error);
+ }
+
+error:
+ return 0;
+}
+
+static int func_flow_create(void *hdl _unused, int client_id _unused,
+ struct ntconn_header_s *hdr, char **data, int *len)
+{
+ int status;
+ struct flow_error error;
+ uint64_t flow = 0UL;
+ int port = MAX_PORTS;
+
+ struct create_flow_ntconnect *flow_cpy =
+ (struct create_flow_ntconnect *)&(*data)[hdr->len];
+
+ if (hdr->blob_len != sizeof(struct create_flow_ntconnect)) {
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "Error in filter data");
+ return ntconn_error(data, len, this_module_name,
+ NTCONN_ERR_CODE_INVALID_REQUEST);
+ }
+
+ port = flow_cpy->port;
+
+ if (port >= MAX_PORTS) {
+ NT_LOG(ERR, NTCONNECT, "port id out of range");
+ return ntconn_flow_err_reply_status(data, len,
+ NTCONN_FLOW_ERR_INVALID_PORT,
+ ENODEV);
+ }
+
+#ifdef DEBUG_PARSING
+ int i;
+
+ for (i = 0; i < MAX_FLOW_STREAM_ELEM; i++) {
+ if (flow_cpy[i].elem[i].type == FLOW_ELEM_TYPE_END) {
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_END\n");
+ break;
+ }
+ switch (flow_cpy->elem[i].type) {
+ case FLOW_ELEM_TYPE_IPV4:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_IPV4 %i\n", i);
+ NT_LOG(DBG, NTCONNECT, " src_ip: %u.%u.%u.%u\n",
+ ((const char *)&flow_cpy->elem[i]
+ .spec_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .spec_cpy.u.ipv4.hdr.src_ip)[1] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .spec_cpy.u.ipv4.hdr.src_ip)[2] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .spec_cpy.u.ipv4.hdr.src_ip)[3] & 0xFF);
+ NT_LOG(DBG, NTCONNECT, " dst_ip: %u.%u.%u.%u\n",
+ ((const char *)&flow_cpy->elem[i]
+ .spec_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .spec_cpy.u.ipv4.hdr.dst_ip)[1] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .spec_cpy.u.ipv4.hdr.dst_ip)[2] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .spec_cpy.u.ipv4.hdr.dst_ip)[3] & 0xFF);
+ NT_LOG(DBG, NTCONNECT, " src_mask: %u.%u.%u.%u\n",
+ ((const char *)&flow_cpy->elem[i]
+ .mask_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .mask_cpy.u.ipv4.hdr.src_ip)[1] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .mask_cpy.u.ipv4.hdr.src_ip)[2] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .mask_cpy.u.ipv4.hdr.src_ip)[3] & 0xFF);
+ NT_LOG(DBG, NTCONNECT, " dst_mask: %u.%u.%u.%u\n",
+ ((const char *)&flow_cpy->elem[i]
+ .mask_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .mask_cpy.u.ipv4.hdr.dst_ip)[1] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .mask_cpy.u.ipv4.hdr.dst_ip)[2] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .mask_cpy.u.ipv4.hdr.dst_ip)[3] & 0xFF);
+ break;
+ case FLOW_ELEM_TYPE_ETH:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_ETH %i\n", i);
+ NT_LOG(DBG, NTCONNECT,
+ " src mac: %02X:%02X:%02X:%02X:%02X:%02X\n",
+ flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[0] & 0xFF,
+ flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[1] & 0xFF,
+ flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[2] & 0xFF,
+ flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[3] & 0xFF,
+ flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[4] & 0xFF,
+ flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[5] & 0xFF);
+ NT_LOG(DBG, NTCONNECT,
+ " dst mac: %02X:%02X:%02X:%02X:%02X:%02X\n",
+ flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[0] & 0xFF,
+ flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[1] & 0xFF,
+ flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[2] & 0xFF,
+ flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[3] & 0xFF,
+ flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[4] & 0xFF,
+ flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[5] & 0xFF);
+ NT_LOG(DBG, NTCONNECT,
+ " src mask %02X:%02X:%02X:%02X:%02X:%02X\n",
+ flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[0] & 0xFF,
+ flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[1] & 0xFF,
+ flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[2] & 0xFF,
+ flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[3] & 0xFF,
+ flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[4] & 0xFF,
+ flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[5] & 0xFF);
+ NT_LOG(DBG, NTCONNECT,
+ " dst mask %02X:%02X:%02X:%02X:%02X:%02X\n",
+ flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[0] & 0xFF,
+ flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[1] & 0xFF,
+ flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[2] & 0xFF,
+ flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[3] & 0xFF,
+ flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[4] & 0xFF,
+ flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[5] & 0xFF);
+ break;
+ case FLOW_ELEM_TYPE_VLAN:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_VLAN %i\n", i);
+ break;
+ case FLOW_ELEM_TYPE_IPV6:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_IPV6 %i\n", i);
+ break;
+ case FLOW_ELEM_TYPE_SCTP:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_SCTP %i\n", i);
+ break;
+ case FLOW_ELEM_TYPE_TCP:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_TCP %i\n", i);
+ break;
+ case FLOW_ELEM_TYPE_UDP:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_UDP %i\n", i);
+ break;
+ case FLOW_ELEM_TYPE_ICMP:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_ICMP %i\n", i);
+ break;
+ case FLOW_ELEM_TYPE_VXLAN:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_VXLAN %i\n", i);
+ break;
+ case FLOW_ELEM_TYPE_PORT_ID:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_PORT_ID %i\n",
+ i);
+ break;
+ default:
+ NT_LOG(DBG, NTCONNECT, "Unknown item %u\n",
+ flow_cpy->elem[i].type);
+ break;
+ }
+ }
+
+ for (i = 0; i < MAX_FLOW_STREAM_ELEM; i++) {
+ uint32_t j;
+
+ if (flow_cpy->action[i].type == FLOW_ACTION_TYPE_END) {
+ NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_END\n");
+ break;
+ }
+ switch (flow_cpy->action[i].type) {
+ case FLOW_ACTION_TYPE_RSS:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_RSS %i\n", i);
+ NT_LOG(DBG, NTCONNECT, " queue nb: %u\n",
+ flow_cpy->action[i].conf_cpy.u.rss.rss.queue_num);
+ NT_LOG(DBG, NTCONNECT, " queue: ");
+ for (j = 0;
+ j < flow_cpy->action[i]
+ .conf_cpy.u.rss.rss.queue_num &&
+ j < FLOW_MAX_QUEUES;
+ j++) {
+ NT_LOG(DBG, NTCONNECT, "%u ",
+ flow_cpy->action[i]
+ .conf_cpy.u.rss.cpy_queue[j]);
+ }
+ NT_LOG(DBG, NTCONNECT, "\n");
+ break;
+
+ case FLOW_ACTION_TYPE_POP_VLAN:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_POP_VLAN %i\n",
+ i);
+ break;
+ case FLOW_ACTION_TYPE_PUSH_VLAN:
+ NT_LOG(DBG, NTCONNECT,
+ "FLOW_ACTION_TYPE_PUSH_VLAN %i\n", i);
+ break;
+ case FLOW_ACTION_TYPE_SET_VLAN_VID:
+ NT_LOG(DBG, NTCONNECT,
+ "FLOW_ACTION_TYPE_SET_VLAN_VID %i\n", i);
+ break;
+ case FLOW_ACTION_TYPE_SET_VLAN_PCP:
+ NT_LOG(DBG, NTCONNECT,
+ "FLOW_ACTION_TYPE_SET_VLAN_PCP %i\n", i);
+ break;
+ case FLOW_ACTION_TYPE_VXLAN_DECAP:
+ NT_LOG(DBG, NTCONNECT,
+ "FLOW_ACTION_TYPE_VXLAN_DECAP %i\n", i);
+ break;
+ case FLOW_ACTION_TYPE_VXLAN_ENCAP:
+ NT_LOG(DBG, NTCONNECT,
+ "FLOW_ACTION_TYPE_VXLAN_ENCAP %i\n", i);
+ break;
+ case FLOW_ACTION_TYPE_DROP:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_DROP %i\n", i);
+ break;
+ case FLOW_ACTION_TYPE_COUNT:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_COUNT %i\n",
+ i);
+ break;
+ case FLOW_ACTION_TYPE_MARK:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_MARK %i\n", i);
+ break;
+ case FLOW_ACTION_TYPE_PORT_ID:
+ NT_LOG(DBG, NTCONNECT,
+ "FLOW_ACTION_TYPE_PORT_ID %i: ID=%u\n", i,
+ flow_cpy->action[i].conf_cpy.u.port_id.id);
+ break;
+ case FLOW_ACTION_TYPE_QUEUE:
+ NT_LOG(DBG, NTCONNECT,
+ "FLOW_ACTION_TYPE_QUEUE %i: queue=%u\n", i,
+ flow_cpy->action[i].conf_cpy.u.queue.index);
+ break;
+ case FLOW_ACTION_TYPE_SET_TAG:
+ NT_LOG(DBG, NTCONNECT,
+ "FLOW_ACTION_TYPE_SET_TAG %i: idx=%u, data=%u, mask=%X\n",
+ i, flow_cpy->action[i].conf_cpy.u.tag.index,
+ flow_cpy->action[i].conf_cpy.u.tag.data,
+ flow_cpy->action[i].conf_cpy.u.tag.mask);
+ break;
+ default:
+ NT_LOG(DBG, NTCONNECT, "Unknown action %u\n",
+ flow_cpy->action[i].type);
+ break;
+ }
+ }
+#endif
+
+ /* Call filter with data */
+ flow = make_flow_create(FLOW_API_FUNC_CREATE, port, flow_cpy, &status,
+ &error);
+ if (flow) {
+ *data = malloc(sizeof(struct create_flow_return_s));
+ if (!*data)
+ goto create_flow_error_malloc;
+ struct create_flow_return_s *return_value =
+ (struct create_flow_return_s *)*data;
+ *len = sizeof(struct create_flow_return_s);
+ return_value->flow = flow;
+ return REQUEST_OK;
+ }
+
+ *data = malloc(sizeof(struct flow_error_return_s));
+ if (!*data)
+ goto create_flow_error_malloc;
+ struct flow_error_return_s *return_value =
+ (struct flow_error_return_s *)*data;
+ *len = sizeof(struct flow_error_return_s);
+ return_value->type = error.type;
+ strlcpy(return_value->err_msg, error.message, ERR_MSG_LEN);
+ return REQUEST_OK;
+
+create_flow_error_malloc:
+
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "Not able to allocate memory %s", __func__);
+ return REQUEST_ERR;
+}
+
+static int func_flow_validate(void *hdl _unused, int client_id _unused,
+ struct ntconn_header_s *hdr, char **data,
+ int *len)
+{
+ int status;
+ struct flow_error error;
+ int port = MAX_PORTS;
+
+ struct create_flow_ntconnect *flow_cpy =
+ (struct create_flow_ntconnect *)&(*data)[hdr->len];
+
+ if (hdr->blob_len != sizeof(struct create_flow_ntconnect)) {
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "Error in filter data");
+ return ntconn_error(data, len, this_module_name,
+ NTCONN_ERR_CODE_INVALID_REQUEST);
+ }
+
+ set_error(&error);
+
+#ifdef DEBUG_FLOW
+ NT_LOG(DBG, NTCONNECT, "func_flow_create\n");
+#endif
+
+ port = flow_cpy->port;
+
+#ifdef DEBUG_FLOW
+ NT_LOG(DBG, NTCONNECT, "Port id=%u\n", port);
+#endif
+
+ if (port >= MAX_PORTS) {
+ NT_LOG(ERR, NTCONNECT, "port id out of range");
+ return ntconn_flow_err_reply_status(data, len,
+ NTCONN_FLOW_ERR_INVALID_PORT, ENODEV);
+ }
+
+#ifdef DEBUG_PARSING
+ int i;
+
+ for (i = 0; i < MAX_FLOW_STREAM_ELEM; i++) {
+ if (flow_cpy[i].elem[i].type == FLOW_ELEM_TYPE_END) {
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_END\n");
+ break;
+ }
+ switch (flow_cpy->elem[i].type) {
+ case FLOW_ELEM_TYPE_IPV4:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_IPV4 %i\n", i);
+ NT_LOG(DBG, NTCONNECT, " src_ip: %u.%u.%u.%u\n",
+ ((const char *)&flow_cpy->elem[i]
+ .spec_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .spec_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .spec_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .spec_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF);
+ NT_LOG(DBG, NTCONNECT, " dst_ip: %u.%u.%u.%u\n",
+ ((const char *)&flow_cpy->elem[i]
+ .spec_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .spec_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .spec_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .spec_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF);
+ NT_LOG(DBG, NTCONNECT, " src_mask: %u.%u.%u.%u\n",
+ ((const char *)&flow_cpy->elem[i]
+ .mask_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .mask_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .mask_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .mask_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF);
+ NT_LOG(DBG, NTCONNECT, " dst_mask: %u.%u.%u.%u\n",
+ ((const char *)&flow_cpy->elem[i]
+ .mask_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .mask_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .mask_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF,
+ ((const char *)&flow_cpy->elem[i]
+ .mask_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF);
+ break;
+ case FLOW_ELEM_TYPE_ETH:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_ETH %i\n", i);
+ NT_LOG(DBG, NTCONNECT,
+ " src mac: %02X:%02X:%02X:%02X:%02X:%02X\n",
+ flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[0] & 0xFF,
+ flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[1] & 0xFF,
+ flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[2] & 0xFF,
+ flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[3] & 0xFF,
+ flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[4] & 0xFF,
+ flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[5] & 0xFF);
+ NT_LOG(DBG, NTCONNECT,
+ " dst mac: %02X:%02X:%02X:%02X:%02X:%02X\n",
+ flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[0] & 0xFF,
+ flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[1] & 0xFF,
+ flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[2] & 0xFF,
+ flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[3] & 0xFF,
+ flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[4] & 0xFF,
+ flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[5] & 0xFF);
+ NT_LOG(DBG, NTCONNECT,
+ " src mask %02X:%02X:%02X:%02X:%02X:%02X\n",
+ flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[0] & 0xFF,
+ flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[1] & 0xFF,
+ flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[2] & 0xFF,
+ flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[3] & 0xFF,
+ flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[4] & 0xFF,
+ flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[5] & 0xFF);
+ NT_LOG(DBG, NTCONNECT,
+ " dst mask %02X:%02X:%02X:%02X:%02X:%02X\n",
+ flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[0] & 0xFF,
+ flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[1] & 0xFF,
+ flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[2] & 0xFF,
+ flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[3] & 0xFF,
+ flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[4] & 0xFF,
+ flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[5] & 0xFF);
+ break;
+ case FLOW_ELEM_TYPE_VLAN:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_VLAN %i\n", i);
+ break;
+ case FLOW_ELEM_TYPE_IPV6:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_IPV6 %i\n", i);
+ break;
+ case FLOW_ELEM_TYPE_SCTP:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_SCTP %i\n", i);
+ break;
+ case FLOW_ELEM_TYPE_TCP:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_TCP %i\n", i);
+ break;
+ case FLOW_ELEM_TYPE_UDP:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_UDP %i\n", i);
+ break;
+ case FLOW_ELEM_TYPE_ICMP:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_ICMP %i\n", i);
+ break;
+ case FLOW_ELEM_TYPE_VXLAN:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_VXLAN %i\n", i);
+ break;
+ case FLOW_ELEM_TYPE_PORT_ID:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ELEM_TYPE_PORT_ID %i\n",
+ i);
+ break;
+ default:
+ NT_LOG(DBG, NTCONNECT, "Unknown item %u\n",
+ flow_cpy->elem[i].type);
+ break;
+ }
+ }
+
+ for (i = 0; i < MAX_FLOW_STREAM_ELEM; i++) {
+ uint32_t j;
+
+ if (flow_cpy->action[i].type == FLOW_ACTION_TYPE_END) {
+ NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_END\n");
+ break;
+ }
+ switch (flow_cpy->action[i].type) {
+ case FLOW_ACTION_TYPE_RSS:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_RSS %i\n", i);
+ NT_LOG(DBG, NTCONNECT, " queue nb: %u\n",
+ flow_cpy->action[i].conf_cpy.u.rss.rss.queue_num);
+ NT_LOG(DBG, NTCONNECT, " queue: ");
+ for (j = 0;
+ j < flow_cpy->action[i]
+ .conf_cpy.u.rss.rss.queue_num &&
+ j < FLOW_MAX_QUEUES;
+ j++) {
+ NT_LOG(DBG, NTCONNECT, "%u ",
+ flow_cpy->action[i]
+ .conf_cpy.u.rss.cpy_queue[j]);
+ }
+ NT_LOG(DBG, NTCONNECT, "\n");
+ break;
+
+ case FLOW_ACTION_TYPE_POP_VLAN:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_POP_VLAN %i\n",
+ i);
+ break;
+ case FLOW_ACTION_TYPE_PUSH_VLAN:
+ NT_LOG(DBG, NTCONNECT,
+ "FLOW_ACTION_TYPE_PUSH_VLAN %i\n", i);
+ break;
+ case FLOW_ACTION_TYPE_SET_VLAN_VID:
+ NT_LOG(DBG, NTCONNECT,
+ "FLOW_ACTION_TYPE_SET_VLAN_VID %i\n", i);
+ break;
+ case FLOW_ACTION_TYPE_SET_VLAN_PCP:
+ NT_LOG(DBG, NTCONNECT,
+ "FLOW_ACTION_TYPE_SET_VLAN_PCP %i\n", i);
+ break;
+ case FLOW_ACTION_TYPE_VXLAN_DECAP:
+ NT_LOG(DBG, NTCONNECT,
+ "FLOW_ACTION_TYPE_VXLAN_DECAP %i\n", i);
+ break;
+ case FLOW_ACTION_TYPE_VXLAN_ENCAP:
+ NT_LOG(DBG, NTCONNECT,
+ "FLOW_ACTION_TYPE_VXLAN_ENCAP %i\n", i);
+ break;
+ case FLOW_ACTION_TYPE_DROP:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_DROP %i\n", i);
+ break;
+ case FLOW_ACTION_TYPE_COUNT:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_COUNT %i\n",
+ i);
+ break;
+ case FLOW_ACTION_TYPE_MARK:
+ NT_LOG(DBG, NTCONNECT, "FLOW_ACTION_TYPE_MARK %i\n", i);
+ break;
+ case FLOW_ACTION_TYPE_PORT_ID:
+ NT_LOG(DBG, NTCONNECT,
+ "FLOW_ACTION_TYPE_PORT_ID %i: ID=%u\n", i,
+ flow_cpy->action[i].conf_cpy.u.port_id.id);
+ break;
+ case FLOW_ACTION_TYPE_QUEUE:
+ NT_LOG(DBG, NTCONNECT,
+ "FLOW_ACTION_TYPE_QUEUE %i: queue=%u\n", i,
+ flow_cpy->action[i].conf_cpy.u.queue.index);
+ break;
+ case FLOW_ACTION_TYPE_SET_TAG:
+ NT_LOG(DBG, NTCONNECT,
+ "FLOW_ACTION_TYPE_SET_TAG %i: idx=%u, data=%u, mask=%X\n",
+ i, flow_cpy->action[i].conf_cpy.u.tag.index,
+ flow_cpy->action[i].conf_cpy.u.tag.data,
+ flow_cpy->action[i].conf_cpy.u.tag.mask);
+ break;
+ default:
+ NT_LOG(DBG, NTCONNECT, "Unknown action %u\n",
+ flow_cpy->action[i].type);
+ break;
+ }
+ }
+#endif
+
+ /* Call filter with data */
+ make_flow_create(FLOW_API_FUNC_VALIDATE, port, flow_cpy, &status,
+ &error);
+ return copy_return_status(data, len, status, &error);
+
+ /* Call filter with data */
+ make_flow_create(FLOW_API_FUNC_VALIDATE, port, flow_cpy, &status,
+ &error);
+ if (!status) {
+ *data = malloc(sizeof(struct validate_flow_return_s));
+ if (!*data)
+ goto validate_flow_error_malloc;
+ struct validate_flow_return_s *return_value =
+ (struct validate_flow_return_s *)*data;
+ *len = sizeof(struct validate_flow_return_s);
+ return_value->status = 0;
+ return REQUEST_OK;
+ }
+
+ *data = malloc(sizeof(struct flow_error_return_s));
+ if (!*data)
+ goto validate_flow_error_malloc;
+ struct flow_error_return_s *return_value =
+ (struct flow_error_return_s *)*data;
+ *len = sizeof(struct flow_error_return_s);
+ return_value->type = error.type;
+ strlcpy(return_value->err_msg, error.message, ERR_MSG_LEN);
+ return_value->status = status;
+ return REQUEST_OK;
+
+validate_flow_error_malloc:
+
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "Not able to allocate memory %s", __func__);
+ return REQUEST_ERR;
+}
+
+static int func_flow_query(void *hdl _unused, int client_id _unused,
+ struct ntconn_header_s *hdr, char **data, int *len)
+{
+ int status;
+ struct flow_error error;
+ int port = MAX_PORTS;
+ struct flow_handle *flow;
+
+ struct query_flow_ntconnect *flow_cpy =
+ (struct query_flow_ntconnect *)&(*data)[hdr->len];
+
+ if (hdr->blob_len != sizeof(struct query_flow_ntconnect)) {
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "Error in filter data");
+ return ntconn_error(data, len, this_module_name,
+ NTCONN_ERR_CODE_INVALID_REQUEST);
+ }
+
+ set_error(&error);
+
+#ifdef DEBUG_FLOW
+ NT_LOG(DBG, NTCONNECT, "%s: [%s:%u] enter\n", __func__, __FILE__, __LINE__);
+#endif
+
+ port = flow_cpy->port;
+
+#ifdef DEBUG_FLOW
+ NT_LOG(DBG, NTCONNECT, "Port id=%u\n", port);
+#endif
+
+ if (port >= MAX_PORTS) {
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "port id out of range");
+ return ntconn_flow_err_reply_status(data, len,
+ NTCONN_FLOW_ERR_INVALID_PORT, ENODEV);
+ }
+
+#ifdef DEBUG_FLOW
+ NT_LOG(DBG, NTCONNECT, "flow=0x%016llX\n",
+ (unsigned long long)flow_cpy->flow);
+#endif
+
+ flow = (struct flow_handle *)flow_cpy->flow;
+
+ const struct flow_action action = {
+ flow_cpy->action.type,
+ (const void *)&flow_cpy->action.conf_cpy.u.count
+ };
+
+ /* Call filter with data */
+ void *data_out = NULL;
+ uint32_t length = 0;
+
+ status = flow_query(port_eth[port].flw_dev, flow, &action, &data_out,
+ &length, &error);
+
+ *data = malloc(sizeof(struct query_flow_return_s) + length);
+ if (*data) {
+ struct query_flow_return_s *return_value =
+ (struct query_flow_return_s *)*data;
+ *len = sizeof(struct query_flow_return_s) + length;
+
+ return_value->status = status;
+ return_value->type = error.type;
+ strlcpy(return_value->err_msg, error.message, ERR_MSG_LEN);
+
+ if (data_out) {
+ memcpy(return_value->data, data_out, length);
+ return_value->data_length = length;
+ free(data_out);
+ } else {
+ return_value->data_length = 0;
+ }
+ return REQUEST_OK;
+ }
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "Not able to allocate memory %s",
+ __func__);
+ return REQUEST_ERR;
+}
+
+static int flow_request(void *hdl, int client_id _unused,
+ struct ntconn_header_s *hdr, char *function,
+ char **data, int *len)
+{
+ return execute_function(this_module_name, hdl, client_id, hdr, function,
+ adapter_entry_funcs, data, len, 0);
+}
+
+static void flow_free_data(void *hdl _unused, char *data)
+{
+ if (data)
+ free(data);
+}
+
+static void flow_client_cleanup(void *hdl _unused, int client_id _unused)
+{
+ /* Nothing to do */
+}
+
+static const ntconnapi_t ntconn_flow_op = { this_module_name,
+ NTCONN_FLOW_VERSION_MAJOR,
+ NTCONN_FLOW_VERSION_MINOR,
+ flow_request,
+ flow_free_data,
+ flow_client_cleanup
+ };
+
+int ntconn_flow_register(struct drv_s *drv)
+{
+ int i;
+
+ for (i = 0; i < MAX_CLIENTS; i++) {
+ if (flow_hdl[i].drv == NULL)
+ break;
+ }
+ if (i == MAX_CLIENTS) {
+ NT_LOG(ERR, NTCONNECT,
+ "Cannot register more adapters into NtConnect framework");
+ return -1;
+ }
+
+ flow_hdl[i].drv = drv;
+ return register_ntconn_mod(&drv->p_dev->addr, (void *)&flow_hdl[i],
+ &ntconn_flow_op);
+}
diff --git a/drivers/net/ntnic/ntconnect_modules/ntconn_meter.c b/drivers/net/ntnic/ntconnect_modules/ntconn_meter.c
new file mode 100644
index 0000000000..7c21690f8b
--- /dev/null
+++ b/drivers/net/ntnic/ntconnect_modules/ntconn_meter.c
@@ -0,0 +1,517 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include <errno.h>
+#include "ntnic_ethdev.h"
+#include "ntconnect.h"
+#include "ntos_system.h"
+#include "ntconn_modules.h"
+#include "ntconn_mod_helper.h"
+#include "nt_util.h"
+#include "ntlog.h"
+#include "ntnic_vf_vdpa.h"
+
+#include "ntconnect_api_meter.h"
+#include "flow_api_profile_inline.h"
+
+#include <rte_errno.h>
+#include <rte_mtr.h>
+#include <rte_mtr_driver.h>
+
+#define NTCONN_METER_VERSION_MAJOR 0U
+#define NTCONN_METER_VERSION_MINOR 1U
+
+#define this_module_name "meter"
+
+#define MAX_CLIENTS 32
+
+#define UNUSED __rte_unused
+
+static struct meter_hdl_s {
+ struct drv_s *drv;
+} meter_hdl[MAX_CLIENTS];
+
+static ntconn_err_t ntconn_err[] = {
+ { NTCONN_METER_ERR_NONE, "Success" },
+ { NTCONN_METER_ERR_INTERNAL_ERROR, "Internal error" },
+ { NTCONN_METER_ERR_INVALID_PORT, "Invalid virtual port" },
+ { NTCONN_METER_ERR_PROFILE_ID, "Profile ID out of range" },
+ { NTCONN_METER_ERR_POLICY_ID, "Policy ID out of range" },
+ { NTCONN_METER_ERR_METER_ID, "Meter ID out of range" },
+ { -1, NULL }
+};
+
+/********************************************************************/
+/* Get error message corresponding to the error code */
+/********************************************************************/
+static const char *get_error_msg(uint32_t err_code)
+{
+ int idx = 0;
+
+ if (err_code < NTCONN_METER_ERR_INTERNAL_ERROR) {
+ const ntconn_err_t *err_msg = get_ntconn_error(err_code);
+
+ return err_msg->err_text;
+ }
+ while (ntconn_err[idx].err_code != (uint32_t)-1 &&
+ ntconn_err[idx].err_code != err_code)
+ idx++;
+ if (ntconn_err[idx].err_code == (uint32_t)-1)
+ idx = 1;
+ return ntconn_err[idx].err_text;
+}
+
+/*
+ * Filter functions
+ */
+static int func_meter_get_capabilities(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char **data,
+ int *len);
+static int func_meter_setup(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char **data, int *len);
+static int func_meter_read(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char **data, int *len);
+static struct func_s adapter_entry_funcs[] = {
+ { "capabilities", NULL, func_meter_get_capabilities },
+ { "setup", NULL, func_meter_setup },
+ { "read", NULL, func_meter_read },
+ { NULL, NULL, NULL },
+};
+
+/**********************************************************************/
+/* copy error message corresponding to the error code to error struct */
+/**********************************************************************/
+static void copy_mtr_error(struct rte_mtr_error *error, uint32_t err)
+{
+ error->type = RTE_MTR_ERROR_TYPE_UNSPECIFIED;
+ error->message = get_error_msg(err);
+ error->cause = NULL;
+}
+
+static int func_meter_get_capabilities(void *hdl _unused, int client_id _unused,
+ struct ntconn_header_s *hdr _unused,
+ char **data, int *len)
+{
+ char *saveptr;
+ uint8_t vport = 0;
+ uint8_t port = 0;
+ int status;
+ struct rte_mtr_capabilities cap;
+ struct rte_mtr_error error;
+
+#ifdef DEBUG_METER
+ NT_LOG(DBG, NTCONNECT, "%s: \"%s\"\n", __func__, *data);
+#endif
+
+ char *tok = strtok_r(*data, ",", &saveptr);
+
+ if (tok) {
+ int length = strlen(tok);
+
+ if (length > 6 && memcmp(tok, "vport=", 6) == 0)
+ vport = atoi(tok + 6);
+ }
+#ifdef DEBUG_METER
+ NT_LOG(DBG, NTCONNECT, "vport=%u\n", vport);
+#endif
+
+ if (vport == 0 || vport > 64) {
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "Virtual port is invalid");
+ copy_mtr_error(&error, NTCONN_METER_ERR_INVALID_PORT);
+ status = -ENODEV;
+ goto error_get_capa;
+ }
+
+ port = vport & 1;
+ status = rte_mtr_capabilities_get(port, &cap, &error);
+ if (status == 0) {
+ /* Handle success by copying the return values to the return struct */
+ *data = malloc(sizeof(struct meter_capabilities_return_s));
+ if (!*data)
+ goto error_get_capa_malloc;
+ struct meter_capabilities_return_s *return_value =
+ (struct meter_capabilities_return_s *)*data;
+ *len = sizeof(struct meter_capabilities_return_s);
+ memcpy(&return_value->cap, &cap,
+ sizeof(struct rte_mtr_capabilities));
+ return REQUEST_OK;
+ }
+
+error_get_capa:
+
+ /* Handle errors by copy errors to the error struct */
+ NT_LOG(ERR, NTCONNECT, "Failed to get capabilities for port %u (%u)",
+ port, vport);
+ *data = malloc(sizeof(struct meter_error_return_s));
+ if (!*data)
+ goto error_get_capa_malloc;
+ struct meter_error_return_s *return_value =
+ (struct meter_error_return_s *)*data;
+ *len = sizeof(struct meter_error_return_s);
+ return_value->status = status;
+ return_value->type = error.type;
+ strlcpy(return_value->err_msg, error.message, ERR_MSG_LEN);
+ return REQUEST_OK;
+
+error_get_capa_malloc:
+
+ *len = 0;
+ return REQUEST_ERR;
+}
+
+static int func_meter_setup(void *hdl _unused, int client_id _unused,
+ struct ntconn_header_s *hdr, char **data, int *len)
+{
+ char *saveptr;
+ uint8_t port;
+ uint32_t max_id;
+ int status;
+ struct rte_mtr_error error;
+ int command = UNKNOWN_CMD;
+
+#ifdef DEBUG_METER
+ NT_LOG(DBG, NTCONNECT, "%s: \"%s\"\n", __func__, *data);
+#endif
+
+ if (hdr->blob_len != sizeof(struct meter_setup_s)) {
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "Error: Profile data size is illegal");
+ copy_mtr_error(&error, NTCONN_ERR_CODE_INVALID_REQUEST);
+ status = -EINTR;
+ goto error_meter_setup;
+ }
+
+ /* Get the data blob containing the data for the meter function */
+ struct meter_setup_s *cpy_data =
+ (struct meter_setup_s *)&(*data)[hdr->len];
+
+ if (cpy_data->vport < 4 || cpy_data->vport > 128) {
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "Virtual port is invalid");
+ copy_mtr_error(&error, NTCONN_METER_ERR_INVALID_PORT);
+ status = -ENODEV;
+ goto error_meter_setup;
+ }
+
+ char *tok = strtok_r(*data, ",", &saveptr);
+
+ if (tok) {
+ int length = strlen(tok);
+
+ if (length == 6) {
+ if (memcmp(tok, "addpro", 6) == 0)
+ command = ADD_PROFILE;
+
+ else if (memcmp(tok, "delpro", 6) == 0)
+ command = DEL_PROFILE;
+
+ else if (memcmp(tok, "addpol", 6) == 0)
+ command = ADD_POLICY;
+
+ else if (memcmp(tok, "delpol", 6) == 0)
+ command = DEL_POLICY;
+
+ else if (memcmp(tok, "crtmtr", 6) == 0)
+ command = CREATE_MTR;
+
+ else if (memcmp(tok, "delmtr", 6) == 0)
+ command = DEL_MTR;
+ }
+ }
+
+ if (command == UNKNOWN_CMD) {
+ NT_LOG(ERR, NTCONNECT, "Error: Invalid command");
+ copy_mtr_error(&error, NTCONN_ERR_CODE_INVALID_REQUEST);
+ status = -EINVAL;
+ goto error_meter_setup;
+ }
+
+ /* Port will be either 0 or 1 depending on the VF. */
+ port = cpy_data->vport & 1;
+
+ switch (command) {
+ case ADD_PROFILE:
+ max_id = flow_mtr_meter_policy_n_max() / (RTE_MAX_ETHPORTS - 2);
+ if (cpy_data->id > max_id) {
+ NT_LOG(ERR, NTCONNECT,
+ "Error: Profile ID %u out of range. Max value is %u",
+ cpy_data->id, max_id);
+ copy_mtr_error(&error, NTCONN_METER_ERR_PROFILE_ID);
+ status = -EINVAL;
+ goto error_meter_setup;
+ }
+ cpy_data->id = ((cpy_data->vport - 4) * max_id) + cpy_data->id;
+ status = rte_mtr_meter_profile_add(port, cpy_data->id,
+ &cpy_data->profile, &error);
+ if (status != 0) {
+ NT_LOG(ERR, NTCONNECT,
+ "Failed to add profile for port %u (%u)", port,
+ cpy_data->vport);
+ }
+ break;
+ case DEL_PROFILE:
+ max_id = flow_mtr_meter_policy_n_max() / (RTE_MAX_ETHPORTS - 2);
+ if (cpy_data->id > max_id) {
+ NT_LOG(ERR, NTCONNECT,
+ "Error: Profile ID %u out of range. Max value is %u",
+ cpy_data->id, max_id);
+ copy_mtr_error(&error, NTCONN_METER_ERR_PROFILE_ID);
+ status = -EINVAL;
+ goto error_meter_setup;
+ }
+ cpy_data->id = ((cpy_data->vport - 4) * max_id) + cpy_data->id;
+ status = rte_mtr_meter_profile_delete(port, cpy_data->id,
+ &error);
+ if (status != 0) {
+ NT_LOG(ERR, NTCONNECT,
+ "Failed to delete profile for port %u (%u)",
+ port, cpy_data->vport);
+ }
+ break;
+ case ADD_POLICY:
+ max_id = flow_mtr_meter_policy_n_max() / (RTE_MAX_ETHPORTS - 2);
+ if (cpy_data->id > max_id) {
+ NT_LOG(ERR, NTCONNECT,
+ "Error: Policy ID %u out of range. Max value is %u",
+ cpy_data->id, max_id);
+ copy_mtr_error(&error, NTCONN_METER_ERR_POLICY_ID);
+ status = -EINVAL;
+ goto error_meter_setup;
+ }
+ cpy_data->id = ((cpy_data->vport - 4) * max_id) + cpy_data->id;
+ cpy_data->p.policy.actions[RTE_COLOR_GREEN] =
+ cpy_data->p.actions_green;
+ cpy_data->p.policy.actions[RTE_COLOR_YELLOW] =
+ cpy_data->p.actions_yellow;
+ cpy_data->p.policy.actions[RTE_COLOR_RED] =
+ cpy_data->p.actions_red;
+ status = rte_mtr_meter_policy_add(port, cpy_data->id,
+ &cpy_data->p.policy, &error);
+ if (status != 0) {
+ NT_LOG(ERR, NTCONNECT,
+ "Failed to add policy for port %u (%u)", port,
+ cpy_data->vport);
+ }
+ break;
+ case DEL_POLICY:
+ max_id = flow_mtr_meter_policy_n_max() / (RTE_MAX_ETHPORTS - 2);
+ if (cpy_data->id > max_id) {
+ NT_LOG(ERR, NTCONNECT,
+ "Error: Policy ID %u out of range. Max value is %u",
+ cpy_data->id, max_id);
+ copy_mtr_error(&error, NTCONN_METER_ERR_POLICY_ID);
+ status = -EINVAL;
+ goto error_meter_setup;
+ }
+ cpy_data->id = ((cpy_data->vport - 4) * max_id) + cpy_data->id;
+ status =
+ rte_mtr_meter_policy_delete(port, cpy_data->id, &error);
+ if (status != 0) {
+ NT_LOG(ERR, NTCONNECT,
+ "Failed to delete policy for port %u (%u)", port,
+ cpy_data->vport);
+ }
+ break;
+ case CREATE_MTR:
+ max_id = flow_mtr_meters_supported() / (RTE_MAX_ETHPORTS - 2);
+ if (cpy_data->id > max_id) {
+ NT_LOG(ERR, NTCONNECT,
+ "Error: Meter ID %u out of range. Max value is %u",
+ cpy_data->id, max_id);
+ copy_mtr_error(&error, NTCONN_METER_ERR_METER_ID);
+ status = -EINVAL;
+ goto error_meter_setup;
+ }
+ cpy_data->id = ((cpy_data->vport - 4) * max_id) + cpy_data->id;
+ cpy_data->mtr_params.meter_profile_id =
+ ((cpy_data->vport - 4) *
+ (flow_mtr_meter_policy_n_max() /
+ (RTE_MAX_ETHPORTS - 2))) +
+ cpy_data->mtr_params.meter_profile_id;
+ cpy_data->mtr_params.meter_policy_id =
+ ((cpy_data->vport - 4) *
+ (flow_mtr_meter_policy_n_max() /
+ (RTE_MAX_ETHPORTS - 2))) +
+ cpy_data->mtr_params.meter_policy_id;
+ status = rte_mtr_create(port, cpy_data->id,
+ &cpy_data->mtr_params, cpy_data->shared,
+ &error);
+ if (status != 0) {
+ NT_LOG(ERR, NTCONNECT,
+ "Failed to create meter for port %u (%u)", port,
+ cpy_data->vport);
+ }
+ break;
+ case DEL_MTR:
+ max_id = flow_mtr_meters_supported() / (RTE_MAX_ETHPORTS - 2);
+ if (cpy_data->id > max_id) {
+ NT_LOG(ERR, NTCONNECT,
+ "Error: Meter ID %u out of range. Max value is %u",
+ cpy_data->id, max_id);
+ copy_mtr_error(&error, NTCONN_METER_ERR_METER_ID);
+ status = -EINVAL;
+ goto error_meter_setup;
+ }
+ cpy_data->id = ((cpy_data->vport - 4) * max_id) + cpy_data->id;
+ status = rte_mtr_destroy(port, cpy_data->id, &error);
+ if (status != 0) {
+ NT_LOG(ERR, NTCONNECT,
+ "Failed to destroy meter for port %u (%u)", port,
+ cpy_data->vport);
+ }
+ break;
+ }
+
+ if (status == 0) {
+ /* Handle success by copying the return values to the return struct */
+ *data = malloc(sizeof(struct meter_return_s));
+ if (!*data)
+ goto error_meter_setup_malloc;
+ struct meter_return_s *return_value =
+ (struct meter_return_s *)*data;
+ *len = sizeof(struct meter_return_s);
+ return_value->status = 0;
+ return REQUEST_OK;
+ }
+
+error_meter_setup:
+
+ /* Handle errors by copy errors to the error struct */
+ *data = malloc(sizeof(struct meter_error_return_s));
+ if (!*data)
+ goto error_meter_setup_malloc;
+ struct meter_error_return_s *return_value =
+ (struct meter_error_return_s *)*data;
+ *len = sizeof(struct meter_error_return_s);
+ return_value->status = status;
+ return_value->type = error.type;
+ strlcpy(return_value->err_msg, error.message, ERR_MSG_LEN);
+ return REQUEST_OK;
+
+error_meter_setup_malloc:
+
+ *len = 0;
+ return REQUEST_ERR;
+}
+
+static int func_meter_read(void *hdl _unused, int client_id _unused,
+ struct ntconn_header_s *hdr, char **data, int *len)
+{
+ uint8_t port = 0;
+ int status;
+ struct rte_mtr_error error;
+ struct rte_mtr_stats stats;
+ uint64_t stats_mask;
+
+#ifdef DEBUG_METER
+ NT_LOG(DBG, NTCONNECT, "%s: [%s:%u] enter\n", __func__, __FILE__, __LINE__);
+#endif
+
+ if (hdr->blob_len != sizeof(struct meter_get_stat_s)) {
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT,
+ "Error: Read meter stats data size is illegal");
+ copy_mtr_error(&error, NTCONN_ERR_CODE_INVALID_REQUEST);
+ status = -EINTR;
+ goto error_meter_read;
+ }
+
+ /* Get the data blob containing the data for the meter function */
+ struct meter_get_stat_s *cpy_data =
+ (struct meter_get_stat_s *)&(*data)[hdr->len];
+
+ if (cpy_data->vport < 4 || cpy_data->vport > 128) {
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "Virtual port is invalid");
+ copy_mtr_error(&error, NTCONN_METER_ERR_INVALID_PORT);
+ status = -ENODEV;
+ goto error_meter_read;
+ }
+
+ port = cpy_data->vport & 1;
+ cpy_data->mtr_id =
+ ((cpy_data->vport - 4) *
+ (flow_mtr_meters_supported() / (RTE_MAX_ETHPORTS - 2))) +
+ cpy_data->mtr_id;
+ status = rte_mtr_stats_read(port, cpy_data->mtr_id, &stats, &stats_mask,
+ cpy_data->clear, &error);
+ if (status == 0) {
+ /* Handle success by copying the return values to the return struct */
+ *data = malloc(sizeof(struct meter_return_stat_s));
+ if (!*data)
+ goto error_meter_read_malloc;
+ struct meter_return_stat_s *return_value =
+ (struct meter_return_stat_s *)*data;
+ *len = sizeof(struct meter_return_stat_s);
+ return_value->stats_mask = stats_mask;
+ memcpy(&return_value->stats, &stats,
+ sizeof(struct rte_mtr_stats));
+ return REQUEST_OK;
+ }
+
+error_meter_read:
+ /* Handle errors by copy errors to the error struct */
+ NT_LOG(ERR, NTCONNECT, "Failed to read meter stats");
+ *data = malloc(sizeof(struct meter_error_return_s));
+ if (!*data)
+ goto error_meter_read_malloc;
+ struct meter_error_return_s *return_value =
+ (struct meter_error_return_s *)*data;
+ *len = sizeof(struct meter_error_return_s);
+ strlcpy(return_value->err_msg, error.message, ERR_MSG_LEN);
+ return_value->status = status;
+ return_value->type = error.type;
+ return REQUEST_OK;
+
+error_meter_read_malloc:
+ *len = 0;
+ return REQUEST_ERR;
+}
+
+static int meter_request(void *hdl, int client_id _unused,
+ struct ntconn_header_s *hdr, char *function,
+ char **data, int *len)
+{
+ return execute_function(this_module_name, hdl, client_id, hdr, function,
+ adapter_entry_funcs, data, len, 0);
+}
+
+static void meter_free_data(void *hdl _unused, char *data)
+{
+ if (data)
+ free(data);
+}
+
+static void meter_client_cleanup(void *hdl _unused, int client_id _unused)
+{
+ /* Nothing to do */
+}
+
+static const ntconnapi_t ntconn_meter_op = { this_module_name,
+ NTCONN_METER_VERSION_MAJOR,
+ NTCONN_METER_VERSION_MINOR,
+ meter_request,
+ meter_free_data,
+ meter_client_cleanup
+ };
+
+int ntconn_meter_register(struct drv_s *drv)
+{
+ int i;
+
+ for (i = 0; i < MAX_CLIENTS; i++) {
+ if (meter_hdl[i].drv == NULL)
+ break;
+ }
+ if (i == MAX_CLIENTS) {
+ NT_LOG(ERR, NTCONNECT,
+ "Cannot register more adapters into NtConnect framework");
+ return -1;
+ }
+
+ meter_hdl[i].drv = drv;
+ return register_ntconn_mod(&drv->p_dev->addr, (void *)&meter_hdl[i],
+ &ntconn_meter_op);
+}
diff --git a/drivers/net/ntnic/ntconnect_modules/ntconn_modules.h b/drivers/net/ntnic/ntconnect_modules/ntconn_modules.h
new file mode 100644
index 0000000000..ea379015fe
--- /dev/null
+++ b/drivers/net/ntnic/ntconnect_modules/ntconn_modules.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef _NTCONN_MODULES_H_
+#define _NTCONN_MODULES_H_
+
+#include "ntos_system.h"
+
+/*
+ * All defined NT connection modules
+ */
+int ntconn_adap_register(struct drv_s *drv);
+int ntconn_stat_register(struct drv_s *drv);
+int ntconn_flow_register(struct drv_s *drv);
+int ntconn_meter_register(struct drv_s *drv);
+int ntconn_test_register(struct drv_s *drv);
+
+#endif /* _NTCONN_MODULES_H_ */
diff --git a/drivers/net/ntnic/ntconnect_modules/ntconn_stat.c b/drivers/net/ntnic/ntconnect_modules/ntconn_stat.c
new file mode 100644
index 0000000000..437cf9ddad
--- /dev/null
+++ b/drivers/net/ntnic/ntconnect_modules/ntconn_stat.c
@@ -0,0 +1,877 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include <rte_dev.h>
+#include <rte_bus_pci.h>
+#include <ethdev_pci.h>
+#include <rte_ethdev.h>
+
+#include "ntconnect.h"
+#include "ntconnect_api_statistic.h"
+#include "ntos_system.h"
+#include "ntconn_modules.h"
+#include "ntconn_mod_helper.h"
+#include "nt_util.h"
+#include "ntlog.h"
+#include "ntnic_xstats.h"
+
+#define STAT_VERSION_MAJOR 0U
+#define STAT_VERSION_MINOR 2U
+
+#define this_module_name "stat"
+
+/*
+ * Supported Stat Layout Versions
+ */
+#define NUM_LAYOUT_VERSIONS_SUPPORTED (RTE_DIM(layout_versions_supported))
+static int layout_versions_supported[] = {
+ 6,
+ /*
+ * Add here other layout versions to support
+ * When more versions are added, add new version dependent binary reply structures
+ * in ntconnect_api.h file for client to select on reading layout_version
+ */
+};
+
+enum snap_addr_select_e {
+ SNAP_COLORS,
+ SNAP_QUEUES,
+ SNAP_RX_PORT,
+ SNAP_TX_PORT,
+ SNAP_ADDR_COUNT
+};
+
+struct snap_addr_s {
+ const uint64_t *ptr;
+ unsigned int size;
+};
+
+struct snaps_s {
+ int client_id;
+ /* Pointers into buffer */
+ struct snap_addr_s snap_addr[SNAP_ADDR_COUNT];
+ uint64_t *buffer;
+ struct snaps_s *next;
+};
+
+static struct stat_hdl {
+ struct drv_s *drv;
+ nt4ga_stat_t *p_nt4ga_stat;
+ struct snaps_s *snaps_base;
+} stat_hdl;
+
+enum stat_type_e {
+ STAT_TYPE_COLOR,
+ STAT_TYPE_QUEUE,
+ STAT_TYPE_RX,
+ STAT_TYPE_TX,
+ STAT_TYPE_FLOWMATCHER
+};
+
+static int func_get_snap_colors(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char **data,
+ int *len);
+static int func_get_snap_queues(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char **data,
+ int *len);
+static int func_get_snap_rx_port(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char **data,
+ int *len);
+static int func_get_snap_tx_port(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char **data,
+ int *len);
+static struct func_s func_snap_level2[] = {
+ { "colors", NULL, func_get_snap_colors },
+ { "queues", NULL, func_get_snap_queues },
+ { "rx_counters", NULL, func_get_snap_rx_port },
+ { "tx_counters", NULL, func_get_snap_tx_port },
+ { NULL, NULL, NULL },
+};
+
+static int func_get_layout_version(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char **data,
+ int *len);
+static int func_get_flm(void *hdl, int client_id, struct ntconn_header_s *hdr,
+ char **data, int *len);
+static int func_get_color(void *hdl, int client_id, struct ntconn_header_s *hdr,
+ char **data, int *len);
+static int func_get_queue(void *hdl, int client_id, struct ntconn_header_s *hdr,
+ char **data, int *len);
+static int func_get_rx_counters(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char **data,
+ int *len);
+static int func_get_tx_counters(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char **data,
+ int *len);
+static int func_get_flm_layout_version(void *hdl, int client_id,
+ struct ntconn_header_s *hdr, char **data,
+ int *len);
+
+static struct func_s funcs_get_level1[] = {
+ { "snapshot", func_snap_level2, NULL },
+ { "layout_version", NULL, func_get_layout_version },
+ { "flm", NULL, func_get_flm },
+ { "colors", NULL, func_get_color },
+ { "queues", NULL, func_get_queue },
+ { "rx_counters", NULL, func_get_rx_counters },
+ { "tx_counters", NULL, func_get_tx_counters },
+ { "flm_layout_version", NULL, func_get_flm_layout_version },
+ { NULL, NULL, NULL },
+};
+
+/*
+ * Entry level
+ */
+static int func_snapshot(void *hdl, int client_id, struct ntconn_header_s *hdr,
+ char **data, int *len);
+static struct func_s stat_entry_funcs[] = {
+ { "get", funcs_get_level1, NULL },
+ { "snapshot", NULL, func_snapshot },
+ { NULL, NULL, NULL },
+};
+
+static int read_flm(nt4ga_stat_t *hwstat, uint64_t *val, int nbc)
+{
+ struct ntc_stat_get_data_s *cdata = (struct ntc_stat_get_data_s *)val;
+
+ cdata->nb_counters = (uint64_t)nbc;
+ cdata->timestamp = hwstat->last_timestamp;
+ cdata->is_virt = hwstat->mp_nthw_stat->mb_is_vswitch;
+
+ struct rte_eth_xstat stats[100];
+ struct rte_eth_xstat_name names[100];
+ int cnt_names = nthw_xstats_get_names(hwstat, names, 100,
+ hwstat->mp_nthw_stat->mb_is_vswitch);
+ int cnt_values = nthw_xstats_get(hwstat, stats, 100,
+ hwstat->mp_nthw_stat->mb_is_vswitch, 0);
+ assert(cnt_names == cnt_values);
+
+ /* virt/cap same */
+ struct flowmatcher_type_fields_s *flm =
+ (struct flowmatcher_type_fields_s *)cdata->data;
+ if (hwstat->mp_stat_structs_flm) {
+ int c;
+
+ for (c = 0; c < nbc; c++) {
+ flm->current = hwstat->mp_stat_structs_flm->current;
+ flm->learn_done = hwstat->mp_stat_structs_flm->learn_done;
+ flm->learn_ignore =
+ hwstat->mp_stat_structs_flm->learn_ignore;
+ flm->learn_fail = hwstat->mp_stat_structs_flm->learn_fail;
+ flm->unlearn_done =
+ hwstat->mp_stat_structs_flm->unlearn_done;
+ flm->unlearn_ignore =
+ hwstat->mp_stat_structs_flm->unlearn_ignore;
+ flm->auto_unlearn_done =
+ hwstat->mp_stat_structs_flm->auto_unlearn_done;
+ flm->auto_unlearn_ignore =
+ hwstat->mp_stat_structs_flm->auto_unlearn_ignore;
+ flm->auto_unlearn_fail =
+ hwstat->mp_stat_structs_flm->auto_unlearn_fail;
+ flm->timeout_unlearn_done =
+ hwstat->mp_stat_structs_flm->timeout_unlearn_done;
+ flm->rel_done = hwstat->mp_stat_structs_flm->rel_done;
+ flm->rel_ignore = hwstat->mp_stat_structs_flm->rel_ignore;
+ flm->prb_done = hwstat->mp_stat_structs_flm->prb_done;
+ flm->prb_ignore = hwstat->mp_stat_structs_flm->prb_ignore;
+
+ flm->sta_done = hwstat->mp_stat_structs_flm->sta_done;
+ flm->inf_done = hwstat->mp_stat_structs_flm->inf_done;
+ flm->inf_skip = hwstat->mp_stat_structs_flm->inf_skip;
+ flm->pck_hit = hwstat->mp_stat_structs_flm->pck_hit;
+ flm->pck_miss = hwstat->mp_stat_structs_flm->pck_miss;
+ flm->pck_unh = hwstat->mp_stat_structs_flm->pck_unh;
+ flm->pck_dis = hwstat->mp_stat_structs_flm->pck_dis;
+ flm->csh_hit = hwstat->mp_stat_structs_flm->csh_hit;
+ flm->csh_miss = hwstat->mp_stat_structs_flm->csh_miss;
+ flm->csh_unh = hwstat->mp_stat_structs_flm->csh_unh;
+ flm->cuc_start = hwstat->mp_stat_structs_flm->cuc_start;
+ flm->cuc_move = hwstat->mp_stat_structs_flm->cuc_move;
+ }
+ } else {
+ memset(flm, 0, sizeof(*hwstat->mp_stat_structs_flm));
+ }
+ return nbc * NUM_STAT_RECORD_TYPE_FLOWMATCHER + STAT_INFO_ELEMENTS;
+}
+
+static int read_colors(nt4ga_stat_t *hwstat, uint64_t *val, int nbc)
+{
+ struct ntc_stat_get_data_s *cdata = (struct ntc_stat_get_data_s *)val;
+
+ cdata->nb_counters = (uint64_t)nbc;
+ cdata->timestamp = hwstat->last_timestamp;
+ cdata->is_virt = hwstat->mp_nthw_stat->mb_is_vswitch;
+
+ /* virt/cap same */
+ struct color_type_fields_s *clr =
+ (struct color_type_fields_s *)cdata->data;
+ int c;
+
+ for (c = 0; c < nbc; c++) {
+ clr->pkts = hwstat->mp_stat_structs_color[c].color_packets;
+ clr->octets = hwstat->mp_stat_structs_color[c].color_bytes;
+ clr->tcp_flgs =
+ (uint64_t)hwstat->mp_stat_structs_color[c].tcp_flags;
+ clr++;
+ }
+ return nbc * NUM_STAT_RECORD_TYPE_COLOR + STAT_INFO_ELEMENTS;
+}
+
+static int read_queues(nt4ga_stat_t *hwstat, uint64_t *val, int nbq)
+{
+ struct ntc_stat_get_data_s *qdata = (struct ntc_stat_get_data_s *)val;
+
+ qdata->nb_counters = (uint64_t)nbq;
+ qdata->timestamp = hwstat->last_timestamp;
+ qdata->is_virt = hwstat->mp_nthw_stat->mb_is_vswitch;
+
+ /* virt/cap same */
+ struct queue_type_fields_s *queue =
+ (struct queue_type_fields_s *)qdata->data;
+ int q;
+
+ for (q = 0; q < nbq; q++) {
+ queue->flush_pkts = hwstat->mp_stat_structs_hb[q].flush_packets;
+ queue->drop_pkts = hwstat->mp_stat_structs_hb[q].drop_packets;
+ queue->fwd_pkts = hwstat->mp_stat_structs_hb[q].fwd_packets;
+ queue->dbs_drop_pkts = hwstat->mp_stat_structs_hb[q].dbs_drop_packets;
+ queue->flush_octets = hwstat->mp_stat_structs_hb[q].flush_bytes;
+ queue->drop_octets = hwstat->mp_stat_structs_hb[q].drop_bytes;
+ queue->fwd_octets = hwstat->mp_stat_structs_hb[q].fwd_bytes;
+ queue->dbs_drop_octets = hwstat->mp_stat_structs_hb[q].dbs_drop_bytes;
+ queue++;
+ }
+ return nbq * NUM_STAT_RECORD_TYPE_QUEUE + STAT_INFO_ELEMENTS;
+}
+
+static void copy_rmon_stat(struct port_counters_v2 *cptr,
+ struct stat_rmon_s *rmon)
+{
+ rmon->drop_events = cptr->drop_events;
+ rmon->pkts = cptr->pkts;
+ rmon->octets = cptr->octets;
+ rmon->broadcast_pkts = cptr->broadcast_pkts;
+ rmon->multicast_pkts = cptr->multicast_pkts;
+ rmon->unicast_pkts = cptr->unicast_pkts;
+ rmon->pkts_alignment = cptr->pkts_alignment;
+ rmon->pkts_code_violation = cptr->pkts_code_violation;
+ rmon->pkts_crc = cptr->pkts_crc;
+ rmon->undersize_pkts = cptr->undersize_pkts;
+ rmon->oversize_pkts = cptr->oversize_pkts;
+ rmon->fragments = cptr->fragments;
+ rmon->jabbers_not_truncated = cptr->jabbers_not_truncated;
+ rmon->jabbers_truncated = cptr->jabbers_truncated;
+ rmon->pkts_64_octets = cptr->pkts_64_octets;
+ rmon->pkts_65_to_127_octets = cptr->pkts_65_to_127_octets;
+ rmon->pkts_128_to_255_octets = cptr->pkts_128_to_255_octets;
+ rmon->pkts_256_to_511_octets = cptr->pkts_256_to_511_octets;
+ rmon->pkts_512_to_1023_octets = cptr->pkts_512_to_1023_octets;
+ rmon->pkts_1024_to_1518_octets = cptr->pkts_1024_to_1518_octets;
+ rmon->pkts_1519_to_2047_octets = cptr->pkts_1519_to_2047_octets;
+ rmon->pkts_2048_to_4095_octets = cptr->pkts_2048_to_4095_octets;
+ rmon->pkts_4096_to_8191_octets = cptr->pkts_4096_to_8191_octets;
+ rmon->pkts_8192_to_max_octets = cptr->pkts_8192_to_max_octets;
+}
+
+static int read_rx_counters(nt4ga_stat_t *hwstat, uint64_t *val, int nbp)
+{
+ struct ntc_stat_get_data_s *rxdata = (struct ntc_stat_get_data_s *)val;
+
+ rxdata->nb_counters = (uint64_t)nbp;
+ rxdata->timestamp = hwstat->last_timestamp;
+ rxdata->is_virt = hwstat->mp_nthw_stat->mb_is_vswitch;
+
+ if (rxdata->is_virt) {
+ struct rtx_type_fields_virt_s *rxc =
+ (struct rtx_type_fields_virt_s *)rxdata->data;
+ int p;
+
+ for (p = 0; p < nbp; p++) {
+ rxc->octets =
+ hwstat->virt.mp_stat_structs_port_rx[p].octets;
+ rxc->pkts = hwstat->virt.mp_stat_structs_port_rx[p].pkts;
+ rxc->drop_events =
+ hwstat->virt.mp_stat_structs_port_rx[p].drop_events;
+ rxc->qos_drop_octets =
+ hwstat->virt.mp_stat_structs_port_rx[p]
+ .qos_drop_octets;
+ rxc->qos_drop_pkts = hwstat->virt.mp_stat_structs_port_rx[p]
+ .qos_drop_pkts;
+ rxc++;
+ }
+ return nbp * NUM_STAT_RECORD_TYPE_RX_PORT_VIRT +
+ STAT_INFO_ELEMENTS;
+ } else {
+ struct rx_type_fields_cap_s *rxc =
+ (struct rx_type_fields_cap_s *)rxdata->data;
+ int p;
+
+ for (p = 0; p < nbp; p++) {
+ copy_rmon_stat(&hwstat->cap.mp_stat_structs_port_rx[p],
+ &rxc->rmon);
+
+ /* Rx only port counters */
+ rxc->mac_drop_events =
+ hwstat->cap.mp_stat_structs_port_rx[p]
+ .mac_drop_events;
+ rxc->pkts_lr =
+ hwstat->cap.mp_stat_structs_port_rx[p].pkts_lr;
+ rxc->duplicate =
+ hwstat->cap.mp_stat_structs_port_rx[p].duplicate;
+ rxc->pkts_ip_chksum_error =
+ hwstat->cap.mp_stat_structs_port_rx[p]
+ .pkts_ip_chksum_error;
+ rxc->pkts_udp_chksum_error =
+ hwstat->cap.mp_stat_structs_port_rx[p]
+ .pkts_udp_chksum_error;
+ rxc->pkts_tcp_chksum_error =
+ hwstat->cap.mp_stat_structs_port_rx[p]
+ .pkts_tcp_chksum_error;
+ rxc->pkts_giant_undersize =
+ hwstat->cap.mp_stat_structs_port_rx[p]
+ .pkts_giant_undersize;
+ rxc->pkts_baby_giant =
+ hwstat->cap.mp_stat_structs_port_rx[p]
+ .pkts_baby_giant;
+ rxc->pkts_not_isl_vlan_mpls =
+ hwstat->cap.mp_stat_structs_port_rx[p]
+ .pkts_not_isl_vlan_mpls;
+ rxc->pkts_isl =
+ hwstat->cap.mp_stat_structs_port_rx[p].pkts_isl;
+ rxc->pkts_vlan =
+ hwstat->cap.mp_stat_structs_port_rx[p].pkts_vlan;
+ rxc->pkts_isl_vlan =
+ hwstat->cap.mp_stat_structs_port_rx[p].pkts_isl_vlan;
+ rxc->pkts_mpls =
+ hwstat->cap.mp_stat_structs_port_rx[p].pkts_mpls;
+ rxc->pkts_isl_mpls =
+ hwstat->cap.mp_stat_structs_port_rx[p].pkts_isl_mpls;
+ rxc->pkts_vlan_mpls = hwstat->cap.mp_stat_structs_port_rx[p]
+ .pkts_vlan_mpls;
+ rxc->pkts_isl_vlan_mpls =
+ hwstat->cap.mp_stat_structs_port_rx[p]
+ .pkts_isl_vlan_mpls;
+ rxc->pkts_no_filter = hwstat->cap.mp_stat_structs_port_rx[p]
+ .pkts_no_filter;
+ rxc->pkts_dedup_drop =
+ hwstat->cap.mp_stat_structs_port_rx[p]
+ .pkts_dedup_drop;
+ rxc->pkts_filter_drop =
+ hwstat->cap.mp_stat_structs_port_rx[p]
+ .pkts_filter_drop;
+ rxc->pkts_overflow =
+ hwstat->cap.mp_stat_structs_port_rx[p].pkts_overflow;
+ rxc->pkts_dbs_drop =
+ hwstat->cap.mp_stat_structs_port_rx[p].pkts_dbs_drop;
+ rxc->octets_no_filter =
+ hwstat->cap.mp_stat_structs_port_rx[p]
+ .octets_no_filter;
+ rxc->octets_dedup_drop =
+ hwstat->cap.mp_stat_structs_port_rx[p]
+ .octets_dedup_drop;
+ rxc->octets_filter_drop =
+ hwstat->cap.mp_stat_structs_port_rx[p]
+ .octets_filter_drop;
+ rxc->octets_overflow =
+ hwstat->cap.mp_stat_structs_port_rx[p]
+ .octets_overflow;
+ rxc->octets_dbs_drop =
+ hwstat->cap.mp_stat_structs_port_rx[p]
+ .octets_dbs_drop;
+ rxc->ipft_first_hit = hwstat->cap.mp_stat_structs_port_rx[p]
+ .ipft_first_hit;
+ rxc->ipft_first_not_hit =
+ hwstat->cap.mp_stat_structs_port_rx[p]
+ .ipft_first_not_hit;
+ rxc->ipft_mid_hit =
+ hwstat->cap.mp_stat_structs_port_rx[p].ipft_mid_hit;
+ rxc->ipft_mid_not_hit =
+ hwstat->cap.mp_stat_structs_port_rx[p]
+ .ipft_mid_not_hit;
+ rxc->ipft_last_hit =
+ hwstat->cap.mp_stat_structs_port_rx[p].ipft_last_hit;
+ rxc->ipft_last_not_hit =
+ hwstat->cap.mp_stat_structs_port_rx[p]
+ .ipft_last_not_hit;
+ rxc++;
+ }
+ return nbp * NUM_STAT_RECORD_TYPE_RX_PORT_CAP +
+ STAT_INFO_ELEMENTS;
+ }
+}
+
+static int read_tx_counters(nt4ga_stat_t *hwstat, uint64_t *val, int nbp)
+{
+ struct ntc_stat_get_data_s *txdata = (struct ntc_stat_get_data_s *)val;
+
+ txdata->nb_counters = (uint64_t)nbp;
+ txdata->timestamp = hwstat->last_timestamp;
+ txdata->is_virt = hwstat->mp_nthw_stat->mb_is_vswitch;
+
+ if (txdata->is_virt) {
+ struct rtx_type_fields_virt_s *txc =
+ (struct rtx_type_fields_virt_s *)txdata->data;
+ int p;
+
+ for (p = 0; p < nbp; p++) {
+ txc->octets =
+ hwstat->virt.mp_stat_structs_port_tx[p].octets;
+ txc->pkts = hwstat->virt.mp_stat_structs_port_tx[p].pkts;
+ txc->drop_events =
+ hwstat->virt.mp_stat_structs_port_tx[p].drop_events;
+ txc->qos_drop_octets =
+ hwstat->virt.mp_stat_structs_port_tx[p]
+ .qos_drop_octets;
+ txc->qos_drop_pkts = hwstat->virt.mp_stat_structs_port_tx[p]
+ .qos_drop_pkts;
+ txc++;
+ }
+ return nbp * NUM_STAT_RECORD_TYPE_TX_PORT_VIRT +
+ STAT_INFO_ELEMENTS;
+ } else {
+ struct tx_type_fields_cap_s *txc =
+ (struct tx_type_fields_cap_s *)txdata->data;
+ int p;
+
+ for (p = 0; p < nbp; p++) {
+ copy_rmon_stat(&hwstat->cap.mp_stat_structs_port_tx[p],
+ &txc->rmon);
+ txc->rmon.pkts = hwstat->a_port_tx_packets_total[p];
+ txc++;
+ }
+ return nbp * NUM_STAT_RECORD_TYPE_TX_PORT_CAP +
+ STAT_INFO_ELEMENTS;
+ }
+}
+
+static int func_get_layout_version(void *hdl, int client_id _unused,
+ struct ntconn_header_s *hdr _unused,
+ char **data, int *len)
+{
+ struct stat_hdl *stat = (struct stat_hdl *)hdl;
+
+ if (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) {
+ *data = NULL;
+ *len = 0;
+ return REQUEST_ERR;
+ }
+ *data = malloc(sizeof(int));
+ if (!*data) {
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "memory allocation failed");
+ return REQUEST_ERR;
+ }
+
+ *(int *)*data = stat->p_nt4ga_stat->mp_nthw_stat->mn_stat_layout_version;
+ *len = sizeof(int);
+ return REQUEST_OK;
+}
+
+static int func_get_flm_layout_version(void *hdl, int client_id _unused,
+ struct ntconn_header_s *hdr _unused,
+ char **data, int *len)
+{
+ struct stat_hdl *stat = (struct stat_hdl *)hdl;
+
+ if (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) {
+ *data = NULL;
+ *len = 0;
+ return REQUEST_ERR;
+ }
+ *data = malloc(sizeof(int));
+ if (!*data) {
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "memory allocation failed");
+ return REQUEST_ERR;
+ }
+
+ *(int *)*data = (stat->p_nt4ga_stat->flm_stat_ver < 18) ? 1 : 2;
+ *len = sizeof(int);
+ return REQUEST_OK;
+}
+
+/*
+ * Return total number of 64bit counters occupied by this stat type
+ * additionally, returns total number of records for this type (ie number of queues, ports, etc)
+ */
+static int get_size(struct stat_hdl *stat, enum stat_type_e type,
+ int *num_records)
+{
+ int nrec = 0;
+ int size = 0;
+
+ switch (type) {
+ case STAT_TYPE_COLOR:
+ nrec = stat->p_nt4ga_stat->mp_nthw_stat->m_nb_color_counters / 2;
+ size = nrec * NUM_STAT_RECORD_TYPE_COLOR;
+ break;
+ case STAT_TYPE_QUEUE:
+ nrec = stat->p_nt4ga_stat->mp_nthw_stat->m_nb_rx_host_buffers;
+ size = nrec * NUM_STAT_RECORD_TYPE_QUEUE;
+ break;
+ case STAT_TYPE_RX:
+ nrec = stat->p_nt4ga_stat->mn_rx_ports;
+ size = nrec * ((stat->p_nt4ga_stat->mp_nthw_stat->mb_is_vswitch) ?
+ NUM_STAT_RECORD_TYPE_RX_PORT_VIRT :
+ NUM_STAT_RECORD_TYPE_RX_PORT_CAP);
+ break;
+ case STAT_TYPE_TX:
+ nrec = stat->p_nt4ga_stat->mn_tx_ports;
+ size = nrec * ((stat->p_nt4ga_stat->mp_nthw_stat->mb_is_vswitch) ?
+ NUM_STAT_RECORD_TYPE_TX_PORT_VIRT :
+ NUM_STAT_RECORD_TYPE_TX_PORT_CAP);
+ break;
+ case STAT_TYPE_FLOWMATCHER:
+ nrec = 1;
+ size = nrec * NUM_STAT_RECORD_TYPE_FLOWMATCHER;
+ break;
+ }
+
+ *num_records = nrec;
+ return size + STAT_INFO_ELEMENTS;
+}
+
+static int do_get_stats(struct stat_hdl *stat, char **data, int *len,
+ enum stat_type_e stype,
+ int (*read_counters)(nt4ga_stat_t *, uint64_t *, int))
+{
+ int nbg;
+ int size = get_size(stat, stype, &nbg);
+
+ size *= sizeof(uint64_t);
+ uint64_t *val = (uint64_t *)malloc(size);
+
+ if (!val) {
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "memory allocation failed");
+ return REQUEST_ERR;
+ }
+
+ pthread_mutex_lock(&stat->drv->ntdrv.stat_lck);
+ read_counters(stat->p_nt4ga_stat, val, nbg);
+ pthread_mutex_unlock(&stat->drv->ntdrv.stat_lck);
+
+ *data = (char *)val;
+ *len = size;
+ return REQUEST_OK;
+}
+
+/*
+ * Stat Request functions
+ */
+static int func_get_flm(void *hdl, int client_id _unused,
+ struct ntconn_header_s *hdr _unused, char **data,
+ int *len)
+{
+ struct stat_hdl *stat = (struct stat_hdl *)hdl;
+
+ if (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) {
+ *data = NULL;
+ *len = 0;
+ return REQUEST_ERR;
+ }
+ return do_get_stats(stat, data, len, STAT_TYPE_FLOWMATCHER, read_flm);
+}
+
+static int func_get_color(void *hdl, int client_id _unused,
+ struct ntconn_header_s *hdr _unused, char **data,
+ int *len)
+{
+ struct stat_hdl *stat = (struct stat_hdl *)hdl;
+
+ if (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) {
+ *data = NULL;
+ *len = 0;
+ return REQUEST_ERR;
+ }
+ return do_get_stats(stat, data, len, STAT_TYPE_COLOR, read_colors);
+}
+
+static int func_get_queue(void *hdl, int client_id _unused,
+ struct ntconn_header_s *hdr _unused, char **data,
+ int *len)
+{
+ struct stat_hdl *stat = (struct stat_hdl *)hdl;
+
+ if (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) {
+ *data = NULL;
+ *len = 0;
+ return REQUEST_ERR;
+ }
+ return do_get_stats(stat, data, len, STAT_TYPE_QUEUE, read_queues);
+}
+
+static int func_get_rx_counters(void *hdl, int client_id _unused,
+ struct ntconn_header_s *hdr _unused,
+ char **data, int *len)
+{
+ struct stat_hdl *stat = (struct stat_hdl *)hdl;
+
+ if (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) {
+ *data = NULL;
+ *len = 0;
+ return REQUEST_ERR;
+ }
+ return do_get_stats(stat, data, len, STAT_TYPE_RX, read_rx_counters);
+}
+
+static int func_get_tx_counters(void *hdl, int client_id _unused,
+ struct ntconn_header_s *hdr _unused,
+ char **data, int *len)
+{
+ struct stat_hdl *stat = (struct stat_hdl *)hdl;
+
+ if (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) {
+ *data = NULL;
+ *len = 0;
+ return REQUEST_ERR;
+ }
+ return do_get_stats(stat, data, len, STAT_TYPE_TX, read_tx_counters);
+}
+
+/*
+ * Snapshot handling. This is to ensure atomic reading of all statistics in one collection
+ */
+
+static struct snaps_s *find_client_snap_data(struct stat_hdl *stat,
+ int client_id,
+ struct snaps_s **parent)
+{
+ struct snaps_s *snaps = stat->snaps_base;
+
+ if (parent)
+ *parent = NULL;
+ while (snaps && snaps->client_id != client_id) {
+ if (parent)
+ *parent = snaps;
+ snaps = snaps->next;
+ }
+
+ return snaps;
+}
+
+static struct snaps_s *get_client_snap_data(struct stat_hdl *stat,
+ int client_id)
+{
+ struct snaps_s *snaps = find_client_snap_data(stat, client_id, NULL);
+
+ if (!snaps) {
+ snaps = malloc(sizeof(struct snaps_s)); /* return NULL on malloc failure */
+ if (snaps) {
+ snaps->client_id = client_id;
+ snaps->next = stat->snaps_base;
+ stat->snaps_base = snaps;
+ snaps->buffer = NULL;
+ }
+ }
+ return snaps;
+}
+
+static int func_snapshot(void *hdl, int client_id,
+ struct ntconn_header_s *hdr _unused, char **data,
+ int *len)
+{
+ struct stat_hdl *stat = (struct stat_hdl *)hdl;
+ int nbc, nbq, nbpr, nbpt;
+ struct snaps_s *snaps;
+
+ if (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) {
+ *data = NULL;
+ *len = 0;
+ return REQUEST_ERR;
+ }
+ snaps = get_client_snap_data(stat, client_id);
+ if (!snaps)
+ goto err_out;
+
+ if (snaps->buffer)
+ free(snaps->buffer);
+
+ snaps->snap_addr[SNAP_COLORS].size =
+ (unsigned int)get_size(stat, STAT_TYPE_COLOR, &nbc);
+ snaps->snap_addr[SNAP_QUEUES].size =
+ (unsigned int)get_size(stat, STAT_TYPE_QUEUE, &nbq);
+ snaps->snap_addr[SNAP_RX_PORT].size =
+ (unsigned int)get_size(stat, STAT_TYPE_RX, &nbpr);
+ snaps->snap_addr[SNAP_TX_PORT].size =
+ (unsigned int)get_size(stat, STAT_TYPE_TX, &nbpt);
+
+ unsigned int tot_size = snaps->snap_addr[SNAP_COLORS].size +
+ snaps->snap_addr[SNAP_QUEUES].size +
+ snaps->snap_addr[SNAP_RX_PORT].size +
+ snaps->snap_addr[SNAP_TX_PORT].size;
+
+ snaps->buffer = malloc(tot_size * sizeof(uint64_t));
+ if (!snaps->buffer) {
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "memory allocation failed");
+ return REQUEST_ERR;
+ }
+ uint64_t *val = snaps->buffer;
+
+ snaps->snap_addr[SNAP_COLORS].ptr = val;
+ pthread_mutex_lock(&stat->drv->ntdrv.stat_lck);
+ unsigned int size = read_colors(stat->p_nt4ga_stat, val, nbc);
+
+ if (size != snaps->snap_addr[SNAP_COLORS].size) {
+ NT_LOG(ERR, NTCONNECT, "stat.snapshot: color size mismatch");
+ goto err_out;
+ }
+
+ val += size;
+ snaps->snap_addr[SNAP_QUEUES].ptr = val;
+ size = read_queues(stat->p_nt4ga_stat, val, nbq);
+ if (size != snaps->snap_addr[SNAP_QUEUES].size) {
+ NT_LOG(ERR, NTCONNECT,
+ "stat.snapshot: queue statistic size mismatch");
+ goto err_out;
+ }
+
+ val += size;
+ snaps->snap_addr[SNAP_RX_PORT].ptr = val;
+ size = read_rx_counters(stat->p_nt4ga_stat, val, nbpr);
+ if (size != snaps->snap_addr[SNAP_RX_PORT].size) {
+ NT_LOG(ERR, NTCONNECT,
+ "stat.snapshot: Rx port statistic size mismatch %i, %i",
+ size, snaps->snap_addr[SNAP_RX_PORT].size);
+ goto err_out;
+ }
+
+ val += size;
+ snaps->snap_addr[SNAP_TX_PORT].ptr = val;
+ size = read_tx_counters(stat->p_nt4ga_stat, val, nbpt);
+ if (size != snaps->snap_addr[SNAP_TX_PORT].size) {
+ NT_LOG(ERR, NTCONNECT,
+ "stat.snapshot: Tx port statistic size mismatch");
+ goto err_out;
+ }
+
+ pthread_mutex_unlock(&stat->drv->ntdrv.stat_lck);
+
+ *data = NULL;
+ *len = 0;
+ return REQUEST_OK;
+
+err_out:
+ pthread_mutex_unlock(&stat->drv->ntdrv.stat_lck);
+ return ntconn_error(data, len, "stat",
+ NTCONN_ERR_CODE_INTERNAL_REPLY_ERROR);
+}
+
+static int get_snap_data(void *hdl, int client_id, char **data, int *len,
+ enum snap_addr_select_e snap_addr_idx)
+{
+ struct stat_hdl *stat = (struct stat_hdl *)hdl;
+ struct snaps_s *snaps = find_client_snap_data(stat, client_id, NULL);
+
+ if (!snaps || !snaps->buffer)
+ return ntconn_error(data, len, "stat", NTCONN_ERR_CODE_NO_DATA);
+
+ int ln = snaps->snap_addr[snap_addr_idx].size * sizeof(uint64_t);
+
+ *data = malloc(ln);
+ if (!data) {
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "memory allocation failed");
+ return REQUEST_ERR;
+ }
+ memcpy(*data, snaps->snap_addr[snap_addr_idx].ptr, ln);
+ *len = ln;
+
+ return REQUEST_OK;
+}
+
+static int func_get_snap_colors(void *hdl, int client_id,
+ struct ntconn_header_s *hdr _unused,
+ char **data, int *len)
+{
+ return get_snap_data(hdl, client_id, data, len, SNAP_COLORS);
+}
+
+static int func_get_snap_queues(void *hdl, int client_id,
+ struct ntconn_header_s *hdr _unused,
+ char **data, int *len)
+{
+ return get_snap_data(hdl, client_id, data, len, SNAP_QUEUES);
+}
+
+static int func_get_snap_rx_port(void *hdl, int client_id,
+ struct ntconn_header_s *hdr _unused,
+ char **data, int *len)
+{
+ return get_snap_data(hdl, client_id, data, len, SNAP_RX_PORT);
+}
+
+static int func_get_snap_tx_port(void *hdl, int client_id,
+ struct ntconn_header_s *hdr _unused,
+ char **data, int *len)
+{
+ return get_snap_data(hdl, client_id, data, len, SNAP_TX_PORT);
+}
+
+/*
+ * Stat main request function
+ */
+static int stat_request(void *hdl, int client_id _unused,
+ struct ntconn_header_s *hdr, char *function,
+ char **data, int *len)
+{
+ return execute_function(this_module_name, hdl, client_id, hdr, function,
+ stat_entry_funcs, data, len, 0);
+}
+
+static void stat_free_data(void *hdl _unused, char *data)
+{
+ free(data);
+}
+
+static void stat_client_cleanup(void *hdl, int client_id)
+{
+ struct stat_hdl *stat = (struct stat_hdl *)hdl;
+ struct snaps_s *snaps_parent;
+ struct snaps_s *snaps =
+ find_client_snap_data(stat, client_id, &snaps_parent);
+
+ if (!snaps)
+ return;
+
+ if (snaps_parent)
+ snaps_parent->next = snaps->next;
+ else
+ stat->snaps_base = snaps->next;
+
+ if (snaps->buffer)
+ free(snaps->buffer);
+ free(snaps);
+}
+
+static const ntconnapi_t ntconn_stat_op = {
+ this_module_name, STAT_VERSION_MAJOR, STAT_VERSION_MINOR,
+ stat_request, stat_free_data, stat_client_cleanup
+};
+
+int ntconn_stat_register(struct drv_s *drv)
+{
+ stat_hdl.drv = drv;
+ stat_hdl.p_nt4ga_stat = &drv->ntdrv.adapter_info.nt4ga_stat;
+
+ /* Check supported Layout_versions by this module */
+ size_t i;
+
+ for (i = 0; i < NUM_LAYOUT_VERSIONS_SUPPORTED; i++) {
+ if (stat_hdl.p_nt4ga_stat->mp_nthw_stat->mn_stat_layout_version ==
+ layout_versions_supported[i])
+ break;
+ }
+
+ if (i == NUM_LAYOUT_VERSIONS_SUPPORTED) {
+ NT_LOG(ERR, NTCONNECT,
+ "stat: layout version %i is not supported. Module will not be activated",
+ stat_hdl.p_nt4ga_stat->mp_nthw_stat->mn_stat_layout_version);
+ return -1;
+ }
+
+ return register_ntconn_mod(&drv->p_dev->addr, (void *)&stat_hdl,
+ &ntconn_stat_op);
+}
diff --git a/drivers/net/ntnic/ntconnect_modules/ntconn_test.c b/drivers/net/ntnic/ntconnect_modules/ntconn_test.c
new file mode 100644
index 0000000000..907ea4ff5f
--- /dev/null
+++ b/drivers/net/ntnic/ntconnect_modules/ntconn_test.c
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include <errno.h>
+#include "ntnic_ethdev.h"
+#include "ntconnect.h"
+#include "ntos_system.h"
+#include "ntconn_modules.h"
+#include "ntconn_mod_helper.h"
+#include "nt_util.h"
+#include "ntlog.h"
+#include "ntnic_vf_vdpa.h"
+
+#include "ntconnect_api_test.h"
+
+#define NTCONN_TEST_VERSION_MAJOR 0U
+#define NTCONN_TEST_VERSION_MINOR 1U
+
+#define this_module_name "ntconnect_test"
+
+#define MAX_CLIENTS 32
+
+#define UNUSED __rte_unused
+
+static struct test_hdl_s {
+ struct drv_s *drv;
+} test_hdl[MAX_CLIENTS];
+
+/*
+ * Test functions
+ */
+static int func_test(void *hdl, int client_id, struct ntconn_header_s *hdr,
+ char **data, int *len);
+static struct func_s adapter_entry_funcs[] = {
+ { "test", NULL, func_test },
+ { NULL, NULL, NULL },
+};
+
+static int func_test(void *hdl _unused, int client_id _unused,
+ struct ntconn_header_s *hdr, char **data, int *len)
+{
+ int status = 0;
+ int number = 0;
+ uint32_t size;
+ struct test_s *test_cpy = (struct test_s *)&(*data)[hdr->len];
+
+ if (hdr->blob_len < sizeof(struct test_s)) {
+ NT_LOG(ERR, NTCONNECT, "Error in test data: to small");
+ status = -1;
+ goto TEST_ERROR;
+ }
+
+ number = test_cpy->number;
+ size = sizeof(struct test_s) + sizeof(uint64_t) * number;
+
+ if (hdr->blob_len != size) {
+ NT_LOG(ERR, NTCONNECT, "Error in test data: wrong size");
+ status = -1;
+ goto TEST_ERROR;
+ }
+
+ {
+ *data = malloc(sizeof(struct test_s) +
+ number * sizeof(uint64_t));
+ if (!*data)
+ goto TEST_ERROR_MALLOC;
+ struct test_s *return_value = (struct test_s *)*data;
+ *len = sizeof(struct test_s) + number * sizeof(uint64_t);
+ for (int i = 0; i < number; i++)
+ return_value->test[i] = test_cpy->test[i];
+ return_value->status = 0;
+ return_value->number = number;
+ return REQUEST_OK;
+ }
+
+TEST_ERROR:
+
+ {
+ *data = malloc(sizeof(struct test_s));
+ if (!*data)
+ goto TEST_ERROR_MALLOC;
+ struct test_s *return_value = (struct test_s *)*data;
+ *len = sizeof(struct test_s);
+ return_value->status = status;
+ return_value->number = 0;
+ return REQUEST_OK;
+ }
+
+TEST_ERROR_MALLOC:
+
+ *len = 0;
+ NT_LOG(ERR, NTCONNECT, "Not able to allocate memory %s", __func__);
+ return REQUEST_ERR;
+}
+
+enum {
+ FLOW_API_FUNC_CREATE,
+ FLOW_API_FUNC_VALIDATE,
+};
+
+static int test_request(void *hdl, int client_id _unused,
+ struct ntconn_header_s *hdr, char *function,
+ char **data, int *len)
+{
+ return execute_function(this_module_name, hdl, client_id, hdr, function,
+ adapter_entry_funcs, data, len, 0);
+}
+
+static void test_free_data(void *hdl _unused, char *data)
+{
+ if (data)
+ free(data);
+}
+
+static void test_client_cleanup(void *hdl _unused, int client_id _unused)
+{
+ /* Nothing to do */
+}
+
+static const ntconnapi_t ntconn_test_op = { this_module_name,
+ NTCONN_TEST_VERSION_MAJOR,
+ NTCONN_TEST_VERSION_MINOR,
+ test_request,
+ test_free_data,
+ test_client_cleanup
+ };
+
+int ntconn_test_register(struct drv_s *drv)
+{
+ int i;
+
+ for (i = 0; i < MAX_CLIENTS; i++) {
+ if (test_hdl[i].drv == NULL)
+ break;
+ }
+ if (i == MAX_CLIENTS) {
+ NT_LOG(ERR, NTCONNECT,
+ "Cannot register more adapters into NtConnect framework");
+ return -1;
+ }
+
+ test_hdl[i].drv = drv;
+ return register_ntconn_mod(&drv->p_dev->addr, (void *)&test_hdl[i],
+ &ntconn_test_op);
+}
--
2.39.3
next prev parent reply other threads:[~2023-08-31 12:24 UTC|newest]
Thread overview: 142+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-08-16 13:25 [PATCH 1/8] net/ntnic: initial commit which adds register defines Mykola Kostenok
2023-08-16 13:25 ` [PATCH 2/8] net/ntnic: adds core registers and fpga functionality Mykola Kostenok
2023-08-16 13:25 ` [PATCH 3/8] net/ntnic: adds NT200A02 adapter support Mykola Kostenok
2023-08-16 13:25 ` [PATCH 4/8] net/ntnic: adds flow related FPGA functionality Mykola Kostenok
2023-08-16 13:25 ` [PATCH 5/8] net/ntnic: adds FPGA abstraction layer Mykola Kostenok
2023-08-16 13:25 ` [PATCH 6/8] net/ntnic: adds flow logic Mykola Kostenok
2023-08-16 13:25 ` [PATCH 7/8] net/ntnic: adds ethdev and makes PMD available Mykola Kostenok
2023-08-16 13:25 ` [PATCH 8/8] net/ntnic: adds socket connection to PMD Mykola Kostenok
2023-08-16 14:46 ` Stephen Hemminger
2023-08-25 13:52 ` Christian Koue Muf
2023-08-16 14:47 ` Stephen Hemminger
2023-08-17 14:43 ` [PATCH v2 1/8] net/ntnic: initial commit which adds register defines Mykola Kostenok
2023-08-17 14:43 ` [PATCH v2 2/8] net/ntnic: adds core registers and fpga functionality Mykola Kostenok
2023-08-17 14:43 ` [PATCH v2 3/8] net/ntnic: adds NT200A02 adapter support Mykola Kostenok
2023-08-17 14:43 ` [PATCH v2 4/8] net/ntnic: adds flow related FPGA functionality Mykola Kostenok
2023-08-17 14:43 ` [PATCH v2 5/8] net/ntnic: adds FPGA abstraction layer Mykola Kostenok
2023-08-17 14:43 ` [PATCH v2 6/8] net/ntnic: adds flow logic Mykola Kostenok
2023-08-17 14:43 ` [PATCH v2 7/8] net/ntnic: adds ethdev and makes PMD available Mykola Kostenok
2023-08-17 14:43 ` [PATCH v2 8/8] net/ntnic: adds socket connection to PMD Mykola Kostenok
2023-08-17 22:08 ` [PATCH v2 1/8] net/ntnic: initial commit which adds register defines Tyler Retzlaff
2023-08-18 11:01 ` Mykola Kostenok
2023-08-18 18:41 ` [PATCH v4 " Mykola Kostenok
2023-08-18 18:41 ` [PATCH v4 2/8] net/ntnic: adds core registers and fpga functionality Mykola Kostenok
2023-08-18 18:41 ` [PATCH v4 3/8] net/ntnic: adds NT200A02 adapter support Mykola Kostenok
2023-08-18 18:41 ` [PATCH v4 4/8] net/ntnic: adds flow related FPGA functionality Mykola Kostenok
2023-08-18 18:41 ` [PATCH v4 5/8] net/ntnic: adds FPGA abstraction layer Mykola Kostenok
2023-08-18 18:41 ` [PATCH v4 6/8] net/ntnic: adds flow logic Mykola Kostenok
2023-08-18 18:41 ` [PATCH v4 7/8] net/ntnic: adds ethdev and makes PMD available Mykola Kostenok
2023-08-18 18:41 ` [PATCH v4 8/8] net/ntnic: adds socket connection to PMD Mykola Kostenok
2023-08-21 11:34 ` [PATCH v5 1/8] net/ntnic: initial commit which adds register defines Mykola Kostenok
2023-08-21 11:34 ` [PATCH v5 2/8] net/ntnic: adds core registers and fpga functionality Mykola Kostenok
2023-08-21 11:34 ` [PATCH v5 3/8] net/ntnic: adds NT200A02 adapter support Mykola Kostenok
2023-08-21 11:34 ` [PATCH v5 4/8] net/ntnic: adds flow related FPGA functionality Mykola Kostenok
2023-08-21 11:34 ` [PATCH v5 5/8] net/ntnic: adds FPGA abstraction layer Mykola Kostenok
2023-08-21 11:34 ` [PATCH v5 6/8] net/ntnic: adds flow logic Mykola Kostenok
2023-08-21 11:34 ` [PATCH v5 7/8] net/ntnic: adds ethdev and makes PMD available Mykola Kostenok
2023-08-21 11:34 ` [PATCH v5 8/8] net/ntnic: adds socket connection to PMD Mykola Kostenok
2023-08-21 13:54 ` [PATCH v6 1/8] net/ntnic: initial commit which adds register defines Mykola Kostenok
2023-08-21 13:54 ` [PATCH v6 2/8] net/ntnic: adds core registers and fpga functionality Mykola Kostenok
2023-08-21 13:54 ` [PATCH v6 3/8] net/ntnic: adds NT200A02 adapter support Mykola Kostenok
2023-08-21 13:54 ` [PATCH v6 4/8] net/ntnic: adds flow related FPGA functionality Mykola Kostenok
2023-08-21 13:54 ` [PATCH v6 5/8] net/ntnic: adds FPGA abstraction layer Mykola Kostenok
2023-08-21 13:54 ` [PATCH v6 6/8] net/ntnic: adds flow logic Mykola Kostenok
2023-08-21 13:54 ` [PATCH v6 7/8] net/ntnic: adds ethdev and makes PMD available Mykola Kostenok
2023-08-21 13:54 ` [PATCH v6 8/8] net/ntnic: adds socket connection to PMD Mykola Kostenok
2023-08-22 15:41 ` [PATCH v7 1/8] net/ntnic: initial commit which adds register defines Mykola Kostenok
2023-08-22 15:41 ` [PATCH v7 2/8] net/ntnic: adds core registers and fpga functionality Mykola Kostenok
2023-08-22 15:41 ` [PATCH v7 3/8] net/ntnic: adds NT200A02 adapter support Mykola Kostenok
2023-08-22 15:41 ` [PATCH v7 4/8] net/ntnic: adds flow related FPGA functionality Mykola Kostenok
2023-08-22 15:41 ` [PATCH v7 5/8] net/ntnic: adds FPGA abstraction layer Mykola Kostenok
2023-08-22 15:41 ` [PATCH v7 6/8] net/ntnic: adds flow logic Mykola Kostenok
2023-08-22 15:41 ` [PATCH v7 7/8] net/ntnic: adds ethdev and makes PMD available Mykola Kostenok
2023-08-29 8:13 ` David Marchand
2023-08-22 15:41 ` [PATCH v7 8/8] net/ntnic: adds socket connection to PMD Mykola Kostenok
2023-08-29 8:15 ` [PATCH v8 1/8] net/ntnic: initial commit which adds register defines Mykola Kostenok
2023-08-29 8:15 ` [PATCH v8 2/8] net/ntnic: adds core registers and fpga functionality Mykola Kostenok
2023-08-29 8:15 ` [PATCH v8 3/8] net/ntnic: adds NT200A02 adapter support Mykola Kostenok
2023-08-29 8:15 ` [PATCH v8 4/8] net/ntnic: adds flow related FPGA functionality Mykola Kostenok
2023-08-29 8:15 ` [PATCH v8 5/8] net/ntnic: adds FPGA abstraction layer Mykola Kostenok
2023-08-29 8:15 ` [PATCH v8 6/8] net/ntnic: adds flow logic Mykola Kostenok
2023-08-29 8:15 ` [PATCH v8 7/8] net/ntnic: adds ethdev and makes PMD available Mykola Kostenok
2023-08-29 8:15 ` [PATCH v8 8/8] net/ntnic: adds socket connection to PMD Mykola Kostenok
2023-08-29 10:17 ` [PATCH v9 1/8] net/ntnic: initial commit which adds register defines Mykola Kostenok
2023-08-29 10:17 ` [PATCH v9 2/8] net/ntnic: adds core registers and fpga functionality Mykola Kostenok
2023-08-29 10:17 ` [PATCH v9 3/8] net/ntnic: adds NT200A02 adapter support Mykola Kostenok
2023-08-29 10:17 ` [PATCH v9 4/8] net/ntnic: adds flow related FPGA functionality Mykola Kostenok
2023-08-29 10:17 ` [PATCH v9 5/8] net/ntnic: adds FPGA abstraction layer Mykola Kostenok
2023-08-29 10:17 ` [PATCH v9 6/8] net/ntnic: adds flow logic Mykola Kostenok
2023-08-29 10:17 ` [PATCH v9 7/8] net/ntnic: adds ethdev and makes PMD available Mykola Kostenok
2023-08-29 10:17 ` [PATCH v9 8/8] net/ntnic: adds socket connection to PMD Mykola Kostenok
2023-08-30 16:51 ` [PATCH v10 1/8] net/ntnic: initial commit which adds register defines Mykola Kostenok
2023-08-30 16:51 ` [PATCH v10 2/8] net/ntnic: adds core registers and fpga functionality Mykola Kostenok
2023-08-30 16:51 ` [PATCH v10 3/8] net/ntnic: adds NT200A02 adapter support Mykola Kostenok
2023-08-30 16:51 ` [PATCH v10 4/8] net/ntnic: adds flow related FPGA functionality Mykola Kostenok
2023-08-30 16:51 ` [PATCH v10 5/8] net/ntnic: adds FPGA abstraction layer Mykola Kostenok
2023-08-30 16:51 ` [PATCH v10 6/8] net/ntnic: adds flow logic Mykola Kostenok
2023-08-30 16:51 ` [PATCH v10 7/8] net/ntnic: adds ethdev and makes PMD available Mykola Kostenok
2023-08-30 16:51 ` [PATCH v10 8/8] net/ntnic: adds socket connection to PMD Mykola Kostenok
2023-08-31 12:23 ` [PATCH v11 1/8] net/ntnic: initial commit which adds register defines Mykola Kostenok
2023-08-31 12:23 ` [PATCH v11 2/8] net/ntnic: adds core registers and fpga functionality Mykola Kostenok
2023-08-31 12:23 ` [PATCH v11 3/8] net/ntnic: adds NT200A02 adapter support Mykola Kostenok
2023-08-31 12:23 ` [PATCH v11 4/8] net/ntnic: adds flow related FPGA functionality Mykola Kostenok
2023-08-31 12:23 ` [PATCH v11 5/8] net/ntnic: adds FPGA abstraction layer Mykola Kostenok
2023-08-31 12:23 ` [PATCH v11 6/8] net/ntnic: adds flow logic Mykola Kostenok
2023-08-31 12:23 ` [PATCH v11 7/8] net/ntnic: adds ethdev and makes PMD available Mykola Kostenok
2023-08-31 12:23 ` Mykola Kostenok [this message]
2023-08-31 13:51 ` [PATCH v12 1/8] net/ntnic: initial commit which adds register defines Mykola Kostenok
2023-08-31 13:51 ` [PATCH v12 2/8] net/ntnic: adds core registers and fpga functionality Mykola Kostenok
2023-08-31 13:51 ` [PATCH v12 3/8] net/ntnic: adds NT200A02 adapter support Mykola Kostenok
2023-08-31 13:51 ` [PATCH v12 4/8] net/ntnic: adds flow related FPGA functionality Mykola Kostenok
2023-08-31 13:51 ` [PATCH v12 5/8] net/ntnic: adds FPGA abstraction layer Mykola Kostenok
2023-08-31 13:51 ` [PATCH v12 6/8] net/ntnic: adds flow logic Mykola Kostenok
2023-08-31 13:51 ` [PATCH v12 7/8] net/ntnic: adds ethdev and makes PMD available Mykola Kostenok
2023-08-31 13:51 ` [PATCH v12 8/8] net/ntnic: adds socket connection to PMD Mykola Kostenok
2023-09-01 12:18 ` [PATCH v13 1/8] net/ntnic: initial commit which adds register defines Mykola Kostenok
2023-09-01 12:18 ` [PATCH v13 2/8] net/ntnic: adds core registers and fpga functionality Mykola Kostenok
2023-09-01 12:18 ` [PATCH v13 3/8] net/ntnic: adds NT200A02 adapter support Mykola Kostenok
2023-09-01 12:18 ` [PATCH v13 4/8] net/ntnic: adds flow related FPGA functionality Mykola Kostenok
2023-09-01 12:18 ` [PATCH v13 5/8] net/ntnic: adds FPGA abstraction layer Mykola Kostenok
2023-09-01 12:18 ` [PATCH v13 6/8] net/ntnic: adds flow logic Mykola Kostenok
2023-09-01 12:18 ` [PATCH v13 7/8] net/ntnic: adds ethdev and makes PMD available Mykola Kostenok
2023-09-01 12:18 ` [PATCH v13 8/8] net/ntnic: adds socket connection to PMD Mykola Kostenok
2023-09-02 17:26 ` Patrick Robb
2023-09-04 13:53 ` [PATCH v14 1/8] net/ntnic: initial commit which adds register defines Mykola Kostenok
2023-09-04 13:53 ` [PATCH v14 2/8] net/ntnic: adds core registers and fpga functionality Mykola Kostenok
2023-09-04 13:53 ` [PATCH v14 3/8] net/ntnic: adds NT200A02 adapter support Mykola Kostenok
2023-09-04 13:53 ` [PATCH v14 4/8] net/ntnic: adds flow related FPGA functionality Mykola Kostenok
2023-09-04 13:53 ` [PATCH v14 5/8] net/ntnic: adds FPGA abstraction layer Mykola Kostenok
2023-09-04 13:53 ` [PATCH v14 6/8] net/ntnic: adds flow logic Mykola Kostenok
2023-09-04 13:54 ` [PATCH v14 7/8] net/ntnic: adds ethdev and makes PMD available Mykola Kostenok
2023-09-04 13:54 ` [PATCH v14 8/8] net/ntnic: adds socket connection to PMD Mykola Kostenok
2023-09-05 14:54 ` [PATCH v15 1/8] net/ntnic: initial commit which adds register defines Mykola Kostenok
2023-09-05 14:54 ` [PATCH v15 2/8] net/ntnic: adds core registers and fpga functionality Mykola Kostenok
2023-09-05 14:54 ` [PATCH v15 3/8] net/ntnic: adds NT200A02 adapter support Mykola Kostenok
2023-09-05 14:54 ` [PATCH v15 4/8] net/ntnic: adds flow related FPGA functionality Mykola Kostenok
2023-09-05 14:54 ` [PATCH v15 5/8] net/ntnic: adds FPGA abstraction layer Mykola Kostenok
2023-09-05 14:54 ` [PATCH v15 6/8] net/ntnic: adds flow logic Mykola Kostenok
2023-09-05 14:54 ` [PATCH v15 7/8] net/ntnic: adds ethdev and makes PMD available Mykola Kostenok
2023-09-05 14:54 ` [PATCH v15 8/8] net/ntnic: adds socket connection to PMD Mykola Kostenok
2023-09-08 16:07 ` [PATCH v16 1/8] net/ntnic: initial commit which adds register defines Mykola Kostenok
2023-09-08 16:07 ` [PATCH v16 2/8] net/ntnic: adds core registers and fpga functionality Mykola Kostenok
2023-09-08 16:07 ` [PATCH v16 3/8] net/ntnic: adds NT200A02 adapter support Mykola Kostenok
2023-09-08 16:07 ` [PATCH v16 4/8] net/ntnic: adds flow related FPGA functionality Mykola Kostenok
2023-09-08 16:07 ` [PATCH v16 5/8] net/ntnic: adds FPGA abstraction layer Mykola Kostenok
2023-09-08 16:07 ` [PATCH v16 6/8] net/ntnic: adds flow logic Mykola Kostenok
2023-09-08 16:07 ` [PATCH v16 7/8] net/ntnic: adds ethdev and makes PMD available Mykola Kostenok
2023-09-08 16:07 ` [PATCH v16 8/8] net/ntnic: adds socket connection to PMD Mykola Kostenok
2023-09-15 15:54 ` [PATCH v16 1/8] net/ntnic: initial commit which adds register defines Ferruh Yigit
2023-09-15 18:37 ` Morten Brørup
2023-09-18 9:33 ` Ferruh Yigit
2023-09-19 9:06 ` Christian Koue Muf
2023-09-20 9:48 ` Ferruh Yigit
2023-09-20 13:17 ` Thomas Monjalon
2023-09-21 14:04 ` Ferruh Yigit
2023-09-29 9:21 ` Christian Koue Muf
2023-09-29 9:46 ` Ferruh Yigit
2023-09-29 10:23 ` Thomas Monjalon
2023-10-09 7:57 ` Christian Koue Muf
2023-10-09 9:52 ` Ferruh Yigit
2024-03-29 11:24 ` Ferruh Yigit
2024-04-03 10:55 ` Mykola Kostenok
2024-04-04 12:49 ` Ferruh Yigit
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230831122323.967504-8-mko-plv@napatech.com \
--to=mko-plv@napatech.com \
--cc=ckm@napatech.com \
--cc=dev@dpdk.org \
--cc=thomas@monjalon.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).