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.
AT_CellularNetwork.cpp
00001 /* 00002 * Copyright (c) 2017, Arm Limited and affiliates. 00003 * SPDX-License-Identifier: Apache-2.0 00004 * 00005 * Licensed under the Apache License, Version 2.0 (the "License"); 00006 * you may not use this file except in compliance with the License. 00007 * You may obtain a copy of the License at 00008 * 00009 * http://www.apache.org/licenses/LICENSE-2.0 00010 * 00011 * Unless required by applicable law or agreed to in writing, software 00012 * distributed under the License is distributed on an "AS IS" BASIS, 00013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00014 * See the License for the specific language governing permissions and 00015 * limitations under the License. 00016 */ 00017 00018 #include <stdlib.h> 00019 #include "AT_CellularNetwork.h" 00020 #include "nsapi_ppp.h" 00021 #include "CellularUtil.h" 00022 #include "CellularLog.h" 00023 00024 using namespace std; 00025 using namespace mbed_cellular_util; 00026 using namespace mbed; 00027 00028 struct at_reg_t { 00029 const CellularNetwork::RegistrationType type; 00030 const char *const cmd; 00031 }; 00032 00033 static const at_reg_t at_reg[] = { 00034 { CellularNetwork::C_EREG, "AT+CEREG" }, 00035 { CellularNetwork::C_GREG, "AT+CGREG" }, 00036 { CellularNetwork::C_REG, "AT+CREG" }, 00037 }; 00038 00039 AT_CellularNetwork::AT_CellularNetwork(ATHandler &atHandler) : AT_CellularBase(atHandler), 00040 _stack(NULL), _apn(NULL), _uname(NULL), _pwd(NULL), _ip_stack_type_requested(DEFAULT_STACK), _ip_stack_type(DEFAULT_STACK), _cid(-1), 00041 _connection_status_cb(NULL), _op_act(operator_t::RAT_UNKNOWN), _authentication_type(CHAP), _last_reg_type(C_REG), 00042 _connect_status(NSAPI_STATUS_DISCONNECTED ), _new_context_set(false) 00043 { 00044 00045 _at.set_urc_handler("NO CARRIER", callback(this, &AT_CellularNetwork::urc_no_carrier)); 00046 } 00047 00048 AT_CellularNetwork::~AT_CellularNetwork() 00049 { 00050 free_credentials(); 00051 } 00052 00053 void AT_CellularNetwork::free_credentials() 00054 { 00055 if (_uname) { 00056 free(_uname); 00057 } 00058 00059 if (_pwd) { 00060 free(_pwd); 00061 } 00062 00063 if (_apn) { 00064 free(_apn); 00065 } 00066 } 00067 00068 void AT_CellularNetwork::urc_no_carrier() 00069 { 00070 _connect_status = NSAPI_STATUS_DISCONNECTED ; 00071 if (_connection_status_cb) { 00072 _connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE , NSAPI_STATUS_DISCONNECTED ); 00073 } 00074 } 00075 00076 nsapi_error_t AT_CellularNetwork::set_credentials(const char *apn, 00077 const char *username, const char *password) 00078 { 00079 size_t len; 00080 if (apn && (len = strlen(apn)) > 0) { 00081 _apn = (char*)malloc(len*sizeof(char)+1); 00082 if (_apn) { 00083 memcpy(_apn, apn, len+1); 00084 } else { 00085 return NSAPI_ERROR_NO_MEMORY ; 00086 } 00087 } 00088 00089 if (username && (len = strlen(username)) > 0) { 00090 _uname = (char*)malloc(len*sizeof(char)+1); 00091 if (_uname) { 00092 memcpy(_uname, username, len+1); 00093 } else { 00094 return NSAPI_ERROR_NO_MEMORY ; 00095 } 00096 } 00097 00098 if (password && (len = strlen(password)) > 0) { 00099 _pwd = (char*)malloc(len*sizeof(char)+1); 00100 if (_pwd) { 00101 memcpy(_pwd, password, len+1); 00102 } else { 00103 return NSAPI_ERROR_NO_MEMORY ; 00104 } 00105 } 00106 00107 return NSAPI_ERROR_OK ; 00108 } 00109 00110 nsapi_error_t AT_CellularNetwork::set_credentials(const char *apn, 00111 AuthenticationType type, const char *username, const char *password) 00112 { 00113 nsapi_error_t err = set_credentials(apn, username, password); 00114 if (err) { 00115 return err; 00116 } 00117 00118 _authentication_type = type; 00119 00120 return NSAPI_ERROR_OK ; 00121 } 00122 00123 nsapi_error_t AT_CellularNetwork::connect(const char *apn, 00124 const char *username, const char *password) 00125 { 00126 nsapi_error_t err = set_credentials(apn, username, password); 00127 if (err) { 00128 return err; 00129 } 00130 00131 return connect(); 00132 } 00133 00134 nsapi_error_t AT_CellularNetwork::delete_current_context() 00135 { 00136 tr_info("Delete context %d", _cid); 00137 _at.clear_error(); 00138 _at.cmd_start("AT+CGDCONT="); 00139 _at.write_int(_cid); 00140 _at.cmd_stop(); 00141 _at.resp_start(); 00142 _at.resp_stop(); 00143 00144 if (_at.get_last_error() == NSAPI_ERROR_OK ) { 00145 _cid = -1; 00146 _new_context_set = false; 00147 } 00148 00149 return _at.get_last_error(); 00150 } 00151 00152 nsapi_error_t AT_CellularNetwork::connect() 00153 { 00154 _at.lock(); 00155 00156 _connect_status = NSAPI_STATUS_CONNECTING ; 00157 if (_connection_status_cb) { 00158 _connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE , NSAPI_STATUS_CONNECTING ); 00159 } 00160 00161 nsapi_error_t err = set_context_to_be_activated(); 00162 if (err != NSAPI_ERROR_OK ) { 00163 _at.unlock(); 00164 tr_error("Failed to activate network context!"); 00165 00166 _connect_status = NSAPI_STATUS_DISCONNECTED ; 00167 if (_connection_status_cb) { 00168 _connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE , NSAPI_STATUS_DISCONNECTED ); 00169 } 00170 00171 return err; 00172 } 00173 00174 err = open_data_channel(); 00175 if (err != NSAPI_ERROR_OK ) { 00176 00177 // If new PDP context was created and failed to activate, delete it 00178 if (_new_context_set) { 00179 delete_current_context(); 00180 } 00181 00182 _at.unlock(); 00183 00184 tr_error("Failed to open data channel!"); 00185 00186 _connect_status = NSAPI_STATUS_DISCONNECTED ; 00187 if (_connection_status_cb) { 00188 _connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE , NSAPI_STATUS_DISCONNECTED ); 00189 } 00190 00191 return err; 00192 } 00193 00194 _at.unlock(); 00195 00196 #if !NSAPI_PPP_AVAILABLE 00197 _connect_status = NSAPI_STATUS_GLOBAL_UP ; 00198 if (_connection_status_cb) { 00199 _connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE , NSAPI_STATUS_GLOBAL_UP ); 00200 } 00201 #endif 00202 00203 return NSAPI_ERROR_OK ; 00204 } 00205 00206 nsapi_error_t AT_CellularNetwork::open_data_channel() 00207 { 00208 //old way: _at.send("ATD*99***%d#", _cid) && _at.recv("CONNECT"); 00209 nsapi_error_t err = NSAPI_ERROR_NO_CONNECTION ; 00210 #if NSAPI_PPP_AVAILABLE 00211 tr_info("Open data channel in PPP mode"); 00212 _at.cmd_start("AT+CGDATA=\"PPP\","); 00213 _at.write_int(_cid); 00214 _at.cmd_stop(); 00215 00216 _at.resp_start("CONNECT", true); 00217 if (_at.get_last_error()) { 00218 tr_warn("Failed to CONNECT"); 00219 } 00220 /* Initialize PPP 00221 * mbed_ppp_init() is a blocking call, it will block until 00222 * connected, or timeout after 30 seconds*/ 00223 err = nsapi_ppp_connect(_at.get_file_handle(), callback(this, &AT_CellularNetwork::ppp_status_cb), _uname, _pwd, _ip_stack_type); 00224 #else 00225 // do check for stack to validate that we have support for stack 00226 _stack = get_stack(); 00227 if (!_stack) { 00228 return err; 00229 } 00230 00231 bool is_context_active = false; 00232 _at.cmd_start("AT+CGACT?"); 00233 _at.cmd_stop(); 00234 _at.resp_start("+CGACT:"); 00235 while (_at.info_resp()) { 00236 int context_id = _at.read_int(); 00237 int context_activation_state = _at.read_int(); 00238 if (context_id == _cid && context_activation_state == 1) { 00239 is_context_active = true; 00240 tr_debug("PDP context %d is active.", _cid); 00241 break; 00242 } 00243 } 00244 _at.resp_stop(); 00245 00246 if (!is_context_active) { 00247 tr_info("Activate PDP context %d", _cid); 00248 _at.cmd_start("AT+CGACT=1,"); 00249 _at.write_int(_cid); 00250 _at.cmd_stop(); 00251 _at.resp_start(); 00252 _at.resp_stop(); 00253 } 00254 00255 err = (_at.get_last_error() == NSAPI_ERROR_OK ) ? NSAPI_ERROR_OK : NSAPI_ERROR_NO_CONNECTION ; 00256 #endif 00257 return err; 00258 } 00259 00260 /** 00261 * User initiated disconnect 00262 * 00263 * Disconnects from PPP connection only and brings down the underlying network 00264 * interface 00265 */ 00266 nsapi_error_t AT_CellularNetwork::disconnect() 00267 { 00268 #if NSAPI_PPP_AVAILABLE 00269 return nsapi_ppp_disconnect(_at.get_file_handle()); 00270 #else 00271 _at.lock(); 00272 _at.cmd_start("AT+CGACT=0,"); 00273 _at.write_int(_cid); 00274 _at.cmd_stop(); 00275 _at.resp_start(); 00276 _at.resp_stop(); 00277 _at.restore_at_timeout(); 00278 00279 _connect_status = NSAPI_STATUS_DISCONNECTED ; 00280 if (_connection_status_cb) { 00281 _connection_status_cb(NSAPI_EVENT_CONNECTION_STATUS_CHANGE , NSAPI_STATUS_DISCONNECTED ); 00282 } 00283 00284 return _at.unlock_return_error(); 00285 #endif 00286 } 00287 00288 void AT_CellularNetwork::attach(Callback<void(nsapi_event_t, intptr_t)> status_cb) 00289 { 00290 _connection_status_cb = status_cb; 00291 } 00292 00293 nsapi_connection_status_t AT_CellularNetwork::get_connection_status() const 00294 { 00295 return _connect_status; 00296 } 00297 00298 nsapi_error_t AT_CellularNetwork::set_blocking(bool blocking) 00299 { 00300 #if NSAPI_PPP_AVAILABLE 00301 return nsapi_ppp_set_blocking(blocking); 00302 #else 00303 return NSAPI_ERROR_UNSUPPORTED ; 00304 #endif 00305 } 00306 00307 00308 #if NSAPI_PPP_AVAILABLE 00309 void AT_CellularNetwork::ppp_status_cb(nsapi_event_t event, intptr_t parameter) 00310 { 00311 _connect_status = (nsapi_connection_status_t)parameter; 00312 00313 if (_connection_status_cb) { 00314 _connection_status_cb(event, parameter); 00315 } 00316 } 00317 #endif 00318 00319 00320 00321 nsapi_error_t AT_CellularNetwork::set_context_to_be_activated() 00322 { 00323 // try to find or create context with suitable stack 00324 if (!get_context()) { 00325 return NSAPI_ERROR_NO_CONNECTION ; 00326 } 00327 00328 // if user has defined user name and password we need to call CGAUTH before activating or modifying context 00329 if (_pwd && _uname) { 00330 _at.cmd_start("AT+CGAUTH="); 00331 _at.write_int(_cid); 00332 _at.write_int(_authentication_type); 00333 _at.write_string(_uname); 00334 _at.write_string(_pwd); 00335 _at.cmd_stop(); 00336 _at.resp_start(); 00337 _at.resp_stop(); 00338 if (_at.get_last_error() != NSAPI_ERROR_OK ) { 00339 return NSAPI_ERROR_AUTH_FAILURE ; 00340 } 00341 } 00342 00343 return _at.get_last_error(); 00344 } 00345 00346 bool AT_CellularNetwork::set_new_context(int cid) 00347 { 00348 nsapi_ip_stack_t tmp_stack = _ip_stack_type_requested; 00349 00350 if (tmp_stack == DEFAULT_STACK) { 00351 bool modem_supports_ipv6 = get_modem_stack_type(IPV6_STACK); 00352 bool modem_supports_ipv4 = get_modem_stack_type(IPV4_STACK); 00353 00354 if (modem_supports_ipv6 && modem_supports_ipv4) { 00355 tmp_stack = IPV4V6_STACK; 00356 } else if (modem_supports_ipv6) { 00357 tmp_stack = IPV6_STACK; 00358 } else if (modem_supports_ipv4) { 00359 tmp_stack = IPV4_STACK; 00360 } 00361 } 00362 00363 char pdp_type[8+1] = {0}; 00364 00365 switch (tmp_stack) { 00366 case IPV4_STACK: 00367 strncpy(pdp_type, "IP", sizeof(pdp_type)); 00368 break; 00369 case IPV6_STACK: 00370 strncpy(pdp_type, "IPV6", sizeof(pdp_type)); 00371 break; 00372 case IPV4V6_STACK: 00373 strncpy(pdp_type, "IPV4V6", sizeof(pdp_type)); 00374 break; 00375 default: 00376 break; 00377 } 00378 00379 //apn: "If the value is null or omitted, then the subscription value will be requested." 00380 bool success = false; 00381 _at.cmd_start("AT+CGDCONT="); 00382 _at.write_int(cid); 00383 _at.write_string(pdp_type); 00384 _at.write_string(_apn); 00385 _at.cmd_stop(); 00386 _at.resp_start(); 00387 _at.resp_stop(); 00388 success = (_at.get_last_error() == NSAPI_ERROR_OK ); 00389 00390 // Fall back to ipv4 00391 if (!success && tmp_stack == IPV4V6_STACK) { 00392 tmp_stack = IPV4_STACK; 00393 _at.cmd_start("AT+FCLASS=0;+CGDCONT="); 00394 _at.write_int(cid); 00395 _at.write_string("IP"); 00396 _at.write_string(_apn); 00397 _at.cmd_stop(); 00398 _at.resp_start(); 00399 _at.resp_stop(); 00400 success = (_at.get_last_error() == NSAPI_ERROR_OK ); 00401 } 00402 00403 if (success) { 00404 _ip_stack_type = tmp_stack; 00405 _cid = cid; 00406 _new_context_set = true; 00407 tr_info("New PDP context id %d was created", _cid); 00408 } 00409 00410 return success; 00411 } 00412 00413 bool AT_CellularNetwork::get_context() 00414 { 00415 if (_apn) { 00416 tr_debug("APN in use: %s", _apn); 00417 } else { 00418 tr_debug("NO APN"); 00419 } 00420 00421 _at.cmd_start("AT+CGDCONT?"); 00422 _at.cmd_stop(); 00423 _at.resp_start("+CGDCONT:"); 00424 _cid = -1; 00425 int cid_max = 0; // needed when creating new context 00426 char apn[MAX_ACCESSPOINT_NAME_LENGTH]; 00427 int apn_len = 0; 00428 00429 bool modem_supports_ipv6 = get_modem_stack_type(IPV6_STACK); 00430 bool modem_supports_ipv4 = get_modem_stack_type(IPV4_STACK); 00431 00432 while (_at.info_resp()) { 00433 int cid = _at.read_int(); 00434 if (cid > cid_max) { 00435 cid_max = cid; 00436 } 00437 char pdp_type_from_context[10]; 00438 int pdp_type_len = _at.read_string(pdp_type_from_context, sizeof(pdp_type_from_context) - 1); 00439 if (pdp_type_len > 0) { 00440 apn_len = _at.read_string(apn, sizeof(apn) - 1); 00441 if (apn_len >= 0) { 00442 if (_apn && (strcmp(apn, _apn) != 0) ) { 00443 continue; 00444 } 00445 nsapi_ip_stack_t pdp_stack = string_to_stack_type(pdp_type_from_context); 00446 // Accept dual PDP context for IPv4/IPv6 only modems 00447 if (pdp_stack != DEFAULT_STACK && (get_modem_stack_type(pdp_stack) || pdp_stack == IPV4V6_STACK)) { 00448 if (_ip_stack_type_requested == IPV4_STACK) { 00449 if (pdp_stack == IPV4_STACK || pdp_stack == IPV4V6_STACK) { 00450 _ip_stack_type = _ip_stack_type_requested; 00451 _cid = cid; 00452 break; 00453 } 00454 } else if (_ip_stack_type_requested == IPV6_STACK) { 00455 if (pdp_stack == IPV6_STACK || pdp_stack == IPV4V6_STACK) { 00456 _ip_stack_type = _ip_stack_type_requested; 00457 _cid = cid; 00458 break; 00459 } 00460 } else { 00461 // If dual PDP need to check for IPV4 or IPV6 modem support. Prefer IPv6. 00462 if (pdp_stack == IPV4V6_STACK) { 00463 if (modem_supports_ipv6) { 00464 _ip_stack_type = IPV6_STACK; 00465 _cid = cid; 00466 break; 00467 } else if (modem_supports_ipv4) { 00468 _ip_stack_type = IPV4_STACK; 00469 _cid = cid; 00470 break; 00471 } 00472 // If PDP is IPV4 or IPV6 they are already checked if supported 00473 } else { 00474 _ip_stack_type = pdp_stack; 00475 _cid = cid; 00476 00477 if (pdp_stack == IPV6_STACK) { 00478 break; 00479 } 00480 if (pdp_stack == IPV4_STACK && !modem_supports_ipv6) { 00481 break; 00482 } 00483 } 00484 } 00485 } 00486 } 00487 } 00488 } 00489 _at.resp_stop(); 00490 if (_cid == -1) { // no suitable context was found so create a new one 00491 if (!set_new_context(cid_max+1)) { 00492 return false; 00493 } 00494 } 00495 00496 // save the apn 00497 if (apn_len > 0 && !_apn) { 00498 _apn = (char*)malloc(apn_len*sizeof(char)+1); 00499 if (_apn) { 00500 memcpy(_apn, apn, apn_len+1); 00501 } else { 00502 return false; 00503 } 00504 } 00505 00506 tr_debug("Context id %d", _cid); 00507 return true; 00508 } 00509 00510 nsapi_ip_stack_t AT_CellularNetwork::string_to_stack_type(const char* pdp_type) 00511 { 00512 nsapi_ip_stack_t stack = DEFAULT_STACK; 00513 int len = strlen(pdp_type); 00514 00515 if (len == 6 && memcmp(pdp_type, "IPV4V6", len) == 0) { 00516 stack = IPV4V6_STACK; 00517 } else if (len == 4 && memcmp(pdp_type, "IPV6", len) == 0) { 00518 stack = IPV6_STACK; 00519 } else if (len == 2 && memcmp(pdp_type, "IP", len) == 0) { 00520 stack = IPV4_STACK; 00521 } 00522 return stack; 00523 } 00524 00525 nsapi_error_t AT_CellularNetwork::set_registration_urc(bool urc_on) 00526 { 00527 for (unsigned int i = 0; i < sizeof(at_reg)/sizeof(at_reg[0]); i++) { 00528 if (has_registration(at_reg[i].type)) { 00529 _last_reg_type = at_reg[i].type; 00530 if (urc_on) { 00531 _at.cmd_start(at_reg[i].cmd); 00532 _at.write_string("=2", false); 00533 _at.cmd_stop(); 00534 } else { 00535 _at.cmd_start(at_reg[i].cmd); 00536 _at.write_string("=0", false); 00537 _at.cmd_stop(); 00538 } 00539 00540 _at.resp_start(); 00541 _at.resp_stop(); 00542 } 00543 } 00544 return _at.get_last_error(); 00545 } 00546 00547 nsapi_error_t AT_CellularNetwork::set_registration(const char *plmn) 00548 { 00549 _at.lock(); 00550 00551 nsapi_error_t ret = set_registration_urc(false); 00552 if (ret) { 00553 tr_error("Setting registration URC failed!"); 00554 _at.clear_error(); // allow temporary failures here 00555 } 00556 00557 if (!plmn) { 00558 tr_debug("Automatic network registration"); 00559 _at.cmd_start("AT+COPS?"); 00560 _at.cmd_stop(); 00561 _at.resp_start("AT+COPS:"); 00562 int mode = _at.read_int(); 00563 _at.resp_stop(); 00564 if (mode != 0) { 00565 _at.clear_error(); 00566 _at.cmd_start("AT+COPS=0"); 00567 _at.cmd_stop(); 00568 _at.resp_start(); 00569 _at.resp_stop(); 00570 } 00571 } else { 00572 tr_debug("Manual network registration to %s", plmn); 00573 _at.cmd_start("AT+COPS=4,2,"); 00574 _at.write_string(plmn); 00575 _at.cmd_stop(); 00576 _at.resp_start(); 00577 _at.resp_stop(); 00578 } 00579 00580 return _at.unlock_return_error(); 00581 } 00582 00583 nsapi_error_t AT_CellularNetwork::get_registration_status(RegistrationType type, RegistrationStatus &status) 00584 { 00585 int i = (int)type; 00586 MBED_ASSERT(i >= 0 && i < C_MAX); 00587 00588 const char *rsp[] = { "+CEREG:", "+CGREG:", "+CREG:"}; 00589 00590 const int LAC_LENGTH = 5, CELL_ID_LENGTH = 9; 00591 char lac_string[LAC_LENGTH] = {0}, cell_id_string[CELL_ID_LENGTH] = {0}; 00592 bool lac_read = false, cell_id_read = false; 00593 00594 _cell_id = -1; 00595 _lac = -1; 00596 00597 _at.lock(); 00598 00599 if (!has_registration(at_reg[i].type)) { 00600 _at.unlock(); 00601 return NSAPI_ERROR_UNSUPPORTED ; 00602 } 00603 00604 _at.cmd_start(at_reg[i].cmd); 00605 _at.write_string("=2", false); 00606 _at.cmd_stop(); 00607 _at.resp_start(); 00608 _at.resp_stop(); 00609 00610 _at.cmd_start(at_reg[i].cmd); 00611 _at.write_string("?", false); 00612 _at.cmd_stop(); 00613 00614 _at.resp_start(rsp[i]); 00615 _at.read_int(); // ignore urc mode subparam 00616 status = (RegistrationStatus)_at.read_int(); 00617 00618 int len = _at.read_string(lac_string, LAC_LENGTH); 00619 if (memcmp(lac_string, "ffff", LAC_LENGTH-1) && len >= 0) { 00620 lac_read = true; 00621 } 00622 00623 len = _at.read_string(cell_id_string, CELL_ID_LENGTH); 00624 if (memcmp(cell_id_string, "ffffffff", CELL_ID_LENGTH-1) && len >= 0) { 00625 cell_id_read = true; 00626 } 00627 00628 _at.resp_stop(); 00629 00630 _at.cmd_start(at_reg[i].cmd); 00631 _at.write_string("=0", false); 00632 _at.cmd_stop(); 00633 _at.resp_start(); 00634 _at.resp_stop(); 00635 nsapi_error_t ret = _at.get_last_error(); 00636 _at.unlock(); 00637 00638 if (lac_read) { 00639 _lac = hex_str_to_int(lac_string, LAC_LENGTH); 00640 tr_debug("lac %s %d", lac_string, _lac ); 00641 } 00642 00643 if (cell_id_read) { 00644 _cell_id = hex_str_to_int(cell_id_string, CELL_ID_LENGTH); 00645 tr_debug("cell_id %s %d", cell_id_string, _cell_id ); 00646 } 00647 00648 return ret; 00649 } 00650 00651 nsapi_error_t AT_CellularNetwork::get_cell_id(int &cell_id) 00652 { 00653 RegistrationStatus tmp; 00654 00655 nsapi_error_t error = get_registration_status(_last_reg_type, tmp); 00656 00657 cell_id = _cell_id; 00658 00659 return error; 00660 } 00661 00662 bool AT_CellularNetwork::has_registration(RegistrationType reg_type) 00663 { 00664 (void)reg_type; 00665 return true; 00666 } 00667 00668 nsapi_error_t AT_CellularNetwork::set_attach(int timeout) 00669 { 00670 _at.lock(); 00671 00672 _at.cmd_start("AT+CGATT?"); 00673 _at.cmd_stop(); 00674 _at.resp_start("+CGATT:"); 00675 int attached_state = _at.read_int(); 00676 _at.resp_stop(); 00677 if (attached_state != 1) { 00678 tr_debug("Network attach"); 00679 _at.cmd_start("AT+CGATT=1"); 00680 _at.cmd_stop(); 00681 _at.resp_start(); 00682 _at.resp_stop(); 00683 } 00684 00685 return _at.unlock_return_error(); 00686 } 00687 00688 nsapi_error_t AT_CellularNetwork::get_attach(AttachStatus &status) 00689 { 00690 _at.lock(); 00691 00692 _at.cmd_start("AT+CGATT?"); 00693 _at.cmd_stop(); 00694 00695 _at.resp_start("+CGATT:"); 00696 if (_at.info_resp()) { 00697 int attach_status = _at.read_int(); 00698 status = (attach_status == 1) ? Attached : Detached; 00699 } 00700 _at.resp_stop(); 00701 00702 return _at.unlock_return_error(); 00703 } 00704 00705 00706 nsapi_error_t AT_CellularNetwork::get_apn_backoff_timer(int &backoff_timer) 00707 { 00708 _at.lock(); 00709 00710 // If apn is set 00711 if (_apn) { 00712 _at.cmd_start("AT+CABTRDP="); 00713 _at.write_string(_apn); 00714 _at.cmd_stop(); 00715 _at.resp_start("+CABTRDP:"); 00716 if (_at.info_resp()) { 00717 _at.skip_param(); 00718 backoff_timer = _at.read_int(); 00719 } 00720 _at.resp_stop(); 00721 } 00722 00723 return _at.unlock_return_error(); 00724 } 00725 00726 NetworkStack *AT_CellularNetwork::get_stack() 00727 { 00728 // use lwIP/PPP if modem does not have IP stack 00729 #if NSAPI_PPP_AVAILABLE 00730 _stack = nsapi_ppp_get_stack(); 00731 #else 00732 _stack = NULL; 00733 #endif 00734 return _stack; 00735 } 00736 00737 const char *AT_CellularNetwork::get_ip_address() 00738 { 00739 #if NSAPI_PPP_AVAILABLE 00740 return nsapi_ppp_get_ip_addr(_at.get_file_handle()); 00741 #else 00742 if (!_stack) { 00743 _stack = get_stack(); 00744 } 00745 if (_stack) { 00746 return _stack->get_ip_address(); 00747 } 00748 return NULL; 00749 #endif 00750 } 00751 00752 nsapi_error_t AT_CellularNetwork::set_stack_type(nsapi_ip_stack_t stack_type) 00753 { 00754 00755 if (get_modem_stack_type(stack_type)) { 00756 _ip_stack_type_requested = stack_type; 00757 return NSAPI_ERROR_OK ; 00758 } else { 00759 return NSAPI_ERROR_PARAMETER ; 00760 } 00761 00762 } 00763 00764 nsapi_ip_stack_t AT_CellularNetwork::get_stack_type() 00765 { 00766 return _ip_stack_type; 00767 } 00768 00769 bool AT_CellularNetwork::get_modem_stack_type(nsapi_ip_stack_t requested_stack) 00770 { 00771 if (requested_stack == _ip_stack_type) { 00772 return true; 00773 } else { 00774 return false; 00775 } 00776 } 00777 00778 nsapi_error_t AT_CellularNetwork::set_access_technology_impl(operator_t::RadioAccessTechnology opsAct) 00779 { 00780 return NSAPI_ERROR_UNSUPPORTED ; 00781 } 00782 00783 nsapi_error_t AT_CellularNetwork::set_access_technology(operator_t::RadioAccessTechnology opAct) 00784 { 00785 if (opAct == operator_t::RAT_UNKNOWN) { 00786 return NSAPI_ERROR_UNSUPPORTED ; 00787 } 00788 00789 _op_act = opAct; 00790 00791 return set_access_technology_impl(opAct); 00792 } 00793 00794 nsapi_error_t AT_CellularNetwork::scan_plmn(operList_t &operators, int &opsCount) 00795 { 00796 int idx = 0; 00797 00798 _at.lock(); 00799 00800 _at.cmd_start("AT+COPS=?"); 00801 _at.cmd_stop(); 00802 00803 _at.resp_start("+COPS:"); 00804 00805 int ret, error_code = -1; 00806 operator_t *op = NULL; 00807 00808 while (_at.info_elem('(')) { 00809 00810 op = operators.add_new(); 00811 00812 op->op_status = (operator_t::Status)_at.read_int(); 00813 _at.read_string(op->op_long, sizeof(op->op_long)); 00814 _at.read_string(op->op_short, sizeof(op->op_short)); 00815 _at.read_string(op->op_num, sizeof(op->op_num)); 00816 00817 // Optional - try read an int 00818 ret = _at.read_int(); 00819 op->op_rat = (ret == error_code) ? operator_t::RAT_UNKNOWN:(operator_t::RadioAccessTechnology)ret; 00820 00821 if ((_op_act == operator_t::RAT_UNKNOWN) || 00822 ((op->op_rat != operator_t::RAT_UNKNOWN) && (op->op_rat == _op_act))) { 00823 idx++; 00824 } else { 00825 operators.delete_last(); 00826 } 00827 } 00828 00829 _at.resp_stop(); 00830 00831 opsCount = idx; 00832 00833 return _at.unlock_return_error(); 00834 } 00835 00836 nsapi_error_t AT_CellularNetwork::set_ciot_optimization_config(Supported_UE_Opt supported_opt, 00837 Preferred_UE_Opt preferred_opt) 00838 { 00839 _at.lock(); 00840 00841 _at.cmd_start("AT+CCIOTOPT="); 00842 _at.write_int(_cid); 00843 _at.write_int(supported_opt); 00844 _at.write_int(preferred_opt); 00845 _at.cmd_stop(); 00846 00847 _at.resp_start(); 00848 _at.resp_stop(); 00849 00850 return _at.unlock_return_error(); 00851 } 00852 00853 nsapi_error_t AT_CellularNetwork::get_ciot_optimization_config(Supported_UE_Opt& supported_opt, 00854 Preferred_UE_Opt& preferred_opt) 00855 { 00856 _at.lock(); 00857 00858 _at.cmd_start("AT+CCIOTOPT?"); 00859 _at.cmd_stop(); 00860 00861 _at.resp_start("+CCIOTOPT:"); 00862 _at.read_int(); 00863 if (_at.get_last_error() == NSAPI_ERROR_OK ) { 00864 supported_opt = (Supported_UE_Opt)_at.read_int(); 00865 preferred_opt = (Preferred_UE_Opt)_at.read_int(); 00866 } 00867 00868 _at.resp_stop(); 00869 00870 return _at.unlock_return_error(); 00871 } 00872 00873 nsapi_error_t AT_CellularNetwork::get_rate_control( 00874 CellularNetwork::RateControlExceptionReports &reports, 00875 CellularNetwork::RateControlUplinkTimeUnit &timeUnit, int &uplinkRate) 00876 { 00877 00878 _at.lock(); 00879 00880 _at.cmd_start("AT+CGAPNRC="); 00881 _at.write_int(_cid); 00882 _at.cmd_stop(); 00883 00884 _at.resp_start("+CGAPNRC:"); 00885 _at.read_int(); 00886 if (_at.get_last_error() == NSAPI_ERROR_OK ) { 00887 bool comma_found = true; 00888 int next_element = _at.read_int(); 00889 if (next_element >= 0) { 00890 reports = (RateControlExceptionReports)next_element; 00891 tr_debug("reports %d",reports); 00892 next_element = _at.read_int(); 00893 } else { 00894 comma_found = false; 00895 } 00896 00897 if (comma_found && next_element >= 0) { 00898 timeUnit = (RateControlUplinkTimeUnit)next_element; 00899 tr_debug("time %d",timeUnit); 00900 next_element = _at.read_int(); 00901 } else { 00902 comma_found = false; 00903 } 00904 00905 if (comma_found && next_element >= 0) { 00906 uplinkRate = next_element; 00907 tr_debug("rate %d",uplinkRate); 00908 } 00909 } 00910 _at.resp_stop(); 00911 nsapi_error_t ret = _at.get_last_error(); 00912 _at.unlock(); 00913 00914 return (ret == NSAPI_ERROR_OK ) ? NSAPI_ERROR_OK : NSAPI_ERROR_PARAMETER ; 00915 } 00916 00917 nsapi_error_t AT_CellularNetwork::get_pdpcontext_params(pdpContextList_t& params_list) 00918 { 00919 const int ipv6_subnet_size = 128; 00920 const int max_ipv6_size = 64; 00921 char* ipv6_and_subnetmask = (char*)malloc(ipv6_subnet_size); 00922 if (!ipv6_and_subnetmask) { 00923 return NSAPI_ERROR_NO_MEMORY ; 00924 } 00925 00926 char* temp = (char*)malloc(max_ipv6_size); 00927 if (!temp) { 00928 free(ipv6_and_subnetmask); 00929 return NSAPI_ERROR_NO_MEMORY ; 00930 } 00931 00932 _at.lock(); 00933 00934 _at.cmd_start("AT+CGCONTRDP="); 00935 _at.write_int(_cid); 00936 _at.cmd_stop(); 00937 00938 _at.resp_start("+CGCONTRDP:"); 00939 pdpcontext_params_t *params = NULL; 00940 while (_at.info_resp()) { // response can be zero or many +CGDCONT lines 00941 params = params_list.add_new(); 00942 if (!params) { 00943 tr_warn("Could not allocate new pdpcontext_params_t"); 00944 params_list.delete_all(); 00945 _at.resp_stop(); 00946 free(temp); 00947 free(ipv6_and_subnetmask); 00948 return NSAPI_ERROR_NO_MEMORY ; 00949 } 00950 00951 params->cid = _at.read_int(); 00952 params->bearer_id = _at.read_int(); 00953 _at.read_string(params->apn, sizeof(params->apn)); 00954 00955 // rest are optional params 00956 ipv6_and_subnetmask[0] = '\0'; 00957 temp[0] = '\0'; 00958 _at.read_string(ipv6_and_subnetmask, ipv6_subnet_size); 00959 separate_ip_addresses(ipv6_and_subnetmask, params->local_addr, sizeof(params->local_addr), params->local_subnet_mask, sizeof(params->local_subnet_mask)); 00960 ipv6_and_subnetmask[0] = '\0'; 00961 00962 _at.read_string(ipv6_and_subnetmask, ipv6_subnet_size); 00963 separate_ip_addresses(ipv6_and_subnetmask, params->gateway_addr, sizeof(params->gateway_addr), temp, max_ipv6_size); 00964 prefer_ipv6(params->gateway_addr, sizeof(params->gateway_addr), temp, max_ipv6_size); 00965 ipv6_and_subnetmask[0] = '\0'; 00966 temp[0] = '\0'; 00967 00968 _at.read_string(ipv6_and_subnetmask, ipv6_subnet_size); 00969 separate_ip_addresses(ipv6_and_subnetmask, params->dns_primary_addr, sizeof(params->dns_primary_addr), temp, max_ipv6_size); 00970 prefer_ipv6(params->dns_primary_addr, sizeof(params->dns_primary_addr), temp, max_ipv6_size); 00971 ipv6_and_subnetmask[0] = '\0'; 00972 temp[0] = '\0'; 00973 00974 _at.read_string(ipv6_and_subnetmask, ipv6_subnet_size); 00975 separate_ip_addresses(ipv6_and_subnetmask, params->dns_secondary_addr, sizeof(params->dns_secondary_addr), temp, max_ipv6_size); 00976 prefer_ipv6(params->dns_secondary_addr, sizeof(params->dns_secondary_addr), temp, max_ipv6_size); 00977 ipv6_and_subnetmask[0] = '\0'; 00978 temp[0] = '\0'; 00979 00980 _at.read_string(ipv6_and_subnetmask, ipv6_subnet_size); 00981 separate_ip_addresses(ipv6_and_subnetmask, params->p_cscf_prim_addr, sizeof(params->p_cscf_prim_addr), temp, max_ipv6_size); 00982 prefer_ipv6(params->p_cscf_prim_addr, sizeof(params->p_cscf_prim_addr), temp, max_ipv6_size); 00983 ipv6_and_subnetmask[0] = '\0'; 00984 temp[0] = '\0'; 00985 00986 _at.read_string(ipv6_and_subnetmask, ipv6_subnet_size); 00987 separate_ip_addresses(ipv6_and_subnetmask, params->p_cscf_sec_addr, sizeof(params->p_cscf_sec_addr), temp, max_ipv6_size); 00988 prefer_ipv6(params->p_cscf_sec_addr, sizeof(params->p_cscf_sec_addr), temp, max_ipv6_size); 00989 00990 params->im_signalling_flag = _at.read_int(); 00991 params->lipa_indication = _at.read_int(); 00992 params->ipv4_mtu = _at.read_int(); 00993 params->wlan_offload = _at.read_int(); 00994 params->local_addr_ind = _at.read_int(); 00995 params->non_ip_mtu = _at.read_int(); 00996 params->serving_plmn_rate_control_value = _at.read_int(); 00997 } 00998 _at.resp_stop(); 00999 01000 free(temp); 01001 free(ipv6_and_subnetmask); 01002 01003 return _at.unlock_return_error(); 01004 } 01005 01006 nsapi_error_t AT_CellularNetwork::get_extended_signal_quality(int &rxlev, int &ber, int &rscp, int &ecno, int &rsrq, int &rsrp) 01007 { 01008 _at.lock(); 01009 01010 _at.cmd_start("AT+CESQ"); 01011 _at.cmd_stop(); 01012 01013 _at.resp_start("+CESQ:"); 01014 rxlev = _at.read_int(); 01015 ber = _at.read_int(); 01016 rscp = _at.read_int(); 01017 ecno = _at.read_int(); 01018 rsrq = _at.read_int(); 01019 rsrp = _at.read_int(); 01020 _at.resp_stop(); 01021 if (rxlev < 0 || ber < 0 || rscp < 0 || ecno < 0 || rsrq < 0 || rsrp < 0) { 01022 _at.unlock(); 01023 return NSAPI_ERROR_DEVICE_ERROR ; 01024 } 01025 01026 return _at.unlock_return_error(); 01027 } 01028 01029 nsapi_error_t AT_CellularNetwork::get_signal_quality(int &rssi, int &ber) 01030 { 01031 _at.lock(); 01032 01033 _at.cmd_start("AT+CSQ"); 01034 _at.cmd_stop(); 01035 01036 _at.resp_start("+CSQ:"); 01037 rssi = _at.read_int(); 01038 ber = _at.read_int(); 01039 _at.resp_stop(); 01040 if (rssi < 0 || ber < 0) { 01041 _at.unlock(); 01042 return NSAPI_ERROR_DEVICE_ERROR ; 01043 } 01044 01045 return _at.unlock_return_error(); 01046 } 01047 01048 /** Get the last 3GPP error code 01049 * @return see 3GPP TS 27.007 error codes 01050 */ 01051 int AT_CellularNetwork::get_3gpp_error() 01052 { 01053 return _at.get_3gpp_error(); 01054 } 01055 01056 01057 nsapi_error_t AT_CellularNetwork::get_operator_params(int &format, operator_t &operator_params) 01058 { 01059 _at.lock(); 01060 01061 _at.cmd_start("AT+COPS?"); 01062 _at.cmd_stop(); 01063 01064 _at.resp_start("+COPS:"); 01065 _at.read_int(); //ignore mode 01066 format = _at.read_int(); 01067 01068 if (_at.get_last_error() == NSAPI_ERROR_OK ) { 01069 01070 switch (format) { 01071 case 0: 01072 _at.read_string(operator_params.op_long, sizeof(operator_params.op_long)); 01073 break; 01074 01075 case 1: 01076 _at.read_string(operator_params.op_short, sizeof(operator_params.op_short)); 01077 break; 01078 01079 default: 01080 _at.read_string(operator_params.op_num, sizeof(operator_params.op_num)); 01081 break; 01082 } 01083 01084 operator_params.op_rat = (operator_t::RadioAccessTechnology)_at.read_int(); 01085 } 01086 01087 _at.resp_stop(); 01088 01089 return _at.unlock_return_error(); 01090 }
Generated on Tue Jul 12 2022 14:23:20 by
