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