From 467a31f263752903f46872a1ce7c2c09e7715f35 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Mon, 16 May 2016 22:03:18 -0500 Subject: [PATCH 1/5] Add C027Interface --- net/C027Interface/C027Interface.cpp | 212 ++++++++++++++++++++++++++++ net/C027Interface/C027Interface.h | 168 ++++++++++++++++++++++ net/C027Interface/C027_Support.lib | 1 + 3 files changed, 381 insertions(+) create mode 100644 net/C027Interface/C027Interface.cpp create mode 100644 net/C027Interface/C027Interface.h create mode 100644 net/C027Interface/C027_Support.lib diff --git a/net/C027Interface/C027Interface.cpp b/net/C027Interface/C027Interface.cpp new file mode 100644 index 0000000000..168df13916 --- /dev/null +++ b/net/C027Interface/C027Interface.cpp @@ -0,0 +1,212 @@ +/* C027 implementation of NetworkInterfaceAPI + * Copyright (c) 2015 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. + */ + +#include "C027Interface.h" +#include "mbed.h" + + +// C027Interface implementation +C027Interface::C027Interface(const char *simpin, bool debug) + : _debug(debug) +{ + strcpy(_pin, simpin); +} + +int C027Interface::connect(const char *apn, const char *username, const char *password) +{ + // create the modem + _mdm = new MDMSerial; + if (_debug) { + _mdm->setDebug(4); + } else { + _mdm->setDebug(0); + } + + // initialize the modem + MDMParser::DevStatus devStatus = {}; + MDMParser::NetStatus netStatus = {}; + bool mdmOk = _mdm->init(_pin, &devStatus); + if (_debug) { + _mdm->dumpDevStatus(&devStatus); + } + + if (mdmOk) { + // wait until we are connected + mdmOk = _mdm->registerNet(&netStatus); + if (_debug) { + _mdm->dumpNetStatus(&netStatus); + } + } + + if (mdmOk) { + // join the internet connection + MDMParser::IP ip = _mdm->join(apn, username, password); + _ip_address.set_ip_bytes(&ip, NSAPI_IPv4); + mdmOk = (ip != NOIP); + } + + return mdmOk ? 0 : NSAPI_ERROR_DEVICE_ERROR; +} + +int C027Interface::disconnect() +{ + if (!_mdm->disconnect()) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + return 0; +} + +const char *C027Interface::get_ip_address() +{ + return _ip_address.get_ip_address(); +} + +const char *C027Interface::get_mac_address() +{ + return 0; +} + +struct c027_socket { + int socket; + MDMParser::IpProtocol proto; + MDMParser::IP ip; + int port; +}; + +int C027Interface::socket_open(void **handle, nsapi_protocol_t proto) +{ + MDMParser::IpProtocol mdmproto = (proto == NSAPI_UDP) ? MDMParser::IPPROTO_UDP : MDMParser::IPPROTO_TCP; + int fd = _mdm->socketSocket(mdmproto); + if (fd < 0) { + return NSAPI_ERROR_NO_SOCKET; + } + + _mdm->socketSetBlocking(fd, 10000); + struct c027_socket *socket = new struct c027_socket; + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + socket->socket = fd; + socket->proto = mdmproto; + *handle = socket; + return 0; +} + +int C027Interface::socket_close(void *handle) +{ + struct c027_socket *socket = (struct c027_socket *)handle; + _mdm->socketFree(socket->socket); + + delete socket; + return 0; +} + +int C027Interface::socket_bind(void *handle, const SocketAddress &address) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +int C027Interface::socket_listen(void *handle, int backlog) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +int C027Interface::socket_connect(void *handle, const SocketAddress &addr) +{ + struct c027_socket *socket = (struct c027_socket *)handle; + + if (!_mdm->socketConnect(socket->socket, addr.get_ip_address(), addr.get_port())) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + return 0; +} + +int C027Interface::socket_accept(void **handle, void *server) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +int C027Interface::socket_send(void *handle, const void *data, unsigned size) +{ + struct c027_socket *socket = (struct c027_socket *)handle; + + int sent = _mdm->socketSend(socket->socket, (const char *)data, size); + if (sent == SOCKET_ERROR) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + return sent; +} + +int C027Interface::socket_recv(void *handle, void *data, unsigned size) +{ + struct c027_socket *socket = (struct c027_socket *)handle; + if (!_mdm->socketReadable(socket->socket)) { + return NSAPI_ERROR_WOULD_BLOCK; + } + + int recv = _mdm->socketRecv(socket->socket, (char *)data, size); + if (recv == SOCKET_ERROR) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + return recv; +} + +int C027Interface::socket_sendto(void *handle, const SocketAddress &addr, const void *data, unsigned size) +{ + struct c027_socket *socket = (struct c027_socket *)handle; + + int sent = _mdm->socketSendTo(socket->socket, + *(MDMParser::IP *)addr.get_ip_bytes(), addr.get_port(), + (const char *)data, size); + + if (sent == SOCKET_ERROR) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + return sent; +} + +int C027Interface::socket_recvfrom(void *handle, SocketAddress *addr, void *data, unsigned size) +{ + struct c027_socket *socket = (struct c027_socket *)handle; + if (!_mdm->socketReadable(socket->socket)) { + return NSAPI_ERROR_WOULD_BLOCK; + } + + MDMParser::IP ip; + int port; + + int recv = _mdm->socketRecvFrom(socket->socket, &ip, &port, (char *)data, size); + if (recv == SOCKET_ERROR) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + if (addr) { + addr->set_ip_bytes(&ip, NSAPI_IPv4); + addr->set_port(port); + } + + return recv; +} + +void C027Interface::socket_attach(void *handle, void (*callback)(void *), void *data) +{ +} diff --git a/net/C027Interface/C027Interface.h b/net/C027Interface/C027Interface.h new file mode 100644 index 0000000000..3c71f8f822 --- /dev/null +++ b/net/C027Interface/C027Interface.h @@ -0,0 +1,168 @@ +/* C027 implementation of NetworkInterfaceAPI + * Copyright (c) 2015 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 C027_INTERFACE_H +#define C027_INTERFACE_H + +#include "CellularInterface.h" +#include "MDM.h" + + +/** C027Interface class + * Implementation of the NetworkInterface for C027 + */ +class C027Interface : public NetworkStack, public CellularInterface +{ +public: + /** C027Interfacelifetime + * @param simpin Optional PIN for the SIM + * @param debug Enable debugging + */ + C027Interface(const char *simpin=0, bool debug=false); + + /** Start the interface + * + * @param apn Optional name of the network to connect to + * @param username Optional username for your APN + * @param password Optional password for your APN + * @return 0 on success, negative error code on failure + */ + virtual int connect(const char *apn = 0, const char *username = 0, const char *password = 0); + + /** Stop the interface + * @return 0 on success, negative on failure + */ + virtual int disconnect(); + + /** Get the internally stored IP address + * @return IP address of the interface or null if not yet connected + */ + virtual const char *get_ip_address(); + + /** Get the internally stored MAC address + * @return MAC address of the interface + */ + virtual const char *get_mac_address(); + +protected: + /** Open a socket + * @param handle Handle in which to store new socket + * @param proto Type of socket to open, NSAPI_TCP or NSAPI_UDP + * @return 0 on success, negative on failure + */ + virtual int socket_open(void **handle, nsapi_protocol_t proto); + + /** Close the socket + * @param handle Socket handle + * @return 0 on success, negative on failure + * @note On failure, any memory associated with the socket must still + * be cleaned up + */ + virtual int socket_close(void *handle); + + /** Bind a server socket to a specific port + * @param handle Socket handle + * @param address Local address to listen for incoming connections on + * @return 0 on success, negative on failure. + */ + virtual int socket_bind(void *handle, const SocketAddress &address); + + /** Start listening for incoming connections + * @param handle Socket handle + * @param backlog Number of pending connections that can be queued up at any + * one time [Default: 1] + * @return 0 on success, negative on failure + */ + virtual int socket_listen(void *handle, int backlog); + + /** Connects this TCP socket to the server + * @param handle Socket handle + * @param address SocketAddress to connect to + * @return 0 on success, negative on failure + */ + virtual int socket_connect(void *handle, const SocketAddress &address); + + /** Accept a new connection. + * @param handle Handle in which to store new socket + * @param server Socket handle to server to accept from + * @return 0 on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_accept(void **handle, void *server); + + /** Send data to the remote host + * @param handle Socket handle + * @param data The buffer to send to the host + * @param size The length of the buffer to send + * @return Number of written bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_send(void *handle, const void *data, unsigned size); + + /** Receive data from the remote host + * @param handle Socket handle + * @param data The buffer in which to store the data received from the host + * @param size The maximum length of the buffer + * @return Number of received bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_recv(void *handle, void *data, unsigned size); + + /** Send a packet to a remote endpoint + * @param handle Socket handle + * @param address The remote SocketAddress + * @param data The packet to be sent + * @param size The length of the packet to be sent + * @return the number of written bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_sendto(void *handle, const SocketAddress &address, const void *data, unsigned size); + + /** Receive a packet from a remote endpoint + * @param handle Socket handle + * @param address Destination for the remote SocketAddress or null + * @param buffer The buffer for storing the incoming packet data + * If a packet is too long to fit in the supplied buffer, + * excess bytes are discarded + * @param size The length of the buffer + * @return the number of received bytes on success, negative on failure + * @note This call is not-blocking, if this call would block, must + * immediately return NSAPI_ERROR_WOULD_WAIT + */ + virtual int socket_recvfrom(void *handle, SocketAddress *address, void *buffer, unsigned size); + + /** Register a callback on state change of the socket + * @param handle Socket handle + * @param callback Function to call on state change + * @param data Argument to pass to callback + * @note Callback may be called in an interrupt context. + */ + virtual void socket_attach(void *handle, void (*callback)(void *), void *data); + +private: + // Modem object + bool _debug; + MDMSerial *_mdm; + SocketAddress _ip_address; + char _mac_address[NSAPI_MAC_SIZE]; + char _pin[sizeof "1234"]; +}; + +#endif diff --git a/net/C027Interface/C027_Support.lib b/net/C027Interface/C027_Support.lib new file mode 100644 index 0000000000..5799237032 --- /dev/null +++ b/net/C027Interface/C027_Support.lib @@ -0,0 +1 @@ +https://developer.mbed.org/teams/NetworkSocketAPI/code/C027_Support/#596bcb599ac1 From a878b60a0a56a3c03040f76c90a100a7f850fe46 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 17 May 2016 01:21:08 -0500 Subject: [PATCH 2/5] Add background thread to emulate attach callback for C027 --- net/C027Interface/C027Interface.cpp | 62 ++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/net/C027Interface/C027Interface.cpp b/net/C027Interface/C027Interface.cpp index 168df13916..88971efd6e 100644 --- a/net/C027Interface/C027Interface.cpp +++ b/net/C027Interface/C027Interface.cpp @@ -16,6 +16,7 @@ #include "C027Interface.h" #include "mbed.h" +#include "rtos.h" // C027Interface implementation @@ -85,8 +86,32 @@ struct c027_socket { MDMParser::IpProtocol proto; MDMParser::IP ip; int port; + + MDMSerial *mdm; + Thread thread; + Mutex mutex; + volatile bool running; + void (*callback)(void *); + void *data; }; +static void socket_poll(struct c027_socket *socket) { + bool was_readable = false; + + while (socket->running) { + socket->mutex.lock(); + bool is_readable = !!socket->mdm->socketReadable(socket->socket); + + if (is_readable != was_readable) { + if (socket->callback) { + socket->callback(socket->data); + } + was_readable = is_readable; + } + socket->mutex.unlock(); + } +} + int C027Interface::socket_open(void **handle, nsapi_protocol_t proto) { MDMParser::IpProtocol mdmproto = (proto == NSAPI_UDP) ? MDMParser::IPPROTO_UDP : MDMParser::IPPROTO_TCP; @@ -103,6 +128,11 @@ int C027Interface::socket_open(void **handle, nsapi_protocol_t proto) socket->socket = fd; socket->proto = mdmproto; + socket->mdm = _mdm; + + socket->running = true; + socket->thread.start(socket, socket_poll); + *handle = socket; return 0; } @@ -110,6 +140,10 @@ int C027Interface::socket_open(void **handle, nsapi_protocol_t proto) int C027Interface::socket_close(void *handle) { struct c027_socket *socket = (struct c027_socket *)handle; + + socket->running = false; + socket->thread.join(); + _mdm->socketFree(socket->socket); delete socket; @@ -129,8 +163,12 @@ int C027Interface::socket_listen(void *handle, int backlog) int C027Interface::socket_connect(void *handle, const SocketAddress &addr) { struct c027_socket *socket = (struct c027_socket *)handle; - - if (!_mdm->socketConnect(socket->socket, addr.get_ip_address(), addr.get_port())) { + + socket->mutex.lock(); + bool success = _mdm->socketConnect(socket->socket, addr.get_ip_address(), addr.get_port()); + socket->mutex.unlock(); + + if (!success) { return NSAPI_ERROR_DEVICE_ERROR; } @@ -146,7 +184,10 @@ int C027Interface::socket_send(void *handle, const void *data, unsigned size) { struct c027_socket *socket = (struct c027_socket *)handle; + socket->mutex.lock(); int sent = _mdm->socketSend(socket->socket, (const char *)data, size); + socket->mutex.unlock(); + if (sent == SOCKET_ERROR) { return NSAPI_ERROR_DEVICE_ERROR; } @@ -157,11 +198,16 @@ int C027Interface::socket_send(void *handle, const void *data, unsigned size) int C027Interface::socket_recv(void *handle, void *data, unsigned size) { struct c027_socket *socket = (struct c027_socket *)handle; + + socket->mutex.lock(); if (!_mdm->socketReadable(socket->socket)) { + socket->mutex.unlock(); return NSAPI_ERROR_WOULD_BLOCK; } int recv = _mdm->socketRecv(socket->socket, (char *)data, size); + socket->mutex.unlock(); + if (recv == SOCKET_ERROR) { return NSAPI_ERROR_DEVICE_ERROR; } @@ -173,9 +219,11 @@ int C027Interface::socket_sendto(void *handle, const SocketAddress &addr, const { struct c027_socket *socket = (struct c027_socket *)handle; + socket->mutex.lock(); int sent = _mdm->socketSendTo(socket->socket, *(MDMParser::IP *)addr.get_ip_bytes(), addr.get_port(), (const char *)data, size); + socket->mutex.unlock(); if (sent == SOCKET_ERROR) { return NSAPI_ERROR_DEVICE_ERROR; @@ -187,7 +235,10 @@ int C027Interface::socket_sendto(void *handle, const SocketAddress &addr, const int C027Interface::socket_recvfrom(void *handle, SocketAddress *addr, void *data, unsigned size) { struct c027_socket *socket = (struct c027_socket *)handle; + + socket->mutex.lock(); if (!_mdm->socketReadable(socket->socket)) { + socket->mutex.unlock(); return NSAPI_ERROR_WOULD_BLOCK; } @@ -195,6 +246,8 @@ int C027Interface::socket_recvfrom(void *handle, SocketAddress *addr, void *data int port; int recv = _mdm->socketRecvFrom(socket->socket, &ip, &port, (char *)data, size); + socket->mutex.unlock(); + if (recv == SOCKET_ERROR) { return NSAPI_ERROR_DEVICE_ERROR; } @@ -209,4 +262,9 @@ int C027Interface::socket_recvfrom(void *handle, SocketAddress *addr, void *data void C027Interface::socket_attach(void *handle, void (*callback)(void *), void *data) { + struct c027_socket *socket = (struct c027_socket *)handle; + socket->mutex.lock(); + socket->callback = callback; + socket->data = data; + socket->mutex.unlock(); } From 88293b54f18ee0fc248d3036a46db3819353fef1 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 17 May 2016 02:48:47 -0500 Subject: [PATCH 3/5] Fix idiosyncratic non-blocking behaviour in C027 --- net/C027Interface/C027Interface.cpp | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/net/C027Interface/C027Interface.cpp b/net/C027Interface/C027Interface.cpp index 88971efd6e..ae9ba5aa6d 100644 --- a/net/C027Interface/C027Interface.cpp +++ b/net/C027Interface/C027Interface.cpp @@ -96,19 +96,15 @@ struct c027_socket { }; static void socket_poll(struct c027_socket *socket) { - bool was_readable = false; - while (socket->running) { socket->mutex.lock(); - bool is_readable = !!socket->mdm->socketReadable(socket->socket); - - if (is_readable != was_readable) { + if (socket->mdm->socketReadable(socket->socket)) { if (socket->callback) { socket->callback(socket->data); } - was_readable = is_readable; } socket->mutex.unlock(); + Thread::yield(); } } @@ -166,6 +162,9 @@ int C027Interface::socket_connect(void *handle, const SocketAddress &addr) socket->mutex.lock(); bool success = _mdm->socketConnect(socket->socket, addr.get_ip_address(), addr.get_port()); + if (socket->callback) { + socket->callback(socket->data); + } socket->mutex.unlock(); if (!success) { @@ -186,6 +185,10 @@ int C027Interface::socket_send(void *handle, const void *data, unsigned size) socket->mutex.lock(); int sent = _mdm->socketSend(socket->socket, (const char *)data, size); + + if (socket->callback) { + socket->callback(socket->data); + } socket->mutex.unlock(); if (sent == SOCKET_ERROR) { @@ -212,6 +215,10 @@ int C027Interface::socket_recv(void *handle, void *data, unsigned size) return NSAPI_ERROR_DEVICE_ERROR; } + if (recv == 0) { + return NSAPI_ERROR_WOULD_BLOCK; + } + return recv; } @@ -223,6 +230,10 @@ int C027Interface::socket_sendto(void *handle, const SocketAddress &addr, const int sent = _mdm->socketSendTo(socket->socket, *(MDMParser::IP *)addr.get_ip_bytes(), addr.get_port(), (const char *)data, size); + + if (socket->callback) { + socket->callback(socket->data); + } socket->mutex.unlock(); if (sent == SOCKET_ERROR) { @@ -252,6 +263,10 @@ int C027Interface::socket_recvfrom(void *handle, SocketAddress *addr, void *data return NSAPI_ERROR_DEVICE_ERROR; } + if (recv == 0) { + return NSAPI_ERROR_WOULD_BLOCK; + } + if (addr) { addr->set_ip_bytes(&ip, NSAPI_IPv4); addr->set_port(port); From 901dcbd8932f16189d30a4a200f694f3b54c6e82 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 17 May 2016 03:05:24 -0500 Subject: [PATCH 4/5] Fix C027 endianness issue --- net/C027Interface/C027Interface.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/net/C027Interface/C027Interface.cpp b/net/C027Interface/C027Interface.cpp index ae9ba5aa6d..0ca48f4c3e 100644 --- a/net/C027Interface/C027Interface.cpp +++ b/net/C027Interface/C027Interface.cpp @@ -19,6 +19,20 @@ #include "rtos.h" +// Portable big -> little endian +static inline MDMParser::IP ntohl(MDMParser::IP ip) { + ip = ((0xff & (ip >> 24)) << 0) + | ((0xff & (ip >> 16)) << 8) + | ((0xff & (ip >> 8)) << 16) + | ((0xff & (ip >> 0)) << 24); + return ip; +} + +static inline MDMParser::IP htonl(MDMParser::IP ip) { + return ntohl(ip); +} + + // C027Interface implementation C027Interface::C027Interface(const char *simpin, bool debug) : _debug(debug) @@ -54,7 +68,7 @@ int C027Interface::connect(const char *apn, const char *username, const char *pa if (mdmOk) { // join the internet connection - MDMParser::IP ip = _mdm->join(apn, username, password); + MDMParser::IP ip = htonl(_mdm->join(apn, username, password)); _ip_address.set_ip_bytes(&ip, NSAPI_IPv4); mdmOk = (ip != NOIP); } @@ -228,7 +242,7 @@ int C027Interface::socket_sendto(void *handle, const SocketAddress &addr, const socket->mutex.lock(); int sent = _mdm->socketSendTo(socket->socket, - *(MDMParser::IP *)addr.get_ip_bytes(), addr.get_port(), + ntohl(*(MDMParser::IP *)addr.get_ip_bytes()), addr.get_port(), (const char *)data, size); if (socket->callback) { @@ -268,6 +282,7 @@ int C027Interface::socket_recvfrom(void *handle, SocketAddress *addr, void *data } if (addr) { + ip = htonl(ip); addr->set_ip_bytes(&ip, NSAPI_IPv4); addr->set_port(port); } From 9ce315a18c62011fe4ad4e8572fd2f7abd888ee9 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Tue, 17 May 2016 04:06:03 -0500 Subject: [PATCH 5/5] Increase C027 buffers to handle DTLS packets --- net/C027Interface/C027Interface.cpp | 8 +++++++- net/C027Interface/C027_Support.lib | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/net/C027Interface/C027Interface.cpp b/net/C027Interface/C027Interface.cpp index 0ca48f4c3e..d1644384a1 100644 --- a/net/C027Interface/C027Interface.cpp +++ b/net/C027Interface/C027Interface.cpp @@ -43,7 +43,13 @@ C027Interface::C027Interface(const char *simpin, bool debug) int C027Interface::connect(const char *apn, const char *username, const char *password) { // create the modem - _mdm = new MDMSerial; + _mdm = new MDMSerial( + MDM_IF(MDMTXD, D1), + MDM_IF(MDMRXD, D0), + MDM_IF(MDMBAUD, 115200), + 1024, + 1024); + if (_debug) { _mdm->setDebug(4); } else { diff --git a/net/C027Interface/C027_Support.lib b/net/C027Interface/C027_Support.lib index 5799237032..4661596ad2 100644 --- a/net/C027Interface/C027_Support.lib +++ b/net/C027Interface/C027_Support.lib @@ -1 +1 @@ -https://developer.mbed.org/teams/NetworkSocketAPI/code/C027_Support/#596bcb599ac1 +https://developer.mbed.org/teams/NetworkSocketAPI/code/C027_Support/#b5614db52fc4