takashi kadono / Mbed OS Nucleo_446

Dependencies:   ssd1331

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