Skip to content

Commit

Permalink
iiod: Restore support for reading/writing all attributes
Browse files Browse the repository at this point in the history
Libiio's new API does not allow reading or writing all attributes at the
same time. However, old clients connected to the old interface might
still try to use it.

Re-implement it in IIOD on top of the new API so that old clients still
work.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
  • Loading branch information
pcercuei committed Nov 27, 2023
1 parent 7d97827 commit c759f51
Showing 1 changed file with 177 additions and 12 deletions.
189 changes: 177 additions & 12 deletions iiod/ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -1302,6 +1302,110 @@ ssize_t rw_dev(struct parser_pdata *pdata, struct iio_device *dev,
return ret;
}

static inline uint32_t iiod_htobe32(uint32_t word)
{
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
return word;
#elif defined(__GNUC__)
return __builtin_bswap32(word);
#else
return ((word & 0xff) << 24) | ((word & 0xff00) << 8) |
((word >> 8) & 0xff00) | ((word >> 24) & 0xff);
#endif
}

static inline uint32_t iiod_be32toh(uint32_t word)
{
return iiod_htobe32(word);
}

typedef struct iio_attr * (*rw_attr_cb_t)(const void *, unsigned int);

static int buffer_analyze(unsigned int nb, const char *src, size_t len)
{
while (nb--) {
int32_t val;

if (len < 4)
return -EINVAL;

val = (int32_t) iiod_be32toh(*(uint32_t *) src);
src += 4;
len -= 4;

if (val > 0) {
if ((uint32_t) val > len)
return -EINVAL;

/* Align the length to 4 bytes */
if (val & 3)
val = ((val >> 2) + 1) << 2;
len -= val;
src += val;
}
}

/* We should have analyzed the whole buffer by now */
return !len ? 0 : -EINVAL;
}

static ssize_t read_each_attr(const void *iio, char *buf, size_t len,
unsigned int nb, rw_attr_cb_t cb)
{
const struct iio_attr *attr;
unsigned int i;
char *ptr = buf;
ssize_t ret;

for (i = 0; len >= 4 && i < nb; i++) {
attr = (*cb)(iio, i);
if (!attr)
ret = -ENOENT;
else
ret = iio_attr_read_raw(attr, ptr + 4, len - 4);
*(uint32_t *)ptr = iiod_htobe32(ret);

/* Align the length to 4 bytes */
ret = ret < 0 ? 0 : (ret + 3) & ~0x3;
ptr += 4 + ret;
len -= 4 + ret;
}

return ptr - buf;
}

static ssize_t write_each_attr(const void *iio, const char *buf, size_t len,
unsigned int nb, rw_attr_cb_t cb)
{
const struct iio_attr *attr;
const char *ptr = buf;
unsigned int i;
ssize_t ret;
int32_t val;

ret = buffer_analyze(nb, buf, len);
if (ret < 0)
return ret;

for (i = 0; i < nb; i++) {
val = (int32_t) iiod_be32toh(*(uint32_t *)ptr);
ptr += 4;

if (val > 0) {
attr = (*cb)(iio, i);
if (!attr)
continue;

iio_attr_write_raw(attr, ptr, val);

/* Align the length to 4 bytes */
ptr += (val + 3) & ~0x3;
}
}

return ptr - buf;
}

