/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved.
 *
 * The information contained herein is property of Nordic Semiconductor ASA.
 * Terms and conditions of usage are described in detail in NORDIC
 * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
 *
 * Licensees are granted free, non-transferable use of the information. NO
 * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
 * the file.
 *
 */
 
#include "hal_transport.h"
#include "hci_transport_config.h"
#include "hci_slip.h"
#include "crc16.h"
#include "hci_mem_pool.h"
#include "hci_mem_pool_internal.h"
#include "nordic_common.h"
#include "compiler_abstraction.h"
#include "app_util.h"
#include "app_timer.h"
#include "app_error.h"
#include <stdio.h>
#include <stdbool.h>

#define PKT_HDR_SIZE                    4u                                                                 /**< Packet header size in number of bytes. */
#define PKT_CRC_SIZE                    2u                                                                 /**< Packet CRC size in number of bytes. */
#define PKT_TYPE_VENDOR_SPECIFIC        14u                                                                /**< Packet type vendor specific. */
#define PKT_TYPE_ACK                    0                                                                  /**< Packet type acknowledgement. */
#define DATA_INTEGRITY_MASK             (1u << 6u)                                                         /**< Mask for data integrity bit in the packet header. */
#define RELIABLE_PKT_MASK               (1u << 7u)                                                         /**< Mask for reliable packet bit in the packet header. */
#define INITIAL_ACK_NUMBER_EXPECTED     1u                                                                 /**< Initial acknowledge number expected. */
#define INITIAL_ACK_NUMBER_TX           INITIAL_ACK_NUMBER_EXPECTED                                        /**< Initial acknowledge number transmitted. */
#define INVALID_PKT_TYPE                0xFFFFFFFFu                                                        /**< Internal invalid packet type value. */
#define MAX_TRANSMISSION_TIME           (ROUNDED_DIV((MAX_PACKET_SIZE_IN_BITS * 1000u), USED_BAUD_RATE))   /**< Max transmission time of a single application packet over UART in units of mseconds. */      
#define RETRANSMISSION_TIMEOUT_IN_MS    (3u * MAX_TRANSMISSION_TIME)                                       /**< Retransmission timeout for application packet in units of mseconds. */      
#define APP_TIMER_PRESCALER             0                                                                  /**< Value of the RTC1 PRESCALER register. */
#define RETRANSMISSION_TIMEOUT_IN_TICKS APP_TIMER_TICKS(RETRANSMISSION_TIMEOUT_IN_MS, APP_TIMER_PRESCALER) /**< Retransmission timeout for application packet in units of timer ticks. */             
#define MAX_RETRY_COUNT                 5u                                                                 /**< Max retransmission retry count for application packets. */
#define ACK_BUF_SIZE                    5u                                                                 /**< Length of module internal RX buffer which is big enough to hold an acknowledgement packet. */

/**@brief States of the TX state machine. */
typedef enum
{
    TX_STATE_IDLE,                                                   /**< State for: no application transmission packet processing in progress. */                                                                                             
    TX_STATE_PENDING,                                                /**< State for: TX in progress in slip layer and TX-done event is waited for to signal the end of transmission. */
    TX_STATE_ACTIVE                                                  /**< State for: application packet has been delivered to slip for transmission and peer transport entity acknowledgement packet is waited for. */
} tx_state_t;

/**@brief TX state machine events. */
typedef enum
{
    TX_EVENT_STATE_ENTRY,                                            /**< Event for: state entry use case. */                                                                                             
    TX_EVENT_SLIP_TX_DONE,                                           /**< Event for: HCI_SLIP_TX_DONE event use case. */
    TX_EVENT_TIMEOUT,                                                /**< Event for: retransmission timeout use case. */
    TX_EVENT_VALID_RX_ACK                                            /**< Event for: valid acknowledgement received for TX packet use case. */
} tx_event_t;

static void tx_sm_state_change(tx_state_t new_state);

