adq12b.c revision 0a85b6f0ab0d2edb0d41b32697111ce0e4f43496
1/* 2 comedi/drivers/adq12b.c 3 driver for MicroAxial ADQ12-B data acquisition and control card 4 5 COMEDI - Linux Control and Measurement Device Interface 6 Copyright (C) 2000 David A. Schleef <ds@schleef.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*/ 23/* 24Driver: adq12b 25Description: driver for MicroAxial ADQ12-B data acquisition and control card 26Devices: [MicroAxial] ADQ12-B (adq12b) 27Author: jeremy theler <thelerg@ib.cnea.gov.ar> 28Updated: Thu, 21 Feb 2008 02:56:27 -0300 29Status: works 30 31Driver for the acquisition card ADQ12-B (without any add-on). 32 33 - Analog input is subdevice 0 (16 channels single-ended or 8 differential) 34 - Digital input is subdevice 1 (5 channels) 35 - Digital output is subdevice 1 (8 channels) 36 - The PACER is not supported in this version 37 38If you do not specify any options, they will default to 39 40 # comedi_config /dev/comedi0 adq12b 0x300,0,0 41 42 option 1: I/O base address. The following table is provided as a help 43 of the hardware jumpers. 44 45 address jumper JADR 46 0x300 1 (factory default) 47 0x320 2 48 0x340 3 49 0x360 4 50 0x380 5 51 0x3A0 6 52 53 option 2: unipolar/bipolar ADC selection: 0 -> bipolar, 1 -> unipolar 54 55 selection comedi_config option JUB 56 bipolar 0 2-3 (factory default) 57 unipolar 1 1-2 58 59 option 3: single-ended/differential AI selection: 0 -> SE, 1 -> differential 60 61 selection comedi_config option JCHA JCHB 62 single-ended 0 1-2 1-2 (factory default) 63 differential 1 2-3 2-3 64 65 written by jeremy theler <thelerg@ib.cnea.gov.ar> 66 67 instituto balseiro 68 comision nacional de energia atomica 69 universidad nacional de cuyo 70 argentina 71 72 21-feb-2008 73 + changed supported devices string (missused the [] and ()) 74 75 13-oct-2007 76 + first try 77 78 79*/ 80 81#include "../comedidev.h" 82 83/* address scheme (page 2.17 of the manual) */ 84#define ADQ12B_SIZE 16 85 86#define ADQ12B_CTREG 0x00 87#define ADQ12B_STINR 0x00 88#define ADQ12B_OUTBR 0x04 89#define ADQ12B_ADLOW 0x08 90#define ADQ12B_ADHIG 0x09 91#define ADQ12B_CONT0 0x0c 92#define ADQ12B_CONT1 0x0d 93#define ADQ12B_CONT2 0x0e 94#define ADQ12B_COWORD 0x0f 95 96/* mask of the bit at STINR to check end of conversion */ 97#define ADQ12B_EOC 0x20 98 99#define TIMEOUT 20 100 101/* available ranges through the PGA gains */ 102static const struct comedi_lrange range_adq12b_ai_bipolar = { 4, { 103 BIP_RANGE(5), 104 BIP_RANGE(2), 105 BIP_RANGE(1), 106 BIP_RANGE(0.5) 107 } 108}; 109 110static const struct comedi_lrange range_adq12b_ai_unipolar = { 4, { 111 UNI_RANGE(5), 112 UNI_RANGE(2), 113 UNI_RANGE(1), 114 UNI_RANGE 115 (0.5) 116 } 117}; 118 119struct adq12b_board { 120 const char *name; 121 int ai_se_chans; 122 int ai_diff_chans; 123 int ai_bits; 124 int di_chans; 125 int do_chans; 126}; 127 128static const struct adq12b_board adq12b_boards[] = { 129 { 130 .name = "adq12b", 131 .ai_se_chans = 16, 132 .ai_diff_chans = 8, 133 .ai_bits = 12, 134 .di_chans = 5, 135 .do_chans = 8} 136/* potentially, more adq-based deviced will be added */ 137/*, 138 .name = "adq12b", 139 .ai_chans = 16, // this is just for reference, hardcoded again later 140 .ai_bits = 12, 141 .di_chans = 8, 142 .do_chans = 5 143 }*/ 144}; 145 146#define thisboard ((const struct adq12b_board *)dev->board_ptr) 147 148struct adq12b_private { 149 int unipolar; /* option 2 of comedi_config (1 is iobase) */ 150 int differential; /* option 3 of comedi_config */ 151 int last_channel; 152 int last_range; 153 unsigned int digital_state; 154}; 155 156#define devpriv ((struct adq12b_private *)dev->private) 157 158/* 159 * The struct comedi_driver structure tells the Comedi core module 160 * which functions to call to configure/deconfigure (attach/detach) 161 * the board, and also about the kernel module that contains 162 * the device code. 163 */ 164static int adq12b_attach(struct comedi_device *dev, 165 struct comedi_devconfig *it); 166static int adq12b_detach(struct comedi_device *dev); 167static struct comedi_driver driver_adq12b = { 168driver_name:"adq12b", 169module:THIS_MODULE, 170attach:adq12b_attach, 171detach:adq12b_detach, 172board_name:&adq12b_boards[0].name, 173offset:sizeof(struct adq12b_board), 174num_names:ARRAY_SIZE(adq12b_boards), 175}; 176 177static int adq12b_ai_rinsn(struct comedi_device *dev, 178 struct comedi_subdevice *s, struct comedi_insn *insn, 179 unsigned int *data); 180static int adq12b_di_insn_bits(struct comedi_device *dev, 181 struct comedi_subdevice *s, 182 struct comedi_insn *insn, unsigned int *data); 183static int adq12b_do_insn_bits(struct comedi_device *dev, 184 struct comedi_subdevice *s, 185 struct comedi_insn *insn, unsigned int *data); 186 187/* 188 * Attach is called by the Comedi core to configure the driver 189 * for a particular board. If you specified a board_name array 190 * in the driver structure, dev->board_ptr contains that 191 * address. 192 */ 193static int adq12b_attach(struct comedi_device *dev, struct comedi_devconfig *it) 194{ 195 struct comedi_subdevice *s; 196 unsigned long iobase; 197 int unipolar, differential; 198 199 iobase = it->options[0]; 200 unipolar = it->options[1]; 201 differential = it->options[2]; 202 203 printk("comedi%d: adq12b called with options base=0x%03lx, %s and %s\n", 204 dev->minor, iobase, (unipolar == 1) ? "unipolar" : "bipolar", 205 (differential == 1) ? "differential" : "single-ended"); 206 207 /* if no address was specified, try the default 0x300 */ 208 if (iobase == 0) { 209 printk 210 ("comedi%d: adq12b warning: I/O base address not specified. Trying the default 0x300.\n", 211 dev->minor); 212 iobase = 0x300; 213 } 214 215 printk("comedi%d: adq12b: 0x%04lx ", dev->minor, iobase); 216 if (!request_region(iobase, ADQ12B_SIZE, "adq12b")) { 217 printk("I/O port conflict\n"); 218 return -EIO; 219 } 220 dev->iobase = iobase; 221 222/* 223 * Initialize dev->board_name. Note that we can use the "thisboard" 224 * macro now, since we just initialized it in the last line. 225 */ 226 dev->board_name = thisboard->name; 227 228/* 229 * Allocate the private structure area. alloc_private() is a 230 * convenient macro defined in comedidev.h. 231 */ 232 if (alloc_private(dev, sizeof(struct adq12b_private)) < 0) 233 return -ENOMEM; 234 235/* fill in devpriv structure */ 236 devpriv->unipolar = unipolar; 237 devpriv->differential = differential; 238 devpriv->digital_state = 0; 239/* initialize channel and range to -1 so we make sure we always write 240 at least once to the CTREG in the instruction */ 241 devpriv->last_channel = -1; 242 devpriv->last_range = -1; 243 244/* 245 * Allocate the subdevice structures. alloc_subdevice() is a 246 * convenient macro defined in comedidev.h. 247 */ 248 if (alloc_subdevices(dev, 3) < 0) 249 return -ENOMEM; 250 251 s = dev->subdevices + 0; 252 /* analog input subdevice */ 253 s->type = COMEDI_SUBD_AI; 254 if (differential) { 255 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF; 256 s->n_chan = thisboard->ai_diff_chans; 257 } else { 258 s->subdev_flags = SDF_READABLE | SDF_GROUND; 259 s->n_chan = thisboard->ai_se_chans; 260 } 261 262 if (unipolar) { 263 s->range_table = &range_adq12b_ai_unipolar; 264 } else { 265 s->range_table = &range_adq12b_ai_bipolar; 266 } 267 268 s->maxdata = (1 << thisboard->ai_bits) - 1; 269 270 s->len_chanlist = 4; /* This is the maximum chanlist length that 271 the board can handle */ 272 s->insn_read = adq12b_ai_rinsn; 273 274 s = dev->subdevices + 1; 275 /* digital input subdevice */ 276 s->type = COMEDI_SUBD_DI; 277 s->subdev_flags = SDF_READABLE; 278 s->n_chan = thisboard->di_chans; 279 s->maxdata = 1; 280 s->range_table = &range_digital; 281 s->insn_bits = adq12b_di_insn_bits; 282 283 s = dev->subdevices + 2; 284 /* digital output subdevice */ 285 s->type = COMEDI_SUBD_DO; 286 s->subdev_flags = SDF_WRITABLE; 287 s->n_chan = thisboard->do_chans; 288 s->maxdata = 1; 289 s->range_table = &range_digital; 290 s->insn_bits = adq12b_do_insn_bits; 291 292 printk("attached\n"); 293 294 return 0; 295} 296 297/* 298 * _detach is called to deconfigure a device. It should deallocate 299 * resources. 300 * This function is also called when _attach() fails, so it should be 301 * careful not to release resources that were not necessarily 302 * allocated by _attach(). dev->private and dev->subdevices are 303 * deallocated automatically by the core. 304 */ 305static int adq12b_detach(struct comedi_device *dev) 306{ 307 if (dev->iobase) 308 release_region(dev->iobase, ADQ12B_SIZE); 309 310 kfree(devpriv); 311 312 printk("comedi%d: adq12b: removed\n", dev->minor); 313 314 return 0; 315} 316 317/* 318 * "instructions" read/write data in "one-shot" or "software-triggered" 319 * mode. 320 */ 321 322static int adq12b_ai_rinsn(struct comedi_device *dev, 323 struct comedi_subdevice *s, struct comedi_insn *insn, 324 unsigned int *data) 325{ 326 int n, i; 327 int range, channel; 328 unsigned char hi, lo, status; 329 330 /* change channel and range only if it is different from the previous */ 331 range = CR_RANGE(insn->chanspec); 332 channel = CR_CHAN(insn->chanspec); 333 if (channel != devpriv->last_channel || range != devpriv->last_range) { 334 outb((range << 4) | channel, dev->iobase + ADQ12B_CTREG); 335 udelay(50); /* wait for the mux to settle */ 336 } 337 338 /* trigger conversion */ 339 status = inb(dev->iobase + ADQ12B_ADLOW); 340 341 /* convert n samples */ 342 for (n = 0; n < insn->n; n++) { 343 344 /* wait for end of convertion */ 345 i = 0; 346 do { 347/* udelay(1); */ 348 status = inb(dev->iobase + ADQ12B_STINR); 349 status = status & ADQ12B_EOC; 350 } while (status == 0 && ++i < TIMEOUT); 351/* } while (++i < 10); */ 352 353 /* read data */ 354 hi = inb(dev->iobase + ADQ12B_ADHIG); 355 lo = inb(dev->iobase + ADQ12B_ADLOW); 356 357 /* printk("debug: chan=%d range=%d status=%d hi=%d lo=%d\n", channel, range, status, hi, lo); */ 358 data[n] = (hi << 8) | lo; 359 360 } 361 362 /* return the number of samples read/written */ 363 return n; 364} 365 366static int adq12b_di_insn_bits(struct comedi_device *dev, 367 struct comedi_subdevice *s, 368 struct comedi_insn *insn, unsigned int *data) 369{ 370 371 /* only bits 0-4 have information about digital inputs */ 372 data[1] = (inb(dev->iobase + ADQ12B_STINR) & (0x1f)); 373 374 return 2; 375} 376 377static int adq12b_do_insn_bits(struct comedi_device *dev, 378 struct comedi_subdevice *s, 379 struct comedi_insn *insn, unsigned int *data) 380{ 381 int channel; 382 383 for (channel = 0; channel < 8; channel++) 384 if (((data[0] >> channel) & 0x01) != 0) 385 outb((((data[1] >> channel) & 0x01) << 3) | channel, 386 dev->iobase + ADQ12B_OUTBR); 387 388 /* store information to retrieve when asked for reading */ 389 if (data[0]) { 390 devpriv->digital_state &= ~data[0]; 391 devpriv->digital_state |= (data[0] & data[1]); 392 } 393 394 data[1] = devpriv->digital_state; 395 396 return 2; 397} 398 399/* 400 * A convenient macro that defines init_module() and cleanup_module(), 401 * as necessary. 402 */ 403COMEDI_INITCLEANUP(driver_adq12b); 404