Denislam Valeev / Mbed OS Nucleo_rtos_basic
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers AT_CellularNetwork.cpp Source File

AT_CellularNetwork.cpp

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