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