pcmda12.c revision 214e7b5c8281bf41238f575128e4fec5652ed797
1/*
2    comedi/drivers/pcmda12.c
3    Driver for Winsystems PC-104 based PCM-D/A-12 8-channel AO board.
4
5    COMEDI - Linux Control and Measurement Device Interface
6    Copyright (C) 2006 Calin A. Culianu <calin@ajvar.org>
7
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.
12
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.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21*/
22/*
23Driver: pcmda12
24Description: A driver for the Winsystems PCM-D/A-12
25Devices: [Winsystems] PCM-D/A-12 (pcmda12)
26Author: Calin Culianu <calin@ajvar.org>
27Updated: Fri, 13 Jan 2006 12:01:01 -0500
28Status: works
29
30A driver for the relatively straightforward-to-program PCM-D/A-12.
31This board doesn't support commands, and the only way to set its
32analog output range is to jumper the board.  As such,
33comedi_data_write() ignores the range value specified.
34
35The board uses 16 consecutive I/O addresses starting at the I/O port
36base address.  Each address corresponds to the LSB then MSB of a
37particular channel from 0-7.
38
39Note that the board is not ISA-PNP capable and thus
40needs the I/O port comedi_config parameter.
41
42Note that passing a nonzero value as the second config option will
43enable "simultaneous xfer" mode for this board, in which AO writes
44will not take effect until a subsequent read of any AO channel.  This
45is so that one can speed up programming by preloading all AO registers
46with values before simultaneously setting them to take effect with one
47read command.
48
49Configuration Options:
50  [0] - I/O port base address
51  [1] - Do Simultaneous Xfer (see description)
52*/
53
54#include "../comedidev.h"
55
56#include <linux/pci.h>		/* for PCI devices */
57
58#define SDEV_NO ((int)(s - dev->subdevices))
59#define CHANS 8
60#define IOSIZE 16
61#define LSB(x) ((unsigned char)((x) & 0xff))
62#define MSB(x) ((unsigned char)((((unsigned short)(x))>>8) & 0xff))
63#define LSB_PORT(chan) (dev->iobase + (chan)*2)
64#define MSB_PORT(chan) (LSB_PORT(chan)+1)
65#define BITS 12
66
67/*
68 * Bords
69 */
70struct pcmda12_board {
71	const char *name;
72};
73
74/* note these have no effect and are merely here for reference..
75   these are configured by jumpering the board! */
76static const struct comedi_lrange pcmda12_ranges = {
77	3,
78	{
79			UNI_RANGE(5), UNI_RANGE(10), BIP_RANGE(5)
80		}
81};
82
83static const struct pcmda12_board pcmda12_boards[] = {
84	{
85	.name = "pcmda12",
86		},
87};
88
89/*
90 * Useful for shorthand access to the particular board structure
91 */
92#define thisboard ((const struct pcmda12_board *)dev->board_ptr)
93
94struct pcmda12_private {
95
96	unsigned int ao_readback[CHANS];
97	int simultaneous_xfer_mode;
98};
99
100
101#define devpriv ((struct pcmda12_private *)(dev->private))
102
103/*
104 * The struct comedi_driver structure tells the Comedi core module
105 * which functions to call to configure/deconfigure (attach/detach)
106 * the board, and also about the kernel module that contains
107 * the device code.
108 */
109static int pcmda12_attach(struct comedi_device *dev, struct comedi_devconfig *it);
110static int pcmda12_detach(struct comedi_device *dev);
111
112static void zero_chans(struct comedi_device *dev);
113
114static struct comedi_driver driver = {
115	.driver_name = "pcmda12",
116	.module = THIS_MODULE,
117	.attach = pcmda12_attach,
118	.detach = pcmda12_detach,
119/* It is not necessary to implement the following members if you are
120 * writing a driver for a ISA PnP or PCI card */
121	/* Most drivers will support multiple types of boards by
122	 * having an array of board structures.  These were defined
123	 * in pcmda12_boards[] above.  Note that the element 'name'
124	 * was first in the structure -- Comedi uses this fact to
125	 * extract the name of the board without knowing any details
126	 * about the structure except for its length.
127	 * When a device is attached (by comedi_config), the name
128	 * of the device is given to Comedi, and Comedi tries to
129	 * match it by going through the list of board names.  If
130	 * there is a match, the address of the pointer is put
131	 * into dev->board_ptr and driver->attach() is called.
132	 *
133	 * Note that these are not necessary if you can determine
134	 * the type of board in software.  ISA PnP, PCI, and PCMCIA
135	 * devices are such boards.
136	 */
137	.board_name = &pcmda12_boards[0].name,
138	.offset = sizeof(struct pcmda12_board),
139	.num_names = ARRAY_SIZE(pcmda12_boards),
140};
141
142static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
143	struct comedi_insn *insn, unsigned int *data);
144static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
145	struct comedi_insn *insn, unsigned int *data);
146
147/*
148 * Attach is called by the Comedi core to configure the driver
149 * for a particular board.  If you specified a board_name array
150 * in the driver structure, dev->board_ptr contains that
151 * address.
152 */
153static int pcmda12_attach(struct comedi_device *dev, struct comedi_devconfig *it)
154{
155	struct comedi_subdevice *s;
156	unsigned long iobase;
157
158	iobase = it->options[0];
159	printk("comedi%d: %s: io: %lx %s ", dev->minor, driver.driver_name,
160		iobase, it->options[1] ? "simultaneous xfer mode enabled" : "");
161
162	if (!request_region(iobase, IOSIZE, driver.driver_name)) {
163		printk("I/O port conflict\n");
164		return -EIO;
165	}
166	dev->iobase = iobase;
167
168/*
169 * Initialize dev->board_name.  Note that we can use the "thisboard"
170 * macro now, since we just initialized it in the last line.
171 */
172	dev->board_name = thisboard->name;
173
174/*
175 * Allocate the private structure area.  alloc_private() is a
176 * convenient macro defined in comedidev.h.
177 */
178	if (alloc_private(dev, sizeof(struct pcmda12_private)) < 0) {
179		printk("cannot allocate private data structure\n");
180		return -ENOMEM;
181	}
182
183	devpriv->simultaneous_xfer_mode = it->options[1];
184
185	/*
186	 * Allocate the subdevice structures.  alloc_subdevice() is a
187	 * convenient macro defined in comedidev.h.
188	 *
189	 * Allocate 2 subdevs (32 + 16 DIO lines) or 3 32 DIO subdevs for the
190	 * 96-channel version of the board.
191	 */
192	if (alloc_subdevices(dev, 1) < 0) {
193		printk("cannot allocate subdevice data structures\n");
194		return -ENOMEM;
195	}
196
197	s = dev->subdevices;
198	s->private = NULL;
199	s->maxdata = (0x1 << BITS) - 1;
200	s->range_table = &pcmda12_ranges;
201	s->type = COMEDI_SUBD_AO;
202	s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
203	s->n_chan = CHANS;
204	s->insn_write = &ao_winsn;
205	s->insn_read = &ao_rinsn;
206
207	zero_chans(dev);	/* clear out all the registers, basically */
208
209	printk("attached\n");
210
211	return 1;
212}
213
214/*
215 * _detach is called to deconfigure a device.  It should deallocate
216 * resources.
217 * This function is also called when _attach() fails, so it should be
218 * careful not to release resources that were not necessarily
219 * allocated by _attach().  dev->private and dev->subdevices are
220 * deallocated automatically by the core.
221 */
222static int pcmda12_detach(struct comedi_device *dev)
223{
224	printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
225	if (dev->iobase)
226		release_region(dev->iobase, IOSIZE);
227	return 0;
228}
229
230static void zero_chans(struct comedi_device *dev)
231{				/* sets up an
232				   ASIC chip to defaults */
233	int i;
234	for (i = 0; i < CHANS; ++i) {
235/*      /\* do this as one instruction?? *\/ */
236/*      outw(0, LSB_PORT(chan)); */
237		outb(0, LSB_PORT(i));
238		outb(0, MSB_PORT(i));
239	}
240	inb(LSB_PORT(0));	/* update chans. */
241}
242
243static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
244	struct comedi_insn *insn, unsigned int *data)
245{
246	int i;
247	int chan = CR_CHAN(insn->chanspec);
248
249	/* Writing a list of values to an AO channel is probably not
250	 * very useful, but that's how the interface is defined. */
251	for (i = 0; i < insn->n; ++i) {
252
253/*      /\* do this as one instruction?? *\/ */
254/*      outw(data[i], LSB_PORT(chan)); */
255
256		/* Need to do this as two instructions due to 8-bit bus?? */
257		/*  first, load the low byte */
258		outb(LSB(data[i]), LSB_PORT(chan));
259		/*  next, write the high byte */
260		outb(MSB(data[i]), MSB_PORT(chan));
261
262		/* save shadow register */
263		devpriv->ao_readback[chan] = data[i];
264
265		if (!devpriv->simultaneous_xfer_mode)
266			inb(LSB_PORT(chan));
267	}
268
269	/* return the number of samples written */
270	return i;
271}
272
273/* AO subdevices should have a read insn as well as a write insn.
274
275   Usually this means copying a value stored in devpriv->ao_readback.
276   However, since this driver supports simultaneous xfer then sometimes
277   this function actually accomplishes work.
278
279   Simultaneaous xfer mode is accomplished by loading ALL the values
280   you want for AO in all the channels, then READing off one of the AO
281   registers to initiate the instantaneous simultaneous update of all
282   DAC outputs, which makes all AO channels update simultaneously.
283   This is useful for some control applications, I would imagine.
284*/
285static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
286	struct comedi_insn *insn, unsigned int *data)
287{
288	int i;
289	int chan = CR_CHAN(insn->chanspec);
290
291	for (i = 0; i < insn->n; i++) {
292		if (devpriv->simultaneous_xfer_mode)
293			inb(LSB_PORT(chan));
294		/* read back shadow register */
295		data[i] = devpriv->ao_readback[chan];
296	}
297
298	return i;
299}
300
301/*
302 * A convenient macro that defines init_module() and cleanup_module(),
303 * as necessary.
304 */
305COMEDI_INITCLEANUP(driver);
306