ii_pci20kc.c revision 0e99a2b9a2367e47bebf8bdbb5aa0ce14d586968
1/* 2 * comedi/drivers/ii_pci20kc.c 3 * Driver for Intelligent Instruments PCI-20001C carrier board 4 * and modules. 5 * 6 * Copyright (C) 2000 Markus Kempf <kempf@matsci.uni-sb.de> 7 * with suggestions from David Schleef 8 * 16.06.2000 9 * 10 * Linux device driver for COMEDI 11 * Intelligent Instrumentation 12 * PCI-20001 C-2A Carrier Board 13 * PCI-20341 M-1A 16-Bit analog input module 14 * - differential 15 * - range (-5V - +5V) 16 * - 16 bit 17 * PCI-20006 M-2 16-Bit analog output module 18 * - ranges (-10V - +10V) (0V - +10V) (-5V - +5V) 19 * - 16 bit 20 * 21 * only ONE PCI-20341 module possible 22 * only ONE PCI-20006 module possible 23 * no extern trigger implemented 24 * 25 * NOT WORKING (but soon) only 4 on-board differential channels supported 26 * NOT WORKING (but soon) only ONE di-port and ONE do-port supported instead of 4 digital ports 27 * di-port == Port 0 28 * do-port == Port 1 29 * 30 * The state of this driver is only a starting point for a complete 31 * COMEDI-driver. The final driver should support all features of the 32 * carrier board and modules. 33 * 34 * The test configuration: 35 * 36 * kernel 2.2.14 with RTAI v1.2 and patch-2.2.14rthal2 37 * COMEDI 0.7.45 38 * COMEDILIB 0.7.9 39 * 40 */ 41/* 42Driver: ii_pci20kc 43Description: Intelligent Instruments PCI-20001C carrier board 44Author: Markus Kempf <kempf@matsci.uni-sb.de> 45Devices: [Intelligent Instrumentation] PCI-20001C (ii_pci20kc) 46Status: works 47 48Supports the PCI-20001 C-2a Carrier board, and could probably support 49the other carrier boards with small modifications. Modules supported 50are: 51 PCI-20006 M-2 16-bit analog output module 52 PCI-20341 M-1A 16-bit analog input module 53 54Options: 55 0 Board base address 56 1 IRQ 57 2 first option for module 1 58 3 second option for module 1 59 4 first option for module 2 60 5 second option for module 2 61 6 first option for module 3 62 7 second option for module 3 63 64options for PCI-20006M: 65 first: Analog output channel 0 range configuration 66 0 bipolar 10 (-10V -- +10V) 67 1 unipolar 10 (0V -- +10V) 68 2 bipolar 5 (-5V -- 5V) 69 second: Analog output channel 1 range configuration 70 71options for PCI-20341M: 72 first: Analog input gain configuration 73 0 1 74 1 10 75 2 100 76 3 200 77*/ 78 79/* XXX needs to use ioremap() for compatibility with 2.4 kernels. Should also 80 * check_mem_region() etc. - fmhess */ 81 82#include "../comedidev.h" 83 84#define PCI20000_ID 0x1d 85#define PCI20341_ID 0x77 86#define PCI20006_ID 0xe3 87#define PCI20xxx_EMPTY_ID 0xff 88 89#define PCI20000_OFFSET 0x100 90#define PCI20000_MODULES 3 91 92#define PCI20000_DIO_0 0x80 93#define PCI20000_DIO_1 0x81 94#define PCI20000_DIO_2 0xc0 95#define PCI20000_DIO_3 0xc1 96#define PCI20000_DIO_CONTROL_01 0x83 /* port 0, 1 control */ 97#define PCI20000_DIO_CONTROL_23 0xc3 /* port 2, 3 control */ 98#define PCI20000_DIO_BUFFER 0x82 /* buffer direction and enable */ 99#define PCI20000_DIO_EOC 0xef /* even port, control output */ 100#define PCI20000_DIO_OOC 0xfd /* odd port, control output */ 101#define PCI20000_DIO_EIC 0x90 /* even port, control input */ 102#define PCI20000_DIO_OIC 0x82 /* odd port, control input */ 103#define DIO_CAND 0x12 /* and bit 1, bit 4 of control */ 104#define DIO_BE 0x01 /* buffer: port enable */ 105#define DIO_BO 0x04 /* buffer: output */ 106#define DIO_BI 0x05 /* buffer: input */ 107#define DIO_PS_0 0x00 /* buffer: port shift 0 */ 108#define DIO_PS_1 0x01 /* buffer: port shift 1 */ 109#define DIO_PS_2 0x04 /* buffer: port shift 2 */ 110#define DIO_PS_3 0x05 /* buffer: port shift 3 */ 111 112#define PCI20006_LCHAN0 0x0d 113#define PCI20006_STROBE0 0x0b 114#define PCI20006_LCHAN1 0x15 115#define PCI20006_STROBE1 0x13 116 117#define PCI20341_INIT 0x04 118#define PCI20341_REPMODE 0x00 /* single shot mode */ 119#define PCI20341_PACER 0x00 /* Hardware Pacer disabled */ 120#define PCI20341_CHAN_NR 0x04 /* number of input channels */ 121#define PCI20341_CONFIG_REG 0x10 122#define PCI20341_MOD_STATUS 0x01 123#define PCI20341_OPT_REG 0x11 124#define PCI20341_SET_TIME_REG 0x15 125#define PCI20341_LCHAN_ADDR_REG 0x13 126#define PCI20341_CHAN_LIST 0x80 127#define PCI20341_CC_RESET 0x1b 128#define PCI20341_CHAN_RESET 0x19 129#define PCI20341_SOFT_PACER 0x04 130#define PCI20341_STATUS_REG 0x12 131#define PCI20341_LDATA 0x02 132#define PCI20341_DAISY_CHAIN 0x20 /* On-board inputs only */ 133#define PCI20341_MUX 0x04 /* Enable on-board MUX */ 134#define PCI20341_SCANLIST 0x80 /* Channel/Gain Scan List */ 135 136typedef union { 137 void *iobase; 138 struct { 139 void *iobase; 140 const struct comedi_lrange *ao_range_list[2]; /* range of channels of ao module */ 141 unsigned int last_data[2]; 142 } pci20006; 143 struct { 144 void *iobase; 145 int timebase; 146 int settling_time; 147 int ai_gain; 148 } pci20341; 149} pci20xxx_subdev_private; 150 151struct pci20xxx_private { 152 153 void *ioaddr; 154 pci20xxx_subdev_private subdev_private[PCI20000_MODULES]; 155}; 156 157 158#define devpriv ((struct pci20xxx_private *)dev->private) 159#define CHAN (CR_CHAN(it->chanlist[0])) 160 161static int pci20xxx_attach(struct comedi_device * dev, struct comedi_devconfig * it); 162static int pci20xxx_detach(struct comedi_device * dev); 163 164static struct comedi_driver driver_pci20xxx = { 165 driver_name:"ii_pci20kc", 166 module:THIS_MODULE, 167 attach:pci20xxx_attach, 168 detach:pci20xxx_detach, 169}; 170 171static int pci20006_init(struct comedi_device * dev, struct comedi_subdevice * s, 172 int opt0, int opt1); 173static int pci20341_init(struct comedi_device * dev, struct comedi_subdevice * s, 174 int opt0, int opt1); 175static int pci20xxx_dio_init(struct comedi_device * dev, struct comedi_subdevice * s); 176 177/* 178 options[0] Board base address 179 options[1] IRQ 180 options[2] first option for module 1 181 options[3] second option for module 1 182 options[4] first option for module 2 183 options[5] second option for module 2 184 options[6] first option for module 3 185 options[7] second option for module 3 186 187 options for PCI-20341M: 188 first Analog input gain configuration 189 0 == 1 190 1 == 10 191 2 == 100 192 3 == 200 193 194 options for PCI-20006M: 195 first Analog output channel 0 range configuration 196 0 == bipolar 10 (-10V -- +10V) 197 1 == unipolar 10V (0V -- +10V) 198 2 == bipolar 5V (-5V -- +5V) 199 second Analog output channel 1 range configuration 200 0 == bipolar 10 (-10V -- +10V) 201 1 == unipolar 10V (0V -- +10V) 202 2 == bipolar 5V (-5V -- +5V) 203*/ 204static int pci20xxx_attach(struct comedi_device * dev, struct comedi_devconfig * it) 205{ 206 unsigned char i; 207 int ret; 208 int id; 209 struct comedi_subdevice *s; 210 pci20xxx_subdev_private *sdp; 211 212 if ((ret = alloc_subdevices(dev, 1 + PCI20000_MODULES)) < 0) 213 return ret; 214 if ((ret = alloc_private(dev, sizeof(struct pci20xxx_private))) < 0) 215 return ret; 216 217 devpriv->ioaddr = (void *)(unsigned long)it->options[0]; 218 dev->board_name = "pci20kc"; 219 220 /* Check PCI-20001 C-2A Carrier Board ID */ 221 if ((readb(devpriv->ioaddr) & PCI20000_ID) != PCI20000_ID) { 222 printk("comedi%d: ii_pci20kc", dev->minor); 223 printk(" PCI-20001 C-2A Carrier Board at base=0x%p not found !\n", devpriv->ioaddr); 224 return -EINVAL; 225 } 226 printk("comedi%d:\n", dev->minor); 227 printk("ii_pci20kc: PCI-20001 C-2A at base=0x%p\n", devpriv->ioaddr); 228 229 for (i = 0; i < PCI20000_MODULES; i++) { 230 s = dev->subdevices + i; 231 id = readb(devpriv->ioaddr + (i + 1) * PCI20000_OFFSET); 232 s->private = devpriv->subdev_private + i; 233 sdp = s->private; 234 switch (id) { 235 case PCI20006_ID: 236 sdp->pci20006.iobase = 237 devpriv->ioaddr + (i + 1) * PCI20000_OFFSET; 238 pci20006_init(dev, s, it->options[2 * i + 2], 239 it->options[2 * i + 3]); 240 printk("comedi%d: ii_pci20kc", dev->minor); 241 printk(" PCI-20006 module in slot %d \n", i + 1); 242 break; 243 case PCI20341_ID: 244 sdp->pci20341.iobase = 245 devpriv->ioaddr + (i + 1) * PCI20000_OFFSET; 246 pci20341_init(dev, s, it->options[2 * i + 2], 247 it->options[2 * i + 3]); 248 printk("comedi%d: ii_pci20kc", dev->minor); 249 printk(" PCI-20341 module in slot %d \n", i + 1); 250 break; 251 default: 252 printk("ii_pci20kc: unknown module code 0x%02x in slot %d: module disabled\n", id, i); 253 /* fall through */ 254 case PCI20xxx_EMPTY_ID: 255 s->type = COMEDI_SUBD_UNUSED; 256 break; 257 } 258 } 259 260 /* initialize struct pci20xxx_private */ 261 pci20xxx_dio_init(dev, dev->subdevices + PCI20000_MODULES); 262 263 return 1; 264} 265 266static int pci20xxx_detach(struct comedi_device * dev) 267{ 268 printk("comedi%d: pci20xxx: remove\n", dev->minor); 269 270 return 0; 271} 272 273/* pci20006m */ 274 275static int pci20006_insn_read(struct comedi_device * dev, struct comedi_subdevice * s, 276 struct comedi_insn * insn, unsigned int * data); 277static int pci20006_insn_write(struct comedi_device * dev, struct comedi_subdevice * s, 278 struct comedi_insn * insn, unsigned int * data); 279 280static const struct comedi_lrange *pci20006_range_list[] = { 281 &range_bipolar10, 282 &range_unipolar10, 283 &range_bipolar5, 284}; 285 286static int pci20006_init(struct comedi_device * dev, struct comedi_subdevice * s, 287 int opt0, int opt1) 288{ 289 pci20xxx_subdev_private *sdp = s->private; 290 291 if (opt0 < 0 || opt0 > 2) 292 opt0 = 0; 293 if (opt1 < 0 || opt1 > 2) 294 opt1 = 0; 295 296 sdp->pci20006.ao_range_list[0] = pci20006_range_list[opt0]; 297 sdp->pci20006.ao_range_list[1] = pci20006_range_list[opt1]; 298 299 /* ao subdevice */ 300 s->type = COMEDI_SUBD_AO; 301 s->subdev_flags = SDF_WRITABLE; 302 s->n_chan = 2; 303 s->len_chanlist = 2; 304 s->insn_read = pci20006_insn_read; 305 s->insn_write = pci20006_insn_write; 306 s->maxdata = 0xffff; 307 s->range_table_list = sdp->pci20006.ao_range_list; 308 return 0; 309} 310 311static int pci20006_insn_read(struct comedi_device * dev, struct comedi_subdevice * s, 312 struct comedi_insn * insn, unsigned int * data) 313{ 314 pci20xxx_subdev_private *sdp = s->private; 315 316 data[0] = sdp->pci20006.last_data[CR_CHAN(insn->chanspec)]; 317 318 return 1; 319} 320 321static int pci20006_insn_write(struct comedi_device * dev, struct comedi_subdevice * s, 322 struct comedi_insn * insn, unsigned int * data) 323{ 324 pci20xxx_subdev_private *sdp = s->private; 325 int hi, lo; 326 unsigned int boarddata; 327 328 sdp->pci20006.last_data[CR_CHAN(insn->chanspec)] = data[0]; 329 boarddata = (((unsigned int)data[0] + 0x8000) & 0xffff); /* comedi-data -> board-data */ 330 lo = (boarddata & 0xff); 331 hi = ((boarddata >> 8) & 0xff); 332 333 switch (CR_CHAN(insn->chanspec)) { 334 case 0: 335 writeb(lo, sdp->iobase + PCI20006_LCHAN0); 336 writeb(hi, sdp->iobase + PCI20006_LCHAN0 + 1); 337 writeb(0x00, sdp->iobase + PCI20006_STROBE0); 338 break; 339 case 1: 340 writeb(lo, sdp->iobase + PCI20006_LCHAN1); 341 writeb(hi, sdp->iobase + PCI20006_LCHAN1 + 1); 342 writeb(0x00, sdp->iobase + PCI20006_STROBE1); 343 break; 344 default: 345 printk(" comedi%d: pci20xxx: ao channel Error!\n", dev->minor); 346 return -EINVAL; 347 } 348 349 return 1; 350} 351 352/* PCI20341M */ 353 354static int pci20341_insn_read(struct comedi_device * dev, struct comedi_subdevice * s, 355 struct comedi_insn * insn, unsigned int * data); 356 357static const int pci20341_timebase[] = { 0x00, 0x00, 0x00, 0x04 }; 358static const int pci20341_settling_time[] = { 0x58, 0x58, 0x93, 0x99 }; 359 360static const struct comedi_lrange range_bipolar0_5 = { 1, {BIP_RANGE(0.5)} }; 361static const struct comedi_lrange range_bipolar0_05 = { 1, {BIP_RANGE(0.05)} }; 362static const struct comedi_lrange range_bipolar0_025 = { 1, {BIP_RANGE(0.025)} }; 363 364static const struct comedi_lrange *const pci20341_ranges[] = { 365 &range_bipolar5, 366 &range_bipolar0_5, 367 &range_bipolar0_05, 368 &range_bipolar0_025, 369}; 370 371static int pci20341_init(struct comedi_device * dev, struct comedi_subdevice * s, 372 int opt0, int opt1) 373{ 374 pci20xxx_subdev_private *sdp = s->private; 375 int option; 376 377 /* options handling */ 378 if (opt0 < 0 || opt0 > 3) 379 opt0 = 0; 380 sdp->pci20341.timebase = pci20341_timebase[opt0]; 381 sdp->pci20341.settling_time = pci20341_settling_time[opt0]; 382 383 /* ai subdevice */ 384 s->type = COMEDI_SUBD_AI; 385 s->subdev_flags = SDF_READABLE; 386 s->n_chan = PCI20341_CHAN_NR; 387 s->len_chanlist = PCI20341_SCANLIST; 388 s->insn_read = pci20341_insn_read; 389 s->maxdata = 0xffff; 390 s->range_table = pci20341_ranges[opt0]; 391 392 option = sdp->pci20341.timebase | PCI20341_REPMODE; /* depends on gain, trigger, repetition mode */ 393 394 writeb(PCI20341_INIT, sdp->iobase + PCI20341_CONFIG_REG); /* initialize Module */ 395 writeb(PCI20341_PACER, sdp->iobase + PCI20341_MOD_STATUS); /* set Pacer */ 396 writeb(option, sdp->iobase + PCI20341_OPT_REG); /* option register */ 397 writeb(sdp->pci20341.settling_time, sdp->iobase + PCI20341_SET_TIME_REG); /* settling time counter */ 398 /* trigger not implemented */ 399 return 0; 400} 401 402static int pci20341_insn_read(struct comedi_device * dev, struct comedi_subdevice * s, 403 struct comedi_insn * insn, unsigned int * data) 404{ 405 pci20xxx_subdev_private *sdp = s->private; 406 unsigned int i = 0, j = 0; 407 int lo, hi; 408 unsigned char eoc; /* end of conversion */ 409 unsigned int clb; /* channel list byte */ 410 unsigned int boarddata; 411 412 writeb(1, sdp->iobase + PCI20341_LCHAN_ADDR_REG); /* write number of input channels */ 413 clb = PCI20341_DAISY_CHAIN | PCI20341_MUX | (sdp->pci20341.ai_gain << 3) 414 | CR_CHAN(insn->chanspec); 415 writeb(clb, sdp->iobase + PCI20341_CHAN_LIST); 416 writeb(0x00, sdp->iobase + PCI20341_CC_RESET); /* reset settling time counter and trigger delay counter */ 417 writeb(0x00, sdp->iobase + PCI20341_CHAN_RESET); 418 419 /* generate Pacer */ 420 421 for (i = 0; i < insn->n; i++) { 422 /* data polling isn't the niciest way to get the data, I know, 423 * but there are only 6 cycles (mean) and it is easier than 424 * the whole interrupt stuff 425 */ 426 j = 0; 427 readb(sdp->iobase + PCI20341_SOFT_PACER); /* generate Pacer */ 428 eoc = readb(sdp->iobase + PCI20341_STATUS_REG); 429 while ((eoc < 0x80) && j < 100) { /* poll Interrupt Flag */ 430 j++; 431 eoc = readb(sdp->iobase + PCI20341_STATUS_REG); 432 } 433 if (j >= 100) { 434 printk("comedi%d: pci20xxx: AI interrupt channel %i polling exit !\n", dev->minor, i); 435 return -EINVAL; 436 } 437 lo = readb(sdp->iobase + PCI20341_LDATA); 438 hi = readb(sdp->iobase + PCI20341_LDATA + 1); 439 boarddata = lo + 0x100 * hi; 440 data[i] = (short) ((boarddata + 0x8000) & 0xffff); /* board-data -> comedi-data */ 441 } 442 443 return i; 444} 445 446/* native DIO */ 447 448static void pci20xxx_dio_config(struct comedi_device * dev, struct comedi_subdevice * s); 449static int pci20xxx_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s, 450 struct comedi_insn * insn, unsigned int * data); 451static int pci20xxx_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s, 452 struct comedi_insn * insn, unsigned int * data); 453 454/* initialize struct pci20xxx_private */ 455static int pci20xxx_dio_init(struct comedi_device * dev, struct comedi_subdevice * s) 456{ 457 458 s->type = COMEDI_SUBD_DIO; 459 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 460 s->n_chan = 32; 461 s->insn_bits = pci20xxx_dio_insn_bits; 462 s->insn_config = pci20xxx_dio_insn_config; 463 s->maxdata = 1; 464 s->len_chanlist = 32; 465 s->range_table = &range_digital; 466 s->io_bits = 0; 467 468 /* digital I/O lines default to input on board reset. */ 469 pci20xxx_dio_config(dev, s); 470 471 return 0; 472} 473 474static int pci20xxx_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s, 475 struct comedi_insn * insn, unsigned int * data) 476{ 477 int mask, bits; 478 479 mask = 1 << CR_CHAN(insn->chanspec); 480 if (mask & 0x000000ff) { 481 bits = 0x000000ff; 482 } else if (mask & 0x0000ff00) { 483 bits = 0x0000ff00; 484 } else if (mask & 0x00ff0000) { 485 bits = 0x00ff0000; 486 } else { 487 bits = 0xff000000; 488 } 489 if (data[0]) { 490 s->io_bits |= bits; 491 } else { 492 s->io_bits &= ~bits; 493 } 494 pci20xxx_dio_config(dev, s); 495 496 return 1; 497} 498 499static int pci20xxx_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s, 500 struct comedi_insn * insn, unsigned int * data) 501{ 502 unsigned int mask = data[0]; 503 504 s->state &= ~mask; 505 s->state |= (mask & data[1]); 506 507 mask &= s->io_bits; 508 if (mask & 0x000000ff) 509 writeb((s->state >> 0) & 0xff, 510 devpriv->ioaddr + PCI20000_DIO_0); 511 if (mask & 0x0000ff00) 512 writeb((s->state >> 8) & 0xff, 513 devpriv->ioaddr + PCI20000_DIO_1); 514 if (mask & 0x00ff0000) 515 writeb((s->state >> 16) & 0xff, 516 devpriv->ioaddr + PCI20000_DIO_2); 517 if (mask & 0xff000000) 518 writeb((s->state >> 24) & 0xff, 519 devpriv->ioaddr + PCI20000_DIO_3); 520 521 data[1] = readb(devpriv->ioaddr + PCI20000_DIO_0); 522 data[1] |= readb(devpriv->ioaddr + PCI20000_DIO_1) << 8; 523 data[1] |= readb(devpriv->ioaddr + PCI20000_DIO_2) << 16; 524 data[1] |= readb(devpriv->ioaddr + PCI20000_DIO_3) << 24; 525 526 return 2; 527} 528 529static void pci20xxx_dio_config(struct comedi_device * dev, struct comedi_subdevice * s) 530{ 531 unsigned char control_01; 532 unsigned char control_23; 533 unsigned char buffer; 534 535 control_01 = readb(devpriv->ioaddr + PCI20000_DIO_CONTROL_01); 536 control_23 = readb(devpriv->ioaddr + PCI20000_DIO_CONTROL_23); 537 buffer = readb(devpriv->ioaddr + PCI20000_DIO_BUFFER); 538 539 if (s->io_bits & 0x000000ff) { 540 /* output port 0 */ 541 control_01 &= PCI20000_DIO_EOC; 542 buffer = (buffer & (~(DIO_BE << DIO_PS_0))) | (DIO_BO << 543 DIO_PS_0); 544 } else { 545 /* input port 0 */ 546 control_01 = (control_01 & DIO_CAND) | PCI20000_DIO_EIC; 547 buffer = (buffer & (~(DIO_BI << DIO_PS_0))); 548 } 549 if (s->io_bits & 0x0000ff00) { 550 /* output port 1 */ 551 control_01 &= PCI20000_DIO_OOC; 552 buffer = (buffer & (~(DIO_BE << DIO_PS_1))) | (DIO_BO << 553 DIO_PS_1); 554 } else { 555 /* input port 1 */ 556 control_01 = (control_01 & DIO_CAND) | PCI20000_DIO_OIC; 557 buffer = (buffer & (~(DIO_BI << DIO_PS_1))); 558 } 559 if (s->io_bits & 0x00ff0000) { 560 /* output port 2 */ 561 control_23 &= PCI20000_DIO_EOC; 562 buffer = (buffer & (~(DIO_BE << DIO_PS_2))) | (DIO_BO << 563 DIO_PS_2); 564 } else { 565 /* input port 2 */ 566 control_23 = (control_23 & DIO_CAND) | PCI20000_DIO_EIC; 567 buffer = (buffer & (~(DIO_BI << DIO_PS_2))); 568 } 569 if (s->io_bits & 0xff000000) { 570 /* output port 3 */ 571 control_23 &= PCI20000_DIO_OOC; 572 buffer = (buffer & (~(DIO_BE << DIO_PS_3))) | (DIO_BO << 573 DIO_PS_3); 574 } else { 575 /* input port 3 */ 576 control_23 = (control_23 & DIO_CAND) | PCI20000_DIO_OIC; 577 buffer = (buffer & (~(DIO_BI << DIO_PS_3))); 578 } 579 writeb(control_01, devpriv->ioaddr + PCI20000_DIO_CONTROL_01); 580 writeb(control_23, devpriv->ioaddr + PCI20000_DIO_CONTROL_23); 581 writeb(buffer, devpriv->ioaddr + PCI20000_DIO_BUFFER); 582} 583 584#if 0 585static void pci20xxx_do(struct comedi_device * dev, struct comedi_subdevice * s) 586{ 587 /* XXX if the channel is configured for input, does this 588 do bad things? */ 589 /* XXX it would be a good idea to only update the registers 590 that _need_ to be updated. This requires changes to 591 comedi, however. */ 592 writeb((s->state >> 0) & 0xff, devpriv->ioaddr + PCI20000_DIO_0); 593 writeb((s->state >> 8) & 0xff, devpriv->ioaddr + PCI20000_DIO_1); 594 writeb((s->state >> 16) & 0xff, devpriv->ioaddr + PCI20000_DIO_2); 595 writeb((s->state >> 24) & 0xff, devpriv->ioaddr + PCI20000_DIO_3); 596} 597 598static unsigned int pci20xxx_di(struct comedi_device * dev, struct comedi_subdevice * s) 599{ 600 /* XXX same note as above */ 601 unsigned int bits; 602 603 bits = readb(devpriv->ioaddr + PCI20000_DIO_0); 604 bits |= readb(devpriv->ioaddr + PCI20000_DIO_1) << 8; 605 bits |= readb(devpriv->ioaddr + PCI20000_DIO_2) << 16; 606 bits |= readb(devpriv->ioaddr + PCI20000_DIO_3) << 24; 607 608 return bits; 609} 610#endif 611 612COMEDI_INITCLEANUP(driver_pci20xxx); 613