static tx_state_t                      m_tx_state;                   /**< Current TX state. */
static hci_transport_tx_done_handler_t m_transport_tx_done_handle;   /**< TX done event callback function. */
static hci_transport_event_handler_t   m_transport_event_handle;     /**< Event handler callback function. */
static uint8_t *                       mp_slip_used_rx_buffer;       /**< Reference to RX buffer used by the slip layer. */
static uint32_t                        m_packet_expected_seq_number; /**< Sequence number counter of the packet expected to be received . */ 
static uint32_t                        m_packet_transmit_seq_number; /**< Sequence number counter of the transmitted packet for which acknowledgement packet is waited for. */ 
static uint8_t *                       mp_tx_buffer;                 /**< Pointer to TX application buffer to be transmitted. */
static uint32_t                        m_tx_buffer_length;           /**< Length of application TX packet data to be transmitted in bytes. */
static bool                            m_is_slip_decode_ready;       /**< Boolean to determine has slip decode been completed or not. */
static app_timer_id_t                  m_app_timer_id;               /**< Application timer id. */
static uint32_t                        m_tx_retry_counter;           /**< Application packet retransmission counter. */
static hci_transport_tx_done_result_t  m_tx_done_result_code;        /**< TX done event callback function result code. */
static uint8_t                         m_rx_ack_buffer[ACK_BUF_SIZE];/**< RX buffer big enough to hold an acknowledgement packet and which is taken in use upon receiving  HCI_SLIP_RX_OVERFLOW event. */


/**@brief Function for validating a received packet.
 *
 * @param[in] p_buffer Pointer to the packet data.
 * @param[in] length   Length of packet data in bytes.  
 *
 * @return true if received packet is valid, false in other case.
 */
static bool is_rx_pkt_valid(const uint8_t * p_buffer, uint32_t length)
{  
    // Executed packet filtering algorithm order:
    // - verify packet overall length
    // - verify data integrity bit set
    // - verify reliable packet bit set
    // - verify supported packet type
    // - verify header checksum
    // - verify payload length field
    // - verify CRC
    if (length <= PKT_HDR_SIZE)
    {
        return false;        
    }
    
    if (!(p_buffer[0] & DATA_INTEGRITY_MASK))
    {
        return false;
    }    
    
    if (!(p_buffer[0] & RELIABLE_PKT_MASK))
    {
        return false;
    }        
    
    if ((p_buffer[1] & 0x0Fu) != PKT_TYPE_VENDOR_SPECIFIC)
    {    
        return false;    
    }
    
    const uint32_t expected_checksum = 
        ((p_buffer[0] + p_buffer[1] + p_buffer[2] + p_buffer[3])) & 0xFFu;
    if (expected_checksum != 0)
    {    
        return false;
    }
    
    const uint16_t crc_calculated = crc16_compute(p_buffer, (length - PKT_CRC_SIZE), NULL);
    const uint16_t crc_received   = uint16_decode(&p_buffer[length - PKT_CRC_SIZE]);
    if (crc_calculated != crc_received)
    {
        return false;
    }
    
    return true;
}


/**@brief Function for getting the sequence number of the next reliable packet expected.
 *
 * @return sequence number of the next reliable packet expected.
 */
static __INLINE uint8_t packet_number_expected_get(void)
{    
    return (uint8_t) m_packet_expected_seq_number;
}


/**@brief Function for calculating a packet header checksum.
 *
 * @param[in] p_hdr Pointer to the packet header.
 *
 * @return          Calculated checksum.
 */
static uint8_t header_checksum_calculate(const uint8_t * p_hdr)
{
    // @note: no pointer validation check needed as already checked by calling function.
    uint32_t checksum;
    
    checksum  = p_hdr[0];   
    checksum += p_hdr[1];          
    checksum += p_hdr[2];                  
    checksum &= 0xFFu;
    checksum  = (~checksum + 1u);
    
    return (uint8_t)checksum;
}


/**@brief Function for writing an acknowledgment packet for transmission.
 */
