Gleb Klochkov / Mbed OS Climatcontroll_Main

Dependencies:   esp8266-driver

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers AT_CellularNetwork.cpp Source File

AT_CellularNetwork.cpp

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