]> Pileus Git - ~andy/linux/commitdiff
Staging: comedi: add comedi_parport driver
authorDavid Schleef <ds@schleef.org>
Fri, 14 Nov 2008 23:03:44 +0000 (15:03 -0800)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 6 Jan 2009 21:52:20 +0000 (13:52 -0800)
This adds the comedi_parport driver to the kernel tree

From: David Schleef <ds@schleef.org>
Cc: Frank Mori Hess <fmhess@users.sourceforge.net>
Cc: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/staging/comedi/drivers/Makefile
drivers/staging/comedi/drivers/comedi_parport.c [new file with mode: 0644]

index 95d1f462674710ebb05ca3f656932177ba04da47..158660994765e9bbc9326b2fbda8be808000fd9c 100644 (file)
@@ -5,6 +5,7 @@
 obj-$(CONFIG_COMEDI)                   += comedi_fc.o
 obj-$(CONFIG_COMEDI)                   += comedi_bond.o
 obj-$(CONFIG_COMEDI)                   += comedi_test.o
+obj-$(CONFIG_COMEDI)                   += comedi_parport.o
 
 # Comedi PCI drivers
 obj-$(CONFIG_COMEDI_PCI_DRIVERS)       += mite.o
diff --git a/drivers/staging/comedi/drivers/comedi_parport.c b/drivers/staging/comedi/drivers/comedi_parport.c
new file mode 100644 (file)
index 0000000..9530c47
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+    comedi/drivers/comedi_parport.c
+    hardware driver for standard parallel port
+
+    COMEDI - Linux Control and Measurement Device Interface
+    Copyright (C) 1998,2001 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: comedi_parport
+Description: Standard PC parallel port
+Author: ds
+Status: works in immediate mode
+Devices: [standard] parallel port (comedi_parport)
+Updated: Tue, 30 Apr 2002 21:11:45 -0700
+
+A cheap and easy way to get a few more digital I/O lines.  Steal
+additional parallel ports from old computers or your neighbors'
+computers.
+
+Option list:
+ 0: I/O port base for the parallel port.
+ 1: IRQ
+
+Parallel Port Lines:
+
+pin     subdev  chan    aka
+---     ------  ----    ---
+1       2       0       strobe
+2       0       0       data 0
+3       0       1       data 1
+4       0       2       data 2
+5       0       3       data 3
+6       0       4       data 4
+7       0       5       data 5
+8       0       6       data 6
+9       0       7       data 7
+10      1       3       acknowledge
+11      1       4       busy
+12      1       2       output
+13      1       1       printer selected
+14      2       1       auto LF
+15      1       0       error
+16      2       2       init
+17      2       3       select printer
+18-25   ground
+
+Notes:
+
+Subdevices 0 is digital I/O, subdevice 1 is digital input, and
+subdevice 2 is digital output.  Unlike other Comedi devices,
+subdevice 0 defaults to output.
+
+Pins 13 and 14 are inverted once by Comedi and once by the
+hardware, thus cancelling the effect.
+
+Pin 1 is a strobe, thus acts like one.  There's no way in software
+to change this, at least on a standard parallel port.
+
+Subdevice 3 pretends to be a digital input subdevice, but it always
+returns 0 when read.  However, if you run a command with
+scan_begin_src=TRIG_EXT, it uses pin 10 as a external triggering
+pin, which can be used to wake up tasks.
+*/
+/*
+   see http://www.beyondlogic.org/ for information.
+   or http://www.linux-magazin.de/ausgabe/1999/10/IO/io.html
+ */
+
+#include "../comedidev.h"
+#include <linux/ioport.h>
+
+#define PARPORT_SIZE 3
+
+#define PARPORT_A 0
+#define PARPORT_B 1
+#define PARPORT_C 2
+
+static int parport_attach(comedi_device * dev, comedi_devconfig * it);
+static int parport_detach(comedi_device * dev);
+static comedi_driver driver_parport = {
+      driver_name:"comedi_parport",
+      module:THIS_MODULE,
+      attach:parport_attach,
+      detach:parport_detach,
+};
+
+COMEDI_INITCLEANUP(driver_parport);
+
+typedef struct parport_private_struct {
+       unsigned int a_data;
+       unsigned int c_data;
+       int enable_irq;
+} parport_private;
+#define devpriv ((parport_private *)(dev->private))
+
+static int parport_insn_a(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data)
+{
+       if (data[0]) {
+               devpriv->a_data &= ~data[0];
+               devpriv->a_data |= (data[0] & data[1]);
+
+               outb(devpriv->a_data, dev->iobase + PARPORT_A);
+       }
+
+       data[1] = inb(dev->iobase + PARPORT_A);
+
+       return 2;
+}
+
+static int parport_insn_config_a(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data)
+{
+       if (data[0]) {
+               s->io_bits = 0xff;
+               devpriv->c_data &= ~(1 << 5);
+       } else {
+               s->io_bits = 0;
+               devpriv->c_data |= (1 << 5);
+       }
+       outb(devpriv->c_data, dev->iobase + PARPORT_C);
+
+       return 1;
+}
+
+static int parport_insn_b(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data)
+{
+       if (data[0]) {
+               // should writes be ignored?
+       }
+
+       data[1] = (inb(dev->iobase + PARPORT_B) >> 3);
+
+       return 2;
+}
+
+static int parport_insn_c(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data)
+{
+       data[0] &= 0x0f;
+       if (data[0]) {
+               devpriv->c_data &= ~data[0];
+               devpriv->c_data |= (data[0] & data[1]);
+
+               outb(devpriv->c_data, dev->iobase + PARPORT_C);
+       }
+
+       data[1] = devpriv->c_data & 0xf;
+
+       return 2;
+}
+
+static int parport_intr_insn(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 parport_intr_cmdtest(comedi_device * dev, comedi_subdevice * s,
+       comedi_cmd * cmd)
+{
+       int err = 0;
+       int tmp;
+
+       /* step 1 */
+
+       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_EXT;
+       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_NONE;
+       if (!cmd->stop_src || tmp != cmd->stop_src)
+               err++;
+
+       if (err)
+               return 1;
+
+       /* step 2: ignored */
+
+       if (err)
+               return 2;
+
+       /* step 3: */
+
+       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: ignored */
+
+       if (err)
+               return 4;
+
+       return 0;
+}
+
+static int parport_intr_cmd(comedi_device * dev, comedi_subdevice * s)
+{
+       devpriv->c_data |= 0x10;
+       outb(devpriv->c_data, dev->iobase + PARPORT_C);
+
+       devpriv->enable_irq = 1;
+
+       return 0;
+}
+
+static int parport_intr_cancel(comedi_device * dev, comedi_subdevice * s)
+{
+       printk("parport_intr_cancel()\n");
+
+       devpriv->c_data &= ~0x10;
+       outb(devpriv->c_data, dev->iobase + PARPORT_C);
+
+       devpriv->enable_irq = 0;
+
+       return 0;
+}
+
+static irqreturn_t parport_interrupt(int irq, void *d PT_REGS_ARG)
+{
+       comedi_device *dev = d;
+       comedi_subdevice *s = dev->subdevices + 3;
+
+       if (!devpriv->enable_irq) {
+               printk("comedi_parport: bogus irq, ignored\n");
+               return IRQ_NONE;
+       }
+
+       comedi_buf_put(s->async, 0);
+       s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
+
+       comedi_event(dev, s);
+       return IRQ_HANDLED;
+}
+
+static int parport_attach(comedi_device * dev, comedi_devconfig * it)
+{
+       int ret;
+       unsigned int irq;
+       unsigned long iobase;
+       comedi_subdevice *s;
+
+       iobase = it->options[0];
+       printk("comedi%d: parport: 0x%04lx ", dev->minor, iobase);
+       if (!request_region(iobase, PARPORT_SIZE, "parport (comedi)")) {
+               printk("I/O port conflict\n");
+               return -EIO;
+       }
+       dev->iobase = iobase;
+
+       irq = it->options[1];
+       if (irq) {
+               printk(" irq=%u", irq);
+               ret = comedi_request_irq(irq, parport_interrupt, 0,
+                       "comedi_parport", dev);
+               if (ret < 0) {
+                       printk(" irq not available\n");
+                       return -EINVAL;
+               }
+               dev->irq = irq;
+       }
+       dev->board_name = "parport";
+
+       if ((ret = alloc_subdevices(dev, 4)) < 0)
+               return ret;
+       if ((ret = alloc_private(dev, sizeof(parport_private))) < 0)
+               return ret;
+
+       s = dev->subdevices + 0;
+       s->type = COMEDI_SUBD_DIO;
+       s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+       s->n_chan = 8;
+       s->maxdata = 1;
+       s->range_table = &range_digital;
+       s->insn_bits = parport_insn_a;
+       s->insn_config = parport_insn_config_a;
+
+       s = dev->subdevices + 1;
+       s->type = COMEDI_SUBD_DI;
+       s->subdev_flags = SDF_READABLE;
+       s->n_chan = 5;
+       s->maxdata = 1;
+       s->range_table = &range_digital;
+       s->insn_bits = parport_insn_b;
+
+       s = dev->subdevices + 2;
+       s->type = COMEDI_SUBD_DO;
+       s->subdev_flags = SDF_WRITABLE;
+       s->n_chan = 4;
+       s->maxdata = 1;
+       s->range_table = &range_digital;
+       s->insn_bits = parport_insn_c;
+
+       s = dev->subdevices + 3;
+       if (irq) {
+               dev->read_subdev = s;
+               s->type = COMEDI_SUBD_DI;
+               s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
+               s->n_chan = 1;
+               s->maxdata = 1;
+               s->range_table = &range_digital;
+               s->insn_bits = parport_intr_insn;
+               s->do_cmdtest = parport_intr_cmdtest;
+               s->do_cmd = parport_intr_cmd;
+               s->cancel = parport_intr_cancel;
+       } else {
+               s->type = COMEDI_SUBD_UNUSED;
+       }
+
+       devpriv->a_data = 0;
+       outb(devpriv->a_data, dev->iobase + PARPORT_A);
+       devpriv->c_data = 0;
+       outb(devpriv->c_data, dev->iobase + PARPORT_C);
+
+       printk("\n");
+       return 1;
+}
+
+static int parport_detach(comedi_device * dev)
+{
+       printk("comedi%d: parport: remove\n", dev->minor);
+
+       if (dev->iobase)
+               release_region(dev->iobase, PARPORT_SIZE);
+
+       if (dev->irq)
+               comedi_free_irq(dev->irq, dev);
+
+       return 0;
+}