mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
408 lines
13 KiB
C
408 lines
13 KiB
C
/*
|
|
* Copyright (C) 2020 HAW Hamburg
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/**
|
|
* @{
|
|
*
|
|
* @file
|
|
* @author José I. Alamos <jose.alamos@haw-hamburg.de>
|
|
*/
|
|
|
|
#include "net/netdev/ieee802154_submac.h"
|
|
#include "event/thread.h"
|
|
|
|
static const ieee802154_submac_cb_t _cb;
|
|
|
|
static const netdev_driver_t netdev_submac_driver;
|
|
|
|
static void _ack_timeout(void *arg)
|
|
{
|
|
netdev_ieee802154_submac_t *netdev_submac = arg;
|
|
netdev_t *netdev = arg;
|
|
|
|
netdev_submac->isr_flags |= NETDEV_SUBMAC_FLAGS_ACK_TIMEOUT;
|
|
|
|
netdev->event_callback(netdev, NETDEV_EVENT_ISR);
|
|
}
|
|
|
|
static netopt_state_t _get_submac_state(ieee802154_submac_t *submac)
|
|
{
|
|
if (ieee802154_submac_state_is_idle(submac)) {
|
|
return NETOPT_STATE_SLEEP;
|
|
}
|
|
else {
|
|
return NETOPT_STATE_IDLE;
|
|
}
|
|
}
|
|
|
|
static int _get(netdev_t *netdev, netopt_t opt, void *value, size_t max_len)
|
|
{
|
|
netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev);
|
|
netdev_ieee802154_submac_t *netdev_submac = container_of(netdev_ieee802154, netdev_ieee802154_submac_t, dev);
|
|
ieee802154_submac_t *submac = &netdev_submac->submac;
|
|
|
|
switch (opt) {
|
|
case NETOPT_STATE:
|
|
*((netopt_state_t*) value) = _get_submac_state(submac);
|
|
return 0;
|
|
case NETOPT_TX_RETRIES_NEEDED:
|
|
if (netdev_submac->retrans < 0) {
|
|
return -ENOTSUP;
|
|
}
|
|
*((uint8_t*) value) = netdev_submac->retrans;
|
|
return 1;
|
|
case NETOPT_IEEE802154_PHY:
|
|
*((uint8_t*) value) = ieee802154_get_phy_mode(submac);
|
|
return 1;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return netdev_ieee802154_get(container_of(netdev, netdev_ieee802154_t, netdev), opt,
|
|
value, max_len);
|
|
}
|
|
|
|
static int _set_submac_state(ieee802154_submac_t *submac, netopt_state_t state)
|
|
{
|
|
switch (state) {
|
|
case NETOPT_STATE_SLEEP:
|
|
return ieee802154_set_idle(submac);
|
|
break;
|
|
case NETOPT_STATE_IDLE:
|
|
return ieee802154_set_rx(submac);
|
|
break;
|
|
default:
|
|
return -ENOTSUP;
|
|
}
|
|
}
|
|
|
|
static int _set(netdev_t *netdev, netopt_t opt, const void *value,
|
|
size_t value_len)
|
|
{
|
|
netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev);
|
|
netdev_ieee802154_submac_t *netdev_submac = container_of(netdev_ieee802154,
|
|
netdev_ieee802154_submac_t,
|
|
dev);
|
|
ieee802154_submac_t *submac = &netdev_submac->submac;
|
|
|
|
int res;
|
|
int16_t tx_power;
|
|
|
|
switch (opt) {
|
|
case NETOPT_ADDRESS:
|
|
ieee802154_set_short_addr(submac, value);
|
|
break;
|
|
case NETOPT_ADDRESS_LONG:
|
|
ieee802154_set_ext_addr(submac, value);
|
|
break;
|
|
case NETOPT_NID:
|
|
ieee802154_set_panid(submac, value);
|
|
break;
|
|
case NETOPT_CHANNEL:
|
|
ieee802154_set_channel_number(submac, *((uint16_t *)value));
|
|
break;
|
|
case NETOPT_TX_POWER:
|
|
tx_power = *((int16_t *)value);
|
|
res = ieee802154_set_tx_power(submac, tx_power);
|
|
if (res >= 0) {
|
|
netdev_submac->dev.txpower = tx_power;
|
|
}
|
|
return res;
|
|
case NETOPT_STATE:
|
|
return _set_submac_state(submac, *((netopt_state_t*) value));
|
|
case NETOPT_PROMISCUOUSMODE:
|
|
return ieee802154_radio_set_frame_filter_mode(&submac->dev,
|
|
*((bool *)value) ? IEEE802154_FILTER_PROMISC
|
|
: IEEE802154_FILTER_ACCEPT);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return netdev_ieee802154_set(container_of(netdev, netdev_ieee802154_t, netdev),
|
|
opt, value, value_len);
|
|
}
|
|
|
|
void ieee802154_submac_bh_request(ieee802154_submac_t *submac)
|
|
{
|
|
netdev_ieee802154_submac_t *netdev_submac = container_of(submac,
|
|
netdev_ieee802154_submac_t,
|
|
submac);
|
|
|
|
netdev_t *netdev = &netdev_submac->dev.netdev;
|
|
netdev_submac->isr_flags |= NETDEV_SUBMAC_FLAGS_BH_REQUEST;
|
|
|
|
netdev->event_callback(netdev, NETDEV_EVENT_ISR);
|
|
}
|
|
|
|
void ieee802154_submac_ack_timer_set(ieee802154_submac_t *submac, uint16_t us)
|
|
{
|
|
netdev_ieee802154_submac_t *netdev_submac = container_of(submac,
|
|
netdev_ieee802154_submac_t,
|
|
submac);
|
|
|
|
ztimer_set(ZTIMER_USEC, &netdev_submac->ack_timer, us);
|
|
}
|
|
|
|
void ieee802154_submac_ack_timer_cancel(ieee802154_submac_t *submac)
|
|
{
|
|
netdev_ieee802154_submac_t *netdev_submac = container_of(submac,
|
|
netdev_ieee802154_submac_t,
|
|
submac);
|
|
|
|
ztimer_remove(ZTIMER_USEC, &netdev_submac->ack_timer);
|
|
/* Prevent a race condition between the RX_DONE event and the ACK timeout */
|
|
netdev_submac->isr_flags &= ~NETDEV_SUBMAC_FLAGS_ACK_TIMEOUT;
|
|
|
|
}
|
|
|
|
static int _send(netdev_t *netdev, const iolist_t *pkt)
|
|
{
|
|
netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev);
|
|
netdev_ieee802154_submac_t *netdev_submac = container_of(netdev_ieee802154,
|
|
netdev_ieee802154_submac_t,
|
|
dev);
|
|
ieee802154_submac_t *submac = &netdev_submac->submac;
|
|
|
|
int res = ieee802154_send(submac, pkt);
|
|
if (res >= 0) {
|
|
/* HACK: Used to mark a transmission when called
|
|
* inside the TX Done callback */
|
|
netdev_submac->ev = NETDEV_EVENT_TX_STARTED;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static void _isr(netdev_t *netdev)
|
|
{
|
|
netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev);
|
|
netdev_ieee802154_submac_t *netdev_submac = container_of(netdev_ieee802154,
|
|
netdev_ieee802154_submac_t,
|
|
dev);
|
|
ieee802154_submac_t *submac = &netdev_submac->submac;
|
|
|
|
bool can_dispatch = true;
|
|
do {
|
|
irq_disable();
|
|
int flags = netdev_submac->isr_flags;
|
|
netdev_submac->isr_flags = 0;
|
|
irq_enable();
|
|
|
|
if (flags & NETDEV_SUBMAC_FLAGS_BH_REQUEST) {
|
|
ieee802154_submac_bh_process(submac);
|
|
}
|
|
|
|
if (flags & NETDEV_SUBMAC_FLAGS_ACK_TIMEOUT) {
|
|
ieee802154_submac_ack_timeout_fired(&netdev_submac->submac);
|
|
}
|
|
|
|
if (flags & NETDEV_SUBMAC_FLAGS_TX_DONE) {
|
|
ieee802154_submac_tx_done_cb(&netdev_submac->submac);
|
|
}
|
|
|
|
if (flags & NETDEV_SUBMAC_FLAGS_RX_DONE) {
|
|
ieee802154_submac_rx_done_cb(submac);
|
|
}
|
|
|
|
if (flags & NETDEV_SUBMAC_FLAGS_CRC_ERROR) {
|
|
ieee802154_submac_crc_error_cb(submac);
|
|
}
|
|
|
|
if (flags) {
|
|
can_dispatch = false;
|
|
}
|
|
|
|
} while (netdev_submac->isr_flags != 0);
|
|
|
|
if (netdev_submac->dispatch) {
|
|
/* The SubMAC will not generate further events after calling TX Done
|
|
* or RX Done, but there might be pending ISR events that might not be
|
|
* caught by the previous loop.
|
|
* This should be safe to make sure that all events are cached */
|
|
if (!can_dispatch) {
|
|
netdev->event_callback(netdev, NETDEV_EVENT_ISR);
|
|
return;
|
|
}
|
|
netdev_submac->dispatch = false;
|
|
/* TODO: Prevent race condition when state goes to PREPARE */
|
|
netdev->event_callback(netdev, netdev_submac->ev);
|
|
/* HACK: the TX_STARTED event is used to indicate a frame was
|
|
* sent during the event callback.
|
|
* If no frame was sent go back to RX */
|
|
if (netdev_submac->ev != NETDEV_EVENT_TX_STARTED) {
|
|
ieee802154_set_rx(submac);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static int _recv(netdev_t *netdev, void *buf, size_t len, void *info)
|
|
{
|
|
netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev);
|
|
netdev_ieee802154_submac_t *netdev_submac = container_of(netdev_ieee802154,
|
|
netdev_ieee802154_submac_t,
|
|
dev);
|
|
ieee802154_submac_t *submac = &netdev_submac->submac;
|
|
ieee802154_rx_info_t rx_info;
|
|
|
|
if (buf == NULL && len == 0) {
|
|
return ieee802154_get_frame_length(submac);
|
|
}
|
|
|
|
int res = ieee802154_read_frame(submac, buf, len, &rx_info);
|
|
|
|
if (info) {
|
|
netdev_ieee802154_rx_info_t *netdev_rx_info = info;
|
|
|
|
/* The Radio HAL uses the IEEE 802.15.4 definition for RSSI.
|
|
* Netdev uses dBm. Therefore we need a translation here */
|
|
netdev_rx_info->rssi = ieee802154_rssi_to_dbm(rx_info.rssi);
|
|
|
|
netdev_rx_info->lqi = rx_info.lqi;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static void submac_tx_done(ieee802154_submac_t *submac, int status,
|
|
ieee802154_tx_info_t *info)
|
|
{
|
|
(void)status;
|
|
netdev_ieee802154_submac_t *netdev_submac = container_of(submac,
|
|
netdev_ieee802154_submac_t,
|
|
submac);
|
|
if (info) {
|
|
netdev_submac->retrans = info->retrans;
|
|
}
|
|
|
|
netdev_submac->dispatch = true;
|
|
|
|
switch (status) {
|
|
case TX_STATUS_SUCCESS:
|
|
netdev_submac->ev = NETDEV_EVENT_TX_COMPLETE;
|
|
break;
|
|
case TX_STATUS_FRAME_PENDING:
|
|
netdev_submac->ev = NETDEV_EVENT_TX_COMPLETE_DATA_PENDING;
|
|
break;
|
|
case TX_STATUS_MEDIUM_BUSY:
|
|
netdev_submac->ev = NETDEV_EVENT_TX_MEDIUM_BUSY;
|
|
break;
|
|
case TX_STATUS_NO_ACK:
|
|
netdev_submac->ev = NETDEV_EVENT_TX_NOACK;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void submac_rx_done(ieee802154_submac_t *submac)
|
|
{
|
|
netdev_ieee802154_submac_t *netdev_submac = container_of(submac,
|
|
netdev_ieee802154_submac_t,
|
|
submac);
|
|
netdev_submac->dispatch = true;
|
|
netdev_submac->ev = NETDEV_EVENT_RX_COMPLETE;
|
|
}
|
|
|
|
static const ieee802154_submac_cb_t _cb = {
|
|
.rx_done = submac_rx_done,
|
|
.tx_done = submac_tx_done,
|
|
};
|
|
|
|
/* Event Notification callback */
|
|
static void _hal_radio_cb(ieee802154_dev_t *dev, ieee802154_trx_ev_t status)
|
|
{
|
|
ieee802154_submac_t *submac = container_of(dev, ieee802154_submac_t, dev);
|
|
netdev_ieee802154_submac_t *netdev_submac = container_of(submac,
|
|
netdev_ieee802154_submac_t,
|
|
submac);
|
|
netdev_t *netdev = &netdev_submac->dev.netdev;
|
|
|
|
switch (status) {
|
|
case IEEE802154_RADIO_CONFIRM_TX_DONE:
|
|
netdev_submac->isr_flags |= NETDEV_SUBMAC_FLAGS_TX_DONE;
|
|
break;
|
|
case IEEE802154_RADIO_INDICATION_RX_DONE:
|
|
netdev_submac->isr_flags |= NETDEV_SUBMAC_FLAGS_RX_DONE;
|
|
break;
|
|
case IEEE802154_RADIO_INDICATION_CRC_ERROR:
|
|
netdev_submac->isr_flags |= NETDEV_SUBMAC_FLAGS_CRC_ERROR;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
netdev->event_callback(netdev, NETDEV_EVENT_ISR);
|
|
}
|
|
|
|
static int _init(netdev_t *netdev)
|
|
{
|
|
netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev);
|
|
netdev_ieee802154_submac_t *netdev_submac = container_of(netdev_ieee802154,
|
|
netdev_ieee802154_submac_t,
|
|
dev);
|
|
ieee802154_submac_t *submac = &netdev_submac->submac;
|
|
netdev_ieee802154_setup(netdev_ieee802154);
|
|
|
|
int res = ieee802154_submac_init(submac,
|
|
(network_uint16_t*) netdev_ieee802154->short_addr,
|
|
(eui64_t*) netdev_ieee802154->long_addr);
|
|
|
|
if (res < 0) {
|
|
return res;
|
|
}
|
|
|
|
/* This function already sets the PAN ID to the default one */
|
|
netdev_ieee802154_reset(netdev_ieee802154);
|
|
|
|
uint16_t chan = submac->channel_num;
|
|
int16_t tx_power = submac->tx_pow;
|
|
static const netopt_enable_t ack_req =
|
|
IS_ACTIVE(CONFIG_IEEE802154_DEFAULT_ACK_REQ) ? NETOPT_ENABLE : NETOPT_DISABLE;
|
|
|
|
/* Initialise netdev_ieee802154_t struct */
|
|
netdev_ieee802154_set(netdev_ieee802154, NETOPT_CHANNEL,
|
|
&chan, sizeof(chan));
|
|
netdev_ieee802154_set(netdev_ieee802154, NETOPT_ACK_REQ,
|
|
&ack_req, sizeof(ack_req));
|
|
|
|
netdev_submac->dev.txpower = tx_power;
|
|
|
|
/* signal link UP */
|
|
netdev->event_callback(netdev, NETDEV_EVENT_LINK_UP);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int netdev_ieee802154_submac_init(netdev_ieee802154_submac_t *netdev_submac)
|
|
{
|
|
netdev_t *netdev = &netdev_submac->dev.netdev;
|
|
|
|
netdev->driver = &netdev_submac_driver;
|
|
ieee802154_submac_t *submac = &netdev_submac->submac;
|
|
|
|
submac->cb = &_cb;
|
|
|
|
/* Set the Event Notification */
|
|
submac->dev.cb = _hal_radio_cb;
|
|
|
|
netdev_submac->ack_timer.callback = _ack_timeout;
|
|
netdev_submac->ack_timer.arg = netdev_submac;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const netdev_driver_t netdev_submac_driver = {
|
|
.get = _get,
|
|
.set = _set,
|
|
.send = _send,
|
|
.recv = _recv,
|
|
.isr = _isr,
|
|
.init = _init,
|
|
};
|
|
|
|
/** @} */
|