/* * Copyright (C) 2019 ML!PA Consulting GmbH * * 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 drivers_at86rf215 * @{ * * @file * @brief Implementation of public functions for AT86RF215 driver * * @author Benjamin Valentin * @} */ #include "board.h" #include "byteorder.h" #include "net/ieee802154.h" #include "net/gnrc.h" #include "unaligned.h" #include "at86rf215_internal.h" #include "at86rf215_netdev.h" #include "kernel_defines.h" #define ENABLE_DEBUG 0 #include "debug.h" static void _setup_interface(at86rf215_t *dev, const at86rf215_params_t *params, uint8_t index) { netdev_t *netdev = &dev->netdev.netdev; netdev->driver = &at86rf215_driver; dev->params = *params; dev->state = AT86RF215_STATE_OFF; netdev_register(netdev, NETDEV_AT86RF215, index); } void at86rf215_setup(at86rf215_t *dev_09, at86rf215_t *dev_24, const at86rf215_params_t *params, uint8_t index) { /* configure the sub-GHz interface */ if (dev_09) { dev_09->RF = &RF09_regs; dev_09->BBC = &BBC0_regs; _setup_interface(dev_09, params, 2 * index); dev_09->sibling = dev_24; } /* configure the 2.4 GHz interface */ if (dev_24) { dev_24->RF = &RF24_regs; dev_24->BBC = &BBC1_regs; _setup_interface(dev_24, params, 2 * index + 1); dev_24->sibling = dev_09; } } void at86rf215_reset_and_cfg(at86rf215_t *dev) { netdev_ieee802154_reset(&dev->netdev); /* set device address */ netdev_ieee802154_setup(&dev->netdev); if (is_subGHz(dev)) { dev->netdev.chan = CONFIG_AT86RF215_DEFAULT_SUBGHZ_CHANNEL; } else { dev->netdev.chan = CONFIG_AT86RF215_DEFAULT_CHANNEL; } dev->netdev.pan = CONFIG_IEEE802154_DEFAULT_PANID; /* set default options */ dev->retries_max = AT86RF215_RETRIES_MAX_DEFAULT; dev->csma_retries_max = AT86RF215_CSMA_RETRIES_MAX_DEFAULT; dev->csma_maxbe = AT86RF215_CSMA_MAX_BE_DEFAULT; dev->csma_minbe = AT86RF215_CSMA_MIN_BE_DEFAULT; dev->flags |= AT86RF215_OPT_AUTOACK | AT86RF215_OPT_CSMA #if CONFIG_AT86RF215_RPC_EN | AT86RF215_OPT_RPC #endif ; /* apply the configuration */ at86rf215_reset(dev); /* default to requesting ACKs, just like at86rf2xx */ static const netopt_enable_t ack_req = IS_ACTIVE(CONFIG_IEEE802154_DEFAULT_ACK_REQ) ? NETOPT_ENABLE : NETOPT_DISABLE; netdev_ieee802154_set(&dev->netdev, NETOPT_ACK_REQ, &ack_req, sizeof(ack_req)); /* enable RX start IRQs */ at86rf215_reg_or(dev, dev->BBC->RG_IRQM, BB_IRQ_RXAM); } void at86rf215_reset(at86rf215_t *dev) { uint8_t reg; dev->state = AT86RF215_STATE_OFF; /* Reset state machine to ensure a known state */ at86rf215_rf_cmd(dev, CMD_RF_TRXOFF); at86rf215_await_state(dev, RF_STATE_TRXOFF); if (!dev->sibling) { /* disable 2.4-GHz IRQs if the interface is not enabled */ if (is_subGHz(dev)) { at86rf215_reg_write(dev, RG_BBC1_IRQM, 0); at86rf215_reg_write(dev, RG_RF24_IRQM, 0); at86rf215_reg_write(dev, RG_RF24_CMD, CMD_RF_SLEEP); /* disable sub-GHz IRQs if the interface is not enabled */ } else { at86rf215_reg_write(dev, RG_BBC0_IRQM, 0); at86rf215_reg_write(dev, RG_RF09_IRQM, 0); at86rf215_reg_write(dev, RG_RF09_CMD, CMD_RF_SLEEP); } } /* disable clock output */ if (!IS_ACTIVE(CONFIG_AT86RF215_USE_CLOCK_OUTPUT)){ at86rf215_reg_write(dev, RG_RF_CLKO, 0); } /* allow to configure board-specific trim */ #ifdef CONFIG_AT86RF215_TRIM_VAL at86rf215_set_trim(dev, CONFIG_AT86RF215_TRIM_VAL); #endif /* enable TXFE & RXFE IRQ */ at86rf215_reg_write(dev, dev->BBC->RG_IRQM, BB_IRQ_TXFE | BB_IRQ_RXFE); /* enable EDC IRQ */ at86rf215_reg_write(dev, dev->RF->RG_IRQM, RF_IRQ_EDC | RF_IRQ_TRXRDY); /* set energy detect threshold to -84 dBm */ at86rf215_set_cca_threshold(dev, AT86RF215_EDT_DEFAULT); /* enable address filter 0 */ at86rf215_reg_write(dev, dev->BBC->RG_AFC0, AFC0_AFEN0_MASK ); at86rf215_reg_write(dev, dev->BBC->RG_AMAACKPD, AMAACKPD_PD0_MASK); /* enable auto-ACK with Frame Checksum & Data Rate derived from RX frame */ reg = AMCS_AACKFA_MASK | AMCS_AACKDR_MASK; if (dev->flags & AT86RF215_OPT_AUTOACK) { reg |= AMCS_AACK_MASK; } if (IS_USED(MODULE_AT86RF215_TIMESTAMP)) { at86rf215_reg_write(dev, dev->BBC->RG_CNTC, CNTC_EN_MASK | CNTC_CAPRXS_MASK); } at86rf215_reg_write(dev, dev->BBC->RG_AMCS, reg); if (CONFIG_AT86RF215_DEFAULT_PHY_MODE == IEEE802154_PHY_OQPSK) { at86rf215_configure_legacy_OQPSK(dev, CONFIG_AT86RF215_DEFAULT_OQPSK_RATE); } if (CONFIG_AT86RF215_DEFAULT_PHY_MODE == IEEE802154_PHY_MR_OQPSK) { at86rf215_configure_OQPSK(dev, CONFIG_AT86RF215_DEFAULT_MR_OQPSK_CHIPS, CONFIG_AT86RF215_DEFAULT_MR_OQPSK_RATE); } if (CONFIG_AT86RF215_DEFAULT_PHY_MODE == IEEE802154_PHY_MR_OFDM) { at86rf215_configure_OFDM(dev, CONFIG_AT86RF215_DEFAULT_MR_OFDM_OPT, CONFIG_AT86RF215_DEFAULT_MR_OFDM_MCS); } if (CONFIG_AT86RF215_DEFAULT_PHY_MODE == IEEE802154_PHY_MR_FSK) { at86rf215_configure_FSK(dev, CONFIG_AT86RF215_DEFAULT_MR_FSK_SRATE, CONFIG_AT86RF215_DEFAULT_MR_FSK_MOD_IDX, CONFIG_AT86RF215_DEFAULT_MR_FSK_MORD, CONFIG_AT86RF215_DEFAULT_MR_FSK_FEC); } /* set default channel */ at86rf215_set_chan(dev, dev->netdev.chan); /* set short and long address */ uint64_t long_addr; memcpy(&long_addr, dev->netdev.long_addr, sizeof(long_addr)); at86rf215_set_addr_long(dev, long_addr); at86rf215_set_addr_short(dev, 0, unaligned_get_u16(dev->netdev.short_addr)); /* set default PAN id */ at86rf215_set_pan(dev, 0, dev->netdev.pan); /* set default TX power */ at86rf215_set_txpower(dev, CONFIG_AT86RF215_DEFAULT_TXPOWER); /* start listening for incoming packets */ at86rf215_rf_cmd(dev, CMD_RF_RX); at86rf215_await_state(dev, RF_STATE_RX); dev->state = AT86RF215_STATE_IDLE; } ssize_t at86rf215_send(at86rf215_t *dev, const void *data, size_t len) { /* check data length */ if (len > AT86RF215_MAX_PKT_LENGTH) { DEBUG("[at86rf215] Error: data to send exceeds max packet size\n"); return -EOVERFLOW; } if (at86rf215_tx_prepare(dev)) { return -EBUSY; } at86rf215_tx_load(dev, data, len, 0); at86rf215_tx_exec(dev); return len; } void at86rf215_tx_done(at86rf215_t *dev) { uint8_t amcs = at86rf215_reg_read(dev, dev->BBC->RG_AMCS); /* re-enable AACK, disable TX2RX */ amcs &= ~AMCS_TX2RX_MASK; if (dev->flags & AT86RF215_OPT_AUTOACK) { amcs |= AMCS_AACK_MASK; } at86rf215_reg_write(dev, dev->BBC->RG_AMCS, amcs); } static bool _tx_ongoing(at86rf215_t *dev) { if (dev->flags & AT86RF215_OPT_TX_PENDING) { return true; } /* we can still fill the TX buffer and queue TX when in AT86RF215_STATE_RX_SEND_ACK */ if (dev->state == AT86RF215_STATE_TX || dev->state == AT86RF215_STATE_TX_WAIT_ACK) { return true; } return false; } int at86rf215_tx_prepare(at86rf215_t *dev) { if (dev->state == AT86RF215_STATE_SLEEP) { return -ENETDOWN; } if (_tx_ongoing(dev)) { return -EBUSY; } dev->tx_frame_len = IEEE802154_FCS_LEN; return 0; } size_t at86rf215_tx_load(at86rf215_t *dev, const uint8_t *data, size_t len, size_t offset) { /* set bit if ACK was requested and retransmission is enabled */ if (offset == 0 && (data[0] & IEEE802154_FCF_ACK_REQ) && dev->retries_max) { dev->flags |= AT86RF215_OPT_ACK_REQUESTED; } at86rf215_reg_write_bytes(dev, dev->BBC->RG_FBTXS + offset, data, len); dev->tx_frame_len += (uint16_t) len; return offset + len; } int at86rf215_tx_exec(at86rf215_t *dev) { /* write frame length */ at86rf215_reg_write16(dev, dev->BBC->RG_TXFLL, dev->tx_frame_len); dev->retries = dev->retries_max; dev->csma_retries = dev->csma_retries_max; dev->flags |= AT86RF215_OPT_TX_PENDING; if ((dev->flags & AT86RF215_OPT_CSMA) && !(dev->flags & AT86RF215_OPT_CCATX)) { dev->flags |= AT86RF215_OPT_CCA_PENDING; } if (dev->state == AT86RF215_STATE_IDLE) { at86rf215_rf_cmd(dev, CMD_RF_TXPREP); } else { DEBUG("[at86rf215] will TX after %s\n", at86rf215_sw_state2a(dev->state)); } return 0; } void at86rf215_tx_abort(at86rf215_t *dev) { dev->flags &= ~(AT86RF215_OPT_CCA_PENDING | AT86RF215_OPT_TX_PENDING); at86rf215_tx_done(dev); at86rf215_enable_baseband(dev); at86rf215_rf_cmd(dev, CMD_RF_RX); dev->state = AT86RF215_STATE_IDLE; } bool at86rf215_cca(at86rf215_t *dev) { bool clear; uint8_t old_state; if (dev->state != AT86RF215_STATE_IDLE) { return false; } if (dev->flags & AT86RF215_OPT_TX_PENDING) { return false; } if (!at86rf215_set_rx_from_idle(dev, &old_state)) { return false; } /* disable ED IRQ, baseband */ at86rf215_reg_and(dev, dev->RF->RG_IRQM, ~(RF_IRQ_EDC | RF_IRQ_TRXRDY)); at86rf215_reg_and(dev, dev->BBC->RG_PC, ~PC_BBEN_MASK); at86rf215_disable_rpc(dev); /* start energy detect */ at86rf215_reg_write(dev, dev->RF->RG_EDC, RF_EDSINGLE); while (!(at86rf215_reg_read(dev, dev->RF->RG_IRQS) & RF_IRQ_EDC)) {} clear = !(at86rf215_reg_read(dev, dev->BBC->RG_AMCS) & AMCS_CCAED_MASK); /* enable ED IRQ, baseband */ at86rf215_reg_or(dev, dev->RF->RG_IRQM, RF_IRQ_EDC | RF_IRQ_TRXRDY); at86rf215_reg_or(dev, dev->BBC->RG_PC, PC_BBEN_MASK); at86rf215_enable_rpc(dev); at86rf215_set_idle_from_rx(dev, old_state); return clear; }