static void _rx_management_success(gnrc_netif_t *netif) { LOG_DEBUG("[LWMAC] Reception was successful\n"); gnrc_lwmac_rx_stop(netif); /* Dispatch received packets, timing is not critical anymore */ gnrc_mac_dispatch(&netif->mac.rx); /* Here we check if we are close to the end of the cycle. If yes, * go to sleep. Firstly, get the relative phase. */ uint32_t phase = rtt_get_counter(); if (phase < netif->mac.lwmac.last_wakeup) { phase = (RTT_US_TO_TICKS(GNRC_LWMAC_PHASE_MAX) - netif->mac.lwmac.last_wakeup) + phase; } else { phase = phase - netif->mac.lwmac.last_wakeup; } /* If the relative phase is beyond 4/5 cycle time, go to sleep. */ if (phase > (4 * RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_INTERVAL_US) / 5)) { gnrc_lwmac_set_quit_rx(netif, true); } if (gnrc_lwmac_get_quit_rx(netif)) { lwmac_set_state(netif, GNRC_LWMAC_SLEEPING); } else { /* Go back to LISTENING after successful reception */ lwmac_set_state(netif, GNRC_LWMAC_LISTENING); } }
static void _rx_management_failed(gnrc_netif_t *netif) { /* This may happen frequently because we'll receive WA from * every node in range. */ LOG_DEBUG("[LWMAC] Reception was NOT successful\n"); gnrc_lwmac_rx_stop(netif); if (netif->mac.rx.rx_bad_exten_count >= GNRC_LWMAC_MAX_RX_EXTENSION_NUM) { gnrc_lwmac_set_quit_rx(netif, true); } /* Here we check if we are close to the end of the cycle. If yes, * go to sleep. Firstly, get the relative phase. */ uint32_t phase = rtt_get_counter(); if (phase < netif->mac.lwmac.last_wakeup) { phase = (RTT_US_TO_TICKS(GNRC_LWMAC_PHASE_MAX) - netif->mac.lwmac.last_wakeup) + phase; } else { phase = phase - netif->mac.lwmac.last_wakeup; } /* If the relative phase is beyond 4/5 cycle time, go to sleep. */ if (phase > (4 * RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_INTERVAL_US) / 5)) { gnrc_lwmac_set_quit_rx(netif, true); } if (gnrc_lwmac_get_quit_rx(netif)) { lwmac_set_state(netif, GNRC_LWMAC_SLEEPING); } else { /* Go back to LISTENING for keep hearing on the channel */ lwmac_set_state(netif, GNRC_LWMAC_LISTENING); } }
int rtc_get_time(struct tm *time) { time_t t = (time_t)rtt_get_counter(); gmtime_r(&t, time); return 0; }
static uint32_t _next_inphase_event(uint32_t last, uint32_t interval) { /* Counter did overflow since last wakeup */ if (rtt_get_counter() < last) { /* TODO: Not sure if this was tested :) */ uint32_t tmp = -last; tmp /= interval; tmp++; last += tmp * interval; } /* Add margin to next wakeup so that it will be at least 2ms in the future */ while (last < (rtt_get_counter() + GNRC_LWMAC_RTT_EVENT_MARGIN_TICKS)) { last += interval; } return last; }
void rtt_handler(uint32_t event, gnrc_netif_t *netif) { uint32_t alarm; switch (event & 0xffff) { case GNRC_LWMAC_EVENT_RTT_WAKEUP_PENDING: { /* A new cycle starts, set sleep timing and initialize related MAC-info flags. */ netif->mac.lwmac.last_wakeup = rtt_get_alarm(); alarm = _next_inphase_event(netif->mac.lwmac.last_wakeup, RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_DURATION_US)); rtt_set_alarm(alarm, rtt_cb, (void *) GNRC_LWMAC_EVENT_RTT_SLEEP_PENDING); gnrc_lwmac_set_quit_tx(netif, false); gnrc_lwmac_set_quit_rx(netif, false); gnrc_lwmac_set_phase_backoff(netif, false); netif->mac.rx.rx_bad_exten_count = 0; lwmac_set_state(netif, GNRC_LWMAC_LISTENING); break; } case GNRC_LWMAC_EVENT_RTT_SLEEP_PENDING: { /* Set next wake-up timing. */ alarm = _next_inphase_event(netif->mac.lwmac.last_wakeup, RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_INTERVAL_US)); rtt_set_alarm(alarm, rtt_cb, (void *) GNRC_LWMAC_EVENT_RTT_WAKEUP_PENDING); lwmac_set_state(netif, GNRC_LWMAC_SLEEPING); break; } /* Set initial wake-up alarm that starts the cycle */ case GNRC_LWMAC_EVENT_RTT_START: { LOG_DEBUG("[LWMAC] RTT: Initialize duty cycling\n"); alarm = rtt_get_counter() + RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_DURATION_US); rtt_set_alarm(alarm, rtt_cb, (void *) GNRC_LWMAC_EVENT_RTT_SLEEP_PENDING); gnrc_lwmac_set_dutycycle_active(netif, true); break; } case GNRC_LWMAC_EVENT_RTT_STOP: case GNRC_LWMAC_EVENT_RTT_PAUSE: { rtt_clear_alarm(); LOG_DEBUG("[LWMAC] RTT: Stop duty cycling, now in state %u\n", netif->mac.lwmac.state); gnrc_lwmac_set_dutycycle_active(netif, false); break; } case GNRC_LWMAC_EVENT_RTT_RESUME: { LOG_DEBUG("[LWMAC] RTT: Resume duty cycling\n"); rtt_clear_alarm(); alarm = _next_inphase_event(netif->mac.lwmac.last_wakeup, RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_INTERVAL_US)); rtt_set_alarm(alarm, rtt_cb, (void *) GNRC_LWMAC_EVENT_RTT_WAKEUP_PENDING); gnrc_lwmac_set_dutycycle_active(netif, true); break; } default: break; } }
static void _lwmac_init(gnrc_netif_t *netif) { netdev_t *dev; dev = netif->dev; dev->event_callback = _lwmac_event_cb; /* RTT is used for scheduling wakeup */ rtt_init(); /* Store pid globally, so that IRQ can use it to send msg */ lwmac_pid = netif->pid; /* Enable RX- and TX-started interrupts */ netopt_enable_t enable = NETOPT_ENABLE; dev->driver->set(dev, NETOPT_RX_START_IRQ, &enable, sizeof(enable)); dev->driver->set(dev, NETOPT_TX_START_IRQ, &enable, sizeof(enable)); dev->driver->set(dev, NETOPT_TX_END_IRQ, &enable, sizeof(enable)); uint16_t src_len = IEEE802154_LONG_ADDRESS_LEN; dev->driver->set(dev, NETOPT_SRC_LEN, &src_len, sizeof(src_len)); /* Get own address from netdev */ netif->l2addr_len = dev->driver->get(dev, NETOPT_ADDRESS_LONG, &netif->l2addr, IEEE802154_LONG_ADDRESS_LEN); /* Initialize broadcast sequence number. This at least differs from board * to board */ netif->mac.tx.bcast_seqnr = netif->l2addr[0]; /* Reset all timeouts just to be sure */ gnrc_lwmac_reset_timeouts(netif); /* Start duty cycling */ lwmac_set_state(netif, GNRC_LWMAC_START); #if (GNRC_LWMAC_ENABLE_DUTYCYLE_RECORD == 1) /* Start duty cycle recording */ netif->mac.lwmac.system_start_time_ticks = rtt_get_counter(); netif->mac.lwmac.last_radio_on_time_ticks = netif->mac.lwmac.system_start_time_ticks; netif->mac.lwmac.awake_duration_sum_ticks = 0; netif->mac.lwmac.lwmac_info |= GNRC_LWMAC_RADIO_IS_ON; #endif }
void gnrc_lwmac_tx_start(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt, gnrc_mac_tx_neighbor_t *neighbor) { assert(netif != NULL); assert(pkt != NULL); assert(neighbor != NULL); if (netif->mac.tx.packet) { LOG_WARNING("WARNING: [LWMAC-tx] Starting but tx.packet is still set\n"); gnrc_pktbuf_release(netif->mac.tx.packet); } netif->mac.tx.packet = pkt; netif->mac.tx.current_neighbor = neighbor; netif->mac.tx.state = GNRC_LWMAC_TX_STATE_INIT; netif->mac.tx.wr_sent = 0; #if (LWMAC_ENABLE_DUTYCYLE_RECORD == 1) netif->mac.prot.lwmac.pkt_start_sending_time_ticks = rtt_get_counter(); #endif }
static void _sleep_management(gnrc_netif_t *netif) { /* If a packet is scheduled, no other (possible earlier) packet can be * sent before the first one is handled, even no broadcast */ if (!gnrc_lwmac_timeout_is_running(netif, GNRC_LWMAC_TIMEOUT_WAIT_DEST_WAKEUP)) { gnrc_mac_tx_neighbor_t *neighbour; /* Check if there is packet remaining for retransmission */ if (netif->mac.tx.current_neighbor != NULL) { neighbour = netif->mac.tx.current_neighbor; } else { /* Check if there are broadcasts to send and transmit immediately */ if (gnrc_priority_pktqueue_length(&(netif->mac.tx.neighbors[0].queue)) > 0) { netif->mac.tx.current_neighbor = &(netif->mac.tx.neighbors[0]); lwmac_set_state(netif, GNRC_LWMAC_TRANSMITTING); return; } neighbour = _next_tx_neighbor(netif); } if (neighbour != NULL) { /* if phase is unknown, send immediately. */ if (neighbour->phase > RTT_TICKS_TO_US(GNRC_LWMAC_WAKEUP_INTERVAL_US)) { netif->mac.tx.current_neighbor = neighbour; gnrc_lwmac_set_tx_continue(netif, false); netif->mac.tx.tx_burst_count = 0; lwmac_set_state(netif, GNRC_LWMAC_TRANSMITTING); return; } /* Offset in microseconds when the earliest (phase) destination * node wakes up that we have packets for. */ uint32_t time_until_tx = RTT_TICKS_TO_US(_gnrc_lwmac_ticks_until_phase(neighbour->phase)); /* If there's not enough time to prepare a WR to catch the phase * postpone to next interval */ if (time_until_tx < GNRC_LWMAC_WR_PREPARATION_US) { time_until_tx += GNRC_LWMAC_WAKEUP_INTERVAL_US; } time_until_tx -= GNRC_LWMAC_WR_PREPARATION_US; /* add a random time before goto TX, for avoiding one node for * always holding the medium (if the receiver's phase is recorded earlier in this * particular node) */ uint32_t random_backoff; random_backoff = random_uint32_range(0, GNRC_LWMAC_TIME_BETWEEN_WR_US); time_until_tx = time_until_tx + random_backoff; gnrc_lwmac_set_timeout(netif, GNRC_LWMAC_TIMEOUT_WAIT_DEST_WAKEUP, time_until_tx); /* Register neighbour to be the next */ netif->mac.tx.current_neighbor = neighbour; /* Stop dutycycling, we're preparing to send. This prevents the * timeout arriving late, so that the destination phase would * be missed. */ /* TODO: bad for power savings */ rtt_handler(GNRC_LWMAC_EVENT_RTT_PAUSE, netif); } } else if (gnrc_lwmac_timeout_is_expired(netif, GNRC_LWMAC_TIMEOUT_WAIT_DEST_WAKEUP)) { LOG_DEBUG("[LWMAC] Got timeout for dest wakeup, ticks: %" PRIu32 "\n", rtt_get_counter()); gnrc_lwmac_set_tx_continue(netif, false); netif->mac.tx.tx_burst_count = 0; lwmac_set_state(netif, GNRC_LWMAC_TRANSMITTING); } }
void lwmac_set_state(gnrc_netif_t *netif, gnrc_lwmac_state_t newstate) { gnrc_lwmac_state_t oldstate = netif->mac.lwmac.state; if (newstate == oldstate) { return; } if (newstate >= GNRC_LWMAC_STATE_COUNT) { LOG_ERROR("ERROR: [LWMAC] Trying to set invalid state %u\n", newstate); return; } /* Already change state, but might be reverted to oldstate when needed */ netif->mac.lwmac.state = newstate; /* Actions when leaving old state */ switch (oldstate) { case GNRC_LWMAC_RECEIVING: case GNRC_LWMAC_TRANSMITTING: { /* Enable duty cycling again */ rtt_handler(GNRC_LWMAC_EVENT_RTT_RESUME, netif); #if (GNRC_LWMAC_ENABLE_DUTYCYLE_RECORD == 1) /* Output duty-cycle ratio */ uint64_t duty; duty = (uint64_t) rtt_get_counter(); duty = ((uint64_t) netif->mac.lwmac.awake_duration_sum_ticks) * 100 / (duty - (uint64_t)netif->mac.lwmac.system_start_time_ticks); printf("[LWMAC]: achieved duty-cycle: %lu %% \n", (uint32_t)duty); #endif break; } case GNRC_LWMAC_SLEEPING: { gnrc_lwmac_clear_timeout(netif, GNRC_LWMAC_TIMEOUT_WAKEUP_PERIOD); break; } default: break; } /* Actions when entering new state */ switch (newstate) { /*********************** Operation states *********************************/ case GNRC_LWMAC_LISTENING: { _gnrc_lwmac_set_netdev_state(netif, NETOPT_STATE_IDLE); break; } case GNRC_LWMAC_SLEEPING: { /* Put transceiver to sleep */ _gnrc_lwmac_set_netdev_state(netif, NETOPT_STATE_SLEEP); /* We may have come here through RTT handler, so timeout may still be active */ gnrc_lwmac_clear_timeout(netif, GNRC_LWMAC_TIMEOUT_WAKEUP_PERIOD); if (gnrc_lwmac_get_phase_backoff(netif)) { gnrc_lwmac_set_phase_backoff(netif, false); uint32_t alarm; rtt_clear_alarm(); alarm = random_uint32_range(RTT_US_TO_TICKS((3 * GNRC_LWMAC_WAKEUP_DURATION_US / 2)), RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_INTERVAL_US - (3 * GNRC_LWMAC_WAKEUP_DURATION_US / 2))); LOG_WARNING("WARNING: [LWMAC] phase backoffed: %lu us\n", RTT_TICKS_TO_US(alarm)); netif->mac.lwmac.last_wakeup = netif->mac.lwmac.last_wakeup + alarm; alarm = _next_inphase_event(netif->mac.lwmac.last_wakeup, RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_INTERVAL_US)); rtt_set_alarm(alarm, rtt_cb, (void *) GNRC_LWMAC_EVENT_RTT_WAKEUP_PENDING); } /* Return immediately, so no rescheduling */ return; } /* Trying to send data */ case GNRC_LWMAC_TRANSMITTING: { rtt_handler(GNRC_LWMAC_EVENT_RTT_PAUSE, netif); /**< No duty cycling while RXing */ _gnrc_lwmac_set_netdev_state(netif, NETOPT_STATE_IDLE); /**< Power up netdev */ break; } /* Receiving incoming data */ case GNRC_LWMAC_RECEIVING: { rtt_handler(GNRC_LWMAC_EVENT_RTT_PAUSE, netif); /**< No duty cycling while TXing */ _gnrc_lwmac_set_netdev_state(netif, NETOPT_STATE_IDLE); /**< Power up netdev */ break; } case GNRC_LWMAC_STOPPED: { _gnrc_lwmac_set_netdev_state(netif, NETOPT_STATE_OFF); break; } /*********************** Control states ***********************************/ case GNRC_LWMAC_START: { rtt_handler(GNRC_LWMAC_EVENT_RTT_START, netif); lwmac_set_state(netif, GNRC_LWMAC_LISTENING); break; } case GNRC_LWMAC_STOP: { rtt_handler(GNRC_LWMAC_EVENT_RTT_STOP, netif); lwmac_set_state(netif, GNRC_LWMAC_STOPPED); break; } case GNRC_LWMAC_RESET: { LOG_WARNING("WARNING: [LWMAC] Reset not yet implemented\n"); lwmac_set_state(netif, GNRC_LWMAC_STOP); lwmac_set_state(netif, GNRC_LWMAC_START); break; } /**************************************************************************/ default: { LOG_DEBUG("[LWMAC] No actions for entering state %u\n", newstate); return; } } lwmac_schedule_update(netif); }
/* return false if send data failed, otherwise return true */ static bool _send_data(gnrc_netif_t *netif) { assert(netif != NULL); gnrc_pktsnip_t *pkt = netif->mac.tx.packet; gnrc_pktsnip_t *pkt_payload; assert(pkt != NULL); /* Enable Auto ACK again */ netopt_enable_t autoack = NETOPT_ENABLE; netif->dev->driver->set(netif->dev, NETOPT_AUTOACK, &autoack, sizeof(autoack)); /* It's okay to retry sending DATA. Timing doesn't matter anymore and * destination is waiting for a certain amount of time. */ uint8_t csma_retries = GNRC_LWMAC_DATA_CSMA_RETRIES; netif->dev->driver->set(netif->dev, NETOPT_CSMA_RETRIES, &csma_retries, sizeof(csma_retries)); netif->mac.mac_info |= GNRC_NETIF_MAC_INFO_CSMA_ENABLED; netopt_enable_t csma_enable = NETOPT_ENABLE; netif->dev->driver->set(netif->dev, NETOPT_CSMA, &csma_enable, sizeof(csma_enable)); pkt_payload = pkt->next; /* Insert LWMAC header above NETIF header. The burst (consecutive) transmission * scheme works here (sender side). If the sender finds it has pending packets * for the receiver (and under burst limit), it sets the packet type to * FRAMETYPE_DATA_PENDING, to notice the receiver for next incoming packet. * In case the sender has no more packet for the receiver, it simply sets the * data type to FRAMETYPE_DATA. */ gnrc_lwmac_hdr_t hdr; if ((gnrc_priority_pktqueue_length(&netif->mac.tx.current_neighbor->queue) > 0) && (netif->mac.tx.tx_burst_count < GNRC_LWMAC_MAX_TX_BURST_PKT_NUM)) { hdr.type = GNRC_LWMAC_FRAMETYPE_DATA_PENDING; gnrc_lwmac_set_tx_continue(netif, true); netif->mac.tx.tx_burst_count++; } else { hdr.type = GNRC_LWMAC_FRAMETYPE_DATA; gnrc_lwmac_set_tx_continue(netif, false); } pkt->next = gnrc_pktbuf_add(pkt->next, &hdr, sizeof(hdr), GNRC_NETTYPE_LWMAC); if (pkt->next == NULL) { LOG_ERROR("ERROR: [LWMAC-tx] Cannot allocate pktbuf of type GNRC_NETTYPE_LWMAC\n"); LOG_ERROR("ERROR: [LWMAC-tx] Memory maybe full, drop the data packet\n"); netif->mac.tx.packet->next = pkt_payload; gnrc_pktbuf_release(netif->mac.tx.packet); /* clear packet point to avoid TX retry */ netif->mac.tx.packet = NULL; return false; } /* if found ongoing transmission, quit this cycle for collision avoidance. * Data packet will be re-queued and try to send in the next cycle. */ if (_gnrc_lwmac_get_netdev_state(netif) == NETOPT_STATE_RX) { /* save pointer to netif header */ gnrc_pktsnip_t *netif_snip = pkt->next->next; /* remove LWMAC header */ pkt->next->next = NULL; gnrc_pktbuf_release(pkt->next); /* make append netif header after payload again */ pkt->next = netif_snip; if (!gnrc_mac_queue_tx_packet(&netif->mac.tx, 0, netif->mac.tx.packet)) { gnrc_pktbuf_release(netif->mac.tx.packet); LOG_WARNING("WARNING: [LWMAC-tx] TX queue full, drop packet\n"); } /* drop pointer so it wont be free'd */ netif->mac.tx.packet = NULL; return false; } /* Send data */ int res = _gnrc_lwmac_transmit(netif, pkt); if (res < 0) { LOG_ERROR("ERROR: [LWMAC-tx] Send data failed."); gnrc_pktbuf_release(pkt); /* clear packet point to avoid TX retry */ netif->mac.tx.packet = NULL; return false; } /* Packet has been released by netdev, so drop pointer */ netif->mac.tx.packet = NULL; DEBUG("[LWMAC-tx]: spent %lu WR in TX\n", (unsigned long)netif->mac.tx.wr_sent); #if (LWMAC_ENABLE_DUTYCYLE_RECORD == 1) netif->mac.prot.lwmac.pkt_start_sending_time_ticks = rtt_get_counter() - netif->mac.prot.lwmac.pkt_start_sending_time_ticks; DEBUG("[LWMAC-tx]: pkt sending delay in TX: %lu us\n", RTT_TICKS_TO_US(netif->mac.prot.lwmac.pkt_start_sending_time_ticks)); #endif return true; }