aboutsummaryrefslogtreecommitdiffstats
path: root/mpm/tools/tlv_eeprom
diff options
context:
space:
mode:
authorMichael Auchter <michael.auchter@ni.com>2019-09-09 16:46:14 -0500
committerMartin Braun <martin@gnuradio.org>2021-05-31 05:28:47 -0700
commit530cb0a8aaaac906fe124086e780d6651241b0ad (patch)
tree8a5301163392b3d0c1642da0cb9ba2a9ea176401 /mpm/tools/tlv_eeprom
parentfa997d52cc62c3c39f5b7560890e31d41e4eb9e9 (diff)
downloaduhd-530cb0a8aaaac906fe124086e780d6651241b0ad.tar.gz
uhd-530cb0a8aaaac906fe124086e780d6651241b0ad.tar.bz2
uhd-530cb0a8aaaac906fe124086e780d6651241b0ad.zip
mpm: add tlv_eeprom
Add support for parsing an eeprom that uses tag-length-value to store contents. Co-authored-by: Michael Auchter <michael.auchter@ni.com> Co-authored-by: Virendra Kakade <virendra.kakade@ni.com> Co-authored-by: Cristina Fuentes <cristina.fuentes@ni.com>
Diffstat (limited to 'mpm/tools/tlv_eeprom')
-rw-r--r--mpm/tools/tlv_eeprom/CMakeLists.txt40
-rw-r--r--mpm/tools/tlv_eeprom/crc.c71
-rw-r--r--mpm/tools/tlv_eeprom/eeprom-dump.c34
-rw-r--r--mpm/tools/tlv_eeprom/eeprom-id.c66
-rw-r--r--mpm/tools/tlv_eeprom/eeprom-init.c376
-rw-r--r--mpm/tools/tlv_eeprom/eeprom-path36
-rw-r--r--mpm/tools/tlv_eeprom/eeprom-pids.c50
-rw-r--r--mpm/tools/tlv_eeprom/eeprom-pids.h21
-rw-r--r--mpm/tools/tlv_eeprom/eeprom-wrapper13
-rw-r--r--mpm/tools/tlv_eeprom/tlv_eeprom.c98
-rw-r--r--mpm/tools/tlv_eeprom/tlv_eeprom.h104
-rw-r--r--mpm/tools/tlv_eeprom/tlv_eeprom_io.c88
-rw-r--r--mpm/tools/tlv_eeprom/tlv_eeprom_io.h31
-rw-r--r--mpm/tools/tlv_eeprom/usrp_eeprom.h206
14 files changed, 1234 insertions, 0 deletions
diff --git a/mpm/tools/tlv_eeprom/CMakeLists.txt b/mpm/tools/tlv_eeprom/CMakeLists.txt
new file mode 100644
index 000000000..75cf3c9cc
--- /dev/null
+++ b/mpm/tools/tlv_eeprom/CMakeLists.txt
@@ -0,0 +1,40 @@
+#
+# Copyright 2019 Ettus Research, a National Instruments Brand
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+set(eeprom_tool_sources)
+set(eeprom_tool_libs)
+
+if(ENABLE_LIBMPM AND ENABLE_X400)
+ message(STATUS "Adding MPM EEPROM tools...")
+ set(eeprom_tool_libs tlv_eeprom.c tlv_eeprom_io.c crc.c eeprom-pids.c)
+ list(APPEND eeprom_tool_sources
+ eeprom-dump.c
+ eeprom-id.c
+ eeprom-init.c
+ )
+
+ add_executable(eeprom-update-core eeprom-init.c ${eeprom_tool_libs})
+ target_compile_definitions(eeprom-update-core PRIVATE -DTLV_EEPROM_UPDATE)
+ install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/eeprom-update-core DESTINATION ${RUNTIME_DIR})
+ add_custom_target(eeprom-update ALL COMMAND ${CMAKE_COMMAND} -E create_symlink eeprom-wrapper eeprom-update)
+ install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/eeprom-update DESTINATION ${RUNTIME_DIR})
+endif(ENABLE_LIBMPM AND ENABLE_X400)
+
+foreach(eeprom_tool_source ${eeprom_tool_sources})
+ get_filename_component(eeprom_tool ${eeprom_tool_source} NAME_WE)
+ # install eeprom-id, eeprom-dump, etc. with "-core" appendix
+ add_executable(${eeprom_tool}-core ${eeprom_tool_source} ${eeprom_tool_libs})
+ install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${eeprom_tool}-core DESTINATION ${RUNTIME_DIR})
+ # install eeprom-id, eeprom-dump, etc. wrappers
+ add_custom_target(${eeprom_tool} ALL COMMAND ${CMAKE_COMMAND} -E create_symlink eeprom-wrapper ${eeprom_tool})
+ install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${eeprom_tool} DESTINATION ${RUNTIME_DIR})
+endforeach(eeprom_tool_source ${eeprom_tool_sources})
+
+install(PROGRAMS
+ eeprom-path
+ eeprom-wrapper
+ DESTINATION ${RUNTIME_DIR}
+)
diff --git a/mpm/tools/tlv_eeprom/crc.c b/mpm/tools/tlv_eeprom/crc.c
new file mode 100644
index 000000000..df16b71db
--- /dev/null
+++ b/mpm/tools/tlv_eeprom/crc.c
@@ -0,0 +1,71 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include <stdint.h>
+#include "tlv_eeprom.h"
+
+static const uint32_t crc32_tab[] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+static uint32_t crc32(uint32_t crc, const void *buf, size_t size)
+{
+ const uint8_t *p;
+
+ p = buf;
+
+ while (size--)
+ crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+
+ return crc;
+}
+
+uint32_t tlv_eeprom_crc(const struct tlv_eeprom *e)
+{
+ return crc32(0, &e->size, sizeof(e->size) + e->size);
+}
diff --git a/mpm/tools/tlv_eeprom/eeprom-dump.c b/mpm/tools/tlv_eeprom/eeprom-dump.c
new file mode 100644
index 000000000..9afcef431
--- /dev/null
+++ b/mpm/tools/tlv_eeprom/eeprom-dump.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2019 Ettus Research, a National Instruments Brand
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "tlv_eeprom.h"
+#include "tlv_eeprom_io.h"
+#include "usrp_eeprom.h"
+
+int main(int argc, char **argv)
+{
+ struct tlv_eeprom *eeprom;
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s <eeprom>\n", argv[0]);
+ return 1;
+ }
+
+ eeprom = tlv_eeprom_read_from_file(argv[1]);
+ if (!eeprom)
+ return 1;
+
+ if (!tlv_eeprom_validate(eeprom, USRP_EEPROM_MAGIC))
+ tlv_for_each(eeprom->tlv, eeprom->size, usrp_eeprom_trace);
+ else
+ fprintf(stderr, "eeprom contents invalid!\n");
+
+ free(eeprom);
+
+ return 0;
+}
diff --git a/mpm/tools/tlv_eeprom/eeprom-id.c b/mpm/tools/tlv_eeprom/eeprom-id.c
new file mode 100644
index 000000000..e00732f0a
--- /dev/null
+++ b/mpm/tools/tlv_eeprom/eeprom-id.c
@@ -0,0 +1,66 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "tlv_eeprom.h"
+#include "tlv_eeprom_io.h"
+#include "usrp_eeprom.h"
+#include "eeprom-pids.h"
+
+int main(int argc, char **argv)
+{
+ struct tlv_eeprom *eeprom;
+ const struct usrp_eeprom_module_info *module_info;
+ const struct usrp_eeprom_board_info *board_info;
+ const struct pid_info *pid_info;
+ unsigned int pid, rev;
+ const char *serial;
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s <eeprom>\n", argv[0]);
+ return 1;
+ }
+
+ eeprom = tlv_eeprom_read_from_file(argv[1]);
+ if (tlv_eeprom_validate(eeprom, USRP_EEPROM_MAGIC)) {
+ fprintf(stderr, "failed to validate eeprom\n");
+ return 1;
+ }
+
+ module_info = tlv_lookup(eeprom->tlv, eeprom->size,
+ USRP_EEPROM_MODULE_INFO_TAG);
+ board_info = tlv_lookup(eeprom->tlv, eeprom->size,
+ USRP_EEPROM_BOARD_INFO_TAG);
+
+ if (module_info) {
+ serial = module_info->serial;
+ pid = module_info->pid;
+ rev = module_info->rev;
+ } else if (board_info) {
+ serial = board_info->serial;
+ pid = board_info->pid;
+ rev = board_info->rev;
+ } else {
+ fprintf(stderr, "couldn't find module_info or board_info\n");
+ return 1;
+ }
+
+ pid_info = get_info_from_pid(pid);
+
+ if (pid_info && pid_info->name) {
+ printf("product=ni-%s-rev%u\n", pid_info->name, rev + pid_info->rev_offset);
+ } else if (pid_info && pid_info->description) {
+ printf("product=ni-(%s)-rev%u\n", pid_info->description, rev + pid_info->rev_offset);
+ } else {
+ printf("product=unknown-(%04x)\n", pid);
+ }
+ printf("serial=%s\n", serial);
+
+ free(eeprom);
+ return 0;
+}
diff --git a/mpm/tools/tlv_eeprom/eeprom-init.c b/mpm/tools/tlv_eeprom/eeprom-init.c
new file mode 100644
index 000000000..6cca434fc
--- /dev/null
+++ b/mpm/tools/tlv_eeprom/eeprom-init.c
@@ -0,0 +1,376 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2019 Ettus Research, a National Instruments Brand
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tlv_eeprom.h"
+#include "tlv_eeprom_io.h"
+#include "usrp_eeprom.h"
+
+#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((*x)))
+
+int parse_board_info(int argc, char **argv, void *data)
+{
+ struct usrp_eeprom_board_info *info = data;
+
+ if (!argc) {
+ sprintf(data, "<pid> <rev> <compat_rev> <serial>");
+ return 0;
+ }
+
+ assert(argc >= 4);
+
+ info->pid = strtoul(argv[0], NULL, 0);
+ info->rev = strtoul(argv[1], NULL, 0);
+ info->compat_rev = strtoul(argv[2], NULL, 0);
+ strncpy(info->serial, argv[3], 8);
+ info->serial[7] = '\0';
+
+ return 4;
+}
+static struct usrp_eeprom_board_info board_info;
+
+int parse_module_info(int argc, char **argv, void *data)
+{
+ struct usrp_eeprom_module_info *info = data;
+
+ if (!argc) {
+ sprintf(data, "<pid> <rev> <serial>");
+ return 0;
+ }
+
+ assert(argc >= 3);
+
+ info->pid = strtoul(argv[0], NULL, 0);
+ info->rev = strtoul(argv[1], NULL, 0);
+ strncpy(info->serial, argv[2], 8);
+ info->serial[7] = '\0';
+
+ return 3;
+}
+static struct usrp_eeprom_module_info module_info;
+
+int parse_mac(int argc, char **argv, void *data)
+{
+ struct usrp_eeprom_mac_addr *mac = data;
+ int ret;
+
+ if (!argc) {
+ sprintf(data, "<mac_addr>");
+ return 0;
+ }
+
+ assert(argc >= 1);
+
+ ret = sscanf(*argv, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+ mac->addr + 0, mac->addr + 1, mac->addr + 2,
+ mac->addr + 3, mac->addr + 4, mac->addr + 5);
+ assert(ret == 6);
+
+ return 1;
+}
+static struct usrp_eeprom_mac_addr mac_addrs[USRP_EEPROM_MAX_MAC_ADDRS];
+
+int parse_db_pwr_seq(int argc, char **argv, void *data)
+{
+ struct usrp_eeprom_db_pwr_seq *seq = data;
+ uint8_t i;
+
+ if (!argc) {
+ sprintf(data, "<nsteps> [delay supply_mask]...");
+ return 0;
+ }
+
+ assert(argc >= 1);
+ seq->nsteps = strtoul(argv[0], NULL, 0);
+ assert(argc >= 1 + (2 * seq->nsteps));
+
+ argv++;
+ for (i = 0; i < seq->nsteps; i++) {
+ seq->steps[i].delay = strtoul(*argv++, NULL, 0);
+ seq->steps[i].supply_mask = strtoul(*argv++, NULL, 0);
+ }
+
+ return 1 + 2 * i;
+}
+static struct usrp_eeprom_db_pwr_seq db_pwr_seq;
+
+int parse_mcu_flags(int argc, char **argv, void *data)
+{
+ struct usrp_eeprom_mcu_flags *flags = data;
+ unsigned long tmp;
+ int i;
+
+ if (!argc) {
+ sprintf(data, "<flags[0]> <flags[1]> <flags[2]> "
+ "<flags[3]> <flags[4]> <flags[5]>");
+ return 0;
+ }
+
+ assert(argc >= ARRAY_SIZE(flags->flags));
+ for (i = 0; i < ARRAY_SIZE(flags->flags); i++) {
+ tmp = strtoul(argv[i], NULL, 0);
+ assert(tmp <= 0xFF);
+
+ flags->flags[i] = tmp;
+ }
+
+ return i;
+}
+static struct usrp_eeprom_mcu_flags mcu_flags;
+
+int parse_fan_limits(int argc, char **argv, void *data)
+{
+ struct usrp_eeprom_fan_limits *fan_limits = data;
+ int i;
+
+ if (!argc) {
+ sprintf(data, "<fan min rpm> <fan start rpm> <fan max rpm>");
+ return 0;
+ }
+
+ assert(argc >= 3);
+ fan_limits->min = strtoul(argv[0], NULL, 0);
+ fan_limits->start = strtoul(argv[1], NULL, 0);
+ fan_limits->max = strtoul(argv[2], NULL, 0);
+
+ return 3;
+}
+static struct usrp_eeprom_fan_limits fan_limits;
+
+int parse_fan_fixed_capacity(int argc, char **argv, void *data)
+{
+ struct usrp_eeprom_fan_fixed_capacity *fan_fixed_capacity = data;
+
+ if (!argc) {
+ sprintf(data, "<fan fixed capacity>");
+ return 0;
+ }
+
+ fan_fixed_capacity->capacity = strtoul(argv[0], NULL, 0);
+
+ return 1;
+}
+static struct usrp_eeprom_fan_fixed_capacity fan_fixed_capacity;
+
+int parse_clkaux_tuning_word(int argc, char **argv, void *data)
+{
+ struct usrp_eeprom_clkaux_tuning_word *clkaux_tuning_word = data;
+
+ if (!argc) {
+ sprintf(data, "<dac tuning word>");
+ return 0;
+ }
+
+ clkaux_tuning_word->tuning_word = strtoul(argv[0], NULL, 0);
+
+ return 1;
+}
+static struct usrp_eeprom_clkaux_tuning_word clkaux_tuning_word;
+
+struct arg_parser {
+ const char *arg;
+ const char *alias;
+ void *store;
+ size_t size;
+ uint8_t tag;
+ int (*parse)(int, char **, void *);
+ int dirty;
+};
+
+#define PARSER_(name_, alias_, data_, tag_, parser_) { \
+ .arg = #name_, \
+ .alias = alias_, \
+ .store = &(data_), \
+ .size = sizeof((data_)), \
+ .tag = (tag_), \
+ .parse = (parser_), \
+}
+
+#define PARSER(param_, tag_) PARSER_(param_, NULL, param_, tag_, parse_ ##param_)
+#define MAC_PARSER_ALIAS(idx_, alias_) PARSER_( mac_addr_ ## idx_, (alias_), mac_addrs[(idx_)], USRP_EEPROM_MAC_ADDR_TAG((idx_)), parse_mac)
+#define MAC_PARSER(idx_) MAC_PARSER_ALIAS(idx_, NULL)
+
+static struct arg_parser parsers[] = {
+ PARSER(board_info, USRP_EEPROM_BOARD_INFO_TAG),
+ PARSER(module_info, USRP_EEPROM_MODULE_INFO_TAG),
+ MAC_PARSER_ALIAS(0, "eth0_mac"),
+ MAC_PARSER_ALIAS(1, "qsfp0_mac"),
+ MAC_PARSER_ALIAS(2, "qsfp1_mac"),
+ MAC_PARSER(3),
+ MAC_PARSER(4),
+ MAC_PARSER(5),
+ MAC_PARSER(6),
+ MAC_PARSER(7),
+ MAC_PARSER(8),
+ MAC_PARSER(9),
+ MAC_PARSER(10),
+ MAC_PARSER(11),
+ MAC_PARSER(12),
+ PARSER(db_pwr_seq, USRP_EEPROM_DB_PWR_SEQ_TAG),
+ PARSER(mcu_flags, USRP_EEPROM_MCU_FLAGS),
+ PARSER(fan_limits, USRP_EEPROM_FAN_LIMITS),
+ PARSER(fan_fixed_capacity, USRP_EEPROM_FAN_FIXED_CAPACITY),
+ PARSER(clkaux_tuning_word, USRP_EEPROM_CLKAUX_TUNING_WORD),
+};
+
+static struct arg_parser *parser_lookup(const char *arg)
+{
+ for (size_t i = 0; i < ARRAY_SIZE(parsers); i++) {
+ if (!strcmp(parsers[i].arg, arg))
+ return parsers + i;
+ if (parsers[i].alias && !strcmp(parsers[i].alias, arg))
+ return parsers + i;
+ }
+
+ return NULL;
+}
+
+#ifdef TLV_EEPROM_UPDATE
+static void tlv_update(uint8_t tag, uint8_t len, const void *val)
+{
+ struct arg_parser *parser = NULL;
+
+ for (size_t i = 0; i < ARRAY_SIZE(parsers); i++) {
+ if (parsers[i].tag == tag)
+ parser = parsers + i;
+ }
+
+ if (!parser) {
+ fprintf(stderr, "found unknown tag %02x len %u", tag, len);
+ fprintf(stderr, "cannot use this utility to update\n");
+ exit(EXIT_FAILURE);
+ }
+
+ assert(len == parser->size);
+ memcpy(parser->store, val, len);
+ parser->dirty = 1;
+}
+
+static void handle_update(const char *filename)
+{
+ struct tlv_eeprom *eeprom;
+
+ eeprom = tlv_eeprom_read_from_file(filename);
+ if (!eeprom) {
+ perror("failed to read");
+ exit(EXIT_FAILURE);
+ }
+
+ if (tlv_eeprom_validate(eeprom, USRP_EEPROM_MAGIC)) {
+ fprintf(stderr, "contents invalid, cannot update\n");
+ exit(EXIT_FAILURE);
+ }
+
+ tlv_for_each(eeprom->tlv, eeprom->size, tlv_update);
+ free(eeprom);
+}
+#else
+static void handle_update(const char *filename)
+{
+}
+#endif
+
+static void validate_file_matches(const struct tlv_eeprom *eeprom,
+ const char *filename)
+{
+ struct tlv_eeprom *file_eeprom;
+
+ file_eeprom = tlv_eeprom_read_from_file(filename);
+ if (!file_eeprom) {
+ perror("failed to read for validate");
+ exit(EXIT_FAILURE);
+ }
+
+ if (memcmp(file_eeprom, eeprom, sizeof(*eeprom))) {
+ fprintf(stderr, "eeprom validation failed! "
+ "values read do not match what was written. "
+ "perhaps the eeprom was write-protected?\n");
+ exit(EXIT_FAILURE);
+ }
+
+ free(file_eeprom);
+}
+
+static void usage(const char *argv0)
+{
+ const struct arg_parser *p;
+ char buffer[4096];
+
+ fprintf(stderr, "usage: %s <output_file>\n", argv0);
+ for (size_t i = 0; i < ARRAY_SIZE(parsers); i++) {
+ p = parsers + i;
+ p->parse(0, NULL, buffer);
+ fprintf(stderr, "\t--%s %s\n", p->arg, buffer);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct arg_parser *parser;
+ const char *output_file;
+ struct tlv_eeprom eeprom;
+ uint8_t *ptr;
+ int ret;
+
+ if (argc < 2) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ output_file = argv[1];
+ handle_update(output_file);
+
+ argc -= 2;
+ argv += 2;
+
+ while (argc > 0) {
+ if (strncmp("--", *argv, 2)) {
+ fprintf(stderr, "unexpected arg\n");
+ return 1;
+ }
+
+ *argv += 2;
+ parser = parser_lookup(*argv);
+ if (!parser) {
+ fprintf(stderr, "unknown type: %s\n", *argv);
+ return 1;
+ }
+
+ argc--;
+ argv++;
+
+ ret = parser->parse(argc, argv, parser->store);
+ if (ret < 0) {
+ fprintf(stderr, "parsing failed\n");
+ return 1;
+ }
+ parser->dirty = 1;
+
+ argc -= ret;
+ argv += ret;
+ }
+
+ /*
+ * Now that the data's been parsed, write back any changed fields
+ */
+ memset(&eeprom, 0, sizeof(eeprom));
+ ptr = eeprom.tlv;
+ for (size_t i = 0; i < ARRAY_SIZE(parsers); i++) {
+ parser = parsers + i;
+ if (!parser->dirty)
+ continue;
+ ptr += tlv_write(ptr, parser->tag, parser->size, parser->store);
+ }
+
+ tlv_eeprom_seal(&eeprom, USRP_EEPROM_MAGIC, ptr - eeprom.tlv);
+ tlv_eeprom_write_to_file(&eeprom, output_file);
+ validate_file_matches(&eeprom, output_file);
+
+ return 0;
+}
diff --git a/mpm/tools/tlv_eeprom/eeprom-path b/mpm/tools/tlv_eeprom/eeprom-path
new file mode 100644
index 000000000..123c3b7f7
--- /dev/null
+++ b/mpm/tools/tlv_eeprom/eeprom-path
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+
+from pathlib import Path
+import sys
+
+DT_BASE = Path("/sys/firmware/devicetree/base")
+
+def find_eeprom(name):
+ with open(DT_BASE / "__symbols__" / name) as f:
+ dt_path = DT_BASE / f.read()[1:-1]
+
+ for dev in Path("/sys/bus/nvmem/devices").glob("*"):
+ of_node_path = dev / "of_node"
+ if of_node_path.exists() and dt_path.samefile(of_node_path):
+ return dev / "nvmem"
+
+def list_eeproms():
+ return [dev.name for dev in Path(DT_BASE / "__symbols__").glob("*_eeprom")]
+
+if __name__ == "__main__":
+ if len(sys.argv) != 2:
+ print("Return the path to an eeprom")
+ print("usage: %s <eeprom_name>" % sys.argv[0])
+ print("valid eeprom_names: " + ', '.join(list_eeproms()))
+ sys.exit(1)
+ name = sys.argv[1]
+ try:
+ path = find_eeprom(name)
+ if path is None:
+ print("%s: found symbol, but nvmem device not present"% sys.argv[0], file=sys.stderr)
+ sys.exit(1)
+ except:
+ print("%s: could not find nvmem device for devicetree symbol %s" % (sys.argv[0], name), file=sys.stderr)
+ sys.exit(1)
+
+ print(path)
diff --git a/mpm/tools/tlv_eeprom/eeprom-pids.c b/mpm/tools/tlv_eeprom/eeprom-pids.c
new file mode 100644
index 000000000..d181133d7
--- /dev/null
+++ b/mpm/tools/tlv_eeprom/eeprom-pids.c
@@ -0,0 +1,50 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "eeprom-pids.h"
+#include <stddef.h>
+
+static struct pid_info pid_list[] = {
+ { 0x0410, "titanium", "X410 Motherboard", 0 },
+ { 0x4000, NULL, "Power Aux Board", 0 },
+ { 0x4001, NULL, "Debug RF DB", 0 },
+ { 0x4002, "zbx", "Zirconium RF DB", 0},
+ { 0x4003, NULL, "HDMI SE DIO Aux Board", 0},
+ { 0x4004, NULL, "Clocking Aux Board with GPSDO", 0},
+ { 0x4005, NULL, "Clocking Aux Board (no GPSDO)", 0},
+ { 0x4006, NULL, "IF Test Manufacturing CCA", 0},
+};
+
+#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((*x)))
+
+const struct pid_info* get_info_from_pid(uint16_t pid) {
+ for (size_t i = 0; i < ARRAY_SIZE(pid_list); i++)
+ if (pid_list[i].pid == pid)
+ return &pid_list[i];
+
+ return NULL;
+}
+
+const char* get_name_from_pid(uint16_t pid) {
+ const struct pid_info *info = get_info_from_pid(pid);
+ if (!info)
+ return NULL;
+ return info->name;
+}
+
+const char* get_description_from_pid(uint16_t pid) {
+ const struct pid_info *info = get_info_from_pid(pid);
+ if (!info)
+ return NULL;
+ return info->description;
+}
+
+uint16_t get_rev_offset_from_pid(uint16_t pid) {
+ const struct pid_info *info = get_info_from_pid(pid);
+ if (!info)
+ return -1;
+ return info->rev_offset;
+}
diff --git a/mpm/tools/tlv_eeprom/eeprom-pids.h b/mpm/tools/tlv_eeprom/eeprom-pids.h
new file mode 100644
index 000000000..8358d6384
--- /dev/null
+++ b/mpm/tools/tlv_eeprom/eeprom-pids.h
@@ -0,0 +1,21 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#pragma once
+
+#include <stdint.h>
+
+struct pid_info {
+ uint16_t pid;
+ const char* name;
+ const char* description;
+ uint16_t rev_offset;
+};
+
+const struct pid_info* get_info_from_pid(uint16_t pid);
+const char* get_name_from_pid(uint16_t pid);
+const char* get_description_from_pid(uint16_t pid);
+uint16_t get_rev_offset_from_pid(uint16_t pid);
diff --git a/mpm/tools/tlv_eeprom/eeprom-wrapper b/mpm/tools/tlv_eeprom/eeprom-wrapper
new file mode 100644
index 000000000..4e7c7dbfa
--- /dev/null
+++ b/mpm/tools/tlv_eeprom/eeprom-wrapper
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+set -e
+
+util=$(basename $0)
+if [ $# -ge 1 ]; then
+ symbol=$1
+ if [ -n "$symbol" ] && [ "${symbol:(-7)}" != "_eeprom" ]; then
+ symbol="${symbol}_eeprom"
+ fi
+ path=$(eeprom-path $symbol)
+fi
+$util-core $path ${*:2} 2> >( sed "s!-core <\(eeprom\|output_file\)>! <symbol>!" >&2)
diff --git a/mpm/tools/tlv_eeprom/tlv_eeprom.c b/mpm/tools/tlv_eeprom/tlv_eeprom.c
new file mode 100644
index 000000000..8b2e2d9c0
--- /dev/null
+++ b/mpm/tools/tlv_eeprom/tlv_eeprom.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2019 Ettus Research, a National Instruments Brand
+ */
+
+#include <string.h>
+#include "tlv_eeprom.h"
+
+struct tlv_eeprom *tlv_eeprom_init(void *data)
+{
+ struct tlv_eeprom *e = data;
+
+ return e;
+}
+
+void tlv_eeprom_seal(struct tlv_eeprom *e, uint32_t magic, size_t len)
+{
+ e->magic = magic;
+ e->size = len;
+ e->crc = tlv_eeprom_crc(e);
+}
+
+int tlv_eeprom_validate(const struct tlv_eeprom *e, uint32_t magic)
+{
+ uint32_t crc;
+
+ if (e->magic != magic)
+ return -1;
+
+ if (e->size > TLV_EEPROM_SIZE)
+ return -2;
+
+ crc = tlv_eeprom_crc(e);
+ if (e->crc != crc)
+ return -3;
+
+ return 0;
+}
+
+struct tlv {
+ uint8_t tag;
+ uint8_t len;
+ uint8_t val[0];
+};
+
+
+static inline const struct tlv *tlv_next(const struct tlv *tlv, const void *end)
+{
+ const void *p = &tlv->val;
+
+ p += tlv->len;
+ if (p >= end)
+ return NULL;
+ return p;
+}
+
+static inline const struct tlv *tlv_find(const struct tlv *tlv,
+ const void *end, uint8_t tag)
+{
+ do {
+ if (tlv->tag == tag)
+ break;
+ } while ((tlv = tlv_next(tlv, end)));
+
+ return tlv;
+}
+
+void tlv_for_each(const void *buf, size_t bufsz,
+ void (*fn)(uint8_t tag, uint8_t len, const void *val))
+{
+ const struct tlv *tlv = buf;
+
+ do {
+ fn(tlv->tag, tlv->len, tlv->val);
+ } while ((tlv = tlv_next(tlv, buf + bufsz)));
+}
+
+const void *tlv_lookup(const void *buf, size_t bufsz, uint8_t tag)
+{
+ const struct tlv *tlv;
+
+ tlv = tlv_find(buf, buf + bufsz, tag);
+ if (!tlv)
+ return tlv;
+
+ return tlv->val;
+}
+
+size_t tlv_write(void *buf, uint8_t tag, uint8_t len, const void *val)
+{
+ struct tlv *tlv = buf;
+
+ tlv->tag = tag;
+ tlv->len = len;
+ memcpy(tlv->val, val, len);
+
+ return sizeof(*tlv) + len;
+}
diff --git a/mpm/tools/tlv_eeprom/tlv_eeprom.h b/mpm/tools/tlv_eeprom/tlv_eeprom.h
new file mode 100644
index 000000000..319a3cc29
--- /dev/null
+++ b/mpm/tools/tlv_eeprom/tlv_eeprom.h
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2019 Ettus Research, a National Instruments Brand
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+/*
+ * All eeproms on this platform are 256 bytes long
+ */
+#define TLV_EEPROM_SIZE (256)
+
+/**
+ * struct tlv_eeprom - structure describing the eeprom layout
+ *
+ * NOTE: All fields stored to eeprom are little endian, and this library
+ * assumes the processor running it is also little endian (i.e., no endianess
+ * conversion is performed).
+ *
+ * @magic: magic to identify the eeprom
+ * @crc: crc over the size field plus the following size bytes
+ * @size: size of remaining data in bytes
+ * @tlv: up to 244 bytes of tlv data
+ */
+struct tlv_eeprom {
+ uint32_t magic;
+ uint32_t crc;
+ uint32_t size;
+ uint8_t tlv[TLV_EEPROM_SIZE - 3 * sizeof(uint32_t)];
+} __attribute__((packed));
+
+/**
+ * tlv_eeprom_init - create tlv_eeprom from backing store
+ * @data: backing store, which must be at least TLV_EEPROM_SIZE bytes
+ */
+struct tlv_eeprom *tlv_eeprom_init(void *data);
+
+/**
+ * tlv_eeprom_validate - check if an eeprom is valid
+ * @e: eeprom to validate
+ * @magic: expected magic for this eeprom
+ *
+ * Checks to see whether the eeprom is valid. This validates the magic, the
+ * size, and the CRC of the data.
+ *
+ * Return: zero on success, -1 for a magic mismatch, -2 for an invalid size, -3
+ * for an invalid crc
+ */
+int tlv_eeprom_validate(const struct tlv_eeprom *e, uint32_t magic);
+
+/**
+ * tlv_eeprom_seal - seal an eeprom
+ * @e: eeprom to seal
+ * @magic: magic to use for this eeprom
+ * @len: size of tlv data stored in eeprom
+ *
+ * Updates the magic, size, and crc of the eeprom. This should be called prior
+ * to writing an eeprom.
+ */
+void tlv_eeprom_seal(struct tlv_eeprom *e, uint32_t magic, size_t len);
+
+/*
+ * tlv_eeprom_crc - return the CRC for the eeprom
+ * @eeprom: eeprom to compute the crc
+ *
+ * Note: e->size must be valid!
+ *
+ * Returns: the crc
+ */
+extern uint32_t tlv_eeprom_crc(const struct tlv_eeprom *e);
+
+/**
+ * tlv_write - writes a tlv tuple to the buffer
+ * @buf: buffer to write to
+ * @tag: tag for this data
+ * @len: length of value
+ * @val: value to write
+ *
+ * Return: number of bytes written to buffer, including tag and size
+ */
+size_t tlv_write(void *buf, uint8_t tag, uint8_t len, const void *val);
+
+/**
+ * tlv_lookup - lookup the value associated with a tag
+ * @buf: buffer containing tlv data
+ * @bufsz: size of buffer containing tlv data
+ * @tag: tag to lookup
+ *
+ * Return: pointer to value at tag, or NULL if tag was not found
+ */
+const void *tlv_lookup(const void *buf, size_t bufsz, uint8_t tag);
+
+/*
+ * tlv_for_each - call fn for each value in buffer
+ * @buf: buffer containing tlv data
+ * @bufsz: size of buffer containing tlv data
+ * @fn: function to invoke for each value
+ */
+void tlv_for_each(const void *buf, size_t bufsz,
+ void (*fn)(uint8_t tag, uint8_t len, const void *val));
+
diff --git a/mpm/tools/tlv_eeprom/tlv_eeprom_io.c b/mpm/tools/tlv_eeprom/tlv_eeprom_io.c
new file mode 100644
index 000000000..83586e4f6
--- /dev/null
+++ b/mpm/tools/tlv_eeprom/tlv_eeprom_io.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2019 Ettus Research, a National Instruments Brand
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "tlv_eeprom.h"
+
+struct tlv_eeprom *tlv_eeprom_read_from_file(const char *path)
+{
+ struct tlv_eeprom *ep;
+ int fd;
+ uint8_t *ptr;
+ size_t len;
+ ssize_t rd;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ perror("couldn't open file");
+ return NULL;
+ }
+
+
+ ep = malloc(sizeof(struct tlv_eeprom));
+ if (!ep) {
+ perror("alloc failed");
+ return NULL;
+ }
+
+ len = sizeof(struct tlv_eeprom);
+ ptr = (uint8_t *)ep;
+
+ while (len) {
+ rd = read(fd, ptr, len);
+ if (rd < 0) {
+ perror("read failed");
+ goto out;
+ }
+
+ len -= rd;
+ ptr += rd;
+
+ if (!rd)
+ break;
+ }
+
+ return ep;
+out:
+ free(ep);
+ return NULL;
+}
+
+int tlv_eeprom_write_to_file(const struct tlv_eeprom *e, const char *path)
+{
+ int fd, rv = 0;
+ uint8_t *ptr;
+ size_t len;
+ ssize_t wr;
+
+ fd = open(path, O_WRONLY | O_CREAT, 0666);
+ if (fd < 0) {
+ perror("couldn't open file");
+ return -1;
+ }
+
+ len = sizeof(*e);
+ ptr = (uint8_t *)e;
+
+ while (len) {
+ wr = write(fd, ptr, len);
+ if (wr < 0) {
+ perror("error writing file");
+ goto out;
+ }
+ len -= wr;
+ ptr += wr;
+ }
+
+out:
+ close(fd);
+ return rv;
+}
diff --git a/mpm/tools/tlv_eeprom/tlv_eeprom_io.h b/mpm/tools/tlv_eeprom/tlv_eeprom_io.h
new file mode 100644
index 000000000..348bc6207
--- /dev/null
+++ b/mpm/tools/tlv_eeprom/tlv_eeprom_io.h
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2019 Ettus Research, a National Instruments Brand
+ */
+
+#pragma once
+
+#include "tlv_eeprom.h"
+
+/**
+ * tlv_eeprom_write_to_file - write an eeprom to a file
+ * @e: eeprom to write
+ * @path: destination to write to
+ *
+ * Writes the eeprom to the specified path
+ *
+ * Return: zero on success, else negative error code
+ */
+int tlv_eeprom_write_to_file(const struct tlv_eeprom *e, const char *path);
+
+/**
+ * tlv_eeprom_read_from_file - read an eeprom from file
+ * @path: path to read from
+ *
+ * Reads from the path into an allocated eeprom object. No validation of the
+ * data is performed, and caller is responsible for free'ing this.
+ *
+ * Return: eeprom, or NULL if error
+ */
+struct tlv_eeprom *tlv_eeprom_read_from_file(const char *path);
+
diff --git a/mpm/tools/tlv_eeprom/usrp_eeprom.h b/mpm/tools/tlv_eeprom/usrp_eeprom.h
new file mode 100644
index 000000000..224ce8c1b
--- /dev/null
+++ b/mpm/tools/tlv_eeprom/usrp_eeprom.h
@@ -0,0 +1,206 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2019 Ettus Research, a National Instruments Brand
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#define USRP_EEPROM_MAGIC 0x55535250
+
+/**
+ * struct usrp_eeprom_board_info - common board info
+ *
+ * @pid: product id for the board
+ * @rev: current hardware revision, starting at 1
+ * @compat_rev: last hardware revision this is compatible with
+ * @serial: NUL-terminated serial number string
+ */
+#define USRP_EEPROM_BOARD_INFO_TAG (0x10)
+struct usrp_eeprom_board_info {
+ uint16_t pid;
+ uint16_t rev;
+ uint16_t compat_rev;
+ char serial[8];
+} __attribute__((packed));
+
+/**
+ * struct usrp_eeprom_module_info - common module info
+ *
+ * @pid: product id for the module
+ * @rev: module revision, starting at 1
+ * @serial: NUL-terminated serial number string for the module
+ */
+#define USRP_EEPROM_MODULE_INFO_TAG (0x11)
+struct usrp_eeprom_module_info {
+ uint16_t pid;
+ uint16_t rev;
+ char serial[8];
+} __attribute__((packed));
+
+/**
+ * struct usrp_eeprom_mac_addr - mac address
+ *
+ * @addr: 6-byte MAC address
+ */
+
+/* tags 0xA0 through 0xAF are reserved for MAC addresses */
+#define USRP_EEPROM_MAX_MAC_ADDRS 0xF
+#define USRP_EEPROM_MAC_ADDR_TAG(x) (0xA0 + ((x) & USRP_EEPROM_MAX_MAC_ADDRS))
+#define USRP_EEPROM_ETH0_ADDR_TAG USRP_EEPROM_MAC_ADDR_TAG(0)
+#define USRP_EEPROM_QSFP0_ADDR_TAG USRP_EEPROM_MAC_ADDR_TAG(1)
+#define USRP_EEPROM_QSFP1_ADDR_TAG USRP_EEPROM_MAC_ADDR_TAG(2)
+struct usrp_eeprom_mac_addr {
+ uint8_t addr[6];
+} __attribute__((packed));
+
+/**
+ * struct usrp_eeprom_db_pwr_seq - daughterboard power sequence
+ *
+ * @nsteps: the number of steps in the sequence
+ * @steps.delay: delay in milliseconds to wait after enabling supplies
+ * @steps.supply_mask: bitmask of supplies to enable
+ * For X410: bit 0 = 1.8, bit 1 = 2.5, 3.3, 3.7, 12
+ */
+#define USRP_EEPROM_DB_PWR_SEQ_TAG (0x12)
+struct usrp_eeprom_db_pwr_seq {
+ uint8_t nsteps;
+ struct {
+ uint16_t delay;
+ uint8_t supply_mask;
+ } steps[8];
+};
+
+#define USRP_EEPROM_MCU_FLAGS (0x20)
+struct usrp_eeprom_mcu_flags {
+ uint8_t flags[6];
+} __attribute__((packed));
+
+/**
+ * struct usrp_eeprom_fan_limits - fan speed (rpm) limits
+ *
+ * @min: minimum configurable speed
+ * @start: necessary start speed
+ * @max: maximum configurable speed
+ */
+#define USRP_EEPROM_FAN_LIMITS (0x21)
+struct usrp_eeprom_fan_limits {
+ uint16_t min;
+ uint16_t start;
+ uint16_t max;
+} __attribute__((packed));
+
+/**
+ * struct usrp_eeprom_fan_fixed_capacity - fixed cooling capacity
+ *
+ * @capacity: fixed cooling capacity percentage. Range 0-100.
+ * @reserved: extra byte for alignment
+ */
+#define USRP_EEPROM_FAN_FIXED_CAPACITY (0x22)
+struct usrp_eeprom_fan_fixed_capacity {
+ uint8_t capacity;
+ uint8_t reserved; /* for natural alignment */
+} __attribute__((packed));
+
+/**
+ * struct usrp_eeprom_clkaux_tuning_word - clk aux dac tuning word
+ *
+ * @tuning_word: clocking aux board dac tuning word. Range 0-1023.
+ */
+#define USRP_EEPROM_CLKAUX_TUNING_WORD (0x23)
+struct usrp_eeprom_clkaux_tuning_word {
+ uint16_t tuning_word;
+} __attribute__((packed));
+
+#include <stdio.h>
+#include <assert.h>
+
+static void usrp_eeprom_trace(uint8_t tag, uint8_t len, const void *val)
+{
+ uint8_t i;
+
+ switch (tag) {
+ case USRP_EEPROM_BOARD_INFO_TAG:
+ {
+ const struct usrp_eeprom_board_info *v = val;
+ assert(sizeof(*v) == len);
+ printf("%s (0x%02x) ", "usrp_eeprom_board_info", tag);
+ printf("pid: 0x%04x, rev: 0x%04x, compat_rev: 0x%04x, serial: %s\n",
+ v->pid, v->rev, v->compat_rev, v->serial);
+ }
+ break;
+ case USRP_EEPROM_MODULE_INFO_TAG:
+ {
+ const struct usrp_eeprom_module_info *v = val;
+ assert(sizeof(*v) == len);
+ printf("%s (0x%02x) ", "usrp_eeprom_module_info", tag);
+ printf("pid: 0x%04x, rev: 0x%04x, serial: %s\n",
+ v->pid, v->rev, v->serial);
+ }
+ break;
+ case USRP_EEPROM_MAC_ADDR_TAG(0) ... USRP_EEPROM_MAC_ADDR_TAG(USRP_EEPROM_MAX_MAC_ADDRS):
+ {
+ const struct usrp_eeprom_mac_addr *v = val;
+ assert(sizeof(*v) == len);
+ printf("%s mac_addr_%d (0x%02x) ", "usrp_eeprom_mac_addr",
+ tag - USRP_EEPROM_MAC_ADDR_TAG(0), tag);
+ for (i = 0; i < 6; i++)
+ printf("%02x%c", v->addr[i], i == 5 ? ' ' : ':');
+ printf("\n");
+ }
+ break;
+ case USRP_EEPROM_DB_PWR_SEQ_TAG:
+ {
+ const struct usrp_eeprom_db_pwr_seq *v = val;
+ assert(sizeof(*v) == len);
+ printf("%s (0x%02x) ", "usrp_eeprom_db_pwr_seq", tag);
+ for (i = 0; i < 8; i++)
+ printf("(%u, 0x%02x) ", v->steps[i].delay, v->steps[i].supply_mask);
+ printf("\n");
+ }
+ break;
+ case USRP_EEPROM_MCU_FLAGS:
+ {
+ const struct usrp_eeprom_mcu_flags *v = val;
+ printf("%s (0x%02x) ", "usrp_eeprom_mcu_flags", tag);
+ for (i = 0; i < 6; i++)
+ printf("0x%02x ", v->flags[i]);
+ printf("\n");
+ }
+ break;
+ case USRP_EEPROM_FAN_LIMITS:
+ {
+ const struct usrp_eeprom_fan_limits *v = val;
+ printf("%s (0x%02x) ", "usrp_eeprom_fan_limits", tag);
+ printf("min: %d, start: %d, max: %d", v->min, v->start, v->max);
+ printf("\n");
+ }
+ break;
+ case USRP_EEPROM_FAN_FIXED_CAPACITY:
+ {
+ const struct usrp_eeprom_fan_fixed_capacity *v = val;
+ printf("%s (0x%02x) ", "usrp_eeprom_fan_fixed_capacity", tag);
+ printf("%d", v->capacity);
+ printf("\n");
+ }
+ break;
+ case USRP_EEPROM_CLKAUX_TUNING_WORD:
+ {
+ const struct usrp_eeprom_clkaux_tuning_word *v = val;
+ printf("%s (0x%02x) ", "usrp_eeprom_clkaux_tuning_word", tag);
+ printf("%d", v->tuning_word);
+ printf("\n");
+ }
+ break;
+ default:
+ {
+ const uint8_t *ptr = val;
+ printf("%s (0x%02x) len: %hhu, val: ", "unknown", tag, len);
+ for (i = 0; i < len; i++)
+ printf("%02x ", ptr[i]);
+ printf("\n");
+ break;
+ }
+ }
+}