[PATCH 4/6] IIO:Core add in kernel interface mapping and getting IIO channels.

From: Jonathan Cameron
Date: Tue Oct 18 2011 - 11:29:45 EST


Two elements here:
* Map as defined in include/linux/iio/inkern.h
* Matching code to actually get the iio_dev and channel
that we want from the global list of IIO devices.

Signed-off-by: Jonathan Cameron <jic23@xxxxxxxxx>
---
drivers/Makefile | 2 +-
drivers/iio/Makefile | 1 +
drivers/iio/iio.c | 112 ++++++++++++++++++++++++++++++++++++++++++++
drivers/iio/inkern.c | 21 ++++++++
include/linux/iio/iio.h | 7 ++-
include/linux/iio/inkern.h | 63 +++++++++++++++++++++++++
6 files changed, 204 insertions(+), 2 deletions(-)

diff --git a/drivers/Makefile b/drivers/Makefile
index df39628..2b389c5 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -130,5 +130,5 @@ obj-$(CONFIG_VIRT_DRIVERS) += virt/

obj-$(CONFIG_HYPERV) += hv/

-obj-$(CONFIG_IIO) += iio/
+obj-y += iio/

diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index db3c426..cfb588a 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -1,6 +1,7 @@
#
# Makefile for the Industrial I/O subsystem
#
+obj-y = inkern.o

obj-$(CONFIG_IIO) += iio.o
industrialio-y := core.o
diff --git a/drivers/iio/iio.c b/drivers/iio/iio.c
index 246a093..ae2daf0 100644
--- a/drivers/iio/iio.c
+++ b/drivers/iio/iio.c
@@ -12,8 +12,10 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/idr.h>
+#include <linux/err.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
+#include <linux/iio/inkern.h>

static DEFINE_MUTEX(iio_device_list_lock);
static LIST_HEAD(iio_device_list);
@@ -71,6 +73,116 @@ static const char * const iio_chan_info_postfix[] = {
= "quadrature_correction_raw",
};

