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