Kenji Arai / TYBLE16_mbedlized_os5_several_examples_1st

Dependencies:   nRF51_Vdd TextLCD BME280

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ATCmdParser.cpp Source File

ATCmdParser.cpp

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