get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/130584/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 130584,
    "url": "http://patches.dpdk.org/api/patches/130584/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20230821135426.1478754-8-mko-plv@napatech.com/",
    "project": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20230821135426.1478754-8-mko-plv@napatech.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20230821135426.1478754-8-mko-plv@napatech.com",
    "date": "2023-08-21T13:54:26",
    "name": "[v6,8/8] net/ntnic: adds socket connection to PMD",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "f3fd17a01c3088f68319d8897ec7074757562614",
    "submitter": {
        "id": 3153,
        "url": "http://patches.dpdk.org/api/people/3153/?format=api",
        "name": "Mykola Kostenok",
        "email": "mko-plv@napatech.com"
    },
    "delegate": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20230821135426.1478754-8-mko-plv@napatech.com/mbox/",
    "series": [
        {
            "id": 29293,
            "url": "http://patches.dpdk.org/api/series/29293/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=29293",
            "date": "2023-08-21T13:54:21",
            "name": "[v6,1/8] net/ntnic: initial commit which adds register defines",
            "version": 6,
            "mbox": "http://patches.dpdk.org/series/29293/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/130584/comments/",
    "check": "warning",
    "checks": "http://patches.dpdk.org/api/patches/130584/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id B4F68430C1;\n\tMon, 21 Aug 2023 15:55:53 +0200 (CEST)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 70D9C43278;\n\tMon, 21 Aug 2023 15:54:49 +0200 (CEST)",
            "from egress-ip33a.ess.de.barracuda.com\n (egress-ip33a.ess.de.barracuda.com [18.185.115.192])\n by mails.dpdk.org (Postfix) with ESMTP id 188584326B\n for <dev@dpdk.org>; Mon, 21 Aug 2023 15:54:45 +0200 (CEST)",
            "from EUR05-DB8-obe.outbound.protection.outlook.com\n (mail-db8eur05lp2107.outbound.protection.outlook.com [104.47.17.107]) by\n mx-outbound43-64.eu-central-1c.ess.aws.cudaops.com (version=TLSv1.2\n cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO);\n Mon, 21 Aug 2023 13:54:37 +0000",
            "from DB8PR04CA0013.eurprd04.prod.outlook.com (2603:10a6:10:110::23)\n by AM0P190MB0674.EURP190.PROD.OUTLOOK.COM (2603:10a6:208:194::20)\n with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6699.24; Mon, 21 Aug\n 2023 13:54:31 +0000",
            "from DU2PEPF0001E9C5.eurprd03.prod.outlook.com\n (2603:10a6:10:110:cafe::90) by DB8PR04CA0013.outlook.office365.com\n (2603:10a6:10:110::23) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6699.20 via Frontend\n Transport; Mon, 21 Aug 2023 13:54:31 +0000",
            "from localhost.localdomain.com (178.72.21.4) by\n DU2PEPF0001E9C5.mail.protection.outlook.com (10.167.8.74) with Microsoft SMTP\n Server id 15.20.6699.14 via Frontend Transport; Mon, 21 Aug 2023 13:54:31\n +0000"
        ],
        "ARC-Seal": "i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none;\n b=W/CMwdpsQKFDMo2NbVrbCT7jarp/kF1lf79Ye5tpNAg87V89oNhV4eIA9IPuUoASbrE5tmXM82gGaiZGcpUoxpkjny8qTfnqaCGF8oJrgbxTc2WQ/IFWOO9j2emnZoQnG/+Enq5AaI1dM1YPgj7TAUHQOQ9okO2qAB0B+5IuO5Qkae3UJBaKaHJ3gxbWOTtFK7HUMNJ+exSUlR/yZaExopAQfX06WRsdp6aq0jcVXLJPaxK+m2Ck/7kVGHZGMcQDPu714Fl4wHztcSe/7mp5i1KCIZzaToxtO+a1XIA85AKUOvempAst2SaUft7BcNL0PW0a0flW2QpaGCcMedJzgA==",
        "ARC-Message-Signature": "i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com;\n s=arcselector9901;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1;\n bh=sk0CbhEGjUM3UWyu7es8gTT0QMWDzYHTbLTZBNN6d4Y=;\n b=U/HIKYgD0xVjIitzyYFvkaVDojMu5AcUU13eO9Kb0V8B2xxcIXNSyKypE5ZOkcEI4TfW85trBTR7GnGCFtH3YFxQ6L2eBa0cY02dW9a3KoC5jC3gb0/czieJUsoinM3RJ6HQxbpa4U5V4S2nFVhBFe1/ycv6sMlbMapImOIb7n927wRviMjIwWsUqtr4fA5NuWjDvsRgAZ+fy28rdxm9Jw8q5XVsl2JUufe6RYfE1AuWVzPh1dYLP6RgbdM31GBVIQ8KBe7JbV+Wi+CQCrVsX0iqDtXJPl08NhiW6kdJ1x0yzpKyZw6ahR3SZ9p5LdZrPD+BVw9cE1Vcb9KRICkCfQ==",
        "ARC-Authentication-Results": "i=1; mx.microsoft.com 1; spf=fail (sender ip is\n 178.72.21.4) smtp.rcpttodomain=dpdk.org smtp.mailfrom=napatech.com;\n dmarc=fail (p=none sp=none pct=100) action=none header.from=napatech.com;\n dkim=none (message not signed); arc=none",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=napatech.com;\n s=selector1;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;\n bh=sk0CbhEGjUM3UWyu7es8gTT0QMWDzYHTbLTZBNN6d4Y=;\n b=dR80S/yYrMKMO7hgKHoagcNu+j8ef/R0wvk7IjkTW4ayUsR8e6sCJjcMyqmFUCXpbJIHet7v6BRwxwwPiqfYsXXbqvbAqHcmfTghkqwGrUYD7Az5gdf73yrfhu7jeb3Eo2qNAhFJioeZSPKiQlzFFrc9DXz5gcSsd4vjmiR2Vpg=",
        "X-MS-Exchange-Authentication-Results": "spf=fail (sender IP is 178.72.21.4)\n smtp.mailfrom=napatech.com; dkim=none (message not signed)\n header.d=none;dmarc=fail action=none header.from=napatech.com;",
        "Received-SPF": "Fail (protection.outlook.com: domain of napatech.com does not\n designate 178.72.21.4 as permitted sender) receiver=protection.outlook.com;\n client-ip=178.72.21.4; helo=localhost.localdomain.com;",
        "From": "Mykola Kostenok <mko-plv@napatech.com>",
        "To": "dev@dpdk.org,\n\tmko-plv@napatech.com",
        "Cc": "ckm@napatech.com",
        "Subject": "[PATCH v6 8/8] net/ntnic: adds socket connection to PMD",
        "Date": "Mon, 21 Aug 2023 15:54:26 +0200",
        "Message-Id": "<20230821135426.1478754-8-mko-plv@napatech.com>",
        "X-Mailer": "git-send-email 2.39.3",
        "In-Reply-To": "<20230821135426.1478754-1-mko-plv@napatech.com>",
        "References": "<20230816132552.2483752-1-mko-plv@napatech.com>\n <20230821135426.1478754-1-mko-plv@napatech.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-EOPAttributedMessage": "0",
        "X-MS-PublicTrafficType": "Email",
        "X-MS-TrafficTypeDiagnostic": "DU2PEPF0001E9C5:EE_|AM0P190MB0674:EE_",
        "Content-Type": "text/plain",
        "X-MS-Office365-Filtering-Correlation-Id": "b1f2704a-cd42-445f-d3b9-08dba24e24e9",
        "X-MS-Exchange-SenderADCheck": "1",
        "X-MS-Exchange-AntiSpam-Relay": "0",
        "X-Microsoft-Antispam": "BCL:0;",
        "X-Microsoft-Antispam-Message-Info": "\n 1e/2BBsazCcPt7v79oO+U+usu/wT9gUvmfBC0QSNpCGorZMCnf0u4yQOweiBYWygqMf05GwcLrTQB4yEhk4WHXGKa3WYdjZfUdzxJU5fDxjLkN9966qKN1oBc1ZxZm1U89Wbs10mXoqnamkwdbZrPcBqQVKdZiz92VxCUqFcfhGKjGg+7zo5tytkVKidso4h51UQbzgx6JCJr9Yqic+/cpHWzQ0xFOA9HFH87wpBloxlGysVOaS1/lSShYEpgrVwsEk8b2ZdwMIjoM4sa88EdiDQ8JPDs6yiLcWzDVoUMm7/vtFbi7BBjxUcR66Q1MHZo0OkSQhqwc61EYZK/ZPlznuiKlxmxbFQD3+bsJTCxPvWfMf2VjC5HcVW762MeR4E/hcfdVXRyhDh/Tm63wsfaVhLotOylb4Kgayw83QlF5TwsXhUVGfNyRgE7N/+O565ZEIZWOiSIgxVSyTEzrOLGUcKcZ0ffmmYTGJKt7CSIpeRz0wdwHuOF22BX/mS3ALYEjAMnwEC3Hjprpf2B4/XyIXmmN6Io/u4F5Xk5pzxXtLulod4vAGYU+OgKJNm362ovMwZi5DAw3PRMFyN6wEknL+gQtwpc/Y0iox+6Em/lTk2qoTApsoUuyGoaPJwZmOXPyv6wAVyBEPK3PtHiyjro+4QLV0dUd8FXJAJXifPrLw28vcTgExX5YwZE+orqjQ4RSI5ZOkOy8QVsU7Tk/ZlAkcFp+xiw6W6Km/1+Ir7MSyDPOloGkaUMIR512CCnZEvh/mttoS6hkJZYMZt1Dp06g==",
        "X-Forefront-Antispam-Report": "CIP:178.72.21.4; CTRY:DK; LANG:en; SCL:1; SRV:;\n IPV:NLI; SFV:NSPM; H:localhost.localdomain.com; PTR:InfoDomainNonexistent;\n CAT:NONE;\n SFS:(13230031)(6069001)(346002)(39840400004)(376002)(136003)(396003)(82310400011)(451199024)(186009)(1800799009)(36840700001)(46966006)(118246002)(2906002)(30864003)(40480700001)(6506007)(6486002)(83380400001)(5660300002)(336012)(26005)(34020700004)(86362001)(36860700001)(47076005)(8676002)(2616005)(8936002)(956004)(107886003)(4326008)(70206006)(316002)(6512007)(70586007)(36736006)(9316004)(478600001)(356005)(81166007)(6666004)(36756003)(41300700001)(1076003)(36900700001)(559001)(579004);\n DIR:OUT; SFP:1101;",
        "X-MS-Exchange-AntiSpam-ExternalHop-MessageData-ChunkCount": "1",
        "X-MS-Exchange-AntiSpam-ExternalHop-MessageData-0": "\n eybKw/0oFFkiLieOX7oP1GYcw6L+45RWudxFt0AbFHvoWgEJLRLGN4VqpN/yyM+u0VD5DrQ4zDnaAWL/Rqt8yRZv4sAfQIW5XcxRoRUJUCqMSt1VLZQxU0iykFUq21SbVSuYNhXtE5TQc4CyCVml0nORqyzllH2/2fgvkafFTfHuS5el2pdkyWJb00EAo/Du1Kiz+gRkj2ssNbHhyUBmqIcKGPulSoo/Nk64H3nsHLcKeqfJA1OAyOlczFKIEq9qWp2UqfRoC1iZkBYt9xKPixf2T83WnrgPjKO9cIl7k7SVQ/jNLyyZH35T9EOpv4wgOvk5kSqoQVcWDA4DUS4A+CeJRgpBfpo+NoOExqPrYZAYaZpAFaMXwVEwwbqz2SccXs+PB8laiHaA327QDrRmYEi2lCnWgMGwYaTx0cNvLnhtqkHAUBmxLM0uSmv5D7we6ENPUPGoJCAJ4GJWWlAAd8FAt2KK+XXx4jXHeWw4T9+1/cUfSQHoA0vxe5SuLOW/lDWZlQOQZejjIW+oOPBpUYl7uECt7MhlIfsdSf0IFT2r5HTGVEdydkvEDEgH/4fbrfagAaZ8/oL/Rt9CCpBNW7h1QH5nhIljbqHdZWMtxzYWP1tCS38BCRZshseMEGfFHd+Vee6ATi4e5gsUxgDSXTaR8evWl2ZW9VTmcp7j/ohfyw9InOz6hFJiKFEO2M55ys95doKUw8C3r+WthNAyGcPv5qLydmAlb717y1hu3JwV+l+XBjlWvelI9f7R8Dql+jvno1Kb/aSZVE7+kbD/X3sWvbh++rKE7myo3LFYI3w=",
        "X-OriginatorOrg": "napatech.com",
        "X-MS-Exchange-CrossTenant-OriginalArrivalTime": "21 Aug 2023 13:54:31.1791 (UTC)",
        "X-MS-Exchange-CrossTenant-Network-Message-Id": "\n b1f2704a-cd42-445f-d3b9-08dba24e24e9",
        "X-MS-Exchange-CrossTenant-Id": "c4540d0b-728a-4233-9da5-9ea30c7ec3ed",
        "X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp": "\n TenantId=c4540d0b-728a-4233-9da5-9ea30c7ec3ed; Ip=[178.72.21.4];\n Helo=[localhost.localdomain.com]",
        "X-MS-Exchange-CrossTenant-AuthSource": "\n DU2PEPF0001E9C5.eurprd03.prod.outlook.com",
        "X-MS-Exchange-CrossTenant-AuthAs": "Anonymous",
        "X-MS-Exchange-CrossTenant-FromEntityHeader": "HybridOnPrem",
        "X-MS-Exchange-Transport-CrossTenantHeadersStamped": "AM0P190MB0674",
        "X-BESS-ID": "1692626077-311072-12495-15881-1",
        "X-BESS-VER": "2019.1_20230817.1828",
        "X-BESS-Apparent-Source-IP": "104.47.17.107",
        "X-BESS-Parts": "H4sIAAAAAAACA4uuVkqtKFGyUioBkjpK+cVKVoZmxoaGZkB2BlA4NcnUKDk5NT\n HFKMnc0jTF0jLNwDI1xdwsNcnYwDI5yUSpNhYAgWLrJEMAAAA=",
        "X-BESS-Outbound-Spam-Score": "0.50",
        "X-BESS-Outbound-Spam-Report": "Code version 3.2,\n rules version 3.2.2.250292 [from\n cloudscan13-215.eu-central-1a.ess.aws.cudaops.com]\n Rule breakdown below\n pts rule name              description\n ---- ---------------------- --------------------------------\n 0.50 BSF_RULE7568M          META: Custom Rule 7568M\n 0.00 BSF_BESS_OUTBOUND      META: BESS Outbound",
        "X-BESS-Outbound-Spam-Status": "SCORE=0.50 using account:ESS113687 scores of\n KILL_LEVEL=7.0 tests=BSF_RULE7568M, BSF_BESS_OUTBOUND",
        "X-BESS-BRTS-Status": "1",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org"
    },
    "content": "From: Christian Koue Muf <ckm@napatech.com>\n\nThe socket connection is used by Napatech's tools for monitoring\nand rte_flow programming from other processes.\n\nSigned-off-by: Christian Koue Muf <ckm@napatech.com>\nReviewed-by: Mykola Kostenok <mko-plv@napatech.com>\n---\nv2:\n* Fixed WARNING:TYPO_SPELLING\nv4:\n* Fixed Alpine build\n---\n drivers/net/ntnic/meson.build                 |   24 +\n .../ntconnect/include/ntconn_mod_helper.h     |   97 ++\n .../net/ntnic/ntconnect/include/ntconnect.h   |   96 ++\n .../ntnic/ntconnect/include/ntconnect_api.h   |   87 ++\n .../ntconnect/include/ntconnect_api_adapter.h |  221 +++\n .../ntconnect/include/ntconnect_api_flow.h    |  168 +++\n .../ntconnect/include/ntconnect_api_meter.h   |   89 ++\n .../include/ntconnect_api_statistic.h         |  173 +++\n .../ntconnect/include/ntconnect_api_test.h    |   18 +\n drivers/net/ntnic/ntconnect/ntconn_server.c   |   97 ++\n drivers/net/ntnic/ntconnect/ntconnect.c       |  641 ++++++++\n .../ntnic/ntconnect_modules/ntconn_adapter.c  |  775 ++++++++++\n .../net/ntnic/ntconnect_modules/ntconn_flow.c | 1310 +++++++++++++++++\n .../ntnic/ntconnect_modules/ntconn_meter.c    |  517 +++++++\n .../ntnic/ntconnect_modules/ntconn_modules.h  |   19 +\n .../net/ntnic/ntconnect_modules/ntconn_stat.c |  876 +++++++++++\n .../net/ntnic/ntconnect_modules/ntconn_test.c |  146 ++\n 17 files changed, 5354 insertions(+)\n create mode 100644 drivers/net/ntnic/ntconnect/include/ntconn_mod_helper.h\n create mode 100644 drivers/net/ntnic/ntconnect/include/ntconnect.h\n create mode 100644 drivers/net/ntnic/ntconnect/include/ntconnect_api.h\n create mode 100644 drivers/net/ntnic/ntconnect/include/ntconnect_api_adapter.h\n create mode 100644 drivers/net/ntnic/ntconnect/include/ntconnect_api_flow.h\n create mode 100644 drivers/net/ntnic/ntconnect/include/ntconnect_api_meter.h\n create mode 100644 drivers/net/ntnic/ntconnect/include/ntconnect_api_statistic.h\n create mode 100644 drivers/net/ntnic/ntconnect/include/ntconnect_api_test.h\n create mode 100644 drivers/net/ntnic/ntconnect/ntconn_server.c\n create mode 100644 drivers/net/ntnic/ntconnect/ntconnect.c\n create mode 100644 drivers/net/ntnic/ntconnect_modules/ntconn_adapter.c\n create mode 100644 drivers/net/ntnic/ntconnect_modules/ntconn_flow.c\n create mode 100644 drivers/net/ntnic/ntconnect_modules/ntconn_meter.c\n create mode 100644 drivers/net/ntnic/ntconnect_modules/ntconn_modules.h\n create mode 100644 drivers/net/ntnic/ntconnect_modules/ntconn_stat.c\n create mode 100644 drivers/net/ntnic/ntconnect_modules/ntconn_test.c",
    "diff": "diff --git a/drivers/net/ntnic/meson.build b/drivers/net/ntnic/meson.build\nindex d3c3129a0c..bc34ae80b2 100644\n--- a/drivers/net/ntnic/meson.build\n+++ b/drivers/net/ntnic/meson.build\n@@ -13,6 +13,22 @@ if not dpdk_conf.has('RTE_ARCH_X86_64')\n     subdir_done()\n endif\n \n+# config object\n+ntnic_conf = configuration_data()\n+\n+# transfer options into config object\n+ntnic_conf.set('NT_TOOLS', true)\n+\n+# check option 'debug' (boolean flag derived from meson buildtype)\n+if get_option('debug')\n+    cflags += '-DDEBUG'\n+endif\n+\n+# check nt_tools build option\n+if ntnic_conf.get('NT_TOOLS')\n+    cflags += '-DNT_TOOLS'\n+endif\n+\n # includes\n includes = [\n     include_directories('.'),\n@@ -26,6 +42,7 @@ includes = [\n     include_directories('nthw/supported'),\n     include_directories('nthw/flow_api'),\n     include_directories('nthw/flow_filter'),\n+    include_directories('ntconnect/include'),\n     include_directories('sensors'),\n     include_directories('sensors/avr_sensors'),\n     include_directories('sensors/board_sensors'),\n@@ -47,6 +64,13 @@ sources = files(\n     'nim/nt_link_speed.c',\n     'nim/qsfp_sensors.c',\n     'nim/sfp_sensors.c',\n+    'ntconnect/ntconn_server.c',\n+    'ntconnect/ntconnect.c',\n+    'ntconnect_modules/ntconn_adapter.c',\n+    'ntconnect_modules/ntconn_flow.c',\n+    'ntconnect_modules/ntconn_meter.c',\n+    'ntconnect_modules/ntconn_stat.c',\n+    'ntconnect_modules/ntconn_test.c',\n     'nthw/core/nthw_clock_profiles.c',\n     'nthw/core/nthw_fpga.c',\n     'nthw/core/nthw_fpga_nt200a0x.c',\ndiff --git a/drivers/net/ntnic/ntconnect/include/ntconn_mod_helper.h b/drivers/net/ntnic/ntconnect/include/ntconn_mod_helper.h\nnew file mode 100644\nindex 0000000000..f55c4141cc\n--- /dev/null\n+++ b/drivers/net/ntnic/ntconnect/include/ntconn_mod_helper.h\n@@ -0,0 +1,97 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _NTCONN_MOD_HELPER_H_\n+#define _NTCONN_MOD_HELPER_H_\n+\n+#include \"ntconnect.h\"\n+\n+/*\n+ * Module parameter function call tree structures\n+ */\n+struct func_s {\n+\tconst char *param;\n+\tstruct func_s *sub_funcs;\n+\tint (*func)(void *hdl, int client_fd, struct ntconn_header_s *hdr,\n+\t\t    char **data, int *len);\n+};\n+\n+static inline int ntconn_error(char **data, int *len, const char *module,\n+\t\t\t       enum ntconn_err_e err_code)\n+{\n+\t*len = 0;\n+\tif (data) {\n+\t\tconst ntconn_err_t *ntcerr = get_ntconn_error(err_code);\n+\t\t*data = malloc(4 + strlen(module) + 1 +\n+\t\t\t       strlen(ntcerr->err_text) + 1);\n+\t\tif (*data) {\n+\t\t\tsprintf(*data, \"----%s:%s\", module, ntcerr->err_text);\n+\t\t\t*len = strlen(*data) + 1; /* add 0 - terminator */\n+\t\t\t*(uint32_t *)*data = (uint32_t)ntcerr->err_code;\n+\t\t}\n+\t}\n+\treturn REQUEST_ERR;\n+}\n+\n+static inline int ntconn_reply_status(char **data, int *len,\n+\t\t\t\t      enum ntconn_reply_code_e code)\n+{\n+\t*len = 0;\n+\tif (data) {\n+\t\t*data = malloc(sizeof(uint32_t));\n+\t\tif (*data) {\n+\t\t\t*len = sizeof(uint32_t);\n+\t\t\t*(uint32_t *)*data = (uint32_t)code;\n+\t\t}\n+\t}\n+\treturn REQUEST_OK;\n+}\n+\n+static inline int execute_function(const char *module, void *hdl, int client_id,\n+\t\t\t\t   struct ntconn_header_s *hdr, char *function,\n+\t\t\t\t   struct func_s *func_list, char **data,\n+\t\t\t\t   int *len, int recur_depth)\n+{\n+\tchar *tok = strtok(function, \",\");\n+\n+\tif (!tok) {\n+\t\tif (recur_depth == 0)\n+\t\t\treturn ntconn_error(data, len, module,\n+\t\t\t\t\t    NTCONN_ERR_CODE_MISSING_INVALID_PARAM);\n+\t\telse\n+\t\t\treturn ntconn_error(data, len, module,\n+\t\t\t\t\t    NTCONN_ERR_CODE_FUNCTION_PARAM_INCOMPLETE);\n+\t}\n+\n+\thdr->len -= strlen(tok) + 1;\n+\tchar *sub_funcs = function + strlen(tok) + 1;\n+\tint idx = 0;\n+\n+\twhile (func_list[idx].param) {\n+\t\tif (strcmp(func_list[idx].param, tok) == 0) {\n+\t\t\t/* hit */\n+\t\t\tif (func_list[idx].sub_funcs) {\n+\t\t\t\treturn execute_function(module, hdl, client_id,\n+\t\t\t\t\t\t\thdr, sub_funcs,\n+\t\t\t\t\t\t\tfunc_list[idx].sub_funcs,\n+\t\t\t\t\t\t\tdata, len,\n+\t\t\t\t\t\t\t++recur_depth);\n+\t\t\t} else if (func_list[idx].func) {\n+\t\t\t\t/* commands/parameters for function in text, zero-terminated */\n+\t\t\t\t*data = sub_funcs;\n+\t\t\t\treturn func_list[idx].func(hdl, client_id, hdr,\n+\t\t\t\t\t\t\t   data, len);\n+\t\t\t} else {\n+\t\t\t\treturn ntconn_error(data, len, module,\n+\t\t\t\t\t\t    NTCONN_ERR_CODE_INTERNAL_FUNC_ERROR);\n+\t\t\t}\n+\t\t}\n+\t\tidx++;\n+\t}\n+\t/* no hits */\n+\treturn ntconn_error(data, len, module,\n+\t\t\t    NTCONN_ERR_CODE_FUNC_PARAM_NOT_RECOGNIZED);\n+}\n+\n+#endif /* _NTCONN_MOD_HELPER_H_ */\ndiff --git a/drivers/net/ntnic/ntconnect/include/ntconnect.h b/drivers/net/ntnic/ntconnect/include/ntconnect.h\nnew file mode 100644\nindex 0000000000..9dcf2ec0a1\n--- /dev/null\n+++ b/drivers/net/ntnic/ntconnect/include/ntconnect.h\n@@ -0,0 +1,96 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _NTCONNECT_H_\n+#define _NTCONNECT_H_\n+\n+#include <rte_pci.h>\n+#include <sched.h>\n+#include <stdint.h>\n+\n+#include \"ntconnect_api.h\"\n+\n+#define REQUEST_OK 0\n+#define REQUEST_ERR -1\n+\n+typedef struct ntconn_api_s {\n+\t/*\n+\t * Name specifying this module. This name is used in the request string\n+\t */\n+\tconst char *module;\n+\t/*\n+\t * The version of this module integration\n+\t */\n+\tuint32_t version_major;\n+\tuint32_t version_minor;\n+\t/*\n+\t * The request function:\n+\t * hdl       : pointer to the context of this instance.\n+\t * client_id : identifying the client. To be able to manage client specific data/state.\n+\t * function  : pointer to the remainder of the request command (Layer 3). May be modified.\n+\t *             an example: <pci_id>;adapter;get,interface,port0,link_speed function will\n+\t *             then be 'get,interface,port0,link_speed'.\n+\t * hdr       : header for length of command string and length of binary blop.\n+\t *             The command string will start at \"*data\" and will have the length hdr->len.\n+\t *             The binary blob will start at \"&(*data)[hdr->len]\" and will have the length\n+\t *             hdr->blob_len.\n+\t * data      : pointer to the resulting data. Typically this will be allocated.\n+\t * len       : length of the data in the reply.\n+\t *\n+\t * return    : REQUEST_OK on success, REQUEST_ERR on failure. On failure, the data and len\n+\t *             can contain an describing error text\n+\t */\n+\tint (*request)(void *hdl, int client_id, struct ntconn_header_s *hdr,\n+\t\t       char *function, char **data, int *len);\n+\t/*\n+\t * After each request call, and when 'len' returns > 0, this function is called\n+\t * after sending reply to client.\n+\t * hdl       : pointer to the context of this instance.\n+\t * data      : the data pointer returned in the request function\n+\t */\n+\tvoid (*free_data)(void *hdl, char *data);\n+\t/*\n+\t * Clean up of client specific data allocations. Called when client disconnects from server\n+\t * hdl       : pointer to the context of this instance.\n+\t * client_id : identifying the client.\n+\t */\n+\tvoid (*client_cleanup)(void *hdl, int client_id);\n+} ntconnapi_t;\n+\n+/*\n+ * ntconn error\n+ */\n+typedef struct ntconn_err_s {\n+\tuint32_t err_code;\n+\tconst char *err_text;\n+} ntconn_err_t;\n+\n+const ntconn_err_t *get_ntconn_error(enum ntconn_err_e err_code);\n+\n+typedef struct ntconn_mod_s {\n+\tvoid *hdl;\n+\tstruct pci_id_s addr;\n+\tconst ntconnapi_t *op;\n+\n+\tpthread_mutex_t mutex;\n+\tstruct ntconn_mod_s *next;\n+} ntconn_mod_t;\n+\n+struct ntconn_server_s {\n+\tint serv_fd;\n+\tint running;\n+\t/*\n+\t * list of different pci_ids registered aka SmartNICs\n+\t */\n+\tstruct pci_id_s pci_id_list[MAX_PCI_IDS]; /* 0 - terminates */\n+\tcpu_set_t cpuset;\n+};\n+\n+int ntconn_server_register(void *server);\n+\n+int register_ntconn_mod(const struct rte_pci_addr *addr, void *hdl,\n+\t\t\tconst ntconnapi_t *op);\n+int ntconnect_init(const char *sockname, cpu_set_t cpuset);\n+\n+#endif /* _NTCONNECT_H_ */\ndiff --git a/drivers/net/ntnic/ntconnect/include/ntconnect_api.h b/drivers/net/ntnic/ntconnect/include/ntconnect_api.h\nnew file mode 100644\nindex 0000000000..14668bf2ee\n--- /dev/null\n+++ b/drivers/net/ntnic/ntconnect/include/ntconnect_api.h\n@@ -0,0 +1,87 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _NTCONNECT_API_H_\n+#define _NTCONNECT_API_H_\n+\n+#include \"stdint.h\"\n+/*\n+ * NtConnect API\n+ */\n+\n+#define NTCONNECT_SOCKET \"/var/run/ntconnect/ntconnect.sock\"\n+\n+enum ntconn_err_e {\n+\tNTCONN_ERR_CODE_NONE = 0U,\n+\tNTCONN_ERR_CODE_INTERNAL_ERROR,\n+\tNTCONN_ERR_CODE_INVALID_REQUEST,\n+\tNTCONN_ERR_CODE_INTERNAL_REPLY_ERROR,\n+\tNTCONN_ERR_CODE_NO_DATA,\n+\tNTCONN_ERR_CODE_NOT_YET_IMPLEMENTED,\n+\tNTCONN_ERR_CODE_MISSING_INVALID_PARAM,\n+\tNTCONN_ERR_CODE_FUNCTION_PARAM_INCOMPLETE,\n+\tNTCONN_ERR_CODE_INTERNAL_FUNC_ERROR,\n+\tNTCONN_ERR_CODE_FUNC_PARAM_NOT_RECOGNIZED,\n+};\n+\n+enum ntconn_reply_code_e {\n+\tNTCONN_ADAPTER_ERR_PORT_STATE_FAIL = 0U,\n+\tNTCONN_ADAPTER_ERR_WRONG_LINK_STATE,\n+\tNTCONN_ADAPTER_ERR_TX_POWER_FAIL,\n+};\n+\n+enum {\n+\tNTCONN_TAG_NONE,\n+\tNTCONN_TAG_REQUEST,\n+\tNTCONN_TAG_REPLY,\n+\tNTCONN_TAG_ERROR\n+};\n+\n+#define MESSAGE_BUFFER 256\n+#define MAX_ERR_MESSAGE_LENGTH 256\n+\n+struct reply_err_s {\n+\tenum ntconn_err_e err_code;\n+\tchar msg[MAX_ERR_MESSAGE_LENGTH];\n+};\n+\n+#define NTCMOD_HDR_LEN sizeof(struct ntconn_header_s)\n+struct ntconn_header_s {\n+\tuint16_t tag;\n+\tuint16_t len;\n+\tuint32_t blob_len;\n+};\n+\n+struct pci_id_s {\n+\tunion {\n+\t\tuint64_t pci_id;\n+\t\tstruct {\n+\t\t\tuint32_t domain;\n+\t\t\tuint8_t bus;\n+\t\t\tuint8_t devid;\n+\t\t\tuint8_t function;\n+\t\t\tuint8_t pad;\n+\t\t};\n+\t};\n+};\n+\n+#define VERSION_HI(version) ((unsigned int)((version) >> 32))\n+#define VERSION_LO(version) ((unsigned int)((version) & 0xffffffff))\n+\n+/*\n+ * Binary interface description for ntconnect module replies\n+ */\n+\n+/*\n+ * server get,nic_pci_ids\n+ */\n+#define MAX_PCI_IDS 16\n+#define NICS_PCI_ID_LEN 12\n+\n+struct ntc_nic_pci_ids_s {\n+\tchar nic_pci_id[MAX_PCI_IDS][NICS_PCI_ID_LEN + 1];\n+\tint num_nics;\n+};\n+\n+#endif /* _NTCONNECT_API_H_ */\ndiff --git a/drivers/net/ntnic/ntconnect/include/ntconnect_api_adapter.h b/drivers/net/ntnic/ntconnect/include/ntconnect_api_adapter.h\nnew file mode 100644\nindex 0000000000..affe905027\n--- /dev/null\n+++ b/drivers/net/ntnic/ntconnect/include/ntconnect_api_adapter.h\n@@ -0,0 +1,221 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _NTCONNECT_API_ADAPTER_H_\n+#define _NTCONNECT_API_ADAPTER_H_\n+\n+/*\n+ * adapter get,interfaces\n+ */\n+enum port_speed {\n+\tPORT_LINK_SPEED_UNKNOWN,\n+\tPORT_LINK_SPEED_NONE_REPORTED,\n+\tPORT_LINK_SPEED_10M,\n+\tPORT_LINK_SPEED_100M,\n+\tPORT_LINK_SPEED_1G,\n+\tPORT_LINK_SPEED_10G,\n+\tPORT_LINK_SPEED_25G,\n+\tPORT_LINK_SPEED_40G,\n+\tPORT_LINK_SPEED_50G,\n+\tPORT_LINK_SPEED_100G,\n+};\n+\n+enum port_states {\n+\tPORT_STATE_DISABLED,\n+\tPORT_STATE_NIM_PRESENT,\n+\tPORT_STATE_NIM_ABSENT,\n+\tPORT_STATE_VIRTUAL_UNATTACHED,\n+\tPORT_STATE_VIRTUAL_SPLIT,\n+\tPORT_STATE_VIRTUAL_PACKED,\n+\tPORT_STATE_VIRTUAL_RELAY,\n+};\n+\n+enum port_link { PORT_LINK_UNKNOWN, PORT_LINK_UP, PORT_LINK_DOWN };\n+\n+enum port_type {\n+\tPORT_TYPE_PHY_NORMAL, /* Normal phy port (no LAG) */\n+\t/* Link aggregated phy port in active/active LAG configuration */\n+\tPORT_TYPE_PHY_LAG_ACTIVE_ACTIVE,\n+\tPORT_TYPE_PHY_LAG_PRIMARY, /* Primary phy port in active/backup LAG configuration */\n+\tPORT_TYPE_PHY_LAG_BACKUP, /* Backup phy port in active/backup LAG configuration */\n+\tPORT_TYPE_VIRT,\n+\tPORT_TYPE_LAST\n+};\n+\n+enum nim_identifier_e {\n+\tNIM_UNKNOWN = 0x00, /* Nim type is unknown */\n+\tNIM_GBIC = 0x01, /* Nim type = GBIC */\n+\tNIM_FIXED = 0x02, /* Nim type = FIXED */\n+\tNIM_SFP_SFP_PLUS = 0x03, /* Nim type = SFP/SFP+ */\n+\tNIM_300_PIN_XBI = 0x04, /* Nim type = 300 pin XBI */\n+\tNIM_XEN_PAK = 0x05, /* Nim type = XEN-PAK */\n+\tNIM_XFP = 0x06, /* Nim type = XFP */\n+\tNIM_XFF = 0x07, /* Nim type = XFF */\n+\tNIM_XFP_E = 0x08, /* Nim type = XFP-E */\n+\tNIM_XPAK = 0x09, /* Nim type = XPAK */\n+\tNIM_X2 = 0x0A, /* Nim type = X2 */\n+\tNIM_DWDM = 0x0B, /* Nim type = DWDM */\n+\tNIM_QSFP = 0x0C, /* Nim type = QSFP */\n+\tNIM_QSFP_PLUS = 0x0D, /* Nim type = QSFP+ */\n+\tNIM_QSFP28 = 0x11, /* Nim type = QSFP28 */\n+\tNIM_CFP4 = 0x12, /* Nim type = CFP4 */\n+};\n+\n+/*\n+ * Port types\n+ */\n+enum port_type_e {\n+\tPORT_TYPE_NOT_AVAILABLE =\n+\t\t0, /* The NIM/port type is not available (unknown) */\n+\tPORT_TYPE_NOT_RECOGNISED, /* The NIM/port type not recognized */\n+\tPORT_TYPE_RJ45, /* RJ45 type */\n+\tPORT_TYPE_SFP_NOT_PRESENT, /* SFP type but slot is empty */\n+\tPORT_TYPE_SFP_SX, /* SFP SX */\n+\tPORT_TYPE_SFP_SX_DD, /* SFP SX digital diagnostic */\n+\tPORT_TYPE_SFP_LX, /* SFP LX */\n+\tPORT_TYPE_SFP_LX_DD, /* SFP LX digital diagnostic */\n+\tPORT_TYPE_SFP_ZX, /* SFP ZX */\n+\tPORT_TYPE_SFP_ZX_DD, /* SFP ZX digital diagnostic */\n+\tPORT_TYPE_SFP_CU, /* SFP copper */\n+\tPORT_TYPE_SFP_CU_DD, /* SFP copper digital diagnostic */\n+\tPORT_TYPE_SFP_NOT_RECOGNISED, /* SFP unknown */\n+\tPORT_TYPE_XFP, /* XFP */\n+\tPORT_TYPE_XPAK, /* XPAK */\n+\tPORT_TYPE_SFP_CU_TRI_SPEED, /* SFP copper tri-speed */\n+\tPORT_TYPE_SFP_CU_TRI_SPEED_DD, /* SFP copper tri-speed digital diagnostic */\n+\tPORT_TYPE_SFP_PLUS, /* SFP+ type */\n+\tPORT_TYPE_SFP_PLUS_NOT_PRESENT, /* SFP+ type but slot is empty */\n+\tPORT_TYPE_XFP_NOT_PRESENT, /* XFP type but slot is empty */\n+\tPORT_TYPE_QSFP_PLUS_NOT_PRESENT, /* QSFP type but slot is empty */\n+\tPORT_TYPE_QSFP_PLUS, /* QSFP type */\n+\tPORT_TYPE_SFP_PLUS_PASSIVE_DAC, /* SFP+ Passive DAC */\n+\tPORT_TYPE_SFP_PLUS_ACTIVE_DAC, /* SFP+ Active DAC */\n+\tPORT_TYPE_CFP4, /* CFP4 type */\n+\tPORT_TYPE_CFP4_LR4 = PORT_TYPE_CFP4, /* CFP4 100G, LR4 type */\n+\tPORT_TYPE_CFP4_NOT_PRESENT, /* CFP4 type but slot is empty */\n+\tPORT_TYPE_INITIALIZE, /* The port type is not fully established yet */\n+\tPORT_TYPE_NIM_NOT_PRESENT, /* Generic \"Not present\" */\n+\tPORT_TYPE_HCB, /* Test mode: Host Compliance Board */\n+\tPORT_TYPE_NOT_SUPPORTED, /* The NIM type is not supported in this context */\n+\tPORT_TYPE_SFP_PLUS_DUAL_RATE, /* SFP+ supports 1G/10G */\n+\tPORT_TYPE_CFP4_SR4, /* CFP4 100G, SR4 type */\n+\tPORT_TYPE_QSFP28_NOT_PRESENT, /* QSFP28 type but slot is empty */\n+\tPORT_TYPE_QSFP28, /* QSFP28 type */\n+\tPORT_TYPE_QSFP28_SR4, /* QSFP28-SR4 type */\n+\tPORT_TYPE_QSFP28_LR4, /* QSFP28-LR4 type */\n+\t/* Deprecated. The port type should not mention speed eg 4x10 or 1x40 */\n+\tPORT_TYPE_QSFP_PLUS_4X10,\n+\t/* Deprecated. The port type should not mention speed eg 4x10 or 1x40 */\n+\tPORT_TYPE_QSFP_PASSIVE_DAC_4X10,\n+\tPORT_TYPE_QSFP_PASSIVE_DAC =\n+\t\tPORT_TYPE_QSFP_PASSIVE_DAC_4X10, /* QSFP passive DAC type */\n+\t/* Deprecated. The port type should not mention speed eg 4x10 or 1x40 */\n+\tPORT_TYPE_QSFP_ACTIVE_DAC_4X10,\n+\tPORT_TYPE_QSFP_ACTIVE_DAC =\n+\t\tPORT_TYPE_QSFP_ACTIVE_DAC_4X10, /* QSFP active DAC type */\n+\tPORT_TYPE_SFP_28, /* SFP28 type */\n+\tPORT_TYPE_SFP_28_SR, /* SFP28-SR type */\n+\tPORT_TYPE_SFP_28_LR, /* SFP28-LR type */\n+\tPORT_TYPE_SFP_28_CR_CA_L, /* SFP28-CR-CA-L type */\n+\tPORT_TYPE_SFP_28_CR_CA_S, /* SFP28-CR-CA-S type */\n+\tPORT_TYPE_SFP_28_CR_CA_N, /* SFP28-CR-CA-N type */\n+\tPORT_TYPE_QSFP28_CR_CA_L, /* QSFP28-CR-CA-L type */\n+\tPORT_TYPE_QSFP28_CR_CA_S, /* QSFP28-CR-CA-S type */\n+\tPORT_TYPE_QSFP28_CR_CA_N, /* QSFP28-CR-CA-N type */\n+\tPORT_TYPE_SFP_28_SR_DR, /* SFP28-SR-DR type */\n+\tPORT_TYPE_SFP_28_LR_DR, /* SFP28-LR-DR type */\n+\tPORT_TYPE_SFP_FX, /* SFP FX */\n+\tPORT_TYPE_SFP_PLUS_CU, /* SFP+ CU type */\n+\tPORT_TYPE_QSFP28_FR, /* QSFP28-FR type. Uses PAM4 modulation on one lane only */\n+\tPORT_TYPE_QSFP28_DR, /* QSFP28-DR type. Uses PAM4 modulation on one lane only */\n+\tPORT_TYPE_QSFP28_LR, /* QSFP28-LR type. Uses PAM4 modulation on one lane only */\n+};\n+\n+struct mac_addr_s {\n+\tuint8_t addr_b[6];\n+};\n+\n+struct nim_link_length_s {\n+\t/* NIM link length (in meters) supported SM (9um). A value of 0xFFFF indicates that the\n+\t * length is >= 65535 m\n+\t */\n+\tuint16_t sm;\n+\tuint16_t ebw; /* NIM link length (in meters) supported EBW (50um) */\n+\tuint16_t mm50; /* NIM link length (in meters) supported MM (50um) */\n+\tuint16_t mm62; /* NIM link length (in meters) supported MM (62.5um) */\n+\tuint16_t copper; /* NIM link length (in meters) supported copper */\n+};\n+\n+struct nim_data_s {\n+\tuint8_t nim_id;\n+\tuint8_t port_type;\n+\tchar vendor_name[17];\n+\tchar prod_no[17];\n+\tchar serial_no[17];\n+\tchar date[9];\n+\tchar rev[5];\n+\tuint8_t pwr_level_req;\n+\tuint8_t pwr_level_cur;\n+\tstruct nim_link_length_s link_length;\n+};\n+\n+struct sensor {\n+\tuint8_t sign;\n+\tuint8_t type;\n+\tuint32_t current_value;\n+\tuint32_t min_value;\n+\tuint32_t max_value;\n+\tchar name[50];\n+};\n+\n+struct ntc_sensors_s {\n+\tuint16_t adapter_sensors_cnt;\n+\tuint16_t ports_cnt;\n+\tuint16_t nim_sensors_cnt[8];\n+\tchar adapter_name[24];\n+};\n+\n+#define MAX_RSS_QUEUES 128\n+\n+enum queue_dir_e { QUEUE_INPUT, QUEUE_OUTPUT };\n+\n+struct queue_s {\n+\tenum queue_dir_e dir;\n+\tint idx;\n+};\n+\n+struct ntc_interface_s {\n+\tuint8_t port_id;\n+\tenum port_type type;\n+\tenum port_link link;\n+\tenum port_states port_state;\n+\tenum port_speed port_speed;\n+\tstruct pci_id_s pci_id;\n+\tstruct mac_addr_s mac;\n+\tstruct nim_data_s nim_data;\n+\tuint16_t mtu;\n+\t/* attached queues */\n+\tstruct {\n+\t\tstruct queue_s queue[MAX_RSS_QUEUES];\n+\t\tint num_queues;\n+\t};\n+};\n+\n+/*\n+ * adapter get,interfaces\n+ */\n+struct ntc_interfaces_s {\n+\tint final_list;\n+\tuint8_t nb_ports;\n+\tstruct ntc_interface_s intf[];\n+};\n+\n+/*\n+ * adapter get,info\n+ */\n+struct ntc_adap_get_info_s {\n+\tchar *fw_version[32];\n+};\n+\n+#endif /* _NTCONNECT_API_ADAPTER_H_ */\ndiff --git a/drivers/net/ntnic/ntconnect/include/ntconnect_api_flow.h b/drivers/net/ntnic/ntconnect/include/ntconnect_api_flow.h\nnew file mode 100644\nindex 0000000000..4091d61d7d\n--- /dev/null\n+++ b/drivers/net/ntnic/ntconnect/include/ntconnect_api_flow.h\n@@ -0,0 +1,168 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _NTCONNECT_API_FILTER_H_\n+#define _NTCONNECT_API_FILTER_H_\n+\n+#include \"stream_binary_flow_api.h\"\n+\n+/*\n+ * Create structures allocating the space to carry through ntconnect interface\n+ */\n+#define MAX_FLOW_STREAM_ELEM 16\n+#define MAX_FLOW_STREAM_QUERY_DATA 1024\n+#define MAX_FLOW_STREAM_ERROR_MSG 128\n+#define MAX_FLOW_STREAM_VXLAN_TUN_ELEM 8\n+#define MAX_FLOW_STREAM_COUNT_ACTIONS 4\n+\n+#define MAX_PATH_LEN 128\n+\n+enum ntconn_flow_err_e {\n+\tNTCONN_FLOW_ERR_NONE = 0,\n+\tNTCONN_FLOW_ERR_INTERNAL_ERROR = 0x100,\n+\tNTCONN_FLOW_ERR_PORT_IS_NOT_INITIALIZED,\n+\tNTCONN_FLOW_ERR_INVALID_PORT,\n+\tNTCONN_FLOW_ERR_UNEXPECTED_VIRTIO_PATH,\n+\tNTCONN_FLOW_ERR_UNSUPPORTED_ADAPTER,\n+\tNTCONN_FLOW_ERR_TO_MANY_FLOWS,\n+\tNTCONN_FLOW_ERR_NOT_YET_IMPLEMENTED,\n+\tNTCONN_FLOW_ERR_NO_VF_QUEUES,\n+};\n+\n+struct flow_elem_types_s {\n+\tint valid;\n+\tunion {\n+\t\tint start_addr;\n+\t\tstruct flow_elem_eth eth;\n+\t\tstruct flow_elem_vlan vlan[2];\n+\t\tstruct flow_elem_ipv4 ipv4;\n+\t\tstruct flow_elem_ipv6 ipv6;\n+\t\tstruct flow_elem_sctp sctp;\n+\t\tstruct flow_elem_tcp tcp;\n+\t\tstruct flow_elem_udp udp;\n+\t\tstruct flow_elem_icmp icmp;\n+\t\tstruct flow_elem_vxlan vxlan;\n+\t\tstruct flow_elem_port_id port_id;\n+\t\tstruct flow_elem_tag tag;\n+\t} u;\n+};\n+\n+struct flow_elem_cpy {\n+\tenum flow_elem_type type; /* element type */\n+\tstruct flow_elem_types_s spec_cpy;\n+\tstruct flow_elem_types_s mask_cpy;\n+};\n+\n+struct flow_action_vxlan_encap_cpy {\n+\t/* Encapsulating vxlan tunnel definition */\n+\tstruct flow_elem_cpy vxlan_tunnel[MAX_FLOW_STREAM_VXLAN_TUN_ELEM];\n+};\n+\n+struct flow_action_rss_cpy {\n+\tstruct flow_action_rss rss;\n+\tuint16_t cpy_queue[FLOW_MAX_QUEUES];\n+};\n+\n+#define MAX_ACTION_ENCAP_DATA 512\n+struct flow_action_decap_cpy {\n+\tuint8_t data[MAX_ACTION_ENCAP_DATA];\n+\tsize_t size;\n+\tstruct flow_elem_cpy item_cpy\n+\t\t[RAW_ENCAP_DECAP_ELEMS_MAX]; /* Need room for end command */\n+\tint item_count;\n+};\n+\n+struct flow_action_encap_cpy {\n+\tuint8_t data[MAX_ACTION_ENCAP_DATA];\n+\tsize_t size;\n+\tstruct flow_elem_cpy item_cpy\n+\t\t[RAW_ENCAP_DECAP_ELEMS_MAX]; /* Need room for end command */\n+\tint item_count;\n+};\n+\n+struct flow_action_types_s {\n+\tint valid;\n+\tunion {\n+\t\tint start_addr;\n+\t\tstruct flow_action_rss_cpy rss;\n+\t\tstruct flow_action_push_vlan vlan;\n+\t\tstruct flow_action_set_vlan_vid vlan_vid;\n+\t\tstruct flow_action_vxlan_encap_cpy vxlan;\n+\t\tstruct flow_action_count count;\n+\t\tstruct flow_action_mark mark;\n+\t\tstruct flow_action_port_id port_id;\n+\t\tstruct flow_action_tag tag;\n+\t\tstruct flow_action_queue queue;\n+\t\tstruct flow_action_decap_cpy decap;\n+\t\tstruct flow_action_encap_cpy encap;\n+\t\tstruct flow_action_jump jump;\n+\t\tstruct flow_action_meter meter;\n+\t} u;\n+};\n+\n+struct flow_action_cpy {\n+\tenum flow_action_type type;\n+\tstruct flow_action_types_s conf_cpy;\n+};\n+\n+struct query_flow_ntconnect {\n+\tuint8_t port;\n+\tstruct flow_action_cpy action;\n+\tuint64_t flow;\n+};\n+\n+struct create_flow_ntconnect {\n+\tuint8_t port;\n+\tuint8_t vport;\n+\tstruct flow_attr attr;\n+\tstruct flow_elem_cpy elem[MAX_FLOW_STREAM_ELEM];\n+\tstruct flow_action_cpy action[MAX_FLOW_STREAM_ELEM];\n+};\n+\n+struct destroy_flow_ntconnect {\n+\tuint8_t port;\n+\tuint64_t flow;\n+};\n+\n+#define ERR_MSG_LEN 128LLU\n+\n+struct flow_setport_return {\n+\tstruct flow_queue_id_s queues[FLOW_MAX_QUEUES];\n+\tuint8_t num_queues;\n+};\n+\n+struct flow_error_return_s {\n+\tenum flow_error_e type;\n+\tchar err_msg[ERR_MSG_LEN];\n+\tint status;\n+};\n+\n+struct create_flow_return_s {\n+\tuint64_t flow;\n+};\n+\n+struct validate_flow_return_s {\n+\tint status;\n+};\n+\n+struct query_flow_return_s {\n+\tenum flow_error_e type;\n+\tchar err_msg[ERR_MSG_LEN];\n+\tint status;\n+\tuint32_t data_length;\n+\tuint8_t data[];\n+};\n+\n+struct flow_return_s {\n+\tenum flow_error_e type;\n+\tchar err_msg[ERR_MSG_LEN];\n+\tint status;\n+};\n+\n+struct flow_error_ntconn {\n+\tenum flow_error_e type;\n+\tchar message[ERR_MSG_LEN];\n+};\n+\n+#endif /* _NTCONNECT_API_FILTER_H_ */\ndiff --git a/drivers/net/ntnic/ntconnect/include/ntconnect_api_meter.h b/drivers/net/ntnic/ntconnect/include/ntconnect_api_meter.h\nnew file mode 100644\nindex 0000000000..901b0ccba1\n--- /dev/null\n+++ b/drivers/net/ntnic/ntconnect/include/ntconnect_api_meter.h\n@@ -0,0 +1,89 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _NTCONNECT_METER_FILTER_H_\n+#define _NTCONNECT_METER_FILTER_H_\n+\n+#define FLOW_COOKIE 0x12344321\n+\n+/*\n+ * Create structures allocating the space to carry through ntconnect interface\n+ */\n+\n+#define MAX_PATH_LEN 128\n+\n+enum ntconn_meter_err_e {\n+\tNTCONN_METER_ERR_NONE = 0,\n+\tNTCONN_METER_ERR_INTERNAL_ERROR = 0x100,\n+\tNTCONN_METER_ERR_INVALID_PORT,\n+\tNTCONN_METER_ERR_UNEXPECTED_VIRTIO_PATH,\n+\tNTCONN_METER_ERR_PROFILE_ID,\n+\tNTCONN_METER_ERR_POLICY_ID,\n+\tNTCONN_METER_ERR_METER_ID,\n+};\n+\n+enum ntconn_meter_command_e {\n+\tUNKNOWN_CMD,\n+\tADD_PROFILE,\n+\tDEL_PROFILE,\n+\tADD_POLICY,\n+\tDEL_POLICY,\n+\tCREATE_MTR,\n+\tDEL_MTR\n+};\n+\n+#define ERR_MSG_LEN 128LLU\n+\n+struct meter_error_return_s {\n+\tenum rte_mtr_error_type type;\n+\tint status;\n+\tchar err_msg[ERR_MSG_LEN];\n+};\n+\n+struct meter_setup_s {\n+\tuint8_t vport;\n+\tuint32_t id;\n+\tint shared;\n+\tunion {\n+\t\tstruct rte_mtr_meter_profile profile;\n+\t\tstruct {\n+\t\t\tstruct rte_mtr_meter_policy_params policy;\n+\t\t\tstruct rte_flow_action actions_green[2];\n+\t\t\tstruct rte_flow_action actions_yellow[2];\n+\t\t\tstruct rte_flow_action actions_red[2];\n+\t\t} p;\n+\t\tstruct rte_mtr_params mtr_params;\n+\t};\n+};\n+\n+struct meter_get_stat_s {\n+\tuint8_t vport;\n+\tuint32_t mtr_id;\n+\tint clear;\n+};\n+\n+struct meter_return_stat_s {\n+\tstruct rte_mtr_stats stats;\n+\tuint64_t stats_mask;\n+};\n+\n+struct meter_setup_ptr_s {\n+\tuint32_t id;\n+\tint shared;\n+\tunion {\n+\t\tstruct rte_mtr_meter_profile *profile;\n+\t\tstruct rte_mtr_meter_policy_params *policy;\n+\t\tstruct rte_mtr_params *mtr_params;\n+\t};\n+};\n+\n+struct meter_return_s {\n+\tint status;\n+};\n+\n+struct meter_capabilities_return_s {\n+\tstruct rte_mtr_capabilities cap;\n+};\n+\n+#endif /* _NTCONNECT_METER_FILTER_H_ */\ndiff --git a/drivers/net/ntnic/ntconnect/include/ntconnect_api_statistic.h b/drivers/net/ntnic/ntconnect/include/ntconnect_api_statistic.h\nnew file mode 100644\nindex 0000000000..1022bc2056\n--- /dev/null\n+++ b/drivers/net/ntnic/ntconnect/include/ntconnect_api_statistic.h\n@@ -0,0 +1,173 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _NTCONNECT_API_STATISTIC_H_\n+#define _NTCONNECT_API_STATISTIC_H_\n+\n+/*\n+ * Supported defined statistic records for Stat layout version 6 - defined in nthw_stat module\n+ */\n+#define NUM_STAT_RECORD_TYPE_COLOR \\\n+\t(sizeof(struct color_type_fields_s) / sizeof(uint64_t))\n+struct color_type_fields_s {\n+\tuint64_t pkts;\n+\tuint64_t octets;\n+\tuint64_t tcp_flgs;\n+};\n+\n+#define NUM_STAT_RECORD_TYPE_FLOWMATCHER \\\n+\t(sizeof(struct flowmatcher_type_fields_s) / sizeof(uint64_t))\n+struct flowmatcher_type_fields_s {\n+\t/* FLM 0.17 */\n+\tuint64_t current;\n+\tuint64_t learn_done;\n+\tuint64_t learn_ignore;\n+\tuint64_t learn_fail;\n+\tuint64_t unlearn_done;\n+\tuint64_t unlearn_ignore;\n+\tuint64_t auto_unlearn_done;\n+\tuint64_t auto_unlearn_ignore;\n+\tuint64_t auto_unlearn_fail;\n+\tuint64_t timeout_unlearn_done;\n+\tuint64_t rel_done;\n+\tuint64_t rel_ignore;\n+\tuint64_t prb_done;\n+\tuint64_t prb_ignore;\n+\t/* FLM 0.20 */\n+\tuint64_t sta_done;\n+\tuint64_t inf_done;\n+\tuint64_t inf_skip;\n+\tuint64_t pck_hit;\n+\tuint64_t pck_miss;\n+\tuint64_t pck_unh;\n+\tuint64_t pck_dis;\n+\tuint64_t csh_hit;\n+\tuint64_t csh_miss;\n+\tuint64_t csh_unh;\n+\tuint64_t cuc_start;\n+\tuint64_t cuc_move;\n+};\n+\n+#define NUM_STAT_RECORD_TYPE_QUEUE \\\n+\t(sizeof(struct queue_type_fields_s) / sizeof(uint64_t))\n+struct queue_type_fields_s {\n+\tuint64_t flush_pkts;\n+\tuint64_t drop_pkts;\n+\tuint64_t fwd_pkts;\n+\tuint64_t dbs_drop_pkts;\n+\tuint64_t flush_octets;\n+\tuint64_t drop_octets;\n+\tuint64_t fwd_octets;\n+\tuint64_t dbs_drop_octets;\n+};\n+\n+/*\n+ * Port stat counters for virtualization NICS with virtual ports support\n+ */\n+#define NUM_STAT_RECORD_TYPE_RX_PORT_VIRT \\\n+\t(sizeof(struct rtx_type_fields_virt_s) / sizeof(uint64_t))\n+/* same for Rx and Tx counters on Virt */\n+#define NUM_STAT_RECORD_TYPE_TX_PORT_VIRT NUM_STAT_RECORD_TYPE_RX_PORT_VIRT\n+struct rtx_type_fields_virt_s {\n+\tuint64_t octets;\n+\tuint64_t pkts;\n+\tuint64_t drop_events;\n+\tuint64_t qos_drop_octets;\n+\tuint64_t qos_drop_pkts;\n+};\n+\n+/*\n+ * Port RMON counters for Cap devices\n+ */\n+struct stat_rmon_s {\n+\t/* Sums that are calculated by software */\n+\tuint64_t drop_events;\n+\tuint64_t pkts;\n+\t/* Read from FPGA */\n+\tuint64_t octets;\n+\tuint64_t broadcast_pkts;\n+\tuint64_t multicast_pkts;\n+\tuint64_t unicast_pkts;\n+\tuint64_t pkts_alignment;\n+\tuint64_t pkts_code_violation;\n+\tuint64_t pkts_crc;\n+\tuint64_t undersize_pkts;\n+\tuint64_t oversize_pkts;\n+\tuint64_t fragments;\n+\tuint64_t jabbers_not_truncated;\n+\tuint64_t jabbers_truncated;\n+\tuint64_t pkts_64_octets;\n+\tuint64_t pkts_65_to_127_octets;\n+\tuint64_t pkts_128_to_255_octets;\n+\tuint64_t pkts_256_to_511_octets;\n+\tuint64_t pkts_512_to_1023_octets;\n+\tuint64_t pkts_1024_to_1518_octets;\n+\tuint64_t pkts_1519_to_2047_octets;\n+\tuint64_t pkts_2048_to_4095_octets;\n+\tuint64_t pkts_4096_to_8191_octets;\n+\tuint64_t pkts_8192_to_max_octets;\n+};\n+\n+#define NUM_STAT_RECORD_TYPE_RX_PORT_CAP \\\n+\t(sizeof(struct rx_type_fields_cap_s) / sizeof(uint64_t))\n+struct rx_type_fields_cap_s {\n+\tstruct stat_rmon_s rmon;\n+\tuint64_t mac_drop_events;\n+\tuint64_t pkts_lr;\n+\t/* Rx only port counters */\n+\tuint64_t duplicate;\n+\tuint64_t pkts_ip_chksum_error;\n+\tuint64_t pkts_udp_chksum_error;\n+\tuint64_t pkts_tcp_chksum_error;\n+\tuint64_t pkts_giant_undersize;\n+\tuint64_t pkts_baby_giant;\n+\tuint64_t pkts_not_isl_vlan_mpls;\n+\tuint64_t pkts_isl;\n+\tuint64_t pkts_vlan;\n+\tuint64_t pkts_isl_vlan;\n+\tuint64_t pkts_mpls;\n+\tuint64_t pkts_isl_mpls;\n+\tuint64_t pkts_vlan_mpls;\n+\tuint64_t pkts_isl_vlan_mpls;\n+\tuint64_t pkts_no_filter;\n+\tuint64_t pkts_dedup_drop;\n+\tuint64_t pkts_filter_drop;\n+\tuint64_t pkts_overflow;\n+\tuint64_t pkts_dbs_drop;\n+\tuint64_t octets_no_filter;\n+\tuint64_t octets_dedup_drop;\n+\tuint64_t octets_filter_drop;\n+\tuint64_t octets_overflow;\n+\tuint64_t octets_dbs_drop;\n+\tuint64_t ipft_first_hit;\n+\tuint64_t ipft_first_not_hit;\n+\tuint64_t ipft_mid_hit;\n+\tuint64_t ipft_mid_not_hit;\n+\tuint64_t ipft_last_hit;\n+\tuint64_t ipft_last_not_hit;\n+};\n+\n+#define NUM_STAT_RECORD_TYPE_TX_PORT_CAP \\\n+\t(sizeof(struct tx_type_fields_cap_s) / sizeof(uint64_t))\n+struct tx_type_fields_cap_s {\n+\tstruct stat_rmon_s rmon;\n+};\n+\n+/*\n+ * stat get,colors\n+ * stat get,queues\n+ * stat get,rx_counters\n+ * stat get,tx_counters\n+ */\n+#define STAT_INFO_ELEMENTS \\\n+\t(sizeof(struct ntc_stat_get_data_s) / sizeof(uint64_t))\n+\n+struct ntc_stat_get_data_s {\n+\tuint64_t nb_counters;\n+\tuint64_t timestamp;\n+\tuint64_t is_virt;\n+\tuint64_t data[];\n+};\n+\n+#endif /* _NTCONNECT_API_STATISTIC_H_ */\ndiff --git a/drivers/net/ntnic/ntconnect/include/ntconnect_api_test.h b/drivers/net/ntnic/ntconnect/include/ntconnect_api_test.h\nnew file mode 100644\nindex 0000000000..44cacbd931\n--- /dev/null\n+++ b/drivers/net/ntnic/ntconnect/include/ntconnect_api_test.h\n@@ -0,0 +1,18 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _NTCONNECT_TEST_FILTER_H_\n+#define _NTCONNECT_TEST_FILTER_H_\n+\n+/*\n+ * Create structures allocating the space to carry through ntconnect interface\n+ */\n+\n+struct test_s {\n+\tint number;\n+\tint status;\n+\tuint64_t test[];\n+};\n+\n+#endif /* _NTCONNECT_TEST_FILTER_H_ */\ndiff --git a/drivers/net/ntnic/ntconnect/ntconn_server.c b/drivers/net/ntnic/ntconnect/ntconn_server.c\nnew file mode 100644\nindex 0000000000..34a3c19955\n--- /dev/null\n+++ b/drivers/net/ntnic/ntconnect/ntconn_server.c\n@@ -0,0 +1,97 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include <stdlib.h>\n+#include <string.h>\n+\n+#include \"ntconnect.h\"\n+#include \"ntconn_mod_helper.h\"\n+#include \"nt_util.h\"\n+#include \"ntlog.h\"\n+\n+/*\n+ * Server module always registered on 0000:00:00.0\n+ */\n+#define this_module_name \"server\"\n+\n+#define NTCONNECT_SERVER_VERSION_MAJOR 0U\n+#define NTCONNECT_SERVER_VERSION_MINOR 1U\n+\n+static int func_get_nic_pci(void *hdl, int client_fd,\n+\t\t\t    struct ntconn_header_s *hdr, char **data, int *len);\n+static struct func_s funcs_get_level1[] = {\n+\t{ \"nic_pci_ids\", NULL, func_get_nic_pci },\n+\t{ NULL, NULL, NULL },\n+};\n+\n+/*\n+ * Entry level\n+ */\n+static struct func_s server_entry_funcs[] = {\n+\t{ \"get\", funcs_get_level1, NULL },\n+\t{ NULL, NULL, NULL },\n+};\n+\n+static int func_get_nic_pci(void *hdl, int client_fd _unused,\n+\t\t\t    struct ntconn_header_s *hdr _unused, char **data,\n+\t\t\t    int *len)\n+{\n+\tstruct ntconn_server_s *serv = (struct ntconn_server_s *)hdl;\n+\tstruct ntc_nic_pci_ids_s *npci =\n+\t\tcalloc(1, sizeof(struct ntc_nic_pci_ids_s));\n+\tif (!npci) {\n+\t\t*len = 0;\n+\t\tNT_LOG(ERR, NTCONNECT, \"memory allocation failed\");\n+\t\treturn REQUEST_ERR;\n+\t}\n+\tint i = 0;\n+\n+\twhile (i < MAX_PCI_IDS && serv->pci_id_list[i].pci_id) {\n+\t\tsprintf(npci->nic_pci_id[i], \"%04x:%02x:%02x.%x\",\n+\t\t\tserv->pci_id_list[i].domain & 0xffff,\n+\t\t\tserv->pci_id_list[i].bus, serv->pci_id_list[i].devid,\n+\t\t\tserv->pci_id_list[i].function);\n+\t\ti++;\n+\t}\n+\tnpci->num_nics = i;\n+\t*data = (char *)npci;\n+\t*len = sizeof(struct ntc_nic_pci_ids_s);\n+\n+\treturn REQUEST_OK;\n+}\n+\n+static int ntconn_server_request(void *hdl, int client_id,\n+\t\t\t\t struct ntconn_header_s *hdr, char *function,\n+\t\t\t\t char **data, int *len)\n+{\n+\treturn execute_function(this_module_name, hdl, client_id, hdr, function,\n+\t\t\t\tserver_entry_funcs, data, len, 0);\n+}\n+\n+static void ntconn_server_free_data(void *hdl _unused, char *data)\n+{\n+\tif (data) {\n+#ifdef DEBUG\n+\t\tNT_LOG(DBG, NTCONNECT, \"server free data\\n\");\n+#endif\n+\t\tfree(data);\n+\t}\n+}\n+\n+static const ntconnapi_t ntconn_server_op = { this_module_name,\n+\t\t\t\t\t      NTCONNECT_SERVER_VERSION_MAJOR,\n+\t\t\t\t\t      NTCONNECT_SERVER_VERSION_MINOR,\n+\t\t\t\t\t      ntconn_server_request,\n+\t\t\t\t\t      ntconn_server_free_data,\n+\t\t\t\t\t      NULL\n+\t\t\t\t\t    };\n+\n+int ntconn_server_register(void *server)\n+{\n+\tconst struct rte_pci_addr addr = {\n+\t\t.domain = 0, .bus = 0, .devid = 0, .function = 0\n+\t};\n+\n+\treturn register_ntconn_mod(&addr, server, &ntconn_server_op);\n+}\ndiff --git a/drivers/net/ntnic/ntconnect/ntconnect.c b/drivers/net/ntnic/ntconnect/ntconnect.c\nnew file mode 100644\nindex 0000000000..22ef7828c7\n--- /dev/null\n+++ b/drivers/net/ntnic/ntconnect/ntconnect.c\n@@ -0,0 +1,641 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include <assert.h>\n+#include <ctype.h>\n+#include <errno.h>\n+#include <libgen.h>\n+#include <pthread.h>\n+#include <stdint.h>\n+#include <stdio.h>\n+#include <stdlib.h>\n+#include <string.h>\n+#include <poll.h>\n+#include <sys/socket.h>\n+#include <sys/stat.h>\n+#include <sys/types.h>\n+#include <sys/un.h>\n+#include <unistd.h>\n+\n+#include \"nt_util.h\"\n+#include \"ntconnect.h\"\n+#include \"ntconnect_api.h\"\n+#include \"ntlog.h\"\n+\n+/* clang-format off */\n+ntconn_err_t ntconn_err[] = {\n+\t{NTCONN_ERR_CODE_NONE, \"Success\"},\n+\t{NTCONN_ERR_CODE_INTERNAL_ERROR, \"Internal error\"},\n+\t{NTCONN_ERR_CODE_INTERNAL_REPLY_ERROR, \"Internal error in reply from module\"},\n+\t{NTCONN_ERR_CODE_NO_DATA, \"No data found\"},\n+\t{NTCONN_ERR_CODE_INVALID_REQUEST, \"Invalid request\"},\n+\t{NTCONN_ERR_CODE_NOT_YET_IMPLEMENTED, \"Function not yet implemented\"},\n+\t{NTCONN_ERR_CODE_INTERNAL_FUNC_ERROR, \"Internal error in function call list\"},\n+\t{NTCONN_ERR_CODE_MISSING_INVALID_PARAM, \"Missing or invalid parameter\"},\n+\t{NTCONN_ERR_CODE_FUNCTION_PARAM_INCOMPLETE, \"Function parameter is incomplete\"},\n+\t{NTCONN_ERR_CODE_FUNC_PARAM_NOT_RECOGNIZED,\n+\t\t\"Function or parameter not recognized/supported\"},\n+\t{-1, NULL}\n+};\n+\n+/* clang-format on */\n+\n+static ntconn_mod_t *ntcmod_base;\n+static pthread_t tid;\n+static pthread_t ctid;\n+static struct ntconn_server_s ntconn_serv;\n+\n+const ntconn_err_t *get_ntconn_error(enum ntconn_err_e err_code)\n+{\n+\tint idx = 0;\n+\n+\twhile (ntconn_err[idx].err_code != (uint32_t)-1 &&\n+\t\t\tntconn_err[idx].err_code != err_code)\n+\t\tidx++;\n+\tif (ntconn_err[idx].err_code == (uint32_t)-1)\n+\t\tidx = 1;\n+\n+\treturn &ntconn_err[idx];\n+}\n+\n+int register_ntconn_mod(const struct rte_pci_addr *addr, void *hdl,\n+\t\t\tconst ntconnapi_t *op)\n+{\n+\t/* Verify and check module name is unique */\n+#ifdef DEBUG\n+\tNT_LOG(DBG, NTCONNECT,\n+\t       \"Registering pci: %04x:%02x:%02x.%x, module %s\\n\", addr->domain,\n+\t       addr->bus, addr->devid, addr->function, op->module);\n+#endif\n+\n+\tntconn_mod_t *ntcmod = (ntconn_mod_t *)malloc(sizeof(ntconn_mod_t));\n+\n+\tif (!ntcmod) {\n+\t\tNT_LOG(ERR, NTCONNECT, \"memory allocation failed\");\n+\t\treturn -1;\n+\t}\n+\tntcmod->addr.domain = addr->domain;\n+\tntcmod->addr.bus = addr->bus;\n+\tntcmod->addr.devid = addr->devid;\n+\tntcmod->addr.function = addr->function;\n+\tntcmod->addr.pad = 0;\n+\n+\tntcmod->hdl = hdl;\n+\tntcmod->op = op;\n+\tpthread_mutex_init(&ntcmod->mutex, NULL);\n+\n+\tntcmod->next = ntcmod_base;\n+\tntcmod_base = ntcmod;\n+\n+\tif (ntcmod->addr.pci_id) { /* Avoid server fake pci_id */\n+\t\tint i;\n+\n+\t\tfor (i = 0; i < MAX_PCI_IDS; i++) {\n+\t\t\tif (ntconn_serv.pci_id_list[i].pci_id == 0) {\n+\t\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t\t       \"insert at index %i PCI ID %\" PRIX64 \"\\n\", i,\n+\t\t\t\t       ntcmod->addr.pci_id);\n+\t\t\t\tntconn_serv.pci_id_list[i].pci_id =\n+\t\t\t\t\tntcmod->addr.pci_id;\n+\t\t\t\tbreak;\n+\t\t\t} else if (ntconn_serv.pci_id_list[i].pci_id ==\n+\t\t\t\t\tntcmod->addr.pci_id)\n+\t\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int unix_build_address(const char *path, struct sockaddr_un *addr)\n+{\n+\tif (addr == NULL || path == NULL)\n+\t\treturn -1;\n+\tmemset(addr, 0, sizeof(struct sockaddr_un));\n+\taddr->sun_family = AF_UNIX;\n+\tif (strlen(path) < sizeof(addr->sun_path)) {\n+\t\trte_strscpy(addr->sun_path, path, sizeof(addr->sun_path) - 1);\n+\t\treturn 0;\n+\t}\n+\treturn -1;\n+}\n+\n+#define STATUS_OK 0\n+#define STATUS_INTERNAL_ERROR -1\n+#define STATUS_TRYAGAIN -2\n+#define STATUS_INVALID_PARAMETER -3\n+#define STATUS_CONNECTION_CLOSED -4\n+#define STATUS_CONNECTION_INVALID -5\n+#define STATUS_TIMEOUT -6\n+\n+static int read_data(int fd, size_t len, uint8_t *data, size_t *recv_len,\n+\t\t     int timeout)\n+{\n+\tstruct pollfd pfd;\n+\tssize_t ret;\n+\n+\tpfd.fd = fd;\n+\tpfd.events = POLLIN;\n+\tpfd.revents = 0;\n+\n+\tret = poll(&pfd, 1, timeout);\n+\tif (ret < 0) {\n+\t\tif (errno == EINTR)\n+\t\t\treturn STATUS_TRYAGAIN; /* Caught signal before timeout */\n+\t\tif (errno == EINVAL)\n+\t\t\treturn STATUS_INVALID_PARAMETER; /* Timeout is negative */\n+\t\tif (errno == EFAULT)\n+\t\t\treturn STATUS_INVALID_PARAMETER; /* Fds argument is illegal */\n+\t\t/* else */\n+\t\tassert(0);\n+\t\treturn STATUS_INTERNAL_ERROR;\n+\t}\n+\n+\tif (ret == 0)\n+\t\treturn STATUS_TIMEOUT;\n+\n+\tif (pfd.revents == 0) {\n+\t\tassert(ret == 1);\n+\t\tassert(0); /* Revents cannot be zero when NtSocket_Poll returns 1 */\n+\t\treturn STATUS_TRYAGAIN;\n+\t}\n+\n+\tif ((pfd.revents & POLLIN) &&\n+\t\t\t((pfd.revents & (POLLERR | POLLNVAL)) == 0)) {\n+\t\tret = recv(pfd.fd, data, len, 0);\n+\t\tif (ret < 0) {\n+\t\t\tint lerrno = errno;\n+\n+\t\t\tif (lerrno == EWOULDBLOCK || lerrno == EAGAIN) {\n+\t\t\t\t/*\n+\t\t\t\t * We have data but if the very first read turns out to return\n+\t\t\t\t * EWOULDBLOCK or EAGAIN it means that the remote  end has dropped\n+\t\t\t\t * the connection\n+\t\t\t\t */\n+\t\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t\t       \"The socket with fd %d has been closed by remote end. %d [%s]\\n\",\n+\t\t\t\t       pfd.fd, lerrno, strerror(lerrno));\n+\t\t\t\treturn STATUS_CONNECTION_CLOSED;\n+\t\t\t}\n+\t\t\tif (lerrno != EINTR) {\n+\t\t\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t\t\t       \"recv() from fd %d received errno %d [%s]\\n\",\n+\t\t\t\t       pfd.fd, lerrno, strerror(lerrno));\n+\t\t\t\treturn STATUS_CONNECTION_INVALID;\n+\t\t\t}\n+\t\t\t/* EINTR */\n+\t\t\treturn STATUS_TRYAGAIN;\n+\t\t}\n+\t\tif (ret == 0) {\n+\t\t\tif (pfd.revents & POLLHUP) {\n+\t\t\t\t/* This means that we have read all data and the remote end has\n+\t\t\t\t * HUP\n+\t\t\t\t */\n+#ifdef DEBUG\n+\t\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t\t       \"The remote end has terminated the session\\n\");\n+#endif\n+\t\t\t\treturn STATUS_CONNECTION_CLOSED;\n+\t\t\t}\n+\t\t\treturn STATUS_TRYAGAIN;\n+\t\t}\n+\n+\t\t/* Ret can only be positive at this point */\n+\t\t *recv_len = (size_t)ret;\n+\t\treturn STATUS_OK;\n+\t}\n+\n+\tif ((pfd.revents & POLLHUP) == POLLHUP) {\n+\t\t/* this means that the remote end has HUP */\n+\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t       \"The remote end has terminated the session\\n\");\n+\t\treturn STATUS_CONNECTION_CLOSED;\n+\t}\n+\n+\tNT_LOG(ERR, NTCONNECT,\n+\t       \"poll() returned 0x%x. Invalidating the connection\\n\",\n+\t       pfd.revents);\n+\treturn STATUS_CONNECTION_INVALID;\n+}\n+\n+static int read_all(int clfd, uint8_t *data, size_t length)\n+{\n+\tsize_t recv_len = 0;\n+\tsize_t left = length;\n+\tsize_t pos = 0;\n+\n+\twhile (left > 0) {\n+\t\tint ret = read_data(clfd, left, &data[pos], &recv_len, -1);\n+\n+\t\tif (ret == STATUS_OK) {\n+\t\t\tpos += recv_len;\n+\t\t\tleft -= recv_len;\n+\t\t} else {\n+\t\t\tif (ret == STATUS_CONNECTION_CLOSED || ret == STATUS_TIMEOUT) {\n+\t\t\t\t/* Silently return status */\n+\t\t\t\treturn ret;\n+\t\t\t}\n+\t\t\tif (ret != STATUS_TRYAGAIN) {\n+\t\t\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t\t\t       \"Failed getting packet. Error code: 0x%X\\n\",\n+\t\t\t\t       ret);\n+\t\t\t\treturn ret;\n+\t\t\t}\n+\t\t}\n+\t\t/* Try again */\n+\t}\n+\treturn STATUS_OK;\n+}\n+\n+static int write_all(int fd, const uint8_t *data, size_t size)\n+{\n+\tsize_t len = 0;\n+\n+\twhile (len < size) {\n+\t\tssize_t res = write(fd, (const void *)&data[len], size - len);\n+\n+\t\tif (res < 0) {\n+\t\t\tNT_LOG(ERR, NTCONNECT, \"write to socket failed!\");\n+\t\t\treturn STATUS_INTERNAL_ERROR;\n+\t\t}\n+\t\tlen += res;\n+\t}\n+\treturn 0;\n+}\n+\n+static int read_request(int clfd, char **rdata)\n+{\n+\tuint8_t *data = malloc(MESSAGE_BUFFER * sizeof(uint8_t));\n+\n+\tif (!data) {\n+\t\tNT_LOG(ERR, NTCONNECT, \"memory allocation failed\");\n+\t\treturn STATUS_INTERNAL_ERROR;\n+\t}\n+\n+\tsize_t recv_len = 0;\n+\tint ret = read_data(clfd, MESSAGE_BUFFER, data, &recv_len, -1);\n+\n+\tif (ret) {\n+\t\tfree(data);\n+\t\treturn ret;\n+\t}\n+\n+\tstruct ntconn_header_s hdr;\n+\n+\tmemcpy(&hdr, data, NTCMOD_HDR_LEN);\n+\tsize_t length = (hdr.len + hdr.blob_len) * sizeof(uint8_t);\n+\n+\tif (length > MESSAGE_BUFFER) {\n+\t\tuint8_t *new_data = realloc(data, length);\n+\n+\t\tif (!new_data) {\n+\t\t\tNT_LOG(ERR, NTCONNECT, \"memory reallocation failed\");\n+\t\t\tfree(data);\n+\t\t\treturn STATUS_INTERNAL_ERROR;\n+\t\t}\n+\t\tdata = new_data;\n+\t\tret = read_all(clfd, &data[recv_len], length - recv_len);\n+\t\tif (ret) {\n+\t\t\tfree(data);\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\t*rdata = (char *)data;\n+\treturn STATUS_OK;\n+}\n+\n+static ntconn_mod_t *ntconnect_interpret_request(int clfd,\n+\t\tstruct ntconn_header_s *hdr,\n+\t\tchar **get_req _unused,\n+\t\tchar **module_cmd, int *status)\n+{\n+\tchar pci_id[32];\n+\tchar module[64];\n+\tntconn_mod_t *result_ntcmod = NULL;\n+\tchar *request = NULL;\n+\n+\tint ret = read_request(clfd, &request);\n+\t*status = ret;\n+\t*get_req = request;\n+\n+\tif (ret == STATUS_OK && request) {\n+\t\t*hdr = *(struct ntconn_header_s *)request;\n+\n+\t\tif (!hdr) {\n+\t\t\tNT_LOG(ERR, NTCONNECT, \"hdr returned NULL\\n\");\n+\t\t\t*status = STATUS_INTERNAL_ERROR;\n+\t\t\treturn NULL;\n+\t\t}\n+\n+\t\tswitch (hdr->tag) {\n+\t\tcase NTCONN_TAG_REQUEST: {\n+\t\t\tunsigned long idx = NTCMOD_HDR_LEN;\n+\t\t\tchar *saveptr;\n+\t\t\tchar *req = &request[idx];\n+\n+\t\t\tuint32_t domain = 0xffffffff;\n+\t\t\tuint8_t bus = 0xff;\n+\t\t\tuint8_t devid = 0xff;\n+\t\t\tuint8_t function = 0xff;\n+\n+\t\t\tchar *tok = strtok_r(req, \";\", &saveptr);\n+\n+\t\t\tidx += strlen(tok) + 1;\n+\t\t\tif (!tok)\n+\t\t\t\tgoto err_out;\n+\t\t\trte_strscpy(pci_id, tok, 31);\n+\n+\t\t\ttok = strtok_r(NULL, \";\", &saveptr);\n+\t\t\tidx += strlen(tok) + 1;\n+\t\t\tif (!tok)\n+\t\t\t\tgoto err_out;\n+\t\t\trte_strscpy(module, tok, 63);\n+\n+\t\t\ttok = strtok_r(NULL, \"\", &saveptr);\n+\t\t\thdr->len -= idx;\n+\t\t\tif (tok)\n+\t\t\t\t*module_cmd = &request[idx];\n+\n+\t\t\ttok = strtok_r(pci_id, \":.\", &saveptr);\n+\t\t\tif (!tok)\n+\t\t\t\tgoto err_out;\n+\t\t\tdomain = (uint32_t)strtol(tok, NULL, 16);\n+\t\t\ttok = strtok_r(NULL, \":.\", &saveptr);\n+\t\t\tif (!tok)\n+\t\t\t\tgoto err_out;\n+\t\t\tbus = (uint8_t)strtol(tok, NULL, 16);\n+\n+\t\t\ttok = strtok_r(NULL, \":.\", &saveptr);\n+\t\t\tif (!tok)\n+\t\t\t\tgoto err_out;\n+\t\t\tdevid = (uint8_t)strtol(tok, NULL, 16);\n+\t\t\ttok = strtok_r(NULL, \"\", &saveptr);\n+\t\t\tif (!tok)\n+\t\t\t\tgoto err_out;\n+\t\t\tfunction = (uint8_t)strtol(tok, NULL, 16);\n+\n+\t\t\t/* Search for module registered as <pci_id:module> */\n+\t\t\tntconn_mod_t *ntcmod = ntcmod_base;\n+\n+\t\t\twhile (ntcmod) {\n+\t\t\t\tif (domain == ntcmod->addr.domain &&\n+\t\t\t\t\t\tbus == ntcmod->addr.bus &&\n+\t\t\t\t\t\tdevid == ntcmod->addr.devid &&\n+\t\t\t\t\t\tfunction == ntcmod->addr.function &&\n+\t\t\t\t\t\tstrcmp(ntcmod->op->module, module) == 0) {\n+\t\t\t\t\tresult_ntcmod = ntcmod;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t\tntcmod = ntcmod->next;\n+\t\t\t}\n+\t\t}\n+\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+err_out:\n+\n+\treturn result_ntcmod;\n+}\n+\n+static int send_reply(int clfd, uint16_t reply_tag, const void *data,\n+\t\t      uint32_t size)\n+{\n+\tstruct ntconn_header_s hdr;\n+\n+\thdr.tag = reply_tag;\n+\thdr.len = NTCMOD_HDR_LEN + size;\n+\thdr.blob_len = 0;\n+\tuint8_t *message = malloc(hdr.len * sizeof(uint8_t));\n+\n+\tif (!message) {\n+\t\tNT_LOG(ERR, NTCONNECT, \"memory allocation failed\");\n+\t\treturn STATUS_INTERNAL_ERROR;\n+\t}\n+\tmemcpy(message, (void *)&hdr, NTCMOD_HDR_LEN);\n+\tmemcpy(&message[NTCMOD_HDR_LEN], data, size);\n+\tint res = write_all(clfd, message, hdr.len);\n+\n+\tfree(message);\n+\tif (res)\n+\t\treturn res;\n+\n+\treturn 0;\n+}\n+\n+static int send_reply_free_data(int clfd, ntconn_mod_t *cmod,\n+\t\t\t\tuint16_t reply_tag, void *data, uint32_t size)\n+{\n+\tint res = send_reply(clfd, reply_tag, data, size);\n+\n+\tif (size) {\n+\t\tpthread_mutex_lock(&cmod->mutex);\n+\t\tcmod->op->free_data(cmod->hdl, data);\n+\t\tpthread_mutex_unlock(&cmod->mutex);\n+\t}\n+\n+\treturn res;\n+}\n+\n+static int ntconnect_send_error(int clfd, enum ntconn_err_e err_code)\n+{\n+\tchar err_buf[MAX_ERR_MESSAGE_LENGTH];\n+\tconst ntconn_err_t *ntcerr = get_ntconn_error(err_code);\n+\n+\tsprintf(err_buf, \"----connect:%s\", ntcerr->err_text);\n+\tunsigned int len = strlen(err_buf);\n+\t*(uint32_t *)err_buf = (uint32_t)ntcerr->err_code;\n+\n+\treturn send_reply(clfd, NTCONN_TAG_ERROR, err_buf, len);\n+}\n+\n+static void *ntconnect_worker(void *arg)\n+{\n+\tint status;\n+\tint clfd = (int)(uint64_t)arg;\n+\tchar *module_cmd = NULL;\n+\tchar *request = NULL;\n+\tstruct ntconn_header_s hdr;\n+\n+\tdo {\n+\t\tntconn_mod_t *cmod = ntconnect_interpret_request(clfd, &hdr,\n+\t\t\t\t\t\t\t\t &request,\n+\t\t\t\t\t\t\t\t &module_cmd,\n+\t\t\t\t\t\t\t\t &status);\n+\n+\t\tif (cmod && module_cmd && status == 0) {\n+\t\t\tint len;\n+\t\t\tchar *data;\n+\n+\t\t\t/*\n+\t\t\t * Handle general module commands\n+\t\t\t */\n+\t\t\tif (strcmp(module_cmd, \"version\") == 0) {\n+\t\t\t\tuint64_t version =\n+\t\t\t\t\t((uint64_t)cmod->op->version_major\n+\t\t\t\t\t << 32) +\n+\t\t\t\t\t(cmod->op->version_minor);\n+\n+\t\t\t\tif (send_reply(clfd, NTCONN_TAG_REPLY,\n+\t\t\t\t\t\t(void *)&version,\n+\t\t\t\t\t\tsizeof(uint64_t)))\n+\t\t\t\t\tbreak;\n+\n+\t\t\t} else {\n+\t\t\t\t/*\n+\t\t\t\t * Call module for execution of command\n+\t\t\t\t */\n+\t\t\t\tdata = NULL;\n+\t\t\t\tpthread_mutex_lock(&cmod->mutex);\n+\t\t\t\tint repl = cmod->op->request(cmod->hdl, clfd,\n+\t\t\t\t\t\t\t     &hdr, module_cmd,\n+\t\t\t\t\t\t\t     &data, &len);\n+\t\t\t\tpthread_mutex_unlock(&cmod->mutex);\n+\n+\t\t\t\tif (repl == REQUEST_OK && len >= 0) {\n+\t\t\t\t\tif (send_reply_free_data(clfd, cmod,\n+\t\t\t\t\t\t\t\t NTCONN_TAG_REPLY,\n+\t\t\t\t\t\t\t\t (void *)data,\n+\t\t\t\t\t\t\t\t (uint32_t)len))\n+\t\t\t\t\t\tbreak;\n+\n+\t\t\t\t} else if (repl == REQUEST_ERR && len >= 0) {\n+\t\t\t\t\tif (send_reply_free_data(clfd, cmod,\n+\t\t\t\t\t\t\t\t NTCONN_TAG_ERROR,\n+\t\t\t\t\t\t\t\t (void *)data,\n+\t\t\t\t\t\t\t\t (uint32_t)len))\n+\t\t\t\t\t\tbreak;\n+\t\t\t\t} else {\n+\t\t\t\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t\t\t\t       \"Invalid result from module request function: module %s, result %i\\n\",\n+\t\t\t\t\t       cmod->op->module, repl);\n+\t\t\t\t\tif (ntconnect_send_error(clfd,\n+\t\t\t\t\t\tNTCONN_ERR_CODE_INTERNAL_REPLY_ERROR))\n+\t\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t} else if (status == STATUS_TIMEOUT) {\n+\t\t\t/* Other end is dead */\n+\t\t\tNT_LOG(WRN, NTCONNECT,\n+\t\t\t       \"Client must be dead - timeout\\n\");\n+\t\t\tbreak;\n+\t\t} else if (status == STATUS_CONNECTION_CLOSED) {\n+\t\t\tbreak; /* silently break out */\n+\t\t}\n+\t\t/* Error - send error back */\n+\t\tif (ntconnect_send_error(clfd, NTCONN_ERR_CODE_INVALID_REQUEST))\n+\t\t\tbreak;\n+\t\tif (request)\n+\t\t\tfree(request);\n+\t} while (1); /* while still connected */\n+\n+\tclose(clfd);\n+\n+\t/* call module cleanup callback function for client_id */\n+\tntconn_mod_t *ntcmod = ntcmod_base;\n+\n+\twhile (ntcmod) {\n+\t\tif (ntcmod->op->client_cleanup) {\n+\t\t\tpthread_mutex_lock(&ntcmod->mutex);\n+\t\t\tntcmod->op->client_cleanup(ntcmod->hdl, clfd);\n+\t\t\tpthread_mutex_unlock(&ntcmod->mutex);\n+\t\t}\n+\n+\t\tntcmod = ntcmod->next;\n+\t}\n+\tpthread_exit(NULL);\n+\treturn NULL;\n+}\n+\n+static void *ntconnect_server(void *arg)\n+{\n+\tstruct ntconn_server_s *ntcserv = (struct ntconn_server_s *)arg;\n+\n+\tntcserv->running = 1;\n+\n+#ifdef DEBUG\n+\tNT_LOG(DBG, NTCONNECT, \"Running NT Connection Server fd %i\\n\",\n+\t       ntcserv->serv_fd);\n+#endif\n+\n+\tif (listen(ntcserv->serv_fd, 5) < 0) {\n+\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t       \"Server failed on listen(), stopping thread. err: %s\\n\",\n+\t\t       strerror(errno));\n+\t\tpthread_exit(NULL);\n+\t\treturn NULL;\n+\t}\n+\n+\twhile (ntcserv->running) {\n+\t\tint clfd = accept(ntcserv->serv_fd, NULL, NULL);\n+\n+\t\tif (clfd < 0) {\n+\t\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t\t       \"ERROR from accept(), stopping thread. err: %s\\n\",\n+\t\t\t       strerror(errno));\n+\t\t\tbreak;\n+\t\t}\n+\t\tpthread_create(&ctid, NULL, ntconnect_worker,\n+\t\t\t       (void *)(uint64_t)clfd);\n+\t\tpthread_setaffinity_np(ctid, sizeof(cpu_set_t),\n+\t\t\t\t       &ntcserv->cpuset);\n+\t\t/* Detach immediately. We will never join this thread */\n+\t\tpthread_detach(ctid);\n+\t}\n+\n+\tpthread_exit(NULL);\n+\treturn NULL;\n+}\n+\n+int ntconnect_init(const char *sockname, cpu_set_t cpuset)\n+{\n+\tif (ntcmod_base) {\n+\t\t/* Make sure the socket directory exists */\n+\t\tchar *sockname_copy = strdup(sockname);\n+\t\tchar *sockname_dir = dirname(sockname_copy);\n+\n+\t\tif (mkdir(sockname_dir, 0755) < 0 && errno != EEXIST) {\n+\t\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t\t       \"Can't create socket directory: %s\",\n+\t\t\t       sockname_dir);\n+\t\t\tfree(sockname_copy);\n+\t\t\treturn -1;\n+\t\t}\n+\t\tfree(sockname_copy);\n+\n+\t\t/* Add server to module list - cannot work without */\n+\t\tntconn_server_register(&ntconn_serv);\n+\n+\t\t/* Start named socket server */\n+\t\tstruct sockaddr_un addr;\n+\n+\t\tunix_build_address(sockname, &addr);\n+\n+\t\tntconn_serv.serv_fd = socket(AF_UNIX, SOCK_STREAM, 0);\n+\t\tntconn_serv.cpuset = cpuset;\n+\t\tif (ntconn_serv.serv_fd == -1)\n+\t\t\treturn -1;\n+\n+\t\t/* Make sure the node in filesystem is deleted otherwise bind will fail */\n+\t\tunlink(sockname);\n+\n+\t\tif (bind(ntconn_serv.serv_fd, (struct sockaddr *)&addr,\n+\t\t\t\tsizeof(struct sockaddr_un)) == -1) {\n+\t\t\tclose(ntconn_serv.serv_fd);\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\t/* Run ntconnect service */\n+\t\tpthread_create(&tid, NULL, ntconnect_server, &ntconn_serv);\n+\t\tpthread_setaffinity_np(tid, sizeof(cpu_set_t),\n+\t\t\t\t       &ntconn_serv.cpuset);\n+\t}\n+\n+\treturn 0;\n+}\ndiff --git a/drivers/net/ntnic/ntconnect_modules/ntconn_adapter.c b/drivers/net/ntnic/ntconnect_modules/ntconn_adapter.c\nnew file mode 100644\nindex 0000000000..294b95846b\n--- /dev/null\n+++ b/drivers/net/ntnic/ntconnect_modules/ntconn_adapter.c\n@@ -0,0 +1,775 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include \"ntnic_ethdev.h\"\n+#include \"ntconnect.h\"\n+#include \"ntconnect_api_adapter.h\"\n+#include \"ntos_system.h\"\n+#include \"ntconn_modules.h\"\n+#include \"ntconn_mod_helper.h\"\n+#include \"nt_util.h\"\n+#include \"ntlog.h\"\n+\n+#define NTCONN_ADAP_VERSION_MAJOR 0U\n+#define NTCONN_ADAP_VERSION_MINOR 1U\n+\n+#define this_module_name \"adapter\"\n+\n+#define MAX_ADAPTERS 2\n+\n+static struct adap_hdl_s {\n+\tstruct drv_s *drv;\n+} adap_hdl[MAX_ADAPTERS];\n+\n+static int func_adapter_get_interfaces(void *hdl, int client_id,\n+\t\t\t\t       struct ntconn_header_s *hdr, char **data,\n+\t\t\t\t       int *len);\n+static int func_adapter_get_info(void *hdl, int client_id,\n+\t\t\t\t struct ntconn_header_s *hdr, char **data,\n+\t\t\t\t int *len);\n+static int func_adapter_get_sensors(void *hdl, int client_id _unused,\n+\t\t\t\t    struct ntconn_header_s *hdr _unused,\n+\t\t\t\t    char **data, int *len);\n+static struct func_s funcs_get_level1[] = {\n+\t{ \"interfaces\", NULL, func_adapter_get_interfaces },\n+\t{ \"info\", NULL, func_adapter_get_info },\n+\t{ \"sensors\", NULL, func_adapter_get_sensors },\n+\t{ NULL, NULL, NULL },\n+};\n+\n+static int func_adapter_set_interface(void *hdl, int client_id,\n+\t\t\t\t      struct ntconn_header_s *hdr, char **data,\n+\t\t\t\t      int *len);\n+static int func_adapter_set_adapter(void *hdl, int client_id,\n+\t\t\t\t    struct ntconn_header_s *hdr, char **data,\n+\t\t\t\t    int *len);\n+static struct func_s funcs_set_level1[] = {\n+\t{ \"interface\", NULL, func_adapter_set_interface },\n+\t{ \"adapter\", NULL, func_adapter_set_adapter },\n+\t{ NULL, NULL, NULL },\n+};\n+\n+/*\n+ * Entry level\n+ */\n+static struct func_s adapter_entry_funcs[] = {\n+\t{ \"get\", funcs_get_level1, NULL },\n+\t{ \"set\", funcs_set_level1, NULL },\n+\t{ NULL, NULL, NULL },\n+};\n+\n+static int read_link_speed(enum nt_link_speed_e link_speed)\n+{\n+\tswitch (link_speed) {\n+\tcase NT_LINK_SPEED_10M:\n+\t\treturn PORT_LINK_SPEED_10M;\n+\tcase NT_LINK_SPEED_100M:\n+\t\treturn PORT_LINK_SPEED_100M;\n+\tcase NT_LINK_SPEED_1G:\n+\t\treturn PORT_LINK_SPEED_1G;\n+\tcase NT_LINK_SPEED_10G:\n+\t\treturn PORT_LINK_SPEED_10G;\n+\tcase NT_LINK_SPEED_25G:\n+\t\treturn PORT_LINK_SPEED_25G;\n+\tcase NT_LINK_SPEED_40G:\n+\t\treturn PORT_LINK_SPEED_40G;\n+\tcase NT_LINK_SPEED_50G:\n+\t\treturn PORT_LINK_SPEED_50G;\n+\tcase NT_LINK_SPEED_100G:\n+\t\treturn PORT_LINK_SPEED_100G;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+\treturn PORT_LINK_SPEED_UNKNOWN;\n+}\n+\n+static nt_link_speed_t convert_link_speed(char *speed_str)\n+{\n+\tif (strcmp(speed_str, \"10M\") == 0)\n+\t\treturn NT_LINK_SPEED_10M;\n+\telse if (strcmp(speed_str, \"100M\") == 0)\n+\t\treturn NT_LINK_SPEED_100M;\n+\telse if (strcmp(speed_str, \"1G\") == 0)\n+\t\treturn NT_LINK_SPEED_1G;\n+\telse if (strcmp(speed_str, \"10G\") == 0)\n+\t\treturn NT_LINK_SPEED_10G;\n+\telse if (strcmp(speed_str, \"25G\") == 0)\n+\t\treturn NT_LINK_SPEED_25G;\n+\telse if (strcmp(speed_str, \"40G\") == 0)\n+\t\treturn NT_LINK_SPEED_40G;\n+\telse if (strcmp(speed_str, \"50G\") == 0)\n+\t\treturn NT_LINK_SPEED_50G;\n+\telse if (strcmp(speed_str, \"100G\") == 0)\n+\t\treturn NT_LINK_SPEED_100G;\n+\telse\n+\t\treturn NT_LINK_SPEED_UNKNOWN;\n+}\n+\n+static int func_adapter_get_interfaces(void *hdl, int client_id _unused,\n+\t\t\t\t       struct ntconn_header_s *hdr _unused,\n+\t\t\t\t       char **data, int *len)\n+{\n+\tstruct ntc_interfaces_s *ifs;\n+\tstruct adap_hdl_s *adap = (struct adap_hdl_s *)hdl;\n+\tfpga_info_t *fpga_info = &adap->drv->ntdrv.adapter_info.fpga_info;\n+\tint lag_active;\n+\tint final_list = adap->drv->probe_finished;\n+\t/* keep final_list set before nb_ports are called */\n+\trte_compiler_barrier();\n+\tint nb_ports = rte_eth_dev_count_avail();\n+\n+\t/* Get the \"internals\" structure of phy port 0 to find out if we're running LAG */\n+\tchar phy0_name[128];\n+\n+\trte_eth_dev_get_name_by_port(0, phy0_name);\n+\tstruct rte_eth_dev *phy0_eth_dev = rte_eth_dev_get_by_name(phy0_name);\n+\n+\tif (phy0_eth_dev == NULL || phy0_eth_dev->data == NULL ||\n+\t\t\tphy0_eth_dev->data->dev_private == NULL) {\n+\t\treturn ntconn_error(data, len, this_module_name,\n+\t\t\t\t    NTCONN_ERR_CODE_INTERNAL_ERROR);\n+\t}\n+\tstruct pmd_internals *phy0_internals =\n+\t\t(struct pmd_internals *)phy0_eth_dev->data->dev_private;\n+\tlag_active = (phy0_internals->lag_config == NULL) ? 0 : 1;\n+\tif (lag_active) {\n+\t\t/*\n+\t\t * Phy ports are link aggregated. I.e. number of ports is actually\n+\t\t * one bigger than what rte_eth_dev_count_avail() returned\n+\t\t */\n+\t\tnb_ports++;\n+\n+\t\t/*\n+\t\t * Sanity check:\n+\t\t * For now we know about LAG with 2 ports only.\n+\t\t * If in the future we get HW with more ports, make assert to alert\n+\t\t * the developers that something needs to be looked at...\n+\t\t */\n+\t\tassert(fpga_info->n_phy_ports == 2);\n+\t}\n+\n+\t*len = sizeof(struct ntc_interfaces_s) +\n+\t       sizeof(struct ntc_interface_s) * nb_ports;\n+\tifs = malloc(*len);\n+\tif (!ifs) {\n+\t\t*len = 0;\n+\t\tNT_LOG(ERR, NTCONNECT, \"memory allocation failed\");\n+\t\treturn REQUEST_ERR;\n+\t}\n+\t*data = (char *)ifs;\n+\n+\tifs->nb_ports = nb_ports;\n+\tifs->final_list = final_list;\n+\n+\tint i;\n+\n+\t/* First set the \"port type\" of the physical ports */\n+\tif (lag_active) {\n+\t\tif (phy0_internals->lag_config->mode == BONDING_MODE_8023AD) {\n+\t\t\t/* Active/active LAG */\n+\t\t\tfor (i = 0; i < fpga_info->n_phy_ports; i++) {\n+\t\t\t\tifs->intf[i].type =\n+\t\t\t\t\tPORT_TYPE_PHY_LAG_ACTIVE_ACTIVE;\n+\t\t\t}\n+\t\t} else if (phy0_internals->lag_config->mode ==\n+\t\t\t\tBONDING_MODE_ACTIVE_BACKUP) {\n+\t\t\t/* Active/backup LAG */\n+\t\t\tifs->intf[phy0_internals->lag_config->primary_port]\n+\t\t\t.type = PORT_TYPE_PHY_LAG_PRIMARY;\n+\t\t\tifs->intf[phy0_internals->lag_config->backup_port].type =\n+\t\t\t\tPORT_TYPE_PHY_LAG_BACKUP;\n+\t\t} else {\n+\t\t\t/* Unknown LAG mode */\n+\t\t\tassert(0);\n+\t\t}\n+\t} else {\n+\t\t/* Normal phy ports (not link aggregated) */\n+\t\tfor (i = 0; i < fpga_info->n_phy_ports; i++)\n+\t\t\tifs->intf[i].type = PORT_TYPE_PHY_NORMAL;\n+\t}\n+\n+\t/* Then set the remaining port values for the physical ports. */\n+\tfor (i = 0; i < fpga_info->n_phy_ports; i++) {\n+\t\tchar name[128];\n+\n+\t\tif (i > 0 && lag_active) {\n+\t\t\t/*\n+\t\t\t * Secondary link aggregated port. Just display the \"internals\" values\n+\t\t\t * from port 0\n+\t\t\t */\n+\t\t\trte_eth_dev_get_name_by_port(0, name);\n+\t\t} else {\n+\t\t\trte_eth_dev_get_name_by_port(i, name);\n+\t\t}\n+\t\tstruct rte_eth_dev *eth_dev = rte_eth_dev_get_by_name(name);\n+\n+\t\tstruct pmd_internals *internals =\n+\t\t\t(struct pmd_internals *)eth_dev->data->dev_private;\n+\t\tstruct adapter_info_s *p_adapter_info =\n+\t\t\t\t&adap->drv->ntdrv.adapter_info;\n+\n+\t\tifs->intf[i].port_id = i;\n+\t\tifs->intf[i].pci_id.domain = internals->pci_dev->addr.domain;\n+\t\tifs->intf[i].pci_id.bus = internals->pci_dev->addr.bus;\n+\t\tifs->intf[i].pci_id.devid = internals->pci_dev->addr.devid;\n+\t\tifs->intf[i].pci_id.function =\n+\t\t\tinternals->pci_dev->addr.function;\n+\t\tifs->intf[i].pci_id.pad = 0;\n+\n+\t\tconst bool port_link_status =\n+\t\t\tnt4ga_port_get_link_status(p_adapter_info, i);\n+\t\tifs->intf[i].link = port_link_status ? PORT_LINK_UP :\n+\t\t\t\t    PORT_LINK_DOWN;\n+\n+\t\tconst nt_link_speed_t port_link_speed =\n+\t\t\tnt4ga_port_get_link_speed(p_adapter_info, i);\n+\t\tifs->intf[i].port_speed = read_link_speed(port_link_speed);\n+\n+\t\tconst bool port_adm_state =\n+\t\t\tnt4ga_port_get_adm_state(p_adapter_info, i);\n+\t\tif (!port_adm_state) {\n+\t\t\tifs->intf[i].port_state = PORT_STATE_DISABLED;\n+\t\t} else {\n+\t\t\tconst bool port_nim_present =\n+\t\t\t\tnt4ga_port_get_nim_present(p_adapter_info, i);\n+\t\t\tif (port_nim_present) {\n+\t\t\t\tifs->intf[i].port_state =\n+\t\t\t\t\tPORT_STATE_NIM_PRESENT;\n+\t\t\t} else {\n+\t\t\t\tifs->intf[i].port_state = PORT_STATE_NIM_ABSENT;\n+\t\t\t}\n+\t\t}\n+\n+\t\t/* MTU */\n+\t\tif (i > 0 && lag_active) {\n+\t\t\t/* Secondary link aggregated port. Display same MTU value as port 0 */\n+\t\t\trte_eth_dev_get_mtu(0, &ifs->intf[i].mtu);\n+\t\t} else {\n+\t\t\trte_eth_dev_get_mtu(i, &ifs->intf[i].mtu);\n+\t\t}\n+\n+\t\t/* MAC */\n+\t\tconst uint64_t mac =\n+\t\t\tfpga_info->nthw_hw_info.vpd_info.mn_mac_addr_value + i;\n+\t\tifs->intf[i].mac.addr_b[0] = (mac >> 40) & 0xFFu;\n+\t\tifs->intf[i].mac.addr_b[1] = (mac >> 32) & 0xFFu;\n+\t\tifs->intf[i].mac.addr_b[2] = (mac >> 24) & 0xFFu;\n+\t\tifs->intf[i].mac.addr_b[3] = (mac >> 16) & 0xFFu;\n+\t\tifs->intf[i].mac.addr_b[4] = (mac >> 8) & 0xFFu;\n+\t\tifs->intf[i].mac.addr_b[5] = (mac >> 0) & 0xFFu;\n+\n+\t\tif (i > 0 && lag_active) {\n+\t\t\t/* Secondary link aggregated port. Queues not applicable */\n+\t\t\tifs->intf[i].num_queues = 0;\n+\t\t} else {\n+\t\t\t/* attached hw queues to this interface */\n+\t\t\tunsigned int input_num = internals->nb_rx_queues;\n+\t\t\t/*\n+\t\t\t * These are the \"input\" queues, meaning these go to host and is attached\n+\t\t\t * to receiving from a port\n+\t\t\t */\n+\t\t\tfor (unsigned int ii = 0; ii < input_num; ii++) {\n+\t\t\t\tifs->intf[i].queue[ii].idx =\n+\t\t\t\t\tinternals->rxq_scg[ii].queue.hw_id;\n+\t\t\t\tifs->intf[i].queue[ii].dir = QUEUE_INPUT;\n+\t\t\t}\n+\n+\t\t\t/*\n+\t\t\t * These are the \"output\" queues, meaning these go to a virtual port queue\n+\t\t\t * which typically is used by vDPA\n+\t\t\t */\n+\t\t\tfor (unsigned int ii = 0; ii < internals->vpq_nb_vq;\n+\t\t\t\t\tii++) {\n+\t\t\t\tifs->intf[i].queue[ii + input_num].idx =\n+\t\t\t\t\tinternals->vpq[ii].hw_id;\n+\t\t\t\tifs->intf[i].queue[ii + input_num].dir =\n+\t\t\t\t\tQUEUE_OUTPUT;\n+\t\t\t}\n+\n+\t\t\tifs->intf[i].num_queues =\n+\t\t\t\tinput_num + internals->vpq_nb_vq;\n+\t\t}\n+\n+\t\t/* NIM information */\n+\t\tnim_i2c_ctx_t nim_ctx =\n+\t\t\tnt4ga_port_get_nim_capabilities(p_adapter_info, i);\n+\n+\t\tstrlcpy((char *)&ifs->intf[i].nim_data.vendor_name,\n+\t\t\tnim_ctx.vendor_name,\n+\t\t\tsizeof(ifs->intf[i].nim_data.vendor_name));\n+\t\tstrlcpy((char *)&ifs->intf[i].nim_data.prod_no, nim_ctx.prod_no,\n+\t\t\tsizeof(ifs->intf[i].nim_data.prod_no));\n+\t\tstrlcpy((char *)&ifs->intf[i].nim_data.serial_no,\n+\t\t\tnim_ctx.serial_no,\n+\t\t\tsizeof(ifs->intf[i].nim_data.serial_no));\n+\t\tstrlcpy((char *)&ifs->intf[i].nim_data.date, nim_ctx.date,\n+\t\t\tsizeof(ifs->intf[i].nim_data.date));\n+\t\tstrlcpy((char *)&ifs->intf[i].nim_data.rev, nim_ctx.rev,\n+\t\t\tsizeof(ifs->intf[i].nim_data.rev));\n+\n+\t\tif (nim_ctx.len_info[0] >= 0xFFFF)\n+\t\t\tifs->intf[i].nim_data.link_length.sm = 0xFFFF;\n+\t\telse\n+\t\t\tifs->intf[i].nim_data.link_length.sm =\n+\t\t\t\tnim_ctx.len_info[0];\n+\n+\t\tifs->intf[i].nim_data.link_length.ebw = nim_ctx.len_info[1];\n+\t\tifs->intf[i].nim_data.link_length.mm50 = nim_ctx.len_info[2];\n+\t\tifs->intf[i].nim_data.link_length.mm62 = nim_ctx.len_info[3];\n+\t\tifs->intf[i].nim_data.link_length.copper = nim_ctx.len_info[4];\n+\n+\t\tifs->intf[i].nim_data.pwr_level_req = nim_ctx.pwr_level_req;\n+\t\tifs->intf[i].nim_data.pwr_level_cur = nim_ctx.pwr_level_cur;\n+\t\tifs->intf[i].nim_data.nim_id = nim_ctx.nim_id;\n+\t\tifs->intf[i].nim_data.port_type = nim_ctx.port_type;\n+\t}\n+\n+\t/* And finally handle the virtual ports. */\n+\tint rte_eth_dev_virt_port_offset = lag_active ? 1 :\n+\t\t\t\t\t   fpga_info->n_phy_ports;\n+\tfor (; i < nb_ports; i++, rte_eth_dev_virt_port_offset++) {\n+\t\t/* Continue counting from the \"i\" value reached in the previous for loop */\n+\t\tchar name[128];\n+\n+\t\trte_eth_dev_get_name_by_port(rte_eth_dev_virt_port_offset,\n+\t\t\t\t\t     name);\n+\t\tstruct rte_eth_dev *eth_dev = rte_eth_dev_get_by_name(name);\n+\n+\t\tstruct pmd_internals *internals =\n+\t\t\t(struct pmd_internals *)eth_dev->data->dev_private;\n+\n+\t\tifs->intf[i].port_id = i;\n+\t\tifs->intf[i].type = PORT_TYPE_VIRT;\n+\t\tifs->intf[i].pci_id.domain = internals->pci_dev->addr.domain;\n+\t\tifs->intf[i].pci_id.bus = internals->pci_dev->addr.bus;\n+\t\tifs->intf[i].pci_id.devid = internals->pci_dev->addr.devid;\n+\t\tifs->intf[i].pci_id.function =\n+\t\t\tinternals->pci_dev->addr.function;\n+\t\tifs->intf[i].pci_id.pad = 0;\n+\n+\t\tifs->intf[i].port_speed = PORT_LINK_SPEED_NONE_REPORTED;\n+\t\tswitch (internals->vport_comm) {\n+\t\tcase VIRT_PORT_NEGOTIATED_NONE:\n+\t\t\tifs->intf[i].port_state = PORT_STATE_VIRTUAL_UNATTACHED;\n+\t\t\tifs->intf[i].link = PORT_LINK_DOWN;\n+\t\t\tbreak;\n+\t\tcase VIRT_PORT_NEGOTIATED_SPLIT:\n+\t\t\tifs->intf[i].port_state = PORT_STATE_VIRTUAL_SPLIT;\n+\t\t\tifs->intf[i].link = PORT_LINK_UP;\n+\t\t\tbreak;\n+\t\tcase VIRT_PORT_NEGOTIATED_PACKED:\n+\t\t\tifs->intf[i].port_state = PORT_STATE_VIRTUAL_PACKED;\n+\t\t\tifs->intf[i].link = PORT_LINK_UP;\n+\t\t\tbreak;\n+\t\tcase VIRT_PORT_USE_RELAY:\n+\t\t\tifs->intf[i].port_state = PORT_STATE_VIRTUAL_RELAY;\n+\t\t\tifs->intf[i].link = PORT_LINK_UP;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\t/* MTU */\n+\t\trte_eth_dev_get_mtu(rte_eth_dev_virt_port_offset,\n+\t\t\t\t    &ifs->intf[i].mtu);\n+\n+\t\t/* MAC */\n+\t\tfor (int ii = 0; ii < 6; ii++) {\n+\t\t\tifs->intf[i].mac.addr_b[ii] =\n+\t\t\t\tinternals->eth_addrs[0].addr_bytes[ii];\n+\t\t}\n+\n+\t\t/* attached hw queues to this interface */\n+\t\tunsigned int input_num = internals->nb_rx_queues;\n+\n+\t\t/*\n+\t\t * These are the \"input\" queues, meaning these go to host and is attached to\n+\t\t * receiving from a port\n+\t\t */\n+\t\tfor (unsigned int ii = 0; ii < input_num; ii++) {\n+\t\t\tifs->intf[i].queue[ii].idx =\n+\t\t\t\tinternals->rxq_scg[ii].queue.hw_id;\n+\t\t\tifs->intf[i].queue[ii].dir = QUEUE_INPUT;\n+\t\t}\n+\n+\t\t/*\n+\t\t * These are the \"output\" queues, meaning these go to a virtual port queue\n+\t\t * which typically is used by vDPA\n+\t\t */\n+\t\tunsigned int numq =\n+\t\t\t((internals->vpq_nb_vq + input_num) > MAX_RSS_QUEUES) ?\n+\t\t\tMAX_RSS_QUEUES - input_num :\n+\t\t\tinternals->vpq_nb_vq;\n+\t\tfor (unsigned int ii = 0; ii < numq; ii++) {\n+\t\t\tifs->intf[i].queue[ii + input_num].idx =\n+\t\t\t\tinternals->vpq[ii].hw_id;\n+\t\t\tifs->intf[i].queue[ii + input_num].dir = QUEUE_OUTPUT;\n+\t\t}\n+\t\tifs->intf[i].num_queues = input_num + numq;\n+\t}\n+\treturn REQUEST_OK;\n+}\n+\n+static int func_adapter_get_info(void *hdl, int client_id _unused,\n+\t\t\t\t struct ntconn_header_s *hdr _unused,\n+\t\t\t\t char **data, int *len)\n+{\n+\tstruct adap_hdl_s *adap = (struct adap_hdl_s *)hdl;\n+\tfpga_info_t *fpga_info = &adap->drv->ntdrv.adapter_info.fpga_info;\n+\n+\t*len = sizeof(struct ntc_adap_get_info_s);\n+\t*data = malloc(*len);\n+\tif (!*data) {\n+\t\t*len = 0;\n+\t\tNT_LOG(ERR, NTCONNECT, \"memory allocation failed\");\n+\t\treturn REQUEST_ERR;\n+\t}\n+\n+\tsnprintf(*data, 31, \"%03d-%04d-%02d-%02d\", fpga_info->n_fpga_type_id,\n+\t\t fpga_info->n_fpga_prod_id, fpga_info->n_fpga_ver_id,\n+\t\t fpga_info->n_fpga_rev_id);\n+\n+\treturn REQUEST_OK;\n+}\n+\n+static int func_adapter_get_sensors(void *hdl, int client_id _unused,\n+\t\t\t\t    struct ntconn_header_s *hdr _unused,\n+\t\t\t\t    char **data, int *len)\n+{\n+\tstruct adapter_info_s *adapter =\n+\t\t&(((struct adap_hdl_s *)hdl)->drv->ntdrv.adapter_info);\n+\tstruct sensor *sensor_ptr = NULL;\n+\tuint16_t sensors_num = 0;\n+\tuint8_t *sensors = NULL;\n+\tstruct ntc_sensors_s sensors_info = {\n+\t\t.adapter_sensors_cnt = adapter->adapter_sensors_cnt,\n+\t\t.ports_cnt = adapter->fpga_info.n_phy_ports\n+\t};\n+\tmemcpy(sensors_info.adapter_name, adapter->p_dev_name, 24);\n+\n+\t/* Set a sum of sensor`s counters */\n+\tsensors_num = adapter->adapter_sensors_cnt;\n+\tfor (int i = 0; i < adapter->fpga_info.n_phy_ports; i++) {\n+\t\tsensors_num += adapter->nim_sensors_cnt[i];\n+\t\tsensors_info.nim_sensors_cnt[i] = adapter->nim_sensors_cnt[i];\n+\t}\n+\n+\t*len = sizeof(struct ntc_sensors_s) +\n+\t       sensors_num * sizeof(struct sensor);\n+\n+\t/* Allocate memory for sensors array */\n+\tsensors = malloc(*len);\n+\tif (!sensors) {\n+\t\tNT_LOG(ERR, NTCONNECT, \"memory allocation failed\");\n+\t\t*len = 0;\n+\t\treturn REQUEST_ERR;\n+\t}\n+\tmemcpy(sensors, &sensors_info, sizeof(struct ntc_sensors_s));\n+\tsensor_ptr = (struct sensor *)(sensors + sizeof(struct ntc_sensors_s));\n+\n+\t/* Fetch adapter sensors */\n+\tfor (struct nt_sensor_group *ptr = adapter->adapter_sensors;\n+\t\t\tptr != NULL; ptr = ptr->next) {\n+\t\tsensor_ptr->current_value = ptr->sensor->info.value;\n+\t\tsensor_ptr->min_value = ptr->sensor->info.value_lowest;\n+\t\tsensor_ptr->max_value = ptr->sensor->info.value_highest;\n+\t\tsensor_ptr->sign = ptr->sensor->si;\n+\t\tsensor_ptr->type = ptr->sensor->info.type;\n+\t\tmemcpy(sensor_ptr->name, ptr->sensor->info.name, 50);\n+\t\tsensor_ptr++;\n+\t}\n+\n+\t/* Fetch NIM sensors */\n+\tfor (int i = 0; i < adapter->fpga_info.n_phy_ports; i++) {\n+\t\tfor (struct nim_sensor_group *ptr = adapter->nim_sensors[i];\n+\t\t\t\tptr != NULL; ptr = ptr->next) {\n+\t\t\tsensor_ptr->current_value = ptr->sensor->info.value;\n+\t\t\tsensor_ptr->min_value = ptr->sensor->info.value_lowest;\n+\t\t\tsensor_ptr->max_value = ptr->sensor->info.value_highest;\n+\t\t\tsensor_ptr->sign = ptr->sensor->si;\n+\t\t\tsensor_ptr->type = ptr->sensor->info.type;\n+\n+\t\t\tmemcpy(sensor_ptr->name, ptr->sensor->info.name,\n+\t\t\t       (strlen(ptr->sensor->info.name) >= 50) ?\n+\t\t\t       50 :\n+\t\t\t       strlen(ptr->sensor->info.name));\n+\t\t\tsensor_ptr++;\n+\t\t}\n+\t}\n+\n+\t/* Send response */\n+\t *data = (char *)sensors;\n+\n+\treturn REQUEST_OK;\n+}\n+\n+static int set_port_enable(struct adap_hdl_s *adap, int port_nr)\n+{\n+\tadapter_info_t *p_adapter_info = &adap->drv->ntdrv.adapter_info;\n+\n+\tnt4ga_port_set_adm_state(p_adapter_info, port_nr, true);\n+\n+\treturn REQUEST_OK;\n+}\n+\n+static int set_port_disable(struct adap_hdl_s *adap, int port_nr)\n+{\n+\tadapter_info_t *p_adapter_info = &adap->drv->ntdrv.adapter_info;\n+\n+\tnt4ga_port_set_adm_state(p_adapter_info, port_nr, false);\n+\n+\treturn REQUEST_OK;\n+}\n+\n+static int set_link_up(struct adap_hdl_s *adap, int portid)\n+{\n+\tstruct adapter_info_s *p_adapter_info = &adap->drv->ntdrv.adapter_info;\n+\n+\tconst bool link_status =\n+\t\tnt4ga_port_get_link_status(p_adapter_info, portid);\n+\n+\tif (!link_status) {\n+\t\tnt4ga_port_set_link_status(p_adapter_info, portid, true);\n+\t\tNT_LOG(DBG, NTCONNECT, \"Port %i: Link set to be up\\n\", portid);\n+\t} else {\n+\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t       \"Port %i: Link is already set to be up\\n\", portid);\n+\t}\n+\n+\treturn REQUEST_OK;\n+}\n+\n+static int set_link_down(struct adap_hdl_s *adap, int portid)\n+{\n+\tstruct adapter_info_s *p_adapter_info = &adap->drv->ntdrv.adapter_info;\n+\n+\tconst bool link_status =\n+\t\tnt4ga_port_get_link_status(p_adapter_info, portid);\n+\n+\tif (!link_status) {\n+\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t       \"Port %i: Link is already set to be down\\n\", portid);\n+\t} else {\n+\t\tnt4ga_port_set_link_status(p_adapter_info, portid, false);\n+\t\tNT_LOG(DBG, NTCONNECT, \"Port %i: Link set to be down\\n\",\n+\t\t       portid);\n+\t}\n+\n+\treturn REQUEST_OK;\n+}\n+\n+static int set_link_speed(struct adap_hdl_s *adap, int portid, char *speed_str,\n+\t\t\t  char **data, int *len)\n+{\n+\tstruct adapter_info_s *p_adapter_info = &adap->drv->ntdrv.adapter_info;\n+\n+\tconst bool port_adm_state =\n+\t\tnt4ga_port_get_adm_state(p_adapter_info, portid);\n+\tif (!port_adm_state) {\n+\t\tconst nt_link_speed_t speed = convert_link_speed(speed_str);\n+\n+\t\tif (speed != NT_LINK_SPEED_UNKNOWN) {\n+\t\t\tnt4ga_port_set_link_speed(p_adapter_info, portid, speed);\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"Port %i: set link speed - %s\\n\",\n+\t\t\t       portid, speed_str);\n+\t\t} else {\n+\t\t\treturn ntconn_error(data, len, this_module_name,\n+\t\t\t\t\t    NTCONN_ERR_CODE_MISSING_INVALID_PARAM);\n+\t\t}\n+\t} else {\n+\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t       \"Port %i: fail to set link speed, port is enabled\\n\",\n+\t\t       portid);\n+\t\treturn ntconn_reply_status(data, len,\n+\t\t\t\t\t   NTCONN_ADAPTER_ERR_WRONG_LINK_STATE);\n+\t}\n+\n+\treturn REQUEST_OK;\n+}\n+\n+static int set_loopback_mode(struct adap_hdl_s *adap, int portid, int mode)\n+{\n+\tstruct adapter_info_s *p_adapter_info = &adap->drv->ntdrv.adapter_info;\n+\n+\tNT_LOG(DBG, NTCONNECT, \"Port %i: set loopback mode %i\\n\", portid, mode);\n+\tnt4ga_port_set_loopback_mode(p_adapter_info, portid, mode);\n+\treturn REQUEST_OK;\n+}\n+\n+static int set_tx_power(struct adap_hdl_s *adap, int portid, bool disable,\n+\t\t\tchar **data, int *len)\n+{\n+\tstruct adapter_info_s *p_adapter_info = &adap->drv->ntdrv.adapter_info;\n+\n+\tNT_LOG(DBG, NTCONNECT, \"Port %i: set tx_power %i\\n\", portid, disable);\n+\tif (nt4ga_port_tx_power(p_adapter_info, portid, disable)) {\n+\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t       \"Port %i: ERROR while changing tx_power\\n\", portid);\n+\t\treturn ntconn_reply_status(data, len,\n+\t\t\t\t\t   NTCONN_ADAPTER_ERR_TX_POWER_FAIL);\n+\t}\n+\treturn REQUEST_OK;\n+}\n+\n+static int func_adapter_set_interface(void *hdl, int client_id _unused,\n+\t\t\t\t      struct ntconn_header_s *hdr _unused,\n+\t\t\t\t      char **data, int *len)\n+{\n+\tstruct adap_hdl_s *adap = (struct adap_hdl_s *)hdl;\n+\tchar *saveptr;\n+\tint port_nr;\n+\tint length;\n+\tchar *tok;\n+\n+\t*len = 0;\n+\n+\t/*\n+\t * This will receive the request strings starting with \"adapter;set,interface,....\"\n+\t * so in the situation of a request like: \"adapter,set,interface,port0,link_speed=10G\"\n+\t * the remainder of the command \"port0,link_speed=10G\" will be pointed to by *data,\n+\t * zero-terminated on entry\n+\t */\n+\n+\tif (!(data && *data))\n+\t\treturn ntconn_error(data, len, this_module_name,\n+\t\t\t\t    NTCONN_ERR_CODE_INVALID_REQUEST);\n+\n+\t/* OK to modify *data */\n+\ttok = strtok_r(*data, \",\", &saveptr);\n+\tif (!tok)\n+\t\treturn ntconn_error(data, len, this_module_name,\n+\t\t\t\t    NTCONN_ERR_CODE_MISSING_INVALID_PARAM);\n+\n+\tlength = strlen(tok);\n+\n+\tif (!(length > 4 && memcmp(tok, \"port\", 4) == 0))\n+\t\treturn ntconn_error(data, len, this_module_name,\n+\t\t\t\t    NTCONN_ERR_CODE_MISSING_INVALID_PARAM);\n+\n+\tport_nr = atoi(tok + 4);\n+\n+\t/* Only set on phy ports */\n+\tif (port_nr < adap->drv->ntdrv.adapter_info.fpga_info.n_phy_ports)\n+\t\treturn ntconn_error(data, len, this_module_name,\n+\t\t\t\t    NTCONN_ERR_CODE_MISSING_INVALID_PARAM);\n+\n+\ttok = strtok_r(NULL, \"=,\", &saveptr);\n+\tif (!tok)\n+\t\treturn ntconn_error(data, len, this_module_name,\n+\t\t\tNTCONN_ERR_CODE_MISSING_INVALID_PARAM);\n+\tif (strcmp(tok, \"link_speed\") == 0) {\n+\t\ttok = strtok_r(NULL, \",\", &saveptr);\n+\t\tif (!tok)\n+\t\t\treturn ntconn_error(data, len, this_module_name,\n+\t\t\t\tNTCONN_ERR_CODE_MISSING_INVALID_PARAM);\n+\t\treturn set_link_speed(adap, port_nr, tok, data, len);\n+\t} else if (strcmp(tok, \"enable\") == 0) {\n+\t\treturn set_port_enable(adap, port_nr);\n+\t} else if (strcmp(tok, \"disable\") == 0) {\n+\t\treturn set_port_disable(adap, port_nr);\n+\t} else if (strcmp(tok, \"link_state\") == 0) {\n+\t\ttok = strtok_r(NULL, \",\", &saveptr);\n+\t\tif (!tok)\n+\t\t\treturn ntconn_error(data, len, this_module_name,\n+\t\t\t\tNTCONN_ERR_CODE_MISSING_INVALID_PARAM);\n+\t\tif (strcmp(tok, \"up\") == 0)\n+\t\t\treturn set_link_up(adap, port_nr);\n+\t\telse if (strcmp(tok, \"down\") == 0)\n+\t\t\treturn set_link_down(adap, port_nr);\n+\t} else if (strcmp(tok, \"host_loopback\") == 0) {\n+\t\ttok = strtok_r(NULL, \",\", &saveptr);\n+\t\tif (!tok)\n+\t\t\treturn ntconn_error(data, len, this_module_name,\n+\t\t\t\tNTCONN_ERR_CODE_MISSING_INVALID_PARAM);\n+\t\tif (strcmp(tok, \"on\") == 0)\n+\t\t\treturn set_loopback_mode(adap, port_nr,\n+\t\t\t\tNT_LINK_LOOPBACK_HOST);\n+\t\telse if (strcmp(tok, \"off\") == 0)\n+\t\t\treturn set_loopback_mode(adap, port_nr,\n+\t\t\t\tNT_LINK_LOOPBACK_OFF);\n+\t} else if (strcmp(tok, \"line_loopback\") == 0) {\n+\t\ttok = strtok_r(NULL, \",\", &saveptr);\n+\t\tif (!tok)\n+\t\t\treturn ntconn_error(data, len, this_module_name,\n+\t\t\t\tNTCONN_ERR_CODE_MISSING_INVALID_PARAM);\n+\t\tif (strcmp(tok, \"on\") == 0)\n+\t\t\treturn set_loopback_mode(adap, port_nr,\n+\t\t\t\tNT_LINK_LOOPBACK_LINE);\n+\t\telse if (strcmp(tok, \"off\") == 0)\n+\t\t\treturn set_loopback_mode(adap, port_nr,\n+\t\t\t\tNT_LINK_LOOPBACK_OFF);\n+\t} else if (strcmp(tok, \"tx_power\") == 0) {\n+\t\ttok = strtok_r(NULL, \",\", &saveptr);\n+\t\tif (!tok)\n+\t\t\treturn ntconn_error(data, len, this_module_name,\n+\t\t\t\tNTCONN_ERR_CODE_MISSING_INVALID_PARAM);\n+\t\tif (strcmp(tok, \"on\") == 0)\n+\t\t\treturn set_tx_power(adap, port_nr, false, data, len);\n+\t\telse if (strcmp(tok, \"off\") == 0)\n+\t\t\treturn set_tx_power(adap, port_nr, true, data, len);\n+\t}\n+\n+\t/* Should return 0 on success */\n+\treturn ntconn_error(data, len, this_module_name,\n+\t\t\t    NTCONN_ERR_CODE_MISSING_INVALID_PARAM);\n+}\n+\n+static int func_adapter_set_adapter(void *hdl _unused, int client_id _unused,\n+\t\t\t\t    struct ntconn_header_s *hdr _unused,\n+\t\t\t\t    char **data, int *len)\n+{\n+\tif (data && *data) {\n+\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t       \"Set adapter: Command: %s\\n\", *data);\n+\t}\n+\n+\t*len = 0;\n+\n+\t/* Should return 0 on success */\n+\treturn ntconn_error(data, len, this_module_name,\n+\t\t\t    NTCONN_ERR_CODE_NOT_YET_IMPLEMENTED);\n+}\n+\n+static int adap_request(void *hdl, int client_id _unused,\n+\t\t\tstruct ntconn_header_s *hdr, char *function,\n+\t\t\tchar **data, int *len)\n+{\n+\treturn execute_function(this_module_name, hdl, client_id, hdr, function,\n+\t\t\t\tadapter_entry_funcs, data, len, 0);\n+}\n+\n+static void adap_free_data(void *hdl _unused, char *data)\n+{\n+\tfree(data);\n+}\n+\n+static void adap_client_cleanup(void *hdl _unused, int client_id _unused)\n+{\n+\t/* Nothing to do */\n+}\n+\n+static const ntconnapi_t ntconn_adap_op = { this_module_name,\n+\t\t\t\t\t    NTCONN_ADAP_VERSION_MAJOR,\n+\t\t\t\t\t    NTCONN_ADAP_VERSION_MINOR,\n+\t\t\t\t\t    adap_request,\n+\t\t\t\t\t    adap_free_data,\n+\t\t\t\t\t    adap_client_cleanup\n+\t\t\t\t\t  };\n+\n+int ntconn_adap_register(struct drv_s *drv)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < MAX_ADAPTERS; i++) {\n+\t\tif (adap_hdl[i].drv == NULL)\n+\t\t\tbreak;\n+\t}\n+\tif (i == MAX_ADAPTERS) {\n+\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t       \"Cannot register more adapters into NtConnect framework\");\n+\t\treturn -1;\n+\t}\n+\n+\tadap_hdl[i].drv = drv;\n+\treturn register_ntconn_mod(&drv->p_dev->addr, (void *)&adap_hdl[i],\n+\t\t\t\t   &ntconn_adap_op);\n+}\ndiff --git a/drivers/net/ntnic/ntconnect_modules/ntconn_flow.c b/drivers/net/ntnic/ntconnect_modules/ntconn_flow.c\nnew file mode 100644\nindex 0000000000..31d5dc3edc\n--- /dev/null\n+++ b/drivers/net/ntnic/ntconnect_modules/ntconn_flow.c\n@@ -0,0 +1,1310 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include <errno.h>\n+#include \"ntnic_ethdev.h\"\n+#include \"ntconnect.h\"\n+#include \"ntos_system.h\"\n+#include \"ntconn_modules.h\"\n+#include \"ntconn_mod_helper.h\"\n+#include \"nt_util.h\"\n+#include \"ntlog.h\"\n+#include \"ntnic_vf_vdpa.h\"\n+\n+#include \"ntconnect_api_flow.h\"\n+#include \"ntconnect_api_meter.h\"\n+#include \"stream_binary_flow_api.h\"\n+\n+#include <rte_errno.h>\n+#include \"flow_api.h\"\n+\n+#define DEBUG_FLOW 1\n+\n+#define NTCONN_FLOW_VERSION_MAJOR 0U\n+#define NTCONN_FLOW_VERSION_MINOR 1U\n+\n+#define this_module_name \"filter\"\n+\n+#define MAX_CLIENTS 32\n+\n+#define UNUSED __rte_unused\n+\n+static struct flow_hdl_s {\n+\tstruct drv_s *drv;\n+} flow_hdl[MAX_CLIENTS];\n+\n+#define MAX_PORTS 64\n+static struct port_to_eth_s {\n+\tstruct flow_eth_dev *flw_dev;\n+\tuint32_t forced_vlan_vid;\n+\tuint32_t caller_id;\n+} port_eth[MAX_PORTS];\n+\n+static ntconn_err_t ntconn_err[] = {\n+\t{ NTCONN_FLOW_ERR_NONE, \"Success\" },\n+\t{ NTCONN_FLOW_ERR_INTERNAL_ERROR, \"Internal error\" },\n+\t{ NTCONN_FLOW_ERR_PORT_IS_NOT_INITIALIZED, \"Port is not initialized\" },\n+\t{ NTCONN_FLOW_ERR_UNEXPECTED_VIRTIO_PATH, \"Unexpected virtio path\" },\n+\t{ NTCONN_FLOW_ERR_TO_MANY_FLOWS, \"To many flows\" },\n+\t{ NTCONN_FLOW_ERR_INVALID_PORT, \"Invalid port\" },\n+\t{ NTCONN_FLOW_ERR_NOT_YET_IMPLEMENTED, \"Function not yet implemented\" },\n+\t{ NTCONN_FLOW_ERR_UNSUPPORTED_ADAPTER, \"Adapter is not supported\" },\n+\t{ NTCONN_FLOW_ERR_NO_VF_QUEUES, \"No queues for the VF is found\" },\n+\t{ -1, NULL }\n+};\n+\n+static const char *get_error_msg(enum ntconn_flow_err_e err_code)\n+{\n+\tint idx = 0;\n+\n+\twhile (ntconn_err[idx].err_code != (uint32_t)-1 &&\n+\t\t\tntconn_err[idx].err_code != err_code)\n+\t\tidx++;\n+\tif (ntconn_err[idx].err_code == (uint32_t)-1)\n+\t\tidx = 1;\n+\n+\treturn ntconn_err[idx].err_text;\n+}\n+\n+static inline int ntconn_flow_err_reply_status(char **data, int *len,\n+\t\tenum ntconn_flow_err_e code,\n+\t\tint err)\n+{\n+\t*data = malloc(sizeof(struct flow_return_s));\n+\tif (*data) {\n+\t\tstruct flow_return_s *return_value =\n+\t\t\t(struct flow_return_s *)*data;\n+\t\t*len = sizeof(struct flow_return_s);\n+\t\treturn_value->status = err;\n+\t\treturn_value->type = FLOW_ERROR_GENERAL;\n+\t\tconst char *err_msg = get_error_msg(code);\n+\n+\t\tmemcpy(return_value->err_msg, err_msg,\n+\t\t       RTE_MIN(strlen(err_msg), ERR_MSG_LEN));\n+\t\treturn REQUEST_OK;\n+\t}\n+\t*len = 0;\n+\tNT_LOG(ERR, NTCONNECT, \"Not able to allocate memory\");\n+\treturn REQUEST_ERR;\n+}\n+\n+static inline int ntconn_flow_err_status(char **data, int *len, int err)\n+{\n+\t*data = malloc(sizeof(struct flow_return_s));\n+\tif (*data) {\n+\t\tstruct flow_return_s *return_value =\n+\t\t\t(struct flow_return_s *)*data;\n+\t\t*len = sizeof(struct flow_return_s);\n+\t\treturn_value->status = err;\n+\t\treturn_value->type = FLOW_ERROR_GENERAL;\n+\t\tconst char *err_msg =\n+\t\t\tget_error_msg(NTCONN_FLOW_ERR_INTERNAL_ERROR);\n+\t\tstrlcpy(return_value->err_msg, err_msg, ERR_MSG_LEN);\n+\t\treturn REQUEST_OK;\n+\t}\n+\t*len = 0;\n+\tNT_LOG(ERR, NTCONNECT, \"Not able to allocate memory\");\n+\treturn REQUEST_ERR;\n+}\n+\n+/*\n+ * Filter functions\n+ */\n+static int func_flow_create(void *hdl, int client_id,\n+\t\t\t    struct ntconn_header_s *hdr, char **data, int *len);\n+static int func_flow_validate(void *hdl, int client_id,\n+\t\t\t      struct ntconn_header_s *hdr, char **data,\n+\t\t\t      int *len);\n+static int func_flow_destroy(void *hdl, int client_id,\n+\t\t\t     struct ntconn_header_s *hdr, char **data,\n+\t\t\t     int *len);\n+static int func_flow_flush(void *hdl, int client_id,\n+\t\t\t   struct ntconn_header_s *hdr, char **data, int *len);\n+static int func_flow_query(void *hdl, int client_id,\n+\t\t\t   struct ntconn_header_s *hdr, char **data, int *len);\n+static int func_flow_setport(void *hdl, int client_id,\n+\t\t\t     struct ntconn_header_s *hdr, char **data,\n+\t\t\t     int *len);\n+static struct func_s adapter_entry_funcs[] = {\n+\t{ \"setport\", NULL, func_flow_setport },\n+\t{ \"create\", NULL, func_flow_create },\n+\t{ \"validate\", NULL, func_flow_validate },\n+\t{ \"destroy\", NULL, func_flow_destroy },\n+\t{ \"flush\", NULL, func_flow_flush },\n+\t{ \"query\", NULL, func_flow_query },\n+\t{ NULL, NULL, NULL },\n+};\n+\n+static int copy_return_status(char **data, int *len, int status,\n+\t\t\t      struct flow_error *error)\n+{\n+\t*data = malloc(sizeof(struct flow_return_s));\n+\tif (*data) {\n+\t\tstruct flow_return_s *return_value =\n+\t\t\t(struct flow_return_s *)*data;\n+\t\t*len = sizeof(struct flow_return_s);\n+\n+\t\treturn_value->status = status;\n+\t\treturn_value->type = error->type;\n+\t\tstrlcpy(return_value->err_msg, error->message, ERR_MSG_LEN);\n+\t\treturn REQUEST_OK;\n+\t}\n+\t*len = 0;\n+\tNT_LOG(ERR, NTCONNECT, \"Not able to allocate memory %s\",\n+\t       __func__);\n+\treturn REQUEST_ERR;\n+}\n+\n+static void set_error(struct flow_error *error)\n+{\n+\terror->type = FLOW_ERROR_SUCCESS;\n+\terror->message = \"Operation successfully completed\";\n+}\n+\n+static int func_flow_setport(void *hdl _unused, int client_id _unused,\n+\t\t\t     struct ntconn_header_s *hdr _unused, char **data,\n+\t\t\t     int *len)\n+{\n+\tuint32_t i;\n+\tstruct flow_error error;\n+\tuint32_t nb_port;\n+\tuint8_t in_port = MAX_PORTS;\n+\tchar vpath[MAX_PATH_LEN];\n+\tchar *saveptr;\n+\n+\tset_error(&error);\n+\n+\tnb_port = rte_eth_dev_count_avail();\n+\n+#ifdef DEBUG_FLOW\n+\tNT_LOG(DBG, NTCONNECT, \"%s: \\\"%s\\\"\\n\", __func__, *data);\n+\tNT_LOG(DBG, NTCONNECT, \"Number of ports: %u\\n\", nb_port);\n+#endif\n+\n+\tchar *tok = strtok_r(*data, \",\", &saveptr);\n+\n+\tif (tok) {\n+\t\tint length = strlen(tok);\n+\n+\t\tif (length > 7 && memcmp(tok, \"in_port=\", 5) == 0)\n+\t\t\tin_port = atoi(tok + 7);\n+\t}\n+#ifdef DEBUG_FLOW\n+\tNT_LOG(DBG, NTCONNECT, \"in_port:          %u\\n\", in_port);\n+#endif\n+\n+\ttok = strtok_r(NULL, \",\", &saveptr);\n+\tif (tok) {\n+\t\tint length = strlen(tok);\n+\n+\t\tif (length > 6 && memcmp(tok, \"vpath=\", 6) == 0)\n+\t\t\tstrlcpy(vpath, tok + 6, MAX_PATH_LEN);\n+\t}\n+#ifdef DEBUG_FLOW\n+\tNT_LOG(DBG, NTCONNECT, \"vpath:           %s\\n\", vpath);\n+#endif\n+\n+\t/* Check that the wanted ports are valid ports */\n+\tif (in_port >= nb_port) {\n+\t\tNT_LOG(ERR, NTCONNECT, \"port out of range\");\n+\t\treturn ntconn_flow_err_status(data, len, ENODEV);\n+\t}\n+\n+\tstruct pmd_internals *vp_internals = vp_path_instance_ready(vpath);\n+\n+\tif (!vp_internals) {\n+\t\tNT_LOG(ERR, NTCONNECT, \"Failed to get VF device\");\n+\t\treturn ntconn_flow_err_status(data, len, ENODEV);\n+\t}\n+\n+\t/* Get flow device */\n+\tport_eth[in_port].flw_dev = vp_internals->flw_dev;\n+\n+\tif (port_eth[in_port].flw_dev == NULL) {\n+\t\tNT_LOG(ERR, NTCONNECT, \"Failed to get eth device\");\n+\t\treturn ntconn_flow_err_status(data, len, ENODEV);\n+\t}\n+\n+\t/* Only INLINE is supported */\n+\tif (vp_internals->flw_dev->ndev->flow_profile !=\n+\t\t\tFLOW_ETH_DEV_PROFILE_INLINE) {\n+\t\t/* Only inline profile is supported */\n+\t\tNT_LOG(ERR, NTCONNECT, \"Adapter is not supported\");\n+\t\treturn ntconn_flow_err_status(data, len, ENODEV);\n+\t}\n+\n+\tif (vp_internals->vpq_nb_vq == 0) {\n+\t\tNT_LOG(ERR, NTCONNECT, \"No queues for the VF is found\");\n+\t\treturn ntconn_flow_err_status(data, len, ENODEV);\n+\t}\n+\n+\t/* Server and client must agree of the virtual port number */\n+\tif (vp_internals->port != (in_port + 4U)) {\n+\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t       \"Internal error: Virtual port out of sync\");\n+\t\treturn ntconn_flow_err_status(data, len, ENODEV);\n+\t}\n+\n+#ifdef DEBUG_FLOW\n+\tNT_LOG(DBG, NTCONNECT, \"vport:           %u\\n\", vp_internals->port);\n+\tNT_LOG(DBG, NTCONNECT, \"vlan (forced):   %u\\n\", vp_internals->vlan);\n+#endif\n+\n+\tport_eth[in_port].caller_id = vp_internals->port;\n+\tport_eth[in_port].forced_vlan_vid = vp_internals->vlan;\n+\n+\t*data = malloc(sizeof(struct flow_setport_return));\n+\tif (*data) {\n+\t\tstruct flow_setport_return *return_value =\n+\t\t\t(struct flow_setport_return *)*data;\n+\t\t*len = sizeof(struct flow_setport_return);\n+\t\treturn_value->num_queues = vp_internals->vpq_nb_vq;\n+\n+#ifdef DEBUG_FLOW\n+\t\tNT_LOG(DBG, NTCONNECT, \"Number of queues: %u\\n\",\n+\t\t       vp_internals->vpq_nb_vq);\n+#endif\n+\t\tfor (i = 0; i < vp_internals->vpq_nb_vq && i < MAX_QUEUES;\n+\t\t\t\ti++) {\n+#ifdef DEBUG_FLOW\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"Queue:            %u\\n\",\n+\t\t\t       vp_internals->vpq[i].id);\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"HW ID:            %u\\n\",\n+\t\t\t       vp_internals->vpq[i].hw_id);\n+#endif\n+\t\t\treturn_value->queues[i].id = vp_internals->vpq[i].id;\n+\t\t\treturn_value->queues[i].hw_id =\n+\t\t\t\tvp_internals->vpq[i].hw_id;\n+#ifdef DEBUG_FLOW\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"Setup output port: %u, %04x:%02x:%02x.%x\\n\",\n+\t\t\t       in_port, vp_internals->pci_dev->addr.domain,\n+\t\t\t       vp_internals->pci_dev->addr.bus,\n+\t\t\t       vp_internals->pci_dev->addr.devid,\n+\t\t\t       vp_internals->pci_dev->addr.function);\n+#endif\n+\t\t}\n+\t\treturn REQUEST_OK;\n+\t}\n+\t*len = 0;\n+\treturn REQUEST_ERR;\n+}\n+\n+static int func_flow_flush(void *hdl _unused, int client_id _unused,\n+\t\t\t   struct ntconn_header_s *hdr _unused, char **data,\n+\t\t\t   int *len)\n+{\n+\tstruct flow_error error;\n+\tint port = MAX_PORTS;\n+\tint status = -1;\n+\tchar *saveptr;\n+\n+\tset_error(&error);\n+\n+#ifdef DEBUG_FLOW\n+\tNT_LOG(DBG, NTCONNECT, \"%s: [%s:%u] enter\\n\", __func__, __FILE__, __LINE__);\n+#endif\n+\n+\tchar *tok = strtok_r(*data, \",\", &saveptr);\n+\n+\tif (tok) {\n+\t\tint length = strlen(tok);\n+\n+\t\tif (length > 5 && memcmp(tok, \"port=\", 5) == 0)\n+\t\t\tport = atoi(tok + 5);\n+\t}\n+#ifdef DEBUG_FLOW\n+\tNT_LOG(DBG, NTCONNECT, \"Port id=%u\\n\", port);\n+#endif\n+\n+\tif (port >= MAX_PORTS) {\n+\t\t*len = 0;\n+\t\tNT_LOG(ERR, NTCONNECT, \"port id out of range\");\n+\t\treturn ntconn_flow_err_reply_status(data, len,\n+\t\t\t\t\t\t    NTCONN_FLOW_ERR_INVALID_PORT,\n+\t\t\t\t\t\t    ENODEV);\n+\t}\n+\n+\t/* Call filter with data */\n+\tstatus = flow_flush(port_eth[port].flw_dev, &error);\n+\treturn copy_return_status(data, len, status, &error);\n+}\n+\n+static int func_flow_destroy(void *hdl _unused, int client_id _unused,\n+\t\t\t     struct ntconn_header_s *hdr, char **data, int *len)\n+{\n+\tstruct flow_error error;\n+\tint port = MAX_PORTS;\n+\tuint64_t flow = 0;\n+\tint status = -1;\n+\n+\tstruct destroy_flow_ntconnect *flow_cpy =\n+\t\t(struct destroy_flow_ntconnect *)&(*data)[hdr->len];\n+\n+\tif (hdr->blob_len != sizeof(struct destroy_flow_ntconnect)) {\n+\t\t*len = 0;\n+\t\tNT_LOG(ERR, NTCONNECT, \"Error in filter data\");\n+\t\treturn ntconn_error(data, len, this_module_name,\n+\t\t\t\t    NTCONN_ERR_CODE_INVALID_REQUEST);\n+\t}\n+\n+#ifdef DEBUG_FLOW1\n+\tNT_LOG(DBG, NTCONNECT, \"%s: [%s:%u] enter\\n\", __func__, __FILE__, __LINE__);\n+#endif\n+\n+\tport = flow_cpy->port;\n+\n+#ifdef DEBUG_FLOW1\n+\tNT_LOG(DBG, NTCONNECT, \"Port id=%u\\n\", port);\n+#endif\n+\n+\tif (port >= MAX_PORTS) {\n+\t\t*len = 0;\n+\t\tNT_LOG(ERR, NTCONNECT, \"port id out of range\");\n+\t\treturn ntconn_flow_err_reply_status(data, len,\n+\t\t\t\t\t\t    NTCONN_FLOW_ERR_INVALID_PORT,\n+\t\t\t\t\t\t    ENODEV);\n+\t}\n+\n+\tflow = flow_cpy->flow;\n+\n+#ifdef DEBUG_FLOW1\n+\tNT_LOG(DBG, NTCONNECT, \"flow=0x%016llX\\n\",\n+\t       (unsigned long long)flow);\n+#endif\n+\n+\t/* Call filter with data */\n+\tstatus = flow_destroy(port_eth[port].flw_dev,\n+\t\t\t      (struct flow_handle *)flow, &error);\n+\n+\t*data = malloc(sizeof(struct flow_return_s));\n+\tif (*data) {\n+\t\tstruct flow_return_s *return_value =\n+\t\t\t(struct flow_return_s *)*data;\n+\t\t*len = sizeof(struct flow_return_s);\n+\n+\t\treturn_value->status = status;\n+\t\treturn_value->type = error.type;\n+\t\tstrlcpy(return_value->err_msg, error.message, ERR_MSG_LEN);\n+\t\treturn REQUEST_OK;\n+\t}\n+\t*len = 0;\n+\tNT_LOG(ERR, NTCONNECT, \"Not able to allocate memory %s\",\n+\t       __func__);\n+\treturn REQUEST_ERR;\n+}\n+\n+enum {\n+\tFLOW_API_FUNC_CREATE,\n+\tFLOW_API_FUNC_VALIDATE,\n+};\n+\n+static uint64_t make_flow_create(int func, int port,\n+\t\t\t\t struct create_flow_ntconnect *flow_cpy,\n+\t\t\t\t int *status, struct flow_error *error)\n+{\n+\tstruct flow_elem elem[MAX_FLOW_STREAM_ELEM];\n+\tstruct flow_action action[MAX_FLOW_STREAM_ELEM];\n+\tstruct flow_action_vxlan_encap vxlan_tun;\n+\tstruct flow_action_raw_encap encap;\n+\tstruct flow_action_raw_decap decap;\n+\tstruct flow_elem elem_tun[MAX_FLOW_STREAM_VXLAN_TUN_ELEM];\n+\tint idx = -1;\n+\n+\tstruct flow_attr *attr = &flow_cpy->attr;\n+\tstruct flow_elem_cpy *elem_cpy = flow_cpy->elem;\n+\tstruct flow_action_cpy *action_cpy = flow_cpy->action;\n+\n+\terror->type = FLOW_ERROR_GENERAL;\n+\terror->message = \"To many flows\";\n+\t*status = NTCONN_FLOW_ERR_TO_MANY_FLOWS;\n+\n+\tattr->caller_id = port_eth[port].caller_id;\n+\tattr->forced_vlan_vid = port_eth[port].forced_vlan_vid;\n+\n+\tdo {\n+\t\tidx++;\n+\t\tif (idx > MAX_FLOW_STREAM_ELEM)\n+\t\t\tgoto error;\n+\t\telem[idx].type = elem_cpy[idx].type;\n+\t\tif (!elem_cpy[idx].spec_cpy.valid) {\n+\t\t\telem[idx].spec = NULL;\n+\t\t} else {\n+\t\t\telem[idx].spec =\n+\t\t\t\t(void *)&elem_cpy[idx].spec_cpy.u.start_addr;\n+\t\t}\n+\t\tif (!elem_cpy[idx].mask_cpy.valid) {\n+\t\t\telem[idx].mask = NULL;\n+\t\t} else {\n+\t\t\telem[idx].mask =\n+\t\t\t\t(void *)&elem_cpy[idx].mask_cpy.u.start_addr;\n+\t\t}\n+\t} while (elem_cpy[idx].type != FLOW_ELEM_TYPE_END);\n+\n+\tidx = -1;\n+\tdo {\n+\t\tidx++;\n+\t\tif (idx > MAX_FLOW_STREAM_ELEM)\n+\t\t\tgoto error;\n+\t\taction[idx].type = action_cpy[idx].type;\n+\t\tif (!action_cpy[idx].conf_cpy.valid) {\n+\t\t\taction[idx].conf = NULL;\n+\t\t} else {\n+\t\t\tswitch (action_cpy[idx].type) {\n+\t\t\tcase FLOW_ACTION_TYPE_VXLAN_ENCAP: {\n+\t\t\t\t/*\n+\t\t\t\t * Special VXLAN ENCAP treatment create inner tunnel\n+\t\t\t\t * elements in action\n+\t\t\t\t */\n+\t\t\t\tstruct flow_elem_cpy *tun_elem_cpy =\n+\t\t\t\t\t(struct flow_elem_cpy *)action_cpy[idx]\n+\t\t\t\t\t.conf_cpy.u.vxlan.vxlan_tunnel;\n+\t\t\t\tvxlan_tun.vxlan_tunnel = elem_tun;\n+\t\t\t\tint tun_idx = -1;\n+\n+\t\t\t\tdo {\n+\t\t\t\t\ttun_idx++;\n+\t\t\t\t\tif (tun_idx >\n+\t\t\t\t\t\t\tMAX_FLOW_STREAM_VXLAN_TUN_ELEM) {\n+\t\t\t\t\t\terror->message =\n+\t\t\t\t\t\t\t\"To many VXLAN tunnels\";\n+\t\t\t\t\t\tgoto error;\n+\t\t\t\t\t}\n+\t\t\t\t\telem_tun[tun_idx].type =\n+\t\t\t\t\t\ttun_elem_cpy[tun_idx].type;\n+\t\t\t\t\tif (!tun_elem_cpy[tun_idx]\n+\t\t\t\t\t\t\t.spec_cpy.valid) {\n+\t\t\t\t\t\telem_tun[tun_idx].spec = NULL;\n+\t\t\t\t\t} else {\n+\t\t\t\t\t\telem_tun[tun_idx].spec =\n+\t\t\t\t\t\t\t(void *)&tun_elem_cpy[tun_idx]\n+\t\t\t\t\t\t\t.spec_cpy.u\n+\t\t\t\t\t\t\t.start_addr;\n+\t\t\t\t\t}\n+\t\t\t\t\tif (!tun_elem_cpy[tun_idx]\n+\t\t\t\t\t\t\t.mask_cpy.valid) {\n+\t\t\t\t\t\telem_tun[tun_idx].mask = NULL;\n+\t\t\t\t\t} else {\n+\t\t\t\t\t\telem_tun[tun_idx].mask =\n+\t\t\t\t\t\t\t(void *)&tun_elem_cpy[tun_idx]\n+\t\t\t\t\t\t\t.mask_cpy.u\n+\t\t\t\t\t\t\t.start_addr;\n+\t\t\t\t\t}\n+\t\t\t\t} while (tun_elem_cpy[tun_idx].type !=\n+\t\t\t\t\t\tFLOW_ELEM_TYPE_END);\n+\t\t\t\t/* VXLAN ENCAP tunnel finished */\n+\t\t\t\taction[idx].conf = &vxlan_tun;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t\tcase FLOW_ACTION_TYPE_RSS: {\n+\t\t\t\t/* Need to set queue pointer */\n+\t\t\t\taction_cpy[idx].conf_cpy.u.rss.rss.queue =\n+\t\t\t\t\t(const uint16_t *)&action_cpy[idx]\n+\t\t\t\t\t.conf_cpy.u.rss.cpy_queue;\n+\t\t\t\taction[idx].conf = (void *)&action_cpy[idx]\n+\t\t\t\t\t\t   .conf_cpy.u.rss.rss;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t\tcase FLOW_ACTION_TYPE_METER: {\n+\t\t\t\t/* Need to convert meter ID to uniq ID for the VF */\n+\t\t\t\taction_cpy[idx].conf_cpy.u.meter.mtr_id =\n+\t\t\t\t\t((flow_mtr_meters_supported() /\n+\t\t\t\t\t  (RTE_MAX_ETHPORTS - 2)) *\n+\t\t\t\t\t (flow_cpy->vport - 4)) +\n+\t\t\t\t\taction_cpy[idx].conf_cpy.u.meter.mtr_id;\n+\t\t\t\taction[idx].conf = (void *)&action_cpy[idx]\n+\t\t\t\t\t\t   .conf_cpy.u.meter;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t\tcase FLOW_ACTION_TYPE_RAW_ENCAP: {\n+\t\t\t\tencap.preserve = NULL;\n+\t\t\t\tencap.data =\n+\t\t\t\t\taction_cpy[idx].conf_cpy.u.encap.data;\n+\t\t\t\tencap.item_count =\n+\t\t\t\t\taction_cpy[idx]\n+\t\t\t\t\t.conf_cpy.u.encap.item_count;\n+\t\t\t\tencap.size =\n+\t\t\t\t\taction_cpy[idx].conf_cpy.u.encap.size;\n+\n+\t\t\t\tfor (int eidx = 0;\n+\t\t\t\t\t\teidx <\n+\t\t\t\t\t\taction_cpy[idx].conf_cpy.u.encap.item_count;\n+\t\t\t\t\t\teidx++) {\n+\t\t\t\t\tif (eidx > RAW_ENCAP_DECAP_ELEMS_MAX) {\n+\t\t\t\t\t\terror->message =\n+\t\t\t\t\t\t\t\"To many encap items\";\n+\t\t\t\t\t\tgoto error;\n+\t\t\t\t\t}\n+\t\t\t\t\tencap.items[eidx].type =\n+\t\t\t\t\t\taction_cpy[idx]\n+\t\t\t\t\t\t.conf_cpy.u.encap\n+\t\t\t\t\t\t.item_cpy[eidx]\n+\t\t\t\t\t\t.type;\n+\t\t\t\t\tif (action_cpy[idx]\n+\t\t\t\t\t\t\t.conf_cpy.u.encap\n+\t\t\t\t\t\t\t.item_cpy[eidx]\n+\t\t\t\t\t\t\t.spec_cpy.valid) {\n+\t\t\t\t\t\tencap.items[eidx].spec =\n+\t\t\t\t\t\t\t(void *)&action_cpy[idx]\n+\t\t\t\t\t\t\t.conf_cpy.u\n+\t\t\t\t\t\t\t.encap\n+\t\t\t\t\t\t\t.item_cpy[eidx]\n+\t\t\t\t\t\t\t.spec_cpy.u\n+\t\t\t\t\t\t\t.start_addr;\n+\t\t\t\t\t} else {\n+\t\t\t\t\t\tencap.items[eidx].spec = NULL;\n+\t\t\t\t\t}\n+\t\t\t\t\tif (action_cpy[idx]\n+\t\t\t\t\t\t\t.conf_cpy.u.encap\n+\t\t\t\t\t\t\t.item_cpy[eidx]\n+\t\t\t\t\t\t\t.mask_cpy.valid) {\n+\t\t\t\t\t\tencap.items[eidx].mask =\n+\t\t\t\t\t\t\t(void *)&action_cpy[idx]\n+\t\t\t\t\t\t\t.conf_cpy.u\n+\t\t\t\t\t\t\t.encap\n+\t\t\t\t\t\t\t.item_cpy[eidx]\n+\t\t\t\t\t\t\t.mask_cpy.u\n+\t\t\t\t\t\t\t.start_addr;\n+\t\t\t\t\t} else {\n+\t\t\t\t\t\tencap.items[eidx].mask = NULL;\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t\taction[idx].conf = &encap;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t\tcase FLOW_ACTION_TYPE_RAW_DECAP: {\n+\t\t\t\tdecap.data =\n+\t\t\t\t\taction_cpy[idx].conf_cpy.u.decap.data;\n+\t\t\t\tdecap.item_count =\n+\t\t\t\t\taction_cpy[idx]\n+\t\t\t\t\t.conf_cpy.u.decap.item_count;\n+\t\t\t\tdecap.size =\n+\t\t\t\t\taction_cpy[idx].conf_cpy.u.decap.size;\n+\n+\t\t\t\tfor (int eidx = 0;\n+\t\t\t\t\t\teidx <\n+\t\t\t\t\t\taction_cpy[idx].conf_cpy.u.decap.item_count;\n+\t\t\t\t\t\teidx++) {\n+\t\t\t\t\tif (eidx > RAW_ENCAP_DECAP_ELEMS_MAX) {\n+\t\t\t\t\t\terror->message =\n+\t\t\t\t\t\t\t\"To many decap items\";\n+\t\t\t\t\t\tgoto error;\n+\t\t\t\t\t}\n+\t\t\t\t\tdecap.items[eidx].type =\n+\t\t\t\t\t\taction_cpy[idx]\n+\t\t\t\t\t\t.conf_cpy.u.decap\n+\t\t\t\t\t\t.item_cpy[eidx]\n+\t\t\t\t\t\t.type;\n+\t\t\t\t\tif (action_cpy[idx]\n+\t\t\t\t\t\t\t.conf_cpy.u.decap\n+\t\t\t\t\t\t\t.item_cpy[eidx]\n+\t\t\t\t\t\t\t.spec_cpy.valid) {\n+\t\t\t\t\t\tdecap.items[eidx].spec =\n+\t\t\t\t\t\t\t(void *)&action_cpy[idx]\n+\t\t\t\t\t\t\t.conf_cpy.u\n+\t\t\t\t\t\t\t.decap\n+\t\t\t\t\t\t\t.item_cpy[eidx]\n+\t\t\t\t\t\t\t.spec_cpy.u\n+\t\t\t\t\t\t\t.start_addr;\n+\t\t\t\t\t} else {\n+\t\t\t\t\t\tdecap.items[eidx].spec = NULL;\n+\t\t\t\t\t}\n+\t\t\t\t\tif (action_cpy[idx]\n+\t\t\t\t\t\t\t.conf_cpy.u.decap\n+\t\t\t\t\t\t\t.item_cpy[eidx]\n+\t\t\t\t\t\t\t.mask_cpy.valid) {\n+\t\t\t\t\t\tdecap.items[eidx].mask =\n+\t\t\t\t\t\t\t(void *)&action_cpy[idx]\n+\t\t\t\t\t\t\t.conf_cpy.u\n+\t\t\t\t\t\t\t.decap\n+\t\t\t\t\t\t\t.item_cpy[eidx]\n+\t\t\t\t\t\t\t.mask_cpy.u\n+\t\t\t\t\t\t\t.start_addr;\n+\t\t\t\t\t} else {\n+\t\t\t\t\t\tdecap.items[eidx].mask = NULL;\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t\taction[idx].conf = &decap;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t\tdefault: {\n+\t\t\t\t/* Move conf pointer into conf_cpy data field */\n+\t\t\t\taction[idx].conf =\n+\t\t\t\t\t(void *)&action_cpy[idx]\n+\t\t\t\t\t.conf_cpy.u.start_addr;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t} while (action_cpy[idx].type != FLOW_ACTION_TYPE_END);\n+\n+\t*status = NTCONN_FLOW_ERR_NONE;\n+\tif (func == FLOW_API_FUNC_VALIDATE) {\n+\t\t*status = flow_validate(port_eth[port].flw_dev, elem, action,\n+\t\t\t\t\terror);\n+\t\treturn 0ULL;\n+\t} else {\n+\t\treturn (uint64_t)flow_create(port_eth[port].flw_dev, attr, elem,\n+\t\t\t\t\t     action, error);\n+\t}\n+\n+error:\n+\treturn 0;\n+}\n+\n+static int func_flow_create(void *hdl _unused, int client_id _unused,\n+\t\t\t    struct ntconn_header_s *hdr, char **data, int *len)\n+{\n+\tint status;\n+\tstruct flow_error error;\n+\tuint64_t flow = 0UL;\n+\tint port = MAX_PORTS;\n+\n+\tstruct create_flow_ntconnect *flow_cpy =\n+\t\t(struct create_flow_ntconnect *)&(*data)[hdr->len];\n+\n+\tif (hdr->blob_len != sizeof(struct create_flow_ntconnect)) {\n+\t\t*len = 0;\n+\t\tNT_LOG(ERR, NTCONNECT, \"Error in filter data\");\n+\t\treturn ntconn_error(data, len, this_module_name,\n+\t\t\t\t    NTCONN_ERR_CODE_INVALID_REQUEST);\n+\t}\n+\n+\tport = flow_cpy->port;\n+\n+\tif (port >= MAX_PORTS) {\n+\t\tNT_LOG(ERR, NTCONNECT, \"port id out of range\");\n+\t\treturn ntconn_flow_err_reply_status(data, len,\n+\t\t\t\t\t\t    NTCONN_FLOW_ERR_INVALID_PORT,\n+\t\t\t\t\t\t    ENODEV);\n+\t}\n+\n+#ifdef DEBUG_PARSING\n+\tint i;\n+\n+\tfor (i = 0; i < MAX_FLOW_STREAM_ELEM; i++) {\n+\t\tif (flow_cpy[i].elem[i].type == FLOW_ELEM_TYPE_END) {\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_END\\n\");\n+\t\t\tbreak;\n+\t\t}\n+\t\tswitch (flow_cpy->elem[i].type) {\n+\t\tcase FLOW_ELEM_TYPE_IPV4:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_IPV4 %i\\n\", i);\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"     src_ip:   %u.%u.%u.%u\\n\",\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.spec_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.spec_cpy.u.ipv4.hdr.src_ip)[1] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.spec_cpy.u.ipv4.hdr.src_ip)[2] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.spec_cpy.u.ipv4.hdr.src_ip)[3] & 0xFF);\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"     dst_ip:   %u.%u.%u.%u\\n\",\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.spec_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.spec_cpy.u.ipv4.hdr.dst_ip)[1] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.spec_cpy.u.ipv4.hdr.dst_ip)[2] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.spec_cpy.u.ipv4.hdr.dst_ip)[3] & 0xFF);\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"     src_mask: %u.%u.%u.%u\\n\",\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.mask_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.mask_cpy.u.ipv4.hdr.src_ip)[1] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.mask_cpy.u.ipv4.hdr.src_ip)[2] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.mask_cpy.u.ipv4.hdr.src_ip)[3] & 0xFF);\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"     dst_mask: %u.%u.%u.%u\\n\",\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.mask_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.mask_cpy.u.ipv4.hdr.dst_ip)[1] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.mask_cpy.u.ipv4.hdr.dst_ip)[2] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.mask_cpy.u.ipv4.hdr.dst_ip)[3] & 0xFF);\n+\t\t\tbreak;\n+\t\tcase FLOW_ELEM_TYPE_ETH:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_ETH %i\\n\", i);\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"     src mac:  %02X:%02X:%02X:%02X:%02X:%02X\\n\",\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[0] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[1] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[2] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[3] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[4] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[5] & 0xFF);\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"     dst mac:  %02X:%02X:%02X:%02X:%02X:%02X\\n\",\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[0] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[1] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[2] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[3] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[4] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[5] & 0xFF);\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"     src mask  %02X:%02X:%02X:%02X:%02X:%02X\\n\",\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[0] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[1] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[2] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[3] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[4] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[5] & 0xFF);\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"     dst mask  %02X:%02X:%02X:%02X:%02X:%02X\\n\",\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[0] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[1] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[2] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[3] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[4] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[5] & 0xFF);\n+\t\t\tbreak;\n+\t\tcase FLOW_ELEM_TYPE_VLAN:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_VLAN %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ELEM_TYPE_IPV6:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_IPV6 %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ELEM_TYPE_SCTP:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_SCTP %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ELEM_TYPE_TCP:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_TCP %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ELEM_TYPE_UDP:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_UDP %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ELEM_TYPE_ICMP:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_ICMP %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ELEM_TYPE_VXLAN:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_VXLAN %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ELEM_TYPE_PORT_ID:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_PORT_ID %i\\n\",\n+\t\t\t       i);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"Unknown item %u\\n\",\n+\t\t\t       flow_cpy->elem[i].type);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tfor (i = 0; i < MAX_FLOW_STREAM_ELEM; i++) {\n+\t\tuint32_t j;\n+\n+\t\tif (flow_cpy->action[i].type == FLOW_ACTION_TYPE_END) {\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ACTION_TYPE_END\\n\");\n+\t\t\tbreak;\n+\t\t}\n+\t\tswitch (flow_cpy->action[i].type) {\n+\t\tcase FLOW_ACTION_TYPE_RSS:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ACTION_TYPE_RSS %i\\n\", i);\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"     queue nb: %u\\n\",\n+\t\t\t       flow_cpy->action[i].conf_cpy.u.rss.rss.queue_num);\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"     queue:    \");\n+\t\t\tfor (j = 0;\n+\t\t\t\t\tj < flow_cpy->action[i]\n+\t\t\t\t\t.conf_cpy.u.rss.rss.queue_num &&\n+\t\t\t\t\tj < FLOW_MAX_QUEUES;\n+\t\t\t\t\tj++) {\n+\t\t\t\tNT_LOG(DBG, NTCONNECT, \"%u \",\n+\t\t\t\t       flow_cpy->action[i]\n+\t\t\t\t       .conf_cpy.u.rss.cpy_queue[j]);\n+\t\t\t}\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"\\n\");\n+\t\t\tbreak;\n+\n+\t\tcase FLOW_ACTION_TYPE_POP_VLAN:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ACTION_TYPE_POP_VLAN %i\\n\",\n+\t\t\t       i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_PUSH_VLAN:\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"FLOW_ACTION_TYPE_PUSH_VLAN %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_SET_VLAN_VID:\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"FLOW_ACTION_TYPE_SET_VLAN_VID %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_SET_VLAN_PCP:\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"FLOW_ACTION_TYPE_SET_VLAN_PCP %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_VXLAN_DECAP:\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"FLOW_ACTION_TYPE_VXLAN_DECAP %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_VXLAN_ENCAP:\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"FLOW_ACTION_TYPE_VXLAN_ENCAP %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_DROP:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ACTION_TYPE_DROP %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_COUNT:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ACTION_TYPE_COUNT %i\\n\",\n+\t\t\t       i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_MARK:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ACTION_TYPE_MARK %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_PORT_ID:\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"FLOW_ACTION_TYPE_PORT_ID %i: ID=%u\\n\", i,\n+\t\t\t       flow_cpy->action[i].conf_cpy.u.port_id.id);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_QUEUE:\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"FLOW_ACTION_TYPE_QUEUE %i: queue=%u\\n\", i,\n+\t\t\t       flow_cpy->action[i].conf_cpy.u.queue.index);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_SET_TAG:\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"FLOW_ACTION_TYPE_SET_TAG %i: idx=%u, data=%u, mask=%X\\n\",\n+\t\t\t       i, flow_cpy->action[i].conf_cpy.u.tag.index,\n+\t\t\t       flow_cpy->action[i].conf_cpy.u.tag.data,\n+\t\t\t       flow_cpy->action[i].conf_cpy.u.tag.mask);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"Unknown action %u\\n\",\n+\t\t\t       flow_cpy->action[i].type);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+#endif\n+\n+\t/* Call filter with data */\n+\tflow = make_flow_create(FLOW_API_FUNC_CREATE, port, flow_cpy, &status,\n+\t\t\t\t&error);\n+\tif (flow) {\n+\t\t*data = malloc(sizeof(struct create_flow_return_s));\n+\t\tif (!*data)\n+\t\t\tgoto create_flow_error_malloc;\n+\t\tstruct create_flow_return_s *return_value =\n+\t\t\t(struct create_flow_return_s *)*data;\n+\t\t*len = sizeof(struct create_flow_return_s);\n+\t\treturn_value->flow = flow;\n+\t\treturn REQUEST_OK;\n+\t}\n+\n+\t*data = malloc(sizeof(struct flow_error_return_s));\n+\tif (!*data)\n+\t\tgoto create_flow_error_malloc;\n+\tstruct flow_error_return_s *return_value =\n+\t\t(struct flow_error_return_s *)*data;\n+\t*len = sizeof(struct flow_error_return_s);\n+\treturn_value->type = error.type;\n+\tstrlcpy(return_value->err_msg, error.message, ERR_MSG_LEN);\n+\treturn REQUEST_OK;\n+\n+create_flow_error_malloc:\n+\n+\t*len = 0;\n+\tNT_LOG(ERR, NTCONNECT, \"Not able to allocate memory %s\", __func__);\n+\treturn REQUEST_ERR;\n+}\n+\n+static int func_flow_validate(void *hdl _unused, int client_id _unused,\n+\t\t\t      struct ntconn_header_s *hdr, char **data,\n+\t\t\t      int *len)\n+{\n+\tint status;\n+\tstruct flow_error error;\n+\tint port = MAX_PORTS;\n+\n+\tstruct create_flow_ntconnect *flow_cpy =\n+\t\t(struct create_flow_ntconnect *)&(*data)[hdr->len];\n+\n+\tif (hdr->blob_len != sizeof(struct create_flow_ntconnect)) {\n+\t\t*len = 0;\n+\t\tNT_LOG(ERR, NTCONNECT, \"Error in filter data\");\n+\t\treturn ntconn_error(data, len, this_module_name,\n+\t\t\t\t    NTCONN_ERR_CODE_INVALID_REQUEST);\n+\t}\n+\n+\tset_error(&error);\n+\n+#ifdef DEBUG_FLOW\n+\tNT_LOG(DBG, NTCONNECT, \"func_flow_create\\n\");\n+#endif\n+\n+\tport = flow_cpy->port;\n+\n+#ifdef DEBUG_FLOW\n+\tNT_LOG(DBG, NTCONNECT, \"Port id=%u\\n\", port);\n+#endif\n+\n+\tif (port >= MAX_PORTS) {\n+\t\tNT_LOG(ERR, NTCONNECT, \"port id out of range\");\n+\t\treturn ntconn_flow_err_reply_status(data, len,\n+\t\t\tNTCONN_FLOW_ERR_INVALID_PORT, ENODEV);\n+\t}\n+\n+#ifdef DEBUG_PARSING\n+\tint i;\n+\n+\tfor (i = 0; i < MAX_FLOW_STREAM_ELEM; i++) {\n+\t\tif (flow_cpy[i].elem[i].type == FLOW_ELEM_TYPE_END) {\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_END\\n\");\n+\t\t\tbreak;\n+\t\t}\n+\t\tswitch (flow_cpy->elem[i].type) {\n+\t\tcase FLOW_ELEM_TYPE_IPV4:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_IPV4 %i\\n\", i);\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"     src_ip:   %u.%u.%u.%u\\n\",\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.spec_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.spec_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.spec_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.spec_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF);\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"     dst_ip:   %u.%u.%u.%u\\n\",\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.spec_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.spec_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.spec_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.spec_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF);\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"     src_mask: %u.%u.%u.%u\\n\",\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.mask_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.mask_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.mask_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.mask_cpy.u.ipv4.hdr.src_ip)[0] & 0xFF);\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"     dst_mask: %u.%u.%u.%u\\n\",\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.mask_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.mask_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.mask_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF,\n+\t\t\t\t((const char *)&flow_cpy->elem[i]\n+\t\t\t\t\t.mask_cpy.u.ipv4.hdr.dst_ip)[0] & 0xFF);\n+\t\t\tbreak;\n+\t\tcase FLOW_ELEM_TYPE_ETH:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_ETH %i\\n\", i);\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"     src mac:  %02X:%02X:%02X:%02X:%02X:%02X\\n\",\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[0] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[1] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[2] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[3] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[4] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.s_addr.addr_b[5] & 0xFF);\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"     dst mac:  %02X:%02X:%02X:%02X:%02X:%02X\\n\",\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[0] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[1] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[2] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[3] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[4] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].spec_cpy.u.eth.d_addr.addr_b[5] & 0xFF);\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"     src mask  %02X:%02X:%02X:%02X:%02X:%02X\\n\",\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[0] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[1] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[2] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[3] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[4] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.s_addr.addr_b[5] & 0xFF);\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"     dst mask  %02X:%02X:%02X:%02X:%02X:%02X\\n\",\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[0] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[1] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[2] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[3] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[4] & 0xFF,\n+\t\t\t       flow_cpy->elem[i].mask_cpy.u.eth.d_addr.addr_b[5] & 0xFF);\n+\t\t\tbreak;\n+\t\tcase FLOW_ELEM_TYPE_VLAN:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_VLAN %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ELEM_TYPE_IPV6:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_IPV6 %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ELEM_TYPE_SCTP:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_SCTP %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ELEM_TYPE_TCP:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_TCP %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ELEM_TYPE_UDP:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_UDP %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ELEM_TYPE_ICMP:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_ICMP %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ELEM_TYPE_VXLAN:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_VXLAN %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ELEM_TYPE_PORT_ID:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ELEM_TYPE_PORT_ID %i\\n\",\n+\t\t\t       i);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"Unknown item %u\\n\",\n+\t\t\t       flow_cpy->elem[i].type);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tfor (i = 0; i < MAX_FLOW_STREAM_ELEM; i++) {\n+\t\tuint32_t j;\n+\n+\t\tif (flow_cpy->action[i].type == FLOW_ACTION_TYPE_END) {\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ACTION_TYPE_END\\n\");\n+\t\t\tbreak;\n+\t\t}\n+\t\tswitch (flow_cpy->action[i].type) {\n+\t\tcase FLOW_ACTION_TYPE_RSS:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ACTION_TYPE_RSS %i\\n\", i);\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"     queue nb: %u\\n\",\n+\t\t\t       flow_cpy->action[i].conf_cpy.u.rss.rss.queue_num);\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"     queue:    \");\n+\t\t\tfor (j = 0;\n+\t\t\t\t\tj < flow_cpy->action[i]\n+\t\t\t\t\t.conf_cpy.u.rss.rss.queue_num &&\n+\t\t\t\t\tj < FLOW_MAX_QUEUES;\n+\t\t\t\t\tj++) {\n+\t\t\t\tNT_LOG(DBG, NTCONNECT, \"%u \",\n+\t\t\t\t       flow_cpy->action[i]\n+\t\t\t\t       .conf_cpy.u.rss.cpy_queue[j]);\n+\t\t\t}\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"\\n\");\n+\t\t\tbreak;\n+\n+\t\tcase FLOW_ACTION_TYPE_POP_VLAN:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ACTION_TYPE_POP_VLAN %i\\n\",\n+\t\t\t       i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_PUSH_VLAN:\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"FLOW_ACTION_TYPE_PUSH_VLAN %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_SET_VLAN_VID:\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"FLOW_ACTION_TYPE_SET_VLAN_VID %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_SET_VLAN_PCP:\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"FLOW_ACTION_TYPE_SET_VLAN_PCP %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_VXLAN_DECAP:\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"FLOW_ACTION_TYPE_VXLAN_DECAP %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_VXLAN_ENCAP:\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"FLOW_ACTION_TYPE_VXLAN_ENCAP %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_DROP:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ACTION_TYPE_DROP %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_COUNT:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ACTION_TYPE_COUNT %i\\n\",\n+\t\t\t       i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_MARK:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"FLOW_ACTION_TYPE_MARK %i\\n\", i);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_PORT_ID:\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"FLOW_ACTION_TYPE_PORT_ID %i: ID=%u\\n\", i,\n+\t\t\t       flow_cpy->action[i].conf_cpy.u.port_id.id);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_QUEUE:\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"FLOW_ACTION_TYPE_QUEUE %i: queue=%u\\n\", i,\n+\t\t\t       flow_cpy->action[i].conf_cpy.u.queue.index);\n+\t\t\tbreak;\n+\t\tcase FLOW_ACTION_TYPE_SET_TAG:\n+\t\t\tNT_LOG(DBG, NTCONNECT,\n+\t\t\t       \"FLOW_ACTION_TYPE_SET_TAG %i: idx=%u, data=%u, mask=%X\\n\",\n+\t\t\t       i, flow_cpy->action[i].conf_cpy.u.tag.index,\n+\t\t\t       flow_cpy->action[i].conf_cpy.u.tag.data,\n+\t\t\t       flow_cpy->action[i].conf_cpy.u.tag.mask);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tNT_LOG(DBG, NTCONNECT, \"Unknown action %u\\n\",\n+\t\t\t       flow_cpy->action[i].type);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+#endif\n+\n+\t/* Call filter with data */\n+\tmake_flow_create(FLOW_API_FUNC_VALIDATE, port, flow_cpy, &status,\n+\t\t\t &error);\n+\treturn copy_return_status(data, len, status, &error);\n+\n+\t/* Call filter with data */\n+\tmake_flow_create(FLOW_API_FUNC_VALIDATE, port, flow_cpy, &status,\n+\t\t\t &error);\n+\tif (!status) {\n+\t\t*data = malloc(sizeof(struct validate_flow_return_s));\n+\t\tif (!*data)\n+\t\t\tgoto validate_flow_error_malloc;\n+\t\tstruct validate_flow_return_s *return_value =\n+\t\t\t(struct validate_flow_return_s *)*data;\n+\t\t*len = sizeof(struct validate_flow_return_s);\n+\t\treturn_value->status = 0;\n+\t\treturn REQUEST_OK;\n+\t}\n+\n+\t*data = malloc(sizeof(struct flow_error_return_s));\n+\tif (!*data)\n+\t\tgoto validate_flow_error_malloc;\n+\tstruct flow_error_return_s *return_value =\n+\t\t(struct flow_error_return_s *)*data;\n+\t*len = sizeof(struct flow_error_return_s);\n+\treturn_value->type = error.type;\n+\tstrlcpy(return_value->err_msg, error.message, ERR_MSG_LEN);\n+\treturn_value->status = status;\n+\treturn REQUEST_OK;\n+\n+validate_flow_error_malloc:\n+\n+\t*len = 0;\n+\tNT_LOG(ERR, NTCONNECT, \"Not able to allocate memory %s\", __func__);\n+\treturn REQUEST_ERR;\n+}\n+\n+static int func_flow_query(void *hdl _unused, int client_id _unused,\n+\t\t\t   struct ntconn_header_s *hdr, char **data, int *len)\n+{\n+\tint status;\n+\tstruct flow_error error;\n+\tint port = MAX_PORTS;\n+\tstruct flow_handle *flow;\n+\n+\tstruct query_flow_ntconnect *flow_cpy =\n+\t\t(struct query_flow_ntconnect *)&(*data)[hdr->len];\n+\n+\tif (hdr->blob_len != sizeof(struct query_flow_ntconnect)) {\n+\t\t*len = 0;\n+\t\tNT_LOG(ERR, NTCONNECT, \"Error in filter data\");\n+\t\treturn ntconn_error(data, len, this_module_name,\n+\t\t\t\t    NTCONN_ERR_CODE_INVALID_REQUEST);\n+\t}\n+\n+\tset_error(&error);\n+\n+#ifdef DEBUG_FLOW\n+\tNT_LOG(DBG, NTCONNECT, \"%s: [%s:%u] enter\\n\", __func__, __FILE__, __LINE__);\n+#endif\n+\n+\tport = flow_cpy->port;\n+\n+#ifdef DEBUG_FLOW\n+\tNT_LOG(DBG, NTCONNECT, \"Port id=%u\\n\", port);\n+#endif\n+\n+\tif (port >= MAX_PORTS) {\n+\t\t*len = 0;\n+\t\tNT_LOG(ERR, NTCONNECT, \"port id out of range\");\n+\t\treturn ntconn_flow_err_reply_status(data, len,\n+\t\t\tNTCONN_FLOW_ERR_INVALID_PORT, ENODEV);\n+\t}\n+\n+#ifdef DEBUG_FLOW\n+\tNT_LOG(DBG, NTCONNECT, \"flow=0x%016llX\\n\",\n+\t       (unsigned long long)flow_cpy->flow);\n+#endif\n+\n+\tflow = (struct flow_handle *)flow_cpy->flow;\n+\n+\tconst struct flow_action action = {\n+\t\tflow_cpy->action.type,\n+\t\t(const void *)&flow_cpy->action.conf_cpy.u.count\n+\t};\n+\n+\t/* Call filter with data */\n+\tvoid *data_out = NULL;\n+\tuint32_t length = 0;\n+\n+\tstatus = flow_query(port_eth[port].flw_dev, flow, &action, &data_out,\n+\t\t\t    &length, &error);\n+\n+\t*data = malloc(sizeof(struct query_flow_return_s) + length);\n+\tif (*data) {\n+\t\tstruct query_flow_return_s *return_value =\n+\t\t\t(struct query_flow_return_s *)*data;\n+\t\t*len = sizeof(struct query_flow_return_s) + length;\n+\n+\t\treturn_value->status = status;\n+\t\treturn_value->type = error.type;\n+\t\tstrlcpy(return_value->err_msg, error.message, ERR_MSG_LEN);\n+\n+\t\tif (data_out) {\n+\t\t\tmemcpy(return_value->data, data_out, length);\n+\t\t\treturn_value->data_length = length;\n+\t\t\tfree(data_out);\n+\t\t} else {\n+\t\t\treturn_value->data_length = 0;\n+\t\t}\n+\t\treturn REQUEST_OK;\n+\t}\n+\t*len = 0;\n+\tNT_LOG(ERR, NTCONNECT, \"Not able to allocate memory %s\",\n+\t       __func__);\n+\treturn REQUEST_ERR;\n+}\n+\n+static int flow_request(void *hdl, int client_id _unused,\n+\t\t\tstruct ntconn_header_s *hdr, char *function,\n+\t\t\tchar **data, int *len)\n+{\n+\treturn execute_function(this_module_name, hdl, client_id, hdr, function,\n+\t\t\t\tadapter_entry_funcs, data, len, 0);\n+}\n+\n+static void flow_free_data(void *hdl _unused, char *data)\n+{\n+\tif (data)\n+\t\tfree(data);\n+}\n+\n+static void flow_client_cleanup(void *hdl _unused, int client_id _unused)\n+{\n+\t/* Nothing to do */\n+}\n+\n+static const ntconnapi_t ntconn_flow_op = { this_module_name,\n+\t\t\t\t\t    NTCONN_FLOW_VERSION_MAJOR,\n+\t\t\t\t\t    NTCONN_FLOW_VERSION_MINOR,\n+\t\t\t\t\t    flow_request,\n+\t\t\t\t\t    flow_free_data,\n+\t\t\t\t\t    flow_client_cleanup\n+\t\t\t\t\t  };\n+\n+int ntconn_flow_register(struct drv_s *drv)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < MAX_CLIENTS; i++) {\n+\t\tif (flow_hdl[i].drv == NULL)\n+\t\t\tbreak;\n+\t}\n+\tif (i == MAX_CLIENTS) {\n+\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t       \"Cannot register more adapters into NtConnect framework\");\n+\t\treturn -1;\n+\t}\n+\n+\tflow_hdl[i].drv = drv;\n+\treturn register_ntconn_mod(&drv->p_dev->addr, (void *)&flow_hdl[i],\n+\t\t\t\t   &ntconn_flow_op);\n+}\ndiff --git a/drivers/net/ntnic/ntconnect_modules/ntconn_meter.c b/drivers/net/ntnic/ntconnect_modules/ntconn_meter.c\nnew file mode 100644\nindex 0000000000..7c21690f8b\n--- /dev/null\n+++ b/drivers/net/ntnic/ntconnect_modules/ntconn_meter.c\n@@ -0,0 +1,517 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include <errno.h>\n+#include \"ntnic_ethdev.h\"\n+#include \"ntconnect.h\"\n+#include \"ntos_system.h\"\n+#include \"ntconn_modules.h\"\n+#include \"ntconn_mod_helper.h\"\n+#include \"nt_util.h\"\n+#include \"ntlog.h\"\n+#include \"ntnic_vf_vdpa.h\"\n+\n+#include \"ntconnect_api_meter.h\"\n+#include \"flow_api_profile_inline.h\"\n+\n+#include <rte_errno.h>\n+#include <rte_mtr.h>\n+#include <rte_mtr_driver.h>\n+\n+#define NTCONN_METER_VERSION_MAJOR 0U\n+#define NTCONN_METER_VERSION_MINOR 1U\n+\n+#define this_module_name \"meter\"\n+\n+#define MAX_CLIENTS 32\n+\n+#define UNUSED __rte_unused\n+\n+static struct meter_hdl_s {\n+\tstruct drv_s *drv;\n+} meter_hdl[MAX_CLIENTS];\n+\n+static ntconn_err_t ntconn_err[] = {\n+\t{ NTCONN_METER_ERR_NONE, \"Success\" },\n+\t{ NTCONN_METER_ERR_INTERNAL_ERROR, \"Internal error\" },\n+\t{ NTCONN_METER_ERR_INVALID_PORT, \"Invalid virtual port\" },\n+\t{ NTCONN_METER_ERR_PROFILE_ID, \"Profile ID out of range\" },\n+\t{ NTCONN_METER_ERR_POLICY_ID, \"Policy ID out of range\" },\n+\t{ NTCONN_METER_ERR_METER_ID, \"Meter ID out of range\" },\n+\t{ -1, NULL }\n+};\n+\n+/********************************************************************/\n+/* Get error message corresponding to the error code                */\n+/********************************************************************/\n+static const char *get_error_msg(uint32_t err_code)\n+{\n+\tint idx = 0;\n+\n+\tif (err_code < NTCONN_METER_ERR_INTERNAL_ERROR) {\n+\t\tconst ntconn_err_t *err_msg = get_ntconn_error(err_code);\n+\n+\t\treturn err_msg->err_text;\n+\t}\n+\twhile (ntconn_err[idx].err_code != (uint32_t)-1 &&\n+\t\t\tntconn_err[idx].err_code != err_code)\n+\t\tidx++;\n+\tif (ntconn_err[idx].err_code == (uint32_t)-1)\n+\t\tidx = 1;\n+\treturn ntconn_err[idx].err_text;\n+}\n+\n+/*\n+ * Filter functions\n+ */\n+static int func_meter_get_capabilities(void *hdl, int client_id,\n+\t\t\t\t       struct ntconn_header_s *hdr, char **data,\n+\t\t\t\t       int *len);\n+static int func_meter_setup(void *hdl, int client_id,\n+\t\t\t    struct ntconn_header_s *hdr, char **data, int *len);\n+static int func_meter_read(void *hdl, int client_id,\n+\t\t\t   struct ntconn_header_s *hdr, char **data, int *len);\n+static struct func_s adapter_entry_funcs[] = {\n+\t{ \"capabilities\", NULL, func_meter_get_capabilities },\n+\t{ \"setup\", NULL, func_meter_setup },\n+\t{ \"read\", NULL, func_meter_read },\n+\t{ NULL, NULL, NULL },\n+};\n+\n+/**********************************************************************/\n+/* copy error message corresponding to the error code to error struct */\n+/**********************************************************************/\n+static void copy_mtr_error(struct rte_mtr_error *error, uint32_t err)\n+{\n+\terror->type = RTE_MTR_ERROR_TYPE_UNSPECIFIED;\n+\terror->message = get_error_msg(err);\n+\terror->cause = NULL;\n+}\n+\n+static int func_meter_get_capabilities(void *hdl _unused, int client_id _unused,\n+\t\t\t\t       struct ntconn_header_s *hdr _unused,\n+\t\t\t\t       char **data, int *len)\n+{\n+\tchar *saveptr;\n+\tuint8_t vport = 0;\n+\tuint8_t port = 0;\n+\tint status;\n+\tstruct rte_mtr_capabilities cap;\n+\tstruct rte_mtr_error error;\n+\n+#ifdef DEBUG_METER\n+\tNT_LOG(DBG, NTCONNECT, \"%s: \\\"%s\\\"\\n\", __func__, *data);\n+#endif\n+\n+\tchar *tok = strtok_r(*data, \",\", &saveptr);\n+\n+\tif (tok) {\n+\t\tint length = strlen(tok);\n+\n+\t\tif (length > 6 && memcmp(tok, \"vport=\", 6) == 0)\n+\t\t\tvport = atoi(tok + 6);\n+\t}\n+#ifdef DEBUG_METER\n+\tNT_LOG(DBG, NTCONNECT, \"vport=%u\\n\", vport);\n+#endif\n+\n+\tif (vport == 0 || vport > 64) {\n+\t\t*len = 0;\n+\t\tNT_LOG(ERR, NTCONNECT, \"Virtual port is invalid\");\n+\t\tcopy_mtr_error(&error, NTCONN_METER_ERR_INVALID_PORT);\n+\t\tstatus = -ENODEV;\n+\t\tgoto error_get_capa;\n+\t}\n+\n+\tport = vport & 1;\n+\tstatus = rte_mtr_capabilities_get(port, &cap, &error);\n+\tif (status == 0) {\n+\t\t/* Handle success by copying the return values to the return struct */\n+\t\t*data = malloc(sizeof(struct meter_capabilities_return_s));\n+\t\tif (!*data)\n+\t\t\tgoto error_get_capa_malloc;\n+\t\tstruct meter_capabilities_return_s *return_value =\n+\t\t\t(struct meter_capabilities_return_s *)*data;\n+\t\t*len = sizeof(struct meter_capabilities_return_s);\n+\t\tmemcpy(&return_value->cap, &cap,\n+\t\t       sizeof(struct rte_mtr_capabilities));\n+\t\treturn REQUEST_OK;\n+\t}\n+\n+error_get_capa:\n+\n+\t/* Handle errors by copy errors to the error struct */\n+\tNT_LOG(ERR, NTCONNECT, \"Failed to get capabilities for port %u (%u)\",\n+\t       port, vport);\n+\t*data = malloc(sizeof(struct meter_error_return_s));\n+\tif (!*data)\n+\t\tgoto error_get_capa_malloc;\n+\tstruct meter_error_return_s *return_value =\n+\t\t(struct meter_error_return_s *)*data;\n+\t*len = sizeof(struct meter_error_return_s);\n+\treturn_value->status = status;\n+\treturn_value->type = error.type;\n+\tstrlcpy(return_value->err_msg, error.message, ERR_MSG_LEN);\n+\treturn REQUEST_OK;\n+\n+error_get_capa_malloc:\n+\n+\t*len = 0;\n+\treturn REQUEST_ERR;\n+}\n+\n+static int func_meter_setup(void *hdl _unused, int client_id _unused,\n+\t\t\t    struct ntconn_header_s *hdr, char **data, int *len)\n+{\n+\tchar *saveptr;\n+\tuint8_t port;\n+\tuint32_t max_id;\n+\tint status;\n+\tstruct rte_mtr_error error;\n+\tint command = UNKNOWN_CMD;\n+\n+#ifdef DEBUG_METER\n+\tNT_LOG(DBG, NTCONNECT, \"%s: \\\"%s\\\"\\n\", __func__, *data);\n+#endif\n+\n+\tif (hdr->blob_len != sizeof(struct meter_setup_s)) {\n+\t\t*len = 0;\n+\t\tNT_LOG(ERR, NTCONNECT, \"Error: Profile data size is illegal\");\n+\t\tcopy_mtr_error(&error, NTCONN_ERR_CODE_INVALID_REQUEST);\n+\t\tstatus = -EINTR;\n+\t\tgoto error_meter_setup;\n+\t}\n+\n+\t/* Get the data blob containing the data for the meter function */\n+\tstruct meter_setup_s *cpy_data =\n+\t\t(struct meter_setup_s *)&(*data)[hdr->len];\n+\n+\tif (cpy_data->vport < 4 || cpy_data->vport > 128) {\n+\t\t*len = 0;\n+\t\tNT_LOG(ERR, NTCONNECT, \"Virtual port is invalid\");\n+\t\tcopy_mtr_error(&error, NTCONN_METER_ERR_INVALID_PORT);\n+\t\tstatus = -ENODEV;\n+\t\tgoto error_meter_setup;\n+\t}\n+\n+\tchar *tok = strtok_r(*data, \",\", &saveptr);\n+\n+\tif (tok) {\n+\t\tint length = strlen(tok);\n+\n+\t\tif (length == 6) {\n+\t\t\tif (memcmp(tok, \"addpro\", 6) == 0)\n+\t\t\t\tcommand = ADD_PROFILE;\n+\n+\t\t\telse if (memcmp(tok, \"delpro\", 6) == 0)\n+\t\t\t\tcommand = DEL_PROFILE;\n+\n+\t\t\telse if (memcmp(tok, \"addpol\", 6) == 0)\n+\t\t\t\tcommand = ADD_POLICY;\n+\n+\t\t\telse if (memcmp(tok, \"delpol\", 6) == 0)\n+\t\t\t\tcommand = DEL_POLICY;\n+\n+\t\t\telse if (memcmp(tok, \"crtmtr\", 6) == 0)\n+\t\t\t\tcommand = CREATE_MTR;\n+\n+\t\t\telse if (memcmp(tok, \"delmtr\", 6) == 0)\n+\t\t\t\tcommand = DEL_MTR;\n+\t\t}\n+\t}\n+\n+\tif (command == UNKNOWN_CMD) {\n+\t\tNT_LOG(ERR, NTCONNECT, \"Error: Invalid command\");\n+\t\tcopy_mtr_error(&error, NTCONN_ERR_CODE_INVALID_REQUEST);\n+\t\tstatus = -EINVAL;\n+\t\tgoto error_meter_setup;\n+\t}\n+\n+\t/* Port will be either 0 or 1 depending on the VF. */\n+\tport = cpy_data->vport & 1;\n+\n+\tswitch (command) {\n+\tcase ADD_PROFILE:\n+\t\tmax_id = flow_mtr_meter_policy_n_max() / (RTE_MAX_ETHPORTS - 2);\n+\t\tif (cpy_data->id > max_id) {\n+\t\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t\t       \"Error: Profile ID %u out of range. Max value is %u\",\n+\t\t\t       cpy_data->id, max_id);\n+\t\t\tcopy_mtr_error(&error, NTCONN_METER_ERR_PROFILE_ID);\n+\t\t\tstatus = -EINVAL;\n+\t\t\tgoto error_meter_setup;\n+\t\t}\n+\t\tcpy_data->id = ((cpy_data->vport - 4) * max_id) + cpy_data->id;\n+\t\tstatus = rte_mtr_meter_profile_add(port, cpy_data->id,\n+\t\t\t\t\t\t   &cpy_data->profile, &error);\n+\t\tif (status != 0) {\n+\t\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t\t       \"Failed to add profile for port %u (%u)\", port,\n+\t\t\t       cpy_data->vport);\n+\t\t}\n+\t\tbreak;\n+\tcase DEL_PROFILE:\n+\t\tmax_id = flow_mtr_meter_policy_n_max() / (RTE_MAX_ETHPORTS - 2);\n+\t\tif (cpy_data->id > max_id) {\n+\t\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t\t       \"Error: Profile ID %u out of range. Max value is %u\",\n+\t\t\t       cpy_data->id, max_id);\n+\t\t\tcopy_mtr_error(&error, NTCONN_METER_ERR_PROFILE_ID);\n+\t\t\tstatus = -EINVAL;\n+\t\t\tgoto error_meter_setup;\n+\t\t}\n+\t\tcpy_data->id = ((cpy_data->vport - 4) * max_id) + cpy_data->id;\n+\t\tstatus = rte_mtr_meter_profile_delete(port, cpy_data->id,\n+\t\t\t\t\t\t      &error);\n+\t\tif (status != 0) {\n+\t\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t\t       \"Failed to delete profile for port %u (%u)\",\n+\t\t\t       port, cpy_data->vport);\n+\t\t}\n+\t\tbreak;\n+\tcase ADD_POLICY:\n+\t\tmax_id = flow_mtr_meter_policy_n_max() / (RTE_MAX_ETHPORTS - 2);\n+\t\tif (cpy_data->id > max_id) {\n+\t\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t\t       \"Error: Policy ID %u out of range. Max value is %u\",\n+\t\t\t       cpy_data->id, max_id);\n+\t\t\tcopy_mtr_error(&error, NTCONN_METER_ERR_POLICY_ID);\n+\t\t\tstatus = -EINVAL;\n+\t\t\tgoto error_meter_setup;\n+\t\t}\n+\t\tcpy_data->id = ((cpy_data->vport - 4) * max_id) + cpy_data->id;\n+\t\tcpy_data->p.policy.actions[RTE_COLOR_GREEN] =\n+\t\t\tcpy_data->p.actions_green;\n+\t\tcpy_data->p.policy.actions[RTE_COLOR_YELLOW] =\n+\t\t\tcpy_data->p.actions_yellow;\n+\t\tcpy_data->p.policy.actions[RTE_COLOR_RED] =\n+\t\t\tcpy_data->p.actions_red;\n+\t\tstatus = rte_mtr_meter_policy_add(port, cpy_data->id,\n+\t\t\t\t\t\t  &cpy_data->p.policy, &error);\n+\t\tif (status != 0) {\n+\t\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t\t       \"Failed to add policy for port %u (%u)\", port,\n+\t\t\t       cpy_data->vport);\n+\t\t}\n+\t\tbreak;\n+\tcase DEL_POLICY:\n+\t\tmax_id = flow_mtr_meter_policy_n_max() / (RTE_MAX_ETHPORTS - 2);\n+\t\tif (cpy_data->id > max_id) {\n+\t\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t\t       \"Error: Policy ID %u out of range. Max value is %u\",\n+\t\t\t       cpy_data->id, max_id);\n+\t\t\tcopy_mtr_error(&error, NTCONN_METER_ERR_POLICY_ID);\n+\t\t\tstatus = -EINVAL;\n+\t\t\tgoto error_meter_setup;\n+\t\t}\n+\t\tcpy_data->id = ((cpy_data->vport - 4) * max_id) + cpy_data->id;\n+\t\tstatus =\n+\t\t\trte_mtr_meter_policy_delete(port, cpy_data->id, &error);\n+\t\tif (status != 0) {\n+\t\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t\t       \"Failed to delete policy for port %u (%u)\", port,\n+\t\t\t       cpy_data->vport);\n+\t\t}\n+\t\tbreak;\n+\tcase CREATE_MTR:\n+\t\tmax_id = flow_mtr_meters_supported() / (RTE_MAX_ETHPORTS - 2);\n+\t\tif (cpy_data->id > max_id) {\n+\t\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t\t       \"Error: Meter ID %u out of range. Max value is %u\",\n+\t\t\t       cpy_data->id, max_id);\n+\t\t\tcopy_mtr_error(&error, NTCONN_METER_ERR_METER_ID);\n+\t\t\tstatus = -EINVAL;\n+\t\t\tgoto error_meter_setup;\n+\t\t}\n+\t\tcpy_data->id = ((cpy_data->vport - 4) * max_id) + cpy_data->id;\n+\t\tcpy_data->mtr_params.meter_profile_id =\n+\t\t\t((cpy_data->vport - 4) *\n+\t\t\t (flow_mtr_meter_policy_n_max() /\n+\t\t\t  (RTE_MAX_ETHPORTS - 2))) +\n+\t\t\tcpy_data->mtr_params.meter_profile_id;\n+\t\tcpy_data->mtr_params.meter_policy_id =\n+\t\t\t((cpy_data->vport - 4) *\n+\t\t\t (flow_mtr_meter_policy_n_max() /\n+\t\t\t  (RTE_MAX_ETHPORTS - 2))) +\n+\t\t\tcpy_data->mtr_params.meter_policy_id;\n+\t\tstatus = rte_mtr_create(port, cpy_data->id,\n+\t\t\t\t\t&cpy_data->mtr_params, cpy_data->shared,\n+\t\t\t\t\t&error);\n+\t\tif (status != 0) {\n+\t\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t\t       \"Failed to create meter for port %u (%u)\", port,\n+\t\t\t       cpy_data->vport);\n+\t\t}\n+\t\tbreak;\n+\tcase DEL_MTR:\n+\t\tmax_id = flow_mtr_meters_supported() / (RTE_MAX_ETHPORTS - 2);\n+\t\tif (cpy_data->id > max_id) {\n+\t\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t\t       \"Error: Meter ID %u out of range. Max value is %u\",\n+\t\t\t       cpy_data->id, max_id);\n+\t\t\tcopy_mtr_error(&error, NTCONN_METER_ERR_METER_ID);\n+\t\t\tstatus = -EINVAL;\n+\t\t\tgoto error_meter_setup;\n+\t\t}\n+\t\tcpy_data->id = ((cpy_data->vport - 4) * max_id) + cpy_data->id;\n+\t\tstatus = rte_mtr_destroy(port, cpy_data->id, &error);\n+\t\tif (status != 0) {\n+\t\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t\t       \"Failed to destroy meter for port %u (%u)\", port,\n+\t\t\t       cpy_data->vport);\n+\t\t}\n+\t\tbreak;\n+\t}\n+\n+\tif (status == 0) {\n+\t\t/* Handle success by copying the return values to the return struct */\n+\t\t*data = malloc(sizeof(struct meter_return_s));\n+\t\tif (!*data)\n+\t\t\tgoto error_meter_setup_malloc;\n+\t\tstruct meter_return_s *return_value =\n+\t\t\t(struct meter_return_s *)*data;\n+\t\t*len = sizeof(struct meter_return_s);\n+\t\treturn_value->status = 0;\n+\t\treturn REQUEST_OK;\n+\t}\n+\n+error_meter_setup:\n+\n+\t/* Handle errors by copy errors to the error struct */\n+\t *data = malloc(sizeof(struct meter_error_return_s));\n+\tif (!*data)\n+\t\tgoto error_meter_setup_malloc;\n+\tstruct meter_error_return_s *return_value =\n+\t\t(struct meter_error_return_s *)*data;\n+\t*len = sizeof(struct meter_error_return_s);\n+\treturn_value->status = status;\n+\treturn_value->type = error.type;\n+\tstrlcpy(return_value->err_msg, error.message, ERR_MSG_LEN);\n+\treturn REQUEST_OK;\n+\n+error_meter_setup_malloc:\n+\n+\t*len = 0;\n+\treturn REQUEST_ERR;\n+}\n+\n+static int func_meter_read(void *hdl _unused, int client_id _unused,\n+\t\t\t   struct ntconn_header_s *hdr, char **data, int *len)\n+{\n+\tuint8_t port = 0;\n+\tint status;\n+\tstruct rte_mtr_error error;\n+\tstruct rte_mtr_stats stats;\n+\tuint64_t stats_mask;\n+\n+#ifdef DEBUG_METER\n+\tNT_LOG(DBG, NTCONNECT, \"%s: [%s:%u] enter\\n\", __func__, __FILE__, __LINE__);\n+#endif\n+\n+\tif (hdr->blob_len != sizeof(struct meter_get_stat_s)) {\n+\t\t*len = 0;\n+\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t       \"Error: Read meter stats data size is illegal\");\n+\t\tcopy_mtr_error(&error, NTCONN_ERR_CODE_INVALID_REQUEST);\n+\t\tstatus = -EINTR;\n+\t\tgoto error_meter_read;\n+\t}\n+\n+\t/* Get the data blob containing the data for the meter function */\n+\tstruct meter_get_stat_s *cpy_data =\n+\t\t(struct meter_get_stat_s *)&(*data)[hdr->len];\n+\n+\tif (cpy_data->vport < 4 || cpy_data->vport > 128) {\n+\t\t*len = 0;\n+\t\tNT_LOG(ERR, NTCONNECT, \"Virtual port is invalid\");\n+\t\tcopy_mtr_error(&error, NTCONN_METER_ERR_INVALID_PORT);\n+\t\tstatus = -ENODEV;\n+\t\tgoto error_meter_read;\n+\t}\n+\n+\tport = cpy_data->vport & 1;\n+\tcpy_data->mtr_id =\n+\t\t((cpy_data->vport - 4) *\n+\t\t (flow_mtr_meters_supported() / (RTE_MAX_ETHPORTS - 2))) +\n+\t\tcpy_data->mtr_id;\n+\tstatus = rte_mtr_stats_read(port, cpy_data->mtr_id, &stats, &stats_mask,\n+\t\t\t\t    cpy_data->clear, &error);\n+\tif (status == 0) {\n+\t\t/* Handle success by copying the return values to the return struct */\n+\t\t*data = malloc(sizeof(struct meter_return_stat_s));\n+\t\tif (!*data)\n+\t\t\tgoto error_meter_read_malloc;\n+\t\tstruct meter_return_stat_s *return_value =\n+\t\t\t(struct meter_return_stat_s *)*data;\n+\t\t*len = sizeof(struct meter_return_stat_s);\n+\t\treturn_value->stats_mask = stats_mask;\n+\t\tmemcpy(&return_value->stats, &stats,\n+\t\t       sizeof(struct rte_mtr_stats));\n+\t\treturn REQUEST_OK;\n+\t}\n+\n+error_meter_read:\n+\t/* Handle errors by copy errors to the error struct */\n+\tNT_LOG(ERR, NTCONNECT, \"Failed to read meter stats\");\n+\t*data = malloc(sizeof(struct meter_error_return_s));\n+\tif (!*data)\n+\t\tgoto error_meter_read_malloc;\n+\tstruct meter_error_return_s *return_value =\n+\t\t(struct meter_error_return_s *)*data;\n+\t*len = sizeof(struct meter_error_return_s);\n+\tstrlcpy(return_value->err_msg, error.message, ERR_MSG_LEN);\n+\treturn_value->status = status;\n+\treturn_value->type = error.type;\n+\treturn REQUEST_OK;\n+\n+error_meter_read_malloc:\n+\t*len = 0;\n+\treturn REQUEST_ERR;\n+}\n+\n+static int meter_request(void *hdl, int client_id _unused,\n+\t\t\t struct ntconn_header_s *hdr, char *function,\n+\t\t\t char **data, int *len)\n+{\n+\treturn execute_function(this_module_name, hdl, client_id, hdr, function,\n+\t\t\t\tadapter_entry_funcs, data, len, 0);\n+}\n+\n+static void meter_free_data(void *hdl _unused, char *data)\n+{\n+\tif (data)\n+\t\tfree(data);\n+}\n+\n+static void meter_client_cleanup(void *hdl _unused, int client_id _unused)\n+{\n+\t/* Nothing to do */\n+}\n+\n+static const ntconnapi_t ntconn_meter_op = { this_module_name,\n+\t\t\t\t\t     NTCONN_METER_VERSION_MAJOR,\n+\t\t\t\t\t     NTCONN_METER_VERSION_MINOR,\n+\t\t\t\t\t     meter_request,\n+\t\t\t\t\t     meter_free_data,\n+\t\t\t\t\t     meter_client_cleanup\n+\t\t\t\t\t   };\n+\n+int ntconn_meter_register(struct drv_s *drv)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < MAX_CLIENTS; i++) {\n+\t\tif (meter_hdl[i].drv == NULL)\n+\t\t\tbreak;\n+\t}\n+\tif (i == MAX_CLIENTS) {\n+\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t       \"Cannot register more adapters into NtConnect framework\");\n+\t\treturn -1;\n+\t}\n+\n+\tmeter_hdl[i].drv = drv;\n+\treturn register_ntconn_mod(&drv->p_dev->addr, (void *)&meter_hdl[i],\n+\t\t\t\t   &ntconn_meter_op);\n+}\ndiff --git a/drivers/net/ntnic/ntconnect_modules/ntconn_modules.h b/drivers/net/ntnic/ntconnect_modules/ntconn_modules.h\nnew file mode 100644\nindex 0000000000..ea379015fe\n--- /dev/null\n+++ b/drivers/net/ntnic/ntconnect_modules/ntconn_modules.h\n@@ -0,0 +1,19 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#ifndef _NTCONN_MODULES_H_\n+#define _NTCONN_MODULES_H_\n+\n+#include \"ntos_system.h\"\n+\n+/*\n+ * All defined NT connection modules\n+ */\n+int ntconn_adap_register(struct drv_s *drv);\n+int ntconn_stat_register(struct drv_s *drv);\n+int ntconn_flow_register(struct drv_s *drv);\n+int ntconn_meter_register(struct drv_s *drv);\n+int ntconn_test_register(struct drv_s *drv);\n+\n+#endif /* _NTCONN_MODULES_H_ */\ndiff --git a/drivers/net/ntnic/ntconnect_modules/ntconn_stat.c b/drivers/net/ntnic/ntconnect_modules/ntconn_stat.c\nnew file mode 100644\nindex 0000000000..5c8b8db39e\n--- /dev/null\n+++ b/drivers/net/ntnic/ntconnect_modules/ntconn_stat.c\n@@ -0,0 +1,876 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include <rte_dev.h>\n+#include <rte_bus_pci.h>\n+#include <ethdev_pci.h>\n+#include <rte_ethdev.h>\n+\n+#include \"ntconnect.h\"\n+#include \"ntconnect_api_statistic.h\"\n+#include \"ntos_system.h\"\n+#include \"ntconn_modules.h\"\n+#include \"ntconn_mod_helper.h\"\n+#include \"nt_util.h\"\n+#include \"ntlog.h\"\n+#include \"ntnic_xstats.h\"\n+\n+#define STAT_VERSION_MAJOR 0U\n+#define STAT_VERSION_MINOR 2U\n+\n+#define this_module_name \"stat\"\n+\n+/*\n+ * Supported Stat Layout Versions\n+ */\n+#define NUM_LAYOUT_VERSIONS_SUPPORTED (RTE_DIM(layout_versions_supported))\n+static int layout_versions_supported[] = {\n+\t6,\n+\t/*\n+\t * Add here other layout versions to support\n+\t * When more versions are added, add new version dependent binary reply structures\n+\t * in ntconnect_api.h file for client to select on reading layout_version\n+\t */\n+};\n+\n+enum snap_addr_select_e {\n+\tSNAP_COLORS,\n+\tSNAP_QUEUES,\n+\tSNAP_RX_PORT,\n+\tSNAP_TX_PORT,\n+\tSNAP_ADDR_COUNT\n+};\n+\n+struct snap_addr_s {\n+\tconst uint64_t *ptr;\n+\tunsigned int size;\n+};\n+\n+struct snaps_s {\n+\tint client_id;\n+\t/* Pointers into buffer */\n+\tstruct snap_addr_s snap_addr[SNAP_ADDR_COUNT];\n+\tuint64_t *buffer;\n+\tstruct snaps_s *next;\n+};\n+\n+static struct stat_hdl {\n+\tstruct drv_s *drv;\n+\tnt4ga_stat_t *p_nt4ga_stat;\n+\tstruct snaps_s *snaps_base;\n+} stat_hdl;\n+\n+enum stat_type_e {\n+\tSTAT_TYPE_COLOR,\n+\tSTAT_TYPE_QUEUE,\n+\tSTAT_TYPE_RX,\n+\tSTAT_TYPE_TX,\n+\tSTAT_TYPE_FLOWMATCHER\n+};\n+\n+static int func_get_snap_colors(void *hdl, int client_id,\n+\t\t\t\tstruct ntconn_header_s *hdr, char **data,\n+\t\t\t\tint *len);\n+static int func_get_snap_queues(void *hdl, int client_id,\n+\t\t\t\tstruct ntconn_header_s *hdr, char **data,\n+\t\t\t\tint *len);\n+static int func_get_snap_rx_port(void *hdl, int client_id,\n+\t\t\t\t struct ntconn_header_s *hdr, char **data,\n+\t\t\t\t int *len);\n+static int func_get_snap_tx_port(void *hdl, int client_id,\n+\t\t\t\t struct ntconn_header_s *hdr, char **data,\n+\t\t\t\t int *len);\n+static struct func_s func_snap_level2[] = {\n+\t{ \"colors\", NULL, func_get_snap_colors },\n+\t{ \"queues\", NULL, func_get_snap_queues },\n+\t{ \"rx_counters\", NULL, func_get_snap_rx_port },\n+\t{ \"tx_counters\", NULL, func_get_snap_tx_port },\n+\t{ NULL, NULL, NULL },\n+};\n+\n+static int func_get_layout_version(void *hdl, int client_id,\n+\t\t\t\t   struct ntconn_header_s *hdr, char **data,\n+\t\t\t\t   int *len);\n+static int func_get_flm(void *hdl, int client_id, struct ntconn_header_s *hdr,\n+\t\t\tchar **data, int *len);\n+static int func_get_color(void *hdl, int client_id, struct ntconn_header_s *hdr,\n+\t\t\t  char **data, int *len);\n+static int func_get_queue(void *hdl, int client_id, struct ntconn_header_s *hdr,\n+\t\t\t  char **data, int *len);\n+static int func_get_rx_counters(void *hdl, int client_id,\n+\t\t\t\tstruct ntconn_header_s *hdr, char **data,\n+\t\t\t\tint *len);\n+static int func_get_tx_counters(void *hdl, int client_id,\n+\t\t\t\tstruct ntconn_header_s *hdr, char **data,\n+\t\t\t\tint *len);\n+static int func_get_flm_layout_version(void *hdl, int client_id,\n+\t\t\t\t       struct ntconn_header_s *hdr, char **data,\n+\t\t\t\t       int *len);\n+\n+static struct func_s funcs_get_level1[] = {\n+\t{ \"snapshot\", func_snap_level2, NULL },\n+\t{ \"layout_version\", NULL, func_get_layout_version },\n+\t{ \"flm\", NULL, func_get_flm },\n+\t{ \"colors\", NULL, func_get_color },\n+\t{ \"queues\", NULL, func_get_queue },\n+\t{ \"rx_counters\", NULL, func_get_rx_counters },\n+\t{ \"tx_counters\", NULL, func_get_tx_counters },\n+\t{ \"flm_layout_version\", NULL, func_get_flm_layout_version },\n+\t{ NULL, NULL, NULL },\n+};\n+\n+/*\n+ * Entry level\n+ */\n+static int func_snapshot(void *hdl, int client_id, struct ntconn_header_s *hdr,\n+\t\t\t char **data, int *len);\n+static struct func_s stat_entry_funcs[] = {\n+\t{ \"get\", funcs_get_level1, NULL },\n+\t{ \"snapshot\", NULL, func_snapshot },\n+\t{ NULL, NULL, NULL },\n+};\n+\n+static int read_flm(nt4ga_stat_t *hwstat, uint64_t *val, int nbc)\n+{\n+\tstruct ntc_stat_get_data_s *cdata = (struct ntc_stat_get_data_s *)val;\n+\n+\tcdata->nb_counters = (uint64_t)nbc;\n+\tcdata->timestamp = hwstat->last_timestamp;\n+\tcdata->is_virt = hwstat->mp_nthw_stat->mb_is_vswitch;\n+\n+\tstruct rte_eth_xstat stats[100];\n+\tstruct rte_eth_xstat_name names[100];\n+\tint cnt_names = nthw_xstats_get_names(hwstat, names, 100,\n+\t\t\t\t\t      hwstat->mp_nthw_stat->mb_is_vswitch);\n+\tint cnt_values = nthw_xstats_get(hwstat, stats, 100,\n+\t\t\t\t\t hwstat->mp_nthw_stat->mb_is_vswitch, 0);\n+\tassert(cnt_names == cnt_values);\n+\n+\t/* virt/cap same */\n+\tstruct flowmatcher_type_fields_s *flm =\n+\t\t(struct flowmatcher_type_fields_s *)cdata->data;\n+\tif (hwstat->mp_stat_structs_flm) {\n+\t\tint c;\n+\n+\t\tfor (c = 0; c < nbc; c++) {\n+\t\t\tflm->current = hwstat->mp_stat_structs_flm->current;\n+\t\t\tflm->learn_done = hwstat->mp_stat_structs_flm->learn_done;\n+\t\t\tflm->learn_ignore =\n+\t\t\t\thwstat->mp_stat_structs_flm->learn_ignore;\n+\t\t\tflm->learn_fail = hwstat->mp_stat_structs_flm->learn_fail;\n+\t\t\tflm->unlearn_done =\n+\t\t\t\thwstat->mp_stat_structs_flm->unlearn_done;\n+\t\t\tflm->unlearn_ignore =\n+\t\t\t\thwstat->mp_stat_structs_flm->unlearn_ignore;\n+\t\t\tflm->auto_unlearn_done =\n+\t\t\t\thwstat->mp_stat_structs_flm->auto_unlearn_done;\n+\t\t\tflm->auto_unlearn_ignore =\n+\t\t\t\thwstat->mp_stat_structs_flm->auto_unlearn_ignore;\n+\t\t\tflm->auto_unlearn_fail =\n+\t\t\t\thwstat->mp_stat_structs_flm->auto_unlearn_fail;\n+\t\t\tflm->timeout_unlearn_done =\n+\t\t\t\thwstat->mp_stat_structs_flm->timeout_unlearn_done;\n+\t\t\tflm->rel_done = hwstat->mp_stat_structs_flm->rel_done;\n+\t\t\tflm->rel_ignore = hwstat->mp_stat_structs_flm->rel_ignore;\n+\t\t\tflm->prb_done = hwstat->mp_stat_structs_flm->prb_done;\n+\t\t\tflm->prb_ignore = hwstat->mp_stat_structs_flm->prb_ignore;\n+\n+\t\t\tflm->sta_done = hwstat->mp_stat_structs_flm->sta_done;\n+\t\t\tflm->inf_done = hwstat->mp_stat_structs_flm->inf_done;\n+\t\t\tflm->inf_skip = hwstat->mp_stat_structs_flm->inf_skip;\n+\t\t\tflm->pck_hit = hwstat->mp_stat_structs_flm->pck_hit;\n+\t\t\tflm->pck_miss = hwstat->mp_stat_structs_flm->pck_miss;\n+\t\t\tflm->pck_unh = hwstat->mp_stat_structs_flm->pck_unh;\n+\t\t\tflm->pck_dis = hwstat->mp_stat_structs_flm->pck_dis;\n+\t\t\tflm->csh_hit = hwstat->mp_stat_structs_flm->csh_hit;\n+\t\t\tflm->csh_miss = hwstat->mp_stat_structs_flm->csh_miss;\n+\t\t\tflm->csh_unh = hwstat->mp_stat_structs_flm->csh_unh;\n+\t\t\tflm->cuc_start = hwstat->mp_stat_structs_flm->cuc_start;\n+\t\t\tflm->cuc_move = hwstat->mp_stat_structs_flm->cuc_move;\n+\t\t}\n+\t} else {\n+\t\tmemset(flm, 0, sizeof(*hwstat->mp_stat_structs_flm));\n+\t}\n+\treturn nbc * NUM_STAT_RECORD_TYPE_FLOWMATCHER + STAT_INFO_ELEMENTS;\n+}\n+\n+static int read_colors(nt4ga_stat_t *hwstat, uint64_t *val, int nbc)\n+{\n+\tstruct ntc_stat_get_data_s *cdata = (struct ntc_stat_get_data_s *)val;\n+\n+\tcdata->nb_counters = (uint64_t)nbc;\n+\tcdata->timestamp = hwstat->last_timestamp;\n+\tcdata->is_virt = hwstat->mp_nthw_stat->mb_is_vswitch;\n+\n+\t/* virt/cap same */\n+\tstruct color_type_fields_s *clr =\n+\t\t(struct color_type_fields_s *)cdata->data;\n+\tint c;\n+\n+\tfor (c = 0; c < nbc; c++) {\n+\t\tclr->pkts = hwstat->mp_stat_structs_color[c].color_packets;\n+\t\tclr->octets = hwstat->mp_stat_structs_color[c].color_bytes;\n+\t\tclr->tcp_flgs =\n+\t\t\t(uint64_t)hwstat->mp_stat_structs_color[c].tcp_flags;\n+\t\tclr++;\n+\t}\n+\treturn nbc * NUM_STAT_RECORD_TYPE_COLOR + STAT_INFO_ELEMENTS;\n+}\n+\n+static int read_queues(nt4ga_stat_t *hwstat, uint64_t *val, int nbq)\n+{\n+\tstruct ntc_stat_get_data_s *qdata = (struct ntc_stat_get_data_s *)val;\n+\n+\tqdata->nb_counters = (uint64_t)nbq;\n+\tqdata->timestamp = hwstat->last_timestamp;\n+\tqdata->is_virt = hwstat->mp_nthw_stat->mb_is_vswitch;\n+\n+\t/* virt/cap same */\n+\tstruct queue_type_fields_s *queue =\n+\t\t(struct queue_type_fields_s *)qdata->data;\n+\tint q;\n+\n+\tfor (q = 0; q < nbq; q++) {\n+\t\tqueue->flush_pkts = hwstat->mp_stat_structs_hb[q].flush_packets;\n+\t\tqueue->drop_pkts = hwstat->mp_stat_structs_hb[q].drop_packets;\n+\t\tqueue->fwd_pkts = hwstat->mp_stat_structs_hb[q].fwd_packets;\n+\t\tqueue->dbs_drop_pkts = hwstat->mp_stat_structs_hb[q].dbs_drop_packets;\n+\t\tqueue->flush_octets = hwstat->mp_stat_structs_hb[q].flush_bytes;\n+\t\tqueue->drop_octets = hwstat->mp_stat_structs_hb[q].drop_bytes;\n+\t\tqueue->fwd_octets = hwstat->mp_stat_structs_hb[q].fwd_bytes;\n+\t\tqueue->dbs_drop_octets = hwstat->mp_stat_structs_hb[q].dbs_drop_bytes;\n+\t\tqueue++;\n+\t}\n+\treturn nbq * NUM_STAT_RECORD_TYPE_QUEUE + STAT_INFO_ELEMENTS;\n+}\n+\n+static void copy_rmon_stat(struct port_counters_v2 *cptr,\n+\t\t\t    struct stat_rmon_s *rmon)\n+{\n+\trmon->drop_events = cptr->drop_events;\n+\trmon->pkts = cptr->pkts;\n+\trmon->octets = cptr->octets;\n+\trmon->broadcast_pkts = cptr->broadcast_pkts;\n+\trmon->multicast_pkts = cptr->multicast_pkts;\n+\trmon->unicast_pkts = cptr->unicast_pkts;\n+\trmon->pkts_alignment = cptr->pkts_alignment;\n+\trmon->pkts_code_violation = cptr->pkts_code_violation;\n+\trmon->pkts_crc = cptr->pkts_crc;\n+\trmon->undersize_pkts = cptr->undersize_pkts;\n+\trmon->oversize_pkts = cptr->oversize_pkts;\n+\trmon->fragments = cptr->fragments;\n+\trmon->jabbers_not_truncated = cptr->jabbers_not_truncated;\n+\trmon->jabbers_truncated = cptr->jabbers_truncated;\n+\trmon->pkts_64_octets = cptr->pkts_64_octets;\n+\trmon->pkts_65_to_127_octets = cptr->pkts_65_to_127_octets;\n+\trmon->pkts_128_to_255_octets = cptr->pkts_128_to_255_octets;\n+\trmon->pkts_256_to_511_octets = cptr->pkts_256_to_511_octets;\n+\trmon->pkts_512_to_1023_octets = cptr->pkts_512_to_1023_octets;\n+\trmon->pkts_1024_to_1518_octets = cptr->pkts_1024_to_1518_octets;\n+\trmon->pkts_1519_to_2047_octets = cptr->pkts_1519_to_2047_octets;\n+\trmon->pkts_2048_to_4095_octets = cptr->pkts_2048_to_4095_octets;\n+\trmon->pkts_4096_to_8191_octets = cptr->pkts_4096_to_8191_octets;\n+\trmon->pkts_8192_to_max_octets = cptr->pkts_8192_to_max_octets;\n+}\n+\n+static int read_rx_counters(nt4ga_stat_t *hwstat, uint64_t *val, int nbp)\n+{\n+\tstruct ntc_stat_get_data_s *rxdata = (struct ntc_stat_get_data_s *)val;\n+\n+\trxdata->nb_counters = (uint64_t)nbp;\n+\trxdata->timestamp = hwstat->last_timestamp;\n+\trxdata->is_virt = hwstat->mp_nthw_stat->mb_is_vswitch;\n+\n+\tif (rxdata->is_virt) {\n+\t\tstruct rtx_type_fields_virt_s *rxc =\n+\t\t\t(struct rtx_type_fields_virt_s *)rxdata->data;\n+\t\tint p;\n+\n+\t\tfor (p = 0; p < nbp; p++) {\n+\t\t\trxc->octets =\n+\t\t\t\thwstat->virt.mp_stat_structs_port_rx[p].octets;\n+\t\t\trxc->pkts = hwstat->virt.mp_stat_structs_port_rx[p].pkts;\n+\t\t\trxc->drop_events =\n+\t\t\t\thwstat->virt.mp_stat_structs_port_rx[p].drop_events;\n+\t\t\trxc->qos_drop_octets =\n+\t\t\t\thwstat->virt.mp_stat_structs_port_rx[p]\n+\t\t\t\t.qos_drop_octets;\n+\t\t\trxc->qos_drop_pkts = hwstat->virt.mp_stat_structs_port_rx[p]\n+\t\t\t\t\t     .qos_drop_pkts;\n+\t\t\trxc++;\n+\t\t}\n+\t\treturn nbp * NUM_STAT_RECORD_TYPE_RX_PORT_VIRT +\n+\t\t       STAT_INFO_ELEMENTS;\n+\t} else {\n+\t\tstruct rx_type_fields_cap_s *rxc =\n+\t\t\t(struct rx_type_fields_cap_s *)rxdata->data;\n+\t\tint p;\n+\n+\t\tfor (p = 0; p < nbp; p++) {\n+\t\t\tcopy_rmon_stat(&hwstat->cap.mp_stat_structs_port_rx[p],\n+\t\t\t\t\t&rxc->rmon);\n+\n+\t\t\t/* Rx only port counters */\n+\t\t\trxc->mac_drop_events =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p]\n+\t\t\t\t.mac_drop_events;\n+\t\t\trxc->pkts_lr =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p].pkts_lr;\n+\t\t\trxc->duplicate =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p].duplicate;\n+\t\t\trxc->pkts_ip_chksum_error =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p]\n+\t\t\t\t.pkts_ip_chksum_error;\n+\t\t\trxc->pkts_udp_chksum_error =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p]\n+\t\t\t\t.pkts_udp_chksum_error;\n+\t\t\trxc->pkts_tcp_chksum_error =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p]\n+\t\t\t\t.pkts_tcp_chksum_error;\n+\t\t\trxc->pkts_giant_undersize =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p]\n+\t\t\t\t.pkts_giant_undersize;\n+\t\t\trxc->pkts_baby_giant =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p]\n+\t\t\t\t.pkts_baby_giant;\n+\t\t\trxc->pkts_not_isl_vlan_mpls =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p]\n+\t\t\t\t.pkts_not_isl_vlan_mpls;\n+\t\t\trxc->pkts_isl =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p].pkts_isl;\n+\t\t\trxc->pkts_vlan =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p].pkts_vlan;\n+\t\t\trxc->pkts_isl_vlan =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p].pkts_isl_vlan;\n+\t\t\trxc->pkts_mpls =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p].pkts_mpls;\n+\t\t\trxc->pkts_isl_mpls =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p].pkts_isl_mpls;\n+\t\t\trxc->pkts_vlan_mpls = hwstat->cap.mp_stat_structs_port_rx[p]\n+\t\t\t\t\t      .pkts_vlan_mpls;\n+\t\t\trxc->pkts_isl_vlan_mpls =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p]\n+\t\t\t\t.pkts_isl_vlan_mpls;\n+\t\t\trxc->pkts_no_filter = hwstat->cap.mp_stat_structs_port_rx[p]\n+\t\t\t\t\t      .pkts_no_filter;\n+\t\t\trxc->pkts_dedup_drop =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p]\n+\t\t\t\t.pkts_dedup_drop;\n+\t\t\trxc->pkts_filter_drop =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p]\n+\t\t\t\t.pkts_filter_drop;\n+\t\t\trxc->pkts_overflow =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p].pkts_overflow;\n+\t\t\trxc->pkts_dbs_drop =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p].pkts_dbs_drop;\n+\t\t\trxc->octets_no_filter =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p]\n+\t\t\t\t.octets_no_filter;\n+\t\t\trxc->octets_dedup_drop =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p]\n+\t\t\t\t.octets_dedup_drop;\n+\t\t\trxc->octets_filter_drop =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p]\n+\t\t\t\t.octets_filter_drop;\n+\t\t\trxc->octets_overflow =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p]\n+\t\t\t\t.octets_overflow;\n+\t\t\trxc->octets_dbs_drop =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p]\n+\t\t\t\t.octets_dbs_drop;\n+\t\t\trxc->ipft_first_hit = hwstat->cap.mp_stat_structs_port_rx[p]\n+\t\t\t\t\t      .ipft_first_hit;\n+\t\t\trxc->ipft_first_not_hit =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p]\n+\t\t\t\t.ipft_first_not_hit;\n+\t\t\trxc->ipft_mid_hit =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p].ipft_mid_hit;\n+\t\t\trxc->ipft_mid_not_hit =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p]\n+\t\t\t\t.ipft_mid_not_hit;\n+\t\t\trxc->ipft_last_hit =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p].ipft_last_hit;\n+\t\t\trxc->ipft_last_not_hit =\n+\t\t\t\thwstat->cap.mp_stat_structs_port_rx[p]\n+\t\t\t\t.ipft_last_not_hit;\n+\t\t\trxc++;\n+\t\t}\n+\t\treturn nbp * NUM_STAT_RECORD_TYPE_RX_PORT_CAP +\n+\t\t       STAT_INFO_ELEMENTS;\n+\t}\n+}\n+\n+static int read_tx_counters(nt4ga_stat_t *hwstat, uint64_t *val, int nbp)\n+{\n+\tstruct ntc_stat_get_data_s *txdata = (struct ntc_stat_get_data_s *)val;\n+\n+\ttxdata->nb_counters = (uint64_t)nbp;\n+\ttxdata->timestamp = hwstat->last_timestamp;\n+\ttxdata->is_virt = hwstat->mp_nthw_stat->mb_is_vswitch;\n+\n+\tif (txdata->is_virt) {\n+\t\tstruct rtx_type_fields_virt_s *txc =\n+\t\t\t(struct rtx_type_fields_virt_s *)txdata->data;\n+\t\tint p;\n+\n+\t\tfor (p = 0; p < nbp; p++) {\n+\t\t\ttxc->octets =\n+\t\t\t\thwstat->virt.mp_stat_structs_port_tx[p].octets;\n+\t\t\ttxc->pkts = hwstat->virt.mp_stat_structs_port_tx[p].pkts;\n+\t\t\ttxc->drop_events =\n+\t\t\t\thwstat->virt.mp_stat_structs_port_tx[p].drop_events;\n+\t\t\ttxc->qos_drop_octets =\n+\t\t\t\thwstat->virt.mp_stat_structs_port_tx[p]\n+\t\t\t\t.qos_drop_octets;\n+\t\t\ttxc->qos_drop_pkts = hwstat->virt.mp_stat_structs_port_tx[p]\n+\t\t\t\t\t     .qos_drop_pkts;\n+\t\t\ttxc++;\n+\t\t}\n+\t\treturn nbp * NUM_STAT_RECORD_TYPE_TX_PORT_VIRT +\n+\t\t       STAT_INFO_ELEMENTS;\n+\t} else {\n+\t\tstruct tx_type_fields_cap_s *txc =\n+\t\t\t(struct tx_type_fields_cap_s *)txdata->data;\n+\t\tint p;\n+\n+\t\tfor (p = 0; p < nbp; p++) {\n+\t\t\tcopy_rmon_stat(&hwstat->cap.mp_stat_structs_port_tx[p],\n+\t\t\t\t\t&txc->rmon);\n+\t\t\ttxc->rmon.pkts = hwstat->a_port_tx_packets_total[p];\n+\t\t\ttxc++;\n+\t\t}\n+\t\treturn nbp * NUM_STAT_RECORD_TYPE_TX_PORT_CAP +\n+\t\t       STAT_INFO_ELEMENTS;\n+\t}\n+}\n+\n+static int func_get_layout_version(void *hdl, int client_id _unused,\n+\t\t\t\t   struct ntconn_header_s *hdr _unused,\n+\t\t\t\t   char **data, int *len)\n+{\n+\tstruct stat_hdl *stat = (struct stat_hdl *)hdl;\n+\n+\tif (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) {\n+\t\t*data = NULL;\n+\t\t*len = 0;\n+\t\treturn REQUEST_ERR;\n+\t}\n+\t*data = malloc(sizeof(int));\n+\tif (!*data) {\n+\t\t*len = 0;\n+\t\tNT_LOG(ERR, NTCONNECT, \"memory allocation failed\");\n+\t\treturn REQUEST_ERR;\n+\t}\n+\n+\t*(int *)*data = stat->p_nt4ga_stat->mp_nthw_stat->mn_stat_layout_version;\n+\t*len = sizeof(int);\n+\treturn REQUEST_OK;\n+}\n+\n+static int func_get_flm_layout_version(void *hdl, int client_id _unused,\n+\t\t\t\t       struct ntconn_header_s *hdr _unused,\n+\t\t\t\t       char **data, int *len)\n+{\n+\tstruct stat_hdl *stat = (struct stat_hdl *)hdl;\n+\n+\tif (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) {\n+\t\t*data = NULL;\n+\t\t*len = 0;\n+\t\treturn REQUEST_ERR;\n+\t}\n+\t*data = malloc(sizeof(int));\n+\tif (!*data) {\n+\t\t*len = 0;\n+\t\tNT_LOG(ERR, NTCONNECT, \"memory allocation failed\");\n+\t\treturn REQUEST_ERR;\n+\t}\n+\n+\t*(int *)*data = (stat->p_nt4ga_stat->flm_stat_ver < 18) ? 1 : 2;\n+\t*len = sizeof(int);\n+\treturn REQUEST_OK;\n+}\n+\n+/*\n+ * Return total number of 64bit counters occupied by this stat type\n+ * additionally, returns total number of records for this type (ie number of queues, ports, etc)\n+ */\n+static int get_size(struct stat_hdl *stat, enum stat_type_e type,\n+\t\t     int *num_records)\n+{\n+\tint nrec, size;\n+\n+\tswitch (type) {\n+\tcase STAT_TYPE_COLOR:\n+\t\tnrec = stat->p_nt4ga_stat->mp_nthw_stat->m_nb_color_counters / 2;\n+\t\tsize = nrec * NUM_STAT_RECORD_TYPE_COLOR;\n+\t\tbreak;\n+\tcase STAT_TYPE_QUEUE:\n+\t\tnrec = stat->p_nt4ga_stat->mp_nthw_stat->m_nb_rx_host_buffers;\n+\t\tsize = nrec * NUM_STAT_RECORD_TYPE_QUEUE;\n+\t\tbreak;\n+\tcase STAT_TYPE_RX:\n+\t\tnrec = stat->p_nt4ga_stat->mn_rx_ports;\n+\t\tsize = nrec * ((stat->p_nt4ga_stat->mp_nthw_stat->mb_is_vswitch) ?\n+\t\t\t       NUM_STAT_RECORD_TYPE_RX_PORT_VIRT :\n+\t\t\t       NUM_STAT_RECORD_TYPE_RX_PORT_CAP);\n+\t\tbreak;\n+\tcase STAT_TYPE_TX:\n+\t\tnrec = stat->p_nt4ga_stat->mn_tx_ports;\n+\t\tsize = nrec * ((stat->p_nt4ga_stat->mp_nthw_stat->mb_is_vswitch) ?\n+\t\t\t       NUM_STAT_RECORD_TYPE_TX_PORT_VIRT :\n+\t\t\t       NUM_STAT_RECORD_TYPE_TX_PORT_CAP);\n+\t\tbreak;\n+\tcase STAT_TYPE_FLOWMATCHER:\n+\t\tnrec = 1;\n+\t\tsize = nrec * NUM_STAT_RECORD_TYPE_FLOWMATCHER;\n+\t\tbreak;\n+\t}\n+\n+\t*num_records = nrec;\n+\treturn size + STAT_INFO_ELEMENTS;\n+}\n+\n+static int do_get_stats(struct stat_hdl *stat, char **data, int *len,\n+\t\t\tenum stat_type_e stype,\n+\t\t\tint (*read_counters)(nt4ga_stat_t *, uint64_t *, int))\n+{\n+\tint nbg;\n+\tint size = get_size(stat, stype, &nbg);\n+\n+\tsize *= sizeof(uint64_t);\n+\tuint64_t *val = (uint64_t *)malloc(size);\n+\n+\tif (!val) {\n+\t\t*len = 0;\n+\t\tNT_LOG(ERR, NTCONNECT, \"memory allocation failed\");\n+\t\treturn REQUEST_ERR;\n+\t}\n+\n+\tpthread_mutex_lock(&stat->drv->ntdrv.stat_lck);\n+\tread_counters(stat->p_nt4ga_stat, val, nbg);\n+\tpthread_mutex_unlock(&stat->drv->ntdrv.stat_lck);\n+\n+\t*data = (char *)val;\n+\t*len = size;\n+\treturn REQUEST_OK;\n+}\n+\n+/*\n+ * Stat Request functions\n+ */\n+static int func_get_flm(void *hdl, int client_id _unused,\n+\t\t\tstruct ntconn_header_s *hdr _unused, char **data,\n+\t\t\tint *len)\n+{\n+\tstruct stat_hdl *stat = (struct stat_hdl *)hdl;\n+\n+\tif (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) {\n+\t\t*data = NULL;\n+\t\t*len = 0;\n+\t\treturn REQUEST_ERR;\n+\t}\n+\treturn do_get_stats(stat, data, len, STAT_TYPE_FLOWMATCHER, read_flm);\n+}\n+\n+static int func_get_color(void *hdl, int client_id _unused,\n+\t\t\t  struct ntconn_header_s *hdr _unused, char **data,\n+\t\t\t  int *len)\n+{\n+\tstruct stat_hdl *stat = (struct stat_hdl *)hdl;\n+\n+\tif (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) {\n+\t\t*data = NULL;\n+\t\t*len = 0;\n+\t\treturn REQUEST_ERR;\n+\t}\n+\treturn do_get_stats(stat, data, len, STAT_TYPE_COLOR, read_colors);\n+}\n+\n+static int func_get_queue(void *hdl, int client_id _unused,\n+\t\t\t  struct ntconn_header_s *hdr _unused, char **data,\n+\t\t\t  int *len)\n+{\n+\tstruct stat_hdl *stat = (struct stat_hdl *)hdl;\n+\n+\tif (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) {\n+\t\t*data = NULL;\n+\t\t*len = 0;\n+\t\treturn REQUEST_ERR;\n+\t}\n+\treturn do_get_stats(stat, data, len, STAT_TYPE_QUEUE, read_queues);\n+}\n+\n+static int func_get_rx_counters(void *hdl, int client_id _unused,\n+\t\t\t\tstruct ntconn_header_s *hdr _unused,\n+\t\t\t\tchar **data, int *len)\n+{\n+\tstruct stat_hdl *stat = (struct stat_hdl *)hdl;\n+\n+\tif (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) {\n+\t\t*data = NULL;\n+\t\t*len = 0;\n+\t\treturn REQUEST_ERR;\n+\t}\n+\treturn do_get_stats(stat, data, len, STAT_TYPE_RX, read_rx_counters);\n+}\n+\n+static int func_get_tx_counters(void *hdl, int client_id _unused,\n+\t\t\t\tstruct ntconn_header_s *hdr _unused,\n+\t\t\t\tchar **data, int *len)\n+{\n+\tstruct stat_hdl *stat = (struct stat_hdl *)hdl;\n+\n+\tif (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) {\n+\t\t*data = NULL;\n+\t\t*len = 0;\n+\t\treturn REQUEST_ERR;\n+\t}\n+\treturn do_get_stats(stat, data, len, STAT_TYPE_TX, read_tx_counters);\n+}\n+\n+/*\n+ * Snapshot handling. This is to ensure atomic reading of all statistics in one collection\n+ */\n+\n+static struct snaps_s *find_client_snap_data(struct stat_hdl *stat,\n+\t\tint client_id,\n+\t\tstruct snaps_s **parent)\n+{\n+\tstruct snaps_s *snaps = stat->snaps_base;\n+\n+\tif (parent)\n+\t\t*parent = NULL;\n+\twhile (snaps && snaps->client_id != client_id) {\n+\t\tif (parent)\n+\t\t\t*parent = snaps;\n+\t\tsnaps = snaps->next;\n+\t}\n+\n+\treturn snaps;\n+}\n+\n+static struct snaps_s *get_client_snap_data(struct stat_hdl *stat,\n+\t\tint client_id)\n+{\n+\tstruct snaps_s *snaps = find_client_snap_data(stat, client_id, NULL);\n+\n+\tif (!snaps) {\n+\t\tsnaps = malloc(sizeof(struct snaps_s)); /* return NULL on malloc failure */\n+\t\tif (snaps) {\n+\t\t\tsnaps->client_id = client_id;\n+\t\t\tsnaps->next = stat->snaps_base;\n+\t\t\tstat->snaps_base = snaps;\n+\t\t\tsnaps->buffer = NULL;\n+\t\t}\n+\t}\n+\treturn snaps;\n+}\n+\n+static int func_snapshot(void *hdl, int client_id,\n+\t\t\t struct ntconn_header_s *hdr _unused, char **data,\n+\t\t\t int *len)\n+{\n+\tstruct stat_hdl *stat = (struct stat_hdl *)hdl;\n+\tint nbc, nbq, nbpr, nbpt;\n+\tstruct snaps_s *snaps;\n+\n+\tif (!stat->p_nt4ga_stat || !stat->p_nt4ga_stat->mp_nthw_stat) {\n+\t\t*data = NULL;\n+\t\t*len = 0;\n+\t\treturn REQUEST_ERR;\n+\t}\n+\tsnaps = get_client_snap_data(stat, client_id);\n+\tif (!snaps)\n+\t\tgoto err_out;\n+\n+\tif (snaps->buffer)\n+\t\tfree(snaps->buffer);\n+\n+\tsnaps->snap_addr[SNAP_COLORS].size =\n+\t\t(unsigned int)get_size(stat, STAT_TYPE_COLOR, &nbc);\n+\tsnaps->snap_addr[SNAP_QUEUES].size =\n+\t\t(unsigned int)get_size(stat, STAT_TYPE_QUEUE, &nbq);\n+\tsnaps->snap_addr[SNAP_RX_PORT].size =\n+\t\t(unsigned int)get_size(stat, STAT_TYPE_RX, &nbpr);\n+\tsnaps->snap_addr[SNAP_TX_PORT].size =\n+\t\t(unsigned int)get_size(stat, STAT_TYPE_TX, &nbpt);\n+\n+\tunsigned int tot_size = snaps->snap_addr[SNAP_COLORS].size +\n+\t\t\t\tsnaps->snap_addr[SNAP_QUEUES].size +\n+\t\t\t\tsnaps->snap_addr[SNAP_RX_PORT].size +\n+\t\t\t\tsnaps->snap_addr[SNAP_TX_PORT].size;\n+\n+\tsnaps->buffer = malloc(tot_size * sizeof(uint64_t));\n+\tif (!snaps->buffer) {\n+\t\t*len = 0;\n+\t\tNT_LOG(ERR, NTCONNECT, \"memory allocation failed\");\n+\t\treturn REQUEST_ERR;\n+\t}\n+\tuint64_t *val = snaps->buffer;\n+\n+\tsnaps->snap_addr[SNAP_COLORS].ptr = val;\n+\tpthread_mutex_lock(&stat->drv->ntdrv.stat_lck);\n+\tunsigned int size = read_colors(stat->p_nt4ga_stat, val, nbc);\n+\n+\tif (size != snaps->snap_addr[SNAP_COLORS].size) {\n+\t\tNT_LOG(ERR, NTCONNECT, \"stat.snapshot: color size mismatch\");\n+\t\tgoto err_out;\n+\t}\n+\n+\tval += size;\n+\tsnaps->snap_addr[SNAP_QUEUES].ptr = val;\n+\tsize = read_queues(stat->p_nt4ga_stat, val, nbq);\n+\tif (size != snaps->snap_addr[SNAP_QUEUES].size) {\n+\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t       \"stat.snapshot: queue statistic size mismatch\");\n+\t\tgoto err_out;\n+\t}\n+\n+\tval += size;\n+\tsnaps->snap_addr[SNAP_RX_PORT].ptr = val;\n+\tsize = read_rx_counters(stat->p_nt4ga_stat, val, nbpr);\n+\tif (size != snaps->snap_addr[SNAP_RX_PORT].size) {\n+\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t       \"stat.snapshot: Rx port statistic size mismatch %i, %i\",\n+\t\t       size, snaps->snap_addr[SNAP_RX_PORT].size);\n+\t\tgoto err_out;\n+\t}\n+\n+\tval += size;\n+\tsnaps->snap_addr[SNAP_TX_PORT].ptr = val;\n+\tsize = read_tx_counters(stat->p_nt4ga_stat, val, nbpt);\n+\tif (size != snaps->snap_addr[SNAP_TX_PORT].size) {\n+\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t       \"stat.snapshot: Tx port statistic size mismatch\");\n+\t\tgoto err_out;\n+\t}\n+\n+\tpthread_mutex_unlock(&stat->drv->ntdrv.stat_lck);\n+\n+\t*data = NULL;\n+\t*len = 0;\n+\treturn REQUEST_OK;\n+\n+err_out:\n+\tpthread_mutex_unlock(&stat->drv->ntdrv.stat_lck);\n+\treturn ntconn_error(data, len, \"stat\",\n+\t\t\t    NTCONN_ERR_CODE_INTERNAL_REPLY_ERROR);\n+}\n+\n+static int get_snap_data(void *hdl, int client_id, char **data, int *len,\n+\t\t\t  enum snap_addr_select_e snap_addr_idx)\n+{\n+\tstruct stat_hdl *stat = (struct stat_hdl *)hdl;\n+\tstruct snaps_s *snaps = find_client_snap_data(stat, client_id, NULL);\n+\n+\tif (!snaps || !snaps->buffer)\n+\t\treturn ntconn_error(data, len, \"stat\", NTCONN_ERR_CODE_NO_DATA);\n+\n+\tint ln = snaps->snap_addr[snap_addr_idx].size * sizeof(uint64_t);\n+\n+\t*data = malloc(ln);\n+\tif (!data) {\n+\t\t*len = 0;\n+\t\tNT_LOG(ERR, NTCONNECT, \"memory allocation failed\");\n+\t\treturn REQUEST_ERR;\n+\t}\n+\tmemcpy(*data, snaps->snap_addr[snap_addr_idx].ptr, ln);\n+\t*len = ln;\n+\n+\treturn REQUEST_OK;\n+}\n+\n+static int func_get_snap_colors(void *hdl, int client_id,\n+\t\t\t\tstruct ntconn_header_s *hdr _unused,\n+\t\t\t\tchar **data, int *len)\n+{\n+\treturn get_snap_data(hdl, client_id, data, len, SNAP_COLORS);\n+}\n+\n+static int func_get_snap_queues(void *hdl, int client_id,\n+\t\t\t\tstruct ntconn_header_s *hdr _unused,\n+\t\t\t\tchar **data, int *len)\n+{\n+\treturn get_snap_data(hdl, client_id, data, len, SNAP_QUEUES);\n+}\n+\n+static int func_get_snap_rx_port(void *hdl, int client_id,\n+\t\t\t\t struct ntconn_header_s *hdr _unused,\n+\t\t\t\t char **data, int *len)\n+{\n+\treturn get_snap_data(hdl, client_id, data, len, SNAP_RX_PORT);\n+}\n+\n+static int func_get_snap_tx_port(void *hdl, int client_id,\n+\t\t\t\t struct ntconn_header_s *hdr _unused,\n+\t\t\t\t char **data, int *len)\n+{\n+\treturn get_snap_data(hdl, client_id, data, len, SNAP_TX_PORT);\n+}\n+\n+/*\n+ * Stat main request function\n+ */\n+static int stat_request(void *hdl, int client_id _unused,\n+\t\t\tstruct ntconn_header_s *hdr, char *function,\n+\t\t\tchar **data, int *len)\n+{\n+\treturn execute_function(this_module_name, hdl, client_id, hdr, function,\n+\t\t\t\tstat_entry_funcs, data, len, 0);\n+}\n+\n+static void stat_free_data(void *hdl _unused, char *data)\n+{\n+\tfree(data);\n+}\n+\n+static void stat_client_cleanup(void *hdl, int client_id)\n+{\n+\tstruct stat_hdl *stat = (struct stat_hdl *)hdl;\n+\tstruct snaps_s *snaps_parent;\n+\tstruct snaps_s *snaps =\n+\t\tfind_client_snap_data(stat, client_id, &snaps_parent);\n+\n+\tif (!snaps)\n+\t\treturn;\n+\n+\tif (snaps_parent)\n+\t\tsnaps_parent->next = snaps->next;\n+\telse\n+\t\tstat->snaps_base = snaps->next;\n+\n+\tif (snaps->buffer)\n+\t\tfree(snaps->buffer);\n+\tfree(snaps);\n+}\n+\n+static const ntconnapi_t ntconn_stat_op = {\n+\tthis_module_name, STAT_VERSION_MAJOR, STAT_VERSION_MINOR,\n+\tstat_request,\t  stat_free_data,     stat_client_cleanup\n+};\n+\n+int ntconn_stat_register(struct drv_s *drv)\n+{\n+\tstat_hdl.drv = drv;\n+\tstat_hdl.p_nt4ga_stat = &drv->ntdrv.adapter_info.nt4ga_stat;\n+\n+\t/* Check supported Layout_versions by this module */\n+\tsize_t i;\n+\n+\tfor (i = 0; i < NUM_LAYOUT_VERSIONS_SUPPORTED; i++) {\n+\t\tif (stat_hdl.p_nt4ga_stat->mp_nthw_stat->mn_stat_layout_version ==\n+\t\t\t\tlayout_versions_supported[i])\n+\t\t\tbreak;\n+\t}\n+\n+\tif (i == NUM_LAYOUT_VERSIONS_SUPPORTED) {\n+\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t       \"stat: layout version %i is not supported. Module will not be activated\",\n+\t\t       stat_hdl.p_nt4ga_stat->mp_nthw_stat->mn_stat_layout_version);\n+\t\treturn -1;\n+\t}\n+\n+\treturn register_ntconn_mod(&drv->p_dev->addr, (void *)&stat_hdl,\n+\t\t\t\t   &ntconn_stat_op);\n+}\ndiff --git a/drivers/net/ntnic/ntconnect_modules/ntconn_test.c b/drivers/net/ntnic/ntconnect_modules/ntconn_test.c\nnew file mode 100644\nindex 0000000000..907ea4ff5f\n--- /dev/null\n+++ b/drivers/net/ntnic/ntconnect_modules/ntconn_test.c\n@@ -0,0 +1,146 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2023 Napatech A/S\n+ */\n+\n+#include <errno.h>\n+#include \"ntnic_ethdev.h\"\n+#include \"ntconnect.h\"\n+#include \"ntos_system.h\"\n+#include \"ntconn_modules.h\"\n+#include \"ntconn_mod_helper.h\"\n+#include \"nt_util.h\"\n+#include \"ntlog.h\"\n+#include \"ntnic_vf_vdpa.h\"\n+\n+#include \"ntconnect_api_test.h\"\n+\n+#define NTCONN_TEST_VERSION_MAJOR 0U\n+#define NTCONN_TEST_VERSION_MINOR 1U\n+\n+#define this_module_name \"ntconnect_test\"\n+\n+#define MAX_CLIENTS 32\n+\n+#define UNUSED __rte_unused\n+\n+static struct test_hdl_s {\n+\tstruct drv_s *drv;\n+} test_hdl[MAX_CLIENTS];\n+\n+/*\n+ * Test functions\n+ */\n+static int func_test(void *hdl, int client_id, struct ntconn_header_s *hdr,\n+\t\t     char **data, int *len);\n+static struct func_s adapter_entry_funcs[] = {\n+\t{ \"test\", NULL, func_test },\n+\t{ NULL, NULL, NULL },\n+};\n+\n+static int func_test(void *hdl _unused, int client_id _unused,\n+\t\t     struct ntconn_header_s *hdr, char **data, int *len)\n+{\n+\tint status = 0;\n+\tint number = 0;\n+\tuint32_t size;\n+\tstruct test_s *test_cpy = (struct test_s *)&(*data)[hdr->len];\n+\n+\tif (hdr->blob_len < sizeof(struct test_s)) {\n+\t\tNT_LOG(ERR, NTCONNECT, \"Error in test data: to small\");\n+\t\tstatus = -1;\n+\t\tgoto TEST_ERROR;\n+\t}\n+\n+\tnumber = test_cpy->number;\n+\tsize = sizeof(struct test_s) + sizeof(uint64_t) * number;\n+\n+\tif (hdr->blob_len != size) {\n+\t\tNT_LOG(ERR, NTCONNECT, \"Error in test data: wrong size\");\n+\t\tstatus = -1;\n+\t\tgoto TEST_ERROR;\n+\t}\n+\n+\t{\n+\t\t*data = malloc(sizeof(struct test_s) +\n+\t\t\t       number * sizeof(uint64_t));\n+\t\tif (!*data)\n+\t\t\tgoto TEST_ERROR_MALLOC;\n+\t\tstruct test_s *return_value = (struct test_s *)*data;\n+\t\t*len = sizeof(struct test_s) + number * sizeof(uint64_t);\n+\t\tfor (int i = 0; i < number; i++)\n+\t\t\treturn_value->test[i] = test_cpy->test[i];\n+\t\treturn_value->status = 0;\n+\t\treturn_value->number = number;\n+\t\treturn REQUEST_OK;\n+\t}\n+\n+TEST_ERROR:\n+\n+\t{\n+\t\t*data = malloc(sizeof(struct test_s));\n+\t\tif (!*data)\n+\t\t\tgoto TEST_ERROR_MALLOC;\n+\t\tstruct test_s *return_value = (struct test_s *)*data;\n+\t\t*len = sizeof(struct test_s);\n+\t\treturn_value->status = status;\n+\t\treturn_value->number = 0;\n+\t\treturn REQUEST_OK;\n+\t}\n+\n+TEST_ERROR_MALLOC:\n+\n+\t*len = 0;\n+\tNT_LOG(ERR, NTCONNECT, \"Not able to allocate memory %s\", __func__);\n+\treturn REQUEST_ERR;\n+}\n+\n+enum {\n+\tFLOW_API_FUNC_CREATE,\n+\tFLOW_API_FUNC_VALIDATE,\n+};\n+\n+static int test_request(void *hdl, int client_id _unused,\n+\t\t\tstruct ntconn_header_s *hdr, char *function,\n+\t\t\tchar **data, int *len)\n+{\n+\treturn execute_function(this_module_name, hdl, client_id, hdr, function,\n+\t\t\t\tadapter_entry_funcs, data, len, 0);\n+}\n+\n+static void test_free_data(void *hdl _unused, char *data)\n+{\n+\tif (data)\n+\t\tfree(data);\n+}\n+\n+static void test_client_cleanup(void *hdl _unused, int client_id _unused)\n+{\n+\t/* Nothing to do */\n+}\n+\n+static const ntconnapi_t ntconn_test_op = { this_module_name,\n+\t\t\t\t\t    NTCONN_TEST_VERSION_MAJOR,\n+\t\t\t\t\t    NTCONN_TEST_VERSION_MINOR,\n+\t\t\t\t\t    test_request,\n+\t\t\t\t\t    test_free_data,\n+\t\t\t\t\t    test_client_cleanup\n+\t\t\t\t\t  };\n+\n+int ntconn_test_register(struct drv_s *drv)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < MAX_CLIENTS; i++) {\n+\t\tif (test_hdl[i].drv == NULL)\n+\t\t\tbreak;\n+\t}\n+\tif (i == MAX_CLIENTS) {\n+\t\tNT_LOG(ERR, NTCONNECT,\n+\t\t       \"Cannot register more adapters into NtConnect framework\");\n+\t\treturn -1;\n+\t}\n+\n+\ttest_hdl[i].drv = drv;\n+\treturn register_ntconn_mod(&drv->p_dev->addr, (void *)&test_hdl[i],\n+\t\t\t\t   &ntconn_test_op);\n+}\n",
    "prefixes": [
        "v6",
        "8/8"
    ]
}