2 comedi/drivers/das1800.c
3 Driver for Keitley das1700/das1800 series boards
4 Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net>
6 COMEDI - Linux Control and Measurement Device Interface
7 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
21 Description: Keithley Metrabyte DAS1800 (& compatibles)
22 Author: Frank Mori Hess <fmhess@users.sourceforge.net>
23 Devices: [Keithley Metrabyte] DAS-1701ST (das-1701st),
24 DAS-1701ST-DA (das-1701st-da), DAS-1701/AO (das-1701ao),
25 DAS-1702ST (das-1702st), DAS-1702ST-DA (das-1702st-da),
26 DAS-1702HR (das-1702hr), DAS-1702HR-DA (das-1702hr-da),
27 DAS-1702/AO (das-1702ao), DAS-1801ST (das-1801st),
28 DAS-1801ST-DA (das-1801st-da), DAS-1801HC (das-1801hc),
29 DAS-1801AO (das-1801ao), DAS-1802ST (das-1802st),
30 DAS-1802ST-DA (das-1802st-da), DAS-1802HR (das-1802hr),
31 DAS-1802HR-DA (das-1802hr-da), DAS-1802HC (das-1802hc),
32 DAS-1802AO (das-1802ao)
35 The waveform analog output on the 'ao' cards is not supported.
36 If you need it, send me (Frank Hess) an email.
38 Configuration options:
39 [0] - I/O port base address
40 [1] - IRQ (optional, required for timed or externally triggered conversions)
41 [2] - DMA0 (optional, requires irq)
42 [3] - DMA1 (optional, requires irq and dma0)
46 This driver supports the following Keithley boards:
69 [1] - irq (optional, required for timed or externally triggered conversions)
70 [2] - dma0 (optional, requires irq)
71 [3] - dma1 (optional, requires irq and dma0)
73 irq can be omitted, although the cmd interface will not work without it.
75 analog input cmd triggers supported:
76 start_src: TRIG_NOW | TRIG_EXT
77 scan_begin_src: TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT
78 scan_end_src: TRIG_COUNT
79 convert_src: TRIG_TIMER | TRIG_EXT (TRIG_EXT requires scan_begin_src == TRIG_FOLLOW)
80 stop_src: TRIG_COUNT | TRIG_EXT | TRIG_NONE
82 scan_begin_src triggers TRIG_TIMER and TRIG_EXT use the card's
83 'burst mode' which limits the valid conversion time to 64 microseconds
84 (convert_arg <= 64000). This limitation does not apply if scan_begin_src
88 Only the DAS-1801ST has been tested by me.
89 Unipolar and bipolar ranges cannot be mixed in the channel/gain list.
92 Make it automatically allocate irq and dma channels if they are not specified
93 Add support for analog out on 'ao' cards
94 read insn for analog out
97 #include <linux/module.h>
98 #include <linux/interrupt.h>
99 #include <linux/slab.h>
100 #include <linux/io.h>
101 #include "../comedidev.h"
106 #include "comedi_fc.h"
109 #define DAS1800_SIZE 16 /* uses 16 io addresses */
110 #define FIFO_SIZE 1024 /* 1024 sample fifo */
111 #define UNIPOLAR 0x4 /* bit that determines whether input range is uni/bipolar */
112 #define DMA_BUF_SIZE 0x1ff00 /* size in bytes of dma buffers */
114 /* Registers for the das1800 */
115 #define DAS1800_FIFO 0x0
116 #define DAS1800_QRAM 0x0
117 #define DAS1800_DAC 0x0
118 #define DAS1800_SELECT 0x2
121 #define DAC(a) (0x2 + a)
122 #define DAS1800_DIGITAL 0x3
123 #define DAS1800_CONTROL_A 0x4
130 #define DAS1800_CONTROL_B 0x5
134 #define DMA_CH5_CH6 0x5
135 #define DMA_CH6_CH7 0x6
136 #define DMA_CH7_CH5 0x7
137 #define DMA_ENABLED 0x3 /* mask used to determine if dma is enabled */
146 #define DAS1800_CONTROL_C 0X6
154 #define DAS1800_STATUS 0x7
155 /* bits that prevent interrupt status bits (and CVEN) from being cleared on write */
156 #define CLEAR_INTR_MASK (CVEN_MASK | 0x1f)
163 #define CVEN_MASK 0x40 /* masks CVEN on write */
165 #define DAS1800_BURST_LENGTH 0x8
166 #define DAS1800_BURST_RATE 0x9
167 #define DAS1800_QRAM_ADDRESS 0xa
168 #define DAS1800_COUNTER 0xc
170 #define IOBASE2 0x400 /* offset of additional ioports used on 'ao' cards */
173 das1701st
, das1701st_da
, das1702st
, das1702st_da
, das1702hr
,
175 das1701ao
, das1702ao
, das1801st
, das1801st_da
, das1802st
, das1802st_da
,
176 das1802hr
, das1802hr_da
, das1801hc
, das1802hc
, das1801ao
, das1802ao
179 /* analog input ranges */
180 static const struct comedi_lrange range_ai_das1801
= {
193 static const struct comedi_lrange range_ai_das1802
= {
206 struct das1800_board
{
208 int ai_speed
; /* max conversion period in nanoseconds */
209 int resolution
; /* bits of ai resolution */
210 int qram_len
; /* length of card's channel / gain queue */
211 int common
; /* supports AREF_COMMON flag */
212 int do_n_chan
; /* number of digital output channels */
213 int ao_ability
; /* 0 == no analog out, 1 == basic analog out, 2 == waveform analog out */
214 int ao_n_chan
; /* number of analog out channels */
215 const struct comedi_lrange
*range_ai
; /* available input ranges */
218 /* Warning: the maximum conversion speeds listed below are
219 * not always achievable depending on board setup (see
222 static const struct das1800_board das1800_boards
[] = {
224 .name
= "das-1701st",
232 .range_ai
= &range_ai_das1801
,
235 .name
= "das-1701st-da",
243 .range_ai
= &range_ai_das1801
,
246 .name
= "das-1702st",
254 .range_ai
= &range_ai_das1802
,
257 .name
= "das-1702st-da",
265 .range_ai
= &range_ai_das1802
,
268 .name
= "das-1702hr",
276 .range_ai
= &range_ai_das1802
,
279 .name
= "das-1702hr-da",
287 .range_ai
= &range_ai_das1802
,
290 .name
= "das-1701ao",
298 .range_ai
= &range_ai_das1801
,
301 .name
= "das-1702ao",
309 .range_ai
= &range_ai_das1802
,
312 .name
= "das-1801st",
320 .range_ai
= &range_ai_das1801
,
323 .name
= "das-1801st-da",
331 .range_ai
= &range_ai_das1801
,
334 .name
= "das-1802st",
342 .range_ai
= &range_ai_das1802
,
345 .name
= "das-1802st-da",
353 .range_ai
= &range_ai_das1802
,
356 .name
= "das-1802hr",
364 .range_ai
= &range_ai_das1802
,
367 .name
= "das-1802hr-da",
375 .range_ai
= &range_ai_das1802
,
378 .name
= "das-1801hc",
386 .range_ai
= &range_ai_das1801
,
389 .name
= "das-1802hc",
397 .range_ai
= &range_ai_das1802
,
400 .name
= "das-1801ao",
408 .range_ai
= &range_ai_das1801
,
411 .name
= "das-1802ao",
419 .range_ai
= &range_ai_das1802
,
423 struct das1800_private
{
424 unsigned int count
; /* number of data points left to be taken */
425 unsigned int divisor1
; /* value to load into board's counter 1 for timed conversions */
426 unsigned int divisor2
; /* value to load into board's counter 2 for timed conversions */
427 int irq_dma_bits
; /* bits for control register b */
428 /* dma bits for control register b, stored so that dma can be
429 * turned on and off */
431 unsigned int dma0
; /* dma channels used */
433 unsigned int dma_current
; /* dma channel currently in use */
434 uint16_t *ai_buf0
; /* pointers to dma buffers */
436 uint16_t *dma_current_buf
; /* pointer to dma buffer currently being used */
437 unsigned int dma_transfer_size
; /* size of transfer currently used, in bytes */
438 unsigned long iobase2
; /* secondary io address used for analog out on 'ao' boards */
439 unsigned short ao_update_bits
; /* remembers the last write to the
443 /* analog out range for 'ao' boards */
445 static const struct comedi_lrange range_ao_2 = {
453 static inline uint16_t munge_bipolar_sample(const struct comedi_device
*dev
,
456 const struct das1800_board
*thisboard
= dev
->board_ptr
;
458 sample
+= 1 << (thisboard
->resolution
- 1);
462 static void munge_data(struct comedi_device
*dev
, uint16_t *array
,
463 unsigned int num_elements
)
468 /* see if card is using a unipolar or bipolar range so we can munge data correctly */
469 unipolar
= inb(dev
->iobase
+ DAS1800_CONTROL_C
) & UB
;
471 /* convert to unsigned type if we are in a bipolar mode */
473 for (i
= 0; i
< num_elements
; i
++)
474 array
[i
] = munge_bipolar_sample(dev
, array
[i
]);
478 static void das1800_handle_fifo_half_full(struct comedi_device
*dev
,
479 struct comedi_subdevice
*s
)
481 struct das1800_private
*devpriv
= dev
->private;
482 int numPoints
= 0; /* number of points to read */
483 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
485 numPoints
= FIFO_SIZE
/ 2;
486 /* if we only need some of the points */
487 if (cmd
->stop_src
== TRIG_COUNT
&& devpriv
->count
< numPoints
)
488 numPoints
= devpriv
->count
;
489 insw(dev
->iobase
+ DAS1800_FIFO
, devpriv
->ai_buf0
, numPoints
);
490 munge_data(dev
, devpriv
->ai_buf0
, numPoints
);
491 cfc_write_array_to_buffer(s
, devpriv
->ai_buf0
,
492 numPoints
* sizeof(devpriv
->ai_buf0
[0]));
493 if (cmd
->stop_src
== TRIG_COUNT
)
494 devpriv
->count
-= numPoints
;
497 static void das1800_handle_fifo_not_empty(struct comedi_device
*dev
,
498 struct comedi_subdevice
*s
)
500 struct das1800_private
*devpriv
= dev
->private;
503 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
505 unipolar
= inb(dev
->iobase
+ DAS1800_CONTROL_C
) & UB
;
507 while (inb(dev
->iobase
+ DAS1800_STATUS
) & FNE
) {
508 if (cmd
->stop_src
== TRIG_COUNT
&& devpriv
->count
== 0)
510 dpnt
= inw(dev
->iobase
+ DAS1800_FIFO
);
511 /* convert to unsigned type if we are in a bipolar mode */
514 dpnt
= munge_bipolar_sample(dev
, dpnt
);
515 cfc_write_to_buffer(s
, dpnt
);
516 if (cmd
->stop_src
== TRIG_COUNT
)
521 /* Utility function used by das1800_flush_dma() and das1800_handle_dma().
522 * Assumes dma lock is held */
523 static void das1800_flush_dma_channel(struct comedi_device
*dev
,
524 struct comedi_subdevice
*s
,
525 unsigned int channel
, uint16_t *buffer
)
527 struct das1800_private
*devpriv
= dev
->private;
528 unsigned int num_bytes
, num_samples
;
529 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
531 disable_dma(channel
);
533 /* clear flip-flop to make sure 2-byte registers
534 * get set correctly */
535 clear_dma_ff(channel
);
537 /* figure out how many points to read */
538 num_bytes
= devpriv
->dma_transfer_size
- get_dma_residue(channel
);
539 num_samples
= num_bytes
/ sizeof(short);
541 /* if we only need some of the points */
542 if (cmd
->stop_src
== TRIG_COUNT
&& devpriv
->count
< num_samples
)
543 num_samples
= devpriv
->count
;
545 munge_data(dev
, buffer
, num_samples
);
546 cfc_write_array_to_buffer(s
, buffer
, num_bytes
);
547 if (cmd
->stop_src
== TRIG_COUNT
)
548 devpriv
->count
-= num_samples
;
551 /* flushes remaining data from board when external trigger has stopped acquisition
552 * and we are using dma transfers */
553 static void das1800_flush_dma(struct comedi_device
*dev
,
554 struct comedi_subdevice
*s
)
556 struct das1800_private
*devpriv
= dev
->private;
558 const int dual_dma
= devpriv
->irq_dma_bits
& DMA_DUAL
;
560 flags
= claim_dma_lock();
561 das1800_flush_dma_channel(dev
, s
, devpriv
->dma_current
,
562 devpriv
->dma_current_buf
);
565 /* switch to other channel and flush it */
566 if (devpriv
->dma_current
== devpriv
->dma0
) {
567 devpriv
->dma_current
= devpriv
->dma1
;
568 devpriv
->dma_current_buf
= devpriv
->ai_buf1
;
570 devpriv
->dma_current
= devpriv
->dma0
;
571 devpriv
->dma_current_buf
= devpriv
->ai_buf0
;
573 das1800_flush_dma_channel(dev
, s
, devpriv
->dma_current
,
574 devpriv
->dma_current_buf
);
577 release_dma_lock(flags
);
579 /* get any remaining samples in fifo */
580 das1800_handle_fifo_not_empty(dev
, s
);
583 static void das1800_handle_dma(struct comedi_device
*dev
,
584 struct comedi_subdevice
*s
, unsigned int status
)
586 struct das1800_private
*devpriv
= dev
->private;
588 const int dual_dma
= devpriv
->irq_dma_bits
& DMA_DUAL
;
590 flags
= claim_dma_lock();
591 das1800_flush_dma_channel(dev
, s
, devpriv
->dma_current
,
592 devpriv
->dma_current_buf
);
593 /* re-enable dma channel */
594 set_dma_addr(devpriv
->dma_current
,
595 virt_to_bus(devpriv
->dma_current_buf
));
596 set_dma_count(devpriv
->dma_current
, devpriv
->dma_transfer_size
);
597 enable_dma(devpriv
->dma_current
);
598 release_dma_lock(flags
);
600 if (status
& DMATC
) {
601 /* clear DMATC interrupt bit */
602 outb(CLEAR_INTR_MASK
& ~DMATC
, dev
->iobase
+ DAS1800_STATUS
);
603 /* switch dma channels for next time, if appropriate */
605 /* read data from the other channel next time */
606 if (devpriv
->dma_current
== devpriv
->dma0
) {
607 devpriv
->dma_current
= devpriv
->dma1
;
608 devpriv
->dma_current_buf
= devpriv
->ai_buf1
;
610 devpriv
->dma_current
= devpriv
->dma0
;
611 devpriv
->dma_current_buf
= devpriv
->ai_buf0
;
617 static int das1800_cancel(struct comedi_device
*dev
, struct comedi_subdevice
*s
)
619 struct das1800_private
*devpriv
= dev
->private;
621 outb(0x0, dev
->iobase
+ DAS1800_STATUS
); /* disable conversions */
622 outb(0x0, dev
->iobase
+ DAS1800_CONTROL_B
); /* disable interrupts and dma */
623 outb(0x0, dev
->iobase
+ DAS1800_CONTROL_A
); /* disable and clear fifo and stop triggering */
625 disable_dma(devpriv
->dma0
);
627 disable_dma(devpriv
->dma1
);
631 /* the guts of the interrupt handler, that is shared with das1800_ai_poll */
632 static void das1800_ai_handler(struct comedi_device
*dev
)
634 struct das1800_private
*devpriv
= dev
->private;
635 struct comedi_subdevice
*s
= dev
->read_subdev
;
636 struct comedi_async
*async
= s
->async
;
637 struct comedi_cmd
*cmd
= &async
->cmd
;
638 unsigned int status
= inb(dev
->iobase
+ DAS1800_STATUS
);
640 /* select adc for base address + 0 */
641 outb(ADC
, dev
->iobase
+ DAS1800_SELECT
);
642 /* dma buffer full */
643 if (devpriv
->irq_dma_bits
& DMA_ENABLED
) {
644 /* look for data from dma transfer even if dma terminal count hasn't happened yet */
645 das1800_handle_dma(dev
, s
, status
);
646 } else if (status
& FHF
) { /* if fifo half full */
647 das1800_handle_fifo_half_full(dev
, s
);
648 } else if (status
& FNE
) { /* if fifo not empty */
649 das1800_handle_fifo_not_empty(dev
, s
);
652 async
->events
|= COMEDI_CB_BLOCK
;
653 /* if the card's fifo has overflowed */
655 /* clear OVF interrupt bit */
656 outb(CLEAR_INTR_MASK
& ~OVF
, dev
->iobase
+ DAS1800_STATUS
);
657 dev_err(dev
->class_dev
, "FIFO overflow\n");
658 async
->events
|= COMEDI_CB_ERROR
| COMEDI_CB_EOA
;
659 cfc_handle_events(dev
, s
);
662 /* stop taking data if appropriate */
663 /* stop_src TRIG_EXT */
664 if (status
& CT0TC
) {
665 /* clear CT0TC interrupt bit */
666 outb(CLEAR_INTR_MASK
& ~CT0TC
, dev
->iobase
+ DAS1800_STATUS
);
667 /* make sure we get all remaining data from board before quitting */
668 if (devpriv
->irq_dma_bits
& DMA_ENABLED
)
669 das1800_flush_dma(dev
, s
);
671 das1800_handle_fifo_not_empty(dev
, s
);
672 async
->events
|= COMEDI_CB_EOA
;
673 } else if (cmd
->stop_src
== TRIG_COUNT
&& devpriv
->count
== 0) { /* stop_src TRIG_COUNT */
674 async
->events
|= COMEDI_CB_EOA
;
677 cfc_handle_events(dev
, s
);
680 static int das1800_ai_poll(struct comedi_device
*dev
,
681 struct comedi_subdevice
*s
)
685 /* prevent race with interrupt handler */
686 spin_lock_irqsave(&dev
->spinlock
, flags
);
687 das1800_ai_handler(dev
);
688 spin_unlock_irqrestore(&dev
->spinlock
, flags
);
690 return comedi_buf_n_bytes_ready(s
);
693 static irqreturn_t
das1800_interrupt(int irq
, void *d
)
695 struct comedi_device
*dev
= d
;
698 if (!dev
->attached
) {
699 dev_err(dev
->class_dev
, "premature interrupt\n");
703 /* Prevent race with das1800_ai_poll() on multi processor systems.
704 * Also protects indirect addressing in das1800_ai_handler */
705 spin_lock(&dev
->spinlock
);
706 status
= inb(dev
->iobase
+ DAS1800_STATUS
);
708 /* if interrupt was not caused by das-1800 */
709 if (!(status
& INT
)) {
710 spin_unlock(&dev
->spinlock
);
713 /* clear the interrupt status bit INT */
714 outb(CLEAR_INTR_MASK
& ~INT
, dev
->iobase
+ DAS1800_STATUS
);
715 /* handle interrupt */
716 das1800_ai_handler(dev
);
718 spin_unlock(&dev
->spinlock
);
722 /* converts requested conversion timing to timing compatible with
723 * hardware, used only when card is in 'burst mode'
725 static unsigned int burst_convert_arg(unsigned int convert_arg
, int flags
)
727 unsigned int micro_sec
;
729 /* in burst mode, the maximum conversion time is 64 microseconds */
730 if (convert_arg
> 64000)
733 /* the conversion time must be an integral number of microseconds */
734 switch (flags
& CMDF_ROUND_MASK
) {
735 case CMDF_ROUND_NEAREST
:
737 micro_sec
= (convert_arg
+ 500) / 1000;
739 case CMDF_ROUND_DOWN
:
740 micro_sec
= convert_arg
/ 1000;
743 micro_sec
= (convert_arg
- 1) / 1000 + 1;
747 /* return number of nanoseconds */
748 return micro_sec
* 1000;
751 static int das1800_ai_check_chanlist(struct comedi_device
*dev
,
752 struct comedi_subdevice
*s
,
753 struct comedi_cmd
*cmd
)
755 unsigned int unipolar0
= CR_RANGE(cmd
->chanlist
[0]) & UNIPOLAR
;
758 for (i
= 1; i
< cmd
->chanlist_len
; i
++) {
759 unsigned int unipolar
= CR_RANGE(cmd
->chanlist
[i
]) & UNIPOLAR
;
761 if (unipolar
!= unipolar0
) {
762 dev_dbg(dev
->class_dev
,
763 "unipolar and bipolar ranges cannot be mixed in the chanlist\n");
771 /* test analog input cmd */
772 static int das1800_ai_do_cmdtest(struct comedi_device
*dev
,
773 struct comedi_subdevice
*s
,
774 struct comedi_cmd
*cmd
)
776 const struct das1800_board
*thisboard
= dev
->board_ptr
;
777 struct das1800_private
*devpriv
= dev
->private;
781 /* Step 1 : check if triggers are trivially valid */
783 err
|= cfc_check_trigger_src(&cmd
->start_src
, TRIG_NOW
| TRIG_EXT
);
784 err
|= cfc_check_trigger_src(&cmd
->scan_begin_src
,
785 TRIG_FOLLOW
| TRIG_TIMER
| TRIG_EXT
);
786 err
|= cfc_check_trigger_src(&cmd
->convert_src
, TRIG_TIMER
| TRIG_EXT
);
787 err
|= cfc_check_trigger_src(&cmd
->scan_end_src
, TRIG_COUNT
);
788 err
|= cfc_check_trigger_src(&cmd
->stop_src
,
789 TRIG_COUNT
| TRIG_EXT
| TRIG_NONE
);
794 /* Step 2a : make sure trigger sources are unique */
796 err
|= cfc_check_trigger_is_unique(cmd
->start_src
);
797 err
|= cfc_check_trigger_is_unique(cmd
->scan_begin_src
);
798 err
|= cfc_check_trigger_is_unique(cmd
->convert_src
);
799 err
|= cfc_check_trigger_is_unique(cmd
->stop_src
);
801 /* Step 2b : and mutually compatible */
803 if (cmd
->scan_begin_src
!= TRIG_FOLLOW
&&
804 cmd
->convert_src
!= TRIG_TIMER
)
810 /* Step 3: check if arguments are trivially valid */
812 err
|= cfc_check_trigger_arg_is(&cmd
->start_arg
, 0);
814 if (cmd
->convert_src
== TRIG_TIMER
)
815 err
|= cfc_check_trigger_arg_min(&cmd
->convert_arg
,
816 thisboard
->ai_speed
);
818 err
|= cfc_check_trigger_arg_min(&cmd
->chanlist_len
, 1);
819 err
|= cfc_check_trigger_arg_is(&cmd
->scan_end_arg
, cmd
->chanlist_len
);
821 switch (cmd
->stop_src
) {
823 err
|= cfc_check_trigger_arg_min(&cmd
->stop_arg
, 1);
826 err
|= cfc_check_trigger_arg_is(&cmd
->stop_arg
, 0);
835 /* step 4: fix up any arguments */
837 if (cmd
->scan_begin_src
== TRIG_FOLLOW
&&
838 cmd
->convert_src
== TRIG_TIMER
) {
839 /* we are not in burst mode */
840 arg
= cmd
->convert_arg
;
841 i8253_cascade_ns_to_timer(I8254_OSC_BASE_5MHZ
,
844 &cmd
->convert_arg
, cmd
->flags
);
845 if (arg
!= cmd
->convert_arg
)
847 } else if (cmd
->convert_src
== TRIG_TIMER
) {
848 /* we are in burst mode */
849 arg
= cmd
->convert_arg
;
850 cmd
->convert_arg
= burst_convert_arg(cmd
->convert_arg
,
852 if (arg
!= cmd
->convert_arg
)
855 if (cmd
->scan_begin_src
== TRIG_TIMER
) {
856 arg
= cmd
->convert_arg
* cmd
->chanlist_len
;
857 if (arg
> cmd
->scan_begin_arg
) {
858 cmd
->scan_begin_arg
= arg
;
862 arg
= cmd
->scan_begin_arg
;
863 i8253_cascade_ns_to_timer(I8254_OSC_BASE_5MHZ
,
866 &cmd
->scan_begin_arg
,
868 if (arg
!= cmd
->scan_begin_arg
)
876 /* Step 5: check channel list if it exists */
877 if (cmd
->chanlist
&& cmd
->chanlist_len
> 0)
878 err
|= das1800_ai_check_chanlist(dev
, s
, cmd
);
886 /* returns appropriate bits for control register a, depending on command */
887 static int control_a_bits(const struct comedi_cmd
*cmd
)
891 control_a
= FFEN
; /* enable fifo */
892 if (cmd
->stop_src
== TRIG_EXT
)
894 switch (cmd
->start_src
) {
896 control_a
|= TGEN
| CGSL
;
908 /* returns appropriate bits for control register c, depending on command */
909 static int control_c_bits(const struct comedi_cmd
*cmd
)
914 /* set clock source to internal or external, select analog reference,
915 * select unipolar / bipolar
917 aref
= CR_AREF(cmd
->chanlist
[0]);
918 control_c
= UQEN
; /* enable upper qram addresses */
919 if (aref
!= AREF_DIFF
)
921 if (aref
== AREF_COMMON
)
923 /* if a unipolar range was selected */
924 if (CR_RANGE(cmd
->chanlist
[0]) & UNIPOLAR
)
926 switch (cmd
->scan_begin_src
) {
927 case TRIG_FOLLOW
: /* not in burst mode */
928 switch (cmd
->convert_src
) {
930 /* trig on cascaded counters */
934 /* trig on falling edge of external trigger */
942 /* burst mode with internal pacer clock */
943 control_c
|= BMDE
| IPCLK
;
946 /* burst mode with external trigger */
947 control_c
|= BMDE
| XPCLK
;
956 static void das1800_setup_counters(struct comedi_device
*dev
,
957 const struct comedi_cmd
*cmd
)
959 struct das1800_private
*devpriv
= dev
->private;
960 unsigned long timer_base
= dev
->iobase
+ DAS1800_COUNTER
;
962 /* setup cascaded counters for conversion/scan frequency */
963 if ((cmd
->scan_begin_src
== TRIG_FOLLOW
||
964 cmd
->scan_begin_src
== TRIG_TIMER
) &&
965 cmd
->convert_src
== TRIG_TIMER
) {
966 i8254_set_mode(timer_base
, 0, 1, I8254_MODE2
| I8254_BINARY
);
967 i8254_set_mode(timer_base
, 0, 2, I8254_MODE2
| I8254_BINARY
);
969 i8254_write(timer_base
, 0, 1, devpriv
->divisor1
);
970 i8254_write(timer_base
, 0, 2, devpriv
->divisor2
);
973 /* setup counter 0 for 'about triggering' */
974 if (cmd
->stop_src
== TRIG_EXT
) {
975 i8254_set_mode(timer_base
, 0, 0, I8254_MODE0
| I8254_BINARY
);
977 i8254_write(timer_base
, 0, 0, 1);
981 /* utility function that suggests a dma transfer size based on the conversion period 'ns' */
982 static unsigned int suggest_transfer_size(const struct comedi_cmd
*cmd
)
984 unsigned int size
= DMA_BUF_SIZE
;
985 static const int sample_size
= 2; /* size in bytes of one sample from board */
986 unsigned int fill_time
= 300000000; /* target time in nanoseconds for filling dma buffer */
987 unsigned int max_size
; /* maximum size we will allow for a transfer */
989 /* make dma buffer fill in 0.3 seconds for timed modes */
990 switch (cmd
->scan_begin_src
) {
991 case TRIG_FOLLOW
: /* not in burst mode */
992 if (cmd
->convert_src
== TRIG_TIMER
)
993 size
= (fill_time
/ cmd
->convert_arg
) * sample_size
;
996 size
= (fill_time
/ (cmd
->scan_begin_arg
* cmd
->chanlist_len
)) *
1000 size
= DMA_BUF_SIZE
;
1004 /* set a minimum and maximum size allowed */
1005 max_size
= DMA_BUF_SIZE
;
1006 /* if we are taking limited number of conversions, limit transfer size to that */
1007 if (cmd
->stop_src
== TRIG_COUNT
&&
1008 cmd
->stop_arg
* cmd
->chanlist_len
* sample_size
< max_size
)
1009 max_size
= cmd
->stop_arg
* cmd
->chanlist_len
* sample_size
;
1011 if (size
> max_size
)
1013 if (size
< sample_size
)
1020 static void setup_dma(struct comedi_device
*dev
, const struct comedi_cmd
*cmd
)
1022 struct das1800_private
*devpriv
= dev
->private;
1023 unsigned long lock_flags
;
1024 const int dual_dma
= devpriv
->irq_dma_bits
& DMA_DUAL
;
1026 if ((devpriv
->irq_dma_bits
& DMA_ENABLED
) == 0)
1029 /* determine a reasonable dma transfer size */
1030 devpriv
->dma_transfer_size
= suggest_transfer_size(cmd
);
1031 lock_flags
= claim_dma_lock();
1032 disable_dma(devpriv
->dma0
);
1033 /* clear flip-flop to make sure 2-byte registers for
1034 * count and address get set correctly */
1035 clear_dma_ff(devpriv
->dma0
);
1036 set_dma_addr(devpriv
->dma0
, virt_to_bus(devpriv
->ai_buf0
));
1037 /* set appropriate size of transfer */
1038 set_dma_count(devpriv
->dma0
, devpriv
->dma_transfer_size
);
1039 devpriv
->dma_current
= devpriv
->dma0
;
1040 devpriv
->dma_current_buf
= devpriv
->ai_buf0
;
1041 enable_dma(devpriv
->dma0
);
1042 /* set up dual dma if appropriate */
1044 disable_dma(devpriv
->dma1
);
1045 /* clear flip-flop to make sure 2-byte registers for
1046 * count and address get set correctly */
1047 clear_dma_ff(devpriv
->dma1
);
1048 set_dma_addr(devpriv
->dma1
, virt_to_bus(devpriv
->ai_buf1
));
1049 /* set appropriate size of transfer */
1050 set_dma_count(devpriv
->dma1
, devpriv
->dma_transfer_size
);
1051 enable_dma(devpriv
->dma1
);
1053 release_dma_lock(lock_flags
);
1056 /* programs channel/gain list into card */
1057 static void program_chanlist(struct comedi_device
*dev
,
1058 const struct comedi_cmd
*cmd
)
1060 int i
, n
, chan_range
;
1061 unsigned long irq_flags
;
1062 const int range_mask
= 0x3; /* masks unipolar/bipolar bit off range */
1063 const int range_bitshift
= 8;
1065 n
= cmd
->chanlist_len
;
1066 /* spinlock protects indirect addressing */
1067 spin_lock_irqsave(&dev
->spinlock
, irq_flags
);
1068 outb(QRAM
, dev
->iobase
+ DAS1800_SELECT
); /* select QRAM for baseAddress + 0x0 */
1069 outb(n
- 1, dev
->iobase
+ DAS1800_QRAM_ADDRESS
); /*set QRAM address start */
1070 /* make channel / gain list */
1071 for (i
= 0; i
< n
; i
++) {
1073 CR_CHAN(cmd
->chanlist
[i
]) |
1074 ((CR_RANGE(cmd
->chanlist
[i
]) & range_mask
) <<
1076 outw(chan_range
, dev
->iobase
+ DAS1800_QRAM
);
1078 outb(n
- 1, dev
->iobase
+ DAS1800_QRAM_ADDRESS
); /*finish write to QRAM */
1079 spin_unlock_irqrestore(&dev
->spinlock
, irq_flags
);
1082 /* analog input do_cmd */
1083 static int das1800_ai_do_cmd(struct comedi_device
*dev
,
1084 struct comedi_subdevice
*s
)
1086 struct das1800_private
*devpriv
= dev
->private;
1087 int control_a
, control_c
;
1088 struct comedi_async
*async
= s
->async
;
1089 const struct comedi_cmd
*cmd
= &async
->cmd
;
1091 /* disable dma on CMDF_WAKE_EOS, or CMDF_PRIORITY
1092 * (because dma in handler is unsafe at hard real-time priority) */
1093 if (cmd
->flags
& (CMDF_WAKE_EOS
| CMDF_PRIORITY
))
1094 devpriv
->irq_dma_bits
&= ~DMA_ENABLED
;
1096 devpriv
->irq_dma_bits
|= devpriv
->dma_bits
;
1097 /* interrupt on end of conversion for CMDF_WAKE_EOS */
1098 if (cmd
->flags
& CMDF_WAKE_EOS
) {
1099 /* interrupt fifo not empty */
1100 devpriv
->irq_dma_bits
&= ~FIMD
;
1102 /* interrupt fifo half full */
1103 devpriv
->irq_dma_bits
|= FIMD
;
1105 /* determine how many conversions we need */
1106 if (cmd
->stop_src
== TRIG_COUNT
)
1107 devpriv
->count
= cmd
->stop_arg
* cmd
->chanlist_len
;
1109 das1800_cancel(dev
, s
);
1111 /* determine proper bits for control registers */
1112 control_a
= control_a_bits(cmd
);
1113 control_c
= control_c_bits(cmd
);
1115 /* setup card and start */
1116 program_chanlist(dev
, cmd
);
1117 das1800_setup_counters(dev
, cmd
);
1118 setup_dma(dev
, cmd
);
1119 outb(control_c
, dev
->iobase
+ DAS1800_CONTROL_C
);
1120 /* set conversion rate and length for burst mode */
1121 if (control_c
& BMDE
) {
1122 /* program conversion period with number of microseconds minus 1 */
1123 outb(cmd
->convert_arg
/ 1000 - 1,
1124 dev
->iobase
+ DAS1800_BURST_RATE
);
1125 outb(cmd
->chanlist_len
- 1, dev
->iobase
+ DAS1800_BURST_LENGTH
);
1127 outb(devpriv
->irq_dma_bits
, dev
->iobase
+ DAS1800_CONTROL_B
); /* enable irq/dma */
1128 outb(control_a
, dev
->iobase
+ DAS1800_CONTROL_A
); /* enable fifo and triggering */
1129 outb(CVEN
, dev
->iobase
+ DAS1800_STATUS
); /* enable conversions */
1134 /* read analog input */
1135 static int das1800_ai_rinsn(struct comedi_device
*dev
,
1136 struct comedi_subdevice
*s
,
1137 struct comedi_insn
*insn
, unsigned int *data
)
1139 const struct das1800_board
*thisboard
= dev
->board_ptr
;
1141 int chan
, range
, aref
, chan_range
;
1143 unsigned short dpnt
;
1145 unsigned long irq_flags
;
1147 /* set up analog reference and unipolar / bipolar mode */
1148 aref
= CR_AREF(insn
->chanspec
);
1150 if (aref
!= AREF_DIFF
)
1152 if (aref
== AREF_COMMON
)
1154 /* if a unipolar range was selected */
1155 if (CR_RANGE(insn
->chanspec
) & UNIPOLAR
)
1158 outb(conv_flags
, dev
->iobase
+ DAS1800_CONTROL_C
); /* software conversion enabled */
1159 outb(CVEN
, dev
->iobase
+ DAS1800_STATUS
); /* enable conversions */
1160 outb(0x0, dev
->iobase
+ DAS1800_CONTROL_A
); /* reset fifo */
1161 outb(FFEN
, dev
->iobase
+ DAS1800_CONTROL_A
);
1163 chan
= CR_CHAN(insn
->chanspec
);
1164 /* mask of unipolar/bipolar bit from range */
1165 range
= CR_RANGE(insn
->chanspec
) & 0x3;
1166 chan_range
= chan
| (range
<< 8);
1167 spin_lock_irqsave(&dev
->spinlock
, irq_flags
);
1168 outb(QRAM
, dev
->iobase
+ DAS1800_SELECT
); /* select QRAM for baseAddress + 0x0 */
1169 outb(0x0, dev
->iobase
+ DAS1800_QRAM_ADDRESS
); /* set QRAM address start */
1170 outw(chan_range
, dev
->iobase
+ DAS1800_QRAM
);
1171 outb(0x0, dev
->iobase
+ DAS1800_QRAM_ADDRESS
); /*finish write to QRAM */
1172 outb(ADC
, dev
->iobase
+ DAS1800_SELECT
); /* select ADC for baseAddress + 0x0 */
1174 for (n
= 0; n
< insn
->n
; n
++) {
1175 /* trigger conversion */
1176 outb(0, dev
->iobase
+ DAS1800_FIFO
);
1177 for (i
= 0; i
< timeout
; i
++) {
1178 if (inb(dev
->iobase
+ DAS1800_STATUS
) & FNE
)
1182 dev_err(dev
->class_dev
, "timeout\n");
1186 dpnt
= inw(dev
->iobase
+ DAS1800_FIFO
);
1187 /* shift data to offset binary for bipolar ranges */
1188 if ((conv_flags
& UB
) == 0)
1189 dpnt
+= 1 << (thisboard
->resolution
- 1);
1193 spin_unlock_irqrestore(&dev
->spinlock
, irq_flags
);
1198 /* writes to an analog output channel */
1199 static int das1800_ao_winsn(struct comedi_device
*dev
,
1200 struct comedi_subdevice
*s
,
1201 struct comedi_insn
*insn
, unsigned int *data
)
1203 const struct das1800_board
*thisboard
= dev
->board_ptr
;
1204 struct das1800_private
*devpriv
= dev
->private;
1205 int chan
= CR_CHAN(insn
->chanspec
);
1206 /* int range = CR_RANGE(insn->chanspec); */
1207 int update_chan
= thisboard
->ao_n_chan
- 1;
1208 unsigned short output
;
1209 unsigned long irq_flags
;
1211 /* card expects two's complement data */
1212 output
= data
[0] - (1 << (thisboard
->resolution
- 1));
1213 /* if the write is to the 'update' channel, we need to remember its value */
1214 if (chan
== update_chan
)
1215 devpriv
->ao_update_bits
= output
;
1216 /* write to channel */
1217 spin_lock_irqsave(&dev
->spinlock
, irq_flags
);
1218 outb(DAC(chan
), dev
->iobase
+ DAS1800_SELECT
); /* select dac channel for baseAddress + 0x0 */
1219 outw(output
, dev
->iobase
+ DAS1800_DAC
);
1220 /* now we need to write to 'update' channel to update all dac channels */
1221 if (chan
!= update_chan
) {
1222 outb(DAC(update_chan
), dev
->iobase
+ DAS1800_SELECT
); /* select 'update' channel for baseAddress + 0x0 */
1223 outw(devpriv
->ao_update_bits
, dev
->iobase
+ DAS1800_DAC
);
1225 spin_unlock_irqrestore(&dev
->spinlock
, irq_flags
);
1230 /* reads from digital input channels */
1231 static int das1800_di_rbits(struct comedi_device
*dev
,
1232 struct comedi_subdevice
*s
,
1233 struct comedi_insn
*insn
, unsigned int *data
)
1236 data
[1] = inb(dev
->iobase
+ DAS1800_DIGITAL
) & 0xf;
1242 static int das1800_do_wbits(struct comedi_device
*dev
,
1243 struct comedi_subdevice
*s
,
1244 struct comedi_insn
*insn
,
1247 if (comedi_dio_update_state(s
, data
))
1248 outb(s
->state
, dev
->iobase
+ DAS1800_DIGITAL
);
1255 static int das1800_init_dma(struct comedi_device
*dev
, unsigned int dma0
,
1258 struct das1800_private
*devpriv
= dev
->private;
1259 unsigned long flags
;
1261 /* need an irq to do dma */
1262 if (dev
->irq
&& dma0
) {
1263 /* encode dma0 and dma1 into 2 digit hexadecimal for switch */
1264 switch ((dma0
& 0x7) | (dma1
<< 4)) {
1265 case 0x5: /* dma0 == 5 */
1266 devpriv
->dma_bits
|= DMA_CH5
;
1268 case 0x6: /* dma0 == 6 */
1269 devpriv
->dma_bits
|= DMA_CH6
;
1271 case 0x7: /* dma0 == 7 */
1272 devpriv
->dma_bits
|= DMA_CH7
;
1274 case 0x65: /* dma0 == 5, dma1 == 6 */
1275 devpriv
->dma_bits
|= DMA_CH5_CH6
;
1277 case 0x76: /* dma0 == 6, dma1 == 7 */
1278 devpriv
->dma_bits
|= DMA_CH6_CH7
;
1280 case 0x57: /* dma0 == 7, dma1 == 5 */
1281 devpriv
->dma_bits
|= DMA_CH7_CH5
;
1284 dev_err(dev
->class_dev
,
1285 "only supports dma channels 5 through 7\n");
1286 dev_err(dev
->class_dev
,
1287 "Dual dma only allows the following combinations:\n");
1288 dev_err(dev
->class_dev
,
1289 "dma 5,6 / 6,7 / or 7,5\n");
1292 if (request_dma(dma0
, dev
->driver
->driver_name
)) {
1293 dev_err(dev
->class_dev
,
1294 "failed to allocate dma channel %i\n", dma0
);
1297 devpriv
->dma0
= dma0
;
1298 devpriv
->dma_current
= dma0
;
1300 if (request_dma(dma1
, dev
->driver
->driver_name
)) {
1301 dev_err(dev
->class_dev
,
1302 "failed to allocate dma channel %i\n",
1306 devpriv
->dma1
= dma1
;
1308 devpriv
->ai_buf0
= kmalloc(DMA_BUF_SIZE
, GFP_KERNEL
| GFP_DMA
);
1309 if (devpriv
->ai_buf0
== NULL
)
1311 devpriv
->dma_current_buf
= devpriv
->ai_buf0
;
1314 kmalloc(DMA_BUF_SIZE
, GFP_KERNEL
| GFP_DMA
);
1315 if (devpriv
->ai_buf1
== NULL
)
1318 flags
= claim_dma_lock();
1319 disable_dma(devpriv
->dma0
);
1320 set_dma_mode(devpriv
->dma0
, DMA_MODE_READ
);
1322 disable_dma(devpriv
->dma1
);
1323 set_dma_mode(devpriv
->dma1
, DMA_MODE_READ
);
1325 release_dma_lock(flags
);
1330 static int das1800_probe(struct comedi_device
*dev
)
1332 const struct das1800_board
*board
= dev
->board_ptr
;
1336 /* calc the offset to the boardinfo that was found by the core */
1337 index
= board
- das1800_boards
;
1339 /* verify that the board id matches the boardinfo */
1340 id
= (inb(dev
->iobase
+ DAS1800_DIGITAL
) >> 4) & 0xf;
1343 if (index
== das1801st_da
|| index
== das1802st_da
||
1344 index
== das1701st_da
|| index
== das1702st_da
)
1349 if (index
== das1802hr_da
|| index
== das1702hr_da
)
1354 if (index
== das1801ao
|| index
== das1802ao
||
1355 index
== das1701ao
|| index
== das1702ao
)
1360 if (index
== das1802hr
|| index
== das1702hr
)
1365 if (index
== das1801st
|| index
== das1802st
||
1366 index
== das1701st
|| index
== das1702st
)
1371 if (index
== das1801hc
|| index
== das1802hc
)
1376 dev_err(dev
->class_dev
,
1377 "Board model: probe returned 0x%x (unknown, please report)\n",
1381 dev_err(dev
->class_dev
,
1382 "Board model (probed, not recommended): %s series\n",
1383 das1800_boards
[index
].name
);
1388 static int das1800_attach(struct comedi_device
*dev
,
1389 struct comedi_devconfig
*it
)
1391 const struct das1800_board
*thisboard
;
1392 struct das1800_private
*devpriv
;
1393 struct comedi_subdevice
*s
;
1394 unsigned int irq
= it
->options
[1];
1395 unsigned int dma0
= it
->options
[2];
1396 unsigned int dma1
= it
->options
[3];
1400 devpriv
= comedi_alloc_devpriv(dev
, sizeof(*devpriv
));
1404 ret
= comedi_request_region(dev
, it
->options
[0], DAS1800_SIZE
);
1408 board
= das1800_probe(dev
);
1410 dev_err(dev
->class_dev
, "unable to determine board type\n");
1414 dev
->board_ptr
= das1800_boards
+ board
;
1415 thisboard
= dev
->board_ptr
;
1416 dev
->board_name
= thisboard
->name
;
1418 /* if it is an 'ao' board with fancy analog out then we need extra io ports */
1419 if (thisboard
->ao_ability
== 2) {
1420 unsigned long iobase2
= dev
->iobase
+ IOBASE2
;
1422 ret
= __comedi_request_region(dev
, iobase2
, DAS1800_SIZE
);
1425 devpriv
->iobase2
= iobase2
;
1428 if (irq
== 3 || irq
== 5 || irq
== 7 || irq
== 10 || irq
== 11 ||
1430 ret
= request_irq(irq
, das1800_interrupt
, 0,
1431 dev
->board_name
, dev
);
1437 devpriv
->irq_dma_bits
|= 0x8;
1440 devpriv
->irq_dma_bits
|= 0x10;
1443 devpriv
->irq_dma_bits
|= 0x18;
1446 devpriv
->irq_dma_bits
|= 0x28;
1449 devpriv
->irq_dma_bits
|= 0x30;
1452 devpriv
->irq_dma_bits
|= 0x38;
1458 ret
= das1800_init_dma(dev
, dma0
, dma1
);
1462 if (devpriv
->ai_buf0
== NULL
) {
1464 kmalloc(FIFO_SIZE
* sizeof(uint16_t), GFP_KERNEL
);
1465 if (devpriv
->ai_buf0
== NULL
)
1469 ret
= comedi_alloc_subdevices(dev
, 4);
1473 /* analog input subdevice */
1474 s
= &dev
->subdevices
[0];
1475 s
->type
= COMEDI_SUBD_AI
;
1476 s
->subdev_flags
= SDF_READABLE
| SDF_DIFF
| SDF_GROUND
;
1477 if (thisboard
->common
)
1478 s
->subdev_flags
|= SDF_COMMON
;
1479 s
->n_chan
= thisboard
->qram_len
;
1480 s
->maxdata
= (1 << thisboard
->resolution
) - 1;
1481 s
->range_table
= thisboard
->range_ai
;
1482 s
->insn_read
= das1800_ai_rinsn
;
1484 dev
->read_subdev
= s
;
1485 s
->subdev_flags
|= SDF_CMD_READ
;
1486 s
->len_chanlist
= s
->n_chan
;
1487 s
->do_cmd
= das1800_ai_do_cmd
;
1488 s
->do_cmdtest
= das1800_ai_do_cmdtest
;
1489 s
->poll
= das1800_ai_poll
;
1490 s
->cancel
= das1800_cancel
;
1494 s
= &dev
->subdevices
[1];
1495 if (thisboard
->ao_ability
== 1) {
1496 s
->type
= COMEDI_SUBD_AO
;
1497 s
->subdev_flags
= SDF_WRITABLE
;
1498 s
->n_chan
= thisboard
->ao_n_chan
;
1499 s
->maxdata
= (1 << thisboard
->resolution
) - 1;
1500 s
->range_table
= &range_bipolar10
;
1501 s
->insn_write
= das1800_ao_winsn
;
1503 s
->type
= COMEDI_SUBD_UNUSED
;
1507 s
= &dev
->subdevices
[2];
1508 s
->type
= COMEDI_SUBD_DI
;
1509 s
->subdev_flags
= SDF_READABLE
;
1512 s
->range_table
= &range_digital
;
1513 s
->insn_bits
= das1800_di_rbits
;
1516 s
= &dev
->subdevices
[3];
1517 s
->type
= COMEDI_SUBD_DO
;
1518 s
->subdev_flags
= SDF_WRITABLE
| SDF_READABLE
;
1519 s
->n_chan
= thisboard
->do_n_chan
;
1521 s
->range_table
= &range_digital
;
1522 s
->insn_bits
= das1800_do_wbits
;
1524 das1800_cancel(dev
, dev
->read_subdev
);
1526 /* initialize digital out channels */
1527 outb(0, dev
->iobase
+ DAS1800_DIGITAL
);
1529 /* initialize analog out channels */
1530 if (thisboard
->ao_ability
== 1) {
1531 /* select 'update' dac channel for baseAddress + 0x0 */
1532 outb(DAC(thisboard
->ao_n_chan
- 1),
1533 dev
->iobase
+ DAS1800_SELECT
);
1534 outw(devpriv
->ao_update_bits
, dev
->iobase
+ DAS1800_DAC
);
1540 static void das1800_detach(struct comedi_device
*dev
)
1542 struct das1800_private
*devpriv
= dev
->private;
1546 free_dma(devpriv
->dma0
);
1548 free_dma(devpriv
->dma1
);
1549 kfree(devpriv
->ai_buf0
);
1550 kfree(devpriv
->ai_buf1
);
1551 if (devpriv
->iobase2
)
1552 release_region(devpriv
->iobase2
, DAS1800_SIZE
);
1554 comedi_legacy_detach(dev
);
1557 static struct comedi_driver das1800_driver
= {
1558 .driver_name
= "das1800",
1559 .module
= THIS_MODULE
,
1560 .attach
= das1800_attach
,
1561 .detach
= das1800_detach
,
1562 .num_names
= ARRAY_SIZE(das1800_boards
),
1563 .board_name
= &das1800_boards
[0].name
,
1564 .offset
= sizeof(struct das1800_board
),
1566 module_comedi_driver(das1800_driver
);
1568 MODULE_AUTHOR("Comedi http://www.comedi.org");
1569 MODULE_DESCRIPTION("Comedi low-level driver");
1570 MODULE_LICENSE("GPL");