Daniel Vizcaya / Mbed OS 04_RTOS_Embebidos
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 #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 &reg_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 }