예제 #1
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;
}
예제 #2
0
/* 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;
}