[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1319035628-32580-5-git-send-email-jic23@cam.ac.uk>
Date: Wed, 19 Oct 2011 15:47:06 +0100
From: Jonathan Cameron <jic23@....ac.uk>
To: linux-kernel@...r.kernel.org, linux-iio@...r.kernel.org
Cc: linus.ml.walleij@...il.com, zdevai@...il.com,
linux@....linux.org.uk, arnd@...db.de,
broonie@...nsource.wolfsonmicro.com, gregkh@...e.de,
lm-sensors@...sensors.org, guenter.roeck@...csson.com,
khali@...ux-fr.org, Jonathan Cameron <jic23@....ac.uk>
Subject: [PATCH 4/6] IIO:CORE add in kernel interface mapping and getting IIO channels.
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@....ac.uk>
---
drivers/Makefile | 2 +-
drivers/iio/Makefile | 1 +
drivers/iio/iio.c | 279 +++++++++++++++++++++++++++++++++++++++++++-
drivers/iio/inkern.c | 21 ++++
include/linux/iio/iio.h | 7 +-
include/linux/iio/inkern.h | 87 ++++++++++++++
6 files changed, 390 insertions(+), 7 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 9e6acc1..e094c40 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_IDA(iio_ida);
@@ -68,6 +70,278 @@ static const char * const iio_chan_info_postfix[] = {
= "quadrature_correction_raw",
};
+static void iio_dev_release(struct device *device);
+static struct device_type iio_dev_type = {
+ .name = "iio_device",
+ .release = iio_dev_release,
+};
+
+static int iio_match_dev(struct device *dev, void *data)
+{
+ struct iio_dev *indio_dev;
+ struct device *dev2 = data;
+
+ if (dev->type != &iio_dev_type)
+ return 0;
+
+ indio_dev = container_of(dev, struct iio_dev, dev);
+ if (indio_dev->info->get_hardware_id)
+ return indio_dev->info->get_hardware_id(indio_dev) == dev2;
+ else
+ return indio_dev->dev.parent == dev2;
+}
+
+static int iio_match_dev_name(struct device *dev, void *data)
+{
+ struct iio_dev *indio_dev;
+ const char *name = data;
+
+ if (dev->type != &iio_dev_type)
+ return 0;
+
+ indio_dev = container_of(dev, struct iio_dev, dev);
+ if (indio_dev->info->get_hardware_id)
+ return !strcmp(dev_name(indio_dev->info
+ ->get_hardware_id(indio_dev)),
+ name);
+ else if (indio_dev->dev.parent)
+ return !strcmp(dev_name(indio_dev->dev.parent), name);
+ return 0;
+}
+
+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 *indio_dev = NULL;
+ const struct iio_chan_spec *chan = NULL;
+ struct device *dev_i;
+ 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) ||
+ (name && strcmp(name, c_i->consumer_dev_name) != 0) ||
+ (channel_name &&
+ strcmp(channel_name, c_i->consumer_channel) != 0))
+ continue;
+ c = c_i;
+ break;
+ }
+ if (c == NULL)
+ return ERR_PTR(-ENODEV);
+
+ /* now find the iio device if it has been registered */
+ if (c->adc_dev)
+ dev_i = bus_find_device(&iio_bus_type, NULL, c->adc_dev,
+ &iio_match_dev);
+ else if (c->adc_dev_name)
+ dev_i = bus_find_device(&iio_bus_type, NULL,
+ (void *)c->adc_dev_name,
+ &iio_match_dev_name);
+ else
+ return ERR_PTR(-EINVAL);
+ if (IS_ERR(dev_i))
+ return (void *)dev_i;
+ if (dev_i == NULL)
+ return ERR_PTR(-ENODEV);
+ indio_dev = container_of(dev_i, struct iio_dev, dev);
+
+ /* 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);
+
+static const struct iio_chan_spec
+*iio_chan_spec_from_name(const struct iio_dev *indio_dev,
+ const char *name)
+{
+ int i;
+ const struct iio_chan_spec *chan = NULL;
+ for (i = 0; i < indio_dev->num_channels; i++)
+ if (indio_dev->channels[i].datasheet_name &&
+ strcmp(name, indio_dev->channels[i].datasheet_name) == 0) {
+ chan = &indio_dev->channels[i];
+ break;
+ }
+ return chan;
+}
+
+struct iio_channel **iio_channel_get_all(const struct device *dev,
+ const char *name)
+{
+ struct iio_channel **chans;
+ struct iio_map *c = NULL;
+ struct iio_dev *indio_dev;
+ int nummaps = 0;
+ int mapind = 0;
+ int i, ret;
+ struct device *dev_i;
+
+ if (dev == NULL && name == NULL) {
+ ret = -EINVAL;
+ goto error_ret;
+ }
+
+ /* first count the matching maps */
+ list_for_each_entry(c, &iio_map_list, l)
+ if ((dev && dev != c->consumer_dev) ||
+ (name && strcmp(name, c->consumer_dev_name) != 0))
+ continue;
+ else
+ nummaps++;
+
+ if (nummaps == 0) {
+ ret = -ENODEV;
+ goto error_ret;
+ }
+
+ chans = kzalloc(sizeof(*chans)*(nummaps + 1), GFP_KERNEL);
+ if (chans == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ for (i = 0; i < nummaps; i++) {
+ chans[i] = kzalloc(sizeof(*chans[0]), GFP_KERNEL);
+ if (chans[i] == NULL) {
+ ret = -ENOMEM;
+ goto error_free_chans;
+ }
+ }
+
+ /* for each map fill in the chans element */
+ list_for_each_entry(c, &iio_map_list, l) {
+ dev_i = NULL;
+ if (dev && dev != c->consumer_dev)
+ continue;
+ if (name && strcmp(name, c->consumer_dev_name) != 0)
+ continue;
+ while (1) {
+ if (c->adc_dev) {
+ dev_i = bus_find_device(&iio_bus_type,
+ dev_i,
+ c->adc_dev,
+ &iio_match_dev);
+ } else if (c->adc_dev_name) {
+ dev_i = bus_find_device(&iio_bus_type,
+ dev_i,
+ (void *)c->adc_dev_name,
+ &iio_match_dev_name);
+ } else {
+ ret = -EINVAL;
+ goto error_free_chans;
+ }
+ if (IS_ERR(dev_i)) {
+ ret = PTR_ERR(dev_i);
+ goto error_free_chans;
+ }
+ if (dev_i == NULL)
+ break;
+
+ indio_dev = container_of(dev_i, struct iio_dev, dev);
+ chans[mapind]->indio_dev = indio_dev;
+ chans[mapind]->channel =
+ iio_chan_spec_from_name(indio_dev,
+ c->adc_channel_label);
+ if (chans[mapind]->channel == NULL) {
+ ret = -EINVAL;
+ put_device(&indio_dev->dev);
+ goto error_free_chans;
+ }
+ mapind++;
+ }
+ }
+ return chans;
+
+error_free_chans:
+ for (i = 0; i < nummaps; i++)
+ if (chans[i]) {
+ put_device(&chans[i]->indio_dev->dev);
+ kfree(chans[i]);
+ }
+error_ret:
+
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(iio_channel_get_all);
+
+void iio_channel_release(struct iio_channel *channel)
+{
+ put_device(&channel->indio_dev->dev);
+ kfree(channel);
+}
+EXPORT_SYMBOL_GPL(iio_channel_release);
+
+void iio_channel_release_all(struct iio_channel **channels)
+{
+ int i = 0;
+ struct iio_channel *chan = channels[i];
+
+ while (chan) {
+ put_device(&chan->indio_dev->dev);
+ kfree(chan);
+ i++;
+ chan = channels[i];
+ }
+ kfree(channels);
+}
+EXPORT_SYMBOL_GPL(iio_channel_release_all);
+
+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);
+
+int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2)
+{
+ /* Does this channel have shared scale? */
+ if (chan->channel->info_mask & (1 << IIO_CHAN_INFO_SCALE_SHARED))
+ return chan->indio_dev
+ ->info->read_raw(chan->indio_dev,
+ chan->channel,
+ val, val2,
+ (1 << IIO_CHAN_INFO_SCALE_SHARED));
+ else if (chan->channel->info_mask & (1 << IIO_CHAN_INFO_SCALE_SEPARATE))
+ return chan->indio_dev
+ ->info->read_raw(chan->indio_dev,
+ chan->channel,
+ val, val2,
+ (1 << IIO_CHAN_INFO_SCALE_SEPARATE));
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(iio_read_channel_scale);
+
+enum iio_chan_type iio_get_channel_type(struct iio_channel *channel)
+{
+ return channel->channel->type;
+}
+EXPORT_SYMBOL_GPL(iio_get_channel_type);
+
static void iio_device_free_read_attr(struct iio_dev *indio_dev,
struct iio_dev_attr *p)
{
@@ -93,11 +367,6 @@ static void iio_dev_release(struct device *device)
iio_device_unregister_sysfs(indio_dev);
}
-static struct device_type iio_dev_type = {
- .name = "iio_device",
- .release = iio_dev_release,
-};
-
struct iio_dev *iio_device_allocate(int sizeof_priv)
{
struct iio_dev *dev;
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 8b98e92..472ade9 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -122,7 +122,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;
@@ -142,6 +145,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);
};
/**
@@ -176,6 +180,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..6eef0a2
--- /dev/null
+++ b/include/linux/iio/inkern.h
@@ -0,0 +1,87 @@
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/iio/chan_spec.h>
+
+#ifndef _IIO_INKERN_H_
+#define _IIO_INKERN_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_channel_get_all() - get all channels associated with a client
+ *
+ * returns a null terminated array of pointers to iio_channel structures.
+ */
+struct iio_channel **iio_channel_get_all(const struct device *dev,
+ const char *name);
+
+void iio_channel_release_all(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_get_channel_type() - get the type of a channel
+ * @channel: the channel being queried.
+ *
+ * returns the enum iio_chan_type of the channel
+ */
+enum iio_chan_type iio_get_channel_type(struct iio_channel *channel);
+
+/**
+ * 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);
+
+#endif
--
1.7.7
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists