generic: add pending support for NVMEM ASCII ENV layout driver

Add pending patch to support NVMEM ASCII ENV layout driver. This is a
generic driver to handle simple NVMEM partition that store environment
in a simple text format. This is the case for Linksys devinfo partition
that are litterally txt file with format "name=value\n"

Such driver works similar to u-boot,env with a similar format. While at
it also introduce a patch to generalize mac-base handling for also other
layout driver.

Link: https://github.com/openwrt/openwrt/pull/17839
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
This commit is contained in:
Christian Marangi 2025-02-03 01:08:35 +01:00
parent eba2fbf638
commit 73a6cb983c
No known key found for this signature in database
GPG Key ID: AC001D09ADBFEAD7
3 changed files with 286 additions and 0 deletions

View File

@ -4346,6 +4346,7 @@ CONFIG_NLS_DEFAULT="iso8859-1"
# CONFIG_NVMEM_LAYOUT_ONIE_TLV is not set
# CONFIG_NVMEM_LAYOUT_SL28_VPD is not set
# CONFIG_NVMEM_LAYOUT_U_BOOT_ENV is not set
# CONFIG_NVMEM_LAYOUT_ASCII_ENV is not set
# CONFIG_NVMEM_REBOOT_MODE is not set
# CONFIG_NVMEM_RMEM is not set
# CONFIG_NVMEM_SYSFS is not set

View File

