ublox-cellular-base-n2xx

Revision:
1:d4ff95ab40ae
Child:
3:39eadc84c5ac
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UbloxCellularBaseN2xx.cpp	Mon Jun 26 13:48:04 2017 +0100
@@ -0,0 +1,954 @@
+/* Copyright (c) 2017 ublox Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "UARTSerial.h"
+#include "APN_db.h"
+#include "UbloxCellularBaseN2xx.h"
+#include "onboard_modem_api.h"
+#ifdef FEATURE_COMMON_PAL
+#include "mbed_trace.h"
+#define TRACE_GROUP "UCB"
+#else
+#define tr_debug(format, ...) debug_if(_debug_trace_on, format "\n", ## __VA_ARGS__)
+#define tr_info(format, ...)  debug_if(_debug_trace_on, format "\n", ## __VA_ARGS__)
+#define tr_warn(format, ...)  debug_if(_debug_trace_on, format "\n", ## __VA_ARGS__)
+#define tr_error(format, ...) debug_if(_debug_trace_on, format "\n", ## __VA_ARGS__)
+#endif
+
+#define ATOK _at->recv("OK")
+
+/**********************************************************************
+ * PRIVATE METHODS
+ **********************************************************************/
+
+void UbloxCellularBaseN2xx::set_nwk_reg_status_csd(int status)
+{
+    switch (status) {
+        case CSD_NOT_REGISTERED_NOT_SEARCHING:
+        case CSD_NOT_REGISTERED_SEARCHING:
+            tr_info("Not (yet) registered for circuit switched service");
+            break;
+        case CSD_REGISTERED:
+        case CSD_REGISTERED_ROAMING:
+            tr_info("Registered for circuit switched service");
+            break;
+        case CSD_REGISTRATION_DENIED:
+            tr_info("Circuit switched service denied");
+            break;
+        case CSD_UNKNOWN_COVERAGE:
+            tr_info("Out of circuit switched service coverage");
+            break;
+        case CSD_SMS_ONLY:
+            tr_info("SMS service only");
+            break;
+        case CSD_SMS_ONLY_ROAMING:
+            tr_info("SMS service only");
+            break;
+        case CSD_CSFB_NOT_PREFERRED:
+            tr_info("Registered for circuit switched service with CSFB not preferred");
+            break;
+        default:
+            tr_info("Unknown circuit switched service registration status. %d", status);
+            break;
+    }
+
+    _dev_info.reg_status_csd = static_cast<NetworkRegistrationStatusCsd>(status);
+}
+
+void UbloxCellularBaseN2xx::set_nwk_reg_status_psd(int status)
+{
+    switch (status) {
+        case PSD_NOT_REGISTERED_NOT_SEARCHING:
+        case PSD_NOT_REGISTERED_SEARCHING:
+            tr_info("Not (yet) registered for packet switched service");
+            break;
+        case PSD_REGISTERED:
+        case PSD_REGISTERED_ROAMING:
+            tr_info("Registered for packet switched service");
+            break;
+        case PSD_REGISTRATION_DENIED:
+            tr_info("Packet switched service denied");
+            break;
+        case PSD_UNKNOWN_COVERAGE:
+            tr_info("Out of packet switched service coverage");
+            break;
+        case PSD_EMERGENCY_SERVICES_ONLY:
+            tr_info("Limited access for packet switched service. Emergency use only.");
+            break;
+        default:
+            tr_info("Unknown packet switched service registration status. %d", status);
+            break;
+    }
+
+    _dev_info.reg_status_psd = static_cast<NetworkRegistrationStatusPsd>(status);
+}
+
+void UbloxCellularBaseN2xx::set_nwk_reg_status_eps(int status)
+{
+    switch (status) {
+        case EPS_NOT_REGISTERED_NOT_SEARCHING:
+        case EPS_NOT_REGISTERED_SEARCHING:
+            tr_info("Not (yet) registered for EPS service");
+            break;
+        case EPS_REGISTERED:
+        case EPS_REGISTERED_ROAMING:
+            tr_info("Registered for EPS service");
+            break;
+        case EPS_REGISTRATION_DENIED:
+            tr_info("EPS service denied");
+            break;
+        case EPS_UNKNOWN_COVERAGE:
+            tr_info("Out of EPS service coverage");
+            break;
+        case EPS_EMERGENCY_SERVICES_ONLY:
+            tr_info("Limited access for EPS service. Emergency use only.");
+            break;
+        default:
+            tr_info("Unknown EPS service registration status. %d", status);
+            break;
+    }
+
+    _dev_info.reg_status_eps = static_cast<NetworkRegistrationStatusEps>(status);
+}
+
+void UbloxCellularBaseN2xx::set_rat(int AcTStatus)
+{
+    switch (AcTStatus) {
+        case GSM:
+        case COMPACT_GSM:
+            tr_info("Connected in GSM");
+            break;
+        case UTRAN:
+            tr_info("Connected to UTRAN");
+            break;
+        case EDGE:
+            tr_info("Connected to EDGE");
+            break;
+        case HSDPA:
+            tr_info("Connected to HSDPA");
+            break;
+        case HSUPA:
+            tr_info("Connected to HSPA");
+            break;
+        case HSDPA_HSUPA:
+            tr_info("Connected to HDPA/HSPA");
+            break;
+        case LTE:
+            tr_info("Connected to LTE");
+            break;
+        default:
+            tr_info("Unknown RAT %d", AcTStatus);
+            break;
+    }
+
+    _dev_info.rat = static_cast<RadioAccessNetworkType>(AcTStatus);
+}
+
+bool UbloxCellularBaseN2xx::get_sara_n2xx_info()
+{
+    return (
+        cgmi(_sara_n2xx_info.cgmi) &&
+        cgmm(_sara_n2xx_info.cgmm) &&
+        cgmr(_sara_n2xx_info.cgmr) &&
+        cgsn(1, _sara_n2xx_info.cgsn)
+    );
+}
+
+bool UbloxCellularBaseN2xx::at_req(const char *cmd, const char *recvFormat, const char *response) {
+    bool success = false;         
+    LOCK();
+    
+    MBED_ASSERT(_at != NULL);
+
+    
+    tr_debug("ATREQ: %s => %s", cmd, recvFormat);
+    if(_at->send(cmd) && _at->recv(recvFormat, response) && ATOK) {
+        tr_debug("ATRESULT: %s", response);
+        success = true;        
+    } else {
+        tr_error("ATRESULT: No Answer!");
+    }
+    
+
+    UNLOCK();     
+    return success;
+}
+
+bool UbloxCellularBaseN2xx::at_req(const char *cmd, const char *recvFormat, int *response) {
+    bool success = false;         
+    LOCK();
+    
+    MBED_ASSERT(_at != NULL);
+
+    tr_debug("ATREQ: %s => %s", cmd, recvFormat);
+    if(_at->send(cmd) && _at->recv(recvFormat, response) && ATOK) {
+        tr_debug("ATRESULT: %d", *response);
+        success = true;        
+    }  else {
+        tr_error("ATRESULT: No Answer!");
+    }
+
+    UNLOCK();     
+    return success;
+}
+
+bool UbloxCellularBaseN2xx::at_send(const char *cmd) {
+    bool success = false;         
+    LOCK();
+    
+    MBED_ASSERT(_at != NULL);
+
+    tr_debug("ATSEND: %s", cmd);
+    if(_at->send(cmd) && ATOK) {
+        success = true;        
+    } else {
+        tr_error("Failed to send %s", cmd);
+    }
+
+    UNLOCK();     
+    return success;
+}
+
+bool UbloxCellularBaseN2xx::at_send(const char *cmd, int n) {
+    bool success = false;         
+    LOCK();
+    
+    MBED_ASSERT(_at != NULL);
+
+    tr_debug("ATSEND: %s, %d", cmd, n);
+    if(_at->send(cmd, n) && ATOK) {
+        success = true;        
+    } else {
+        tr_error("Failed to send %s,%d", cmd, n);
+    }
+
+    UNLOCK();     
+    return success;
+}
+
+bool UbloxCellularBaseN2xx::at_send(const char *cmd, const char *arg) {
+    bool success = false;         
+    LOCK();
+    
+    MBED_ASSERT(_at != NULL);
+
+    tr_debug("ATSEND: %s,%s", cmd, arg);
+    if(_at->send(cmd, arg) && ATOK) {
+        success = true;        
+    } else {
+        tr_error("Failed to send %s,%s", cmd, arg);
+    }
+
+    UNLOCK();     
+    return success;
+}
+
+bool UbloxCellularBaseN2xx::cgmi(const char *response)
+{
+    return at_req("AT+CGMI", "%32[^\n]\n", response);
+}
+
+bool UbloxCellularBaseN2xx::cgmm(const char *response) {
+    return at_req("AT+CGMM", "%32[^\n]\n", response);
+}
+
+bool UbloxCellularBaseN2xx::cimi(const char *response) {
+    return at_req("AT+CIMI", "%32[^\n]\n", response);
+}
+
+bool UbloxCellularBaseN2xx::ccid(const char *response) {
+    return at_req("AT+NCCID", "+NCCID:%32[^\n]\n", response);
+}
+
+bool UbloxCellularBaseN2xx::cgmr(const char *response) {
+    return at_req("AT+CGMR", "%32[^\n]\n", response);
+}
+
+bool UbloxCellularBaseN2xx::cgsn(int snt, const char *response) {
+    char cmd[10];
+    sprintf(cmd, "AT+CGSN=%d", snt);
+    
+    return at_req(cmd, "+CGSN:%32[^\n]\n", response);
+}
+
+bool UbloxCellularBaseN2xx::cereg(int n) {        
+    return at_send("AT+CEREG=%d", n);  
+}
+
+nsapi_error_t UbloxCellularBaseN2xx::get_cereg() {    
+    nsapi_error_t r = NSAPI_ERROR_DEVICE_ERROR;    
+    LOCK();
+    
+    MBED_ASSERT(_at != NULL);
+    
+    // The response will be handled by the CEREG URC, by waiting for the OK we know it has been serviced.
+    if (at_send("AT+CEREG?")){ 
+        r = _dev_info.reg_status_eps;
+    }
+    
+    UNLOCK();
+    return r;
+}
+
+nsapi_error_t UbloxCellularBaseN2xx::get_cscon() {
+    char resp[3+1];
+    
+    int n, stat;
+        
+    if (at_req("AT+CSCON?", "+CSCON:%3[^\n]\n", resp) &&  
+        sscanf(resp, "%d,%d", &n, &stat)) {
+            return stat;
+    }
+    
+    return NSAPI_ERROR_DEVICE_ERROR;
+}
+
+nsapi_error_t UbloxCellularBaseN2xx::get_csq() {
+    char resp[5+1];    
+    nsapi_error_t rssi = NSAPI_ERROR_DEVICE_ERROR;
+        
+    LOCK();
+    MBED_ASSERT(_at != NULL);
+        
+    if (at_req("AT+CSQ", "+CSQ:%5[^\n]\n", resp) &&  
+        sscanf(resp, "%d,%*d", &rssi)) {
+            return rssi;
+    }    
+    
+    UNLOCK();    
+    return rssi;
+}
+
+bool UbloxCellularBaseN2xx::cops(const char *plmn) {    
+    return at_send("AT+COPS=1,2,\"%s\"", plmn);
+}
+
+bool UbloxCellularBaseN2xx::cops(int mode) {    
+    return at_send("AT+COPS=%d", mode);    
+}
+
+bool UbloxCellularBaseN2xx::get_cops(int *status) {    
+    return at_req("AT+COPS?", "+COPS: %d", status);
+}
+
+bool UbloxCellularBaseN2xx::cfun(int mode) {
+    return at_send("AT+CFUN=%d", mode);
+}
+
+bool UbloxCellularBaseN2xx::reboot() {
+    return at_send("AT+NRB");    
+}
+
+bool UbloxCellularBaseN2xx::auto_connect(bool state) {
+    return nconfig("AUTOCONNECT", state);
+}
+
+bool UbloxCellularBaseN2xx::nconfig(const char *name, bool state) {    
+    char n[50];
+    
+    if (state)
+        sprintf(n, "AT+NCONFIG=%s,TRUE", name);
+    else
+        sprintf(n, "AT+NCONFIG=%s,FALSE", name);      
+    
+    return at_send(n);
+}
+
+bool UbloxCellularBaseN2xx::get_iccid()
+{    
+    // Returns the ICCID (Integrated Circuit Card ID) of the SIM-card.
+    // ICCID is a serial number identifying the SIM.
+    // AT Command Manual UBX-13002752, section 4.12
+    bool success = ccid(_dev_info.iccid);
+    tr_info("DevInfo: ICCID=%s", _dev_info.iccid);
+    
+    return success;
+}
+
+bool UbloxCellularBaseN2xx::get_imsi()
+{    
+    // International mobile subscriber identification
+    // AT Command Manual UBX-13002752, section 4.11
+    bool success = cimi(_dev_info.imsi);
+    tr_info("DevInfo: IMSI=%s", _dev_info.imsi);
+
+    return success;
+}
+
+bool UbloxCellularBaseN2xx::get_imei()
+{
+    // International mobile equipment identifier
+    // AT Command Manual UBX-13002752, section 4.7
+    bool success = cgsn(1, _dev_info.imei);
+    tr_info("DevInfo: IMEI=%s", _dev_info.imei);
+
+    return success;
+}
+
+bool UbloxCellularBaseN2xx::get_meid()
+{
+    // *** NOT IMPLEMENTED 
+    return false;
+    
+    bool success;
+    LOCK();
+
+    MBED_ASSERT(_at != NULL);
+    
+    // Mobile equipment identifier 
+    // AT Command Manual UBX-13002752, section 4.8
+    success = _at->send("AT+GSN") && _at->recv("%18[^\n]\nOK\n", _dev_info.meid);
+    
+    // tr_info("DevInfo: MEID=%s", _dev_info.meid);
+
+    UNLOCK();
+    return success;
+}
+
+bool UbloxCellularBaseN2xx::set_sms()
+{
+    // *** NOT IMPLEMENTED
+    return false;      
+    
+    bool success = false;
+    char buf[32];
+    LOCK();
+
+    MBED_ASSERT(_at != NULL);
+
+    // Set up SMS format and enable URC
+    // AT Command Manual UBX-13002752, section 11
+    if (_at->send("AT+CMGF=1") && _at->recv("OK")) {
+        tr_debug("SMS in text mode");
+        if (_at->send("AT+CNMI=2,1") && _at->recv("OK")) {
+            tr_debug("SMS URC enabled");
+            // Set to CS preferred since PS preferred doesn't work
+            // on some networks
+            if (_at->send("AT+CGSMS=1") && _at->recv("OK")) {
+                tr_debug("SMS set to CS preferred");
+                success = true;
+                memset (buf, 0, sizeof (buf));
+                if (_at->send("AT+CSCA?") &&
+                    _at->recv("+CSCA: \"%31[^\"]\"", buf) &&
+                    _at->recv("OK")) {
+                    tr_info("SMS Service Centre address is \"%s\"", buf);
+                }
+            }
+        }
+    }
+
+    UNLOCK();
+    return success;
+}
+
+void UbloxCellularBaseN2xx::parser_abort_cb()
+{
+    _at->abort(); 
+}
+
+// Callback for CME ERROR and CMS ERROR.
+void UbloxCellularBaseN2xx::CMX_ERROR_URC()
+{
+    char buf[48];
+
+    if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
+        tr_debug("AT error %s", buf);
+    }
+    parser_abort_cb();
+}
+
+// Callback for EPS registration URC.
+void UbloxCellularBaseN2xx::CEREG_URC()
+{
+    char buf[20];
+    int status;
+    int n, AcT;
+    char tac[4], ci[4]; 
+
+    // If this is the URC it will be a single
+    // digit followed by \n.  If it is the
+    // answer to a CEREG query, it will be
+    // a ": %d,%d\n" where the second digit
+    // indicates the status
+    // Note: not calling _at->recv() from here as we're
+    // already in an _at->recv()
+    // We also hanlde the extended 4 or 5 argument 
+    // response if cereg is set to 2.
+    if (read_at_to_newline(buf, sizeof (buf)) > 0) {
+        if (sscanf(buf, ":%d,%d,%[0123456789abcdef],%[0123456789abcdef],%d\n", &n, &status, tac, ci, &AcT) == 5) {
+            set_nwk_reg_status_eps(status);
+        } else if (sscanf(buf, ":%d,%[0123456789abcdef],%[0123456789abcdef],%d\n`", &status, tac, ci, &AcT) == 4) {
+            set_nwk_reg_status_eps(status);
+        } else if (sscanf(buf, ":%d,%d\n", &n, &status) == 2) {
+            set_nwk_reg_status_eps(status);
+        } else if (sscanf(buf, ":%d\n", &status) == 1) {
+            set_nwk_reg_status_eps(status);
+        }        
+    }
+}
+
+/**********************************************************************
+ * PROTECTED METHODS
+ **********************************************************************/
+
+#if MODEM_ON_BOARD
+void UbloxCellularBaseN2xx::modem_init()
+{
+    ::onboard_modem_init();
+}
+
+void UbloxCellularBaseN2xx::modem_deinit()
+{
+    ::onboard_modem_deinit();
+}
+
+void UbloxCellularBaseN2xx::modem_power_up()
+{
+    ::onboard_modem_power_up();
+}
+
+void UbloxCellularBaseN2xx::modem_power_down()
+{
+    ::onboard_modem_power_down();
+}
+#else
+void UbloxCellularBaseN2xx::modem_init()
+{
+    // Meant to be overridden
+    #error need to do something here!
+}
+
+void UbloxCellularBaseN2xx::modem_deinit()
+{
+    // Meant to be overridden
+}
+
+void UbloxCellularBaseN2xx::modem_power_up()
+{
+    // Meant to be overridden
+}
+
+void UbloxCellularBaseN2xx::modem_power_down()
+{
+    // Mmeant to be overridden
+}
+#endif
+
+// Constructor.
+// Note: to allow this base class to be inherited as a virtual base class
+// by everyone, it takes no parameters.  See also comment above classInit()
+// in the header file.
+UbloxCellularBaseN2xx::UbloxCellularBaseN2xx()
+{  
+    tr_debug("UbloxATCellularBaseN2xx Constructor");
+    
+    _pin = NULL;
+    _at = NULL;
+    _at_timeout = AT_PARSER_TIMEOUT;
+    _fh = NULL;
+    _modem_initialised = false;
+    _sim_pin_check_enabled = false;
+    _debug_trace_on = false;
+
+    _dev_info.dev = DEV_TYPE_NONE;
+    _dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING;
+    _dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING;
+    _dev_info.reg_status_eps = EPS_NOT_REGISTERED_NOT_SEARCHING;
+}
+
+// Destructor.
+UbloxCellularBaseN2xx::~UbloxCellularBaseN2xx()
+{
+    deinit();
+    delete _at;
+    delete _fh;
+}
+
+// Initialise the portions of this class that are parameterised.
+void UbloxCellularBaseN2xx::baseClassInit(PinName tx, PinName rx,
+                                      int baud, bool debug_on)
+{
+    // Only initialise ourselves if it's not already been done
+    if (_at == NULL) {
+        if (_debug_trace_on == false) {
+            _debug_trace_on = debug_on;
+        }
+
+        // Set up File Handle for buffered serial comms with cellular module
+        // (which will be used by the AT parser)
+        _fh = new UARTSerial(tx, rx, baud);
+
+        // Set up the AT parser
+        _at = new ATCmdParser(_fh, OUTPUT_ENTER_KEY, AT_PARSER_BUFFER_SIZE,
+                           _at_timeout, _debug_trace_on);
+
+        // Error cases, out of band handling
+        _at->oob("ERROR", callback(this, &UbloxCellularBaseN2xx::parser_abort_cb));
+        _at->oob("+CME ERROR", callback(this, &UbloxCellularBaseN2xx::CMX_ERROR_URC));
+        _at->oob("+CMS ERROR", callback(this, &UbloxCellularBaseN2xx::CMX_ERROR_URC));
+
+        // Registration status, out of band handling
+        _at->oob("+CEREG", callback(this, &UbloxCellularBaseN2xx::CEREG_URC));
+    }
+}
+
+// Set the AT parser timeout.
+// Note: the AT interface should be locked before this is called.
+void UbloxCellularBaseN2xx::at_set_timeout(int timeout) {
+
+    MBED_ASSERT(_at != NULL);
+
+    _at_timeout = timeout;
+    _at->set_timeout(timeout);
+}
+
+// Read up to size bytes from the AT interface up to a "end".
+// Note: the AT interface should be locked before this is called.
+int UbloxCellularBaseN2xx::read_at_to_char(char * buf, int size, char end)
+{
+    int count = 0;
+    int x = 0;
+
+    if (size > 0) {
+        for (count = 0; (count < size) && (x >= 0) && (x != end); count++) {
+            x = _at->getc();
+            *(buf + count) = (char) x;
+        }
+
+        count--;
+        *(buf + count) = 0;
+
+        // Convert line endings:
+        // If end was '\n' (0x0a) and the preceding character was 0x0d, then
+        // overwrite that with null as well.
+        if ((count > 0) && (end == '\n') && (*(buf + count - 1) == '\x0d')) {
+            count--;
+            *(buf + count) = 0;
+        }
+    }
+
+    return count;
+}
+
+// Power up the modem.
+// Enables the GPIO lines to the modem and then wriggles the power line in short pulses.
+bool UbloxCellularBaseN2xx::power_up()
+{
+    bool success = false;
+    int at_timeout;
+    LOCK();
+
+    at_timeout = _at_timeout; // Has to be inside LOCK()s
+
+    MBED_ASSERT(_at != NULL);
+
+    /* Initialize GPIO lines */
+    tr_info("Powering up modem...");
+    onboard_modem_init();
+    /* Give SARA-N2XX time to reset */
+    tr_debug("Waiting for 5 seconds (booting SARA-N2xx)...");
+    wait_ms(5000);
+
+    at_set_timeout(1000);
+    for (int retry_count = 0; !success && (retry_count < 20); retry_count++) {      
+        _at->flush();        
+        if (at_send("AT")) {
+            success = true;
+        }
+    }
+    at_set_timeout(at_timeout);
+
+    // perform any initialisation AT commands here
+    if (success) {        
+        success = at_send("AT+CMEE=1"); // Turn on verbose responses
+    }
+
+    if (!success) {
+        tr_error("Preliminary modem setup failed.");
+    }
+
+    UNLOCK();
+    return success;
+}
+
+// Power down modem via AT interface.
+void UbloxCellularBaseN2xx::power_down()
+{
+    LOCK();
+
+    MBED_ASSERT(_at != NULL);
+
+    // If we have been running, do a soft power-off first
+    if (_modem_initialised && (_at != NULL)) {
+        // NOT IMPLEMENTED in B656 firmware
+        // at_send("AT+CPWROFF");
+    }
+
+    // Now do a hard power-off
+    onboard_modem_power_down();
+    onboard_modem_deinit();
+
+    _dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING;
+    _dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING;
+    _dev_info.reg_status_eps = EPS_NOT_REGISTERED_NOT_SEARCHING;
+
+   UNLOCK();
+}
+
+// Get the device ID.
+bool UbloxCellularBaseN2xx::set_device_identity(DeviceType *dev)
+{
+    char buf[20];
+    bool success;
+    LOCK();
+
+    MBED_ASSERT(_at != NULL);
+    
+    success = at_req("AT+CGMM", "%19[^\n]\n", buf);
+
+    if (success) {
+        if (strstr(buf, "Neul Hi2110"))
+            *dev = DEV_SARA_N2;           
+    }
+
+    UNLOCK();
+    return success;
+}
+
+// Send initialisation AT commands that are specific to the device.
+bool UbloxCellularBaseN2xx::device_init(DeviceType dev)
+{
+    // SARA-N2xx doesn't have anything to initialise
+    return true;
+}
+
+// Get the SIM card going.
+bool UbloxCellularBaseN2xx::initialise_sim_card()
+{
+    // SARA-N2XX doesn't have any SIM AT Commands for now.
+    return true;    
+}
+
+/**********************************************************************
+ * PUBLIC METHODS
+ **********************************************************************/
+
+// Initialise the modem.
+bool UbloxCellularBaseN2xx::init(const char *pin)
+{
+    MBED_ASSERT(_at != NULL);
+
+    if (!_modem_initialised) {
+        if (power_up()) {
+            tr_info("Modem Ready.");
+            if (pin != NULL) {
+                _pin = pin;
+            }
+            
+            if (initialise_sim_card()) {
+                if (set_device_identity(&_dev_info.dev) && // Set up device identity
+                    device_init(_dev_info.dev) &&
+                    get_sara_n2xx_info()) 
+                    {
+                        tr_debug("CGMM: %s", _sara_n2xx_info.cgmm); 
+                        tr_debug("CGMI: %s", _sara_n2xx_info.cgmi); 
+                        tr_debug("CGMR: %s", _sara_n2xx_info.cgmr); 
+                        tr_debug("CGSN: %s", _sara_n2xx_info.cgsn); 
+                    
+                        // The modem is initialised.  The following checks my still fail,
+                        // of course, but they are all of a "fatal" nature and so we wouldn't
+                        // want to retry them anyway
+                        _modem_initialised = true;
+                }
+            }
+        }
+    }
+
+    return _modem_initialised;
+}
+
+// Perform registration.
+bool UbloxCellularBaseN2xx::nwk_registration()
+{    
+    bool registered = false;
+    int status;
+    int at_timeout;
+    LOCK();
+
+    at_timeout = _at_timeout; // Has to be inside LOCK()s
+
+    MBED_ASSERT(_at != NULL);
+
+    // Enable the packet switched and network registration unsolicited result codes
+    if (cereg(1)) {        
+        // See if we are already in automatic mode
+        if (get_cops(&status)) {
+            if (status != 0) {
+                // Don't check return code here as there's not much
+                // we can do if this fails.
+                cops(0);
+            }
+        }
+        
+        // query cereg just in case
+        get_cereg();
+        registered = is_registered_eps();
+        
+        at_set_timeout(1000);
+        for (int waitSeconds = 0; !registered && (waitSeconds < 180); waitSeconds++) {
+            _at->recv(UNNATURAL_STRING);
+            registered = is_registered_eps();        
+        }
+        at_set_timeout(at_timeout);
+    } else {
+        tr_error("Failed to set CEREG=1");
+    }
+
+    UNLOCK();
+    return registered;
+}
+
+bool UbloxCellularBaseN2xx::is_registered_csd()
+{
+  return (_dev_info.reg_status_csd == CSD_REGISTERED) ||
+          (_dev_info.reg_status_csd == CSD_REGISTERED_ROAMING) ||
+          (_dev_info.reg_status_csd == CSD_CSFB_NOT_PREFERRED);
+}
+
+bool UbloxCellularBaseN2xx::is_registered_psd()
+{
+    return (_dev_info.reg_status_psd == PSD_REGISTERED) ||
+            (_dev_info.reg_status_psd == PSD_REGISTERED_ROAMING);
+}
+
+bool UbloxCellularBaseN2xx::is_registered_eps()
+{
+    return (_dev_info.reg_status_eps == EPS_REGISTERED) ||
+            (_dev_info.reg_status_eps == EPS_REGISTERED_ROAMING);
+}
+
+// Perform deregistration.
+bool UbloxCellularBaseN2xx::nwk_deregistration()
+{
+    bool success = false;
+   
+    MBED_ASSERT(_at != NULL);
+
+    if (cops(2)) {
+        // we need to wait here so that the internal status of the module 
+        wait_ms(1000);
+        
+        _dev_info.reg_status_csd = CSD_NOT_REGISTERED_NOT_SEARCHING;
+        _dev_info.reg_status_psd = PSD_NOT_REGISTERED_NOT_SEARCHING;
+        _dev_info.reg_status_eps = EPS_NOT_REGISTERED_NOT_SEARCHING;        
+        
+        success = true;
+    } else {
+        tr_error("Failed to set COPS=2");
+    }
+
+    return success;
+}
+
+// Put the modem into its lowest power state.
+void UbloxCellularBaseN2xx::deinit()
+{
+    power_down();
+    _modem_initialised = false;
+}
+
+// Set the PIN.
+void UbloxCellularBaseN2xx::set_pin(const char *pin) {
+    _pin = pin;
+}
+
+// Enable or disable SIM pin checking.
+bool UbloxCellularBaseN2xx:: sim_pin_check_enable(bool enableNotDisable)
+{
+    // *** NOT IMPLEMENTED on SARA-N2XX
+    return false;
+
+    bool success = false;
+    LOCK();
+
+    MBED_ASSERT(_at != NULL);
+
+    if (_pin != NULL) {
+        if (_sim_pin_check_enabled && !enableNotDisable) {
+            // Disable the SIM lock
+            if (_at->send("AT+CLCK=\"SC\",0,\"%s\"", _pin) && _at->recv("OK")) {
+                _sim_pin_check_enabled = false;
+                success = true;
+            }
+        } else if (!_sim_pin_check_enabled && enableNotDisable) {
+            // Enable the SIM lock
+            if (_at->send("AT+CLCK=\"SC\",1,\"%s\"", _pin) && _at->recv("OK")) {
+                _sim_pin_check_enabled = true;
+                success = true;
+            }
+        } else {
+            success = true;
+        }
+    }
+
+    UNLOCK();
+    return success;
+}
+
+// Change the pin code for the SIM card.
+bool UbloxCellularBaseN2xx::change_sim_pin(const char *pin)
+{
+    // *** NOT IMPLEMENTED on SARA-N2XX
+    return false;
+
+    bool success = false;
+    LOCK();
+
+    MBED_ASSERT(_at != NULL);
+
+    // Change the SIM pin
+    if ((pin != NULL) && (_pin != NULL)) {
+        if (_at->send("AT+CPWD=\"SC\",\"%s\",\"%s\"", _pin, pin) && _at->recv("OK")) {
+            _pin = pin;
+            success = true;
+        }
+    }
+
+    UNLOCK();
+    return success;
+}
+
+ // Read up to size bytes from the AT interface up to a newline.
+ // This doesn't need a LOCK() UNLOCK() Wrapping as it's only called 
+ // from the URC function, which are already in a lock
+int UbloxCellularBaseN2xx::read_at_to_newline(char * buf, int size)
+{
+    int count = 0;
+    int x = 0;
+
+    if (size > 0) {
+        for (count = 0; (count < size) && (x >= 0) && (x != '\n'); count++) {
+            x = _at->getc();
+            *(buf + count) = (char) x;
+        }
+
+        *(buf + count - 1) = 0;
+        count--;
+    }
+
+    return count;
+}
+// End of File
+