1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-25 06:23:53 +01:00

drivers/mrf24j40: add support for IEEE 802.15.4 Radio HAL

This commit is contained in:
Jose Alamos 2022-08-16 17:28:05 +02:00
parent dacc4ff124
commit 91a299cb7d
No known key found for this signature in database
GPG Key ID: F483EB800EF89DD9
18 changed files with 588 additions and 943 deletions

View File

@ -71,6 +71,7 @@
#include "net/netdev.h"
#include "net/netdev/ieee802154.h"
#include "net/gnrc/nettype.h"
#include "net/ieee802154/radio.h"
#ifdef __cplusplus
extern "C" {
@ -105,6 +106,11 @@ extern "C" {
#define MRF24J40_MAX_FRAME_RETRIES (3U) /**< Number of frame retries (fixed) */
#define MRF24J40_MAX_MINBE (3U) /**< Maximum value of minimum
exponential backoff */
#define MRF24J40_MIN_TXPOWER (-36) /**< Minimum transmission power (dBm) */
#define MRF24J40_MAX_TXPOWER (0) /**< Maximum transmission power (dBm) */
/**
* @defgroup drivers_mrf24j40_config mrf24j40 driver compile configuration
* @ingroup drivers_mrf24j40
@ -151,29 +157,13 @@ typedef struct mrf24j40_params {
* @brief Device descriptor for MRF24J40 radio devices
*/
typedef struct {
netdev_ieee802154_t netdev; /**< netdev parent struct */
/* device specific fields */
mrf24j40_params_t params; /**< parameters for initialization */
uint8_t state; /**< current state of the radio */
uint8_t idle_state; /**< state to return to after sending */
uint8_t tx_frame_len; /**< length of the current TX frame */
uint8_t header_len; /**< length of the header */
const mrf24j40_params_t *params; /**< parameters for initialization */
uint8_t fcf_low; /**< Low 8 FCF bits of the current TX frame. */
uint8_t pending; /**< Flags for pending tasks */
uint8_t irq_flag; /**< Flags for IRQs */
uint8_t tx_retries; /**< Number of retries needed for last transmission */
bool tx_pending; /**< Whether a transmission is pending or not */
} mrf24j40_t;
/**
* @brief Setup an MRF24J40 based device state
*
* @param[out] dev device descriptor
* @param[in] params parameters for device initialization
* @param[in] index index of @p params in a global parameter struct array.
* If initialized manually, pass a unique identifier instead.
*/
void mrf24j40_setup(mrf24j40_t *dev, const mrf24j40_params_t *params, uint8_t index);
/**
* @brief Trigger a hardware reset and configure radio with default values
*
@ -183,26 +173,6 @@ void mrf24j40_setup(mrf24j40_t *dev, const mrf24j40_params_t *params, uint8_t in
*/
int mrf24j40_reset(mrf24j40_t *dev);
/**
* @brief Trigger a clear channel assessment & retrieve RSSI
*
* @param[in] dev device to use
* @param[in] rssi RSSI value from register in dBm
*
* @return true if channel is clear
* @return false if channel is busy
*/
bool mrf24j40_cca(mrf24j40_t *dev, int8_t *rssi);
/**
* @brief Get the short address of the given device
*
* @param[in] dev device to read from
*
* @return the currently set (2-byte) short address
*/
uint16_t mrf24j40_get_addr_short(mrf24j40_t *dev);
/**
* @brief Set the short address of the given device
*
@ -211,14 +181,6 @@ uint16_t mrf24j40_get_addr_short(mrf24j40_t *dev);
*/
void mrf24j40_set_addr_short(mrf24j40_t *dev, uint16_t addr);
/**
* @brief Get the configured long address of the given device
*
* @param[in] dev device to read from
* @param[out] addr the currently set (8-byte) long address
*/
void mrf24j40_get_addr_long(mrf24j40_t *dev, uint8_t *addr);
/**
* @brief Set the long address of the given device
*
@ -261,15 +223,6 @@ uint16_t mrf24j40_get_pan(mrf24j40_t *dev);
*/
void mrf24j40_set_pan(mrf24j40_t *dev, uint16_t pan);
/**
* @brief Get the configured transmission power of the given device [in dBm]
*
* @param[in] dev device to read from
*
* @return configured transmission power in dBm
*/
int16_t mrf24j40_get_txpower(mrf24j40_t *dev);
/**
* @brief Set the transmission power of the given device [in dBm]
*
@ -283,15 +236,6 @@ int16_t mrf24j40_get_txpower(mrf24j40_t *dev);
*/
void mrf24j40_set_txpower(mrf24j40_t *dev, int16_t txpower);
/**
* @brief Get the maximum number of channel access attempts per frame (CSMA)
*
* @param[in] dev device to read from
*
* @return configured number of retries
*/
uint8_t mrf24j40_get_csma_max_retries(mrf24j40_t *dev);
/**
* @brief Set the maximum number of channel access attempts per frame (CSMA)
*
@ -317,15 +261,6 @@ void mrf24j40_set_csma_max_retries(mrf24j40_t *dev, int8_t retries);
*/
void mrf24j40_set_csma_backoff_exp(mrf24j40_t *dev, uint8_t min, uint8_t max);
/**
* @brief Get the CCA threshold value
*
* @param[in] dev device to read value from
*
* @return the current CCA threshold value
*/
int8_t mrf24j40_get_cca_threshold(mrf24j40_t *dev);
/**
* @brief Set the CCA threshold value
*
@ -364,14 +299,6 @@ void mrf24j40_set_state(mrf24j40_t *dev, uint8_t state);
*/
void mrf24j40_set_turbo(mrf24j40_t *dev, bool enable);
/**
* @brief Query the state of the turbo mode
*
* @param[in] dev device to query
* @return true if Turbo Mode is enabled
*/
bool mrf24j40_get_turbo(mrf24j40_t *dev);
/**
* @brief Put in sleep mode
*
@ -379,19 +306,12 @@ bool mrf24j40_get_turbo(mrf24j40_t *dev);
*/
void mrf24j40_sleep(mrf24j40_t *dev);
/**
* @brief Put in sleep mode if idle_state is sleep
*
* @param[in] dev device to put to sleep
*/
void mrf24j40_assert_sleep(mrf24j40_t *dev);
/**
* @brief Wake up from sleep mode
*
* @param[in] dev device to eventually wake up
*/
void mrf24j40_assert_awake(mrf24j40_t *dev);
void mrf24j40_wake_up(mrf24j40_t *dev);
/**
* @brief Reset the internal state machine to TRX_OFF mode.
@ -452,6 +372,27 @@ size_t mrf24j40_tx_load(mrf24j40_t *dev, uint8_t *data, size_t len,
*/
void mrf24j40_tx_exec(mrf24j40_t *dev);
/**
* @brief IRQ Handler for the MRF24J40 device
*
* @param[in] dev pointer to the IEEE 802.15.4 Radio HAL descriptor
*/
void mrf24j40_radio_irq_handler(void *dev);
/**
* @brief Initialize the given MRF24J40 device
* @param[out] dev device descriptor
* @param[in] params parameters for device initialization
* @param[in] hal pointer to IEEE 802.15.4 Radio HAL descriptor
* @param[in] cb ISR callback
* @param[in] ctx context pointer handed to isr
*
* @return 0 on success
* @return <0 on error
*/
int mrf24j40_init(mrf24j40_t *dev, const mrf24j40_params_t *params, ieee802154_dev_t *hal,
gpio_cb_t cb, void *ctx);
#ifdef __cplusplus
}
#endif

View File

@ -13,14 +13,14 @@ menuconfig MODULE_MRF24J40
depends on HAS_PERIPH_GPIO
depends on HAS_PERIPH_GPIO_IRQ
depends on HAS_PERIPH_SPI
select MODULE_IEEE802154
select MODULE_NETDEV
select MODULE_NETDEV_IEEE802154
select MODULE_NETDEV_LEGACY_API
select MODULE_PERIPH_GPIO
select MODULE_PERIPH_GPIO_IRQ
select MODULE_PERIPH_SPI
select MODULE_XTIMER
select MODULE_IEEE802154
select MODULE_ZTIMER_USEC
select HAVE_BHP_IRQ_HANDLER
select HAVE_IEEE802154_RADIO_HAL_INTERFACE
select MRF24J40_OQPSK if MODULE_NETDEV
if MODULE_MRF24J40

View File

@ -1,11 +1,12 @@
USEMODULE += ztimer_usec
USEMODULE += ieee802154
USEMODULE += netdev_ieee802154
USEMODULE += netdev_legacy_api
USEMODULE += xtimer
USEMODULE += bhp
FEATURES_REQUIRED += periph_gpio
FEATURES_REQUIRED += periph_gpio_irq
FEATURES_REQUIRED += periph_spi
ifneq (,$(filter mrf24j40m%,$(USEMODULE)))
ifneq (,$(filter netdev,$(USEMODULE)))
USEMODULE += netdev_ieee802154_submac
DEFAULT_MODULE += netdev_ieee802154_oqpsk
endif

View File

@ -37,7 +37,7 @@ extern "C" {
*
* @return 0 on success, error otherwise
*/
int mrf24j40_init(mrf24j40_t *dev);
int mrf24j40_init_hw(mrf24j40_t *dev);
/**
* @brief Read from a register with a at address `addr` from device `dev`. Register with 8bit address
@ -151,6 +151,13 @@ void mrf24j40_enable_lna(mrf24j40_t *dev);
static inline void mrf24j40_enable_lna(mrf24j40_t *dev) { (void) dev; }
#endif
/**
* @brief Flush the RX FIFO
*
* @param[in] dev device to flush the RX FIFO
*/
void mrf24j40_flush_rx(mrf24j40_t *dev);
#ifdef __cplusplus
}
#endif

View File

@ -219,6 +219,28 @@ extern "C" {
/** @} */
/**
* @brief Shift offsets for TXMCR register (0x11)
* @{
*/
#define MRF24J40_TXMCR_MACMINBE_SHIFT (3U)
/** @} */
/**
* @brief Bitfield definitions for the ACKTMOUT register (0x12)
* @{
*/
#define MRF24J40_ACKTMOUT_DRPACK (0x80)
#define MRF24J40_ACKTMOUT_MAWD6 (0x40)
#define MRF24J40_ACKTMOUT_MAWD5 (0x20)
#define MRF24J40_ACKTMOUT_MAWD4 (0x10)
#define MRF24J40_ACKTMOUT_MAWD3 (0x08)
#define MRF24J40_ACKTMOUT_MAWD2 (0x04)
#define MRF24J40_ACKTMOUT_MAWD1 (0x02)
#define MRF24J40_ACKTMOUT_MAWD0 (0x01)
/** @} */
/**
* @name Bitfield definitions for the PACON2 register (0x18)
* @{

View File

@ -24,138 +24,37 @@
#include "mrf24j40_registers.h"
#include "mrf24j40_internal.h"
#include "mrf24j40_netdev.h"
#include "xtimer.h"
#include "ztimer.h"
#define ENABLE_DEBUG 0
#include "debug.h"
void mrf24j40_setup(mrf24j40_t *dev, const mrf24j40_params_t *params, uint8_t index)
{
netdev_t *netdev = &dev->netdev.netdev;
netdev->driver = &mrf24j40_driver;
/* initialize device descriptor */
dev->params = *params;
netdev_register(netdev, NETDEV_MRF24J40, index);
}
int mrf24j40_reset(mrf24j40_t *dev)
{
int res = mrf24j40_init(dev);
int res = mrf24j40_init_hw(dev);
if (res < 0) {
return res;
}
netdev_ieee802154_reset(&dev->netdev);
/* set device address */
netdev_ieee802154_setup(&dev->netdev);
/* set short and long address */
mrf24j40_set_addr_long(dev, dev->netdev.long_addr);
mrf24j40_set_addr_short(dev, unaligned_get_u16(dev->netdev.short_addr));
mrf24j40_set_chan(dev, CONFIG_IEEE802154_DEFAULT_CHANNEL);
/* configure Immediate Sleep and Wake-Up mode */
mrf24j40_reg_write_short(dev, MRF24J40_REG_WAKECON, MRF24J40_WAKECON_IMMWAKE);
/* set default options */
mrf24j40_set_option(dev, IEEE802154_FCF_PAN_COMP, true);
mrf24j40_set_option(dev, NETDEV_IEEE802154_SRC_MODE_LONG, true);
mrf24j40_set_option(dev, NETDEV_IEEE802154_ACK_REQ, true);
mrf24j40_set_option(dev, MRF24J40_OPT_CSMA, true);
/* go into RX state */
mrf24j40_reset_tasks(dev);
dev->state = 0;
mrf24j40_set_state(dev, MRF24J40_PSEUDO_STATE_IDLE);
DEBUG("mrf24j40_reset(): reset complete.\n");
return 0;
}
bool mrf24j40_cca(mrf24j40_t *dev, int8_t *rssi)
{
uint8_t tmp_ccaedth;
uint8_t status;
uint8_t tmp_rssi;
mrf24j40_assert_awake(dev);
mrf24j40_enable_lna(dev);
/* trigger CCA measurement */
/* take a look onto datasheet chapter 3.6.1 */
mrf24j40_reg_write_short(dev, MRF24J40_REG_BBREG6, MRF24J40_BBREG6_RSSIMODE1);
/* wait for result to be ready */
do {
status = mrf24j40_reg_read_short(dev, MRF24J40_REG_BBREG6);
} while (!(status & MRF24J40_BBREG2_RSSIRDY));
mrf24j40_assert_sleep(dev);
/* return according to measurement */
tmp_ccaedth = mrf24j40_reg_read_short(dev, MRF24J40_REG_CCAEDTH); /* Energy detection threshold */
tmp_rssi = mrf24j40_reg_read_long(dev, MRF24J40_REG_RSSI);
if (rssi != NULL) {
*rssi = mrf24j40_dbm_from_reg(tmp_rssi);
}
mrf24j40_enable_auto_pa_lna(dev);
if (tmp_rssi < tmp_ccaedth) {
/* channel is clear */
return true; /* idle */
}
else {
/* channel is busy */
return false;
}
}
void mrf24j40_tx_prepare(mrf24j40_t *dev)
{
DEBUG("[mrf24j40] TX_Prepare, Current state: %x\n", dev->state);
do {
mrf24j40_update_tasks(dev);
} while (!(dev->pending & MRF24J40_TASK_TX_DONE));
mrf24j40_assert_awake(dev);
dev->pending &= ~(MRF24J40_TASK_TX_DONE);
dev->tx_frame_len = IEEE802154_FCS_LEN;
}
size_t mrf24j40_tx_load(mrf24j40_t *dev, uint8_t *data, size_t len, size_t offset)
{
DEBUG("[mrf24j40] TX_load, Current state: %x\n", dev->state);
dev->tx_frame_len += (uint8_t)len;
DEBUG("[mrf24j40] TX_load\n");
mrf24j40_tx_normal_fifo_write(dev, MRF24J40_TX_NORMAL_FIFO + offset + 2, data, len);
return offset + len;
}
void mrf24j40_tx_exec(mrf24j40_t *dev)
{
netdev_t *netdev = &dev->netdev.netdev;
dev->tx_frame_len = dev->tx_frame_len - IEEE802154_FCS_LEN;
/* write frame length field in FIFO */
mrf24j40_tx_normal_fifo_write(dev, MRF24J40_TX_NORMAL_FIFO + 1, &(dev->tx_frame_len), 1);
/* write header length to FIFO address 0x00 */
/* from figure 3-11 datasheet: header length = 2 Bytes Frame Control
* + 1 Byte Seq. No.
* + 4 to 20 Bytes Addressing Fields
*/
mrf24j40_reg_write_long(dev, MRF24J40_TX_NORMAL_FIFO, dev->header_len);
if (dev->fcf_low & IEEE802154_FCF_ACK_REQ) {
mrf24j40_reg_write_short(dev, MRF24J40_REG_TXNCON, MRF24J40_TXNCON_TXNACKREQ | MRF24J40_TXNCON_TXNTRIG);
}
else {
mrf24j40_reg_write_short(dev, MRF24J40_REG_TXNCON, MRF24J40_TXNCON_TXNTRIG);
}
if (netdev->event_callback) {
netdev->event_callback(netdev, NETDEV_EVENT_TX_STARTED);
}
}

View File

@ -24,7 +24,7 @@
#include "mrf24j40.h"
#include "mrf24j40_internal.h"
#include "mrf24j40_registers.h"
#include "xtimer.h"
#include "ztimer.h"
#define ENABLE_DEBUG 0
#include "debug.h"
@ -64,11 +64,6 @@
* 0b11111000 -> -36.3dB-> -36
*/
static const int16_t tx_pow_to_dbm[] = { 0, 0, -1, -2, -3, -4, -5, -6,
-10, -10, -11, -12, -13, -14, -15, -16,
-20, -20, -21, -22, -23, -24, -25, -26,
-30, -30, -31, -32, -33, -34, -35, -36 };
static const uint8_t dbm_to_tx_pow[] = { 0x00, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, 0x38, 0x38, 0x40,
0x40, 0x50, 0x58, 0x60, 0x68, 0x70, 0x78, 0x78, 0x78, 0x80,
0x80, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xb8, 0xb8, 0xc0,
@ -130,15 +125,6 @@ static void mrf24j40_baseband_reset(mrf24j40_t *dev)
} while (softrst != 0); /* wait until soft-reset has finished */
}
uint16_t mrf24j40_get_addr_short(mrf24j40_t *dev)
{
network_uint16_t naddr;
naddr.u8[1] = mrf24j40_reg_read_short(dev, MRF24J40_REG_SADRL);
naddr.u8[0] = mrf24j40_reg_read_short(dev, MRF24J40_REG_SADRH);
return naddr.u16;
}
void mrf24j40_set_addr_short(mrf24j40_t *dev, uint16_t addr)
{
network_uint16_t naddr;
@ -156,13 +142,6 @@ void mrf24j40_set_addr_short(mrf24j40_t *dev, uint16_t addr)
naddr.u8[0]);
}
void mrf24j40_get_addr_long(mrf24j40_t *dev, uint8_t *addr)
{
for (int i = 0; i < 8; i++) {
addr[7 - i] = mrf24j40_reg_read_short(dev, MRF24J40_REG_EADR0 + i);
}
}
void mrf24j40_set_addr_long(mrf24j40_t *dev, const uint8_t *addr)
{
for (int i = 0; i < 8; i++) {
@ -170,11 +149,6 @@ void mrf24j40_set_addr_long(mrf24j40_t *dev, const uint8_t *addr)
}
}
uint8_t mrf24j40_get_chan(mrf24j40_t *dev)
{
return dev->netdev.chan;
}
void mrf24j40_set_chan(mrf24j40_t *dev, uint8_t channel)
{
uint8_t channel_value;
@ -184,8 +158,6 @@ void mrf24j40_set_chan(mrf24j40_t *dev, uint8_t channel)
return;
}
dev->netdev.chan = channel;
/* Channel settings
* 11 -> Value = 0x03
* 12 -> Value = 0x13
@ -251,11 +223,6 @@ void mrf24j40_set_chan(mrf24j40_t *dev, uint8_t channel)
mrf24j40_reset_state_machine(dev);
}
uint16_t mrf24j40_get_pan(mrf24j40_t *dev)
{
return dev->netdev.pan;
}
void mrf24j40_set_pan(mrf24j40_t *dev, uint16_t pan)
{
le_uint16_t le_pan = byteorder_htols(pan);
@ -265,14 +232,6 @@ void mrf24j40_set_pan(mrf24j40_t *dev, uint16_t pan)
mrf24j40_reg_write_short(dev, MRF24J40_REG_PANIDH, le_pan.u8[1]);
}
int16_t mrf24j40_get_txpower(mrf24j40_t *dev)
{
uint8_t txpower;
txpower = (mrf24j40_reg_read_long(dev, MRF24J40_REG_RFCON3) >> 3) & 0x1F;
return tx_pow_to_dbm[txpower];
}
void mrf24j40_set_txpower(mrf24j40_t *dev, int16_t txpower)
{
uint8_t txpower_reg_value;
@ -286,15 +245,6 @@ void mrf24j40_set_txpower(mrf24j40_t *dev, int16_t txpower)
}
uint8_t mrf24j40_get_csma_max_retries(mrf24j40_t *dev)
{
uint8_t tmp;
tmp = mrf24j40_reg_read_short(dev, MRF24J40_REG_TXMCR);
tmp &= MRF24J40_TXMCR_CSMA_BACKOFF_MASK;
return tmp;
}
void mrf24j40_set_csma_max_retries(mrf24j40_t *dev, int8_t retries)
{
uint8_t tmp;
@ -308,15 +258,6 @@ void mrf24j40_set_csma_max_retries(mrf24j40_t *dev, int8_t retries)
mrf24j40_reg_write_short(dev, MRF24J40_REG_TXMCR, tmp);
}
int8_t mrf24j40_get_cca_threshold(mrf24j40_t *dev)
{
int8_t tmp;
tmp = mrf24j40_reg_read_short(dev, MRF24J40_REG_CCAEDTH); /* Energy detection threshold */
return(dBm_value[tmp]); /* in dBm */
}
void mrf24j40_set_cca_threshold(mrf24j40_t *dev, int8_t value)
{
/* ensure the given value is negative, since a CCA threshold > 0 is
@ -350,11 +291,6 @@ void mrf24j40_set_turbo(mrf24j40_t *dev, bool on)
mrf24j40_baseband_reset(dev);
}
bool mrf24j40_get_turbo(mrf24j40_t *dev)
{
return mrf24j40_reg_read_short(dev, MRF24J40_REG_BBREG0);
}
void mrf24j40_set_option(mrf24j40_t *dev, uint16_t option, bool state)
{
uint8_t tmp;
@ -363,7 +299,6 @@ void mrf24j40_set_option(mrf24j40_t *dev, uint16_t option, bool state)
/* set option field */
if (state) {
dev->netdev.flags |= option;
/* trigger option specific actions */
switch (option) {
case MRF24J40_OPT_CSMA:
@ -396,7 +331,6 @@ void mrf24j40_set_option(mrf24j40_t *dev, uint16_t option, bool state)
}
/* clear option field */
else {
dev->netdev.flags &= ~(option);
/* trigger option specific actions */
switch (option) {
case MRF24J40_OPT_CSMA:
@ -415,10 +349,8 @@ void mrf24j40_set_option(mrf24j40_t *dev, uint16_t option, bool state)
tmp &= ~MRF24J40_RXMCR_PROMI;
tmp &= ~MRF24J40_RXMCR_ERRPKT;
/* re-enable AUTOACK only if the option is set */
if (dev->netdev.flags & NETDEV_IEEE802154_ACK_REQ) {
tmp &= ~(MRF24J40_RXMCR_NOACKRSP);
mrf24j40_reg_write_short(dev, MRF24J40_REG_RXMCR, tmp);
}
tmp &= ~(MRF24J40_RXMCR_NOACKRSP);
mrf24j40_reg_write_short(dev, MRF24J40_REG_RXMCR, tmp);
break;
case NETDEV_IEEE802154_ACK_REQ:
DEBUG("[mrf24j40] opt: disabling auto ACKs\n");
@ -433,28 +365,6 @@ void mrf24j40_set_option(mrf24j40_t *dev, uint16_t option, bool state)
}
}
void mrf24j40_set_state(mrf24j40_t *dev, uint8_t state)
{
uint8_t old_state;
old_state = dev->state;
if (state == old_state) {
return;
}
/* check if asked to wake up from sleep mode */
if (old_state == MRF24J40_PSEUDO_STATE_SLEEP) {
mrf24j40_assert_awake(dev);
}
if (state == MRF24J40_PSEUDO_STATE_SLEEP) {
mrf24j40_sleep(dev);
}
if (state == MRF24J40_PSEUDO_STATE_IDLE) {
dev->state = state;
}
dev->idle_state = state;
}
void mrf24j40_sleep(mrf24j40_t *dev)
{
DEBUG("[mrf24j40] Putting into sleep mode\n");
@ -466,44 +376,33 @@ void mrf24j40_sleep(mrf24j40_t *dev)
mrf24j40_reg_write_short(dev, MRF24J40_REG_SOFTRST, MRF24J40_SOFTRST_RSTPWR);
/* Go to SLEEP mode */
mrf24j40_reg_write_short(dev, MRF24J40_REG_SLPACK, MRF24J40_SLPACK_SLPACK);
dev->state = MRF24J40_PSEUDO_STATE_SLEEP;
}
void mrf24j40_assert_sleep(mrf24j40_t *dev)
void mrf24j40_wake_up(mrf24j40_t *dev)
{
if (dev->idle_state == MRF24J40_PSEUDO_STATE_SLEEP) {
mrf24j40_sleep(dev);
}
}
void mrf24j40_assert_awake(mrf24j40_t *dev)
{
if (dev->state == MRF24J40_PSEUDO_STATE_SLEEP) {
DEBUG("[mrf24j40] Waking up from sleep mode\n");
/* Wake mrf up */
mrf24j40_reg_write_short(dev, MRF24J40_REG_WAKECON, MRF24J40_WAKECON_IMMWAKE | MRF24J40_WAKECON_REGWAKE);
/* undocumented delay, needed for stable wakeup */
xtimer_usleep(MRF24J40_DELAY_SLEEP_TOGGLE);
mrf24j40_reg_write_short(dev, MRF24J40_REG_WAKECON, MRF24J40_WAKECON_IMMWAKE);
/* reset state machine */
mrf24j40_reg_write_short(dev, MRF24J40_REG_RFCTL, MRF24J40_RFCTL_RFRST);
mrf24j40_reg_write_short(dev, MRF24J40_REG_RFCTL, 0x00);
/* After wake-up, delay at least 2 ms to allow 20 MHz main
* oscillator time to stabilize before transmitting or receiving.
*/
xtimer_usleep(MRF24J40_WAKEUP_DELAY);
/* reset interrupts */
mrf24j40_reg_read_short(dev, MRF24J40_REG_INTSTAT);
mrf24j40_enable_auto_pa_lna(dev);
dev->state = MRF24J40_PSEUDO_STATE_IDLE;
}
DEBUG("[mrf24j40] Waking up from sleep mode\n");
/* Wake mrf up */
mrf24j40_reg_write_short(dev, MRF24J40_REG_WAKECON, MRF24J40_WAKECON_IMMWAKE | MRF24J40_WAKECON_REGWAKE);
/* undocumented delay, needed for stable wakeup */
ztimer_sleep(ZTIMER_USEC, MRF24J40_DELAY_SLEEP_TOGGLE);
mrf24j40_reg_write_short(dev, MRF24J40_REG_WAKECON, MRF24J40_WAKECON_IMMWAKE);
/* reset state machine */
mrf24j40_reg_write_short(dev, MRF24J40_REG_RFCTL, MRF24J40_RFCTL_RFRST);
mrf24j40_reg_write_short(dev, MRF24J40_REG_RFCTL, 0x00);
/* After wake-up, delay at least 2 ms to allow 20 MHz main
* oscillator time to stabilize before transmitting or receiving.
*/
ztimer_sleep(ZTIMER_USEC, MRF24J40_WAKEUP_DELAY);
/* reset interrupts */
mrf24j40_reg_read_short(dev, MRF24J40_REG_INTSTAT);
mrf24j40_enable_auto_pa_lna(dev);
}
void mrf24j40_reset_state_machine(mrf24j40_t *dev)
{
mrf24j40_reg_write_short(dev, MRF24J40_REG_RFCTL, MRF24J40_RFCTL_RFRST);
mrf24j40_reg_write_short(dev, MRF24J40_REG_RFCTL, 0x00);
xtimer_usleep(MRF24J40_STATE_RESET_DELAY); /* Delay at least 192us */
ztimer_sleep(ZTIMER_USEC, MRF24J40_STATE_RESET_DELAY); /* Delay at least 192us */
}
void mrf24j40_software_reset(mrf24j40_t *dev)

View File

@ -21,7 +21,7 @@
*/
#include "periph/spi.h"
#include "periph/gpio.h"
#include "xtimer.h"
#include "ztimer.h"
#include "mrf24j40_internal.h"
#include "mrf24j40_registers.h"
#include "kernel_defines.h"
@ -29,12 +29,12 @@
#define ENABLE_DEBUG 0
#include "debug.h"
#define SPIDEV (dev->params.spi)
#define CSPIN (dev->params.cs_pin)
#define SPIDEV (dev->params->spi)
#define CSPIN (dev->params->cs_pin)
static inline void getbus(mrf24j40_t *dev)
{
spi_acquire(SPIDEV, CSPIN, SPI_MODE_0, dev->params.spi_clk);
spi_acquire(SPIDEV, CSPIN, SPI_MODE_0, dev->params->spi_clk);
}
#if IS_ACTIVE(CONFIG_MRF24J40_USE_EXT_PA_LNA)
@ -99,7 +99,7 @@ void mrf24j40_enable_lna(mrf24j40_t *dev)
}
#endif /* CONFIG_MRF24J40_USE_EXT_PA_LNA */
int mrf24j40_init(mrf24j40_t *dev)
int mrf24j40_init_hw(mrf24j40_t *dev)
{
if (IS_ACTIVE(CONFIG_MRF24J40_TEST_SPI_CONNECTION)) {
/* Check if MRF24J40 is available */
@ -263,36 +263,34 @@ void mrf24j40_reset_tasks(mrf24j40_t *dev)
void mrf24j40_update_tasks(mrf24j40_t *dev)
{
if (dev->irq_flag) {
uint8_t newpending = 0;
uint8_t instat = 0;
uint8_t newpending = 0;
uint8_t instat = 0;
dev->irq_flag = 0;
instat = mrf24j40_reg_read_short(dev, MRF24J40_REG_INTSTAT);
/* check if TX done */
if (instat & MRF24J40_INTSTAT_TXNIF) {
newpending |= MRF24J40_TASK_TX_DONE | MRF24J40_TASK_TX_READY;
/* transmit done, returning to configured idle state */
mrf24j40_assert_sleep(dev);
}
if (instat & MRF24J40_INTSTAT_RXIF) {
newpending |= MRF24J40_TASK_RX_READY;
}
/* check if RX pending */
dev->pending |= newpending;
instat = mrf24j40_reg_read_short(dev, MRF24J40_REG_INTSTAT);
/* check if TX done */
if (instat & MRF24J40_INTSTAT_TXNIF) {
newpending |= MRF24J40_TASK_TX_DONE | MRF24J40_TASK_TX_READY;
/* transmit done, returning to configured idle state */
}
if (instat & MRF24J40_INTSTAT_RXIF) {
newpending |= MRF24J40_TASK_RX_READY;
}
/* check if RX pending */
dev->pending |= newpending;
}
void mrf24j40_hardware_reset(mrf24j40_t *dev)
{
/* wake up from sleep in case radio is sleeping */
mrf24j40_assert_awake(dev);
/* trigger hardware reset */
gpio_clear(dev->params.reset_pin);
gpio_clear(dev->params->reset_pin);
/* Datasheet - Not specified */
xtimer_usleep(MRF24J40_RESET_PULSE_WIDTH);
gpio_set(dev->params.reset_pin);
ztimer_sleep(ZTIMER_USEC, MRF24J40_RESET_PULSE_WIDTH);
gpio_set(dev->params->reset_pin);
/* Datasheet - MRF24J40 ~2ms */
xtimer_usleep(MRF24J40_RESET_DELAY);
ztimer_sleep(ZTIMER_USEC, MRF24J40_RESET_DELAY);
}
void mrf24j40_flush_rx(mrf24j40_t *dev)
{
mrf24j40_reg_write_short(dev, MRF24J40_REG_RXFLUSH, MRF24J40_RXFLUSH_RXFLUSH);
}

View File

@ -1,581 +0,0 @@
/*
* Copyright (C) 2017 Neo Nenaco <neo@nenaco.de>
* Copyright (C) 2017 Koen Zandberg <koen@bergzand.net>
*
* 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_mrf24j40
* @{
*
* @file
* @brief Netdev adaption for the MRF24J40 drivers
*
* @author Koen Zandberg <koen@bergzand.net>
* @author Neo Nenaco <neo@nenaco.de>
*
* @}
*/
#include <string.h>
#include <assert.h>
#include <errno.h>
#include "net/eui64.h"
#include "net/ieee802154.h"
#include "net/netdev.h"
#include "net/netdev/ieee802154.h"
#include "mrf24j40.h"
#include "mrf24j40_netdev.h"
#include "mrf24j40_internal.h"
#include "mrf24j40_registers.h"
#define ENABLE_DEBUG 0
#include "debug.h"
static void _irq_handler(void *arg)
{
netdev_t *dev = arg;
netdev_ieee802154_t *netdev_ieee802154 = container_of(dev, netdev_ieee802154_t, netdev);
mrf24j40_t *mrf24j40 = container_of(netdev_ieee802154, mrf24j40_t, netdev);
netdev_trigger_event_isr(dev);
mrf24j40->irq_flag = 1;
}
static int _init(netdev_t *netdev)
{
netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev);
mrf24j40_t *dev = container_of(netdev_ieee802154, mrf24j40_t, netdev);
/* initialize GPIOs */
spi_init_cs(dev->params.spi, dev->params.cs_pin);
gpio_init(dev->params.reset_pin, GPIO_OUT);
gpio_set(dev->params.reset_pin);
gpio_init_int(dev->params.int_pin, GPIO_IN, GPIO_RISING, _irq_handler, dev);
/* reset device to default values and put it into RX state */
if (mrf24j40_reset(dev)) {
return -ENODEV;
}
/* signal link UP */
netdev->event_callback(netdev, NETDEV_EVENT_LINK_UP);
return 0;
}
static int _send(netdev_t *netdev, const iolist_t *iolist)
{
netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev);
mrf24j40_t *dev = container_of(netdev_ieee802154, mrf24j40_t, netdev);
size_t len = 0;
mrf24j40_tx_prepare(dev);
/* load packet data into FIFO */
for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
/* Check if there is data to copy, prevents assertion failure in the
* SPI peripheral if there is no data to copy */
if (iol->iol_len) {
/* current packet data + FCS too long */
if ((len + iol->iol_len + 2) > IEEE802154_FRAME_LEN_MAX) {
DEBUG("[mrf24j40] error: packet too large (%u byte) to be send\n",
(unsigned)len + 2);
return -EOVERFLOW;
}
len = mrf24j40_tx_load(dev, iol->iol_base, iol->iol_len, len);
}
/* only on first iteration: */
if (iol == iolist) {
dev->header_len = len;
/* Grab the FCF bits from the frame header */
dev->fcf_low = *(uint8_t*)(iol->iol_base);
}
}
/* send data out directly if pre-loading is disabled */
if (!(dev->netdev.flags & MRF24J40_OPT_PRELOADING)) {
mrf24j40_tx_exec(dev);
}
/* return the number of bytes that were actually send out */
return (int)len;
}
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);
mrf24j40_t *dev = container_of(netdev_ieee802154, mrf24j40_t, netdev);
uint8_t phr;
size_t pkt_len;
int res = -ENOBUFS;
/* Disable receiving while reading the RX fifo (datasheet sec. 3.11.4) */
mrf24j40_reg_write_short(dev, MRF24J40_REG_BBREG1, MRF24J40_BBREG1_RXDECINV );
/* get the size of the received packet, this resets the receive FIFO to be
* ready for a new frame, so we have to disable RX first (see above) */
phr = mrf24j40_reg_read_long(dev, MRF24J40_RX_FIFO);
pkt_len = (phr & 0x7f) - 2;
/* just return length when buf == NULL */
if (buf == NULL && len == 0) {
return pkt_len;
}
/* Only fill buffer if it is supplied and is large enough */
if (buf && pkt_len <= len) {
/* copy payload */
mrf24j40_rx_fifo_read(dev, 1, (uint8_t *)buf, pkt_len);
if (info != NULL) {
netdev_ieee802154_rx_info_t *radio_info = info;
uint8_t rssi_scalar = 0;
/* Read LQI and RSSI values from the RX fifo */
mrf24j40_rx_fifo_read(dev, phr + 1, &(radio_info->lqi), 1);
mrf24j40_rx_fifo_read(dev, phr + 2, &(rssi_scalar), 1);
radio_info->rssi = mrf24j40_dbm_from_reg(rssi_scalar);
}
res = pkt_len;
}
/* Turn on reception of packets off the air */
mrf24j40_reg_write_short(dev, MRF24J40_REG_BBREG1, 0x00);
/* Return -ENOBUFS if a too small buffer is supplied. Return packet size
* otherwise */
return res;
}
static netopt_state_t _get_state(mrf24j40_t *dev)
{
if (!(dev->pending & MRF24J40_TASK_TX_DONE)) {
return NETOPT_STATE_TX;
}
if (dev->pending & MRF24J40_TASK_RX_READY) {
return NETOPT_STATE_RX;
}
switch (dev->state) {
case MRF24J40_PSEUDO_STATE_SLEEP:
return NETOPT_STATE_SLEEP;
}
return NETOPT_STATE_IDLE;
}
static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len)
{
netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev);
mrf24j40_t *dev = container_of(netdev_ieee802154, mrf24j40_t, netdev);
if (netdev == NULL) {
return -ENODEV;
}
int res;
switch (opt) {
case NETOPT_ADDRESS:
if (max_len < sizeof(uint16_t)) {
res = -EOVERFLOW;
}
else {
*(uint16_t*)val = mrf24j40_get_addr_short(dev);
res = sizeof(uint16_t);
}
break;
case NETOPT_ADDRESS_LONG:
if (max_len < sizeof(uint64_t)) {
res = -EOVERFLOW;
}
else {
mrf24j40_get_addr_long(dev, val);
res = sizeof(uint64_t);
}
break;
case NETOPT_CHANNEL_PAGE:
if (max_len < sizeof(uint16_t)) {
res = -EOVERFLOW;
}
else {
((uint8_t *)val)[1] = 0;
((uint8_t *)val)[0] = 0;
res = sizeof(uint16_t);
}
break;
case NETOPT_STATE:
if (max_len < sizeof(netopt_state_t)) {
res = -EOVERFLOW;
}
else {
*((netopt_state_t *)val) = _get_state(dev);
res = sizeof(netopt_state_t);
}
break;
case NETOPT_PRELOADING:
if (dev->netdev.flags & MRF24J40_OPT_PRELOADING) {
*((netopt_enable_t *)val) = NETOPT_ENABLE;
}
else {
*((netopt_enable_t *)val) = NETOPT_DISABLE;
}
res = sizeof(netopt_enable_t);
break;
case NETOPT_PROMISCUOUSMODE:
if (dev->netdev.flags & MRF24J40_OPT_PROMISCUOUS) {
*((netopt_enable_t *)val) = NETOPT_ENABLE;
}
else {
*((netopt_enable_t *)val) = NETOPT_DISABLE;
}
res = sizeof(netopt_enable_t);
break;
case NETOPT_RX_START_IRQ:
case NETOPT_TX_START_IRQ:
case NETOPT_TX_END_IRQ:
*((netopt_enable_t *)val) = NETOPT_ENABLE;
res = sizeof(netopt_enable_t);
break;
case NETOPT_CSMA:
*((netopt_enable_t *)val) =
!!(dev->netdev.flags & MRF24J40_OPT_CSMA);
res = sizeof(netopt_enable_t);
break;
case NETOPT_TX_POWER:
if (max_len < sizeof(int16_t)) {
res = -EOVERFLOW;
}
else {
*((uint16_t *)val) = mrf24j40_get_txpower(dev);
res = sizeof(uint16_t);
}
break;
case NETOPT_RETRANS:
if (max_len < sizeof(uint8_t)) {
res = -EOVERFLOW;
}
else {
*((uint8_t *)val) = MRF24J40_MAX_FRAME_RETRIES;
res = sizeof(uint8_t);
}
break;
case NETOPT_IS_CHANNEL_CLR:
if (mrf24j40_cca(dev, NULL)) {
*((netopt_enable_t *)val) = NETOPT_ENABLE;
}
else {
*((netopt_enable_t *)val) = NETOPT_DISABLE;
}
res = sizeof(netopt_enable_t);
break;
case NETOPT_LAST_ED_LEVEL:
if (max_len < sizeof(int8_t)) {
res = -EOVERFLOW;
}
else {
mrf24j40_cca(dev, (int8_t *)val);
res = sizeof(int8_t);
}
break;
case NETOPT_CSMA_RETRIES:
if (max_len < sizeof(uint8_t)) {
res = -EOVERFLOW;
}
else {
*((uint8_t *)val) = mrf24j40_get_csma_max_retries(dev);
res = sizeof(uint8_t);
}
break;
case NETOPT_CCA_THRESHOLD:
if (max_len < sizeof(int8_t)) {
res = -EOVERFLOW;
}
else {
*((int8_t *)val) = mrf24j40_get_cca_threshold(dev);
res = sizeof(int8_t);
}
break;
case NETOPT_TX_RETRIES_NEEDED:
if (max_len < sizeof(uint8_t)) {
res = -EOVERFLOW;
}
else {
*((uint8_t *)val) = dev->tx_retries;
res = sizeof(int8_t);
}
break;
#ifdef MODULE_NETDEV_IEEE802154_OQPSK
case NETOPT_IEEE802154_PHY:
assert(max_len >= sizeof(int8_t));
*(uint8_t *)val = IEEE802154_PHY_OQPSK;
return sizeof(uint8_t);
case NETOPT_OQPSK_RATE:
assert(max_len >= sizeof(int8_t));
*(uint8_t *)val = mrf24j40_get_turbo(dev);
return sizeof(uint8_t);
#endif /* MODULE_NETDEV_IEEE802154_OQPSK */
default:
/* try netdev settings */
res = netdev_ieee802154_get(container_of(netdev, netdev_ieee802154_t, netdev),
opt, val, max_len);
}
return res;
}
static int _set_state(mrf24j40_t *dev, netopt_state_t state)
{
switch (state) {
case NETOPT_STATE_SLEEP:
mrf24j40_set_state(dev, MRF24J40_PSEUDO_STATE_SLEEP);
break;
case NETOPT_STATE_IDLE:
mrf24j40_set_state(dev, MRF24J40_PSEUDO_STATE_IDLE);
break;
case NETOPT_STATE_TX:
if (dev->netdev.flags & MRF24J40_OPT_PRELOADING) {
mrf24j40_tx_exec(dev);
}
break;
case NETOPT_STATE_RESET:
mrf24j40_reset(dev);
break;
default:
return -ENOTSUP;
}
return sizeof(netopt_state_t);
}
static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t len)
{
netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev);
mrf24j40_t *dev = container_of(netdev_ieee802154, mrf24j40_t, netdev);
int res = -ENOTSUP;
if (dev == NULL) {
return -ENODEV;
}
switch (opt) {
case NETOPT_ADDRESS:
if (len > sizeof(uint16_t)) {
res = -EOVERFLOW;
}
else {
mrf24j40_set_addr_short(dev, *((const uint16_t *)val));
res = sizeof(uint16_t);
}
break;
case NETOPT_ADDRESS_LONG:
if (len > sizeof(uint64_t)) {
res = -EOVERFLOW;
}
else {
mrf24j40_set_addr_long(dev, val);
res = sizeof(uint64_t);
}
break;
case NETOPT_NID:
if (len > sizeof(uint16_t)) {
res = -EOVERFLOW;
}
else {
mrf24j40_set_pan(dev, *((const uint16_t *)val));
/* don't set res to set netdev_ieee802154_t::pan */
}
break;
case NETOPT_CHANNEL:
if (len != sizeof(uint16_t)) {
res = -EINVAL;
}
else {
uint8_t chan = ((const uint8_t *)val)[0];
if (chan < IEEE802154_CHANNEL_MIN ||
chan > IEEE802154_CHANNEL_MAX ||
dev->netdev.chan == chan) {
res = -EINVAL;
break;
}
mrf24j40_set_chan(dev, chan);
/* don't set res to set netdev_ieee802154_t::chan */
}
break;
case NETOPT_CHANNEL_PAGE:
if (len != sizeof(uint16_t)) {
res = -EINVAL;
}
else {
uint8_t page = ((const uint8_t *)val)[0];
/* mrf24j40 only supports page 0, no need to configure anything in the driver. */
if (page != 0) {
res = -EINVAL;
}
else {
res = sizeof(uint16_t);
}
}
break;
case NETOPT_TX_POWER:
if (len > sizeof(int16_t)) {
res = -EOVERFLOW;
}
else {
mrf24j40_set_txpower(dev, *((const int16_t *)val));
res = sizeof(uint16_t);
}
break;
case NETOPT_STATE:
if (len > sizeof(netopt_state_t)) {
res = -EOVERFLOW;
}
else {
res = _set_state(dev, *((const netopt_state_t *)val));
}
break;
case NETOPT_AUTOACK:
mrf24j40_set_option(dev, NETDEV_IEEE802154_ACK_REQ,
((const bool *)val)[0]);
/* don't set res to set netdev_ieee802154_t::flags */
break;
case NETOPT_PRELOADING:
mrf24j40_set_option(dev, MRF24J40_OPT_PRELOADING,
((const bool *)val)[0]);
res = sizeof(netopt_enable_t);
break;
case NETOPT_PROMISCUOUSMODE:
mrf24j40_set_option(dev, MRF24J40_OPT_PROMISCUOUS,
((const bool *)val)[0]);
res = sizeof(netopt_enable_t);
break;
case NETOPT_CSMA:
mrf24j40_set_option(dev, MRF24J40_OPT_CSMA,
((const bool *)val)[0]);
res = sizeof(netopt_enable_t);
break;
case NETOPT_CSMA_RETRIES:
if ((len > sizeof(uint8_t)) ||
(*((const uint8_t *)val) > 5)) {
res = -EOVERFLOW;
}
else if (dev->netdev.flags & MRF24J40_OPT_CSMA) {
/* only set if CSMA is enabled */
mrf24j40_set_csma_max_retries(dev, *((const uint8_t *)val));
res = sizeof(uint8_t);
}
break;
case NETOPT_CCA_THRESHOLD:
if (len > sizeof(int8_t)) {
res = -EOVERFLOW;
}
else {
mrf24j40_set_cca_threshold(dev, *((const int8_t *)val));
res = sizeof(int8_t);
}
break;
#ifdef MODULE_NETDEV_IEEE802154_OQPSK
case NETOPT_OQPSK_RATE:
res = !!*(uint8_t *)val;
mrf24j40_set_turbo(dev, res);
res = sizeof(uint8_t);
break;
#endif /* MODULE_NETDEV_IEEE802154_OQPSK */
default:
break;
}
/* try netdev building flags */
if (res == -ENOTSUP) {
res = netdev_ieee802154_set(container_of(netdev, netdev_ieee802154_t, netdev),
opt, val, len);
}
return res;
}
static void _isr(netdev_t *netdev)
{
netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev);
mrf24j40_t *dev = container_of(netdev_ieee802154, mrf24j40_t, netdev);
/* update pending bits */
mrf24j40_update_tasks(dev);
DEBUG("[mrf24j40] INTERRUPT (pending: %x),\n", dev->pending);
/* Transmit interrupt occurred */
if (dev->pending & MRF24J40_TASK_TX_READY) {
dev->pending &= ~(MRF24J40_TASK_TX_READY);
DEBUG("[mrf24j40] EVT - TX_END\n");
#ifdef MODULE_NETSTATS_L2
if (netdev->event_callback) {
uint8_t txstat = mrf24j40_reg_read_short(dev, MRF24J40_REG_TXSTAT);
dev->tx_retries = (txstat >> MRF24J40_TXSTAT_MAX_FRAME_RETRIES_SHIFT);
/* transmission failed */
if (txstat & MRF24J40_TXSTAT_TXNSTAT) {
/* TX_NOACK - CCAFAIL */
if (txstat & MRF24J40_TXSTAT_CCAFAIL) {
netdev->event_callback(netdev, NETDEV_EVENT_TX_MEDIUM_BUSY);
DEBUG("[mrf24j40] TX_CHANNEL_ACCESS_FAILURE\n");
}
/* check max retries */
else if (txstat & MRF24J40_TXSTAT_MAX_FRAME_RETRIES) {
netdev->event_callback(netdev, NETDEV_EVENT_TX_NOACK);
DEBUG("[mrf24j40] TX NO_ACK\n");
}
}
else {
netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE);
}
}
#endif
}
/* Receive interrupt occurred */
if (dev->pending & MRF24J40_TASK_RX_READY) {
DEBUG("[mrf24j40] EVT - RX_END\n");
netdev->event_callback(netdev, NETDEV_EVENT_RX_COMPLETE);
dev->pending &= ~(MRF24J40_TASK_RX_READY);
}
DEBUG("[mrf24j40] END IRQ\n");
}
const netdev_driver_t mrf24j40_driver = {
.send = _send,
.recv = _recv,
.init = _init,
.isr = _isr,
.get = _get,
.set = _set,
};

