pcmuio.c revision 055f6636d9eb27bc13236e07739e019496c21221
1/*
2    comedi/drivers/pcmuio.c
3    Driver for Winsystems PC-104 based 48-channel and 96-channel DIO boards.
4
5    COMEDI - Linux Control and Measurement Device Interface
6    Copyright (C) 2006 Calin A. Culianu <calin@ajvar.org>
7
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.
12
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.
17
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.
21*/
22/*
23Driver: pcmuio
24Description: A driver for the PCM-UIO48A and PCM-UIO96A boards from Winsystems.
25Devices: [Winsystems] PCM-UIO48A (pcmuio48), PCM-UIO96A (pcmuio96)
26Author: Calin Culianu <calin@ajvar.org>
27Updated: Fri, 13 Jan 2006 12:01:01 -0500
28Status: works
29
30A driver for the relatively straightforward-to-program PCM-UIO48A and
31PCM-UIO96A boards from Winsystems.  These boards use either one or two
32(in the 96-DIO version) WS16C48 ASIC HighDensity I/O Chips (HDIO).
33This chip is interesting in that each I/O line is individually
34programmable for INPUT or OUTPUT (thus comedi_dio_config can be done
35on a per-channel basis).  Also, each chip supports edge-triggered
36interrupts for the first 24 I/O lines.  Of course, since the
3796-channel version of the board has two ASICs, it can detect polarity
38changes on up to 48 I/O lines.  Since this is essentially an (non-PnP)
39ISA board, I/O Address and IRQ selection are done through jumpers on
40the board.  You need to pass that information to this driver as the
41first and second comedi_config option, respectively.  Note that the
4248-channel version uses 16 bytes of IO memory and the 96-channel
43version uses 32-bytes (in case you are worried about conflicts).  The
4448-channel board is split into two 24-channel comedi subdevices.
45The 96-channel board is split into 4 24-channel DIO subdevices.
46
47Note that IRQ support has been added, but it is untested.
48
49To 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).
51Then, use use comedi_commands with TRIG_NOW.
52Your callback will be called each time an edge is triggered, and the data
53values will be two sample_t's, which should be concatenated to form one
5432-bit unsigned int.  This value is the mask of channels that had
55edges detected from your channel list.  Note that the bits positions
56in the mask correspond to positions in your chanlist when you specified
57the command and *not* channel id's!
58
59To set the polarity of the edge-detection interrupts pass a nonzero value for
60either CR_RANGE or CR_AREF for edge-up polarity, or a zero value for both
61CR_RANGE and CR_AREF if you want edge-down polarity.
62
63In the 48-channel version:
64
65On subdev 0, the first 24 channels channels are edge-detect channels.
66
67In the 96-channel board you have the collowing channels that can do edge detection:
68
69subdev 0, channels 0-24  (first 24 channels of 1st ASIC)
70subdev 2, channels 0-24  (first 24 channels of 2nd ASIC)
71
72Configuration 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!)
76*/
77
78#include "../comedidev.h"
79
80#include <linux/pci.h>		/* for PCI devices */
81
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)*/)
95/* IO Memory sizes */
96#define ASIC_IOSIZE (0x10)
97#define PCMUIO48_IOSIZE ASIC_IOSIZE
98#define PCMUIO96_IOSIZE (ASIC_IOSIZE*2)
99
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.
103
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.
112 */
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.  */
123#define REG_POL0 0x8
124#define REG_POL1 0x9
125#define REG_POL2 0xA
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
132
133#define NUM_PAGED_REGS 3
134#define NUM_PAGES 4
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)
140#define PAGE_POL 1
141#define PAGE_ENAB 2
142#define PAGE_INT_ID 3
143
144/*
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.
148 */
149struct pcmuio_board {
150	const char *name;
151	const int num_asics;
152	const int num_channels_per_port;
153	const int num_ports;
154};
155
156static const struct pcmuio_board pcmuio_boards[] = {
157	{
158	      name:	"pcmuio48",
159	      num_asics:1,
160	      num_ports:6,
161		},
162	{
163	      name:	"pcmuio96",
164	      num_asics:2,
165	      num_ports:12,
166		},
167};
168
169/*
170 * Useful for shorthand access to the particular board structure
171 */
172#define thisboard ((const struct pcmuio_board *)dev->board_ptr)
173
174/* this structure is for data unique to this subdevice.  */
175struct pcmuio_subdev_private {
176	/* mapping of halfwords (bytes) in port/chanarray to iobase */
177	unsigned long iobases[PORTS_PER_SUBDEV];
178
179	/* The below is only used for intr subdevices */
180	struct {
181		int asic;	/* if non-negative, this subdev has an interrupt asic */
182		int first_chan;	/* if nonnegative, the first channel id for
183				   interrupts. */
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 */
190		int active;
191		int stop_count;
192		int continuous;
193		spinlock_t spinlock;
194	} intr;
195};
196
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.  */
200struct pcmuio_private {
201	struct {
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 */
205		int num;
206		unsigned long iobase;
207		unsigned int irq;
208		spinlock_t spinlock;
209	} asics[MAX_ASICS];
210	struct pcmuio_subdev_private *sprivs;
211};
212
213/*
214 * most drivers define the following macro to make it easy to
215 * access the private structure.
216 */
217#define devpriv ((struct pcmuio_private *)dev->private)
218#define subpriv ((struct pcmuio_subdev_private *)s->private)
219/*
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
223 * the device code.
224 */
225static int pcmuio_attach(struct comedi_device * dev, struct comedi_devconfig * it);
226static int pcmuio_detach(struct comedi_device * dev);
227
228static struct comedi_driver driver = {
229      driver_name:"pcmuio",
230      module:THIS_MODULE,
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.
246	 *
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.
250	 */
251      board_name:&pcmuio_boards[0].name,
252      offset:sizeof(struct pcmuio_board),
253      num_names:sizeof(pcmuio_boards) / sizeof(struct pcmuio_board),
254};
255
256static int pcmuio_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
257	struct comedi_insn * insn, unsigned int * data);
258static int pcmuio_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
259	struct comedi_insn * insn, unsigned int * data);
260
261static irqreturn_t interrupt_pcmuio(int irq, void *d PT_REGS_ARG);
262static void pcmuio_stop_intr(struct comedi_device *, struct comedi_subdevice *);
263static int pcmuio_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
264static int pcmuio_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
265static int pcmuio_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
266	struct comedi_cmd * cmd);
267
268/* some helper functions to deal with specifics of this device's registers */
269static void init_asics(struct comedi_device * dev);	/* sets up/clears ASIC chips to defaults */
270static void switch_page(struct comedi_device * dev, int asic, int page);
271#ifdef notused
272static void lock_port(struct comedi_device * dev, int asic, int port);
273static void unlock_port(struct comedi_device * dev, int asic, int port);
274#endif
275
276/*
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
280 * address.
281 */
282static int pcmuio_attach(struct comedi_device * dev, struct comedi_devconfig * it)
283{
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];
288
289	iobase = it->options[0];
290	irq[0] = it->options[1];
291	irq[1] = it->options[2];
292
293	printk("comedi%d: %s: io: %lx ", dev->minor, driver.driver_name,
294		iobase);
295
296	dev->iobase = iobase;
297
298	if (!iobase || !request_region(iobase,
299			thisboard->num_asics * ASIC_IOSIZE,
300			driver.driver_name)) {
301		printk("I/O port conflict\n");
302		return -EIO;
303	}
304
305/*
306 * Initialize dev->board_name.  Note that we can use the "thisboard"
307 * macro now, since we just initialized it in the last line.
308 */
309	dev->board_name = thisboard->name;
310
311/*
312 * Allocate the private structure area.  alloc_private() is a
313 * convenient macro defined in comedidev.h.
314 */
315	if (alloc_private(dev, sizeof(struct pcmuio_private)) < 0) {
316		printk("cannot allocate private data structure\n");
317		return -ENOMEM;
318	}
319
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);
327	}
328
329	chans_left = CHANS_PER_ASIC * thisboard->num_asics;
330	n_subdevs = CALC_N_SUBDEVS(chans_left);
331	devpriv->sprivs =
332		kcalloc(n_subdevs, sizeof(struct pcmuio_subdev_private), GFP_KERNEL);
333	if (!devpriv->sprivs) {
334		printk("cannot allocate subdevice private data structures\n");
335		return -ENOMEM;
336	}
337	/*
338	 * Allocate the subdevice structures.  alloc_subdevice() is a
339	 * convenient macro defined in comedidev.h.
340	 *
341	 * Allocate 2 subdevs (32 + 16 DIO lines) or 3 32 DIO subdevs for the
342	 * 96-channel version of the board.
343	 */
344	if (alloc_subdevices(dev, n_subdevs) < 0) {
345		printk("cannot allocate subdevice data structures\n");
346		return -ENOMEM;
347	}
348
349	port = 0;
350	asic = 0;
351	for (sdev_no = 0; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
352		int byte_no;
353
354		s = dev->subdevices + sdev_no;
355		s->private = devpriv->sprivs + sdev_no;
356		s->maxdata = 1;
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;
368		s->len_chanlist = 1;
369
370		/* save the ioport address for each 'port' of 8 channels in the
371		   subdevice */
372		for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
373			if (port >= PORTS_PER_ASIC) {
374				port = 0;
375				++asic;
376				thisasic_chanct = 0;
377			}
378			subpriv->iobases[byte_no] =
379				devpriv->asics[asic].iobase + port;
380
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;
398			}
399			thisasic_chanct += CHANS_PER_PORT;
400		}
401		spin_lock_init(&subpriv->intr.spinlock);
402
403		chans_left -= s->n_chan;
404
405		if (!chans_left) {
406			asic = 0;	/* reset the asic to our first asic, to do intr subdevs */
407			port = 0;
408		}
409
410	}
411
412	init_asics(dev);	/* clear out all the registers, basically */
413
414	for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
415		if (irq[asic]
416			&& comedi_request_irq(irq[asic], interrupt_pcmuio,
417				IRQF_SHARED, thisboard->name, dev)) {
418			int i;
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;
423			}
424			irq[asic] = 0;
425		}
426		devpriv->asics[asic].irq = irq[asic];
427	}
428
429	dev->irq = irq[0];	/* grr.. wish comedi dev struct supported multiple
430				   irqs.. */
431
432	if (irq[0]) {
433		printk("irq: %u ", irq[0]);
434		if (irq[1] && thisboard->num_asics == 2)
435			printk("second ASIC irq: %u ", irq[1]);
436	} else {
437		printk("(IRQ mode disabled) ");
438	}
439
440	printk("attached\n");
441
442	return 1;
443}
444
445/*
446 * _detach is called to deconfigure a device.  It should deallocate
447 * resources.
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.
452 */
453static int pcmuio_detach(struct comedi_device * dev)
454{
455	int i;
456
457	printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
458	if (dev->iobase)
459		release_region(dev->iobase, ASIC_IOSIZE * thisboard->num_asics);
460
461	for (i = 0; i < MAX_ASICS; ++i) {
462		if (devpriv->asics[i].irq)
463			comedi_free_irq(devpriv->asics[i].irq, dev);
464	}
465
466	if (devpriv && devpriv->sprivs)
467		kfree(devpriv->sprivs);
468
469	return 0;
470}
471
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 */
477static int pcmuio_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
478	struct comedi_insn * insn, unsigned int * data)
479{
480	int byte_no;
481	if (insn->n != 2)
482		return -EINVAL;
483
484	/* NOTE:
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
489
490	   Therefore everything is always inverted. */
491
492	/* The insn data is a mask in data[0] and the new data
493	 * in data[1], each channel cooresponding to a bit. */
494
495#ifdef DAMMIT_ITS_BROKEN
496	/* DEBUG */
497	printk("write mask: %08x  data: %08x\n", data[0], data[1]);
498#endif
499
500	s->state = 0;
501
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;
513
514		byte = inb(ioaddr);	/* read all 8-bits for this port */
515
516#ifdef DAMMIT_ITS_BROKEN
517		/* DEBUG */
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);
519#endif
520
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 */
526			outb(byte, ioaddr);
527		}
528#ifdef DAMMIT_ITS_BROKEN
529		/* DEBUG */
530		printk("data_out_byte %02x\n", (unsigned)byte);
531#endif
532		/* save the digital input lines for this byte.. */
533		s->state |= ((unsigned int)byte) << offset;
534	}
535
536	/* now return the DIO lines to data[1] - note they came inverted! */
537	data[1] = ~s->state;
538
539#ifdef DAMMIT_ITS_BROKEN
540	/* DEBUG */
541	printk("s->state %08x data_out %08x\n", s->state, data[1]);
542#endif
543
544	return 2;
545}
546
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. */
551static int pcmuio_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
552	struct comedi_insn * insn, unsigned int * data)
553{
554	int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
555		chan % 8;
556	unsigned long ioaddr;
557	unsigned char byte;
558
559	/* Compute ioaddr for this channel */
560	ioaddr = subpriv->iobases[byte_no];
561
562	/* NOTE:
563	   writing a 0 an IO channel's bit sets the channel to INPUT
564	   and pulls the line high as well
565
566	   writing a 1 to an IO channel's  bit pulls the line low
567
568	   All channels are implicitly always in OUTPUT mode -- but when
569	   they are high they can be considered to be in INPUT mode..
570
571	   Thus, we only force channels low if the config request was INPUT,
572	   otherwise we do nothing to the hardware.    */
573
574	switch (data[0]) {
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;
579		break;
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". */
583		byte = inb(ioaddr);
584		byte &= ~(1 << bit_no);
585				/**< set input channel to '0' */
586
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 */
590		outb(byte, ioaddr);
591
592		/* save to io_bits */
593		s->io_bits &= ~(1 << chan);
594		break;
595
596	case INSN_CONFIG_DIO_QUERY:
597		/* retreive from shadow register */
598		data[1] =
599			(s->
600			io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
601		return insn->n;
602		break;
603
604	default:
605		return -EINVAL;
606		break;
607	}
608
609	return insn->n;
610}
611
612static void init_asics(struct comedi_device * dev)
613{				/* sets up an
614				   ASIC chip to defaults */
615	int asic;
616
617	for (asic = 0; asic < thisboard->num_asics; ++asic) {
618		int port, page;
619		unsigned long baseaddr = dev->iobase + asic * ASIC_IOSIZE;
620
621		switch_page(dev, asic, 0);	/* switch back to page 0 */
622
623		/* first, clear all the DIO port bits */
624		for (port = 0; port < PORTS_PER_ASIC; ++port)
625			outb(0, baseaddr + REG_PORT0 + port);
626
627		/* Next, clear all the paged registers for each page */
628		for (page = 1; page < NUM_PAGES; ++page) {
629			int reg;
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);
635		}
636
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); */
642		/* END DEBUG */
643
644		switch_page(dev, asic, 0);	/* switch back to default page 0 */
645
646	}
647}
648
649static void switch_page(struct comedi_device * dev, int asic, int page)
650{
651	if (asic < 0 || asic >= thisboard->num_asics)
652		return;		/* paranoia */
653	if (page < 0 || page >= NUM_PAGES)
654		return;		/* more paranoia */
655
656	devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
657	devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
658
659	/* now write out the shadow register */
660	outb(devpriv->asics[asic].pagelock,
661		dev->iobase + ASIC_IOSIZE * asic + REG_PAGELOCK);
662}
663
664#ifdef notused
665static void lock_port(struct comedi_device * dev, int asic, int port)
666{
667	if (asic < 0 || asic >= thisboard->num_asics)
668		return;		/* paranoia */
669	if (port < 0 || port >= PORTS_PER_ASIC)
670		return;		/* more paranoia */
671
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);
676}
677
678static void unlock_port(struct comedi_device * dev, int asic, int port)
679{
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);
688}
689#endif /* notused */
690
691static irqreturn_t interrupt_pcmuio(int irq, void *d PT_REGS_ARG)
692{
693	int asic, got1 = 0;
694	struct comedi_device *dev = (struct comedi_device *) d;
695
696	for (asic = 0; asic < MAX_ASICS; ++asic) {
697		if (irq == devpriv->asics[asic].irq) {
698			unsigned long flags;
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;
703
704			comedi_spin_lock_irqsave(&devpriv->asics[asic].spinlock,
705				flags);
706
707			int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
708
709			if (int_pend) {
710				int port;
711				for (port = 0; port < INTR_PORTS_PER_ASIC;
712					++port) {
713					if (int_pend & (0x1 << port)) {
714						unsigned char
715							io_lines_with_edges = 0;
716						switch_page(dev, asic,
717							PAGE_INT_ID);
718						io_lines_with_edges =
719							inb(iobase +
720							REG_INT_ID0 + port);
721
722						if (io_lines_with_edges)
723							/* clear pending interrupt */
724							outb(0, iobase +
725								REG_INT_ID0 +
726								port);
727
728						triggered |=
729							io_lines_with_edges <<
730							port * 8;
731					}
732				}
733
734				++got1;
735			}
736
737			comedi_spin_unlock_irqrestore(&devpriv->asics[asic].
738				spinlock, flags);
739
740			if (triggered) {
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;
746					++s) {
747					if (subpriv->intr.asic == asic) {	/* this is an interrupt subdev, and it matches this asic! */
748						unsigned long flags;
749						unsigned oldevents;
750
751						comedi_spin_lock_irqsave
752							(&subpriv->intr.
753							spinlock, flags);
754
755						oldevents = s->async->events;
756
757						if (subpriv->intr.active) {
758							unsigned mytrig =
759								((triggered >>
760									subpriv->
761									intr.
762									asic_chan)
763								& ((0x1 << subpriv->intr.num_asic_chans) - 1)) << subpriv->intr.first_chan;
764							if (mytrig & subpriv->
765								intr.
766								enabled_mask) {
767								unsigned int val =
768									0;
769								unsigned int n,
770									ch, len;
771
772								len = s->async->
773									cmd.
774									chanlist_len;
775								for (n = 0;
776									n < len;
777									n++) {
778									ch = CR_CHAN(s->async->cmd.chanlist[n]);
779									if (mytrig & (1U << ch)) {
780										val |= (1U << n);
781									}
782								}
783								/* Write the scan to the buffer. */
784								if (comedi_buf_put(s->async, ((short *) & val)[0])
785									&&
786									comedi_buf_put
787									(s->async, ((short *) & val)[1])) {
788									s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
789								} else {
790									/* Overflow! Stop acquisition!! */
791									/* TODO: STOP_ACQUISITION_CALL_HERE!! */
792									pcmuio_stop_intr
793										(dev,
794										s);
795								}
796
797								/* Check for end of acquisition. */
798								if (!subpriv->
799									intr.
800									continuous)
801								{
802									/* stop_src == TRIG_COUNT */
803									if (subpriv->intr.stop_count > 0) {
804										subpriv->
805											intr.
806											stop_count--;
807										if (subpriv->intr.stop_count == 0) {
808											s->async->events |= COMEDI_CB_EOA;
809											/* TODO: STOP_ACQUISITION_CALL_HERE!! */
810											pcmuio_stop_intr
811												(dev,
812												s);
813										}
814									}
815								}
816							}
817						}
818
819						comedi_spin_unlock_irqrestore
820							(&subpriv->intr.
821							spinlock, flags);
822
823						if (oldevents !=
824							s->async->events) {
825							comedi_event(dev, s);
826						}
827
828					}
829
830				}
831			}
832
833		}
834	}
835	if (!got1)
836		return IRQ_NONE;	/* interrupt from other source */
837	return IRQ_HANDLED;
838}
839
840static void pcmuio_stop_intr(struct comedi_device * dev, struct comedi_subdevice * s)
841{
842	int nports, firstport, asic, port;
843
844	if ((asic = subpriv->intr.asic) < 0)
845		return;		/* not an interrupt subdev */
846
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);
856	}
857}
858
859static int pcmuio_start_intr(struct comedi_device * dev, struct comedi_subdevice * s)
860{
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;
865		return 1;
866	} else {
867		unsigned bits = 0, pol_bits = 0, n;
868		int nports, firstport, asic, port;
869		struct comedi_cmd *cmd = &s->async->cmd;
870
871		if ((asic = subpriv->intr.asic) < 0)
872			return 1;	/* not an interrupt
873					   subdev */
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;
878		if (cmd->chanlist) {
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]);
884			}
885		}
886		bits &= ((0x1 << subpriv->intr.num_asic_chans) -
887			1) << subpriv->intr.first_chan;
888		subpriv->intr.enabled_mask = bits;
889
890		switch_page(dev, asic, PAGE_ENAB);
891		for (port = firstport; port < firstport + nports; ++port) {
892			unsigned enab =
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.. */
898			outb(enab,
899				devpriv->asics[asic].iobase + REG_ENAB0 + port);
900			switch_page(dev, asic, PAGE_POL);
901			outb(pol,
902				devpriv->asics[asic].iobase + REG_ENAB0 + port);
903		}
904	}
905	return 0;
906}
907
908static int pcmuio_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
909{
910	unsigned long flags;
911
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);
916
917	return 0;
918}
919
920/*
921 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
922 */
923static int
924pcmuio_inttrig_start_intr(struct comedi_device * dev, struct comedi_subdevice * s,
925	unsigned int trignum)
926{
927	unsigned long flags;
928	int event = 0;
929
930	if (trignum != 0)
931		return -EINVAL;
932
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);
937	}
938	comedi_spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
939
940	if (event) {
941		comedi_event(dev, s);
942	}
943
944	return 1;
945}
946
947/*
948 * 'do_cmd' function for an 'INTERRUPT' subdevice.
949 */
950static int pcmuio_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
951{
952	struct comedi_cmd *cmd = &s->async->cmd;
953	unsigned long flags;
954	int event = 0;
955
956	comedi_spin_lock_irqsave(&subpriv->intr.spinlock, flags);
957	subpriv->intr.active = 1;
958
959	/* Set up end of acquisition. */
960	switch (cmd->stop_src) {
961	case TRIG_COUNT:
962		subpriv->intr.continuous = 0;
963		subpriv->intr.stop_count = cmd->stop_arg;
964		break;
965	default:
966		/* TRIG_NONE */
967		subpriv->intr.continuous = 1;
968		subpriv->intr.stop_count = 0;
969		break;
970	}
971
972	/* Set up start of acquisition. */
973	switch (cmd->start_src) {
974	case TRIG_INT:
975		s->async->inttrig = pcmuio_inttrig_start_intr;
976		break;
977	default:
978		/* TRIG_NOW */
979		event = pcmuio_start_intr(dev, s);
980		break;
981	}
982	comedi_spin_unlock_irqrestore(&subpriv->intr.spinlock, flags);
983
984	if (event) {
985		comedi_event(dev, s);
986	}
987
988	return 0;
989}
990
991/*
992 * 'do_cmdtest' function for an 'INTERRUPT' subdevice.
993 */
994static int
995pcmuio_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s, struct comedi_cmd * cmd)
996{
997	int err = 0;
998	unsigned int tmp;
999
1000	/* step 1: make sure trigger sources are trivially valid */
1001
1002	tmp = cmd->start_src;
1003	cmd->start_src &= (TRIG_NOW | TRIG_INT);
1004	if (!cmd->start_src || tmp != cmd->start_src)
1005		err++;
1006
1007	tmp = cmd->scan_begin_src;
1008	cmd->scan_begin_src &= TRIG_EXT;
1009	if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
1010		err++;
1011
1012	tmp = cmd->convert_src;
1013	cmd->convert_src &= TRIG_NOW;
1014	if (!cmd->convert_src || tmp != cmd->convert_src)
1015		err++;
1016
1017	tmp = cmd->scan_end_src;
1018	cmd->scan_end_src &= TRIG_COUNT;
1019	if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
1020		err++;
1021
1022	tmp = cmd->stop_src;
1023	cmd->stop_src &= (TRIG_COUNT | TRIG_NONE);
1024	if (!cmd->stop_src || tmp != cmd->stop_src)
1025		err++;
1026
1027	if (err)
1028		return 1;
1029
1030	/* step 2: make sure trigger sources are unique and mutually compatible */
1031
1032	/* these tests are true if more than one _src bit is set */
1033	if ((cmd->start_src & (cmd->start_src - 1)) != 0)
1034		err++;
1035	if ((cmd->scan_begin_src & (cmd->scan_begin_src - 1)) != 0)
1036		err++;
1037	if ((cmd->convert_src & (cmd->convert_src - 1)) != 0)
1038		err++;
1039	if ((cmd->scan_end_src & (cmd->scan_end_src - 1)) != 0)
1040		err++;
1041	if ((cmd->stop_src & (cmd->stop_src - 1)) != 0)
1042		err++;
1043
1044	if (err)
1045		return 2;
1046
1047	/* step 3: make sure arguments are trivially compatible */
1048
1049	/* cmd->start_src == TRIG_NOW || cmd->start_src == TRIG_INT */
1050	if (cmd->start_arg != 0) {
1051		cmd->start_arg = 0;
1052		err++;
1053	}
1054
1055	/* cmd->scan_begin_src == TRIG_EXT */
1056	if (cmd->scan_begin_arg != 0) {
1057		cmd->scan_begin_arg = 0;
1058		err++;
1059	}
1060
1061	/* cmd->convert_src == TRIG_NOW */
1062	if (cmd->convert_arg != 0) {
1063		cmd->convert_arg = 0;
1064		err++;
1065	}
1066
1067	/* cmd->scan_end_src == TRIG_COUNT */
1068	if (cmd->scan_end_arg != cmd->chanlist_len) {
1069		cmd->scan_end_arg = cmd->chanlist_len;
1070		err++;
1071	}
1072
1073	switch (cmd->stop_src) {
1074	case TRIG_COUNT:
1075		/* any count allowed */
1076		break;
1077	case TRIG_NONE:
1078		if (cmd->stop_arg != 0) {
1079			cmd->stop_arg = 0;
1080			err++;
1081		}
1082		break;
1083	default:
1084		break;
1085	}
1086
1087	if (err)
1088		return 3;
1089
1090	/* step 4: fix up any arguments */
1091
1092	/* if (err) return 4; */
1093
1094	return 0;
1095}
1096
1097/*
1098 * A convenient macro that defines init_module() and cleanup_module(),
1099 * as necessary.
1100 */
1101COMEDI_INITCLEANUP(driver);
1102