linux/drivers/iio/inkern.c
<<
>>
Prefs
   1/* The industrial I/O core in kernel channel mapping
   2 *
   3 * Copyright (c) 2011 Jonathan Cameron
   4 *
   5 * This program is free software; you can redistribute it and/or modify it
   6 * under the terms of the GNU General Public License version 2 as published by
   7 * the Free Software Foundation.
   8 */
   9#include <linux/err.h>
  10#include <linux/export.h>
  11#include <linux/slab.h>
  12#include <linux/mutex.h>
  13
  14#include <linux/iio/iio.h>
  15#include "iio_core.h"
  16#include <linux/iio/machine.h>
  17#include <linux/iio/driver.h>
  18#include <linux/iio/consumer.h>
  19
  20struct iio_map_internal {
  21        struct iio_dev *indio_dev;
  22        struct iio_map *map;
  23        struct list_head l;
  24};
  25
  26static LIST_HEAD(iio_map_list);
  27static DEFINE_MUTEX(iio_map_list_lock);
  28
  29int iio_map_array_register(struct iio_dev *indio_dev, struct iio_map *maps)
  30{
  31        int i = 0, ret = 0;
  32        struct iio_map_internal *mapi;
  33
  34        if (maps == NULL)
  35                return 0;
  36
  37        mutex_lock(&iio_map_list_lock);
  38        while (maps[i].consumer_dev_name != NULL) {
  39                mapi = kzalloc(sizeof(*mapi), GFP_KERNEL);
  40                if (mapi == NULL) {
  41                        ret = -ENOMEM;
  42                        goto error_ret;
  43                }
  44                mapi->map = &maps[i];
  45                mapi->indio_dev = indio_dev;
  46                list_add(&mapi->l, &iio_map_list);
  47                i++;
  48        }
  49error_ret:
  50        mutex_unlock(&iio_map_list_lock);
  51
  52        return ret;
  53}
  54EXPORT_SYMBOL_GPL(iio_map_array_register);
  55
  56
  57/* Assumes the exact same array (e.g. memory locations)
  58 * used at unregistration as used at registration rather than
  59 * more complex checking of contents.
  60 */
  61int iio_map_array_unregister(struct iio_dev *indio_dev,
  62                             struct iio_map *maps)
  63{
  64        int i = 0, ret = 0;
  65        bool found_it;
  66        struct iio_map_internal *mapi;
  67
  68        if (maps == NULL)
  69                return 0;
  70
  71        mutex_lock(&iio_map_list_lock);
  72        while (maps[i].consumer_dev_name != NULL) {
  73                found_it = false;
  74                list_for_each_entry(mapi, &iio_map_list, l)
  75                        if (&maps[i] == mapi->map) {
  76                                list_del(&mapi->l);
  77                                kfree(mapi);
  78                                found_it = true;
  79                                break;
  80                        }
  81                if (!found_it) {
  82                        ret = -ENODEV;
  83                        goto error_ret;
  84                }
  85                i++;
  86        }
  87error_ret:
  88        mutex_unlock(&iio_map_list_lock);
  89
  90        return ret;
  91}
  92EXPORT_SYMBOL_GPL(iio_map_array_unregister);
  93
  94static const struct iio_chan_spec
  95*iio_chan_spec_from_name(const struct iio_dev *indio_dev, const char *name)
  96{
  97        int i;
  98        const struct iio_chan_spec *chan = NULL;
  99
 100        for (i = 0; i < indio_dev->num_channels; i++)
 101                if (indio_dev->channels[i].datasheet_name &&
 102                    strcmp(name, indio_dev->channels[i].datasheet_name) == 0) {
 103                        chan = &indio_dev->channels[i];
 104                        break;
 105                }
 106        return chan;
 107}
 108
 109
 110struct iio_channel *iio_channel_get(const char *name, const char *channel_name)
 111{
 112        struct iio_map_internal *c_i = NULL, *c = NULL;
 113        struct iio_channel *channel;
 114        int err;
 115
 116        if (name == NULL && channel_name == NULL)
 117                return ERR_PTR(-ENODEV);
 118
 119        /* first find matching entry the channel map */
 120        mutex_lock(&iio_map_list_lock);
 121        list_for_each_entry(c_i, &iio_map_list, l) {
 122                if ((name && strcmp(name, c_i->map->consumer_dev_name) != 0) ||
 123                    (channel_name &&
 124                     strcmp(channel_name, c_i->map->consumer_channel) != 0))
 125                        continue;
 126                c = c_i;
 127                iio_device_get(c->indio_dev);
 128                break;
 129        }
 130        mutex_unlock(&iio_map_list_lock);
 131        if (c == NULL)
 132                return ERR_PTR(-ENODEV);
 133
 134        channel = kzalloc(sizeof(*channel), GFP_KERNEL);
 135        if (channel == NULL) {
 136                err = -ENOMEM;
 137                goto error_no_mem;
 138        }
 139
 140        channel->indio_dev = c->indio_dev;
 141
 142        if (c->map->adc_channel_label) {
 143                channel->channel =
 144                        iio_chan_spec_from_name(channel->indio_dev,
 145                                                c->map->adc_channel_label);
 146
 147                if (channel->channel == NULL) {
 148                        err = -EINVAL;
 149                        goto error_no_chan;
 150                }
 151        }
 152
 153        return channel;
 154
 155error_no_chan:
 156        kfree(channel);
 157error_no_mem:
 158        iio_device_put(c->indio_dev);
 159        return ERR_PTR(err);
 160}
 161EXPORT_SYMBOL_GPL(iio_channel_get);
 162
 163void iio_channel_release(struct iio_channel *channel)
 164{
 165        iio_device_put(channel->indio_dev);
 166        kfree(channel);
 167}
 168EXPORT_SYMBOL_GPL(iio_channel_release);
 169
 170struct iio_channel *iio_channel_get_all(const char *name)
 171{
 172        struct iio_channel *chans;
 173        struct iio_map_internal *c = NULL;
 174        int nummaps = 0;
 175        int mapind = 0;
 176        int i, ret;
 177
 178        if (name == NULL)
 179                return ERR_PTR(-EINVAL);
 180
 181        mutex_lock(&iio_map_list_lock);
 182        /* first count the matching maps */
 183        list_for_each_entry(c, &iio_map_list, l)
 184                if (name && strcmp(name, c->map->consumer_dev_name) != 0)
 185                        continue;
 186                else
 187                        nummaps++;
 188
 189        if (nummaps == 0) {
 190                ret = -ENODEV;
 191                goto error_ret;
 192        }
 193
 194        /* NULL terminated array to save passing size */
 195        chans = kzalloc(sizeof(*chans)*(nummaps + 1), GFP_KERNEL);
 196        if (chans == NULL) {
 197                ret = -ENOMEM;
 198                goto error_ret;
 199        }
 200
 201        /* for each map fill in the chans element */
 202        list_for_each_entry(c, &iio_map_list, l) {
 203                if (name && strcmp(name, c->map->consumer_dev_name) != 0)
 204                        continue;
 205                chans[mapind].indio_dev = c->indio_dev;
 206                chans[mapind].data = c->map->consumer_data;
 207                chans[mapind].channel =
 208                        iio_chan_spec_from_name(chans[mapind].indio_dev,
 209                                                c->map->adc_channel_label);
 210                if (chans[mapind].channel == NULL) {
 211                        ret = -EINVAL;
 212                        goto error_free_chans;
 213                }
 214                iio_device_get(chans[mapind].indio_dev);
 215                mapind++;
 216        }
 217        if (mapind == 0) {
 218                ret = -ENODEV;
 219                goto error_free_chans;
 220        }
 221        mutex_unlock(&iio_map_list_lock);
 222
 223        return chans;
 224
 225error_free_chans:
 226        for (i = 0; i < nummaps; i++)
 227                iio_device_put(chans[i].indio_dev);
 228        kfree(chans);
 229error_ret:
 230        mutex_unlock(&iio_map_list_lock);
 231
 232        return ERR_PTR(ret);
 233}
 234EXPORT_SYMBOL_GPL(iio_channel_get_all);
 235
 236void iio_channel_release_all(struct iio_channel *channels)
 237{
 238        struct iio_channel *chan = &channels[0];
 239
 240        while (chan->indio_dev) {
 241                iio_device_put(chan->indio_dev);
 242                chan++;
 243        }
 244        kfree(channels);
 245}
 246EXPORT_SYMBOL_GPL(iio_channel_release_all);
 247
 248static int iio_channel_read(struct iio_channel *chan, int *val, int *val2,
 249        enum iio_chan_info_enum info)
 250{
 251        int unused;
 252
 253        if (val2 == NULL)
 254                val2 = &unused;
 255
 256        return chan->indio_dev->info->read_raw(chan->indio_dev, chan->channel,
 257                                                val, val2, info);
 258}
 259
 260int iio_read_channel_raw(struct iio_channel *chan, int *val)
 261{
 262        int ret;
 263
 264        mutex_lock(&chan->indio_dev->info_exist_lock);
 265        if (chan->indio_dev->info == NULL) {
 266                ret = -ENODEV;
 267                goto err_unlock;
 268        }
 269
 270        ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
 271err_unlock:
 272        mutex_unlock(&chan->indio_dev->info_exist_lock);
 273
 274        return ret;
 275}
 276EXPORT_SYMBOL_GPL(iio_read_channel_raw);
 277
 278static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan,
 279        int raw, int *processed, unsigned int scale)
 280{
 281        int scale_type, scale_val, scale_val2, offset;
 282        s64 raw64 = raw;
 283        int ret;
 284
 285        ret = iio_channel_read(chan, &offset, NULL, IIO_CHAN_INFO_SCALE);
 286        if (ret == 0)
 287                raw64 += offset;
 288
 289        scale_type = iio_channel_read(chan, &scale_val, &scale_val2,
 290                                        IIO_CHAN_INFO_SCALE);
 291        if (scale_type < 0)
 292                return scale_type;
 293
 294        switch (scale_type) {
 295        case IIO_VAL_INT:
 296                *processed = raw64 * scale_val;
 297                break;
 298        case IIO_VAL_INT_PLUS_MICRO:
 299                if (scale_val2 < 0)
 300                        *processed = -raw64 * scale_val;
 301                else
 302                        *processed = raw64 * scale_val;
 303                *processed += div_s64(raw64 * (s64)scale_val2 * scale,
 304                                      1000000LL);
 305                break;
 306        case IIO_VAL_INT_PLUS_NANO:
 307                if (scale_val2 < 0)
 308                        *processed = -raw64 * scale_val;
 309                else
 310                        *processed = raw64 * scale_val;
 311                *processed += div_s64(raw64 * (s64)scale_val2 * scale,
 312                                      1000000000LL);
 313                break;
 314        case IIO_VAL_FRACTIONAL:
 315                *processed = div_s64(raw64 * (s64)scale_val * scale,
 316                                     scale_val2);
 317                break;
 318        case IIO_VAL_FRACTIONAL_LOG2:
 319                *processed = (raw64 * (s64)scale_val * scale) >> scale_val2;
 320                break;
 321        default:
 322                return -EINVAL;
 323        }
 324
 325        return 0;
 326}
 327
 328int iio_convert_raw_to_processed(struct iio_channel *chan, int raw,
 329        int *processed, unsigned int scale)
 330{
 331        int ret;
 332
 333        mutex_lock(&chan->indio_dev->info_exist_lock);
 334        if (chan->indio_dev->info == NULL) {
 335                ret = -ENODEV;
 336                goto err_unlock;
 337        }
 338
 339        ret = iio_convert_raw_to_processed_unlocked(chan, raw, processed,
 340                                                        scale);
 341err_unlock:
 342        mutex_unlock(&chan->indio_dev->info_exist_lock);
 343
 344        return ret;
 345}
 346EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed);
 347
 348int iio_read_channel_processed(struct iio_channel *chan, int *val)
 349{
 350        int ret;
 351
 352        mutex_lock(&chan->indio_dev->info_exist_lock);
 353        if (chan->indio_dev->info == NULL) {
 354                ret = -ENODEV;
 355                goto err_unlock;
 356        }
 357
 358        if (iio_channel_has_info(chan->channel, IIO_CHAN_INFO_PROCESSED)) {
 359                ret = iio_channel_read(chan, val, NULL,
 360                                       IIO_CHAN_INFO_PROCESSED);
 361        } else {
 362                ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
 363                if (ret < 0)
 364                        goto err_unlock;
 365                ret = iio_convert_raw_to_processed_unlocked(chan, *val, val, 1);
 366        }
 367
 368err_unlock:
 369        mutex_unlock(&chan->indio_dev->info_exist_lock);
 370
 371        return ret;
 372}
 373EXPORT_SYMBOL_GPL(iio_read_channel_processed);
 374
 375int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2)
 376{
 377        int ret;
 378
 379        mutex_lock(&chan->indio_dev->info_exist_lock);
 380        if (chan->indio_dev->info == NULL) {
 381                ret = -ENODEV;
 382                goto err_unlock;
 383        }
 384
 385        ret = iio_channel_read(chan, val, val2, IIO_CHAN_INFO_SCALE);
 386err_unlock:
 387        mutex_unlock(&chan->indio_dev->info_exist_lock);
 388
 389        return ret;
 390}
 391EXPORT_SYMBOL_GPL(iio_read_channel_scale);
 392
 393int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type)
 394{
 395        int ret = 0;
 396        /* Need to verify underlying driver has not gone away */
 397
 398        mutex_lock(&chan->indio_dev->info_exist_lock);
 399        if (chan->indio_dev->info == NULL) {
 400                ret = -ENODEV;
 401                goto err_unlock;
 402        }
 403
 404        *type = chan->channel->type;
 405err_unlock:
 406        mutex_unlock(&chan->indio_dev->info_exist_lock);
 407
 408        return ret;
 409}
 410EXPORT_SYMBOL_GPL(iio_get_channel_type);
 411
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.