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