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