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