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