
Put square brackets around "COMEDI_DEVICE" on the usage line to indicate that it is optional, and add a line to indicate that it defaults to "/dev/comedi0" if unspecified. Also change "1Ghz" to "1 GHz" in the description of the -F option.
323 lines
9.1 KiB
C
323 lines
9.1 KiB
C
/*
|
|
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 asynchronous output commands"},
|
|
{"SDF_CMD_READ",0x8000,"can do asynchronous input commands"},
|
|
{"SDF_READABLE",0x00010000,"subdevice can be read"},
|
|
{"SDF_WRITABLE",0x00020000,"subdevice can be written"},
|
|
{"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 for commands"},
|
|
{"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) {
|
|
if ((unit & RF_EXTERNAL) != 0)
|
|
strcpy(udesc, "*EXT");
|
|
else
|
|
udesc[0] = '\0';
|
|
switch(RF_UNIT(unit)) {
|
|
case UNIT_volt: strcat(udesc," V"); break;
|
|
case UNIT_mA: strcat(udesc," mA"); break;
|
|
case UNIT_none: strcat(udesc,""); break;
|
|
default: sprintf(udesc + strlen(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;
|
|
int c;
|
|
char strtmp[32];
|
|
int def_n_chans_for_generic_timed = 1;
|
|
int n_chans_for_generic_timed;
|
|
int freq_for_generic_timed = 1E9;
|
|
|
|
while (-1 != (c = getopt(argc, argv, "hvn:F:"))) {
|
|
switch (c) {
|
|
case 'n':
|
|
def_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 1 GHz)\n"
|
|
" -v verbose output\n"
|
|
" -h this help screen\n"
|
|
"COMEDI_DEVICE defaults to /dev/comedi0 if unspecified\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");
|
|
n_chans_for_generic_timed = def_n_chans_for_generic_timed;
|
|
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;
|
|
}
|
|
|