mbed library sources. Supersedes mbed-src.
Dependents: Nucleo_Hello_Encoder BLE_iBeaconScan AM1805_DEMO DISCO-F429ZI_ExportTemplate1 ... more
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 int ATCmdParser::vscanf(const char *format, std::va_list args) 00128 { 00129 // Since format is const, we need to copy it into our buffer to 00130 // add the line's null terminator and clobber value-matches with asterisks. 00131 // 00132 // We just use the beginning of the buffer to avoid unnecessary allocations. 00133 int i = 0; 00134 int offset = 0; 00135 00136 while (format[i]) { 00137 if (format[i] == '%' && format[i + 1] != '%' && format[i + 1] != '*') { 00138 _buffer[offset++] = '%'; 00139 _buffer[offset++] = '*'; 00140 i++; 00141 } else { 00142 _buffer[offset++] = format[i++]; 00143 } 00144 } 00145 00146 // Scanf has very poor support for catching errors 00147 // fortunately, we can abuse the %n specifier to determine 00148 // if the entire string was matched. 00149 _buffer[offset++] = '%'; 00150 _buffer[offset++] = 'n'; 00151 _buffer[offset++] = 0; 00152 00153 // To workaround scanf's lack of error reporting, we actually 00154 // make two passes. One checks the validity with the modified 00155 // format string that only stores the matched characters (%n). 00156 // The other reads in the actual matched values. 00157 // 00158 // We keep trying the match until we succeed or some other error 00159 // derails us. 00160 int j = 0; 00161 00162 while (true) { 00163 // Ran out of space 00164 if (j + 1 >= _buffer_size - offset) { 00165 return false; 00166 } 00167 // Receive next character 00168 int c = getc(); 00169 if (c < 0) { 00170 return -1; 00171 } 00172 _buffer[offset + j++] = c; 00173 _buffer[offset + j] = 0; 00174 00175 // Check for match 00176 int count = -1; 00177 sscanf(_buffer + offset, _buffer, &count); 00178 00179 // We only succeed if all characters in the response are matched 00180 if (count == j) { 00181 // Store the found results 00182 vsscanf(_buffer + offset, format, args); 00183 return j; 00184 } 00185 } 00186 } 00187 00188 00189 // Command parsing with line handling 00190 bool ATCmdParser::vsend(const char *command, std::va_list args) 00191 { 00192 // Create and send command 00193 if (vsprintf(_buffer, command, args) < 0) { 00194 return false; 00195 } 00196 00197 for (int i = 0; _buffer[i]; i++) { 00198 if (putc(_buffer[i]) < 0) { 00199 return false; 00200 } 00201 } 00202 00203 // Finish with newline 00204 for (size_t i = 0; _output_delimiter[i]; i++) { 00205 if (putc(_output_delimiter[i]) < 0) { 00206 return false; 00207 } 00208 } 00209 00210 debug_if(_dbg_on, "AT> %s\n", _buffer); 00211 return true; 00212 } 00213 00214 bool ATCmdParser::vrecv(const char *response, std::va_list args) 00215 { 00216 restart: 00217 _aborted = false; 00218 // Iterate through each line in the expected response 00219 // response being NULL means we just want to check for OOBs 00220 while (!response || response[0]) { 00221 // Since response is const, we need to copy it into our buffer to 00222 // add the line's null terminator and clobber value-matches with asterisks. 00223 // 00224 // We just use the beginning of the buffer to avoid unnecessary allocations. 00225 int i = 0; 00226 int offset = 0; 00227 bool whole_line_wanted = false; 00228 00229 while (response && response[i]) { 00230 if (response[i] == '%' && response[i + 1] != '%' && response[i + 1] != '*') { 00231 _buffer[offset++] = '%'; 00232 _buffer[offset++] = '*'; 00233 i++; 00234 } else { 00235 _buffer[offset++] = response[i++]; 00236 // Find linebreaks, taking care not to be fooled if they're in a %[^\n] conversion specification 00237 if (response[i - 1] == '\n' && !(i >= 3 && response[i - 3] == '[' && response[i - 2] == '^')) { 00238 whole_line_wanted = true; 00239 break; 00240 } 00241 } 00242 } 00243 00244 // Scanf has very poor support for catching errors 00245 // fortunately, we can abuse the %n specifier to determine 00246 // if the entire string was matched. 00247 _buffer[offset++] = '%'; 00248 _buffer[offset++] = 'n'; 00249 _buffer[offset++] = 0; 00250 00251 debug_if(_dbg_on, "AT? %s\n", _buffer); 00252 // To workaround scanf's lack of error reporting, we actually 00253 // make two passes. One checks the validity with the modified 00254 // format string that only stores the matched characters (%n). 00255 // The other reads in the actual matched values. 00256 // 00257 // We keep trying the match until we succeed or some other error 00258 // derails us. 00259 int j = 0; 00260 00261 while (true) { 00262 // If just peeking for OOBs, and at start of line, check 00263 // readability 00264 if (!response && j == 0 && !_fh->readable()) { 00265 return false; 00266 } 00267 // Receive next character 00268 int c = getc(); 00269 if (c < 0) { 00270 debug_if(_dbg_on, "AT(Timeout)\n"); 00271 return false; 00272 } 00273 // Simplify newlines (borrowed from retarget.cpp) 00274 if ((c == CR && _in_prev != LF) || 00275 (c == LF && _in_prev != CR)) { 00276 _in_prev = c; 00277 c = '\n'; 00278 } else if ((c == CR && _in_prev == LF) || 00279 (c == LF && _in_prev == CR)) { 00280 _in_prev = c; 00281 // onto next character 00282 continue; 00283 } else { 00284 _in_prev = c; 00285 } 00286 _buffer[offset + j++] = c; 00287 _buffer[offset + j] = 0; 00288 00289 // Check for oob data 00290 for (struct oob *oob = _oobs; oob; oob = oob->next) { 00291 if ((unsigned)j == oob->len && memcmp( 00292 oob->prefix, _buffer + offset, oob->len) == 0) { 00293 debug_if(_dbg_on, "AT! %s\n", oob->prefix); 00294 _oob_cb_count++; 00295 oob->cb(); 00296 00297 if (_aborted) { 00298 debug_if(_dbg_on, "AT(Aborted)\n"); 00299 return false; 00300 } 00301 // oob may have corrupted non-reentrant buffer, 00302 // so we need to set it up again 00303 goto restart; 00304 } 00305 } 00306 00307 // Check for match 00308 int count = -1; 00309 if (whole_line_wanted && c != '\n') { 00310 // Don't attempt scanning until we get delimiter if they included it in format 00311 // This allows recv("Foo: %s\n") to work, and not match with just the first character of a string 00312 // (scanf does not itself match whitespace in its format string, so \n is not significant to it) 00313 } else if (response) { 00314 sscanf(_buffer + offset, _buffer, &count); 00315 } 00316 00317 // We only succeed if all characters in the response are matched 00318 if (count == j) { 00319 debug_if(_dbg_on, "AT= %s\n", _buffer + offset); 00320 // Reuse the front end of the buffer 00321 memcpy(_buffer, response, i); 00322 _buffer[i] = 0; 00323 00324 // Store the found results 00325 vsscanf(_buffer + offset, _buffer, args); 00326 00327 // Jump to next line and continue parsing 00328 response += i; 00329 break; 00330 } 00331 00332 // Clear the buffer when we hit a newline or ran out of space 00333 // running out of space usually means we ran into binary data 00334 if (c == '\n' || j + 1 >= _buffer_size - offset) { 00335 debug_if(_dbg_on, "AT< %s", _buffer + offset); 00336 j = 0; 00337 } 00338 } 00339 } 00340 00341 return true; 00342 } 00343 00344 // Mapping to vararg functions 00345 int ATCmdParser::printf(const char *format, ...) 00346 { 00347 std::va_list args; 00348 va_start(args, format); 00349 int res = vprintf(format, args); 00350 va_end(args); 00351 return res; 00352 } 00353 00354 int ATCmdParser::scanf(const char *format, ...) 00355 { 00356 std::va_list args; 00357 va_start(args, format); 00358 int res = vscanf(format, args); 00359 va_end(args); 00360 return res; 00361 } 00362 00363 bool ATCmdParser::send(const char *command, ...) 00364 { 00365 std::va_list args; 00366 va_start(args, command); 00367 bool res = vsend(command, args); 00368 va_end(args); 00369 return res; 00370 } 00371 00372 bool ATCmdParser::recv(const char *response, ...) 00373 { 00374 std::va_list args; 00375 va_start(args, response); 00376 bool res = vrecv(response, args); 00377 va_end(args); 00378 return res; 00379 } 00380 00381 // oob registration 00382 void ATCmdParser::oob(const char *prefix, Callback<void()> cb) 00383 { 00384 struct oob *oob = new struct oob; 00385 oob->len = strlen(prefix); 00386 oob->prefix = prefix; 00387 oob->cb = cb; 00388 oob->next = _oobs; 00389 _oobs = oob; 00390 } 00391 00392 void ATCmdParser::abort() 00393 { 00394 _aborted = true; 00395 } 00396 00397 bool ATCmdParser::process_oob() 00398 { 00399 int pre_count = _oob_cb_count; 00400 recv(NULL); 00401 return _oob_cb_count != pre_count; 00402 } 00403 00404 } 00405
Generated on Tue Jul 12 2022 20:41:14 by 1.7.2