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#include <linux/module.h>
76#include <linux/interrupt.h>
77#include <linux/slab.h>
78
79#include "../comedidev.h"
80
81
82
83
84#define PCMMIO_AI_LSB_REG 0x00
85#define PCMMIO_AI_MSB_REG 0x01
86#define PCMMIO_AI_CMD_REG 0x02
87#define PCMMIO_AI_CMD_SE (1 << 7)
88#define PCMMIO_AI_CMD_ODD_CHAN (1 << 6)
89#define PCMMIO_AI_CMD_CHAN_SEL(x) (((x) & 0x3) << 4)
90#define PCMMIO_AI_CMD_RANGE(x) (((x) & 0x3) << 2)
91#define PCMMIO_RESOURCE_REG 0x02
92#define PCMMIO_RESOURCE_IRQ(x) (((x) & 0xf) << 0)
93#define PCMMIO_AI_STATUS_REG 0x03
94#define PCMMIO_AI_STATUS_DATA_READY (1 << 7)
95#define PCMMIO_AI_STATUS_DATA_DMA_PEND (1 << 6)
96#define PCMMIO_AI_STATUS_CMD_DMA_PEND (1 << 5)
97#define PCMMIO_AI_STATUS_IRQ_PEND (1 << 4)
98#define PCMMIO_AI_STATUS_DATA_DRQ_ENA (1 << 2)
99#define PCMMIO_AI_STATUS_REG_SEL (1 << 3)
100#define PCMMIO_AI_STATUS_CMD_DRQ_ENA (1 << 1)
101#define PCMMIO_AI_STATUS_IRQ_ENA (1 << 0)
102#define PCMMIO_AI_RES_ENA_REG 0x03
103#define PCMMIO_AI_RES_ENA_CMD_REG_ACCESS (0 << 3)
104#define PCMMIO_AI_RES_ENA_AI_RES_ACCESS (1 << 3)
105#define PCMMIO_AI_RES_ENA_DIO_RES_ACCESS (1 << 4)
106#define PCMMIO_AI_2ND_ADC_OFFSET 0x04
107
108#define PCMMIO_AO_LSB_REG 0x08
109#define PCMMIO_AO_LSB_SPAN(x) (((x) & 0xf) << 0)
110#define PCMMIO_AO_MSB_REG 0x09
111#define PCMMIO_AO_CMD_REG 0x0a
112#define PCMMIO_AO_CMD_WR_SPAN (0x2 << 4)
113#define PCMMIO_AO_CMD_WR_CODE (0x3 << 4)
114#define PCMMIO_AO_CMD_UPDATE (0x4 << 4)
115#define PCMMIO_AO_CMD_UPDATE_ALL (0x5 << 4)
116#define PCMMIO_AO_CMD_WR_SPAN_UPDATE (0x6 << 4)
117#define PCMMIO_AO_CMD_WR_CODE_UPDATE (0x7 << 4)
118#define PCMMIO_AO_CMD_WR_SPAN_UPDATE_ALL (0x8 << 4)
119#define PCMMIO_AO_CMD_WR_CODE_UPDATE_ALL (0x9 << 4)
120#define PCMMIO_AO_CMD_RD_B1_SPAN (0xa << 4)
121#define PCMMIO_AO_CMD_RD_B1_CODE (0xb << 4)
122#define PCMMIO_AO_CMD_RD_B2_SPAN (0xc << 4)
123#define PCMMIO_AO_CMD_RD_B2_CODE (0xd << 4)
124#define PCMMIO_AO_CMD_NOP (0xf << 4)
125#define PCMMIO_AO_CMD_CHAN_SEL(x) (((x) & 0x03) << 1)
126#define PCMMIO_AO_CMD_CHAN_SEL_ALL (0x0f << 0)
127#define PCMMIO_AO_STATUS_REG 0x0b
128#define PCMMIO_AO_STATUS_DATA_READY (1 << 7)
129#define PCMMIO_AO_STATUS_DATA_DMA_PEND (1 << 6)
130#define PCMMIO_AO_STATUS_CMD_DMA_PEND (1 << 5)
131#define PCMMIO_AO_STATUS_IRQ_PEND (1 << 4)
132#define PCMMIO_AO_STATUS_DATA_DRQ_ENA (1 << 2)
133#define PCMMIO_AO_STATUS_REG_SEL (1 << 3)
134#define PCMMIO_AO_STATUS_CMD_DRQ_ENA (1 << 1)
135#define PCMMIO_AO_STATUS_IRQ_ENA (1 << 0)
136#define PCMMIO_AO_RESOURCE_ENA_REG 0x0b
137#define PCMMIO_AO_2ND_DAC_OFFSET 0x04
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156#define PCMMIO_PORT_REG(x) (0x10 + (x))
157#define PCMMIO_INT_PENDING_REG 0x16
158#define PCMMIO_PAGE_LOCK_REG 0x17
159#define PCMMIO_LOCK_PORT(x) ((1 << (x)) & 0x3f)
160#define PCMMIO_PAGE(x) (((x) & 0x3) << 6)
161#define PCMMIO_PAGE_MASK PCMUIO_PAGE(3)
162#define PCMMIO_PAGE_POL 1
163#define PCMMIO_PAGE_ENAB 2
164#define PCMMIO_PAGE_INT_ID 3
165#define PCMMIO_PAGE_REG(x) (0x18 + (x))
166
167static const struct comedi_lrange pcmmio_ai_ranges = {
168 4, {
169 BIP_RANGE(5),
170 BIP_RANGE(10),
171 UNI_RANGE(5),
172 UNI_RANGE(10)
173 }
174};
175
176static const struct comedi_lrange pcmmio_ao_ranges = {
177 6, {
178 UNI_RANGE(5),
179 UNI_RANGE(10),
180 BIP_RANGE(5),
181 BIP_RANGE(10),
182 BIP_RANGE(2.5),
183 RANGE(-2.5, 7.5)
184 }
185};
186
187struct pcmmio_private {
188 spinlock_t pagelock;
189 spinlock_t spinlock;
190 unsigned int enabled_mask;
191 unsigned int active:1;
192};
193
194static void pcmmio_dio_write(struct comedi_device *dev, unsigned int val,
195 int page, int port)
196{
197 struct pcmmio_private *devpriv = dev->private;
198 unsigned long iobase = dev->iobase;
199 unsigned long flags;
200
201 spin_lock_irqsave(&devpriv->pagelock, flags);
202 if (page == 0) {
203
204 outb(val & 0xff, iobase + PCMMIO_PORT_REG(port + 0));
205 outb((val >> 8) & 0xff, iobase + PCMMIO_PORT_REG(port + 1));
206 outb((val >> 16) & 0xff, iobase + PCMMIO_PORT_REG(port + 2));
207 } else {
208 outb(PCMMIO_PAGE(page), iobase + PCMMIO_PAGE_LOCK_REG);
209 outb(val & 0xff, iobase + PCMMIO_PAGE_REG(0));
210 outb((val >> 8) & 0xff, iobase + PCMMIO_PAGE_REG(1));
211 outb((val >> 16) & 0xff, iobase + PCMMIO_PAGE_REG(2));
212 }
213 spin_unlock_irqrestore(&devpriv->pagelock, flags);
214}
215
216static unsigned int pcmmio_dio_read(struct comedi_device *dev,
217 int page, int port)
218{
219 struct pcmmio_private *devpriv = dev->private;
220 unsigned long iobase = dev->iobase;
221 unsigned long flags;
222 unsigned int val;
223
224 spin_lock_irqsave(&devpriv->pagelock, flags);
225 if (page == 0) {
226
227 val = inb(iobase + PCMMIO_PORT_REG(port + 0));
228 val |= (inb(iobase + PCMMIO_PORT_REG(port + 1)) << 8);
229 val |= (inb(iobase + PCMMIO_PORT_REG(port + 2)) << 16);
230 } else {
231 outb(PCMMIO_PAGE(page), iobase + PCMMIO_PAGE_LOCK_REG);
232 val = inb(iobase + PCMMIO_PAGE_REG(0));
233 val |= (inb(iobase + PCMMIO_PAGE_REG(1)) << 8);
234 val |= (inb(iobase + PCMMIO_PAGE_REG(2)) << 16);
235 }
236 spin_unlock_irqrestore(&devpriv->pagelock, flags);
237
238 return val;
239}
240
241
242
243
244
245
246
247
248
249
250
251static int pcmmio_dio_insn_bits(struct comedi_device *dev,
252 struct comedi_subdevice *s,
253 struct comedi_insn *insn,
254 unsigned int *data)
255{
256
257 int port = s->index == 2 ? 0 : 3;
258 unsigned int chanmask = (1 << s->n_chan) - 1;
259 unsigned int mask;
260 unsigned int val;
261
262 mask = comedi_dio_update_state(s, data);
263 if (mask) {
264
265
266
267
268
269
270
271
272 val = ~s->state & chanmask;
273 val &= s->io_bits;
274 pcmmio_dio_write(dev, val, 0, port);
275 }
276
277
278 val = pcmmio_dio_read(dev, 0, port);
279
280
281 data[1] = ~val & chanmask;
282
283 return insn->n;
284}
285
286static int pcmmio_dio_insn_config(struct comedi_device *dev,
287 struct comedi_subdevice *s,
288 struct comedi_insn *insn,
289 unsigned int *data)
290{
291
292 int port = s->index == 2 ? 0 : 3;
293 int ret;
294
295 ret = comedi_dio_insn_config(dev, s, insn, data, 0);
296 if (ret)
297 return ret;
298
299 if (data[0] == INSN_CONFIG_DIO_INPUT)
300 pcmmio_dio_write(dev, s->io_bits, 0, port);
301
302 return insn->n;
303}
304
305static void pcmmio_reset(struct comedi_device *dev)
306{
307
308 pcmmio_dio_write(dev, 0, 0, 0);
309 pcmmio_dio_write(dev, 0, 0, 3);
310
311
312 pcmmio_dio_write(dev, 0, PCMMIO_PAGE_POL, 0);
313 pcmmio_dio_write(dev, 0, PCMMIO_PAGE_ENAB, 0);
314 pcmmio_dio_write(dev, 0, PCMMIO_PAGE_INT_ID, 0);
315}
316
317
318static void pcmmio_stop_intr(struct comedi_device *dev,
319 struct comedi_subdevice *s)
320{
321 struct pcmmio_private *devpriv = dev->private;
322
323 devpriv->enabled_mask = 0;
324 devpriv->active = 0;
325 s->async->inttrig = NULL;
326
327
328 pcmmio_dio_write(dev, 0, PCMMIO_PAGE_ENAB, 0);
329}
330
331static void pcmmio_handle_dio_intr(struct comedi_device *dev,
332 struct comedi_subdevice *s,
333 unsigned int triggered)
334{
335 struct pcmmio_private *devpriv = dev->private;
336 struct comedi_cmd *cmd = &s->async->cmd;
337 unsigned int val = 0;
338 unsigned long flags;
339 int i;
340
341 spin_lock_irqsave(&devpriv->spinlock, flags);
342
343 if (!devpriv->active)
344 goto done;
345
346 if (!(triggered & devpriv->enabled_mask))
347 goto done;
348
349 for (i = 0; i < cmd->chanlist_len; i++) {
350 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
351
352 if (triggered & (1 << chan))
353 val |= (1 << i);
354 }
355
356 comedi_buf_write_samples(s, &val, 1);
357
358 if (cmd->stop_src == TRIG_COUNT &&
359 s->async->scans_done >= cmd->stop_arg)
360 s->async->events |= COMEDI_CB_EOA;
361
362done:
363 spin_unlock_irqrestore(&devpriv->spinlock, flags);
364
365 comedi_handle_events(dev, s);
366}
367
368static irqreturn_t interrupt_pcmmio(int irq, void *d)
369{
370 struct comedi_device *dev = d;
371 struct comedi_subdevice *s = dev->read_subdev;
372 unsigned int triggered;
373 unsigned char int_pend;
374
375
376 int_pend = inb(dev->iobase + PCMMIO_INT_PENDING_REG) & 0x07;
377 if (!int_pend)
378 return IRQ_NONE;
379
380
381 triggered = pcmmio_dio_read(dev, PCMMIO_PAGE_INT_ID, 0);
382 pcmmio_dio_write(dev, 0, PCMMIO_PAGE_INT_ID, 0);
383
384 pcmmio_handle_dio_intr(dev, s, triggered);
385
386 return IRQ_HANDLED;
387}
388
389
390static void pcmmio_start_intr(struct comedi_device *dev,
391 struct comedi_subdevice *s)
392{
393 struct pcmmio_private *devpriv = dev->private;
394 struct comedi_cmd *cmd = &s->async->cmd;
395 unsigned int bits = 0;
396 unsigned int pol_bits = 0;
397 int i;
398
399 devpriv->enabled_mask = 0;
400 devpriv->active = 1;
401 if (cmd->chanlist) {
402 for (i = 0; i < cmd->chanlist_len; i++) {
403 unsigned int chanspec = cmd->chanlist[i];
404 unsigned int chan = CR_CHAN(chanspec);
405 unsigned int range = CR_RANGE(chanspec);
406 unsigned int aref = CR_AREF(chanspec);
407
408 bits |= (1 << chan);
409 pol_bits |= (((aref || range) ? 1 : 0) << chan);
410 }
411 }
412 bits &= ((1 << s->n_chan) - 1);
413 devpriv->enabled_mask = bits;
414
415
416 pcmmio_dio_write(dev, pol_bits, PCMMIO_PAGE_POL, 0);
417 pcmmio_dio_write(dev, bits, PCMMIO_PAGE_ENAB, 0);
418}
419
420static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
421{
422 struct pcmmio_private *devpriv = dev->private;
423 unsigned long flags;
424
425 spin_lock_irqsave(&devpriv->spinlock, flags);
426 if (devpriv->active)
427 pcmmio_stop_intr(dev, s);
428 spin_unlock_irqrestore(&devpriv->spinlock, flags);
429
430 return 0;
431}
432
433static int pcmmio_inttrig_start_intr(struct comedi_device *dev,
434 struct comedi_subdevice *s,
435 unsigned int trig_num)
436{
437 struct pcmmio_private *devpriv = dev->private;
438 struct comedi_cmd *cmd = &s->async->cmd;
439 unsigned long flags;
440
441 if (trig_num != cmd->start_arg)
442 return -EINVAL;
443
444 spin_lock_irqsave(&devpriv->spinlock, flags);
445 s->async->inttrig = NULL;
446 if (devpriv->active)
447 pcmmio_start_intr(dev, s);
448 spin_unlock_irqrestore(&devpriv->spinlock, flags);
449
450 return 1;
451}
452
453
454
455
456static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
457{
458 struct pcmmio_private *devpriv = dev->private;
459 struct comedi_cmd *cmd = &s->async->cmd;
460 unsigned long flags;
461
462 spin_lock_irqsave(&devpriv->spinlock, flags);
463 devpriv->active = 1;
464
465
466 if (cmd->start_src == TRIG_INT)
467 s->async->inttrig = pcmmio_inttrig_start_intr;
468 else
469 pcmmio_start_intr(dev, s);
470
471 spin_unlock_irqrestore(&devpriv->spinlock, flags);
472
473 return 0;
474}
475
476static int pcmmio_cmdtest(struct comedi_device *dev,
477 struct comedi_subdevice *s,
478 struct comedi_cmd *cmd)
479{
480 int err = 0;
481
482
483
484 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
485 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
486 err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW);
487 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
488 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
489
490 if (err)
491 return 1;
492
493
494
495 err |= comedi_check_trigger_is_unique(cmd->start_src);
496 err |= comedi_check_trigger_is_unique(cmd->stop_src);
497
498
499
500 if (err)
501 return 2;
502
503
504
505 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
506 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
507 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
508 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
509 cmd->chanlist_len);
510
511 if (cmd->stop_src == TRIG_COUNT)
512 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
513 else
514 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
515
516 if (err)
517 return 3;
518
519
520
521
522
523 return 0;
524}
525
526static int pcmmio_ai_eoc(struct comedi_device *dev,
527 struct comedi_subdevice *s,
528 struct comedi_insn *insn,
529 unsigned long context)
530{
531 unsigned char status;
532
533 status = inb(dev->iobase + PCMMIO_AI_STATUS_REG);
534 if (status & PCMMIO_AI_STATUS_DATA_READY)
535 return 0;
536 return -EBUSY;
537}
538
539static int pcmmio_ai_insn_read(struct comedi_device *dev,
540 struct comedi_subdevice *s,
541 struct comedi_insn *insn,
542 unsigned int *data)
543{
544 unsigned long iobase = dev->iobase;
545 unsigned int chan = CR_CHAN(insn->chanspec);
546 unsigned int range = CR_RANGE(insn->chanspec);
547 unsigned int aref = CR_AREF(insn->chanspec);
548 unsigned char cmd = 0;
549 unsigned int val;
550 int ret;
551 int i;
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568 if (chan > 7) {
569 chan -= 8;
570 iobase += PCMMIO_AI_2ND_ADC_OFFSET;
571 }
572
573 if (aref == AREF_GROUND)
574 cmd |= PCMMIO_AI_CMD_SE;
575 if (chan % 2)
576 cmd |= PCMMIO_AI_CMD_ODD_CHAN;
577 cmd |= PCMMIO_AI_CMD_CHAN_SEL(chan / 2);
578 cmd |= PCMMIO_AI_CMD_RANGE(range);
579
580 outb(cmd, iobase + PCMMIO_AI_CMD_REG);
581
582 ret = comedi_timeout(dev, s, insn, pcmmio_ai_eoc, 0);
583 if (ret)
584 return ret;
585
586 val = inb(iobase + PCMMIO_AI_LSB_REG);
587 val |= inb(iobase + PCMMIO_AI_MSB_REG) << 8;
588
589 for (i = 0; i < insn->n; i++) {
590 outb(cmd, iobase + PCMMIO_AI_CMD_REG);
591
592 ret = comedi_timeout(dev, s, insn, pcmmio_ai_eoc, 0);
593 if (ret)
594 return ret;
595
596 val = inb(iobase + PCMMIO_AI_LSB_REG);
597 val |= inb(iobase + PCMMIO_AI_MSB_REG) << 8;
598
599
600 if (comedi_range_is_bipolar(s, range))
601 val = comedi_offset_munge(s, val);
602
603 data[i] = val;
604 }
605
606 return insn->n;
607}
608
609static int pcmmio_ao_eoc(struct comedi_device *dev,
610 struct comedi_subdevice *s,
611 struct comedi_insn *insn,
612 unsigned long context)
613{
614 unsigned char status;
615
616 status = inb(dev->iobase + PCMMIO_AO_STATUS_REG);
617 if (status & PCMMIO_AO_STATUS_DATA_READY)
618 return 0;
619 return -EBUSY;
620}
621
622static int pcmmio_ao_insn_write(struct comedi_device *dev,
623 struct comedi_subdevice *s,
624 struct comedi_insn *insn,
625 unsigned int *data)
626{
627 unsigned long iobase = dev->iobase;
628 unsigned int chan = CR_CHAN(insn->chanspec);
629 unsigned int range = CR_RANGE(insn->chanspec);
630 unsigned char cmd = 0;
631 int ret;
632 int i;
633
634
635
636
637
638
639 if (chan > 3) {
640 cmd |= PCMMIO_AO_CMD_CHAN_SEL(chan - 4);
641 iobase += PCMMIO_AO_2ND_DAC_OFFSET;
642 } else {
643 cmd |= PCMMIO_AO_CMD_CHAN_SEL(chan);
644 }
645
646
647 outb(PCMMIO_AO_LSB_SPAN(range), iobase + PCMMIO_AO_LSB_REG);
648 outb(0, iobase + PCMMIO_AO_MSB_REG);
649 outb(cmd | PCMMIO_AO_CMD_WR_SPAN_UPDATE, iobase + PCMMIO_AO_CMD_REG);
650
651 ret = comedi_timeout(dev, s, insn, pcmmio_ao_eoc, 0);
652 if (ret)
653 return ret;
654
655 for (i = 0; i < insn->n; i++) {
656 unsigned int val = data[i];
657
658
659 outb(val & 0xff, iobase + PCMMIO_AO_LSB_REG);
660 outb((val >> 8) & 0xff, iobase + PCMMIO_AO_MSB_REG);
661 outb(cmd | PCMMIO_AO_CMD_WR_CODE_UPDATE,
662 iobase + PCMMIO_AO_CMD_REG);
663
664 ret = comedi_timeout(dev, s, insn, pcmmio_ao_eoc, 0);
665 if (ret)
666 return ret;
667
668 s->readback[chan] = val;
669 }
670
671 return insn->n;
672}
673
674static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
675{
676 struct pcmmio_private *devpriv;
677 struct comedi_subdevice *s;
678 int ret;
679
680 ret = comedi_request_region(dev, it->options[0], 32);
681 if (ret)
682 return ret;
683
684 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
685 if (!devpriv)
686 return -ENOMEM;
687
688 spin_lock_init(&devpriv->pagelock);
689 spin_lock_init(&devpriv->spinlock);
690
691 pcmmio_reset(dev);
692
693 if (it->options[1]) {
694 ret = request_irq(it->options[1], interrupt_pcmmio, 0,
695 dev->board_name, dev);
696 if (ret == 0) {
697 dev->irq = it->options[1];
698
699
700 outb(PCMMIO_AI_RES_ENA_DIO_RES_ACCESS,
701 dev->iobase + PCMMIO_AI_RES_ENA_REG);
702 outb(PCMMIO_RESOURCE_IRQ(dev->irq),
703 dev->iobase + PCMMIO_RESOURCE_REG);
704 }
705 }
706
707 ret = comedi_alloc_subdevices(dev, 4);
708 if (ret)
709 return ret;
710
711
712 s = &dev->subdevices[0];
713 s->type = COMEDI_SUBD_AI;
714 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
715 s->n_chan = 16;
716 s->maxdata = 0xffff;
717 s->range_table = &pcmmio_ai_ranges;
718 s->insn_read = pcmmio_ai_insn_read;
719
720
721 outb(PCMMIO_AI_RES_ENA_CMD_REG_ACCESS,
722 dev->iobase + PCMMIO_AI_RES_ENA_REG);
723 outb(PCMMIO_AI_RES_ENA_CMD_REG_ACCESS,
724 dev->iobase + PCMMIO_AI_RES_ENA_REG + PCMMIO_AI_2ND_ADC_OFFSET);
725
726
727 s = &dev->subdevices[1];
728 s->type = COMEDI_SUBD_AO;
729 s->subdev_flags = SDF_READABLE;
730 s->n_chan = 8;
731 s->maxdata = 0xffff;
732 s->range_table = &pcmmio_ao_ranges;
733 s->insn_write = pcmmio_ao_insn_write;
734
735 ret = comedi_alloc_subdev_readback(s);
736 if (ret)
737 return ret;
738
739
740 outb(0, dev->iobase + PCMMIO_AO_RESOURCE_ENA_REG);
741 outb(0, dev->iobase + PCMMIO_AO_2ND_DAC_OFFSET +
742 PCMMIO_AO_RESOURCE_ENA_REG);
743
744
745 s = &dev->subdevices[2];
746 s->type = COMEDI_SUBD_DIO;
747 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
748 s->n_chan = 24;
749 s->maxdata = 1;
750 s->len_chanlist = 1;
751 s->range_table = &range_digital;
752 s->insn_bits = pcmmio_dio_insn_bits;
753 s->insn_config = pcmmio_dio_insn_config;
754 if (dev->irq) {
755 dev->read_subdev = s;
756 s->subdev_flags |= SDF_CMD_READ | SDF_LSAMPL | SDF_PACKED;
757 s->len_chanlist = s->n_chan;
758 s->cancel = pcmmio_cancel;
759 s->do_cmd = pcmmio_cmd;
760 s->do_cmdtest = pcmmio_cmdtest;
761 }
762
763
764 s = &dev->subdevices[3];
765 s->type = COMEDI_SUBD_DIO;
766 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
767 s->n_chan = 24;
768 s->maxdata = 1;
769 s->range_table = &range_digital;
770 s->insn_bits = pcmmio_dio_insn_bits;
771 s->insn_config = pcmmio_dio_insn_config;
772
773 return 0;
774}
775
776static struct comedi_driver pcmmio_driver = {
777 .driver_name = "pcmmio",
778 .module = THIS_MODULE,
779 .attach = pcmmio_attach,
780 .detach = comedi_legacy_detach,
781};
782module_comedi_driver(pcmmio_driver);
783
784MODULE_AUTHOR("Comedi http://www.comedi.org");
785MODULE_DESCRIPTION("Comedi driver for Winsystems PCM-MIO PC/104 board");
786MODULE_LICENSE("GPL");
787