diff --git a/cc2520.h b/cc2520.h index d700e9f..304ba86 100644 --- a/cc2520.h +++ b/cc2520.h @@ -76,6 +76,11 @@ #define CC2520_DEF_INIT_BACKOFF 4960 #define CC2520_DEF_CONG_BACKOFF 2240 +// We go for around a 1% duty cycle of the radio +// for LPL stuff. +#define CC2520_DEF_LPL_WAKEUP_INTERVAL 512000 +#define CC2520_DEF_LPL_LISTEN_WINDOW 5120 + // Error codes #define CC2520_TX_SUCCESS 0 #define CC2520_TX_BUSY 1 diff --git a/csma.c b/csma.c index aef0120..425ce80 100644 --- a/csma.c +++ b/csma.c @@ -47,6 +47,7 @@ int cc2520_csma_init() backoff_max_init = CC2520_DEF_INIT_BACKOFF; backoff_max_cong = CC2520_DEF_CONG_BACKOFF; + spin_lock_init(&state_sl); csma_state = CC2520_CSMA_IDLE; cur_tx_buf = kmalloc(PKT_BUFF_SIZE, GFP_KERNEL); @@ -97,10 +98,10 @@ static void cc2520_csma_start_timer(int us_period) static enum hrtimer_restart cc2520_csma_timer_cb(struct hrtimer *timer) { - ktime_t kt; - int new_backoff; + ktime_t kt; + int new_backoff; - //printk(KERN_INFO "[cc2520] - csma timer fired. \n"); + //printk(KERN_INFO "[cc2520] - csma timer fired. \n"); if (cc2520_radio_is_clear()) { //printk(KERN_INFO "[cc2520] - channel clear, sending.\n"); csma_bottom->tx(cur_tx_buf, cur_tx_len); diff --git a/interface.c b/interface.c index 1da7971..eb6285c 100644 --- a/interface.c +++ b/interface.c @@ -89,7 +89,7 @@ static ssize_t interface_write( else { result = down_interruptible(&tx_sem); if (result) - return -ERESTARTSYS; + return -ERESTARTSYS; } DBG((KERN_INFO "[cc2520] - write lock obtained.\n")); @@ -106,9 +106,10 @@ static ssize_t interface_write( // the form of a semaphore. interface_bottom->tx(tx_buf_c, pkt_len); result = down_interruptible(&tx_done_sem); - if (result) + if (result) { return -ERESTARTSYS; - + } + // Step 4: Finally return and allow other callers to write // packets. DBG((KERN_INFO "[cc2520] - wrote %d bytes.\n", pkt_len)); diff --git a/lpl.c b/lpl.c index 112036f..08086bc 100644 --- a/lpl.c +++ b/lpl.c @@ -1,5 +1,10 @@ #include +#include +#include +#include + #include "lpl.h" +#include "packet.h" #include "cc2520.h" struct cc2520_interface *lpl_top; @@ -8,6 +13,27 @@ struct cc2520_interface *lpl_bottom; static int cc2520_lpl_tx(u8 * buf, u8 len); static void cc2520_lpl_tx_done(u8 status); static void cc2520_lpl_rx_done(u8 *buf, u8 len); +static enum hrtimer_restart cc2520_lpl_timer_cb(struct hrtimer *timer); +static void cc2520_lpl_start_timer(void); + +static int lpl_window; +static int lpl_interval; +static bool lpl_enabled; + +static struct hrtimer lpl_timer; + +static u8* cur_tx_buf; +static u8 cur_tx_len; + +static spinlock_t state_sl; + +enum cc2520_lpl_state_enum { + CC2520_LPL_IDLE, + CC2520_LPL_TX, + CC2520_LPL_TIMER_EXPIRED +}; + +static int lpl_state; int cc2520_lpl_init() { @@ -15,25 +41,150 @@ int cc2520_lpl_init() lpl_bottom->tx_done = cc2520_lpl_tx_done; lpl_bottom->rx_done = cc2520_lpl_rx_done; + lpl_window = CC2520_DEF_LPL_LISTEN_WINDOW; + lpl_interval = CC2520_DEF_LPL_WAKEUP_INTERVAL; + lpl_enabled = true; + + cur_tx_buf = kmalloc(PKT_BUFF_SIZE, GFP_KERNEL); + if (!cur_tx_buf) { + goto error; + } + + spin_lock_init(&state_sl); + lpl_state = CC2520_LPL_IDLE; + + hrtimer_init(&lpl_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + lpl_timer.function = &cc2520_lpl_timer_cb; + return 0; + + error: + if (cur_tx_buf) { + kfree(cur_tx_buf); + cur_tx_buf = NULL; + } + + return -EFAULT; } void cc2520_lpl_free() { + if (cur_tx_buf) { + kfree(cur_tx_buf); + cur_tx_buf = NULL; + } + hrtimer_cancel(&lpl_timer); } static int cc2520_lpl_tx(u8 * buf, u8 len) { - return lpl_bottom->tx(buf, len); + if (lpl_enabled) { + spin_lock(&state_sl); + if (lpl_state == CC2520_LPL_IDLE) { + lpl_state = CC2520_LPL_TX; + spin_unlock(&state_sl); + + memcpy(cur_tx_buf, buf, len); + cur_tx_len = len; + + lpl_bottom->tx(cur_tx_buf, cur_tx_len); + cc2520_lpl_start_timer(); + } + else { + spin_unlock(&state_sl); + lpl_top->tx_done(-CC2520_TX_BUSY); + } + + return 0; + } + else { + return lpl_bottom->tx(buf, len); + } } static void cc2520_lpl_tx_done(u8 status) { - lpl_top->tx_done(status); + if (lpl_enabled) { + spin_lock(&state_sl); + if (cc2520_packet_requires_ack_wait(cur_tx_buf)) { + if (status == CC2520_TX_SUCCESS) { + lpl_state = CC2520_LPL_IDLE; + spin_unlock(&state_sl); + + hrtimer_cancel(&lpl_timer); + lpl_top->tx_done(status); + } + else if (lpl_state == CC2520_LPL_TIMER_EXPIRED) { + lpl_state = CC2520_LPL_IDLE; + spin_unlock(&state_sl); + lpl_top->tx_done(-CC2520_TX_FAILED); + } + else { + spin_unlock(&state_sl); + printk(KERN_INFO "[cc2520] - lpl retransmit.\n"); + lpl_bottom->tx(cur_tx_buf, cur_tx_len); + } + } + else { + if (lpl_state == CC2520_LPL_TIMER_EXPIRED) { + lpl_state = CC2520_LPL_IDLE; + spin_unlock(&state_sl); + lpl_top->tx_done(CC2520_TX_SUCCESS); + } + else { + spin_unlock(&state_sl); + lpl_bottom->tx(cur_tx_buf, cur_tx_len); + } + } + } + else { + lpl_top->tx_done(status); + } + // if packet requires ack, examine status. + // if success terminate LPL window + // else if status != TIMER_EXPIRED resend + // else resend } static void cc2520_lpl_rx_done(u8 *buf, u8 len) { lpl_top->rx_done(buf, len); +} + +static void cc2520_lpl_start_timer() +{ + ktime_t kt; + kt = ktime_set(0, 1000 * (lpl_interval + 2 * lpl_window)); + hrtimer_start(&lpl_timer, kt, HRTIMER_MODE_REL); +} + +static enum hrtimer_restart cc2520_lpl_timer_cb(struct hrtimer *timer) +{ + spin_lock(&state_sl); + if (lpl_state == CC2520_LPL_TX) { + lpl_state = CC2520_LPL_TIMER_EXPIRED; + spin_unlock(&state_sl); + } + else { + spin_unlock(&state_sl); + INFO((KERN_INFO "[cc2520] - lpl timer in improbable state.\n")); + } + + return HRTIMER_NORESTART; +} + +void cc2520_lpl_set_enabled(bool enabled) +{ + lpl_enabled = enabled; +} + +void cc2520_lpl_set_listen_length(int length) +{ + lpl_window = length; +} + +void cc2520_lpl_set_wakeup_interval(int interval) +{ + lpl_interval = interval; } \ No newline at end of file diff --git a/lpl.h b/lpl.h index ab726a3..8e8d522 100644 --- a/lpl.h +++ b/lpl.h @@ -9,4 +9,8 @@ extern struct cc2520_interface *lpl_bottom; int cc2520_lpl_init(void); void cc2520_lpl_free(void); +void cc2520_lpl_set_enabled(bool enabled); +void cc2520_lpl_set_listen_length(int length); +void cc2520_lpl_set_wakeup_interval(int interval); + #endif \ No newline at end of file