static void ack_transmit(void)
{
    static uint8_t ack_packet[PKT_HDR_SIZE];
    
    // TX ACK packet format:
    // - Unreliable Packet type
    // - Payload Length set to 0
    // - Sequence Number set to 0
    // - Header checksum calculated
    // - Acknowledge Number set correctly            
    ack_packet[0] = (packet_number_expected_get() << 3u);
    ack_packet[1] = 0;    
    ack_packet[2] = 0;        
    ack_packet[3] = header_checksum_calculate(ack_packet); 

    // @note: no return value check needed for hci_slip_write(...) call as acknowledgement packets 
    // are considered to be from system design point of view unreliable packets.Use case where 
    // underlying slip layer does not accept a packet for transmission is managed either by:
    // - acknowledged by possible future application packet as acknowledgement number header field 
    // is included 
    // - protocol peer entity will retransmit the packet        
    UNUSED_VARIABLE(hci_slip_write(ack_packet, sizeof(ack_packet)));   
}


/**@brief Function for validating a received packet.
 *
 * @param[in] p_buffer Pointer to the packet data. 
 *
 * @return sequence number field of the packet header with unrelated data masked out.
 */
static __INLINE uint8_t packet_seq_nmbr_extract(const uint8_t * p_buffer)
{
    return (p_buffer[0] & 0x07u);
}


/**@brief Function for incrementing the sequence number counter for next reliable packet expected.
 */
static __INLINE void packet_number_expected_inc(void)
{
    ++m_packet_expected_seq_number;
    m_packet_expected_seq_number &= 0x07u;    
}


/**@brief Function for decoding a packet type field.
 *
 * @param[in] p_buffer Pointer to the packet data. 
 * @param[in] length   Length of packet data in bytes.  
 *
 * @return Packet type field or INVALID_PKT_TYPE in case of decode error.
 */
static __INLINE uint32_t packet_type_decode(const uint8_t * p_buffer, uint32_t length)
{
    // @note: no pointer validation check needed as allready checked by calling function.
    uint32_t return_value;
                
    if (length >= PKT_HDR_SIZE)
    {
        return_value = (p_buffer[1] & 0x0Fu);
    }    
    else
    {
        return_value = INVALID_PKT_TYPE;
    }    
    
    return return_value;
}


/**@brief Function for processing a received vendor specific packet.
 *
 * @param[in] p_buffer Pointer to the packet data. 
 * @param[in] length   Length of packet data in bytes.  
 */
static void rx_vendor_specific_pkt_type_handle(const uint8_t * p_buffer, uint32_t length)
{
    // @note: no pointer validation check needed as allready checked by calling function.
    uint32_t err_code;
    
    if (is_rx_pkt_valid(p_buffer, length))
    {
        // RX packet is valid: validate sequence number.
        const uint8_t rx_seq_number = packet_seq_nmbr_extract(p_buffer);
        if (packet_number_expected_get() == rx_seq_number)
        {
            // Sequence number is valid: transmit acknowledgement.
            packet_number_expected_inc();                    
            ack_transmit();                    

            m_is_slip_decode_ready = true;            
            
            err_code = hci_mem_pool_rx_data_size_set(length);
            APP_ERROR_CHECK(err_code);

            err_code = hci_mem_pool_rx_produce(RX_BUF_SIZE, (void **)&mp_slip_used_rx_buffer); 
            APP_ERROR_CHECK_BOOL((err_code == NRF_SUCCESS) || (err_code == NRF_ERROR_NO_MEM));

            // If memory pool RX buffer produce succeeded we register that buffer to slip layer 
            // otherwise we register the internal acknowledgement buffer. 
            err_code = hci_slip_rx_buffer_register(
                (err_code == NRF_SUCCESS) ? mp_slip_used_rx_buffer : m_rx_ack_buffer, 
                (err_code == NRF_SUCCESS) ? RX_BUF_SIZE : ACK_BUF_SIZE);            

            APP_ERROR_CHECK(err_code);                
                    
            if (m_transport_event_handle != NULL)
            {
                // Send application event of RX packet reception.
                const hci_transport_evt_t evt = {HCI_TRANSPORT_RX_RDY};
                m_transport_event_handle(evt);
            }                     
        }
        else
        {
            // RX packet discarded: sequence number not valid, set the same buffer to slip layer in 
            // order to avoid buffer overrun. 
            err_code = hci_slip_rx_buffer_register(mp_slip_used_rx_buffer, RX_BUF_SIZE);                            
            APP_ERROR_CHECK(err_code);                            
                
            // As packet did not have expected sequence number: send acknowledgement with the 
            // current expected sequence number.
            ack_transmit();
        }
    }
    else
    {
        // RX packet discarded: reset the same buffer to slip layer in order to avoid buffer
        // overrun. 
        err_code = hci_slip_rx_buffer_register(mp_slip_used_rx_buffer, RX_BUF_SIZE);                            
        APP_ERROR_CHECK(err_code);                                    
    }            
}


