pcmda12.c revision 71b5f4f11971dea972832ad63a994c7e5b45db6b
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 MIN(a,b) ( ((a) < (b)) ? (a) : (b) )
59#define SDEV_NO ((int)(s - dev->subdevices))
60#define CHANS 8
61#define IOSIZE 16
62#define LSB(x) ((unsigned char)((x) & 0xff))
63#define MSB(x) ((unsigned char)((((unsigned short)(x))>>8) & 0xff))
64#define LSB_PORT(chan) (dev->iobase + (chan)*2)
65#define MSB_PORT(chan) (LSB_PORT(chan)+1)
66#define BITS 12
67
68/*
69 * Bords
70 */
71typedef struct pcmda12_board_struct {
72	const char *name;
73} pcmda12_board;
74
75/* note these have no effect and are merely here for reference..
76   these are configured by jumpering the board! */
77static const comedi_lrange pcmda12_ranges = {
78	3,
79	{
80			UNI_RANGE(5), UNI_RANGE(10), BIP_RANGE(5)
81		}
82};
83
84static const pcmda12_board pcmda12_boards[] = {
85	{
86	      name:	"pcmda12",
87		},
88};
89
90/*
91 * Useful for shorthand access to the particular board structure
92 */
93#define thisboard ((const pcmda12_board *)dev->board_ptr)
94
95typedef struct {
96	unsigned int ao_readback[CHANS];
97	int simultaneous_xfer_mode;
98} pcmda12_private;
99
100#define devpriv ((pcmda12_private *)(dev->private))
101
102/*
103 * The comedi_driver structure tells the Comedi core module
104 * which functions to call to configure/deconfigure (attach/detach)
105 * the board, and also about the kernel module that contains
106 * the device code.
107 */
108static int pcmda12_attach(struct comedi_device * dev, comedi_devconfig * it);
109static int pcmda12_detach(struct comedi_device * dev);
110
111static void zero_chans(struct comedi_device * dev);
112
113static comedi_driver driver = {
114      driver_name:"pcmda12",
115      module:THIS_MODULE,
116      attach:pcmda12_attach,
117      detach:pcmda12_detach,
118/* It is not necessary to implement the following members if you are
119 * writing a driver for a ISA PnP or PCI card */
120	/* Most drivers will support multiple types of boards by
121	 * having an array of board structures.  These were defined
122	 * in pcmda12_boards[] above.  Note that the element 'name'
123	 * was first in the structure -- Comedi uses this fact to
124	 * extract the name of the board without knowing any details
125	 * about the structure except for its length.
126	 * When a device is attached (by comedi_config), the name
127	 * of the device is given to Comedi, and Comedi tries to
128	 * match it by going through the list of board names.  If
129	 * there is a match, the address of the pointer is put
130	 * into dev->board_ptr and driver->attach() is called.
131	 *
132	 * Note that these are not necessary if you can determine
133	 * the type of board in software.  ISA PnP, PCI, and PCMCIA
134	 * devices are such boards.
135	 */
136      board_name:&pcmda12_boards[0].name,
137      offset:sizeof(pcmda12_board),
138      num_names:sizeof(pcmda12_boards) / sizeof(pcmda12_board),
139};
140
141static int ao_winsn(struct comedi_device * dev, comedi_subdevice * s,
142	comedi_insn * insn, unsigned int * data);
143static int ao_rinsn(struct comedi_device * dev, comedi_subdevice * s,
144	comedi_insn * insn, unsigned int * data);
145
146/*
147 * Attach is called by the Comedi core to configure the driver
148 * for a particular board.  If you specified a board_name array
149 * in the driver structure, dev->board_ptr contains that
150 * address.
151 */
152static int pcmda12_attach(struct comedi_device * dev, comedi_devconfig * it)
153{
154	comedi_subdevice *s;
155	unsigned long iobase;
156
157	iobase = it->options[0];
158	printk("comedi%d: %s: io: %lx %s ", dev->minor, driver.driver_name,
159		iobase, it->options[1] ? "simultaneous xfer mode enabled" : "");
160
161	if (!request_region(iobase, IOSIZE, driver.driver_name)) {
162		printk("I/O port conflict\n");
163		return -EIO;
164	}
165	dev->iobase = iobase;
166
167/*
168 * Initialize dev->board_name.  Note that we can use the "thisboard"
169 * macro now, since we just initialized it in the last line.
170 */
171	dev->board_name = thisboard->name;
172
173/*
174 * Allocate the private structure area.  alloc_private() is a
175 * convenient macro defined in comedidev.h.
176 */
177	if (alloc_private(dev, sizeof(pcmda12_private)) < 0) {
178		printk("cannot allocate private data structure\n");
179		return -ENOMEM;
180	}
181
182	devpriv->simultaneous_xfer_mode = it->options[1];
183
184	/*
185	 * Allocate the subdevice structures.  alloc_subdevice() is a
186	 * convenient macro defined in comedidev.h.
187	 *
188	 * Allocate 2 subdevs (32 + 16 DIO lines) or 3 32 DIO subdevs for the
189	 * 96-channel version of the board.
190	 */
191	if (alloc_subdevices(dev, 1) < 0) {
192		printk("cannot allocate subdevice data structures\n");
193		return -ENOMEM;
194	}
195
196	s = dev->subdevices;
197	s->private = NULL;
198	s->maxdata = (0x1 << BITS) - 1;
199	s->range_table = &pcmda12_ranges;
200	s->type = COMEDI_SUBD_AO;
201	s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
202	s->n_chan = CHANS;
203	s->insn_write = &ao_winsn;
204	s->insn_read = &ao_rinsn;
205
206	zero_chans(dev);	/* clear out all the registers, basically */
207
208	printk("attached\n");
209
210	return 1;
211}
212
213/*
214 * _detach is called to deconfigure a device.  It should deallocate
215 * resources.
216 * This function is also called when _attach() fails, so it should be
217 * careful not to release resources that were not necessarily
218 * allocated by _attach().  dev->private and dev->subdevices are
219 * deallocated automatically by the core.
220 */
221static int pcmda12_detach(struct comedi_device * dev)
222{
223	printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
224	if (dev->iobase)
225		release_region(dev->iobase, IOSIZE);
226	return 0;
227}
228
229static void zero_chans(struct comedi_device * dev)
230{				/* sets up an
231				   ASIC chip to defaults */
232	int i;
233	for (i = 0; i < CHANS; ++i) {
234/*      /\* do this as one instruction?? *\/ */
235/*      outw(0, LSB_PORT(chan)); */
236		outb(0, LSB_PORT(i));
237		outb(0, MSB_PORT(i));
238	}
239	inb(LSB_PORT(0));	/* update chans. */
240}
241
242static int ao_winsn(struct comedi_device * dev, comedi_subdevice * s,
243	comedi_insn * insn, unsigned int * data)
244{
245	int i;
246	int chan = CR_CHAN(insn->chanspec);
247
248	/* Writing a list of values to an AO channel is probably not
249	 * very useful, but that's how the interface is defined. */
250	for (i = 0; i < insn->n; ++i) {
251
252/*      /\* do this as one instruction?? *\/ */
253/*      outw(data[i], LSB_PORT(chan)); */
254
255		/* Need to do this as two instructions due to 8-bit bus?? */
256		/*  first, load the low byte */
257		outb(LSB(data[i]), LSB_PORT(chan));
258		/*  next, write the high byte */
259		outb(MSB(data[i]), MSB_PORT(chan));
260
261		/* save shadow register */
262		devpriv->ao_readback[chan] = data[i];
263
264		if (!devpriv->simultaneous_xfer_mode)
265			inb(LSB_PORT(chan));
266	}
267
268	/* return the number of samples written */
269	return i;
270}
271
272/* AO subdevices should have a read insn as well as a write insn.
273
274   Usually this means copying a value stored in devpriv->ao_readback.
275   However, since this driver supports simultaneous xfer then sometimes
276   this function actually accomplishes work.
277
278   Simultaneaous xfer mode is accomplished by loading ALL the values
279   you want for AO in all the channels, then READing off one of the AO
280   registers to initiate the instantaneous simultaneous update of all
281   DAC outputs, which makes all AO channels update simultaneously.
282   This is useful for some control applications, I would imagine.
283*/
284static int ao_rinsn(struct comedi_device * dev, comedi_subdevice * s,
285	comedi_insn * insn, unsigned int * data)
286{
287	int i;
288	int chan = CR_CHAN(insn->chanspec);
289
290	for (i = 0; i < insn->n; i++) {
291		if (devpriv->simultaneous_xfer_mode)
292			inb(LSB_PORT(chan));
293		/* read back shadow register */
294		data[i] = devpriv->ao_readback[chan];
295	}
296
297	return i;
298}
299
300/*
301 * A convenient macro that defines init_module() and cleanup_module(),
302 * as necessary.
303 */
304COMEDI_INITCLEANUP(driver);
305