mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
474 lines
13 KiB
C
474 lines
13 KiB
C
/*
|
|
* Copyright (C) 2015 Freie Universität Berlin
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @ingroup tests
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief Test application for AT86RF2xx network device driver
|
|
*
|
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "sys/uio.h"
|
|
#include "luid.h"
|
|
|
|
#include "net/netdev.h"
|
|
#include "shell.h"
|
|
#include "net/ieee802154/submac.h"
|
|
#include "net/ieee802154.h"
|
|
#include "net/netdev/ieee802154_submac.h"
|
|
#include "net/l2util.h"
|
|
#include "test_utils/expect.h"
|
|
#include "ztimer.h"
|
|
|
|
#include "common.h"
|
|
|
|
#define MAX_LINE (80)
|
|
|
|
ieee802154_submac_t submac; /**< IEEE 802.15.4 SubMAC descriptor */
|
|
mutex_t lock; /**< lock used to synchronize SubMAC operation */
|
|
ztimer_t ack_timer; /**< required for the ACK timer */
|
|
eui64_t long_addr; /**< SubMAC extended address */
|
|
network_uint16_t short_addr; /**< SubMAC short address */
|
|
|
|
static void _ev_tx_done_handler(event_t *event); /**< TX Done event handler */
|
|
static void _ev_rx_done_handler(event_t *event); /**< RX Done event handler */
|
|
static void _ev_crc_error_handler(event_t *event); /**< CRC Error event handler */
|
|
static void _ev_bh_request_handler(event_t *event); /**< BH Request event handler */
|
|
static void _ev_ack_timeout_handler(event_t *event); /**< ACK Timeout event handler */
|
|
static void _ev_set_rx_handler(event_t *event); /**< Set RX event handler */
|
|
|
|
static event_t ev_tx_done = { .handler = _ev_tx_done_handler }; /**< TX Done descriptor */
|
|
static event_t ev_rx_done = { .handler = _ev_rx_done_handler }; /**< RX Done descriptor */
|
|
static event_t ev_crc_error = { .handler = _ev_crc_error_handler }; /**< CRC Error descriptor */
|
|
static event_t ev_bh_request = { .handler = _ev_bh_request_handler }; /**< BH Request descriptor */
|
|
static event_t ev_ack_timeout = { .handler = _ev_ack_timeout_handler }; /**< ACK TO descriptor */
|
|
static event_t ev_set_rx = { .handler = _ev_set_rx_handler }; /**< Set RX descriptor */
|
|
|
|
uint8_t buffer[IEEE802154_FRAME_LEN_MAX]; /* buffer to store IEEE 802.15.4 frames */
|
|
uint8_t seq; /* sequence number of IEEE 802.15.4 frame */
|
|
|
|
struct _reg_container {
|
|
int count; /* device index */
|
|
};
|
|
|
|
static void submac_rx_done(ieee802154_submac_t *submac);
|
|
static void submac_tx_done(ieee802154_submac_t *submac, int status,
|
|
ieee802154_tx_info_t *info);
|
|
|
|
static const ieee802154_submac_cb_t _cb = {
|
|
.rx_done = submac_rx_done,
|
|
.tx_done = submac_tx_done,
|
|
};
|
|
|
|
static const uint8_t payload[] =
|
|
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam ornare" \
|
|
"lacinia mi elementum interdum ligula.";
|
|
|
|
static int print_addr(int argc, char **argv);
|
|
static int txtsnd(int argc, char **argv);
|
|
static const shell_command_t shell_commands[] = {
|
|
{ "print_addr", "Print IEEE802.15.4 addresses", print_addr },
|
|
{ "txtsnd", "Send IEEE 802.15.4 packet", txtsnd },
|
|
{ NULL, NULL, NULL }
|
|
};
|
|
|
|
/****** SubMAC South Bond API Implementation ******/
|
|
|
|
static void _ev_tx_done_handler(event_t *event)
|
|
{
|
|
(void)event;
|
|
mutex_lock(&lock);
|
|
ieee802154_submac_tx_done_cb(&submac);
|
|
mutex_unlock(&lock);
|
|
}
|
|
|
|
static void _ev_rx_done_handler(event_t *event)
|
|
{
|
|
(void)event;
|
|
mutex_lock(&lock);
|
|
ieee802154_submac_rx_done_cb(&submac);
|
|
mutex_unlock(&lock);
|
|
}
|
|
|
|
static void _ev_crc_error_handler(event_t *event)
|
|
{
|
|
(void)event;
|
|
mutex_lock(&lock);
|
|
ieee802154_submac_crc_error_cb(&submac);
|
|
mutex_unlock(&lock);
|
|
}
|
|
|
|
static void _ev_bh_request_handler(event_t *event)
|
|
{
|
|
(void)event;
|
|
mutex_lock(&lock);
|
|
ieee802154_submac_bh_process(&submac);
|
|
mutex_unlock(&lock);
|
|
}
|
|
|
|
static void _ev_ack_timeout_handler(event_t *event)
|
|
{
|
|
(void)event;
|
|
mutex_lock(&lock);
|
|
ieee802154_submac_ack_timeout_fired(&submac);
|
|
mutex_unlock(&lock);
|
|
}
|
|
|
|
void ieee802154_submac_ack_timer_set(ieee802154_submac_t *submac, uint16_t us)
|
|
{
|
|
(void)submac;
|
|
ztimer_set(ZTIMER_USEC, &ack_timer, us);
|
|
}
|
|
|
|
void ieee802154_submac_ack_timer_cancel(ieee802154_submac_t *submac)
|
|
{
|
|
(void)submac;
|
|
ztimer_remove(ZTIMER_USEC, &ack_timer);
|
|
/* Avoid race conditions between RX_DONE and ACK_TIMEOUT */
|
|
if (ev_ack_timeout.list_node.next) {
|
|
event_cancel(EVENT_PRIO_HIGHEST, &ev_ack_timeout);
|
|
}
|
|
}
|
|
|
|
static void _ack_timeout(void *arg)
|
|
{
|
|
(void)arg;
|
|
event_post(EVENT_PRIO_HIGHEST, &ev_ack_timeout);
|
|
}
|
|
|
|
static void _hal_radio_cb(ieee802154_dev_t *dev, ieee802154_trx_ev_t status)
|
|
{
|
|
(void)dev;
|
|
switch (status) {
|
|
case IEEE802154_RADIO_CONFIRM_TX_DONE:
|
|
event_post(EVENT_PRIO_HIGHEST, &ev_tx_done);
|
|
break;
|
|
case IEEE802154_RADIO_INDICATION_RX_DONE:
|
|
event_post(EVENT_PRIO_HIGHEST, &ev_rx_done);
|
|
break;
|
|
case IEEE802154_RADIO_INDICATION_CRC_ERROR:
|
|
event_post(EVENT_PRIO_HIGHEST, &ev_crc_error);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ieee802154_submac_bh_request(ieee802154_submac_t *submac)
|
|
{
|
|
(void)submac;
|
|
event_post(EVENT_PRIO_HIGHEST, &ev_bh_request);
|
|
}
|
|
|
|
static ieee802154_dev_t *_reg_callback(ieee802154_dev_type_t type, void *opaque)
|
|
{
|
|
struct _reg_container *reg = opaque;
|
|
|
|
printf("Trying to register ");
|
|
switch (type) {
|
|
case IEEE802154_DEV_TYPE_CC2538_RF:
|
|
printf("cc2538_rf");
|
|
break;
|
|
case IEEE802154_DEV_TYPE_NRF802154:
|
|
printf("nrf52840");
|
|
break;
|
|
case IEEE802154_DEV_TYPE_SOCKET_ZEP:
|
|
printf("socket_zep");
|
|
break;
|
|
case IEEE802154_DEV_TYPE_KW2XRF:
|
|
printf("kw2xrf");
|
|
break;
|
|
case IEEE802154_DEV_TYPE_MRF24J40:
|
|
printf("mrf24j40");
|
|
break;
|
|
}
|
|
|
|
puts(".");
|
|
if (reg->count > 0) {
|
|
puts("For the moment this test only supports one radio");
|
|
return NULL;
|
|
}
|
|
|
|
puts("Success");
|
|
return &submac.dev;
|
|
}
|
|
|
|
/****** Helpers ******/
|
|
|
|
void _print_addr(uint8_t *addr, size_t addr_len)
|
|
{
|
|
for (size_t i = 0; i < addr_len; i++) {
|
|
if (i != 0) {
|
|
printf(":");
|
|
}
|
|
printf("%02x", (unsigned)addr[i]);
|
|
}
|
|
}
|
|
|
|
/****** Upper layer implementation ******/
|
|
#define IEEE802154_LONG_ADDRESS_LEN_STR_MAX \
|
|
(sizeof("00:00:00:00:00:00:00:00"))
|
|
|
|
static int print_addr(int argc, char **argv)
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
char addr_str[IEEE802154_LONG_ADDRESS_LEN_STR_MAX];
|
|
printf("%s\n", l2util_addr_to_str(
|
|
long_addr.uint8, IEEE802154_LONG_ADDRESS_LEN, addr_str));
|
|
return 0;
|
|
}
|
|
|
|
static void _ev_set_rx_handler(event_t *event)
|
|
{
|
|
(void)event;
|
|
mutex_lock(&lock);
|
|
ieee802154_set_rx(&submac);
|
|
mutex_unlock(&lock);
|
|
}
|
|
|
|
static void submac_tx_done(ieee802154_submac_t *submac, int status,
|
|
ieee802154_tx_info_t *info)
|
|
{
|
|
(void)info;
|
|
(void)submac;
|
|
switch (status) {
|
|
case TX_STATUS_SUCCESS:
|
|
puts("Tx complete");
|
|
break;
|
|
case TX_STATUS_FRAME_PENDING:
|
|
puts("Tx complete with pending data");
|
|
break;
|
|
case TX_STATUS_MEDIUM_BUSY:
|
|
puts("Medium Busy");
|
|
break;
|
|
case TX_STATUS_NO_ACK:
|
|
puts("No ACK");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Schedule the state change. Calling this function directly in the callback
|
|
* will return error */
|
|
event_post(EVENT_PRIO_HIGHEST, &ev_set_rx);
|
|
}
|
|
|
|
static void submac_rx_done(ieee802154_submac_t *submac)
|
|
{
|
|
uint8_t src[IEEE802154_LONG_ADDRESS_LEN], dst[IEEE802154_LONG_ADDRESS_LEN];
|
|
int data_len;
|
|
size_t mhr_len, src_len, dst_len;
|
|
le_uint16_t src_pan, dst_pan;
|
|
ieee802154_rx_info_t rx_info;
|
|
|
|
putchar('\n');
|
|
data_len = ieee802154_read_frame(submac, buffer, sizeof(buffer), &rx_info);
|
|
if (data_len < 0) {
|
|
puts("Couldn't read frame");
|
|
return;
|
|
}
|
|
mhr_len = ieee802154_get_frame_hdr_len(buffer);
|
|
if (mhr_len == 0) {
|
|
puts("Unexpected MHR for incoming packet");
|
|
return;
|
|
}
|
|
dst_len = ieee802154_get_dst(buffer, dst, &dst_pan);
|
|
src_len = ieee802154_get_src(buffer, src, &src_pan);
|
|
switch (buffer[0] & IEEE802154_FCF_TYPE_MASK) {
|
|
case IEEE802154_FCF_TYPE_BEACON:
|
|
puts("BEACON");
|
|
break;
|
|
case IEEE802154_FCF_TYPE_DATA:
|
|
puts("DATA");
|
|
break;
|
|
case IEEE802154_FCF_TYPE_ACK:
|
|
puts("ACK");
|
|
break;
|
|
case IEEE802154_FCF_TYPE_MACCMD:
|
|
puts("MACCMD");
|
|
break;
|
|
default:
|
|
puts("UNKNOWN");
|
|
break;
|
|
}
|
|
printf("Dest. PAN: 0x%04x, Dest. addr.: ",
|
|
byteorder_ltohs(dst_pan));
|
|
_print_addr(dst, dst_len);
|
|
printf("\nSrc. PAN: 0x%04x, Src. addr.: ",
|
|
byteorder_ltohs(src_pan));
|
|
_print_addr(src, src_len);
|
|
printf("\nSecurity: ");
|
|
if (buffer[0] & IEEE802154_FCF_SECURITY_EN) {
|
|
printf("1, ");
|
|
}
|
|
else {
|
|
printf("0, ");
|
|
}
|
|
printf("Frame pend.: ");
|
|
if (buffer[0] & IEEE802154_FCF_FRAME_PEND) {
|
|
printf("1, ");
|
|
}
|
|
else {
|
|
printf("0, ");
|
|
}
|
|
printf("ACK req.: ");
|
|
if (buffer[0] & IEEE802154_FCF_ACK_REQ) {
|
|
printf("1, ");
|
|
}
|
|
else {
|
|
printf("0, ");
|
|
}
|
|
printf("PAN comp.: ");
|
|
if (buffer[0] & IEEE802154_FCF_PAN_COMP) {
|
|
puts("1");
|
|
}
|
|
else {
|
|
puts("0");
|
|
}
|
|
printf("Version: ");
|
|
printf("%u, ", (unsigned)((buffer[1] & IEEE802154_FCF_VERS_MASK) >> 4));
|
|
printf("Seq.: %u\n", (unsigned)ieee802154_get_seq(buffer));
|
|
od_hex_dump(buffer + mhr_len, data_len - mhr_len, 0);
|
|
printf("txt (%u chars): ", data_len - mhr_len);
|
|
for (int i = mhr_len; i < data_len; i++) {
|
|
if ((buffer[i] > 0x1F) && (buffer[i] < 0x80)) {
|
|
putchar((char)buffer[i]);
|
|
}
|
|
else {
|
|
putchar('?');
|
|
}
|
|
if (((((i - mhr_len) + 1) % (MAX_LINE - sizeof("txt: "))) == 1) &&
|
|
(i - mhr_len) != 0) {
|
|
printf("\n ");
|
|
}
|
|
}
|
|
printf("\n");
|
|
printf("RSSI: %i, LQI: %u\n\n", rx_info.rssi, rx_info.lqi);
|
|
|
|
/* Schedule the state change. Calling this function directly in the callback
|
|
* will return error */
|
|
event_post(EVENT_PRIO_HIGHEST, &ev_set_rx);
|
|
}
|
|
|
|
static int send(uint8_t *dst, size_t dst_len,
|
|
size_t len)
|
|
{
|
|
uint8_t flags;
|
|
uint8_t mhr[IEEE802154_MAX_HDR_LEN];
|
|
int mhr_len;
|
|
|
|
le_uint16_t src_pan, dst_pan;
|
|
iolist_t iol_data = {
|
|
.iol_base = (void *)payload,
|
|
.iol_len = len,
|
|
.iol_next = NULL,
|
|
};
|
|
|
|
flags = IEEE802154_FCF_TYPE_DATA | IEEE802154_FCF_ACK_REQ;
|
|
src_pan = byteorder_btols(byteorder_htons(CONFIG_IEEE802154_DEFAULT_PANID));
|
|
dst_pan = byteorder_btols(byteorder_htons(CONFIG_IEEE802154_DEFAULT_PANID));
|
|
|
|
uint8_t src_len = IEEE802154_LONG_ADDRESS_LEN;
|
|
void *src = &submac.ext_addr;
|
|
|
|
/* fill MAC header, seq should be set by device */
|
|
if ((mhr_len = ieee802154_set_frame_hdr(mhr, src, src_len,
|
|
dst, dst_len,
|
|
src_pan, dst_pan,
|
|
flags, seq++)) < 0) {
|
|
puts("txtsnd: Error preperaring frame");
|
|
return 1;
|
|
}
|
|
|
|
iolist_t pkt;
|
|
pkt.iol_next = &iol_data;
|
|
pkt.iol_base = mhr;
|
|
pkt.iol_len = mhr_len;
|
|
|
|
mutex_lock(&lock);
|
|
int res = ieee802154_send(&submac, &pkt);
|
|
mutex_unlock(&lock);
|
|
if (res < 0) {
|
|
puts("Error: Frame couldn't be sent");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int txtsnd(int argc, char **argv)
|
|
{
|
|
uint8_t addr[IEEE802154_LONG_ADDRESS_LEN];
|
|
size_t len;
|
|
size_t res;
|
|
|
|
if (argc != 3) {
|
|
puts("Usage: txtsnd <long_addr> <len>");
|
|
return 1;
|
|
}
|
|
|
|
res = l2util_addr_from_str(argv[1], addr);
|
|
if (res == 0) {
|
|
puts("Usage: txtsnd <long_addr> <len>");
|
|
return 1;
|
|
}
|
|
len = atoi(argv[2]);
|
|
return send(addr, res, len);
|
|
}
|
|
|
|
static int _init(void)
|
|
{
|
|
mutex_init(&lock);
|
|
|
|
submac.cb = &_cb;
|
|
|
|
/* Set the Event Notification */
|
|
submac.dev.cb = _hal_radio_cb;
|
|
|
|
ack_timer.callback = _ack_timeout;
|
|
ack_timer.arg = NULL;
|
|
|
|
luid_base(&long_addr, sizeof(long_addr));
|
|
eui64_set_local(&long_addr);
|
|
eui64_clear_group(&long_addr);
|
|
eui_short_from_eui64(&long_addr, &short_addr);
|
|
|
|
struct _reg_container reg = { 0 };
|
|
ieee802154_hal_test_init_devs(_reg_callback, ®);
|
|
|
|
int res = ieee802154_submac_init(&submac, &short_addr, &long_addr);
|
|
|
|
if (res < 0) {
|
|
return res;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
_init();
|
|
|
|
/* start the shell */
|
|
puts("Initialization successful - starting the shell now");
|
|
|
|
char line_buf[SHELL_DEFAULT_BUFSIZE];
|
|
|
|
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
|
|
|
|
return 0;
|
|
}
|