6a3c2aded6
* Simplify EINTR/ECANCEL error handling 1. Add semaphore uninterruptible wait function 2 .Replace semaphore wait loop with a single uninterruptible wait 3. Replace all sem_xxx to nxsem_xxx * Unify the void cast usage 1. Remove void cast for function because many place ignore the returned value witout cast 2. Replace void cast for variable with UNUSED macro
557 lines
16 KiB
C
557 lines
16 KiB
C
/****************************************************************************
|
|
* net/ieee802154/ieee802154_sendto.c
|
|
*
|
|
* Copyright (C) 2017-2018 Gregory Nutt. All rights reserved.
|
|
* Author: Gregory Nutt <gnutt@nuttx.org>
|
|
*
|
|
* 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 <nuttx/config.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <assert.h>
|
|
#include <debug.h>
|
|
|
|
#include <arch/irq.h>
|
|
|
|
#include <nuttx/clock.h>
|
|
#include <nuttx/semaphore.h>
|
|
#include <nuttx/mm/iob.h>
|
|
#include <nuttx/net/radiodev.h>
|
|
#include <nuttx/net/net.h>
|
|
|
|
#include "utils/utils.h"
|
|
#include "netdev/netdev.h"
|
|
#include "devif/devif.h"
|
|
#include "socket/socket.h"
|
|
#include "ieee802154/ieee802154.h"
|
|
|
|
#ifdef CONFIG_NET_IEEE802154
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
/* This structure holds the state of the send operation until it can be
|
|
* operated upon by the event handler.
|
|
*/
|
|
|
|
struct ieee802154_sendto_s
|
|
{
|
|
FAR struct socket *is_sock; /* Points to the parent socket structure */
|
|
FAR struct devif_callback_s *is_cb; /* Reference to callback instance */
|
|
struct ieee802154_saddr_s is_destaddr; /* Frame destination address */
|
|
sem_t is_sem; /* Used to wake up the waiting thread */
|
|
FAR const uint8_t *is_buffer; /* User buffer of data to send */
|
|
size_t is_buflen; /* Number of bytes in the is_buffer */
|
|
ssize_t is_sent; /* The number of bytes sent (or error) */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: ieee802154_anyaddrnull
|
|
*
|
|
* Description:
|
|
* If the destination address is all zero in the MAC header buf, then it is
|
|
* broadcast on the 802.15.4 network.
|
|
*
|
|
* Input Parameters:
|
|
* addr - The address to check
|
|
* addrlen - The length of the address in bytes
|
|
*
|
|
* Returned Value:
|
|
* True if the address is all zero.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static bool ieee802154_anyaddrnull(FAR const uint8_t *addr, uint8_t addrlen)
|
|
{
|
|
while (addrlen-- > 0)
|
|
{
|
|
if (addr[addrlen] != 0x00)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ieee802154_saddrnull
|
|
*
|
|
* Description:
|
|
* If the destination address is all zero in the MAC header buf, then it is
|
|
* broadcast on the 802.15.4 network.
|
|
*
|
|
* Input Parameters:
|
|
* eaddr - The short address to check
|
|
*
|
|
* Returned Value:
|
|
* The address length associated with the address mode.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline bool ieee802154_saddrnull(FAR const uint8_t *saddr)
|
|
{
|
|
return ieee802154_anyaddrnull(saddr, IEEE802154_SADDRSIZE);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ieee802154_eaddrnull
|
|
*
|
|
* Description:
|
|
* If the destination address is all zero in the MAC header buf, then it is
|
|
* broadcast on the 802.15.4 network.
|
|
*
|
|
* Input Parameters:
|
|
* eaddr - The extended address to check
|
|
*
|
|
* Returned Value:
|
|
* The address length associated with the address mode.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline bool ieee802154_eaddrnull(FAR const uint8_t *eaddr)
|
|
{
|
|
return ieee802154_anyaddrnull(eaddr, IEEE802154_EADDRSIZE);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ieee802154_meta_data
|
|
*
|
|
* Description:
|
|
* Based on the collected attributes and addresses, construct the MAC meta
|
|
* data structure that we need to interface with the IEEE 802.15.4 MAC.
|
|
*
|
|
* Input Parameters:
|
|
* radio - Radio network driver state instance.
|
|
* pstate - Send state structure instance
|
|
* meta - Location to return the corresponding meta data.
|
|
* paylen - The size of the data payload to be sent.
|
|
*
|
|
* Returned Value:
|
|
* Ok is returned on success; Othewise a negated errno value is returned.
|
|
*
|
|
* Assumptions:
|
|
* Called with the network locked.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void ieee802154_meta_data(FAR struct radio_driver_s *radio,
|
|
FAR struct ieee802154_sendto_s *pstate,
|
|
FAR struct ieee802154_frame_meta_s *meta)
|
|
{
|
|
FAR struct ieee802154_saddr_s *destaddr;
|
|
FAR struct ieee802154_saddr_s *srcaddr;
|
|
FAR struct ieee802154_conn_s *conn;
|
|
FAR struct socket *psock;
|
|
bool rcvrnull;
|
|
|
|
DEBUGASSERT(radio != NULL && pstate != NULL && pstate->is_sock != NULL &&
|
|
meta != NULL);
|
|
|
|
psock = pstate->is_sock;
|
|
DEBUGASSERT(psock->s_conn != NULL);
|
|
|
|
conn = (FAR struct ieee802154_conn_s *)psock->s_conn;
|
|
srcaddr = &conn->laddr;
|
|
destaddr = &pstate->is_destaddr;
|
|
|
|
DEBUGASSERT(srcaddr->s_mode != IEEE802154_ADDRMODE_NONE &&
|
|
destaddr->s_mode != IEEE802154_ADDRMODE_NONE);
|
|
|
|
/* Initialize all settings to all zero */
|
|
|
|
memset(meta, 0, sizeof(struct ieee802154_frame_meta_s));
|
|
|
|
/* Source address mode */
|
|
|
|
meta->srcmode = srcaddr->s_mode;
|
|
|
|
/* Check for a broadcast destination address (all zero) */
|
|
|
|
if (destaddr->s_mode == IEEE802154_ADDRMODE_EXTENDED)
|
|
{
|
|
/* Extended destination address mode */
|
|
|
|
rcvrnull = ieee802154_eaddrnull(destaddr->s_eaddr);
|
|
}
|
|
else
|
|
{
|
|
/* Short destination address mode */
|
|
|
|
rcvrnull = ieee802154_saddrnull(destaddr->s_saddr);
|
|
}
|
|
|
|
if (rcvrnull)
|
|
{
|
|
meta->flags.ackreq = TRUE;
|
|
}
|
|
|
|
/* Destination address.
|
|
*
|
|
* If the output address is NULL, then it is broadcast on the 802.15.4
|
|
* network.
|
|
*/
|
|
|
|
if (rcvrnull)
|
|
{
|
|
/* Broadcast requires short address mode. */
|
|
|
|
meta->destaddr.mode = IEEE802154_ADDRMODE_SHORT;
|
|
IEEE802154_PANIDCOPY(meta->destaddr.panid, destaddr->s_panid);
|
|
meta->destaddr.saddr[0] = 0xff;
|
|
meta->destaddr.saddr[1] = 0xff;
|
|
memset(meta->destaddr.eaddr, 0, IEEE802154_EADDRSIZE);
|
|
}
|
|
else
|
|
{
|
|
/* Destination address. */
|
|
|
|
meta->destaddr.mode = destaddr->s_mode;
|
|
IEEE802154_PANIDCOPY(meta->destaddr.panid, destaddr->s_panid);
|
|
|
|
if (destaddr->s_mode == IEEE802154_ADDRMODE_SHORT)
|
|
{
|
|
IEEE802154_SADDRCOPY(meta->destaddr.saddr, destaddr->s_eaddr);
|
|
memset(meta->destaddr.eaddr, 0, IEEE802154_EADDRSIZE);
|
|
}
|
|
else
|
|
{
|
|
IEEE802154_EADDRCOPY(meta->destaddr.eaddr, destaddr->s_eaddr);
|
|
memset(meta->destaddr.saddr, 0, IEEE802154_SADDRSIZE);
|
|
}
|
|
}
|
|
|
|
/* Handle associated with MSDU. Will increment once per packet, not
|
|
* necesarily per frame: The same MSDU handle will be used for each
|
|
* fragment of a disassembled packet.
|
|
*/
|
|
|
|
meta->handle = radio->r_msdu_handle++;
|
|
|
|
#ifdef CONFIG_IEEE802154_SECURITY
|
|
# warning CONFIG_IEEE802154_SECURITY not yet supported
|
|
#endif
|
|
|
|
#ifdef CONFIG_IEEE802154_UWB
|
|
# warning CONFIG_IEEE802154_UWB not yet supported
|
|
#endif
|
|
|
|
/* Ranging left zero */
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: ieee802154_sendto_eventhandler
|
|
****************************************************************************/
|
|
|
|
static uint16_t ieee802154_sendto_eventhandler(FAR struct net_driver_s *dev,
|
|
FAR void *pvconn,
|
|
FAR void *pvpriv,
|
|
uint16_t flags)
|
|
{
|
|
FAR struct radio_driver_s *radio;
|
|
FAR struct ieee802154_sendto_s *pstate;
|
|
struct ieee802154_frame_meta_s meta;
|
|
FAR struct iob_s *iob;
|
|
int hdrlen;
|
|
int ret;
|
|
|
|
DEBUGASSERT(pvpriv != NULL && dev != NULL && pvconn != NULL);
|
|
|
|
/* Ignore polls from non IEEE 802.15.4 network drivers */
|
|
|
|
if (dev->d_lltype != NET_LL_IEEE802154)
|
|
{
|
|
return flags;
|
|
}
|
|
|
|
/* Make sure that this is the driver to which the socket is connected. */
|
|
|
|
#warning Missing logic
|
|
|
|
pstate = (FAR struct ieee802154_sendto_s *)pvpriv;
|
|
radio = (FAR struct radio_driver_s *)dev;
|
|
|
|
ninfo("flags: %04x sent: %d\n", flags, pstate->is_sent);
|
|
|
|
if (pstate != NULL && (flags & IEEE802154_POLL) != 0)
|
|
{
|
|
/* Initialize the meta data */
|
|
|
|
ieee802154_meta_data(radio, pstate, &meta);
|
|
|
|
/* Get the IEEE 802.15.4 MAC header length */
|
|
|
|
hdrlen = radio->r_get_mhrlen(radio, &meta);
|
|
if (hdrlen < 0)
|
|
{
|
|
nerr("ERROR: Failed to get header length: %d\n", hdrlen);
|
|
ret = hdrlen;
|
|
goto errout;
|
|
}
|
|
|
|
/* Verify that the user buffer can fit within the frame with this
|
|
* MAC header.
|
|
*/
|
|
|
|
DEBUGASSERT(CONFIG_NET_IEEE802154_FRAMELEN <= CONFIG_IOB_BUFSIZE);
|
|
if (pstate->is_buflen + hdrlen > IEEE802154_FRAMELEN)
|
|
{
|
|
nerr("ERROR: User buffer will not fit into the frame: %u > %u\n",
|
|
(unsigned int)(pstate->is_buflen + hdrlen),
|
|
(unsigned int)CONFIG_IOB_BUFSIZE);
|
|
ret = -E2BIG;
|
|
goto errout;
|
|
}
|
|
|
|
/* Allocate an IOB to hold the frame data */
|
|
|
|
iob = net_ioballoc(false, IOBUSER_NET_SOCK_IEEE802154);
|
|
if (iob == NULL)
|
|
{
|
|
nwarn("WARNING: Failed to allocate IOB\n");
|
|
return flags;
|
|
}
|
|
|
|
/* Initialize the IOB */
|
|
|
|
iob->io_offset = hdrlen;
|
|
iob->io_len = pstate->is_buflen + hdrlen;
|
|
iob->io_pktlen = pstate->is_buflen + hdrlen;
|
|
|
|
/* Copy the user data into the IOB */
|
|
|
|
memcpy(&iob->io_data[hdrlen], pstate->is_buffer, pstate->is_buflen);
|
|
|
|
/* And submit the IOB to the network driver */
|
|
|
|
ret = radio->r_req_data(radio, &meta, iob);
|
|
if (ret < 0)
|
|
{
|
|
nerr("ERROR: r_req_data() failed: %d\n", ret);
|
|
goto errout;
|
|
}
|
|
|
|
/* Save the successful result */
|
|
|
|
pstate->is_sent = pstate->is_buflen;
|
|
|
|
/* Don't allow any further call backs. */
|
|
|
|
pstate->is_cb->flags = 0;
|
|
pstate->is_cb->priv = NULL;
|
|
pstate->is_cb->event = NULL;
|
|
|
|
/* Wake up the waiting thread */
|
|
|
|
nxsem_post(&pstate->is_sem);
|
|
}
|
|
|
|
return flags;
|
|
|
|
errout:
|
|
|
|
/* Don't allow any further call backs. */
|
|
|
|
pstate->is_cb->flags = 0;
|
|
pstate->is_cb->priv = NULL;
|
|
pstate->is_cb->event = NULL;
|
|
pstate->is_sent = ret;
|
|
|
|
/* Wake up the waiting thread */
|
|
|
|
nxsem_post(&pstate->is_sem);
|
|
return flags;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: psock_ieee802154_sendto
|
|
*
|
|
* Description:
|
|
* If sendto() is used on a connection-mode (SOCK_STREAM, SOCK_SEQPACKET)
|
|
* socket, the parameters to and 'tolen' are ignored (and the error EISCONN
|
|
* may be returned when they are not NULL and 0), and the error ENOTCONN is
|
|
* returned when the socket was not actually connected.
|
|
*
|
|
* Input Parameters:
|
|
* psock A pointer to a NuttX-specific, internal socket structure
|
|
* buf Data to send
|
|
* len Length of data to send
|
|
* flags Send flags
|
|
* to Address of recipient
|
|
* tolen The length of the address structure
|
|
*
|
|
* Returned Value:
|
|
* On success, returns the number of characters sent. On error,
|
|
* a negated errno value is retruend. See sendto() for the complete list
|
|
* of return values.
|
|
*
|
|
****************************************************************************/
|
|
|
|
ssize_t psock_ieee802154_sendto(FAR struct socket *psock, FAR const void *buf,
|
|
size_t len, int flags,
|
|
FAR const struct sockaddr *to, socklen_t tolen)
|
|
{
|
|
FAR struct sockaddr_ieee802154_s *destaddr;
|
|
FAR struct radio_driver_s *radio;
|
|
FAR struct ieee802154_conn_s *conn;
|
|
struct ieee802154_sendto_s state;
|
|
int ret = OK;
|
|
|
|
/* Verify that the sockfd corresponds to valid, allocated socket */
|
|
|
|
if (psock == NULL || psock->s_crefs <= 0)
|
|
{
|
|
return -EBADF;
|
|
}
|
|
|
|
conn = (FAR struct ieee802154_conn_s *)psock->s_conn;
|
|
DEBUGASSERT(conn != NULL);
|
|
|
|
/* Verify that the address is large enough to be a valid PF_IEEE802154
|
|
* address.
|
|
*/
|
|
|
|
if (tolen < sizeof(struct ieee802154_saddr_s))
|
|
{
|
|
return -EDESTADDRREQ;
|
|
}
|
|
|
|
/* Get the device driver that will service this transfer */
|
|
|
|
radio = ieee802154_find_device(conn, &conn->laddr);
|
|
if (radio == NULL)
|
|
{
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* Perform the send operation */
|
|
|
|
/* Initialize the state structure. This is done with the network
|
|
* locked because we don't want anything to happen until we are
|
|
* ready.
|
|
*/
|
|
|
|
net_lock();
|
|
memset(&state, 0, sizeof(struct ieee802154_sendto_s));
|
|
|
|
/* This semaphore is used for signaling and, hence, should not have
|
|
* priority inheritance enabled.
|
|
*/
|
|
|
|
nxsem_init(&state.is_sem, 0, 0); /* Doesn't really fail */
|
|
nxsem_setprotocol(&state.is_sem, SEM_PRIO_NONE);
|
|
|
|
state.is_sock = psock; /* Socket descriptor to use */
|
|
state.is_buflen = len; /* Number of bytes to send */
|
|
state.is_buffer = buf; /* Buffer to send from */
|
|
|
|
/* Copy the destination address */
|
|
|
|
destaddr = (FAR struct sockaddr_ieee802154_s *)to;
|
|
memcpy(&state.is_destaddr, &destaddr->sa_addr,
|
|
sizeof(struct ieee802154_saddr_s));
|
|
|
|
if (len > 0)
|
|
{
|
|
/* Allocate resource to receive a callback */
|
|
|
|
state.is_cb = ieee802154_callback_alloc(&radio->r_dev, conn);
|
|
if (state.is_cb)
|
|
{
|
|
/* Set up the callback in the connection */
|
|
|
|
state.is_cb->flags = PKT_POLL;
|
|
state.is_cb->priv = (FAR void *)&state;
|
|
state.is_cb->event = ieee802154_sendto_eventhandler;
|
|
|
|
/* Notify the device driver that new TX data is available. */
|
|
|
|
netdev_txnotify_dev(&radio->r_dev);
|
|
|
|
/* Wait for the send to complete or an error to occur.
|
|
* net_lockedwait will also terminate if a signal is received.
|
|
*/
|
|
|
|
ret = net_lockedwait(&state.is_sem);
|
|
|
|
/* Make sure that no further events are processed */
|
|
|
|
ieee802154_callback_free(&radio->r_dev, conn, state.is_cb);
|
|
}
|
|
}
|
|
|
|
nxsem_destroy(&state.is_sem);
|
|
net_unlock();
|
|
|
|
/* Check for a errors, Errors are signaled by negative errno values
|
|
* for the send length
|
|
*/
|
|
|
|
if (state.is_sent < 0)
|
|
{
|
|
return state.is_sent;
|
|
}
|
|
|
|
/* If net_lockedwait failed, then we were probably reawakened by a signal. In
|
|
* this case, net_lockedwait will have returned negated errno appropriately.
|
|
*/
|
|
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Return the number of bytes actually sent */
|
|
|
|
return state.is_sent;
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IEEE802154 */
|