2021-01-22 17:34:05

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v2 00/12] iio: core,buffer: add support for multiple IIO buffers per IIO device

Continuing from:
https://lore.kernel.org/linux-iio/[email protected]/

Changelog v1 -> v2:
* 'iio: buffer: rework buffer & scan_elements dir creation'
add more doc-strings detailing the reasoning for this change
* 'iio: buffer: re-route scan_elements via it's kobj_type'
move list_del() before the kfree()'s in the list destruction
* 'iio: buffer: introduce support for attaching more IIO buffers'
- changed to 'cnt' variable vs re-using the 'i' for unwinding in
iio_buffer_alloc_sysfs_and_mask()
- removed kfree(old) in iio_device_attach_buffer()
- made iio_device_attach_buffer() an int return; this means that some
follow up patches are needed to make this return value be used;
* 'iio: buffer: add ioctl() to support opening extra buffers for IIO device'
- tested ioctl() with a simple C program; attached to comment;
- changed 'i' variable usage to 'sz' for alloc
- changed logic for buffer0; returning FD 0; userspace should know
that the IIO_BUFFER_GET_FD_IOCTL call returns 0 for buffer0;
this is because I can't find a way to determine the FD of the
ioctl() in the kernel; duplicating an ioctl() for buffer0 is also bad;

Alexandru Ardelean (12):
iio: core: register chardev only if needed
iio: buffer: add back-ref from iio_buffer to iio_dev
iio: buffer: rework buffer & scan_elements dir creation
iio: buffer: add index to the first IIO buffer dir and symlink it back
iio: core: split __iio_device_attr_init() to init only the attr object
iio: buffer: re-route scan_elements via it's kobj_type
iio: buffer: re-route core buffer attributes via it's new kobj_type
iio: buffer: add helper to get the IIO device to which a buffer
belongs
iio: re-route all buffer attributes through new buffer kobj_type
iio: core: wrap iio device & buffer into struct for character devices
iio: buffer: introduce support for attaching more IIO buffers
iio: buffer: add ioctl() to support opening extra buffers for IIO
device

drivers/iio/accel/adxl372.c | 36 +-
drivers/iio/accel/bmc150-accel-core.c | 34 +-
drivers/iio/adc/at91-sama5d2_adc.c | 30 +-
.../buffer/industrialio-buffer-dmaengine.c | 13 +-
.../cros_ec_sensors/cros_ec_sensors_core.c | 30 +-
.../common/hid-sensors/hid-sensor-trigger.c | 32 +-
drivers/iio/iio_core.h | 11 +
drivers/iio/industrialio-buffer.c | 647 ++++++++++++++----
drivers/iio/industrialio-core.c | 117 ++--
include/linux/iio/buffer.h | 6 +-
include/linux/iio/buffer_impl.h | 25 +-
include/linux/iio/iio-opaque.h | 6 +
include/linux/iio/iio.h | 2 +-
include/linux/iio/sysfs.h | 50 ++
include/uapi/linux/iio/buffer.h | 10 +
15 files changed, 790 insertions(+), 259 deletions(-)
create mode 100644 include/uapi/linux/iio/buffer.h

--
2.17.1


2021-01-22 17:34:06

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v2 05/12] iio: core: split __iio_device_attr_init() to init only the attr object

The __iio_device_attr_init() function initializes a device_attribute
object, but mostly it just does a lot of name creation logic.

We will want to re-use this logic for name-creation, so this change
re-purposes the __iio_device_attr_init() to be a __iio_attr_init() function
which just handles the creation of the attribute name.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
drivers/iio/industrialio-core.c | 43 +++++++++++++--------------------
1 file changed, 17 insertions(+), 26 deletions(-)

diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 95d66745f118..b8f7261945f5 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -968,22 +968,15 @@ static ssize_t iio_write_channel_info(struct device *dev,
}

