removed libperl dependency by switching to bison/flex to parse calibration

file.
This commit is contained in:
Frank Mori Hess 2003-03-05 00:30:00 +00:00
parent fb2bf948b2
commit b950e1c63c
5 changed files with 492 additions and 204 deletions

View file

@ -2,23 +2,25 @@
include ../Config
include ../version
PERL_LDFLAGS := -lperl $(shell perl -MConfig -e 'print $$Config{perllibs}')
PERL_INC := $(shell perl -MConfig -e 'print $$Config{archlib}')/CORE
CFLAGS += -fPIC -I../include -I. -I$(PERL_INC)
CFLAGS += -fPIC -I../include -I.
OBJS=comedi.o timer.o sv.o range.o ioctl.o filler.o timed.o error.o \
dio.o data.o get.o cmd.o buffer.o calib.o
dio.o data.o get.o cmd.o buffer.o calib.o calib_lex.o calib_yacc.o
SONAME=libcomedi$(SONAME_SUFFIX).so.0
libcomedi.a: $(OBJS) version_script
#$(CC) -shared -Wl,-soname,libcomedi.so,-T,version_script -o libcomedi.so.${VERSION_CODE} $(OBJS) -lm
$(CC) -shared -Wl,-soname,$(SONAME) -Wl,--version-script,version_script -o libcomedi.so.${version} $(OBJS) -lm $(PERL_LDFLAGS)
$(CC) -shared -Wl,-soname,$(SONAME) -Wl,--version-script,version_script -o libcomedi.so.${version} $(OBJS) -lm
$(AR) rs libcomedi.a $(OBJS)
ln -sf libcomedi.so.${version} libcomedi.so
ln -sf libcomedi.so.${version} libcomedi.so.0
clean:
rm -f libcomedi.a libcomedi.so* *.o
rm -f libcomedi.a libcomedi.so* *.o calib_lex.c calib_yacc.c calib_yacc.h
calib_lex.c: calib_lex.l calib_yacc.h
flex -Pcalib_yy -o$@ $<
calib_yacc.c + calib_yacc.h: calib_yacc.y
bison -d -y -p calib_yy -o calib_yacc.c calib_yacc.y

View file

