generic: add pending support for NVMEM ASCII ENV layout driver
authorChristian Marangi <ansuelsmth@gmail.com>
Mon, 3 Feb 2025 00:08:35 +0000 (01:08 +0100)
committerChristian Marangi <ansuelsmth@gmail.com>
Mon, 3 Feb 2025 00:54:26 +0000 (01:54 +0100)
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>
target/linux/generic/config-6.6
target/linux/generic/pending-6.6/809-01-nvmem-core-generalize-mac-base-cells-handling.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/809-02-nvmem-layouts-add-support-for-ascii-env-driver.patch [new file with mode: 0644]

index a92151700d7012b386a71db35166461128e6bf64..b83aecdc88636cfa3bd947853ba2801870285a43 100644 (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
diff --git a/target/linux/generic/pending-6.6/809-01-nvmem-core-generalize-mac-base-cells-handling.patch b/target/linux/generic/pending-6.6/809-01-nvmem-core-generalize-mac-base-cells-handling.patch
new file mode 100644 (file)
index 0000000..9cdee61
--- /dev/null
@@ -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;
diff --git a/target/linux/generic/pending-6.6/809-02-nvmem-layouts-add-support-for-ascii-env-driver.patch b/target/linux/generic/pending-6.6/809-02-nvmem-layouts-add-support-for-ascii-env-driver.patch
new file mode 100644 (file)
index 0000000..dd4af0f
--- /dev/null
@@ -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");