pcmda12.c revision 790c55415aa31f4c732729f94d2c3a54f7d3bfc2
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(comedi_device * dev, comedi_devconfig * it); 109static int pcmda12_detach(comedi_device * dev); 110 111static void zero_chans(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(comedi_device * dev, comedi_subdevice * s, 142 comedi_insn * insn, unsigned int * data); 143static int ao_rinsn(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(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(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(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(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(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