GNSS

Dependents:   ublox-at-cellular-interface-ext

Fork of ublox-at-cellular-interface by u-blox

Files at this revision

API Documentation at this revision

Comitter:
RobMeades
Date:
Mon Jun 12 21:32:21 2017 +0000
Child:
1:bc228becc45d
Commit message:
Initial commit, not yet compiling but I'm tired of doing everything in the on-line IDE so I need to publish to take the code off-line.

Changed in this revision

TESTS/unit_tests/default/main.cpp Show annotated file Show diff for this revision Revisions of this file
TESTS/unit_tests/default/template_mbed_app.txt Show annotated file Show diff for this revision Revisions of this file
UbloxATCellularInterface.cpp Show annotated file Show diff for this revision Revisions of this file
UbloxATCellularInterface.h Show annotated file Show diff for this revision Revisions of this file
ublox-cellular-base.lib Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/unit_tests/default/main.cpp	Mon Jun 12 21:32:21 2017 +0000
@@ -0,0 +1,1070 @@
+#include "ublox_modem_driver/UbloxATCellularInterface.h"
+#include "greentea-client/test_env.h"
+#include "unity.h"
+#include "utest.h"
+#include "UDPSocket.h"
+#ifdef FEATURE_COMMON_PAL
+#include "mbed_trace.h"
+#define TRACE_GROUP "TEST"
+#else
+#define tr_debug(format, ...) debug(format, ## __VA_ARGS__)
+#define tr_info(format, ...)  debug(format, ## __VA_ARGS__)
+#define tr_warn(format, ...)  debug(format, ## __VA_ARGS__)
+#define tr_error(format, ...) debug(format, ## __VA_ARGS__)
+#endif
+
+using namespace utest::v1;
+
+// IMPORTANT!!! if you make a change to the tests here you should
+// check whether the same change should be made to the tests under
+// the GENERIC driver.
+
+// ----------------------------------------------------------------
+// COMPILE-TIME MACROS
+// ----------------------------------------------------------------
+
+// These macros can be overridden with an mbed_app.json file and
+// contents of the following form:
+//
+//{
+//    "config": {
+//        "default-pin": {
+//            "value": "\"1234\""
+//        }
+//}
+//
+// See the template_mbed_app.txt in this directory for a fuller example.
+
+// Whether debug trace is on
+#ifndef MBED_CONF_APP_DEBUG_ON
+# define MBED_CONF_APP_DEBUG_ON false
+#endif
+
+// Run the SIM change tests, which require the DEFAULT_PIN
+// above to be correct for the board on which the test
+// is being run (and the SIM PIN to be disabled before tests run).
+#ifndef MBED_CONF_APP_RUN_SIM_PIN_CHANGE_TESTS
+# define MBED_CONF_APP_RUN_SIM_PIN_CHANGE_TESTS 0
+#endif
+
+#if MBED_CONF_APP_RUN_SIM_PIN_CHANGE_TESTS
+# ifndef MBED_CONF_APP_DEFAULT_PIN
+#   error "MBED_CONF_APP_DEFAULT_PIN must be defined to run the SIM tests"
+# endif
+# ifndef MBED_CONF_APP_ALT_PIN
+#   error "MBED_CONF_APP_ALT_PIN must be defined to run the SIM tests"
+# endif
+# ifndef MBED_CONF_APP_INCORRECT_PIN
+#   error "MBED_CONF_APP_INCORRECT_PIN must be defined to run the SIM tests"
+# endif
+#endif
+
+// The credentials of the SIM in the board.
+#ifndef MBED_CONF_APP_DEFAULT_PIN
+// Note: if PIN is enabled on your SIM, or you wish to run the SIM PIN change
+// tests, you must define the PIN for your SIM (see note above on using
+// mbed_app.json to do so).
+# define MBED_CONF_APP_DEFAULT_PIN "0000"
+#endif
+#ifndef MBED_CONF_APP_APN
+# define MBED_CONF_APP_APN         NULL
+#endif
+#ifndef MBED_CONF_APP_USERNAME
+# define MBED_CONF_APP_USERNAME    NULL
+#endif
+#ifndef MBED_CONF_APP_PASSWORD
+# define MBED_CONF_APP_PASSWORD    NULL
+#endif
+
+// Alternate PIN to use during pin change testing
+#ifndef MBED_CONF_APP_ALT_PIN
+# define MBED_CONF_APP_ALT_PIN    "9876"
+#endif
+
+// A PIN that is definitely incorrect
+#ifndef MBED_CONF_APP_INCORRECT_PIN
+# define MBED_CONF_APP_INCORRECT_PIN "1530"
+#endif
+
+// Servers and ports
+#ifdef MBED_CONF_APP_ECHO_SERVER
+# ifndef MBED_CONF_APP_ECHO_UDP_PORT
+#  error "MBED_CONF_APP_ECHO_UDP_PORT (the port on which your echo server echoes UDP packets) must be defined"
+# endif
+# ifndef MBED_CONF_APP_ECHO_TCP_PORT
+#  error "MBED_CONF_APP_ECHO_TCP_PORT (the port on which your echo server echoes TCP packets) must be defined"
+# endif
+#endif
+
+#ifndef MBED_CONF_APP_NTP_SERVER
+# define MBED_CONF_APP_NTP_SERVER "2.pool.ntp.org"
+#else
+# ifndef MBED_CONF_APP_NTP_PORT
+#  error "MBED_CONF_APP_NTP_PORT must be defined if MBED_CONF_APP_NTP_SERVER is defined"
+# endif
+#endif
+#ifndef MBED_CONF_APP_NTP_PORT
+# define MBED_CONF_APP_NTP_PORT 123
+#endif
+
+#ifndef MBED_CONF_APP_LOCAL_PORT
+# define MBED_CONF_APP_LOCAL_PORT 15
+#endif
+
+// UDP packet size limit for testing
+#ifndef MBED_CONF_APP_UDP_MAX_PACKET_SIZE
+#  define MBED_CONF_APP_UDP_MAX_PACKET_SIZE 1024
+#endif
+
+// The maximum size of UDP data fragmented across
+// multiple packets
+#ifndef MBED_CONF_APP_UDP_MAX_FRAG_PACKET_SIZE
+# define MBED_CONF_APP_UDP_MAX_FRAG_PACKET_SIZE 1500
+#endif
+
+// TCP packet size limit for testing
+#ifndef MBED_CONF_APP_MBED_CONF_APP_TCP_MAX_PACKET_SIZE
+# define MBED_CONF_APP_TCP_MAX_PACKET_SIZE 1500
+#endif
+
+// The number of retries for UDP exchanges
+#define NUM_UDP_RETRIES 5
+
+// How long to wait for stuff to travel in the async echo tests
+#define ASYNC_TEST_WAIT_TIME 10000
+
+// The maximum number of sockets that can be open at one time
+#define MAX_NUM_SOCKETS 7
+
+// ----------------------------------------------------------------
+// PRIVATE VARIABLES
+// ----------------------------------------------------------------
+
+#ifdef FEATURE_COMMON_PAL
+// Lock for debug prints
+static Mutex mtx;
+#endif
+
+// An instance of the cellular driver
+static UbloxATCellularInterface *driver =
+       new UbloxATCellularInterface(MDMTXD, MDMRXD,
+                                    MBED_CONF_UBLOX_CELL_BAUD_RATE,
+                                    MBED_CONF_APP_DEBUG_ON);
+
+// Connection flag
+static bool connection_has_gone_down = false;
+
+// Data to exchange
+static const char send_data[] =  "_____0000:0123456789012345678901234567890123456789"
+                                 "01234567890123456789012345678901234567890123456789"
+                                 "_____0100:0123456789012345678901234567890123456789"
+                                 "01234567890123456789012345678901234567890123456789"
+                                 "_____0200:0123456789012345678901234567890123456789"
+                                 "01234567890123456789012345678901234567890123456789"
+                                 "_____0300:0123456789012345678901234567890123456789"
+                                 "01234567890123456789012345678901234567890123456789"
+                                 "_____0400:0123456789012345678901234567890123456789"
+                                 "01234567890123456789012345678901234567890123456789"
+                                 "_____0500:0123456789012345678901234567890123456789"
+                                 "01234567890123456789012345678901234567890123456789"
+                                 "_____0600:0123456789012345678901234567890123456789"
+                                 "01234567890123456789012345678901234567890123456789"
+                                 "_____0700:0123456789012345678901234567890123456789"
+                                 "01234567890123456789012345678901234567890123456789"
+                                 "_____0800:0123456789012345678901234567890123456789"
+                                 "01234567890123456789012345678901234567890123456789"
+                                 "_____0900:0123456789012345678901234567890123456789"
+                                 "01234567890123456789012345678901234567890123456789"
+                                 "_____1000:0123456789012345678901234567890123456789"
+                                 "01234567890123456789012345678901234567890123456789"
+                                 "_____1100:0123456789012345678901234567890123456789"
+                                 "01234567890123456789012345678901234567890123456789"
+                                 "_____1200:0123456789012345678901234567890123456789"
+                                 "01234567890123456789012345678901234567890123456789"
+                                 "_____1300:0123456789012345678901234567890123456789"
+                                 "01234567890123456789012345678901234567890123456789"
+                                 "_____1400:0123456789012345678901234567890123456789"
+                                 "01234567890123456789012345678901234567890123456789"
+                                 "_____1500:0123456789012345678901234567890123456789"
+                                 "01234567890123456789012345678901234567890123456789"
+                                 "_____1600:0123456789012345678901234567890123456789"
+                                 "01234567890123456789012345678901234567890123456789"
+                                 "_____1700:0123456789012345678901234567890123456789"
+                                 "01234567890123456789012345678901234567890123456789"
+                                 "_____1800:0123456789012345678901234567890123456789"
+                                 "01234567890123456789012345678901234567890123456789"
+                                 "_____1900:0123456789012345678901234567890123456789"
+                                 "01234567890123456789012345678901234567890123456789"
+                                 "_____2000:0123456789012345678901234567890123456789"
+                                 "01234567890123456789012345678901234567890123456789";
+
+// ----------------------------------------------------------------
+// PRIVATE FUNCTIONS
+// ----------------------------------------------------------------
+
+#ifdef FEATURE_COMMON_PAL
+// Locks for debug prints
+static void lock()
+{
+    mtx.lock();
+}
+
+static void unlock()
+{
+    mtx.unlock();
+}
+#endif
+
+// Callback in case the connection goes down
+static void connection_down_cb(nsapi_error_t err)
+{
+    connection_has_gone_down = true;
+}
+
+#ifdef MBED_CONF_APP_ECHO_SERVER
+// Make sure that size is greater than 0 and no more than limit,
+// useful since, when moduloing a very large number number,
+// compilers sometimes screw up and produce a small *negative*
+// number.  Who knew?  For example, GCC decided that
+// 492318453 (0x1d582ef5) modulo 508 was -47 (0xffffffd1).
+static int fix (int size, int limit)
+{
+    if (size <= 0) {
+        size = limit / 2; // better than 1
+    } else if (size > limit) {
+        size = limit;
+    }
+
+    return size;
+}
+
+// Do a UDP socket echo test to a given host of a given packet size
+static void do_udp_echo(UDPSocket *sock, SocketAddress *host_address, int size)
+{
+    bool success = false;
+    void * recv_data = malloc (size);
+    SocketAddress sender_address;
+    TEST_ASSERT(recv_data != NULL);
+
+    // Retry this a few times, don't want to fail due to a flaky link
+    for (int x = 0; !success && (x < NUM_UDP_RETRIES); x++) {
+        tr_debug("Echo testing UDP packet size %d byte(s), try %d.", size, x + 1);
+        if ((sock->sendto(*host_address, (void*) send_data, size) == size) &&
+            (sock->recvfrom(&sender_address, recv_data, size) == size)) {
+            TEST_ASSERT (memcmp(send_data, recv_data, size) == 0);
+            TEST_ASSERT (strcmp(sender_address.get_ip_address(), host_address->get_ip_address()) == 0);
+            TEST_ASSERT (sender_address.get_port() == host_address->get_port());
+            success = true;
+        }
+    }
+    TEST_ASSERT (success);
+    TEST_ASSERT(!connection_has_gone_down);
+
+    free (recv_data);
+}
+
+// The asynchronous callback
+static void async_cb(bool *callback_triggered)
+{
+
+    TEST_ASSERT (callback_triggered != NULL);
+    *callback_triggered = true;
+}
+
+// Do a UDP echo but using the asynchronous driver; we can exchange
+// packets longer in size than one UDP packet this way
+static void do_udp_echo_async(UDPSocket *sock, SocketAddress *host_address,
+                              int size, bool *callback_triggered)
+{
+    void * recv_data = malloc (size);
+    int recv_size = 0;
+    SocketAddress sender_address;
+    Timer timer;
+    int x, y, z;
+    TEST_ASSERT(recv_data != NULL);
+
+    *callback_triggered = false;
+    for (y = 0; (recv_size < size) && (y < NUM_UDP_RETRIES); y++) {
+        tr_debug("Echo testing UDP packet size %d byte(s) async, try %d.", size, y + 1);
+        recv_size = 0;
+        // Retry this a few times, don't want to fail due to a flaky link
+        if (sock->sendto(*host_address, (void *) send_data, size) == size) {
+            // Wait for all the echoed data to arrive
+            timer.start();
+            while ((recv_size < size) && (timer.read_ms() < ASYNC_TEST_WAIT_TIME)) {
+                if (*callback_triggered) {
+                    *callback_triggered = false;
+                    x = sock->recvfrom(&sender_address, (char *) recv_data + recv_size, size);
+                    if (x > 0) {
+                        recv_size += x;
+                    }
+                    tr_debug("%d byte(s) echoed back so far, %d to go.", recv_size, size - recv_size);
+                    TEST_ASSERT(strcmp(sender_address.get_ip_address(), host_address->get_ip_address()) == 0);
+                    TEST_ASSERT(sender_address.get_port() == host_address->get_port());
+                }
+                wait_ms(10);
+            }
+            timer.stop();
+            timer.reset();
+
+            // If everything arrived back, check it's the same as we sent
+            if (recv_size == size) {
+                z = memcmp(send_data, recv_data, size);
+                if (z != 0) {
+                    tr_debug("WARNING: mismatch, retrying");
+                    tr_debug("Sent %d, |%*.*s|", size, size, size, send_data);
+                    tr_debug("Rcvd %d, |%*.*s|", size, size, size, (char *) recv_data);
+                    // If things don't match, it could be due to data loss (this is UDP
+                    // you know...), so set recv_size to 0 to cause another try
+                    recv_size = 0;
+                }
+            }
+        }
+    }
+
+    TEST_ASSERT(recv_size == size);
+    TEST_ASSERT(!connection_has_gone_down);
+
+    free (recv_data);
+}
+
+// Send an entire TCP data buffer until done
+static int sendAll(TCPSocket *sock,  const char *data, int size)
+{
+    int x;
+    int count = 0;
+    Timer timer;
+
+    timer.start();
+    while ((count < size) && (timer.read_ms() < 10000)) {
+        x = sock->send(data + count, size - count);
+        if (x > 0) {
+            count += x;
+            tr_debug("%d byte(s) sent, %d left to send.", count, size - count);
+        }
+        wait_ms(10);
+    }
+    timer.stop();
+
+    return count;
+}
+
+// Do a TCP echo but using the asynchronous driver
+static void do_tcp_echo_async(TCPSocket *sock, int size, bool *callback_triggered)
+{
+    void * recv_data = malloc (size);
+    int recv_size = 0;
+    int x, y;
+    Timer timer;
+    TEST_ASSERT(recv_data != NULL);
+
+    *callback_triggered = false;
+    tr_debug("Echo testing TCP packet size %d byte(s) async.", size);
+    TEST_ASSERT (sendAll(sock, send_data, size) == size);
+
+    // Wait for all the echoed data to arrive
+    timer.start();
+    while ((recv_size < size) && (timer.read_ms() < ASYNC_TEST_WAIT_TIME)) {
+        if (*callback_triggered) {
+            *callback_triggered = false;
+            x = sock->recv((char *) recv_data + recv_size, size);
+            TEST_ASSERT(x > 0);
+            recv_size += x;
+            tr_debug("%d byte(s) echoed back so far, %d to go.", recv_size, size - recv_size);
+        }
+        wait_ms(10);
+    }
+    TEST_ASSERT(recv_size == size);
+    y = memcmp(send_data, recv_data, size);
+    if (y != 0) {
+        tr_debug("Sent %d, |%*.*s|", size, size, size, send_data);
+        tr_debug("Rcvd %d, |%*.*s|", size, size, size, (char *) recv_data);
+        TEST_ASSERT(false);
+    }
+    timer.stop();
+
+    TEST_ASSERT(!connection_has_gone_down);
+
+    free (recv_data);
+}
+#endif
+
+// Get NTP time from a socket
+static void do_ntp_sock (UDPSocket *sock, SocketAddress ntp_address)
+{
+    char ntp_values[48] = { 0 };
+    time_t timestamp = 0;
+    struct tm *localTime;
+    char timeString[25];
+    time_t TIME1970 = 2208988800U;
+    int len;
+    bool comms_done = false;
+
+    ntp_values[0] = '\x1b';
+
+    // Retry this a few times, don't want to fail due to a flaky link
+    for (unsigned int x = 0; !comms_done && (x < NUM_UDP_RETRIES); x++) {
+        sock->sendto(ntp_address, (void*) ntp_values, sizeof(ntp_values));
+        len = sock->recvfrom(&ntp_address, (void*) ntp_values, sizeof(ntp_values));
+        if (len > 0) {
+            comms_done = true;
+        }
+    }
+    TEST_ASSERT (comms_done);
+
+    tr_debug("UDP: %d byte(s) returned by NTP server.", len);
+    if (len >= 43) {
+        timestamp |= ((int) *(ntp_values + 40)) << 24;
+        timestamp |= ((int) *(ntp_values + 41)) << 16;
+        timestamp |= ((int) *(ntp_values + 42)) << 8;
+        timestamp |= ((int) *(ntp_values + 43));
+        timestamp -= TIME1970;
+        srand (timestamp);
+        tr_debug("srand() called");
+        localTime = localtime(&timestamp);
+        if (localTime) {
+            if (strftime(timeString, sizeof(timeString), "%a %b %d %H:%M:%S %Y", localTime) > 0) {
+                printf("NTP timestamp is %s.\n", timeString);
+            }
+        }
+    }
+}
+
+// Get NTP time
+static void do_ntp(UbloxATCellularInterface *driver)
+{
+    UDPSocket sock;
+    SocketAddress host_address;
+
+    TEST_ASSERT(sock.open(driver) == 0)
+
+    TEST_ASSERT(driver->gethostbyname(MBED_CONF_APP_NTP_SERVER, &host_address) == 0);
+    host_address.set_port(MBED_CONF_APP_NTP_PORT);
+
+    tr_debug("UDP: NIST server %s address: %s on port %d.", MBED_CONF_APP_NTP_SERVER,
+             host_address.get_ip_address(), host_address.get_port());
+
+    sock.set_timeout(10000);
+
+    do_ntp_sock(&sock, host_address);
+
+    sock.close();
+}
+
+// Use a connection, checking that it is good
+static void use_connection(UbloxATCellularInterface *driver)
+{
+    const char * ip_address = driver->get_ip_address();
+    const char * net_mask = driver->get_netmask();
+    const char * gateway = driver->get_gateway();
+
+    TEST_ASSERT(driver->is_connected());
+
+    TEST_ASSERT(ip_address != NULL);
+    tr_debug ("IP address %s.", ip_address);
+    TEST_ASSERT(net_mask == NULL);
+    tr_debug ("Net mask %s.", net_mask);
+    TEST_ASSERT(gateway != NULL);
+    tr_debug ("Gateway %s.", gateway);
+
+    do_ntp(driver);
+    TEST_ASSERT(!connection_has_gone_down);
+}
+
+// Drop a connection and check that it has dropped
+static void drop_connection(UbloxATCellularInterface *driver)
+{
+    TEST_ASSERT(driver->disconnect() == 0);
+    TEST_ASSERT(connection_has_gone_down);
+    connection_has_gone_down = false;
+    TEST_ASSERT(!driver->is_connected());
+}
+
+// ----------------------------------------------------------------
+// TESTS
+// ----------------------------------------------------------------
+
+// Call srand() using the NTP server
+void test_set_randomise() {
+    UDPSocket sock;
+    SocketAddress host_address;
+
+    TEST_ASSERT(driver->connect(MBED_CONF_APP_DEFAULT_PIN, MBED_CONF_APP_APN,
+                                MBED_CONF_APP_USERNAME, MBED_CONF_APP_PASSWORD) == 0);
+    do_ntp(driver);
+    TEST_ASSERT(!connection_has_gone_down);
+    drop_connection(driver);
+}
+
+#ifdef MBED_CONF_APP_ECHO_SERVER
+
+// Test UDP data exchange
+void test_udp_echo() {
+    UDPSocket sock;
+    SocketAddress host_address;
+    SocketAddress local_address;
+    int x;
+    int size;
+
+    TEST_ASSERT(driver->connect(MBED_CONF_APP_DEFAULT_PIN, MBED_CONF_APP_APN,
+                                MBED_CONF_APP_USERNAME, MBED_CONF_APP_PASSWORD) == 0);
+
+    TEST_ASSERT(driver->gethostbyname(MBED_CONF_APP_ECHO_SERVER, &host_address) == 0);
+    host_address.set_port(MBED_CONF_APP_ECHO_UDP_PORT);
+
+    tr_debug("UDP: Server %s address: %s on port %d.", MBED_CONF_APP_ECHO_SERVER,
+             host_address.get_ip_address(), host_address.get_port());
+
+    TEST_ASSERT(sock.open(driver) == 0)
+
+    // Do a bind, just for the helluvit
+    local_address.set_port(MBED_CONF_APP_LOCAL_PORT);
+    TEST_ASSERT(sock.bind(local_address) == 0);
+
+    sock.set_timeout(10000);
+
+    // Test min, max, and some random sizes in-between
+    do_udp_echo(&sock, &host_address, 1);
+    do_udp_echo(&sock, &host_address, MBED_CONF_APP_UDP_MAX_PACKET_SIZE);
+    for (x = 0; x < 10; x++) {
+        size = (rand() % MBED_CONF_APP_UDP_MAX_PACKET_SIZE) + 1;
+        size = fix(size, MBED_CONF_APP_UDP_MAX_PACKET_SIZE);
+        do_udp_echo(&sock, &host_address, size);
+    }
+
+    sock.close();
+    drop_connection(driver);
+    tr_debug("%d UDP packets of size up to %d byte(s) echoed successfully.",
+             x, MBED_CONF_APP_UDP_MAX_PACKET_SIZE);
+}
+
+// Test many different sizes of UDP data arriving at once
+void  test_udp_echo_recv_sizes() {
+    UDPSocket sock;
+    SocketAddress host_address;
+    int x, y, z;
+    int size;
+    int tries = 0;
+    unsigned int offset;
+    char * recv_data;
+    bool packetLoss;
+    bool sendSuccess;
+    Timer timer;
+
+    driver->deinit();
+    TEST_ASSERT(driver->connect(MBED_CONF_APP_DEFAULT_PIN, MBED_CONF_APP_APN,
+                                MBED_CONF_APP_USERNAME, MBED_CONF_APP_PASSWORD) == 0);
+
+    TEST_ASSERT(driver->gethostbyname(MBED_CONF_APP_ECHO_SERVER, &host_address) == 0);
+    host_address.set_port(MBED_CONF_APP_ECHO_UDP_PORT);
+
+    tr_debug("UDP: Server %s address: %s on port %d.", MBED_CONF_APP_ECHO_SERVER,
+             host_address.get_ip_address(), host_address.get_port());
+
+    TEST_ASSERT(sock.open(driver) == 0)
+
+    do {
+        tr_debug("--- UDP packet size test, test try %d, flushing input buffers", tries + 1);
+        // First of all, clear any junk from the socket
+        sock.set_timeout(1000);
+        recv_data = (char *) malloc (MBED_CONF_APP_UDP_MAX_PACKET_SIZE);
+        TEST_ASSERT(recv_data != NULL);
+        while (sock.recvfrom(&host_address, (void *) recv_data, MBED_CONF_APP_UDP_MAX_PACKET_SIZE) > 0) {
+            // Throw it away
+        }
+        free (recv_data);
+
+        sock.set_timeout(10000);
+
+        // Throw random sized UDP packets up...
+        x = 0;
+        offset = 0;
+        while (offset < sizeof (send_data)) {
+            size = (rand() % (MBED_CONF_APP_UDP_MAX_PACKET_SIZE / 2)) + 1;
+            size = fix(size, MBED_CONF_APP_UDP_MAX_PACKET_SIZE / 2);
+            if (offset + size > sizeof (send_data)) {
+                size = sizeof (send_data) - offset;
+            }
+            sendSuccess = false;
+            for (y = 0; !sendSuccess && (y < NUM_UDP_RETRIES); y++) {
+                tr_debug("Sending UDP packet number %d, size %d byte(s), send try %d.", x + 1, size, y + 1);
+                if (sock.sendto(host_address, (void *) (send_data + offset), size) == size) {
+                    sendSuccess = true;
+                    offset += size;
+                }
+            }
+            TEST_ASSERT(sendSuccess);
+            x++;
+        }
+        tr_debug("--- All UDP packets sent");
+
+        // ...and capture them all again afterwards
+        recv_data = (char *) malloc (sizeof (send_data));
+        TEST_ASSERT(recv_data != NULL);
+        memset (recv_data, 0, sizeof (send_data));
+        size = 0;
+        y = 0;
+        packetLoss = false;
+        timer.start();
+        while ((size < (int) sizeof (send_data)) && (timer.read_ms() < 10000)) {
+            y = sock.recvfrom(&host_address, (void *) (recv_data + size), sizeof (send_data) - size);
+            if (y > 0) {
+                size += y;
+            }
+        }
+        timer.stop();
+        timer.reset();
+        tr_debug(   "--- Either received everything back or timed out waiting");
+
+        // Check that we reassembled everything correctly
+        if (size == sizeof (send_data)) {
+            for (x = 0; ((*(recv_data + x) == *(send_data + x))) && (x < (int) sizeof (send_data)); x++) {
+            }
+            if (x != sizeof (send_data)) {
+                y = x - 5;
+                if (y < 0) {
+                    y = 0;
+                }
+                z = 10;
+                if (y + z > (int) sizeof (send_data)) {
+                    z = sizeof(send_data) - y;
+                }
+                tr_debug("   --- Difference at character %d (send \"%*.*s\", recv \"%*.*s\")",
+                         x + 1, z, z, send_data + y, z, z, recv_data + y);
+                packetLoss = true;
+            }
+        } else {
+            tr_debug("   --- %d bytes missing (%d bytes received when %d were expected))",
+                      sizeof (send_data) - size, size, sizeof (send_data));
+            packetLoss = true;
+        }
+        free (recv_data);
+        tries++;
+    } while (packetLoss && (tries < NUM_UDP_RETRIES));
+
+    TEST_ASSERT(!packetLoss);
+    TEST_ASSERT(!connection_has_gone_down);
+    sock.close();
+    drop_connection(driver);
+}
+
+// Test UDP data exchange via the asynchronous sigio() mechanism
+void test_udp_echo_async() {
+    UDPSocket sock;
+    SocketAddress host_address;
+    SocketAddress local_address;
+    bool callback_triggered = false;
+    int x;
+    int size;
+
+    driver->deinit();
+    TEST_ASSERT(driver->connect(MBED_CONF_APP_DEFAULT_PIN, MBED_CONF_APP_APN,
+                                MBED_CONF_APP_USERNAME, MBED_CONF_APP_PASSWORD) == 0);
+
+    TEST_ASSERT(driver->gethostbyname(MBED_CONF_APP_ECHO_SERVER, &host_address) == 0);
+    host_address.set_port(MBED_CONF_APP_ECHO_UDP_PORT);
+
+    tr_debug("UDP: Server %s address: %s on port %d.", MBED_CONF_APP_ECHO_SERVER,
+             host_address.get_ip_address(), host_address.get_port());
+
+    TEST_ASSERT(sock.open(driver) == 0)
+
+    // Set up the async callback and set the timeout to zero
+    sock.sigio(callback(async_cb, &callback_triggered));
+    sock.set_timeout(0);
+
+    // Test min, max, and some random sizes in-between
+    // and this time allow the UDP packets to be fragmented
+    do_udp_echo_async(&sock, &host_address, 1, &callback_triggered);
+    do_udp_echo_async(&sock, &host_address, MBED_CONF_APP_UDP_MAX_FRAG_PACKET_SIZE,
+                      &callback_triggered);
+    for (x = 0; x < 10; x++) {
+        size = (rand() % MBED_CONF_APP_UDP_MAX_FRAG_PACKET_SIZE) + 1;
+        size = fix(size, MBED_CONF_APP_UDP_MAX_FRAG_PACKET_SIZE);
+        do_udp_echo_async(&sock, &host_address, size, &callback_triggered);
+    }
+
+    sock.close();
+
+    drop_connection(driver);
+
+    tr_debug("%d UDP packets of size up to %d byte(s) echoed asynchronously and successfully.",
+             x, MBED_CONF_APP_UDP_MAX_FRAG_PACKET_SIZE);
+}
+
+// Test many different sizes of TCP data arriving at once
+void  test_tcp_echo_recv_sizes() {
+    TCPSocket sock;
+    SocketAddress host_address;
+    int x, y, z;
+    int size;
+    unsigned int offset;
+    char * recv_data;
+    Timer timer;
+
+    driver->deinit();
+    TEST_ASSERT(driver->connect(MBED_CONF_APP_DEFAULT_PIN, MBED_CONF_APP_APN,
+                                MBED_CONF_APP_USERNAME, MBED_CONF_APP_PASSWORD) == 0);
+
+    TEST_ASSERT(driver->gethostbyname(MBED_CONF_APP_ECHO_SERVER, &host_address) == 0);
+    host_address.set_port(MBED_CONF_APP_ECHO_TCP_PORT);
+
+    tr_debug("TCP: Server %s address: %s on port %d.", MBED_CONF_APP_ECHO_SERVER,
+             host_address.get_ip_address(), host_address.get_port());
+
+    TEST_ASSERT(sock.open(driver) == 0)
+
+    TEST_ASSERT(sock.connect(host_address) == 0);
+
+    sock.set_timeout(10000);
+
+    // Throw random sized TCP packets up...
+    x = 0;
+    offset = 0;
+    while (offset < sizeof (send_data)) {
+        size = (rand() % (MBED_CONF_APP_UDP_MAX_PACKET_SIZE / 2)) + 1;
+        size = fix(size, MBED_CONF_APP_UDP_MAX_PACKET_SIZE / 2);
+        if (offset + size > sizeof (send_data)) {
+            size = sizeof (send_data) - offset;
+        }
+        tr_debug("Sending TCP packet number %d, size %d byte(s).", x + 1, size);
+        TEST_ASSERT(sendAll(&sock, (send_data + offset), size) == size);
+        offset += size;
+        x++;
+    }
+
+    // ...and capture them all again afterwards
+    recv_data = (char *) malloc (sizeof (send_data));
+    TEST_ASSERT(recv_data != NULL);
+    memset (recv_data, 0, sizeof (send_data));
+    size = 0;
+    x = 0;
+    timer.start();
+    while ((size < (int) sizeof (send_data)) && (timer.read_ms() < 30000)) {
+        y = sock.recv((void *) (recv_data + size), sizeof (send_data) - size);
+        tr_debug("Received TCP packet number %d, size %d byte(s).", x, y);
+        size += y;
+        x++;
+    }
+    timer.stop();
+    timer.reset();
+
+    // Check that we reassembled everything correctly
+    for (x = 0; ((*(recv_data + x) == *(send_data + x))) && (x < (int) sizeof (send_data)); x++) {
+    }
+    if (x != sizeof (send_data)) {
+        y = x - 5;
+        if (y < 0) {
+            y = 0;
+        }
+        z = 10;
+        if (y + z > (int) sizeof (send_data)) {
+            z = sizeof(send_data) - y;
+        }
+        tr_debug("Difference at character %d (send \"%*.*s\", recv \"%*.*s\")",
+                 x + 1, z, z, send_data + y, z, z, recv_data + y);
+        TEST_ASSERT(false);
+    }
+    free (recv_data);
+
+    TEST_ASSERT(!connection_has_gone_down);
+    sock.close();
+    drop_connection(driver);
+}
+
+// Test TCP data exchange via the asynchronous sigio() mechanism
+void test_tcp_echo_async() {
+    TCPSocket sock;
+    SocketAddress host_address;
+    bool callback_triggered = false;
+    int x;
+    int size;
+
+    driver->deinit();
+    TEST_ASSERT(driver->connect(MBED_CONF_APP_DEFAULT_PIN, MBED_CONF_APP_APN,
+                                MBED_CONF_APP_USERNAME, MBED_CONF_APP_PASSWORD) == 0);
+
+    TEST_ASSERT(driver->gethostbyname(MBED_CONF_APP_ECHO_SERVER, &host_address) == 0);
+    host_address.set_port(MBED_CONF_APP_ECHO_TCP_PORT);
+
+    tr_debug("TCP: Server %s address: %s on port %d.", MBED_CONF_APP_ECHO_SERVER,
+             host_address.get_ip_address(), host_address.get_port());
+
+    TEST_ASSERT(sock.open(driver) == 0)
+
+    // Set up the async callback and set the timeout to zero
+    sock.sigio(callback(async_cb, &callback_triggered));
+    sock.set_timeout(0);
+
+    TEST_ASSERT(sock.connect(host_address) == 0);
+    // Test min, max, and some random sizes in-between
+    do_tcp_echo_async(&sock, 1, &callback_triggered);
+    do_tcp_echo_async(&sock, MBED_CONF_APP_TCP_MAX_PACKET_SIZE, &callback_triggered);
+    for (x = 0; x < 10; x++) {
+        size = (rand() % MBED_CONF_APP_TCP_MAX_PACKET_SIZE) + 1;
+        size = fix(size, MBED_CONF_APP_TCP_MAX_PACKET_SIZE);
+        do_tcp_echo_async(&sock, size, &callback_triggered);
+    }
+
+    sock.close();
+
+    drop_connection(driver);
+
+    tr_debug("%d TCP packets of size up to %d byte(s) echoed asynchronously and successfully.",
+             x, MBED_CONF_APP_TCP_MAX_PACKET_SIZE);
+}
+#endif
+
+// Allocate max sockets
+void test_max_sockets() {
+    UDPSocket sock[MAX_NUM_SOCKETS];
+    UDPSocket sockNone;
+    SocketAddress host_address;
+
+    TEST_ASSERT(driver->connect(MBED_CONF_APP_DEFAULT_PIN, MBED_CONF_APP_APN,
+                                 MBED_CONF_APP_USERNAME, MBED_CONF_APP_PASSWORD) == 0);
+
+    TEST_ASSERT(driver->gethostbyname(MBED_CONF_APP_NTP_SERVER, &host_address) == 0);
+    host_address.set_port(MBED_CONF_APP_NTP_PORT);
+
+    // Open the first socket and use it
+    TEST_ASSERT(sock[0].open(driver) == 0)
+    sock[0].set_timeout(10000);
+    do_ntp_sock(&sock[0], host_address);
+
+    // Check that we stop being able to get sockets at the max number
+    for (int x = 1; x < (int) (sizeof (sock) / sizeof (sock[0])); x++) {
+        TEST_ASSERT(sock[x].open(driver) == 0)
+    }
+    TEST_ASSERT(sockNone.open(driver) < 0);
+
+    // Now use the last one
+    sock[sizeof (sock) / sizeof (sock[0]) - 1].set_timeout(10000);
+    do_ntp_sock(&sock[sizeof (sock) / sizeof (sock[0]) - 1], host_address);
+
+    // Close all of the sockets
+    for (int x = 0; x < (int) (sizeof (sock) / sizeof (sock[0])); x++) {
+        TEST_ASSERT(sock[x].close() == 0);
+    }
+
+    drop_connection(driver);
+}
+
+// Connect with credentials included in the connect request
+void test_connect_credentials() {
+
+    driver->deinit();
+    TEST_ASSERT(driver->connect(MBED_CONF_APP_DEFAULT_PIN, MBED_CONF_APP_APN,
+                                MBED_CONF_APP_USERNAME, MBED_CONF_APP_PASSWORD) == 0);
+    use_connection(driver);
+    drop_connection(driver);
+}
+
+// Test with credentials preset
+void test_connect_preset_credentials() {
+
+    driver->deinit();
+    TEST_ASSERT(driver->init(MBED_CONF_APP_DEFAULT_PIN));
+    driver->set_credentials(MBED_CONF_APP_APN, MBED_CONF_APP_USERNAME,
+                            MBED_CONF_APP_PASSWORD);
+    TEST_ASSERT(driver->connect(MBED_CONF_APP_DEFAULT_PIN) == 0);
+    use_connection(driver);
+    drop_connection(driver);
+}
+
+// Test adding and using a SIM pin, then removing it, using the pending
+// mechanism where the change doesn't occur until connect() is called
+void test_check_sim_pin_pending() {
+
+    driver->deinit();
+
+    // Enable PIN checking (which will use the current PIN)
+    // and also flag that the PIN should be changed to MBED_CONF_APP_ALT_PIN,
+    // then try connecting
+    driver->set_sim_pin_check(true);
+    driver->set_new_sim_pin(MBED_CONF_APP_ALT_PIN);
+    TEST_ASSERT(driver->connect(MBED_CONF_APP_DEFAULT_PIN, MBED_CONF_APP_APN,
+                                MBED_CONF_APP_USERNAME, MBED_CONF_APP_PASSWORD) == 0);
+    use_connection(driver);
+    drop_connection(driver);
+    driver->deinit();
+
+    // Now change the PIN back to what it was before
+    driver->set_new_sim_pin(MBED_CONF_APP_DEFAULT_PIN);
+    TEST_ASSERT(driver->connect(MBED_CONF_APP_ALT_PIN, MBED_CONF_APP_APN,
+                                MBED_CONF_APP_USERNAME, MBED_CONF_APP_PASSWORD) == 0);
+    use_connection(driver);
+    drop_connection(driver);
+    driver->deinit();
+
+    // Check that it was changed back, and this time
+    // use the other way of entering the PIN
+    driver->set_sim_pin(MBED_CONF_APP_DEFAULT_PIN);
+    TEST_ASSERT(driver->connect(NULL, MBED_CONF_APP_APN, MBED_CONF_APP_USERNAME,
+                                MBED_CONF_APP_PASSWORD) == 0);
+    use_connection(driver);
+    drop_connection(driver);
+    driver->deinit();
+
+    // Remove PIN checking again and check that it no
+    // longer matters what the PIN is
+    driver->set_sim_pin_check(false);
+    TEST_ASSERT(driver->connect(MBED_CONF_APP_DEFAULT_PIN, MBED_CONF_APP_APN,
+                                MBED_CONF_APP_USERNAME, MBED_CONF_APP_PASSWORD) == 0);
+    use_connection(driver);
+    drop_connection(driver);
+    driver->deinit();
+    TEST_ASSERT(driver->init(NULL));
+    TEST_ASSERT(driver->connect(MBED_CONF_APP_INCORRECT_PIN, MBED_CONF_APP_APN,
+                                MBED_CONF_APP_USERNAME, MBED_CONF_APP_PASSWORD) == 0);
+    use_connection(driver);
+    drop_connection(driver);
+
+    // Put the SIM pin back to the correct value for any subsequent tests
+    driver->set_sim_pin(MBED_CONF_APP_DEFAULT_PIN);
+}
+
+// Test adding and using a SIM pin, then removing it, using the immediate
+// mechanism
+void test_check_sim_pin_immediate() {
+
+    driver->deinit();
+    driver->connection_status_cb(connection_down_cb);
+
+    // Enable PIN checking (which will use the current PIN), change
+    // the PIN to MBED_CONF_APP_ALT_PIN, then try connecting after powering on and
+    // off the modem
+    driver->set_sim_pin_check(true, true, MBED_CONF_APP_DEFAULT_PIN);
+    driver->set_new_sim_pin(MBED_CONF_APP_ALT_PIN, true);
+    driver->deinit();
+    TEST_ASSERT(driver->init(NULL));
+    TEST_ASSERT(driver->connect(MBED_CONF_APP_ALT_PIN, MBED_CONF_APP_APN,
+                                MBED_CONF_APP_USERNAME, MBED_CONF_APP_PASSWORD) == 0);
+    use_connection(driver);
+    drop_connection(driver);
+
+    driver->connection_status_cb(connection_down_cb);
+
+    // Now change the PIN back to what it was before
+    driver->set_new_sim_pin(MBED_CONF_APP_DEFAULT_PIN, true);
+    driver->deinit();
+    driver->set_sim_pin(MBED_CONF_APP_DEFAULT_PIN);
+    TEST_ASSERT(driver->init(NULL));
+    TEST_ASSERT(driver->connect(NULL, MBED_CONF_APP_APN, MBED_CONF_APP_USERNAME,
+                                MBED_CONF_APP_PASSWORD) == 0);
+    use_connection(driver);
+    drop_connection(driver);
+
+    driver->connection_status_cb(connection_down_cb);
+
+    // Remove PIN checking again and check that it no
+    // longer matters what the PIN is
+    driver->set_sim_pin_check(false, true);
+    driver->deinit();
+    TEST_ASSERT(driver->init(MBED_CONF_APP_INCORRECT_PIN));
+    TEST_ASSERT(driver->connect(NULL, MBED_CONF_APP_APN, MBED_CONF_APP_USERNAME,
+                                MBED_CONF_APP_PASSWORD) == 0);
+    use_connection(driver);
+    drop_connection(driver);
+
+    // Put the SIM pin back to the correct value for any subsequent tests
+    driver->set_sim_pin(MBED_CONF_APP_DEFAULT_PIN);
+}
+
+// Test being able to connect with a local instance of the driver
+// NOTE: since this local instance will fiddle with bits of HW that the
+// static instance thought it owned, the static instance will no longer
+// work afterwards, hence this must be run as the last test in the list
+void test_connect_local_instance_last_test() {
+
+    UbloxATCellularInterface *pLocalInterface = NULL;
+
+    pLocalInterface = new UbloxATCellularInterface(MDMTXD, MDMRXD,
+                                                   MBED_CONF_UBLOX_CELL_BAUD_RATE,
+                                                   MBED_CONF_APP_DEBUG_ON);
+    pLocalInterface->connection_status_cb(connection_down_cb);
+
+    TEST_ASSERT(pLocalInterface->connect(MBED_CONF_APP_DEFAULT_PIN, MBED_CONF_APP_APN,
+                                         MBED_CONF_APP_USERNAME, MBED_CONF_APP_PASSWORD) == 0);
+    use_connection(pLocalInterface);
+    drop_connection(pLocalInterface);
+    delete pLocalInterface;
+
+    pLocalInterface = new UbloxATCellularInterface(MDMTXD, MDMRXD,
+                                                   MBED_CONF_UBLOX_CELL_BAUD_RATE,
+                                                   MBED_CONF_APP_DEBUG_ON);
+    pLocalInterface->connection_status_cb(connection_down_cb);
+
+    TEST_ASSERT(pLocalInterface->connect(MBED_CONF_APP_DEFAULT_PIN, MBED_CONF_APP_APN,
+                                         MBED_CONF_APP_USERNAME, MBED_CONF_APP_PASSWORD) == 0);
+    use_connection(pLocalInterface);
+    drop_connection(pLocalInterface);
+    delete pLocalInterface;
+}
+
+// ----------------------------------------------------------------
+// TEST ENVIRONMENT
+// ----------------------------------------------------------------
+
+// Setup the test environment
+utest::v1::status_t test_setup(const size_t number_of_cases) {
+    // Setup Greentea with a timeout
+    GREENTEA_SETUP(960, "default_auto");
+    return verbose_test_setup_handler(number_of_cases);
+}
+
+// IMPORTANT!!! if you make a change to the tests here you should
+// check whether the same change should be made to the tests under
+// the GENERIC driver.
+
+// Test cases
+Case cases[] = {
+    Case("Set randomise", test_set_randomise),
+#ifdef MBED_CONF_APP_ECHO_SERVER
+    Case("UDP echo test", test_udp_echo),
+# ifndef TARGET_UBLOX_C027 // Not enough RAM on little 'ole C027 to run this test
+    Case("UDP recv sizes", test_udp_echo_recv_sizes),
+# endif
+    Case("UDP async echo test", test_udp_echo_async),
+# ifndef TARGET_UBLOX_C027 // Not enough RAM on little 'ole C027 to run this test
+    Case("TCP recv sizes", test_tcp_echo_recv_sizes),
+# endif
+    Case("TCP async echo test", test_tcp_echo_async),
+#endif
+#ifndef TARGET_UBLOX_C027 // Not enough RAM on little 'ole C027 to run this test
+    Case("Alloc max sockets", test_max_sockets),
+#endif
+    Case("Connect with credentials", test_connect_credentials),
+    Case("Connect with preset credentials", test_connect_preset_credentials),
+#if MBED_CONF_APP_RUN_SIM_PIN_CHANGE_TESTS
+    Case("Check SIM pin, pending", test_check_sim_pin_pending),
+    Case("Check SIM pin, immediate", test_check_sim_pin_immediate),
+#endif
+#ifndef TARGET_UBLOX_C027 // Not enough RAM on little 'ole C027 for this
+    Case("Connect using local instance, must be last test", test_connect_local_instance_last_test)
+#endif
+};
+
+Specification specification(test_setup, cases);
+
+// ----------------------------------------------------------------
+// MAIN
+// ----------------------------------------------------------------
+
+int main() {
+
+#ifdef FEATURE_COMMON_PAL
+    mbed_trace_init();
+
+    mbed_trace_mutex_wait_function_set(lock);
+    mbed_trace_mutex_release_function_set(unlock);
+#endif
+
+    driver->connection_status_cb(connection_down_cb);
+
+    // Run tests
+    return !Harness::run(specification);
+}
+
+// End Of File
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/unit_tests/default/template_mbed_app.txt	Mon Jun 12 21:32:21 2017 +0000
@@ -0,0 +1,84 @@
+{
+    "config": {
+        "platform": {
+            "help": "The platform for the cellular feature, e.g. UBLOX or MTS_DRAGONFLY",
+            "value": "UBLOX"
+        },
+        "buffer-size": {
+            "value": 0
+        },
+        "debug-on": {
+            "help": "Set to true to get AT interface debug",
+            "value": false
+        },
+        "run-sim-pin-change-tests": {
+            "help": "If 1, run the SIM PIN change tests, for which default-pin must be defined",
+            "value": 0
+        },
+        "default-pin": {
+            "help": "The current value of the SIM PIN as a string; if PIN is enabled on your SIM, or you wish to run the SIM PIN change tests, you must put the PIN for your SIM here",
+            "value": "\"1234\""
+        },
+        "apn": {
+            "help": "The APN string to use for this SIM/network, set to 0 if none",
+            "value": 0
+        },
+        "username": {
+            "help": "The user name string to use for this APN, set to zero if none",
+            "value": 0
+        },
+        "password": {
+            "help": "The password string to use for this APN, set to 0 if none",
+            "value": 0
+        },
+        "alt-pin": {
+            "help": "The SIM PIN (as a string) that will be used for SIM PIN change tests (the SIM PIN will be changed back to default-pin afterwards)",
+            "value": "\"9876\""
+        },
+        "incorrect-pin": {
+            "help": "A SIM PIN (as a string) that should not be the same as default-pin or alt-pin",
+            "value": "\"1530\""
+        },
+        "echo-server": {
+            "help": "The URL string of the UDP/TCP echo server to use during testing; if this is not defined, no echo tests will be run (and it is REALLY recommended to run them for this driver)",
+            "value": "\"yourechoserver.com\""
+        },
+        "echo-udp-port": {
+            "help": "The port to connect to on echo-server for UDP testing",
+            "value": 7
+        },
+        "echo-tcp-port": {
+            "help": "The port to connect to on echo-server for TCP testing",
+            "value": 7
+        },
+        "ntp-server": {
+            "help": "The URL string of the NTP server to use during testing",
+            "value": "\"2.pool.ntp.org\""
+        },
+        "ntp-port": {
+            "help": "The port to connect to on ntp-server",
+            "value": 123
+        },
+        "local-port": {
+            "help": "The local port to use when testing sock.bind()",
+            "value": 16
+        },
+        "udp-max-packet-size": {
+            "help": "The maximum UDP packet size to use when testing; 1024 bytes is the limit at the AT interface but 508 bytes is considered more reliable for the public internet",
+            "value": 508
+        }
+        "udp-max-frag-packet-size": {
+            "help": "The maximum size of UDP data to test with that we know will be fragmented across multiple UDP packets",
+            "value": 1500
+        }
+    },
+    "target_overrides": {
+        "*": {
+            "target.features_add": ["COMMON_PAL"],
+            "platform.stdio-convert-newlines": true,
+            "platform.stdio-baud-rate": 9600,
+            "platform.default-serial-baud-rate": 115200,
+            "mbed-trace.enable": 1
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UbloxATCellularInterface.cpp	Mon Jun 12 21:32:21 2017 +0000
@@ -0,0 +1,1208 @@
+/* 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 "UbloxATCellularInterface.h"
+#include "mbed_poll.h"
+#include "nsapi.h"
+#include "APN_db.h"
+#ifdef FEATURE_COMMON_PAL
+#include "mbed_trace.h"
+#define TRACE_GROUP "UACI"
+#else
+#define tr_debug(format, ...) debug(format, ## __VA_ARGS__)
+#define tr_info(format, ...)  debug(format, ## __VA_ARGS__)
+#define tr_warn(format, ...)  debug(format, ## __VA_ARGS__)
+#define tr_error(format, ...) debug(format, ## __VA_ARGS__)
+#endif
+
+/**********************************************************************
+ * PRIVATE METHODS
+ **********************************************************************/
+
+// Event thread for asynchronous received data handling.
+void UbloxATCellularInterface::handle_event(){
+    pollfh fhs;
+    int count;
+    int at_timeout;
+
+    fhs.fh = _fh;
+    fhs.events = POLLIN;
+
+    while (true) {
+        count = poll(&fhs, 1, 1000);
+        if (count > 0 && (fhs.revents & POLLIN)) {
+            LOCK();
+            at_timeout = _at_timeout;
+            at_set_timeout(10); // Avoid blocking but also make sure we don't
+                                // time out if we get ahead of the serial port
+            _at->debug_on(false); // Debug here screws with the test output
+            // Let the URCs run
+            _at->recv(UNNATURAL_STRING);
+            _at->debug_on(_debug_trace_on);
+            at_set_timeout(at_timeout);
+            UNLOCK();
+        }
+    }
+}
+
+// Find or create a socket from the list.
+UbloxATCellularInterface::SockCtrl * UbloxATCellularInterface::find_socket(int modem_handle)
+{
+    UbloxATCellularInterface::SockCtrl *socket = NULL;
+
+    for (unsigned int x = 0; (socket == NULL) && (x < sizeof(_sockets) / sizeof(_sockets[0])); x++) {
+        if (_sockets[x].modem_handle == modem_handle) {
+            socket = &(_sockets[x]);
+        }
+    }
+
+    return socket;
+}
+
+// Clear out the storage for a socket
+void UbloxATCellularInterface::clear_socket(UbloxATCellularInterface::SockCtrl * socket)
+{
+    if (socket != NULL) {
+        socket->modem_handle = SOCKET_UNUSED;
+        socket->pending     = 0;
+        socket->callback    = NULL;
+        socket->data        = NULL;
+    }
+}
+
+// Check that a socket pointer is valid
+bool UbloxATCellularInterface::check_socket(SockCtrl * socket)
+{
+    bool success = false;
+
+    if (socket != NULL) {
+        for (unsigned int x = 0; !success && (x < sizeof(_sockets) / sizeof(_sockets[0])); x++) {
+            if (socket == &(_sockets[x])) {
+                success = true;
+            }
+        }
+    }
+
+    return success;
+}
+
+// Convert nsapi_security_t to the modem security numbers
+int UbloxATCellularInterface::nsapi_security_to_modem_security(nsapi_security_t nsapi_security)
+{
+    int modem_security = 3;
+
+    switch (nsapi_security)
+    {
+        case NSAPI_SECURITY_NONE:
+            modem_security = 0;
+            break;
+        case NSAPI_SECURITY_PAP:
+            modem_security = 1;
+            break;
+        case NSAPI_SECURITY_CHAP:
+            modem_security = 2;
+            break;
+        case NSAPI_SECURITY_UNKNOWN:
+            modem_security = 3;
+            break;
+        default:
+            modem_security = 3;
+            break;
+    }
+
+    return modem_security;
+}
+
+// Callback for Socket Read URC.
+void UbloxATCellularInterface::UUSORD_URC()
+{
+    int a;
+    int b;
+    char buf[32];
+    SockCtrl *socket;
+
+    // Note: not calling _at->recv() from here as we're
+    // already in an _at->recv()
+    // +UUSORD: <socket>,<length>
+    if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
+        if (sscanf(buf, ": %d,%d", &a, &b) == 2) {
+            socket = find_socket(a);
+            if (socket != NULL) {
+                socket->pending = b;
+                // No debug prints here as they can affect timing
+                // and cause data loss in UARTSerial
+                if (socket->callback != NULL) {
+                    socket->callback(socket->data);
+                }
+            }
+        }
+    }
+}
+
+// Callback for Socket Read From URC.
+void UbloxATCellularInterface::UUSORF_URC()
+{
+    int a;
+    int b;
+    char buf[32];
+    SockCtrl *socket;
+
+    // Note: not calling _at->recv() from here as we're
+    // already in an _at->recv()
+    // +UUSORF: <socket>,<length>
+    if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
+        if (sscanf(buf, ": %d,%d", &a, &b) == 2) {
+            socket = find_socket(a);
+            if (socket != NULL) {
+                socket->pending = b;
+                // No debug prints here as they can affect timing
+                // and cause data loss in UARTSerial
+                if (socket->callback != NULL) {
+                    socket->callback(socket->data);
+                }
+            }
+        }
+    }
+}
+
+// Callback for Socket Close URC.
+void UbloxATCellularInterface::UUSOCL_URC()
+{
+    int a;
+    char buf[32];
+    SockCtrl *socket;
+
+    // Note: not calling _at->recv() from here as we're
+    // already in an _at->recv()
+    // +UUSOCL: <socket>
+    if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
+        if (sscanf(buf, ": %d", &a) == 1) {
+            socket = find_socket(a);
+            tr_debug("Socket 0x%08x: handle %d closed by remote host",
+                     (unsigned int) socket, a);
+            clear_socket(socket);
+        }
+    }
+}
+
+// Callback for UUPSDD.
+void UbloxATCellularInterface::UUPSDD_URC()
+{
+    int a;
+    char buf[32];
+    SockCtrl *socket;
+
+    // Note: not calling _at->recv() from here as we're
+    // already in an _at->recv()
+    // +UUPSDD: <socket>
+    if (read_at_to_char(buf, sizeof (buf), '\n') > 0) {
+        if (sscanf(buf, ": %d", &a) == 1) {
+            socket = find_socket(a);
+            tr_debug("Socket 0x%08x: handle %d connection lost",
+                     (unsigned int) socket, a);
+            clear_socket(socket);
+            if (_connection_status_cb) {
+                _connection_status_cb(NSAPI_ERROR_CONNECTION_LOST);
+            }
+        }
+    }
+}
+
+/**********************************************************************
+ * PROTECTED METHODS: GENERAL
+ **********************************************************************/
+
+// Get the next set of credentials, based on IMSI.
+void UbloxATCellularInterface::get_next_credentials(const char * config)
+{
+    if (config) {
+        _apn    = _APN_GET(config);
+        _uname  = _APN_GET(config);
+        _pwd    = _APN_GET(config);
+    }
+
+    _apn    = _apn     ?  _apn    : "";
+    _uname  = _uname   ?  _uname  : "";
+    _pwd    = _pwd     ?  _pwd    : "";
+}
+
+// Active a connection profile on board the modem.
+// Note: the AT interface should be locked before this is called.
+bool UbloxATCellularInterface::activate_profile(const char* apn,
+                                                const char* username,
+                                                const char* password,
+                                                nsapi_security_t auth)
+{
+    bool activated = false;
+    bool success = false;
+    int at_timeout = _at_timeout;
+    SocketAddress address;
+
+    // Set up the APN
+    if (*apn) {
+        success = _at->send("AT+UPSD=" PROFILE ",1,\"%s\"", apn) && _at->recv("OK");
+    }
+    if (success && *username) {
+        success = _at->send("AT+UPSD=" PROFILE ",2,\"%s\"", username) && _at->recv("OK");
+    }
+    if (success && *password) {
+        success = _at->send("AT+UPSD=" PROFILE ",3,\"%s\"", password) && _at->recv("OK");
+    }
+
+    if (success) {
+        // Set up dynamic IP address assignment.
+        success = _at->send("AT+UPSD=" PROFILE ",7,\"0.0.0.0\"") && _at->recv("OK");
+        // Set up the authentication protocol
+        // 0 = none
+        // 1 = PAP (Password Authentication Protocol)
+        // 2 = CHAP (Challenge Handshake Authentication Protocol)
+        for (int protocol = nsapi_security_to_modem_security(NSAPI_SECURITY_NONE);
+             success && (protocol <= nsapi_security_to_modem_security(NSAPI_SECURITY_CHAP)); protocol++) {
+            if ((_auth == NSAPI_SECURITY_UNKNOWN) || (nsapi_security_to_modem_security(_auth) == protocol)) {
+                if (_at->send("AT+UPSD=" PROFILE ",6,%d", protocol) && _at->recv("OK")) {
+                    // Activate, waiting 30 seconds for the connection to be made
+                    at_set_timeout(30000);
+                    activated = _at->send("AT+UPSDA=" PROFILE ",3") && _at->recv("OK");
+                    at_set_timeout(at_timeout);
+                }
+            }
+        }
+    }
+
+    return activated;
+}
+
+// Activate a profile by reusing an external PDP context.
+// Note: the AT interface should be locked before this is called.
+bool UbloxATCellularInterface::activate_profile_reuse_external(void)
+{
+    bool success = false;
+    int cid = -1;
+    char ip[NSAPI_IP_SIZE];
+    SocketAddress address;
+    int t;
+    int at_timeout = _at_timeout;
+
+    //+CGDCONT: <cid>,"IP","<apn name>","<ip adr>",0,0,0,0,0,0
+    if (_at->send("AT+CGDCONT?")) {
+        if (_at->recv("+CGDCONT: %d,\"IP\",\"%*[^\"]\",\"%" u_stringify(NSAPI_IP_SIZE) "[^\"]\",%*d,%*d,%*d,%*d,%*d,%*d",
+                      &t, ip) &&
+            _at->recv("OK")) {
+            // Check if the IP address is valid
+            if (address.set_ip_address(ip)) {
+                cid = t;
+            }
+        }
+    }
+
+    // If a context has been found, use it
+    if ((cid != -1) && (_at->send("AT+UPSD=" PROFILE ",100,%d", cid) && _at->recv("OK"))) {
+        // Activate, waiting 30 seconds for the connection to be made
+        at_set_timeout(30000);
+        success = _at->send("AT+UPSDA=" PROFILE ",3") && _at->recv("OK");
+        at_set_timeout(at_timeout);
+    }
+
+    return success;
+}
+
+// Activate a profile by context ID.
+// Note: the AT interface should be locked before this is called.
+bool UbloxATCellularInterface::activate_profile_by_cid(int cid,
+                                                       const char* apn,
+                                                       const char* username,
+                                                       const char* password,
+                                                       nsapi_security_t auth)
+{
+    bool success = false;
+    int at_timeout = _at_timeout;
+
+    if (_at->send("AT+CGDCONT=%d,\"IP\",\"%s\"", cid, apn) && _at->recv("OK") &&
+        _at->send("AT+UAUTHREQ=%d,%d,\"%s\",\"%s\"", cid, nsapi_security_to_modem_security(auth),
+                  username, password) && _at->recv("OK") &&
+        _at->send("AT+UPSD=" PROFILE ",100,%d", cid) && _at->recv("OK")) {
+
+        // Wait 30 seconds for the connection to be made
+        at_set_timeout(30000);
+        // Activate the protocol
+        success = _at->send("AT+UPSDA=" PROFILE ",3") && _at->recv("OK");
+        at_set_timeout(at_timeout);
+    }
+
+    return success;
+}
+
+// Connect the on board IP stack of the modem.
+bool UbloxATCellularInterface::connect_modem_stack()
+{
+    bool success = false;
+    int active = 0;
+    const char * config = NULL;
+    LOCK();
+
+    // Check the profile
+    if (_at->send("AT+UPSND=" PROFILE ",8") && _at->recv("+UPSND: %*d,%*d,%d\n", &active) &&
+        _at->recv("OK")) {
+        if (active == 0) {
+            // If the caller hasn't entered an APN, try to find it
+            if (_apn == NULL) {
+                config = apnconfig(_dev_info.imsi);
+            }
+
+            // Attempt to connect
+            do {
+                // Set up APN and IP protocol for PDP context
+                get_next_credentials(config);
+                _auth = (*_uname && *_pwd) ? _auth : NSAPI_SECURITY_NONE;
+                if ((_dev_info.dev != DEV_TOBY_L2) && (_dev_info.dev != DEV_MPCI_L2)) {
+                    success = activate_profile(_apn, _uname, _pwd, _auth);
+                } else {
+                    success = activate_profile_reuse_external();
+                    if (success) {
+                        tr_debug("Reusing external context");
+                    } else {
+                        success = activate_profile_by_cid(1, _apn, _uname, _pwd, _auth);
+                    }
+                }
+            } while (!success && config && *config);
+        } else {
+            // If the profile is already active, we're good
+            success = true;
+        }
+    }
+
+    if (!success) {
+        tr_error("Failed to connect, check your APN/username/password");
+    }
+
+    UNLOCK();
+    return success;
+}
+
+// Disconnect the on board IP stack of the modem.
+bool UbloxATCellularInterface::disconnect_modem_stack()
+{
+    bool success = false;
+    LOCK();
+
+    if (get_ip_address() != NULL) {
+        if (_at->send("AT+UPSDA=" PROFILE ",4") && _at->recv("OK")) {
+            success = true;
+            if (_connection_status_cb) {
+                _connection_status_cb(NSAPI_ERROR_CONNECTION_LOST);
+            }
+        }
+    }
+
+    UNLOCK();
+    return success;
+}
+
+/**********************************************************************
+ * PROTECTED METHODS: NETWORK INTERFACE and SOCKETS
+ **********************************************************************/
+
+// Gain access to us.
+NetworkStack *UbloxATCellularInterface::get_stack()
+{
+    return this;
+}
+
+// Create a socket.
+nsapi_error_t UbloxATCellularInterface::socket_open(nsapi_socket_t *handle,
+                                                    nsapi_protocol_t proto)
+{
+    nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
+    bool success = false;
+    int modem_handle;
+    SockCtrl *socket;
+    LOCK();
+
+    // Find a free socket
+    socket = find_socket();
+    tr_debug("socket_open(%d)", proto);
+
+    if (socket != NULL) {
+        if (proto == NSAPI_UDP) {
+            success = _at->send("AT+USOCR=17");
+        } else if (proto == NSAPI_TCP) {
+            success = _at->send("AT+USOCR=6");
+        } else  {
+            nsapi_error = NSAPI_ERROR_UNSUPPORTED;
+        }
+
+        if (success) {
+            nsapi_error = NSAPI_ERROR_NO_SOCKET;
+            if (_at->recv("+USOCR: %d\n", &modem_handle) && (modem_handle != SOCKET_UNUSED) &&
+                _at->recv("OK")) {
+                tr_debug("Socket 0x%8x: handle %d was created", (unsigned int) socket, modem_handle);
+                clear_socket(socket);
+                socket->modem_handle         = modem_handle;
+                *handle = (nsapi_socket_t) socket;
+                nsapi_error = NSAPI_ERROR_OK;
+            }
+        }
+    } else {
+        nsapi_error = NSAPI_ERROR_NO_MEMORY;
+    }
+
+    UNLOCK();
+    return nsapi_error;
+}
+
+// Close a socket.
+nsapi_error_t UbloxATCellularInterface::socket_close(nsapi_socket_t handle)
+{
+    nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
+    SockCtrl *socket = (SockCtrl *) handle;
+    LOCK();
+
+    tr_debug("socket_close(0x%08x)", (unsigned int) handle);
+
+    MBED_ASSERT (check_socket(socket));
+
+    if (_at->send("AT+USOCL=%d", socket->modem_handle) &&
+        _at->recv("OK")) {
+        clear_socket(socket);
+        nsapi_error = NSAPI_ERROR_OK;
+    }
+
+    UNLOCK();
+    return nsapi_error;
+}
+
+// Bind a local port to a socket.
+nsapi_error_t UbloxATCellularInterface::socket_bind(nsapi_socket_t handle,
+                                                    const SocketAddress &address)
+{
+    nsapi_error_t nsapi_error = NSAPI_ERROR_NO_SOCKET;
+    int proto;
+    int modem_handle;
+    SockCtrl savedSocket;
+    SockCtrl *socket = (SockCtrl *) handle;
+    LOCK();
+
+    tr_debug("socket_bind(0x%08x, :%d)", (unsigned int) handle, address.get_port());
+
+    MBED_ASSERT (check_socket(socket));
+
+    // Query the socket type
+    if (_at->send("AT+USOCTL=%d,0", socket->modem_handle) &&
+        _at->recv("+USOCTL: %*d,0,%d\n", &proto) &&
+        _at->recv("OK")) {
+        savedSocket = *socket;
+        nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
+        // Now close the socket and re-open it with the binding given
+        if (_at->send("AT+USOCL=%d", socket->modem_handle) &&
+            _at->recv("OK")) {
+            clear_socket(socket);
+            nsapi_error = NSAPI_ERROR_CONNECTION_LOST;
+            if (_at->send("AT+USOCR=%d,%d", proto, address.get_port()) &&
+                _at->recv("+USOCR: %d\n", &modem_handle) && (modem_handle != SOCKET_UNUSED) &&
+                _at->recv("OK")) {
+                *socket = savedSocket;
+                nsapi_error = NSAPI_ERROR_OK;
+            }
+        }
+    }
+
+    UNLOCK();
+    return nsapi_error;
+}
+
+// Connect to a socket
+nsapi_error_t UbloxATCellularInterface::socket_connect(nsapi_socket_t handle,
+                                                       const SocketAddress &address)
+{
+    nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
+    SockCtrl *socket = (SockCtrl *) handle;
+    LOCK();
+
+    tr_debug("socket_connect(0x%08x, %s(:%d))", (unsigned int) handle,
+             address.get_ip_address(), address.get_port());
+
+    MBED_ASSERT (check_socket(socket));
+
+    if (_at->send("AT+USOCO=%d,\"%s\",%d", socket->modem_handle,
+                  address.get_ip_address(), address.get_port()) &&
+        _at->recv("OK")) {
+        nsapi_error = NSAPI_ERROR_OK;
+    }
+
+    UNLOCK();
+    return nsapi_error;
+}
+
+// Send to a socket.
+nsapi_size_or_error_t UbloxATCellularInterface::socket_send(nsapi_socket_t handle,
+                                                            const void *data,
+                                                            nsapi_size_t size)
+{
+    nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR;
+    bool success = true;
+    const char *buf = (const char *) data;
+    nsapi_size_t blk = MAX_WRITE_SIZE;
+    nsapi_size_t count = size;
+    SockCtrl *socket = (SockCtrl *) handle;
+
+    tr_debug("socket_send(0x%08x, 0x%08x, %d)", (unsigned int) handle, (unsigned int) data, size);
+
+    MBED_ASSERT (check_socket(socket));
+
+    while ((count > 0) && success) {
+        if (count < blk) {
+            blk = count;
+        }
+        LOCK();
+
+        if (_at->send("AT+USOWR=%d,%d", socket->modem_handle, blk) && _at->recv("@")) {
+            wait_ms(50);
+            if ((_at->write(buf, blk) < (int) blk) ||
+                 !_at->recv("OK")) {
+                success = false;
+            }
+        } else {
+            success = false;
+        }
+
+        UNLOCK();
+        buf += blk;
+        count -= blk;
+    }
+
+    if (success) {
+        nsapi_error_size = size - count;
+        if (_debug_trace_on) {
+            tr_debug("socket_send: %d \"%*.*s\"", size, size, size, (char *) data);
+        }
+    }
+
+    return nsapi_error_size;
+}
+
+// Send to an IP address.
+nsapi_size_or_error_t UbloxATCellularInterface::socket_sendto(nsapi_socket_t handle,
+                                                              const SocketAddress &address,
+                                                              const void *data,
+                                                              nsapi_size_t size)
+{
+    nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR;
+    bool success = true;
+    const char *buf = (const char *) data;
+    nsapi_size_t blk = MAX_WRITE_SIZE;
+    nsapi_size_t count = size;
+    SockCtrl *socket = (SockCtrl *) handle;
+
+    tr_debug("socket_sendto(0x%8x, %s(:%d), 0x%08x, %d)", (unsigned int) handle,
+             address.get_ip_address(), address.get_port(), (unsigned int) data, size);
+
+    MBED_ASSERT (check_socket(socket));
+
+    if (size > MAX_WRITE_SIZE) {
+        tr_warn("WARNING: packet length %d is too big for one UDP packet (max %d), will be fragmented.", size, MAX_WRITE_SIZE);
+    }
+
+    while ((count > 0) && success) {
+        if (count < blk) {
+            blk = count;
+        }
+        LOCK();
+
+        if (_at->send("AT+USOST=%d,\"%s\",%d,%d", socket->modem_handle,
+                      address.get_ip_address(), address.get_port(), blk) &&
+            _at->recv("@")) {
+            wait_ms(50);
+            if ((_at->write(buf, blk) >= (int) blk) &&
+                 _at->recv("OK")) {
+            } else {
+                success = false;
+            }
+        } else {
+            success = false;
+        }
+
+        UNLOCK();
+        buf += blk;
+        count -= blk;
+    }
+
+    if (success) {
+        nsapi_error_size = size - count;
+        if (_debug_trace_on) {
+            tr_debug("socket_sendto: %d \"%*.*s\"", size, size, size, (char *) data);
+        }
+    }
+
+    return nsapi_error_size;
+}
+
+// Receive from a socket, TCP style.
+nsapi_size_or_error_t UbloxATCellularInterface::socket_recv(nsapi_socket_t handle,
+                                                            void *data,
+                                                            nsapi_size_t size)
+{
+    nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR;
+    bool success = true;
+    char *buf = (char *) data;
+    nsapi_size_t read_blk;
+    nsapi_size_t count = 0;
+    unsigned int usord_sz;
+    int read_sz;
+    Timer timer;
+    SockCtrl *socket = (SockCtrl *) handle;
+    int at_timeout;
+
+    tr_debug("socket_recv(0x%08x, 0x%08x, %d)",
+             (unsigned int) handle, (unsigned int) data, size);
+
+    MBED_ASSERT (check_socket(socket));
+
+    timer.start();
+
+    while (success && (size > 0)) {
+        LOCK();
+        at_timeout = _at_timeout;
+        at_set_timeout(1000);
+
+        read_blk = MAX_READ_SIZE;
+        if (read_blk > size) {
+            read_blk = size;
+        }
+        if (socket->pending > 0) {
+            tr_debug("Socket 0x%08x: modem handle %d has %d byte(s) pending",
+                     (unsigned int) socket, socket->modem_handle, socket->pending);
+            _at->debug_on(false); // ABSOLUTELY no time for debug here if you want to
+                                  // be able to read packets of any size without
+                                  // losing characters in UARTSerial
+            if (_at->send("AT+USORD=%d,%d", socket->modem_handle, read_blk) &&
+                _at->recv("+USORD: %*d,%d,\"", &usord_sz)) {
+                socket->pending -= usord_sz; // Must use what +USORD returns here as it
+                                             // may be less than we asked for
+                // Note: insert no debug between _at->recv() and _at->read(), no time...
+                if (usord_sz > size) {
+                    usord_sz = size;
+                }
+                read_sz = _at->read(buf, usord_sz);
+                if (read_sz > 0) {
+                    tr_debug("...read %d byte(s) from modem handle %d...", read_sz,
+                             socket->modem_handle);
+                    if (_debug_trace_on) {
+                        tr_debug("Read returned %d,  |%*.*s|", read_sz, read_sz, read_sz, buf);
+                    }
+                    count += read_sz;
+                    buf += read_sz;
+                    size -= read_sz;
+                } else {
+                    // read() should not fail
+                    success = false;
+                }
+                tr_debug("Socket 0x%08x: modem handle %d now has only %d byte(s) pending",
+                         (unsigned int) socket, socket->modem_handle, socket->pending);
+                // Wait for the "OK" before continuing
+                _at->recv("OK");
+            } else {
+                // Should never fail to do _at->send()/_at->recv()
+                success = false;
+            }
+            _at->debug_on(_debug_trace_on);
+        } else if (timer.read_ms() < SOCKET_TIMEOUT) {
+            // Wait for URCs
+            _at->recv(UNNATURAL_STRING);
+        } else {
+            if (count == 0) {
+                // Timeout with nothing received
+                nsapi_error_size = NSAPI_ERROR_WOULD_BLOCK;
+                success = false;
+            }
+            size = 0; // This simply to cause an exit
+        }
+
+        at_set_timeout(at_timeout);
+        UNLOCK();
+    }
+    timer.stop();
+
+    if (success) {
+        nsapi_error_size = count;
+    }
+
+    if (_debug_trace_on) {
+        tr_debug("socket_recv: %d \"%*.*s\"", count, count, count, buf - count);
+    } else {
+        tr_debug("socket_recv: received %d byte(s)", count);
+    }
+
+    return nsapi_error_size;
+}
+
+// Receive a packet over a UDP socket.
+nsapi_size_or_error_t UbloxATCellularInterface::socket_recvfrom(nsapi_socket_t handle,
+                                                                SocketAddress *address,
+                                                                void *data,
+                                                                nsapi_size_t size)
+{
+    nsapi_size_or_error_t nsapi_error_size = NSAPI_ERROR_DEVICE_ERROR;
+    bool success = true;
+    char *buf = (char *) data;
+    nsapi_size_t read_blk;
+    nsapi_size_t count = 0;
+    char ipAddress[NSAPI_IP_SIZE];
+    int port;
+    unsigned int usorf_sz;
+    int read_sz;
+    Timer timer;
+    SockCtrl *socket = (SockCtrl *) handle;
+    int at_timeout;
+
+    tr_debug("socket_recvfrom(0x%08x, 0x%08x, %d)",
+             (unsigned int) handle, (unsigned int) data, size);
+
+    MBED_ASSERT (check_socket(socket));
+
+    timer.start();
+
+    while (success && (size > 0)) {
+        LOCK();
+        at_timeout = _at_timeout;
+        at_set_timeout(1000);
+
+        read_blk = MAX_READ_SIZE;
+        if (read_blk > size) {
+            read_blk = size;
+        }
+        if (socket->pending > 0) {
+            tr_debug("Socket 0x%08x: modem handle %d has %d byte(s) pending",
+                     (unsigned int) socket, socket->modem_handle, socket->pending);
+            memset (ipAddress, 0, sizeof (ipAddress)); // Ensure terminator
+
+            // Note: the maximum length of UDP packet we can receive comes from
+            // fitting all of the following into one buffer:
+            //
+            // +USORF: xx,"max.len.ip.address.ipv4.or.ipv6",yyyyy,wwww,"the_data"\r\n
+            //
+            // where xx is the handle, max.len.ip.address.ipv4.or.ipv6 is NSAPI_IP_SIZE,
+            // yyyyy is the port number (max 65536), wwww is the length of the data and
+            // the_data is binary data. I make that 29 + 48 + len(the_data),
+            // so the overhead is 77 bytes.
+
+            _at->debug_on(false); // ABSOLUTELY no time for debug here if you want to
+                                  // be able to read packets of any size without
+                                  // losing characters in UARTSerial
+            if (_at->send("AT+USORF=%d,%d", socket->modem_handle, read_blk) &&
+                _at->recv("+USORF: %*d,\"%" u_stringify(NSAPI_IP_SIZE) "[^\"]\",%d,%d,\"",
+                          ipAddress, &port, &usorf_sz)) {
+                socket->pending -= usorf_sz; // Must use what +USORF returns here as it
+                                             // may be less than we asked for
+                // Note: insert no debug between _at->recv() and _at->read(), no time...
+                if (usorf_sz > size) {
+                    usorf_sz = size;
+                }
+                read_sz = _at->read(buf, usorf_sz);
+                if (read_sz > 0) {
+                    address->set_ip_address(ipAddress);
+                    address->set_port(port);
+                    tr_debug("...read %d byte(s) from modem handle %d...", read_sz,
+                             socket->modem_handle);
+                    if (_debug_trace_on) {
+                        tr_debug("Read returned %d,  |%*.*s|", read_sz, read_sz, read_sz, buf);
+                    }
+                    count += read_sz;
+                    buf += read_sz;
+                    size -= read_sz;
+                    if ((usorf_sz < read_blk) || (usorf_sz == MAX_READ_SIZE)) {
+                        size = 0; // If we've received less than we asked for, or
+                                  // the max size, then a whole UDP packet has arrived and
+                                  // this means DONE.
+                    }
+                } else {
+                    // read() should not fail
+                    success = false;
+                }
+                tr_debug("Socket 0x%08x: modem handle %d now has only %d byte(s) pending",
+                         (unsigned int) socket, socket->modem_handle, socket->pending);
+                // Wait for the "OK" before continuing
+                _at->recv("OK");
+            } else {
+                // Should never fail to do _at->send()/_at->recv()
+                success = false;
+            }
+            _at->debug_on(_debug_trace_on);
+        } else if (timer.read_ms() < SOCKET_TIMEOUT) {
+            // Wait for URCs
+            _at->recv(UNNATURAL_STRING);
+        } else {
+            if (count == 0) {
+                // Timeout with nothing received
+                nsapi_error_size = NSAPI_ERROR_WOULD_BLOCK;
+                success = false;
+            }
+            size = 0; // This simply to cause an exit
+        }
+
+        at_set_timeout(at_timeout);
+        UNLOCK();
+    }
+    timer.stop();
+
+    if (success) {
+        nsapi_error_size = count;
+    }
+
+    if (_debug_trace_on) {
+        tr_debug("socket_recvfrom: %d \"%*.*s\"", count, count, count, buf - count);
+    } else {
+        tr_debug("socket_recvfrom: received %d byte(s)", count);
+    }
+
+    return nsapi_error_size;
+}
+
+// Attach an event callback to a socket, required for asynchronous
+// data reception
+void UbloxATCellularInterface::socket_attach(nsapi_socket_t handle,
+                                             void (*callback)(void *),
+                                             void *data)
+{
+    SockCtrl *socket = (SockCtrl *) handle;
+
+    MBED_ASSERT (check_socket(socket));
+
+    socket->callback = callback;
+    socket->data = data;
+}
+
+// Unsupported TCP server functions.
+nsapi_error_t UbloxATCellularInterface::socket_listen(nsapi_socket_t handle,
+                                                      int backlog)
+{
+    return NSAPI_ERROR_UNSUPPORTED;
+}
+nsapi_error_t UbloxATCellularInterface::socket_accept(nsapi_socket_t server,
+                                                      nsapi_socket_t *handle,
+                                                      SocketAddress *address)
+{
+    return NSAPI_ERROR_UNSUPPORTED;
+}
+
+// Unsupported option functions.
+nsapi_error_t UbloxATCellularInterface::setsockopt(nsapi_socket_t handle,
+                                                   int level, int optname,
+                                                   const void *optval,
+                                                   unsigned optlen)
+{
+    return NSAPI_ERROR_UNSUPPORTED;
+}
+nsapi_error_t UbloxATCellularInterface::getsockopt(nsapi_socket_t handle,
+                                                   int level, int optname,
+                                                   void *optval,
+                                                   unsigned *optlen)
+{
+    return NSAPI_ERROR_UNSUPPORTED;
+}
+
+/**********************************************************************
+ * PUBLIC METHODS
+ **********************************************************************/
+
+// Constructor.
+UbloxATCellularInterface::UbloxATCellularInterface(PinName tx,
+                                                   PinName rx,
+                                                   int baud,
+                                                   bool debug_on)
+{
+    _sim_pin_check_change_pending = false;
+    _sim_pin_check_change_pending_enabled_value = false;
+    _sim_pin_change_pending = false;
+    _sim_pin_change_pending_new_pin_value = NULL;
+    _apn = NULL;
+    _uname = NULL;
+    _pwd = NULL;
+    _connection_status_cb = NULL;
+
+    // Initialise sockets storage
+    memset(_sockets, 0, sizeof(_sockets));
+    for (unsigned int socket = 0; socket < sizeof(_sockets) / sizeof(_sockets[0]); socket++) {
+        _sockets[socket].modem_handle = SOCKET_UNUSED;
+        _sockets[socket].callback = NULL;
+        _sockets[socket].data = NULL;
+    }
+
+    // The authentication to use
+    _auth = NSAPI_SECURITY_UNKNOWN;
+
+    // Nullify the temporary IP address storage
+    _ip = NULL;
+
+    // Initialise the base class, which starts the AT parser
+    baseClassInit(tx, rx, baud, debug_on);
+
+    // Start the event handler thread for Rx data
+    event_thread.start(callback(this, &UbloxATCellularInterface::handle_event));
+
+    // URC handlers for sockets
+    _at->oob("+UUSORD", callback(this, &UbloxATCellularInterface::UUSORD_URC));
+    _at->oob("+UUSORF", callback(this, &UbloxATCellularInterface::UUSORF_URC));
+    _at->oob("+UUSOCL", callback(this, &UbloxATCellularInterface::UUSOCL_URC));
+    _at->oob("+UUPSDD", callback(this, &UbloxATCellularInterface::UUPSDD_URC));
+}
+
+// Destructor.
+UbloxATCellularInterface::~UbloxATCellularInterface()
+{
+    // Free _ip if it was ever allocated
+    free(_ip);
+}
+
+// Set the authentication scheme.
+void UbloxATCellularInterface::set_authentication(nsapi_security_t auth)
+{
+    _auth = auth;
+}
+
+// Set APN, user name and password.
+void  UbloxATCellularInterface::set_credentials(const char *apn,
+                                                const char *uname,
+                                                const char *pwd)
+{
+    _apn = apn;
+    _uname = uname;
+    _pwd = pwd;
+}
+
+// Set PIN.
+void UbloxATCellularInterface::set_sim_pin(const char *pin) {
+    set_pin(pin);
+}
+
+// Get the IP address of a host.
+nsapi_error_t UbloxATCellularInterface::gethostbyname(const char *host,
+                                                      SocketAddress *address,
+                                                      nsapi_version_t version)
+{
+    nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
+    char ipAddress[NSAPI_IP_SIZE];
+
+    if (address->set_ip_address(host)) {
+        nsapi_error = NSAPI_ERROR_OK;
+    } else {
+        LOCK();
+        memset (ipAddress, 0, sizeof (ipAddress)); // Ensure terminator
+        if (_at->send("AT+UDNSRN=0,\"%s\"", host) &&
+            _at->recv("+UDNSRN: \"%" u_stringify(NSAPI_IP_SIZE) "[^\"]\"", ipAddress) &&
+            _at->recv("OK")) {
+            if (address->set_ip_address(ipAddress)) {
+                nsapi_error = NSAPI_ERROR_OK;
+            }
+        }
+        UNLOCK();
+    }
+
+    return nsapi_error;
+}
+
+// Make a cellular connection
+nsapi_error_t UbloxATCellularInterface::connect(const char *sim_pin,
+                                                const char *apn,
+                                                const char *uname,
+                                                const char *pwd)
+{
+    nsapi_error_t nsapi_error;
+
+    if (sim_pin != NULL) {
+        _pin = sim_pin;
+    }
+
+    if (apn != NULL) {
+        _apn = apn;
+    }
+
+    if ((uname != NULL) && (pwd != NULL)) {
+        _uname = uname;
+        _pwd = pwd;
+    } else {
+        _uname = NULL;
+        _pwd = NULL;
+    }
+
+    nsapi_error = connect();
+
+    return nsapi_error;
+}
+
+// Make a cellular connection using the IP stack on board the cellular modem
+nsapi_error_t UbloxATCellularInterface::connect()
+{
+    nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
+    bool registered = false;
+
+    // Set up modem and then register with the network
+    if (init()) {
+        nsapi_error = NSAPI_ERROR_NO_CONNECTION;
+        // Perform any pending SIM actions
+        if (_sim_pin_check_change_pending) {
+            if (!sim_pin_check_enable(_sim_pin_check_change_pending_enabled_value)) {
+                nsapi_error = NSAPI_ERROR_AUTH_FAILURE;
+            }
+            _sim_pin_check_change_pending = false;
+        }
+        if (_sim_pin_change_pending) {
+            if (!change_sim_pin(_sim_pin_change_pending_new_pin_value)) {
+                nsapi_error = NSAPI_ERROR_AUTH_FAILURE;
+            }
+            _sim_pin_change_pending = false;
+        }
+
+        if (nsapi_error == NSAPI_ERROR_NO_CONNECTION) {
+            for (int retries = 0; !registered && (retries < 3); retries++) {
+                if (nwk_registration()) {
+                    registered = true;;
+                }
+            }
+        }
+    }
+
+    // Attempt to establish a connection
+    if (registered && connect_modem_stack()) {
+        nsapi_error = NSAPI_ERROR_OK;
+    }
+
+    return nsapi_error;
+}
+
+// User initiated disconnect.
+nsapi_error_t UbloxATCellularInterface::disconnect()
+{
+    nsapi_error_t nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
+
+    if (disconnect_modem_stack() && nwk_deregistration()) {
+        nsapi_error = NSAPI_ERROR_OK;
+    }
+
+    return nsapi_error;
+}
+
+// Enable or disable SIM PIN check lock.
+nsapi_error_t UbloxATCellularInterface::set_sim_pin_check(bool set,
+                                                          bool immediate,
+                                                          const char *sim_pin)
+{
+    nsapi_error_t nsapi_error = NSAPI_ERROR_AUTH_FAILURE;
+
+    if (sim_pin != NULL) {
+        _pin = sim_pin;
+    }
+
+    if (immediate) {
+        if (init()) {
+            if (sim_pin_check_enable(set)) {
+                nsapi_error = NSAPI_ERROR_OK;
+            }
+        } else {
+            nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
+        }
+    } else {
+        nsapi_error = NSAPI_ERROR_OK;
+        _sim_pin_check_change_pending = true;
+        _sim_pin_check_change_pending_enabled_value = set;
+    }
+
+    return nsapi_error;
+}
+
+// Change the PIN code for the SIM card.
+nsapi_error_t UbloxATCellularInterface::set_new_sim_pin(const char *new_pin,
+                                                        bool immediate,
+                                                        const char *old_pin)
+{
+    nsapi_error_t nsapi_error = NSAPI_ERROR_AUTH_FAILURE;
+
+    if (old_pin != NULL) {
+        _pin = old_pin;
+    }
+
+    if (immediate) {
+        if (init()) {
+            if (change_sim_pin(new_pin)) {
+                nsapi_error = NSAPI_ERROR_OK;
+            }
+        } else {
+            nsapi_error = NSAPI_ERROR_DEVICE_ERROR;
+        }
+    } else {
+        nsapi_error = NSAPI_ERROR_OK;
+        _sim_pin_change_pending = true;
+        _sim_pin_change_pending_new_pin_value = new_pin;
+    }
+
+    return nsapi_error;
+}
+
+// Determine if the connection is up.
+bool UbloxATCellularInterface::is_connected()
+{
+    return get_ip_address() != NULL;
+}
+
+// Get the IP address of the on-board modem IP stack.
+const char * UbloxATCellularInterface::get_ip_address()
+{
+    SocketAddress address;
+    LOCK();
+
+    if (_ip == NULL) {
+        // Temporary storage for an IP address string with terminator
+        _ip = (char *) malloc(NSAPI_IP_SIZE);
+    }
+
+    if (_ip != NULL) {
+        memset(_ip, 0, NSAPI_IP_SIZE); // Ensure a terminator
+        // +UPSND=<profile_id>,<param_tag>[,<dynamic_param_val>]
+        // If we get back a quoted "w.x.y.z" then we have an IP address,
+        // otherwise we don't.
+        if (!_at->send("AT+UPSND=" PROFILE ",0") ||
+            !_at->recv("+UPSND: " PROFILE ",0,\"%" u_stringify(NSAPI_IP_SIZE) "[^\"]\"", _ip) ||
+            !_at->recv("OK") ||
+            !address.set_ip_address(_ip) || // Return NULL if the address is not a valid one
+            !address) { // Return null if the address is zero
+            free (_ip);
+            _ip = NULL;
+        }
+    }
+
+    UNLOCK();
+    return _ip;
+}
+
+// Get the local network mask.
+const char *UbloxATCellularInterface::get_netmask()
+{
+    // Not implemented.
+    return NULL;
+}
+
+// Get the local gateways.
+const char *UbloxATCellularInterface::get_gateway()
+{
+    return get_ip_address();
+}
+
+// Callback in case the connection is lost.
+void UbloxATCellularInterface::connection_status_cb(Callback<void(nsapi_error_t)> cb)
+{
+    _connection_status_cb = cb;
+}
+
+// End of file
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UbloxATCellularInterface.h	Mon Jun 12 21:32:21 2017 +0000
@@ -0,0 +1,625 @@
+/* Copyright (c) 2017 ARM 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.
+ */
+
+#ifndef _UBLOX_CELLULAR_DRIVER_GEN_AT_DATA_
+#define _UBLOX_CELLULAR_DRIVER_GEN_AT_DATA_
+
+#include "ublox_modem_driver/UbloxCellularBase.h"
+#include "netsocket/CellularBase.h"
+#include "NetworkStack.h"
+
+/** UbloxATCellularInterface class.
+ *
+ *  This uses the cellular-tuned IP stack that
+ *  is on-board the cellular modem instead of the
+ *  LWIP stack on the mbed MCU.
+ *
+ *  There are three advantages to using this mechanism:
+ *
+ *  1.  Since the modem interface remains in AT mode
+ *      throughout, it is possible to continue using
+ *      any AT commands (e.g. send SMS, use the module's
+ *      file system, etc.) while the connection is up.
+ *
+ *  2.  The UbloxATCellularInterfaceExt class can
+ *      be used to perform very simple HTTP and FTP
+ *      operations using the modem's on-board clients.
+ *
+ *  3.  LWIP is not required (and hence RAM is saved).
+ *
+ *  The disadvantage is that some additional parsing
+ *  (at the AT interface) has to go on in order to exchange
+ *  IP packets, so this is less efficient under heavy loads.
+ *  Also TCP Server and getting/setting of socket options is
+ *  currently not supported.
+ */
+
+// Forward declaration
+class NetworkStack;
+
+/*
+ * NOTE: order is important in the inheritance below!  PAL takes this class
+ * and casts it to CellularInterface and so CellularInterface has to be first
+ * in the last for that to work.
+ */
+
+/** UbloxATCellularInterface class.
+ *
+ *  This class implements the network stack interface into the cellular
+ *  modems on the C030 and C027 boards for 2G/3G/4G modules using
+ *  the IP stack running on the cellular module.
+ */
+class UbloxATCellularInterface : public CellularBase, public NetworkStack, virtual public UbloxCellularBase  {
+
+public:
+    /** Constructor.
+     *
+     * @param tx       the UART TX data pin to which the modem is attached.
+     * @param rx       the UART RX data pin to which the modem is attached.
+     * @param baud     the UART baud rate.
+     * @param debug_on true to switch AT interface debug on, otherwise false.
+     */
+     UbloxATCellularInterface(PinName tx = MDMTXD,
+                              PinName rx = MDMRXD,
+                              int baud = MBED_CONF_UBLOX_CELL_BAUD_RATE,
+                              bool debug_on = false);
+
+     /* Destructor.
+      */
+     virtual ~UbloxATCellularInterface();
+
+    /** The amount of extra space needed in terms of AT interface
+     * characters to get a chunk of user data (i.e. one UDP packet
+     * or a portion of a TCP packet) across the AT interface.
+     */
+    #define AT_PACKET_OVERHEAD 77
+
+    /** The profile to use (on board the modem).
+     */
+    #define PROFILE "0"
+
+    /** Translates a host name to an IP address with specific IP version.
+     *
+     *  The host name may be either a domain name or an IP address. If the
+     *  host name is an IP address, no network transactions will be performed.
+     *
+     *  If no stack-specific DNS resolution is provided, the host name
+     *  will be resolved using a UDP socket on the stack.
+     *
+     *  @param host     Host name to resolve.
+     *  @param address  Destination for the host SocketAddress.
+     *  @param version  IP version of address to resolve, NSAPI_UNSPEC indicates
+     *                  version is chosen by the stack (defaults to NSAPI_UNSPEC).
+     *  @return         0 on success, negative error code on failure.
+     */
+    virtual nsapi_error_t gethostbyname(const char *host,
+                                        SocketAddress *address,
+                                        nsapi_version_t version = NSAPI_UNSPEC);
+
+    /** Set the authentication scheme.
+     *
+     *  @param auth      The authentication scheme, chose from
+     *                   NSAPI_SECURITY_NONE, NSAPI_SECURITY_PAP,
+     *                   NSAPI_SECURITY_CHAP or NSAPI_SECURITY_UNKNOWN;
+     *                   use NSAPI_SECURITY_UNKNOWN to try all of none,
+     *                   PAP and CHAP.
+     */
+    virtual void set_authentication(nsapi_security_t auth);
+
+    /** Set the cellular network credentials.
+     *
+     *  Please check documentation of connect() for default behaviour of APN settings.
+     *
+     *  @param apn      Access point name.
+     *  @param uname    Optionally, user name.
+     *  @param pwd      Optionally, password.
+     */
+    virtual void set_credentials(const char *apn, const char *uname = 0,
+                                 const char *pwd = 0);
+
+    /** Set the PIN code for the SIM card.
+     *
+     *  @param sim_pin      PIN for the SIM card.
+     */
+    virtual void set_sim_pin(const char *sim_pin);
+
+    /** Connect to the cellular network and start the interface.
+     *
+     *  Attempts to connect to a cellular network.  Note: if init() has
+     *  not been called beforehand, connect() will call it first.
+     *
+     *  @param sim_pin     PIN for the SIM card.
+     *  @param apn         Optionally, access point name.
+     *  @param uname       Optionally, user name.
+     *  @param pwd         Optionally, password.
+     *  @return            NSAPI_ERROR_OK on success, or negative error code on failure.
+     */
+    virtual nsapi_error_t connect(const char *sim_pin, const char *apn = 0,
+                                  const char *uname = 0, const char *pwd = 0);
+
+    /** Attempt to connect to the cellular network.
+     *
+     *  Brings up the network interface. Connects to the cellular radio
+     *  network and then brings up IP stack on the cellular modem to be used
+     *  indirectly via AT commands, rather than LWIP.  Note: if init() has
+     *  not been called beforehand, connect() will call it first.
+     *  NOTE: even a failed attempt to connect will cause the modem to remain
+     *  powered up.  To power it down, call deinit().
+     *
+     *  For APN setup, default behaviour is to use 'internet' as APN string
+     *  and assuming no authentication is required, i.e., user name and password
+     *  are not set. Optionally, a database lookup can be requested by turning
+     *  on the APN database lookup feature. The APN database is by no means
+     *  exhaustive (feel free to submit a pull request with additional values).
+     *  It contains a short list of some public APNs with publicly available
+     *  user names and passwords (if required) in some particular countries only.
+     *  Lookup is done using IMSI (International mobile subscriber identifier).
+     *
+     *  The preferred method is to setup APN using 'set_credentials()' API.
+     *
+     *  If you find that the AT interface returns "CONNECT" but shortly afterwards
+     *  drops the connection then 99% of the time this will be because the APN
+     *  is incorrect.
+     *
+     *  @return            0 on success, negative error code on failure.
+     */
+    virtual nsapi_error_t connect();
+
+    /** Attempt to disconnect from the network.
+     *
+     *  Brings down the network interface.
+     *  Does not bring down the Radio network.
+     *
+     *  @return            0 on success, negative error code on failure.
+     */
+    virtual nsapi_error_t disconnect();
+
+    /** Adds or removes a SIM facility lock.
+     *
+     * Can be used to enable or disable SIM PIN check at device startup.
+     *
+     * @param set          Can be set to true if the SIM PIN check is supposed
+     *                     to be enabled and vice versa.
+     * @param immediate    If true, change the SIM PIN now, else set a flag
+     *                     and make the change only when connect() is called.
+     *                     If this is true and init() has not been called previously,
+     *                     it will be called first.
+     * @param sim_pin      The current SIM PIN, must be a const.  If this is not
+     *                     provided, the SIM PIN must have previously been set by a
+     *                     call to set_sim_pin().
+     * @return             0 on success, negative error code on failure.
+     */
+    nsapi_error_t set_sim_pin_check(bool set, bool immediate = false,
+                                    const char *sim_pin = NULL);
+
+    /** Change the PIN for the SIM card.
+     *
+     * Provide the new PIN for your SIM card with this API.  It is ONLY possible to
+     * change the SIM PIN when SIM PIN checking is ENABLED.
+     *
+     * @param new_pin    New PIN to be used in string format, must be a const.
+     * @param immediate  If true, change the SIM PIN now, else set a flag
+     *                   and make the change only when connect() is called.
+     *                   If this is true and init() has not been called previously,
+     *                   it will be called first.
+     * @param old_pin    Old PIN, must be a const.  If this is not provided, the SIM PIN
+     *                   must have previously been set by a call to set_sim_pin().
+     * @return           0 on success, negative error code on failure.
+     */
+    nsapi_error_t set_new_sim_pin(const char *new_pin, bool immediate = false,
+                                  const char *old_pin = NULL);
+
+    /** Check if the connection is currently established or not.
+     *
+     * @return          True if connected to a data network, otherwise false.
+     */
+    virtual bool is_connected();
+
+    /** Get the local IP address
+     *
+     *  @return         Null-terminated representation of the local IP address
+     *                  or null if no IP address has been received.
+     */
+    virtual const char *get_ip_address();
+
+    /** Get the local network mask.
+     *
+     *  @return         Null-terminated representation of the local network mask
+     *                  or null if no network mask has been received.
+     */
+    virtual const char *get_netmask();
+
+    /** Get the local gateways.
+     *
+     *  @return         Null-terminated representation of the local gateway
+     *                  or null if no network mask has been received.
+     */
+    virtual const char *get_gateway();
+
+    /** Call back in case connection is lost.
+     *
+     * @param cb     The function to call.
+     */
+    void connection_status_cb(Callback<void(nsapi_error_t)> cb);
+
+protected:
+
+    /** Socket "unused" value.
+     */
+    #define SOCKET_UNUSED -1
+
+    /** Socket timeout value in milliseconds.
+     * Note: the sockets layer above will retry the
+     * call to the functions here when they return NSAPI_ERROR_WOULD_BLOCK
+     * and the user has set a larger timeout or full blocking.
+     */
+    #define SOCKET_TIMEOUT 1000
+
+    /** The maximum number of bytes in a packet that can be written
+     * to the AT interface in one go.
+     */
+    #define MAX_WRITE_SIZE 1024
+
+    /** The maximum number of bytes in a packet that can be read from
+     * from the AT interface in one go.
+     */
+    #define MAX_READ_SIZE 1024
+
+    /** Management structure for sockets.
+     */
+    typedef struct {
+        int modem_handle;  //!< The modem's handle for the socket.
+        volatile nsapi_size_t pending; //!< The number of received bytes pending.
+        void (*callback)(void *); //!< A callback for events.
+        void *data; //!< A data pointer that must be passed to the callback.
+    } SockCtrl;
+
+    /** Sockets storage.
+     */
+    SockCtrl _sockets[7];
+
+    /** Storage for a single IP address.
+     */
+    char *_ip;
+
+    /** The APN to use.
+     */
+    const char *_apn;
+
+    /** The user name to use.
+     */
+    const char *_uname;
+
+    /** The password to use.
+     */
+    const char *_pwd;
+
+    /** The type of authentication to use.
+     */
+    nsapi_security_t _auth;
+
+    /** Get the next set of credentials from the database.
+     */
+    virtual void get_next_credentials(const char * config);
+
+    /** Activate one of the on-board modem's connection profiles.
+     *
+     * @param apn      The APN to use.
+     * @param username The user name to use.
+     * @param password The password to use.
+     * @param auth     The authentication method to use
+     *                 (NSAPI_SECURITY_NONE, NSAPI_SECURITY_PAP,
+     *                 NSAPI_SECURITY_CHAP or NSAPI_SECURITY_UNKNOWN).
+     * @return         True if successful, otherwise false.
+     */
+    virtual bool activate_profile(const char* apn, const char* username,
+                                  const char* password, nsapi_security_t auth);
+
+    /** Activate a profile using the existing external connection.
+     *
+     * @return true if successful, otherwise false.
+     */
+    virtual bool activate_profile_reuse_external(void);
+
+    /** Activate a profile based on connection ID.
+     *
+     * @param cid       The connection ID.
+     * @param apn       The APN to use.
+     * @param username  The user name to use.
+     * @param password  The password to use.
+     * @param auth      The authentication method to use.
+     * @return          True if successful, otherwise false.
+     */
+    virtual bool activate_profile_by_cid(int cid, const char* apn, const char* username,
+                                         const char* password, nsapi_security_t auth);
+
+    /** Connect the on board IP stack of the modem.
+     *
+     * @return         True if successful, otherwise false.
+     */
+    virtual bool connect_modem_stack();
+
+    /** Disconnect the on board IP stack of the modem.
+     *
+     * @return         True if successful, otherwise false.
+     */
+    virtual bool disconnect_modem_stack();
+
+    /** Provide access to the NetworkStack object
+     *
+     *  @return        The underlying NetworkStack object.
+     */
+    virtual NetworkStack *get_stack();
+
+protected:
+
+    /** Open a socket.
+     *
+     *  Creates a network socket and stores it in the specified handle.
+     *  The handle must be passed to following calls on the socket.
+     *
+     *  @param handle   Destination for the handle to a newly created socket.
+     *  @param proto    Protocol of socket to open, NSAPI_TCP or NSAPI_UDP.
+     *  @return         0 on success, negative error code on failure.
+     */
+    virtual nsapi_error_t socket_open(nsapi_socket_t *handle,
+                                      nsapi_protocol_t proto);
+
+    /** Close a socket.
+     *
+     *  Closes any open connection and deallocates any memory associated
+     *  with the socket.
+     *
+     *  @param handle   Socket handle.
+     *  @return         0 on success, negative error code on failure.
+     */
+    virtual nsapi_error_t socket_close(nsapi_socket_t handle);
+
+    /** Bind a specific port to a socket.
+     *
+     *  Binding a socket specifies port on which to receive
+     *  data. The IP address is ignored.  Note that binding
+     *  a socket involves closing it and reopening and so the
+     *  bind operation should be carried out before any others.
+     *
+     *  @param handle   Socket handle.
+     *  @param address  Local address to bind (of which only the port is used).
+     *  @return         0 on success, negative error code on failure.
+     */
+    virtual nsapi_error_t socket_bind(nsapi_socket_t handle,
+                                      const SocketAddress &address);
+
+    /** Connects TCP socket to a remote host.
+     *
+     *  Initiates a connection to a remote server specified by the
+     *  indicated address.
+     *
+     *  @param handle   Socket handle.
+     *  @param address  The SocketAddress of the remote host.
+     *  @return         0 on success, negative error code on failure.
+     */
+    virtual nsapi_error_t socket_connect(nsapi_socket_t handle,
+                                         const SocketAddress &address);
+
+    /** Send data over a TCP socket.
+     *
+     *  The socket must be connected to a remote host. Returns the number of
+     *  bytes sent from the buffer.  This class sets no upper buffer limit on
+     *  buffer size and the maximum packet size is not connected with the
+     *  platform.buffered-serial-txbuf-size/platform.buffered-serial-rxbuf-size
+     *  definitions.
+     *
+     *  @param handle   Socket handle.
+     *  @param data     Buffer of data to send to the host.
+     *  @param size     Size of the buffer in bytes.
+     *  @return         Number of sent bytes on success, negative error
+     *                  code on failure.
+     */
+    virtual nsapi_size_or_error_t socket_send(nsapi_socket_t handle,
+                                              const void *data, nsapi_size_t size);
+
+    /** Send a packet over a UDP socket.
+     *
+     *  Sends data to the specified address. Returns the number of bytes
+     *  sent from the buffer.
+     *
+     *  PACKET SIZES: the maximum packet size that can be sent in a single
+     *  UDP packet is limited by the configuration value
+     *  platform.buffered-serial-txbuf-size (defaults to 256).
+     *  The maximum UDP packet size is:
+     *
+     *  platform.buffered-serial-txbuf-size - AT_PACKET_OVERHEAD
+     *
+     *  ...with a limit of 1024 bytes (at the AT interface). So, to allow sending
+     *  of a 1024 byte UDP packet, edit your mbed_app.json to add a target override
+     *  setting platform.buffered-serial-txbuf-size to 1101.  However, for
+     *  UDP packets, 508 bytes is considered a more realistic size, taking into
+     *  account fragmentation sizes over the public internet, which leads to a
+     *  platform.buffered-serial-txbuf-size/platform.buffered-serial-rxbuf-size
+     *  setting of 585.
+     *
+     *  If size is larger than this limit, the data will be split across separate
+     *  UDP packets.
+     *
+     *  @param handle   Socket handle.
+     *  @param address  The SocketAddress of the remote host.
+     *  @param data     Buffer of data to send to the host.
+     *  @param size     Size of the buffer in bytes.
+     *  @return         Number of sent bytes on success, negative error
+     *                  code on failure.
+     */
+    virtual nsapi_size_or_error_t socket_sendto(nsapi_socket_t handle,
+                                                const SocketAddress &address,
+                                                const void *data,
+                                                nsapi_size_t size);
+
+    /** Receive data over a TCP socket.
+     *
+     *  The socket must be connected to a remote host. Returns the number of
+     *  bytes received into the buffer.  This class sets no upper limit on the
+     *  buffer size and the maximum packet size is not connected with the
+     *  platform.buffered-serial-txbuf-size/platform.buffered-serial-rxbuf-size
+     *  definitions.
+     *
+     *  @param handle   Socket handle.
+     *  @param data     Destination buffer for data received from the host.
+     *  @param size     Size of the buffer in bytes.
+     *  @return         Number of received bytes on success, negative error
+     *                  code on failure.
+     */
+    virtual nsapi_size_or_error_t socket_recv(nsapi_socket_t handle,
+                                              void *data, nsapi_size_t size);
+
+    /** Receive a packet over a UDP socket.
+     *
+     *  Receives data and stores the source address in address if address
+     *  is not NULL. Returns the number of bytes received into the buffer.
+     *
+     *  PACKET SIZES: the maximum packet size that can be retrieved in a
+     *  single call to this method is limited by the configuration value
+     *  platform.buffered-serial-rxbuf-size (default 256).  The maximum
+     *  UDP packet size is:
+     *
+     *  platform.buffered-serial-rxbuf-size - AT_PACKET_OVERHEAD
+     *
+     *  ...with a limit of 1024 (at the AT interface). So to allow reception of a
+     *  1024 byte UDP packet in a single call, edit your mbed_app.json to add a
+     *  target override setting platform.buffered-serial-rxbuf-size to 1101.
+     *
+     *  If the received packet is larger than this limit, any remainder will
+     *  be returned in subsequent calls to this method.  Once a single UDP
+     *  packet has been received, this method will return.
+     *
+     *  @param handle   Socket handle.
+     *  @param address  Destination for the source address or NULL.
+     *  @param data     Destination buffer for data received from the host.
+     *  @param size     Size of the buffer in bytes.
+     *  @return         Number of received bytes on success, negative error
+     *                  code on failure.
+     */
+    virtual nsapi_size_or_error_t socket_recvfrom(nsapi_socket_t handle,
+                                                  SocketAddress *address,
+                                                  void *data, nsapi_size_t size);
+
+    /** Register a callback on state change of the socket.
+     *
+     *  The specified callback will be called on state changes such as when
+     *  the socket can recv/send/accept successfully and on when an error
+     *  occurs. The callback may also be called spuriously without reason.
+     *
+     *  The callback may be called in an interrupt context and should not
+     *  perform expensive operations such as recv/send calls.
+     *
+     *  @param handle   Socket handle.
+     *  @param callback Function to call on state change.
+     *  @param data     Argument to pass to callback.
+     */
+    virtual void socket_attach(nsapi_socket_t handle, void (*callback)(void *),
+                               void *data);
+
+    /** Listen for connections on a TCP socket.
+     *
+     *  Marks the socket as a passive socket that can be used to accept
+     *  incoming connections.
+     *
+     *  @param handle   Socket handle.
+     *  @param backlog  Number of pending connections that can be queued
+     *                  simultaneously, defaults to 1.
+     *  @return         0 on success, negative error code on failure.
+     */
+    virtual nsapi_error_t socket_listen(nsapi_socket_t handle, int backlog);
+
+    /** Accepts a connection on a TCP socket.
+     *
+     *  The server socket must be bound and set to listen for connections.
+     *  On a new connection, creates a network socket and stores it in the
+     *  specified handle. The handle must be passed to following calls on
+     *  the socket.
+     *
+     *  A stack may have a finite number of sockets, in this case
+     *  NSAPI_ERROR_NO_SOCKET is returned if no socket is available.
+     *
+     *  This call is non-blocking. If accept would block,
+     *  NSAPI_ERROR_WOULD_BLOCK is returned immediately.
+     *
+     *  @param server   Socket handle to server to accept from.
+     *  @param handle   Destination for a handle to the newly created socket.
+     *  @param address  Destination for the remote address or NULL.
+     *  @return         0 on success, negative error code on failure.
+     */
+    virtual nsapi_error_t socket_accept(nsapi_socket_t server,
+                                        nsapi_socket_t *handle,
+                                        SocketAddress *address = 0);
+
+    /**  Set stack-specific socket options.
+     *
+     *  The setsockopt allow an application to pass stack-specific hints
+     *  to the underlying stack. For unsupported options,
+     *  NSAPI_ERROR_UNSUPPORTED is returned and the socket is unmodified.
+     *
+     *  @param handle   Socket handle.
+     *  @param level    Stack-specific protocol level.
+     *  @param optname  Stack-specific option identifier.
+     *  @param optval   Option value.
+     *  @param optlen   Length of the option value.
+     *  @return         0 on success, negative error code on failure.
+     */
+    virtual nsapi_error_t setsockopt(nsapi_socket_t handle, int level,
+                                     int optname, const void *optval,
+                                     unsigned optlen);
+
+    /**  Get stack-specific socket options.
+     *
+     *  The getstackopt allow an application to retrieve stack-specific hints
+     *  from the underlying stack. For unsupported options,
+     *  NSAPI_ERROR_UNSUPPORTED is returned and optval is unmodified.
+     *
+     *  @param handle   Socket handle.
+     *  @param level    Stack-specific protocol level.
+     *  @param optname  Stack-specific option identifier.
+     *  @param optval   Destination for option value.
+     *  @param optlen   Length of the option value.
+     *  @return         0 on success, negative error code on failure.
+     */
+    virtual nsapi_error_t getsockopt(nsapi_socket_t handle, int level,
+                                     int optname, void *optval,
+                                     unsigned *optlen);
+
+private:
+
+    // u_ added to namespace us somewhat as this darned macro
+    // is defined by everyone and their dog
+    #define u_stringify(a) str(a)
+    #define str(a) #a
+
+    bool _sim_pin_check_change_pending;
+    bool _sim_pin_check_change_pending_enabled_value;
+    bool _sim_pin_change_pending;
+    const char *_sim_pin_change_pending_new_pin_value;
+    Thread event_thread;
+    void handle_event();
+    SockCtrl * find_socket(int modem_handle = SOCKET_UNUSED);
+    void clear_socket(SockCtrl * socket);
+    bool check_socket(SockCtrl * socket);
+    int nsapi_security_to_modem_security(nsapi_security_t nsapi_security);
+    Callback<void(nsapi_error_t)> _connection_status_cb;
+    void UUSORD_URC();
+    void UUSORF_URC();
+    void UUSOCL_URC();
+    void UUPSDD_URC();
+};
+
+#endif // _UBLOX_CELLULAR_DRIVER_GEN_AT_DATA_
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ublox-cellular-base.lib	Mon Jun 12 21:32:21 2017 +0000
@@ -0,0 +1,1 @@
+https://developer.mbed.org/teams/ublox/code/ublox-cellular-base/#5cffef3371f6