mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
348 lines
10 KiB
C
348 lines
10 KiB
C
/*
|
|
* 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 <benjamin.valentin@ml-pa.com>
|
|
* @}
|
|
*/
|
|
|
|
#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;
|
|
}
|