Marco Zecchini
/
Example_RTOS
Rtos API example
Embed:
(wiki syntax)
Show/hide line numbers
ATCmdParser.cpp
00001 /* Copyright (c) 2017 ARM Limited 00002 * 00003 * Licensed under the Apache License, Version 2.0 (the "License"); 00004 * you may not use this file except in compliance with the License. 00005 * You may obtain a copy of the License at 00006 * 00007 * http://www.apache.org/licenses/LICENSE-2.0 00008 * 00009 * Unless required by applicable law or agreed to in writing, software 00010 * distributed under the License is distributed on an "AS IS" BASIS, 00011 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00012 * See the License for the specific language governing permissions and 00013 * limitations under the License. 00014 * 00015 * @section DESCRIPTION 00016 * 00017 * Parser for the AT command syntax 00018 * 00019 */ 00020 00021 #include "ATCmdParser.h" 00022 #include "mbed_poll.h" 00023 #include "mbed_debug.h" 00024 00025 #ifdef LF 00026 #undef LF 00027 #define LF 10 00028 #else 00029 #define LF 10 00030 #endif 00031 00032 #ifdef CR 00033 #undef CR 00034 #define CR 13 00035 #else 00036 #define CR 13 00037 #endif 00038 00039 // getc/putc handling with timeouts 00040 int ATCmdParser::putc(char c) 00041 { 00042 pollfh fhs; 00043 fhs.fh = _fh; 00044 fhs.events = POLLOUT; 00045 00046 int count = poll(&fhs, 1, _timeout); 00047 if (count > 0 && (fhs.revents & POLLOUT)) { 00048 return _fh->write(&c, 1) == 1 ? 0 : -1; 00049 } else { 00050 return -1; 00051 } 00052 } 00053 00054 int ATCmdParser::getc() 00055 { 00056 pollfh fhs; 00057 fhs.fh = _fh; 00058 fhs.events = POLLIN; 00059 00060 int count = poll(&fhs, 1, _timeout); 00061 if (count > 0 && (fhs.revents & POLLIN)) { 00062 unsigned char ch; 00063 return _fh->read(&ch, 1) == 1 ? ch : -1; 00064 } else { 00065 return -1; 00066 } 00067 } 00068 00069 void ATCmdParser::flush() 00070 { 00071 while (_fh->readable()) { 00072 unsigned char ch; 00073 _fh->read(&ch, 1); 00074 } 00075 } 00076 00077 00078 // read/write handling with timeouts 00079 int ATCmdParser::write(const char *data, int size) 00080 { 00081 int i = 0; 00082 for ( ; i < size; i++) { 00083 if (putc(data[i]) < 0) { 00084 return -1; 00085 } 00086 } 00087 return i; 00088 } 00089 00090 int ATCmdParser::read(char *data, int size) 00091 { 00092 int i = 0; 00093 for ( ; i < size; i++) { 00094 int c = getc(); 00095 if (c < 0) { 00096 return -1; 00097 } 00098 data[i] = c; 00099 } 00100 return i; 00101 } 00102 00103 00104 // printf/scanf handling 00105 int ATCmdParser::vprintf(const char *format, va_list args) 00106 { 00107 00108 if (vsprintf(_buffer, format, args) < 0) { 00109 return false; 00110 } 00111 00112 int i = 0; 00113 for ( ; _buffer[i]; i++) { 00114 if (putc(_buffer[i]) < 0) { 00115 return -1; 00116 } 00117 } 00118 return i; 00119 } 00120 00121 int ATCmdParser::vscanf(const char *format, va_list args) 00122 { 00123 // Since format is const, we need to copy it into our buffer to 00124 // add the line's null terminator and clobber value-matches with asterisks. 00125 // 00126 // We just use the beginning of the buffer to avoid unnecessary allocations. 00127 int i = 0; 00128 int offset = 0; 00129 00130 while (format[i]) { 00131 if (format[i] == '%' && format[i+1] != '%' && format[i+1] != '*') { 00132 _buffer[offset++] = '%'; 00133 _buffer[offset++] = '*'; 00134 i++; 00135 } else { 00136 _buffer[offset++] = format[i++]; 00137 } 00138 } 00139 00140 // Scanf has very poor support for catching errors 00141 // fortunately, we can abuse the %n specifier to determine 00142 // if the entire string was matched. 00143 _buffer[offset++] = '%'; 00144 _buffer[offset++] = 'n'; 00145 _buffer[offset++] = 0; 00146 00147 // To workaround scanf's lack of error reporting, we actually 00148 // make two passes. One checks the validity with the modified 00149 // format string that only stores the matched characters (%n). 00150 // The other reads in the actual matched values. 00151 // 00152 // We keep trying the match until we succeed or some other error 00153 // derails us. 00154 int j = 0; 00155 00156 while (true) { 00157 // Ran out of space 00158 if (j+1 >= _buffer_size - offset) { 00159 return false; 00160 } 00161 // Recieve next character 00162 int c = getc(); 00163 if (c < 0) { 00164 return -1; 00165 } 00166 _buffer[offset + j++] = c; 00167 _buffer[offset + j] = 0; 00168 00169 // Check for match 00170 int count = -1; 00171 sscanf(_buffer+offset, _buffer, &count); 00172 00173 // We only succeed if all characters in the response are matched 00174 if (count == j) { 00175 // Store the found results 00176 vsscanf(_buffer+offset, format, args); 00177 return j; 00178 } 00179 } 00180 } 00181 00182 00183 // Command parsing with line handling 00184 bool ATCmdParser::vsend(const char *command, va_list args) 00185 { 00186 // Create and send command 00187 if (vsprintf(_buffer, command, args) < 0) { 00188 return false; 00189 } 00190 00191 for (int i = 0; _buffer[i]; i++) { 00192 if (putc(_buffer[i]) < 0) { 00193 return false; 00194 } 00195 } 00196 00197 // Finish with newline 00198 for (size_t i = 0; _output_delimiter[i]; i++) { 00199 if (putc(_output_delimiter[i]) < 0) { 00200 return false; 00201 } 00202 } 00203 00204 debug_if(_dbg_on, "AT> %s\n", _buffer); 00205 return true; 00206 } 00207 00208 bool ATCmdParser::vrecv(const char *response, va_list args) 00209 { 00210 restart: 00211 _aborted = false; 00212 // Iterate through each line in the expected response 00213 while (response[0]) { 00214 // Since response is const, we need to copy it into our buffer to 00215 // add the line's null terminator and clobber value-matches with asterisks. 00216 // 00217 // We just use the beginning of the buffer to avoid unnecessary allocations. 00218 int i = 0; 00219 int offset = 0; 00220 bool whole_line_wanted = false; 00221 00222 while (response[i]) { 00223 if (response[i] == '%' && response[i+1] != '%' && response[i+1] != '*') { 00224 _buffer[offset++] = '%'; 00225 _buffer[offset++] = '*'; 00226 i++; 00227 } else { 00228 _buffer[offset++] = response[i++]; 00229 // Find linebreaks, taking care not to be fooled if they're in a %[^\n] conversion specification 00230 if (response[i - 1] == '\n' && !(i >= 3 && response[i-3] == '[' && response[i-2] == '^')) { 00231 whole_line_wanted = true; 00232 break; 00233 } 00234 } 00235 } 00236 00237 // Scanf has very poor support for catching errors 00238 // fortunately, we can abuse the %n specifier to determine 00239 // if the entire string was matched. 00240 _buffer[offset++] = '%'; 00241 _buffer[offset++] = 'n'; 00242 _buffer[offset++] = 0; 00243 00244 debug_if(_dbg_on, "AT? %s\n", _buffer); 00245 // To workaround scanf's lack of error reporting, we actually 00246 // make two passes. One checks the validity with the modified 00247 // format string that only stores the matched characters (%n). 00248 // The other reads in the actual matched values. 00249 // 00250 // We keep trying the match until we succeed or some other error 00251 // derails us. 00252 int j = 0; 00253 00254 while (true) { 00255 // Receive next character 00256 int c = getc(); 00257 if (c < 0) { 00258 debug_if(_dbg_on, "AT(Timeout)\n"); 00259 return false; 00260 } 00261 // Simplify newlines (borrowed from retarget.cpp) 00262 if ((c == CR && _in_prev != LF) || 00263 (c == LF && _in_prev != CR)) { 00264 _in_prev = c; 00265 c = '\n'; 00266 } else if ((c == CR && _in_prev == LF) || 00267 (c == LF && _in_prev == CR)) { 00268 _in_prev = c; 00269 // onto next character 00270 continue; 00271 } else { 00272 _in_prev = c; 00273 } 00274 _buffer[offset + j++] = c; 00275 _buffer[offset + j] = 0; 00276 00277 // Check for oob data 00278 for (struct oob *oob = _oobs; oob; oob = oob->next) { 00279 if ((unsigned)j == oob->len && memcmp( 00280 oob->prefix, _buffer+offset, oob->len) == 0) { 00281 debug_if(_dbg_on, "AT! %s\n", oob->prefix); 00282 oob->cb(); 00283 00284 if (_aborted) { 00285 debug_if(_dbg_on, "AT(Aborted)\n"); 00286 return false; 00287 } 00288 // oob may have corrupted non-reentrant buffer, 00289 // so we need to set it up again 00290 goto restart; 00291 } 00292 } 00293 00294 // Check for match 00295 int count = -1; 00296 if (whole_line_wanted && c != '\n') { 00297 // Don't attempt scanning until we get delimiter if they included it in format 00298 // This allows recv("Foo: %s\n") to work, and not match with just the first character of a string 00299 // (scanf does not itself match whitespace in its format string, so \n is not significant to it) 00300 } else { 00301 sscanf(_buffer+offset, _buffer, &count); 00302 } 00303 00304 // We only succeed if all characters in the response are matched 00305 if (count == j) { 00306 debug_if(_dbg_on, "AT= %s\n", _buffer+offset); 00307 // Reuse the front end of the buffer 00308 memcpy(_buffer, response, i); 00309 _buffer[i] = 0; 00310 00311 // Store the found results 00312 vsscanf(_buffer+offset, _buffer, args); 00313 00314 // Jump to next line and continue parsing 00315 response += i; 00316 break; 00317 } 00318 00319 // Clear the buffer when we hit a newline or ran out of space 00320 // running out of space usually means we ran into binary data 00321 if (c == '\n' || j+1 >= _buffer_size - offset) { 00322 debug_if(_dbg_on, "AT< %s", _buffer+offset); 00323 j = 0; 00324 } 00325 } 00326 } 00327 00328 return true; 00329 } 00330 00331 // Mapping to vararg functions 00332 int ATCmdParser::printf(const char *format, ...) 00333 { 00334 va_list args; 00335 va_start(args, format); 00336 int res = vprintf(format, args); 00337 va_end(args); 00338 return res; 00339 } 00340 00341 int ATCmdParser::scanf(const char *format, ...) 00342 { 00343 va_list args; 00344 va_start(args, format); 00345 int res = vscanf(format, args); 00346 va_end(args); 00347 return res; 00348 } 00349 00350 bool ATCmdParser::send(const char *command, ...) 00351 { 00352 va_list args; 00353 va_start(args, command); 00354 bool res = vsend(command, args); 00355 va_end(args); 00356 return res; 00357 } 00358 00359 bool ATCmdParser::recv(const char *response, ...) 00360 { 00361 va_list args; 00362 va_start(args, response); 00363 bool res = vrecv(response, args); 00364 va_end(args); 00365 return res; 00366 } 00367 00368 // oob registration 00369 void ATCmdParser::oob(const char *prefix, Callback<void()> cb) 00370 { 00371 struct oob *oob = new struct oob; 00372 oob->len = strlen(prefix); 00373 oob->prefix = prefix; 00374 oob->cb = cb; 00375 oob->next = _oobs; 00376 _oobs = oob; 00377 } 00378 00379 void ATCmdParser::abort() 00380 { 00381 _aborted = true; 00382 } 00383 00384 bool ATCmdParser::process_oob() 00385 { 00386 if (!_fh->readable()) { 00387 return false; 00388 } 00389 00390 int i = 0; 00391 while (true) { 00392 // Receive next character 00393 int c = getc(); 00394 if (c < 0) { 00395 return false; 00396 } 00397 _buffer[i++] = c; 00398 _buffer[i] = 0; 00399 00400 // Check for oob data 00401 struct oob *oob = _oobs; 00402 while (oob) { 00403 if (i == (int)oob->len && memcmp( 00404 oob->prefix, _buffer, oob->len) == 0) { 00405 debug_if(_dbg_on, "AT! %s\r\n", oob->prefix); 00406 oob->cb(); 00407 return true; 00408 } 00409 oob = oob->next; 00410 } 00411 00412 // Clear the buffer when we hit a newline or ran out of space 00413 // running out of space usually means we ran into binary data 00414 if (i+1 >= _buffer_size || 00415 strcmp(&_buffer[i-_output_delim_size], _output_delimiter) == 0) { 00416 00417 debug_if(_dbg_on, "AT< %s", _buffer); 00418 i = 0; 00419 } 00420 } 00421 } 00422 00423
Generated on Sun Jul 17 2022 08:25:20 by 1.7.2