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