mbed library sources. Supersedes mbed-src.

Dependents:   Nucleo_Hello_Encoder BLE_iBeaconScan AM1805_DEMO DISCO-F429ZI_ExportTemplate1 ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ATCmdParser.cpp Source File

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