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