Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers AT_CellularContext.cpp Source File

AT_CellularContext.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 <stdio.h>
00019 #include "AT_CellularContext.h"
00020 #include "AT_CellularNetwork.h"
00021 #include "AT_CellularStack.h"
00022 #include "AT_CellularDevice.h"
00023 #include "CellularLog.h"
00024 #if (DEVICE_SERIAL && DEVICE_INTERRUPTIN) || defined(DOXYGEN_ONLY)
00025 #include "UARTSerial.h"
00026 #endif // #if DEVICE_SERIAL
00027 #include "ThisThread.h"
00028 
00029 #define NETWORK_TIMEOUT 30 * 60 * 1000 // 30 minutes
00030 #define DEVICE_TIMEOUT 5 * 60 * 1000 // 5 minutes
00031 // Timeout to wait for URC indicating ciot optimization support from network
00032 #define CP_OPT_NW_REPLY_TIMEOUT 3000 // 3 seconds
00033 
00034 #if NSAPI_PPP_AVAILABLE
00035 #define AT_SYNC_TIMEOUT 1000 // 1 second timeout
00036 #include "nsapi_ppp.h"
00037 #endif
00038 
00039 #if MBED_CONF_CELLULAR_USE_APN_LOOKUP
00040 #include "CellularInformation.h"
00041 #include "APN_db.h"
00042 #endif //MBED_CONF_CELLULAR_USE_APN_LOOKUP
00043 
00044 using namespace mbed_cellular_util;
00045 using namespace mbed;
00046 using namespace rtos;
00047 
00048 AT_CellularContext::AT_CellularContext(ATHandler &at, CellularDevice *device, const char *apn, bool cp_req, bool nonip_req) :
00049     _current_op(OP_INVALID), _fh(0), _cp_req(cp_req), _is_connected(false), _at(at)
00050 {
00051     tr_info("New CellularContext %s (%p)", apn ? apn : "", this);
00052     _nonip_req = nonip_req;
00053     _apn = apn;
00054     _device = device;
00055 }
00056 
00057 AT_CellularContext::~AT_CellularContext()
00058 {
00059     tr_info("Delete CellularContext with apn: [%s] (%p)", _apn ? _apn : "", this);
00060 
00061     _is_blocking = true;
00062     (void)disconnect();
00063 
00064     if (_nw) {
00065         _device->close_network();
00066     }
00067 
00068     if (_cp_netif) {
00069         delete _cp_netif;
00070     }
00071 }
00072 
00073 void AT_CellularContext::set_file_handle(FileHandle *fh)
00074 {
00075     tr_info("CellularContext filehandle %p", fh);
00076     _fh = fh;
00077     _at.set_file_handle(_fh);
00078 }
00079 
00080 #if (DEVICE_SERIAL && DEVICE_INTERRUPTIN) || defined(DOXYGEN_ONLY)
00081 void AT_CellularContext::set_file_handle(UARTSerial *serial, PinName dcd_pin, bool active_high)
00082 {
00083     tr_info("CellularContext serial %p", serial);
00084     _dcd_pin = dcd_pin;
00085     _active_high = active_high;
00086     _fh = serial;
00087     _at.set_file_handle(static_cast<FileHandle *>(serial));
00088     enable_hup(false);
00089 }
00090 #endif // #if DEVICE_SERIAL
00091 
00092 void AT_CellularContext::enable_hup(bool enable)
00093 {
00094     if (_dcd_pin != NC) {
00095 #if (DEVICE_SERIAL && DEVICE_INTERRUPTIN) || defined(DOXYGEN_ONLY)
00096         static_cast<UARTSerial *>(_fh)->set_data_carrier_detect(enable ? _dcd_pin : NC, _active_high);
00097 #endif // #if DEVICE_SERIAL
00098     }
00099 }
00100 
00101 AT_CellularDevice *AT_CellularContext::get_device() const
00102 {
00103     return static_cast<AT_CellularDevice *>(CellularContext::get_device());
00104 }
00105 
00106 void AT_CellularContext::do_connect_with_retry()
00107 {
00108     CellularContext::do_connect_with_retry();
00109 }
00110 
00111 nsapi_error_t AT_CellularContext::connect()
00112 {
00113     tr_info("CellularContext connect");
00114     if (_is_connected) {
00115         return NSAPI_ERROR_IS_CONNECTED ;
00116     }
00117     call_network_cb(NSAPI_STATUS_CONNECTING );
00118 
00119     nsapi_error_t err = _device->attach_to_network();
00120     _cb_data.error = check_operation(err, OP_CONNECT);
00121     _retry_count = 0;
00122     if (_is_blocking) {
00123         if (_cb_data.error == NSAPI_ERROR_OK  || _cb_data.error == NSAPI_ERROR_ALREADY ) {
00124             do_connect_with_retry();
00125         }
00126     } else {
00127         if (_cb_data.error == NSAPI_ERROR_ALREADY ) {
00128             // device is already attached, to be async we must use queue to connect and give proper callbacks
00129             int id = _device->get_queue()->call_in(0, this, &AT_CellularContext::do_connect_with_retry);
00130             if (id == 0) {
00131                 return NSAPI_ERROR_NO_MEMORY ;
00132             }
00133             return NSAPI_ERROR_OK ;
00134         }
00135     }
00136 
00137     if (_cb_data.error == NSAPI_ERROR_ALREADY ) {
00138         return NSAPI_ERROR_OK ;
00139     }
00140 
00141     return _cb_data.error;
00142 }
00143 
00144 nsapi_error_t AT_CellularContext::set_device_ready()
00145 {
00146     nsapi_error_t err = _device->set_device_ready();
00147     return check_operation(err, OP_DEVICE_READY);
00148 }
00149 
00150 nsapi_error_t AT_CellularContext::set_sim_ready()
00151 {
00152     nsapi_error_t err = _device->set_sim_ready();
00153     return check_operation(err, OP_SIM_READY);
00154 }
00155 
00156 nsapi_error_t AT_CellularContext::register_to_network()
00157 {
00158     nsapi_error_t err = _device->register_to_network();
00159     return check_operation(err, OP_REGISTER);
00160 }
00161 
00162 nsapi_error_t AT_CellularContext::attach_to_network()
00163 {
00164     nsapi_error_t err = _device->attach_to_network();
00165     return check_operation(err, OP_ATTACH);
00166 }
00167 
00168 nsapi_error_t AT_CellularContext::check_operation(nsapi_error_t err, ContextOperation op)
00169 {
00170     _current_op = op;
00171     if (err == NSAPI_ERROR_IN_PROGRESS  || err == NSAPI_ERROR_OK ) {
00172         if (_is_blocking) {
00173             int sema_acq = _semaphore.try_acquire_for(get_timeout_for_operation(op)); // cellular network searching may take several minutes
00174             if (!sema_acq) {
00175                 tr_warning("No cellular connection");
00176                 return NSAPI_ERROR_TIMEOUT ;
00177             }
00178             return _cb_data.error;// callback might have been completed with an error, must return that error here
00179         }
00180     }
00181 
00182     return err;
00183 }
00184 
00185 nsapi_connection_status_t AT_CellularContext::get_connection_status() const
00186 {
00187     return _connect_status;
00188 }
00189 
00190 uint32_t AT_CellularContext::get_timeout_for_operation(ContextOperation op) const
00191 {
00192     uint32_t timeout = NETWORK_TIMEOUT; // default timeout is 30 minutes as registration and attach may take time
00193     if (op == OP_SIM_READY || op == OP_DEVICE_READY) {
00194         timeout = DEVICE_TIMEOUT; // use 5 minutes for device ready and sim
00195     }
00196     return timeout;
00197 }
00198 
00199 bool AT_CellularContext::is_connected()
00200 {
00201     return _is_connected;
00202 }
00203 
00204 NetworkStack *AT_CellularContext::get_stack()
00205 {
00206 #if NSAPI_PPP_AVAILABLE
00207     // use lwIP/PPP if modem does not have IP stack
00208     if (!_stack) {
00209         _stack = nsapi_ppp_get_stack();
00210     }
00211 #endif
00212     return _stack;
00213 }
00214 
00215 nsapi_error_t AT_CellularContext::get_netmask(SocketAddress *address)
00216 {
00217     return NSAPI_ERROR_UNSUPPORTED ;
00218 }
00219 
00220 const char *AT_CellularContext::get_netmask()
00221 {
00222     return NULL;
00223 }
00224 
00225 nsapi_error_t AT_CellularContext::get_gateway(SocketAddress *address)
00226 {
00227     return NSAPI_ERROR_UNSUPPORTED ;
00228 }
00229 
00230 const char *AT_CellularContext::get_gateway()
00231 {
00232     return NULL;
00233 }
00234 
00235 nsapi_error_t AT_CellularContext::get_ip_address(SocketAddress *address)
00236 {
00237     if (!address) {
00238         return NSAPI_ERROR_PARAMETER ;
00239     }
00240 #if NSAPI_PPP_AVAILABLE
00241     address->set_ip_address(nsapi_ppp_get_ip_addr(_at.get_file_handle()));
00242     return NSAPI_ERROR_OK ;
00243 #else
00244     if (!_stack) {
00245         _stack = get_stack();
00246     }
00247     if (_stack) {
00248         _stack->get_ip_address(address);
00249         return NSAPI_ERROR_OK ;
00250     }
00251     return NSAPI_ERROR_NO_CONNECTION ;
00252 #endif
00253 }
00254 
00255 const char *AT_CellularContext::get_ip_address()
00256 {
00257 #if NSAPI_PPP_AVAILABLE
00258     return nsapi_ppp_get_ip_addr(_at.get_file_handle());
00259 #else
00260     if (!_stack) {
00261         _stack = get_stack();
00262     }
00263     if (_stack) {
00264         return _stack->get_ip_address();
00265     }
00266     return NULL;
00267 #endif
00268 }
00269 
00270 char *AT_CellularContext::get_interface_name(char *interface_name)
00271 {
00272     if (_cid < 0) {
00273         return NULL;
00274     }
00275     MBED_ASSERT(interface_name);
00276     sprintf(interface_name, "ce%d", _cid);
00277     return interface_name;
00278 }
00279 
00280 void AT_CellularContext::attach(Callback<void(nsapi_event_t, intptr_t)> status_cb)
00281 {
00282     _status_cb = status_cb;
00283 }
00284 
00285 nsapi_error_t AT_CellularContext::set_blocking(bool blocking)
00286 {
00287     nsapi_error_t err = NSAPI_ERROR_OK ;
00288     tr_info("CellularContext set blocking %d", blocking);
00289 #if NSAPI_PPP_AVAILABLE
00290     err = nsapi_ppp_set_blocking(blocking);
00291 #endif
00292     _is_blocking = blocking;
00293     return err;
00294 }
00295 
00296 void AT_CellularContext::set_plmn(const char *plmn)
00297 {
00298     tr_info("CellularContext plmn %s", (plmn ? plmn : "NULL"));
00299     _device->set_plmn(plmn);
00300 }
00301 
00302 void AT_CellularContext::set_sim_pin(const char *sim_pin)
00303 {
00304     _device->set_sim_pin(sim_pin);
00305 }
00306 
00307 nsapi_error_t AT_CellularContext::connect(const char *sim_pin, const char *apn, const char *uname,
00308                                           const char *pwd)
00309 {
00310     set_sim_pin(sim_pin);
00311     set_credentials(apn, uname, pwd);
00312     return connect();
00313 }
00314 
00315 void AT_CellularContext::set_credentials(const char *apn, const char *uname, const char *pwd)
00316 {
00317     _apn = apn;
00318     _uname = uname;
00319     _pwd = pwd;
00320 }
00321 
00322 // PDP Context handling
00323 void AT_CellularContext::delete_current_context()
00324 {
00325     tr_info("Delete context %d", _cid);
00326     _at.clear_error();
00327 
00328     _at.at_cmd_discard("+CGDCONT", "=", "%d", _cid);
00329 
00330     if (_at.get_last_error() == NSAPI_ERROR_OK ) {
00331         set_cid(-1);
00332         _new_context_set = false;
00333     }
00334 
00335     // there is nothing we can do if deleting of context fails. No point reporting an error (for example disconnect).
00336     _at.clear_error();
00337 }
00338 
00339 nsapi_error_t AT_CellularContext::do_user_authentication()
00340 {
00341     // if user has defined user name and password we need to call CGAUTH before activating or modifying context
00342     if (_pwd && _uname) {
00343         if (!get_device()->get_property(AT_CellularDevice::PROPERTY_AT_CGAUTH)) {
00344             return NSAPI_ERROR_UNSUPPORTED ;
00345         }
00346         const bool stored_debug_state = _at.get_debug();
00347         _at.set_debug(false);
00348 
00349         _at.at_cmd_discard("+CGAUTH", "=", "%d%d%s%s", _cid, _authentication_type, _uname, _pwd);
00350 
00351         _at.set_debug(stored_debug_state);
00352 
00353         if (_at.get_last_error() != NSAPI_ERROR_OK ) {
00354             return NSAPI_ERROR_AUTH_FAILURE ;
00355         }
00356     }
00357 
00358     return NSAPI_ERROR_OK ;
00359 }
00360 
00361 AT_CellularDevice::CellularProperty AT_CellularContext::pdp_type_t_to_cellular_property(pdp_type_t pdp_type)
00362 {
00363     AT_CellularDevice::CellularProperty prop = AT_CellularDevice::PROPERTY_IPV4_PDP_TYPE;
00364     if (pdp_type == IPV6_PDP_TYPE) {
00365         prop = AT_CellularDevice::PROPERTY_IPV6_PDP_TYPE;
00366     } else if (pdp_type == IPV4V6_PDP_TYPE) {
00367         prop = AT_CellularDevice::PROPERTY_IPV4V6_PDP_TYPE;
00368     } else if (pdp_type == NON_IP_PDP_TYPE) {
00369         prop = AT_CellularDevice::PROPERTY_NON_IP_PDP_TYPE;
00370     }
00371 
00372     return prop;
00373 }
00374 
00375 bool AT_CellularContext::get_context()
00376 {
00377     _at.cmd_start_stop("+CGDCONT", "?");
00378     _at.resp_start("+CGDCONT:");
00379     set_cid(-1);
00380     int cid_max = 0; // needed when creating new context
00381     char apn[MAX_ACCESSPOINT_NAME_LENGTH];
00382     int apn_len = 0;
00383 
00384     while (_at.info_resp()) {
00385         int cid = _at.read_int();
00386         if (cid > cid_max) {
00387             cid_max = cid;
00388         }
00389         char pdp_type_from_context[10];
00390         int pdp_type_len = _at.read_string(pdp_type_from_context, sizeof(pdp_type_from_context));
00391         if (pdp_type_len > 0) {
00392             apn_len = _at.read_string(apn, sizeof(apn));
00393             if (apn_len >= 0) {
00394                 if (_apn && (strcmp(apn, _apn) != 0)) {
00395                     continue;
00396                 }
00397 
00398                 // APN matched -> Check PDP type
00399                 pdp_type_t pdp_type = string_to_pdp_type(pdp_type_from_context);
00400 
00401                 // Accept exact matching PDP context type or dual PDP context for modems that support both IPv4 and IPv6 stacks
00402                 if (get_device()->get_property(pdp_type_t_to_cellular_property(pdp_type)) ||
00403                         ((pdp_type == IPV4V6_PDP_TYPE && (get_device()->get_property(AT_CellularDevice::PROPERTY_IPV4_PDP_TYPE) &&
00404                                                           get_device()->get_property(AT_CellularDevice::PROPERTY_IPV6_PDP_TYPE))) && !_nonip_req)) {
00405                     _pdp_type = pdp_type;
00406                     set_cid(cid);
00407                 }
00408             }
00409         }
00410     }
00411 
00412     _at.resp_stop();
00413     if (_cid == -1) { // no suitable context was found so create a new one
00414         if (!set_new_context(cid_max + 1)) {
00415             return false;
00416         }
00417     }
00418 
00419     // save the apn
00420     if (apn_len > 0 && !_apn) {
00421         memcpy(_found_apn, apn, apn_len + 1);
00422     }
00423 
00424     tr_info("Found PDP context %d", _cid);
00425 
00426     return true;
00427 }
00428 
00429 bool AT_CellularContext::set_new_context(int cid)
00430 {
00431     char pdp_type_str[8 + 1] = {0};
00432     pdp_type_t pdp_type = IPV4_PDP_TYPE;
00433 
00434     if (_nonip_req && _cp_in_use && get_device()->get_property(AT_CellularDevice::PROPERTY_NON_IP_PDP_TYPE)) {
00435         strncpy(pdp_type_str, "Non-IP", sizeof(pdp_type_str));
00436         pdp_type = NON_IP_PDP_TYPE;
00437     } else if (get_device()->get_property(AT_CellularDevice::PROPERTY_IPV4V6_PDP_TYPE) ||
00438                (get_device()->get_property(AT_CellularDevice::PROPERTY_IPV4_PDP_TYPE) &&
00439                 get_device()->get_property(AT_CellularDevice::PROPERTY_IPV6_PDP_TYPE))) {
00440         strncpy(pdp_type_str, "IPV4V6", sizeof(pdp_type_str));
00441         pdp_type = IPV4V6_PDP_TYPE;
00442     } else if (get_device()->get_property(AT_CellularDevice::PROPERTY_IPV6_PDP_TYPE)) {
00443         strncpy(pdp_type_str, "IPV6", sizeof(pdp_type_str));
00444         pdp_type = IPV6_PDP_TYPE;
00445     } else if (get_device()->get_property(AT_CellularDevice::PROPERTY_IPV4_PDP_TYPE)) {
00446         strncpy(pdp_type_str, "IP", sizeof(pdp_type_str));
00447         pdp_type = IPV4_PDP_TYPE;
00448     } else {
00449         return false;
00450     }
00451 
00452     //apn: "If the value is null or omitted, then the subscription value will be requested."
00453 
00454     bool success = (_at.at_cmd_discard("+CGDCONT", "=", "%d%s%s", cid, pdp_type_str, _apn) == NSAPI_ERROR_OK );
00455 
00456     if (success) {
00457         _pdp_type = pdp_type;
00458         set_cid(cid);
00459         _new_context_set = true;
00460         tr_info("New PDP context %d, type %d", _cid, pdp_type);
00461     }
00462 
00463     return success;
00464 }
00465 
00466 nsapi_error_t AT_CellularContext::do_activate_context()
00467 {
00468     if (_nonip_req && _cp_in_use) {
00469         return activate_non_ip_context();
00470     }
00471 
00472     // In IP case but also when Non-IP is requested and
00473     // control plane optimization is not established -> activate ip context
00474     _nonip_req = false;
00475     return activate_ip_context();
00476 }
00477 
00478 nsapi_error_t AT_CellularContext::activate_ip_context()
00479 {
00480     nsapi_error_t ret = find_and_activate_context();
00481 #if !NSAPI_PPP_AVAILABLE
00482     if (ret == NSAPI_ERROR_OK ) {
00483         pdpContextList_t params_list;
00484         if (get_pdpcontext_params(params_list) == NSAPI_ERROR_OK ) {
00485             pdpcontext_params_t *pdp = params_list.get_head();
00486             while (pdp) {
00487                 SocketAddress addr;
00488                 if (addr.set_ip_address(pdp->dns_secondary_addr)) {
00489                     nsapi_addr_t taddr = addr.get_addr();
00490                     for (int i = 0; i < ((taddr.version == NSAPI_IPv6 ) ? NSAPI_IPv6_BYTES : NSAPI_IPv4_BYTES); i++) {
00491                         if (taddr.bytes[i] != 0) { // check the address is not all zero
00492                             tr_info("DNS secondary %s", pdp->dns_secondary_addr);
00493                             char ifn[5]; // "ce" + two digit _cid + zero
00494                             add_dns_server(addr, get_interface_name(ifn));
00495                             break;
00496                         }
00497                     }
00498                 }
00499                 if (addr.set_ip_address(pdp->dns_primary_addr)) {
00500                     nsapi_addr_t taddr = addr.get_addr();
00501                     for (int i = 0; i < ((taddr.version == NSAPI_IPv6 ) ? NSAPI_IPv6_BYTES : NSAPI_IPv4_BYTES); i++) {
00502                         if (taddr.bytes[i] != 0) { // check the address is not all zero
00503                             tr_info("DNS primary %s", pdp->dns_primary_addr);
00504                             char ifn[5]; // "ce" + two digit _cid + zero
00505                             add_dns_server(addr, get_interface_name(ifn));
00506                             break;
00507                         }
00508                     }
00509                 }
00510                 pdp = pdp->next;
00511             }
00512         }
00513     }
00514 #endif
00515     return ret;
00516 }
00517 
00518 nsapi_error_t AT_CellularContext::activate_non_ip_context()
00519 {
00520     return find_and_activate_context();
00521 }
00522 
00523 void AT_CellularContext::activate_context()
00524 {
00525     tr_info("Activate PDP context %d", _cid);
00526     _at.at_cmd_discard("+CGACT", "=1,", "%d", _cid);
00527     if (_at.get_last_error() == NSAPI_ERROR_OK ) {
00528         _is_context_activated = true;
00529     }
00530 }
00531 
00532 nsapi_error_t AT_CellularContext::find_and_activate_context()
00533 {
00534     _at.lock();
00535 
00536     nsapi_error_t err = NSAPI_ERROR_OK ;
00537 
00538     // try to find or create context of suitable type
00539     if (get_context()) {
00540 #if NSAPI_PPP_AVAILABLE
00541         _at.unlock();
00542         // in PPP we don't activate any context but leave it to PPP stack
00543         return err;
00544 #else
00545         // try to authenticate user before activating or modifying context
00546         err = do_user_authentication();
00547 #endif // NSAPI_PPP_AVAILABLE
00548     } else {
00549         err = NSAPI_ERROR_NO_CONNECTION ;
00550     }
00551 
00552     if (err != NSAPI_ERROR_OK ) {
00553         _at.unlock();
00554         tr_error("Failed to activate network context! (%d)", err);
00555         return err;
00556     }
00557 
00558     // do check for stack to validate that we have support for stack
00559     if (!get_stack()) {
00560         _at.unlock();
00561         tr_error("No cellular stack!");
00562         return NSAPI_ERROR_UNSUPPORTED ;
00563     }
00564 
00565     _is_context_active = false;
00566     _is_context_activated = false;
00567 
00568     _is_context_active = _nw->is_active_context(NULL, _cid);
00569 
00570     if (!_is_context_active) {
00571         activate_context();
00572     }
00573 
00574     err = (_at.get_last_error() == NSAPI_ERROR_OK ) ? NSAPI_ERROR_OK  : NSAPI_ERROR_NO_CONNECTION ;
00575 
00576     // If new PDP context was created and failed to activate, delete it
00577     if (err != NSAPI_ERROR_OK  && _new_context_set) {
00578         delete_current_context();
00579     } else if (err == NSAPI_ERROR_OK ) {
00580         _is_context_active = true;
00581     }
00582 
00583     _at.unlock();
00584 
00585     return err;
00586 }
00587 
00588 void AT_CellularContext::do_connect()
00589 {
00590     if (!_is_context_active) {
00591         _cb_data.error = do_activate_context();
00592     } else {
00593         _cb_data.error = NSAPI_ERROR_OK ;
00594     }
00595 
00596 #if !NSAPI_PPP_AVAILABLE
00597     // in PPP mode we did not activate any context, just searched the correct _cid
00598     if (_status_cb) {
00599         _status_cb((nsapi_event_t)CellularActivatePDPContext, (intptr_t)&_cb_data);
00600     }
00601 #endif // !NSAPI_PPP_AVAILABLE
00602 
00603     if (_cb_data.error != NSAPI_ERROR_OK ) {
00604         _is_connected = false;
00605         return;
00606     }
00607 #if NSAPI_PPP_AVAILABLE
00608     if (_cb_data.error == NSAPI_ERROR_OK ) {
00609         _at.lock();
00610         _cb_data.error = open_data_channel();
00611         _at.unlock();
00612         if (_cb_data.error != NSAPI_ERROR_OK ) {
00613             _is_connected = false;
00614         }
00615     }
00616 #else
00617     _is_connected = true;
00618 #endif
00619 }
00620 
00621 #if NSAPI_PPP_AVAILABLE
00622 nsapi_error_t AT_CellularContext::open_data_channel()
00623 {
00624     // If Non-IP in use fail
00625     if (_pdp_type == NON_IP_PDP_TYPE) {
00626         tr_error("Attempt of PPP connect over NON-IP: failed to CONNECT");
00627         return NSAPI_ERROR_PARAMETER ;
00628     }
00629 
00630     tr_info("CellularContext PPP connect");
00631     if (get_device()->get_property(AT_CellularDevice::PROPERTY_AT_CGDATA)) {
00632         _at.cmd_start_stop("+CGDATA", "=\"PPP\",", "%d", _cid);
00633     } else {
00634         MBED_ASSERT(_cid >= 0 && _cid <= 99);
00635         _at.cmd_start("ATD*99***");
00636         _at.use_delimiter(false);
00637         _at.write_int(_cid);
00638         _at.write_string("#", false);
00639         _at.use_delimiter(true);
00640         _at.cmd_stop();
00641     }
00642 
00643     _at.resp_start("CONNECT", true);
00644     if (_at.get_last_error()) {
00645         tr_error("Failed to CONNECT");
00646         return _at.get_last_error();
00647     }
00648 
00649     _at.set_is_filehandle_usable(false);
00650     enable_hup(true);
00651     /* Initialize PPP
00652      * If blocking: mbed_ppp_init() is a blocking call, it will block until
00653                   connected, or timeout after 30 seconds*/
00654     nsapi_error_t err = nsapi_ppp_connect(_at.get_file_handle(), callback(this, &AT_CellularContext::ppp_status_cb), _uname, _pwd, (nsapi_ip_stack_t)_pdp_type);
00655     if (err) {
00656         tr_error("nsapi_ppp_connect failed");
00657         ppp_disconnected();
00658     }
00659 
00660     return err;
00661 }
00662 
00663 void AT_CellularContext::ppp_status_cb(nsapi_event_t ev, intptr_t ptr)
00664 {
00665     tr_debug("ppp_status_cb: event %d, ptr %d", ev, ptr);
00666 
00667     if (ev == NSAPI_EVENT_CONNECTION_STATUS_CHANGE  && ptr == NSAPI_STATUS_GLOBAL_UP ) {
00668         _is_connected = true;
00669     } else {
00670         // catch all NSAPI_STATUS_DISCONNECTED events but send to device only when we did not ask for disconnect.
00671         if (ev == NSAPI_EVENT_CONNECTION_STATUS_CHANGE  && ptr == NSAPI_STATUS_DISCONNECTED ) {
00672             if (_is_connected) { // set to false in disconnect() before calling nsapi_ppp_disconnect()
00673                 _is_connected = false;
00674                 ppp_disconnected();
00675                 _device->cellular_callback(ev, ptr, this);
00676             }
00677             return; // return here so if we were not in connected state we don't send NSAPI_STATUS_DISCONNECTED event
00678         }
00679         _is_connected = false;
00680     }
00681 
00682     // call device's callback, it will broadcast this to here (cellular_callback)
00683     _device->cellular_callback(ev, ptr, this);
00684 }
00685 
00686 void AT_CellularContext::ppp_disconnected()
00687 {
00688     enable_hup(false);
00689 
00690     // after ppp disconnect if we wan't to use same at handler we need to set filehandle again to athandler so it
00691     // will set the correct sigio and nonblocking
00692     _at.lock();
00693     _at.set_is_filehandle_usable(true);
00694     if (!_at.sync(AT_SYNC_TIMEOUT)) { // consume extra characters after ppp disconnect, also it may take a while until modem listens AT commands
00695         tr_error("AT sync failed after PPP Disconnect");
00696     }
00697     _at.unlock();
00698 }
00699 
00700 #endif //#if NSAPI_PPP_AVAILABLE
00701 
00702 void AT_CellularContext::do_disconnect()
00703 {
00704     if (!_nw || !_is_connected) {
00705         if (_new_context_set) {
00706             delete_current_context();
00707         }
00708         set_cid(-1);
00709         _cb_data.error = NSAPI_ERROR_NO_CONNECTION ;
00710     }
00711 
00712     // set false here so callbacks know that we are not connected and so should not send DISCONNECTED
00713     _is_connected = false;
00714 #if NSAPI_PPP_AVAILABLE
00715     nsapi_error_t err = nsapi_ppp_disconnect(_at.get_file_handle());
00716     if (err != NSAPI_ERROR_OK ) {
00717         tr_error("CellularContext disconnect failed!");
00718         // continue even in failure due to ppp disconnect in any case releases filehandle
00719     }
00720     ppp_disconnected();
00721 #endif // NSAPI_PPP_AVAILABLE
00722     _at.lock();
00723 
00724     // deactivate a context only if we have activated
00725     if (_is_context_activated) {
00726         if (_nonip_req && _cp_in_use) {
00727             deactivate_non_ip_context();
00728         } else {
00729             deactivate_ip_context();
00730         }
00731     }
00732 
00733     // don't call multiple times disconnect if we already got that event from network urc or ppp
00734     if (_connect_status != NSAPI_STATUS_DISCONNECTED ) {
00735         _device->cellular_callback(NSAPI_EVENT_CONNECTION_STATUS_CHANGE , NSAPI_STATUS_DISCONNECTED , this);
00736     }
00737     _is_context_active = false;
00738     _connect_status = NSAPI_STATUS_DISCONNECTED ;
00739 
00740 
00741     if (_new_context_set) {
00742         delete_current_context();
00743     }
00744     set_cid(-1);
00745     _cb_data.error = _at.unlock_return_error();
00746 }
00747 
00748 nsapi_error_t AT_CellularContext::disconnect()
00749 {
00750     tr_info("CellularContext disconnect()");
00751     if (_is_blocking) {
00752         do_disconnect();
00753         return _cb_data.error;
00754     } else {
00755         int event_id = _device->get_queue()->call_in(0, this, &AT_CellularContext::do_disconnect);
00756         if (event_id == 0) {
00757             return NSAPI_ERROR_NO_MEMORY ;
00758         }
00759         return NSAPI_ERROR_OK ;
00760     }
00761 }
00762 
00763 void AT_CellularContext::deactivate_ip_context()
00764 {
00765     check_and_deactivate_context();
00766 }
00767 
00768 void AT_CellularContext::deactivate_non_ip_context()
00769 {
00770     check_and_deactivate_context();
00771 }
00772 
00773 void AT_CellularContext::deactivate_context()
00774 {
00775     _at.at_cmd_discard("+CGACT", "=0,", "%d", _cid);
00776 }
00777 
00778 void AT_CellularContext::check_and_deactivate_context()
00779 {
00780     // CGACT and CGATT commands might take up to 3 minutes to respond.
00781     _at.set_at_timeout(180 * 1000);
00782     int active_contexts_count = 0;
00783     _is_context_active = _nw->is_active_context(&active_contexts_count, _cid);
00784 
00785     CellularNetwork::RadioAccessTechnology rat = CellularNetwork::RAT_GSM;
00786     // always return NSAPI_ERROR_OK
00787     CellularNetwork::registration_params_t reg_params;
00788     _nw->get_registration_params(reg_params);
00789     rat = reg_params._act;
00790     // 3GPP TS 27.007:
00791     // For EPS, if an attempt is made to disconnect the last PDN connection, then the MT responds with ERROR
00792     if (_is_context_active && (rat < CellularNetwork::RAT_E_UTRAN || active_contexts_count > 1)) {
00793         _at.clear_error();
00794         deactivate_context();
00795     }
00796 
00797     if (_new_context_set) {
00798         delete_current_context();
00799     }
00800 
00801     _at.restore_at_timeout();
00802 }
00803 
00804 nsapi_error_t AT_CellularContext::get_apn_backoff_timer(int &backoff_timer)
00805 {
00806     // If apn is set
00807     if (_apn) {
00808         _at.lock();
00809         _at.cmd_start_stop("+CABTRDP", "=", "%s", _apn);
00810         _at.resp_start("+CABTRDP:");
00811         if (_at.info_resp()) {
00812             _at.skip_param();
00813             backoff_timer = _at.read_int();
00814         }
00815         _at.resp_stop();
00816         return _at.unlock_return_error();
00817     }
00818 
00819     return NSAPI_ERROR_PARAMETER ;
00820 }
00821 
00822 nsapi_error_t AT_CellularContext::get_rate_control(
00823     CellularContext::RateControlExceptionReports &reports,
00824     CellularContext::RateControlUplinkTimeUnit &timeUnit, int &uplinkRate)
00825 {
00826     _at.lock();
00827     _at.cmd_start_stop("+CGAPNRC", "=", "%d", _cid);
00828 
00829     _at.resp_start("+CGAPNRC:");
00830     _at.read_int();
00831     if (_at.get_last_error() == NSAPI_ERROR_OK ) {
00832         bool comma_found = true;
00833         int next_element = _at.read_int();
00834         if (next_element >= 0) {
00835             reports = (RateControlExceptionReports)next_element;
00836             next_element = _at.read_int();
00837         } else {
00838             comma_found = false;
00839         }
00840 
00841         if (comma_found && next_element >= 0) {
00842             timeUnit = (RateControlUplinkTimeUnit)next_element;
00843             next_element = _at.read_int();
00844         } else {
00845             comma_found = false;
00846         }
00847 
00848         if (comma_found && next_element >= 0) {
00849             uplinkRate = next_element;
00850         }
00851         if (_at.get_last_error() == NSAPI_ERROR_OK ) {
00852             tr_debug("CGAPNRC: reports %d, time %d, rate %d", reports, timeUnit, uplinkRate);
00853         }
00854     }
00855     _at.resp_stop();
00856 
00857     return _at.unlock_return_error();
00858 }
00859 
00860 nsapi_error_t AT_CellularContext::get_pdpcontext_params(pdpContextList_t &params_list)
00861 {
00862     const int ipv6_subnet_size = 128;
00863     char *ipv6_and_subnetmask = new char[ipv6_subnet_size];
00864 
00865     _at.lock();
00866 
00867     _at.cmd_start_stop("+CGCONTRDP", "=", "%d", _cid);
00868 
00869     _at.resp_start("+CGCONTRDP:");
00870     pdpcontext_params_t *params = NULL;
00871     while (_at.info_resp()) { // response can be zero or many +CGDCONT lines
00872         params = params_list.add_new();
00873         params->cid = _at.read_int();
00874         params->bearer_id = _at.read_int();
00875         _at.read_string(params->apn, sizeof(params->apn));
00876 
00877         // rest are optional params
00878         ipv6_and_subnetmask[0] = '\0';
00879         _at.read_string(ipv6_and_subnetmask, ipv6_subnet_size);
00880         separate_ip_addresses(ipv6_and_subnetmask, params->local_addr, sizeof(params->local_addr), params->local_subnet_mask, sizeof(params->local_subnet_mask));
00881         ipv6_and_subnetmask[0] = '\0';
00882 
00883         _at.read_string(ipv6_and_subnetmask, ipv6_subnet_size);
00884         separate_ip_addresses(ipv6_and_subnetmask, params->gateway_addr, sizeof(params->gateway_addr), NULL, 0);
00885         ipv6_and_subnetmask[0] = '\0';
00886 
00887         _at.read_string(ipv6_and_subnetmask, ipv6_subnet_size);
00888         separate_ip_addresses(ipv6_and_subnetmask, params->dns_primary_addr, sizeof(params->dns_primary_addr), NULL, 0);
00889         ipv6_and_subnetmask[0] = '\0';
00890 
00891         _at.read_string(ipv6_and_subnetmask, ipv6_subnet_size);
00892         separate_ip_addresses(ipv6_and_subnetmask, params->dns_secondary_addr, sizeof(params->dns_secondary_addr), NULL, 0);
00893         ipv6_and_subnetmask[0] = '\0';
00894 
00895         _at.read_string(ipv6_and_subnetmask, ipv6_subnet_size);
00896         separate_ip_addresses(ipv6_and_subnetmask, params->p_cscf_prim_addr, sizeof(params->p_cscf_prim_addr), NULL, 0);
00897         ipv6_and_subnetmask[0] = '\0';
00898 
00899         _at.read_string(ipv6_and_subnetmask, ipv6_subnet_size);
00900         separate_ip_addresses(ipv6_and_subnetmask, params->p_cscf_sec_addr, sizeof(params->p_cscf_sec_addr), NULL, 0);
00901 
00902         params->im_signalling_flag = _at.read_int();
00903         params->lipa_indication = _at.read_int();
00904         params->ipv4_mtu = _at.read_int();
00905         params->wlan_offload = _at.read_int();
00906         params->local_addr_ind = _at.read_int();
00907         params->non_ip_mtu = _at.read_int();
00908         params->serving_plmn_rate_control_value = _at.read_int();
00909     }
00910     _at.resp_stop();
00911 
00912     delete [] ipv6_and_subnetmask;
00913 
00914     return _at.unlock_return_error();
00915 }
00916 
00917 // Called by CellularDevice for network and cellular device changes
00918 void AT_CellularContext::cellular_callback(nsapi_event_t ev, intptr_t ptr)
00919 {
00920     if (ev >= NSAPI_EVENT_CELLULAR_STATUS_BASE  && ev <= NSAPI_EVENT_CELLULAR_STATUS_END ) {
00921         cell_callback_data_t *data = (cell_callback_data_t *)ptr;
00922         cellular_connection_status_t st = (cellular_connection_status_t)ev;
00923         _cb_data.error = data->error;
00924         _cb_data.final_try = data->final_try;
00925         if (data->final_try) {
00926             if (_current_op != OP_INVALID) {
00927                 _semaphore.release();
00928             }
00929         }
00930 #if MBED_CONF_CELLULAR_USE_APN_LOOKUP
00931         if (st == CellularSIMStatusChanged && data->status_data == CellularDevice::SimStateReady &&
00932                 _cb_data.error == NSAPI_ERROR_OK ) {
00933             if (!_apn) {
00934                 char imsi[MAX_IMSI_LENGTH + 1];
00935                 ThisThread::sleep_for(1000); // need to wait to access SIM in some modems
00936                 _cb_data.error = _device->open_information()->get_imsi(imsi, sizeof(imsi));
00937                 if (_cb_data.error == NSAPI_ERROR_OK ) {
00938                     const char *apn_config = apnconfig(imsi);
00939                     if (apn_config) {
00940                         const char *apn = _APN_GET(apn_config);
00941                         const char *uname = _APN_GET(apn_config);
00942                         const char *pwd = _APN_GET(apn_config);
00943                         tr_info("Looked up APN %s", apn);
00944                         set_credentials(apn, uname, pwd);
00945                     }
00946                 } else {
00947                     tr_error("APN lookup failed");
00948                     _device->stop();
00949                     if (_is_blocking) {
00950                         // operation failed, release semaphore
00951                         if (_current_op != OP_INVALID) {
00952                             _semaphore.release();
00953                         }
00954                     }
00955                 }
00956                 _device->close_information();
00957             }
00958         }
00959 #endif // MBED_CONF_CELLULAR_USE_APN_LOOKUP
00960 
00961         if (!_nw && st == CellularDeviceReady && _cb_data.error == NSAPI_ERROR_OK ) {
00962             _nw = _device->open_network(_fh);
00963         }
00964 
00965         if (_cp_req && !_cp_in_use && (_cb_data.error == NSAPI_ERROR_OK ) &&
00966                 (st == CellularSIMStatusChanged && data->status_data == CellularDevice::SimStateReady)) {
00967             if (setup_control_plane_opt() != NSAPI_ERROR_OK ) {
00968                 tr_error("Control plane SETUP failed!");
00969             } else {
00970                 tr_info("Control plane SETUP success!");
00971             }
00972         }
00973 
00974         if (_is_blocking) {
00975             if (_cb_data.error != NSAPI_ERROR_OK ) {
00976                 // operation failed, release semaphore
00977                 if (_current_op != OP_INVALID) {
00978                     _current_op = OP_INVALID;
00979                     _semaphore.release();
00980                 }
00981             } else {
00982                 if ((st == CellularDeviceReady && _current_op == OP_DEVICE_READY) ||
00983                         (st == CellularSIMStatusChanged && _current_op == OP_SIM_READY &&
00984                          data->status_data == CellularDevice::SimStateReady)) {
00985                     // target reached, release semaphore
00986                     _current_op = OP_INVALID;
00987                     _semaphore.release();
00988                 } else if (st == CellularRegistrationStatusChanged && (data->status_data == CellularNetwork::RegisteredHomeNetwork ||
00989                                                                        data->status_data == CellularNetwork::RegisteredRoaming || data->status_data == CellularNetwork::AlreadyRegistered) && _current_op == OP_REGISTER) {
00990                     // target reached, release semaphore
00991                     _current_op = OP_INVALID;
00992                     _semaphore.release();
00993                 } else if (st == CellularAttachNetwork && (_current_op == OP_ATTACH || _current_op == OP_CONNECT) &&
00994                            data->status_data == CellularNetwork::Attached) {
00995                     // target reached, release semaphore
00996                     _current_op = OP_INVALID;
00997                     _semaphore.release();
00998                 }
00999             }
01000         } else {
01001             // non blocking
01002             if (st == CellularAttachNetwork && _current_op == OP_CONNECT && _cb_data.error == NSAPI_ERROR_OK  &&
01003                     data->status_data == CellularNetwork::Attached) {
01004                 _current_op = OP_INVALID;
01005                 // forward all Cellular specific events to application
01006                 if (_status_cb) {
01007                     _status_cb(ev, ptr);
01008                 }
01009                 _retry_count = 0;
01010                 do_connect_with_retry();
01011                 return;
01012             }
01013         }
01014 
01015         // forward all Cellular specific events to application
01016         if (_status_cb) {
01017             _status_cb(ev, ptr);
01018         }
01019     } else {
01020 #if NSAPI_PPP_AVAILABLE
01021         if (_is_blocking) {
01022             if (ev == NSAPI_EVENT_CONNECTION_STATUS_CHANGE  && ptr == NSAPI_STATUS_GLOBAL_UP ) {
01023                 tr_info("CellularContext IP %s", get_ip_address());
01024                 _cb_data.error = NSAPI_ERROR_OK ;
01025             } else if (ev == NSAPI_EVENT_CONNECTION_STATUS_CHANGE  && ptr == NSAPI_STATUS_DISCONNECTED ) {
01026                 tr_info("cellular_callback: PPP mode and NSAPI_STATUS_DISCONNECTED");
01027                 _cb_data.error = NSAPI_ERROR_NO_CONNECTION ;
01028                 set_cid(-1);
01029                 _is_connected = false;
01030                 ppp_disconnected();
01031             }
01032         }
01033 #else
01034         if (ev == NSAPI_EVENT_CONNECTION_STATUS_CHANGE  && ptr == NSAPI_STATUS_DISCONNECTED ) {
01035             tr_info("cb: CellularContext disconnected");
01036             set_cid(-1);
01037             _is_connected = false;
01038         }
01039 #endif // NSAPI_PPP_AVAILABLE
01040         // forward status change events to application, call_network_cb will make sure that only changed event are forwarded
01041         call_network_cb((nsapi_connection_status_t)ptr);
01042     }
01043 }
01044 
01045 ControlPlane_netif *AT_CellularContext::get_cp_netif()
01046 {
01047     tr_error("No control plane interface available from base context!");
01048     return NULL;
01049 }
01050 
01051 nsapi_error_t AT_CellularContext::setup_control_plane_opt()
01052 {
01053     // check if control plane optimization already set
01054     mbed::CellularNetwork::CIoT_Supported_Opt supported_network_opt;
01055 
01056     if (_nw->get_ciot_network_optimization_config(supported_network_opt)) {
01057         return NSAPI_ERROR_DEVICE_ERROR ;
01058     }
01059 
01060     if (supported_network_opt == mbed::CellularNetwork::CIOT_OPT_CONTROL_PLANE  ||
01061             supported_network_opt == mbed::CellularNetwork::CIOT_OPT_BOTH) {
01062         _cp_in_use = true;
01063         return NSAPI_ERROR_OK ;
01064     }
01065 
01066     // ciot optimization not set by app so need to set it now
01067     nsapi_error_t ciot_opt_ret;
01068     ciot_opt_ret = _nw->set_ciot_optimization_config(mbed::CellularNetwork::CIOT_OPT_CONTROL_PLANE,
01069                                                      mbed::CellularNetwork::PREFERRED_UE_OPT_CONTROL_PLANE,
01070                                                      callback(this, &AT_CellularContext::ciot_opt_cb));
01071 
01072     if (ciot_opt_ret != NSAPI_ERROR_OK ) {
01073         return ciot_opt_ret;
01074     }
01075 
01076     //wait for control plane opt call back to release semaphore
01077     _cp_opt_semaphore.try_acquire_for(CP_OPT_NW_REPLY_TIMEOUT);
01078 
01079     if (_cp_in_use) {
01080         return NSAPI_ERROR_OK ;
01081     }
01082 
01083     return NSAPI_ERROR_DEVICE_ERROR ;
01084 }
01085 
01086 void AT_CellularContext::ciot_opt_cb(mbed::CellularNetwork::CIoT_Supported_Opt  ciot_opt)
01087 {
01088     if (ciot_opt == mbed::CellularNetwork::CIOT_OPT_CONTROL_PLANE ||
01089             ciot_opt == mbed::CellularNetwork::CIOT_OPT_BOTH) {
01090         _cp_in_use = true;
01091     }
01092     _cp_opt_semaphore.release();
01093 }
01094 
01095 void AT_CellularContext::set_disconnect()
01096 {
01097     tr_debug("AT_CellularContext::set_disconnect()");
01098     _is_connected = false;
01099     _device->cellular_callback(NSAPI_EVENT_CONNECTION_STATUS_CHANGE , NSAPI_STATUS_DISCONNECTED , this);
01100 }
01101 
01102 void AT_CellularContext::set_cid(int cid)
01103 {
01104     _cid = cid;
01105     if (_stack) {
01106         static_cast<AT_CellularStack *>(_stack)->set_cid(_cid);
01107     }
01108 }
01109 
01110 ATHandler &AT_CellularContext::get_at_handler()
01111 {
01112     return _at;
01113 }