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 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>
49 #define S526_START_AI_CONV 0
50 #define S526_AI_READ 0
54 #define S526_IOSIZE 0x40
55 #define S526_NUM_PORTS 27
120 unsigned short coutSource: 1;
121 unsigned short coutPolarity: 1;
122 unsigned short autoLoadResetRcap: 3;
123 unsigned short hwCtEnableSource: 2;
124 unsigned short ctEnableCtrl: 2;
125 unsigned short clockSource: 2;
126 unsigned short countDir: 1;
127 unsigned short countDirCtrl: 1;
128 unsigned short outputRegLatchCtrl: 1;
129 unsigned short preloadRegSel: 1;
130 unsigned short reserved: 1;
131 } counter_mode_register_t;
134 counter_mode_register_t reg;
135 unsigned short value;
138 #define MAX_GPCT_CONFIG_DATA 6
140 /* Different Application Classes for GPCT Subdevices */
141 /* The list is not exhaustive and needs discussion! */
144 CountingAndTimeMeasurement,
145 SinglePulseGeneration,
146 PulseTrainGeneration,
149 } S526_GPCT_APP_CLASS;
152 /* Config struct for different GPCT subdevice Application Classes and
155 typedef struct s526GPCTConfig
157 S526_GPCT_APP_CLASS app;
158 int data[MAX_GPCT_CONFIG_DATA];
159 } s526_gpct_config_t;
161 static s526_gpct_config_t s526_gpct_config[4];
162 static unsigned short s526_ai_config = 0;
165 * Board descriptions for two imaginary boards. Describing the
166 * boards in this way is optional, and completely driver-dependent.
167 * Some drivers use arrays such as this, other do not.
169 typedef struct s526_board_struct{
180 static const s526_board s526_boards[] = {
193 #define ADDR_REG(reg) (int)((int)((s526_board *)dev->iobase) + reg)
194 #define ADDR_CHAN_REG(reg, chan) (int)((int)((s526_board *)dev->iobase) + reg + chan * 8)
197 * Useful for shorthand access to the particular board structure
199 #define thisboard ((const s526_board *)dev->board_ptr)
201 /* this structure is for data unique to this hardware driver. If
202 several hardware drivers keep similar information in this structure,
203 feel free to suggest moving the variable to the comedi_device struct. */
207 /* would be useful for a PCI device */
208 struct pci_dev *pci_dev;
210 /* Used for AO readback */
211 lsampl_t ao_readback[2];
214 * most drivers define the following macro to make it easy to
215 * access the private structure.
217 #define devpriv ((s526_private *)dev->private)
220 * The comedi_driver structure tells the Comedi core module
221 * which functions to call to configure/deconfigure (attach/detach)
222 * the board, and also about the kernel module that contains
225 static int s526_attach(comedi_device *dev,comedi_devconfig *it);
226 static int s526_detach(comedi_device *dev);
227 static comedi_driver driver_s526={
232 /* It is not necessary to implement the following members if you are
233 * writing a driver for a ISA PnP or PCI card */
234 /* Most drivers will support multiple types of boards by
235 * having an array of board structures. These were defined
236 * in s526_boards[] above. Note that the element 'name'
237 * was first in the structure -- Comedi uses this fact to
238 * extract the name of the board without knowing any details
239 * about the structure except for its length.
240 * When a device is attached (by comedi_config), the name
241 * of the device is given to Comedi, and Comedi tries to
242 * match it by going through the list of board names. If
243 * there is a match, the address of the pointer is put
244 * into dev->board_ptr and driver->attach() is called.
246 * Note that these are not necessary if you can determine
247 * the type of board in software. ISA PnP, PCI, and PCMCIA
248 * devices are such boards.
250 board_name: &s526_boards[0].name,
251 offset: sizeof(s526_board),
252 num_names: sizeof(s526_boards) / sizeof(s526_board),
255 static int s526_gpct_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
256 static int s526_gpct_insn_config(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
257 static int s526_gpct_winsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
258 static int s526_ai_insn_config(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
259 static int s526_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
260 static int s526_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
261 static int s526_ao_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
262 static int s526_dio_insn_bits(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data);
263 static int s526_dio_insn_config(comedi_device *dev,comedi_subdevice *s, comedi_insn *insn,lsampl_t *data);
266 * Attach is called by the Comedi core to configure the driver
267 * for a particular board. If you specified a board_name array
268 * in the driver structure, dev->board_ptr contains that
271 static int s526_attach(comedi_device *dev,comedi_devconfig *it)
277 // int subdev_channel = 0;
279 printk("comedi%d: s526: ", dev->minor);
281 iobase=it->options[0];
282 if(!iobase || !request_region(iobase, S526_IOSIZE, thisboard->name)){
283 comedi_error(dev,"I/O port conflict");
288 printk("iobase=0x%lux\n", dev->iobase);
290 /*** make it a little quieter, exw, 8/29/06
291 for (i = 0; i < S526_NUM_PORTS; i++) {
292 printk("0x%02x: 0x%04x\n", ADDR_REG(s526_ports[i]), inw(ADDR_REG(s526_ports[i])));
297 * Initialize dev->board_name. Note that we can use the "thisboard"
298 * macro now, since we just initialized it in the last line.
300 dev->board_ptr = &s526_boards[0];
302 dev->board_name = thisboard->name;
305 * Allocate the private structure area. alloc_private() is a
306 * convenient macro defined in comedidev.h.
308 if(alloc_private(dev, sizeof(s526_private)) < 0)
312 * Allocate the subdevice structures. alloc_subdevice() is a
313 * convenient macro defined in comedidev.h.
315 dev->n_subdevices = 4;
316 if(alloc_subdevices(dev, dev->n_subdevices) < 0)
320 /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
321 s->type = COMEDI_SUBD_COUNTER;
322 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
323 /* KG: What does SDF_LSAMPL (see multiq3.c) mean? */
324 s->n_chan = thisboard->gpct_chans;
325 s->maxdata = 0x00ffffff; /* 24 bit counter */
326 s->insn_read = s526_gpct_rinsn;
327 s->insn_config = s526_gpct_insn_config;
328 s->insn_write = s526_gpct_winsn;
330 /* Command are not implemented yet, however they are necessary to
331 allocate the necessary memory for the comedi_async struct (used
332 to trigger the GPCT in case of pulsegenerator function */
333 //s->do_cmd = s526_gpct_cmd;
334 //s->do_cmdtest = s526_gpct_cmdtest;
335 //s->cancel = s526_gpct_cancel;
338 //dev->read_subdev=s;
339 /* analog input subdevice */
340 s->type=COMEDI_SUBD_AI;
341 /* we support single-ended (ground) and differential */
342 s->subdev_flags=SDF_READABLE|SDF_DIFF;
345 s->range_table=&range_bipolar10;
346 s->len_chanlist=16; /* This is the maximum chanlist length that
347 the board can handle */
348 s->insn_read = s526_ai_rinsn;
349 s->insn_config = s526_ai_insn_config;
352 /* analog output subdevice */
353 s->type=COMEDI_SUBD_AO;
354 s->subdev_flags=SDF_WRITABLE;
357 s->range_table=&range_bipolar10;
358 s->insn_write = s526_ao_winsn;
359 s->insn_read = s526_ao_rinsn;
362 /* digital i/o subdevice */
363 if(thisboard->have_dio){
364 s->type=COMEDI_SUBD_DIO;
365 s->subdev_flags=SDF_READABLE|SDF_WRITABLE;
368 s->range_table=&range_digital;
369 s->insn_bits = s526_dio_insn_bits;
370 s->insn_config = s526_dio_insn_config;
372 s->type = COMEDI_SUBD_UNUSED;
375 printk("attached\n");
380 // Example of Counter Application
381 //One-shot (software trigger)
382 cmReg.reg.coutSource = 0; // out RCAP
383 cmReg.reg.coutPolarity = 1; // Polarity inverted
384 cmReg.reg.autoLoadResetRcap = 1; // Auto load 0:disabled, 1:enabled
385 cmReg.reg.hwCtEnableSource = 3; // NOT RCAP
386 cmReg.reg.ctEnableCtrl = 2; // Hardware
387 cmReg.reg.clockSource = 2; // Internal
388 cmReg.reg.countDir = 1; // Down
389 cmReg.reg.countDirCtrl = 1; // Software
390 cmReg.reg.outputRegLatchCtrl = 0; // latch on read
391 cmReg.reg.preloadRegSel = 0; // PR0
392 cmReg.reg.reserved = 0;
394 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
396 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
397 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
399 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset the counter
400 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Load the counter from PR0
402 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset RCAP (fires one-shot)
407 // Set Counter Mode Register
408 cmReg.reg.coutSource = 0; // out RCAP
409 cmReg.reg.coutPolarity = 0; // Polarity inverted
410 cmReg.reg.autoLoadResetRcap = 0; // Auto load disabled
411 cmReg.reg.hwCtEnableSource = 2; // NOT RCAP
412 cmReg.reg.ctEnableCtrl = 1; // 1: Software, >1 : Hardware
413 cmReg.reg.clockSource = 3; // x4
414 cmReg.reg.countDir = 0; // up
415 cmReg.reg.countDirCtrl = 0; // quadrature
416 cmReg.reg.outputRegLatchCtrl = 0; // latch on read
417 cmReg.reg.preloadRegSel = 0; // PR0
418 cmReg.reg.reserved = 0;
421 printk("Mode reg=0x%04x, 0x%04x\n", cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
422 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
424 printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n)));
426 // Load the pre-laod register high word
427 // value = (sampl_t) (0x55);
428 // outw(value, ADDR_CHAN_REG(REG_C0H, n));
430 // Load the pre-laod register low word
431 // value = (sampl_t)(0xaa55);
432 // outw(value, ADDR_CHAN_REG(REG_C0L, n));
434 // Write the Counter Control Register
435 // outw(value, ADDR_CHAN_REG(REG_C0C, 0));
438 // Reset the counter if it is software preload
439 if (cmReg.reg.autoLoadResetRcap == 0) {
440 outw(0x8000, ADDR_CHAN_REG(REG_C0C, n)); // Reset the counter
441 outw(0x4000, ADDR_CHAN_REG(REG_C0C, n)); // Load the counter from PR0
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)));
449 printk("Current registres:\n");
451 for (i = 0; i < S526_NUM_PORTS; i++) {
452 printk("0x%02x: 0x%04x\n", ADDR_REG(s526_ports[i]), inw(ADDR_REG(s526_ports[i])));
459 * _detach is called to deconfigure a device. It should deallocate
461 * This function is also called when _attach() fails, so it should be
462 * careful not to release resources that were not necessarily
463 * allocated by _attach(). dev->private and dev->subdevices are
464 * deallocated automatically by the core.
466 static int s526_detach(comedi_device *dev)
468 printk("comedi%d: s526: remove\n", dev->minor);
471 release_region(dev->iobase, S526_IOSIZE);
476 static int s526_gpct_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
478 int i; // counts the Data
479 int counter_channel = CR_CHAN(insn->chanspec);
480 unsigned short datalow;
481 unsigned short datahigh;
486 printk("s526: INSN_READ: n should be > 0\n");
489 // Read the low word first
490 for ( i=0 ; i < insn->n ; i++ )
492 datalow = inw(ADDR_CHAN_REG(REG_C0L, counter_channel));
493 datahigh = inw(ADDR_CHAN_REG(REG_C0H, counter_channel));
494 data[i] = (int)(datahigh & 0x00FF);
495 data[i] = (data[i] << 16) | (datalow & 0xFFFF);
496 // printk("s526 GPCT[%d]: %x(0x%04x, 0x%04x)\n", counter_channel, data[i], datahigh, datalow);
501 static int s526_gpct_insn_config(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
503 int subdev_channel = CR_CHAN(insn->chanspec);// Unpack chanspec
507 // printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n", subdev_channel);
509 for(i = 0; i < MAX_GPCT_CONFIG_DATA; i++) {
510 s526_gpct_config[subdev_channel].data[i] = insn->data[i];
511 // printk("data[%d]=%x\n", i, insn->data[i]);
514 // Check what type of Counter the user requested, data[0] contains
515 // the Application type
516 switch(insn->data[0])
518 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
520 data[0]: Application Type
521 data[1]: Counter Mode Register Value
522 data[2]: Pre-load Register Value
523 data[3]: Conter Control Register
525 printk("s526: GPCT_INSN_CONFIG: Configuring Encoder\n");
526 s526_gpct_config[subdev_channel].app = PositionMeasurement;
529 // Example of Counter Application
530 //One-shot (software trigger)
531 cmReg.reg.coutSource = 0; // out RCAP
532 cmReg.reg.coutPolarity = 1; // Polarity inverted
533 cmReg.reg.autoLoadResetRcap = 0; // Auto load disabled
534 cmReg.reg.hwCtEnableSource = 3; // NOT RCAP
535 cmReg.reg.ctEnableCtrl = 2; // Hardware
536 cmReg.reg.clockSource = 2; // Internal
537 cmReg.reg.countDir = 1; // Down
538 cmReg.reg.countDirCtrl = 1; // Software
539 cmReg.reg.outputRegLatchCtrl = 0; // latch on read
540 cmReg.reg.preloadRegSel = 0; // PR0
541 cmReg.reg.reserved = 0;
543 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
545 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
546 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
548 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset the counter
549 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Load the counter from PR0
551 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset RCAP (fires one-shot)
556 // Set Counter Mode Register
557 cmReg.reg.coutSource = 0; // out RCAP
558 cmReg.reg.coutPolarity = 0; // Polarity inverted
559 cmReg.reg.autoLoadResetRcap = 0; // Auto load disabled
560 cmReg.reg.hwCtEnableSource = 2; // NOT RCAP
561 cmReg.reg.ctEnableCtrl = 1; // 1: Software, >1 : Hardware
562 cmReg.reg.clockSource = 3; // x4
563 cmReg.reg.countDir = 0; // up
564 cmReg.reg.countDirCtrl = 0; // quadrature
565 cmReg.reg.outputRegLatchCtrl = 0; // latch on read
566 cmReg.reg.preloadRegSel = 0; // PR0
567 cmReg.reg.reserved = 0;
569 // Set Counter Mode Register
570 // printk("s526: Counter Mode register=%x\n", cmReg.value);
571 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
573 // Reset the counter if it is software preload
574 if (cmReg.reg.autoLoadResetRcap == 0) {
575 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset the counter
576 // outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Load the counter from PR0
579 cmReg.reg.countDirCtrl = 0; // 0 quadrature, 1 software control
581 // data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4
582 if(insn->data[1] == GPCT_X2) {
583 cmReg.reg.clockSource = 1;
584 }else if(insn->data[1] == GPCT_X4) {
585 cmReg.reg.clockSource = 2;
587 cmReg.reg.clockSource = 0;
590 // When to take into account the indexpulse:
591 if(insn->data[2] == GPCT_IndexPhaseLowLow) {
592 }else if(insn->data[2] == GPCT_IndexPhaseLowHigh) {
593 }else if(insn->data[2] == GPCT_IndexPhaseHighLow) {
594 }else if(insn->data[2] == GPCT_IndexPhaseHighHigh) {
597 // Take into account the index pulse?
598 if(insn->data[3] == GPCT_RESET_COUNTER_ON_INDEX)
599 cmReg.reg.autoLoadResetRcap = 4; // Auto load with INDEX^
602 // Set Counter Mode Register
603 cmReg.value = (sampl_t)(insn->data[1] & 0xFFFF);
604 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
606 // Load the pre-laod register high word
607 value = (sampl_t) ((insn->data[2] >> 16) & 0xFFFF);
608 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
610 // Load the pre-laod register low word
611 value = (sampl_t)(insn->data[2] & 0xFFFF);
612 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
614 // Write the Counter Control Register
615 if (insn->data[3] != 0) {
616 value = (sampl_t)(insn->data[3] & 0xFFFF);
617 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
620 // Reset the counter if it is software preload
621 if (cmReg.reg.autoLoadResetRcap == 0) {
622 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset the counter
623 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Load the counter from PR0
628 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
630 data[0]: Application Type
631 data[1]: Counter Mode Register Value
632 data[2]: Pre-load Register 0 Value
633 data[3]: Pre-load Register 1 Value
634 data[4]: Conter Control Register
636 printk("s526: GPCT_INSN_CONFIG: Configuring SPG\n");
637 s526_gpct_config[subdev_channel].app = SinglePulseGeneration;
639 // Set Counter Mode Register
640 cmReg.value = (sampl_t)(insn->data[1] & 0xFFFF);
641 cmReg.reg.preloadRegSel = 0; // PR0
642 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
644 // Load the pre-laod register 0 high word
645 value = (sampl_t) ((insn->data[2] >> 16) & 0xFFFF);
646 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
648 // Load the pre-laod register 0 low word
649 value = (sampl_t)(insn->data[2] & 0xFFFF);
650 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
652 // Set Counter Mode Register
653 cmReg.value = (sampl_t)(insn->data[1] & 0xFFFF);
654 cmReg.reg.preloadRegSel = 1; // PR1
655 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
657 // Load the pre-laod register 1 high word
658 value = (sampl_t) ((insn->data[3] >> 16) & 0xFFFF);
659 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
661 // Load the pre-laod register 1 low word
662 value = (sampl_t)(insn->data[3] & 0xFFFF);
663 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
665 // Write the Counter Control Register
666 if (insn->data[3] != 0) {
667 value = (sampl_t)(insn->data[3] & 0xFFFF);
668 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
672 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
674 data[0]: Application Type
675 data[1]: Counter Mode Register Value
676 data[2]: Pre-load Register 0 Value
677 data[3]: Pre-load Register 1 Value
678 data[4]: Conter Control Register
680 printk("s526: GPCT_INSN_CONFIG: Configuring PTG\n");
681 s526_gpct_config[subdev_channel].app = PulseTrainGeneration;
683 // Set Counter Mode Register
684 cmReg.value = (sampl_t)(insn->data[1] & 0xFFFF);
685 cmReg.reg.preloadRegSel = 0; // PR0
686 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
688 // Load the pre-laod register 0 high word
689 value = (sampl_t) ((insn->data[2] >> 16) & 0xFFFF);
690 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
692 // Load the pre-laod register 0 low word
693 value = (sampl_t)(insn->data[2] & 0xFFFF);
694 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
696 // Set Counter Mode Register
697 cmReg.value = (sampl_t)(insn->data[1] & 0xFFFF);
698 cmReg.reg.preloadRegSel = 1; // PR1
699 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
701 // Load the pre-laod register 1 high word
702 value = (sampl_t) ((insn->data[3] >> 16) & 0xFFFF);
703 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
705 // Load the pre-laod register 1 low word
706 value = (sampl_t)(insn->data[3] & 0xFFFF);
707 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
709 // Write the Counter Control Register
710 if (insn->data[3] != 0) {
711 value = (sampl_t)(insn->data[3] & 0xFFFF);
712 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
717 printk("s526: unsupported GPCT_insn_config\n");
725 static int s526_gpct_winsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
727 int subdev_channel = CR_CHAN(insn->chanspec);// Unpack chanspec
730 printk("s526: GPCT_INSN_WRITE on channel %d\n", subdev_channel);
731 cmReg.value = inw(ADDR_CHAN_REG(REG_C0M, subdev_channel));
732 printk("s526: Counter Mode Register: %x\n", cmReg.value);
733 // Check what Application of Counter this channel is configured for
734 switch(s526_gpct_config[subdev_channel].app)
736 case PositionMeasurement:
737 printk("S526: INSN_WRITE: PM\n");
738 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H, subdev_channel));
739 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
742 case SinglePulseGeneration:
743 printk("S526: INSN_WRITE: SPG\n");
744 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H, subdev_channel));
745 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
748 case PulseTrainGeneration:
749 /* data[0] contains the PULSE_WIDTH
750 data[1] contains the PULSE_PERIOD
751 @pre PULSE_PERIOD > PULSE_WIDTH > 0
752 The above periods must be expressed as a multiple of the
753 pulse frequency on the selected source
755 printk("S526: INSN_WRITE: PTG\n");
756 if ( (insn->data[1] > insn->data[0]) && (insn->data[0] > 0 ) )
758 (s526_gpct_config[subdev_channel]).data[0] = insn->data[0];
759 (s526_gpct_config[subdev_channel]).data[1] = insn->data[1];
763 printk("%d \t %d\n",insn->data[1],insn->data[2]);
764 printk("s526: INSN_WRITE: PTG: Problem with Pulse params\n");
768 value = (sampl_t)((*data >> 16) & 0xFFFF);
769 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
770 value = (sampl_t)(*data & 0xFFFF);
771 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
773 default: // Impossible
774 printk("s526: INSN_WRITE: Functionality %d not implemented yet\n",
775 s526_gpct_config[subdev_channel].app);
779 // return the number of samples written
783 #define ISR_ADC_DONE 0x4
784 static int s526_ai_insn_config(comedi_device *dev,comedi_subdevice *s,
785 comedi_insn *insn, lsampl_t *data)
787 int result = -EINVAL;
789 if (insn->n < 1) return result;
793 /* data[0] : channels was set in relevant bits.
797 // Enable ADC interrupt
798 outw(ISR_ADC_DONE, ADDR_REG(REG_IER));
799 // printk("s526: ADC current value: 0x%04x\n", inw(ADDR_REG(REG_ADC)));
800 s526_ai_config = (data[0] & 0x3FF) << 5;
802 s526_ai_config |= 0x8000; //set the delay
804 s526_ai_config |= 0x0001; // ADC start bit.
811 * "instructions" read/write data in "one-shot" or "software-triggered"
814 static int s526_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
817 int chan = CR_CHAN(insn->chanspec);
818 unsigned short value;
822 value = s526_ai_config | (chan << 1);
823 // outw(value, ADDR_REG(REG_ADC)); do it with ADC start
826 /* convert n samples */
827 for(n=0; n<insn->n; n++){
828 /* trigger conversion */
829 value |= 0x0001; // ADC start
830 outw(value, ADDR_REG(REG_ADC));
831 // printk("s526: Wrote 0x%04x to ADC\n", value);
832 // printk("s526: ADC reg=0x%04x\n", inw(ADDR_REG(REG_ADC)));
835 /* wait for conversion to end */
836 for(i=0;i<TIMEOUT;i++){
837 status = inw(ADDR_REG(REG_ISR));
838 if (status & ISR_ADC_DONE) {
839 outw(ISR_ADC_DONE, ADDR_REG(REG_ISR));
844 /* rt_printk() should be used instead of printk()
845 * whenever the code can be called from real-time. */
846 rt_printk("s526: ADC(0x%04x) timeout\n", inw(ADDR_REG(REG_ISR)));
851 d = inw(ADDR_REG(REG_ADD));
852 // printk("AI[%d]=0x%04x\n", n, (unsigned short)(d & 0xFFFF));
857 /* return the number of samples read/written */
862 static int s526_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
865 int chan = CR_CHAN(insn->chanspec);
868 // printk("s526_ao_winsn\n");
870 // outw(val, dev->iobase + REG_DAC);
871 outw(val, ADDR_REG(REG_DAC));
873 /* Writing a list of values to an AO channel is probably not
874 * very useful, but that's how the interface is defined. */
875 for(i=0;i<insn->n;i++){
876 /* a typical programming sequence */
877 // outw(data[i], dev->iobase + REG_ADD); // write the data to preload register
878 outw(data[i], ADDR_REG(REG_ADD)); // write the data to preload register
879 devpriv->ao_readback[chan] = data[i];
880 // outw(val + 1, dev->iobase + REG_DAC); // starts the D/A conversion.
881 outw(val + 1, ADDR_REG(REG_DAC)); // starts the D/A conversion.
884 /* return the number of samples read/written */
888 /* AO subdevices should have a read insn as well as a write insn.
889 * Usually this means copying a value stored in devpriv. */
890 static int s526_ao_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
893 int chan = CR_CHAN(insn->chanspec);
895 for(i=0;i<insn->n;i++)
896 data[i] = devpriv->ao_readback[chan];
901 /* DIO devices are slightly special. Although it is possible to
902 * implement the insn_read/insn_write interface, it is much more
903 * useful to applications if you implement the insn_bits interface.
904 * This allows packed reading/writing of the DIO channels. The
905 * comedi core can convert between insn_bits and insn_read/write */
906 static int s526_dio_insn_bits(comedi_device *dev,comedi_subdevice *s,
907 comedi_insn *insn,lsampl_t *data)
909 if(insn->n!=2)return -EINVAL;
911 /* The insn data is a mask in data[0] and the new data
912 * in data[1], each channel cooresponding to a bit. */
914 s->state &= ~data[0];
915 s->state |= data[0]&data[1];
916 /* Write out the new digital output lines */
917 outw(s->state, ADDR_REG(REG_DIO));
920 /* on return, data[1] contains the value of the digital
921 * input and output lines. */
922 data[1]=inw(ADDR_REG(REG_DIO)) & 0xFF; // low 8 bits are the data
923 /* or we could just return the software copy of the output values if
924 * it was a purely digital output subdevice */
930 static int s526_dio_insn_config(comedi_device *dev,comedi_subdevice *s,
931 comedi_insn *insn,lsampl_t *data)
933 int chan=CR_CHAN(insn->chanspec);
936 printk("S526 DIO insn_config\n");
938 if(insn->n!=1)return -EINVAL;
940 value = inw(ADDR_REG(REG_DIO));
942 /* The input or output configuration of each digital line is
943 * configured by a special insn_config instruction. chanspec
944 * contains the channel to be changed, and data[0] contains the
945 * value COMEDI_INPUT or COMEDI_OUTPUT. */
947 if(data[0]==COMEDI_OUTPUT){
948 value |= 1 << (chan + 10); // bit 10/11 set the group 1/2's mode
949 s->io_bits |= (0xF << chan);
951 value &= ~(1 << (chan + 10)); // 1 is output, 0 is input.
952 s->io_bits &= ~(0xF << chan);
954 outw(value, ADDR_REG(REG_DIO));
960 * A convenient macro that defines init_module() and cleanup_module(),
963 COMEDI_INITCLEANUP(driver_s526);