pcl816.c revision b147ed60775ff976f2a0724fa643f9b508bc8652
1/* 2 comedi/drivers/pcl816.c 3 4 Author: Juan Grigera <juan@grigera.com.ar> 5 based on pcl818 by Michal Dobes <dobes@tesnet.cz> and bits of pcl812 6 7 hardware driver for Advantech cards: 8 card: PCL-816, PCL814B 9 driver: pcl816 10*/ 11/* 12Driver: pcl816 13Description: Advantech PCL-816 cards, PCL-814 14Author: Juan Grigera <juan@grigera.com.ar> 15Devices: [Advantech] PCL-816 (pcl816), PCL-814B (pcl814b) 16Status: works 17Updated: Tue, 2 Apr 2002 23:15:21 -0800 18 19PCL 816 and 814B have 16 SE/DIFF ADCs, 16 DACs, 16 DI and 16 DO. 20Differences are at resolution (16 vs 12 bits). 21 22The driver support AI command mode, other subdevices not written. 23 24Analog output and digital input and output are not supported. 25 26Configuration Options: 27 [0] - IO Base 28 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7) 29 [2] - DMA (0=disable, 1, 3) 30 [3] - 0, 10=10MHz clock for 8254 31 1= 1MHz clock for 8254 32 33*/ 34 35#include "../comedidev.h" 36 37#include <linux/ioport.h> 38#include <linux/mc146818rtc.h> 39#include <linux/gfp.h> 40#include <linux/delay.h> 41#include <linux/io.h> 42#include <asm/dma.h> 43 44#include "8253.h" 45 46#define DEBUG(x) x 47 48/* boards constants */ 49/* IO space len */ 50#define PCLx1x_RANGE 16 51 52/* #define outb(x,y) printk("OUTB(%x, 200+%d)\n", x,y-0x200); outb(x,y) */ 53 54/* INTEL 8254 counters */ 55#define PCL816_CTR0 4 56#define PCL816_CTR1 5 57#define PCL816_CTR2 6 58/* R: counter read-back register W: counter control */ 59#define PCL816_CTRCTL 7 60 61/* R: A/D high byte W: A/D range control */ 62#define PCL816_RANGE 9 63/* W: clear INT request */ 64#define PCL816_CLRINT 10 65/* R: next mux scan channel W: mux scan channel & range control pointer */ 66#define PCL816_MUX 11 67/* R/W: operation control register */ 68#define PCL816_CONTROL 12 69 70/* R: return status byte W: set DMA/IRQ */ 71#define PCL816_STATUS 13 72#define PCL816_STATUS_DRDY_MASK 0x80 73 74/* R: low byte of A/D W: soft A/D trigger */ 75#define PCL816_AD_LO 8 76/* R: high byte of A/D W: A/D range control */ 77#define PCL816_AD_HI 9 78 79/* type of interrupt handler */ 80#define INT_TYPE_AI1_INT 1 81#define INT_TYPE_AI1_DMA 2 82#define INT_TYPE_AI3_INT 4 83#define INT_TYPE_AI3_DMA 5 84#ifdef unused 85#define INT_TYPE_AI1_DMA_RTC 9 86#define INT_TYPE_AI3_DMA_RTC 10 87 88/* RTC stuff... */ 89#define RTC_IRQ 8 90#define RTC_IO_EXTENT 0x10 91#endif 92 93#define MAGIC_DMA_WORD 0x5a5a 94 95static const struct comedi_lrange range_pcl816 = { 8, { 96 BIP_RANGE(10), 97 BIP_RANGE(5), 98 BIP_RANGE(2.5), 99 BIP_RANGE(1.25), 100 UNI_RANGE(10), 101 UNI_RANGE(5), 102 UNI_RANGE(2.5), 103 UNI_RANGE(1.25), 104 } 105}; 106 107struct pcl816_board { 108 109 const char *name; /* board name */ 110 int n_ranges; /* len of range list */ 111 int n_aichan; /* num of A/D chans in diferencial mode */ 112 unsigned int ai_ns_min; /* minimal allowed delay between samples (in ns) */ 113 int n_aochan; /* num of D/A chans */ 114 int n_dichan; /* num of DI chans */ 115 int n_dochan; /* num of DO chans */ 116 const struct comedi_lrange *ai_range_type; /* default A/D rangelist */ 117 const struct comedi_lrange *ao_range_type; /* default D/A rangelist */ 118 unsigned int io_range; /* len of IO space */ 119 unsigned int IRQbits; /* allowed interrupts */ 120 unsigned int DMAbits; /* allowed DMA chans */ 121 int ai_maxdata; /* maxdata for A/D */ 122 int ao_maxdata; /* maxdata for D/A */ 123 int ai_chanlist; /* allowed len of channel list A/D */ 124 int ao_chanlist; /* allowed len of channel list D/A */ 125 int i8254_osc_base; /* 1/frequency of on board oscilator in ns */ 126}; 127 128static const struct pcl816_board boardtypes[] = { 129 {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816, 130 &range_pcl816, PCLx1x_RANGE, 131 0x00fc, /* IRQ mask */ 132 0x0a, /* DMA mask */ 133 0xffff, /* 16-bit card */ 134 0xffff, /* D/A maxdata */ 135 1024, 136 1, /* ao chan list */ 137 100}, 138 {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816, 139 &range_pcl816, PCLx1x_RANGE, 140 0x00fc, 141 0x0a, 142 0x3fff, /* 14 bit card */ 143 0x3fff, 144 1024, 145 1, 146 100}, 147}; 148 149#define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl816_board)) 150#define devpriv ((struct pcl816_private *)dev->private) 151#define this_board ((const struct pcl816_board *)dev->board_ptr) 152 153static int pcl816_attach(struct comedi_device *dev, 154 struct comedi_devconfig *it); 155static int pcl816_detach(struct comedi_device *dev); 156 157#ifdef unused 158static int RTC_lock; /* RTC lock */ 159static int RTC_timer_lock; /* RTC int lock */ 160#endif 161 162static struct comedi_driver driver_pcl816 = { 163 .driver_name = "pcl816", 164 .module = THIS_MODULE, 165 .attach = pcl816_attach, 166 .detach = pcl816_detach, 167 .board_name = &boardtypes[0].name, 168 .num_names = n_boardtypes, 169 .offset = sizeof(struct pcl816_board), 170}; 171 172static int __init driver_pcl816_init_module(void) 173{ 174 return comedi_driver_register(&driver_pcl816); 175} 176 177static void __exit driver_pcl816_cleanup_module(void) 178{ 179 comedi_driver_unregister(&driver_pcl816); 180} 181 182module_init(driver_pcl816_init_module); 183module_exit(driver_pcl816_cleanup_module); 184 185struct pcl816_private { 186 187 unsigned int dma; /* used DMA, 0=don't use DMA */ 188 int dma_rtc; /* 1=RTC used with DMA, 0=no RTC alloc */ 189#ifdef unused 190 unsigned long rtc_iobase; /* RTC port region */ 191 unsigned int rtc_iosize; 192 unsigned int rtc_irq; 193#endif 194 unsigned long dmabuf[2]; /* pointers to begin of DMA buffers */ 195 unsigned int dmapages[2]; /* len of DMA buffers in PAGE_SIZEs */ 196 unsigned int hwdmaptr[2]; /* hardware address of DMA buffers */ 197 unsigned int hwdmasize[2]; /* len of DMA buffers in Bytes */ 198 unsigned int dmasamplsize; /* size in samples hwdmasize[0]/2 */ 199 unsigned int last_top_dma; /* DMA pointer in last RTC int */ 200 int next_dma_buf; /* which DMA buffer will be used next round */ 201 long dma_runs_to_end; /* how many we must permorm DMA transfer to end of record */ 202 unsigned long last_dma_run; /* how many bytes we must transfer on last DMA page */ 203 204 unsigned int ai_scans; /* len of scanlist */ 205 unsigned char ai_neverending; /* if=1, then we do neverending record (you must use cancel()) */ 206 int irq_free; /* 1=have allocated IRQ */ 207 int irq_blocked; /* 1=IRQ now uses any subdev */ 208#ifdef unused 209 int rtc_irq_blocked; /* 1=we now do AI with DMA&RTC */ 210#endif 211 int irq_was_now_closed; /* when IRQ finish, there's stored int816_mode for last interrupt */ 212 int int816_mode; /* who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */ 213 struct comedi_subdevice *last_int_sub; /* ptr to subdevice which now finish */ 214 int ai_act_scan; /* how many scans we finished */ 215 unsigned int ai_act_chanlist[16]; /* MUX setting for actual AI operations */ 216 unsigned int ai_act_chanlist_len; /* how long is actual MUX list */ 217 unsigned int ai_act_chanlist_pos; /* actual position in MUX list */ 218 unsigned int ai_n_chan; /* how many channels per scan */ 219 unsigned int ai_poll_ptr; /* how many sampes transfer poll */ 220 struct comedi_subdevice *sub_ai; /* ptr to AI subdevice */ 221#ifdef unused 222 struct timer_list rtc_irq_timer; /* timer for RTC sanity check */ 223 unsigned long rtc_freq; /* RTC int freq */ 224#endif 225}; 226 227/* 228============================================================================== 229*/ 230static int check_channel_list(struct comedi_device *dev, 231 struct comedi_subdevice *s, 232 unsigned int *chanlist, unsigned int chanlen); 233static void setup_channel_list(struct comedi_device *dev, 234 struct comedi_subdevice *s, 235 unsigned int *chanlist, unsigned int seglen); 236static int pcl816_ai_cancel(struct comedi_device *dev, 237 struct comedi_subdevice *s); 238static void start_pacer(struct comedi_device *dev, int mode, 239 unsigned int divisor1, unsigned int divisor2); 240#ifdef unused 241static int set_rtc_irq_bit(unsigned char bit); 242#endif 243 244static int pcl816_ai_cmdtest(struct comedi_device *dev, 245 struct comedi_subdevice *s, 246 struct comedi_cmd *cmd); 247static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s); 248 249/* 250============================================================================== 251 ANALOG INPUT MODE0, 816 cards, slow version 252*/ 253static int pcl816_ai_insn_read(struct comedi_device *dev, 254 struct comedi_subdevice *s, 255 struct comedi_insn *insn, unsigned int *data) 256{ 257 int n; 258 int timeout; 259 260 DPRINTK("mode 0 analog input\n"); 261 /* software trigger, DMA and INT off */ 262 outb(0, dev->iobase + PCL816_CONTROL); 263 /* clear INT (conversion end) flag */ 264 outb(0, dev->iobase + PCL816_CLRINT); 265 266 /* Set the input channel */ 267 outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX); 268 /* select gain */ 269 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE); 270 271 for (n = 0; n < insn->n; n++) { 272 273 outb(0, dev->iobase + PCL816_AD_LO); /* start conversion */ 274 275 timeout = 100; 276 while (timeout--) { 277 if (!(inb(dev->iobase + PCL816_STATUS) & 278 PCL816_STATUS_DRDY_MASK)) { 279 /* return read value */ 280 data[n] = 281 ((inb(dev->iobase + 282 PCL816_AD_HI) << 8) | 283 (inb(dev->iobase + PCL816_AD_LO))); 284 /* clear INT (conversion end) flag */ 285 outb(0, dev->iobase + PCL816_CLRINT); 286 break; 287 } 288 udelay(1); 289 } 290 /* Return timeout error */ 291 if (!timeout) { 292 comedi_error(dev, "A/D insn timeout\n"); 293 data[0] = 0; 294 /* clear INT (conversion end) flag */ 295 outb(0, dev->iobase + PCL816_CLRINT); 296 return -EIO; 297 } 298 299 } 300 return n; 301} 302 303/* 304============================================================================== 305 analog input interrupt mode 1 & 3, 818 cards 306 one sample per interrupt version 307*/ 308static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d) 309{ 310 struct comedi_device *dev = d; 311 struct comedi_subdevice *s = dev->subdevices + 0; 312 int low, hi; 313 int timeout = 50; /* wait max 50us */ 314 315 while (timeout--) { 316 if (!(inb(dev->iobase + PCL816_STATUS) & 317 PCL816_STATUS_DRDY_MASK)) 318 break; 319 udelay(1); 320 } 321 if (!timeout) { /* timeout, bail error */ 322 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */ 323 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!"); 324 pcl816_ai_cancel(dev, s); 325 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR; 326 comedi_event(dev, s); 327 return IRQ_HANDLED; 328 329 } 330 331 /* get the sample */ 332 low = inb(dev->iobase + PCL816_AD_LO); 333 hi = inb(dev->iobase + PCL816_AD_HI); 334 335 comedi_buf_put(s->async, (hi << 8) | low); 336 337 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */ 338 339 if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len) 340 devpriv->ai_act_chanlist_pos = 0; 341 342 s->async->cur_chan++; 343 if (s->async->cur_chan >= devpriv->ai_n_chan) { 344 s->async->cur_chan = 0; 345 devpriv->ai_act_scan++; 346 } 347 348 if (!devpriv->ai_neverending) 349 /* all data sampled */ 350 if (devpriv->ai_act_scan >= devpriv->ai_scans) { 351 /* all data sampled */ 352 pcl816_ai_cancel(dev, s); 353 s->async->events |= COMEDI_CB_EOA; 354 } 355 comedi_event(dev, s); 356 return IRQ_HANDLED; 357} 358 359/* 360============================================================================== 361 analog input dma mode 1 & 3, 816 cards 362*/ 363static void transfer_from_dma_buf(struct comedi_device *dev, 364 struct comedi_subdevice *s, short *ptr, 365 unsigned int bufptr, unsigned int len) 366{ 367 int i; 368 369 s->async->events = 0; 370 371 for (i = 0; i < len; i++) { 372 373 comedi_buf_put(s->async, ptr[bufptr++]); 374 375 if (++devpriv->ai_act_chanlist_pos >= 376 devpriv->ai_act_chanlist_len) { 377 devpriv->ai_act_chanlist_pos = 0; 378 } 379 380 s->async->cur_chan++; 381 if (s->async->cur_chan >= devpriv->ai_n_chan) { 382 s->async->cur_chan = 0; 383 devpriv->ai_act_scan++; 384 } 385 386 if (!devpriv->ai_neverending) 387 /* all data sampled */ 388 if (devpriv->ai_act_scan >= devpriv->ai_scans) { 389 pcl816_ai_cancel(dev, s); 390 s->async->events |= COMEDI_CB_EOA; 391 s->async->events |= COMEDI_CB_BLOCK; 392 break; 393 } 394 } 395 396 comedi_event(dev, s); 397} 398 399static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d) 400{ 401 struct comedi_device *dev = d; 402 struct comedi_subdevice *s = dev->subdevices + 0; 403 int len, bufptr, this_dma_buf; 404 unsigned long dma_flags; 405 short *ptr; 406 407 disable_dma(devpriv->dma); 408 this_dma_buf = devpriv->next_dma_buf; 409 410 /* switch dma bufs */ 411 if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) { 412 413 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf; 414 set_dma_mode(devpriv->dma, DMA_MODE_READ); 415 dma_flags = claim_dma_lock(); 416/* clear_dma_ff (devpriv->dma); */ 417 set_dma_addr(devpriv->dma, 418 devpriv->hwdmaptr[devpriv->next_dma_buf]); 419 if (devpriv->dma_runs_to_end) { 420 set_dma_count(devpriv->dma, 421 devpriv->hwdmasize[devpriv-> 422 next_dma_buf]); 423 } else { 424 set_dma_count(devpriv->dma, devpriv->last_dma_run); 425 } 426 release_dma_lock(dma_flags); 427 enable_dma(devpriv->dma); 428 } 429 430 devpriv->dma_runs_to_end--; 431 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */ 432 433 ptr = (short *)devpriv->dmabuf[this_dma_buf]; 434 435 len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr; 436 bufptr = devpriv->ai_poll_ptr; 437 devpriv->ai_poll_ptr = 0; 438 439 transfer_from_dma_buf(dev, s, ptr, bufptr, len); 440 return IRQ_HANDLED; 441} 442 443/* 444============================================================================== 445 INT procedure 446*/ 447static irqreturn_t interrupt_pcl816(int irq, void *d) 448{ 449 struct comedi_device *dev = d; 450 DPRINTK("<I>"); 451 452 if (!dev->attached) { 453 comedi_error(dev, "premature interrupt"); 454 return IRQ_HANDLED; 455 } 456 457 switch (devpriv->int816_mode) { 458 case INT_TYPE_AI1_DMA: 459 case INT_TYPE_AI3_DMA: 460 return interrupt_pcl816_ai_mode13_dma(irq, d); 461 case INT_TYPE_AI1_INT: 462 case INT_TYPE_AI3_INT: 463 return interrupt_pcl816_ai_mode13_int(irq, d); 464 } 465 466 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */ 467 if ((!dev->irq) | (!devpriv->irq_free) | (!devpriv->irq_blocked) | 468 (!devpriv->int816_mode)) { 469 if (devpriv->irq_was_now_closed) { 470 devpriv->irq_was_now_closed = 0; 471 /* comedi_error(dev,"last IRQ.."); */ 472 return IRQ_HANDLED; 473 } 474 comedi_error(dev, "bad IRQ!"); 475 return IRQ_NONE; 476 } 477 comedi_error(dev, "IRQ from unknown source!"); 478 return IRQ_NONE; 479} 480 481/* 482============================================================================== 483 COMMAND MODE 484*/ 485static void pcl816_cmdtest_out(int e, struct comedi_cmd *cmd) 486{ 487 printk(KERN_INFO "pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e, 488 cmd->start_src, cmd->scan_begin_src, cmd->convert_src); 489 printk(KERN_INFO "pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e, 490 cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg); 491 printk(KERN_INFO "pcl816 e=%d stopsrc=%x scanend=%x\n", e, 492 cmd->stop_src, cmd->scan_end_src); 493 printk(KERN_INFO "pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n", 494 e, cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len); 495} 496 497/* 498============================================================================== 499*/ 500static int pcl816_ai_cmdtest(struct comedi_device *dev, 501 struct comedi_subdevice *s, struct comedi_cmd *cmd) 502{ 503 int err = 0; 504 int tmp, divisor1 = 0, divisor2 = 0; 505 506 DEBUG(printk(KERN_INFO "pcl816 pcl812_ai_cmdtest\n"); 507 pcl816_cmdtest_out(-1, cmd); 508 ); 509 510 /* step 1: make sure trigger sources are trivially valid */ 511 tmp = cmd->start_src; 512 cmd->start_src &= TRIG_NOW; 513 if (!cmd->start_src || tmp != cmd->start_src) 514 err++; 515 516 tmp = cmd->scan_begin_src; 517 cmd->scan_begin_src &= TRIG_FOLLOW; 518 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src) 519 err++; 520 521 tmp = cmd->convert_src; 522 cmd->convert_src &= TRIG_EXT | TRIG_TIMER; 523 if (!cmd->convert_src || tmp != cmd->convert_src) 524 err++; 525 526 tmp = cmd->scan_end_src; 527 cmd->scan_end_src &= TRIG_COUNT; 528 if (!cmd->scan_end_src || tmp != cmd->scan_end_src) 529 err++; 530 531 tmp = cmd->stop_src; 532 cmd->stop_src &= TRIG_COUNT | TRIG_NONE; 533 if (!cmd->stop_src || tmp != cmd->stop_src) 534 err++; 535 536 if (err) 537 return 1; 538 539 540 /* 541 * step 2: make sure trigger sources 542 * are unique and mutually compatible 543 */ 544 545 if (cmd->start_src != TRIG_NOW) { 546 cmd->start_src = TRIG_NOW; 547 err++; 548 } 549 550 if (cmd->scan_begin_src != TRIG_FOLLOW) { 551 cmd->scan_begin_src = TRIG_FOLLOW; 552 err++; 553 } 554 555 if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) { 556 cmd->convert_src = TRIG_TIMER; 557 err++; 558 } 559 560 if (cmd->scan_end_src != TRIG_COUNT) { 561 cmd->scan_end_src = TRIG_COUNT; 562 err++; 563 } 564 565 if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT) 566 err++; 567 568 if (err) 569 return 2; 570 571 572 /* step 3: make sure arguments are trivially compatible */ 573 if (cmd->start_arg != 0) { 574 cmd->start_arg = 0; 575 err++; 576 } 577 578 if (cmd->scan_begin_arg != 0) { 579 cmd->scan_begin_arg = 0; 580 err++; 581 } 582 if (cmd->convert_src == TRIG_TIMER) { 583 if (cmd->convert_arg < this_board->ai_ns_min) { 584 cmd->convert_arg = this_board->ai_ns_min; 585 err++; 586 } 587 } else { /* TRIG_EXT */ 588 if (cmd->convert_arg != 0) { 589 cmd->convert_arg = 0; 590 err++; 591 } 592 } 593 594 if (cmd->scan_end_arg != cmd->chanlist_len) { 595 cmd->scan_end_arg = cmd->chanlist_len; 596 err++; 597 } 598 if (cmd->stop_src == TRIG_COUNT) { 599 if (!cmd->stop_arg) { 600 cmd->stop_arg = 1; 601 err++; 602 } 603 } else { /* TRIG_NONE */ 604 if (cmd->stop_arg != 0) { 605 cmd->stop_arg = 0; 606 err++; 607 } 608 } 609 610 if (err) 611 return 3; 612 613 614 /* step 4: fix up any arguments */ 615 if (cmd->convert_src == TRIG_TIMER) { 616 tmp = cmd->convert_arg; 617 i8253_cascade_ns_to_timer(this_board->i8254_osc_base, 618 &divisor1, &divisor2, 619 &cmd->convert_arg, 620 cmd->flags & TRIG_ROUND_MASK); 621 if (cmd->convert_arg < this_board->ai_ns_min) 622 cmd->convert_arg = this_board->ai_ns_min; 623 if (tmp != cmd->convert_arg) 624 err++; 625 } 626 627 if (err) 628 return 4; 629 630 631 /* step 5: complain about special chanlist considerations */ 632 633 if (cmd->chanlist) { 634 if (!check_channel_list(dev, s, cmd->chanlist, 635 cmd->chanlist_len)) 636 return 5; /* incorrect channels list */ 637 } 638 639 return 0; 640} 641 642static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) 643{ 644 unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq; 645 struct comedi_cmd *cmd = &s->async->cmd; 646 unsigned int seglen; 647 648 if (cmd->start_src != TRIG_NOW) 649 return -EINVAL; 650 if (cmd->scan_begin_src != TRIG_FOLLOW) 651 return -EINVAL; 652 if (cmd->scan_end_src != TRIG_COUNT) 653 return -EINVAL; 654 if (cmd->scan_end_arg != cmd->chanlist_len) 655 return -EINVAL; 656/* if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; */ 657 if (devpriv->irq_blocked) 658 return -EBUSY; 659 660 if (cmd->convert_src == TRIG_TIMER) { 661 if (cmd->convert_arg < this_board->ai_ns_min) 662 cmd->convert_arg = this_board->ai_ns_min; 663 664 i8253_cascade_ns_to_timer(this_board->i8254_osc_base, &divisor1, 665 &divisor2, &cmd->convert_arg, 666 cmd->flags & TRIG_ROUND_MASK); 667 668 /* PCL816 crash if any divisor is set to 1 */ 669 if (divisor1 == 1) { 670 divisor1 = 2; 671 divisor2 /= 2; 672 } 673 if (divisor2 == 1) { 674 divisor2 = 2; 675 divisor1 /= 2; 676 } 677 } 678 679 start_pacer(dev, -1, 0, 0); /* stop pacer */ 680 681 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len); 682 if (seglen < 1) 683 return -EINVAL; 684 setup_channel_list(dev, s, cmd->chanlist, seglen); 685 udelay(1); 686 687 devpriv->ai_n_chan = cmd->chanlist_len; 688 devpriv->ai_act_scan = 0; 689 s->async->cur_chan = 0; 690 devpriv->irq_blocked = 1; 691 devpriv->ai_poll_ptr = 0; 692 devpriv->irq_was_now_closed = 0; 693 694 if (cmd->stop_src == TRIG_COUNT) { 695 devpriv->ai_scans = cmd->stop_arg; 696 devpriv->ai_neverending = 0; 697 } else { 698 devpriv->ai_scans = 0; 699 devpriv->ai_neverending = 1; 700 } 701 702 /* don't we want wake up every scan? */ 703 if ((cmd->flags & TRIG_WAKE_EOS)) { 704 printk(KERN_INFO 705 "pl816: You wankt WAKE_EOS but I dont want handle it"); 706 /* devpriv->ai_eos=1; */ 707 /* if (devpriv->ai_n_chan==1) */ 708 /* devpriv->dma=0; // DMA is useless for this situation */ 709 } 710 711 if (devpriv->dma) { 712 bytes = devpriv->hwdmasize[0]; 713 if (!devpriv->ai_neverending) { 714 /* how many */ 715 bytes = s->async->cmd.chanlist_len * 716 s->async->cmd.chanlist_len * 717 sizeof(short); 718 719 /* how many DMA pages we must fill */ 720 devpriv->dma_runs_to_end = bytes / 721 devpriv->hwdmasize[0]; 722 723 /* on last dma transfer must be moved */ 724 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0]; 725 devpriv->dma_runs_to_end--; 726 if (devpriv->dma_runs_to_end >= 0) 727 bytes = devpriv->hwdmasize[0]; 728 } else 729 devpriv->dma_runs_to_end = -1; 730 731 devpriv->next_dma_buf = 0; 732 set_dma_mode(devpriv->dma, DMA_MODE_READ); 733 dma_flags = claim_dma_lock(); 734 clear_dma_ff(devpriv->dma); 735 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]); 736 set_dma_count(devpriv->dma, bytes); 737 release_dma_lock(dma_flags); 738 enable_dma(devpriv->dma); 739 } 740 741 start_pacer(dev, 1, divisor1, divisor2); 742 dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7); 743 744 switch (cmd->convert_src) { 745 case TRIG_TIMER: 746 devpriv->int816_mode = INT_TYPE_AI1_DMA; 747 748 /* Pacer+IRQ+DMA */ 749 outb(0x32, dev->iobase + PCL816_CONTROL); 750 751 /* write irq and DMA to card */ 752 outb(dmairq, dev->iobase + PCL816_STATUS); 753 break; 754 755 default: 756 devpriv->int816_mode = INT_TYPE_AI3_DMA; 757 758 /* Ext trig+IRQ+DMA */ 759 outb(0x34, dev->iobase + PCL816_CONTROL); 760 761 /* write irq to card */ 762 outb(dmairq, dev->iobase + PCL816_STATUS); 763 break; 764 } 765 766 DPRINTK("pcl816 END: pcl812_ai_cmd()\n"); 767 return 0; 768} 769 770static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s) 771{ 772 unsigned long flags; 773 unsigned int top1, top2, i; 774 775 if (!devpriv->dma) 776 return 0; /* poll is valid only for DMA transfer */ 777 778 spin_lock_irqsave(&dev->spinlock, flags); 779 780 for (i = 0; i < 20; i++) { 781 top1 = get_dma_residue(devpriv->dma); /* where is now DMA */ 782 top2 = get_dma_residue(devpriv->dma); 783 if (top1 == top2) 784 break; 785 } 786 if (top1 != top2) { 787 spin_unlock_irqrestore(&dev->spinlock, flags); 788 return 0; 789 } 790 791 /* where is now DMA in buffer */ 792 top1 = devpriv->hwdmasize[0] - top1; 793 top1 >>= 1; /* sample position */ 794 top2 = top1 - devpriv->ai_poll_ptr; 795 if (top2 < 1) { /* no new samples */ 796 spin_unlock_irqrestore(&dev->spinlock, flags); 797 return 0; 798 } 799 800 transfer_from_dma_buf(dev, s, 801 (short *)devpriv->dmabuf[devpriv->next_dma_buf], 802 devpriv->ai_poll_ptr, top2); 803 804 devpriv->ai_poll_ptr = top1; /* new buffer position */ 805 spin_unlock_irqrestore(&dev->spinlock, flags); 806 807 return s->async->buf_write_count - s->async->buf_read_count; 808} 809 810/* 811============================================================================== 812 cancel any mode 1-4 AI 813*/ 814static int pcl816_ai_cancel(struct comedi_device *dev, 815 struct comedi_subdevice *s) 816{ 817/* DEBUG(printk("pcl816_ai_cancel()\n");) */ 818 819 if (devpriv->irq_blocked > 0) { 820 switch (devpriv->int816_mode) { 821#ifdef unused 822 case INT_TYPE_AI1_DMA_RTC: 823 case INT_TYPE_AI3_DMA_RTC: 824 set_rtc_irq_bit(0); /* stop RTC */ 825 del_timer(&devpriv->rtc_irq_timer); 826#endif 827 case INT_TYPE_AI1_DMA: 828 case INT_TYPE_AI3_DMA: 829 disable_dma(devpriv->dma); 830 case INT_TYPE_AI1_INT: 831 case INT_TYPE_AI3_INT: 832 outb(inb(dev->iobase + PCL816_CONTROL) & 0x73, 833 dev->iobase + PCL816_CONTROL); /* Stop A/D */ 834 udelay(1); 835 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */ 836 837 /* Stop pacer */ 838 outb(0xb0, dev->iobase + PCL816_CTRCTL); 839 outb(0x70, dev->iobase + PCL816_CTRCTL); 840 outb(0, dev->iobase + PCL816_AD_LO); 841 inb(dev->iobase + PCL816_AD_LO); 842 inb(dev->iobase + PCL816_AD_HI); 843 844 /* clear INT request */ 845 outb(0, dev->iobase + PCL816_CLRINT); 846 847 /* Stop A/D */ 848 outb(0, dev->iobase + PCL816_CONTROL); 849 devpriv->irq_blocked = 0; 850 devpriv->irq_was_now_closed = devpriv->int816_mode; 851 devpriv->int816_mode = 0; 852 devpriv->last_int_sub = s; 853/* s->busy = 0; */ 854 break; 855 } 856 } 857 858 DEBUG(printk("comedi: pcl816_ai_cancel() successful\n");) 859 return 0; 860} 861 862/* 863============================================================================== 864 chech for PCL816 865*/ 866static int pcl816_check(unsigned long iobase) 867{ 868 outb(0x00, iobase + PCL816_MUX); 869 udelay(1); 870 if (inb(iobase + PCL816_MUX) != 0x00) 871 return 1; /* there isn't card */ 872 outb(0x55, iobase + PCL816_MUX); 873 udelay(1); 874 if (inb(iobase + PCL816_MUX) != 0x55) 875 return 1; /* there isn't card */ 876 outb(0x00, iobase + PCL816_MUX); 877 udelay(1); 878 outb(0x18, iobase + PCL816_CONTROL); 879 udelay(1); 880 if (inb(iobase + PCL816_CONTROL) != 0x18) 881 return 1; /* there isn't card */ 882 return 0; /* ok, card exist */ 883} 884 885/* 886============================================================================== 887 reset whole PCL-816 cards 888*/ 889static void pcl816_reset(struct comedi_device *dev) 890{ 891/* outb (0, dev->iobase + PCL818_DA_LO); DAC=0V */ 892/* outb (0, dev->iobase + PCL818_DA_HI); */ 893/* udelay (1); */ 894/* outb (0, dev->iobase + PCL818_DO_HI); DO=$0000 */ 895/* outb (0, dev->iobase + PCL818_DO_LO); */ 896/* udelay (1); */ 897 outb(0, dev->iobase + PCL816_CONTROL); 898 outb(0, dev->iobase + PCL816_MUX); 899 outb(0, dev->iobase + PCL816_CLRINT); 900 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */ 901 outb(0x70, dev->iobase + PCL816_CTRCTL); 902 outb(0x30, dev->iobase + PCL816_CTRCTL); 903 outb(0, dev->iobase + PCL816_RANGE); 904} 905 906/* 907============================================================================== 908 Start/stop pacer onboard pacer 909*/ 910static void 911start_pacer(struct comedi_device *dev, int mode, unsigned int divisor1, 912 unsigned int divisor2) 913{ 914 outb(0x32, dev->iobase + PCL816_CTRCTL); 915 outb(0xff, dev->iobase + PCL816_CTR0); 916 outb(0x00, dev->iobase + PCL816_CTR0); 917 udelay(1); 918 919 /* set counter 2 as mode 3 */ 920 outb(0xb4, dev->iobase + PCL816_CTRCTL); 921 /* set counter 1 as mode 3 */ 922 outb(0x74, dev->iobase + PCL816_CTRCTL); 923 udelay(1); 924 925 if (mode == 1) { 926 DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1, 927 divisor2); 928 outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2); 929 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2); 930 outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1); 931 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1); 932 } 933 934 /* clear pending interrupts (just in case) */ 935/* outb(0, dev->iobase + PCL816_CLRINT); */ 936} 937 938/* 939============================================================================== 940 Check if channel list from user is builded correctly 941 If it's ok, then return non-zero length of repeated segment of channel list 942*/ 943static int 944check_channel_list(struct comedi_device *dev, 945 struct comedi_subdevice *s, unsigned int *chanlist, 946 unsigned int chanlen) 947{ 948 unsigned int chansegment[16]; 949 unsigned int i, nowmustbechan, seglen, segpos; 950 951 /* correct channel and range number check itself comedi/range.c */ 952 if (chanlen < 1) { 953 comedi_error(dev, "range/channel list is empty!"); 954 return 0; 955 } 956 957 if (chanlen > 1) { 958 /* first channel is every time ok */ 959 chansegment[0] = chanlist[0]; 960 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) { 961 /* build part of chanlist */ 962 DEBUG(printk(KERN_INFO "%d. %d %d\n", i, 963 CR_CHAN(chanlist[i]), 964 CR_RANGE(chanlist[i]));) 965 966 /* we detect loop, this must by finish */ 967 if (chanlist[0] == chanlist[i]) 968 break; 969 nowmustbechan = 970 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen; 971 if (nowmustbechan != CR_CHAN(chanlist[i])) { 972 /* channel list isn't continuous :-( */ 973 printk(KERN_WARNING 974 "comedi%d: pcl816: channel list must " 975 "be continuous! chanlist[%i]=%d but " 976 "must be %d or %d!\n", dev->minor, 977 i, CR_CHAN(chanlist[i]), nowmustbechan, 978 CR_CHAN(chanlist[0])); 979 return 0; 980 } 981 /* well, this is next correct channel in list */ 982 chansegment[i] = chanlist[i]; 983 } 984 985 /* check whole chanlist */ 986 for (i = 0, segpos = 0; i < chanlen; i++) { 987 DEBUG(printk("%d %d=%d %d\n", 988 CR_CHAN(chansegment[i % seglen]), 989 CR_RANGE(chansegment[i % seglen]), 990 CR_CHAN(chanlist[i]), 991 CR_RANGE(chanlist[i]));) 992 if (chanlist[i] != chansegment[i % seglen]) { 993 printk(KERN_WARNING 994 "comedi%d: pcl816: bad channel or range" 995 " number! chanlist[%i]=%d,%d,%d and not" 996 " %d,%d,%d!\n", dev->minor, i, 997 CR_CHAN(chansegment[i]), 998 CR_RANGE(chansegment[i]), 999 CR_AREF(chansegment[i]), 1000 CR_CHAN(chanlist[i % seglen]), 1001 CR_RANGE(chanlist[i % seglen]), 1002 CR_AREF(chansegment[i % seglen])); 1003 return 0; /* chan/gain list is strange */ 1004 } 1005 } 1006 } else { 1007 seglen = 1; 1008 } 1009 1010 return seglen; /* we can serve this with MUX logic */ 1011} 1012 1013/* 1014============================================================================== 1015 Program scan/gain logic with channel list. 1016*/ 1017static void 1018setup_channel_list(struct comedi_device *dev, 1019 struct comedi_subdevice *s, unsigned int *chanlist, 1020 unsigned int seglen) 1021{ 1022 unsigned int i; 1023 1024 devpriv->ai_act_chanlist_len = seglen; 1025 devpriv->ai_act_chanlist_pos = 0; 1026 1027 for (i = 0; i < seglen; i++) { /* store range list to card */ 1028 devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]); 1029 outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX); 1030 /* select gain */ 1031 outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE); 1032 } 1033 1034 udelay(1); 1035 /* select channel interval to scan */ 1036 outb(devpriv->ai_act_chanlist[0] | 1037 (devpriv->ai_act_chanlist[seglen - 1] << 4), 1038 dev->iobase + PCL816_MUX); 1039} 1040 1041#ifdef unused 1042/* 1043============================================================================== 1044 Enable(1)/disable(0) periodic interrupts from RTC 1045*/ 1046static int set_rtc_irq_bit(unsigned char bit) 1047{ 1048 unsigned char val; 1049 unsigned long flags; 1050 1051 if (bit == 1) { 1052 RTC_timer_lock++; 1053 if (RTC_timer_lock > 1) 1054 return 0; 1055 } else { 1056 RTC_timer_lock--; 1057 if (RTC_timer_lock < 0) 1058 RTC_timer_lock = 0; 1059 if (RTC_timer_lock > 0) 1060 return 0; 1061 } 1062 1063 save_flags(flags); 1064 cli(); 1065 val = CMOS_READ(RTC_CONTROL); 1066 if (bit) 1067 val |= RTC_PIE; 1068 else 1069 val &= ~RTC_PIE; 1070 1071 CMOS_WRITE(val, RTC_CONTROL); 1072 CMOS_READ(RTC_INTR_FLAGS); 1073 restore_flags(flags); 1074 return 0; 1075} 1076#endif 1077 1078/* 1079============================================================================== 1080 Free any resources that we have claimed 1081*/ 1082static void free_resources(struct comedi_device *dev) 1083{ 1084 /* printk("free_resource()\n"); */ 1085 if (dev->private) { 1086 pcl816_ai_cancel(dev, devpriv->sub_ai); 1087 pcl816_reset(dev); 1088 if (devpriv->dma) 1089 free_dma(devpriv->dma); 1090 if (devpriv->dmabuf[0]) 1091 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]); 1092 if (devpriv->dmabuf[1]) 1093 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]); 1094#ifdef unused 1095 if (devpriv->rtc_irq) 1096 free_irq(devpriv->rtc_irq, dev); 1097 if ((devpriv->dma_rtc) && (RTC_lock == 1)) { 1098 if (devpriv->rtc_iobase) 1099 release_region(devpriv->rtc_iobase, 1100 devpriv->rtc_iosize); 1101 } 1102#endif 1103 } 1104 1105 if (dev->irq) 1106 free_irq(dev->irq, dev); 1107 if (dev->iobase) 1108 release_region(dev->iobase, this_board->io_range); 1109 /* printk("free_resource() end\n"); */ 1110} 1111 1112/* 1113============================================================================== 1114 1115 Initialization 1116 1117*/ 1118static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it) 1119{ 1120 int ret; 1121 unsigned long iobase; 1122 unsigned int irq, dma; 1123 unsigned long pages; 1124 /* int i; */ 1125 struct comedi_subdevice *s; 1126 1127 /* claim our I/O space */ 1128 iobase = it->options[0]; 1129 printk("comedi%d: pcl816: board=%s, ioport=0x%03lx", dev->minor, 1130 this_board->name, iobase); 1131 1132 if (!request_region(iobase, this_board->io_range, "pcl816")) { 1133 printk("I/O port conflict\n"); 1134 return -EIO; 1135 } 1136 1137 dev->iobase = iobase; 1138 1139 if (pcl816_check(iobase)) { 1140 printk(KERN_ERR ", I cann't detect board. FAIL!\n"); 1141 return -EIO; 1142 } 1143 1144 ret = alloc_private(dev, sizeof(struct pcl816_private)); 1145 if (ret < 0) 1146 return ret; /* Can't alloc mem */ 1147 1148 /* set up some name stuff */ 1149 dev->board_name = this_board->name; 1150 1151 /* grab our IRQ */ 1152 irq = 0; 1153 if (this_board->IRQbits != 0) { /* board support IRQ */ 1154 irq = it->options[1]; 1155 if (irq) { /* we want to use IRQ */ 1156 if (((1 << irq) & this_board->IRQbits) == 0) { 1157 printk 1158 (", IRQ %u is out of allowed range, " 1159 "DISABLING IT", irq); 1160 irq = 0; /* Bad IRQ */ 1161 } else { 1162 if (request_irq 1163 (irq, interrupt_pcl816, 0, "pcl816", dev)) { 1164 printk 1165 (", unable to allocate IRQ %u, " 1166 "DISABLING IT", irq); 1167 irq = 0; /* Can't use IRQ */ 1168 } else { 1169 printk(KERN_INFO ", irq=%u", irq); 1170 } 1171 } 1172 } 1173 } 1174 1175 dev->irq = irq; 1176 if (irq) /* 1=we have allocated irq */ 1177 devpriv->irq_free = 1; 1178 else 1179 devpriv->irq_free = 0; 1180 1181 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */ 1182 devpriv->int816_mode = 0; /* mode of irq */ 1183 1184#ifdef unused 1185 /* grab RTC for DMA operations */ 1186 devpriv->dma_rtc = 0; 1187 if (it->options[2] > 0) { /* we want to use DMA */ 1188 if (RTC_lock == 0) { 1189 if (!request_region(RTC_PORT(0), RTC_IO_EXTENT, 1190 "pcl816 (RTC)")) 1191 goto no_rtc; 1192 } 1193 devpriv->rtc_iobase = RTC_PORT(0); 1194 devpriv->rtc_iosize = RTC_IO_EXTENT; 1195 RTC_lock++; 1196#ifdef UNTESTED_CODE 1197 if (!request_irq(RTC_IRQ, interrupt_pcl816_ai_mode13_dma_rtc, 0, 1198 "pcl816 DMA (RTC)", dev)) { 1199 devpriv->dma_rtc = 1; 1200 devpriv->rtc_irq = RTC_IRQ; 1201 printk(", dma_irq=%u", devpriv->rtc_irq); 1202 } else { 1203 RTC_lock--; 1204 if (RTC_lock == 0) { 1205 if (devpriv->rtc_iobase) 1206 release_region(devpriv->rtc_iobase, 1207 devpriv->rtc_iosize); 1208 } 1209 devpriv->rtc_iobase = 0; 1210 devpriv->rtc_iosize = 0; 1211 } 1212#else 1213 printk("pcl816: RTC code missing"); 1214#endif 1215 1216 } 1217 1218no_rtc: 1219#endif 1220 /* grab our DMA */ 1221 dma = 0; 1222 devpriv->dma = dma; 1223 if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0)) 1224 goto no_dma; /* if we haven't IRQ, we can't use DMA */ 1225 1226 if (this_board->DMAbits != 0) { /* board support DMA */ 1227 dma = it->options[2]; 1228 if (dma < 1) 1229 goto no_dma; /* DMA disabled */ 1230 1231 if (((1 << dma) & this_board->DMAbits) == 0) { 1232 printk(", DMA is out of allowed range, FAIL!\n"); 1233 return -EINVAL; /* Bad DMA */ 1234 } 1235 ret = request_dma(dma, "pcl816"); 1236 if (ret) { 1237 printk(KERN_ERR 1238 ", unable to allocate DMA %u, FAIL!\n", dma); 1239 return -EBUSY; /* DMA isn't free */ 1240 } 1241 1242 devpriv->dma = dma; 1243 printk(KERN_INFO ", dma=%u", dma); 1244 pages = 2; /* we need 16KB */ 1245 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages); 1246 1247 if (!devpriv->dmabuf[0]) { 1248 printk(", unable to allocate DMA buffer, FAIL!\n"); 1249 /* 1250 * maybe experiment with try_to_free_pages() 1251 * will help .... 1252 */ 1253 return -EBUSY; /* no buffer :-( */ 1254 } 1255 devpriv->dmapages[0] = pages; 1256 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]); 1257 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE; 1258 /* printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */ 1259 1260 if (devpriv->dma_rtc == 0) { /* we must do duble buff :-( */ 1261 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages); 1262 if (!devpriv->dmabuf[1]) { 1263 printk(KERN_ERR 1264 ", unable to allocate DMA buffer, " 1265 "FAIL!\n"); 1266 return -EBUSY; 1267 } 1268 devpriv->dmapages[1] = pages; 1269 devpriv->hwdmaptr[1] = 1270 virt_to_bus((void *)devpriv->dmabuf[1]); 1271 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE; 1272 } 1273 } 1274 1275no_dma: 1276 1277/* if (this_board->n_aochan > 0) 1278 subdevs[1] = COMEDI_SUBD_AO; 1279 if (this_board->n_dichan > 0) 1280 subdevs[2] = COMEDI_SUBD_DI; 1281 if (this_board->n_dochan > 0) 1282 subdevs[3] = COMEDI_SUBD_DO; 1283*/ 1284 1285 ret = alloc_subdevices(dev, 1); 1286 if (ret < 0) 1287 return ret; 1288 1289 s = dev->subdevices + 0; 1290 if (this_board->n_aichan > 0) { 1291 s->type = COMEDI_SUBD_AI; 1292 devpriv->sub_ai = s; 1293 dev->read_subdev = s; 1294 s->subdev_flags = SDF_READABLE | SDF_CMD_READ; 1295 s->n_chan = this_board->n_aichan; 1296 s->subdev_flags |= SDF_DIFF; 1297 /* printk (", %dchans DIFF DAC - %d", s->n_chan, i); */ 1298 s->maxdata = this_board->ai_maxdata; 1299 s->len_chanlist = this_board->ai_chanlist; 1300 s->range_table = this_board->ai_range_type; 1301 s->cancel = pcl816_ai_cancel; 1302 s->do_cmdtest = pcl816_ai_cmdtest; 1303 s->do_cmd = pcl816_ai_cmd; 1304 s->poll = pcl816_ai_poll; 1305 s->insn_read = pcl816_ai_insn_read; 1306 } else { 1307 s->type = COMEDI_SUBD_UNUSED; 1308 } 1309 1310#if 0 1311case COMEDI_SUBD_AO: 1312 s->subdev_flags = SDF_WRITABLE | SDF_GROUND; 1313 s->n_chan = this_board->n_aochan; 1314 s->maxdata = this_board->ao_maxdata; 1315 s->len_chanlist = this_board->ao_chanlist; 1316 s->range_table = this_board->ao_range_type; 1317 break; 1318 1319case COMEDI_SUBD_DI: 1320 s->subdev_flags = SDF_READABLE; 1321 s->n_chan = this_board->n_dichan; 1322 s->maxdata = 1; 1323 s->len_chanlist = this_board->n_dichan; 1324 s->range_table = &range_digital; 1325 break; 1326 1327case COMEDI_SUBD_DO: 1328 s->subdev_flags = SDF_WRITABLE; 1329 s->n_chan = this_board->n_dochan; 1330 s->maxdata = 1; 1331 s->len_chanlist = this_board->n_dochan; 1332 s->range_table = &range_digital; 1333 break; 1334#endif 1335 1336 pcl816_reset(dev); 1337 1338 printk("\n"); 1339 1340 return 0; 1341} 1342 1343/* 1344============================================================================== 1345 Removes device 1346 */ 1347static int pcl816_detach(struct comedi_device *dev) 1348{ 1349 DEBUG(printk(KERN_INFO "comedi%d: pcl816: remove\n", dev->minor);) 1350 free_resources(dev); 1351#ifdef unused 1352 if (devpriv->dma_rtc) 1353 RTC_lock--; 1354#endif 1355 return 0; 1356} 1357 1358MODULE_AUTHOR("Comedi http://www.comedi.org"); 1359MODULE_DESCRIPTION("Comedi low-level driver"); 1360MODULE_LICENSE("GPL"); 1361