immortalwrt/target/linux/generic/pending-6.6/809-02-nvmem-layouts-add-support-for-ascii-env-driver.patch
Christian Marangi 73a6cb983c
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>
2025-02-03 01:54:26 +01:00

188 lines
5.2 KiB
Diff

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");