diff --git a/net/ESP8266Interface/ESP8266Interface.cpp b/net/ESP8266Interface/ESP8266Interface.cpp index f675d7618e..82b717b40e 100644 --- a/net/ESP8266Interface/ESP8266Interface.cpp +++ b/net/ESP8266Interface/ESP8266Interface.cpp @@ -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) { } diff --git a/net/ESP8266Interface/ESP8266Interface.h b/net/ESP8266Interface/ESP8266Interface.h index 88ce4dcf17..ce26e5ea82 100644 --- a/net/ESP8266Interface/ESP8266Interface.h +++ b/net/ESP8266Interface/ESP8266Interface.h @@ -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 - diff --git a/net/LWIPInterface/LWIPInterface.cpp b/net/LWIPInterface/LWIPInterface.cpp index 18df1b3328..9f13b5ccd5 100644 --- a/net/LWIPInterface/LWIPInterface.cpp +++ b/net/LWIPInterface/LWIPInterface.cpp @@ -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; +} diff --git a/net/LWIPInterface/LWIPInterface.h b/net/LWIPInterface/LWIPInterface.h index 055b7edda2..139ab45548 100644 --- a/net/LWIPInterface/LWIPInterface.h +++ b/net/LWIPInterface/LWIPInterface.h @@ -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 diff --git a/net/NetworkSocketAPI/CellularInterface.h b/net/NetworkSocketAPI/CellularInterface.h new file mode 100644 index 0000000000..0f9c28b840 --- /dev/null +++ b/net/NetworkSocketAPI/CellularInterface.h @@ -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 diff --git a/net/NetworkSocketAPI/DnsQuery/DnsQuery.cpp b/net/NetworkSocketAPI/DnsQuery/DnsQuery.cpp index e940e34694..2c209f529e 100644 --- a/net/NetworkSocketAPI/DnsQuery/DnsQuery.cpp +++ b/net/NetworkSocketAPI/DnsQuery/DnsQuery.cpp @@ -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); diff --git a/net/NetworkSocketAPI/DnsQuery/DnsQuery.h b/net/NetworkSocketAPI/DnsQuery/DnsQuery.h index 6e1e6265b7..3c5dfa7d70 100644 --- a/net/NetworkSocketAPI/DnsQuery/DnsQuery.h +++ b/net/NetworkSocketAPI/DnsQuery/DnsQuery.h @@ -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); diff --git a/net/NetworkSocketAPI/EthernetInterface.h b/net/NetworkSocketAPI/EthernetInterface.h index a48846f650..c0214c688b 100644 --- a/net/NetworkSocketAPI/EthernetInterface.h +++ b/net/NetworkSocketAPI/EthernetInterface.h @@ -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 diff --git a/net/NetworkSocketAPI/MeshInterface.h b/net/NetworkSocketAPI/MeshInterface.h index 884708a623..811b3f7352 100644 --- a/net/NetworkSocketAPI/MeshInterface.h +++ b/net/NetworkSocketAPI/MeshInterface.h @@ -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 diff --git a/net/NetworkSocketAPI/NetworkInterface.cpp b/net/NetworkSocketAPI/NetworkInterface.cpp deleted file mode 100644 index 455d3ddf3b..0000000000 --- a/net/NetworkSocketAPI/NetworkInterface.cpp +++ /dev/null @@ -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); -} diff --git a/net/NetworkSocketAPI/NetworkInterface.h b/net/NetworkSocketAPI/NetworkInterface.h deleted file mode 100644 index 654e3a5e55..0000000000 --- a/net/NetworkSocketAPI/NetworkInterface.h +++ /dev/null @@ -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 diff --git a/net/NetworkSocketAPI/NetworkStack.cpp b/net/NetworkSocketAPI/NetworkStack.cpp new file mode 100644 index 0000000000..1b70dd6551 --- /dev/null +++ b/net/NetworkSocketAPI/NetworkStack.cpp @@ -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; +} diff --git a/net/NetworkSocketAPI/NetworkStack.h b/net/NetworkSocketAPI/NetworkStack.h new file mode 100644 index 0000000000..30dea56b26 --- /dev/null +++ b/net/NetworkSocketAPI/NetworkStack.h @@ -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 diff --git a/net/NetworkSocketAPI/Socket.cpp b/net/NetworkSocketAPI/Socket.cpp index 7f90895ede..fd32a30cb8 100644 --- a/net/NetworkSocketAPI/Socket.cpp +++ b/net/NetworkSocketAPI/Socket.cpp @@ -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(); + } } diff --git a/net/NetworkSocketAPI/Socket.h b/net/NetworkSocketAPI/Socket.h index 6fb8d6127f..bee2d11a74 100644 --- a/net/NetworkSocketAPI/Socket.h +++ b/net/NetworkSocketAPI/Socket.h @@ -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 + 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 diff --git a/net/NetworkSocketAPI/SocketAddress.cpp b/net/NetworkSocketAPI/SocketAddress.cpp index f57fd8253f..1496aef21d 100644 --- a/net/NetworkSocketAPI/SocketAddress.cpp +++ b/net/NetworkSocketAPI/SocketAddress.cpp @@ -15,37 +15,210 @@ */ #include "SocketAddress.h" -#include "NetworkInterface.h" +#include "NetworkStack.h" #include #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; +} diff --git a/net/NetworkSocketAPI/SocketAddress.h b/net/NetworkSocketAPI/SocketAddress.h index e6fedc2496..64f2991e0b 100644 --- a/net/NetworkSocketAPI/SocketAddress.h +++ b/net/NetworkSocketAPI/SocketAddress.h @@ -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 -/** 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; }; diff --git a/net/NetworkSocketAPI/TCPServer.cpp b/net/NetworkSocketAPI/TCPServer.cpp index aa3623caa0..4d4c9f5390 100644 --- a/net/NetworkSocketAPI/TCPServer.cpp +++ b/net/NetworkSocketAPI/TCPServer.cpp @@ -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(); } diff --git a/net/NetworkSocketAPI/TCPServer.h b/net/NetworkSocketAPI/TCPServer.h index 697104c66f..9155f78e67 100644 --- a/net/NetworkSocketAPI/TCPServer.h +++ b/net/NetworkSocketAPI/TCPServer.h @@ -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 - 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 diff --git a/net/NetworkSocketAPI/TCPSocket.cpp b/net/NetworkSocketAPI/TCPSocket.cpp index 13bd8b6c0a..14c0b61736 100644 --- a/net/NetworkSocketAPI/TCPSocket.cpp +++ b/net/NetworkSocketAPI/TCPSocket.cpp @@ -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(); } diff --git a/net/NetworkSocketAPI/TCPSocket.h b/net/NetworkSocketAPI/TCPSocket.h index 36693c387f..5341174344 100644 --- a/net/NetworkSocketAPI/TCPSocket.h +++ b/net/NetworkSocketAPI/TCPSocket.h @@ -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 - 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 - 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 diff --git a/net/NetworkSocketAPI/UDPSocket.cpp b/net/NetworkSocketAPI/UDPSocket.cpp index 90ed74ad39..dbff20fbf0 100644 --- a/net/NetworkSocketAPI/UDPSocket.cpp +++ b/net/NetworkSocketAPI/UDPSocket.cpp @@ -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(); } diff --git a/net/NetworkSocketAPI/UDPSocket.h b/net/NetworkSocketAPI/UDPSocket.h index 9d1ab06112..d5152d6348 100644 --- a/net/NetworkSocketAPI/UDPSocket.h +++ b/net/NetworkSocketAPI/UDPSocket.h @@ -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 - 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 - 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 diff --git a/net/NetworkSocketAPI/WiFiInterface.h b/net/NetworkSocketAPI/WiFiInterface.h index 541a6a8fa8..09f5340d32 100644 --- a/net/NetworkSocketAPI/WiFiInterface.h +++ b/net/NetworkSocketAPI/WiFiInterface.h @@ -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 diff --git a/net/atmel-rf-driver.lib b/net/atmel-rf-driver.lib index 368d967a37..409b903ec1 100644 --- a/net/atmel-rf-driver.lib +++ b/net/atmel-rf-driver.lib @@ -1 +1 @@ -https://github.com/ARMmbed/atmel-rf-driver-mirror.git/#f4c48e5e98f6 +https://github.com/ARMmbed/atmel-rf-driver-mirror/#473759f1a37c1863cb2adfcb2a87ac3f1da144ed diff --git a/net/mbed-client-classic.lib b/net/mbed-client-classic.lib index d016f7d813..de43c820ee 100644 --- a/net/mbed-client-classic.lib +++ b/net/mbed-client-classic.lib @@ -1 +1 @@ -https://github.com/ARMmbed/mbed-client-classic.git/#0cf03c143a2a +https://github.com/ARMmbed/mbed-client-classic/#3120f1a11ca18fc0fedcccfb5042e62d4faeda0a diff --git a/net/mbed-mesh-api.lib b/net/mbed-mesh-api.lib index f45138d229..15ba5f511d 100644 --- a/net/mbed-mesh-api.lib +++ b/net/mbed-mesh-api.lib @@ -1 +1 @@ -https://github.com/ARMmbed/mbed-mesh-api-mirror.git/#f7a198bb1e66 +https://github.com/ARMmbed/mbed-mesh-api-mirror/#7312d7621c5c6b89f7c0476a2b103a9f1ea71c3c diff --git a/net/nanostack-hal-mbed-cmsis-rtos.lib b/net/nanostack-hal-mbed-cmsis-rtos.lib index e8134cd07c..4a54aad23e 100644 --- a/net/nanostack-hal-mbed-cmsis-rtos.lib +++ b/net/nanostack-hal-mbed-cmsis-rtos.lib @@ -1 +1 @@ -https://github.com/ARMmbed/nanostack-hal-mbed-cmsis-rtos.git/#ab64e255deb9 +https://github.com/ARMmbed/nanostack-hal-mbed-cmsis-rtos/#1d27e2613bf7a454f553e2363c115a1635fa9002 diff --git a/net/nanostack-interface/NanostackInterface.cpp b/net/nanostack-interface/NanostackInterface.cpp new file mode 100644 index 0000000000..4b40acc4dc --- /dev/null +++ b/net/nanostack-interface/NanostackInterface.cpp @@ -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; /*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(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(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(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(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(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(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(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(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(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(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); +} diff --git a/net/nanostack-interface/NanostackInterface.h b/net/nanostack-interface/NanostackInterface.h new file mode 100644 index 0000000000..7d747cf63d --- /dev/null +++ b/net/nanostack-interface/NanostackInterface.h @@ -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(mesh_api); } +private: + +}; + +class ThreadInterface : public NanostackInterface { +public: + int connect(); +protected: + MeshThread *get_mesh_api() const { return static_cast(mesh_api); } +private: + +}; + + +#endif /* NANOSTACK_INTERFACE_H_ */ diff --git a/net/nanostack-libservice.lib b/net/nanostack-libservice.lib index 98b4a94a0a..dacd2c665f 100644 --- a/net/nanostack-libservice.lib +++ b/net/nanostack-libservice.lib @@ -1 +1 @@ -https://github.com/ARMmbed/nanostack-libservice-mirror.git/#e3f7da74a143 +https://github.com/ARMmbed/nanostack-libservice-mirror/#5490767fc3f8b9f204be20f88456f63a84810d06 diff --git a/net/sal-iface-6lowpan-morpheus-private.lib b/net/sal-iface-6lowpan-morpheus-private.lib deleted file mode 100644 index 36e7ef81d8..0000000000 --- a/net/sal-iface-6lowpan-morpheus-private.lib +++ /dev/null @@ -1 +0,0 @@ -https://github.com/ARMmbed/sal-iface-6lowpan-morpheus-private-mirror.git/#2b4852e22679 diff --git a/net/sal-stack-nanostack-eventloop.lib b/net/sal-stack-nanostack-eventloop.lib index b6736e0045..84b804682d 100644 --- a/net/sal-stack-nanostack-eventloop.lib +++ b/net/sal-stack-nanostack-eventloop.lib @@ -1 +1 @@ -https://github.com/ARMmbed/sal-stack-nanostack-eventloop-mirror.git/#627b9769e352 +https://github.com/ARMmbed/sal-stack-nanostack-eventloop-mirror/#dcbeaf4babf7d74d252e905fd15d1d63bbaaf9c2 diff --git a/net/sal-stack-nanostack-private.lib b/net/sal-stack-nanostack-private.lib index 0e000b3b3c..1f920f848b 100644 --- a/net/sal-stack-nanostack-private.lib +++ b/net/sal-stack-nanostack-private.lib @@ -1 +1 @@ -https://github.com/ARMmbed/sal-stack-nanostack-private-mirror.git/#1374c77b03fb +https://github.com/ARMmbed/sal-stack-nanostack-private-mirror/#c83ddc4f776847d62ae3aa53d591157adc910b59