diff --git a/drivers/staging/iio/iio.h b/drivers/staging/iio/iio.h index 9459c17dee48..b9bce9aeb19e 100644 --- a/drivers/staging/iio/iio.h +++ b/drivers/staging/iio/iio.h @@ -88,6 +88,25 @@ enum iio_endian { IIO_LE, }; +struct iio_chan_spec; +struct iio_dev; + +/** + * struct iio_chan_spec_ext_info - Extended channel info attribute + * @name: Info attribute name + * @shared: Whether this attribute is shared between all channels. + * @read: Read callback for this info attribute, may be NULL. + * @write: Write callback for this info attribute, may be NULL. + */ +struct iio_chan_spec_ext_info { + const char *name; + bool shared; + ssize_t (*read)(struct iio_dev *, struct iio_chan_spec const *, + char *buf); + ssize_t (*write)(struct iio_dev *, struct iio_chan_spec const *, + const char *buf, size_t len); +}; + /** * struct iio_chan_spec - specification of a single channel * @type: What type of measurement is the channel making. @@ -107,6 +126,9 @@ enum iio_endian { * @info_mask: What information is to be exported about this channel. * This includes calibbias, scale etc. * @event_mask: What events can this channel produce. + * @ext_info: Array of extended info attributes for this channel. + * The array is NULL terminated, the last element should + * have it's name field set to NULL. * @extend_name: Allows labeling of channel attributes with an * informative name. Note this has no effect codes etc, * unlike modifiers. @@ -141,6 +163,7 @@ struct iio_chan_spec { } scan_type; long info_mask; long event_mask; + const struct iio_chan_spec_ext_info *ext_info; char *extend_name; const char *datasheet_name; unsigned processed_val:1; diff --git a/drivers/staging/iio/industrialio-core.c b/drivers/staging/iio/industrialio-core.c index 3a2d08026624..645bb45c89e8 100644 --- a/drivers/staging/iio/industrialio-core.c +++ b/drivers/staging/iio/industrialio-core.c @@ -144,6 +144,33 @@ static void __exit iio_exit(void) bus_unregister(&iio_bus_type); } +static ssize_t iio_read_channel_ext_info(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + const struct iio_chan_spec_ext_info *ext_info; + + ext_info = &this_attr->c->ext_info[this_attr->address]; + + return ext_info->read(indio_dev, this_attr->c, buf); +} + +static ssize_t iio_write_channel_ext_info(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + const struct iio_chan_spec_ext_info *ext_info; + + ext_info = &this_attr->c->ext_info[this_attr->address]; + + return ext_info->write(indio_dev, this_attr->c, buf, len); +} + static ssize_t iio_read_channel_info(struct device *dev, struct device_attribute *attr, char *buf) @@ -423,6 +450,7 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, struct iio_chan_spec const *chan) { int ret, i, attrcount = 0; + const struct iio_chan_spec_ext_info *ext_info; if (chan->channel < 0) return 0; @@ -457,6 +485,31 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, goto error_ret; attrcount++; } + + if (chan->ext_info) { + unsigned int i = 0; + for (ext_info = chan->ext_info; ext_info->name; ext_info++) { + ret = __iio_add_chan_devattr(ext_info->name, + chan, + ext_info->read ? + &iio_read_channel_ext_info : NULL, + ext_info->write ? + &iio_write_channel_ext_info : NULL, + i, + ext_info->shared, + &indio_dev->dev, + &indio_dev->channel_attr_list); + i++; + if (ret == -EBUSY && ext_info->shared) + continue; + + if (ret) + goto error_ret; + + attrcount++; + } + } + ret = attrcount; error_ret: return ret;