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