2 comedi/drivers/pcmuio.c
3 Driver for Winsystems PC-104 based 48-channel and 96-channel DIO boards.
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 2006 Calin A. Culianu <calin@ajvar.org>
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.
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.
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.
24 Description: A driver for the PCM-UIO48A and PCM-UIO96A boards from Winsystems.
25 Devices: [Winsystems] PCM-UIO48A (pcmuio48), PCM-UIO96A (pcmuio96)
26 Author: Calin Culianu <calin@ajvar.org>
27 Updated: Fri, 13 Jan 2006 12:01:01 -0500
30 A driver for the relatively straightforward-to-program PCM-UIO48A and
31 PCM-UIO96A boards from Winsystems. These boards use either one or two
32 (in the 96-DIO version) WS16C48 ASIC HighDensity I/O Chips (HDIO).
33 This chip is interesting in that each I/O line is individually
34 programmable for INPUT or OUTPUT (thus comedi_dio_config can be done
35 on a per-channel basis). Also, each chip supports edge-triggered
36 interrupts for the first 24 I/O lines. Of course, since the
37 96-channel version of the board has two ASICs, it can detect polarity
38 changes on up to 48 I/O lines. Since this is essentially an (non-PnP)
39 ISA board, I/O Address and IRQ selection are done through jumpers on
40 the board. You need to pass that information to this driver as the
41 first and second comedi_config option, respectively. Note that the
42 48-channel version uses 16 bytes of IO memory and the 96-channel
43 version uses 32-bytes (in case you are worried about conflicts). The
44 48-channel board is split into two 24-channel comedi subdevices.
45 The 96-channel board is split into 4 24-channel DIO subdevices.
47 Note that IRQ support has been added, but it is untested.
49 To use edge-detection IRQ support, pass the IRQs of both ASICS
50 (for the 96 channel version) or just 1 ASIC (for 48-channel version).
51 Then, use use comedi_commands with TRIG_NOW.
52 Your callback will be called each time an edge is triggered, and the data
53 values will be two sample_t's, which should be concatenated to form one
54 32-bit unsigned int. This value is the mask of channels that had
55 edges detected from your channel list. Note that the bits positions
56 in the mask correspond to positions in your chanlist when you specified
57 the command and *not* channel id's!
59 To set the polarity of the edge-detection interrupts pass a nonzero value for
60 either CR_RANGE or CR_AREF for edge-up polarity, or a zero value for both
61 CR_RANGE and CR_AREF if you want edge-down polarity.
63 In the 48-channel version:
65 On subdev 0, the first 24 channels channels are edge-detect channels.
67 In the 96-channel board you have the collowing channels that can do edge detection:
69 subdev 0, channels 0-24 (first 24 channels of 1st ASIC)
70 subdev 2, channels 0-24 (first 24 channels of 2nd ASIC)
72 Configuration Options:
73 [0] - I/O port base address
74 [1] - IRQ (for first ASIC, or first 24 channels)
75 [2] - IRQ for second ASIC (pcmuio96 only - IRQ for chans 48-72 .. can be the same as first irq!)
78 #include "../comedidev.h"
80 #include <linux/pci.h> /* for PCI devices */
82 #define MIN(a, b) ( ((a) < (b)) ? (a) : (b) )
83 #define CHANS_PER_PORT 8
84 #define PORTS_PER_ASIC 6
85 #define INTR_PORTS_PER_ASIC 3
86 #define MAX_CHANS_PER_SUBDEV 24 /* number of channels per comedi subdevice */
87 #define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT)
88 #define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC)
89 #define INTR_CHANS_PER_ASIC 24
90 #define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT)
91 #define MAX_DIO_CHANS (PORTS_PER_ASIC*2*CHANS_PER_PORT)
92 #define MAX_ASICS (MAX_DIO_CHANS/CHANS_PER_ASIC)
93 #define SDEV_NO ((int)(s - dev->subdevices))
94 #define CALC_N_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) /*+ (nchans > INTR_CHANS_PER_ASIC ? 2 : 1)*/)
96 #define ASIC_IOSIZE (0x10)
97 #define PCMUIO48_IOSIZE ASIC_IOSIZE
98 #define PCMUIO96_IOSIZE (ASIC_IOSIZE*2)
100 /* Some offsets - these are all in the 16byte IO memory offset from
101 the base address. Note that there is a paging scheme to swap out
102 offsets 0x8-0xA using the PAGELOCK register. See the table below.
104 Register(s) Pages R/W? Description
105 --------------------------------------------------------------
106 REG_PORTx All R/W Read/Write/Configure IO
107 REG_INT_PENDING All ReadOnly Quickly see which INT_IDx has int.
108 REG_PAGELOCK All WriteOnly Select a page
109 REG_POLx Pg. 1 only WriteOnly Select edge-detection polarity
110 REG_ENABx Pg. 2 only WriteOnly Enable/Disable edge-detect. int.
111 REG_INT_IDx Pg. 3 only R/W See which ports/bits have ints.
113 #define REG_PORT0 0x0
114 #define REG_PORT1 0x1
115 #define REG_PORT2 0x2
116 #define REG_PORT3 0x3
117 #define REG_PORT4 0x4
118 #define REG_PORT5 0x5
119 #define REG_INT_PENDING 0x6
120 #define REG_PAGELOCK 0x7 /* page selector register, upper 2 bits select a page
121 and bits 0-5 are used to 'lock down' a particular
122 port above to make it readonly. */
126 #define REG_ENAB0 0x8
127 #define REG_ENAB1 0x9
128 #define REG_ENAB2 0xA
129 #define REG_INT_ID0 0x8
130 #define REG_INT_ID1 0x9
131 #define REG_INT_ID2 0xA
133 #define NUM_PAGED_REGS 3
135 #define FIRST_PAGED_REG 0x8
136 #define REG_PAGE_BITOFFSET 6
137 #define REG_LOCK_BITOFFSET 0
138 #define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1))
139 #define REG_LOCK_MASK ~(REG_PAGE_MASK)
142 #define PAGE_INT_ID 3
145 * Board descriptions for two imaginary boards. Describing the
146 * boards in this way is optional, and completely driver-dependent.
147 * Some drivers use arrays such as this, other do not.
149 struct pcmuio_board {
152 const int num_channels_per_port;
156 static const struct pcmuio_board pcmuio_boards[] = {
170 * Useful for shorthand access to the particular board structure
172 #define thisboard ((const struct pcmuio_board *)dev->board_ptr)
174 /* this structure is for data unique to this subdevice. */
175 struct pcmuio_subdev_private {
176 /* mapping of halfwords (bytes) in port/chanarray to iobase */
177 unsigned long iobases[PORTS_PER_SUBDEV];
179 /* The below is only used for intr subdevices */
181 int asic; /* if non-negative, this subdev has an interrupt asic */
182 int first_chan; /* if nonnegative, the first channel id for
184 int num_asic_chans; /* the number of asic channels in this subdev
185 that have interrutps */
186 int asic_chan; /* if nonnegative, the first channel id with
187 respect to the asic that has interrupts */
188 int enabled_mask; /* subdev-relative channel mask for channels
189 we are interested in */
197 /* this structure is for data unique to this hardware driver. If
198 several hardware drivers keep similar information in this structure,
199 feel free to suggest moving the variable to the struct comedi_device struct. */
200 struct pcmuio_private {
202 unsigned char pagelock; /* current page and lock */
203 unsigned char pol[NUM_PAGED_REGS]; /* shadow of POLx registers */
204 unsigned char enab[NUM_PAGED_REGS]; /* shadow of ENABx registers */
206 unsigned long iobase;
210 struct pcmuio_subdev_private *sprivs;
214 * most drivers define the following macro to make it easy to
215 * access the private structure.
217 #define devpriv ((struct pcmuio_private *)dev->private)
218 #define subpriv ((struct pcmuio_subdev_private *)s->private)
220 * The struct comedi_driver structure tells the Comedi core module
221 * which functions to call to configure/deconfigure (attach/detach)
222 * the board, and also about the kernel module that contains
225 static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it);
226 static int pcmuio_detach(struct comedi_device *dev);
228 static struct comedi_driver driver = {
229 driver_name:"pcmuio",
231 attach:pcmuio_attach,
232 detach:pcmuio_detach,
233 /* It is not necessary to implement the following members if you are
234 * writing a driver for a ISA PnP or PCI card */
235 /* Most drivers will support multiple types of boards by
236 * having an array of board structures. These were defined
237 * in pcmuio_boards[] above. Note that the element 'name'
238 * was first in the structure -- Comedi uses this fact to
239 * extract the name of the board without knowing any details
240 * about the structure except for its length.
241 * When a device is attached (by comedi_config), the name
242 * of the device is given to Comedi, and Comedi tries to
243 * match it by going through the list of board names. If
244 * there is a match, the address of the pointer is put
245 * into dev->board_ptr and driver->attach() is called.
247 * Note that these are not necessary if you can determine
248 * the type of board in software. ISA PnP, PCI, and PCMCIA
249 * devices are such boards.
251 board_name:&pcmuio_boards[0].name,
252 offset:sizeof(struct pcmuio_board),
253 num_names:sizeof(pcmuio_boards) / sizeof(struct pcmuio_board),
256 static int pcmuio_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
257 struct comedi_insn * insn, unsigned int * data);
258 static int pcmuio_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
259 struct comedi_insn * insn, unsigned int * data);
261 static irqreturn_t interrupt_pcmuio(int irq, void *d);
262 static void pcmuio_stop_intr(struct comedi_device *, struct comedi_subdevice *);
263 static int pcmuio_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
264 static int pcmuio_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
265 static int pcmuio_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
266 struct comedi_cmd * cmd);
268 /* some helper functions to deal with specifics of this device's registers */
269 static void init_asics(struct comedi_device * dev); /* sets up/clears ASIC chips to defaults */
270 static void switch_page(struct comedi_device * dev, int asic, int page);
272 static void lock_port(struct comedi_device * dev, int asic, int port);
273 static void unlock_port(struct comedi_device * dev, int asic, int port);
277 * Attach is called by the Comedi core to configure the driver
278 * for a particular board. If you specified a board_name array
279 * in the driver structure, dev->board_ptr contains that
282 static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
284 struct comedi_subdevice *s;
285 int sdev_no, chans_left, n_subdevs, port, asic, thisasic_chanct = 0;
286 unsigned long iobase;
287 unsigned int irq[MAX_ASICS];
289 iobase = it->options[0];
290 irq[0] = it->options[1];
291 irq[1] = it->options[2];
293 printk("comedi%d: %s: io: %lx ", dev->minor, driver.driver_name,
296 dev->iobase = iobase;
298 if (!iobase || !request_region(iobase,
299 thisboard->num_asics * ASIC_IOSIZE,
300 driver.driver_name)) {
301 printk("I/O port conflict\n");
306 * Initialize dev->board_name. Note that we can use the "thisboard"
307 * macro now, since we just initialized it in the last line.
309 dev->board_name = thisboard->name;
312 * Allocate the private structure area. alloc_private() is a
313 * convenient macro defined in comedidev.h.
315 if (alloc_private(dev, sizeof(struct pcmuio_private)) < 0) {
316 printk("cannot allocate private data structure\n");
320 for (asic = 0; asic < MAX_ASICS; ++asic) {
321 devpriv->asics[asic].num = asic;
322 devpriv->asics[asic].iobase = dev->iobase + asic * ASIC_IOSIZE;
323 devpriv->asics[asic].irq = 0; /* this gets actually set at the end of
324 this function when we
325 comedi_request_irqs */
326 spin_lock_init(&devpriv->asics[asic].spinlock);
329 chans_left = CHANS_PER_ASIC * thisboard->num_asics;
330 n_subdevs = CALC_N_SUBDEVS(chans_left);
332 kcalloc(n_subdevs, sizeof(struct pcmuio_subdev_private), GFP_KERNEL);
333 if (!devpriv->sprivs) {
334 printk("cannot allocate subdevice private data structures\n");
338 * Allocate the subdevice structures. alloc_subdevice() is a
339 * convenient macro defined in comedidev.h.
341 * Allocate 2 subdevs (32 + 16 DIO lines) or 3 32 DIO subdevs for the
342 * 96-channel version of the board.
344 if (alloc_subdevices(dev, n_subdevs) < 0) {
345 printk("cannot allocate subdevice data structures\n");
351 for (sdev_no = 0; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
354 s = dev->subdevices + sdev_no;
355 s->private = devpriv->sprivs + sdev_no;
357 s->range_table = &range_digital;
358 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
359 s->type = COMEDI_SUBD_DIO;
360 s->insn_bits = pcmuio_dio_insn_bits;
361 s->insn_config = pcmuio_dio_insn_config;
362 s->n_chan = MIN(chans_left, MAX_CHANS_PER_SUBDEV);
363 subpriv->intr.asic = -1;
364 subpriv->intr.first_chan = -1;
365 subpriv->intr.asic_chan = -1;
366 subpriv->intr.num_asic_chans = -1;
367 subpriv->intr.active = 0;
370 /* save the ioport address for each 'port' of 8 channels in the
372 for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
373 if (port >= PORTS_PER_ASIC) {
378 subpriv->iobases[byte_no] =
379 devpriv->asics[asic].iobase + port;
381 if (thisasic_chanct <
382 CHANS_PER_PORT * INTR_PORTS_PER_ASIC
383 && subpriv->intr.asic < 0) {
384 /* this is an interrupt subdevice, so setup the struct */
385 subpriv->intr.asic = asic;
386 subpriv->intr.active = 0;
387 subpriv->intr.stop_count = 0;
388 subpriv->intr.first_chan = byte_no * 8;
389 subpriv->intr.asic_chan = thisasic_chanct;
390 subpriv->intr.num_asic_chans =
391 s->n_chan - subpriv->intr.first_chan;
392 dev->read_subdev = s;
393 s->subdev_flags |= SDF_CMD_READ;
394 s->cancel = pcmuio_cancel;
395 s->do_cmd = pcmuio_cmd;
396 s->do_cmdtest = pcmuio_cmdtest;
397 s->len_chanlist = subpriv->intr.num_asic_chans;
399 thisasic_chanct += CHANS_PER_PORT;
401 spin_lock_init(&subpriv->intr.spinlock);
403 chans_left -= s->n_chan;
406 asic = 0; /* reset the asic to our first asic, to do intr subdevs */
412 init_asics(dev); /* clear out all the registers, basically */
414 for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
416 && comedi_request_irq(irq[asic], interrupt_pcmuio,
417 IRQF_SHARED, thisboard->name, dev)) {
419 /* unroll the allocated irqs.. */
420 for (i = asic - 1; i >= 0; --i) {
421 comedi_free_irq(irq[i], dev);
422 devpriv->asics[i].irq = irq[i] = 0;
426 devpriv->asics[asic].irq = irq[asic];
429 dev->irq = irq[0]; /* grr.. wish comedi dev struct supported multiple
433 printk("irq: %u ", irq[0]);
434 if (irq[1] && thisboard->num_asics == 2)
435 printk("second ASIC irq: %u ", irq[1]);
437 printk("(IRQ mode disabled) ");
440 printk("attached\n");
446 * _detach is called to deconfigure a device. It should deallocate
448 * This function is also called when _attach() fails, so it should be
449 * careful not to release resources that were not necessarily
450 * allocated by _attach(). dev->private and dev->subdevices are
451 * deallocated automatically by the core.
453 static int pcmuio_detach(struct comedi_device *dev)
457 printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
459 release_region(dev->iobase, ASIC_IOSIZE * thisboard->num_asics);
461 for (i = 0; i < MAX_ASICS; ++i) {
462 if (devpriv->asics[i].irq)
463 comedi_free_irq(devpriv->asics[i].irq, dev);
466 if (devpriv && devpriv->sprivs)
467 kfree(devpriv->sprivs);
472 /* DIO devices are slightly special. Although it is possible to
473 * implement the insn_read/insn_write interface, it is much more
474 * useful to applications if you implement the insn_bits interface.
475 * This allows packed reading/writing of the DIO channels. The
476 * comedi core can convert between insn_bits and insn_read/write */
477 static int pcmuio_dio_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
478 struct comedi_insn *insn, unsigned int *data)
485 reading a 0 means this channel was high
486 writine a 0 sets the channel high
487 reading a 1 means this channel was low
488 writing a 1 means set this channel low
490 Therefore everything is always inverted. */
492 /* The insn data is a mask in data[0] and the new data
493 * in data[1], each channel cooresponding to a bit. */
495 #ifdef DAMMIT_ITS_BROKEN
497 printk("write mask: %08x data: %08x\n", data[0], data[1]);
502 for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
503 /* address of 8-bit port */
504 unsigned long ioaddr = subpriv->iobases[byte_no],
505 /* bit offset of port in 32-bit doubleword */
506 offset = byte_no * 8;
507 /* this 8-bit port's data */
508 unsigned char byte = 0,
509 /* The write mask for this port (if any) */
510 write_mask_byte = (data[0] >> offset) & 0xff,
511 /* The data byte for this port */
512 data_byte = (data[1] >> offset) & 0xff;
514 byte = inb(ioaddr); /* read all 8-bits for this port */
516 #ifdef DAMMIT_ITS_BROKEN
518 printk("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ", byte_no, (unsigned)write_mask_byte, (unsigned)data_byte, offset, ioaddr, (unsigned)byte);
521 if (write_mask_byte) {
522 /* this byte has some write_bits -- so set the output lines */
523 byte &= ~write_mask_byte; /* clear bits for write mask */
524 byte |= ~data_byte & write_mask_byte; /* set to inverted data_byte */
525 /* Write out the new digital output state */
528 #ifdef DAMMIT_ITS_BROKEN
530 printk("data_out_byte %02x\n", (unsigned)byte);
532 /* save the digital input lines for this byte.. */
533 s->state |= ((unsigned int)byte) << offset;
536 /* now return the DIO lines to data[1] - note they came inverted! */
539 #ifdef DAMMIT_ITS_BROKEN
541 printk("s->state %08x data_out %08x\n", s->state, data[1]);
547 /* The input or output configuration of each digital line is
548 * configured by a special insn_config instruction. chanspec
549 * contains the channel to be changed, and data[0] contains the
550 * value COMEDI_INPUT or COMEDI_OUTPUT. */
551 static int pcmuio_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s,
552 struct comedi_insn *insn, unsigned int *data)
554 int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
556 unsigned long ioaddr;
559 /* Compute ioaddr for this channel */
560 ioaddr = subpriv->iobases[byte_no];
563 writing a 0 an IO channel's bit sets the channel to INPUT
564 and pulls the line high as well
566 writing a 1 to an IO channel's bit pulls the line low
568 All channels are implicitly always in OUTPUT mode -- but when
569 they are high they can be considered to be in INPUT mode..
571 Thus, we only force channels low if the config request was INPUT,
572 otherwise we do nothing to the hardware. */
575 case INSN_CONFIG_DIO_OUTPUT:
576 /* save to io_bits -- don't actually do anything since
577 all input channels are also output channels... */
578 s->io_bits |= 1 << chan;
580 case INSN_CONFIG_DIO_INPUT:
581 /* write a 0 to the actual register representing the channel
582 to set it to 'input'. 0 means "float high". */
584 byte &= ~(1 << bit_no);
585 /**< set input channel to '0' */
587 /* write out byte -- this is the only time we actually affect the
588 hardware as all channels are implicitly output -- but input
589 channels are set to float-high */
592 /* save to io_bits */
593 s->io_bits &= ~(1 << chan);
596 case INSN_CONFIG_DIO_QUERY:
597 /* retreive from shadow register */
600 io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
612 static void init_asics(struct comedi_device *dev)
614 ASIC chip to defaults */
617 for (asic = 0; asic < thisboard->num_asics; ++asic) {
619 unsigned long baseaddr = dev->iobase + asic * ASIC_IOSIZE;
621 switch_page(dev, asic, 0); /* switch back to page 0 */
623 /* first, clear all the DIO port bits */
624 for (port = 0; port < PORTS_PER_ASIC; ++port)
625 outb(0, baseaddr + REG_PORT0 + port);
627 /* Next, clear all the paged registers for each page */
628 for (page = 1; page < NUM_PAGES; ++page) {
630 /* now clear all the paged registers */
631 switch_page(dev, asic, page);
632 for (reg = FIRST_PAGED_REG;
633 reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
634 outb(0, baseaddr + reg);
637 /* DEBUG set rising edge interrupts on port0 of both asics */
638 /*switch_page(dev, asic, PAGE_POL);
639 outb(0xff, baseaddr + REG_POL0);
640 switch_page(dev, asic, PAGE_ENAB);
641 outb(0xff, baseaddr + REG_ENAB0); */
644 switch_page(dev, asic, 0); /* switch back to default page 0 */
649 static void switch_page(struct comedi_device *dev, int asic, int page)
651 if (asic < 0 || asic >= thisboard->num_asics)
652 return; /* paranoia */
653 if (page < 0 || page >= NUM_PAGES)
654 return; /* more paranoia */
656 devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
657 devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
659 /* now write out the shadow register */
660 outb(devpriv->asics[asic].pagelock,
661 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
665 static void lock_port(struct comedi_device *dev, int asic, int port)
667 if (asic < 0 || asic >= thisboard->num_asics)
668 return; /* paranoia */
669 if (port < 0 || port >= PORTS_PER_ASIC)
670 return; /* more paranoia */
672 devpriv->asics[asic].pagelock |= 0x1 << port;
673 /* now write out the shadow register */
674 outb(devpriv->asics[asic].pagelock,
675 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
678 static void unlock_port(struct comedi_device *dev, int asic, int port)
680 if (asic < 0 || asic >= thisboard->num_asics)
681 return; /* paranoia */
682 if (port < 0 || port >= PORTS_PER_ASIC)
683 return; /* more paranoia */
684 devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
685 /* now write out the shadow register */
686 outb(devpriv->asics[asic].pagelock,
687 dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
691 static irqreturn_t interrupt_pcmuio(int irq, void *d)
694 struct comedi_device *dev = (struct comedi_device *) d;
696 for (asic = 0; asic < MAX_ASICS; ++asic) {
697 if (irq == devpriv->asics[asic].irq) {
699 unsigned triggered = 0;
700 unsigned long iobase = devpriv->asics[asic].iobase;
701 /* it is an interrupt for ASIC #asic */
702 unsigned char int_pend;
704 comedi_spin_lock_irqsave(&devpriv->asics[asic].spinlock,
707 int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
711 for (port = 0; port < INTR_PORTS_PER_ASIC;
713 if (int_pend & (0x1 << port)) {
715 io_lines_with_edges = 0;
716 switch_page(dev, asic,
718 io_lines_with_edges =
722 if (io_lines_with_edges)
723 /* clear pending interrupt */
729 io_lines_with_edges <<
737 comedi_spin_unlock_irqrestore(&devpriv->asics[asic].
741 struct comedi_subdevice *s;
742 /* TODO here: dispatch io lines to subdevs with commands.. */
743 printk("PCMUIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n", irq, asic, triggered);
744 for (s = dev->subdevices;
745 s < dev->subdevices + dev->n_subdevices;
747 if (subpriv->intr.asic == asic) { /* this is an interrupt subdev, and it matches this asic! */
751 comedi_spin_lock_irqsave
755 oldevents = s->async->events;
757 if (subpriv->intr.active) {
763 & ((0x1 << subpriv->intr.num_asic_chans) - 1)) << subpriv->intr.first_chan;
764 if (mytrig & subpriv->
778 ch = CR_CHAN(s->async->cmd.chanlist[n]);
779 if (mytrig & (1U << ch)) {
783 /* Write the scan to the buffer. */
784 if (comedi_buf_put(s->async, ((short *) & val)[0])
787 (s->async, ((short *) & val)[1])) {
788 s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
790 /* Overflow! Stop acquisition!! */
791 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
797 /* Check for end of acquisition. */
802 /* stop_src == TRIG_COUNT */
803 if (subpriv->intr.stop_count > 0) {
807 if (subpriv->intr.stop_count == 0) {
808 s->async->events |= COMEDI_CB_EOA;
809 /* TODO: STOP_ACQUISITION_CALL_HERE!! */
819 comedi_spin_unlock_irqrestore
825 comedi_event(dev, s);
836 return IRQ_NONE; /* interrupt from other source */
840 static void pcmuio_stop_intr(struct comedi_device *dev, struct comedi_subdevice *s)
842 int nports, firstport, asic, port;
844 if ((asic = subpriv->intr.asic) < 0)
845 return; /* not an interrupt subdev */
847 subpriv->intr.enabled_mask = 0;
848 subpriv->intr.active = 0;
849 s->async->inttrig = 0;
850 nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
851 firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
852 switch_page(dev, asic, PAGE_ENAB);
853 for (port = firstport; port < firstport + nports; ++port) {
854 /* disable all intrs for this subdev.. */
855 outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
859 static int pcmuio_start_intr(struct comedi_device *dev, struct comedi_subdevice *s)
861 if (!subpriv->intr.continuous && subpriv->intr.stop_count == 0) {
862 /* An empty acquisition! */
863 s->async->events |= COMEDI_CB_EOA;
864 subpriv->intr.active = 0;
867 unsigned bits = 0, pol_bits = 0, n;
868 int nports, firstport, asic, port;
869 struct comedi_cmd *cmd = &s->async->cmd;
871 if ((asic = subpriv->intr.asic) < 0)
872 return 1; /* not an interrupt
874 subpriv->intr.enabled_mask = 0;
875 subpriv->intr.active = 1;
876 nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
877 firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
879 for (n = 0; n < cmd->chanlist_len; n++) {
880 bits |= (1U << CR_CHAN(cmd->chanlist[n]));
881 pol_bits |= (CR_AREF(cmd->chanlist[n])
882 || CR_RANGE(cmd->chanlist[n]) ? 1U : 0U)
883 << CR_CHAN(cmd->chanlist[n]);
886 bits &= ((0x1 << subpriv->intr.num_asic_chans) -
887 1) << subpriv->intr.first_chan;
888 subpriv->intr.enabled_mask = bits;
890 switch_page(dev, asic, PAGE_ENAB);
891 for (port = firstport; port < firstport + nports; ++port) {
893 bits >> (subpriv->intr.first_chan + (port -
894 firstport) * 8) & 0xff, pol =
895 pol_bits >> (subpriv->intr.first_chan + (port -
896 firstport) * 8) & 0xff;
897 /* set enab intrs for this subdev.. */
899 devpriv->asics[asic].iobase + REG_ENAB0 + port);
900 switch_page(dev, asic, PAGE_POL);
902 devpriv->asics[asic].iobase + REG_ENAB0 + port);
908 static int pcmuio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
912 comedi_spin_lock_irqsave(&subpriv->intr.spinlock, flags);
913 if (subpriv->intr.active)
914 pcmuio_stop_intr(dev, s);
915 comedi_spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
921 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
924 pcmuio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
925 unsigned int trignum)
933 comedi_spin_lock_irqsave(&subpriv->intr.spinlock, flags);
934 s->async->inttrig = 0;
935 if (subpriv->intr.active) {
936 event = pcmuio_start_intr(dev, s);
938 comedi_spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
941 comedi_event(dev, s);
948 * 'do_cmd' function for an 'INTERRUPT' subdevice.
950 static int pcmuio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
952 struct comedi_cmd *cmd = &s->async->cmd;
956 comedi_spin_lock_irqsave(&subpriv->intr.spinlock, flags);
957 subpriv->intr.active = 1;
959 /* Set up end of acquisition. */
960 switch (cmd->stop_src) {
962 subpriv->intr.continuous = 0;
963 subpriv->intr.stop_count = cmd->stop_arg;
967 subpriv->intr.continuous = 1;
968 subpriv->intr.stop_count = 0;
972 /* Set up start of acquisition. */
973 switch (cmd->start_src) {
975 s->async->inttrig = pcmuio_inttrig_start_intr;
979 event = pcmuio_start_intr(dev, s);
982 comedi_spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
985 comedi_event(dev, s);
992 * 'do_cmdtest' function for an 'INTERRUPT' subdevice.
995 pcmuio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_cmd *cmd)
1000 /* step 1: make sure trigger sources are trivially valid */
1002 tmp = cmd->start_src;
1003 cmd->start_src &= (TRIG_NOW | TRIG_INT);
1004 if (!cmd->start_src || tmp != cmd->start_src)
1007 tmp = cmd->scan_begin_src;
1008 cmd->scan_begin_src &= TRIG_EXT;
1009 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
1012 tmp = cmd->convert_src;
1013 cmd->convert_src &= TRIG_NOW;
1014 if (!cmd->convert_src || tmp != cmd->convert_src)
1017 tmp = cmd->scan_end_src;
1018 cmd->scan_end_src &= TRIG_COUNT;
1019 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
1022 tmp = cmd->stop_src;
1023 cmd->stop_src &= (TRIG_COUNT | TRIG_NONE);
1024 if (!cmd->stop_src || tmp != cmd->stop_src)
1030 /* step 2: make sure trigger sources are unique and mutually compatible */
1032 /* these tests are true if more than one _src bit is set */
1033 if ((cmd->start_src & (cmd->start_src - 1)) != 0)
1035 if ((cmd->scan_begin_src & (cmd->scan_begin_src - 1)) != 0)
1037 if ((cmd->convert_src & (cmd->convert_src - 1)) != 0)
1039 if ((cmd->scan_end_src & (cmd->scan_end_src - 1)) != 0)
1041 if ((cmd->stop_src & (cmd->stop_src - 1)) != 0)
1047 /* step 3: make sure arguments are trivially compatible */
1049 /* cmd->start_src == TRIG_NOW || cmd->start_src == TRIG_INT */
1050 if (cmd->start_arg != 0) {
1055 /* cmd->scan_begin_src == TRIG_EXT */
1056 if (cmd->scan_begin_arg != 0) {
1057 cmd->scan_begin_arg = 0;
1061 /* cmd->convert_src == TRIG_NOW */
1062 if (cmd->convert_arg != 0) {
1063 cmd->convert_arg = 0;
1067 /* cmd->scan_end_src == TRIG_COUNT */
1068 if (cmd->scan_end_arg != cmd->chanlist_len) {
1069 cmd->scan_end_arg = cmd->chanlist_len;
1073 switch (cmd->stop_src) {
1075 /* any count allowed */
1078 if (cmd->stop_arg != 0) {
1090 /* step 4: fix up any arguments */
1092 /* if (err) return 4; */
1098 * A convenient macro that defines init_module() and cleanup_module(),
1101 COMEDI_INITCLEANUP(driver);