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.
25 Description: Inova ICP_MULTI
26 Author: Anne Smorthit <anne.smorthit@sfwte.ch>
27 Devices: [Inova] ICP_MULTI (icp_multi)
30 The driver works for analog input and output and digital input and output.
31 It does not work with interrupts or with the counters. Currently no support
34 It has 16 single-ended or 8 differential Analogue Input channels with 12-bit
35 resolution. Ranges : 5V, 10V, +/-5V, +/-10V, 0..20mA and 4..20mA. Input
36 ranges can be individually programmed for each channel. Voltage or current
37 measurement is selected by jumper.
39 There are 4 x 12-bit Analogue Outputs. Ranges : 5V, 10V, +/-5V, +/-10V
41 16 x Digital Inputs, 24V
43 8 x Digital Outputs, 24V, 1A
48 [0] - PCI bus number - if bus number and slot number are 0,
49 then driver search for first unused card
53 #include <linux/interrupt.h>
54 #include "../comedidev.h"
56 #include <linux/delay.h>
57 #include <linux/pci.h>
59 #include "icp_multi.h"
61 #define PCI_DEVICE_ID_ICP_MULTI 0x8000
63 /* Hardware types of the cards */
64 #define TYPE_ICP_MULTI 0
66 #define IORANGE_ICP_MULTI 32
68 #define ICP_MULTI_ADC_CSR 0 /* R/W: ADC command/status register */
69 #define ICP_MULTI_AI 2 /* R: Analogue input data */
70 #define ICP_MULTI_DAC_CSR 4 /* R/W: DAC command/status register */
71 #define ICP_MULTI_AO 6 /* R/W: Analogue output data */
72 #define ICP_MULTI_DI 8 /* R/W: Digital inouts */
73 #define ICP_MULTI_DO 0x0A /* R/W: Digital outputs */
74 #define ICP_MULTI_INT_EN 0x0C /* R/W: Interrupt enable register */
75 #define ICP_MULTI_INT_STAT 0x0E /* R/W: Interrupt status register */
76 #define ICP_MULTI_CNTR0 0x10 /* R/W: Counter 0 */
77 #define ICP_MULTI_CNTR1 0x12 /* R/W: counter 1 */
78 #define ICP_MULTI_CNTR2 0x14 /* R/W: Counter 2 */
79 #define ICP_MULTI_CNTR3 0x16 /* R/W: Counter 3 */
81 #define ICP_MULTI_SIZE 0x20 /* 32 bytes */
83 /* Define bits from ADC command/status register */
84 #define ADC_ST 0x0001 /* Start ADC */
85 #define ADC_BSY 0x0001 /* ADC busy */
86 #define ADC_BI 0x0010 /* Bipolar input range 1 = bipolar */
87 #define ADC_RA 0x0020 /* Input range 0 = 5V, 1 = 10V */
88 #define ADC_DI 0x0040 /* Differential input mode 1 = differential */
90 /* Define bits from DAC command/status register */
91 #define DAC_ST 0x0001 /* Start DAC */
92 #define DAC_BSY 0x0001 /* DAC busy */
93 #define DAC_BI 0x0010 /* Bipolar input range 1 = bipolar */
94 #define DAC_RA 0x0020 /* Input range 0 = 5V, 1 = 10V */
96 /* Define bits from interrupt enable/status registers */
97 #define ADC_READY 0x0001 /* A/d conversion ready interrupt */
98 #define DAC_READY 0x0002 /* D/a conversion ready interrupt */
99 #define DOUT_ERROR 0x0004 /* Digital output error interrupt */
100 #define DIN_STATUS 0x0008 /* Digital input status change interrupt */
101 #define CIE0 0x0010 /* Counter 0 overrun interrupt */
102 #define CIE1 0x0020 /* Counter 1 overrun interrupt */
103 #define CIE2 0x0040 /* Counter 2 overrun interrupt */
104 #define CIE3 0x0080 /* Counter 3 overrun interrupt */
106 /* Useful definitions */
107 #define Status_IRQ 0x00ff /* All interrupts */
109 /* Define analogue range */
110 static const struct comedi_lrange range_analog = { 4, {
118 static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 };
121 ==============================================================================
122 Data & Structure declarations
123 ==============================================================================
125 static unsigned short pci_list_builded; /*>0 list of card is known */
128 const char *name; /* driver name */
130 int iorange; /* I/O range len */
131 char have_irq; /* 1=card support IRQ */
132 char cardtype; /* 0=ICP Multi */
133 const struct comedi_lrange *rangelist_ai; /* rangelist for A/D */
134 const char *rangecode; /* range codes for programming */
137 struct icp_multi_private {
138 struct pcilst_struct *card; /* pointer to card */
139 char valid; /* card is usable */
140 void __iomem *io_addr; /* Pointer to mapped io address */
141 resource_size_t phys_iobase; /* Physical io address */
142 unsigned int AdcCmdStatus; /* ADC Command/Status register */
143 unsigned int DacCmdStatus; /* DAC Command/Status register */
144 unsigned int IntEnable; /* Interrupt Enable register */
145 unsigned int IntStatus; /* Interrupt Status register */
146 unsigned int act_chanlist[32]; /* list of scaned channel */
147 unsigned char act_chanlist_len; /* len of scanlist */
148 unsigned char act_chanlist_pos; /* actual position in MUX list */
149 unsigned int *ai_chanlist; /* actaul chanlist */
150 short *ai_data; /* data buffer */
151 short ao_data[4]; /* data output buffer */
152 short di_data; /* Digital input data */
153 unsigned int do_data; /* Remember digital output data */
156 #define devpriv ((struct icp_multi_private *)dev->private)
157 #define this_board ((const struct boardtype *)dev->board_ptr)
160 ==============================================================================
162 Name: setup_channel_list
165 This function sets the appropriate channel selection,
166 differential input mode and range bits in the ADC Command/
170 struct comedi_device *dev Pointer to current service structure
171 struct comedi_subdevice *s Pointer to current subdevice structure
172 unsigned int *chanlist Pointer to packed channel list
173 unsigned int n_chan Number of channels to scan
177 ==============================================================================
179 static void setup_channel_list(struct comedi_device *dev,
180 struct comedi_subdevice *s,
181 unsigned int *chanlist, unsigned int n_chan)
183 unsigned int i, range, chanprog;
186 devpriv->act_chanlist_len = n_chan;
187 devpriv->act_chanlist_pos = 0;
189 for (i = 0; i < n_chan; i++) {
191 chanprog = CR_CHAN(chanlist[i]);
193 /* Determine if it is a differential channel (Bit 15 = 1) */
194 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
202 /* Clear channel, range and input mode bits
203 * in A/D command/status register */
204 devpriv->AdcCmdStatus &= 0xf00f;
206 /* Set channel number and differential mode status bit */
208 /* Set channel number, bits 9-11 & mode, bit 6 */
209 devpriv->AdcCmdStatus |= (chanprog << 9);
210 devpriv->AdcCmdStatus |= ADC_DI;
212 /* Set channel number, bits 8-11 */
213 devpriv->AdcCmdStatus |= (chanprog << 8);
215 /* Get range for current channel */
216 range = this_board->rangecode[CR_RANGE(chanlist[i])];
217 /* Set range. bits 4-5 */
218 devpriv->AdcCmdStatus |= range;
220 /* Output channel, range, mode to ICP Multi */
221 writew(devpriv->AdcCmdStatus,
222 devpriv->io_addr + ICP_MULTI_ADC_CSR);
227 ==============================================================================
229 Name: icp_multi_insn_read_ai
232 This function reads a single analogue input.
235 struct comedi_device *dev Pointer to current device structure
236 struct comedi_subdevice *s Pointer to current subdevice structure
237 struct comedi_insn *insn Pointer to current comedi instruction
238 unsigned int *data Pointer to analogue input data
240 Returns:int Nmuber of instructions executed
242 ==============================================================================
244 static int icp_multi_insn_read_ai(struct comedi_device *dev,
245 struct comedi_subdevice *s,
246 struct comedi_insn *insn, unsigned int *data)
250 /* Disable A/D conversion ready interrupt */
251 devpriv->IntEnable &= ~ADC_READY;
252 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
254 /* Clear interrupt status */
255 devpriv->IntStatus |= ADC_READY;
256 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
258 /* Set up appropriate channel, mode and range data, for specified ch */
259 setup_channel_list(dev, s, &insn->chanspec, 1);
261 for (n = 0; n < insn->n; n++) {
262 /* Set start ADC bit */
263 devpriv->AdcCmdStatus |= ADC_ST;
264 writew(devpriv->AdcCmdStatus,
265 devpriv->io_addr + ICP_MULTI_ADC_CSR);
266 devpriv->AdcCmdStatus &= ~ADC_ST;
270 /* Wait for conversion to complete, or get fed up waiting */
273 if (!(readw(devpriv->io_addr +
274 ICP_MULTI_ADC_CSR) & ADC_BSY))
280 /* If we reach here, a timeout has occurred */
281 comedi_error(dev, "A/D insn timeout");
283 /* Disable interrupt */
284 devpriv->IntEnable &= ~ADC_READY;
285 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
287 /* Clear interrupt status */
288 devpriv->IntStatus |= ADC_READY;
289 writew(devpriv->IntStatus,
290 devpriv->io_addr + ICP_MULTI_INT_STAT);
292 /* Clear data received */
299 (readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff;
302 /* Disable interrupt */
303 devpriv->IntEnable &= ~ADC_READY;
304 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
306 /* Clear interrupt status */
307 devpriv->IntStatus |= ADC_READY;
308 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
314 ==============================================================================
316 Name: icp_multi_insn_write_ao
319 This function writes a single analogue output.
322 struct comedi_device *dev Pointer to current device structure
323 struct comedi_subdevice *s Pointer to current subdevice structure
324 struct comedi_insn *insn Pointer to current comedi instruction
325 unsigned int *data Pointer to analogue output data
327 Returns:int Nmuber of instructions executed
329 ==============================================================================
331 static int icp_multi_insn_write_ao(struct comedi_device *dev,
332 struct comedi_subdevice *s,
333 struct comedi_insn *insn, unsigned int *data)
335 int n, chan, range, timeout;
337 /* Disable D/A conversion ready interrupt */
338 devpriv->IntEnable &= ~DAC_READY;
339 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
341 /* Clear interrupt status */
342 devpriv->IntStatus |= DAC_READY;
343 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
345 /* Get channel number and range */
346 chan = CR_CHAN(insn->chanspec);
347 range = CR_RANGE(insn->chanspec);
349 /* Set up range and channel data */
350 /* Bit 4 = 1 : Bipolar */
352 /* Bit 5 = 1 : 10V */
353 /* Bits 8-9 : Channel number */
354 devpriv->DacCmdStatus &= 0xfccf;
355 devpriv->DacCmdStatus |= this_board->rangecode[range];
356 devpriv->DacCmdStatus |= (chan << 8);
358 writew(devpriv->DacCmdStatus, devpriv->io_addr + ICP_MULTI_DAC_CSR);
360 for (n = 0; n < insn->n; n++) {
361 /* Wait for analogue output data register to be
362 * ready for new data, or get fed up waiting */
365 if (!(readw(devpriv->io_addr +
366 ICP_MULTI_DAC_CSR) & DAC_BSY))
372 /* If we reach here, a timeout has occurred */
373 comedi_error(dev, "D/A insn timeout");
375 /* Disable interrupt */
376 devpriv->IntEnable &= ~DAC_READY;
377 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
379 /* Clear interrupt status */
380 devpriv->IntStatus |= DAC_READY;
381 writew(devpriv->IntStatus,
382 devpriv->io_addr + ICP_MULTI_INT_STAT);
384 /* Clear data received */
385 devpriv->ao_data[chan] = 0;
390 /* Write data to analogue output data register */
391 writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
393 /* Set DAC_ST bit to write the data to selected channel */
394 devpriv->DacCmdStatus |= DAC_ST;
395 writew(devpriv->DacCmdStatus,
396 devpriv->io_addr + ICP_MULTI_DAC_CSR);
397 devpriv->DacCmdStatus &= ~DAC_ST;
399 /* Save analogue output data */
400 devpriv->ao_data[chan] = data[n];
407 ==============================================================================
409 Name: icp_multi_insn_read_ao
412 This function reads a single analogue output.
415 struct comedi_device *dev Pointer to current device structure
416 struct comedi_subdevice *s Pointer to current subdevice structure
417 struct comedi_insn *insn Pointer to current comedi instruction
418 unsigned int *data Pointer to analogue output data
420 Returns:int Nmuber of instructions executed
422 ==============================================================================
424 static int icp_multi_insn_read_ao(struct comedi_device *dev,
425 struct comedi_subdevice *s,
426 struct comedi_insn *insn, unsigned int *data)
430 /* Get channel number */
431 chan = CR_CHAN(insn->chanspec);
433 /* Read analogue outputs */
434 for (n = 0; n < insn->n; n++)
435 data[n] = devpriv->ao_data[chan];
441 ==============================================================================
443 Name: icp_multi_insn_bits_di
446 This function reads the digital inputs.
449 struct comedi_device *dev Pointer to current device structure
450 struct comedi_subdevice *s Pointer to current subdevice structure
451 struct comedi_insn *insn Pointer to current comedi instruction
452 unsigned int *data Pointer to analogue output data
454 Returns:int Nmuber of instructions executed
456 ==============================================================================
458 static int icp_multi_insn_bits_di(struct comedi_device *dev,
459 struct comedi_subdevice *s,
460 struct comedi_insn *insn, unsigned int *data)
462 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
468 ==============================================================================
470 Name: icp_multi_insn_bits_do
473 This function writes the appropriate digital outputs.
476 struct comedi_device *dev Pointer to current device structure
477 struct comedi_subdevice *s Pointer to current subdevice structure
478 struct comedi_insn *insn Pointer to current comedi instruction
479 unsigned int *data Pointer to analogue output data
481 Returns:int Nmuber of instructions executed
483 ==============================================================================
485 static int icp_multi_insn_bits_do(struct comedi_device *dev,
486 struct comedi_subdevice *s,
487 struct comedi_insn *insn, unsigned int *data)
490 s->state &= ~data[0];
491 s->state |= (data[0] & data[1]);
493 printk(KERN_DEBUG "Digital outputs = %4x \n", s->state);
495 writew(s->state, devpriv->io_addr + ICP_MULTI_DO);
498 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
504 ==============================================================================
506 Name: icp_multi_insn_read_ctr
509 This function reads the specified counter.
512 struct comedi_device *dev Pointer to current device structure
513 struct comedi_subdevice *s Pointer to current subdevice structure
514 struct comedi_insn *insn Pointer to current comedi instruction
515 unsigned int *data Pointer to counter data
517 Returns:int Nmuber of instructions executed
519 ==============================================================================
521 static int icp_multi_insn_read_ctr(struct comedi_device *dev,
522 struct comedi_subdevice *s,
523 struct comedi_insn *insn, unsigned int *data)
529 ==============================================================================
531 Name: icp_multi_insn_write_ctr
534 This function write to the specified counter.
537 struct comedi_device *dev Pointer to current device structure
538 struct comedi_subdevice *s Pointer to current subdevice structure
539 struct comedi_insn *insn Pointer to current comedi instruction
540 unsigned int *data Pointer to counter data
542 Returns:int Nmuber of instructions executed
544 ==============================================================================
546 static int icp_multi_insn_write_ctr(struct comedi_device *dev,
547 struct comedi_subdevice *s,
548 struct comedi_insn *insn,
555 ==============================================================================
557 Name: interrupt_service_icp_multi
560 This function is the interrupt service routine for all
561 interrupts generated by the icp multi board.
565 void *d Pointer to current device
567 ==============================================================================
569 static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
571 struct comedi_device *dev = d;
574 /* Is this interrupt from our board? */
575 int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
580 /* Determine which interrupt is active & handle it */
608 ==============================================================================
610 Name: check_channel_list
613 This function checks if the channel list, provided by user
617 struct comedi_device *dev Pointer to current service structure
618 struct comedi_subdevice *s Pointer to current subdevice structure
619 unsigned int *chanlist Pointer to packed channel list
620 unsigned int n_chan Number of channels to scan
622 Returns:int 0 = failure
625 ==============================================================================
627 static int check_channel_list(struct comedi_device *dev,
628 struct comedi_subdevice *s,
629 unsigned int *chanlist, unsigned int n_chan)
633 /* Check that we at least have one channel to check */
635 comedi_error(dev, "range/channel list is empty!");
638 /* Check all channels */
639 for (i = 0; i < n_chan; i++) {
640 /* Check that channel number is < maximum */
641 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
642 if (CR_CHAN(chanlist[i]) > (s->nchan / 2)) {
644 "Incorrect differential ai ch-nr");
648 if (CR_CHAN(chanlist[i]) > s->n_chan) {
650 "Incorrect ai channel number");
660 ==============================================================================
662 Name: icp_multi_reset
665 This function resets the icp multi device to a 'safe' state
668 struct comedi_device *dev Pointer to current service structure
670 Returns:int 0 = success
672 ==============================================================================
674 static int icp_multi_reset(struct comedi_device *dev)
678 /* Clear INT enables and requests */
679 writew(0, devpriv->io_addr + ICP_MULTI_INT_EN);
680 writew(0x00ff, devpriv->io_addr + ICP_MULTI_INT_STAT);
682 /* Set DACs to 0..5V range and 0V output */
683 for (i = 0; i < 4; i++) {
684 devpriv->DacCmdStatus &= 0xfcce;
686 /* Set channel number */
687 devpriv->DacCmdStatus |= (i << 8);
690 writew(0, devpriv->io_addr + ICP_MULTI_AO);
692 /* Set start conversion bit */
693 devpriv->DacCmdStatus |= DAC_ST;
695 /* Output to command / status register */
696 writew(devpriv->DacCmdStatus,
697 devpriv->io_addr + ICP_MULTI_DAC_CSR);
699 /* Delay to allow DAC time to recover */
703 /* Digital outputs to 0 */
704 writew(0, devpriv->io_addr + ICP_MULTI_DO);
709 static int icp_multi_attach(struct comedi_device *dev,
710 struct comedi_devconfig *it)
712 struct comedi_subdevice *s;
713 int ret, subdev, n_subdevices;
715 struct pcilst_struct *card = NULL;
716 resource_size_t io_addr[5], iobase;
717 unsigned char pci_bus, pci_slot, pci_func;
720 "icp_multi EDBG: BGN: icp_multi_attach(...)\n");
722 /* Allocate private data storage space */
723 ret = alloc_private(dev, sizeof(struct icp_multi_private));
727 /* Initialise list of PCI cards in system, if not already done so */
728 if (pci_list_builded++ == 0)
729 pci_card_list_init(PCI_VENDOR_ID_ICP, 0);
732 "Anne's comedi%d: icp_multi: board=%s", dev->minor,
735 card = select_and_alloc_pci_card(PCI_VENDOR_ID_ICP,
736 this_board->device_id, it->options[0],
742 devpriv->card = card;
744 if ((pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0],
746 printk(KERN_WARNING " - Can't get configuration data!\n");
751 devpriv->phys_iobase = iobase;
754 ", b:s:f=%d:%d:%d, io=0x%8llx \n", pci_bus, pci_slot, pci_func,
755 (unsigned long long)iobase);
757 devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
759 if (devpriv->io_addr == NULL) {
760 printk(KERN_WARNING "ioremap failed.\n");
764 dev->board_name = this_board->name;
773 ret = comedi_alloc_subdevices(dev, n_subdevices);
777 icp_multi_reset(dev);
779 if (this_board->have_irq) {
781 if (request_irq(irq, interrupt_service_icp_multi,
782 IRQF_SHARED, "Inova Icp Multi", dev)) {
784 "unable to allocate IRQ %u, DISABLING IT",
786 irq = 0; /* Can't use IRQ */
788 printk(KERN_WARNING ", irq=%u", irq);
790 printk(KERN_WARNING ", IRQ disabled");
796 printk(KERN_WARNING ".\n");
800 s = &dev->subdevices[subdev];
801 dev->read_subdev = s;
802 s->type = COMEDI_SUBD_AI;
803 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
806 s->len_chanlist = 16;
807 s->range_table = this_board->rangelist_ai;
808 s->insn_read = icp_multi_insn_read_ai;
811 s = &dev->subdevices[subdev];
812 s->type = COMEDI_SUBD_AO;
813 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
817 s->range_table = &range_analog;
818 s->insn_write = icp_multi_insn_write_ao;
819 s->insn_read = icp_multi_insn_read_ao;
822 s = &dev->subdevices[subdev];
823 s->type = COMEDI_SUBD_DI;
824 s->subdev_flags = SDF_READABLE;
827 s->len_chanlist = 16;
828 s->range_table = &range_digital;
830 s->insn_bits = icp_multi_insn_bits_di;
833 s = &dev->subdevices[subdev];
834 s->type = COMEDI_SUBD_DO;
835 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
839 s->range_table = &range_digital;
842 s->insn_bits = icp_multi_insn_bits_do;
845 s = &dev->subdevices[subdev];
846 s->type = COMEDI_SUBD_COUNTER;
847 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
852 s->insn_read = icp_multi_insn_read_ctr;
853 s->insn_write = icp_multi_insn_write_ctr;
861 static void icp_multi_detach(struct comedi_device *dev)
865 icp_multi_reset(dev);
867 free_irq(dev->irq, dev);
868 if (dev->private && devpriv->io_addr)
869 iounmap(devpriv->io_addr);
870 if (dev->private && devpriv->card)
871 pci_card_free(devpriv->card);
872 if (--pci_list_builded == 0)
873 pci_card_list_cleanup(PCI_VENDOR_ID_ICP);
876 static const struct boardtype boardtypes[] = {
879 .device_id = PCI_DEVICE_ID_ICP_MULTI,
880 .iorange = IORANGE_ICP_MULTI,
882 .cardtype = TYPE_ICP_MULTI,
883 .rangelist_ai = &range_analog,
884 .rangecode = range_codes_analog,
888 static struct comedi_driver icp_multi_driver = {
889 .driver_name = "icp_multi",
890 .module = THIS_MODULE,
891 .attach = icp_multi_attach,
892 .detach = icp_multi_detach,
893 .num_names = ARRAY_SIZE(boardtypes),
894 .board_name = &boardtypes[0].name,
895 .offset = sizeof(struct boardtype),
898 static int __devinit icp_multi_pci_probe(struct pci_dev *dev,
899 const struct pci_device_id *ent)
901 return comedi_pci_auto_config(dev, &icp_multi_driver);
904 static void __devexit icp_multi_pci_remove(struct pci_dev *dev)
906 comedi_pci_auto_unconfig(dev);
909 static DEFINE_PCI_DEVICE_TABLE(icp_multi_pci_table) = {
910 { PCI_DEVICE(PCI_VENDOR_ID_ICP, PCI_DEVICE_ID_ICP_MULTI) },
913 MODULE_DEVICE_TABLE(pci, icp_multi_pci_table);
915 static struct pci_driver icp_multi_pci_driver = {
917 .id_table = icp_multi_pci_table,
918 .probe = icp_multi_pci_probe,
919 .remove = __devexit_p(icp_multi_pci_remove),
921 module_comedi_pci_driver(icp_multi_driver, icp_multi_pci_driver);
923 MODULE_AUTHOR("Comedi http://www.comedi.org");
924 MODULE_DESCRIPTION("Comedi low-level driver");
925 MODULE_LICENSE("GPL");