diff --git a/core/net/mac/xmac.c b/core/net/mac/xmac.c index 259d92485..5e626b4b8 100644 --- a/core/net/mac/xmac.c +++ b/core/net/mac/xmac.c @@ -28,7 +28,7 @@ * * This file is part of the Contiki operating system. * - * $Id: xmac.c,v 1.43 2009/11/27 14:27:50 fros4943 Exp $ + * $Id: xmac.c,v 1.44 2009/12/06 13:16:59 adamdunkels Exp $ */ /** @@ -60,13 +60,24 @@ #include -#define WITH_CHANNEL_CHECK 0 /* Seems to work badly when enabled */ +#ifndef WITH_TIMESYNCH #define WITH_TIMESYNCH 0 -#define WITH_QUEUE 0 +#endif +#ifndef WITH_ACK_OPTIMIZATION #define WITH_ACK_OPTIMIZATION 1 +#endif +#ifndef WITH_RANDOM_WAIT_BEFORE_SEND #define WITH_RANDOM_WAIT_BEFORE_SEND 0 +#endif +#ifndef WITH_ENCOUNTER_OPTIMIZATION #define WITH_ENCOUNTER_OPTIMIZATION 1 +#endif +#ifndef WITH_STREAMING #define WITH_STREAMING 1 +#endif +#ifndef WITH_STROBE_BROADCAST +#define WITH_STROBE_BROADCAST 0 +#endif struct announcement_data { uint16_t id; @@ -114,6 +125,8 @@ struct xmac_hdr { #define DEFAULT_PERIOD (DEFAULT_OFF_TIME + DEFAULT_ON_TIME) +#define WAIT_TIME_BEFORE_STROBE_ACK RTIMER_ARCH_SECOND / 1000 + /* On some platforms, we may end up with a DEFAULT_PERIOD that is 0 which will make compilation fail due to a modulo operation in the code. To ensure that DEFAULT_PERIOD is greater than zero, we use @@ -135,7 +148,7 @@ struct xmac_hdr { struct xmac_config xmac_config = { DEFAULT_ON_TIME, DEFAULT_OFF_TIME, - 20 * DEFAULT_ON_TIME + DEFAULT_OFF_TIME, + 4 * DEFAULT_ON_TIME + DEFAULT_OFF_TIME, DEFAULT_STROBE_WAIT_TIME }; @@ -247,6 +260,38 @@ off(void) } } /*---------------------------------------------------------------------------*/ +static char powercycle(struct rtimer *t, void *ptr); +static void +schedule_powercycle(struct rtimer *t, rtimer_clock_t time) +{ + int r; + if(xmac_is_on) { + r = rtimer_set(t, RTIMER_TIME(t) + time, 1, + (void (*)(struct rtimer *, void *))powercycle, NULL); + if(r) { + PRINTF("schedule_powercycle: could not set rtimer\n"); + } + } +} +static void +powercycle_turn_radio_off(void) +{ + if(we_are_sending == 0 && + waiting_for_packet == 0) { + off(); + } +#if XMAC_CONF_COMPOWER + compower_accumulate(&compower_idle_activity); +#endif /* XMAC_CONF_COMPOWER */ +} +static void +powercycle_turn_radio_on(void) +{ + if(we_are_sending == 0 && + waiting_for_packet == 0) { + on(); + } +} static char powercycle(struct rtimer *t, void *ptr) { @@ -272,74 +317,26 @@ powercycle(struct rtimer *t, void *ptr) someone_is_sending--; } + /* If there were a strobe in the air, turn radio on */ + powercycle_turn_radio_on(); + schedule_powercycle(t, xmac_config.on_time); + PT_YIELD(&pt); + if(xmac_config.off_time > 0) { - if(we_are_sending == 0) { - if(waiting_for_packet == 0) { - off(); -#if XMAC_CONF_COMPOWER - compower_accumulate(&compower_idle_activity); -#endif /* XMAC_CONF_COMPOWER */ - } else { - waiting_for_packet++; - if(waiting_for_packet > 2) { - /* We should not be awake for more than two consecutive - power cycles without having heard a packet, so we turn off - the radio. */ - waiting_for_packet = 0; - if(we_are_sending == 0) { - off(); - } -#if XMAC_CONF_COMPOWER - compower_accumulate(&compower_idle_activity); -#endif /* XMAC_CONF_COMPOWER */ - } + powercycle_turn_radio_off(); + if(waiting_for_packet != 0) { + waiting_for_packet++; + if(waiting_for_packet > 2) { + /* We should not be awake for more than two consecutive + power cycles without having heard a packet, so we turn off + the radio. */ + waiting_for_packet = 0; + powercycle_turn_radio_off(); } } - -#if WITH_TIMESYNCH -#define NUM_SLOTS 16 - should_be = ((timesynch_rtimer_to_time(RTIMER_TIME(t)) + - xmac_config.off_time) & - ~(xmac_config.off_time + xmac_config.on_time - 1)) + - (rimeaddr_node_addr.u8[0] % NUM_SLOTS * - ((xmac_config.off_time + xmac_config.on_time) / NUM_SLOTS)); - - should_be = timesynch_time_to_rtimer(should_be); - - if(should_be - RTIMER_TIME(t) > xmac_config.off_time) { - adjust = xmac_config.off_time / 2; - } else { - adjust = should_be - RTIMER_TIME(t); - } - if(xmac_is_on) { - r = rtimer_set(t, RTIMER_TIME(t) + adjust, 1, - (void (*)(struct rtimer *, void *))powercycle, ptr); - } -#else /* WITH_TIMESYNCH */ - if(xmac_is_on) { - r = rtimer_set(t, RTIMER_TIME(t) + xmac_config.off_time, 1, - (void (*)(struct rtimer *, void *))powercycle, ptr); - } -#endif /* WITH_TIMESYNCH */ - if(r) { - PRINTF("xmac: 1 could not set rtimer %d\n", r); - } + schedule_powercycle(t, xmac_config.off_time); PT_YIELD(&pt); } - - if(we_are_sending == 0 && - waiting_for_packet == 0) { - on(); - } - if(xmac_is_on) { - r = rtimer_set(t, RTIMER_TIME(t) + xmac_config.on_time, 1, - (void (*)(struct rtimer *, void *))powercycle, ptr); - } - if(r) { - PRINTF("xmac: 3 could not set rtimer %d\n", r); - } - - PT_YIELD(&pt); } PT_END(&pt); @@ -460,6 +457,7 @@ send_packet(void) struct encounter *e; struct queuebuf *packet; int is_already_streaming = 0; + uint8_t collisions; #if WITH_RANDOM_WAIT_BEFORE_SEND { @@ -498,7 +496,7 @@ send_packet(void) if(len == 0 || strobe_len > sizeof(strobe)) { /* Failed to send */ PRINTF("xmac: send failed, too large header\n"); - return 0; + return MAC_TX_ERR_FATAL; } memcpy(strobe, packetbuf_hdrptr(), len); strobe[len] = DISPATCH; /* dispatch */ @@ -510,30 +508,9 @@ send_packet(void) /* No buffer available */ PRINTF("xmac: send failed, no queue buffer available (of %u)\n", QUEUEBUF_CONF_NUM); - return 0; + return MAC_TX_ERR; } - -#if WITH_CHANNEL_CHECK - /* Check if there are other strobes in the air. */ - if(!someone_is_sending) { - waiting_for_packet = 1; - on(); - t0 = RTIMER_NOW(); - while(RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + xmac_config.strobe_wait_time * 2)) { - len = radio->read(&strobe.hdr, sizeof(strobe.hdr)); - if(len > 0) { - someone_is_sending = 1; - } - } - waiting_for_packet = 0; - } - - while(someone_is_sending); - -#endif /* WITH_CHANNEL_CHECK */ - - /* By setting we_are_sending to one, we ensure that the rtimer powercycle interrupt do not interfere with us sending the packet. */ we_are_sending = 1; @@ -580,7 +557,7 @@ send_packet(void) now = RTIMER_NOW(); wait = ((rtimer_clock_t)(e->time - now)) % (DEFAULT_PERIOD); - expected = now + wait - DEFAULT_ON_TIME * 2; + expected = now + wait - 2 * DEFAULT_ON_TIME; #if WITH_ACK_OPTIMIZATION /* Wait until the receiver is expected to be awake */ @@ -607,72 +584,77 @@ send_packet(void) /* Send a train of strobes until the receiver answers with an ACK. */ /* Turn on the radio to listen for the strobe ACK. */ - if(!is_broadcast) { - on(); - } - + on(); + collisions = 0; if(!is_already_streaming) { - - watchdog_stop(); - got_strobe_ack = 0; - for(strobes = 0; - got_strobe_ack == 0 && - RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + xmac_config.strobe_time); - strobes++) { - + watchdog_stop(); + got_strobe_ack = 0; t = RTIMER_NOW(); + for(strobes = 0, collisions = 0; + got_strobe_ack == 0 && collisions == 0 && + RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + xmac_config.strobe_time); + strobes++) { - /* Send the strobe packet. */ - radio->send(strobe, strobe_len); - - while(got_strobe_ack == 0 && - RTIMER_CLOCK_LT(RTIMER_NOW(), t + xmac_config.strobe_wait_time)) { - /* See if we got an ACK */ - if(!is_broadcast) { - packetbuf_clear(); - len = radio->read(packetbuf_dataptr(), PACKETBUF_SIZE); + while(got_strobe_ack == 0 && + RTIMER_CLOCK_LT(RTIMER_NOW(), t + xmac_config.strobe_wait_time)) { + rtimer_clock_t now = RTIMER_NOW(); + /* See if we got an ACK */ + packetbuf_clear(); + len = radio->read(packetbuf_dataptr(), PACKETBUF_SIZE); if(len > 0) { - packetbuf_set_datalen(len); - if(framer_get()->parse()) { - hdr = packetbuf_dataptr(); + packetbuf_set_datalen(len); + if(framer_get()->parse()) { + hdr = packetbuf_dataptr(); + if(hdr->dispatch == DISPATCH && hdr->type == TYPE_STROBE_ACK) { + if(rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER), + &rimeaddr_node_addr)) { + /* We got an ACK from the receiver, so we can immediately send + the packet. */ + got_strobe_ack = 1; + encounter_time = now; + } else { + PRINTDEBUG("xmac: strobe ack for someone else\n"); + } + } else /*if(hdr->dispatch == DISPATCH && hdr->type == TYPE_STROBE)*/ { + PRINTDEBUG("xmac: strobe from someone else\n"); + collisions++; + /* } else { + PRINTDEBUG("xmac: ignored len %u\n", len);*/ + } + } else { + PRINTF("xmac: send failed to parse %u\n", len); + } + } + } - if(hdr->dispatch == DISPATCH && hdr->type == TYPE_STROBE_ACK) { - if(rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER), - &rimeaddr_node_addr)) { - /* We got an ACK from the receiver, so we can immediately send - the packet. */ - got_strobe_ack = 1; - encounter_time = RTIMER_NOW(); - } else { - PRINTDEBUG("xmac: strobe ack for someone else\n"); - } - } else { - /* TODO One of the nodes should back-off since several - nodes are sending at once. If possible and if the - packet is destined for this node: send a strobe ack - if it is a strobe packet, queue the packet if it is a - data packet? */ - PRINTDEBUG("xmac: ignored data %u\n", len); -#if DEBUG - /* collisions++;*/ -#endif /* DEBUG */ - } - } else { - PRINTF("xmac: send failed to parse %u\n", len); - } - } + t = RTIMER_NOW(); + /* Send the strobe packet. */ + if(got_strobe_ack == 0 && collisions == 0) { + + if(is_broadcast) { +#if WITH_STROBE_BROADCAST + radio->send(strobe, strobe_len); +#else + /* restore the packet to send */ + queuebuf_to_packetbuf(packet); + radio->send(packetbuf_hdrptr(), packetbuf_totlen()); +#endif + off(); + } else { + rtimer_clock_t wt; + radio->send(strobe, strobe_len); +#if 1 + /* Turn off the radio for a while to let the other side + respond. We don't need to keep our radio on when we know + that the other side needs some time to produce a reply. */ + off(); + wt = RTIMER_NOW(); + while(RTIMER_CLOCK_LT(RTIMER_NOW(), wt + WAIT_TIME_BEFORE_STROBE_ACK)); +#endif /* 0 */ + on(); + } } } - - /* XXX: turn off radio if we haven't heard an ACK within a - specified time interval. */ - - /* if(got_strobe_ack == 0) { - off(); - while(RTIMER_CLOCK_LT(RTIMER_NOW(), t + xmac_config.strobe_wait_time)); - on(); - }*/ - } } #if WITH_ACK_OPTIMIZATION @@ -697,7 +679,7 @@ send_packet(void) queuebuf_free(packet); /* Send the data packet. */ - if(is_broadcast || got_strobe_ack || is_streaming) { + if((is_broadcast || got_strobe_ack || is_streaming) && collisions == 0) { radio->send(packetbuf_hdrptr(), packetbuf_totlen()); } @@ -729,29 +711,23 @@ send_packet(void) we_are_sending = 0; LEDS_OFF(LEDS_BLUE); - return 1; + if(collisions == 0) { + return MAC_TX_OK; + } else { + someone_is_sending++; + return MAC_TX_COLLISION; + } } /*---------------------------------------------------------------------------*/ -static struct queuebuf *queued_packet; static int qsend_packet(void) { if(someone_is_sending) { PRINTF("xmac: should queue packet, now just dropping %d %d %d %d.\n", waiting_for_packet, someone_is_sending, we_are_sending, radio_is_on); - if(queued_packet != NULL) { - RIMESTATS_ADD(sendingdrop); - return 0; - } else { -#if WITH_QUEUE - queued_packet = queuebuf_new_from_packetbuf(); - return 1; -#else - RIMESTATS_ADD(sendingdrop); - return 0; -#endif - } + RIMESTATS_ADD(sendingdrop); + return MAC_TX_COLLISION; } else { PRINTF("xmac: send immediately.\n"); return send_packet(); @@ -814,12 +790,6 @@ read_packet(void) waiting_for_packet = 0; - /* XXX should set timer to send queued packet later. */ - if(queued_packet != NULL) { - queuebuf_free(queued_packet); - queued_packet = NULL; - } - PRINTDEBUG("xmac: data(%u)\n", packetbuf_datalen()); return packetbuf_datalen(); } else {