/**
 * @file tal_tx.c
 *
 * @brief This file handles the frame transmission within the TAL.
 *
 * $Id: tal_tx.c 23383 2010-09-09 10:52:19Z sschneid $
 *
 * @author    Atmel Corporation: http://www.atmel.com
 * @author    Support email: avr@atmel.com
 */
/*
 * Copyright (c) 2009, Atmel Corporation All rights reserved.
 *
 * Licensed under Atmel's Limited License Agreement --> EULA.txt
 */

/* === INCLUDES ============================================================ */

#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include "pal.h"
#include "return_val.h"
#include "tal.h"
#include "ieee_const.h"
#include "tal_pib.h"
#include "tal_irq_handler.h"
#include "tal_constants.h"
#include "tal_tx.h"
#include "stack_config.h"
#include "bmm.h"
#include "qmm.h"
#include "tal_rx.h"
#include "tal_internal.h"
#include "atmega128rfa1.h"
#ifdef BEACON_SUPPORT
#include "tal_slotted_csma.h"
#endif  /* BEACON_SUPPORT */
#include "mac_build_config.h"

/* === TYPES =============================================================== */


/* === MACROS ============================================================== */


/* === GLOBALS ============================================================= */

static trx_trac_status_t trx_trac_status;

/* === PROTOTYPES ========================================================== */


/* === IMPLEMENTATION ====================================================== */

/**
 * @brief Requests to TAL to transmit frame
 *
 * This function is called by the MAC to deliver a frame to the TAL
 * to be transmitted by the transceiver.
 *
 * @param tx_frame Pointer to the frame_info_t structure updated by the MAC layer
 * @param csma_mode Indicates mode of csma-ca to be performed for this frame
 * @param perform_frame_retry Indicates whether to retries are to be performed for
 *                            this frame
 *
 * @return MAC_SUCCESS  if the TAL has accepted the data from the MAC for frame
 *                 transmission
 *         TAL_BUSY if the TAL is busy servicing the previous MAC request
 */
retval_t tal_tx_frame(frame_info_t *tx_frame,
                      csma_mode_t csma_mode,
                      bool perform_frame_retry)
{
    if (tal_state != TAL_IDLE)
    {
        return TAL_BUSY;
    }

    /*
     * Store the pointer to the provided frame structure.
     * This is needed for the callback function.
     */
    mac_frame_ptr = tx_frame;

    /* Set pointer to actual mpdu to be downloaded to the transceiver. */
    tal_frame_to_tx = tx_frame->mpdu;

    /*
     * In case the frame is too large, return immediately indicating
     * invalid status.
     */
    if (tal_frame_to_tx == NULL)
    {
        return MAC_INVALID_PARAMETER;
    }

#ifdef BEACON_SUPPORT
    // check if beacon mode is used
    if (csma_mode == CSMA_SLOTTED)
    {
        if (!slotted_csma_start(perform_frame_retry))
        {
            return MAC_CHANNEL_ACCESS_FAILURE;
        }
    }
    else
    {
#if (MAC_INDIRECT_DATA_FFD == 1)
        /*
         * Check if frame is using indirect transmission, but do not use the
         * indirect_in_transit flag. This flag is not set for null data frames.
         */
        if ((tal_pib_BeaconOrder < NON_BEACON_NWK) && (csma_mode == NO_CSMA_WITH_IFS) && (perform_frame_retry == false))
         {
            /*
             * Check if indirect transmission can be completed before the next
             * beacon transmission.
             */
            uint32_t time_between_beacons_sym;
            uint32_t next_beacon_time_sym;
            uint32_t now_time_sym;
            uint32_t duration_before_beacon_sym;

            /* Calculate the entire transaction duration. Re-use function of slotted CSMA.
             * The additional two backoff periods used for CCA are kept as a guard time.
             */
            calculate_transaction_duration();

            /* Calculate the duration until the next beacon needs to be transmitted. */
            time_between_beacons_sym = TAL_GET_BEACON_INTERVAL_TIME(tal_pib_BeaconOrder);
            next_beacon_time_sym = tal_add_time_symbols(tal_pib_BeaconTxTime,
                                                            time_between_beacons_sym);
            pal_get_current_time(&now_time_sym);
            now_time_sym = TAL_CONVERT_US_TO_SYMBOLS(now_time_sym);
            duration_before_beacon_sym = tal_sub_time_symbols(next_beacon_time_sym, now_time_sym);

            /* Check if transaction can be completed before next beacon transmission. */
            if ((now_time_sym >= next_beacon_time_sym) ||
                ((transaction_duration_periods * aUnitBackoffPeriod) > duration_before_beacon_sym))
            {
                /*
                 * Transaction will not be completed before next beacon transmission.
                 * Therefore the transmission is not executed.
                 */
                 return MAC_CHANNEL_ACCESS_FAILURE;
            }
        }
#endif  /* #if (MAC_INDIRECT_DATA_FFD == 1) */
        send_frame(tal_frame_to_tx, csma_mode, perform_frame_retry);
    }
#else   /* No BEACON_SUPPORT */
    send_frame(tal_frame_to_tx, csma_mode, perform_frame_retry);
#endif  /* BEACON_SUPPORT / No BEACON_SUPPORT */

    return MAC_SUCCESS;
}



