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); } }
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; } }
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); }
static uint8_t _packet_process_in_wait_for_wa(gnrc_netif_t *netif) { assert(netif != NULL); uint8_t tx_info = 0; gnrc_pktsnip_t *pkt; bool found_wa = false; bool postponed = false; bool from_expected_destination = false; while ((pkt = gnrc_priority_pktqueue_pop(&netif->mac.rx.queue)) != NULL) { LOG_DEBUG("[LWMAC-tx] Inspecting pkt @ %p\n", pkt); /* Parse packet */ gnrc_lwmac_packet_info_t info; int ret = _gnrc_lwmac_parse_packet(pkt, &info); if (ret != 0) { LOG_DEBUG("[LWMAC-tx] Packet could not be parsed: %i\n", ret); gnrc_pktbuf_release(pkt); continue; } if (memcmp(&info.src_addr.addr, &netif->mac.tx.current_neighbor->l2_addr, netif->mac.tx.current_neighbor->l2_addr_len) == 0) { from_expected_destination = true; } if (info.header->type == GNRC_LWMAC_FRAMETYPE_BROADCAST) { _gnrc_lwmac_dispatch_defer(netif->mac.rx.dispatch_buffer, pkt); gnrc_mac_dispatch(&netif->mac.rx); /* Drop pointer to it can't get released */ pkt = NULL; continue; } /* Check if destination is talking to another node. It will sleep * after a finished transaction so there's no point in trying any * further now. */ if (!(memcmp(&info.dst_addr.addr, &netif->l2addr, netif->l2addr_len) == 0) && from_expected_destination) { 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; postponed = true; gnrc_pktbuf_release(pkt); break; } /* if found anther node is also trying to send data, * quit this cycle for collision avoidance. */ if (info.header->type == GNRC_LWMAC_FRAMETYPE_WR) { 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; postponed = true; gnrc_pktbuf_release(pkt); break; } if (info.header->type != GNRC_LWMAC_FRAMETYPE_WA) { LOG_DEBUG("[LWMAC-tx] Packet is not WA: 0x%02x\n", info.header->type); gnrc_pktbuf_release(pkt); continue; } if (from_expected_destination) { /* calculate the phase of the receiver based on WA */ netif->mac.tx.timestamp = _gnrc_lwmac_phase_now(); gnrc_lwmac_frame_wa_t *wa_hdr; wa_hdr = (gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_LWMAC))->data; if (netif->mac.tx.timestamp >= wa_hdr->current_phase) { netif->mac.tx.timestamp = netif->mac.tx.timestamp - wa_hdr->current_phase; } else { netif->mac.tx.timestamp += RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_INTERVAL_US); netif->mac.tx.timestamp -= wa_hdr->current_phase; } uint32_t own_phase; own_phase = _gnrc_lwmac_ticks_to_phase(netif->mac.prot.lwmac.last_wakeup); if (own_phase >= netif->mac.tx.timestamp) { own_phase = own_phase - netif->mac.tx.timestamp; } else { own_phase = netif->mac.tx.timestamp - own_phase; } if ((own_phase < RTT_US_TO_TICKS((3 * GNRC_LWMAC_WAKEUP_DURATION_US / 2))) || (own_phase > RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_INTERVAL_US - (3 * GNRC_LWMAC_WAKEUP_DURATION_US / 2)))) { gnrc_lwmac_set_phase_backoff(netif, true); LOG_WARNING("WARNING: [LWMAC-tx] phase close\n"); } } /* No need to keep pkt anymore */ gnrc_pktbuf_release(pkt); if (!from_expected_destination) { LOG_DEBUG("[LWMAC-tx] Packet is not from expected destination\n"); break; } /* All checks passed so this must be a valid WA */ gnrc_lwmac_clear_timeout(netif, GNRC_LWMAC_TIMEOUT_WR); found_wa = true; break; } if (postponed) { LOG_INFO("[LWMAC-tx] Destination is talking to another node, postpone\n"); tx_info |= GNRC_LWMAC_TX_FAIL; return tx_info; } if (!found_wa) { LOG_DEBUG("[LWMAC-tx] No WA yet\n"); return tx_info; } /* Save newly calculated phase for destination */ netif->mac.tx.current_neighbor->phase = netif->mac.tx.timestamp; LOG_INFO("[LWMAC-tx] New phase: %" PRIu32 "\n", netif->mac.tx.timestamp); /* We've got our WA, so discard the rest, TODO: no flushing */ gnrc_priority_pktqueue_flush(&netif->mac.rx.queue); tx_info |= GNRC_LWMAC_TX_SUCCESS; return tx_info; }
/* return false if send wa failed, otherwise return true */ static bool _send_wa(gnrc_netdev_t *gnrc_netdev) { gnrc_pktsnip_t *pkt; gnrc_pktsnip_t *pkt_lwmac; gnrc_netif_hdr_t *nethdr_wa; assert(gnrc_netdev != NULL); assert(gnrc_netdev->rx.l2_addr.len != 0); /* if found ongoing transmission, * quit sending WA for collision avoidance. */ if (_gnrc_lwmac_get_netdev_state(gnrc_netdev) == NETOPT_STATE_RX) { gnrc_netdev->rx.rx_bad_exten_count++; return false; } /* Assemble WA packet */ gnrc_lwmac_frame_wa_t lwmac_hdr; lwmac_hdr.header.type = GNRC_LWMAC_FRAMETYPE_WA; lwmac_hdr.dst_addr = gnrc_netdev->rx.l2_addr; uint32_t phase_now = _gnrc_lwmac_phase_now(); /* Embed the current 'relative phase timing' (counted from the start of this cycle) * of the receiver into its WA packet, thus to allow the sender to infer the * receiver's exact wake-up timing */ if (phase_now > _gnrc_lwmac_ticks_to_phase(gnrc_netdev->lwmac.last_wakeup)) { lwmac_hdr.current_phase = (phase_now - _gnrc_lwmac_ticks_to_phase(gnrc_netdev->lwmac.last_wakeup)); } else { lwmac_hdr.current_phase = (phase_now + RTT_US_TO_TICKS(GNRC_LWMAC_WAKEUP_INTERVAL_US)) - _gnrc_lwmac_ticks_to_phase(gnrc_netdev->lwmac.last_wakeup); } pkt = gnrc_pktbuf_add(NULL, &lwmac_hdr, sizeof(lwmac_hdr), GNRC_NETTYPE_LWMAC); if (pkt == NULL) { LOG_ERROR("ERROR: [LWMAC-rx] Cannot allocate pktbuf of type GNRC_NETTYPE_LWMAC\n"); gnrc_netdev_lwmac_set_quit_rx(gnrc_netdev, true); return false; } pkt_lwmac = pkt; pkt = gnrc_pktbuf_add(pkt, NULL, sizeof(gnrc_netif_hdr_t) + gnrc_netdev->rx.l2_addr.len, GNRC_NETTYPE_NETIF); if (pkt == NULL) { LOG_ERROR("ERROR: [LWMAC-rx] Cannot allocate pktbuf of type GNRC_NETTYPE_NETIF\n"); gnrc_pktbuf_release(pkt_lwmac); gnrc_netdev_lwmac_set_quit_rx(gnrc_netdev, true); return false; } /* We wouldn't get here if add the NETIF header had failed, so no sanity checks needed */ nethdr_wa = (gnrc_netif_hdr_t *)(gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_NETIF)->data); /* Construct NETIF header and insert address for WA packet */ gnrc_netif_hdr_init(nethdr_wa, 0, gnrc_netdev->rx.l2_addr.len); /* Send WA as broadcast*/ nethdr_wa->flags |= GNRC_NETIF_HDR_FLAGS_BROADCAST; /* Disable Auto ACK */ netopt_enable_t autoack = NETOPT_DISABLE; gnrc_netdev->dev->driver->set(gnrc_netdev->dev, NETOPT_AUTOACK, &autoack, sizeof(autoack)); /* Send WA */ if (gnrc_netdev->send(gnrc_netdev, pkt) < 0) { LOG_ERROR("ERROR: [LWMAC-rx] Send WA failed."); if (pkt != NULL) { gnrc_pktbuf_release(pkt); } gnrc_netdev_lwmac_set_quit_rx(gnrc_netdev, true); return false; } /* Enable Auto ACK again for data reception */ autoack = NETOPT_ENABLE; gnrc_netdev->dev->driver->set(gnrc_netdev->dev, NETOPT_AUTOACK, &autoack, sizeof(autoack)); return true; }