Merge pull request #88 from geky/nsapi-changes

Update Network Socket API
Sam Grove 2016-05-16 09:21:15 -07:00
commit b7cdc204fd
34 changed files with 3280 additions and 1139 deletions

View File

@ -84,7 +84,7 @@ struct esp8266_socket {
bool connected;
};
void *ESP8266Interface::socket_create(nsapi_protocol_t proto)
int ESP8266Interface::socket_open(void **handle, nsapi_protocol_t proto)
{
// Look for an unused socket
int id = -1;
@ -98,38 +98,37 @@ void *ESP8266Interface::socket_create(nsapi_protocol_t proto)
}
if (id == -1) {
return 0;
return NSAPI_ERROR_NO_SOCKET;
}
struct esp8266_socket *socket = new struct esp8266_socket;
if (!socket) {
return 0;
return NSAPI_ERROR_NO_SOCKET;
}
socket->id = id;
socket->proto = proto;
socket->connected = false;
return socket;
*handle = socket;
return 0;
}
void ESP8266Interface::socket_destroy(void *handle)
int ESP8266Interface::socket_close(void *handle)
{
struct esp8266_socket *socket = (struct esp8266_socket *)handle;
int err = 0;
_esp.setTimeout(ESP8266_MISC_TIMEOUT);
if (!_esp.close(socket->id)) {
err = NSAPI_ERROR_DEVICE_ERROR;
}
_ids[socket->id] = false;
delete socket;
return err;
}
int ESP8266Interface::socket_set_option(void *handle, int optname, const void *optval, unsigned optlen)
{
return NSAPI_ERROR_UNSUPPORTED;
}
int ESP8266Interface::socket_get_option(void *handle, int optname, void *optval, unsigned *optlen)
{
return NSAPI_ERROR_UNSUPPORTED;
}
int ESP8266Interface::socket_bind(void *handle, int port)
int ESP8266Interface::socket_bind(void *handle, const SocketAddress &address)
{
return NSAPI_ERROR_UNSUPPORTED;
}
@ -153,12 +152,7 @@ int ESP8266Interface::socket_connect(void *handle, const SocketAddress &addr)
return 0;
}
bool ESP8266Interface::socket_is_connected(void *handle)
{
return true;
}
int ESP8266Interface::socket_accept(void *handle, void **connection)
int ESP8266Interface::socket_accept(void **handle, void *server)
{
return NSAPI_ERROR_UNSUPPORTED;
}
@ -207,26 +201,6 @@ int ESP8266Interface::socket_recvfrom(void *handle, SocketAddress *addr, void *d
return socket_recv(socket, data, size);
}
int ESP8266Interface::socket_close(void *handle, bool shutdown)
{
struct esp8266_socket *socket = (struct esp8266_socket *)handle;
_esp.setTimeout(ESP8266_MISC_TIMEOUT);
if (!_esp.close(socket->id)) {
return NSAPI_ERROR_DEVICE_ERROR;
}
return 0;
}
void ESP8266Interface::socket_attach_accept(void *handle, void (*callback)(void *), void *id)
{
}
void ESP8266Interface::socket_attach_send(void *handle, void (*callback)(void *), void *id)
{
}
void ESP8266Interface::socket_attach_recv(void *handle, void (*callback)(void *), void *id)
void ESP8266Interface::socket_attach(void *handle, void (*callback)(void *), void *data)
{
}

View File

