Base class for the ublox-xxx-cellular-xxx classes. Cannot be used standalone, only inherited by classes that do properly useful stuff. Or, to put it another way, if you are using any of the ublox-xxx-cellular-xxx classes, you will need this class also.

Dependents:   example-ublox-cellular-interface example-ublox-cellular-driver-gen HelloMQTT example-ublox-cellular-interface_r410M ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers UbloxCellularBase.cpp Source File

UbloxCellularBase.cpp

00001 /* Copyright (c) 2019 ublox Limited
00002  *
00003  * Licensed under the Apache License, Version 2.0 (the "License");
00004  * you may not use this file except in compliance with the License.
00005  * You may obtain a copy of the License at
00006  *
00007  *     http://www.apache.org/licenses/LICENSE-2.0
00008  *
00009  * Unless required by applicable law or agreed to in writing, software
00010  * distributed under the License is distributed on an "AS IS" BASIS,
00011  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012  * See the License for the specific language governing permissions and
00013  * limitations under the License.
00014  */
00015 
00016 #include "UARTSerial.h"
00017 #include "APN_db.h"
00018 #include "UbloxCellularBase.h"
00019 #include "onboard_modem_api.h"
00020 #ifdef FEATURE_COMMON_PAL
00021 #include "mbed_trace.h"
00022 #define TRACE_GROUP "UCB"
00023 #else
00024 #define tr_debug(format, ...) debug_if(_debug_trace_on, format "\n", ## __VA_ARGS__)
00025 #define tr_info(format, ...)  debug_if(_debug_trace_on, format "\n", ## __VA_ARGS__)
00026 #define tr_warn(format, ...)  debug_if(_debug_trace_on, format "\n", ## __VA_ARGS__)
00027 #define tr_error(format, ...) debug_if(_debug_trace_on, format "\n", ## __VA_ARGS__)
00028 #define tr_critical(format, ...) debug("\n" format "\n", ## __VA_ARGS__)
00029 #endif
00030 
00031 /* Array to convert the 3G qual number into a median EC_NO_LEV number.
00032  */
00033                             /* 0   1   2   3   4   5   6  7 */
00034                             /* 44, 41, 35, 29, 23, 17, 11, 7*/
00035 const int qualConvert3G[] = {-2, -4, -7, -10, -13, -16, -19, -21};
00036 
00037 /* Array to convert the 3G "rssi" number into a dBm RSCP value rounded up to the
00038  * nearest whole number.
00039  */
00040 const int rscpConvert3G[] = {-108, -105, -103, -100,  -98,  -96,  -94,  -93,   /* 0 - 7 */
00041                               -91,  -89,  -88,  -85,  -83,  -80,  -78,  -76,   /* 8 - 15 */
00042                               -74,  -73,  -70,  -68,  -66,  -64,  -63,  -60,   /* 16 - 23 */
00043                               -58,  -56,  -54,  -53,  -51,  -49,  -48,  -46};  /* 24 - 31 */
00044 
00045 /* Array to convert the LTE rssi number into a dBm value rounded up to the
00046  * nearest whole number.
00047  */
00048 const int rssiConvertLte[] = {-118, -115, -113, -110, -108, -105, -103, -100,   /* 0 - 7 */
00049                                -98,  -95,  -93,  -90,  -88,  -85,  -83,  -80,   /* 8 - 15 */
00050                                -78,  -76,  -74,  -73,  -71,  -69,  -68,  -65,   /* 16 - 23 */
00051                                -63,  -61,  -60,  -59,  -58,  -55,  -53,  -48};  /* 24 - 31 */
00052 
00053 /**********************************************************************
00054  * PRIVATE METHODS
00055  **********************************************************************/
00056 
00057 void UbloxCellularBase::set_nwk_reg_status_csd(int status)
00058 {
00059     switch (status) {
00060         case CSD_NOT_REGISTERED_NOT_SEARCHING:
00061         case CSD_NOT_REGISTERED_SEARCHING:
00062             tr_info("Not (yet) registered for circuit switched service");
00063             break;
00064         case CSD_REGISTERED:
00065         case CSD_REGISTERED_ROAMING:
00066             tr_info("Registered for circuit switched service");
00067             break;
00068         case CSD_REGISTRATION_DENIED:
00069             tr_info("Circuit switched service denied");
00070             break;
00071         case CSD_UNKNOWN_COVERAGE:
00072             tr_info("Out of circuit switched service coverage");
00073             break;
00074         case CSD_SMS_ONLY:
00075             tr_info("SMS service only");
00076             break;
00077         case CSD_SMS_ONLY_ROAMING:
00078             tr_info("SMS service only");
00079             break;
00080         case CSD_CSFB_NOT_PREFERRED:
00081             tr_info("Registered for circuit switched service with CSFB not preferred");
00082             break;
00083         default:
00084             tr_info("Unknown circuit switched service registration status. %d", status);
00085             break;
00086     }
00087 
00088     _dev_info.reg_status_csd = static_cast<NetworkRegistrationStatusCsd>(status);
00089 }
00090 
00091 void UbloxCellularBase::set_nwk_reg_status_psd(int status)
00092 {
00093     switch (status) {
00094         case PSD_NOT_REGISTERED_NOT_SEARCHING:
00095         case PSD_NOT_REGISTERED_SEARCHING:
00096             tr_info("Not (yet) registered for packet switched service");
00097             break;
00098         case PSD_REGISTERED:
00099         case PSD_REGISTERED_ROAMING:
00100             tr_info("Registered for packet switched service");
00101             break;
00102         case PSD_REGISTRATION_DENIED:
00103             tr_info("Packet switched service denied");
00104             break;
00105         case PSD_UNKNOWN_COVERAGE:
00106             tr_info("Out of packet switched service coverage");
00107             break;
00108         case PSD_EMERGENCY_SERVICES_ONLY:
00109             tr_info("Limited access for packet switched service. Emergency use only.");
00110             break;
00111         default:
00112             tr_info("Unknown packet switched service registration status. %d", status);
00113             break;
00114     }
00115 
00116     _dev_info.reg_status_psd = static_cast<NetworkRegistrationStatusPsd>(status);
00117 }
00118 
00119 void UbloxCellularBase::set_nwk_reg_status_eps(int status)
00120 {
00121     switch (status) {
00122         case EPS_NOT_REGISTERED_NOT_SEARCHING:
00123         case EPS_NOT_REGISTERED_SEARCHING:
00124             tr_info("Not (yet) registered for EPS service");
00125             break;
00126         case EPS_REGISTERED:
00127         case EPS_REGISTERED_ROAMING:
00128             tr_info("Registered for EPS service");
00129             break;
00130         case EPS_REGISTRATION_DENIED:
00131             tr_info("EPS service denied");
00132             break;
00133         case EPS_UNKNOWN_COVERAGE:
00134             tr_info("Out of EPS service coverage");
00135             break;
00136         case EPS_EMERGENCY_SERVICES_ONLY:
00137             tr_info("Limited access for EPS service. Emergency use only.");
00138             break;
00139         default:
00140             tr_info("Unknown EPS service registration status. %d", status);
00141             break;
00142     }
00143 
00144     _dev_info.reg_status_eps = static_cast<NetworkRegistrationStatusEps>(status);
00145 }
00146 
00147 #ifdef TARGET_UBLOX_C030_R412M
00148 void UbloxCellularBase::set_modem_psm_state(int status)
00149 {
00150     switch (status) {
00151         case ASLEEP:
00152             tr_info("Modem is going in PSM sleep");
00153             break;
00154         case AWAKE:
00155             tr_info("Modem is awake from PSM sleep");
00156             break;
00157         default:
00158             tr_info("Unknown PSM state. %d", status);
00159             break;
00160     }
00161 
00162     _dev_info.modem_psm_state = static_cast<ModemPSMState>(status);
00163 }
00164 #endif
00165 
00166 void UbloxCellularBase::set_rat(int acTStatus)
00167 {
00168     switch (acTStatus) {
00169         case GSM:
00170         case COMPACT_GSM:
00171             tr_info("Connected in GSM");
00172             break;
00173         case UTRAN:
00174             tr_info("Connected to UTRAN");
00175             break;
00176         case EDGE:
00177             tr_info("Connected to EDGE");
00178             break;
00179         case HSDPA:
00180             tr_info("Connected to HSDPA");
00181             break;
00182         case HSUPA:
00183             tr_info("Connected to HSPA");
00184             break;
00185         case HSDPA_HSUPA:
00186             tr_info("Connected to HDPA/HSPA");
00187             break;
00188         case LTE:
00189             tr_info("Connected to LTE");
00190             break;
00191         case EC_GSM_IoT:
00192             tr_info("Connected to EC_GSM_IoT");
00193             break;
00194         case E_UTRAN_NB_S1:
00195             tr_info("Connected to E_UTRAN NB1");
00196             break;
00197         default:
00198             tr_info("Unknown RAT %d", acTStatus);
00199             break;
00200     }
00201 
00202     _dev_info.rat = static_cast<RadioAccessNetworkType>(acTStatus);
00203 }
00204 
00205 bool UbloxCellularBase::get_iccid()
00206 {
00207     bool success;
00208     LOCK();
00209 
00210     MBED_ASSERT(_at != NULL);
00211 
00212     // Returns the ICCID (Integrated Circuit Card ID) of the SIM-card.
00213     // ICCID is a serial number identifying the SIM.
00214     // AT Command Manual UBX-13002752, section 4.12
00215     success = _at->send("AT+CCID") && _at->recv("+CCID: %20[^\n]\nOK\n", _dev_info.iccid);
00216     tr_info("DevInfo: ICCID=%s", _dev_info.iccid);
00217 
00218     UNLOCK();
00219     return success;
00220 }
00221 
00222 bool UbloxCellularBase::get_imsi()
00223 {
00224     bool success;
00225     LOCK();
00226 
00227     MBED_ASSERT(_at != NULL);
00228 
00229     // International mobile subscriber identification
00230     // AT Command Manual UBX-13002752, section 4.11
00231     success = _at->send("AT+CIMI") && _at->recv("%15[^\n]\nOK\n", _dev_info.imsi);
00232     tr_info("DevInfo: IMSI=%s", _dev_info.imsi);
00233 
00234     UNLOCK();
00235     return success;
00236 }
00237 
00238 bool UbloxCellularBase::get_imei()
00239 {
00240     bool success;
00241     LOCK();
00242 
00243     MBED_ASSERT(_at != NULL);
00244 
00245     // International mobile equipment identifier
00246     // AT Command Manual UBX-13002752, section 4.7
00247     success = _at->send("AT+CGSN") && _at->recv("%15[^\n]\nOK\n", _dev_info.imei);
00248     tr_info("DevInfo: IMEI=%s", _dev_info.imei);
00249 
00250     UNLOCK();
00251     return success;
00252 }
00253 
00254 bool UbloxCellularBase::get_meid()
00255 {
00256     bool success;
00257     LOCK();
00258 
00259     MBED_ASSERT(_at != NULL);
00260 
00261     // Mobile equipment identifier
00262     // AT Command Manual UBX-13002752, section 4.8
00263     success = _at->send("AT+GSN") && _at->recv("%18[^\n]\nOK\n", _dev_info.meid);
00264     tr_info("DevInfo: MEID=%s", _dev_info.meid);
00265 
00266     UNLOCK();
00267     return success;
00268 }
00269 
00270 bool UbloxCellularBase::set_sms()
00271 {
00272     bool success = false;
00273     char buf[32];
00274     LOCK();
00275 
00276     MBED_ASSERT(_at != NULL);
00277 
00278     // Set up SMS format and enable URC
00279     // AT Command Manual UBX-13002752, section 11
00280     if (_at->send("AT+CMGF=1") && _at->recv("OK")) {
00281         tr_debug("SMS in text mode");
00282         if (_at->send("AT+CNMI=2,1") && _at->recv("OK")) {
00283             tr_debug("SMS URC enabled");
00284             // Set to CS preferred since PS preferred doesn't work
00285             // on some networks
00286             if (_at->send("AT+CGSMS=1") && _at->recv("OK")) {
00287                 tr_debug("SMS set to CS preferred");
00288                 success = true;
00289                 memset (buf, 0, sizeof (buf));
00290                 if (_at->send("AT+CSCA?") &&
00291                     _at->recv("+CSCA: \"%31[^\"]\"", buf) &&
00292                     _at->recv("OK")) {
00293                     tr_info("SMS Service Centre address is \"%s\"", buf);
00294                 }
00295             }
00296         }
00297     }
00298 
00299     UNLOCK();
00300     return success;
00301 }
00302 
00303 void UbloxCellularBase::parser_abort_cb()
00304 {
00305     _at->abort();
00306 }
00307 
00308 // Callback for CME ERROR and CMS ERROR.
00309 void UbloxCellularBase::CMX_ERROR_URC()
00310 {
00311     char buf[48];
00312 
00313     if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
00314         tr_debug("AT error %s", buf);
00315     }
00316     parser_abort_cb();
00317 }
00318 
00319 // Callback for circuit switched registration URC.
00320 void UbloxCellularBase::CREG_URC()
00321 {
00322     char buf[10];
00323     int status;
00324     int acTStatus;
00325 
00326     // If this is the URC it will be a single
00327     // digit followed by \n.  If it is the
00328     // answer to a CREG query, it will be
00329     // a ": %d,%d\n" where the second digit
00330     // indicates the status
00331     // Note: not calling _at->recv() from here as we're
00332     // already in an _at->recv()
00333     if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
00334         if (sscanf(buf, ": %*d,%d,%*d,%*d,%d,", &status, &acTStatus) == 2) {
00335             set_nwk_reg_status_csd(status);
00336             set_rat(acTStatus);
00337         } else if (sscanf(buf, ": %*d,%d", &status) == 1) {
00338             set_nwk_reg_status_csd(status);
00339         } else if (sscanf(buf, ": %d", &status) == 1) {
00340             set_nwk_reg_status_csd(status);
00341         }
00342     }
00343 }
00344 
00345 // Callback for packet switched registration URC.
00346 void UbloxCellularBase::CGREG_URC()
00347 {
00348     char buf[10];
00349     int status;
00350     int acTStatus;
00351 
00352     // If this is the URC it will be a single
00353     // digit followed by \n.  If it is the
00354     // answer to a CGREG query, it will be
00355     // a ": %d,%d\n" where the second digit
00356     // indicates the status
00357     // Note: not calling _at->recv() from here as we're
00358     // already in an _at->recv()
00359     if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
00360         if (sscanf(buf, ": %*d,%d,%*d,%*d,%d,", &status, &acTStatus) == 2) {
00361             set_nwk_reg_status_csd(status);
00362             set_rat(acTStatus);
00363         } else if (sscanf(buf, ": %*d,%d", &status) == 1) {
00364             set_nwk_reg_status_psd(status);
00365         } else if (sscanf(buf, ": %d", &status) == 1) {
00366             set_nwk_reg_status_psd(status);
00367         }
00368     }
00369 }
00370 
00371 // Callback for EPS registration URC.
00372 void UbloxCellularBase::CEREG_URC()
00373 {
00374     char buf[10];
00375     int status;
00376     int acTStatus;
00377 
00378     // If this is the URC it will be a single
00379     // digit followed by \n.  If it is the
00380     // answer to a CEREG query, it will be
00381     // a ": %d,%d\n" where the second digit
00382     // indicates the status
00383     // Note: not calling _at->recv() from here as we're
00384     // already in an _at->recv()
00385     if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
00386         if (sscanf(buf, ": %*d,%d,%*d,%*d,%d,", &status, &acTStatus) == 2) {
00387             set_nwk_reg_status_csd(status);
00388             set_rat(acTStatus);
00389         } else if (sscanf(buf, ": %*d,%d", &status) == 1) {
00390             set_nwk_reg_status_eps(status);
00391         } else if (sscanf(buf, ": %d", &status) == 1) {
00392             set_nwk_reg_status_eps(status);
00393         }
00394     }
00395 }
00396 
00397 // Callback UMWI, just filtering it out.
00398 void UbloxCellularBase::UMWI_URC()
00399 {
00400     char buf[10];
00401 
00402     // Note: not calling _at->recv() from here as we're
00403     // already in an _at->recv()
00404     read_at_to_char(buf, sizeof (buf), '\n');
00405 }
00406 
00407 #ifdef TARGET_UBLOX_C030_R412M
00408 // Callback UUPSMR, set/clear flag for modem psm state.
00409 void UbloxCellularBase::UUPSMR_URC()
00410 {
00411     int status;
00412     char buf[10];
00413 
00414     if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
00415         if (sscanf(buf, ": %d", &status) == 1) {
00416             set_modem_psm_state(status);
00417             //call application registered callbacks
00418             if (status == AWAKE) { //modem coming out of sleep
00419                 if (_func_psm_coming_out) {
00420                     _func_psm_coming_out(_cb_param_psm_coming_out);
00421                 }
00422             } else if(status == ASLEEP) { //modem going into sleep
00423                 if (_func_psm_going_in) {
00424                     _func_psm_going_in(_cb_param_psm_going_in);
00425                 }
00426             }
00427         }
00428     }
00429 }
00430 #endif
00431 /**********************************************************************
00432  * PROTECTED METHODS
00433  **********************************************************************/
00434 
00435 #if MODEM_ON_BOARD
00436 void UbloxCellularBase::modem_init()
00437 {
00438     ::onboard_modem_init();
00439 }
00440 
00441 void UbloxCellularBase::modem_deinit()
00442 {
00443     ::onboard_modem_deinit();
00444 }
00445 
00446 void UbloxCellularBase::modem_power_up()
00447 {
00448     ::onboard_modem_power_up();
00449 }
00450 
00451 void UbloxCellularBase::modem_power_down()
00452 {
00453     ::onboard_modem_power_down();
00454 }
00455 #else
00456 void UbloxCellularBase::modem_init()
00457 {
00458     // Meant to be overridden
00459 }
00460 
00461 void UbloxCellularBase::modem_deinit()
00462 {
00463     // Meant to be overridden
00464 }
00465 
00466 void UbloxCellularBase::modem_power_up()
00467 {
00468     // Meant to be overridden
00469 }
00470 
00471 void UbloxCellularBase::modem_power_down()
00472 {
00473     // Mmeant to be overridden
00474 }
00475 #endif
00476 
00477 // Constructor.
00478 // Note: to allow this base class to be inherited as a virtual base class
00479 // by everyone, it takes no parameters.  See also comment above classInit()
00480 // in the header file.
00481 UbloxCellularBase::UbloxCellularBase()
00482 {
00483     _pin = NULL;
00484     _at = NULL;
00485     _at_timeout = AT_PARSER_TIMEOUT;
00486     _fh = NULL;
00487     _modem_initialised = false;
00488     _sim_pin_check_enabled = false;
00489     _debug_trace_on = false;
00490 
00491     _dev_info.dev = DEV_TYPE_NONE;
00492     _dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING;
00493     _dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING;
00494     _dev_info.reg_status_eps = EPS_NOT_REGISTERED_NOT_SEARCHING;
00495 #ifdef TARGET_UBLOX_C030_R412M
00496     _dev_info.modem_psm_state = AWAKE;
00497     _psm_status = UNKNOWN;
00498     _cb_param_psm_going_in = NULL;
00499     _func_psm_going_in = NULL;
00500     _cb_param_psm_coming_out = NULL;
00501     _func_psm_coming_out = NULL;
00502 #endif
00503 }
00504 
00505 // Destructor.
00506 UbloxCellularBase::~UbloxCellularBase()
00507 {
00508     deinit();
00509     delete _at;
00510     delete _fh;
00511 }
00512 
00513 // Initialise the portions of this class that are parameterised.
00514 void UbloxCellularBase::baseClassInit(PinName tx, PinName rx,
00515                                       int baud, bool debug_on)
00516 {
00517     // Only initialise ourselves if it's not already been done
00518     if (_at == NULL) {
00519         if (_debug_trace_on == false) {
00520             _debug_trace_on = debug_on;
00521         }
00522         _baud = baud;
00523 
00524         // Set up File Handle for buffered serial comms with cellular module
00525         // (which will be used by the AT parser)
00526         // Note: the UART is initialised to run no faster than 115200 because
00527         // the modems cannot reliably auto-baud at faster rates.  The faster
00528         // rate is adopted later with a specific AT command and the
00529         // UARTSerial rate is adjusted at that time
00530         if (baud > 115200) {
00531             baud = 115200;
00532         }
00533         _fh = new UARTSerial(tx, rx, baud);
00534 
00535         // Set up the AT parser
00536 #ifdef TARGET_UBLOX_C030_R41XM
00537         _at = new UbloxATCmdParser(_fh, OUTPUT_ENTER_KEY, AT_PARSER_BUFFER_SIZE,
00538                               _at_timeout, _debug_trace_on);
00539 #else
00540         _at = new ATCmdParser(_fh, OUTPUT_ENTER_KEY, AT_PARSER_BUFFER_SIZE,
00541                               _at_timeout, _debug_trace_on);
00542 #endif
00543 
00544         // Error cases, out of band handling
00545         _at->oob("ERROR", callback(this, &UbloxCellularBase::parser_abort_cb));
00546         _at->oob("+CME ERROR", callback(this, &UbloxCellularBase::CMX_ERROR_URC));
00547         _at->oob("+CMS ERROR", callback(this, &UbloxCellularBase::CMX_ERROR_URC));
00548 
00549         // Registration status, out of band handling
00550         _at->oob("+CREG", callback(this, &UbloxCellularBase::CREG_URC));
00551         _at->oob("+CGREG", callback(this, &UbloxCellularBase::CGREG_URC));
00552         _at->oob("+CEREG", callback(this, &UbloxCellularBase::CEREG_URC));
00553 
00554         // Capture the UMWI, just to stop it getting in the way
00555         _at->oob("+UMWI", callback(this, &UbloxCellularBase::UMWI_URC));
00556 #ifdef TARGET_UBLOX_C030_R412M
00557         // Handle PSM URC for going in and coming out of PSM
00558         _at->oob("+UUPSMR", callback(this, &UbloxCellularBase::UUPSMR_URC));
00559 #endif
00560     }
00561 }
00562 
00563 // Set the AT parser timeout.
00564 // Note: the AT interface should be locked before this is called.
00565 void UbloxCellularBase::at_set_timeout(int timeout) {
00566 
00567     MBED_ASSERT(_at != NULL);
00568 
00569     _at_timeout = timeout;
00570     _at->set_timeout(timeout);
00571 }
00572 
00573 // Read up to size bytes from the AT interface up to a "end".
00574 // Note: the AT interface should be locked before this is called.
00575 int UbloxCellularBase::read_at_to_char(char * buf, int size, char end)
00576 {
00577     int count = 0;
00578     int x = 0;
00579 
00580     if (size > 0) {
00581         for (count = 0; (count < size) && (x >= 0) && (x != end); count++) {
00582             x = _at->getc();
00583             *(buf + count) = (char) x;
00584         }
00585 
00586         count--;
00587         *(buf + count) = 0;
00588 
00589         // Convert line endings:
00590         // If end was '\n' (0x0a) and the preceding character was 0x0d, then
00591         // overwrite that with null as well.
00592         if ((count > 0) && (end == '\n') && (*(buf + count - 1) == '\x0d')) {
00593             count--;
00594             *(buf + count) = 0;
00595         }
00596     }
00597 
00598     return count;
00599 }
00600 
00601 // Power up the modem.
00602 // Enables the GPIO lines to the modem and then wriggles the power line in short pulses.
00603 bool UbloxCellularBase::power_up()
00604 {
00605     bool success = false;
00606 
00607     MBED_ASSERT(_at != NULL);
00608 
00609     /* Initialize GPIO lines */
00610     tr_info("Powering up modem...");
00611     modem_init();
00612     /* Give modem a little time to settle down */
00613     ThisThread::sleep_for(250);
00614 
00615     for (int retry_count = 0; !success && (retry_count < 20); retry_count++) {
00616         //In case of SARA-R4, modem takes a while to turn on, constantly toggling the power pin every ~2 secs causes the modem to never power up.
00617         if ( (retry_count % 5) == 0) {
00618             modem_power_up();
00619         }
00620         success = is_modem_ready();
00621     }
00622 
00623     return success;
00624 }
00625 
00626 bool UbloxCellularBase::setup_modem()
00627 {
00628     bool success = false;
00629     LOCK();
00630 
00631     MBED_ASSERT(_at != NULL);
00632 
00633     // Set the final baud rate
00634     if (_at->send("AT+IPR=%d", _baud) && _at->recv("OK")) {
00635         // Need to wait for things to be sorted out on the modem side
00636         ThisThread::sleep_for(100);
00637         ((UARTSerial *)_fh)->set_baud(_baud);
00638 
00639         // Turn off modem echoing and turn on verbose responses
00640         success = _at->send("ATE0;+CMEE=2") && _at->recv("OK") &&
00641                   // The following commands are best sent separately
00642                   _at->send("AT&K0") && _at->recv("OK") && // Turn off RTC/CTS handshaking
00643                   _at->send("AT&C1") && _at->recv("OK") && // Set DCD circuit(109), changes in accordance with the carrier detect status
00644                   _at->send("AT&D0") && _at->recv("OK"); // Set DTR circuit, we ignore the state change of DTR
00645     }
00646 
00647     if (!success) {
00648         tr_error("Preliminary modem setup failed.");
00649     }
00650 
00651     UNLOCK();
00652     return success;
00653 }
00654 
00655 bool UbloxCellularBase::is_modem_ready()
00656 {
00657     bool success = false;
00658     int at_timeout;
00659     LOCK();
00660 
00661     at_timeout = _at_timeout; // Has to be inside LOCK()s
00662 
00663     MBED_ASSERT(_at != NULL);
00664 
00665     _at->flush();
00666     at_set_timeout(1000);
00667     if (_at->send("AT")) {
00668         // C027 needs a delay here
00669         ThisThread::sleep_for(100);
00670         if (_at->recv("OK")) {
00671             success = true;
00672         }
00673     }
00674     at_set_timeout(at_timeout);
00675 
00676     UNLOCK();
00677     return success;
00678 }
00679 
00680 bool UbloxCellularBase::initialize_modem()
00681 {
00682     bool success = false;
00683 
00684     if (power_up()) {
00685         success = setup_modem();
00686     } else {
00687         tr_error("Preliminary modem setup failed.");
00688     }
00689     return success;
00690 }
00691 
00692 // Power down modem via AT interface.
00693 void UbloxCellularBase::power_down()
00694 {
00695     LOCK();
00696 
00697     MBED_ASSERT(_at != NULL);
00698 
00699     // power-off modem
00700     modem_power_down();
00701     modem_deinit();
00702 
00703     if (_modem_initialised && (_at != NULL)) {
00704         int at_timeout = _at_timeout; // Save previous timeout
00705         _at->set_timeout(1000);
00706         // Check modem is powered off
00707         if(_at->send("AT") && _at->recv("OK")) {
00708             _at->send("AT+CPWROFF") && _at->recv("OK");
00709         }
00710         _at->set_timeout(at_timeout);
00711     }
00712 
00713     _dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING;
00714     _dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING;
00715     _dev_info.reg_status_eps = EPS_NOT_REGISTERED_NOT_SEARCHING;
00716 
00717    UNLOCK();
00718 }
00719 
00720 // Get the device ID.
00721 bool UbloxCellularBase::set_device_identity(DeviceType *dev)
00722 {
00723     char buf[20];
00724     bool success;
00725     LOCK();
00726 
00727     MBED_ASSERT(_at != NULL);
00728 
00729     success = _at->send("ATI") && _at->recv("%19[^\n]\nOK\n", buf);
00730 
00731     if (success) {
00732         if (strstr(buf, "SARA-G35"))
00733             *dev = DEV_SARA_G35;
00734         else if (strstr(buf, "LISA-U200-03S"))
00735             *dev = DEV_LISA_U2_03S;
00736         else if (strstr(buf, "LISA-U2"))
00737             *dev = DEV_LISA_U2;
00738         else if (strstr(buf, "SARA-U2"))
00739             *dev = DEV_SARA_U2;
00740         else if (strstr(buf, "SARA-R4"))
00741             *dev = DEV_SARA_R4;
00742         else if (strstr(buf, "LEON-G2"))
00743             *dev = DEV_LEON_G2;
00744         else if (strstr(buf, "TOBY-L2"))
00745             *dev = DEV_TOBY_L2;
00746         else if (strstr(buf, "MPCI-L2"))
00747             *dev = DEV_MPCI_L2;
00748     }
00749 
00750     UNLOCK();
00751     return success;
00752 }
00753 
00754 // Send initialisation AT commands that are specific to the device.
00755 bool UbloxCellularBase::device_init(DeviceType dev)
00756 {
00757     bool success = false;
00758     LOCK();
00759 
00760     MBED_ASSERT(_at != NULL);
00761 
00762     if ((dev == DEV_LISA_U2) || (dev == DEV_LEON_G2) || (dev == DEV_TOBY_L2)) {
00763         success = _at->send("AT+UGPIOC=20,2") && _at->recv("OK");
00764     } else if ((dev == DEV_SARA_U2) || (dev == DEV_SARA_G35)) {
00765         success = _at->send("AT+UGPIOC=16,2") && _at->recv("OK");
00766     } else {
00767         success = true;
00768     }
00769 
00770     UNLOCK();
00771     return success;
00772 }
00773 
00774 // Get the SIM card going.
00775 bool UbloxCellularBase::initialise_sim_card()
00776 {
00777     bool success = false;
00778     int retry_count = 0;
00779     bool done = false;
00780     LOCK();
00781 
00782     MBED_ASSERT(_at != NULL);
00783 
00784     /* SIM initialisation may take a significant amount, so an error is
00785      * kind of expected. We should retry 10 times until we succeed or timeout. */
00786     for (retry_count = 0; !done && (retry_count < 10); retry_count++) {
00787         char pinstr[16];
00788 
00789         if (_at->send("AT+CPIN?") && _at->recv("+CPIN: %15[^\n]\n", pinstr) &&
00790             _at->recv("OK")) {
00791             done = true;
00792             if (strcmp(pinstr, "SIM PIN") == 0) {
00793                 _sim_pin_check_enabled = true;
00794                 if (_at->send("AT+CPIN=\"%s\"", _pin)) {
00795                     if (_at->recv("OK")) {
00796                         tr_info("PIN correct");
00797                         success = true;
00798                     } else {
00799                         tr_error("Incorrect PIN");
00800                     }
00801                 }
00802             } else if (strcmp(pinstr, "READY") == 0) {
00803                 _sim_pin_check_enabled = false;
00804                 tr_info("No PIN required");
00805                 success = true;
00806             } else {
00807                 tr_debug("Unexpected response from SIM: \"%s\"", pinstr);
00808             }
00809         }
00810 
00811         /* wait for a second before retry */
00812         ThisThread::sleep_for(1000);
00813     }
00814 
00815     if (done) {
00816         tr_info("SIM Ready.");
00817     } else {
00818         tr_error("SIM not ready.");
00819     }
00820 
00821     UNLOCK();
00822     return success;
00823 }
00824 
00825 /**********************************************************************
00826  * PUBLIC METHODS
00827  **********************************************************************/
00828 
00829 // Initialise the modem.
00830 bool UbloxCellularBase::init(const char *pin)
00831 {
00832     int x;
00833     MBED_ASSERT(_at != NULL);
00834 
00835     if (!_modem_initialised) {
00836         if (initialize_modem()) {
00837             tr_info("Modem Ready.");
00838             if (pin != NULL) {
00839                 _pin = pin;
00840             }
00841 #ifdef TARGET_UBLOX_C027
00842             if (set_functionality_mode(FUNC_MIN)) {
00843 #else
00844             if (set_functionality_mode(FUNC_AIRPLANE)) {
00845 #endif
00846                 if (initialise_sim_card()) {
00847 #ifdef TARGET_UBLOX_C030_R41XM
00848                     int mno_profile;
00849                     if (get_mno_profile(&mno_profile)) {
00850 #ifdef MBED_CONF_APP_DEFAULT_MNO_PROFILE
00851                         if (mno_profile != MBED_CONF_APP_DEFAULT_MNO_PROFILE && set_mno_profile((MNOProfile)MBED_CONF_APP_DEFAULT_MNO_PROFILE)) {
00852                             reboot_modem();
00853                             while(is_modem_ready() == false) {
00854                                 ThisThread::sleep_for(1000);
00855                             }
00856                             setup_modem();
00857                             mno_profile = MBED_CONF_APP_DEFAULT_MNO_PROFILE;
00858                         }
00859 #endif
00860                         if (mno_profile == SW_DEFAULT) {
00861                             tr_critical("!!CANNOT USE PROFILE 0(SW_DEFAULT). PLEASE SET AN APPROPRIATE MNO PROFILE!!");
00862                             _default_profile_is_set = true;
00863                             return false;
00864                         }
00865                     }
00866 #ifdef TARGET_UBLOX_C030_R412M
00867                     int status = 0, periodic_time = 0, active_time = 0;
00868                     if (_psm_status == UNKNOWN) {
00869                         if (get_power_saving_mode(&status, &periodic_time, &active_time)) {
00870                             if (status) { //PSM is already enabled either by a previous run or MNO profile
00871                                 tr_info("PSM is already enabled, periodic_time %d, active_time %d", periodic_time, active_time);
00872                                 _psm_status = ENABLED;
00873                                 if ( !(set_psm_urcs(true)) ) { //enable PSM URCs
00874                                     tr_error("Modem does not support PSM URCs, disabling PSM");
00875                                     disable_power_saving_mode();
00876                                 } else if (!_func_psm_going_in){
00877                                     tr_critical("!!PSM IS ENABLED, CALLBACK NOT ATTACHED. PLEASE REGISTER ONE!!");
00878                                 }
00879                             }
00880                         }
00881                     } else if (_psm_status == ENABLED && !_func_psm_going_in){
00882                         tr_critical("!!PSM IS ENABLED, CALLBACK NOT ATTACHED. PLEASE REGISTER ONE!!");
00883                     }
00884 #elif TARGET_UBLOX_C030_R410M
00885                     disable_power_saving_mode(); //PSM is currently not supported by driver for R410M due to lack of URCs
00886 #endif
00887                     if (_at->is_idle_mode_enabled() == false || _psm_status == ENABLED) {
00888                         //application has not yet enabled idle mode so disable it
00889                         //PSM got enabled by MNO, disable idle mode.
00890                         set_idle_mode(false);
00891                     }
00892 #endif
00893                     if (set_device_identity(&_dev_info.dev) && // Set up device identity
00894                         device_init(_dev_info.dev)) {// Initialise this device
00895                         // Get the integrated circuit ID of the SIM
00896                         if (get_iccid()) {
00897                             // Try a few times to get the IMSI (since on some modems this can
00898                             // take a while to be retrieved, especially if a SIM PIN
00899                             // was set)
00900                             for (x = 0; (x < 3) && !get_imsi(); x++) {
00901                                 ThisThread::sleep_for(1000);
00902                             }
00903 
00904                             if (x < 3) { // If we got the IMSI, can get the others
00905                                 if (get_imei() && // Get international mobile equipment identifier
00906                                     get_meid() && // Probably the same as the IMEI
00907                                     set_sms()) { // And set up SMS
00908                                     // The modem is initialised.
00909                                     _modem_initialised = true;
00910                                     tr_info("Modem initialized");
00911                                 }
00912                             }
00913                         }
00914                     }
00915                 }
00916             }
00917         }
00918     }
00919 
00920     return _modem_initialised;
00921 }
00922 
00923 // Perform registration.
00924 bool UbloxCellularBase::nwk_registration()
00925 {
00926     bool atSuccess = false;
00927     bool registered = false;
00928     int status;
00929     int at_timeout;
00930     LOCK();
00931 
00932     at_timeout = _at_timeout; // Has to be inside LOCK()s
00933 
00934     MBED_ASSERT(_at != NULL);
00935 
00936     if (!is_registered_psd() && !is_registered_csd() && !is_registered_eps()) {
00937         if (set_functionality_mode(FUNC_FULL)) {
00938             tr_info("Searching Network...");
00939             // Enable the packet switched and network registration unsolicited result codes
00940             if (_at->send("AT+CREG=1") && _at->recv("OK") &&
00941                 _at->send("AT+CGREG=1") && _at->recv("OK")) {
00942                 atSuccess = true;
00943                 if (_at->send("AT+CEREG=1")) {
00944                     _at->recv("OK");
00945                     // Don't check return value as this works for LTE only
00946                 }
00947 
00948                 if (atSuccess) {
00949                     // See if we are already in automatic mode
00950                     if (_at->send("AT+COPS?") && _at->recv("+COPS: %d", &status) &&
00951                         _at->recv("OK")) {
00952                         // If not, set it
00953                         if (status != 0) {
00954                             // Don't check return code here as there's not much
00955                             // we can do if this fails.
00956                             _at->send("AT+COPS=0") && _at->recv("OK");
00957                         }
00958                     }
00959 
00960                     // Query the registration status directly as well,
00961                     // just in case
00962                     if (_at->send("AT+CREG?") && _at->recv("OK")) {
00963                         // Answer will be processed by URC
00964                     }
00965                     if (_at->send("AT+CGREG?") && _at->recv("OK")) {
00966                         // Answer will be processed by URC
00967                     }
00968                     if (_at->send("AT+CEREG?")) {
00969                         _at->recv("OK");
00970                         // Don't check return value as this works for LTE only
00971                     }
00972                 }
00973             }
00974             // Wait for registration to succeed
00975             at_set_timeout(1000);
00976             for (int waitSeconds = 0; !registered && (waitSeconds < 180); waitSeconds++) {
00977                 registered = is_registered_psd() || is_registered_csd() || is_registered_eps();
00978                 _at->recv(UNNATURAL_STRING);
00979             }
00980             at_set_timeout(at_timeout);
00981 
00982             if (registered) {
00983                 // This should return quickly but sometimes the status field is not returned
00984                 // so make the timeout short
00985                 at_set_timeout(1000);
00986                 if (_at->send("AT+COPS?") && _at->recv("+COPS: %*d,%*d,\"%*[^\"]\",%d\nOK\n", &status)) {
00987                     set_rat(status);
00988                 }
00989                 at_set_timeout(at_timeout);
00990             }
00991         }
00992     } else {
00993         registered = true;
00994     }
00995     
00996     UNLOCK();
00997     return registered;
00998 }
00999 
01000 bool UbloxCellularBase::is_registered_csd()
01001 {
01002   return (_dev_info.reg_status_csd == CSD_REGISTERED) ||
01003           (_dev_info.reg_status_csd == CSD_REGISTERED_ROAMING) ||
01004           (_dev_info.reg_status_csd == CSD_CSFB_NOT_PREFERRED);
01005 }
01006 
01007 bool UbloxCellularBase::is_registered_psd()
01008 {
01009     return (_dev_info.reg_status_psd == PSD_REGISTERED) ||
01010             (_dev_info.reg_status_psd == PSD_REGISTERED_ROAMING);
01011 }
01012 
01013 bool UbloxCellularBase::is_registered_eps()
01014 {
01015     return (_dev_info.reg_status_eps == EPS_REGISTERED) ||
01016             (_dev_info.reg_status_eps == EPS_REGISTERED_ROAMING);
01017 }
01018 
01019 // Perform deregistration.
01020 bool UbloxCellularBase::nwk_deregistration()
01021 {
01022     bool success = false;
01023     LOCK();
01024 
01025     MBED_ASSERT(_at != NULL);
01026 
01027     int at_timeout = _at_timeout;  // Has to be inside LOCK()s
01028     at_set_timeout(3*60*1000); //command has 3 minutes timeout
01029 
01030     if (_at->send("AT+COPS=2") && _at->recv("OK")) {
01031         _dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING;
01032         _dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING;
01033         _dev_info.reg_status_eps = EPS_NOT_REGISTERED_NOT_SEARCHING;
01034         success = true;
01035     }
01036 
01037     at_set_timeout(at_timeout);
01038     UNLOCK();
01039     return success;
01040 }
01041 
01042 // Put the modem into its lowest power state.
01043 void UbloxCellularBase::deinit()
01044 {
01045     power_down();
01046     _modem_initialised = false;
01047 }
01048 
01049 // Set the PIN.
01050 void UbloxCellularBase::set_pin(const char *pin) {
01051     _pin = pin;
01052 }
01053 
01054 // Enable or disable SIM pin checking.
01055 bool UbloxCellularBase::sim_pin_check_enable(bool enableNotDisable)
01056 {
01057     bool success = false;;
01058     LOCK();
01059 
01060     MBED_ASSERT(_at != NULL);
01061 
01062     if (_pin != NULL) {
01063         if (_sim_pin_check_enabled && !enableNotDisable) {
01064             // Disable the SIM lock
01065             if (_at->send("AT+CLCK=\"SC\",0,\"%s\"", _pin) && _at->recv("OK")) {
01066                 _sim_pin_check_enabled = false;
01067                 success = true;
01068             }
01069         } else if (!_sim_pin_check_enabled && enableNotDisable) {
01070             // Enable the SIM lock
01071             if (_at->send("AT+CLCK=\"SC\",1,\"%s\"", _pin) && _at->recv("OK")) {
01072                 _sim_pin_check_enabled = true;
01073                 success = true;
01074             }
01075         } else {
01076             success = true;
01077         }
01078     }
01079 
01080     UNLOCK();
01081     return success;
01082 }
01083 
01084 // Change the pin code for the SIM card.
01085 bool UbloxCellularBase::change_sim_pin(const char *pin)
01086 {
01087     bool success = false;;
01088     LOCK();
01089 
01090     MBED_ASSERT(_at != NULL);
01091 
01092     // Change the SIM pin
01093     if ((pin != NULL) && (_pin != NULL)) {
01094         if (_at->send("AT+CPWD=\"SC\",\"%s\",\"%s\"", _pin, pin) && _at->recv("OK")) {
01095             _pin = pin;
01096             success = true;
01097         }
01098     }
01099 
01100     UNLOCK();
01101     return success;
01102 }
01103 
01104 // Get the IMEI.
01105 bool UbloxCellularBase::get_imei(char *imei_to_send, int size)
01106 {
01107     bool success;
01108     LOCK();
01109 
01110     MBED_ASSERT(_at != NULL);
01111 
01112     // International mobile equipment identifier
01113     // AT Command Manual UBX-13002752, section 4.7
01114     success = _at->send("AT+CGSN") && _at->recv("%15[^\n]\nOK\n", _dev_info.imei);
01115     tr_info("DevInfo: IMEI=%s", _dev_info.imei);
01116 
01117     if (success)    {
01118         memcpy(imei_to_send,_dev_info.imei,size);
01119         imei_to_send[size-1] = '\0';
01120     }
01121 
01122     UNLOCK();
01123     return success;
01124 }
01125 
01126 // Get the IMEI of the module.
01127 const char *UbloxCellularBase::imei()
01128 {
01129     return _dev_info.imei;
01130 }
01131 
01132 // Get the Mobile Equipment ID (which may be the same as the IMEI).
01133 const char *UbloxCellularBase::meid()
01134 {
01135     return _dev_info.meid;
01136 }
01137 
01138 // Get the IMSI of the SIM.
01139 const char *UbloxCellularBase::imsi()
01140 {
01141     // (try) to update the IMSI, just in case the SIM has changed
01142     get_imsi();
01143     
01144     return _dev_info.imsi;
01145 }
01146 
01147 // Get the ICCID of the SIM.
01148 const char *UbloxCellularBase::iccid()
01149 {
01150     // (try) to update the ICCID, just in case the SIM has changed
01151     get_iccid();
01152     
01153     return _dev_info.iccid;
01154 }
01155 
01156 // Get the RSSI in dBm.
01157 int UbloxCellularBase::rssi()
01158 {
01159     char buf[7] = {0};
01160     int rssi = 0;
01161     int qual = 0;
01162     int rssiRet = 0;
01163     bool success;
01164     LOCK();
01165 
01166     MBED_ASSERT(_at != NULL);
01167 
01168     success = _at->send("AT+CSQ") && _at->recv("+CSQ: %6[^\n]\nOK\n", buf);
01169 
01170     if (success) {
01171         if (sscanf(buf, "%d,%d", &rssi, &qual) == 2) {
01172             // AT+CSQ returns a coded RSSI value and an RxQual value
01173             // For 2G an RSSI of 0 corresponds to -113 dBm or less, 
01174             // an RSSI of 31 corresponds to -51 dBm or less and hence
01175             // each value is a 2 dB step.
01176             // For LTE the mapping is defined in the array rssiConvertLte[].
01177             // For 3G the mapping to RSCP is defined in the array rscpConvert3G[]
01178             // and the RSSI value is then RSCP - the EC_NO_LEV number derived
01179             // by putting the qual number through qualConvert3G[].
01180             if ((rssi >= 0) && (rssi <= 31)) {
01181                 switch (_dev_info.rat) {
01182                     case UTRAN:
01183                     case HSDPA:
01184                     case HSUPA:
01185                     case HSDPA_HSUPA:
01186                         // 3G
01187                         if ((qual >= 0) && (qual <= 7)) {
01188                             qual = qualConvert3G[qual];
01189                             rssiRet = rscpConvert3G[rssi];
01190                             rssiRet -= qual;
01191                         }
01192 
01193                         break;
01194                     case LTE:
01195                         // LTE
01196                         rssiRet = rssiConvertLte[rssi];
01197                         break;
01198                     case GSM:
01199                     case COMPACT_GSM:
01200                     case EDGE:
01201                     default:
01202                         // GSM or assumed GSM if the RAT is not known
01203                         rssiRet = -(113 - (rssi << 2));
01204                         break;
01205                 }
01206             }
01207         }
01208     }
01209 
01210     UNLOCK();
01211     return rssiRet;
01212 }
01213 
01214 //RAT should be set in a detached state (AT+COPS=2)
01215 bool UbloxCellularBase::set_modem_rat(RAT selected_rat, RAT preferred_rat, RAT second_preferred_rat)
01216 {
01217 #ifdef TARGET_UBLOX_C030_R41XM
01218     if (_default_profile_is_set == true) {
01219         tr_critical("!!CANNOT USE PROFILE 0(SW_DEFAULT). PLEASE SET AN APPROPRIATE MNO PROFILE!!");
01220         return false;
01221     }
01222 #endif
01223 
01224     bool success = false;
01225     char command[16] = {0x00};
01226 
01227     //check if modem is registered with network
01228     if (is_registered_csd() || is_registered_psd() || is_registered_eps()) {
01229         tr_error("RAT should only be set in detached state");
01230         return false;
01231     }
01232 
01233     if (preferred_rat != NOT_USED && second_preferred_rat != NOT_USED) {
01234         sprintf(command, "AT+URAT=%d,%d,%d", selected_rat, preferred_rat, second_preferred_rat);
01235     } else if (preferred_rat != NOT_USED) {
01236         sprintf(command, "AT+URAT=%d,%d", selected_rat, preferred_rat);
01237     } else if (second_preferred_rat != NOT_USED) {
01238         sprintf(command, "AT+URAT=%d,%d", selected_rat, second_preferred_rat);
01239     } else {
01240         sprintf(command, "AT+URAT=%d", selected_rat);
01241     }
01242 
01243     LOCK();
01244     if (_at->send(command) && _at->recv("OK"))  {
01245         success = true;
01246     } else {
01247         tr_error("unable to set the specified RAT");
01248         success = false;
01249     }
01250     UNLOCK();
01251 
01252     return success;
01253 }
01254 
01255 bool UbloxCellularBase::get_modem_rat(int *selected_rat, int *preferred_rat, int *second_preferred_rat)
01256 {
01257 #ifdef TARGET_UBLOX_C030_R41XM
01258     if (_default_profile_is_set == true) {
01259         tr_critical("!!CANNOT USE PROFILE 0(SW_DEFAULT). PLEASE SET AN APPROPRIATE MNO PROFILE!!");
01260         return false;
01261     }
01262 #endif
01263 
01264     bool success = false;
01265     char buf[24] = {0x00};
01266 
01267     if (selected_rat == NULL || preferred_rat == NULL || second_preferred_rat == NULL) {
01268         tr_info("invalid pointers");
01269         return false;
01270     }
01271 
01272     MBED_ASSERT(_at != NULL);
01273 
01274     *selected_rat = NOT_USED;
01275     *preferred_rat = NOT_USED;
01276     *second_preferred_rat = NOT_USED;
01277 
01278     LOCK();
01279 
01280     if (_at->send("AT+URAT?") && _at->recv("%23[^\n]\nOK\n", buf)) {
01281         if (sscanf(buf, "+URAT: %d,%d,%d", selected_rat, preferred_rat, second_preferred_rat) == 3) {
01282             success = true;
01283         } else if (sscanf(buf, "+URAT: %d,%d", selected_rat, preferred_rat) == 2) {
01284             success = true;
01285         } else if (sscanf(buf, "+URAT: %d", selected_rat) == 1) {
01286             success = true;
01287         }
01288     }
01289 
01290     UNLOCK();
01291     return success;
01292 }
01293 
01294 //application should call init() or connect() in order to initialize the modem
01295 bool UbloxCellularBase::reboot_modem()
01296 {
01297     return (set_functionality_mode(FUNC_RESET));
01298 }
01299 
01300 bool UbloxCellularBase::set_functionality_mode(FunctionalityMode mode)
01301 {
01302     bool return_val = false;
01303     int at_timeout;
01304     LOCK();
01305 
01306     MBED_ASSERT(_at != NULL);
01307 
01308     at_timeout = _at_timeout; // Has to be inside LOCK()s
01309     at_set_timeout(3*60*1000); //command has 3 minutes timeout
01310 
01311     if (_at->send("AT+CFUN=%d", mode) && _at->recv("OK")) {
01312         return_val = true;
01313     }
01314 
01315     if (mode == FUNC_RESET || mode == FUNC_RESET_WITH_SIM) {
01316         _modem_initialised = false;
01317     }
01318 
01319     at_set_timeout(at_timeout);
01320     UNLOCK();
01321 
01322     return return_val;
01323 }
01324 
01325 bool UbloxCellularBase::get_functionality_mode(int *mode)
01326 {
01327     bool return_val = false;
01328 
01329     if (mode == NULL) {
01330         return false;
01331     }
01332 
01333     LOCK();
01334     MBED_ASSERT(_at != NULL);
01335 
01336     if ( (_at->send("AT+CFUN?") && _at->recv("+CFUN: %d", mode) && _at->recv("OK")) )  {
01337         return_val = true;
01338     }
01339 
01340     UNLOCK();
01341     return return_val;
01342 }
01343 
01344 #ifdef TARGET_UBLOX_C030_R41XM
01345 bool UbloxCellularBase::set_mno_profile(MNOProfile profile)
01346 {
01347     bool return_val = false;
01348     int current_profile;
01349     MNOProfile arr[MAX_NUM_PROFILES] = { SW_DEFAULT, SIM_ICCID, ATT, TMO, VODAFONE, DT, STANDARD_EU
01350 #ifdef TARGET_UBLOX_C030_R410M
01351                                          , VERIZON, TELSTRA, CT, SPRINT, TELUS
01352 #endif
01353                                        };
01354 
01355     if (is_registered_csd() || is_registered_psd() || is_registered_eps()) {
01356         tr_error("MNO profile should only be set in detached state");
01357         return false;
01358     }
01359 
01360     if (get_mno_profile(&current_profile)) {
01361         if (current_profile == profile) { //Ref to UBX-18019856 7.1.7, parameters will be updated only if we switch to another profile first
01362             for (uint8_t index = 0; index < MAX_NUM_PROFILES; index++) { //get the index of current profile and use the next one
01363                 if (arr[index] == current_profile) {
01364                     index = ((index + 1) % MAX_NUM_PROFILES);
01365                     current_profile = arr[index];
01366                     break;
01367                 }
01368             }
01369 
01370             LOCK();
01371             if (_at->send("AT+UMNOPROF=%d", current_profile) && _at->recv("OK")) {
01372                 tr_info("temporary MNO profile set: %d", current_profile);
01373             }
01374             UNLOCK();
01375         }
01376         LOCK();
01377         if (_at->send("AT+UMNOPROF=%d", profile) && _at->recv("OK")) {
01378             if (profile == SW_DEFAULT) {
01379                 tr_critical("!!CANNOT USE PROFILE 0(SW_DEFAULT). PLEASE SET AN APPROPRIATE MNO PROFILE!!");
01380                 _default_profile_is_set = true;
01381             } else {
01382                 _default_profile_is_set = false;
01383             }
01384             return_val = true;
01385         } else {
01386             tr_error("unable to set user specified profile");
01387         }
01388         UNLOCK();
01389     } else {
01390         tr_error("could not read MNO profile");
01391     }
01392 
01393     return return_val;
01394 }
01395 
01396 bool UbloxCellularBase::get_mno_profile(int *profile)
01397 {
01398     bool return_val = false;
01399     char buf[4] = {0x00};
01400 
01401     if (profile == NULL) {
01402         return false;
01403     }
01404 
01405     LOCK();
01406     MBED_ASSERT(_at != NULL);
01407 
01408     if (_at->send("AT+UMNOPROF?") && _at->recv("+UMNOPROF: %3[^\n]\nOK\n", buf))  {
01409         *profile = atoi(buf);
01410         return_val = true;
01411     }
01412 
01413     UNLOCK();
01414     return return_val;
01415 }
01416 // Enable or Disable the UPSV power saving mode
01417 bool UbloxCellularBase::set_idle_mode(bool enable)
01418 {
01419 #ifdef TARGET_UBLOX_C030_R412M
01420     if (_psm_status == ENABLED && enable == true) {
01421         return false;
01422     }
01423 #endif
01424 
01425     bool success = false;
01426     LOCK();
01427 
01428     MBED_ASSERT(_at != NULL);
01429 
01430     if (_at->send("AT+UPSV=%d", enable ? 4 : 0) && _at->recv("OK")) {
01431         if (enable == true) {
01432             _at->idle_mode_enabled();
01433         }
01434         else {
01435             _at->idle_mode_disabled();
01436         }
01437         success = true;
01438     }
01439 
01440     UNLOCK();
01441     return success;
01442 }
01443 
01444 bool UbloxCellularBase::get_idle_mode(int *status)
01445 {
01446     bool return_val = false;
01447 
01448     if (status == NULL) {
01449         return false;
01450     }
01451 
01452     LOCK();
01453     MBED_ASSERT(_at != NULL);
01454 
01455     if ( (_at->send("AT+UPSV?") && _at->recv("+UPSV: %d", status) && _at->recv("OK")) )  {
01456         if (*status == 4) {
01457             *status = 1;
01458         }
01459         return_val = true;
01460     }
01461 
01462     UNLOCK();
01463     return return_val;
01464 }
01465 
01466 int UbloxCellularBase::set_receive_period(int mode, tEDRXAccessTechnology act_type, uint8_t edrx_value) {
01467     char edrx[5];
01468     uint_to_binary_str(edrx_value, edrx, 5, 4);
01469     edrx[4] = '\0';
01470     int status = 1;
01471 
01472     LOCK();
01473 
01474     if (_at->send("AT+CEDRXS=%d,%d,\"%s\"", mode, act_type, edrx) && _at->recv("OK")) {
01475         status = 0;
01476     }
01477     else {
01478         status = 1;
01479     }
01480 
01481 
01482     UNLOCK();
01483 
01484     return status;
01485 }
01486 
01487 int UbloxCellularBase::set_receive_period(int mode, tEDRXAccessTechnology act_type) {
01488     int status = 1;
01489 
01490     LOCK();
01491 
01492     if (_at->send("AT+CEDRXS=%d,%d", mode, act_type) && _at->recv("OK")) {
01493 
01494         status = 0;
01495     }
01496     else {
01497         status = 1;
01498     }
01499 
01500     UNLOCK();
01501 
01502     return status;
01503 }
01504 
01505 int UbloxCellularBase::set_receive_period(int mode) {
01506     int status = 1;
01507 
01508     LOCK();
01509 
01510     if (_at->send("AT+CEDRXS=%d", mode) && _at->recv("OK")) {
01511 
01512         status = 0;
01513     }
01514     else {
01515         status = 1;
01516     }
01517 
01518     UNLOCK();
01519 
01520     return status;
01521 }
01522 
01523 uint32_t UbloxCellularBase::get_receive_period() {
01524     uint32_t edrx_value = 2;
01525     char buf[24] = {0x00};
01526     char edrx_val[5];
01527     tEDRXAccessTechnology act_type;
01528 
01529     LOCK();
01530 
01531     if (_at->send("AT+CEDRXS?") && _at->recv("%23[^\n]\nOK\n", buf)) {
01532         if (sscanf(buf, "+CEDRXS: %d,\"%s\"", (int *)&act_type, edrx_val) == 2) {
01533 
01534             edrx_value = binary_str_to_uint(edrx_val,4);
01535         }
01536     }
01537 
01538     if (_at->send("AT+CEDRXRDP") && _at->recv("OK")) {
01539     }
01540 
01541     tr_info("edrx_value. %d", edrx_value);
01542 
01543     UNLOCK();
01544     return edrx_value;
01545 }
01546 
01547 void UbloxCellularBase::uint_to_binary_str(uint32_t num, char* str, int str_size, int bit_cnt)
01548 {
01549     if (!str || str_size < bit_cnt) {
01550         return;
01551     }
01552     int tmp, pos = 0;
01553 
01554     for (int i = 31; i >= 0; i--) {
01555         tmp = num >> i;
01556         if (i < bit_cnt) {
01557             if (tmp&1) {
01558                 str[pos] = 1 + '0';
01559             } else {
01560                 str[pos] = 0 + '0';
01561             }
01562             pos++;
01563         }
01564     }
01565 }
01566 
01567 uint32_t UbloxCellularBase::binary_str_to_uint(const char *binary_string, int binary_string_length)
01568 {
01569     if (!binary_string || !binary_string_length) {
01570         return 0;
01571     }
01572 
01573     int integer_output = 0, base_exp = 1;
01574 
01575     for (int i = binary_string_length - 1; i >= 0; i--) {
01576         if (binary_string[i] == '1') {
01577             integer_output += (base_exp << (binary_string_length - (i+1)));
01578         }
01579     }
01580 
01581     return integer_output;
01582 }
01583 
01584 bool UbloxCellularBase::set_band_bitmask(RAT rat, uint64_t bitmask) {
01585 
01586     if (_default_profile_is_set == true) {
01587         tr_critical("!!CANNOT USE PROFILE 0(SW_DEFAULT). PLEASE SET AN APPROPRIATE MNO PROFILE!!");
01588         return false;
01589     }
01590 
01591     bool status = false;
01592     UBandmaskRAT eBandMastRat;
01593 
01594     if(rat == LTE_CATM1) {
01595         eBandMastRat = UBANDMASK_RAT_LTE_CATM1;
01596     }
01597     else if(rat == LTE_CATNB1) {
01598         eBandMastRat = UBANDMASK_RAT_LTE_CATNB1;
01599     }
01600     else {
01601         tr_error("Invalid RAT for Band mask selection: %d", rat);
01602 
01603         return false;
01604     }
01605 
01606     tr_info("UBANDMASK RAT %d, bitmask : %llu", eBandMastRat, bitmask);
01607 
01608     LOCK();
01609 
01610     if (_at->send("AT+UBANDMASK=%d,%llu", eBandMastRat, bitmask) && _at->recv("OK")) {
01611 
01612         status = true;
01613     }
01614     UNLOCK();
01615 
01616     return status;
01617 }
01618 bool UbloxCellularBase::get_band_bitmask(uint64_t *m1_bitmask, uint64_t *nb1_bitmask)
01619 {
01620     if (_default_profile_is_set == true) {
01621         tr_critical("!!CANNOT USE PROFILE 0(SW_DEFAULT). PLEASE SET AN APPROPRIATE MNO PROFILE!!");
01622         return false;
01623     }
01624 
01625     bool status = false;
01626     int eBandMastRat0, eBandMastRat1;
01627 
01628     LOCK();
01629 
01630     if(_at->send("AT+UBANDMASK?") && _at->recv("+UBANDMASK: %d,%llu,%d,%llu\nOK\n", &eBandMastRat0, m1_bitmask, &eBandMastRat1, nb1_bitmask)) {
01631 
01632         status = true;
01633     }
01634     UNLOCK();
01635 
01636     return status;
01637 }
01638 
01639 bool UbloxCellularBase::disable_power_saving_mode()
01640 {
01641     bool return_value = false;
01642 
01643     LOCK();
01644     if (_at->send("AT+CPSMS=0") && _at->recv("OK")) {
01645 #ifdef TARGET_UBLOX_C030_R412M
01646         _at->send("AT+UPSMR=0");
01647         _at->recv("OK");
01648 #endif
01649         _psm_status = DISABLED;
01650         return_value = true;
01651     }
01652     UNLOCK();
01653 
01654     return return_value;
01655 }
01656 #endif //TARGET_UBLOX_C030_R41XM
01657 
01658 
01659 #ifdef TARGET_UBLOX_C030_R412M
01660 bool UbloxCellularBase::get_power_saving_mode(int *status, int *periodic_time, int *active_time)
01661 {
01662     char pt_encoded[8+1];// timer value encoded as 3GPP IE
01663     char at_encoded[8+1];// timer value encoded as 3GPP IE
01664     int value, multiplier;
01665     bool return_val;
01666 
01667     if (status == NULL || periodic_time == NULL || active_time == NULL) {
01668         return false;
01669     }
01670 
01671     LOCK();
01672     //+UCPSMS:1,,,"01000011","01000011"
01673     if (_at->send("AT+UCPSMS?") && _at->recv("+UCPSMS:%d,,,\"%8c\",\"%8c\"\nOK\n", status, pt_encoded, at_encoded)) {
01674         if (*status == true) {
01675             //PSM is enabled, decode the timer values, periodic TAU first
01676             value =  (pt_encoded[7]- '0');
01677             value += (pt_encoded[6]- '0') << 1;
01678             value += (pt_encoded[5]- '0') << 2;
01679             value += (pt_encoded[4]- '0') << 3;
01680             value += (pt_encoded[3]- '0') << 4;
01681 
01682             multiplier =  (pt_encoded[2]- '0');
01683             multiplier += (pt_encoded[1]- '0') << 1;
01684             multiplier += (pt_encoded[0]- '0') << 2;
01685 
01686             switch(multiplier) {
01687                 //10 minutes
01688                 case 0:
01689                     value = value*10*60;
01690                 break;
01691 
01692                 //1 hour
01693                 case 1:
01694                     value = value*60*60;
01695                 break;
01696 
01697                 //10 hours
01698                 case 2:
01699                     value = value*10*60*60;
01700                 break;
01701 
01702                 //2 seconds
01703                 case 3:
01704                     value = value*2;
01705                 break;
01706 
01707                 //30 seconds
01708                 case 4:
01709                     value = value*30;
01710                 break;
01711 
01712                 //1 minute
01713                 case 5:
01714                     value = value*60;
01715                 break;
01716 
01717                 //320 hours
01718                 case 6:
01719                     value = value*320*60*60;
01720                 break;
01721 
01722                 default:
01723                     value = -1;
01724                 break;
01725             }
01726             *periodic_time = value;
01727 
01728             //decode the active time
01729             value =  (at_encoded[7]- '0');
01730             value += (at_encoded[6]- '0') << 1;
01731             value += (at_encoded[5]- '0') << 2;
01732             value += (at_encoded[4]- '0') << 3;
01733             value += (at_encoded[3]- '0') << 4;
01734 
01735             multiplier =  (at_encoded[2]- '0');
01736             multiplier += (at_encoded[1]- '0') << 1;
01737             multiplier += (at_encoded[0]- '0') << 2;
01738 
01739             switch(multiplier) {
01740                 //2 seconds
01741                 case 0:
01742                     value = value*2;
01743                 break;
01744 
01745                 //1 minute
01746                 case 1:
01747                     value = value*60;
01748                 break;
01749 
01750                 //decihours (6minutes)
01751                 case 2:
01752                     value = value*6*60;
01753                 break;
01754 
01755                 default:
01756                     value = -1;
01757                 break;
01758             }
01759             *active_time = value;
01760         }
01761         return_val = true;
01762     } else {
01763         return_val = false;
01764     }
01765     UNLOCK();
01766     return return_val;
01767 }
01768 
01769 bool UbloxCellularBase::set_power_saving_mode(int periodic_time, int active_time)
01770 {
01771 
01772     if (_at->is_idle_mode_enabled() == true) {
01773         tr_error("Please disable idle mode(+UPSV) first");
01774         return false;
01775     }
01776     bool return_val = false;
01777 
01778     LOCK();
01779     int at_timeout = _at_timeout;
01780     at_set_timeout(10000); //AT+CPSMS has response time of < 10s
01781 
01782     if (_at->send("AT+UPSMR?") && _at->recv("OK")) { //PSM string encoding code borrowed from AT_CellularPower.cpp
01783         /**
01784             Table 10.5.163a/3GPP TS 24.008: GPRS Timer 3 information element
01785 
01786             Bits 5 to 1 represent the binary coded timer value.
01787 
01788             Bits 6 to 8 defines the timer value unit for the GPRS timer as follows:
01789             8 7 6
01790             0 0 0 value is incremented in multiples of 10 minutes
01791             0 0 1 value is incremented in multiples of 1 hour
01792             0 1 0 value is incremented in multiples of 10 hours
01793             0 1 1 value is incremented in multiples of 2 seconds
01794             1 0 0 value is incremented in multiples of 30 seconds
01795             1 0 1 value is incremented in multiples of 1 minute
01796             1 1 0 value is incremented in multiples of 320 hours (NOTE 1)
01797             1 1 1 value indicates that the timer is deactivated (NOTE 2).
01798          */
01799         char pt[8+1];// timer value encoded as 3GPP IE
01800         const int ie_value_max = 0x1f;
01801         uint32_t periodic_timer = 0;
01802         if (periodic_time <= 2*ie_value_max) { // multiples of 2 seconds
01803             periodic_timer = periodic_time/2;
01804             strcpy(pt, "01100000");
01805         } else {
01806             if (periodic_time <= 30*ie_value_max) { // multiples of 30 seconds
01807                 periodic_timer = periodic_time/30;
01808                 strcpy(pt, "10000000");
01809             } else {
01810                 if (periodic_time <= 60*ie_value_max) { // multiples of 1 minute
01811                     periodic_timer = periodic_time/60;
01812                     strcpy(pt, "10100000");
01813                 } else {
01814                     if (periodic_time <= 10*60*ie_value_max) { // multiples of 10 minutes
01815                         periodic_timer = periodic_time/(10*60);
01816                         strcpy(pt, "00000000");
01817                     } else {
01818                         if (periodic_time <= 60*60*ie_value_max) { // multiples of 1 hour
01819                             periodic_timer = periodic_time/(60*60);
01820                             strcpy(pt, "00100000");
01821                         } else {
01822                             if (periodic_time <= 10*60*60*ie_value_max) { // multiples of 10 hours
01823                                 periodic_timer = periodic_time/(10*60*60);
01824                                 strcpy(pt, "01000000");
01825                             } else { // multiples of 320 hours
01826                                 int t = periodic_time / (320*60*60);
01827                                 if (t > ie_value_max) {
01828                                     t = ie_value_max;
01829                                 }
01830                                 periodic_timer = t;
01831                                 strcpy(pt, "11000000");
01832                             }
01833                         }
01834                     }
01835                 }
01836             }
01837         }
01838 
01839         uint_to_binary_str(periodic_timer, &pt[3], sizeof(pt)-3, 5);
01840         pt[8] = '\0';
01841 
01842         /**
01843             Table 10.5.172/3GPP TS 24.008: GPRS Timer information element
01844 
01845             Bits 5 to 1 represent the binary coded timer value.
01846 
01847             Bits 6 to 8 defines the timer value unit for the GPRS timer as follows:
01848 
01849             8 7 6
01850             0 0 0  value is incremented in multiples of 2 seconds
01851             0 0 1  value is incremented in multiples of 1 minute
01852             0 1 0  value is incremented in multiples of decihours
01853             1 1 1  value indicates that the timer is deactivated.
01854 
01855             Other values shall be interpreted as multiples of 1 minute in this version of the protocol.
01856         */
01857         char at[8+1];
01858         uint32_t active_timer; // timer value encoded as 3GPP IE
01859         if (active_time <= 2*ie_value_max) { // multiples of 2 seconds
01860             active_timer = active_time/2;
01861             strcpy(at, "00000000");
01862         } else {
01863             if (active_time <= 60*ie_value_max) { // multiples of 1 minute
01864                 active_timer = (1<<5) | (active_time/60);
01865                 strcpy(at, "00100000");
01866             } else { // multiples of decihours
01867                 int t = active_time / (6*60);
01868                 if (t > ie_value_max) {
01869                     t = ie_value_max;
01870                 }
01871                 active_timer = t;
01872                 strcpy(at, "01000000");
01873             }
01874         }
01875 
01876         uint_to_binary_str(active_timer, &at[3], sizeof(at)-3, 5);
01877         at[8] = '\0';
01878 
01879         if (_at->send("AT+CPSMS=1,,,\"%s\",\"%s\"", pt, at) && _at->recv("OK")) {
01880             if (set_psm_urcs(true)) {//enable the PSM URC
01881                 tr_info("PSM enabled successfully!");
01882                 _psm_status = ENABLED;
01883                 return_val = true;
01884             } else {
01885                 tr_error("Error enabling PSM URCs, PSM not enabled");
01886                 _at->send("AT+CPSMS=0");
01887                 _at->recv("OK");
01888                 return_val = false;
01889             }
01890         } else {
01891             tr_error("+CPSMS command failed");
01892             return_val = false;
01893         }
01894     } else {
01895         tr_error("PSM URCs not supported by this version of modem");
01896     }
01897 
01898     at_set_timeout(at_timeout);
01899     UNLOCK();
01900     return return_val;
01901 }
01902 
01903 bool UbloxCellularBase::is_modem_awake()
01904 {
01905   return (_dev_info.modem_psm_state == AWAKE);
01906 }
01907 
01908 //application should call init() or connect() in order to initialize the modem
01909 void UbloxCellularBase::wakeup_modem()
01910 {
01911     LOCK();
01912 
01913     MBED_ASSERT(_at != NULL);
01914 
01915     tr_info("Waking up modem...");
01916 
01917     modem_power_up();
01918 
01919     _dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING;
01920     _dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING;
01921     _dev_info.reg_status_eps = EPS_NOT_REGISTERED_NOT_SEARCHING;
01922     _modem_initialised = false;
01923 
01924     UNLOCK();
01925 }
01926 
01927 bool UbloxCellularBase::set_psm_urcs(bool enable)
01928 {
01929 
01930     bool success = false;
01931     LOCK();
01932 
01933     MBED_ASSERT(_at != NULL);
01934 
01935     if (_at->send("AT+UPSMR=%d", enable ? 1 : 0) && _at->recv("OK")) {
01936         success = true;
01937     }
01938 
01939     UNLOCK();
01940     return success;
01941 }
01942 #endif
01943 
01944 // End of File
01945