ssize_t read_dev_attr(struct parser_pdata *pdata, struct iio_device *dev,
const char *name, enum iio_attr_type type)
{
Expand All @@ -1312,12 +1416,32 @@ ssize_t read_dev_attr(struct parser_pdata *pdata, struct iio_device *dev,
* of data. */
char buf[0x10000];
ssize_t ret = -EINVAL;
unsigned int nb;

if (!dev) {
print_value(pdata, -ENODEV);
return -ENODEV;
}

if (!name) {
switch (type) {
case IIO_ATTR_TYPE_DEVICE:
nb = iio_device_get_attrs_count(dev);
ret = read_each_attr(dev, buf, sizeof(buf) - 1, nb,
(rw_attr_cb_t)iio_device_get_attr);
break;
case IIO_ATTR_TYPE_DEBUG:
nb = iio_device_get_debug_attrs_count(dev);
ret = read_each_attr(dev, buf, sizeof(buf) - 1, nb,
(rw_attr_cb_t)iio_device_get_debug_attr);
break;
default:
return -EINVAL;
}

goto out_print_value;
}

switch (type) {
case IIO_ATTR_TYPE_DEVICE:
attr = iio_device_find_attr(dev, name);
Expand Down Expand Up @@ -1354,6 +1478,7 @@ ssize_t read_dev_attr(struct parser_pdata *pdata, struct iio_device *dev,
break;
}

out_print_value:
print_value(pdata, ret);
if (ret < 0)
return ret;
Expand All @@ -1367,21 +1492,41 @@ ssize_t write_dev_attr(struct parser_pdata *pdata, struct iio_device *dev,
{
const struct iio_device_pdata *dev_pdata;
const struct iio_attr *attr;
unsigned int nb;
ssize_t ret = -ENOMEM;
char *buf;

if (!dev) {
ret = -ENODEV;
goto err_print_value;
goto out_print_value;
}

buf = malloc(len);
if (!buf)
goto err_print_value;
goto out_print_value;

ret = read_all(pdata, buf, len);
if (ret < 0)
goto err_free_buffer;
goto out_free_buffer;

if (!name) {
switch (type) {
case IIO_ATTR_TYPE_DEVICE:
nb = iio_device_get_attrs_count(dev);
ret = write_each_attr(dev, buf, len - 1, nb,
(rw_attr_cb_t)iio_device_get_attr);
break;
case IIO_ATTR_TYPE_DEBUG:
nb = iio_device_get_debug_attrs_count(dev);
ret = write_each_attr(dev, buf, len - 1, nb,
(rw_attr_cb_t)iio_device_get_debug_attr);
break;
default:
return -EINVAL;
}

goto out_print_value;
}

switch (type) {
case IIO_ATTR_TYPE_DEVICE:
Expand Down Expand Up @@ -1419,30 +1564,42 @@ ssize_t write_dev_attr(struct parser_pdata *pdata, struct iio_device *dev,
break;
}

err_free_buffer:
out_free_buffer:
free(buf);
err_print_value:
out_print_value:
print_value(pdata, ret);
return ret;
}

ssize_t read_chn_attr(struct parser_pdata *pdata,
struct iio_channel *chn, const char *name)
{
char buf[1024];
/* We use a very large buffer here, as if attr is NULL all the
* attributes will be read, which may represents a few kilobytes worth
* of data. */
char buf[0x10000];
ssize_t ret = -ENODEV;
const struct iio_attr *attr;
unsigned int nb;

if (chn) {
if (!chn) {
ret = pdata->dev ? -ENXIO : -ENODEV;
goto out_print_value;
}

if (!name) {
nb = iio_channel_get_attrs_count(chn);
ret = read_each_attr(chn, buf, sizeof(buf) - 1, nb,
(rw_attr_cb_t)iio_channel_get_attr);
} else {
attr = iio_channel_find_attr(chn, name);
if (attr)
ret = iio_attr_read_raw(attr, buf, sizeof(buf) - 1);
else
ret = -ENOENT;
} else {
ret = pdata->dev ? -ENXIO : -ENODEV;
}

out_print_value:
print_value(pdata, ret);
if (ret < 0)
return ret;
Expand All @@ -1456,6 +1613,7 @@ ssize_t write_chn_attr(struct parser_pdata *pdata,
{
const struct iio_attr *attr;
ssize_t ret = -ENOMEM;
unsigned int nb;
char *buf;

buf = malloc(len);
Expand All @@ -1466,14 +1624,21 @@ ssize_t write_chn_attr(struct parser_pdata *pdata,
if (ret < 0)
goto err_free_buffer;

if (chn) {
if (!chn) {
ret = pdata->dev ? -ENXIO : -ENODEV;
goto err_free_buffer;
}

if (!name) {
nb = iio_channel_get_attrs_count(chn);
ret = write_each_attr(chn, buf, sizeof(buf) - 1, nb,
(rw_attr_cb_t)iio_channel_get_attr);
} else {
attr = iio_channel_find_attr(chn, name);
if (attr)
ret = iio_attr_write_raw(attr, buf, len);
else
ret = -ENOENT;
} else {
ret = pdata->dev ? -ENXIO : -ENODEV;
}

err_free_buffer:
Expand Down

0 comments on commit c759f51

Please sign in to comment.