static
-int __iio_device_attr_init(struct device_attribute *dev_attr,
- const char *postfix,
- struct iio_chan_spec const *chan,
- ssize_t (*readfunc)(struct device *dev,
- struct device_attribute *attr,
- char *buf),
- ssize_t (*writefunc)(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t len),
- enum iio_shared_by shared_by)
+int iio_attr_init(struct attribute *attr,
+ const char *postfix,
+ struct iio_chan_spec const *chan,
+ enum iio_shared_by shared_by)
{
int ret = 0;
char *name = NULL;
char *full_postfix;
- sysfs_attr_init(&dev_attr->attr);
+ sysfs_attr_init(attr);

/* Build up postfix of <extend_name>_<modifier>_postfix */
if (chan->modified && (shared_by == IIO_SEPARATE)) {
@@ -1079,17 +1072,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
ret = -ENOMEM;
goto error_free_full_postfix;
}
- dev_attr->attr.name = name;
-
- if (readfunc) {
- dev_attr->attr.mode |= S_IRUGO;
- dev_attr->show = readfunc;
- }
-
- if (writefunc) {
- dev_attr->attr.mode |= S_IWUSR;
- dev_attr->store = writefunc;
- }
+ attr->name = name;

error_free_full_postfix:
kfree(full_postfix);
@@ -1122,9 +1105,7 @@ int __iio_add_chan_devattr(const char *postfix,
iio_attr = kzalloc(sizeof(*iio_attr), GFP_KERNEL);
if (iio_attr == NULL)
return -ENOMEM;
- ret = __iio_device_attr_init(&iio_attr->dev_attr,
- postfix, chan,
- readfunc, writefunc, shared_by);
+ ret = iio_attr_init(&iio_attr->dev_attr.attr, postfix, chan, shared_by);
if (ret)
goto error_iio_dev_attr_free;
iio_attr->c = chan;
@@ -1140,6 +1121,16 @@ int __iio_add_chan_devattr(const char *postfix,
}
list_add(&iio_attr->l, attr_list);

+ if (readfunc) {
+ iio_attr->dev_attr.attr.mode |= S_IRUGO;
+ iio_attr->dev_attr.show = readfunc;
+ }
+
+ if (writefunc) {
+ iio_attr->dev_attr.attr.mode |= S_IWUSR;
+ iio_attr->dev_attr.store = writefunc;
+ }
+
return 0;

error_device_attr_deinit:
--
2.17.1

2021-01-22 17:36:01

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v2 04/12] iio: buffer: add index to the first IIO buffer dir and symlink it back

This change makes it so that the first buffer directory is named 'buffer0'
and moves the 'scan_elements' under it.

For backwards compatibility these folders are symlinked back to the
original folders.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
drivers/iio/industrialio-buffer.c | 66 +++++++++++++++++++++----------
1 file changed, 46 insertions(+), 20 deletions(-)

diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 0f470d902790..628d78125126 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -1369,7 +1369,8 @@ static void iio_sysfs_del_attrs(struct kobject *kobj, struct attribute **ptr)
* IIO device.
*/
static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
- struct iio_dev *indio_dev)
+ struct iio_dev *indio_dev,
+ unsigned int idx)
{
struct iio_dev_attr *p;
struct attribute **attr;
@@ -1401,7 +1402,7 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
buffer->buffer_attrs = attr;

ret = kobject_init_and_add(&buffer->buffer_dir, &iio_buffer_dir_ktype,
- &indio_dev->dev.kobj, "buffer");
+ &indio_dev->dev.kobj, "buffer%u", idx);
if (ret)
goto error_buffer_free_attrs;

@@ -1446,7 +1447,7 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
}

ret = kobject_init_and_add(&buffer->scan_el_dir, &iio_scan_el_dir_ktype,
- &indio_dev->dev.kobj, "scan_elements");
+ &buffer->buffer_dir, "scan_elements");
if (ret)
goto error_free_scan_attrs;

@@ -1477,6 +1478,22 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
return ret;
}

