18ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
28ffdff6aSGreg Kroah-Hartman /*
38ffdff6aSGreg Kroah-Hartman * kcomedilib/kcomedilib.c
48ffdff6aSGreg Kroah-Hartman * a comedlib interface for kernel modules
58ffdff6aSGreg Kroah-Hartman *
68ffdff6aSGreg Kroah-Hartman * COMEDI - Linux Control and Measurement Device Interface
78ffdff6aSGreg Kroah-Hartman * Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
88ffdff6aSGreg Kroah-Hartman */
98ffdff6aSGreg Kroah-Hartman
108ffdff6aSGreg Kroah-Hartman #include <linux/module.h>
118ffdff6aSGreg Kroah-Hartman
128ffdff6aSGreg Kroah-Hartman #include <linux/errno.h>
138ffdff6aSGreg Kroah-Hartman #include <linux/kernel.h>
148ffdff6aSGreg Kroah-Hartman #include <linux/sched.h>
158ffdff6aSGreg Kroah-Hartman #include <linux/fcntl.h>
168ffdff6aSGreg Kroah-Hartman #include <linux/mm.h>
178ffdff6aSGreg Kroah-Hartman #include <linux/io.h>
188ffdff6aSGreg Kroah-Hartman
19*df0e68c1SIan Abbott #include <linux/comedi.h>
20*df0e68c1SIan Abbott #include <linux/comedi/comedidev.h>
21*df0e68c1SIan Abbott #include <linux/comedi/comedilib.h>
228ffdff6aSGreg Kroah-Hartman
238ffdff6aSGreg Kroah-Hartman MODULE_AUTHOR("David Schleef <ds@schleef.org>");
248ffdff6aSGreg Kroah-Hartman MODULE_DESCRIPTION("Comedi kernel library");
258ffdff6aSGreg Kroah-Hartman MODULE_LICENSE("GPL");
268ffdff6aSGreg Kroah-Hartman
comedi_open(const char * filename)278ffdff6aSGreg Kroah-Hartman struct comedi_device *comedi_open(const char *filename)
288ffdff6aSGreg Kroah-Hartman {
298ffdff6aSGreg Kroah-Hartman struct comedi_device *dev, *retval = NULL;
308ffdff6aSGreg Kroah-Hartman unsigned int minor;
318ffdff6aSGreg Kroah-Hartman
328ffdff6aSGreg Kroah-Hartman if (strncmp(filename, "/dev/comedi", 11) != 0)
338ffdff6aSGreg Kroah-Hartman return NULL;
348ffdff6aSGreg Kroah-Hartman
358ffdff6aSGreg Kroah-Hartman if (kstrtouint(filename + 11, 0, &minor))
368ffdff6aSGreg Kroah-Hartman return NULL;
378ffdff6aSGreg Kroah-Hartman
388ffdff6aSGreg Kroah-Hartman if (minor >= COMEDI_NUM_BOARD_MINORS)
398ffdff6aSGreg Kroah-Hartman return NULL;
408ffdff6aSGreg Kroah-Hartman
418ffdff6aSGreg Kroah-Hartman dev = comedi_dev_get_from_minor(minor);
428ffdff6aSGreg Kroah-Hartman if (!dev)
438ffdff6aSGreg Kroah-Hartman return NULL;
448ffdff6aSGreg Kroah-Hartman
458ffdff6aSGreg Kroah-Hartman down_read(&dev->attach_lock);
468ffdff6aSGreg Kroah-Hartman if (dev->attached)
478ffdff6aSGreg Kroah-Hartman retval = dev;
488ffdff6aSGreg Kroah-Hartman else
498ffdff6aSGreg Kroah-Hartman retval = NULL;
508ffdff6aSGreg Kroah-Hartman up_read(&dev->attach_lock);
518ffdff6aSGreg Kroah-Hartman
528ffdff6aSGreg Kroah-Hartman if (!retval)
538ffdff6aSGreg Kroah-Hartman comedi_dev_put(dev);
548ffdff6aSGreg Kroah-Hartman
558ffdff6aSGreg Kroah-Hartman return retval;
568ffdff6aSGreg Kroah-Hartman }
578ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_open);
588ffdff6aSGreg Kroah-Hartman
comedi_close(struct comedi_device * dev)598ffdff6aSGreg Kroah-Hartman int comedi_close(struct comedi_device *dev)
608ffdff6aSGreg Kroah-Hartman {
618ffdff6aSGreg Kroah-Hartman comedi_dev_put(dev);
628ffdff6aSGreg Kroah-Hartman return 0;
638ffdff6aSGreg Kroah-Hartman }
648ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_close);
658ffdff6aSGreg Kroah-Hartman
comedi_do_insn(struct comedi_device * dev,struct comedi_insn * insn,unsigned int * data)668ffdff6aSGreg Kroah-Hartman static int comedi_do_insn(struct comedi_device *dev,
678ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn,
688ffdff6aSGreg Kroah-Hartman unsigned int *data)
698ffdff6aSGreg Kroah-Hartman {
708ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s;
718ffdff6aSGreg Kroah-Hartman int ret;
728ffdff6aSGreg Kroah-Hartman
738ffdff6aSGreg Kroah-Hartman mutex_lock(&dev->mutex);
748ffdff6aSGreg Kroah-Hartman
758ffdff6aSGreg Kroah-Hartman if (!dev->attached) {
768ffdff6aSGreg Kroah-Hartman ret = -EINVAL;
778ffdff6aSGreg Kroah-Hartman goto error;
788ffdff6aSGreg Kroah-Hartman }
798ffdff6aSGreg Kroah-Hartman
808ffdff6aSGreg Kroah-Hartman /* a subdevice instruction */
818ffdff6aSGreg Kroah-Hartman if (insn->subdev >= dev->n_subdevices) {
828ffdff6aSGreg Kroah-Hartman ret = -EINVAL;
838ffdff6aSGreg Kroah-Hartman goto error;
848ffdff6aSGreg Kroah-Hartman }
858ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[insn->subdev];
868ffdff6aSGreg Kroah-Hartman
878ffdff6aSGreg Kroah-Hartman if (s->type == COMEDI_SUBD_UNUSED) {
888ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev,
898ffdff6aSGreg Kroah-Hartman "%d not usable subdevice\n", insn->subdev);
908ffdff6aSGreg Kroah-Hartman ret = -EIO;
918ffdff6aSGreg Kroah-Hartman goto error;
928ffdff6aSGreg Kroah-Hartman }
938ffdff6aSGreg Kroah-Hartman
948ffdff6aSGreg Kroah-Hartman /* XXX check lock */
958ffdff6aSGreg Kroah-Hartman
968ffdff6aSGreg Kroah-Hartman ret = comedi_check_chanlist(s, 1, &insn->chanspec);
978ffdff6aSGreg Kroah-Hartman if (ret < 0) {
988ffdff6aSGreg Kroah-Hartman dev_err(dev->class_dev, "bad chanspec\n");
998ffdff6aSGreg Kroah-Hartman ret = -EINVAL;
1008ffdff6aSGreg Kroah-Hartman goto error;
1018ffdff6aSGreg Kroah-Hartman }
1028ffdff6aSGreg Kroah-Hartman
1038ffdff6aSGreg Kroah-Hartman if (s->busy) {
1048ffdff6aSGreg Kroah-Hartman ret = -EBUSY;
1058ffdff6aSGreg Kroah-Hartman goto error;
1068ffdff6aSGreg Kroah-Hartman }
1078ffdff6aSGreg Kroah-Hartman s->busy = dev;
1088ffdff6aSGreg Kroah-Hartman
1098ffdff6aSGreg Kroah-Hartman switch (insn->insn) {
1108ffdff6aSGreg Kroah-Hartman case INSN_BITS:
1118ffdff6aSGreg Kroah-Hartman ret = s->insn_bits(dev, s, insn, data);
1128ffdff6aSGreg Kroah-Hartman break;
1138ffdff6aSGreg Kroah-Hartman case INSN_CONFIG:
1148ffdff6aSGreg Kroah-Hartman /* XXX should check instruction length */
1158ffdff6aSGreg Kroah-Hartman ret = s->insn_config(dev, s, insn, data);
1168ffdff6aSGreg Kroah-Hartman break;
1178ffdff6aSGreg Kroah-Hartman default:
1188ffdff6aSGreg Kroah-Hartman ret = -EINVAL;
1198ffdff6aSGreg Kroah-Hartman break;
1208ffdff6aSGreg Kroah-Hartman }
1218ffdff6aSGreg Kroah-Hartman
1228ffdff6aSGreg Kroah-Hartman s->busy = NULL;
1238ffdff6aSGreg Kroah-Hartman error:
1248ffdff6aSGreg Kroah-Hartman
1258ffdff6aSGreg Kroah-Hartman mutex_unlock(&dev->mutex);
1268ffdff6aSGreg Kroah-Hartman return ret;
1278ffdff6aSGreg Kroah-Hartman }
1288ffdff6aSGreg Kroah-Hartman
comedi_dio_get_config(struct comedi_device * dev,unsigned int subdev,unsigned int chan,unsigned int * io)1298ffdff6aSGreg Kroah-Hartman int comedi_dio_get_config(struct comedi_device *dev, unsigned int subdev,
1308ffdff6aSGreg Kroah-Hartman unsigned int chan, unsigned int *io)
1318ffdff6aSGreg Kroah-Hartman {
1328ffdff6aSGreg Kroah-Hartman struct comedi_insn insn;
1338ffdff6aSGreg Kroah-Hartman unsigned int data[2];
1348ffdff6aSGreg Kroah-Hartman int ret;
1358ffdff6aSGreg Kroah-Hartman
1368ffdff6aSGreg Kroah-Hartman memset(&insn, 0, sizeof(insn));
1378ffdff6aSGreg Kroah-Hartman insn.insn = INSN_CONFIG;
1388ffdff6aSGreg Kroah-Hartman insn.n = 2;
1398ffdff6aSGreg Kroah-Hartman insn.subdev = subdev;
1408ffdff6aSGreg Kroah-Hartman insn.chanspec = CR_PACK(chan, 0, 0);
1418ffdff6aSGreg Kroah-Hartman data[0] = INSN_CONFIG_DIO_QUERY;
1428ffdff6aSGreg Kroah-Hartman data[1] = 0;
1438ffdff6aSGreg Kroah-Hartman ret = comedi_do_insn(dev, &insn, data);
1448ffdff6aSGreg Kroah-Hartman if (ret >= 0)
1458ffdff6aSGreg Kroah-Hartman *io = data[1];
1468ffdff6aSGreg Kroah-Hartman return ret;
1478ffdff6aSGreg Kroah-Hartman }
1488ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_dio_get_config);
1498ffdff6aSGreg Kroah-Hartman
comedi_dio_config(struct comedi_device * dev,unsigned int subdev,unsigned int chan,unsigned int io)1508ffdff6aSGreg Kroah-Hartman int comedi_dio_config(struct comedi_device *dev, unsigned int subdev,
1518ffdff6aSGreg Kroah-Hartman unsigned int chan, unsigned int io)
1528ffdff6aSGreg Kroah-Hartman {
1538ffdff6aSGreg Kroah-Hartman struct comedi_insn insn;
1548ffdff6aSGreg Kroah-Hartman
1558ffdff6aSGreg Kroah-Hartman memset(&insn, 0, sizeof(insn));
1568ffdff6aSGreg Kroah-Hartman insn.insn = INSN_CONFIG;
1578ffdff6aSGreg Kroah-Hartman insn.n = 1;
1588ffdff6aSGreg Kroah-Hartman insn.subdev = subdev;
1598ffdff6aSGreg Kroah-Hartman insn.chanspec = CR_PACK(chan, 0, 0);
1608ffdff6aSGreg Kroah-Hartman
1618ffdff6aSGreg Kroah-Hartman return comedi_do_insn(dev, &insn, &io);
1628ffdff6aSGreg Kroah-Hartman }
1638ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_dio_config);
1648ffdff6aSGreg Kroah-Hartman
comedi_dio_bitfield2(struct comedi_device * dev,unsigned int subdev,unsigned int mask,unsigned int * bits,unsigned int base_channel)1658ffdff6aSGreg Kroah-Hartman int comedi_dio_bitfield2(struct comedi_device *dev, unsigned int subdev,
1668ffdff6aSGreg Kroah-Hartman unsigned int mask, unsigned int *bits,
1678ffdff6aSGreg Kroah-Hartman unsigned int base_channel)
1688ffdff6aSGreg Kroah-Hartman {
1698ffdff6aSGreg Kroah-Hartman struct comedi_insn insn;
1708ffdff6aSGreg Kroah-Hartman unsigned int data[2];
1718ffdff6aSGreg Kroah-Hartman unsigned int n_chan;
1728ffdff6aSGreg Kroah-Hartman unsigned int shift;
1738ffdff6aSGreg Kroah-Hartman int ret;
1748ffdff6aSGreg Kroah-Hartman
1758ffdff6aSGreg Kroah-Hartman base_channel = CR_CHAN(base_channel);
1768ffdff6aSGreg Kroah-Hartman n_chan = comedi_get_n_channels(dev, subdev);
1778ffdff6aSGreg Kroah-Hartman if (base_channel >= n_chan)
1788ffdff6aSGreg Kroah-Hartman return -EINVAL;
1798ffdff6aSGreg Kroah-Hartman
1808ffdff6aSGreg Kroah-Hartman memset(&insn, 0, sizeof(insn));
1818ffdff6aSGreg Kroah-Hartman insn.insn = INSN_BITS;
1828ffdff6aSGreg Kroah-Hartman insn.chanspec = base_channel;
1838ffdff6aSGreg Kroah-Hartman insn.n = 2;
1848ffdff6aSGreg Kroah-Hartman insn.subdev = subdev;
1858ffdff6aSGreg Kroah-Hartman
1868ffdff6aSGreg Kroah-Hartman data[0] = mask;
1878ffdff6aSGreg Kroah-Hartman data[1] = *bits;
1888ffdff6aSGreg Kroah-Hartman
1898ffdff6aSGreg Kroah-Hartman /*
1908ffdff6aSGreg Kroah-Hartman * Most drivers ignore the base channel in insn->chanspec.
1918ffdff6aSGreg Kroah-Hartman * Fix this here if the subdevice has <= 32 channels.
1928ffdff6aSGreg Kroah-Hartman */
1938ffdff6aSGreg Kroah-Hartman if (n_chan <= 32) {
1948ffdff6aSGreg Kroah-Hartman shift = base_channel;
1958ffdff6aSGreg Kroah-Hartman if (shift) {
1968ffdff6aSGreg Kroah-Hartman insn.chanspec = 0;
1978ffdff6aSGreg Kroah-Hartman data[0] <<= shift;
1988ffdff6aSGreg Kroah-Hartman data[1] <<= shift;
1998ffdff6aSGreg Kroah-Hartman }
2008ffdff6aSGreg Kroah-Hartman } else {
2018ffdff6aSGreg Kroah-Hartman shift = 0;
2028ffdff6aSGreg Kroah-Hartman }
2038ffdff6aSGreg Kroah-Hartman
2048ffdff6aSGreg Kroah-Hartman ret = comedi_do_insn(dev, &insn, data);
2058ffdff6aSGreg Kroah-Hartman *bits = data[1] >> shift;
2068ffdff6aSGreg Kroah-Hartman return ret;
2078ffdff6aSGreg Kroah-Hartman }
2088ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_dio_bitfield2);
2098ffdff6aSGreg Kroah-Hartman
comedi_find_subdevice_by_type(struct comedi_device * dev,int type,unsigned int subd)2108ffdff6aSGreg Kroah-Hartman int comedi_find_subdevice_by_type(struct comedi_device *dev, int type,
2118ffdff6aSGreg Kroah-Hartman unsigned int subd)
2128ffdff6aSGreg Kroah-Hartman {
2138ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s;
2148ffdff6aSGreg Kroah-Hartman int ret = -ENODEV;
2158ffdff6aSGreg Kroah-Hartman
2168ffdff6aSGreg Kroah-Hartman down_read(&dev->attach_lock);
2178ffdff6aSGreg Kroah-Hartman if (dev->attached)
2188ffdff6aSGreg Kroah-Hartman for (; subd < dev->n_subdevices; subd++) {
2198ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[subd];
2208ffdff6aSGreg Kroah-Hartman if (s->type == type) {
2218ffdff6aSGreg Kroah-Hartman ret = subd;
2228ffdff6aSGreg Kroah-Hartman break;
2238ffdff6aSGreg Kroah-Hartman }
2248ffdff6aSGreg Kroah-Hartman }
2258ffdff6aSGreg Kroah-Hartman up_read(&dev->attach_lock);
2268ffdff6aSGreg Kroah-Hartman return ret;
2278ffdff6aSGreg Kroah-Hartman }
2288ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_find_subdevice_by_type);
2298ffdff6aSGreg Kroah-Hartman
comedi_get_n_channels(struct comedi_device * dev,unsigned int subdevice)2308ffdff6aSGreg Kroah-Hartman int comedi_get_n_channels(struct comedi_device *dev, unsigned int subdevice)
2318ffdff6aSGreg Kroah-Hartman {
2328ffdff6aSGreg Kroah-Hartman int n;
2338ffdff6aSGreg Kroah-Hartman
2348ffdff6aSGreg Kroah-Hartman down_read(&dev->attach_lock);
2358ffdff6aSGreg Kroah-Hartman if (!dev->attached || subdevice >= dev->n_subdevices)
2368ffdff6aSGreg Kroah-Hartman n = 0;
2378ffdff6aSGreg Kroah-Hartman else
2388ffdff6aSGreg Kroah-Hartman n = dev->subdevices[subdevice].n_chan;
2398ffdff6aSGreg Kroah-Hartman up_read(&dev->attach_lock);
2408ffdff6aSGreg Kroah-Hartman
2418ffdff6aSGreg Kroah-Hartman return n;
2428ffdff6aSGreg Kroah-Hartman }
2438ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_get_n_channels);
2448ffdff6aSGreg Kroah-Hartman
kcomedilib_module_init(void)2458ffdff6aSGreg Kroah-Hartman static int __init kcomedilib_module_init(void)
2468ffdff6aSGreg Kroah-Hartman {
2478ffdff6aSGreg Kroah-Hartman return 0;
2488ffdff6aSGreg Kroah-Hartman }
2498ffdff6aSGreg Kroah-Hartman
kcomedilib_module_exit(void)2508ffdff6aSGreg Kroah-Hartman static void __exit kcomedilib_module_exit(void)
2518ffdff6aSGreg Kroah-Hartman {
2528ffdff6aSGreg Kroah-Hartman }
2538ffdff6aSGreg Kroah-Hartman
2548ffdff6aSGreg Kroah-Hartman module_init(kcomedilib_module_init);
2558ffdff6aSGreg Kroah-Hartman module_exit(kcomedilib_module_exit);
256