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 */
135 const struct comedi_lrange *rangelist_ao; /* rangelist for D/A */
138 struct icp_multi_private {
139 struct pcilst_struct *card; /* pointer to card */
140 char valid; /* card is usable */
141 void __iomem *io_addr; /* Pointer to mapped io address */
142 resource_size_t phys_iobase; /* Physical io address */
143 unsigned int AdcCmdStatus; /* ADC Command/Status register */
144 unsigned int DacCmdStatus; /* DAC Command/Status register */
145 unsigned int IntEnable; /* Interrupt Enable register */
146 unsigned int IntStatus; /* Interrupt Status register */
147 unsigned int act_chanlist[32]; /* list of scaned channel */
148 unsigned char act_chanlist_len; /* len of scanlist */
149 unsigned char act_chanlist_pos; /* actual position in MUX list */
150 unsigned int *ai_chanlist; /* actaul chanlist */
151 short *ai_data; /* data buffer */
152 short ao_data[4]; /* data output buffer */
153 short di_data; /* Digital input data */
154 unsigned int do_data; /* Remember digital output data */
157 #define devpriv ((struct icp_multi_private *)dev->private)
158 #define this_board ((const struct boardtype *)dev->board_ptr)
161 ==============================================================================
163 Name: setup_channel_list
166 This function sets the appropriate channel selection,
167 differential input mode and range bits in the ADC Command/
171 struct comedi_device *dev Pointer to current service structure
172 struct comedi_subdevice *s Pointer to current subdevice structure
173 unsigned int *chanlist Pointer to packed channel list
174 unsigned int n_chan Number of channels to scan
178 ==============================================================================
180 static void setup_channel_list(struct comedi_device *dev,
181 struct comedi_subdevice *s,
182 unsigned int *chanlist, unsigned int n_chan)
184 unsigned int i, range, chanprog;
187 devpriv->act_chanlist_len = n_chan;
188 devpriv->act_chanlist_pos = 0;
190 for (i = 0; i < n_chan; i++) {
192 chanprog = CR_CHAN(chanlist[i]);
194 /* Determine if it is a differential channel (Bit 15 = 1) */
195 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
203 /* Clear channel, range and input mode bits
204 * in A/D command/status register */
205 devpriv->AdcCmdStatus &= 0xf00f;
207 /* Set channel number and differential mode status bit */
209 /* Set channel number, bits 9-11 & mode, bit 6 */
210 devpriv->AdcCmdStatus |= (chanprog << 9);
211 devpriv->AdcCmdStatus |= ADC_DI;
213 /* Set channel number, bits 8-11 */
214 devpriv->AdcCmdStatus |= (chanprog << 8);
216 /* Get range for current channel */
217 range = this_board->rangecode[CR_RANGE(chanlist[i])];
218 /* Set range. bits 4-5 */
219 devpriv->AdcCmdStatus |= range;
221 /* Output channel, range, mode to ICP Multi */
222 writew(devpriv->AdcCmdStatus,
223 devpriv->io_addr + ICP_MULTI_ADC_CSR);
228 ==============================================================================
230 Name: icp_multi_insn_read_ai
233 This function reads a single analogue input.
236 struct comedi_device *dev Pointer to current device structure
237 struct comedi_subdevice *s Pointer to current subdevice structure
238 struct comedi_insn *insn Pointer to current comedi instruction
239 unsigned int *data Pointer to analogue input data
241 Returns:int Nmuber of instructions executed
243 ==============================================================================
245 static int icp_multi_insn_read_ai(struct comedi_device *dev,
246 struct comedi_subdevice *s,
247 struct comedi_insn *insn, unsigned int *data)
251 /* Disable A/D conversion ready interrupt */
252 devpriv->IntEnable &= ~ADC_READY;
253 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
255 /* Clear interrupt status */
256 devpriv->IntStatus |= ADC_READY;
257 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
259 /* Set up appropriate channel, mode and range data, for specified ch */
260 setup_channel_list(dev, s, &insn->chanspec, 1);
262 for (n = 0; n < insn->n; n++) {
263 /* Set start ADC bit */
264 devpriv->AdcCmdStatus |= ADC_ST;
265 writew(devpriv->AdcCmdStatus,
266 devpriv->io_addr + ICP_MULTI_ADC_CSR);
267 devpriv->AdcCmdStatus &= ~ADC_ST;
271 /* Wait for conversion to complete, or get fed up waiting */
274 if (!(readw(devpriv->io_addr +
275 ICP_MULTI_ADC_CSR) & ADC_BSY))
281 /* If we reach here, a timeout has occurred */
282 comedi_error(dev, "A/D insn timeout");
284 /* Disable interrupt */
285 devpriv->IntEnable &= ~ADC_READY;
286 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
288 /* Clear interrupt status */
289 devpriv->IntStatus |= ADC_READY;
290 writew(devpriv->IntStatus,
291 devpriv->io_addr + ICP_MULTI_INT_STAT);
293 /* Clear data received */
300 (readw(devpriv->io_addr + ICP_MULTI_AI) >> 4) & 0x0fff;
303 /* Disable interrupt */
304 devpriv->IntEnable &= ~ADC_READY;
305 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
307 /* Clear interrupt status */
308 devpriv->IntStatus |= ADC_READY;
309 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
315 ==============================================================================
317 Name: icp_multi_insn_write_ao
320 This function writes a single analogue output.
323 struct comedi_device *dev Pointer to current device structure
324 struct comedi_subdevice *s Pointer to current subdevice structure
325 struct comedi_insn *insn Pointer to current comedi instruction
326 unsigned int *data Pointer to analogue output data
328 Returns:int Nmuber of instructions executed
330 ==============================================================================
332 static int icp_multi_insn_write_ao(struct comedi_device *dev,
333 struct comedi_subdevice *s,
334 struct comedi_insn *insn, unsigned int *data)
336 int n, chan, range, timeout;
338 /* Disable D/A conversion ready interrupt */
339 devpriv->IntEnable &= ~DAC_READY;
340 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
342 /* Clear interrupt status */
343 devpriv->IntStatus |= DAC_READY;
344 writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
346 /* Get channel number and range */
347 chan = CR_CHAN(insn->chanspec);
348 range = CR_RANGE(insn->chanspec);
350 /* Set up range and channel data */
351 /* Bit 4 = 1 : Bipolar */
353 /* Bit 5 = 1 : 10V */
354 /* Bits 8-9 : Channel number */
355 devpriv->DacCmdStatus &= 0xfccf;
356 devpriv->DacCmdStatus |= this_board->rangecode[range];
357 devpriv->DacCmdStatus |= (chan << 8);
359 writew(devpriv->DacCmdStatus, devpriv->io_addr + ICP_MULTI_DAC_CSR);
361 for (n = 0; n < insn->n; n++) {
362 /* Wait for analogue output data register to be
363 * ready for new data, or get fed up waiting */
366 if (!(readw(devpriv->io_addr +
367 ICP_MULTI_DAC_CSR) & DAC_BSY))
373 /* If we reach here, a timeout has occurred */
374 comedi_error(dev, "D/A insn timeout");
376 /* Disable interrupt */
377 devpriv->IntEnable &= ~DAC_READY;
378 writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
380 /* Clear interrupt status */
381 devpriv->IntStatus |= DAC_READY;
382 writew(devpriv->IntStatus,
383 devpriv->io_addr + ICP_MULTI_INT_STAT);
385 /* Clear data received */
386 devpriv->ao_data[chan] = 0;
391 /* Write data to analogue output data register */
392 writew(data[n], devpriv->io_addr + ICP_MULTI_AO);
394 /* Set DAC_ST bit to write the data to selected channel */
395 devpriv->DacCmdStatus |= DAC_ST;
396 writew(devpriv->DacCmdStatus,
397 devpriv->io_addr + ICP_MULTI_DAC_CSR);
398 devpriv->DacCmdStatus &= ~DAC_ST;
400 /* Save analogue output data */
401 devpriv->ao_data[chan] = data[n];
408 ==============================================================================
410 Name: icp_multi_insn_read_ao
413 This function reads a single analogue output.
416 struct comedi_device *dev Pointer to current device structure
417 struct comedi_subdevice *s Pointer to current subdevice structure
418 struct comedi_insn *insn Pointer to current comedi instruction
419 unsigned int *data Pointer to analogue output data
421 Returns:int Nmuber of instructions executed
423 ==============================================================================
425 static int icp_multi_insn_read_ao(struct comedi_device *dev,
426 struct comedi_subdevice *s,
427 struct comedi_insn *insn, unsigned int *data)
431 /* Get channel number */
432 chan = CR_CHAN(insn->chanspec);
434 /* Read analogue outputs */
435 for (n = 0; n < insn->n; n++)
436 data[n] = devpriv->ao_data[chan];
442 ==============================================================================
444 Name: icp_multi_insn_bits_di
447 This function reads the digital inputs.
450 struct comedi_device *dev Pointer to current device structure
451 struct comedi_subdevice *s Pointer to current subdevice structure
452 struct comedi_insn *insn Pointer to current comedi instruction
453 unsigned int *data Pointer to analogue output data
455 Returns:int Nmuber of instructions executed
457 ==============================================================================
459 static int icp_multi_insn_bits_di(struct comedi_device *dev,
460 struct comedi_subdevice *s,
461 struct comedi_insn *insn, unsigned int *data)
463 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
469 ==============================================================================
471 Name: icp_multi_insn_bits_do
474 This function writes the appropriate digital outputs.
477 struct comedi_device *dev Pointer to current device structure
478 struct comedi_subdevice *s Pointer to current subdevice structure
479 struct comedi_insn *insn Pointer to current comedi instruction
480 unsigned int *data Pointer to analogue output data
482 Returns:int Nmuber of instructions executed
484 ==============================================================================
486 static int icp_multi_insn_bits_do(struct comedi_device *dev,
487 struct comedi_subdevice *s,
488 struct comedi_insn *insn, unsigned int *data)
491 s->state &= ~data[0];
492 s->state |= (data[0] & data[1]);
494 printk(KERN_DEBUG "Digital outputs = %4x \n", s->state);
496 writew(s->state, devpriv->io_addr + ICP_MULTI_DO);
499 data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
505 ==============================================================================
507 Name: icp_multi_insn_read_ctr
510 This function reads the specified counter.
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
518 Returns:int Nmuber of instructions executed
520 ==============================================================================
522 static int icp_multi_insn_read_ctr(struct comedi_device *dev,
523 struct comedi_subdevice *s,
524 struct comedi_insn *insn, unsigned int *data)
530 ==============================================================================
532 Name: icp_multi_insn_write_ctr
535 This function write to the specified counter.
538 struct comedi_device *dev Pointer to current device structure
539 struct comedi_subdevice *s Pointer to current subdevice structure
540 struct comedi_insn *insn Pointer to current comedi instruction
541 unsigned int *data Pointer to counter data
543 Returns:int Nmuber of instructions executed
545 ==============================================================================
547 static int icp_multi_insn_write_ctr(struct comedi_device *dev,
548 struct comedi_subdevice *s,
549 struct comedi_insn *insn,
556 ==============================================================================
558 Name: interrupt_service_icp_multi
561 This function is the interrupt service routine for all
562 interrupts generated by the icp multi board.
566 void *d Pointer to current device
568 ==============================================================================
570 static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
572 struct comedi_device *dev = d;
575 /* Is this interrupt from our board? */
576 int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
581 /* Determine which interrupt is active & handle it */
609 ==============================================================================
611 Name: check_channel_list
614 This function checks if the channel list, provided by user
618 struct comedi_device *dev Pointer to current service structure
619 struct comedi_subdevice *s Pointer to current subdevice structure
620 unsigned int *chanlist Pointer to packed channel list
621 unsigned int n_chan Number of channels to scan
623 Returns:int 0 = failure
626 ==============================================================================
628 static int check_channel_list(struct comedi_device *dev,
629 struct comedi_subdevice *s,
630 unsigned int *chanlist, unsigned int n_chan)
634 /* Check that we at least have one channel to check */
636 comedi_error(dev, "range/channel list is empty!");
639 /* Check all channels */
640 for (i = 0; i < n_chan; i++) {
641 /* Check that channel number is < maximum */
642 if (CR_AREF(chanlist[i]) == AREF_DIFF) {
643 if (CR_CHAN(chanlist[i]) > (s->nchan / 2)) {
645 "Incorrect differential ai ch-nr");
649 if (CR_CHAN(chanlist[i]) > s->n_chan) {
651 "Incorrect ai channel number");
661 ==============================================================================
663 Name: icp_multi_reset
666 This function resets the icp multi device to a 'safe' state
669 struct comedi_device *dev Pointer to current service structure
671 Returns:int 0 = success
673 ==============================================================================
675 static int icp_multi_reset(struct comedi_device *dev)
679 /* Clear INT enables and requests */
680 writew(0, devpriv->io_addr + ICP_MULTI_INT_EN);
681 writew(0x00ff, devpriv->io_addr + ICP_MULTI_INT_STAT);
683 /* Set DACs to 0..5V range and 0V output */
684 for (i = 0; i < 4; i++) {
685 devpriv->DacCmdStatus &= 0xfcce;
687 /* Set channel number */
688 devpriv->DacCmdStatus |= (i << 8);
691 writew(0, devpriv->io_addr + ICP_MULTI_AO);
693 /* Set start conversion bit */
694 devpriv->DacCmdStatus |= DAC_ST;
696 /* Output to command / status register */
697 writew(devpriv->DacCmdStatus,
698 devpriv->io_addr + ICP_MULTI_DAC_CSR);
700 /* Delay to allow DAC time to recover */
704 /* Digital outputs to 0 */
705 writew(0, devpriv->io_addr + ICP_MULTI_DO);
710 static int icp_multi_attach(struct comedi_device *dev,
711 struct comedi_devconfig *it)
713 struct comedi_subdevice *s;
714 int ret, subdev, n_subdevices;
716 struct pcilst_struct *card = NULL;
717 resource_size_t io_addr[5], iobase;
718 unsigned char pci_bus, pci_slot, pci_func;
721 "icp_multi EDBG: BGN: icp_multi_attach(...)\n");
723 /* Allocate private data storage space */
724 ret = alloc_private(dev, sizeof(struct icp_multi_private));
728 /* Initialise list of PCI cards in system, if not already done so */
729 if (pci_list_builded++ == 0)
730 pci_card_list_init(PCI_VENDOR_ID_ICP, 0);
733 "Anne's comedi%d: icp_multi: board=%s", dev->minor,
736 card = select_and_alloc_pci_card(PCI_VENDOR_ID_ICP,
737 this_board->device_id, it->options[0],
743 devpriv->card = card;
745 if ((pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0],
747 printk(KERN_WARNING " - Can't get configuration data!\n");
752 devpriv->phys_iobase = iobase;
755 ", b:s:f=%d:%d:%d, io=0x%8llx \n", pci_bus, pci_slot, pci_func,
756 (unsigned long long)iobase);
758 devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
760 if (devpriv->io_addr == NULL) {
761 printk(KERN_WARNING "ioremap failed.\n");
765 dev->board_name = this_board->name;
774 ret = comedi_alloc_subdevices(dev, n_subdevices);
778 icp_multi_reset(dev);
780 if (this_board->have_irq) {
782 if (request_irq(irq, interrupt_service_icp_multi,
783 IRQF_SHARED, "Inova Icp Multi", dev)) {
785 "unable to allocate IRQ %u, DISABLING IT",
787 irq = 0; /* Can't use IRQ */
789 printk(KERN_WARNING ", irq=%u", irq);
791 printk(KERN_WARNING ", IRQ disabled");
797 printk(KERN_WARNING ".\n");
801 s = &dev->subdevices[subdev];
802 dev->read_subdev = s;
803 s->type = COMEDI_SUBD_AI;
804 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
807 s->len_chanlist = 16;
808 s->range_table = this_board->rangelist_ai;
809 s->insn_read = icp_multi_insn_read_ai;
812 s = &dev->subdevices[subdev];
813 s->type = COMEDI_SUBD_AO;
814 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
818 s->range_table = &range_analog;
819 s->insn_write = icp_multi_insn_write_ao;
820 s->insn_read = icp_multi_insn_read_ao;
823 s = &dev->subdevices[subdev];
824 s->type = COMEDI_SUBD_DI;
825 s->subdev_flags = SDF_READABLE;
828 s->len_chanlist = 16;
829 s->range_table = &range_digital;
831 s->insn_bits = icp_multi_insn_bits_di;
834 s = &dev->subdevices[subdev];
835 s->type = COMEDI_SUBD_DO;
836 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
840 s->range_table = &range_digital;
843 s->insn_bits = icp_multi_insn_bits_do;
846 s = &dev->subdevices[subdev];
847 s->type = COMEDI_SUBD_COUNTER;
848 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
853 s->insn_read = icp_multi_insn_read_ctr;
854 s->insn_write = icp_multi_insn_write_ctr;
862 static void icp_multi_detach(struct comedi_device *dev)
866 icp_multi_reset(dev);
868 free_irq(dev->irq, dev);
869 if (dev->private && devpriv->io_addr)
870 iounmap(devpriv->io_addr);
871 if (dev->private && devpriv->card)
872 pci_card_free(devpriv->card);
873 if (--pci_list_builded == 0)
874 pci_card_list_cleanup(PCI_VENDOR_ID_ICP);
877 static const struct boardtype boardtypes[] = {
880 .device_id = PCI_DEVICE_ID_ICP_MULTI,
881 .iorange = IORANGE_ICP_MULTI,
883 .cardtype = TYPE_ICP_MULTI,
884 .rangelist_ai = &range_analog,
885 .rangecode = range_codes_analog,
886 .rangelist_ao = &range_analog,
890 static struct comedi_driver icp_multi_driver = {
891 .driver_name = "icp_multi",
892 .module = THIS_MODULE,
893 .attach = icp_multi_attach,
894 .detach = icp_multi_detach,
895 .num_names = ARRAY_SIZE(boardtypes),
896 .board_name = &boardtypes[0].name,
897 .offset = sizeof(struct boardtype),
900 static int __devinit icp_multi_pci_probe(struct pci_dev *dev,
901 const struct pci_device_id *ent)
903 return comedi_pci_auto_config(dev, &icp_multi_driver);
906 static void __devexit icp_multi_pci_remove(struct pci_dev *dev)
908 comedi_pci_auto_unconfig(dev);
911 static DEFINE_PCI_DEVICE_TABLE(icp_multi_pci_table) = {
912 { PCI_DEVICE(PCI_VENDOR_ID_ICP, PCI_DEVICE_ID_ICP_MULTI) },
915 MODULE_DEVICE_TABLE(pci, icp_multi_pci_table);
917 static struct pci_driver icp_multi_pci_driver = {
919 .id_table = icp_multi_pci_table,
920 .probe = icp_multi_pci_probe,
921 .remove = __devexit_p(icp_multi_pci_remove),
923 module_comedi_pci_driver(icp_multi_driver, icp_multi_pci_driver);
925 MODULE_AUTHOR("Comedi http://www.comedi.org");
926 MODULE_DESCRIPTION("Comedi low-level driver");
927 MODULE_LICENSE("GPL");