2016-06-16 16:00:11 +02:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2016 Intel Corporation.
|
|
|
|
*
|
2017-01-18 17:01:01 -08:00
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
2016-06-16 16:00:11 +02:00
|
|
|
*/
|
|
|
|
|
2016-12-15 13:55:01 +01:00
|
|
|
#if defined(CONFIG_NET_DEBUG_L2_IEEE802154)
|
2016-06-16 16:00:11 +02:00
|
|
|
#define SYS_LOG_DOMAIN "net/ieee802154"
|
2016-12-15 13:55:01 +01:00
|
|
|
#define NET_LOG_ENABLED 1
|
2016-06-16 16:00:11 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <net/net_core.h>
|
|
|
|
#include <net/net_l2.h>
|
|
|
|
#include <net/net_if.h>
|
|
|
|
|
2016-11-08 12:33:53 +01:00
|
|
|
#include "ipv6.h"
|
|
|
|
|
2016-06-16 16:00:11 +02:00
|
|
|
#include <errno.h>
|
|
|
|
|
2016-07-07 14:11:06 +02:00
|
|
|
#ifdef CONFIG_NET_6LO
|
2016-11-16 09:39:31 +01:00
|
|
|
#ifdef CONFIG_NET_L2_IEEE802154_FRAGMENT
|
|
|
|
#include "ieee802154_fragment.h"
|
|
|
|
#endif
|
2016-07-07 14:11:06 +02:00
|
|
|
#include <6lo.h>
|
|
|
|
#endif /* CONFIG_NET_6LO */
|
|
|
|
|
2016-06-16 16:00:11 +02:00
|
|
|
#include <net/ieee802154_radio.h>
|
|
|
|
|
|
|
|
#include "ieee802154_frame.h"
|
2016-10-07 10:53:22 +02:00
|
|
|
#include "ieee802154_mgmt.h"
|
2016-06-16 16:00:11 +02:00
|
|
|
|
2016-07-12 16:09:29 +02:00
|
|
|
#if 0
|
|
|
|
|
2017-01-23 13:05:40 +02:00
|
|
|
#include <misc/printk.h>
|
2016-07-12 16:09:29 +02:00
|
|
|
|
2016-10-26 15:37:05 +02:00
|
|
|
static inline void hexdump(uint8_t *pkt, uint16_t length, uint8_t reserve)
|
2016-07-12 16:09:29 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < length;) {
|
|
|
|
int j;
|
|
|
|
|
2017-01-23 13:05:40 +02:00
|
|
|
printk("\t");
|
2016-07-12 16:09:29 +02:00
|
|
|
|
|
|
|
for (j = 0; j < 10 && i < length; j++, i++) {
|
2016-10-26 15:37:05 +02:00
|
|
|
#if defined(CONFIG_SYS_LOG_SHOW_COLOR)
|
2016-11-14 15:35:14 +01:00
|
|
|
if (i < reserve && reserve) {
|
2017-01-23 13:05:40 +02:00
|
|
|
printk(SYS_LOG_COLOR_YELLOW);
|
2016-10-26 15:37:05 +02:00
|
|
|
} else {
|
2017-01-23 13:05:40 +02:00
|
|
|
printk(SYS_LOG_COLOR_OFF);
|
2016-10-26 15:37:05 +02:00
|
|
|
}
|
|
|
|
#endif
|
2017-01-23 13:05:40 +02:00
|
|
|
printk("%02x ", *pkt++);
|
2016-07-12 16:09:29 +02:00
|
|
|
}
|
|
|
|
|
2016-10-26 15:37:05 +02:00
|
|
|
#if defined(CONFIG_SYS_LOG_SHOW_COLOR)
|
|
|
|
if (i < reserve) {
|
2017-01-23 13:05:40 +02:00
|
|
|
printk(SYS_LOG_COLOR_OFF);
|
2016-10-26 15:37:05 +02:00
|
|
|
}
|
|
|
|
#endif
|
2017-01-23 13:05:40 +02:00
|
|
|
printk("\n");
|
2016-07-12 16:09:29 +02:00
|
|
|
}
|
|
|
|
}
|
2016-10-26 15:37:05 +02:00
|
|
|
|
|
|
|
static void pkt_hexdump(struct net_buf *buf, bool each_frag_reserve)
|
|
|
|
{
|
2016-11-15 08:49:10 +01:00
|
|
|
uint16_t reserve = each_frag_reserve ? net_nbuf_ll_reserve(buf) : 0;
|
2016-10-26 15:37:05 +02:00
|
|
|
struct net_buf *frag;
|
|
|
|
|
2017-01-23 13:05:40 +02:00
|
|
|
printk("IEEE 802.15.4 packet content:\n");
|
2016-10-26 15:37:05 +02:00
|
|
|
|
|
|
|
frag = buf->frags;
|
|
|
|
while (frag) {
|
|
|
|
hexdump(each_frag_reserve ?
|
2016-11-15 08:49:10 +01:00
|
|
|
frag->data - reserve : frag->data,
|
2016-10-26 15:37:05 +02:00
|
|
|
frag->len + reserve, reserve);
|
|
|
|
|
|
|
|
frag = frag->frags;
|
|
|
|
}
|
|
|
|
}
|
2016-07-12 16:09:29 +02:00
|
|
|
#else
|
|
|
|
#define pkt_hexdump(...)
|
|
|
|
#endif
|
|
|
|
|
2016-06-28 15:05:07 +02:00
|
|
|
#ifdef CONFIG_NET_L2_IEEE802154_ACK_REPLY
|
|
|
|
static inline void ieee802154_acknowledge(struct net_if *iface,
|
|
|
|
struct ieee802154_mpdu *mpdu)
|
|
|
|
{
|
|
|
|
struct net_buf *buf, *frag;
|
|
|
|
|
|
|
|
if (!mpdu->mhr.fs->fc.ar) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = net_nbuf_get_reserve_tx(0);
|
|
|
|
if (!buf) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
frag = net_nbuf_get_reserve_data(IEEE802154_ACK_PKT_LENGTH);
|
|
|
|
|
|
|
|
net_buf_frag_insert(buf, frag);
|
|
|
|
net_nbuf_set_ll_reserve(buf, net_buf_headroom(frag));
|
|
|
|
|
|
|
|
if (ieee802154_create_ack_frame(iface, buf, mpdu->mhr.fs->sequence)) {
|
2016-12-20 10:29:52 +00:00
|
|
|
const struct ieee802154_radio_api *radio =
|
|
|
|
iface->dev->driver_api;
|
2016-06-28 15:05:07 +02:00
|
|
|
|
|
|
|
net_buf_add(frag, IEEE802154_ACK_PKT_LENGTH);
|
|
|
|
|
net/ieee802154: Modify radio TX function signature
The cause for this change is TCP. Until now, the radio strategy driver
(ALOHA or CSMA) was providing the actual nbuf, and not the buffer
fragment, counting on the fact that the loop was using
net_buf_frag_del() which made so, iteration after iteration, buffer
framgent to be always buf->frags. The problem with this logic is loosing
the fragments that might be still referenced by TCP, in case the whole
buffer did not make it so TCP can retry later and so on.
Instead, TX now takes the nbuf and the actual frag to send. It could
have been working with just a pointer on the data, and the whole length
of the frame. But it has been avoided due to possible future devices,
that will be smarter and run CSMA directly in the hw, thus it will
require to access the whole buffer list through the nbuf.
Change-Id: I8d77b1e13b648c0ec3645cb2d55d1910d00381ea
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
2017-01-26 14:31:30 +01:00
|
|
|
radio->tx(iface->dev, buf, frag);
|
2016-06-28 15:05:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
net_nbuf_unref(buf);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define ieee802154_acknowledge(...)
|
|
|
|
#endif /* CONFIG_NET_L2_IEEE802154_ACK_REPLY */
|
|
|
|
|
2016-08-10 15:39:01 +02:00
|
|
|
static inline void set_buf_ll_addr(struct net_linkaddr *addr, bool comp,
|
|
|
|
enum ieee802154_addressing_mode mode,
|
|
|
|
struct ieee802154_address_field *ll)
|
|
|
|
{
|
|
|
|
if (mode == IEEE802154_ADDR_MODE_NONE) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mode == IEEE802154_ADDR_MODE_EXTENDED) {
|
|
|
|
addr->len = IEEE802154_EXT_ADDR_LENGTH;
|
|
|
|
|
|
|
|
if (comp) {
|
|
|
|
addr->addr = ll->comp.addr.ext_addr;
|
|
|
|
} else {
|
|
|
|
addr->addr = ll->plain.addr.ext_addr;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* ToDo: Handle short address (lookup known nbr, ...) */
|
|
|
|
addr->len = 0;
|
|
|
|
addr->addr = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-07 14:11:06 +02:00
|
|
|
#ifdef CONFIG_NET_6LO
|
2016-10-25 09:59:33 +02:00
|
|
|
static inline
|
|
|
|
enum net_verdict ieee802154_manage_recv_buffer(struct net_if *iface,
|
|
|
|
struct net_buf *buf)
|
|
|
|
{
|
|
|
|
enum net_verdict verdict = NET_CONTINUE;
|
2016-07-07 14:11:06 +02:00
|
|
|
uint32_t src;
|
|
|
|
uint32_t dst;
|
2016-10-25 09:59:33 +02:00
|
|
|
|
2016-10-31 15:02:47 +02:00
|
|
|
/* Upper IP stack expects the link layer address to be in
|
|
|
|
* big endian format so we must swap it here.
|
|
|
|
*/
|
|
|
|
if (net_nbuf_ll_src(buf)->addr &&
|
|
|
|
net_nbuf_ll_src(buf)->len == IEEE802154_EXT_ADDR_LENGTH) {
|
|
|
|
sys_mem_swap(net_nbuf_ll_src(buf)->addr,
|
|
|
|
net_nbuf_ll_src(buf)->len);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (net_nbuf_ll_dst(buf)->addr &&
|
|
|
|
net_nbuf_ll_dst(buf)->len == IEEE802154_EXT_ADDR_LENGTH) {
|
|
|
|
sys_mem_swap(net_nbuf_ll_dst(buf)->addr,
|
|
|
|
net_nbuf_ll_dst(buf)->len);
|
|
|
|
}
|
|
|
|
|
2016-10-25 09:59:33 +02:00
|
|
|
/** Uncompress will drop the current fragment. Buf ll src/dst address
|
|
|
|
* will then be wrong and must be updated according to the new fragment.
|
|
|
|
*/
|
|
|
|
src = net_nbuf_ll_src(buf)->addr ?
|
|
|
|
net_nbuf_ll_src(buf)->addr - net_nbuf_ll(buf) : 0;
|
|
|
|
dst = net_nbuf_ll_dst(buf)->addr ?
|
|
|
|
net_nbuf_ll_dst(buf)->addr - net_nbuf_ll(buf) : 0;
|
|
|
|
|
2016-11-16 09:39:31 +01:00
|
|
|
#ifdef CONFIG_NET_L2_IEEE802154_FRAGMENT
|
2016-10-25 09:59:33 +02:00
|
|
|
verdict = ieee802154_reassemble(buf);
|
|
|
|
if (verdict == NET_DROP) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (!net_6lo_uncompress(buf)) {
|
|
|
|
NET_DBG("Packet decompression failed");
|
|
|
|
verdict = NET_DROP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
net_nbuf_ll_src(buf)->addr = src ? net_nbuf_ll(buf) + src : NULL;
|
|
|
|
net_nbuf_ll_dst(buf)->addr = dst ? net_nbuf_ll(buf) + dst : NULL;
|
|
|
|
|
|
|
|
pkt_hexdump(buf, false);
|
|
|
|
out:
|
|
|
|
return verdict;
|
|
|
|
}
|
2016-10-26 14:56:47 +02:00
|
|
|
|
|
|
|
static inline bool ieee802154_manage_send_buffer(struct net_if *iface,
|
|
|
|
struct net_buf *buf)
|
|
|
|
{
|
|
|
|
bool ret;
|
|
|
|
|
2016-11-15 08:49:10 +01:00
|
|
|
pkt_hexdump(buf, false);
|
|
|
|
|
2016-11-16 09:39:31 +01:00
|
|
|
#ifdef CONFIG_NET_L2_IEEE802154_FRAGMENT
|
2016-10-26 14:56:47 +02:00
|
|
|
ret = net_6lo_compress(buf, true, ieee802154_fragment);
|
|
|
|
#else
|
|
|
|
ret = net_6lo_compress(buf, true, NULL);
|
|
|
|
#endif
|
2016-10-31 15:02:47 +02:00
|
|
|
|
2016-11-14 15:35:14 +01:00
|
|
|
pkt_hexdump(buf, false);
|
2016-10-26 14:56:47 +02:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-10-25 09:59:33 +02:00
|
|
|
#else /* CONFIG_NET_6LO */
|
|
|
|
|
|
|
|
#define ieee802154_manage_recv_buffer(...) NET_CONTINUE
|
2016-11-07 18:23:54 -02:00
|
|
|
#define ieee802154_manage_send_buffer(...) true
|
2016-10-25 09:59:33 +02:00
|
|
|
|
2016-07-07 14:11:06 +02:00
|
|
|
#endif /* CONFIG_NET_6LO */
|
2016-06-16 16:00:11 +02:00
|
|
|
|
2016-10-25 09:59:33 +02:00
|
|
|
static enum net_verdict ieee802154_recv(struct net_if *iface,
|
|
|
|
struct net_buf *buf)
|
|
|
|
{
|
|
|
|
struct ieee802154_mpdu mpdu;
|
|
|
|
|
2016-06-16 16:00:11 +02:00
|
|
|
if (!ieee802154_validate_frame(net_nbuf_ll(buf),
|
|
|
|
net_buf_frags_len(buf), &mpdu)) {
|
|
|
|
return NET_DROP;
|
|
|
|
}
|
|
|
|
|
2016-10-07 10:53:22 +02:00
|
|
|
if (mpdu.mhr.fs->fc.frame_type == IEEE802154_FRAME_TYPE_BEACON) {
|
|
|
|
return ieee802154_handle_beacon(iface, &mpdu);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ieee802154_is_scanning(iface)) {
|
|
|
|
return NET_DROP;
|
|
|
|
}
|
|
|
|
|
2016-10-19 17:08:09 +02:00
|
|
|
if (mpdu.mhr.fs->fc.frame_type == IEEE802154_FRAME_TYPE_MAC_COMMAND) {
|
|
|
|
return ieee802154_handle_mac_command(iface, &mpdu);
|
2016-06-16 16:00:11 +02:00
|
|
|
}
|
|
|
|
|
2016-10-19 17:08:09 +02:00
|
|
|
/* At this point the frame has to be a DATA one */
|
|
|
|
|
2016-08-10 15:39:01 +02:00
|
|
|
ieee802154_acknowledge(iface, &mpdu);
|
|
|
|
|
2016-06-16 16:00:11 +02:00
|
|
|
net_nbuf_set_ll_reserve(buf, mpdu.payload - (void *)net_nbuf_ll(buf));
|
|
|
|
net_buf_pull(buf->frags, net_nbuf_ll_reserve(buf));
|
|
|
|
|
2016-08-10 15:39:01 +02:00
|
|
|
set_buf_ll_addr(net_nbuf_ll_src(buf), mpdu.mhr.fs->fc.pan_id_comp,
|
|
|
|
mpdu.mhr.fs->fc.src_addr_mode, mpdu.mhr.src_addr);
|
2016-06-16 16:00:11 +02:00
|
|
|
|
2016-08-10 15:39:01 +02:00
|
|
|
set_buf_ll_addr(net_nbuf_ll_dst(buf), false,
|
|
|
|
mpdu.mhr.fs->fc.dst_addr_mode, mpdu.mhr.dst_addr);
|
2016-06-16 16:00:11 +02:00
|
|
|
|
2016-10-26 15:37:05 +02:00
|
|
|
pkt_hexdump(buf, true);
|
2016-07-12 16:09:29 +02:00
|
|
|
|
2016-10-25 09:59:33 +02:00
|
|
|
return ieee802154_manage_recv_buffer(iface, buf);
|
2016-06-16 16:00:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static enum net_verdict ieee802154_send(struct net_if *iface,
|
|
|
|
struct net_buf *buf)
|
|
|
|
{
|
2016-10-26 10:58:45 +02:00
|
|
|
uint8_t reserved_space = net_nbuf_ll_reserve(buf);
|
|
|
|
struct net_buf *frag;
|
2016-11-08 19:27:04 +01:00
|
|
|
struct in6_addr dst;
|
2016-10-26 10:58:45 +02:00
|
|
|
|
|
|
|
if (net_nbuf_family(buf) != AF_INET6) {
|
2016-06-16 16:00:11 +02:00
|
|
|
return NET_DROP;
|
|
|
|
}
|
|
|
|
|
2016-11-08 19:27:04 +01:00
|
|
|
/* 6lo is going to compress the ipv6 header, and thus accessing
|
|
|
|
* packet's ipv6 address won't be possible anymore when creating
|
|
|
|
* the frame */
|
|
|
|
memcpy(&dst, &NET_IPV6_BUF(buf)->dst, sizeof(struct in6_addr));
|
|
|
|
|
|
|
|
if (!ieee802154_manage_send_buffer(iface, buf)) {
|
|
|
|
return NET_DROP;
|
|
|
|
}
|
|
|
|
|
2016-10-26 10:58:45 +02:00
|
|
|
frag = buf->frags;
|
|
|
|
while (frag) {
|
2016-11-08 17:22:03 +01:00
|
|
|
if (frag->len > IEEE802154_MTU) {
|
|
|
|
NET_ERR("Frag %p as too big length %u",
|
|
|
|
frag, frag->len);
|
|
|
|
return NET_DROP;
|
|
|
|
}
|
|
|
|
|
2016-11-08 19:27:04 +01:00
|
|
|
if (!ieee802154_create_data_frame(iface, &dst,
|
2016-10-26 10:58:45 +02:00
|
|
|
frag->data - reserved_space,
|
|
|
|
reserved_space)) {
|
|
|
|
return NET_DROP;
|
|
|
|
}
|
|
|
|
|
|
|
|
frag = frag->frags;
|
|
|
|
}
|
|
|
|
|
2016-10-26 15:37:05 +02:00
|
|
|
pkt_hexdump(buf, true);
|
2016-07-12 16:09:29 +02:00
|
|
|
|
2016-06-16 16:00:11 +02:00
|
|
|
net_if_queue_tx(iface, buf);
|
|
|
|
|
|
|
|
return NET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint16_t ieeee802154_reserve(struct net_if *iface, void *data)
|
|
|
|
{
|
|
|
|
return ieee802154_compute_header_size(iface, (struct in6_addr *)data);
|
|
|
|
}
|
|
|
|
|
|
|
|
NET_L2_INIT(IEEE802154_L2,
|
2016-12-21 13:33:09 +02:00
|
|
|
ieee802154_recv, ieee802154_send, ieeee802154_reserve, NULL);
|
2016-06-16 16:00:11 +02:00
|
|
|
|
|
|
|
void ieee802154_init(struct net_if *iface)
|
|
|
|
{
|
2016-12-20 10:29:52 +00:00
|
|
|
const struct ieee802154_radio_api *radio =
|
|
|
|
iface->dev->driver_api;
|
2016-06-16 16:00:11 +02:00
|
|
|
|
|
|
|
NET_DBG("Initializing IEEE 802.15.4 stack on iface %p", iface);
|
|
|
|
|
2016-10-07 10:53:22 +02:00
|
|
|
ieee802154_mgmt_init(iface);
|
|
|
|
|
2016-06-17 11:05:44 +02:00
|
|
|
#ifdef CONFIG_NET_L2_IEEE802154_ORFD
|
|
|
|
struct ieee802154_context *ctx = net_if_l2_data(iface);
|
|
|
|
const uint8_t *mac = iface->link_addr.addr;
|
2016-10-31 14:32:02 +02:00
|
|
|
uint8_t long_addr[8];
|
2016-06-17 11:05:44 +02:00
|
|
|
uint16_t short_addr;
|
|
|
|
|
2016-10-31 14:32:02 +02:00
|
|
|
sys_memcpy_swap(long_addr, mac, 8);
|
|
|
|
|
2016-06-17 11:05:44 +02:00
|
|
|
short_addr = (mac[0] << 8) + mac[1];
|
|
|
|
|
|
|
|
radio->set_short_addr(iface->dev, short_addr);
|
2016-10-31 14:32:02 +02:00
|
|
|
radio->set_ieee_addr(iface->dev, long_addr);
|
2016-06-17 11:05:44 +02:00
|
|
|
|
|
|
|
ctx->pan_id = CONFIG_NET_L2_IEEE802154_ORFD_PAN_ID;
|
|
|
|
ctx->channel = CONFIG_NET_L2_IEEE802154_ORFD_CHANNEL;
|
|
|
|
|
|
|
|
radio->set_pan_id(iface->dev, ctx->pan_id);
|
|
|
|
radio->set_channel(iface->dev, ctx->channel);
|
|
|
|
#endif
|
2016-06-16 16:00:11 +02:00
|
|
|
radio->start(iface->dev);
|
|
|
|
}
|