@ -27,50 +27,16 @@
#include <string.h>
#include <comedilib.h>
#include <libinternal.h>
#include <EXTERN.h>
#include <perl.h>
static int extract_ph_string( PerlInterpreter *my_perl, const char *perl_statement,
char *result, unsigned int result_size )
static int check_cal_file( comedi_t *dev, const struct calibration_file_contents *parsed_file )
{
SV *perl_retval;
STRLEN len;
perl_retval = eval_pv( perl_statement, FALSE );
strncpy( result, SvPV( perl_retval, len ), result_size );
return 0;
}
static int extract_ph_integer( PerlInterpreter *my_perl, const char *perl_statement )
{
SV *perl_retval;
int result;
perl_retval = eval_pv( perl_statement, FALSE );
result = SvIV( perl_retval );
return result;
}
static int check_cal_file( comedi_t *dev, PerlInterpreter *my_perl )
{
char result[ 100 ];
int retval;
retval = extract_ph_string( my_perl, "$cal->{driver_name};",
result, sizeof( result ) );
if( retval < 0 ) return retval;
if( strcmp( comedi_get_driver_name( dev ), result ) )
if( strcmp( comedi_get_driver_name( dev ), parsed_file->driver_name ) )
{
fprintf( stderr, "driver name does not match calibration file\n" );
return -1;
}
retval = extract_ph_string( my_perl, "$cal->{board_name};",
result, sizeof( result ) );
if( retval < 0 ) return retval;
if( strcmp( comedi_get_board_name( dev ), result ) )
if( strcmp( comedi_get_board_name( dev ), parsed_file->board_name ) )
{
fprintf( stderr, "board name does not match calibration file\n" );
return -1;
@ -79,89 +45,67 @@ static int check_cal_file( comedi_t *dev, PerlInterpreter *my_perl )
return 0;
}
static inline int num_calibrations( PerlInterpreter *my_perl )
static inline int valid_channel( const struct calibration_file_contents *parsed_file,
unsigned int cal_index, unsigned int channel )
{
return extract_ph_integer( my_perl, "scalar( @{$cal->{calibrations}} );" );
}
int num_channels, i;
static int extract_array_element( PerlInterpreter *my_perl, unsigned int cal_index,
const char *array_name, unsigned int array_index )
{
char element[ 100 ];
snprintf( element, sizeof( element ),
"$cal->{ calibrations }[ %i ]->{ %s }[ %i ];", cal_index, array_name, array_index );
return extract_ph_integer( my_perl, element );
}
static int extract_array_length( PerlInterpreter *my_perl, unsigned int cal_index,
const char *array_name )
{
char element[ 100 ];
snprintf( element, sizeof( element ),
"scalar( @{ $cal->{ calibrations }[ %i ]->{ %s } } );", cal_index, array_name );
return extract_ph_integer( my_perl, element );
}
static int extract_subdevice( PerlInterpreter *my_perl, unsigned int cal_index )
{
char element[ 100 ];
snprintf( element, sizeof( element ),
"$cal->{ calibrations }[ %i ]->{ subdevice };", cal_index );
return extract_ph_integer( my_perl, element );
}
static int valid_item( PerlInterpreter *my_perl, unsigned int cal_index,
const char *item_type, unsigned int item )
{
int num_items, i;
num_items = extract_array_length( my_perl, cal_index, item_type );
if( num_items < 0 ) return 0;
if( num_items == 0 ) return 1;
for( i = 0; i < num_items; i++ )
num_channels = parsed_file->calibrations[ cal_index ].num_channels;
if( num_channels == 0 ) return 1;
for( i = 0; i < num_channels; i++ )
{
if( extract_array_element( my_perl, cal_index, item_type, i ) == item )
if( parsed_file->calibrations[ cal_index ].channels[ i ] == channel )
return 1;
}
return 0;
}
static inline int valid_range( PerlInterpreter *my_perl, unsigned int cal_index,
unsigned int range )
static inline int valid_range( const struct calibration_file_contents *parsed_file,
unsigned int cal_index, unsigned int range )
{
return valid_item( my_perl, cal_index, "ranges", range );
int num_ranges, i;
num_ranges = parsed_file->calibrations[ cal_index ].num_ranges;
if( num_ranges == 0 ) return 1;
for( i = 0; i < num_ranges; i++ )
{
if( parsed_file->calibrations[ cal_index ].ranges[ i ] == range )
return 1;
}
return 0;
}
static inline int valid_channel( PerlInterpreter *my_perl, unsigned int cal_index,
unsigned int channel )
static inline int valid_aref( const struct calibration_file_contents *parsed_file,
unsigned int cal_index, unsigned int aref )
{
return valid_item( my_perl, cal_index, "channels", channel );
int num_arefs, i;
num_arefs = parsed_file->calibrations[ cal_index ].num_arefs;
if( num_arefs == 0 ) return 1;
for( i = 0; i < num_arefs; i++ )
{
if( parsed_file->calibrations[ cal_index ].arefs[ i ] == aref )
return 1;
}
return 0;
}
static inline int valid_aref( PerlInterpreter *my_perl, unsigned int cal_index,
unsigned int aref )
{
return valid_item( my_perl, cal_index, "arefs", aref );
}
static int find_calibration( PerlInterpreter *my_perl, unsigned int subdev,
unsigned int channel, unsigned int range, unsigned int aref )
static int find_calibration( const struct calibration_file_contents *parsed_file,
unsigned int subdev, unsigned int channel, unsigned int range, unsigned int aref )
{
int num_cals, i;
num_cals = num_calibrations( my_perl );
if( num_cals < 0 ) return num_cals;
num_cals = parsed_file->num_calibrations;
for( i = 0; i < num_cals; i++ )
{
if( extract_subdevice( my_perl, i ) != subdev ) continue;
if( valid_range( my_perl, i, range ) == 0 ) continue;
if( valid_channel( my_perl, i, channel ) == 0 ) continue;
if( valid_aref( my_perl, i, aref ) == 0 ) continue;
if( parsed_file->calibrations[ i ].subdevice != subdev ) continue;
if( valid_range( parsed_file, i, range ) == 0 ) continue;
if( valid_channel( parsed_file, i, channel ) == 0 ) continue;
if( valid_aref( parsed_file, i, aref ) == 0 ) continue;
break;
}
if( i == num_cals ) return -1;
@ -169,101 +113,27 @@ static int find_calibration( PerlInterpreter *my_perl, unsigned int subdev,
return i;
}
static int set_calibration( comedi_t *dev, PerlInterpreter *my_perl,
static int set_calibration( comedi_t *dev, const struct calibration_file_contents *parsed_file,
unsigned int cal_index )
{
int i, retval, num_caldacs;
num_caldacs = extract_array_length( my_perl, cal_index, "caldacs" );
if( num_caldacs < 0 ) return num_caldacs;
num_caldacs = parsed_file->calibrations[ cal_index ].num_caldacs;
for( i = 0; i < num_caldacs; i++ )
{
int subdev, channel, value;
char *element;
struct caldac_setting caldac;
asprintf( &element, "$cal->{calibrations}[ %i ]->{caldacs}[ %i ]->{subdevice};",
cal_index, i );
subdev = extract_ph_integer( my_perl, element );
free( element );
if( subdev < 0 )
{
fprintf( stderr, "failed to extract subdev\n" );
return subdev;
}
caldac = parsed_file->calibrations[ cal_index ].caldacs[ i ];
asprintf( &element, "$cal->{calibrations}[ %i ]->{caldacs}[ %i ]->{channel};",
cal_index, i );
channel = extract_ph_integer( my_perl, element );
free( element );
if( channel < 0 )
{
fprintf( stderr, "failed to extract channel\n" );
return channel;
}
asprintf( &element, "$cal->{calibrations}[ %i ]->{caldacs}[ %i ]->{value};",
cal_index, i );
value = extract_ph_integer( my_perl, element );
free( element );
if( value < 0 )
{
fprintf( stderr, "failed to extract value\n" );
return value;
}
retval = comedi_data_write( dev, subdev, channel, 0, 0, value );
retval = comedi_data_write( dev, caldac.subdevice, caldac.channel,
0, 0, caldac.value );
if( retval < 0 ) return retval;
}
return 0;
}
static PerlInterpreter* alloc_my_perl( void )
{
PerlInterpreter *my_perl;
char *embedding[] = { "", "-e", "0" };
my_perl = perl_alloc();
if( my_perl == NULL )
{
fprintf( stderr, "failed to alloc perl interpreter\n");
return my_perl;
}
perl_construct( my_perl );
perl_parse(my_perl, NULL, 3, embedding, NULL);
return my_perl;
}
static int startup_my_perl( PerlInterpreter *my_perl, const char *file_path )
{
int retval;
char perl_prog[ 1024 ];
snprintf( perl_prog, sizeof( perl_prog ),
"
my $hash = `cat '%s'`;
eval \"\\$cal = $hash;\";
", file_path );
retval = perl_run( my_perl );
if( retval )
{
fprintf( stderr, "nonzero exit from perl_run\n");
return -1;
}
eval_pv( perl_prog, FALSE );
return 0;
}
static void cleanup_my_perl( PerlInterpreter *my_perl )
{
perl_destruct( my_perl );
perl_free( my_perl );
}
EXPORT_SYMBOL(comedi_apply_calibration,0.7.20);
int comedi_apply_calibration( comedi_t *dev, unsigned int subdev, unsigned int channel,
unsigned int range, unsigned int aref, const char *cal_file_path )
@ -272,7 +142,8 @@ int comedi_apply_calibration( comedi_t *dev, unsigned int subdev, unsigned int c
char file_path[ 1024 ];
int retval;
int cal_index;
PerlInterpreter *my_perl;
FILE *cal_file;
const struct calibration_file_contents *parsed_file;
if( cal_file_path )
{
@ -290,35 +161,32 @@ int comedi_apply_calibration( comedi_t *dev, unsigned int subdev, unsigned int c
( unsigned long ) file_stats.st_ino );
}
my_perl = alloc_my_perl();
if( my_perl == NULL )
return -1;
cal_file = fopen( file_path, "r" );
if( cal_file == NULL ) return -1;
retval = startup_my_perl( my_perl, file_path );
parsed_file = parse_calibration_file( cal_file );
if( parsed_file == NULL ) return -1;
fclose( cal_file );
retval = check_cal_file( dev, parsed_file );
if( retval < 0 )
{
cleanup_my_perl( my_perl );
cleanup_calibration_parse();
return retval;
}
retval = check_cal_file( dev, my_perl );
if( retval < 0 )
{
cleanup_my_perl( my_perl );
return retval;
}
cal_index = find_calibration( my_perl, subdev, channel, range, aref );
cal_index = find_calibration( parsed_file, subdev, channel, range, aref );
if( cal_index < 0 )
{
cleanup_my_perl( my_perl );
cleanup_calibration_parse();
return cal_index;
}
retval = set_calibration( dev, my_perl, cal_index );
retval = set_calibration( dev, parsed_file, cal_index );
if( retval < 0 );
{
cleanup_my_perl( my_perl );
cleanup_calibration_parse();
return retval;
}

81
lib/calib_lex.l Normal file
View file

@ -0,0 +1,81 @@
%option noyywrap
%{
/*
lib/calib_lex.l
code for parsing calibration file, generated by flex
Copyright (C) 2003 Frank Mori Hess <fmhess@users.sourceforge.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
of the License.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
*/
#include "libinternal.h"
#include "calib_yacc.h"
YYLTYPE yylloc;
char string_buf[ 100 ];
char *string_buf_ptr;
%}
%x COMMENT
%x STRING
%%
<STRING,INITIAL>\n { yylloc.first_line++; }
"#" BEGIN(COMMENT);
<COMMENT>\n { yylloc.first_line++; BEGIN(INITIAL); }
\" { string_buf_ptr = string_buf; BEGIN(STRING); }
<STRING>\" {
*string_buf_ptr = 0;
BEGIN(INITIAL);
calib_yylval.sval = string_buf;
return ( T_STRING );
}
<STRING>[^\n\"]+ {
char *yptr = yytext;
while ( *yptr && ( string_buf_ptr - string_buf ) < sizeof( string_buf ) - 1 )
*string_buf_ptr++ = *yptr++;
}
driver_name { return ( T_DRIVER_NAME ); }
board_name { return ( T_BOARD_NAME ); }
calibrations { return ( T_CALIBRATIONS ); }
subdevice { return ( T_SUBDEVICE); }
channels { return (T_CHANNELS); }
ranges { return ( T_RANGES ); }
arefs { return ( T_AREFS ); }
caldacs { return ( T_CALDACS ); }
channel { return ( T_CHANNEL ); }
value { return ( T_VALUE ); }
=> { return ( T_ASSIGN ); };
(0x)?(00)?[0-9a-fA-F]+ { calib_yylval.ival = strtol( calib_yytext, NULL, 0 );
return( T_NUMBER ); }
[ \t]
. { return( calib_yytext[0] ); }
%%

301
lib/calib_yacc.y Normal file
View file

@ -0,0 +1,301 @@
%{
/*
lib/calib_yacc.y
code for parsing calibration file, generated by bison
Copyright (C) 2003 Frank Mori Hess <fmhess@users.sourceforge.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
of the License.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
*/
#include <stdio.h>
#include "libinternal.h"
#include <string.h>
#include <stdlib.h>
#define YYERROR_VERBOSE
struct calibration_file_contents file_contents;
static struct caldac_setting caldac;
static int cal_index;
static void free_calibration_setting( struct calibration_setting *setting )
{
if( setting->channels );
{
free( setting->channels );
setting->channels = NULL;
setting->num_channels = 0;
}
if( setting->ranges );
{
free( setting->ranges );
setting->ranges = NULL;
setting->num_ranges = 0;
}
setting->num_arefs = 0;
if( setting->caldacs );
{
free( setting->caldacs );
setting->caldacs = NULL;
setting->num_caldacs = 0;
}
}
static void free_calibrations( struct calibration_file_contents *file_contents )
{
int i;
if( file_contents->calibrations == NULL ) return;
for( i = 0; i < file_contents->num_calibrations; i++ )
{
free_calibration_setting( &file_contents->calibrations[ i ] );
}
file_contents->calibrations = NULL;
}
static int add_calibration_setting( struct calibration_file_contents *file_contents )
{
struct calibration_setting *temp;
temp = realloc( file_contents->calibrations,
( file_contents->num_calibrations + 1 ) * sizeof( struct calibration_setting ) );
if( temp == NULL ) return -1;
file_contents->calibrations = temp;
memset( &file_contents->calibrations[ file_contents->num_calibrations ],
0, sizeof( struct calibration_setting ) );
file_contents->num_calibrations++;
return 0;
}
static struct calibration_setting* current_setting( struct calibration_file_contents *file_contents )
{
int retval;
while( cal_index >= file_contents->num_calibrations )
{
retval = add_calibration_setting( file_contents );
if( retval < 0 ) return NULL;
}
return &file_contents->calibrations[ cal_index ];
}
static int add_channel( struct calibration_file_contents *file_contents, int channel )
{
int *temp;
struct calibration_setting *setting;
setting = current_setting( file_contents );
if( setting == NULL ) return -1;
temp = realloc( setting->channels, ( setting->num_channels + 1 ) * sizeof( int ) );
if( temp == NULL ) return -1;
setting->channels = temp;
setting->channels[ setting->num_channels++ ] = channel;
return 0;
}
static int add_range( struct calibration_file_contents *file_contents, int range )
{
int *temp;
struct calibration_setting *setting;
setting = current_setting( file_contents );
if( setting == NULL ) return -1;
temp = realloc( setting->ranges, ( setting->num_ranges + 1 ) * sizeof( int ) );
if( temp == NULL ) return -1;
setting->ranges = temp;
setting->ranges[ setting->num_ranges++ ] = range;
return 0;
}
static int add_aref( struct calibration_file_contents *file_contents, int aref )
{
struct calibration_setting *setting;
setting = current_setting( file_contents );
if( setting == NULL ) return -1;
if( setting->num_arefs >= sizeof( setting->arefs ) /
sizeof( setting->arefs[ 0 ] ) )
return -1;
setting->arefs[ setting->num_arefs++ ] = aref;
return 0;
}
static int add_caldac( struct calibration_file_contents *file_contents,
struct caldac_setting caldac )
{
struct caldac_setting *temp;
struct calibration_setting *setting;
setting = current_setting( file_contents );
if( setting == NULL ) return -1;
temp = realloc( setting->caldacs, ( setting->num_caldacs + 1 ) *
sizeof( struct caldac_setting ) );
if( temp == NULL ) return -1;
setting->caldacs = temp;
setting->caldacs[ setting->num_caldacs++ ] = caldac;
return 0;
}
static void init_calib_parse( void )
{
memset( &file_contents, 0, sizeof( file_contents ) );
cal_index = 0;
}
extern void cleanup_calibration_parse( void )
{
if( file_contents.driver_name )
{
free( file_contents.driver_name );
file_contents.driver_name = NULL;
}
if( file_contents.board_name )
{
free( file_contents.board_name );
file_contents.board_name = NULL;
}
free_calibrations( &file_contents );
}
extern const struct calibration_file_contents* parse_calibration_file( FILE *file )
{
calib_yyrestart( file );
init_calib_parse();
if( calib_yyparse() ) return NULL;
return &file_contents;
}
%}
%union
{
int ival;
char *sval;
}
%token T_DRIVER_NAME T_BOARD_NAME T_CALIBRATIONS T_SUBDEVICE T_CHANNELS
%token T_RANGES T_AREFS T_CALDACS T_CHANNEL T_VALUE T_NUMBER T_STRING
%token T_ASSIGN
%type <ival> T_NUMBER
%type <sval> T_STRING
%%
input: '{' hash '}'
| error
{
fprintf(stderr, "input error on line %i\n", @1.first_line );
return -1;
}
;
hash: /* empty */
| hash_element
| hash_element ',' hash
;
hash_element: T_DRIVER_NAME T_ASSIGN T_STRING
{
if( file_contents.driver_name != NULL ) YYABORT;
file_contents.driver_name = strdup( $3 );
}
| T_BOARD_NAME T_ASSIGN T_STRING
{
if( file_contents.board_name != NULL ) YYABORT;
file_contents.board_name = strdup( $3 );
}
| T_CALIBRATIONS T_ASSIGN '[' calibrations_array ']'
;
calibrations_array: /* empty */
| '{' calibration_setting '}'
| '{' calibration_setting '}' ',' calibrations_array
;
calibration_setting: /* empty */ { cal_index++; }
| calibration_setting_element
| calibration_setting_element ',' calibration_setting
;
calibration_setting_element: T_SUBDEVICE T_ASSIGN T_NUMBER
{
struct calibration_setting *setting;
setting = current_setting( &file_contents );
if( setting == NULL ) YYABORT;
setting->subdevice = $3;
}
| T_CHANNELS T_ASSIGN '[' channels_array ']'
| T_RANGES T_ASSIGN '[' ranges_array ']'
| T_AREFS T_ASSIGN '[' arefs_array ']'
| T_CALDACS T_ASSIGN '[' caldacs_array ']'
;
channels_array: /* empty */
| channel
| channel ',' channels_array
;
channel: T_NUMBER { add_channel( &file_contents, $1 ); }
;
ranges_array: /* empty */
| range
| range ',' ranges_array
;
range: T_NUMBER { add_range( &file_contents, $1 ); }
;
arefs_array: /* empty */
| aref
| aref ',' arefs_array
;
aref: T_NUMBER { add_aref( &file_contents, $1 ); }
;
caldacs_array: /* empty */
| '{' caldac '}'
| '{' caldac '}' ',' caldacs_array
;
caldac: /* empty */ { add_caldac( &file_contents, caldac ); }
| caldac_element
| caldac_element ',' caldac
;
caldac_element: T_SUBDEVICE T_ASSIGN T_NUMBER { caldac.subdevice = $3; }
| T_CHANNEL T_ASSIGN T_NUMBER { caldac.channel = $3; }
| T_VALUE T_ASSIGN T_NUMBER { caldac.value = $3; }
;
%%
void calib_yyerror(char *s)
{
fprintf(stderr, "%s\n", s);
}

View file

@ -151,8 +151,44 @@ enum{
COMEDILIB_BADCHAN,
};
// used by range.c, was in comedilib.h but apparently deprecated so I put it here - fmhess
/* used by range.c, was in comedilib.h but apparently deprecated so I put it here - fmhess */
int comedi_get_rangetype(comedi_t *it,unsigned int subdevice,unsigned int chan);
/* structs and functions used for parsing calibration files */
struct caldac_setting
{
unsigned int subdevice;
unsigned int channel;
unsigned int value;
};
struct calibration_setting
{
unsigned int subdevice;
unsigned int *channels;
unsigned int num_channels;
unsigned int *ranges;
unsigned int num_ranges;
unsigned int arefs[ 4 ];
unsigned int num_arefs;
struct caldac_setting *caldacs;
unsigned int num_caldacs;
};
struct calibration_file_contents
{
char *driver_name;
char *board_name;
struct calibration_setting *calibrations;
unsigned int num_calibrations;
};
int calib_yylex( void );
void calib_yyerror( char *s );
int calib_yyparse( void );
void calib_yyrestart( FILE *calibration_file );
const struct calibration_file_contents* parse_calibration_file( FILE *file );
void cleanup_calibration_parse( void );
#endif