162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * comedi/drivers/pcl730.c
462306a36Sopenharmony_ci * Driver for Advantech PCL-730 and clones
562306a36Sopenharmony_ci * José Luis Sánchez
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci * Driver: pcl730
1062306a36Sopenharmony_ci * Description: Advantech PCL-730 (& compatibles)
1162306a36Sopenharmony_ci * Devices: [Advantech] PCL-730 (pcl730), PCM-3730 (pcm3730), PCL-725 (pcl725),
1262306a36Sopenharmony_ci *   PCL-733 (pcl733), PCL-734 (pcl734),
1362306a36Sopenharmony_ci *   [ADLink] ACL-7130 (acl7130), ACL-7225b (acl7225b),
1462306a36Sopenharmony_ci *   [ICP] ISO-730 (iso730), P8R8-DIO (p8r8dio), P16R16-DIO (p16r16dio),
1562306a36Sopenharmony_ci *   [Diamond Systems] OPMM-1616-XT (opmm-1616-xt), PEARL-MM-P (pearl-mm-p),
1662306a36Sopenharmony_ci *   IR104-PBF (ir104-pbf),
1762306a36Sopenharmony_ci * Author: José Luis Sánchez (jsanchezv@teleline.es)
1862306a36Sopenharmony_ci * Status: untested
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * Configuration options:
2162306a36Sopenharmony_ci *   [0] - I/O port base
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * Interrupts are not supported.
2462306a36Sopenharmony_ci * The ACL-7130 card has an 8254 timer/counter not supported by this driver.
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include <linux/module.h>
2862306a36Sopenharmony_ci#include <linux/comedi/comedidev.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/*
3162306a36Sopenharmony_ci * Register map
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * The register map varies slightly depending on the board type but
3462306a36Sopenharmony_ci * all registers are 8-bit.
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci * The boardinfo 'io_range' is used to allow comedi to request the
3762306a36Sopenharmony_ci * proper range required by the board.
3862306a36Sopenharmony_ci *
3962306a36Sopenharmony_ci * The comedi_subdevice 'private' data is used to pass the register
4062306a36Sopenharmony_ci * offset to the (*insn_bits) functions to read/write the correct
4162306a36Sopenharmony_ci * registers.
4262306a36Sopenharmony_ci *
4362306a36Sopenharmony_ci * The basic register mapping looks like this:
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci *     BASE+0  Isolated outputs 0-7 (write) / inputs 0-7 (read)
4662306a36Sopenharmony_ci *     BASE+1  Isolated outputs 8-15 (write) / inputs 8-15 (read)
4762306a36Sopenharmony_ci *     BASE+2  TTL outputs 0-7 (write) / inputs 0-7 (read)
4862306a36Sopenharmony_ci *     BASE+3  TTL outputs 8-15 (write) / inputs 8-15 (read)
4962306a36Sopenharmony_ci *
5062306a36Sopenharmony_ci * The pcm3730 board does not have register BASE+1.
5162306a36Sopenharmony_ci *
5262306a36Sopenharmony_ci * The pcl725 and p8r8dio only have registers BASE+0 and BASE+1:
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci *     BASE+0  Isolated outputs 0-7 (write) (read back on p8r8dio)
5562306a36Sopenharmony_ci *     BASE+1  Isolated inputs 0-7 (read)
5662306a36Sopenharmony_ci *
5762306a36Sopenharmony_ci * The acl7225b and p16r16dio boards have this register mapping:
5862306a36Sopenharmony_ci *
5962306a36Sopenharmony_ci *     BASE+0  Isolated outputs 0-7 (write) (read back)
6062306a36Sopenharmony_ci *     BASE+1  Isolated outputs 8-15 (write) (read back)
6162306a36Sopenharmony_ci *     BASE+2  Isolated inputs 0-7 (read)
6262306a36Sopenharmony_ci *     BASE+3  Isolated inputs 8-15 (read)
6362306a36Sopenharmony_ci *
6462306a36Sopenharmony_ci * The pcl733 and pcl733 boards have this register mapping:
6562306a36Sopenharmony_ci *
6662306a36Sopenharmony_ci *     BASE+0  Isolated outputs 0-7 (write) or inputs 0-7 (read)
6762306a36Sopenharmony_ci *     BASE+1  Isolated outputs 8-15 (write) or inputs 8-15 (read)
6862306a36Sopenharmony_ci *     BASE+2  Isolated outputs 16-23 (write) or inputs 16-23 (read)
6962306a36Sopenharmony_ci *     BASE+3  Isolated outputs 24-31 (write) or inputs 24-31 (read)
7062306a36Sopenharmony_ci *
7162306a36Sopenharmony_ci * The opmm-1616-xt board has this register mapping:
7262306a36Sopenharmony_ci *
7362306a36Sopenharmony_ci *     BASE+0  Isolated outputs 0-7 (write) (read back)
7462306a36Sopenharmony_ci *     BASE+1  Isolated outputs 8-15 (write) (read back)
7562306a36Sopenharmony_ci *     BASE+2  Isolated inputs 0-7 (read)
7662306a36Sopenharmony_ci *     BASE+3  Isolated inputs 8-15 (read)
7762306a36Sopenharmony_ci *
7862306a36Sopenharmony_ci *     These registers are not currently supported:
7962306a36Sopenharmony_ci *
8062306a36Sopenharmony_ci *     BASE+2  Relay select register (write)
8162306a36Sopenharmony_ci *     BASE+3  Board reset control register (write)
8262306a36Sopenharmony_ci *     BASE+4  Interrupt control register (write)
8362306a36Sopenharmony_ci *     BASE+4  Change detect 7-0 status register (read)
8462306a36Sopenharmony_ci *     BASE+5  LED control register (write)
8562306a36Sopenharmony_ci *     BASE+5  Change detect 15-8 status register (read)
8662306a36Sopenharmony_ci *
8762306a36Sopenharmony_ci * The pearl-mm-p board has this register mapping:
8862306a36Sopenharmony_ci *
8962306a36Sopenharmony_ci *     BASE+0  Isolated outputs 0-7 (write)
9062306a36Sopenharmony_ci *     BASE+1  Isolated outputs 8-15 (write)
9162306a36Sopenharmony_ci *
9262306a36Sopenharmony_ci * The ir104-pbf board has this register mapping:
9362306a36Sopenharmony_ci *
9462306a36Sopenharmony_ci *     BASE+0  Isolated outputs 0-7 (write) (read back)
9562306a36Sopenharmony_ci *     BASE+1  Isolated outputs 8-15 (write) (read back)
9662306a36Sopenharmony_ci *     BASE+2  Isolated outputs 16-19 (write) (read back)
9762306a36Sopenharmony_ci *     BASE+4  Isolated inputs 0-7 (read)
9862306a36Sopenharmony_ci *     BASE+5  Isolated inputs 8-15 (read)
9962306a36Sopenharmony_ci *     BASE+6  Isolated inputs 16-19 (read)
10062306a36Sopenharmony_ci */
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistruct pcl730_board {
10362306a36Sopenharmony_ci	const char *name;
10462306a36Sopenharmony_ci	unsigned int io_range;
10562306a36Sopenharmony_ci	unsigned is_pcl725:1;
10662306a36Sopenharmony_ci	unsigned is_acl7225b:1;
10762306a36Sopenharmony_ci	unsigned is_ir104:1;
10862306a36Sopenharmony_ci	unsigned has_readback:1;
10962306a36Sopenharmony_ci	unsigned has_ttl_io:1;
11062306a36Sopenharmony_ci	int n_subdevs;
11162306a36Sopenharmony_ci	int n_iso_out_chan;
11262306a36Sopenharmony_ci	int n_iso_in_chan;
11362306a36Sopenharmony_ci	int n_ttl_chan;
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic const struct pcl730_board pcl730_boards[] = {
11762306a36Sopenharmony_ci	{
11862306a36Sopenharmony_ci		.name		= "pcl730",
11962306a36Sopenharmony_ci		.io_range	= 0x04,
12062306a36Sopenharmony_ci		.has_ttl_io	= 1,
12162306a36Sopenharmony_ci		.n_subdevs	= 4,
12262306a36Sopenharmony_ci		.n_iso_out_chan	= 16,
12362306a36Sopenharmony_ci		.n_iso_in_chan	= 16,
12462306a36Sopenharmony_ci		.n_ttl_chan	= 16,
12562306a36Sopenharmony_ci	}, {
12662306a36Sopenharmony_ci		.name		= "iso730",
12762306a36Sopenharmony_ci		.io_range	= 0x04,
12862306a36Sopenharmony_ci		.n_subdevs	= 4,
12962306a36Sopenharmony_ci		.n_iso_out_chan	= 16,
13062306a36Sopenharmony_ci		.n_iso_in_chan	= 16,
13162306a36Sopenharmony_ci		.n_ttl_chan	= 16,
13262306a36Sopenharmony_ci	}, {
13362306a36Sopenharmony_ci		.name		= "acl7130",
13462306a36Sopenharmony_ci		.io_range	= 0x08,
13562306a36Sopenharmony_ci		.has_ttl_io	= 1,
13662306a36Sopenharmony_ci		.n_subdevs	= 4,
13762306a36Sopenharmony_ci		.n_iso_out_chan	= 16,
13862306a36Sopenharmony_ci		.n_iso_in_chan	= 16,
13962306a36Sopenharmony_ci		.n_ttl_chan	= 16,
14062306a36Sopenharmony_ci	}, {
14162306a36Sopenharmony_ci		.name		= "pcm3730",
14262306a36Sopenharmony_ci		.io_range	= 0x04,
14362306a36Sopenharmony_ci		.has_ttl_io	= 1,
14462306a36Sopenharmony_ci		.n_subdevs	= 4,
14562306a36Sopenharmony_ci		.n_iso_out_chan	= 8,
14662306a36Sopenharmony_ci		.n_iso_in_chan	= 8,
14762306a36Sopenharmony_ci		.n_ttl_chan	= 16,
14862306a36Sopenharmony_ci	}, {
14962306a36Sopenharmony_ci		.name		= "pcl725",
15062306a36Sopenharmony_ci		.io_range	= 0x02,
15162306a36Sopenharmony_ci		.is_pcl725	= 1,
15262306a36Sopenharmony_ci		.n_subdevs	= 2,
15362306a36Sopenharmony_ci		.n_iso_out_chan	= 8,
15462306a36Sopenharmony_ci		.n_iso_in_chan	= 8,
15562306a36Sopenharmony_ci	}, {
15662306a36Sopenharmony_ci		.name		= "p8r8dio",
15762306a36Sopenharmony_ci		.io_range	= 0x02,
15862306a36Sopenharmony_ci		.is_pcl725	= 1,
15962306a36Sopenharmony_ci		.has_readback	= 1,
16062306a36Sopenharmony_ci		.n_subdevs	= 2,
16162306a36Sopenharmony_ci		.n_iso_out_chan	= 8,
16262306a36Sopenharmony_ci		.n_iso_in_chan	= 8,
16362306a36Sopenharmony_ci	}, {
16462306a36Sopenharmony_ci		.name		= "acl7225b",
16562306a36Sopenharmony_ci		.io_range	= 0x08,		/* only 4 are used */
16662306a36Sopenharmony_ci		.is_acl7225b	= 1,
16762306a36Sopenharmony_ci		.has_readback	= 1,
16862306a36Sopenharmony_ci		.n_subdevs	= 2,
16962306a36Sopenharmony_ci		.n_iso_out_chan	= 16,
17062306a36Sopenharmony_ci		.n_iso_in_chan	= 16,
17162306a36Sopenharmony_ci	}, {
17262306a36Sopenharmony_ci		.name		= "p16r16dio",
17362306a36Sopenharmony_ci		.io_range	= 0x04,
17462306a36Sopenharmony_ci		.is_acl7225b	= 1,
17562306a36Sopenharmony_ci		.has_readback	= 1,
17662306a36Sopenharmony_ci		.n_subdevs	= 2,
17762306a36Sopenharmony_ci		.n_iso_out_chan	= 16,
17862306a36Sopenharmony_ci		.n_iso_in_chan	= 16,
17962306a36Sopenharmony_ci	}, {
18062306a36Sopenharmony_ci		.name		= "pcl733",
18162306a36Sopenharmony_ci		.io_range	= 0x04,
18262306a36Sopenharmony_ci		.n_subdevs	= 1,
18362306a36Sopenharmony_ci		.n_iso_in_chan	= 32,
18462306a36Sopenharmony_ci	}, {
18562306a36Sopenharmony_ci		.name		= "pcl734",
18662306a36Sopenharmony_ci		.io_range	= 0x04,
18762306a36Sopenharmony_ci		.n_subdevs	= 1,
18862306a36Sopenharmony_ci		.n_iso_out_chan	= 32,
18962306a36Sopenharmony_ci	}, {
19062306a36Sopenharmony_ci		.name		= "opmm-1616-xt",
19162306a36Sopenharmony_ci		.io_range	= 0x10,
19262306a36Sopenharmony_ci		.is_acl7225b	= 1,
19362306a36Sopenharmony_ci		.has_readback	= 1,
19462306a36Sopenharmony_ci		.n_subdevs	= 2,
19562306a36Sopenharmony_ci		.n_iso_out_chan	= 16,
19662306a36Sopenharmony_ci		.n_iso_in_chan	= 16,
19762306a36Sopenharmony_ci	}, {
19862306a36Sopenharmony_ci		.name		= "pearl-mm-p",
19962306a36Sopenharmony_ci		.io_range	= 0x02,
20062306a36Sopenharmony_ci		.n_subdevs	= 1,
20162306a36Sopenharmony_ci		.n_iso_out_chan	= 16,
20262306a36Sopenharmony_ci	}, {
20362306a36Sopenharmony_ci		.name		= "ir104-pbf",
20462306a36Sopenharmony_ci		.io_range	= 0x08,
20562306a36Sopenharmony_ci		.is_ir104	= 1,
20662306a36Sopenharmony_ci		.has_readback	= 1,
20762306a36Sopenharmony_ci		.n_iso_out_chan	= 20,
20862306a36Sopenharmony_ci		.n_iso_in_chan	= 20,
20962306a36Sopenharmony_ci	},
21062306a36Sopenharmony_ci};
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic int pcl730_do_insn_bits(struct comedi_device *dev,
21362306a36Sopenharmony_ci			       struct comedi_subdevice *s,
21462306a36Sopenharmony_ci			       struct comedi_insn *insn,
21562306a36Sopenharmony_ci			       unsigned int *data)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	unsigned long reg = (unsigned long)s->private;
21862306a36Sopenharmony_ci	unsigned int mask;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	mask = comedi_dio_update_state(s, data);
22162306a36Sopenharmony_ci	if (mask) {
22262306a36Sopenharmony_ci		if (mask & 0x00ff)
22362306a36Sopenharmony_ci			outb(s->state & 0xff, dev->iobase + reg);
22462306a36Sopenharmony_ci		if ((mask & 0xff00) && (s->n_chan > 8))
22562306a36Sopenharmony_ci			outb((s->state >> 8) & 0xff, dev->iobase + reg + 1);
22662306a36Sopenharmony_ci		if ((mask & 0xff0000) && (s->n_chan > 16))
22762306a36Sopenharmony_ci			outb((s->state >> 16) & 0xff, dev->iobase + reg + 2);
22862306a36Sopenharmony_ci		if ((mask & 0xff000000) && (s->n_chan > 24))
22962306a36Sopenharmony_ci			outb((s->state >> 24) & 0xff, dev->iobase + reg + 3);
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	data[1] = s->state;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	return insn->n;
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic unsigned int pcl730_get_bits(struct comedi_device *dev,
23862306a36Sopenharmony_ci				    struct comedi_subdevice *s)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	unsigned long reg = (unsigned long)s->private;
24162306a36Sopenharmony_ci	unsigned int val;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	val = inb(dev->iobase + reg);
24462306a36Sopenharmony_ci	if (s->n_chan > 8)
24562306a36Sopenharmony_ci		val |= (inb(dev->iobase + reg + 1) << 8);
24662306a36Sopenharmony_ci	if (s->n_chan > 16)
24762306a36Sopenharmony_ci		val |= (inb(dev->iobase + reg + 2) << 16);
24862306a36Sopenharmony_ci	if (s->n_chan > 24)
24962306a36Sopenharmony_ci		val |= (inb(dev->iobase + reg + 3) << 24);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	return val;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic int pcl730_di_insn_bits(struct comedi_device *dev,
25562306a36Sopenharmony_ci			       struct comedi_subdevice *s,
25662306a36Sopenharmony_ci			       struct comedi_insn *insn,
25762306a36Sopenharmony_ci			       unsigned int *data)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	data[1] = pcl730_get_bits(dev, s);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	return insn->n;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic int pcl730_attach(struct comedi_device *dev,
26562306a36Sopenharmony_ci			 struct comedi_devconfig *it)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	const struct pcl730_board *board = dev->board_ptr;
26862306a36Sopenharmony_ci	struct comedi_subdevice *s;
26962306a36Sopenharmony_ci	int subdev;
27062306a36Sopenharmony_ci	int ret;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	ret = comedi_request_region(dev, it->options[0], board->io_range);
27362306a36Sopenharmony_ci	if (ret)
27462306a36Sopenharmony_ci		return ret;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	ret = comedi_alloc_subdevices(dev, board->n_subdevs);
27762306a36Sopenharmony_ci	if (ret)
27862306a36Sopenharmony_ci		return ret;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	subdev = 0;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (board->n_iso_out_chan) {
28362306a36Sopenharmony_ci		/* Isolated Digital Outputs */
28462306a36Sopenharmony_ci		s = &dev->subdevices[subdev++];
28562306a36Sopenharmony_ci		s->type		= COMEDI_SUBD_DO;
28662306a36Sopenharmony_ci		s->subdev_flags	= SDF_WRITABLE;
28762306a36Sopenharmony_ci		s->n_chan	= board->n_iso_out_chan;
28862306a36Sopenharmony_ci		s->maxdata	= 1;
28962306a36Sopenharmony_ci		s->range_table	= &range_digital;
29062306a36Sopenharmony_ci		s->insn_bits	= pcl730_do_insn_bits;
29162306a36Sopenharmony_ci		s->private	= (void *)0;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		/* get the initial state if supported */
29462306a36Sopenharmony_ci		if (board->has_readback)
29562306a36Sopenharmony_ci			s->state = pcl730_get_bits(dev, s);
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	if (board->n_iso_in_chan) {
29962306a36Sopenharmony_ci		/* Isolated Digital Inputs */
30062306a36Sopenharmony_ci		s = &dev->subdevices[subdev++];
30162306a36Sopenharmony_ci		s->type		= COMEDI_SUBD_DI;
30262306a36Sopenharmony_ci		s->subdev_flags	= SDF_READABLE;
30362306a36Sopenharmony_ci		s->n_chan	= board->n_iso_in_chan;
30462306a36Sopenharmony_ci		s->maxdata	= 1;
30562306a36Sopenharmony_ci		s->range_table	= &range_digital;
30662306a36Sopenharmony_ci		s->insn_bits	= pcl730_di_insn_bits;
30762306a36Sopenharmony_ci		s->private	= board->is_ir104 ? (void *)4 :
30862306a36Sopenharmony_ci				  board->is_acl7225b ? (void *)2 :
30962306a36Sopenharmony_ci				  board->is_pcl725 ? (void *)1 : (void *)0;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if (board->has_ttl_io) {
31362306a36Sopenharmony_ci		/* TTL Digital Outputs */
31462306a36Sopenharmony_ci		s = &dev->subdevices[subdev++];
31562306a36Sopenharmony_ci		s->type		= COMEDI_SUBD_DO;
31662306a36Sopenharmony_ci		s->subdev_flags	= SDF_WRITABLE;
31762306a36Sopenharmony_ci		s->n_chan	= board->n_ttl_chan;
31862306a36Sopenharmony_ci		s->maxdata	= 1;
31962306a36Sopenharmony_ci		s->range_table	= &range_digital;
32062306a36Sopenharmony_ci		s->insn_bits	= pcl730_do_insn_bits;
32162306a36Sopenharmony_ci		s->private	= (void *)2;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci		/* TTL Digital Inputs */
32462306a36Sopenharmony_ci		s = &dev->subdevices[subdev++];
32562306a36Sopenharmony_ci		s->type		= COMEDI_SUBD_DI;
32662306a36Sopenharmony_ci		s->subdev_flags	= SDF_READABLE;
32762306a36Sopenharmony_ci		s->n_chan	= board->n_ttl_chan;
32862306a36Sopenharmony_ci		s->maxdata	= 1;
32962306a36Sopenharmony_ci		s->range_table	= &range_digital;
33062306a36Sopenharmony_ci		s->insn_bits	= pcl730_di_insn_bits;
33162306a36Sopenharmony_ci		s->private	= (void *)2;
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	return 0;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic struct comedi_driver pcl730_driver = {
33862306a36Sopenharmony_ci	.driver_name	= "pcl730",
33962306a36Sopenharmony_ci	.module		= THIS_MODULE,
34062306a36Sopenharmony_ci	.attach		= pcl730_attach,
34162306a36Sopenharmony_ci	.detach		= comedi_legacy_detach,
34262306a36Sopenharmony_ci	.board_name	= &pcl730_boards[0].name,
34362306a36Sopenharmony_ci	.num_names	= ARRAY_SIZE(pcl730_boards),
34462306a36Sopenharmony_ci	.offset		= sizeof(struct pcl730_board),
34562306a36Sopenharmony_ci};
34662306a36Sopenharmony_cimodule_comedi_driver(pcl730_driver);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ciMODULE_AUTHOR("Comedi https://www.comedi.org");
34962306a36Sopenharmony_ciMODULE_DESCRIPTION("Comedi low-level driver");
35062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
351