Example #1
0
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;
    }
}
Example #2
0
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;
}
Example #3
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);
    }
}
Example #4
0
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);
}
Example #5
0
/* 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;
}
Example #6
0
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;
}
Example #7
0
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;
}
Example #8
0
/* 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;
}
Example #9
0
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;
}