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