1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * das6402.c 4 * Comedi driver for DAS6402 compatible boards 5 * Copyright(c) 2014 H Hartley Sweeten <hsweeten@visionengravers.com> 6 * 7 * Rewrite of an experimental driver by: 8 * Copyright (C) 1999 Oystein Svendsen <svendsen@pvv.org> 9 */ 10 11 /* 12 * Driver: das6402 13 * Description: Keithley Metrabyte DAS6402 (& compatibles) 14 * Devices: [Keithley Metrabyte] DAS6402-12 (das6402-12), 15 * DAS6402-16 (das6402-16) 16 * Author: H Hartley Sweeten <hsweeten@visionengravers.com> 17 * Updated: Fri, 14 Mar 2014 10:18:43 -0700 18 * Status: unknown 19 * 20 * Configuration Options: 21 * [0] - I/O base address 22 * [1] - IRQ (optional, needed for async command support) 23 */ 24 25 #include <linux/module.h> 26 #include <linux/interrupt.h> 27 28 #include "../comedidev.h" 29 30 #include "comedi_8254.h" 31 32 /* 33 * Register I/O map 34 */ 35 #define DAS6402_AI_DATA_REG 0x00 36 #define DAS6402_AI_MUX_REG 0x02 37 #define DAS6402_AI_MUX_LO(x) (((x) & 0x3f) << 0) 38 #define DAS6402_AI_MUX_HI(x) (((x) & 0x3f) << 8) 39 #define DAS6402_DI_DO_REG 0x03 40 #define DAS6402_AO_DATA_REG(x) (0x04 + ((x) * 2)) 41 #define DAS6402_AO_LSB_REG(x) (0x04 + ((x) * 2)) 42 #define DAS6402_AO_MSB_REG(x) (0x05 + ((x) * 2)) 43 #define DAS6402_STATUS_REG 0x08 44 #define DAS6402_STATUS_FFNE BIT(0) 45 #define DAS6402_STATUS_FHALF BIT(1) 46 #define DAS6402_STATUS_FFULL BIT(2) 47 #define DAS6402_STATUS_XINT BIT(3) 48 #define DAS6402_STATUS_INT BIT(4) 49 #define DAS6402_STATUS_XTRIG BIT(5) 50 #define DAS6402_STATUS_INDGT BIT(6) 51 #define DAS6402_STATUS_10MHZ BIT(7) 52 #define DAS6402_STATUS_W_CLRINT BIT(0) 53 #define DAS6402_STATUS_W_CLRXTR BIT(1) 54 #define DAS6402_STATUS_W_CLRXIN BIT(2) 55 #define DAS6402_STATUS_W_EXTEND BIT(4) 56 #define DAS6402_STATUS_W_ARMED BIT(5) 57 #define DAS6402_STATUS_W_POSTMODE BIT(6) 58 #define DAS6402_STATUS_W_10MHZ BIT(7) 59 #define DAS6402_CTRL_REG 0x09 60 #define DAS6402_CTRL_TRIG(x) ((x) << 0) 61 #define DAS6402_CTRL_SOFT_TRIG DAS6402_CTRL_TRIG(0) 62 #define DAS6402_CTRL_EXT_FALL_TRIG DAS6402_CTRL_TRIG(1) 63 #define DAS6402_CTRL_EXT_RISE_TRIG DAS6402_CTRL_TRIG(2) 64 #define DAS6402_CTRL_PACER_TRIG DAS6402_CTRL_TRIG(3) 65 #define DAS6402_CTRL_BURSTEN BIT(2) 66 #define DAS6402_CTRL_XINTE BIT(3) 67 #define DAS6402_CTRL_IRQ(x) ((x) << 4) 68 #define DAS6402_CTRL_INTE BIT(7) 69 #define DAS6402_TRIG_REG 0x0a 70 #define DAS6402_TRIG_TGEN BIT(0) 71 #define DAS6402_TRIG_TGSEL BIT(1) 72 #define DAS6402_TRIG_TGPOL BIT(2) 73 #define DAS6402_TRIG_PRETRIG BIT(3) 74 #define DAS6402_AO_RANGE(_chan, _range) ((_range) << ((_chan) ? 6 : 4)) 75 #define DAS6402_AO_RANGE_MASK(_chan) (3 << ((_chan) ? 6 : 4)) 76 #define DAS6402_MODE_REG 0x0b 77 #define DAS6402_MODE_RANGE(x) ((x) << 2) 78 #define DAS6402_MODE_POLLED DAS6402_MODE_RANGE(0) 79 #define DAS6402_MODE_FIFONEPTY DAS6402_MODE_RANGE(1) 80 #define DAS6402_MODE_FIFOHFULL DAS6402_MODE_RANGE(2) 81 #define DAS6402_MODE_EOB DAS6402_MODE_RANGE(3) 82 #define DAS6402_MODE_ENHANCED BIT(4) 83 #define DAS6402_MODE_SE BIT(5) 84 #define DAS6402_MODE_UNI BIT(6) 85 #define DAS6402_MODE_DMA(x) ((x) << 7) 86 #define DAS6402_MODE_DMA1 DAS6402_MODE_DMA(0) 87 #define DAS6402_MODE_DMA3 DAS6402_MODE_DMA(1) 88 #define DAS6402_TIMER_BASE 0x0c 89 90 static const struct comedi_lrange das6402_ai_ranges = { 91 8, { 92 BIP_RANGE(10), 93 BIP_RANGE(5), 94 BIP_RANGE(2.5), 95 BIP_RANGE(1.25), 96 UNI_RANGE(10), 97 UNI_RANGE(5), 98 UNI_RANGE(2.5), 99 UNI_RANGE(1.25) 100 } 101 }; 102 103 /* 104 * Analog output ranges are programmable on the DAS6402/12. 105 * For the DAS6402/16 the range bits have no function, the 106 * DAC ranges are selected by switches on the board. 107 */ 108 static const struct comedi_lrange das6402_ao_ranges = { 109 4, { 110 BIP_RANGE(5), 111 BIP_RANGE(10), 112 UNI_RANGE(5), 113 UNI_RANGE(10) 114 } 115 }; 116 117 struct das6402_boardinfo { 118 const char *name; 119 unsigned int maxdata; 120 }; 121 122 static struct das6402_boardinfo das6402_boards[] = { 123 { 124 .name = "das6402-12", 125 .maxdata = 0x0fff, 126 }, { 127 .name = "das6402-16", 128 .maxdata = 0xffff, 129 }, 130 }; 131 132 struct das6402_private { 133 unsigned int irq; 134 unsigned int ao_range; 135 }; 136 137 static void das6402_set_mode(struct comedi_device *dev, 138 unsigned int mode) 139 { 140 outb(DAS6402_MODE_ENHANCED | mode, dev->iobase + DAS6402_MODE_REG); 141 } 142 143 static void das6402_set_extended(struct comedi_device *dev, 144 unsigned int val) 145 { 146 outb(DAS6402_STATUS_W_EXTEND, dev->iobase + DAS6402_STATUS_REG); 147 outb(DAS6402_STATUS_W_EXTEND | val, dev->iobase + DAS6402_STATUS_REG); 148 outb(val, dev->iobase + DAS6402_STATUS_REG); 149 } 150 151 static void das6402_clear_all_interrupts(struct comedi_device *dev) 152 { 153 outb(DAS6402_STATUS_W_CLRINT | 154 DAS6402_STATUS_W_CLRXTR | 155 DAS6402_STATUS_W_CLRXIN, dev->iobase + DAS6402_STATUS_REG); 156 } 157 158 static void das6402_ai_clear_eoc(struct comedi_device *dev) 159 { 160 outb(DAS6402_STATUS_W_CLRINT, dev->iobase + DAS6402_STATUS_REG); 161 } 162 163 static unsigned int das6402_ai_read_sample(struct comedi_device *dev, 164 struct comedi_subdevice *s) 165 { 166 unsigned int val; 167 168 val = inw(dev->iobase + DAS6402_AI_DATA_REG); 169 if (s->maxdata == 0x0fff) 170 val >>= 4; 171 return val; 172 } 173 174 static irqreturn_t das6402_interrupt(int irq, void *d) 175 { 176 struct comedi_device *dev = d; 177 struct comedi_subdevice *s = dev->read_subdev; 178 struct comedi_async *async = s->async; 179 struct comedi_cmd *cmd = &async->cmd; 180 unsigned int status; 181 182 status = inb(dev->iobase + DAS6402_STATUS_REG); 183 if ((status & DAS6402_STATUS_INT) == 0) 184 return IRQ_NONE; 185 186 if (status & DAS6402_STATUS_FFULL) { 187 async->events |= COMEDI_CB_OVERFLOW; 188 } else if (status & DAS6402_STATUS_FFNE) { 189 unsigned short val; 190 191 val = das6402_ai_read_sample(dev, s); 192 comedi_buf_write_samples(s, &val, 1); 193 194 if (cmd->stop_src == TRIG_COUNT && 195 async->scans_done >= cmd->stop_arg) 196 async->events |= COMEDI_CB_EOA; 197 } 198 199 das6402_clear_all_interrupts(dev); 200 201 comedi_handle_events(dev, s); 202 203 return IRQ_HANDLED; 204 } 205 206 static void das6402_ai_set_mode(struct comedi_device *dev, 207 struct comedi_subdevice *s, 208 unsigned int chanspec, 209 unsigned int mode) 210 { 211 unsigned int range = CR_RANGE(chanspec); 212 unsigned int aref = CR_AREF(chanspec); 213 214 mode |= DAS6402_MODE_RANGE(range); 215 if (aref == AREF_GROUND) 216 mode |= DAS6402_MODE_SE; 217 if (comedi_range_is_unipolar(s, range)) 218 mode |= DAS6402_MODE_UNI; 219 220 das6402_set_mode(dev, mode); 221 } 222 223 static int das6402_ai_cmd(struct comedi_device *dev, 224 struct comedi_subdevice *s) 225 { 226 struct das6402_private *devpriv = dev->private; 227 struct comedi_cmd *cmd = &s->async->cmd; 228 unsigned int chan_lo = CR_CHAN(cmd->chanlist[0]); 229 unsigned int chan_hi = CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]); 230 231 das6402_ai_set_mode(dev, s, cmd->chanlist[0], DAS6402_MODE_FIFONEPTY); 232 233 /* load the mux for chanlist conversion */ 234 outw(DAS6402_AI_MUX_HI(chan_hi) | DAS6402_AI_MUX_LO(chan_lo), 235 dev->iobase + DAS6402_AI_MUX_REG); 236 237 comedi_8254_update_divisors(dev->pacer); 238 comedi_8254_pacer_enable(dev->pacer, 1, 2, true); 239 240 /* enable interrupt and pacer trigger */ 241 outb(DAS6402_CTRL_INTE | 242 DAS6402_CTRL_IRQ(devpriv->irq) | 243 DAS6402_CTRL_PACER_TRIG, dev->iobase + DAS6402_CTRL_REG); 244 245 return 0; 246 } 247 248 static int das6402_ai_check_chanlist(struct comedi_device *dev, 249 struct comedi_subdevice *s, 250 struct comedi_cmd *cmd) 251 { 252 unsigned int chan0 = CR_CHAN(cmd->chanlist[0]); 253 unsigned int range0 = CR_RANGE(cmd->chanlist[0]); 254 unsigned int aref0 = CR_AREF(cmd->chanlist[0]); 255 int i; 256 257 for (i = 1; i < cmd->chanlist_len; i++) { 258 unsigned int chan = CR_CHAN(cmd->chanlist[i]); 259 unsigned int range = CR_RANGE(cmd->chanlist[i]); 260 unsigned int aref = CR_AREF(cmd->chanlist[i]); 261 262 if (chan != chan0 + i) { 263 dev_dbg(dev->class_dev, 264 "chanlist must be consecutive\n"); 265 return -EINVAL; 266 } 267 268 if (range != range0) { 269 dev_dbg(dev->class_dev, 270 "chanlist must have the same range\n"); 271 return -EINVAL; 272 } 273 274 if (aref != aref0) { 275 dev_dbg(dev->class_dev, 276 "chanlist must have the same reference\n"); 277 return -EINVAL; 278 } 279 280 if (aref0 == AREF_DIFF && chan > (s->n_chan / 2)) { 281 dev_dbg(dev->class_dev, 282 "chanlist differential channel too large\n"); 283 return -EINVAL; 284 } 285 } 286 return 0; 287 } 288 289 static int das6402_ai_cmdtest(struct comedi_device *dev, 290 struct comedi_subdevice *s, 291 struct comedi_cmd *cmd) 292 { 293 int err = 0; 294 unsigned int arg; 295 296 /* Step 1 : check if triggers are trivially valid */ 297 298 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW); 299 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW); 300 err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER); 301 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); 302 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); 303 304 if (err) 305 return 1; 306 307 /* Step 2a : make sure trigger sources are unique */ 308 309 err |= comedi_check_trigger_is_unique(cmd->stop_src); 310 311 /* Step 2b : and mutually compatible */ 312 313 if (err) 314 return 2; 315 316 /* Step 3: check if arguments are trivially valid */ 317 318 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); 319 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0); 320 err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000); 321 err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1); 322 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, 323 cmd->chanlist_len); 324 325 if (cmd->stop_src == TRIG_COUNT) 326 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1); 327 else /* TRIG_NONE */ 328 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); 329 330 if (err) 331 return 3; 332 333 /* step 4: fix up any arguments */ 334 335 arg = cmd->convert_arg; 336 comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags); 337 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg); 338 339 if (err) 340 return 4; 341 342 /* Step 5: check channel list if it exists */ 343 if (cmd->chanlist && cmd->chanlist_len > 0) 344 err |= das6402_ai_check_chanlist(dev, s, cmd); 345 346 if (err) 347 return 5; 348 349 return 0; 350 } 351 352 static int das6402_ai_cancel(struct comedi_device *dev, 353 struct comedi_subdevice *s) 354 { 355 outb(DAS6402_CTRL_SOFT_TRIG, dev->iobase + DAS6402_CTRL_REG); 356 357 return 0; 358 } 359 360 static void das6402_ai_soft_trig(struct comedi_device *dev) 361 { 362 outw(0, dev->iobase + DAS6402_AI_DATA_REG); 363 } 364 365 static int das6402_ai_eoc(struct comedi_device *dev, 366 struct comedi_subdevice *s, 367 struct comedi_insn *insn, 368 unsigned long context) 369 { 370 unsigned int status; 371 372 status = inb(dev->iobase + DAS6402_STATUS_REG); 373 if (status & DAS6402_STATUS_FFNE) 374 return 0; 375 return -EBUSY; 376 } 377 378 static int das6402_ai_insn_read(struct comedi_device *dev, 379 struct comedi_subdevice *s, 380 struct comedi_insn *insn, 381 unsigned int *data) 382 { 383 unsigned int chan = CR_CHAN(insn->chanspec); 384 unsigned int aref = CR_AREF(insn->chanspec); 385 int ret; 386 int i; 387 388 if (aref == AREF_DIFF && chan > (s->n_chan / 2)) 389 return -EINVAL; 390 391 /* enable software conversion trigger */ 392 outb(DAS6402_CTRL_SOFT_TRIG, dev->iobase + DAS6402_CTRL_REG); 393 394 das6402_ai_set_mode(dev, s, insn->chanspec, DAS6402_MODE_POLLED); 395 396 /* load the mux for single channel conversion */ 397 outw(DAS6402_AI_MUX_HI(chan) | DAS6402_AI_MUX_LO(chan), 398 dev->iobase + DAS6402_AI_MUX_REG); 399 400 for (i = 0; i < insn->n; i++) { 401 das6402_ai_clear_eoc(dev); 402 das6402_ai_soft_trig(dev); 403 404 ret = comedi_timeout(dev, s, insn, das6402_ai_eoc, 0); 405 if (ret) 406 break; 407 408 data[i] = das6402_ai_read_sample(dev, s); 409 } 410 411 das6402_ai_clear_eoc(dev); 412 413 return insn->n; 414 } 415 416 static int das6402_ao_insn_write(struct comedi_device *dev, 417 struct comedi_subdevice *s, 418 struct comedi_insn *insn, 419 unsigned int *data) 420 { 421 struct das6402_private *devpriv = dev->private; 422 unsigned int chan = CR_CHAN(insn->chanspec); 423 unsigned int range = CR_RANGE(insn->chanspec); 424 unsigned int val; 425 int i; 426 427 /* set the range for this channel */ 428 val = devpriv->ao_range; 429 val &= ~DAS6402_AO_RANGE_MASK(chan); 430 val |= DAS6402_AO_RANGE(chan, range); 431 if (val != devpriv->ao_range) { 432 devpriv->ao_range = val; 433 outb(val, dev->iobase + DAS6402_TRIG_REG); 434 } 435 436 /* 437 * The DAS6402/16 has a jumper to select either individual 438 * update (UPDATE) or simultaneous updating (XFER) of both 439 * DAC's. In UPDATE mode, when the MSB is written, that DAC 440 * is updated. In XFER mode, after both DAC's are loaded, 441 * a read cycle of any DAC register will update both DAC's 442 * simultaneously. 443 * 444 * If you have XFER mode enabled a (*insn_read) will need 445 * to be performed in order to update the DAC's with the 446 * last value written. 447 */ 448 for (i = 0; i < insn->n; i++) { 449 val = data[i]; 450 451 s->readback[chan] = val; 452 453 if (s->maxdata == 0x0fff) { 454 /* 455 * DAS6402/12 has the two 8-bit DAC registers, left 456 * justified (the 4 LSB bits are don't care). Data 457 * can be written as one word. 458 */ 459 val <<= 4; 460 outw(val, dev->iobase + DAS6402_AO_DATA_REG(chan)); 461 } else { 462 /* 463 * DAS6402/16 uses both 8-bit DAC registers and needs 464 * to be written LSB then MSB. 465 */ 466 outb(val & 0xff, 467 dev->iobase + DAS6402_AO_LSB_REG(chan)); 468 outb((val >> 8) & 0xff, 469 dev->iobase + DAS6402_AO_LSB_REG(chan)); 470 } 471 } 472 473 return insn->n; 474 } 475 476 static int das6402_ao_insn_read(struct comedi_device *dev, 477 struct comedi_subdevice *s, 478 struct comedi_insn *insn, 479 unsigned int *data) 480 { 481 unsigned int chan = CR_CHAN(insn->chanspec); 482 483 /* 484 * If XFER mode is enabled, reading any DAC register 485 * will update both DAC's simultaneously. 486 */ 487 inw(dev->iobase + DAS6402_AO_LSB_REG(chan)); 488 489 return comedi_readback_insn_read(dev, s, insn, data); 490 } 491 492 static int das6402_di_insn_bits(struct comedi_device *dev, 493 struct comedi_subdevice *s, 494 struct comedi_insn *insn, 495 unsigned int *data) 496 { 497 data[1] = inb(dev->iobase + DAS6402_DI_DO_REG); 498 499 return insn->n; 500 } 501 502 static int das6402_do_insn_bits(struct comedi_device *dev, 503 struct comedi_subdevice *s, 504 struct comedi_insn *insn, 505 unsigned int *data) 506 { 507 if (comedi_dio_update_state(s, data)) 508 outb(s->state, dev->iobase + DAS6402_DI_DO_REG); 509 510 data[1] = s->state; 511 512 return insn->n; 513 } 514 515 static void das6402_reset(struct comedi_device *dev) 516 { 517 struct das6402_private *devpriv = dev->private; 518 519 /* enable "Enhanced" mode */ 520 outb(DAS6402_MODE_ENHANCED, dev->iobase + DAS6402_MODE_REG); 521 522 /* enable 10MHz pacer clock */ 523 das6402_set_extended(dev, DAS6402_STATUS_W_10MHZ); 524 525 /* enable software conversion trigger */ 526 outb(DAS6402_CTRL_SOFT_TRIG, dev->iobase + DAS6402_CTRL_REG); 527 528 /* default ADC to single-ended unipolar 10V inputs */ 529 das6402_set_mode(dev, DAS6402_MODE_RANGE(0) | 530 DAS6402_MODE_POLLED | 531 DAS6402_MODE_SE | 532 DAS6402_MODE_UNI); 533 534 /* default mux for single channel conversion (channel 0) */ 535 outw(DAS6402_AI_MUX_HI(0) | DAS6402_AI_MUX_LO(0), 536 dev->iobase + DAS6402_AI_MUX_REG); 537 538 /* set both DAC's for unipolar 5V output range */ 539 devpriv->ao_range = DAS6402_AO_RANGE(0, 2) | DAS6402_AO_RANGE(1, 2); 540 outb(devpriv->ao_range, dev->iobase + DAS6402_TRIG_REG); 541 542 /* set both DAC's to 0V */ 543 outw(0, dev->iobase + DAS6402_AO_DATA_REG(0)); 544 outw(0, dev->iobase + DAS6402_AO_DATA_REG(0)); 545 inw(dev->iobase + DAS6402_AO_LSB_REG(0)); 546 547 /* set all digital outputs low */ 548 outb(0, dev->iobase + DAS6402_DI_DO_REG); 549 550 das6402_clear_all_interrupts(dev); 551 } 552 553 static int das6402_attach(struct comedi_device *dev, 554 struct comedi_devconfig *it) 555 { 556 const struct das6402_boardinfo *board = dev->board_ptr; 557 struct das6402_private *devpriv; 558 struct comedi_subdevice *s; 559 int ret; 560 561 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 562 if (!devpriv) 563 return -ENOMEM; 564 565 ret = comedi_request_region(dev, it->options[0], 0x10); 566 if (ret) 567 return ret; 568 569 das6402_reset(dev); 570 571 /* IRQs 2,3,5,6,7, 10,11,15 are valid for "enhanced" mode */ 572 if ((1 << it->options[1]) & 0x8cec) { 573 ret = request_irq(it->options[1], das6402_interrupt, 0, 574 dev->board_name, dev); 575 if (ret == 0) { 576 dev->irq = it->options[1]; 577 578 switch (dev->irq) { 579 case 10: 580 devpriv->irq = 4; 581 break; 582 case 11: 583 devpriv->irq = 1; 584 break; 585 case 15: 586 devpriv->irq = 6; 587 break; 588 default: 589 devpriv->irq = dev->irq; 590 break; 591 } 592 } 593 } 594 595 dev->pacer = comedi_8254_init(dev->iobase + DAS6402_TIMER_BASE, 596 I8254_OSC_BASE_10MHZ, I8254_IO8, 0); 597 if (!dev->pacer) 598 return -ENOMEM; 599 600 ret = comedi_alloc_subdevices(dev, 4); 601 if (ret) 602 return ret; 603 604 /* Analog Input subdevice */ 605 s = &dev->subdevices[0]; 606 s->type = COMEDI_SUBD_AI; 607 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF; 608 s->n_chan = 64; 609 s->maxdata = board->maxdata; 610 s->range_table = &das6402_ai_ranges; 611 s->insn_read = das6402_ai_insn_read; 612 if (dev->irq) { 613 dev->read_subdev = s; 614 s->subdev_flags |= SDF_CMD_READ; 615 s->len_chanlist = s->n_chan; 616 s->do_cmdtest = das6402_ai_cmdtest; 617 s->do_cmd = das6402_ai_cmd; 618 s->cancel = das6402_ai_cancel; 619 } 620 621 /* Analog Output subdevice */ 622 s = &dev->subdevices[1]; 623 s->type = COMEDI_SUBD_AO; 624 s->subdev_flags = SDF_WRITABLE; 625 s->n_chan = 2; 626 s->maxdata = board->maxdata; 627 s->range_table = &das6402_ao_ranges; 628 s->insn_write = das6402_ao_insn_write; 629 s->insn_read = das6402_ao_insn_read; 630 631 ret = comedi_alloc_subdev_readback(s); 632 if (ret) 633 return ret; 634 635 /* Digital Input subdevice */ 636 s = &dev->subdevices[2]; 637 s->type = COMEDI_SUBD_DI; 638 s->subdev_flags = SDF_READABLE; 639 s->n_chan = 8; 640 s->maxdata = 1; 641 s->range_table = &range_digital; 642 s->insn_bits = das6402_di_insn_bits; 643 644 /* Digital Input subdevice */ 645 s = &dev->subdevices[3]; 646 s->type = COMEDI_SUBD_DO; 647 s->subdev_flags = SDF_WRITABLE; 648 s->n_chan = 8; 649 s->maxdata = 1; 650 s->range_table = &range_digital; 651 s->insn_bits = das6402_do_insn_bits; 652 653 return 0; 654 } 655 656 static struct comedi_driver das6402_driver = { 657 .driver_name = "das6402", 658 .module = THIS_MODULE, 659 .attach = das6402_attach, 660 .detach = comedi_legacy_detach, 661 .board_name = &das6402_boards[0].name, 662 .num_names = ARRAY_SIZE(das6402_boards), 663 .offset = sizeof(struct das6402_boardinfo), 664 }; 665 module_comedi_driver(das6402_driver) 666 667 MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>"); 668 MODULE_DESCRIPTION("Comedi driver for DAS6402 compatible boards"); 669 MODULE_LICENSE("GPL"); 670