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.
Fork of OmniWheels by
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 Fri Jul 22 2022 04:53:45 by
