Retry
Fork of ublox-at-cellular-interface by
Revision 0:7ccf0e7e8a83, committed 2017-06-12
- 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
--- /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(×tamp);
+ 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
