Add adv_pci_dio driver from Michal Dobes
authorDavid Schleef <ds@schleef.org>
Tue, 27 May 2003 17:27:43 +0000 (17:27 +0000)
committerDavid Schleef <ds@schleef.org>
Tue, 27 May 2003 17:27:43 +0000 (17:27 +0000)
comedi/drivers/Makefile.in
comedi/drivers/adv_pci_dio.c [new file with mode: 0644]

index 0b40ae6f4f8448e2903cd8cbb389fdd355725829..ab8e04a2393e61c5b297f0100ec0a1960bd4791e 100644 (file)
@@ -5,6 +5,7 @@ select(CONFIG_COMEDI_8255 8255.o)
 select(CONFIG_COMEDI_ADL_PCI9111 adl_pci9111.o)
 select(CONFIG_COMEDI_ADL_PCI9118 adl_pci9118.o amcc_s5933.o)
 select(CONFIG_COMEDI_ADV_PCI1710 adv_pci1710.o amcc_s5933.o)
+select(CONFIG_COMEDI_ADV_PCI_DIO adv_pci_dio.o)
 select(CONFIG_COMEDI_AMPLC_PCI230 amplc_pci230.o)
 select(CONFIG_COMEDI_AMPLC_PC236 amplc_pc236.o)
 select(CONFIG_COMEDI_AMPLC_PC263 amplc_pc263.o)