/**
 * @brief Implements the handling of the transmission end.
 *
 * This function handles the callback for the transmission end.
 */
void tx_done_handling(void)
{
    tal_state = TAL_IDLE;

#if (defined BEACON_SUPPORT) || (defined ENABLE_TSTAMP)
    /*
     * The entire timestamp calculation is only required for beaconing networks
     * or if timestamping is explicitly enabled.
     */

    /* Calculate negative offset of timestamp */
    uint16_t offset;

    /* Calcuated the tx time */
    offset =
          TAL_CONVERT_SYMBOLS_TO_US((PHY_OVERHEAD + LENGTH_FIELD_LEN) * SYMBOLS_PER_OCTET)
        + TAL_PSDU_US_PER_OCTET(*tal_frame_to_tx + FCS_LEN)
        + IRQ_PROCESSING_DLY_US;
    if (mac_frame_ptr->mpdu[PL_POS_FCF_1] & FCF_ACK_REQUEST)
    {
        /* Tx timestamp needs to be reduced by ACK duration etc. */
        offset += TAL_CONVERT_SYMBOLS_TO_US((PHY_OVERHEAD + LENGTH_FIELD_LEN) * SYMBOLS_PER_OCTET) +
                  TAL_PSDU_US_PER_OCTET(ACK_PAYLOAD_LEN + FCS_LEN);

#ifdef HIGH_DATA_RATE_SUPPORT
        if (tal_pib_CurrentPage == 0)
        {
            offset += TAL_CONVERT_SYMBOLS_TO_US(aTurnaroundTime);

        }
        else
        {
            offset += 32;
        }
#else
        offset += TAL_CONVERT_SYMBOLS_TO_US(aTurnaroundTime);
#endif  /* #ifdef HIGH_DATA_RATE_SUPPORT */
    }
    mac_frame_ptr->time_stamp -= offset;
#endif  /* #if (defined BEACON_SUPPORT) || (defined ENABLE_TSTAMP) */

    switch (trx_trac_status)
    {
        case TRAC_SUCCESS:
            tal_tx_frame_done_cb(MAC_SUCCESS, mac_frame_ptr);
            break;

        case TRAC_SUCCESS_DATA_PENDING:
            tal_tx_frame_done_cb(TAL_FRAME_PENDING, mac_frame_ptr);
            break;

        case TRAC_CHANNEL_ACCESS_FAILURE:
            tal_tx_frame_done_cb(MAC_CHANNEL_ACCESS_FAILURE, mac_frame_ptr);
            break;

        case TRAC_NO_ACK:
            tal_tx_frame_done_cb(MAC_NO_ACK, mac_frame_ptr);
            break;

        case TRAC_INVALID:
            tal_tx_frame_done_cb(FAILURE, mac_frame_ptr);
            break;

        default:
            ASSERT("Unexpected tal_tx_state" == 0);
            tal_tx_frame_done_cb(FAILURE, mac_frame_ptr);
            break;
    }
} /* tx_done_handling() */



/**
 * @brief Sends frame
 *
 * @param frame_tx Pointer to prepared frame
 * @param use_csma Flag indicating if CSMA is requested
 * @param tx_retries Flag indicating if transmission retries are requested
 *                   by the MAC layer
 */
