diff --git a/drivers/include/kw2xrf.h b/drivers/include/kw2xrf.h index 57b07f64f070..47195ef3a64c 100644 --- a/drivers/include/kw2xrf.h +++ b/drivers/include/kw2xrf.h @@ -88,20 +88,14 @@ extern "C" { #define KW2XRF_OPT_RAWDUMP (NETDEV_IEEE802154_RAW) /**< legacy define */ #define KW2XRF_OPT_ACK_REQ (NETDEV_IEEE802154_ACK_REQ) /**< legacy define */ -#define KW2XRF_OPT_AUTOCCA (0x0100) /**< CCA before TX active */ -#define KW2XRF_OPT_PROMISCUOUS (0x0200) /**< promiscuous mode - * active */ +#define KW2XRF_OPT_CSMA (0x0100) /**< use CSMA/CA algorithm for sending */ +#define KW2XRF_OPT_PROMISCUOUS (0x0200) /**< promiscuous mode active */ #define KW2XRF_OPT_PRELOADING (0x0400) /**< preloading enabled */ -#define KW2XRF_OPT_TELL_TX_START (0x0800) /**< notify MAC layer on TX - * start */ -#define KW2XRF_OPT_TELL_TX_END (0x1000) /**< notify MAC layer on TX - * finished */ -#define KW2XRF_OPT_TELL_RX_START (0x2000) /**< notify MAC layer on RX - * start */ -#define KW2XRF_OPT_TELL_RX_END (0x4000) /**< notify MAC layer on RX - * finished */ -#define KW2XRF_OPT_AUTOACK (0x8000) /**< enable automatically ACK - * for incommint packet */ +#define KW2XRF_OPT_TELL_TX_START (0x0800) /**< notify MAC layer on TX start */ +#define KW2XRF_OPT_TELL_TX_END (0x1000) /**< notify MAC layer on TX finished */ +#define KW2XRF_OPT_TELL_RX_START (0x2000) /**< notify MAC layer on RX start */ +#define KW2XRF_OPT_TELL_RX_END (0x4000) /**< notify MAC layer on RX finished */ +#define KW2XRF_OPT_AUTOACK (0x8000) /**< enable automatic ACK for incoming packet */ /** @} */ /** @@ -135,6 +129,16 @@ typedef struct { this is required to know when to return to @ref kw2xrf_t::idle_state */ int16_t tx_power; /**< The current tx-power setting of the device */ + + /** -----------------Internal CSMA algorithm parameters-----------------*/ + uint8_t csma_min_be; /**< Minimum backoff exponent (macMinBe) */ + uint8_t csma_max_be; /**< Maximum backoff exponent (macMaxBe) */ + uint8_t csma_be; /**< CSMA backoff exponent in the current transmission attempt */ + uint32_t csma_delay; /**< The actual delay for the CSMA algorithm */ + + /** -----------------Internal counters-----------------*/ + uint8_t num_backoffs; /**< Number of CSMA backoffs so far in the current transmission attempt */ + uint8_t max_backoffs; /**< Maximum number of CSMA backoffs when waiting for channel clear (macMaxCsmaBackoffs) */ /** @} */ } kw2xrf_t; diff --git a/drivers/kw2xrf/kw2xrf.c b/drivers/kw2xrf/kw2xrf.c index 456bf9d6acd1..9eda4e881c31 100644 --- a/drivers/kw2xrf/kw2xrf.c +++ b/drivers/kw2xrf/kw2xrf.c @@ -64,6 +64,12 @@ void kw2xrf_setup(kw2xrf_t *dev, const kw2xrf_params_t *params) dev->idle_state = XCVSEQ_RECEIVE; dev->state = 0; dev->pending_tx = 0; + + /* Set default parameters according to STD IEEE802.15.4-2015 */ + dev->csma_max_be = 5; + dev->csma_min_be = 3; + dev->max_backoffs = 4; + kw2xrf_spi_init(dev); kw2xrf_set_power_mode(dev, KW2XRF_IDLE); DEBUG("[kw2xrf] setup finished\n"); @@ -105,7 +111,7 @@ void kw2xrf_reset_phy(kw2xrf_t *dev) kw2xrf_set_option(dev, KW2XRF_OPT_AUTOACK, true); kw2xrf_set_option(dev, KW2XRF_OPT_ACK_REQ, true); - kw2xrf_set_option(dev, KW2XRF_OPT_AUTOCCA, true); + kw2xrf_set_option(dev, KW2XRF_OPT_CSMA, true); kw2xrf_set_power_mode(dev, KW2XRF_AUTODOZE); kw2xrf_set_sequence(dev, dev->idle_state); diff --git a/drivers/kw2xrf/kw2xrf_getset.c b/drivers/kw2xrf/kw2xrf_getset.c index 6b59d03da6ba..0bb0798c80ef 100644 --- a/drivers/kw2xrf/kw2xrf_getset.c +++ b/drivers/kw2xrf/kw2xrf_getset.c @@ -372,7 +372,7 @@ void kw2xrf_set_option(kw2xrf_t *dev, uint16_t option, bool state) /* trigger option specific actions */ switch (option) { - case KW2XRF_OPT_AUTOCCA: + case KW2XRF_OPT_CSMA: LOG_DEBUG("[kw2xrf] opt: enabling CCA before TX mode\n"); kw2xrf_set_dreg_bit(dev, MKW2XDM_PHY_CTRL1, MKW2XDM_PHY_CTRL1_CCABFRTX); @@ -423,7 +423,7 @@ void kw2xrf_set_option(kw2xrf_t *dev, uint16_t option, bool state) dev->netdev.flags &= ~(option); /* trigger option specific actions */ switch (option) { - case KW2XRF_OPT_AUTOCCA: + case KW2XRF_OPT_CSMA: kw2xrf_clear_dreg_bit(dev, MKW2XDM_PHY_CTRL1, MKW2XDM_PHY_CTRL1_CCABFRTX); break; diff --git a/drivers/kw2xrf/kw2xrf_netdev.c b/drivers/kw2xrf/kw2xrf_netdev.c index 629b4cc700a5..0335817c8f4d 100644 --- a/drivers/kw2xrf/kw2xrf_netdev.c +++ b/drivers/kw2xrf/kw2xrf_netdev.c @@ -18,13 +18,18 @@ * @author Johann Fischer * @author Peter Kietzmann * @author Joakim NohlgÄrd + * @author Adem-Can Agdas */ +// >>< TODO: Complete DEBUG messages +// >>< TODO: Code conventions + #include #include #include #include "log.h" +#include "random.h" #include "thread_flags.h" #include "net/eui64.h" #include "net/ieee802154.h" @@ -43,6 +48,7 @@ #include "debug.h" #define _MACACKWAITDURATION (864 / 16) /* 864us * 62500Hz */ +#define KW2XRF_CSMA_UNIT_TIME 20 //320 us #define KW2XRF_THREAD_FLAG_ISR (1 << 8) @@ -95,15 +101,52 @@ static size_t kw2xrf_tx_load(uint8_t *pkt_buf, uint8_t *buf, size_t len, size_t return offset + len; } +/** + * @brief Generate a random number for using as a CSMA delay value + */ +static inline uint32_t kw2xrf_csma_random_delay(kw2xrf_t *dev) +{ + /* Use topmost csma_be bits of the random number */ + uint32_t rnd = random_uint32() >> (32 - dev->csma_be); + + if(rnd == 0) + { + rnd = 1; //Wait at least ONE unit time, so timer target doesn't pass + } + + return (rnd * KW2XRF_CSMA_UNIT_TIME); +} + static void kw2xrf_tx_exec(kw2xrf_t *dev) { - if ((dev->netdev.flags & KW2XRF_OPT_ACK_REQ) && - (_send_last_fcf & IEEE802154_FCF_ACK_REQ)) { + kw2xrf_set_sequence(dev, XCVSEQ_IDLE); + + bool transmit_with_receive = ((dev->netdev.flags & KW2XRF_OPT_ACK_REQ) && + (_send_last_fcf & IEEE802154_FCF_ACK_REQ)); + bool csma_requested = (dev->netdev.flags & KW2XRF_OPT_CSMA); + + if(csma_requested) + { + /* Allow TMR2 interrupt to schedule a TX operation */ + /* This MUST be done BEFORE the sequence has been written to XCVSEQ, otherwise it will be executed immediately */ + /* For more info check the Reference Manual */ + /* Chapter 7.7.3 Using T2CMP to Trigger Transceiver Operations*/ + kw2xrf_timer2_seq_start_on(dev); + } + + if (transmit_with_receive) { kw2xrf_set_sequence(dev, XCVSEQ_TX_RX); } else { kw2xrf_set_sequence(dev, XCVSEQ_TRANSMIT); } + + if(csma_requested) + { + /* Enable TMR2 interrupt and set timeout for TMR2 */ + /* This should be done after the sequence has been written to XCVSEQ */ + kw2xrf_trigger_tx_ops_enable(dev, dev->csma_delay); + } } static void kw2xrf_wait_idle(kw2xrf_t *dev) @@ -152,7 +195,6 @@ static int _send(netdev_t *netdev, const iolist_t *iolist) len = kw2xrf_tx_load(pkt_buf, iol->iol_base, iol->iol_len, len); } - kw2xrf_set_sequence(dev, XCVSEQ_IDLE); dev->pending_tx++; /* @@ -168,6 +210,10 @@ static int _send(netdev_t *netdev, const iolist_t *iolist) /* send data out directly if pre-loading id disabled */ if (!(dev->netdev.flags & KW2XRF_OPT_PRELOADING)) { + /* New transmission! Reset the CSMA counters */ + dev->csma_be = dev->csma_min_be; + dev->num_backoffs = 0; + dev->csma_delay = kw2xrf_csma_random_delay(dev); kw2xrf_tx_exec(dev); } @@ -315,11 +361,32 @@ int _get(netdev_t *netdev, netopt_t opt, void *value, size_t len) !!(dev->netdev.flags & KW2XRF_OPT_TELL_TX_END); return sizeof(netopt_enable_t); - case NETOPT_AUTOCCA: + case NETOPT_CSMA: *((netopt_enable_t *)value) = - !!(dev->netdev.flags & KW2XRF_OPT_AUTOCCA); + !!(dev->netdev.flags & KW2XRF_OPT_CSMA); return sizeof(netopt_enable_t); + case NETOPT_CSMA_RETRIES: + if (len != sizeof(uint8_t)) { + return -EOVERFLOW; + } + *((uint8_t *)value) = dev->max_backoffs; + return len; + + case NETOPT_CSMA_MAXBE: + if (len != sizeof(uint8_t)) { + return -EOVERFLOW; + } + *((uint8_t *)value) = dev->csma_max_be; + return len; + + case NETOPT_CSMA_MINBE: + if (len != sizeof(uint8_t)) { + return -EOVERFLOW; + } + *((uint8_t *)value) = dev->csma_min_be; + return len; + case NETOPT_CHANNEL: if (len < sizeof(uint16_t)) { return -EOVERFLOW; @@ -503,13 +570,39 @@ static int _set(netdev_t *netdev, netopt_t opt, const void *value, size_t len) ((bool *)value)[0]); res = sizeof(netopt_enable_t); break; - - case NETOPT_AUTOCCA: - kw2xrf_set_option(dev, KW2XRF_OPT_AUTOCCA, + case NETOPT_CSMA: + kw2xrf_set_option(dev, KW2XRF_OPT_CSMA, ((bool *)value)[0]); res = sizeof(netopt_enable_t); break; + case NETOPT_CSMA_RETRIES: + if (len != sizeof(uint8_t)) { + res = -EOVERFLOW; + break; + } + dev->max_backoffs = *((const uint8_t*)value); + res = len; + break; + + case NETOPT_CSMA_MAXBE: + if (len != sizeof(uint8_t)) { + res = -EOVERFLOW; + break; + } + dev->csma_max_be = *((const uint8_t*)value); + res = len; + break; + + case NETOPT_CSMA_MINBE: + if (len != sizeof(uint8_t)) { + res = -EOVERFLOW; + break; + } + dev->csma_min_be = *((const uint8_t*)value); + res = len; + break; + case NETOPT_CCA_THRESHOLD: if (len < sizeof(uint8_t)) { res = -EOVERFLOW; @@ -614,21 +707,55 @@ static void _isr_event_seq_t(netdev_t *netdev, uint8_t *dregs) if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_SEQIRQ) { DEBUG("[kw2xrf] SEQIRQ\n"); irqsts1 |= MKW2XDM_IRQSTS1_SEQIRQ; + bool retransmission_issued = false; if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_CCAIRQ) { irqsts1 |= MKW2XDM_IRQSTS1_CCAIRQ; + if (dregs[MKW2XDM_IRQSTS2] & MKW2XDM_IRQSTS2_CCA) { DEBUG("[kw2xrf] CCA CH busy\n"); - netdev->event_callback(netdev, NETDEV_EVENT_TX_MEDIUM_BUSY); + /* Channel busy: Try CSMA backoff */ + /* Info: The fact, that a CCA was performed here, means that NETOPT_CSMA was set before, so we don't have to check that */ + /* Limit reached ? */ + if(dev->num_backoffs < dev->max_backoffs) + { + /* Limit for retransmissions hasn't been reached yet: try again! */ + dev->num_backoffs++; + if(dev->csma_be < dev->csma_max_be) + { + dev->csma_be++; + } + dev->csma_delay = kw2xrf_csma_random_delay(dev); + kw2xrf_tx_exec(dev); + retransmission_issued = true; + } + else + { + /* Limit for retransmissions reached: give up */ + netdev->event_callback(netdev, NETDEV_EVENT_TX_MEDIUM_BUSY); + } } else { netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE); } } - assert(dev->pending_tx != 0); - dev->pending_tx--; - kw2xrf_set_idle_sequence(dev); + if(!retransmission_issued) + { + /* If a retransmission was issued, that means we're not done yet! */ + assert(dev->pending_tx != 0); + dev->pending_tx--; + kw2xrf_set_idle_sequence(dev); + } + } + + if (dregs[MKW2XDM_IRQSTS3] & MKW2XDM_IRQSTS3_TMR2IRQ) { + /* TMR2 interrupt means a TX operation that was scheduled by TMR2 has been executed */ + + /* Disallow TMR2 interrupt to schedule a TX operation */ + kw2xrf_timer2_seq_start_off(dev); + /* Disable TMR2 interrupt and reset TMR2IRQ */ + kw2xrf_trigger_tx_ops_disable(dev); } kw2xrf_write_dreg(dev, MKW2XDM_IRQSTS1, irqsts1); @@ -689,18 +816,37 @@ static void _isr_event_seq_tr(netdev_t *netdev, uint8_t *dregs) } if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_SEQIRQ) { + DEBUG("[kw2xrf] SEQIRQ\n"); + irqsts1 |= MKW2XDM_IRQSTS1_SEQIRQ; + bool retry_issued = false; + if (dregs[MKW2XDM_IRQSTS1] & MKW2XDM_IRQSTS1_CCAIRQ) { irqsts1 |= MKW2XDM_IRQSTS1_CCAIRQ; if (dregs[MKW2XDM_IRQSTS2] & MKW2XDM_IRQSTS2_CCA) { DEBUG("[kw2xrf] CCA CH busy\n"); - netdev->event_callback(netdev, NETDEV_EVENT_TX_MEDIUM_BUSY); + /* Channel busy: Try CSMA backoff */ + /* Info: The fact, that a CCA was performed here, means that NETOPT_CSMA was set before, so we don't have to check that */ + /* Limit reached ? */ + if(dev->num_backoffs < dev->max_backoffs) + { + /* Limit for backoffs hasn't been reached yet: try again! */ + dev->num_backoffs++; + if(dev->csma_be < dev->csma_max_be) + { + dev->csma_be++; + } + dev->csma_delay = kw2xrf_csma_random_delay(dev); + kw2xrf_tx_exec(dev); + retry_issued = true; + } + else + { + /* Limit for retransmissions reached: give up */ + netdev->event_callback(netdev, NETDEV_EVENT_TX_MEDIUM_BUSY); + } } } - irqsts1 |= MKW2XDM_IRQSTS1_SEQIRQ; - assert(dev->pending_tx != 0); - dev->pending_tx--; - if (dregs[MKW2XDM_IRQSTS3] & MKW2XDM_IRQSTS3_TMR3IRQ) { /* if the sequence was aborted by timer 3, ACK timed out */ DEBUG("[kw2xrf] TC3TMOUT, SEQIRQ, TX failed\n"); @@ -714,8 +860,23 @@ static void _isr_event_seq_tr(netdev_t *netdev, uint8_t *dregs) kw2xrf_timer3_seq_abort_off(dev); /* Disable interrupt for TMR3 and reset TMR3IRQ */ kw2xrf_abort_rx_ops_disable(dev); - /* Go back to idle state */ - kw2xrf_set_idle_sequence(dev); + + if(!retry_issued) + { + /* If a retry was issued, that means we're not done yet! */ + assert(dev->pending_tx != 0); + dev->pending_tx--; + kw2xrf_set_idle_sequence(dev); + } + } + + if (dregs[MKW2XDM_IRQSTS3] & MKW2XDM_IRQSTS3_TMR2IRQ) { + /* TMR2 interrupt means a TX operation that was scheduled by TMR2 has been executed */ + + /* Disallow TMR2 interrupt to schedule a TX operation */ + kw2xrf_timer2_seq_start_off(dev); + /* Disable TMR2 interrupt and reset TMR2IRQ */ + kw2xrf_trigger_tx_ops_disable(dev); } kw2xrf_write_dreg(dev, MKW2XDM_IRQSTS1, irqsts1);