3 * Comedi driver for 6/12-Channel D/A Output and DIO cards
5 * COMEDI - Linux Control and Measurement Device Interface
6 * Copyright (C) 1998 David A. Schleef <ds@schleef.org>
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.
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.
21 * Description: Advantech PCL-726 & compatibles
22 * Author: David A. Schleef <ds@schleef.org>
24 * Devices: [Advantech] PCL-726 (pcl726), PCL-727 (pcl727), PCL-728 (pcl728),
25 * [ADLink] ACL-6126 (acl6126), ACL-6128 (acl6128)
27 * Configuration Options:
29 * [1] - IRQ (ACL-6126 only)
30 * [2] - D/A output range for channel 0
31 * [3] - D/A output range for channel 1
33 * Boards with > 2 analog output channels:
34 * [4] - D/A output range for channel 2
35 * [5] - D/A output range for channel 3
36 * [6] - D/A output range for channel 4
37 * [7] - D/A output range for channel 5
39 * Boards with > 6 analog output channels:
40 * [8] - D/A output range for channel 6
41 * [9] - D/A output range for channel 7
42 * [10] - D/A output range for channel 8
43 * [11] - D/A output range for channel 9
44 * [12] - D/A output range for channel 10
45 * [13] - D/A output range for channel 11
47 * For PCL-726 the D/A output ranges are:
48 * 0: 0-5V, 1: 0-10V, 2: +/-5V, 3: +/-10V, 4: 4-20mA, 5: unknown
51 * 0: 0-5V, 1: 0-10V, 2: +/-5V, 3: 4-20mA
53 * For PCL-728 and ACL-6128:
54 * 0: 0-5V, 1: 0-10V, 2: +/-5V, 3: +/-10V, 4: 4-20mA, 5: 0-20mA
57 * 0: 0-5V, 1: 0-10V, 2: +/-5V, 3: +/-10V, 4: 4-20mA
60 #include <linux/module.h>
61 #include <linux/interrupt.h>
63 #include "../comedidev.h"
65 #include "comedi_fc.h"
67 #define PCL726_AO_MSB_REG(x) (0x00 + ((x) * 2))
68 #define PCL726_AO_LSB_REG(x) (0x01 + ((x) * 2))
69 #define PCL726_DO_MSB_REG 0x0c
70 #define PCL726_DO_LSB_REG 0x0d
71 #define PCL726_DI_MSB_REG 0x0e
72 #define PCL726_DI_LSB_REG 0x0f
74 #define PCL727_DI_MSB_REG 0x00
75 #define PCL727_DI_LSB_REG 0x01
76 #define PCL727_DO_MSB_REG 0x18
77 #define PCL727_DO_LSB_REG 0x19
79 static const struct comedi_lrange
*const rangelist_726
[] = {
88 static const struct comedi_lrange
*const rangelist_727
[] = {
95 static const struct comedi_lrange
*const rangelist_728
[] = {
104 struct pcl726_board
{
106 unsigned long io_len
;
107 unsigned int irq_mask
;
108 const struct comedi_lrange
*const *ao_ranges
;
111 unsigned int have_dio
:1;
112 unsigned int is_pcl727
:1;
115 static const struct pcl726_board pcl726_boards
[] = {
119 .ao_ranges
= &rangelist_726
[0],
120 .ao_num_ranges
= ARRAY_SIZE(rangelist_726
),
126 .ao_ranges
= &rangelist_727
[0],
127 .ao_num_ranges
= ARRAY_SIZE(rangelist_727
),
134 .ao_num_ranges
= ARRAY_SIZE(rangelist_728
),
135 .ao_ranges
= &rangelist_728
[0],
141 .ao_num_ranges
= ARRAY_SIZE(rangelist_726
),
142 .ao_ranges
= &rangelist_726
[0],
148 .ao_num_ranges
= ARRAY_SIZE(rangelist_728
),
149 .ao_ranges
= &rangelist_728
[0],
154 struct pcl726_private
{
155 const struct comedi_lrange
*rangelist
[12];
156 unsigned int cmd_running
:1;
159 static int pcl726_intr_insn_bits(struct comedi_device
*dev
,
160 struct comedi_subdevice
*s
,
161 struct comedi_insn
*insn
,
168 static int pcl726_intr_cmdtest(struct comedi_device
*dev
,
169 struct comedi_subdevice
*s
,
170 struct comedi_cmd
*cmd
)
174 /* Step 1 : check if triggers are trivially valid */
176 err
|= cfc_check_trigger_src(&cmd
->start_src
, TRIG_NOW
);
177 err
|= cfc_check_trigger_src(&cmd
->scan_begin_src
, TRIG_EXT
);
178 err
|= cfc_check_trigger_src(&cmd
->convert_src
, TRIG_FOLLOW
);
179 err
|= cfc_check_trigger_src(&cmd
->scan_end_src
, TRIG_COUNT
);
180 err
|= cfc_check_trigger_src(&cmd
->stop_src
, TRIG_NONE
);
185 /* Step 2a : make sure trigger sources are unique */
186 /* Step 2b : and mutually compatible */
188 /* Step 3: check if arguments are trivially valid */
190 err
|= cfc_check_trigger_arg_is(&cmd
->start_arg
, 0);
191 err
|= cfc_check_trigger_arg_is(&cmd
->scan_begin_arg
, 0);
192 err
|= cfc_check_trigger_arg_is(&cmd
->convert_arg
, 0);
193 err
|= cfc_check_trigger_arg_is(&cmd
->scan_end_arg
, cmd
->chanlist_len
);
194 err
|= cfc_check_trigger_arg_is(&cmd
->stop_arg
, 0);
199 /* Step 4: fix up any arguments */
201 /* Step 5: check channel list if it exists */
206 static int pcl726_intr_cmd(struct comedi_device
*dev
,
207 struct comedi_subdevice
*s
)
209 struct pcl726_private
*devpriv
= dev
->private;
211 devpriv
->cmd_running
= 1;
216 static int pcl726_intr_cancel(struct comedi_device
*dev
,
217 struct comedi_subdevice
*s
)
219 struct pcl726_private
*devpriv
= dev
->private;
221 devpriv
->cmd_running
= 0;
226 static irqreturn_t
pcl726_interrupt(int irq
, void *d
)
228 struct comedi_device
*dev
= d
;
229 struct comedi_subdevice
*s
= dev
->read_subdev
;
230 struct pcl726_private
*devpriv
= dev
->private;
232 if (devpriv
->cmd_running
) {
233 pcl726_intr_cancel(dev
, s
);
235 comedi_buf_write_samples(s
, &s
->state
, 1);
236 comedi_handle_events(dev
, s
);
242 static int pcl726_ao_insn_write(struct comedi_device
*dev
,
243 struct comedi_subdevice
*s
,
244 struct comedi_insn
*insn
,
247 unsigned int chan
= CR_CHAN(insn
->chanspec
);
248 unsigned int range
= CR_RANGE(insn
->chanspec
);
251 for (i
= 0; i
< insn
->n
; i
++) {
252 unsigned int val
= data
[i
];
254 s
->readback
[chan
] = val
;
256 /* bipolar data to the DAC is two's complement */
257 if (comedi_chan_range_is_bipolar(s
, chan
, range
))
258 val
= comedi_offset_munge(s
, val
);
260 /* order is important, MSB then LSB */
261 outb((val
>> 8) & 0xff, dev
->iobase
+ PCL726_AO_MSB_REG(chan
));
262 outb(val
& 0xff, dev
->iobase
+ PCL726_AO_LSB_REG(chan
));
268 static int pcl726_di_insn_bits(struct comedi_device
*dev
,
269 struct comedi_subdevice
*s
,
270 struct comedi_insn
*insn
,
273 const struct pcl726_board
*board
= dev
->board_ptr
;
276 if (board
->is_pcl727
) {
277 val
= inb(dev
->iobase
+ PCL727_DI_LSB_REG
);
278 val
|= (inb(dev
->iobase
+ PCL727_DI_MSB_REG
) << 8);
280 val
= inb(dev
->iobase
+ PCL726_DI_LSB_REG
);
281 val
|= (inb(dev
->iobase
+ PCL726_DI_MSB_REG
) << 8);
289 static int pcl726_do_insn_bits(struct comedi_device
*dev
,
290 struct comedi_subdevice
*s
,
291 struct comedi_insn
*insn
,
294 const struct pcl726_board
*board
= dev
->board_ptr
;
295 unsigned long io
= dev
->iobase
;
298 mask
= comedi_dio_update_state(s
, data
);
300 if (board
->is_pcl727
) {
302 outb(s
->state
& 0xff, io
+ PCL727_DO_LSB_REG
);
304 outb((s
->state
>> 8), io
+ PCL727_DO_MSB_REG
);
307 outb(s
->state
& 0xff, io
+ PCL726_DO_LSB_REG
);
309 outb((s
->state
>> 8), io
+ PCL726_DO_MSB_REG
);
318 static int pcl726_attach(struct comedi_device
*dev
,
319 struct comedi_devconfig
*it
)
321 const struct pcl726_board
*board
= dev
->board_ptr
;
322 struct pcl726_private
*devpriv
;
323 struct comedi_subdevice
*s
;
328 ret
= comedi_request_region(dev
, it
->options
[0], board
->io_len
);
332 devpriv
= comedi_alloc_devpriv(dev
, sizeof(*devpriv
));
337 * Hook up the external trigger source interrupt only if the
338 * user config option is valid and the board supports interrupts.
340 if (it
->options
[1] && (board
->irq_mask
& (1 << it
->options
[1]))) {
341 ret
= request_irq(it
->options
[1], pcl726_interrupt
, 0,
342 dev
->board_name
, dev
);
344 /* External trigger source is from Pin-17 of CN3 */
345 dev
->irq
= it
->options
[1];
349 /* setup the per-channel analog output range_table_list */
350 for (i
= 0; i
< 12; i
++) {
351 unsigned int opt
= it
->options
[2 + i
];
353 if (opt
< board
->ao_num_ranges
&& i
< board
->ao_nchan
)
354 devpriv
->rangelist
[i
] = board
->ao_ranges
[opt
];
356 devpriv
->rangelist
[i
] = &range_unknown
;
359 subdev
= board
->have_dio
? 3 : 1;
362 ret
= comedi_alloc_subdevices(dev
, subdev
);
368 /* Analog Output subdevice */
369 s
= &dev
->subdevices
[subdev
++];
370 s
->type
= COMEDI_SUBD_AO
;
371 s
->subdev_flags
= SDF_WRITABLE
| SDF_GROUND
;
372 s
->n_chan
= board
->ao_nchan
;
374 s
->range_table_list
= devpriv
->rangelist
;
375 s
->insn_write
= pcl726_ao_insn_write
;
377 ret
= comedi_alloc_subdev_readback(s
);
381 if (board
->have_dio
) {
382 /* Digital Input subdevice */
383 s
= &dev
->subdevices
[subdev
++];
384 s
->type
= COMEDI_SUBD_DI
;
385 s
->subdev_flags
= SDF_READABLE
;
388 s
->insn_bits
= pcl726_di_insn_bits
;
389 s
->range_table
= &range_digital
;
391 /* Digital Output subdevice */
392 s
= &dev
->subdevices
[subdev
++];
393 s
->type
= COMEDI_SUBD_DO
;
394 s
->subdev_flags
= SDF_WRITABLE
;
397 s
->insn_bits
= pcl726_do_insn_bits
;
398 s
->range_table
= &range_digital
;
402 /* Digial Input subdevice - Interrupt support */
403 s
= &dev
->subdevices
[subdev
++];
404 dev
->read_subdev
= s
;
405 s
->type
= COMEDI_SUBD_DI
;
406 s
->subdev_flags
= SDF_READABLE
| SDF_CMD_READ
;
409 s
->range_table
= &range_digital
;
410 s
->insn_bits
= pcl726_intr_insn_bits
;
412 s
->do_cmdtest
= pcl726_intr_cmdtest
;
413 s
->do_cmd
= pcl726_intr_cmd
;
414 s
->cancel
= pcl726_intr_cancel
;
420 static struct comedi_driver pcl726_driver
= {
421 .driver_name
= "pcl726",
422 .module
= THIS_MODULE
,
423 .attach
= pcl726_attach
,
424 .detach
= comedi_legacy_detach
,
425 .board_name
= &pcl726_boards
[0].name
,
426 .num_names
= ARRAY_SIZE(pcl726_boards
),
427 .offset
= sizeof(struct pcl726_board
),
429 module_comedi_driver(pcl726_driver
);
431 MODULE_AUTHOR("Comedi http://www.comedi.org");
432 MODULE_DESCRIPTION("Comedi driver for Advantech PCL-726 & compatibles");
433 MODULE_LICENSE("GPL");