linux/drivers/staging/comedi/kcomedilib/kcomedilib_main.c
<<
>>
Prefs
   1/*
   2 * kcomedilib/kcomedilib.c
   3 * a comedlib interface for kernel modules
   4 *
   5 * COMEDI - Linux Control and Measurement Device Interface
   6 * Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 */
  18
  19#include <linux/module.h>
  20
  21#include <linux/errno.h>
  22#include <linux/kernel.h>
  23#include <linux/sched.h>
  24#include <linux/fcntl.h>
  25#include <linux/mm.h>
  26#include <linux/io.h>
  27
  28#include "../comedi.h"
  29#include "../comedilib.h"
  30#include "../comedidev.h"
  31
  32MODULE_AUTHOR("David Schleef <ds@schleef.org>");
  33MODULE_DESCRIPTION("Comedi kernel library");
  34MODULE_LICENSE("GPL");
  35
  36struct comedi_device *comedi_open(const char *filename)
  37{
  38        struct comedi_device *dev, *retval = NULL;
  39        unsigned int minor;
  40
  41        if (strncmp(filename, "/dev/comedi", 11) != 0)
  42                return NULL;
  43
  44        if (kstrtouint(filename + 11, 0, &minor))
  45                return NULL;
  46
  47        if (minor >= COMEDI_NUM_BOARD_MINORS)
  48                return NULL;
  49
  50        dev = comedi_dev_get_from_minor(minor);
  51        if (!dev)
  52                return NULL;
  53
  54        down_read(&dev->attach_lock);
  55        if (dev->attached)
  56                retval = dev;
  57        else
  58                retval = NULL;
  59        up_read(&dev->attach_lock);
  60
  61        if (!retval)
  62                comedi_dev_put(dev);
  63
  64        return retval;
  65}
  66EXPORT_SYMBOL_GPL(comedi_open);
  67
  68int comedi_close(struct comedi_device *dev)
  69{
  70        comedi_dev_put(dev);
  71        return 0;
  72}
  73EXPORT_SYMBOL_GPL(comedi_close);
  74
  75static int comedi_do_insn(struct comedi_device *dev,
  76                          struct comedi_insn *insn,
  77                          unsigned int *data)
  78{
  79        struct comedi_subdevice *s;
  80        int ret;
  81
  82        mutex_lock(&dev->mutex);
  83
  84        if (!dev->attached) {
  85                ret = -EINVAL;
  86                goto error;
  87        }
  88
  89        /* a subdevice instruction */
  90        if (insn->subdev >= dev->n_subdevices) {
  91                ret = -EINVAL;
  92                goto error;
  93        }
  94        s = &dev->subdevices[insn->subdev];
  95
  96        if (s->type == COMEDI_SUBD_UNUSED) {
  97                dev_err(dev->class_dev,
  98                        "%d not usable subdevice\n", insn->subdev);
  99                ret = -EIO;
 100                goto error;
 101        }
 102
 103        /* XXX check lock */
 104
 105        ret = comedi_check_chanlist(s, 1, &insn->chanspec);
 106        if (ret < 0) {
 107                dev_err(dev->class_dev, "bad chanspec\n");
 108                ret = -EINVAL;
 109                goto error;
 110        }
 111
 112        if (s->busy) {
 113                ret = -EBUSY;
 114                goto error;
 115        }
 116        s->busy = dev;
 117
 118        switch (insn->insn) {
 119        case INSN_BITS:
 120                ret = s->insn_bits(dev, s, insn, data);
 121                break;
 122        case INSN_CONFIG:
 123                /* XXX should check instruction length */
 124                ret = s->insn_config(dev, s, insn, data);
 125                break;
 126        default:
 127                ret = -EINVAL;
 128                break;
 129        }
 130
 131        s->busy = NULL;
 132error:
 133
 134        mutex_unlock(&dev->mutex);
 135        return ret;
 136}
 137
 138int comedi_dio_get_config(struct comedi_device *dev, unsigned int subdev,
 139                          unsigned int chan, unsigned int *io)
 140{
 141        struct comedi_insn insn;
 142        unsigned int data[2];
 143        int ret;
 144
 145        memset(&insn, 0, sizeof(insn));
 146        insn.insn = INSN_CONFIG;
 147        insn.n = 2;
 148        insn.subdev = subdev;
 149        insn.chanspec = CR_PACK(chan, 0, 0);
 150        data[0] = INSN_CONFIG_DIO_QUERY;
 151        data[1] = 0;
 152        ret = comedi_do_insn(dev, &insn, data);
 153        if (ret >= 0)
 154                *io = data[1];
 155        return ret;
 156}
 157EXPORT_SYMBOL_GPL(comedi_dio_get_config);
 158
 159int comedi_dio_config(struct comedi_device *dev, unsigned int subdev,
 160                      unsigned int chan, unsigned int io)
 161{
 162        struct comedi_insn insn;
 163
 164        memset(&insn, 0, sizeof(insn));
 165        insn.insn = INSN_CONFIG;
 166        insn.n = 1;
 167        insn.subdev = subdev;
 168        insn.chanspec = CR_PACK(chan, 0, 0);
 169
 170        return comedi_do_insn(dev, &insn, &io);
 171}
 172EXPORT_SYMBOL_GPL(comedi_dio_config);
 173
 174int comedi_dio_bitfield2(struct comedi_device *dev, unsigned int subdev,
 175                         unsigned int mask, unsigned int *bits,
 176                         unsigned int base_channel)
 177{
 178        struct comedi_insn insn;
 179        unsigned int data[2];
 180        unsigned int n_chan;
 181        unsigned int shift;
 182        int ret;
 183
 184        base_channel = CR_CHAN(base_channel);
 185        n_chan = comedi_get_n_channels(dev, subdev);
 186        if (base_channel >= n_chan)
 187                return -EINVAL;
 188
 189        memset(&insn, 0, sizeof(insn));
 190        insn.insn = INSN_BITS;
 191        insn.chanspec = base_channel;
 192        insn.n = 2;
 193        insn.subdev = subdev;
 194
 195        data[0] = mask;
 196        data[1] = *bits;
 197
 198        /*
 199         * Most drivers ignore the base channel in insn->chanspec.
 200         * Fix this here if the subdevice has <= 32 channels.
 201         */
 202        if (n_chan <= 32) {
 203                shift = base_channel;
 204                if (shift) {
 205                        insn.chanspec = 0;
 206                        data[0] <<= shift;
 207                        data[1] <<= shift;
 208                }
 209        } else {
 210                shift = 0;
 211        }
 212
 213        ret = comedi_do_insn(dev, &insn, data);
 214        *bits = data[1] >> shift;
 215        return ret;
 216}
 217EXPORT_SYMBOL_GPL(comedi_dio_bitfield2);
 218
 219int comedi_find_subdevice_by_type(struct comedi_device *dev, int type,
 220                                  unsigned int subd)
 221{
 222        struct comedi_subdevice *s;
 223        int ret = -ENODEV;
 224
 225        down_read(&dev->attach_lock);
 226        if (dev->attached)
 227                for (; subd < dev->n_subdevices; subd++) {
 228                        s = &dev->subdevices[subd];
 229                        if (s->type == type) {
 230                                ret = subd;
 231                                break;
 232                        }
 233                }
 234        up_read(&dev->attach_lock);
 235        return ret;
 236}
 237EXPORT_SYMBOL_GPL(comedi_find_subdevice_by_type);
 238
 239int comedi_get_n_channels(struct comedi_device *dev, unsigned int subdevice)
 240{
 241        int n;
 242
 243        down_read(&dev->attach_lock);
 244        if (!dev->attached || subdevice >= dev->n_subdevices)
 245                n = 0;
 246        else
 247                n = dev->subdevices[subdevice].n_chan;
 248        up_read(&dev->attach_lock);
 249
 250        return n;
 251}
 252EXPORT_SYMBOL_GPL(comedi_get_n_channels);
 253