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 | 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 _______________________________________________ lm-sensors mailing list lm-sensors@xxxxxxxxxxxxxx http://lists.lm-sensors.org/mailman/listinfo/lm-sensors