/*---------------------------------------------------------------------------*/ static int send_packet(void) { rtimer_clock_t t0; rtimer_clock_t t; rtimer_clock_t encounter_time = 0; int strobes; int ret; #if 0 struct xmac_hdr *hdr; #endif uint8_t got_strobe_ack = 0; uint8_t got_ack = 0; uint8_t strobe[MAX_STROBE_SIZE]; int strobe_len, len; int is_broadcast = 0; /*int is_reliable; */ struct encounter *e; struct queuebuf *packet; int is_already_streaming = 0; uint8_t collisions; /* Create the X-MAC header for the data packet. */ packetbuf_set_addr(PACKETBUF_ADDR_SENDER, &rimeaddr_node_addr); if(rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER), &rimeaddr_null)) { is_broadcast = 1; PRINTDEBUG("xmac: send broadcast\n"); } else { #if UIP_CONF_IPV6 PRINTDEBUG("xmac: send unicast to %02x%02x:%02x%02x:%02x%02x:%02x%02x\n", packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[0], packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[1], packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[2], packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[3], packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[4], packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[5], packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[6], packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[7]); #else PRINTDEBUG("xmac: send unicast to %u.%u\n", packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[0], packetbuf_addr(PACKETBUF_ADDR_RECEIVER)->u8[1]); #endif /* UIP_CONF_IPV6 */ } /* is_reliable = packetbuf_attr(PACKETBUF_ATTR_RELIABLE) || packetbuf_attr(PACKETBUF_ATTR_ERELIABLE); */ packetbuf_set_attr(PACKETBUF_ATTR_MAC_ACK, 1); len = NETSTACK_FRAMER.create(); strobe_len = len + sizeof(struct xmac_hdr); if(len < 0 || strobe_len > (int)sizeof(strobe)) { /* Failed to send */ PRINTF("xmac: send failed, too large header\n"); return MAC_TX_ERR_FATAL; } memcpy(strobe, packetbuf_hdrptr(), len); strobe[len] = DISPATCH; /* dispatch */ strobe[len + 1] = TYPE_STROBE; /* type */ packetbuf_compact(); packet = queuebuf_new_from_packetbuf(); if(packet == NULL) { /* No buffer available */ PRINTF("xmac: send failed, no queue buffer available (of %u)\n", QUEUEBUF_CONF_NUM); return MAC_TX_ERR; } #if WITH_STREAMING if(is_streaming == 1 && (rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER), &is_streaming_to) || rimeaddr_cmp(packetbuf_addr(PACKETBUF_ADDR_RECEIVER), &is_streaming_to_too))) { is_already_streaming = 1; } if(packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) == PACKETBUF_ATTR_PACKET_TYPE_STREAM) { is_streaming = 1; 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; } #endif /* WITH_STREAMING */ off(); #if WITH_ENCOUNTER_OPTIMIZATION /* We go through the list of encounters to find if we have recorded an encounter with this particular neighbor. If so, we can compute the time for the next expected encounter and setup a ctimer to switch on the radio just before the encounter. */ for(e = list_head(encounter_list); e != NULL; e = list_item_next(e)) { const rimeaddr_t *neighbor = packetbuf_addr(PACKETBUF_ADDR_RECEIVER); if(rimeaddr_cmp(neighbor, &e->neighbor)) { rtimer_clock_t wait, now, expected; /* We expect encounters to happen every DEFAULT_PERIOD time units. The next expected encounter is at time e->time + DEFAULT_PERIOD. To compute a relative offset, we subtract with clock_time(). Because we are only interested in turning on the radio within the DEFAULT_PERIOD period, we compute the waiting time with modulo DEFAULT_PERIOD. */ now = RTIMER_NOW(); wait = ((rtimer_clock_t)(e->time - now)) % (DEFAULT_PERIOD); if(wait < 2 * DEFAULT_ON_TIME) { wait = DEFAULT_PERIOD; } expected = now + wait - 2 * DEFAULT_ON_TIME; #if WITH_ACK_OPTIMIZATION /* Wait until the receiver is expected to be awake */ if(packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) != PACKETBUF_ATTR_PACKET_TYPE_ACK && is_streaming == 0) { /* Do not wait if we are sending an ACK, because then the receiver will already be awake. */ while(RTIMER_CLOCK_LT(RTIMER_NOW(), expected)); } #else /* WITH_ACK_OPTIMIZATION */ /* Wait until the receiver is expected to be awake */ while(RTIMER_CLOCK_LT(RTIMER_NOW(), expected)); #endif /* WITH_ACK_OPTIMIZATION */ } } #endif /* WITH_ENCOUNTER_OPTIMIZATION */ /* 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; t0 = RTIMER_NOW(); strobes = 0; LEDS_ON(LEDS_BLUE); /* Send a train of strobes until the receiver answers with an ACK. */ /* Turn on the radio to listen for the strobe ACK. */ // on(); collisions = 0; if(!is_already_streaming) { 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++) { while(got_strobe_ack == 0 && RTIMER_CLOCK_LT(RTIMER_NOW(), t + xmac_config.strobe_wait_time)) { #if 0 rtimer_clock_t now = RTIMER_NOW(); /* See if we got an ACK */ packetbuf_clear(); len = NETSTACK_RADIO.read(packetbuf_dataptr(), PACKETBUF_SIZE); if(len > 0) { packetbuf_set_datalen(len); if(NETSTACK_FRAMER.parse() >= 0) { 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 { PRINTF("xmac: send failed to parse %u\n", len); } } #endif /* 0 */ } t = RTIMER_NOW(); /* Send the strobe packet. */ if(got_strobe_ack == 0 && collisions == 0) { if(is_broadcast) { #if WITH_STROBE_BROADCAST ret = NETSTACK_RADIO.send(strobe, strobe_len); #else /* restore the packet to send */ queuebuf_to_packetbuf(packet); ret = NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen()); #endif off(); } else { #if 0 rtimer_clock_t wt; #endif on(); ret = NETSTACK_RADIO.send(strobe, strobe_len); #if 0 /* 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 */ #if RDC_CONF_HARDWARE_ACK if(ret == RADIO_TX_OK) { got_strobe_ack = 1; } else { off(); } #else if(detect_ack()) { got_strobe_ack = 1; } else { off(); } #endif /* RDC_CONF_HARDWARE_ACK */ } } } } #if WITH_ACK_OPTIMIZATION /* If we have received the strobe ACK, and we are sending a packet that will need an upper layer ACK (as signified by the PACKETBUF_ATTR_RELIABLE packet attribute), we keep the radio on. */ if(got_strobe_ack && (packetbuf_attr(PACKETBUF_ATTR_RELIABLE) || packetbuf_attr(PACKETBUF_ATTR_ERELIABLE) || packetbuf_attr(PACKETBUF_ATTR_PACKET_TYPE) == PACKETBUF_ATTR_PACKET_TYPE_STREAM)) { on(); /* Wait for ACK packet */ waiting_for_packet = 1; } else { off(); } #endif /* WITH_ACK_OPTIMIZATION */ /* restore the packet to send */ queuebuf_to_packetbuf(packet); queuebuf_free(packet); /* Send the data packet. */ if((is_broadcast || got_strobe_ack || is_streaming) && collisions == 0) { ret = NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen()); if(!is_broadcast) { #if RDC_CONF_HARDWARE_ACK if(ret == RADIO_TX_OK) { got_ack = 1; } #else if(detect_ack()) { got_ack = 1; } #endif /* RDC_CONF_HARDWARE_ACK */ } } off(); #if WITH_ENCOUNTER_OPTIMIZATION if(got_strobe_ack && !is_streaming) { register_encounter(packetbuf_addr(PACKETBUF_ADDR_RECEIVER), encounter_time); } #endif /* WITH_ENCOUNTER_OPTIMIZATION */ watchdog_start(); PRINTF("xmac: send (strobes=%u,len=%u,%s), done\n", strobes, packetbuf_totlen(), got_strobe_ack ? "ack" : "no ack"); #if XMAC_CONF_COMPOWER /* Accumulate the power consumption for the packet transmission. */ compower_accumulate(¤t_packet); /* Convert the accumulated power consumption for the transmitted packet to packet attributes so that the higher levels can keep track of the amount of energy spent on transmitting the packet. */ compower_attrconv(¤t_packet); /* Clear the accumulated power consumption so that it is ready for the next packet. */ compower_clear(¤t_packet); #endif /* XMAC_CONF_COMPOWER */ we_are_sending = 0; LEDS_OFF(LEDS_BLUE); if(collisions == 0) { if(is_broadcast == 0 && got_ack == 0) { return MAC_TX_NOACK; } else { return MAC_TX_OK; } } else { someone_is_sending++; return MAC_TX_COLLISION; } }
/** * Read a packet from the underlying radio driver. If the incoming * packet is a probe packet and the sender of the probe matches the * destination address of the queued packet (if any), the queued packet * is sent. */ static void input_packet(void) { struct lpp_hdr hdr; clock_time_t reception_time; reception_time = clock_time(); if(!NETSTACK_FRAMER.parse()) { printf("lpp input_packet framer error\n"); } memcpy(&hdr, packetbuf_dataptr(), sizeof(struct lpp_hdr));; packetbuf_hdrreduce(sizeof(struct lpp_hdr)); /* PRINTF("got packet type %d\n", hdr->type);*/ if(hdr.type == TYPE_PROBE) { struct announcement_msg adata; /* Register the encounter with the sending node. We now know the neighbor's phase. */ register_encounter(&hdr.sender, reception_time); /* Parse incoming announcements */ memcpy(&adata, packetbuf_dataptr(), MIN(packetbuf_datalen(), sizeof(adata))); #if 0 PRINTF("%d.%d: probe from %d.%d with %d announcements\n", rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], hdr.sender.u8[0], hdr.sender.u8[1], adata->num); if(adata.num / sizeof(struct announcement_data) > sizeof(struct announcement_msg)) { /* Sanity check. The number of announcements is too large - corrupt packet has been received. */ return 0; } for(i = 0; i < adata.num; ++i) { /* PRINTF("%d.%d: announcement %d: %d\n", rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], adata->data[i].id, adata->data[i].value);*/ announcement_heard(&hdr.sender, adata.data[i].id, adata.data[i].value); } #endif /* 0 */ /* Go through the list of packets to be sent to see if any of them match the sender of the probe, or if they are a broadcast packet that should be sent. */ if(list_length(queued_packets_list) > 0) { struct queue_list_item *i; for(i = list_head(queued_packets_list); i != NULL; i = list_item_next(i)) { const rimeaddr_t *receiver; uint8_t sent; sent = 0; receiver = queuebuf_addr(i->packet, PACKETBUF_ADDR_RECEIVER); if(rimeaddr_cmp(receiver, &hdr.sender) || rimeaddr_cmp(receiver, &rimeaddr_null)) { queuebuf_to_packetbuf(i->packet); #if WITH_PENDING_BROADCAST if(i->broadcast_flag == BROADCAST_FLAG_NONE || i->broadcast_flag == BROADCAST_FLAG_SEND) { i->num_transmissions = 1; NETSTACK_RADIO.send(queuebuf_dataptr(i->packet), queuebuf_datalen(i->packet)); sent = 1; PRINTF("%d.%d: got a probe from %d.%d, sent packet to %d.%d\n", rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], hdr.sender.u8[0], hdr.sender.u8[1], receiver->u8[0], receiver->u8[1]); } else { PRINTF("%d.%d: got a probe from %d.%d, did not send packet\n", rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], hdr.sender.u8[0], hdr.sender.u8[1]); } #else /* WITH_PENDING_BROADCAST */ i->num_transmissions = 1; NETSTACK_RADIO.send(queuebuf_dataptr(i->packet), queuebuf_datalen(i->packet)); PRINTF("%d.%d: got a probe from %d.%d, sent packet to %d.%d\n", rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], hdr.sender.u8[0], hdr.sender.u8[1], receiver->u8[0], receiver->u8[1]); #endif /* WITH_PENDING_BROADCAST */ /* off();*/ /* Attribute the energy spent on listening for the probe to this packet transmission. */ compower_accumulate(&i->compower); /* If the packet was not a broadcast packet, we dequeue it now. Broadcast packets should be transmitted to all neighbors, and are dequeued by the dutycycling function instead, after the appropriate time. */ if(!rimeaddr_cmp(receiver, &rimeaddr_null)) { if(detect_ack()) { remove_queued_packet(i, 1); } else { remove_queued_packet(i, 0); } #if WITH_PROBE_AFTER_TRANSMISSION /* Send a probe packet to catch any reply from the other node. */ restart_dutycycle(PROBE_AFTER_TRANSMISSION_TIME); #endif /* WITH_PROBE_AFTER_TRANSMISSION */ #if WITH_STREAMING if(is_streaming) { ctimer_set(&stream_probe_timer, STREAM_PROBE_TIME, send_stream_probe, NULL); } #endif /* WITH_STREAMING */ } if(sent) { turn_radio_off(); } #if WITH_ACK_OPTIMIZATION if(packetbuf_attr(PACKETBUF_ATTR_RELIABLE) || packetbuf_attr(PACKETBUF_ATTR_ERELIABLE)) { /* We're sending a packet that needs an ACK, so we keep the radio on in anticipation of the ACK. */ turn_radio_on(); } #endif /* WITH_ACK_OPTIMIZATION */ } } } } else if(hdr.type == TYPE_DATA) { turn_radio_off(); if(!rimeaddr_cmp(&hdr.receiver, &rimeaddr_null)) { if(!rimeaddr_cmp(&hdr.receiver, &rimeaddr_node_addr)) { /* Not broadcast or for us */ PRINTF("%d.%d: data not for us from %d.%d\n", rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], hdr.sender.u8[0], hdr.sender.u8[1]); return; } packetbuf_set_addr(PACKETBUF_ADDR_RECEIVER, &hdr.receiver); } packetbuf_set_addr(PACKETBUF_ADDR_SENDER, &hdr.sender); PRINTF("%d.%d: got data from %d.%d\n", rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1], hdr.sender.u8[0], hdr.sender.u8[1]); /* Accumulate the power consumption for the packet reception. */ compower_accumulate(¤t_packet); /* Convert the accumulated power consumption for the received packet to packet attributes so that the higher levels can keep track of the amount of energy spent on receiving the packet. */ compower_attrconv(¤t_packet); /* Clear the accumulated power consumption so that it is ready for the next packet. */ compower_clear(¤t_packet); #if WITH_PENDING_BROADCAST if(rimeaddr_cmp(&hdr.receiver, &rimeaddr_null)) { /* This is a broadcast packet. Check the list of pending packets to see if we are currently sending a broadcast. If so, we refrain from sending our broadcast until one sleep cycle period, so that the other broadcaster will have finished sending. */ struct queue_list_item *i; for(i = list_head(queued_packets_list); i != NULL; i = list_item_next(i)) { /* If the packet is a broadcast packet that is not yet ready to be sent, we do not send it. */ if(i->broadcast_flag == BROADCAST_FLAG_PENDING) { PRINTF("Someone else is sending, pending -> waiting\n"); set_broadcast_flag(i, BROADCAST_FLAG_WAITING); } } } #endif /* WITH_PENDING_BROADCAST */ #if WITH_PROBE_AFTER_RECEPTION /* XXX send probe after receiving a packet to facilitate data streaming. We must first copy the contents of the packetbuf into a queuebuf to avoid overwriting the data with the probe packet. */ if(rimeaddr_cmp(&hdr.receiver, &rimeaddr_node_addr)) { struct queuebuf *q; q = queuebuf_new_from_packetbuf(); if(q != NULL) { send_probe(); queuebuf_to_packetbuf(q); queuebuf_free(q); } } #endif /* WITH_PROBE_AFTER_RECEPTION */ #if WITH_ADAPTIVE_OFF_TIME off_time = LOWEST_OFF_TIME; restart_dutycycle(off_time); #endif /* WITH_ADAPTIVE_OFF_TIME */ NETSTACK_MAC.input(); } }