Added comedi_board_info which has been inspired by the original
authorBernd Porr <berndporr@f2s.com>
Wed, 2 May 2012 23:39:04 +0000 (00:39 +0100)
committerBernd Porr <berndporr@f2s.com>
Wed, 2 May 2012 23:39:04 +0000 (00:39 +0100)
info in the demo directory. I've made it a bit more verbose
and also took into account that now the generic_timed command
takes more than one channel so that a user can experiment which
sampling rates are possible with different channel numbers.
I've also added an option to set the sampling rate for the generic_timed
command. That sould actually not be needed but I'm sure there
are drivers out there which do not correct the sampling rate
downwards automatically.

Makefile.am
comedi_board_info/Makefile.am [new file with mode: 0644]
comedi_board_info/comedi_board_info.1 [new file with mode: 0644]
comedi_board_info/comedi_board_info.c [new file with mode: 0644]
configure.ac

index 16f520708268920b4d303ab0d8bdd8af0b631ccb..f4361776cdd45e64985874b078d696238e45fd11 100644 (file)
@@ -1,5 +1,5 @@
 
-SUBDIRS = lib comedi_config man testing demo doc swig \
+SUBDIRS = lib comedi_config man testing demo comedi_board_info doc swig \
        include etc scxi c++
 
 pkgconfigdir = $(libdir)/pkgconfig
