kcomedilib_main.c revision ea6d0d4cab4f4f2d6a88f3bce4707fe92696fd3f
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 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 22*/ 23 24#define __NO_VERSION__ 25#include <linux/module.h> 26 27#include <linux/errno.h> 28#include <linux/kernel.h> 29#include <linux/sched.h> 30#include <linux/fcntl.h> 31#include <linux/delay.h> 32#include <linux/ioport.h> 33#include <linux/mm.h> 34#include <linux/slab.h> 35#include <asm/io.h> 36 37#include "../comedi.h" 38#include "../comedilib.h" 39#include "../comedidev.h" 40 41MODULE_AUTHOR("David Schleef <ds@schleef.org>"); 42MODULE_DESCRIPTION("Comedi kernel library"); 43MODULE_LICENSE("GPL"); 44 45void *comedi_open(const char *filename) 46{ 47 struct comedi_device_file_info *dev_file_info; 48 struct comedi_device *dev; 49 unsigned int minor; 50 51 if (strncmp(filename, "/dev/comedi", 11) != 0) 52 return NULL; 53 54 minor = simple_strtoul(filename + 11, NULL, 0); 55 56 if (minor >= COMEDI_NUM_BOARD_MINORS) 57 return NULL; 58 59 dev_file_info = comedi_get_device_file_info(minor); 60 if (dev_file_info == NULL) 61 return NULL; 62 dev = dev_file_info->device; 63 64 if (dev == NULL || !dev->attached) 65 return NULL; 66 67 if (!try_module_get(dev->driver->module)) 68 return NULL; 69 70 return (void *) dev; 71} 72 73void *comedi_open_old(unsigned int minor) 74{ 75 struct comedi_device_file_info *dev_file_info; 76 struct comedi_device *dev; 77 78 if (minor >= COMEDI_NUM_MINORS) 79 return NULL; 80 81 dev_file_info = comedi_get_device_file_info(minor); 82 if (dev_file_info == NULL) 83 return NULL; 84 dev = dev_file_info->device; 85 86 if (dev == NULL || !dev->attached) 87 return NULL; 88 89 return (void *) dev; 90} 91 92int comedi_close(void *d) 93{ 94 struct comedi_device *dev = (struct comedi_device *) d; 95 96 module_put(dev->driver->module); 97 98 return 0; 99} 100 101int comedi_loglevel(int newlevel) 102{ 103 return 0; 104} 105 106void comedi_perror(const char *message) 107{ 108 rt_printk("%s: unknown error\n", message); 109} 110 111char *comedi_strerror(int err) 112{ 113 return "unknown error"; 114} 115 116int comedi_fileno(void *d) 117{ 118 struct comedi_device *dev = (struct comedi_device *) d; 119 120 /* return something random */ 121 return dev->minor; 122} 123 124int comedi_command(void *d, struct comedi_cmd *cmd) 125{ 126 struct comedi_device *dev = (struct comedi_device *) d; 127 struct comedi_subdevice *s; 128 struct comedi_async *async; 129 unsigned runflags; 130 131 if (cmd->subdev >= dev->n_subdevices) 132 return -ENODEV; 133 134 s = dev->subdevices + cmd->subdev; 135 if (s->type == COMEDI_SUBD_UNUSED) 136 return -EIO; 137 138 async = s->async; 139 if (async == NULL) 140 return -ENODEV; 141 142 if (s->busy) 143 return -EBUSY; 144 s->busy = d; 145 146 if (async->cb_mask & COMEDI_CB_EOS) 147 cmd->flags |= TRIG_WAKE_EOS; 148 149 async->cmd = *cmd; 150 151 runflags = SRF_RUNNING; 152 153#ifdef CONFIG_COMEDI_RT 154 if (comedi_switch_to_rt(dev) == 0) 155 runflags |= SRF_RT; 156#endif 157 comedi_set_subdevice_runflags(s, ~0, runflags); 158 159 comedi_reset_async_buf(async); 160 161 return s->do_cmd(dev, s); 162} 163 164int comedi_command_test(void *d, struct comedi_cmd *cmd) 165{ 166 struct comedi_device *dev = (struct comedi_device *) d; 167 struct comedi_subdevice *s; 168 169 if (cmd->subdev >= dev->n_subdevices) 170 return -ENODEV; 171 172 s = dev->subdevices + cmd->subdev; 173 if (s->type == COMEDI_SUBD_UNUSED) 174 return -EIO; 175 176 if (s->async == NULL) 177 return -ENODEV; 178 179 return s->do_cmdtest(dev, s, cmd); 180} 181 182/* 183 * COMEDI_INSN 184 * perform an instruction 185 */ 186int comedi_do_insn(void *d, comedi_insn *insn) 187{ 188 struct comedi_device *dev = (struct comedi_device *) d; 189 struct comedi_subdevice *s; 190 int ret = 0; 191 192 if (insn->insn & INSN_MASK_SPECIAL) { 193 switch (insn->insn) { 194 case INSN_GTOD: 195 { 196 struct timeval tv; 197 198 do_gettimeofday(&tv); 199 insn->data[0] = tv.tv_sec; 200 insn->data[1] = tv.tv_usec; 201 ret = 2; 202 203 break; 204 } 205 case INSN_WAIT: 206 /* XXX isn't the value supposed to be nanosecs? */ 207 if (insn->n != 1 || insn->data[0] >= 100) { 208 ret = -EINVAL; 209 break; 210 } 211 comedi_udelay(insn->data[0]); 212 ret = 1; 213 break; 214 case INSN_INTTRIG: 215 if (insn->n != 1) { 216 ret = -EINVAL; 217 break; 218 } 219 if (insn->subdev >= dev->n_subdevices) { 220 rt_printk("%d not usable subdevice\n", 221 insn->subdev); 222 ret = -EINVAL; 223 break; 224 } 225 s = dev->subdevices + insn->subdev; 226 if (!s->async) { 227 rt_printk("no async\n"); 228 ret = -EINVAL; 229 break; 230 } 231 if (!s->async->inttrig) { 232 rt_printk("no inttrig\n"); 233 ret = -EAGAIN; 234 break; 235 } 236 ret = s->async->inttrig(dev, s, insn->data[0]); 237 if (ret >= 0) 238 ret = 1; 239 break; 240 default: 241 ret = -EINVAL; 242 } 243 } else { 244 /* a subdevice instruction */ 245 if (insn->subdev >= dev->n_subdevices) { 246 ret = -EINVAL; 247 goto error; 248 } 249 s = dev->subdevices + insn->subdev; 250 251 if (s->type == COMEDI_SUBD_UNUSED) { 252 rt_printk("%d not useable subdevice\n", insn->subdev); 253 ret = -EIO; 254 goto error; 255 } 256 257 /* XXX check lock */ 258 259 ret = check_chanlist(s, 1, &insn->chanspec); 260 if (ret < 0) { 261 rt_printk("bad chanspec\n"); 262 ret = -EINVAL; 263 goto error; 264 } 265 266 if (s->busy) { 267 ret = -EBUSY; 268 goto error; 269 } 270 s->busy = d; 271 272 switch (insn->insn) { 273 case INSN_READ: 274 ret = s->insn_read(dev, s, insn, insn->data); 275 break; 276 case INSN_WRITE: 277 ret = s->insn_write(dev, s, insn, insn->data); 278 break; 279 case INSN_BITS: 280 ret = s->insn_bits(dev, s, insn, insn->data); 281 break; 282 case INSN_CONFIG: 283 /* XXX should check instruction length */ 284 ret = s->insn_config(dev, s, insn, insn->data); 285 break; 286 default: 287 ret = -EINVAL; 288 break; 289 } 290 291 s->busy = NULL; 292 } 293 if (ret < 0) 294 goto error; 295#if 0 296 /* XXX do we want this? -- abbotti #if'ed it out for now. */ 297 if (ret != insn->n) { 298 rt_printk("BUG: result of insn != insn.n\n"); 299 ret = -EINVAL; 300 goto error; 301 } 302#endif 303 error: 304 305 return ret; 306} 307 308/* 309 COMEDI_LOCK 310 lock subdevice 311 312 arg: 313 subdevice number 314 315 reads: 316 none 317 318 writes: 319 none 320 321 necessary locking: 322 - ioctl/rt lock (this type) 323 - lock while subdevice busy 324 - lock while subdevice being programmed 325 326*/ 327int comedi_lock(void *d, unsigned int subdevice) 328{ 329 struct comedi_device *dev = (struct comedi_device *) d; 330 struct comedi_subdevice *s; 331 unsigned long flags; 332 int ret = 0; 333 334 if (subdevice >= dev->n_subdevices) 335 return -EINVAL; 336 337 s = dev->subdevices + subdevice; 338 339 comedi_spin_lock_irqsave(&s->spin_lock, flags); 340 341 if (s->busy) { 342 ret = -EBUSY; 343 } else { 344 if (s->lock) { 345 ret = -EBUSY; 346 } else { 347 s->lock = d; 348 } 349 } 350 351 comedi_spin_unlock_irqrestore(&s->spin_lock, flags); 352 353 return ret; 354} 355 356/* 357 COMEDI_UNLOCK 358 unlock subdevice 359 360 arg: 361 subdevice number 362 363 reads: 364 none 365 366 writes: 367 none 368 369*/ 370int comedi_unlock(void *d, unsigned int subdevice) 371{ 372 struct comedi_device *dev = (struct comedi_device *) d; 373 struct comedi_subdevice *s; 374 unsigned long flags; 375 struct comedi_async *async; 376 int ret; 377 378 if (subdevice >= dev->n_subdevices) 379 return -EINVAL; 380 381 s = dev->subdevices + subdevice; 382 383 async = s->async; 384 385 comedi_spin_lock_irqsave(&s->spin_lock, flags); 386 387 if (s->busy) { 388 ret = -EBUSY; 389 } else if (s->lock && s->lock != (void *)d) { 390 ret = -EACCES; 391 } else { 392 s->lock = NULL; 393 394 if (async) { 395 async->cb_mask = 0; 396 async->cb_func = NULL; 397 async->cb_arg = NULL; 398 } 399 400 ret = 0; 401 } 402 403 comedi_spin_unlock_irqrestore(&s->spin_lock, flags); 404 405 return ret; 406} 407 408/* 409 COMEDI_CANCEL 410 cancel acquisition ioctl 411 412 arg: 413 subdevice number 414 415 reads: 416 nothing 417 418 writes: 419 nothing 420 421*/ 422int comedi_cancel(void *d, unsigned int subdevice) 423{ 424 struct comedi_device *dev = (struct comedi_device *) d; 425 struct comedi_subdevice *s; 426 int ret = 0; 427 428 if (subdevice >= dev->n_subdevices) 429 return -EINVAL; 430 431 s = dev->subdevices + subdevice; 432 433 if (s->lock && s->lock != d) 434 return -EACCES; 435 436#if 0 437 if (!s->busy) 438 return 0; 439 440 if (s->busy != d) 441 return -EBUSY; 442#endif 443 444 if (!s->cancel || !s->async) 445 return -EINVAL; 446 447 ret = s->cancel(dev, s); 448 449 if (ret) 450 return ret; 451 452#ifdef CONFIG_COMEDI_RT 453 if (comedi_get_subdevice_runflags(s) & SRF_RT) 454 comedi_switch_to_non_rt(dev); 455 456#endif 457 comedi_set_subdevice_runflags(s, SRF_RUNNING | SRF_RT, 0); 458 s->async->inttrig = NULL; 459 s->busy = NULL; 460 461 return 0; 462} 463 464/* 465 registration of callback functions 466 */ 467int comedi_register_callback(void *d, unsigned int subdevice, 468 unsigned int mask, int (*cb) (unsigned int, void *), void *arg) 469{ 470 struct comedi_device *dev = (struct comedi_device *) d; 471 struct comedi_subdevice *s; 472 struct comedi_async *async; 473 474 if (subdevice >= dev->n_subdevices) 475 return -EINVAL; 476 477 s = dev->subdevices + subdevice; 478 479 async = s->async; 480 if (s->type == COMEDI_SUBD_UNUSED || !async) 481 return -EIO; 482 483 /* are we locked? (ioctl lock) */ 484 if (s->lock && s->lock != d) 485 return -EACCES; 486 487 /* are we busy? */ 488 if (s->busy) 489 return -EBUSY; 490 491 if (!mask) { 492 async->cb_mask = 0; 493 async->cb_func = NULL; 494 async->cb_arg = NULL; 495 } else { 496 async->cb_mask = mask; 497 async->cb_func = cb; 498 async->cb_arg = arg; 499 } 500 501 return 0; 502} 503 504int comedi_poll(void *d, unsigned int subdevice) 505{ 506 struct comedi_device *dev = (struct comedi_device *) d; 507 struct comedi_subdevice *s = dev->subdevices; 508 struct comedi_async *async; 509 510 if (subdevice >= dev->n_subdevices) 511 return -EINVAL; 512 513 s = dev->subdevices + subdevice; 514 515 async = s->async; 516 if (s->type == COMEDI_SUBD_UNUSED || !async) 517 return -EIO; 518 519 /* are we locked? (ioctl lock) */ 520 if (s->lock && s->lock != d) 521 return -EACCES; 522 523 /* are we running? XXX wrong? */ 524 if (!s->busy) 525 return -EIO; 526 527 return s->poll(dev, s); 528} 529 530/* WARNING: not portable */ 531int comedi_map(void *d, unsigned int subdevice, void *ptr) 532{ 533 struct comedi_device *dev = (struct comedi_device *) d; 534 struct comedi_subdevice *s; 535 536 if (subdevice >= dev->n_subdevices) 537 return -EINVAL; 538 539 s = dev->subdevices + subdevice; 540 541 if (!s->async) 542 return -EINVAL; 543 544 if (ptr) 545 *((void **)ptr) = s->async->prealloc_buf; 546 547 /* XXX no reference counting */ 548 549 return 0; 550} 551 552/* WARNING: not portable */ 553int comedi_unmap(void *d, unsigned int subdevice) 554{ 555 struct comedi_device *dev = (struct comedi_device *) d; 556 struct comedi_subdevice *s; 557 558 if (subdevice >= dev->n_subdevices) 559 return -EINVAL; 560 561 s = dev->subdevices + subdevice; 562 563 if (!s->async) 564 return -EINVAL; 565 566 /* XXX no reference counting */ 567 568 return 0; 569} 570