View File

@ -0,0 +1,374 @@
#include "mrf24j40.h"
#include "mrf24j40_params.h"
#include "mrf24j40_internal.h"
#include "mrf24j40_registers.h"
#include "net/ieee802154/radio.h"
#define ENABLE_DEBUG 0
#include "debug.h"
static const ieee802154_radio_ops_t mrf24j40_ops;
void mrf24j40_radio_irq_handler(void *ctx)
{
ieee802154_dev_t *hal = ctx;
mrf24j40_t *dev = hal->priv;
/* update pending bits */
mrf24j40_update_tasks(dev);
DEBUG("[mrf24j40] INTERRUPT (pending: %x),\n", dev->pending);
/* Transmit interrupt occurred */
if (dev->pending & MRF24J40_TASK_TX_READY) {
hal->cb(hal, IEEE802154_RADIO_CONFIRM_TX_DONE);
}
/* Receive interrupt occurred */
if (dev->pending & MRF24J40_TASK_RX_READY) {
DEBUG("[mrf24j40] EVT - RX_END\n");
dev->pending &= ~(MRF24J40_TASK_RX_READY);
/* Prevent race condition if there is an ongoing transmission */
if (dev->tx_pending) {
mrf24j40_flush_rx(dev);
}
else {
hal->cb(hal, IEEE802154_RADIO_INDICATION_RX_DONE);
}
}
DEBUG("[mrf24j40] END IRQ\n");
}
static int _write(ieee802154_dev_t *hal, const iolist_t *iolist)
{
uint8_t len = 0;
mrf24j40_t *dev = hal->priv;
/* load packet data into FIFO */
for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
/* Check if there is data to copy, prevents assertion failure in the
* SPI peripheral if there is no data to copy */
if (iol->iol_len) {
/* current packet data + FCS too long */
len = mrf24j40_tx_load(dev, iol->iol_base, iol->iol_len, len);
}
/* only on first iteration: */
if (iol == iolist) {
/* Grab the FCF bits from the frame header */
dev->fcf_low = *(uint8_t*)(iol->iol_base);
}
}
/* write frame length field in FIFO */
mrf24j40_tx_normal_fifo_write(dev, MRF24J40_TX_NORMAL_FIFO + 1, &len, 1);
return 0;
}
int mrf24j40_init(mrf24j40_t *dev, const mrf24j40_params_t *params, ieee802154_dev_t *hal,
gpio_cb_t cb, void *ctx)
{
hal->driver = &mrf24j40_ops;
hal->priv = dev;
dev->params = params;
/* initialize GPIOs */
spi_init_cs(dev->params->spi, dev->params->cs_pin);
gpio_init(dev->params->reset_pin, GPIO_OUT);
gpio_set(dev->params->reset_pin);
gpio_init_int(dev->params->int_pin, GPIO_IN, GPIO_RISING, cb, ctx);
/* reset device to default values */
if (mrf24j40_reset(dev)) {
return -ENODEV;
}
/* Set device to SLEEP */
mrf24j40_sleep(dev);
return 0;
}
static int _read(ieee802154_dev_t *hal, void *buf, size_t size, ieee802154_rx_info_t *info)
{
uint8_t phr;
size_t pkt_len;
int res = -ENOBUFS;
mrf24j40_t *dev = hal->priv;
phr = mrf24j40_reg_read_long(dev, MRF24J40_RX_FIFO);
pkt_len = (phr & 0x7f) - IEEE802154_FCS_LEN;
if (buf) {
if (pkt_len > size) {
mrf24j40_flush_rx(dev);
return -ENOBUFS;
}
mrf24j40_rx_fifo_read(dev, 1, (uint8_t *)buf, pkt_len);
if (info != NULL) {
uint8_t rssi_scalar = 0;
/* Read LQI and RSSI values from the RX fifo */
mrf24j40_rx_fifo_read(dev, phr + 1, &(info->lqi), 1);
mrf24j40_rx_fifo_read(dev, phr + 2, &(rssi_scalar), 1);
info->rssi = mrf24j40_dbm_from_reg(rssi_scalar);
}
res = pkt_len;
}
else {
mrf24j40_flush_rx(dev);
}
/* Return -ENOBUFS if a too small buffer is supplied. Return packet size
* otherwise */
return res;
}
static int _len(ieee802154_dev_t *hal)
{
mrf24j40_t *dev = hal->priv;
return (mrf24j40_reg_read_long(dev, MRF24J40_RX_FIFO) & 0x7f) - IEEE802154_FCS_LEN;
}
static int _off(ieee802154_dev_t *hal)
{
mrf24j40_t *dev = hal->priv;
mrf24j40_sleep(dev);
return -ENOTSUP;
}
static int _request_on(ieee802154_dev_t *hal)
{
mrf24j40_t *dev = hal->priv;
mrf24j40_wake_up(dev);
return 0;
}
static int _confirm_on(ieee802154_dev_t *hal)
{
(void) hal;
/* Nothing to do here */
return 0;
}
static int _request_op(ieee802154_dev_t *hal, ieee802154_hal_op_t op, void *ctx)
{
int res = -EBUSY;
mrf24j40_t *dev = hal->priv;
(void) ctx;
switch (op) {
case IEEE802154_HAL_OP_TRANSMIT:
if (dev->fcf_low & IEEE802154_FCF_ACK_REQ) {
mrf24j40_reg_write_short(dev, MRF24J40_REG_TXNCON, MRF24J40_TXNCON_TXNACKREQ | MRF24J40_TXNCON_TXNTRIG);
mrf24j40_reg_write_short(dev, MRF24J40_REG_BBREG1, 0x00);
}
else {
mrf24j40_reg_write_short(dev, MRF24J40_REG_TXNCON, MRF24J40_TXNCON_TXNTRIG);
}
dev->tx_pending = true;
res = 0;
break;
case IEEE802154_HAL_OP_SET_RX:
mrf24j40_reg_write_short(dev, MRF24J40_REG_BBREG1, 0x00);
res = 0;
mrf24j40_reg_write_short(dev, MRF24J40_REG_INTCON, ~(MRF24J40_INTCON_RXIE));
break;
case IEEE802154_HAL_OP_SET_IDLE:
mrf24j40_reg_write_short(dev, MRF24J40_REG_BBREG1, MRF24J40_BBREG1_RXDECINV );
mrf24j40_reg_write_short(dev, MRF24J40_REG_INTCON, ~(MRF24J40_INTCON_TXNIE));
res = 0;
break;
case IEEE802154_HAL_OP_CCA:
mrf24j40_enable_lna(dev);
mrf24j40_reg_write_short(dev, MRF24J40_REG_BBREG6, MRF24J40_BBREG6_RSSIMODE1);
mrf24j40_reg_write_short(dev, MRF24J40_REG_INTCON, 0xFF);
res = 0;
break;
}
return res;
}
static int _confirm_op(ieee802154_dev_t *hal, ieee802154_hal_op_t op, void *ctx)
{
int res = -EAGAIN;
mrf24j40_t *dev = hal->priv;
uint8_t tmp_ccaedth;
uint8_t tmp_rssi;
switch (op) {
case IEEE802154_HAL_OP_TRANSMIT:
if (dev->pending & MRF24J40_TASK_TX_READY) {
dev->pending &= ~(MRF24J40_TASK_TX_READY);
if (ctx) {
uint8_t txstat = mrf24j40_reg_read_short(dev, MRF24J40_REG_TXSTAT);
ieee802154_tx_info_t *info = ctx;
if (txstat & MRF24J40_TXSTAT_TXNSTAT) {
info->status = (txstat & MRF24J40_TXSTAT_CCAFAIL)
? TX_STATUS_MEDIUM_BUSY
: TX_STATUS_NO_ACK;
}
else {
info->status = TX_STATUS_SUCCESS;
}
info->retrans = txstat >> MRF24J40_TXSTAT_MAX_FRAME_RETRIES_SHIFT;
}
mrf24j40_reg_write_short(dev, MRF24J40_REG_BBREG1, MRF24J40_BBREG1_RXDECINV);
dev->tx_pending = false;
return 0;
}
break;
case IEEE802154_HAL_OP_SET_RX:
case IEEE802154_HAL_OP_SET_IDLE:
res = 0;
break;
case IEEE802154_HAL_OP_CCA:
if ((mrf24j40_reg_read_short(dev, MRF24J40_REG_BBREG6) & MRF24J40_BBREG2_RSSIRDY)) {
tmp_ccaedth = mrf24j40_reg_read_short(dev, MRF24J40_REG_CCAEDTH); /* Energy detection threshold */
tmp_rssi = mrf24j40_reg_read_long(dev, MRF24J40_REG_RSSI);
mrf24j40_enable_auto_pa_lna(dev);
*((bool*) ctx) = (tmp_rssi < tmp_ccaedth) ? true : false;
res = 0;
}
break;
}
return res;
}
static int _set_cca_threshold(ieee802154_dev_t *hal, int8_t threshold)
{
mrf24j40_t *dev = hal->priv;
mrf24j40_set_cca_threshold(dev, threshold);
return 0;
}
static int _set_cca_mode(ieee802154_dev_t *dev, ieee802154_cca_mode_t mode)
{
(void) dev;
(void) mode;
if (mode != IEEE802154_CCA_MODE_ED_THRESHOLD) {
return -ENOTSUP;
}
return 0;
}
static int _config_phy(ieee802154_dev_t *hal, const ieee802154_phy_conf_t *conf)
{
mrf24j40_t *dev = hal->priv;
int8_t pow = conf->pow;
uint8_t channel = conf->channel;
if (pow < MRF24J40_MIN_TXPOWER && pow > MRF24J40_MAX_TXPOWER) {
return -EINVAL;
}
mrf24j40_set_txpower(dev, pow);
mrf24j40_set_chan(dev, channel);
return 0;
}
static int _set_csma_params(ieee802154_dev_t *hal, const ieee802154_csma_be_t *bd, int8_t retries)
{
mrf24j40_t *dev = hal->priv;
uint8_t tmp;
if (bd->min > MRF24J40_MAX_MINBE) {
return -EINVAL;
}
tmp = mrf24j40_reg_read_short(dev, MRF24J40_REG_TXMCR) & ~MRF24J40_TXMCR_MACMINBE;
if (retries >= 0) {
tmp |= bd->min << MRF24J40_TXMCR_MACMINBE_SHIFT;
}
/* This radio ignores max_be */
mrf24j40_set_csma_max_retries(dev, retries);
return 0;
}
static int _config_addr_filter(ieee802154_dev_t *hal, ieee802154_af_cmd_t cmd, const void *value)
{
mrf24j40_t *dev = hal->priv;
switch (cmd) {
case IEEE802154_AF_SHORT_ADDR:
mrf24j40_set_addr_short(dev, *((const uint16_t *)value));
break;
case IEEE802154_AF_EXT_ADDR:
mrf24j40_set_addr_long(dev, value);
break;
case IEEE802154_AF_PANID:
mrf24j40_set_pan(dev, *((const uint16_t *)value));
break;
case IEEE802154_AF_PAN_COORD:
return -ENOTSUP;
}
return 0;
}
static int _config_src_addr_match(ieee802154_dev_t *hal, ieee802154_src_match_t cmd, const void *value)
{
mrf24j40_t *dev = hal->priv;
uint8_t tmp = mrf24j40_reg_read_short(dev, MRF24J40_REG_ACKTMOUT) & ~MRF24J40_ACKTMOUT_DRPACK;
bool en;
switch (cmd) {
case IEEE802154_SRC_MATCH_EN:
en = *((bool*) value);
tmp = en ? tmp | MRF24J40_ACKTMOUT_DRPACK : tmp;
mrf24j40_reg_write_short(dev, MRF24J40_REG_ACKTMOUT, tmp);
return 0;
default:
return -ENOTSUP;
}
}
static int _set_frame_filter_mode(ieee802154_dev_t *hal, ieee802154_filter_mode_t mode)
{
mrf24j40_t *dev = hal->priv;
switch (mode) {
case IEEE802154_FILTER_ACCEPT:
mrf24j40_set_option(dev, MRF24J40_OPT_PROMISCUOUS, false);
break;
case IEEE802154_FILTER_PROMISC:
mrf24j40_set_option(dev, MRF24J40_OPT_PROMISCUOUS, true);
break;
case IEEE802154_FILTER_ACK_ONLY:
default:
return -ENOTSUP;
}
return 0;
}
int _set_frame_retrans(ieee802154_dev_t *hal, uint8_t retrans)
{
(void) hal;
(void) retrans;
/* This radio does not allow to set the number of retransmission, but this
* must still be defined because this radio declares
* IEEE802154_CAP_FRAME_RETRANS */
return -ENOTSUP;
}
static const ieee802154_radio_ops_t mrf24j40_ops = {
.caps = IEEE802154_CAP_24_GHZ
| IEEE802154_CAP_IRQ_TX_DONE
| IEEE802154_CAP_FRAME_RETRANS
| IEEE802154_CAP_FRAME_RETRANS_INFO
| IEEE802154_CAP_PHY_OQPSK
| IEEE802154_CAP_REG_RETENTION,
.write = _write,
.read = _read,
.request_on = _request_on,
.confirm_on = _confirm_on,
.len = _len,
.off = _off,
.request_op = _request_op,
.confirm_op = _confirm_op,
.set_cca_threshold = _set_cca_threshold,
.set_cca_mode = _set_cca_mode,
.config_phy = _config_phy,
.set_csma_params = _set_csma_params,
.config_addr_filter = _config_addr_filter,
.config_src_addr_match = _config_src_addr_match,
.set_frame_filter_mode = _set_frame_filter_mode,
.set_frame_retrans = _set_frame_retrans,
};

