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