/**************************************************************************** * net/sixlowpan/sixlowpan_send.c * * Copyright (C) 2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name NuttX nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include "netdev/netdev.h" #include "devif/devif.h" #include "sixlowpan/sixlowpan_internal.h" #ifdef CONFIG_NET_6LOWPAN /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* These are temporary stubs. Something like this would be needed to * monitor the health of a IPv6 neighbor. */ #define neighbor_reachable(dev) #define neighbor_notreachable(dev) /**************************************************************************** * Private Types ****************************************************************************/ /* This is the state data provided to the send interrupt logic. No actions * can be taken until the until we receive the TX poll, then we can call * sixlowpan_queue_frames() with this data strurcture. */ struct sixlowpan_send_s { FAR struct devif_callback_s *s_cb; /* Reference to callback instance */ sem_t s_waitsem; /* Supports waiting for driver events */ int s_result; /* The result of the transfer */ uint16_t s_timeout; /* Send timeout in deciseconds */ systime_t s_time; /* Last send time for determining timeout */ FAR const struct ipv6_hdr_s *s_ipv6hdr; /* IPv6 header, followed by UDP or TCP header. */ FAR const struct sixlowpan_tagaddr_s *s_destmac; /* Destination MAC address */ FAR const void *s_buf; /* Data to send */ size_t s_len; /* Length of data in buf */ }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: send_timeout * * Description: * Check for send timeout. * * Input Parameters: * sinfo - Send state structure reference * * Returned Value: * TRUE:timeout FALSE:no timeout * * Assumptions: * The network is locked * ****************************************************************************/ static inline bool send_timeout(FAR struct sixlowpan_send_s *sinfo) { /* Check for a timeout. Zero means none and, in that case, we will let * the send wait forever. */ if (sinfo->s_timeout != 0) { /* Check if the configured timeout has elapsed */ systime_t timeo_ticks = DSEC2TICK(sinfo->s_timeout); systime_t elapsed = clock_systimer() - sinfo->s_time; if (elapsed >= timeo_ticks) { return true; } } /* No timeout */ return false; } /**************************************************************************** * Name: send_interrupt * * Description: * This function is called from the interrupt level to perform the actual * send operation when polled by the lower, device interfacing layer. * * Parameters: * dev - The structure of the network driver that caused the interrupt * conn - The connection structure associated with the socket * flags - Set of events describing why the callback was invoked * * Returned Value: * None * * Assumptions: * The network is locked. * ****************************************************************************/ static uint16_t send_interrupt(FAR struct net_driver_s *dev, FAR void *pvconn, FAR void *pvpriv, uint16_t flags) { FAR struct sixlowpan_send_s *sinfo = (FAR struct sixlowpan_send_s *)pvpriv; ninfo("flags: %04x: %d\n", flags); #ifdef CONFIG_NET_MULTILINK /* Verify that this is an IEEE802.15.4 network driver. */ if (dev->d_lltype != NET_LL_IEEE802154) { return flags; } #endif #ifdef CONFIG_NET_MULTINIC /* REVISIT: Verify that this is the correct IEEE802.15.4 network driver to * route the outgoing frame(s). Chances are that there is only one * IEEE802.15.4 network driver */ #endif /* Check if the IEEE802.15.4 network driver went down */ if ((flags & NETDEV_DOWN) != 0) { nwarn("WARNING: Device is down\n"); sinfo->s_result = -ENOTCONN; goto end_wait; } /* Check for a poll for TX data. */ if ((flags & WPAN_NEWDATA) == 0) { DEBUGASSERT((flags & WPAN_POLL) != 0); /* Transfer the frame list to the IEEE802.15.4 MAC device */ sinfo->s_result = sixlowpan_queue_frames((FAR struct ieee802154_driver_s *)dev, sinfo->s_ipv6hdr, sinfo->s_buf, sinfo->s_len, sinfo->s_destmac); flags &= ~WPAN_POLL; neighbor_reachable(dev); goto end_wait; } /* Check for a timeout. */ if (send_timeout(sinfo)) { /* Yes.. report the timeout */ nwarn("WARNING: SEND timeout\n"); sinfo->s_result = -ETIMEDOUT; neighbor_notreachable(dev); goto end_wait; } /* Continue waiting */ return flags; end_wait: /* Do not allow any further callbacks */ sinfo->s_cb->flags = 0; sinfo->s_cb->priv = NULL; sinfo->s_cb->event = NULL; /* Wake up the waiting thread */ sem_post(&sinfo->s_waitsem); return flags; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: sixlowpan_send * * Description: * Process an outgoing UDP or TCP packet. Takes an IP packet and formats * it to be sent on an 802.15.4 network using 6lowpan. Called from common * UDP/TCP send logic. * * The payload data is in the caller 'buf' and is of length 'buflen'. * Compressed headers will be added and if necessary the packet is * fragmented. The resulting packet/fragments are submitted to the MAC * via the network driver i_req_data method. * * Input Parameters: * dev - The IEEE802.15.4 MAC network driver interface. * list - Head of callback list for send interrupt * ipv6hdr - IPv6 header followed by TCP or UDP header. * buf - Data to send * len - Length of data to send * destmac - The IEEE802.15.4 MAC address of the destination * timeout - Send timeout in deciseconds * * Returned Value: * Ok is returned on success; Othewise a negated errno value is returned. * This function is expected to fail if the driver is not an IEEE802.15.4 * MAC network driver. In that case, the UDP/TCP will fall back to normal * IPv4/IPv6 formatting. * * Assumptions: * Called with the network locked. * ****************************************************************************/ int sixlowpan_send(FAR struct net_driver_s *dev, FAR struct devif_callback_s **list, FAR const struct ipv6_hdr_s *ipv6hdr, FAR const void *buf, size_t len, FAR const struct sixlowpan_tagaddr_s *destmac, uint16_t timeout) { struct sixlowpan_send_s sinfo; ninfo("len=%lu timeout=%u\n", (unsigned long)len, timeout); /* Initialize the send state structure */ sem_init(&sinfo.s_waitsem, 0, 0); (void)sem_setprotocol(&sinfo.s_waitsem, SEM_PRIO_NONE); sinfo.s_result = -EBUSY; sinfo.s_timeout = timeout; sinfo.s_time = clock_systimer(); sinfo.s_ipv6hdr = ipv6hdr; sinfo.s_destmac = destmac; sinfo.s_buf = buf; sinfo.s_len = len; net_lock(); if (len > 0) { /* Allocate resources to receive a callback. * * The second parameter is NULL meaning that we can get only * device related events, no connect-related events. */ sinfo.s_cb = devif_callback_alloc(dev, list); if (sinfo.s_cb != NULL) { int ret; /* Set up the callback in the connection */ sinfo.s_cb->flags = (NETDEV_DOWN | WPAN_POLL); sinfo.s_cb->priv = (FAR void *)&sinfo; sinfo.s_cb->event = send_interrupt; /* Notify the IEEE802.15.4 MAC that we have data to send. */ netdev_txnotify_dev(dev); /* Wait for the send to complete or an error to occur: NOTES: (1) * net_lockedwait will also terminate if a signal is received, (2) interrupts * may be disabled! They will be re-enabled while the task sleeps and * automatically re-enabled when the task restarts. */ ninfo("Wait for send complete\n"); ret = net_lockedwait(&sinfo.s_waitsem); if (ret < 0) { sinfo.s_result = -get_errno(); } /* Make sure that no further interrupts are processed */ devif_conn_callback_free(dev, sinfo.s_cb, list); } } sem_destroy(&sinfo.s_waitsem); net_unlock(); return (sinfo.s_result < 0 ? sinfo.s_result : len); } #endif /* CONFIG_NET_6LOWPAN */