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