Rtos API example

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         // Recieve 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     while (response[0]) {
00214         // Since response is const, we need to copy it into our buffer to
00215         // add the line's null terminator and clobber value-matches with asterisks.
00216         //
00217         // We just use the beginning of the buffer to avoid unnecessary allocations.
00218         int i = 0;
00219         int offset = 0;
00220         bool whole_line_wanted = false;
00221 
00222         while (response[i]) {
00223             if (response[i] == '%' && response[i+1] != '%' && response[i+1] != '*') {
00224                 _buffer[offset++] = '%';
00225                 _buffer[offset++] = '*';
00226                 i++;
00227             } else {
00228                 _buffer[offset++] = response[i++];
00229                 // Find linebreaks, taking care not to be fooled if they're in a %[^\n] conversion specification
00230                 if (response[i - 1] == '\n' && !(i >= 3 && response[i-3] == '[' && response[i-2] == '^')) {
00231                     whole_line_wanted = true;
00232                     break;
00233                 }
00234             }
00235         }
00236 
00237         // Scanf has very poor support for catching errors
00238         // fortunately, we can abuse the %n specifier to determine
00239         // if the entire string was matched.
00240         _buffer[offset++] = '%';
00241         _buffer[offset++] = 'n';
00242         _buffer[offset++] = 0;
00243 
00244         debug_if(_dbg_on, "AT? %s\n", _buffer);
00245         // To workaround scanf's lack of error reporting, we actually
00246         // make two passes. One checks the validity with the modified
00247         // format string that only stores the matched characters (%n).
00248         // The other reads in the actual matched values.
00249         //
00250         // We keep trying the match until we succeed or some other error
00251         // derails us.
00252         int j = 0;
00253 
00254         while (true) {
00255             // Receive next character
00256             int c = getc();
00257             if (c < 0) {
00258                 debug_if(_dbg_on, "AT(Timeout)\n");
00259                 return false;
00260             }
00261             // Simplify newlines (borrowed from retarget.cpp)
00262             if ((c == CR && _in_prev != LF) ||
00263                 (c == LF && _in_prev != CR)) {
00264                 _in_prev = c;
00265                 c = '\n';
00266             } else if ((c == CR && _in_prev == LF) ||
00267                        (c == LF && _in_prev == CR)) {
00268                 _in_prev = c;
00269                 // onto next character
00270                 continue;
00271             } else {
00272                 _in_prev = c;
00273             }
00274             _buffer[offset + j++] = c;
00275             _buffer[offset + j] = 0;
00276 
00277             // Check for oob data
00278             for (struct oob *oob = _oobs; oob; oob = oob->next) {
00279                 if ((unsigned)j == oob->len && memcmp(
00280                         oob->prefix, _buffer+offset, oob->len) == 0) {
00281                     debug_if(_dbg_on, "AT! %s\n", oob->prefix);
00282                     oob->cb();
00283 
00284                     if (_aborted) {
00285                         debug_if(_dbg_on, "AT(Aborted)\n");
00286                         return false;
00287                     }
00288                     // oob may have corrupted non-reentrant buffer,
00289                     // so we need to set it up again
00290                     goto restart;
00291                 }
00292             }
00293 
00294             // Check for match
00295             int count = -1;
00296             if (whole_line_wanted && c != '\n') {
00297                 // Don't attempt scanning until we get delimiter if they included it in format
00298                 // This allows recv("Foo: %s\n") to work, and not match with just the first character of a string
00299                 // (scanf does not itself match whitespace in its format string, so \n is not significant to it)
00300             } else {
00301                 sscanf(_buffer+offset, _buffer, &count);
00302             }
00303 
00304             // We only succeed if all characters in the response are matched
00305             if (count == j) {
00306                 debug_if(_dbg_on, "AT= %s\n", _buffer+offset);
00307                 // Reuse the front end of the buffer
00308                 memcpy(_buffer, response, i);
00309                 _buffer[i] = 0;
00310 
00311                 // Store the found results
00312                 vsscanf(_buffer+offset, _buffer, args);
00313 
00314                 // Jump to next line and continue parsing
00315                 response += i;
00316                 break;
00317             }
00318 
00319             // Clear the buffer when we hit a newline or ran out of space
00320             // running out of space usually means we ran into binary data
00321             if (c == '\n' || j+1 >= _buffer_size - offset) {
00322                 debug_if(_dbg_on, "AT< %s", _buffer+offset);
00323                 j = 0;
00324             }
00325         }
00326     }
00327 
00328     return true;
00329 }
00330 
00331 // Mapping to vararg functions
00332 int ATCmdParser::printf(const char *format, ...)
00333 {
00334     va_list args;
00335     va_start(args, format);
00336     int res = vprintf(format, args);
00337     va_end(args);
00338     return res;
00339 }
00340 
00341 int ATCmdParser::scanf(const char *format, ...)
00342 {
00343     va_list args;
00344     va_start(args, format);
00345     int res = vscanf(format, args);
00346     va_end(args);
00347     return res;
00348 }
00349 
00350 bool ATCmdParser::send(const char *command, ...)
00351 {
00352     va_list args;
00353     va_start(args, command);
00354     bool res = vsend(command, args);
00355     va_end(args);
00356     return res;
00357 }
00358 
00359 bool ATCmdParser::recv(const char *response, ...)
00360 {
00361     va_list args;
00362     va_start(args, response);
00363     bool res = vrecv(response, args);
00364     va_end(args);
00365     return res;
00366 }
00367 
00368 // oob registration
00369 void ATCmdParser::oob(const char *prefix, Callback<void()> cb)
00370 {
00371     struct oob *oob = new struct oob;
00372     oob->len = strlen(prefix);
00373     oob->prefix = prefix;
00374     oob->cb = cb;
00375     oob->next = _oobs;
00376     _oobs = oob;
00377 }
00378 
00379 void ATCmdParser::abort()
00380 {
00381     _aborted = true;
00382 }
00383 
00384 bool ATCmdParser::process_oob()
00385 {
00386     if (!_fh->readable()) {
00387         return false;
00388     }
00389 
00390     int i = 0;
00391     while (true) {
00392         // Receive next character
00393         int c = getc();
00394         if (c < 0) {
00395             return false;
00396         }
00397         _buffer[i++] = c;
00398         _buffer[i] = 0;
00399 
00400         // Check for oob data
00401         struct oob *oob = _oobs;
00402         while (oob) {
00403             if (i == (int)oob->len && memcmp(
00404                     oob->prefix, _buffer, oob->len) == 0) {
00405                 debug_if(_dbg_on, "AT! %s\r\n", oob->prefix);
00406                 oob->cb();
00407                 return true;
00408             }
00409             oob = oob->next;
00410         }
00411         
00412         // Clear the buffer when we hit a newline or ran out of space
00413         // running out of space usually means we ran into binary data
00414         if (i+1 >= _buffer_size ||
00415             strcmp(&_buffer[i-_output_delim_size], _output_delimiter) == 0) {
00416 
00417             debug_if(_dbg_on, "AT< %s", _buffer);
00418             i = 0;
00419         }
00420     }
00421 }
00422 
00423