3 Sensoray s526 Comedi driver
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
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.
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.
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.
25 Description: Sensoray 526 driver
26 Devices: [Sensoray] 526 (s526)
28 Everett Wang <everett.wang@everteq.com>
29 Updated: Thu, 14 Sep. 2006
36 Commands are not supported yet.
38 Configuration Options:
40 comedi_config /dev/comedi0 s526 0x2C0,0x3
44 #include <linux/comedidev.h>
45 #include <linux/ioport.h>
46 #include <asm/byteorder.h>
50 #define S526_START_AI_CONV 0
51 #define S526_AI_READ 0
54 #define S526_IOSIZE 0x40
55 #define S526_NUM_PORTS 27
86 static const int s526_ports[] = {
117 #if defined (__LITTLE_ENDIAN_BITFIELD)
118 unsigned short coutSource:1;
119 unsigned short coutPolarity:1;
120 unsigned short autoLoadResetRcap:3;
121 unsigned short hwCtEnableSource:2;
122 unsigned short ctEnableCtrl:2;
123 unsigned short clockSource:2;
124 unsigned short countDir:1;
125 unsigned short countDirCtrl:1;
126 unsigned short outputRegLatchCtrl:1;
127 unsigned short preloadRegSel:1;
128 unsigned short reserved:1;
129 #elif defined(__BIG_ENDIAN_BITFIELD)
130 unsigned short reserved:1;
131 unsigned short preloadRegSel:1;
132 unsigned short outputRegLatchCtrl:1;
133 unsigned short countDirCtrl:1;
134 unsigned short countDir:1;
135 unsigned short clockSource:2;
136 unsigned short ctEnableCtrl:2;
137 unsigned short hwCtEnableSource:2;
138 unsigned short autoLoadResetRcap:3;
139 unsigned short coutPolarity:1;
140 unsigned short coutSource:1;
142 #error Unknown bit field order
144 } counter_mode_register_t;
147 counter_mode_register_t reg;
148 unsigned short value;
151 #define MAX_GPCT_CONFIG_DATA 6
153 /* Different Application Classes for GPCT Subdevices */
154 /* The list is not exhaustive and needs discussion! */
156 CountingAndTimeMeasurement,
157 SinglePulseGeneration,
158 PulseTrainGeneration,
161 } S526_GPCT_APP_CLASS;
163 /* Config struct for different GPCT subdevice Application Classes and
166 typedef struct s526GPCTConfig {
167 S526_GPCT_APP_CLASS app;
168 int data[MAX_GPCT_CONFIG_DATA];
169 } s526_gpct_config_t;
172 * Board descriptions for two imaginary boards. Describing the
173 * boards in this way is optional, and completely driver-dependent.
174 * Some drivers use arrays such as this, other do not.
176 typedef struct s526_board_struct {
187 static const s526_board s526_boards[] = {
200 #define ADDR_REG(reg) (dev->iobase + (reg))
201 #define ADDR_CHAN_REG(reg, chan) (dev->iobase + (reg) + (chan) * 8)
204 * Useful for shorthand access to the particular board structure
206 #define thisboard ((const s526_board *)dev->board_ptr)
208 /* this structure is for data unique to this hardware driver. If
209 several hardware drivers keep similar information in this structure,
210 feel free to suggest moving the variable to the comedi_device struct. */
214 /* would be useful for a PCI device */
215 struct pci_dev *pci_dev;
217 /* Used for AO readback */
218 lsampl_t ao_readback[2];
220 s526_gpct_config_t s526_gpct_config[4];
221 unsigned short s526_ai_config;
224 * most drivers define the following macro to make it easy to
225 * access the private structure.
227 #define devpriv ((s526_private *)dev->private)
230 * The comedi_driver structure tells the Comedi core module
231 * which functions to call to configure/deconfigure (attach/detach)
232 * the board, and also about the kernel module that contains
235 static int s526_attach(comedi_device * dev, comedi_devconfig * it);
236 static int s526_detach(comedi_device * dev);
237 static comedi_driver driver_s526 = {
242 /* It is not necessary to implement the following members if you are
243 * writing a driver for a ISA PnP or PCI card */
244 /* Most drivers will support multiple types of boards by
245 * having an array of board structures. These were defined
246 * in s526_boards[] above. Note that the element 'name'
247 * was first in the structure -- Comedi uses this fact to
248 * extract the name of the board without knowing any details
249 * about the structure except for its length.
250 * When a device is attached (by comedi_config), the name
251 * of the device is given to Comedi, and Comedi tries to
252 * match it by going through the list of board names. If
253 * there is a match, the address of the pointer is put
254 * into dev->board_ptr and driver->attach() is called.
256 * Note that these are not necessary if you can determine
257 * the type of board in software. ISA PnP, PCI, and PCMCIA
258 * devices are such boards.
260 board_name:&s526_boards[0].name,
261 offset:sizeof(s526_board),
262 num_names:sizeof(s526_boards) / sizeof(s526_board),
265 static int s526_gpct_rinsn(comedi_device * dev, comedi_subdevice * s,
266 comedi_insn * insn, lsampl_t * data);
267 static int s526_gpct_insn_config(comedi_device * dev, comedi_subdevice * s,
268 comedi_insn * insn, lsampl_t * data);
269 static int s526_gpct_winsn(comedi_device * dev, comedi_subdevice * s,
270 comedi_insn * insn, lsampl_t * data);
271 static int s526_ai_insn_config(comedi_device * dev, comedi_subdevice * s,
272 comedi_insn * insn, lsampl_t * data);
273 static int s526_ai_rinsn(comedi_device * dev, comedi_subdevice * s,
274 comedi_insn * insn, lsampl_t * data);
275 static int s526_ao_winsn(comedi_device * dev, comedi_subdevice * s,
276 comedi_insn * insn, lsampl_t * data);
277 static int s526_ao_rinsn(comedi_device * dev, comedi_subdevice * s,
278 comedi_insn * insn, lsampl_t * data);
279 static int s526_dio_insn_bits(comedi_device * dev, comedi_subdevice * s,
280 comedi_insn * insn, lsampl_t * data);
281 static int s526_dio_insn_config(comedi_device * dev, comedi_subdevice * s,
282 comedi_insn * insn, lsampl_t * data);
285 * Attach is called by the Comedi core to configure the driver
286 * for a particular board. If you specified a board_name array
287 * in the driver structure, dev->board_ptr contains that
290 static int s526_attach(comedi_device * dev, comedi_devconfig * it)
296 // int subdev_channel = 0;
299 printk("comedi%d: s526: ", dev->minor);
301 iobase = it->options[0];
302 if (!iobase || !request_region(iobase, S526_IOSIZE, thisboard->name)) {
303 comedi_error(dev, "I/O port conflict");
306 dev->iobase = iobase;
308 printk("iobase=0x%lx\n", dev->iobase);
310 /*** make it a little quieter, exw, 8/29/06
311 for (i = 0; i < S526_NUM_PORTS; i++) {
312 printk("0x%02x: 0x%04x\n", ADDR_REG(s526_ports[i]), inw(ADDR_REG(s526_ports[i])));
317 * Initialize dev->board_name. Note that we can use the "thisboard"
318 * macro now, since we just initialized it in the last line.
320 dev->board_ptr = &s526_boards[0];
322 dev->board_name = thisboard->name;
325 * Allocate the private structure area. alloc_private() is a
326 * convenient macro defined in comedidev.h.
328 if (alloc_private(dev, sizeof(s526_private)) < 0)
332 * Allocate the subdevice structures. alloc_subdevice() is a
333 * convenient macro defined in comedidev.h.
335 dev->n_subdevices = 4;
336 if (alloc_subdevices(dev, dev->n_subdevices) < 0)
339 s = dev->subdevices + 0;
340 /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
341 s->type = COMEDI_SUBD_COUNTER;
342 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
343 /* KG: What does SDF_LSAMPL (see multiq3.c) mean? */
344 s->n_chan = thisboard->gpct_chans;
345 s->maxdata = 0x00ffffff; /* 24 bit counter */
346 s->insn_read = s526_gpct_rinsn;
347 s->insn_config = s526_gpct_insn_config;
348 s->insn_write = s526_gpct_winsn;
350 /* Command are not implemented yet, however they are necessary to
351 allocate the necessary memory for the comedi_async struct (used
352 to trigger the GPCT in case of pulsegenerator function */
353 //s->do_cmd = s526_gpct_cmd;
354 //s->do_cmdtest = s526_gpct_cmdtest;
355 //s->cancel = s526_gpct_cancel;
357 s = dev->subdevices + 1;
358 //dev->read_subdev=s;
359 /* analog input subdevice */
360 s->type = COMEDI_SUBD_AI;
361 /* we support differential */
362 s->subdev_flags = SDF_READABLE | SDF_DIFF;
363 /* channels 0 to 7 are the regular differential inputs */
364 /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
367 s->range_table = &range_bipolar10;
368 s->len_chanlist = 16; /* This is the maximum chanlist length that
369 the board can handle */
370 s->insn_read = s526_ai_rinsn;
371 s->insn_config = s526_ai_insn_config;
373 s = dev->subdevices + 2;
374 /* analog output subdevice */
375 s->type = COMEDI_SUBD_AO;
376 s->subdev_flags = SDF_WRITABLE;
379 s->range_table = &range_bipolar10;
380 s->insn_write = s526_ao_winsn;
381 s->insn_read = s526_ao_rinsn;
383 s = dev->subdevices + 3;
384 /* digital i/o subdevice */
385 if (thisboard->have_dio) {
386 s->type = COMEDI_SUBD_DIO;
387 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
390 s->range_table = &range_digital;
391 s->insn_bits = s526_dio_insn_bits;
392 s->insn_config = s526_dio_insn_config;
394 s->type = COMEDI_SUBD_UNUSED;
397 printk("attached\n");
402 // Example of Counter Application
403 //One-shot (software trigger)
404 cmReg.reg.coutSource = 0; // out RCAP
405 cmReg.reg.coutPolarity = 1; // Polarity inverted
406 cmReg.reg.autoLoadResetRcap = 1; // Auto load 0:disabled, 1:enabled
407 cmReg.reg.hwCtEnableSource = 3; // NOT RCAP
408 cmReg.reg.ctEnableCtrl = 2; // Hardware
409 cmReg.reg.clockSource = 2; // Internal
410 cmReg.reg.countDir = 1; // Down
411 cmReg.reg.countDirCtrl = 1; // Software
412 cmReg.reg.outputRegLatchCtrl = 0; // latch on read
413 cmReg.reg.preloadRegSel = 0; // PR0
414 cmReg.reg.reserved = 0;
416 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
418 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
419 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
421 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset the counter
422 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Load the counter from PR0
424 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset RCAP (fires one-shot)
428 // Set Counter Mode Register
429 cmReg.reg.coutSource = 0; // out RCAP
430 cmReg.reg.coutPolarity = 0; // Polarity inverted
431 cmReg.reg.autoLoadResetRcap = 0; // Auto load disabled
432 cmReg.reg.hwCtEnableSource = 2; // NOT RCAP
433 cmReg.reg.ctEnableCtrl = 1; // 1: Software, >1 : Hardware
434 cmReg.reg.clockSource = 3; // x4
435 cmReg.reg.countDir = 0; // up
436 cmReg.reg.countDirCtrl = 0; // quadrature
437 cmReg.reg.outputRegLatchCtrl = 0; // latch on read
438 cmReg.reg.preloadRegSel = 0; // PR0
439 cmReg.reg.reserved = 0;
442 printk("Mode reg=0x%04x, 0x%04lx\n", cmReg.value, ADDR_CHAN_REG(REG_C0M,
444 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
446 printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n)));
448 // Load the pre-laod register high word
449 // value = (sampl_t) (0x55);
450 // outw(value, ADDR_CHAN_REG(REG_C0H, n));
452 // Load the pre-laod register low word
453 // value = (sampl_t)(0xaa55);
454 // outw(value, ADDR_CHAN_REG(REG_C0L, n));
456 // Write the Counter Control Register
457 // outw(value, ADDR_CHAN_REG(REG_C0C, 0));
459 // Reset the counter if it is software preload
460 if (cmReg.reg.autoLoadResetRcap == 0) {
461 outw(0x8000, ADDR_CHAN_REG(REG_C0C, n)); // Reset the counter
462 outw(0x4000, ADDR_CHAN_REG(REG_C0C, n)); // Load the counter from PR0
465 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
467 printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n)));
470 printk("Current registres:\n");
472 for (i = 0; i < S526_NUM_PORTS; i++) {
473 printk("0x%02lx: 0x%04x\n", ADDR_REG(s526_ports[i]),
474 inw(ADDR_REG(s526_ports[i])));
480 * _detach is called to deconfigure a device. It should deallocate
482 * This function is also called when _attach() fails, so it should be
483 * careful not to release resources that were not necessarily
484 * allocated by _attach(). dev->private and dev->subdevices are
485 * deallocated automatically by the core.
487 static int s526_detach(comedi_device * dev)
489 printk("comedi%d: s526: remove\n", dev->minor);
492 release_region(dev->iobase, S526_IOSIZE);
497 static int s526_gpct_rinsn(comedi_device * dev, comedi_subdevice * s,
498 comedi_insn * insn, lsampl_t * data)
500 int i; // counts the Data
501 int counter_channel = CR_CHAN(insn->chanspec);
502 unsigned short datalow;
503 unsigned short datahigh;
507 printk("s526: INSN_READ: n should be > 0\n");
510 // Read the low word first
511 for (i = 0; i < insn->n; i++) {
512 datalow = inw(ADDR_CHAN_REG(REG_C0L, counter_channel));
513 datahigh = inw(ADDR_CHAN_REG(REG_C0H, counter_channel));
514 data[i] = (int)(datahigh & 0x00FF);
515 data[i] = (data[i] << 16) | (datalow & 0xFFFF);
516 // printk("s526 GPCT[%d]: %x(0x%04x, 0x%04x)\n", counter_channel, data[i], datahigh, datalow);
521 static int s526_gpct_insn_config(comedi_device * dev, comedi_subdevice * s,
522 comedi_insn * insn, lsampl_t * data)
524 int subdev_channel = CR_CHAN(insn->chanspec); // Unpack chanspec
529 // printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n", subdev_channel);
531 for (i = 0; i < MAX_GPCT_CONFIG_DATA; i++) {
532 devpriv->s526_gpct_config[subdev_channel].data[i] = data[i];
533 // printk("data[%d]=%x\n", i, data[i]);
536 // Check what type of Counter the user requested, data[0] contains
537 // the Application type
539 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
541 data[0]: Application Type
542 data[1]: Counter Mode Register Value
543 data[2]: Pre-load Register Value
544 data[3]: Conter Control Register
546 printk("s526: GPCT_INSN_CONFIG: Configuring Encoder\n");
547 devpriv->s526_gpct_config[subdev_channel].app =
551 // Example of Counter Application
552 //One-shot (software trigger)
553 cmReg.reg.coutSource = 0; // out RCAP
554 cmReg.reg.coutPolarity = 1; // Polarity inverted
555 cmReg.reg.autoLoadResetRcap = 0; // Auto load disabled
556 cmReg.reg.hwCtEnableSource = 3; // NOT RCAP
557 cmReg.reg.ctEnableCtrl = 2; // Hardware
558 cmReg.reg.clockSource = 2; // Internal
559 cmReg.reg.countDir = 1; // Down
560 cmReg.reg.countDirCtrl = 1; // Software
561 cmReg.reg.outputRegLatchCtrl = 0; // latch on read
562 cmReg.reg.preloadRegSel = 0; // PR0
563 cmReg.reg.reserved = 0;
565 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
567 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
568 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
570 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset the counter
571 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Load the counter from PR0
573 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset RCAP (fires one-shot)
578 // Set Counter Mode Register
579 cmReg.value = data[1] & 0xFFFF;
581 // printk("s526: Counter Mode register=%x\n", cmReg.value);
582 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
584 // Reset the counter if it is software preload
585 if (cmReg.reg.autoLoadResetRcap == 0) {
586 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset the counter
587 // outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Load the counter from PR0
590 cmReg.reg.countDirCtrl = 0; // 0 quadrature, 1 software control
592 // data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4
593 if (data[1] == GPCT_X2) {
594 cmReg.reg.clockSource = 1;
595 } else if (data[1] == GPCT_X4) {
596 cmReg.reg.clockSource = 2;
598 cmReg.reg.clockSource = 0;
601 // When to take into account the indexpulse:
602 if (data[2] == GPCT_IndexPhaseLowLow) {
603 } else if (data[2] == GPCT_IndexPhaseLowHigh) {
604 } else if (data[2] == GPCT_IndexPhaseHighLow) {
605 } else if (data[2] == GPCT_IndexPhaseHighHigh) {
607 // Take into account the index pulse?
608 if (data[3] == GPCT_RESET_COUNTER_ON_INDEX)
609 cmReg.reg.autoLoadResetRcap = 4; // Auto load with INDEX^
611 // Set Counter Mode Register
612 cmReg.value = (sampl_t) (data[1] & 0xFFFF);
613 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
615 // Load the pre-laod register high word
616 value = (sampl_t) ((data[2] >> 16) & 0xFFFF);
617 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
619 // Load the pre-laod register low word
620 value = (sampl_t) (data[2] & 0xFFFF);
621 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
623 // Write the Counter Control Register
625 value = (sampl_t) (data[3] & 0xFFFF);
626 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
628 // Reset the counter if it is software preload
629 if (cmReg.reg.autoLoadResetRcap == 0) {
630 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset the counter
631 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Load the counter from PR0
636 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
638 data[0]: Application Type
639 data[1]: Counter Mode Register Value
640 data[2]: Pre-load Register 0 Value
641 data[3]: Pre-load Register 1 Value
642 data[4]: Conter Control Register
644 printk("s526: GPCT_INSN_CONFIG: Configuring SPG\n");
645 devpriv->s526_gpct_config[subdev_channel].app =
646 SinglePulseGeneration;
648 // Set Counter Mode Register
649 cmReg.value = (sampl_t) (data[1] & 0xFFFF);
650 cmReg.reg.preloadRegSel = 0; // PR0
651 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
653 // Load the pre-laod register 0 high word
654 value = (sampl_t) ((data[2] >> 16) & 0xFFFF);
655 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
657 // Load the pre-laod register 0 low word
658 value = (sampl_t) (data[2] & 0xFFFF);
659 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
661 // Set Counter Mode Register
662 cmReg.value = (sampl_t) (data[1] & 0xFFFF);
663 cmReg.reg.preloadRegSel = 1; // PR1
664 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
666 // Load the pre-laod register 1 high word
667 value = (sampl_t) ((data[3] >> 16) & 0xFFFF);
668 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
670 // Load the pre-laod register 1 low word
671 value = (sampl_t) (data[3] & 0xFFFF);
672 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
674 // Write the Counter Control Register
676 value = (sampl_t) (data[4] & 0xFFFF);
677 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
681 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
683 data[0]: Application Type
684 data[1]: Counter Mode Register Value
685 data[2]: Pre-load Register 0 Value
686 data[3]: Pre-load Register 1 Value
687 data[4]: Conter Control Register
689 printk("s526: GPCT_INSN_CONFIG: Configuring PTG\n");
690 devpriv->s526_gpct_config[subdev_channel].app =
691 PulseTrainGeneration;
693 // Set Counter Mode Register
694 cmReg.value = (sampl_t) (data[1] & 0xFFFF);
695 cmReg.reg.preloadRegSel = 0; // PR0
696 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
698 // Load the pre-laod register 0 high word
699 value = (sampl_t) ((data[2] >> 16) & 0xFFFF);
700 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
702 // Load the pre-laod register 0 low word
703 value = (sampl_t) (data[2] & 0xFFFF);
704 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
706 // Set Counter Mode Register
707 cmReg.value = (sampl_t) (data[1] & 0xFFFF);
708 cmReg.reg.preloadRegSel = 1; // PR1
709 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
711 // Load the pre-laod register 1 high word
712 value = (sampl_t) ((data[3] >> 16) & 0xFFFF);
713 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
715 // Load the pre-laod register 1 low word
716 value = (sampl_t) (data[3] & 0xFFFF);
717 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
719 // Write the Counter Control Register
721 value = (sampl_t) (data[4] & 0xFFFF);
722 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
727 printk("s526: unsupported GPCT_insn_config\n");
735 static int s526_gpct_winsn(comedi_device * dev, comedi_subdevice * s,
736 comedi_insn * insn, lsampl_t * data)
738 int subdev_channel = CR_CHAN(insn->chanspec); // Unpack chanspec
742 printk("s526: GPCT_INSN_WRITE on channel %d\n", subdev_channel);
743 cmReg.value = inw(ADDR_CHAN_REG(REG_C0M, subdev_channel));
744 printk("s526: Counter Mode Register: %x\n", cmReg.value);
746 printk("S525: INSN_WRITE: Can't handle data length %u\n",
750 // Check what Application of Counter this channel is configured for
751 switch (devpriv->s526_gpct_config[subdev_channel].app) {
752 case PositionMeasurement:
753 printk("S526: INSN_WRITE: PM\n");
754 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
756 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
759 case SinglePulseGeneration:
760 printk("S526: INSN_WRITE: SPG\n");
761 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
763 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
766 case PulseTrainGeneration:
767 /* data[0] contains the PULSE_WIDTH
768 data[1] contains the PULSE_PERIOD
769 @pre PULSE_PERIOD > PULSE_WIDTH > 0
770 The above periods must be expressed as a multiple of the
771 pulse frequency on the selected source
773 printk("S526: INSN_WRITE: PTG\n");
775 printk("s526: INSN_WRITE: PTG: Problem with data length -> %u\n",
778 } else if ((data[1] > data[0]) && (data[0] > 0)) {
779 (devpriv->s526_gpct_config[subdev_channel]).data[0] =
781 (devpriv->s526_gpct_config[subdev_channel]).data[1] =
784 printk("s526: INSN_WRITE: PTG: Problem with Pulse params -> %d %d\n",
789 value = (sampl_t) ((*data >> 16) & 0xFFFF);
790 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
791 value = (sampl_t) (*data & 0xFFFF);
792 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
794 default: // Impossible
795 printk("s526: INSN_WRITE: Functionality %d not implemented yet\n", devpriv->s526_gpct_config[subdev_channel].app);
799 // return the number of samples written
803 #define ISR_ADC_DONE 0x4
804 static int s526_ai_insn_config(comedi_device * dev, comedi_subdevice * s,
805 comedi_insn * insn, lsampl_t * data)
807 int result = -EINVAL;
814 /* data[0] : channels was set in relevant bits.
817 /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
818 * enable channels here. The channel should be enabled in the
819 * INSN_READ handler. */
821 // Enable ADC interrupt
822 outw(ISR_ADC_DONE, ADDR_REG(REG_IER));
823 // printk("s526: ADC current value: 0x%04x\n", inw(ADDR_REG(REG_ADC)));
824 devpriv->s526_ai_config = (data[0] & 0x3FF) << 5;
826 devpriv->s526_ai_config |= 0x8000; //set the delay
828 devpriv->s526_ai_config |= 0x0001; // ADC start bit.
834 * "instructions" read/write data in "one-shot" or "software-triggered"
837 static int s526_ai_rinsn(comedi_device * dev, comedi_subdevice * s,
838 comedi_insn * insn, lsampl_t * data)
841 int chan = CR_CHAN(insn->chanspec);
842 unsigned short value;
846 /* Set configured delay, enable channel for this channel only,
847 * select "ADC read" channel, set "ADC start" bit. */
848 value = (devpriv->s526_ai_config & 0x8000) |
849 ((1 << 5) << chan) | (chan << 1) | 0x0001;
851 /* convert n samples */
852 for (n = 0; n < insn->n; n++) {
853 /* trigger conversion */
854 outw(value, ADDR_REG(REG_ADC));
855 // printk("s526: Wrote 0x%04x to ADC\n", value);
856 // printk("s526: ADC reg=0x%04x\n", inw(ADDR_REG(REG_ADC)));
859 /* wait for conversion to end */
860 for (i = 0; i < TIMEOUT; i++) {
861 status = inw(ADDR_REG(REG_ISR));
862 if (status & ISR_ADC_DONE) {
863 outw(ISR_ADC_DONE, ADDR_REG(REG_ISR));
868 /* rt_printk() should be used instead of printk()
869 * whenever the code can be called from real-time. */
870 rt_printk("s526: ADC(0x%04x) timeout\n",
871 inw(ADDR_REG(REG_ISR)));
876 d = inw(ADDR_REG(REG_ADD));
877 // printk("AI[%d]=0x%04x\n", n, (unsigned short)(d & 0xFFFF));
880 data[n] = d ^ 0x8000;
883 /* return the number of samples read/written */
887 static int s526_ao_winsn(comedi_device * dev, comedi_subdevice * s,
888 comedi_insn * insn, lsampl_t * data)
891 int chan = CR_CHAN(insn->chanspec);
894 // printk("s526_ao_winsn\n");
896 // outw(val, dev->iobase + REG_DAC);
897 outw(val, ADDR_REG(REG_DAC));
899 /* Writing a list of values to an AO channel is probably not
900 * very useful, but that's how the interface is defined. */
901 for (i = 0; i < insn->n; i++) {
902 /* a typical programming sequence */
903 // outw(data[i], dev->iobase + REG_ADD); // write the data to preload register
904 outw(data[i], ADDR_REG(REG_ADD)); // write the data to preload register
905 devpriv->ao_readback[chan] = data[i];
906 // outw(val + 1, dev->iobase + REG_DAC); // starts the D/A conversion.
907 outw(val + 1, ADDR_REG(REG_DAC)); // starts the D/A conversion.
910 /* return the number of samples read/written */
914 /* AO subdevices should have a read insn as well as a write insn.
915 * Usually this means copying a value stored in devpriv. */
916 static int s526_ao_rinsn(comedi_device * dev, comedi_subdevice * s,
917 comedi_insn * insn, lsampl_t * data)
920 int chan = CR_CHAN(insn->chanspec);
922 for (i = 0; i < insn->n; i++)
923 data[i] = devpriv->ao_readback[chan];
928 /* DIO devices are slightly special. Although it is possible to
929 * implement the insn_read/insn_write interface, it is much more
930 * useful to applications if you implement the insn_bits interface.
931 * This allows packed reading/writing of the DIO channels. The
932 * comedi core can convert between insn_bits and insn_read/write */
933 static int s526_dio_insn_bits(comedi_device * dev, comedi_subdevice * s,
934 comedi_insn * insn, lsampl_t * data)
939 /* The insn data is a mask in data[0] and the new data
940 * in data[1], each channel cooresponding to a bit. */
942 s->state &= ~data[0];
943 s->state |= data[0] & data[1];
944 /* Write out the new digital output lines */
945 outw(s->state, ADDR_REG(REG_DIO));
948 /* on return, data[1] contains the value of the digital
949 * input and output lines. */
950 data[1] = inw(ADDR_REG(REG_DIO)) & 0xFF; // low 8 bits are the data
951 /* or we could just return the software copy of the output values if
952 * it was a purely digital output subdevice */
953 //data[1]=s->state & 0xFF;
958 static int s526_dio_insn_config(comedi_device * dev, comedi_subdevice * s,
959 comedi_insn * insn, lsampl_t * data)
961 int chan = CR_CHAN(insn->chanspec);
964 printk("S526 DIO insn_config\n");
966 /* The input or output configuration of each digital line is
967 * configured by a special insn_config instruction. chanspec
968 * contains the channel to be changed, and data[0] contains the
969 * value COMEDI_INPUT or COMEDI_OUTPUT. */
972 mask = 0xF << (group << 2);
974 case INSN_CONFIG_DIO_OUTPUT:
975 s->state |= 1 << (group + 10); // bit 10/11 set the group 1/2's mode
978 case INSN_CONFIG_DIO_INPUT:
979 s->state &= ~(1 << (group + 10));// 1 is output, 0 is input.
982 case INSN_CONFIG_DIO_QUERY:
983 data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT;
988 outw(s->state, ADDR_REG(REG_DIO));
994 * A convenient macro that defines init_module() and cleanup_module(),
997 COMEDI_INITCLEANUP(driver_s526);