Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers CellularConnectionFSM.cpp Source File

CellularConnectionFSM.cpp

00001 /*
00002  * Copyright (c) 2017, Arm Limited and affiliates.
00003  * SPDX-License-Identifier: Apache-2.0
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License");
00006  * you may not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  *     http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 #include "CellularConnectionFSM.h"
00019 
00020 #ifdef CELLULAR_DEVICE
00021 
00022 #ifndef MBED_TRACE_MAX_LEVEL
00023 #define MBED_TRACE_MAX_LEVEL TRACE_LEVEL_INFO
00024 #endif
00025 #include "CellularLog.h"
00026 
00027 // timeout to wait for AT responses
00028 #define TIMEOUT_POWER_ON     (1*1000)
00029 #define TIMEOUT_SIM_PIN      (1*1000)
00030 #define TIMEOUT_NETWORK      (10*1000)
00031 #define TIMEOUT_REGISTRATION (180*1000)
00032 
00033 // maximum time when retrying network register, attach and connect in seconds ( 20minutes )
00034 #define TIMEOUT_NETWORK_MAX (20*60)
00035 
00036 #define RETRY_COUNT_DEFAULT 3
00037 
00038 namespace mbed {
00039 
00040 CellularConnectionFSM::CellularConnectionFSM() :
00041         _serial(0), _state(STATE_INIT), _next_state(_state), _status_callback(0), _network(0), _power(0), _sim(0),
00042         _queue(8 * EVENTS_EVENT_SIZE), _queue_thread(0), _retry_count(0), _state_retry_count(0), _at_queue(8 * EVENTS_EVENT_SIZE)
00043 {
00044     memset(_sim_pin, 0, sizeof(_sim_pin));
00045 #if MBED_CONF_CELLULAR_RANDOM_MAX_START_DELAY == 0
00046     _start_time = 0;
00047 #else
00048     _start_time = rand() % (MBED_CONF_CELLULAR_RANDOM_MAX_START_DELAY);
00049 #endif // MBED_CONF_CELLULAR_RANDOM_MAX_START_DELAY
00050 
00051     // set initial retry values in seconds
00052     _retry_timeout_array[0] = 1;
00053     _retry_timeout_array[1] = 2;
00054     _retry_timeout_array[2] = 4;
00055     _retry_timeout_array[3] = 16;
00056     _retry_timeout_array[4] = 32;
00057     _retry_timeout_array[5] = 60;
00058     _retry_timeout_array[6] = 120;
00059     _retry_timeout_array[7] = 360;
00060     _retry_timeout_array[8] = 600;
00061     _retry_timeout_array[9] = TIMEOUT_NETWORK_MAX;
00062     _retry_array_length = MAX_RETRY_ARRAY_SIZE;
00063     
00064     _cellularDevice = new CELLULAR_DEVICE(_at_queue);
00065 }
00066 
00067 CellularConnectionFSM::~CellularConnectionFSM()
00068 {
00069     stop();
00070 }
00071 
00072 nsapi_error_t CellularConnectionFSM::init()
00073 {
00074     _power = _cellularDevice->open_power(_serial);
00075     if (!_power) {
00076         stop();
00077         return NSAPI_ERROR_NO_MEMORY ;
00078     }
00079     _network = _cellularDevice->open_network(_serial);
00080     if (!_network) {
00081         stop();
00082         return NSAPI_ERROR_NO_MEMORY ;
00083     }
00084 
00085     _sim = _cellularDevice->open_sim(_serial);
00086     if (!_sim) {
00087         stop();
00088         return NSAPI_ERROR_NO_MEMORY ;
00089     }
00090 
00091     _at_queue.chain(&_queue);
00092 
00093     tr_info("init done...");
00094     return NSAPI_ERROR_OK ;
00095 }
00096 
00097 bool CellularConnectionFSM::open_power(FileHandle *fh)
00098 {
00099     if (!_power) {
00100         _power = _cellularDevice->open_power(fh);
00101         if (!_power) {
00102             return false;
00103         }
00104     }
00105     nsapi_error_t err = _power->on();
00106     if (err != NSAPI_ERROR_OK  && err != NSAPI_ERROR_UNSUPPORTED ) {
00107         tr_warn("Cellular start failed. Power off/on.");
00108         err = _power->off();
00109         if (err != NSAPI_ERROR_OK  && err != NSAPI_ERROR_UNSUPPORTED ) {
00110             tr_error("Cellular power down failed!");
00111         }
00112         return false;
00113     }
00114     return true;
00115 }
00116 
00117 void CellularConnectionFSM::set_sim_pin(const char * sim_pin)
00118 {
00119     strncpy(_sim_pin, sim_pin, sizeof(_sim_pin));
00120 }
00121 
00122 bool CellularConnectionFSM::open_sim()
00123 {
00124     CellularSIM::SimState state = CellularSIM::SimStateUnknown;
00125     // wait until SIM is readable
00126     // here you could add wait(secs) if you know start delay of your SIM
00127     while (_sim->get_sim_state(state) != NSAPI_ERROR_OK  || state == CellularSIM::SimStateUnknown) {
00128         tr_info("Waiting for SIM (state %d)...", state);
00129         return false;
00130     }
00131     tr_info("Initial SIM state: %d", state);
00132 
00133     if (strlen(_sim_pin)) {
00134         nsapi_error_t err;
00135         if (state == CellularSIM::SimStatePinNeeded) {
00136             tr_info("SIM pin required, entering pin: %s", _sim_pin);
00137             err = _sim->set_pin(_sim_pin);
00138             if (err) {
00139                 tr_error("SIM pin set failed with: %d, bailing out...", err);
00140                 return false;
00141             }
00142             // here you could add wait(secs) if you know delay of changing PIN on your SIM
00143             for (int i = 0; i < MAX_SIM_READY_WAITING_TIME; i++) {
00144                 if (_sim->get_sim_state(state) == NSAPI_ERROR_OK  && state == CellularSIM::SimStateReady) {
00145                     break;
00146                 }
00147                 tr_info("SIM state: %d", state);
00148                 return false;
00149             }
00150         }
00151     } else {
00152         tr_info("No SIM pin provided.");
00153     }
00154 
00155     return state == CellularSIM::SimStateReady;
00156 }
00157 
00158 void CellularConnectionFSM::device_ready()
00159 {
00160     CellularInformation *info = _cellularDevice->open_information(_serial);
00161     char device_info_buf[2048]; // may be up to 2048 according to 3GPP
00162 
00163     if (info->get_manufacturer(device_info_buf, sizeof(device_info_buf)) == NSAPI_ERROR_OK ) {
00164         tr_info("Cellular device manufacturer: %s", device_info_buf);
00165     }
00166     if (info->get_model(device_info_buf, sizeof(device_info_buf)) == NSAPI_ERROR_OK ) {
00167         tr_info("Cellular device model: %s", device_info_buf);
00168     }
00169     if (info->get_revision(device_info_buf, sizeof(device_info_buf)) == NSAPI_ERROR_OK ) {
00170         tr_info("Cellular device revision: %s", device_info_buf);
00171     }
00172 }
00173 
00174 bool CellularConnectionFSM::set_network_registration(char *plmn)
00175 {
00176     if (_network->set_registration(plmn) != NSAPI_ERROR_OK ) {
00177         tr_error("Failed to set network registration.");
00178         return false;
00179     }
00180     return true;
00181 }
00182 
00183 bool CellularConnectionFSM::get_network_registration(CellularNetwork::RegistrationType type,
00184         CellularNetwork::RegistrationStatus &status, bool &is_registered)
00185 {
00186     is_registered = false;
00187     bool is_roaming = false;
00188     nsapi_error_t err = _network->get_registration_status(type, status);
00189     if (err != NSAPI_ERROR_OK ) {
00190         if (err != NSAPI_ERROR_UNSUPPORTED ) {
00191             tr_warn("Get network registration failed (type %d)!", type);
00192         }
00193         return false;
00194     }
00195     switch (status) {
00196         case CellularNetwork::RegisteredRoaming:
00197             is_roaming = true;
00198             // fall-through
00199         case CellularNetwork::RegisteredHomeNetwork:
00200             is_registered = true;
00201             break;
00202         case CellularNetwork::RegisteredSMSOnlyRoaming:
00203             is_roaming = true;
00204             // fall-through
00205         case CellularNetwork::RegisteredSMSOnlyHome:
00206             tr_warn("SMS only network registration!");
00207             break;
00208         case CellularNetwork::RegisteredCSFBNotPreferredRoaming:
00209             is_roaming = true;
00210             // fall-through
00211         case CellularNetwork::RegisteredCSFBNotPreferredHome:
00212             tr_warn("Not preferred network registration!");
00213             break;
00214         case CellularNetwork::AttachedEmergencyOnly:
00215             tr_warn("Emergency only network registration!");
00216             break;
00217         case CellularNetwork::RegistrationDenied:
00218         case CellularNetwork::NotRegistered:
00219         case CellularNetwork::Unknown:
00220         case CellularNetwork::SearchingNetwork:
00221         default:
00222             break;
00223     }
00224 
00225     if (is_roaming) {
00226         tr_warn("Roaming cellular network!");
00227     }
00228 
00229     return true;
00230 }
00231 
00232 bool CellularConnectionFSM::get_attach_network(CellularNetwork::AttachStatus &status)
00233 {
00234     nsapi_error_t err = _network->get_attach(status);
00235     if (err != NSAPI_ERROR_OK ) {
00236         return false;
00237     }
00238     return true;
00239 }
00240 
00241 bool CellularConnectionFSM::set_attach_network()
00242 {
00243     nsapi_error_t attach_err = _network->set_attach();
00244     if (attach_err != NSAPI_ERROR_OK ) {
00245         return false;
00246     }
00247     return true;
00248 }
00249 
00250 void CellularConnectionFSM::report_failure(const char* msg)
00251 {
00252     tr_error("Cellular network failed: %s", msg);
00253     if (_status_callback) {
00254         _status_callback(_state, _next_state);
00255     }
00256 }
00257 
00258 nsapi_error_t CellularConnectionFSM::continue_to_state(CellularState state)
00259 {
00260     if (state < _state) {
00261         _state = state;
00262     }
00263     if (!_queue.call_in(0, callback(this, &CellularConnectionFSM::event))) {
00264         stop();
00265         return NSAPI_ERROR_NO_MEMORY ;
00266     }
00267 
00268     return NSAPI_ERROR_OK ;
00269 }
00270 
00271 void CellularConnectionFSM::event()
00272 {
00273     nsapi_error_t err;
00274     int event_timeout = -1;
00275 
00276     switch (_state) {
00277         case STATE_INIT:
00278             event_timeout = _start_time;
00279             tr_info("INIT state, waiting %d ms before POWER state)", _start_time);
00280             _next_state = STATE_POWER_ON;
00281             break;
00282         case STATE_POWER_ON:
00283             _cellularDevice->set_timeout(TIMEOUT_POWER_ON);
00284             tr_info("Cellular power ON (timeout %d ms)", TIMEOUT_POWER_ON);
00285             if (open_power(_serial)) {
00286                 _next_state = STATE_DEVICE_READY;
00287                 _retry_count = 0;
00288             } else {
00289                 if (++_retry_count <= RETRY_COUNT_DEFAULT) {
00290                     tr_warn("Power ON retry %d", _retry_count);
00291                     event_timeout = 3 * 1000;
00292                 } else {
00293                     report_failure("Power");
00294                     return;
00295                 }
00296             }
00297             break;
00298         case STATE_DEVICE_READY:
00299             _cellularDevice->set_timeout(TIMEOUT_POWER_ON);
00300             if (_power->set_at_mode() == NSAPI_ERROR_OK ) {
00301                 tr_info("Cellular device ready");
00302                 _next_state = STATE_SIM_PIN;
00303                 _retry_count = 0;
00304                 device_ready();
00305             } else {
00306                 tr_info("Waiting for cellular device (retry %d/%d, timeout %d ms)", _retry_count, RETRY_COUNT_DEFAULT,
00307                         TIMEOUT_POWER_ON);
00308                 if (_retry_count++ <= RETRY_COUNT_DEFAULT) {
00309                     event_timeout = 3 * 1000;
00310                 } else {
00311                     report_failure("Power");
00312                     return;
00313                 }
00314             }
00315             break;
00316         case STATE_SIM_PIN:
00317             _cellularDevice->set_timeout(TIMEOUT_SIM_PIN);
00318             tr_info("Start cellular (timeout %d ms)", TIMEOUT_SIM_PIN);
00319             if (open_sim()) {
00320                 _next_state = STATE_REGISTERING_NETWORK;
00321                 _retry_count = 0;
00322                 _state_retry_count = 0;
00323                 tr_info("Check for network registration");
00324             } else {
00325                 if (_retry_count++ <= RETRY_COUNT_DEFAULT) {
00326                     tr_warn("Waiting for SIM %d/%d", _retry_count, RETRY_COUNT_DEFAULT);
00327                     event_timeout = 3 * 1000;
00328                 } else {
00329                     report_failure("Entering SIM PIN");
00330                     return;
00331                 }
00332             }
00333             break;
00334         case STATE_REGISTERING_NETWORK:
00335             _cellularDevice->set_timeout(TIMEOUT_NETWORK);
00336             CellularNetwork::RegistrationStatus status;
00337             bool is_registered;
00338             _next_state = STATE_REGISTER_NETWORK;
00339             for (int type = 0; type < CellularNetwork::C_MAX; type++) {
00340                 if (get_network_registration((CellularNetwork::RegistrationType) type, status, is_registered)) {
00341                     tr_debug("get_network_registration: type=%d, status=%d", type, status);
00342                     if (is_registered) {
00343                         tr_info("Registered to cellular network (type %d, status %d)", type, status);
00344                         _next_state = STATE_ATTACHING_NETWORK;
00345                         _retry_count = 0;
00346                         _state_retry_count = 0;
00347                         event_timeout = 0;
00348                         tr_info("Check cellular network attach state");
00349                         break;
00350                     } else {
00351                         if (_retry_count < 180) {
00352                             event_timeout = 1000;
00353                             _next_state = STATE_REGISTERING_NETWORK;
00354                             tr_info("Waiting for registration %d/180 (type %d, status %d)", _retry_count, type, status);
00355                         } else {
00356                             tr_info("Start cellular registration");
00357                             _next_state = STATE_REGISTER_NETWORK;
00358                             _retry_count = 0;
00359                             break;
00360                         }
00361                     }
00362                 }
00363             }
00364 
00365             if (_next_state == STATE_REGISTERING_NETWORK) {
00366                 _retry_count++;
00367             }
00368             break;
00369         case STATE_REGISTER_NETWORK:
00370             _cellularDevice->set_timeout(TIMEOUT_REGISTRATION);
00371             tr_info("Register to cellular network (timeout %d ms)", TIMEOUT_REGISTRATION);
00372             if (set_network_registration()) {
00373                 _next_state = STATE_REGISTERING_NETWORK;
00374                 _retry_count = 0;
00375                 if (_state_retry_count > RETRY_COUNT_DEFAULT) {
00376                     report_failure("Registration retry");
00377                     return;
00378                 }
00379                 _state_retry_count++;
00380             } else {
00381                 if (_retry_count < _retry_array_length) {
00382                     event_timeout = _retry_timeout_array[_retry_count] * 1000;
00383                     _retry_count++;
00384                 } else {
00385                     report_failure("Registration");
00386                     return;
00387                 }
00388             }
00389             break;
00390         case STATE_ATTACHING_NETWORK:
00391             _cellularDevice->set_timeout(TIMEOUT_NETWORK);
00392             CellularNetwork::AttachStatus attach_status;
00393             if (get_attach_network(attach_status)) {
00394                 if (attach_status == CellularNetwork::Attached) {
00395                     _next_state = STATE_CONNECT_NETWORK;
00396                     _retry_count = 0;
00397                 } else {
00398                     _next_state = STATE_ATTACH_NETWORK;
00399                     _retry_count = 0;
00400                 }
00401             } else {
00402                 if (_retry_count++ <= RETRY_COUNT_DEFAULT) {
00403                     event_timeout = 1 * 1000;
00404                 } else {
00405                     report_failure("Attaching");
00406                     return;
00407                 }
00408             }
00409             break;
00410         case STATE_ATTACH_NETWORK:
00411             _cellularDevice->set_timeout(TIMEOUT_NETWORK);
00412             tr_info("Attach to cellular network (timeout %d ms)", TIMEOUT_NETWORK);
00413             if (set_attach_network()) {
00414                 _next_state = STATE_ATTACHING_NETWORK;
00415                 _retry_count = 0;
00416                 if (_state_retry_count >= RETRY_COUNT_DEFAULT) {
00417                     report_failure("Attach retry");
00418                     return;
00419                 }
00420                 _state_retry_count++;
00421                 tr_info("Cellular network attaching");
00422             } else {
00423                 if (_retry_count < _retry_array_length) {
00424                     event_timeout = _retry_timeout_array[_retry_count] * 1000;
00425                     _retry_count++;
00426                 } else {
00427                     report_failure("Attach");
00428                     return;
00429                 }
00430             }
00431             break;
00432         case STATE_CONNECT_NETWORK:
00433             _cellularDevice->set_timeout(TIMEOUT_NETWORK);
00434             tr_info("Connect to cellular network (timeout %d ms)", TIMEOUT_NETWORK);
00435             err = _network->connect();
00436             if (!err) {
00437                 _next_state = STATE_CONNECTED;
00438             } else {
00439                 if (_retry_count < _retry_array_length) {
00440                     event_timeout = _retry_timeout_array[_retry_count] * 1000;
00441                     _retry_count++;
00442                 } else {
00443                     report_failure("Network Connect");
00444                     return;
00445                 }
00446             }
00447             break;
00448         case STATE_CONNECTED:
00449             _cellularDevice->set_timeout(TIMEOUT_NETWORK);
00450             tr_debug("Cellular ready! (timeout %d ms)", TIMEOUT_NETWORK);
00451             if (_status_callback) {
00452                 if (!_status_callback(_state, _next_state)) {
00453                     return;
00454                 }
00455             }
00456             break;
00457         default:
00458             MBED_ASSERT(0);
00459             break;
00460     }
00461 
00462     if (_next_state != _state || event_timeout >= 0) {
00463         if (_next_state != _state) { // state exit condition
00464             tr_info("Cellular state from %d to %d", _state, _next_state);
00465             if (_status_callback) {
00466                 if (!_status_callback(_state, _next_state)) {
00467                     return;
00468                 }
00469             }
00470         } else {
00471             if (event_timeout == 0) {
00472                 static int retry_count = 0;
00473                 if (++retry_count <= 3) {
00474                     tr_info("Cellular event retry %d", retry_count);
00475                 } else {
00476                     report_failure("Cellular connection failed!");
00477                     return;
00478                 }
00479             } else {
00480                 tr_info("Cellular event in %d milliseconds", event_timeout);
00481             }
00482         }
00483         _state = _next_state;
00484         if (event_timeout == -1) {
00485             event_timeout = 0;
00486         }
00487         if (!_queue.call_in(event_timeout, callback(this, &CellularConnectionFSM::event))) {
00488             report_failure("Cellular event failure!");
00489             return;
00490         }
00491     }
00492 }
00493 
00494 nsapi_error_t CellularConnectionFSM::start_dispatch()
00495 {
00496     tr_info("CellularConnectionUtil::start");
00497     tr_info("Create cellular thread");
00498 
00499     MBED_ASSERT(!_queue_thread);
00500 
00501     _queue_thread = new rtos::Thread;
00502     if (!_queue_thread) {
00503         stop();
00504         return NSAPI_ERROR_NO_MEMORY ;
00505     }
00506     if (_queue_thread->start(callback(&_queue, &events::EventQueue::dispatch_forever)) != osOK) {
00507         stop();
00508         return NSAPI_ERROR_NO_MEMORY ;
00509     }
00510 
00511     tr_info("CellularConnectionUtil::started");
00512     return NSAPI_ERROR_OK ;
00513 }
00514 
00515 void CellularConnectionFSM::stop()
00516 {
00517     tr_info("CellularConnectionUtil::stop");
00518     _cellularDevice->close_power();
00519     _cellularDevice->close_network();
00520     if (_queue_thread) {
00521         _queue_thread->terminate();
00522         _queue_thread = NULL;
00523     }
00524 }
00525 
00526 void CellularConnectionFSM::set_serial(UARTSerial *serial)
00527 {
00528     _serial = serial;
00529 }
00530 
00531 void CellularConnectionFSM::set_callback(mbed::Callback<bool(int, int)> status_callback)
00532 {
00533     _status_callback = status_callback;
00534 }
00535 
00536 events::EventQueue *CellularConnectionFSM::get_queue()
00537 {
00538     return &_queue;
00539 }
00540 
00541 CellularNetwork* CellularConnectionFSM::get_network()
00542 {
00543     return _network;
00544 }
00545 
00546 CellularDevice* CellularConnectionFSM::get_device()
00547 {
00548     return _cellularDevice;
00549 }
00550 
00551 CellularSIM* CellularConnectionFSM::get_sim()
00552 {
00553     return _sim;
00554 }
00555 
00556 NetworkStack *CellularConnectionFSM::get_stack()
00557 {
00558     return _cellularDevice->get_stack();
00559 }
00560 
00561 void CellularConnectionFSM::set_retry_timeout_array(uint16_t timeout[], int array_len)
00562 {
00563     _retry_array_length = array_len > MAX_RETRY_ARRAY_SIZE ? MAX_RETRY_ARRAY_SIZE : array_len;
00564 
00565     for (int i = 0; i < _retry_array_length; i++) {
00566         _retry_timeout_array[i] = timeout[i];
00567     }
00568 }
00569 
00570 } // namespace
00571 
00572 #endif // CELLULAR_DEVICE