void send_frame(uint8_t *frame_tx, csma_mode_t csma_mode, bool tx_retries)
{
    tal_trx_status_t trx_status;

    // configure tx according to tx_retries
    if (tx_retries)
    {
        pal_trx_bit_write(SR_MAX_FRAME_RETRIES, tal_pib_MaxFrameRetries);
    }
    else
    {
        pal_trx_bit_write(SR_MAX_FRAME_RETRIES, 0);
    }

    // configure tx according to csma usage
    if ((csma_mode == NO_CSMA_NO_IFS) || (csma_mode == NO_CSMA_WITH_IFS))
    {
        if (tx_retries)
        {
            pal_trx_bit_write(SR_MAX_CSMA_RETRIES, tal_pib_MaxCSMABackoffs);
            pal_trx_reg_write(RG_CSMA_BE, 0x00);
        }
        else
        {
            pal_trx_bit_write(SR_MAX_CSMA_RETRIES, 7);
        }
    }
    else
    {
        pal_trx_reg_write(RG_CSMA_BE, ((tal_pib_MaxBE << 4) | tal_pib_MinBE));
        pal_trx_bit_write(SR_MAX_CSMA_RETRIES, tal_pib_MaxCSMABackoffs);
    }

    do
    {
        trx_status = set_trx_state(CMD_TX_ARET_ON);
    } while (trx_status != TX_ARET_ON);

    /* Handle interframe spacing */
    if (csma_mode == NO_CSMA_WITH_IFS)
    {
        if (last_frame_length > aMaxSIFSFrameSize)
        {
            pal_timer_delay(TAL_CONVERT_SYMBOLS_TO_US(macMinLIFSPeriod_def)
                            - IRQ_PROCESSING_DLY_US - PRE_TX_DURATION_US);
        }
        else
        {
            pal_timer_delay(TAL_CONVERT_SYMBOLS_TO_US(macMinSIFSPeriod_def)
                            - IRQ_PROCESSING_DLY_US - PRE_TX_DURATION_US);
        }
    }
    else
    {
        /*
         * If no delay is applied after switching to TX_ARET_ON,
         * a short delay is required that allows that a pending TX_END IRQ for
         * ACK transmission gets served.
         */
        pal_timer_delay(TRX_IRQ_DELAY_US);
    }

    ENTER_CRITICAL_REGION();    // prevent from buffer underrun

    /* Toggle the SLP_TR pin triggering transmission. */
    PAL_SLP_TR_HIGH();
    PAL_WAIT_65_NS();
    PAL_SLP_TR_LOW();

    /*
     * Send the frame to the transceiver.
     * Note: The PhyHeader is the first byte of the frame to
     * be sent to the transceiver and this contains the frame
     * length.
     */
    pal_trx_frame_write(frame_tx, frame_tx[0]-1);

    tal_state = TAL_TX_AUTO;

    LEAVE_CRITICAL_REGION();
}


/**
 * @brief Handles interrupts issued due to end of transmission
 */
