nsapi_size_or_error_t LWIP::socket_sendto(nsapi_socket_t handle, const SocketAddress &address, const void *data, nsapi_size_t size) { struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle; ip_addr_t ip_addr; nsapi_addr_t addr = address.get_addr(); if (!convert_mbed_addr_to_lwip(&ip_addr, &addr)) { return NSAPI_ERROR_PARAMETER; } struct netbuf *buf = netbuf_new(); err_t err = netbuf_ref(buf, data, (u16_t)size); if (err != ERR_OK) { netbuf_free(buf); return err_remap(err); } err = netconn_sendto(s->conn, buf, &ip_addr, address.get_port()); netbuf_delete(buf); if (err != ERR_OK) { return err_remap(err); } return size; }
nsapi_error_t LWIP::socket_bind(nsapi_socket_t handle, const SocketAddress &address) { struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle; ip_addr_t ip_addr; if ( #if LWIP_TCP (NETCONNTYPE_GROUP(s->conn->type) == NETCONN_TCP && s->conn->pcb.tcp->local_port != 0) || #endif (NETCONNTYPE_GROUP(s->conn->type) == NETCONN_UDP && s->conn->pcb.udp->local_port != 0)) { return NSAPI_ERROR_PARAMETER; } nsapi_addr_t addr = address.get_addr(); if (!convert_mbed_addr_to_lwip(&ip_addr, &addr)) { return NSAPI_ERROR_PARAMETER; } if (!ip_addr_isany_val(ip_addr) && !is_local_addr(&ip_addr)) { return NSAPI_ERROR_PARAMETER; } err_t err = netconn_bind(s->conn, &ip_addr, address.get_port()); return err_remap(err); }
nsapi_error_t LWIP::socket_close(nsapi_socket_t handle) { struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle; netbuf_delete(s->buf); err_t err = netconn_delete(s->conn); arena_dealloc(s); return err_remap(err); }
nsapi_size_or_error_t LWIP::socket_send(nsapi_socket_t handle, const void *data, nsapi_size_t size) { struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle; size_t bytes_written = 0; err_t err = netconn_write_partly(s->conn, data, size, NETCONN_COPY, &bytes_written); if (err != ERR_OK) { return err_remap(err); } return (nsapi_size_or_error_t)bytes_written; }
nsapi_error_t LWIP::socket_listen(nsapi_socket_t handle, int backlog) { #if LWIP_TCP struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle; if (s->conn->pcb.tcp->local_port == 0) { return NSAPI_ERROR_PARAMETER; } err_t err = netconn_listen_with_backlog(s->conn, backlog); return err_remap(err); #else return NSAPI_ERROR_UNSUPPORTED; #endif }
nsapi_error_t LWIP::socket_connect(nsapi_socket_t handle, const SocketAddress &address) { struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle; ip_addr_t ip_addr; nsapi_addr_t addr = address.get_addr(); if (!convert_mbed_addr_to_lwip(&ip_addr, &addr)) { return NSAPI_ERROR_PARAMETER; } netconn_set_nonblocking(s->conn, false); err_t err = netconn_connect(s->conn, &ip_addr, address.get_port()); netconn_set_nonblocking(s->conn, true); return err_remap(err); }
nsapi_error_t LWIP::socket_accept(nsapi_socket_t server, nsapi_socket_t *handle, SocketAddress *address) { #if LWIP_TCP struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)server; struct mbed_lwip_socket *ns = arena_alloc(); if (!ns) { return NSAPI_ERROR_NO_SOCKET; } if (s->conn->pcb.tcp->state != LISTEN) { return NSAPI_ERROR_PARAMETER; } err_t err = netconn_accept(s->conn, &ns->conn); if (err != ERR_OK) { arena_dealloc(ns); return err_remap(err); } netconn_set_recvtimeout(ns->conn, 1); *(struct mbed_lwip_socket **)handle = ns; ip_addr_t peer_addr; nsapi_addr_t addr; u16_t port; (void) netconn_peer(ns->conn, &peer_addr, &port); convert_lwip_addr_to_mbed(&addr, &peer_addr); if (address) { address->set_addr(addr); address->set_port(port); } netconn_set_nonblocking(ns->conn, true); return 0; #else return NSAPI_ERROR_UNSUPPORTED; #endif }
nsapi_size_or_error_t LWIP::socket_recvfrom(nsapi_socket_t handle, SocketAddress *address, void *data, nsapi_size_t size) { struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle; struct netbuf *buf; err_t err = netconn_recv(s->conn, &buf); if (err != ERR_OK) { return err_remap(err); } if (address) { nsapi_addr_t addr; convert_lwip_addr_to_mbed(&addr, netbuf_fromaddr(buf)); address->set_addr(addr); address->set_port(netbuf_fromport(buf)); } u16_t recv = netbuf_copy(buf, data, (u16_t)size); netbuf_delete(buf); return recv; }
nsapi_size_or_error_t LWIP::socket_recv(nsapi_socket_t handle, void *data, nsapi_size_t size) { struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle; if (!s->buf) { err_t err = netconn_recv(s->conn, &s->buf); s->offset = 0; if (err != ERR_OK) { return err_remap(err); } } u16_t recv = netbuf_copy_partial(s->buf, data, (u16_t)size, s->offset); s->offset += recv; if (s->offset >= netbuf_len(s->buf)) { netbuf_delete(s->buf); s->buf = 0; } return recv; }
nsapi_error_t LWIP::Interface::bringdown() { // Check if we've connected if (connected == NSAPI_STATUS_DISCONNECTED) { return NSAPI_ERROR_NO_CONNECTION; } #if LWIP_DHCP // Disconnect from the network if (dhcp_started) { dhcp_release(&netif); dhcp_stop(&netif); dhcp_started = false; dhcp_has_to_be_set = false; } #endif if (ppp) { /* this is a blocking call, returns when PPP is properly closed */ err_t err = ppp_lwip_disconnect(hw); if (err) { return err_remap(err); } MBED_ASSERT(!netif_is_link_up(&netif)); /*if (netif_is_link_up(&netif)) { if (sys_arch_sem_wait(&unlinked, 15000) == SYS_ARCH_TIMEOUT) { return NSAPI_ERROR_DEVICE_ERROR; } }*/ } else { netif_set_down(&netif); } #if LWIP_IPV6 mbed_lwip_clear_ipv6_addresses(&netif); #endif #if LWIP_IPV4 ip_addr_set_zero(&(netif.ip_addr)); ip_addr_set_zero(&(netif.netmask)); ip_addr_set_zero(&(netif.gw)); #endif osSemaphoreDelete(has_any_addr); osSemaphoreAttr_t attr; attr.name = NULL; attr.attr_bits = 0; attr.cb_mem = &has_any_addr_sem; attr.cb_size = sizeof has_any_addr_sem; has_any_addr = osSemaphoreNew(UINT16_MAX, 0, &attr); #if PREF_ADDR_TIMEOUT osSemaphoreDelete(has_pref_addr); attr.cb_mem = &has_pref_addr_sem; attr.cb_size = sizeof has_pref_addr_sem; has_pref_addr = osSemaphoreNew(UINT16_MAX, 0, &attr); #endif #if BOTH_ADDR_TIMEOUT osSemaphoreDelete(has_both_addr); attr.cb_mem = &has_both_addr_sem; attr.cb_size = sizeof has_both_addr_sem; has_both_addr = osSemaphoreNew(UINT16_MAX, 0, &attr); #endif has_addr_state = 0; connected = NSAPI_STATUS_DISCONNECTED; if (client_callback) { client_callback(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, connected); } return 0; }
nsapi_error_t LWIP::Interface::bringup(bool dhcp, const char *ip, const char *netmask, const char *gw, const nsapi_ip_stack_t stack, bool block) { // Check if we've already connected if (connected == NSAPI_STATUS_GLOBAL_UP) { return NSAPI_ERROR_IS_CONNECTED; } else if (connected == NSAPI_STATUS_CONNECTING) { return NSAPI_ERROR_ALREADY; } connected = NSAPI_STATUS_CONNECTING; blocking = block; #if LWIP_DHCP if (stack != IPV6_STACK && dhcp) { dhcp_has_to_be_set = true; } #endif #if LWIP_IPV6 if (stack != IPV4_STACK) { if (netif.hwaddr_len == 6) { netif_create_ip6_linklocal_address(&netif, 1/*from MAC*/); } #if LWIP_IPV6_MLD /* * For hardware/netifs that implement MAC filtering. * All-nodes link-local is handled by default, so we must let the hardware know * to allow multicast packets in. * Should set mld_mac_filter previously. */ if (netif.mld_mac_filter != NULL) { ip6_addr_t ip6_allnodes_ll; ip6_addr_set_allnodes_linklocal(&ip6_allnodes_ll); netif.mld_mac_filter(&netif, &ip6_allnodes_ll, NETIF_ADD_MAC_FILTER); } #endif /* LWIP_IPV6_MLD */ #if LWIP_IPV6_AUTOCONFIG /* IPv6 address autoconfiguration not enabled by default */ netif.ip6_autoconfig_enabled = 1; #endif /* LWIP_IPV6_AUTOCONFIG */ } else { // Disable rourter solicitations netif.rs_count = 0; } #endif /* LWIP_IPV6 */ #if LWIP_IPV4 if (stack != IPV6_STACK) { if (!dhcp && !ppp) { ip4_addr_t ip_addr; ip4_addr_t netmask_addr; ip4_addr_t gw_addr; if (!inet_aton(ip, &ip_addr) || !inet_aton(netmask, &netmask_addr) || !inet_aton(gw, &gw_addr)) { return NSAPI_ERROR_PARAMETER; } netif_set_addr(&netif, &ip_addr, &netmask_addr, &gw_addr); } } #endif if (client_callback) { client_callback(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, NSAPI_STATUS_CONNECTING); } if (ppp) { err_t err = ppp_lwip_connect(hw); if (err) { connected = NSAPI_STATUS_DISCONNECTED; if (client_callback) { client_callback(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, NSAPI_STATUS_DISCONNECTED); } return err_remap(err); } } if (!netif_is_link_up(&netif)) { if (blocking) { if (osSemaphoreAcquire(linked, 15000) != osOK) { if (ppp) { (void) ppp_lwip_disconnect(hw); } return NSAPI_ERROR_NO_CONNECTION; } } } else { nsapi_error_t ret = set_dhcp(); if (ret != NSAPI_ERROR_OK) { return ret; } } if (!blocking) { // Done enough - as addresses are acquired, there will be // connected callbacks. // XXX shouldn't this be NSAPI_ERROR_IN_PROGRESS if in CONNECTING state? return NSAPI_ERROR_OK; } // If doesn't have address if (!LWIP::get_ip_addr(true, &netif)) { if (osSemaphoreAcquire(has_any_addr, DHCP_TIMEOUT * 1000) != osOK) { if (ppp) { (void) ppp_lwip_disconnect(hw); } return NSAPI_ERROR_DHCP_FAILURE; } } #if PREF_ADDR_TIMEOUT if (stack != IPV4_STACK && stack != IPV6_STACK) { // If address is not for preferred stack waits a while to see // if preferred stack address is acquired if (!LWIP::get_ip_addr(false, &netif)) { osSemaphoreAcquire(has_pref_addr, PREF_ADDR_TIMEOUT * 1000); } } #endif #if BOTH_ADDR_TIMEOUT if (stack != IPV4_STACK && stack != IPV6_STACK) { // If addresses for both stacks are not available waits a while to // see if address for both stacks are acquired if (!(LWIP::get_ipv4_addr(&netif) && LWIP::get_ipv6_addr(&netif))) { osSemaphoreAcquire(has_both_addr, BOTH_ADDR_TIMEOUT * 1000); } } #endif add_dns_addr(&netif); return NSAPI_ERROR_OK; }
nsapi_error_t LWIP::setsockopt(nsapi_socket_t handle, int level, int optname, const void *optval, unsigned optlen) { struct mbed_lwip_socket *s = (struct mbed_lwip_socket *)handle; switch (optname) { case NSAPI_BIND_TO_DEVICE: if (optlen > NSAPI_INTERFACE_NAME_MAX_SIZE) { return NSAPI_ERROR_UNSUPPORTED; } if (NETCONNTYPE_GROUP(s->conn->type) == NETCONN_TCP) { s->conn->pcb.tcp->interface_name = (const char *)optval; } if (NETCONNTYPE_GROUP(s->conn->type) == NETCONN_UDP) { s->conn->pcb.udp->interface_name = (const char *)optval; } #if LWIP_RAW if (NETCONNTYPE_GROUP(s->conn->type) == NETCONN_RAW) { s->conn->pcb.raw->interface_name = (const char *)optval; } #endif return 0; #if LWIP_TCP case NSAPI_KEEPALIVE: if (optlen != sizeof(int) || NETCONNTYPE_GROUP(s->conn->type) != NETCONN_TCP) { return NSAPI_ERROR_UNSUPPORTED; } s->conn->pcb.tcp->so_options |= SOF_KEEPALIVE; return 0; case NSAPI_KEEPIDLE: if (optlen != sizeof(int) || NETCONNTYPE_GROUP(s->conn->type) != NETCONN_TCP) { return NSAPI_ERROR_UNSUPPORTED; } s->conn->pcb.tcp->keep_idle = *(int *)optval; return 0; case NSAPI_KEEPINTVL: if (optlen != sizeof(int) || NETCONNTYPE_GROUP(s->conn->type) != NETCONN_TCP) { return NSAPI_ERROR_UNSUPPORTED; } s->conn->pcb.tcp->keep_intvl = *(int *)optval; return 0; #endif case NSAPI_REUSEADDR: if (optlen != sizeof(int)) { return NSAPI_ERROR_UNSUPPORTED; } if (*(int *)optval) { ip_set_option(s->conn->pcb.ip, SOF_REUSEADDR); } else { ip_reset_option(s->conn->pcb.ip, SOF_REUSEADDR); } return 0; case NSAPI_ADD_MEMBERSHIP: case NSAPI_DROP_MEMBERSHIP: { if (optlen != sizeof(nsapi_ip_mreq_t)) { return NSAPI_ERROR_PARAMETER; } err_t igmp_err; const nsapi_ip_mreq_t *imr = static_cast<const nsapi_ip_mreq_t *>(optval); /* Check interface address type matches group, or is unspecified */ if (imr->imr_interface.version != NSAPI_UNSPEC && imr->imr_interface.version != imr->imr_multiaddr.version) { return NSAPI_ERROR_PARAMETER; } ip_addr_t if_addr; ip_addr_t multi_addr; /* Convert the group address */ if (!convert_mbed_addr_to_lwip(&multi_addr, &imr->imr_multiaddr)) { return NSAPI_ERROR_PARAMETER; } /* Convert the interface address, or make sure it's the correct sort of "any" */ if (imr->imr_interface.version != NSAPI_UNSPEC) { if (!convert_mbed_addr_to_lwip(&if_addr, &imr->imr_interface)) { return NSAPI_ERROR_PARAMETER; } } else { ip_addr_set_any(IP_IS_V6(&if_addr), &if_addr); } igmp_err = ERR_USE; // Maps to NSAPI_ERROR_UNSUPPORTED int32_t member_pair_index = find_multicast_member(s, imr); if (optname == NSAPI_ADD_MEMBERSHIP) { if (!s->multicast_memberships) { // First multicast join on this socket, allocate space for membership tracking s->multicast_memberships = (nsapi_ip_mreq_t *)malloc(sizeof(nsapi_ip_mreq_t) * LWIP_SOCKET_MAX_MEMBERSHIPS); if (!s->multicast_memberships) { return NSAPI_ERROR_NO_MEMORY; } } else if (s->multicast_memberships_count == LWIP_SOCKET_MAX_MEMBERSHIPS) { return NSAPI_ERROR_NO_MEMORY; } if (member_pair_index != -1) { return NSAPI_ERROR_ADDRESS_IN_USE; } member_pair_index = next_free_multicast_member(s, 0); adaptation.lock(); #if LWIP_IPV4 if (IP_IS_V4(&if_addr)) { igmp_err = igmp_joingroup(ip_2_ip4(&if_addr), ip_2_ip4(&multi_addr)); } #endif #if LWIP_IPV6 if (IP_IS_V6(&if_addr)) { igmp_err = mld6_joingroup(ip_2_ip6(&if_addr), ip_2_ip6(&multi_addr)); } #endif adaptation.unlock(); if (igmp_err == ERR_OK) { set_multicast_member_registry_bit(s, member_pair_index); s->multicast_memberships[member_pair_index] = *imr; s->multicast_memberships_count++; } } else { if (member_pair_index == -1) { return NSAPI_ERROR_NO_ADDRESS; } clear_multicast_member_registry_bit(s, member_pair_index); s->multicast_memberships_count--; adaptation.lock(); #if LWIP_IPV4 if (IP_IS_V4(&if_addr)) { igmp_err = igmp_leavegroup(ip_2_ip4(&if_addr), ip_2_ip4(&multi_addr)); } #endif #if LWIP_IPV6 if (IP_IS_V6(&if_addr)) { igmp_err = mld6_leavegroup(ip_2_ip6(&if_addr), ip_2_ip6(&multi_addr)); } #endif adaptation.unlock(); } return err_remap(igmp_err); } default: return NSAPI_ERROR_UNSUPPORTED; } }