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