/* 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 _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; }
/* 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; }
/* 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; }