summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/iio/industrialio-core.c259
-rw-r--r--include/linux/iio/iio.h29
-rw-r--r--include/linux/iio/types.h5
3 files changed, 260 insertions, 33 deletions
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 649725bc15c1..aaca42862389 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -577,66 +577,82 @@ int of_iio_read_mount_matrix(const struct device *dev,
#endif
EXPORT_SYMBOL(of_iio_read_mount_matrix);
-/**
- * iio_format_value() - Formats a IIO value into its string representation
- * @buf: The buffer to which the formatted value gets written
- * @type: One of the IIO_VAL_... constants. This decides how the val
- * and val2 parameters are formatted.
- * @size: Number of IIO value entries contained in vals
- * @vals: Pointer to the values, exact meaning depends on the
- * type parameter.
- *
- * Return: 0 by default, a negative number on failure or the
- * total number of characters written for a type that belongs
- * to the IIO_VAL_... constant.
- */
-ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals)
+static ssize_t __iio_format_value(char *buf, size_t len, unsigned int type,
+ int size, const int *vals)
{
unsigned long long tmp;
+ int tmp0, tmp1;
bool scale_db = false;
switch (type) {
case IIO_VAL_INT:
- return sprintf(buf, "%d\n", vals[0]);
+ return snprintf(buf, len, "%d", vals[0]);
case IIO_VAL_INT_PLUS_MICRO_DB:
scale_db = true;
case IIO_VAL_INT_PLUS_MICRO:
if (vals[1] < 0)
- return sprintf(buf, "-%d.%06u%s\n", abs(vals[0]),
- -vals[1], scale_db ? " dB" : "");
+ return snprintf(buf, len, "-%d.%06u%s", abs(vals[0]),
+ -vals[1], scale_db ? " dB" : "");
else
- return sprintf(buf, "%d.%06u%s\n", vals[0], vals[1],
- scale_db ? " dB" : "");
+ return snprintf(buf, len, "%d.%06u%s", vals[0], vals[1],
+ scale_db ? " dB" : "");
case IIO_VAL_INT_PLUS_NANO:
if (vals[1] < 0)
- return sprintf(buf, "-%d.%09u\n", abs(vals[0]),
- -vals[1]);
+ return snprintf(buf, len, "-%d.%09u", abs(vals[0]),
+ -vals[1]);
else
- return sprintf(buf, "%d.%09u\n", vals[0], vals[1]);
+ return snprintf(buf, len, "%d.%09u", vals[0], vals[1]);
case IIO_VAL_FRACTIONAL:
tmp = div_s64((s64)vals[0] * 1000000000LL, vals[1]);
- vals[0] = (int)div_s64_rem(tmp, 1000000000, &vals[1]);
- return sprintf(buf, "%d.%09u\n", vals[0], abs(vals[1]));
+ tmp1 = vals[1];
+ tmp0 = (int)div_s64_rem(tmp, 1000000000, &tmp1);
+ return snprintf(buf, len, "%d.%09u", tmp0, abs(tmp1));
case IIO_VAL_FRACTIONAL_LOG2:
tmp = (s64)vals[0] * 1000000000LL >> vals[1];
- vals[1] = do_div(tmp, 1000000000LL);
- vals[0] = tmp;
- return sprintf(buf, "%d.%09u\n", vals[0], vals[1]);
+ tmp1 = do_div(tmp, 1000000000LL);
+ tmp0 = tmp;
+ return snprintf(buf, len, "%d.%09u", tmp0, tmp1);
case IIO_VAL_INT_MULTIPLE:
{
int i;
- int len = 0;
+ int l = 0;
- for (i = 0; i < size; ++i)
- len += snprintf(&buf[len], PAGE_SIZE - len, "%d ",
- vals[i]);
- len += snprintf(&buf[len], PAGE_SIZE - len, "\n");
- return len;
+ for (i = 0; i < size; ++i) {
+ l += snprintf(&buf[l], len - l, "%d ", vals[i]);
+ if (l >= len)
+ break;
+ }
+ return l;
}
default:
return 0;
}
}
+
+/**
+ * iio_format_value() - Formats a IIO value into its string representation
+ * @buf: The buffer to which the formatted value gets written
+ * which is assumed to be big enough (i.e. PAGE_SIZE).
+ * @type: One of the IIO_VAL_... constants. This decides how the val
+ * and val2 parameters are formatted.
+ * @size: Number of IIO value entries contained in vals
+ * @vals: Pointer to the values, exact meaning depends on the
+ * type parameter.
+ *
+ * Return: 0 by default, a negative number on failure or the
+ * total number of characters written for a type that belongs
+ * to the IIO_VAL_... constant.
+ */
+ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals)
+{
+ ssize_t len;
+
+ len = __iio_format_value(buf, PAGE_SIZE, type, size, vals);
+ if (len >= PAGE_SIZE - 1)
+ return -EFBIG;
+
+ return len + sprintf(buf + len, "\n");
+}
EXPORT_SYMBOL_GPL(iio_format_value);
static ssize_t iio_read_channel_info(struct device *dev,
@@ -664,6 +680,119 @@ static ssize_t iio_read_channel_info(struct device *dev,
return iio_format_value(buf, ret, val_len, vals);
}
+static ssize_t iio_format_avail_list(char *buf, const int *vals,
+ int type, int length)
+{
+ int i;
+ ssize_t len = 0;
+
+ switch (type) {
+ case IIO_VAL_INT:
+ for (i = 0; i < length; i++) {
+ len += __iio_format_value(buf + len, PAGE_SIZE - len,
+ type, 1, &vals[i]);
+ if (len >= PAGE_SIZE)
+ return -EFBIG;
+ if (i < length - 1)
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ " ");
+ else
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "\n");
+ if (len >= PAGE_SIZE)
+ return -EFBIG;
+ }
+ break;
+ default:
+ for (i = 0; i < length / 2; i++) {
+ len += __iio_format_value(buf + len, PAGE_SIZE - len,
+ type, 2, &vals[i * 2]);
+ if (len >= PAGE_SIZE)
+ return -EFBIG;
+ if (i < length / 2 - 1)
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ " ");
+ else
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "\n");
+ if (len >= PAGE_SIZE)
+ return -EFBIG;
+ }
+ }
+
+ return len;
+}
+
+static ssize_t iio_format_avail_range(char *buf, const int *vals, int type)
+{
+ int i;
+ ssize_t len;
+
+ len = snprintf(buf, PAGE_SIZE, "[");
+ switch (type) {
+ case IIO_VAL_INT:
+ for (i = 0; i < 3; i++) {
+ len += __iio_format_value(buf + len, PAGE_SIZE - len,
+ type, 1, &vals[i]);
+ if (len >= PAGE_SIZE)
+ return -EFBIG;
+ if (i < 2)
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ " ");
+ else
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "]\n");
+ if (len >= PAGE_SIZE)
+ return -EFBIG;
+ }
+ break;
+ default:
+ for (i = 0; i < 3; i++) {
+ len += __iio_format_value(buf + len, PAGE_SIZE - len,
+ type, 2, &vals[i * 2]);
+ if (len >= PAGE_SIZE)
+ return -EFBIG;
+ if (i < 2)
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ " ");
+ else
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "]\n");
+ if (len >= PAGE_SIZE)
+ return -EFBIG;
+ }
+ }
+
+ return len;
+}
+
+static ssize_t iio_read_channel_info_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ const int *vals;
+ int ret;
+ int length;
+ int type;
+
+ ret = indio_dev->info->read_avail(indio_dev, this_attr->c,
+ &vals, &type, &length,
+ this_attr->address);
+
+ if (ret < 0)
+ return ret;
+ switch (ret) {
+ case IIO_AVAIL_LIST:
+ return iio_format_avail_list(buf, vals, type, length);
+ case IIO_AVAIL_RANGE:
+ return iio_format_avail_range(buf, vals, type);
+ default:
+ return -EINVAL;
+ }
+}
+
/**
* iio_str_to_fixpoint() - Parse a fixed-point number from a string
* @str: The string to parse
@@ -980,6 +1109,40 @@ static int iio_device_add_info_mask_type(struct iio_dev *indio_dev,
return attrcount;
}
+static int iio_device_add_info_mask_type_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ enum iio_shared_by shared_by,
+ const long *infomask)
+{
+ int i, ret, attrcount = 0;
+ char *avail_postfix;
+
+ for_each_set_bit(i, infomask, sizeof(infomask) * 8) {
+ avail_postfix = kasprintf(GFP_KERNEL,
+ "%s_available",
+ iio_chan_info_postfix[i]);
+ if (!avail_postfix)
+ return -ENOMEM;
+
+ ret = __iio_add_chan_devattr(avail_postfix,
+ chan,
+ &iio_read_channel_info_avail,
+ NULL,
+ i,
+ shared_by,
+ &indio_dev->dev,
+ &indio_dev->channel_attr_list);
+ kfree(avail_postfix);
+ if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE))
+ continue;
+ else if (ret < 0)
+ return ret;
+ attrcount++;
+ }
+
+ return attrcount;
+}
+
static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan)
{
@@ -995,6 +1158,14 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
return ret;
attrcount += ret;
+ ret = iio_device_add_info_mask_type_avail(indio_dev, chan,
+ IIO_SEPARATE,
+ &chan->
+ info_mask_separate_available);
+ if (ret < 0)
+ return ret;
+ attrcount += ret;
+
ret = iio_device_add_info_mask_type(indio_dev, chan,
IIO_SHARED_BY_TYPE,
&chan->info_mask_shared_by_type);
@@ -1002,6 +1173,14 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
return ret;
attrcount += ret;
+ ret = iio_device_add_info_mask_type_avail(indio_dev, chan,
+ IIO_SHARED_BY_TYPE,
+ &chan->
+ info_mask_shared_by_type_available);
+ if (ret < 0)
+ return ret;
+ attrcount += ret;
+
ret = iio_device_add_info_mask_type(indio_dev, chan,
IIO_SHARED_BY_DIR,
&chan->info_mask_shared_by_dir);
@@ -1009,6 +1188,13 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
return ret;
attrcount += ret;
+ ret = iio_device_add_info_mask_type_avail(indio_dev, chan,
+ IIO_SHARED_BY_DIR,
+ &chan->info_mask_shared_by_dir_available);
+ if (ret < 0)
+ return ret;
+ attrcount += ret;
+
ret = iio_device_add_info_mask_type(indio_dev, chan,
IIO_SHARED_BY_ALL,
&chan->info_mask_shared_by_all);
@@ -1016,6 +1202,13 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
return ret;
attrcount += ret;
+ ret = iio_device_add_info_mask_type_avail(indio_dev, chan,
+ IIO_SHARED_BY_ALL,
+ &chan->info_mask_shared_by_all_available);
+ if (ret < 0)
+ return ret;
+ attrcount += ret;
+
if (chan->ext_info) {
unsigned int i = 0;
for (ext_info = chan->ext_info; ext_info->name; ext_info++) {
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index 4591d8ea41bd..849d524645e8 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -225,12 +225,22 @@ struct iio_event_spec {
* endianness: little or big endian
* @info_mask_separate: What information is to be exported that is specific to
* this channel.
+ * @info_mask_separate_available: What availability information is to be
+ * exported that is specific to this channel.
* @info_mask_shared_by_type: What information is to be exported that is shared
* by all channels of the same type.
+ * @info_mask_shared_by_type_available: What availability information is to be
+ * exported that is shared by all channels of the same
+ * type.
* @info_mask_shared_by_dir: What information is to be exported that is shared
* by all channels of the same direction.
+ * @info_mask_shared_by_dir_available: What availability information is to be
+ * exported that is shared by all channels of the same
+ * direction.
* @info_mask_shared_by_all: What information is to be exported that is shared
* by all channels.
+ * @info_mask_shared_by_all_available: What availability information is to be
+ * exported that is shared by all channels.
* @event_spec: Array of events which should be registered for this
* channel.
* @num_event_specs: Size of the event_spec array.
@@ -269,9 +279,13 @@ struct iio_chan_spec {
enum iio_endian endianness;
} scan_type;
long info_mask_separate;
+ long info_mask_separate_available;
long info_mask_shared_by_type;
+ long info_mask_shared_by_type_available;
long info_mask_shared_by_dir;
+ long info_mask_shared_by_dir_available;
long info_mask_shared_by_all;
+ long info_mask_shared_by_all_available;
const struct iio_event_spec *event_spec;
unsigned int num_event_specs;
const struct iio_chan_spec_ext_info *ext_info;
@@ -349,6 +363,14 @@ struct iio_dev;
* max_len specifies maximum number of elements
* vals pointer can contain. val_len is used to return
* length of valid elements in vals.
+ * @read_avail: function to return the available values from the device.
+ * mask specifies which value. Note 0 means the available
+ * values for the channel in question. Return value
+ * specifies if a IIO_AVAIL_LIST or a IIO_AVAIL_RANGE is
+ * returned in vals. The type of the vals are returned in
+ * type and the number of vals is returned in length. For
+ * ranges, there are always three vals returned; min, step
+ * and max. For lists, all possible values are enumerated.
* @write_raw: function to write a value to the device.
* Parameters are the same as for read_raw.
* @write_raw_get_fmt: callback function to query the expected
@@ -397,6 +419,13 @@ struct iio_info {
int *val_len,
long mask);
+ int (*read_avail)(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals,
+ int *type,
+ int *length,
+ long mask);
+
int (*write_raw)(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val,
diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
index 32b579525004..2aa7b6384d64 100644
--- a/include/linux/iio/types.h
+++ b/include/linux/iio/types.h
@@ -29,4 +29,9 @@ enum iio_event_info {
#define IIO_VAL_FRACTIONAL 10
#define IIO_VAL_FRACTIONAL_LOG2 11
+enum iio_available_type {
+ IIO_AVAIL_LIST,
+ IIO_AVAIL_RANGE,
+};
+
#endif /* _IIO_TYPES_H_ */