1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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
98struct 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
108static int dev_8255_attach(struct comedi_device *dev,
109 struct comedi_devconfig *it);
110static int dev_8255_detach(struct comedi_device *dev);
111static 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
118COMEDI_INITCLEANUP(driver_8255);
119
120static void do_config(struct comedi_device *dev, struct comedi_subdevice *s);
121
122void 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
136static 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
148static 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
174static 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
212static void do_config(struct comedi_device *dev, struct comedi_subdevice *s)
213{
214 int config;
215
216 config = CR_CW;
217
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
229static 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
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
267
268 if (err)
269 return 2;
270
271
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
298
299 if (err)
300 return 4;
301
302 return 0;
303}
304
305static int subdev_8255_cmd(struct comedi_device *dev,
306 struct comedi_subdevice *s)
307{
308
309
310 return 0;
311}
312
313static int subdev_8255_cancel(struct comedi_device *dev,
314 struct comedi_subdevice *s)
315{
316
317
318 return 0;
319}
320
321int 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
351int 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
370void subdev_8255_cleanup(struct comedi_device *dev, struct comedi_subdevice *s)
371{
372 if (s->private) {
373
374
375
376
377
378 kfree(s->private);
379 }
380}
381
382
383
384
385
386
387
388static 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
432static 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
452EXPORT_SYMBOL(subdev_8255_init);
453EXPORT_SYMBOL(subdev_8255_init_irq);
454EXPORT_SYMBOL(subdev_8255_cleanup);
455EXPORT_SYMBOL(subdev_8255_interrupt);
456