@ -23,184 +23,146 @@
#define ESP8266_SOCKET_COUNT 5
/** ESP8266Interface class
* Implementation of the NetworkInterface for the ESP8266
* Implementation of the NetworkStack for the ESP8266
*/
class ESP8266Interface : public WiFiInterface
class ESP8266Interface : public NetworkStack, public WiFiInterface
{
public:
/** ESP8266Interface lifetime
/param tx TX pin
/param rx RX pin
/param debug Enable debugging
*/
* @param tx TX pin
* @param rx RX pin
* @param debug Enable debugging
*/
ESP8266Interface(PinName tx, PinName rx, bool debug = false);
/** Start the interface
/param ssid Name of the network to connect to
/param pass Security passphrase to connect to the network
/param security Type of encryption for connection
/return 0 on success, negative on failure
*/
*
* Attempts to connect to a WiFi network. If passphrase is invalid,
* NSAPI_ERROR_AUTH_ERROR is returned.
*
* @param ssid Name of the network to connect to
* @param pass Security passphrase to connect to the network
* @param security Type of encryption for connection
* @return 0 on success, negative error code on failure
*/
virtual int connect(
const char *ssid,
const char *pass,
nsapi_security_t security = NSAPI_SECURITY_NONE);
/** Stop the interface
* @return 0 on success, negative on failure
* @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
*/
* @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
*/
* @return MAC address of the interface
*/
virtual const char *get_mac_address();
protected:
/** Create a socket
/param proto The type of socket to open, TCP or UDP
/return The alocated socket or null on failure
*/
virtual void *socket_create(nsapi_protocol_t proto);
/** 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);
/** Destroy a socket
/param socket Previously allocated socket
*/
virtual void socket_destroy(void *handle);
/** Set socket options
\param handle Socket handle
\param optname Option ID
\param optval Option value
\param optlen Length of the option value
\return 0 on success, negative on failure
*/
virtual int socket_set_option(void *handle, int optname, const void *optval, unsigned int optlen);
/** Get socket options
\param handle Socket handle
\param optname Option ID
\param optval Buffer pointer where to write the option value
\param optlen Length of the option value
\return 0 on success, negative on failure
*/
virtual int socket_get_option(void *handle, int optname, void *optval, unsigned int *optlen);
/** 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 port The port to listen for incoming connections on
\return 0 on success, negative on failure.
*/
virtual int socket_bind(void *handle, int 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
*/
* @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
*/
* @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);
/** Check if the socket is connected
\param handle Socket handle
\return true if connected, false otherwise
*/
virtual bool socket_is_connected(void *handle);
/** Accept a new connection.
\param handle Socket handle
\param socket A TCPSocket instance that will handle the incoming connection.
\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 **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
*/
* @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
*/
* @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
*/
* @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
*/
* @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);
/** Close the socket
\param handle Socket handle
\param shutdown free the left-over data in message queues
*/
virtual int socket_close(void *handle, bool shutdown);
/** Register a callback on when a new connection is ready
\param handle Socket handle
\param callback Function to call when accept will succeed, may be called in
interrupt context.
\param id Argument to pass to callback
*/
virtual void socket_attach_accept(void *handle, void (*callback)(void *), void *id);
/** Register a callback on when send is ready
\param handle Socket handle
\param callback Function to call when accept will succeed, may be called in
interrupt context.
\param id Argument to pass to callback
*/
virtual void socket_attach_send(void *handle, void (*callback)(void *), void *id);
/** Register a callback on when recv is ready
\param handle Socket handle
\param callback Function to call when accept will succeed, may be called in
interrupt context.
\param id Argument to pass to callback
*/
virtual void socket_attach_recv(void *handle, void (*callback)(void *), void *id);
/** 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:
ESP8266 _esp;
@ -208,4 +170,3 @@ private:
};
#endif

View File

@ -25,6 +25,15 @@
#include "lwip/netdb.h"
#include "netif/etharp.h"
#include "eth_arch.h"
#include "lwip/netif.h"
#include "lwip/udp.h"
#include "lwip/tcp.h"
#include "lwip/tcp_impl.h"
#include "lwip/timers.h"
#include "lwip/dns.h"
#include "lwip/def.h"
#include "lwip/ip_addr.h"
/* TCP/IP and Network Interface Initialisation */
static struct netif netif;
@ -36,19 +45,19 @@ static Semaphore tcpip_inited(0);
static Semaphore netif_linked(0);
static Semaphore netif_up(0);
static void tcpip_init_done(void *)
static void tcpip_init_irq(void *)
{
tcpip_inited.release();
}
static void netif_link_callback(struct netif *netif)
static void netif_link_irq(struct netif *netif)
{
if (netif_is_link_up(netif)) {
netif_linked.release();
}
}
static void netif_status_callback(struct netif *netif)
static void netif_status_irq(struct netif *netif)
{
if (netif_is_up(netif)) {
strcpy(ip_addr, inet_ntoa(netif->ip_addr));
@ -58,15 +67,15 @@ static void netif_status_callback(struct netif *netif)
static void init_netif(ip_addr_t *ipaddr, ip_addr_t *netmask, ip_addr_t *gw)
{
tcpip_init(tcpip_init_done, NULL);
tcpip_init(tcpip_init_irq, NULL);
tcpip_inited.wait();
memset((void*) &netif, 0, sizeof(netif));
netif_add(&netif, ipaddr, netmask, gw, NULL, eth_arch_enetif_init, tcpip_input);
netif_set_default(&netif);
netif_set_link_callback (&netif, netif_link_callback);
netif_set_status_callback(&netif, netif_status_callback);
netif_set_link_callback (&netif, netif_link_irq);
netif_set_status_callback(&netif, netif_status_irq);
}
static void set_mac_address(void)
@ -82,6 +91,7 @@ static void set_mac_address(void)
}
/* Interface implementation */
int LWIPInterface::connect()
{
// Set up network
@ -122,194 +132,422 @@ const char *LWIPInterface::get_mac_address()
return mac_addr;
}
void *LWIPInterface::socket_create(nsapi_protocol_t proto)
struct lwip_socket {
nsapi_protocol_t proto;
union {
struct udp_pcb *udp;
struct tcp_pcb *tcp;
};
struct tcp_pcb *npcb;
struct pbuf *rx_chain;
Semaphore *sem;
void (*callback)(void *);
void *data;
};
static void udp_recv_irq(
void *arg, struct udp_pcb *upcb, struct pbuf *p,
struct ip_addr *addr, uint16_t port);
int LWIPInterface::socket_open(void **handle, nsapi_protocol_t proto)
{
int type = (proto == NSAPI_UDP) ? SOCK_DGRAM : SOCK_STREAM;
int fd = lwip_socket(AF_INET, type, 0);
if (fd < 0) {
return 0;
struct lwip_socket *s = new struct lwip_socket;
if (!s) {
return NSAPI_ERROR_NO_SOCKET;
}
return (void *)(fd+1);
}
memset(s, 0, sizeof *s);
void LWIPInterface::socket_destroy(void *handle)
{
int fd = (int)handle-1;
lwip_close(fd);
}
switch (proto) {
case NSAPI_UDP:
s->proto = proto;
s->udp = udp_new();
if (!s->udp) {
return NSAPI_ERROR_NO_SOCKET;
}
int LWIPInterface::socket_set_option(void *handle, int optname, const void *optval, unsigned optlen)
{
int fd = (int)handle-1;
return lwip_setsockopt(fd, SOL_SOCKET, optname, optval, (socklen_t)optlen);
}
udp_recv(s->udp, udp_recv_irq, s);
*handle = s;
return 0;
int LWIPInterface::socket_get_option(void *handle, int optname, void *optval, unsigned *optlen)
{
int fd = (int)handle-1;
return lwip_getsockopt(fd, SOL_SOCKET, optname, optval, (socklen_t*)optlen);
}
case NSAPI_TCP:
s->proto = proto;
s->tcp = tcp_new();
if (!s->tcp) {
return NSAPI_ERROR_NO_SOCKET;
}
int LWIPInterface::socket_bind(void *handle, int port)
{
int fd = (int)handle-1;
struct sockaddr_in sa;
memset(&sa, 0, sizeof sa);
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr.s_addr = INADDR_ANY;
if (lwip_bind(fd, (const struct sockaddr *)&sa, sizeof sa) < 0) {
return NSAPI_ERROR_DEVICE_ERROR;
tcp_arg(s->tcp, s);
//tcp_err(s->tcp, tcp_error_irq);
*handle = s;
return 0;
}
return 0;
return NSAPI_ERROR_DEVICE_ERROR;
}
int LWIPInterface::socket_close(void *handle)
{
struct lwip_socket *s = (struct lwip_socket *)handle;
int err = 0;
switch (s->proto) {
case NSAPI_UDP:
udp_disconnect(s->udp);
udp_remove(s->udp);
break;
case NSAPI_TCP:
if (tcp_close(s->tcp)) {
err = NSAPI_ERROR_DEVICE_ERROR;
}
break;
}
if (s->rx_chain) {
pbuf_free(s->rx_chain);
s->rx_chain = 0;
}
delete s;
return err;
}
int LWIPInterface::socket_bind(void *handle, const SocketAddress &address)
{
struct lwip_socket *s = (struct lwip_socket *)handle;
ip_addr_t ip_addr = ip_addr_any; // TODO use address
switch (s->proto) {
case NSAPI_UDP:
if (udp_bind(s->udp, &ip_addr, address.get_port())) {
return NSAPI_ERROR_DEVICE_ERROR;
}
return 0;
case NSAPI_TCP:
if (tcp_bind(s->tcp, &ip_addr, address.get_port())) {
return NSAPI_ERROR_DEVICE_ERROR;
}
return 0;
}
return NSAPI_ERROR_DEVICE_ERROR;
}
static err_t tcp_accept_irq(void *arg, struct tcp_pcb *tpcb, err_t err);
int LWIPInterface::socket_listen(void *handle, int backlog)
{
return NSAPI_ERROR_UNSUPPORTED;
struct lwip_socket *s = (struct lwip_socket *)handle;
if (s->tcp->state != LISTEN) {
struct tcp_pcb *server = tcp_listen(s->tcp);
if (!server) {
return NSAPI_ERROR_NO_SOCKET;
}
s->tcp = server;
s->npcb = 0;
}
tcp_arg(s->tcp, s);
tcp_accept(s->tcp, tcp_accept_irq);
return 0;
}
static err_t tcp_sent_irq(void *arg, struct tcp_pcb *tpcb, uint16_t len);
static err_t tcp_recv_irq(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err);
static err_t tcp_connect_irq(void *handle, struct tcp_pcb *tpcb, err_t err)
{
struct lwip_socket *s = (struct lwip_socket *)handle;
tcp_sent(tpcb, tcp_sent_irq);
tcp_recv(tpcb, tcp_recv_irq);
s->sem->release();
return ERR_OK;
}
int LWIPInterface::socket_connect(void *handle, const SocketAddress &addr)
{
int fd = (int)handle-1;
struct sockaddr_in sa;
memset(&sa, 0, sizeof sa);
inet_aton(addr.get_ip_address(), &sa.sin_addr);
sa.sin_family = AF_INET;
sa.sin_port = htons(addr.get_port());
struct lwip_socket *s = (struct lwip_socket *)handle;
if (lwip_connect(fd, (const struct sockaddr *)&sa, sizeof sa) < 0) {
ip_addr_t ip_addr;
inet_aton(addr.get_ip_address(), &ip_addr);
Semaphore connected(0);
s->sem = &connected;
tcp_connect(s->tcp, &ip_addr, addr.get_port(), tcp_connect_irq);
// Wait for connection
if (connected.wait(1500) < 0) {
return NSAPI_ERROR_NO_CONNECTION;
}
return 0;
}
bool LWIPInterface::socket_is_connected(void *handle)
static err_t tcp_refuse_irq(void *handle, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
return true;
return ERR_WOULDBLOCK;
}
int LWIPInterface::socket_accept(void *handle, void **connection)
static err_t tcp_accept_irq(void *handle, struct tcp_pcb *npcb, err_t err)
{
return NSAPI_ERROR_UNSUPPORTED;
}
int LWIPInterface::socket_send(void *handle, const void *p, unsigned size)
{
int fd = (int)handle-1;
uint8_t *data = (uint8_t *)p;
unsigned written = 0;
while (written < size) {
int ret = lwip_send(fd, data + written, size - written, 0);
if (ret > 0) {
written += ret;
} else if (ret == 0) {
return NSAPI_ERROR_NO_CONNECTION;
} else {
return NSAPI_ERROR_DEVICE_ERROR;
}
struct lwip_socket *s = (struct lwip_socket *)handle;
if (s->npcb) {
tcp_abort(npcb);
return ERR_ABRT;
}
return written;
tcp_recv(npcb, tcp_refuse_irq);
s->npcb = npcb;
if (s->callback) {
s->callback(s->data);
}
return ERR_OK;
}
int LWIPInterface::socket_recv(void *handle, void *data, unsigned size)
int LWIPInterface::socket_accept(void **handle, void *server)
{
int fd = (int)handle-1;
int ret = lwip_recv(fd, data, size, MSG_DONTWAIT);
if (ret > 0) {
return ret;
} else if (ret == 0) {
return NSAPI_ERROR_NO_CONNECTION;
} else if (ret == -1) {
struct lwip_socket *s = (struct lwip_socket *)server;
if (!s->npcb) {
return NSAPI_ERROR_WOULD_BLOCK;
} else {
return NSAPI_ERROR_DEVICE_ERROR;
}
}
int LWIPInterface::socket_sendto(void *handle, const SocketAddress &addr, const void *p, unsigned size)
{
int fd = (int)handle-1;
uint8_t *data = (uint8_t *)p;
unsigned written = 0;
struct sockaddr_in sa;
memset(&sa, 0, sizeof sa);
inet_aton(addr.get_ip_address(), &sa.sin_addr);
sa.sin_family = AF_INET;
sa.sin_port = htons(addr.get_port());
while (written < size) {
int ret = lwip_sendto(fd, data + written, size - written, 0,
(const struct sockaddr *)&sa, sizeof sa);
if (ret > 0) {
written += ret;
} else if (ret == 0) {
return NSAPI_ERROR_NO_CONNECTION;
} else {
return NSAPI_ERROR_DEVICE_ERROR;
}
}
return written;
}
int LWIPInterface::socket_recvfrom(void *handle, SocketAddress *addr, void *data, unsigned size)
{
int fd = (int)handle-1;
struct sockaddr_in sa;
socklen_t sa_len = sizeof sa;
int ret = lwip_recvfrom(fd, data, size, MSG_DONTWAIT,
(struct sockaddr *)&sa, &sa_len);
if (ret > 0 && addr) {
addr->set_ip_address(inet_ntoa(sa.sin_addr));
addr->set_port(ntohs(sa.sin_port));
struct lwip_socket *ns = new struct lwip_socket;
if (!ns) {
return NSAPI_ERROR_NO_SOCKET;
}
if (ret > 0) {
return ret;
} else if (ret == 0) {
return NSAPI_ERROR_NO_CONNECTION;
} else if (ret == -1) {
return NSAPI_ERROR_WOULD_BLOCK;
} else {
return NSAPI_ERROR_DEVICE_ERROR;
}
}
memset(ns, 0, sizeof *ns);
int LWIPInterface::socket_close(void *handle, bool shutdown)
{
int fd = (int)handle-1;
if (shutdown) {
lwip_shutdown(fd, SHUT_RDWR);
}
ns->tcp = s->npcb;
s->npcb = 0;
lwip_close(fd);
tcp_accepted(ns->tcp);
tcp_arg(ns->tcp, ns);
//tcp_err(ns->tcp, tcp_error_irq);
tcp_sent(ns->tcp, tcp_sent_irq);
tcp_recv(ns->tcp, tcp_recv_irq);
*handle = ns;
return 0;
}
void LWIPInterface::socket_attach_accept(void *handle, void (*callback)(void *), void *id)
static struct pbuf *pbuf_consume(struct pbuf *p, size_t consume, bool free_partial)
{
do {
if (consume <= p->len) {
// advance the payload pointer by the number of bytes copied
p->payload = (char *)p->payload + consume;
// reduce the length by the number of bytes copied
p->len -= consume;
// break out of the loop
consume = 0;
}
if (p->len == 0 || consume > p->len || (consume == 0 && free_partial)) {
struct pbuf *q;
q = p->next;
// decrement the number of bytes copied by the length of the buffer
if(consume > p->len)
consume -= p->len;
// Free the current pbuf
// NOTE: This operation is interrupt safe, but not thread safe.
if (q != NULL) {
pbuf_ref(q);
}
pbuf_free(p);
p = q;
}
} while (consume);
return p;
}
void LWIPInterface::socket_attach_send(void *handle, void (*callback)(void *), void *id)
static err_t tcp_sent_irq(void *handle, struct tcp_pcb *tpcb, uint16_t len)
{
struct lwip_socket *s = (struct lwip_socket *)handle;
if (s->callback) {
s->callback(s->data);
}
return ERR_OK;
}
void LWIPInterface::socket_attach_recv(void *handle, void (*callback)(void *), void *id)
int LWIPInterface::socket_send(void *handle, const void *buf, unsigned size)
{
struct lwip_socket *s = (struct lwip_socket *)handle;
if (tcp_write(s->tcp, buf, size, TCP_WRITE_FLAG_COPY)) {
return NSAPI_ERROR_DEVICE_ERROR;
}
tcp_output(s->tcp);
return size;
}
static err_t tcp_recv_irq(void *handle, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
struct lwip_socket *s = (struct lwip_socket *)handle;
// Check for disconnect
if (!p) {
// Zero pcb during disconnect, since disconnect will cause a free
switch (tpcb->state) {
case FIN_WAIT_1:
case FIN_WAIT_2:
case TIME_WAIT:
s->tcp = 0;
break;
default:
break;
}
return ERR_OK;
}
__disable_irq();
if (!s->rx_chain) {
s->rx_chain = p;
} else {
pbuf_cat(s->rx_chain, p);
}
__enable_irq();
if (s->callback) {
s->callback(s->data);
}
return ERR_OK;
}
int LWIPInterface::socket_recv(void *handle, void *buf, unsigned size)
{
struct lwip_socket *s = (struct lwip_socket *)handle;
// Disconnected
if (!s->tcp && !s->rx_chain) {
return NSAPI_ERROR_NO_CONNECTION;
}
// Nothing ready
if (!s->rx_chain) {
return NSAPI_ERROR_WOULD_BLOCK;
}
// Copy out pbuf
struct pbuf *p = s->rx_chain;
int copied = pbuf_copy_partial(p, buf, size, 0);
s->rx_chain = pbuf_consume(p, copied, false);
// Update TCP window
tcp_recved(s->tcp, copied);
return copied;
}
int LWIPInterface::socket_sendto(void *handle, const SocketAddress &addr, const void *buf, unsigned size)
{
struct lwip_socket *s = (struct lwip_socket *)handle;
struct pbuf *pb = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
if (pbuf_take(pb, buf, size)) {
return NSAPI_ERROR_DEVICE_ERROR;
}
ip_addr_t id_addr;
inet_aton(addr.get_ip_address(), &id_addr);
err_t err = udp_sendto(s->udp, pb, &id_addr, addr.get_port());
pbuf_free(pb);
if (err) {
return NSAPI_ERROR_DEVICE_ERROR;
}
if (s->callback) {
s->callback(s->data);
}
return size;
}
static void udp_recv_irq(
void *handle, struct udp_pcb *upcb, struct pbuf *p,
struct ip_addr *addr, uint16_t port)
{
struct lwip_socket *s = (struct lwip_socket *)handle;
__disable_irq();
if (!s->rx_chain) {
s->rx_chain = p;
} else {
// Attach p to buffer chain without changing the tot_len
// NOTE: This is not how pbufs are intended to work, but it is necessary to deal with
// a) fragmentation and b) packet queueing
struct pbuf *q = s->rx_chain;
while (q->next) { q = q->next; }
q->next = p;
}
__enable_irq();
if (s->callback) {
s->callback(s->data);
}
}
int LWIPInterface::socket_recvfrom(void *handle, SocketAddress *addr, void *buf, unsigned size)
{
struct lwip_socket *s = (struct lwip_socket *)handle;
// Disconnected
if (!s->udp && !s->rx_chain) {
return NSAPI_ERROR_NO_CONNECTION;
}
// Nothing ready
if (!s->rx_chain) {
return NSAPI_ERROR_WOULD_BLOCK;
}
struct pbuf *p = s->rx_chain;
if (addr) {
struct udp_hdr *udphdr;
struct ip_hdr *iphdr;
// roll back the pbuf by udp_hdr to find the source port
pbuf_header(p, UDP_HLEN);
udphdr = (struct udp_hdr *)p->payload;
// roll back the pbuf by ip_hdr to find the source IP
pbuf_header(p, IP_HLEN);
iphdr = (struct ip_hdr *)p->payload;
// put the pbuf back where it was
pbuf_header(p, -UDP_HLEN - IP_HLEN);
addr->set_ip_address(inet_ntoa(iphdr->src));
addr->set_port(ntohs(udphdr->src));
}
// Copy out pbuf
size = size < p->tot_len ? size : p->tot_len;
int copied = pbuf_copy_partial(p, buf, size, 0);
s->rx_chain = pbuf_consume(p, p->tot_len, true);
return copied;
}
void LWIPInterface::socket_attach(void *handle, void (*callback)(void *), void *data)
{
struct lwip_socket *s = (struct lwip_socket *)handle;
s->callback = callback;
s->data = data;
}

View File

@ -23,171 +23,129 @@
/** LWIPInterface class
* Implementation of the NetworkInterface for LWIP
* Implementation of the NetworkStack for LWIP
*/
class LWIPInterface : public EthernetInterface
class LWIPInterface : public NetworkStack, public EthernetInterface
{
public:
/** Start the interface
* @return 0 on success, negative on failure
* @return 0 on success, negative on failure
*/
virtual int connect();
/** Stop the interface
* @return 0 on success, negative on failure
* @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
*/
* @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
*/
* @return MAC address of the interface
*/
virtual const char *get_mac_address();
protected:
/** Create a socket
/param proto The type of socket to open, TCP or UDP
/return The alocated socket or null on failure
*/
virtual void *socket_create(nsapi_protocol_t proto);
/** 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);
/** Destroy a socket
/param socket Previously allocated socket
*/
virtual void socket_destroy(void *handle);
/** Set socket options
\param handle Socket handle
\param optname Option ID
\param optval Option value
\param optlen Length of the option value
\return 0 on success, negative on failure
*/
virtual int socket_set_option(void *handle, int optname, const void *optval, unsigned int optlen);
/** Get socket options
\param handle Socket handle
\param optname Option ID
\param optval Buffer pointer where to write the option value
\param optlen Length of the option value
\return 0 on success, negative on failure
*/
virtual int socket_get_option(void *handle, int optname, void *optval, unsigned int *optlen);
/** 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 port The port to listen for incoming connections on
\return 0 on success, negative on failure.
*/
virtual int socket_bind(void *handle, int 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
*/
* @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
*/
* @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);
/** Check if the socket is connected
\param handle Socket handle
\return true if connected, false otherwise
*/
virtual bool socket_is_connected(void *handle);
/** Accept a new connection.
\param handle Socket handle
\param socket A TCPSocket instance that will handle the incoming connection.
\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 **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
*/
* @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
*/
* @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
*/
* @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
*/
* @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);
/** Close the socket
\param handle Socket handle
\param shutdown free the left-over data in message queues
*/
virtual int socket_close(void *handle, bool shutdown);
/** Register a callback on when a new connection is ready
\param handle Socket handle
\param callback Function to call when accept will succeed, may be called in
interrupt context.
\param id Argument to pass to callback
*/
virtual void socket_attach_accept(void *handle, void (*callback)(void *), void *id);
/** Register a callback on when send is ready
\param handle Socket handle
\param callback Function to call when accept will succeed, may be called in
interrupt context.
\param id Argument to pass to callback
*/
virtual void socket_attach_send(void *handle, void (*callback)(void *), void *id);
/** Register a callback on when recv is ready
\param handle Socket handle
\param callback Function to call when accept will succeed, may be called in
interrupt context.
\param id Argument to pass to callback
*/
virtual void socket_attach_recv(void *handle, void (*callback)(void *), void *id);
/** 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);
};
#endif

View File

@ -0,0 +1,51 @@
/* CellularInterface
* 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 CELLULAR_INTERFACE_H
#define CELLULAR_INTERFACE_H
#include "NetworkStack.h"
/** CellularInterface class
*
* Common interface that is shared between ethernet hardware
*/
class CellularInterface
{
public:
/** 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) = 0;
/** Stop the interface
*
* @return 0 on success, negative error code on failure
*/
virtual int disconnect() = 0;
/** Get the local MAC address
*
* @return Null-terminated representation of the local MAC address
*/
virtual const char *get_mac_address() = 0;
};
#endif

View File

@ -185,7 +185,7 @@ static int32_t query(UDPSocket *socket, const SocketAddress &addr, const char *h
return NSAPI_ERROR_DNS_FAILURE;
}
int32_t dnsQuery(NetworkInterface *iface, const char *host, char *ip)
int32_t dnsQuery(NetworkStack *iface, const char *host, char *ip)
{
if (isIP(host)) {
strcpy(ip, host);

View File

@ -18,7 +18,7 @@
#ifndef __DNSQUERY_H__
#define __DNSQUERY_H__
#include "NetworkInterface.h"
#include "NetworkStack.h"
#include "UDPSocket.h"
@ -34,7 +34,7 @@
* @returns 0 on succes, NS_DNS_FAILURE if host is not found,
* or a negative value for other errors.
*/
int32_t dnsQuery(NetworkInterface *iface, const char *host, char *ip);
int32_t dnsQuery(NetworkStack *iface, const char *host, char *ip);
int32_t dnsQuery(UDPSocket *sock, const char *host, char *ip);

View File

@ -1,4 +1,4 @@
/* Socket
/* EthernetInterface
* Copyright (c) 2015 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -17,23 +17,32 @@
#ifndef ETHERNET_INTERFACE_H
#define ETHERNET_INTERFACE_H
#include "NetworkInterface.h"
#include "NetworkStack.h"
/** EthernetInterface class
* Common interface that is shared between ethernet hardware
*
* Common interface that is shared between ethernet hardware.
*/
class EthernetInterface : public NetworkInterface
class EthernetInterface
{
public:
/** Start the interface
* @return 0 on success, negative on failure
*
* @return 0 on success, negative error code on failure
*/
virtual int connect() = 0;
/** Stop the interface
* @return 0 on success, negative on failure
*
* @return 0 on success, negative error code on failure
*/
virtual int disconnect() = 0;
/** Get the local MAC address
*
* @return Null-terminated representation of the local MAC address
*/
virtual const char *get_mac_address() = 0;
};
#endif

View File

@ -1,4 +1,4 @@
/* Socket
/* MeshInterface
* Copyright (c) 2015 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -17,23 +17,32 @@
#ifndef MESH_INTERFACE_H
#define MESH_INTERFACE_H
#include "NetworkInterface.h"
#include "NetworkStack.h"
/** MeshInterface class
* Common interface that is shared between ethernet hardware
*
* Common interface that is shared between mesh hardware
*/
class MeshInterface : public NetworkInterface
class MeshInterface
{
public:
/** Start the interface
*
* @return 0 on success, negative on failure
*/
virtual int connect() = 0;
/** Stop the interface
*
* @return 0 on success, negative on failure
*/
virtual int disconnect() = 0;
/** Get the local MAC address
*
* @return Null-terminated representation of the local MAC address
*/
virtual const char *get_mac_address() = 0;
};
#endif

View File

@ -1,23 +0,0 @@
/* Socket
* 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 "DnsQuery.h"
#include "mbed.h"
int NetworkInterface::gethostbyname(const char *name, char *dest)
{
return dnsQuery(this, name, dest);
}

View File

@ -1,235 +0,0 @@
/* Socket
* 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 NETWORK_INTERFACE_H
#define NETWORK_INTERFACE_H
#include "mbed.h"
#include "SocketAddress.h"
/**
* @enum ns_error_t
* @brief enum of standardized error codes
*/
enum nsapi_error_t {
NSAPI_ERROR_WOULD_BLOCK = -3001, /*!< no data is not available but call is non-blocking */
NSAPI_ERROR_UNSUPPORTED = -3002, /*!< unsupported configuration */
NSAPI_ERROR_NO_CONNECTION = -3003, /*!< not connected to a network */
NSAPI_ERROR_NO_SOCKET = -3004, /*!< socket not available for use */
NSAPI_ERROR_NO_ADDRESS = -3005, /*!< IP address is not known */
NSAPI_ERROR_NO_MEMORY = -3006, /*!< memory resource not available */
NSAPI_ERROR_DNS_FAILURE = -3007, /*!< DNS failed to complete successfully */
NSAPI_ERROR_DHCP_FAILURE = -3008, /*!< DHCP failed to complete successfully */
NSAPI_ERROR_AUTH_FAILURE = -3009, /*!< connection to access point faield */
NSAPI_ERROR_DEVICE_ERROR = -3010, /*!< failure interfacing with the network procesor */
};
/**
* @enum ns_opt_t
* @brief enum of available options
*/
enum ns_opt_t {
};
/** Enum of socket protocols
/enum protocol_t
*/
enum nsapi_protocol_t {
NSAPI_TCP, /*!< Socket is of TCP type */
NSAPI_UDP, /*!< Socket is of UDP type */
};
/** NetworkInterface class
* Common interface that is shared between all hardware that
* can connect to a network over IP.
*/
class NetworkInterface
{
public:
virtual ~NetworkInterface() {};
/** Get the internally stored IP address
/return IP address of the interface or null if not yet connected
*/
virtual const char *get_ip_address() = 0;
/** Get the internally stored MAC address
/return MAC address of the interface
*/
virtual const char *get_mac_address() = 0;
/** Get the current status of the interface
/return true if connected
*/
virtual bool is_connected() {
return get_ip_address() != NULL;
}
/** Looks up the specified host's IP address
/param name Hostname to lookup
/param dest Destination for IP address, must have space for SocketAddress::IP_SIZE
/return 0 on success, negative on failure
*/
virtual int gethostbyname(const char *name, char *dest);
protected:
friend class Socket;
friend class UDPSocket;
friend class TCPSocket;
friend class TCPServer;
/** Create a socket
/param proto The type of socket to open, TCP or UDP
/return The alocated socket or null on failure
*/
virtual void *socket_create(nsapi_protocol_t proto) = 0;
/** Destroy a socket
/param socket Previously allocated socket
*/
virtual void socket_destroy(void *handle) = 0;
/** Set socket options
\param handle Socket handle
\param optname Option ID
\param optval Option value
\param optlen Length of the option value
\return 0 on success, negative on failure
*/
virtual int socket_set_option(void *handle, int optname, const void *optval, unsigned int optlen) = 0;
/** Get socket options
\param handle Socket handle
\param optname Option ID
\param optval Buffer pointer where to write the option value
\param optlen Length of the option value
\return 0 on success, negative on failure
*/
virtual int socket_get_option(void *handle, int optname, void *optval, unsigned int *optlen) = 0;
/** Bind a server socket to a specific port
\param handle Socket handle
\param port The port to listen for incoming connections on
\return 0 on success, negative on failure.
*/
virtual int socket_bind(void *handle, int port) = 0;
/** 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) = 0;
/** 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) = 0;
/** Check if the socket is connected
\param handle Socket handle
\return true if connected, false otherwise
*/
virtual bool socket_is_connected(void *handle) = 0;
/** Accept a new connection.
\param handle Socket handle
\param socket A TCPSocket instance that will handle the incoming connection.
\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 **connection) = 0;
/** 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) = 0;
/** 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) = 0;
/** 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) = 0;
/** 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) = 0;
/** Close the socket
\param handle Socket handle
\param shutdown free the left-over data in message queues
*/
virtual int socket_close(void *handle, bool shutdown) = 0;
/** Register a callback on when a new connection is ready
\param handle Socket handle
\param callback Function to call when accept will succeed, may be called in
interrupt context.
\param id Argument to pass to callback
*/
virtual void socket_attach_accept(void *handle, void (*callback)(void *), void *id) = 0;
/** Register a callback on when send is ready
\param handle Socket handle
\param callback Function to call when accept will succeed, may be called in
interrupt context.
\param id Argument to pass to callback
*/
virtual void socket_attach_send(void *handle, void (*callback)(void *), void *id) = 0;
/** Register a callback on when recv is ready
\param handle Socket handle
\param callback Function to call when accept will succeed, may be called in
interrupt context.
\param id Argument to pass to callback
*/
virtual void socket_attach_recv(void *handle, void (*callback)(void *), void *id) = 0;
};
#endif

View File

@ -0,0 +1,50 @@
/* Socket
* 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 "DnsQuery.h"
#include "mbed.h"
int NetworkStack::gethostbyname(SocketAddress *address, const char *name)
{
char buffer[NSAPI_IP_SIZE];
int err = dnsQuery(this, name, buffer);
if (err) {
return err;
}
address->set_ip_address(buffer);
return 0;
}
int NetworkStack::setstackopt(int level, int optname, const void *optval, unsigned optlen)
{
return NSAPI_ERROR_UNSUPPORTED;
}
int NetworkStack::getstackopt(int level, int optname, void *optval, unsigned *optlen)
{
return NSAPI_ERROR_UNSUPPORTED;
}
int NetworkStack::setsockopt(void *handle, int level, int optname, const void *optval, unsigned optlen)
{
return NSAPI_ERROR_UNSUPPORTED;
}
int NetworkStack::getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen)
{
return NSAPI_ERROR_UNSUPPORTED;
}

View File

@ -0,0 +1,338 @@
/* NetworkStack
* 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 NETWORK_INTERFACE_H
#define NETWORK_INTERFACE_H
#include "mbed.h"
#include "SocketAddress.h"
/** Enum of standardized error codes
*
* Valid error codes have negative values and may
* be returned by any network operation.
*
* @enum nsapi_error_t
*/
enum nsapi_error_t {
NSAPI_ERROR_WOULD_BLOCK = -3001, /*!< no data is not available but call is non-blocking */
NSAPI_ERROR_UNSUPPORTED = -3002, /*!< unsupported functionality */
NSAPI_ERROR_PARAMETER = -3003, /*!< invalid configuration */
NSAPI_ERROR_NO_CONNECTION = -3004, /*!< not connected to a network */
NSAPI_ERROR_NO_SOCKET = -3005, /*!< socket not available for use */
NSAPI_ERROR_NO_ADDRESS = -3006, /*!< IP address is not known */
NSAPI_ERROR_NO_MEMORY = -3007, /*!< memory resource not available */
NSAPI_ERROR_DNS_FAILURE = -3008, /*!< DNS failed to complete successfully */
NSAPI_ERROR_DHCP_FAILURE = -3009, /*!< DHCP failed to complete successfully */
NSAPI_ERROR_AUTH_FAILURE = -3010, /*!< connection to access point faield */
NSAPI_ERROR_DEVICE_ERROR = -3011, /*!< failure interfacing with the network procesor */
};
/** Enum of socket protocols
*
* The socket protocol specifies a particular protocol to
* be used with a newly created socket.
*
* @enum nsapi_protocol_t
*/
enum nsapi_protocol_t {
NSAPI_TCP, /*!< Socket is of TCP type */
NSAPI_UDP, /*!< Socket is of UDP type */
};
/* Enum of standardized stack option levels
*
* @enum nsapi_level_t
*/
enum nsapi_level_t {
NSAPI_STACK, /*!< Stack option level */
NSAPI_SOCKET, /*!< Socket option level */
};
/* Enum of standardized stack options
*
* These options may not be supported on all stacks, in which
* case NSAPI_ERROR_UNSUPPORTED may be returned from setsockopt.
*
* @enum nsapi_option_t
*/
enum nsapi_option_t {
NSAPI_REUSEADDR, /*!< Allow bind to reuse local addresses */
NSAPI_KEEPALIVE, /*!< Enables sending of keepalive messages */
NSAPI_LINGER, /*!< Keeps close from returning until queues empty */
NSAPI_SNDBUF, /*!< Sets send buffer size */
NSAPI_RCVBUF, /*!< Sets recv buffer size */
};
/** NetworkStack class
*
* Common interface that is shared between hardware that
* can connect to a network over IP. By implementing the
* NetworkStack, a network stack can be used as a target
* for instantiating network sockets.
*/
class NetworkStack
{
public:
virtual ~NetworkStack() {};
/** Get the local IP address
*
* @return Null-terminated representation of the local IP address
* or null if not yet connected
*/
virtual const char *get_ip_address() = 0;
/** Translates a hostname to an IP address
*
* The hostname may be either a domain name or an IP address. If the
* hostname is an IP address, no network transactions will be performed.
*
* If no stack-specific DNS resolution is provided, the hostname
* will be resolve using a UDP socket on the stack.
*
* @param address Destination for the host SocketAddress
* @param host Hostname to resolve
* @return 0 on success, negative error code on failure
*/
virtual int gethostbyname(SocketAddress *address, const char *host);
/* Set stack-specific stack options
*
* The setstackopt allow an application to pass stack-specific hints
* to the underlying stack. For unsupported options,
* NSAPI_ERROR_UNSUPPORTED is returned and the stack is unmodified.
*
* @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 int setstackopt(int level, int optname, const void *optval, unsigned optlen);
/* Get stack-specific stack 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 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 int getstackopt(int level, int optname, void *optval, unsigned *optlen);
protected:
friend class Socket;
friend class UDPSocket;
friend class TCPSocket;
friend class TCPServer;
/** Opens a socket
*
* 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.
*
* @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 int socket_open(void **handle, nsapi_protocol_t proto) = 0;
/** Close the 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 int socket_close(void *handle) = 0;
/** Bind a specific address to a socket
*
* Binding a socket specifies the address and port on which to recieve
* data. If the IP address is zeroed, only the port is bound.
*
* @param handle Socket handle
* @param address Local address to bind
* @return 0 on success, negative error code on failure.
*/
virtual int socket_bind(void *handle, const SocketAddress &address) = 0;
/** 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
* @return 0 on success, negative error code on failure
*/
virtual int socket_listen(void *handle, int backlog) = 0;
/** 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 int socket_connect(void *handle, const SocketAddress &address) = 0;
/** 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 handle Destination for a handle to the newly created sockey
* @param server Socket handle to server to accept from
* @return 0 on success, negative error code on failure
*/
virtual int socket_accept(void **handle, void *server) = 0;
/** 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 call is non-blocking. If send would block,
* NSAPI_ERROR_WOULD_BLOCK is returned immediately.
*
* @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 int socket_send(void *handle, const void *data, unsigned size) = 0;
/** 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 call is non-blocking. If recv would block,
* NSAPI_ERROR_WOULD_BLOCK is returned immediately.
*
* @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 int socket_recv(void *handle, void *data, unsigned size) = 0;
/** Send a packet over a UDP socket
*
* Sends data to the specified address. Returns the number of bytes
* sent from the buffer.
*
* This call is non-blocking. If sendto would block,
* NSAPI_ERROR_WOULD_BLOCK is returned immediately.
*
* @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 int socket_sendto(void *handle, const SocketAddress &address, const void *data, unsigned size) = 0;
/** 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.
*
* This call is non-blocking. If recvfrom would block,
* NSAPI_ERROR_WOULD_BLOCK is returned immediately.
*
* @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 int socket_recvfrom(void *handle, SocketAddress *address, void *buffer, unsigned size) = 0;
/** 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(void *handle, void (*callback)(void *), void *data) = 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 int setsockopt(void *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 int getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen);
};
#endif

View File

@ -16,67 +16,156 @@
#include "Socket.h"
Socket::Socket(NetworkInterface *iface, nsapi_protocol_t proto)
: _iface(iface)
, _blocking(true)
, _timeout(0)
Socket::Socket()
: _iface(0)
, _socket(0)
, _timeout(osWaitForever)
{
_socket = _iface->socket_create(proto);
}
Socket::~Socket()
{
if (_socket) {
close(false);
// Underlying close is thread safe
close();
}
int Socket::open(NetworkStack *iface, nsapi_protocol_t proto)
{
_lock.lock();
if (_iface != NULL) {
_lock.unlock();
return NSAPI_ERROR_PARAMETER;
}
_iface = iface;
void *socket;
int err = _iface->socket_open(&socket, proto);
if (err) {
_lock.unlock();
return err;
}
_socket = socket;
_iface->socket_attach(_socket, &Socket::thunk, this);
_lock.unlock();
return 0;
}
int Socket::close()
{
_lock.lock();
int ret = 0;
if (_socket) {
_iface->socket_attach(_socket, 0, 0);
void * socket = _socket;
_socket = 0;
ret = _iface->socket_close(socket);
}
// Wakeup anything in a blocking operation
// on this socket
socket_event();
_lock.unlock();
return ret;
}
int Socket::bind(uint16_t port)
{
// Underlying bind is thread safe
SocketAddress addr(0, port);
return bind(addr);
}
int Socket::bind(const char *address, uint16_t port)
{
// Underlying bind is thread safe
SocketAddress addr(address, port);
return bind(addr);
}
int Socket::bind(const SocketAddress &address)
{
_lock.lock();
int ret = NSAPI_ERROR_NO_SOCKET;
if (_socket) {
ret = _iface->socket_bind(_socket, address);
}
_lock.unlock();
return ret;
}
void Socket::set_blocking(bool blocking)
{
_blocking = blocking;
// Socket::set_timeout is thread safe
set_timeout(blocking ? -1 : 0);
}
void Socket::set_timeout(unsigned timeout)
void Socket::set_timeout(int timeout)
{
_timeout = timeout;
}
_lock.lock();
int Socket::set_option(int optname, const void *optval, unsigned int optlen)
{
if (!_socket) {
return NSAPI_ERROR_NO_SOCKET;
if (timeout >= 0) {
_timeout = (uint32_t)timeout;
} else {
_timeout = osWaitForever;
}
return _iface->socket_set_option(_socket, optname, optval, optlen);
_lock.unlock();
}
int Socket::get_option(int optname, void *optval, unsigned int *optlen)
int Socket::setsockopt(int level, int optname, const void *optval, unsigned optlen)
{
if (!_socket) {
return NSAPI_ERROR_NO_SOCKET;
_lock.lock();
int ret = NSAPI_ERROR_NO_SOCKET;
if (_socket) {
ret = _iface->setsockopt(_socket, level, optname, optval, optlen);
}
return _iface->socket_get_option(_socket, optname, optval, optlen);
_lock.unlock();
return ret;
}
int Socket::close(bool shutdown)
int Socket::getsockopt(int level, int optname, void *optval, unsigned *optlen)
{
if (!_socket) {
return 0;
_lock.lock();
int ret = NSAPI_ERROR_NO_SOCKET;
if (_socket) {
ret = _iface->getsockopt(_socket, level, optname, optval, optlen);
}
int err = _iface->socket_close(_socket, shutdown);
if (!err) {
void *socket = _socket;
_socket = 0;
_iface->socket_destroy(socket);
}
_lock.unlock();
return ret;
return err;
}
void Socket::thunk(void *p)
void Socket::attach(FunctionPointer callback)
{
FunctionPointer *fptr = (FunctionPointer *)p;
(*fptr)();
_lock.lock();
_callback = callback;
_lock.unlock();
}
void Socket::thunk(void *data)
{
Socket *self = (Socket *)data;
self->socket_event();
}
void Socket::socket_event(void)
{
if (_callback) {
_callback();
}
}

View File

@ -18,56 +18,166 @@
#define SOCKET_H
#include "SocketAddress.h"
#include "NetworkInterface.h"
#include "NetworkStack.h"
#include "Mutex.h"
/** Abstract socket class
*/
class Socket {
public:
/** Socket lifetime
/** Destroy a socket
*
* Closes socket if the socket is still open
*/
virtual ~Socket();
/** Set blocking or non-blocking mode of the socket
\param blocking true for blocking mode, false for non-blocking mode.
*/
void set_blocking(bool blocking);
/** Set timeout on a socket operation if blocking behaviour is enabled
\param timeout timeout in ms
*/
void set_timeout(unsigned int timeout);
/** Set socket options
\param optname Option ID
\param optval Option value
\param optlen Length of the option value
\return 0 on success, negative on failure
*/
int set_option(int optname, const void *optval, unsigned optlen);
/** Get socket options
\param optname Option ID
\param optval Buffer pointer where to write the option value
\param optlen Length of the option value
\return 0 on success, negative on failure
*/
int get_option(int optname, void *optval, unsigned *optlen);
/** Opens a socket
*
* Creates a network socket on the specified network stack.
* Not needed if stack is passed to the socket's constructor.
*
* @param iface Network stack as target for socket
* @return 0 on success, negative error code on failure
*/
virtual int open(NetworkStack *iface) = 0;
/** Close the socket
\param shutdown free the left-over data in message queues
*/
int close(bool shutdown=true);
*
* Closes any open connection and deallocates any memory associated
* with the socket. Called from destructor if socket is not closed.
*
* @return 0 on success, negative error code on failure
*/
int close();
/** Bind a specific address to a socket
*
* Binding a socket specifies the address and port on which to recieve
* data.
*
* @param port Local port to bind
* @return 0 on success, negative error code on failure.
*/
int bind(uint16_t port);
/** Bind a specific address to a socket
*
* Binding a socket specifies the address and port on which to recieve
* data. If the IP address is zeroed, only the port is bound.
*
* @param address Null-terminated local address to bind
* @param port Local port to bind
* @return 0 on success, negative error code on failure.
*/
int bind(const char *address, uint16_t port);
/** Bind a specific address to a socket
*
* Binding a socket specifies the address and port on which to recieve
* data. If the IP address is zeroed, only the port is bound.
*
* @param address Local address to bind
* @return 0 on success, negative error code on failure.
*/
int bind(const SocketAddress &address);
/** Set blocking or non-blocking mode of the socket
*
* Initially all sockets are in blocking mode. In non-blocking mode
* blocking operations such as send/recv/accept return
* NSAPI_ERROR_WOULD_BLOCK if they can not continue.
*
* set_blocking(false) is equivalent to set_timeout(-1)
* set_blocking(true) is equivalent to set_timeout(0)
*
* @param blocking true for blocking mode, false for non-blocking mode.
*/
void set_blocking(bool blocking);
/** Set timeout on blocking socket operations
*
* Initially all sockets have unbounded timeouts. NSAPI_ERROR_WOULD_BLOCK
* is returned if a blocking operation takes longer than the specified
* timeout. A timeout of 0 removes the timeout from the socket. A negative
* value give the socket an unbounded timeout.
*
* set_timeout(0) is equivalent to set_blocking(false)
* set_timeout(-1) is equivalent to set_blocking(true)
*
* @param timeout Timeout in milliseconds
*/
void set_timeout(int timeout);
/* 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 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
*/
int setsockopt(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 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
*/
int getsockopt(int level, int optname, void *optval, unsigned *optlen);
/** 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 callback Function to call on state change
*/
void attach(FunctionPointer callback);
/** 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 tptr Pointer to object to call method on
* @param mptr Method to call on state change
*/
template <typename T, typename M>
void attach(T *tptr, M mptr) {
attach(FunctionPointer(tptr, mptr));
}
protected:
Socket(NetworkInterface *iface, nsapi_protocol_t proto);
Socket();
int open(NetworkStack *iface, nsapi_protocol_t proto);
static void thunk(void *);
virtual void socket_event(void);
NetworkInterface *_iface;
NetworkStack *_iface;
void *_socket;
bool _blocking;
unsigned _timeout;
uint32_t _timeout;
FunctionPointer _callback;
rtos::Mutex _lock;
};
#endif

View File

@ -15,37 +15,210 @@
*/
#include "SocketAddress.h"
#include "NetworkInterface.h"
#include "NetworkStack.h"
#include <string.h>
#include "mbed.h"
SocketAddress::SocketAddress(NetworkInterface *iface, const char *host, uint16_t port)
{
int err = iface->gethostbyname(host, _ip_address);
set_port(port);
if (err) {
_ip_address[0] = '\0';
_port = 0;
static bool ipv4_is_valid(const char *addr)
{
int i = 0;
// Check each digit for [0-9.]
for (; addr[i]; i++) {
if (!(addr[i] >= '0' && addr[i] <= '9') && addr[i] != '.') {
return false;
}
}
// Ending with '.' garuntees host
if (i > 0 && addr[i-1] == '.') {
return false;
}
return true;
}
static bool ipv6_is_valid(const char *addr)
{
// Check each digit for [0-9a-fA-F:]
for (int i = 0; addr[i]; i++) {
if (!(addr[i] >= '0' && addr[i] <= '9') &&
!(addr[i] >= 'a' && addr[i] <= 'f') &&
!(addr[i] >= 'A' && addr[i] <= 'F') &&
addr[i] != ':') {
return false;
}
}
return true;
}
static void ipv4_from_address(uint8_t *bytes, const char *addr)
{
int count = 0;
int i = 0;
for (; count < NSAPI_IPv4_BYTES; count++) {
int scanned = sscanf(&addr[i], "%hhu", &bytes[count]);
if (scanned < 1) {
return;
}
for (; addr[i] != '.'; i++) {
if (!addr[i]) {
return;
}
}
i++;
}
}
static int ipv6_scan_chunk(uint16_t *shorts, const char *chunk) {
int count = 0;
int i = 0;
for (; count < NSAPI_IPv6_BYTES/2; count++) {
int scanned = sscanf(&chunk[i], "%hx", &shorts[count]);
if (scanned < 1) {
return count;
}
for (; chunk[i] != ':'; i++) {
if (!chunk[i]) {
return count+1;
}
}
i++;
}
return count;
}
static void ipv6_from_address(uint8_t *bytes, const char *addr)
{
// Start with zeroed address
uint16_t shorts[NSAPI_IPv6_BYTES/2];
memset(shorts, 0, sizeof shorts);
int suffix = 0;
// Find double colons and scan suffix
for (int i = 0; addr[i]; i++) {
if (addr[i] == ':' && addr[i+1] == ':') {
suffix = ipv6_scan_chunk(shorts, &addr[i+2]);
break;
}
}
// Move suffix to end
memmove(&shorts[NSAPI_IPv6_BYTES/2-suffix], &shorts[0],
suffix*sizeof(uint16_t));
// Scan prefix
ipv6_scan_chunk(shorts, &addr[0]);
// Flip bytes
for (int i = 0; i < NSAPI_IPv6_BYTES/2; i++) {
bytes[2*i+0] = (uint8_t)(shorts[i] >> 8);
bytes[2*i+1] = (uint8_t)(shorts[i] >> 0);
}
}
static void ipv4_to_address(char *addr, const uint8_t *bytes)
{
sprintf(addr, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3]);
}
static void ipv6_to_address(char *addr, const uint8_t *bytes)
{
for (int i = 0; i < NSAPI_IPv6_BYTES/2; i++) {
sprintf(&addr[5*i], "%02x%02x", bytes[2*i], bytes[2*i+1]);
addr[5*i+4] = ':';
}
addr[NSAPI_IPv6_SIZE-1] = '\0';
}
SocketAddress::SocketAddress(NetworkStack *iface, const char *host, uint16_t port)
{
memset(&_ip_address, 0, sizeof _ip_address);
// Check for valid IP addresses
if (host && ipv4_is_valid(host)) {
_ip_version = NSAPI_IPv4;
ipv4_from_address(_ip_bytes, host);
set_port(port);
} else if (host && ipv6_is_valid(host)) {
_ip_version = NSAPI_IPv6;
ipv4_from_address(_ip_bytes, host);
set_port(port);
} else {
// DNS lookup
int err = iface->gethostbyname(this, host);
if (!err) {
set_port(port);
} else {
_ip_version = NSAPI_IPv4;
memset(_ip_bytes, 0, NSAPI_IPv4_BYTES);
set_port(0);
}
}
}
SocketAddress::SocketAddress(const char *addr, uint16_t port)
{
memset(&_ip_address, 0, sizeof _ip_address);
set_ip_address(addr);
set_port(port);
}
SocketAddress::SocketAddress(const void *bytes, nsapi_version_t version, uint16_t port)
{
memset(&_ip_address, 0, sizeof _ip_address);
set_ip_bytes(bytes, version);
set_port(port);
}
SocketAddress::SocketAddress(const SocketAddress &addr)
{
set_ip_address(addr.get_ip_address());
memset(&_ip_address, 0, sizeof _ip_address);
set_ip_bytes(addr.get_ip_bytes(), addr.get_ip_version());
set_port(addr.get_port());
}
void SocketAddress::set_ip_address(const char *addr)
{
strncpy(_ip_address, addr, sizeof _ip_address);
_ip_address[sizeof _ip_address - 1] = '\0';
_ip_address[0] = '\0';
if (addr && ipv4_is_valid(addr)) {
_ip_version = NSAPI_IPv4;
ipv4_from_address(_ip_bytes, addr);
} else if (addr && ipv6_is_valid(addr)) {
_ip_version = NSAPI_IPv6;
ipv6_from_address(_ip_bytes, addr);
} else {
_ip_version = NSAPI_IPv4;
memset(_ip_bytes, 0, NSAPI_IPv4_BYTES);
}
}
void SocketAddress::set_ip_bytes(const void *bytes, nsapi_version_t version)
{
_ip_address[0] = '\0';
if (version == NSAPI_IPv4) {
_ip_version = NSAPI_IPv4;
memcpy(_ip_bytes, bytes, NSAPI_IPv4_BYTES);
} else if (version == NSAPI_IPv6) {
_ip_version = NSAPI_IPv6;
memcpy(_ip_bytes, bytes, NSAPI_IPv6_BYTES);
} else {
_ip_version = NSAPI_IPv4;
memset(_ip_bytes, 0, NSAPI_IPv4_BYTES);
}
}
void SocketAddress::set_port(uint16_t port)
@ -55,13 +228,48 @@ void SocketAddress::set_port(uint16_t port)
const char *SocketAddress::get_ip_address() const
{
if (!_ip_address[0]) {
return 0;
char *ip_address = (char *)_ip_address;
if (!ip_address[0]) {
if (_ip_version == NSAPI_IPv4) {
ipv4_to_address(ip_address, _ip_bytes);
} else if (_ip_version == NSAPI_IPv6) {
ipv6_to_address(ip_address, _ip_bytes);
}
}
return _ip_address;
return ip_address;
}
const void *SocketAddress::get_ip_bytes() const
{
return _ip_bytes;
}
nsapi_version_t SocketAddress::get_ip_version() const
{
return _ip_version;
}
uint16_t SocketAddress::get_port() const
{
return _port;
}
SocketAddress::operator bool() const
{
int count = 0;
if (_ip_version == NSAPI_IPv4) {
count = NSAPI_IPv4_BYTES;
} else if (_ip_version == NSAPI_IPv6) {
count = NSAPI_IPv6_BYTES;
}
for (int i = 0; i < count; i++) {
if (_ip_bytes[i]) {
return true;
}
}
return false;
}

View File

@ -1,4 +1,4 @@
/* Socket
/* SocketAddress
* Copyright (c) 2015 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,64 +19,147 @@
#include <stdint.h>
/** Maximum size of IP address
*/
#define NSAPI_IP_SIZE 16
/** Maximum size of MAC address
*/
/** Maximum size of IP address representation
*/
#define NSAPI_IP_SIZE NSAPI_IPv6_SIZE
/** Maximum number of bytes for IP address
*/
#define NSAPI_IP_BYTES NSAPI_IPv6_BYTES
/** Maximum size of MAC address representation
*/
#define NSAPI_MAC_SIZE 18
// Predeclared classes
class NetworkInterface;
/** Maximum number of bytes for MAC address
*/
#define NSAPI_MAC_BYTES 6
/**
* A general socket address composed of the IP address and port
/** Enum of IP address versions
*
* The IP version specifies the type of an IP address.
*
* @enum nsapi_version_t
*/
enum nsapi_version_t {
NSAPI_IPv4, /*!< Address is IPv4 */
NSAPI_IPv6, /*!< Address is IPv6 */
};
/** Size of IPv4 representation
*/
#define NSAPI_IPv4_SIZE 16
/** Number of bytes in IPv4 address
*/
#define NSAPI_IPv4_BYTES 4
/** Size of IPv6 representation
*/
#define NSAPI_IPv6_SIZE 40
/** Number of bytes in IPv6 address
*/
#define NSAPI_IPv6_BYTES 16
// Predeclared classes
class NetworkStack;
/** SocketAddress class
*
* Representation of an IP address and port pair.
*/
class SocketAddress {
public:
/** SocketAddress construction using DNS resolution
/param iface NetworkInterface to use for DNS resolution
/param addr Null-terminated hostname that will be resolved
/param port 16-bit port
/note on failure, IP address and port will be set to null
*/
SocketAddress(NetworkInterface *iface, const char *addr, uint16_t port = 0);
/** Create a SocketAddress from a hostname and port
*
* The hostname may be either a domain name or an IP address. If the
* hostname is an IP address, no network transactions will be performed.
*
* On failure, the IP address and port will be set to zero
*
* @param iface Network stack to use for DNS resolution
* @param host Hostname to resolve
* @param port Optional 16-bit port
*/
SocketAddress(NetworkStack *iface, const char *host, uint16_t port = 0);
/** SocketAddress construction
/param addr Null-terminated IP address
/param port 16-bit port
/note on failure, IP address and port will be set to null
*/
/** Create a SocketAddress from an IP address and port
*
* @param host Null-terminated representation of the IP address
* @param port Optional 16-bit port
*/
SocketAddress(const char *addr = 0, uint16_t port = 0);
/** SocketAddress construction
/param addr SocketAddress to copy
*/
/** Create a SocketAddress from a raw IP address and port
*
* @param bytes Raw IP address in big-endian order
* @param version IP address version, NSAPI_IPv4 or NSAPI_IPv6
* @param port Optional 16-bit port
*/
SocketAddress(const void *bytes, nsapi_version_t version, uint16_t port = 0);
/** Create a SocketAddress from another SocketAddress
*
* @param address SocketAddress to copy
*/
SocketAddress(const SocketAddress &addr);
/** Set the IP address
\param addr Null-terminated string representing the IP address
*
* @param addr Null-terminated represention of the IP address
*/
void set_ip_address(const char *addr);
/** Set the raw IP address
*
* @param bytes Raw IP address in big-endian order
* @param version IP address version, NSAPI_IPv4 or NSAPI_IPv6
*/
void set_ip_bytes(const void *bytes, nsapi_version_t version);
/** Set the port
\param port 16-bit port
*
* @param port 16-bit port
*/
void set_port(uint16_t port);
/** Get the IP address
\return The string representation of the IP Address
*
* @return Null-terminated representation of the IP Address
*/
const char *get_ip_address() const;
/** Get the raw IP address
*
* @return Raw IP address in big-endian order
*/
const void *get_ip_bytes() const;
/** Get the IP address version
*
* @return IP address version, NSAPI_IPv4 or NSAPI_IPv6
*/
nsapi_version_t get_ip_version() const;
/** Get the port
\return The 16-bit port
*
* @return The 16-bit port
*/
uint16_t get_port(void) const;
uint16_t get_port() const;
/** Test if address is zero
*
* @return True if address is not zero
*/
operator bool() const;
private:
char _ip_address[NSAPI_IP_SIZE];
uint8_t _ip_bytes[NSAPI_IP_BYTES];
nsapi_version_t _ip_version;
uint16_t _port;
};

View File

@ -17,71 +17,85 @@
#include "TCPServer.h"
#include "Timer.h"
TCPServer::TCPServer(NetworkInterface *iface)
: Socket(iface, NSAPI_TCP)
TCPServer::TCPServer(): _accept_sem(0)
{
}
int TCPServer::bind(uint16_t port)
TCPServer::TCPServer(NetworkStack *iface): _accept_sem(0)
{
if (!_socket) {
return NSAPI_ERROR_NO_SOCKET;
}
open(iface);
}
return _iface->socket_bind(_socket, port);
int TCPServer::open(NetworkStack *iface)
{
return Socket::open(iface, NSAPI_TCP);
}
int TCPServer::listen(int backlog)
{
if (!_socket) {
return NSAPI_ERROR_NO_SOCKET;
_lock.lock();
int ret = NSAPI_ERROR_NO_SOCKET;
if (_socket) {
ret = _iface->socket_listen(_socket, backlog);
}
return _iface->socket_listen(_socket, backlog);
_lock.unlock();
return ret;
}
int TCPServer::accept(TCPSocket *connection)
{
mbed::Timer timer;
timer.start();
void *socket = connection->_socket;
connection->_socket = 0;
_iface->socket_destroy(socket);
_lock.lock();
int ret = NSAPI_ERROR_NO_SOCKET;
while (true) {
if (!_socket) {
return NSAPI_ERROR_NO_SOCKET;
ret = NSAPI_ERROR_NO_SOCKET;
break;
}
int err = _iface->socket_accept(_socket, &socket);
void *socket;
ret = _iface->socket_accept(&socket, _socket);
if (0 == ret) {
connection->_lock.lock();
if (err > 0) {
if (connection->_socket) {
connection->close();
}
connection->_iface = _iface;
connection->_socket = socket;
_iface->socket_attach(socket, &Socket::thunk, connection);
connection->_lock.unlock();
break;
}
if (err != NSAPI_ERROR_WOULD_BLOCK || !_blocking ||
(_timeout && timer.read_ms() > _timeout)) {
return err;
if (NSAPI_ERROR_WOULD_BLOCK == ret) {
int32_t count;
_lock.unlock();
count = _accept_sem.wait(_timeout);
_lock.lock();
if (count < 1) {
ret = NSAPI_ERROR_WOULD_BLOCK;
break;
}
}
}
_lock.unlock();
return ret;
}
void TCPServer::attach_accept(FunctionPointer callback)
void TCPServer::socket_event()
{
_accept_cb = callback;
if (_socket && _accept_cb) {
return _iface->socket_attach_accept(_socket, Socket::thunk, &_accept_cb);
} else if (_socket) {
return _iface->socket_attach_accept(_socket, 0, 0);
int32_t status = _accept_sem.wait(0);
if (status <= 1) {
_accept_sem.release();
}
}
TCPServer::~TCPServer()
{
if (_socket && _accept_cb) {
_iface->socket_attach_accept(_socket, 0, 0);
}
Socket::socket_event();
}

View File

@ -1,4 +1,4 @@
/* Socket
/* TCPServer
* Copyright (c) 2015 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -19,49 +19,65 @@
#include "Socket.h"
#include "TCPSocket.h"
#include "NetworkInterface.h"
#include "NetworkStack.h"
#include "Semaphore.h"
/** TCP Server.
/** TCP socket server
*/
class TCPServer : public Socket {
public:
/** TCP Server lifetime
*/
TCPServer(NetworkInterface *iface);
virtual ~TCPServer();
/** Create an uninitialized socket
*
* Must call open to initialize the socket on a network stack.
*/
TCPServer();
/** Create a socket on a network stack
*
* Creates and opens a socket on the specified network stack.
*
* @param iface Network stack as target for socket
*/
TCPServer(NetworkStack *iface);
/** Opens a socket
*
* Creates a network socket on the specified network stack.
* Not needed if stack is passed to the socket's constructor.
*
* @param iface Network stack as target for socket
* @return 0 on success, negative error code on failure
*/
virtual int open(NetworkStack *iface);
/** Bind a socket to a specific port
\param port The port to listen for incoming connections on
\return 0 on success, negative on failure
*/
int bind(uint16_t port);
/** Listen for connections on a TCP socket
*
* Marks the socket as a passive socket that can be used to accept
* incoming connections.
*
* @param backlog Number of pending connections that can be queued
* simultaneously, defaults to 1
* @return 0 on success, negative error code on failure
*/
int listen(int backlog = 1);
/** Start listening for incoming connections
\param backlog Number of pending connections that can be queued up at any
one time [Default: 1]
\return 0 on success, negative on failure
*/
int listen(int backlog=1);
/** Accept a new connection.
\param socket A TCPSocket instance that will handle the incoming connection.
\return 0 on success, negative on failure.
*/
/** 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 using the specified
* socket instance.
*
* By default, accept blocks until data is sent. If socket is set to
* non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned
* immediately.
*
* @param socket TCPSocket instance that will handle the incoming connection.
* @return 0 on success, negative error code on failure
*/
int accept(TCPSocket *connection);
/** Register a callback on when a new connection is ready
\param callback Function to call when accept will succeed, may be called in
interrupt context.
*/
void attach_accept(FunctionPointer callback);
template <typename T, typename M>
void attach_accept(T *tptr, M mptr) {
attach_accept(FunctionPointer(tptr, mptr));
}
private:
FunctionPointer _accept_cb;
protected:
virtual void socket_event(void);
rtos::Semaphore _accept_sem;
};
#endif

View File

@ -17,101 +17,140 @@
#include "TCPSocket.h"
#include "Timer.h"
TCPSocket::TCPSocket(NetworkInterface *iface)
: Socket(iface, NSAPI_TCP)
TCPSocket::TCPSocket(): _read_sem(0), _write_sem(0)
{
}
TCPSocket::TCPSocket(NetworkStack *iface): _read_sem(0), _write_sem(0)
{
// TCPSocket::open is thread safe
open(iface);
}
int TCPSocket::open(NetworkStack *iface)
{
// Socket::open is thread safe
return Socket::open(iface, NSAPI_TCP);
}
int TCPSocket::connect(const SocketAddress &addr)
{
if (!_socket) {
return NSAPI_ERROR_NO_SOCKET;
_lock.lock();
int ret = NSAPI_ERROR_NO_SOCKET;
if (_socket) {
ret = _iface->socket_connect(_socket, addr);
}
return _iface->socket_connect(_socket, addr);
_lock.unlock();
return ret;
}
int TCPSocket::connect(const char *host, uint16_t port)
{
_lock.lock();
SocketAddress addr(_iface, host, port);
if (!addr.get_ip_address()) {
return NSAPI_ERROR_DNS_FAILURE;
int ret = NSAPI_ERROR_DNS_FAILURE;
if (addr) {
ret = connect(addr);
}
return connect(addr);
}
bool TCPSocket::is_connected()
{
return _socket && _iface->socket_is_connected(_socket);
_lock.unlock();
return ret;
}
int TCPSocket::send(const void *data, unsigned size)
{
mbed::Timer timer;
timer.start();
if (osOK != _write_lock.lock(_timeout)) {
return NSAPI_ERROR_WOULD_BLOCK;
}
_lock.lock();
int ret;
while (true) {
if (!_socket) {
return NSAPI_ERROR_NO_SOCKET;
ret = NSAPI_ERROR_NO_SOCKET;
break;
}
int sent = _iface->socket_send(_socket, data, size);
if (sent != NSAPI_ERROR_WOULD_BLOCK || !_blocking ||
(_timeout && timer.read_ms() > _timeout)) {
return sent;
if ((0 == _timeout) || (NSAPI_ERROR_WOULD_BLOCK != sent)) {
ret = sent;
break;
} else {
int32_t count;
// Release lock before blocking so other threads
// accessing this object aren't blocked
_lock.unlock();
count = _write_sem.wait(_timeout);
_lock.lock();
if (count < 1) {
// Semaphore wait timed out so break out and return
ret = NSAPI_ERROR_WOULD_BLOCK;
break;
}
}
}
_lock.unlock();
_write_lock.unlock();
return ret;
}
int TCPSocket::recv(void *data, unsigned size)
{
mbed::Timer timer;
timer.start();
if (osOK != _read_lock.lock(_timeout)) {
return NSAPI_ERROR_WOULD_BLOCK;
}
_lock.lock();
int ret;
while (true) {
if (!_socket) {
return NSAPI_ERROR_NO_SOCKET;
ret = NSAPI_ERROR_NO_SOCKET;
break;
}
int recv = _iface->socket_recv(_socket, data, size);
if (recv != NSAPI_ERROR_WOULD_BLOCK || !_blocking ||
(_timeout && timer.read_ms() > _timeout)) {
return recv;
if ((0 == _timeout) || (NSAPI_ERROR_WOULD_BLOCK != recv)) {
ret = recv;
break;
} else {
int32_t count;
// Release lock before blocking so other threads
// accessing this object aren't blocked
_lock.unlock();
count = _read_sem.wait(_timeout);
_lock.lock();
if (count < 1) {
// Semaphore wait timed out so break out and return
ret = NSAPI_ERROR_WOULD_BLOCK;
break;
}
}
}
_lock.unlock();
_read_lock.unlock();
return ret;
}
void TCPSocket::attach_send(FunctionPointer callback)
void TCPSocket::socket_event()
{
_send_cb = callback;
if (_socket && _send_cb) {
return _iface->socket_attach_send(_socket, Socket::thunk, &_send_cb);
} else if (_socket) {
return _iface->socket_attach_send(_socket, 0, 0);
}
}
void TCPSocket::attach_recv(FunctionPointer callback)
{
_recv_cb = callback;
if (_socket && _recv_cb) {
return _iface->socket_attach_recv(_socket, Socket::thunk, &_recv_cb);
} else if (_socket) {
return _iface->socket_attach_recv(_socket, 0, 0);
}
}
TCPSocket::~TCPSocket()
{
if (_socket && _send_cb) {
_iface->socket_attach_send(_socket, 0, 0);
}
if (_socket && _recv_cb) {
_iface->socket_attach_recv(_socket, 0, 0);
int32_t count;
count = _write_sem.wait(0);
if (count <= 1) {
_write_sem.release();
}
count = _read_sem.wait(0);
if (count <= 1) {
_read_sem.release();
}
Socket::socket_event();
}

View File

@ -1,4 +1,4 @@
/* Socket
/* TCPSocket
* Copyright (c) 2015 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -18,78 +18,97 @@
#define TCPSOCKET_H
#include "Socket.h"
#include "NetworkInterface.h"
#include "NetworkStack.h"
#include "Semaphore.h"
/**
TCP socket connection
*/
/** TCP socket connection
*/
class TCPSocket : public Socket {
public:
/** TCP socket lifetime
*/
TCPSocket(NetworkInterface *iface);
virtual ~TCPSocket();
/** Connects this TCP socket to the server
\param host The host to connect to. It can either be an IP Address
or a hostname that will be resolved with DNS
\param port The host's port to connect to
\return 0 on success, negative on failure
*/
/** Create an uninitialized socket
*
* Must call open to initialize the socket on a network stack.
*/
TCPSocket();
/** Create a socket on a network stack
*
* Creates and opens a socket on the specified network stack.
*
* @param iface Network stack as target for socket
*/
TCPSocket(NetworkStack *iface);
/** Opens a socket
*
* Creates a network socket on the specified network stack.
* Not needed if stack is passed to the socket's constructor.
*
* @param iface Network stack as target for socket
* @return 0 on success, negative error code on failure
*/
virtual int open(NetworkStack *iface);
/** Connects TCP socket to a remote host
*
* Initiates a connection to a remote server specified by either
* a domain name or an IP address and a port.
*
* @param host Hostname of the remote host
* @param port Port of the remote host
* @return 0 on success, negative error code on failure
*/
int connect(const char *host, uint16_t port);
/** Connects this TCP socket to the server
\param address SocketAddress to connect to
\return 0 on success, negative on failure
*/
/** Connects TCP socket to a remote host
*
* Initiates a connection to a remote server specified by the
* indicated address.
*
* @param address The SocketAddress of the remote host
* @return 0 on success, negative error code on failure
*/
int connect(const SocketAddress &address);
/** Check if the socket is connected
\return true if connected, false otherwise
*/
bool is_connected();
/** Send data to the remote host
\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
*/
/** Send data over a TCP socket
*
* The socket must be connected to a remote host. Returns the number of
* bytes sent from the buffer.
*
* By default, send blocks until data is sent. If socket is set to
* non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned
* immediately.
*
* @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
*/
int send(const void *data, unsigned size);
/** Receive data from the remote host
\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
*/
/** Receive data over a TCP socket
*
* The socket must be connected to a remote host. Returns the number of
* bytes received into the buffer.
*
* By default, recv blocks until data is sent. If socket is set to
* non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned
* immediately.
*
* @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
*/
int recv(void *data, unsigned size);
/** Register a callback on when send is ready
\param callback Function to call when send will succeed, may be called in
interrupt context.
*/
void attach_send(FunctionPointer callback);
template <typename T, typename M>
void attach_send(T *tptr, M mptr) {
attach_send(FunctionPointer(tptr, mptr));
}
/** Register a callback on when recv is ready
\param callback Function to call when recv will succeed, may be called in
interrupt context.
*/
void attach_recv(FunctionPointer callback);
template <typename T, typename M>
void attach_recv(T *tptr, M mptr) {
attach_recv(FunctionPointer(tptr, mptr));
}
private:
protected:
virtual void socket_event(void);
rtos::Mutex _read_lock;
rtos::Semaphore _read_sem;
rtos::Mutex _write_lock;
rtos::Semaphore _write_sem;
friend class TCPServer;
FunctionPointer _send_cb;
FunctionPointer _recv_cb;
};
#endif

View File

@ -17,84 +17,124 @@
#include "UDPSocket.h"
#include "Timer.h"
UDPSocket::UDPSocket(NetworkInterface *iface)
: Socket(iface, NSAPI_UDP)
UDPSocket::UDPSocket(): _read_sem(0), _write_sem(0)
{
}
int UDPSocket::bind(uint16_t port)
UDPSocket::UDPSocket(NetworkStack *iface): _read_sem(0), _write_sem(0)
{
if (!_socket) {
return NSAPI_ERROR_NO_SOCKET;
open(iface);
}
int UDPSocket::open(NetworkStack *iface)
{
return Socket::open(iface, NSAPI_UDP);
}
int UDPSocket::sendto(const char *host, uint16_t port, const void *data, unsigned size)
{
SocketAddress addr(_iface, host, port);
if (!addr) {
return NSAPI_ERROR_DNS_FAILURE;
}
return _iface->socket_bind(_socket, port);
// sendto is thread safe
int ret = sendto(addr, data, size);
return ret;
}
int UDPSocket::sendto(const SocketAddress &address, const void *data, unsigned size)
{
mbed::Timer timer;
timer.start();
if (osOK != _write_lock.lock(_timeout)) {
return NSAPI_ERROR_WOULD_BLOCK;
}
_lock.lock();
int ret;
while (true) {
if (!_socket) {
return NSAPI_ERROR_NO_SOCKET;
ret = NSAPI_ERROR_NO_SOCKET;
break;
}
int sent = _iface->socket_sendto(_socket, address, data, size);
if (sent != NSAPI_ERROR_WOULD_BLOCK || !_blocking ||
(_timeout && timer.read_ms() > _timeout)) {
return sent;
if ((0 == _timeout) || (NSAPI_ERROR_WOULD_BLOCK != sent)) {
ret = sent;
break;
} else {
int32_t count;
// Release lock before blocking so other threads
// accessing this object aren't blocked
_lock.unlock();
count = _write_sem.wait(_timeout);
_lock.lock();
if (count < 1) {
// Semaphore wait timed out so break out and return
ret = NSAPI_ERROR_WOULD_BLOCK;
break;
}
}
}
_lock.unlock();
_write_lock.unlock();
return ret;
}
int UDPSocket::recvfrom(SocketAddress *address, void *buffer, unsigned size)
{
mbed::Timer timer;
timer.start();
if (osOK != _read_lock.lock(_timeout)) {
return NSAPI_ERROR_WOULD_BLOCK;
}
_lock.lock();
int ret;
while (true) {
if (!_socket) {
return NSAPI_ERROR_NO_SOCKET;
ret = NSAPI_ERROR_NO_SOCKET;
break;
}
int recv = _iface->socket_recvfrom(_socket, address, buffer, size);
if (recv != NSAPI_ERROR_WOULD_BLOCK || !_blocking ||
(_timeout && timer.read_ms() > _timeout)) {
return recv;
if ((0 == _timeout) || (NSAPI_ERROR_WOULD_BLOCK != recv)) {
ret = recv;
break;
} else {
int32_t count;
// Release lock before blocking so other threads
// accessing this object aren't blocked
_lock.unlock();
count = _read_sem.wait(_timeout);
_lock.lock();
if (count < 1) {
// Semaphore wait timed out so break out and return
ret = NSAPI_ERROR_WOULD_BLOCK;
break;
}
}
}
_lock.unlock();
_read_lock.unlock();
return ret;
}
void UDPSocket::attach_send(FunctionPointer callback)
void UDPSocket::socket_event()
{
_send_cb = callback;
if (_socket && _send_cb) {
return _iface->socket_attach_send(_socket, Socket::thunk, &_send_cb);
} else if (_socket) {
return _iface->socket_attach_send(_socket, 0, 0);
int32_t count;
count = _write_sem.wait(0);
if (count <= 1) {
_write_sem.release();
}
}
void UDPSocket::attach_recv(FunctionPointer callback)
{
_recv_cb = callback;
if (_socket && _recv_cb) {
return _iface->socket_attach_recv(_socket, Socket::thunk, &_recv_cb);
} else if (_socket) {
return _iface->socket_attach_recv(_socket, 0, 0);
}
}
UDPSocket::~UDPSocket()
{
if (_socket && _send_cb) {
_iface->socket_attach_send(_socket, 0, 0);
count = _read_sem.wait(0);
if (count <= 1) {
_read_sem.release();
}
if (_socket && _recv_cb) {
_iface->socket_attach_recv(_socket, 0, 0);
}
Socket::socket_event();
}

View File

@ -1,4 +1,4 @@
/* Socket
/* UDPSocket
* Copyright (c) 2015 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -18,67 +18,95 @@
#define UDPSOCKET_H
#include "Socket.h"
#include "NetworkInterface.h"
#include "NetworkStack.h"
#include "Semaphore.h"
/**
UDP Socket
*/
/** UDP socket
*/
class UDPSocket : public Socket {
public:
/** UDPSocket lifetime
*/
UDPSocket(NetworkInterface *iface);
virtual ~UDPSocket();
/** Bind a UDP Server Socket to a specific port
\param port The port to listen for incoming connections on
\return 0 on success, negative on failure.
*/
int bind(uint16_t port);
/** Create an uninitialized socket
*
* Must call open to initialize the socket on a network stack.
*/
UDPSocket();
/** Send a packet to a remote endpoint
\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
*/
/** Create a socket on a network stack
*
* Creates and opens a socket on the specified network stack.
*
* @param iface Network stack as target for socket
*/
UDPSocket(NetworkStack *iface);
/** Opens a socket
*
* Creates a network socket on the specified network stack.
* Not needed if stack is passed to the socket's constructor.
*
* @param iface Network stack as target for socket
* @return 0 on success, negative error code on failure
*/
virtual int open(NetworkStack *iface);
/** Send a packet over a UDP socket
*
* Sends data to the specified address specified by either a domain name
* or an IP address and port. Returns the number of bytes sent from the
* buffer.
*
* By default, sendto blocks until data is sent. If socket is set to
* non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned
* immediately.
*
* @param host Hostname of the remote host
* @param port Port 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
*/
int sendto(const char *host, uint16_t port, const void *data, unsigned size);
/** Send a packet over a UDP socket
*
* Sends data to the specified address. Returns the number of bytes
* sent from the buffer.
*
* By default, sendto blocks until data is sent. If socket is set to
* non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned
* immediately.
*
* @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
*/
int sendto(const SocketAddress &address, const void *data, unsigned size);
/** Receive a packet from a remote endpoint
\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
*/
int recvfrom(SocketAddress *address, void *buffer, unsigned size);
/** Register a callback on when send is ready
\param callback Function to call when send will succeed, may be called in
interrupt context.
*/
void attach_send(FunctionPointer callback);
template <typename T, typename M>
void attach_send(T *tptr, M mptr) {
attach_send(FunctionPointer(tptr, mptr));
}
/** Register a callback on when recv is ready
\param callback Function to call when recv will succeed, may be called in
interrupt context.
*/
void attach_recv(FunctionPointer callback);
template <typename T, typename M>
void attach_recv(T *tptr, M mptr) {
attach_recv(FunctionPointer(tptr, mptr));
}
private:
FunctionPointer _send_cb;
FunctionPointer _recv_cb;
/** 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.
*
* By default, recvfrom blocks until data is sent. If socket is set to
* non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned
* immediately.
*
* @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
*/
int recvfrom(SocketAddress *address, void *data, unsigned size);
protected:
virtual void socket_event(void);
rtos::Mutex _read_lock;
rtos::Semaphore _read_sem;
rtos::Mutex _write_lock;
rtos::Semaphore _write_sem;
};
#endif

View File

@ -1,4 +1,4 @@
/* Socket
/* WiFiInterface
* Copyright (c) 2015 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -17,10 +17,15 @@
#ifndef WIFI_INTERFACE_H
#define WIFI_INTERFACE_H
#include "NetworkInterface.h"
#include "NetworkStack.h"
/** Enum for WiFi encryption types
*/
/** Enum of WiFi encryption types
*
* The security type specifies a particular security to use when
* connected to a WiFi network
*
* @enum nsapi_protocol_t
*/
enum nsapi_security_t {
NSAPI_SECURITY_NONE = 0, /*!< open access point */
NSAPI_SECURITY_WEP, /*!< phrase conforms to WEP */
@ -29,23 +34,35 @@ enum nsapi_security_t {
};
/** WiFiInterface class
*
* Common interface that is shared between WiFi devices
*/
class WiFiInterface : public NetworkInterface
class WiFiInterface
{
public:
/** Start the interface
/param ssid Name of the network to connect to
/param pass Security passphrase to connect to the network
/param security Type of encryption for connection
/return 0 on success, negative on failure
*/
*
* Attempts to connect to a WiFi network. If passphrase is invalid,
* NSAPI_ERROR_AUTH_ERROR is returned.
*
* @param ssid Name of the network to connect to
* @param pass Security passphrase to connect to the network
* @param security Type of encryption for connection
* @return 0 on success, negative error code on failure
*/
virtual int connect(const char *ssid, const char *pass, nsapi_security_t security = NSAPI_SECURITY_NONE) = 0;
/** Stop the interface
/return 0 on success, negative on failure
*/
*
* @return 0 on success, negative error code on failure
*/
virtual int disconnect() = 0;
/** Get the local MAC address
*
* @return Null-terminated representation of the local MAC address
*/
virtual const char *get_mac_address() = 0;
};
#endif

View File

@ -1 +1 @@
https://github.com/ARMmbed/atmel-rf-driver-mirror.git/#f4c48e5e98f6
https://github.com/ARMmbed/atmel-rf-driver-mirror/#473759f1a37c1863cb2adfcb2a87ac3f1da144ed

View File

@ -1 +1 @@
https://github.com/ARMmbed/mbed-client-classic.git/#0cf03c143a2a
https://github.com/ARMmbed/mbed-client-classic/#3120f1a11ca18fc0fedcccfb5042e62d4faeda0a

View File

@ -1 +1 @@
https://github.com/ARMmbed/mbed-mesh-api-mirror.git/#f7a198bb1e66
https://github.com/ARMmbed/mbed-mesh-api-mirror/#7312d7621c5c6b89f7c0476a2b103a9f1ea71c3c

View File

@ -1 +1 @@
https://github.com/ARMmbed/nanostack-hal-mbed-cmsis-rtos.git/#ab64e255deb9
https://github.com/ARMmbed/nanostack-hal-mbed-cmsis-rtos/#1d27e2613bf7a454f553e2363c115a1635fa9002

View File

@ -0,0 +1,899 @@
/* Nanostack implementation of NetworkSocketAPI
* Copyright (c) 2016 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 "mbed.h"
#include "rtos.h"
#include "NanostackInterface.h"
#include "ns_address.h"
#include "nsdynmemLIB.h"
#include "eventOS_scheduler.h"
#include "mbed-mesh-api/MeshInterfaceFactory.h"
#include "socket_api.h"
#include "driverRFPhy.h"
#include "net_interface.h"
#include "ip6string.h"
// Uncomment to enable trace
//#define HAVE_DEBUG
#include "ns_trace.h"
#define TRACE_GROUP "nsif"
#define NS_INTERFACE_SOCKETS_MAX 16 //same as NanoStack SOCKET_MAX
#define NANOSTACK_SOCKET_UDP 17 // same as nanostack SOCKET_UDP
#define NANOSTACK_SOCKET_TCP 6 // same as nanostack SOCKET_TCP
#define MALLOC ns_dyn_mem_alloc
#define FREE ns_dyn_mem_free
#define nanostack_lock() eventOS_scheduler_mutex_wait()
#define nanostack_unlock() eventOS_scheduler_mutex_release()
#define nanostack_assert_locked() //MBED_ASSERT(eventOS_scheduler_mutex_is_owner())
enum socket_mode_t {
SOCKET_MODE_UNOPENED, // No socket ID
SOCKET_MODE_OPENED, // Socket ID but no assigned use yet
SOCKET_MODE_CONNECTING, // Socket is connecting but not open yet
SOCKET_MODE_DATAGRAM, // Socket is bound to a port and listening for datagrams
SOCKET_MODE_STREAM, // Socket has an open stream
SOCKET_MODE_CLOSED, // Socket is closed and resources are freed
};
class NanostackBuffer {
public:
NanostackBuffer *next; /*<! next buffer */
ns_address_t ns_address; /*<! address where data is received */
uint16_t length; /*<! data length in this buffer */
uint8_t payload[1]; /*<! Trailing buffer data */
};
class NanostackSocket {
public:
static void socket_callback(void *cb);
static void* operator new(std::size_t sz);
static void operator delete(void* ptr);
NanostackSocket(int8_t protocol);
~NanostackSocket(void);
bool open(void);
void close(void);
bool closed(void) {return SOCKET_MODE_CLOSED == mode;}
bool is_bound(void);
void set_bound(void);
bool is_connecting(void);
void set_connecting(ns_address_t *addr);
void set_connected(void);
// Socket events from nanostack
void event_data(socket_callback_t *sock_cb);
void event_bind_done(socket_callback_t *sock_cb);
void event_connnect_closed(socket_callback_t *sock_cb);
void event_tx_done(socket_callback_t *sock_cb);
// Run callback to signal the next layer of the NSAPI
void signal_event(void);
// Add or remove a socket to the listening socket
void accept_list_add(NanostackSocket *socket);
NanostackSocket * accept_list_remove(void);
bool data_available(void);
size_t data_copy_and_free(void *dest, size_t len, SocketAddress *address, bool stream);
void data_free_all(void);
void data_attach(NanostackBuffer *data_buf);
void (*callback)(void *);
void *callback_data;
int8_t socket_id; /*!< allocated socket ID */
int8_t proto; /*!< UDP or TCP */
bool addr_valid;
ns_address_t ns_address;
private:
NanostackBuffer *rxBufChain; /*!< Receive buffers */
socket_mode_t mode;
};
static Semaphore connect_semaphore(0);
static NanostackSocket * socket_tbl[NS_INTERFACE_SOCKETS_MAX] = {0};
static int map_mesh_error(mesh_error_t err)
{
switch (err) {
case MESH_ERROR_NONE: return 0;
case MESH_ERROR_MEMORY: return NSAPI_ERROR_NO_MEMORY;
case MESH_ERROR_PARAM: return NSAPI_ERROR_UNSUPPORTED;
case MESH_ERROR_STATE: return NSAPI_ERROR_DEVICE_ERROR;
default: return NSAPI_ERROR_DEVICE_ERROR;
}
}
static void convert_mbed_addr_to_ns(ns_address_t *ns_addr,
const SocketAddress *s_addr)
{
ns_addr->type = ADDRESS_IPV6;
ns_addr->identifier = s_addr->get_port();
const char *str = s_addr->get_ip_address();
stoip6(str, strlen(str), ns_addr->address);
}
static void convert_ns_addr_to_mbed(SocketAddress *s_addr, const ns_address_t *ns_addr)
{
char str[40];
ip6tos(ns_addr->address, str);
s_addr->set_port(ns_addr->identifier);
s_addr->set_ip_address(str);
}
void* NanostackSocket::operator new(std::size_t sz) {
return MALLOC(sz);
}
void NanostackSocket::operator delete(void* ptr) {
FREE(ptr);
}
NanostackSocket::NanostackSocket(int8_t protocol)
{
nanostack_assert_locked();
callback = NULL;
callback_data = NULL;
socket_id = -1;
rxBufChain = NULL;
proto = protocol;
addr_valid = false;
memset(&ns_address, 0, sizeof(ns_address));
mode = SOCKET_MODE_UNOPENED;
}
NanostackSocket::~NanostackSocket()
{
nanostack_assert_locked();
if (mode != SOCKET_MODE_CLOSED) {
close();
}
if (socket_id >= 0) {
int ret = socket_free(socket_id);
MBED_ASSERT(0 == ret);
MBED_ASSERT(socket_tbl[socket_id] == this);
socket_id = -1;
data_free_all();
}
}
bool NanostackSocket::open(void)
{
nanostack_assert_locked();
MBED_ASSERT(SOCKET_MODE_UNOPENED == mode);
int temp_socket = socket_open(proto, 0, socket_callback);
if (temp_socket < 0) {
tr_error("NanostackSocket::open() failed");
return false;
}
if (temp_socket >= NS_INTERFACE_SOCKETS_MAX) {
MBED_ASSERT(false);
return false;
}
if (socket_tbl[temp_socket] != NULL) {
MBED_ASSERT(false);
return false;
}
socket_id = temp_socket;
socket_tbl[socket_id] = this;
mode = SOCKET_MODE_OPENED;
return true;
}
void NanostackSocket::close()
{
nanostack_assert_locked();
MBED_ASSERT(mode != SOCKET_MODE_CLOSED);
if (socket_id >= 0) {
int ret = socket_close(socket_id, (addr_valid ? &ns_address : NULL));
MBED_ASSERT(0 == ret);
} else {
MBED_ASSERT(SOCKET_MODE_UNOPENED == mode);
}
data_free_all();
mode = SOCKET_MODE_CLOSED;
signal_event();
}
bool NanostackSocket::is_bound()
{
return SOCKET_MODE_DATAGRAM == mode;
}
void NanostackSocket::set_bound()
{
nanostack_assert_locked();
MBED_ASSERT(SOCKET_MODE_OPENED == mode);
if (SOCKET_UDP == proto) {
mode = SOCKET_MODE_DATAGRAM;
}
}
bool NanostackSocket::is_connecting()
{
return SOCKET_MODE_CONNECTING == mode;
}
void NanostackSocket::set_connecting(ns_address_t *addr)
{
nanostack_assert_locked();
MBED_ASSERT(SOCKET_MODE_OPENED == mode);
memcpy(&ns_address, addr, sizeof(ns_address_t));
mode = SOCKET_MODE_CONNECTING;
}
void NanostackSocket::set_connected()
{
nanostack_assert_locked();
MBED_ASSERT(SOCKET_MODE_CONNECTING == mode);
mode = SOCKET_MODE_STREAM;
}
void NanostackSocket::signal_event()
{
nanostack_assert_locked();
if (callback != NULL) {
callback(callback_data);
}
}
void NanostackSocket::socket_callback(void *cb) {
nanostack_assert_locked();
socket_callback_t *sock_cb = (socket_callback_t *) cb;
NanostackSocket *socket = socket_tbl[sock_cb->socket_id];
MBED_ASSERT(socket != NULL);
tr_debug("socket_callback() sock=%d, event=%d, interface=%d, data len=%d",
sock_cb->socket_id, sock_cb->event_type, sock_cb->interface_id, sock_cb->d_len);
switch (sock_cb->event_type) {
case SOCKET_DATA:
tr_debug("SOCKET_DATA, sock=%d, bytes=%d", sock_cb->socket_id, sock_cb->d_len);
socket->event_data(sock_cb);
break;
case SOCKET_BIND_DONE:
tr_debug("SOCKET_BIND_DONE");
socket->event_bind_done(sock_cb);
break;
case SOCKET_BIND_FAIL: // Not used in NS
tr_debug("SOCKET_BIND_FAIL");
break;
case SOCKET_BIND_AUTH_FAIL: // Not used in NS
tr_debug("SOCKET_BIND_AUTH_FAIL");
break;
case SOCKET_SERVER_CONNECT_TO_CLIENT: // Not used in NS
tr_debug("SOCKET_SERVER_CONNECT_TO_CLIENT");
break;
case SOCKET_TX_FAIL:
tr_debug("SOCKET_TX_FAIL");
break;
case SOCKET_CONNECT_CLOSED:
tr_debug("SOCKET_CONNECT_CLOSED");
socket->event_connnect_closed(sock_cb);
break;
case SOCKET_CONNECT_FAIL_CLOSED: // Not used in NS
tr_debug("SOCKET_CONNECT_FAIL_CLOSED");
break;
case SOCKET_NO_ROUTE:
tr_debug("SOCKET_NO_ROUTE");
break;
case SOCKET_TX_DONE:
tr_debug("SOCKET_TX_DONE, %d bytes sent", sock_cb->d_len);
socket->event_tx_done(sock_cb);
break;
default:
// SOCKET_NO_RAM, error case for SOCKET_TX_DONE
break;
}
}
bool NanostackSocket::data_available()
{
nanostack_assert_locked();
MBED_ASSERT((SOCKET_MODE_DATAGRAM == mode) ||
(SOCKET_MODE_CONNECTING == mode) ||
(SOCKET_MODE_STREAM == mode));
return (NULL == rxBufChain) ? false : true;
}
size_t NanostackSocket::data_copy_and_free(void *dest, size_t len,
SocketAddress *address, bool stream)
{
nanostack_assert_locked();
MBED_ASSERT((SOCKET_MODE_DATAGRAM == mode) ||
(mode == SOCKET_MODE_STREAM));
NanostackBuffer *data_buf = rxBufChain;
if (NULL == data_buf) {
// No data
return 0;
}
if (address) {
convert_ns_addr_to_mbed(address, &data_buf->ns_address);
}
size_t copy_size = (len > data_buf->length) ? data_buf->length : len;
memcpy(dest, data_buf->payload, copy_size);
if (stream && (copy_size < data_buf->length)) {
// Update the size in the buffer
size_t new_buf_size = data_buf->length - copy_size;
memmove(data_buf->payload, data_buf->payload + copy_size, new_buf_size);
data_buf->length = new_buf_size;
} else {
// Entire packet used so free it
rxBufChain = data_buf->next;
FREE(data_buf);
}
return copy_size;
}
void NanostackSocket::data_free_all(void)
{
nanostack_assert_locked();
// No mode requirement
NanostackBuffer *buffer = rxBufChain;
while (buffer != NULL) {
NanostackBuffer *next_buffer = buffer->next;
FREE(buffer);
buffer = next_buffer;
}
}
void NanostackSocket::data_attach(NanostackBuffer *data_buf)
{
nanostack_assert_locked();
MBED_ASSERT((SOCKET_MODE_DATAGRAM == mode) ||
(SOCKET_MODE_STREAM == mode));
// Add to linked list
tr_debug("data_attach socket=%p", this);
if (NULL == rxBufChain) {
rxBufChain = data_buf;
} else {
NanostackBuffer *buf_tmp = rxBufChain;
while (NULL != buf_tmp->next) {
buf_tmp = buf_tmp->next;
}
buf_tmp->next = data_buf;
}
signal_event();
}
void NanostackSocket::event_data(socket_callback_t *sock_cb)
{
nanostack_assert_locked();
MBED_ASSERT((SOCKET_MODE_DATAGRAM == mode) ||
(SOCKET_MODE_STREAM == mode));
// Allocate buffer
NanostackBuffer *recv_buff = (NanostackBuffer *) MALLOC(
sizeof(NanostackBuffer) + sock_cb->d_len);
if (NULL == recv_buff) {
tr_error("alloc failed!");
return;
}
recv_buff->next = NULL;
// Write data to buffer
int16_t length = socket_read(sock_cb->socket_id,
&recv_buff->ns_address, recv_buff->payload,
sock_cb->d_len);
if (length < 0) {
tr_error("socket_read failed!");
FREE(recv_buff);
return;
}
recv_buff->length = length;
data_attach(recv_buff);
}
void NanostackSocket::event_tx_done(socket_callback_t *sock_cb)
{
nanostack_assert_locked();
MBED_ASSERT((SOCKET_MODE_STREAM == mode) ||
(SOCKET_MODE_DATAGRAM == mode));
signal_event();
}
void NanostackSocket::event_bind_done(socket_callback_t *sock_cb)
{
nanostack_assert_locked();
MBED_ASSERT(SOCKET_MODE_CONNECTING == mode);
set_connected();
signal_event();
}
void NanostackSocket::event_connnect_closed(socket_callback_t *sock_cb)
{
nanostack_assert_locked();
// Only TCP sockets can be closed by the remote end
MBED_ASSERT((SOCKET_MODE_STREAM == mode) ||
(SOCKET_MODE_CONNECTING == mode));
close();
}
void NanostackInterface::mesh_network_handler(mesh_connection_status_t status)
{
nanostack_lock();
if (status == MESH_CONNECTED) {
connect_semaphore.release();
}
nanostack_unlock();
}
int NanostackInterface::register_rf()
{
nanostack_lock();
rf_device_id = rf_device_register();
if (rf_device_id < 0) {
nanostack_unlock();
return -1;
}
// Read mac address after registering the device.
rf_read_mac_address(eui64);
sprintf(mac_addr_str, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", eui64[0], eui64[1], eui64[2], eui64[3], eui64[4], eui64[5], eui64[6], eui64[7]);
nanostack_unlock();
return 0;
}
int NanostackInterface::actual_connect()
{
nanostack_lock();
mesh_error_t status = get_mesh_api()->connect();
if (status != MESH_ERROR_NONE) {
nanostack_unlock();
return map_mesh_error(status);
}
// Release mutex before blocking
nanostack_unlock();
int32_t count = connect_semaphore.wait(30000);
if (count <= 0) {
return NSAPI_ERROR_DHCP_FAILURE; // sort of...
}
return 0;
}
int NanostackInterface::disconnect()
{
nanostack_lock();
mesh_error_t status = get_mesh_api()->disconnect();
nanostack_unlock();
return map_mesh_error(status);
}
int ThreadInterface::connect()
{
nanostack_lock();
mesh_api = MeshInterfaceFactory::createInterface(MESH_TYPE_THREAD);
if (!mesh_api) {
nanostack_unlock();
return NSAPI_ERROR_NO_MEMORY;
}
if (register_rf() < 0) {
nanostack_unlock();
return NSAPI_ERROR_DEVICE_ERROR;
}
mesh_error_t status = ((MeshThread *)mesh_api)->init(rf_device_id, AbstractMesh::mesh_network_handler_t(static_cast<NanostackInterface *>(this), &ThreadInterface::mesh_network_handler), eui64, NULL);
if (status != MESH_ERROR_NONE) {
nanostack_unlock();
return map_mesh_error(status);
}
int ret = this->actual_connect();
nanostack_unlock();
return ret;
}
int LoWPANNDInterface::connect()
{
nanostack_lock();
mesh_api = MeshInterfaceFactory::createInterface(MESH_TYPE_6LOWPAN_ND);
if (!mesh_api) {
nanostack_unlock();
return NSAPI_ERROR_NO_MEMORY;
}
if (register_rf() < 0) {
nanostack_unlock();
return NSAPI_ERROR_DEVICE_ERROR;
}
mesh_error_t status = ((Mesh6LoWPAN_ND *)mesh_api)->init(rf_device_id, AbstractMesh::mesh_network_handler_t(static_cast<NanostackInterface *>(this), &LoWPANNDInterface::mesh_network_handler));
if (status != MESH_ERROR_NONE) {
nanostack_unlock();
return map_mesh_error(status);
}
int ret = this->actual_connect();
nanostack_unlock();
return ret;
}
int NanostackInterface::socket_open(void **handle, nsapi_protocol_t protocol)
{
// Validate parameters
if (NULL == handle) {
MBED_ASSERT(false);
return NSAPI_ERROR_NO_SOCKET;
}
int8_t ns_proto;
if (NSAPI_UDP == protocol) {
ns_proto = SOCKET_UDP;
} else if (NSAPI_TCP == protocol) {
ns_proto = SOCKET_TCP;
} else {
MBED_ASSERT(false);
return NSAPI_ERROR_UNSUPPORTED;
}
*handle = (void*)NULL;
nanostack_lock();
NanostackSocket * socket = new NanostackSocket(ns_proto);
if (NULL == socket) {
nanostack_unlock();
tr_debug("socket_open() ret=%i", NSAPI_ERROR_NO_MEMORY);
return NSAPI_ERROR_NO_MEMORY;
}
if (!socket->open()) {
delete socket;
nanostack_unlock();
tr_debug("socket_open() ret=%i", NSAPI_ERROR_DEVICE_ERROR);
return NSAPI_ERROR_DEVICE_ERROR;
}
*handle = (void*)socket;
nanostack_unlock();
tr_debug("socket_open() socket=%p, sock_id=%d, ret=0", socket, socket->socket_id);
return 0;
}
int NanostackInterface::socket_close(void *handle)
{
// Validate parameters
NanostackSocket * socket = static_cast<NanostackSocket *>(handle);
if (NULL == handle) {
MBED_ASSERT(false);
return NSAPI_ERROR_NO_SOCKET;
}
tr_debug("socket_close(socket=%p) sock_id=%d", socket, socket->socket_id);
nanostack_lock();
delete socket;
nanostack_unlock();
return 0;
}
int NanostackInterface::socket_sendto(void *handle, const SocketAddress &address, const void *data, unsigned int size)
{
// Validate parameters
NanostackSocket * socket = static_cast<NanostackSocket *>(handle);
if (NULL == handle) {
MBED_ASSERT(false);
return NSAPI_ERROR_NO_SOCKET;
}
nanostack_lock();
int ret;
if (socket->closed()) {
ret = NSAPI_ERROR_NO_CONNECTION;
} else if (NANOSTACK_SOCKET_TCP == socket->proto) {
tr_error("socket_sendto() not supported with SOCKET_STREAM!");
ret = NSAPI_ERROR_UNSUPPORTED;
} else {
ns_address_t ns_address;
convert_mbed_addr_to_ns(&ns_address, &address);
if (!socket->is_bound()) {
socket->set_bound();
}
int8_t send_to_status = ::socket_sendto(socket->socket_id, &ns_address,
(uint8_t *)data, size);
/*
* \return 0 on success.
* \return -1 invalid socket id.
* \return -2 Socket memory allocation fail.
* \return -3 TCP state not established.
* \return -4 Socket tx process busy.
* \return -5 TLS authentication not ready.
* \return -6 Packet too short.
* */
if (-4 == send_to_status) {
ret = NSAPI_ERROR_WOULD_BLOCK;
} else if (0 != send_to_status) {
tr_error("socket_sendto: error=%d", send_to_status);
ret = NSAPI_ERROR_DEVICE_ERROR;
} else {
ret = size;
}
}
nanostack_unlock();
tr_debug("socket_sendto(socket=%p) sock_id=%d, ret=%i", socket, socket->socket_id, ret);
return ret;
}
int NanostackInterface::socket_recvfrom(void *handle, SocketAddress *address, void *buffer, unsigned size)
{
// Validate parameters
NanostackSocket * socket = static_cast<NanostackSocket *>(handle);
if (NULL == handle) {
MBED_ASSERT(false);
return NSAPI_ERROR_NO_SOCKET;
}
if (NULL == buffer) {
MBED_ASSERT(false);
return NSAPI_ERROR_PARAMETER;
}
if (0 == size) {
MBED_ASSERT(false);
return NSAPI_ERROR_PARAMETER;
}
nanostack_lock();
int ret;
if (socket->closed()) {
ret = NSAPI_ERROR_NO_CONNECTION;
} else if (NANOSTACK_SOCKET_TCP == socket->proto) {
tr_error("recv_from() not supported with SOCKET_STREAM!");
ret = NSAPI_ERROR_UNSUPPORTED;
} else if (!socket->data_available()) {
ret = NSAPI_ERROR_WOULD_BLOCK;
} else {
ret = socket->data_copy_and_free(buffer, size, address, false);
}
nanostack_unlock();
tr_debug("socket_recvfrom(socket=%p) sock_id=%d, ret=%i", socket, socket->socket_id, ret);
return ret;
}
int NanostackInterface::socket_bind(void *handle, const SocketAddress &address)
{
// Validate parameters
NanostackSocket * socket = static_cast<NanostackSocket *>(handle);
if (NULL == handle) {
MBED_ASSERT(false);
return NSAPI_ERROR_NO_SOCKET;
}
nanostack_lock();
ns_address_t ns_address;
ns_address.type = ADDRESS_IPV6;
memset(ns_address.address, 0, sizeof ns_address.address);
ns_address.identifier = address.get_port();
int ret = NSAPI_ERROR_DEVICE_ERROR;
if (0 == ::socket_bind(socket->socket_id, &ns_address)) {
socket->set_bound();
ret = 0;
}
nanostack_unlock();
tr_debug("socket_bind(socket=%p) sock_id=%d, ret=%i", socket, socket->socket_id, ret);
return ret;
}
const char *NanostackInterface::get_ip_address()
{
nanostack_lock();
const char *ret = NULL;
if (mesh_api && mesh_api->getOwnIpAddress(ip_addr_str, sizeof ip_addr_str)) {
ret = ip_addr_str;
}
nanostack_unlock();
return ret;
}
const char *NanostackInterface::get_mac_address()
{
return mac_addr_str;
}
int NanostackInterface::setsockopt(void *handle, int level, int optname, const void *optval, unsigned optlen)
{
return NSAPI_ERROR_UNSUPPORTED;
}
int NanostackInterface::getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen)
{
return NSAPI_ERROR_UNSUPPORTED;
}
int NanostackInterface::socket_listen(void *handle, int backlog)
{
return NSAPI_ERROR_UNSUPPORTED;
}
int NanostackInterface::socket_connect(void *handle, const SocketAddress &addr)
{
// Validate parameters
NanostackSocket * socket = static_cast<NanostackSocket *>(handle);
if (NULL == handle) {
MBED_ASSERT(false);
return NSAPI_ERROR_NO_SOCKET;
}
nanostack_lock();
int ret;
ns_address_t ns_addr;
int random_port = socket->is_bound() ? 0 : 1;
convert_mbed_addr_to_ns(&ns_addr, &addr);
if (0 == ::socket_connect(socket->socket_id, &ns_addr, random_port)) {
socket->set_connecting(&ns_addr);
ret = 0;
} else {
ret = NSAPI_ERROR_DEVICE_ERROR;
}
nanostack_unlock();
tr_debug("socket_connect(socket=%p) sock_id=%d, ret=%i", socket, socket->socket_id, ret);
return ret;
}
int NanostackInterface::socket_accept(void **handle, void *server)
{
return NSAPI_ERROR_UNSUPPORTED;
}
int NanostackInterface::socket_send(void *handle, const void *p, unsigned size)
{
// Validate parameters
NanostackSocket * socket = static_cast<NanostackSocket *>(handle);
if (NULL == handle) {
MBED_ASSERT(false);
return NSAPI_ERROR_NO_SOCKET;
}
nanostack_lock();
int ret;
if (socket->closed()) {
ret = NSAPI_ERROR_NO_CONNECTION;
} else if (socket->is_connecting()) {
ret = NSAPI_ERROR_WOULD_BLOCK;
} else {
ret = ::socket_sendto(socket->socket_id, &socket->ns_address, (uint8_t*)p, size);
/*
* \return 0 on success.
* \return -1 invalid socket id.
* \return -2 Socket memory allocation fail.
* \return -3 TCP state not established.
* \return -4 Socket tx process busy.
* \return -5 TLS authentication not ready.
* \return -6 Packet too short.
* */
if (-4 == ret) {
ret = NSAPI_ERROR_WOULD_BLOCK;
} else if (ret != 0) {
tr_warning("socket_sendto ret %i, socket_id %i", ret, socket->socket_id);
ret = NSAPI_ERROR_DEVICE_ERROR;
} else {
ret = size;
}
}
nanostack_unlock();
tr_debug("socket_send(socket=%p) sock_id=%d, ret=%i", socket, socket->socket_id, ret);
return ret;
}
int NanostackInterface::socket_recv(void *handle, void *data, unsigned size)
{
// Validate parameters
NanostackSocket * socket = static_cast<NanostackSocket *>(handle);
if (NULL == handle) {
MBED_ASSERT(false);
return NSAPI_ERROR_NO_SOCKET;
}
nanostack_lock();
int ret;
if (socket->closed()) {
ret = NSAPI_ERROR_NO_CONNECTION;
} else if (socket->data_available()) {
ret = socket->data_copy_and_free(data, size, NULL, true);
} else {
ret = NSAPI_ERROR_WOULD_BLOCK;
}
nanostack_unlock();
tr_debug("socket_recv(socket=%p) sock_id=%d, ret=%i", socket, socket->socket_id, ret);
return ret;
}
void NanostackInterface::socket_attach(void *handle, void (*callback)(void *), void *id)
{
// Validate parameters
NanostackSocket * socket = static_cast<NanostackSocket *>(handle);
if (NULL == handle) {
MBED_ASSERT(false);
return;
}
nanostack_lock();
socket->callback = callback;
socket->callback_data = id;
nanostack_unlock();
tr_debug("socket_attach(socket=%p) sock_id=%d", socket, socket->socket_id);
}

View File

@ -0,0 +1,250 @@
/*
* Copyright (c) 2016 ARM Limited. All rights reserved.
*/
#ifndef NANOSTACK_INTERFACE_H_
#define NANOSTACK_INTERFACE_H_
#include "NetworkStack.h"
#include "MeshInterface.h"
#include "mbed-mesh-api/Mesh6LoWPAN_ND.h"
#include "mbed-mesh-api/MeshThread.h"
class NanostackInterface : public MeshInterface, public NetworkStack {
public:
int disconnect();
/** Get the internally stored IP address
/return IP address of the interface or null if not yet connected
*/
const char *get_ip_address();
/** Get the internally stored MAC address
/return MAC address of the interface
*/
const char *get_mac_address();
protected:
/** Opens a socket
*
* 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.
*
* @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 int socket_open(void **handle, nsapi_protocol_t proto);
/** Close the 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 int socket_close(void *handle);
/** Bind a specific address to a socket
*
* Binding a socket specifies the address and port on which to recieve
* data. If the IP address is zeroed, only the port is bound.
*
* @param handle Socket handle
* @param address Local address to bind
* @return 0 on success, negative error code on failure.
*/
virtual int socket_bind(void *handle, const SocketAddress &address);
/** 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
* @return 0 on success, negative error code on failure
*/
virtual int socket_listen(void *handle, int backlog);
/** 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 int socket_connect(void *handle, const SocketAddress &address);
/** 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 handle Destination for a handle to the newly created sockey
* @param server Socket handle to server to accept from
* @return 0 on success, negative error code on failure
*/
virtual int socket_accept(void **handle, void *server);
/** 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 call is non-blocking. If send would block,
* NSAPI_ERROR_WOULD_BLOCK is returned immediately.
*
* @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 int socket_send(void *handle, const void *data, unsigned 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 call is non-blocking. If recv would block,
* NSAPI_ERROR_WOULD_BLOCK is returned immediately.
*
* @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 int socket_recv(void *handle, void *data, unsigned size);
/** Send a packet over a UDP socket
*
* Sends data to the specified address. Returns the number of bytes
* sent from the buffer.
*
* This call is non-blocking. If sendto would block,
* NSAPI_ERROR_WOULD_BLOCK is returned immediately.
*
* @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 int socket_sendto(void *handle, const SocketAddress &address, const void *data, unsigned 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.
*
* This call is non-blocking. If recvfrom would block,
* NSAPI_ERROR_WOULD_BLOCK is returned immediately.
*
* @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 int socket_recvfrom(void *handle, SocketAddress *address, void *buffer, unsigned 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(void *handle, void (*callback)(void *), void *data);
/* 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 int setsockopt(void *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 int getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen);
protected:
int register_rf();
int actual_connect();
virtual AbstractMesh *get_mesh_api() const = 0;
void mesh_network_handler(mesh_connection_status_t status);
AbstractMesh *mesh_api;
int8_t rf_device_id;
uint8_t eui64[8];
char ip_addr_str[40];
char mac_addr_str[24];
};
class LoWPANNDInterface : public NanostackInterface {
public:
int connect();
protected:
Mesh6LoWPAN_ND *get_mesh_api() const { return static_cast<Mesh6LoWPAN_ND *>(mesh_api); }
private:
};
class ThreadInterface : public NanostackInterface {
public:
int connect();
protected:
MeshThread *get_mesh_api() const { return static_cast<MeshThread *>(mesh_api); }
private:
};
#endif /* NANOSTACK_INTERFACE_H_ */

View File

@ -1 +1 @@
https://github.com/ARMmbed/nanostack-libservice-mirror.git/#e3f7da74a143
https://github.com/ARMmbed/nanostack-libservice-mirror/#5490767fc3f8b9f204be20f88456f63a84810d06

View File

@ -1 +0,0 @@
https://github.com/ARMmbed/sal-iface-6lowpan-morpheus-private-mirror.git/#2b4852e22679

View File

@ -1 +1 @@
https://github.com/ARMmbed/sal-stack-nanostack-eventloop-mirror.git/#627b9769e352
https://github.com/ARMmbed/sal-stack-nanostack-eventloop-mirror/#dcbeaf4babf7d74d252e905fd15d1d63bbaaf9c2

View File

@ -1 +1 @@
https://github.com/ARMmbed/sal-stack-nanostack-private-mirror.git/#1374c77b03fb
https://github.com/ARMmbed/sal-stack-nanostack-private-mirror/#c83ddc4f776847d62ae3aa53d591157adc910b59