+/**
+ * __iio_buffer_free_sysfs_and_mask() - Free sysfs objects for a single IIO buffer
+ * @buffer: the iio buffer for which to destroy the objects
+ */
+static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer)
+{
+ iio_sysfs_del_attrs(&buffer->scan_el_dir, buffer->scan_el_attrs);
+ kobject_put(&buffer->scan_el_dir);
+ kfree(buffer->scan_el_attrs);
+ bitmap_free(buffer->scan_mask);
+ iio_sysfs_del_attrs(&buffer->buffer_dir, buffer->buffer_attrs);
+ iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
+ kobject_put(&buffer->buffer_dir);
+ kfree(buffer->buffer_attrs);
+}
+
/**
* iio_buffer_alloc_sysfs_and_mask() - Allocate sysfs attributes to attached buffers
* @indio_dev: the iio device for which to create the buffer sysfs attributes
@@ -1492,7 +1509,7 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
{
struct iio_buffer *buffer = indio_dev->buffer;
const struct iio_chan_spec *channels;
- int i;
+ int i, ret;

channels = indio_dev->channels;
if (channels) {
@@ -1506,23 +1523,29 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
if (!buffer)
return 0;

- return __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev);
-}
+ ret = __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev, 0);
+ if (ret)
+ return ret;

-/**
- * __iio_buffer_free_sysfs_and_mask() - Free sysfs objects for a single IIO buffer
- * @buffer: the iio buffer for which to destroy the objects
- */
-static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer)
-{
- iio_sysfs_del_attrs(&buffer->scan_el_dir, buffer->scan_el_attrs);
- kobject_put(&buffer->scan_el_dir);
- kfree(buffer->scan_el_attrs);
- bitmap_free(buffer->scan_mask);
- iio_sysfs_del_attrs(&buffer->buffer_dir, buffer->buffer_attrs);
- iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
- kobject_put(&buffer->buffer_dir);
- kfree(buffer->buffer_attrs);
+ ret = sysfs_create_link(&indio_dev->dev.kobj,
+ &indio_dev->buffer->buffer_dir,
+ "buffer");
+ if (ret)
+ goto error_free_sysfs_and_mask;
+
+ ret = sysfs_create_link(&indio_dev->dev.kobj,
+ &indio_dev->buffer->scan_el_dir,
+ "scan_elements");
+ if (ret)
+ goto error_remove_buffer_dir_link;
+
+ return 0;
+
+error_remove_buffer_dir_link:
+ sysfs_remove_link(&indio_dev->dev.kobj, "buffer");
+error_free_sysfs_and_mask:
+ __iio_buffer_free_sysfs_and_mask(buffer);
+ return ret;
}

/**
@@ -1538,6 +1561,9 @@ void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
if (!buffer)
return;

+ sysfs_remove_link(&indio_dev->dev.kobj, "scan_elements");
+ sysfs_remove_link(&indio_dev->dev.kobj, "buffer");
+
__iio_buffer_free_sysfs_and_mask(buffer);
}

--
2.17.1

2021-01-22 17:37:09

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v2 02/12] iio: buffer: add back-ref from iio_buffer to iio_dev

An IIO device will have multiple buffers, but it shouldn't be allowed that
an IIO buffer should belong to more than 1 IIO device.

Once things get moved more from IIO device to the IIO buffer, and an IIO
device will be able to have more than 1 buffer attached, there will be a
need for a back-ref to the IIO device [from the IIO buffer].

This change adds that.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
drivers/iio/industrialio-buffer.c | 2 ++
include/linux/iio/buffer_impl.h | 3 +++
2 files changed, 5 insertions(+)

diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 2f7426a2f47c..0412c4fda4c1 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -1505,5 +1505,7 @@ void iio_device_attach_buffer(struct iio_dev *indio_dev,
struct iio_buffer *buffer)
{
indio_dev->buffer = iio_buffer_get(buffer);
+
+ indio_dev->buffer->indio_dev = indio_dev;
}
EXPORT_SYMBOL_GPL(iio_device_attach_buffer);
diff --git a/include/linux/iio/buffer_impl.h b/include/linux/iio/buffer_impl.h
index a63dc07b7350..67d73d465e02 100644
--- a/include/linux/iio/buffer_impl.h
+++ b/include/linux/iio/buffer_impl.h
@@ -69,6 +69,9 @@ struct iio_buffer_access_funcs {
* those writing new buffer implementations.
*/
struct iio_buffer {
+ /** @indio_dev: IIO device to which this buffer belongs to. */
+ struct iio_dev *indio_dev;
+
/** @length: Number of datums in buffer. */
unsigned int length;

--
2.17.1

2021-01-22 17:37:10

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v2 01/12] iio: core: register chardev only if needed

We only need a chardev if we need to support buffers and/or events.

With this change, a chardev will be created only if an IIO buffer is
attached OR an event_interface is configured.

Otherwise, no chardev will be created, and the IIO device will get
registered with the 'device_add()' call.

Quite a lot of IIO devices don't really need a chardev, so this is a minor
improvement to the IIO core, as the IIO device will take up (slightly)
fewer resources.

