1/* 2 comedi/drivers/dt3000.c 3 Data Translation DT3000 series driver 4 5 COMEDI - Linux Control and Measurement Device Interface 6 Copyright (C) 1999 David A. Schleef <ds@schleef.org> 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17*/ 18/* 19Driver: dt3000 20Description: Data Translation DT3000 series 21Author: ds 22Devices: [Data Translation] DT3001 (dt3000), DT3001-PGL, DT3002, DT3003, 23 DT3003-PGL, DT3004, DT3005, DT3004-200 24Updated: Mon, 14 Apr 2008 15:41:24 +0100 25Status: works 26 27Configuration Options: not applicable, uses PCI auto config 28 29There is code to support AI commands, but it may not work. 30 31AO commands are not supported. 32*/ 33 34/* 35 The DT3000 series is Data Translation's attempt to make a PCI 36 data acquisition board. The design of this series is very nice, 37 since each board has an on-board DSP (Texas Instruments TMS320C52). 38 However, a few details are a little annoying. The boards lack 39 bus-mastering DMA, which eliminates them from serious work. 40 They also are not capable of autocalibration, which is a common 41 feature in modern hardware. The default firmware is pretty bad, 42 making it nearly impossible to write an RT compatible driver. 43 It would make an interesting project to write a decent firmware 44 for these boards. 45 46 Data Translation originally wanted an NDA for the documentation 47 for the 3k series. However, if you ask nicely, they might send 48 you the docs without one, also. 49*/ 50 51#include <linux/module.h> 52#include <linux/pci.h> 53#include <linux/delay.h> 54#include <linux/interrupt.h> 55 56#include "../comedidev.h" 57 58#include "comedi_fc.h" 59 60static const struct comedi_lrange range_dt3000_ai = { 61 4, { 62 BIP_RANGE(10), 63 BIP_RANGE(5), 64 BIP_RANGE(2.5), 65 BIP_RANGE(1.25) 66 } 67}; 68 69static const struct comedi_lrange range_dt3000_ai_pgl = { 70 4, { 71 BIP_RANGE(10), 72 BIP_RANGE(1), 73 BIP_RANGE(0.1), 74 BIP_RANGE(0.02) 75 } 76}; 77 78enum dt3k_boardid { 79 BOARD_DT3001, 80 BOARD_DT3001_PGL, 81 BOARD_DT3002, 82 BOARD_DT3003, 83 BOARD_DT3003_PGL, 84 BOARD_DT3004, 85 BOARD_DT3005, 86}; 87 88struct dt3k_boardtype { 89 const char *name; 90 int adchan; 91 int adbits; 92 int ai_speed; 93 const struct comedi_lrange *adrange; 94 int dachan; 95 int dabits; 96}; 97 98static const struct dt3k_boardtype dt3k_boardtypes[] = { 99 [BOARD_DT3001] = { 100 .name = "dt3001", 101 .adchan = 16, 102 .adbits = 12, 103 .adrange = &range_dt3000_ai, 104 .ai_speed = 3000, 105 .dachan = 2, 106 .dabits = 12, 107 }, 108 [BOARD_DT3001_PGL] = { 109 .name = "dt3001-pgl", 110 .adchan = 16, 111 .adbits = 12, 112 .adrange = &range_dt3000_ai_pgl, 113 .ai_speed = 3000, 114 .dachan = 2, 115 .dabits = 12, 116 }, 117 [BOARD_DT3002] = { 118 .name = "dt3002", 119 .adchan = 32, 120 .adbits = 12, 121 .adrange = &range_dt3000_ai, 122 .ai_speed = 3000, 123 }, 124 [BOARD_DT3003] = { 125 .name = "dt3003", 126 .adchan = 64, 127 .adbits = 12, 128 .adrange = &range_dt3000_ai, 129 .ai_speed = 3000, 130 .dachan = 2, 131 .dabits = 12, 132 }, 133 [BOARD_DT3003_PGL] = { 134 .name = "dt3003-pgl", 135 .adchan = 64, 136 .adbits = 12, 137 .adrange = &range_dt3000_ai_pgl, 138 .ai_speed = 3000, 139 .dachan = 2, 140 .dabits = 12, 141 }, 142 [BOARD_DT3004] = { 143 .name = "dt3004", 144 .adchan = 16, 145 .adbits = 16, 146 .adrange = &range_dt3000_ai, 147 .ai_speed = 10000, 148 .dachan = 2, 149 .dabits = 12, 150 }, 151 [BOARD_DT3005] = { 152 .name = "dt3005", /* a.k.a. 3004-200 */ 153 .adchan = 16, 154 .adbits = 16, 155 .adrange = &range_dt3000_ai, 156 .ai_speed = 5000, 157 .dachan = 2, 158 .dabits = 12, 159 }, 160}; 161 162/* dual-ported RAM location definitions */ 163 164#define DPR_DAC_buffer (4*0x000) 165#define DPR_ADC_buffer (4*0x800) 166#define DPR_Command (4*0xfd3) 167#define DPR_SubSys (4*0xfd3) 168#define DPR_Encode (4*0xfd4) 169#define DPR_Params(a) (4*(0xfd5+(a))) 170#define DPR_Tick_Reg_Lo (4*0xff5) 171#define DPR_Tick_Reg_Hi (4*0xff6) 172#define DPR_DA_Buf_Front (4*0xff7) 173#define DPR_DA_Buf_Rear (4*0xff8) 174#define DPR_AD_Buf_Front (4*0xff9) 175#define DPR_AD_Buf_Rear (4*0xffa) 176#define DPR_Int_Mask (4*0xffb) 177#define DPR_Intr_Flag (4*0xffc) 178#define DPR_Response_Mbx (4*0xffe) 179#define DPR_Command_Mbx (4*0xfff) 180 181#define AI_FIFO_DEPTH 2003 182#define AO_FIFO_DEPTH 2048 183 184/* command list */ 185 186#define CMD_GETBRDINFO 0 187#define CMD_CONFIG 1 188#define CMD_GETCONFIG 2 189#define CMD_START 3 190#define CMD_STOP 4 191#define CMD_READSINGLE 5 192#define CMD_WRITESINGLE 6 193#define CMD_CALCCLOCK 7 194#define CMD_READEVENTS 8 195#define CMD_WRITECTCTRL 16 196#define CMD_READCTCTRL 17 197#define CMD_WRITECT 18 198#define CMD_READCT 19 199#define CMD_WRITEDATA 32 200#define CMD_READDATA 33 201#define CMD_WRITEIO 34 202#define CMD_READIO 35 203#define CMD_WRITECODE 36 204#define CMD_READCODE 37 205#define CMD_EXECUTE 38 206#define CMD_HALT 48 207 208#define SUBS_AI 0 209#define SUBS_AO 1 210#define SUBS_DIN 2 211#define SUBS_DOUT 3 212#define SUBS_MEM 4 213#define SUBS_CT 5 214 215/* interrupt flags */ 216#define DT3000_CMDONE 0x80 217#define DT3000_CTDONE 0x40 218#define DT3000_DAHWERR 0x20 219#define DT3000_DASWERR 0x10 220#define DT3000_DAEMPTY 0x08 221#define DT3000_ADHWERR 0x04 222#define DT3000_ADSWERR 0x02 223#define DT3000_ADFULL 0x01 224 225#define DT3000_COMPLETION_MASK 0xff00 226#define DT3000_COMMAND_MASK 0x00ff 227#define DT3000_NOTPROCESSED 0x0000 228#define DT3000_NOERROR 0x5500 229#define DT3000_ERROR 0xaa00 230#define DT3000_NOTSUPPORTED 0xff00 231 232#define DT3000_EXTERNAL_CLOCK 1 233#define DT3000_RISING_EDGE 2 234 235#define TMODE_MASK 0x1c 236 237#define DT3000_AD_TRIG_INTERNAL (0<<2) 238#define DT3000_AD_TRIG_EXTERNAL (1<<2) 239#define DT3000_AD_RETRIG_INTERNAL (2<<2) 240#define DT3000_AD_RETRIG_EXTERNAL (3<<2) 241#define DT3000_AD_EXTRETRIG (4<<2) 242 243#define DT3000_CHANNEL_MODE_SE 0 244#define DT3000_CHANNEL_MODE_DI 1 245 246struct dt3k_private { 247 unsigned int lock; 248 unsigned int ai_front; 249 unsigned int ai_rear; 250}; 251 252#define TIMEOUT 100 253 254static void dt3k_send_cmd(struct comedi_device *dev, unsigned int cmd) 255{ 256 int i; 257 unsigned int status = 0; 258 259 writew(cmd, dev->mmio + DPR_Command_Mbx); 260 261 for (i = 0; i < TIMEOUT; i++) { 262 status = readw(dev->mmio + DPR_Command_Mbx); 263 if ((status & DT3000_COMPLETION_MASK) != DT3000_NOTPROCESSED) 264 break; 265 udelay(1); 266 } 267 268 if ((status & DT3000_COMPLETION_MASK) != DT3000_NOERROR) 269 dev_dbg(dev->class_dev, "%s: timeout/error status=0x%04x\n", 270 __func__, status); 271} 272 273static unsigned int dt3k_readsingle(struct comedi_device *dev, 274 unsigned int subsys, unsigned int chan, 275 unsigned int gain) 276{ 277 writew(subsys, dev->mmio + DPR_SubSys); 278 279 writew(chan, dev->mmio + DPR_Params(0)); 280 writew(gain, dev->mmio + DPR_Params(1)); 281 282 dt3k_send_cmd(dev, CMD_READSINGLE); 283 284 return readw(dev->mmio + DPR_Params(2)); 285} 286 287static void dt3k_writesingle(struct comedi_device *dev, unsigned int subsys, 288 unsigned int chan, unsigned int data) 289{ 290 writew(subsys, dev->mmio + DPR_SubSys); 291 292 writew(chan, dev->mmio + DPR_Params(0)); 293 writew(0, dev->mmio + DPR_Params(1)); 294 writew(data, dev->mmio + DPR_Params(2)); 295 296 dt3k_send_cmd(dev, CMD_WRITESINGLE); 297} 298 299static void dt3k_ai_empty_fifo(struct comedi_device *dev, 300 struct comedi_subdevice *s) 301{ 302 struct dt3k_private *devpriv = dev->private; 303 int front; 304 int rear; 305 int count; 306 int i; 307 unsigned short data; 308 309 front = readw(dev->mmio + DPR_AD_Buf_Front); 310 count = front - devpriv->ai_front; 311 if (count < 0) 312 count += AI_FIFO_DEPTH; 313 314 rear = devpriv->ai_rear; 315 316 for (i = 0; i < count; i++) { 317 data = readw(dev->mmio + DPR_ADC_buffer + rear); 318 comedi_buf_put(s, data); 319 rear++; 320 if (rear >= AI_FIFO_DEPTH) 321 rear = 0; 322 } 323 324 devpriv->ai_rear = rear; 325 writew(rear, dev->mmio + DPR_AD_Buf_Rear); 326} 327 328static int dt3k_ai_cancel(struct comedi_device *dev, 329 struct comedi_subdevice *s) 330{ 331 writew(SUBS_AI, dev->mmio + DPR_SubSys); 332 dt3k_send_cmd(dev, CMD_STOP); 333 334 writew(0, dev->mmio + DPR_Int_Mask); 335 336 return 0; 337} 338 339static int debug_n_ints; 340 341/* FIXME! Assumes shared interrupt is for this card. */ 342/* What's this debug_n_ints stuff? Obviously needs some work... */ 343static irqreturn_t dt3k_interrupt(int irq, void *d) 344{ 345 struct comedi_device *dev = d; 346 struct comedi_subdevice *s = dev->read_subdev; 347 unsigned int status; 348 349 if (!dev->attached) 350 return IRQ_NONE; 351 352 status = readw(dev->mmio + DPR_Intr_Flag); 353 354 if (status & DT3000_ADFULL) { 355 dt3k_ai_empty_fifo(dev, s); 356 s->async->events |= COMEDI_CB_BLOCK; 357 } 358 359 if (status & (DT3000_ADSWERR | DT3000_ADHWERR)) 360 s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA; 361 362 debug_n_ints++; 363 if (debug_n_ints >= 10) 364 s->async->events |= COMEDI_CB_EOA; 365 366 cfc_handle_events(dev, s); 367 return IRQ_HANDLED; 368} 369 370static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *nanosec, 371 unsigned int flags) 372{ 373 int divider, base, prescale; 374 375 /* This function needs improvment */ 376 /* Don't know if divider==0 works. */ 377 378 for (prescale = 0; prescale < 16; prescale++) { 379 base = timer_base * (prescale + 1); 380 switch (flags & CMDF_ROUND_MASK) { 381 case CMDF_ROUND_NEAREST: 382 default: 383 divider = (*nanosec + base / 2) / base; 384 break; 385 case CMDF_ROUND_DOWN: 386 divider = (*nanosec) / base; 387 break; 388 case CMDF_ROUND_UP: 389 divider = (*nanosec) / base; 390 break; 391 } 392 if (divider < 65536) { 393 *nanosec = divider * base; 394 return (prescale << 16) | (divider); 395 } 396 } 397 398 prescale = 15; 399 base = timer_base * (1 << prescale); 400 divider = 65535; 401 *nanosec = divider * base; 402 return (prescale << 16) | (divider); 403} 404 405static int dt3k_ai_cmdtest(struct comedi_device *dev, 406 struct comedi_subdevice *s, struct comedi_cmd *cmd) 407{ 408 const struct dt3k_boardtype *this_board = dev->board_ptr; 409 int err = 0; 410 unsigned int arg; 411 412 /* Step 1 : check if triggers are trivially valid */ 413 414 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW); 415 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER); 416 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER); 417 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); 418 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT); 419 420 if (err) 421 return 1; 422 423 /* Step 2a : make sure trigger sources are unique */ 424 /* Step 2b : and mutually compatible */ 425 426 /* Step 3: check if arguments are trivially valid */ 427 428 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0); 429 430 if (cmd->scan_begin_src == TRIG_TIMER) { 431 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg, 432 this_board->ai_speed); 433 err |= cfc_check_trigger_arg_max(&cmd->scan_begin_arg, 434 100 * 16 * 65535); 435 } 436 437 if (cmd->convert_src == TRIG_TIMER) { 438 err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 439 this_board->ai_speed); 440 err |= cfc_check_trigger_arg_max(&cmd->convert_arg, 441 50 * 16 * 65535); 442 } 443 444 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len); 445 446 if (cmd->stop_src == TRIG_COUNT) 447 err |= cfc_check_trigger_arg_max(&cmd->stop_arg, 0x00ffffff); 448 else /* TRIG_NONE */ 449 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0); 450 451 if (err) 452 return 3; 453 454 /* step 4: fix up any arguments */ 455 456 if (cmd->scan_begin_src == TRIG_TIMER) { 457 arg = cmd->scan_begin_arg; 458 dt3k_ns_to_timer(100, &arg, cmd->flags); 459 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, arg); 460 } 461 462 if (cmd->convert_src == TRIG_TIMER) { 463 arg = cmd->convert_arg; 464 dt3k_ns_to_timer(50, &arg, cmd->flags); 465 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg); 466 467 if (cmd->scan_begin_src == TRIG_TIMER) { 468 arg = cmd->convert_arg * cmd->scan_end_arg; 469 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg, 470 arg); 471 } 472 } 473 474 if (err) 475 return 4; 476 477 return 0; 478} 479 480static int dt3k_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) 481{ 482 struct comedi_cmd *cmd = &s->async->cmd; 483 int i; 484 unsigned int chan, range, aref; 485 unsigned int divider; 486 unsigned int tscandiv; 487 488 for (i = 0; i < cmd->chanlist_len; i++) { 489 chan = CR_CHAN(cmd->chanlist[i]); 490 range = CR_RANGE(cmd->chanlist[i]); 491 492 writew((range << 6) | chan, dev->mmio + DPR_ADC_buffer + i); 493 } 494 aref = CR_AREF(cmd->chanlist[0]); 495 496 writew(cmd->scan_end_arg, dev->mmio + DPR_Params(0)); 497 498 if (cmd->convert_src == TRIG_TIMER) { 499 divider = dt3k_ns_to_timer(50, &cmd->convert_arg, cmd->flags); 500 writew((divider >> 16), dev->mmio + DPR_Params(1)); 501 writew((divider & 0xffff), dev->mmio + DPR_Params(2)); 502 } 503 504 if (cmd->scan_begin_src == TRIG_TIMER) { 505 tscandiv = dt3k_ns_to_timer(100, &cmd->scan_begin_arg, 506 cmd->flags); 507 writew((tscandiv >> 16), dev->mmio + DPR_Params(3)); 508 writew((tscandiv & 0xffff), dev->mmio + DPR_Params(4)); 509 } 510 511 writew(DT3000_AD_RETRIG_INTERNAL, dev->mmio + DPR_Params(5)); 512 writew(aref == AREF_DIFF, dev->mmio + DPR_Params(6)); 513 514 writew(AI_FIFO_DEPTH / 2, dev->mmio + DPR_Params(7)); 515 516 writew(SUBS_AI, dev->mmio + DPR_SubSys); 517 dt3k_send_cmd(dev, CMD_CONFIG); 518 519 writew(DT3000_ADFULL | DT3000_ADSWERR | DT3000_ADHWERR, 520 dev->mmio + DPR_Int_Mask); 521 522 debug_n_ints = 0; 523 524 writew(SUBS_AI, dev->mmio + DPR_SubSys); 525 dt3k_send_cmd(dev, CMD_START); 526 527 return 0; 528} 529 530static int dt3k_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s, 531 struct comedi_insn *insn, unsigned int *data) 532{ 533 int i; 534 unsigned int chan, gain, aref; 535 536 chan = CR_CHAN(insn->chanspec); 537 gain = CR_RANGE(insn->chanspec); 538 /* XXX docs don't explain how to select aref */ 539 aref = CR_AREF(insn->chanspec); 540 541 for (i = 0; i < insn->n; i++) 542 data[i] = dt3k_readsingle(dev, SUBS_AI, chan, gain); 543 544 return i; 545} 546 547static int dt3k_ao_insn_write(struct comedi_device *dev, 548 struct comedi_subdevice *s, 549 struct comedi_insn *insn, 550 unsigned int *data) 551{ 552 unsigned int chan = CR_CHAN(insn->chanspec); 553 unsigned int val = s->readback[chan]; 554 int i; 555 556 for (i = 0; i < insn->n; i++) { 557 val = data[i]; 558 dt3k_writesingle(dev, SUBS_AO, chan, val); 559 } 560 s->readback[chan] = val; 561 562 return insn->n; 563} 564 565static void dt3k_dio_config(struct comedi_device *dev, int bits) 566{ 567 /* XXX */ 568 writew(SUBS_DOUT, dev->mmio + DPR_SubSys); 569 570 writew(bits, dev->mmio + DPR_Params(0)); 571#if 0 572 /* don't know */ 573 writew(0, dev->mmio + DPR_Params(1)); 574 writew(0, dev->mmio + DPR_Params(2)); 575#endif 576 577 dt3k_send_cmd(dev, CMD_CONFIG); 578} 579 580static int dt3k_dio_insn_config(struct comedi_device *dev, 581 struct comedi_subdevice *s, 582 struct comedi_insn *insn, 583 unsigned int *data) 584{ 585 unsigned int chan = CR_CHAN(insn->chanspec); 586 unsigned int mask; 587 int ret; 588 589 if (chan < 4) 590 mask = 0x0f; 591 else 592 mask = 0xf0; 593 594 ret = comedi_dio_insn_config(dev, s, insn, data, mask); 595 if (ret) 596 return ret; 597 598 dt3k_dio_config(dev, (s->io_bits & 0x01) | ((s->io_bits & 0x10) >> 3)); 599 600 return insn->n; 601} 602 603static int dt3k_dio_insn_bits(struct comedi_device *dev, 604 struct comedi_subdevice *s, 605 struct comedi_insn *insn, 606 unsigned int *data) 607{ 608 if (comedi_dio_update_state(s, data)) 609 dt3k_writesingle(dev, SUBS_DOUT, 0, s->state); 610 611 data[1] = dt3k_readsingle(dev, SUBS_DIN, 0, 0); 612 613 return insn->n; 614} 615 616static int dt3k_mem_insn_read(struct comedi_device *dev, 617 struct comedi_subdevice *s, 618 struct comedi_insn *insn, 619 unsigned int *data) 620{ 621 unsigned int addr = CR_CHAN(insn->chanspec); 622 int i; 623 624 for (i = 0; i < insn->n; i++) { 625 writew(SUBS_MEM, dev->mmio + DPR_SubSys); 626 writew(addr, dev->mmio + DPR_Params(0)); 627 writew(1, dev->mmio + DPR_Params(1)); 628 629 dt3k_send_cmd(dev, CMD_READCODE); 630 631 data[i] = readw(dev->mmio + DPR_Params(2)); 632 } 633 634 return i; 635} 636 637static int dt3000_auto_attach(struct comedi_device *dev, 638 unsigned long context) 639{ 640 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 641 const struct dt3k_boardtype *this_board = NULL; 642 struct dt3k_private *devpriv; 643 struct comedi_subdevice *s; 644 int ret = 0; 645 646 if (context < ARRAY_SIZE(dt3k_boardtypes)) 647 this_board = &dt3k_boardtypes[context]; 648 if (!this_board) 649 return -ENODEV; 650 dev->board_ptr = this_board; 651 dev->board_name = this_board->name; 652 653 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 654 if (!devpriv) 655 return -ENOMEM; 656 657 ret = comedi_pci_enable(dev); 658 if (ret < 0) 659 return ret; 660 661 dev->mmio = pci_ioremap_bar(pcidev, 0); 662 if (!dev->mmio) 663 return -ENOMEM; 664 665 if (pcidev->irq) { 666 ret = request_irq(pcidev->irq, dt3k_interrupt, IRQF_SHARED, 667 dev->board_name, dev); 668 if (ret == 0) 669 dev->irq = pcidev->irq; 670 } 671 672 ret = comedi_alloc_subdevices(dev, 4); 673 if (ret) 674 return ret; 675 676 s = &dev->subdevices[0]; 677 /* ai subdevice */ 678 s->type = COMEDI_SUBD_AI; 679 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF; 680 s->n_chan = this_board->adchan; 681 s->insn_read = dt3k_ai_insn; 682 s->maxdata = (1 << this_board->adbits) - 1; 683 s->range_table = &range_dt3000_ai; /* XXX */ 684 if (dev->irq) { 685 dev->read_subdev = s; 686 s->subdev_flags |= SDF_CMD_READ; 687 s->len_chanlist = 512; 688 s->do_cmd = dt3k_ai_cmd; 689 s->do_cmdtest = dt3k_ai_cmdtest; 690 s->cancel = dt3k_ai_cancel; 691 } 692 693 s = &dev->subdevices[1]; 694 /* ao subsystem */ 695 s->type = COMEDI_SUBD_AO; 696 s->subdev_flags = SDF_WRITABLE; 697 s->n_chan = 2; 698 s->maxdata = (1 << this_board->dabits) - 1; 699 s->len_chanlist = 1; 700 s->range_table = &range_bipolar10; 701 s->insn_write = dt3k_ao_insn_write; 702 s->insn_read = comedi_readback_insn_read; 703 704 ret = comedi_alloc_subdev_readback(s); 705 if (ret) 706 return ret; 707 708 s = &dev->subdevices[2]; 709 /* dio subsystem */ 710 s->type = COMEDI_SUBD_DIO; 711 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 712 s->n_chan = 8; 713 s->insn_config = dt3k_dio_insn_config; 714 s->insn_bits = dt3k_dio_insn_bits; 715 s->maxdata = 1; 716 s->len_chanlist = 8; 717 s->range_table = &range_digital; 718 719 s = &dev->subdevices[3]; 720 /* mem subsystem */ 721 s->type = COMEDI_SUBD_MEMORY; 722 s->subdev_flags = SDF_READABLE; 723 s->n_chan = 0x1000; 724 s->insn_read = dt3k_mem_insn_read; 725 s->maxdata = 0xff; 726 s->len_chanlist = 1; 727 s->range_table = &range_unknown; 728 729#if 0 730 s = &dev->subdevices[4]; 731 /* proc subsystem */ 732 s->type = COMEDI_SUBD_PROC; 733#endif 734 735 return 0; 736} 737 738static struct comedi_driver dt3000_driver = { 739 .driver_name = "dt3000", 740 .module = THIS_MODULE, 741 .auto_attach = dt3000_auto_attach, 742 .detach = comedi_pci_detach, 743}; 744 745static int dt3000_pci_probe(struct pci_dev *dev, 746 const struct pci_device_id *id) 747{ 748 return comedi_pci_auto_config(dev, &dt3000_driver, id->driver_data); 749} 750 751static const struct pci_device_id dt3000_pci_table[] = { 752 { PCI_VDEVICE(DT, 0x0022), BOARD_DT3001 }, 753 { PCI_VDEVICE(DT, 0x0023), BOARD_DT3002 }, 754 { PCI_VDEVICE(DT, 0x0024), BOARD_DT3003 }, 755 { PCI_VDEVICE(DT, 0x0025), BOARD_DT3004 }, 756 { PCI_VDEVICE(DT, 0x0026), BOARD_DT3005 }, 757 { PCI_VDEVICE(DT, 0x0027), BOARD_DT3001_PGL }, 758 { PCI_VDEVICE(DT, 0x0028), BOARD_DT3003_PGL }, 759 { 0 } 760}; 761MODULE_DEVICE_TABLE(pci, dt3000_pci_table); 762 763static struct pci_driver dt3000_pci_driver = { 764 .name = "dt3000", 765 .id_table = dt3000_pci_table, 766 .probe = dt3000_pci_probe, 767 .remove = comedi_pci_auto_unconfig, 768}; 769module_comedi_pci_driver(dt3000_driver, dt3000_pci_driver); 770 771MODULE_AUTHOR("Comedi http://www.comedi.org"); 772MODULE_DESCRIPTION("Comedi low-level driver"); 773MODULE_LICENSE("GPL"); 774