diff --git a/cpu/cc2538/include/cc2538_rf.h b/cpu/cc2538/include/cc2538_rf.h index 29f9b410b2..e94f4fe0a2 100644 --- a/cpu/cc2538/include/cc2538_rf.h +++ b/cpu/cc2538/include/cc2538_rf.h @@ -68,9 +68,41 @@ extern "C" { #define CC2538_CRC_BIT_MASK (0x80) +#define CC2538_CCA_THR_MASK (0x000000FF) /**< CCA Threshold mask */ + +#define CC2538_CCA_MODE_MASK (0x18) /**< CCA Mode mask */ +#define CC2538_CCA_MODE_POS (3U) /**< CCA Mode pos */ + +#define CC2538_CSP_SKIP_INST_MASK (0x70) /**< CSP Skip instruction mask */ +#define CC2538_CSP_SKIP_INST_POS (4U) /**< CSP Skip instruction pos */ + +#define CC2538_CSP_SKIP_N_MASK (0x08) /**< CSP Skip condition negation mask */ + +#define CC2538_CSP_SKIP_COND_CCA (0x00) /**< CSP Skip condition is valid CCA */ +#define CC2538_CSP_SKIP_COND_CSPZ (0x06) /**< CSP Skip condition is CSPZ is 0 */ +#define CC2538_CSP_SKIP_COND_RSSI (0x07) /**< CSP Skip condition is valid RSSI */ + +#define CC2538_SFR_MTMSEL_MASK (0x7) /**< MAC Timer selection mask */ +#define CC2538_SFR_MTMSEL_TIMER_P (0x2) /**< Selects Timer period */ +#define CC2538_MCTRL_SYNC_MASK (0x2) /**< Sync MAC Timer to external clock */ +#define CC2538_MCTRL_RUN_MASK (0x1) /**< Run MAC Timer */ + +#define CC2538_CSP_MCU_CTRL_MASK (0x1) /**< MCU Ctrl mask */ + +#define CC2538_CSP_INCMAXY_MAX_MASK (0x7) /**< CSP INCMAXY instruction (increment Register CSPX + without exceeding CSPY) */ + +#define CC2538_RXENABLE_RXON_MASK (0x80) /**< RX on mask */ + #define CC2538_RSSI_OFFSET (-73) /**< Signal strength offset value */ #define CC2538_RF_SENSITIVITY (-97) /**< dBm typical, normal conditions */ +#define CC2538_ACCEPT_FT_2_ACK (1 << 5) /**< enable or disable the ACK filter */ +#define CC2538_STATE_SFD_WAIT_RANGE_MIN (0x03U) /**< min range value of SFD wait state */ +#define CC2538_STATE_SFD_WAIT_RANGE_MAX (0x06U) /**< max range value of SFD wait state */ +#define CC2538_FRMCTRL1_PENDING_OR_MASK (0x04) /**< mask for enabling or disabling the + frame pending bit */ + #define RFCORE_ASSERT(expr) (void)( (expr) || RFCORE_ASSERT_failure(#expr, __FUNCTION__, __LINE__) ) #if DEVELHELP @@ -134,6 +166,18 @@ enum { RXMASKZERO = BIT(7), }; +/* + * @brief RFCORE_XREG_RFIRQM1 / RFCORE_XREG_RFIRQF1 bits + */ +enum { + TXACKDONE = BIT(0), + TXDONE = BIT(1), + RF_IDLE = BIT(2), + CSP_MANINT = BIT(3), + CSP_STOP = BIT(4), + CSP_WAIT = BIT(5), +}; + /* Values for use with CCTEST_OBSSELx registers: */ #define OBSSEL_EN BIT(7) enum { diff --git a/cpu/cc2538/radio/cc2538_rf.c b/cpu/cc2538/radio/cc2538_rf.c index 18b66f84cf..541dae136d 100644 --- a/cpu/cc2538/radio/cc2538_rf.c +++ b/cpu/cc2538/radio/cc2538_rf.c @@ -28,7 +28,15 @@ #define ENABLE_DEBUG (0) #include "debug.h" -#define CC2538_ACCEPT_FT_2_ACK (1 << 5) +/* + * @brief MAC timer period + * + * The period is set to the CSMA-CA Backoff Period Unit (20 symbols, 320 us). + * The system clock runs at 32 MHz. Thus, the timeout period is + * 320us * 32MHz = ~10738 (0x29F2) + */ +#define TIMER_PERIOD_LSB (0xF2) +#define TIMER_PERIOD_MSB (0x29) typedef struct { cc2538_reg_t *reg_addr; @@ -58,6 +66,9 @@ static const init_pair_t init_table[] = { {&RFCORE_XREG_SRCMATCH, 0x00 }, {&RFCORE_XREG_FIFOPCTRL, CC2538_RF_MAX_DATA_LEN }, {&RFCORE_XREG_RFIRQM0, FIFOP | RXPKTDONE }, +#if IS_USED(MODULE_IEEE802154_RADIO_HAL) + {&RFCORE_XREG_RFIRQM1, TXDONE | CSP_STOP }, +#endif {&RFCORE_XREG_RFERRM, STROBE_ERR | TXUNDERF | TXOVERF | RXUNDERF | RXOVERF | NLOCK}, {NULL, 0}, }; @@ -86,10 +97,6 @@ void cc2538_init(void) *pair->reg_addr = pair->value; } - cc2538_set_tx_power(CC2538_RF_POWER_DEFAULT); - cc2538_set_chan(CC2538_RF_CHANNEL_DEFAULT); - cc2538_set_addr_long(cc2538_get_eui64_primary()); - /* Select the observable signals (maximum of three) */ RFCORE_XREG_RFC_OBS_CTRL0 = tx_active; RFCORE_XREG_RFC_OBS_CTRL1 = rx_active; @@ -123,6 +130,9 @@ void cc2538_init(void) NVIC_SetPriority(RF_ERR_ALT_IRQn, RADIO_IRQ_PRIO); NVIC_EnableIRQ(RF_ERR_ALT_IRQn); + + NVIC_SetPriority(MAC_TIMER_ALT_IRQn, RADIO_IRQ_PRIO); + NVIC_EnableIRQ(MAC_TIMER_ALT_IRQn); } else { NVIC_SetPriority(RF_RXTX_IRQn, RADIO_IRQ_PRIO); @@ -130,14 +140,25 @@ void cc2538_init(void) NVIC_SetPriority(RF_ERR_IRQn, RADIO_IRQ_PRIO); NVIC_EnableIRQ(RF_ERR_IRQn); + + NVIC_SetPriority(MACTIMER_IRQn, RADIO_IRQ_PRIO); + NVIC_EnableIRQ(MACTIMER_IRQn); } + RFCORE_SFR_MTMSEL &= ~CC2538_SFR_MTMSEL_MASK; + /* Select timer period */ + RFCORE_SFR_MTMSEL |= CC2538_SFR_MTMSEL_TIMER_P; + + /* Fix timer to Backoff period */ + RFCORE_SFR_MTM0 |= TIMER_PERIOD_LSB; + RFCORE_SFR_MTM1 |= TIMER_PERIOD_MSB; + + RFCORE_SFR_MTMSEL &= ~CC2538_SFR_MTMSEL_MASK; + RFCORE_SFR_MTCTRL |= CC2538_MCTRL_SYNC_MASK | CC2538_MCTRL_RUN_MASK; + /* Flush the receive and transmit FIFOs */ RFCORE_SFR_RFST = ISFLUSHTX; RFCORE_SFR_RFST = ISFLUSHRX; - /* Disable/filter l2 Acks */ - RFCORE_XREG_FRMFILT1 &= ~CC2538_ACCEPT_FT_2_ACK; - cc2538_on(); } bool cc2538_is_on(void) @@ -177,4 +198,9 @@ void cc2538_setup(cc2538_rf_t *dev) netdev->driver = &cc2538_rf_driver; cc2538_init(); + cc2538_set_tx_power(CC2538_RF_POWER_DEFAULT); + cc2538_set_chan(CC2538_RF_CHANNEL_DEFAULT); + cc2538_set_addr_long(cc2538_get_eui64_primary()); + + cc2538_on(); } diff --git a/cpu/cc2538/radio/cc2538_rf_internal.c b/cpu/cc2538/radio/cc2538_rf_internal.c index 0fbeb9cca7..bc3ee3558e 100644 --- a/cpu/cc2538/radio/cc2538_rf_internal.c +++ b/cpu/cc2538/radio/cc2538_rf_internal.c @@ -64,9 +64,6 @@ void isr_rfcoreerr(void) void isr_rfcorerxtx(void) { - RFCORE_SFR_RFIRQF0 = 0; - RFCORE_SFR_RFIRQF1 = 0; - _irq_handler(); cortexm_isr_end(); diff --git a/cpu/cc2538/radio/cc2538_rf_netdev.c b/cpu/cc2538/radio/cc2538_rf_netdev.c index 13c3b1cb1d..5762685c6f 100644 --- a/cpu/cc2538/radio/cc2538_rf_netdev.c +++ b/cpu/cc2538/radio/cc2538_rf_netdev.c @@ -31,11 +31,16 @@ #define ENABLE_DEBUG (0) #include "debug.h" +#if !IS_USED(MODULE_IEEE802154_RADIO_HAL) + /* Reference pointer for the IRQ handler */ static netdev_t *_dev; void _irq_handler(void) { + RFCORE_SFR_RFIRQF0 = 0; + RFCORE_SFR_RFIRQF1 = 0; + netdev_trigger_event_isr(_dev); } @@ -417,3 +422,6 @@ const netdev_driver_t cc2538_rf_driver = { .isr = _isr, .init = _init, }; +#else +int dont_be_pedantic; +#endif /* MODULE_IEEE802154_RADIO_HAL */ diff --git a/cpu/cc2538/radio/cc2538_rf_radio_ops.c b/cpu/cc2538/radio/cc2538_rf_radio_ops.c new file mode 100644 index 0000000000..688daecc10 --- /dev/null +++ b/cpu/cc2538/radio/cc2538_rf_radio_ops.c @@ -0,0 +1,492 @@ +#include +#include + +#include "net/gnrc.h" + +#include "cc2538_rf.h" +#include "cc2538_rf_internal.h" + +#include "net/ieee802154/radio.h" + +#if IS_USED(MODULE_IEEE802154_RADIO_HAL) +static const ieee802154_radio_ops_t cc2538_rf_ops; + +ieee802154_dev_t cc2538_rf_dev = { + .driver = &cc2538_rf_ops, +}; + +static uint8_t cc2538_min_be = CONFIG_IEEE802154_DEFAULT_CSMA_CA_MIN_BE; +static uint8_t cc2538_max_be = CONFIG_IEEE802154_DEFAULT_CSMA_CA_MAX_BE; +static int cc2538_csma_ca_retries = CONFIG_IEEE802154_DEFAULT_CSMA_CA_RETRIES; + +static bool cc2538_cca_status; /**< status of the last CCA request */ +static bool cc2538_cca; /**< used to check wether the last CCA result + corresponds to a CCA request or send with + CSMA-CA */ + +static int _write(ieee802154_dev_t *dev, const iolist_t *iolist) +{ + (void) dev; + int pkt_len = 0; + RFCORE_SFR_RFST = ISFLUSHTX; + rfcore_write_byte(0); + + for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) { + if (iol->iol_len) { + pkt_len += iol->iol_len; + rfcore_write_fifo(iol->iol_base, iol->iol_len); + } + } + + /* Set first byte of TX FIFO to the packet length */ + rfcore_poke_tx_fifo(0, pkt_len + CC2538_AUTOCRC_LEN); + return 0; +} + +static int _confirm_transmit(ieee802154_dev_t *dev, ieee802154_tx_info_t *info) +{ + (void) dev; + + if (RFCORE->XREG_FSMSTAT1bits.TX_ACTIVE != 0 || !(RFCORE_XREG_CSPCTRL & CC2538_CSP_MCU_CTRL_MASK)) { + return -EAGAIN; + } + + if (info) { + if (cc2538_csma_ca_retries >= 0 && RFCORE_XREG_CSPZ == 0) { + info->status = TX_STATUS_MEDIUM_BUSY; + } + else { + info->status = TX_STATUS_SUCCESS; + } + } + + return 0; +} + +static int _request_transmit(ieee802154_dev_t *dev) +{ + (void) dev; + + if (cc2538_csma_ca_retries < 0) { + RFCORE_SFR_RFST = ISTXON; + } + else { + cc2538_cca = false; + + RFCORE_SFR_RFST = ISRXON; + /* Clear last program */ + RFCORE_SFR_RFST = ISCLEAR; + + /* If the RSSI is not yet valid, skip 0 instructions. This creates + * a busy loop until the RSSI is valid. */ + RFCORE_SFR_RFST = SKIP_S_C + | CC2538_CSP_SKIP_N_MASK + | CC2538_CSP_SKIP_COND_RSSI; + + /* Set a label right before the backoff */ + RFCORE_SFR_RFST = LABEL; + + /* Load a random number with "register Y" LSBs into register X. + * This is equivalent to choosing a random number between + * (0, 2^(Y+1)). + * Then, wait "register X" number of backoff units */ + RFCORE_SFR_RFST = RANDXY; + RFCORE_SFR_RFST = WAITX; + + /* If CCA is not valid, skip the next stop instruction. In such case + * the CSP_STOP interrupt will trigger the transmission since the + * channel is clear */ + RFCORE_SFR_RFST = SKIP_S_C + | (1 << CC2538_CSP_SKIP_INST_POS) + | CC2538_CSP_SKIP_N_MASK + | CC2538_CSP_SKIP_COND_CCA; + + RFCORE_SFR_RFST = STOP; + + /* If we are here, the channel was not clear. Decrement the register Z + * (remaining attempts) */ + RFCORE_SFR_RFST = DECZ; + + /* Update the backoff exponent */ + RFCORE_SFR_RFST = INCMAXY | (cc2538_max_be & CC2538_CSP_INCMAXY_MAX_MASK); + + /* If the are CSMA-CA retries left, go back to the defined label */ + RFCORE_SFR_RFST = RPT_C + | CC2538_CSP_SKIP_N_MASK + | CC2538_CSP_SKIP_COND_CSPZ; + + /* Stop the program. The CSP_STOP interrupt will trigger the routine + * to inform the upper layer that CSMA-CA failed. */ + RFCORE_SFR_RFST = STOP; + + RFCORE_XREG_CSPX = 0; /* Holds timer value */ + RFCORE_XREG_CSPY = cc2538_min_be; /* Holds MinBE */ + + assert(cc2538_csma_ca_retries >= 0); + RFCORE_XREG_CSPZ = cc2538_csma_ca_retries + 1; /* Holds CSMA-CA attempts (retries + 1) */ + + RFCORE_XREG_CSPCTRL &= ~CC2538_CSP_MCU_CTRL_MASK; + + /* Execute the program */ + RFCORE_SFR_RFST = ISSTART; + } + return 0; +} + +static int _len(ieee802154_dev_t *dev) +{ + (void) dev; + return rfcore_peek_rx_fifo(0) - IEEE802154_FCS_LEN; +} + +static int _indication_rx(ieee802154_dev_t *dev, void *buf, size_t size, ieee802154_rx_info_t *info) +{ + (void) dev; + int res; + size_t pkt_len = rfcore_read_byte(); + + pkt_len -= IEEE802154_FCS_LEN; + + if (pkt_len > size) { + RFCORE_SFR_RFST = ISFLUSHRX; + return -ENOBUFS; + } + + if (buf != NULL) { + rfcore_read_fifo(buf, pkt_len); + res = pkt_len; + if (info != NULL) { + uint8_t corr_val; + int8_t rssi_val; + rssi_val = rfcore_read_byte(); + + /* The number of dB above maximum sensitivity detected for the + * received packet */ + info->rssi = -CC2538_RSSI_OFFSET + rssi_val + IEEE802154_RADIO_RSSI_OFFSET; + + corr_val = rfcore_read_byte() & CC2538_CORR_VAL_MASK; + + if (corr_val < CC2538_CORR_VAL_MIN) { + corr_val = CC2538_CORR_VAL_MIN; + } + else if (corr_val > CC2538_CORR_VAL_MAX) { + corr_val = CC2538_CORR_VAL_MAX; + } + + /* Interpolate the correlation value between 0 - 255 + * to provide an LQI value */ + info->lqi = 255 * (corr_val - CC2538_CORR_VAL_MIN) / + (CC2538_CORR_VAL_MAX - CC2538_CORR_VAL_MIN); + } + } + else { + res = 0; + } + + RFCORE_SFR_RFST = ISFLUSHRX; + + return res; +} + +static int _confirm_cca(ieee802154_dev_t *dev) +{ + (void) dev; + + RFCORE_XREG_RFIRQM0 |= RXPKTDONE; + + return cc2538_cca_status; +} + +static int _request_cca(ieee802154_dev_t *dev) +{ + (void) dev; + if (!RFCORE->XREG_FSMSTAT1bits.RX_ACTIVE) { + return -EINVAL; + } + + /* Ignore baseband processing */ + RFCORE_XREG_RFIRQM0 &= ~RXPKTDONE; + + cc2538_cca = true; + RFCORE_SFR_RFST = ISCLEAR; + RFCORE_SFR_RFST = STOP; + RFCORE_XREG_CSPCTRL &= ~CC2538_CSP_MCU_CTRL_MASK; + + /* Execute the last program */ + RFCORE_SFR_RFST = ISSTART; + + return 0; +} + +static int _set_cca_threshold(ieee802154_dev_t *dev, int8_t threshold) +{ + (void) dev; + (void) threshold; + + if (threshold < CC2538_RF_SENSITIVITY) { + return -EINVAL; + } + + RFCORE_XREG_CCACTRL0 &= ~CC2538_CCA_THR_MASK; + RFCORE_XREG_CCACTRL0 |= (threshold - CC2538_RSSI_OFFSET) & CC2538_CCA_THR_MASK; + + return 0; +} + +static int _config_phy(ieee802154_dev_t *dev, ieee802154_phy_conf_t *conf) +{ + (void) dev; + int8_t pow = conf->pow; + if (pow < OUTPUT_POWER_MIN || pow > OUTPUT_POWER_MAX) { + return -EINVAL; + } + + uint8_t channel = conf->channel; + cc2538_set_freq(IEEE802154_CHAN2FREQ(channel)); + cc2538_set_tx_power(pow); + + return 0; +} + +static int _confirm_set_trx_state(ieee802154_dev_t *dev) +{ + (void) dev; + if (RFCORE->XREG_FSMSTAT0bits.FSM_FFCTRL_STATE == FSM_STATE_RX_CALIBRATION) { + return -EAGAIN; + } + return 0; +} + +static int _request_set_trx_state(ieee802154_dev_t *dev, ieee802154_trx_state_t state) +{ + + (void) dev; + bool wait_sfd = RFCORE->XREG_FSMSTAT0bits.FSM_FFCTRL_STATE >= CC2538_STATE_SFD_WAIT_RANGE_MIN + && RFCORE->XREG_FSMSTAT0bits.FSM_FFCTRL_STATE <= CC2538_STATE_SFD_WAIT_RANGE_MAX; + if ((RFCORE->XREG_FSMSTAT1bits.RX_ACTIVE && !wait_sfd) || + RFCORE->XREG_FSMSTAT1bits.TX_ACTIVE) { + return -EBUSY; + } + + switch(state) { + case IEEE802154_TRX_STATE_TRX_OFF: + case IEEE802154_TRX_STATE_TX_ON: + if (RFCORE->XREG_FSMSTAT0bits.FSM_FFCTRL_STATE != FSM_STATE_IDLE) { + RFCORE_SFR_RFST = ISRFOFF; + } + break; + case IEEE802154_TRX_STATE_RX_ON: + RFCORE_XREG_RFIRQM0 |= RXPKTDONE; + RFCORE_SFR_RFST = ISRXON; + break; + } + + return 0; +} + +void _irq_handler(void) +{ + uint_fast8_t flags_f0 = RFCORE_SFR_RFIRQF0; + uint_fast8_t flags_f1 = RFCORE_SFR_RFIRQF1; + + RFCORE_SFR_RFIRQF0 = 0; + RFCORE_SFR_RFIRQF1 = 0; + + if (flags_f1 & TXDONE) { + cc2538_rf_dev.cb(&cc2538_rf_dev, IEEE802154_RADIO_CONFIRM_TX_DONE); + } + + if (flags_f0 & RXPKTDONE) { + /* CRC check */ + uint8_t pkt_len = rfcore_peek_rx_fifo(0); + if (rfcore_peek_rx_fifo(pkt_len) & CC2538_CRC_BIT_MASK) { + cc2538_rf_dev.cb(&cc2538_rf_dev, IEEE802154_RADIO_INDICATION_RX_DONE); + } + else { + /* CRC failed; discard packet */ + RFCORE_SFR_RFST = ISFLUSHRX; + } + + } + + /* Check if the interrupt was triggered because the CSP finished its routine + * (CSMA-CA or CCA request) + */ + if (flags_f1 & CSP_STOP) { + RFCORE_XREG_CSPCTRL |= CC2538_CSP_MCU_CTRL_MASK; + if (!cc2538_cca) { + if (RFCORE_XREG_CSPZ > 0) { + RFCORE_XREG_RXMASKCLR = CC2538_RXENABLE_RXON_MASK; + RFCORE_SFR_RFST = ISTXON; + } + else { + cc2538_rf_dev.cb(&cc2538_rf_dev, IEEE802154_RADIO_CONFIRM_TX_DONE); + } + } + else { + cc2538_cca_status = BOOLEAN(RFCORE->XREG_FSMSTAT1bits.CCA) && RFCORE->XREG_RSSISTATbits.RSSI_VALID; + cc2538_rf_dev.cb(&cc2538_rf_dev, IEEE802154_RADIO_CONFIRM_CCA); + } + } +} + +static int _off(ieee802154_dev_t *dev) +{ + (void) dev; + return -ENOTSUP; +} + +static bool _get_cap(ieee802154_dev_t *dev, ieee802154_rf_caps_t cap) +{ + (void) dev; + switch(cap) { + case IEEE802154_CAP_24_GHZ: + case IEEE802154_CAP_IRQ_TX_DONE: + case IEEE802154_CAP_IRQ_CCA_DONE: + case IEEE802154_CAP_AUTO_CSMA: + return true; + default: + return false; + } +} + +static int _set_hw_addr_filter(ieee802154_dev_t *dev, const network_uint16_t *short_addr, + const eui64_t *ext_addr, const uint16_t *pan_id) +{ + (void) dev; + if (short_addr) { + RFCORE_FFSM_SHORT_ADDR0 = short_addr->u8[1]; + RFCORE_FFSM_SHORT_ADDR1 = short_addr->u8[0]; + } + + if (ext_addr) { + RFCORE_FFSM_EXT_ADDR0 = ext_addr->uint8[7]; + RFCORE_FFSM_EXT_ADDR1 = ext_addr->uint8[6]; + RFCORE_FFSM_EXT_ADDR2 = ext_addr->uint8[5]; + RFCORE_FFSM_EXT_ADDR3 = ext_addr->uint8[4]; + RFCORE_FFSM_EXT_ADDR4 = ext_addr->uint8[3]; + RFCORE_FFSM_EXT_ADDR5 = ext_addr->uint8[2]; + RFCORE_FFSM_EXT_ADDR6 = ext_addr->uint8[1]; + RFCORE_FFSM_EXT_ADDR7 = ext_addr->uint8[0]; + } + + if (pan_id) { + RFCORE_FFSM_PAN_ID0 = *pan_id; + RFCORE_FFSM_PAN_ID1 = (*pan_id) >> 8; + } + + return 0; +} + +static int _confirm_on(ieee802154_dev_t *dev) +{ + (void) dev; + /* TODO */ + return 0; +} + +static int _request_on(ieee802154_dev_t *dev) +{ + (void) dev; + /* TODO */ + return 0; +} + +static int _set_cca_mode(ieee802154_dev_t *dev, ieee802154_cca_mode_t mode) +{ + (void) dev; + uint8_t tmp = 0; + switch (mode) { + case IEEE802154_CCA_MODE_ED_THRESHOLD: + tmp = 1; + break; + case IEEE802154_CCA_MODE_CARRIER_SENSING: + tmp = 2; + break; + case IEEE802154_CCA_MODE_ED_THRESH_AND_CS: + tmp = 3; + break; + case IEEE802154_CCA_MODE_ED_THRESH_OR_CS: + return -ENOTSUP; + } + + RFCORE_XREG_CCACTRL1 &= CC2538_CCA_MODE_MASK; + RFCORE_XREG_CCACTRL1 |= (tmp << CC2538_CCA_MODE_POS); + + return 0; +} + +static int _set_rx_mode(ieee802154_dev_t *dev, ieee802154_rx_mode_t mode) +{ + (void) dev; + + bool promisc = false; + bool ack_filter = true; + switch (mode) { + case IEEE802154_RX_AACK_DISABLED: + RFCORE->XREG_FRMCTRL0bits.AUTOACK = 0; + break; + case IEEE802154_RX_AACK_ENABLED: + RFCORE->XREG_FRMCTRL0bits.AUTOACK = 1; + RFCORE_XREG_FRMCTRL1 &= ~CC2538_FRMCTRL1_PENDING_OR_MASK; + break; + case IEEE802154_RX_AACK_FRAME_PENDING: + RFCORE->XREG_FRMCTRL0bits.AUTOACK = 1; + RFCORE_XREG_FRMCTRL1 |= CC2538_FRMCTRL1_PENDING_OR_MASK; + break; + case IEEE802154_RX_PROMISC: + promisc = true; + break; + case IEEE802154_RX_WAIT_FOR_ACK: + ack_filter = false; + break; + } + + if (ack_filter) { + RFCORE_XREG_FRMFILT1 &= ~CC2538_ACCEPT_FT_2_ACK; + } + else { + RFCORE_XREG_FRMFILT1 |= CC2538_ACCEPT_FT_2_ACK; + } + + cc2538_set_monitor(promisc); + return 0; +} + +static int _set_csma_params(ieee802154_dev_t *dev, ieee802154_csma_be_t *bd, int8_t retries) +{ + (void) dev; + if (bd) { + cc2538_min_be = bd->min; + cc2538_max_be = bd->max; + } + + cc2538_csma_ca_retries = retries; + + return 0; +} + +static const ieee802154_radio_ops_t cc2538_rf_ops = { + .write = _write, + .request_transmit = _request_transmit, + .confirm_transmit = _confirm_transmit, + .len = _len, + .indication_rx = _indication_rx, + .off = _off, + .request_on = _request_on, + .confirm_on = _confirm_on, + .request_set_trx_state = _request_set_trx_state, + .confirm_set_trx_state = _confirm_set_trx_state, + .request_cca = _request_cca, + .confirm_cca = _confirm_cca, + .get_cap = _get_cap, + .set_cca_threshold = _set_cca_threshold, + .set_cca_mode = _set_cca_mode, + .config_phy = _config_phy, + .set_hw_addr_filter = _set_hw_addr_filter, + .set_csma_params = _set_csma_params, + .set_rx_mode = _set_rx_mode, +}; +#else +int dont_be_pedantic; +#endif diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index d6287c3f8e..76a7869ac5 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -59,6 +59,7 @@ PSEUDOMODULES += gnrc_sock_check_reuse PSEUDOMODULES += gnrc_txtsnd PSEUDOMODULES += heap_cmd PSEUDOMODULES += i2c_scan +PSEUDOMODULES += ieee802154_radio_hal PSEUDOMODULES += ina3221_alerts PSEUDOMODULES += l2filter_blacklist PSEUDOMODULES += l2filter_whitelist diff --git a/sys/include/net/ieee802154.h b/sys/include/net/ieee802154.h index 9c60fd7b71..11bc3b1257 100644 --- a/sys/include/net/ieee802154.h +++ b/sys/include/net/ieee802154.h @@ -98,6 +98,13 @@ extern "C" { #define IEEE802154G_FRAME_LEN_MAX (2047U) /**< maximum 802.15.4g-2012 frame length */ #define IEEE802154_ACK_FRAME_LEN (5U) /**< ACK frame length */ +/** + * @brief value of measured power when RSSI is zero. + * + * This value is defined in the IEEE 802.15.4 standard + */ +#define IEEE802154_RADIO_RSSI_OFFSET (-174) + /** * For the SUN PHYs, the value is 1 ms expressed in symbol periods, rounded * up to the next integer number of symbol periods using the ceiling() function. @@ -196,6 +203,27 @@ extern const uint8_t ieee802154_addr_bcast[IEEE802154_ADDR_BCAST_LEN]; #endif /** @} */ +/** + * @brief IEEE802.15.4 default value for minimum backoff exponent + */ +#ifndef CONFIG_IEEE802154_DEFAULT_CSMA_CA_MIN_BE +#define CONFIG_IEEE802154_DEFAULT_CSMA_CA_MIN_BE (3U) +#endif + +/** + * @brief IEEE802.15.4 default value for maximum number of CSMA-CA retries. + */ +#ifndef CONFIG_IEEE802154_DEFAULT_CSMA_CA_RETRIES +#define CONFIG_IEEE802154_DEFAULT_CSMA_CA_RETRIES (4U) +#endif + +/** + * @brief IEEE802.15.4 default value for maximum backoff exponent + */ +#ifndef CONFIG_IEEE802154_DEFAULT_CSMA_CA_MAX_BE +#define CONFIG_IEEE802154_DEFAULT_CSMA_CA_MAX_BE (5U) +#endif + /** * @brief Initializes an IEEE 802.15.4 MAC frame header in @p buf. * diff --git a/sys/net/link_layer/ieee802154/Kconfig b/sys/net/link_layer/ieee802154/Kconfig index 2d530c77d8..5a35677e01 100644 --- a/sys/net/link_layer/ieee802154/Kconfig +++ b/sys/net/link_layer/ieee802154/Kconfig @@ -33,4 +33,16 @@ if KCONFIG_USEMODULE_IEEE802154 int "IEEE802.15.4 default TX power (in dBm)" default 0 + config IEEE802154_DEFAULT_CSMA_CA_MIN + int "IEEE802.15.4 default CSMA-CA minimum backoff exponent" + default 3 + + config IEEE802154_DEFAULT_CSMA_CA_RETRIES + int "IEEE802.15.4 default CSMA-CA maximum number of retries" + default 4 + + config IEEE802154_DEFAULT_CSMA_CA_MAX + int "IEEE802.15.4 default CSMA-CA maximum backoff exponent" + default 5 + endif # KCONFIG_USEMODULE_IEEE802154 diff --git a/tests/ieee802154_hal/Makefile b/tests/ieee802154_hal/Makefile new file mode 100644 index 0000000000..a56493f2e5 --- /dev/null +++ b/tests/ieee802154_hal/Makefile @@ -0,0 +1,31 @@ +include ../Makefile.tests_common + +BOARD_WHITELIST := cc2538dk \ + omote \ + openmote-b \ + openmote-cc2538 \ + remote-pa \ + remote-reva \ + remote-revb + +DISABLE_MODULE += auto_init_at86rf2xx auto_init_nrf802154 + +USEMODULE += od +USEMODULE += luid +USEMODULE += ieee802154 +USEMODULE += shell +USEMODULE += ps +USEMODULE += event_thread_highest +USEMODULE += event_callback +USEMODULE += xtimer +USEMODULE += ieee802154_radio_hal + +CFLAGS += -DEVENT_THREAD_HIGHEST_STACKSIZE=1024 + +# define the driver to be used for selected boards +DRIVER := cc2538_rf + +# include the selected driver +USEMODULE += $(DRIVER) + +include $(RIOTBASE)/Makefile.include diff --git a/tests/ieee802154_hal/common.h b/tests/ieee802154_hal/common.h new file mode 100644 index 0000000000..56620a2fac --- /dev/null +++ b/tests/ieee802154_hal/common.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2020 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. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Common header for ieee802154_hal tests. + * + * @author José I. Alamos + */ +#ifndef COMMON_H +#define COMMON_H + +#include "net/ieee802154/radio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Application-internal functions and variables for ieee802154_hal tests + * @internal + * @{ + */ +void ieee802154_hal_test_init_devs(void); +ieee802154_dev_t *ieee802154_hal_test_get_dev(int id); +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* COMMON_H */ +/** @} */ diff --git a/tests/ieee802154_hal/init_devs.c b/tests/ieee802154_hal/init_devs.c new file mode 100644 index 0000000000..0cba99556a --- /dev/null +++ b/tests/ieee802154_hal/init_devs.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2020 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. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Test application for ieee802154_hal + * + * @author José I. Alamos + * + * @} + */ + +#include "assert.h" +#include "kernel_defines.h" +#include "net/ieee802154/radio.h" + +#ifdef MODULE_CC2538_RF +#include "cc2538_rf.h" +#endif + +#ifdef MODULE_CC2538_RF +extern ieee802154_dev_t cc2538_rf_dev; +#endif + +#define RADIOS_NUMOF IS_USED(MODULE_CC2538_RF) + +#if RADIOS_NUMOF == 0 +#error "Radio is not supported" +#endif + +static ieee802154_dev_t *_radios[RADIOS_NUMOF]; + +static void _register_radios(void) +{ + int i=0; + +#ifdef MODULE_CC2538_RF + _radios[i++] = &cc2538_rf_dev; +#endif + + assert(i == RADIOS_NUMOF); +} + +void ieee802154_hal_test_init_devs(void) +{ + /* Register all radios */ + _register_radios(); + + /* Call the init function of the device (this should be handled by + * `auto_init`) */ +#ifdef MODULE_CC2538_RF + cc2538_init(); +#endif +} + +ieee802154_dev_t *ieee802154_hal_test_get_dev(int id) +{ + assert(id < RADIOS_NUMOF); + return _radios[id]; +} diff --git a/tests/ieee802154_hal/main.c b/tests/ieee802154_hal/main.c new file mode 100644 index 0000000000..29ab5c57bd --- /dev/null +++ b/tests/ieee802154_hal/main.c @@ -0,0 +1,656 @@ +/* + * Copyright (C) 2020 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. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Test application for IEEE 802.15.4 Radio HAL + * + * @author José I. Alamos + * + * @} + */ + +#include + +#include "common.h" +#include "errno.h" +#include "event/thread.h" +#include "luid.h" + +#include "net/ieee802154.h" +#include "net/ieee802154/radio.h" + +#include "shell.h" +#include "shell_commands.h" + +#include "xtimer.h" + +#define SYMBOL_TIME (16U) /**< 16 us */ + +#define ACK_TIMEOUT_TIME (40 * SYMBOL_TIME) + +#define RADIO_DEFAULT_ID (0U) + +static inline void _set_trx_state(int state, bool verbose); + +static uint8_t buffer[127]; +static xtimer_t timer_ack; +static mutex_t lock; + +static const char *str_states[3]= {"TRX_OFF", "RX", "TX"}; +static ieee802154_rx_mode_t current_rx_mode; +static eui64_t ext_addr; +static network_uint16_t short_addr; +static uint8_t seq; + +static void _print_packet(size_t size, uint8_t lqi, int16_t rssi) +{ + if (buffer[0] & IEEE802154_FCF_TYPE_ACK && ((seq-1) == buffer[2])) { + printf("Received valid ACK with sqn %i\n", buffer[2]); + } + else { + puts("Packet received:"); + for (unsigned i=0;i 0) { + /* Print packet while we wait for the state transition */ + _print_packet(size, info.lqi, info.rssi); + } +} + +static event_t _rx_done_event = { + .handler = _rx_done_handler, +}; + +/* Event Notification callback */ +static void _hal_radio_cb(ieee802154_dev_t *dev, ieee802154_trx_ev_t status) +{ + (void) dev; + switch(status) { + case IEEE802154_RADIO_CONFIRM_TX_DONE: + case IEEE802154_RADIO_CONFIRM_CCA: + mutex_unlock(&lock); + break; + case IEEE802154_RADIO_INDICATION_RX_DONE: + event_post(EVENT_PRIO_HIGHEST, &_rx_done_event); + break; + default: + break; + } +} + +static void _tx_finish_handler(event_t *event) +{ + ieee802154_tx_info_t tx_info; + + (void) event; + /* The TX_DONE event indicates it's safe to call the confirm counterpart */ + assert(ieee802154_radio_confirm_transmit(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID), &tx_info) >= 0); + + if (!ieee802154_radio_has_irq_ack_timeout(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID)) && !ieee802154_radio_has_frame_retrans(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID))) { + /* This is just to show how the MAC layer would handle ACKs... */ + _set_trx_state(IEEE802154_TRX_STATE_RX_ON, false); + xtimer_set(&timer_ack, ACK_TIMEOUT_TIME); + } + + switch (tx_info.status) { + case TX_STATUS_SUCCESS: + puts("Transmission succeeded"); + break; + case TX_STATUS_FRAME_PENDING: + puts("Transmission succeeded and there's pending data"); + break; + case TX_STATUS_MEDIUM_BUSY: + puts("Medium busy"); + break; + case TX_STATUS_NO_ACK: + puts("No ACK"); + break; + } + + puts(""); + + if (ieee802154_radio_has_frame_retrans_info(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID))) { + printf("Retransmission attempts: %i\n", tx_info.retrans); + } + + puts(""); +} + +static event_t _tx_finish_ev = { + .handler = _tx_finish_handler, +}; + +static void _send(iolist_t *pkt) +{ + /* Request a state change to RX_ON */ + ieee802154_radio_request_set_trx_state(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID), IEEE802154_TRX_STATE_TX_ON); + + /* Write the packet to the radio */ + ieee802154_radio_write(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID), pkt); + + /* Block until the radio confirms the state change */ + while(ieee802154_radio_confirm_set_trx_state(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID)) == -EAGAIN); + + /* Trigger the transmit and wait for the mutex unlock (TX_DONE event) */ + ieee802154_radio_request_transmit(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID)); + ieee802154_radio_set_rx_mode(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID), IEEE802154_RX_WAIT_FOR_ACK); + mutex_lock(&lock); + + event_post(EVENT_PRIO_HIGHEST, &_tx_finish_ev); +} + +static int _init(void) +{ + ieee802154_hal_test_init_devs(); + + /* Set the Event Notification */ + ((ieee802154_dev_t*) ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID))->cb = _hal_radio_cb; + + /* Note that addresses are not kept in the radio. This assumes MAC layers + * already have a copy of the address */ + luid_get_eui64(&ext_addr); + luid_get_short(&short_addr); + + /* Since the device was already initialized, turn on the radio. + * The transceiver state will be "TRX_OFF" */ + ieee802154_radio_request_on(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID)); + while(ieee802154_radio_confirm_on(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID)) == -EAGAIN) {} + + current_rx_mode = IEEE802154_RX_AACK_ENABLED; + ieee802154_radio_set_rx_mode(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID), current_rx_mode); + + uint16_t panid = CONFIG_IEEE802154_DEFAULT_PANID; + /* Set all IEEE addresses */ + ieee802154_radio_set_hw_addr_filter(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID), &short_addr, &ext_addr, &panid); + + /* Set PHY configuration */ + ieee802154_phy_conf_t conf = {.channel=CONFIG_IEEE802154_DEFAULT_CHANNEL, .page=CONFIG_IEEE802154_DEFAULT_SUBGHZ_PAGE, .pow=CONFIG_IEEE802154_DEFAULT_TXPOWER}; + + ieee802154_radio_config_phy(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID), &conf); + + /* Set the transceiver state to RX_ON in order to receive packets */ + _set_trx_state(IEEE802154_TRX_STATE_RX_ON, false); + + return 0; +} + +uint8_t payload[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam ornare lacinia mi elementum interdum ligula."; + +static int send(uint8_t *dst, size_t dst_len, + size_t len) +{ + uint8_t flags; + uint8_t mhr[IEEE802154_MAX_HDR_LEN]; + int mhr_len; + + le_uint16_t src_pan, dst_pan; + iolist_t iol_data = { + .iol_base = payload, + .iol_len = len, + .iol_next = NULL, + }; + + flags = IEEE802154_FCF_TYPE_DATA | IEEE802154_FCF_ACK_REQ; + + src_pan = byteorder_btols(byteorder_htons(CONFIG_IEEE802154_DEFAULT_PANID)); + dst_pan = byteorder_btols(byteorder_htons(CONFIG_IEEE802154_DEFAULT_PANID)); + uint8_t src_len = IEEE802154_LONG_ADDRESS_LEN; + void *src = &ext_addr; + + /* fill MAC header, seq should be set by device */ + if ((mhr_len = ieee802154_set_frame_hdr(mhr, src, src_len, + dst, dst_len, + src_pan, dst_pan, + flags, seq++)) < 0) { + puts("txtsnd: Error preperaring frame"); + return 1; + } + + iolist_t iol_hdr = { + .iol_next = &iol_data, + .iol_base = mhr, + .iol_len = mhr_len, + }; + + _send(&iol_hdr); + return 0; +} + +static inline int _dehex(char c, int default_) +{ + if ('0' <= c && c <= '9') { + return c - '0'; + } + else if ('A' <= c && c <= 'F') { + return c - 'A' + 10; + } + else if ('a' <= c && c <= 'f') { + return c - 'a' + 10; + } + else { + return default_; + } +} + +static size_t _parse_addr(uint8_t *out, size_t out_len, const char *in) +{ + const char *end_str = in; + uint8_t *out_end = out; + size_t count = 0; + int assert_cell = 1; + + if (!in || !*in) { + return 0; + } + while (end_str[1]) { + ++end_str; + } + + while (end_str >= in) { + int a = 0, b = _dehex(*end_str--, -1); + if (b < 0) { + if (assert_cell) { + return 0; + } + else { + assert_cell = 1; + continue; + } + } + assert_cell = 0; + + if (end_str >= in) { + a = _dehex(*end_str--, 0); + } + + if (++count > out_len) { + return 0; + } + *out_end++ = (a << 4) | b; + } + if (assert_cell) { + return 0; + } + /* out is reversed */ + + while (out < --out_end) { + uint8_t tmp = *out_end; + *out_end = *out; + *out++ = tmp; + } + + return count; +} + +int _cca(int argc, char **argv) +{ + (void) argc; + (void) argv; + ieee802154_radio_request_cca(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID)); + mutex_lock(&lock); + int res = ieee802154_radio_confirm_cca(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID)); + assert(res >= 0); + + if (res > 0) { + puts("CLEAR"); + } + else { + puts("BUSY"); + } + + return 0; +} + +static inline void _set_trx_state(int state, bool verbose) +{ + xtimer_ticks32_t a; + int res = ieee802154_radio_request_set_trx_state(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID), state); + if (verbose) { + a = xtimer_now(); + if(res != 0) { + printf("%i != 0 \n", res); + assert(false); + } + } + + while ((res = ieee802154_radio_confirm_set_trx_state(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID))) == -EAGAIN) {} + if (verbose) { + if (res != 0) { + printf("%i != 0 \n", res); + assert(false); + } + uint32_t secs = xtimer_usec_from_ticks(xtimer_diff(xtimer_now(), a)); + printf("\tTransition took %" PRIu32 " usecs\n", secs); + } +} + +int _test_states(int argc, char **argv) +{ + (void) argc; + (void) argv; + for (int i=0; i<3;i++) { + printf("Setting state to: %s\n", str_states[i]); + _set_trx_state(i, true); + for (int j=0; j<3; j++) { + if (i == j) { + continue; + } + printf("%s-> %s\n", str_states[i], str_states[j]); + _set_trx_state(j, true); + printf("%s-> %s\n", str_states[j], str_states[i]); + _set_trx_state(i, true); + } + puts(""); + } + + _set_trx_state(IEEE802154_TRX_STATE_RX_ON, true); + + return 0; +} + +int txtsnd(int argc, char **argv) +{ + uint8_t addr[IEEE802154_LONG_ADDRESS_LEN]; + size_t len; + size_t res; + + if (argc != 3) { + puts("Usage: txtsnd "); + return 1; + } + + res = _parse_addr(addr, sizeof(addr), argv[1]); + if (res == 0) { + puts("Usage: txtsnd "); + return 1; + } + len = atoi(argv[2]); + return send(addr, res, len); +} + +static int rx_mode_cmd(int argc, char **argv) +{ + ieee802154_rx_mode_t conf; + if (argc < 2) { + printf("Usage: %s ", argv[0]); + return 1; + } + + if (strcmp(argv[1], "pend") == 0) { + conf = IEEE802154_RX_AACK_FRAME_PENDING; + puts("ACK enabled with Frame Pending"); + } + else if (strcmp(argv[1], "off") == 0) { + conf = IEEE802154_RX_AACK_DISABLED; + puts("ACK disabled"); + } + else if (strcmp(argv[1], "promisc") == 0) { + conf = IEEE802154_RX_PROMISC; + puts("Promiscuous mode enabled"); + } + else { + conf = IEEE802154_RX_AACK_ENABLED; + puts("ACK enabled"); + } + + current_rx_mode = conf; + ieee802154_radio_set_rx_mode(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID), conf); + return 0; +} + +int config_phy(int argc, char **argv) +{ + if (argc < 3) { + puts("Usage: config_phy "); + return 1; + } + + uint8_t channel = atoi(argv[1]); + int8_t tx_pow = atoi(argv[2]); + + if (channel < 11 || channel > 26) { + puts("Wrong channel configuration (11 <= channel <= 26)."); + return 1; + } + ieee802154_dev_t *dev = ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID); + ieee802154_phy_conf_t conf = {.channel=channel, .page=0, .pow=tx_pow}; + if (ieee802154_radio_config_phy(dev, &conf) < 0) { + puts("Channel or TX power settings not supported"); + } + else { + puts("Success!"); + } + + /* Set the transceiver state to RX_ON in order to receive packets */ + _set_trx_state(IEEE802154_TRX_STATE_RX_ON, false); + + return 0; +} + +int txmode_cmd(int argc, char **argv) +{ + ieee802154_dev_t *dev = ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID); + + if (argc < 2) { + printf("Usage: %s \n", argv[0]); + return 1; + } + + ieee802154_csma_be_t csma_conf = {.min = 3, .max = 5}; + + int retries; + if (strcmp(argv[1], "direct") == 0) { + retries = -1; + } + else if (strcmp(argv[1], "cca") == 0) { + retries = 0; + } + else { + retries = 4; + } + + if (ieee802154_radio_set_csma_params(dev, &csma_conf, retries) < 0) { + puts("Not supported"); + } + else { + puts("Ok"); + } + return 0; +} + +static int _config_cca_cmd(int argc, char **argv) +{ + ieee802154_dev_t *dev = ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID); + ieee802154_cca_mode_t mode; + + if (argc < 2) { + printf("Usage: %s [ED threshold]\n", argv[0]); + return 1; + } + + if (strcmp(argv[1], "ed") == 0) { + mode = IEEE802154_CCA_MODE_ED_THRESHOLD; + } + else if (strcmp(argv[1], "cs") == 0) { + mode = IEEE802154_CCA_MODE_CARRIER_SENSING; + } + else if (strcmp(argv[1], "or") == 0) { + mode = IEEE802154_CCA_MODE_ED_THRESH_OR_CS; + } + else { + mode = IEEE802154_CCA_MODE_ED_THRESH_AND_CS; + } + + if (ieee802154_radio_set_cca_mode(dev, mode) == -ENOTSUP) { + puts("CCA mode not supported."); + return 1; + } + puts("CCA mode set."); + + if (argc > 2) { + int8_t thresh = atoi(argv[2]); + if (ieee802154_radio_set_cca_threshold(dev, thresh) < 0) { + puts("Error setting the threshold"); + return 1; + } + else { + printf("Set threshold to %i\n", thresh); + } + } + return 0; +} + +static int _caps_cmd(int argc, char **argv) +{ + (void) argc; + (void) argv; + + bool has_frame_retrans = false; + bool has_auto_csma = false; + bool has_24_ghz = false; + bool has_sub_ghz = false; + bool has_irq_tx_done = false; + bool has_irq_rx_start = false; + bool has_irq_ack_timeout = false; + bool has_irq_cca_done = false; + bool has_frame_retrans_info = false; + + if (ieee802154_radio_has_frame_retrans(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID))) { + has_frame_retrans = true; + } + + if (ieee802154_radio_has_auto_csma(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID))) { + has_auto_csma = true; + } + + if (ieee802154_radio_has_24_ghz(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID))) { + has_24_ghz = true; + } + + if (ieee802154_radio_has_sub_ghz(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID))) { + has_sub_ghz = true; + } + + if (ieee802154_radio_has_irq_tx_done(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID))) { + has_irq_tx_done = true; + } + + if (ieee802154_radio_has_irq_rx_start(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID))) { + has_irq_rx_start = true; + } + + if (ieee802154_radio_has_irq_ack_timeout(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID))) { + has_irq_ack_timeout = true; + } + + if (ieee802154_radio_has_irq_cca_done(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID))) { + has_irq_cca_done = true; + } + + if (ieee802154_radio_has_frame_retrans_info(ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID))) { + has_frame_retrans_info = true; + } + + puts(" CAPS "); + puts("================================="); + printf("- Frame Retransmissions: %s\n", has_frame_retrans ? "y" : "n"); + printf(" * Info retries: %s\n", has_frame_retrans_info ? "y" : "n"); + printf("- Auto CSMA-CA: %s\n", has_auto_csma ? "y" : "n"); + printf("- 2.4 GHz band: %s\n", has_24_ghz ? "y" : "n"); + printf("- SubGHz band: %s\n", has_sub_ghz ? "y" : "n"); + printf("- TX DONE indication: %s\n", has_irq_tx_done ? "y" : "n"); + printf(" * ACK Timeout indication: %s\n", has_irq_ack_timeout ? "y" : "n"); + printf("- RX_START indication: %s\n", has_irq_rx_start ? "y" : "n"); + printf("- CCA Done indication: %s\n", has_irq_cca_done ? "y" : "n"); + + return 0; +} + +static const shell_command_t shell_commands[] = { + { "config_phy", "Set channel and TX power", config_phy}, + { "print_addr", "Print IEEE802.15.4 addresses", print_addr}, + { "txtsnd", "Send IEEE 802.15.4 packet", txtsnd }, + { "test_states", "Test state changes", _test_states }, + { "cca", "Perform CCA", _cca }, + { "config_cca", "Config CCA parameters", _config_cca_cmd }, + { "rx_mode", "Enable/Disable AACK or set Frame Pending bit or set promiscuos mode", rx_mode_cmd }, + { "tx_mode", "Enable CSMA-CA, CCA or direct transmission", txmode_cmd }, + { "caps", "Get a list of caps supported by the device", _caps_cmd }, + { NULL, NULL, NULL } +}; + +int main(void) +{ + mutex_init(&lock); + mutex_lock(&lock); + _init(); + + /* start the shell */ + puts("Initialization successful - starting the shell now"); + + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + + return 0; +}