void gnrc_lwmac_tx_stop(gnrc_netif_t *netif) { assert(netif != NULL); gnrc_lwmac_clear_timeout(netif, GNRC_LWMAC_TIMEOUT_WR); gnrc_lwmac_clear_timeout(netif, GNRC_LWMAC_TIMEOUT_NO_RESPONSE); gnrc_lwmac_clear_timeout(netif, GNRC_LWMAC_TIMEOUT_NEXT_BROADCAST); gnrc_lwmac_clear_timeout(netif, GNRC_LWMAC_TIMEOUT_BROADCAST_END); netif->mac.tx.state = GNRC_LWMAC_TX_STATE_STOPPED; /* Release packet in case of failure */ if (netif->mac.tx.packet) { if (netif->mac.tx.tx_retry_count >= GNRC_LWMAC_MAX_DATA_TX_RETRIES) { netif->mac.tx.tx_retry_count = 0; gnrc_pktbuf_release(netif->mac.tx.packet); netif->mac.tx.packet = NULL; LOG_WARNING("WARNING: [LWMAC-tx] Drop TX packet\n"); } else { netif->mac.tx.tx_retry_count++; return; } } if (!gnrc_lwmac_get_tx_continue(netif)) { netif->mac.tx.current_neighbor = NULL; } }
void gnrc_lwmac_rx_stop(gnrc_netdev_t *gnrc_netdev) { if (!gnrc_netdev) { return; } gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA); gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_STOPPED; gnrc_netdev->rx.l2_addr.len = 0; }
static void _lwmac_update_listening(gnrc_netif_t *netif) { /* In case has pending packet to send, clear rtt alarm thus to goto * transmission initialization (in SLEEPING management) right after the * listening period */ if ((_next_tx_neighbor(netif) != NULL) || (netif->mac.tx.current_neighbor != NULL)) { rtt_handler(GNRC_LWMAC_EVENT_RTT_PAUSE, netif); } /* Set timeout for if there's no successful rx transaction that will * change state to SLEEPING. */ if (!gnrc_lwmac_timeout_is_running(netif, GNRC_LWMAC_TIMEOUT_WAKEUP_PERIOD)) { gnrc_lwmac_set_timeout(netif, GNRC_LWMAC_TIMEOUT_WAKEUP_PERIOD, GNRC_LWMAC_WAKEUP_DURATION_US); } else if (gnrc_lwmac_timeout_is_expired(netif, GNRC_LWMAC_TIMEOUT_WAKEUP_PERIOD)) { /* Dispatch first as there still may be broadcast packets. */ gnrc_mac_dispatch(&netif->mac.rx); netif->mac.lwmac.state = GNRC_LWMAC_SLEEPING; /* Enable duty cycling again */ rtt_handler(GNRC_LWMAC_EVENT_RTT_RESUME, netif); _gnrc_lwmac_set_netdev_state(netif, NETOPT_STATE_SLEEP); gnrc_lwmac_clear_timeout(netif, GNRC_LWMAC_TIMEOUT_WAKEUP_PERIOD); /* if there is a packet for transmission, schedule update to start * transmission initialization immediately. */ gnrc_mac_tx_neighbor_t *neighbour = _next_tx_neighbor(netif); if ((neighbour != NULL) || (netif->mac.tx.current_neighbor != NULL)) { /* This triggers packet sending procedure in sleeping immediately. */ lwmac_schedule_update(netif); return; } } if (gnrc_priority_pktqueue_length(&netif->mac.rx.queue) > 0) { /* Do wake-up extension in each packet reception. */ gnrc_lwmac_clear_timeout(netif, GNRC_LWMAC_TIMEOUT_WAKEUP_PERIOD); lwmac_set_state(netif, GNRC_LWMAC_RECEIVING); } }
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); }
/* Returns whether rescheduling is needed or not */ static bool _lwmac_tx_update(gnrc_netif_t *netif) { assert(netif != NULL); bool reschedule = false; switch (netif->mac.tx.state) { case GNRC_LWMAC_TX_STATE_INIT: { gnrc_lwmac_clear_timeout(netif, GNRC_LWMAC_TIMEOUT_WR); gnrc_lwmac_clear_timeout(netif, GNRC_LWMAC_TIMEOUT_NO_RESPONSE); gnrc_lwmac_clear_timeout(netif, GNRC_LWMAC_TIMEOUT_NEXT_BROADCAST); gnrc_lwmac_clear_timeout(netif, GNRC_LWMAC_TIMEOUT_BROADCAST_END); /* if found ongoing transmission, * quit this cycle for collision avoidance. */ if (_gnrc_lwmac_get_netdev_state(netif) == NETOPT_STATE_RX) { 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; netif->mac.tx.state = GNRC_LWMAC_TX_STATE_FAILED; reschedule = true; break; } /* check if the packet is for broadcast */ if (gnrc_netif_hdr_get_flag(netif->mac.tx.packet) & (GNRC_NETIF_HDR_FLAGS_BROADCAST | GNRC_NETIF_HDR_FLAGS_MULTICAST)) { /* Set CSMA retries as configured and enable */ uint8_t csma_retries = GNRC_LWMAC_BROADCAST_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)); netif->mac.tx.state = GNRC_LWMAC_TX_STATE_SEND_BROADCAST; reschedule = true; break; } else { /* Use CSMA for the first WR */ netif->mac.mac_info |= GNRC_NETIF_MAC_INFO_CSMA_ENABLED; netopt_enable_t csma_disable = NETOPT_ENABLE; netif->dev->driver->set(netif->dev, NETOPT_CSMA, &csma_disable, sizeof(csma_disable)); /* Set a timeout for the maximum transmission procedure */ gnrc_lwmac_set_timeout(netif, GNRC_LWMAC_TIMEOUT_NO_RESPONSE, GNRC_LWMAC_PREAMBLE_DURATION_US); netif->mac.tx.state = GNRC_LWMAC_TX_STATE_SEND_WR; reschedule = true; break; } } case GNRC_LWMAC_TX_STATE_SEND_BROADCAST: { uint8_t tx_info = _send_bcast(netif); if (tx_info & GNRC_LWMAC_TX_SUCCESS) { netif->mac.tx.state = GNRC_LWMAC_TX_STATE_SUCCESSFUL; reschedule = true; break; } if (tx_info & GNRC_LWMAC_TX_FAIL) { netif->mac.tx.state = GNRC_LWMAC_TX_STATE_FAILED; reschedule = true; break; } break; } case GNRC_LWMAC_TX_STATE_SEND_WR: { /* In case of no Tx-isr error (e.g., no Tx-isr), goto TX failure. */ if (gnrc_lwmac_timeout_is_expired(netif, GNRC_LWMAC_TIMEOUT_NO_RESPONSE)) { LOG_WARNING("WARNING: [LWMAC-tx] No response from destination, " "probably no TX-ISR\n"); netif->mac.tx.state = GNRC_LWMAC_TX_STATE_FAILED; reschedule = true; break; } LOG_DEBUG("[LWMAC-tx] GNRC_LWMAC_TX_STATE_SEND_WR\n"); uint8_t tx_info = _send_wr(netif); if (tx_info & GNRC_LWMAC_TX_FAIL) { netif->mac.tx.state = GNRC_LWMAC_TX_STATE_FAILED; reschedule = true; break; } netif->mac.tx.state = GNRC_LWMAC_TX_STATE_WAIT_WR_SENT; reschedule = false; break; } case GNRC_LWMAC_TX_STATE_WAIT_WR_SENT: { LOG_DEBUG("[LWMAC-tx] GNRC_LWMAC_TX_STATE_WAIT_WR_SENT\n"); /* In case of no Tx-isr error (e.g., no Tx-isr), goto TX failure. */ if (gnrc_lwmac_timeout_is_expired(netif, GNRC_LWMAC_TIMEOUT_NO_RESPONSE)) { LOG_WARNING("WARNING: [LWMAC-tx] No response from destination\n"); netif->mac.tx.state = GNRC_LWMAC_TX_STATE_FAILED; reschedule = true; break; } if (gnrc_netif_get_tx_feedback(netif) == TX_FEEDBACK_UNDEF) { LOG_DEBUG("[LWMAC-tx] WR not yet completely sent\n"); break; } /* If found ongoing transmission, goto TX failure, i.e., postpone transmission to * next cycle. This is mainly for collision avoidance. */ if (gnrc_netif_get_tx_feedback(netif) == TX_FEEDBACK_BUSY) { 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"); } /* clear packet point to avoid TX retry */ netif->mac.tx.packet = NULL; netif->mac.tx.state = GNRC_LWMAC_TX_STATE_FAILED; reschedule = true; break; } if (netif->mac.tx.wr_sent == 0) { /* Only the first WR use CSMA */ netif->mac.mac_info &= ~GNRC_NETIF_MAC_INFO_CSMA_ENABLED; netopt_enable_t csma_disable = NETOPT_DISABLE; netif->dev->driver->set(netif->dev, NETOPT_CSMA, &csma_disable, sizeof(csma_disable)); } netif->mac.tx.wr_sent++; /* Set timeout for next WR in case no WA will be received */ gnrc_lwmac_set_timeout(netif, GNRC_LWMAC_TIMEOUT_WR, GNRC_LWMAC_TIME_BETWEEN_WR_US); /* Debug WR timing */ LOG_DEBUG("[LWMAC-tx] Destination phase was: %" PRIu32 "\n", netif->mac.tx.current_neighbor->phase); LOG_DEBUG("[LWMAC-tx] Phase when sent was: %" PRIu32 "\n", _gnrc_lwmac_ticks_to_phase(netif->mac.tx.timestamp)); LOG_DEBUG("[LWMAC-tx] Ticks when sent was: %" PRIu32 "\n", netif->mac.tx.timestamp); _gnrc_lwmac_set_netdev_state(netif, NETOPT_STATE_IDLE); netif->mac.tx.state = GNRC_LWMAC_TX_STATE_WAIT_FOR_WA; reschedule = false; break; } case GNRC_LWMAC_TX_STATE_WAIT_FOR_WA: { LOG_DEBUG("[LWMAC-tx] GNRC_LWMAC_TX_STATE_WAIT_FOR_WA\n"); if (gnrc_lwmac_timeout_is_expired(netif, GNRC_LWMAC_TIMEOUT_NO_RESPONSE)) { LOG_WARNING("WARNING: [LWMAC-tx] No response from destination\n"); netif->mac.tx.state = GNRC_LWMAC_TX_STATE_FAILED; reschedule = true; break; } if (gnrc_lwmac_timeout_is_expired(netif, GNRC_LWMAC_TIMEOUT_WR)) { /* In case the sender is in consecutive (burst) transmission to the receiver, * meaning that the sender has already successfully sent at least one data to * the receiver, then the sender will only spend one WR for triggering the next * transmission procedure. And, if this WR doesn't work (no WA replied), the * sender regards consecutive transmission failed. */ if (gnrc_lwmac_get_tx_continue(netif)) { LOG_DEBUG("[LWMAC-tx] Tx burst fail\n"); 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; netif->mac.tx.state = GNRC_LWMAC_TX_STATE_FAILED; reschedule = true; break; } else { /* If this is the first transmission to the receiver for locating the * latter's wake-up period, the sender just keep sending WRs until it * finds the WA. */ netif->mac.tx.state = GNRC_LWMAC_TX_STATE_SEND_WR; reschedule = true; break; } } if (_gnrc_lwmac_get_netdev_state(netif) == NETOPT_STATE_RX) { /* Wait for completion of frame reception */ break; } uint8_t tx_info = _packet_process_in_wait_for_wa(netif); if (tx_info & GNRC_LWMAC_TX_FAIL) { netif->mac.tx.state = GNRC_LWMAC_TX_STATE_FAILED; reschedule = true; break; } if (tx_info & GNRC_LWMAC_TX_SUCCESS) { netif->mac.tx.state = GNRC_LWMAC_TX_STATE_SEND_DATA; reschedule = true; break; } else { /* No WA yet */ break; } } case GNRC_LWMAC_TX_STATE_SEND_DATA: { LOG_DEBUG("[LWMAC-tx] GNRC_LWMAC_TX_STATE_SEND_DATA\n"); if (!_send_data(netif)) { netif->mac.tx.state = GNRC_LWMAC_TX_STATE_FAILED; reschedule = true; break; } netif->mac.tx.state = GNRC_LWMAC_TX_STATE_WAIT_FEEDBACK; reschedule = false; break; } case GNRC_LWMAC_TX_STATE_WAIT_FEEDBACK: { /* In case of no Tx-isr error, goto TX failure. */ if (gnrc_lwmac_timeout_is_expired(netif, GNRC_LWMAC_TIMEOUT_NO_RESPONSE)) { netif->mac.tx.state = GNRC_LWMAC_TX_STATE_FAILED; reschedule = true; break; } LOG_DEBUG("[LWMAC-tx] GNRC_LWMAC_TX_STATE_WAIT_FEEDBACK\n"); if (gnrc_netif_get_tx_feedback(netif) == TX_FEEDBACK_UNDEF) { break; } else if (gnrc_netif_get_tx_feedback(netif) == TX_FEEDBACK_SUCCESS) { netif->mac.tx.state = GNRC_LWMAC_TX_STATE_SUCCESSFUL; reschedule = true; break; } else if (gnrc_netif_get_tx_feedback(netif) == TX_FEEDBACK_NOACK) { LOG_ERROR("ERROR: [LWMAC-tx] Not ACKED\n"); netif->mac.tx.state = GNRC_LWMAC_TX_STATE_FAILED; reschedule = true; break; } else if (gnrc_netif_get_tx_feedback(netif) == TX_FEEDBACK_BUSY) { LOG_ERROR("ERROR: [LWMAC-tx] Channel busy \n"); netif->mac.tx.state = GNRC_LWMAC_TX_STATE_FAILED; reschedule = true; break; } LOG_ERROR("ERROR: [LWMAC-tx] Tx feedback unhandled: %i\n", gnrc_netif_get_tx_feedback(netif)); netif->mac.tx.state = GNRC_LWMAC_TX_STATE_FAILED; reschedule = true; break; } case GNRC_LWMAC_TX_STATE_SUCCESSFUL: case GNRC_LWMAC_TX_STATE_FAILED: { break; } case GNRC_LWMAC_TX_STATE_STOPPED: { LOG_DEBUG("[LWMAC-tx] Transmission state machine is stopped\n"); } } return reschedule; }
static uint8_t _send_bcast(gnrc_netif_t *netif) { assert(netif != NULL); uint8_t tx_info = 0; gnrc_pktsnip_t *pkt = netif->mac.tx.packet; bool first = false; if (gnrc_lwmac_timeout_is_running(netif, GNRC_LWMAC_TIMEOUT_BROADCAST_END)) { if (gnrc_lwmac_timeout_is_expired(netif, GNRC_LWMAC_TIMEOUT_BROADCAST_END)) { gnrc_lwmac_clear_timeout(netif, GNRC_LWMAC_TIMEOUT_NEXT_BROADCAST); gnrc_pktbuf_release(pkt); netif->mac.tx.packet = NULL; tx_info |= GNRC_LWMAC_TX_SUCCESS; return tx_info; } } else { LOG_INFO("[LWMAC-tx] Initialize broadcasting\n"); gnrc_lwmac_set_timeout(netif, GNRC_LWMAC_TIMEOUT_BROADCAST_END, GNRC_LWMAC_BROADCAST_DURATION_US); gnrc_pktsnip_t *pkt_payload; /* Prepare packet with LWMAC header*/ gnrc_lwmac_frame_broadcast_t hdr; hdr.header.type = GNRC_LWMAC_FRAMETYPE_BROADCAST; hdr.seq_nr = netif->mac.tx.bcast_seqnr++; pkt_payload = pkt->next; 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 FRAMETYPE_BROADCAST\n"); netif->mac.tx.packet->next = pkt_payload; /* Drop the broadcast packet */ LOG_ERROR("ERROR: [LWMAC-tx] Memory maybe full, drop the broadcast packet\n"); gnrc_pktbuf_release(netif->mac.tx.packet); /* clear packet point to avoid TX retry */ netif->mac.tx.packet = NULL; tx_info |= GNRC_LWMAC_TX_FAIL; return tx_info; } /* No Auto-ACK for broadcast packets */ netopt_enable_t autoack = NETOPT_DISABLE; netif->dev->driver->set(netif->dev, NETOPT_AUTOACK, &autoack, sizeof(autoack)); first = true; } if (gnrc_lwmac_timeout_is_expired(netif, GNRC_LWMAC_TIMEOUT_NEXT_BROADCAST) || first) { /* if found ongoing transmission, quit this cycle for collision avoidance. * Broadcast 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; tx_info |= GNRC_LWMAC_TX_FAIL; return tx_info; } /* Don't let the packet be released yet, we want to send it again */ gnrc_pktbuf_hold(pkt, 1); int res = _gnrc_lwmac_transmit(netif, pkt); if (res < 0) { LOG_ERROR("ERROR: [LWMAC-tx] Send broadcast pkt failed."); tx_info |= GNRC_LWMAC_TX_FAIL; return tx_info; } gnrc_lwmac_set_timeout(netif, GNRC_LWMAC_TIMEOUT_NEXT_BROADCAST, GNRC_LWMAC_TIME_BETWEEN_BROADCAST_US); LOG_INFO("[LWMAC-tx] Broadcast sent\n"); } return tx_info; }
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; }
/* Returns whether rescheduling is needed or not */ static bool _lwmac_rx_update(gnrc_netdev_t *gnrc_netdev) { bool reschedule = false; if (!gnrc_netdev) { return reschedule; } switch (gnrc_netdev->rx.state) { case GNRC_LWMAC_RX_STATE_INIT: { gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA); gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_WAIT_FOR_WR; reschedule = true; break; } case GNRC_LWMAC_RX_STATE_WAIT_FOR_WR: { LOG_DEBUG("[LWMAC-rx] GNRC_LWMAC_RX_STATE_WAIT_FOR_WR\n"); uint8_t rx_info = _packet_process_in_wait_for_wr(gnrc_netdev); /* if found broadcast packet, goto rx successful */ if (rx_info & GNRC_LWMAC_RX_FOUND_BROADCAST) { gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_SUCCESSFUL; reschedule = true; break; } if (!(rx_info & GNRC_LWMAC_RX_FOUND_WR)) { LOG_DEBUG("[LWMAC-rx] No WR found, stop RX\n"); gnrc_netdev->rx.rx_bad_exten_count++; gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_FAILED; reschedule = true; break; } gnrc_priority_pktqueue_flush(&gnrc_netdev->rx.queue); /* Found WR packet (preamble), goto next state to send WA (preamble-ACK) */ gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_SEND_WA; reschedule = true; break; } case GNRC_LWMAC_RX_STATE_SEND_WA: { LOG_DEBUG("[LWMAC-rx] GNRC_LWMAC_RX_STATE_SEND_WA\n"); if (!_send_wa(gnrc_netdev)) { gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_FAILED; reschedule = true; break; } gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_WAIT_WA_SENT; reschedule = false; break; } case GNRC_LWMAC_RX_STATE_WAIT_WA_SENT: { LOG_DEBUG("[LWMAC-rx] GNRC_LWMAC_RX_STATE_WAIT_WA_SENT\n"); if (gnrc_netdev_get_tx_feedback(gnrc_netdev) == TX_FEEDBACK_UNDEF) { LOG_DEBUG("[LWMAC-rx] WA not yet completely sent\n"); break; } /* When reach here, WA has been sent, set timeout for expected data arrival */ gnrc_lwmac_set_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA, GNRC_LWMAC_DATA_DELAY_US); _gnrc_lwmac_set_netdev_state(gnrc_netdev, NETOPT_STATE_IDLE); gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_WAIT_FOR_DATA; reschedule = false; break; } case GNRC_LWMAC_RX_STATE_WAIT_FOR_DATA: { LOG_DEBUG("[LWMAC-rx] GNRC_LWMAC_RX_STATE_WAIT_FOR_DATA\n"); uint8_t rx_info = _packet_process_in_wait_for_data(gnrc_netdev); /* If WA got lost we wait for data but we will be hammered with WR * packets. So a WR indicates a lost WA => reset RX state machine. */ if (rx_info & GNRC_LWMAC_RX_FOUND_WR) { LOG_INFO("[LWMAC-rx] WA probably got lost, reset RX state machine\n"); /* Start over again */ gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_INIT; reschedule = true; break; } /* Only timeout if no packet (presumably the expected data) is being * received. This won't be blocked by WRs as they restart the state * machine (see above). */ if (gnrc_lwmac_timeout_is_expired(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA)) { if (!gnrc_netdev_get_rx_started(gnrc_netdev)) { LOG_INFO("[LWMAC-rx] DATA timed out\n"); gnrc_netdev->rx.rx_bad_exten_count++; gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_FAILED; reschedule = true; } else { /* If radio is receiving packet, reset wait data timeout */ gnrc_lwmac_set_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA, GNRC_LWMAC_DATA_DELAY_US); } break; } if (!(rx_info & GNRC_LWMAC_RX_FOUND_DATA)) { LOG_DEBUG("[LWMAC-rx] No DATA yet\n"); break; } gnrc_netdev->rx.state = GNRC_LWMAC_RX_STATE_SUCCESSFUL; reschedule = true; break; } case GNRC_LWMAC_RX_STATE_SUCCESSFUL: case GNRC_LWMAC_RX_STATE_FAILED: { break; } case GNRC_LWMAC_RX_STATE_STOPPED: { LOG_DEBUG("[LWMAC-rx] Reception state machine is stopped\n"); } } return reschedule; }
static uint8_t _packet_process_in_wait_for_data(gnrc_netdev_t *gnrc_netdev) { uint8_t rx_info = 0; gnrc_pktsnip_t *pkt; assert(gnrc_netdev != NULL); pkt = NULL; while ((pkt = gnrc_priority_pktqueue_pop(&gnrc_netdev->rx.queue)) != NULL) { LOG_DEBUG("[LWMAC-rx] Inspecting pkt @ %p\n", pkt); /* Parse packet */ gnrc_lwmac_packet_info_t info; if (_gnrc_lwmac_parse_packet(pkt, &info) != 0) { LOG_DEBUG("[LWMAC-rx] Packet could not be parsed\n"); gnrc_pktbuf_release(pkt); continue; } if (info.header->type == GNRC_LWMAC_FRAMETYPE_BROADCAST) { _gnrc_lwmac_dispatch_defer(gnrc_netdev->rx.dispatch_buffer, pkt); gnrc_mac_dispatch(&gnrc_netdev->rx); /* quit listening period to avoid receiving duplicate broadcast packets */ gnrc_netdev_lwmac_set_quit_rx(gnrc_netdev, true); continue; } if (!(memcmp(&info.src_addr.addr, &gnrc_netdev->rx.l2_addr.addr, gnrc_netdev->rx.l2_addr.len) == 0)) { LOG_DEBUG("[LWMAC-rx] Packet is not from destination\n"); gnrc_pktbuf_release(pkt); /* Reset timeout to wait for the data packet */ gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA); gnrc_lwmac_set_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA, GNRC_LWMAC_DATA_DELAY_US); continue; } if (!(memcmp(&info.dst_addr.addr, &gnrc_netdev->l2_addr, gnrc_netdev->l2_addr_len) == 0)) { LOG_DEBUG("[LWMAC-rx] Packet is not for us\n"); gnrc_pktbuf_release(pkt); /* Reset timeout to wait for the data packet */ gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA); gnrc_lwmac_set_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA, GNRC_LWMAC_DATA_DELAY_US); continue; } /* Sender maybe didn't get the WA */ if (info.header->type == GNRC_LWMAC_FRAMETYPE_WR) { LOG_DEBUG("[LWMAC-rx] Found a WR while waiting for DATA\n"); gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA); rx_info |= GNRC_LWMAC_RX_FOUND_WR; /* Push WR back to rx queue */ gnrc_mac_queue_rx_packet(&gnrc_netdev->rx, 0, pkt); break; } switch (info.header->type) { case GNRC_LWMAC_FRAMETYPE_DATA: case GNRC_LWMAC_FRAMETYPE_DATA_PENDING: { /* Receiver gets the data packet */ _gnrc_lwmac_dispatch_defer(gnrc_netdev->rx.dispatch_buffer, pkt); gnrc_mac_dispatch(&gnrc_netdev->rx); LOG_DEBUG("[LWMAC-rx] Found DATA!\n"); gnrc_lwmac_clear_timeout(gnrc_netdev, GNRC_LWMAC_TIMEOUT_DATA); rx_info |= GNRC_LWMAC_RX_FOUND_DATA; return rx_info; } default: { gnrc_pktbuf_release(pkt); } } } return rx_info; }