Staging: Comedi: Lindent changes to comdi driver in staging tree
[safe/jmp/linux-2.6] / drivers / staging / comedi / drivers / 8255.c
1 /*
2     comedi/drivers/8255.c
3     Driver for 8255
4
5     COMEDI - Linux Control and Measurement Device Interface
6     Copyright (C) 1998 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 /*
24 Driver: 8255
25 Description: generic 8255 support
26 Devices: [standard] 8255 (8255)
27 Author: ds
28 Status: works
29 Updated: Fri,  7 Jun 2002 12:56:45 -0700
30
31 The classic in digital I/O.  The 8255 appears in Comedi as a single
32 digital I/O subdevice with 24 channels.  The channel 0 corresponds
33 to the 8255's port A, bit 0; channel 23 corresponds to port C, bit
34 7.  Direction configuration is done in blocks, with channels 0-7,
35 8-15, 16-19, and 20-23 making up the 4 blocks.  The only 8255 mode
36 supported is mode 0.
37
38 You should enable compilation this driver if you plan to use a board
39 that has an 8255 chip.  For multifunction boards, the main driver will
40 configure the 8255 subdevice automatically.
41
42 This driver also works independently with ISA and PCI cards that
43 directly map the 8255 registers to I/O ports, including cards with
44 multiple 8255 chips.  To configure the driver for such a card, the
45 option list should be a list of the I/O port bases for each of the
46 8255 chips.  For example,
47
48   comedi_config /dev/comedi0 8255 0x200,0x204,0x208,0x20c
49
50 Note that most PCI 8255 boards do NOT work with this driver, and
51 need a separate driver as a wrapper.  For those that do work, the
52 I/O port base address can be found in the output of 'lspci -v'.
53
54 */
55
56 /*
57    This file contains an exported subdevice for driving an 8255.
58
59    To use this subdevice as part of another driver, you need to
60    set up the subdevice in the attach function of the driver by
61    calling:
62
63      subdev_8255_init(device, subdevice, callback_function, arg)
64
65    device and subdevice are pointers to the device and subdevice
66    structures.  callback_function will be called to provide the
67    low-level input/output to the device, i.e., actual register
68    access.  callback_function will be called with the value of arg
69    as the last parameter.  If the 8255 device is mapped as 4
70    consecutive I/O ports, you can use NULL for callback_function
71    and the I/O port base for arg, and an internal function will
72    handle the register access.
73
74    In addition, if the main driver handles interrupts, you can
75    enable commands on the subdevice by calling subdev_8255_init_irq()
76    instead.  Then, when you get an interrupt that is likely to be
77    from the 8255, you should call subdev_8255_interrupt(), which
78    will copy the latched value to a Comedi buffer.
79  */
80
81 #include "../comedidev.h"
82
83 #include <linux/ioport.h>
84
85 #define _8255_SIZE 4
86
87 #define _8255_DATA 0
88 #define _8255_CR 3
89
90 #define CR_C_LO_IO      0x01
91 #define CR_B_IO         0x02
92 #define CR_B_MODE       0x04
93 #define CR_C_HI_IO      0x08
94 #define CR_A_IO         0x10
95 #define CR_A_MODE(a)    ((a)<<5)
96 #define CR_CW           0x80
97
98 struct subdev_8255_struct {
99         unsigned long cb_arg;
100         int (*cb_func) (int, int, int, unsigned long);
101         int have_irq;
102 };
103
104 #define CALLBACK_ARG    (((struct subdev_8255_struct *)s->private)->cb_arg)
105 #define CALLBACK_FUNC   (((struct subdev_8255_struct *)s->private)->cb_func)
106 #define subdevpriv      ((struct subdev_8255_struct *)s->private)
107
108 static int dev_8255_attach(struct comedi_device *dev,
109                            struct comedi_devconfig *it);
110 static int dev_8255_detach(struct comedi_device *dev);
111 static struct comedi_driver driver_8255 = {
112         .driver_name = "8255",
113         .module = THIS_MODULE,
114         .attach = dev_8255_attach,
115         .detach = dev_8255_detach,
116 };
117
118 COMEDI_INITCLEANUP(driver_8255);
119
120 static void do_config(struct comedi_device *dev, struct comedi_subdevice *s);
121
122 void subdev_8255_interrupt(struct comedi_device *dev,
123                            struct comedi_subdevice *s)
124 {
125         short d;
126
127         d = CALLBACK_FUNC(0, _8255_DATA, 0, CALLBACK_ARG);
128         d |= (CALLBACK_FUNC(0, _8255_DATA + 1, 0, CALLBACK_ARG) << 8);
129
130         comedi_buf_put(s->async, d);
131         s->async->events |= COMEDI_CB_EOS;
132
133         comedi_event(dev, s);
134 }
135
136 static int subdev_8255_cb(int dir, int port, int data, unsigned long arg)
137 {
138         unsigned long iobase = arg;
139
140         if (dir) {
141                 outb(data, iobase + port);
142                 return 0;
143         } else {
144                 return inb(iobase + port);
145         }
146 }
147
148 static int subdev_8255_insn(struct comedi_device *dev,
149                             struct comedi_subdevice *s,
150                             struct comedi_insn *insn, unsigned int *data)
151 {
152         if (data[0]) {
153                 s->state &= ~data[0];
154                 s->state |= (data[0] & data[1]);
155
156                 if (data[0] & 0xff)
157                         CALLBACK_FUNC(1, _8255_DATA, s->state & 0xff,
158                                       CALLBACK_ARG);
159                 if (data[0] & 0xff00)
160                         CALLBACK_FUNC(1, _8255_DATA + 1, (s->state >> 8) & 0xff,
161                                       CALLBACK_ARG);
162                 if (data[0] & 0xff0000)
163                         CALLBACK_FUNC(1, _8255_DATA + 2,
164                                       (s->state >> 16) & 0xff, CALLBACK_ARG);
165         }
166
167         data[1] = CALLBACK_FUNC(0, _8255_DATA, 0, CALLBACK_ARG);
168         data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 1, 0, CALLBACK_ARG) << 8);
169         data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 2, 0, CALLBACK_ARG) << 16);
170
171         return 2;
172 }
173
174 static int subdev_8255_insn_config(struct comedi_device *dev,
175                                    struct comedi_subdevice *s,
176                                    struct comedi_insn *insn, unsigned int *data)
177 {
178         unsigned int mask;
179         unsigned int bits;
180
181         mask = 1 << CR_CHAN(insn->chanspec);
182         if (mask & 0x0000ff) {
183                 bits = 0x0000ff;
184         } else if (mask & 0x00ff00) {
185                 bits = 0x00ff00;
186         } else if (mask & 0x0f0000) {
187                 bits = 0x0f0000;
188         } else {
189                 bits = 0xf00000;
190         }
191
192         switch (data[0]) {
193         case INSN_CONFIG_DIO_INPUT:
194                 s->io_bits &= ~bits;
195                 break;
196         case INSN_CONFIG_DIO_OUTPUT:
197                 s->io_bits |= bits;
198                 break;
199         case INSN_CONFIG_DIO_QUERY:
200                 data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT;
201                 return insn->n;
202                 break;
203         default:
204                 return -EINVAL;
205         }
206
207         do_config(dev, s);
208
209         return 1;
210 }
211
212 static void do_config(struct comedi_device *dev, struct comedi_subdevice *s)
213 {
214         int config;
215
216         config = CR_CW;
217         /* 1 in io_bits indicates output, 1 in config indicates input */
218         if (!(s->io_bits & 0x0000ff))
219                 config |= CR_A_IO;
220         if (!(s->io_bits & 0x00ff00))
221                 config |= CR_B_IO;
222         if (!(s->io_bits & 0x0f0000))
223                 config |= CR_C_LO_IO;
224         if (!(s->io_bits & 0xf00000))
225                 config |= CR_C_HI_IO;
226         CALLBACK_FUNC(1, _8255_CR, config, CALLBACK_ARG);
227 }
228
229 static int subdev_8255_cmdtest(struct comedi_device *dev,
230                                struct comedi_subdevice *s,
231                                struct comedi_cmd *cmd)
232 {
233         int err = 0;
234         unsigned int tmp;
235
236         /* step 1 */
237
238         tmp = cmd->start_src;
239         cmd->start_src &= TRIG_NOW;
240         if (!cmd->start_src || tmp != cmd->start_src)
241                 err++;
242
243         tmp = cmd->scan_begin_src;
244         cmd->scan_begin_src &= TRIG_EXT;
245         if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
246                 err++;
247
248         tmp = cmd->convert_src;
249         cmd->convert_src &= TRIG_FOLLOW;
250         if (!cmd->convert_src || tmp != cmd->convert_src)
251                 err++;
252
253         tmp = cmd->scan_end_src;
254         cmd->scan_end_src &= TRIG_COUNT;
255         if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
256                 err++;
257
258         tmp = cmd->stop_src;
259         cmd->stop_src &= TRIG_NONE;
260         if (!cmd->stop_src || tmp != cmd->stop_src)
261                 err++;
262
263         if (err)
264                 return 1;
265
266         /* step 2 */
267
268         if (err)
269                 return 2;
270
271         /* step 3 */
272
273         if (cmd->start_arg != 0) {
274                 cmd->start_arg = 0;
275                 err++;
276         }
277         if (cmd->scan_begin_arg != 0) {
278                 cmd->scan_begin_arg = 0;
279                 err++;
280         }
281         if (cmd->convert_arg != 0) {
282                 cmd->convert_arg = 0;
283                 err++;
284         }
285         if (cmd->scan_end_arg != 1) {
286                 cmd->scan_end_arg = 1;
287                 err++;
288         }
289         if (cmd->stop_arg != 0) {
290                 cmd->stop_arg = 0;
291                 err++;
292         }
293
294         if (err)
295                 return 3;
296
297         /* step 4 */
298
299         if (err)
300                 return 4;
301
302         return 0;
303 }
304
305 static int subdev_8255_cmd(struct comedi_device *dev,
306                            struct comedi_subdevice *s)
307 {
308         /* FIXME */
309
310         return 0;
311 }
312
313 static int subdev_8255_cancel(struct comedi_device *dev,
314                               struct comedi_subdevice *s)
315 {
316         /* FIXME */
317
318         return 0;
319 }
320
321 int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s,
322                      int (*cb) (int, int, int, unsigned long),
323                      unsigned long arg)
324 {
325         s->type = COMEDI_SUBD_DIO;
326         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
327         s->n_chan = 24;
328         s->range_table = &range_digital;
329         s->maxdata = 1;
330
331         s->private = kmalloc(sizeof(struct subdev_8255_struct), GFP_KERNEL);
332         if (!s->private)
333                 return -ENOMEM;
334
335         CALLBACK_ARG = arg;
336         if (cb == NULL) {
337                 CALLBACK_FUNC = subdev_8255_cb;
338         } else {
339                 CALLBACK_FUNC = cb;
340         }
341         s->insn_bits = subdev_8255_insn;
342         s->insn_config = subdev_8255_insn_config;
343
344         s->state = 0;
345         s->io_bits = 0;
346         do_config(dev, s);
347
348         return 0;
349 }
350
351 int subdev_8255_init_irq(struct comedi_device *dev, struct comedi_subdevice *s,
352                          int (*cb) (int, int, int, unsigned long),
353                          unsigned long arg)
354 {
355         int ret;
356
357         ret = subdev_8255_init(dev, s, cb, arg);
358         if (ret < 0)
359                 return ret;
360
361         s->do_cmdtest = subdev_8255_cmdtest;
362         s->do_cmd = subdev_8255_cmd;
363         s->cancel = subdev_8255_cancel;
364
365         subdevpriv->have_irq = 1;
366
367         return 0;
368 }
369
370 void subdev_8255_cleanup(struct comedi_device *dev, struct comedi_subdevice *s)
371 {
372         if (s->private) {
373                 /* this test does nothing, so comment it out
374                  * if (subdevpriv->have_irq) {
375                  * }
376                  */
377
378                 kfree(s->private);
379         }
380 }
381
382 /*
383
384    Start of the 8255 standalone device
385
386  */
387
388 static int dev_8255_attach(struct comedi_device *dev,
389                            struct comedi_devconfig *it)
390 {
391         int ret;
392         unsigned long iobase;
393         int i;
394
395         printk("comedi%d: 8255:", dev->minor);
396
397         dev->board_name = "8255";
398
399         for (i = 0; i < COMEDI_NDEVCONFOPTS; i++) {
400                 iobase = it->options[i];
401                 if (!iobase)
402                         break;
403         }
404         if (i == 0) {
405                 printk(" no devices specified\n");
406                 return -EINVAL;
407         }
408
409         ret = alloc_subdevices(dev, i);
410         if (ret < 0)
411                 return ret;
412
413         for (i = 0; i < dev->n_subdevices; i++) {
414                 iobase = it->options[i];
415
416                 printk(" 0x%04lx", iobase);
417                 if (!request_region(iobase, _8255_SIZE, "8255")) {
418                         printk(" (I/O port conflict)");
419
420                         dev->subdevices[i].type = COMEDI_SUBD_UNUSED;
421                 } else {
422                         subdev_8255_init(dev, dev->subdevices + i, NULL,
423                                          iobase);
424                 }
425         }
426
427         printk("\n");
428
429         return 0;
430 }
431
432 static int dev_8255_detach(struct comedi_device *dev)
433 {
434         int i;
435         unsigned long iobase;
436         struct comedi_subdevice *s;
437
438         printk("comedi%d: 8255: remove\n", dev->minor);
439
440         for (i = 0; i < dev->n_subdevices; i++) {
441                 s = dev->subdevices + i;
442                 if (s->type != COMEDI_SUBD_UNUSED) {
443                         iobase = CALLBACK_ARG;
444                         release_region(iobase, _8255_SIZE);
445                 }
446                 subdev_8255_cleanup(dev, s);
447         }
448
449         return 0;
450 }
451
452 EXPORT_SYMBOL(subdev_8255_init);
453 EXPORT_SYMBOL(subdev_8255_init_irq);
454 EXPORT_SYMBOL(subdev_8255_cleanup);
455 EXPORT_SYMBOL(subdev_8255_interrupt);