/** @file * @brief Network initialization * * Initialize the network IP stack. Create two fibers, one for reading data * from applications (Tx fiber) and one for reading data from IP stack * and passing that data to applications (Rx fiber). */ /* * Copyright (c) 2015 Intel Corporation * * 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. */ #ifdef CONFIG_NETWORKING_WITH_LOGGING #define DEBUG 1 #endif #include "contiki/ip/uip-debug.h" #include #include #include #include #include #include #include #include #include #include #include "net_driver_15_4.h" #include "net_driver_slip.h" #include "net_driver_ethernet.h" #include "net_driver_bt.h" #include "contiki/os/sys/process.h" #include "contiki/os/sys/etimer.h" #include "contiki/os/sys/ctimer.h" #include "contiki/netstack.h" #include "contiki/ipv6/uip-ds6.h" #include "contiki/ip/simple-udp.h" #include "contiki/os/dev/slip.h" #ifdef CONFIG_15_4_BEACON_SUPPORT #include "contiki/mac/handler-802154.h" #endif #ifdef CONFIG_DHCP #include "contiki/ip/dhcpc.h" #endif /* Declare some private functions only to be used in this file so the * prototypes are not found in .h file. */ struct nano_fifo *net_context_get_queue(struct net_context *context); struct simple_udp_connection * net_context_get_udp_connection(struct net_context *context); int net_context_get_receiver_registered(struct net_context *context); void net_context_set_receiver_registered(struct net_context *context); int net_context_tcp_init(struct net_context *context, struct net_buf *buf, enum net_tcp_type); int net_context_tcp_send(struct net_buf *buf); void *net_context_get_internal_connection(struct net_context *context); struct net_buf *net_context_tcp_get_pending(struct net_context *context); void net_context_tcp_set_pending(struct net_context *context, struct net_buf *buf); void net_context_set_connection_status(struct net_context *context, int status); void net_context_unset_receiver_registered(struct net_context *context); extern void net_context_tcp_set_retry_count(struct net_context *context, uint8_t count); extern uint8_t net_context_tcp_get_retry_count(struct net_context *context); /* Stacks for the tx & rx fibers. * FIXME: stack size needs fine-tuning */ #define STACKSIZE_UNIT 1024 #ifndef CONFIG_IP_RX_STACK_SIZE #define CONFIG_IP_RX_STACK_SIZE (STACKSIZE_UNIT * 1) #endif #ifndef CONFIG_IP_TX_STACK_SIZE #define CONFIG_IP_TX_STACK_SIZE (STACKSIZE_UNIT * 1) #endif #ifndef CONFIG_IP_TIMER_STACK_SIZE #define CONFIG_IP_TIMER_STACK_SIZE (STACKSIZE_UNIT * 3 / 2) #endif static char __noinit __stack rx_fiber_stack[CONFIG_IP_RX_STACK_SIZE]; static char __noinit __stack tx_fiber_stack[CONFIG_IP_TX_STACK_SIZE]; static char __noinit __stack timer_fiber_stack[CONFIG_IP_TIMER_STACK_SIZE]; static nano_thread_id_t timer_fiber_id, tx_fiber_id; static uint8_t initialized; static struct net_dev { /* Queue for incoming packets from driver */ struct nano_fifo rx_queue; /* Queue for outgoing packets from apps */ struct nano_fifo tx_queue; /* Registered network driver */ struct net_driver *drv; } netdev; /* Called by application to send a packet */ int net_send(struct net_buf *buf) { int ret = 0; if (!buf || ip_buf_len(buf) == 0) { return -ENODATA; } if (buf->len && !uip_appdatalen(buf)) { uip_appdatalen(buf) = buf->len; } #ifdef CONFIG_NETWORKING_WITH_TCP #define MAX_TCP_RETRY_COUNT 3 if (ip_buf_context(buf) && net_context_get_tuple(ip_buf_context(buf))->ip_proto == IPPROTO_TCP) { struct uip_conn *conn; int status; uint8_t retry_count; net_context_tcp_init(ip_buf_context(buf), buf, NET_TCP_TYPE_CLIENT); status = net_context_get_connection_status( ip_buf_context(buf)); NET_DBG("context %p buf %p status %d\n", ip_buf_context(buf), buf, status); switch (status) { case EISCONN: /* User should be able to send new data now. */ NET_DBG("Send new data buf %p ref %d\n", buf, buf->ref); net_context_set_connection_status(ip_buf_context(buf), 0); conn = (struct uip_conn *)net_context_get_internal_connection(ip_buf_context(buf)); if (conn->buf) { ip_buf_unref(conn->buf); if (conn->buf == buf) { conn->buf = NULL; return 0; } conn->buf = NULL; } break; case -EALREADY: NET_DBG("Connection established\n"); return 0; case -EINPROGRESS: NET_DBG("Connection being established\n"); retry_count = net_context_tcp_get_retry_count( ip_buf_context(buf)); if (retry_count < MAX_TCP_RETRY_COUNT) { net_context_tcp_set_retry_count( ip_buf_context(buf), ++retry_count); return status; } net_context_tcp_set_retry_count(ip_buf_context(buf), 0); break; case -ECONNRESET: NET_DBG("Connection reset\n"); net_context_unset_receiver_registered( ip_buf_context(buf)); return status; } ret = status; } #endif nano_fifo_put(&netdev.tx_queue, buf); /* Tell the IP stack it can proceed with the packet */ fiber_wakeup(tx_fiber_id); return ret; } #ifdef CONFIG_NETWORKING_STATISTICS #define STAT(s) uip_stat.s #define PRINT_STATISTICS_INTERVAL (10 * sys_clock_ticks_per_sec) #define net_print_statistics stats /* to make the debug print line shorter */ #if NET_MAC_CONF_STATS #include "mac/mac.h" #endif #if RPL_CONF_STATS #include "rpl/rpl-private.h" #endif #if NET_COAP_CONF_STATS #include "er-coap/er-coap.h" #endif #if HANDLER_802154_CONF_STATS #include "mac/handler-802154.h" #endif static void stats(void) { static clock_time_t last_print; /* See contiki/ip/uip.h for descriptions of the different values */ if (clock_time() > (last_print + PRINT_STATISTICS_INTERVAL)) { #if NET_MAC_CONF_STATS #define MAC_STAT(s) (net_mac_stats.s) NET_DBG("L2 bytes recv %d\tsent\t%d\n", MAC_STAT(bytes_received), MAC_STAT(bytes_sent)); #endif NET_DBG("IP recv %d\tsent\t%d\tdrop\t%d\tforwarded\t%d\n", STAT(ip.recv), STAT(ip.sent), STAT(ip.drop), STAT(ip.forwarded)); NET_DBG("IP vhlerr %d\thblener\t%d\tlblener\t%d\n", STAT(ip.vhlerr), STAT(ip.hblenerr), STAT(ip.lblenerr)); NET_DBG("IP fragerr %d\tchkerr\t%d\tprotoer\t%d\n", STAT(ip.fragerr), STAT(ip.chkerr), STAT(ip.protoerr)); #ifdef CONFIG_NETWORKING_WITH_TCP NET_DBG("TCP recv %d\tsent\t%d\tdrop\t%d\n", STAT(tcp.recv), STAT(tcp.sent), STAT(tcp.drop)); NET_DBG("TCP chkerr %d\tackerr\t%d\trst\t%d\n", STAT(tcp.chkerr), STAT(tcp.ackerr), STAT(tcp.rst)); NET_DBG("TCP rexmit %d\tsyndrop\t%d\tsynrst\t%d\n", STAT(tcp.rexmit), STAT(tcp.syndrop), STAT(tcp.synrst)); #endif NET_DBG("ICMP recv %d\tsent\t%d\tdrop\t%d\n", STAT(icmp.recv), STAT(icmp.sent), STAT(icmp.drop)); NET_DBG("ICMP typeer %d\tchkerr\t%d\n", STAT(icmp.typeerr), STAT(icmp.chkerr)); NET_DBG("UDP recv %d\tsent\t%d\tdrop\t%d\n", STAT(udp.recv), STAT(udp.sent), STAT(udp.drop)); NET_DBG("UDP chkerr %d\n", STAT(icmp.chkerr)); #if NET_COAP_CONF_STATS NET_DBG("CoAP recv %d\terr\t%d\tsent\t%d\tre-sent\t%d\n", NET_COAP_STAT(recv), NET_COAP_STAT(recv_err), NET_COAP_STAT(sent), NET_COAP_STAT(re_sent)); #endif #if NETSTACK_CONF_WITH_IPV6 NET_DBG("ND recv %d\tsent\t%d\tdrop\t%d\n", STAT(nd6.recv), STAT(nd6.sent), STAT(nd6.drop)); #endif #if RPL_CONF_STATS #define RSTAT(s) RPL_STAT(rpl_stats.s) NET_DBG("RPL overflows %d\tl-repairs\t%d\tg-repairs\t%d\n", RSTAT(mem_overflows), RSTAT(local_repairs), RSTAT(global_repairs)); NET_DBG("RPL malformed %d\tresets \t%d\tp-switch\t%d\n", RSTAT(malformed_msgs), RSTAT(resets), RSTAT(parent_switch)); NET_DBG("RPL f-errors %d\tl-errors\t%d\tl-warnings\t%d\n", RSTAT(forward_errors), RSTAT(loop_errors), RSTAT(loop_warnings)); NET_DBG("RPL r-repairs %d\n", RSTAT(root_repairs)); #endif #if HANDLER_802154_CONF_STATS #define IEEE802154_STAT(s) (handler_802154_stats.s) NET_DBG("802.15.4 beacons recv\t%d\tsent\t%d\treqs sent\t%d\n", IEEE802154_STAT(beacons_received), IEEE802154_STAT(beacons_sent), IEEE802154_STAT(beacons_reqs_sent)); #endif last_print = clock_time(); } } #else #define net_print_statistics() #endif /* Switch the ports and addresses and set route and neighbor cache. * Returns 1 if packet was sent properly, in this case it is the caller * that needs to release the net_buf. If 0 is returned, then uIP stack * has released the net_buf already because there was an some net related * error when sending the buffer. */ static inline int udp_prepare_and_send(struct net_context *context, struct net_buf *buf) { #ifdef CONFIG_NETWORKING_IPV6_NO_ND uip_ds6_route_t *route_old, *route_new = NULL; uip_ds6_nbr_t *nbr; #endif uip_ipaddr_t tmp; uint16_t port; uint8_t ret; if (uip_len(buf) == 0) { /* This is expected as uIP will typically set the * packet length to 0 after receiving it. So we need * to fix the length here. The protocol specific * part is added also here. */ uip_len(buf) = ip_buf_len(buf); } ip_buf_appdata(buf) = &uip_buf(buf)[UIP_IPUDPH_LEN + UIP_LLH_LEN]; port = NET_BUF_UDP(buf)->srcport; NET_BUF_UDP(buf)->srcport = NET_BUF_UDP(buf)->destport; NET_BUF_UDP(buf)->destport = port; uip_ipaddr_copy(&tmp, &NET_BUF_IP(buf)->srcipaddr); uip_ipaddr_copy(&NET_BUF_IP(buf)->srcipaddr, &NET_BUF_IP(buf)->destipaddr); uip_ipaddr_copy(&NET_BUF_IP(buf)->destipaddr, &tmp); #ifdef CONFIG_NETWORKING_IPV6_NO_ND /* The peer needs to be in neighbor cache before route can be added. */ nbr = uip_ds6_nbr_lookup((uip_ipaddr_t *)&NET_BUF_IP(buf)->destipaddr); if (!nbr) { const uip_lladdr_t *lladdr = (const uip_lladdr_t *)&ip_buf_ll_src(buf); nbr = uip_ds6_nbr_add( (uip_ipaddr_t *)&NET_BUF_IP(buf)->destipaddr, lladdr, 0, NBR_REACHABLE); if (!nbr) { NET_DBG("Cannot add peer "); PRINT6ADDR(&NET_BUF_IP(buf)->destipaddr); PRINT(" to neighbor cache\n"); } } /* Temporarily add route to peer, delete the route after * sending the packet. Check if there was already a * route and do not remove it if there was existing * route to this peer. */ route_old = uip_ds6_route_lookup(&NET_BUF_IP(buf)->destipaddr); if (!route_old) { route_new = uip_ds6_route_add(&NET_BUF_IP(buf)->destipaddr, 128, &NET_BUF_IP(buf)->destipaddr); if (!route_new) { NET_DBG("Cannot add route to peer "); PRINT6ADDR(&NET_BUF_IP(buf)->destipaddr); PRINT("\n"); } } #endif ret = simple_udp_sendto_port(buf, net_context_get_udp_connection(context), ip_buf_appdata(buf), ip_buf_appdatalen(buf), &NET_BUF_IP(buf)->destipaddr, uip_ntohs(NET_BUF_UDP(buf)->destport)); if (ret <= 0) { ret = -EINVAL; NET_DBG("Packet could not be sent properly.\n"); } else { ret = 0; } #ifdef CONFIG_NETWORKING_IPV6_NO_ND if (!route_old && route_new) { /* This will also remove the neighbor cache entry */ uip_ds6_route_rm(route_new); } #endif return ret; } #ifdef CONFIG_NETWORKING_WITH_TCP /* Switch the ports and addresses. Returns 1 if packet was sent properly, * in this case it is the caller that needs to release the net_buf. * If 0 is returned, then uIP stack has released the net_buf already * because there was an some net related error when sending the buffer. */ static inline int tcp_prepare_and_send(struct net_context *context, struct net_buf *buf) { #ifdef CONFIG_NETWORKING_IPV6_NO_ND uip_ds6_route_t *route_old, *route_new = NULL; uip_ds6_nbr_t *nbr; #endif uip_ipaddr_t tmp; uint16_t port; int ret; uip_len(buf) = uip_slen(buf) = ip_buf_len(buf); uip_flags(buf) |= UIP_NEWDATA; port = NET_BUF_UDP(buf)->srcport; NET_BUF_UDP(buf)->srcport = NET_BUF_UDP(buf)->destport; NET_BUF_UDP(buf)->destport = port; uip_ipaddr_copy(&tmp, &NET_BUF_IP(buf)->srcipaddr); uip_ipaddr_copy(&NET_BUF_IP(buf)->srcipaddr, &NET_BUF_IP(buf)->destipaddr); uip_ipaddr_copy(&NET_BUF_IP(buf)->destipaddr, &tmp); #ifdef CONFIG_NETWORKING_IPV6_NO_ND /* The peer needs to be in neighbor cache before route can be added. */ nbr = uip_ds6_nbr_lookup((uip_ipaddr_t *)&NET_BUF_IP(buf)->destipaddr); if (!nbr) { const uip_lladdr_t *lladdr = (const uip_lladdr_t *)&ip_buf_ll_src(buf); nbr = uip_ds6_nbr_add( (uip_ipaddr_t *)&NET_BUF_IP(buf)->destipaddr, lladdr, 0, NBR_REACHABLE); if (!nbr) { NET_DBG("Cannot add peer "); PRINT6ADDR(&NET_BUF_IP(buf)->destipaddr); PRINT(" to neighbor cache\n"); } } /* Temporarily add route to peer, delete the route after * sending the packet. Check if there was already a * route and do not remove it if there was existing * route to this peer. */ route_old = uip_ds6_route_lookup(&NET_BUF_IP(buf)->destipaddr); if (!route_old) { route_new = uip_ds6_route_add(&NET_BUF_IP(buf)->destipaddr, 128, &NET_BUF_IP(buf)->destipaddr); if (!route_new) { NET_DBG("Cannot add route to peer "); PRINT6ADDR(&NET_BUF_IP(buf)->destipaddr); PRINT("\n"); } } #endif NET_DBG("Packet output len %d\n", uip_len(buf)); ret = net_context_tcp_send(buf); if (ret < 0 && ret != -EAGAIN) { NET_DBG("Packet could not be sent properly (err %d)\n", ret); } ip_buf_sent_status(buf) = 0; #ifdef CONFIG_NETWORKING_IPV6_NO_ND if (!route_old && route_new) { /* This will also remove the neighbor cache entry */ uip_ds6_route_rm(route_new); } #endif return ret; } #endif /* Application wants to send a reply */ int net_reply(struct net_context *context, struct net_buf *buf) { struct net_tuple *tuple; struct uip_udp_conn *udp; int ret = 0; if (!context || !buf) { return -EINVAL; } tuple = net_context_get_tuple(context); if (!tuple) { return -ENOENT; } switch (tuple->ip_proto) { case IPPROTO_UDP: udp = uip_udp_conn(buf); if (!udp) { NET_ERR("UDP connection missing\n"); return -ESRCH; } ret = udp_prepare_and_send(context, buf); break; case IPPROTO_TCP: #ifdef CONFIG_NETWORKING_WITH_TCP ret = tcp_prepare_and_send(context, buf); #else NET_DBG("TCP not supported\n"); return -EINVAL; #endif break; case IPPROTO_ICMPV6: NET_DBG("ICMPv6 not yet supported\n"); return -EINVAL; } return ret; } /* Called by driver when an IP packet has been received */ int net_recv(struct net_buf *buf) { if (ip_buf_len(buf) == 0) { return -ENODATA; } nano_fifo_put(&netdev.rx_queue, buf); return 0; } static void udp_packet_receive(struct simple_udp_connection *c, const uip_ipaddr_t *source_addr, uint16_t source_port, const uip_ipaddr_t *dest_addr, uint16_t dest_port, const uint8_t *data, uint16_t datalen, void *user_data, struct net_buf *buf) { struct net_context *context = user_data; if (!context) { /* If the context is not there, then we must discard * the buffer here, otherwise we have a buffer leak. */ ip_buf_unref(buf); return; } ip_buf_appdatalen(buf) = datalen; ip_buf_appdata(buf) = &uip_buf(buf)[UIP_IPUDPH_LEN + UIP_LLH_LEN]; NET_DBG("packet received context %p len %d " "appdata %p appdatalen %d\n", context, ip_buf_len(buf), ip_buf_appdata(buf), ip_buf_appdatalen(buf)); nano_fifo_put(net_context_get_queue(context), buf); } #ifdef CONFIG_NANO_TIMEOUTS static inline struct net_buf *buf_wait_timeout(struct nano_fifo *queue, int32_t timeout) { switch (sys_execution_context_type_get()) { case NANO_CTX_FIBER: return nano_fiber_fifo_get(queue, timeout); case NANO_CTX_TASK: return nano_task_fifo_get(queue, timeout); case NANO_CTX_ISR: default: /* Invalid context type */ break; } return NULL; } #endif /* Called by application when it wants to receive network data */ struct net_buf *net_receive(struct net_context *context, int32_t timeout) { struct nano_fifo *rx_queue = net_context_get_queue(context); struct net_buf *buf; struct net_tuple *tuple; int ret = 0; uint16_t reserve = 0; tuple = net_context_get_tuple(context); if (!tuple) { return NULL; } switch (tuple->ip_proto) { case IPPROTO_UDP: if (!net_context_get_receiver_registered(context)) { struct simple_udp_connection *udp = net_context_get_udp_connection(context); ret = simple_udp_register(udp, tuple->local_port, #ifdef CONFIG_NETWORKING_WITH_IPV6 (uip_ip6addr_t *)&tuple->remote_addr->in6_addr, #else (uip_ip4addr_t *)&tuple->remote_addr->in_addr, #endif tuple->remote_port, udp_packet_receive, context); if (!ret) { NET_DBG("UDP connection listener failed\n"); ret = -ENOENT; break; } } net_context_set_receiver_registered(context); ret = 0; reserve = UIP_IPUDPH_LEN + UIP_LLH_LEN; break; case IPPROTO_TCP: #ifdef CONFIG_NETWORKING_WITH_TCP ret = net_context_tcp_init(context, NULL, NET_TCP_TYPE_SERVER); if (ret) { NET_DBG("TCP connection init failed\n"); ret = -ENOENT; break; } ret = 0; #else NET_DBG("TCP not supported\n"); ret = -EINVAL; #endif break; case IPPROTO_ICMPV6: NET_DBG("ICMPv6 not yet supported\n"); ret = -EINVAL; break; default: NET_ERR("Invalid IP protocol. " "Internal data structure corrupted!\n"); ret = -EINVAL; break; } if (ret) { return NULL; } switch (timeout) { case TICKS_UNLIMITED: buf = nano_fifo_get(rx_queue, TICKS_UNLIMITED); break; case TICKS_NONE: buf = nano_fifo_get(rx_queue, TICKS_NONE); break; default: #ifdef CONFIG_NANO_TIMEOUTS buf = buf_wait_timeout(rx_queue, timeout); #else /* CONFIG_NANO_TIMEOUTS */ buf = nano_fifo_get(rx_queue, TICKS_NONE); #endif break; } #ifdef CONFIG_NETWORKING_WITH_TCP if (tuple->ip_proto == IPPROTO_TCP && (ip_buf_appdata(buf) > (void *)buf->data)) { /* We need to skip the TCP header + possible extensions */ reserve = ip_buf_appdata(buf) - (void *)buf->data; } #endif if (buf && reserve) { ip_buf_appdatalen(buf) = ip_buf_len(buf) - reserve; ip_buf_appdata(buf) = &uip_buf(buf)[reserve]; } return buf; } static void udp_packet_reply(struct simple_udp_connection *c, const uip_ipaddr_t *source_addr, uint16_t source_port, const uip_ipaddr_t *dest_addr, uint16_t dest_port, const uint8_t *data, uint16_t datalen, void *user_data, struct net_buf *buf) { struct net_context *context = user_data; struct nano_fifo *queue; if (!context) { /* If the context is not there, then we must discard * the buffer here, otherwise we have a buffer leak. */ ip_buf_unref(buf); return; } queue = net_context_get_queue(context); /* Contiki stack will overwrite the uip_len(buf) and * uip_appdatalen(buf) values, so in order to allow * the application to use them, copy the values here. */ ip_buf_appdatalen(buf) = datalen; NET_DBG("packet reply context %p len %d " "appdata %p appdatalen %d queue %p\n", context, ip_buf_len(buf), ip_buf_appdata(buf), ip_buf_appdatalen(buf), queue); nano_fifo_put(queue, buf); } /* Internal function to send network data to uIP stack */ static int check_and_send_packet(struct net_buf *buf) { struct net_tuple *tuple; struct simple_udp_connection *udp; int ret = 0; if (!netdev.drv) { return -EINVAL; } tuple = net_context_get_tuple(ip_buf_context(buf)); if (!tuple) { return -EINVAL; } switch (tuple->ip_proto) { case IPPROTO_UDP: udp = net_context_get_udp_connection(ip_buf_context(buf)); if (!net_context_get_receiver_registered(ip_buf_context(buf))) { ret = simple_udp_register(udp, tuple->local_port, #ifdef CONFIG_NETWORKING_WITH_IPV6 (uip_ip6addr_t *)&tuple->remote_addr->in6_addr, #else (uip_ip4addr_t *)&tuple->remote_addr->in_addr, #endif tuple->remote_port, udp_packet_reply, ip_buf_context(buf)); if (!ret) { NET_DBG("UDP connection creation failed\n"); ret = -ENOENT; break; } net_context_set_receiver_registered(ip_buf_context(buf)); } if (ip_buf_appdatalen(buf) == 0) { /* User application has not set the application data * length. The buffer will be discarded if we do not * set the value correctly. */ uip_appdatalen(buf) = buf->len - (UIP_IPUDPH_LEN + UIP_LLH_LEN); } ret = simple_udp_send(buf, udp, uip_appdata(buf), uip_appdatalen(buf)); break; case IPPROTO_TCP: #ifdef CONFIG_NETWORKING_WITH_TCP if (ip_buf_appdatalen(buf) == 0) { /* User application has not set the application data * length. The buffer will be discarded if we do not * set the value correctly. */ uip_appdatalen(buf) = buf->len - (UIP_IPTCPH_LEN + UIP_LLH_LEN); } if (uip_len(buf) == 0) { uip_len(buf) = buf->len; } ret = net_context_tcp_send(buf); if (ret < 0 && ret != -EAGAIN) { NET_DBG("Packet could not be sent properly " "(err %d)\n", ret); } else if (ret == 0) { /* For TCP the return status 0 means that the packet * is released already. The caller of this function * expects return value of > 0 in this case. */ ret = 1; } else { ip_buf_sent_status(buf) = ret; ret = true; /* This will prevent caller to discard * the buffer that needs to be resent * again. */ } #else NET_DBG("TCP not supported\n"); ret = -EINVAL; #endif break; case IPPROTO_ICMPV6: NET_DBG("ICMPv6 not yet supported\n"); ret = -EINVAL; break; } return ret; } static void net_tx_fiber(void) { NET_DBG("Starting TX fiber (stack %d bytes)\n", sizeof(tx_fiber_stack)); while (1) { struct net_buf *buf; int ret; /* Get next packet from application - wait if necessary */ buf = nano_fifo_get(&netdev.tx_queue, TICKS_UNLIMITED); NET_DBG("Sending (buf %p, len %u) to IP stack\n", buf, buf->len); /* What to do with the buffer: * <0: error, release the buffer * 0: message was discarded by uIP, release the buffer here * >0: message was sent ok, buffer released already */ ret = check_and_send_packet(buf); if (ret < 0) { ip_buf_unref(buf); goto wait_next; } else if (ret > 0) { goto wait_next; } NET_BUF_CHECK_IF_NOT_IN_USE(buf); /* Check for any events that we might need to process */ do { ret = process_run(buf); } while (ret > 0); ip_buf_unref(buf); wait_next: /* Check stack usage (no-op if not enabled) */ net_analyze_stack("TX fiber", tx_fiber_stack, sizeof(tx_fiber_stack)); net_print_statistics(); } } static void net_rx_fiber(void) { struct net_buf *buf; NET_DBG("Starting RX fiber (stack %d bytes)\n", sizeof(rx_fiber_stack)); while (1) { buf = nano_fifo_get(&netdev.rx_queue, TICKS_UNLIMITED); /* Check stack usage (no-op if not enabled) */ net_analyze_stack("RX fiber", rx_fiber_stack, sizeof(rx_fiber_stack)); NET_DBG("Received buf %p\n", buf); if (!tcpip_input(buf)) { ip_buf_unref(buf); } /* The buffer is on to its way to receiver at this * point. We must not remove it here. */ net_print_statistics(); } } /* * Run various Contiki timers. */ #define MAX_TIMER_WAKEUP 0x7ffffff static void net_timer_fiber(void) { clock_time_t next_wakeup; NET_DBG("Starting net timer fiber (stack %d bytes)\n", sizeof(timer_fiber_stack)); while (1) { /* Run various timers */ next_wakeup = etimer_request_poll(); if (next_wakeup == 0) { /* There was no timers, wait again */ next_wakeup = MAX_TIMER_WAKEUP; } else { if (next_wakeup > MAX_TIMER_WAKEUP) { next_wakeup = MAX_TIMER_WAKEUP; } #ifdef CONFIG_INIT_STACKS { #define PRINT_CYCLE (60 * sys_clock_ticks_per_sec) static uint32_t next_print; uint32_t curr = sys_tick_get_32(); /* Print stack usage every n. sec */ if (!next_print || (next_print < curr && (!((curr - next_print) > PRINT_CYCLE)))) { uint32_t new_print; net_analyze_stack("timer fiber", timer_fiber_stack, sizeof(timer_fiber_stack)); new_print = curr + PRINT_CYCLE; if (new_print > curr) { next_print = new_print; } else { /* Overflow */ next_print = PRINT_CYCLE - (0xffffffff - curr); } } } #endif } fiber_sleep(next_wakeup); } } static void init_rx_queue(void) { nano_fifo_init(&netdev.rx_queue); fiber_start(rx_fiber_stack, sizeof(rx_fiber_stack), (nano_fiber_entry_t)net_rx_fiber, 0, 0, 7, 0); } static void init_tx_queue(void) { nano_fifo_init(&netdev.tx_queue); tx_fiber_id = fiber_start(tx_fiber_stack, sizeof(tx_fiber_stack), (nano_fiber_entry_t)net_tx_fiber, 0, 0, 7, 0); } static void init_timer_fiber(void) { timer_fiber_id = fiber_start(timer_fiber_stack, sizeof(timer_fiber_stack), (nano_fiber_entry_t)net_timer_fiber, 0, 0, 7, 0); } void net_timer_check(void) { fiber_wakeup(timer_fiber_id); } int net_set_mac(uint8_t *mac, uint8_t len) { if (!mac) { NET_ERR("MAC address cannot be NULL\n"); return -EINVAL; } if ((len > UIP_LLADDR_LEN) || (len != 6 && len != 8)) { NET_ERR("Wrong ll addr len, len %d, max %d\n", len, UIP_LLADDR_LEN); return -EINVAL; } linkaddr_set_node_addr((linkaddr_t *)mac); NET_DBG("MAC "); PRINTLLADDR((uip_lladdr_t *)&linkaddr_node_addr); PRINTF("\n"); #ifdef CONFIG_NETWORKING_WITH_IPV6 if (!initialized) { memcpy(&uip_lladdr, mac, len); } else { uip_ds6_addr_t *lladdr; uip_ds6_set_lladdr((uip_lladdr_t *)mac); lladdr = uip_ds6_get_link_local(-1); NET_DBG("Tentative link-local IPv6 address "); PRINT6ADDR(&lladdr->ipaddr); PRINTF("\n"); } #else memcpy(&uip_lladdr, mac, len); NET_DBG("IPv4 address "); PRINT6ADDR(&uip_hostaddr); PRINTF("\n"); #endif return 0; } static uint8_t net_tcpip_output(struct net_buf *buf, const uip_lladdr_t *lladdr) { int res; if (!netdev.drv) { return 0; } if (lladdr) { linkaddr_copy(&ip_buf_ll_dest(buf), (const linkaddr_t *)lladdr); } else { linkaddr_copy(&ip_buf_ll_dest(buf), &linkaddr_null); } if (ip_buf_len(buf) == 0) { return 0; } res = netdev.drv->send(buf); if (res < 0) { res = 0; } return (uint8_t)res; } static int network_initialization(void) { /* Initialize and start Contiki uIP stack */ clock_init(); rtimer_init(); ctimer_init(); process_init(); tcpip_set_outputfunc(net_tcpip_output); process_start(&tcpip_process, NULL, NULL); process_start(&simple_udp_process, NULL, NULL); process_start(&etimer_process, NULL, NULL); process_start(&ctimer_process, NULL, NULL); slip_start(); #if CONFIG_15_4_BEACON_SUPPORT && CONFIG_NETWORKING_WITH_15_4_PAN_ID handler_802154_join(CONFIG_NETWORKING_WITH_15_4_PAN_ID, 1); #endif #ifdef CONFIG_NETWORKING_WITH_15_4_PAN_ID NETSTACK_RADIO.set_value(RADIO_PARAM_PAN_ID, IEEE802154_PANID); #endif #ifdef CONFIG_DHCP dhcpc_init(uip_lladdr.addr, sizeof(uip_lladdr.addr)); #endif return 0; } int net_register_driver(struct net_driver *drv) { int r; if (netdev.drv) { return -EALREADY; } if (!drv->open || !drv->send) { return -EINVAL; } r = drv->open(); if (r < 0) { return r; } netdev.drv = drv; return 0; } void net_unregister_driver(struct net_driver *drv) { netdev.drv = NULL; } int net_init(void) { if (initialized) return -EALREADY; initialized = 1; #if UIP_STATISTICS == 1 memset(&uip_stat, 0, sizeof(uip_stat)); #endif /* UIP_STATISTICS == 1 */ net_context_init(); ip_buf_init(); l2_buf_init(); init_tx_queue(); init_rx_queue(); init_timer_fiber(); #if defined(CONFIG_NETWORKING_WITH_15_4) net_driver_15_4_init(); #endif #if defined(CONFIG_NETWORKING_WITH_BT) net_driver_bt_init(); #endif net_driver_slip_init(); net_driver_ethernet_init(); return network_initialization(); }