lib/ble/src/ble_encode_transport.c

Committer:
jinu
Date:
2017-02-09
Revision:
0:6ba9b94b8997

File content as of revision 0:6ba9b94b8997:

/* Copyright (c) Nordic Semiconductor ASA
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this
 * list of conditions and the following disclaimer in the documentation and/or
 * other materials provided with the distribution.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of other
 * contributors to this software may be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 * 4. This software must only be used in a processor manufactured by Nordic
 * Semiconductor ASA, or in a processor manufactured by a third party that
 * is used in combination with a processor manufactured by Nordic Semiconductor.
 *
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "ble_encode_access.h"
#include "ble_encode_transport.h"
#include <stdbool.h>
#include <stdio.h>
#include "ble.h"
#include "ble_rpc_defines.h"
#include "ble_app.h"
#include "hal_transport.h"
#include "nrf_error.h"
#include "app_error.h"
#include "compiler_abstraction.h"

static ble_command_resp_decode_callback_t m_cmd_resp_decode_callback;   /**< BLE command response decode callback function. */
static ble_encode_cmd_resp_handler_t      m_ble_cmd_resp_handler;       /**< BLE command response application callback function. */
static ble_encode_event_handler_t         m_ble_event_handler;          /**< BLE event application callback function. */  
static const uint8_t *                    mp_evt_buffer;                /**< Pointer to begin of received BLE event buffer. */  
static uint16_t                           m_evt_length;                 /**< Length of data in bytes in BLE event buffer. */  
static ble_encode_cmd_write_mode_t        m_cmd_write_mode;             /**< @ref ble_encode_transport_cmd_write API command mode. */                                      
static volatile uint32_t                  m_event_flags;                /**< Variable for storing boolean event flags. */

#define FLAG_CMD_RESP_HANDLER_REGISTERED (1u << 0)                      /**< Flag for determining is command response handler registered or not. */
#define FLAG_EVT_AVAILABLE_FOR_POP       (1u << 1u)                     /**< Flag for determining is event available for application extraction. */
#define FLAG_CMD_WRITE_INPROGRESS        (1u << 2u)                     /**< Flag for determining is command write in progress. */


/**@brief Function for processing BLE command response.
 * 
 * @param[in] p_buffer  Pointer to the begin of command response after packet type field.
 * @param[in] length    Length of data in bytes. 
 */
static __INLINE void ble_command_response_process(const uint8_t * p_buffer, uint32_t length)
{    
    // @note: System design does not allow any valid use case for the callback to be NULL  
    // which will imply design error that must be fixed at compile time.    
    APP_ERROR_CHECK_BOOL(m_cmd_resp_decode_callback != NULL);      
    const uint32_t result_code = m_cmd_resp_decode_callback(p_buffer, length);
    
    // @note: Relevant flags must be cleared before notifying the application of command response 
    // reception due to the fact that application can call back within the same context.
    m_event_flags &= ~(FLAG_CMD_WRITE_INPROGRESS | FLAG_CMD_RESP_HANDLER_REGISTERED);

    // @note: System design does not allow any valid use case for the callback to be NULL  
    // which will imply design error that must be fixed at compile time.    
    APP_ERROR_CHECK_BOOL(m_ble_cmd_resp_handler != NULL);          
    m_ble_cmd_resp_handler(result_code);    
}


/**@brief Function for processing BLE event.
 * 
 * @param[in] p_buffer  Pointer to the begin of event after packet type field.
 * @param[in] length    Length of data in bytes. 
 */
static __INLINE void ble_event_process(const uint8_t * p_buffer, uint32_t length)
{
    // @note: System design does not allow any valid use case for the callback to be NULL  
    // which will imply design error that must be fixed at compile time.    
    APP_ERROR_CHECK_BOOL(m_ble_event_handler != NULL);      
    
    mp_evt_buffer = p_buffer;
    m_evt_length  = length;
    
    m_event_flags |= FLAG_EVT_AVAILABLE_FOR_POP;    
    m_ble_event_handler(BLE_ENCODE_EVT_RDY);
}


/**@brief Function for processing RX packet ready event from transport layer.
 */