void handle_tx_end_irq(void)
{
    /* Check if TX_END interrupt, is issued due to automatic ACK transmission. */
#if ((MAC_START_REQUEST_CONFIRM == 1) && (defined BEACON_SUPPORT))
    if ((tal_state != TAL_TX_AUTO) && (!tal_beacon_transmission))
#else
    if (tal_state != TAL_TX_AUTO)
#endif
    {
        /* Automatic ACK transmission completed -> No further processing. */
        return;
    }

#if ((MAC_START_REQUEST_CONFIRM == 1) && (defined BEACON_SUPPORT))
    if (tal_beacon_transmission)
    {
        tal_beacon_transmission = false;

        if (tal_csma_state == BACKOFF_WAITING_FOR_BEACON)
        {
            // Slotted CSMA has been waiting for a beacon, now it can continue.
            tal_csma_state = CSMA_HANDLE_BEACON;
        }
    }
    else
#endif /* ((MAC_START_REQUEST_CONFIRM == 1) && (defined BEACON_SUPPORT)) */
    {
#if (defined BEACON_SUPPORT) || (defined ENABLE_TSTAMP)
        /*
         * Store tx timestamp to frame_info_t structure.
         * The timestamping is only required for beaconing networks
         * or if timestamping is explicitly enabled.
         */
        pal_get_current_time(&mac_frame_ptr->time_stamp);
#endif  /* #if (defined BEACON_SUPPORT) || (defined ENABLE_TSTAMP) */

        /* Read trac status before enabling RX_AACK_ON. */
        trx_trac_status = (trx_trac_status_t)pal_trx_bit_read(SR_TRAC_STATUS);

#ifdef BEACON_SUPPORT
        if (tal_csma_state == FRAME_SENDING)    // Transmission was issued by slotted CSMA
        {
            PIN_TX_END();
            tal_state = TAL_SLOTTED_CSMA;

            /* Map status message of transceiver to TAL constants. */
            switch (trx_trac_status)
            {
                case TRAC_SUCCESS_DATA_PENDING:
                    PIN_ACK_OK_START();
                    tal_csma_state = TX_DONE_FRAME_PENDING;
                     break;

                case TRAC_SUCCESS:
                    PIN_ACK_OK_START();
                    tal_csma_state = TX_DONE_SUCCESS;
                    break;

                case TRAC_CHANNEL_ACCESS_FAILURE:
                    PIN_NO_ACK_START();
                    tal_csma_state = CSMA_ACCESS_FAILURE;
                    break;

                case TRAC_NO_ACK:
                    PIN_NO_ACK_START();
                    tal_csma_state = TX_DONE_NO_ACK;
                    break;

                case TRAC_INVALID:  /* Handle this in the same way as default. */
                default:
                    ASSERT("not handled trac status" == 0);
                    tal_csma_state = CSMA_ACCESS_FAILURE;
                    break;
            }
            PIN_ACK_OK_END();
            PIN_ACK_WAITING_END();
        }
        else
#endif  /* BEACON_SUPPORT */
        // Trx has handled the entire transmission incl. CSMA
        {
            tal_state = TAL_TX_DONE;    // Further handling is done by tx_done_handling()
        }
    }

    /*
     * After transmission has finished, switch receiver on again.
     * Check if receive buffer is available.
     */
    if (NULL == tal_rx_buffer)
    {
        set_trx_state(CMD_PLL_ON);
        tal_rx_on_required = true;
    }
    else
    {
        set_trx_state(CMD_RX_AACK_ON);
    }
}



/**
 * @brief Beacon frame transmission
 */
#if ((MAC_START_REQUEST_CONFIRM == 1) && (defined BEACON_SUPPORT))
void tal_tx_beacon(frame_info_t *tx_frame)
{
    tal_trx_status_t trx_status;

    /* Set pointer to actual mpdu to be downloaded to the transceiver. */
    uint8_t *tal_beacon_to_tx = tx_frame->mpdu;

    /* Avoid that the beacon is transmitted while other transmision is on-going. */
    if (tal_state == TAL_TX_AUTO)
    {
        ASSERT("trying to transmit beacon while ongoing transmission" == 0);
        return;
    }

    /* Send the pre-created beacon frame to the transceiver. */
    do
    {
        trx_status = set_trx_state(CMD_PLL_ON);
#if (DEBUG > 1)
        if (trx_status != PLL_ON)
        {
            ASSERT("PLL_ON failed for beacon transmission" == 0);
        }
#endif
    } while (trx_status != PLL_ON);

    // @TODO wait for talbeaconTxTime

    ENTER_CRITICAL_REGION();    // prevent from buffer underrun

    /* Toggle the SLP_TR pin triggering transmission. */
    PAL_SLP_TR_HIGH();
    PAL_WAIT_65_NS();
    PAL_SLP_TR_LOW();

    /*
     * Send the frame to the transceiver.
     * Note: The PhyHeader is the first byte of the frame to
     * be sent to the transceiver and this contains the frame
     * length.
     */
    pal_trx_frame_write(tal_beacon_to_tx, tal_beacon_to_tx[0]-1);

    tal_beacon_transmission = true;

    LEAVE_CRITICAL_REGION();
}
#endif /* ((MAC_START_REQUEST_CONFIRM == 1) && (defined BEACON_SUPPORT)) */


/* EOF */

