staging: comedi: pcmad: remove subdevice pointer math
[linux-2.6-block.git] / drivers / staging / comedi / drivers / pcmda12.c
CommitLineData
647d8b45
CC
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
647d8b45
CC
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 */
387c136a 70struct pcmda12_board {
647d8b45 71 const char *name;
387c136a 72};
647d8b45
CC
73
74/* note these have no effect and are merely here for reference..
75 these are configured by jumpering the board! */
9ced1de6 76static const struct comedi_lrange pcmda12_ranges = {
647d8b45
CC
77 3,
78 {
0a85b6f0
MT
79 UNI_RANGE(5), UNI_RANGE(10), BIP_RANGE(5)
80 }
647d8b45
CC
81};
82
39eb312d
BP
83struct pcmda12_private {
84
790c5541 85 unsigned int ao_readback[CHANS];
647d8b45 86 int simultaneous_xfer_mode;
39eb312d
BP
87};
88
39eb312d 89#define devpriv ((struct pcmda12_private *)(dev->private))
647d8b45 90
924f4685
HS
91static void zero_chans(struct comedi_device *dev)
92{ /* sets up an
93 ASIC chip to defaults */
94 int i;
95 for (i = 0; i < CHANS; ++i) {
96/* /\* do this as one instruction?? *\/ */
97/* outw(0, LSB_PORT(chan)); */
98 outb(0, LSB_PORT(i));
99 outb(0, MSB_PORT(i));
100 }
101 inb(LSB_PORT(0)); /* update chans. */
102}
647d8b45 103
924f4685
HS
104static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
105 struct comedi_insn *insn, unsigned int *data)
106{
107 int i;
108 int chan = CR_CHAN(insn->chanspec);
647d8b45 109
924f4685
HS
110 /* Writing a list of values to an AO channel is probably not
111 * very useful, but that's how the interface is defined. */
112 for (i = 0; i < insn->n; ++i) {
647d8b45 113
924f4685
HS
114/* /\* do this as one instruction?? *\/ */
115/* outw(data[i], LSB_PORT(chan)); */
116
117 /* Need to do this as two instructions due to 8-bit bus?? */
118 /* first, load the low byte */
119 outb(LSB(data[i]), LSB_PORT(chan));
120 /* next, write the high byte */
121 outb(MSB(data[i]), MSB_PORT(chan));
122
123 /* save shadow register */
124 devpriv->ao_readback[chan] = data[i];
125
126 if (!devpriv->simultaneous_xfer_mode)
127 inb(LSB_PORT(chan));
128 }
129
130 /* return the number of samples written */
131 return i;
132}
133
134/* AO subdevices should have a read insn as well as a write insn.
135
136 Usually this means copying a value stored in devpriv->ao_readback.
137 However, since this driver supports simultaneous xfer then sometimes
138 this function actually accomplishes work.
139
140 Simultaneaous xfer mode is accomplished by loading ALL the values
141 you want for AO in all the channels, then READing off one of the AO
142 registers to initiate the instantaneous simultaneous update of all
143 DAC outputs, which makes all AO channels update simultaneously.
144 This is useful for some control applications, I would imagine.
145*/
da91b269 146static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
924f4685
HS
147 struct comedi_insn *insn, unsigned int *data)
148{
149 int i;
150 int chan = CR_CHAN(insn->chanspec);
151
152 for (i = 0; i < insn->n; i++) {
153 if (devpriv->simultaneous_xfer_mode)
154 inb(LSB_PORT(chan));
155 /* read back shadow register */
156 data[i] = devpriv->ao_readback[chan];
157 }
158
159 return i;
160}
647d8b45 161
0a85b6f0
MT
162static int pcmda12_attach(struct comedi_device *dev,
163 struct comedi_devconfig *it)
647d8b45 164{
2fbffee0 165 const struct pcmda12_board *board = comedi_board(dev);
34c43922 166 struct comedi_subdevice *s;
647d8b45 167 unsigned long iobase;
8b6c5694 168 int ret;
647d8b45
CC
169
170 iobase = it->options[0];
8b83e005 171 printk(KERN_INFO
2d2111ea 172 "comedi%d: %s: io: %lx %s ", dev->minor, dev->driver->driver_name,
0a85b6f0 173 iobase, it->options[1] ? "simultaneous xfer mode enabled" : "");
647d8b45 174
2d2111ea 175 if (!request_region(iobase, IOSIZE, dev->driver->driver_name)) {
647d8b45
CC
176 printk("I/O port conflict\n");
177 return -EIO;
178 }
179 dev->iobase = iobase;
180
2fbffee0 181 dev->board_name = board->name;
647d8b45
CC
182
183/*
184 * Allocate the private structure area. alloc_private() is a
185 * convenient macro defined in comedidev.h.
186 */
39eb312d 187 if (alloc_private(dev, sizeof(struct pcmda12_private)) < 0) {
8b83e005 188 printk(KERN_ERR "cannot allocate private data structure\n");
647d8b45
CC
189 return -ENOMEM;
190 }
191
192 devpriv->simultaneous_xfer_mode = it->options[1];
193
8b6c5694
HS
194 ret = comedi_alloc_subdevices(dev, 1);
195 if (ret)
196 return ret;
647d8b45
CC
197
198 s = dev->subdevices;
199 s->private = NULL;
200 s->maxdata = (0x1 << BITS) - 1;
201 s->range_table = &pcmda12_ranges;
202 s->type = COMEDI_SUBD_AO;
203 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
204 s->n_chan = CHANS;
205 s->insn_write = &ao_winsn;
206 s->insn_read = &ao_rinsn;
207
208 zero_chans(dev); /* clear out all the registers, basically */
209
8b83e005 210 printk(KERN_INFO "attached\n");
647d8b45
CC
211
212 return 1;
213}
214
484ecc95 215static void pcmda12_detach(struct comedi_device *dev)
647d8b45 216{
647d8b45
CC
217 if (dev->iobase)
218 release_region(dev->iobase, IOSIZE);
647d8b45
CC
219}
220
924f4685
HS
221static const struct pcmda12_board pcmda12_boards[] = {
222 {
223 .name = "pcmda12",
224 },
225};
647d8b45 226
294f930d 227static struct comedi_driver pcmda12_driver = {
924f4685
HS
228 .driver_name = "pcmda12",
229 .module = THIS_MODULE,
230 .attach = pcmda12_attach,
231 .detach = pcmda12_detach,
232 .board_name = &pcmda12_boards[0].name,
233 .offset = sizeof(struct pcmda12_board),
234 .num_names = ARRAY_SIZE(pcmda12_boards),
235};
294f930d 236module_comedi_driver(pcmda12_driver);
90f703d3
AT
237
238MODULE_AUTHOR("Comedi http://www.comedi.org");
239MODULE_DESCRIPTION("Comedi low-level driver");
240MODULE_LICENSE("GPL");