256 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * kcomedilib/kcomedilib.c4 * a comedlib interface for kernel modules5 *6 * COMEDI - Linux Control and Measurement Device Interface7 * 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 <linux/comedi.h>20#include <linux/comedi/comedidev.h>21#include <linux/comedi/comedilib.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 else49 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 else238 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