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