adv_pci_dio.c revision c3744138715045adb316284ee7a1e608f0278f6c
1/*
2 * comedi/drivers/adv_pci_dio.c
3 *
4 * Author: Michal Dobes <dobes@tesnet.cz>
5 *
6 *  Hardware driver for Advantech PCI DIO cards.
7*/
8/*
9Driver: adv_pci_dio
10Description: Advantech PCI-1730, PCI-1733, PCI-1734, PCI-1736UP,
11             PCI-1750, PCI-1751, PCI-1752, PCI-1753/E, PCI-1754,
12             PCI-1756, PCI-1762
13Author: Michal Dobes <dobes@tesnet.cz>
14Devices: [Advantech] PCI-1730 (adv_pci_dio), PCI-1733,
15  PCI-1734, PCI-1736UP, PCI-1750,
16  PCI-1751, PCI-1752, PCI-1753,
17  PCI-1753+PCI-1753E, PCI-1754, PCI-1756,
18  PCI-1760, PCI-1762
19Status: untested
20Updated: Mon, 14 Apr 2008 10:43:08 +0100
21
22This driver supports now only insn interface for DI/DO/DIO.
23
24Configuration options:
25  [0] - PCI bus of device (optional)
26  [1] - PCI slot of device (optional)
27          If bus/slot is not specified, the first available PCI
28          device will be used.
29
30*/
31
32#include "../comedidev.h"
33
34#include <linux/delay.h>
35
36#include "comedi_pci.h"
37#include "8255.h"
38
39#undef PCI_DIO_EXTDEBUG		/* if defined, enable extensive debug logging */
40
41#undef DPRINTK
42#ifdef PCI_DIO_EXTDEBUG
43#define DPRINTK(fmt, args...) rt_printk(fmt, ## args)
44#else
45#define DPRINTK(fmt, args...)
46#endif
47
48/* hardware types of the cards */
49enum hw_cards_id {
50	TYPE_PCI1730, TYPE_PCI1733, TYPE_PCI1734, TYPE_PCI1736,
51	TYPE_PCI1750,
52	TYPE_PCI1751,
53	TYPE_PCI1752,
54	TYPE_PCI1753, TYPE_PCI1753E,
55	TYPE_PCI1754, TYPE_PCI1756,
56	TYPE_PCI1760,
57	TYPE_PCI1762
58};
59
60/* which I/O instructions to use */
61enum hw_io_access {
62	IO_8b, IO_16b
63};
64
65#define MAX_DI_SUBDEVS	2	/* max number of DI subdevices per card */
66#define MAX_DO_SUBDEVS	2	/* max number of DO subdevices per card */
67#define MAX_DIO_SUBDEVG	2	/* max number of DIO subdevices group per card */
68
69#define SIZE_8255	   4	/* 8255 IO space length */
70
71#define PCIDIO_MAINREG	   2	/* main I/O region for all Advantech cards? */
72
73/* Register offset definitions */
74/*  Advantech PCI-1730/3/4 */
75#define PCI1730_IDI	   0	/* R:   Isolated digital input  0-15 */
76#define PCI1730_IDO	   0	/* W:   Isolated digital output 0-15 */
77#define PCI1730_DI	   2	/* R:   Digital input  0-15 */
78#define PCI1730_DO	   2	/* W:   Digital output 0-15 */
79#define PCI1733_IDI	   0	/* R:   Isolated digital input  0-31 */
80#define	PCI1730_3_INT_EN	0x08	/* R/W: enable/disable interrupts */
81#define	PCI1730_3_INT_RF	0x0c	/* R/W: set falling/raising edge for interrupts */
82#define	PCI1730_3_INT_CLR	0x10	/* R/W: clear interrupts */
83#define PCI1734_IDO	   0	/* W:   Isolated digital output 0-31 */
84#define PCI173x_BOARDID	   4	/* R:   Board I/D switch for 1730/3/4 */
85
86/*  Advantech PCI-1736UP */
87#define PCI1736_IDI        0    /* R:   Isolated digital input  0-15 */
88#define PCI1736_IDO        0    /* W:   Isolated digital output 0-15 */
89#define PCI1736_3_INT_EN        0x08    /* R/W: enable/disable interrupts */
90#define PCI1736_3_INT_RF        0x0c    /* R/W: set falling/raising edge for interrupts */
91#define PCI1736_3_INT_CLR       0x10    /* R/W: clear interrupts */
92#define PCI1736_BOARDID    4            /* R:   Board I/D switch for 1736UP */
93#define PCI1736_MAINREG    0            /* Normal register (2) doesn't work */
94
95/*  Advantech PCI-1750 */
96#define PCI1750_IDI	   0	/* R:   Isolated digital input  0-15 */
97#define PCI1750_IDO	   0	/* W:   Isolated digital output 0-15 */
98#define PCI1750_ICR	  32	/* W:   Interrupt control register */
99#define PCI1750_ISR	  32	/* R:   Interrupt status register */
100
101/*  Advantech PCI-1751/3/3E */
102#define PCI1751_DIO	   0	/* R/W: begin of 8255 registers block */
103#define PCI1751_ICR	  32	/* W:   Interrupt control register */
104#define PCI1751_ISR	  32	/* R:   Interrupt status register */
105#define PCI1753_DIO	   0	/* R/W: begin of 8255 registers block */
106#define PCI1753_ICR0	  16	/* R/W: Interrupt control register group 0 */
107#define PCI1753_ICR1	  17	/* R/W: Interrupt control register group 1 */
108#define PCI1753_ICR2	  18	/* R/W: Interrupt control register group 2 */
109#define PCI1753_ICR3	  19	/* R/W: Interrupt control register group 3 */
110#define PCI1753E_DIO	  32	/* R/W: begin of 8255 registers block */
111#define PCI1753E_ICR0	  48	/* R/W: Interrupt control register group 0 */
112#define PCI1753E_ICR1	  49	/* R/W: Interrupt control register group 1 */
113#define PCI1753E_ICR2	  50	/* R/W: Interrupt control register group 2 */
114#define PCI1753E_ICR3	  51	/* R/W: Interrupt control register group 3 */
115
116/*  Advantech PCI-1752/4/6 */
117#define PCI1752_IDO	   0	/* R/W: Digital output  0-31 */
118#define PCI1752_IDO2	   4	/* R/W: Digital output 32-63 */
119#define PCI1754_IDI	   0	/* R:   Digital input   0-31 */
120#define PCI1754_IDI2	   4	/* R:   Digital input  32-64 */
121#define PCI1756_IDI	   0	/* R:   Digital input   0-31 */
122#define PCI1756_IDO	   4	/* R/W: Digital output  0-31 */
123#define PCI1754_6_ICR0	0x08	/* R/W: Interrupt control register group 0 */
124#define PCI1754_6_ICR1	0x0a	/* R/W: Interrupt control register group 1 */
125#define PCI1754_ICR2	0x0c	/* R/W: Interrupt control register group 2 */
126#define PCI1754_ICR3	0x0e	/* R/W: Interrupt control register group 3 */
127#define PCI1752_6_CFC	0x12	/* R/W: set/read channel freeze function */
128#define PCI175x_BOARDID	0x10	/* R:   Board I/D switch for 1752/4/6 */
129
130/*  Advantech PCI-1762 registers */
131#define PCI1762_RO	   0	/* R/W: Relays status/output */
132#define PCI1762_IDI	   2	/* R:   Isolated input status */
133#define PCI1762_BOARDID	   4	/* R:   Board I/D switch */
134#define PCI1762_ICR	   6	/* W:   Interrupt control register */
135#define PCI1762_ISR	   6	/* R:   Interrupt status register */
136
137/*  Advantech PCI-1760 registers */
138#define OMB0		0x0c	/* W:   Mailbox outgoing registers */
139#define OMB1		0x0d
140#define OMB2		0x0e
141#define OMB3		0x0f
142#define IMB0		0x1c	/* R:   Mailbox incoming registers */
143#define IMB1		0x1d
144#define IMB2		0x1e
145#define IMB3		0x1f
146#define INTCSR0		0x38	/* R/W: Interrupt control registers */
147#define INTCSR1		0x39
148#define INTCSR2		0x3a
149#define INTCSR3		0x3b
150
151/*  PCI-1760 mailbox commands */
152#define CMD_ClearIMB2		0x00	/* Clear IMB2 status and return actaul DI status in IMB3 */
153#define CMD_SetRelaysOutput	0x01	/* Set relay output from OMB0 */
154#define CMD_GetRelaysStatus	0x02	/* Get relay status to IMB0 */
155#define CMD_ReadCurrentStatus	0x07	/* Read the current status of the register in OMB0, result in IMB0 */
156#define CMD_ReadFirmwareVersion	0x0e	/* Read the firmware ver., result in IMB1.IMB0 */
157#define CMD_ReadHardwareVersion	0x0f	/* Read the hardware ver., result in IMB1.IMB0 */
158#define CMD_EnableIDIFilters	0x20	/* Enable IDI filters based on bits in OMB0 */
159#define CMD_EnableIDIPatternMatch 0x21	/* Enable IDI pattern match based on bits in OMB0 */
160#define CMD_SetIDIPatternMatch	0x22	/* Enable IDI pattern match based on bits in OMB0 */
161#define CMD_EnableIDICounters	0x28	/* Enable IDI counters based on bits in OMB0 */
162#define CMD_ResetIDICounters	0x29	/* Reset IDI counters based on bits in OMB0 to its reset values */
163#define CMD_OverflowIDICounters	0x2a	/* Enable IDI counters overflow interrupts  based on bits in OMB0 */
164#define CMD_MatchIntIDICounters	0x2b	/* Enable IDI counters match value interrupts  based on bits in OMB0 */
165#define CMD_EdgeIDICounters	0x2c	/* Set IDI up counters count edge (bit=0 - rising, =1 - falling) */
166#define CMD_GetIDICntCurValue	0x2f	/* Read IDI{OMB0} up counter current value */
167#define CMD_SetIDI0CntResetValue 0x40	/* Set IDI0 Counter Reset Value 256*OMB1+OMB0 */
168#define CMD_SetIDI1CntResetValue 0x41	/* Set IDI1 Counter Reset Value 256*OMB1+OMB0 */
169#define CMD_SetIDI2CntResetValue 0x42	/* Set IDI2 Counter Reset Value 256*OMB1+OMB0 */
170#define CMD_SetIDI3CntResetValue 0x43	/* Set IDI3 Counter Reset Value 256*OMB1+OMB0 */
171#define CMD_SetIDI4CntResetValue 0x44	/* Set IDI4 Counter Reset Value 256*OMB1+OMB0 */
172#define CMD_SetIDI5CntResetValue 0x45	/* Set IDI5 Counter Reset Value 256*OMB1+OMB0 */
173#define CMD_SetIDI6CntResetValue 0x46	/* Set IDI6 Counter Reset Value 256*OMB1+OMB0 */
174#define CMD_SetIDI7CntResetValue 0x47	/* Set IDI7 Counter Reset Value 256*OMB1+OMB0 */
175#define CMD_SetIDI0CntMatchValue 0x48	/* Set IDI0 Counter Match Value 256*OMB1+OMB0 */
176#define CMD_SetIDI1CntMatchValue 0x49	/* Set IDI1 Counter Match Value 256*OMB1+OMB0 */
177#define CMD_SetIDI2CntMatchValue 0x4a	/* Set IDI2 Counter Match Value 256*OMB1+OMB0 */
178#define CMD_SetIDI3CntMatchValue 0x4b	/* Set IDI3 Counter Match Value 256*OMB1+OMB0 */
179#define CMD_SetIDI4CntMatchValue 0x4c	/* Set IDI4 Counter Match Value 256*OMB1+OMB0 */
180#define CMD_SetIDI5CntMatchValue 0x4d	/* Set IDI5 Counter Match Value 256*OMB1+OMB0 */
181#define CMD_SetIDI6CntMatchValue 0x4e	/* Set IDI6 Counter Match Value 256*OMB1+OMB0 */
182#define CMD_SetIDI7CntMatchValue 0x4f	/* Set IDI7 Counter Match Value 256*OMB1+OMB0 */
183
184#define OMBCMD_RETRY	0x03	/* 3 times try request before error */
185
186static int pci_dio_attach(struct comedi_device *dev, struct comedi_devconfig *it);
187static int pci_dio_detach(struct comedi_device *dev);
188
189struct diosubd_data {
190	int chans;		/*  num of chans */
191	int addr;		/*  PCI address ofset */
192	int regs;		/*  number of registers to read or 8255 subdevices */
193	unsigned int specflags;	/*  addon subdevice flags */
194};
195
196struct dio_boardtype {
197	const char *name;	/*  board name */
198	int vendor_id;		/*  vendor/device PCI ID */
199	int device_id;
200	int main_pci_region;	/*  main I/O PCI region */
201	enum hw_cards_id cardtype;
202	struct diosubd_data sdi[MAX_DI_SUBDEVS];	/*  DI chans */
203	struct diosubd_data sdo[MAX_DO_SUBDEVS];	/*  DO chans */
204	struct diosubd_data sdio[MAX_DIO_SUBDEVG];	/*  DIO 8255 chans */
205	struct diosubd_data boardid;	/*  card supports board ID switch */
206	enum hw_io_access io_access;
207};
208
209static DEFINE_PCI_DEVICE_TABLE(pci_dio_pci_table) = {
210	{PCI_VENDOR_ID_ADVANTECH, 0x1730, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
211	{PCI_VENDOR_ID_ADVANTECH, 0x1733, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
212	{PCI_VENDOR_ID_ADVANTECH, 0x1734, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
213	{PCI_VENDOR_ID_ADVANTECH, 0x1736, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
214	{PCI_VENDOR_ID_ADVANTECH, 0x1750, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
215	{PCI_VENDOR_ID_ADVANTECH, 0x1751, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
216	{PCI_VENDOR_ID_ADVANTECH, 0x1752, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
217	{PCI_VENDOR_ID_ADVANTECH, 0x1753, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
218	{PCI_VENDOR_ID_ADVANTECH, 0x1754, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
219	{PCI_VENDOR_ID_ADVANTECH, 0x1756, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
220	{PCI_VENDOR_ID_ADVANTECH, 0x1760, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
221	{PCI_VENDOR_ID_ADVANTECH, 0x1762, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
222	{0}
223};
224
225MODULE_DEVICE_TABLE(pci, pci_dio_pci_table);
226
227static const struct dio_boardtype boardtypes[] = {
228	{"pci1730", PCI_VENDOR_ID_ADVANTECH, 0x1730, PCIDIO_MAINREG,
229			TYPE_PCI1730,
230			{{16, PCI1730_DI, 2, 0}, {16, PCI1730_IDI, 2, 0}},
231			{{16, PCI1730_DO, 2, 0}, {16, PCI1730_IDO, 2, 0}},
232			{{0, 0, 0, 0}, {0, 0, 0, 0}},
233			{4, PCI173x_BOARDID, 1, SDF_INTERNAL},
234			IO_8b,
235		},
236	{"pci1733", PCI_VENDOR_ID_ADVANTECH, 0x1733, PCIDIO_MAINREG,
237			TYPE_PCI1733,
238			{{0, 0, 0, 0}, {32, PCI1733_IDI, 4, 0}},
239			{{0, 0, 0, 0}, {0, 0, 0, 0}},
240			{{0, 0, 0, 0}, {0, 0, 0, 0}},
241			{4, PCI173x_BOARDID, 1, SDF_INTERNAL},
242		IO_8b},
243	{"pci1734", PCI_VENDOR_ID_ADVANTECH, 0x1734, PCIDIO_MAINREG,
244			TYPE_PCI1734,
245			{{0, 0, 0, 0}, {0, 0, 0, 0}},
246			{{0, 0, 0, 0}, {32, PCI1734_IDO, 4, 0}},
247			{{0, 0, 0, 0}, {0, 0, 0, 0}},
248			{4, PCI173x_BOARDID, 1, SDF_INTERNAL},
249		IO_8b},
250	{"pci1736", PCI_VENDOR_ID_ADVANTECH, 0x1736, PCI1736_MAINREG,
251			TYPE_PCI1736,
252			{{0, 0, 0, 0}, {16, PCI1736_IDI, 2, 0}},
253			{{0, 0, 0, 0}, {16, PCI1736_IDO, 2, 0}},
254			{{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
255			{ 4, PCI1736_BOARDID, 1, SDF_INTERNAL},
256			IO_8b,
257        },
258	{"pci1750", PCI_VENDOR_ID_ADVANTECH, 0x1750, PCIDIO_MAINREG,
259			TYPE_PCI1750,
260			{{0, 0, 0, 0}, {16, PCI1750_IDI, 2, 0}},
261			{{0, 0, 0, 0}, {16, PCI1750_IDO, 2, 0}},
262			{{0, 0, 0, 0}, {0, 0, 0, 0}},
263			{0, 0, 0, 0},
264		IO_8b},
265	{"pci1751", PCI_VENDOR_ID_ADVANTECH, 0x1751, PCIDIO_MAINREG,
266			TYPE_PCI1751,
267			{{0, 0, 0, 0}, {0, 0, 0, 0}},
268			{{0, 0, 0, 0}, {0, 0, 0, 0}},
269			{{48, PCI1751_DIO, 2, 0}, {0, 0, 0, 0}},
270			{0, 0, 0, 0},
271		IO_8b},
272	{"pci1752", PCI_VENDOR_ID_ADVANTECH, 0x1752, PCIDIO_MAINREG,
273			TYPE_PCI1752,
274			{{0, 0, 0, 0}, {0, 0, 0, 0}},
275			{{32, PCI1752_IDO, 2, 0}, {32, PCI1752_IDO2, 2, 0}},
276			{{0, 0, 0, 0}, {0, 0, 0, 0}},
277			{4, PCI175x_BOARDID, 1, SDF_INTERNAL},
278		IO_16b},
279	{"pci1753", PCI_VENDOR_ID_ADVANTECH, 0x1753, PCIDIO_MAINREG,
280			TYPE_PCI1753,
281			{{0, 0, 0, 0}, {0, 0, 0, 0}},
282			{{0, 0, 0, 0}, {0, 0, 0, 0}},
283			{{96, PCI1753_DIO, 4, 0}, {0, 0, 0, 0}},
284			{0, 0, 0, 0},
285		IO_8b},
286	{"pci1753e", PCI_VENDOR_ID_ADVANTECH, 0x1753, PCIDIO_MAINREG,
287			TYPE_PCI1753E,
288			{{0, 0, 0, 0}, {0, 0, 0, 0}},
289			{{0, 0, 0, 0}, {0, 0, 0, 0}},
290			{{96, PCI1753_DIO, 4, 0}, {96, PCI1753E_DIO, 4, 0}},
291			{0, 0, 0, 0},
292		IO_8b},
293	{"pci1754", PCI_VENDOR_ID_ADVANTECH, 0x1754, PCIDIO_MAINREG,
294			TYPE_PCI1754,
295			{{32, PCI1754_IDI, 2, 0}, {32, PCI1754_IDI2, 2, 0}},
296			{{0, 0, 0, 0}, {0, 0, 0, 0}},
297			{{0, 0, 0, 0}, {0, 0, 0, 0}},
298			{4, PCI175x_BOARDID, 1, SDF_INTERNAL},
299		IO_16b},
300	{"pci1756", PCI_VENDOR_ID_ADVANTECH, 0x1756, PCIDIO_MAINREG,
301			TYPE_PCI1756,
302			{{0, 0, 0, 0}, {32, PCI1756_IDI, 2, 0}},
303			{{0, 0, 0, 0}, {32, PCI1756_IDO, 2, 0}},
304			{{0, 0, 0, 0}, {0, 0, 0, 0}},
305			{4, PCI175x_BOARDID, 1, SDF_INTERNAL},
306		IO_16b},
307	{"pci1760", PCI_VENDOR_ID_ADVANTECH, 0x1760, 0,
308			TYPE_PCI1760,
309			{{0, 0, 0, 0}, {0, 0, 0, 0}},	/*  This card have own setup work */
310			{{0, 0, 0, 0}, {0, 0, 0, 0}},
311			{{0, 0, 0, 0}, {0, 0, 0, 0}},
312			{0, 0, 0, 0},
313		IO_8b},
314	{"pci1762", PCI_VENDOR_ID_ADVANTECH, 0x1762, PCIDIO_MAINREG,
315			TYPE_PCI1762,
316			{{0, 0, 0, 0}, {16, PCI1762_IDI, 1, 0}},
317			{{0, 0, 0, 0}, {16, PCI1762_RO, 1, 0}},
318			{{0, 0, 0, 0}, {0, 0, 0, 0}},
319			{4, PCI1762_BOARDID, 1, SDF_INTERNAL},
320		IO_16b}
321};
322
323#define n_boardtypes (sizeof(boardtypes)/sizeof(struct dio_boardtype))
324
325static struct comedi_driver driver_pci_dio = {
326      driver_name:"adv_pci_dio",
327      module:THIS_MODULE,
328      attach:pci_dio_attach,
329      detach:pci_dio_detach
330};
331
332struct pci_dio_private {
333	struct pci_dio_private *prev;	/*  previous private struct */
334	struct pci_dio_private *next;	/*  next private struct */
335	struct pci_dev *pcidev;	/*  pointer to board's pci_dev */
336	char valid;		/*  card is usable */
337	char GlobalIrqEnabled;	/*  1= any IRQ source is enabled */
338	/*  PCI-1760 specific data */
339	unsigned char IDICntEnable;	/*  counter's counting enable status */
340	unsigned char IDICntOverEnable;	/*  counter's overflow interrupts enable status */
341	unsigned char IDICntMatchEnable;	/*  counter's match interrupts enable status */
342	unsigned char IDICntEdge;	/*  counter's count edge value (bit=0 - rising, =1 - falling) */
343	unsigned short CntResValue[8];	/*  counters' reset value */
344	unsigned short CntMatchValue[8];	/*  counters' match interrupt value */
345	unsigned char IDIFiltersEn;	/*  IDI's digital filters enable status */
346	unsigned char IDIPatMatchEn;	/*  IDI's pattern match enable status */
347	unsigned char IDIPatMatchValue;	/*  IDI's pattern match value */
348	unsigned short IDIFiltrLow[8];	/*  IDI's filter value low signal */
349	unsigned short IDIFiltrHigh[8];	/*  IDI's filter value high signal */
350};
351
352static struct pci_dio_private *pci_priv = NULL;	/* list of allocated cards */
353
354#define devpriv ((struct pci_dio_private *)dev->private)
355#define this_board ((const struct dio_boardtype *)dev->board_ptr)
356
357/*
358==============================================================================
359*/
360static int pci_dio_insn_bits_di_b(struct comedi_device *dev, struct comedi_subdevice *s,
361	struct comedi_insn *insn, unsigned int *data)
362{
363	const struct diosubd_data *d = (const struct diosubd_data *)s->private;
364	int i;
365
366	data[1] = 0;
367	for (i = 0; i < d->regs; i++) {
368		data[1] |= inb(dev->iobase + d->addr + i) << (8 * i);
369	}
370
371	return 2;
372}
373
374/*
375==============================================================================
376*/
377static int pci_dio_insn_bits_di_w(struct comedi_device *dev, struct comedi_subdevice *s,
378	struct comedi_insn *insn, unsigned int *data)
379{
380	const struct diosubd_data *d = (const struct diosubd_data *)s->private;
381	int i;
382
383	data[1] = 0;
384	for (i = 0; i < d->regs; i++)
385		data[1] |= inw(dev->iobase + d->addr + 2 * i) << (16 * i);
386
387	return 2;
388}
389
390/*
391==============================================================================
392*/
393static int pci_dio_insn_bits_do_b(struct comedi_device *dev, struct comedi_subdevice *s,
394	struct comedi_insn *insn, unsigned int *data)
395{
396	const struct diosubd_data *d = (const struct diosubd_data *)s->private;
397	int i;
398
399	if (data[0]) {
400		s->state &= ~data[0];
401		s->state |= (data[0] & data[1]);
402		for (i = 0; i < d->regs; i++)
403			outb((s->state >> (8 * i)) & 0xff,
404				dev->iobase + d->addr + i);
405	}
406	data[1] = s->state;
407
408	return 2;
409}
410
411/*
412==============================================================================
413*/
414static int pci_dio_insn_bits_do_w(struct comedi_device *dev, struct comedi_subdevice *s,
415	struct comedi_insn *insn, unsigned int *data)
416{
417	const struct diosubd_data *d = (const struct diosubd_data *)s->private;
418	int i;
419
420	if (data[0]) {
421		s->state &= ~data[0];
422		s->state |= (data[0] & data[1]);
423		for (i = 0; i < d->regs; i++)
424			outw((s->state >> (16 * i)) & 0xffff,
425				dev->iobase + d->addr + 2 * i);
426	}
427	data[1] = s->state;
428
429	return 2;
430}
431
432/*
433==============================================================================
434*/
435static int pci1760_unchecked_mbxrequest(struct comedi_device *dev,
436	unsigned char *omb, unsigned char *imb, int repeats)
437{
438	int cnt, tout, ok = 0;
439
440	for (cnt = 0; cnt < repeats; cnt++) {
441		outb(omb[0], dev->iobase + OMB0);
442		outb(omb[1], dev->iobase + OMB1);
443		outb(omb[2], dev->iobase + OMB2);
444		outb(omb[3], dev->iobase + OMB3);
445		for (tout = 0; tout < 251; tout++) {
446			imb[2] = inb(dev->iobase + IMB2);
447			if (imb[2] == omb[2]) {
448				imb[0] = inb(dev->iobase + IMB0);
449				imb[1] = inb(dev->iobase + IMB1);
450				imb[3] = inb(dev->iobase + IMB3);
451				ok = 1;
452				break;
453			}
454			comedi_udelay(1);
455		}
456		if (ok)
457			return 0;
458	}
459
460	comedi_error(dev, "PCI-1760 mailbox request timeout!");
461	return -ETIME;
462}
463
464static int pci1760_clear_imb2(struct comedi_device *dev)
465{
466	unsigned char omb[4] = { 0x0, 0x0, CMD_ClearIMB2, 0x0 };
467	unsigned char imb[4];
468	/* check if imb2 is already clear */
469	if (inb(dev->iobase + IMB2) == CMD_ClearIMB2)
470		return 0;
471	return pci1760_unchecked_mbxrequest(dev, omb, imb, OMBCMD_RETRY);
472}
473
474static int pci1760_mbxrequest(struct comedi_device *dev,
475	unsigned char *omb, unsigned char *imb)
476{
477	if (omb[2] == CMD_ClearIMB2) {
478		comedi_error(dev,
479			"bug! this function should not be used for CMD_ClearIMB2 command");
480		return -EINVAL;
481	}
482	if (inb(dev->iobase + IMB2) == omb[2]) {
483		int retval;
484		retval = pci1760_clear_imb2(dev);
485		if (retval < 0)
486			return retval;
487	}
488	return pci1760_unchecked_mbxrequest(dev, omb, imb, OMBCMD_RETRY);
489}
490
491/*
492==============================================================================
493*/
494static int pci1760_insn_bits_di(struct comedi_device *dev, struct comedi_subdevice *s,
495	struct comedi_insn *insn, unsigned int *data)
496{
497	data[1] = inb(dev->iobase + IMB3);
498
499	return 2;
500}
501
502/*
503==============================================================================
504*/
505static int pci1760_insn_bits_do(struct comedi_device *dev, struct comedi_subdevice *s,
506	struct comedi_insn *insn, unsigned int *data)
507{
508	int ret;
509	unsigned char omb[4] = {
510		0x00,
511		0x00,
512		CMD_SetRelaysOutput,
513		0x00
514	};
515	unsigned char imb[4];
516
517	if (data[0]) {
518		s->state &= ~data[0];
519		s->state |= (data[0] & data[1]);
520		omb[0] = s->state;
521		ret = pci1760_mbxrequest(dev, omb, imb);
522		if (!ret)
523			return ret;
524	}
525	data[1] = s->state;
526
527	return 2;
528}
529
530/*
531==============================================================================
532*/
533static int pci1760_insn_cnt_read(struct comedi_device *dev, struct comedi_subdevice *s,
534	struct comedi_insn *insn, unsigned int *data)
535{
536	int ret, n;
537	unsigned char omb[4] = {
538		CR_CHAN(insn->chanspec) & 0x07,
539		0x00,
540		CMD_GetIDICntCurValue,
541		0x00
542	};
543	unsigned char imb[4];
544
545	for (n = 0; n < insn->n; n++) {
546		ret = pci1760_mbxrequest(dev, omb, imb);
547		if (!ret)
548			return ret;
549		data[n] = (imb[1] << 8) + imb[0];
550	}
551
552	return n;
553}
554
555/*
556==============================================================================
557*/
558static int pci1760_insn_cnt_write(struct comedi_device *dev, struct comedi_subdevice *s,
559	struct comedi_insn *insn, unsigned int *data)
560{
561	int ret;
562	unsigned char chan = CR_CHAN(insn->chanspec) & 0x07;
563	unsigned char bitmask = 1 << chan;
564	unsigned char omb[4] = {
565		data[0] & 0xff,
566		(data[0] >> 8) & 0xff,
567		CMD_SetIDI0CntResetValue + chan,
568		0x00
569	};
570	unsigned char imb[4];
571
572	if (devpriv->CntResValue[chan] != (data[0] & 0xffff)) {	/*  Set reset value if different */
573		ret =  pci1760_mbxrequest(dev, omb, imb);
574		if (!ret)
575			return ret;
576		devpriv->CntResValue[chan] = data[0] & 0xffff;
577	}
578
579	omb[0] = bitmask;	/*  reset counter to it reset value */
580	omb[2] = CMD_ResetIDICounters;
581	ret = pci1760_mbxrequest(dev, omb, imb);
582	if (!ret)
583		return ret;
584
585	if (!(bitmask & devpriv->IDICntEnable)) {	/*  start counter if it don't run */
586		omb[0] = bitmask;
587		omb[2] = CMD_EnableIDICounters;
588		ret = pci1760_mbxrequest(dev, omb, imb);
589		if (!ret)
590			return ret;
591		devpriv->IDICntEnable |= bitmask;
592	}
593	return 1;
594}
595
596/*
597==============================================================================
598*/
599static int pci1760_reset(struct comedi_device *dev)
600{
601	int i;
602	unsigned char omb[4] = { 0x00, 0x00, 0x00, 0x00 };
603	unsigned char imb[4];
604
605	outb(0, dev->iobase + INTCSR0);	/*  disable IRQ */
606	outb(0, dev->iobase + INTCSR1);
607	outb(0, dev->iobase + INTCSR2);
608	outb(0, dev->iobase + INTCSR3);
609	devpriv->GlobalIrqEnabled = 0;
610
611	omb[0] = 0x00;
612	omb[2] = CMD_SetRelaysOutput;	/*  reset relay outputs */
613	pci1760_mbxrequest(dev, omb, imb);
614
615	omb[0] = 0x00;
616	omb[2] = CMD_EnableIDICounters;	/*  disable IDI up counters */
617	pci1760_mbxrequest(dev, omb, imb);
618	devpriv->IDICntEnable = 0;
619
620	omb[0] = 0x00;
621	omb[2] = CMD_OverflowIDICounters;	/*  disable counters overflow interrupts */
622	pci1760_mbxrequest(dev, omb, imb);
623	devpriv->IDICntOverEnable = 0;
624
625	omb[0] = 0x00;
626	omb[2] = CMD_MatchIntIDICounters;	/*  disable counters match value interrupts */
627	pci1760_mbxrequest(dev, omb, imb);
628	devpriv->IDICntMatchEnable = 0;
629
630	omb[0] = 0x00;
631	omb[1] = 0x80;
632	for (i = 0; i < 8; i++) {	/*  set IDI up counters match value */
633		omb[2] = CMD_SetIDI0CntMatchValue + i;
634		pci1760_mbxrequest(dev, omb, imb);
635		devpriv->CntMatchValue[i] = 0x8000;
636	}
637
638	omb[0] = 0x00;
639	omb[1] = 0x00;
640	for (i = 0; i < 8; i++) {	/*  set IDI up counters reset value */
641		omb[2] = CMD_SetIDI0CntResetValue + i;
642		pci1760_mbxrequest(dev, omb, imb);
643		devpriv->CntResValue[i] = 0x0000;
644	}
645
646	omb[0] = 0xff;
647	omb[2] = CMD_ResetIDICounters;	/*  reset IDI up counters to reset values */
648	pci1760_mbxrequest(dev, omb, imb);
649
650	omb[0] = 0x00;
651	omb[2] = CMD_EdgeIDICounters;	/*  set IDI up counters count edge */
652	pci1760_mbxrequest(dev, omb, imb);
653	devpriv->IDICntEdge = 0x00;
654
655	omb[0] = 0x00;
656	omb[2] = CMD_EnableIDIFilters;	/*  disable all digital in filters */
657	pci1760_mbxrequest(dev, omb, imb);
658	devpriv->IDIFiltersEn = 0x00;
659
660	omb[0] = 0x00;
661	omb[2] = CMD_EnableIDIPatternMatch;	/*  disable pattern matching */
662	pci1760_mbxrequest(dev, omb, imb);
663	devpriv->IDIPatMatchEn = 0x00;
664
665	omb[0] = 0x00;
666	omb[2] = CMD_SetIDIPatternMatch;	/*  set pattern match value */
667	pci1760_mbxrequest(dev, omb, imb);
668	devpriv->IDIPatMatchValue = 0x00;
669
670	return 0;
671}
672
673/*
674==============================================================================
675*/
676static int pci_dio_reset(struct comedi_device *dev)
677{
678	DPRINTK("adv_pci_dio EDBG: BGN: pci171x_reset(...)\n");
679
680	switch (this_board->cardtype) {
681	case TYPE_PCI1730:
682		outb(0, dev->iobase + PCI1730_DO);	/*  clear outputs */
683		outb(0, dev->iobase + PCI1730_DO + 1);
684		outb(0, dev->iobase + PCI1730_IDO);
685		outb(0, dev->iobase + PCI1730_IDO + 1);
686		/* NO break there! */
687	case TYPE_PCI1733:
688		outb(0, dev->iobase + PCI1730_3_INT_EN);	/*  disable interrupts */
689		outb(0x0f, dev->iobase + PCI1730_3_INT_CLR);	/*  clear interrupts */
690		outb(0, dev->iobase + PCI1730_3_INT_RF);	/*  set rising edge trigger */
691		break;
692	case TYPE_PCI1734:
693		outb(0, dev->iobase + PCI1734_IDO);	/*  clear outputs */
694		outb(0, dev->iobase + PCI1734_IDO + 1);
695		outb(0, dev->iobase + PCI1734_IDO + 2);
696		outb(0, dev->iobase + PCI1734_IDO + 3);
697		break;
698
699	case TYPE_PCI1736:
700		outb(0, dev->iobase+PCI1736_IDO);
701		outb(0, dev->iobase+PCI1736_IDO+1);
702		outb(0, dev->iobase+PCI1736_3_INT_EN);  /*  disable interrupts */
703		outb(0x0f, dev->iobase+PCI1736_3_INT_CLR);/*  clear interrupts */
704		outb(0, dev->iobase+PCI1736_3_INT_RF);  /*  set rising edge trigger */
705		break;
706
707	case TYPE_PCI1750:
708	case TYPE_PCI1751:
709		outb(0x88, dev->iobase + PCI1750_ICR);	/*  disable & clear interrupts */
710		break;
711	case TYPE_PCI1752:
712		outw(0, dev->iobase + PCI1752_6_CFC);	/*  disable channel freeze function */
713		outw(0, dev->iobase + PCI1752_IDO);	/*  clear outputs */
714		outw(0, dev->iobase + PCI1752_IDO + 2);
715		outw(0, dev->iobase + PCI1752_IDO2);
716		outw(0, dev->iobase + PCI1752_IDO2 + 2);
717		break;
718	case TYPE_PCI1753E:
719		outb(0x88, dev->iobase + PCI1753E_ICR0);	/*  disable & clear interrupts */
720		outb(0x80, dev->iobase + PCI1753E_ICR1);
721		outb(0x80, dev->iobase + PCI1753E_ICR2);
722		outb(0x80, dev->iobase + PCI1753E_ICR3);
723		/* NO break there! */
724	case TYPE_PCI1753:
725		outb(0x88, dev->iobase + PCI1753_ICR0);	/*  disable & clear interrupts */
726		outb(0x80, dev->iobase + PCI1753_ICR1);
727		outb(0x80, dev->iobase + PCI1753_ICR2);
728		outb(0x80, dev->iobase + PCI1753_ICR3);
729		break;
730	case TYPE_PCI1754:
731		outw(0x08, dev->iobase + PCI1754_6_ICR0);	/*  disable and clear interrupts */
732		outw(0x08, dev->iobase + PCI1754_6_ICR1);
733		outw(0x08, dev->iobase + PCI1754_ICR2);
734		outw(0x08, dev->iobase + PCI1754_ICR3);
735		break;
736	case TYPE_PCI1756:
737		outw(0, dev->iobase + PCI1752_6_CFC);	/*  disable channel freeze function */
738		outw(0x08, dev->iobase + PCI1754_6_ICR0);	/*  disable and clear interrupts */
739		outw(0x08, dev->iobase + PCI1754_6_ICR1);
740		outw(0, dev->iobase + PCI1756_IDO);	/*  clear outputs */
741		outw(0, dev->iobase + PCI1756_IDO + 2);
742		break;
743	case TYPE_PCI1760:
744		pci1760_reset(dev);
745		break;
746	case TYPE_PCI1762:
747		outw(0x0101, dev->iobase + PCI1762_ICR);	/*  disable & clear interrupts */
748		break;
749	}
750
751	DPRINTK("adv_pci_dio EDBG: END: pci171x_reset(...)\n");
752
753	return 0;
754}
755
756/*
757==============================================================================
758*/
759static int pci1760_attach(struct comedi_device *dev, struct comedi_devconfig *it)
760{
761	struct comedi_subdevice *s;
762	int subdev = 0;
763
764	s = dev->subdevices + subdev;
765	s->type = COMEDI_SUBD_DI;
766	s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON;
767	s->n_chan = 8;
768	s->maxdata = 1;
769	s->len_chanlist = 8;
770	s->range_table = &range_digital;
771	s->insn_bits = pci1760_insn_bits_di;
772	subdev++;
773
774	s = dev->subdevices + subdev;
775	s->type = COMEDI_SUBD_DO;
776	s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
777	s->n_chan = 8;
778	s->maxdata = 1;
779	s->len_chanlist = 8;
780	s->range_table = &range_digital;
781	s->state = 0;
782	s->insn_bits = pci1760_insn_bits_do;
783	subdev++;
784
785	s = dev->subdevices + subdev;
786	s->type = COMEDI_SUBD_TIMER;
787	s->subdev_flags = SDF_WRITABLE | SDF_LSAMPL;
788	s->n_chan = 2;
789	s->maxdata = 0xffffffff;
790	s->len_chanlist = 2;
791/*       s->insn_config=pci1760_insn_pwm_cfg; */
792	subdev++;
793
794	s = dev->subdevices + subdev;
795	s->type = COMEDI_SUBD_COUNTER;
796	s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
797	s->n_chan = 8;
798	s->maxdata = 0xffff;
799	s->len_chanlist = 8;
800	s->insn_read = pci1760_insn_cnt_read;
801	s->insn_write = pci1760_insn_cnt_write;
802/*       s->insn_config=pci1760_insn_cnt_cfg; */
803	subdev++;
804
805	return 0;
806}
807
808/*
809==============================================================================
810*/
811static int pci_dio_add_di(struct comedi_device *dev, struct comedi_subdevice *s,
812	const struct diosubd_data *d, int subdev)
813{
814	s->type = COMEDI_SUBD_DI;
815	s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON | d->specflags;
816	if (d->chans > 16)
817		s->subdev_flags |= SDF_LSAMPL;
818	s->n_chan = d->chans;
819	s->maxdata = 1;
820	s->len_chanlist = d->chans;
821	s->range_table = &range_digital;
822	switch (this_board->io_access) {
823	case IO_8b:
824		s->insn_bits = pci_dio_insn_bits_di_b;
825		break;
826	case IO_16b:
827		s->insn_bits = pci_dio_insn_bits_di_w;
828		break;
829	}
830	s->private = (void *)d;
831
832	return 0;
833}
834
835/*
836==============================================================================
837*/
838static int pci_dio_add_do(struct comedi_device *dev, struct comedi_subdevice *s,
839	const struct diosubd_data *d, int subdev)
840{
841	s->type = COMEDI_SUBD_DO;
842	s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
843	if (d->chans > 16)
844		s->subdev_flags |= SDF_LSAMPL;
845	s->n_chan = d->chans;
846	s->maxdata = 1;
847	s->len_chanlist = d->chans;
848	s->range_table = &range_digital;
849	s->state = 0;
850	switch (this_board->io_access) {
851	case IO_8b:
852		s->insn_bits = pci_dio_insn_bits_do_b;
853		break;
854	case IO_16b:
855		s->insn_bits = pci_dio_insn_bits_do_w;
856		break;
857	}
858	s->private = (void *)d;
859
860	return 0;
861}
862
863/*
864==============================================================================
865*/
866static int CheckAndAllocCard(struct comedi_device *dev, struct comedi_devconfig *it,
867	struct pci_dev *pcidev)
868{
869	struct pci_dio_private *pr, *prev;
870
871	for (pr = pci_priv, prev = NULL; pr != NULL; prev = pr, pr = pr->next) {
872		if (pr->pcidev == pcidev) {
873			return 0;	/*  this card is used, look for another */
874		}
875	}
876
877	if (prev) {
878		devpriv->prev = prev;
879		prev->next = devpriv;
880	} else {
881		pci_priv = devpriv;
882	}
883
884	devpriv->pcidev = pcidev;
885
886	return 1;
887}
888
889/*
890==============================================================================
891*/
892static int pci_dio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
893{
894	struct comedi_subdevice *s;
895	int ret, subdev, n_subdevices, i, j;
896	unsigned long iobase;
897	struct pci_dev *pcidev;
898
899	rt_printk("comedi%d: adv_pci_dio: ", dev->minor);
900
901	ret = alloc_private(dev, sizeof(struct pci_dio_private));
902	if (ret < 0) {
903		rt_printk(", Error: Cann't allocate private memory!\n");
904		return -ENOMEM;
905	}
906
907	for (pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
908		pcidev != NULL;
909		pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
910		/*  loop through cards supported by this driver */
911		for (i = 0; i < n_boardtypes; ++i) {
912			if (boardtypes[i].vendor_id != pcidev->vendor)
913				continue;
914			if (boardtypes[i].device_id != pcidev->device)
915				continue;
916			/*  was a particular bus/slot requested? */
917			if (it->options[0] || it->options[1]) {
918				/*  are we on the wrong bus/slot? */
919				if (pcidev->bus->number != it->options[0] ||
920					PCI_SLOT(pcidev->devfn) !=
921					it->options[1]) {
922					continue;
923				}
924			}
925			ret = CheckAndAllocCard(dev, it, pcidev);
926			if (ret != 1) continue;
927			dev->board_ptr = boardtypes + i;
928			break;
929		}
930		if (dev->board_ptr)
931			break;
932	}
933
934	if (!dev->board_ptr) {
935		rt_printk
936			(", Error: Requested type of the card was not found!\n");
937		return -EIO;
938	}
939
940	if (comedi_pci_enable(pcidev, driver_pci_dio.driver_name)) {
941		rt_printk
942			(", Error: Can't enable PCI device and request regions!\n");
943		return -EIO;
944	}
945	iobase = pci_resource_start(pcidev, this_board->main_pci_region);
946	rt_printk(", b:s:f=%d:%d:%d, io=0x%4lx",
947		pcidev->bus->number, PCI_SLOT(pcidev->devfn),
948		PCI_FUNC(pcidev->devfn), iobase);
949
950	dev->iobase = iobase;
951	dev->board_name = this_board->name;
952
953	if (this_board->cardtype == TYPE_PCI1760) {
954		n_subdevices = 4;	/*  8 IDI, 8 IDO, 2 PWM, 8 CNT */
955	} else {
956		n_subdevices = 0;
957		for (i = 0; i < MAX_DI_SUBDEVS; i++)
958			if (this_board->sdi[i].chans)
959				n_subdevices++;
960		for (i = 0; i < MAX_DO_SUBDEVS; i++)
961			if (this_board->sdo[i].chans)
962				n_subdevices++;
963		for (i = 0; i < MAX_DIO_SUBDEVG; i++)
964			n_subdevices += this_board->sdio[i].regs;
965		if (this_board->boardid.chans)
966			n_subdevices++;
967	}
968
969	ret = alloc_subdevices(dev, n_subdevices);
970	if (ret < 0) {
971		rt_printk(", Error: Cann't allocate subdevice memory!\n");
972		return ret;
973	}
974
975	rt_printk(".\n");
976
977	subdev = 0;
978
979	for (i = 0; i < MAX_DI_SUBDEVS; i++)
980		if (this_board->sdi[i].chans) {
981			s = dev->subdevices + subdev;
982			pci_dio_add_di(dev, s, &this_board->sdi[i], subdev);
983			subdev++;
984		}
985
986	for (i = 0; i < MAX_DO_SUBDEVS; i++)
987		if (this_board->sdo[i].chans) {
988			s = dev->subdevices + subdev;
989			pci_dio_add_do(dev, s, &this_board->sdo[i], subdev);
990			subdev++;
991		}
992
993	for (i = 0; i < MAX_DIO_SUBDEVG; i++)
994		for (j = 0; j < this_board->sdio[i].regs; j++) {
995			s = dev->subdevices + subdev;
996			subdev_8255_init(dev, s, NULL,
997				dev->iobase + this_board->sdio[i].addr +
998				SIZE_8255 * j);
999			subdev++;
1000		}
1001
1002	if (this_board->boardid.chans) {
1003		s = dev->subdevices + subdev;
1004		s->type = COMEDI_SUBD_DI;
1005		pci_dio_add_di(dev, s, &this_board->boardid, subdev);
1006		subdev++;
1007	}
1008
1009	if (this_board->cardtype == TYPE_PCI1760)
1010		pci1760_attach(dev, it);
1011
1012	devpriv->valid = 1;
1013
1014	pci_dio_reset(dev);
1015
1016	return 0;
1017}
1018
1019/*
1020==============================================================================
1021*/
1022static int pci_dio_detach(struct comedi_device *dev)
1023{
1024	int i, j;
1025	struct comedi_subdevice *s;
1026	int subdev;
1027
1028	if (dev->private) {
1029		if (devpriv->valid) {
1030			pci_dio_reset(dev);
1031		}
1032
1033		/* This shows the silliness of using this kind of
1034		 * scheme for numbering subdevices.  Don't do it.  --ds */
1035		subdev = 0;
1036		for (i = 0; i < MAX_DI_SUBDEVS; i++) {
1037			if (this_board->sdi[i].chans) {
1038				subdev++;
1039			}
1040		}
1041		for (i = 0; i < MAX_DO_SUBDEVS; i++) {
1042			if (this_board->sdo[i].chans) {
1043				subdev++;
1044			}
1045		}
1046		for (i = 0; i < MAX_DIO_SUBDEVG; i++) {
1047			for (j = 0; j < this_board->sdio[i].regs; j++) {
1048				s = dev->subdevices + subdev;
1049				subdev_8255_cleanup(dev, s);
1050				subdev++;
1051			}
1052		}
1053
1054		for (i = 0; i < dev->n_subdevices; i++) {
1055			s = dev->subdevices + i;
1056			s->private = NULL;
1057		}
1058
1059		if (devpriv->pcidev) {
1060			if (dev->iobase) {
1061				comedi_pci_disable(devpriv->pcidev);
1062			}
1063			pci_dev_put(devpriv->pcidev);
1064		}
1065
1066		if (devpriv->prev) {
1067			devpriv->prev->next = devpriv->next;
1068		} else {
1069			pci_priv = devpriv->next;
1070		}
1071		if (devpriv->next) {
1072			devpriv->next->prev = devpriv->prev;
1073		}
1074	}
1075
1076	return 0;
1077}
1078
1079/*
1080==============================================================================
1081*/
1082COMEDI_PCI_INITCLEANUP(driver_pci_dio, pci_dio_pci_table);
1083/*
1084==============================================================================
1085*/
1086