Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

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 // 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 }