Knight KE / Mbed OS Game_Master
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_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 &reg_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 &params_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 }