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