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