diff --git a/core/net/mac/contikimac.c b/core/net/mac/contikimac.c index e81f6c953..78c766d4d 100644 --- a/core/net/mac/contikimac.c +++ b/core/net/mac/contikimac.c @@ -58,9 +58,6 @@ #ifndef WITH_PHASE_OPTIMIZATION #define WITH_PHASE_OPTIMIZATION 1 #endif -#ifndef WITH_STREAMING -#define WITH_STREAMING 0 -#endif #ifdef CONTIKIMAC_CONF_WITH_CONTIKIMAC_HEADER #define WITH_CONTIKIMAC_HEADER CONTIKIMAC_CONF_WITH_CONTIKIMAC_HEADER #else @@ -90,6 +87,14 @@ struct hdr { #define CYCLE_TIME (RTIMER_ARCH_SECOND / NETSTACK_RDC_CHANNEL_CHECK_RATE) #endif +/* Are we currently receiving a burst? */ +static int we_are_receiving_burst = 0; +/* Has the receiver been awoken by a burst we're sending? */ +static int is_receiver_awake = 0; + +/* BURST_RECV_TIME is the maximum time a receiver waits for the + next packet of a burst when FRAME_PENDING is set. */ +#define INTER_PACKET_DEADLINE CLOCK_SECOND / 32 /* ContikiMAC performs periodic channel checks. Each channel check consists of two or more CCA checks. CCA_COUNT_MAX is the number of @@ -97,6 +102,10 @@ struct hdr { two.*/ #define CCA_COUNT_MAX 2 +/* Before starting a transmission, Contikimac checks the availability + of the channel with CCA_COUNT_MAX_TX consecutive CCAs */ +#define CCA_COUNT_MAX_TX 6 + /* CCA_CHECK_TIME is the time it takes to perform a CCA check. */ #define CCA_CHECK_TIME RTIMER_ARCH_SECOND / 8192 @@ -107,6 +116,10 @@ struct hdr { CCAs. */ #define CHECK_TIME (CCA_COUNT_MAX * (CCA_CHECK_TIME + CCA_SLEEP_TIME)) +/* CHECK_TIME_TX is the total time it takes to perform CCA_COUNT_MAX_TX + CCAs. */ +#define CHECK_TIME_TX (CCA_COUNT_MAX_TX * (CCA_CHECK_TIME + CCA_SLEEP_TIME)) + /* LISTEN_TIME_AFTER_PACKET_DETECTED is the time that we keep checking for activity after a potential packet has been detected by a CCA check. */ @@ -130,7 +143,7 @@ struct hdr { /* GUARD_TIME is the time before the expected phase of a neighbor that a transmitted should begin transmitting packets. */ -#define GUARD_TIME 11 * CHECK_TIME +#define GUARD_TIME 10 * CHECK_TIME + CHECK_TIME_TX /* INTER_PACKET_INTERVAL is the interval between two successive packet transmissions */ #define INTER_PACKET_INTERVAL RTIMER_ARCH_SECOND / 5000 @@ -174,10 +187,6 @@ static volatile unsigned char radio_is_on = 0; #define PRINTDEBUG(...) #endif -/* Flag that is used to keep track of whether or not we are snooping - for announcements from neighbors. */ -static volatile uint8_t is_snooping; - #if CONTIKIMAC_CONF_COMPOWER static struct compower_activity current_packet; #endif /* CONTIKIMAC_CONF_COMPOWER */ @@ -198,10 +207,6 @@ PHASE_LIST(phase_list, MAX_PHASE_NEIGHBORS); #endif /* WITH_PHASE_OPTIMIZATION */ -static volatile uint8_t is_streaming; -static rimeaddr_t is_streaming_to, is_streaming_to_too; -static volatile rtimer_clock_t stream_until; - #define DEFAULT_STREAM_TIME (4 * CYCLE_TIME) #ifndef MIN @@ -238,7 +243,7 @@ on(void) static void off(void) { - if(contikimac_is_on && radio_is_on != 0 && /*is_streaming == 0 &&*/ + if(contikimac_is_on && radio_is_on != 0 && contikimac_keep_radio_on == 0) { radio_is_on = 0; NETSTACK_RADIO.off(); @@ -292,7 +297,7 @@ powercycle_turn_radio_off(void) uint8_t was_on = radio_is_on; #endif /* CONTIKIMAC_CONF_COMPOWER */ - if(we_are_sending == 0) { + if(we_are_sending == 0 && we_are_receiving_burst == 0) { off(); #if CONTIKIMAC_CONF_COMPOWER if(was_on && !radio_is_on) { @@ -305,7 +310,7 @@ powercycle_turn_radio_off(void) static void powercycle_turn_radio_on(void) { - if(we_are_sending == 0) { + if(we_are_sending == 0 && we_are_receiving_burst == 0) { on(); } } @@ -324,93 +329,82 @@ powercycle(struct rtimer *t, void *ptr) cycle_start += CYCLE_TIME; - if(WITH_STREAMING && is_streaming) { - if(!RTIMER_CLOCK_LT(RTIMER_NOW(), stream_until)) { - is_streaming = 0; - rimeaddr_copy(&is_streaming_to, &rimeaddr_null); - rimeaddr_copy(&is_streaming_to_too, &rimeaddr_null); - } - } - packet_seen = 0; - do { - for(count = 0; count < CCA_COUNT_MAX; ++count) { - t0 = RTIMER_NOW(); - if(we_are_sending == 0) { - powercycle_turn_radio_on(); - /* Check if a packet is seen in the air. If so, we keep the + for(count = 0; count < CCA_COUNT_MAX; ++count) { + t0 = RTIMER_NOW(); + if(we_are_sending == 0 && we_are_receiving_burst == 0) { + powercycle_turn_radio_on(); + /* Check if a packet is seen in the air. If so, we keep the radio on for a while (LISTEN_TIME_AFTER_PACKET_DETECTED) to be able to receive the packet. We also continuously check the radio medium to make sure that we wasn't woken up by a false positive: a spurious radio interference that was not caused by an incoming packet. */ - if(NETSTACK_RADIO.channel_clear() == 0) { - packet_seen = 1; - break; - } - powercycle_turn_radio_off(); + if(NETSTACK_RADIO.channel_clear() == 0) { + packet_seen = 1; + break; } - schedule_powercycle_fixed(t, RTIMER_NOW() + CCA_SLEEP_TIME); - PT_YIELD(&pt); + powercycle_turn_radio_off(); } + schedule_powercycle_fixed(t, RTIMER_NOW() + CCA_SLEEP_TIME); + PT_YIELD(&pt); + } - if(packet_seen) { - static rtimer_clock_t start; - static uint8_t silence_periods, periods; - start = RTIMER_NOW(); - - periods = silence_periods = 0; - while(we_are_sending == 0 && radio_is_on && - RTIMER_CLOCK_LT(RTIMER_NOW(), - (start + LISTEN_TIME_AFTER_PACKET_DETECTED))) { - - /* Check for a number of consecutive periods of + if(packet_seen) { + static rtimer_clock_t start; + static uint8_t silence_periods, periods; + start = RTIMER_NOW(); + + periods = silence_periods = 0; + while(we_are_sending == 0 && radio_is_on && + RTIMER_CLOCK_LT(RTIMER_NOW(), + (start + LISTEN_TIME_AFTER_PACKET_DETECTED))) { + + /* Check for a number of consecutive periods of non-activity. If we see two such periods, we turn the radio off. Also, if a packet has been successfully received (as indicated by the NETSTACK_RADIO.pending_packet() function), we stop snooping. */ - if(NETSTACK_RADIO.channel_clear()) { - ++silence_periods; - } else { - silence_periods = 0; - } - - ++periods; - - if(NETSTACK_RADIO.receiving_packet()) { - silence_periods = 0; - } - if(silence_periods > MAX_SILENCE_PERIODS) { - powercycle_turn_radio_off(); - break; - } - if(WITH_FAST_SLEEP && - periods > MAX_NONACTIVITY_PERIODS && - !(NETSTACK_RADIO.receiving_packet() || - NETSTACK_RADIO.pending_packet())) { - powercycle_turn_radio_off(); - break; - } - if(NETSTACK_RADIO.pending_packet()) { - break; - } - - schedule_powercycle(t, CCA_CHECK_TIME + CCA_SLEEP_TIME); - PT_YIELD(&pt); + if(NETSTACK_RADIO.channel_clear()) { + ++silence_periods; + } else { + silence_periods = 0; } - if(radio_is_on) { - if(!(NETSTACK_RADIO.receiving_packet() || - NETSTACK_RADIO.pending_packet()) || + + ++periods; + + if(NETSTACK_RADIO.receiving_packet()) { + silence_periods = 0; + } + if(silence_periods > MAX_SILENCE_PERIODS) { + powercycle_turn_radio_off(); + break; + } + if(WITH_FAST_SLEEP && + periods > MAX_NONACTIVITY_PERIODS && + !(NETSTACK_RADIO.receiving_packet() || + NETSTACK_RADIO.pending_packet())) { + powercycle_turn_radio_off(); + break; + } + if(NETSTACK_RADIO.pending_packet()) { + break; + } + + schedule_powercycle(t, CCA_CHECK_TIME + CCA_SLEEP_TIME); + PT_YIELD(&pt); + } + if(radio_is_on) { + if(!(NETSTACK_RADIO.receiving_packet() || + NETSTACK_RADIO.pending_packet()) || !RTIMER_CLOCK_LT(RTIMER_NOW(), - (start + LISTEN_TIME_AFTER_PACKET_DETECTED))) { - powercycle_turn_radio_off(); - } + (start + LISTEN_TIME_AFTER_PACKET_DETECTED))) { + powercycle_turn_radio_off(); } } - } while((is_snooping || is_streaming) && - RTIMER_CLOCK_LT(RTIMER_NOW() - cycle_start, CYCLE_TIME - CHECK_TIME * 8)); + } if(RTIMER_CLOCK_LT(RTIMER_NOW() - cycle_start, CYCLE_TIME - CHECK_TIME * 4)) { schedule_powercycle_fixed(t, CYCLE_TIME + cycle_start); @@ -443,7 +437,7 @@ broadcast_rate_drop(void) } /*---------------------------------------------------------------------------*/ static int -send_packet(mac_callback_t mac_callback, void *mac_callback_ptr) +send_packet(mac_callback_t mac_callback, void *mac_callback_ptr, struct rdc_buf_list *buf_list) { rtimer_clock_t t0; rtimer_clock_t encounter_time = 0, previous_txtime = 0; @@ -458,6 +452,7 @@ send_packet(mac_callback_t mac_callback, void *mac_callback_ptr) int i; int ret; uint8_t contikimac_was_on; + uint8_t seqno; #if WITH_CONTIKIMAC_HEADER struct hdr *chdr; #endif /* WITH_CONTIKIMAC_HEADER */ @@ -495,25 +490,6 @@ send_packet(mac_callback_t mac_callback, void *mac_callback_ptr) is_reliable = packetbuf_attr(PACKETBUF_ATTR_RELIABLE) || packetbuf_attr(PACKETBUF_ATTR_ERELIABLE); - if(WITH_STREAMING) { - if(packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) == - PACKETBUF_ATTR_PACKET_TYPE_STREAM) { - if(rimeaddr_cmp(&is_streaming_to, &rimeaddr_null)) { - rimeaddr_copy(&is_streaming_to, - packetbuf_addr(PACKETBUF_ADDR_RECEIVER)); - } else if(!rimeaddr_cmp - (&is_streaming_to, packetbuf_addr(PACKETBUF_ADDR_RECEIVER))) { - rimeaddr_copy(&is_streaming_to_too, - packetbuf_addr(PACKETBUF_ADDR_RECEIVER)); - } - stream_until = RTIMER_NOW() + DEFAULT_STREAM_TIME; - is_streaming = 1; - } - } - - if(is_streaming) { - packetbuf_set_attr(PACKETBUF_ATTR_PENDING, 1); - } packetbuf_set_attr(PACKETBUF_ATTR_MAC_ACK, 1); #if WITH_CONTIKIMAC_HEADER @@ -568,11 +544,11 @@ send_packet(mac_callback_t mac_callback, void *mac_callback_ptr) /* Remove the MAC-layer header since it will be recreated next time around. */ packetbuf_hdr_remove(hdrlen); - if(!is_broadcast && !is_streaming) { + if(!is_broadcast && !is_receiver_awake) { #if WITH_PHASE_OPTIMIZATION ret = phase_wait(&phase_list, packetbuf_addr(PACKETBUF_ADDR_RECEIVER), CYCLE_TIME, GUARD_TIME, - mac_callback, mac_callback_ptr); + mac_callback, mac_callback_ptr, buf_list, 0); if(ret == PHASE_DEFERRED) { return MAC_TX_DEFERRED; } @@ -617,10 +593,10 @@ send_packet(mac_callback_t mac_callback, void *mac_callback_ptr) contikimac_is_on when we are done. */ contikimac_was_on = contikimac_is_on; contikimac_is_on = 1; - - if(is_streaming == 0) { + + if(is_receiver_awake == 0) { /* Check if there are any transmissions by others. */ - for(i = 0; i < CCA_COUNT_MAX; ++i) { + for(i = 0; i < CCA_COUNT_MAX_TX; ++i) { t0 = RTIMER_NOW(); on(); while(RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + CCA_CHECK_TIME)) { } @@ -646,21 +622,22 @@ send_packet(mac_callback_t mac_callback, void *mac_callback_ptr) if(!is_broadcast) { on(); } - + watchdog_periodic(); t0 = RTIMER_NOW(); + seqno = packetbuf_attr(PACKETBUF_ATTR_MAC_SEQNO); for(strobes = 0, collisions = 0; got_strobe_ack == 0 && collisions == 0 && RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + STROBE_TIME); strobes++) { watchdog_periodic(); - - if(is_known_receiver && !RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + MAX_PHASE_STROBE_TIME)) { + + if((is_receiver_awake || is_known_receiver) && !RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + MAX_PHASE_STROBE_TIME)) { PRINTF("miss to %d\n", packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[0]); break; } - + len = 0; previous_txtime = RTIMER_NOW(); @@ -683,7 +660,7 @@ send_packet(mac_callback_t mac_callback, void *mac_callback_ptr) while(RTIMER_CLOCK_LT(RTIMER_NOW(), wt + AFTER_ACK_DETECTECT_WAIT_TIME)) { } len = NETSTACK_RADIO.read(ackbuf, ACK_LEN); - if(len == ACK_LEN) { + if(len == ACK_LEN && seqno == ackbuf[ACK_LEN-1]) { got_strobe_ack = 1; encounter_time = previous_txtime; break; @@ -740,42 +717,97 @@ send_packet(mac_callback_t mac_callback, void *mac_callback_ptr) } if(!is_broadcast) { - if(collisions == 0 && is_streaming == 0) { + if(collisions == 0 && is_receiver_awake == 0) { phase_update(&phase_list, packetbuf_addr(PACKETBUF_ADDR_RECEIVER), encounter_time, ret); } } #endif /* WITH_PHASE_OPTIMIZATION */ - if(WITH_STREAMING) { - if(packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) == - PACKETBUF_ATTR_PACKET_TYPE_STREAM_END) { - is_streaming = 0; - } - } - return ret; } /*---------------------------------------------------------------------------*/ static void qsend_packet(mac_callback_t sent, void *ptr) { - int ret = send_packet(sent, ptr); + int ret = send_packet(sent, ptr, NULL); if(ret != MAC_TX_DEFERRED) { mac_call_sent_callback(sent, ptr, ret, 1); } } /*---------------------------------------------------------------------------*/ +void +qsend_list(mac_callback_t sent, void *ptr, struct rdc_buf_list *buf_list) +{ + struct rdc_buf_list *curr = buf_list; + struct rdc_buf_list *next; + int ret; + if(curr == NULL) { + mac_call_sent_callback(sent, ptr, MAC_TX_ERR, 1); + return; + } + /* Do not send during reception of a burst */ + if(we_are_receiving_burst) { + queuebuf_to_packetbuf(curr->buf); + /* We try to defer, and return an error this wasn't possible */ + int ret = phase_wait(&phase_list, packetbuf_addr(PACKETBUF_ADDR_RECEIVER), + CYCLE_TIME, GUARD_TIME, + sent, ptr, curr, 2); + if(ret != PHASE_DEFERRED) { + mac_call_sent_callback(sent, ptr, MAC_TX_ERR, 1); + } + return; + } + /* The receiver needs to be awoken before we send */ + is_receiver_awake = 0; + do { /* A loop sending a burst of packets from buf_list */ + next = list_item_next(curr); + + /* Prepare the packetbuf */ + queuebuf_to_packetbuf(curr->buf); + if(next != NULL) { + packetbuf_set_attr(PACKETBUF_ATTR_PENDING, 1); + } + + /* Send the current packet */ + ret = send_packet(sent, ptr, curr); + if(ret != MAC_TX_DEFERRED) { + mac_call_sent_callback(sent, ptr, ret, 1); + } + + if(ret == MAC_TX_OK) { + if(next != NULL) { + /* We're in a burst, no need to wake the receiver up again */ + is_receiver_awake = 1; + curr = next; + } + } else { + /* The transmission failed, we stop the burst */ + next = NULL; + } + } while(next != NULL); + is_receiver_awake = 0; +} +/*---------------------------------------------------------------------------*/ +/* Timer callback triggered when receiving a burst, after having waited for a next + packet for a too long time. Turns the radio off and leaves burst reception mode */ +static void +recv_burst_off(void *ptr) +{ + off(); + we_are_receiving_burst = 0; +} +/*---------------------------------------------------------------------------*/ static void input_packet(void) { - /* We have received the packet, so we can go back to being - asleep. */ - off(); + static struct ctimer ct; + if(!we_are_receiving_burst) { + off(); + } /* printf("cycle_start 0x%02x 0x%02x\n", cycle_start, cycle_start % CYCLE_TIME);*/ - if(packetbuf_totlen() > 0 && NETSTACK_FRAMER.parse()) { #if WITH_CONTIKIMAC_HEADER @@ -798,14 +830,16 @@ input_packet(void) /* This is a regular packet that is destined to us or to the broadcast address. */ -#if WITH_PHASE_OPTIMIZATION - /* If the sender has set its pending flag, it has its radio - turned on and we should drop the phase estimation that we - have from before. */ - if(packetbuf_attr(PACKETBUF_ATTR_PENDING)) { - phase_remove(&phase_list, packetbuf_addr(PACKETBUF_ADDR_SENDER)); + /* If FRAME_PENDING is set, we are receiving a packets in a burst */ + we_are_receiving_burst = packetbuf_attr(PACKETBUF_ATTR_PENDING); + if(we_are_receiving_burst) { + on(); + /* Set a timer to turn the radio off in case we do not receive a next packet */ + ctimer_set(&ct, INTER_PACKET_DEADLINE, recv_burst_off, NULL); + } else { + off(); + ctimer_stop(&ct); } -#endif /* WITH_PHASE_OPTIMIZATION */ /* Check for duplicate packet by comparing the sequence number of the incoming packet with the last few ones we saw. */ @@ -859,6 +893,7 @@ init(void) { radio_is_on = 0; PT_INIT(&pt); + rtimer_set(&rt, RTIMER_NOW() + CYCLE_TIME, 1, (void (*)(struct rtimer *, void *))powercycle, NULL); @@ -906,6 +941,7 @@ const struct rdc_driver contikimac_driver = { "ContikiMAC", init, qsend_packet, + qsend_list, input_packet, turn_on, turn_off, diff --git a/core/net/mac/csma.c b/core/net/mac/csma.c index b8fa3c9a2..3a1c61142 100644 --- a/core/net/mac/csma.c +++ b/core/net/mac/csma.c @@ -43,6 +43,7 @@ #include "net/queuebuf.h" #include "sys/ctimer.h" +#include "sys/clock.h" #include "lib/random.h" @@ -76,26 +77,51 @@ #error Change CSMA_CONF_MAX_MAC_TRANSMISSIONS in contiki-conf.h or in your Makefile. #endif /* CSMA_CONF_MAX_MAC_TRANSMISSIONS < 1 */ -struct queued_packet { - struct queued_packet *next; - struct queuebuf *buf; - /* struct ctimer retransmit_timer;*/ +/* Packet metadata */ +struct qbuf_metadata { mac_callback_t sent; void *cptr; - uint8_t transmissions, max_transmissions; - uint8_t collisions, deferrals; + uint8_t max_transmissions; }; -#define MAX_QUEUED_PACKETS 6 -MEMB(packet_memb, struct queued_packet, MAX_QUEUED_PACKETS); -LIST(queued_packet_list); +/* Every neighbor has its own packet queue */ +struct neighbor_queue { + struct neighbor_queue *next; + rimeaddr_t addr; + struct ctimer transmit_timer; + uint8_t transmissions; + uint8_t collisions, deferrals; + LIST_STRUCT(queued_packet_list); +}; -static struct ctimer transmit_timer; +/* The maximum number of co-existing neighbor queues */ +#ifdef CSMA_CONF_MAX_NEIGHBOR_QUEUES +#define CSMA_MAX_NEIGHBOR_QUEUES CSMA_CONF_MAX_NEIGHBOR_QUEUES +#else +#define CSMA_MAX_NEIGHBOR_QUEUES 2 +#endif /* CSMA_CONF_MAX_NEIGHBOR_QUEUES */ -static uint8_t rdc_is_transmitting; +#define MAX_QUEUED_PACKETS QUEUEBUF_NUM +MEMB(neighbor_memb, struct neighbor_queue, CSMA_MAX_NEIGHBOR_QUEUES); +MEMB(packet_memb, struct rdc_buf_list, MAX_QUEUED_PACKETS); +MEMB(metadata_memb, struct qbuf_metadata, MAX_QUEUED_PACKETS); +LIST(neighbor_list); static void packet_sent(void *ptr, int status, int num_transmissions); +static void transmit_packet_list(void *ptr); +/*---------------------------------------------------------------------------*/ +static struct +neighbor_queue *neighbor_queue_from_addr(const rimeaddr_t *addr) { + struct neighbor_queue *n = list_head(neighbor_list); + while(n != NULL) { + if(rimeaddr_cmp(&n->addr, addr)) { + return n; + } + n = list_item_next(n); + } + return NULL; +} /*---------------------------------------------------------------------------*/ static clock_time_t default_timebase(void) @@ -115,61 +141,44 @@ default_timebase(void) } /*---------------------------------------------------------------------------*/ static void -transmit_queued_packet(void *ptr) +transmit_packet_list(void *ptr) { - /* struct queued_packet *q = ptr;*/ - struct queued_packet *q; - - /* Don't transmit a packet if the RDC is still transmitting the - previous one. */ - if(rdc_is_transmitting) { - return; - } - - // printf("q %d\n", list_length(queued_packet_list)); - - q = list_head(queued_packet_list); - - if(q != NULL) { - queuebuf_to_packetbuf(q->buf); - PRINTF("csma: sending number %d %p, queue len %d\n", q->transmissions, q, - list_length(queued_packet_list)); - // printf("s %d\n", packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[0]); - rdc_is_transmitting = 1; - NETSTACK_RDC.send(packet_sent, q); - } -} -/*---------------------------------------------------------------------------*/ -static void -start_transmission_timer(void) -{ - PRINTF("csma: start_transmission_timer, queue len %d\n", - list_length(queued_packet_list)); - if(list_length(queued_packet_list) > 0) { - if(ctimer_expired(&transmit_timer)) { - ctimer_set(&transmit_timer, 0, - transmit_queued_packet, NULL); + struct neighbor_queue *n = ptr; + if(n) { + struct rdc_buf_list *q = list_head(n->queued_packet_list); + if(q != NULL) { + PRINTF("csma: preparing number %d %p, queue len %d\n", n->transmissions, q, + list_length(n->queued_packet_list)); + /* Send packets in the neighbor's list */ + NETSTACK_RDC.send_list(packet_sent, n, q); } } } /*---------------------------------------------------------------------------*/ static void -free_queued_packet(void) +free_first_packet(struct neighbor_queue *n) { - struct queued_packet *q; - - // printf("q %d\n", list_length(queued_packet_list)); - - q = list_head(queued_packet_list); - + struct rdc_buf_list *q = list_head(n->queued_packet_list); if(q != NULL) { + /* Remove first packet from list and deallocate */ queuebuf_free(q->buf); - list_remove(queued_packet_list, q); + list_pop(n->queued_packet_list); + memb_free(&metadata_memb, q->ptr); memb_free(&packet_memb, q); PRINTF("csma: free_queued_packet, queue length %d\n", - list_length(queued_packet_list)); - if(list_length(queued_packet_list) > 0) { - ctimer_set(&transmit_timer, default_timebase(), transmit_queued_packet, NULL); + list_length(n->queued_packet_list)); + if(list_head(n->queued_packet_list)) { + /* There is a next packet. We reset current tx information */ + n->transmissions = 0; + n->collisions = 0; + n->deferrals = 0; + /* Set a timer for next transmissions */ + ctimer_set(&n->transmit_timer, default_timebase(), transmit_packet_list, n); + } else { + /* This was the last packet in the queue, we free the neighbor */ + ctimer_stop(&n->transmit_timer); + list_remove(neighbor_list, n); + memb_free(&neighbor_memb, n); } } } @@ -177,32 +186,32 @@ free_queued_packet(void) static void packet_sent(void *ptr, int status, int num_transmissions) { - struct queued_packet *q = ptr; + struct neighbor_queue *n = ptr; + struct rdc_buf_list *q = list_head(n->queued_packet_list); + struct qbuf_metadata *metadata = (struct qbuf_metadata *)q->ptr; clock_time_t time = 0; mac_callback_t sent; void *cptr; int num_tx; int backoff_transmissions; - rdc_is_transmitting = 0; - switch(status) { case MAC_TX_OK: case MAC_TX_NOACK: - q->transmissions++; + n->transmissions++; break; case MAC_TX_COLLISION: - q->collisions++; + n->collisions++; break; case MAC_TX_DEFERRED: - q->deferrals++; + n->deferrals++; break; } - sent = q->sent; - cptr = q->cptr; - num_tx = q->transmissions; - + sent = metadata->sent; + cptr = metadata->cptr; + num_tx = n->transmissions; + if(status == MAC_TX_COLLISION || status == MAC_TX_NOACK) { @@ -211,13 +220,13 @@ packet_sent(void *ptr, int status, int num_transmissions) switch(status) { case MAC_TX_COLLISION: - PRINTF("csma: rexmit collision %d\n", q->transmissions); + PRINTF("csma: rexmit collision %d\n", n->transmissions); break; case MAC_TX_NOACK: - PRINTF("csma: rexmit noack %d\n", q->transmissions); + PRINTF("csma: rexmit noack %d\n", n->transmissions); break; default: - PRINTF("csma: rexmit err %d, %d\n", status, q->transmissions); + PRINTF("csma: rexmit err %d, %d\n", status, n->transmissions); } /* The retransmission time must be proportional to the channel @@ -227,7 +236,7 @@ packet_sent(void *ptr, int status, int num_transmissions) /* The retransmission time uses a linear backoff so that the interval between the transmissions increase with each retransmit. */ - backoff_transmissions = q->transmissions + 1; + backoff_transmissions = n->transmissions + 1; /* Clamp the number of backoffs so that we don't get a too long timeout here, since that will delay all packets in the @@ -235,32 +244,29 @@ packet_sent(void *ptr, int status, int num_transmissions) if(backoff_transmissions > 3) { backoff_transmissions = 3; } + time = time + (random_rand() % (backoff_transmissions * time)); - if(q->transmissions < q->max_transmissions) { + if(n->transmissions < metadata->max_transmissions) { PRINTF("csma: retransmitting with time %lu %p\n", time, q); - ctimer_set(&transmit_timer, time, - transmit_queued_packet, NULL); - + ctimer_set(&n->transmit_timer, time, + transmit_packet_list, n); /* This is needed to correctly attribute energy that we spent transmitting this packet. */ queuebuf_update_attr_from_packetbuf(q->buf); - } else { PRINTF("csma: drop with status %d after %d transmissions, %d collisions\n", - status, q->transmissions, q->collisions); - /* queuebuf_to_packetbuf(q->buf);*/ - free_queued_packet(); + status, n->transmissions, n->collisions); + free_first_packet(n); mac_call_sent_callback(sent, cptr, status, num_tx); } } else { if(status == MAC_TX_OK) { - PRINTF("csma: rexmit ok %d\n", q->transmissions); + PRINTF("csma: rexmit ok %d\n", n->transmissions); } else { - PRINTF("csma: rexmit failed %d: %d\n", q->transmissions, status); + PRINTF("csma: rexmit failed %d: %d\n", n->transmissions, status); } - /* queuebuf_to_packetbuf(q->buf);*/ - free_queued_packet(); + free_first_packet(n); mac_call_sent_callback(sent, cptr, status, num_tx); } } @@ -268,53 +274,89 @@ packet_sent(void *ptr, int status, int num_transmissions) static void send_packet(mac_callback_t sent, void *ptr) { - struct queued_packet *q; + struct rdc_buf_list *q; + struct neighbor_queue *n; static uint16_t seqno; - + packetbuf_set_attr(PACKETBUF_ATTR_MAC_SEQNO, seqno++); /* If the packet is a broadcast, do not allocate a queue entry. Instead, just send it out. */ if(!rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER), &rimeaddr_null)) { + const rimeaddr_t *addr = packetbuf_addr(PACKETBUF_ADDR_RECEIVER); - /* Remember packet for later. */ - q = memb_alloc(&packet_memb); - if(q != NULL) { - q->buf = queuebuf_new_from_packetbuf(); - if(q->buf != NULL) { - if(packetbuf_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS) == 0) { - /* Use default configuration for max transmissions */ - q->max_transmissions = CSMA_MAX_MAC_TRANSMISSIONS; - } else { - q->max_transmissions = - packetbuf_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS); - } - q->transmissions = 0; - q->collisions = 0; - q->deferrals = 0; - q->sent = sent; - q->cptr = ptr; - if(packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) == - PACKETBUF_ATTR_PACKET_TYPE_ACK) { - list_push(queued_packet_list, q); - } else { - list_add(queued_packet_list, q); - } - start_transmission_timer(); - return; + /* Look for the neighbor entry */ + n = neighbor_queue_from_addr(addr); + if(n == NULL) { + /* Allocate a new neighbor entry */ + n = memb_alloc(&neighbor_memb); + if(n != NULL) { + /* Init neighbor entry */ + rimeaddr_copy(&n->addr, addr); + n->transmissions = 0; + n->collisions = 0; + n->deferrals = 0; + /* Init packet list for this neighbor */ + LIST_STRUCT_INIT(n, queued_packet_list); + /* Add neighbor to the list */ + list_add(neighbor_list, n); } - memb_free(&packet_memb, q); - PRINTF("csma: could not allocate queuebuf, will drop if collision or noack\n"); } - PRINTF("csma: could not allocate memb, will drop if collision or noack\n"); + + if(n != NULL) { + /* Add packet to the neighbor's queue */ + q = memb_alloc(&packet_memb); + if(q != NULL) { + q->ptr = memb_alloc(&metadata_memb); + if(q->ptr != NULL) { + q->buf = queuebuf_new_from_packetbuf(); + if(q->buf != NULL) { + struct qbuf_metadata *metadata = (struct qbuf_metadata *)q->ptr; + /* Neighbor and packet successfully allocated */ + if(packetbuf_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS) == 0) { + /* Use default configuration for max transmissions */ + metadata->max_transmissions = CSMA_MAX_MAC_TRANSMISSIONS; + } else { + metadata->max_transmissions = + packetbuf_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS); + } + metadata->sent = sent; + metadata->cptr = ptr; + + if(packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) == + PACKETBUF_ATTR_PACKET_TYPE_ACK) { + list_push(n->queued_packet_list, q); + } else { + list_add(n->queued_packet_list, q); + } + + /* If q is the first packet in the neighbor's queue, send asap */ + if(list_head(n->queued_packet_list) == q) { + ctimer_set(&n->transmit_timer, 0, transmit_packet_list, n); + } + return; + } + memb_free(&metadata_memb, q->ptr); + PRINTF("csma: could not allocate queuebuf, dropping packet\n"); + } + memb_free(&packet_memb, q); + PRINTF("csma: could not allocate queuebuf, dropping packet\n"); + } + /* The packet allocation failed. Remove and free neighbor entry if empty. */ + if(list_length(n->queued_packet_list) == 0) { + list_remove(neighbor_list, n); + memb_free(&neighbor_memb, n); + } + PRINTF("csma: could not allocate packet, dropping packet\n"); + } else { + PRINTF("csma: could not allocate neighbor, dropping packet\n"); + } + mac_call_sent_callback(sent, ptr, MAC_TX_ERR, 1); } else { - PRINTF("csma: send broadcast (%d) or without retransmissions (%d)\n", - !rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER), - &rimeaddr_null), - packetbuf_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS)); + PRINTF("csma: send broadcast\n"); + NETSTACK_RDC.send(sent, ptr); } - NETSTACK_RDC.send(sent, ptr); } /*---------------------------------------------------------------------------*/ static void @@ -348,7 +390,8 @@ static void init(void) { memb_init(&packet_memb); - rdc_is_transmitting = 0; + memb_init(&metadata_memb); + memb_init(&neighbor_memb); } /*---------------------------------------------------------------------------*/ const struct mac_driver csma_driver = { diff --git a/core/net/mac/cxmac.c b/core/net/mac/cxmac.c index 84e945ead..61d942813 100644 --- a/core/net/mac/cxmac.c +++ b/core/net/mac/cxmac.c @@ -699,6 +699,15 @@ qsend_packet(mac_callback_t sent, void *ptr) mac_call_sent_callback(sent, ptr, ret, 1); } /*---------------------------------------------------------------------------*/ +void +qsend_list(mac_callback_t sent, void *ptr, struct rdc_buf_list *buf_list) +{ + if(buf_list != NULL) { + queuebuf_to_packetbuf(buf_list->buf); + qsend_packet(sent, ptr); + } +} +/*---------------------------------------------------------------------------*/ static void input_packet(void) { @@ -914,6 +923,7 @@ const struct rdc_driver cxmac_driver = "CX-MAC", cxmac_init, qsend_packet, + qsend_list, input_packet, turn_on, turn_off, diff --git a/core/net/mac/lpp.c b/core/net/mac/lpp.c index d61583597..11b73c370 100644 --- a/core/net/mac/lpp.c +++ b/core/net/mac/lpp.c @@ -726,6 +726,15 @@ send_packet(mac_callback_t sent, void *ptr) } } /*---------------------------------------------------------------------------*/ +void +send_list(mac_callback_t sent, void *ptr, struct rdc_buf_list *buf_list) +{ + if(buf_list != NULL) { + queuebuf_to_packetbuf(buf_list->buf); + send_packet(sent, ptr); + } +} +/*---------------------------------------------------------------------------*/ static int detect_ack(void) { @@ -1024,6 +1033,7 @@ const struct rdc_driver lpp_driver = { "LPP", init, send_packet, + send_list, input_packet, on, off, diff --git a/core/net/mac/nullrdc-noframer.c b/core/net/mac/nullrdc-noframer.c index ab5483af1..3a909dcb6 100644 --- a/core/net/mac/nullrdc-noframer.c +++ b/core/net/mac/nullrdc-noframer.c @@ -55,6 +55,15 @@ send_packet(mac_callback_t sent, void *ptr) mac_call_sent_callback(sent, ptr, ret, 1); } /*---------------------------------------------------------------------------*/ +void +send_list(mac_callback_t sent, void *ptr, struct rdc_buf_list *buf_list) +{ + if(buf_list != NULL) { + queuebuf_to_packetbuf(buf_list->buf); + send_packet(sent, ptr); + } +} +/*---------------------------------------------------------------------------*/ static void packet_input(void) { @@ -93,6 +102,7 @@ const struct rdc_driver nullrdc_noframer_driver = { "nullrdc-noframer", init, send_packet, + send_list, packet_input, on, off, diff --git a/core/net/mac/nullrdc.c b/core/net/mac/nullrdc.c index b57dcb729..c705e64f3 100644 --- a/core/net/mac/nullrdc.c +++ b/core/net/mac/nullrdc.c @@ -201,6 +201,15 @@ send_packet(mac_callback_t sent, void *ptr) mac_call_sent_callback(sent, ptr, ret, 1); } /*---------------------------------------------------------------------------*/ +void +send_list(mac_callback_t sent, void *ptr, struct rdc_buf_list *buf_list) +{ + if(buf_list != NULL) { + queuebuf_to_packetbuf(buf_list->buf); + send_packet(sent, ptr); + } +} +/*---------------------------------------------------------------------------*/ static void packet_input(void) { @@ -278,6 +287,7 @@ const struct rdc_driver nullrdc_driver = { "nullrdc", init, send_packet, + send_list, packet_input, on, off, diff --git a/core/net/mac/phase.c b/core/net/mac/phase.c index e1af5407e..513330181 100644 --- a/core/net/mac/phase.c +++ b/core/net/mac/phase.c @@ -52,6 +52,7 @@ struct phase_queueitem { mac_callback_t mac_callback; void *mac_callback_ptr; struct queuebuf *q; + struct rdc_buf_list *buf_list; }; #define PHASE_DEFER_THRESHOLD 1 @@ -150,17 +151,23 @@ send_packet(void *ptr) { struct phase_queueitem *p = ptr; - queuebuf_to_packetbuf(p->q); - queuebuf_free(p->q); + if(p->buf_list == NULL) { + queuebuf_to_packetbuf(p->q); + queuebuf_free(p->q); + NETSTACK_RDC.send(p->mac_callback, p->mac_callback_ptr); + } else { + NETSTACK_RDC.send_list(p->mac_callback, p->mac_callback_ptr, p->buf_list); + } + memb_free(&queued_packets_memb, p); - NETSTACK_RDC.send(p->mac_callback, p->mac_callback_ptr); } /*---------------------------------------------------------------------------*/ phase_status_t phase_wait(struct phase_list *list, const rimeaddr_t *neighbor, rtimer_clock_t cycle_time, rtimer_clock_t guard_time, - mac_callback_t mac_callback, void *mac_callback_ptr) + mac_callback_t mac_callback, void *mac_callback_ptr, + struct rdc_buf_list *buf_list, int extra_deferment) { struct phase *e; // const rimeaddr_t *neighbor = packetbuf_addr(PACKETBUF_ADDR_RECEIVER); @@ -169,8 +176,8 @@ phase_wait(struct phase_list *list, time for the next expected phase and setup a ctimer to switch on the radio just before the phase. */ e = find_neighbor(list, neighbor); - if(e != NULL) { - rtimer_clock_t wait, now, expected; + if((e != NULL) | extra_deferment) { + rtimer_clock_t wait, now, expected, sync; clock_time_t ctimewait; /* We expect phases to happen every CYCLE_TIME time @@ -188,31 +195,38 @@ phase_wait(struct phase_list *list, }*/ now = RTIMER_NOW(); - wait = (rtimer_clock_t)((e->time - now) & + + sync = (e == NULL) ? now : e->time; + wait = (rtimer_clock_t)((sync - now) & (cycle_time - 1)); if(wait < guard_time) { wait += cycle_time; } - + if(extra_deferment) { + wait += extra_deferment * cycle_time; + } + + ctimewait = (CLOCK_SECOND * (wait - guard_time)) / RTIMER_ARCH_SECOND; - - if(ctimewait > PHASE_DEFER_THRESHOLD) { + + if((ctimewait > PHASE_DEFER_THRESHOLD) | extra_deferment) { struct phase_queueitem *p; p = memb_alloc(&queued_packets_memb); if(p != NULL) { - p->q = queuebuf_new_from_packetbuf(); - if(p->q != NULL) { - p->mac_callback = mac_callback; - p->mac_callback_ptr = mac_callback_ptr; - ctimer_set(&p->timer, ctimewait, send_packet, p); - return PHASE_DEFERRED; - } else { - memb_free(&queued_packets_memb, p); + if(buf_list == NULL) { + p->q = queuebuf_new_from_packetbuf(); } + p->mac_callback = mac_callback; + p->mac_callback_ptr = mac_callback_ptr; + p->buf_list = buf_list; + ctimer_set(&p->timer, ctimewait, send_packet, p); + return PHASE_DEFERRED; + } else { + memb_free(&queued_packets_memb, p); } } - + expected = now + wait - guard_time; if(!RTIMER_CLOCK_LT(expected, now)) { /* Wait until the receiver is expected to be awake */ diff --git a/core/net/mac/phase.h b/core/net/mac/phase.h index ce11cebfc..1ce17a697 100644 --- a/core/net/mac/phase.h +++ b/core/net/mac/phase.h @@ -75,7 +75,8 @@ typedef enum { void phase_init(struct phase_list *list); phase_status_t phase_wait(struct phase_list *list, const rimeaddr_t *neighbor, rtimer_clock_t cycle_time, rtimer_clock_t wait_before, - mac_callback_t mac_callback, void *mac_callback_ptr); + mac_callback_t mac_callback, void *mac_callback_ptr, + struct rdc_buf_list *buf_list, int extra_deferment); void phase_update(const struct phase_list *list, const rimeaddr_t *neighbor, rtimer_clock_t time, int mac_status); diff --git a/core/net/mac/rdc.h b/core/net/mac/rdc.h index 5106d8888..333728c97 100644 --- a/core/net/mac/rdc.h +++ b/core/net/mac/rdc.h @@ -45,6 +45,13 @@ #include "contiki-conf.h" #include "net/mac/mac.h" +/* List of packets to be sent by RDC layer */ +struct rdc_buf_list { + struct rdc_buf_list *next; + struct queuebuf *buf; + void *ptr; +}; + /** * The structure of a RDC (radio duty cycling) driver in Contiki. */ @@ -57,6 +64,9 @@ struct rdc_driver { /** Send a packet from the Rime buffer */ void (* send)(mac_callback_t sent_callback, void *ptr); + /** Send a packet list */ + void (* send_list)(mac_callback_t sent_callback, void *ptr, struct rdc_buf_list *list); + /** Callback for getting notified of incoming packet. */ void (* input)(void); diff --git a/core/net/mac/sicslowmac.c b/core/net/mac/sicslowmac.c index 987401190..47c76ae04 100644 --- a/core/net/mac/sicslowmac.c +++ b/core/net/mac/sicslowmac.c @@ -175,6 +175,15 @@ send_packet(mac_callback_t sent, void *ptr) } } /*---------------------------------------------------------------------------*/ +void +send_list(mac_callback_t sent, void *ptr, struct rdc_buf_list *buf_list) +{ + if(buf_list != NULL) { + queuebuf_to_packetbuf(buf_list->buf); + send_packet(sent, ptr); + } +} +/*---------------------------------------------------------------------------*/ static void input_packet(void) { @@ -248,6 +257,7 @@ const struct rdc_driver sicslowmac_driver = { "sicslowmac", init, send_packet, + send_list, input_packet, on, off, diff --git a/core/net/mac/xmac.c b/core/net/mac/xmac.c index 547e93b8f..864089ecc 100644 --- a/core/net/mac/xmac.c +++ b/core/net/mac/xmac.c @@ -763,6 +763,15 @@ qsend_packet(mac_callback_t sent, void *ptr) mac_call_sent_callback(sent, ptr, ret, 1); } /*---------------------------------------------------------------------------*/ +void +qsend_list(mac_callback_t sent, void *ptr, struct rdc_buf_list *buf_list) +{ + if(buf_list != NULL) { + queuebuf_to_packetbuf(buf_list->buf); + qsend_packet(sent, ptr); + } +} +/*---------------------------------------------------------------------------*/ static void input_packet(void) { @@ -999,6 +1008,7 @@ const struct rdc_driver xmac_driver = "X-MAC", init, qsend_packet, + qsend_list, input_packet, turn_on, turn_off, diff --git a/core/net/queuebuf.c b/core/net/queuebuf.c index 3a6bdbf10..e5b748cb3 100644 --- a/core/net/queuebuf.c +++ b/core/net/queuebuf.c @@ -44,6 +44,9 @@ */ #include "contiki-net.h" +#if WITH_SWAP +#include "cfs/cfs.h" +#endif #include /* for memcpy() */ @@ -53,6 +56,8 @@ #define QUEUEBUF_REF_NUM 2 #endif +/* Structure pointing to a buffer either stored + in RAM or swapped in CFS */ struct queuebuf { #if QUEUEBUF_DEBUG struct queuebuf *next; @@ -60,6 +65,19 @@ struct queuebuf { int line; clock_time_t time; #endif /* QUEUEBUF_DEBUG */ +#if WITH_SWAP + enum {IN_RAM, IN_CFS} location; + union { +#endif + struct queuebuf_data *ram_ptr; +#if WITH_SWAP + int swap_id; + }; +#endif +}; + +/* The actual queuebuf data */ +struct queuebuf_data { uint16_t len; uint8_t data[PACKETBUF_SIZE]; struct packetbuf_attr attrs[PACKETBUF_NUM_ATTRS]; @@ -75,6 +93,37 @@ struct queuebuf_ref { MEMB(bufmem, struct queuebuf, QUEUEBUF_NUM); MEMB(refbufmem, struct queuebuf_ref, QUEUEBUF_REF_NUM); +MEMB(buframmem, struct queuebuf_data, QUEUEBUFRAM_NUM); + +#if WITH_SWAP + +/* Swapping allows to store up to QUEUEBUF_NUM - QUEUEBUFRAM_NUM + queuebufs in CFS. The swap is made of several large CFS files. + Every buffer stored in CFS has a swap id, referring to a specific + offset in one of these files. */ +#define NQBUF_FILES 4 +#define NQBUF_PER_FILE 256 +#define QBUF_FILE_SIZE (NQBUF_PER_FILE*sizeof(struct queuebuf_data)) +#define NQBUF_ID (NQBUF_PER_FILE * NQBUF_FILES) + +struct qbuf_file { + int fd; + int usage; + int renewable; +}; + +/* A statically allocated queuebuf used as a cache for swapped qbufs */ +static struct queuebuf_data tmpdata; +/* A pointer to the qbuf associated to the data in tmpdata */ +static struct queuebuf *tmpdata_qbuf = NULL; +/* The swap id counter */ +static int next_swap_id = 0; +/* The swap files */ +static struct qbuf_file qbuf_files[NQBUF_FILES]; +/* The timer used to renew files during inactivity periods */ +static struct ctimer renew_timer; + +#endif #if QUEUEBUF_DEBUG #include "lib/list.h" @@ -99,10 +148,161 @@ LIST(queuebuf_list); uint8_t queuebuf_len, queuebuf_ref_len, queuebuf_max_len; #endif /* QUEUEBUF_STATS */ +static void queuebuf_remove_from_file(int swap_id); + +#if WITH_SWAP +/*---------------------------------------------------------------------------*/ +static void +qbuf_renew_file(int file) +{ + int ret; + char name[2]; + name[0] = 'a' + file; + name[1] = '\0'; + if(qbuf_files[file].renewable == 1) { + PRINTF("qbuf_renew_file: removing file %d\n", file); + cfs_remove(name); + } + ret = cfs_open(name, CFS_READ | CFS_WRITE); + if(ret == -1) { + PRINTF("qbuf_renew_file: cfs open error\n"); + } + qbuf_files[file].fd = ret; + qbuf_files[file].usage = 0; + qbuf_files[file].renewable = 0; +} +/*---------------------------------------------------------------------------*/ +/* Renews every file with renewable flag set */ +static void +qbuf_renew_all(void *unused) +{ + int i; + for(i=0; iswap_id == swap_id) { + tmpdata_qbuf->swap_id = -1; + } + } +} +/*---------------------------------------------------------------------------*/ +static int +get_new_swap_id(void) +{ + int fileid; + int swap_id = next_swap_id; + fileid = swap_id / NQBUF_PER_FILE; + if(swap_id % NQBUF_PER_FILE == 0) { /* This is the first id in the file */ + if(qbuf_files[fileid].renewable) { + qbuf_renew_file(fileid); + } + if(qbuf_files[fileid].usage>0) { + return -1; + } + } + qbuf_files[fileid].usage++; + next_swap_id = (next_swap_id+1) % NQBUF_ID; + return swap_id; +} +/*---------------------------------------------------------------------------*/ +/* Flush tmpdata to CFS */ +static int +queuebuf_flush_tmpdata(void) +{ + int fileid, fd, ret; + cfs_offset_t offset; + if(tmpdata_qbuf) { + queuebuf_remove_from_file(tmpdata_qbuf->swap_id); + tmpdata_qbuf->swap_id = get_new_swap_id(); + if(tmpdata_qbuf->swap_id == -1) { + return -1; + } + fileid = tmpdata_qbuf->swap_id / NQBUF_PER_FILE; + offset = (tmpdata_qbuf->swap_id % NQBUF_PER_FILE) * sizeof(struct queuebuf_data); + fd = qbuf_files[fileid].fd; + ret = cfs_seek(fd, offset, CFS_SEEK_SET); + if(ret == -1) { + PRINTF("queuebuf_flush_tmpdata: cfs seek error\n"); + return -1; + } + ret = cfs_write(fd, &tmpdata, sizeof(struct queuebuf_data)); + if(ret == -1) { + PRINTF("queuebuf_flush_tmpdata: cfs write error\n"); + return -1; + } + } + return 0; +} +/*---------------------------------------------------------------------------*/ +/* If the queuebuf is in CFS, load it to tmpdata */ +static struct queuebuf_data * +queuebuf_load_to_ram(struct queuebuf *b) +{ + int fileid, fd, ret; + cfs_offset_t offset; + if(b->location == IN_RAM) { /* the qbuf is loacted in RAM */ + return b->ram_ptr; + } else { /* the qbuf is located in CFS */ + if(tmpdata_qbuf && tmpdata_qbuf->swap_id == b->swap_id) { /* the qbuf is already in tmpdata */ + return &tmpdata; + } else { /* the qbuf needs to be loaded from CFS */ + tmpdata_qbuf = b; + /* read the qbuf from CFS */ + fileid = b->swap_id / NQBUF_PER_FILE; + offset = (b->swap_id % NQBUF_PER_FILE) * sizeof(struct queuebuf_data); + fd = qbuf_files[fileid].fd; + ret = cfs_seek(fd, offset, CFS_SEEK_SET); + if(ret == -1) { + PRINTF("queuebuf_load_to_ram: cfs seek error\n"); + } + ret = cfs_read(fd, &tmpdata, sizeof(struct queuebuf_data)); + if(ret == -1) { + PRINTF("queuebuf_load_to_ram: cfs read error\n"); + } + return &tmpdata; + } + } +} +#else /* WITH_SWAP */ +/*---------------------------------------------------------------------------*/ +static struct queuebuf_data * +queuebuf_load_to_ram(struct queuebuf *b) +{ + return b->ram_ptr; +} +#endif /* WITH_SWAP */ /*---------------------------------------------------------------------------*/ void queuebuf_init(void) { +#if WITH_SWAP + int i; + for(i=0; iline = line; buf->time = clock_time(); #endif /* QUEUEBUF_DEBUG */ + buf->ram_ptr = memb_alloc(&buframmem); +#if WITH_SWAP + /* If the allocation failed, store the qbuf in swap files */ + if(buf->ram_ptr != NULL) { + buf->location = IN_RAM; + buframptr = buf->ram_ptr; + } else { + buf->location = IN_CFS; + buf->swap_id = -1; + tmpdata_qbuf = buf; + buframptr = &tmpdata; + } +#else + if(buf->ram_ptr == NULL) { + PRINTF("queuebuf_new_from_packetbuf: could not queuebuf data\n"); + return NULL; + } + buframptr = buf->ram_ptr; +#endif + + buframptr->len = packetbuf_copyto(buframptr->data); + packetbuf_attr_copyto(buframptr->attrs, buframptr->addrs); + +#if WITH_SWAP + if(buf->location == IN_CFS) { + if(queuebuf_flush_tmpdata() == -1) { + /* We were unable to write the data in the swap */ + memb_free(&bufmem, buf); + return NULL; + } + } +#endif + #if QUEUEBUF_STATS ++queuebuf_len; PRINTF("queuebuf len %d\n", queuebuf_len); printf("#A q=%d\n", queuebuf_len); if(queuebuf_len == queuebuf_max_len + 1) { - memb_free(&bufmem, buf); - queuebuf_len--; - return NULL; + memb_free(&bufmem, buf); + queuebuf_len--; + return NULL; } #endif /* QUEUEBUF_STATS */ - buf->len = packetbuf_copyto(buf->data); - packetbuf_attr_copyto(buf->attrs, buf->addrs); + } else { PRINTF("queuebuf_new_from_packetbuf: could not allocate a queuebuf\n"); } @@ -165,13 +398,28 @@ queuebuf_new_from_packetbuf(void) void queuebuf_update_attr_from_packetbuf(struct queuebuf *buf) { - packetbuf_attr_copyto(buf->attrs, buf->addrs); + struct queuebuf_data *buframptr = queuebuf_load_to_ram(buf); + packetbuf_attr_copyto(buframptr->attrs, buframptr->addrs); +#if WITH_SWAP + if(buf->location == IN_CFS) { + queuebuf_flush_tmpdata(); + } +#endif } /*---------------------------------------------------------------------------*/ void queuebuf_free(struct queuebuf *buf) { if(memb_inmemb(&bufmem, buf)) { +#if WITH_SWAP + if(buf->location == IN_RAM) { + memb_free(&buframmem, buf->ram_ptr); + } else { + queuebuf_remove_from_file(buf->swap_id); + } +#else + memb_free(&buframmem, buf->ram_ptr); +#endif memb_free(&bufmem, buf); #if QUEUEBUF_STATS --queuebuf_len; @@ -192,10 +440,10 @@ void queuebuf_to_packetbuf(struct queuebuf *b) { struct queuebuf_ref *r; - if(memb_inmemb(&bufmem, b)) { - packetbuf_copyfrom(b->data, b->len); - packetbuf_attr_copyfrom(b->attrs, b->addrs); + struct queuebuf_data *buframptr = queuebuf_load_to_ram(b); + packetbuf_copyfrom(buframptr->data, buframptr->len); + packetbuf_attr_copyfrom(buframptr->attrs, buframptr->addrs); } else if(memb_inmemb(&refbufmem, b)) { r = (struct queuebuf_ref *)b; packetbuf_clear(); @@ -209,9 +457,10 @@ void * queuebuf_dataptr(struct queuebuf *b) { struct queuebuf_ref *r; - + if(memb_inmemb(&bufmem, b)) { - return b->data; + struct queuebuf_data *buframptr = queuebuf_load_to_ram(b); + return buframptr->data; } else if(memb_inmemb(&refbufmem, b)) { r = (struct queuebuf_ref *)b; return r->ref; @@ -222,19 +471,22 @@ queuebuf_dataptr(struct queuebuf *b) int queuebuf_datalen(struct queuebuf *b) { - return b->len; + struct queuebuf_data *buframptr = queuebuf_load_to_ram(b); + return buframptr->len; } /*---------------------------------------------------------------------------*/ rimeaddr_t * queuebuf_addr(struct queuebuf *b, uint8_t type) { - return &b->addrs[type - PACKETBUF_ADDR_FIRST].addr; + struct queuebuf_data *buframptr = queuebuf_load_to_ram(b); + return &buframptr->addrs[type - PACKETBUF_ADDR_FIRST].addr; } /*---------------------------------------------------------------------------*/ packetbuf_attr_t queuebuf_attr(struct queuebuf *b, uint8_t type) { - return b->attrs[type].val; + struct queuebuf_data *buframptr = queuebuf_load_to_ram(b); + return buframptr->attrs[type].val; } /*---------------------------------------------------------------------------*/ void diff --git a/core/net/queuebuf.h b/core/net/queuebuf.h index f9d434100..bdb44e6e7 100644 --- a/core/net/queuebuf.h +++ b/core/net/queuebuf.h @@ -56,12 +56,30 @@ #include "net/packetbuf.h" +/* QUEUEBUF_NUM is the total number of queuebuf */ #ifdef QUEUEBUF_CONF_NUM #define QUEUEBUF_NUM QUEUEBUF_CONF_NUM #else #define QUEUEBUF_NUM 8 #endif +/* QUEUEBUFRAM_NUM is the number of queuebufs stored in RAM. + If QUEUEBUFRAM_CONF_NUM is set lower than QUEUEBUF_NUM, + swapping is enabled and queuebufs are stored either in RAM of CFS. + If QUEUEBUFRAM_CONF_NUM is unset or >= to QUEUEBUF_NUM, all + queuebufs are in RAM and swapping is disabled. */ +#ifdef QUEUEBUFRAM_CONF_NUM + #if QUEUEBUFRAM_CONF_NUM>QUEUEBUF_NUM + #error "QUEUEBUFRAM_CONF_NUM cannot be greater than QUEUEBUF_NUM" + #else + #define QUEUEBUFRAM_NUM QUEUEBUFRAM_CONF_NUM + #define WITH_SWAP (QUEUEBUFRAM_NUM < QUEUEBUF_NUM) + #endif +#else /* QUEUEBUFRAM_CONF_NUM */ + #define QUEUEBUFRAM_NUM QUEUEBUF_NUM + #define WITH_SWAP 0 +#endif /* QUEUEBUFRAM_CONF_NUM */ + #ifdef QUEUEBUF_CONF_DEBUG #define QUEUEBUF_DEBUG QUEUEBUF_CONF_DEBUG #else /* QUEUEBUF_CONF_DEBUG */ diff --git a/examples/udp-stream/Makefile b/examples/udp-stream/Makefile new file mode 100644 index 000000000..37185ac8c --- /dev/null +++ b/examples/udp-stream/Makefile @@ -0,0 +1,14 @@ +all: udp-stream +APPS = servreg-hack +CONTIKI = ../.. + +ifndef TARGET +TARGET = sky +endif + +WITH_UIP6 = 1 +UIP_CONF_IPV6 = 1 +CFLAGS += -DPROJECT_CONF_H=\"project-conf.h\" +SMALL = 1 + +include $(CONTIKI)/Makefile.include diff --git a/examples/udp-stream/README b/examples/udp-stream/README new file mode 100644 index 000000000..e5622b2eb --- /dev/null +++ b/examples/udp-stream/README @@ -0,0 +1,21 @@ +This is an example of bursts support in CSMA/ContikiMAC, +together with storage of long packet queue in CFS. This +is useful to support large fragmented UDP datagrams or +continuous data streaming. The current implementation +is a simplified version of the techniques presented in +"Lossy Links, Low Power, High Throughput", published in +the proceeding of ACM SenSys 2011. + +In this example, node with ID==5 sends bursts of UDP +datagrams to node with ID==1, the root of the RPL dodag. + +Testing in cooja: + $make TARGET=cooja udp-stream.csc + +Testing on Tmote sky: +1) set node IDs to different motes so node 5 sends to +node 1 (using examples/sky-shell) +2) compile and program: + $make TARGET=sky udp-stream.upload +3) monitor motes with: + $make login MOTE=xxx diff --git a/examples/udp-stream/project-conf.h b/examples/udp-stream/project-conf.h new file mode 100644 index 000000000..d90849f8c --- /dev/null +++ b/examples/udp-stream/project-conf.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2010, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: project-conf.h,v 1.1 2010/10/28 13:11:08 simonduq Exp $ + */ + +#ifndef __PROJECT_H__ +#define __PROJECT_H__ + +/* Free some code and RAM space */ +#define UIP_CONF_TCP 0 +#undef UIP_CONF_DS6_NBR_NBU +#define UIP_CONF_DS6_NBR_NBU 12 +#undef UIP_CONF_DS6_ROUTE_NBU +#define UIP_CONF_DS6_ROUTE_NBU 12 + +/* The total number of queuebuf */ +#undef QUEUEBUF_CONF_NUM +#define QUEUEBUF_CONF_NUM 140 +/* The number of queuebuf actually stored in RAM. If + not set or equal to the total number of queuebuf, + swapping is disabled, and CFS not linked. */ +#define QUEUEBUFRAM_CONF_NUM 2 + +/* Set a large (1 sector) default size for coffee files. */ +#define COFFEE_CONF_DYN_SIZE (COFFEE_SECTOR_SIZE - COFFEE_PAGE_SIZE + 1) + +#endif /* __PROJECT_H__ */ diff --git a/examples/udp-stream/udp-stream.c b/examples/udp-stream/udp-stream.c new file mode 100644 index 000000000..a00d552b3 --- /dev/null +++ b/examples/udp-stream/udp-stream.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2006, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + * $Id: hello-world.c,v 1.1 2006/10/02 21:46:46 simonduq Exp $ + */ + +/** + * \file + * An example sending a UDP stream + * \author + * Simon Duquennoy + */ + +#include "contiki.h" +#include "contiki-net.h" +#include "uip.h" +#include "net/rpl/rpl.h" +#include "node-id.h" +#include "servreg-hack.h" + +#include "cfs/cfs.h" +#include "cfs/cfs-coffee.h" + +#define SINK_ID 1 +#define SENDER_ID 5 +#define DATASIZE 73 +#define STREAMLEN QUEUEBUF_CONF_NUM +#define UDP_PORT 9876 +#define SERVICE_ID 190 + +struct msg { + uint8_t streamno; + uint8_t seqno; + uint8_t buf[DATASIZE]; +}; + +static struct simple_udp_connection unicast_connection; + +/*---------------------------------------------------------------------------*/ +PROCESS(udpstream_process, "UDP Stream Process"); +AUTOSTART_PROCESSES(&udpstream_process); + +/*---------------------------------------------------------------------------*/ +static uip_ipaddr_t * +set_global_address(void) +{ + static uip_ipaddr_t ipaddr; + int i; + uint8_t state; + + uip_ip6addr(&ipaddr, 0xaaaa, 0, 0, 0, 0, 0, 0, 0); + uip_ds6_set_addr_iid(&ipaddr, &uip_lladdr); + uip_ds6_addr_add(&ipaddr, 0, ADDR_AUTOCONF); + + printf("IPv6 addresses: "); + for(i = 0; i < UIP_DS6_ADDR_NB; i++) { + state = uip_ds6_if.addr_list[i].state; + if(uip_ds6_if.addr_list[i].isused && + (state == ADDR_TENTATIVE || state == ADDR_PREFERRED)) { + uip_debug_ipaddr_print(&uip_ds6_if.addr_list[i].ipaddr); + printf("\n"); + } + } + + return &ipaddr; +} +/*---------------------------------------------------------------------------*/ +static void +create_rpl_dag(uip_ipaddr_t *ipaddr) +{ + struct uip_ds6_addr *root_if; + root_if = uip_ds6_addr_lookup(ipaddr); + if(root_if != NULL) { + rpl_dag_t *dag; + uip_ipaddr_t prefix; + + rpl_set_root(ipaddr); + dag = rpl_get_dag(RPL_ANY_INSTANCE); + uip_ip6addr(&prefix, 0xaaaa, 0, 0, 0, 0, 0, 0, 0); + rpl_set_prefix(dag, &prefix, 64); + printf("created a new RPL dag\n"); + } else { + printf("failed to create a new RPL DAG\n"); + } +} +/*---------------------------------------------------------------------------*/ +static void +receiver(struct simple_udp_connection *c, + const uip_ipaddr_t *sender_addr, + uint16_t sender_port, + const uip_ipaddr_t *receiver_addr, + uint16_t receiver_port, + const uint8_t *data, + uint16_t datalen) +{ + static int cpt; + cpt++; + if(cpt%128==0) { + printf("Received %d datagrams\n", cpt); + } +} +/*---------------------------------------------------------------------------*/ +static void +send_stream(uip_ipaddr_t *addr, uint16_t streamno) +{ + int i; + int seqno = 0; + struct msg msg; + + memset(&msg, 0xaa, sizeof(msg)); + msg.streamno = streamno; + for(i=0; i + + [CONTIKI_DIR]/tools/cooja/apps/mspsim + [CONTIKI_DIR]/tools/cooja/apps/serial_socket + [CONTIKI_DIR]/tools/cooja/apps/mrm + + UDP Stream Example + 0 + 123456 + 1000000 + + se.sics.cooja.radiomediums.UDGM + 50.0 + 100.0 + 1.0 + 1.0 + + + 40000 + + + se.sics.cooja.mspmote.SkyMoteType + mote + mote + [CONTIKI_DIR]/examples/udp-stream/udp-stream.c + make udp-stream.sky TARGET=sky + [CONTIKI_DIR]/examples/udp-stream/udp-stream.sky + se.sics.cooja.interfaces.Position + se.sics.cooja.interfaces.RimeAddress + se.sics.cooja.interfaces.IPAddress + se.sics.cooja.interfaces.Mote2MoteRelations + se.sics.cooja.interfaces.MoteAttributes + se.sics.cooja.mspmote.interfaces.MspClock + se.sics.cooja.mspmote.interfaces.MspMoteID + se.sics.cooja.mspmote.interfaces.SkyButton + se.sics.cooja.mspmote.interfaces.SkyFlash + se.sics.cooja.mspmote.interfaces.SkyCoffeeFilesystem + se.sics.cooja.mspmote.interfaces.SkyByteRadio + se.sics.cooja.mspmote.interfaces.MspSerial + se.sics.cooja.mspmote.interfaces.SkyLED + se.sics.cooja.mspmote.interfaces.MspDebugOutput + se.sics.cooja.mspmote.interfaces.SkyTemperature + + + + + se.sics.cooja.interfaces.Position + 33.260163187353555 + 30.643217359962595 + 0.0 + + + se.sics.cooja.mspmote.interfaces.MspMoteID + 1 + + mote + + + + + se.sics.cooja.interfaces.Position + 70.87477363740156 + 31.656474063135494 + 0.0 + + + se.sics.cooja.mspmote.interfaces.MspMoteID + 2 + + mote + + + + + se.sics.cooja.interfaces.Position + 112.33402646502834 + 47.18506616257089 + 0.0 + + + se.sics.cooja.mspmote.interfaces.MspMoteID + 3 + + mote + + + + + se.sics.cooja.interfaces.Position + 132.53950850661624 + 78.99637996219282 + 0.0 + + + se.sics.cooja.mspmote.interfaces.MspMoteID + 4 + + mote + + + + + se.sics.cooja.interfaces.Position + 136.72844990548202 + 114.94735349716447 + 0.0 + + + se.sics.cooja.mspmote.interfaces.MspMoteID + 5 + + mote + + + + se.sics.cooja.plugins.SimControl + 259 + 4 + 178 + 0 + 0 + + + se.sics.cooja.plugins.Visualizer + + se.sics.cooja.plugins.skins.IDVisualizerSkin + se.sics.cooja.plugins.skins.UDGMVisualizerSkin + se.sics.cooja.plugins.skins.MoteTypeVisualizerSkin + se.sics.cooja.plugins.skins.AttributeVisualizerSkin + 1.2465387687077096 0.0 0.0 1.2465387687077096 22.99764760264848 12.704392736072247 + + 257 + 3 + 297 + 0 + 180 + + + se.sics.cooja.plugins.LogListener + + + + + 568 + 2 + 663 + 257 + -2 + + + se.sics.cooja.plugins.RadioLogger + + 449 + + + 605 + 1 + 661 + 824 + 0 + + + se.sics.cooja.plugins.TimeLine + + 0 + 1 + 2 + 3 + 4 + + + 125 + 100000.0 + + 1429 + 0 + 141 + 1 + 661 + + + diff --git a/platform/sky/cfs-coffee-arch.h b/platform/sky/cfs-coffee-arch.h index 6f30068fa..08373c5e9 100644 --- a/platform/sky/cfs-coffee-arch.h +++ b/platform/sky/cfs-coffee-arch.h @@ -52,7 +52,11 @@ #define COFFEE_MAX_OPEN_FILES 6 #define COFFEE_FD_SET_SIZE 8 #define COFFEE_LOG_TABLE_LIMIT 256 -#define COFFEE_DYN_SIZE 4*1024 +#ifdef COFFEE_CONF_DYN_SIZE +#define COFFEE_DYN_SIZE COFFEE_CONF_DYN_SIZE +#else +#define COFFEE_DYN_SIZE 4*1024 +#endif #define COFFEE_LOG_SIZE 1024 #define COFFEE_IO_SEMANTICS 1