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