linux/drivers/comedi/drivers/comedi_parport.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * comedi_parport.c
   4 * Comedi driver for standard parallel port
   5 *
   6 * For more information see:
   7 *      http://retired.beyondlogic.org/spp/parallel.htm
   8 *
   9 * COMEDI - Linux Control and Measurement Device Interface
  10 * Copyright (C) 1998,2001 David A. Schleef <ds@schleef.org>
  11 */
  12
  13/*
  14 * Driver: comedi_parport
  15 * Description: Standard PC parallel port
  16 * Author: ds
  17 * Status: works in immediate mode
  18 * Devices: [standard] parallel port (comedi_parport)
  19 * Updated: Tue, 30 Apr 2002 21:11:45 -0700
  20 *
  21 * A cheap and easy way to get a few more digital I/O lines. Steal
  22 * additional parallel ports from old computers or your neighbors'
  23 * computers.
  24 *
  25 * Option list:
  26 *   0: I/O port base for the parallel port.
  27 *   1: IRQ (optional)
  28 *
  29 * Parallel Port Lines:
  30 *
  31 *       pin   subdev  chan  type  name
  32 *      -----  ------  ----  ----  --------------
  33 *        1      2       0    DO   strobe
  34 *        2      0       0    DIO  data 0
  35 *        3      0       1    DIO  data 1
  36 *        4      0       2    DIO  data 2
  37 *        5      0       3    DIO  data 3
  38 *        6      0       4    DIO  data 4
  39 *        7      0       5    DIO  data 5
  40 *        8      0       6    DIO  data 6
  41 *        9      0       7    DIO  data 7
  42 *       10      1       3    DI   ack
  43 *       11      1       4    DI   busy
  44 *       12      1       2    DI   paper out
  45 *       13      1       1    DI   select in
  46 *       14      2       1    DO   auto LF
  47 *       15      1       0    DI   error
  48 *       16      2       2    DO   init
  49 *       17      2       3    DO   select printer
  50 *      18-25                      ground
  51 *
  52 * When an IRQ is configured subdevice 3 pretends to be a digital
  53 * input subdevice, but it always returns 0 when read. However, if
  54 * you run a command with scan_begin_src=TRIG_EXT, it uses pin 10
  55 * as a external trigger, which can be used to wake up tasks.
  56 */
  57
  58#include <linux/module.h>
  59#include <linux/interrupt.h>
  60
  61#include "../comedidev.h"
  62
  63/*
  64 * Register map
  65 */
  66#define PARPORT_DATA_REG        0x00
  67#define PARPORT_STATUS_REG      0x01
  68#define PARPORT_CTRL_REG        0x02
  69#define PARPORT_CTRL_IRQ_ENA    BIT(4)
  70#define PARPORT_CTRL_BIDIR_ENA  BIT(5)
  71
  72static int parport_data_reg_insn_bits(struct comedi_device *dev,
  73                                      struct comedi_subdevice *s,
  74                                      struct comedi_insn *insn,
  75                                      unsigned int *data)
  76{
  77        if (comedi_dio_update_state(s, data))
  78                outb(s->state, dev->iobase + PARPORT_DATA_REG);
  79
  80        data[1] = inb(dev->iobase + PARPORT_DATA_REG);
  81
  82        return insn->n;
  83}
  84
  85static int parport_data_reg_insn_config(struct comedi_device *dev,
  86                                        struct comedi_subdevice *s,
  87                                        struct comedi_insn *insn,
  88                                        unsigned int *data)
  89{
  90        unsigned int ctrl;
  91        int ret;
  92
  93        ret = comedi_dio_insn_config(dev, s, insn, data, 0xff);
  94        if (ret)
  95                return ret;
  96
  97        ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
  98        if (s->io_bits)
  99                ctrl &= ~PARPORT_CTRL_BIDIR_ENA;
 100        else
 101                ctrl |= PARPORT_CTRL_BIDIR_ENA;
 102        outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
 103
 104        return insn->n;
 105}
 106
 107static int parport_status_reg_insn_bits(struct comedi_device *dev,
 108                                        struct comedi_subdevice *s,
 109                                        struct comedi_insn *insn,
 110                                        unsigned int *data)
 111{
 112        data[1] = inb(dev->iobase + PARPORT_STATUS_REG) >> 3;
 113
 114        return insn->n;
 115}
 116
 117static int parport_ctrl_reg_insn_bits(struct comedi_device *dev,
 118                                      struct comedi_subdevice *s,
 119                                      struct comedi_insn *insn,
 120                                      unsigned int *data)
 121{
 122        unsigned int ctrl;
 123
 124        if (comedi_dio_update_state(s, data)) {
 125                ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
 126                ctrl &= (PARPORT_CTRL_IRQ_ENA | PARPORT_CTRL_BIDIR_ENA);
 127                ctrl |= s->state;
 128                outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
 129        }
 130
 131        data[1] = s->state;
 132
 133        return insn->n;
 134}
 135
 136static int parport_intr_insn_bits(struct comedi_device *dev,
 137                                  struct comedi_subdevice *s,
 138                                  struct comedi_insn *insn,
 139                                  unsigned int *data)
 140{
 141        data[1] = 0;
 142        return insn->n;
 143}
 144
 145static int parport_intr_cmdtest(struct comedi_device *dev,
 146                                struct comedi_subdevice *s,
 147                                struct comedi_cmd *cmd)
 148{
 149        int err = 0;
 150
 151        /* Step 1 : check if triggers are trivially valid */
 152
 153        err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
 154        err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
 155        err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
 156        err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 157        err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
 158
 159        if (err)
 160                return 1;
 161
 162        /* Step 2a : make sure trigger sources are unique */
 163        /* Step 2b : and mutually compatible */
 164
 165        /* Step 3: check if arguments are trivially valid */
 166
 167        err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 168        err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 169        err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
 170        err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 171                                           cmd->chanlist_len);
 172        err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 173
 174        if (err)
 175                return 3;
 176
 177        /* Step 4: fix up any arguments */
 178
 179        /* Step 5: check channel list if it exists */
 180
 181        return 0;
 182}
 183
 184static int parport_intr_cmd(struct comedi_device *dev,
 185                            struct comedi_subdevice *s)
 186{
 187        unsigned int ctrl;
 188
 189        ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
 190        ctrl |= PARPORT_CTRL_IRQ_ENA;
 191        outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
 192
 193        return 0;
 194}
 195
 196static int parport_intr_cancel(struct comedi_device *dev,
 197                               struct comedi_subdevice *s)
 198{
 199        unsigned int ctrl;
 200
 201        ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
 202        ctrl &= ~PARPORT_CTRL_IRQ_ENA;
 203        outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
 204
 205        return 0;
 206}
 207
 208static irqreturn_t parport_interrupt(int irq, void *d)
 209{
 210        struct comedi_device *dev = d;
 211        struct comedi_subdevice *s = dev->read_subdev;
 212        unsigned int ctrl;
 213        unsigned short val = 0;
 214
 215        ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
 216        if (!(ctrl & PARPORT_CTRL_IRQ_ENA))
 217                return IRQ_NONE;
 218
 219        comedi_buf_write_samples(s, &val, 1);
 220        comedi_handle_events(dev, s);
 221
 222        return IRQ_HANDLED;
 223}
 224
 225static int parport_attach(struct comedi_device *dev,
 226                          struct comedi_devconfig *it)
 227{
 228        struct comedi_subdevice *s;
 229        int ret;
 230
 231        ret = comedi_request_region(dev, it->options[0], 0x03);
 232        if (ret)
 233                return ret;
 234
 235        if (it->options[1]) {
 236                ret = request_irq(it->options[1], parport_interrupt, 0,
 237                                  dev->board_name, dev);
 238                if (ret == 0)
 239                        dev->irq = it->options[1];
 240        }
 241
 242        ret = comedi_alloc_subdevices(dev, dev->irq ? 4 : 3);
 243        if (ret)
 244                return ret;
 245
 246        /* Digial I/O subdevice - Parallel port DATA register */
 247        s = &dev->subdevices[0];
 248        s->type         = COMEDI_SUBD_DIO;
 249        s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 250        s->n_chan       = 8;
 251        s->maxdata      = 1;
 252        s->range_table  = &range_digital;
 253        s->insn_bits    = parport_data_reg_insn_bits;
 254        s->insn_config  = parport_data_reg_insn_config;
 255
 256        /* Digial Input subdevice - Parallel port STATUS register */
 257        s = &dev->subdevices[1];
 258        s->type         = COMEDI_SUBD_DI;
 259        s->subdev_flags = SDF_READABLE;
 260        s->n_chan       = 5;
 261        s->maxdata      = 1;
 262        s->range_table  = &range_digital;
 263        s->insn_bits    = parport_status_reg_insn_bits;
 264
 265        /* Digial Output subdevice - Parallel port CONTROL register */
 266        s = &dev->subdevices[2];
 267        s->type         = COMEDI_SUBD_DO;
 268        s->subdev_flags = SDF_WRITABLE;
 269        s->n_chan       = 4;
 270        s->maxdata      = 1;
 271        s->range_table  = &range_digital;
 272        s->insn_bits    = parport_ctrl_reg_insn_bits;
 273
 274        if (dev->irq) {
 275                /* Digial Input subdevice - Interrupt support */
 276                s = &dev->subdevices[3];
 277                dev->read_subdev = s;
 278                s->type         = COMEDI_SUBD_DI;
 279                s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
 280                s->n_chan       = 1;
 281                s->maxdata      = 1;
 282                s->range_table  = &range_digital;
 283                s->insn_bits    = parport_intr_insn_bits;
 284                s->len_chanlist = 1;
 285                s->do_cmdtest   = parport_intr_cmdtest;
 286                s->do_cmd       = parport_intr_cmd;
 287                s->cancel       = parport_intr_cancel;
 288        }
 289
 290        outb(0, dev->iobase + PARPORT_DATA_REG);
 291        outb(0, dev->iobase + PARPORT_CTRL_REG);
 292
 293        return 0;
 294}
 295
 296static struct comedi_driver parport_driver = {
 297        .driver_name    = "comedi_parport",
 298        .module         = THIS_MODULE,
 299        .attach         = parport_attach,
 300        .detach         = comedi_legacy_detach,
 301};
 302module_comedi_driver(parport_driver);
 303
 304MODULE_AUTHOR("Comedi https://www.comedi.org");
 305MODULE_DESCRIPTION("Comedi: Standard parallel port driver");
 306MODULE_LICENSE("GPL");
 307