catch errors from find_calibration()
[comedilib.git] / lib / calib.c
1 /*
2     lib/calib.c
3     functions for setting calibration
4
5     Copyright (C) 2003 Frank Mori Hess <fmhess@users.sourceforge.net
6
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Lesser General Public
9     License as published by the Free Software Foundation, version 2.1
10     of the License.
11
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15     Lesser General Public License for more details.
16
17     You should have received a copy of the GNU Lesser General Public
18     License along with this library; if not, write to the Free Software
19     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
20     USA.
21 */
22
23 #define _GNU_SOURCE
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <comedilib.h>
29 #include <libinternal.h>
30
31 static int extract_ph_string( const char *file_path, const char *hash_ref,
32         const char *element, char *result, unsigned int result_size )
33 {
34         char perl_prog[ 1024 ];
35         FILE *perl_stdout;
36         int retval;
37
38         snprintf( perl_prog, sizeof( perl_prog ),
39                 "perl -e '
40                 use strict;
41                 use warnings;
42                 my $hash;
43                 my $%s;
44                 $hash = `cat %s`;
45                 eval \"\\$%s = $hash;\";
46                 print %s;
47                 '",
48                 hash_ref, file_path, hash_ref, element );
49
50         perl_stdout = popen( perl_prog, "r");
51         if( perl_stdout == NULL )
52         {
53                 fprintf( stderr, "popen() failed in ph_extract_element()\n" );
54                 return -1;
55         }
56
57         if( fgets( result, result_size, perl_stdout ) == NULL )
58         {
59                 fprintf( stderr, "fgets() returned NULL in ph_extract_element()\n" );
60                 return -1;
61         }
62
63         retval = pclose( perl_stdout );
64         if( retval )
65         {
66                 fprintf( stderr, "perl returned error %i\n in ph_extract_element()", retval );
67                 return -1;
68         }
69
70         return 0;
71 }
72
73 static int extract_ph_integer( const char *file_path, const char *hash_ref,
74         const char *element )
75 {
76         char result[ 100 ];
77         int retval;
78
79         retval = extract_ph_string( file_path, hash_ref, element, result, sizeof( result ) );
80         if( retval < 0 ) return retval;
81
82         return strtol( result, NULL, 0 );
83 }
84
85 static int check_cal_file( comedi_t *dev, const char *file_path )
86 {
87         char result[ 100 ];
88         int retval;
89
90         retval = extract_ph_string( file_path, "cal", "$cal->{driver_name}",
91                 result, sizeof( result ) );
92         if( retval < 0 ) return retval;
93
94         if( strcmp( comedi_get_driver_name( dev ), result ) )
95         {
96                 fprintf( stderr, "driver name does not match calibration file\n" );
97                 return -1;
98         }
99
100         retval = extract_ph_string( file_path, "cal", "$cal->{board_name}",
101                 result, sizeof( result ) );
102         if( retval < 0 ) return retval;
103
104         if( strcmp( comedi_get_board_name( dev ), result ) )
105         {
106                 fprintf( stderr, "board name does not match calibration file\n" );
107                 return -1;
108         }
109
110         return 0;
111 }
112
113 static inline int num_calibrations( const char *file_path )
114 {
115         return extract_ph_integer( file_path, "cal", "scalar( @{$cal->{calibrations}} )" );
116 }
117
118 static int extract_array_element( const char *file_path, unsigned int cal_index,
119         const char *array_name, unsigned int array_index )
120 {
121         char element[ 100 ];
122
123         snprintf( element, sizeof( element ),
124                 "$cal->{ calibrations }[ %i ]->{ %s }[ %i ]", cal_index, array_name, array_index );
125         return extract_ph_integer( file_path, "cal", element );
126 }
127
128 static int extract_array_length( const char *file_path, unsigned int cal_index,
129         const char *array_name )
130 {
131         char element[ 100 ];
132
133         snprintf( element, sizeof( element ),
134                 "scalar( @{ $cal->{ calibrations }[ %i ]->{ %s } } )", cal_index, array_name );
135         return extract_ph_integer( file_path, "cal", element );
136 }
137
138 static int extract_subdevice( const char *file_path, unsigned int cal_index )
139 {
140         char element[ 100 ];
141
142         snprintf( element, sizeof( element ),
143                 "$cal->{ calibrations }[ %i ]->{ subdevice }", cal_index );
144         return extract_ph_integer( file_path, "cal", element );
145 }
146
147 static int valid_item( const char *file_path, unsigned int cal_index,
148         const char *item_type, unsigned int item )
149 {
150         int num_items, i;
151
152         num_items = extract_array_length( file_path, cal_index, item_type );
153         if( num_items < 0 ) return 0;
154         if( num_items == 0 ) return 1;
155         for( i = 0; i < num_items; i++ )
156         {
157                 if( extract_array_element( file_path, cal_index, item_type, i ) == item )
158                         return 1;
159         }
160
161         return 0;
162 }
163
164 static inline int valid_range( const char *file_path, unsigned int cal_index,
165         unsigned int range )
166 {
167         return valid_item( file_path, cal_index, "ranges", range );
168 }
169
170 static inline int valid_channel( const char *file_path, unsigned int cal_index,
171         unsigned int channel )
172 {
173         return valid_item( file_path, cal_index, "channels", channel );
174 }
175
176 static inline int valid_aref( const char *file_path, unsigned int cal_index,
177         unsigned int aref )
178 {
179         return valid_item( file_path, cal_index, "arefs", aref );
180 }
181
182 static int find_calibration( const char *file_path, unsigned int subdev,
183         unsigned int channel, unsigned int range, unsigned int aref )
184 {
185         int num_cals, i;
186
187         num_cals = num_calibrations( file_path );
188         if( num_cals < 0 ) return num_cals;
189
190         for( i = 0; i < num_cals; i++ )
191         {
192                 if( extract_subdevice( file_path, i ) != subdev ) continue;
193                 if( valid_range( file_path, i, range ) == 0 ) continue;
194                 if( valid_channel( file_path, i, channel ) == 0 ) continue;
195                 if( valid_aref( file_path, i, aref ) == 0 ) continue;
196                 break;
197         }
198         if( i == num_cals ) return -1;
199
200         return i;
201 }
202
203 static int set_calibration( comedi_t *dev, const char *file_path,
204         unsigned int cal_index )
205 {
206         int i, retval, num_caldacs;
207
208         num_caldacs = extract_array_length( file_path, cal_index, "caldacs" );
209         if( num_caldacs < 0 ) return num_caldacs;
210
211         for( i = 0; i < num_caldacs; i++ )
212         {
213                 int subdev, channel, value;
214                 char *element;
215
216                 asprintf( &element, "$cal->{calibrations}[ %i ]->{caldacs}[ %i ]->{subdevice}",
217                         cal_index, i );
218                 subdev = extract_ph_integer( file_path, "cal", element );
219                 free( element );
220                 if( subdev < 0 )
221                 {
222                         fprintf( stderr, "failed to extract subdev\n" );
223                         return subdev;
224                 }
225
226                 asprintf( &element, "$cal->{calibrations}[ %i ]->{caldacs}[ %i ]->{channel}",
227                         cal_index, i );
228                 channel = extract_ph_integer( file_path, "cal", element );
229                 free( element );
230                 if( channel < 0 )
231                 {
232                         fprintf( stderr, "failed to extract channel\n" );
233                         return channel;
234                 }
235
236                 asprintf( &element, "$cal->{calibrations}[ %i ]->{caldacs}[ %i ]->{value}",
237                         cal_index, i );
238                 value = extract_ph_integer( file_path, "cal", element );
239                 free( element );
240                 if( value < 0 )
241                 {
242                         fprintf( stderr, "failed to extract value\n" );
243                         return value;
244                 }
245
246                 retval = comedi_data_write( dev, subdev, channel, 0, 0, value );
247                 if( retval < 0 ) return retval;
248         }
249
250         return 0;
251 }
252
253 EXPORT_SYMBOL(comedi_set_calibration,0.7.20);
254 int comedi_set_calibration( comedi_t *dev, unsigned int subdev, unsigned int channel,
255         unsigned int range, unsigned int aref, const char *cal_file_path )
256 {
257         struct stat file_stats;
258         char file_path[ 1024 ];
259         int retval;
260         int cal_index;
261
262         if( cal_file_path )
263         {
264                 strncpy( file_path, cal_file_path, sizeof( file_path ) );
265         }else
266         {
267                 if( fstat( comedi_fileno( dev ), &file_stats ) < 0 )
268                 {
269                         fprintf( stderr, "failed to get file stats of comedi device file\n" );
270                         return -1;
271                 }
272
273                 snprintf( file_path, sizeof( file_path ), "/etc/comedi/calibrations/%s_0x%lx",
274                         comedi_get_board_name( dev ),
275                         ( unsigned long ) file_stats.st_ino );
276         }
277
278         retval = check_cal_file( dev, file_path );
279         if( retval < 0 ) return retval;
280
281         cal_index = find_calibration( file_path, subdev, channel, range, aref );
282         if( cal_index < 0 ) return cal_index;
283         
284         retval = set_calibration( dev, file_path, cal_index );
285         if( retval < 0 ) return retval;
286
287         return 0;
288 }