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