[PATCH v2 4/4] test: add ethdev module EEPROM decoding tests

Roman Khromenok roma55592 at yandex.ru
Mon Sep 28 10:47:29 CEST 2026


Add unit tests for rte_eth_module_eeprom_parse().

The tests decode SFF-8079, SFF-8472 and SFF-8636 EEPROM images
built in the test, check the identification and diagnostic fields,
and check that the SFF-8472 diagnostics are skipped when page A2h
is not complete.
Invalid parameters, too short data and unknown module types
must be rejected without invoking the callback.

Signed-off-by: Roman Khromenok <roma55592 at yandex.ru>
---
 app/test/meson.build                 |   1 +
 app/test/test_ethdev_module_eeprom.c | 285 +++++++++++++++++++++++++++
 2 files changed, 286 insertions(+)
 create mode 100644 app/test/test_ethdev_module_eeprom.c

diff --git a/app/test/meson.build b/app/test/meson.build
index 9dcdb069e8..c6a808d63d 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -74,6 +74,7 @@ source_file_deps = {
     'test_errno.c': [],
     'test_ethdev_api.c': ['ethdev'],
     'test_ethdev_link.c': ['ethdev'],
+    'test_ethdev_module_eeprom.c': ['ethdev'],
     'test_event_crypto_adapter.c': ['cryptodev', 'eventdev', 'bus_vdev'],
     'test_event_dma_adapter.c': ['dmadev', 'eventdev', 'bus_vdev'],
     'test_event_eth_rx_adapter.c': ['ethdev', 'eventdev', 'bus_vdev'],
diff --git a/app/test/test_ethdev_module_eeprom.c b/app/test/test_ethdev_module_eeprom.c
new file mode 100644
index 0000000000..8a09b870da
--- /dev/null
+++ b/app/test/test_ethdev_module_eeprom.c
@@ -0,0 +1,285 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2026 Roman Khromenok
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_ethdev.h>
+#include <rte_string_fns.h>
+
+#include "test.h"
+
+#define MAX_FIELDS 128
+#define MAX_NAME_LEN 64
+#define MAX_VALUE_LEN 128
+
+struct decoded_fields {
+	unsigned int count;
+	struct {
+		char name[MAX_NAME_LEN];
+		char value[MAX_VALUE_LEN];
+	} field[MAX_FIELDS];
+};
+
+static struct decoded_fields fields;
+
+static void
+collect_field(const char *name, const char *value, void *arg)
+{
+	struct decoded_fields *f = arg;
+
+	if (f->count >= MAX_FIELDS)
+		return;
+	strlcpy(f->field[f->count].name, name, MAX_NAME_LEN);
+	strlcpy(f->field[f->count].value, value, MAX_VALUE_LEN);
+	f->count++;
+}
+
+static const char *
+find_field(const char *name)
+{
+	unsigned int i;
+
+	for (i = 0; i < fields.count; i++)
+		if (strcmp(fields.field[i].name, name) == 0)
+			return fields.field[i].value;
+	return NULL;
+}
+
+static int
+parse(uint32_t type, const uint8_t *data, uint32_t length)
+{
+	memset(&fields, 0, sizeof(fields));
+	return rte_eth_module_eeprom_parse(type, data, length, collect_field, &fields);
+}
+
+#define CHECK_FIELD(name, expected) do { \
+	const char *value = find_field(name); \
+	TEST_ASSERT_NOT_NULL(value, "Field \"%s\" not found", name); \
+	TEST_ASSERT(strcmp(value, expected) == 0, \
+		"Field \"%s\": expected \"%s\", got \"%s\"", name, expected, value); \
+} while (0)
+
+#define CHECK_NO_FIELD(name) \
+	TEST_ASSERT_NULL(find_field(name), "Unexpected field \"%s\"", name)
+
+static void
+put_u16(uint8_t *data, unsigned int offset, uint16_t value)
+{
+	data[offset] = value >> 8;
+	data[offset + 1] = value & 0xff;
+}
+
+/* SFP+ 10GBASE-LR module, SFF-8472 layout: page A0h then page A2h */
+static void
+fill_sfp(uint8_t *data)
+{
+	uint8_t *a2 = data + RTE_ETH_MODULE_SFF_8079_LEN;
+
+	memset(data, 0, RTE_ETH_MODULE_SFF_8472_LEN);
+	data[0] = 0x03;		/* identifier: SFP */
+	data[1] = 0x04;		/* extended identifier */
+	data[2] = 0x07;		/* connector: LC */
+	data[3] = 0x20;		/* 10G Base-LR */
+	data[11] = 0x06;	/* encoding: 64B/66B */
+	data[12] = 103;		/* nominal bit rate, units of 100 MBd */
+	memcpy(&data[20], "STARRY          ", 16);
+	data[37] = 0x00;
+	data[38] = 0x1b;
+	data[39] = 0x21;
+	memcpy(&data[40], "SFP-10G-LR-20   ", 16);
+	memcpy(&data[56], "A   ", 4);
+	memcpy(&data[68], "2024082600001   ", 16);
+	memcpy(&data[84], "240902  ", 8);
+	/* diagnostics implemented, internally calibrated, average RX power */
+	data[92] = 0x68;
+
+	put_u16(a2, 96, 0x2940);	/* temperature: 41.25 C */
+	put_u16(a2, 98, 33205);		/* voltage: 3.3205 V */
+	put_u16(a2, 100, 19790);	/* TX bias: 39.580 mA */
+	put_u16(a2, 102, 7754);		/* TX power: 0.7754 mW */
+	put_u16(a2, 104, 6724);		/* RX power: 0.6724 mW */
+}
+
+/* QSFP28 module, SFF-8636 layout: lower page then upper page 00h */
+static void
+fill_qsfp(uint8_t *data)
+{
+	memset(data, 0, RTE_ETH_MODULE_SFF_8636_LEN);
+	data[0] = 0x11;		/* identifier: QSFP28 */
+	put_u16(data, 22, 0x3040);	/* temperature: 48.25 C */
+	put_u16(data, 26, 32638);	/* voltage: 3.2638 V */
+	put_u16(data, 34, 1);		/* channel 1 RX power: 0.0001 mW */
+	put_u16(data, 42, 21525);	/* channel 1 TX bias: 43.050 mA */
+	put_u16(data, 50, 12763);	/* channel 1 TX power: 1.2763 mW */
+
+	data[128] = 0x11;	/* identifier: QSFP28 */
+	data[130] = 0x07;	/* connector: LC */
+	memcpy(&data[148], "FINISAR CORP.   ", 16);
+	data[165] = 0x00;
+	data[166] = 0x90;
+	data[167] = 0x65;
+	memcpy(&data[168], "FTLC1157RGPL6-FB", 16);
+	memcpy(&data[184], "A0", 2);
+	memcpy(&data[196], "X24A15P         ", 16);
+	memcpy(&data[212], "190826  ", 8);
+	data[220] = 0x08;	/* average RX power */
+}
+
+static int
+check_sfp_identity(void)
+{
+	CHECK_FIELD("Identifier", "0x03 (SFP)");
+	CHECK_FIELD("Connector", "0x07 (LC)");
+	CHECK_FIELD("Vendor name", "STARRY");
+	CHECK_FIELD("Vendor OUI", "00:1b:21");
+	CHECK_FIELD("Vendor PN", "SFP-10G-LR-20");
+	CHECK_FIELD("Vendor rev", "A");
+	CHECK_FIELD("Vendor SN", "2024082600001");
+	CHECK_FIELD("Date code", "240902");
+	return TEST_SUCCESS;
+}
+
+static int
+test_module_eeprom_sfp_8079(void)
+{
+	uint8_t data[RTE_ETH_MODULE_SFF_8472_LEN];
+
+	fill_sfp(data);
+	TEST_ASSERT_SUCCESS(parse(RTE_ETH_MODULE_SFF_8079, data, RTE_ETH_MODULE_SFF_8079_LEN),
+		"Failed to parse SFF-8079 data");
+	TEST_ASSERT_SUCCESS(check_sfp_identity(), "Wrong SFF-8079 base information");
+	CHECK_NO_FIELD("Optical diagnostics support");
+	return TEST_SUCCESS;
+}
+
+static int
+test_module_eeprom_sfp_8472(void)
+{
+	uint8_t data[RTE_ETH_MODULE_SFF_8472_LEN];
+
+	fill_sfp(data);
+	TEST_ASSERT_SUCCESS(parse(RTE_ETH_MODULE_SFF_8472, data, sizeof(data)),
+		"Failed to parse SFF-8472 data");
+	TEST_ASSERT_SUCCESS(check_sfp_identity(), "Wrong SFF-8472 base information");
+	CHECK_FIELD("Optical diagnostics support", "Yes");
+	CHECK_FIELD("Laser bias current", "39.580 mA");
+	CHECK_FIELD("Laser output power", "0.7754 mW / -1.10 dBm");
+	CHECK_FIELD("Receiver signal average optical power", "0.6724 mW / -1.72 dBm");
+	CHECK_FIELD("Module temperature", "41.25 degrees C / 106.25 degrees F");
+	CHECK_FIELD("Module voltage", "3.3205 V");
+	return TEST_SUCCESS;
+}
+
+static int
+test_module_eeprom_sfp_8472_short(void)
+{
+	uint8_t data[RTE_ETH_MODULE_SFF_8472_LEN];
+	const uint32_t lengths[] = {
+		RTE_ETH_MODULE_SFF_8079_LEN,
+		RTE_ETH_MODULE_SFF_8472_LEN - 1,
+	};
+	unsigned int i;
+
+	fill_sfp(data);
+	for (i = 0; i < RTE_DIM(lengths); i++) {
+		TEST_ASSERT_SUCCESS(parse(RTE_ETH_MODULE_SFF_8472, data, lengths[i]),
+			"Failed to parse SFF-8472 data of %u bytes", lengths[i]);
+		TEST_ASSERT_SUCCESS(check_sfp_identity(),
+			"Wrong SFF-8472 base information from %u bytes", lengths[i]);
+		/* page A2h is not complete, diagnostics must be skipped */
+		CHECK_NO_FIELD("Optical diagnostics support");
+		CHECK_NO_FIELD("Laser bias current");
+	}
+	return TEST_SUCCESS;
+}
+
+static int
+test_module_eeprom_qsfp_8636(void)
+{
+	uint8_t data[RTE_ETH_MODULE_SFF_8636_LEN];
+
+	fill_qsfp(data);
+	TEST_ASSERT_SUCCESS(parse(RTE_ETH_MODULE_SFF_8636, data, sizeof(data)),
+		"Failed to parse SFF-8636 data");
+	CHECK_FIELD("Identifier", "0x11 (QSFP28)");
+	CHECK_FIELD("Connector", "0x07 (LC)");
+	CHECK_FIELD("Vendor name", "FINISAR CORP.");
+	CHECK_FIELD("Vendor OUI", "00:90:65");
+	CHECK_FIELD("Vendor PN", "FTLC1157RGPL6-FB");
+	CHECK_FIELD("Vendor rev", "A0");
+	CHECK_FIELD("Vendor SN", "X24A15P");
+	CHECK_FIELD("Module temperature", "48.25 degrees C / 118.85 degrees F");
+	CHECK_FIELD("Module voltage", "3.2638 V");
+	/* page 03h is not provided */
+	CHECK_FIELD("Alarm/warning flags implemented", "No");
+	CHECK_FIELD("Laser tx bias current (Channel 1)", "43.050 mA");
+	CHECK_FIELD("Transmit avg optical power (Channel 1)", "1.2763 mW / 1.06 dBm");
+	CHECK_FIELD("Rcvr signal avg optical power(Channel 1)", "0.0001 mW / -40.00 dBm");
+	return TEST_SUCCESS;
+}
+
+static int
+test_module_eeprom_invalid(void)
+{
+	uint8_t data[RTE_ETH_MODULE_SFF_8636_MAX_LEN] = {0};
+	const uint32_t types[] = {
+		RTE_ETH_MODULE_SFF_8079,
+		RTE_ETH_MODULE_SFF_8472,
+		RTE_ETH_MODULE_SFF_8436,
+		RTE_ETH_MODULE_SFF_8636,
+	};
+	unsigned int i;
+	int ret;
+
+	/* all supported types require at least one full page */
+	for (i = 0; i < RTE_DIM(types); i++) {
+		ret = parse(types[i], data, 255);
+		TEST_ASSERT_EQUAL(ret, -EINVAL,
+			"Type %u with 255 bytes: expected %d, got %d", types[i], -EINVAL, ret);
+		TEST_ASSERT_EQUAL(fields.count, 0,
+			"Type %u with 255 bytes: callback called %u times",
+			types[i], fields.count);
+	}
+
+	ret = parse(0, data, sizeof(data));
+	TEST_ASSERT_EQUAL(ret, -ENOTSUP, "Type 0: expected %d, got %d", -ENOTSUP, ret);
+	ret = parse(RTE_ETH_MODULE_SFF_8436 + 1, data, sizeof(data));
+	TEST_ASSERT_EQUAL(ret, -ENOTSUP, "Unknown type: expected %d, got %d", -ENOTSUP, ret);
+	TEST_ASSERT_EQUAL(fields.count, 0, "Unknown type: callback called %u times",
+		fields.count);
+
+	ret = rte_eth_module_eeprom_parse(RTE_ETH_MODULE_SFF_8079, NULL,
+		RTE_ETH_MODULE_SFF_8079_LEN, collect_field, &fields);
+	TEST_ASSERT_EQUAL(ret, -EINVAL, "NULL data: expected %d, got %d", -EINVAL, ret);
+	ret = rte_eth_module_eeprom_parse(RTE_ETH_MODULE_SFF_8079, data,
+		RTE_ETH_MODULE_SFF_8079_LEN, NULL, NULL);
+	TEST_ASSERT_EQUAL(ret, -EINVAL, "NULL callback: expected %d, got %d", -EINVAL, ret);
+
+	return TEST_SUCCESS;
+}
+
+static struct unit_test_suite module_eeprom_testsuite = {
+	.suite_name = "ethdev module EEPROM decoding",
+	.setup = NULL,
+	.teardown = NULL,
+	.unit_test_cases = {
+		TEST_CASE(test_module_eeprom_sfp_8079),
+		TEST_CASE(test_module_eeprom_sfp_8472),
+		TEST_CASE(test_module_eeprom_sfp_8472_short),
+		TEST_CASE(test_module_eeprom_qsfp_8636),
+		TEST_CASE(test_module_eeprom_invalid),
+		TEST_CASES_END()
+	}
+};
+
+static int
+test_module_eeprom(void)
+{
+	return unit_test_suite_runner(&module_eeprom_testsuite);
+}
+
+REGISTER_FAST_TEST(ethdev_module_eeprom, NOHUGE_OK, ASAN_OK, test_module_eeprom);
-- 
2.47.3



More information about the dev mailing list