/* * Copyright (C) 2018 Hamburg University of Applied Sciences * 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 Peter Kietzmann * @author Oliver Hahm * @author Francisco Molina * @} */ #include #include "leds.h" #include "debugpins.h" #include "sctimer.h" #include "net/netopt.h" #include "net/ieee802154.h" #include "net/netdev/ieee802154.h" #include "openwsn.h" #include "openwsn_radio.h" #define LOG_LEVEL LOG_NONE #include "log.h" openwsn_radio_t openwsn_radio; static void _event_cb(netdev_t *dev, netdev_event_t event); /* stores the NETDEV_EVENT_ISR capture time to tag the following NETDEV_EVENT */ static PORT_TIMER_WIDTH _txrx_event_capture_time = 0; int openwsn_radio_init(netdev_t *netdev) { assert(netdev); LOG_DEBUG("[openwsn/radio]: initialize riot-adaptation\n"); openwsn_radio.dev = netdev; if (netdev->driver->init(netdev)) { LOG_ERROR("[openwsn/radio]: unable to initialize device\n"); return -1; } netdev->event_callback = _event_cb; LOG_DEBUG("[openwsn/radio]: put radio in standby\n"); netopt_state_t state = NETOPT_STATE_STANDBY; netdev->driver->set(netdev, NETOPT_STATE, &(state), sizeof(state)); LOG_DEBUG("[openwsn/radio]: set needed netdev options\n"); netopt_enable_t enable; /* Enable needed IRQs */ enable = NETOPT_ENABLE; netdev->driver->set(netdev, NETOPT_TX_START_IRQ, &(enable), sizeof(enable)); enable = NETOPT_ENABLE; netdev->driver->set(netdev, NETOPT_RX_START_IRQ, &(enable), sizeof(enable)); enable = NETOPT_ENABLE; netdev->driver->set(netdev, NETOPT_RX_END_IRQ, &(enable), sizeof(enable)); enable = NETOPT_ENABLE; netdev->driver->set(netdev, NETOPT_TX_END_IRQ, &(enable), sizeof(enable)); enable = NETOPT_DISABLE; /* Enable basic mode, no AUTOACK. no CSMA , no frame filtering */ netdev->driver->set(netdev, NETOPT_AUTOACK, &(enable), sizeof(enable)); enable = NETOPT_DISABLE; netdev->driver->set(netdev, NETOPT_CSMA, &(enable), sizeof(enable)); enable = NETOPT_ENABLE; netdev->driver->set(netdev, NETOPT_RAWMODE, &(enable), sizeof(enable)); uint8_t retrans = 0; /* MAC layer will handle retransmissions */ netdev->driver->set(netdev, NETOPT_RETRANS, &(retrans), sizeof(uint8_t)); /* Enable TX with preloading */ enable = NETOPT_ENABLE; netdev->driver->set(netdev, NETOPT_PRELOADING, &(enable), sizeof(enable)); /* Set default PANID */ uint16_t panid = OPENWSN_PANID; netdev->driver->set(netdev, NETOPT_NID, &(panid), sizeof(uint16_t)); 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) { netopt_state_t state = NETOPT_STATE_RESET; openwsn_radio.dev->driver->set(openwsn_radio.dev, NETOPT_STATE, &(state), sizeof(netopt_state_t)); state = NETOPT_STATE_STANDBY; openwsn_radio.dev->driver->set(openwsn_radio.dev, NETOPT_STATE, &(state), sizeof(netopt_state_t)); } void radio_setFrequency(uint8_t frequency, radio_freq_t tx_or_rx) { (void)tx_or_rx; uint16_t chan = frequency; openwsn_radio.dev->driver->set(openwsn_radio.dev, NETOPT_CHANNEL, &(chan), sizeof(chan)); } void radio_rfOn(void) { netopt_state_t state = NETOPT_STATE_IDLE; openwsn_radio.dev->driver->set(openwsn_radio.dev, NETOPT_STATE, &(state), sizeof(netopt_state_t)); } void radio_rfOff(void) { netopt_state_t state = NETOPT_STATE_STANDBY; openwsn_radio.dev->driver->set(openwsn_radio.dev, NETOPT_STATE, &(state), sizeof(netopt_state_t)); debugpins_radio_clr(); leds_radio_off(); } void radio_loadPacket(uint8_t *packet, uint16_t len) { /* NETOPT_PRELOADING is enabled in radio_init so this will only load the packet */ /* 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 (openwsn_radio.dev->driver->send(openwsn_radio.dev, &pkt) < 0) { LOG_DEBUG("[openwsn/radio]: couldn't load pkt\n"); } LOG_DEBUG("[openwsn/radio]: loaded radio packet\n"); } void radio_txEnable(void) { debugpins_radio_set(); leds_radio_on(); } void radio_txNow(void) { netopt_state_t state = NETOPT_STATE_TX; openwsn_radio.dev->driver->set(openwsn_radio.dev, NETOPT_STATE, &state, sizeof(netopt_state_t)); } void radio_rxEnable(void) { debugpins_radio_set(); leds_radio_on(); netopt_state_t state = NETOPT_STATE_IDLE; openwsn_radio.dev->driver->set(openwsn_radio.dev, NETOPT_STATE, &(state), sizeof(state)); } void radio_rxNow(void) { /* nothing to do */ } void radio_getReceivedFrame(uint8_t *bufRead, uint8_t *lenRead, uint8_t maxBufLen, int8_t *rssi, uint8_t *lqi, bool *crc) { /* OpenWSN packets are 130 bytes to hold all required data for an SPI transaction since in some implementations it's used directly in the SPI shift register: - 1B spi address, 1B length, 125B data, 2B CRC, 1B LQI In RIOT we don't do this so maxBufLen is irrelevant, packet size will always be enough to hold IEEE802154_FRAME_LEN_MAX, but in practice only 125B of data are copied into bufRead. */ (void)maxBufLen; netdev_ieee802154_rx_info_t rx_info; int bytes_expected = openwsn_radio.dev->driver->recv(openwsn_radio.dev, NULL, 0, NULL); if (bytes_expected < (int)(IEEE802154_ACK_FRAME_LEN - IEEE802154_FCS_LEN)) { /* drop invalid packet */ openwsn_radio.dev->driver->recv(openwsn_radio.dev, NULL, bytes_expected, NULL); radio_rxEnable(); return; } int nread = openwsn_radio.dev->driver->recv(openwsn_radio.dev, bufRead, bytes_expected, &rx_info); /* FCS is skipped by netdev in the returned length, but OpenWSN includes IEEE802154_FCS_LEN in its length value */ *lenRead = nread + IEEE802154_FCS_LEN; /* get rssi, lqi & crc */ *rssi = rx_info.rssi; *lqi = rx_info.lqi; /* only valid crc frames are currently accepted */ *crc = 1; radio_rxEnable(); } static void _event_cb(netdev_t *dev, netdev_event_t event) { (void)dev; if (event == NETDEV_EVENT_ISR) { /* capture the time */ debugpins_isr_set(); _txrx_event_capture_time = sctimer_readCounter(); openwsn_radio.dev->driver->isr(openwsn_radio.dev); debugpins_isr_clr(); } else { LOG_DEBUG("[openwsn/radio]: event triggered -> %i\n", event); switch (event) { case NETDEV_EVENT_RX_STARTED: openwsn_radio.startFrame_cb(_txrx_event_capture_time); LOG_DEBUG("[openwsn/radio]: NETDEV_EVENT_RX_STARTED\n"); break; case NETDEV_EVENT_TX_STARTED: openwsn_radio.startFrame_cb(_txrx_event_capture_time); LOG_DEBUG("[openwsn/radio]: NETDEV_EVENT_TX_STARTED\n"); break; case NETDEV_EVENT_RX_COMPLETE: openwsn_radio.endFrame_cb(_txrx_event_capture_time); LOG_DEBUG("[openwsn/radio]: NETDEV_EVENT_RX_COMPLETE\n"); break; case NETDEV_EVENT_TX_COMPLETE: openwsn_radio.endFrame_cb(_txrx_event_capture_time); LOG_DEBUG("[openwsn/radio]: NETDEV_EVENT_TX_COMPLETE\n"); break; default: break; } } }