static void _tx_management(gnrc_netif_t *netif) { gnrc_lwmac_tx_state_t state_tx = netif->mac.tx.state; switch (state_tx) { case GNRC_LWMAC_TX_STATE_STOPPED: { _tx_management_stopped(netif); break; } case GNRC_LWMAC_TX_STATE_FAILED: { /* If transmission failure, do not try burst transmissions and quit other * transmission attempts in this cycle for collision avoidance */ gnrc_lwmac_set_tx_continue(netif, false); gnrc_lwmac_set_quit_tx(netif, true); /* falls through */ /* TX packet will therefore be dropped. No automatic resending here, * we did our best. */ } case GNRC_LWMAC_TX_STATE_SUCCESSFUL: { _tx_management_success(netif); break; } default: gnrc_lwmac_tx_update(netif); } /* If state has changed, reschedule main state machine */ if (state_tx != netif->mac.tx.state) { lwmac_schedule_update(netif); } }
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); } }
/* 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; }