+struct iio_channel *iio_channel_get(const struct device *dev,
+ const char *name,
+ const char *channel_name)
+{
+ struct iio_map *c_i = NULL, *c = NULL;
+ struct iio_dev *i_i = NULL, *indio_dev = NULL;
+ const struct iio_chan_spec *chan = NULL;
+
+ int i;
+ struct iio_channel *channel;
+
+ if (dev == NULL && name == NULL && channel_name == NULL)
+ return ERR_PTR(-ENODEV);
+ /* first find matching entry the channel map */
+ list_for_each_entry(c_i, &iio_map_list, l) {
+ if (dev && dev != c_i->consumer_dev)
+ continue;
+ if (name && strcmp(name, c_i->consumer_dev_name) != 0)
+ continue;
+
+ if (channel_name && strcmp(channel_name, c_i->consumer_channel)
+ != 0)
+ continue;
+ c = c_i;
+ break;
+ }
+ if (c == NULL)
+ return ERR_PTR(-ENODEV);
+
+ mutex_lock(&iio_device_list_lock);
+ /* now find the iio device if it has been registered */
+ if (c->adc_dev) {
+ list_for_each_entry(i_i, &iio_device_list,
+ dev_list_entry) {
+ if (i_i->info->get_hardware_id && c->adc_dev ==
+ indio_dev->info->get_hardware_id(indio_dev)) {
+ indio_dev = i_i;
+ break;
+ } else if (indio_dev->dev.parent &&
+ c->adc_dev == indio_dev->dev.parent) {
+ indio_dev = i_i;
+ break;
+ }
+ }
+ } else if (c->adc_dev_name) {
+ list_for_each_entry(i_i, &iio_device_list,
+ dev_list_entry) {
+ if (i_i->info->get_hardware_id) {
+ if (c->adc_dev_name ==
+ dev_name(i_i->info->get_hardware_id(i_i))) {
+ indio_dev = i_i;
+ break;
+ }
+ } else if (i_i->dev.parent) {
+ if (strcmp(c->adc_dev_name,
+ dev_name(i_i->dev.parent)) == 0) {
+ indio_dev = i_i;
+ break;
+ }
+ } else {
+ printk(KERN_ERR "invalid map\n");
+ mutex_unlock(&iio_device_list_lock);
+ return ERR_PTR(-ENODEV);
+ }
+ }
+ }
+ if (indio_dev)
+ get_device(&indio_dev->dev);
+ mutex_unlock(&iio_device_list_lock);
+ if (indio_dev == NULL)
+ return ERR_PTR(-ENODEV);
+
+ /* finally verify the channel exists */
+ if (c->adc_channel_label)
+ for (i = 0; i < indio_dev->num_channels; i++)
+ if (indio_dev->channels[i].datasheet_name &&
+ strcmp(c->adc_channel_label,
+ indio_dev->channels[i].datasheet_name)
+ == 0) {
+ chan = &indio_dev->channels[i];
+ break;
+ }
+ channel = kmalloc(sizeof(*channel), GFP_KERNEL);
+ if (channel == NULL)
+ return ERR_PTR(-ENOMEM);
+ channel->indio_dev = indio_dev;
+ if (chan == NULL)
+ channel->channel = &indio_dev->channels[c->channel_number];
+ else
+ channel->channel = chan;
+ return channel;
+}
+EXPORT_SYMBOL_GPL(iio_channel_get);
+
+void iio_channel_release(struct iio_channel *channel)
+{
+ put_device(&channel->indio_dev->dev);
+ kfree(channel);
+}
+EXPORT_SYMBOL_GPL(iio_channel_release);
+
+
+int iio_read_channel_raw(struct iio_channel *chan, int *val)
+{
+ int val2;
+ return chan->indio_dev->info->read_raw(chan->indio_dev, chan->channel,
+ val, &val2, 0);
+}
+EXPORT_SYMBOL_GPL(iio_read_channel_raw);
+
static void iio_device_free_read_attr(struct iio_dev *indio_dev,
struct iio_dev_attr *p)
{
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
new file mode 100644
index 0000000..e2ba5d6
--- /dev/null
+++ b/drivers/iio/inkern.c
@@ -0,0 +1,21 @@
+/* The industrial I/O core in kernel channel mapping
+ *
+ * Copyright (c) 2011 Jonathan Cameron
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <linux/iio/inkern.h>
+#include <linux/err.h>
+
+LIST_HEAD(iio_map_list);
+EXPORT_SYMBOL_GPL(iio_map_list);
+void iio_map_array_register(struct iio_map *map, int nummaps)
+{
+ int i;
+ for (i = 0; i < nummaps; i++)
+ list_add(&map[i].l, &iio_map_list);
+}
+EXPORT_SYMBOL(iio_map_array_register);
+
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index f14e7dc..34c51b7 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -153,7 +153,10 @@ struct iio_dev;
* @write_raw_get_fmt: callback function to query the expected
* format/precision. If not set by the driver, write_raw
* returns IIO_VAL_INT_PLUS_MICRO.
- **/
+ * @get_hardware_id: obtain device relating to hardware. Typically based on
+ * the parent device (actual hardware). Note that if
+ * not specified then iio_dev.dev->parent is used.
+ */
struct iio_info {
struct module *driver_module;
const struct attribute_group *attrs;
@@ -173,6 +176,7 @@ struct iio_info {
int (*write_raw_get_fmt)(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
long mask);
+ struct device *(*get_hardware_id)(struct iio_dev *indio_dev);
};

/**
@@ -208,6 +212,7 @@ struct iio_dev {
#define IIO_MAX_GROUPS 1
const struct attribute_group *groups[IIO_MAX_GROUPS + 1];
int groupcounter;
+ struct list_head dev_list_entry;
};

/**
diff --git a/include/linux/iio/inkern.h b/include/linux/iio/inkern.h
new file mode 100644
index 0000000..89b43d32
--- /dev/null
+++ b/include/linux/iio/inkern.h
@@ -0,0 +1,63 @@
+#include <linux/device.h>
+#include <linux/list.h>
+
+struct iio_dev;
+struct iio_chan_spec;
+
+struct iio_channel {
+ struct iio_dev *indio_dev;
+ const struct iio_chan_spec *channel;
+};
+
+extern struct list_head iio_map_list;
+
+struct iio_map {
+ /* iio device side */
+ struct device *adc_dev;
+ const char *adc_dev_name;
+ const char *adc_channel_label;
+ int channel_number; /*naughty starting point */
+
+ /* consumer side */
+ struct device *consumer_dev;
+ const char *consumer_dev_name;
+ const char *consumer_channel;
+
+ /* management - probably neater ways of doing this */
+ struct list_head l;
+};
+
+void iio_map_array_register(struct iio_map *map, int nummaps);
+/**
+ * iio_channel_get() - get an opaque reference to a specified device.
+ */
+struct iio_channel *iio_channel_get(const struct device *dev,
+ const char *name,
+ const char *consumer_channel);
+void iio_channel_release(struct iio_channel *chan);
+
+/**
+ * iio_read_channel_raw() - read from a given channel
+ * @channel: the channel being queried.
+ * @val: value read back.
+ *
+ * Note raw reads from iio channels are in adc counts and hence
+ * scale will need to be applied if standard units required.
+ *
+ * Maybe want to pass the type as a sanity check.
+ */
+int iio_read_channel_raw(struct iio_channel *chan,
+ int *val);
+
+/**
+ * iio_read_channel_scale() - read the scale value for a channel
+ * @channel: the channel being queried.
+ * @val: first part of value read back.
+ * @val2: second part of value read back.
+ *
+ * Note returns a description of what is in val and val2, such
+ * as IIO_VAL_INT_PLUS_MICRO telling us we have a value of val
+ * + val2/1e6
+ */
+int iio_read_channel_scale(struct iio_channel *chan, int *val,
+ int *val2);
--
1.7.7

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/