~ubuntu-branches/ubuntu/utopic/xoscope/utopic

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
};