static bool _wait_preamble_ack_preambleack(gnrc_netif_t *netif, gnrc_gomach_packet_info_t *info, gnrc_pktsnip_t *pkt) { if ((memcmp(&netif->l2addr, &info->dst_addr.addr, netif->l2addr_len) == 0) && (memcmp(&netif->mac.tx.current_neighbor->l2_addr, &info->src_addr.addr, netif->mac.tx.current_neighbor->l2_addr_len) == 0)) { /* Got preamble-ACK from targeted device. */ gnrc_gomach_set_got_preamble_ack(netif, true); /* Analyze the preamble-ACK to get phase-locked with the neighbor device. */ gnrc_gomach_process_preamble_ack(netif, pkt); gnrc_pktbuf_release(pkt); gnrc_priority_pktqueue_flush(&netif->mac.rx.queue); return false; } /* Preamble-ACK is not from targeted device. release it. */ gnrc_pktbuf_release(pkt); return true; }
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; }
static uint8_t _send_wr(gnrc_netif_t *netif) { assert(netif != NULL); uint8_t tx_info = 0; gnrc_pktsnip_t *pkt; gnrc_pktsnip_t *pkt_lwmac; gnrc_netif_hdr_t *nethdr; /* 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) { 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; } /* Assemble WR */ gnrc_lwmac_frame_wr_t wr_hdr; wr_hdr.header.type = GNRC_LWMAC_FRAMETYPE_WR; memcpy(&(wr_hdr.dst_addr.addr), netif->mac.tx.current_neighbor->l2_addr, netif->mac.tx.current_neighbor->l2_addr_len); wr_hdr.dst_addr.len = netif->mac.tx.current_neighbor->l2_addr_len; pkt = gnrc_pktbuf_add(NULL, &wr_hdr, sizeof(wr_hdr), GNRC_NETTYPE_LWMAC); if (pkt == NULL) { LOG_ERROR("ERROR: [LWMAC-tx] Cannot allocate pktbuf of type GNRC_NETTYPE_LWMAC\n"); gnrc_pktbuf_release(netif->mac.tx.packet); LOG_ERROR("ERROR: [LWMAC-tx] Memory maybe full, drop the data packet\n"); /* clear packet point to avoid TX retry */ netif->mac.tx.packet = NULL; tx_info |= GNRC_LWMAC_TX_FAIL; return tx_info; } /* track the location of this lwmac_frame_wr_t header */ pkt_lwmac = pkt; pkt = gnrc_pktbuf_add(pkt, NULL, sizeof(gnrc_netif_hdr_t), GNRC_NETTYPE_NETIF); if (pkt == NULL) { LOG_ERROR("ERROR: [LWMAC-tx] Cannot allocate pktbuf of type GNRC_NETTYPE_NETIF\n"); gnrc_pktbuf_release(pkt_lwmac); LOG_ERROR("ERROR: [LWMAC-tx] Memory maybe full, drop the data 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; } /* We wouldn't get here if adding the NETIF header had failed, so no * sanity checks needed */ nethdr = (gnrc_netif_hdr_t *) (gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_NETIF))->data; /* Construct NETIF header and insert address for WR packet */ gnrc_netif_hdr_init(nethdr, 0, 0); /* Send WR as broadcast*/ nethdr->flags |= GNRC_NETIF_HDR_FLAGS_BROADCAST; /* Disable Auto ACK */ netopt_enable_t autoack = NETOPT_DISABLE; netif->dev->driver->set(netif->dev, NETOPT_AUTOACK, &autoack, sizeof(autoack)); /* Prepare WR, this will discard any frame in the transceiver that has * possibly arrived in the meantime but we don't care at this point. */ int res = _gnrc_lwmac_transmit(netif, pkt); if (res < 0) { LOG_ERROR("ERROR: [LWMAC-tx] Send WR failed."); if (pkt != NULL) { gnrc_pktbuf_release(pkt); } tx_info |= GNRC_LWMAC_TX_FAIL; return tx_info; } gnrc_priority_pktqueue_flush(&netif->mac.rx.queue); 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; }