ARPDAS_QNX6 1.0
|
00001 #include <errno.h> 00002 #include <string.h> 00003 #include <stdarg.h> 00004 #include <stdlib.h> 00005 #include <ctype.h> 00006 #include <termios.h> 00007 #include "SerSelector.h" 00008 #include "nortlib.h" 00009 #include "nl_assert.h" 00010 #include "msg.h" 00011 00012 TM_Selectee::TM_Selectee(const char *name, void *data, 00013 unsigned short size) : Selectee() { 00014 init(name, data, size); 00015 } 00016 00017 TM_Selectee::TM_Selectee() : Selectee() { 00018 TMid = 0; 00019 } 00020 00021 void TM_Selectee::init(const char *name, void *data, 00022 unsigned short size) { 00023 TMid = Col_send_init( name, data, size, 0 ); 00024 if ( TMid ) { 00025 fd = TMid->fd; 00026 flags = Selector::Sel_Write; 00027 } 00028 } 00029 00030 /** 00031 * Issues Col_send_reset() 00032 */ 00033 TM_Selectee::~TM_Selectee() { 00034 Col_send_reset(TMid); 00035 fd = -1; 00036 } 00037 00038 /** 00039 * Calls Col_send() and sets gflag(0) 00040 */ 00041 int TM_Selectee::ProcessData(int flag) { 00042 Col_send(TMid); 00043 Stor->set_gflag(0); 00044 return 0; 00045 } 00046 00047 /** 00048 * Opens the specified channel using tm_dev_name() and 00049 * tm_open_name(). By default, tm_open_name will produce a 00050 * fatal error if the resulting path is not found. 00051 */ 00052 Cmd_Selectee::Cmd_Selectee( const char *name ) : 00053 Ser_Sel(tm_dev_name(name), O_RDONLY, 0) {} 00054 Cmd_Selectee::Cmd_Selectee( const char *name, int bufsz ) : 00055 Ser_Sel(tm_dev_name(name), O_RDONLY, bufsz) {} 00056 00057 int Cmd_Selectee::ProcessData(int flag) { 00058 return 1; 00059 } 00060 00061 /** 00062 * @param path The full path to the serial device. If path == NULL, 00063 * the fd will not be opened. 00064 * @param open_flags Flags from <fcntl.h> passed to open() 00065 * @param bufsz The size buffer to be allocated. 00066 */ 00067 Ser_Sel::Ser_Sel(const char *path, int open_flags, int bufsz ) 00068 : Selectee() { 00069 sersel_init(); 00070 init(path, open_flags, bufsz); 00071 } 00072 00073 Ser_Sel::Ser_Sel() : Selectee() { 00074 sersel_init(); 00075 } 00076 00077 /** 00078 * @param path The full path to the serial device. If path == NULL, 00079 * the fd will not be opened. 00080 * @param open_flags Flags from <fcntl.h> passed to open() 00081 * @param bufsz The size buffer to be allocated. 00082 */ 00083 void Ser_Sel::init(const char *path, int open_flags, int bufsz) { 00084 if (path == 0) { 00085 fd = -1; 00086 } else { 00087 fd = tm_open_name(path, NULL, open_flags); 00088 switch (open_flags & O_ACCMODE) { 00089 case O_RDWR: 00090 case O_RDONLY: 00091 flags |= Selector::Sel_Read; 00092 break; 00093 } 00094 } 00095 if (bufsz > 0) { 00096 buf = (unsigned char *)new_memory(bufsz); 00097 bufsize = bufsz; 00098 } 00099 } 00100 00101 void Ser_Sel::sersel_init() { 00102 fd = -1; 00103 buf = 0; 00104 bufsize = 0; 00105 nc = cp = 0; 00106 n_fills = n_empties = 0; 00107 n_eagain = n_eintr = 0; 00108 total_errors = 0; 00109 total_suppressed = 0; 00110 n_errors = 0; 00111 n_suppressed = 0; 00112 } 00113 00114 /** 00115 * Frees the allocated buffer and reports statistics. 00116 */ 00117 Ser_Sel::~Ser_Sel() { 00118 if (buf) free_memory(buf); 00119 buf = 0; 00120 nl_error( 0, "n_fills: %d n_empties: %d " 00121 "total_errors: %d total_suppressed: %d", 00122 n_fills, n_empties, total_errors, total_suppressed ); 00123 nl_error( 0, "n_eagain: %d n_eintr: %d", n_eagain, n_eintr); 00124 } 00125 00126 /** 00127 * Initializes the serial parameters for the device. The min 00128 * and time parameters can be used to optimize reads. See 00129 * tcsetattr VMIN and VTIME parameters for more information. 00130 * @param baud The desired baud rate 00131 * @param bits number of data bits (5-8) 00132 * @param par 'n', 'e', 'o', 'm', 's' for none, even, odd, mark or space. 00133 * @param stopbits The number of stop bits: 1 or 2 00134 * @param min The minimum number of characters to respond to 00135 * @param time The time gap value 00136 */ 00137 void Ser_Sel::setup( int baud, int bits, char par, int stopbits, 00138 int min, int time ) { 00139 struct termios termios_p; 00140 int bitsflag; 00141 00142 if ( fd < 0 ) return; 00143 if ( tcgetattr( fd, &termios_p) ) { 00144 nl_error( 2, "Error on tcgetattr: %s", strerror(errno) ); 00145 return; 00146 } 00147 termios_p.c_iflag = 0; 00148 termios_p.c_lflag &= ~(ECHO|ICANON|ISIG|ECHOE|ECHOK|ECHONL); 00149 termios_p.c_cflag = CLOCAL|CREAD; 00150 termios_p.c_oflag &= ~(OPOST); 00151 termios_p.c_ispeed = termios_p.c_ospeed = baud; 00152 switch (bits) { 00153 case 5: bitsflag = CS5; break; 00154 case 6: bitsflag = CS6; break; 00155 case 7: bitsflag = CS7; break; 00156 case 8: bitsflag = CS8; break; 00157 default: 00158 nl_error( 3, "Invalid bits value: %d", bits ); 00159 } 00160 termios_p.c_cflag |= bitsflag; 00161 switch (par) { 00162 case 'n': bitsflag = 0; break; 00163 case 'e': bitsflag = PARENB; break; 00164 case 'o': bitsflag = PARENB|PARODD; break; 00165 case 'm': bitsflag = PARENB|PARODD|PARSTK; break; 00166 case 's': bitsflag = PARENB|PARSTK; break; 00167 default: 00168 nl_error( 3, "Invalid parity selector: '%c'", par ); 00169 } 00170 termios_p.c_cflag |= bitsflag; 00171 switch (stopbits) { 00172 case 1: break; 00173 case 2: termios_p.c_cflag |= CSTOPB; break; 00174 default: 00175 nl_error(3, "Invalid number of stop bits: %d", stopbits ); 00176 } 00177 termios_p.c_cc[VMIN] = min; 00178 termios_p.c_cc[VTIME] = time; 00179 if ( tcsetattr(fd, TCSANOW, &termios_p) ) 00180 nl_error( 2, "Error on tcsetattr: %s", strerror(errno) ); 00181 } 00182 00183 /** 00184 * Reads characters from the device, reporting any errors. 00185 * Guarantees that buf is NUL-terminated, and sets nc to the 00186 * total number of characters. Each call to fillbuf() increments 00187 * the n_fills counter, which is reported at termination 00188 * @return non-zero on error. 00189 */ 00190 int Ser_Sel::fillbuf() { 00191 int i; 00192 if (!buf) nl_error(4, "Ser_Sel::fillbuf with no buffer"); 00193 ++n_fills; 00194 i = read( fd, &buf[nc], bufsize - 1 - nc ); 00195 if ( i < 0 ) { 00196 if ( errno == EAGAIN ) { 00197 ++n_eagain; 00198 } else if (errno == EINTR) { 00199 ++n_eintr; 00200 } else { 00201 nl_error( 2, "Error %d on read from serial port", errno ); 00202 return 1; 00203 } 00204 return 0; 00205 } 00206 nc += i; 00207 buf[nc] = '\0'; 00208 return 0; 00209 } 00210 00211 /** 00212 * Each call to consume() increments the n_empties counter which is 00213 * reported at termination. If n_fills is much greater than n_empties, 00214 * you may need to adjust your min and time settings for more efficient 00215 * operation. 00216 * @param nchars number of characters to remove from front of buffer 00217 */ 00218 void Ser_Sel::consume(int nchars) { 00219 if ( nchars > 0 ) { 00220 ++n_empties; 00221 if ( nchars < nc ) { 00222 int nb = nc - nchars; 00223 memmove(&buf[0], &buf[nchars], nb+1); 00224 nc = nb; 00225 } else { 00226 nc = 0; 00227 } 00228 cp = 0; 00229 } 00230 } 00231 00232 /** 00233 * Invokes fillbuf() until there is no input remaining. 00234 */ 00235 void Ser_Sel::flush_input() { 00236 do { 00237 nc = cp = 0; 00238 if (fillbuf()) return; 00239 } while (nc > 0); 00240 } 00241 00242 #define QERR_THRESHOLD 5 00243 /** 00244 * Reports the error message, provided the qualified error count 00245 * is not above the limit. report_ok() will decrement the qualified 00246 * error count. It is assumed that all messages are of severity 00247 * MSG_ERR. Messages at other levels, either more or less severe, 00248 * should be sent directly to msg(). 00249 */ 00250 void Ser_Sel::report_err( const char *fmt, ... ) { 00251 ++total_errors; 00252 if ( n_errors < QERR_THRESHOLD ) 00253 ++n_errors; 00254 if ( n_suppressed == 0 && n_errors < QERR_THRESHOLD ) { 00255 va_list args; 00256 00257 va_start(args, fmt); 00258 msgv( 2, fmt, args ); 00259 va_end(args); 00260 if (nc) 00261 msg( 2, "Input was: '%s'", ascii_escape((char*)buf, nc) ); 00262 } else { 00263 if ( !n_suppressed ) 00264 msg( 2, "Error threshold reached: suppressing errors" ); 00265 ++n_suppressed; 00266 ++total_suppressed; 00267 } 00268 } 00269 00270 /** 00271 * Indicate that data has successfully been received. 00272 */ 00273 void Ser_Sel::report_ok() { 00274 if ( n_errors > 0 ) { 00275 if ( --n_errors <= 0 && n_suppressed ) { 00276 msg( 0, "Error recovery: %d error messages suppressed", n_suppressed ); 00277 n_suppressed = 0; 00278 } 00279 } 00280 } 00281 00282 /** 00283 * Parsing utility function that searches forward in the buffer for the 00284 * specified start character. Updates cp to point just past the start 00285 * char. If the character is not found, the buffer is emptied. 00286 * @param c The search character 00287 * @return zero if the character is found. 00288 */ 00289 int Ser_Sel::not_found(unsigned char c) { 00290 while ( cp < nc ) { 00291 if ( buf[cp++] == c ) 00292 return 0; 00293 } 00294 if ( nc ) { 00295 report_err( "Synch char '%c' not found", c ); 00296 nc = cp = 0; 00297 } 00298 return 1; 00299 } 00300 00301 /** 00302 * Parsing utility function to read in a hex integer starting 00303 * at the current position. Integer may be proceeded by optional 00304 * whitespace. 00305 * @param[out] hexval The integer value 00306 * @return zero if an integer was converted, non-zero if the current char is not a digit. 00307 */ 00308 int Ser_Sel::not_hex( unsigned short &hexval ) { 00309 while (cp < nc && isspace(buf[cp])) 00310 ++cp; 00311 if (! isxdigit(buf[cp])) { 00312 if (cp < nc) 00313 report_err("No hex digits at col %d", cp); 00314 return 1; 00315 } 00316 while ( isxdigit(buf[cp]) ) { 00317 unsigned short digval = isdigit(buf[cp]) ? ( buf[cp] - '0' ) : 00318 ( tolower(buf[cp]) - 'a' + 10 ); 00319 hexval = hexval * 16 + digval; 00320 ++cp; 00321 } 00322 return 0; 00323 } 00324 00325 /** 00326 * Parsing utility function to read in a decimal integer starting 00327 * at the current position. Integer may be proceeded by optional 00328 * whitespace and an optional sign. 00329 * @param[out] val The integer value 00330 * @return zero if an integer was converted, non-zero if the current char is not a digit. 00331 */ 00332 int Ser_Sel::not_int( int &val ) { 00333 bool negative = false; 00334 // fillbuf() guarantees the buffer will be NUL-terminated, so any check 00335 // that will fail on a NUL is OK without checking the cp < nc 00336 while (isspace(buf[cp])) 00337 ++cp; 00338 if (buf[cp] == '-') { 00339 negative = true; 00340 ++cp; 00341 } else if (buf[cp] == '+') ++cp; 00342 if ( isdigit(buf[cp]) ) { 00343 val = buf[cp++] - '0'; 00344 while ( isdigit(buf[cp]) ) { 00345 val = 10*val + buf[cp++] - '0'; 00346 } 00347 if (negative) val = -val; 00348 return 0; 00349 } else { 00350 if ( cp < nc ) 00351 report_err( "Expected int at column %d", cp ); 00352 return 1; 00353 } 00354 } 00355 00356 /** 00357 * Parsing utility function to check that the string matches the 00358 * input at the current position. On success, advances cp to just 00359 * after the matched string. On failure, cp points to the first 00360 * character that does not match. If only a partial record was 00361 * received, that could be the NUL at the end of the buffer. 00362 * @param str The comparison string. 00363 * @return zero if the string matches the input buffer. 00364 */ 00365 int Ser_Sel::not_str( const char *str_in, unsigned int len ) { 00366 unsigned int start_cp = cp; 00367 unsigned int i; 00368 const unsigned char *str = (const unsigned char *)str_in; 00369 if ( cp < 0 || cp > nc || nc < 0 || buf == 0 ) 00370 nl_error( 4, "Ser_Sel precondition failed: " 00371 "cp = %d, nc = %d, buf %s", 00372 cp, nc, buf ? "not NULL" : "is NULL" ); 00373 for (i = 0; i < len; ++i) { 00374 if ( str[i] != buf[start_cp+i] ) { 00375 if ( cp < nc ) 00376 report_err( "Expected string '%s' at column %d", 00377 ascii_escape(str_in, len), start_cp ); 00378 return 1; 00379 } 00380 ++cp; 00381 } 00382 return 0; 00383 } 00384 00385 int Ser_Sel::not_str( const char *str ) { 00386 return not_str(str, strlen(str)); 00387 } 00388 00389 int Ser_Sel::not_str(const std::string &s) { 00390 return not_str(s.c_str(), s.length()); 00391 } 00392 00393 00394 /** 00395 * Parsing utility function to convert a string in the input 00396 * buffer to a float value. Updates cp to point just after the 00397 * converted string on success. 00398 * @param val[out] The converted value 00399 * @return zero if the conversion succeeded. 00400 */ 00401 int Ser_Sel::not_float( float &val ) { 00402 char *endptr; 00403 int ncf; 00404 if ( cp < 0 || cp > nc || nc < 0 || nc >= bufsize || buf == 0 ) 00405 msg( 4, "Ser_Sel precondition failed: " 00406 "cp = %d, nc = %d, bufsize = %d, buf %s", 00407 cp, nc, bufsize, buf ? "not NULL" : "is NULL" ); 00408 val = strtof( (char*)&buf[cp], &endptr ); 00409 ncf = endptr - (char*)&buf[cp]; 00410 if ( ncf == 0 ) { 00411 if ( cp < nc ) 00412 report_err( "Expected float at column %d", cp ); 00413 return 1; 00414 } else { 00415 nl_assert( ncf > 0 && cp + ncf <= nc ); 00416 cp += ncf; 00417 return 0; 00418 } 00419 } 00420 00421 const char *ascii_escape(const char *ibuf, int len) { 00422 static std::string s; 00423 char snbuf[8]; 00424 int ix = 0, nb; 00425 s.clear(); 00426 while (ix < len ) { 00427 unsigned char c = ibuf[ix++]; 00428 if ( isprint(c) ) { 00429 s.push_back(c); 00430 } else { 00431 switch ( c ) { 00432 case '\n': 00433 s.push_back('\\'); 00434 s.push_back('n'); 00435 break; 00436 case '\r': 00437 s.push_back('\\'); 00438 s.push_back('r'); 00439 break; 00440 case '\t': 00441 s.push_back('\\'); 00442 s.push_back('t'); 00443 break; 00444 default: 00445 nb = snprintf( snbuf, 8, "\\x%02x", c); 00446 s.append(snbuf); 00447 break; 00448 } 00449 } 00450 } 00451 return s.c_str(); 00452 } 00453 00454 const char *ascii_escape(const std::string &s) { 00455 return ascii_escape(s.c_str(), s.length()); 00456 } 00457 00458 /** 00459 * Named differently to disambiguate from C version 00460 * in nortlib2. Invokes the C++ versions, which 00461 * have no inherent length limitation and can deal 00462 * with embedded NULs. 00463 */ 00464 const char *ascii_esc(const char *str) { 00465 return ascii_escape(str, strlen(str)); 00466 }