kcomedilib_main.c revision b79a7a2089294cba254f6972e4e224fea643b84a
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 45comedi_t *comedi_open(const char *filename) 46{ 47 struct comedi_device_file_info *dev_file_info; 48 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 (comedi_t *) dev; 71} 72 73comedi_t *comedi_open_old(unsigned int minor) 74{ 75 struct comedi_device_file_info *dev_file_info; 76 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 (comedi_t *) dev; 90} 91 92int comedi_close(comedi_t * d) 93{ 94 comedi_device *dev = (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(comedi_t * d) 117{ 118 comedi_device *dev = (comedi_device *) d; 119 120 /* return something random */ 121 return dev->minor; 122} 123 124int comedi_command(comedi_t * d, comedi_cmd * cmd) 125{ 126 comedi_device *dev = (comedi_device *) d; 127 comedi_subdevice *s; 128 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(comedi_t * d, comedi_cmd * cmd) 165{ 166 comedi_device *dev = (comedi_device *) d; 167 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(comedi_t * d, comedi_insn * insn) 187{ 188 comedi_device *dev = (comedi_device *) d; 189 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 if ((ret = check_chanlist(s, 1, &insn->chanspec)) < 0) { 260 rt_printk("bad chanspec\n"); 261 ret = -EINVAL; 262 goto error; 263 } 264 265 if (s->busy) { 266 ret = -EBUSY; 267 goto error; 268 } 269 s->busy = d; 270 271 switch (insn->insn) { 272 case INSN_READ: 273 ret = s->insn_read(dev, s, insn, insn->data); 274 break; 275 case INSN_WRITE: 276 ret = s->insn_write(dev, s, insn, insn->data); 277 break; 278 case INSN_BITS: 279 ret = s->insn_bits(dev, s, insn, insn->data); 280 break; 281 case INSN_CONFIG: 282 /* XXX should check instruction length */ 283 ret = s->insn_config(dev, s, insn, insn->data); 284 break; 285 default: 286 ret = -EINVAL; 287 break; 288 } 289 290 s->busy = NULL; 291 } 292 if (ret < 0) 293 goto error; 294#if 0 295 /* XXX do we want this? -- abbotti #if'ed it out for now. */ 296 if (ret != insn->n) { 297 rt_printk("BUG: result of insn != insn.n\n"); 298 ret = -EINVAL; 299 goto error; 300 } 301#endif 302 error: 303 304 return ret; 305} 306 307/* 308 COMEDI_LOCK 309 lock subdevice 310 311 arg: 312 subdevice number 313 314 reads: 315 none 316 317 writes: 318 none 319 320 necessary locking: 321 - ioctl/rt lock (this type) 322 - lock while subdevice busy 323 - lock while subdevice being programmed 324 325*/ 326int comedi_lock(comedi_t * d, unsigned int subdevice) 327{ 328 comedi_device *dev = (comedi_device *) d; 329 comedi_subdevice *s; 330 unsigned long flags; 331 int ret = 0; 332 333 if (subdevice >= dev->n_subdevices) { 334 return -EINVAL; 335 } 336 s = dev->subdevices + subdevice; 337 338 comedi_spin_lock_irqsave(&s->spin_lock, flags); 339 340 if (s->busy) { 341 ret = -EBUSY; 342 } else { 343 if (s->lock) { 344 ret = -EBUSY; 345 } else { 346 s->lock = d; 347 } 348 } 349 350 comedi_spin_unlock_irqrestore(&s->spin_lock, flags); 351 352 return ret; 353} 354 355/* 356 COMEDI_UNLOCK 357 unlock subdevice 358 359 arg: 360 subdevice number 361 362 reads: 363 none 364 365 writes: 366 none 367 368*/ 369int comedi_unlock(comedi_t * d, unsigned int subdevice) 370{ 371 comedi_device *dev = (comedi_device *) d; 372 comedi_subdevice *s; 373 unsigned long flags; 374 comedi_async *async; 375 int ret; 376 377 if (subdevice >= dev->n_subdevices) { 378 return -EINVAL; 379 } 380 s = dev->subdevices + subdevice; 381 382 async = s->async; 383 384 comedi_spin_lock_irqsave(&s->spin_lock, flags); 385 386 if (s->busy) { 387 ret = -EBUSY; 388 } else if (s->lock && s->lock != (void *)d) { 389 ret = -EACCES; 390 } else { 391 s->lock = NULL; 392 393 if (async) { 394 async->cb_mask = 0; 395 async->cb_func = NULL; 396 async->cb_arg = NULL; 397 } 398 399 ret = 0; 400 } 401 402 comedi_spin_unlock_irqrestore(&s->spin_lock, flags); 403 404 return ret; 405} 406 407/* 408 COMEDI_CANCEL 409 cancel acquisition ioctl 410 411 arg: 412 subdevice number 413 414 reads: 415 nothing 416 417 writes: 418 nothing 419 420*/ 421int comedi_cancel(comedi_t * d, unsigned int subdevice) 422{ 423 comedi_device *dev = (comedi_device *) d; 424 comedi_subdevice *s; 425 int ret = 0; 426 427 if (subdevice >= dev->n_subdevices) { 428 return -EINVAL; 429 } 430 s = dev->subdevices + subdevice; 431 432 if (s->lock && s->lock != d) 433 return -EACCES; 434 435#if 0 436 if (!s->busy) 437 return 0; 438 439 if (s->busy != d) 440 return -EBUSY; 441#endif 442 443 if (!s->cancel || !s->async) 444 return -EINVAL; 445 446 if ((ret = s->cancel(dev, s))) 447 return ret; 448 449#ifdef CONFIG_COMEDI_RT 450 if (comedi_get_subdevice_runflags(s) & SRF_RT) { 451 comedi_switch_to_non_rt(dev); 452 } 453#endif 454 comedi_set_subdevice_runflags(s, SRF_RUNNING | SRF_RT, 0); 455 s->async->inttrig = NULL; 456 s->busy = NULL; 457 458 return 0; 459} 460 461/* 462 registration of callback functions 463 */ 464int comedi_register_callback(comedi_t * d, unsigned int subdevice, 465 unsigned int mask, int (*cb) (unsigned int, void *), void *arg) 466{ 467 comedi_device *dev = (comedi_device *) d; 468 comedi_subdevice *s; 469 comedi_async *async; 470 471 if (subdevice >= dev->n_subdevices) { 472 return -EINVAL; 473 } 474 s = dev->subdevices + subdevice; 475 476 async = s->async; 477 if (s->type == COMEDI_SUBD_UNUSED || !async) 478 return -EIO; 479 480 /* are we locked? (ioctl lock) */ 481 if (s->lock && s->lock != d) 482 return -EACCES; 483 484 /* are we busy? */ 485 if (s->busy) 486 return -EBUSY; 487 488 if (!mask) { 489 async->cb_mask = 0; 490 async->cb_func = NULL; 491 async->cb_arg = NULL; 492 } else { 493 async->cb_mask = mask; 494 async->cb_func = cb; 495 async->cb_arg = arg; 496 } 497 498 return 0; 499} 500 501int comedi_poll(comedi_t * d, unsigned int subdevice) 502{ 503 comedi_device *dev = (comedi_device *) d; 504 comedi_subdevice *s = dev->subdevices; 505 comedi_async *async; 506 507 if (subdevice >= dev->n_subdevices) { 508 return -EINVAL; 509 } 510 s = dev->subdevices + subdevice; 511 512 async = s->async; 513 if (s->type == COMEDI_SUBD_UNUSED || !async) 514 return -EIO; 515 516 /* are we locked? (ioctl lock) */ 517 if (s->lock && s->lock != d) 518 return -EACCES; 519 520 /* are we running? XXX wrong? */ 521 if (!s->busy) 522 return -EIO; 523 524 return s->poll(dev, s); 525} 526 527/* WARNING: not portable */ 528int comedi_map(comedi_t * d, unsigned int subdevice, void *ptr) 529{ 530 comedi_device *dev = (comedi_device *) d; 531 comedi_subdevice *s; 532 533 if (subdevice >= dev->n_subdevices) { 534 return -EINVAL; 535 } 536 s = dev->subdevices + subdevice; 537 538 if (!s->async) 539 return -EINVAL; 540 541 if (ptr) { 542 *((void **)ptr) = s->async->prealloc_buf; 543 } 544 545 /* XXX no reference counting */ 546 547 return 0; 548} 549 550/* WARNING: not portable */ 551int comedi_unmap(comedi_t * d, unsigned int subdevice) 552{ 553 comedi_device *dev = (comedi_device *) d; 554 comedi_subdevice *s; 555 556 if (subdevice >= dev->n_subdevices) { 557 return -EINVAL; 558 } 559 s = dev->subdevices + subdevice; 560 561 if (!s->async) 562 return -EINVAL; 563 564 /* XXX no reference counting */ 565 566 return 0; 567} 568