In order to not create a chardev, we mostly just need to not initialize the
indio_dev->dev.devt field. If that is un-initialized, cdev_device_add()
behaves like device_add().

Signed-off-by: Alexandru Ardelean <[email protected]>
---
drivers/iio/industrialio-core.c | 23 ++++++++++++++++++-----
1 file changed, 18 insertions(+), 5 deletions(-)

diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 7db761afa578..0a6fd299a978 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1761,6 +1761,15 @@ static const struct file_operations iio_buffer_fileops = {
.release = iio_chrdev_release,
};

+static const struct file_operations iio_event_fileops = {
+ .owner = THIS_MODULE,
+ .llseek = noop_llseek,
+ .unlocked_ioctl = iio_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
+ .open = iio_chrdev_open,
+ .release = iio_chrdev_release,
+};
+
static int iio_check_unique_scan_index(struct iio_dev *indio_dev)
{
int i, j;
@@ -1788,6 +1797,7 @@ static const struct iio_buffer_setup_ops noop_ring_setup_ops;

int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
{
+ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
int ret;

if (!indio_dev->info)
@@ -1805,9 +1815,6 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
if (ret < 0)
return ret;

- /* configure elements for the chrdev */
- indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id);
-
iio_device_register_debugfs(indio_dev);

ret = iio_buffer_alloc_sysfs_and_mask(indio_dev);
@@ -1836,9 +1843,15 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
indio_dev->setup_ops == NULL)
indio_dev->setup_ops = &noop_ring_setup_ops;

- cdev_init(&indio_dev->chrdev, &iio_buffer_fileops);
+ if (indio_dev->buffer)
+ cdev_init(&indio_dev->chrdev, &iio_buffer_fileops);
+ else if (iio_dev_opaque->event_interface)
+ cdev_init(&indio_dev->chrdev, &iio_event_fileops);

- indio_dev->chrdev.owner = this_mod;
+ if (indio_dev->buffer || iio_dev_opaque->event_interface) {
+ indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id);
+ indio_dev->chrdev.owner = this_mod;
+ }

ret = cdev_device_add(&indio_dev->chrdev, &indio_dev->dev);
if (ret < 0)
--
2.17.1

2021-01-22 17:44:28

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v2 03/12] iio: buffer: rework buffer & scan_elements dir creation

When adding more than one IIO buffer per IIO device, we will need to create
a buffer & scan_elements directory for each buffer.
We also want to move the 'scan_elements' to be a sub-directory of the
'buffer' folder.

The format we want to reach is, for a iio:device0 folder, for 2 buffers
[for example], we have a 'buffer0' and a 'buffer1' subfolder, and each with
it's own 'scan_elements' subfolder.

So, for example:
iio:device0/buffer0
scan_elements/

iio:device0/buffer1
scan_elements/

The other attributes under 'bufferX' would remain unchanged.

However, we would also need to symlink back to the old 'buffer' &
'scan_elements' folders, to keep backwards compatibility.

Doing all these, require that we maintain the kobjects for each 'bufferX'
and 'scan_elements' so that we can symlink them back. We also need to
implement the sysfs_ops for these folders.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
drivers/iio/industrialio-buffer.c | 195 +++++++++++++++++++++++++++---
drivers/iio/industrialio-core.c | 24 ++--
include/linux/iio/buffer_impl.h | 14 ++-
include/linux/iio/iio.h | 2 +-
4 files changed, 200 insertions(+), 35 deletions(-)

diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 0412c4fda4c1..0f470d902790 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -1175,8 +1175,6 @@ static ssize_t iio_buffer_store_enable(struct device *dev,
return (ret < 0) ? ret : len;
}

