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 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 ((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,
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(check_region(iobase, S526_IOSIZE) < 0){
283 comedi_error(dev,"I/O port conflict");
286 request_region(iobase, S526_IOSIZE, thisboard->name);
289 printk("iobase=0x%x\n", dev->iobase);
291 /*** make it a little quieter, exw, 8/29/06
292 for (i = 0; i < S526_NUM_PORTS; i++) {
293 printk("0x%02x: 0x%04x\n", ADDR_REG(s526_ports[i]), inw(ADDR_REG(s526_ports[i])));
298 * Initialize dev->board_name. Note that we can use the "thisboard"
299 * macro now, since we just initialized it in the last line.
301 thisboard = (s526_board *)&s526_boards[0];
303 dev->board_name = thisboard->name;
306 * Allocate the private structure area. alloc_private() is a
307 * convenient macro defined in comedidev.h.
309 if(alloc_private(dev, sizeof(s526_private)) < 0)
313 * Allocate the subdevice structures. alloc_subdevice() is a
314 * convenient macro defined in comedidev.h.
316 dev->n_subdevices = 4;
317 if(alloc_subdevices(dev, dev->n_subdevices) < 0)
321 /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
322 s->type = COMEDI_SUBD_COUNTER;
323 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
324 /* KG: What does SDF_LSAMPL (see multiq3.c) mean? */
325 s->n_chan = thisboard->gpct_chans;
326 s->maxdata = 0x00ffffff; /* 24 bit counter */
327 s->insn_read = s526_gpct_rinsn;
328 s->insn_config = s526_gpct_insn_config;
329 s->insn_write = s526_gpct_winsn;
331 /* Command are not implemented yet, however they are necessary to
332 allocate the necessary memory for the comedi_async struct (used
333 to trigger the GPCT in case of pulsegenerator function */
334 //s->do_cmd = s526_gpct_cmd;
335 //s->do_cmdtest = s526_gpct_cmdtest;
336 //s->cancel = s526_gpct_cancel;
339 //dev->read_subdev=s;
340 /* analog input subdevice */
341 s->type=COMEDI_SUBD_AI;
342 /* we support single-ended (ground) and differential */
343 s->subdev_flags=SDF_READABLE|SDF_DIFF;
346 s->range_table=&range_bipolar10;
347 s->len_chanlist=16; /* This is the maximum chanlist length that
348 the board can handle */
349 s->insn_read = s526_ai_rinsn;
350 s->insn_config = s526_ai_insn_config;
353 /* analog output subdevice */
354 s->type=COMEDI_SUBD_AO;
355 s->subdev_flags=SDF_WRITABLE;
358 s->range_table=&range_bipolar10;
359 s->insn_write = s526_ao_winsn;
360 s->insn_read = s526_ao_rinsn;
363 /* digital i/o subdevice */
364 if(thisboard->have_dio){
365 s->type=COMEDI_SUBD_DIO;
366 s->subdev_flags=SDF_READABLE|SDF_WRITABLE;
369 s->range_table=&range_digital;
370 s->insn_bits = s526_dio_insn_bits;
371 s->insn_config = s526_dio_insn_config;
373 s->type = COMEDI_SUBD_UNUSED;
376 printk("attached\n");
381 // Example of Counter Application
382 //One-shot (software trigger)
383 cmReg.reg.coutSource = 0; // out RCAP
384 cmReg.reg.coutPolarity = 1; // Polarity inverted
385 cmReg.reg.autoLoadResetRcap = 1; // Auto load 0:disabled, 1:enabled
386 cmReg.reg.hwCtEnableSource = 3; // NOT RCAP
387 cmReg.reg.ctEnableCtrl = 2; // Hardware
388 cmReg.reg.clockSource = 2; // Internal
389 cmReg.reg.countDir = 1; // Down
390 cmReg.reg.countDirCtrl = 1; // Software
391 cmReg.reg.outputRegLatchCtrl = 0; // latch on read
392 cmReg.reg.preloadRegSel = 0; // PR0
393 cmReg.reg.reserved = 0;
395 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
397 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
398 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
400 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset the counter
401 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Load the counter from PR0
403 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset RCAP (fires one-shot)
408 // Set Counter Mode Register
409 cmReg.reg.coutSource = 0; // out RCAP
410 cmReg.reg.coutPolarity = 0; // Polarity inverted
411 cmReg.reg.autoLoadResetRcap = 0; // Auto load disabled
412 cmReg.reg.hwCtEnableSource = 2; // NOT RCAP
413 cmReg.reg.ctEnableCtrl = 1; // 1: Software, >1 : Hardware
414 cmReg.reg.clockSource = 3; // x4
415 cmReg.reg.countDir = 0; // up
416 cmReg.reg.countDirCtrl = 0; // quadrature
417 cmReg.reg.outputRegLatchCtrl = 0; // latch on read
418 cmReg.reg.preloadRegSel = 0; // PR0
419 cmReg.reg.reserved = 0;
422 printk("Mode reg=0x%04x, 0x%04x\n", cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
423 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
425 printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n)));
427 // Load the pre-laod register high word
428 // value = (sampl_t) (0x55);
429 // outw(value, ADDR_CHAN_REG(REG_C0H, n));
431 // Load the pre-laod register low word
432 // value = (sampl_t)(0xaa55);
433 // outw(value, ADDR_CHAN_REG(REG_C0L, n));
435 // Write the Counter Control Register
436 // outw(value, ADDR_CHAN_REG(REG_C0C, 0));
439 // Reset the counter if it is software preload
440 if (cmReg.reg.autoLoadResetRcap == 0) {
441 outw(0x8000, ADDR_CHAN_REG(REG_C0C, n)); // Reset the counter
442 outw(0x4000, ADDR_CHAN_REG(REG_C0C, n)); // Load the counter from PR0
445 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
447 printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n)));
450 printk("Current registres:\n");
452 for (i = 0; i < S526_NUM_PORTS; i++) {
453 printk("0x%02x: 0x%04x\n", ADDR_REG(s526_ports[i]), inw(ADDR_REG(s526_ports[i])));
460 * _detach is called to deconfigure a device. It should deallocate
462 * This function is also called when _attach() fails, so it should be
463 * careful not to release resources that were not necessarily
464 * allocated by _attach(). dev->private and dev->subdevices are
465 * deallocated automatically by the core.
467 static int s526_detach(comedi_device *dev)
469 printk("comedi%d: s526: remove\n", dev->minor);
472 release_region(dev->iobase, S526_IOSIZE);
477 static int s526_gpct_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
479 int i; // counts the Data
480 int counter_channel = CR_CHAN(insn->chanspec);
481 unsigned short datalow;
482 unsigned short datahigh;
487 printk("s526: INSN_READ: n should be > 0\n");
490 // Read the low word first
491 for ( i=0 ; i < insn->n ; i++ )
493 datalow = inw(ADDR_CHAN_REG(REG_C0L, counter_channel));
494 datahigh = inw(ADDR_CHAN_REG(REG_C0H, counter_channel));
495 data[i] = (int)(datahigh & 0x00FF);
496 data[i] = (data[i] << 16) | (datalow & 0xFFFF);
497 // printk("s526 GPCT[%d]: %x(0x%04x, 0x%04x)\n", counter_channel, data[i], datahigh, datalow);
502 static int s526_gpct_insn_config(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
504 int subdev_channel = CR_CHAN(insn->chanspec);// Unpack chanspec
508 // printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n", subdev_channel);
510 for(i = 0; i < MAX_GPCT_CONFIG_DATA; i++) {
511 s526_gpct_config[subdev_channel].data[i] = insn->data[i];
512 // printk("data[%d]=%x\n", i, insn->data[i]);
515 // Check what type of Counter the user requested, data[0] contains
516 // the Application type
517 switch(insn->data[0])
519 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
521 data[0]: Application Type
522 data[1]: Counter Mode Register Value
523 data[2]: Pre-load Register Value
524 data[3]: Conter Control Register
526 printk("s526: GPCT_INSN_CONFIG: Configuring Encoder\n");
527 s526_gpct_config[subdev_channel].app = PositionMeasurement;
530 // Example of Counter Application
531 //One-shot (software trigger)
532 cmReg.reg.coutSource = 0; // out RCAP
533 cmReg.reg.coutPolarity = 1; // Polarity inverted
534 cmReg.reg.autoLoadResetRcap = 0; // Auto load disabled
535 cmReg.reg.hwCtEnableSource = 3; // NOT RCAP
536 cmReg.reg.ctEnableCtrl = 2; // Hardware
537 cmReg.reg.clockSource = 2; // Internal
538 cmReg.reg.countDir = 1; // Down
539 cmReg.reg.countDirCtrl = 1; // Software
540 cmReg.reg.outputRegLatchCtrl = 0; // latch on read
541 cmReg.reg.preloadRegSel = 0; // PR0
542 cmReg.reg.reserved = 0;
544 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
546 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
547 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
549 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset the counter
550 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Load the counter from PR0
552 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset RCAP (fires one-shot)
557 // Set Counter Mode Register
558 cmReg.reg.coutSource = 0; // out RCAP
559 cmReg.reg.coutPolarity = 0; // Polarity inverted
560 cmReg.reg.autoLoadResetRcap = 0; // Auto load disabled
561 cmReg.reg.hwCtEnableSource = 2; // NOT RCAP
562 cmReg.reg.ctEnableCtrl = 1; // 1: Software, >1 : Hardware
563 cmReg.reg.clockSource = 3; // x4
564 cmReg.reg.countDir = 0; // up
565 cmReg.reg.countDirCtrl = 0; // quadrature
566 cmReg.reg.outputRegLatchCtrl = 0; // latch on read
567 cmReg.reg.preloadRegSel = 0; // PR0
568 cmReg.reg.reserved = 0;
570 // Set Counter Mode Register
571 // printk("s526: Counter Mode register=%x\n", cmReg.value);
572 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
574 // Reset the counter if it is software preload
575 if (cmReg.reg.autoLoadResetRcap == 0) {
576 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset the counter
577 // outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Load the counter from PR0
580 cmReg.reg.countDirCtrl = 0; // 0 quadrature, 1 software control
582 // data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4
583 if(insn->data[1] == GPCT_X2) {
584 cmReg.reg.clockSource = 1;
585 }else if(insn->data[1] == GPCT_X4) {
586 cmReg.reg.clockSource = 2;
588 cmReg.reg.clockSource = 0;
591 // When to take into account the indexpulse:
592 if(insn->data[2] == GPCT_IndexPhaseLowLow) {
593 }else if(insn->data[2] == GPCT_IndexPhaseLowHigh) {
594 }else if(insn->data[2] == GPCT_IndexPhaseHighLow) {
595 }else if(insn->data[2] == GPCT_IndexPhaseHighHigh) {
598 // Take into account the index pulse?
599 if(insn->data[3] == GPCT_RESET_COUNTER_ON_INDEX)
600 cmReg.reg.autoLoadResetRcap = 4; // Auto load with INDEX^
603 // Set Counter Mode Register
604 cmReg.value = (sampl_t)(insn->data[1] & 0xFFFF);
605 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
607 // Load the pre-laod register high word
608 value = (sampl_t) ((insn->data[2] >> 16) & 0xFFFF);
609 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
611 // Load the pre-laod register low word
612 value = (sampl_t)(insn->data[2] & 0xFFFF);
613 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
615 // Write the Counter Control Register
616 if (insn->data[3] != 0) {
617 value = (sampl_t)(insn->data[3] & 0xFFFF);
618 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
621 // Reset the counter if it is software preload
622 if (cmReg.reg.autoLoadResetRcap == 0) {
623 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Reset the counter
624 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); // Load the counter from PR0
629 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
631 data[0]: Application Type
632 data[1]: Counter Mode Register Value
633 data[2]: Pre-load Register 0 Value
634 data[3]: Pre-load Register 1 Value
635 data[4]: Conter Control Register
637 printk("s526: GPCT_INSN_CONFIG: Configuring SPG\n");
638 s526_gpct_config[subdev_channel].app = SinglePulseGeneration;
640 // Set Counter Mode Register
641 cmReg.value = (sampl_t)(insn->data[1] & 0xFFFF);
642 cmReg.reg.preloadRegSel = 0; // PR0
643 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
645 // Load the pre-laod register 0 high word
646 value = (sampl_t) ((insn->data[2] >> 16) & 0xFFFF);
647 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
649 // Load the pre-laod register 0 low word
650 value = (sampl_t)(insn->data[2] & 0xFFFF);
651 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
653 // Set Counter Mode Register
654 cmReg.value = (sampl_t)(insn->data[1] & 0xFFFF);
655 cmReg.reg.preloadRegSel = 1; // PR1
656 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
658 // Load the pre-laod register 1 high word
659 value = (sampl_t) ((insn->data[3] >> 16) & 0xFFFF);
660 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
662 // Load the pre-laod register 1 low word
663 value = (sampl_t)(insn->data[3] & 0xFFFF);
664 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
666 // Write the Counter Control Register
667 if (insn->data[3] != 0) {
668 value = (sampl_t)(insn->data[3] & 0xFFFF);
669 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
673 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
675 data[0]: Application Type
676 data[1]: Counter Mode Register Value
677 data[2]: Pre-load Register 0 Value
678 data[3]: Pre-load Register 1 Value
679 data[4]: Conter Control Register
681 printk("s526: GPCT_INSN_CONFIG: Configuring PTG\n");
682 s526_gpct_config[subdev_channel].app = PulseTrainGeneration;
684 // Set Counter Mode Register
685 cmReg.value = (sampl_t)(insn->data[1] & 0xFFFF);
686 cmReg.reg.preloadRegSel = 0; // PR0
687 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
689 // Load the pre-laod register 0 high word
690 value = (sampl_t) ((insn->data[2] >> 16) & 0xFFFF);
691 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
693 // Load the pre-laod register 0 low word
694 value = (sampl_t)(insn->data[2] & 0xFFFF);
695 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
697 // Set Counter Mode Register
698 cmReg.value = (sampl_t)(insn->data[1] & 0xFFFF);
699 cmReg.reg.preloadRegSel = 1; // PR1
700 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
702 // Load the pre-laod register 1 high word
703 value = (sampl_t) ((insn->data[3] >> 16) & 0xFFFF);
704 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
706 // Load the pre-laod register 1 low word
707 value = (sampl_t)(insn->data[3] & 0xFFFF);
708 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
710 // Write the Counter Control Register
711 if (insn->data[3] != 0) {
712 value = (sampl_t)(insn->data[3] & 0xFFFF);
713 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
718 printk("s526: unsupported GPCT_insn_config\n");
726 static int s526_gpct_winsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
728 int subdev_channel = CR_CHAN(insn->chanspec);// Unpack chanspec
731 printk("s526: GPCT_INSN_WRITE on channel %d\n", subdev_channel);
732 cmReg.value = inw(ADDR_CHAN_REG(REG_C0M, subdev_channel));
733 printk("s526: Counter Mode Register: %x\n", cmReg.value);
734 // Check what Application of Counter this channel is configured for
735 switch(s526_gpct_config[subdev_channel].app)
737 case PositionMeasurement:
738 printk("S526: INSN_WRITE: PM\n");
739 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H, subdev_channel));
740 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
743 case SinglePulseGeneration:
744 printk("S526: INSN_WRITE: SPG\n");
745 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H, subdev_channel));
746 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
749 case PulseTrainGeneration:
750 /* data[0] contains the PULSE_WIDTH
751 data[1] contains the PULSE_PERIOD
752 @pre PULSE_PERIOD > PULSE_WIDTH > 0
753 The above periods must be expressed as a multiple of the
754 pulse frequency on the selected source
756 printk("S526: INSN_WRITE: PTG\n");
757 if ( (insn->data[1] > insn->data[0]) && (insn->data[0] > 0 ) )
759 (s526_gpct_config[subdev_channel]).data[0] = insn->data[0];
760 (s526_gpct_config[subdev_channel]).data[1] = insn->data[1];
764 printk("%d \t %d\n",insn->data[1],insn->data[2]);
765 printk("s526: INSN_WRITE: PTG: Problem with Pulse params\n");
769 value = (sampl_t)((*data >> 16) & 0xFFFF);
770 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
771 value = (sampl_t)(*data & 0xFFFF);
772 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
774 default: // Impossible
775 printk("s526: INSN_WRITE: Functionality %d not implemented yet\n",
776 s526_gpct_config[subdev_channel].app);
780 // return the number of samples written
784 #define ISR_ADC_DONE 0x4
785 static int s526_ai_insn_config(comedi_device *dev,comedi_subdevice *s,
786 comedi_insn *insn, lsampl_t *data)
788 int result = -EINVAL;
790 if (insn->n < 1) return result;
794 /* data[0] : channels was set in relevant bits.
798 // Enable ADC interrupt
799 outw(ISR_ADC_DONE, ADDR_REG(REG_IER));
800 // printk("s526: ADC current value: 0x%04x\n", inw(ADDR_REG(REG_ADC)));
801 s526_ai_config = (data[0] & 0x3FF) << 5;
803 s526_ai_config |= 0x8000; //set the delay
805 s526_ai_config |= 0x0001; // ADC start bit.
812 * "instructions" read/write data in "one-shot" or "software-triggered"
815 static int s526_ai_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
818 int chan = CR_CHAN(insn->chanspec);
819 unsigned short value;
823 value = s526_ai_config | (chan << 1);
824 // outw(value, ADDR_REG(REG_ADC)); do it with ADC start
827 /* convert n samples */
828 for(n=0; n<insn->n; n++){
829 /* trigger conversion */
830 value |= 0x0001; // ADC start
831 outw(value, ADDR_REG(REG_ADC));
832 // printk("s526: Wrote 0x%04x to ADC\n", value);
833 // printk("s526: ADC reg=0x%04x\n", inw(ADDR_REG(REG_ADC)));
836 /* wait for conversion to end */
837 for(i=0;i<TIMEOUT;i++){
838 status = inw(ADDR_REG(REG_ISR));
839 if (status & ISR_ADC_DONE) {
840 outw(ISR_ADC_DONE, ADDR_REG(REG_ISR));
845 /* rt_printk() should be used instead of printk()
846 * whenever the code can be called from real-time. */
847 rt_printk("s526: ADC(0x%04x) timeout\n", inw(ADDR_REG(REG_ISR)));
852 d = inw(ADDR_REG(REG_ADD));
853 // printk("AI[%d]=0x%04x\n", n, (unsigned short)(d & 0xFFFF));
858 /* return the number of samples read/written */
863 static int s526_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
866 int chan = CR_CHAN(insn->chanspec);
869 // printk("s526_ao_winsn\n");
871 // outw(val, dev->iobase + REG_DAC);
872 outw(val, ADDR_REG(REG_DAC));
874 /* Writing a list of values to an AO channel is probably not
875 * very useful, but that's how the interface is defined. */
876 for(i=0;i<insn->n;i++){
877 /* a typical programming sequence */
878 // outw(data[i], dev->iobase + REG_ADD); // write the data to preload register
879 outw(data[i], ADDR_REG(REG_ADD)); // write the data to preload register
880 devpriv->ao_readback[chan] = data[i];
881 // outw(val + 1, dev->iobase + REG_DAC); // starts the D/A conversion.
882 outw(val + 1, ADDR_REG(REG_DAC)); // starts the D/A conversion.
885 /* return the number of samples read/written */
889 /* AO subdevices should have a read insn as well as a write insn.
890 * Usually this means copying a value stored in devpriv. */
891 static int s526_ao_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
894 int chan = CR_CHAN(insn->chanspec);
896 for(i=0;i<insn->n;i++)
897 data[i] = devpriv->ao_readback[chan];
902 /* DIO devices are slightly special. Although it is possible to
903 * implement the insn_read/insn_write interface, it is much more
904 * useful to applications if you implement the insn_bits interface.
905 * This allows packed reading/writing of the DIO channels. The
906 * comedi core can convert between insn_bits and insn_read/write */
907 static int s526_dio_insn_bits(comedi_device *dev,comedi_subdevice *s,
908 comedi_insn *insn,lsampl_t *data)
910 if(insn->n!=2)return -EINVAL;
912 /* The insn data is a mask in data[0] and the new data
913 * in data[1], each channel cooresponding to a bit. */
915 s->state &= ~data[0];
916 s->state |= data[0]&data[1];
917 /* Write out the new digital output lines */
918 outw(s->state, ADDR_REG(REG_DIO));
921 /* on return, data[1] contains the value of the digital
922 * input and output lines. */
923 data[1]=inw(ADDR_REG(REG_DIO)) & 0xFF; // low 8 bits are the data
924 /* or we could just return the software copy of the output values if
925 * it was a purely digital output subdevice */
931 static int s526_dio_insn_config(comedi_device *dev,comedi_subdevice *s,
932 comedi_insn *insn,lsampl_t *data)
934 int chan=CR_CHAN(insn->chanspec);
937 printk("S526 DIO insn_config\n");
939 if(insn->n!=1)return -EINVAL;
941 value = inw(ADDR_REG(REG_DIO));
943 /* The input or output configuration of each digital line is
944 * configured by a special insn_config instruction. chanspec
945 * contains the channel to be changed, and data[0] contains the
946 * value COMEDI_INPUT or COMEDI_OUTPUT. */
948 if(data[0]==COMEDI_OUTPUT){
949 value |= 1 << (chan + 10); // bit 10/11 set the group 1/2's mode
950 s->io_bits |= (0xF << chan);
952 value &= ~(1 << (chan + 10)); // 1 is output, 0 is input.
953 s->io_bits &= ~(0xF << chan);
955 outw(value, ADDR_REG(REG_DIO));
961 * A convenient macro that defines init_module() and cleanup_module(),
964 COMEDI_INITCLEANUP(driver_s526);