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; /*=1 list of card is know */
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: boardtypes,
189 offset: sizeof(boardtype),
191 COMEDI_INITCLEANUP(driver_icp_multi);
194 char valid; // card is usable
195 void *io_addr; // Pointer to mapped io address
196 unsigned long phys_iobase; // Physical io address
197 unsigned int AdcCmdStatus; // ADC Command/Status register
198 unsigned int DacCmdStatus; // DAC Command/Status register
199 unsigned int IntEnable; // Interrupt Enable register
200 unsigned int IntStatus; // Interrupt Status register
201 unsigned int act_chanlist[32]; // list of scaned channel
202 unsigned char act_chanlist_len; // len of scanlist
203 unsigned char act_chanlist_pos; // actual position in MUX list
204 unsigned int *ai_chanlist; // actaul chanlist
205 sampl_t *ai_data; // data buffer
206 sampl_t ao_data[4]; // data output buffer
207 sampl_t di_data; // Digital input data
208 unsigned int do_data; // Remember digital output data
211 #define devpriv ((icp_multi_private *)dev->private)
212 #define this_board ((boardtype *)dev->board_ptr)
215 ==============================================================================
216 More forward declarations
217 ==============================================================================
221 static int check_channel_list(comedi_device * dev, comedi_subdevice * s, unsigned int *chanlist, unsigned int n_chan);
223 static void setup_channel_list(comedi_device * dev, comedi_subdevice * s, unsigned int *chanlist, unsigned int n_chan);
224 static int icp_multi_reset(comedi_device *dev);
228 ==============================================================================
230 ==============================================================================
235 ==============================================================================
237 Name: icp_multi_insn_read_ai
240 This function reads a single analogue input.
243 comedi_device *dev Pointer to current device structure
244 comedi_subdevice *s Pointer to current subdevice structure
245 comedi_insn *insn Pointer to current comedi instruction
246 lsampl_t *data Pointer to analogue input data
248 Returns:int Nmuber of instructions executed
250 ==============================================================================
252 static int icp_multi_insn_read_ai(comedi_device * dev, comedi_subdevice * s, comedi_insn *insn, lsampl_t *data)
256 #ifdef ICP_MULTI_EXTDEBUG
257 printk("icp multi EDBG: BGN: icp_multi_insn_read_ai(...)\n");
259 // Disable A/D conversion ready interrupt
260 devpriv->IntEnable &= ~ADC_READY;
261 writew(devpriv->IntEnable,dev->iobase + ICP_MULTI_INT_EN);
263 // Clear interrupt status
264 devpriv->IntStatus |= ADC_READY;
265 writew(devpriv->IntStatus,dev->iobase + ICP_MULTI_INT_STAT);
267 // Set up appropriate channel, mode and range data, for specified channel
268 setup_channel_list(dev, s, &insn->chanspec, 1);
270 #ifdef ICP_MULTI_EXTDEBUG
271 printk("icp_multi A ST=%4x IO=%x\n",readw(dev->iobase+ICP_MULTI_ADC_CSR), dev->iobase+ICP_MULTI_ADC_CSR);
274 for (n=0; n<insn->n; n++) {
276 devpriv->AdcCmdStatus |= ADC_ST;
277 writew(devpriv->AdcCmdStatus, dev->iobase+ICP_MULTI_ADC_CSR);
278 devpriv->AdcCmdStatus &= ~ADC_ST;
280 #ifdef ICP_MULTI_EXTDEBUG
281 printk("icp multi B n=%d ST=%4x\n",n,readw(dev->iobase+ICP_MULTI_ADC_CSR));
286 #ifdef ICP_MULTI_EXTDEBUG
287 printk("icp multi C n=%d ST=%4x\n",n,readw(dev->iobase+ICP_MULTI_ADC_CSR));
290 // Wait for conversion to complete, or get fed up waiting
293 if (!(readw(dev->iobase+ICP_MULTI_ADC_CSR) & ADC_BSY))
296 #ifdef ICP_MULTI_EXTDEBUG
298 printk("icp multi D n=%d tm=%d ST=%4x\n",n,timeout,readw(dev->iobase+ICP_MULTI_ADC_CSR));
304 // If we reach here, a timeout has occurred
305 comedi_error(dev,"A/D insn timeout");
308 devpriv->IntEnable &= ~ADC_READY;
309 writew(devpriv->IntEnable,dev->iobase + ICP_MULTI_INT_EN);
311 // Clear interrupt status
312 devpriv->IntStatus |= ADC_READY;
313 writew(devpriv->IntStatus,dev->iobase + ICP_MULTI_INT_STAT);
315 // Clear data received
318 #ifdef ICP_MULTI_EXTDEBUG
319 printk("icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n",n);
324 data[n] = (readw(dev->iobase+ICP_MULTI_AI) >> 4 ) & 0x0fff;
328 devpriv->IntEnable &= ~ADC_READY;
329 writew(devpriv->IntEnable,dev->iobase + ICP_MULTI_INT_EN);
331 // Clear interrupt status
332 devpriv->IntStatus |= ADC_READY;
333 writew(devpriv->IntStatus,dev->iobase + ICP_MULTI_INT_STAT);
335 #ifdef ICP_MULTI_EXTDEBUG
336 printk("icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n",n);
342 ==============================================================================
344 Name: icp_multi_insn_write_ao
347 This function writes a single analogue output.
350 comedi_device *dev Pointer to current device structure
351 comedi_subdevice *s Pointer to current subdevice structure
352 comedi_insn *insn Pointer to current comedi instruction
353 lsampl_t *data Pointer to analogue output data
355 Returns:int Nmuber of instructions executed
357 ==============================================================================
359 static int icp_multi_insn_write_ao(comedi_device * dev, comedi_subdevice * s, comedi_insn *insn, lsampl_t *data)
361 int n, chan, range, timeout;
363 #ifdef ICP_MULTI_EXTDEBUG
364 printk("icp multi EDBG: BGN: icp_multi_insn_write_ao(...)\n");
366 // Disable D/A conversion ready interrupt
367 devpriv->IntEnable &= ~DAC_READY;
368 writew(devpriv->IntEnable,dev->iobase + ICP_MULTI_INT_EN);
370 // Clear interrupt status
371 devpriv->IntStatus |= DAC_READY;
372 writew(devpriv->IntStatus,dev->iobase + ICP_MULTI_INT_STAT);
374 // Get channel number and range
375 chan = CR_CHAN(insn->chanspec);
376 range = CR_RANGE(insn->chanspec);
378 // Set up range and channel data
379 // Bit 4 = 1 : Bipolar
382 // Bits 8-9 : Channel number
383 devpriv->DacCmdStatus &= 0xfccf;
384 devpriv->DacCmdStatus |= this_board->rangecode[range];
385 devpriv->DacCmdStatus |= (chan << 8);
387 writew(devpriv->DacCmdStatus, dev->iobase+ICP_MULTI_DAC_CSR);
389 for (n=0; n<insn->n; n++) {
390 // Wait for analogue output data register to be ready for new data, or get fed up waiting
393 if (!(readw(dev->iobase+ICP_MULTI_DAC_CSR) & DAC_BSY))
396 #ifdef ICP_MULTI_EXTDEBUG
398 printk("icp multi A n=%d tm=%d ST=%4x\n",n,timeout,readw(dev->iobase+ICP_MULTI_DAC_CSR));
404 // If we reach here, a timeout has occurred
405 comedi_error(dev,"D/A insn timeout");
408 devpriv->IntEnable &= ~DAC_READY;
409 writew(devpriv->IntEnable,dev->iobase + ICP_MULTI_INT_EN);
411 // Clear interrupt status
412 devpriv->IntStatus |= DAC_READY;
413 writew(devpriv->IntStatus,dev->iobase + ICP_MULTI_INT_STAT);
415 // Clear data received
416 devpriv->ao_data[chan]=0;
418 #ifdef ICP_MULTI_EXTDEBUG
419 printk("icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n",n);
424 // Write data to analogue output data register
425 writew(data[n], dev->iobase + ICP_MULTI_AO);
427 // Set DAC_ST bit to write the data to selected channel
428 devpriv->DacCmdStatus |= DAC_ST;
429 writew(devpriv->DacCmdStatus, dev->iobase+ICP_MULTI_DAC_CSR);
430 devpriv->DacCmdStatus &= ~DAC_ST;
432 // Save analogue output data
433 devpriv->ao_data[chan]=data[n];
436 #ifdef ICP_MULTI_EXTDEBUG
437 printk("icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n",n);
443 ==============================================================================
445 Name: icp_multi_insn_read_ao
448 This function reads a single analogue output.
451 comedi_device *dev Pointer to current device structure
452 comedi_subdevice *s Pointer to current subdevice structure
453 comedi_insn *insn Pointer to current comedi instruction
454 lsampl_t *data Pointer to analogue output data
456 Returns:int Nmuber of instructions executed
458 ==============================================================================
460 static int icp_multi_insn_read_ao(comedi_device * dev, comedi_subdevice * s, comedi_insn *insn, lsampl_t *data)
464 // Get channel number
465 chan = CR_CHAN(insn->chanspec);
467 // Read analogue outputs
468 for (n=0; n<insn->n; n++)
469 data[n]=devpriv->ao_data[chan];
475 ==============================================================================
477 Name: icp_multi_insn_bits_di
480 This function reads the digital inputs.
483 comedi_device *dev Pointer to current device structure
484 comedi_subdevice *s Pointer to current subdevice structure
485 comedi_insn *insn Pointer to current comedi instruction
486 lsampl_t *data Pointer to analogue output data
488 Returns:int Nmuber of instructions executed
490 ==============================================================================
492 static int icp_multi_insn_bits_di(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
494 data[1] = readw(dev->iobase + ICP_MULTI_DI);
500 ==============================================================================
502 Name: icp_multi_insn_bits_do
505 This function writes the appropriate digital outputs.
508 comedi_device *dev Pointer to current device structure
509 comedi_subdevice *s Pointer to current subdevice structure
510 comedi_insn *insn Pointer to current comedi instruction
511 lsampl_t *data Pointer to analogue output data
513 Returns:int Nmuber of instructions executed
515 ==============================================================================
517 static int icp_multi_insn_bits_do(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data)
519 #ifdef ICP_MULTI_EXTDEBUG
520 printk("icp multi EDBG: BGN: icp_multi_insn_bits_do(...)\n");
524 s->state &= ~data[0];
525 s->state |= (data[0] & data[1]);
527 printk("Digital outputs = %4x \n", s->state);
529 writew(s->state, dev->iobase + ICP_MULTI_DO);
532 data[1] = readw(dev->iobase + ICP_MULTI_DI);
534 #ifdef ICP_MULTI_EXTDEBUG
535 printk("icp multi EDBG: END: icp_multi_insn_bits_do(...)\n");
542 ==============================================================================
544 Name: icp_multi_insn_read_ctr
547 This function reads the specified counter.
550 comedi_device *dev Pointer to current device structure
551 comedi_subdevice *s Pointer to current subdevice structure
552 comedi_insn *insn Pointer to current comedi instruction
553 lsampl_t *data Pointer to counter data
555 Returns:int Nmuber of instructions executed
557 ==============================================================================
559 static int icp_multi_insn_read_ctr(comedi_device * dev, comedi_subdevice * s, comedi_insn *insn, lsampl_t *data)
566 ==============================================================================
568 Name: icp_multi_insn_write_ctr
571 This function write to the specified counter.
574 comedi_device *dev Pointer to current device structure
575 comedi_subdevice *s Pointer to current subdevice structure
576 comedi_insn *insn Pointer to current comedi instruction
577 lsampl_t *data Pointer to counter data
579 Returns:int Nmuber of instructions executed
581 ==============================================================================
583 static int icp_multi_insn_write_ctr(comedi_device * dev, comedi_subdevice * s, comedi_insn *insn, lsampl_t *data)
591 ==============================================================================
593 Name: interrupt_service_icp_multi
596 This function is the interrupt service routine for all
597 interrupts generated by the icp multi board.
601 void *d Pointer to current device
602 struct pt_regs *regs Pointer to
604 ==============================================================================
606 static irqreturn_t interrupt_service_icp_multi(int irq, void *d, struct pt_regs *regs)
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(dev->iobase + ICP_MULTI_INT_STAT) & Status_IRQ;
621 #ifdef ICP_MULTI_EXTDEBUG
622 printk("icp multi EDBG: interrupt_service_icp_multi() ST: %4x\n",readw(dev->iobase + 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, dev->iobase+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, dev->iobase + ICP_MULTI_INT_EN);
811 writew(0x00ff, dev->iobase + 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, dev->iobase+ICP_MULTI_AO);
824 // Set start conversion bit
825 devpriv->DacCmdStatus |= DAC_ST;
827 // Output to command / status register
828 writew(devpriv->DacCmdStatus, dev->iobase+ICP_MULTI_DAC_CSR);
830 // Delay to allow DAC time to recover
834 // Digital outputs to 0
835 writew(0, dev->iobase + 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,irq;
865 struct pcilst_struct *card=NULL;
866 unsigned long io_addr[5], iobase;
867 unsigned char pci_bus, pci_slot, pci_func;
869 printk("icp_multi EDBG: BGN: icp_multi_attach(...)\n");
871 // Alocate private data storage space
872 if ((ret=alloc_private(dev, sizeof(icp_multi_private)))<0)
875 // Initialise list of PCI cards in system, if not already done so
876 if (!pci_list_builded) {
877 pci_card_list_init(PCI_VENDOR_ID_ICP,
878 #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 if ((pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0], &irq, &master))<0) {
894 printk(" - Can't get configuration data!\n");
900 // if(check_mem_region(iobase, ICP_MULTI_SIZE))
902 /* Couldn't allocate io space */
903 // printk(KERN_WARNING "couldn't allocate IO space\n");
906 // request_mem_region(iobase, ICP_MULTI_SIZE, "icp_multi");
907 devpriv->phys_iobase = iobase;
909 printk(", b:s:f=%d:%d:%d, io=0x%8lx \n", pci_bus, pci_slot, pci_func, iobase);
911 devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
913 if (devpriv->io_addr == NULL) {
914 printk("ioremap failed.\n");
918 #ifdef ICP_MULTI_EXTDEBUG
919 printk("0x%08lx mapped to %p, ", iobase, devpriv->io_addr);
922 dev->iobase = (int)devpriv->io_addr;
924 dev->board_name = this_board->name;
927 if (this_board->n_aichan) n_subdevices++;
928 if (this_board->n_aochan) n_subdevices++;
929 if (this_board->n_dichan) n_subdevices++;
930 if (this_board->n_dochan) n_subdevices++;
931 if (this_board->n_ctrs) n_subdevices++;
933 if((ret=alloc_subdevices(dev, n_subdevices))<0) {
938 if (this_board->have_irq) {
940 if (comedi_request_irq(irq, interrupt_service_icp_multi, SA_SHIRQ, "Inova Icp Multi", dev)) {
941 printk(", unable to allocate IRQ %d, DISABLING IT", irq);
942 irq=0; /* Can't use IRQ */
945 printk(", irq=%d", irq);
948 printk(", IRQ disabled");
959 if (this_board->n_aichan) {
960 s = dev->subdevices + subdev;
961 dev->read_subdev = s;
962 s->type = COMEDI_SUBD_AI;
963 s->subdev_flags = SDF_READABLE|SDF_COMMON|SDF_GROUND;
964 if (this_board->n_aichand)
965 s->subdev_flags |= SDF_DIFF;
966 s->n_chan = this_board->n_aichan;
967 s->maxdata = this_board->ai_maxdata;
968 s->len_chanlist = this_board->n_aichan;
969 s->range_table = this_board->rangelist_ai;
970 s->insn_read=icp_multi_insn_read_ai;
974 if (this_board->n_aochan) {
975 s = dev->subdevices + subdev;
976 s->type = COMEDI_SUBD_AO;
977 s->subdev_flags = SDF_WRITABLE|SDF_GROUND|SDF_COMMON;
978 s->n_chan = this_board->n_aochan;
979 s->maxdata = this_board->ao_maxdata;
980 s->len_chanlist = this_board->n_aochan;
981 s->range_table = this_board->rangelist_ao;
982 s->insn_write=icp_multi_insn_write_ao;
983 s->insn_read=icp_multi_insn_read_ao;
987 if (this_board->n_dichan) {
988 s = dev->subdevices + subdev;
989 s->type = COMEDI_SUBD_DI;
990 s->subdev_flags = SDF_READABLE;
991 s->n_chan = this_board->n_dichan;
993 s->len_chanlist = this_board->n_dichan;
994 s->range_table = &range_digital;
996 s->insn_bits=icp_multi_insn_bits_di;
1000 if (this_board->n_dochan) {
1001 s = dev->subdevices + subdev;
1002 s->type = COMEDI_SUBD_DO;
1003 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
1004 s->n_chan = this_board->n_dochan;
1006 s->len_chanlist = this_board->n_dochan;
1007 s->range_table = &range_digital;
1008 s->io_bits=(1 << this_board->n_dochan)-1;
1010 s->insn_bits=icp_multi_insn_bits_do;
1014 if (this_board->n_ctrs) {
1015 s = dev->subdevices + subdev;
1016 s->type = COMEDI_SUBD_COUNTER;
1017 s->subdev_flags = SDF_WRITABLE|SDF_GROUND|SDF_COMMON;
1018 s->n_chan = this_board->n_ctrs;
1019 s->maxdata = 0xffff;
1020 s->len_chanlist = this_board->n_ctrs;
1022 s->insn_read=icp_multi_insn_read_ctr;
1023 s->insn_write=icp_multi_insn_write_ctr;
1029 icp_multi_reset(dev);
1031 #ifdef ICP_MULTI_EXTDEBUG
1032 printk("icp multi EDBG: END: icp_multi_attach(...)\n");
1039 ==============================================================================
1041 Name: icp_multi_detach
1044 This function releases all the resources used by the current
1048 comedi_device *dev Pointer to current device structure
1050 Returns:int 0 = success
1052 ==============================================================================
1054 static int icp_multi_detach(comedi_device *dev)
1059 icp_multi_reset(dev);
1062 comedi_free_irq(dev->irq,dev);
1065 iounmap(devpriv->io_addr);
1066 // release_mem_region(iobase, ICP_MULTI_SIZE);
1069 if (pci_list_builded) {
1070 pci_card_list_cleanup(PCI_VENDOR_ID_ICP);