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.
  • [get | view] (2008-10-02 08:41:25, 6.0 KB) [[attachment:.nrec.cfg-6025E]]
  • [get | view] (2008-09-10 07:20:35, 19403.8 KB) [[attachment:NVIDIA-Linux-x86-173.14.12-pkg1.run]]
  • [get | view] (2009-09-25 06:47:46, 22148.6 KB) [[attachment:NVIDIA-Linux-x86-185.18.36-pkg1.run]]
  • [get | view] (2009-12-23 08:47:42, 23470.9 KB) [[attachment:NVIDIA-Linux-x86-190.53-pkg1.run]]
  • [get | view] (2010-03-04 12:00:30, 33101.6 KB) [[attachment:NVIDIA-Linux-x86-195.36.08-pkg1.run]]
  • [get | view] (2009-11-30 07:51:21, 14874.3 KB) [[attachment:NVIDIA-Linux-x86-96.43.14-pkg1.run]]
  • [get | view] (2009-02-09 13:36:28, 20268.3 KB) [[attachment:NVIDIA-Linux-x86_64-180.22-pkg2.run]]
  • [get | view] (2011-04-08 07:22:29, 82902.1 KB) [[attachment:ati-driver-installer-9-12-x86.x86_64.run]]
  • [get | view] (2009-12-03 11:30:43, 29.9 KB) [[attachment:comedi.c]]
  • [get | view] (2011-12-01 14:14:57, 101.4 KB) [[attachment:config-2.6.32.11]]
  • [get | view] (2013-12-04 23:53:35, 115.9 KB) [[attachment:config-2.6.38.8]]
  • [get | view] (2010-03-04 13:36:03, 78.2 KB) [[attachment:config-HP_dc7900]]
  • [get | view] (2008-09-24 11:52:19, 76.1 KB) [[attachment:config-lenovo-t61]]
  • [get | view] (2010-02-23 10:24:03, 78.2 KB) [[attachment:config-samsung-p55]]
  • [get | view] (2008-10-02 08:48:33, 76.1 KB) [[attachment:config-sprectra-PC]]
  • [get | view] (2008-10-02 06:58:08, 5.8 KB) [[attachment:hdfview.png]]
  • [get | view] (2009-02-25 08:06:22, 0.3 KB) [[attachment:interfaces-dhcp]]
  • [get | view] (2009-02-25 08:06:12, 0.4 KB) [[attachment:interfaces-mininetz]]
  • [get | view] (2009-07-07 08:01:57, 0.1 KB) [[attachment:sample_start_with_log.sh]]
  • [get | view] (2008-10-02 06:58:52, 1.8 KB) [[attachment:symbol32.png]]
  • [get | view] (2017-01-12 10:22:04, 1.7 KB) [[attachment:symbol48_grey.png]]
  • [get | view] (2017-01-12 10:22:13, 1.7 KB) [[attachment:symbol48_grey_inverted.png]]
  • [get | view] (2008-09-24 08:54:35, 882.5 KB) [[attachment:xoscope.tgz]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.