das16m1.c revision 5f74ea14c07fee91d3bdbaad88bff6264c6200e6
1/* 2 comedi/drivers/das16m1.c 3 CIO-DAS16/M1 driver 4 Author: Frank Mori Hess, based on code from the das16 5 driver. 6 Copyright (C) 2001 Frank Mori Hess <fmhess@users.sourceforge.net> 7 8 COMEDI - Linux Control and Measurement Device Interface 9 Copyright (C) 2000 David A. Schleef <ds@schleef.org> 10 11 This program is free software; you can redistribute it and/or modify 12 it under the terms of the GNU General Public License as published by 13 the Free Software Foundation; either version 2 of the License, or 14 (at your option) any later version. 15 16 This program is distributed in the hope that it will be useful, 17 but WITHOUT ANY WARRANTY; without even the implied warranty of 18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 GNU General Public License for more details. 20 21 You should have received a copy of the GNU General Public License 22 along with this program; if not, write to the Free Software 23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 24 25************************************************************************ 26*/ 27/* 28Driver: das16m1 29Description: CIO-DAS16/M1 30Author: Frank Mori Hess <fmhess@users.sourceforge.net> 31Devices: [Measurement Computing] CIO-DAS16/M1 (cio-das16/m1) 32Status: works 33 34This driver supports a single board - the CIO-DAS16/M1. 35As far as I know, there are no other boards that have 36the same register layout. Even the CIO-DAS16/M1/16 is 37significantly different. 38 39I was _barely_ able to reach the full 1 MHz capability 40of this board, using a hard real-time interrupt 41(set the TRIG_RT flag in your struct comedi_cmd and use 42rtlinux or RTAI). The board can't do dma, so the bottleneck is 43pulling the data across the ISA bus. I timed the interrupt 44handler, and it took my computer ~470 microseconds to pull 512 45samples from the board. So at 1 Mhz sampling rate, 46expect your CPU to be spending almost all of its 47time in the interrupt handler. 48 49This board has some unusual restrictions for its channel/gain list. If the 50list has 2 or more channels in it, then two conditions must be satisfied: 51(1) - even/odd channels must appear at even/odd indices in the list 52(2) - the list must have an even number of entries. 53 54Options: 55 [0] - base io address 56 [1] - irq (optional, but you probably want it) 57 58irq can be omitted, although the cmd interface will not work without it. 59*/ 60 61#include <linux/ioport.h> 62#include "../comedidev.h" 63 64#include "8255.h" 65#include "8253.h" 66#include "comedi_fc.h" 67 68#define DAS16M1_SIZE 16 69#define DAS16M1_SIZE2 8 70 71#define DAS16M1_XTAL 100 /* 10 MHz master clock */ 72 73#define FIFO_SIZE 1024 /* 1024 sample fifo */ 74 75/* 76 CIO-DAS16_M1.pdf 77 78 "cio-das16/m1" 79 80 0 a/d bits 0-3, mux start 12 bit 81 1 a/d bits 4-11 unused 82 2 status control 83 3 di 4 bit do 4 bit 84 4 unused clear interrupt 85 5 interrupt, pacer 86 6 channel/gain queue address 87 7 channel/gain queue data 88 89ab 8254 89 cdef 8254 90 400 8255 91 404-407 8254 92 93*/ 94 95#define DAS16M1_AI 0 /* 16-bit wide register */ 96#define AI_CHAN(x) ((x) & 0xf) 97#define DAS16M1_CS 2 98#define EXT_TRIG_BIT 0x1 99#define OVRUN 0x20 100#define IRQDATA 0x80 101#define DAS16M1_DIO 3 102#define DAS16M1_CLEAR_INTR 4 103#define DAS16M1_INTR_CONTROL 5 104#define EXT_PACER 0x2 105#define INT_PACER 0x3 106#define PACER_MASK 0x3 107#define INTE 0x80 108#define DAS16M1_QUEUE_ADDR 6 109#define DAS16M1_QUEUE_DATA 7 110#define Q_CHAN(x) ((x) & 0x7) 111#define Q_RANGE(x) (((x) & 0xf) << 4) 112#define UNIPOLAR 0x40 113#define DAS16M1_8254_FIRST 0x8 114#define DAS16M1_8254_FIRST_CNTRL 0xb 115#define TOTAL_CLEAR 0x30 116#define DAS16M1_8254_SECOND 0xc 117#define DAS16M1_82C55 0x400 118#define DAS16M1_8254_THIRD 0x404 119 120static const struct comedi_lrange range_das16m1 = { 9, 121 { 122 BIP_RANGE(5), 123 BIP_RANGE(2.5), 124 BIP_RANGE(1.25), 125 BIP_RANGE(0.625), 126 UNI_RANGE(10), 127 UNI_RANGE(5), 128 UNI_RANGE(2.5), 129 UNI_RANGE(1.25), 130 BIP_RANGE(10), 131 } 132}; 133 134static int das16m1_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s, 135 struct comedi_insn *insn, unsigned int *data); 136static int das16m1_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s, 137 struct comedi_insn *insn, unsigned int *data); 138static int das16m1_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 139 struct comedi_insn *insn, unsigned int *data); 140 141static int das16m1_cmd_test(struct comedi_device *dev, struct comedi_subdevice *s, 142 struct comedi_cmd *cmd); 143static int das16m1_cmd_exec(struct comedi_device *dev, struct comedi_subdevice *s); 144static int das16m1_cancel(struct comedi_device *dev, struct comedi_subdevice *s); 145 146static int das16m1_poll(struct comedi_device *dev, struct comedi_subdevice *s); 147static irqreturn_t das16m1_interrupt(int irq, void *d); 148static void das16m1_handler(struct comedi_device *dev, unsigned int status); 149 150static unsigned int das16m1_set_pacer(struct comedi_device *dev, unsigned int ns, 151 int round_flag); 152 153static int das16m1_irq_bits(unsigned int irq); 154 155struct das16m1_board { 156 const char *name; 157 unsigned int ai_speed; 158}; 159 160static const struct das16m1_board das16m1_boards[] = { 161 { 162 .name = "cio-das16/m1", /* CIO-DAS16_M1.pdf */ 163 .ai_speed = 1000, /* 1MHz max speed */ 164 }, 165}; 166 167static int das16m1_attach(struct comedi_device *dev, struct comedi_devconfig *it); 168static int das16m1_detach(struct comedi_device *dev); 169static struct comedi_driver driver_das16m1 = { 170 .driver_name = "das16m1", 171 .module = THIS_MODULE, 172 .attach = das16m1_attach, 173 .detach = das16m1_detach, 174 .board_name = &das16m1_boards[0].name, 175 .num_names = ARRAY_SIZE(das16m1_boards), 176 .offset = sizeof(das16m1_boards[0]), 177}; 178 179struct das16m1_private_struct { 180 unsigned int control_state; 181 volatile unsigned int adc_count; /* number of samples completed */ 182 /* initial value in lower half of hardware conversion counter, 183 * needed to keep track of whether new count has been loaded into 184 * counter yet (loaded by first sample conversion) */ 185 u16 initial_hw_count; 186 short ai_buffer[FIFO_SIZE]; 187 unsigned int do_bits; /* saves status of digital output bits */ 188 unsigned int divisor1; /* divides master clock to obtain conversion speed */ 189 unsigned int divisor2; /* divides master clock to obtain conversion speed */ 190}; 191#define devpriv ((struct das16m1_private_struct *)(dev->private)) 192#define thisboard ((const struct das16m1_board *)(dev->board_ptr)) 193 194COMEDI_INITCLEANUP(driver_das16m1); 195 196static inline short munge_sample(short data) 197{ 198 return (data >> 4) & 0xfff; 199} 200 201static int das16m1_cmd_test(struct comedi_device *dev, struct comedi_subdevice *s, 202 struct comedi_cmd *cmd) 203{ 204 unsigned int err = 0, tmp, i; 205 206 /* make sure triggers are valid */ 207 tmp = cmd->start_src; 208 cmd->start_src &= TRIG_NOW | TRIG_EXT; 209 if (!cmd->start_src || tmp != cmd->start_src) 210 err++; 211 212 tmp = cmd->scan_begin_src; 213 cmd->scan_begin_src &= TRIG_FOLLOW; 214 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src) 215 err++; 216 217 tmp = cmd->convert_src; 218 cmd->convert_src &= TRIG_TIMER | TRIG_EXT; 219 if (!cmd->convert_src || tmp != cmd->convert_src) 220 err++; 221 222 tmp = cmd->scan_end_src; 223 cmd->scan_end_src &= TRIG_COUNT; 224 if (!cmd->scan_end_src || tmp != cmd->scan_end_src) 225 err++; 226 227 tmp = cmd->stop_src; 228 cmd->stop_src &= TRIG_COUNT | TRIG_NONE; 229 if (!cmd->stop_src || tmp != cmd->stop_src) 230 err++; 231 232 if (err) 233 return 1; 234 235 /* step 2: make sure trigger sources are unique and mutually compatible */ 236 if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE) 237 err++; 238 if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT) 239 err++; 240 if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT) 241 err++; 242 243 if (err) 244 return 2; 245 246 /* step 3: make sure arguments are trivially compatible */ 247 if (cmd->start_arg != 0) { 248 cmd->start_arg = 0; 249 err++; 250 } 251 252 if (cmd->scan_begin_src == TRIG_FOLLOW) { 253 /* internal trigger */ 254 if (cmd->scan_begin_arg != 0) { 255 cmd->scan_begin_arg = 0; 256 err++; 257 } 258 } 259 260 if (cmd->convert_src == TRIG_TIMER) { 261 if (cmd->convert_arg < thisboard->ai_speed) { 262 cmd->convert_arg = thisboard->ai_speed; 263 err++; 264 } 265 } 266 267 if (cmd->scan_end_arg != cmd->chanlist_len) { 268 cmd->scan_end_arg = cmd->chanlist_len; 269 err++; 270 } 271 272 if (cmd->stop_src == TRIG_COUNT) { 273 /* any count is allowed */ 274 } else { 275 /* TRIG_NONE */ 276 if (cmd->stop_arg != 0) { 277 cmd->stop_arg = 0; 278 err++; 279 } 280 } 281 282 if (err) 283 return 3; 284 285 /* step 4: fix up arguments */ 286 287 if (cmd->convert_src == TRIG_TIMER) { 288 tmp = cmd->convert_arg; 289 /* calculate counter values that give desired timing */ 290 i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL, 291 &(devpriv->divisor1), &(devpriv->divisor2), 292 &(cmd->convert_arg), cmd->flags & TRIG_ROUND_MASK); 293 if (tmp != cmd->convert_arg) 294 err++; 295 } 296 297 if (err) 298 return 4; 299 300 /* check chanlist against board's peculiarities */ 301 if (cmd->chanlist && cmd->chanlist_len > 1) { 302 for (i = 0; i < cmd->chanlist_len; i++) { 303 /* even/odd channels must go into even/odd queue addresses */ 304 if ((i % 2) != (CR_CHAN(cmd->chanlist[i]) % 2)) { 305 comedi_error(dev, "bad chanlist:\n" 306 " even/odd channels must go have even/odd chanlist indices"); 307 err++; 308 } 309 } 310 if ((cmd->chanlist_len % 2) != 0) { 311 comedi_error(dev, 312 "chanlist must be of even length or length 1"); 313 err++; 314 } 315 } 316 317 if (err) 318 return 5; 319 320 return 0; 321} 322 323static int das16m1_cmd_exec(struct comedi_device *dev, struct comedi_subdevice *s) 324{ 325 struct comedi_async *async = s->async; 326 struct comedi_cmd *cmd = &async->cmd; 327 unsigned int byte, i; 328 329 if (dev->irq == 0) { 330 comedi_error(dev, "irq required to execute comedi_cmd"); 331 return -1; 332 } 333 334 /* disable interrupts and internal pacer */ 335 devpriv->control_state &= ~INTE & ~PACER_MASK; 336 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL); 337 338 /* set software count */ 339 devpriv->adc_count = 0; 340 /* Initialize lower half of hardware counter, used to determine how 341 * many samples are in fifo. Value doesn't actually load into counter 342 * until counter's next clock (the next a/d conversion) */ 343 i8254_load(dev->iobase + DAS16M1_8254_FIRST, 0, 1, 0, 2); 344 /* remember current reading of counter so we know when counter has 345 * actually been loaded */ 346 devpriv->initial_hw_count = 347 i8254_read(dev->iobase + DAS16M1_8254_FIRST, 0, 1); 348 /* setup channel/gain queue */ 349 for (i = 0; i < cmd->chanlist_len; i++) { 350 outb(i, dev->iobase + DAS16M1_QUEUE_ADDR); 351 byte = Q_CHAN(CR_CHAN(cmd-> 352 chanlist[i])) | Q_RANGE(CR_RANGE(cmd-> 353 chanlist[i])); 354 outb(byte, dev->iobase + DAS16M1_QUEUE_DATA); 355 } 356 357 /* set counter mode and counts */ 358 cmd->convert_arg = 359 das16m1_set_pacer(dev, cmd->convert_arg, 360 cmd->flags & TRIG_ROUND_MASK); 361 362 /* set control & status register */ 363 byte = 0; 364 /* if we are using external start trigger (also board dislikes having 365 * both start and conversion triggers external simultaneously) */ 366 if (cmd->start_src == TRIG_EXT && cmd->convert_src != TRIG_EXT) { 367 byte |= EXT_TRIG_BIT; 368 } 369 outb(byte, dev->iobase + DAS16M1_CS); 370 /* clear interrupt bit */ 371 outb(0, dev->iobase + DAS16M1_CLEAR_INTR); 372 373 /* enable interrupts and internal pacer */ 374 devpriv->control_state &= ~PACER_MASK; 375 if (cmd->convert_src == TRIG_TIMER) { 376 devpriv->control_state |= INT_PACER; 377 } else { 378 devpriv->control_state |= EXT_PACER; 379 } 380 devpriv->control_state |= INTE; 381 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL); 382 383 return 0; 384} 385 386static int das16m1_cancel(struct comedi_device *dev, struct comedi_subdevice *s) 387{ 388 devpriv->control_state &= ~INTE & ~PACER_MASK; 389 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL); 390 391 return 0; 392} 393 394static int das16m1_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, 395 struct comedi_insn *insn, unsigned int *data) 396{ 397 int i, n; 398 int byte; 399 const int timeout = 1000; 400 401 /* disable interrupts and internal pacer */ 402 devpriv->control_state &= ~INTE & ~PACER_MASK; 403 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL); 404 405 /* setup channel/gain queue */ 406 outb(0, dev->iobase + DAS16M1_QUEUE_ADDR); 407 byte = Q_CHAN(CR_CHAN(insn->chanspec)) | Q_RANGE(CR_RANGE(insn-> 408 chanspec)); 409 outb(byte, dev->iobase + DAS16M1_QUEUE_DATA); 410 411 for (n = 0; n < insn->n; n++) { 412 /* clear IRQDATA bit */ 413 outb(0, dev->iobase + DAS16M1_CLEAR_INTR); 414 /* trigger conversion */ 415 outb(0, dev->iobase); 416 417 for (i = 0; i < timeout; i++) { 418 if (inb(dev->iobase + DAS16M1_CS) & IRQDATA) 419 break; 420 } 421 if (i == timeout) { 422 comedi_error(dev, "timeout"); 423 return -ETIME; 424 } 425 data[n] = munge_sample(inw(dev->iobase)); 426 } 427 428 return n; 429} 430 431static int das16m1_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s, 432 struct comedi_insn *insn, unsigned int *data) 433{ 434 unsigned int bits; 435 436 bits = inb(dev->iobase + DAS16M1_DIO) & 0xf; 437 data[1] = bits; 438 data[0] = 0; 439 440 return 2; 441} 442 443static int das16m1_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s, 444 struct comedi_insn *insn, unsigned int *data) 445{ 446 unsigned int wbits; 447 448 /* only set bits that have been masked */ 449 data[0] &= 0xf; 450 wbits = devpriv->do_bits; 451 /* zero bits that have been masked */ 452 wbits &= ~data[0]; 453 /* set masked bits */ 454 wbits |= data[0] & data[1]; 455 devpriv->do_bits = wbits; 456 data[1] = wbits; 457 458 outb(devpriv->do_bits, dev->iobase + DAS16M1_DIO); 459 460 return 2; 461} 462 463static int das16m1_poll(struct comedi_device *dev, struct comedi_subdevice *s) 464{ 465 unsigned long flags; 466 unsigned int status; 467 468 /* prevent race with interrupt handler */ 469 spin_lock_irqsave(&dev->spinlock, flags); 470 status = inb(dev->iobase + DAS16M1_CS); 471 das16m1_handler(dev, status); 472 spin_unlock_irqrestore(&dev->spinlock, flags); 473 474 return s->async->buf_write_count - s->async->buf_read_count; 475} 476 477static irqreturn_t das16m1_interrupt(int irq, void *d) 478{ 479 int status; 480 struct comedi_device *dev = d; 481 482 if (dev->attached == 0) { 483 comedi_error(dev, "premature interrupt"); 484 return IRQ_HANDLED; 485 } 486 /* prevent race with comedi_poll() */ 487 spin_lock(&dev->spinlock); 488 489 status = inb(dev->iobase + DAS16M1_CS); 490 491 if ((status & (IRQDATA | OVRUN)) == 0) { 492 comedi_error(dev, "spurious interrupt"); 493 spin_unlock(&dev->spinlock); 494 return IRQ_NONE; 495 } 496 497 das16m1_handler(dev, status); 498 499 /* clear interrupt */ 500 outb(0, dev->iobase + DAS16M1_CLEAR_INTR); 501 502 spin_unlock(&dev->spinlock); 503 return IRQ_HANDLED; 504} 505 506static void munge_sample_array(short *array, unsigned int num_elements) 507{ 508 unsigned int i; 509 510 for (i = 0; i < num_elements; i++) { 511 array[i] = munge_sample(array[i]); 512 } 513} 514 515static void das16m1_handler(struct comedi_device *dev, unsigned int status) 516{ 517 struct comedi_subdevice *s; 518 struct comedi_async *async; 519 struct comedi_cmd *cmd; 520 u16 num_samples; 521 u16 hw_counter; 522 523 s = dev->read_subdev; 524 async = s->async; 525 async->events = 0; 526 cmd = &async->cmd; 527 528 /* figure out how many samples are in fifo */ 529 hw_counter = i8254_read(dev->iobase + DAS16M1_8254_FIRST, 0, 1); 530 /* make sure hardware counter reading is not bogus due to initial value 531 * not having been loaded yet */ 532 if (devpriv->adc_count == 0 && hw_counter == devpriv->initial_hw_count) { 533 num_samples = 0; 534 } else { 535 /* The calculation of num_samples looks odd, but it uses the following facts. 536 * 16 bit hardware counter is initialized with value of zero (which really 537 * means 0x1000). The counter decrements by one on each conversion 538 * (when the counter decrements from zero it goes to 0xffff). num_samples 539 * is a 16 bit variable, so it will roll over in a similar fashion to the 540 * hardware counter. Work it out, and this is what you get. */ 541 num_samples = -hw_counter - devpriv->adc_count; 542 } 543 /* check if we only need some of the points */ 544 if (cmd->stop_src == TRIG_COUNT) { 545 if (num_samples > cmd->stop_arg * cmd->chanlist_len) 546 num_samples = cmd->stop_arg * cmd->chanlist_len; 547 } 548 /* make sure we dont try to get too many points if fifo has overrun */ 549 if (num_samples > FIFO_SIZE) 550 num_samples = FIFO_SIZE; 551 insw(dev->iobase, devpriv->ai_buffer, num_samples); 552 munge_sample_array(devpriv->ai_buffer, num_samples); 553 cfc_write_array_to_buffer(s, devpriv->ai_buffer, 554 num_samples * sizeof(short)); 555 devpriv->adc_count += num_samples; 556 557 if (cmd->stop_src == TRIG_COUNT) { 558 if (devpriv->adc_count >= cmd->stop_arg * cmd->chanlist_len) { /* end of acquisition */ 559 das16m1_cancel(dev, s); 560 async->events |= COMEDI_CB_EOA; 561 } 562 } 563 564 /* this probably won't catch overruns since the card doesn't generate 565 * overrun interrupts, but we might as well try */ 566 if (status & OVRUN) { 567 das16m1_cancel(dev, s); 568 async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR; 569 comedi_error(dev, "fifo overflow"); 570 } 571 572 comedi_event(dev, s); 573 574} 575 576/* This function takes a time in nanoseconds and sets the * 577 * 2 pacer clocks to the closest frequency possible. It also * 578 * returns the actual sampling period. */ 579static unsigned int das16m1_set_pacer(struct comedi_device *dev, unsigned int ns, 580 int rounding_flags) 581{ 582 i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL, &(devpriv->divisor1), 583 &(devpriv->divisor2), &ns, rounding_flags & TRIG_ROUND_MASK); 584 585 /* Write the values of ctr1 and ctr2 into counters 1 and 2 */ 586 i8254_load(dev->iobase + DAS16M1_8254_SECOND, 0, 1, devpriv->divisor1, 587 2); 588 i8254_load(dev->iobase + DAS16M1_8254_SECOND, 0, 2, devpriv->divisor2, 589 2); 590 591 return ns; 592} 593 594static int das16m1_irq_bits(unsigned int irq) 595{ 596 int ret; 597 598 switch (irq) { 599 case 10: 600 ret = 0x0; 601 break; 602 case 11: 603 ret = 0x1; 604 break; 605 case 12: 606 ret = 0x2; 607 break; 608 case 15: 609 ret = 0x3; 610 break; 611 case 2: 612 ret = 0x4; 613 break; 614 case 3: 615 ret = 0x5; 616 break; 617 case 5: 618 ret = 0x6; 619 break; 620 case 7: 621 ret = 0x7; 622 break; 623 default: 624 return -1; 625 break; 626 } 627 return ret << 4; 628} 629 630/* 631 * Options list: 632 * 0 I/O base 633 * 1 IRQ 634 */ 635 636static int das16m1_attach(struct comedi_device *dev, struct comedi_devconfig *it) 637{ 638 struct comedi_subdevice *s; 639 int ret; 640 unsigned int irq; 641 unsigned long iobase; 642 643 iobase = it->options[0]; 644 645 printk("comedi%d: das16m1:", dev->minor); 646 647 ret = alloc_private(dev, sizeof(struct das16m1_private_struct)); 648 if (ret < 0) 649 return ret; 650 651 dev->board_name = thisboard->name; 652 653 printk(" io 0x%lx-0x%lx 0x%lx-0x%lx", 654 iobase, iobase + DAS16M1_SIZE, 655 iobase + DAS16M1_82C55, iobase + DAS16M1_82C55 + DAS16M1_SIZE2); 656 if (!request_region(iobase, DAS16M1_SIZE, driver_das16m1.driver_name)) { 657 printk(" I/O port conflict\n"); 658 return -EIO; 659 } 660 if (!request_region(iobase + DAS16M1_82C55, DAS16M1_SIZE2, 661 driver_das16m1.driver_name)) { 662 release_region(iobase, DAS16M1_SIZE); 663 printk(" I/O port conflict\n"); 664 return -EIO; 665 } 666 dev->iobase = iobase; 667 668 /* now for the irq */ 669 irq = it->options[1]; 670 /* make sure it is valid */ 671 if (das16m1_irq_bits(irq) >= 0) { 672 ret = request_irq(irq, das16m1_interrupt, 0, 673 driver_das16m1.driver_name, dev); 674 if (ret < 0) { 675 printk(", irq unavailable\n"); 676 return ret; 677 } 678 dev->irq = irq; 679 printk(", irq %u\n", irq); 680 } else if (irq == 0) { 681 printk(", no irq\n"); 682 } else { 683 printk(", invalid irq\n" 684 " valid irqs are 2, 3, 5, 7, 10, 11, 12, or 15\n"); 685 return -EINVAL; 686 } 687 688 ret = alloc_subdevices(dev, 4); 689 if (ret < 0) 690 return ret; 691 692 s = dev->subdevices + 0; 693 dev->read_subdev = s; 694 /* ai */ 695 s->type = COMEDI_SUBD_AI; 696 s->subdev_flags = SDF_READABLE | SDF_CMD_READ; 697 s->n_chan = 8; 698 s->subdev_flags = SDF_DIFF; 699 s->len_chanlist = 256; 700 s->maxdata = (1 << 12) - 1; 701 s->range_table = &range_das16m1; 702 s->insn_read = das16m1_ai_rinsn; 703 s->do_cmdtest = das16m1_cmd_test; 704 s->do_cmd = das16m1_cmd_exec; 705 s->cancel = das16m1_cancel; 706 s->poll = das16m1_poll; 707 708 s = dev->subdevices + 1; 709 /* di */ 710 s->type = COMEDI_SUBD_DI; 711 s->subdev_flags = SDF_READABLE; 712 s->n_chan = 4; 713 s->maxdata = 1; 714 s->range_table = &range_digital; 715 s->insn_bits = das16m1_di_rbits; 716 717 s = dev->subdevices + 2; 718 /* do */ 719 s->type = COMEDI_SUBD_DO; 720 s->subdev_flags = SDF_WRITABLE | SDF_READABLE; 721 s->n_chan = 4; 722 s->maxdata = 1; 723 s->range_table = &range_digital; 724 s->insn_bits = das16m1_do_wbits; 725 726 s = dev->subdevices + 3; 727 /* 8255 */ 728 subdev_8255_init(dev, s, NULL, dev->iobase + DAS16M1_82C55); 729 730 /* disable upper half of hardware conversion counter so it doesn't mess with us */ 731 outb(TOTAL_CLEAR, dev->iobase + DAS16M1_8254_FIRST_CNTRL); 732 733 /* initialize digital output lines */ 734 outb(devpriv->do_bits, dev->iobase + DAS16M1_DIO); 735 736 /* set the interrupt level */ 737 if (dev->irq) 738 devpriv->control_state = das16m1_irq_bits(dev->irq); 739 else 740 devpriv->control_state = 0; 741 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL); 742 743 return 0; 744} 745 746static int das16m1_detach(struct comedi_device *dev) 747{ 748 printk("comedi%d: das16m1: remove\n", dev->minor); 749 750/* das16m1_reset(dev); */ 751 752 if (dev->subdevices) 753 subdev_8255_cleanup(dev, dev->subdevices + 3); 754 755 if (dev->irq) 756 free_irq(dev->irq, dev); 757 758 if (dev->iobase) { 759 release_region(dev->iobase, DAS16M1_SIZE); 760 release_region(dev->iobase + DAS16M1_82C55, DAS16M1_SIZE2); 761 } 762 763 return 0; 764} 765