765 lines
18 KiB
C
765 lines
18 KiB
C
/** @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.
|
|
*/
|
|
|
|
#define DEBUG DEBUG_PRINT
|
|
#include "contiki/ip/uip-debug.h"
|
|
|
|
#include <nanokernel.h>
|
|
#include <toolchain.h>
|
|
#include <sections.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include <net/net_buf.h>
|
|
#include <net/net_core.h>
|
|
#include <net/net_ip.h>
|
|
#include <net/net_socket.h>
|
|
|
|
#include "net_driver_15_4.h"
|
|
#include "net_driver_slip.h"
|
|
|
|
#include "contiki/os/sys/process.h"
|
|
#include "contiki/os/sys/etimer.h"
|
|
#include "contiki/netstack.h"
|
|
#include "contiki/ipv6/uip-ds6.h"
|
|
#include "contiki/ip/simple-udp.h"
|
|
#include "contiki/os/dev/slip.h"
|
|
|
|
/* 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);
|
|
|
|
/* Stacks for the tx & rx fibers.
|
|
* FIXME: stack size needs fine-tuning
|
|
*/
|
|
#define STACKSIZE_UNIT 1024
|
|
static char __noinit rx_fiber_stack[STACKSIZE_UNIT * 1];
|
|
static char __noinit tx_fiber_stack[STACKSIZE_UNIT * 1];
|
|
static char __noinit timer_fiber_stack[STACKSIZE_UNIT * 1];
|
|
|
|
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)
|
|
{
|
|
if (buf->len == 0) {
|
|
return -ENODATA;
|
|
}
|
|
|
|
nano_fifo_put(&netdev.tx_queue, buf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#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
|
|
|
|
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));
|
|
|
|
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 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
|
|
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) = uip_slen(buf) = uip_appdatalen(buf) =
|
|
net_buf_datalen(buf);
|
|
}
|
|
|
|
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 *)&buf->src;
|
|
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),
|
|
buf->data, buf->len,
|
|
&NET_BUF_IP(buf)->destipaddr,
|
|
uip_ntohs(NET_BUF_UDP(buf)->destport));
|
|
if (!ret) {
|
|
NET_DBG("Packet could not be sent properly.\n");
|
|
}
|
|
|
|
#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;
|
|
}
|
|
|
|
/* 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:
|
|
NET_DBG("TCP not yet supported\n");
|
|
return -EINVAL;
|
|
break;
|
|
case IPPROTO_ICMPV6:
|
|
NET_DBG("ICMPv6 not yet supported\n");
|
|
return -EINVAL;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Called by driver when an IP packet has been received */
|
|
int net_recv(struct net_buf *buf)
|
|
{
|
|
if (buf->len == 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.
|
|
*/
|
|
net_buf_put(buf);
|
|
return;
|
|
}
|
|
|
|
uip_appdatalen(buf) = datalen;
|
|
buf->datalen = datalen;
|
|
buf->data = uip_appdata(buf);
|
|
|
|
NET_DBG("packet received buf %p context %p len %d appdatalen %d\n",
|
|
buf, context, buf->len, datalen);
|
|
|
|
nano_fifo_put(net_context_get_queue(context), buf);
|
|
}
|
|
|
|
/* 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_tuple *tuple;
|
|
int ret = 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;
|
|
break;
|
|
case IPPROTO_TCP:
|
|
NET_DBG("TCP not yet supported\n");
|
|
ret = -EINVAL;
|
|
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:
|
|
return nano_fifo_get_wait(rx_queue);
|
|
case TICKS_NONE:
|
|
return nano_fifo_get(rx_queue);
|
|
default:
|
|
#ifdef CONFIG_NANO_TIMEOUTS
|
|
#ifdef CONFIG_MICROKERNEL
|
|
return nano_task_fifo_get_wait_timeout(rx_queue, timeout);
|
|
#else /* CONFIG_MICROKERNEL */
|
|
return nano_fiber_fifo_get_wait_timeout(rx_queue, timeout);
|
|
#endif
|
|
#else /* CONFIG_NANO_TIMEOUTS */
|
|
return nano_fifo_get(rx_queue);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
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.
|
|
*/
|
|
net_buf_put(buf);
|
|
return;
|
|
}
|
|
|
|
queue = net_context_get_queue(context);
|
|
|
|
NET_DBG("packet reply buf %p context %p len %d queue %p\n",
|
|
buf, context, buf->len, queue);
|
|
|
|
/* 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.
|
|
*/
|
|
buf->datalen = uip_len(buf);
|
|
buf->data = uip_appdata(buf);
|
|
|
|
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(buf->context);
|
|
if (!tuple) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (tuple->ip_proto) {
|
|
case IPPROTO_UDP:
|
|
udp = net_context_get_udp_connection(buf->context);
|
|
if (!net_context_get_receiver_registered(buf->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_reply,
|
|
buf->context);
|
|
if (!ret) {
|
|
NET_DBG("UDP connection creation failed\n");
|
|
ret = -ENOENT;
|
|
break;
|
|
}
|
|
net_context_set_receiver_registered(buf->context);
|
|
}
|
|
|
|
/* Remember the original length as the uIP stack might
|
|
* reset the uip_len(buf) value.
|
|
*/
|
|
buf->datalen = uip_len(buf);
|
|
|
|
ret = simple_udp_send(buf, udp, buf->data, buf->len);
|
|
|
|
break;
|
|
case IPPROTO_TCP:
|
|
NET_DBG("TCP not yet supported\n");
|
|
ret = -EINVAL;
|
|
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\n");
|
|
|
|
while (1) {
|
|
struct net_buf *buf;
|
|
uint8_t ret;
|
|
|
|
/* Get next packet from application - wait if necessary */
|
|
buf = nano_fifo_get_wait(&netdev.tx_queue);
|
|
|
|
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) {
|
|
net_buf_put(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);
|
|
|
|
net_buf_put(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\n");
|
|
|
|
while (1) {
|
|
buf = nano_fifo_get_wait(&netdev.rx_queue);
|
|
|
|
/* 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)) {
|
|
net_buf_put(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. At the moment this is done via polling.
|
|
* Max. timeout is set to 60sec so that we wake up at least once a minute.
|
|
*/
|
|
#define DEFAULT_TIMER_WAKEUP (2 * sys_clock_ticks_per_sec)
|
|
#define MAX_TIMER_WAKEUP (60 * sys_clock_ticks_per_sec)
|
|
|
|
static void net_timer_fiber(void)
|
|
{
|
|
clock_time_t next_wakeup;
|
|
|
|
NET_DBG("Starting net timer fiber\n");
|
|
|
|
while (1) {
|
|
/* Run various timers */
|
|
next_wakeup = etimer_request_poll();
|
|
|
|
if (next_wakeup == 0) {
|
|
/* There was no timers, wait a bit */
|
|
next_wakeup = DEFAULT_TIMER_WAKEUP;
|
|
} else {
|
|
if (next_wakeup > MAX_TIMER_WAKEUP) {
|
|
NET_DBG("Too long wakeup %d\n", next_wakeup);
|
|
next_wakeup = MAX_TIMER_WAKEUP;
|
|
}
|
|
if (!(next_wakeup < CLOCK_SECOND * 2)) {
|
|
NET_DBG("Next wakeup %d\n", next_wakeup);
|
|
}
|
|
|
|
#ifdef CONFIG_INIT_STACKS
|
|
{
|
|
static clock_time_t last_print;
|
|
if ((last_print + 10) < clock_seconds()) {
|
|
net_analyze_stack("timer fiber",
|
|
timer_fiber_stack,
|
|
sizeof(timer_fiber_stack));
|
|
last_print = clock_seconds();
|
|
}
|
|
}
|
|
#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);
|
|
|
|
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)
|
|
{
|
|
fiber_start(timer_fiber_stack, sizeof(timer_fiber_stack),
|
|
(nano_fiber_entry_t) net_timer_fiber, 0, 0, 7, 0);
|
|
}
|
|
|
|
int net_set_mac(uint8_t *mac, uint8_t len)
|
|
{
|
|
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
|
|
{
|
|
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");
|
|
|
|
lladdr->state = ADDR_AUTOCONF;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static uint8_t net_tcpip_output(struct net_buf *buf, const uip_lladdr_t *lladdr)
|
|
{
|
|
if (!netdev.drv) {
|
|
return 0;
|
|
}
|
|
|
|
if(lladdr == NULL) {
|
|
linkaddr_copy(&buf->dest, &linkaddr_null);
|
|
} else {
|
|
linkaddr_copy(&buf->dest, (const linkaddr_t *)lladdr);
|
|
}
|
|
|
|
return netdev.drv->send(buf);
|
|
}
|
|
|
|
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);
|
|
process_start(&simple_udp_process, NULL);
|
|
process_start(&etimer_process, NULL);
|
|
|
|
slip_start();
|
|
|
|
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)
|
|
{
|
|
static uint8_t initialized;
|
|
|
|
if (initialized)
|
|
return -EALREADY;
|
|
|
|
initialized = 1;
|
|
|
|
#if UIP_STATISTICS == 1
|
|
memset(&uip_stat, 0, sizeof(uip_stat));
|
|
#endif /* UIP_STATISTICS == 1 */
|
|
|
|
net_context_init();
|
|
net_buf_init();
|
|
init_tx_queue();
|
|
init_rx_queue();
|
|
init_timer_fiber();
|
|
|
|
#if defined (CONFIG_NETWORKING_WITH_15_4)
|
|
net_driver_15_4_init();
|
|
#endif
|
|
|
|
net_driver_slip_init();
|
|
|
|
return network_initialization();
|
|
}
|