Implementation of 3G USB Modem Huawei E372

Dependents:   PYRN

Committer:
clemounet
Date:
Fri Feb 20 16:48:12 2015 +0000
Revision:
0:67daedd6f74f
Child:
2:61ac95f0af72
3G Modem driver HUAWEI E372

Who changed what in which revision?

UserRevisionLine numberNew contents of line
clemounet 0:67daedd6f74f 1 /* ATCommandsInterface.cpp */
clemounet 0:67daedd6f74f 2 /* Copyright (C) 2012 mbed.org, MIT License
clemounet 0:67daedd6f74f 3 *
clemounet 0:67daedd6f74f 4 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
clemounet 0:67daedd6f74f 5 * and associated documentation files (the "Software"), to deal in the Software without restriction,
clemounet 0:67daedd6f74f 6 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
clemounet 0:67daedd6f74f 7 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
clemounet 0:67daedd6f74f 8 * furnished to do so, subject to the following conditions:
clemounet 0:67daedd6f74f 9 *
clemounet 0:67daedd6f74f 10 * The above copyright notice and this permission notice shall be included in all copies or
clemounet 0:67daedd6f74f 11 * substantial portions of the Software.
clemounet 0:67daedd6f74f 12 *
clemounet 0:67daedd6f74f 13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
clemounet 0:67daedd6f74f 14 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
clemounet 0:67daedd6f74f 15 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
clemounet 0:67daedd6f74f 16 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
clemounet 0:67daedd6f74f 17 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
clemounet 0:67daedd6f74f 18 */
clemounet 0:67daedd6f74f 19
clemounet 0:67daedd6f74f 20 #include "dbg.h"
clemounet 0:67daedd6f74f 21 #include <cstdio>
clemounet 0:67daedd6f74f 22 //#include <cstring> //For memset, strstr...
clemounet 0:67daedd6f74f 23
clemounet 0:67daedd6f74f 24 #include "ATCommandsInterface.h"
clemounet 0:67daedd6f74f 25
clemounet 0:67daedd6f74f 26 ATCommandsInterface::ATCommandsInterface(IOStream* pStream) :
clemounet 0:67daedd6f74f 27 m_pStream(pStream), m_open(false), m_env2AT(), m_AT2Env(), m_processingMtx(),
clemounet 0:67daedd6f74f 28 m_processingThread(&ATCommandsInterface::staticCallback, this, (osPriority)AT_THREAD_PRIORITY, 4*192),
clemounet 0:67daedd6f74f 29 m_eventsMtx()
clemounet 0:67daedd6f74f 30 {
clemounet 0:67daedd6f74f 31 memset(m_eventsHandlers, 0, MAX_AT_EVENTS_HANDLERS * sizeof(IATEventsHandler*));
clemounet 0:67daedd6f74f 32
clemounet 0:67daedd6f74f 33 m_processingMtx.lock();
clemounet 0:67daedd6f74f 34 }
clemounet 0:67daedd6f74f 35
clemounet 0:67daedd6f74f 36 //Open connection to AT Interface in order to execute command & register/unregister events
clemounet 0:67daedd6f74f 37 int ATCommandsInterface::open()
clemounet 0:67daedd6f74f 38 {
clemounet 0:67daedd6f74f 39 if( m_open )
clemounet 0:67daedd6f74f 40 {
clemounet 0:67daedd6f74f 41 USB_WARN("AT interface is already open");
clemounet 0:67daedd6f74f 42 return OK;
clemounet 0:67daedd6f74f 43 }
clemounet 0:67daedd6f74f 44 USB_DBG("Opening AT interface");
clemounet 0:67daedd6f74f 45 //Start processing
clemounet 0:67daedd6f74f 46 m_processingThread.signal_set(AT_SIG_PROCESSING_START);
clemounet 0:67daedd6f74f 47
clemounet 0:67daedd6f74f 48 m_processingMtx.unlock();
clemounet 0:67daedd6f74f 49
clemounet 0:67daedd6f74f 50 m_open = true;
clemounet 0:67daedd6f74f 51
clemounet 0:67daedd6f74f 52 //Advertize this to events handlers
clemounet 0:67daedd6f74f 53 m_eventsMtx.lock();
clemounet 0:67daedd6f74f 54 for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot
clemounet 0:67daedd6f74f 55 {
clemounet 0:67daedd6f74f 56 if( m_eventsHandlers[i] != NULL )
clemounet 0:67daedd6f74f 57 {
clemounet 0:67daedd6f74f 58 m_eventsHandlers[i]->onDispatchStart();
clemounet 0:67daedd6f74f 59 }
clemounet 0:67daedd6f74f 60 }
clemounet 0:67daedd6f74f 61 m_eventsMtx.unlock();
clemounet 0:67daedd6f74f 62
clemounet 0:67daedd6f74f 63 USB_DBG("AT interface opened");
clemounet 0:67daedd6f74f 64
clemounet 0:67daedd6f74f 65 return OK;
clemounet 0:67daedd6f74f 66 }
clemounet 0:67daedd6f74f 67
clemounet 0:67daedd6f74f 68 //Initialize AT link
clemounet 0:67daedd6f74f 69 int ATCommandsInterface::init()
clemounet 0:67daedd6f74f 70 {
clemounet 0:67daedd6f74f 71 USB_DBG("Sending ATZ E1 V1");
clemounet 0:67daedd6f74f 72 //Should we flush m_pStream at this point ???
clemounet 0:67daedd6f74f 73 int err;
clemounet 0:67daedd6f74f 74 int tries = 5;
clemounet 0:67daedd6f74f 75 do
clemounet 0:67daedd6f74f 76 {
clemounet 0:67daedd6f74f 77 err = executeSimple("ATZ E1 V1", NULL, 3000); //Enable echo and verbosity
clemounet 0:67daedd6f74f 78 if(err && tries)
clemounet 0:67daedd6f74f 79 {
clemounet 0:67daedd6f74f 80 USB_WARN("No response, trying again");
clemounet 0:67daedd6f74f 81 Thread::wait(1000); //Give dongle time to recover
clemounet 0:67daedd6f74f 82 }
clemounet 0:67daedd6f74f 83 } while(err && tries--);
clemounet 0:67daedd6f74f 84 if( err )
clemounet 0:67daedd6f74f 85 {
clemounet 0:67daedd6f74f 86 USB_ERR("Sending ATZ E1 V1 returned with err code %d", err);
clemounet 0:67daedd6f74f 87 return err;
clemounet 0:67daedd6f74f 88 }
clemounet 0:67daedd6f74f 89
clemounet 0:67daedd6f74f 90 USB_DBG("AT interface initialized");
clemounet 0:67daedd6f74f 91
clemounet 0:67daedd6f74f 92 return OK;
clemounet 0:67daedd6f74f 93 }
clemounet 0:67daedd6f74f 94
clemounet 0:67daedd6f74f 95 //Close connection
clemounet 0:67daedd6f74f 96 int ATCommandsInterface::close()
clemounet 0:67daedd6f74f 97 {
clemounet 0:67daedd6f74f 98 if( !m_open )
clemounet 0:67daedd6f74f 99 {
clemounet 0:67daedd6f74f 100 USB_WARN("AT interface is already closed");
clemounet 0:67daedd6f74f 101 return OK;
clemounet 0:67daedd6f74f 102 }
clemounet 0:67daedd6f74f 103
clemounet 0:67daedd6f74f 104 USB_DBG("Closing AT interface");
clemounet 0:67daedd6f74f 105
clemounet 0:67daedd6f74f 106 //Stop processing
clemounet 0:67daedd6f74f 107 m_processingThread.signal_set(AT_SIG_PROCESSING_STOP);
clemounet 0:67daedd6f74f 108 //m_stopSphre.release();
clemounet 0:67daedd6f74f 109
clemounet 0:67daedd6f74f 110 int* msg = m_env2AT.alloc(osWaitForever);
clemounet 0:67daedd6f74f 111 *msg = AT_STOP;
clemounet 0:67daedd6f74f 112 m_env2AT.put(msg); //Used to unstall the process if needed
clemounet 0:67daedd6f74f 113
clemounet 0:67daedd6f74f 114 //Unlock process routine (abort read)
clemounet 0:67daedd6f74f 115 m_pStream->abortRead(); //This is thread-safe
clemounet 0:67daedd6f74f 116 m_processingMtx.lock();
clemounet 0:67daedd6f74f 117 m_open = false;
clemounet 0:67daedd6f74f 118
clemounet 0:67daedd6f74f 119 //Advertize this to events handlers
clemounet 0:67daedd6f74f 120 m_eventsMtx.lock();
clemounet 0:67daedd6f74f 121 for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot
clemounet 0:67daedd6f74f 122 {
clemounet 0:67daedd6f74f 123 if( m_eventsHandlers[i] != NULL )
clemounet 0:67daedd6f74f 124 {
clemounet 0:67daedd6f74f 125 m_eventsHandlers[i]->onDispatchStop();
clemounet 0:67daedd6f74f 126 }
clemounet 0:67daedd6f74f 127 }
clemounet 0:67daedd6f74f 128 m_eventsMtx.unlock();
clemounet 0:67daedd6f74f 129
clemounet 0:67daedd6f74f 130 USB_DBG("AT interface closed");
clemounet 0:67daedd6f74f 131 return OK;
clemounet 0:67daedd6f74f 132 }
clemounet 0:67daedd6f74f 133
clemounet 0:67daedd6f74f 134 bool ATCommandsInterface::isOpen()
clemounet 0:67daedd6f74f 135 {
clemounet 0:67daedd6f74f 136 return m_open;
clemounet 0:67daedd6f74f 137 }
clemounet 0:67daedd6f74f 138
clemounet 0:67daedd6f74f 139 int ATCommandsInterface::executeSimple(const char* command, ATResult* pResult, uint32_t timeout/*=1000*/)
clemounet 0:67daedd6f74f 140 {
clemounet 0:67daedd6f74f 141 return execute(command, this, pResult, timeout);
clemounet 0:67daedd6f74f 142 }
clemounet 0:67daedd6f74f 143
clemounet 0:67daedd6f74f 144 int ATCommandsInterface::execute(const char* command, IATCommandsProcessor* pProcessor, ATResult* pResult, uint32_t timeout/*=1000*/)
clemounet 0:67daedd6f74f 145 {
clemounet 0:67daedd6f74f 146 USB_DBG("Executing command %s", command);
clemounet 0:67daedd6f74f 147 if(!m_open)
clemounet 0:67daedd6f74f 148 {
clemounet 0:67daedd6f74f 149 USB_WARN("Interface is not open!");
clemounet 0:67daedd6f74f 150 return NET_INVALID;
clemounet 0:67daedd6f74f 151 }
clemounet 0:67daedd6f74f 152
clemounet 0:67daedd6f74f 153 //Lock transaction mutex
clemounet 0:67daedd6f74f 154 m_transactionMtx.lock();
clemounet 0:67daedd6f74f 155
clemounet 0:67daedd6f74f 156 //Discard previous result if it arrived too late
clemounet 0:67daedd6f74f 157 osEvent evt = m_AT2Env.get(0);
clemounet 0:67daedd6f74f 158
clemounet 0:67daedd6f74f 159 if(evt.status == osEventMail)
clemounet 0:67daedd6f74f 160 {
clemounet 0:67daedd6f74f 161 m_AT2Env.free((int*)evt.value.p);
clemounet 0:67daedd6f74f 162 USB_WARN("Previous result discarded");
clemounet 0:67daedd6f74f 163 }
clemounet 0:67daedd6f74f 164
clemounet 0:67daedd6f74f 165 //Send params to the process routine
clemounet 0:67daedd6f74f 166 m_transactionCommand = command;
clemounet 0:67daedd6f74f 167 if(pProcessor != NULL)
clemounet 0:67daedd6f74f 168 {
clemounet 0:67daedd6f74f 169 m_pTransactionProcessor = pProcessor;
clemounet 0:67daedd6f74f 170 }
clemounet 0:67daedd6f74f 171 else
clemounet 0:67daedd6f74f 172 {
clemounet 0:67daedd6f74f 173 m_pTransactionProcessor = this; //Use default behaviour
clemounet 0:67daedd6f74f 174 }
clemounet 0:67daedd6f74f 175
clemounet 0:67daedd6f74f 176 Thread::wait(100); //FIXME find stg else
clemounet 0:67daedd6f74f 177
clemounet 0:67daedd6f74f 178 USB_DBG("Sending command ready signal to AT thread & aborting current blocking read operation");
clemounet 0:67daedd6f74f 179
clemounet 0:67daedd6f74f 180 //Produce command ready signal
clemounet 0:67daedd6f74f 181 int* msg = m_env2AT.alloc(osWaitForever);
clemounet 0:67daedd6f74f 182 *msg = AT_CMD_READY;
clemounet 0:67daedd6f74f 183 m_env2AT.put(msg);
clemounet 0:67daedd6f74f 184
clemounet 0:67daedd6f74f 185 USB_DBG("Trying to enter abortRead()");
clemounet 0:67daedd6f74f 186 //Unlock process routine (abort read)
clemounet 0:67daedd6f74f 187 m_pStream->abortRead(); //This is thread-safe
clemounet 0:67daedd6f74f 188
clemounet 0:67daedd6f74f 189 //Wait for a result (get result message)
clemounet 0:67daedd6f74f 190 evt = m_AT2Env.get(timeout);
clemounet 0:67daedd6f74f 191
clemounet 0:67daedd6f74f 192 if(evt.status != osEventMail)
clemounet 0:67daedd6f74f 193 {
clemounet 0:67daedd6f74f 194 //Cancel request
clemounet 0:67daedd6f74f 195 msg = m_env2AT.alloc(osWaitForever);
clemounet 0:67daedd6f74f 196 *msg = AT_TIMEOUT;
clemounet 0:67daedd6f74f 197 m_env2AT.put(msg);
clemounet 0:67daedd6f74f 198
clemounet 0:67daedd6f74f 199 USB_DBG("Trying to enter abortRead()");
clemounet 0:67daedd6f74f 200 //Unlock process routine (abort read)
clemounet 0:67daedd6f74f 201 m_pStream->abortRead(); //This is thread-safe
clemounet 0:67daedd6f74f 202
clemounet 0:67daedd6f74f 203 USB_WARN("Command returned no message");
clemounet 0:67daedd6f74f 204 m_transactionMtx.unlock();
clemounet 0:67daedd6f74f 205 return NET_TIMEOUT;
clemounet 0:67daedd6f74f 206 }
clemounet 0:67daedd6f74f 207 USB_DBG("Command returned with message %d", *msg);
clemounet 0:67daedd6f74f 208
clemounet 0:67daedd6f74f 209 m_AT2Env.free((int*)evt.value.p);
clemounet 0:67daedd6f74f 210
clemounet 0:67daedd6f74f 211 if(pResult != NULL)
clemounet 0:67daedd6f74f 212 {
clemounet 0:67daedd6f74f 213 *pResult = m_transactionResult;
clemounet 0:67daedd6f74f 214 }
clemounet 0:67daedd6f74f 215
clemounet 0:67daedd6f74f 216 int ret = ATResultToReturnCode(m_transactionResult);
clemounet 0:67daedd6f74f 217 if(ret != OK)
clemounet 0:67daedd6f74f 218 {
clemounet 0:67daedd6f74f 219 USB_WARN("Command returned AT result %d with code %d", m_transactionResult.result, m_transactionResult.code);
clemounet 0:67daedd6f74f 220 }
clemounet 0:67daedd6f74f 221
clemounet 0:67daedd6f74f 222 USB_DBG("Command returned successfully");
clemounet 0:67daedd6f74f 223
clemounet 0:67daedd6f74f 224 //Unlock transaction mutex
clemounet 0:67daedd6f74f 225 m_transactionMtx.unlock();
clemounet 0:67daedd6f74f 226
clemounet 0:67daedd6f74f 227 return ret;
clemounet 0:67daedd6f74f 228 }
clemounet 0:67daedd6f74f 229
clemounet 0:67daedd6f74f 230 int ATCommandsInterface::registerEventsHandler(IATEventsHandler* pHdlr)
clemounet 0:67daedd6f74f 231 {
clemounet 0:67daedd6f74f 232 m_eventsMtx.lock();
clemounet 0:67daedd6f74f 233 for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot
clemounet 0:67daedd6f74f 234 {
clemounet 0:67daedd6f74f 235 if( m_eventsHandlers[i] == NULL )
clemounet 0:67daedd6f74f 236 {
clemounet 0:67daedd6f74f 237 m_eventsHandlers[i] = pHdlr;
clemounet 0:67daedd6f74f 238 m_eventsMtx.unlock();
clemounet 0:67daedd6f74f 239 return OK;
clemounet 0:67daedd6f74f 240 }
clemounet 0:67daedd6f74f 241 }
clemounet 0:67daedd6f74f 242 m_eventsMtx.unlock();
clemounet 0:67daedd6f74f 243 return NET_OOM; //No room left
clemounet 0:67daedd6f74f 244 }
clemounet 0:67daedd6f74f 245
clemounet 0:67daedd6f74f 246 int ATCommandsInterface::deregisterEventsHandler(IATEventsHandler* pHdlr)
clemounet 0:67daedd6f74f 247 {
clemounet 0:67daedd6f74f 248 m_eventsMtx.lock();
clemounet 0:67daedd6f74f 249 for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find handler in list
clemounet 0:67daedd6f74f 250 {
clemounet 0:67daedd6f74f 251 if( m_eventsHandlers[i] == pHdlr )
clemounet 0:67daedd6f74f 252 {
clemounet 0:67daedd6f74f 253 m_eventsHandlers[i] = NULL;
clemounet 0:67daedd6f74f 254 m_eventsMtx.unlock();
clemounet 0:67daedd6f74f 255 return OK;
clemounet 0:67daedd6f74f 256 }
clemounet 0:67daedd6f74f 257 }
clemounet 0:67daedd6f74f 258 m_eventsMtx.unlock();
clemounet 0:67daedd6f74f 259 return NET_NOTFOUND; //Not found
clemounet 0:67daedd6f74f 260 }
clemounet 0:67daedd6f74f 261
clemounet 0:67daedd6f74f 262
clemounet 0:67daedd6f74f 263 int ATCommandsInterface::tryReadLine()
clemounet 0:67daedd6f74f 264 {
clemounet 0:67daedd6f74f 265 static bool lineDetected = false;
clemounet 0:67daedd6f74f 266
clemounet 0:67daedd6f74f 267 //Block on serial read or incoming command
clemounet 0:67daedd6f74f 268 USB_DBG("Trying to read a new line from stream");
clemounet 0:67daedd6f74f 269 int ret = m_pStream->waitAvailable(); //This can be aborted
clemounet 0:67daedd6f74f 270 size_t readLen = 0;
clemounet 0:67daedd6f74f 271 if(ret == OK)
clemounet 0:67daedd6f74f 272 {
clemounet 0:67daedd6f74f 273 ret = m_pStream->read((uint8_t*)m_inputBuf + m_inputPos, &readLen, AT_INPUT_BUF_SIZE - 1 - m_inputPos, 0); //Do NOT wait at this point
clemounet 0:67daedd6f74f 274 }
clemounet 0:67daedd6f74f 275 if(ret == OK)
clemounet 0:67daedd6f74f 276 {
clemounet 0:67daedd6f74f 277 m_inputPos+=readLen;
clemounet 0:67daedd6f74f 278 m_inputBuf[m_inputPos] = '\0'; //Add null terminating character to ease the use of str* functions
clemounet 0:67daedd6f74f 279 USB_DBG("In buffer: [%s]", m_inputBuf);
clemounet 0:67daedd6f74f 280 }
clemounet 0:67daedd6f74f 281
clemounet 0:67daedd6f74f 282 if( ret == NET_INTERRUPTED ) //It is worth checking readLen as data might have been read even though the read was interrupted
clemounet 0:67daedd6f74f 283 {
clemounet 0:67daedd6f74f 284 USB_DBG("Read was interrupted");
clemounet 0:67daedd6f74f 285 return NET_INTERRUPTED; //0 chars were read
clemounet 0:67daedd6f74f 286 }
clemounet 0:67daedd6f74f 287 else if(readLen == 0)
clemounet 0:67daedd6f74f 288 {
clemounet 0:67daedd6f74f 289 USB_DBG("Nothing read");
clemounet 0:67daedd6f74f 290 return OK; //0 chars were read
clemounet 0:67daedd6f74f 291 }
clemounet 0:67daedd6f74f 292
clemounet 0:67daedd6f74f 293 USB_DBG("Trying to process incoming line");
clemounet 0:67daedd6f74f 294 bool lineProcessed = false;
clemounet 0:67daedd6f74f 295
clemounet 0:67daedd6f74f 296 do
clemounet 0:67daedd6f74f 297 {
clemounet 0:67daedd6f74f 298 lineProcessed = false; //Reset flag
clemounet 0:67daedd6f74f 299
clemounet 0:67daedd6f74f 300 USB_DBG("New iteration");
clemounet 0:67daedd6f74f 301
clemounet 0:67daedd6f74f 302 //Look for a new line
clemounet 0:67daedd6f74f 303 if(!lineDetected)
clemounet 0:67daedd6f74f 304 {
clemounet 0:67daedd6f74f 305 USB_DBG("No line detected yet");
clemounet 0:67daedd6f74f 306 //Try to look for a starting CRLF
clemounet 0:67daedd6f74f 307 char* crPtr = strchr(m_inputBuf, CR);
clemounet 0:67daedd6f74f 308 /*
clemounet 0:67daedd6f74f 309 Different cases at this point:
clemounet 0:67daedd6f74f 310 - CRLF%c sequence: this is the start of a line
clemounet 0:67daedd6f74f 311 - CRLFCR(LF) sequence: this is the end of a line (followed by the beginning of the next one)
clemounet 0:67daedd6f74f 312 - LF: this is the trailing LF char of the previous line, discard
clemounet 0:67daedd6f74f 313 - CR / CRLF incomplete sequence: more data is needed to determine which action to take
clemounet 0:67daedd6f74f 314 - %c ... CR sequence: this should be the echo of the previous sequence
clemounet 0:67daedd6f74f 315 - %c sequence: This might be the echo of the previous command; more data is needed to determine which action to take
clemounet 0:67daedd6f74f 316
clemounet 0:67daedd6f74f 317 In every case, move mem at the beginning
clemounet 0:67daedd6f74f 318 */
clemounet 0:67daedd6f74f 319 if(crPtr != NULL)
clemounet 0:67daedd6f74f 320 {
clemounet 0:67daedd6f74f 321 USB_DBG("CR char found");
clemounet 0:67daedd6f74f 322
clemounet 0:67daedd6f74f 323 #if 0
clemounet 0:67daedd6f74f 324 //Discard all preceding characters (can do nothing if m_inputBuf == crPtr)
clemounet 0:67daedd6f74f 325 memmove(m_inputBuf, crPtr, (m_inputPos + 1) - (crPtr-m_inputBuf)); //Move null-terminating char as well
clemounet 0:67daedd6f74f 326 m_inputPos = m_inputPos - (crPtr-m_inputBuf); //Adjust m_inputPos
clemounet 0:67daedd6f74f 327 #endif
clemounet 0:67daedd6f74f 328
clemounet 0:67daedd6f74f 329 //If the line starts with CR, this should be a result code
clemounet 0:67daedd6f74f 330 if( crPtr == m_inputBuf )
clemounet 0:67daedd6f74f 331 {
clemounet 0:67daedd6f74f 332 //To determine the sequence we need at least 3 chars
clemounet 0:67daedd6f74f 333 if(m_inputPos >= 3)
clemounet 0:67daedd6f74f 334 {
clemounet 0:67daedd6f74f 335 //Look for a LF char next to the CR char
clemounet 0:67daedd6f74f 336 if(m_inputBuf[1] == LF)
clemounet 0:67daedd6f74f 337 {
clemounet 0:67daedd6f74f 338 //At this point we can check whether this is the end of a preceding line or the beginning of a new one
clemounet 0:67daedd6f74f 339 if(m_inputBuf[2] != CR)
clemounet 0:67daedd6f74f 340 {
clemounet 0:67daedd6f74f 341 USB_DBG("Beginning of new line found");
clemounet 0:67daedd6f74f 342 //Beginning of a line
clemounet 0:67daedd6f74f 343 lineDetected = true; //Move to next state-machine step
clemounet 0:67daedd6f74f 344 }
clemounet 0:67daedd6f74f 345 else
clemounet 0:67daedd6f74f 346 {
clemounet 0:67daedd6f74f 347 //End of an unprocessed line
clemounet 0:67daedd6f74f 348 USB_WARN("End of unprocessed line");
clemounet 0:67daedd6f74f 349 }
clemounet 0:67daedd6f74f 350 //In both cases discard CRLF
clemounet 0:67daedd6f74f 351 USB_DBG("Discarding CRLF");
clemounet 0:67daedd6f74f 352 memmove(m_inputBuf, m_inputBuf + 2, (m_inputPos + 1) - 2); //Move null-terminating char as well
clemounet 0:67daedd6f74f 353 m_inputPos = m_inputPos - 2; //Adjust m_inputPos
clemounet 0:67daedd6f74f 354 }
clemounet 0:67daedd6f74f 355 else
clemounet 0:67daedd6f74f 356 {
clemounet 0:67daedd6f74f 357 //This is completely unexpected, discard the CR char to try to recover good state
clemounet 0:67daedd6f74f 358 USB_WARN("Unexpected %c char (%02d code) found after CR char", m_inputBuf[1]);
clemounet 0:67daedd6f74f 359 memmove(m_inputBuf, m_inputBuf + 1, (m_inputPos + 1) - 1); //Move null-terminating char as well
clemounet 0:67daedd6f74f 360 m_inputPos = m_inputPos - 1; //Adjust m_inputPos
clemounet 0:67daedd6f74f 361 }
clemounet 0:67daedd6f74f 362 }
clemounet 0:67daedd6f74f 363 }
clemounet 0:67daedd6f74f 364 //if the line does NOT begin with CR, this can be an echo of the previous command, process it
clemounet 0:67daedd6f74f 365 else
clemounet 0:67daedd6f74f 366 {
clemounet 0:67daedd6f74f 367 int crPos = crPtr - m_inputBuf;
clemounet 0:67daedd6f74f 368 int lfOff = 0; //Offset for LF if present
clemounet 0:67daedd6f74f 369 USB_DBG("New line found (possible echo of command)");
clemounet 0:67daedd6f74f 370 //This is the end of line
clemounet 0:67daedd6f74f 371 //Replace m_inputBuf[crPos] with null-terminating char
clemounet 0:67daedd6f74f 372 m_inputBuf[crPos] = '\0';
clemounet 0:67daedd6f74f 373 //Check if there is a LF char afterwards
clemounet 0:67daedd6f74f 374 if(m_inputPos - crPos >= 1)
clemounet 0:67daedd6f74f 375 {
clemounet 0:67daedd6f74f 376 if(m_inputBuf[crPos+1] == LF)
clemounet 0:67daedd6f74f 377 {
clemounet 0:67daedd6f74f 378 lfOff++; //We will discard LF char as well
clemounet 0:67daedd6f74f 379 }
clemounet 0:67daedd6f74f 380 }
clemounet 0:67daedd6f74f 381 //Process line
clemounet 0:67daedd6f74f 382 int ret = processReadLine();
clemounet 0:67daedd6f74f 383 if(ret)
clemounet 0:67daedd6f74f 384 {
clemounet 0:67daedd6f74f 385 m_inputPos = 0;
clemounet 0:67daedd6f74f 386 m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
clemounet 0:67daedd6f74f 387 lineDetected = false;
clemounet 0:67daedd6f74f 388 return ret;
clemounet 0:67daedd6f74f 389 }
clemounet 0:67daedd6f74f 390
clemounet 0:67daedd6f74f 391 //If sendData has been called, all incoming data has been discarded
clemounet 0:67daedd6f74f 392 if(m_inputPos > 0)
clemounet 0:67daedd6f74f 393 {
clemounet 0:67daedd6f74f 394 memmove(m_inputBuf, m_inputBuf + crPos + lfOff + 1, (m_inputPos + 1) - (crPos + lfOff + 1)); //Move null-terminating char as well
clemounet 0:67daedd6f74f 395 m_inputPos = m_inputPos - (crPos + lfOff + 1); //Adjust m_inputPos
clemounet 0:67daedd6f74f 396 }
clemounet 0:67daedd6f74f 397 USB_DBG("One line was successfully processed");
clemounet 0:67daedd6f74f 398 lineProcessed = true; //Line was processed with success
clemounet 0:67daedd6f74f 399 lineDetected = false; //Search now for a new line
clemounet 0:67daedd6f74f 400 }
clemounet 0:67daedd6f74f 401 }
clemounet 0:67daedd6f74f 402 else if(m_inputBuf[0] == LF) //If there is a remaining LF char from the previous line, discard it
clemounet 0:67daedd6f74f 403 {
clemounet 0:67daedd6f74f 404 USB_DBG("Discarding single LF char");
clemounet 0:67daedd6f74f 405 memmove(m_inputBuf, m_inputBuf + 1, (m_inputPos + 1) - 1); //Move null-terminating char as well
clemounet 0:67daedd6f74f 406 m_inputPos = m_inputPos - 1; //Adjust m_inputPos
clemounet 0:67daedd6f74f 407 }
clemounet 0:67daedd6f74f 408 }
clemounet 0:67daedd6f74f 409
clemounet 0:67daedd6f74f 410 //Look for the end of line
clemounet 0:67daedd6f74f 411 if(lineDetected)
clemounet 0:67daedd6f74f 412 {
clemounet 0:67daedd6f74f 413 USB_DBG("Looking for end of line");
clemounet 0:67daedd6f74f 414 //Try to look for a terminating CRLF
clemounet 0:67daedd6f74f 415 char* crPtr = strchr(m_inputBuf, CR);
clemounet 0:67daedd6f74f 416 /*
clemounet 0:67daedd6f74f 417 Different cases at this point:
clemounet 0:67daedd6f74f 418 - CRLF sequence: this is the end of the line
clemounet 0:67daedd6f74f 419 - CR%c sequence : unexpected
clemounet 0:67daedd6f74f 420 - CR incomplete sequence: more data is needed to determine which action to take
clemounet 0:67daedd6f74f 421 */
clemounet 0:67daedd6f74f 422
clemounet 0:67daedd6f74f 423 //Try to look for a '>' (greater than character) that marks an entry prompt
clemounet 0:67daedd6f74f 424 char* greaterThanPtr = strchr(m_inputBuf, GD);
clemounet 0:67daedd6f74f 425 /*
clemounet 0:67daedd6f74f 426 This character must be detected as there is no CRLF sequence at the end of an entry prompt
clemounet 0:67daedd6f74f 427 */
clemounet 0:67daedd6f74f 428
clemounet 0:67daedd6f74f 429 if(crPtr != NULL)
clemounet 0:67daedd6f74f 430 {
clemounet 0:67daedd6f74f 431 USB_DBG("CR char found");
clemounet 0:67daedd6f74f 432 int crPos = crPtr - m_inputBuf;
clemounet 0:67daedd6f74f 433 //To determine the sequence we need at least 2 chars
clemounet 0:67daedd6f74f 434 if(m_inputPos - crPos >= 2)
clemounet 0:67daedd6f74f 435 {
clemounet 0:67daedd6f74f 436 //Look for a LF char next to the CR char
clemounet 0:67daedd6f74f 437 if(m_inputBuf[crPos + 1] == LF)
clemounet 0:67daedd6f74f 438 {
clemounet 0:67daedd6f74f 439 USB_DBG("End of new line found");
clemounet 0:67daedd6f74f 440 //This is the end of line
clemounet 0:67daedd6f74f 441 //Replace m_inputBuf[crPos] with null-terminating char
clemounet 0:67daedd6f74f 442 m_inputBuf[crPos] = '\0';
clemounet 0:67daedd6f74f 443 //Process line
clemounet 0:67daedd6f74f 444 int ret = processReadLine();
clemounet 0:67daedd6f74f 445 if(ret)
clemounet 0:67daedd6f74f 446 {
clemounet 0:67daedd6f74f 447 m_inputPos = 0;
clemounet 0:67daedd6f74f 448 m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
clemounet 0:67daedd6f74f 449 lineDetected = false;
clemounet 0:67daedd6f74f 450 return ret;
clemounet 0:67daedd6f74f 451 }
clemounet 0:67daedd6f74f 452
clemounet 0:67daedd6f74f 453 //If sendData has been called, all incoming data has been discarded
clemounet 0:67daedd6f74f 454 if(m_inputPos > 0)
clemounet 0:67daedd6f74f 455 {
clemounet 0:67daedd6f74f 456 //Shift remaining data to beginning of buffer
clemounet 0:67daedd6f74f 457 memmove(m_inputBuf, m_inputBuf + crPos + 2, (m_inputPos + 1) - (crPos + 2)); //Move null-terminating char as well
clemounet 0:67daedd6f74f 458 m_inputPos = m_inputPos - (crPos + 2); //Adjust m_inputPos
clemounet 0:67daedd6f74f 459 }
clemounet 0:67daedd6f74f 460
clemounet 0:67daedd6f74f 461 USB_DBG("One line was successfully processed");
clemounet 0:67daedd6f74f 462 lineProcessed = true; //Line was processed with success
clemounet 0:67daedd6f74f 463 }
clemounet 0:67daedd6f74f 464 else
clemounet 0:67daedd6f74f 465 {
clemounet 0:67daedd6f74f 466 //This is completely unexpected, discard all chars till the CR char to try to recover good state
clemounet 0:67daedd6f74f 467 USB_WARN("Unexpected %c char (%02d code) found in incoming line", m_inputBuf[crPos + 1]);
clemounet 0:67daedd6f74f 468 memmove(m_inputBuf, m_inputBuf + crPos + 1, (m_inputPos + 1) - (crPos + 1)); //Move null-terminating char as well
clemounet 0:67daedd6f74f 469 m_inputPos = m_inputPos - (crPos + 1); //Adjust m_inputPos
clemounet 0:67daedd6f74f 470 }
clemounet 0:67daedd6f74f 471 lineDetected = false; //In both case search now for a new line
clemounet 0:67daedd6f74f 472 }
clemounet 0:67daedd6f74f 473 }
clemounet 0:67daedd6f74f 474 else if(greaterThanPtr != NULL)
clemounet 0:67daedd6f74f 475 {
clemounet 0:67daedd6f74f 476 USB_DBG("> char found");
clemounet 0:67daedd6f74f 477 int gdPos = greaterThanPtr - m_inputBuf;
clemounet 0:67daedd6f74f 478 //To determine the sequence we need at least 2 chars
clemounet 0:67daedd6f74f 479 if(m_inputPos - gdPos >= 2)
clemounet 0:67daedd6f74f 480 {
clemounet 0:67daedd6f74f 481 //Look for a space char next to the GD char
clemounet 0:67daedd6f74f 482 if(m_inputBuf[gdPos + 1] == ' ')
clemounet 0:67daedd6f74f 483 {
clemounet 0:67daedd6f74f 484 //This is an entry prompt
clemounet 0:67daedd6f74f 485 //Replace m_inputBuf[gdPos] with null-terminating char
clemounet 0:67daedd6f74f 486 m_inputBuf[gdPos] = '\0';
clemounet 0:67daedd6f74f 487
clemounet 0:67daedd6f74f 488 //Shift remaining data to beginning of buffer
clemounet 0:67daedd6f74f 489 memmove(m_inputBuf, m_inputBuf + gdPos + 1, (m_inputPos + 1) - (gdPos + 1)); //Move null-terminating char as well
clemounet 0:67daedd6f74f 490 m_inputPos = m_inputPos - (gdPos + 1); //Adjust m_inputPos
clemounet 0:67daedd6f74f 491
clemounet 0:67daedd6f74f 492 //Process prompt
clemounet 0:67daedd6f74f 493 ret = processEntryPrompt();
clemounet 0:67daedd6f74f 494 if(ret)
clemounet 0:67daedd6f74f 495 {
clemounet 0:67daedd6f74f 496 m_inputPos = 0;
clemounet 0:67daedd6f74f 497 m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
clemounet 0:67daedd6f74f 498 lineDetected = false;
clemounet 0:67daedd6f74f 499 return ret;
clemounet 0:67daedd6f74f 500 }
clemounet 0:67daedd6f74f 501
clemounet 0:67daedd6f74f 502 USB_DBG("One line was successfully processed");
clemounet 0:67daedd6f74f 503 lineProcessed = true; //Line was processed with success
clemounet 0:67daedd6f74f 504 }
clemounet 0:67daedd6f74f 505 else
clemounet 0:67daedd6f74f 506 {
clemounet 0:67daedd6f74f 507 //This is completely unexpected, discard all chars till the GD char to try to recover good state
clemounet 0:67daedd6f74f 508 USB_WARN("Unexpected %c char (%02d code) found in incoming line", m_inputBuf[gdPos + 1]);
clemounet 0:67daedd6f74f 509 memmove(m_inputBuf, m_inputBuf + gdPos + 1, (m_inputPos + 1) - (gdPos + 1)); //Move null-terminating char as well
clemounet 0:67daedd6f74f 510 m_inputPos = m_inputPos - (gdPos + 1); //Adjust m_inputPos
clemounet 0:67daedd6f74f 511 }
clemounet 0:67daedd6f74f 512 lineDetected = false; //In both case search now for a new line
clemounet 0:67daedd6f74f 513 }
clemounet 0:67daedd6f74f 514 }
clemounet 0:67daedd6f74f 515 }
clemounet 0:67daedd6f74f 516 } while(lineProcessed); //If one complete line was processed there might be other incoming lines that can also be processed without reading the buffer again
clemounet 0:67daedd6f74f 517
clemounet 0:67daedd6f74f 518 //If the line could not be processed AND buffer is full, it means that we won't ever be able to process it (buffer too short)
clemounet 0:67daedd6f74f 519 if(m_inputPos == AT_INPUT_BUF_SIZE - 1)
clemounet 0:67daedd6f74f 520 {
clemounet 0:67daedd6f74f 521 //Discard everything
clemounet 0:67daedd6f74f 522 m_inputPos = 0;
clemounet 0:67daedd6f74f 523 m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
clemounet 0:67daedd6f74f 524 USB_WARN("Incoming buffer is too short to process incoming line");
clemounet 0:67daedd6f74f 525 //Look for a new line
clemounet 0:67daedd6f74f 526 lineDetected = false;
clemounet 0:67daedd6f74f 527 }
clemounet 0:67daedd6f74f 528
clemounet 0:67daedd6f74f 529 USB_DBG("Processed every full incoming lines");
clemounet 0:67daedd6f74f 530
clemounet 0:67daedd6f74f 531 return OK;
clemounet 0:67daedd6f74f 532 }
clemounet 0:67daedd6f74f 533
clemounet 0:67daedd6f74f 534 int ATCommandsInterface::trySendCommand()
clemounet 0:67daedd6f74f 535 {
clemounet 0:67daedd6f74f 536 osEvent evt = m_env2AT.get(0);
clemounet 0:67daedd6f74f 537 USB_DBG("status = %d, msg = %d", evt.status, evt.value.p);
clemounet 0:67daedd6f74f 538 if(evt.status == osEventMail)
clemounet 0:67daedd6f74f 539 {
clemounet 0:67daedd6f74f 540 int* msg = (int*) evt.value.p;
clemounet 0:67daedd6f74f 541 if( *msg == AT_CMD_READY ) //Command pending
clemounet 0:67daedd6f74f 542 {
clemounet 0:67daedd6f74f 543 if(m_transactionState != IDLE)
clemounet 0:67daedd6f74f 544 {
clemounet 0:67daedd6f74f 545 USB_WARN("Previous command not processed!");
clemounet 0:67daedd6f74f 546 }
clemounet 0:67daedd6f74f 547 USB_DBG("Sending pending command");
clemounet 0:67daedd6f74f 548 m_pStream->write((uint8_t*)m_transactionCommand, strlen(m_transactionCommand), osWaitForever);
clemounet 0:67daedd6f74f 549 char cr = CR;
clemounet 0:67daedd6f74f 550 m_pStream->write((uint8_t*)&cr, 1, osWaitForever); //Carriage return line terminator
clemounet 0:67daedd6f74f 551 m_transactionState = COMMAND_SENT;
clemounet 0:67daedd6f74f 552 }
clemounet 0:67daedd6f74f 553 else
clemounet 0:67daedd6f74f 554 {
clemounet 0:67daedd6f74f 555 m_transactionState = IDLE; //State-machine reset
clemounet 0:67daedd6f74f 556 }
clemounet 0:67daedd6f74f 557 m_env2AT.free(msg);
clemounet 0:67daedd6f74f 558 }
clemounet 0:67daedd6f74f 559 return OK;
clemounet 0:67daedd6f74f 560 }
clemounet 0:67daedd6f74f 561
clemounet 0:67daedd6f74f 562 int ATCommandsInterface::processReadLine()
clemounet 0:67daedd6f74f 563 {
clemounet 0:67daedd6f74f 564 USB_DBG("Processing read line [%s]", m_inputBuf);
clemounet 0:67daedd6f74f 565 //The line is stored in m_inputBuf
clemounet 0:67daedd6f74f 566 if(m_transactionState == COMMAND_SENT)
clemounet 0:67daedd6f74f 567 {
clemounet 0:67daedd6f74f 568 //If the command has been sent, checks echo to see if it has been received properly
clemounet 0:67daedd6f74f 569 if( strcmp(m_transactionCommand, m_inputBuf) == 0 )
clemounet 0:67daedd6f74f 570 {
clemounet 0:67daedd6f74f 571 USB_DBG("Command echo received");
clemounet 0:67daedd6f74f 572 //If so, it means that the following lines will only be solicited results
clemounet 0:67daedd6f74f 573 m_transactionState = READING_RESULT;
clemounet 0:67daedd6f74f 574 return OK;
clemounet 0:67daedd6f74f 575 }
clemounet 0:67daedd6f74f 576 }
clemounet 0:67daedd6f74f 577 if(m_transactionState == IDLE || m_transactionState == COMMAND_SENT)
clemounet 0:67daedd6f74f 578 {
clemounet 0:67daedd6f74f 579 bool found = false;
clemounet 0:67daedd6f74f 580 char* pSemicol = strchr(m_inputBuf, ':');
clemounet 0:67daedd6f74f 581 char* pData = NULL;
clemounet 0:67daedd6f74f 582 if( pSemicol != NULL ) //Split the identifier & the result code (if it exists)
clemounet 0:67daedd6f74f 583 {
clemounet 0:67daedd6f74f 584 *pSemicol = '\0';
clemounet 0:67daedd6f74f 585 pData = pSemicol + 1;
clemounet 0:67daedd6f74f 586 if(pData[0]==' ')
clemounet 0:67daedd6f74f 587 {
clemounet 0:67daedd6f74f 588 pData++; //Suppress whitespace
clemounet 0:67daedd6f74f 589 }
clemounet 0:67daedd6f74f 590 }
clemounet 0:67daedd6f74f 591 //Looks for a unsolicited result code; we can have m_transactionState == COMMAND_SENT as the code may have arrived just before we sent the command
clemounet 0:67daedd6f74f 592 m_eventsMtx.lock();
clemounet 0:67daedd6f74f 593 //Go through the list
clemounet 0:67daedd6f74f 594 for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot
clemounet 0:67daedd6f74f 595 {
clemounet 0:67daedd6f74f 596 if( m_eventsHandlers[i] != NULL )
clemounet 0:67daedd6f74f 597 {
clemounet 0:67daedd6f74f 598 if( m_eventsHandlers[i]->isATCodeHandled(m_inputBuf) )
clemounet 0:67daedd6f74f 599 {
clemounet 0:67daedd6f74f 600 m_eventsHandlers[i]->onEvent(m_inputBuf, pData);
clemounet 0:67daedd6f74f 601 found = true; //Do not break here as there might be multiple handlers for one event type
clemounet 0:67daedd6f74f 602 }
clemounet 0:67daedd6f74f 603 }
clemounet 0:67daedd6f74f 604 }
clemounet 0:67daedd6f74f 605 m_eventsMtx.unlock();
clemounet 0:67daedd6f74f 606 if(found)
clemounet 0:67daedd6f74f 607 {
clemounet 0:67daedd6f74f 608 return OK;
clemounet 0:67daedd6f74f 609 }
clemounet 0:67daedd6f74f 610 }
clemounet 0:67daedd6f74f 611 if(m_transactionState == READING_RESULT)
clemounet 0:67daedd6f74f 612 {
clemounet 0:67daedd6f74f 613 //The following lines can either be a command response or a result code (OK / ERROR / CONNECT / +CME ERROR: %s / +CMS ERROR: %s)
clemounet 0:67daedd6f74f 614 if(strcmp("OK", m_inputBuf) == 0)
clemounet 0:67daedd6f74f 615 {
clemounet 0:67daedd6f74f 616 USB_DBG("OK result received");
clemounet 0:67daedd6f74f 617 m_transactionResult.code = 0;
clemounet 0:67daedd6f74f 618 m_transactionResult.result = ATResult::AT_OK;
clemounet 0:67daedd6f74f 619 m_transactionState = IDLE;
clemounet 0:67daedd6f74f 620 int* msg = m_AT2Env.alloc(osWaitForever);
clemounet 0:67daedd6f74f 621 *msg = AT_RESULT_READY;
clemounet 0:67daedd6f74f 622 m_AT2Env.put(msg); //Command has been processed
clemounet 0:67daedd6f74f 623 return OK;
clemounet 0:67daedd6f74f 624 }
clemounet 0:67daedd6f74f 625 else if(strcmp("ERROR", m_inputBuf) == 0)
clemounet 0:67daedd6f74f 626 {
clemounet 0:67daedd6f74f 627 USB_DBG("ERROR result received");
clemounet 0:67daedd6f74f 628 m_transactionResult.code = 0;
clemounet 0:67daedd6f74f 629 m_transactionResult.result = ATResult::AT_ERROR;
clemounet 0:67daedd6f74f 630 m_transactionState = IDLE;
clemounet 0:67daedd6f74f 631 int* msg = m_AT2Env.alloc(osWaitForever);
clemounet 0:67daedd6f74f 632 *msg = AT_RESULT_READY;
clemounet 0:67daedd6f74f 633 m_AT2Env.put(msg); //Command has been processed
clemounet 0:67daedd6f74f 634 return OK;
clemounet 0:67daedd6f74f 635 }
clemounet 0:67daedd6f74f 636 else if(strncmp("CONNECT", m_inputBuf, 7 /*=strlen("CONNECT")*/) == 0) //Result can be "CONNECT" or "CONNECT %d", indicating baudrate
clemounet 0:67daedd6f74f 637 {
clemounet 0:67daedd6f74f 638 USB_DBG("CONNECT result received");
clemounet 0:67daedd6f74f 639 m_transactionResult.code = 0;
clemounet 0:67daedd6f74f 640 m_transactionResult.result = ATResult::AT_CONNECT;
clemounet 0:67daedd6f74f 641 m_transactionState = IDLE;
clemounet 0:67daedd6f74f 642 int* msg = m_AT2Env.alloc(osWaitForever);
clemounet 0:67daedd6f74f 643 *msg = AT_RESULT_READY;
clemounet 0:67daedd6f74f 644 m_AT2Env.put(msg); //Command has been processed
clemounet 0:67daedd6f74f 645 return OK;
clemounet 0:67daedd6f74f 646 }
clemounet 0:67daedd6f74f 647 else if(strcmp("COMMAND NOT SUPPORT", m_inputBuf) == 0) //Huawei-specific, not normalized
clemounet 0:67daedd6f74f 648 {
clemounet 0:67daedd6f74f 649 USB_DBG("COMMAND NOT SUPPORT result received");
clemounet 0:67daedd6f74f 650 m_transactionResult.code = 0;
clemounet 0:67daedd6f74f 651 m_transactionResult.result = ATResult::AT_ERROR;
clemounet 0:67daedd6f74f 652 m_transactionState = IDLE;
clemounet 0:67daedd6f74f 653 int* msg = m_AT2Env.alloc(osWaitForever);
clemounet 0:67daedd6f74f 654 *msg = AT_RESULT_READY;
clemounet 0:67daedd6f74f 655 m_AT2Env.put(msg); //Command has been processed
clemounet 0:67daedd6f74f 656 return OK;
clemounet 0:67daedd6f74f 657 }
clemounet 0:67daedd6f74f 658 else if(strstr(m_inputBuf, "+CME ERROR:") == m_inputBuf) //Mobile Equipment Error
clemounet 0:67daedd6f74f 659 {
clemounet 0:67daedd6f74f 660 std::sscanf(m_inputBuf + 12 /* =strlen("+CME ERROR: ") */, "%d", &m_transactionResult.code);
clemounet 0:67daedd6f74f 661 USB_DBG("+CME ERROR: %d result received", m_transactionResult.code);
clemounet 0:67daedd6f74f 662 m_transactionResult.result = ATResult::AT_CME_ERROR;
clemounet 0:67daedd6f74f 663 m_transactionState = IDLE;
clemounet 0:67daedd6f74f 664 int* msg = m_AT2Env.alloc(osWaitForever);
clemounet 0:67daedd6f74f 665 *msg = AT_RESULT_READY;
clemounet 0:67daedd6f74f 666 m_AT2Env.put(msg); //Command has been processed
clemounet 0:67daedd6f74f 667 return OK;
clemounet 0:67daedd6f74f 668 }
clemounet 0:67daedd6f74f 669 else if(strstr(m_inputBuf, "+CMS ERROR:") == m_inputBuf) //SIM Error
clemounet 0:67daedd6f74f 670 {
clemounet 0:67daedd6f74f 671 std::sscanf(m_inputBuf + 13 /* =strlen("+CME ERROR: ") */, "%d", &m_transactionResult.code);
clemounet 0:67daedd6f74f 672 USB_DBG("+CMS ERROR: %d result received", m_transactionResult.code);
clemounet 0:67daedd6f74f 673 m_transactionResult.result = ATResult::AT_CMS_ERROR;
clemounet 0:67daedd6f74f 674 m_transactionState = IDLE;
clemounet 0:67daedd6f74f 675 int* msg = m_AT2Env.alloc(osWaitForever);
clemounet 0:67daedd6f74f 676 *msg = AT_RESULT_READY;
clemounet 0:67daedd6f74f 677 m_AT2Env.put(msg); //Command has been processed
clemounet 0:67daedd6f74f 678 return OK;
clemounet 0:67daedd6f74f 679 }
clemounet 0:67daedd6f74f 680 else
clemounet 0:67daedd6f74f 681 {
clemounet 0:67daedd6f74f 682 USB_DBG("Unprocessed result received: '%s'", m_inputBuf);
clemounet 0:67daedd6f74f 683 //Must call transaction processor to complete line processing
clemounet 0:67daedd6f74f 684 int ret = m_pTransactionProcessor->onNewATResponseLine(this, m_inputBuf); //Here sendData can be called
clemounet 0:67daedd6f74f 685 return ret;
clemounet 0:67daedd6f74f 686 }
clemounet 0:67daedd6f74f 687 }
clemounet 0:67daedd6f74f 688
clemounet 0:67daedd6f74f 689 return OK;
clemounet 0:67daedd6f74f 690 }
clemounet 0:67daedd6f74f 691
clemounet 0:67daedd6f74f 692 int ATCommandsInterface::processEntryPrompt()
clemounet 0:67daedd6f74f 693 {
clemounet 0:67daedd6f74f 694 USB_DBG("Calling prompt handler");
clemounet 0:67daedd6f74f 695 int ret = m_pTransactionProcessor->onNewEntryPrompt(this); //Here sendData can be called
clemounet 0:67daedd6f74f 696
clemounet 0:67daedd6f74f 697 if( ret != NET_MOREINFO ) //A new prompt is expected
clemounet 0:67daedd6f74f 698 {
clemounet 0:67daedd6f74f 699 USB_DBG("Sending break character");
clemounet 0:67daedd6f74f 700 //Send CTRL+Z (break sequence) to exit prompt
clemounet 0:67daedd6f74f 701 char seq[2] = {BRK, 0x00};
clemounet 0:67daedd6f74f 702 sendData(seq);
clemounet 0:67daedd6f74f 703 }
clemounet 0:67daedd6f74f 704 return OK;
clemounet 0:67daedd6f74f 705 }
clemounet 0:67daedd6f74f 706
clemounet 0:67daedd6f74f 707 //Commands that can be called during onNewATResponseLine callback, additionally to close()
clemounet 0:67daedd6f74f 708 //Access to this method is protected (can ONLY be called on processing thread during IATCommandsProcessor::onNewATResponseLine execution)
clemounet 0:67daedd6f74f 709 int ATCommandsInterface::sendData(const char* data)
clemounet 0:67daedd6f74f 710 {
clemounet 0:67daedd6f74f 711 //m_inputBuf is cleared at this point (and MUST therefore be empty)
clemounet 0:67daedd6f74f 712 int dataLen = strlen(data);
clemounet 0:67daedd6f74f 713 USB_DBG("Sending raw string of length %d", dataLen);
clemounet 0:67daedd6f74f 714 int ret = m_pStream->write((uint8_t*)data, dataLen, osWaitForever);
clemounet 0:67daedd6f74f 715 if(ret)
clemounet 0:67daedd6f74f 716 {
clemounet 0:67daedd6f74f 717 USB_WARN("Could not write to stream (returned %d)", ret);
clemounet 0:67daedd6f74f 718 return ret;
clemounet 0:67daedd6f74f 719 }
clemounet 0:67daedd6f74f 720
clemounet 0:67daedd6f74f 721 int dataPos = 0;
clemounet 0:67daedd6f74f 722 do
clemounet 0:67daedd6f74f 723 {
clemounet 0:67daedd6f74f 724 //Read echo
clemounet 0:67daedd6f74f 725 size_t readLen;
clemounet 0:67daedd6f74f 726 int ret = m_pStream->read((uint8_t*)m_inputBuf, &readLen, MIN(dataLen - dataPos, AT_INPUT_BUF_SIZE - 1), osWaitForever); //Make sure we do not read more than needed otherwise it could break the parser
clemounet 0:67daedd6f74f 727 if(ret)
clemounet 0:67daedd6f74f 728 {
clemounet 0:67daedd6f74f 729 USB_WARN("Could not read from stream (returned %d)", ret);
clemounet 0:67daedd6f74f 730 m_inputPos = 0; //Reset input buffer state
clemounet 0:67daedd6f74f 731 m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
clemounet 0:67daedd6f74f 732 return ret;
clemounet 0:67daedd6f74f 733 }
clemounet 0:67daedd6f74f 734
clemounet 0:67daedd6f74f 735 if( memcmp(m_inputBuf, data + dataPos, readLen) != 0 )
clemounet 0:67daedd6f74f 736 {
clemounet 0:67daedd6f74f 737 //Echo does not match output
clemounet 0:67daedd6f74f 738 USB_WARN("Echo does not match output");
clemounet 0:67daedd6f74f 739 m_inputPos = 0; //Reset input buffer state
clemounet 0:67daedd6f74f 740 m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
clemounet 0:67daedd6f74f 741 return NET_DIFF;
clemounet 0:67daedd6f74f 742 }
clemounet 0:67daedd6f74f 743
clemounet 0:67daedd6f74f 744 dataPos += readLen;
clemounet 0:67daedd6f74f 745 //If all characters have not been read yet
clemounet 0:67daedd6f74f 746
clemounet 0:67daedd6f74f 747 } while(dataPos < dataLen);
clemounet 0:67daedd6f74f 748
clemounet 0:67daedd6f74f 749 USB_DBG("String sent successfully");
clemounet 0:67daedd6f74f 750
clemounet 0:67daedd6f74f 751 m_inputPos = 0; //Reset input buffer state
clemounet 0:67daedd6f74f 752 m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
clemounet 0:67daedd6f74f 753
clemounet 0:67daedd6f74f 754 return OK;
clemounet 0:67daedd6f74f 755 }
clemounet 0:67daedd6f74f 756
clemounet 0:67daedd6f74f 757 /*static*/ void ATCommandsInterface::staticCallback(void const* p)
clemounet 0:67daedd6f74f 758 {
clemounet 0:67daedd6f74f 759 ((ATCommandsInterface*)p)->process();
clemounet 0:67daedd6f74f 760 }
clemounet 0:67daedd6f74f 761
clemounet 0:67daedd6f74f 762 int ATCommandsInterface::ATResultToReturnCode(ATResult result) //Helper
clemounet 0:67daedd6f74f 763 {
clemounet 0:67daedd6f74f 764 if(result.result == ATResult::AT_OK)
clemounet 0:67daedd6f74f 765 {
clemounet 0:67daedd6f74f 766 return OK;
clemounet 0:67daedd6f74f 767 }
clemounet 0:67daedd6f74f 768 else
clemounet 0:67daedd6f74f 769 {
clemounet 0:67daedd6f74f 770 return NET_MOREINFO;
clemounet 0:67daedd6f74f 771 }
clemounet 0:67daedd6f74f 772 }
clemounet 0:67daedd6f74f 773
clemounet 0:67daedd6f74f 774 /*virtual*/ int ATCommandsInterface::onNewATResponseLine(ATCommandsInterface* pInst, const char* line) //Default implementation for simple commands handling
clemounet 0:67daedd6f74f 775 {
clemounet 0:67daedd6f74f 776 return OK;
clemounet 0:67daedd6f74f 777 }
clemounet 0:67daedd6f74f 778
clemounet 0:67daedd6f74f 779 /*virtual*/ int ATCommandsInterface::onNewEntryPrompt(ATCommandsInterface* pInst) //Default implementation (just sends Ctrl+Z to exit the prompt by returning OK right-away)
clemounet 0:67daedd6f74f 780 {
clemounet 0:67daedd6f74f 781 return OK;
clemounet 0:67daedd6f74f 782 }
clemounet 0:67daedd6f74f 783
clemounet 0:67daedd6f74f 784 void ATCommandsInterface::process() //Processing thread
clemounet 0:67daedd6f74f 785 {
clemounet 0:67daedd6f74f 786 USB_DBG("AT Thread started");
clemounet 0:67daedd6f74f 787 while(true)
clemounet 0:67daedd6f74f 788 {
clemounet 0:67daedd6f74f 789 USB_DBG("AT Processing on hold");
clemounet 0:67daedd6f74f 790 m_processingThread.signal_wait(AT_SIG_PROCESSING_START); //Block until the process is started
clemounet 0:67daedd6f74f 791
clemounet 0:67daedd6f74f 792 m_processingMtx.lock();
clemounet 0:67daedd6f74f 793 USB_DBG("AT Processing started");
clemounet 0:67daedd6f74f 794 //First of all discard buffer
clemounet 0:67daedd6f74f 795 int ret;
clemounet 0:67daedd6f74f 796 size_t readLen;
clemounet 0:67daedd6f74f 797 do //Drop everything
clemounet 0:67daedd6f74f 798 {
clemounet 0:67daedd6f74f 799 ret = m_pStream->read((uint8_t*)m_inputBuf, &readLen, AT_INPUT_BUF_SIZE - 1, 0); //Do NOT wait at this point
clemounet 0:67daedd6f74f 800 } while(ret == OK);
clemounet 0:67daedd6f74f 801 m_inputPos = 0; //Clear input buffer
clemounet 0:67daedd6f74f 802 do
clemounet 0:67daedd6f74f 803 {
clemounet 0:67daedd6f74f 804 USB_DBG("Trying to read a new line");
clemounet 0:67daedd6f74f 805 tryReadLine();
clemounet 0:67daedd6f74f 806 USB_DBG("Trying to send a pending command");
clemounet 0:67daedd6f74f 807 trySendCommand();
clemounet 0:67daedd6f74f 808 } while( m_processingThread.signal_wait(AT_SIG_PROCESSING_STOP, 0).status != osEventSignal ); //Loop until the process is interrupted
clemounet 0:67daedd6f74f 809 m_processingMtx.unlock();
clemounet 0:67daedd6f74f 810 USB_DBG("AT Processing stopped");
clemounet 0:67daedd6f74f 811 }
clemounet 0:67daedd6f74f 812 }
clemounet 0:67daedd6f74f 813