/**@brief Function for getting the sequence number of a reliable TX packet for which peer protocol 
 * entity acknowledgment is pending.
 *
 * @return sequence number of a reliable TX packet for which peer protocol entity acknowledgement
 * is pending.
 */
static __INLINE uint8_t packet_number_to_transmit_get(void)
{
    return m_packet_transmit_seq_number;
}


/**@brief Function for getting the expected acknowledgement number.
 *
 * @return expected acknowledgement number.
 */
static __INLINE uint8_t expected_ack_number_get(void)
{
    uint8_t seq_nmbr = packet_number_to_transmit_get(); 
    ++seq_nmbr;
    seq_nmbr &= 0x07u;
    
    return seq_nmbr;
}


/**@brief Function for processing a received acknowledgement packet.
 *
 * Verifies does the received acknowledgement packet has the expected acknowledgement number and 
 * that the header checksum is correct. 
 *
 * @param[in] p_buffer Pointer to the packet data. 
 *
 * @return true if valid acknowledgement packet received.
 */
static __INLINE bool rx_ack_pkt_type_handle(const uint8_t * p_buffer)
{
    // @note: no pointer validation check needed as allready checked by calling function.
    
    // Verify header checksum.
    const uint32_t expected_checksum = 
        ((p_buffer[0] + p_buffer[1] + p_buffer[2] + p_buffer[3])) & 0xFFu;
    if (expected_checksum != 0)
    {    
        return false;
    }
    
    const uint8_t ack_number = (p_buffer[0] >> 3u) & 0x07u;
    
    // Verify expected acknowledgment number.
    return (ack_number == expected_ack_number_get());
}


/**@brief Function for incrementing the sequence number counter of the TX packet. 
 */
static __INLINE void packet_number_tx_inc(void)
{
    ++m_packet_transmit_seq_number;
    m_packet_transmit_seq_number &= 0x07u;
}


/**@brief Function for TX state machine event processing in a state centric manner.
 *
 * @param[in] event Type of event occurred.
 */
static void tx_sm_event_handle(tx_event_t event)
{
    uint32_t err_code;
    
    switch (m_tx_state)
    {
        case TX_STATE_IDLE:
            if (event == TX_EVENT_STATE_ENTRY)
            {                 
                err_code = app_timer_stop(m_app_timer_id);
                APP_ERROR_CHECK(err_code);
                
                // Send TX-done event if registered handler exists.
                if (m_transport_tx_done_handle != NULL)                
                {
                    m_transport_tx_done_handle(m_tx_done_result_code);
                }                
            }
            break;
            
        case TX_STATE_PENDING:
            if (event == TX_EVENT_SLIP_TX_DONE)
            {     
                // @note: this call should always succeed as called from HCI_SLIP_TX_DONE context 
                // and error cases are managed by dedicated error event from the slip layer.
                err_code = hci_slip_write(mp_tx_buffer, 
                                         (m_tx_buffer_length + PKT_HDR_SIZE + PKT_CRC_SIZE));  
                APP_ERROR_CHECK(err_code);                                                  
                tx_sm_state_change(TX_STATE_ACTIVE);                                              
            }        
            break;
            
        case TX_STATE_ACTIVE:
            switch (event)
            {
                case TX_EVENT_VALID_RX_ACK:                    
                    // Tx sequence number counter incremented as packet transmission 
                    // acknowledged by peer transport entity.
                    packet_number_tx_inc();                                  
                    tx_sm_state_change(TX_STATE_IDLE);
                    break;
                    
                case TX_EVENT_STATE_ENTRY:
                    m_tx_retry_counter = 0;
                    err_code = app_timer_start(m_app_timer_id, 
                                               RETRANSMISSION_TIMEOUT_IN_TICKS, 
                                               NULL);
                    APP_ERROR_CHECK(err_code);
                    break;
                    
                case TX_EVENT_TIMEOUT:
                    if (m_tx_retry_counter != MAX_RETRY_COUNT)
                    {
                        ++m_tx_retry_counter;
                        // @note: no return value check done for hci_slip_write(...) call as current 
                        // system design allows use case where retransmission is not accepted by the 
                        // slip layer due to existing acknowledgement packet transmission in the 
                        // slip layer. 
                        UNUSED_VARIABLE(hci_slip_write(mp_tx_buffer, 
                                                      (m_tx_buffer_length + 
                                                      PKT_HDR_SIZE        + 
                                                      PKT_CRC_SIZE)));                
                    }    
                    else
                    {
                        // Application packet retransmission count reached: 
                        // - set correct TX done event callback function result code
                        // - execute state change    
                        // @note: m_tx_retry_counter is reset in TX_STATE_ACTIVE state entry.
                        m_tx_done_result_code = HCI_TRANSPORT_TX_DONE_FAILURE;         
                        tx_sm_state_change(TX_STATE_IDLE);
                    }                
                    break;
                    
                default:
                    // No implementation needed.
                    break;
            }
            break;
            
        default:        
            // No implementation needed.
            break;
    }
}