@ -0,0 +1,98 @@
From 995a6e0d3fdd1e4fb38465f224db8a4c7b1e279d Mon Sep 17 00:00:00 2001
From: Christian Marangi <ansuelsmth@gmail.com>
Date: Mon, 3 Feb 2025 00:10:18 +0100
Subject: [PATCH 1/2] nvmem: core: generalize "mac-base" cells handling
Generalize support of "mac-base" nvmem cells and provide a GPL symbol to
permit also other NVMEM layout driver to parse mac-base cells.
It's VERY COMMON for some specially formatted NVMEM to expose a mac
address in ASCII format or HEX format hence prevent code duplication by
exposing a common helper.
Such helper will change the nvmem_info_cell and apply the correct post
process function to correctly parse the mac address.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/nvmem/core.c | 41 +++++++++++++++++++---------------
include/linux/nvmem-provider.h | 4 ++++
2 files changed, 27 insertions(+), 18 deletions(-)
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -855,6 +855,27 @@ static int nvmem_mac_base_hex_read(void
return 0;
}
+void nvmem_layout_parse_mac_base(struct nvmem_cell_info *info)
+{
+ if (!of_device_is_compatible(info->np, "mac-base"))
+ return;
+
+ if (info->bytes == ETH_ALEN) {
+ info->raw_len = info->bytes;
+ info->bytes = ETH_ALEN;
+ info->read_post_process = nvmem_mac_base_raw_read;
+ } else if (info->bytes == 2 * ETH_ALEN) {
+ info->raw_len = info->bytes;
+ info->bytes = ETH_ALEN;
+ info->read_post_process = nvmem_mac_base_hex_read;
+ } else if (info->bytes == 3 * ETH_ALEN - 1) {
+ info->raw_len = info->bytes;
+ info->bytes = ETH_ALEN;
+ info->read_post_process = nvmem_mac_base_ascii_read;
+ }
+}
+EXPORT_SYMBOL_GPL(nvmem_layout_parse_mac_base);
+
static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np)
{
struct device *dev = &nvmem->dev;
@@ -894,24 +915,8 @@ static int nvmem_add_cells_from_dt(struc
if (nvmem->fixup_dt_cell_info)
nvmem->fixup_dt_cell_info(nvmem, &info);
- if (of_device_is_compatible(np, "fixed-layout")) {
- if (of_device_is_compatible(child, "mac-base")) {
- if (info.bytes == ETH_ALEN) {
- info.raw_len = info.bytes;
- info.bytes = ETH_ALEN;
- info.read_post_process = nvmem_mac_base_raw_read;
- } else if (info.bytes == 2 * ETH_ALEN) {
- info.raw_len = info.bytes;
- info.bytes = ETH_ALEN;
- info.read_post_process = nvmem_mac_base_hex_read;
- } else if (info.bytes == 3 * ETH_ALEN - 1) {
- info.raw_len = info.bytes;
- info.bytes = ETH_ALEN;
- info.read_post_process = nvmem_mac_base_ascii_read;
- }
-
- }
- }
+ if (of_device_is_compatible(np, "fixed-layout"))
+ nvmem_layout_parse_mac_base(&info);
ret = nvmem_add_one_cell(nvmem, &info);
kfree(info.name);
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -242,6 +242,8 @@ static inline void nvmem_layout_unregist
#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF)
+void nvmem_layout_parse_mac_base(struct nvmem_cell_info *info);
+
/**
* of_nvmem_layout_get_container() - Get OF node of layout container
*
@@ -254,6 +256,8 @@ struct device_node *of_nvmem_layout_get_
#else /* CONFIG_NVMEM && CONFIG_OF */
+static inline void nvmem_layout_parse_mac_base(void) {}
+
static inline struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
{
return NULL;

View File

@ -0,0 +1,187 @@
From 38287e8ec5c0281377fc70f11f20bcd9986a05f5 Mon Sep 17 00:00:00 2001
From: Christian Marangi <ansuelsmth@gmail.com>
Date: Mon, 3 Feb 2025 00:36:12 +0100
Subject: [PATCH 2/2] nvmem: layouts: add support for ascii-env driver
Add support for simple ASCII envirorment driver for NVMEM layouts.
It's very common for devices to store simple text file format in
partition for environment varibles. The most common pattern is variable
name, a delimiter and variable value all separated by a new line
character (\n).
This driver adds support for exporting such data and expose NVMEM cells
so they can be referenced by other drivers. This driver also supports
parsing mac-base NVMEM cells to parse ASCII or HEX mac address.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/nvmem/layouts/Kconfig | 13 +++
drivers/nvmem/layouts/Makefile | 1 +
drivers/nvmem/layouts/ascii-env.c | 131 ++++++++++++++++++++++++++++++
3 files changed, 145 insertions(+)
create mode 100644 drivers/nvmem/layouts/ascii-env.c
--- a/drivers/nvmem/layouts/Kconfig
+++ b/drivers/nvmem/layouts/Kconfig
@@ -37,6 +37,19 @@ config NVMEM_LAYOUT_U_BOOT_ENV
If unsure, say N.
+config NVMEM_LAYOUT_ASCII_ENV
+ tristate "ASCII environment variables layout"
+ help
+ It's very common for devices to store simple text file format in
+ partition for environment varibles. The most common pattern is variable
+ name, a delimiter and variable value all separated by a new line
+ character (\n).
+ This driver adds support for exporting such data and expose NVMEM cells
+ so they can be referenced by other drivers. This driver also supports
+ parsing mac-base NVMEM cells to parse ASCII or HEX mac address.
+
+ If unsure, say N.
+
endmenu
endif
--- a/drivers/nvmem/layouts/Makefile
+++ b/drivers/nvmem/layouts/Makefile
@@ -6,3 +6,4 @@
obj-$(CONFIG_NVMEM_LAYOUT_SL28_VPD) += sl28vpd.o
obj-$(CONFIG_NVMEM_LAYOUT_ONIE_TLV) += onie-tlv.o
obj-$(CONFIG_NVMEM_LAYOUT_U_BOOT_ENV) += u-boot-env.o
+obj-$(CONFIG_NVMEM_LAYOUT_ASCII_ENV) += ascii-env.o
--- /dev/null
+++ b/drivers/nvmem/layouts/ascii-env.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024 Christian Marangi <ansuelsmth@gmail.com>
+ *
+ * This borrow some parse logic from u-boot-env.
+ */
+#include <linux/nvmem-consumer.h>
+#include <linux/nvmem-provider.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+/*
+ * Parse a buffer as an ASCII text with name delimiter value and each pattern separated
+ * with a new line char '\n'
+ * Example: (delimiter '=')
+ * name=value\nname2=value2\n
+ * 2 Cell:
+ * - name: value
+ * - name2: value2
+ */
+static int ascii_env_parse_cells(struct device *dev, struct nvmem_device *nvmem, uint8_t *buf,
+ size_t data_len, const char delim)
+{
+ char *var, *value, *eq, *lf;
+ char *data = buf;
+
+ /*
+ * Warning the inner loop take care of replacing '\n'
+ * with '\0', hence we can use strlen on value.
+ */
+ for (var = data; var < data + data_len && *var;
+ var = value + strlen(value) + 1) {
+ struct nvmem_cell_info info = {};
+
+ eq = strchr(var, delim);
+ if (!eq)
+ break;
+ *eq = '\0';
+ value = eq + 1;
+
+ /* Replace '\n' with '\0' to use strlen for value */
+ lf = strchr(value, '\n');
+ if (!lf)
+ break;
+ *lf = '\0';
+
+ info.name = devm_kstrdup(dev, var, GFP_KERNEL);
+ if (!info.name)
+ return -ENOMEM;
+ info.offset = value - data;
+ info.bytes = strlen(value);
+ info.np = of_get_child_by_name(dev->of_node, info.name);
+
+ nvmem_layout_parse_mac_base(&info);
+
+ nvmem_add_one_cell(nvmem, &info);
+ }
+
+ return 0;
+}
+
+static int ascii_env_add_cells(struct nvmem_layout *layout)
+{
+ struct nvmem_device *nvmem = layout->nvmem;
+ struct device *dev = &layout->dev;
+ size_t dev_size;
+ uint8_t *buf;
+ char delim;
+ int bytes;
+ int ret;
+
+ /* Get the delimiter for name value pattern */
+ delim = device_get_match_data(dev);
+
+ dev_size = nvmem_dev_size(nvmem);
+
+ buf = kzalloc(dev_size, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ bytes = nvmem_device_read(nvmem, 0, dev_size, buf);
+ if (bytes < 0) {
+ ret = bytes;
+ goto err_kfree;
+ } else if (bytes != dev_size) {
+ ret = -EIO;
+ goto err_kfree;
+ }
+
+ buf[dev_size - 1] = '\0';
+ ret = ascii_env_parse_cells(dev, nvmem, buf, dev_size, delim);
+
+err_kfree:
+ kfree(buf);
+err_out:
+ return ret;
+}
+
+static int ascii_env_probe(struct nvmem_layout *layout)
+{
+ layout->add_cells = ascii_env_add_cells;
+
+ return nvmem_layout_register(layout);
+}
+
+static void ascii_env_remove(struct nvmem_layout *layout)
+{
+ nvmem_layout_unregister(layout);
+}
+
+static const struct of_device_id ascii_env_of_match_table[] = {
+ { .compatible = "ascii-eq-delim-env", .data = (void *)'=', },
+ {},
+};
+
+static struct nvmem_layout_driver ascii_env_layout = {
+ .driver = {
+ .name = "ascii-env-layout",
+ .of_match_table = ascii_env_of_match_table,
+ },
+ .probe = ascii_env_probe,
+ .remove = ascii_env_remove,
+};
+module_nvmem_layout_driver(ascii_env_layout);
+
+MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(of, ascii_env_of_match_table);
+MODULE_DESCRIPTION("NVMEM layout driver for ASCII environment variables");