Path: blob/master/drivers/comedi/kcomedilib/kcomedilib_main.c
26282 views
// SPDX-License-Identifier: GPL-2.0+1/*2* kcomedilib/kcomedilib.c3* a comedlib interface for kernel modules4*5* COMEDI - Linux Control and Measurement Device Interface6* Copyright (C) 1997-2000 David A. Schleef <[email protected]>7*/89#include <linux/module.h>1011#include <linux/errno.h>12#include <linux/kernel.h>13#include <linux/sched.h>14#include <linux/fcntl.h>15#include <linux/mm.h>16#include <linux/io.h>1718#include <linux/comedi.h>19#include <linux/comedi/comedidev.h>20#include <linux/comedi/comedilib.h>2122MODULE_AUTHOR("David Schleef <[email protected]>");23MODULE_DESCRIPTION("Comedi kernel library");24MODULE_LICENSE("GPL");2526struct comedi_device *comedi_open(const char *filename)27{28struct comedi_device *dev, *retval = NULL;29unsigned int minor;3031if (strncmp(filename, "/dev/comedi", 11) != 0)32return NULL;3334if (kstrtouint(filename + 11, 0, &minor))35return NULL;3637if (minor >= COMEDI_NUM_BOARD_MINORS)38return NULL;3940dev = comedi_dev_get_from_minor(minor);41if (!dev)42return NULL;4344down_read(&dev->attach_lock);45if (dev->attached)46retval = dev;47else48retval = NULL;49up_read(&dev->attach_lock);5051if (!retval)52comedi_dev_put(dev);5354return retval;55}56EXPORT_SYMBOL_GPL(comedi_open);5758int comedi_close(struct comedi_device *dev)59{60comedi_dev_put(dev);61return 0;62}63EXPORT_SYMBOL_GPL(comedi_close);6465static int comedi_do_insn(struct comedi_device *dev,66struct comedi_insn *insn,67unsigned int *data)68{69struct comedi_subdevice *s;70int ret;7172mutex_lock(&dev->mutex);7374if (!dev->attached) {75ret = -EINVAL;76goto error;77}7879/* a subdevice instruction */80if (insn->subdev >= dev->n_subdevices) {81ret = -EINVAL;82goto error;83}84s = &dev->subdevices[insn->subdev];8586if (s->type == COMEDI_SUBD_UNUSED) {87dev_err(dev->class_dev,88"%d not usable subdevice\n", insn->subdev);89ret = -EIO;90goto error;91}9293/* XXX check lock */9495ret = comedi_check_chanlist(s, 1, &insn->chanspec);96if (ret < 0) {97dev_err(dev->class_dev, "bad chanspec\n");98ret = -EINVAL;99goto error;100}101102if (s->busy) {103ret = -EBUSY;104goto error;105}106s->busy = dev;107108switch (insn->insn) {109case INSN_BITS:110ret = s->insn_bits(dev, s, insn, data);111break;112case INSN_CONFIG:113/* XXX should check instruction length */114ret = s->insn_config(dev, s, insn, data);115break;116default:117ret = -EINVAL;118break;119}120121s->busy = NULL;122error:123124mutex_unlock(&dev->mutex);125return ret;126}127128int comedi_dio_get_config(struct comedi_device *dev, unsigned int subdev,129unsigned int chan, unsigned int *io)130{131struct comedi_insn insn;132unsigned int data[2];133int ret;134135memset(&insn, 0, sizeof(insn));136insn.insn = INSN_CONFIG;137insn.n = 2;138insn.subdev = subdev;139insn.chanspec = CR_PACK(chan, 0, 0);140data[0] = INSN_CONFIG_DIO_QUERY;141data[1] = 0;142ret = comedi_do_insn(dev, &insn, data);143if (ret >= 0)144*io = data[1];145return ret;146}147EXPORT_SYMBOL_GPL(comedi_dio_get_config);148149int comedi_dio_config(struct comedi_device *dev, unsigned int subdev,150unsigned int chan, unsigned int io)151{152struct comedi_insn insn;153154memset(&insn, 0, sizeof(insn));155insn.insn = INSN_CONFIG;156insn.n = 1;157insn.subdev = subdev;158insn.chanspec = CR_PACK(chan, 0, 0);159160return comedi_do_insn(dev, &insn, &io);161}162EXPORT_SYMBOL_GPL(comedi_dio_config);163164int comedi_dio_bitfield2(struct comedi_device *dev, unsigned int subdev,165unsigned int mask, unsigned int *bits,166unsigned int base_channel)167{168struct comedi_insn insn;169unsigned int data[2];170unsigned int n_chan;171unsigned int shift;172int ret;173174base_channel = CR_CHAN(base_channel);175n_chan = comedi_get_n_channels(dev, subdev);176if (base_channel >= n_chan)177return -EINVAL;178179memset(&insn, 0, sizeof(insn));180insn.insn = INSN_BITS;181insn.chanspec = base_channel;182insn.n = 2;183insn.subdev = subdev;184185data[0] = mask;186data[1] = *bits;187188/*189* Most drivers ignore the base channel in insn->chanspec.190* Fix this here if the subdevice has <= 32 channels.191*/192if (n_chan <= 32) {193shift = base_channel;194if (shift) {195insn.chanspec = 0;196data[0] <<= shift;197data[1] <<= shift;198}199} else {200shift = 0;201}202203ret = comedi_do_insn(dev, &insn, data);204*bits = data[1] >> shift;205return ret;206}207EXPORT_SYMBOL_GPL(comedi_dio_bitfield2);208209int comedi_find_subdevice_by_type(struct comedi_device *dev, int type,210unsigned int subd)211{212struct comedi_subdevice *s;213int ret = -ENODEV;214215down_read(&dev->attach_lock);216if (dev->attached)217for (; subd < dev->n_subdevices; subd++) {218s = &dev->subdevices[subd];219if (s->type == type) {220ret = subd;221break;222}223}224up_read(&dev->attach_lock);225return ret;226}227EXPORT_SYMBOL_GPL(comedi_find_subdevice_by_type);228229int comedi_get_n_channels(struct comedi_device *dev, unsigned int subdevice)230{231int n;232233down_read(&dev->attach_lock);234if (!dev->attached || subdevice >= dev->n_subdevices)235n = 0;236else237n = dev->subdevices[subdevice].n_chan;238up_read(&dev->attach_lock);239240return n;241}242EXPORT_SYMBOL_GPL(comedi_get_n_channels);243244static int __init kcomedilib_module_init(void)245{246return 0;247}248249static void __exit kcomedilib_module_exit(void)250{251}252253module_init(kcomedilib_module_init);254module_exit(kcomedilib_module_exit);255256257