/**@brief Function for changing the state of the TX state machine.
 *
 * @param[in] new_state State TX state machine transits to.
 */
static void tx_sm_state_change(tx_state_t new_state)
{
    m_tx_state = new_state;
    tx_sm_event_handle(TX_EVENT_STATE_ENTRY);
}


/**@brief Function for handling slip events.
 *
 * @param[in] event The event structure.
 */
void slip_event_handle(hci_slip_evt_t event)
{    
    uint32_t return_code;
    uint32_t err_code;    
    
    switch (event.evt_type)
    {
        case HCI_SLIP_TX_DONE:   
            tx_sm_event_handle(TX_EVENT_SLIP_TX_DONE);
            break;
            
        case HCI_SLIP_RX_RDY:
            return_code = packet_type_decode(event.packet, event.packet_length);
            
            switch (return_code)
            {
                case PKT_TYPE_VENDOR_SPECIFIC:
                    rx_vendor_specific_pkt_type_handle(event.packet, event.packet_length);
                    break;
                    
                case PKT_TYPE_ACK:
                    if (rx_ack_pkt_type_handle(event.packet))
                    {
                        // Valid expected acknowledgement packet received: set correct TX done event 
                        // callback function result code and execute state change.
                        m_tx_done_result_code = HCI_TRANSPORT_TX_DONE_SUCCESS; 
                        tx_sm_event_handle(TX_EVENT_VALID_RX_ACK);
                    }
                
                /* fall-through */                
                default:
                    // RX packet dropped: reset memory buffer to slip in order to avoid RX buffer 
                    // overflow. 
                    // If existing mem pool produced RX buffer exists reuse that one. If existing 
                    // mem pool produced RX buffer does not exist try to produce new one. If 
                    // producing fails use the internal acknowledgement buffer.                    
                    if (mp_slip_used_rx_buffer != NULL)
                    {
                        err_code = hci_slip_rx_buffer_register(mp_slip_used_rx_buffer, RX_BUF_SIZE);                                                            
                        APP_ERROR_CHECK(err_code);                                                                
                    }
                    else
                    {
                        err_code = hci_mem_pool_rx_produce(RX_BUF_SIZE, 
                                                           (void **)&mp_slip_used_rx_buffer); 
                        APP_ERROR_CHECK_BOOL((err_code == NRF_SUCCESS) || 
                                            (err_code == NRF_ERROR_NO_MEM));

                        err_code = hci_slip_rx_buffer_register(
                            (err_code == NRF_SUCCESS) ? mp_slip_used_rx_buffer : m_rx_ack_buffer, 
                            (err_code == NRF_SUCCESS) ? RX_BUF_SIZE : ACK_BUF_SIZE);            
                        APP_ERROR_CHECK(err_code);                                                                
                    }
                    break;
            }
            break;

        case HCI_SLIP_RX_OVERFLOW:
            err_code = hci_slip_rx_buffer_register(m_rx_ack_buffer, ACK_BUF_SIZE);                                                            
            APP_ERROR_CHECK(err_code);                                                                
            break;
        
        case HCI_SLIP_ERROR:
            APP_ERROR_HANDLER(event.evt_type);
            break;
            
        default:
            APP_ERROR_HANDLER(event.evt_type); 
            break;
    }
}


