2 comedi/drivers/icp_multi.c
4 COMEDI - Linux Control and Measurement Device Interface
5 Copyright (C) 1997-2002 David A. Schleef <ds@schleef.org>
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.
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.
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.
26 Description: Inova ICP_MULTI
27 Author: Anne Smorthit <anne.smorthit@sfwte.ch>
28 Devices: [Inova] ICP_MULTI (icp_multi)
31 The driver works for analog input and output and digital input and output.
32 It does not work with interrupts or with the counters. Currently no support
35 It has 16 single-ended or 8 differential Analogue Input channels with 12-bit
36 resolution. Ranges : 5V, 10V, +/-5V, +/-10V, 0..20mA and 4..20mA. Input
37 ranges can be individually programmed for each channel. Voltage or current
38 measurement is selected by jumper.
40 There are 4 x 12-bit Analogue Outputs. Ranges : 5V, 10V, +/-5V, +/-10V
42 16 x Digital Inputs, 24V
44 8 x Digital Outputs, 24V, 1A
49 [0] - PCI bus number - if bus number and slot number are 0,
50 then driver search for first unused card
54 #include <linux/comedidev.h>
56 #include <linux/delay.h>
57 #include <linux/pci.h>
59 #include "icp_multi.h"
62 #define DEVICE_ID 0x8000 /* Device ID */
64 #define ICP_MULTI_EXTDEBUG
66 // Hardware types of the cards
67 #define TYPE_ICP_MULTI 0
69 #define IORANGE_ICP_MULTI 32
71 #define ICP_MULTI_ADC_CSR 0 /* R/W: ADC command/status register */
72 #define ICP_MULTI_AI 2 /* R: Analogue input data */
73 #define ICP_MULTI_DAC_CSR 4 /* R/W: DAC command/status register */
74 #define ICP_MULTI_AO 6 /* R/W: Analogue output data */
75 #define ICP_MULTI_DI 8 /* R/W: Digital inouts */
76 #define ICP_MULTI_DO 0x0A /* R/W: Digital outputs */
77 #define ICP_MULTI_INT_EN 0x0C /* R/W: Interrupt enable register */
78 #define ICP_MULTI_INT_STAT 0x0E /* R/W: Interrupt status register */
79 #define ICP_MULTI_CNTR0 0x10 /* R/W: Counter 0 */
80 #define ICP_MULTI_CNTR1 0x12 /* R/W: counter 1 */
81 #define ICP_MULTI_CNTR2 0x14 /* R/W: Counter 2 */
82 #define ICP_MULTI_CNTR3 0x16 /* R/W: Counter 3 */
84 #define ICP_MULTI_SIZE 0x20 /* 32 bytes */
86 // Define bits from ADC command/status register
87 #define ADC_ST 0x0001 /* Start ADC */
88 #define ADC_BSY 0x0001 /* ADC busy */
89 #define ADC_BI 0x0010 /* Bipolar input range 1 = bipolar */
90 #define ADC_RA 0x0020 /* Input range 0 = 5V, 1 = 10V */
91 #define ADC_DI 0x0040 /* Differential input mode 1 = differential */
93 // Define bits from DAC command/status register
94 #define DAC_ST 0x0001 /* Start DAC */
95 #define DAC_BSY 0x0001 /* DAC busy */
96 #define DAC_BI 0x0010 /* Bipolar input range 1 = bipolar */
97 #define DAC_RA 0x0020 /* Input range 0 = 5V, 1 = 10V */
99 // Define bits from interrupt enable/status registers
100 #define ADC_READY 0x0001 /* A/d conversion ready interrupt */
101 #define DAC_READY 0x0002 /* D/a conversion ready interrupt */
102 #define DOUT_ERROR 0x0004 /* Digital output error interrupt */
103 #define DIN_STATUS 0x0008 /* Digital input status change interrupt */
104 #define CIE0 0x0010 /* Counter 0 overrun interrupt */
105 #define CIE1 0x0020 /* Counter 1 overrun interrupt */
106 #define CIE2 0x0040 /* Counter 2 overrun interrupt */
107 #define CIE3 0x0080 /* Counter 3 overrun interrupt */
109 // Useful definitions
110 #define Status_IRQ 0x00ff // All interrupts
113 // Define analogue range
114 static comedi_lrange range_analog={ 4, {
122 static char range_codes_analog[]={0x00, 0x20, 0x10, 0x30};
126 ==============================================================================
128 ==============================================================================
130 static int icp_multi_attach(comedi_device *dev, comedi_devconfig *it);
131 static int icp_multi_detach(comedi_device *dev);
135 ==============================================================================
136 Data & Structure declarations
137 ==============================================================================
139 static unsigned short pci_list_builded=0; /*>0 list of card is known */
142 char *name; // driver name
144 int iorange; // I/O range len
145 char have_irq; // 1=card support IRQ
146 char cardtype; // 0=ICP Multi
147 int n_aichan; // num of A/D chans
148 int n_aichand; // num of A/D chans in diff mode
149 int n_aochan; // num of D/A chans
150 int n_dichan; // num of DI chans
151 int n_dochan; // num of DO chans
152 int n_ctrs; // num of counters
153 int ai_maxdata; // resolution of A/D
154 int ao_maxdata; // resolution of D/A
155 comedi_lrange *rangelist_ai; // rangelist for A/D
156 char *rangecode; // range codes for programming
157 comedi_lrange *rangelist_ao; // rangelist for D/A
160 static boardtype boardtypes[] =
162 {"icp_multi", // Driver name
163 DEVICE_ID, // PCI device ID
164 IORANGE_ICP_MULTI, // I/O range length
165 1, // 1=Card supports interrupts
166 TYPE_ICP_MULTI, // Card type = ICP MULTI
167 16, // Num of A/D channels
168 8, // Num of A/D channels in diff mode
169 4, // Num of D/A channels
170 16, // Num of digital inputs
171 8, // Num of digital outputs
172 4, // Num of counters
173 0x0fff, // Resolution of A/D
174 0x0fff, // Resolution of D/A
175 &range_analog, // Rangelist for A/D
176 range_codes_analog, // Range codes for programming
177 &range_analog }, // Rangelist for D/A
180 #define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))
182 static comedi_driver driver_icp_multi={
183 driver_name: "icp_multi",
185 attach: icp_multi_attach,
186 detach: icp_multi_detach,
187 num_names: n_boardtypes,
188 board_name: (const char**)boardtypes,
189 offset: sizeof(boardtype),
191 COMEDI_INITCLEANUP(driver_icp_multi);
194 struct pcilst_struct *card; // pointer to card
195 char valid; // card is usable
196 void *io_addr; // Pointer to mapped io address
197 resource_size_t phys_iobase; // Physical io address
198 unsigned int AdcCmdStatus; // ADC Command/Status register
199 unsigned int DacCmdStatus; // DAC Command/Status register
200 unsigned int IntEnable; // Interrupt Enable register
201 unsigned int IntStatus; // Interrupt Status register
202 unsigned int act_chanlist[32]; // list of scaned channel
203 unsigned char act_chanlist_len; // len of scanlist
204 unsigned char act_chanlist_pos; // actual position in MUX list
205 unsigned int *ai_chanlist; // actaul chanlist
206 sampl_t *ai_data; // data buffer
207 sampl_t ao_data[4]; // data output buffer
208 sampl_t di_data; // Digital input data
209 unsigned int do_data; // Remember digital output data
212 #define devpriv ((icp_multi_private *)dev->private)
213 #define this_board ((boardtype *)dev->board_ptr)
216 ==============================================================================
217 More forward declarations
218 ==============================================================================
222 static int check_channel_list(comedi_device * dev, comedi_subdevice * s, unsigned int *chanlist, unsigned int n_chan);
224 static void setup_channel_list(comedi_device * dev, comedi_subdevice * s, unsigned int *chanlist, unsigned int n_chan);
225 static int icp_multi_reset(comedi_device *dev);
229 ==============================================================================
231 ==============================================================================
236 ==============================================================================
238 Name: icp_multi_insn_read_ai
241 This function reads a single analogue input.
244 comedi_device *dev Pointer to current device structure
245 comedi_subdevice *s Pointer to current subdevice structure
246 comedi_insn *insn Pointer to current comedi instruction
247 lsampl_t *data Pointer to analogue input data
249 Returns:int Nmuber of instructions executed
251 ==============================================================================
253 static int icp_multi_insn_read_ai(comedi_device * dev, comedi_subdevice * s, comedi_insn *insn, lsampl_t *data)
257 #ifdef ICP_MULTI_EXTDEBUG
258 printk("icp multi EDBG: BGN: icp_multi_insn_read_ai(...)\n");
260 // Disable A/D conversion ready interrupt
261 devpriv->IntEnable &= ~ADC_READY;
262 writew(devpriv->IntEnable,devpriv->io_addr + ICP_MULTI_INT_EN);
264 // Clear interrupt status
265 devpriv->IntStatus |= ADC_READY;
266 writew(devpriv->IntStatus,devpriv->io_addr + ICP_MULTI_INT_STAT);
268 // Set up appropriate channel, mode and range data, for specified channel
269 setup_channel_list(dev, s, &insn->chanspec, 1);
271 #ifdef ICP_MULTI_EXTDEBUG
272 printk("icp_multi A ST=%4x IO=%p\n",readw(devpriv->io_addr+ICP_MULTI_ADC_CSR), devpriv->io_addr+ICP_MULTI_ADC_CSR);
275 for (n=0; n<insn->n; n++) {
277 devpriv->AdcCmdStatus |= ADC_ST;
278 writew(devpriv->AdcCmdStatus, devpriv->io_addr+ICP_MULTI_ADC_CSR);
279 devpriv->AdcCmdStatus &= ~ADC_ST;
281 #ifdef ICP_MULTI_EXTDEBUG
282 printk("icp multi B n=%d ST=%4x\n",n,readw(devpriv->io_addr+ICP_MULTI_ADC_CSR));
287 #ifdef ICP_MULTI_EXTDEBUG
288 printk("icp multi C n=%d ST=%4x\n",n,readw(devpriv->io_addr+ICP_MULTI_ADC_CSR));
291 // Wait for conversion to complete, or get fed up waiting
294 if (!(readw(devpriv->io_addr+ICP_MULTI_ADC_CSR) & ADC_BSY))
297 #ifdef ICP_MULTI_EXTDEBUG
299 printk("icp multi D n=%d tm=%d ST=%4x\n",n,timeout,readw(devpriv->io_addr+ICP_MULTI_ADC_CSR));
305 // If we reach here, a timeout has occurred
306 comedi_error(dev,"A/D insn timeout");
309 devpriv->IntEnable &= ~ADC_READY;
310 writew(devpriv->IntEnable,devpriv->io_addr + ICP_MULTI_INT_EN);
312 // Clear interrupt status
313 devpriv->IntStatus |= ADC_READY;
314 writew(devpriv->IntStatus,devpriv->io_addr + ICP_MULTI_INT_STAT);
316 // Clear data received
319 #ifdef ICP_MULTI_EXTDEBUG
320 printk("icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n",n);
325 data[n] = (readw(devpriv->io_addr+ICP_MULTI_AI) >> 4 ) & 0x0fff;
329 devpriv->IntEnable &= ~ADC_READY;
330 writew(devpriv->IntEnable,devpriv->io_addr + ICP_MULTI_INT_EN);
332 // Clear interrupt status
333 devpriv->IntStatus |= ADC_READY;
334 writew(devpriv->IntStatus,devpriv->io_addr + ICP_MULTI_INT_STAT);
336 #ifdef ICP_MULTI_EXTDEBUG
337 printk("icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n",n);
343 ==============================================================================
345 Name: icp_multi_insn_write_ao
348 This function writes a single analogue output.
351 comedi_device *dev Pointer to current device structure
352 comedi_subdevice *s Pointer to current subdevice structure
353 comedi_insn *insn Pointer to current comedi instruction
354 lsampl_t *data Pointer to analogue output data
356 Returns:int Nmuber of instructions executed
358 ==============================================================================
360 static int icp_multi_insn_write_ao(comedi_device * dev, comedi_subdevice * s, comedi_insn *insn, lsampl_t *data)
362 int n, chan, range, timeout;
364 #ifdef ICP_MULTI_EXTDEBUG
365 printk("icp multi EDBG: BGN: icp_multi_insn_write_ao(...)\n");
367 // Disable D/A conversion ready interrupt
368 devpriv->IntEnable &= ~DAC_READY;
369 writew(devpriv->IntEnable,devpriv->io_addr + ICP_MULTI_INT_EN);
371 // Clear interrupt status
372 devpriv->IntStatus |= DAC_READY;
373 writew(devpriv->IntStatus,devpriv->io_addr + ICP_MULTI_INT_STAT);
375 // Get channel number and range
376 chan = CR_CHAN(insn->chanspec);
377 range = CR_RANGE(insn->chanspec);
379 // Set up range and channel data
380 // Bit 4 = 1 : Bipolar
383 // Bits 8-9 : Channel number
384 devpriv->DacCmdStatus &= 0xfccf;
385 devpriv->DacCmdStatus |= this_board->rangecode[range];
386 devpriv->DacCmdStatus |= (chan << 8);
388 writew(devpriv->DacCmdStatus, devpriv->io_addr+ICP_MULTI_DAC_CSR);
390 for (n=0; n<insn->n; n++) {
391 // Wait for analogue output data register to be ready for new data, or get fed up waiting
394 if (!(readw(devpriv->io_addr+ICP_MULTI_DAC_CSR) & DAC_BSY))
397 #ifdef ICP_MULTI_EXTDEBUG
399 printk("icp multi A n=%d tm=%d ST=%4x\n",n,timeout,readw(devpriv->io_addr+ICP_MULTI_DAC_CSR));
405 // If we reach here, a timeout has occurred
406 comedi_error(dev,"D/A insn timeout");
409 devpriv->IntEnable &= ~DAC_READY;
410 writew(devpriv->IntEnable,devpriv->io_addr + ICP_MULTI_INT_EN);
412 // Clear interrupt status
413 devpriv->IntStatus |= DAC_READY;
414 writew(devpriv->IntStatus,devpriv->io_addr + ICP_MULTI_INT_STAT);
416 // Clear data received
417 devpriv->ao_data[chan]=0;
419 #ifdef ICP_MULTI_EXTDEBUG
420 printk("icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n",n);
425 // Write data to analogue output data register
426 writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
428 // Set DAC_ST bit to write the data to selected channel
429 devpriv->DacCmdStatus |= DAC_ST;
430 writew(devpriv->DacCmdStatus, devpriv->io_addr+ICP_MULTI_DAC_CSR);
431 devpriv->DacCmdStatus &= ~DAC_ST;
433 // Save analogue output data
434 devpriv->ao_data[chan]=data[n];
437 #ifdef ICP_MULTI_EXTDEBUG
438 printk("icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n",n);
444 ==============================================================================
446 Name: icp_multi_insn_read_ao
449 This function reads a single analogue output.
452 comedi_device *dev Pointer to current device structure
453 comedi_subdevice *s Pointer to current subdevice structure
454 comedi_insn *insn Pointer to current comedi instruction
455 lsampl_t *data Pointer to analogue output data
457 Returns:int Nmuber of instructions executed
459 ==============================================================================
461 static int icp_multi_insn_read_ao(comedi_device * dev, comedi_subdevice * s, comedi_insn *insn, lsampl_t *data)
465 // Get channel number
466 chan = CR_CHAN(insn->chanspec);
468 // Read analogue outputs
469 for (n=0; n<insn->n; n++)
470 data[n]=devpriv->ao_data[chan];
476 ==============================================================================
478 Name: icp_multi_insn_bits_di
481 This function reads the digital inputs.
484 comedi_device *dev Pointer to current device structure
485 comedi_subdevice *s Pointer to current subdevice structure
486 comedi_insn *insn Pointer to current comedi instruction
487 lsampl_t *data Pointer to analogue output data
489 Returns:int Nmuber of instructions executed
491 ==============================================================================
493 static int icp_multi_insn_bits_di(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
495 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
501 ==============================================================================
503 Name: icp_multi_insn_bits_do
506 This function writes the appropriate digital outputs.
509 comedi_device *dev Pointer to current device structure
510 comedi_subdevice *s Pointer to current subdevice structure
511 comedi_insn *insn Pointer to current comedi instruction
512 lsampl_t *data Pointer to analogue output data
514 Returns:int Nmuber of instructions executed
516 ==============================================================================
518 static int icp_multi_insn_bits_do(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data)
520 #ifdef ICP_MULTI_EXTDEBUG
521 printk("icp multi EDBG: BGN: icp_multi_insn_bits_do(...)\n");
525 s->state &= ~data[0];
526 s->state |= (data[0] & data[1]);
528 printk("Digital outputs = %4x \n", s->state);
530 writew(s->state, devpriv->io_addr + ICP_MULTI_DO);
533 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
535 #ifdef ICP_MULTI_EXTDEBUG
536 printk("icp multi EDBG: END: icp_multi_insn_bits_do(...)\n");
543 ==============================================================================
545 Name: icp_multi_insn_read_ctr
548 This function reads the specified counter.
551 comedi_device *dev Pointer to current device structure
552 comedi_subdevice *s Pointer to current subdevice structure
553 comedi_insn *insn Pointer to current comedi instruction
554 lsampl_t *data Pointer to counter data
556 Returns:int Nmuber of instructions executed
558 ==============================================================================
560 static int icp_multi_insn_read_ctr(comedi_device * dev, comedi_subdevice * s, comedi_insn *insn, lsampl_t *data)
567 ==============================================================================
569 Name: icp_multi_insn_write_ctr
572 This function write to the specified counter.
575 comedi_device *dev Pointer to current device structure
576 comedi_subdevice *s Pointer to current subdevice structure
577 comedi_insn *insn Pointer to current comedi instruction
578 lsampl_t *data Pointer to counter data
580 Returns:int Nmuber of instructions executed
582 ==============================================================================
584 static int icp_multi_insn_write_ctr(comedi_device * dev, comedi_subdevice * s, comedi_insn *insn, lsampl_t *data)
592 ==============================================================================
594 Name: interrupt_service_icp_multi
597 This function is the interrupt service routine for all
598 interrupts generated by the icp multi board.
602 void *d Pointer to current device
604 ==============================================================================
606 static irqreturn_t interrupt_service_icp_multi(int irq, void *d PT_REGS_ARG)
608 comedi_device *dev = d;
611 #ifdef ICP_MULTI_EXTDEBUG
612 printk("icp multi EDBG: BGN: interrupt_service_icp_multi(%d,...)\n",irq);
615 // Is this interrupt from our board?
616 int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
621 #ifdef ICP_MULTI_EXTDEBUG
622 printk("icp multi EDBG: interrupt_service_icp_multi() ST: %4x\n",readw(devpriv->io_addr + ICP_MULTI_INT_STAT));
625 // Determine which interrupt is active & handle it
649 #ifdef ICP_MULTI_EXTDEBUG
650 printk("icp multi EDBG: END: interrupt_service_icp_multi(...)\n");
658 ==============================================================================
660 Name: check_channel_list
663 This function checks if the channel list, provided by user
667 comedi_device *dev Pointer to current sevice structure
668 comedi_subdevice *s Pointer to current subdevice structure
669 unsigned int *chanlist Pointer to packed channel list
670 unsigned int n_chan Number of channels to scan
672 Returns:int 0 = failure
675 ==============================================================================
677 static int check_channel_list(comedi_device * dev, comedi_subdevice * s, unsigned int *chanlist, unsigned int n_chan)
681 #ifdef ICP_MULTI_EXTDEBUG
682 printk("icp multi EDBG: check_channel_list(...,%d)\n",n_chan);
684 // Check that we at least have one channel to check
686 comedi_error(dev,"range/channel list is empty!");
690 // Check all channels
691 for (i=0; i<n_chan; i++) {
692 // Check that channel number is < maximum
693 if (CR_AREF(chanlist[i])==AREF_DIFF) {
694 if (CR_CHAN(chanlist[i]) > this_board->n_aichand) {
695 comedi_error(dev,"Incorrect differential ai channel number");
700 if (CR_CHAN(chanlist[i]) > this_board->n_aichan) {
701 comedi_error(dev,"Incorrect ai channel number");
713 ==============================================================================
715 Name: setup_channel_list
718 This function sets the appropriate channel selection,
719 differential input mode and range bits in the ADC Command/
723 comedi_device *dev Pointer to current sevice structure
724 comedi_subdevice *s Pointer to current subdevice structure
725 unsigned int *chanlist Pointer to packed channel list
726 unsigned int n_chan Number of channels to scan
730 ==============================================================================
732 static void setup_channel_list(comedi_device * dev, comedi_subdevice * s, unsigned int *chanlist,
735 unsigned int i, range, chanprog;
738 #ifdef ICP_MULTI_EXTDEBUG
739 printk("icp multi EDBG: setup_channel_list(...,%d)\n",n_chan);
741 devpriv->act_chanlist_len=n_chan;
742 devpriv->act_chanlist_pos=0;
744 for (i=0; i<n_chan; i++) {
746 chanprog=CR_CHAN(chanlist[i]);
748 // Determine if it is a differential channel (Bit 15 = 1)
749 if (CR_AREF(chanlist[i])==AREF_DIFF) {
758 // Clear channel, range and input mode bits in A/D command/status register
759 devpriv->AdcCmdStatus &= 0xf00f;
761 // Set channel number and differential mode status bit
763 // Set channel number, bits 9-11 & mode, bit 6
764 devpriv->AdcCmdStatus |= (chanprog << 9);
765 devpriv->AdcCmdStatus |= ADC_DI;
768 // Set channel number, bits 8-11
769 devpriv->AdcCmdStatus |= (chanprog << 8);
771 // Get range for current channel
772 range=this_board->rangecode[CR_RANGE(chanlist[i])];
773 // Set range. bits 4-5
774 devpriv->AdcCmdStatus |= range;
776 /* Output channel, range, mode to ICP Multi*/
777 writew(devpriv->AdcCmdStatus, devpriv->io_addr+ICP_MULTI_ADC_CSR);
779 #ifdef ICP_MULTI_EXTDEBUG
780 printk("GS: %2d. [%4x]=%4x %4x\n", i, chanprog, range, devpriv->act_chanlist[i]);
788 ==============================================================================
790 Name: icp_multi_reset
793 This function resets the icp multi device to a 'safe' state
796 comedi_device *dev Pointer to current sevice structure
798 Returns:int 0 = success
800 ==============================================================================
802 static int icp_multi_reset(comedi_device *dev)
806 #ifdef ICP_MULTI_EXTDEBUG
807 printk("icp_multi EDBG: BGN: icp_multi_reset(...)\n");
809 // Clear INT enables and requests
810 writew(0, devpriv->io_addr + ICP_MULTI_INT_EN);
811 writew(0x00ff, devpriv->io_addr + ICP_MULTI_INT_STAT);
813 if (this_board->n_aochan)
814 // Set DACs to 0..5V range and 0V output
815 for (i =0; i < this_board->n_aochan; i++) {
816 devpriv->DacCmdStatus &= 0xfcce;
818 // Set channel number
819 devpriv->DacCmdStatus |= (i << 8);
822 writew(0, devpriv->io_addr+ICP_MULTI_AO);
824 // Set start conversion bit
825 devpriv->DacCmdStatus |= DAC_ST;
827 // Output to command / status register
828 writew(devpriv->DacCmdStatus, devpriv->io_addr+ICP_MULTI_DAC_CSR);
830 // Delay to allow DAC time to recover
834 // Digital outputs to 0
835 writew(0, devpriv->io_addr + ICP_MULTI_DO);
837 #ifdef ICP_MULTI_EXTDEBUG
838 printk("icp multi EDBG: END: icp_multi_reset(...)\n");
844 ==============================================================================
846 Name: icp_multi_attach
849 This function sets up all the appropriate data for the current
853 comedi_device *dev Pointer to current device structure
854 comedi_devconfig *it Pointer to current device configuration
856 Returns:int 0 = success
858 ==============================================================================
860 static int icp_multi_attach(comedi_device *dev,comedi_devconfig *it)
863 int ret, subdev, n_subdevices;
864 unsigned short master;
866 struct pcilst_struct *card=NULL;
867 resource_size_t io_addr[5], iobase;
868 unsigned char pci_bus, pci_slot, pci_func;
870 printk("icp_multi EDBG: BGN: icp_multi_attach(...)\n");
872 // Alocate private data storage space
873 if ((ret=alloc_private(dev, sizeof(icp_multi_private)))<0)
876 // Initialise list of PCI cards in system, if not already done so
877 if (pci_list_builded++ == 0) {
878 pci_card_list_init(PCI_VENDOR_ID_ICP,
879 #ifdef ICP_MULTI_EXTDEBUG
887 printk("Anne's comedi%d: icp_multi: board=%s", dev->minor, this_board->name);
889 if ((card=select_and_alloc_pci_card(PCI_VENDOR_ID_ICP, this_board->device_id, it->options[0], it->options[1]))==NULL)
892 devpriv->card = card;
894 if ((pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0], &irq, &master))<0) {
895 printk(" - Can't get configuration data!\n");
900 devpriv->phys_iobase = iobase;
902 printk(", b:s:f=%d:%d:%d, io=0x%8llx \n", pci_bus, pci_slot, pci_func,
903 (unsigned long long)iobase);
905 devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
907 if (devpriv->io_addr == NULL) {
908 printk("ioremap failed.\n");
912 #ifdef ICP_MULTI_EXTDEBUG
913 printk("0x%08llx mapped to %p, ", (unsigned long long)iobase, devpriv->io_addr);
916 dev->board_name = this_board->name;
919 if (this_board->n_aichan) n_subdevices++;
920 if (this_board->n_aochan) n_subdevices++;
921 if (this_board->n_dichan) n_subdevices++;
922 if (this_board->n_dochan) n_subdevices++;
923 if (this_board->n_ctrs) n_subdevices++;
925 if((ret=alloc_subdevices(dev, n_subdevices))<0) {
929 if (this_board->have_irq) {
931 if (comedi_request_irq(irq, interrupt_service_icp_multi, IRQF_SHARED, "Inova Icp Multi", dev)) {
932 printk(", unable to allocate IRQ %u, DISABLING IT", irq);
933 irq=0; /* Can't use IRQ */
936 printk(", irq=%u", irq);
939 printk(", IRQ disabled");
950 if (this_board->n_aichan) {
951 s = dev->subdevices + subdev;
952 dev->read_subdev = s;
953 s->type = COMEDI_SUBD_AI;
954 s->subdev_flags = SDF_READABLE|SDF_COMMON|SDF_GROUND;
955 if (this_board->n_aichand)
956 s->subdev_flags |= SDF_DIFF;
957 s->n_chan = this_board->n_aichan;
958 s->maxdata = this_board->ai_maxdata;
959 s->len_chanlist = this_board->n_aichan;
960 s->range_table = this_board->rangelist_ai;
961 s->insn_read=icp_multi_insn_read_ai;
965 if (this_board->n_aochan) {
966 s = dev->subdevices + subdev;
967 s->type = COMEDI_SUBD_AO;
968 s->subdev_flags = SDF_WRITABLE|SDF_GROUND|SDF_COMMON;
969 s->n_chan = this_board->n_aochan;
970 s->maxdata = this_board->ao_maxdata;
971 s->len_chanlist = this_board->n_aochan;
972 s->range_table = this_board->rangelist_ao;
973 s->insn_write=icp_multi_insn_write_ao;
974 s->insn_read=icp_multi_insn_read_ao;
978 if (this_board->n_dichan) {
979 s = dev->subdevices + subdev;
980 s->type = COMEDI_SUBD_DI;
981 s->subdev_flags = SDF_READABLE;
982 s->n_chan = this_board->n_dichan;
984 s->len_chanlist = this_board->n_dichan;
985 s->range_table = &range_digital;
987 s->insn_bits=icp_multi_insn_bits_di;
991 if (this_board->n_dochan) {
992 s = dev->subdevices + subdev;
993 s->type = COMEDI_SUBD_DO;
994 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
995 s->n_chan = this_board->n_dochan;
997 s->len_chanlist = this_board->n_dochan;
998 s->range_table = &range_digital;
999 s->io_bits=(1 << this_board->n_dochan)-1;
1001 s->insn_bits=icp_multi_insn_bits_do;
1005 if (this_board->n_ctrs) {
1006 s = dev->subdevices + subdev;
1007 s->type = COMEDI_SUBD_COUNTER;
1008 s->subdev_flags = SDF_WRITABLE|SDF_GROUND|SDF_COMMON;
1009 s->n_chan = this_board->n_ctrs;
1010 s->maxdata = 0xffff;
1011 s->len_chanlist = this_board->n_ctrs;
1013 s->insn_read=icp_multi_insn_read_ctr;
1014 s->insn_write=icp_multi_insn_write_ctr;
1020 icp_multi_reset(dev);
1022 #ifdef ICP_MULTI_EXTDEBUG
1023 printk("icp multi EDBG: END: icp_multi_attach(...)\n");
1030 ==============================================================================
1032 Name: icp_multi_detach
1035 This function releases all the resources used by the current
1039 comedi_device *dev Pointer to current device structure
1041 Returns:int 0 = success
1043 ==============================================================================
1045 static int icp_multi_detach(comedi_device *dev)
1050 icp_multi_reset(dev);
1053 comedi_free_irq(dev->irq,dev);
1055 if (dev->private && devpriv->io_addr)
1056 iounmap(devpriv->io_addr);
1058 if (dev->private && devpriv->card)
1059 pci_card_free(devpriv->card);
1061 if (--pci_list_builded == 0) {
1062 pci_card_list_cleanup(PCI_VENDOR_ID_ICP);