Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pcmda12.c
Go to the documentation of this file.
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 <[email protected]>
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 /*
23 Driver: pcmda12
24 Description: A driver for the Winsystems PCM-D/A-12
25 Devices: [Winsystems] PCM-D/A-12 (pcmda12)
26 Author: Calin Culianu <[email protected]>
27 Updated: Fri, 13 Jan 2006 12:01:01 -0500
28 Status: works
29 
30 A driver for the relatively straightforward-to-program PCM-D/A-12.
31 This board doesn't support commands, and the only way to set its
32 analog output range is to jumper the board. As such,
33 comedi_data_write() ignores the range value specified.
34 
35 The board uses 16 consecutive I/O addresses starting at the I/O port
36 base address. Each address corresponds to the LSB then MSB of a
37 particular channel from 0-7.
38 
39 Note that the board is not ISA-PNP capable and thus
40 needs the I/O port comedi_config parameter.
41 
42 Note that passing a nonzero value as the second config option will
43 enable "simultaneous xfer" mode for this board, in which AO writes
44 will not take effect until a subsequent read of any AO channel. This
45 is so that one can speed up programming by preloading all AO registers
46 with values before simultaneously setting them to take effect with one
47 read command.
48 
49 Configuration 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  */
70 struct 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! */
76 static const struct comedi_lrange pcmda12_ranges = {
77  3,
78  {
79  UNI_RANGE(5), UNI_RANGE(10), BIP_RANGE(5)
80  }
81 };
82 
84 
85  unsigned int ao_readback[CHANS];
87 };
88 
89 #define devpriv ((struct pcmda12_private *)(dev->private))
90 
91 static 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 }
103 
104 static 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);
109 
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) {
113 
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 */
146 static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
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 }
161 
162 static int pcmda12_attach(struct comedi_device *dev,
163  struct comedi_devconfig *it)
164 {
165  const struct pcmda12_board *board = comedi_board(dev);
166  struct comedi_subdevice *s;
167  unsigned long iobase;
168  int ret;
169 
170  iobase = it->options[0];
172  "comedi%d: %s: io: %lx %s ", dev->minor, dev->driver->driver_name,
173  iobase, it->options[1] ? "simultaneous xfer mode enabled" : "");
174 
175  if (!request_region(iobase, IOSIZE, dev->driver->driver_name)) {
176  printk("I/O port conflict\n");
177  return -EIO;
178  }
179  dev->iobase = iobase;
180 
181  dev->board_name = board->name;
182 
183 /*
184  * Allocate the private structure area. alloc_private() is a
185  * convenient macro defined in comedidev.h.
186  */
187  if (alloc_private(dev, sizeof(struct pcmda12_private)) < 0) {
188  printk(KERN_ERR "cannot allocate private data structure\n");
189  return -ENOMEM;
190  }
191 
192  devpriv->simultaneous_xfer_mode = it->options[1];
193 
194  ret = comedi_alloc_subdevices(dev, 1);
195  if (ret)
196  return ret;
197 
198  s = &dev->subdevices[0];
199  s->private = NULL;
200  s->maxdata = (0x1 << BITS) - 1;
201  s->range_table = &pcmda12_ranges;
202  s->type = COMEDI_SUBD_AO;
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 
210  printk(KERN_INFO "attached\n");
211 
212  return 1;
213 }
214 
215 static void pcmda12_detach(struct comedi_device *dev)
216 {
217  if (dev->iobase)
219 }
220 
221 static const struct pcmda12_board pcmda12_boards[] = {
222  {
223  .name = "pcmda12",
224  },
225 };
226 
227 static struct comedi_driver pcmda12_driver = {
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 };
236 module_comedi_driver(pcmda12_driver);
237 
238 MODULE_AUTHOR("Comedi http://www.comedi.org");
239 MODULE_DESCRIPTION("Comedi low-level driver");
240 MODULE_LICENSE("GPL");