1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37#include <linux/module.h>
38#include <linux/interrupt.h>
39#include "../comedidev.h"
40
41#include <linux/delay.h>
42
43#include "comedi_fc.h"
44
45#define DT2814_SIZE 2
46
47#define DT2814_CSR 0
48#define DT2814_DATA 1
49
50
51
52
53
54#define DT2814_FINISH 0x80
55#define DT2814_ERR 0x40
56#define DT2814_BUSY 0x20
57#define DT2814_ENB 0x10
58#define DT2814_CHANMASK 0x0f
59
60struct dt2814_private {
61
62 int ntrig;
63 int curadchan;
64};
65
66#define DT2814_TIMEOUT 10
67#define DT2814_MAX_SPEED 100000
68
69static int dt2814_ai_insn_read(struct comedi_device *dev,
70 struct comedi_subdevice *s,
71 struct comedi_insn *insn, unsigned int *data)
72{
73 int n, i, hi, lo;
74 int chan;
75 int status = 0;
76
77 for (n = 0; n < insn->n; n++) {
78 chan = CR_CHAN(insn->chanspec);
79
80 outb(chan, dev->iobase + DT2814_CSR);
81 for (i = 0; i < DT2814_TIMEOUT; i++) {
82 status = inb(dev->iobase + DT2814_CSR);
83 printk(KERN_INFO "dt2814: status: %02x\n", status);
84 udelay(10);
85 if (status & DT2814_FINISH)
86 break;
87 }
88 if (i >= DT2814_TIMEOUT) {
89 printk(KERN_INFO "dt2814: status: %02x\n", status);
90 return -ETIMEDOUT;
91 }
92
93 hi = inb(dev->iobase + DT2814_DATA);
94 lo = inb(dev->iobase + DT2814_DATA);
95
96 data[n] = (hi << 4) | (lo >> 4);
97 }
98
99 return n;
100}
101
102static int dt2814_ns_to_timer(unsigned int *ns, unsigned int flags)
103{
104 int i;
105 unsigned int f;
106
107
108
109 f = 10000;
110 for (i = 0; i < 8; i++) {
111 if ((2 * (*ns)) < (f * 11))
112 break;
113 f *= 10;
114 }
115
116 *ns = f;
117
118 return i;
119}
120
121static int dt2814_ai_cmdtest(struct comedi_device *dev,
122 struct comedi_subdevice *s, struct comedi_cmd *cmd)
123{
124 int err = 0;
125 int tmp;
126
127
128
129 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
130 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
131 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
132 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
133 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
134
135 if (err)
136 return 1;
137
138
139
140 err |= cfc_check_trigger_is_unique(cmd->stop_src);
141
142
143
144 if (err)
145 return 2;
146
147
148
149 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
150
151 err |= cfc_check_trigger_arg_max(&cmd->scan_begin_arg, 1000000000);
152 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg,
153 DT2814_MAX_SPEED);
154
155 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
156
157 if (cmd->stop_src == TRIG_COUNT)
158 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 2);
159 else
160 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
161
162 if (err)
163 return 3;
164
165
166
167 tmp = cmd->scan_begin_arg;
168 dt2814_ns_to_timer(&cmd->scan_begin_arg, cmd->flags & TRIG_ROUND_MASK);
169 if (tmp != cmd->scan_begin_arg)
170 err++;
171
172 if (err)
173 return 4;
174
175 return 0;
176}
177
178static int dt2814_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
179{
180 struct dt2814_private *devpriv = dev->private;
181 struct comedi_cmd *cmd = &s->async->cmd;
182 int chan;
183 int trigvar;
184
185 trigvar =
186 dt2814_ns_to_timer(&cmd->scan_begin_arg,
187 cmd->flags & TRIG_ROUND_MASK);
188
189 chan = CR_CHAN(cmd->chanlist[0]);
190
191 devpriv->ntrig = cmd->stop_arg;
192 outb(chan | DT2814_ENB | (trigvar << 5), dev->iobase + DT2814_CSR);
193
194 return 0;
195
196}
197
198static irqreturn_t dt2814_interrupt(int irq, void *d)
199{
200 int lo, hi;
201 struct comedi_device *dev = d;
202 struct dt2814_private *devpriv = dev->private;
203 struct comedi_subdevice *s;
204 int data;
205
206 if (!dev->attached) {
207 comedi_error(dev, "spurious interrupt");
208 return IRQ_HANDLED;
209 }
210
211 s = &dev->subdevices[0];
212
213 hi = inb(dev->iobase + DT2814_DATA);
214 lo = inb(dev->iobase + DT2814_DATA);
215
216 data = (hi << 4) | (lo >> 4);
217
218 if (!(--devpriv->ntrig)) {
219 int i;
220
221 outb(0, dev->iobase + DT2814_CSR);
222
223
224
225 for (i = 0; i < DT2814_TIMEOUT; i++) {
226 if (inb(dev->iobase + DT2814_CSR) & DT2814_FINISH)
227 break;
228 }
229 inb(dev->iobase + DT2814_DATA);
230 inb(dev->iobase + DT2814_DATA);
231
232 s->async->events |= COMEDI_CB_EOA;
233 }
234 comedi_event(dev, s);
235 return IRQ_HANDLED;
236}
237
238static int dt2814_attach(struct comedi_device *dev, struct comedi_devconfig *it)
239{
240 struct dt2814_private *devpriv;
241 int i, irq;
242 int ret;
243 struct comedi_subdevice *s;
244
245 ret = comedi_request_region(dev, it->options[0], DT2814_SIZE);
246 if (ret)
247 return ret;
248
249 outb(0, dev->iobase + DT2814_CSR);
250 udelay(100);
251 if (inb(dev->iobase + DT2814_CSR) & DT2814_ERR) {
252 printk(KERN_ERR "reset error (fatal)\n");
253 return -EIO;
254 }
255 i = inb(dev->iobase + DT2814_DATA);
256 i = inb(dev->iobase + DT2814_DATA);
257
258 irq = it->options[1];
259#if 0
260 if (irq < 0) {
261 save_flags(flags);
262 sti();
263 irqs = probe_irq_on();
264
265 outb(0, dev->iobase + DT2814_CSR);
266
267 udelay(100);
268
269 irq = probe_irq_off(irqs);
270 restore_flags(flags);
271 if (inb(dev->iobase + DT2814_CSR) & DT2814_ERR)
272 printk(KERN_DEBUG "error probing irq (bad)\n");
273
274
275 i = inb(dev->iobase + DT2814_DATA);
276 i = inb(dev->iobase + DT2814_DATA);
277 }
278#endif
279 dev->irq = 0;
280 if (irq > 0) {
281 if (request_irq(irq, dt2814_interrupt, 0, "dt2814", dev)) {
282 printk(KERN_WARNING "(irq %d unavailable)\n", irq);
283 } else {
284 printk(KERN_INFO "( irq = %d )\n", irq);
285 dev->irq = irq;
286 }
287 } else if (irq == 0) {
288 printk(KERN_WARNING "(no irq)\n");
289 } else {
290#if 0
291 printk(KERN_DEBUG "(probe returned multiple irqs--bad)\n");
292#else
293 printk(KERN_WARNING "(irq probe not implemented)\n");
294#endif
295 }
296
297 ret = comedi_alloc_subdevices(dev, 1);
298 if (ret)
299 return ret;
300
301 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
302 if (!devpriv)
303 return -ENOMEM;
304
305 s = &dev->subdevices[0];
306 dev->read_subdev = s;
307 s->type = COMEDI_SUBD_AI;
308 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
309 s->n_chan = 16;
310 s->len_chanlist = 1;
311 s->insn_read = dt2814_ai_insn_read;
312 s->do_cmd = dt2814_ai_cmd;
313 s->do_cmdtest = dt2814_ai_cmdtest;
314 s->maxdata = 0xfff;
315 s->range_table = &range_unknown;
316
317 return 0;
318}
319
320static struct comedi_driver dt2814_driver = {
321 .driver_name = "dt2814",
322 .module = THIS_MODULE,
323 .attach = dt2814_attach,
324 .detach = comedi_legacy_detach,
325};
326module_comedi_driver(dt2814_driver);
327
328MODULE_AUTHOR("Comedi http://www.comedi.org");
329MODULE_DESCRIPTION("Comedi low-level driver");
330MODULE_LICENSE("GPL");
331