View File

@ -22,6 +22,8 @@
#include "mrf24j40_params.h"
#include "lwip_init_devs.h"
#include "bhp/msg.h"
#include "net/netdev/ieee802154_submac.h"
#define ENABLE_DEBUG 0
#include "debug.h"
@ -30,12 +32,20 @@
static lwip_netif_t netif[NETIF_MRF24J40_NUMOF];
static mrf24j40_t mrf24j40_devs[NETIF_MRF24J40_NUMOF];
static netdev_ieee802154_submac_t mrf24j40_netdev[NETIF_MRF24J40_NUMOF];
static void auto_init_mrf24j40(void)
{
for (unsigned i = 0; i < NETIF_MRF24J40_NUMOF; i++) {
mrf24j40_setup(&mrf24j40_devs[i], &mrf24j40_params[i], i);
if (lwip_add_6lowpan(&netif[i], &mrf24j40_devs[i].netdev.netdev) == NULL) {
bhp_msg_init(&netif[i].bhp, &mrf24j40_radio_irq_handler, &mrf24j40_netdev[i].submac.dev);
mrf24j40_init(&mrf24j40_devs[i], &mrf24j40_params[i], &mrf24j40_netdev[i].submac.dev,
bhp_msg_isr_cb, &netif[i].bhp);
netdev_register(&mrf24j40_netdev[i].dev.netdev, NETDEV_MRF24J40, i);
netdev_ieee802154_submac_init(&mrf24j40_netdev[i]);
if (lwip_add_6lowpan(&netif[i], &mrf24j40_netdev[i].dev.netdev) == NULL) {
DEBUG("Could not add mrf24j40 device\n");
return;
}

View File

@ -22,6 +22,8 @@
#include "net/gnrc/netif/ieee802154.h"
#include "net/gnrc.h"
#include "include/init_devs.h"
#include "net/netdev/ieee802154_submac.h"
#include "bhp/event.h"
#include "mrf24j40.h"
#include "mrf24j40_params.h"
@ -38,20 +40,31 @@
#define MRF24J40_NUM ARRAY_SIZE(mrf24j40_params)
static mrf24j40_t mrf24j40_devs[MRF24J40_NUM];
static netdev_ieee802154_submac_t mrf24j40_netdev[MRF24J40_NUM];
static char _mrf24j40_stacks[MRF24J40_NUM][MRF24J40_MAC_STACKSIZE];
static gnrc_netif_t _netif[MRF24J40_NUM];
static bhp_event_t mrf24j40_bhp[MRF24J40_NUM];
void auto_init_mrf24j40(void)
{
for (unsigned i = 0; i < MRF24J40_NUM; i++) {
const mrf24j40_params_t *p = &mrf24j40_params[i];
LOG_DEBUG("[auto_init_netif] initializing mrf24j40 #%u\n", i);
mrf24j40_setup(&mrf24j40_devs[i], &mrf24j40_params[i], i);
/* Init Bottom Half Processor (with events module) and radio */
bhp_event_init(&mrf24j40_bhp[i], &_netif[i].evq[GNRC_NETIF_EVQ_INDEX_PRIO_HIGH], &mrf24j40_radio_irq_handler, &mrf24j40_netdev[i].submac.dev);
mrf24j40_init(&mrf24j40_devs[i], (mrf24j40_params_t*) p,&mrf24j40_netdev[i].submac.dev,
bhp_event_isr_cb, &mrf24j40_bhp[i]);
netdev_register(&mrf24j40_netdev[i].dev.netdev, NETDEV_MRF24J40, i);
netdev_ieee802154_submac_init(&mrf24j40_netdev[i]);
gnrc_netif_ieee802154_create(&_netif[i], _mrf24j40_stacks[i],
MRF24J40_MAC_STACKSIZE, MRF24J40_MAC_PRIO,
"mrf24j40",
&mrf24j40_devs[i].netdev.netdev);
&mrf24j40_netdev[i].dev.netdev);
}
}
/** @} */

View File

@ -0,0 +1,11 @@
# Copyright (c) 2022 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.
#
config APPLICATION
bool
default y
select HAVE_MRF24J40

View File

@ -2,6 +2,31 @@ INCLUDES += -I$(APPDIR)
include ../Makefile.tests_common
# Due to missing Kconfig support, whitelist boards that are known to have a
# working Kconfig dependency resolution.
# Other boards should still work when running with CONTINUE_ON_EXPECTED_ERRORS=1
BOARD_WHITELIST += adafruit-clue \
adafruit-itsybitsy-nrf52 \
arduino-nano-33-ble \
cc2538dk \
feather-nrf52840 \
native \
nrf52840dk \
nrf52840dongle \
nrf52840-mdk \
nucleo-l152re \
nucleo-f303re \
nucleo-wl55jc \
omote \
openmote-cc2538 \
reel \
remote-pa \
remote-reva \
remote-revb \
pba-d-01-kw2x \
#
USEMODULE += test_utils_netdev_ieee802154_minimal
# select the driver to test
@ -10,3 +35,7 @@ USEMODULE += mrf24j40ma
CFLAGS += -DEVENT_THREAD_STACKSIZE_DEFAULT=1024
include $(RIOTBASE)/Makefile.include
ifneq (,$(filter bhp,$(USEMODULE)))
USEMODULE += bhp_event
endif

View File

@ -0,0 +1,5 @@
CONFIG_MODULE_MRF24J40=y
CONFIG_MODULE_MRF24J40MA=y
CONFIG_MODULE_SHELL=y
CONFIG_MODULE_TEST_UTILS_NETDEV_IEEE802154_MINIMAL=y
CONFIG_MODULE_NETDEV_IEEE802154_SUBMAC=y

View File

@ -0,0 +1 @@
../ieee802154_hal/common.h

View File

@ -0,0 +1 @@
../ieee802154_hal/init_devs.c

View File

@ -25,22 +25,37 @@
#include "mrf24j40_params.h"
#include "shell.h"
#include "test_utils/netdev_ieee802154_minimal.h"
#include "net/netdev/ieee802154_submac.h"
#include "common.h"
static mrf24j40_t mrf24j40[MRF24J40_NUM];
static netdev_ieee802154_submac_t mrf24j40_netdev[MRF24J40_NUM];
static ieee802154_dev_t *_reg_callback(ieee802154_dev_type_t type, void *opaque)
{
if (type != IEEE802154_DEV_TYPE_MRF24J40) {
assert(false);
}
int *c = opaque;
return &mrf24j40_netdev[(*(c))++].submac.dev;
}
int netdev_ieee802154_minimal_init_devs(netdev_event_cb_t cb) {
puts("Initializing MRF24J40 devices");
int c = 0;
/* This function will iterate through all kw2xrf radios */
ieee802154_hal_test_init_devs(_reg_callback, &c);
for (unsigned i = 0; i < MRF24J40_NUM; i++) {
printf("%d out of %d\n", i + 1, MRF24J40_NUM);
/* setup the specific driver */
mrf24j40_setup(&mrf24j40[i], &mrf24j40_params[i], i);
netdev_register(&mrf24j40_netdev[i].dev.netdev, NETDEV_MRF24J40, 0);
netdev_ieee802154_submac_init(&mrf24j40_netdev[i]);
/* set the application-provided callback */
mrf24j40[i].netdev.netdev.event_callback = cb;
mrf24j40_netdev[i].dev.netdev.event_callback = cb;
/* initialize the device driver */
int res = mrf24j40[i].netdev.netdev.driver->init(&mrf24j40[i].netdev.netdev);
int res = mrf24j40_netdev[i].dev.netdev.driver->init(&mrf24j40_netdev[i].dev.netdev);
if (res != 0) {
return -1;
}