-static const char * const iio_scan_elements_group_name = "scan_elements";
-
static ssize_t iio_buffer_show_watermark(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -1252,6 +1250,124 @@ static struct attribute *iio_buffer_attrs[] = {
&dev_attr_data_available.attr,
};

+#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
+
+static ssize_t iio_buffer_dir_attr_show(struct kobject *kobj,
+ struct attribute *attr,
+ char *buf)
+{
+ struct iio_buffer *buffer = container_of(kobj, struct iio_buffer, buffer_dir);
+ struct device_attribute *dattr;
+
+ dattr = to_dev_attr(attr);
+
+ return dattr->show(&buffer->indio_dev->dev, dattr, buf);
+}
+
+static ssize_t iio_buffer_dir_attr_store(struct kobject *kobj,
+ struct attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_buffer *buffer = container_of(kobj, struct iio_buffer, buffer_dir);
+ struct device_attribute *dattr;
+
+ dattr = to_dev_attr(attr);
+
+ return dattr->store(&buffer->indio_dev->dev, dattr, buf, len);
+}
+
+static const struct sysfs_ops iio_buffer_dir_sysfs_ops = {
+ .show = iio_buffer_dir_attr_show,
+ .store = iio_buffer_dir_attr_store,
+};
+
+static struct kobj_type iio_buffer_dir_ktype = {
+ .sysfs_ops = &iio_buffer_dir_sysfs_ops,
+};
+
+static ssize_t iio_scan_el_dir_attr_show(struct kobject *kobj,
+ struct attribute *attr,
+ char *buf)
+{
+ struct iio_buffer *buffer = container_of(kobj, struct iio_buffer, scan_el_dir);
+ struct device_attribute *dattr = to_dev_attr(attr);
+
+ return dattr->show(&buffer->indio_dev->dev, dattr, buf);
+}
+
+static ssize_t iio_scan_el_dir_attr_store(struct kobject *kobj,
+ struct attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_buffer *buffer = container_of(kobj, struct iio_buffer, scan_el_dir);
+ struct device_attribute *dattr = to_dev_attr(attr);
+
+ return dattr->store(&buffer->indio_dev->dev, dattr, buf, len);
+}
+
+static const struct sysfs_ops iio_scan_el_dir_sysfs_ops = {
+ .show = iio_scan_el_dir_attr_show,
+ .store = iio_scan_el_dir_attr_store,
+};
+
+static struct kobj_type iio_scan_el_dir_ktype = {
+ .sysfs_ops = &iio_scan_el_dir_sysfs_ops,
+};
+
+/*
+ * These iio_sysfs_{add,del}_attrs() are essentially re-implementations of
+ * sysfs_create_files() & sysfs_remove_files(), but they are meant to get
+ * around the const-pointer mismatch situation with using them.
+ *
+ * sysfs_{create,remove}_files() uses 'const struct attribute * const *ptr',
+ * while these are happy with just 'struct attribute **ptr'
+ */
+static int iio_sysfs_add_attrs(struct kobject *kobj, struct attribute **ptr)
+{
+ int err = 0;
+ int i;
+
+ for (i = 0; ptr[i] && !err; i++)
+ err = sysfs_create_file(kobj, ptr[i]);
+ if (err)
+ while (--i >= 0)
+ sysfs_remove_file(kobj, ptr[i]);
+ return err;
+}
+
+static void iio_sysfs_del_attrs(struct kobject *kobj, struct attribute **ptr)
+{
+ int i;
+
+ for (i = 0; ptr[i]; i++)
+ sysfs_remove_file(kobj, ptr[i]);
+}
+
+/**
+ * __iio_buffer_alloc_sysfs_and_mask() - Allocate sysfs attributes to an attached buffer
+ * @buffer: the buffer object for which the sysfs attributes are created for
+ * @indio_dev: the iio device to which the iio buffer belongs to
+ *
+ * Return 0, or negative for error.
+ *
+ * This function must be called for each single buffer. The sysfs attributes for that
+ * buffer will be created, and the IIO device object will be the parent kobject of that
+ * the kobjects created here.
+ * Because we need to redirect sysfs attribute to it's IIO buffer object, we need to
+ * implement our own sysfs_ops, and each IIO buffer will keep a kobject for the
+ * 'bufferX' directory and one for the 'scan_elements' directory.
+ * And in order to do this, this function must be called after the IIO device object
+ * has been added via device_add(). This fundamentally changes how sysfs attributes
+ * were created before (with one single IIO buffer per IIO device), where the
+ * sysfs attributes for the buffer were mapped as attribute groups on the IIO device
+ * groups object list.
+ * Using kobjects directly for the 'bufferX' and 'scan_elements' directories allows
+ * us to symlink them back to keep backwards compatibility for the old sysfs interface
+ * for IIO buffers while also allowing us to support multiple IIO buffers per one
+ * IIO device.
+ */
static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
struct iio_dev *indio_dev)
{
@@ -1282,12 +1398,16 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
memcpy(&attr[ARRAY_SIZE(iio_buffer_attrs)], buffer->attrs,
sizeof(struct attribute *) * attrcount);

- attr[attrcount + ARRAY_SIZE(iio_buffer_attrs)] = NULL;
+ buffer->buffer_attrs = attr;

- buffer->buffer_group.name = "buffer";
- buffer->buffer_group.attrs = attr;
+ ret = kobject_init_and_add(&buffer->buffer_dir, &iio_buffer_dir_ktype,
+ &indio_dev->dev.kobj, "buffer");
+ if (ret)
+ goto error_buffer_free_attrs;

- indio_dev->groups[indio_dev->groupcounter++] = &buffer->buffer_group;
+ ret = iio_sysfs_add_attrs(&buffer->buffer_dir, buffer->buffer_attrs);
+ if (ret)
+ goto error_buffer_kobject_put;

attrcount = 0;
INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list);
@@ -1317,32 +1437,57 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
}
}

