dt2817.c revision 7114a28011f9d5f3d981731ad341177c21f9d948
1/*
2    comedi/drivers/dt2817.c
3    Hardware driver for Data Translation DT2817
4
5    COMEDI - Linux Control and Measurement Device Interface
6    Copyright (C) 1998 David A. Schleef <ds@schleef.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*/
23/*
24Driver: dt2817
25Description: Data Translation DT2817
26Author: ds
27Status: complete
28Devices: [Data Translation] DT2817 (dt2817)
29
30A very simple digital I/O card.  Four banks of 8 lines, each bank
31is configurable for input or output.  One wonders why it takes a
3250 page manual to describe this thing.
33
34The driver (which, btw, is much less than 50 pages) has 1 subdevice
35with 32 channels, configurable in groups of 8.
36
37Configuration options:
38  [0] - I/O port base base address
39*/
40
41#include "../comedidev.h"
42
43#include <linux/ioport.h>
44
45#define DT2817_SIZE 5
46
47#define DT2817_CR 0
48#define DT2817_DATA 1
49
50static int dt2817_attach(struct comedi_device *dev,
51			 struct comedi_devconfig *it);
52static int dt2817_detach(struct comedi_device *dev);
53static struct comedi_driver driver_dt2817 = {
54	.driver_name = "dt2817",
55	.module = THIS_MODULE,
56	.attach = dt2817_attach,
57	.detach = dt2817_detach,
58};
59
60static int __init driver_dt2817_init_module(void)
61{
62	return comedi_driver_register(&driver_dt2817);
63}
64
65static void __exit driver_dt2817_cleanup_module(void)
66{
67	comedi_driver_unregister(&driver_dt2817);
68}
69
70module_init(driver_dt2817_init_module);
71module_exit(driver_dt2817_cleanup_module);
72
73static int dt2817_dio_insn_config(struct comedi_device *dev,
74				  struct comedi_subdevice *s,
75				  struct comedi_insn *insn, unsigned int *data)
76{
77	int mask;
78	int chan;
79	int oe = 0;
80
81	if (insn->n != 1)
82		return -EINVAL;
83
84	chan = CR_CHAN(insn->chanspec);
85	if (chan < 8) {
86		mask = 0xff;
87	} else if (chan < 16) {
88		mask = 0xff00;
89	} else if (chan < 24) {
90		mask = 0xff0000;
91	} else
92		mask = 0xff000000;
93	if (data[0])
94		s->io_bits |= mask;
95	else
96		s->io_bits &= ~mask;
97
98	if (s->io_bits & 0x000000ff)
99		oe |= 0x1;
100	if (s->io_bits & 0x0000ff00)
101		oe |= 0x2;
102	if (s->io_bits & 0x00ff0000)
103		oe |= 0x4;
104	if (s->io_bits & 0xff000000)
105		oe |= 0x8;
106
107	outb(oe, dev->iobase + DT2817_CR);
108
109	return 1;
110}
111
112static int dt2817_dio_insn_bits(struct comedi_device *dev,
113				struct comedi_subdevice *s,
114				struct comedi_insn *insn, unsigned int *data)
115{
116	unsigned int changed;
117
118	/* It's questionable whether it is more important in
119	 * a driver like this to be deterministic or fast.
120	 * We choose fast. */
121
122	if (data[0]) {
123		changed = s->state;
124		s->state &= ~data[0];
125		s->state |= (data[0] & data[1]);
126		changed ^= s->state;
127		changed &= s->io_bits;
128		if (changed & 0x000000ff)
129			outb(s->state & 0xff, dev->iobase + DT2817_DATA + 0);
130		if (changed & 0x0000ff00)
131			outb((s->state >> 8) & 0xff,
132			     dev->iobase + DT2817_DATA + 1);
133		if (changed & 0x00ff0000)
134			outb((s->state >> 16) & 0xff,
135			     dev->iobase + DT2817_DATA + 2);
136		if (changed & 0xff000000)
137			outb((s->state >> 24) & 0xff,
138			     dev->iobase + DT2817_DATA + 3);
139	}
140	data[1] = inb(dev->iobase + DT2817_DATA + 0);
141	data[1] |= (inb(dev->iobase + DT2817_DATA + 1) << 8);
142	data[1] |= (inb(dev->iobase + DT2817_DATA + 2) << 16);
143	data[1] |= (inb(dev->iobase + DT2817_DATA + 3) << 24);
144
145	return 2;
146}
147
148static int dt2817_attach(struct comedi_device *dev, struct comedi_devconfig *it)
149{
150	int ret;
151	struct comedi_subdevice *s;
152	unsigned long iobase;
153
154	iobase = it->options[0];
155	printk("comedi%d: dt2817: 0x%04lx ", dev->minor, iobase);
156	if (!request_region(iobase, DT2817_SIZE, "dt2817")) {
157		printk("I/O port conflict\n");
158		return -EIO;
159	}
160	dev->iobase = iobase;
161	dev->board_name = "dt2817";
162
163	ret = alloc_subdevices(dev, 1);
164	if (ret < 0)
165		return ret;
166
167	s = dev->subdevices + 0;
168
169	s->n_chan = 32;
170	s->type = COMEDI_SUBD_DIO;
171	s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
172	s->range_table = &range_digital;
173	s->maxdata = 1;
174	s->insn_bits = dt2817_dio_insn_bits;
175	s->insn_config = dt2817_dio_insn_config;
176
177	s->state = 0;
178	outb(0, dev->iobase + DT2817_CR);
179
180	printk("\n");
181
182	return 0;
183}
184
185static int dt2817_detach(struct comedi_device *dev)
186{
187	printk("comedi%d: dt2817: remove\n", dev->minor);
188
189	if (dev->iobase)
190		release_region(dev->iobase, DT2817_SIZE);
191
192	return 0;
193}
194
195MODULE_AUTHOR("Comedi http://www.comedi.org");
196MODULE_DESCRIPTION("Comedi low-level driver");
197MODULE_LICENSE("GPL");
198