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