icp_multi.c revision 194b9e3cf49319231e8b47125dc5d043350f4406
1/* 2 comedi/drivers/icp_multi.c 3 4 COMEDI - Linux Control and Measurement Device Interface 5 Copyright (C) 1997-2002 David A. Schleef <ds@schleef.org> 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 21*/ 22 23/* 24Driver: icp_multi 25Description: Inova ICP_MULTI 26Author: Anne Smorthit <anne.smorthit@sfwte.ch> 27Devices: [Inova] ICP_MULTI (icp_multi) 28Status: works 29 30The driver works for analog input and output and digital input and output. 31It does not work with interrupts or with the counters. Currently no support 32for DMA. 33 34It has 16 single-ended or 8 differential Analogue Input channels with 12-bit 35resolution. Ranges : 5V, 10V, +/-5V, +/-10V, 0..20mA and 4..20mA. Input 36ranges can be individually programmed for each channel. Voltage or current 37measurement is selected by jumper. 38 39There are 4 x 12-bit Analogue Outputs. Ranges : 5V, 10V, +/-5V, +/-10V 40 4116 x Digital Inputs, 24V 42 438 x Digital Outputs, 24V, 1A 44 454 x 16-bit counters 46 47Configuration options: not applicable, uses PCI auto config 48*/ 49 50#include <linux/interrupt.h> 51#include "../comedidev.h" 52 53#include <linux/delay.h> 54#include <linux/pci.h> 55 56#define PCI_DEVICE_ID_ICP_MULTI 0x8000 57 58#define ICP_MULTI_ADC_CSR 0 /* R/W: ADC command/status register */ 59#define ICP_MULTI_AI 2 /* R: Analogue input data */ 60#define ICP_MULTI_DAC_CSR 4 /* R/W: DAC command/status register */ 61#define ICP_MULTI_AO 6 /* R/W: Analogue output data */ 62#define ICP_MULTI_DI 8 /* R/W: Digital inouts */ 63#define ICP_MULTI_DO 0x0A /* R/W: Digital outputs */ 64#define ICP_MULTI_INT_EN 0x0C /* R/W: Interrupt enable register */ 65#define ICP_MULTI_INT_STAT 0x0E /* R/W: Interrupt status register */ 66#define ICP_MULTI_CNTR0 0x10 /* R/W: Counter 0 */ 67#define ICP_MULTI_CNTR1 0x12 /* R/W: counter 1 */ 68#define ICP_MULTI_CNTR2 0x14 /* R/W: Counter 2 */ 69#define ICP_MULTI_CNTR3 0x16 /* R/W: Counter 3 */ 70 71#define ICP_MULTI_SIZE 0x20 /* 32 bytes */ 72 73/* Define bits from ADC command/status register */ 74#define ADC_ST 0x0001 /* Start ADC */ 75#define ADC_BSY 0x0001 /* ADC busy */ 76#define ADC_BI 0x0010 /* Bipolar input range 1 = bipolar */ 77#define ADC_RA 0x0020 /* Input range 0 = 5V, 1 = 10V */ 78#define ADC_DI 0x0040 /* Differential input mode 1 = differential */ 79 80/* Define bits from DAC command/status register */ 81#define DAC_ST 0x0001 /* Start DAC */ 82#define DAC_BSY 0x0001 /* DAC busy */ 83#define DAC_BI 0x0010 /* Bipolar input range 1 = bipolar */ 84#define DAC_RA 0x0020 /* Input range 0 = 5V, 1 = 10V */ 85 86/* Define bits from interrupt enable/status registers */ 87#define ADC_READY 0x0001 /* A/d conversion ready interrupt */ 88#define DAC_READY 0x0002 /* D/a conversion ready interrupt */ 89#define DOUT_ERROR 0x0004 /* Digital output error interrupt */ 90#define DIN_STATUS 0x0008 /* Digital input status change interrupt */ 91#define CIE0 0x0010 /* Counter 0 overrun interrupt */ 92#define CIE1 0x0020 /* Counter 1 overrun interrupt */ 93#define CIE2 0x0040 /* Counter 2 overrun interrupt */ 94#define CIE3 0x0080 /* Counter 3 overrun interrupt */ 95 96/* Useful definitions */ 97#define Status_IRQ 0x00ff /* All interrupts */ 98 99/* Define analogue range */ 100static const struct comedi_lrange range_analog = { 4, { 101 UNI_RANGE(5), 102 UNI_RANGE(10), 103 BIP_RANGE(5), 104 BIP_RANGE(10) 105 } 106}; 107 108static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 }; 109 110/* 111============================================================================== 112 Data & Structure declarations 113============================================================================== 114*/ 115 116struct icp_multi_private { 117 char valid; /* card is usable */ 118 void __iomem *io_addr; /* Pointer to mapped io address */ 119 unsigned int AdcCmdStatus; /* ADC Command/Status register */ 120 unsigned int DacCmdStatus; /* DAC Command/Status register */ 121 unsigned int IntEnable; /* Interrupt Enable register */ 122 unsigned int IntStatus; /* Interrupt Status register */ 123 unsigned int act_chanlist[32]; /* list of scaned channel */ 124 unsigned char act_chanlist_len; /* len of scanlist */ 125 unsigned char act_chanlist_pos; /* actual position in MUX list */ 126 unsigned int *ai_chanlist; /* actaul chanlist */ 127 short *ai_data; /* data buffer */ 128 short ao_data[4]; /* data output buffer */ 129 short di_data; /* Digital input data */ 130 unsigned int do_data; /* Remember digital output data */ 131}; 132 133#define devpriv ((struct icp_multi_private *)dev->private) 134 135/* 136============================================================================== 137 138Name: setup_channel_list 139 140Description: 141 This function sets the appropriate channel selection, 142 differential input mode and range bits in the ADC Command/ 143 Status register. 144 145Parameters: 146 struct comedi_device *dev Pointer to current service structure 147 struct comedi_subdevice *s Pointer to current subdevice structure 148 unsigned int *chanlist Pointer to packed channel list 149 unsigned int n_chan Number of channels to scan 150 151Returns:Void 152 153============================================================================== 154*/ 155static void setup_channel_list(struct comedi_device *dev, 156 struct comedi_subdevice *s, 157 unsigned int *chanlist, unsigned int n_chan) 158{ 159 unsigned int i, range, chanprog; 160 unsigned int diff; 161 162 devpriv->act_chanlist_len = n_chan; 163 devpriv->act_chanlist_pos = 0; 164 165 for (i = 0; i < n_chan; i++) { 166 /* Get channel */ 167 chanprog = CR_CHAN(chanlist[i]); 168 169 /* Determine if it is a differential channel (Bit 15 = 1) */ 170 if (CR_AREF(chanlist[i]) == AREF_DIFF) { 171 diff = 1; 172 chanprog &= 0x0007; 173 } else { 174 diff = 0; 175 chanprog &= 0x000f; 176 } 177 178 /* Clear channel, range and input mode bits 179 * in A/D command/status register */ 180 devpriv->AdcCmdStatus &= 0xf00f; 181 182 /* Set channel number and differential mode status bit */ 183 if (diff) { 184 /* Set channel number, bits 9-11 & mode, bit 6 */ 185 devpriv->AdcCmdStatus |= (chanprog << 9); 186 devpriv->AdcCmdStatus |= ADC_DI; 187 } else 188 /* Set channel number, bits 8-11 */ 189 devpriv->AdcCmdStatus |= (chanprog << 8); 190 191 /* Get range for current channel */ 192 range = range_codes_analog[CR_RANGE(chanlist[i])]; 193 /* Set range. bits 4-5 */ 194 devpriv->AdcCmdStatus |= range; 195 196 /* Output channel, range, mode to ICP Multi */ 197 writew(devpriv->AdcCmdStatus, 198 devpriv->io_addr + ICP_MULTI_ADC_CSR); 199 } 200} 201 202/* 203============================================================================== 204 205Name: icp_multi_insn_read_ai 206 207Description: 208 This function reads a single analogue input. 209 210Parameters: 211 struct comedi_device *dev Pointer to current device structure 212 struct comedi_subdevice *s Pointer to current subdevice structure 213 struct comedi_insn *insn Pointer to current comedi instruction 214 unsigned int *data Pointer to analogue input data 215 216Returns:int Nmuber of instructions executed 217 218============================================================================== 219*/ 220static int icp_multi_insn_read_ai(struct comedi_device *dev, 221 struct comedi_subdevice *s, 222 struct comedi_insn *insn, unsigned int *data) 223{ 224 int n, timeout; 225 226 /* Disable A/D conversion ready interrupt */ 227 devpriv->IntEnable &= ~ADC_READY; 228 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN); 229 230 /* Clear interrupt status */ 231 devpriv->IntStatus |= ADC_READY; 232 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT); 233 234 /* Set up appropriate channel, mode and range data, for specified ch */ 235 setup_channel_list(dev, s, &insn->chanspec, 1); 236 237 for (n = 0; n < insn->n; n++) { 238 /* Set start ADC bit */ 239 devpriv->AdcCmdStatus |= ADC_ST; 240 writew(devpriv->AdcCmdStatus, 241 devpriv->io_addr + ICP_MULTI_ADC_CSR); 242 devpriv->AdcCmdStatus &= ~ADC_ST; 243 244 udelay(1); 245 246 /* Wait for conversion to complete, or get fed up waiting */ 247 timeout = 100; 248 while (timeout--) { 249 if (!(readw(devpriv->io_addr + 250 ICP_MULTI_ADC_CSR) & ADC_BSY)) 251 goto conv_finish; 252 253 udelay(1); 254 } 255 256 /* If we reach here, a timeout has occurred */ 257 comedi_error(dev, "A/D insn timeout"); 258 259 /* Disable interrupt */ 260 devpriv->IntEnable &= ~ADC_READY; 261 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN); 262 263 /* Clear interrupt status */ 264 devpriv->IntStatus |= ADC_READY; 265 writew(devpriv->IntStatus, 266 devpriv->io_addr + ICP_MULTI_INT_STAT); 267 268 /* Clear data received */ 269 data[n] = 0; 270 271 return -ETIME; 272 273conv_finish: 274 data[n] = 275 (readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff; 276 } 277 278 /* Disable interrupt */ 279 devpriv->IntEnable &= ~ADC_READY; 280 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN); 281 282 /* Clear interrupt status */ 283 devpriv->IntStatus |= ADC_READY; 284 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT); 285 286 return n; 287} 288 289/* 290============================================================================== 291 292Name: icp_multi_insn_write_ao 293 294Description: 295 This function writes a single analogue output. 296 297Parameters: 298 struct comedi_device *dev Pointer to current device structure 299 struct comedi_subdevice *s Pointer to current subdevice structure 300 struct comedi_insn *insn Pointer to current comedi instruction 301 unsigned int *data Pointer to analogue output data 302 303Returns:int Nmuber of instructions executed 304 305============================================================================== 306*/ 307static int icp_multi_insn_write_ao(struct comedi_device *dev, 308 struct comedi_subdevice *s, 309 struct comedi_insn *insn, unsigned int *data) 310{ 311 int n, chan, range, timeout; 312 313 /* Disable D/A conversion ready interrupt */ 314 devpriv->IntEnable &= ~DAC_READY; 315 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN); 316 317 /* Clear interrupt status */ 318 devpriv->IntStatus |= DAC_READY; 319 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT); 320 321 /* Get channel number and range */ 322 chan = CR_CHAN(insn->chanspec); 323 range = CR_RANGE(insn->chanspec); 324 325 /* Set up range and channel data */ 326 /* Bit 4 = 1 : Bipolar */ 327 /* Bit 5 = 0 : 5V */ 328 /* Bit 5 = 1 : 10V */ 329 /* Bits 8-9 : Channel number */ 330 devpriv->DacCmdStatus &= 0xfccf; 331 devpriv->DacCmdStatus |= range_codes_analog[range]; 332 devpriv->DacCmdStatus |= (chan << 8); 333 334 writew(devpriv->DacCmdStatus, devpriv->io_addr + ICP_MULTI_DAC_CSR); 335 336 for (n = 0; n < insn->n; n++) { 337 /* Wait for analogue output data register to be 338 * ready for new data, or get fed up waiting */ 339 timeout = 100; 340 while (timeout--) { 341 if (!(readw(devpriv->io_addr + 342 ICP_MULTI_DAC_CSR) & DAC_BSY)) 343 goto dac_ready; 344 345 udelay(1); 346 } 347 348 /* If we reach here, a timeout has occurred */ 349 comedi_error(dev, "D/A insn timeout"); 350 351 /* Disable interrupt */ 352 devpriv->IntEnable &= ~DAC_READY; 353 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN); 354 355 /* Clear interrupt status */ 356 devpriv->IntStatus |= DAC_READY; 357 writew(devpriv->IntStatus, 358 devpriv->io_addr + ICP_MULTI_INT_STAT); 359 360 /* Clear data received */ 361 devpriv->ao_data[chan] = 0; 362 363 return -ETIME; 364 365dac_ready: 366 /* Write data to analogue output data register */ 367 writew(data[n], devpriv->io_addr + ICP_MULTI_AO); 368 369 /* Set DAC_ST bit to write the data to selected channel */ 370 devpriv->DacCmdStatus |= DAC_ST; 371 writew(devpriv->DacCmdStatus, 372 devpriv->io_addr + ICP_MULTI_DAC_CSR); 373 devpriv->DacCmdStatus &= ~DAC_ST; 374 375 /* Save analogue output data */ 376 devpriv->ao_data[chan] = data[n]; 377 } 378 379 return n; 380} 381 382/* 383============================================================================== 384 385Name: icp_multi_insn_read_ao 386 387Description: 388 This function reads a single analogue output. 389 390Parameters: 391 struct comedi_device *dev Pointer to current device structure 392 struct comedi_subdevice *s Pointer to current subdevice structure 393 struct comedi_insn *insn Pointer to current comedi instruction 394 unsigned int *data Pointer to analogue output data 395 396Returns:int Nmuber of instructions executed 397 398============================================================================== 399*/ 400static int icp_multi_insn_read_ao(struct comedi_device *dev, 401 struct comedi_subdevice *s, 402 struct comedi_insn *insn, unsigned int *data) 403{ 404 int n, chan; 405 406 /* Get channel number */ 407 chan = CR_CHAN(insn->chanspec); 408 409 /* Read analogue outputs */ 410 for (n = 0; n < insn->n; n++) 411 data[n] = devpriv->ao_data[chan]; 412 413 return n; 414} 415 416/* 417============================================================================== 418 419Name: icp_multi_insn_bits_di 420 421Description: 422 This function reads the digital inputs. 423 424Parameters: 425 struct comedi_device *dev Pointer to current device structure 426 struct comedi_subdevice *s Pointer to current subdevice structure 427 struct comedi_insn *insn Pointer to current comedi instruction 428 unsigned int *data Pointer to analogue output data 429 430Returns:int Nmuber of instructions executed 431 432============================================================================== 433*/ 434static int icp_multi_insn_bits_di(struct comedi_device *dev, 435 struct comedi_subdevice *s, 436 struct comedi_insn *insn, unsigned int *data) 437{ 438 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI); 439 440 return insn->n; 441} 442 443/* 444============================================================================== 445 446Name: icp_multi_insn_bits_do 447 448Description: 449 This function writes the appropriate digital outputs. 450 451Parameters: 452 struct comedi_device *dev Pointer to current device structure 453 struct comedi_subdevice *s Pointer to current subdevice structure 454 struct comedi_insn *insn Pointer to current comedi instruction 455 unsigned int *data Pointer to analogue output data 456 457Returns:int Nmuber of instructions executed 458 459============================================================================== 460*/ 461static int icp_multi_insn_bits_do(struct comedi_device *dev, 462 struct comedi_subdevice *s, 463 struct comedi_insn *insn, unsigned int *data) 464{ 465 if (data[0]) { 466 s->state &= ~data[0]; 467 s->state |= (data[0] & data[1]); 468 469 printk(KERN_DEBUG "Digital outputs = %4x \n", s->state); 470 471 writew(s->state, devpriv->io_addr + ICP_MULTI_DO); 472 } 473 474 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI); 475 476 return insn->n; 477} 478 479/* 480============================================================================== 481 482Name: icp_multi_insn_read_ctr 483 484Description: 485 This function reads the specified counter. 486 487Parameters: 488 struct comedi_device *dev Pointer to current device structure 489 struct comedi_subdevice *s Pointer to current subdevice structure 490 struct comedi_insn *insn Pointer to current comedi instruction 491 unsigned int *data Pointer to counter data 492 493Returns:int Nmuber of instructions executed 494 495============================================================================== 496*/ 497static int icp_multi_insn_read_ctr(struct comedi_device *dev, 498 struct comedi_subdevice *s, 499 struct comedi_insn *insn, unsigned int *data) 500{ 501 return 0; 502} 503 504/* 505============================================================================== 506 507Name: icp_multi_insn_write_ctr 508 509Description: 510 This function write to the specified counter. 511 512Parameters: 513 struct comedi_device *dev Pointer to current device structure 514 struct comedi_subdevice *s Pointer to current subdevice structure 515 struct comedi_insn *insn Pointer to current comedi instruction 516 unsigned int *data Pointer to counter data 517 518Returns:int Nmuber of instructions executed 519 520============================================================================== 521*/ 522static int icp_multi_insn_write_ctr(struct comedi_device *dev, 523 struct comedi_subdevice *s, 524 struct comedi_insn *insn, 525 unsigned int *data) 526{ 527 return 0; 528} 529 530/* 531============================================================================== 532 533Name: interrupt_service_icp_multi 534 535Description: 536 This function is the interrupt service routine for all 537 interrupts generated by the icp multi board. 538 539Parameters: 540 int irq 541 void *d Pointer to current device 542 543============================================================================== 544*/ 545static irqreturn_t interrupt_service_icp_multi(int irq, void *d) 546{ 547 struct comedi_device *dev = d; 548 int int_no; 549 550 /* Is this interrupt from our board? */ 551 int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ; 552 if (!int_no) 553 /* No, exit */ 554 return IRQ_NONE; 555 556 /* Determine which interrupt is active & handle it */ 557 switch (int_no) { 558 case ADC_READY: 559 break; 560 case DAC_READY: 561 break; 562 case DOUT_ERROR: 563 break; 564 case DIN_STATUS: 565 break; 566 case CIE0: 567 break; 568 case CIE1: 569 break; 570 case CIE2: 571 break; 572 case CIE3: 573 break; 574 default: 575 break; 576 577 } 578 579 return IRQ_HANDLED; 580} 581 582#if 0 583/* 584============================================================================== 585 586Name: check_channel_list 587 588Description: 589 This function checks if the channel list, provided by user 590 is built correctly 591 592Parameters: 593 struct comedi_device *dev Pointer to current service structure 594 struct comedi_subdevice *s Pointer to current subdevice structure 595 unsigned int *chanlist Pointer to packed channel list 596 unsigned int n_chan Number of channels to scan 597 598Returns:int 0 = failure 599 1 = success 600 601============================================================================== 602*/ 603static int check_channel_list(struct comedi_device *dev, 604 struct comedi_subdevice *s, 605 unsigned int *chanlist, unsigned int n_chan) 606{ 607 unsigned int i; 608 609 /* Check that we at least have one channel to check */ 610 if (n_chan < 1) { 611 comedi_error(dev, "range/channel list is empty!"); 612 return 0; 613 } 614 /* Check all channels */ 615 for (i = 0; i < n_chan; i++) { 616 /* Check that channel number is < maximum */ 617 if (CR_AREF(chanlist[i]) == AREF_DIFF) { 618 if (CR_CHAN(chanlist[i]) > (s->nchan / 2)) { 619 comedi_error(dev, 620 "Incorrect differential ai ch-nr"); 621 return 0; 622 } 623 } else { 624 if (CR_CHAN(chanlist[i]) > s->n_chan) { 625 comedi_error(dev, 626 "Incorrect ai channel number"); 627 return 0; 628 } 629 } 630 } 631 return 1; 632} 633#endif 634 635/* 636============================================================================== 637 638Name: icp_multi_reset 639 640Description: 641 This function resets the icp multi device to a 'safe' state 642 643Parameters: 644 struct comedi_device *dev Pointer to current service structure 645 646Returns:int 0 = success 647 648============================================================================== 649*/ 650static int icp_multi_reset(struct comedi_device *dev) 651{ 652 unsigned int i; 653 654 /* Clear INT enables and requests */ 655 writew(0, devpriv->io_addr + ICP_MULTI_INT_EN); 656 writew(0x00ff, devpriv->io_addr + ICP_MULTI_INT_STAT); 657 658 /* Set DACs to 0..5V range and 0V output */ 659 for (i = 0; i < 4; i++) { 660 devpriv->DacCmdStatus &= 0xfcce; 661 662 /* Set channel number */ 663 devpriv->DacCmdStatus |= (i << 8); 664 665 /* Output 0V */ 666 writew(0, devpriv->io_addr + ICP_MULTI_AO); 667 668 /* Set start conversion bit */ 669 devpriv->DacCmdStatus |= DAC_ST; 670 671 /* Output to command / status register */ 672 writew(devpriv->DacCmdStatus, 673 devpriv->io_addr + ICP_MULTI_DAC_CSR); 674 675 /* Delay to allow DAC time to recover */ 676 udelay(1); 677 } 678 679 /* Digital outputs to 0 */ 680 writew(0, devpriv->io_addr + ICP_MULTI_DO); 681 682 return 0; 683} 684 685static int icp_multi_attach_pci(struct comedi_device *dev, 686 struct pci_dev *pcidev) 687{ 688 struct comedi_subdevice *s; 689 resource_size_t iobase; 690 int ret; 691 692 comedi_set_hw_dev(dev, &pcidev->dev); 693 dev->board_name = dev->driver->driver_name; 694 695 ret = alloc_private(dev, sizeof(struct icp_multi_private)); 696 if (ret < 0) 697 return ret; 698 699 ret = comedi_pci_enable(pcidev, dev->board_name); 700 if (ret) 701 return ret; 702 iobase = pci_resource_start(pcidev, 2); 703 dev->iobase = iobase; 704 705 devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE); 706 if (!devpriv->io_addr) 707 return -ENOMEM; 708 709 ret = comedi_alloc_subdevices(dev, 5); 710 if (ret) 711 return ret; 712 713 icp_multi_reset(dev); 714 715 if (pcidev->irq) { 716 ret = request_irq(pcidev->irq, interrupt_service_icp_multi, 717 IRQF_SHARED, dev->board_name, dev); 718 if (ret == 0) 719 dev->irq = pcidev->irq; 720 } 721 722 s = &dev->subdevices[0]; 723 dev->read_subdev = s; 724 s->type = COMEDI_SUBD_AI; 725 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF; 726 s->n_chan = 16; 727 s->maxdata = 0x0fff; 728 s->len_chanlist = 16; 729 s->range_table = &range_analog; 730 s->insn_read = icp_multi_insn_read_ai; 731 732 s = &dev->subdevices[1]; 733 s->type = COMEDI_SUBD_AO; 734 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON; 735 s->n_chan = 4; 736 s->maxdata = 0x0fff; 737 s->len_chanlist = 4; 738 s->range_table = &range_analog; 739 s->insn_write = icp_multi_insn_write_ao; 740 s->insn_read = icp_multi_insn_read_ao; 741 742 s = &dev->subdevices[2]; 743 s->type = COMEDI_SUBD_DI; 744 s->subdev_flags = SDF_READABLE; 745 s->n_chan = 16; 746 s->maxdata = 1; 747 s->len_chanlist = 16; 748 s->range_table = &range_digital; 749 s->io_bits = 0; 750 s->insn_bits = icp_multi_insn_bits_di; 751 752 s = &dev->subdevices[3]; 753 s->type = COMEDI_SUBD_DO; 754 s->subdev_flags = SDF_WRITABLE | SDF_READABLE; 755 s->n_chan = 8; 756 s->maxdata = 1; 757 s->len_chanlist = 8; 758 s->range_table = &range_digital; 759 s->io_bits = 0xff; 760 s->state = 0; 761 s->insn_bits = icp_multi_insn_bits_do; 762 763 s = &dev->subdevices[4]; 764 s->type = COMEDI_SUBD_COUNTER; 765 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON; 766 s->n_chan = 4; 767 s->maxdata = 0xffff; 768 s->len_chanlist = 4; 769 s->state = 0; 770 s->insn_read = icp_multi_insn_read_ctr; 771 s->insn_write = icp_multi_insn_write_ctr; 772 773 devpriv->valid = 1; 774 775 dev_info(dev->class_dev, "%s attached, irq %sabled\n", 776 dev->board_name, dev->irq ? "en" : "dis"); 777 778 return 0; 779} 780 781static void icp_multi_detach(struct comedi_device *dev) 782{ 783 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 784 785 if (dev->private) 786 if (devpriv->valid) 787 icp_multi_reset(dev); 788 if (dev->irq) 789 free_irq(dev->irq, dev); 790 if (dev->private && devpriv->io_addr) 791 iounmap(devpriv->io_addr); 792 if (pcidev) { 793 if (dev->iobase) 794 comedi_pci_disable(pcidev); 795 } 796} 797 798static struct comedi_driver icp_multi_driver = { 799 .driver_name = "icp_multi", 800 .module = THIS_MODULE, 801 .attach_pci = icp_multi_attach_pci, 802 .detach = icp_multi_detach, 803}; 804 805static int __devinit icp_multi_pci_probe(struct pci_dev *dev, 806 const struct pci_device_id *ent) 807{ 808 return comedi_pci_auto_config(dev, &icp_multi_driver); 809} 810 811static void __devexit icp_multi_pci_remove(struct pci_dev *dev) 812{ 813 comedi_pci_auto_unconfig(dev); 814} 815 816static DEFINE_PCI_DEVICE_TABLE(icp_multi_pci_table) = { 817 { PCI_DEVICE(PCI_VENDOR_ID_ICP, PCI_DEVICE_ID_ICP_MULTI) }, 818 { 0 } 819}; 820MODULE_DEVICE_TABLE(pci, icp_multi_pci_table); 821 822static struct pci_driver icp_multi_pci_driver = { 823 .name = "icp_multi", 824 .id_table = icp_multi_pci_table, 825 .probe = icp_multi_pci_probe, 826 .remove = __devexit_p(icp_multi_pci_remove), 827}; 828module_comedi_pci_driver(icp_multi_driver, icp_multi_pci_driver); 829 830MODULE_AUTHOR("Comedi http://www.comedi.org"); 831MODULE_DESCRIPTION("Comedi low-level driver"); 832MODULE_LICENSE("GPL"); 833