Merge branch 'fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/evalenti/linux...
[linux-2.6-block.git] / drivers / staging / comedi / drivers / comedi_parport.c
CommitLineData
241ab6ad 1/*
7821e8e4
HS
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 */
241ab6ad 21
241ab6ad 22/*
7821e8e4
HS
23 * Driver: comedi_parport
24 * Description: Standard PC parallel port
25 * Author: ds
26 * Status: works in immediate mode
db908f55 27 * Devices: [standard] parallel port (comedi_parport)
7821e8e4
HS
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.
241ab6ad
DS
65 */
66
ce157f80 67#include <linux/module.h>
70265d24 68#include <linux/interrupt.h>
241ab6ad 69
2f0ab460
HS
70#include "../comedidev.h"
71
27020ffe
HS
72#include "comedi_fc.h"
73
5790b427
HS
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)
241ab6ad 82
4c921337
HS
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)
241ab6ad 87{
4c921337 88 if (comedi_dio_update_state(s, data))
9f1a0909 89 outb(s->state, dev->iobase + PARPORT_DATA_REG);
241ab6ad 90
5790b427 91 data[1] = inb(dev->iobase + PARPORT_DATA_REG);
241ab6ad 92
a2714e3e 93 return insn->n;
241ab6ad
DS
94}
95
9f2843d1
HS
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)
241ab6ad 100{
4dc3a892 101 unsigned int ctrl;
9f2843d1 102 int ret;
d3bcf099 103
9f2843d1
HS
104 ret = comedi_dio_insn_config(dev, s, insn, data, 0xff);
105 if (ret)
106 return ret;
107
4dc3a892 108 ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
9f2843d1 109 if (s->io_bits)
4dc3a892 110 ctrl &= ~PARPORT_CTRL_BIDIR_ENA;
9f2843d1 111 else
4dc3a892
HS
112 ctrl |= PARPORT_CTRL_BIDIR_ENA;
113 outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
241ab6ad 114
9f2843d1 115 return insn->n;
241ab6ad
DS
116}
117
6e2dbe3c
HS
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)
241ab6ad 122{
6e2dbe3c 123 data[1] = inb(dev->iobase + PARPORT_STATUS_REG) >> 3;
241ab6ad 124
a2714e3e 125 return insn->n;
241ab6ad
DS
126}
127
cdf434ee
HS
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)
241ab6ad 132{
4dc3a892 133 unsigned int ctrl;
d3bcf099 134
cdf434ee 135 if (comedi_dio_update_state(s, data)) {
4dc3a892
HS
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);
241ab6ad
DS
140 }
141
cdf434ee 142 data[1] = s->state;
241ab6ad 143
a2714e3e 144 return insn->n;
241ab6ad
DS
145}
146
efc6da4d
HS
147static int parport_intr_insn_bits(struct comedi_device *dev,
148 struct comedi_subdevice *s,
149 struct comedi_insn *insn,
150 unsigned int *data)
241ab6ad 151{
241ab6ad 152 data[1] = 0;
a2714e3e 153 return insn->n;
241ab6ad
DS
154}
155
0a85b6f0
MT
156static int parport_intr_cmdtest(struct comedi_device *dev,
157 struct comedi_subdevice *s,
ea6d0d4c 158 struct comedi_cmd *cmd)
241ab6ad
DS
159{
160 int err = 0;
241ab6ad 161
27020ffe 162 /* Step 1 : check if triggers are trivially valid */
241ab6ad 163
27020ffe
HS
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);
241ab6ad
DS
169
170 if (err)
171 return 1;
172
27020ffe
HS
173 /* Step 2a : make sure trigger sources are unique */
174 /* Step 2b : and mutually compatible */
241ab6ad 175
24042710 176 /* Step 3: check if arguments are trivially valid */
241ab6ad 177
24042710
HS
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);
f50cebb9 181 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
24042710 182 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
241ab6ad
DS
183
184 if (err)
185 return 3;
186
68bd0f6f 187 /* Step 4: fix up any arguments */
241ab6ad 188
68bd0f6f 189 /* Step 5: check channel list if it exists */
241ab6ad
DS
190
191 return 0;
192}
193
0a85b6f0
MT
194static int parport_intr_cmd(struct comedi_device *dev,
195 struct comedi_subdevice *s)
241ab6ad 196{
4dc3a892 197 unsigned int ctrl;
d3bcf099 198
4dc3a892
HS
199 ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
200 ctrl |= PARPORT_CTRL_IRQ_ENA;
201 outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
241ab6ad 202
241ab6ad
DS
203 return 0;
204}
205
0a85b6f0
MT
206static int parport_intr_cancel(struct comedi_device *dev,
207 struct comedi_subdevice *s)
241ab6ad 208{
4dc3a892 209 unsigned int ctrl;
d3bcf099 210
4dc3a892
HS
211 ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
212 ctrl &= ~PARPORT_CTRL_IRQ_ENA;
213 outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
241ab6ad 214
241ab6ad
DS
215 return 0;
216}
217
70265d24 218static irqreturn_t parport_interrupt(int irq, void *d)
241ab6ad 219{
71b5f4f1 220 struct comedi_device *dev = d;
b3860bff 221 struct comedi_subdevice *s = dev->read_subdev;
b6ede054 222 unsigned int ctrl;
241ab6ad 223
b6ede054
HS
224 ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
225 if (!(ctrl & PARPORT_CTRL_IRQ_ENA))
241ab6ad 226 return IRQ_NONE;
241ab6ad 227
9a84a7d2 228 comedi_buf_write_samples(s, &s->state, 1);
bc881651 229 comedi_handle_events(dev, s);
9a84a7d2 230
241ab6ad
DS
231 return IRQ_HANDLED;
232}
233
0a85b6f0
MT
234static int parport_attach(struct comedi_device *dev,
235 struct comedi_devconfig *it)
241ab6ad 236{
34c43922 237 struct comedi_subdevice *s;
8bee2dea 238 int ret;
241ab6ad 239
5790b427 240 ret = comedi_request_region(dev, it->options[0], 0x03);
8bee2dea
HS
241 if (ret)
242 return ret;
241ab6ad 243
7e6f185e
HS
244 if (it->options[1]) {
245 ret = request_irq(it->options[1], parport_interrupt, 0,
246 dev->board_name, dev);
247 if (ret == 0)
248 dev->irq = it->options[1];
241ab6ad 249 }
241ab6ad 250
7e6f185e 251 ret = comedi_alloc_subdevices(dev, dev->irq ? 4 : 3);
8b6c5694 252 if (ret)
241ab6ad 253 return ret;
8b6c5694 254
88f0b62b 255 /* Digial I/O subdevice - Parallel port DATA register */
9da5ae29 256 s = &dev->subdevices[0];
88f0b62b
HS
257 s->type = COMEDI_SUBD_DIO;
258 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
259 s->n_chan = 8;
260 s->maxdata = 1;
261 s->range_table = &range_digital;
262 s->insn_bits = parport_data_reg_insn_bits;
263 s->insn_config = parport_data_reg_insn_config;
264
265 /* Digial Input subdevice - Parallel port STATUS register */
9da5ae29 266 s = &dev->subdevices[1];
88f0b62b
HS
267 s->type = COMEDI_SUBD_DI;
268 s->subdev_flags = SDF_READABLE;
269 s->n_chan = 5;
270 s->maxdata = 1;
271 s->range_table = &range_digital;
272 s->insn_bits = parport_status_reg_insn_bits;
273
274 /* Digial Output subdevice - Parallel port CONTROL register */
9da5ae29 275 s = &dev->subdevices[2];
88f0b62b
HS
276 s->type = COMEDI_SUBD_DO;
277 s->subdev_flags = SDF_WRITABLE;
278 s->n_chan = 4;
279 s->maxdata = 1;
280 s->range_table = &range_digital;
281 s->insn_bits = parport_ctrl_reg_insn_bits;
241ab6ad 282
7e6f185e 283 if (dev->irq) {
88f0b62b 284 /* Digial Input subdevice - Interrupt support */
7e6f185e 285 s = &dev->subdevices[3];
241ab6ad 286 dev->read_subdev = s;
88f0b62b
HS
287 s->type = COMEDI_SUBD_DI;
288 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
289 s->n_chan = 1;
290 s->maxdata = 1;
291 s->range_table = &range_digital;
efc6da4d 292 s->insn_bits = parport_intr_insn_bits;
f50cebb9 293 s->len_chanlist = 1;
88f0b62b
HS
294 s->do_cmdtest = parport_intr_cmdtest;
295 s->do_cmd = parport_intr_cmd;
296 s->cancel = parport_intr_cancel;
241ab6ad
DS
297 }
298
9f1a0909 299 outb(0, dev->iobase + PARPORT_DATA_REG);
4dc3a892 300 outb(0, dev->iobase + PARPORT_CTRL_REG);
241ab6ad 301
b8d0f3ae 302 return 0;
241ab6ad
DS
303}
304
bfba5e78
HS
305static struct comedi_driver parport_driver = {
306 .driver_name = "comedi_parport",
307 .module = THIS_MODULE,
308 .attach = parport_attach,
3d1fe3f7 309 .detach = comedi_legacy_detach,
bfba5e78
HS
310};
311module_comedi_driver(parport_driver);
312
90f703d3 313MODULE_AUTHOR("Comedi http://www.comedi.org");
7c9cf743 314MODULE_DESCRIPTION("Comedi: Standard parallel port driver");
90f703d3 315MODULE_LICENSE("GPL");