- buffer->scan_el_group.name = iio_scan_elements_group_name;
-
- buffer->scan_el_group.attrs = kcalloc(attrcount + 1,
- sizeof(buffer->scan_el_group.attrs[0]),
- GFP_KERNEL);
- if (buffer->scan_el_group.attrs == NULL) {
+ buffer->scan_el_attrs = kcalloc(attrcount + 1,
+ sizeof(buffer->scan_el_attrs[0]),
+ GFP_KERNEL);
+ if (buffer->scan_el_attrs == NULL) {
ret = -ENOMEM;
goto error_free_scan_mask;
}
- attrn = 0;

+ ret = kobject_init_and_add(&buffer->scan_el_dir, &iio_scan_el_dir_ktype,
+ &indio_dev->dev.kobj, "scan_elements");
+ if (ret)
+ goto error_free_scan_attrs;
+
+ attrn = 0;
list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l)
- buffer->scan_el_group.attrs[attrn++] = &p->dev_attr.attr;
- indio_dev->groups[indio_dev->groupcounter++] = &buffer->scan_el_group;
+ buffer->scan_el_attrs[attrn++] = &p->dev_attr.attr;
+
+ ret = iio_sysfs_add_attrs(&buffer->scan_el_dir, buffer->scan_el_attrs);
+ if (ret)
+ goto error_scan_kobject_put;

return 0;

+error_scan_kobject_put:
+ kobject_put(&buffer->scan_el_dir);
+error_free_scan_attrs:
+ kfree(buffer->scan_el_attrs);
error_free_scan_mask:
bitmap_free(buffer->scan_mask);
error_cleanup_dynamic:
+ iio_sysfs_del_attrs(&buffer->buffer_dir, buffer->buffer_attrs);
iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
- kfree(buffer->buffer_group.attrs);
+error_buffer_kobject_put:
+ kobject_put(&buffer->buffer_dir);
+error_buffer_free_attrs:
+ kfree(buffer->buffer_attrs);

return ret;
}

+/**
+ * iio_buffer_alloc_sysfs_and_mask() - Allocate sysfs attributes to attached buffers
+ * @indio_dev: the iio device for which to create the buffer sysfs attributes
+ *
+ * Return 0, or negative for error.
+ *
+ * If the IIO device has no buffer attached, no sysfs attributes will be created.
+ * This function must be called after the IIO device object has been created and
+ * registered with device_add(). See __iio_buffer_alloc_sysfs_and_mask() for more
+ * details.
+ */
int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
{
struct iio_buffer *buffer = indio_dev->buffer;
@@ -1364,14 +1509,28 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
return __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev);
}

+/**
+ * __iio_buffer_free_sysfs_and_mask() - Free sysfs objects for a single IIO buffer
+ * @buffer: the iio buffer for which to destroy the objects
+ */
static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer)
{
+ iio_sysfs_del_attrs(&buffer->scan_el_dir, buffer->scan_el_attrs);
+ kobject_put(&buffer->scan_el_dir);
+ kfree(buffer->scan_el_attrs);
bitmap_free(buffer->scan_mask);
- kfree(buffer->buffer_group.attrs);
- kfree(buffer->scan_el_group.attrs);
+ iio_sysfs_del_attrs(&buffer->buffer_dir, buffer->buffer_attrs);
iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
+ kobject_put(&buffer->buffer_dir);
+ kfree(buffer->buffer_attrs);
}