uint32_t hci_transport_evt_handler_reg(hci_transport_event_handler_t event_handler)
{
    uint32_t err_code;    
    
    m_transport_event_handle = event_handler;
    err_code                 = hci_slip_evt_handler_register(slip_event_handle); 
    APP_ERROR_CHECK(err_code);                                                                        
    
    return (event_handler != NULL) ? NRF_SUCCESS : NRF_ERROR_NULL;        
}


uint32_t hci_transport_tx_done_register(hci_transport_tx_done_handler_t event_handler)
{
    uint32_t err_code;    
    
    m_transport_tx_done_handle = event_handler;
    err_code                   = hci_slip_evt_handler_register(slip_event_handle);    
    APP_ERROR_CHECK(err_code);                                                                    
    
    return (event_handler != NULL) ? NRF_SUCCESS : NRF_ERROR_NULL;        
}


/**@brief Function for handling the application packet retransmission timeout.
 *        
 * This function is registered in the @ref app_timer module when a timer is created on 
 * @ref hci_transport_open. 
 *
 * @note This function must be executed in APP-LO context otherwise retransmission behaviour is 
 *       undefined, see @ref nrf51_system_integration_serialization.
 * 
 * @param[in] p_context The timeout context.
 */
void hci_transport_timeout_handle(void * p_context)
{
    tx_sm_event_handle(TX_EVENT_TIMEOUT);
}


uint32_t hci_transport_open(void)
{
    mp_tx_buffer                 = NULL;
    m_tx_buffer_length           = 0;
    m_tx_retry_counter           = 0;
    m_is_slip_decode_ready       = false;
    m_tx_state                   = TX_STATE_IDLE;
    m_packet_expected_seq_number = INITIAL_ACK_NUMBER_EXPECTED;
    m_packet_transmit_seq_number = INITIAL_ACK_NUMBER_TX;
    m_tx_done_result_code        = HCI_TRANSPORT_TX_DONE_FAILURE;
    
    uint32_t err_code = app_timer_create(&m_app_timer_id, 
                                         APP_TIMER_MODE_REPEATED, 
                                         hci_transport_timeout_handle);
    if (err_code != NRF_SUCCESS)
    {    
        // @note: conduct required interface adjustment.
        return NRF_ERROR_INTERNAL;
    }
    
    err_code = hci_mem_pool_open();
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }    
    
    err_code = hci_slip_open();
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }     
    
    err_code = hci_mem_pool_rx_produce(RX_BUF_SIZE, (void **)&mp_slip_used_rx_buffer);
    if (err_code != NRF_SUCCESS)
    {
        // @note: conduct required interface adjustment.
        return NRF_ERROR_INTERNAL;
    }   
    
    err_code = hci_slip_rx_buffer_register(mp_slip_used_rx_buffer, RX_BUF_SIZE);
    
    return err_code;    
}


uint32_t hci_transport_close(void)
{
    uint32_t err_code;

    m_transport_tx_done_handle = NULL;
    m_transport_event_handle   = NULL;
        
    err_code = hci_mem_pool_close();
    APP_ERROR_CHECK(err_code);    
    err_code = hci_slip_close();
    APP_ERROR_CHECK(err_code);    
    
    // @note: NRF_ERROR_NO_MEM is the only return value which should never be returned.
    err_code = app_timer_stop(m_app_timer_id);
    APP_ERROR_CHECK_BOOL(err_code != NRF_ERROR_NO_MEM);
    
    return NRF_SUCCESS;
}    


uint32_t hci_transport_tx_alloc(uint8_t ** pp_memory)
{
    const uint32_t err_code = hci_mem_pool_tx_alloc((void **)pp_memory);    
    if (err_code == NRF_SUCCESS)
    {
        // @note: no need to validate pp_memory against null as validation has already been done 
        // by hci_mem_pool_tx_alloc(...) and visible to us from the method return code.
        //lint -e(413) "Likely use of null pointer"        
        *pp_memory += PKT_HDR_SIZE; 
    }
    
    return err_code;
}


