1
by Steffen Joeris
Import upstream version 1.12 |
1 |
/*
|
1.1.1
by Bhavani Shankar
Import upstream version 2.0 |
2 |
* @(#)$Id: comedi.c,v 2.4 2009/01/17 06:44:55 baccala Exp $
|
1
by Steffen Joeris
Import upstream version 1.12 |
3 |
*
|
4 |
* Author: Brent Baccala <baccala@freesoft.org>
|
|
5 |
*
|
|
6 |
* Public domain.
|
|
7 |
*
|
|
8 |
* This file implements the COMEDI interface for xoscope
|
|
9 |
*
|
|
10 |
* The capturing occurs in what a normal oscilloscope would call "chop
|
|
11 |
* mode" - samples are alternated between the various channels being
|
|
12 |
* captured. This has the effect of reducing the overall sampling
|
|
13 |
* rate by a factor equal to the number of channels being captured.
|
|
14 |
* You could also (but we don't) implement an "alt mode" - an entire
|
|
15 |
* sweep is taken from one channel, then the entire next sweep is
|
|
16 |
* taken from the next channel, etc. Triggering would be a problem.
|
|
17 |
*
|
|
18 |
*/
|
|
19 |
||
20 |
#include <stdio.h> |
|
21 |
#include <stdlib.h> |
|
22 |
#include <unistd.h> |
|
23 |
#include <string.h> |
|
24 |
#include <ctype.h> |
|
25 |
#include <fcntl.h> |
|
26 |
#include <errno.h> |
|
27 |
#include <sys/ioctl.h> |
|
28 |
#include <sys/time.h> |
|
29 |
#include <sys/poll.h> |
|
30 |
#include <asm/page.h> |
|
31 |
#include <comedilib.h> |
|
32 |
#include "oscope.h" /* program defaults */ |
|
33 |
#include "func.h" |
|
34 |
||
35 |
#define COMEDI_RANGE 0 /* XXX user should set this */ |
|
36 |
||
37 |
/* Some of these variables aren't defined static because we need to
|
|
38 |
* get at them from our GTK dialog callbacks. None of them are set
|
|
39 |
* from there, that's done via set_option(), but there are read, and
|
|
40 |
* for that reason need to be global.
|
|
41 |
*/
|
|
42 |
||
43 |
comedi_t *comedi_dev = NULL; |
|
1.1.1
by Bhavani Shankar
Import upstream version 2.0 |
44 |
gchar *comedi_board_name = NULL; |
1
by Steffen Joeris
Import upstream version 1.12 |
45 |
|
46 |
static int comedi_opened = 0; /* t if open has at least been _attempted_ */ |
|
47 |
static int comedi_running = 0; |
|
48 |
static int comedi_error = 0; |
|
49 |
||
50 |
/* device_name[] is an array we write the COMEDI device into if it is
|
|
51 |
* set by an option (then point device at device_name). If there's
|
|
52 |
* no option, device stays pointing to the default - /dev/comedi0
|
|
53 |
*/
|
|
54 |
||
55 |
static char device_name[256]; |
|
56 |
char *comedi_devname = "/dev/comedi0"; |
|
57 |
||
58 |
int comedi_subdevice = 0; |
|
59 |
||
60 |
int comedi_rate = 50000; /* XXX set this to max valid upon open */ |
|
61 |
||
62 |
/* Size of COMEDI's kernel buffer. -1 means leave it at the default.
|
|
63 |
* Slight bug - if you change it, then change it back to -1, the
|
|
64 |
* default setting won't return until you close and re-open the
|
|
65 |
* device. Actually, that might not be good enough - you might
|
|
66 |
* need to re-configure the device.
|
|
67 |
*/
|
|
68 |
||
69 |
int comedi_bufsize = -1; |
|
70 |
||
71 |
#define BUFSZ 1024
|
|
72 |
static sampl_t buf[BUFSZ]; |
|
73 |
static int bufvalid=0; |
|
74 |
||
75 |
// has to be set later
|
|
76 |
int zero_value = -1; |
|
77 |
||
1.1.1
by Bhavani Shankar
Import upstream version 2.0 |
78 |
static int lag = 0; /* lag - see get_data() */ |
79 |
||
1
by Steffen Joeris
Import upstream version 1.12 |
80 |
static int subdevice_flags = 0; |
81 |
static int subdevice_type = COMEDI_SUBD_UNUSED; |
|
82 |
||
83 |
int comedi_aref = AREF_GROUND; /* Global voltage reference setting */ |
|
84 |
||
85 |
/* This structure associates xoscope Signal structures with the COMEDI
|
|
86 |
* channel they are receiving on. capture_list gets emptied by
|
|
87 |
* close_comedi(), added to by enable_chan(), deleted from by
|
|
88 |
* disable_chan(), used by start_comedi_running() to build a channel
|
|
89 |
* list, and used by get_data() to figure out in which Signal(s) to
|
|
90 |
* put the data.
|
|
91 |
*/
|
|
92 |
||
93 |
#define NCHANS 8
|
|
94 |
||
95 |
struct capture { |
|
96 |
struct capture * next; |
|
97 |
int chan; |
|
98 |
Signal * signal; |
|
99 |
};
|
|
100 |
||
101 |
static struct capture *capture_list = NULL; |
|
102 |
||
103 |
static int active_channels=0; |
|
104 |
||
105 |
static Signal comedi_chans[NCHANS]; |
|
106 |
||
107 |
/* Triggering information - the (one) channel we're triggering on, the
|
|
108 |
* sample level, the type of trigger (0 - freerun, 1 - ascending,
|
|
109 |
* 2 - descending), and the index of the trigger channel in the
|
|
110 |
* capture list
|
|
111 |
*/
|
|
112 |
||
113 |
static int trig_chan = 0; |
|
114 |
static int trig_level = 0; |
|
115 |
static int trig_mode = 0; |
|
116 |
static int trig_index = -1; |
|
117 |
||
118 |
/* This function is defined as do-nothing and weak, meaning it can be
|
|
119 |
* overridden by the linker without error. It's used to start the X
|
|
120 |
* Windows GTK options dialog for COMEDI, and is defined in this way
|
|
121 |
* so that this object file can be used either with or without GTK.
|
|
122 |
* If this causes compiler problems, just comment out the attribute
|
|
123 |
* line and leave the do-nothing function. You will then need to
|
|
124 |
* comment out both lines to generate an object file that can be used
|
|
125 |
* with GTK.
|
|
126 |
*/
|
|
127 |
||
128 |
void comedi_gtk_options() __attribute__ ((weak)); |
|
129 |
void comedi_gtk_options() {} |
|
130 |
||
131 |
/* This function gets called at various points that we need to stop
|
|
132 |
* and restart COMEDI, like a rate change, or a change to the list
|
|
133 |
* of channels we're capturing for. start_comedi_running() gets
|
|
134 |
* called automatically by get_data(), so we can use stop_comedi_running()
|
|
135 |
* pretty liberally.
|
|
136 |
*/
|
|
137 |
||
138 |
static void stop_comedi_running(void) |
|
139 |
{
|
|
140 |
if (comedi_running) { |
|
141 |
comedi_cancel(comedi_dev, 0); |
|
142 |
comedi_running = 0; |
|
143 |
}
|
|
144 |
bufvalid = 0; |
|
145 |
}
|
|
146 |
||
147 |
/* XXX This function should make sure the Signal arrays are reset to sane
|
|
148 |
* values. Right now, it just sets their volt and rate values.
|
|
149 |
*/
|
|
150 |
||
151 |
static int start_comedi_running(void) |
|
152 |
{
|
|
153 |
int try; |
|
154 |
int ret = -1; |
|
155 |
comedi_cmd cmd; |
|
156 |
unsigned int chanlist[NCHANS]; |
|
157 |
struct capture *capture; |
|
158 |
comedi_range *comedi_rng; |
|
159 |
int maxdata; |
|
160 |
||
161 |
if (!comedi_dev) return 0; |
|
162 |
||
163 |
/* There might have been an error condition that was cleared (like
|
|
164 |
* switching from an unsupported subdevice to a supported one), so
|
|
165 |
* clear comedi_error here and set it if there's a problem later.
|
|
166 |
* If we're not capturing anything, make sure we set subdevice_type
|
|
167 |
* before we return, because nchans() depends on this variable to
|
|
168 |
* figure out how to interpret comedi_get_n_channels()
|
|
169 |
*/
|
|
170 |
||
171 |
comedi_error = 0; |
|
172 |
||
173 |
subdevice_flags = comedi_get_subdevice_flags(comedi_dev, comedi_subdevice); |
|
174 |
subdevice_type = comedi_get_subdevice_type(comedi_dev, comedi_subdevice); |
|
175 |
||
176 |
if (active_channels == 0) return 0; |
|
177 |
||
178 |
if (comedi_bufsize > 0) { |
|
179 |
/* comedi 0.7.66 has a bug in its buffer size handling. Not only
|
|
180 |
* does it fail to round up to a multiple of PAGE_SIZE correctly,
|
|
181 |
* but if you attempt to set a buffer smaller than PAGE_SIZE, it
|
|
182 |
* will deallocate the buffer and you'll never get it back without
|
|
183 |
* re-configuring the device. We round up to PAGE_SIZE here to
|
|
184 |
* avoid the bug. This is the only reason we need <asm/page.h> in
|
|
185 |
* our include list.
|
|
186 |
*/
|
|
187 |
comedi_bufsize = (comedi_bufsize + PAGE_SIZE - 1) & PAGE_MASK; |
|
188 |
ret = comedi_set_buffer_size(comedi_dev, comedi_subdevice, comedi_bufsize); |
|
189 |
if (ret < 0) { |
|
190 |
comedi_error = comedi_errno(); |
|
191 |
return ret; |
|
192 |
}
|
|
193 |
}
|
|
194 |
||
195 |
/* Now we build a COMEDI command structure */
|
|
196 |
||
197 |
bzero(&cmd, sizeof(cmd)); |
|
198 |
cmd.subdev = comedi_subdevice; |
|
199 |
||
200 |
/* Start with a channel list based on capture_list
|
|
201 |
*
|
|
202 |
* This code matches up with get_data(), which assumes that the captured
|
|
203 |
* data is in the same order as the channels in the capture_list
|
|
204 |
*/
|
|
205 |
||
206 |
cmd.chanlist = chanlist; |
|
207 |
cmd.chanlist_len = 0; |
|
208 |
||
209 |
for (capture = capture_list; capture != NULL; capture = capture->next) { |
|
210 |
chanlist[cmd.chanlist_len++] = CR_PACK(capture->chan,0,comedi_aref); |
|
211 |
}
|
|
212 |
||
213 |
if (cmd.chanlist_len == 0) { |
|
214 |
return 0; |
|
215 |
}
|
|
216 |
||
217 |
/* comedilib has a comedi_get_cmd_generic_timed() function, but it's
|
|
218 |
* set up for sampling a single channel, so I don't use it.
|
|
219 |
* Instead, I try several different varients on comedi command
|
|
220 |
* structures in the hopes of finding one that works.
|
|
221 |
*/
|
|
222 |
||
223 |
try = 0; |
|
224 |
do { |
|
225 |
||
226 |
switch (try) { |
|
227 |
||
228 |
/* The first thing we try is to simultaneously sample (that's
|
|
229 |
* the convert_src of TRIG_NOW) all the channels at the
|
|
230 |
* requested rate.
|
|
231 |
*/
|
|
232 |
||
233 |
case 0: |
|
234 |
cmd.start_src = TRIG_NOW; |
|
235 |
cmd.start_arg = 0; |
|
236 |
||
237 |
cmd.scan_begin_src = TRIG_TIMER; |
|
238 |
cmd.scan_begin_arg = 1e9 / comedi_rate; |
|
239 |
||
240 |
cmd.convert_src = TRIG_NOW; |
|
241 |
cmd.convert_arg = 0; |
|
242 |
||
243 |
cmd.scan_end_src = TRIG_COUNT; |
|
244 |
cmd.scan_end_arg = cmd.chanlist_len; |
|
245 |
||
246 |
cmd.stop_src = TRIG_NONE; |
|
247 |
cmd.stop_arg = 0; |
|
248 |
||
249 |
break; |
|
250 |
||
251 |
/* There's a good chance that won't work (not many cards support it).
|
|
252 |
* So now try sampling each channel at a staggered interval of
|
|
253 |
* the requested rate times the number of channels.
|
|
254 |
*/
|
|
255 |
||
256 |
case 1: |
|
257 |
cmd.start_src = TRIG_NOW; |
|
258 |
cmd.start_arg = 0; |
|
259 |
||
260 |
cmd.scan_begin_src = TRIG_FOLLOW; |
|
261 |
cmd.scan_begin_arg = 0; |
|
262 |
||
263 |
cmd.convert_src = TRIG_TIMER; |
|
264 |
cmd.convert_arg = 1e9 / (comedi_rate * active_channels); |
|
265 |
||
266 |
cmd.scan_end_src = TRIG_COUNT; |
|
267 |
cmd.scan_end_arg = cmd.chanlist_len; |
|
268 |
||
269 |
cmd.stop_src = TRIG_NONE; |
|
270 |
cmd.stop_arg = 0; |
|
271 |
||
272 |
break; |
|
273 |
||
274 |
/* OK, that didn't work. Maybe the card wants timers on
|
|
275 |
* both the scan and conversion?
|
|
276 |
*/
|
|
277 |
||
278 |
case 2: |
|
279 |
cmd.start_src = TRIG_NOW; |
|
280 |
cmd.start_arg = 0; |
|
281 |
||
282 |
cmd.scan_begin_src = TRIG_TIMER; |
|
283 |
cmd.scan_begin_arg = 1e9 / comedi_rate; |
|
284 |
||
285 |
cmd.convert_src = TRIG_TIMER; |
|
286 |
cmd.convert_arg = 1e9 / (comedi_rate * active_channels); |
|
287 |
||
288 |
cmd.scan_end_src = TRIG_COUNT; |
|
289 |
cmd.scan_end_arg = cmd.chanlist_len; |
|
290 |
||
291 |
cmd.stop_src = TRIG_NONE; |
|
292 |
cmd.stop_arg = 0; |
|
293 |
||
294 |
break; |
|
295 |
||
296 |
/* Nothing we tried worked! There are other possibilities, but
|
|
297 |
* none are currently supported by this code. Complain and
|
|
298 |
* return the error code from the last thing we tried.
|
|
299 |
*/
|
|
300 |
||
301 |
default: |
|
302 |
||
303 |
comedi_error = comedi_errno(); |
|
304 |
return ret; |
|
305 |
||
306 |
}
|
|
307 |
||
308 |
/* COMEDI command testing can be a little funky. We get a return
|
|
309 |
* code indicating which phase of test failed. Basically, if
|
|
310 |
* phase 1 or 2 failed, we're screwed. If phase 3 failed, it
|
|
311 |
* might be because we've pushed the limits of the timing past
|
|
312 |
* where it can go, and if phase 4 failed, it's just because the
|
|
313 |
* device can't support exactly the timings we asked for. In
|
|
314 |
* either of the last two cases, the driver adjusts the offending
|
|
315 |
* parameters. That's why we call this function three times.
|
|
316 |
*/
|
|
317 |
||
318 |
ret = comedi_command_test(comedi_dev,&cmd); |
|
319 |
ret = comedi_command_test(comedi_dev,&cmd); |
|
320 |
ret = comedi_command_test(comedi_dev,&cmd); |
|
321 |
||
322 |
try ++; |
|
323 |
||
324 |
} while (ret != 0); |
|
325 |
||
326 |
/* Now we adjust our global rate to whatever we got the card to do. */
|
|
327 |
||
328 |
if (cmd.scan_begin_src == TRIG_TIMER) { |
|
329 |
comedi_rate = 1e9 / cmd.scan_begin_arg; |
|
330 |
} else if (cmd.convert_src == TRIG_TIMER) { |
|
331 |
comedi_rate = 1e9 / cmd.convert_arg; |
|
332 |
comedi_rate /= active_channels; |
|
333 |
} else { |
|
334 |
fprintf(stderr, "neither convert_src nor start_src is TRIG_TIMER!?!\n"); |
|
335 |
}
|
|
336 |
||
337 |
/* Voltage range is currently a global setting. Find it, and save
|
|
338 |
* it into all the Signal(s) we're collecting data into (if we're
|
|
339 |
* capturing an analog input subdevice; digital subdevs don't do
|
|
340 |
* this). Signal->volts should be in milivolts per 320 sample
|
|
341 |
* values, so take the voltage range given by COMEDI, multiply by
|
|
342 |
* 1000 (volts -> millivolts), divide by 2^(sampl_t bits) (sample
|
|
343 |
* values in an sampl_t), to get millivolts per sample value, and
|
|
344 |
* multiply by 320 to get millivolts per 320 sample values. 320 is
|
|
345 |
* the size of the vertical display area, in case you wondered.
|
|
346 |
*
|
|
347 |
* Also, set the rate (samples/sec) at which we'll capture data
|
|
348 |
*/
|
|
349 |
||
350 |
for (capture = capture_list; capture != NULL; capture = capture->next) { |
|
351 |
||
352 |
if (subdevice_type == COMEDI_SUBD_AI) { |
|
353 |
||
354 |
comedi_rng = comedi_get_range(comedi_dev, |
|
355 |
comedi_subdevice, |
|
356 |
capture->chan, COMEDI_RANGE); |
|
357 |
maxdata=comedi_get_maxdata(comedi_dev, |
|
358 |
comedi_subdevice, |
|
359 |
0); |
|
360 |
capture->signal->volts |
|
361 |
= (comedi_rng->max - comedi_rng->min) |
|
362 |
* 1000 * 320 / maxdata; |
|
363 |
||
364 |
if (zero_value<0) { |
|
365 |
// we have to set zero value
|
|
366 |
if ((comedi_rng->min<0)&&(comedi_rng->max>0)) { |
|
367 |
// we are bipolar
|
|
368 |
zero_value=maxdata/2; |
|
369 |
} else { |
|
370 |
// we are unipolar
|
|
371 |
zero_value=0; |
|
372 |
}
|
|
373 |
}
|
|
374 |
||
375 |
capture->signal->bits = 0; |
|
376 |
||
377 |
#if 0
|
|
378 |
printf(" [%g,%g] %s\n",comedi_rng->min,comedi_rng->max,
|
|
379 |
comedi_rng->unit == UNIT_volt ? "V" : "");
|
|
380 |
#endif
|
|
381 |
||
382 |
} else { |
|
383 |
||
384 |
capture->signal->bits = comedi_get_n_channels(comedi_dev, comedi_subdevice); |
|
385 |
||
386 |
capture->signal->volts = 0; |
|
387 |
||
388 |
}
|
|
389 |
||
390 |
capture->signal->rate = comedi_rate; |
|
391 |
}
|
|
392 |
||
393 |
#if 0
|
|
394 |
fprintf(stderr, "Sampling every %d(%d) ns(Hz)\n",
|
|
395 |
cmd.convert_arg, comedi_rate);
|
|
396 |
#endif
|
|
397 |
||
398 |
ret = comedi_command(comedi_dev,&cmd); |
|
399 |
if (ret >= 0) { |
|
400 |
fcntl(comedi_fileno(comedi_dev), F_SETFL, O_NONBLOCK); |
|
401 |
comedi_running = 1; |
|
402 |
} else { |
|
403 |
comedi_error = comedi_errno(); |
|
404 |
}
|
|
405 |
return ret; |
|
406 |
}
|
|
407 |
||
408 |
static void |
|
409 |
close_comedi() |
|
410 |
{
|
|
411 |
#if 0
|
|
412 |
struct capture *capture;
|
|
413 |
#endif
|
|
414 |
||
415 |
if (comedi_dev) comedi_close(comedi_dev); |
|
416 |
comedi_dev = NULL; |
|
1.1.1
by Bhavani Shankar
Import upstream version 2.0 |
417 |
if (comedi_board_name) g_free(comedi_board_name); |
418 |
comedi_board_name = NULL; |
|
1
by Steffen Joeris
Import upstream version 1.12 |
419 |
comedi_running = 0; |
420 |
comedi_opened = 0; |
|
421 |
||
422 |
/* Leave active channels alone here in case we're closing
|
|
423 |
* a device because of an error and want to re-open later.
|
|
424 |
*/
|
|
425 |
||
426 |
#if 0
|
|
427 |
while (capture_list != NULL) {
|
|
428 |
capture = capture_list->next;
|
|
429 |
free(capture_list);
|
|
430 |
capture_list = capture;
|
|
431 |
}
|
|
432 |
||
433 |
active_channels = 0;
|
|
434 |
#endif
|
|
435 |
}
|
|
436 |
||
437 |
static int |
|
438 |
open_comedi(void) |
|
439 |
{
|
|
440 |
int i; |
|
441 |
static int once=0; |
|
442 |
||
443 |
close_comedi(); |
|
444 |
comedi_error = 0; |
|
445 |
comedi_opened = 1; |
|
446 |
subdevice_flags = 0; |
|
447 |
subdevice_type = COMEDI_SUBD_UNUSED; |
|
448 |
||
449 |
if (!once) { |
|
450 |
||
451 |
/* XXX once is a kludge */
|
|
452 |
||
453 |
/* Setup the Signal structures. Note that the name is set to 'a',
|
|
454 |
* 'b', 'c', etc, to conform with xoscope usage and to avoid
|
|
455 |
* confusing the user with the display channels. COMEDI, of
|
|
456 |
* course, numbers its channels 0, 1, 2, etc
|
|
457 |
*/
|
|
458 |
||
459 |
for (i = 0 ; i < NCHANS ; i++) { /* XXX hardwired at 8 */ |
|
1.1.1
by Bhavani Shankar
Import upstream version 2.0 |
460 |
comedi_chans[i].data = NULL; |
1
by Steffen Joeris
Import upstream version 1.12 |
461 |
comedi_chans[i].num = comedi_chans[i].frame = comedi_chans[i].volts = 0; |
462 |
comedi_chans[i].listeners = 0; |
|
463 |
//sprintf(comedi_chans[i].name, "Channel %d", i);
|
|
464 |
sprintf(comedi_chans[i].name, "Channel %c", 'a' + i); |
|
465 |
comedi_chans[i].savestr[0] = 'a' + i; |
|
466 |
comedi_chans[i].savestr[1] = '\0'; |
|
467 |
}
|
|
468 |
||
469 |
once = 1; |
|
470 |
}
|
|
471 |
||
472 |
comedi_dev = comedi_open(comedi_devname); |
|
473 |
||
474 |
if (! comedi_dev) { |
|
475 |
comedi_error = comedi_errno(); |
|
476 |
return 0; |
|
477 |
}
|
|
478 |
||
1.1.1
by Bhavani Shankar
Import upstream version 2.0 |
479 |
/* All the GTK stuff uses UTF8, and complains to stderr if it
|
480 |
* doesn't get it.
|
|
481 |
*/
|
|
482 |
||
483 |
comedi_board_name = g_locale_to_utf8(comedi_get_board_name(comedi_dev), |
|
484 |
-1, NULL, NULL, NULL); |
|
485 |
||
1
by Steffen Joeris
Import upstream version 1.12 |
486 |
/* XXX I'd kinda like to do this here, but then the read() loop
|
487 |
* below (to get offset correction for the DAQP) returns errors
|
|
488 |
* (-EAGAIN). If do this later, and start_comedi_running() has
|
|
489 |
* problems, then it might hang in get_data(), but I hope I've
|
|
490 |
* fixed that now...
|
|
491 |
*/
|
|
492 |
/* fcntl(comedi_fileno(comedi_dev), F_SETFL, O_NONBLOCK); */
|
|
493 |
||
1.1.1
by Bhavani Shankar
Import upstream version 2.0 |
494 |
if (comedi_board_name && strncmp(comedi_board_name, "DAQP", 4) == 0) { |
1
by Steffen Joeris
Import upstream version 1.12 |
495 |
|
496 |
/* Special case for DAQP - unfortunately, COMEDI doesn't (yet) provide
|
|
497 |
* a generic interface for boards that can do offset correction,
|
|
498 |
* so this special case is designed to handle the Quatech DAQP.
|
|
499 |
* We collect a hundred samples from channel 4 in differential
|
|
500 |
* mode, a non-existant channel used by the DAQP specifically for
|
|
501 |
* offset correction.
|
|
502 |
*/
|
|
503 |
||
504 |
comedi_cmd cmd; |
|
1.1.1
by Bhavani Shankar
Import upstream version 2.0 |
505 |
unsigned int chan; |
1
by Steffen Joeris
Import upstream version 1.12 |
506 |
int ret; |
507 |
||
508 |
ret = comedi_get_cmd_generic_timed(comedi_dev, comedi_subdevice, &cmd, 0); |
|
509 |
||
510 |
if (ret >= 0) { |
|
511 |
chan = CR_PACK(4,0,AREF_DIFF); |
|
512 |
cmd.chanlist = &chan; |
|
513 |
cmd.chanlist_len = 1; |
|
514 |
cmd.start_src = TRIG_NOW; |
|
515 |
cmd.start_arg = 0; |
|
516 |
cmd.stop_src = TRIG_COUNT; |
|
517 |
cmd.stop_arg = 100; |
|
518 |
||
519 |
ret = comedi_command_test(comedi_dev, &cmd); |
|
520 |
||
521 |
if (ret >= 0) { |
|
522 |
ret = comedi_command(comedi_dev, &cmd); |
|
523 |
if (ret >= 0) { |
|
524 |
int i = 0; |
|
525 |
while ((i < (100 * sizeof(sampl_t))) |
|
526 |
&& (ret = read(comedi_fileno(comedi_dev), buf, |
|
527 |
100 * sizeof(sampl_t) - i)) > 0) { |
|
528 |
i += ret; |
|
529 |
}
|
|
530 |
if (i == (100 * sizeof(sampl_t))) { |
|
531 |
zero_value = 0; |
|
532 |
for (i=0; i<100; i++) zero_value += buf[i]; |
|
533 |
zero_value /= 100; |
|
534 |
}
|
|
535 |
}
|
|
536 |
}
|
|
537 |
}
|
|
538 |
||
539 |
if (ret == -1) { |
|
540 |
comedi_error = comedi_errno(); |
|
541 |
/* close_comedi(); */
|
|
542 |
return 0; |
|
543 |
}
|
|
544 |
||
545 |
comedi_cancel(comedi_dev, 0); |
|
546 |
}
|
|
547 |
||
548 |
/* XXX Why can't we do this here? It doesn't seem to "take" */
|
|
549 |
/* fcntl(comedi_fileno(comedi_dev), F_SETFL, O_NONBLOCK); */
|
|
550 |
||
551 |
if (start_comedi_running() < 0) { |
|
552 |
return 0; |
|
553 |
} else { |
|
554 |
return 1; |
|
555 |
}
|
|
556 |
||
557 |
}
|
|
558 |
||
559 |
void
|
|
560 |
reset_comedi(void) |
|
561 |
{
|
|
562 |
if (comedi_dev == NULL) { |
|
563 |
open_comedi(); |
|
564 |
if (comedi_dev == NULL) { |
|
565 |
return; |
|
566 |
}
|
|
567 |
}
|
|
568 |
||
569 |
stop_comedi_running(); |
|
570 |
}
|
|
571 |
||
572 |
static int nchans(void) |
|
573 |
{
|
|
574 |
int chans; |
|
575 |
int i; |
|
576 |
||
577 |
if (! comedi_opened) open_comedi(); |
|
578 |
||
579 |
/* open_comedi() calls start_comedi_running() just before it
|
|
580 |
* returns, so that's how we know subdevice_type has been set
|
|
581 |
* correctly when we get here. However, I really don't know if
|
|
582 |
* open_comedi() SHOULD call start_comedi_running() at all, so we
|
|
583 |
* may need to revisit this.
|
|
584 |
*/
|
|
585 |
||
586 |
if (comedi_dev == NULL) { |
|
587 |
return 0; |
|
588 |
} else if (subdevice_type == COMEDI_SUBD_AI) { |
|
589 |
/* analog subdevice - mark all channels analog and return num of chans */
|
|
590 |
chans = comedi_get_n_channels(comedi_dev, comedi_subdevice); |
|
591 |
for (i = 0; i < chans; i ++) comedi_chans[i].bits = 0; |
|
592 |
return chans; |
|
593 |
} else { |
|
594 |
/* digital subdevice - n_channels returns number of bits */
|
|
595 |
comedi_chans[0].bits = comedi_get_n_channels(comedi_dev, comedi_subdevice); |
|
596 |
return 1; |
|
597 |
}
|
|
598 |
}
|
|
599 |
||
600 |
static int fd(void) |
|
601 |
{
|
|
602 |
return (comedi_running ? comedi_fileno(comedi_dev) : -1); |
|
603 |
}
|
|
604 |
||
605 |
/* reset() - part of the data source API. Called when we're ready to
|
|
606 |
* start capturing. Clears the old capture_list and builds a new one.
|
|
607 |
* capture_ptr is used to make sure we build the list from the top
|
|
608 |
* down, not the bottom up, mainly to make sure trig_index counts from
|
|
609 |
* the top down. Finally, we start COMEDI. We don't really need to
|
|
610 |
* start COMEDI, just prep it, but we start it in order to set the
|
|
611 |
* rate and volts fields (during start_comedi_running) in the Signal
|
|
612 |
* structures.
|
|
613 |
*/
|
|
614 |
||
615 |
static void |
|
616 |
reset(void) |
|
617 |
{
|
|
618 |
struct capture *capture; |
|
619 |
struct capture **capture_ptr; |
|
620 |
int i; |
|
621 |
||
622 |
stop_comedi_running(); |
|
623 |
||
624 |
for (capture = capture_list; capture != NULL; capture = capture_list) { |
|
625 |
capture_list = capture->next; |
|
626 |
free(capture); |
|
627 |
}
|
|
628 |
||
629 |
capture_list = NULL; |
|
630 |
active_channels = 0; |
|
631 |
trig_index = -1; |
|
632 |
capture_ptr = &capture_list; |
|
633 |
||
634 |
for (i = 0; i < NCHANS; i++) { |
|
635 |
if ((comedi_chans[i].listeners) || ((trig_mode > 0) && (trig_chan == i))) { |
|
636 |
||
637 |
capture = malloc(sizeof(struct capture)); |
|
638 |
if (capture == NULL) { |
|
639 |
perror("enable_chan() malloc failed"); |
|
640 |
exit(1); |
|
641 |
}
|
|
642 |
||
643 |
capture->chan = i; |
|
644 |
capture->signal = &comedi_chans[i]; |
|
645 |
capture->next = NULL; |
|
646 |
*capture_ptr = capture; |
|
647 |
capture_ptr = &capture->next; |
|
648 |
||
649 |
comedi_chans[i].num = 0; |
|
650 |
comedi_chans[i].frame ++; |
|
651 |
||
652 |
if ((trig_mode > 0) && (trig_chan == i)) trig_index = active_channels; |
|
653 |
||
654 |
active_channels ++; |
|
655 |
}
|
|
656 |
}
|
|
657 |
||
658 |
start_comedi_running(); |
|
659 |
}
|
|
660 |
||
661 |
static Signal * comedi_chan(int chan) |
|
662 |
{
|
|
663 |
return &comedi_chans[chan]; |
|
664 |
}
|
|
665 |
||
666 |
static int set_trigger(int chan, int *levelp, int mode) |
|
667 |
{
|
|
668 |
trig_chan = chan; |
|
669 |
trig_level = *levelp; |
|
670 |
trig_mode = mode; |
|
671 |
/* XXX check that trig_level is within subdevice's range */
|
|
672 |
return 1; |
|
673 |
}
|
|
674 |
||
675 |
static void clear_trigger(void) |
|
676 |
{
|
|
677 |
trig_mode = 0; |
|
678 |
}
|
|
679 |
||
680 |
/* Current COMEDI rate logic has some bizarre effects. As we increase
|
|
681 |
* the number of channels sampled, the rate goes down (usually), but
|
|
682 |
* doesn't go back up when we decrease the number of sampled chans.
|
|
683 |
*/
|
|
684 |
||
685 |
/* XXX the rate we calculate might not be the one that actually gets used */
|
|
686 |
||
687 |
static int change_rate(int dir) |
|
688 |
{
|
|
689 |
int oldrate = comedi_rate; |
|
690 |
||
691 |
if (dir == 1) { |
|
692 |
comedi_rate *= 2; |
|
693 |
} else { |
|
694 |
comedi_rate /= 2; |
|
695 |
}
|
|
696 |
||
697 |
stop_comedi_running(); |
|
698 |
||
699 |
return (comedi_rate != oldrate); |
|
700 |
}
|
|
701 |
||
702 |
/* set_width(int)
|
|
703 |
*
|
|
704 |
* sets the frame width (number of samples captured per sweep) globally
|
|
705 |
* for all the channels.
|
|
706 |
*/
|
|
707 |
||
708 |
static void set_width(int width) |
|
709 |
{
|
|
710 |
int i; |
|
711 |
||
712 |
for (i=0; i<NCHANS; i++) { |
|
713 |
comedi_chans[i].width = width; |
|
1.1.1
by Bhavani Shankar
Import upstream version 2.0 |
714 |
if (comedi_chans[i].data != NULL) free(comedi_chans[i].data); |
715 |
comedi_chans[i].data = malloc(width * sizeof(short)); |
|
1
by Steffen Joeris
Import upstream version 1.12 |
716 |
}
|
717 |
}
|
|
718 |
||
719 |
/* get_data() -
|
|
720 |
* read all available data from comedi device, return value is TRUE if we
|
|
721 |
* actually put some samples into the sweep buffer (and thus need a redisplay)
|
|
722 |
*
|
|
723 |
* This function should always return at the end of a sweep
|
|
724 |
*/
|
|
725 |
||
726 |
#define convert(sample) (sample - zero_value)
|
|
727 |
||
728 |
static int get_data(void) |
|
729 |
{
|
|
730 |
int bytes_read; |
|
731 |
int samples_read; |
|
732 |
int scans_read; |
|
733 |
int samples_per_frame; |
|
734 |
sampl_t *current_scan, *last_scan; |
|
735 |
int i, j; |
|
736 |
int delay; |
|
737 |
int triggered=0; |
|
738 |
int was_in_sweep=in_progress; |
|
739 |
static struct timeval tv1, tv2; |
|
740 |
struct capture *capture; |
|
741 |
||
742 |
/* This code used to try and start COMEDI running if it wasn't running
|
|
743 |
* already. But if fd() already returned -1, the main code doesn't
|
|
744 |
* think we're running, so it's best to leave things alone here...
|
|
745 |
*/
|
|
746 |
||
747 |
if (! comedi_dev || ! comedi_running) return 0; |
|
748 |
||
749 |
/* The way the code's written right now, all the channels are
|
|
750 |
* sampled at the same rate and for the same width (number of
|
|
751 |
* samples per frame), so we just use the width from the first
|
|
752 |
* channel in the capture list to figure how many samples we're
|
|
753 |
* capturing.
|
|
754 |
*/
|
|
755 |
||
756 |
samples_per_frame = capture_list->signal->width; |
|
757 |
||
758 |
/* It is possible for this loop to be entered with a full buffer of
|
|
759 |
* data already (bufvalid == sizeof(buf)). In that case, the read will
|
|
760 |
* be called with a zero byte buffer size, and will return zero.
|
|
761 |
* That's why the comparison reads ">=0" and not ">0"
|
|
762 |
*/
|
|
763 |
while ((bytes_read = read(comedi_fileno(comedi_dev), |
|
764 |
((char *)buf) + bufvalid, sizeof(buf) - bufvalid)) |
|
765 |
>= 0) { |
|
766 |
||
767 |
// fprintf(stderr, "bytes_read=%d; bufvalid=%d\n", bytes_read, bufvalid);
|
|
768 |
||
769 |
bytes_read += bufvalid; |
|
770 |
||
771 |
gettimeofday(&tv1, NULL); |
|
772 |
samples_read = bytes_read / sizeof(sampl_t); |
|
773 |
scans_read = samples_read / active_channels; |
|
774 |
||
775 |
/* This is here to catch the case when there's nothing (or not
|
|
776 |
* much) in the buffer, and the read() call returned nothing.
|
|
777 |
*/
|
|
778 |
||
779 |
if (scans_read == 0 && bytes_read == 0) break; |
|
780 |
||
781 |
for (i = 0; i < scans_read; i++) { |
|
782 |
||
783 |
current_scan = buf + i * active_channels; |
|
784 |
||
785 |
if (!in_progress && scope.run && i>0) { |
|
786 |
||
787 |
/* Sweep isn't in_progress, so look for a trigger -
|
|
788 |
* anything (trig_mode==0) or a transition between the last
|
|
789 |
* sample and the current one that crossed the trig_level
|
|
790 |
* threshold, either going positive (trig_mode==1) or going
|
|
791 |
* negative (trig_mode==2). Since we check the previous sample,
|
|
792 |
* there's an "i>0" case in the above if statement, and that
|
|
793 |
* does mean that we'll miss a trigger if the transition
|
|
794 |
* exactly corresponds with a read buffer boundary.
|
|
795 |
*/
|
|
796 |
||
797 |
last_scan = buf + (i-1) * active_channels; |
|
798 |
||
799 |
if ((trig_mode == 0) || |
|
800 |
((trig_mode == 1) && |
|
801 |
(convert(current_scan[trig_index]) >= trig_level) && |
|
802 |
(convert(last_scan[trig_index]) < trig_level)) || |
|
803 |
((trig_mode == 2) && |
|
804 |
(convert(current_scan[trig_index]) <= trig_level) && |
|
805 |
(convert(last_scan[trig_index]) > trig_level))) { |
|
806 |
||
807 |
/* found something to trigger on, so compute a delay value
|
|
808 |
* based on extrapolating a straight line between the two
|
|
809 |
* sample values that straddle the triggering point, for
|
|
810 |
* high-frequency signals that change significantly between
|
|
811 |
* the two samples. Set up all the relevent Signal
|
|
812 |
* structures, then fall through into the triggered case
|
|
813 |
* below
|
|
814 |
*/
|
|
815 |
||
816 |
delay = 0; |
|
817 |
||
818 |
if (trig_mode != 0) { |
|
819 |
short current = convert(current_scan[trig_index]); |
|
820 |
short last = convert(last_scan[trig_index]); |
|
821 |
if (current != last) { |
|
822 |
delay = abs(10000 * (current - trig_level) / (current - last)); |
|
823 |
}
|
|
824 |
}
|
|
825 |
||
826 |
for (j=0, capture=capture_list; |
|
827 |
capture != NULL; capture=capture->next, j++) { |
|
828 |
capture->signal->frame ++; |
|
829 |
capture->signal->delay = delay; |
|
830 |
capture->signal->num = 0; |
|
831 |
}
|
|
832 |
if (j != active_channels) { |
|
833 |
fprintf(stderr, "ERROR! j != active_channels in get_data()\n"); |
|
834 |
}
|
|
835 |
||
836 |
in_progress = 1; |
|
837 |
||
838 |
}
|
|
839 |
}
|
|
840 |
||
841 |
if (in_progress) { |
|
842 |
||
843 |
/* Sweep in progress */
|
|
844 |
||
845 |
for (j=0, capture=capture_list; |
|
846 |
capture != NULL; capture=capture->next, j++) { |
|
847 |
capture->signal->data[capture->signal->num ++] |
|
848 |
= convert(current_scan[j]); |
|
849 |
in_progress = capture->signal->num; |
|
850 |
}
|
|
851 |
if (j != active_channels) { |
|
852 |
fprintf(stderr, "ERROR! j != active_channels in get_data()\n"); |
|
853 |
}
|
|
854 |
||
855 |
triggered = 1; |
|
856 |
||
857 |
if (in_progress >= samples_per_frame) { |
|
858 |
||
859 |
in_progress = 0; |
|
860 |
||
861 |
/* If we were in the middle of a sweep when we entered this function,
|
|
862 |
* return now. Otherwise, keep looking for more sweeps.
|
|
863 |
*/
|
|
864 |
||
865 |
if (was_in_sweep) { |
|
866 |
||
867 |
bufvalid = bytes_read - (i * active_channels * sizeof(sampl_t)); |
|
868 |
if (bufvalid) { |
|
869 |
memcpy(buf, (char *) buf + bytes_read - bufvalid, bufvalid); |
|
870 |
}
|
|
871 |
||
1.1.1
by Bhavani Shankar
Import upstream version 2.0 |
872 |
lag = 0; |
1
by Steffen Joeris
Import upstream version 1.12 |
873 |
return triggered; |
874 |
}
|
|
875 |
}
|
|
876 |
||
877 |
}
|
|
878 |
}
|
|
879 |
||
880 |
/* It would be nice if COMEDI never returned a partial scan
|
|
881 |
* to a read() call. Unfortunately, it often does, so we
|
|
882 |
* need to tuck the "extra" data away until the next time
|
|
883 |
* through this loop...
|
|
884 |
*/
|
|
885 |
||
886 |
bufvalid = bytes_read - (scans_read * active_channels * sizeof(sampl_t)); |
|
887 |
if (bufvalid) { |
|
888 |
memcpy(buf, (char *) buf + bytes_read - bufvalid, bufvalid); |
|
889 |
}
|
|
890 |
||
891 |
}
|
|
892 |
||
893 |
if ((bytes_read < 0) && (errno != EAGAIN)) { |
|
894 |
||
895 |
/* The most common cause of a COMEDI read error is a buffer
|
|
896 |
* overflow. There are all kinds of ways to do it, from hitting
|
|
897 |
* space to stop the scope trace to dragging a window while
|
|
898 |
* xoscope is running. In the later case, the window manager will
|
|
899 |
* do an X server grab, which will block xoscope the next time it
|
|
900 |
* tries to perform an X operation. Holding the mouse down longer
|
|
901 |
* than a split second will cause the COMEDI kernel buffer to
|
|
902 |
* overflow, which will trigger this code the next time through.
|
|
903 |
*
|
|
904 |
* comedi-0.7.60 returned EINVAL on buffer overflows;
|
|
905 |
* comedi-0.7.66 returns EPIPE
|
|
1.1.1
by Bhavani Shankar
Import upstream version 2.0 |
906 |
*
|
907 |
* In any event, if we detect an overflow, we reset the capture to
|
|
908 |
* start a new trace, record how many microseconds elapsed since
|
|
909 |
* the last time we were able to read the device, and report this
|
|
910 |
* on the screen as "lag".
|
|
1
by Steffen Joeris
Import upstream version 1.12 |
911 |
*/
|
912 |
||
913 |
if (errno != EINVAL && errno != EPIPE) perror("comedi read"); |
|
914 |
||
915 |
start_comedi_running(); |
|
916 |
bufvalid = 0; |
|
917 |
gettimeofday(&tv2, NULL); |
|
1.1.1
by Bhavani Shankar
Import upstream version 2.0 |
918 |
lag = 1000000*(tv2.tv_sec-tv1.tv_sec) + tv2.tv_usec - tv1.tv_usec; |
1
by Steffen Joeris
Import upstream version 1.12 |
919 |
return 0; |
920 |
}
|
|
921 |
||
1.1.1
by Bhavani Shankar
Import upstream version 2.0 |
922 |
lag = 0; |
1
by Steffen Joeris
Import upstream version 1.12 |
923 |
return triggered; |
924 |
}
|
|
925 |
||
926 |
||
927 |
static char * status_str(int i) |
|
928 |
{
|
|
929 |
static char buffer[16]; |
|
930 |
char *error = comedi_strerror(comedi_error); |
|
931 |
||
932 |
switch (i) { |
|
933 |
case 0: |
|
934 |
return comedi_devname; |
|
935 |
case 2: |
|
936 |
if (comedi_dev) { |
|
1.1.1
by Bhavani Shankar
Import upstream version 2.0 |
937 |
return comedi_board_name; |
1
by Steffen Joeris
Import upstream version 1.12 |
938 |
} else { |
939 |
return split_field(error, 0, 16); |
|
940 |
}
|
|
941 |
||
942 |
case 4: |
|
943 |
if (!comedi_dev) { |
|
944 |
return split_field(error, 1, 16); |
|
945 |
} else { |
|
946 |
return ""; |
|
947 |
}
|
|
948 |
||
949 |
case 1: |
|
950 |
if (comedi_dev) { |
|
951 |
sprintf(buffer, "Subdevice %d", comedi_subdevice); |
|
952 |
return buffer; |
|
953 |
} else { |
|
954 |
return ""; |
|
955 |
}
|
|
956 |
case 3: |
|
957 |
if (comedi_dev && comedi_error) { |
|
958 |
return split_field(error, 0, 16); |
|
1.1.1
by Bhavani Shankar
Import upstream version 2.0 |
959 |
} else if (lag > 1000) { |
960 |
snprintf(buffer, sizeof(buffer), "%d ms lag", lag/1000); |
|
961 |
} else if (lag > 0) { |
|
962 |
snprintf(buffer, sizeof(buffer), "%d \302\265s lag", lag); |
|
1
by Steffen Joeris
Import upstream version 1.12 |
963 |
} else { |
964 |
return ""; |
|
965 |
}
|
|
966 |
case 5: |
|
967 |
if (comedi_dev && comedi_error) { |
|
968 |
return split_field(error, 1, 16); |
|
969 |
} else { |
|
970 |
return ""; |
|
971 |
}
|
|
972 |
||
973 |
default: |
|
974 |
return NULL; |
|
975 |
}
|
|
976 |
}
|
|
977 |
||
978 |
||
979 |
/* Option 1 key - global analog reference toggle
|
|
980 |
*
|
|
981 |
* Analog COMEDI devices typically can select between different
|
|
982 |
* references (which signal line is treated as zero point).
|
|
983 |
*/
|
|
984 |
||
985 |
static int option1(void) |
|
986 |
{
|
|
987 |
if (comedi_aref == AREF_GROUND && subdevice_flags & SDF_DIFF) |
|
988 |
comedi_aref = AREF_DIFF; |
|
989 |
else if (comedi_aref == AREF_GROUND && subdevice_flags & SDF_COMMON) |
|
990 |
comedi_aref = AREF_COMMON; |
|
991 |
else if (comedi_aref == AREF_DIFF && subdevice_flags & SDF_COMMON) |
|
992 |
comedi_aref = AREF_COMMON; |
|
993 |
else if (comedi_aref == AREF_DIFF && subdevice_flags & SDF_GROUND) |
|
994 |
comedi_aref = AREF_GROUND; |
|
995 |
else if (comedi_aref == AREF_COMMON && subdevice_flags & SDF_GROUND) |
|
996 |
comedi_aref = AREF_GROUND; |
|
997 |
else if (comedi_aref == AREF_COMMON && subdevice_flags & SDF_DIFF) |
|
998 |
comedi_aref = AREF_DIFF; |
|
999 |
else
|
|
1000 |
return 0; |
|
1001 |
||
1002 |
return 1; |
|
1003 |
}
|
|
1004 |
||
1005 |
static char * option1str(void) |
|
1006 |
{
|
|
1007 |
if (! (subdevice_flags & (SDF_GROUND | SDF_DIFF | SDF_COMMON))) { |
|
1008 |
return NULL; |
|
1009 |
} else if (comedi_aref == AREF_GROUND) { |
|
1010 |
return "AREF_GROUND"; |
|
1011 |
} else if (comedi_aref == AREF_DIFF) { |
|
1012 |
return "AREF_DIFF"; |
|
1013 |
} else if (comedi_aref == AREF_COMMON) { |
|
1014 |
return "AREF_COMMON"; |
|
1015 |
} else { |
|
1016 |
return "AREF unknown"; |
|
1017 |
}
|
|
1018 |
}
|
|
1019 |
||
1020 |
||
1.1.1
by Bhavani Shankar
Import upstream version 2.0 |
1021 |
#if 0
|
1022 |
||
1023 |
/* XXX Option 2 key - use this for a per-channel Range setting?
|
|
1024 |
*
|
|
1025 |
* If so, would also need to add option strings below.
|
|
1026 |
*/
|
|
1027 |
||
1
by Steffen Joeris
Import upstream version 1.12 |
1028 |
|
1029 |
static int option2(void)
|
|
1030 |
{
|
|
1031 |
return 0;
|
|
1032 |
}
|
|
1033 |
||
1034 |
static char * option2str(void)
|
|
1035 |
{
|
|
1036 |
return NULL;
|
|
1037 |
}
|
|
1038 |
||
1.1.1
by Bhavani Shankar
Import upstream version 2.0 |
1039 |
#endif
|
1
by Steffen Joeris
Import upstream version 1.12 |
1040 |
|
1041 |
static int comedi_set_option(char *option) |
|
1042 |
{
|
|
1043 |
char buf[256]; |
|
1044 |
char *p = buf; |
|
1045 |
||
1046 |
do { |
|
1047 |
*p++ = tolower(*option); |
|
1048 |
} while (*option++ && p < buf + sizeof(buf)); |
|
1049 |
||
1050 |
if (sscanf(buf, "rate=%d", &comedi_rate) == 1) { |
|
1051 |
reset_comedi(); |
|
1052 |
return 1; |
|
1053 |
} else if (sscanf(buf, "device=%s", device_name) == 1) { |
|
1054 |
comedi_devname = device_name; |
|
1055 |
close_comedi(); |
|
1056 |
/* reset_comedi(); */
|
|
1057 |
open_comedi(); |
|
1058 |
return 1; |
|
1059 |
} else if (sscanf(buf, "subdevice=%d", &comedi_subdevice) == 1) { |
|
1060 |
reset_comedi(); |
|
1061 |
return 1; |
|
1062 |
} else if (strcasecmp(buf, "aref=ground") == 0) { |
|
1063 |
comedi_aref = AREF_GROUND; |
|
1064 |
reset_comedi(); |
|
1065 |
return 1; |
|
1066 |
} else if (strcasecmp(buf, "aref=diff") == 0) { |
|
1067 |
comedi_aref = AREF_DIFF; |
|
1068 |
reset_comedi(); |
|
1069 |
return 1; |
|
1070 |
} else if (strcasecmp(buf, "bufsize=default") == 0) { |
|
1071 |
comedi_bufsize = -1; |
|
1072 |
return 1; |
|
1073 |
} else if (sscanf(buf, "bufsize=%d", &comedi_bufsize) == 1) { |
|
1074 |
reset_comedi(); |
|
1075 |
return 1; |
|
1076 |
} else { |
|
1077 |
return 0; |
|
1078 |
}
|
|
1079 |
}
|
|
1080 |
||
1081 |
static char * comedi_save_option(int i) |
|
1082 |
{
|
|
1083 |
static char buf[256]; |
|
1084 |
||
1085 |
switch (i) { |
|
1086 |
case 0: |
|
1087 |
snprintf(buf, sizeof(buf), "rate=%d", comedi_rate); |
|
1088 |
return buf; |
|
1089 |
||
1090 |
case 1: |
|
1091 |
snprintf(buf, sizeof(buf), "device=%s", comedi_devname); |
|
1092 |
return buf; |
|
1093 |
||
1094 |
case 2: |
|
1095 |
snprintf(buf, sizeof(buf), "subdevice=%d", comedi_subdevice); |
|
1096 |
return buf; |
|
1097 |
||
1098 |
case 3: |
|
1099 |
switch (comedi_aref) { |
|
1100 |
case AREF_GROUND: |
|
1101 |
return "aref=ground"; |
|
1102 |
case AREF_DIFF: |
|
1103 |
return "aref=diff"; |
|
1104 |
case AREF_COMMON: |
|
1105 |
return "aref=common"; |
|
1106 |
default: |
|
1107 |
return ""; |
|
1108 |
}
|
|
1109 |
||
1110 |
case 4: |
|
1111 |
if (comedi_bufsize > 0) { |
|
1112 |
snprintf(buf, sizeof(buf), "bufsize=%d", comedi_bufsize); |
|
1113 |
return buf; |
|
1114 |
} else { |
|
1115 |
return "bufsize=default"; |
|
1116 |
}
|
|
1117 |
||
1118 |
default: |
|
1119 |
return NULL; |
|
1120 |
}
|
|
1121 |
}
|
|
1122 |
||
1123 |
DataSrc datasrc_comedi = { |
|
1124 |
"COMEDI", |
|
1125 |
nchans, |
|
1126 |
comedi_chan, |
|
1127 |
set_trigger, |
|
1128 |
clear_trigger, |
|
1129 |
change_rate, |
|
1130 |
set_width, |
|
1131 |
reset, |
|
1132 |
fd, |
|
1133 |
get_data, |
|
1134 |
status_str, |
|
1135 |
option1, |
|
1136 |
option1str, |
|
1.1.1
by Bhavani Shankar
Import upstream version 2.0 |
1137 |
#if 0
|
1
by Steffen Joeris
Import upstream version 1.12 |
1138 |
option2,
|
1139 |
option2str,
|
|
1.1.1
by Bhavani Shankar
Import upstream version 2.0 |
1140 |
#else
|
1141 |
NULL, |
|
1142 |
NULL, |
|
1143 |
#endif
|
|
1
by Steffen Joeris
Import upstream version 1.12 |
1144 |
comedi_set_option, |
1145 |
comedi_save_option, |
|
1146 |
comedi_gtk_options, |
|
1147 |
};
|