/* * Copyright (C) 2020 Inria * * 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 pkg_openwsn * @{ * * @file * @brief RIOT adaption of the "radio" bsp module * * @author Francisco Molina * @} */ #include #include #include #include "leds.h" #include "debugpins.h" #include "sctimer.h" #include "idmanager.h" #include "eui64.h" #include "byteorder.h" #include "luid.h" #include "net/ieee802154.h" #include "net/ieee802154/radio.h" #include "openwsn.h" #include "openwsn_radio.h" #define LOG_LEVEL LOG_NONE #include "log.h" openwsn_radio_t openwsn_radio; /* stores the event capture time */ static PORT_TIMER_WIDTH _txrx_event_capture_time = 0; /* set if frame with valid CRC is received, false otherwise */ static atomic_bool _valid_crc = true; void _idmanager_addr_override(void) { /* Initiate Id manager here and not in `openstack_init` function to allow overriding the short id address before additional stack components are initiated */ idmanager_init(); /* override 16b address to avoid short address collision */ network_uint16_t short_addr; luid_get_short(&short_addr); open_addr_t id; id.type = ADDR_16B; memcpy(&id.addr_16b, short_addr.u8, IEEE802154_SHORT_ADDRESS_LEN); idmanager_setMyID(&id); /* override PANID */ id.type = ADDR_PANID; network_uint16_t panid_be = byteorder_htons(OPENWSN_PANID); memcpy(&id.panid, &panid_be, IEEE802154_SHORT_ADDRESS_LEN); idmanager_setMyID(&id); /* recover ADDR_64B */ eui64_t eui64; eui64_get(eui64.uint8); /* Set all IEEE addresses */ uint16_t panid = OPENWSN_PANID; ieee802154_radio_config_addr_filter(openwsn_radio.dev, IEEE802154_AF_SHORT_ADDR, &short_addr); ieee802154_radio_config_addr_filter(openwsn_radio.dev, IEEE802154_AF_EXT_ADDR, &eui64); ieee802154_radio_config_addr_filter(openwsn_radio.dev, IEEE802154_AF_PANID, &panid); } static void _hal_radio_cb(ieee802154_dev_t *dev, ieee802154_trx_ev_t status) { (void)dev; debugpins_isr_set(); _txrx_event_capture_time = sctimer_readCounter(); debugpins_isr_clr(); switch (status) { case IEEE802154_RADIO_CONFIRM_TX_DONE: ieee802154_radio_confirm_transmit(openwsn_radio.dev, NULL); ieee802154_radio_request_set_idle(openwsn_radio.dev, true); while (ieee802154_radio_confirm_set_idle(openwsn_radio.dev) == -EAGAIN) {} openwsn_radio.endFrame_cb(_txrx_event_capture_time); break; case IEEE802154_RADIO_INDICATION_CRC_ERROR: _valid_crc = false; ieee802154_radio_request_set_idle(openwsn_radio.dev, true); while (ieee802154_radio_confirm_set_idle(openwsn_radio.dev) == -EAGAIN) {} openwsn_radio.endFrame_cb(_txrx_event_capture_time); break; case IEEE802154_RADIO_INDICATION_RX_DONE: _valid_crc = true; ieee802154_radio_request_set_idle(openwsn_radio.dev, true); while (ieee802154_radio_confirm_set_idle(openwsn_radio.dev) == -EAGAIN) {} openwsn_radio.endFrame_cb(_txrx_event_capture_time); break; case IEEE802154_RADIO_INDICATION_TX_START: case IEEE802154_RADIO_INDICATION_RX_START: openwsn_radio.startFrame_cb(_txrx_event_capture_time); break; default: break; } } int openwsn_radio_init(void *radio_dev) { assert(radio_dev); ieee802154_dev_t *dev = (ieee802154_dev_t *)radio_dev; LOG_DEBUG("[openwsn/radio]: initialize riot-adaptation\n"); openwsn_radio.dev = dev; /* override short_address and panid*/ _idmanager_addr_override(); if (ieee802154_radio_request_on(dev)) { LOG_ERROR("[openwsn/radio]: unable to initialize device\n"); return -1; } /* Set the Event Notification */ dev->cb = _hal_radio_cb; /* If the radio is still not in TRX_OFF state, spin */ while (ieee802154_radio_confirm_on(dev) == -EAGAIN) {} /* Enable basic mode (no AUTOACK. no CSMA-CA). * Auto ACK is disabled via CONFIG_IEEE802154_AUTO_ACK_DISABLE. */ /* MAC layer retransmissions are disabled by _set_csma_params() */ ieee802154_radio_set_csma_params(dev, NULL, -1); if (IS_USED(MODULE_CC2538_RF)) { /* If frame filtering is enabled cc2538 will not accept beacons where the destination-address mode is 0 (no destination address). per rfc8180 4.5.1 the destination address must be set, which means the destination-address mode can't be 0 */ ieee802154_radio_set_frame_filter_mode(dev, IEEE802154_FILTER_PROMISC); } /* Configure PHY settings (channel, TX power) */ ieee802154_phy_conf_t conf = { .channel = CONFIG_IEEE802154_DEFAULT_CHANNEL, .page = CONFIG_IEEE802154_DEFAULT_CHANNEL, .pow = CONFIG_IEEE802154_DEFAULT_TXPOWER }; ieee802154_radio_config_phy(dev, &conf); return 0; } void radio_setStartFrameCb(radio_capture_cbt cb) { openwsn_radio.startFrame_cb = cb; } void radio_setEndFrameCb(radio_capture_cbt cb) { openwsn_radio.endFrame_cb = cb; } void radio_reset(void) { /* TODO: this is not handled correctly since not all radios implement this */ ieee802154_radio_off(openwsn_radio.dev); ieee802154_radio_request_on(openwsn_radio.dev); /* If the radio is still not in TRX_OFF state, spin */ while (ieee802154_radio_confirm_on(openwsn_radio.dev) == -EAGAIN) {} } void radio_setFrequency(uint8_t frequency, radio_freq_t tx_or_rx) { (void)tx_or_rx; ieee802154_phy_conf_t conf = { .channel = frequency, .page = CONFIG_IEEE802154_DEFAULT_CHANNEL, .pow = CONFIG_IEEE802154_DEFAULT_TXPOWER }; ieee802154_radio_config_phy(openwsn_radio.dev, &conf); } void radio_rfOn(void) { ieee802154_radio_request_on(openwsn_radio.dev); /* If the radio is still not in TRX_OFF state, spin */ while (ieee802154_radio_confirm_on(openwsn_radio.dev) == -EAGAIN) {} /* HACK: cc2538 does not implement on() correctly, remove when it does*/ ieee802154_radio_request_set_idle(openwsn_radio.dev, true); while (ieee802154_radio_confirm_set_idle(openwsn_radio.dev) == -EAGAIN) {} } void radio_rfOff(void) { /* radio_rfOff is called in the middle of a slot and is not always followed by an `radio_rfOn`, so don't call `ieee802154_radio_off` and only send the radio to `TrxOFF` instead */ int ret = ieee802154_radio_request_set_idle(openwsn_radio.dev, true); if (ret) { LOG_ERROR("[openwsn/radio]: request_set_idle failed %s\n", __FUNCTION__); } else { debugpins_radio_clr(); leds_radio_off(); while (ieee802154_radio_confirm_set_idle(openwsn_radio.dev) == -EAGAIN) {} } } void radio_loadPacket(uint8_t *packet, uint16_t len) { /* OpenWSN `len` accounts for the FCS field which is set by default by netdev, so remove from the actual packet `len` */ iolist_t pkt = { .iol_base = (void *)packet, .iol_len = (size_t)(len - IEEE802154_FCS_LEN), }; if (ieee802154_radio_write(openwsn_radio.dev, &pkt) < 0) { LOG_ERROR("[openwsn/radio]: couldn't load pkt\n"); } } void radio_txEnable(void) { int ret = ieee802154_radio_request_set_idle(openwsn_radio.dev, true); if (ret) { LOG_ERROR("[openwsn/radio]: request_set_idle failed %s\n", __FUNCTION__); } else { while (ieee802154_radio_confirm_set_idle(openwsn_radio.dev) == -EAGAIN) {} debugpins_radio_set(); leds_radio_on(); } } void radio_txNow(void) { int ret = ieee802154_radio_request_transmit(openwsn_radio.dev); if (ret) { LOG_ERROR("[openwsn/radio]: request_transmit failed %s\n", __FUNCTION__); } else { /* Trigger startFrame manually if no IEEE802154_CAP_IRQ_TX_START */ if (!ieee802154_radio_has_irq_tx_start(openwsn_radio.dev)) { debugpins_isr_set(); _txrx_event_capture_time = sctimer_readCounter(); debugpins_isr_clr(); openwsn_radio.startFrame_cb(_txrx_event_capture_time); } } } void radio_rxEnable(void) { int ret = ieee802154_radio_request_set_idle(openwsn_radio.dev, true); if (ret) { LOG_ERROR("[openwsn/radio]: request_set_idle failed %s\n", __FUNCTION__); } else { while (ieee802154_radio_confirm_set_idle(openwsn_radio.dev) == -EAGAIN) {} debugpins_radio_set(); leds_radio_on(); } } void radio_rxNow(void) { int ret = ieee802154_radio_request_set_rx(openwsn_radio.dev); if (ret) { LOG_ERROR("[openwsn/radio]: request_set_rx failed %s\n", __FUNCTION__); } else { while (ieee802154_radio_confirm_set_rx(openwsn_radio.dev) == -EAGAIN) {} } } void radio_getReceivedFrame(uint8_t *bufRead, uint8_t *lenRead, uint8_t maxBufLen, int8_t *rssi, uint8_t *lqi, bool *crc) { ieee802154_rx_info_t rx_info; size_t size = ieee802154_radio_read(openwsn_radio.dev, bufRead, maxBufLen, &rx_info); /* FCS is skipped by the radio-hal in the returned length, but OpenWSN includes IEEE802154_FCS_LEN in its length value */ *lenRead = size + IEEE802154_FCS_LEN; /* get rssi, lqi & crc */ *rssi = ieee802154_rssi_to_dbm(rx_info.rssi); *lqi = rx_info.lqi; *crc = _valid_crc ? 1 : 0; }