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