Implementation of 3G USB Modem Huawei E372
ATCommandsInterface.cpp@0:67daedd6f74f, 2015-02-20 (annotated)
- 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?
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 | 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 |