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