1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * comedi/drivers/das800.c 4 * Driver for Keitley das800 series boards and compatibles 5 * Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net> 6 * 7 * COMEDI - Linux Control and Measurement Device Interface 8 * Copyright (C) 2000 David A. Schleef <ds@schleef.org> 9 */ 10/* 11 * Driver: das800 12 * Description: Keithley Metrabyte DAS800 (& compatibles) 13 * Author: Frank Mori Hess <fmhess@users.sourceforge.net> 14 * Devices: [Keithley Metrabyte] DAS-800 (das-800), DAS-801 (das-801), 15 * DAS-802 (das-802), 16 * [Measurement Computing] CIO-DAS800 (cio-das800), 17 * CIO-DAS801 (cio-das801), CIO-DAS802 (cio-das802), 18 * CIO-DAS802/16 (cio-das802/16) 19 * Status: works, cio-das802/16 untested - email me if you have tested it 20 * 21 * Configuration options: 22 * [0] - I/O port base address 23 * [1] - IRQ (optional, required for timed or externally triggered conversions) 24 * 25 * Notes: 26 * IRQ can be omitted, although the cmd interface will not work without it. 27 * 28 * All entries in the channel/gain list must use the same gain and be 29 * consecutive channels counting upwards in channel number (these are 30 * hardware limitations.) 31 * 32 * I've never tested the gain setting stuff since I only have a 33 * DAS-800 board with fixed gain. 34 * 35 * The cio-das802/16 does not have a fifo-empty status bit! Therefore 36 * only fifo-half-full transfers are possible with this card. 37 * 38 * cmd triggers supported: 39 * start_src: TRIG_NOW | TRIG_EXT 40 * scan_begin_src: TRIG_FOLLOW 41 * scan_end_src: TRIG_COUNT 42 * convert_src: TRIG_TIMER | TRIG_EXT 43 * stop_src: TRIG_NONE | TRIG_COUNT 44 */ 45 46#include <linux/module.h> 47#include <linux/interrupt.h> 48#include <linux/delay.h> 49#include <linux/comedi/comedidev.h> 50#include <linux/comedi/comedi_8254.h> 51 52#define N_CHAN_AI 8 /* number of analog input channels */ 53 54/* Registers for the das800 */ 55 56#define DAS800_LSB 0 57#define FIFO_EMPTY 0x1 58#define FIFO_OVF 0x2 59#define DAS800_MSB 1 60#define DAS800_CONTROL1 2 61#define CONTROL1_INTE 0x8 62#define DAS800_CONV_CONTROL 2 63#define ITE 0x1 64#define CASC 0x2 65#define DTEN 0x4 66#define IEOC 0x8 67#define EACS 0x10 68#define CONV_HCEN 0x80 69#define DAS800_SCAN_LIMITS 2 70#define DAS800_STATUS 2 71#define IRQ 0x8 72#define BUSY 0x80 73#define DAS800_GAIN 3 74#define CIO_FFOV 0x8 /* cio-das802/16 fifo overflow */ 75#define CIO_ENHF 0x90 /* cio-das802/16 fifo half full int ena */ 76#define CONTROL1 0x80 77#define CONV_CONTROL 0xa0 78#define SCAN_LIMITS 0xc0 79#define ID 0xe0 80#define DAS800_8254 4 81#define DAS800_STATUS2 7 82#define STATUS2_HCEN 0x80 83#define STATUS2_INTE 0X20 84#define DAS800_ID 7 85 86#define DAS802_16_HALF_FIFO_SZ 128 87 88struct das800_board { 89 const char *name; 90 int ai_speed; 91 const struct comedi_lrange *ai_range; 92 int resolution; 93}; 94 95static const struct comedi_lrange range_das801_ai = { 96 9, { 97 BIP_RANGE(5), 98 BIP_RANGE(10), 99 UNI_RANGE(10), 100 BIP_RANGE(0.5), 101 UNI_RANGE(1), 102 BIP_RANGE(0.05), 103 UNI_RANGE(0.1), 104 BIP_RANGE(0.01), 105 UNI_RANGE(0.02) 106 } 107}; 108 109static const struct comedi_lrange range_cio_das801_ai = { 110 9, { 111 BIP_RANGE(5), 112 BIP_RANGE(10), 113 UNI_RANGE(10), 114 BIP_RANGE(0.5), 115 UNI_RANGE(1), 116 BIP_RANGE(0.05), 117 UNI_RANGE(0.1), 118 BIP_RANGE(0.005), 119 UNI_RANGE(0.01) 120 } 121}; 122 123static const struct comedi_lrange range_das802_ai = { 124 9, { 125 BIP_RANGE(5), 126 BIP_RANGE(10), 127 UNI_RANGE(10), 128 BIP_RANGE(2.5), 129 UNI_RANGE(5), 130 BIP_RANGE(1.25), 131 UNI_RANGE(2.5), 132 BIP_RANGE(0.625), 133 UNI_RANGE(1.25) 134 } 135}; 136 137static const struct comedi_lrange range_das80216_ai = { 138 8, { 139 BIP_RANGE(10), 140 UNI_RANGE(10), 141 BIP_RANGE(5), 142 UNI_RANGE(5), 143 BIP_RANGE(2.5), 144 UNI_RANGE(2.5), 145 BIP_RANGE(1.25), 146 UNI_RANGE(1.25) 147 } 148}; 149 150enum das800_boardinfo { 151 BOARD_DAS800, 152 BOARD_CIODAS800, 153 BOARD_DAS801, 154 BOARD_CIODAS801, 155 BOARD_DAS802, 156 BOARD_CIODAS802, 157 BOARD_CIODAS80216, 158}; 159 160static const struct das800_board das800_boards[] = { 161 [BOARD_DAS800] = { 162 .name = "das-800", 163 .ai_speed = 25000, 164 .ai_range = &range_bipolar5, 165 .resolution = 12, 166 }, 167 [BOARD_CIODAS800] = { 168 .name = "cio-das800", 169 .ai_speed = 20000, 170 .ai_range = &range_bipolar5, 171 .resolution = 12, 172 }, 173 [BOARD_DAS801] = { 174 .name = "das-801", 175 .ai_speed = 25000, 176 .ai_range = &range_das801_ai, 177 .resolution = 12, 178 }, 179 [BOARD_CIODAS801] = { 180 .name = "cio-das801", 181 .ai_speed = 20000, 182 .ai_range = &range_cio_das801_ai, 183 .resolution = 12, 184 }, 185 [BOARD_DAS802] = { 186 .name = "das-802", 187 .ai_speed = 25000, 188 .ai_range = &range_das802_ai, 189 .resolution = 12, 190 }, 191 [BOARD_CIODAS802] = { 192 .name = "cio-das802", 193 .ai_speed = 20000, 194 .ai_range = &range_das802_ai, 195 .resolution = 12, 196 }, 197 [BOARD_CIODAS80216] = { 198 .name = "cio-das802/16", 199 .ai_speed = 10000, 200 .ai_range = &range_das80216_ai, 201 .resolution = 16, 202 }, 203}; 204 205struct das800_private { 206 unsigned int do_bits; /* digital output bits */ 207}; 208 209static void das800_ind_write(struct comedi_device *dev, 210 unsigned int val, unsigned int reg) 211{ 212 /* 213 * Select dev->iobase + 2 to be desired register 214 * then write to that register. 215 */ 216 outb(reg, dev->iobase + DAS800_GAIN); 217 outb(val, dev->iobase + 2); 218} 219 220static unsigned int das800_ind_read(struct comedi_device *dev, unsigned int reg) 221{ 222 /* 223 * Select dev->iobase + 7 to be desired register 224 * then read from that register. 225 */ 226 outb(reg, dev->iobase + DAS800_GAIN); 227 return inb(dev->iobase + 7); 228} 229 230static void das800_enable(struct comedi_device *dev) 231{ 232 const struct das800_board *board = dev->board_ptr; 233 struct das800_private *devpriv = dev->private; 234 unsigned long irq_flags; 235 236 spin_lock_irqsave(&dev->spinlock, irq_flags); 237 /* enable fifo-half full interrupts for cio-das802/16 */ 238 if (board->resolution == 16) 239 outb(CIO_ENHF, dev->iobase + DAS800_GAIN); 240 /* enable hardware triggering */ 241 das800_ind_write(dev, CONV_HCEN, CONV_CONTROL); 242 /* enable card's interrupt */ 243 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1); 244 spin_unlock_irqrestore(&dev->spinlock, irq_flags); 245} 246 247static void das800_disable(struct comedi_device *dev) 248{ 249 unsigned long irq_flags; 250 251 spin_lock_irqsave(&dev->spinlock, irq_flags); 252 /* disable hardware triggering of conversions */ 253 das800_ind_write(dev, 0x0, CONV_CONTROL); 254 spin_unlock_irqrestore(&dev->spinlock, irq_flags); 255} 256 257static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s) 258{ 259 das800_disable(dev); 260 return 0; 261} 262 263static int das800_ai_check_chanlist(struct comedi_device *dev, 264 struct comedi_subdevice *s, 265 struct comedi_cmd *cmd) 266{ 267 unsigned int chan0 = CR_CHAN(cmd->chanlist[0]); 268 unsigned int range0 = CR_RANGE(cmd->chanlist[0]); 269 int i; 270 271 for (i = 1; i < cmd->chanlist_len; i++) { 272 unsigned int chan = CR_CHAN(cmd->chanlist[i]); 273 unsigned int range = CR_RANGE(cmd->chanlist[i]); 274 275 if (chan != (chan0 + i) % s->n_chan) { 276 dev_dbg(dev->class_dev, 277 "chanlist must be consecutive, counting upwards\n"); 278 return -EINVAL; 279 } 280 281 if (range != range0) { 282 dev_dbg(dev->class_dev, 283 "chanlist must all have the same gain\n"); 284 return -EINVAL; 285 } 286 } 287 288 return 0; 289} 290 291static int das800_ai_do_cmdtest(struct comedi_device *dev, 292 struct comedi_subdevice *s, 293 struct comedi_cmd *cmd) 294{ 295 const struct das800_board *board = dev->board_ptr; 296 int err = 0; 297 298 /* Step 1 : check if triggers are trivially valid */ 299 300 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT); 301 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW); 302 err |= comedi_check_trigger_src(&cmd->convert_src, 303 TRIG_TIMER | TRIG_EXT); 304 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); 305 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); 306 307 if (err) 308 return 1; 309 310 /* Step 2a : make sure trigger sources are unique */ 311 312 err |= comedi_check_trigger_is_unique(cmd->start_src); 313 err |= comedi_check_trigger_is_unique(cmd->convert_src); 314 err |= comedi_check_trigger_is_unique(cmd->stop_src); 315 316 /* Step 2b : and mutually compatible */ 317 318 if (err) 319 return 2; 320 321 /* Step 3: check if arguments are trivially valid */ 322 323 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); 324 325 if (cmd->convert_src == TRIG_TIMER) { 326 err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 327 board->ai_speed); 328 } 329 330 err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1); 331 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, 332 cmd->chanlist_len); 333 334 if (cmd->stop_src == TRIG_COUNT) 335 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1); 336 else /* TRIG_NONE */ 337 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); 338 339 if (err) 340 return 3; 341 342 /* step 4: fix up any arguments */ 343 344 if (cmd->convert_src == TRIG_TIMER) { 345 unsigned int arg = cmd->convert_arg; 346 347 comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags); 348 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg); 349 } 350 351 if (err) 352 return 4; 353 354 /* Step 5: check channel list if it exists */ 355 if (cmd->chanlist && cmd->chanlist_len > 0) 356 err |= das800_ai_check_chanlist(dev, s, cmd); 357 358 if (err) 359 return 5; 360 361 return 0; 362} 363 364static int das800_ai_do_cmd(struct comedi_device *dev, 365 struct comedi_subdevice *s) 366{ 367 const struct das800_board *board = dev->board_ptr; 368 struct comedi_async *async = s->async; 369 struct comedi_cmd *cmd = &async->cmd; 370 unsigned int gain = CR_RANGE(cmd->chanlist[0]); 371 unsigned int start_chan = CR_CHAN(cmd->chanlist[0]); 372 unsigned int end_chan = (start_chan + cmd->chanlist_len - 1) % 8; 373 unsigned int scan_chans = (end_chan << 3) | start_chan; 374 int conv_bits; 375 unsigned long irq_flags; 376 377 das800_disable(dev); 378 379 spin_lock_irqsave(&dev->spinlock, irq_flags); 380 /* set scan limits */ 381 das800_ind_write(dev, scan_chans, SCAN_LIMITS); 382 spin_unlock_irqrestore(&dev->spinlock, irq_flags); 383 384 /* set gain */ 385 if (board->resolution == 12 && gain > 0) 386 gain += 0x7; 387 gain &= 0xf; 388 outb(gain, dev->iobase + DAS800_GAIN); 389 390 /* enable auto channel scan, send interrupts on end of conversion 391 * and set clock source to internal or external 392 */ 393 conv_bits = 0; 394 conv_bits |= EACS | IEOC; 395 if (cmd->start_src == TRIG_EXT) 396 conv_bits |= DTEN; 397 if (cmd->convert_src == TRIG_TIMER) { 398 conv_bits |= CASC | ITE; 399 comedi_8254_update_divisors(dev->pacer); 400 comedi_8254_pacer_enable(dev->pacer, 1, 2, true); 401 } 402 403 spin_lock_irqsave(&dev->spinlock, irq_flags); 404 das800_ind_write(dev, conv_bits, CONV_CONTROL); 405 spin_unlock_irqrestore(&dev->spinlock, irq_flags); 406 407 das800_enable(dev); 408 return 0; 409} 410 411static unsigned int das800_ai_get_sample(struct comedi_device *dev) 412{ 413 unsigned int lsb = inb(dev->iobase + DAS800_LSB); 414 unsigned int msb = inb(dev->iobase + DAS800_MSB); 415 416 return (msb << 8) | lsb; 417} 418 419static irqreturn_t das800_interrupt(int irq, void *d) 420{ 421 struct comedi_device *dev = d; 422 struct das800_private *devpriv = dev->private; 423 struct comedi_subdevice *s = dev->read_subdev; 424 struct comedi_async *async; 425 struct comedi_cmd *cmd; 426 unsigned long irq_flags; 427 unsigned int status; 428 unsigned short val; 429 bool fifo_empty; 430 bool fifo_overflow; 431 int i; 432 433 status = inb(dev->iobase + DAS800_STATUS); 434 if (!(status & IRQ)) 435 return IRQ_NONE; 436 if (!dev->attached) 437 return IRQ_HANDLED; 438 439 async = s->async; 440 cmd = &async->cmd; 441 442 spin_lock_irqsave(&dev->spinlock, irq_flags); 443 status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN; 444 /* 445 * Don't release spinlock yet since we want to make sure 446 * no one else disables hardware conversions. 447 */ 448 449 /* if hardware conversions are not enabled, then quit */ 450 if (status == 0) { 451 spin_unlock_irqrestore(&dev->spinlock, irq_flags); 452 return IRQ_HANDLED; 453 } 454 455 for (i = 0; i < DAS802_16_HALF_FIFO_SZ; i++) { 456 val = das800_ai_get_sample(dev); 457 if (s->maxdata == 0x0fff) { 458 fifo_empty = !!(val & FIFO_EMPTY); 459 fifo_overflow = !!(val & FIFO_OVF); 460 } else { 461 /* cio-das802/16 has no fifo empty status bit */ 462 fifo_empty = false; 463 fifo_overflow = !!(inb(dev->iobase + DAS800_GAIN) & 464 CIO_FFOV); 465 } 466 if (fifo_empty || fifo_overflow) 467 break; 468 469 if (s->maxdata == 0x0fff) 470 val >>= 4; /* 12-bit sample */ 471 472 val &= s->maxdata; 473 comedi_buf_write_samples(s, &val, 1); 474 475 if (cmd->stop_src == TRIG_COUNT && 476 async->scans_done >= cmd->stop_arg) { 477 async->events |= COMEDI_CB_EOA; 478 break; 479 } 480 } 481 482 if (fifo_overflow) { 483 spin_unlock_irqrestore(&dev->spinlock, irq_flags); 484 async->events |= COMEDI_CB_ERROR; 485 comedi_handle_events(dev, s); 486 return IRQ_HANDLED; 487 } 488 489 if (!(async->events & COMEDI_CB_CANCEL_MASK)) { 490 /* 491 * Re-enable card's interrupt. 492 * We already have spinlock, so indirect addressing is safe 493 */ 494 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, 495 CONTROL1); 496 spin_unlock_irqrestore(&dev->spinlock, irq_flags); 497 } else { 498 /* otherwise, stop taking data */ 499 spin_unlock_irqrestore(&dev->spinlock, irq_flags); 500 das800_disable(dev); 501 } 502 comedi_handle_events(dev, s); 503 return IRQ_HANDLED; 504} 505 506static int das800_ai_eoc(struct comedi_device *dev, 507 struct comedi_subdevice *s, 508 struct comedi_insn *insn, 509 unsigned long context) 510{ 511 unsigned int status; 512 513 status = inb(dev->iobase + DAS800_STATUS); 514 if ((status & BUSY) == 0) 515 return 0; 516 return -EBUSY; 517} 518 519static int das800_ai_insn_read(struct comedi_device *dev, 520 struct comedi_subdevice *s, 521 struct comedi_insn *insn, 522 unsigned int *data) 523{ 524 struct das800_private *devpriv = dev->private; 525 unsigned int chan = CR_CHAN(insn->chanspec); 526 unsigned int range = CR_RANGE(insn->chanspec); 527 unsigned long irq_flags; 528 unsigned int val; 529 int ret; 530 int i; 531 532 das800_disable(dev); 533 534 /* set multiplexer */ 535 spin_lock_irqsave(&dev->spinlock, irq_flags); 536 das800_ind_write(dev, chan | devpriv->do_bits, CONTROL1); 537 spin_unlock_irqrestore(&dev->spinlock, irq_flags); 538 539 /* set gain / range */ 540 if (s->maxdata == 0x0fff && range) 541 range += 0x7; 542 range &= 0xf; 543 outb(range, dev->iobase + DAS800_GAIN); 544 545 udelay(5); 546 547 for (i = 0; i < insn->n; i++) { 548 /* trigger conversion */ 549 outb_p(0, dev->iobase + DAS800_MSB); 550 551 ret = comedi_timeout(dev, s, insn, das800_ai_eoc, 0); 552 if (ret) 553 return ret; 554 555 val = das800_ai_get_sample(dev); 556 if (s->maxdata == 0x0fff) 557 val >>= 4; /* 12-bit sample */ 558 data[i] = val & s->maxdata; 559 } 560 561 return insn->n; 562} 563 564static int das800_di_insn_bits(struct comedi_device *dev, 565 struct comedi_subdevice *s, 566 struct comedi_insn *insn, 567 unsigned int *data) 568{ 569 data[1] = (inb(dev->iobase + DAS800_STATUS) >> 4) & 0x7; 570 571 return insn->n; 572} 573 574static int das800_do_insn_bits(struct comedi_device *dev, 575 struct comedi_subdevice *s, 576 struct comedi_insn *insn, 577 unsigned int *data) 578{ 579 struct das800_private *devpriv = dev->private; 580 unsigned long irq_flags; 581 582 if (comedi_dio_update_state(s, data)) { 583 devpriv->do_bits = s->state << 4; 584 585 spin_lock_irqsave(&dev->spinlock, irq_flags); 586 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, 587 CONTROL1); 588 spin_unlock_irqrestore(&dev->spinlock, irq_flags); 589 } 590 591 data[1] = s->state; 592 593 return insn->n; 594} 595 596static const struct das800_board *das800_probe(struct comedi_device *dev) 597{ 598 const struct das800_board *board = dev->board_ptr; 599 int index = board ? board - das800_boards : -EINVAL; 600 int id_bits; 601 unsigned long irq_flags; 602 603 /* 604 * The dev->board_ptr will be set by comedi_device_attach() if the 605 * board name provided by the user matches a board->name in this 606 * driver. If so, this function sanity checks the id_bits to verify 607 * that the board is correct. 608 * 609 * If the dev->board_ptr is not set, the user is trying to attach 610 * an unspecified board to this driver. In this case the id_bits 611 * are used to 'probe' for the correct dev->board_ptr. 612 */ 613 spin_lock_irqsave(&dev->spinlock, irq_flags); 614 id_bits = das800_ind_read(dev, ID) & 0x3; 615 spin_unlock_irqrestore(&dev->spinlock, irq_flags); 616 617 switch (id_bits) { 618 case 0x0: 619 if (index == BOARD_DAS800 || index == BOARD_CIODAS800) 620 return board; 621 index = BOARD_DAS800; 622 break; 623 case 0x2: 624 if (index == BOARD_DAS801 || index == BOARD_CIODAS801) 625 return board; 626 index = BOARD_DAS801; 627 break; 628 case 0x3: 629 if (index == BOARD_DAS802 || index == BOARD_CIODAS802 || 630 index == BOARD_CIODAS80216) 631 return board; 632 index = BOARD_DAS802; 633 break; 634 default: 635 dev_dbg(dev->class_dev, "Board model: 0x%x (unknown)\n", 636 id_bits); 637 return NULL; 638 } 639 dev_dbg(dev->class_dev, "Board model (probed): %s series\n", 640 das800_boards[index].name); 641 642 return &das800_boards[index]; 643} 644 645static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it) 646{ 647 const struct das800_board *board; 648 struct das800_private *devpriv; 649 struct comedi_subdevice *s; 650 unsigned int irq = it->options[1]; 651 unsigned long irq_flags; 652 int ret; 653 654 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 655 if (!devpriv) 656 return -ENOMEM; 657 658 ret = comedi_request_region(dev, it->options[0], 0x8); 659 if (ret) 660 return ret; 661 662 board = das800_probe(dev); 663 if (!board) 664 return -ENODEV; 665 dev->board_ptr = board; 666 dev->board_name = board->name; 667 668 if (irq > 1 && irq <= 7) { 669 ret = request_irq(irq, das800_interrupt, 0, "das800", 670 dev); 671 if (ret == 0) 672 dev->irq = irq; 673 } 674 675 dev->pacer = comedi_8254_io_alloc(dev->iobase + DAS800_8254, 676 I8254_OSC_BASE_1MHZ, I8254_IO8, 0); 677 if (IS_ERR(dev->pacer)) 678 return PTR_ERR(dev->pacer); 679 680 ret = comedi_alloc_subdevices(dev, 3); 681 if (ret) 682 return ret; 683 684 /* Analog Input subdevice */ 685 s = &dev->subdevices[0]; 686 dev->read_subdev = s; 687 s->type = COMEDI_SUBD_AI; 688 s->subdev_flags = SDF_READABLE | SDF_GROUND; 689 s->n_chan = 8; 690 s->maxdata = (1 << board->resolution) - 1; 691 s->range_table = board->ai_range; 692 s->insn_read = das800_ai_insn_read; 693 if (dev->irq) { 694 s->subdev_flags |= SDF_CMD_READ; 695 s->len_chanlist = 8; 696 s->do_cmdtest = das800_ai_do_cmdtest; 697 s->do_cmd = das800_ai_do_cmd; 698 s->cancel = das800_cancel; 699 } 700 701 /* Digital Input subdevice */ 702 s = &dev->subdevices[1]; 703 s->type = COMEDI_SUBD_DI; 704 s->subdev_flags = SDF_READABLE; 705 s->n_chan = 3; 706 s->maxdata = 1; 707 s->range_table = &range_digital; 708 s->insn_bits = das800_di_insn_bits; 709 710 /* Digital Output subdevice */ 711 s = &dev->subdevices[2]; 712 s->type = COMEDI_SUBD_DO; 713 s->subdev_flags = SDF_WRITABLE; 714 s->n_chan = 4; 715 s->maxdata = 1; 716 s->range_table = &range_digital; 717 s->insn_bits = das800_do_insn_bits; 718 719 das800_disable(dev); 720 721 /* initialize digital out channels */ 722 spin_lock_irqsave(&dev->spinlock, irq_flags); 723 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1); 724 spin_unlock_irqrestore(&dev->spinlock, irq_flags); 725 726 return 0; 727}; 728 729static struct comedi_driver driver_das800 = { 730 .driver_name = "das800", 731 .module = THIS_MODULE, 732 .attach = das800_attach, 733 .detach = comedi_legacy_detach, 734 .num_names = ARRAY_SIZE(das800_boards), 735 .board_name = &das800_boards[0].name, 736 .offset = sizeof(struct das800_board), 737}; 738module_comedi_driver(driver_das800); 739 740MODULE_AUTHOR("Comedi https://www.comedi.org"); 741MODULE_DESCRIPTION("Comedi low-level driver"); 742MODULE_LICENSE("GPL"); 743