fahim alavi / ublox-cellular-base-SARA-R5

Dependents:   example-ublox-cellular-psm

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