]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blame - drivers/staging/comedi/drivers/comedi_parport.c
Merge tag 'pinctrl-v4.1-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw...
[mirror_ubuntu-zesty-kernel.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
5790b427
HS
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 (1 << 4)
79#define PARPORT_CTRL_BIDIR_ENA (1 << 5)
241ab6ad 80
4c921337
HS
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)
241ab6ad 85{
4c921337 86 if (comedi_dio_update_state(s, data))
9f1a0909 87 outb(s->state, dev->iobase + PARPORT_DATA_REG);
241ab6ad 88
5790b427 89 data[1] = inb(dev->iobase + PARPORT_DATA_REG);
241ab6ad 90
a2714e3e 91 return insn->n;
241ab6ad
DS
92}
93
9f2843d1
HS
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)
241ab6ad 98{
4dc3a892 99 unsigned int ctrl;
9f2843d1 100 int ret;
d3bcf099 101
9f2843d1
HS
102 ret = comedi_dio_insn_config(dev, s, insn, data, 0xff);
103 if (ret)
104 return ret;
105
4dc3a892 106 ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
9f2843d1 107 if (s->io_bits)
4dc3a892 108 ctrl &= ~PARPORT_CTRL_BIDIR_ENA;
9f2843d1 109 else
4dc3a892
HS
110 ctrl |= PARPORT_CTRL_BIDIR_ENA;
111 outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
241ab6ad 112
9f2843d1 113 return insn->n;
241ab6ad
DS
114}
115
6e2dbe3c
HS
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)
241ab6ad 120{
6e2dbe3c 121 data[1] = inb(dev->iobase + PARPORT_STATUS_REG) >> 3;
241ab6ad 122
a2714e3e 123 return insn->n;
241ab6ad
DS
124}
125
cdf434ee
HS
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)
241ab6ad 130{
4dc3a892 131 unsigned int ctrl;
d3bcf099 132
cdf434ee 133 if (comedi_dio_update_state(s, data)) {
4dc3a892
HS
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);
241ab6ad
DS
138 }
139
cdf434ee 140 data[1] = s->state;
241ab6ad 141
a2714e3e 142 return insn->n;
241ab6ad
DS
143}
144
efc6da4d
HS
145static int parport_intr_insn_bits(struct comedi_device *dev,
146 struct comedi_subdevice *s,
147 struct comedi_insn *insn,
148 unsigned int *data)
241ab6ad 149{
241ab6ad 150 data[1] = 0;
a2714e3e 151 return insn->n;
241ab6ad
DS
152}
153
0a85b6f0
MT
154static int parport_intr_cmdtest(struct comedi_device *dev,
155 struct comedi_subdevice *s,
ea6d0d4c 156 struct comedi_cmd *cmd)
241ab6ad
DS
157{
158 int err = 0;
241ab6ad 159
27020ffe 160 /* Step 1 : check if triggers are trivially valid */
241ab6ad 161
070c8e16
IA
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);
241ab6ad
DS
167
168 if (err)
169 return 1;
170
27020ffe
HS
171 /* Step 2a : make sure trigger sources are unique */
172 /* Step 2b : and mutually compatible */
241ab6ad 173
24042710 174 /* Step 3: check if arguments are trivially valid */
241ab6ad 175
070c8e16
IA
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);
241ab6ad
DS
182
183 if (err)
184 return 3;
185
68bd0f6f 186 /* Step 4: fix up any arguments */
241ab6ad 187
68bd0f6f 188 /* Step 5: check channel list if it exists */
241ab6ad
DS
189
190 return 0;
191}
192
0a85b6f0
MT
193static int parport_intr_cmd(struct comedi_device *dev,
194 struct comedi_subdevice *s)
241ab6ad 195{
4dc3a892 196 unsigned int ctrl;
d3bcf099 197
4dc3a892
HS
198 ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
199 ctrl |= PARPORT_CTRL_IRQ_ENA;
200 outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
241ab6ad 201
241ab6ad
DS
202 return 0;
203}
204
0a85b6f0
MT
205static int parport_intr_cancel(struct comedi_device *dev,
206 struct comedi_subdevice *s)
241ab6ad 207{
4dc3a892 208 unsigned int ctrl;
d3bcf099 209
4dc3a892
HS
210 ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
211 ctrl &= ~PARPORT_CTRL_IRQ_ENA;
212 outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
241ab6ad 213
241ab6ad
DS
214 return 0;
215}
216
70265d24 217static irqreturn_t parport_interrupt(int irq, void *d)
241ab6ad 218{
71b5f4f1 219 struct comedi_device *dev = d;
b3860bff 220 struct comedi_subdevice *s = dev->read_subdev;
b6ede054 221 unsigned int ctrl;
241ab6ad 222
b6ede054
HS
223 ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
224 if (!(ctrl & PARPORT_CTRL_IRQ_ENA))
241ab6ad 225 return IRQ_NONE;
241ab6ad 226
9a84a7d2 227 comedi_buf_write_samples(s, &s->state, 1);
bc881651 228 comedi_handle_events(dev, s);
9a84a7d2 229
241ab6ad
DS
230 return IRQ_HANDLED;
231}
232
0a85b6f0
MT
233static int parport_attach(struct comedi_device *dev,
234 struct comedi_devconfig *it)
241ab6ad 235{
34c43922 236 struct comedi_subdevice *s;
8bee2dea 237 int ret;
241ab6ad 238
5790b427 239 ret = comedi_request_region(dev, it->options[0], 0x03);
8bee2dea
HS
240 if (ret)
241 return ret;
241ab6ad 242
7e6f185e
HS
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];
241ab6ad 248 }
241ab6ad 249
7e6f185e 250 ret = comedi_alloc_subdevices(dev, dev->irq ? 4 : 3);
8b6c5694 251 if (ret)
241ab6ad 252 return ret;
8b6c5694 253
88f0b62b 254 /* Digial I/O subdevice - Parallel port DATA register */
9da5ae29 255 s = &dev->subdevices[0];
88f0b62b
HS
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 */
9da5ae29 265 s = &dev->subdevices[1];
88f0b62b
HS
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 */
9da5ae29 274 s = &dev->subdevices[2];
88f0b62b
HS
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;
241ab6ad 281
7e6f185e 282 if (dev->irq) {
88f0b62b 283 /* Digial Input subdevice - Interrupt support */
7e6f185e 284 s = &dev->subdevices[3];
241ab6ad 285 dev->read_subdev = s;
88f0b62b
HS
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;
efc6da4d 291 s->insn_bits = parport_intr_insn_bits;
f50cebb9 292 s->len_chanlist = 1;
88f0b62b
HS
293 s->do_cmdtest = parport_intr_cmdtest;
294 s->do_cmd = parport_intr_cmd;
295 s->cancel = parport_intr_cancel;
241ab6ad
DS
296 }
297
9f1a0909 298 outb(0, dev->iobase + PARPORT_DATA_REG);
4dc3a892 299 outb(0, dev->iobase + PARPORT_CTRL_REG);
241ab6ad 300
b8d0f3ae 301 return 0;
241ab6ad
DS
302}
303
bfba5e78
HS
304static struct comedi_driver parport_driver = {
305 .driver_name = "comedi_parport",
306 .module = THIS_MODULE,
307 .attach = parport_attach,
3d1fe3f7 308 .detach = comedi_legacy_detach,
bfba5e78
HS
309};
310module_comedi_driver(parport_driver);
311
90f703d3 312MODULE_AUTHOR("Comedi http://www.comedi.org");
7c9cf743 313MODULE_DESCRIPTION("Comedi: Standard parallel port driver");
90f703d3 314MODULE_LICENSE("GPL");