uint32_t hci_transport_tx_free(void)
{
    return hci_mem_pool_tx_free();
}


/**@brief Function for constructing 1st byte of the packet header of the packet to be transmitted.
 *
 * @return 1st byte of the packet header of the packet to be transmitted
 */
static __INLINE uint8_t tx_packet_byte_zero_construct(void)
{
    const uint32_t value = DATA_INTEGRITY_MASK                  | 
                           RELIABLE_PKT_MASK                    | 
                           (packet_number_expected_get() << 3u) | 
                           packet_number_to_transmit_get();   
    
    return (uint8_t) value;
}


/**@brief Function for handling the application packet write request in tx-idle state.
 */
static uint32_t pkt_write_handle(void)
{   
    uint32_t err_code;     
    
    // Set packet header fields.

    mp_tx_buffer   -= PKT_HDR_SIZE;
    mp_tx_buffer[0] = tx_packet_byte_zero_construct();
                
    const uint16_t type_and_length_fields = ((m_tx_buffer_length << 4u) | PKT_TYPE_VENDOR_SPECIFIC);            
    // @note: no use case for uint16_encode(...) return value.
    UNUSED_VARIABLE(uint16_encode(type_and_length_fields, &(mp_tx_buffer[1])));
    mp_tx_buffer[3] = header_checksum_calculate(mp_tx_buffer);
    
    // Calculate, append CRC to the packet and write it.
        
    const uint16_t crc = crc16_compute(mp_tx_buffer, (PKT_HDR_SIZE + m_tx_buffer_length), NULL);
    // @note: no use case for uint16_encode(...) return value.
    UNUSED_VARIABLE(uint16_encode(crc, &(mp_tx_buffer[PKT_HDR_SIZE + m_tx_buffer_length])));        
    err_code = hci_slip_write(mp_tx_buffer, (m_tx_buffer_length + PKT_HDR_SIZE + PKT_CRC_SIZE));        
    switch (err_code)
    {
        case NRF_SUCCESS:
            tx_sm_state_change(TX_STATE_ACTIVE);
            break;
                
        case NRF_ERROR_NO_MEM:
            tx_sm_state_change(TX_STATE_PENDING);            
            err_code = NRF_SUCCESS;
            break;
                
        default:
            // No implementation needed.
            break;
    }
    
    return err_code;
}


uint32_t hci_transport_pkt_write(const uint8_t * p_buffer, uint16_t length)
{
    uint32_t err_code;
    
    if (p_buffer)
    {          
        switch (m_tx_state)
        {
            case TX_STATE_IDLE:
                mp_tx_buffer       = (uint8_t *)p_buffer;
                m_tx_buffer_length = length;            
                err_code = pkt_write_handle();
                break;
                
            default:
                err_code = NRF_ERROR_NO_MEM;
                break;
        }
    }
    else
    {
        err_code = NRF_ERROR_NULL;
    }
    
    return err_code;    
}


uint32_t hci_transport_rx_pkt_extract(uint8_t ** pp_buffer, uint16_t * p_length)
{
    uint32_t err_code;
    
    if (pp_buffer != NULL && p_length != NULL)
    {
        uint32_t length = 0; 
        
        if (m_is_slip_decode_ready)
        {
            m_is_slip_decode_ready = false;
            err_code               = hci_mem_pool_rx_extract(pp_buffer, &length);
            length                -= (PKT_HDR_SIZE + PKT_CRC_SIZE);
            
            *p_length              = (uint16_t)length; 
            *pp_buffer            += PKT_HDR_SIZE;
        }
        else
        {
            err_code = NRF_ERROR_NO_MEM;
        }
    }
    else
    {
        err_code = NRF_ERROR_NULL;
    }
    
    return err_code;
}


uint32_t hci_transport_rx_pkt_consume(uint8_t * p_buffer)
{
    return (hci_mem_pool_rx_consume(p_buffer - PKT_HDR_SIZE));     
}
