Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers CellularStateMachine.cpp Source File

CellularStateMachine.cpp

00001 /*
00002  * Copyright (c) 2018, 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 "CellularStateMachine.h"
00019 #include "CellularDevice.h"
00020 #include "CellularLog.h"
00021 #include "Thread.h"
00022 #include "mbed_shared_queues.h"
00023 
00024 #ifndef MBED_TRACE_MAX_LEVEL
00025 #define MBED_TRACE_MAX_LEVEL TRACE_LEVEL_INFO
00026 #endif
00027 
00028 // timeout to wait for AT responses
00029 #define TIMEOUT_POWER_ON     (1*1000)
00030 #define TIMEOUT_SIM_PIN      (1*1000)
00031 #define TIMEOUT_NETWORK      (10*1000)
00032 /** CellularStateMachine does connecting up to packet service attach, and
00033  *  after that it's up to CellularContext::connect() to connect to PDN.
00034  *  If CellularContext or an application does not set timeout (via `CellularDevice::set_timeout`)
00035  *  then TIMEOUT_CONNECT is used also for connecting to PDN and also for socket operations.
00036  */
00037 #define TIMEOUT_CONNECT      (60*1000)
00038 #define TIMEOUT_REGISTRATION (180*1000)
00039 
00040 // maximum time when retrying network register, attach and connect in seconds ( 20minutes )
00041 #define TIMEOUT_NETWORK_MAX (20*60)
00042 
00043 #define RETRY_COUNT_DEFAULT 3
00044 
00045 
00046 const int STM_STOPPED = -99;
00047 const int ACTIVE_PDP_CONTEXT = 0x01;
00048 const int ATTACHED_TO_NETWORK = 0x02;
00049 const int DEVICE_READY = 0x04;
00050 
00051 namespace mbed {
00052 
00053 CellularStateMachine::CellularStateMachine(CellularDevice &device, events::EventQueue &queue, CellularNetwork &nw) :
00054 #ifdef MBED_CONF_RTOS_PRESENT
00055     _queue_thread(0),
00056 #endif
00057     _cellularDevice(device), _state(STATE_INIT), _next_state(_state), _target_state(_state),
00058     _event_status_cb(0), _network(nw), _queue(queue), _sim_pin(0), _retry_count(0),
00059     _event_timeout(-1), _event_id(-1), _plmn(0), _command_success(false),
00060     _is_retry(false), _cb_data(), _current_event(CellularDeviceReady), _status(0)
00061 {
00062 #if MBED_CONF_CELLULAR_RANDOM_MAX_START_DELAY == 0
00063     _start_time = 0;
00064 #else
00065     // so that not every device don't start at the exact same time (for example after power outage)
00066     _start_time = rand() % (MBED_CONF_CELLULAR_RANDOM_MAX_START_DELAY);
00067 #endif // MBED_CONF_CELLULAR_RANDOM_MAX_START_DELAY
00068 
00069     // set initial retry values in seconds
00070     _retry_timeout_array[0] = 1; // double time on each retry in order to keep network happy
00071     _retry_timeout_array[1] = 2;
00072     _retry_timeout_array[2] = 4;
00073     _retry_timeout_array[3] = 8;
00074     _retry_timeout_array[4] = 16;
00075     _retry_timeout_array[5] = 32;
00076     _retry_timeout_array[6] = 64;
00077     _retry_timeout_array[7] = 128; // if around two minutes was not enough then let's wait much longer
00078     _retry_timeout_array[8] = 600;
00079     _retry_timeout_array[9] = TIMEOUT_NETWORK_MAX;
00080     _retry_array_length = CELLULAR_RETRY_ARRAY_SIZE;
00081 
00082     _state_timeout_power_on = TIMEOUT_POWER_ON;
00083     _state_timeout_sim_pin = TIMEOUT_SIM_PIN;
00084     _state_timeout_registration = TIMEOUT_REGISTRATION;
00085     _state_timeout_network = TIMEOUT_NETWORK;
00086     _state_timeout_connect = TIMEOUT_CONNECT;
00087 }
00088 
00089 CellularStateMachine::~CellularStateMachine()
00090 {
00091     tr_debug("CellularStateMachine destruct");
00092     stop();
00093 }
00094 
00095 void CellularStateMachine::reset()
00096 {
00097     _state = STATE_INIT;
00098     _event_timeout = -1;
00099     _event_id = -1;
00100     _is_retry = false;
00101     _status = 0;
00102     _target_state = STATE_INIT;
00103     enter_to_state(STATE_INIT);
00104 }
00105 
00106 void CellularStateMachine::stop()
00107 {
00108     tr_debug("CellularStateMachine stop");
00109 #ifdef MBED_CONF_RTOS_PRESENT
00110     if (_queue_thread) {
00111         _queue_thread->terminate();
00112         delete _queue_thread;
00113         _queue_thread = NULL;
00114     }
00115 #else
00116     _queue.chain(NULL);
00117 #endif
00118 
00119     reset();
00120     _event_id = STM_STOPPED;
00121 }
00122 
00123 bool CellularStateMachine::power_on()
00124 {
00125     _cb_data.error = _cellularDevice.hard_power_on();
00126     if (_cb_data.error != NSAPI_ERROR_OK ) {
00127         tr_warn("Hard power on failed.");
00128         return false;
00129     }
00130     return true;
00131 }
00132 
00133 void CellularStateMachine::set_sim_pin(const char *sim_pin)
00134 {
00135     _sim_pin = sim_pin;
00136 }
00137 
00138 void CellularStateMachine::set_plmn(const char *plmn)
00139 {
00140     _plmn = plmn;
00141 }
00142 
00143 bool CellularStateMachine::open_sim()
00144 {
00145     CellularDevice::SimState state = CellularDevice::SimStateUnknown;
00146     // wait until SIM is readable
00147     // here you could add wait(secs) if you know start delay of your SIM
00148     _cb_data.error = _cellularDevice.get_sim_state(state);
00149     if (_cb_data.error != NSAPI_ERROR_OK ) {
00150         tr_info("Waiting for SIM (err while reading)...");
00151         return false;
00152     }
00153 
00154     // report current state so callback can set sim pin if needed
00155     _cb_data.status_data = state;
00156     send_event_cb(CellularSIMStatusChanged);
00157 
00158     if (state == CellularDevice::SimStatePinNeeded) {
00159         if (_sim_pin) {
00160             tr_info("Entering PIN to open SIM");
00161             _cb_data.error = _cellularDevice.set_pin(_sim_pin);
00162             if (_cb_data.error) {
00163                 tr_error("Failed to set PIN: error %d", _cb_data.error);
00164             }
00165         } else {
00166             // No sim pin provided even it's needed, stop state machine
00167             tr_error("PIN required but no SIM pin provided.");
00168             _retry_count = CELLULAR_RETRY_ARRAY_SIZE;
00169             return false;
00170         }
00171     }
00172 
00173     bool sim_ready = state == CellularDevice::SimStateReady;
00174 
00175     if (sim_ready) {
00176 #ifdef MBED_CONF_CELLULAR_CLEAR_ON_CONNECT
00177         if (_cellularDevice.clear() != NSAPI_ERROR_OK ) {
00178             tr_warning("CellularDevice clear failed");
00179             return false;
00180         }
00181 #endif
00182         _cb_data.error = _network.set_registration(_plmn);
00183         tr_debug("STM: set_registration: %d, plmn: %s", _cb_data.error, _plmn ? _plmn : "NULL");
00184         if (_cb_data.error) {
00185             return false;
00186         }
00187     }
00188 
00189     return sim_ready;
00190 }
00191 
00192 bool CellularStateMachine::is_registered()
00193 {
00194     CellularNetwork::RegistrationStatus status;
00195     bool is_registered = false;
00196 
00197     // accept only CGREG/CEREG. CREG is for circuit switch network changed. If we accept CREG attach will fail if also
00198     // CGREG/CEREG is not registered.
00199     for (int type = 0; type < CellularNetwork::C_REG; type++) {
00200         if (get_network_registration((CellularNetwork::RegistrationType) type, status, is_registered)) {
00201             if (is_registered) {
00202                 break;
00203             }
00204         }
00205     }
00206 
00207     _cb_data.status_data = status;
00208     // in manual registering we are forcing registration to certain network so we don't accept active context or attached
00209     // as indication that device is registered to correct network.
00210     if (_plmn && strlen(_plmn)) {
00211         return is_registered;
00212     }
00213     return is_registered || _status;
00214 }
00215 
00216 bool CellularStateMachine::get_network_registration(CellularNetwork::RegistrationType type,
00217                                                     CellularNetwork::RegistrationStatus &status, bool &is_registered)
00218 {
00219     is_registered = false;
00220     bool is_roaming = false;
00221     CellularNetwork::registration_params_t reg_params;
00222     _cb_data.error = _network.get_registration_params(type, reg_params);
00223 
00224     if (_cb_data.error != NSAPI_ERROR_OK ) {
00225         if (_cb_data.error != NSAPI_ERROR_UNSUPPORTED ) {
00226             tr_warn("Get network registration failed (type %d)!", type);
00227         }
00228         return false;
00229     }
00230     status = reg_params._status;
00231     switch (status) {
00232         case CellularNetwork::RegisteredRoaming:
00233             is_roaming = true;// @suppress("No break at end of case")
00234         // fall-through
00235         case CellularNetwork::RegisteredHomeNetwork:
00236             is_registered = true;
00237             break;
00238         case CellularNetwork::RegisteredSMSOnlyRoaming:
00239             is_roaming = true;// @suppress("No break at end of case")
00240         // fall-through
00241         case CellularNetwork::RegisteredSMSOnlyHome:
00242             tr_warn("SMS only network registration!");
00243             break;
00244         case CellularNetwork::RegisteredCSFBNotPreferredRoaming:
00245             is_roaming = true; // @suppress("No break at end of case")
00246         // fall-through
00247         case CellularNetwork::RegisteredCSFBNotPreferredHome:
00248             tr_warn("Not preferred network registration!");
00249             break;
00250         case CellularNetwork::AttachedEmergencyOnly:
00251             tr_warn("Emergency only network registration!");
00252             break;
00253         case CellularNetwork::RegistrationDenied:
00254         case CellularNetwork::NotRegistered:
00255         case CellularNetwork::Unknown:
00256         case CellularNetwork::SearchingNetwork:
00257         default:
00258             break;
00259     }
00260 
00261     if (is_roaming) {
00262         tr_info("Roaming network.");
00263     }
00264 
00265     return true;
00266 }
00267 
00268 void CellularStateMachine::report_failure(const char *msg)
00269 {
00270     tr_error("CellularStateMachine failure: %s", msg);
00271 
00272     _event_id = -1;
00273     _cb_data.final_try = true;
00274     send_event_cb(_current_event);
00275 
00276     tr_error("CellularStateMachine target state %s, current state %s", get_state_string(_target_state), get_state_string(_state));
00277 }
00278 
00279 const char *CellularStateMachine::get_state_string(CellularState state) const
00280 {
00281 #if MBED_CONF_MBED_TRACE_ENABLE
00282     static const char *strings[STATE_MAX_FSM_STATE] = { "Init", "Power", "Device ready", "SIM pin", "Signal quality", "Registering network", "Attaching network"};
00283     return strings[state];
00284 #else
00285     return NULL;
00286 #endif // #if MBED_CONF_MBED_TRACE_ENABLE
00287 }
00288 
00289 void CellularStateMachine::enter_to_state(CellularState state)
00290 {
00291     _next_state = state;
00292     _retry_count = 0;
00293     _command_success = false;
00294     _cb_data.error = NSAPI_ERROR_OK ;
00295     _cb_data.status_data = -1;
00296     _cb_data.final_try = false;
00297 }
00298 
00299 void CellularStateMachine::retry_state_or_fail()
00300 {
00301     if (_retry_count < _retry_array_length) {
00302         tr_debug("%s: retry %d/%d", get_state_string(_state), _retry_count, _retry_array_length);
00303         // send info to application/driver about error logic so it can implement proper error logic
00304         _cb_data.status_data = _current_event;
00305         _cb_data.data = &_retry_count;
00306         _cb_data.error = NSAPI_ERROR_OK ;
00307         send_event_cb(CellularStateRetryEvent);
00308 
00309         _event_timeout = _retry_timeout_array[_retry_count];
00310         _is_retry = true;
00311         _cb_data.error = NSAPI_ERROR_OK ;
00312         _retry_count++;
00313     } else {
00314         _cb_data.final_try = true;
00315         report_failure(get_state_string(_state));
00316     }
00317 }
00318 
00319 void CellularStateMachine::state_init()
00320 {
00321     change_timeout(_state_timeout_power_on);
00322     tr_info("Start connecting (timeout %d ms)", _state_timeout_power_on);
00323     _cb_data.error = _cellularDevice.is_ready();
00324     _status = _cb_data.error ? 0 : DEVICE_READY;
00325     if (_cb_data.error != NSAPI_ERROR_OK ) {
00326         _event_timeout = _start_time;
00327         if (_start_time > 0) {
00328             tr_info("Startup delay %d ms", _start_time);
00329         }
00330         enter_to_state(STATE_POWER_ON);
00331     } else {
00332         enter_to_state(STATE_DEVICE_READY);
00333     }
00334 }
00335 
00336 void CellularStateMachine::state_power_on()
00337 {
00338     change_timeout(_state_timeout_power_on);
00339     tr_info("Modem power ON (timeout %d ms)", _state_timeout_power_on);
00340     if (power_on()) {
00341         enter_to_state(STATE_DEVICE_READY);
00342     } else {
00343         // retry to power on device
00344         retry_state_or_fail();
00345     }
00346 }
00347 
00348 bool CellularStateMachine::device_ready()
00349 {
00350     tr_info("Modem ready");
00351 
00352 #ifdef MBED_CONF_CELLULAR_RADIO_ACCESS_TECHNOLOGY
00353     MBED_ASSERT(MBED_CONF_CELLULAR_RADIO_ACCESS_TECHNOLOGY >= CellularNetwork::RAT_GSM &&
00354                 MBED_CONF_CELLULAR_RADIO_ACCESS_TECHNOLOGY < CellularNetwork::RAT_UNKNOWN);
00355     nsapi_error_t err = _network.set_access_technology((CellularNetwork::RadioAccessTechnology)MBED_CONF_CELLULAR_RADIO_ACCESS_TECHNOLOGY);
00356     if (err != NSAPI_ERROR_OK  && err != NSAPI_ERROR_UNSUPPORTED ) {
00357         tr_warning("Failed to set access technology to %d", MBED_CONF_CELLULAR_RADIO_ACCESS_TECHNOLOGY);
00358         return false;
00359     }
00360 #endif // MBED_CONF_CELLULAR_DEBUG_AT
00361 
00362     send_event_cb(CellularDeviceReady);
00363     _cellularDevice.set_ready_cb(0);
00364 
00365     return true;
00366 }
00367 
00368 void CellularStateMachine::state_device_ready()
00369 {
00370     change_timeout(_state_timeout_power_on);
00371     if (!(_status & DEVICE_READY)) {
00372         tr_debug("Device was not ready, calling soft_power_on()");
00373         _cb_data.error = _cellularDevice.soft_power_on();
00374     }
00375     if (_cb_data.error == NSAPI_ERROR_OK ) {
00376         _cb_data.error = _cellularDevice.init();
00377         if (_cb_data.error == NSAPI_ERROR_OK ) {
00378             if (device_ready()) {
00379                 _status = 0;
00380                 enter_to_state(STATE_SIM_PIN);
00381             } else {
00382                 tr_warning("Power cycle CellularDevice and restart connecting");
00383                 (void) _cellularDevice.soft_power_off();
00384                 (void) _cellularDevice.hard_power_off();
00385                 _status = 0;
00386                 _is_retry = true;
00387                 enter_to_state(STATE_INIT);
00388             }
00389         }
00390     }
00391     if (_cb_data.error != NSAPI_ERROR_OK ) {
00392         if (_retry_count == 0) {
00393             _cellularDevice.set_ready_cb(callback(this, &CellularStateMachine::device_ready_cb));
00394         }
00395         retry_state_or_fail();
00396     }
00397 }
00398 
00399 void CellularStateMachine::state_sim_pin()
00400 {
00401     change_timeout(_state_timeout_sim_pin);
00402     tr_info("Setup SIM (timeout %d ms)", _state_timeout_sim_pin);
00403     if (open_sim()) {
00404         bool success = false;
00405         for (int type = 0; type < CellularNetwork::C_MAX; type++) {
00406             _cb_data.error = _network.set_registration_urc((CellularNetwork::RegistrationType)type, true);
00407             if (!_cb_data.error && (type == CellularNetwork::C_EREG || type == CellularNetwork::C_GREG)) {
00408                 success = true;
00409             }
00410         }
00411         if (!success) {
00412             tr_error("Failed to set CEREG/CGREG URC's for registration");
00413             retry_state_or_fail();
00414             return;
00415         }
00416         if (_network.is_active_context()) { // check if context was already activated
00417             tr_debug("Active context found.");
00418             _status |= ACTIVE_PDP_CONTEXT;
00419         }
00420         CellularNetwork::AttachStatus status = CellularNetwork::Detached; // check if modem is already attached to a network
00421         if (_network.get_attach(status) == NSAPI_ERROR_OK  && status == CellularNetwork::Attached) {
00422             _status |= ATTACHED_TO_NETWORK;
00423             tr_debug("Cellular already attached.");
00424         }
00425 
00426         // if packet domain event reporting is not set it's not a stopper. We might lack some events when we are
00427         // dropped from the network.
00428         _cb_data.error = _network.set_packet_domain_event_reporting(true);
00429         if (_cb_data.error == NSAPI_STATUS_ERROR_UNSUPPORTED) {
00430             tr_warning("Packet domain event reporting not supported!");
00431         } else if (_cb_data.error) {
00432             tr_warning("Packet domain event reporting set failed!");
00433         }
00434         enter_to_state(STATE_SIGNAL_QUALITY);
00435     } else {
00436         retry_state_or_fail();
00437     }
00438 }
00439 
00440 void CellularStateMachine::state_signal_quality()
00441 {
00442     _cb_data.error = _network.get_signal_quality(_signal_quality.rssi, &_signal_quality.ber);
00443 
00444     if (_cb_data.error != NSAPI_ERROR_OK ) {
00445         retry_state_or_fail();
00446     } else {
00447         _cb_data.data = &_signal_quality;
00448         send_event_cb(_current_event);
00449         enter_to_state(STATE_REGISTERING_NETWORK);
00450     }
00451 }
00452 
00453 void CellularStateMachine::state_registering()
00454 {
00455     change_timeout(_state_timeout_network);
00456     if (is_registered()) {
00457         if (_cb_data.status_data != CellularNetwork::RegisteredHomeNetwork &&
00458                 _cb_data.status_data != CellularNetwork::RegisteredRoaming && _status) {
00459             // there was already activated context or attached to network, and registration status is not registered, set to already registered.
00460             _cb_data.status_data = CellularNetwork::AlreadyRegistered;
00461         }
00462         _cb_data.error = NSAPI_ERROR_OK ;
00463         send_event_cb(_current_event);
00464         // we are already registered, go to attach
00465         enter_to_state(STATE_ATTACHING_NETWORK);
00466     } else {
00467         tr_info("Network registration (timeout %d ms)", _state_timeout_registration);
00468         change_timeout(_state_timeout_registration);
00469         if (!_command_success && !_plmn) { // don't call set_registration twice for manual registration
00470             _cb_data.error = _network.set_registration(_plmn);
00471             _command_success = (_cb_data.error == NSAPI_ERROR_OK );
00472         }
00473         retry_state_or_fail();
00474     }
00475 }
00476 
00477 void CellularStateMachine::state_attaching()
00478 {
00479     if (_status != ATTACHED_TO_NETWORK) {
00480         change_timeout(_state_timeout_connect);
00481         tr_info("Attaching network (timeout %d ms)", _state_timeout_connect);
00482         _cb_data.error = _network.set_attach();
00483     }
00484     if (_cb_data.error == NSAPI_ERROR_OK ) {
00485         _cb_data.status_data = CellularNetwork::Attached;
00486         send_event_cb(_current_event);
00487     } else {
00488         retry_state_or_fail();
00489     }
00490 }
00491 
00492 void CellularStateMachine::continue_from_state(CellularState state)
00493 {
00494     _mutex.lock();
00495     tr_info("%s => %s", get_state_string((CellularStateMachine::CellularState)_state),
00496             get_state_string((CellularStateMachine::CellularState)state));
00497     _state = state;
00498     enter_to_state(state);
00499     _event_id = _queue.call_in(0, this, &CellularStateMachine::event);
00500     if (!_event_id) {
00501         _event_id = -1;
00502         _cb_data.error = NSAPI_ERROR_NO_MEMORY ;
00503         report_failure("Failed to call queue.");
00504         stop();
00505     }
00506     _mutex.unlock();
00507 }
00508 
00509 nsapi_error_t CellularStateMachine::run_to_state(CellularStateMachine::CellularState state)
00510 {
00511     _mutex.lock();
00512     // call pre_event via queue so that it's in same thread and it's safe to decisions
00513     int id = _queue.call_in(0, this, &CellularStateMachine::pre_event, state);
00514     if (!id) {
00515         report_failure("Failed to call queue.");
00516         stop();
00517         _mutex.unlock();
00518         return NSAPI_ERROR_NO_MEMORY ;
00519     }
00520     _mutex.unlock();
00521     return NSAPI_ERROR_OK ;
00522 }
00523 
00524 void CellularStateMachine::pre_event(CellularState state)
00525 {
00526     if (_target_state < state) {
00527         // new wanted state will not be achieved with current _target_state so update it
00528         _target_state = state;
00529     } else {
00530         // wanted state is already / will be achieved, return without launching new event
00531         return;
00532     }
00533     // if _event_id is -1 it means that new event is not going to be launched so we must launch new event
00534     if (_event_id == -1) {
00535         if (!_cb_data.final_try) {
00536             // update next state so that we don't continue from previous state if state machine was paused and then started again.
00537             // but only if earlier try did not finish to failure, then we must continue from that state
00538             _state = _next_state;
00539         }
00540         enter_to_state(_next_state);
00541         _event_id = _queue.call_in(0, this, &CellularStateMachine::event);
00542         if (!_event_id) {
00543             _event_id = -1;
00544             report_failure("Failed to call queue.");
00545             stop();
00546         }
00547     }
00548 }
00549 
00550 bool CellularStateMachine::get_current_status(CellularStateMachine::CellularState &current_state, CellularStateMachine::CellularState &target_state)
00551 {
00552     bool is_running;
00553     _mutex.lock();
00554     current_state = _state;
00555     target_state = _target_state;
00556     if (_event_id == -1 || _event_id == STM_STOPPED) {
00557         is_running = false;
00558     } else {
00559         is_running = true;
00560     }
00561     _mutex.unlock();
00562     return is_running;
00563 }
00564 
00565 void CellularStateMachine::event()
00566 {
00567     // Don't send Signal quality when in signal quality state or it can confuse callback functions when running retry logic
00568     if (_state > STATE_SIGNAL_QUALITY) {
00569         _cb_data.error = _network.get_signal_quality(_signal_quality.rssi, &_signal_quality.ber);
00570         _cb_data.data = &_signal_quality;
00571 
00572         if (_cb_data.error == NSAPI_ERROR_OK ) {
00573             send_event_cb(CellularSignalQuality);
00574             if (_signal_quality.rssi == CellularNetwork::SignalQualityUnknown) {
00575                 tr_info("RSSI unknown");
00576             } else {
00577                 tr_info("RSSI %d dBm", _signal_quality.rssi);
00578             }
00579         }
00580     }
00581 
00582     _event_timeout = -1;
00583     _is_retry = false;
00584 
00585     switch (_state) {
00586         case STATE_INIT:
00587             _current_event = CellularDeviceReady;
00588             state_init();
00589             break;
00590         case STATE_POWER_ON:
00591             _current_event = CellularDeviceReady;
00592             state_power_on();
00593             break;
00594         case STATE_DEVICE_READY:
00595             _current_event = CellularDeviceReady;
00596             state_device_ready();
00597             break;
00598         case STATE_SIM_PIN:
00599             _current_event = CellularSIMStatusChanged;
00600             state_sim_pin();
00601             break;
00602         case STATE_SIGNAL_QUALITY:
00603             _current_event = CellularSignalQuality;
00604             state_signal_quality();
00605             break;
00606         case STATE_REGISTERING_NETWORK:
00607             _current_event = CellularRegistrationStatusChanged;
00608             state_registering();
00609             break;
00610         case STATE_ATTACHING_NETWORK:
00611             _current_event = CellularAttachNetwork;
00612             state_attaching();
00613             break;
00614         default:
00615             MBED_ASSERT(0);
00616             break;
00617     }
00618 
00619     if (check_is_target_reached()) {
00620         _event_id = -1;
00621         return;
00622     }
00623 
00624     if (_next_state != _state || _event_timeout >= 0) {
00625         if (_next_state != _state) { // state exit condition
00626             tr_debug("%s => %s", get_state_string((CellularStateMachine::CellularState)_state),
00627                      get_state_string((CellularStateMachine::CellularState)_next_state));
00628         } else {
00629             tr_info("Continue after %d seconds", _event_timeout);
00630         }
00631         _state = _next_state;
00632         if (_event_timeout == -1) {
00633             _event_timeout = 0;
00634         }
00635         _event_id = _queue.call_in(_event_timeout * 1000, callback(this, &CellularStateMachine::event));
00636         if (!_event_id) {
00637             _cb_data.error = NSAPI_ERROR_NO_MEMORY ;
00638             report_failure("CellularStateMachine event failure!");
00639             return;
00640         }
00641     }
00642 }
00643 
00644 nsapi_error_t CellularStateMachine::start_dispatch()
00645 {
00646 #ifdef MBED_CONF_RTOS_PRESENT
00647     if (!_queue_thread) {
00648         _queue_thread = new rtos::Thread(osPriorityNormal, 2048, NULL, "stm_queue");
00649         _event_id = STM_STOPPED;
00650     }
00651 
00652     if (_event_id == STM_STOPPED) {
00653         if (_queue_thread->start(callback(&_queue, &events::EventQueue::dispatch_forever)) != osOK) {
00654             report_failure("Failed to start thread.");
00655             stop();
00656             return NSAPI_ERROR_NO_MEMORY ;
00657         }
00658     }
00659 
00660     _event_id = -1;
00661 #else
00662     _queue.chain(mbed_event_queue());
00663 #endif
00664     return NSAPI_ERROR_OK ;
00665 }
00666 
00667 void CellularStateMachine::set_cellular_callback(mbed::Callback<void(nsapi_event_t, intptr_t)> status_cb)
00668 {
00669     _event_status_cb = status_cb;
00670 }
00671 
00672 void CellularStateMachine::send_event_cb(cellular_connection_status_t status)
00673 {
00674     if (_event_status_cb) {
00675         _event_status_cb((nsapi_event_t)status, (intptr_t)&_cb_data);
00676     }
00677 }
00678 
00679 void CellularStateMachine::change_timeout(const int &timeout)
00680 {
00681     _cb_data.status_data = _current_event;
00682     _cb_data.data = &timeout;
00683     _cb_data.error = NSAPI_ERROR_OK ;
00684     // event callback is a preferred method to communicate to CellularDevice,
00685     // for example calling CellularDevice::set_timeout would call back to this class
00686     send_event_cb(CellularDeviceTimeout);
00687 }
00688 
00689 bool CellularStateMachine::check_is_target_reached()
00690 {
00691     if (((_target_state == _state || _target_state < _next_state) && _cb_data.error == NSAPI_ERROR_OK  && !_is_retry) ||
00692             _event_id == STM_STOPPED) {
00693         if (_target_state != _state && _target_state < _next_state) {
00694             // we are skipping the state, update _state to current state because we have reached it
00695             _state = _target_state;
00696         }
00697         _event_id = -1;
00698         return true;
00699     }
00700     return false;
00701 }
00702 
00703 void CellularStateMachine::cellular_event_changed(nsapi_event_t ev, intptr_t ptr)
00704 {
00705     cell_callback_data_t *data = (cell_callback_data_t *)ptr;
00706     if ((cellular_connection_status_t)ev == CellularRegistrationStatusChanged && (
00707                 _state == STATE_REGISTERING_NETWORK || _state == STATE_SIGNAL_QUALITY)) {
00708         // expect packet data so only these states are valid
00709         CellularNetwork::registration_params_t reg_params;
00710         nsapi_error_t err = _network.get_registration_params(reg_params);
00711 
00712         if (err == NSAPI_ERROR_OK  && (reg_params._type == CellularNetwork::C_EREG || reg_params._type == CellularNetwork::C_GREG)) {
00713             if ((data->status_data == CellularNetwork::RegisteredHomeNetwork ||
00714                     data->status_data == CellularNetwork::RegisteredRoaming) && data->error == NSAPI_ERROR_OK ) {
00715                 _queue.cancel(_event_id);
00716                 _is_retry = false;
00717                 _event_id = -1;
00718                 if (!check_is_target_reached()) {
00719                     continue_from_state(STATE_ATTACHING_NETWORK);
00720                 }
00721             }
00722         } else {
00723             tr_debug("creg event, discard...");
00724         }
00725     }
00726 }
00727 
00728 void CellularStateMachine::device_ready_cb()
00729 {
00730     tr_debug("Device ready callback");
00731     if (_state == STATE_DEVICE_READY && _cellularDevice.init() == NSAPI_ERROR_OK ) {
00732         tr_debug("State was STATE_DEVICE_READY and at mode ready, cancel state and move to next");
00733         _queue.cancel(_event_id);
00734         _event_id = -1;
00735         if (device_ready()) {
00736             _is_retry = false;
00737             _status = 0;
00738             if (!check_is_target_reached()) {
00739                 continue_from_state(STATE_SIM_PIN);
00740             }
00741         } else {
00742             continue_from_state(STATE_DEVICE_READY);
00743         }
00744     }
00745 }
00746 
00747 void CellularStateMachine::set_retry_timeout_array(const uint16_t timeout[], int array_len)
00748 {
00749     if (!timeout || array_len <= 0) {
00750         _retry_array_length = 0;
00751         return;
00752     }
00753     _retry_array_length = array_len > CELLULAR_RETRY_ARRAY_SIZE ? CELLULAR_RETRY_ARRAY_SIZE : array_len;
00754     for (int i = 0; i < _retry_array_length; i++) {
00755         _retry_timeout_array[i] = timeout[i];
00756     }
00757 }
00758 
00759 void CellularStateMachine::get_retry_timeout_array(uint16_t *timeout, int &array_len) const
00760 {
00761     for (int i = 0; i < _retry_array_length; i++) {
00762         timeout[i] = _retry_timeout_array[i];
00763     }
00764     array_len = _retry_array_length;
00765 }
00766 
00767 void CellularStateMachine::set_timeout(int timeout)
00768 {
00769     _state_timeout_power_on = timeout;
00770     _state_timeout_sim_pin = timeout;
00771     _state_timeout_registration = timeout;
00772     _state_timeout_network = timeout;
00773     _state_timeout_connect = timeout;
00774 }
00775 
00776 } // namespace
00777