static __INLINE void rx_packet_ready_event_process(void)
{    
    uint8_t * p_buffer;
    uint16_t  length;
    
    // @note: This implementation is based on system design where max 1 RX buffer is available.
    // On any other design this system will fail as multiple received events can override existing 
    // received event if application does not pop the event out within the current context. 
    if (!(m_event_flags & FLAG_EVT_AVAILABLE_FOR_POP))
    {
        uint32_t err_code = hci_transport_rx_pkt_extract(&p_buffer, &length);
        // @note: System design does not allow any valid use case for the RX packet extract failure 
        // which will imply design error that must be fixed at compile time.
        APP_ERROR_CHECK(err_code);  
        
        const uint8_t packet_type = p_buffer[0];    // @todo: use #define for 0
        
        switch (packet_type)
        {
            case BLE_RPC_PKT_RESP:
            case BLE_RPC_PKT_DTM_RESP:
                // Adjust buffer begin pointer and length values after bypassing the packet type 
                // field. 
                ble_command_response_process(&(p_buffer[1]), --length);  // @todo: use #define for 1
                
                // @todo: consider moving consume prior application completion event.
                err_code = hci_transport_rx_pkt_consume(p_buffer);
                // @note: System design does not allow any valid use case for the RX packet consume 
                // failure which will imply design error that must be fixed at compile time.
                APP_ERROR_CHECK(err_code);                      
                break;
                
            case BLE_RPC_PKT_EVT:
                // Adjust buffer begin pointer and length values after bypassing the packet type 
                // field.             
                ble_event_process(&(p_buffer[1]), --length); // @todo: use #define for 1
                break;
                
            default:
                // @note: Should never happen.
                APP_ERROR_HANDLER(packet_type);
                break;
        }    
    }
    else
    {
        // @note: Should never happen.
        APP_ERROR_HANDLER(m_event_flags);    
    }    
}


/**@brief Function for processing events from transport layer.
 * 
 * @param[in] event     Transport layer event to process.
 */
static void transport_event_process(hci_transport_evt_t event)
{     
    switch (event.evt_type)
    {
        case HCI_TRANSPORT_RX_RDY:
            rx_packet_ready_event_process();
            break;
        case HCI_TRANSPORT_RX_STARTED:
            break;
        default:
            // @note: Should never happen.
            APP_ERROR_HANDLER(event.evt_type);
            break;
    }    
}


/**@brief Function for processing TX done event from transport layer.
 *
 * @param[in] result    TX done event result code. 
 */
static void transport_tx_done_process(hci_transport_tx_done_result_t result)
{
    APP_ERROR_CHECK_BOOL(result == HCI_TRANSPORT_TX_DONE_SUCCESS);
    
    // Actions are only executed if no command response is required for the transmitted command.
    if (m_cmd_write_mode == BLE_ENCODE_WRITE_MODE_NO_RESP)
    {    
        // @note: System design does not allow any valid use case for the callback to be NULL  
        // which will imply design error that must be fixed at compile time.    
        APP_ERROR_CHECK_BOOL(m_cmd_resp_decode_callback != NULL);      
        const uint32_t result_code = m_cmd_resp_decode_callback(NULL, 0);
        
        // @note: Relevant flags must be cleared before notifying the application of command 
        // processing completion due to the fact that application can call back within the same 
        // context.
        m_event_flags &= ~(FLAG_CMD_WRITE_INPROGRESS | FLAG_CMD_RESP_HANDLER_REGISTERED);

        // @note: System design does not allow any valid use case for the callback to be NULL  
        // which will imply design error that must be fixed at compile time.    
        APP_ERROR_CHECK_BOOL(m_ble_cmd_resp_handler != NULL);          
        m_ble_cmd_resp_handler(result_code);    
    }
}


uint32_t ble_encode_open(void)
{   
    uint32_t err_code = hci_transport_evt_handler_reg(transport_event_process);    
    // @note: Should never fail.
    APP_ERROR_CHECK(err_code);                
    err_code = hci_transport_tx_done_register(transport_tx_done_process);    
    // @note: Should never fail.
    APP_ERROR_CHECK(err_code);                    

    return hci_transport_open();
}


uint32_t ble_encode_close(void)
{
    m_event_flags              = 0;
    m_evt_length               = 0;  
    m_ble_cmd_resp_handler     = NULL;
    m_cmd_resp_decode_callback = NULL;
    m_ble_event_handler        = NULL;
    mp_evt_buffer              = NULL;
    m_cmd_write_mode           = BLE_ENCODE_WRITE_MODE_MAX;    
    
    return hci_transport_close();
}


