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