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