pcmmio.c revision 4b2ba24399cfcd7c80a20cd3bbedc5df0ebd4345
1/*
2    comedi/drivers/pcmmio.c
3    Driver for Winsystems PC-104 based multifunction IO board.
4
5    COMEDI - Linux Control and Measurement Device Interface
6    Copyright (C) 2007 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: pcmmio
24Description: A driver for the PCM-MIO multifunction board
25Devices: [Winsystems] PCM-MIO (pcmmio)
26Author: Calin Culianu <calin@ajvar.org>
27Updated: Wed, May 16 2007 16:21:10 -0500
28Status: works
29
30A driver for the relatively new PCM-MIO multifunction board from
31Winsystems.  This board is a PC-104 based I/O board.  It contains
32four subdevices:
33  subdevice 0 - 16 channels of 16-bit AI
34  subdevice 1 - 8 channels of 16-bit AO
35  subdevice 2 - first 24 channels of the 48 channel of DIO
36	(with edge-triggered interrupt support)
37  subdevice 3 - last 24 channels of the 48 channel DIO
38	(no interrupt support for this bank of channels)
39
40  Some notes:
41
42  Synchronous reads and writes are the only things implemented for AI and AO,
43  even though the hardware itself can do streaming acquisition, etc.  Anyone
44  want to add asynchronous I/O for AI/AO as a feature?  Be my guest...
45
46  Asynchronous I/O for the DIO subdevices *is* implemented, however!  They are
47  basically edge-triggered interrupts for any configuration of the first
48  24 DIO-lines.
49
50  Also note that this interrupt support is untested.
51
52  A few words about edge-detection IRQ support (commands on DIO):
53
54  * To use edge-detection IRQ support for the DIO subdevice, pass the IRQ
55    of the board to the comedi_config command.  The board IRQ is not jumpered
56    but rather configured through software, so any IRQ from 1-15 is OK.
57
58  * Due to the genericity of the comedi API, you need to create a special
59    comedi_command in order to use edge-triggered interrupts for DIO.
60
61  * Use comedi_commands with TRIG_NOW.  Your callback will be called each
62    time an edge is detected on the specified DIO line(s), and the data
63    values will be two sample_t's, which should be concatenated to form
64    one 32-bit unsigned int.  This value is the mask of channels that had
65    edges detected from your channel list.  Note that the bits positions
66    in the mask correspond to positions in your chanlist when you
67    specified the command and *not* channel id's!
68
69 *  To set the polarity of the edge-detection interrupts pass a nonzero value
70    for either CR_RANGE or CR_AREF for edge-up polarity, or a zero
71    value for both CR_RANGE and CR_AREF if you want edge-down polarity.
72
73Configuration Options:
74  [0] - I/O port base address
75  [1] - IRQ (optional -- for edge-detect interrupt support only,
76	leave out if you don't need this feature)
77*/
78
79#include <linux/interrupt.h>
80#include "../comedidev.h"
81#include "pcm_common.h"
82#include <linux/pci.h>		/* for PCI devices */
83
84/* This stuff is all from pcmuio.c -- it refers to the DIO subdevices only */
85#define CHANS_PER_PORT   8
86#define PORTS_PER_ASIC   6
87#define INTR_PORTS_PER_ASIC   3
88#define MAX_CHANS_PER_SUBDEV 24	/* number of channels per comedi subdevice */
89#define PORTS_PER_SUBDEV (MAX_CHANS_PER_SUBDEV/CHANS_PER_PORT)
90#define CHANS_PER_ASIC (CHANS_PER_PORT*PORTS_PER_ASIC)
91#define INTR_CHANS_PER_ASIC 24
92#define INTR_PORTS_PER_SUBDEV (INTR_CHANS_PER_ASIC/CHANS_PER_PORT)
93#define MAX_DIO_CHANS   (PORTS_PER_ASIC*1*CHANS_PER_PORT)
94#define MAX_ASICS       (MAX_DIO_CHANS/CHANS_PER_ASIC)
95#define SDEV_NO ((int)(s - dev->subdevices))
96#define CALC_N_DIO_SUBDEVS(nchans) ((nchans)/MAX_CHANS_PER_SUBDEV + (!!((nchans)%MAX_CHANS_PER_SUBDEV)) /*+ (nchans > INTR_CHANS_PER_ASIC ? 2 : 1)*/)
97/* IO Memory sizes */
98#define ASIC_IOSIZE (0x0B)
99#define PCMMIO48_IOSIZE ASIC_IOSIZE
100
101/* Some offsets - these are all in the 16byte IO memory offset from
102   the base address.  Note that there is a paging scheme to swap out
103   offsets 0x8-0xA using the PAGELOCK register.  See the table below.
104
105  Register(s)       Pages        R/W?        Description
106  --------------------------------------------------------------
107  REG_PORTx         All          R/W         Read/Write/Configure IO
108  REG_INT_PENDING   All          ReadOnly    Quickly see which INT_IDx has int.
109  REG_PAGELOCK      All          WriteOnly   Select a page
110  REG_POLx          Pg. 1 only   WriteOnly   Select edge-detection polarity
111  REG_ENABx         Pg. 2 only   WriteOnly   Enable/Disable edge-detect. int.
112  REG_INT_IDx       Pg. 3 only   R/W         See which ports/bits have ints.
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				 * page selector register, upper 2 bits select
123				 * a page and bits 0-5 are used to 'lock down'
124				 * a particular port above to make it readonly.
125				 */
126#define REG_POL0 0x8
127#define REG_POL1 0x9
128#define REG_POL2 0xA
129#define REG_ENAB0 0x8
130#define REG_ENAB1 0x9
131#define REG_ENAB2 0xA
132#define REG_INT_ID0 0x8
133#define REG_INT_ID1 0x9
134#define REG_INT_ID2 0xA
135
136#define NUM_PAGED_REGS 3
137#define NUM_PAGES 4
138#define FIRST_PAGED_REG 0x8
139#define REG_PAGE_BITOFFSET 6
140#define REG_LOCK_BITOFFSET 0
141#define REG_PAGE_MASK (~((0x1<<REG_PAGE_BITOFFSET)-1))
142#define REG_LOCK_MASK (~(REG_PAGE_MASK))
143#define PAGE_POL 1
144#define PAGE_ENAB 2
145#define PAGE_INT_ID 3
146
147typedef int (*comedi_insn_fn_t) (struct comedi_device *,
148				 struct comedi_subdevice *,
149				 struct comedi_insn *, unsigned int *);
150
151static int ai_rinsn(struct comedi_device *, struct comedi_subdevice *,
152		    struct comedi_insn *, unsigned int *);
153static int ao_rinsn(struct comedi_device *, struct comedi_subdevice *,
154		    struct comedi_insn *, unsigned int *);
155static int ao_winsn(struct comedi_device *, struct comedi_subdevice *,
156		    struct comedi_insn *, unsigned int *);
157
158/*
159 * Board descriptions for two imaginary boards.  Describing the
160 * boards in this way is optional, and completely driver-dependent.
161 * Some drivers use arrays such as this, other do not.
162 */
163struct pcmmio_board {
164	const char *name;
165	const int dio_num_asics;
166	const int dio_num_ports;
167	const int total_iosize;
168	const int ai_bits;
169	const int ao_bits;
170	const int n_ai_chans;
171	const int n_ao_chans;
172	const struct comedi_lrange *ai_range_table, *ao_range_table;
173	comedi_insn_fn_t ai_rinsn, ao_rinsn, ao_winsn;
174};
175
176static const struct comedi_lrange ranges_ai = {
177	4, {RANGE(-5., 5.), RANGE(-10., 10.), RANGE(0., 5.), RANGE(0., 10.)}
178};
179
180static const struct comedi_lrange ranges_ao = {
181	6, {RANGE(0., 5.), RANGE(0., 10.), RANGE(-5., 5.), RANGE(-10., 10.),
182	  RANGE(-2.5, 2.5), RANGE(-2.5, 7.5)}
183};
184
185static const struct pcmmio_board pcmmio_boards[] = {
186	{
187	 .name = "pcmmio",
188	 .dio_num_asics = 1,
189	 .dio_num_ports = 6,
190	 .total_iosize = 32,
191	 .ai_bits = 16,
192	 .ao_bits = 16,
193	 .n_ai_chans = 16,
194	 .n_ao_chans = 8,
195	 .ai_range_table = &ranges_ai,
196	 .ao_range_table = &ranges_ao,
197	 .ai_rinsn = ai_rinsn,
198	 .ao_rinsn = ao_rinsn,
199	 .ao_winsn = ao_winsn},
200};
201
202/*
203 * Useful for shorthand access to the particular board structure
204 */
205#define thisboard ((const struct pcmmio_board *)dev->board_ptr)
206
207/* this structure is for data unique to this subdevice.  */
208struct pcmmio_subdev_private {
209
210	union {
211		/* for DIO: mapping of halfwords (bytes)
212		   in port/chanarray to iobase */
213		unsigned long iobases[PORTS_PER_SUBDEV];
214
215		/* for AI/AO */
216		unsigned long iobase;
217	};
218	union {
219		struct {
220
221			/* The below is only used for intr subdevices */
222			struct {
223				/*
224				 * if non-negative, this subdev has an
225				 * interrupt asic
226				 */
227				int asic;
228				/*
229				 * if nonnegative, the first channel id for
230				 * interrupts.
231				 */
232				int first_chan;
233				/*
234				 * the number of asic channels in this subdev
235				 * that have interrutps
236				 */
237				int num_asic_chans;
238				/*
239				 * if nonnegative, the first channel id with
240				 * respect to the asic that has interrupts
241				 */
242				int asic_chan;
243				/*
244				 * subdev-relative channel mask for channels
245				 * we are interested in
246				 */
247				int enabled_mask;
248				int active;
249				int stop_count;
250				int continuous;
251				spinlock_t spinlock;
252			} intr;
253		} dio;
254		struct {
255			/* the last unsigned int data written */
256			unsigned int shadow_samples[8];
257		} ao;
258	};
259};
260
261/*
262 * this structure is for data unique to this hardware driver.  If
263 * several hardware drivers keep similar information in this structure,
264 * feel free to suggest moving the variable to the struct comedi_device struct.
265 */
266struct pcmmio_private {
267	/* stuff for DIO */
268	struct {
269		unsigned char pagelock;	/* current page and lock */
270		/* shadow of POLx registers */
271		unsigned char pol[NUM_PAGED_REGS];
272		/* shadow of ENABx registers */
273		unsigned char enab[NUM_PAGED_REGS];
274		int num;
275		unsigned long iobase;
276		unsigned int irq;
277		spinlock_t spinlock;
278	} asics[MAX_ASICS];
279	struct pcmmio_subdev_private *sprivs;
280};
281
282/*
283 * most drivers define the following macro to make it easy to
284 * access the private structure.
285 */
286#define devpriv ((struct pcmmio_private *)dev->private)
287#define subpriv ((struct pcmmio_subdev_private *)s->private)
288/*
289 * The struct comedi_driver structure tells the Comedi core module
290 * which functions to call to configure/deconfigure (attach/detach)
291 * the board, and also about the kernel module that contains
292 * the device code.
293 */
294static int pcmmio_attach(struct comedi_device *dev,
295			 struct comedi_devconfig *it);
296static int pcmmio_detach(struct comedi_device *dev);
297
298static struct comedi_driver driver = {
299	.driver_name = "pcmmio",
300	.module = THIS_MODULE,
301	.attach = pcmmio_attach,
302	.detach = pcmmio_detach,
303/* It is not necessary to implement the following members if you are
304 * writing a driver for a ISA PnP or PCI card */
305	/* Most drivers will support multiple types of boards by
306	 * having an array of board structures.  These were defined
307	 * in pcmmio_boards[] above.  Note that the element 'name'
308	 * was first in the structure -- Comedi uses this fact to
309	 * extract the name of the board without knowing any details
310	 * about the structure except for its length.
311	 * When a device is attached (by comedi_config), the name
312	 * of the device is given to Comedi, and Comedi tries to
313	 * match it by going through the list of board names.  If
314	 * there is a match, the address of the pointer is put
315	 * into dev->board_ptr and driver->attach() is called.
316	 *
317	 * Note that these are not necessary if you can determine
318	 * the type of board in software.  ISA PnP, PCI, and PCMCIA
319	 * devices are such boards.
320	 */
321	.board_name = &pcmmio_boards[0].name,
322	.offset = sizeof(struct pcmmio_board),
323	.num_names = ARRAY_SIZE(pcmmio_boards),
324};
325
326static int pcmmio_dio_insn_bits(struct comedi_device *dev,
327				struct comedi_subdevice *s,
328				struct comedi_insn *insn, unsigned int *data);
329static int pcmmio_dio_insn_config(struct comedi_device *dev,
330				  struct comedi_subdevice *s,
331				  struct comedi_insn *insn, unsigned int *data);
332
333static irqreturn_t interrupt_pcmmio(int irq, void *d);
334static void pcmmio_stop_intr(struct comedi_device *, struct comedi_subdevice *);
335static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
336static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
337static int pcmmio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
338			  struct comedi_cmd *cmd);
339
340/* some helper functions to deal with specifics of this device's registers */
341/* sets up/clears ASIC chips to defaults */
342static void init_asics(struct comedi_device *dev);
343static void switch_page(struct comedi_device *dev, int asic, int page);
344#ifdef notused
345static void lock_port(struct comedi_device *dev, int asic, int port);
346static void unlock_port(struct comedi_device *dev, int asic, int port);
347#endif
348
349/*
350 * Attach is called by the Comedi core to configure the driver
351 * for a particular board.  If you specified a board_name array
352 * in the driver structure, dev->board_ptr contains that
353 * address.
354 */
355static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
356{
357	struct comedi_subdevice *s;
358	int sdev_no, chans_left, n_dio_subdevs, n_subdevs, port, asic,
359	    thisasic_chanct = 0;
360	unsigned long iobase;
361	unsigned int irq[MAX_ASICS];
362
363	iobase = it->options[0];
364	irq[0] = it->options[1];
365
366	printk("comedi%d: %s: io: %lx ", dev->minor, driver.driver_name,
367	       iobase);
368
369	dev->iobase = iobase;
370
371	if (!iobase || !request_region(iobase,
372				       thisboard->total_iosize,
373				       driver.driver_name)) {
374		printk("I/O port conflict\n");
375		return -EIO;
376	}
377
378/*
379 * Initialize dev->board_name.  Note that we can use the "thisboard"
380 * macro now, since we just initialized it in the last line.
381 */
382	dev->board_name = thisboard->name;
383
384/*
385 * Allocate the private structure area.  alloc_private() is a
386 * convenient macro defined in comedidev.h.
387 */
388	if (alloc_private(dev, sizeof(struct pcmmio_private)) < 0) {
389		printk("cannot allocate private data structure\n");
390		return -ENOMEM;
391	}
392
393	for (asic = 0; asic < MAX_ASICS; ++asic) {
394		devpriv->asics[asic].num = asic;
395		devpriv->asics[asic].iobase =
396		    dev->iobase + 16 + asic * ASIC_IOSIZE;
397		/*
398		 * this gets actually set at the end of this function when we
399		 * request_irqs
400		 */
401		devpriv->asics[asic].irq = 0;
402		spin_lock_init(&devpriv->asics[asic].spinlock);
403	}
404
405	chans_left = CHANS_PER_ASIC * thisboard->dio_num_asics;
406	n_dio_subdevs = CALC_N_DIO_SUBDEVS(chans_left);
407	n_subdevs = n_dio_subdevs + 2;
408	devpriv->sprivs =
409	    kcalloc(n_subdevs, sizeof(struct pcmmio_subdev_private),
410		    GFP_KERNEL);
411	if (!devpriv->sprivs) {
412		printk("cannot allocate subdevice private data structures\n");
413		return -ENOMEM;
414	}
415	/*
416	 * Allocate the subdevice structures.  alloc_subdevice() is a
417	 * convenient macro defined in comedidev.h.
418	 *
419	 * Allocate 1 AI + 1 AO + 2 DIO subdevs (24 lines per DIO)
420	 */
421	if (alloc_subdevices(dev, n_subdevs) < 0) {
422		printk("cannot allocate subdevice data structures\n");
423		return -ENOMEM;
424	}
425
426	/* First, AI */
427	sdev_no = 0;
428	s = dev->subdevices + sdev_no;
429	s->private = devpriv->sprivs + sdev_no;
430	s->maxdata = (1 << thisboard->ai_bits) - 1;
431	s->range_table = thisboard->ai_range_table;
432	s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
433	s->type = COMEDI_SUBD_AI;
434	s->n_chan = thisboard->n_ai_chans;
435	s->len_chanlist = s->n_chan;
436	s->insn_read = thisboard->ai_rinsn;
437	subpriv->iobase = dev->iobase + 0;
438	/* initialize the resource enable register by clearing it */
439	outb(0, subpriv->iobase + 3);
440	outb(0, subpriv->iobase + 4 + 3);
441
442	/* Next, AO */
443	++sdev_no;
444	s = dev->subdevices + sdev_no;
445	s->private = devpriv->sprivs + sdev_no;
446	s->maxdata = (1 << thisboard->ao_bits) - 1;
447	s->range_table = thisboard->ao_range_table;
448	s->subdev_flags = SDF_READABLE;
449	s->type = COMEDI_SUBD_AO;
450	s->n_chan = thisboard->n_ao_chans;
451	s->len_chanlist = s->n_chan;
452	s->insn_read = thisboard->ao_rinsn;
453	s->insn_write = thisboard->ao_winsn;
454	subpriv->iobase = dev->iobase + 8;
455	/* initialize the resource enable register by clearing it */
456	outb(0, subpriv->iobase + 3);
457	outb(0, subpriv->iobase + 4 + 3);
458
459	++sdev_no;
460	port = 0;
461	asic = 0;
462	for (; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
463		int byte_no;
464
465		s = dev->subdevices + sdev_no;
466		s->private = devpriv->sprivs + sdev_no;
467		s->maxdata = 1;
468		s->range_table = &range_digital;
469		s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
470		s->type = COMEDI_SUBD_DIO;
471		s->insn_bits = pcmmio_dio_insn_bits;
472		s->insn_config = pcmmio_dio_insn_config;
473		s->n_chan = min(chans_left, MAX_CHANS_PER_SUBDEV);
474		subpriv->dio.intr.asic = -1;
475		subpriv->dio.intr.first_chan = -1;
476		subpriv->dio.intr.asic_chan = -1;
477		subpriv->dio.intr.num_asic_chans = -1;
478		subpriv->dio.intr.active = 0;
479		s->len_chanlist = 1;
480
481		/* save the ioport address for each 'port' of 8 channels in the
482		   subdevice */
483		for (byte_no = 0; byte_no < PORTS_PER_SUBDEV; ++byte_no, ++port) {
484			if (port >= PORTS_PER_ASIC) {
485				port = 0;
486				++asic;
487				thisasic_chanct = 0;
488			}
489			subpriv->iobases[byte_no] =
490			    devpriv->asics[asic].iobase + port;
491
492			if (thisasic_chanct <
493			    CHANS_PER_PORT * INTR_PORTS_PER_ASIC
494			    && subpriv->dio.intr.asic < 0) {
495				/*
496				 * this is an interrupt subdevice,
497				 * so setup the struct
498				 */
499				subpriv->dio.intr.asic = asic;
500				subpriv->dio.intr.active = 0;
501				subpriv->dio.intr.stop_count = 0;
502				subpriv->dio.intr.first_chan = byte_no * 8;
503				subpriv->dio.intr.asic_chan = thisasic_chanct;
504				subpriv->dio.intr.num_asic_chans =
505				    s->n_chan - subpriv->dio.intr.first_chan;
506				s->cancel = pcmmio_cancel;
507				s->do_cmd = pcmmio_cmd;
508				s->do_cmdtest = pcmmio_cmdtest;
509				s->len_chanlist =
510				    subpriv->dio.intr.num_asic_chans;
511			}
512			thisasic_chanct += CHANS_PER_PORT;
513		}
514		spin_lock_init(&subpriv->dio.intr.spinlock);
515
516		chans_left -= s->n_chan;
517
518		if (!chans_left) {
519			/*
520			 * reset the asic to our first asic,
521			 * to do intr subdevs
522			 */
523			asic = 0;
524			port = 0;
525		}
526
527	}
528
529	init_asics(dev);	/* clear out all the registers, basically */
530
531	for (asic = 0; irq[0] && asic < MAX_ASICS; ++asic) {
532		if (irq[asic]
533		    && request_irq(irq[asic], interrupt_pcmmio,
534				   IRQF_SHARED, thisboard->name, dev)) {
535			int i;
536			/* unroll the allocated irqs.. */
537			for (i = asic - 1; i >= 0; --i) {
538				free_irq(irq[i], dev);
539				devpriv->asics[i].irq = irq[i] = 0;
540			}
541			irq[asic] = 0;
542		}
543		devpriv->asics[asic].irq = irq[asic];
544	}
545
546	dev->irq = irq[0];	/*
547				 * grr.. wish comedi dev struct supported
548				 * multiple irqs..
549				 */
550
551	if (irq[0]) {
552		printk("irq: %u ", irq[0]);
553		if (thisboard->dio_num_asics == 2 && irq[1])
554			printk("second ASIC irq: %u ", irq[1]);
555	} else {
556		printk("(IRQ mode disabled) ");
557	}
558
559	printk("attached\n");
560
561	return 1;
562}
563
564/*
565 * _detach is called to deconfigure a device.  It should deallocate
566 * resources.
567 * This function is also called when _attach() fails, so it should be
568 * careful not to release resources that were not necessarily
569 * allocated by _attach().  dev->private and dev->subdevices are
570 * deallocated automatically by the core.
571 */
572static int pcmmio_detach(struct comedi_device *dev)
573{
574	int i;
575
576	printk("comedi%d: %s: remove\n", dev->minor, driver.driver_name);
577	if (dev->iobase)
578		release_region(dev->iobase, thisboard->total_iosize);
579
580	for (i = 0; i < MAX_ASICS; ++i) {
581		if (devpriv && devpriv->asics[i].irq)
582			free_irq(devpriv->asics[i].irq, dev);
583	}
584
585	if (devpriv && devpriv->sprivs)
586		kfree(devpriv->sprivs);
587
588	return 0;
589}
590
591/* DIO devices are slightly special.  Although it is possible to
592 * implement the insn_read/insn_write interface, it is much more
593 * useful to applications if you implement the insn_bits interface.
594 * This allows packed reading/writing of the DIO channels.  The
595 * comedi core can convert between insn_bits and insn_read/write */
596static int pcmmio_dio_insn_bits(struct comedi_device *dev,
597				struct comedi_subdevice *s,
598				struct comedi_insn *insn, unsigned int *data)
599{
600	int byte_no;
601	if (insn->n != 2)
602		return -EINVAL;
603
604	/* NOTE:
605	   reading a 0 means this channel was high
606	   writine a 0 sets the channel high
607	   reading a 1 means this channel was low
608	   writing a 1 means set this channel low
609
610	   Therefore everything is always inverted. */
611
612	/* The insn data is a mask in data[0] and the new data
613	 * in data[1], each channel cooresponding to a bit. */
614
615#ifdef DAMMIT_ITS_BROKEN
616	/* DEBUG */
617	printk("write mask: %08x  data: %08x\n", data[0], data[1]);
618#endif
619
620	s->state = 0;
621
622	for (byte_no = 0; byte_no < s->n_chan / CHANS_PER_PORT; ++byte_no) {
623		/* address of 8-bit port */
624		unsigned long ioaddr = subpriv->iobases[byte_no],
625		    /* bit offset of port in 32-bit doubleword */
626		    offset = byte_no * 8;
627		/* this 8-bit port's data */
628		unsigned char byte = 0,
629		    /* The write mask for this port (if any) */
630		    write_mask_byte = (data[0] >> offset) & 0xff,
631		    /* The data byte for this port */
632		    data_byte = (data[1] >> offset) & 0xff;
633
634		byte = inb(ioaddr);	/* read all 8-bits for this port */
635
636#ifdef DAMMIT_ITS_BROKEN
637		/* DEBUG */
638		printk
639		    ("byte %d wmb %02x db %02x offset %02d io %04x, data_in %02x ",
640		     byte_no, (unsigned)write_mask_byte, (unsigned)data_byte,
641		     offset, ioaddr, (unsigned)byte);
642#endif
643
644		if (write_mask_byte) {
645			/*
646			 * this byte has some write_bits
647			 * -- so set the output lines
648			 */
649			/* clear bits for write mask */
650			byte &= ~write_mask_byte;
651			/* set to inverted data_byte */
652			byte |= ~data_byte & write_mask_byte;
653			/* Write out the new digital output state */
654			outb(byte, ioaddr);
655		}
656#ifdef DAMMIT_ITS_BROKEN
657		/* DEBUG */
658		printk("data_out_byte %02x\n", (unsigned)byte);
659#endif
660		/* save the digital input lines for this byte.. */
661		s->state |= ((unsigned int)byte) << offset;
662	}
663
664	/* now return the DIO lines to data[1] - note they came inverted! */
665	data[1] = ~s->state;
666
667#ifdef DAMMIT_ITS_BROKEN
668	/* DEBUG */
669	printk("s->state %08x data_out %08x\n", s->state, data[1]);
670#endif
671
672	return 2;
673}
674
675/* The input or output configuration of each digital line is
676 * configured by a special insn_config instruction.  chanspec
677 * contains the channel to be changed, and data[0] contains the
678 * value COMEDI_INPUT or COMEDI_OUTPUT. */
679static int pcmmio_dio_insn_config(struct comedi_device *dev,
680				  struct comedi_subdevice *s,
681				  struct comedi_insn *insn, unsigned int *data)
682{
683	int chan = CR_CHAN(insn->chanspec), byte_no = chan / 8, bit_no =
684	    chan % 8;
685	unsigned long ioaddr;
686	unsigned char byte;
687
688	/* Compute ioaddr for this channel */
689	ioaddr = subpriv->iobases[byte_no];
690
691	/* NOTE:
692	   writing a 0 an IO channel's bit sets the channel to INPUT
693	   and pulls the line high as well
694
695	   writing a 1 to an IO channel's  bit pulls the line low
696
697	   All channels are implicitly always in OUTPUT mode -- but when
698	   they are high they can be considered to be in INPUT mode..
699
700	   Thus, we only force channels low if the config request was INPUT,
701	   otherwise we do nothing to the hardware.    */
702
703	switch (data[0]) {
704	case INSN_CONFIG_DIO_OUTPUT:
705		/* save to io_bits -- don't actually do anything since
706		   all input channels are also output channels... */
707		s->io_bits |= 1 << chan;
708		break;
709	case INSN_CONFIG_DIO_INPUT:
710		/* write a 0 to the actual register representing the channel
711		   to set it to 'input'.  0 means "float high". */
712		byte = inb(ioaddr);
713		byte &= ~(1 << bit_no);
714				/**< set input channel to '0' */
715
716		/*
717		 * write out byte -- this is the only time we actually affect
718		 * the hardware as all channels are implicitly output
719		 * -- but input channels are set to float-high
720		 */
721		outb(byte, ioaddr);
722
723		/* save to io_bits */
724		s->io_bits &= ~(1 << chan);
725		break;
726
727	case INSN_CONFIG_DIO_QUERY:
728		/* retreive from shadow register */
729		data[1] =
730		    (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
731		return insn->n;
732		break;
733
734	default:
735		return -EINVAL;
736		break;
737	}
738
739	return insn->n;
740}
741
742static void init_asics(struct comedi_device *dev)
743{				/* sets up an
744				   ASIC chip to defaults */
745	int asic;
746
747	for (asic = 0; asic < thisboard->dio_num_asics; ++asic) {
748		int port, page;
749		unsigned long baseaddr = devpriv->asics[asic].iobase;
750
751		switch_page(dev, asic, 0);	/* switch back to page 0 */
752
753		/* first, clear all the DIO port bits */
754		for (port = 0; port < PORTS_PER_ASIC; ++port)
755			outb(0, baseaddr + REG_PORT0 + port);
756
757		/* Next, clear all the paged registers for each page */
758		for (page = 1; page < NUM_PAGES; ++page) {
759			int reg;
760			/* now clear all the paged registers */
761			switch_page(dev, asic, page);
762			for (reg = FIRST_PAGED_REG;
763			     reg < FIRST_PAGED_REG + NUM_PAGED_REGS; ++reg)
764				outb(0, baseaddr + reg);
765		}
766
767		/* DEBUG  set rising edge interrupts on port0 of both asics */
768		/*switch_page(dev, asic, PAGE_POL);
769		   outb(0xff, baseaddr + REG_POL0);
770		   switch_page(dev, asic, PAGE_ENAB);
771		   outb(0xff, baseaddr + REG_ENAB0); */
772		/* END DEBUG */
773
774		/* switch back to default page 0 */
775		switch_page(dev, asic, 0);
776	}
777}
778
779static void switch_page(struct comedi_device *dev, int asic, int page)
780{
781	if (asic < 0 || asic >= thisboard->dio_num_asics)
782		return;		/* paranoia */
783	if (page < 0 || page >= NUM_PAGES)
784		return;		/* more paranoia */
785
786	devpriv->asics[asic].pagelock &= ~REG_PAGE_MASK;
787	devpriv->asics[asic].pagelock |= page << REG_PAGE_BITOFFSET;
788
789	/* now write out the shadow register */
790	outb(devpriv->asics[asic].pagelock,
791	     devpriv->asics[asic].iobase + REG_PAGELOCK);
792}
793
794#ifdef notused
795static void lock_port(struct comedi_device *dev, int asic, int port)
796{
797	if (asic < 0 || asic >= thisboard->dio_num_asics)
798		return;		/* paranoia */
799	if (port < 0 || port >= PORTS_PER_ASIC)
800		return;		/* more paranoia */
801
802	devpriv->asics[asic].pagelock |= 0x1 << port;
803	/* now write out the shadow register */
804	outb(devpriv->asics[asic].pagelock,
805	     devpriv->asics[asic].iobase + REG_PAGELOCK);
806	return;
807}
808
809static void unlock_port(struct comedi_device *dev, int asic, int port)
810{
811	if (asic < 0 || asic >= thisboard->dio_num_asics)
812		return;		/* paranoia */
813	if (port < 0 || port >= PORTS_PER_ASIC)
814		return;		/* more paranoia */
815	devpriv->asics[asic].pagelock &= ~(0x1 << port) | REG_LOCK_MASK;
816	/* now write out the shadow register */
817	outb(devpriv->asics[asic].pagelock,
818	     devpriv->asics[asic].iobase + REG_PAGELOCK);
819}
820#endif /* notused */
821
822static irqreturn_t interrupt_pcmmio(int irq, void *d)
823{
824	int asic, got1 = 0;
825	struct comedi_device *dev = (struct comedi_device *)d;
826
827	for (asic = 0; asic < MAX_ASICS; ++asic) {
828		if (irq == devpriv->asics[asic].irq) {
829			unsigned long flags;
830			unsigned triggered = 0;
831			unsigned long iobase = devpriv->asics[asic].iobase;
832			/* it is an interrupt for ASIC #asic */
833			unsigned char int_pend;
834
835			spin_lock_irqsave(&devpriv->asics[asic].spinlock,
836					  flags);
837
838			int_pend = inb(iobase + REG_INT_PENDING) & 0x07;
839
840			if (int_pend) {
841				int port;
842				for (port = 0; port < INTR_PORTS_PER_ASIC;
843				     ++port) {
844					if (int_pend & (0x1 << port)) {
845						unsigned char
846						    io_lines_with_edges = 0;
847						switch_page(dev, asic,
848							    PAGE_INT_ID);
849						io_lines_with_edges =
850						    inb(iobase +
851							REG_INT_ID0 + port);
852
853						if (io_lines_with_edges)
854							/*
855							 * clear pending
856							 * interrupt
857							 */
858							outb(0, iobase +
859							     REG_INT_ID0 +
860							     port);
861
862						triggered |=
863						    io_lines_with_edges <<
864						    port * 8;
865					}
866				}
867
868				++got1;
869			}
870
871			spin_unlock_irqrestore(&devpriv->asics[asic].spinlock,
872					       flags);
873
874			if (triggered) {
875				struct comedi_subdevice *s;
876				/*
877				 * TODO here: dispatch io lines to subdevs
878				 * with commands..
879				 */
880				printk
881				    ("PCMMIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n",
882				     irq, asic, triggered);
883				for (s = dev->subdevices + 2;
884				     s < dev->subdevices + dev->n_subdevices;
885				     ++s) {
886					/*
887					 * this is an interrupt subdev,
888					 * and it matches this asic!
889					 */
890					if (subpriv->dio.intr.asic == asic) {
891						unsigned long flags;
892						unsigned oldevents;
893
894						spin_lock_irqsave(&subpriv->dio.
895								  intr.spinlock,
896								  flags);
897
898						oldevents = s->async->events;
899
900						if (subpriv->dio.intr.active) {
901							unsigned mytrig =
902							    ((triggered >>
903							      subpriv->dio.intr.asic_chan)
904							     &
905							     ((0x1 << subpriv->
906							       dio.intr.
907							       num_asic_chans) -
908							      1)) << subpriv->
909							    dio.intr.first_chan;
910							if (mytrig &
911							    subpriv->dio.
912							    intr.enabled_mask) {
913								unsigned int val
914								    = 0;
915								unsigned int n,
916								    ch, len;
917
918								len =
919								    s->
920								    async->cmd.chanlist_len;
921								for (n = 0;
922								     n < len;
923								     n++) {
924									ch = CR_CHAN(s->async->cmd.chanlist[n]);
925									if (mytrig & (1U << ch))
926										val |= (1U << n);
927								}
928								/* Write the scan to the buffer. */
929								if (comedi_buf_put(s->async, ((short *)&val)[0])
930								    &&
931								    comedi_buf_put
932								    (s->async,
933								     ((short *)
934								      &val)[1])) {
935									s->async->events |= (COMEDI_CB_BLOCK | COMEDI_CB_EOS);
936								} else {
937									/* Overflow! Stop acquisition!! */
938									/* TODO: STOP_ACQUISITION_CALL_HERE!! */
939									pcmmio_stop_intr
940									    (dev,
941									     s);
942								}
943
944								/* Check for end of acquisition. */
945								if (!subpriv->dio.intr.continuous) {
946									/* stop_src == TRIG_COUNT */
947									if (subpriv->dio.intr.stop_count > 0) {
948										subpriv->dio.intr.stop_count--;
949										if (subpriv->dio.intr.stop_count == 0) {
950											s->async->events |= COMEDI_CB_EOA;
951											/* TODO: STOP_ACQUISITION_CALL_HERE!! */
952											pcmmio_stop_intr
953											    (dev,
954											     s);
955										}
956									}
957								}
958							}
959						}
960
961						spin_unlock_irqrestore
962						    (&subpriv->dio.intr.
963						     spinlock, flags);
964
965						if (oldevents !=
966						    s->async->events) {
967							comedi_event(dev, s);
968						}
969
970					}
971
972				}
973			}
974
975		}
976	}
977	if (!got1)
978		return IRQ_NONE;	/* interrupt from other source */
979	return IRQ_HANDLED;
980}
981
982static void pcmmio_stop_intr(struct comedi_device *dev,
983			     struct comedi_subdevice *s)
984{
985	int nports, firstport, asic, port;
986
987	asic = subpriv->dio.intr.asic;
988	if (asic < 0)
989		return;		/* not an interrupt subdev */
990
991	subpriv->dio.intr.enabled_mask = 0;
992	subpriv->dio.intr.active = 0;
993	s->async->inttrig = 0;
994	nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
995	firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
996	switch_page(dev, asic, PAGE_ENAB);
997	for (port = firstport; port < firstport + nports; ++port) {
998		/* disable all intrs for this subdev.. */
999		outb(0, devpriv->asics[asic].iobase + REG_ENAB0 + port);
1000	}
1001}
1002
1003static int pcmmio_start_intr(struct comedi_device *dev,
1004			     struct comedi_subdevice *s)
1005{
1006	if (!subpriv->dio.intr.continuous && subpriv->dio.intr.stop_count == 0) {
1007		/* An empty acquisition! */
1008		s->async->events |= COMEDI_CB_EOA;
1009		subpriv->dio.intr.active = 0;
1010		return 1;
1011	} else {
1012		unsigned bits = 0, pol_bits = 0, n;
1013		int nports, firstport, asic, port;
1014		struct comedi_cmd *cmd = &s->async->cmd;
1015
1016		asic = subpriv->dio.intr.asic;
1017		if (asic < 0)
1018			return 1;	/* not an interrupt
1019					   subdev */
1020		subpriv->dio.intr.enabled_mask = 0;
1021		subpriv->dio.intr.active = 1;
1022		nports = subpriv->dio.intr.num_asic_chans / CHANS_PER_PORT;
1023		firstport = subpriv->dio.intr.asic_chan / CHANS_PER_PORT;
1024		if (cmd->chanlist) {
1025			for (n = 0; n < cmd->chanlist_len; n++) {
1026				bits |= (1U << CR_CHAN(cmd->chanlist[n]));
1027				pol_bits |= (CR_AREF(cmd->chanlist[n])
1028					     || CR_RANGE(cmd->
1029							 chanlist[n]) ? 1U : 0U)
1030				    << CR_CHAN(cmd->chanlist[n]);
1031			}
1032		}
1033		bits &= ((0x1 << subpriv->dio.intr.num_asic_chans) -
1034			 1) << subpriv->dio.intr.first_chan;
1035		subpriv->dio.intr.enabled_mask = bits;
1036
1037		{
1038			/*
1039			 * the below code configures the board
1040			 * to use a specific IRQ from 0-15.
1041			 */
1042			unsigned char b;
1043			/*
1044			 * set resource enable register
1045			 * to enable IRQ operation
1046			 */
1047			outb(1 << 4, dev->iobase + 3);
1048			/* set bits 0-3 of b to the irq number from 0-15 */
1049			b = dev->irq & ((1 << 4) - 1);
1050			outb(b, dev->iobase + 2);
1051			/* done, we told the board what irq to use */
1052		}
1053
1054		switch_page(dev, asic, PAGE_ENAB);
1055		for (port = firstport; port < firstport + nports; ++port) {
1056			unsigned enab =
1057			    bits >> (subpriv->dio.intr.first_chan + (port -
1058								     firstport)
1059				     * 8) & 0xff, pol =
1060			    pol_bits >> (subpriv->dio.intr.first_chan +
1061					 (port - firstport) * 8) & 0xff;
1062			/* set enab intrs for this subdev.. */
1063			outb(enab,
1064			     devpriv->asics[asic].iobase + REG_ENAB0 + port);
1065			switch_page(dev, asic, PAGE_POL);
1066			outb(pol,
1067			     devpriv->asics[asic].iobase + REG_ENAB0 + port);
1068		}
1069	}
1070	return 0;
1071}
1072
1073static int pcmmio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
1074{
1075	unsigned long flags;
1076
1077	spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
1078	if (subpriv->dio.intr.active)
1079		pcmmio_stop_intr(dev, s);
1080	spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
1081
1082	return 0;
1083}
1084
1085/*
1086 * Internal trigger function to start acquisition for an 'INTERRUPT' subdevice.
1087 */
1088static int
1089pcmmio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
1090			  unsigned int trignum)
1091{
1092	unsigned long flags;
1093	int event = 0;
1094
1095	if (trignum != 0)
1096		return -EINVAL;
1097
1098	spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
1099	s->async->inttrig = 0;
1100	if (subpriv->dio.intr.active)
1101		event = pcmmio_start_intr(dev, s);
1102	spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
1103
1104	if (event)
1105		comedi_event(dev, s);
1106
1107	return 1;
1108}
1109
1110/*
1111 * 'do_cmd' function for an 'INTERRUPT' subdevice.
1112 */
1113static int pcmmio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
1114{
1115	struct comedi_cmd *cmd = &s->async->cmd;
1116	unsigned long flags;
1117	int event = 0;
1118
1119	spin_lock_irqsave(&subpriv->dio.intr.spinlock, flags);
1120	subpriv->dio.intr.active = 1;
1121
1122	/* Set up end of acquisition. */
1123	switch (cmd->stop_src) {
1124	case TRIG_COUNT:
1125		subpriv->dio.intr.continuous = 0;
1126		subpriv->dio.intr.stop_count = cmd->stop_arg;
1127		break;
1128	default:
1129		/* TRIG_NONE */
1130		subpriv->dio.intr.continuous = 1;
1131		subpriv->dio.intr.stop_count = 0;
1132		break;
1133	}
1134
1135	/* Set up start of acquisition. */
1136	switch (cmd->start_src) {
1137	case TRIG_INT:
1138		s->async->inttrig = pcmmio_inttrig_start_intr;
1139		break;
1140	default:
1141		/* TRIG_NOW */
1142		event = pcmmio_start_intr(dev, s);
1143		break;
1144	}
1145	spin_unlock_irqrestore(&subpriv->dio.intr.spinlock, flags);
1146
1147	if (event)
1148		comedi_event(dev, s);
1149
1150	return 0;
1151}
1152
1153static int
1154pcmmio_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
1155	       struct comedi_cmd *cmd)
1156{
1157	return comedi_pcm_cmdtest(dev, s, cmd);
1158}
1159
1160static int adc_wait_ready(unsigned long iobase)
1161{
1162	unsigned long retry = 100000;
1163	while (retry--)
1164		if (inb(iobase + 3) & 0x80)
1165			return 0;
1166	return 1;
1167}
1168
1169/* All this is for AI and AO */
1170static int ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
1171		    struct comedi_insn *insn, unsigned int *data)
1172{
1173	int n;
1174	unsigned long iobase = subpriv->iobase;
1175
1176	/*
1177	   1. write the CMD byte (to BASE+2)
1178	   2. read junk lo byte (BASE+0)
1179	   3. read junk hi byte (BASE+1)
1180	   4. (mux settled so) write CMD byte again (BASE+2)
1181	   5. read valid lo byte(BASE+0)
1182	   6. read valid hi byte(BASE+1)
1183
1184	   Additionally note that the BASE += 4 if the channel >= 8
1185	 */
1186
1187	/* convert n samples */
1188	for (n = 0; n < insn->n; n++) {
1189		unsigned chan = CR_CHAN(insn->chanspec), range =
1190		    CR_RANGE(insn->chanspec), aref = CR_AREF(insn->chanspec);
1191		unsigned char command_byte = 0;
1192		unsigned iooffset = 0;
1193		short sample, adc_adjust = 0;
1194
1195		if (chan > 7)
1196			chan -= 8, iooffset = 4;	/*
1197							 * use the second dword
1198							 * for channels > 7
1199							 */
1200
1201		if (aref != AREF_DIFF) {
1202			aref = AREF_GROUND;
1203			command_byte |= 1 << 7;	/*
1204						 * set bit 7 to indicate
1205						 * single-ended
1206						 */
1207		}
1208		if (range < 2)
1209			adc_adjust = 0x8000;	/*
1210						 * bipolar ranges
1211						 * (-5,5 .. -10,10 need to be
1212						 * adjusted -- that is.. they
1213						 * need to wrap around by
1214						 * adding 0x8000
1215						 */
1216
1217		if (chan % 2) {
1218			command_byte |= 1 << 6;	/*
1219						 * odd-numbered channels
1220						 * have bit 6 set
1221						 */
1222		}
1223
1224		/* select the channel, bits 4-5 == chan/2 */
1225		command_byte |= ((chan / 2) & 0x3) << 4;
1226
1227		/* set the range, bits 2-3 */
1228		command_byte |= (range & 0x3) << 2;
1229
1230		/* need to do this twice to make sure mux settled */
1231		/* chan/range/aref select */
1232		outb(command_byte, iobase + iooffset + 2);
1233
1234		/* wait for the adc to say it finised the conversion */
1235		adc_wait_ready(iobase + iooffset);
1236
1237		/* select the chan/range/aref AGAIN */
1238		outb(command_byte, iobase + iooffset + 2);
1239
1240		adc_wait_ready(iobase + iooffset);
1241
1242		/* read data lo byte */
1243		sample = inb(iobase + iooffset + 0);
1244
1245		/* read data hi byte */
1246		sample |= inb(iobase + iooffset + 1) << 8;
1247		sample += adc_adjust;	/* adjustment .. munge data */
1248		data[n] = sample;
1249	}
1250	/* return the number of samples read/written */
1251	return n;
1252}
1253
1254static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
1255		    struct comedi_insn *insn, unsigned int *data)
1256{
1257	int n;
1258	for (n = 0; n < insn->n; n++) {
1259		unsigned chan = CR_CHAN(insn->chanspec);
1260		if (chan < s->n_chan)
1261			data[n] = subpriv->ao.shadow_samples[chan];
1262	}
1263	return n;
1264}
1265
1266static int wait_dac_ready(unsigned long iobase)
1267{
1268	unsigned long retry = 100000L;
1269
1270	/* This may seem like an absurd way to handle waiting and violates the
1271	   "no busy waiting" policy. The fact is that the hardware is
1272	   normally so fast that we usually only need one time through the loop
1273	   anyway. The longer timeout is for rare occasions and for detecting
1274	   non-existant hardware.  */
1275
1276	while (retry--) {
1277		if (inb(iobase + 3) & 0x80)
1278			return 0;
1279
1280	}
1281	return 1;
1282}
1283
1284static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
1285		    struct comedi_insn *insn, unsigned int *data)
1286{
1287	int n;
1288	unsigned iobase = subpriv->iobase, iooffset = 0;
1289
1290	for (n = 0; n < insn->n; n++) {
1291		unsigned chan = CR_CHAN(insn->chanspec), range =
1292		    CR_RANGE(insn->chanspec);
1293		if (chan < s->n_chan) {
1294			unsigned char command_byte = 0, range_byte =
1295			    range & ((1 << 4) - 1);
1296			if (chan >= 4)
1297				chan -= 4, iooffset += 4;
1298			/* set the range.. */
1299			outb(range_byte, iobase + iooffset + 0);
1300			outb(0, iobase + iooffset + 1);
1301
1302			/* tell it to begin */
1303			command_byte = (chan << 1) | 0x60;
1304			outb(command_byte, iobase + iooffset + 2);
1305
1306			wait_dac_ready(iobase + iooffset);
1307
1308			/* low order byte */
1309			outb(data[n] & 0xff, iobase + iooffset + 0);
1310
1311			/* high order byte */
1312			outb((data[n] >> 8) & 0xff, iobase + iooffset + 1);
1313
1314			/*
1315			 * set bit 4 of command byte to indicate
1316			 * data is loaded and trigger conversion
1317			 */
1318			command_byte = 0x70 | (chan << 1);
1319			/* trigger converion */
1320			outb(command_byte, iobase + iooffset + 2);
1321
1322			wait_dac_ready(iobase + iooffset);
1323
1324			/* save to shadow register for ao_rinsn */
1325			subpriv->ao.shadow_samples[chan] = data[n];
1326		}
1327	}
1328	return n;
1329}
1330
1331/*
1332 * A convenient macro that defines init_module() and cleanup_module(),
1333 * as necessary.
1334 */
1335COMEDI_INITCLEANUP(driver);
1336