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#include <linux/interrupt.h>
79#include "../comedidev.h"
80#include "pcm_common.h"
81
82#include <linux/pci.h>
83
84#define CHANS_PER_PORT 8
85#define PORTS_PER_ASIC 6
86#define INTR_PORTS_PER_ASIC 3
87#define MAX_CHANS_PER_SUBDEV 24
88#define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT)
89#define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC)
90#define INTR_CHANS_PER_ASIC 24
91#define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT)
92#define MAX_DIO_CHANS (PORTS_PER_ASIC*2*CHANS_PER_PORT)
93#define MAX_ASICS (MAX_DIO_CHANS/CHANS_PER_ASIC)
94#define SDEV_NO ((int)(s - dev->subdevices))
95#define CALC_N_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) )
96
97#define ASIC_IOSIZE (0x10)
98#define PCMUIO48_IOSIZE ASIC_IOSIZE
99#define PCMUIO96_IOSIZE (ASIC_IOSIZE*2)
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114#define REG_PORT0 0x0
115#define REG_PORT1 0x1
116#define REG_PORT2 0x2
117#define REG_PORT3 0x3
118#define REG_PORT4 0x4
119#define REG_PORT5 0x5
120#define REG_INT_PENDING 0x6
121#define REG_PAGELOCK 0x7
122
123
124#define REG_POL0 0x8
125#define REG_POL1 0x9
126#define REG_POL2 0xA
127#define REG_ENAB0 0x8
128#define REG_ENAB1 0x9
129#define REG_ENAB2 0xA
130#define REG_INT_ID0 0x8
131#define REG_INT_ID1 0x9
132#define REG_INT_ID2 0xA
133
134#define NUM_PAGED_REGS 3
135#define NUM_PAGES 4
136#define FIRST_PAGED_REG 0x8
137#define REG_PAGE_BITOFFSET 6
138#define REG_LOCK_BITOFFSET 0
139#define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1))
140#define REG_LOCK_MASK ~(REG_PAGE_MASK)
141#define PAGE_POL 1
142#define PAGE_ENAB 2
143#define PAGE_INT_ID 3
144
145
146
147
148
149
150struct pcmuio_board {
151 const char *name;
152 const int num_asics;
153 const int num_channels_per_port;
154 const int num_ports;
155};
156
157static const struct pcmuio_board pcmuio_boards[] = {
158 {
159 .name = "pcmuio48",
160 .num_asics = 1,
161 .num_ports = 6,
162 },
163 {
164 .name = "pcmuio96",
165 .num_asics = 2,
166 .num_ports = 12,
167 },
168};
169
170
171
172
173#define thisboard ((const struct pcmuio_board *)dev->board_ptr)
174
175
176struct pcmuio_subdev_private {
177
178 unsigned long iobases[PORTS_PER_SUBDEV];
179
180
181 struct {
182 int asic;
183 int first_chan;
184
185 int num_asic_chans;
186
187 int asic_chan;
188
189 int enabled_mask;
190
191 int active;
192 int stop_count;
193 int continuous;
194 spinlock_t spinlock;
195 } intr;
196};
197
198
199
200
201struct pcmuio_private {
202 struct {
203 unsigned char pagelock;
204 unsigned char pol[NUM_PAGED_REGS];
205 unsigned char enab[NUM_PAGED_REGS];
206 int num;
207 unsigned long iobase;
208 unsigned int irq;
209 spinlock_t spinlock;
210 } asics[MAX_ASICS];
211 struct pcmuio_subdev_private *sprivs;
212};
213
214
215
216
217
218#define devpriv ((struct pcmuio_private *)dev->private)
219#define subpriv ((struct pcmuio_subdev_private *)s->private)
220
221
222
223
224
225
226static int pcmuio_attach(struct comedi_device *dev,
227 struct comedi_devconfig *it);
228static int pcmuio_detach(struct comedi_device *dev);
229
230static struct comedi_driver driver = {
231 .driver_name = "pcmuio",
232 .module = THIS_MODULE,
233 .attach = pcmuio_attach,
234 .detach = pcmuio_detach,
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253 .board_name = &pcmuio_boards[0].name,
254 .offset = sizeof(struct pcmuio_board),
255 .num_names = ARRAY_SIZE(pcmuio_boards),
256};
257
258static int pcmuio_dio_insn_bits(struct comedi_device *dev,
259 struct comedi_subdevice *s,
260 struct comedi_insn *insn, unsigned int *data);
261static int pcmuio_dio_insn_config(struct comedi_device *dev,
262 struct comedi_subdevice *s,
263 struct comedi_insn *insn, unsigned int *data);
264
265static irqreturn_t interrupt_pcmuio(int irq, void *d);
266static void pcmuio_stop_intr(struct comedi_device *, struct comedi_subdevice *);
267static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
268static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
269static int pcmuio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
270 struct comedi_cmd *cmd);
271
272
273static void init_asics(struct comedi_device *dev);
274static void switch_page(struct comedi_device *dev, int asic, int page);
275#ifdef notused
276static void lock_port(struct comedi_device *dev, int asic, int port);
277static void unlock_port(struct comedi_device *dev, int asic, int port);
278#endif
279
280
281
282
283
284
285
286static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
287{
288 struct comedi_subdevice *s;
289 int sdev_no, chans_left, n_subdevs, port, asic, thisasic_chanct = 0;
290 unsigned long iobase;
291 unsigned int irq[MAX_ASICS];
292
293 iobase = it->options[0];
294 irq[0] = it->options[1];
295 irq[1] = it->options[2];
296
297 printk("comedi%d: %s: io: %lx ", dev->minor, driver.driver_name,
298 iobase);
299
300 dev->iobase = iobase;
301
302 if (!iobase || !request_region(iobase,
303 thisboard->num_asics * ASIC_IOSIZE,
304 driver.driver_name)) {
305 printk("I/O port conflict\n");
306 return -EIO;
307 }
308
309
310
311
312
313 dev->board_name = thisboard->name;
314
315
316
317
318
319 if (alloc_private(dev, sizeof(struct pcmuio_private)) < 0) {
320 printk("cannot allocate private data structure\n");
321 return -ENOMEM;
322 }
323
324 for (asic = 0; asic < MAX_ASICS; ++asic) {
325 devpriv->asics[asic].num = asic;
326 devpriv->asics[asic].iobase = dev->iobase + asic * ASIC_IOSIZE;
327 devpriv->asics[asic].irq = 0;
328
329
330 spin_lock_init(&devpriv->asics[asic].spinlock);
331 }
332
333 chans_left = CHANS_PER_ASIC * thisboard->num_asics;
334 n_subdevs = CALC_N_SUBDEVS(chans_left);
335 devpriv->sprivs =
336 kcalloc(n_subdevs, sizeof(struct pcmuio_subdev_private),
337 GFP_KERNEL);
338 if (!devpriv->sprivs) {
339 printk("cannot allocate subdevice private data structures\n");
340 return -ENOMEM;
341 }
342
343
344
345
346
347
348
349 if (alloc_subdevices(dev, n_subdevs) < 0) {
350 printk("cannot allocate subdevice data structures\n");
351 return -ENOMEM;
352 }
353
354 port = 0;
355 asic = 0;
356 for (sdev_no = 0; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
357 int byte_no;
358
359 s = dev->subdevices + sdev_no;
360 s->private = devpriv->sprivs + sdev_no;
361 s->maxdata = 1;
362 s->range_table = &range_digital;
363 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
364 s->type = COMEDI_SUBD_DIO;
365 s->insn_bits = pcmuio_dio_insn_bits;
366 s->insn_config = pcmuio_dio_insn_config;
367 s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
368 subpriv->intr.asic = -1;
369 subpriv->intr.first_chan = -1;
370 subpriv->intr.asic_chan = -1;
371 subpriv->intr.num_asic_chans = -1;
372 subpriv->intr.active = 0;
373 s->len_chanlist = 1;
374
375
376
377 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
378 if (port >= PORTS_PER_ASIC) {
379 port = 0;
380 ++asic;
381 thisasic_chanct = 0;
382 }
383 subpriv->iobases[byte_no] =
384 devpriv->asics[asic].iobase + port;
385
386 if (thisasic_chanct <
387 CHANS_PER_PORT * INTR_PORTS_PER_ASIC
388 && subpriv->intr.asic < 0) {
389
390 subpriv->intr.asic = asic;
391 subpriv->intr.active = 0;
392 subpriv->intr.stop_count = 0;
393 subpriv->intr.first_chan = byte_no * 8;
394 subpriv->intr.asic_chan = thisasic_chanct;
395 subpriv->intr.num_asic_chans =
396 s->n_chan - subpriv->intr.first_chan;
397 dev->read_subdev = s;
398 s->subdev_flags |= SDF_CMD_READ;
399 s->cancel = pcmuio_cancel;
400 s->do_cmd = pcmuio_cmd;
401 s->do_cmdtest = pcmuio_cmdtest;
402 s->len_chanlist = subpriv->intr.num_asic_chans;
403 }
404 thisasic_chanct += CHANS_PER_PORT;
405 }
406 spin_lock_init(&subpriv->intr.spinlock);
407
408 chans_left -= s->n_chan;
409
410 if (!chans_left) {
411 asic = 0;
412 port = 0;
413 }
414
415 }
416
417 init_asics(dev);
418
419 for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
420 if (irq[asic]
421 && request_irq(irq[asic], interrupt_pcmuio,
422 IRQF_SHARED, thisboard->name, dev)) {
423 int i;
424
425 for (i = asic - 1; i >= 0; --i) {
426 free_irq(irq[i], dev);
427 devpriv->asics[i].irq = irq[i] = 0;
428 }
429 irq[asic] = 0;
430 }
431 devpriv->asics[asic].irq = irq[asic];
432 }
433
434 dev->irq = irq[0];
435
436
437 if (irq[0]) {
438 printk("irq: %u ", irq[0]);
439 if (irq[1] && thisboard->num_asics == 2)
440 printk("second ASIC irq: %u ", irq[1]);
441 } else {
442 printk("(IRQ mode disabled) ");
443 }
444
445 printk("attached\n");
446
447 return 1;
448}
449
450
451
452
453
454
455
456
457
458static int pcmuio_detach(struct comedi_device *dev)
459{
460 int i;
461
462 printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
463 if (dev->iobase)
464 release_region(dev->iobase, ASIC_IOSIZE * thisboard->num_asics);
465
466 for (i = 0; i < MAX_ASICS; ++i) {
467 if (devpriv->asics[i].irq)
468 free_irq(devpriv->asics[i].irq, dev);
469 }
470
471 if (devpriv && devpriv->sprivs)
472 kfree(devpriv->sprivs);
473
474 return 0;
475}
476
477
478
479
480
481
482static int pcmuio_dio_insn_bits(struct comedi_device *dev,
483 struct comedi_subdevice *s,
484 struct comedi_insn *insn, unsigned int *data)
485{
486 int byte_no;
487 if (insn->n != 2)
488 return -EINVAL;
489
490
491
492
493
494
495
496
497
498
499
500
501#ifdef DAMMIT_ITS_BROKEN
502
503 printk("write mask: %08x data: %08x\n", data[0], data[1]);
504#endif
505
506 s->state = 0;
507
508 for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
509
510 unsigned long ioaddr = subpriv->iobases[byte_no],
511
512 offset = byte_no * 8;
513
514 unsigned char byte = 0,
515
516 write_mask_byte = (data[0] >> offset) & 0xff,
517
518 data_byte = (data[1] >> offset) & 0xff;
519
520 byte = inb(ioaddr);
521
522#ifdef DAMMIT_ITS_BROKEN
523
524 printk
525 ("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ",
526 byte_no, (unsigned)write_mask_byte, (unsigned)data_byte,
527 offset, ioaddr, (unsigned)byte);
528#endif
529
530 if (write_mask_byte) {
531
532 byte &= ~write_mask_byte;
533 byte |= ~data_byte & write_mask_byte;
534
535 outb(byte, ioaddr);
536 }
537#ifdef DAMMIT_ITS_BROKEN
538
539 printk("data_out_byte %02x\n", (unsigned)byte);
540#endif
541
542 s->state |= ((unsigned int)byte) << offset;
543 }
544
545
546 data[1] = ~s->state;
547
548#ifdef DAMMIT_ITS_BROKEN
549
550 printk("s->state %08x data_out %08x\n", s->state, data[1]);
551#endif
552
553 return 2;
554}
555
556
557
558
559
560static int pcmuio_dio_insn_config(struct comedi_device *dev,
561 struct comedi_subdevice *s,
562 struct comedi_insn *insn, unsigned int *data)
563{
564 int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
565 chan % 8;
566 unsigned long ioaddr;
567 unsigned char byte;
568
569
570 ioaddr = subpriv->iobases[byte_no];
571
572
573
574
575
576
577
578
579
580
581
582
583
584 switch (data[0]) {
585 case INSN_CONFIG_DIO_OUTPUT:
586
587
588 s->io_bits |= 1 << chan;
589 break;
590 case INSN_CONFIG_DIO_INPUT:
591
592
593 byte = inb(ioaddr);
594 byte &= ~(1 << bit_no);
595
596
597
598
599
600 outb(byte, ioaddr);
601
602
603 s->io_bits &= ~(1 << chan);
604 break;
605
606 case INSN_CONFIG_DIO_QUERY:
607
608 data[1] =
609 (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
610 return insn->n;
611 break;
612
613 default:
614 return -EINVAL;
615 break;
616 }
617
618 return insn->n;
619}
620
621static void init_asics(struct comedi_device *dev)
622{
623
624 int asic;
625
626 for (asic = 0; asic < thisboard->num_asics; ++asic) {
627 int port, page;
628 unsigned long baseaddr = dev->iobase + asic * ASIC_IOSIZE;
629
630 switch_page(dev, asic, 0);
631
632
633 for (port = 0; port < PORTS_PER_ASIC; ++port)
634 outb(0, baseaddr + REG_PORT0 + port);
635
636
637 for (page = 1; page < NUM_PAGES; ++page) {
638 int reg;
639
640 switch_page(dev, asic, page);
641 for (reg = FIRST_PAGED_REG;
642 reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
643 outb(0, baseaddr + reg);
644 }
645
646
647
648
649
650
651
652
653 switch_page(dev, asic, 0);
654
655 }
656}
657
658static void switch_page(struct comedi_device *dev, int asic, int page)
659{
660 if (asic < 0 || asic >= thisboard->num_asics)
661 return;
662 if (page < 0 || page >= NUM_PAGES)
663 return;
664
665 devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
666 devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
667
668
669 outb(devpriv->asics[asic].pagelock,
670 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
671}
672
673#ifdef notused
674static void lock_port(struct comedi_device *dev, int asic, int port)
675{
676 if (asic < 0 || asic >= thisboard->num_asics)
677 return;
678 if (port < 0 || port >= PORTS_PER_ASIC)
679 return;
680
681 devpriv->asics[asic].pagelock |= 0x1 << port;
682
683 outb(devpriv->asics[asic].pagelock,
684 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
685}
686
687static void unlock_port(struct comedi_device *dev, int asic, int port)
688{
689 if (asic < 0 || asic >= thisboard->num_asics)
690 return;
691 if (port < 0 || port >= PORTS_PER_ASIC)
692 return;
693 devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
694
695 outb(devpriv->asics[asic].pagelock,
696 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
697}
698#endif
699
700static irqreturn_t interrupt_pcmuio(int irq, void *d)
701{
702 int asic, got1 = 0;
703 struct comedi_device *dev = (struct comedi_device *)d;
704
705 for (asic = 0; asic < MAX_ASICS; ++asic) {
706 if (irq == devpriv->asics[asic].irq) {
707 unsigned long flags;
708 unsigned triggered = 0;
709 unsigned long iobase = devpriv->asics[asic].iobase;
710
711 unsigned char int_pend;
712
713 spin_lock_irqsave(&devpriv->asics[asic].spinlock,
714 flags);
715
716 int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
717
718 if (int_pend) {
719 int port;
720 for (port = 0; port < INTR_PORTS_PER_ASIC;
721 ++port) {
722 if (int_pend & (0x1 << port)) {
723 unsigned char
724 io_lines_with_edges = 0;
725 switch_page(dev, asic,
726 PAGE_INT_ID);
727 io_lines_with_edges =
728 inb(iobase +
729 REG_INT_ID0 + port);
730
731 if (io_lines_with_edges)
732
733 outb(0, iobase +
734 REG_INT_ID0 +
735 port);
736
737 triggered |=
738 io_lines_with_edges <<
739 port * 8;
740 }
741 }
742
743 ++got1;
744 }
745
746 spin_unlock_irqrestore(&devpriv->asics[asic].spinlock,
747 flags);
748
749 if (triggered) {
750 struct comedi_subdevice *s;
751
752 printk
753 ("PCMUIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n",
754 irq, asic, triggered);
755 for (s = dev->subdevices;
756 s < dev->subdevices + dev->n_subdevices;
757 ++s) {
758 if (subpriv->intr.asic == asic) {
759 unsigned long flags;
760 unsigned oldevents;
761
762 spin_lock_irqsave(&subpriv->
763 intr.spinlock,
764 flags);
765
766 oldevents = s->async->events;
767
768 if (subpriv->intr.active) {
769 unsigned mytrig =
770 ((triggered >>
771 subpriv->intr.asic_chan)
772 &
773 ((0x1 << subpriv->
774 intr.
775 num_asic_chans) -
776 1)) << subpriv->
777 intr.first_chan;
778 if (mytrig &
779 subpriv->intr.enabled_mask)
780 {
781 unsigned int val
782 = 0;
783 unsigned int n,
784 ch, len;
785
786 len =
787 s->
788 async->cmd.chanlist_len;
789 for (n = 0;
790 n < len;
791 n++) {
792 ch = CR_CHAN(s->async->cmd.chanlist[n]);
793 if (mytrig & (1U << ch)) {
794 val |= (1U << n);
795 }
796 }
797
798 if (comedi_buf_put(s->async, ((short *)&val)[0])
799 &&
800 comedi_buf_put
801 (s->async,
802 ((short *)
803 &val)[1]))
804 {
805 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
806 } else {
807
808
809 pcmuio_stop_intr
810 (dev,
811 s);
812 }
813
814
815 if (!subpriv->intr.continuous) {
816
817 if (subpriv->intr.stop_count > 0) {
818 subpriv->intr.stop_count--;
819 if (subpriv->intr.stop_count == 0) {
820 s->async->events |= COMEDI_CB_EOA;
821
822 pcmuio_stop_intr
823 (dev,
824 s);
825 }
826 }
827 }
828 }
829 }
830
831 spin_unlock_irqrestore
832 (&subpriv->intr.spinlock,
833 flags);
834
835 if (oldevents !=
836 s->async->events) {
837 comedi_event(dev, s);
838 }
839
840 }
841
842 }
843 }
844
845 }
846 }
847 if (!got1)
848 return IRQ_NONE;
849 return IRQ_HANDLED;
850}
851
852static void pcmuio_stop_intr(struct comedi_device *dev,
853 struct comedi_subdevice *s)
854{
855 int nports, firstport, asic, port;
856
857 asic = subpriv->intr.asic;
858 if (asic < 0)
859 return;
860
861 subpriv->intr.enabled_mask = 0;
862 subpriv->intr.active = 0;
863 s->async->inttrig = 0;
864 nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
865 firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
866 switch_page(dev, asic, PAGE_ENAB);
867 for (port = firstport; port < firstport + nports; ++port) {
868
869 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
870 }
871}
872
873static int pcmuio_start_intr(struct comedi_device *dev,
874 struct comedi_subdevice *s)
875{
876 if (!subpriv->intr.continuous && subpriv->intr.stop_count == 0) {
877
878 s->async->events |= COMEDI_CB_EOA;
879 subpriv->intr.active = 0;
880 return 1;
881 } else {
882 unsigned bits = 0, pol_bits = 0, n;
883 int nports, firstport, asic, port;
884 struct comedi_cmd *cmd = &s->async->cmd;
885
886 asic = subpriv->intr.asic;
887 if (asic < 0)
888 return 1;
889
890 subpriv->intr.enabled_mask = 0;
891 subpriv->intr.active = 1;
892 nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
893 firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
894 if (cmd->chanlist) {
895 for (n = 0; n < cmd->chanlist_len; n++) {
896 bits |= (1U << CR_CHAN(cmd->chanlist[n]));
897 pol_bits |= (CR_AREF(cmd->chanlist[n])
898 || CR_RANGE(cmd->
899 chanlist[n]) ? 1U : 0U)
900 << CR_CHAN(cmd->chanlist[n]);
901 }
902 }
903 bits &= ((0x1 << subpriv->intr.num_asic_chans) -
904 1) << subpriv->intr.first_chan;
905 subpriv->intr.enabled_mask = bits;
906
907 switch_page(dev, asic, PAGE_ENAB);
908 for (port = firstport; port < firstport + nports; ++port) {
909 unsigned enab =
910 bits >> (subpriv->intr.first_chan + (port -
911 firstport) *
912 8) & 0xff, pol =
913 pol_bits >> (subpriv->intr.first_chan +
914 (port - firstport) * 8) & 0xff;
915
916 outb(enab,
917 devpriv->asics[asic].iobase + REG_ENAB0 + port);
918 switch_page(dev, asic, PAGE_POL);
919 outb(pol,
920 devpriv->asics[asic].iobase + REG_ENAB0 + port);
921 }
922 }
923 return 0;
924}
925
926static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
927{
928 unsigned long flags;
929
930 spin_lock_irqsave(&subpriv->intr.spinlock, flags);
931 if (subpriv->intr.active)
932 pcmuio_stop_intr(dev, s);
933 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
934
935 return 0;
936}
937
938
939
940
941static int
942pcmuio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
943 unsigned int trignum)
944{
945 unsigned long flags;
946 int event = 0;
947
948 if (trignum != 0)
949 return -EINVAL;
950
951 spin_lock_irqsave(&subpriv->intr.spinlock, flags);
952 s->async->inttrig = 0;
953 if (subpriv->intr.active) {
954 event = pcmuio_start_intr(dev, s);
955 }
956 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
957
958 if (event) {
959 comedi_event(dev, s);
960 }
961
962 return 1;
963}
964
965
966
967
968static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
969{
970 struct comedi_cmd *cmd = &s->async->cmd;
971 unsigned long flags;
972 int event = 0;
973
974 spin_lock_irqsave(&subpriv->intr.spinlock, flags);
975 subpriv->intr.active = 1;
976
977
978 switch (cmd->stop_src) {
979 case TRIG_COUNT:
980 subpriv->intr.continuous = 0;
981 subpriv->intr.stop_count = cmd->stop_arg;
982 break;
983 default:
984
985 subpriv->intr.continuous = 1;
986 subpriv->intr.stop_count = 0;
987 break;
988 }
989
990
991 switch (cmd->start_src) {
992 case TRIG_INT:
993 s->async->inttrig = pcmuio_inttrig_start_intr;
994 break;
995 default:
996
997 event = pcmuio_start_intr(dev, s);
998 break;
999 }
1000 spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
1001
1002 if (event) {
1003 comedi_event(dev, s);
1004 }
1005
1006 return 0;
1007}
1008
1009static int
1010pcmuio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
1011 struct comedi_cmd *cmd)
1012{
1013 return comedi_pcm_cmdtest(dev, s, cmd);
1014}
1015
1016
1017
1018
1019
1020COMEDI_INITCLEANUP(driver);
1021