Commit | Line | Data |
---|---|---|
ac52af96 DS |
1 | /* |
2 | comedi/drivers/poc.c | |
3 | Mini-drivers for POC (Piece of Crap) boards | |
4 | Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net> | |
5 | Copyright (C) 2001 David A. Schleef <ds@schleef.org> | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 2 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program; if not, write to the Free Software | |
19 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
20 | */ | |
21 | /* | |
22 | Driver: poc | |
23 | Description: Generic driver for very simple devices | |
24 | Author: ds | |
25 | Devices: [Keithley Metrabyte] DAC-02 (dac02), [Advantech] PCL-733 (pcl733), | |
26 | PCL-734 (pcl734) | |
27 | Updated: Sat, 16 Mar 2002 17:34:48 -0800 | |
28 | Status: unknown | |
29 | ||
30 | This driver is indended to support very simple ISA-based devices, | |
31 | including: | |
32 | dac02 - Keithley DAC-02 analog output board | |
33 | pcl733 - Advantech PCL-733 | |
34 | pcl734 - Advantech PCL-734 | |
35 | ||
36 | Configuration options: | |
37 | [0] - I/O port base | |
38 | */ | |
39 | ||
40 | #include "../comedidev.h" | |
41 | ||
42 | #include <linux/ioport.h> | |
43 | ||
da91b269 BP |
44 | static int poc_attach(struct comedi_device *dev, struct comedi_devconfig *it); |
45 | static int poc_detach(struct comedi_device *dev); | |
46 | static int readback_insn(struct comedi_device *dev, struct comedi_subdevice *s, | |
0a85b6f0 | 47 | struct comedi_insn *insn, unsigned int *data); |
da91b269 BP |
48 | |
49 | static int dac02_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, | |
0a85b6f0 MT |
50 | struct comedi_insn *insn, unsigned int *data); |
51 | static int pcl733_insn_bits(struct comedi_device *dev, | |
52 | struct comedi_subdevice *s, | |
53 | struct comedi_insn *insn, unsigned int *data); | |
54 | static int pcl734_insn_bits(struct comedi_device *dev, | |
55 | struct comedi_subdevice *s, | |
56 | struct comedi_insn *insn, unsigned int *data); | |
ac52af96 DS |
57 | |
58 | struct boarddef_struct { | |
59 | const char *name; | |
60 | unsigned int iosize; | |
71b5f4f1 | 61 | int (*setup) (struct comedi_device *); |
ac52af96 DS |
62 | int type; |
63 | int n_chan; | |
64 | int n_bits; | |
0a85b6f0 MT |
65 | int (*winsn) (struct comedi_device *, struct comedi_subdevice *, |
66 | struct comedi_insn *, unsigned int *); | |
67 | int (*rinsn) (struct comedi_device *, struct comedi_subdevice *, | |
68 | struct comedi_insn *, unsigned int *); | |
69 | int (*insnbits) (struct comedi_device *, struct comedi_subdevice *, | |
70 | struct comedi_insn *, unsigned int *); | |
9ced1de6 | 71 | const struct comedi_lrange *range; |
ac52af96 DS |
72 | }; |
73 | static const struct boarddef_struct boards[] = { | |
74 | { | |
0a85b6f0 MT |
75 | .name = "dac02", |
76 | .iosize = 8, | |
77 | /* .setup = dac02_setup, */ | |
78 | .type = COMEDI_SUBD_AO, | |
79 | .n_chan = 2, | |
80 | .n_bits = 12, | |
81 | .winsn = dac02_ao_winsn, | |
82 | .rinsn = readback_insn, | |
83 | .range = &range_unknown, | |
84 | }, | |
ac52af96 | 85 | { |
0a85b6f0 MT |
86 | .name = "pcl733", |
87 | .iosize = 4, | |
88 | .type = COMEDI_SUBD_DI, | |
89 | .n_chan = 32, | |
90 | .n_bits = 1, | |
91 | .insnbits = pcl733_insn_bits, | |
92 | .range = &range_digital, | |
93 | }, | |
ac52af96 | 94 | { |
0a85b6f0 MT |
95 | .name = "pcl734", |
96 | .iosize = 4, | |
97 | .type = COMEDI_SUBD_DO, | |
98 | .n_chan = 32, | |
99 | .n_bits = 1, | |
100 | .insnbits = pcl734_insn_bits, | |
101 | .range = &range_digital, | |
102 | }, | |
ac52af96 DS |
103 | }; |
104 | ||
b6ac1613 | 105 | #define n_boards ARRAY_SIZE(boards) |
ac52af96 DS |
106 | #define this_board ((const struct boarddef_struct *)dev->board_ptr) |
107 | ||
139dfbdf | 108 | static struct comedi_driver driver_poc = { |
68c3dbff BP |
109 | .driver_name = "poc", |
110 | .module = THIS_MODULE, | |
111 | .attach = poc_attach, | |
112 | .detach = poc_detach, | |
113 | .board_name = &boards[0].name, | |
114 | .num_names = n_boards, | |
115 | .offset = sizeof(boards[0]), | |
ac52af96 DS |
116 | }; |
117 | ||
da91b269 | 118 | static int poc_attach(struct comedi_device *dev, struct comedi_devconfig *it) |
ac52af96 | 119 | { |
34c43922 | 120 | struct comedi_subdevice *s; |
ac52af96 DS |
121 | unsigned long iobase; |
122 | unsigned int iosize; | |
123 | ||
124 | iobase = it->options[0]; | |
94941bc8 | 125 | printk(KERN_INFO "comedi%d: poc: using %s iobase 0x%lx\n", dev->minor, |
0a85b6f0 | 126 | this_board->name, iobase); |
ac52af96 DS |
127 | |
128 | dev->board_name = this_board->name; | |
129 | ||
130 | if (iobase == 0) { | |
a917d4c4 | 131 | printk(KERN_ERR "io base address required\n"); |
ac52af96 DS |
132 | return -EINVAL; |
133 | } | |
134 | ||
135 | iosize = this_board->iosize; | |
136 | /* check if io addresses are available */ | |
137 | if (!request_region(iobase, iosize, "dac02")) { | |
a917d4c4 CC |
138 | printk(KERN_ERR "I/O port conflict: failed to allocate ports " |
139 | "0x%lx to 0x%lx\n", iobase, iobase + iosize - 1); | |
ac52af96 DS |
140 | return -EIO; |
141 | } | |
142 | dev->iobase = iobase; | |
143 | ||
144 | if (alloc_subdevices(dev, 1) < 0) | |
145 | return -ENOMEM; | |
790c5541 | 146 | if (alloc_private(dev, sizeof(unsigned int) * this_board->n_chan) < 0) |
ac52af96 DS |
147 | return -ENOMEM; |
148 | ||
149 | /* analog output subdevice */ | |
150 | s = dev->subdevices + 0; | |
151 | s->type = this_board->type; | |
152 | s->n_chan = this_board->n_chan; | |
153 | s->maxdata = (1 << this_board->n_bits) - 1; | |
154 | s->range_table = this_board->range; | |
155 | s->insn_write = this_board->winsn; | |
156 | s->insn_read = this_board->rinsn; | |
157 | s->insn_bits = this_board->insnbits; | |
94941bc8 | 158 | if (s->type == COMEDI_SUBD_AO || s->type == COMEDI_SUBD_DO) |
ac52af96 | 159 | s->subdev_flags = SDF_WRITABLE; |
ac52af96 DS |
160 | |
161 | return 0; | |
162 | } | |
163 | ||
da91b269 | 164 | static int poc_detach(struct comedi_device *dev) |
ac52af96 DS |
165 | { |
166 | /* only free stuff if it has been allocated by _attach */ | |
167 | if (dev->iobase) | |
168 | release_region(dev->iobase, this_board->iosize); | |
169 | ||
94941bc8 | 170 | printk(KERN_INFO "comedi%d: dac02: remove\n", dev->minor); |
ac52af96 DS |
171 | |
172 | return 0; | |
173 | } | |
174 | ||
da91b269 | 175 | static int readback_insn(struct comedi_device *dev, struct comedi_subdevice *s, |
0a85b6f0 | 176 | struct comedi_insn *insn, unsigned int *data) |
ac52af96 DS |
177 | { |
178 | int chan; | |
179 | ||
180 | chan = CR_CHAN(insn->chanspec); | |
0a85b6f0 | 181 | data[0] = ((unsigned int *)dev->private)[chan]; |
ac52af96 DS |
182 | |
183 | return 1; | |
184 | } | |
185 | ||
186 | /* DAC-02 registers */ | |
187 | #define DAC02_LSB(a) (2 * a) | |
188 | #define DAC02_MSB(a) (2 * a + 1) | |
189 | ||
da91b269 | 190 | static int dac02_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, |
0a85b6f0 | 191 | struct comedi_insn *insn, unsigned int *data) |
ac52af96 DS |
192 | { |
193 | int temp; | |
194 | int chan; | |
195 | int output; | |
196 | ||
197 | chan = CR_CHAN(insn->chanspec); | |
0a85b6f0 | 198 | ((unsigned int *)dev->private)[chan] = data[0]; |
ac52af96 DS |
199 | output = data[0]; |
200 | #ifdef wrong | |
2696fb57 | 201 | /* convert to complementary binary if range is bipolar */ |
ac52af96 DS |
202 | if ((CR_RANGE(insn->chanspec) & 0x2) == 0) |
203 | output = ~output; | |
204 | #endif | |
205 | temp = (output << 4) & 0xf0; | |
206 | outb(temp, dev->iobase + DAC02_LSB(chan)); | |
207 | temp = (output >> 4) & 0xff; | |
208 | outb(temp, dev->iobase + DAC02_MSB(chan)); | |
209 | ||
210 | return 1; | |
211 | } | |
212 | ||
0a85b6f0 MT |
213 | static int pcl733_insn_bits(struct comedi_device *dev, |
214 | struct comedi_subdevice *s, | |
215 | struct comedi_insn *insn, unsigned int *data) | |
ac52af96 DS |
216 | { |
217 | if (insn->n != 2) | |
218 | return -EINVAL; | |
219 | ||
220 | data[1] = inb(dev->iobase + 0); | |
221 | data[1] |= (inb(dev->iobase + 1) << 8); | |
222 | data[1] |= (inb(dev->iobase + 2) << 16); | |
223 | data[1] |= (inb(dev->iobase + 3) << 24); | |
224 | ||
225 | return 2; | |
226 | } | |
227 | ||
0a85b6f0 MT |
228 | static int pcl734_insn_bits(struct comedi_device *dev, |
229 | struct comedi_subdevice *s, | |
230 | struct comedi_insn *insn, unsigned int *data) | |
ac52af96 DS |
231 | { |
232 | if (insn->n != 2) | |
233 | return -EINVAL; | |
234 | if (data[0]) { | |
235 | s->state &= ~data[0]; | |
236 | s->state |= (data[0] & data[1]); | |
237 | if ((data[0] >> 0) & 0xff) | |
238 | outb((s->state >> 0) & 0xff, dev->iobase + 0); | |
239 | if ((data[0] >> 8) & 0xff) | |
240 | outb((s->state >> 8) & 0xff, dev->iobase + 1); | |
241 | if ((data[0] >> 16) & 0xff) | |
242 | outb((s->state >> 16) & 0xff, dev->iobase + 2); | |
243 | if ((data[0] >> 24) & 0xff) | |
244 | outb((s->state >> 24) & 0xff, dev->iobase + 3); | |
245 | } | |
246 | data[1] = s->state; | |
247 | ||
248 | return 2; | |
249 | } | |
250 | ||
7114a280 AT |
251 | static int __init driver_poc_init_module(void) |
252 | { | |
253 | return comedi_driver_register(&driver_poc); | |
254 | } | |
255 | ||
256 | static void __exit driver_poc_cleanup_module(void) | |
257 | { | |
258 | comedi_driver_unregister(&driver_poc); | |
259 | } | |
260 | ||
261 | module_init(driver_poc_init_module); | |
262 | module_exit(driver_poc_cleanup_module); | |
90f703d3 AT |
263 | |
264 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
265 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
266 | MODULE_LICENSE("GPL"); |