xref: /openbmc/linux/drivers/comedi/kcomedilib/kcomedilib_main.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
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