uint32_t ble_encode_cmd_resp_handler_reg(ble_encode_cmd_resp_handler_t ble_command_resp_handler)
{
    if (ble_command_resp_handler == NULL)
    {
        return NRF_ERROR_NULL;
    }
        
    uint32_t err_code;
    
    if (!(m_event_flags & FLAG_CMD_RESP_HANDLER_REGISTERED))
    {
        m_event_flags         |= FLAG_CMD_RESP_HANDLER_REGISTERED;
        m_ble_cmd_resp_handler = ble_command_resp_handler;
        err_code               = NRF_SUCCESS;    
    }
    else
    {
        err_code = NRF_ERROR_BUSY;
    }
    
    return err_code;
}


uint32_t ble_encode_evt_handler_register(ble_encode_event_handler_t ble_event_handler)
{
    if (ble_event_handler == NULL)
    {
        return NRF_ERROR_NULL;
    }
    
    m_ble_event_handler = ble_event_handler;
    
    return NRF_SUCCESS;
}


uint8_t * ble_encode_transport_tx_alloc(void)
{
    uint8_t * p_buffer;
    uint32_t err_code;

    // This should be called only from main application context and not from interrupt, otherwise it
    // will block the system.
    do
    {
        err_code = hci_transport_tx_alloc(&p_buffer);

        //__WFE();
    }
    while (err_code == NRF_ERROR_NO_MEM);

    
    return p_buffer;
}


void ble_encode_transport_tx_free(void)
{
    const uint32_t err_code = hci_transport_tx_free();
    // @note: Should never fail.
    APP_ERROR_CHECK(err_code);        
}


void ble_encode_transport_cmd_write(const uint8_t *                    p_buffer, 
                                    uint32_t                           length, 
                                    ble_encode_cmd_write_mode_t        cmd_write_mode,                                    
                                    ble_command_resp_decode_callback_t cmd_resp_decode_callback)
{    
    APP_ERROR_CHECK_BOOL(!(m_event_flags & FLAG_CMD_WRITE_INPROGRESS));    
    APP_ERROR_CHECK_BOOL(p_buffer != NULL);
    APP_ERROR_CHECK_BOOL(length != 0);    
    APP_ERROR_CHECK_BOOL((cmd_write_mode == BLE_ENCODE_WRITE_MODE_RESP) || 
                         (cmd_write_mode == BLE_ENCODE_WRITE_MODE_NO_RESP));        
    APP_ERROR_CHECK_BOOL(cmd_resp_decode_callback != NULL);        
    
    m_event_flags             |= FLAG_CMD_WRITE_INPROGRESS;
    m_cmd_write_mode           = cmd_write_mode;
    m_cmd_resp_decode_callback = cmd_resp_decode_callback;
    
    const uint32_t err_code = hci_transport_pkt_write(p_buffer, length);
    // @note: Should never fail as system design allows only 1 command to be in progress at any 
    // time.
    APP_ERROR_CHECK(err_code);    
}                                    


uint32_t ble_encode_event_pop(ble_evt_t * p_event, uint32_t * p_event_len)
{
    uint32_t err_code;
    
    if (p_event_len == NULL)
    {
        return NRF_ERROR_NULL;
    }    

    if (m_event_flags & FLAG_EVT_AVAILABLE_FOR_POP)
    {
        m_event_flags &= ~FLAG_EVT_AVAILABLE_FOR_POP;    
        
        err_code = ble_event_dec(mp_evt_buffer, m_evt_length, p_event, p_event_len);
        // @note: Should never happen. 
        APP_ERROR_CHECK_BOOL((err_code == NRF_SUCCESS) || (err_code == NRF_ERROR_DATA_SIZE));
        
        if ((err_code == NRF_SUCCESS) && (p_event != NULL))
        {
            // @note: (p_event != NULL) check needs to be included in order to cover the p_event 
            // length query use case.
        
            // @note: Decrement buffer pointer to original received from 
            // @ref hci_transport_rx_pkt_extract.
            --mp_evt_buffer;
            err_code = hci_transport_rx_pkt_consume((uint8_t *)mp_evt_buffer);
            // @note: System design does not allow any valid use case for the RX packet consume failure 
            // which will imply design error that must be fixed at compile time.
            APP_ERROR_CHECK(err_code);                      
        }
    }
    else
    {
        err_code = NRF_ERROR_NO_MEM;
    }
            
    return err_code;
}