and compatibles
COMEDI - Linux Control and Measurement Device Interface
- Copyright (C) 1998 David A. Schleef <ds@stm.lbl.gov>
+ Copyright (C) 1998 David A. Schleef <ds@schleef.org>
Janne Jalkanen <jalkanen@cs.hut.fi>
Eric Bunn <ebu@cs.hut.fi>
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+/*
+Driver: pcl711.o
+Description: Advantech PCL-711 and 711b, ADLink ACL-8112
+Author: ds, Janne Jalkanen <jalkanen@cs.hut.fi>, Eric Bunn <ebu@cs.hut.fi>
+Status: mostly complete
+Devices: [Advantech] PCL-711 (pcl711), PCL-711B (pcl711b),
+ [AdLink] ACL-8112HG (acl8112hg), ACL-8112DG (acl8112dg)
+
+Since these boards do not have DMA or FIFOs, only immediate mode is
+supported.
+
+*/
/*
Dave Andruczyk <dave@tech.buffalostate.edu> also wrote a
*/
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/malloc.h>
-#include <linux/errno.h>
+#include <linux/comedidev.h>
+
#include <linux/ioport.h>
#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/timex.h>
-#include <linux/timer.h>
-#include <asm/io.h>
-#include <comedi_module.h>
-#include <8253.h>
+
+#include "8253.h"
#define PCL711_DO_LO 13
#define PCL711_DO_HI 14
-static comedi_lrange range_pcl711b_ai = { 5, {
+static const comedi_lrange range_pcl711b_ai = { 5, {
BIP_RANGE( 5 ),
BIP_RANGE( 2.5 ),
BIP_RANGE( 1.25 ),
BIP_RANGE( 0.625 ),
BIP_RANGE( 0.3125 )
}};
-static comedi_lrange range_acl8112hg_ai = { 12, {
+static const comedi_lrange range_acl8112hg_ai = { 12, {
BIP_RANGE( 5 ),
BIP_RANGE( 0.5 ),
BIP_RANGE( 0.05 ),
BIP_RANGE( 0.1 ),
BIP_RANGE( 0.01 )
}};
-static comedi_lrange range_acl8112dg_ai = { 9, {
+static const comedi_lrange range_acl8112dg_ai = { 9, {
BIP_RANGE( 5 ),
BIP_RANGE( 2.5 ),
BIP_RANGE( 1.25 ),
BIP_RANGE( 10 )
}};
-static int pcl711_attach(comedi_device *dev,comedi_devconfig *it);
-static int pcl711_detach(comedi_device *dev);
-static int pcl711_recognize(char *name);
-comedi_driver driver_pcl711={
- driver_name: "pcl711",
- module: &__this_module,
- attach: pcl711_attach,
- detach: pcl711_detach,
- recognize: pcl711_recognize,
-};
-
-typedef int bool;
-
/*
* flags
*/
#define PCL711_TIMEOUT 100
#define PCL711_DRDY 0x10
-int i8253_osc_base = 500; /* 2 Mhz */
+static const int i8253_osc_base = 500; /* 2 Mhz */
typedef struct {
- char *name;
+ const char *name;
int is_pcl711b;
int is_8112;
int is_dg;
int n_aichan;
int n_aochan;
int maxirq;
- comedi_lrange * ai_range_type;
+ const comedi_lrange * ai_range_type;
} boardtype;
-static boardtype boardtypes[] =
+static const boardtype boardtypes[] =
{
{"pcl711", 0, 0, 0, 5, 8, 1, 0, &range_bipolar5},
{"pcl711b", 1, 0, 0, 5, 8, 1, 7, &range_pcl711b_ai},
{"acl8112dg", 0, 1, 1, 9, 16, 2, 15, &range_acl8112dg_ai},
};
#define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))
+#define this_board ((const boardtype *)dev->board_ptr)
+
+static int pcl711_attach(comedi_device *dev,comedi_devconfig *it);
+static int pcl711_detach(comedi_device *dev);
+static comedi_driver driver_pcl711={
+ driver_name: "pcl711",
+ module: THIS_MODULE,
+ attach: pcl711_attach,
+ detach: pcl711_detach,
+ board_name: &boardtypes[0].name,
+ num_names: n_boardtypes,
+ offset: sizeof(boardtype),
+};
+COMEDI_INITCLEANUP(driver_pcl711);
typedef struct {
int board;
int ntrig;
int aip[8];
int mode;
+ lsampl_t ao_readback[2];
+ unsigned int divisor1;
+ unsigned int divisor2;
} pcl711_private;
#define devpriv ((pcl711_private *)dev->private)
-#define this_board (boardtypes+dev->board)
-static void pcl711_interrupt(int irq, void *d, struct pt_regs *regs)
+static irqreturn_t pcl711_interrupt(int irq, void *d PT_REGS_ARG)
{
int lo, hi;
int data;
} else {
outb(0, dev->iobase + PCL711_MODE);
}
- s->busy = 0;
- comedi_done(dev,s);
+ s->async->events |= COMEDI_CB_EOA;
}
+ comedi_event(dev, s, s->async->events);
+ return IRQ_HANDLED;
}
static void pcl711_set_changain(comedi_device * dev, int chan)
int chan_register;
outb(CR_RANGE(chan), dev->iobase + PCL711_GAIN);
-
+
chan_register=CR_CHAN(chan);
if (this_board->is_8112) {
}
}
-static int pcl711_ai_mode0(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+static int pcl711_ai_insn(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data)
{
- int hi, lo, i;
- int nmax=40; /* 1000 us / 25 us */
- int n;
+ int i,n;
+ int hi,lo;
- if(it->n<=nmax)nmax=it->n;
+ pcl711_set_changain(dev,insn->chanspec);
- pcl711_set_changain(dev,it->chanlist[0]);
-
- /*
- a sensible precaution to wait for the mux to
- settle here. is 10us enough?
- */
- udelay(10);
+ for(n=0;n<insn->n;n++){
+ /*
+ * Write the correct mode (software polling) and start polling by writing
+ * to the trigger register
+ */
+ outb(1, dev->iobase + PCL711_MODE);
-for(n=0;n<nmax;n++){
- /*
- * Write the correct mode (software polling) and start polling by writing
- * to the trigger register
- */
- outb(1, dev->iobase + PCL711_MODE);
+ if (this_board->is_8112) {
+ }else{
+ outb(0, dev->iobase + PCL711_SOFTTRIG);
+ }
- if (this_board->is_8112) {
- }else{
- outb(0, dev->iobase + PCL711_SOFTTRIG);
- }
+ i=PCL711_TIMEOUT;
+ while(--i){
+ hi = inb(dev->iobase + PCL711_AD_HI);
+ if (!(hi & PCL711_DRDY))
+ goto ok;
+ comedi_udelay(1);
+ }
+ rt_printk("comedi%d: pcl711: A/D timeout\n", dev->minor);
+ return -ETIME;
- i=PCL711_TIMEOUT;
- while(--i){
- hi = inb(dev->iobase + PCL711_AD_HI);
- if (!(hi & PCL711_DRDY))
- goto ok;
- udelay(5);
- }
- rt_printk("comedi%d: pcl711: A/D timeout\n", dev->minor);
- return -ETIME;
-
ok:
- lo = inb(dev->iobase + PCL711_AD_LO);
+ lo = inb(dev->iobase + PCL711_AD_LO);
- it->data[n] = ((hi & 0xf) << 8) | lo;
-}
+ data[n] = ((hi & 0xf) << 8) | lo;
+ }
return n;
}
-static int pcl711_ai_mode4(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+static int pcl711_ai_cmdtest(comedi_device *dev, comedi_subdevice *s,
+ comedi_cmd *cmd)
{
- if (!this_board->is_pcl711b || dev->irq == 0)
- return -EINVAL;
+ int tmp;
+ int err = 0;
- pcl711_set_changain(dev,it->chanlist[0]);
+ /* step 1 */
+ tmp=cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if(!cmd->start_src || tmp!=cmd->start_src)err++;
- /*
- * Set mode to "no internal trigger"
- */
- outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
+ tmp=cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_TIMER|TRIG_EXT;
+ if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++;
+
+ tmp=cmd->convert_src;
+ cmd->convert_src &= TRIG_NOW;
+ if(!cmd->convert_src || tmp!=cmd->convert_src)err++;
+
+ tmp=cmd->scan_end_src;
+ cmd->scan_end_src &= TRIG_COUNT;
+ if(!cmd->scan_end_src || tmp!=cmd->scan_end_src)err++;
+
+ tmp=cmd->stop_src;
+ cmd->stop_src &= TRIG_COUNT|TRIG_NONE;
+ if(!cmd->stop_src || tmp!=cmd->stop_src)err++;
+
+ if(err)return 1;
+
+ /* step 2 */
+
+ if(cmd->scan_begin_src!=TRIG_TIMER &&
+ cmd->scan_begin_src!=TRIG_EXT)err++;
+ if(cmd->stop_src!=TRIG_COUNT &&
+ cmd->stop_src!=TRIG_NONE)err++;
+
+ if(err)return 2;
+
+ /* step 3 */
+
+ if(cmd->start_arg!=0){
+ cmd->start_arg=0;
+ err++;
+ }
+ if(cmd->scan_begin_src==TRIG_EXT){
+ if(cmd->scan_begin_arg!=0){
+ cmd->scan_begin_arg=0;
+ err++;
+ }
+ }else{
+#define MAX_SPEED 1000
+#define TIMER_BASE 100
+ if(cmd->scan_begin_arg<MAX_SPEED){
+ cmd->scan_begin_arg=MAX_SPEED;
+ err++;
+ }
+ }
+ if(cmd->convert_arg!=0){
+ cmd->convert_arg=0;
+ err++;
+ }
+ if(cmd->scan_end_arg!=cmd->chanlist_len){
+ cmd->scan_end_arg=cmd->chanlist_len;
+ err++;
+ }
+ if(cmd->stop_src==TRIG_NONE){
+ if(cmd->stop_arg!=0){
+ cmd->stop_arg=0;
+ err++;
+ }
+ }else{
+ /* ignore */
+ }
+
+ if(err)return 3;
+
+ /* step 4 */
+
+ if(cmd->scan_begin_src==TRIG_TIMER){
+ tmp = cmd->scan_begin_arg;
+ i8253_cascade_ns_to_timer_2div(TIMER_BASE,
+ &devpriv->divisor1,&devpriv->divisor2,
+ &cmd->scan_begin_arg, cmd->flags&TRIG_ROUND_MASK);
+ if(tmp!=cmd->scan_begin_arg)
+ err++;
+ }
+
+ if(err)return 4;
return 0;
}
-
-static int pcl711_ai_mode1(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+static int pcl711_ai_cmd(comedi_device *dev, comedi_subdevice *s)
{
int timer1,timer2;
+ comedi_cmd *cmd = &s->async->cmd;
- if (!dev->irq)
- return -EINVAL;
-
- pcl711_set_changain(dev,it->chanlist[0]);
+ pcl711_set_changain(dev,cmd->chanlist[0]);
- /*
- * Set timers
- * timer chip is an 8253, with timers 1 and 2
- * cascaded
- * 0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
- * Mode 2 = Rate generator
- *
- * 0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
- */
+ if(cmd->scan_begin_src==TRIG_TIMER){
+ /*
+ * Set timers
+ * timer chip is an 8253, with timers 1 and 2
+ * cascaded
+ * 0x74 = Select Counter 1 | LSB/MSB | Mode=2 | Binary
+ * Mode 2 = Rate generator
+ *
+ * 0xb4 = Select Counter 2 | LSB/MSB | Mode=2 | Binary
+ */
- i8253_cascade_ns_to_timer(&timer1,&timer2,&it->trigvar,TRIG_ROUND_NEAREST);
+ i8253_cascade_ns_to_timer(i8253_osc_base,&timer1,&timer2,
+ &cmd->scan_begin_arg,TRIG_ROUND_NEAREST);
- outb(0x74, dev->iobase + PCL711_CTRCTL);
- outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
- outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
- outb(0xb4, dev->iobase + PCL711_CTRCTL);
- outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
- outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
+ outb(0x74, dev->iobase + PCL711_CTRCTL);
+ outb(timer1 & 0xff, dev->iobase + PCL711_CTR1);
+ outb((timer1 >> 8) & 0xff, dev->iobase + PCL711_CTR1);
+ outb(0xb4, dev->iobase + PCL711_CTRCTL);
+ outb(timer2 & 0xff, dev->iobase + PCL711_CTR2);
+ outb((timer2 >> 8) & 0xff, dev->iobase + PCL711_CTR2);
- /* clear pending interrupts (just in case) */
- outb(0, dev->iobase + PCL711_CLRINTR);
+ /* clear pending interrupts (just in case) */
+ outb(0, dev->iobase + PCL711_CLRINTR);
- /*
- * Set mode to IRQ transfer
- */
- outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
+ /*
+ * Set mode to IRQ transfer
+ */
+ outb(devpriv->mode | 6, dev->iobase + PCL711_MODE);
+ }else{
+ /* external trigger */
+ outb(devpriv->mode | 3, dev->iobase + PCL711_MODE);
+ }
return 0;
}
/*
analog output
*/
-static int pcl711_ao(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+static int pcl711_ao_insn(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data)
{
- int chan = CR_CHAN(it->chanlist[0]);
- sampl_t data = it->data[0];
+ int n;
+ int chan = CR_CHAN(insn->chanspec);
- outb((data & 0xff), dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
- outb((data >> 8), dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
+ for(n=0;n<insn->n;n++){
+ outb((data[n] & 0xff), dev->iobase + (chan ? PCL711_DA1_LO : PCL711_DA0_LO));
+ outb((data[n] >> 8), dev->iobase + (chan ? PCL711_DA1_HI : PCL711_DA0_HI));
+
+ devpriv->ao_readback[chan] = data[n];
+ }
+
+ return n;
+}
+
+static int pcl711_ao_insn_read(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data)
+{
+ int n;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for(n=0;n<insn->n;n++){
+ data[n] = devpriv->ao_readback[chan];
+ }
+
+ return n;
- return 0;
}
/* Digital port read - Untested on 8112 */
-static int pcl711_di(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+static int pcl711_di_insn_bits(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn *insn,lsampl_t *data)
{
- int data;
- int chan;
- int i;
+ if(insn->n!=2)return -EINVAL;
- data = inb(dev->iobase + PCL711_DI_LO) |
+ data[1] = inb(dev->iobase + PCL711_DI_LO) |
(inb(dev->iobase + PCL711_DI_HI) << 8);
- for(i=0;i<it->n_chan;i++){
- chan=CR_CHAN(it->chanlist[i]);
- it->data[i]=(data>>chan)&1;
- }
-
- return it->n_chan;
+ return 2;
}
/* Digital port write - Untested on 8112 */
-static int pcl711_do(comedi_device * dev, comedi_subdevice * s, comedi_trig * it)
+static int pcl711_do_insn_bits(comedi_device * dev, comedi_subdevice * s,
+ comedi_insn *insn,lsampl_t *data)
{
- int mask, data;
- int chan;
- int i;
-
- data=s->state;
- for(i=0;i<it->n_chan;i++){
- chan=CR_CHAN(it->chanlist[i]);
- mask=(1<<chan);
- data &= ~mask;
- if(it->data[i])
- data |= mask;
+ if(insn->n!=2)return -EINVAL;
+
+ if(data[0]){
+ s->state &= ~data[0];
+ s->state |= data[0]&data[1];
}
- outb(data & 0xff, dev->iobase + PCL711_DO_LO);
- outb((data >> 8), dev->iobase + PCL711_DO_HI);
- s->state = data;
+ if(data[0]&0x00ff)
+ outb(s->state & 0xff, dev->iobase + PCL711_DO_LO);
+ if(data[0]&0xff00)
+ outb((s->state >> 8), dev->iobase + PCL711_DO_HI);
- return it->n_chan;
+ data[1]=s->state;
+
+ return 2;
}
/* Free any resources that we have claimed */
-static void free_resources(comedi_device * dev)
+static int pcl711_detach(comedi_device * dev)
{
+ printk("comedi%d: pcl711: remove\n", dev->minor);
+
if (dev->irq)
- free_irq(dev->irq, dev);
+ comedi_free_irq(dev->irq, dev);
if (dev->iobase)
- release_region(dev->iobase, dev->iosize);
-}
-
-static int pcl711_recognize(char *name)
-{
- int i;
+ release_region(dev->iobase, PCL711_SIZE);
- for (i = 0; i < n_boardtypes; i++) {
- if (!strcmp(boardtypes[i].name, name)) {
- return i;
- }
- }
- return -1;
+ return 0;
}
/* Initialization */
static int pcl711_attach(comedi_device * dev, comedi_devconfig * it)
{
int ret;
- int iobase;
- int irq;
+ unsigned long iobase;
+ unsigned int irq;
comedi_subdevice *s;
/* claim our I/O space */
iobase = it->options[0];
- printk("comedi%d: pcl711: 0x%04x ", dev->minor, iobase);
- if (check_region(iobase, PCL711_SIZE) < 0) {
+ printk("comedi%d: pcl711: 0x%04lx ", dev->minor, iobase);
+ if (!request_region(iobase, PCL711_SIZE, "pcl711")) {
printk("I/O port conflict\n");
return -EIO;
}
- request_region(dev->iobase, PCL711_SIZE, "pcl711");
dev->iobase = iobase;
- dev->iosize = PCL711_SIZE;
/* there should be a sanity check here */
/* set up some name stuff */
- dev->board_name = boardtypes[dev->board].name;
+ dev->board_name = this_board->name;
/* grab our IRQ */
irq = it->options[1];
- if (irq < 0 || irq > boardtypes[dev->board].maxirq) {
+ if (irq > this_board->maxirq) {
printk("irq out of range\n");
- free_resources(dev);
return -EINVAL;
}
if (irq) {
- if (request_irq(irq, pcl711_interrupt, SA_INTERRUPT, "pcl711", dev)) {
- printk("unable to allocate irq %d\n", irq);
- free_resources(dev);
+ if (comedi_request_irq(irq, pcl711_interrupt, 0, "pcl711", dev)) {
+ printk("unable to allocate irq %u\n", irq);
return -EINVAL;
} else {
- printk("( irq = %d )\n", irq);
+ printk("( irq = %u )\n", irq);
}
}
dev->irq = irq;
- dev->n_subdevices = 4;
- if((ret=alloc_subdevices(dev))<0)
+ if((ret=alloc_subdevices(dev, 4))<0)
return ret;
if((ret=alloc_private(dev,sizeof(pcl711_private)))<0)
return ret;
s = dev->subdevices + 0;
/* AI subdevice */
s->type = COMEDI_SUBD_AI;
- s->subdev_flags = SDF_READABLE;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
s->n_chan = this_board->n_aichan;
s->maxdata = 0xfff;
s->len_chanlist = 1;
s->range_table = this_board->ai_range_type;
- s->trig[0] = pcl711_ai_mode0;
- s->trig[1] = pcl711_ai_mode1;
- s->trig[4] = pcl711_ai_mode4;
+ s->insn_read = pcl711_ai_insn;
+ if(irq){
+ dev->read_subdev = s;
+ s->subdev_flags |= SDF_CMD_READ;
+ s->do_cmdtest = pcl711_ai_cmdtest;
+ s->do_cmd = pcl711_ai_cmd;
+ }
s++;
/* AO subdevice */
s->type = COMEDI_SUBD_AO;
- s->subdev_flags = SDF_WRITEABLE;
+ s->subdev_flags = SDF_WRITABLE;
s->n_chan = this_board->n_aochan;
s->maxdata = 0xfff;
s->len_chanlist = 1;
s->range_table = &range_bipolar5;
- s->trig[0] = pcl711_ao;
+ s->insn_write = pcl711_ao_insn;
+ s->insn_read = pcl711_ao_insn_read;
s++;
/* 16-bit digital input */
s->maxdata = 1;
s->len_chanlist = 16;
s->range_table = &range_digital;
- s->trig[0] = pcl711_di;
+ s->insn_bits = pcl711_di_insn_bits;
s++;
/* 16-bit digital out */
s->type = COMEDI_SUBD_DO;
- s->subdev_flags = SDF_WRITEABLE;
+ s->subdev_flags = SDF_WRITABLE;
s->n_chan = 16;
s->maxdata = 1;
s->len_chanlist = 16;
s->range_table = &range_digital;
s->state=0;
- s->trig[0] = pcl711_do;
+ s->insn_bits = pcl711_do_insn_bits;
/*
this is the "base value" for the mode register, which is
return 0;
}
-
-/*
- * Removes device
- */
-
-static int pcl711_detach(comedi_device * dev)
-{
- printk("comedi%d: pcl711: remove\n", dev->minor);
-
- free_resources(dev);
-
- return 0;
-}
-
-#ifdef MODULE
-int init_module(void)
-{
- comedi_driver_register(&driver_pcl711);
-
- return 0;
-}
-
-void cleanup_module(void)
-{
- comedi_driver_unregister(&driver_pcl711);
-}
-#endif