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