2 comedi/drivers/pcl726.c
4 hardware driver for Advantech cards:
5 card: PCL-726, PCL-727, PCL-728
6 driver: pcl726, pcl727, pcl728
8 card: ACL-6126, ACL-6128
9 driver: acl6126, acl6128
11 COMEDI - Linux Control and Measurement Device Interface
12 Copyright (C) 1998 David A. Schleef <ds@schleef.org>
14 This program is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation; either version 2 of the License, or
17 (at your option) any later version.
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
26 Description: Advantech PCL-726 & compatibles
29 Devices: [Advantech] PCL-726 (pcl726), PCL-727 (pcl727), PCL-728 (pcl728),
30 [ADLink] ACL-6126 (acl6126), ACL-6128 (acl6128)
32 Interrupts are not supported.
36 [2]...[7] - D/A output range for channel 1-6:
37 0: 0-5V, 1: 0-10V, 2: +/-5V, 3: +/-10V,
38 4: 4-20mA, 5: unknown (external reference)
42 [2]...[13] - D/A output range for channel 1-12:
43 0: 0-5V, 1: 0-10V, 2: +/-5V,
46 Options for PCL-728 and ACL-6128:
48 [2], [3] - D/A output range for channel 1 and 2:
49 0: 0-5V, 1: 0-10V, 2: +/-5V, 3: +/-10V,
54 [1] - IRQ (0=disable, 3, 5, 6, 7, 9, 10, 11, 12, 15) (currently ignored)
55 [2]...[7] - D/A output range for channel 1-6:
56 0: 0-5V, 1: 0-10V, 2: +/-5V, 3: +/-10V,
61 Thanks to Circuit Specialists for having programming info (!) on
62 their web page. (http://www.cir.com/)
65 #include <linux/module.h>
66 #include "../comedidev.h"
68 #undef ACL6126_IRQ /* no interrupt support (yet) */
70 #define PCL726_SIZE 16
71 #define PCL727_SIZE 32
74 #define PCL726_AO_MSB_REG(x) (0x00 + ((x) * 2))
75 #define PCL726_AO_LSB_REG(x) (0x01 + ((x) * 2))
77 #define PCL726_DO_HI 12
78 #define PCL726_DO_LO 13
79 #define PCL726_DI_HI 14
80 #define PCL726_DI_LO 15
82 #define PCL727_DO_HI 24
83 #define PCL727_DO_LO 25
84 #define PCL727_DI_HI 0
85 #define PCL727_DI_LO 1
87 static const struct comedi_lrange *const rangelist_726[] = {
88 &range_unipolar5, &range_unipolar10,
89 &range_bipolar5, &range_bipolar10,
90 &range_4_20mA, &range_unknown
93 static const struct comedi_lrange *const rangelist_727[] = {
94 &range_unipolar5, &range_unipolar10,
99 static const struct comedi_lrange *const rangelist_728[] = {
100 &range_unipolar5, &range_unipolar10,
101 &range_bipolar5, &range_bipolar10,
102 &range_4_20mA, &range_0_20mA
105 struct pcl726_board {
107 const char *name; /* driver name */
108 int n_aochan; /* num of D/A chans */
109 int num_of_ranges; /* num of ranges */
110 unsigned int IRQbits; /* allowed interrupts */
111 unsigned int io_range; /* len of IO space */
112 char have_dio; /* 1=card have DI/DO ports */
113 int di_hi; /* ports for DI/DO operations */
117 const struct comedi_lrange *const *range_type_list;
118 /* list of supported ranges */
121 static const struct pcl726_board boardtypes[] = {
127 .io_range = PCL726_SIZE,
129 .di_hi = PCL726_DI_HI,
130 .di_lo = PCL726_DI_LO,
131 .do_hi = PCL726_DO_HI,
132 .do_lo = PCL726_DO_LO,
133 .range_type_list = &rangelist_726[0],
139 .io_range = PCL727_SIZE,
141 .di_hi = PCL727_DI_HI,
142 .di_lo = PCL727_DI_LO,
143 .do_hi = PCL727_DO_HI,
144 .do_lo = PCL727_DO_LO,
145 .range_type_list = &rangelist_727[0],
151 .io_range = PCL728_SIZE,
157 .range_type_list = &rangelist_728[0],
163 .io_range = PCL726_SIZE,
165 .di_hi = PCL726_DI_HI,
166 .di_lo = PCL726_DI_LO,
167 .do_hi = PCL726_DO_HI,
168 .do_lo = PCL726_DO_LO,
169 .range_type_list = &rangelist_726[0],
175 .io_range = PCL728_SIZE,
181 .range_type_list = &rangelist_728[0],
185 struct pcl726_private {
186 const struct comedi_lrange *rangelist[12];
187 unsigned int ao_readback[12];
190 static int pcl726_ao_insn_write(struct comedi_device *dev,
191 struct comedi_subdevice *s,
192 struct comedi_insn *insn,
195 struct pcl726_private *devpriv = dev->private;
196 unsigned int chan = CR_CHAN(insn->chanspec);
197 unsigned int range = CR_RANGE(insn->chanspec);
201 for (i = 0; i < insn->n; i++) {
203 devpriv->ao_readback[chan] = val;
205 /* bipolar data to the DAC is two's complement */
206 if (comedi_chan_range_is_bipolar(s, chan, range))
207 val = comedi_offset_munge(s, val);
209 /* order is important, MSB then LSB */
210 outb((val >> 8) & 0xff, dev->iobase + PCL726_AO_MSB_REG(chan));
211 outb(val & 0xff, dev->iobase + PCL726_AO_LSB_REG(chan));
217 static int pcl726_ao_insn_read(struct comedi_device *dev,
218 struct comedi_subdevice *s,
219 struct comedi_insn *insn,
222 struct pcl726_private *devpriv = dev->private;
223 unsigned int chan = CR_CHAN(insn->chanspec);
226 for (i = 0; i < insn->n; i++)
227 data[i] = devpriv->ao_readback[chan];
232 static int pcl726_di_insn_bits(struct comedi_device *dev,
233 struct comedi_subdevice *s,
234 struct comedi_insn *insn, unsigned int *data)
236 const struct pcl726_board *board = comedi_board(dev);
238 data[1] = inb(dev->iobase + board->di_lo) |
239 (inb(dev->iobase + board->di_hi) << 8);
244 static int pcl726_do_insn_bits(struct comedi_device *dev,
245 struct comedi_subdevice *s,
246 struct comedi_insn *insn,
249 const struct pcl726_board *board = comedi_board(dev);
252 mask = comedi_dio_update_state(s, data);
255 outb(s->state & 0xff, dev->iobase + board->do_lo);
257 outb((s->state >> 8), dev->iobase + board->do_hi);
265 static int pcl726_attach(struct comedi_device *dev, struct comedi_devconfig *it)
267 const struct pcl726_board *board = comedi_board(dev);
268 struct pcl726_private *devpriv;
269 struct comedi_subdevice *s;
275 ret = comedi_request_region(dev, it->options[0], board->io_range);
279 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
283 for (i = 0; i < 12; i++)
284 devpriv->rangelist[i] = &range_unknown;
288 if (boardtypes[board].IRQbits != 0) { /* board support IRQ */
289 irq = it->options[1];
290 devpriv->first_chan = 2;
291 if (irq) { /* we want to use IRQ */
292 if (((1 << irq) & boardtypes[board].IRQbits) == 0) {
294 ", IRQ %d is out of allowed range,"
295 " DISABLING IT", irq);
296 irq = 0; /* Bad IRQ */
298 if (request_irq(irq, interrupt_pcl818, 0,
299 dev->board_name, dev)) {
301 ", unable to allocate IRQ %d,"
302 " DISABLING IT", irq);
303 irq = 0; /* Can't use IRQ */
305 printk(", irq=%d", irq);
316 ret = comedi_alloc_subdevices(dev, 3);
320 s = &dev->subdevices[0];
322 s->type = COMEDI_SUBD_AO;
323 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
324 s->n_chan = board->n_aochan;
327 s->insn_write = pcl726_ao_insn_write;
328 s->insn_read = pcl726_ao_insn_read;
329 s->range_table_list = devpriv->rangelist;
330 for (i = 0; i < board->n_aochan; i++) {
333 j = it->options[2 + 1];
334 if ((j < 0) || (j >= board->num_of_ranges)) {
336 ("Invalid range for channel %d! Must be 0<=%d<%d\n",
337 i, j, board->num_of_ranges - 1);
340 devpriv->rangelist[i] = board->range_type_list[j];
343 s = &dev->subdevices[1];
345 if (!board->have_dio) {
346 s->type = COMEDI_SUBD_UNUSED;
348 s->type = COMEDI_SUBD_DI;
349 s->subdev_flags = SDF_READABLE | SDF_GROUND;
353 s->insn_bits = pcl726_di_insn_bits;
354 s->range_table = &range_digital;
357 s = &dev->subdevices[2];
359 if (!board->have_dio) {
360 s->type = COMEDI_SUBD_UNUSED;
362 s->type = COMEDI_SUBD_DO;
363 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
367 s->insn_bits = pcl726_do_insn_bits;
368 s->range_table = &range_digital;
374 static struct comedi_driver pcl726_driver = {
375 .driver_name = "pcl726",
376 .module = THIS_MODULE,
377 .attach = pcl726_attach,
378 .detach = comedi_legacy_detach,
379 .board_name = &boardtypes[0].name,
380 .num_names = ARRAY_SIZE(boardtypes),
381 .offset = sizeof(struct pcl726_board),
383 module_comedi_driver(pcl726_driver);
385 MODULE_AUTHOR("Comedi http://www.comedi.org");
386 MODULE_DESCRIPTION("Comedi low-level driver");
387 MODULE_LICENSE("GPL");