//============================================================================
//  Title   : radio_interface
//  Desc    : Nordic radio 802.15.4 interface functions
//  2022-8-5   Glen     Created
//============================================================================
// Documentation on the 802.15.4 driver here:
// https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/nrfxlib/nrf_802154/README.html
//
// Sample code used for low level radio functions, rx pool, and how to receive
// are in the SDK here: \nRF_Connect\v2.0.0\nrf\samples\peripheral\802154_phy_test\src
//
//== Includes ================================================================
#include "radio_interface.h"
#include <string.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/printk.h>
#include <hal/nrf_radio.h>
#include <string.h>
#include <stdio.h>
#include "radio.h"
#include "iomapping.h"

//== Definitions and Types ===================================================
//#define NO_TRANSMIT_FCC_VERSION                  1
#define RF_FCS_SIZE                                (2u)
#define POWER_OUTPUT_LEVEL_INTO_AMPLIFIER          0      // dBm
#define POWER_INCREASE_AMPLIFIER                   22     // dBm

typedef uint16_t ptt_pkt_len_t;

struct rf_rx_pkt_s {
	uint8_t *rf_buf; /**< pointer to buffer inside radio driver with a received packet */
	uint8_t *data; /**< pointer to payload */
	ptt_pkt_len_t length; /**< size of payload of received packet */
	int8_t rssi; /**< RSSI */
	uint8_t lqi; /**< LQI */
};

//== Global Variables ========================================================
static volatile bool m_tx_complete = true;

//== Function prototypes =====================================================

//============================================================================

//----------------------------------------------------------------------------
// Name  : radio_interface_config
// Desc  : Meant to reset the radio but there isn't an obvious nrf function to
//         that and it might not even make sense for an on-processor radio
// Ins   : none
// Outs  : none
//----------------------------------------------------------------------------
void radio_interface_config(void)
{
   // Clear the TX in progress flag
   m_tx_complete = true;
}

//----------------------------------------------------------------------------
// Name  : radio_interface_receive
// Desc  : Start the radio to receive packets
// Ins   : none
// Outs  : none
//----------------------------------------------------------------------------
void radio_interface_receive(void)
{
  nrf_802154_receive();
}

//----------------------------------------------------------------------------
// Name  : radio_interface_init
// Desc  : Configure the radio
// Ins   : none
// Outs  : none
//----------------------------------------------------------------------------
int radio_interface_init(void)
{
  /* nrf radio driver initialization */
   nrf_802154_init();

   uint8_t panId[] = { 0x00, 0x01 };
   nrf_802154_pan_id_set(&panId[0]);
   nrf_802154_channel_set(25);
   nrf_802154_promiscuous_set(false);
   return 0;
}

//----------------------------------------------------------------------------
// Name  : radio_interface_channel_set
// Desc  : Send the channel to the radio driver
// Ins   : none
// Outs  : none
//----------------------------------------------------------------------------
void radio_interface_channel_set(uint8_t channel)
{
   nrf_802154_channel_set(channel);
}

//----------------------------------------------------------------------------
// Name  : nrf_802154_received_raw
// Desc  : Received a packet from the nrf radio. Save off needed data and call 
//         rf_process_rx_packets to handle it
// Ins   : none
// Outs  : none
//----------------------------------------------------------------------------
void nrf_802154_received_raw(uint8_t *data, int8_t power, uint8_t lqi)
{
	struct rf_rx_pkt_s rx_pkt;
	bool pkt_placed = false;

	if (data == NULL)
   {
      return;
   }
   
   rx_pkt.rf_buf = data;
   rx_pkt.data = &data[1];
   rx_pkt.length = data[0] - RF_FCS_SIZE;

   rx_pkt.lqi = lqi; // It looks like we don't need these ED conversions after all
   pkt_placed = true;

   // Convert the RSSI from dBm to a custom scale
   rx_pkt.rssi = radio_interface_signal_strength_lookup(power);

   radio_interrupt(true, false, rx_pkt.data, rx_pkt.length, rx_pkt.rssi, rx_pkt.lqi, 0);
   nrf_802154_buffer_free_raw(rx_pkt.rf_buf);
}

//----------------------------------------------------------------------------
// Name  : nrf_802154_transmitted_raw
// Desc  : Override the WEAK defined library "tx done" function so that I can
//         add a call to the radio_interrupt in radio.c for status tracking
//         If ACK was requested for the transmitted frame, this function is 
//         called after a proper ACK is received. If ACK was not requested, 
//         this function is called just after transmission has ended.
// Ins   : none
// Outs  : none
//----------------------------------------------------------------------------
void nrf_802154_transmitted_raw(uint8_t * p_frame, const nrf_802154_transmit_done_metadata_t * p_metadata)
{
    (void)p_frame;

    uint8_t * p_ack = p_metadata->data.transmitted.p_ack;

    if (p_ack != NULL)
    {
        nrf_802154_buffer_free_raw(p_ack);
    }

    radio_interrupt(false, true, NULL, 0, 0, 0, 0);   // Call the radio TX complete interrupt, no failure
    //printk("TXDONE m_tx_complete = true\r\n");
    m_tx_complete = true;
}

//----------------------------------------------------------------------------
// Name  : nrf_802154_transmit_failed
// Desc  : Notifies that a frame was not transmitted due to a busy channel.
// Ins   : p_frame      Pointer to a buffer that contains PHR and PSDU of the frame that was not transmitted
// Ins   : error        Reason of the failure.
// Ins   : p_metadata   Pointer to a metadata structure describing frame passed in @p p_frame.
// Outs  : none
//----------------------------------------------------------------------------
void nrf_802154_transmit_failed(uint8_t * p_frame, nrf_802154_tx_error_t error, const nrf_802154_transmit_done_metadata_t * p_metadata)
{
   radio_interrupt(false, true, NULL, 0, 0, 0, 1);   // Call the radio TX complete interrupt, with a failure
   m_tx_complete = true;
   //printk("TXFAIL m_tx_complete = true\r\n");
   printk("\r\nrf_802154_transmit_failed\r\n");
}

//----------------------------------------------------------------------------
// Name  : radio_interface_transmit_packet
// Desc  : Send a packet out
// Ins   : A pointer to the data to send
// Outs  : none
//----------------------------------------------------------------------------
void radio_interface_transmit_packet(uint8_t *txdata)
{
   bool txstatus; 

   // The library is aware of the gain added by the PA, so you have to set the final output
   // power level you want, not just the level you want to go into the amplifier like it used to be
   nrf_802154_tx_power_set(POWER_OUTPUT_LEVEL_INTO_AMPLIFIER + POWER_INCREASE_AMPLIFIER);

   //printk("PRE-TX Set m_tx_complete false, Was %d\r\n", m_tx_complete);
   m_tx_complete = false;  // Set a flag that will be cleared when the transmission is either complete (plus acknowledgment) or it has failed      

   txstatus = nrf_802154_transmit_raw(txdata, NULL);

   if (txstatus == false)  // Failure to transmit 
   {
      printk("\r\nTransmit failure\r\n");
   }
}


//----------------------------------------------------------------------------
// Name  : radio_interface_get_tx_busy
// Desc  : Returns 1 if the radio has not yet completed its transmit (including receiving the hardware ACK if requested)
// Ins   : none
// Outs  : 1 for busy, 0 for not busy
//----------------------------------------------------------------------------
uint8_t radio_interface_get_tx_busy(void)
{
   if (m_tx_complete == false)
   {
      return 1;
   }
   else
   {
      return 0;
   }
}
