[PATCH 2/4] nvmem: core: allow nvmem_cell_post_process_t callbacks to adjust buffer

From: Rafał Miłecki
Date: Wed Feb 22 2023 - 12:23:35 EST


From: Rafał Miłecki <rafal@xxxxxxxxxx>

Sometimes reading NVMEM cell value involves some data reformatting. it
may require resizing available buffer. Support that.

It's required e.g. to provide properly formatted MAC address in case
it's stored in a non-binary format (e.g. using ASCII).

Signed-off-by: Rafał Miłecki <rafal@xxxxxxxxxx>
---
drivers/nvmem/core.c | 23 ++++++++++++++---------
drivers/nvmem/imx-ocotp.c | 8 ++++----
include/linux/nvmem-provider.h | 4 ++--
3 files changed, 20 insertions(+), 15 deletions(-)

diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index c5c9a4654241..18fbfbf61ec3 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -1418,35 +1418,36 @@ static void nvmem_shift_read_buffer_in_place(struct nvmem_cell_entry *cell, void

static int __nvmem_cell_read(struct nvmem_device *nvmem,
struct nvmem_cell_entry *cell,
- void *buf, size_t *len, const char *id, int index)
+ void **buf, size_t *len, const char *id, int index)
{
+ size_t bytes = cell->bytes;
int rc;

- rc = nvmem_reg_read(nvmem, cell->offset, buf, cell->bytes);
+ rc = nvmem_reg_read(nvmem, cell->offset, *buf, bytes);

if (rc)
return rc;

/* shift bits in-place */
if (cell->bit_offset || cell->nbits)
- nvmem_shift_read_buffer_in_place(cell, buf);
+ nvmem_shift_read_buffer_in_place(cell, *buf);

if (cell->read_post_process) {
rc = cell->read_post_process(nvmem->priv, id, index,
- cell->offset, buf, cell->bytes);
+ cell->offset, buf, &bytes);
if (rc)
return rc;
}

if (nvmem->cell_post_process) {
rc = nvmem->cell_post_process(nvmem->priv, id, index,
- cell->offset, buf, cell->bytes);
+ cell->offset, buf, &bytes);
if (rc)
return rc;
}

if (len)
- *len = cell->bytes;
+ *len = bytes;

return 0;
}
@@ -1464,7 +1465,7 @@ static int __nvmem_cell_read(struct nvmem_device *nvmem,
void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len)
{
struct nvmem_device *nvmem = cell->entry->nvmem;
- u8 *buf;
+ void *buf;
int rc;

if (!nvmem)
@@ -1474,7 +1475,7 @@ void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len)
if (!buf)
return ERR_PTR(-ENOMEM);

- rc = __nvmem_cell_read(nvmem, cell->entry, buf, len, cell->id, cell->index);
+ rc = __nvmem_cell_read(nvmem, cell->entry, &buf, len, cell->id, cell->index);
if (rc) {
kfree(buf);
return ERR_PTR(rc);
@@ -1791,11 +1792,15 @@ ssize_t nvmem_device_cell_read(struct nvmem_device *nvmem,
if (!nvmem)
return -EINVAL;

+ /* Cells with read_post_process hook may realloc buffer we can't allow here */
+ if (info->read_post_process)
+ return -EINVAL;
+
rc = nvmem_cell_info_to_nvmem_cell_entry_nodup(nvmem, info, &cell);
if (rc)
return rc;

- rc = __nvmem_cell_read(nvmem, &cell, buf, &len, NULL, 0);
+ rc = __nvmem_cell_read(nvmem, &cell, &buf, &len, NULL, 0);
if (rc)
return rc;

diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c
index e37a82f98ba6..0e0ab27cbfe3 100644
--- a/drivers/nvmem/imx-ocotp.c
+++ b/drivers/nvmem/imx-ocotp.c
@@ -223,18 +223,18 @@ static int imx_ocotp_read(void *context, unsigned int offset,
}

static int imx_ocotp_cell_pp(void *context, const char *id, int index,
- unsigned int offset, void *data, size_t bytes)
+ unsigned int offset, void **data, size_t *bytes)
{
struct ocotp_priv *priv = context;

/* Deal with some post processing of nvmem cell data */
if (id && !strcmp(id, "mac-address")) {
if (priv->params->reverse_mac_address) {
- u8 *buf = data;
+ u8 *buf = *data;
int i;

- for (i = 0; i < bytes/2; i++)
- swap(buf[i], buf[bytes - i - 1]);
+ for (i = 0; i < *bytes / 2; i++)
+ swap(buf[i], buf[*bytes - i - 1]);
}
}

diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h
index f87fd64eee8f..9c212f7bb7d1 100644
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -19,8 +19,8 @@ typedef int (*nvmem_reg_read_t)(void *priv, unsigned int offset,
typedef int (*nvmem_reg_write_t)(void *priv, unsigned int offset,
void *val, size_t bytes);
/* used for vendor specific post processing of cell data */
-typedef int (*nvmem_cell_post_process_t)(void *priv, const char *id, int index,
- unsigned int offset, void *buf, size_t bytes);
+typedef int (*nvmem_cell_post_process_t)(void *priv, const char *id, int index, unsigned int offset,
+ void **buf, size_t *bytes);

enum nvmem_type {
NVMEM_TYPE_UNKNOWN = 0,
--
2.34.1