diff --git a/comedi_board_info/Makefile.am b/comedi_board_info/Makefile.am
new file mode 100644 (file)
index 0000000..c0b8c24
--- /dev/null
@@ -0,0 +1,7 @@
+bin_PROGRAMS = comedi_board_info
+
+man_MANS = comedi_board_info.1
+
+comedi_board_info_SOURCES = comedi_board_info.c
+comedi_board_info_CFLAGS = $(COMEDILIB_CFLAGS)
+comedi_board_info_LDADD = $(COMEDILIB_LIBS)
diff --git a/comedi_board_info/comedi_board_info.1 b/comedi_board_info/comedi_board_info.1
new file mode 100644 (file)
index 0000000..99a64ec
--- /dev/null
@@ -0,0 +1,40 @@
+.TH COMEDI_BOARD_INFO "1" "May 2012" "comedi_board_info" "User Commands"
+.SH NAME
+comedi_board_info \- reports useful information about COMEDI devices
+.SH SYNOPSIS
+.B comedi_board_info
+[\fIOPTIONS\fR] COMEDI_DEVICE
+.SH DESCRIPTION
+This program reads information about a comedi device and
+displays it in a human-readable form. It also
+probes the asynchronous acquisition by calling the function
+probe_cmd_generic_timed
+which provides information about the maximum sampling rate of
+the board
+and how the card acquires multiple channels (e.g. at once or
+one by one).
+.TP
+\-n NUMBER_OF_CHANNELS
+Requests the number of channels for the asynchronous acquisition.
+This might influence the maximum sampling rate and the mode how
+the comedi device samples the channels.
+If the number is higher than the available
+channels this will be corrected downwards to the number of available
+channels.
+.TP
+\-F SAMPLING_RATE 
+Requests the sampling rate for an asynchronous
+acquisition.  By default this is set to comedi's highest sampling rate
+so that the driver is forced to reduce it and will in most cases
+return the maximum possible sampling rate. 
+.TP
+\-v
+Verbose output.
+.TP
+\-h
+Shows a brief help screen.
+.SH "SEE ALSO"
+More documentation can be found on
+http://www.comedi.org/
+.SH AUTHOR
+comedi_board_info was written by David Schleef <ds@schleef.org> and Bernd Porr <berndporr@f2s.com>.
diff --git a/comedi_board_info/comedi_board_info.c b/comedi_board_info/comedi_board_info.c
new file mode 100644 (file)
index 0000000..bbe38ee
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+   This program reads information about a comedi device and
+   displays the information in a human-readable form.
+ */
+
+#include <stdio.h>
+#include <comedilib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+
+static char * const default_filename = "/dev/comedi0";
+
+int verbose = 0;
+
+static const char * const subdevice_types[]={
+       "unused",
+       "analog input",
+       "analog output",
+       "digital input",
+       "digital output",
+       "digital I/O",
+       "counter",
+       "timer",
+       "memory",
+       "calibration",
+       "processor",
+       "serial digital I/O",
+       "pwm"
+};
+
+struct subdev_flag {
+       const char *sdf_define;
+       unsigned int bitmask;
+       const char *description;
+};
+
+static struct subdev_flag subdev_flags[] = {
+       {"SDF_MAXDATA",0x0010,"maxdata depends on channel"},
+       {"SDF_FLAGS",0x0020,"flags depend on channel"},
+       {"SDF_RANGETYPE",0x0040,"range type depends on channel"},
+       {"SDF_MODE0",0x0080,"can do mode 0"},
+       {"SDF_MODE1",0x0100,"can do mode 1"},
+       {"SDF_MODE2",0x0200,"can do mode 2"},
+       {"SDF_MODE3",0x0400,"can do mode 3"},
+       {"SDF_MODE4",0x0800,"can do mode 4"},
+       {"SDF_SOFT_CALIBRATED",0x2000,"subdevice uses software calibration"},
+       {"SDF_CMD_WRITE",0x4000,"can do output commands"},
+       {"SDF_CMD_READ",0x8000,"can do input commands"},
+       {"SDF_READABLE",0x00010000,"subdevice can be read (e.g. analog input)"},
+       {"SDF_WRITABLE",0x00020000,"subdevice can be written (e.g. analog output)"},
+       {"SDF_INTERNAL",0x00040000,"subdevice does not have externally visible lines"},
+       {"SDF_GROUND",0x00100000,"can do aref=ground"},
+       {"SDF_COMMON",0x00200000,"can do aref=common"},
+       {"SDF_DIFF",0x00400000,"aref=diff"},
+       {"SDF_OTHER",0x00800000,"can do aref=other"},
+       {"SDF_DITHER",0x01000000,"can do dithering"},
+       {"SDF_DEGLITCH",0x02000000,"can do deglitching"},
+       {"SDF_MMAP",0x04000000,"can do mmap()"},
+       {"SDF_RUNNING",0x08000000,"subdevice is acquiring data"},
+       {"SDF_LSAMPL",0x10000000,"subdevice uses 32-bit samples"},
+       {"SDF_PACKED",0x20000000,"subdevice can do packed DIO"},
+       {0,0,0}};
+
+void explain_subdevice_flags(char* padding,unsigned int sf) {
+       int i = 0;
+       while (subdev_flags[i].sdf_define) {
+               if (sf & subdev_flags[i].bitmask)
+                       printf("%s%s:%s\n",
+                              padding,
+                              subdev_flags[i].sdf_define,
+                              subdev_flags[i].description);
+               i++;
+       }
+}
+
+void unit_to_desc(char *udesc,int unit) {
+       switch(unit) {
+       case UNIT_volt: strcpy(udesc," V"); break;
+       case UNIT_mA: strcpy(udesc," mA"); break;
+       case UNIT_none: strcpy(udesc,""); break; 
+       default: sprintf(udesc," (unknown unit %d)",
+                        unit);
+       }
+}
+
+
+
+char *tobinary(char *s,int bits,int n)
+{
+       int bit=1<<n;
+       char *t=s;
+
+       for(;bit;bit>>=1)
+               *t++=(bits&bit)?'1':'0';
+       *t=0;
+
+       return s;
+}
+
+
+char *cmd_src(int src,char *buf)
+{
+       buf[0]=0;
+
+       if(src&TRIG_NONE)strcat(buf,"none|");
+       if(src&TRIG_NOW)strcat(buf,"now|");
+       if(src&TRIG_FOLLOW)strcat(buf, "follow|");
+       if(src&TRIG_TIME)strcat(buf, "time|");
+       if(src&TRIG_TIMER)strcat(buf, "timer|");
+       if(src&TRIG_COUNT)strcat(buf, "count|");
+       if(src&TRIG_EXT)strcat(buf, "ext|");
+       if(src&TRIG_INT)strcat(buf, "int|");
+#ifdef TRIG_OTHER
+       if(src&TRIG_OTHER)strcat(buf, "other|");
+#endif
+
+       if(strlen(buf)==0){
+               sprintf(buf,"unknown(0x%08x)",src);
+       }else{
+               buf[strlen(buf)-1]=0;
+       }
+
+       return buf;
+}
+
+
+
+void probe_cmd_generic_timed(comedi_t *it,int s,int n_channels,int freq_for_generic_timed)
+{
+       comedi_cmd cmd;
+       char buf[100];
+
+       printf("  command structure filled with probe_cmd_generic_timed for %d channels:\n",
+              n_channels);
+       if(comedi_get_cmd_generic_timed(it, s, &cmd, n_channels, 1E9/freq_for_generic_timed)<0){
+               printf("    not supported\n");
+       }else{
+               printf("    start: %s %d\n",
+                       cmd_src(cmd.start_src,buf),cmd.start_arg);
+               printf("    scan_begin: %s %d\n",
+                       cmd_src(cmd.scan_begin_src,buf),cmd.scan_begin_arg);
+               if (verbose) {
+                       if ((cmd.scan_begin_src == TRIG_TIMER)&&(cmd.scan_begin_arg)) {
+                               printf("      scan_begin_src = TRIG_TIMER:\n"
+                                      "      The sampling rate is defined per scan\n"
+                                      "      meaning all channels are sampled at\n"
+                                      "      the same time. The maximum sampling rate is f=%d Hz\n",
+                                      (int)(1E9/cmd.scan_begin_arg));}
+               }
+               printf("    convert: %s %d\n",
+                       cmd_src(cmd.convert_src,buf),cmd.convert_arg);
+               if (verbose) {
+                       if ((cmd.convert_src == TRIG_TIMER)&&(cmd.convert_arg)) {
+                               printf("      convert_src = TRIG_TIMER\n"
+                                      "      The sampling rate is defined per channel\n"
+                                      "      meaning that a multiplexer is being switched from\n"
+                                      "      channel to channel at a maximum rate of %d Hz.\n"
+                                      "      The overall sampling rate needs to be divided\n"
+                                      "      by the number of channels and results in f=%d Hz.\n",
+                                      (int)(1E9/cmd.convert_arg),
+                                      (int)(1E9/cmd.convert_arg/n_channels));
+                       }
+               }
+               printf("    scan_end: %s %d\n",
+                       cmd_src(cmd.scan_end_src,buf),cmd.scan_end_arg);
+               printf("    stop: %s %d\n",
+                       cmd_src(cmd.stop_src,buf),cmd.stop_arg);
+       }
+}
+
+
+
+void get_command_stuff(comedi_t *it,int s,int n_chans_for_generic_timed,int freq_for_generic_timed)
+{
+       comedi_cmd cmd;
+       char buf[100];
+
+       if(comedi_get_cmd_src_mask(it,s,&cmd)<0){
+               printf("    not supported\n");
+       }else{
+               printf("    start: %s\n",cmd_src(cmd.start_src,buf));
+               if (cmd.start_src == TRIG_EXT)
+                       printf("    cmd.start_src allows external trigger (TRIG_EXT),"
+                              "    for example from on input pin at the device.\n");
+               printf("    scan_begin: %s\n",cmd_src(cmd.scan_begin_src,buf));
+               printf("    convert: %s\n",cmd_src(cmd.convert_src,buf));
+               printf("    scan_end: %s\n",cmd_src(cmd.scan_end_src,buf));
+               printf("    stop: %s\n",cmd_src(cmd.stop_src,buf));
+
+               probe_cmd_generic_timed(it,s,n_chans_for_generic_timed,freq_for_generic_timed);
+       }
+}
+
+
+               
+int main(int argc,char *argv[])
+{
+       int i,j;
+       int n_subdevices,type;
+       const char *type_str;
+       int chan,n_chans;
+       int n_ranges;
+       int subdev_flags;
+       comedi_range *rng;
+       comedi_t *it;
+       char *filename = default_filename;
+       char c;
+       char strtmp[16];
+       int n_chans_for_generic_timed = 1;
+       int freq_for_generic_timed = 1E9;
+
+       while (-1 != (c = getopt(argc, argv, "hvn:F:"))) {
+               switch (c) {
+               case 'n':
+                       n_chans_for_generic_timed = strtoul(optarg, NULL, 0);
+                       break;
+               case 'F':
+                       freq_for_generic_timed = strtoul(optarg, NULL, 0);
+                       break;
+               case 'v':
+                       verbose++;
+                       break;
+               case 'h':
+               default:
+                       fprintf(stderr,
+                               "usage: comedi_board_info [OPTIONS] COMEDI_DEVICE\n"
+                               "   -n    number of channels for async command (default 1)\n"
+                               "   -F    probing sampling rate for async command (default 1Ghz)\n"
+                               "   -v    verbose output\n"
+                               "   -h    this help screen\n");
+                       exit(1);
+               }
+       }
+
+       if(optind < argc) {
+               filename = argv[optind];
+       }
+
+       it = comedi_open(filename);
+       if(!it){
+               comedi_perror(filename);
+               exit(1);
+       }
+
+       printf("overall info:\n");
+       printf("  version code: 0x%06x\n", comedi_get_version_code(it));
+       printf("  driver name: %s\n", comedi_get_driver_name(it));
+       printf("  board name: %s\n", comedi_get_board_name(it));
+       printf("  number of subdevices: %d\n", n_subdevices = comedi_get_n_subdevices(it));
+
+       for(i = 0; i < n_subdevices; i++){
+               printf("subdevice %d:\n",i);
+               type = comedi_get_subdevice_type(it, i);
+               if(type < (int)(sizeof(subdevice_types) / sizeof(subdevice_types[0]))){
+                       type_str = subdevice_types[type];
+               }else{
+                       type_str = "UNKNOWN";
+               }
+               printf("  type: %d (%s)\n",type,type_str);
+               if(type==COMEDI_SUBD_UNUSED)
+                       continue;
+               subdev_flags = comedi_get_subdevice_flags(it, i);
+               printf("  flags: 0x%08x\n",subdev_flags);
+               if (verbose) explain_subdevice_flags("          ",subdev_flags);
+               n_chans=comedi_get_n_channels(it,i);
+               printf("  number of channels: %d\n",n_chans);
+               if(!comedi_maxdata_is_chan_specific(it,i)){
+                       printf("  max data value: %lu\n", (unsigned long)comedi_get_maxdata(it,i,0));
+               }else{
+                       printf("  max data value: (channel specific)\n");
+                       for(chan=0;chan<n_chans;chan++){
+                               printf("    chan%d: %lu\n",chan,
+                                       (unsigned long)comedi_get_maxdata(it,i,chan));
+                       }
+               }
+               printf("  ranges:\n");
+               if(!comedi_range_is_chan_specific(it,i)){
+                       n_ranges=comedi_get_n_ranges(it,i,0);
+                       printf("    all chans:");
+                       for(j=0;j<n_ranges;j++){
+                               rng=comedi_get_range(it,i,0,j);
+                               unit_to_desc(strtmp,rng->unit);
+                               printf(" [%g%s,%g%s]",rng->min,strtmp,rng->max,strtmp);
+                       }
+                       printf("\n");
+               }else{
+                       for(chan=0;chan<n_chans;chan++){
+                               n_ranges=comedi_get_n_ranges(it,i,chan);
+                               printf("    chan%d:",chan);
+                               for(j=0;j<n_ranges;j++){
+                                       rng=comedi_get_range(it,i,chan,j);
+                                       unit_to_desc(strtmp,rng->unit);
+                                       printf(" [%g%s,%g%s]",rng->min,strtmp,rng->max,strtmp);
+                               }
+                               printf("\n");
+                       }
+               }
+               printf("  command:\n");
+               if (n_chans_for_generic_timed>n_chans)
+                       n_chans_for_generic_timed = n_chans;
+               if (n_chans_for_generic_timed<1)
+                       n_chans_for_generic_timed = 1;
+               if (freq_for_generic_timed > 1E9)
+                       freq_for_generic_timed = 1E9;
+               if (freq_for_generic_timed < 1)
+                       freq_for_generic_timed = 1;
+               get_command_stuff(it,i,n_chans_for_generic_timed,freq_for_generic_timed);
+       }
+
+       return 0;
+}
+
index 97c848e55d5ff621305f3fc5c9a618ec11a5db2f..ffb85a7e34fccbf50cfd3d8905f2bfb2390c106f 100644 (file)
@@ -275,6 +275,7 @@ fi
 AC_CONFIG_FILES(
 Makefile
 comedi_config/Makefile
+comedi_board_info/Makefile
 c++/Makefile
 c++/include/Makefile
 etc/Makefile