--- /dev/null
+/*
+ comedi/drivers/ni_6527.c
+ driver for National Instruments PCI-6527
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1999,2002,2003 David A. Schleef <ds@schleef.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+Driver: ni6527.o
+Description: National Instruments 6527
+Author: ds
+Status: works
+Devices: [National Instruments] 6527
+Updated: Sat, 25 Jan 2003 13:24:40 -0800
+
+
+*/
+
+/*
+ Manuals (available from ftp://ftp.natinst.com/support/manuals)
+
+ 370106b.pdf 6527 Register Level Programmer Manual
+
+ */
+
+#define DEBUG 1
+#define DEBUG_FLAGS
+
+#include <linux/comedidev.h>
+
+#include "mite.h"
+
+
+#define NI6527_DIO_SIZE 4096
+#define NI6527_MITE_SIZE 4096
+
+
+#define Port_Register(x) (0x00+(x))
+#define ID_Register 0x06
+
+#define Clear_Register 0x07
+#define ClrEdge 0x08
+#define ClrOverflow 0x04
+#define ClrFilter 0x02
+#define ClrInterval 0x01
+
+#define Filter_Interval(x) (0x08+(x))
+#define Filter_Enable(x) (0x0c+(x))
+
+#define Change_Status 0x14
+#define MasterInterruptStatus 0x04
+#define Overflow 0x02
+#define EdgeStatus 0x01
+
+#define Master_Interrupt_Control 0x15
+#define FallingEdgeIntEnable 0x10
+#define RisingEdgeIntEnable 0x08
+#define MasterInterruptEnable 0x04
+#define OverflowIntEnable 0x02
+#define EdgeIntEnable 0x01
+
+#define Rising_Edge_Detection_Enable(x) (0x018+(x))
+#define Falling_Edge_Detection_Enable(x) (0x020+(x))
+
+
+
+static int ni6527_attach(comedi_device *dev,comedi_devconfig *it);
+static int ni6527_detach(comedi_device *dev);
+static comedi_driver driver_ni6527={
+ driver_name: "ni6527",
+ module: THIS_MODULE,
+ attach: ni6527_attach,
+ detach: ni6527_detach,
+};
+COMEDI_INITCLEANUP(driver_ni6527);
+
+typedef struct{
+ int dev_id;
+ char *name;
+}ni6527_board;
+static ni6527_board ni6527_boards[] = {
+ {
+ dev_id: 0x2b20,
+ name: "pci-6527",
+ },
+ {
+ dev_id: 0x2b10,
+ name: "pxi-6527",
+ },
+};
+
+#define n_ni6527_boards (sizeof(ni6527_boards)/sizeof(ni6527_boards[0]))
+#define this_board ((ni6527_board *)dev->board_ptr)
+
+static struct pci_device_id ni6527_pci_table[] __devinitdata = {
+ { PCI_VENDOR_ID_NATINST, 0x2b10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { PCI_VENDOR_ID_NATINST, 0x2b20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, ni6527_pci_table);
+
+typedef struct{
+ struct mite_struct *mite;
+ unsigned int filter_interval;
+ unsigned int filter_enable;
+}ni6527_private;
+#define devpriv ((ni6527_private *)dev->private)
+
+static int ni6527_find_device(comedi_device *dev,int bus,int slot);
+
+
+static int ni6527_di_insn_config(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data)
+{
+ int chan = CR_CHAN(insn->chanspec);
+ unsigned int interval;
+
+ if(insn->n!=2)return -EINVAL;
+
+ if(data[0] != INSN_CONFIG_FILTER)return -EINVAL;
+
+ if(data[1]){
+ interval = (data[1]+100)/200;
+ data[1] = interval*200;
+
+ if(interval!=devpriv->filter_interval){
+ writeb(interval&0xff, dev->iobase + Filter_Interval(0));
+ writeb((interval>>8)&0xff, dev->iobase + Filter_Interval(1));
+ writeb((interval>>16)&0x0f, dev->iobase + Filter_Interval(1));
+
+ writeb(ClrInterval, dev->iobase + Clear_Register);
+
+ devpriv->filter_interval = interval;
+ }
+
+ devpriv->filter_enable |= 1<<chan;
+ }else{
+ devpriv->filter_enable &= ~(1<<chan);
+ }
+
+ writeb(devpriv->filter_enable, dev->iobase + Filter_Enable(0));
+ writeb(devpriv->filter_enable>>8, dev->iobase + Filter_Enable(1));
+ writeb(devpriv->filter_enable>>16, dev->iobase + Filter_Enable(2));
+
+ return 2;
+}
+
+static int ni6527_di_insn_bits(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data)
+{
+ if(insn->n!=2)return -EINVAL;
+
+ data[1] = readb(dev->iobase+Port_Register(0));
+ data[1] |= readb(dev->iobase+Port_Register(0))<<8;
+ data[1] |= readb(dev->iobase+Port_Register(0))<<16;
+
+ return 2;
+}
+
+static int ni6527_do_insn_bits(comedi_device *dev,comedi_subdevice *s,
+ comedi_insn *insn,lsampl_t *data)
+{
+ if(insn->n!=2)return -EINVAL;
+ if(data[0]){
+ s->state &= ~data[0];
+ s->state |= (data[0]&data[1]);
+
+ /* The open relay state on the board cooresponds to 1,
+ * but in Comedi, it is represented by 0. */
+ if(data[0]&0x0000ff){
+ writeb((s->state^0xff),dev->iobase+Port_Register(3));
+ }
+ if(data[0]&0x00ff00){
+ writeb((s->state>>8)^0xff,dev->iobase+Port_Register(4));
+ }
+ if(data[0]&0xff0000){
+ writeb((s->state>>16)^0xff,dev->iobase+Port_Register(5));
+ }
+ }
+ data[1] = s->state;
+
+ return 2;
+}
+
+static void ni6527_interrupt(int irq, void *d, struct pt_regs *regs)
+{
+ comedi_device *dev = d;
+ comedi_subdevice *s = dev->subdevices + 2;
+ unsigned int status;
+
+ status = readb(dev->iobase + Change_Status);
+ if(!status&MasterInterruptStatus)return;
+ if(!status&EdgeStatus)return;
+
+ writeb(ClrEdge | ClrOverflow, dev->iobase + Clear_Register);
+
+ comedi_buf_put(s->async, 0);
+ s->async->events |= COMEDI_CB_EOS;
+ comedi_event(dev,s,s->async->events);
+}
+
+static int ni6527_intr_cmdtest(comedi_device *dev,comedi_subdevice *s,
+ comedi_cmd *cmd)
+{
+ int err=0;
+ int tmp;
+
+ /* step 1: make sure trigger sources are trivially valid */
+
+ tmp=cmd->start_src;
+ cmd->start_src &= TRIG_NOW;
+ if(!cmd->start_src || tmp!=cmd->start_src)err++;
+
+ tmp=cmd->scan_begin_src;
+ cmd->scan_begin_src &= TRIG_OTHER;
+ if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++;
+
+ tmp=cmd->convert_src;
+ cmd->convert_src &= TRIG_FOLLOW;
+ 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;
+ if(!cmd->stop_src || tmp!=cmd->stop_src)err++;
+
+ if(err)return 1;
+
+ /* step 2: make sure trigger sources are unique and mutually compatible */
+
+ if(err)return 2;
+
+ /* step 3: make sure arguments are trivially compatible */
+
+ if(cmd->start_arg!=0){
+ cmd->start_arg=0;
+ err++;
+ }
+ if(cmd->scan_begin_arg!=0){
+ cmd->scan_begin_arg = 0;
+ err++;
+ }
+ if(cmd->convert_arg!=0){
+ cmd->convert_arg = 0;
+ err++;
+ }
+
+ if(cmd->scan_end_arg!=1){
+ cmd->scan_end_arg=1;
+ err++;
+ }
+ if(cmd->stop_arg!=0){
+ cmd->stop_arg=0;
+ err++;
+ }
+
+ if(err)return 3;
+
+ /* step 4: fix up any arguments */
+
+ if(err)return 4;
+
+ return 0;
+}
+
+static int ni6527_intr_cmd(comedi_device *dev,comedi_subdevice *s)
+{
+ //comedi_cmd *cmd = &s->async->cmd;
+
+ writeb(ClrEdge|ClrOverflow, dev->iobase + Clear_Register);
+ writeb(FallingEdgeIntEnable|RisingEdgeIntEnable|
+ MasterInterruptEnable|EdgeIntEnable,
+ dev->iobase + Master_Interrupt_Control);
+
+ return 0;
+}
+
+static int ni6527_intr_cancel(comedi_device *dev,comedi_subdevice *s)
+{
+ writeb(0x00, dev->iobase + Master_Interrupt_Control);
+
+ return 0;
+}
+
+static int ni6527_intr_insn_bits(comedi_device *dev, comedi_subdevice *s,
+ comedi_insn *insn, lsampl_t *data)
+{
+ if(insn->n < 1)return -EINVAL;
+
+ data[1] = 0;
+ return 2;
+}
+
+static int ni6527_intr_insn_config(comedi_device *dev, comedi_subdevice *s,
+ comedi_insn *insn, lsampl_t *data)
+{
+ if(insn->n < 1)return -EINVAL;
+ if(data[0] != INSN_CONFIG_CHANGE_NOTIFY)return -EINVAL;
+
+ writeb(data[1], dev->iobase + Rising_Edge_Detection_Enable(0));
+ writeb(data[1]>>8, dev->iobase + Rising_Edge_Detection_Enable(1));
+ writeb(data[1]>>16, dev->iobase + Rising_Edge_Detection_Enable(2));
+
+ writeb(data[2], dev->iobase + Falling_Edge_Detection_Enable(0));
+ writeb(data[2]>>8, dev->iobase + Falling_Edge_Detection_Enable(1));
+ writeb(data[2]>>16, dev->iobase + Falling_Edge_Detection_Enable(2));
+
+ return 2;
+}
+
+
+static int ni6527_attach(comedi_device *dev,comedi_devconfig *it)
+{
+ comedi_subdevice *s;
+ int ret;
+
+ printk("comedi%d: ni6527:",dev->minor);
+
+ if((ret=alloc_private(dev,sizeof(ni6527_private)))<0)
+ return ret;
+
+ ret=ni6527_find_device(dev,it->options[0],it->options[1]);
+ if(ret<0)return ret;
+
+ ret = mite_setup(devpriv->mite);
+ if(ret < 0)
+ {
+ printk("error setting up mite\n");
+ return ret;
+ }
+ dev->iobase = mite_iobase(devpriv->mite);
+
+ dev->board_name=this_board->name;
+ dev->irq=mite_irq(devpriv->mite);
+ printk(" %s",dev->board_name);
+
+ printk(" ID=0x%02x", readb(dev->iobase + ID_Register));
+
+ dev->n_subdevices=2;
+ if((ret=alloc_subdevices(dev))<0)
+ return ret;
+
+ s=dev->subdevices+0;
+ s->type=COMEDI_SUBD_DI;
+ s->subdev_flags=SDF_READABLE;
+ s->n_chan=24;
+ s->range_table=&range_digital;
+ s->maxdata=1;
+ s->insn_config = ni6527_di_insn_config;
+ s->insn_bits = ni6527_di_insn_bits;
+
+ s=dev->subdevices+1;
+ s->type=COMEDI_SUBD_DO;
+ s->subdev_flags=SDF_READABLE|SDF_WRITABLE;
+ s->n_chan=24;
+ s->range_table=&range_unknown; /* FIXME: actually conductance */
+ s->maxdata=1;
+ s->insn_bits = ni6527_do_insn_bits;
+
+ s=dev->subdevices + 2;
+ dev->read_subdev = s;
+ s->type=COMEDI_SUBD_DI;
+ s->subdev_flags=SDF_READABLE;
+ s->n_chan=1;
+ s->range_table=&range_unknown;
+ s->maxdata=1;
+ s->do_cmdtest = ni6527_intr_cmdtest;
+ s->do_cmd = ni6527_intr_cmd;
+ s->cancel = ni6527_intr_cancel;
+ s->insn_bits = ni6527_intr_insn_bits;
+ s->insn_config = ni6527_intr_insn_config;
+
+ writeb(0x00, dev->iobase + Filter_Enable(0));
+ writeb(0x00, dev->iobase + Filter_Enable(1));
+ writeb(0x00, dev->iobase + Filter_Enable(2));
+
+ writeb(ClrEdge|ClrOverflow|ClrFilter|ClrInterval,
+ dev->iobase + Clear_Register);
+ writeb(0x00, dev->iobase + Master_Interrupt_Control);
+
+ ret=comedi_request_irq(dev->irq,ni6527_interrupt,SA_SHIRQ,"ni6527",dev);
+ if(ret<0){
+ dev->irq=0;
+ printk(" irq not available");
+ }
+
+ printk("\n");
+
+ return 0;
+}
+
+static int ni6527_detach(comedi_device *dev)
+{
+ if(dev->iobase){
+ writeb(0x00, dev->iobase + Master_Interrupt_Control);
+ }
+
+ if(dev->irq){
+ comedi_free_irq(dev->irq,dev);
+ }
+
+ if(devpriv && devpriv->mite){
+ mite_unsetup(devpriv->mite);
+ }
+
+ return 0;
+}
+
+static int ni6527_find_device(comedi_device *dev,int bus,int slot)
+{
+ struct mite_struct *mite;
+ int i;
+
+ for(mite=mite_devices;mite;mite=mite->next){
+ if(mite->used)continue;
+ if(bus || slot){
+ if(bus!=mite->pcidev->bus->number ||
+ slot!=PCI_SLOT(mite->pcidev->devfn))
+ continue;
+ }
+ for(i=0;i<n_ni6527_boards;i++){
+ if(mite_device_id(mite)==ni6527_boards[i].dev_id){
+ dev->board_ptr = ni6527_boards + i;
+ devpriv->mite=mite;
+ return 0;
+ }
+ }
+ }
+ printk("no device found\n");
+ mite_list_devices();
+ return -EIO;
+}
+