--- /dev/null
+/*
+ module/c6xdigio.c
+ hardware driver for Mechatronic Systems Inc. C6x_DIGIO DSP daughter card.
+ (http://robot0.ge.uiuc.edu/~spong/mecha/)
+
+ COMEDI - Linux Control and Measurement Device Interface
+ Copyright (C) 1999 Dan Block
+
+ 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.
+
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/errno.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 <linux/comedidev.h>
+#include <linux/pnp.h>
+
+static u8 ReadByteFromHwPort(int addr)
+{
+ u8 result = inb(addr);
+ return result;
+}
+
+static void WriteByteToHwPort(int addr, u8 val)
+{
+ outb_p(val, addr);
+}
+#define C6XDIGIO_SIZE 3
+
+/*
+ * port offsets
+ */
+#define C6XDIGIO_PARALLEL_DATA 0
+#define C6XDIGIO_PARALLEL_STATUS 1
+#define C6XDIGIO_PARALLEL_CONTROL 2
+struct pwmbitstype {
+ unsigned sb0: 2;
+ unsigned sb1: 2;
+ unsigned sb2: 2;
+ unsigned sb3: 2;
+ unsigned sb4: 2;
+};
+union pwmcmdtype {
+ unsigned cmd; // assuming here that int is 32bit
+ struct pwmbitstype bits;
+};
+struct encbitstype {
+ unsigned sb0: 3;
+ unsigned sb1: 3;
+ unsigned sb2: 3;
+ unsigned sb3: 3;
+ unsigned sb4: 3;
+ unsigned sb5: 3;
+ unsigned sb6: 3;
+ unsigned sb7: 3;
+};
+union encvaluetype {
+ unsigned value;
+ struct encbitstype bits;
+};
+
+#define C6XDIGIO_TIME_OUT 20
+
+static int c6xdigio_attach(comedi_device *dev,comedi_devconfig *it);
+static int c6xdigio_detach(comedi_device *dev);
+comedi_driver driver_c6xdigio={
+ driver_name: "c6xdigio",
+ module: THIS_MODULE,
+ attach: c6xdigio_attach,
+ detach: c6xdigio_detach,
+};
+
+static void C6X_pwmInit( unsigned baseAddr ) {
+ int timeout=0;
+
+//printk("Inside C6X_pwmInit\n");
+
+ WriteByteToHwPort(baseAddr,0x70);
+ while ( ((ReadByteFromHwPort(baseAddr+1) & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ timeout++;
+ }
+
+ WriteByteToHwPort(baseAddr,0x74);
+ timeout=0;
+ while ( ((ReadByteFromHwPort(baseAddr+1) & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ timeout++;
+ }
+
+ WriteByteToHwPort(baseAddr,0x70);
+ timeout=0;
+ while ( ((ReadByteFromHwPort(baseAddr+1) & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ timeout++;
+ }
+
+ WriteByteToHwPort(baseAddr,0x0);
+ timeout=0;
+ while ( ((ReadByteFromHwPort(baseAddr+1) & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ timeout++;
+ }
+
+}
+
+static void C6X_pwmOutput( unsigned baseAddr, unsigned channel, int value )
+{
+ unsigned ppcmd;
+ union pwmcmdtype pwm;
+ int timeout=0;
+ unsigned tmp;
+
+
+ //printk("Inside C6X_pwmOutput\n");
+
+ pwm.cmd = value;
+ if (pwm.cmd > 498)
+ pwm.cmd = 498;
+ if (pwm.cmd < 2)
+ pwm.cmd = 2;
+
+ if (channel == 0) {
+ ppcmd = 0x28;
+ } else { // if channel == 1
+ ppcmd = 0x30;
+ } /* endif */
+
+ WriteByteToHwPort(baseAddr,ppcmd+pwm.bits.sb0);
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ while ( ((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ timeout++;
+ }
+
+ WriteByteToHwPort(baseAddr,ppcmd+pwm.bits.sb1+0x4);
+ timeout=0;
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ while ( ((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ timeout++;
+ }
+
+ WriteByteToHwPort(baseAddr,ppcmd+pwm.bits.sb2);
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ while ( ((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ timeout++;
+ }
+
+ WriteByteToHwPort(baseAddr,ppcmd+pwm.bits.sb3+0x4);
+ timeout=0;
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ while ( ((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ timeout++;
+ }
+
+ WriteByteToHwPort(baseAddr,ppcmd+pwm.bits.sb4);
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ while ( ((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ timeout++;
+ }
+
+ WriteByteToHwPort(baseAddr,0x0);
+ timeout=0;
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ while ( ((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ timeout++;
+ }
+
+}
+
+static int C6X_encInput( unsigned baseAddr, unsigned channel)
+{
+ unsigned ppcmd;
+ union encvaluetype enc;
+ int timeout=0;
+ int tmp;
+
+
+ //printk("Inside C6X_encInput\n");
+
+ enc.value = 0;
+ if (channel == 0) {
+ ppcmd = 0x48;
+ } else {
+ ppcmd = 0x50;
+ }
+ WriteByteToHwPort(baseAddr,ppcmd);
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ while ( ((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ timeout++;
+ }
+
+ enc.bits.sb0 = ((ReadByteFromHwPort(baseAddr+1) >> 3) & 0x7);
+ WriteByteToHwPort(baseAddr,ppcmd+0x4);
+ timeout=0;
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ while ( ((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ timeout++;
+ }
+ enc.bits.sb1 = ((ReadByteFromHwPort(baseAddr+1) >> 3) & 0x7);
+ WriteByteToHwPort(baseAddr,ppcmd);
+ timeout=0;
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ while ( ((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ timeout++;
+ }
+ enc.bits.sb2 = ((ReadByteFromHwPort(baseAddr+1) >> 3) & 0x7);
+ WriteByteToHwPort(baseAddr,ppcmd+0x4);
+ timeout=0;
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ while ( ((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ timeout++;
+ }
+ enc.bits.sb3 = ((ReadByteFromHwPort(baseAddr+1) >> 3) & 0x7);
+ WriteByteToHwPort(baseAddr,ppcmd);
+ timeout=0;
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ while ( ((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ timeout++;
+ }
+ enc.bits.sb4 = ((ReadByteFromHwPort(baseAddr+1) >> 3) & 0x7);
+ WriteByteToHwPort(baseAddr,ppcmd+0x4);
+ timeout=0;
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ while ( ((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ timeout++;
+ }
+ enc.bits.sb5 = ((ReadByteFromHwPort(baseAddr+1) >> 3) & 0x7);
+ WriteByteToHwPort(baseAddr,ppcmd);
+ timeout=0;
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ while ( ((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ timeout++;
+ }
+ enc.bits.sb6 = ((ReadByteFromHwPort(baseAddr+1) >> 3) & 0x7);
+ WriteByteToHwPort(baseAddr,ppcmd+0x4);
+ timeout=0;
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ while ( ((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ timeout++;
+ }
+ enc.bits.sb7 = ((ReadByteFromHwPort(baseAddr+1) >> 3) & 0x7);
+ WriteByteToHwPort(baseAddr,ppcmd);
+ timeout=0;
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ while ( ((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ timeout++;
+ }
+
+ WriteByteToHwPort(baseAddr,0x0);
+ timeout=0;
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ while ( ((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ tmp = ReadByteFromHwPort(baseAddr+1);
+ timeout++;
+ }
+
+ return(enc.value ^ 0x800000);
+}
+
+static void C6X_encResetAll( unsigned baseAddr )
+{
+ unsigned timeout=0;
+
+//printk("Inside C6X_encResetAll\n");
+
+ WriteByteToHwPort(baseAddr,0x68);
+ while ( ((ReadByteFromHwPort(baseAddr+1) & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ timeout++;
+ }
+ WriteByteToHwPort(baseAddr,0x6C);
+ timeout=0;
+ while ( ((ReadByteFromHwPort(baseAddr+1) & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ timeout++;
+ }
+ WriteByteToHwPort(baseAddr,0x68);
+ timeout=0;
+ while ( ((ReadByteFromHwPort(baseAddr+1) & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ timeout++;
+ }
+ WriteByteToHwPort(baseAddr,0x0);
+ timeout=0;
+ while ( ((ReadByteFromHwPort(baseAddr+1) & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT) ) {
+ timeout++;
+ }
+}
+
+
+static int c6xdigio_pwmo_insn_read(comedi_device *dev,
+ comedi_subdevice *s,
+ comedi_insn *insn,
+ lsampl_t *data)
+{
+ printk("c6xdigio_pwmo_insn_read %x\n", insn->n);
+ return insn->n;
+}
+
+static int c6xdigio_pwmo_insn_write(comedi_device *dev,
+ comedi_subdevice *s,
+ comedi_insn *insn,
+ lsampl_t *data)
+{
+ int i;
+ int chan = CR_CHAN(insn->chanspec);
+
+ // printk("c6xdigio_pwmo_insn_write %x\n", insn->n);
+ for(i=0;i<insn->n;i++){
+ C6X_pwmOutput(dev->iobase, chan, data[i]);
+ /* devpriv->ao_readback[chan] = data[i];*/
+ }
+ return i;
+}
+
+
+//static int c6xdigio_ei_init_insn_read(comedi_device *dev,
+// comedi_subdevice *s,
+// comedi_insn *insn,
+// lsampl_t *data)
+//{
+// printk("c6xdigio_ei_init_insn_read %x\n", insn->n);
+// return insn->n;
+//}
+
+//static int c6xdigio_ei_init_insn_write(comedi_device *dev,
+// comedi_subdevice *s,
+// comedi_insn *insn,
+// lsampl_t *data)
+//{
+// int i;
+// int chan = CR_CHAN(insn->chanspec);
+//
+// C6X_encResetAll( dev->iobase );
+//
+// return insn->n;
+//}
+
+static int c6xdigio_ei_insn_read(comedi_device *dev,
+ comedi_subdevice *s,
+ comedi_insn *insn,
+ lsampl_t *data)
+{
+ // printk("c6xdigio_ei__insn_read %x\n", insn->n);
+ int n;
+ int chan = CR_CHAN(insn->chanspec);
+
+ for(n=0;n<insn->n;n++){
+ data[n] = (C6X_encInput(dev->iobase,chan) & 0xffffff);
+ }
+
+ return n;
+}
+
+
+static void board_init(comedi_device *dev) {
+
+ //printk("Inside board_init\n");
+
+ C6X_pwmInit(dev->iobase);
+ C6X_encResetAll(dev->iobase);
+
+}
+
+//static void board_halt(comedi_device *dev) {
+// C6X_pwmInit(dev->iobase);
+//}
+
+/*
+ options[0] - I/O port
+ options[1] - irq
+ options[2] - number of encoder chips installed
+ */
+
+static const struct pnp_device_id c6xdigio_pnp_tbl[] = {
+ /* Standard LPT Printer Port */
+ {.id = "PNP0400", .driver_data = 0},
+ /* ECP Printer Port */
+ {.id = "PNP0401", .driver_data = 0},
+ { }
+};
+
+static struct pnp_driver c6xdigio_pnp_driver = {
+ .name = "c6xdigio",
+ .id_table = c6xdigio_pnp_tbl,
+};
+
+static int c6xdigio_attach(comedi_device * dev, comedi_devconfig * it)
+{
+ int result = 0;
+ int iobase;
+ int irq;
+ comedi_subdevice *s;
+
+ iobase = it->options[0];
+ printk("comedi%d: c6xdigio: 0x%04x\n", dev->minor, iobase);
+ if (!request_region(iobase, C6XDIGIO_SIZE, "c6xdigio")) {
+ printk("comedi%d: I/O port conflict\n", dev->minor);
+ return -EIO;
+ }
+ dev->iobase = iobase;
+ dev->board_name = "c6xdigio";
+
+ result = alloc_subdevices(dev, 2); // 3 with encoder_init write
+ if(result<0)return result;
+
+ // Make sure that PnP ports gets activated
+ pnp_register_driver (&c6xdigio_pnp_driver);
+
+
+ irq = it->options[1];
+ if (irq > 0) {
+ printk("comedi%d: irq = %d ignored\n", dev->minor, irq);
+ } else if(irq == 0) {
+ printk("comedi%d: no irq\n", dev->minor);
+ }
+
+
+ s = dev->subdevices + 0;
+ /* pwm output subdevice */
+ s->type = COMEDI_SUBD_AO; // Not sure what to put here
+ s->subdev_flags = SDF_WRITEABLE;
+ s->n_chan = 2;
+ /* s->trig[0] = c6xdigio_pwmo;*/
+ s->insn_read = c6xdigio_pwmo_insn_read;
+ s->insn_write = c6xdigio_pwmo_insn_write;
+ s->maxdata = 500;
+ s->range_table = &range_bipolar10; // A suitable lie
+
+ s = dev->subdevices + 1;
+ /* encoder (counter) subdevice */
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
+ s->n_chan = 2;
+ /* s->trig[0] = c6xdigio_ei;*/
+ s->insn_read = c6xdigio_ei_insn_read;
+ s->maxdata = 0xffffff;
+ s->range_table = &range_unknown;
+
+ // s = dev->subdevices + 2;
+ // /* pwm output subdevice */
+ // s->type = COMEDI_SUBD_COUNTER; // Not sure what to put here
+ // s->subdev_flags = SDF_WRITEABLE;
+ // s->n_chan = 1;
+ // /* s->trig[0] = c6xdigio_ei_init; */
+ // s->insn_read = c6xdigio_ei_init_insn_read;
+ // s->insn_write = c6xdigio_ei_init_insn_write;
+ // s->maxdata = 0xFFFF; // Really just a don't care
+ // s->range_table = &range_unknown; // Not sure what to put here
+
+ // I will call this init anyway but more than likely the DSP board will not be connect
+ // when device driver is loaded.
+ board_init(dev);
+
+
+ return 0;
+}
+
+
+static int c6xdigio_detach(comedi_device * dev)
+{
+// board_halt(dev); // may not need this
+
+ printk("comedi%d: c6xdigio: remove\n", dev->minor);
+
+ if (dev->iobase) { release_region(dev->iobase, C6XDIGIO_SIZE); }
+ if (dev->irq) { free_irq(dev->irq,dev); } // Not using IRQ so I am not sure if I need this
+ pnp_unregister_driver (&c6xdigio_pnp_driver);
+
+ return 0;
+}
+
+
+#ifdef MODULE
+int init_module(void)
+{
+ comedi_driver_register(&driver_c6xdigio);
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ comedi_driver_unregister(&driver_c6xdigio);
+}
+#endif
+
+
+