Skip to content

Commit

Permalink
nvmem: core: allow .read_post_process() callbacks to adjust buffer
Browse files Browse the repository at this point in the history
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@milecki.pl>
  • Loading branch information
Rafał Miłecki authored and intel-lab-lkp committed Jan 11, 2023
1 parent dfe4935 commit 8feca4b
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 26 deletions.
27 changes: 16 additions & 11 deletions drivers/nvmem/core.c
Expand Up @@ -1538,29 +1538,26 @@ 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)
{
int rc;

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

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(cell->priv, id, index,
cell->offset, buf, cell->bytes);
cell->offset, buf, len);
if (rc)
return rc;
}

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

return 0;
}

Expand All @@ -1577,22 +1574,26 @@ 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;
size_t bytes = cell->entry->bytes;
void *buf;
int rc;

if (!nvmem)
return ERR_PTR(-EINVAL);

buf = kzalloc(cell->entry->bytes, GFP_KERNEL);
buf = kzalloc(bytes, GFP_KERNEL);
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, &bytes, cell->id, cell->index);
if (rc) {
kfree(buf);
return ERR_PTR(rc);
}

if (len)
*len = bytes;

return buf;
}
EXPORT_SYMBOL_GPL(nvmem_cell_read);
Expand Down Expand Up @@ -1904,11 +1905,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, &cell.bytes, NULL, 0);
if (rc)
return rc;

Expand Down
8 changes: 4 additions & 4 deletions drivers/nvmem/imx-ocotp.c
Expand Up @@ -223,15 +223,15 @@ 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)
{
u8 *buf = data;
u8 *buf = *data;
int i;

/* Deal with some post processing of nvmem cell data */
if (id && !strcmp(id, "mac-address"))
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]);

return 0;
}
Expand Down
5 changes: 2 additions & 3 deletions drivers/nvmem/layouts/onie-tlv.c
Expand Up @@ -75,10 +75,9 @@ static const char *onie_tlv_cell_name(u8 type)
}

static int onie_tlv_mac_read_cb(void *priv, const char *id, int index,
unsigned int offset, void *buf,
size_t bytes)
unsigned int offset, void **buf, size_t *bytes)
{
eth_addr_add(buf, index);
eth_addr_add(*buf, index);

return 0;
}
Expand Down
10 changes: 5 additions & 5 deletions drivers/nvmem/layouts/sl28vpd.c
Expand Up @@ -22,19 +22,19 @@ struct sl28vpd_v1 {
} __packed;

static int sl28vpd_mac_address_pp(void *priv, const char *id, int index,
unsigned int offset, void *buf,
size_t bytes)
unsigned int offset, void **buf,
size_t *bytes)
{
if (bytes != ETH_ALEN)
if (*bytes != ETH_ALEN)
return -EINVAL;

if (index < 0)
return -EINVAL;

if (!is_valid_ether_addr(buf))
if (!is_valid_ether_addr(*buf))
return -EINVAL;

eth_addr_add(buf, index);
eth_addr_add(*buf, index);

return 0;
}
Expand Down
5 changes: 2 additions & 3 deletions include/linux/nvmem-provider.h
Expand Up @@ -19,9 +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,
Expand Down

0 comments on commit 8feca4b

Please sign in to comment.