diff options
Diffstat (limited to 'drivers/iio')
-rw-r--r-- | drivers/iio/industrialio-event.c | 193 |
1 files changed, 171 insertions, 22 deletions
diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index 4a3fd5acda94..b7a5d7cbed42 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -201,6 +201,26 @@ static const char * const iio_ev_dir_text[] = { [IIO_EV_DIR_FALLING] = "falling" }; +static const char * const iio_ev_info_text[] = { + [IIO_EV_INFO_ENABLE] = "en", + [IIO_EV_INFO_VALUE] = "value", +}; + +static enum iio_event_direction iio_ev_attr_dir(struct iio_dev_attr *attr) +{ + return attr->c->event_spec[attr->address & 0xffff].dir; +} + +static enum iio_event_type iio_ev_attr_type(struct iio_dev_attr *attr) +{ + return attr->c->event_spec[attr->address & 0xffff].type; +} + +static enum iio_event_info iio_ev_attr_info(struct iio_dev_attr *attr) +{ + return (attr->address >> 16) & 0xffff; +} + static ssize_t iio_ev_state_store(struct device *dev, struct device_attribute *attr, const char *buf, @@ -215,9 +235,14 @@ static ssize_t iio_ev_state_store(struct device *dev, if (ret < 0) return ret; - ret = indio_dev->info->write_event_config(indio_dev, - this_attr->address, - val); + if (indio_dev->info->write_event_config) + ret = indio_dev->info->write_event_config(indio_dev, + this_attr->address, val); + else + ret = indio_dev->info->write_event_config_new(indio_dev, + this_attr->c, iio_ev_attr_type(this_attr), + iio_ev_attr_dir(this_attr), val); + return (ret < 0) ? ret : len; } @@ -227,9 +252,15 @@ static ssize_t iio_ev_state_show(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - int val = indio_dev->info->read_event_config(indio_dev, - this_attr->address); + int val; + if (indio_dev->info->read_event_config) + val = indio_dev->info->read_event_config(indio_dev, + this_attr->address); + else + val = indio_dev->info->read_event_config_new(indio_dev, + this_attr->c, iio_ev_attr_type(this_attr), + iio_ev_attr_dir(this_attr)); if (val < 0) return val; else @@ -242,14 +273,24 @@ static ssize_t iio_ev_value_show(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - int val, ret; - - ret = indio_dev->info->read_event_value(indio_dev, - this_attr->address, &val); - if (ret < 0) - return ret; + int val, val2; + int ret; - return sprintf(buf, "%d\n", val); + if (indio_dev->info->read_event_value) { + ret = indio_dev->info->read_event_value(indio_dev, + this_attr->address, &val); + if (ret < 0) + return ret; + return sprintf(buf, "%d\n", val); + } else { + ret = indio_dev->info->read_event_value_new(indio_dev, + this_attr->c, iio_ev_attr_type(this_attr), + iio_ev_attr_dir(this_attr), iio_ev_attr_info(this_attr), + &val, &val2); + if (ret < 0) + return ret; + return iio_format_value(buf, ret, val, val2); + } } static ssize_t iio_ev_value_store(struct device *dev, @@ -259,25 +300,120 @@ static ssize_t iio_ev_value_store(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - int val; + int val, val2; int ret; - if (!indio_dev->info->write_event_value) + if (!indio_dev->info->write_event_value && + !indio_dev->info->write_event_value_new) return -EINVAL; - ret = kstrtoint(buf, 10, &val); - if (ret) - return ret; - - ret = indio_dev->info->write_event_value(indio_dev, this_attr->address, - val); + if (indio_dev->info->write_event_value) { + ret = kstrtoint(buf, 10, &val); + if (ret) + return ret; + ret = indio_dev->info->write_event_value(indio_dev, + this_attr->address, val); + } else { + ret = iio_str_to_fixpoint(buf, 100000, &val, &val2); + if (ret) + return ret; + ret = indio_dev->info->write_event_value_new(indio_dev, + this_attr->c, iio_ev_attr_type(this_attr), + iio_ev_attr_dir(this_attr), iio_ev_attr_info(this_attr), + val, val2); + } if (ret < 0) return ret; return len; } -static int iio_device_add_event_sysfs(struct iio_dev *indio_dev, +static int iio_device_add_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int spec_index, + enum iio_event_type type, enum iio_event_direction dir, + enum iio_shared_by shared_by, const unsigned long *mask) +{ + ssize_t (*show)(struct device *, struct device_attribute *, char *); + ssize_t (*store)(struct device *, struct device_attribute *, + const char *, size_t); + unsigned int attrcount = 0; + unsigned int i; + char *postfix; + int ret; + + for_each_set_bit(i, mask, sizeof(*mask)) { + postfix = kasprintf(GFP_KERNEL, "%s_%s_%s", + iio_ev_type_text[type], iio_ev_dir_text[dir], + iio_ev_info_text[i]); + if (postfix == NULL) + return -ENOMEM; + + if (i == IIO_EV_INFO_ENABLE) { + show = iio_ev_state_show; + store = iio_ev_state_store; + } else { + show = iio_ev_value_show; + store = iio_ev_value_store; + } + + ret = __iio_add_chan_devattr(postfix, chan, show, store, + (i << 16) | spec_index, shared_by, &indio_dev->dev, + &indio_dev->event_interface->dev_attr_list); + kfree(postfix); + + if (ret) + return ret; + + attrcount++; + } + + return attrcount; +} + +static int iio_device_add_event_sysfs_new(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + int ret = 0, i, attrcount = 0; + enum iio_event_direction dir; + enum iio_event_type type; + + for (i = 0; i < chan->num_event_specs; i++) { + type = chan->event_spec[i].type; + dir = chan->event_spec[i].dir; + + ret = iio_device_add_event(indio_dev, chan, i, type, dir, + IIO_SEPARATE, &chan->event_spec[i].mask_separate); + if (ret < 0) + goto error_ret; + attrcount += ret; + + ret = iio_device_add_event(indio_dev, chan, i, type, dir, + IIO_SHARED_BY_TYPE, + &chan->event_spec[i].mask_shared_by_type); + if (ret < 0) + goto error_ret; + attrcount += ret; + + ret = iio_device_add_event(indio_dev, chan, i, type, dir, + IIO_SHARED_BY_DIR, + &chan->event_spec[i].mask_shared_by_dir); + if (ret < 0) + goto error_ret; + attrcount += ret; + + ret = iio_device_add_event(indio_dev, chan, i, type, dir, + IIO_SHARED_BY_ALL, + &chan->event_spec[i].mask_shared_by_all); + if (ret < 0) + goto error_ret; + attrcount += ret; + } + ret = attrcount; +error_ret: + return ret; +} + +static int iio_device_add_event_sysfs_old(struct iio_dev *indio_dev, struct iio_chan_spec const *chan) { int ret = 0, i, attrcount = 0; @@ -350,6 +486,16 @@ error_ret: return ret; } + +static int iio_device_add_event_sysfs(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + if (chan->event_mask) + return iio_device_add_event_sysfs_old(indio_dev, chan); + else + return iio_device_add_event_sysfs_new(indio_dev, chan); +} + static inline int __iio_add_event_config_attrs(struct iio_dev *indio_dev) { int j, ret, attrcount = 0; @@ -369,9 +515,12 @@ static bool iio_check_for_dynamic_events(struct iio_dev *indio_dev) { int j; - for (j = 0; j < indio_dev->num_channels; j++) + for (j = 0; j < indio_dev->num_channels; j++) { if (indio_dev->channels[j].event_mask != 0) return true; + if (indio_dev->channels[j].num_event_specs != 0) + return true; + } return false; } |