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#include "comedi_fc.h" 73 74/* 75 * Register map 76 */ 77#define PARPORT_DATA_REG 0x00 78#define PARPORT_STATUS_REG 0x01 79#define PARPORT_CTRL_REG 0x02 80#define PARPORT_CTRL_IRQ_ENA (1 << 4) 81#define PARPORT_CTRL_BIDIR_ENA (1 << 5) 82 83static int parport_data_reg_insn_bits(struct comedi_device *dev, 84 struct comedi_subdevice *s, 85 struct comedi_insn *insn, 86 unsigned int *data) 87{ 88 if (comedi_dio_update_state(s, data)) 89 outb(s->state, dev->iobase + PARPORT_DATA_REG); 90 91 data[1] = inb(dev->iobase + PARPORT_DATA_REG); 92 93 return insn->n; 94} 95 96static int parport_data_reg_insn_config(struct comedi_device *dev, 97 struct comedi_subdevice *s, 98 struct comedi_insn *insn, 99 unsigned int *data) 100{ 101 unsigned int ctrl; 102 int ret; 103 104 ret = comedi_dio_insn_config(dev, s, insn, data, 0xff); 105 if (ret) 106 return ret; 107 108 ctrl = inb(dev->iobase + PARPORT_CTRL_REG); 109 if (s->io_bits) 110 ctrl &= ~PARPORT_CTRL_BIDIR_ENA; 111 else 112 ctrl |= PARPORT_CTRL_BIDIR_ENA; 113 outb(ctrl, dev->iobase + PARPORT_CTRL_REG); 114 115 return insn->n; 116} 117 118static int parport_status_reg_insn_bits(struct comedi_device *dev, 119 struct comedi_subdevice *s, 120 struct comedi_insn *insn, 121 unsigned int *data) 122{ 123 data[1] = inb(dev->iobase + PARPORT_STATUS_REG) >> 3; 124 125 return insn->n; 126} 127 128static int parport_ctrl_reg_insn_bits(struct comedi_device *dev, 129 struct comedi_subdevice *s, 130 struct comedi_insn *insn, 131 unsigned int *data) 132{ 133 unsigned int ctrl; 134 135 if (comedi_dio_update_state(s, data)) { 136 ctrl = inb(dev->iobase + PARPORT_CTRL_REG); 137 ctrl &= (PARPORT_CTRL_IRQ_ENA | PARPORT_CTRL_BIDIR_ENA); 138 ctrl |= s->state; 139 outb(ctrl, dev->iobase + PARPORT_CTRL_REG); 140 } 141 142 data[1] = s->state; 143 144 return insn->n; 145} 146 147static int parport_intr_insn_bits(struct comedi_device *dev, 148 struct comedi_subdevice *s, 149 struct comedi_insn *insn, 150 unsigned int *data) 151{ 152 data[1] = 0; 153 return insn->n; 154} 155 156static int parport_intr_cmdtest(struct comedi_device *dev, 157 struct comedi_subdevice *s, 158 struct comedi_cmd *cmd) 159{ 160 int err = 0; 161 162 /* Step 1 : check if triggers are trivially valid */ 163 164 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW); 165 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT); 166 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW); 167 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); 168 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE); 169 170 if (err) 171 return 1; 172 173 /* Step 2a : make sure trigger sources are unique */ 174 /* Step 2b : and mutually compatible */ 175 176 /* Step 3: check if arguments are trivially valid */ 177 178 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0); 179 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0); 180 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0); 181 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len); 182 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0); 183 184 if (err) 185 return 3; 186 187 /* Step 4: fix up any arguments */ 188 189 /* Step 5: check channel list if it exists */ 190 191 return 0; 192} 193 194static int parport_intr_cmd(struct comedi_device *dev, 195 struct comedi_subdevice *s) 196{ 197 unsigned int ctrl; 198 199 ctrl = inb(dev->iobase + PARPORT_CTRL_REG); 200 ctrl |= PARPORT_CTRL_IRQ_ENA; 201 outb(ctrl, dev->iobase + PARPORT_CTRL_REG); 202 203 return 0; 204} 205 206static int parport_intr_cancel(struct comedi_device *dev, 207 struct comedi_subdevice *s) 208{ 209 unsigned int ctrl; 210 211 ctrl = inb(dev->iobase + PARPORT_CTRL_REG); 212 ctrl &= ~PARPORT_CTRL_IRQ_ENA; 213 outb(ctrl, dev->iobase + PARPORT_CTRL_REG); 214 215 return 0; 216} 217 218static irqreturn_t parport_interrupt(int irq, void *d) 219{ 220 struct comedi_device *dev = d; 221 struct comedi_subdevice *s = dev->read_subdev; 222 unsigned int ctrl; 223 224 ctrl = inb(dev->iobase + PARPORT_CTRL_REG); 225 if (!(ctrl & PARPORT_CTRL_IRQ_ENA)) 226 return IRQ_NONE; 227 228 comedi_buf_put(s, 0); 229 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS; 230 231 comedi_event(dev, s); 232 return IRQ_HANDLED; 233} 234 235static int parport_attach(struct comedi_device *dev, 236 struct comedi_devconfig *it) 237{ 238 struct comedi_subdevice *s; 239 int ret; 240 241 ret = comedi_request_region(dev, it->options[0], 0x03); 242 if (ret) 243 return ret; 244 245 if (it->options[1]) { 246 ret = request_irq(it->options[1], parport_interrupt, 0, 247 dev->board_name, dev); 248 if (ret == 0) 249 dev->irq = it->options[1]; 250 } 251 252 ret = comedi_alloc_subdevices(dev, dev->irq ? 4 : 3); 253 if (ret) 254 return ret; 255 256 /* Digial I/O subdevice - Parallel port DATA register */ 257 s = &dev->subdevices[0]; 258 s->type = COMEDI_SUBD_DIO; 259 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 260 s->n_chan = 8; 261 s->maxdata = 1; 262 s->range_table = &range_digital; 263 s->insn_bits = parport_data_reg_insn_bits; 264 s->insn_config = parport_data_reg_insn_config; 265 266 /* Digial Input subdevice - Parallel port STATUS register */ 267 s = &dev->subdevices[1]; 268 s->type = COMEDI_SUBD_DI; 269 s->subdev_flags = SDF_READABLE; 270 s->n_chan = 5; 271 s->maxdata = 1; 272 s->range_table = &range_digital; 273 s->insn_bits = parport_status_reg_insn_bits; 274 275 /* Digial Output subdevice - Parallel port CONTROL register */ 276 s = &dev->subdevices[2]; 277 s->type = COMEDI_SUBD_DO; 278 s->subdev_flags = SDF_WRITABLE; 279 s->n_chan = 4; 280 s->maxdata = 1; 281 s->range_table = &range_digital; 282 s->insn_bits = parport_ctrl_reg_insn_bits; 283 284 if (dev->irq) { 285 /* Digial Input subdevice - Interrupt support */ 286 s = &dev->subdevices[3]; 287 dev->read_subdev = s; 288 s->type = COMEDI_SUBD_DI; 289 s->subdev_flags = SDF_READABLE | SDF_CMD_READ; 290 s->n_chan = 1; 291 s->maxdata = 1; 292 s->range_table = &range_digital; 293 s->insn_bits = parport_intr_insn_bits; 294 s->len_chanlist = 1; 295 s->do_cmdtest = parport_intr_cmdtest; 296 s->do_cmd = parport_intr_cmd; 297 s->cancel = parport_intr_cancel; 298 } 299 300 outb(0, dev->iobase + PARPORT_DATA_REG); 301 outb(0, dev->iobase + PARPORT_CTRL_REG); 302 303 return 0; 304} 305 306static struct comedi_driver parport_driver = { 307 .driver_name = "comedi_parport", 308 .module = THIS_MODULE, 309 .attach = parport_attach, 310 .detach = comedi_legacy_detach, 311}; 312module_comedi_driver(parport_driver); 313 314MODULE_AUTHOR("Comedi http://www.comedi.org"); 315MODULE_DESCRIPTION("Comedi: Standard parallel port driver"); 316MODULE_LICENSE("GPL"); 317