diff --git a/comedi/drivers/adv_pci_dio.c b/comedi/drivers/adv_pci_dio.c
new file mode 100644 (file)
index 0000000..d138cf2
--- /dev/null
@@ -0,0 +1,1046 @@
+/*
+ * comedi/drivers/adv_pci_dio.c
+ *
+ * Author: Michal Dobes <dobes@tesnet.cz>
+ *
+ *  Hardware driver for Advantech PCI DIO cards.
+*/
+/*
+Driver: adv_pci_dio.o
+Description: Advantech PCI-1730, PCI-1733, PCI-1734, PCI-1750, PCI-1751,
+             Advantech PCI-1752, PCI-1753/E, PCI-1754, PCI-1756, PCI-1762
+Author: Michal Dobes <dobes@tesnet.cz>
+Devices: [Advantech] PCI-1730 (pci1730), PCI-1733 (pci1733),
+  PCI-1734 (pci1734), PCI-1750 (pci1750), PCI-1751 (pci1751),
+  PCI-1752 (pci1752), PCI-1753 (pci1753), PCI-1753+PCI-1753E (pci1753e),
+  PCI-1754 (pci1754), PCI-1756 (pci1756), PCI-1760(pci1760), 
+  PCI-1762 (pci1762)
+Status: untested
+Updated: 2003-04-06
+
+This driver supports now only insn interface for DI/DO/DIO.
+
+Configuration options:
+  [0] - PCI bus of device (optional)
+  [1] - PCI slot of device (optional)
+          If bus/slot is not specified, the first available PCI
+          device will be used.
+
+*/
+
+#include <linux/comedidev.h>
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include "8255.h"
+
+#undef PCI_DIO_EXTDEBUG        /* if defined, enable extensive debug logging */
+
+#undef DPRINTK
+#ifdef PCI_DIO_EXTDEBUG
+#define DPRINTK(fmt, args...) rt_printk(fmt, ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+// hardware types of the cards
+typedef enum {
+       TYPE_PCI1730, TYPE_PCI1733, TYPE_PCI1734,
+       TYPE_PCI1750, 
+       TYPE_PCI1751,
+       TYPE_PCI1752, 
+       TYPE_PCI1753, TYPE_PCI1753E,
+       TYPE_PCI1754, TYPE_PCI1756,
+       TYPE_PCI1760,
+       TYPE_PCI1762
+} hw_cards_id;
+
+// which I/O instructions to use
+typedef enum {
+       IO_8b, IO_16b
+} hw_io_access;
+
+#define MAX_DI_SUBDEVS 2       /* max number of DI subdevices per card */
+#define MAX_DO_SUBDEVS 2       /* max number of DO subdevices per card */
+#define MAX_DIO_SUBDEVG        2       /* max number of DIO subdevices group per card */
+
+#define SIZE_8255         4    /* 8255 IO space length */
+
+#define PCIDIO_MAINREG    2            /* main I/O region for all Advantech cards? */
+
+/* Register offset definitions */
+// Advantech PCI-1730/3/4
+#define PCI1730_IDI       0            /* R:   Isolated digital input  0-15 */
+#define PCI1730_IDO       0            /* W:   Isolated digital output 0-15 */
+#define PCI1730_DI        2            /* R:   Digital input  0-15 */
+#define PCI1730_DO        2            /* W:   Digital output 0-15 */
+#define PCI1733_IDI       0            /* R:   Isolated digital input  0-31 */
+#define        PCI1730_3_INT_EN        0x08    /* R/W: enable/disable interrupts */
+#define        PCI1730_3_INT_RF        0x0c    /* R/W: set falling/raising edge for interrupts */
+#define        PCI1730_3_INT_CLR       0x10    /* R/W: clear interrupts */
+#define PCI1734_IDO       0            /* W:   Isolated digital output 0-31 */
+#define PCI173x_BOARDID           4            /* R:   Board I/D switch for 1730/3/4 */
+
+// Advantech PCI-1750
+#define PCI1750_IDI       0            /* R:   Isolated digital input  0-15 */
+#define PCI1750_IDO       0            /* W:   Isolated digital output 0-15 */
+#define PCI1750_ICR      32            /* W:   Interrupt control register */
+#define PCI1750_ISR      32            /* R:   Interrupt status register */
+
+// Advantech PCI-1751/3/3E
+#define PCI1751_DIO       0            /* R/W: begin of 8255 registers block */
+#define PCI1751_ICR      32            /* W:   Interrupt control register */
+#define PCI1751_ISR      32            /* R:   Interrupt status register */
+#define PCI1753_DIO       0            /* R/W: begin of 8255 registers block */
+#define PCI1753_ICR0     17            /* R/W: Interrupt control register group 0 */
+#define PCI1753_ICR1     18            /* R/W: Interrupt control register group 0 */
+#define PCI1753_ICR2     19            /* R/W: Interrupt control register group 0 */
+#define PCI1753_ICR3     48            /* R/W: Interrupt control register group 0 */
+#define PCI1753E_DIO     32            /* R/W: begin of 8255 registers block */
+#define PCI1753E_ICR0    49            /* R/W: Interrupt control register group 0 */
+#define PCI1753E_ICR1    50            /* R/W: Interrupt control register group 0 */
+#define PCI1753E_ICR2    51            /* R/W: Interrupt control register group 0 */
+#define PCI1753E_ICR3    52            /* R/W: Interrupt control register group 0 */
+
+
+// Advantech PCI-1752/4/6
+#define PCI1752_IDO       0            /* R/W: Digital output  0-31 */
+#define PCI1752_IDO2      4            /* R/W: Digital output 32-63 */
+#define PCI1754_IDI       0            /* R:   Digital input   0-31 */
+#define PCI1754_IDI2      4            /* R:   Digital input  32-64 */
+#define PCI1756_IDI       0            /* R:   Digital input   0-31 */
+#define PCI1756_IDO       4            /* R/W: Digital output  0-31 */
+#define PCI1754_6_ICR0 0x08            /* R/W: Interrupt control register group 0 */
+#define PCI1754_6_ICR1 0x0a            /* R/W: Interrupt control register group 1 */
+#define PCI1754_ICR2   0x0c            /* R/W: Interrupt control register group 2 */
+#define PCI1754_ICR3   0x0e            /* R/W: Interrupt control register group 3 */
+#define PCI1752_6_CFC  0x12            /* R/W: set/read channel freeze function */
+#define PCI175x_BOARDID        0x10            /* R:   Board I/D switch for 1752/4/6 */
+
+// Advantech PCI-1762 registers
+#define PCI1762_RO        0            /* R/W: Relays status/output */
+#define PCI1762_IDI       0            /* R:   Isolated input status */
+#define PCI1762_BOARDID           4            /* R:   Board I/D switch */
+#define PCI1762_ICR       6            /* W:   Interrupt control register */
+#define PCI1762_ISR       6            /* R:   Interrupt status register */
+
+// Advantech PCI-1760 registers
+#define OMB0           0x0c            /* W:   Mailbox outgoing registers */
+#define OMB1           0x0d
+#define OMB2           0x0e
+#define OMB3           0x0f
+#define IMB0           0x1c            /* R:   Mailbox incoming registers */
+#define IMB1           0x1d
+#define IMB2           0x1e
+#define IMB3           0x1f
+#define INTCSR0                0x38            /* R/W: Interrupt control registers */
+#define INTCSR1                0x39
+#define INTCSR2                0x3a
+#define INTCSR3                0x3b
+
+// PCI-1760 mailbox commands
+#define CMD_ClearIMB2          0x00    /* Clear IMB2 status and return actaul DI status in IMB3 */
+#define CMD_SetRelaysOutput    0x01    /* Set relay output from OMB0 */
+#define CMD_GetRelaysStatus    0x02    /* Get relay status to IMB0 */
+#define CMD_ReadCurrentStatus  0x07    /* Read the current status of the register in OMB0, result in IMB0 */
+#define CMD_ReadFirmwareVersion        0x0e    /* Read the firmware ver., result in IMB1.IMB0 */
+#define CMD_ReadHardwareVersion        0x0f    /* Read the hardware ver., result in IMB1.IMB0 */
+#define CMD_EnableIDIFilters   0x20    /* Enable IDI filters based on bits in OMB0 */
+#define CMD_EnableIDIPatternMatch 0x21 /* Enable IDI pattern match based on bits in OMB0 */
+#define CMD_SetIDIPatternMatch 0x22    /* Enable IDI pattern match based on bits in OMB0 */
+#define CMD_EnableIDICounters  0x28    /* Enable IDI counters based on bits in OMB0 */
+#define CMD_ResetIDICounters   0x29    /* Reset IDI counters based on bits in OMB0 to its reset values */
+#define CMD_OverflowIDICounters        0x2a    /* Enable IDI counters overflow interrupts  based on bits in OMB0 */
+#define CMD_MatchIntIDICounters        0x2b    /* Enable IDI counters match value interrupts  based on bits in OMB0 */
+#define CMD_EdgeIDICounters    0x2c    /* Set IDI up counters count edge (bit=0 - rising, =1 - falling) */
+#define CMD_GetIDICntCurValue  0x2f    /* Read IDI{OMB0} up counter current value */
+#define CMD_SetIDI0CntResetValue 0x40  /* Set IDI0 Counter Reset Value 256*OMB1+OMB0 */
+#define CMD_SetIDI1CntResetValue 0x41  /* Set IDI1 Counter Reset Value 256*OMB1+OMB0 */
+#define CMD_SetIDI2CntResetValue 0x42  /* Set IDI2 Counter Reset Value 256*OMB1+OMB0 */
+#define CMD_SetIDI3CntResetValue 0x43  /* Set IDI3 Counter Reset Value 256*OMB1+OMB0 */
+#define CMD_SetIDI4CntResetValue 0x44  /* Set IDI4 Counter Reset Value 256*OMB1+OMB0 */
+#define CMD_SetIDI5CntResetValue 0x45  /* Set IDI5 Counter Reset Value 256*OMB1+OMB0 */
+#define CMD_SetIDI6CntResetValue 0x46  /* Set IDI6 Counter Reset Value 256*OMB1+OMB0 */
+#define CMD_SetIDI7CntResetValue 0x47  /* Set IDI7 Counter Reset Value 256*OMB1+OMB0 */
+#define CMD_SetIDI0CntMatchValue 0x48  /* Set IDI0 Counter Match Value 256*OMB1+OMB0 */
+#define CMD_SetIDI1CntMatchValue 0x49  /* Set IDI1 Counter Match Value 256*OMB1+OMB0 */
+#define CMD_SetIDI2CntMatchValue 0x4a  /* Set IDI2 Counter Match Value 256*OMB1+OMB0 */
+#define CMD_SetIDI3CntMatchValue 0x4b  /* Set IDI3 Counter Match Value 256*OMB1+OMB0 */
+#define CMD_SetIDI4CntMatchValue 0x4c  /* Set IDI4 Counter Match Value 256*OMB1+OMB0 */
+#define CMD_SetIDI5CntMatchValue 0x4d  /* Set IDI5 Counter Match Value 256*OMB1+OMB0 */
+#define CMD_SetIDI6CntMatchValue 0x4e  /* Set IDI6 Counter Match Value 256*OMB1+OMB0 */
+#define CMD_SetIDI7CntMatchValue 0x4f  /* Set IDI7 Counter Match Value 256*OMB1+OMB0 */
+
+#define OMBCMD_RETRY   0x03            /* 3 times try request before error */
+
+static int pci_dio_attach(comedi_device *dev,comedi_devconfig *it);
+static int pci_dio_detach(comedi_device *dev);
+
+typedef struct {
+       int             chans;          // num of chans
+       int             addr;           // PCI address ofset
+       int             regs;           // number of registers to read or 8255 subdevices
+       unsigned int    specflags;      // addon subdevice flags
+} diosubd_data;
+
+typedef struct {
+       char            *name;          // driver name
+       int             vendor_id;      // vendor/device PCI ID
+       int             device_id;
+       int             main_pci_region;// main I/O OCI region
+       hw_cards_id     cardtype;       // {enum hw_cards_id_enum}
+       diosubd_data    sdi[MAX_DI_SUBDEVS];    // DI chans
+       diosubd_data    sdo[MAX_DO_SUBDEVS];    // DO chans
+       diosubd_data    sdio[MAX_DIO_SUBDEVG];  // DIO 8255 chans
+       diosubd_data    boardid;        // card supports board ID switch
+       hw_io_access    io_access;      // {enum hw_io_access_enum}
+} boardtype;
+
+static struct pci_device_id pci_dio_pci_table[] = __devinitdata {
+       { PCI_VENDOR_ID_ADVANTECH, 0x1730, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+       { PCI_VENDOR_ID_ADVANTECH, 0x1733, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+       { PCI_VENDOR_ID_ADVANTECH, 0x1734, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+       { PCI_VENDOR_ID_ADVANTECH, 0x1750, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+       { PCI_VENDOR_ID_ADVANTECH, 0x1751, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+       { PCI_VENDOR_ID_ADVANTECH, 0x1752, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+       { PCI_VENDOR_ID_ADVANTECH, 0x1753, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+       { PCI_VENDOR_ID_ADVANTECH, 0x1754, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+       { PCI_VENDOR_ID_ADVANTECH, 0x1756, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+       { PCI_VENDOR_ID_ADVANTECH, 0x1760, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+       { PCI_VENDOR_ID_ADVANTECH, 0x1762, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+       { 0 }
+};
+MODULE_DEVICE_TABLE(pci, pci_dio_pci_table);
+
+static boardtype boardtypes[] =
+{
+       {"pci1730", PCI_VENDOR_ID_ADVANTECH, 0x1730, PCIDIO_MAINREG,
+        TYPE_PCI1730,
+        {{16, PCI1730_DI, 2, 0}, {16, PCI1730_IDI, 2, 0}},
+        {{16, PCI1730_DO, 2, 0}, {16, PCI1730_IDO, 2, 0}},
+        {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
+         { 4, PCI173x_BOARDID, 1, SDF_INTERNAL},
+        IO_8b, 
+       },
+       {"pci1733", PCI_VENDOR_ID_ADVANTECH, 0x1733, PCIDIO_MAINREG,
+        TYPE_PCI1733,
+        {{ 0, 0, 0, 0}, {32, PCI1733_IDI, 4, 0}},
+        {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
+        {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
+         { 4, PCI173x_BOARDID, 1, SDF_INTERNAL},
+        IO_8b
+       },
+       {"pci1734", PCI_VENDOR_ID_ADVANTECH, 0x1734, PCIDIO_MAINREG,
+        TYPE_PCI1734,
+        {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
+        {{ 0, 0, 0, 0}, {32, PCI1734_IDO, 4,  0}},
+        {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
+         { 4, PCI173x_BOARDID, 1, SDF_INTERNAL},
+        IO_8b
+       },
+       {"pci1750", PCI_VENDOR_ID_ADVANTECH, 0x1750, PCIDIO_MAINREG,
+        TYPE_PCI1750,
+        {{ 0, 0, 0, 0}, {16, PCI1750_IDI, 2, 0}},
+        {{ 0, 0, 0, 0}, {16, PCI1750_IDO, 2, 0}},
+        {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
+         { 0, 0, 0, 0},
+        IO_8b
+       },
+       {"pci1751", PCI_VENDOR_ID_ADVANTECH, 0x1751, PCIDIO_MAINREG,
+        TYPE_PCI1751,
+        {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
+        {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
+        {{48, PCI1751_DIO, 2, 0}, { 0, 0, 0, 0}},
+         { 0, 0, 0, 0},
+        IO_8b
+       },
+       {"pci1752", PCI_VENDOR_ID_ADVANTECH, 0x1752, PCIDIO_MAINREG,
+        TYPE_PCI1752,
+        {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
+        {{32, PCI1752_IDO, 2, 0}, {32, PCI1752_IDO2, 2, 0}},
+        {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
+         { 4, PCI175x_BOARDID, 1, SDF_INTERNAL},
+        IO_16b
+       },
+       {"pci1753", PCI_VENDOR_ID_ADVANTECH, 0x1753, PCIDIO_MAINREG,
+        TYPE_PCI1753,
+        {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
+        {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
+        {{96, PCI1753_DIO, 4, 0}, { 0, 0, 0, 0}},
+         { 0, 0, 0, 0},
+        IO_8b
+       },
+       {"pci1753e", PCI_VENDOR_ID_ADVANTECH, 0x1753, PCIDIO_MAINREG,
+        TYPE_PCI1753E,
+        {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
+        {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
+        {{96, PCI1753_DIO, 4, 0}, {96, PCI1753E_DIO, 4, 0}},
+         { 0, 0, 0, 0},
+        IO_8b
+       },
+       {"pci1754", PCI_VENDOR_ID_ADVANTECH, 0x1754, PCIDIO_MAINREG,
+        TYPE_PCI1754,
+        {{32, PCI1754_IDI, 2, 0}, {32, PCI1754_IDI2, 2, 0}},
+        {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
+        {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
+         { 4, PCI175x_BOARDID, 1, SDF_INTERNAL},
+        IO_16b
+       },
+       {"pci1756", PCI_VENDOR_ID_ADVANTECH, 0x1756, PCIDIO_MAINREG,
+        TYPE_PCI1756,
+        {{ 0, 0, 0, 0}, {32, PCI1756_IDI, 2, 0}},
+        {{ 0, 0, 0, 0}, {32, PCI1756_IDO, 2, 0}},
+        {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
+         { 4, PCI175x_BOARDID, 1, SDF_INTERNAL},
+        IO_16b
+       },
+       {"pci1760", PCI_VENDOR_ID_ADVANTECH, 0x1760, PCIDIO_MAINREG,
+        TYPE_PCI1760,
+        {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},        // This card have own setup work
+        {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
+        {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
+         { 0, 0, 0, 0},
+        IO_8b
+       },
+       {"pci1762", PCI_VENDOR_ID_ADVANTECH, 0x1762, PCIDIO_MAINREG,
+        TYPE_PCI1762,
+        {{ 0, 0, 0, 0}, {16, PCI1762_IDI, 1, 0}},
+        {{ 0, 0, 0, 0}, {16, PCI1762_RO, 1, 0}},
+        {{ 0, 0, 0, 0}, { 0, 0, 0, 0}},
+         { 4, PCI1762_BOARDID, 1, SDF_INTERNAL},
+        IO_16b
+       }
+};
+
+#define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))
+
+static comedi_driver driver_pci_dio={
+       driver_name:    "adv_pci_dio",
+       module:         THIS_MODULE,
+       attach:         pci_dio_attach,
+       detach:         pci_dio_detach,
+       num_names:      n_boardtypes,
+       board_name:     boardtypes,
+       offset:         sizeof(boardtype),
+};
+typedef struct pci_dio_private_st pci_dio_private;
+struct pci_dio_private_st {
+       pci_dio_private         *prev;          // previous private struct
+       pci_dio_private         *next;          // next private struct
+       struct pci_dev          *pcidev;        // pointer to board's pci_dev
+       char                    valid;          // card is usable
+       char                    enabled;        // PCI card is enabled
+       char                    GlobalIrqEnabled;// 1= any IRQ source is enabled
+       // PCI-1760 specific data
+       unsigned char           IDICntEnable;   // counter's counting enable status
+       unsigned char           IDICntOverEnable;// counter's overflow interrupts enable status
+       unsigned char           IDICntMatchEnable;// counter's match interrupts enable status
+       unsigned char           IDICntEdge;     // counter's count edge value (bit=0 - rising, =1 - falling)
+       unsigned short          CntResValue[8]; // counters' reset value
+       unsigned short          CntMatchValue[8];// counters' match interrupt value
+       unsigned char           IDIFiltersEn;   // IDI's digital filters enable status
+       unsigned char           IDIPatMatchEn;  // IDI's pattern match enable status
+       unsigned char           IDIPatMatchValue;// IDI's pattern match value
+       unsigned short          IDIFiltrLow[8]; // IDI's filter value low signal
+       unsigned short          IDIFiltrHigh[8];// IDI's filter value high signal
+};
+
+static pci_dio_private *pci_priv=NULL; /* list of allocated cards */
+
+#define devpriv ((pci_dio_private *)dev->private)
+#define this_board ((boardtype *)dev->board_ptr)
+
+/*
+==============================================================================
+*/
+static int pci_dio_insn_bits_di_b(comedi_device *dev, comedi_subdevice *s, 
+       comedi_insn *insn, lsampl_t *data)
+{
+       diosubd_data *d=(diosubd_data *)s->private;
+       int i;
+       
+       data[1]=0;
+       for (i=0; i<d->regs;i++) {
+               data[1]|=inb(dev->iobase+d->addr+i)<<(8*i);
+       }
+
+       return 2;
+}
+
+/*
+==============================================================================
+*/
+static int pci_dio_insn_bits_di_w(comedi_device *dev, comedi_subdevice *s, 
+       comedi_insn *insn, lsampl_t *data)
+{
+       diosubd_data *d=(diosubd_data *)s->private;
+       int i;
+       
+       data[1]=0;
+       for (i=0; i<d->regs; i++)
+               data[1]|=inw(dev->iobase+d->addr+2*i)<<(16*i);
+
+       return 2;
+}
+
+/*
+==============================================================================
+*/
+static int pci_dio_insn_bits_do_b(comedi_device *dev, comedi_subdevice *s, 
+       comedi_insn *insn, lsampl_t *data)
+{
+       diosubd_data *d=(diosubd_data *)s->private;
+       int i;
+
+       if (data[0]) {
+               s->state &= ~data[0];
+               s->state |= (data[0]&data[1]);
+               for (i=0; i<d->regs; i++)
+                       outb((s->state>>(8*i))&0xff, dev->iobase+d->addr+i);
+       }
+       data[1] = s->state;
+
+       return 2;
+}
+
+/*
+==============================================================================
+*/
+static int pci_dio_insn_bits_do_w(comedi_device *dev, comedi_subdevice *s, 
+       comedi_insn *insn,lsampl_t *data)
+{
+       diosubd_data *d=(diosubd_data *)s->private;
+       int i;
+
+       if (data[0]) {
+               s->state &= ~data[0];
+               s->state |= (data[0]&data[1]);
+               for (i=0; i<d->regs; i++)
+                   outw((s->state>>(16*i))&0xffff, dev->iobase+d->addr+2*i);
+       }
+       data[1] = s->state;
+
+       return 2;
+}
+
+
+/*
+==============================================================================
+*/
+static int pci1760_mbxrequest(comedi_device *dev, comedi_subdevice *s,
+       unsigned char *omb, unsigned char *imb, 
+       int repeats)
+{
+       int cnt, tout, ok=0;
+       
+       for (cnt=0; cnt<repeats; cnt++) {
+               outb(omb[0], dev->iobase+OMB0);
+               outb(omb[1], dev->iobase+OMB1);
+               outb(omb[2], dev->iobase+OMB2);
+               outb(omb[3], dev->iobase+OMB3);
+               for(tout=0; tout<251; tout++) {
+                       if ((imb[2]=inb(dev->iobase+IMB2))==omb[2]) {
+                               imb[0]=inb(dev->iobase+IMB0);
+                               imb[1]=inb(dev->iobase+IMB1);
+                               imb[3]=inb(dev->iobase+IMB3);
+                               ok=1;
+                               break;
+                       }
+                       comedi_udelay(1);
+               }
+               if (ok) return 0;
+       }
+       
+       comedi_error(dev, "PCI-1760 mailbox request timeout!");
+       return -ETIME;
+}
+
+/*
+==============================================================================
+*/
+static int pci1760_insn_bits_di(comedi_device *dev, comedi_subdevice *s, 
+       comedi_insn *insn, lsampl_t *data)
+{
+       int ret;
+       unsigned char omb[4]={
+               0x00, 
+               0x00, 
+               CMD_ClearIMB2, 
+               0x00};
+       unsigned char imb[4];
+       
+       if (!(ret=pci1760_mbxrequest(dev, s, omb, imb, OMBCMD_RETRY)))
+               return ret;
+
+       data[1]=imb[3];
+
+       return 2;
+}
+
+/*
+==============================================================================
+*/
+static int pci1760_insn_read_di(comedi_device *dev, comedi_subdevice *s, 
+       comedi_insn *insn, lsampl_t *data)
+{
+       int ret, n;
+       unsigned char omb[4]={
+               0x00, 
+               0x00, 
+               CMD_ClearIMB2, 
+               0x00};
+       unsigned char imb[4];
+       
+       for (n=0; n<insn->n; n++) {
+               if (!(ret=pci1760_mbxrequest(dev, s, omb, imb, OMBCMD_RETRY)))
+                       return ret;
+               data[n] = imb[3];
+       }
+
+       return n;
+}
+
+/*
+==============================================================================
+*/
+static int pci1760_insn_bits_do(comedi_device *dev, comedi_subdevice *s, 
+       comedi_insn *insn, lsampl_t *data)
+{
+       int ret;
+       unsigned char omb[4]={
+               0x00, 
+               0x00, 
+               CMD_SetRelaysOutput, 
+               0x00};
+       unsigned char imb[4];
+
+       if (data[0]) {
+               s->state &= ~data[0];
+               s->state |= (data[0]&data[1]);
+               omb[0] = s->state;
+               if (!(ret=pci1760_mbxrequest(dev, s, omb, imb, OMBCMD_RETRY)))
+                       return ret;
+       }
+       data[1] = s->state;
+
+       return 2;
+}
+
+/*
+==============================================================================
+*/
+static int pci1760_insn_cnt_read(comedi_device *dev, comedi_subdevice *s, 
+       comedi_insn *insn, lsampl_t *data)
+{
+       int ret, n;
+       unsigned char omb[4]={
+               CR_CHAN(insn->chanspec)&0x07, 
+               0x00, 
+               CMD_GetIDICntCurValue, 
+               0x00};
+       unsigned char imb[4];
+
+       for (n=0; n<insn->n; n++) {
+               if (!(ret=pci1760_mbxrequest(dev, s, omb, imb, OMBCMD_RETRY)))
+                       return ret;
+               data[n] = (imb[1]<<8) + imb[0];
+       }
+
+       return n;
+}
+
+/*
+==============================================================================
+*/
+static int pci1760_insn_cnt_write(comedi_device *dev, comedi_subdevice *s, 
+       comedi_insn *insn, lsampl_t *data)
+{
+       int ret;
+       unsigned char chan=CR_CHAN(insn->chanspec)&0x07;
+       unsigned char bitmask=1<<chan;
+       unsigned char omb[4]={
+               data[0]&0xff, 
+               (data[0]>>8)&0xff, 
+               CMD_SetIDI0CntResetValue+chan, 
+               0x00};
+       unsigned char imb[4];
+
+       if (devpriv->CntResValue[chan] != (data[0]&0xffff)) { // Set reset value if different
+               if (!(ret=pci1760_mbxrequest(dev, s, omb, imb, OMBCMD_RETRY)))
+                       return ret;
+               devpriv->CntResValue[chan] = data[0]&0xffff;
+       }
+
+       omb[0] = bitmask;       // reset counter to it reset value
+       omb[2] = CMD_ResetIDICounters;
+       if (!(ret=pci1760_mbxrequest(dev, s, omb, imb, OMBCMD_RETRY)))
+               return ret;
+
+       if (!(bitmask & devpriv->IDICntEnable)) {       // start counter if it don't run
+               omb[0] = bitmask;
+               omb[2] = CMD_EnableIDICounters;
+               if (!(ret=pci1760_mbxrequest(dev, s, omb, imb, OMBCMD_RETRY)))
+                       return ret;
+               devpriv->IDICntEnable |= bitmask;
+       }
+       return 1;
+}
+
+/*
+==============================================================================
+*/
+static int pci1760_reset(comedi_device *dev)
+{
+       int i;
+       unsigned char omb[4]={0x00, 0x00, 0x00, 0x00 };
+       unsigned char imb[4];
+
+       outb(0, dev->iobase+INTCSR0);   // disable IRQ
+       outb(0, dev->iobase+INTCSR1);
+       outb(0, dev->iobase+INTCSR2);
+       outb(0, dev->iobase+INTCSR3);
+       devpriv->GlobalIrqEnabled = 0;
+
+       omb[0] = 0x00;
+       omb[2] = CMD_SetRelaysOutput;   // reset relay outputs
+       pci1760_mbxrequest(dev, NULL, omb, imb, OMBCMD_RETRY);
+       
+       omb[0] = 0x00;
+       omb[2] = CMD_EnableIDICounters; // disable IDI up counters
+       pci1760_mbxrequest(dev, NULL, omb, imb, OMBCMD_RETRY);
+       devpriv->IDICntEnable = 0;
+
+       omb[0] = 0x00;
+       omb[2] = CMD_OverflowIDICounters;       // disable counters overflow interrupts
+       pci1760_mbxrequest(dev, NULL, omb, imb, OMBCMD_RETRY);
+       devpriv->IDICntOverEnable = 0;
+
+       omb[0] = 0x00;
+       omb[2] = CMD_MatchIntIDICounters;       // disable counters match value interrupts
+       pci1760_mbxrequest(dev, NULL, omb, imb, OMBCMD_RETRY);
+       devpriv->IDICntMatchEnable = 0;
+
+       omb[0] = 0x00;
+       omb[1] = 0x80;
+       for (i=0; i<8; i++) {   // set IDI up counters match value
+               omb[2] = CMD_SetIDI0CntMatchValue+i;
+               pci1760_mbxrequest(dev, NULL, omb, imb, OMBCMD_RETRY);
+               devpriv->CntMatchValue[i] = 0x8000;
+       }
+
+       omb[0] = 0x00;
+       omb[1] = 0x00;
+       for (i=0; i<8; i++) {   // set IDI up counters reset value
+               omb[2] = CMD_SetIDI0CntResetValue+i;
+               pci1760_mbxrequest(dev, NULL, omb, imb, OMBCMD_RETRY);
+               devpriv->CntResValue[i] = 0x0000;
+       }
+       
+       omb[0] = 0xff;
+       omb[2] = CMD_ResetIDICounters;  // reset IDI up counters to reset values
+       pci1760_mbxrequest(dev, NULL, omb, imb, OMBCMD_RETRY);
+
+       omb[0] = 0x00;
+       omb[2] = CMD_EdgeIDICounters;   // set IDI up counters count edge
+       pci1760_mbxrequest(dev, NULL, omb, imb, OMBCMD_RETRY);
+       devpriv->IDICntEdge = 0x00;
+
+       omb[0] = 0x00;
+       omb[2] = CMD_EnableIDIFilters;  // disable all digital in filters
+       pci1760_mbxrequest(dev, NULL, omb, imb, OMBCMD_RETRY);
+       devpriv->IDIFiltersEn = 0x00;
+
+       omb[0] = 0x00;
+       omb[2] = CMD_EnableIDIPatternMatch;     // disable pattern matching
+       pci1760_mbxrequest(dev, NULL, omb, imb, OMBCMD_RETRY);
+       devpriv->IDIPatMatchEn = 0x00;
+
+       omb[0] = 0x00;
+       omb[2] = CMD_SetIDIPatternMatch;        // set pattern match value
+       pci1760_mbxrequest(dev, NULL, omb, imb, OMBCMD_RETRY);
+       devpriv->IDIPatMatchValue = 0x00;
+
+       return 0;
+}
+
+/* 
+==============================================================================
+*/
+static int pci_dio_reset(comedi_device *dev)
+{
+       DPRINTK("adv_pci_dio EDBG: BGN: pci171x_reset(...)\n");
+       
+       switch (this_board->cardtype) {
+       case TYPE_PCI1730:
+               outb(0, dev->iobase+PCI1730_DO);        // clear outputs
+               outb(0, dev->iobase+PCI1730_DO+1);
+               outb(0, dev->iobase+PCI1730_IDO);
+               outb(0, dev->iobase+PCI1730_IDO+1);
+               /* NO break there! */
+       case TYPE_PCI1733:
+               outb(0, dev->iobase+PCI1730_3_INT_EN);  // disable interrupts
+               outb(0x0f, dev->iobase+PCI1730_3_INT_CLR);// clear interrupts
+               outb(0, dev->iobase+PCI1730_3_INT_RF);  // set rising edge trigger
+               break;
+       case TYPE_PCI1734:
+               outb(0, dev->iobase+PCI1734_IDO);       // clear outputs
+               outb(0, dev->iobase+PCI1734_IDO+1);
+               outb(0, dev->iobase+PCI1734_IDO+2);
+               outb(0, dev->iobase+PCI1734_IDO+3);
+               break;
+       case TYPE_PCI1750:
+       case TYPE_PCI1751:
+               outb(0x88, dev->iobase+PCI1750_ICR);    // disable & clear interrupts
+               break;
+       case TYPE_PCI1752:
+               outw(0, dev->iobase+PCI1752_6_CFC);     // disable channel freeze function
+               outw(0, dev->iobase+PCI1752_IDO);       // clear outputs
+               outw(0, dev->iobase+PCI1752_IDO+2);
+               outw(0, dev->iobase+PCI1752_IDO2);
+               outw(0, dev->iobase+PCI1752_IDO2+2);
+               break;
+       case TYPE_PCI1753E:
+               outb(0x88, dev->iobase+PCI1753E_ICR0);  // disable & clear interrupts
+               outb(0x80, dev->iobase+PCI1753E_ICR1);
+               outb(0x80, dev->iobase+PCI1753E_ICR2);
+               outb(0x80, dev->iobase+PCI1753E_ICR3);
+               /* NO break there! */
+       case TYPE_PCI1753:
+               outb(0x88, dev->iobase+PCI1753_ICR0);   // disable & clear interrupts
+               outb(0x80, dev->iobase+PCI1753_ICR1);
+               outb(0x80, dev->iobase+PCI1753_ICR2);
+               outb(0x80, dev->iobase+PCI1753_ICR3);
+               break;
+       case TYPE_PCI1754:
+               outw(0x08, dev->iobase+PCI1754_6_ICR0); // disable and clear interrupts 
+               outw(0x08, dev->iobase+PCI1754_6_ICR1);
+               outw(0x08, dev->iobase+PCI1754_ICR2);
+               outw(0x08, dev->iobase+PCI1754_ICR3);
+               break;
+       case TYPE_PCI1756:
+               outw(0, dev->iobase+PCI1752_6_CFC);     // disable channel freeze function
+               outw(0x08, dev->iobase+PCI1754_6_ICR0); // disable and clear interrupts 
+               outw(0x08, dev->iobase+PCI1754_6_ICR1);
+               outw(0, dev->iobase+PCI1756_IDO);       // clear outputs
+               outw(0, dev->iobase+PCI1756_IDO+2);
+               break;
+       case TYPE_PCI1760:
+               pci1760_reset(dev);
+               break;
+       case TYPE_PCI1762:
+               outw(0x0101, dev->iobase+PCI1750_ICR);  // disable & clear interrupts
+               break;
+       }
+
+       DPRINTK("adv_pci_dio EDBG: END: pci171x_reset(...)\n");
+
+       return 0;
+}
+
+/* 
+==============================================================================
+*/
+static int pci1760_attach(comedi_device *dev, comedi_devconfig *it)
+{
+       comedi_subdevice *s;
+       int subdev=0;
+
+       s = dev->subdevices + subdev;
+       s->type = COMEDI_SUBD_DI;
+       s->subdev_flags = SDF_READABLE|SDF_GROUND|SDF_COMMON;
+       s->n_chan = 8;
+       s->maxdata = 1;
+       s->len_chanlist = 8;
+       s->range_table = &range_digital;
+       s->io_bits=0;           /* all bits input */
+       s->insn_read=pci1760_insn_read_di;
+       s->insn_bits=pci1760_insn_bits_di;
+       subdev++;       
+
+       s = dev->subdevices + subdev;
+       s->type = COMEDI_SUBD_DO;
+       s->subdev_flags = SDF_WRITABLE|SDF_GROUND|SDF_COMMON;
+       s->n_chan = 8;
+       s->maxdata = 1;
+       s->len_chanlist = 8;
+       s->range_table = &range_digital;
+       s->io_bits=255; /* all bits output */
+       s->state=0;
+       s->insn_bits=pci1760_insn_bits_do;
+       subdev++;       
+
+       s = dev->subdevices + subdev;
+       s->type = COMEDI_SUBD_TIMER;
+       s->subdev_flags = SDF_WRITABLE|SDF_LSAMPL;
+       s->n_chan = 2;
+       s->maxdata = 0xffffffff;
+       s->len_chanlist = 2;
+//     s->insn_config=pci1760_insn_pwm_cfg;
+       subdev++;       
+
+       s = dev->subdevices + subdev;
+       s->type = COMEDI_SUBD_COUNTER;
+       s->subdev_flags = SDF_READABLE|SDF_WRITABLE;
+       s->n_chan = 8;
+       s->maxdata = 0xffff;
+       s->len_chanlist = 8;
+       s->insn_read=pci1760_insn_cnt_read;
+       s->insn_write=pci1760_insn_cnt_write;
+//     s->insn_config=pci1760_insn_cnt_cfg;
+       subdev++;       
+
+       return 0;
+}
+
+/* 
+==============================================================================
+*/
+static int pci_dio_add_di(comedi_device *dev, comedi_subdevice *s, 
+       diosubd_data *d, int subdev)
+{
+       s->type = COMEDI_SUBD_DI;
+       s->subdev_flags = SDF_READABLE|SDF_GROUND|SDF_COMMON|d->specflags;
+       if (d->chans>16) s->subdev_flags |= SDF_LSAMPL;
+       s->n_chan = d->chans;
+       s->maxdata = 1;
+       s->len_chanlist = d->chans;
+       s->range_table = &range_digital;
+       s->io_bits=0;           /* all bits input */
+       switch (this_board->io_access) {
+       case IO_8b:
+               s->insn_bits=pci_dio_insn_bits_di_b;
+               break;
+       case IO_16b:
+               s->insn_bits=pci_dio_insn_bits_di_w;
+               break;
+       }
+       s->private=d;
+
+       return 0;
+}
+
+/* 
+==============================================================================
+*/
+static int pci_dio_add_do(comedi_device *dev, comedi_subdevice *s, 
+       diosubd_data *d, int subdev)
+{
+       s->type = COMEDI_SUBD_DO;
+       s->subdev_flags = SDF_WRITABLE|SDF_GROUND|SDF_COMMON;
+       if (d->chans>16) s->subdev_flags |= SDF_LSAMPL;
+       s->n_chan = d->chans;
+       s->maxdata = 1;
+       s->len_chanlist = d->chans;
+       s->range_table = &range_digital;
+       s->io_bits=(1 << d->chans)-1;   /* all bits output */
+       s->state=0;
+       switch (this_board->io_access) {
+       case IO_8b:
+               s->insn_bits=pci_dio_insn_bits_do_b;
+               break;
+       case IO_16b:
+               s->insn_bits=pci_dio_insn_bits_do_w;
+               break;
+       }
+       s->private=d;
+
+       return 0;
+}
+
+/* 
+==============================================================================
+*/
+static int CheckAndAllocCard(comedi_device *dev, comedi_devconfig *it,
+       struct pci_dev* pcidev)
+{
+       pci_dio_private *pr, *prev;
+       
+       if (!pci_priv) {        // well, first card in system
+               pci_priv=devpriv;
+               return 1;
+       }
+       
+       for (pr=pci_priv, prev=NULL; pr!=NULL; prev=pr, pr=pr->next)
+               if (pr->pcidev==pcidev) {
+                       if (it->options[0]||it->options[1]) {
+                               if ((pr->pcidev->bus->number==it->options[0])&&
+                                   (PCI_SLOT(pr->pcidev->devfn)==it->options[1])) {
+                                       rt_printk(", Error: Card on requested position is used!\n");
+                                       return 2;
+                               }
+                       }
+                       return 0;       // this card is used, look for another
+               }
+
+       if (prev) { devpriv->prev=prev; }
+               { pci_priv=devpriv; }
+       
+       return 1;
+}
+
+/* 
+==============================================================================
+*/
+static int pci_dio_attach(comedi_device *dev, comedi_devconfig *it)
+{
+       comedi_subdevice *s;
+       int ret, subdev, n_subdevices, i, j, iobase, found=0;
+       struct pci_dev* pcidev;
+
+       rt_printk("comedi%d: adv_pci_dio: board=%s",
+               dev->minor, this_board->name);
+
+       if ((ret=alloc_private(dev,sizeof(pci_dio_private)))<0) {
+               rt_printk(", Error: Cann't allocate private memory!\n");
+               return -ENOMEM;
+       }
+
+       pci_for_each_dev(pcidev) {
+               if ((pcidev->vendor!=this_board->vendor_id)||
+                   (pcidev->device!=this_board->device_id))
+                       continue;
+               if (it->options[0]||it->options[1]) {
+                       if ((pcidev->bus->number!=it->options[0])||
+                           (PCI_SLOT(pcidev->devfn)!=it->options[1])) {
+                               continue;
+                       }
+               }
+               ret=CheckAndAllocCard(dev, it, pcidev); 
+               if (ret==1) { found=1; break; }
+               if (ret>1) { 
+                       pci_dio_detach(dev);
+                       return -EIO;
+               }
+       }
+
+       if (!found) {
+               rt_printk(", Error: Requested type of the card was not found!\n");
+               pci_dio_detach(dev);
+               return -EIO;
+       }
+       
+       iobase=pci_resource_start(pcidev, this_board->main_pci_region);
+       rt_printk(", b:s:f=%d:%d:%d, io=0x%4x",
+               pcidev->bus->number, PCI_SLOT(pcidev->devfn), PCI_FUNC(pcidev->devfn),
+               iobase);
+       
+       if (pci_request_regions(pcidev, driver_pci_dio.driver_name)) {
+               pci_dio_detach(dev);
+               rt_printk(", Error: Cann't allocate PCI device!\n");
+               return -EIO;
+       }
+       devpriv->pcidev=pcidev;
+       
+       if (pci_enable_device(pcidev)) {
+               pci_dio_detach(dev);
+               rt_printk(", Error: Cann't enable PCI device!\n");
+               return -EIO;
+       }
+       devpriv->enabled=1;
+       
+       dev->iobase=iobase;
+       dev->board_name=this_board->name;
+       
+       if (this_board->cardtype==TYPE_PCI1760) {
+               n_subdevices=4; // 8 IDI, 8 IDO, 2 PWM, 8 CNT 
+       } else {
+               n_subdevices=0;
+               for (i=0; i<MAX_DI_SUBDEVS; i++)
+                       if (this_board->sdi[i].chans) n_subdevices++;
+               for (i=0; i<MAX_DO_SUBDEVS; i++)
+                       if (this_board->sdo[i].chans) n_subdevices++;
+               for (i=0; i<MAX_DIO_SUBDEVG; i++)
+                       n_subdevices+=this_board->sdio[i].regs;
+               if (this_board->boardid.chans) n_subdevices++;
+       }
+
+        if((ret=alloc_subdevices(dev, n_subdevices))<0) {
+               rt_printk(", Error: Cann't allocate subdevice memory!\n");
+               pci_dio_detach(dev);
+               return ret;
+       }
+
+       rt_printk(".\n");
+       
+       subdev=0;
+       
+       for (i=0; i<MAX_DI_SUBDEVS; i++)
+               if (this_board->sdi[i].chans) {
+                       s = dev->subdevices + subdev;
+                       pci_dio_add_di(dev, s, &this_board->sdi[i], subdev);
+                       subdev++;
+               }
+
+       for (i=0; i<MAX_DO_SUBDEVS; i++)
+               if (this_board->sdo[i].chans) {
+                       s = dev->subdevices + subdev;
+                       pci_dio_add_do(dev, s, &this_board->sdo[i], subdev);
+                       subdev++;
+               }
+
+       for (i=0; i<MAX_DIO_SUBDEVG; i++)
+               for (j=0; j<this_board->sdio[i].regs; j++) {
+                       s = dev->subdevices + subdev;
+                       subdev_8255_init(dev, s, NULL, 
+                               dev->iobase+this_board->sdio[i].addr+SIZE_8255*j);
+                       subdev++;
+               }
+
+       if (this_board->boardid.chans) {
+               s = dev->subdevices + subdev;
+               s->type = COMEDI_SUBD_DI;
+               pci_dio_add_di(dev, s, &this_board->boardid, subdev);
+               subdev++;
+       }
+       
+       if (this_board->cardtype==TYPE_PCI1760) 
+               pci1760_attach(dev, it);
+               
+       devpriv->valid=1;
+
+       pci_dio_reset(dev);
+
+       return 0;
+}
+
+/* 
+==============================================================================
+*/
+static int pci_dio_detach(comedi_device *dev)
+{
+       int i;
+       comedi_subdevice *s;
+               
+       if (dev->private) {
+               if (devpriv->valid) {
+                       pci_dio_reset(dev);
+               }
+
+               for (i=0; i<dev->n_subdevices; i++) {
+                       s=dev->subdevices+i;
+                       s->private=NULL;
+               }
+
+               if (devpriv->enabled)
+                       pci_disable_device(devpriv->pcidev);
+
+               if (devpriv->pcidev)
+                       pci_release_regions(devpriv->pcidev);
+               
+               if (devpriv->prev) { devpriv->prev->next=devpriv->next; }
+                                   { pci_priv=devpriv->next; }
+               if (devpriv->next) { devpriv->next->prev=devpriv->prev; }
+
+       }       
+
+       return 0;
+}
+
+/* 
+==============================================================================
+*/
+COMEDI_INITCLEANUP(driver_pci_dio);
+/* 
+==============================================================================
+*/