diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/comedi_parport.c')
-rw-r--r-- | drivers/staging/comedi/drivers/comedi_parport.c | 380 |
1 files changed, 177 insertions, 203 deletions
diff --git a/drivers/staging/comedi/drivers/comedi_parport.c b/drivers/staging/comedi/drivers/comedi_parport.c index f28a15f0274..9de81c7712f 100644 --- a/drivers/staging/comedi/drivers/comedi_parport.c +++ b/drivers/staging/comedi/drivers/comedi_parport.c @@ -1,168 +1,153 @@ /* - 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. -*/ -/* -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. -*/ + * comedi_parport.c + * Comedi driver for standard parallel port + * + * For more information see: + * http://retired.beyondlogic.org/spp/parallel.htm + * + * 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. + */ + /* - see http://www.beyondlogic.org/ for information. - or http://www.linux-magazin.de/ausgabe/1999/10/IO/io.html + * 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 (optional) + * + * Parallel Port Lines: + * + * pin subdev chan type name + * ----- ------ ---- ---- -------------- + * 1 2 0 DO strobe + * 2 0 0 DIO data 0 + * 3 0 1 DIO data 1 + * 4 0 2 DIO data 2 + * 5 0 3 DIO data 3 + * 6 0 4 DIO data 4 + * 7 0 5 DIO data 5 + * 8 0 6 DIO data 6 + * 9 0 7 DIO data 7 + * 10 1 3 DI ack + * 11 1 4 DI busy + * 12 1 2 DI paper out + * 13 1 1 DI select in + * 14 2 1 DO auto LF + * 15 1 0 DI error + * 16 2 2 DO init + * 17 2 3 DO select printer + * 18-25 ground + * + * When an IRQ is configured 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 trigger, which can be used to wake up tasks. */ #include <linux/module.h> -#include "../comedidev.h" #include <linux/interrupt.h> -#include "comedi_fc.h" - -#define PARPORT_SIZE 3 - -#define PARPORT_A 0 -#define PARPORT_B 1 -#define PARPORT_C 2 +#include "../comedidev.h" -struct parport_private { - unsigned int a_data; - unsigned int c_data; - int enable_irq; -}; +#include "comedi_fc.h" -static int parport_insn_a(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) +/* + * Register map + */ +#define PARPORT_DATA_REG 0x00 +#define PARPORT_STATUS_REG 0x01 +#define PARPORT_CTRL_REG 0x02 +#define PARPORT_CTRL_IRQ_ENA (1 << 4) +#define PARPORT_CTRL_BIDIR_ENA (1 << 5) + +static int parport_data_reg_insn_bits(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) { - struct parport_private *devpriv = dev->private; - - if (data[0]) { - devpriv->a_data &= ~data[0]; - devpriv->a_data |= (data[0] & data[1]); - - outb(devpriv->a_data, dev->iobase + PARPORT_A); - } + if (comedi_dio_update_state(s, data)) + outb(s->state, dev->iobase + PARPORT_DATA_REG); - data[1] = inb(dev->iobase + PARPORT_A); + data[1] = inb(dev->iobase + PARPORT_DATA_REG); return insn->n; } -static int parport_insn_config_a(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) +static int parport_data_reg_insn_config(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) { - struct parport_private *devpriv = dev->private; - - 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); + unsigned int ctrl; + int ret; + + ret = comedi_dio_insn_config(dev, s, insn, data, 0xff); + if (ret) + return ret; + + ctrl = inb(dev->iobase + PARPORT_CTRL_REG); + if (s->io_bits) + ctrl &= ~PARPORT_CTRL_BIDIR_ENA; + else + ctrl |= PARPORT_CTRL_BIDIR_ENA; + outb(ctrl, dev->iobase + PARPORT_CTRL_REG); - return 1; + return insn->n; } -static int parport_insn_b(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) +static int parport_status_reg_insn_bits(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) { - if (data[0]) { - /* should writes be ignored? */ - /* anyone??? */ - } - - data[1] = (inb(dev->iobase + PARPORT_B) >> 3); + data[1] = inb(dev->iobase + PARPORT_STATUS_REG) >> 3; return insn->n; } -static int parport_insn_c(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) +static int parport_ctrl_reg_insn_bits(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) { - struct parport_private *devpriv = dev->private; - - data[0] &= 0x0f; - if (data[0]) { - devpriv->c_data &= ~data[0]; - devpriv->c_data |= (data[0] & data[1]); + unsigned int ctrl; - outb(devpriv->c_data, dev->iobase + PARPORT_C); + if (comedi_dio_update_state(s, data)) { + ctrl = inb(dev->iobase + PARPORT_CTRL_REG); + ctrl &= (PARPORT_CTRL_IRQ_ENA | PARPORT_CTRL_BIDIR_ENA); + ctrl |= s->state; + outb(ctrl, dev->iobase + PARPORT_CTRL_REG); } - data[1] = devpriv->c_data & 0xf; + data[1] = s->state; return insn->n; } -static int parport_intr_insn(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) +static int parport_intr_insn_bits(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) { data[1] = 0; return insn->n; @@ -213,12 +198,11 @@ static int parport_intr_cmdtest(struct comedi_device *dev, static int parport_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s) { - struct parport_private *devpriv = dev->private; + unsigned int ctrl; - devpriv->c_data |= 0x10; - outb(devpriv->c_data, dev->iobase + PARPORT_C); - - devpriv->enable_irq = 1; + ctrl = inb(dev->iobase + PARPORT_CTRL_REG); + ctrl |= PARPORT_CTRL_IRQ_ENA; + outb(ctrl, dev->iobase + PARPORT_CTRL_REG); return 0; } @@ -226,12 +210,11 @@ static int parport_intr_cmd(struct comedi_device *dev, static int parport_intr_cancel(struct comedi_device *dev, struct comedi_subdevice *s) { - struct parport_private *devpriv = dev->private; - - devpriv->c_data &= ~0x10; - outb(devpriv->c_data, dev->iobase + PARPORT_C); + unsigned int ctrl; - devpriv->enable_irq = 0; + ctrl = inb(dev->iobase + PARPORT_CTRL_REG); + ctrl &= ~PARPORT_CTRL_IRQ_ENA; + outb(ctrl, dev->iobase + PARPORT_CTRL_REG); return 0; } @@ -239,10 +222,11 @@ static int parport_intr_cancel(struct comedi_device *dev, static irqreturn_t parport_interrupt(int irq, void *d) { struct comedi_device *dev = d; - struct parport_private *devpriv = dev->private; - struct comedi_subdevice *s = &dev->subdevices[3]; + struct comedi_subdevice *s = dev->read_subdev; + unsigned int ctrl; - if (!devpriv->enable_irq) + ctrl = inb(dev->iobase + PARPORT_CTRL_REG); + if (!(ctrl & PARPORT_CTRL_IRQ_ENA)) return IRQ_NONE; comedi_buf_put(s->async, 0); @@ -255,79 +239,69 @@ static irqreturn_t parport_interrupt(int irq, void *d) static int parport_attach(struct comedi_device *dev, struct comedi_devconfig *it) { - struct parport_private *devpriv; struct comedi_subdevice *s; - unsigned int irq; int ret; - ret = comedi_request_region(dev, it->options[0], PARPORT_SIZE); + ret = comedi_request_region(dev, it->options[0], 0x03); if (ret) return ret; - irq = it->options[1]; - if (irq) { - ret = request_irq(irq, parport_interrupt, 0, dev->board_name, - dev); - if (ret < 0) { - dev_err(dev->class_dev, "irq not available\n"); - return -EINVAL; - } - dev->irq = irq; + if (it->options[1]) { + ret = request_irq(it->options[1], parport_interrupt, 0, + dev->board_name, dev); + if (ret == 0) + dev->irq = it->options[1]; } - ret = comedi_alloc_subdevices(dev, 4); + ret = comedi_alloc_subdevices(dev, dev->irq ? 4 : 3); if (ret) return ret; - devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); - if (!devpriv) - return -ENOMEM; - + /* Digial I/O subdevice - Parallel port DATA register */ 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->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_data_reg_insn_bits; + s->insn_config = parport_data_reg_insn_config; + + /* Digial Input subdevice - Parallel port STATUS register */ 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->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_status_reg_insn_bits; + + /* Digial Output subdevice - Parallel port CONTROL register */ 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) { + 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_ctrl_reg_insn_bits; + + if (dev->irq) { + /* Digial Input subdevice - Interrupt support */ + s = &dev->subdevices[3]; 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; + 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_bits; + s->do_cmdtest = parport_intr_cmdtest; + s->do_cmd = parport_intr_cmd; + s->cancel = parport_intr_cancel; } - devpriv->a_data = 0; - outb(devpriv->a_data, dev->iobase + PARPORT_A); - devpriv->c_data = 0; - outb(devpriv->c_data, dev->iobase + PARPORT_C); + outb(0, dev->iobase + PARPORT_DATA_REG); + outb(0, dev->iobase + PARPORT_CTRL_REG); return 0; } @@ -341,5 +315,5 @@ static struct comedi_driver parport_driver = { module_comedi_driver(parport_driver); MODULE_AUTHOR("Comedi http://www.comedi.org"); -MODULE_DESCRIPTION("Comedi low-level driver"); +MODULE_DESCRIPTION("Comedi: Standard parallel port driver"); MODULE_LICENSE("GPL"); |