Newer
Older
/****************************************************************************
* net/ieee802154/ieee802154_sendto.c
*
* Copyright (C) 2017 Gregory Nutt. All rights reserved.
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
* 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>
Gregory Nutt
committed
#include <nuttx/net/radiodev.h>
#include <nuttx/net/net.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 from the interrupt level.
*/
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:
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
* 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)
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
{
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.
Gregory Nutt
committed
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 = iob_alloc(0);
if (iob == NULL)
{
nwarn("WARNING: Failed to allocate IOB\n");
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 */
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 */
}
/****************************************************************************
* 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.
*
* 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,
Gregory Nutt
committed
* 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)
{
Gregory Nutt
committed
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))
{
Gregory Nutt
committed
return -EDESTADDRREQ;
/* Get the device driver that will service this transfer */
radio = ieee802154_find_device(conn, &conn->laddr);
if (radio == NULL)
{
Gregory Nutt
committed
return -ENODEV;
}
/* Set the socket state to sending */
psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_SEND);
/* Perform the send operation */
/* Initialize the state structure. This is done with interrupts
* disabled 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.
*/
(void)nxsem_init(&state.is_sem, 0, 0); /* Doesn't really fail */
(void)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: 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.
*/
ret = net_lockedwait(&state.is_sem);
/* Make sure that no further interrupts are processed */
ieee802154_callback_free(&radio->r_dev, conn, state.is_cb);
net_unlock();
/* Set the socket state to idle */
psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_IDLE);
/* Check for a errors, Errors are signaled by negative errno values
Gregory Nutt
committed
return state.is_sent;
}
/* If net_lockedwait failed, then we were probably reawakened by a signal. In
Jussi Kivilinna
committed
* this case, net_lockedwait will have returned negated errno appropriately.
Gregory Nutt
committed
return ret;
}
/* Return the number of bytes actually sent */
}
#endif /* CONFIG_NET_IEEE802154 */