Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: TYBLE16_simple_data_logger TYBLE16_MP3_Air
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 ¶ms_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 }
Generated on Tue Jul 12 2022 13:54:01 by