+/**
+ * iio_buffer_free_sysfs_and_mask() - Free sysfs objects for all IIO buffers
+ * @indio_dev: the iio device for which to destroy the objects
+ *
+ * If the IIO device has no buffer attached, nothing will be done.
+ */
void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
{
struct iio_buffer *buffer = indio_dev->buffer;
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 0a6fd299a978..95d66745f118 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1817,18 +1817,11 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)

iio_device_register_debugfs(indio_dev);

- ret = iio_buffer_alloc_sysfs_and_mask(indio_dev);
- if (ret) {
- dev_err(indio_dev->dev.parent,
- "Failed to create buffer sysfs interfaces\n");
- goto error_unreg_debugfs;
- }
-
ret = iio_device_register_sysfs(indio_dev);
if (ret) {
dev_err(indio_dev->dev.parent,
"Failed to register sysfs interfaces\n");
- goto error_buffer_free_sysfs;
+ goto error_unreg_debugfs;
}
ret = iio_device_register_eventset(indio_dev);
if (ret) {
@@ -1857,14 +1850,21 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
if (ret < 0)
goto error_unreg_eventset;

+ ret = iio_buffer_alloc_sysfs_and_mask(indio_dev);
+ if (ret) {
+ dev_err(indio_dev->dev.parent,
+ "Failed to create buffer sysfs interfaces\n");
+ goto error_device_del;
+ }
+
return 0;

+error_device_del:
+ cdev_device_del(&indio_dev->chrdev, &indio_dev->dev);
error_unreg_eventset:
iio_device_unregister_eventset(indio_dev);
error_free_sysfs:
iio_device_unregister_sysfs(indio_dev);
-error_buffer_free_sysfs:
- iio_buffer_free_sysfs_and_mask(indio_dev);
error_unreg_debugfs:
iio_device_unregister_debugfs(indio_dev);
return ret;
@@ -1880,6 +1880,8 @@ void iio_device_unregister(struct iio_dev *indio_dev)
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
struct iio_ioctl_handler *h, *t;

+ iio_buffer_free_sysfs_and_mask(indio_dev);
+
cdev_device_del(&indio_dev->chrdev, &indio_dev->dev);

mutex_lock(&indio_dev->info_exist_lock);
@@ -1897,8 +1899,6 @@ void iio_device_unregister(struct iio_dev *indio_dev)
iio_buffer_wakeup_poll(indio_dev);

mutex_unlock(&indio_dev->info_exist_lock);
-
- iio_buffer_free_sysfs_and_mask(indio_dev);
}
EXPORT_SYMBOL(iio_device_unregister);

diff --git a/include/linux/iio/buffer_impl.h b/include/linux/iio/buffer_impl.h
index 67d73d465e02..77e169e51434 100644
--- a/include/linux/iio/buffer_impl.h
+++ b/include/linux/iio/buffer_impl.h
@@ -103,14 +103,20 @@ struct iio_buffer {
/* @scan_el_dev_attr_list: List of scan element related attributes. */
struct list_head scan_el_dev_attr_list;

- /* @buffer_group: Attributes of the buffer group. */
- struct attribute_group buffer_group;
+ /* @buffer_dir: kobject for the 'buffer' directory of this buffer */
+ struct kobject buffer_dir;
+
+ /* @buffer_attrs: Attributes of the buffer group. */
+ struct attribute **buffer_attrs;
+
+ /* @scan_el_dir: kobject for the 'scan_elements' directory of this buffer */
+ struct kobject scan_el_dir;

/*
- * @scan_el_group: Attribute group for those attributes not
+ * @scan_el_attrs: Array of attributes for those attributes not
* created from the iio_chan_info array.
*/
- struct attribute_group scan_el_group;
+ struct attribute **scan_el_attrs;

/* @attrs: Standard attributes of the buffer. */
const struct attribute **attrs;
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index e4a9822e6495..59b317dc45b8 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -556,7 +556,7 @@ struct iio_dev {
struct mutex info_exist_lock;
const struct iio_buffer_setup_ops *setup_ops;
struct cdev chrdev;
-#define IIO_MAX_GROUPS 6
+#define IIO_MAX_GROUPS 4
const struct attribute_group *groups[IIO_MAX_GROUPS + 1];
int groupcounter;

--
2.17.1