Commit | Line | Data |
---|---|---|
62ed6662 BB |
1 | /*====================================================================== |
2 | ||
3 | comedi/drivers/quatech_daqp_cs.c | |
4 | ||
5 | Quatech DAQP PCMCIA data capture cards COMEDI client driver | |
6 | Copyright (C) 2000, 2003 Brent Baccala <baccala@freesoft.org> | |
7 | The DAQP interface code in this file is released into the public domain. | |
8 | ||
9 | COMEDI - Linux Control and Measurement Device Interface | |
10 | Copyright (C) 1998 David A. Schleef <ds@schleef.org> | |
11 | http://www.comedi.org/ | |
12 | ||
13 | quatech_daqp_cs.c 1.10 | |
14 | ||
15 | Documentation for the DAQP PCMCIA cards can be found on Quatech's site: | |
16 | ||
3420f6b4 | 17 | ftp://ftp.quatech.com/Manuals/daqp-208.pdf |
62ed6662 BB |
18 | |
19 | This manual is for both the DAQP-208 and the DAQP-308. | |
20 | ||
21 | What works: | |
22 | ||
23 | - A/D conversion | |
24 | - 8 channels | |
25 | - 4 gain ranges | |
26 | - ground ref or differential | |
27 | - single-shot and timed both supported | |
28 | - D/A conversion, single-shot | |
29 | - digital I/O | |
30 | ||
31 | What doesn't: | |
32 | ||
33 | - any kind of triggering - external or D/A channel 1 | |
34 | - the card's optional expansion board | |
35 | - the card's timer (for anything other than A/D conversion) | |
36 | - D/A update modes other than immediate (i.e, timed) | |
37 | - fancier timing modes | |
38 | - setting card's FIFO buffer thresholds to anything but default | |
39 | ||
40 | ======================================================================*/ | |
41 | ||
42 | /* | |
43 | Driver: quatech_daqp_cs | |
44 | Description: Quatech DAQP PCMCIA data capture cards | |
45 | Author: Brent Baccala <baccala@freesoft.org> | |
46 | Status: works | |
47 | Devices: [Quatech] DAQP-208 (daqp), DAQP-308 | |
48 | */ | |
49 | ||
ce157f80 | 50 | #include <linux/module.h> |
3142788b | 51 | #include <linux/semaphore.h> |
7ec52ed2 AIB |
52 | #include <linux/completion.h> |
53 | ||
f0dff1a4 | 54 | #include "../comedi_pcmcia.h" |
27020ffe | 55 | |
1801726e | 56 | struct daqp_private { |
62ed6662 | 57 | int stop; |
62ed6662 BB |
58 | |
59 | enum { semaphore, buffer } interrupt_mode; | |
60 | ||
7ec52ed2 | 61 | struct completion eos; |
ab64f663 | 62 | }; |
62ed6662 | 63 | |
62ed6662 BB |
64 | /* The DAQP communicates with the system through a 16 byte I/O window. */ |
65 | ||
66 | #define DAQP_FIFO_SIZE 4096 | |
67 | ||
68 | #define DAQP_FIFO 0 | |
69 | #define DAQP_SCANLIST 1 | |
70 | #define DAQP_CONTROL 2 | |
71 | #define DAQP_STATUS 2 | |
72 | #define DAQP_DIGITAL_IO 3 | |
73 | #define DAQP_PACER_LOW 4 | |
74 | #define DAQP_PACER_MID 5 | |
75 | #define DAQP_PACER_HIGH 6 | |
76 | #define DAQP_COMMAND 7 | |
77 | #define DAQP_DA 8 | |
78 | #define DAQP_TIMER 10 | |
79 | #define DAQP_AUX 15 | |
80 | ||
81 | #define DAQP_SCANLIST_DIFFERENTIAL 0x4000 | |
491205bb HS |
82 | #define DAQP_SCANLIST_GAIN(x) ((x) << 12) |
83 | #define DAQP_SCANLIST_CHANNEL(x) ((x) << 8) | |
62ed6662 | 84 | #define DAQP_SCANLIST_START 0x0080 |
491205bb | 85 | #define DAQP_SCANLIST_EXT_GAIN(x) ((x) << 4) |
62ed6662 BB |
86 | #define DAQP_SCANLIST_EXT_CHANNEL(x) (x) |
87 | ||
075d5816 HS |
88 | #define DAQP_CONTROL_PACER_CLK(x) (((x) & 0x3) << 6) |
89 | #define DAQP_CONTROL_PACER_CLK_EXT DAQP_CONTROL_PACER_CLK(0) | |
90 | #define DAQP_CONTROL_PACER_CLK_5MHZ DAQP_CONTROL_PACER_CLK(1) | |
91 | #define DAQP_CONTROL_PACER_CLK_1MHZ DAQP_CONTROL_PACER_CLK(2) | |
92 | #define DAQP_CONTROL_PACER_CLK_100KHZ DAQP_CONTROL_PACER_CLK(3) | |
62ed6662 BB |
93 | #define DAQP_CONTORL_EXPANSION 0x20 |
94 | #define DAQP_CONTROL_EOS_INT_ENABLE 0x10 | |
95 | #define DAQP_CONTROL_FIFO_INT_ENABLE 0x08 | |
96 | #define DAQP_CONTROL_TRIGGER_ONESHOT 0x00 | |
97 | #define DAQP_CONTROL_TRIGGER_CONTINUOUS 0x04 | |
98 | #define DAQP_CONTROL_TRIGGER_INTERNAL 0x00 | |
99 | #define DAQP_CONTROL_TRIGGER_EXTERNAL 0x02 | |
100 | #define DAQP_CONTROL_TRIGGER_RISING 0x00 | |
101 | #define DAQP_CONTROL_TRIGGER_FALLING 0x01 | |
102 | ||
103 | #define DAQP_STATUS_IDLE 0x80 | |
104 | #define DAQP_STATUS_RUNNING 0x40 | |
105 | #define DAQP_STATUS_EVENTS 0x38 | |
106 | #define DAQP_STATUS_DATA_LOST 0x20 | |
107 | #define DAQP_STATUS_END_OF_SCAN 0x10 | |
108 | #define DAQP_STATUS_FIFO_THRESHOLD 0x08 | |
109 | #define DAQP_STATUS_FIFO_FULL 0x04 | |
110 | #define DAQP_STATUS_FIFO_NEARFULL 0x02 | |
111 | #define DAQP_STATUS_FIFO_EMPTY 0x01 | |
112 | ||
113 | #define DAQP_COMMAND_ARM 0x80 | |
114 | #define DAQP_COMMAND_RSTF 0x40 | |
115 | #define DAQP_COMMAND_RSTQ 0x20 | |
116 | #define DAQP_COMMAND_STOP 0x10 | |
117 | #define DAQP_COMMAND_LATCH 0x08 | |
118 | #define DAQP_COMMAND_100kHz 0x00 | |
119 | #define DAQP_COMMAND_50kHz 0x02 | |
120 | #define DAQP_COMMAND_25kHz 0x04 | |
121 | #define DAQP_COMMAND_FIFO_DATA 0x01 | |
122 | #define DAQP_COMMAND_FIFO_PROGRAM 0x00 | |
123 | ||
124 | #define DAQP_AUX_TRIGGER_TTL 0x00 | |
125 | #define DAQP_AUX_TRIGGER_ANALOG 0x80 | |
126 | #define DAQP_AUX_TRIGGER_PRETRIGGER 0x40 | |
127 | #define DAQP_AUX_TIMER_INT_ENABLE 0x20 | |
128 | #define DAQP_AUX_TIMER_RELOAD 0x00 | |
129 | #define DAQP_AUX_TIMER_PAUSE 0x08 | |
130 | #define DAQP_AUX_TIMER_GO 0x10 | |
131 | #define DAQP_AUX_TIMER_GO_EXTERNAL 0x18 | |
132 | #define DAQP_AUX_TIMER_EXTERNAL_SRC 0x04 | |
133 | #define DAQP_AUX_TIMER_INTERNAL_SRC 0x00 | |
134 | #define DAQP_AUX_DA_DIRECT 0x00 | |
135 | #define DAQP_AUX_DA_OVERFLOW 0x01 | |
136 | #define DAQP_AUX_DA_EXTERNAL 0x02 | |
137 | #define DAQP_AUX_DA_PACER 0x03 | |
138 | ||
139 | #define DAQP_AUX_RUNNING 0x80 | |
140 | #define DAQP_AUX_TRIGGERED 0x40 | |
141 | #define DAQP_AUX_DA_BUFFER 0x20 | |
142 | #define DAQP_AUX_TIMER_OVERFLOW 0x10 | |
143 | #define DAQP_AUX_CONVERSION 0x08 | |
144 | #define DAQP_AUX_DATA_LOST 0x04 | |
145 | #define DAQP_AUX_FIFO_NEARFULL 0x02 | |
146 | #define DAQP_AUX_FIFO_EMPTY 0x01 | |
147 | ||
e23fe9a1 HS |
148 | static const struct comedi_lrange range_daqp_ai = { |
149 | 4, { | |
150 | BIP_RANGE(10), | |
151 | BIP_RANGE(5), | |
152 | BIP_RANGE(2.5), | |
153 | BIP_RANGE(1.25) | |
154 | } | |
62ed6662 BB |
155 | }; |
156 | ||
d9952688 HS |
157 | static int daqp_clear_events(struct comedi_device *dev, int loops) |
158 | { | |
159 | unsigned int status; | |
160 | ||
161 | /* | |
162 | * Reset any pending interrupts (my card has a tendency to require | |
163 | * require multiple reads on the status register to achieve this). | |
164 | */ | |
165 | while (--loops) { | |
166 | status = inb(dev->iobase + DAQP_STATUS); | |
167 | if ((status & DAQP_STATUS_EVENTS) == 0) | |
168 | return 0; | |
169 | } | |
170 | dev_err(dev->class_dev, "couldn't clear events in status register\n"); | |
171 | return -EBUSY; | |
172 | } | |
173 | ||
62ed6662 BB |
174 | /* Cancel a running acquisition */ |
175 | ||
da91b269 | 176 | static int daqp_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s) |
62ed6662 | 177 | { |
1801726e | 178 | struct daqp_private *devpriv = dev->private; |
62ed6662 | 179 | |
1801726e | 180 | if (devpriv->stop) |
62ed6662 | 181 | return -EIO; |
3420f6b4 | 182 | |
62ed6662 BB |
183 | outb(DAQP_COMMAND_STOP, dev->iobase + DAQP_COMMAND); |
184 | ||
185 | /* flush any linguring data in FIFO - superfluous here */ | |
186 | /* outb(DAQP_COMMAND_RSTF, dev->iobase+DAQP_COMMAND); */ | |
187 | ||
1801726e | 188 | devpriv->interrupt_mode = semaphore; |
62ed6662 BB |
189 | |
190 | return 0; | |
191 | } | |
192 | ||
c3f7e153 HS |
193 | static unsigned int daqp_ai_get_sample(struct comedi_device *dev, |
194 | struct comedi_subdevice *s) | |
195 | { | |
196 | unsigned int val; | |
197 | ||
198 | /* | |
199 | * Get a two's complement sample from the FIFO and | |
200 | * return the munged offset binary value. | |
201 | */ | |
202 | val = inb(dev->iobase + DAQP_FIFO); | |
203 | val |= inb(dev->iobase + DAQP_FIFO) << 8; | |
204 | return comedi_offset_munge(s, val); | |
205 | } | |
206 | ||
62ed6662 BB |
207 | /* Interrupt handler |
208 | * | |
1801726e HS |
209 | * Operates in one of two modes. If devpriv->interrupt_mode is |
210 | * 'semaphore', just signal the devpriv->eos completion and return | |
62ed6662 BB |
211 | * (one-shot mode). Otherwise (continuous mode), read data in from |
212 | * the card, transfer it to the buffer provided by the higher-level | |
213 | * comedi kernel module, and signal various comedi callback routines, | |
214 | * which run pretty quick. | |
215 | */ | |
e3752a1d | 216 | static enum irqreturn daqp_interrupt(int irq, void *dev_id) |
62ed6662 | 217 | { |
c1271742 | 218 | struct comedi_device *dev = dev_id; |
1801726e | 219 | struct daqp_private *devpriv = dev->private; |
c1271742 | 220 | struct comedi_subdevice *s = dev->read_subdev; |
2d7840c7 | 221 | struct comedi_cmd *cmd = &s->async->cmd; |
62ed6662 BB |
222 | int loop_limit = 10000; |
223 | int status; | |
224 | ||
c1271742 | 225 | if (!dev->attached) |
e3752a1d | 226 | return IRQ_NONE; |
62ed6662 | 227 | |
1801726e | 228 | switch (devpriv->interrupt_mode) { |
62ed6662 | 229 | case semaphore: |
1801726e | 230 | complete(&devpriv->eos); |
62ed6662 BB |
231 | break; |
232 | ||
233 | case buffer: | |
62ed6662 | 234 | while (!((status = inb(dev->iobase + DAQP_STATUS)) |
0a85b6f0 | 235 | & DAQP_STATUS_FIFO_EMPTY)) { |
8bab0d68 | 236 | unsigned short data; |
62ed6662 BB |
237 | |
238 | if (status & DAQP_STATUS_DATA_LOST) { | |
3e6cb74f | 239 | s->async->events |= COMEDI_CB_OVERFLOW; |
53f63dc7 | 240 | dev_warn(dev->class_dev, "data lost\n"); |
62ed6662 BB |
241 | break; |
242 | } | |
243 | ||
c3f7e153 | 244 | data = daqp_ai_get_sample(dev, s); |
a4a68fe2 | 245 | comedi_buf_write_samples(s, &data, 1); |
62ed6662 BB |
246 | |
247 | /* If there's a limit, decrement it | |
248 | * and stop conversion if zero | |
249 | */ | |
250 | ||
2d7840c7 HS |
251 | if (cmd->stop_src == TRIG_COUNT && |
252 | s->async->scans_done >= cmd->stop_arg) { | |
253 | s->async->events |= COMEDI_CB_EOA; | |
254 | break; | |
62ed6662 BB |
255 | } |
256 | ||
257 | if ((loop_limit--) <= 0) | |
258 | break; | |
259 | } | |
260 | ||
261 | if (loop_limit <= 0) { | |
ce3ed9f0 YT |
262 | dev_warn(dev->class_dev, |
263 | "loop_limit reached in daqp_interrupt()\n"); | |
3e6cb74f | 264 | s->async->events |= COMEDI_CB_ERROR; |
62ed6662 BB |
265 | } |
266 | ||
44aaad2d | 267 | comedi_handle_events(dev, s); |
62ed6662 | 268 | } |
e3752a1d | 269 | return IRQ_HANDLED; |
62ed6662 BB |
270 | } |
271 | ||
bd7807f9 HS |
272 | static void daqp_ai_set_one_scanlist_entry(struct comedi_device *dev, |
273 | unsigned int chanspec, | |
274 | int start) | |
275 | { | |
276 | unsigned int chan = CR_CHAN(chanspec); | |
277 | unsigned int range = CR_RANGE(chanspec); | |
278 | unsigned int aref = CR_AREF(chanspec); | |
279 | unsigned int val; | |
280 | ||
281 | val = DAQP_SCANLIST_CHANNEL(chan) | DAQP_SCANLIST_GAIN(range); | |
282 | ||
283 | if (aref == AREF_DIFF) | |
284 | val |= DAQP_SCANLIST_DIFFERENTIAL; | |
285 | ||
286 | if (start) | |
287 | val |= DAQP_SCANLIST_START; | |
288 | ||
289 | outb(val & 0xff, dev->iobase + DAQP_SCANLIST); | |
290 | outb((val >> 8) & 0xff, dev->iobase + DAQP_SCANLIST); | |
291 | } | |
292 | ||
62ed6662 BB |
293 | /* One-shot analog data acquisition routine */ |
294 | ||
0a85b6f0 MT |
295 | static int daqp_ai_insn_read(struct comedi_device *dev, |
296 | struct comedi_subdevice *s, | |
297 | struct comedi_insn *insn, unsigned int *data) | |
62ed6662 | 298 | { |
1801726e | 299 | struct daqp_private *devpriv = dev->private; |
d9952688 | 300 | int ret; |
62ed6662 | 301 | int i; |
62ed6662 | 302 | |
1801726e | 303 | if (devpriv->stop) |
62ed6662 | 304 | return -EIO; |
3420f6b4 | 305 | |
62ed6662 BB |
306 | /* Stop any running conversion */ |
307 | daqp_ai_cancel(dev, s); | |
308 | ||
309 | outb(0, dev->iobase + DAQP_AUX); | |
310 | ||
311 | /* Reset scan list queue */ | |
312 | outb(DAQP_COMMAND_RSTQ, dev->iobase + DAQP_COMMAND); | |
313 | ||
314 | /* Program one scan list entry */ | |
bd7807f9 | 315 | daqp_ai_set_one_scanlist_entry(dev, insn->chanspec, 1); |
62ed6662 BB |
316 | |
317 | /* Reset data FIFO (see page 28 of DAQP User's Manual) */ | |
318 | ||
319 | outb(DAQP_COMMAND_RSTF, dev->iobase + DAQP_COMMAND); | |
320 | ||
321 | /* Set trigger */ | |
075d5816 HS |
322 | outb(DAQP_CONTROL_TRIGGER_ONESHOT | DAQP_CONTROL_TRIGGER_INTERNAL | |
323 | DAQP_CONTROL_PACER_CLK_100KHZ | DAQP_CONTROL_EOS_INT_ENABLE, | |
324 | dev->iobase + DAQP_CONTROL); | |
62ed6662 | 325 | |
d9952688 HS |
326 | ret = daqp_clear_events(dev, 10000); |
327 | if (ret) | |
328 | return ret; | |
62ed6662 | 329 | |
1801726e HS |
330 | init_completion(&devpriv->eos); |
331 | devpriv->interrupt_mode = semaphore; | |
62ed6662 BB |
332 | |
333 | for (i = 0; i < insn->n; i++) { | |
62ed6662 BB |
334 | /* Start conversion */ |
335 | outb(DAQP_COMMAND_ARM | DAQP_COMMAND_FIFO_DATA, | |
0a85b6f0 | 336 | dev->iobase + DAQP_COMMAND); |
62ed6662 | 337 | |
7ec52ed2 | 338 | /* Wait for interrupt service routine to unblock completion */ |
62ed6662 | 339 | /* Maybe could use a timeout here, but it's interruptible */ |
1801726e | 340 | if (wait_for_completion_interruptible(&devpriv->eos)) |
62ed6662 BB |
341 | return -EINTR; |
342 | ||
c3f7e153 | 343 | data[i] = daqp_ai_get_sample(dev, s); |
62ed6662 BB |
344 | } |
345 | ||
346 | return insn->n; | |
347 | } | |
348 | ||
349 | /* This function converts ns nanoseconds to a counter value suitable | |
350 | * for programming the device. We always use the DAQP's 5 MHz clock, | |
351 | * which with its 24-bit counter, allows values up to 84 seconds. | |
352 | * Also, the function adjusts ns so that it cooresponds to the actual | |
353 | * time that the device will use. | |
354 | */ | |
355 | ||
a207c12f | 356 | static int daqp_ns_to_timer(unsigned int *ns, unsigned int flags) |
62ed6662 BB |
357 | { |
358 | int timer; | |
359 | ||
360 | timer = *ns / 200; | |
361 | *ns = timer * 200; | |
362 | ||
363 | return timer; | |
364 | } | |
365 | ||
366 | /* cmdtest tests a particular command to see if it is valid. | |
367 | * Using the cmdtest ioctl, a user can create a valid cmd | |
368 | * and then have it executed by the cmd ioctl. | |
369 | * | |
370 | * cmdtest returns 1,2,3,4 or 0, depending on which tests | |
371 | * the command passes. | |
372 | */ | |
373 | ||
0a85b6f0 MT |
374 | static int daqp_ai_cmdtest(struct comedi_device *dev, |
375 | struct comedi_subdevice *s, struct comedi_cmd *cmd) | |
62ed6662 BB |
376 | { |
377 | int err = 0; | |
6c71941d | 378 | unsigned int arg; |
62ed6662 | 379 | |
27020ffe | 380 | /* Step 1 : check if triggers are trivially valid */ |
62ed6662 | 381 | |
75a12586 IA |
382 | err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW); |
383 | err |= comedi_check_trigger_src(&cmd->scan_begin_src, | |
27020ffe | 384 | TRIG_TIMER | TRIG_FOLLOW); |
75a12586 | 385 | err |= comedi_check_trigger_src(&cmd->convert_src, |
27020ffe | 386 | TRIG_TIMER | TRIG_NOW); |
75a12586 IA |
387 | err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); |
388 | err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); | |
62ed6662 BB |
389 | |
390 | if (err) | |
391 | return 1; | |
392 | ||
27020ffe | 393 | /* Step 2a : make sure trigger sources are unique */ |
62ed6662 | 394 | |
75a12586 IA |
395 | err |= comedi_check_trigger_is_unique(cmd->scan_begin_src); |
396 | err |= comedi_check_trigger_is_unique(cmd->convert_src); | |
397 | err |= comedi_check_trigger_is_unique(cmd->stop_src); | |
27020ffe HS |
398 | |
399 | /* Step 2b : and mutually compatible */ | |
62ed6662 BB |
400 | |
401 | if (err) | |
402 | return 2; | |
403 | ||
42cae4a1 HS |
404 | /* Step 3: check if arguments are trivially valid */ |
405 | ||
75a12586 | 406 | err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); |
62ed6662 | 407 | |
62ed6662 BB |
408 | #define MAX_SPEED 10000 /* 100 kHz - in nanoseconds */ |
409 | ||
75a12586 IA |
410 | if (cmd->scan_begin_src == TRIG_TIMER) { |
411 | err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, | |
412 | MAX_SPEED); | |
413 | } | |
62ed6662 BB |
414 | |
415 | /* If both scan_begin and convert are both timer values, the only | |
416 | * way that can make sense is if the scan time is the number of | |
417 | * conversions times the convert time | |
418 | */ | |
419 | ||
420 | if (cmd->scan_begin_src == TRIG_TIMER && cmd->convert_src == TRIG_TIMER | |
0a85b6f0 | 421 | && cmd->scan_begin_arg != cmd->convert_arg * cmd->scan_end_arg) { |
42cae4a1 | 422 | err |= -EINVAL; |
62ed6662 BB |
423 | } |
424 | ||
75a12586 IA |
425 | if (cmd->convert_src == TRIG_TIMER) { |
426 | err |= comedi_check_trigger_arg_min(&cmd->convert_arg, | |
427 | MAX_SPEED); | |
428 | } | |
62ed6662 | 429 | |
75a12586 IA |
430 | err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, |
431 | cmd->chanlist_len); | |
42cae4a1 HS |
432 | |
433 | if (cmd->stop_src == TRIG_COUNT) | |
75a12586 | 434 | err |= comedi_check_trigger_arg_max(&cmd->stop_arg, 0x00ffffff); |
42cae4a1 | 435 | else /* TRIG_NONE */ |
75a12586 | 436 | err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); |
62ed6662 BB |
437 | |
438 | if (err) | |
439 | return 3; | |
440 | ||
441 | /* step 4: fix up any arguments */ | |
442 | ||
443 | if (cmd->scan_begin_src == TRIG_TIMER) { | |
6c71941d | 444 | arg = cmd->scan_begin_arg; |
a207c12f | 445 | daqp_ns_to_timer(&arg, cmd->flags); |
75a12586 | 446 | err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg); |
62ed6662 BB |
447 | } |
448 | ||
449 | if (cmd->convert_src == TRIG_TIMER) { | |
6c71941d | 450 | arg = cmd->convert_arg; |
a207c12f | 451 | daqp_ns_to_timer(&arg, cmd->flags); |
75a12586 | 452 | err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg); |
62ed6662 BB |
453 | } |
454 | ||
455 | if (err) | |
456 | return 4; | |
457 | ||
458 | return 0; | |
459 | } | |
460 | ||
da91b269 | 461 | static int daqp_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
62ed6662 | 462 | { |
1801726e | 463 | struct daqp_private *devpriv = dev->private; |
ea6d0d4c | 464 | struct comedi_cmd *cmd = &s->async->cmd; |
e3752a1d | 465 | int counter; |
62ed6662 BB |
466 | int scanlist_start_on_every_entry; |
467 | int threshold; | |
d9952688 | 468 | int ret; |
62ed6662 | 469 | int i; |
62ed6662 | 470 | |
1801726e | 471 | if (devpriv->stop) |
62ed6662 | 472 | return -EIO; |
3420f6b4 | 473 | |
62ed6662 BB |
474 | /* Stop any running conversion */ |
475 | daqp_ai_cancel(dev, s); | |
476 | ||
477 | outb(0, dev->iobase + DAQP_AUX); | |
478 | ||
479 | /* Reset scan list queue */ | |
480 | outb(DAQP_COMMAND_RSTQ, dev->iobase + DAQP_COMMAND); | |
481 | ||
482 | /* Program pacer clock | |
483 | * | |
484 | * There's two modes we can operate in. If convert_src is | |
485 | * TRIG_TIMER, then convert_arg specifies the time between | |
486 | * each conversion, so we program the pacer clock to that | |
487 | * frequency and set the SCANLIST_START bit on every scanlist | |
488 | * entry. Otherwise, convert_src is TRIG_NOW, which means | |
489 | * we want the fastest possible conversions, scan_begin_src | |
490 | * is TRIG_TIMER, and scan_begin_arg specifies the time between | |
491 | * each scan, so we program the pacer clock to this frequency | |
492 | * and only set the SCANLIST_START bit on the first entry. | |
493 | */ | |
494 | ||
495 | if (cmd->convert_src == TRIG_TIMER) { | |
a207c12f | 496 | counter = daqp_ns_to_timer(&cmd->convert_arg, cmd->flags); |
62ed6662 BB |
497 | outb(counter & 0xff, dev->iobase + DAQP_PACER_LOW); |
498 | outb((counter >> 8) & 0xff, dev->iobase + DAQP_PACER_MID); | |
499 | outb((counter >> 16) & 0xff, dev->iobase + DAQP_PACER_HIGH); | |
500 | scanlist_start_on_every_entry = 1; | |
501 | } else { | |
a207c12f | 502 | counter = daqp_ns_to_timer(&cmd->scan_begin_arg, cmd->flags); |
62ed6662 BB |
503 | outb(counter & 0xff, dev->iobase + DAQP_PACER_LOW); |
504 | outb((counter >> 8) & 0xff, dev->iobase + DAQP_PACER_MID); | |
505 | outb((counter >> 16) & 0xff, dev->iobase + DAQP_PACER_HIGH); | |
506 | scanlist_start_on_every_entry = 0; | |
507 | } | |
508 | ||
509 | /* Program scan list */ | |
62ed6662 | 510 | for (i = 0; i < cmd->chanlist_len; i++) { |
bd7807f9 | 511 | int start = (i == 0 || scanlist_start_on_every_entry); |
62ed6662 | 512 | |
bd7807f9 | 513 | daqp_ai_set_one_scanlist_entry(dev, cmd->chanlist[i], start); |
62ed6662 BB |
514 | } |
515 | ||
516 | /* Now it's time to program the FIFO threshold, basically the | |
517 | * number of samples the card will buffer before it interrupts | |
518 | * the CPU. | |
519 | * | |
520 | * If we don't have a stop count, then use half the size of | |
521 | * the FIFO (the manufacturer's recommendation). Consider | |
522 | * that the FIFO can hold 2K samples (4K bytes). With the | |
523 | * threshold set at half the FIFO size, we have a margin of | |
524 | * error of 1024 samples. At the chip's maximum sample rate | |
525 | * of 100,000 Hz, the CPU would have to delay interrupt | |
526 | * service for a full 10 milliseconds in order to lose data | |
527 | * here (as opposed to higher up in the kernel). I've never | |
528 | * seen it happen. However, for slow sample rates it may | |
529 | * buffer too much data and introduce too much delay for the | |
530 | * user application. | |
531 | * | |
532 | * If we have a stop count, then things get more interesting. | |
533 | * If the stop count is less than the FIFO size (actually | |
534 | * three-quarters of the FIFO size - see below), we just use | |
535 | * the stop count itself as the threshold, the card interrupts | |
536 | * us when that many samples have been taken, and we kill the | |
537 | * acquisition at that point and are done. If the stop count | |
538 | * is larger than that, then we divide it by 2 until it's less | |
539 | * than three quarters of the FIFO size (we always leave the | |
540 | * top quarter of the FIFO as protection against sluggish CPU | |
541 | * interrupt response) and use that as the threshold. So, if | |
542 | * the stop count is 4000 samples, we divide by two twice to | |
543 | * get 1000 samples, use that as the threshold, take four | |
544 | * interrupts to get our 4000 samples and are done. | |
545 | * | |
546 | * The algorithm could be more clever. For example, if 81000 | |
547 | * samples are requested, we could set the threshold to 1500 | |
548 | * samples and take 54 interrupts to get 81000. But 54 isn't | |
549 | * a power of two, so this algorithm won't find that option. | |
550 | * Instead, it'll set the threshold at 1266 and take 64 | |
551 | * interrupts to get 81024 samples, of which the last 24 will | |
552 | * be discarded... but we won't get the last interrupt until | |
553 | * they've been collected. To find the first option, the | |
554 | * computer could look at the prime decomposition of the | |
555 | * sample count (81000 = 3^4 * 5^3 * 2^3) and factor it into a | |
556 | * threshold (1500 = 3 * 5^3 * 2^2) and an interrupt count (54 | |
557 | * = 3^3 * 2). Hmmm... a one-line while loop or prime | |
558 | * decomposition of integers... I'll leave it the way it is. | |
559 | * | |
560 | * I'll also note a mini-race condition before ignoring it in | |
561 | * the code. Let's say we're taking 4000 samples, as before. | |
562 | * After 1000 samples, we get an interrupt. But before that | |
563 | * interrupt is completely serviced, another sample is taken | |
564 | * and loaded into the FIFO. Since the interrupt handler | |
565 | * empties the FIFO before returning, it will read 1001 samples. | |
566 | * If that happens four times, we'll end up taking 4004 samples, | |
567 | * not 4000. The interrupt handler will discard the extra four | |
568 | * samples (by halting the acquisition with four samples still | |
569 | * in the FIFO), but we will have to wait for them. | |
570 | * | |
571 | * In short, this code works pretty well, but for either of | |
572 | * the two reasons noted, might end up waiting for a few more | |
573 | * samples than actually requested. Shouldn't make too much | |
574 | * of a difference. | |
575 | */ | |
576 | ||
577 | /* Save away the number of conversions we should perform, and | |
578 | * compute the FIFO threshold (in bytes, not samples - that's | |
1801726e | 579 | * why we multiple devpriv->count by 2 = sizeof(sample)) |
62ed6662 BB |
580 | */ |
581 | ||
582 | if (cmd->stop_src == TRIG_COUNT) { | |
2d7840c7 HS |
583 | unsigned long long nsamples; |
584 | unsigned long long nbytes; | |
585 | ||
586 | nsamples = (unsigned long long)cmd->stop_arg * | |
587 | cmd->scan_end_arg; | |
588 | nbytes = nsamples * comedi_bytes_per_sample(s); | |
589 | while (nbytes > DAQP_FIFO_SIZE * 3 / 4) | |
590 | nbytes /= 2; | |
591 | threshold = nbytes; | |
62ed6662 | 592 | } else { |
62ed6662 BB |
593 | threshold = DAQP_FIFO_SIZE / 2; |
594 | } | |
595 | ||
596 | /* Reset data FIFO (see page 28 of DAQP User's Manual) */ | |
597 | ||
598 | outb(DAQP_COMMAND_RSTF, dev->iobase + DAQP_COMMAND); | |
599 | ||
600 | /* Set FIFO threshold. First two bytes are near-empty | |
601 | * threshold, which is unused; next two bytes are near-full | |
602 | * threshold. We computed the number of bytes we want in the | |
603 | * FIFO when the interrupt is generated, what the card wants | |
604 | * is actually the number of available bytes left in the FIFO | |
605 | * when the interrupt is to happen. | |
606 | */ | |
607 | ||
608 | outb(0x00, dev->iobase + DAQP_FIFO); | |
609 | outb(0x00, dev->iobase + DAQP_FIFO); | |
610 | ||
611 | outb((DAQP_FIFO_SIZE - threshold) & 0xff, dev->iobase + DAQP_FIFO); | |
612 | outb((DAQP_FIFO_SIZE - threshold) >> 8, dev->iobase + DAQP_FIFO); | |
613 | ||
614 | /* Set trigger */ | |
075d5816 HS |
615 | outb(DAQP_CONTROL_TRIGGER_CONTINUOUS | DAQP_CONTROL_TRIGGER_INTERNAL | |
616 | DAQP_CONTROL_PACER_CLK_5MHZ | DAQP_CONTROL_FIFO_INT_ENABLE, | |
617 | dev->iobase + DAQP_CONTROL); | |
62ed6662 | 618 | |
d9952688 HS |
619 | ret = daqp_clear_events(dev, 100); |
620 | if (ret) | |
621 | return ret; | |
62ed6662 | 622 | |
1801726e | 623 | devpriv->interrupt_mode = buffer; |
62ed6662 BB |
624 | |
625 | /* Start conversion */ | |
626 | outb(DAQP_COMMAND_ARM | DAQP_COMMAND_FIFO_DATA, | |
0a85b6f0 | 627 | dev->iobase + DAQP_COMMAND); |
62ed6662 BB |
628 | |
629 | return 0; | |
630 | } | |
631 | ||
e031642e HS |
632 | static int daqp_ao_empty(struct comedi_device *dev, |
633 | struct comedi_subdevice *s, | |
634 | struct comedi_insn *insn, | |
635 | unsigned long context) | |
636 | { | |
637 | unsigned int status; | |
638 | ||
639 | status = inb(dev->iobase + DAQP_AUX); | |
640 | if ((status & DAQP_AUX_DA_BUFFER) == 0) | |
641 | return 0; | |
642 | return -EBUSY; | |
643 | } | |
644 | ||
0a85b6f0 MT |
645 | static int daqp_ao_insn_write(struct comedi_device *dev, |
646 | struct comedi_subdevice *s, | |
d35dcc89 HS |
647 | struct comedi_insn *insn, |
648 | unsigned int *data) | |
62ed6662 | 649 | { |
1801726e | 650 | struct daqp_private *devpriv = dev->private; |
d35dcc89 | 651 | unsigned int chan = CR_CHAN(insn->chanspec); |
d35dcc89 | 652 | int i; |
62ed6662 | 653 | |
1801726e | 654 | if (devpriv->stop) |
62ed6662 | 655 | return -EIO; |
62ed6662 | 656 | |
62ed6662 BB |
657 | /* Make sure D/A update mode is direct update */ |
658 | outb(0, dev->iobase + DAQP_AUX); | |
659 | ||
d35dcc89 | 660 | for (i = 0; i > insn->n; i++) { |
e024181b | 661 | unsigned val = data[i]; |
e031642e | 662 | int ret; |
e024181b | 663 | |
e031642e HS |
664 | /* D/A transfer rate is about 8ms */ |
665 | ret = comedi_timeout(dev, s, insn, daqp_ao_empty, 0); | |
666 | if (ret) | |
667 | return ret; | |
92b66775 | 668 | |
1271dd24 HS |
669 | /* write the two's complement value to the channel */ |
670 | outw((chan << 12) | comedi_offset_munge(s, val), | |
671 | dev->iobase + DAQP_DA); | |
e031642e HS |
672 | |
673 | s->readback[chan] = val; | |
d35dcc89 HS |
674 | } |
675 | ||
676 | return insn->n; | |
62ed6662 BB |
677 | } |
678 | ||
62100fef | 679 | static int daqp_di_insn_bits(struct comedi_device *dev, |
0a85b6f0 | 680 | struct comedi_subdevice *s, |
62100fef HS |
681 | struct comedi_insn *insn, |
682 | unsigned int *data) | |
62ed6662 | 683 | { |
1801726e | 684 | struct daqp_private *devpriv = dev->private; |
62ed6662 | 685 | |
1801726e | 686 | if (devpriv->stop) |
62ed6662 | 687 | return -EIO; |
62ed6662 BB |
688 | |
689 | data[0] = inb(dev->iobase + DAQP_DIGITAL_IO); | |
690 | ||
62100fef | 691 | return insn->n; |
62ed6662 BB |
692 | } |
693 | ||
6a911d8a HS |
694 | static int daqp_do_insn_bits(struct comedi_device *dev, |
695 | struct comedi_subdevice *s, | |
696 | struct comedi_insn *insn, | |
697 | unsigned int *data) | |
62ed6662 | 698 | { |
1801726e | 699 | struct daqp_private *devpriv = dev->private; |
62ed6662 | 700 | |
1801726e | 701 | if (devpriv->stop) |
62ed6662 | 702 | return -EIO; |
62ed6662 | 703 | |
97f4289a | 704 | if (comedi_dio_update_state(s, data)) |
6a911d8a | 705 | outb(s->state, dev->iobase + DAQP_DIGITAL_IO); |
6a911d8a HS |
706 | |
707 | data[1] = s->state; | |
708 | ||
709 | return insn->n; | |
62ed6662 BB |
710 | } |
711 | ||
c04edbf2 HS |
712 | static int daqp_auto_attach(struct comedi_device *dev, |
713 | unsigned long context) | |
62ed6662 | 714 | { |
c04edbf2 | 715 | struct pcmcia_device *link = comedi_to_pcmcia_dev(dev); |
1801726e | 716 | struct daqp_private *devpriv; |
34c43922 | 717 | struct comedi_subdevice *s; |
c65c64d0 | 718 | int ret; |
62ed6662 | 719 | |
0bdab509 | 720 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
1801726e | 721 | if (!devpriv) |
c04edbf2 | 722 | return -ENOMEM; |
62ed6662 | 723 | |
c04edbf2 | 724 | link->config_flags |= CONF_AUTO_SET_IO | CONF_ENABLE_IRQ; |
a9d50943 | 725 | ret = comedi_pcmcia_enable(dev, NULL); |
c04edbf2 HS |
726 | if (ret) |
727 | return ret; | |
87fe1452 | 728 | dev->iobase = link->resource[0]->start; |
62ed6662 | 729 | |
c1271742 | 730 | link->priv = dev; |
c04edbf2 HS |
731 | ret = pcmcia_request_irq(link, daqp_interrupt); |
732 | if (ret) | |
733 | return ret; | |
734 | ||
2f0b9d08 | 735 | ret = comedi_alloc_subdevices(dev, 4); |
8b6c5694 | 736 | if (ret) |
62ed6662 BB |
737 | return ret; |
738 | ||
123c0e03 | 739 | s = &dev->subdevices[0]; |
62ed6662 | 740 | dev->read_subdev = s; |
b7c0afa4 HS |
741 | s->type = COMEDI_SUBD_AI; |
742 | s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ; | |
743 | s->n_chan = 8; | |
744 | s->len_chanlist = 2048; | |
745 | s->maxdata = 0xffff; | |
746 | s->range_table = &range_daqp_ai; | |
747 | s->insn_read = daqp_ai_insn_read; | |
748 | s->do_cmdtest = daqp_ai_cmdtest; | |
749 | s->do_cmd = daqp_ai_cmd; | |
750 | s->cancel = daqp_ai_cancel; | |
62ed6662 | 751 | |
123c0e03 | 752 | s = &dev->subdevices[1]; |
b7c0afa4 | 753 | s->type = COMEDI_SUBD_AO; |
ef49d832 | 754 | s->subdev_flags = SDF_WRITABLE; |
b7c0afa4 HS |
755 | s->n_chan = 2; |
756 | s->maxdata = 0x0fff; | |
757 | s->range_table = &range_bipolar5; | |
758 | s->insn_write = daqp_ao_insn_write; | |
92b66775 HS |
759 | |
760 | ret = comedi_alloc_subdev_readback(s); | |
761 | if (ret) | |
762 | return ret; | |
62ed6662 | 763 | |
4d6b7008 HS |
764 | /* |
765 | * Digital Input subdevice | |
766 | * NOTE: The digital input lines are shared: | |
767 | * | |
768 | * Chan Normal Mode Expansion Mode | |
769 | * ---- ----------------- ---------------------------- | |
770 | * 0 DI0, ext. trigger Same as normal mode | |
771 | * 1 DI1 External gain select, lo bit | |
772 | * 2 DI2, ext. clock Same as normal mode | |
773 | * 3 DI3 External gain select, hi bit | |
774 | */ | |
123c0e03 | 775 | s = &dev->subdevices[2]; |
b7c0afa4 HS |
776 | s->type = COMEDI_SUBD_DI; |
777 | s->subdev_flags = SDF_READABLE; | |
4d6b7008 | 778 | s->n_chan = 4; |
62100fef HS |
779 | s->maxdata = 1; |
780 | s->insn_bits = daqp_di_insn_bits; | |
62ed6662 | 781 | |
23d4f024 HS |
782 | /* |
783 | * Digital Output subdevice | |
784 | * NOTE: The digital output lines share the same pins on the | |
785 | * interface connector as the four external channel selection | |
786 | * bits. If expansion mode is used the digital outputs do not | |
787 | * work. | |
788 | */ | |
123c0e03 | 789 | s = &dev->subdevices[3]; |
b7c0afa4 | 790 | s->type = COMEDI_SUBD_DO; |
ef49d832 | 791 | s->subdev_flags = SDF_WRITABLE; |
23d4f024 | 792 | s->n_chan = 4; |
6a911d8a HS |
793 | s->maxdata = 1; |
794 | s->insn_bits = daqp_do_insn_bits; | |
62ed6662 | 795 | |
b7c0afa4 | 796 | return 0; |
62ed6662 BB |
797 | } |
798 | ||
d1db2a41 HS |
799 | static struct comedi_driver driver_daqp = { |
800 | .driver_name = "quatech_daqp_cs", | |
801 | .module = THIS_MODULE, | |
c04edbf2 | 802 | .auto_attach = daqp_auto_attach, |
25736670 | 803 | .detach = comedi_pcmcia_disable, |
d1db2a41 HS |
804 | }; |
805 | ||
d1db2a41 | 806 | static int daqp_cs_suspend(struct pcmcia_device *link) |
62ed6662 | 807 | { |
c1271742 | 808 | struct comedi_device *dev = link->priv; |
1801726e | 809 | struct daqp_private *devpriv = dev ? dev->private : NULL; |
62ed6662 | 810 | |
d1db2a41 | 811 | /* Mark the device as stopped, to block IO until later */ |
1801726e HS |
812 | if (devpriv) |
813 | devpriv->stop = 1; | |
c1271742 | 814 | |
d1db2a41 HS |
815 | return 0; |
816 | } | |
62ed6662 | 817 | |
d1db2a41 HS |
818 | static int daqp_cs_resume(struct pcmcia_device *link) |
819 | { | |
c1271742 | 820 | struct comedi_device *dev = link->priv; |
1801726e | 821 | struct daqp_private *devpriv = dev ? dev->private : NULL; |
62ed6662 | 822 | |
1801726e HS |
823 | if (devpriv) |
824 | devpriv->stop = 0; | |
62ed6662 BB |
825 | |
826 | return 0; | |
d1db2a41 | 827 | } |
62ed6662 | 828 | |
d1db2a41 | 829 | static int daqp_cs_attach(struct pcmcia_device *link) |
62ed6662 | 830 | { |
c04edbf2 | 831 | return comedi_pcmcia_auto_config(link, &driver_daqp); |
62ed6662 BB |
832 | } |
833 | ||
2202a5a7 | 834 | static const struct pcmcia_device_id daqp_cs_id_table[] = { |
62ed6662 BB |
835 | PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0027), |
836 | PCMCIA_DEVICE_NULL | |
837 | }; | |
62ed6662 BB |
838 | MODULE_DEVICE_TABLE(pcmcia, daqp_cs_id_table); |
839 | ||
e3752a1d | 840 | static struct pcmcia_driver daqp_cs_driver = { |
433a0e22 HS |
841 | .name = "quatech_daqp_cs", |
842 | .owner = THIS_MODULE, | |
843 | .id_table = daqp_cs_id_table, | |
844 | .probe = daqp_cs_attach, | |
c04edbf2 | 845 | .remove = comedi_pcmcia_auto_unconfig, |
433a0e22 HS |
846 | .suspend = daqp_cs_suspend, |
847 | .resume = daqp_cs_resume, | |
62ed6662 | 848 | }; |
f3493a97 | 849 | module_comedi_pcmcia_driver(driver_daqp, daqp_cs_driver); |
04c59041 HS |
850 | |
851 | MODULE_DESCRIPTION("Comedi driver for Quatech DAQP PCMCIA data capture cards"); | |
852 | MODULE_AUTHOR("Brent Baccala <baccala@freesoft.org>"); | |
853 | MODULE_LICENSE("GPL"); |