comedi_parport.c revision 8487d0e93a24de43c847b923f7bbbd9097e59d47
1/*
2    comedi/drivers/comedi_parport.c
3    hardware driver for standard parallel port
4
5    COMEDI - Linux Control and Measurement Device Interface
6    Copyright (C) 1998,2001 David A. Schleef <ds@schleef.org>
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22*/
23/*
24Driver: comedi_parport
25Description: Standard PC parallel port
26Author: ds
27Status: works in immediate mode
28Devices: [standard] parallel port (comedi_parport)
29Updated: Tue, 30 Apr 2002 21:11:45 -0700
30
31A cheap and easy way to get a few more digital I/O lines.  Steal
32additional parallel ports from old computers or your neighbors'
33computers.
34
35Option list:
36 0: I/O port base for the parallel port.
37 1: IRQ
38
39Parallel Port Lines:
40
41pin     subdev  chan    aka
42---     ------  ----    ---
431       2       0       strobe
442       0       0       data 0
453       0       1       data 1
464       0       2       data 2
475       0       3       data 3
486       0       4       data 4
497       0       5       data 5
508       0       6       data 6
519       0       7       data 7
5210      1       3       acknowledge
5311      1       4       busy
5412      1       2       output
5513      1       1       printer selected
5614      2       1       auto LF
5715      1       0       error
5816      2       2       init
5917      2       3       select printer
6018-25   ground
61
62Notes:
63
64Subdevices 0 is digital I/O, subdevice 1 is digital input, and
65subdevice 2 is digital output.  Unlike other Comedi devices,
66subdevice 0 defaults to output.
67
68Pins 13 and 14 are inverted once by Comedi and once by the
69hardware, thus cancelling the effect.
70
71Pin 1 is a strobe, thus acts like one.  There's no way in software
72to change this, at least on a standard parallel port.
73
74Subdevice 3 pretends to be a digital input subdevice, but it always
75returns 0 when read.  However, if you run a command with
76scan_begin_src=TRIG_EXT, it uses pin 10 as a external triggering
77pin, which can be used to wake up tasks.
78*/
79/*
80   see http://www.beyondlogic.org/ for information.
81   or http://www.linux-magazin.de/ausgabe/1999/10/IO/io.html
82 */
83
84#include "../comedidev.h"
85#include <linux/interrupt.h>
86#include <linux/ioport.h>
87
88#define PARPORT_SIZE 3
89
90#define PARPORT_A 0
91#define PARPORT_B 1
92#define PARPORT_C 2
93
94static int parport_attach(struct comedi_device *dev,
95			  struct comedi_devconfig *it);
96static int parport_detach(struct comedi_device *dev);
97static struct comedi_driver driver_parport = {
98	.driver_name = "comedi_parport",
99	.module = THIS_MODULE,
100	.attach = parport_attach,
101	.detach = parport_detach,
102};
103
104COMEDI_INITCLEANUP(driver_parport);
105
106struct parport_private {
107	unsigned int a_data;
108	unsigned int c_data;
109	int enable_irq;
110};
111#define devpriv ((struct parport_private *)(dev->private))
112
113static int parport_insn_a(struct comedi_device *dev, struct comedi_subdevice *s,
114			  struct comedi_insn *insn, unsigned int *data)
115{
116	if (data[0]) {
117		devpriv->a_data &= ~data[0];
118		devpriv->a_data |= (data[0] & data[1]);
119
120		outb(devpriv->a_data, dev->iobase + PARPORT_A);
121	}
122
123	data[1] = inb(dev->iobase + PARPORT_A);
124
125	return 2;
126}
127
128static int parport_insn_config_a(struct comedi_device *dev,
129				 struct comedi_subdevice *s,
130				 struct comedi_insn *insn, unsigned int *data)
131{
132	if (data[0]) {
133		s->io_bits = 0xff;
134		devpriv->c_data &= ~(1 << 5);
135	} else {
136		s->io_bits = 0;
137		devpriv->c_data |= (1 << 5);
138	}
139	outb(devpriv->c_data, dev->iobase + PARPORT_C);
140
141	return 1;
142}
143
144static int parport_insn_b(struct comedi_device *dev, struct comedi_subdevice *s,
145			  struct comedi_insn *insn, unsigned int *data)
146{
147	if (data[0]) {
148		/* should writes be ignored? */
149		/* anyone??? */
150	}
151
152	data[1] = (inb(dev->iobase + PARPORT_B) >> 3);
153
154	return 2;
155}
156
157static int parport_insn_c(struct comedi_device *dev, struct comedi_subdevice *s,
158			  struct comedi_insn *insn, unsigned int *data)
159{
160	data[0] &= 0x0f;
161	if (data[0]) {
162		devpriv->c_data &= ~data[0];
163		devpriv->c_data |= (data[0] & data[1]);
164
165		outb(devpriv->c_data, dev->iobase + PARPORT_C);
166	}
167
168	data[1] = devpriv->c_data & 0xf;
169
170	return 2;
171}
172
173static int parport_intr_insn(struct comedi_device *dev,
174			     struct comedi_subdevice *s,
175			     struct comedi_insn *insn, unsigned int *data)
176{
177	if (insn->n < 1)
178		return -EINVAL;
179
180	data[1] = 0;
181	return 2;
182}
183
184static int parport_intr_cmdtest(struct comedi_device *dev,
185				struct comedi_subdevice *s,
186				struct comedi_cmd *cmd)
187{
188	int err = 0;
189	int tmp;
190
191	/* step 1 */
192
193	tmp = cmd->start_src;
194	cmd->start_src &= TRIG_NOW;
195	if (!cmd->start_src || tmp != cmd->start_src)
196		err++;
197
198	tmp = cmd->scan_begin_src;
199	cmd->scan_begin_src &= TRIG_EXT;
200	if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
201		err++;
202
203	tmp = cmd->convert_src;
204	cmd->convert_src &= TRIG_FOLLOW;
205	if (!cmd->convert_src || tmp != cmd->convert_src)
206		err++;
207
208	tmp = cmd->scan_end_src;
209	cmd->scan_end_src &= TRIG_COUNT;
210	if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
211		err++;
212
213	tmp = cmd->stop_src;
214	cmd->stop_src &= TRIG_NONE;
215	if (!cmd->stop_src || tmp != cmd->stop_src)
216		err++;
217
218	if (err)
219		return 1;
220
221	/* step 2: ignored */
222
223	if (err)
224		return 2;
225
226	/* step 3: */
227
228	if (cmd->start_arg != 0) {
229		cmd->start_arg = 0;
230		err++;
231	}
232	if (cmd->scan_begin_arg != 0) {
233		cmd->scan_begin_arg = 0;
234		err++;
235	}
236	if (cmd->convert_arg != 0) {
237		cmd->convert_arg = 0;
238		err++;
239	}
240	if (cmd->scan_end_arg != 1) {
241		cmd->scan_end_arg = 1;
242		err++;
243	}
244	if (cmd->stop_arg != 0) {
245		cmd->stop_arg = 0;
246		err++;
247	}
248
249	if (err)
250		return 3;
251
252	/* step 4: ignored */
253
254	if (err)
255		return 4;
256
257	return 0;
258}
259
260static int parport_intr_cmd(struct comedi_device *dev,
261			    struct comedi_subdevice *s)
262{
263	devpriv->c_data |= 0x10;
264	outb(devpriv->c_data, dev->iobase + PARPORT_C);
265
266	devpriv->enable_irq = 1;
267
268	return 0;
269}
270
271static int parport_intr_cancel(struct comedi_device *dev,
272			       struct comedi_subdevice *s)
273{
274	printk(KERN_DEBUG "parport_intr_cancel()\n");
275
276	devpriv->c_data &= ~0x10;
277	outb(devpriv->c_data, dev->iobase + PARPORT_C);
278
279	devpriv->enable_irq = 0;
280
281	return 0;
282}
283
284static irqreturn_t parport_interrupt(int irq, void *d)
285{
286	struct comedi_device *dev = d;
287	struct comedi_subdevice *s = dev->subdevices + 3;
288
289	if (!devpriv->enable_irq) {
290		printk(KERN_ERR "comedi_parport: bogus irq, ignored\n");
291		return IRQ_NONE;
292	}
293
294	comedi_buf_put(s->async, 0);
295	s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
296
297	comedi_event(dev, s);
298	return IRQ_HANDLED;
299}
300
301static int parport_attach(struct comedi_device *dev,
302			  struct comedi_devconfig *it)
303{
304	int ret;
305	unsigned int irq;
306	unsigned long iobase;
307	struct comedi_subdevice *s;
308
309	iobase = it->options[0];
310	printk(KERN_INFO "comedi%d: parport: 0x%04lx ", dev->minor, iobase);
311	if (!request_region(iobase, PARPORT_SIZE, "parport (comedi)")) {
312		printk(KERN_ERR "I/O port conflict\n");
313		return -EIO;
314	}
315	dev->iobase = iobase;
316
317	irq = it->options[1];
318	if (irq) {
319		printk(KERN_INFO " irq=%u", irq);
320		ret = request_irq(irq, parport_interrupt, 0, "comedi_parport",
321				  dev);
322		if (ret < 0) {
323			printk(KERN_ERR " irq not available\n");
324			return -EINVAL;
325		}
326		dev->irq = irq;
327	}
328	dev->board_name = "parport";
329
330	ret = alloc_subdevices(dev, 4);
331	if (ret < 0)
332		return ret;
333	ret = alloc_private(dev, sizeof(struct parport_private));
334	if (ret < 0)
335		return ret;
336
337	s = dev->subdevices + 0;
338	s->type = COMEDI_SUBD_DIO;
339	s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
340	s->n_chan = 8;
341	s->maxdata = 1;
342	s->range_table = &range_digital;
343	s->insn_bits = parport_insn_a;
344	s->insn_config = parport_insn_config_a;
345
346	s = dev->subdevices + 1;
347	s->type = COMEDI_SUBD_DI;
348	s->subdev_flags = SDF_READABLE;
349	s->n_chan = 5;
350	s->maxdata = 1;
351	s->range_table = &range_digital;
352	s->insn_bits = parport_insn_b;
353
354	s = dev->subdevices + 2;
355	s->type = COMEDI_SUBD_DO;
356	s->subdev_flags = SDF_WRITABLE;
357	s->n_chan = 4;
358	s->maxdata = 1;
359	s->range_table = &range_digital;
360	s->insn_bits = parport_insn_c;
361
362	s = dev->subdevices + 3;
363	if (irq) {
364		dev->read_subdev = s;
365		s->type = COMEDI_SUBD_DI;
366		s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
367		s->n_chan = 1;
368		s->maxdata = 1;
369		s->range_table = &range_digital;
370		s->insn_bits = parport_intr_insn;
371		s->do_cmdtest = parport_intr_cmdtest;
372		s->do_cmd = parport_intr_cmd;
373		s->cancel = parport_intr_cancel;
374	} else {
375		s->type = COMEDI_SUBD_UNUSED;
376	}
377
378	devpriv->a_data = 0;
379	outb(devpriv->a_data, dev->iobase + PARPORT_A);
380	devpriv->c_data = 0;
381	outb(devpriv->c_data, dev->iobase + PARPORT_C);
382
383	printk(KERN_INFO "\n");
384	return 1;
385}
386
387static int parport_detach(struct comedi_device *dev)
388{
389	printk(KERN_INFO "comedi%d: parport: remove\n", dev->minor);
390
391	if (dev->iobase)
392		release_region(dev->iobase, PARPORT_SIZE);
393
394	if (dev->irq)
395		free_irq(dev->irq, dev);
396
397	return 0;
398}
399