add files from nrf52832 bootloader project

This commit is contained in:
hathach
2018-02-07 23:32:49 +07:00
parent ac1f0e7955
commit 9f1d9f321e
186 changed files with 83021 additions and 0 deletions

View File

@ -0,0 +1,32 @@
/* 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.
*
*/
/** @file
*
* @defgroup memory_pool_internal Memory Pool Internal
* @{
* @ingroup memory_pool
*
* @brief Memory pool internal definitions
*/
#ifndef MEM_POOL_INTERNAL_H__
#define MEM_POOL_INTERNAL_H__
#define TX_BUF_SIZE 4u /**< TX buffer size in bytes. */
#define RX_BUF_SIZE 32u /**< RX buffer size in bytes. */
#define RX_BUF_QUEUE_SIZE 8u /**< RX buffer element size. */
#endif // MEM_POOL_INTERNAL_H__
/** @} */

View File

@ -0,0 +1,457 @@
/* 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 "bootloader.h"
#include "bootloader_types.h"
#include "bootloader_util.h"
#include "bootloader_settings.h"
#include "dfu.h"
#include "dfu_transport.h"
#include "nrf.h"
#include "app_error.h"
#include "nrf_sdm.h"
#include "nrf_mbr.h"
#include "nordic_common.h"
#include "crc16.h"
#include "pstorage.h"
#include "app_scheduler.h"
#include "nrf_delay.h"
#include "sdk_common.h"
#include "app_timer_appsh.h"
#define APP_TIMER_PRESCALER 0
#define IRQ_ENABLED 0x01 /**< Field identifying if an interrupt is enabled. */
#ifdef NRF52
#define MAX_NUMBER_INTERRUPTS 39
#else
#define MAX_NUMBER_INTERRUPTS 32 /**< Maximum number of interrupts available. */
#endif
/**@brief Enumeration for specifying current bootloader status.
*/
typedef enum
{
BOOTLOADER_UPDATING, /**< Bootloader status for indicating that an update is in progress. */
BOOTLOADER_SETTINGS_SAVING, /**< Bootloader status for indicating that saving of bootloader settings is in progress. */
BOOTLOADER_COMPLETE, /**< Bootloader status for indicating that all operations for the update procedure has completed and it is safe to reset the system. */
BOOTLOADER_TIMEOUT, /**< Bootloader status field for indicating that a timeout has occured and current update process should be aborted. */
BOOTLOADER_RESET, /**< Bootloader status field for indicating that a reset has been requested and current update process should be aborted. */
} bootloader_status_t;
static pstorage_handle_t m_bootsettings_handle; /**< Pstorage handle to use for registration and identifying the bootloader module on subsequent calls to the pstorage module for load and store of bootloader setting in flash. */
static bootloader_status_t m_update_status; /**< Current update status for the bootloader module to ensure correct behaviour when updating settings and when update completes. */
// Adafruit modification for dual transports and forced startup DFU
extern bool is_ota(void);
APP_TIMER_DEF( _forced_startup_dfu_timer );
volatile bool forced_startup_dfu_packet_received = false;
volatile static bool _terminate_startup_dfu = false;
/**@brief Function for handling callbacks from pstorage module.
*
* @details Handles pstorage results for clear and storage operation. For detailed description of
* the parameters provided with the callback, please refer to \ref pstorage_ntf_cb_t.
*/
static void pstorage_callback_handler(pstorage_handle_t * p_handle,
uint8_t op_code,
uint32_t result,
uint8_t * p_data,
uint32_t data_len)
{
// If we are in BOOTLOADER_SETTINGS_SAVING state and we receive an PSTORAGE_STORE_OP_CODE
// response then settings has been saved and update has completed.
if ((m_update_status == BOOTLOADER_SETTINGS_SAVING) && (op_code == PSTORAGE_STORE_OP_CODE))
{
m_update_status = BOOTLOADER_COMPLETE;
}
APP_ERROR_CHECK(result);
}
// Adafruit modifcation
static void terminate_startup_dfu(void * p_event_data, uint16_t event_size)
{
(void) p_event_data;
(void) event_size;
_terminate_startup_dfu = true;
}
/* Terminate the forced DFU mode on startup if no packets is received
* by put an terminal handler to scheduler
*/
static void forced_startup_dfu_timer_handler(void * p_context)
{
// No packets are received within timeout, terminal and DFU mode
// forced_startup_dfu_packet_received is set by process_dfu_packet() in dfu_transport_serial.c
if (!forced_startup_dfu_packet_received)
{
app_sched_event_put(NULL, 0, terminate_startup_dfu);
}
}
/**@brief Function for waiting for events.
*
* @details This function will place the chip in low power mode while waiting for events from
* the SoftDevice or other peripherals. When interrupted by an event, it will call the
* @ref app_sched_execute function to process the received event. This function will return
* when the final state of the firmware update is reached OR when a tear down is in
* progress.
*/
static void wait_for_events(void)
{
for (;;)
{
// Wait in low power state for any events.
uint32_t err_code = sd_app_evt_wait();
APP_ERROR_CHECK(err_code);
// Event received. Process it from the scheduler.
app_sched_execute();
if ((m_update_status == BOOTLOADER_COMPLETE) ||
(m_update_status == BOOTLOADER_TIMEOUT) ||
(m_update_status == BOOTLOADER_RESET))
{
// When update has completed or a timeout/reset occured we will return.
return;
}
// Forced startup dfu mode timeout without any received packet
if (_terminate_startup_dfu)
{
return;
}
}
}
bool bootloader_app_is_valid(uint32_t app_addr)
{
const bootloader_settings_t * p_bootloader_settings;
// There exists an application in CODE region 1.
if (*((uint32_t *)app_addr) == EMPTY_FLASH_MASK)
{
return false;
}
bool success = false;
bootloader_util_settings_get(&p_bootloader_settings);
// The application in CODE region 1 is flagged as valid during update.
if (p_bootloader_settings->bank_0 == BANK_VALID_APP)
{
uint16_t image_crc = 0;
// A stored crc value of 0 indicates that CRC checking is not used.
if (p_bootloader_settings->bank_0_crc != 0)
{
image_crc = crc16_compute((uint8_t *)DFU_BANK_0_REGION_START,
p_bootloader_settings->bank_0_size,
NULL);
}
success = (image_crc == p_bootloader_settings->bank_0_crc);
}
return success;
}
static void bootloader_settings_save(bootloader_settings_t * p_settings)
{
uint32_t err_code = pstorage_clear(&m_bootsettings_handle, sizeof(bootloader_settings_t));
APP_ERROR_CHECK(err_code);
err_code = pstorage_store(&m_bootsettings_handle,
(uint8_t *)p_settings,
sizeof(bootloader_settings_t),
0);
APP_ERROR_CHECK(err_code);
}
void bootloader_dfu_update_process(dfu_update_status_t update_status)
{
static bootloader_settings_t settings;
const bootloader_settings_t * p_bootloader_settings;
bootloader_util_settings_get(&p_bootloader_settings);
if (update_status.status_code == DFU_UPDATE_APP_COMPLETE)
{
settings.bank_0_crc = update_status.app_crc;
settings.bank_0_size = update_status.app_size;
settings.bank_0 = BANK_VALID_APP;
settings.bank_1 = BANK_INVALID_APP;
m_update_status = BOOTLOADER_SETTINGS_SAVING;
bootloader_settings_save(&settings);
}
else if (update_status.status_code == DFU_UPDATE_SD_COMPLETE)
{
settings.bank_0_crc = update_status.app_crc;
settings.bank_0_size = update_status.sd_size +
update_status.bl_size +
update_status.app_size;
settings.bank_0 = BANK_VALID_SD;
settings.bank_1 = BANK_INVALID_APP;
settings.sd_image_size = update_status.sd_size;
settings.bl_image_size = update_status.bl_size;
settings.app_image_size = update_status.app_size;
settings.sd_image_start = update_status.sd_image_start;
m_update_status = BOOTLOADER_SETTINGS_SAVING;
bootloader_settings_save(&settings);
}
else if (update_status.status_code == DFU_UPDATE_BOOT_COMPLETE)
{
settings.bank_0 = p_bootloader_settings->bank_0;
settings.bank_0_crc = p_bootloader_settings->bank_0_crc;
settings.bank_0_size = p_bootloader_settings->bank_0_size;
settings.bank_1 = BANK_VALID_BOOT;
settings.sd_image_size = update_status.sd_size;
settings.bl_image_size = update_status.bl_size;
settings.app_image_size = update_status.app_size;
m_update_status = BOOTLOADER_SETTINGS_SAVING;
bootloader_settings_save(&settings);
}
else if (update_status.status_code == DFU_UPDATE_SD_SWAPPED)
{
if (p_bootloader_settings->bank_0 == BANK_VALID_SD)
{
settings.bank_0_crc = 0;
settings.bank_0_size = 0;
settings.bank_0 = BANK_INVALID_APP;
}
// This handles cases where SoftDevice was not updated, hence bank0 keeps its settings.
else
{
settings.bank_0 = p_bootloader_settings->bank_0;
settings.bank_0_crc = p_bootloader_settings->bank_0_crc;
settings.bank_0_size = p_bootloader_settings->bank_0_size;
}
settings.bank_1 = BANK_INVALID_APP;
settings.sd_image_size = 0;
settings.bl_image_size = 0;
settings.app_image_size = 0;
m_update_status = BOOTLOADER_SETTINGS_SAVING;
bootloader_settings_save(&settings);
}
else if (update_status.status_code == DFU_TIMEOUT)
{
// Timeout has occurred. Close the connection with the DFU Controller.
uint32_t err_code;
if ( is_ota() )
{
err_code = dfu_transport_ble_close();
}else
{
err_code = dfu_transport_serial_close();
}
APP_ERROR_CHECK(err_code);
m_update_status = BOOTLOADER_TIMEOUT;
}
else if (update_status.status_code == DFU_BANK_0_ERASED)
{
settings.bank_0_crc = 0;
settings.bank_0_size = 0;
settings.bank_0 = BANK_INVALID_APP;
settings.bank_1 = p_bootloader_settings->bank_1;
bootloader_settings_save(&settings);
}
else if (update_status.status_code == DFU_RESET)
{
m_update_status = BOOTLOADER_RESET;
}
else
{
// No implementation needed.
}
}
uint32_t bootloader_init(void)
{
uint32_t err_code;
pstorage_module_param_t storage_params = {.cb = pstorage_callback_handler};
err_code = pstorage_init();
VERIFY_SUCCESS(err_code);
m_bootsettings_handle.block_id = BOOTLOADER_SETTINGS_ADDRESS;
err_code = pstorage_register(&storage_params, &m_bootsettings_handle);
return err_code;
}
uint32_t bootloader_dfu_start(bool ota, uint32_t timeout_ms)
{
uint32_t err_code;
// Clear swap if banked update is used.
err_code = dfu_init();
VERIFY_SUCCESS(err_code);
if ( ota )
{
err_code = dfu_transport_ble_update_start();
}else
{
// timeout_ms > 0 is forced startup DFU
if ( timeout_ms )
{
forced_startup_dfu_packet_received = false;
_terminate_startup_dfu = false;
(void) app_timer_create(&_forced_startup_dfu_timer, APP_TIMER_MODE_SINGLE_SHOT, forced_startup_dfu_timer_handler);
app_timer_start(_forced_startup_dfu_timer, APP_TIMER_TICKS(timeout_ms, APP_TIMER_PRESCALER), NULL);
}
err_code = dfu_transport_serial_update_start();
}
wait_for_events();
// Close Serial transport after done
if ( !ota ) dfu_transport_serial_close();
return err_code;
}
/**@brief Function for disabling all interrupts before jumping from bootloader to application.
*/
static void interrupts_disable(void)
{
uint32_t interrupt_setting_mask;
uint32_t irq = 0; // We start from first interrupt, i.e. interrupt 0.
// Fetch the current interrupt settings.
interrupt_setting_mask = NVIC->ISER[0];
for (; irq < MAX_NUMBER_INTERRUPTS; irq++)
{
if (interrupt_setting_mask & (IRQ_ENABLED << irq))
{
// The interrupt was enabled, and hence disable it.
NVIC_DisableIRQ((IRQn_Type)irq);
}
}
}
void bootloader_app_start(uint32_t app_addr)
{
// If the applications CRC has been checked and passed, the magic number will be written and we
// can start the application safely.
uint32_t err_code = sd_softdevice_disable();
APP_ERROR_CHECK(err_code);
interrupts_disable();
// Disable RTC1
NRF_RTC1->EVTENCLR = RTC_EVTEN_COMPARE0_Msk;
NRF_RTC1->INTENCLR = RTC_INTENSET_COMPARE0_Msk;
NRF_RTC1->TASKS_STOP = 1;
NRF_RTC1->TASKS_CLEAR = 1;
err_code = sd_softdevice_vector_table_base_set(CODE_REGION_1_START);
APP_ERROR_CHECK(err_code);
bootloader_util_app_start(CODE_REGION_1_START);
}
bool bootloader_dfu_sd_in_progress(void)
{
const bootloader_settings_t * p_bootloader_settings;
bootloader_util_settings_get(&p_bootloader_settings);
if (p_bootloader_settings->bank_0 == BANK_VALID_SD ||
p_bootloader_settings->bank_1 == BANK_VALID_BOOT)
{
return true;
}
return false;
}
uint32_t bootloader_dfu_sd_update_continue(void)
{
uint32_t err_code;
if ((dfu_sd_image_validate() == NRF_SUCCESS) &&
(dfu_bl_image_validate() == NRF_SUCCESS))
{
return NRF_SUCCESS;
}
// Ensure that flash operations are not executed within the first 100 ms seconds to allow
// a debugger to be attached.
nrf_delay_ms(100);
err_code = dfu_sd_image_swap();
APP_ERROR_CHECK(err_code);
err_code = dfu_sd_image_validate();
APP_ERROR_CHECK(err_code);
err_code = dfu_bl_image_swap();
APP_ERROR_CHECK(err_code);
return err_code;
}
uint32_t bootloader_dfu_sd_update_finalize(void)
{
dfu_update_status_t update_status = {DFU_UPDATE_SD_SWAPPED, };
bootloader_dfu_update_process(update_status);
wait_for_events();
return NRF_SUCCESS;
}
void bootloader_settings_get(bootloader_settings_t * const p_settings)
{
const bootloader_settings_t * p_bootloader_settings;
bootloader_util_settings_get(&p_bootloader_settings);
p_settings->bank_0 = p_bootloader_settings->bank_0;
p_settings->bank_0_crc = p_bootloader_settings->bank_0_crc;
p_settings->bank_0_size = p_bootloader_settings->bank_0_size;
p_settings->bank_1 = p_bootloader_settings->bank_1;
p_settings->sd_image_size = p_bootloader_settings->sd_image_size;
p_settings->bl_image_size = p_bootloader_settings->bl_image_size;
p_settings->app_image_size = p_bootloader_settings->app_image_size;
p_settings->sd_image_start = p_bootloader_settings->sd_image_start;
}

View File

@ -0,0 +1,97 @@
/* 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.
*
*/
/**@file
*
* @defgroup nrf_bootloader Bootloader API.
* @{
*
* @brief Bootloader module interface.
*/
#ifndef BOOTLOADER_H__
#define BOOTLOADER_H__
#include <stdbool.h>
#include <stdint.h>
#include "bootloader_types.h"
#include <dfu_types.h>
/**@brief Function for initializing the Bootloader.
*
* @retval NRF_SUCCESS If bootloader was succesfully initialized.
*/
uint32_t bootloader_init(void);
/**@brief Function for validating application region in flash.
*
* @param[in] app_addr Address to the region in flash where the application is stored.
*
* @retval true If Application region is valid.
* @retval false If Application region is not valid.
*/
bool bootloader_app_is_valid(uint32_t app_addr);
/**@brief Function for starting the Device Firmware Update.
*
* @retval NRF_SUCCESS If new application image was successfully transferred.
*/
uint32_t bootloader_dfu_start(bool ota, uint32_t timeout_ms);
/**@brief Function for exiting bootloader and booting into application.
*
* @details This function will disable SoftDevice and all interrupts before jumping to application.
* The SoftDevice vector table base for interrupt forwarding will be set the application
* address.
*
* @param[in] app_addr Address to the region where the application is stored.
*/
void bootloader_app_start(uint32_t app_addr);
/**@brief Function for retrieving the bootloader settings.
*
* @param[out] p_settings A copy of the current bootloader settings is returned in the structure
* provided.
*/
void bootloader_settings_get(bootloader_settings_t * const p_settings);
/**@brief Function for processing DFU status update.
*
* @param[in] update_status DFU update status.
*/
void bootloader_dfu_update_process(dfu_update_status_t update_status);
/**@brief Function getting state of SoftDevice update in progress.
* After a successfull SoftDevice transfer the system restarts in orderto disable SoftDevice
* and complete the update.
*
* @retval true A SoftDevice update is in progress. This indicates that second stage
* of a SoftDevice update procedure can be initiated.
* @retval false No SoftDevice update is in progress.
*/
bool bootloader_dfu_sd_in_progress(void);
/**@brief Function for continuing the Device Firmware Update of a SoftDevice.
*
* @retval NRF_SUCCESS If the final stage of SoftDevice update was successful.
*/
uint32_t bootloader_dfu_sd_update_continue(void);
/**@brief Function for finalizing the Device Firmware Update of a SoftDevice.
*
* @retval NRF_SUCCESS If the final stage of SoftDevice update was successful.
*/
uint32_t bootloader_dfu_sd_update_finalize(void);
#endif // BOOTLOADER_H__
/**@} */

View File

@ -0,0 +1,63 @@
/* 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 "bootloader_settings.h"
#include <stdint.h>
#include <dfu_types.h>
#if defined ( __CC_ARM )
uint8_t m_boot_settings[CODE_PAGE_SIZE] __attribute__((at(BOOTLOADER_SETTINGS_ADDRESS))) __attribute__((used)); /**< This variable reserves a codepage for bootloader specific settings, to ensure the compiler doesn't locate any code or variables at his location. */
uint32_t m_uicr_bootloader_start_address __attribute__((at(NRF_UICR_BOOT_START_ADDRESS))) = BOOTLOADER_REGION_START; /**< This variable makes the linker script write the bootloader start address to the UICR register. This value will be written in the HEX file and thus written to UICR when the bootloader is flashed into the chip. */
#elif defined ( __GNUC__ )
uint8_t m_boot_settings[CODE_PAGE_SIZE] __attribute__ ((section(".bootloaderSettings"))); /**< This variable reserves a codepage for bootloader specific settings, to ensure the compiler doesn't locate any code or variables at his location. */
volatile uint32_t m_uicr_bootloader_start_address __attribute__ ((section(".uicrBootStartAddress"))) = BOOTLOADER_REGION_START; /**< This variable ensures that the linker script will write the bootloader start address to the UICR register. This value will be written in the HEX file and thus written to UICR when the bootloader is flashed into the chip. */
#elif defined ( __ICCARM__ )
__no_init uint8_t m_boot_settings[CODE_PAGE_SIZE] @ BOOTLOADER_SETTINGS_ADDRESS; /**< This variable reserves a codepage for bootloader specific settings, to ensure the compiler doesn't locate any code or variables at his location. */
__root const uint32_t m_uicr_bootloader_start_address @ NRF_UICR_BOOT_START_ADDRESS = BOOTLOADER_REGION_START; /**< This variable ensures that the linker script will write the bootloader start address to the UICR register. This value will be written in the HEX file and thus written to UICR when the bootloader is flashed into the chip. */
#endif
#if defined ( NRF52 )
#if defined ( __CC_ARM )
uint8_t m_mbr_params_page[CODE_PAGE_SIZE] __attribute__((at(BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS))) __attribute__((used)); /**< This variable reserves a codepage for mbr parameters, to ensure the compiler doesn't locate any code or variables at his location. */
uint32_t m_uicr_mbr_params_page_address __attribute__((at(NRF_UICR_MBR_PARAMS_PAGE_ADDRESS)))
= BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS; /**< This variable makes the linker script write the mbr parameters page address to the UICR register. This value will be written in the HEX file and thus written to the UICR when the bootloader is flashed into the chip */
#elif defined (__GNUC__ )
uint8_t m_mbr_params_page[CODE_PAGE_SIZE] __attribute__ ((section(".mbrParamsPage"))); /**< This variable reserves a codepage for mbr parameters, to ensure the compiler doesn't locate any code or variables at his location. */
volatile uint32_t m_uicr_mbr_params_page_address __attribute__ ((section(".uicrMbrParamsPageAddress")))
= BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS; /**< This variable makes the linker script write the mbr parameters page address to the UICR register. This value will be written in the HEX file and thus written to the UICR when the bootloader is flashed into the chip */
#elif defined (__ICCARM__ )
__no_init uint8_t m_mbr_params_page[CODE_PAGE_SIZE] @ BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS; /**< This variable reserves a codepage for bootloader specific settings, to ensure the compiler doesn't locate any code or variables at his location. */
__root const uint32_t m_uicr_mbr_params_page_address @ NRF_UICR_MBR_PARAMS_PAGE_ADDRESS = BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS; /**< This variable ensures that the linker script will write the bootloader start address to the UICR register. This value will be written in the HEX file and thus written to UICR when the bootloader is flashed into the chip. */
#endif
#endif //defined ( NRF52 )
void bootloader_util_settings_get(const bootloader_settings_t ** pp_bootloader_settings)
{
// Read only pointer to bootloader settings in flash.
bootloader_settings_t const * const p_bootloader_settings =
(bootloader_settings_t *)&m_boot_settings[0];
*pp_bootloader_settings = p_bootloader_settings;
}

View File

@ -0,0 +1,35 @@
/* Copyright (c) 2014 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.
*
*/
/**@file
*
* @defgroup nrf_bootloader_settings Bootloader settings API.
* @{
*
* @brief Bootloader settings module interface.
*/
#ifndef BOOTLOADER_SETTINGS_H__
#define BOOTLOADER_SETTINGS_H__
#include <stdint.h>
#include "bootloader_types.h"
/**@brief Function for getting the bootloader settings.
*
* @param[out] pp_bootloader_settings Bootloader settings.
*/
void bootloader_util_settings_get(const bootloader_settings_t ** pp_bootloader_settings);
#endif // BOOTLOADER_SETTINGS_H__
/**@} */

View File

@ -0,0 +1,59 @@
/* 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.
*
*/
/**@file
*
* @defgroup nrf_bootloader_types Types and definitions.
* @{
*
* @ingroup nrf_bootloader
*
* @brief Bootloader module type and definitions.
*/
#ifndef BOOTLOADER_TYPES_H__
#define BOOTLOADER_TYPES_H__
#include <stdint.h>
#define BOOTLOADER_DFU_START 0xB1
#define BOOTLOADER_SVC_APP_DATA_PTR_GET 0x02
/**@brief DFU Bank state code, which indicates wether the bank contains: A valid image, invalid image, or an erased flash.
*/
typedef enum
{
BANK_VALID_APP = 0x01,
BANK_VALID_SD = 0xA5,
BANK_VALID_BOOT = 0xAA,
BANK_ERASED = 0xFE,
BANK_INVALID_APP = 0xFF,
} bootloader_bank_code_t;
/**@brief Structure holding bootloader settings for application and bank data.
*/
typedef struct
{
bootloader_bank_code_t bank_0; /**< Variable to store if bank 0 contains a valid application. */
uint16_t bank_0_crc; /**< If bank is valid, this field will contain a valid CRC of the total image. */
bootloader_bank_code_t bank_1; /**< Variable to store if bank 1 has been erased/prepared for new image. Bank 1 is only used in Banked Update scenario. */
uint32_t bank_0_size; /**< Size of active image in bank0 if present, otherwise 0. */
uint32_t sd_image_size; /**< Size of SoftDevice image in bank0 if bank_0 code is BANK_VALID_SD. */
uint32_t bl_image_size; /**< Size of Bootloader image in bank0 if bank_0 code is BANK_VALID_SD. */
uint32_t app_image_size; /**< Size of Application image in bank0 if bank_0 code is BANK_VALID_SD. */
uint32_t sd_image_start; /**< Location in flash where SoftDevice image is stored for SoftDevice update. */
} bootloader_settings_t;
#endif // BOOTLOADER_TYPES_H__
/**@} */

View File

@ -0,0 +1,152 @@
/* 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 "bootloader_util.h"
#include <stdint.h>
#include <string.h>
/**
* @brief Function for aborting current application/bootloader jump to to other app/bootloader.
*
* @details This functions will use the address provide to swap the stack pointer and then load
* the address of the reset handler to be executed. It will check current system mode
* (thread/handler) and if in thread mode it will reset into other application.
* If in handler mode \ref isr_abort will be executed to ensure correct exit of handler
* mode and jump into reset handler of other application.
*
* @param[in] start_addr Start address of other application. This address must point to the
initial stack pointer of the application.
*
* @note This function will never return but issue a reset into provided application.
*/
#if defined ( __CC_ARM )
__asm static void bootloader_util_reset(uint32_t start_addr)
{
LDR R5, [R0] ; Get App initial MSP for bootloader.
MSR MSP, R5 ; Set the main stack pointer to the applications MSP.
LDR R0, [R0, #0x04] ; Load Reset handler into R0. This will be first argument to branch instruction (BX).
MOVS R4, #0xFF ; Load ones to R4.
SXTB R4, R4 ; Sign extend R4 to obtain 0xFFFFFFFF instead of 0xFF.
MRS R5, IPSR ; Load IPSR to R5 to check for handler or thread mode.
CMP R5, #0x00 ; Compare, if 0 then we are in thread mode and can continue to reset handler of bootloader.
BNE isr_abort ; If not zero we need to exit current ISR and jump to reset handler of bootloader.
MOV LR, R4 ; Clear the link register and set to ones to ensure no return, R4 = 0xFFFFFFFF.
BX R0 ; Branch to reset handler of bootloader.
isr_abort
; R4 contains ones from line above. Will be popped as R12 when exiting ISR (Cleaning up the registers).
MOV R5, R4 ; Fill with ones before jumping to reset handling. We be popped as LR when exiting ISR. Ensures no return to application.
MOV R6, R0 ; Move address of reset handler to R6. Will be popped as PC when exiting ISR. Ensures the reset handler will be executed when exist ISR.
MOVS r7, #0x21 ; Move MSB reset value of xPSR to R7. Will be popped as xPSR when exiting ISR. xPSR is 0x21000000 thus MSB is 0x21.
REV r7, r7 ; Reverse byte order to put 0x21 as MSB.
PUSH {r4-r7} ; Push everything to new stack to allow interrupt handler to fetch it on exiting the ISR.
MOVS R4, #0x00 ; Fill with zeros before jumping to reset handling. We be popped as R0 when exiting ISR (Cleaning up of the registers).
MOVS R5, #0x00 ; Fill with zeros before jumping to reset handling. We be popped as R1 when exiting ISR (Cleaning up of the registers).
MOVS R6, #0x00 ; Fill with zeros before jumping to reset handling. We be popped as R2 when exiting ISR (Cleaning up of the registers).
MOVS R7, #0x00 ; Fill with zeros before jumping to reset handling. We be popped as R3 when exiting ISR (Cleaning up of the registers).
PUSH {r4-r7} ; Push zeros (R4-R7) to stack to prepare for exiting the interrupt routine.
MOVS R0, #0xF9 ; Move the execution return command into register, 0xFFFFFFF9.
SXTB R0, R0 ; Sign extend R0 to obtain 0xFFFFFFF9 instead of 0xF9.
BX R0 ; No return - Handler mode will be exited. Stack will be popped and execution will continue in reset handler initializing other application.
ALIGN
}
#elif defined ( __GNUC__ )
static inline void bootloader_util_reset(uint32_t start_addr)
{
__asm volatile(
"ldr r0, [%0]\t\n" // Get App initial MSP for bootloader.
"msr msp, r0\t\n" // Set the main stack pointer to the applications MSP.
"ldr r0, [%0, #0x04]\t\n" // Load Reset handler into R0.
"movs r4, #0xFF\t\n" // Move ones to R4.
"sxtb r4, r4\t\n" // Sign extend R4 to obtain 0xFFFFFFFF instead of 0xFF.
"mrs r5, IPSR\t\n" // Load IPSR to R5 to check for handler or thread mode.
"cmp r5, #0x00\t\n" // Compare, if 0 then we are in thread mode and can continue to reset handler of bootloader.
"bne isr_abort\t\n" // If not zero we need to exit current ISR and jump to reset handler of bootloader.
"mov lr, r4\t\n" // Clear the link register and set to ones to ensure no return.
"bx r0\t\n" // Branch to reset handler of bootloader.
"isr_abort: \t\n"
"mov r5, r4\t\n" // Fill with ones before jumping to reset handling. Will be popped as LR when exiting ISR. Ensures no return to application.
"mov r6, r0\t\n" // Move address of reset handler to R6. Will be popped as PC when exiting ISR. Ensures the reset handler will be executed when exist ISR.
"movs r7, #0x21\t\n" // Move MSB reset value of xPSR to R7. Will be popped as xPSR when exiting ISR. xPSR is 0x21000000 thus MSB is 0x21.
"rev r7, r7\t\n" // Reverse byte order to put 0x21 as MSB.
"push {r4-r7}\t\n" // Push everything to new stack to allow interrupt handler to fetch it on exiting the ISR.
"movs r4, #0x00\t\n" // Fill with zeros before jumping to reset handling. We be popped as R0 when exiting ISR (Cleaning up of the registers).
"movs r5, #0x00\t\n" // Fill with zeros before jumping to reset handling. We be popped as R1 when exiting ISR (Cleaning up of the registers).
"movs r6, #0x00\t\n" // Fill with zeros before jumping to reset handling. We be popped as R2 when exiting ISR (Cleaning up of the registers).
"movs r7, #0x00\t\n" // Fill with zeros before jumping to reset handling. We be popped as R3 when exiting ISR (Cleaning up of the registers).
"push {r4-r7}\t\n" // Push zeros (R4-R7) to stack to prepare for exiting the interrupt routine.
"movs r0, #0xF9\t\n" // Move the execution return command into register, 0xFFFFFFF9.
"sxtb r0, r0\t\n" // Sign extend R0 to obtain 0xFFFFFFF9 instead of 0xF9.
"bx r0\t\n" // No return - Handler mode will be exited. Stack will be popped and execution will continue in reset handler initializing other application.
".align\t\n"
:: "r" (start_addr) // Argument list for the gcc assembly. start_addr is %0.
: "r0", "r4", "r5", "r6", "r7" // List of register maintained manually.
);
}
#elif defined ( __ICCARM__ )
static inline void bootloader_util_reset(uint32_t start_addr)
{
asm("ldr r5, [%0]\n" // Get App initial MSP for bootloader.
"msr msp, r5\n" // Set the main stack pointer to the applications MSP.
"ldr r0, [%0, #0x04]\n" // Load Reset handler into R0.
"movs r4, #0x00\n" // Load zero into R4.
"mvns r4, r4\n" // Invert R4 to ensure it contain ones.
"mrs r5, IPSR\n" // Load IPSR to R5 to check for handler or thread mode
"cmp r5, #0x00\n" // Compare, if 0 then we are in thread mode and can continue to reset handler of bootloader.
"bne.n isr_abort\n" // If not zero we need to exit current ISR and jump to reset handler of bootloader.
"mov lr, r4\n" // Clear the link register and set to ones to ensure no return.
"bx r0\n" // Branch to reset handler of bootloader.
"isr_abort: \n"
// R4 contains ones from line above. We be popped as R12 when exiting ISR (Cleaning up the registers).
"mov r5, r4\n" // Fill with ones before jumping to reset handling. Will be popped as LR when exiting ISR. Ensures no return to application.
"mov r6, r0\n" // Move address of reset handler to R6. Will be popped as PC when exiting ISR. Ensures the reset handler will be executed when exist ISR.
"movs r7, #0x21\n" // Move MSB reset value of xPSR to R7. Will be popped as xPSR when exiting ISR. xPSR is 0x21000000 thus MSB is 0x21.
"rev r7, r7\n" // Reverse byte order to put 0x21 as MSB.
"push {r4-r7}\n" // Push everything to new stack to allow interrupt handler to fetch it on exiting the ISR.
"movs r4, #0x00\n" // Fill with zeros before jumping to reset handling. We be popped as R0 when exiting ISR (Cleaning up of the registers).
"movs r5, #0x00\n" // Fill with zeros before jumping to reset handling. We be popped as R1 when exiting ISR (Cleaning up of the registers).
"movs r6, #0x00\n" // Fill with zeros before jumping to reset handling. We be popped as R2 when exiting ISR (Cleaning up of the registers).
"movs r7, #0x00\n" // Fill with zeros before jumping to reset handling. We be popped as R3 when exiting ISR (Cleaning up of the registers).
"push {r4-r7}\n" // Push zeros (R4-R7) to stack to prepare for exiting the interrupt routine.
"movs r0, #0x06\n" // Load 0x06 into R6 to prepare for exec return command.
"mvns r0, r0\n" // Invert 0x06 to obtain EXEC_RETURN, 0xFFFFFFF9.
"bx r0\n" // No return - Handler mode will be exited. Stack will be popped and execution will continue in reset handler initializing other application.
:: "r" (start_addr) // Argument list for the IAR assembly. start_addr is %0.
: "r0", "r4", "r5", "r6", "r7"); // List of register maintained manually.
}
#else
#error Compiler not supported.
#endif
void bootloader_util_app_start(uint32_t start_addr)
{
bootloader_util_reset(start_addr);
}

View File

@ -0,0 +1,38 @@
/* 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.
*
*/
/**@file
*
* @defgroup nrf_bootloader_util Bootloader util API.
* @{
*
* @brief Bootloader util module interface.
*/
#ifndef BOOTLOADER_UTIL_H__
#define BOOTLOADER_UTIL_H__
#include <stdint.h>
#include "bootloader_types.h"
/**@brief Function for starting the application (or bootloader) at the provided address.
*
* @param[in] start_addr Start address.
*
* @note This function will never retrun. Instead it will reset into the application of the
* provided address.
*/
void bootloader_util_app_start(uint32_t start_addr);
#endif // BOOTLOADER_UTIL_H__
/**@} */

View File

@ -0,0 +1,134 @@
/* 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.
*
*/
/**@file
*
* @defgroup nrf_dfu Device Firmware Update API.
* @{
*
* @brief Device Firmware Update module interface.
*/
#ifndef DFU_H__
#define DFU_H__
#include <dfu_types.h>
#include <stdbool.h>
#include <stdint.h>
/**@brief DFU event callback for asynchronous calls.
*
* @param[in] packet Packet type for which this callback is related. START_PACKET, DATA_PACKET.
* @param[in] result Operation result code. NRF_SUCCESS when a queued operation was successful.
* @param[in] p_data Pointer to the data to which the operation is related.
*/
typedef void (*dfu_callback_t)(uint32_t packet, uint32_t result, uint8_t * p_data);
/**@brief Function for initializing the Device Firmware Update module.
*
* @return NRF_SUCCESS on success, an error_code otherwise.
*/
uint32_t dfu_init(void);
/**@brief Function for registering a callback listener for \ref dfu_data_pkt_handle callbacks.
*
* @param[in] callback_handler Callback handler for receiving DFU events on completed operations
* of DFU packets.
*/
void dfu_register_callback(dfu_callback_t callback_handler);
/**@brief Function for setting the DFU image size.
*
* @details Function sets the DFU image size. This function must be called when an update is started
* in order to notify the DFU of the new image size. If multiple images are to be
* transferred within the same update context then this function must be called with size
* information for each image being transfered.
* If an image type is not being transfered, e.g. SoftDevice but no Application , then the
* image size for application must be zero.
*
* @param[in] p_packet Pointer to the DFU packet containing information on DFU update process to
* be started.
*
* @return NRF_SUCCESS on success, an error_code otherwise.
*/
uint32_t dfu_start_pkt_handle(dfu_update_packet_t * p_packet);
/**@brief Function for handling DFU data packets.
*
* @param[in] p_packet Pointer to the DFU packet.
*
* @return NRF_SUCCESS on success, an error_code otherwise.
*/
uint32_t dfu_data_pkt_handle(dfu_update_packet_t * p_packet);
/**@brief Function for handling DFU init packets.
*
* @return NRF_SUCCESS on success, an error_code otherwise.
*/
uint32_t dfu_init_pkt_handle(dfu_update_packet_t * p_packet);
/**@brief Function for validating a transferred image after the transfer has completed.
*
* @return NRF_SUCCESS on success, an error_code otherwise.
*/
uint32_t dfu_image_validate(void);
/**@brief Function for activating the transfered image after validation has successfully completed.
*
* @return NRF_SUCCESS on success, an error_code otherwise.
*/
uint32_t dfu_image_activate(void);
/**@brief Function for reseting the current update procedure and return to initial state.
*
* @details This function call will result in a system reset to ensure correct system behavior.
* The reset will might be scheduled to execute at a later point in time to ensure pending
* flash operations has completed.
*/
void dfu_reset(void);
/**@brief Function for validating that new bootloader has been correctly installed.
*
* @return NRF_SUCCESS if install was successful. NRF_ERROR_NULL if the images differs.
*/
uint32_t dfu_bl_image_validate(void);
/**@brief Function for validating that new SoftDevicehas been correctly installed.
*
* @return NRF_SUCCESS if install was successful. NRF_ERROR_NULL if the images differs.
*/
uint32_t dfu_sd_image_validate(void);
/**@brief Function for swapping existing bootloader with newly received.
*
* @return NRF_SUCCESS on succesfull swapping. For error code please refer to
* \ref sd_mbr_command_copy_bl_t.
*/
uint32_t dfu_bl_image_swap(void);
/**@brief Function for swapping existing SoftDevice with newly received.
*
* @return NRF_SUCCESS on succesfull swapping. For error code please refer to
* \ref sd_mbr_command_copy_sd_t.
*/
uint32_t dfu_sd_image_swap(void);
/**@brief Function for handling DFU init packet complete.
*
* @return NRF_SUCCESS on success, an error_code otherwise.
*/
uint32_t dfu_init_pkt_complete(void);
#endif // DFU_H__
/** @} */

View File

@ -0,0 +1,192 @@
/* Copyright (c) 2014 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 "dfu_app_handler.h"
#include <string.h>
#include "bootloader_util.h"
#include "nrf.h"
#include "nrf_sdm.h"
#include "ble_gatt.h"
#include "ble_gatts.h"
#include "app_error.h"
#include "dfu_ble_svc.h"
#include "device_manager.h"
#include "nrf_delay.h"
#define IRQ_ENABLED 0x01 /**< Field that identifies if an interrupt is enabled. */
#define MAX_NUMBER_INTERRUPTS 32 /**< Maximum number of interrupts available. */
static void dfu_app_reset_prepare(void); /**< Forward declaration of default reset handler. */
static dfu_app_reset_prepare_t m_reset_prepare = dfu_app_reset_prepare; /**< Callback function to application to prepare for system reset. Allows application to clean up service and memory before reset. */
static dfu_ble_peer_data_t m_peer_data; /**< Peer data to be used for data exchange when resetting into DFU mode. */
static dm_handle_t m_dm_handle; /**< Device Manager handle with instance IDs of current BLE connection. */
/**@brief Function for reset_prepare handler if the application has not registered a handler.
*/
static void dfu_app_reset_prepare(void)
{
// Reset prepare should be handled by application.
// This function can be extended to include default handling if application does not implement
// own handler.
}
/**@brief Function for disabling all interrupts before jumping from bootloader to application.
*/
static void interrupts_disable(void)
{
uint32_t interrupt_setting_mask;
uint32_t irq;
// Fetch the current interrupt settings.
interrupt_setting_mask = NVIC->ISER[0];
// Loop from interrupt 0 for disabling of all interrupts.
for (irq = 0; irq < MAX_NUMBER_INTERRUPTS; irq++)
{
if (interrupt_setting_mask & (IRQ_ENABLED << irq))
{
// The interrupt was enabled, hence disable it.
NVIC_DisableIRQ((IRQn_Type)irq);
}
}
}
/**@brief Function for providing peer information to DFU for re-establishing a bonded connection in
* DFU mode.
*
* @param[in] conn_handle Connection handle for the connection requesting DFU mode.
*/
static void dfu_app_peer_data_set(uint16_t conn_handle)
{
uint32_t err_code;
dm_sec_keyset_t key_set;
uint32_t app_context_data = 0;
dm_application_context_t app_context;
/** [DFU bond sharing] */
err_code = dm_handle_get(conn_handle, &m_dm_handle);
if (err_code == NRF_SUCCESS)
{
err_code = dm_distributed_keys_get(&m_dm_handle, &key_set);
if (err_code == NRF_SUCCESS)
{
APP_ERROR_CHECK(err_code);
m_peer_data.addr = key_set.keys_central.p_id_key->id_addr_info;
m_peer_data.irk = key_set.keys_central.p_id_key->id_info;
m_peer_data.enc_key.enc_info = key_set.keys_periph.enc_key.p_enc_key->enc_info;
m_peer_data.enc_key.master_id = key_set.keys_periph.enc_key.p_enc_key->master_id;
err_code = dfu_ble_svc_peer_data_set(&m_peer_data);
APP_ERROR_CHECK(err_code);
app_context_data = (DFU_APP_ATT_TABLE_CHANGED << DFU_APP_ATT_TABLE_POS);
app_context.len = sizeof(app_context_data);
app_context.p_data = (uint8_t *)&app_context_data;
app_context.flags = 0;
err_code = dm_application_context_set(&m_dm_handle, &app_context);
APP_ERROR_CHECK(err_code);
}
else
{
// Keys were not available, thus we have a non-encrypted connection.
err_code = dm_peer_addr_get(&m_dm_handle, &m_peer_data.addr);
APP_ERROR_CHECK(err_code);
err_code = dfu_ble_svc_peer_data_set(&m_peer_data);
APP_ERROR_CHECK(err_code);
}
}
/** [DFU bond sharing] */
}
/**@brief Function for preparing the reset, disabling SoftDevice, and jumping to the bootloader.
*
* @param[in] conn_handle Connection handle for peer requesting to enter DFU mode.
*/
static void bootloader_start(uint16_t conn_handle)
{
uint32_t err_code;
uint16_t sys_serv_attr_len = sizeof(m_peer_data.sys_serv_attr);
err_code = sd_ble_gatts_sys_attr_get(conn_handle,
m_peer_data.sys_serv_attr,
&sys_serv_attr_len,
BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS);
if (err_code != NRF_SUCCESS)
{
// Any error at this stage means the system service attributes could not be fetched.
// This means the service changed indication cannot be sent in DFU mode, but connection
// is still possible to establish.
}
m_reset_prepare();
err_code = sd_power_gpregret_set(BOOTLOADER_DFU_START);
APP_ERROR_CHECK(err_code);
err_code = sd_softdevice_disable();
APP_ERROR_CHECK(err_code);
err_code = sd_softdevice_vector_table_base_set(NRF_UICR->NRFFW[0]);
APP_ERROR_CHECK(err_code);
dfu_app_peer_data_set(conn_handle);
NVIC_ClearPendingIRQ(SWI2_IRQn);
interrupts_disable();
bootloader_util_app_start(NRF_UICR->NRFFW[0]);
}
void dfu_app_on_dfu_evt(ble_dfu_t * p_dfu, ble_dfu_evt_t * p_evt)
{
switch (p_evt->ble_dfu_evt_type)
{
case BLE_DFU_START:
// Starting the bootloader - will cause reset.
bootloader_start(p_dfu->conn_handle);
break;
default:
{
// Unsupported event received from DFU Service.
// Send back BLE_DFU_RESP_VAL_NOT_SUPPORTED message to peer.
uint32_t err_code = ble_dfu_response_send(p_dfu,
BLE_DFU_START_PROCEDURE,
BLE_DFU_RESP_VAL_NOT_SUPPORTED);
APP_ERROR_CHECK(err_code);
}
break;
}
}
void dfu_app_reset_prepare_set(dfu_app_reset_prepare_t reset_prepare_func)
{
m_reset_prepare = reset_prepare_func;
}
void dfu_app_dm_appl_instance_set(dm_application_instance_t app_instance)
{
uint32_t err_code;
err_code = dm_application_instance_set(&app_instance, &m_dm_handle);
APP_ERROR_CHECK(err_code);
}

View File

@ -0,0 +1,86 @@
/* Copyright (c) 2014 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.
*
*/
/** @file
*
* @defgroup nrf_dfu_app_handler DFU BLE packet handling in application
* @{
*
* @brief Handling of DFU BLE packets in the application.
*
* @details This module implements the handling of DFU packets for switching
* from an application to the bootloader and start DFU mode. The DFU
* packets are transmitted over BLE.
* This module handles only the StartDFU packet, which allows a BLE
* application to expose support for the DFU Service.
* The actual DFU Service runs in a dedicated environment after a BLE
* disconnect and reset of the \nRFXX device.
* The host must reconnect and continue the update procedure with
* access to the full DFU Service.
*
* @note The application must propagate DFU events to this module by calling
* @ref dfu_app_on_dfu_evt from the @ref ble_dfu_evt_handler_t callback.
*/
#ifndef DFU_APP_HANDLER_H__
#define DFU_APP_HANDLER_H__
#include "ble_dfu.h"
#include "nrf_svc.h"
#include "bootloader_types.h"
#include "device_manager.h"
#define DFU_APP_ATT_TABLE_POS 0 /**< Position for the ATT table changed setting. */
#define DFU_APP_ATT_TABLE_CHANGED 1 /**< Value indicating that the ATT table might have changed. This value will be set in the application-specific context in Device Manager when entering DFU mode. */
/**@brief DFU application reset_prepare function. This function is a callback that allows the
* application to prepare for an upcoming application reset.
*/
typedef void (*dfu_app_reset_prepare_t)(void);
/**@brief Function for handling events from the DFU Service.
*
* @details The application must inject this function into the DFU Service or propagate DFU events
* to the dfu_app_handler module by calling this function in the application-specific DFU event
* handler.
*
* @param[in] p_dfu Pointer to the DFU Service structure to which the include event relates.
* @param[in] p_evt Pointer to the DFU event.
*/
void dfu_app_on_dfu_evt(ble_dfu_t * p_dfu, ble_dfu_evt_t * p_evt);
/**@brief Function for registering a function to prepare a reset.
*
* @details The provided function is executed before resetting the system into bootloader/DFU
* mode. By registering this function, the caller is notified before the reset and can
* thus prepare the application for reset. For example, the application can gracefully
* disconnect any peers on BLE, turn of LEDS, ensure that all pending flash operations
* have completed, and so on.
*
* @param[in] reset_prepare_func Function to be executed before a reset.
*/
void dfu_app_reset_prepare_set(dfu_app_reset_prepare_t reset_prepare_func);
/**@brief Function for setting the Device Manager application instance.
*
* @details This function allows to set the @ref dm_application_instance_t value that is returned by the
* Device Manager when the application registers using @ref dm_register.
* If this function is not called, it is not be possible to share bonding information
* from the application to the bootloader/DFU when entering DFU mode.
*
* @param[in] app_instance Value for the application instance in use.
*/
void dfu_app_dm_appl_instance_set(dm_application_instance_t app_instance);
#endif // DFU_APP_HANDLER_H__
/** @} */

View File

@ -0,0 +1,87 @@
/* Copyright (c) 2014 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.
*
*/
/**@file
*
* @defgroup dfu_bank_internal Device Firmware Update internal header for bank handling in DFU.
* @{
*
* @brief Device Firmware Update Bank handling module interface.
*
* @details This header is intended for shared definition and functions between single and dual bank
* implementations used for DFU support. It is not supposed to be used for external access
* to the DFU module.
*
*/
#ifndef DFU_BANK_INTERNAL_H__
#define DFU_BANK_INTERNAL_H__
#include <dfu_types.h>
/**@brief States of the DFU state machine. */
typedef enum
{
DFU_STATE_INIT_ERROR, /**< State for: dfu_init(...) error. */
DFU_STATE_IDLE, /**< State for: idle. */
DFU_STATE_PREPARING, /**< State for: preparing, indicates that the flash is being erased and no data packets can be processed. */
DFU_STATE_RDY, /**< State for: ready. */
DFU_STATE_RX_INIT_PKT, /**< State for: receiving initialization packet. */
DFU_STATE_RX_DATA_PKT, /**< State for: receiving data packet. */
DFU_STATE_VALIDATE, /**< State for: validate. */
DFU_STATE_WAIT_4_ACTIVATE /**< State for: waiting for dfu_image_activate(). */
} dfu_state_t;
#define APP_TIMER_PRESCALER 0 /**< Value of the RTC1 PRESCALER register. */
#define DFU_TIMEOUT_INTERVAL APP_TIMER_TICKS(300000, APP_TIMER_PRESCALER) /**< DFU timeout interval in units of timer ticks. */
#define IS_UPDATING_SD(START_PKT) ((START_PKT).dfu_update_mode & DFU_UPDATE_SD) /**< Macro for determining if a SoftDevice update is ongoing. */
#define IS_UPDATING_BL(START_PKT) ((START_PKT).dfu_update_mode & DFU_UPDATE_BL) /**< Macro for determining if a Bootloader update is ongoing. */
#define IS_UPDATING_APP(START_PKT) ((START_PKT).dfu_update_mode & DFU_UPDATE_APP) /**< Macro for determining if a Application update is ongoing. */
#define IMAGE_WRITE_IN_PROGRESS() (m_data_received > 0) /**< Macro for determining if an image write is in progress. */
#define IS_WORD_SIZED(SIZE) ((SIZE & (sizeof(uint32_t) - 1)) == 0) /**< Macro for checking that the provided is word sized. */
/**@cond NO_DOXYGEN */
static uint32_t m_data_received; /**< Amount of received data. */
/**@endcond */
/**@brief Type definition of function used for preparing of the bank before receiving of a
* software image.
*
* @param[in] image_size Size of software image being received.
*/
typedef void (*dfu_bank_prepare_t)(uint32_t image_size);
/**@brief Type definition of function used for handling clear complete of the bank before
* receiving of a software image.
*/
typedef void (*dfu_bank_cleared_t)(void);
/**@brief Type definition of function used for activating of the software image received.
*
* @return NRF_SUCCESS If the image has been successfully activated any other NRF_ERROR code in
* case of a failure.
*/
typedef uint32_t (*dfu_bank_activate_t)(void);
/**@brief Structure for holding of function pointers for needed prepare and activate procedure for
* the requested update procedure.
*/
typedef struct
{
dfu_bank_prepare_t prepare; /**< Function pointer to the prepare function called on start of update procedure. */
dfu_bank_cleared_t cleared; /**< Function pointer to the cleared function called after prepare function completes. */
dfu_bank_activate_t activate; /**< Function pointer to the activate function called on finalizing the update procedure. */
} dfu_bank_func_t;
#endif // DFU_BANK_INTERNAL_H__
/** @} */

View File

@ -0,0 +1,80 @@
/* Copyright (c) 2014 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.
*
*/
/** @file
*
* @defgroup nrf_dfu_ble_svc DFU BLE SVC
* @{
*
* @brief DFU BLE SVC in bootloader. The DFU BLE SuperVisor Calls allow an application to execute
* functions in the installed bootloader.
*
* @details This module implements handling of SuperVisor Calls in the bootloader.
* SuperVisor Calls allow for an application to execute calls into the bootloader.
* Currently, it is possible to exchange bonding information (like keys) from the
* application to a bootloader supporting DFU OTA using BLE, so the update process can be
* done through an already existing bond.
*
* @note The application must make sure that all SuperVisor Calls (SVC) are forwarded to the
* bootloader to ensure correct behavior. Forwarding of SVCs to the bootloader is
* done using the SoftDevice SVC @ref sd_softdevice_vector_table_base_set with the value
* present in @c NRF_UICR->NRFFW[0].
*/
#ifndef DFU_BLE_SVC_H__
#define DFU_BLE_SVC_H__
#include "nrf_svc.h"
#include <stdint.h>
#include "ble_gap.h"
#include "nrf.h"
#include "nrf_soc.h"
#include "nrf_error_sdm.h"
#define BOOTLOADER_SVC_BASE 0x0 /**< The number of the lowest SVC number reserved for the bootloader. */
#define SYSTEM_SERVICE_ATT_SIZE 8 /**< Size of the system service attribute length including CRC-16 at the end. */
/**@brief The SVC numbers used by the SVC functions in the SoC library. */
enum BOOTLOADER_SVCS
{
DFU_BLE_SVC_PEER_DATA_SET = BOOTLOADER_SVC_BASE, /**< SVC number for the setting of peer data call. */
BOOTLOADER_SVC_LAST
};
/**@brief DFU Peer data structure.
*
* @details This structure contains peer data needed for connection to a bonded device during DFU.
* The peer data must be provided by the application to the bootloader during buttonless
* update. See @ref dfu_ble_svc_peer_data_set. It contains bond information about the
* desired DFU peer.
*/
typedef struct
{
ble_gap_addr_t addr; /**< BLE GAP address of the device that initiated the DFU process. */
ble_gap_irk_t irk; /**< IRK of the device that initiated the DFU process if this device uses Private Resolvable Addresses. */
ble_gap_enc_key_t enc_key; /**< Encryption key structure containing encrypted diversifier and LTK for re-establishing the bond. */
uint8_t sys_serv_attr[SYSTEM_SERVICE_ATT_SIZE]; /**< System service attributes for restoring of Service Changed Indication setting in DFU mode. */
} dfu_ble_peer_data_t;
/**@brief SVC Function for setting peer data containing address, IRK, and LTK to establish bonded
* connection in DFU mode.
*
* @param[in] p_peer_data Pointer to the peer data containing keys for the connection.
*
* @retval NRF_ERROR_NULL If a NULL pointer was provided as argument.
* @retval NRF_SUCCESS If the function completed successfully.
*/
SVCALL(DFU_BLE_SVC_PEER_DATA_SET, uint32_t, dfu_ble_svc_peer_data_set(dfu_ble_peer_data_t * p_peer_data));
#endif // DFU_BLE_SVC_H__
/** @} */

View File

@ -0,0 +1,43 @@
/* Copyright (c) 2014 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.
*
*/
/** @file
*
* @defgroup nrf_dfu_ble_svc_internal DFU BLE SVC internal
* @{
*
* @brief DFU BLE SVC internal functions in bootloader. The DFU BLE SuperVisor Calls allow an
* application to execute functions in the installed bootloader. This interface provides
* internal Bootloader DFU functions for retrieving data exchanged through SuperVisor Calls.
*
*/
#ifndef DFU_BLE_SVC_INTERNAL_H__
#define DFU_BLE_SVC_INTERNAL_H__
#include <stdint.h>
#include "dfu_ble_svc.h"
#include "ble_gap.h"
/**@brief Internal bootloader/DFU function for retrieving peer data provided from application.
*
* @param[out] p_peer_data Peer data set by application to be used for DFU connection.
*
* @retval NRF_SUCCESS If peer data is valid and can be used for connection.
* @retval NRF_ERROR_NULL If p_peer_data is a NULL pointer.
* @retval NRF_ERROR_INVALID_DATA If peer data is not available or invalid.
*/
uint32_t dfu_ble_peer_data_get(dfu_ble_peer_data_t * p_peer_data);
#endif // DFU_BLE_SVC_INTERNAL_H__
/** @} */

View File

@ -0,0 +1,840 @@
/* 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 <stddef.h>
#include "dfu.h"
#include <dfu_types.h>
#include "dfu_bank_internal.h"
#include "nrf.h"
#include "nrf_sdm.h"
#include "app_error.h"
#include "app_timer.h"
#include "bootloader.h"
#include "bootloader_types.h"
#include "pstorage.h"
#include "nrf_mbr.h"
#include "dfu_init.h"
#include "sdk_common.h"
static dfu_state_t m_dfu_state; /**< Current DFU state. */
static uint32_t m_image_size; /**< Size of the image that will be transmitted. */
static dfu_start_packet_t m_start_packet; /**< Start packet received for this update procedure. Contains update mode and image sizes information to be used for image transfer. */
static uint8_t m_init_packet[128]; /**< Init packet, can hold CRC, Hash, Signed Hash and similar, for image validation, integrety check and authorization checking. */
static uint8_t m_init_packet_length; /**< Length of init packet received. */
static uint16_t m_image_crc; /**< Calculated CRC of the image received. */
APP_TIMER_DEF(m_dfu_timer_id); /**< Application timer id. */
static bool m_dfu_timed_out = false; /**< Boolean flag value for tracking DFU timer timeout state. */
static pstorage_handle_t m_storage_handle_swap; /**< Pstorage handle for the swap area (bank 1). Bank used when updating an application or bootloader without SoftDevice. */
static pstorage_handle_t m_storage_handle_app; /**< Pstorage handle for the application area (bank 0). Bank used when updating a SoftDevice w/wo bootloader. Handle also used when swapping received application from bank 1 to bank 0. */
static pstorage_handle_t * mp_storage_handle_active; /**< Pointer to the pstorage handle for the active bank for receiving of data packets. */
static dfu_callback_t m_data_pkt_cb; /**< Callback from DFU Bank module for notification of asynchronous operation such as flash prepare. */
static dfu_bank_func_t m_functions; /**< Structure holding operations for the selected update process. */
/**@brief Function for handling callbacks from pstorage module.
*
* @details Handles pstorage results for clear and storage operation. For detailed description of
* the parameters provided with the callback, please refer to \ref pstorage_ntf_cb_t.
*/
static void pstorage_callback_handler(pstorage_handle_t * p_handle,
uint8_t op_code,
uint32_t result,
uint8_t * p_data,
uint32_t data_len)
{
switch (op_code)
{
case PSTORAGE_STORE_OP_CODE:
if ((m_dfu_state == DFU_STATE_RX_DATA_PKT) && (m_data_pkt_cb != NULL))
{
m_data_pkt_cb(DATA_PACKET, result, p_data);
}
break;
case PSTORAGE_CLEAR_OP_CODE:
if (m_dfu_state == DFU_STATE_PREPARING)
{
m_functions.cleared();
m_dfu_state = DFU_STATE_RDY;
if (m_data_pkt_cb != NULL)
{
m_data_pkt_cb(START_PACKET, result, p_data);
}
}
break;
default:
break;
}
APP_ERROR_CHECK(result);
}
/**@brief Function for handling the DFU timeout.
*
* @param[in] p_context The timeout context.
*/
static void dfu_timeout_handler(void * p_context)
{
UNUSED_PARAMETER(p_context);
dfu_update_status_t update_status;
m_dfu_timed_out = true;
update_status.status_code = DFU_TIMEOUT;
bootloader_dfu_update_process(update_status);
}
/**@brief Function for restarting the DFU Timer.
*
* @details This function will stop and restart the DFU timer. This function will be called by the
* functions handling any DFU packet received from the peer that is transferring a firmware
* image.
*/
static uint32_t dfu_timer_restart(void)
{
if (m_dfu_timed_out)
{
// The DFU timer had already timed out.
return NRF_ERROR_INVALID_STATE;
}
uint32_t err_code = app_timer_stop(m_dfu_timer_id);
APP_ERROR_CHECK(err_code);
err_code = app_timer_start(m_dfu_timer_id, DFU_TIMEOUT_INTERVAL, NULL);
APP_ERROR_CHECK(err_code);
return err_code;
}
/**@brief Function for preparing of flash before receiving SoftDevice image.
*
* @details This function will erase current application area to ensure sufficient amount of
* storage for the SoftDevice image. Upon erase complete a callback will be done.
* See \ref dfu_bank_prepare_t for further details.
*/
static void dfu_prepare_func_app_erase(uint32_t image_size)
{
uint32_t err_code;
mp_storage_handle_active = &m_storage_handle_app;
// Doing a SoftDevice update thus current application must be cleared to ensure enough space
// for new SoftDevice.
m_dfu_state = DFU_STATE_PREPARING;
err_code = pstorage_clear(&m_storage_handle_app, m_image_size);
APP_ERROR_CHECK(err_code);
}
/**@brief Function for preparing swap before receiving application or bootloader image.
*
* @details This function will erase current swap area to ensure flash is ready for storage of the
* Application or Bootloader image. Upon erase complete a callback will be done.
* See \ref dfu_bank_prepare_t for further details.
*/
static void dfu_prepare_func_swap_erase(uint32_t image_size)
{
uint32_t err_code;
mp_storage_handle_active = &m_storage_handle_swap;
m_dfu_state = DFU_STATE_PREPARING;
// err_code = pstorage_clear(&m_storage_handle_swap, DFU_IMAGE_MAX_SIZE_BANKED);
err_code = pstorage_clear(&m_storage_handle_swap, image_size);
APP_ERROR_CHECK(err_code);
}
/**@brief Function for handling behaviour when clear operation has completed.
*/
static void dfu_cleared_func_swap(void)
{
// Do nothing.
}
/**@brief Function for handling behaviour when clear operation has completed.
*/
static void dfu_cleared_func_app(void)
{
dfu_update_status_t update_status = {DFU_BANK_0_ERASED, };
bootloader_dfu_update_process(update_status);
}
/**@brief Function for calculating storage offset for receiving SoftDevice image.
*
* @details When a new SoftDevice is received it will be temporary stored in flash before moved to
* address 0x0. In order to succesfully validate transfer and relocation it is important
* that temporary image and final installed image does not ovwerlap hence an offset must
* be calculated in case new image is larger than currently installed SoftDevice.
*/
uint32_t offset_calculate(uint32_t sd_image_size)
{
uint32_t offset = 0;
if (m_start_packet.sd_image_size > DFU_BANK_0_REGION_START)
{
uint32_t page_mask = (CODE_PAGE_SIZE - 1);
uint32_t diff = m_start_packet.sd_image_size - DFU_BANK_0_REGION_START;
offset = diff & ~page_mask;
// Align offset to next page if image size is not page sized.
if ((diff & page_mask) > 0)
{
offset += CODE_PAGE_SIZE;
}
}
return offset;
}
/**@brief Function for activating received SoftDevice image.
*
* @note This function will not move the SoftDevice image.
* The bootloader settings will be marked as SoftDevice update complete and the swapping of
* current SoftDevice will occur after system reset.
*
* @return NRF_SUCCESS on success.
*/
static uint32_t dfu_activate_sd(void)
{
dfu_update_status_t update_status;
update_status.status_code = DFU_UPDATE_SD_COMPLETE;
update_status.app_crc = m_image_crc;
update_status.sd_image_start = DFU_BANK_0_REGION_START;
update_status.sd_size = m_start_packet.sd_image_size;
update_status.bl_size = m_start_packet.bl_image_size;
update_status.app_size = m_start_packet.app_image_size;
bootloader_dfu_update_process(update_status);
return NRF_SUCCESS;
}
/**@brief Function for activating received Application image.
*
* @details This function will move the received application image fram swap (bank 1) to
* application area (bank 0).
*
* @return NRF_SUCCESS on success. Error code otherwise.
*/
static uint32_t dfu_activate_app(void)
{
uint32_t err_code;
// Erase BANK 0.
err_code = pstorage_clear(&m_storage_handle_app, m_start_packet.app_image_size);
APP_ERROR_CHECK(err_code);
err_code = pstorage_store(&m_storage_handle_app,
(uint8_t *)m_storage_handle_swap.block_id,
m_start_packet.app_image_size,
0);
if (err_code == NRF_SUCCESS)
{
dfu_update_status_t update_status;
memset(&update_status, 0, sizeof(dfu_update_status_t ));
update_status.status_code = DFU_UPDATE_APP_COMPLETE;
update_status.app_crc = m_image_crc;
update_status.app_size = m_start_packet.app_image_size;
bootloader_dfu_update_process(update_status);
}
return err_code;
}
/**@brief Function for activating received Bootloader image.
*
* @note This function will not move the bootloader image.
* The bootloader settings will be marked as Bootloader update complete and the swapping of
* current bootloader will occur after system reset.
*
* @return NRF_SUCCESS on success.
*/
static uint32_t dfu_activate_bl(void)
{
dfu_update_status_t update_status;
update_status.status_code = DFU_UPDATE_BOOT_COMPLETE;
update_status.app_crc = m_image_crc;
update_status.sd_size = m_start_packet.sd_image_size;
update_status.bl_size = m_start_packet.bl_image_size;
update_status.app_size = m_start_packet.app_image_size;
bootloader_dfu_update_process(update_status);
return NRF_SUCCESS;
}
uint32_t dfu_init(void)
{
uint32_t err_code;
pstorage_module_param_t storage_module_param = {.cb = pstorage_callback_handler};
m_init_packet_length = 0;
m_image_crc = 0;
err_code = pstorage_register(&storage_module_param, &m_storage_handle_app);
if (err_code != NRF_SUCCESS)
{
m_dfu_state = DFU_STATE_INIT_ERROR;
return err_code;
}
m_storage_handle_app.block_id = DFU_BANK_0_REGION_START;
m_storage_handle_swap = m_storage_handle_app;
m_storage_handle_swap.block_id = DFU_BANK_1_REGION_START;
// Create the timer to monitor the activity by the peer doing the firmware update.
err_code = app_timer_create(&m_dfu_timer_id,
APP_TIMER_MODE_SINGLE_SHOT,
dfu_timeout_handler);
APP_ERROR_CHECK(err_code);
// Start the DFU timer.
err_code = app_timer_start(m_dfu_timer_id, DFU_TIMEOUT_INTERVAL, NULL);
APP_ERROR_CHECK(err_code);
m_data_received = 0;
m_dfu_state = DFU_STATE_IDLE;
return NRF_SUCCESS;
}
void dfu_register_callback(dfu_callback_t callback_handler)
{
m_data_pkt_cb = callback_handler;
}
uint32_t dfu_start_pkt_handle(dfu_update_packet_t * p_packet)
{
uint32_t err_code;
m_start_packet = *(p_packet->params.start_packet);
// Check that the requested update procedure is supported.
// Currently the following combinations are allowed:
// - Application
// - SoftDevice
// - Bootloader
// - SoftDevice with Bootloader
if (IS_UPDATING_APP(m_start_packet) &&
(IS_UPDATING_SD(m_start_packet) || IS_UPDATING_BL(m_start_packet)))
{
// App update is only supported independently.
return NRF_ERROR_NOT_SUPPORTED;
}
if (!(IS_WORD_SIZED(m_start_packet.sd_image_size) &&
IS_WORD_SIZED(m_start_packet.bl_image_size) &&
IS_WORD_SIZED(m_start_packet.app_image_size)))
{
// Image_sizes are not a multiple of 4 (word size).
return NRF_ERROR_NOT_SUPPORTED;
}
m_image_size = m_start_packet.sd_image_size + m_start_packet.bl_image_size +
m_start_packet.app_image_size;
if (m_start_packet.bl_image_size > DFU_BL_IMAGE_MAX_SIZE)
{
return NRF_ERROR_DATA_SIZE;
}
if (IS_UPDATING_SD(m_start_packet))
{
if (m_image_size > (DFU_IMAGE_MAX_SIZE_FULL))
{
return NRF_ERROR_DATA_SIZE;
}
m_functions.prepare = dfu_prepare_func_app_erase;
m_functions.cleared = dfu_cleared_func_app;
m_functions.activate = dfu_activate_sd;
}
else
{
if (m_image_size > DFU_IMAGE_MAX_SIZE_BANKED)
{
return NRF_ERROR_DATA_SIZE;
}
m_functions.prepare = dfu_prepare_func_swap_erase;
m_functions.cleared = dfu_cleared_func_swap;
if (IS_UPDATING_BL(m_start_packet))
{
m_functions.activate = dfu_activate_bl;
}
else
{
m_functions.activate = dfu_activate_app;
}
}
switch (m_dfu_state)
{
case DFU_STATE_IDLE:
// Valid peer activity detected. Hence restart the DFU timer.
err_code = dfu_timer_restart();
VERIFY_SUCCESS(err_code);
m_functions.prepare(m_image_size);
break;
default:
err_code = NRF_ERROR_INVALID_STATE;
break;
}
return err_code;
}
uint32_t dfu_data_pkt_handle(dfu_update_packet_t * p_packet)
{
uint32_t data_length;
uint32_t err_code;
uint32_t * p_data;
VERIFY_PARAM_NOT_NULL(p_packet);
// Check pointer alignment.
if (!is_word_aligned(p_packet->params.data_packet.p_data_packet))
{
// The p_data_packet is not word aligned address.
return NRF_ERROR_INVALID_ADDR;
}
switch (m_dfu_state)
{
case DFU_STATE_RDY:
case DFU_STATE_RX_INIT_PKT:
return NRF_ERROR_INVALID_STATE;
case DFU_STATE_RX_DATA_PKT:
data_length = p_packet->params.data_packet.packet_length * sizeof(uint32_t);
if ((m_data_received + data_length) > m_image_size)
{
// The caller is trying to write more bytes into the flash than the size provided to
// the dfu_image_size_set function. This is treated as a serious error condition and
// an unrecoverable one. Hence point the variable mp_app_write_address to the top of
// the flash area. This will ensure that all future application data packet writes
// will be blocked because of the above check.
m_data_received = 0xFFFFFFFF;
return NRF_ERROR_DATA_SIZE;
}
// Valid peer activity detected. Hence restart the DFU timer.
err_code = dfu_timer_restart();
VERIFY_SUCCESS(err_code);
p_data = (uint32_t *)p_packet->params.data_packet.p_data_packet;
err_code = pstorage_store(mp_storage_handle_active,
(uint8_t *)p_data,
data_length,
m_data_received);
VERIFY_SUCCESS(err_code);
m_data_received += data_length;
if (m_data_received != m_image_size)
{
// The entire image is not received yet. More data is expected.
err_code = NRF_ERROR_INVALID_LENGTH;
}
else
{
// The entire image has been received. Return NRF_SUCCESS.
err_code = NRF_SUCCESS;
}
break;
default:
err_code = NRF_ERROR_INVALID_STATE;
break;
}
return err_code;
}
uint32_t dfu_init_pkt_complete(void)
{
uint32_t err_code = NRF_ERROR_INVALID_STATE;
// DFU initialization has been done and a start packet has been received.
if (IMAGE_WRITE_IN_PROGRESS())
{
// Image write is already in progress. Cannot handle an init packet now.
return NRF_ERROR_INVALID_STATE;
}
if (m_dfu_state == DFU_STATE_RX_INIT_PKT)
{
err_code = dfu_init_prevalidate(m_init_packet, m_init_packet_length, m_start_packet.dfu_update_mode);
if (err_code == NRF_SUCCESS)
{
m_dfu_state = DFU_STATE_RX_DATA_PKT;
}
else
{
m_init_packet_length = 0;
}
}
return err_code;
}
uint32_t dfu_init_pkt_handle(dfu_update_packet_t * p_packet)
{
uint32_t err_code = NRF_SUCCESS;
uint32_t length;
switch (m_dfu_state)
{
case DFU_STATE_RDY:
m_dfu_state = DFU_STATE_RX_INIT_PKT;
// When receiving init packet in state ready just update and fall through this case.
case DFU_STATE_RX_INIT_PKT:
// DFU initialization has been done and a start packet has been received.
if (IMAGE_WRITE_IN_PROGRESS())
{
// Image write is already in progress. Cannot handle an init packet now.
return NRF_ERROR_INVALID_STATE;
}
// Valid peer activity detected. Hence restart the DFU timer.
err_code = dfu_timer_restart();
VERIFY_SUCCESS(err_code);
length = p_packet->params.data_packet.packet_length * sizeof(uint32_t);
if ((m_init_packet_length + length) > sizeof(m_init_packet))
{
return NRF_ERROR_INVALID_LENGTH;
}
memcpy(&m_init_packet[m_init_packet_length],
&p_packet->params.data_packet.p_data_packet[0],
length);
m_init_packet_length += length;
break;
default:
// Either the start packet was not received or dfu_init function was not called before.
err_code = NRF_ERROR_INVALID_STATE;
break;
}
return err_code;
}
uint32_t dfu_image_validate()
{
uint32_t err_code;
switch (m_dfu_state)
{
case DFU_STATE_RX_DATA_PKT:
// Check if the application image write has finished.
if (m_data_received != m_image_size)
{
// Image not yet fully transfered by the peer or the peer has attempted to write
// too much data. Hence the validation should fail.
err_code = NRF_ERROR_INVALID_STATE;
}
else
{
m_dfu_state = DFU_STATE_VALIDATE;
// Valid peer activity detected. Hence restart the DFU timer.
err_code = dfu_timer_restart();
if (err_code == NRF_SUCCESS)
{
err_code = dfu_init_postvalidate((uint8_t *)mp_storage_handle_active->block_id,
m_image_size);
VERIFY_SUCCESS(err_code);
m_dfu_state = DFU_STATE_WAIT_4_ACTIVATE;
}
}
break;
default:
err_code = NRF_ERROR_INVALID_STATE;
break;
}
return err_code;
}
uint32_t dfu_image_activate()
{
uint32_t err_code;
switch (m_dfu_state)
{
case DFU_STATE_WAIT_4_ACTIVATE:
// Stop the DFU Timer because the peer activity need not be monitored any longer.
err_code = app_timer_stop(m_dfu_timer_id);
APP_ERROR_CHECK(err_code);
err_code = m_functions.activate();
break;
default:
err_code = NRF_ERROR_INVALID_STATE;
break;
}
return err_code;
}
void dfu_reset(void)
{
dfu_update_status_t update_status;
update_status.status_code = DFU_RESET;
bootloader_dfu_update_process(update_status);
}
static uint32_t dfu_compare_block(uint32_t * ptr1, uint32_t * ptr2, uint32_t len)
{
sd_mbr_command_t sd_mbr_cmd;
sd_mbr_cmd.command = SD_MBR_COMMAND_COMPARE;
sd_mbr_cmd.params.compare.ptr1 = ptr1;
sd_mbr_cmd.params.compare.ptr2 = ptr2;
sd_mbr_cmd.params.compare.len = len / sizeof(uint32_t);
return sd_mbr_command(&sd_mbr_cmd);
}
static uint32_t dfu_copy_sd(uint32_t * src, uint32_t * dst, uint32_t len)
{
sd_mbr_command_t sd_mbr_cmd;
sd_mbr_cmd.command = SD_MBR_COMMAND_COPY_SD;
sd_mbr_cmd.params.copy_sd.src = src;
sd_mbr_cmd.params.copy_sd.dst = dst;
sd_mbr_cmd.params.copy_sd.len = len / sizeof(uint32_t);
return sd_mbr_command(&sd_mbr_cmd);
}
static uint32_t dfu_sd_img_block_swap(uint32_t * src,
uint32_t * dst,
uint32_t len,
uint32_t block_size)
{
// It is neccesarry to swap the new SoftDevice in 3 rounds to ensure correct copy of data
// and verifucation of data in case power reset occurs during write to flash.
// To ensure the robustness of swapping the images are compared backwards till start of
// image swap. If the back is identical everything is swapped.
uint32_t err_code = dfu_compare_block(src, dst, len);
if (err_code == NRF_SUCCESS)
{
return err_code;
}
if ((uint32_t)dst > SOFTDEVICE_REGION_START)
{
err_code = dfu_sd_img_block_swap((uint32_t *)((uint32_t)src - block_size),
(uint32_t *)((uint32_t)dst - block_size),
block_size,
block_size);
VERIFY_SUCCESS(err_code);
}
err_code = dfu_copy_sd(src, dst, len);
VERIFY_SUCCESS(err_code);
return dfu_compare_block(src, dst, len);
}
uint32_t dfu_sd_image_swap(void)
{
bootloader_settings_t boot_settings;
bootloader_settings_get(&boot_settings);
if (boot_settings.sd_image_size == 0)
{
return NRF_SUCCESS;
}
if ((SOFTDEVICE_REGION_START + boot_settings.sd_image_size) > boot_settings.sd_image_start)
{
uint32_t err_code;
uint32_t sd_start = SOFTDEVICE_REGION_START;
uint32_t block_size = (boot_settings.sd_image_start - sd_start) / 2;
/* ##### FIX START ##### */
block_size &= ~(uint32_t)(CODE_PAGE_SIZE - 1);
/* ##### FIX END ##### */
uint32_t image_end = boot_settings.sd_image_start + boot_settings.sd_image_size;
uint32_t img_block_start = boot_settings.sd_image_start + 2 * block_size;
uint32_t sd_block_start = sd_start + 2 * block_size;
if (SD_SIZE_GET(MBR_SIZE) < boot_settings.sd_image_size)
{
// This will clear a page thus ensuring the old image is invalidated before swapping.
err_code = dfu_copy_sd((uint32_t *)(sd_start + block_size),
(uint32_t *)(sd_start + block_size),
sizeof(uint32_t));
VERIFY_SUCCESS(err_code);
err_code = dfu_copy_sd((uint32_t *)sd_start, (uint32_t *)sd_start, sizeof(uint32_t));
VERIFY_SUCCESS(err_code);
}
return dfu_sd_img_block_swap((uint32_t *)img_block_start,
(uint32_t *)sd_block_start,
image_end - img_block_start,
block_size);
}
else
{
if (boot_settings.sd_image_size != 0)
{
return dfu_copy_sd((uint32_t *)boot_settings.sd_image_start,
(uint32_t *)SOFTDEVICE_REGION_START,
boot_settings.sd_image_size);
}
}
return NRF_SUCCESS;
}
uint32_t dfu_bl_image_swap(void)
{
bootloader_settings_t bootloader_settings;
sd_mbr_command_t sd_mbr_cmd;
bootloader_settings_get(&bootloader_settings);
if (bootloader_settings.bl_image_size != 0)
{
uint32_t bl_image_start = (bootloader_settings.sd_image_size == 0) ?
DFU_BANK_1_REGION_START :
bootloader_settings.sd_image_start +
bootloader_settings.sd_image_size;
sd_mbr_cmd.command = SD_MBR_COMMAND_COPY_BL;
sd_mbr_cmd.params.copy_bl.bl_src = (uint32_t *)(bl_image_start);
sd_mbr_cmd.params.copy_bl.bl_len = bootloader_settings.bl_image_size / sizeof(uint32_t);
return sd_mbr_command(&sd_mbr_cmd);
}
return NRF_SUCCESS;
}
uint32_t dfu_bl_image_validate(void)
{
bootloader_settings_t bootloader_settings;
sd_mbr_command_t sd_mbr_cmd;
bootloader_settings_get(&bootloader_settings);
if (bootloader_settings.bl_image_size != 0)
{
uint32_t bl_image_start = (bootloader_settings.sd_image_size == 0) ?
DFU_BANK_1_REGION_START :
bootloader_settings.sd_image_start +
bootloader_settings.sd_image_size;
sd_mbr_cmd.command = SD_MBR_COMMAND_COMPARE;
sd_mbr_cmd.params.compare.ptr1 = (uint32_t *)BOOTLOADER_REGION_START;
sd_mbr_cmd.params.compare.ptr2 = (uint32_t *)(bl_image_start);
sd_mbr_cmd.params.compare.len = bootloader_settings.bl_image_size / sizeof(uint32_t);
return sd_mbr_command(&sd_mbr_cmd);
}
return NRF_SUCCESS;
}
uint32_t dfu_sd_image_validate(void)
{
bootloader_settings_t bootloader_settings;
sd_mbr_command_t sd_mbr_cmd;
bootloader_settings_get(&bootloader_settings);
if (bootloader_settings.sd_image_size == 0)
{
return NRF_SUCCESS;
}
if ((SOFTDEVICE_REGION_START + bootloader_settings.sd_image_size) > bootloader_settings.sd_image_start)
{
uint32_t sd_start = SOFTDEVICE_REGION_START;
uint32_t block_size = (bootloader_settings.sd_image_start - sd_start) / 2;
uint32_t image_end = bootloader_settings.sd_image_start +
bootloader_settings.sd_image_size;
uint32_t img_block_start = bootloader_settings.sd_image_start + 2 * block_size;
uint32_t sd_block_start = sd_start + 2 * block_size;
if (SD_SIZE_GET(MBR_SIZE) < bootloader_settings.sd_image_size)
{
return NRF_ERROR_NULL;
}
return dfu_sd_img_block_swap((uint32_t *)img_block_start,
(uint32_t *)sd_block_start,
image_end - img_block_start,
block_size);
}
sd_mbr_cmd.command = SD_MBR_COMMAND_COMPARE;
sd_mbr_cmd.params.compare.ptr1 = (uint32_t *)SOFTDEVICE_REGION_START;
sd_mbr_cmd.params.compare.ptr2 = (uint32_t *)bootloader_settings.sd_image_start;
sd_mbr_cmd.params.compare.len = bootloader_settings.sd_image_size / sizeof(uint32_t);
return sd_mbr_command(&sd_mbr_cmd);
}

View File

@ -0,0 +1,134 @@
/* Copyright (c) 2014 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.
*
*/
/**@file
*
* @defgroup nrf_dfu_init Init packet handling in DFU
* @{
*
* @brief Device Firmware Update module type and function declaration for init packet handling.
*
* @details This header contains basic functionality for performing safety checks on software
* updates for \nRFXX based devices. It provides a skeleton for pre-checking an init packet
* to ensure the following image is compatible with this device. A safety check should
* always be performed to prevent accidental flashing of unsupported applications or a
* wrong combination of application and SoftDevice.
* The device information contains information such as:
* - Device type (2 bytes), for example Heart Rate. The device type is a number defined by
* the customer. It can be located in UICR or FICR.
* - Device revision (2 bytes), for example major revision 1, minor revision 0. The device
* revision is a number defined by the customer. It can be located in UICR or FICR.
* - List of SoftDevices supported by this application, for example
* 0x0049 = S110v6_0_0
* 0xFFFE = S110 development (any SoftDevice accepted),
* - CRC or hash of firmware image
*
* @note This module does not support security features such as image signing, but the corresponding
* implementation allows for such extensions.
* If the init packet is signed by a trusted source, it must be decrypted before it can be
* processed.
*/
#ifndef DFU_INIT_H__
#define DFU_INIT_H__
#include <stdint.h>
#include "nrf.h"
/**@brief Structure contained in an init packet. Contains information on device type, revision, and
* supported SoftDevices.
*/
typedef struct
{
uint16_t device_type; /**< Device type (2 bytes), for example Heart Rate. This number must be defined by the customer before production. It can be located in UICR or FICR. */
uint16_t device_rev; /**< Device revision (2 bytes), for example major revision 1, minor revision 0. This number must be defined by the customer before production. It can be located in UICR or FICR. */
uint32_t app_version; /**< Application version for the image software. This field allows for additional checking, for example ensuring that a downgrade is not allowed. */
uint16_t softdevice_len; /**< Number of different SoftDevice revisions compatible with this application. The list of SoftDevice firmware IDs is defined in @ref softdevice. */
uint16_t softdevice[1]; /**< Variable length array of SoftDevices compatible with this application. The length of the array is specified in the length field. SoftDevice firmware id 0xFFFE indicates any SoftDevice. */
} dfu_init_packet_t;
/**@brief Structure holding basic device information settings.
*/
typedef struct
{
uint16_t device_type; /**< Device type (2 bytes), for example Heart Rate. This number must be defined by the customer before production. It can be located in UICR or FICR. */
uint16_t device_rev; /**< Device revision (2 bytes), for example major revision 1, minor revision 0. This number must be defined by the customer before production. It can be located in UICR or FICR. */
} dfu_device_info_t;
/** The device info offset can be modified to place the device info settings at a different location.
* If the customer reserved UICR location is used for other application specific data, the offset
* must be updated to avoid collision with that data.
*/
/** [DFU UICR DEV offset] */
#define UICR_CUSTOMER_DEVICE_INFO_OFFSET 0x0 /**< Device info offset inside the customer UICR reserved area. Customers may change this value to place the device information in a user-preferred location. */
/** [DFU UICR DEV offset] */
#define UICR_CUSTOMER_RESERVED_OFFSET 0x80 /**< Customer reserved area in the UICR. The area from UICR + 0x80 is reserved for customer usage. */
#define DFU_DEVICE_INFO_BASE (NRF_UICR_BASE + \
UICR_CUSTOMER_RESERVED_OFFSET + \
UICR_CUSTOMER_DEVICE_INFO_OFFSET) /**< The device information base address inside of UICR. */
#define DFU_DEVICE_INFO ((dfu_device_info_t *)DFU_DEVICE_INFO_BASE) /**< The memory mapped structure for device information data. */
#define DFU_DEVICE_TYPE_EMPTY ((uint16_t)0xFFFF) /**< Mask indicating no device type is present in UICR. 0xFFFF is default flash pattern when not written with data. */
#define DFU_DEVICE_REVISION_EMPTY ((uint16_t)0xFFFF) /**< Mask indicating no device revision is present in UICR. 0xFFFF is default flash pattern when not written with data. */
#define DFU_SOFTDEVICE_ANY ((uint16_t)0xFFFE) /**< Mask indicating that any SoftDevice is allowed for updating this application. Allows for easy development. Not to be used in production images. */
/**@brief DFU prevalidate call for pre-checking the received init packet.
*
* @details Pre-validation will safety check the firmware image to be transfered in second stage.
* The function currently checks the device type, device revision, application firmware
* version, and supported SoftDevices. More checks should be added according to
* customer-specific requirements.
*
* @param[in] p_init_data Pointer to the init packet. If the init packet is encrypted or signed,
* it must first be decrypted before being checked.
* @param[in] init_data_len Length of the init data.
*
* @retval NRF_SUCCESS If the pre-validation succeeded, that means the image is
* supported by the device and it is considered to come from a
* trusted source (signing).
* @retval NRF_ERROR_INVALID_DATA If the pre-validation failed, that means the image is not
* supported by the device or comes from an un-trusted source
* (signing).
* @retval NRF_ERROR_INVALID_LENGTH If the size of the init packet is not within the limits of
* the init packet handler.
*/
uint32_t dfu_init_prevalidate(uint8_t * p_init_data, uint32_t init_data_len, uint8_t image_type);
/**@brief DFU postvalidate call for post-checking the received image using the init packet.
*
* @details Post-validation can verify the integrity check the firmware image received before
* activating the image.
* Checks performed can be:
* - A simple CRC as shown in the corresponding implementation of this API in the file
* dfu_init_template.c
* - A hash for better verification of the image.
* - A signature to ensure the image originates from a trusted source.
* Checks are intended to be expanded for customer-specific requirements.
*
* @param[in] p_image Pointer to the received image. The init data provided in the call
* \ref dfu_init_prevalidate will be used for validating the image.
* @param[in] image_len Length of the image data.
*
* @retval NRF_SUCCESS If the post-validation succeeded, that meant the integrity of the
* image has been verified and the image originates from a trusted
* source (signing).
* @retval NRF_ERROR_INVALID_DATA If the post-validation failed, that meant the post check of the
* image failed such as the CRC is not matching the image transfered
* or the verification of the image fails (signing).
*/
uint32_t dfu_init_postvalidate(uint8_t * p_image, uint32_t image_len);
#endif // DFU_INIT_H__
/**@} */

View File

@ -0,0 +1,172 @@
/* Copyright (c) 2014 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.
*
*/
/**@file
*
* @defgroup nrf_dfu_init_template Template file with an DFU init packet handling example.
* @{
*
* @ingroup nrf_dfu
*
* @brief This file contains a template on how to implement DFU init packet handling.
*
* @details The template shows how device type and revision can be used for a safety check of the
* received image. It shows how validation can be performed in two stages:
* - Stage 1: Pre-check of firmware image before transfer to ensure the firmware matches:
* - Device Type.
* - Device Revision.
* Installed SoftDevice.
* This template can be extended with additional checks according to needs.
* For example, such a check could be the origin of the image (trusted source)
* based on a signature scheme.
* - Stage 2: Post-check of the image after image transfer but before installing firmware.
* For example, such a check could be an integrity check in form of hashing or
* verification of a signature.
* In this template, a simple CRC check is carried out.
* The CRC check can be replaced with other mechanisms, like signing.
*
* @note This module does not support security features such as image signing, but the
* implementation allows for such extension.
* If the init packet is signed by a trusted source, it must be decrypted before it can be
* processed.
*/
#include "dfu_init.h"
#include <stdint.h>
#include <string.h>
#include <dfu_types.h>
#include "nrf_error.h"
#include "crc16.h"
// ADAFRUIT
// All firmware init data must has Device Type ADAFRUIT_DEVICE_TYPE
// SD + Bootloader upgrade must have ADAFRUIT_SD_UNLOCK_CODE in Device Revision
#define ADAFRUIT_DEVICE_TYPE 0x0052 // for nrf52
#define ADAFRUIT_SD_UNLOCK_CODE 0xADAF
#define DFU_INIT_PACKET_EXT_LENGTH_MIN 2 //< Minimum length of the extended init packet. The extended init packet may contain a CRC, a HASH, or other data. This value must be changed according to the requirements of the system. The template uses a minimum value of two in order to hold a CRC. */
#define DFU_INIT_PACKET_EXT_LENGTH_MAX 10 //< Maximum length of the extended init packet. The extended init packet may contain a CRC, a HASH, or other data. This value must be changed according to the requirements of the system. The template uses a maximum value of 10 in order to hold a CRC and any padded data on transport layer without overflow. */
static uint8_t m_extended_packet[DFU_INIT_PACKET_EXT_LENGTH_MAX]; //< Data array for storage of the extended data received. The extended data follows the normal init data of type \ref dfu_init_packet_t. Extended data can be used for a CRC, hash, signature, or other data. */
static uint8_t m_extended_packet_length; //< Length of the extended data received with init packet. */
uint32_t dfu_init_prevalidate(uint8_t * p_init_data, uint32_t init_data_len, uint8_t image_type)
{
uint32_t i = 0;
// In order to support signing or encryption then any init packet decryption function / library
// should be called from here or implemented at this location.
// Length check to ensure valid data are parsed.
if (init_data_len < sizeof(dfu_init_packet_t))
{
return NRF_ERROR_INVALID_LENGTH;
}
// Current template uses clear text data so they can be casted for pre-check.
dfu_init_packet_t * p_init_packet = (dfu_init_packet_t *)p_init_data;
m_extended_packet_length = ((uint32_t)p_init_data + init_data_len) -
(uint32_t)&p_init_packet->softdevice[p_init_packet->softdevice_len];
if (m_extended_packet_length < DFU_INIT_PACKET_EXT_LENGTH_MIN)
{
return NRF_ERROR_INVALID_LENGTH;
}
if (((uint32_t)p_init_data + init_data_len) <
(uint32_t)&p_init_packet->softdevice[p_init_packet->softdevice_len])
{
return NRF_ERROR_INVALID_LENGTH;
}
memcpy(m_extended_packet,
&p_init_packet->softdevice[p_init_packet->softdevice_len],
m_extended_packet_length);
/** [DFU init application version] */
// To support application versioning, this check should be updated.
// This template allows for any application to be installed. However,
// customers can place a revision number at the bottom of the application
// to be verified by the bootloader. This can be done at a location
// relative to the application, for example the application start
// address + 0x0100.
/** [DFU init application version] */
// First check to verify the image to be transfered matches the device type.
// If no Device type is present in DFU_DEVICE_INFO then any image will be accepted.
// if ((DFU_DEVICE_INFO->device_type != DFU_DEVICE_TYPE_EMPTY) &&
// (p_init_packet->device_type != DFU_DEVICE_INFO->device_type))
// {
// return NRF_ERROR_INVALID_DATA;
// }
// Second check to verify the image to be transfered matches the device revision.
// If no Device revision is present in DFU_DEVICE_INFO then any image will be accepted.
// if ((DFU_DEVICE_INFO->device_rev != DFU_DEVICE_REVISION_EMPTY) &&
// (p_init_packet->device_rev != DFU_DEVICE_INFO->device_rev))
if ( p_init_packet->device_type != ADAFRUIT_DEVICE_TYPE )
{
return NRF_ERROR_FORBIDDEN;
}
// Adafruit unlock code must match to upgrade SoftDevice and/or Bootloader
if ( image_type & (DFU_UPDATE_SD | DFU_UPDATE_BL) )
{
if (p_init_packet->device_rev != ADAFRUIT_SD_UNLOCK_CODE)
{
return NRF_ERROR_FORBIDDEN;
}
}
// Third check: Check the array of supported SoftDevices by this application.
// If the installed SoftDevice does not match any SoftDevice in the list then an
// error is returned.
while (i < p_init_packet->softdevice_len)
{
if (p_init_packet->softdevice[i] == DFU_SOFTDEVICE_ANY ||
p_init_packet->softdevice[i++] == SD_FWID_GET(MBR_SIZE))
{
return NRF_SUCCESS;
}
}
// No matching SoftDevice found - Return NRF_ERROR_INVALID_DATA.
return NRF_ERROR_INVALID_DATA;
}
uint32_t dfu_init_postvalidate(uint8_t * p_image, uint32_t image_len)
{
uint16_t image_crc;
uint16_t received_crc;
// In order to support hashing (and signing) then the (decrypted) hash should be fetched and
// the corresponding hash should be calculated over the image at this location.
// If hashing (or signing) is added to the system then the CRC validation should be removed.
// calculate CRC from active block.
image_crc = crc16_compute(p_image, image_len, NULL);
// Decode the received CRC from extended data.
received_crc = uint16_decode((uint8_t *)&m_extended_packet[0]);
// Compare the received and calculated CRC.
if (image_crc != received_crc)
{
return NRF_ERROR_INVALID_DATA;
}
return NRF_SUCCESS;
}

View File

@ -0,0 +1,788 @@
/* 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 <stddef.h>
#include "dfu.h"
#include <dfu_types.h>
#include "dfu_bank_internal.h"
#include "nrf.h"
#include "nrf_sdm.h"
#include "app_error.h"
#include "app_timer.h"
#include "bootloader.h"
#include "bootloader_types.h"
#include "pstorage.h"
#include "nrf_mbr.h"
#include "dfu_init.h"
#include "sdk_common.h"
static dfu_state_t m_dfu_state; /**< Current DFU state. */
static uint32_t m_image_size; /**< Size of the image that will be transmitted. */
static dfu_start_packet_t m_start_packet; /**< Start packet received for this update procedure. Contains update mode and image sizes information to be used for image transfer. */
static uint8_t m_init_packet[64]; /**< Init packet, can hold CRC, Hash, Signed Hash and similar, for image validation, integrety check and authorization checking. */
static uint8_t m_init_packet_length; /**< Length of init packet received. */
static uint16_t m_image_crc; /**< Calculated CRC of the image received. */
APP_TIMER_DEF(m_dfu_timer_id); /**< Application timer id. */
static bool m_dfu_timed_out = false; /**< Boolean flag value for tracking DFU timer timeout state. */
static pstorage_handle_t m_storage_handle_app; /**< Pstorage handle for the application area (bank 0). Bank used when updating a SoftDevice w/wo bootloader. Handle also used when swapping received application from bank 1 to bank 0. */
static pstorage_handle_t * mp_storage_handle_active; /**< Pointer to the pstorage handle for the active bank for receiving of data packets. */
static dfu_callback_t m_data_pkt_cb; /**< Callback from DFU Bank module for notification of asynchronous operation such as flash prepare. */
static dfu_bank_func_t m_functions; /**< Structure holding operations for the selected update process. */
/**@brief Function for handling callbacks from pstorage module.
*
* @details Handles pstorage results for clear and storage operation. For detailed description of
* the parameters provided with the callback, please refer to \ref pstorage_ntf_cb_t.
*/
static void pstorage_callback_handler(pstorage_handle_t * p_handle,
uint8_t op_code,
uint32_t result,
uint8_t * p_data,
uint32_t data_len)
{
switch (op_code)
{
case PSTORAGE_STORE_OP_CODE:
if ((m_dfu_state == DFU_STATE_RX_DATA_PKT) && (m_data_pkt_cb != NULL))
{
m_data_pkt_cb(DATA_PACKET, result, p_data);
}
break;
case PSTORAGE_CLEAR_OP_CODE:
if (m_dfu_state == DFU_STATE_PREPARING)
{
m_functions.cleared();
m_dfu_state = DFU_STATE_RDY;
if (m_data_pkt_cb != NULL)
{
m_data_pkt_cb(START_PACKET, result, p_data);
}
}
break;
default:
break;
}
APP_ERROR_CHECK(result);
}
/**@brief Function for handling the DFU timeout.
*
* @param[in] p_context The timeout context.
*/
static void dfu_timeout_handler(void * p_context)
{
UNUSED_PARAMETER(p_context);
dfu_update_status_t update_status;
m_dfu_timed_out = true;
update_status.status_code = DFU_TIMEOUT;
bootloader_dfu_update_process(update_status);
}
/**@brief Function for restarting the DFU Timer.
*
* @details This function will stop and restart the DFU timer. This function will be called by the
* functions handling any DFU packet received from the peer that is transferring a firmware
* image.
*/
static uint32_t dfu_timer_restart(void)
{
if (m_dfu_timed_out)
{
// The DFU timer had already timed out.
return NRF_ERROR_INVALID_STATE;
}
uint32_t err_code = app_timer_stop(m_dfu_timer_id);
APP_ERROR_CHECK(err_code);
err_code = app_timer_start(m_dfu_timer_id, DFU_TIMEOUT_INTERVAL, NULL);
APP_ERROR_CHECK(err_code);
return err_code;
}
/**@brief Function for preparing of flash before receiving SoftDevice image.
*
* @details This function will erase current application area to ensure sufficient amount of
* storage for the SoftDevice image. Upon erase complete a callback will be done.
* See \ref dfu_bank_prepare_t for further details.
*/
static void dfu_prepare_func_app_erase(uint32_t image_size)
{
uint32_t err_code;
mp_storage_handle_active = &m_storage_handle_app;
// Doing a SoftDevice update thus current application must be cleared to ensure enough space
// for new SoftDevice.
m_dfu_state = DFU_STATE_PREPARING;
err_code = pstorage_clear(&m_storage_handle_app, m_image_size);
APP_ERROR_CHECK(err_code);
}
/**@brief Function for handling behaviour when clear operation has completed.
*/
static void dfu_cleared_func_app(void)
{
dfu_update_status_t update_status = {DFU_BANK_0_ERASED, };
bootloader_dfu_update_process(update_status);
}
/**@brief Function for calculating storage offset for receiving SoftDevice image.
*
* @details When a new SoftDevice is received it will be temporary stored in flash before moved to
* address 0x0. In order to succesfully validate transfer and relocation it is important
* that temporary image and final installed image does not ovwerlap hence an offset must
* be calculated in case new image is larger than currently installed SoftDevice.
*/
uint32_t offset_calculate(uint32_t sd_image_size)
{
uint32_t offset = 0;
if (m_start_packet.sd_image_size > DFU_BANK_0_REGION_START)
{
uint32_t page_mask = (CODE_PAGE_SIZE - 1);
uint32_t diff = m_start_packet.sd_image_size - DFU_BANK_0_REGION_START;
offset = diff & ~page_mask;
// Align offset to next page if image size is not page sized.
if ((diff & page_mask) > 0)
{
offset += CODE_PAGE_SIZE;
}
}
return offset;
}
/**@brief Function for activating received SoftDevice image.
*
* @note This function will not move the SoftDevice image.
* The bootloader settings will be marked as SoftDevice update complete and the swapping of
* current SoftDevice will occur after system reset.
*
* @return NRF_SUCCESS on success.
*/
static uint32_t dfu_activate_sd(void)
{
dfu_update_status_t update_status;
update_status.status_code = DFU_UPDATE_SD_COMPLETE;
update_status.app_crc = m_image_crc;
update_status.sd_image_start = DFU_BANK_0_REGION_START;
update_status.sd_size = m_start_packet.sd_image_size;
update_status.bl_size = m_start_packet.bl_image_size;
update_status.app_size = m_start_packet.app_image_size;
bootloader_dfu_update_process(update_status);
return NRF_SUCCESS;
}
/**@brief Function for activating received Application image.
*
* @details This function will move the received application image fram swap (bank 1) to
* application area (bank 0).
*
* @return NRF_SUCCESS on success. Error code otherwise.
*/
static uint32_t dfu_activate_app(void)
{
uint32_t err_code = NRF_SUCCESS;
dfu_update_status_t update_status;
memset(&update_status, 0, sizeof(dfu_update_status_t ));
update_status.status_code = DFU_UPDATE_APP_COMPLETE;
update_status.app_crc = m_image_crc;
update_status.app_size = m_start_packet.app_image_size;
bootloader_dfu_update_process(update_status);
return err_code;
}
/**@brief Function for activating received Bootloader image.
*
* @note This function will not move the bootloader image.
* The bootloader settings will be marked as Bootloader update complete and the swapping of
* current bootloader will occur after system reset.
*
* @return NRF_SUCCESS on success.
*/
static uint32_t dfu_activate_bl(void)
{
dfu_update_status_t update_status;
update_status.status_code = DFU_UPDATE_BOOT_COMPLETE;
update_status.app_crc = m_image_crc;
update_status.sd_size = m_start_packet.sd_image_size;
update_status.bl_size = m_start_packet.bl_image_size;
update_status.app_size = m_start_packet.app_image_size;
bootloader_dfu_update_process(update_status);
return NRF_SUCCESS;
}
uint32_t dfu_init(void)
{
uint32_t err_code;
pstorage_module_param_t storage_module_param = {.cb = pstorage_callback_handler};
m_init_packet_length = 0;
m_image_crc = 0;
err_code = pstorage_register(&storage_module_param, &m_storage_handle_app);
if (err_code != NRF_SUCCESS)
{
m_dfu_state = DFU_STATE_INIT_ERROR;
return err_code;
}
m_storage_handle_app.block_id = DFU_BANK_0_REGION_START;
// Create the timer to monitor the activity by the peer doing the firmware update.
err_code = app_timer_create(&m_dfu_timer_id,
APP_TIMER_MODE_SINGLE_SHOT,
dfu_timeout_handler);
APP_ERROR_CHECK(err_code);
// Start the DFU timer.
err_code = app_timer_start(m_dfu_timer_id, DFU_TIMEOUT_INTERVAL, NULL);
APP_ERROR_CHECK(err_code);
m_data_received = 0;
m_dfu_state = DFU_STATE_IDLE;
return NRF_SUCCESS;
}
void dfu_register_callback(dfu_callback_t callback_handler)
{
m_data_pkt_cb = callback_handler;
}
uint32_t dfu_start_pkt_handle(dfu_update_packet_t * p_packet)
{
uint32_t err_code;
m_start_packet = *(p_packet->params.start_packet);
// Check that the requested update procedure is supported.
// Currently the following combinations are allowed:
// - Application
// - SoftDevice
// - Bootloader
// - SoftDevice with Bootloader
if (IS_UPDATING_APP(m_start_packet) &&
(IS_UPDATING_SD(m_start_packet) || IS_UPDATING_BL(m_start_packet)))
{
// App update is only supported independently.
return NRF_ERROR_NOT_SUPPORTED;
}
if (!(IS_WORD_SIZED(m_start_packet.sd_image_size) &&
IS_WORD_SIZED(m_start_packet.bl_image_size) &&
IS_WORD_SIZED(m_start_packet.app_image_size)))
{
// Image_sizes are not a multiple of 4 (word size).
return NRF_ERROR_NOT_SUPPORTED;
}
m_image_size = m_start_packet.sd_image_size + m_start_packet.bl_image_size +
m_start_packet.app_image_size;
if (m_start_packet.bl_image_size > DFU_BL_IMAGE_MAX_SIZE)
{
return NRF_ERROR_DATA_SIZE;
}
if (m_image_size > (DFU_IMAGE_MAX_SIZE_FULL))
{
return NRF_ERROR_DATA_SIZE;
}
m_functions.prepare = dfu_prepare_func_app_erase;
m_functions.cleared = dfu_cleared_func_app;
if (IS_UPDATING_SD(m_start_packet))
{
m_functions.activate = dfu_activate_sd;
}
else if (IS_UPDATING_BL(m_start_packet))
{
m_functions.activate = dfu_activate_bl;
}
else
{
m_functions.activate = dfu_activate_app;
}
switch (m_dfu_state)
{
case DFU_STATE_IDLE:
// Valid peer activity detected. Hence restart the DFU timer.
err_code = dfu_timer_restart();
VERIFY_SUCCESS(err_code);
m_functions.prepare(m_image_size);
break;
default:
err_code = NRF_ERROR_INVALID_STATE;
break;
}
return err_code;
}
uint32_t dfu_data_pkt_handle(dfu_update_packet_t * p_packet)
{
uint32_t data_length;
uint32_t err_code;
uint32_t * p_data;
VERIFY_PARAM_NOT_NULL(p_packet);
// Check pointer alignment.
if (!is_word_aligned(p_packet->params.data_packet.p_data_packet))
{
// The p_data_packet is not word aligned address.
return NRF_ERROR_INVALID_ADDR;
}
switch (m_dfu_state)
{
case DFU_STATE_RDY:
case DFU_STATE_RX_INIT_PKT:
return NRF_ERROR_INVALID_STATE;
case DFU_STATE_RX_DATA_PKT:
data_length = p_packet->params.data_packet.packet_length * sizeof(uint32_t);
if ((m_data_received + data_length) > m_image_size)
{
// The caller is trying to write more bytes into the flash than the size provided to
// the dfu_image_size_set function. This is treated as a serious error condition and
// an unrecoverable one. Hence point the variable mp_app_write_address to the top of
// the flash area. This will ensure that all future application data packet writes
// will be blocked because of the above check.
m_data_received = 0xFFFFFFFF;
return NRF_ERROR_DATA_SIZE;
}
// Valid peer activity detected. Hence restart the DFU timer.
err_code = dfu_timer_restart();
VERIFY_SUCCESS(err_code);
p_data = (uint32_t *)p_packet->params.data_packet.p_data_packet;
err_code = pstorage_store(mp_storage_handle_active,
(uint8_t *)p_data,
data_length,
m_data_received);
VERIFY_SUCCESS(err_code);
m_data_received += data_length;
if (m_data_received != m_image_size)
{
// The entire image is not received yet. More data is expected.
err_code = NRF_ERROR_INVALID_LENGTH;
}
else
{
// The entire image has been received. Return NRF_SUCCESS.
err_code = NRF_SUCCESS;
}
break;
default:
err_code = NRF_ERROR_INVALID_STATE;
break;
}
return err_code;
}
uint32_t dfu_init_pkt_complete(void)
{
uint32_t err_code = NRF_ERROR_INVALID_STATE;
// DFU initialization has been done and a start packet has been received.
if (IMAGE_WRITE_IN_PROGRESS())
{
// Image write is already in progress. Cannot handle an init packet now.
return NRF_ERROR_INVALID_STATE;
}
if (m_dfu_state == DFU_STATE_RX_INIT_PKT)
{
err_code = dfu_init_prevalidate(m_init_packet, m_init_packet_length, m_start_packet.dfu_update_mode);
if (err_code == NRF_SUCCESS)
{
m_dfu_state = DFU_STATE_RX_DATA_PKT;
}
else
{
m_init_packet_length = 0;
}
}
return err_code;
}
uint32_t dfu_init_pkt_handle(dfu_update_packet_t * p_packet)
{
uint32_t err_code = NRF_SUCCESS;
uint32_t length;
switch (m_dfu_state)
{
case DFU_STATE_RDY:
m_dfu_state = DFU_STATE_RX_INIT_PKT;
// When receiving init packet in state ready just update and fall through this case.
case DFU_STATE_RX_INIT_PKT:
// DFU initialization has been done and a start packet has been received.
if (IMAGE_WRITE_IN_PROGRESS())
{
// Image write is already in progress. Cannot handle an init packet now.
return NRF_ERROR_INVALID_STATE;
}
// Valid peer activity detected. Hence restart the DFU timer.
err_code = dfu_timer_restart();
VERIFY_SUCCESS(err_code);
length = p_packet->params.data_packet.packet_length * sizeof(uint32_t);
if ((m_init_packet_length + length) > sizeof(m_init_packet))
{
return NRF_ERROR_INVALID_LENGTH;
}
memcpy(&m_init_packet[m_init_packet_length],
&p_packet->params.data_packet.p_data_packet[0],
length);
m_init_packet_length += length;
break;
default:
// Either the start packet was not received or dfu_init function was not called before.
err_code = NRF_ERROR_INVALID_STATE;
break;
}
return err_code;
}
uint32_t dfu_image_validate()
{
uint32_t err_code;
switch (m_dfu_state)
{
case DFU_STATE_RX_DATA_PKT:
// Check if the application image write has finished.
if (m_data_received != m_image_size)
{
// Image not yet fully transfered by the peer or the peer has attempted to write
// too much data. Hence the validation should fail.
err_code = NRF_ERROR_INVALID_STATE;
}
else
{
m_dfu_state = DFU_STATE_VALIDATE;
// Valid peer activity detected. Hence restart the DFU timer.
err_code = dfu_timer_restart();
if (err_code == NRF_SUCCESS)
{
err_code = dfu_init_postvalidate((uint8_t *)mp_storage_handle_active->block_id,
m_image_size);
VERIFY_SUCCESS(err_code);
m_dfu_state = DFU_STATE_WAIT_4_ACTIVATE;
}
}
break;
default:
err_code = NRF_ERROR_INVALID_STATE;
break;
}
return err_code;
}
uint32_t dfu_image_activate()
{
uint32_t err_code;
switch (m_dfu_state)
{
case DFU_STATE_WAIT_4_ACTIVATE:
// Stop the DFU Timer because the peer activity need not be monitored any longer.
err_code = app_timer_stop(m_dfu_timer_id);
APP_ERROR_CHECK(err_code);
err_code = m_functions.activate();
break;
default:
err_code = NRF_ERROR_INVALID_STATE;
break;
}
return err_code;
}
void dfu_reset(void)
{
dfu_update_status_t update_status;
update_status.status_code = DFU_RESET;
bootloader_dfu_update_process(update_status);
}
static uint32_t dfu_compare_block(uint32_t * ptr1, uint32_t * ptr2, uint32_t len)
{
sd_mbr_command_t sd_mbr_cmd;
sd_mbr_cmd.command = SD_MBR_COMMAND_COMPARE;
sd_mbr_cmd.params.compare.ptr1 = ptr1;
sd_mbr_cmd.params.compare.ptr2 = ptr2;
sd_mbr_cmd.params.compare.len = len / sizeof(uint32_t);
return sd_mbr_command(&sd_mbr_cmd);
}
static uint32_t dfu_copy_sd(uint32_t * src, uint32_t * dst, uint32_t len)
{
sd_mbr_command_t sd_mbr_cmd;
sd_mbr_cmd.command = SD_MBR_COMMAND_COPY_SD;
sd_mbr_cmd.params.copy_sd.src = src;
sd_mbr_cmd.params.copy_sd.dst = dst;
sd_mbr_cmd.params.copy_sd.len = len / sizeof(uint32_t);
return sd_mbr_command(&sd_mbr_cmd);
}
static uint32_t dfu_sd_img_block_swap(uint32_t * src,
uint32_t * dst,
uint32_t len,
uint32_t block_size)
{
// It is neccesarry to swap the new SoftDevice in 3 rounds to ensure correct copy of data
// and verifucation of data in case power reset occurs during write to flash.
// To ensure the robustness of swapping the images are compared backwards till start of
// image swap. If the back is identical everything is swapped.
uint32_t err_code = dfu_compare_block(src, dst, len);
if (err_code == NRF_SUCCESS)
{
return err_code;
}
if ((uint32_t)dst > SOFTDEVICE_REGION_START)
{
err_code = dfu_sd_img_block_swap((uint32_t *)((uint32_t)src - block_size),
(uint32_t *)((uint32_t)dst - block_size),
block_size,
block_size);
VERIFY_SUCCESS(err_code);
}
err_code = dfu_copy_sd(src, dst, len);
VERIFY_SUCCESS(err_code);
return dfu_compare_block(src, dst, len);
}
uint32_t dfu_sd_image_swap(void)
{
bootloader_settings_t boot_settings;
bootloader_settings_get(&boot_settings);
if (boot_settings.sd_image_size == 0)
{
return NRF_SUCCESS;
}
if ((SOFTDEVICE_REGION_START + boot_settings.sd_image_size) > boot_settings.sd_image_start)
{
uint32_t err_code;
uint32_t sd_start = SOFTDEVICE_REGION_START;
uint32_t block_size = (boot_settings.sd_image_start - sd_start) / 2;
/* ##### FIX START ##### */
block_size &= ~(uint32_t)(CODE_PAGE_SIZE - 1);
/* ##### FIX END ##### */
uint32_t image_end = boot_settings.sd_image_start + boot_settings.sd_image_size;
uint32_t img_block_start = boot_settings.sd_image_start + 2 * block_size;
uint32_t sd_block_start = sd_start + 2 * block_size;
if (SD_SIZE_GET(MBR_SIZE) < boot_settings.sd_image_size)
{
// This will clear a page thus ensuring the old image is invalidated before swapping.
err_code = dfu_copy_sd((uint32_t *)(sd_start + block_size),
(uint32_t *)(sd_start + block_size),
sizeof(uint32_t));
VERIFY_SUCCESS(err_code);
err_code = dfu_copy_sd((uint32_t *)sd_start, (uint32_t *)sd_start, sizeof(uint32_t));
VERIFY_SUCCESS(err_code);
}
return dfu_sd_img_block_swap((uint32_t *)img_block_start,
(uint32_t *)sd_block_start,
image_end - img_block_start,
block_size);
}
else
{
if (boot_settings.sd_image_size != 0)
{
return dfu_copy_sd((uint32_t *)boot_settings.sd_image_start,
(uint32_t *)SOFTDEVICE_REGION_START,
boot_settings.sd_image_size);
}
}
return NRF_SUCCESS;
}
uint32_t dfu_bl_image_swap(void)
{
bootloader_settings_t bootloader_settings;
sd_mbr_command_t sd_mbr_cmd;
bootloader_settings_get(&bootloader_settings);
if (bootloader_settings.bl_image_size != 0)
{
uint32_t bl_image_start = (bootloader_settings.sd_image_size == 0) ?
DFU_BANK_0_REGION_START :
bootloader_settings.sd_image_start +
bootloader_settings.sd_image_size;
sd_mbr_cmd.command = SD_MBR_COMMAND_COPY_BL;
sd_mbr_cmd.params.copy_bl.bl_src = (uint32_t *)(bl_image_start);
sd_mbr_cmd.params.copy_bl.bl_len = bootloader_settings.bl_image_size / sizeof(uint32_t);
return sd_mbr_command(&sd_mbr_cmd);
}
return NRF_SUCCESS;
}
uint32_t dfu_bl_image_validate(void)
{
bootloader_settings_t bootloader_settings;
sd_mbr_command_t sd_mbr_cmd;
bootloader_settings_get(&bootloader_settings);
if (bootloader_settings.bl_image_size != 0)
{
uint32_t bl_image_start = (bootloader_settings.sd_image_size == 0) ?
DFU_BANK_0_REGION_START :
bootloader_settings.sd_image_start +
bootloader_settings.sd_image_size;
sd_mbr_cmd.command = SD_MBR_COMMAND_COMPARE;
sd_mbr_cmd.params.compare.ptr1 = (uint32_t *)BOOTLOADER_REGION_START;
sd_mbr_cmd.params.compare.ptr2 = (uint32_t *)(bl_image_start);
sd_mbr_cmd.params.compare.len = bootloader_settings.bl_image_size / sizeof(uint32_t);
return sd_mbr_command(&sd_mbr_cmd);
}
return NRF_SUCCESS;
}
uint32_t dfu_sd_image_validate(void)
{
bootloader_settings_t bootloader_settings;
sd_mbr_command_t sd_mbr_cmd;
bootloader_settings_get(&bootloader_settings);
if (bootloader_settings.sd_image_size == 0)
{
return NRF_SUCCESS;
}
if ((SOFTDEVICE_REGION_START + bootloader_settings.sd_image_size) > bootloader_settings.sd_image_start)
{
uint32_t sd_start = SOFTDEVICE_REGION_START;
uint32_t block_size = (bootloader_settings.sd_image_start - sd_start) / 2;
uint32_t image_end = bootloader_settings.sd_image_start +
bootloader_settings.sd_image_size;
uint32_t img_block_start = bootloader_settings.sd_image_start + 2 * block_size;
uint32_t sd_block_start = sd_start + 2 * block_size;
if (SD_SIZE_GET(MBR_SIZE) < bootloader_settings.sd_image_size)
{
return NRF_ERROR_NULL;
}
return dfu_sd_img_block_swap((uint32_t *)img_block_start,
(uint32_t *)sd_block_start,
image_end - img_block_start,
block_size);
}
sd_mbr_cmd.command = SD_MBR_COMMAND_COMPARE;
sd_mbr_cmd.params.compare.ptr1 = (uint32_t *)SOFTDEVICE_REGION_START;
sd_mbr_cmd.params.compare.ptr2 = (uint32_t *)bootloader_settings.sd_image_start;
sd_mbr_cmd.params.compare.len = bootloader_settings.sd_image_size / sizeof(uint32_t);
return sd_mbr_command(&sd_mbr_cmd);
}

View File

@ -0,0 +1,44 @@
/* 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.
*
*/
/**@file
*
* @defgroup nrf_dfu_transport DFU transport API.
* @{
*
* @brief DFU transport module interface.
*/
#ifndef DFU_TRANSPORT_H__
#define DFU_TRANSPORT_H__
#include <stdint.h>
/**@brief Function for starting the update of Device Firmware.
*
* @retval NRF_SUCCESS Operation success.
*/
uint32_t dfu_transport_serial_update_start(void);
/**@brief Function for closing the transport layer.
*
* @retval NRF_SUCCESS Operation success.
*/
uint32_t dfu_transport_serial_close(void);
uint32_t dfu_transport_ble_update_start(void);
uint32_t dfu_transport_ble_close();
#endif // DFU_TRANSPORT_H__
/**@} */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,314 @@
/* 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 "dfu_transport.h"
#include <stddef.h>
#include "dfu.h"
#include <dfu_types.h>
#include "app_error.h"
#include "app_util.h"
#include "hci_transport.h"
#include "app_timer.h"
#include "app_scheduler.h"
#define MAX_BUFFERS 4u /**< Maximum number of buffers that can be received queued without being consumed. */
/**
* defgroup Data Packet Queue Access Operation Macros
* @{
*/
/** Provides status showing if the queue is full or not. */
#define DATA_QUEUE_FULL() \
(((MAX_BUFFERS -1) == m_data_queue.count) ? true : false)
/** Provides status showing if the queue is empty or not */
#define DATA_QUEUE_EMPTY() \
((0 == m_data_queue.count) ? true : false)
/** Initializes an element of the data queue. */
#define DATA_QUEUE_ELEMENT_INIT(i) \
m_data_queue.data_packet[(i)].packet_type = INVALID_PACKET
/** Sets the packet type of an element of the data queue. */
#define DATA_QUEUE_ELEMENT_SET_PTYPE(i, t) \
m_data_queue.data_packet[(i)].packet_type = (t)
/** Copies a data packet pointer of an element of the data queue. */
#define DATA_QUEUE_ELEMENT_COPY_PDATA(i, dp) \
m_data_queue.data_packet[(i)].params.data_packet.p_data_packet = (uint32_t *)(dp)
/** Sets the packet length of an element in the data queue. */
#define DATA_QUEUE_ELEMENT_SET_PLEN(i, l) \
m_data_queue.data_packet[(i)].params.data_packet.packet_length = (l)
/** Gets a data packet pointer of an element in the data queue. */
#define DATA_QUEUE_ELEMENT_GET_PDATA(i) \
(m_data_queue.data_packet[(i)].params.data_packet.p_data_packet)
/** Gets the packet type of an element in the data queue. */
#define DATA_QUEUE_ELEMENT_GET_PTYPE(i) \
m_data_queue.data_packet[(i)].packet_type
/* @} */
/** Abstracts data packet queue */
typedef struct
{
dfu_update_packet_t data_packet[MAX_BUFFERS]; /**< Bootloader data packets used when processing data from the UART. */
volatile uint8_t count; /**< Counter to maintain number of elements in the queue. */
} dfu_data_queue_t;
static dfu_data_queue_t m_data_queue; /**< Received-data packet queue. */
/**@brief Initializes an element of the data buffer queue.
*
* @param[in] element_index index of the element.
*/
static void data_queue_element_init (uint8_t element_index)
{
DATA_QUEUE_ELEMENT_INIT(element_index);
DATA_QUEUE_ELEMENT_SET_PTYPE(element_index, INVALID_PACKET);
DATA_QUEUE_ELEMENT_COPY_PDATA(element_index, NULL);
DATA_QUEUE_ELEMENT_SET_PLEN(element_index, 0);
}
/** Initializes data buffer queue */
static void data_queue_init(void)
{
uint32_t index;
m_data_queue.count = 0;
for (index = 0; index < MAX_BUFFERS; index++)
{
data_queue_element_init(index);
}
}
/**@brief Function for freeing an element.
*
* @param[in] element_index index of the element.
*/
static uint32_t data_queue_element_free(uint8_t element_index)
{
uint8_t * p_data;
uint32_t retval;
retval = NRF_ERROR_INVALID_PARAM;
if (MAX_BUFFERS > element_index)
{
p_data = (uint8_t *)DATA_QUEUE_ELEMENT_GET_PDATA(element_index);
if (INVALID_PACKET != DATA_QUEUE_ELEMENT_GET_PTYPE(element_index))
{
m_data_queue.count--;
data_queue_element_init (element_index);
retval = hci_transport_rx_pkt_consume((p_data - 4));
APP_ERROR_CHECK(retval);
}
}
else
{
return NRF_ERROR_INVALID_PARAM;
}
return NRF_SUCCESS;
}
/**@brief Function for Allocating element.
*
* @param[in] packet_type packet type.
* @param[out] p_element_index index of the element.
*/
static uint32_t data_queue_element_alloc(uint8_t * p_element_index, uint8_t packet_type)
{
uint32_t retval;
uint32_t index;
retval = NRF_ERROR_NO_MEM;
if (INVALID_PACKET == packet_type)
{
retval = NRF_ERROR_INVALID_PARAM;
}
else if (true == DATA_QUEUE_FULL())
{
retval = NRF_ERROR_NO_MEM;
}
else
{
for (index = 0; index < MAX_BUFFERS; index++)
{
if (INVALID_PACKET == DATA_QUEUE_ELEMENT_GET_PTYPE(index))
{
// Found a free element: allocate, and end search.
*p_element_index = index;
DATA_QUEUE_ELEMENT_SET_PTYPE(index, packet_type);
retval = NRF_SUCCESS;
m_data_queue.count++;
break;
}
}
}
return retval;
}
// Flush everything on disconnect or stop.
static void data_queue_flush(void)
{
uint32_t index;
for (index = 0; index < MAX_BUFFERS; index++)
{
// In this case it does not matter if free succeeded or not as data packets are being flushed because DFU Trnsport was closed
(void)data_queue_element_free(index);
}
}
/**@brief Function for handling the callback events from the dfu module.
* Callbacks are expected when \ref dfu_data_pkt_handle has been executed.
*
* @param[in] packet Packet type for which this callback is related. START_PACKET, DATA_PACKET.
* @param[in] result Operation result code. NRF_SUCCESS when a queued operation was successful.
* @param[in] p_data Pointer to the data to which the operation is related.
*/
static void dfu_cb_handler(uint32_t packet, uint32_t result, uint8_t * p_data)
{
APP_ERROR_CHECK(result);
}
static void process_dfu_packet(void * p_event_data, uint16_t event_size)
{
uint32_t retval;
uint32_t index;
dfu_update_packet_t * packet;
// Adafruit modification for startup dfu
extern bool forced_startup_dfu_packet_received;
forced_startup_dfu_packet_received = true;
while (false == DATA_QUEUE_EMPTY())
{
// Fetch the element to be processed.
for (index = 0; index < MAX_BUFFERS ; index++)
{
packet = &m_data_queue.data_packet[index];
if (INVALID_PACKET != packet->packet_type)
{
extern void blinky_fast_set(bool isFast);
switch (DATA_QUEUE_ELEMENT_GET_PTYPE(index))
{
case DATA_PACKET:
(void)dfu_data_pkt_handle(packet);
break;
case START_PACKET:
packet->params.start_packet =
(dfu_start_packet_t*)packet->params.data_packet.p_data_packet;
retval = dfu_start_pkt_handle(packet);
APP_ERROR_CHECK(retval);
break;
case INIT_PACKET:
(void)dfu_init_pkt_handle(packet);
retval = dfu_init_pkt_complete();
APP_ERROR_CHECK(retval);
blinky_fast_set(true);
break;
case STOP_DATA_PACKET:
(void)dfu_image_validate();
(void)dfu_image_activate();
blinky_fast_set(false);
// Break the loop by returning.
return;
default:
// No implementation needed.
break;
}
// Free the processed element.
retval = data_queue_element_free(index);
APP_ERROR_CHECK(retval);
}
}
}
}
void rpc_transport_event_handler(hci_transport_evt_t event)
{
uint32_t retval;
uint16_t rpc_cmd_length_read = 0;
uint8_t * p_rpc_cmd_buffer = NULL;
uint8_t element_index;
retval = hci_transport_rx_pkt_extract(&p_rpc_cmd_buffer, &rpc_cmd_length_read);
if (NRF_SUCCESS == retval)
{
// Verify if the data queue can buffer the packet.
retval = data_queue_element_alloc(&element_index, p_rpc_cmd_buffer[0]);
if (NRF_SUCCESS == retval)
{
//subtract 1 since we are interested in payload length and not the type field.
DATA_QUEUE_ELEMENT_SET_PLEN(element_index,(rpc_cmd_length_read / sizeof(uint32_t)) - 1);
DATA_QUEUE_ELEMENT_COPY_PDATA(element_index, &p_rpc_cmd_buffer[4]);
retval = app_sched_event_put(NULL, 0, process_dfu_packet);
}
}
if (p_rpc_cmd_buffer != NULL && NRF_SUCCESS != retval)
{
// Free the packet that could not be processed.
retval = hci_transport_rx_pkt_consume(p_rpc_cmd_buffer);
APP_ERROR_CHECK(retval);
}
}
uint32_t dfu_transport_serial_update_start(void)
{
uint32_t err_code;
// Initialize data buffer queue.
data_queue_init();
dfu_register_callback(dfu_cb_handler);
// Open transport layer.
err_code = hci_transport_open();
APP_ERROR_CHECK(err_code);
// Register callback to be run when commands have been received by the transport layer.
err_code = hci_transport_evt_handler_reg(rpc_transport_event_handler);
APP_ERROR_CHECK(err_code);
return NRF_SUCCESS;
}
uint32_t dfu_transport_serial_close(void)
{
// Remove all buffered packets.
data_queue_flush();
return hci_transport_close();
}

View File

@ -0,0 +1,168 @@
/* 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.
*
*/
/**@file
*
* @defgroup nrf_dfu_types Types and definitions.
* @{
*
* @ingroup nrf_dfu
*
* @brief Device Firmware Update module type and definitions.
*/
#ifndef DFU_TYPES_H__
#define DFU_TYPES_H__
#include <stdint.h>
#include "nrf_sdm.h"
#include "nrf_mbr.h"
#include "nrf.h"
#include "app_util.h"
#define NRF_UICR_BOOT_START_ADDRESS (NRF_UICR_BASE + 0x14) /**< Register where the bootloader start address is stored in the UICR register. */
#if defined(NRF52)
#define NRF_UICR_MBR_PARAMS_PAGE_ADDRESS (NRF_UICR_BASE + 0x18) /**< Register where the mbr params page is stored in the UICR register. (Only in use in nRF52 MBR).*/
#endif
#define CODE_REGION_1_START SD_SIZE_GET(MBR_SIZE) /**< This field should correspond to the size of Code Region 0, (which is identical to Start of Code Region 1), found in UICR.CLEN0 register. This value is used for compile safety, as the linker will fail if application expands into bootloader. Runtime, the bootloader will use the value found in UICR.CLEN0. */
#define SOFTDEVICE_REGION_START MBR_SIZE /**< This field should correspond to start address of the bootloader, found in UICR.RESERVED, 0x10001014, register. This value is used for sanity check, so the bootloader will fail immediately if this value differs from runtime value. The value is used to determine max application size for updating. */
#ifdef NRF51
#define CODE_PAGE_SIZE 0x0400 /**< Size of a flash codepage. Used for size of the reserved flash space in the bootloader region. Will be runtime checked against NRF_UICR->CODEPAGESIZE to ensure the region is correct. */
#ifdef SIGNING
#define BOOTLOADER_REGION_START 0x00039C00 /**< This field should correspond to start address of the bootloader, found in UICR.RESERVED, 0x10001014, register. This value is used for sanity check, so the bootloader will fail immediately if this value differs from runtime value. The value is used to determine max application size for updating. */
#define BOOTLOADER_SETTINGS_ADDRESS 0x0003D800 /**< The field specifies the page location of the bootloader settings address. */
#else
#define BOOTLOADER_REGION_START 0x0003C000 /**< This field should correspond to start address of the bootloader, found in UICR.RESERVED, 0x10001014, register. This value is used for sanity check, so the bootloader will fail immediately if this value differs from runtime value. The value is used to determine max application size for updating. */
#define BOOTLOADER_SETTINGS_ADDRESS 0x0003FC00 /**< The field specifies the page location of the bootloader settings address. */
#endif
#elif defined(NRF52)
#define BOOTLOADER_REGION_START 0x00074000 /**< This field should correspond to start address of the bootloader, found in UICR.RESERVED, 0x10001014, register. This value is used for sanity check, so the bootloader will fail immediately if this value differs from runtime value. The value is used to determine max application size for updating. */
#define BOOTLOADER_SETTINGS_ADDRESS 0x0007F000 /**< The field specifies the page location of the bootloader settings address. */
#define BOOTLOADER_MBR_PARAMS_PAGE_ADDRESS 0x0007E000 /**< The field specifies the page location of the mbr params page address. */
#define CODE_PAGE_SIZE 0x1000 /**< Size of a flash codepage. Used for size of the reserved flash space in the bootloader region. Will be runtime checked against NRF_UICR->CODEPAGESIZE to ensure the region is correct. */
#else
#error No target defined
#endif
#define DFU_REGION_TOTAL_SIZE (BOOTLOADER_REGION_START - CODE_REGION_1_START) /**< Total size of the region between SD and Bootloader. */
#ifndef DFU_APP_DATA_RESERVED
#define DFU_APP_DATA_RESERVED CODE_PAGE_SIZE * 7 /**< Size of Application Data that must be preserved between application updates. This value must be a multiple of page size. Page size is 0x400 (1024d) bytes, thus this value must be 0x0000, 0x0400, 0x0800, 0x0C00, 0x1000, etc. */
#endif
#define DFU_IMAGE_MAX_SIZE_FULL (DFU_REGION_TOTAL_SIZE - DFU_APP_DATA_RESERVED) /**< Maximum size of an application, excluding save data from the application. */
#define DFU_IMAGE_MAX_SIZE_BANKED (((DFU_IMAGE_MAX_SIZE_FULL) - \
(DFU_IMAGE_MAX_SIZE_FULL % (2 * CODE_PAGE_SIZE)))/2) /**< Maximum size of an application, excluding save data from the application. */
#define DFU_BL_IMAGE_MAX_SIZE (BOOTLOADER_SETTINGS_ADDRESS - BOOTLOADER_REGION_START) /**< Maximum size of a bootloader, excluding save data from the current bootloader. */
#define DFU_BANK_0_REGION_START CODE_REGION_1_START /**< Bank 0 region start. */
#define DFU_BANK_1_REGION_START (DFU_BANK_0_REGION_START + DFU_IMAGE_MAX_SIZE_BANKED) /**< Bank 1 region start. */
#define EMPTY_FLASH_MASK 0xFFFFFFFF /**< Bit mask that defines an empty address in flash. */
#define INVALID_PACKET 0x00 /**< Invalid packet identifies. */
#define INIT_PACKET 0x01 /**< Packet identifies for initialization packet. */
#define STOP_INIT_PACKET 0x02 /**< Packet identifies for stop initialization packet. Used when complete init packet has been received so that the init packet can be used for pre validaiton. */
#define START_PACKET 0x03 /**< Packet identifies for the Data Start Packet. */
#define DATA_PACKET 0x04 /**< Packet identifies for a Data Packet. */
#define STOP_DATA_PACKET 0x05 /**< Packet identifies for the Data Stop Packet. */
#define DFU_UPDATE_SD 0x01 /**< Bit field indicating update of SoftDevice is ongoing. */
#define DFU_UPDATE_BL 0x02 /**< Bit field indicating update of bootloader is ongoing. */
#define DFU_UPDATE_APP 0x04 /**< Bit field indicating update of application is ongoing. */
#define DFU_INIT_RX 0x00 /**< Op Code identifies for receiving init packet. */
#define DFU_INIT_COMPLETE 0x01 /**< Op Code identifies for transmission complete of init packet. */
// Safe guard to ensure during compile time that the DFU_APP_DATA_RESERVED is a multiple of page size.
STATIC_ASSERT((((DFU_APP_DATA_RESERVED) & (CODE_PAGE_SIZE - 1)) == 0x00));
/**@brief Structure holding a start packet containing update mode and image sizes.
*/
typedef struct
{
uint8_t dfu_update_mode; /**< Packet type, used to identify the content of the received packet referenced by data packet. */
uint32_t sd_image_size; /**< Size of the SoftDevice image to be transferred. Zero if no SoftDevice image will be transfered. */
uint32_t bl_image_size; /**< Size of the Bootloader image to be transferred. Zero if no Bootloader image will be transfered. */
uint32_t app_image_size; /**< Size of the application image to be transmitted. Zero if no Bootloader image will be transfered. */
} dfu_start_packet_t;
/**@brief Structure holding a bootloader init/data packet received.
*/
typedef struct
{
uint32_t packet_length; /**< Packet length of the data packet. Each data is word size, meaning length of 4 is 4 words, not bytes. */
uint32_t * p_data_packet; /**< Data Packet received. Each data is a word size entry. */
} dfu_data_packet_t;
/**@brief Structure for holding dfu update packet. Packet type indicate the type of packet.
*/
typedef struct
{
uint32_t packet_type; /**< Packet type, used to identify the content of the received packet referenced by data packet. */
union
{
dfu_data_packet_t data_packet; /**< Used when packet type is INIT_PACKET or DATA_PACKET. Packet contains data received for init or data. */
dfu_start_packet_t * start_packet; /**< Used when packet type is START_DATA_PACKET. Will contain information on software to be updtaed, i.e. SoftDevice, Bootloader and/or Application along with image sizes. */
} params;
} dfu_update_packet_t;
/**@brief DFU status error codes.
*/
typedef enum
{
DFU_UPDATE_APP_COMPLETE, /**< Status update of application complete.*/
DFU_UPDATE_SD_COMPLETE, /**< Status update of SoftDevice update complete. Note that this solely indicates that a new SoftDevice has been received and stored in bank 0 and 1. */
DFU_UPDATE_SD_SWAPPED, /**< Status update of SoftDevice update complete. Note that this solely indicates that a new SoftDevice has been received and stored in bank 0 and 1. */
DFU_UPDATE_BOOT_COMPLETE, /**< Status update complete.*/
DFU_BANK_0_ERASED, /**< Status bank 0 erased.*/
DFU_TIMEOUT, /**< Status timeout.*/
DFU_RESET /**< Status Reset to indicate current update procedure has been aborted and system should reset. */
} dfu_update_status_code_t;
/**@brief Structure holding DFU complete event.
*/
typedef struct
{
dfu_update_status_code_t status_code; /**< Device Firmware Update status. */
uint16_t app_crc; /**< CRC of the recieved application. */
uint32_t sd_size; /**< Size of the recieved SoftDevice. */
uint32_t bl_size; /**< Size of the recieved BootLoader. */
uint32_t app_size; /**< Size of the recieved Application. */
uint32_t sd_image_start; /**< Location in flash where the received SoftDevice image is stored. */
} dfu_update_status_t;
/**@brief Update complete handler type. */
typedef void (*dfu_complete_handler_t)(dfu_update_status_t dfu_update_status);
#endif // DFU_TYPES_H__
/**@} */

View File

@ -0,0 +1,32 @@
/* 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.
*
*/
/** @file
*
* @defgroup memory_pool_internal Memory Pool Internal
* @{
* @ingroup memory_pool
*
* @brief Memory pool internal definitions
*/
#ifndef MEM_POOL_INTERNAL_H__
#define MEM_POOL_INTERNAL_H__
#define TX_BUF_SIZE 32u /**< TX buffer size in bytes. */
#define RX_BUF_SIZE 600u /**< RX buffer size in bytes. */
#define RX_BUF_QUEUE_SIZE 2u /**< RX buffer element size. */
#endif // MEM_POOL_INTERNAL_H__
/** @} */

View File

@ -0,0 +1,45 @@
/* 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.
*
*/
/**@file
*
* @defgroup ble_sdk_bootloader_hci_congfig HCI Transport Layer Configuration
* @{
* @ingroup dfu_bootloader_api
* @brief Definition of HCI Transport Layer configurable parameters
*/
#ifndef HCI_TRANSPORT_CONFIG_H__
#define HCI_TRANSPORT_CONFIG_H__
#include "boards.h" /**< Default include for boards.h which means that default pin numbers will be used for RX, TX, CTS, and RTS on the UART. Other pin number can be used if desired. */
/** This section covers configurable parameters for the HCI Transport SLIP layer. */
#define HCI_SLIP_UART_RX_PIN_NUMBER RX_PIN_NUMBER /**< Defines the UART RX pin number. The default pin for the board is chosen, but can be overwritten. */
#define HCI_SLIP_UART_TX_PIN_NUMBER TX_PIN_NUMBER /**< Defines the UART TX pin number. The default pin for the board is chosen, but can be overwritten. */
#define HCI_SLIP_UART_RTS_PIN_NUMBER RTS_PIN_NUMBER /**< Defines the UART RTS pin number. The default pin for the board is chosen, but can be overwritten. */
#define HCI_SLIP_UART_CTS_PIN_NUMBER CTS_PIN_NUMBER /**< Defines the UART CTS pin number. The default pin for the board is chosen, but can be overwritten. */
#define HCI_SLIP_UART_MODE APP_UART_FLOW_CONTROL_DISABLED /**< Defines the UART mode to be used. Use UART Low Power with Flow Control - Valid values are defined in \ref app_uart_flow_control_t. For further information on the UART Low Power mode, please refer to: \ref app_uart . */
#define HCI_SLIP_UART_BAUDRATE UART_BAUDRATE_BAUDRATE_Baud115200 // UART_BAUDRATE_BAUDRATE_Baud38400 /**< Defines the UART Baud rate. Default is 38400 baud. */
/** This section covers configurable parameters for the HCI Transport layer that are used for calculating correct value for the retransmission timer timeout. */
#define MAX_PACKET_SIZE_IN_BITS 8000u /**< Maximum size of a single application packet in bits. */
#define USED_BAUD_RATE 115200u /**< The used uart baudrate. */
#endif // HCI_TRANSPORT_CONFIG_H__
/** @} */

View File

@ -0,0 +1,31 @@
/* 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 "crc16.h"
#include <stdlib.h>
uint16_t crc16_compute(uint8_t const * p_data, uint32_t size, uint16_t const * p_crc)
{
uint16_t crc = (p_crc == NULL) ? 0xFFFF : *p_crc;
for (uint32_t i = 0; i < size; i++)
{
crc = (uint8_t)(crc >> 8) | (crc << 8);
crc ^= p_data[i];
crc ^= (uint8_t)(crc & 0xFF) >> 4;
crc ^= (crc << 8) << 4;
crc ^= ((crc & 0xFF) << 4) << 1;
}
return crc;
}

View File

@ -0,0 +1,44 @@
/* 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.
*
*/
/** @file
*
* @defgroup crc_compute CRC compute
* @{
* @ingroup hci_transport
*
* @brief This module implements CRC-16-CCITT (polynomial 0x1021) with 0xFFFF initial value.
* The data can be passed in multiple blocks.
*/
#ifndef CRC16_H__
#define CRC16_H__
#include <stdint.h>
/**@brief Function for calculating CRC-16 in blocks.
*
* Feed each consecutive data block into this function, along with the current value of p_crc as
* returned by the previous call of this function. The first call of this function should pass NULL
* as the initial value of the crc in p_crc.
*
* @param[in] p_data The input data block for computation.
* @param[in] size The size of the input data block in bytes.
* @param[in] p_crc The previous calculated CRC-16 value or NULL if first call.
*
* @return The updated CRC-16 value, based on the input supplied.
*/
uint16_t crc16_compute(uint8_t const * p_data, uint32_t size, uint16_t const * p_crc);
#endif // CRC16_H__
/** @} */

View File

@ -0,0 +1,186 @@
/* 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 "app_fifo.h"
#include "sdk_common.h"
#include "nordic_common.h"
static __INLINE uint32_t fifo_length(app_fifo_t * p_fifo)
{
uint32_t tmp = p_fifo->read_pos;
return p_fifo->write_pos - tmp;
}
#define FIFO_LENGTH fifo_length(p_fifo) /**< Macro for calculating the FIFO length. */
/**@brief Put one byte to the FIFO. */
static __INLINE void fifo_put(app_fifo_t * p_fifo, uint8_t byte)
{
p_fifo->p_buf[p_fifo->write_pos & p_fifo->buf_size_mask] = byte;
p_fifo->write_pos++;
}
/**@brief Look at one byte in the FIFO. */
static __INLINE void fifo_peek(app_fifo_t * p_fifo, uint16_t index, uint8_t * p_byte)
{
*p_byte = p_fifo->p_buf[(p_fifo->read_pos + index) & p_fifo->buf_size_mask];
}
/**@brief Get one byte from the FIFO. */
static __INLINE void fifo_get(app_fifo_t * p_fifo, uint8_t * p_byte)
{
fifo_peek(p_fifo, 0, p_byte);
p_fifo->read_pos++;
}
uint32_t app_fifo_init(app_fifo_t * p_fifo, uint8_t * p_buf, uint16_t buf_size)
{
// Check buffer for null pointer.
if (p_buf == NULL)
{
return NRF_ERROR_NULL;
}
// Check that the buffer size is a power of two.
if (!IS_POWER_OF_TWO(buf_size))
{
return NRF_ERROR_INVALID_LENGTH;
}
p_fifo->p_buf = p_buf;
p_fifo->buf_size_mask = buf_size - 1;
p_fifo->read_pos = 0;
p_fifo->write_pos = 0;
return NRF_SUCCESS;
}
uint32_t app_fifo_put(app_fifo_t * p_fifo, uint8_t byte)
{
if (FIFO_LENGTH <= p_fifo->buf_size_mask)
{
fifo_put(p_fifo, byte);
return NRF_SUCCESS;
}
return NRF_ERROR_NO_MEM;
}
uint32_t app_fifo_get(app_fifo_t * p_fifo, uint8_t * p_byte)
{
if (FIFO_LENGTH != 0)
{
fifo_get(p_fifo, p_byte);
return NRF_SUCCESS;
}
return NRF_ERROR_NOT_FOUND;
}
uint32_t app_fifo_peek(app_fifo_t * p_fifo, uint16_t index, uint8_t * p_byte)
{
if (FIFO_LENGTH > index)
{
fifo_peek(p_fifo, index, p_byte);
return NRF_SUCCESS;
}
return NRF_ERROR_NOT_FOUND;
}
uint32_t app_fifo_flush(app_fifo_t * p_fifo)
{
p_fifo->read_pos = p_fifo->write_pos;
return NRF_SUCCESS;
}
uint32_t app_fifo_read(app_fifo_t * p_fifo, uint8_t * p_byte_array, uint32_t * p_size)
{
VERIFY_PARAM_NOT_NULL(p_fifo);
VERIFY_PARAM_NOT_NULL(p_size);
const uint32_t byte_count = fifo_length(p_fifo);
const uint32_t requested_len = (*p_size);
uint32_t index = 0;
uint32_t read_size = MIN(requested_len, byte_count);
(*p_size) = byte_count;
// Check if the FIFO is empty.
if (byte_count == 0)
{
return NRF_ERROR_NOT_FOUND;
}
// Check if application has requested only the size.
if (p_byte_array == NULL)
{
return NRF_SUCCESS;
}
// Fetch bytes from the FIFO.
while (index < read_size)
{
fifo_get(p_fifo, &p_byte_array[index++]);
}
(*p_size) = read_size;
return NRF_SUCCESS;
}
uint32_t app_fifo_write(app_fifo_t * p_fifo, uint8_t const * p_byte_array, uint32_t * p_size)
{
VERIFY_PARAM_NOT_NULL(p_fifo);
VERIFY_PARAM_NOT_NULL(p_size);
const uint32_t available_count = p_fifo->buf_size_mask - fifo_length(p_fifo) + 1;
const uint32_t requested_len = (*p_size);
uint32_t index = 0;
uint32_t write_size = MIN(requested_len, available_count);
(*p_size) = available_count;
// Check if the FIFO is FULL.
if (available_count == 0)
{
return NRF_ERROR_NO_MEM;
}
// Check if application has requested only the size.
if (p_byte_array == NULL)
{
return NRF_SUCCESS;
}
//Fetch bytes from the FIFO.
while (index < write_size)
{
fifo_put(p_fifo, p_byte_array[index++]);
}
(*p_size) = write_size;
return NRF_SUCCESS;
}

View File

@ -0,0 +1,145 @@
/* 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.
*
*/
/**@file
*
* @defgroup app_fifo FIFO implementation
* @{
* @ingroup app_common
*
* @brief FIFO implementation.
*/
#ifndef APP_FIFO_H__
#define APP_FIFO_H__
#include <stdint.h>
#include <stdlib.h>
/**@brief A FIFO instance structure.
* @details Keeps track of which bytes to read and write next.
* Also, it keeps the information about which memory is allocated for the buffer
* and its size. This structure must be initialized by app_fifo_init() before use.
*/
typedef struct
{
uint8_t * p_buf; /**< Pointer to FIFO buffer memory. */
uint16_t buf_size_mask; /**< Read/write index mask. Also used for size checking. */
volatile uint32_t read_pos; /**< Next read position in the FIFO buffer. */
volatile uint32_t write_pos; /**< Next write position in the FIFO buffer. */
} app_fifo_t;
/**@brief Function for initializing the FIFO.
*
* @param[out] p_fifo FIFO object.
* @param[in] p_buf FIFO buffer for storing data. The buffer size must be a power of two.
* @param[in] buf_size Size of the FIFO buffer provided. This size must be a power of two.
*
* @retval NRF_SUCCESS If initialization was successful.
* @retval NRF_ERROR_NULL If a NULL pointer is provided as buffer.
* @retval NRF_ERROR_INVALID_LENGTH If size of buffer provided is not a power of two.
*/
uint32_t app_fifo_init(app_fifo_t * p_fifo, uint8_t * p_buf, uint16_t buf_size);
/**@brief Function for adding an element to the FIFO.
*
* @param[in] p_fifo Pointer to the FIFO.
* @param[in] byte Data byte to add to the FIFO.
*
* @retval NRF_SUCCESS If an element has been successfully added to the FIFO.
* @retval NRF_ERROR_NO_MEM If the FIFO is full.
*/
uint32_t app_fifo_put(app_fifo_t * p_fifo, uint8_t byte);
/**@brief Function for getting the next element from the FIFO.
*
* @param[in] p_fifo Pointer to the FIFO.
* @param[out] p_byte Byte fetched from the FIFO.
*
* @retval NRF_SUCCESS If an element was returned.
* @retval NRF_ERROR_NOT_FOUND If there are no more elements in the queue.
*/
uint32_t app_fifo_get(app_fifo_t * p_fifo, uint8_t * p_byte);
/**@brief Function for looking at an element in the FIFO, without consuming it.
*
* @param[in] p_fifo Pointer to the FIFO.
* @param[in] index Which element to look at. The lower the index, the earlier it was put.
* @param[out] p_byte Byte fetched from the FIFO.
*
* @retval NRF_SUCCESS If an element was returned.
* @retval NRF_ERROR_NOT_FOUND If there are no more elements in the queue, or the index was
* too large.
*/
uint32_t app_fifo_peek(app_fifo_t * p_fifo, uint16_t index, uint8_t * p_byte);
/**@brief Function for flushing the FIFO.
*
* @param[in] p_fifo Pointer to the FIFO.
*
* @retval NRF_SUCCESS If the FIFO was flushed successfully.
*/
uint32_t app_fifo_flush(app_fifo_t * p_fifo);
/**@brief Function for reading bytes from the FIFO.
*
* This function can also be used to get the number of bytes in the FIFO.
*
* @param[in] p_fifo Pointer to the FIFO. Must not be NULL.
* @param[out] p_byte_array Memory pointer where the read bytes are fetched from the FIFO.
* Can be NULL. If NULL, the number of bytes that can be read in the FIFO
* are returned in the p_size parameter.
* @param[inout] p_size Address to memory indicating the maximum number of bytes to be read.
* The provided memory is overwritten with the actual number of bytes
* read if the procedure was successful. This field must not be NULL.
* If p_byte_array is set to NULL by the application, this parameter
* returns the number of bytes in the FIFO.
*
* @retval NRF_SUCCESS If the procedure is successful. The actual number of bytes read might
* be less than the requested maximum, depending on how many elements exist
* in the FIFO. Even if less bytes are returned, the procedure is considered
* successful.
* @retval NRF_ERROR_NULL If a NULL parameter was passed for a parameter that must not
* be NULL.
* @retval NRF_ERROR_NOT_FOUND If the FIFO is empty.
*/
uint32_t app_fifo_read(app_fifo_t * p_fifo, uint8_t * p_byte_array, uint32_t * p_size);
/**@brief Function for writing bytes to the FIFO.
*
* This function can also be used to get the available size on the FIFO.
*
* @param[in] p_fifo Pointer to the FIFO. Must not be NULL.
* @param[in] p_byte_array Memory pointer containing the bytes to be written to the FIFO.
* Can be NULL. If NULL, this function returns the number of bytes
* that can be written to the FIFO.
* @param[inout] p_size Address to memory indicating the maximum number of bytes to be written.
* The provided memory is overwritten with the number of bytes that were actually
* written if the procedure is successful. This field must not be NULL.
* If p_byte_array is set to NULL by the application, this parameter
* returns the number of bytes available in the FIFO.
*
* @retval NRF_SUCCESS If the procedure is successful. The actual number of bytes written might
* be less than the requested maximum, depending on how much room there is in
* the FIFO. Even if less bytes are written, the procedure is considered
* successful. If the write was partial, the application should use
* subsequent calls to attempt writing the data again.
* @retval NRF_ERROR_NULL If a NULL parameter was passed for a parameter that must not
* be NULL.
* @retval NRF_ERROR_NO_MEM If the FIFO is full.
*
*/
uint32_t app_fifo_write(app_fifo_t * p_fifo, uint8_t const * p_byte_array, uint32_t * p_size);
#endif // APP_FIFO_H__
/** @} */

View File

@ -0,0 +1,60 @@
/* Copyright (c) 2015 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.
*
*/
#ifndef HARFAULT_H__
#define HARFAULT_H__
#include <stdint.h>
#include <stddef.h>
/**
* @defgroup hardfault_default HardFault exception
* @{
* @brief Default HardFault exception implementation.
* @ingroup app_common
*/
/**
* @brief Contents of the stack.
*
* This structure is used to re-create the stack layout after a HardFault exception was raised.
*/
typedef struct HardFault_stack
{
uint32_t r0; ///< R0 register.
uint32_t r1; ///< R1 register.
uint32_t r2; ///< R2 register.
uint32_t r3; ///< R3 register.
uint32_t r12; ///< R12 register.
uint32_t lr; ///< Link register.
uint32_t pc; ///< Program counter.
uint32_t psr; ///< Program status register.
}HardFault_stack_t;
/**
* @brief Function for processing HardFault exceptions.
*
* An application that needs to process HardFault exceptions should provide an implementation of this function.
* It will be called from the HardFault handler.
* If no implementation is provided, the library uses a default one, which just restarts the MCU.
*
* @note If the DEBUG_NRF macro is defined, the software breakpoint is set just before the call
* to this function.
*
* @param p_stack Pointer to the stack bottom.
* This pointer might be NULL if the HardFault was called when the main stack was
* the active stack and a stack overrun is detected.
* In such a situation, the stack pointer is reinitialized to the default position,
* and the stack content is lost.
*/
void HardFault_process(HardFault_stack_t *p_stack);
/** @} */
#endif /* HARFAULT_H__ */

View File

@ -0,0 +1,47 @@
/* Copyright (c) 2015 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 "hardfault.h"
#include "nrf.h"
#include "compiler_abstraction.h"
#include "nordic_common.h"
#ifdef SOFTDEVICE_PRESENT
#include "nrf_soc.h"
#endif
#if defined(DEBUG_NRF)
/**
* @brief Pointer to the last received stack pointer.
*
* This pointer is set in the debug version of the HardFault handler.
* It helps to debug HardFault reasons.
*/
volatile HardFault_stack_t *HardFault_p_stack;
#endif
/*lint -save -e14 */
__WEAK void HardFault_process(HardFault_stack_t *p_stack)
{
// Restart the system by default
NVIC_SystemReset();
}
/*lint -restore */
void HardFault_c_handler( uint32_t *p_stack_address )
{
#if defined(DEBUG_NRF)
HardFault_p_stack = (HardFault_stack_t*)p_stack_address;
/* Generate breakpoint if debugger is connected */
__BKPT(0);
#endif
HardFault_process((HardFault_stack_t*)p_stack_address);
}

View File

@ -0,0 +1,52 @@
/* Copyright (c) 2015 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 <stdint.h>
void HardFault_Handler(void) __attribute__(( naked ));
void HardFault_Handler(void)
{
__asm volatile(
" .syntax unified \n"
" ldr r0, =0xFFFFFFFD \n"
" cmp r0, lr \n"
" bne HardFault_Handler_ChooseMSP \n"
/* Reading PSP into R0 */
" mrs r0, PSP \n"
" b HardFault_Handler_Continue \n"
"HardFault_Handler_ChooseMSP: \n"
/* Reading MSP into R0 */
" mrs r0, MSP \n"
/* -----------------------------------------------------------------
* If we have selected MSP check if we may use stack safetly.
* If not - reset the stack to the initial value. */
" ldr r1, =__StackTop \n"
" ldr r2, =__StackLimit \n"
/* MSP is in the range of <__StackTop, __StackLimit) */
" cmp r0, r1 \n"
" bhi HardFault_MoveSP \n"
" cmp r0, r2 \n"
" bhi HardFault_Handler_Continue \n"
/* ----------------------------------------------------------------- */
"HardFault_MoveSP: \n"
" mov SP, r1 \n"
" movs r0, #0 \n"
"HardFault_Handler_Continue: \n"
" ldr r3, =HardFault_c_handler \n"
" bx r3 \n"
" .align \n"
);
}

View File

@ -0,0 +1,65 @@
/* Copyright (c) 2015 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 <stdint.h>
#pragma section = "CSTACK"
extern void HardFault_c_handler( uint32_t * );
__stackless void HardFault_Handler(void);
__stackless void HardFault_Handler(void)
{
__asm volatile(
" ldr r0, 100f \n"
" cmp r0, lr \n"
" bne 1f \n"
/* Reading PSP into R0 */
" mrs r0, PSP \n"
" b 3f \n"
"1: \n"
/* Reading MSP into R0 */
" mrs r0, MSP \n"
/* -----------------------------------------------------------------
* If we have selected MSP check if we may use stack safetly.
* If not - reset the stack to the initial value. */
" ldr r1, 101f \n"
" ldr r2, 102f \n"
/* MSP is in the range of <__StackTop, __StackLimit) */
" cmp r0, r1 \n"
" bhi 2f \n"
" cmp r0, r2 \n"
" bhi 3f \n"
/* ----------------------------------------------------------------- */
"2: \n"
" mov SP, r1 \n"
" movs r0, #0 \n"
"3: \n"
" ldr r3, 103f \n"
" bx r3 \n"
"100: \n"
" DC32 0xFFFFFFFD \n"
"101: \n"
" DC32 %c0 \n"
"102: \n"
" DC32 %c1 \n"
"103: \n"
" DC32 %c2 \n"
: /* Outputs */
: /* Inputs */
"i"(__section_end("CSTACK")),
"i"(__section_begin("CSTACK")),
"i"(&HardFault_c_handler)
);
}

View File

@ -0,0 +1,61 @@
/* Copyright (c) 2015 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 <stdint.h>
//lint -save -e27 -e10 -e19 -e40
extern char STACK$$Base;
/* This variable should be static but then it cannot be used in assembly code below.
* The problem here is that the address of the section can be archived by $$ operator
* that is not allowed in assembly code. */
char const * HardFault_Handler_stack_bottom = &STACK$$Base;
//lint -restore
__asm void HardFault_Handler(void)
{
PRESERVE8
EXTERN HardFault_c_handler
EXTERN __initial_sp
EXTERN HardFault_Handler_stack_bottom
ldr r0, =0xFFFFFFFD
cmp r0, lr
bne HardFault_Handler_ChooseMSP
/* Reading PSP into R0 */
mrs r0, PSP
b HardFault_Handler_Continue
HardFault_Handler_ChooseMSP
/* Reading MSP into R0 */
mrs r0, MSP
/* -----------------------------------------------------------------
* If we have selected MSP, check if we may use stack safely.
* If not - reset the stack to the initial value. */
ldr r1, =__initial_sp
ldr r2, =HardFault_Handler_stack_bottom
ldr r2, [r2]
/* MSP is in the range of <__StackTop, __StackLimit) */
cmp r0, r1
bhi HardFault_MoveSP
cmp r0, r2
bhi HardFault_Handler_Continue
/* ----------------------------------------------------------------- */
HardFault_MoveSP
mov SP, r1
movs r0, #0
HardFault_Handler_Continue
ldr r3, =HardFault_c_handler
bx r3
ALIGN
}

View File

@ -0,0 +1,49 @@
/* Copyright (c) 2015 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 <stdint.h>
void HardFault_Handler(void) __attribute__(( naked ));
void HardFault_Handler(void)
{
__asm volatile(
" ldr r3, =HardFault_c_handler \n"
" tst lr, #4 \n"
/* PSP is quite simple and does not require additional handler */
" itt ne \n"
" mrsne r0, psp \n"
/* Jump to the handler, do not store LR - returning from handler just exits exception */
" bxne r3 \n"
/* Processing MSP requires stack checking */
" mrs r0, msp \n"
" ldr r1, =__StackTop \n"
" ldr r2, =__StackLimit \n"
/* MSP is in the range of <__StackTop, __StackLimit) */
" cmp r0, r1 \n"
" bhi HardFault_MoveSP \n"
" cmp r0, r2 \n"
" bhi HardFault_Handler_Continue \n"
"HardFault_MoveSP: \n"
" mov sp, r1 \n"
" mov r0, #0 \n"
"HardFault_Handler_Continue: \n"
" bx r3 \n"
" .align \n"
);
}

View File

@ -0,0 +1,64 @@
/* Copyright (c) 2015 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 <stdint.h>
#pragma section = "CSTACK"
extern void HardFault_c_handler( uint32_t * );
__stackless void HardFault_Handler(void);
__stackless void HardFault_Handler(void)
{
__asm volatile(
" ldr.n r3, 103f \n"
" tst lr, #4 \n"
/* PSP is quite simple and does not require additional handler */
" itt ne \n"
" mrsne r0, psp \n"
/* Jump to the handler, do not store LR - returning from handler just exits exception */
" bxne r3 \n"
/* Processing MSP requires stack checking */
" mrs r0, msp \n"
" ldr.n r1, 101f \n"
" ldr.n r2, 102f \n"
/* MSP is in the range of <__StackTop, __StackLimit) */
" cmp r0, r1 \n"
" bhi.n 1f \n"
" cmp r0, r2 \n"
" bhi.n 2f \n"
"1: \n"
" mov sp, r1 \n"
" mov r0, #0 \n"
"2: \n"
" bx r3 \n"
/* Data alignment if required */
" nop \n"
"101: \n"
" DC32 %c0 \n"
"102: \n"
" DC32 %c1 \n"
"103: \n"
" DC32 %c2 \n"
: /* Outputs */
: /* Inputs */
"i"(__section_end("CSTACK")),
"i"(__section_begin("CSTACK")),
"i"(&HardFault_c_handler)
);
}

View File

@ -0,0 +1,60 @@
/* Copyright (c) 2015 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 <stdint.h>
//lint -save -e27 -e10 -e19 -e40
extern char STACK$$Base;
/* This variable should be static but then it cannot be used in assembly code below.
* The problem here is that the address of the section can be archived by $$ operator
* that is not allowed in assembly code. */
char const * HardFault_Handler_stack_bottom = &STACK$$Base;
//lint -restore
__asm void HardFault_Handler(void)
{
PRESERVE8
EXTERN HardFault_c_handler
EXTERN __initial_sp
EXTERN HardFault_Handler_stack_bottom
ldr r3, =HardFault_c_handler
tst lr, #4
/* PSP is quite simple and does not require additional handler */
itt ne
mrsne r0, psp
/* Jump to the handler, do not store LR - returning from handler just exits exception */
bxne r3
/* Processing MSP requires stack checking */
mrs r0, msp
ldr r1, =__initial_sp
ldr r2, =HardFault_Handler_stack_bottom
ldr r2, [r2]
/* MSP is in the range of <__StackTop, __StackLimit) */
cmp r0, r1
bhi HardFault_MoveSP
cmp r0, r2
bhi HardFault_Handler_Continue
HardFault_MoveSP
mov sp, r1
mov r0, #0
HardFault_Handler_Continue
bx r3
ALIGN
}

View File

@ -0,0 +1,253 @@
/* 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 "hci_mem_pool.h"
//#include "hci_mem_pool_internal.h"
#include "nordic_common.h"
#include <stdbool.h>
#include <stdio.h>
// Adafruit for dual transport serial + ble
#define BLE_TX_BUF_SIZE 4u /**< TX buffer size in bytes. */
#define BLE_RX_BUF_SIZE 32u /**< RX buffer size in bytes. */
#define BLE_RX_BUF_QUEUE_SIZE 8u /**< RX buffer element size. */
#define SERIAL_TX_BUF_SIZE 32u /**< TX buffer size in bytes. */
#define SERIAL_RX_BUF_SIZE 600u /**< RX buffer size in bytes. */
#define SERIAL_RX_BUF_QUEUE_SIZE 2u /**< RX buffer element size. */
extern bool is_ota(void);
#define RX_BUF_SIZE (is_ota() ? BLE_RX_BUF_SIZE : SERIAL_RX_BUF_SIZE)
#define TX_BUF_SIZE (is_ota() ? BLE_TX_BUF_SIZE : SERIAL_TX_BUF_SIZE)
#define RX_BUF_QUEUE_SIZE (is_ota() ? BLE_RX_BUF_QUEUE_SIZE : SERIAL_RX_BUF_QUEUE_SIZE)
/**@brief RX buffer element instance structure.
*/
typedef struct
{
// uint8_t rx_buffer[RX_BUF_SIZE]; /**< RX buffer memory array. */
uint8_t rx_buffer[MAX(BLE_RX_BUF_SIZE, SERIAL_RX_BUF_SIZE)];
uint32_t length; /**< Length of the RX buffer memory array. */
} rx_buffer_elem_t;
/**@brief RX buffer queue element instance structure.
*/
typedef struct
{
rx_buffer_elem_t * p_buffer; /**< Pointer to RX buffer element. */
uint32_t free_window_count; /**< Free space element count. */
uint32_t free_available_count; /**< Free area element count. */
uint32_t read_available_count; /**< Read area element count. */
uint32_t write_index; /**< Write position index. */
uint32_t read_index; /**< Read position index. */
uint32_t free_index; /**< Free position index. */
} rx_buffer_queue_t;
static bool m_is_tx_allocated; /**< Boolean value to determine if the TX buffer is allocated. */
static rx_buffer_elem_t m_rx_buffer_elem_queue[MAX(BLE_RX_BUF_QUEUE_SIZE, SERIAL_RX_BUF_QUEUE_SIZE)]; /**< RX buffer element instances. */
static rx_buffer_queue_t m_rx_buffer_queue; /**< RX buffer queue element instance. */
uint32_t hci_mem_pool_open(void)
{
m_is_tx_allocated = false;
m_rx_buffer_queue.p_buffer = m_rx_buffer_elem_queue;
m_rx_buffer_queue.free_window_count = RX_BUF_QUEUE_SIZE;
m_rx_buffer_queue.free_available_count = 0;
m_rx_buffer_queue.read_available_count = 0;
m_rx_buffer_queue.write_index = 0;
m_rx_buffer_queue.read_index = 0;
m_rx_buffer_queue.free_index = 0;
return NRF_SUCCESS;
}
uint32_t hci_mem_pool_close(void)
{
return NRF_SUCCESS;
}
uint32_t hci_mem_pool_tx_alloc(void ** pp_buffer)
{
static uint8_t tx_buffer[MAX(BLE_TX_BUF_SIZE, SERIAL_TX_BUF_SIZE)];
uint32_t err_code;
if (pp_buffer == NULL)
{
return NRF_ERROR_NULL;
}
if (!m_is_tx_allocated)
{
m_is_tx_allocated = true;
*pp_buffer = tx_buffer;
err_code = NRF_SUCCESS;
}
else
{
err_code = NRF_ERROR_NO_MEM;
}
return err_code;
}
uint32_t hci_mem_pool_tx_free(void)
{
m_is_tx_allocated = false;
return NRF_SUCCESS;
}
uint32_t hci_mem_pool_rx_produce(uint32_t length, void ** pp_buffer)
{
uint32_t err_code;
if (pp_buffer == NULL)
{
return NRF_ERROR_NULL;
}
*pp_buffer = NULL;
if (m_rx_buffer_queue.free_window_count != 0)
{
if (length <= RX_BUF_SIZE)
{
--(m_rx_buffer_queue.free_window_count);
++(m_rx_buffer_queue.read_available_count);
*pp_buffer =
m_rx_buffer_queue.p_buffer[m_rx_buffer_queue.write_index].rx_buffer;
m_rx_buffer_queue.free_index |= (1u << m_rx_buffer_queue.write_index);
// @note: Adjust the write_index making use of the fact that the buffer size is of
// power of two and two's complement arithmetic. For details refer example to book
// "Making embedded systems: Elicia White".
m_rx_buffer_queue.write_index =
(m_rx_buffer_queue.write_index + 1u) & (RX_BUF_QUEUE_SIZE - 1u);
err_code = NRF_SUCCESS;
}
else
{
err_code = NRF_ERROR_DATA_SIZE;
}
}
else
{
err_code = NRF_ERROR_NO_MEM;
}
return err_code;
}
uint32_t hci_mem_pool_rx_consume(uint8_t * p_buffer)
{
uint32_t err_code;
uint32_t consume_index;
uint32_t start_index;
if (m_rx_buffer_queue.free_available_count != 0)
{
// Find the buffer that has been freed -
// Start at read_index minus free_available_count and then increment until read index.
err_code = NRF_ERROR_INVALID_ADDR;
consume_index = (m_rx_buffer_queue.read_index - m_rx_buffer_queue.free_available_count) &
(RX_BUF_QUEUE_SIZE - 1u);
start_index = consume_index;
do
{
if (m_rx_buffer_queue.p_buffer[consume_index].rx_buffer == p_buffer)
{
m_rx_buffer_queue.free_index ^= (1u << consume_index);
err_code = NRF_SUCCESS;
break;
}
else
{
consume_index = (consume_index + 1u) & (RX_BUF_QUEUE_SIZE - 1u);
}
}
while (consume_index != m_rx_buffer_queue.read_index);
while (!(m_rx_buffer_queue.free_index & (1 << start_index)) &&
(m_rx_buffer_queue.free_available_count != 0))
{
--(m_rx_buffer_queue.free_available_count);
++(m_rx_buffer_queue.free_window_count);
start_index = (consume_index + 1u) & (RX_BUF_QUEUE_SIZE - 1u);
}
}
else
{
err_code = NRF_ERROR_NO_MEM;
}
return err_code;
}
uint32_t hci_mem_pool_rx_data_size_set(uint32_t length)
{
// @note: Adjust the write_index making use of the fact that the buffer size is of power
// of two and two's complement arithmetic. For details refer example to book
// "Making embedded systems: Elicia White".
const uint32_t index = (m_rx_buffer_queue.write_index - 1u) & (RX_BUF_QUEUE_SIZE - 1u);
m_rx_buffer_queue.p_buffer[index].length = length;
return NRF_SUCCESS;
}
uint32_t hci_mem_pool_rx_extract(uint8_t ** pp_buffer, uint32_t * p_length)
{
uint32_t err_code;
if ((pp_buffer == NULL) || (p_length == NULL))
{
return NRF_ERROR_NULL;
}
if (m_rx_buffer_queue.read_available_count != 0)
{
--(m_rx_buffer_queue.read_available_count);
++(m_rx_buffer_queue.free_available_count);
*pp_buffer =
m_rx_buffer_queue.p_buffer[m_rx_buffer_queue.read_index].rx_buffer;
*p_length =
m_rx_buffer_queue.p_buffer[m_rx_buffer_queue.read_index].length;
// @note: Adjust the write_index making use of the fact that the buffer size is of power
// of two and two's complement arithmetic. For details refer example to book
// "Making embedded systems: Elicia White".
m_rx_buffer_queue.read_index =
(m_rx_buffer_queue.read_index + 1u) & (RX_BUF_QUEUE_SIZE - 1u);
err_code = NRF_SUCCESS;
}
else
{
err_code = NRF_ERROR_NO_MEM;
}
return err_code;
}

View File

@ -0,0 +1,132 @@
/* 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.
*
*/
/** @file
*
* @defgroup memory_pool Memory pool
* @{
* @ingroup app_common
*
* @brief Memory pool implementation
*
* Memory pool implementation, based on circular buffer data structure, which supports asynchronous
* processing of RX data. The current default implementation supports 1 TX buffer and 4 RX buffers.
* The memory managed by the pool is allocated from static storage instead of heap. The internal
* design of the circular buffer implementing the RX memory layout is illustrated in the picture
* below.
*
* @image html memory_pool.png "Circular buffer design"
*
* The expected call order for the RX APIs is as follows:
* - hci_mem_pool_rx_produce
* - hci_mem_pool_rx_data_size_set
* - hci_mem_pool_rx_extract
* - hci_mem_pool_rx_consume
*
* @warning If the above mentioned expected call order is violated the end result can be undefined.
*
* \par Component specific configuration options
*
* The following compile time configuration options are available to suit various implementations:
* - TX_BUF_SIZE TX buffer size in bytes.
* - RX_BUF_SIZE RX buffer size in bytes.
* - RX_BUF_QUEUE_SIZE RX buffer element size.
*/
#ifndef HCI_MEM_POOL_H__
#define HCI_MEM_POOL_H__
#include <stdint.h>
#include "nrf_error.h"
/**@brief Function for opening the module.
*
* @retval NRF_SUCCESS Operation success.
*/
uint32_t hci_mem_pool_open(void);
/**@brief Function for closing the module.
*
* @retval NRF_SUCCESS Operation success.
*/
uint32_t hci_mem_pool_close(void);
/**@brief Function for allocating requested amount of TX memory.
*
* @param[out] pp_buffer Pointer to the allocated memory.
*
* @retval NRF_SUCCESS Operation success. Memory was allocated.
* @retval NRF_ERROR_NO_MEM Operation failure. No memory available for allocation.
* @retval NRF_ERROR_NULL Operation failure. NULL pointer supplied.
*/
uint32_t hci_mem_pool_tx_alloc(void ** pp_buffer);
/**@brief Function for freeing previously allocated TX memory.
*
* @note Memory management follows the FIFO principle meaning that free() order must match the
* alloc(...) order, which is the reason for omitting exact memory block identifier as an
* input parameter.
*
* @retval NRF_SUCCESS Operation success. Memory was freed.
*/
uint32_t hci_mem_pool_tx_free(void);
/**@brief Function for producing a free RX memory block for usage.
*
* @note Upon produce request amount being 0, NRF_SUCCESS is returned.
*
* @param[in] length Amount, in bytes, of free memory to be produced.
* @param[out] pp_buffer Pointer to the allocated memory.
*
* @retval NRF_SUCCESS Operation success. Free RX memory block produced.
* @retval NRF_ERROR_NO_MEM Operation failure. No suitable memory available for allocation.
* @retval NRF_ERROR_DATA_SIZE Operation failure. Request size exceeds limit.
* @retval NRF_ERROR_NULL Operation failure. NULL pointer supplied.
*/
uint32_t hci_mem_pool_rx_produce(uint32_t length, void ** pp_buffer);
/**@brief Function for setting the length of the last produced RX memory block.
*
* @warning If call to this API is omitted the end result is that the following call to
* mem_pool_rx_extract will return incorrect data in the p_length output parameter.
*
* @param[in] length Amount, in bytes, of actual memory used.
*
* @retval NRF_SUCCESS Operation success. Length was set.
*/
uint32_t hci_mem_pool_rx_data_size_set(uint32_t length);
/**@brief Function for extracting a packet, which has been filled with read data, for further
* processing.
*
* @param[out] pp_buffer Pointer to the packet data.
* @param[out] p_length Length of packet data in bytes.
*
* @retval NRF_SUCCESS Operation success.
* @retval NRF_ERROR_NO_MEM Operation failure. No packet available to extract.
* @retval NRF_ERROR_NULL Operation failure. NULL pointer supplied.
*/
uint32_t hci_mem_pool_rx_extract(uint8_t ** pp_buffer, uint32_t * p_length);
/**@brief Function for freeing previously extracted packet, which has been filled with read data.
*
* @param[in] p_buffer Pointer to consumed buffer.
*
* @retval NRF_SUCCESS Operation success.
* @retval NRF_ERROR_NO_MEM Operation failure. No packet available to free.
* @retval NRF_ERROR_INVALID_ADDR Operation failure. Not a valid pointer.
*/
uint32_t hci_mem_pool_rx_consume(uint8_t * p_buffer);
#endif // HCI_MEM_POOL_H__
/** @} */

View File

@ -0,0 +1,428 @@
/* 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 "hci_slip.h"
#include <stdlib.h>
#include "hci_transport_config.h"
#include "app_uart.h"
#include "nrf_error.h"
#define APP_SLIP_END 0xC0 /**< SLIP code for identifying the beginning and end of a packet frame.. */
#define APP_SLIP_ESC 0xDB /**< SLIP escape code. This code is used to specify that the following character is specially encoded. */
#define APP_SLIP_ESC_END 0xDC /**< SLIP special code. When this code follows 0xDB, this character is interpreted as payload data 0xC0.. */
#define APP_SLIP_ESC_ESC 0xDD /**< SLIP special code. When this code follows 0xDB, this character is interpreted as payload data 0xDB. */
/** @brief States for the SLIP state machine. */
typedef enum
{
SLIP_OFF, /**< SLIP state OFF. */
SLIP_READY, /**< SLIP state ON. */
SLIP_TRANSMITTING, /**< SLIP state is transmitting indicating write() has been called but data transmission has not completed. */
} slip_states_t;
static slip_states_t m_current_state = SLIP_OFF; /** Current state for the SLIP TX state machine. */
static hci_slip_event_handler_t m_slip_event_handler; /** Event callback function for handling of SLIP events, @ref hci_slip_evt_type_t . */
static const uint8_t * mp_tx_buffer; /** Pointer to the current TX buffer that is in transmission. */
static uint32_t m_tx_buffer_length; /** Length of the current TX buffer that is in transmission. */
static volatile uint32_t m_tx_buffer_index; /** Current index for next byte to transmit in the mp_tx_buffer. */
static uint8_t * mp_rx_buffer; /** Pointer to the current RX buffer where the next SLIP decoded packet will be stored. */
static uint32_t m_rx_buffer_length; /** Length of the current RX buffer. */
static uint32_t m_rx_received_count; /** Number of SLIP decoded bytes received and stored in mp_rx_buffer. */
/**@brief Function for parsing bytes received on the UART until a SLIP escape byte is received.
*
* @param[in] byte Byte received in UART module.
*/
static void handle_rx_byte_default(uint8_t byte);
/**@brief Function for parsing bytes received on the UART until SLIP end byte is received.
*
* @param[in] byte Byte received in UART module.
*/
static void handle_rx_byte_wait_start(uint8_t byte);
/**@brief Function for decoding a received SLIP escape byte.
* It will ensure correct decoding of the byte following the SLIP escape byte.
*
* @param[in] byte Byte received in UART module.
*/
static void handle_rx_byte_esc(uint8_t byte);
/**@brief Function pointer for parsing and decoding SLIP bytes from the UART module.
*
* @param[in] byte Byte received in UART module.
*/
static void (*handle_rx_byte) (uint8_t byte) = handle_rx_byte_wait_start;
/**@brief Function pointer for sending a byte through the UART module.
*/
static uint32_t send_tx_byte_default(void);
/**@brief Function for transferring a SLIP escape byte (0xDB) when special bytes are transferred,
* that is 0xC0 and 0xDB.
*/
static uint32_t send_tx_byte_esc(void);
/**@brief Function for transferring a byte when it collides with SLIP commands and follows the SLIP
* escape byte, that is 0xC0 => 0xDC and 0xDB => 0xDD.
*/
static uint32_t send_tx_byte_encoded(void);
/**@brief Function for transferring the SLIP end frame byte, 0xC0.
*/
static uint32_t send_tx_byte_end(void);
/**@brief Function pointer for sending a byte through the UART module.
*/
uint32_t (*send_tx_byte) (void) = send_tx_byte_default;
static uint32_t send_tx_byte_end(void)
{
uint32_t err_code = app_uart_put(APP_SLIP_END);
if ((err_code == NRF_SUCCESS) && (m_tx_buffer_index == 0))
{
// Packet transmission started.
send_tx_byte = send_tx_byte_default;
}
return err_code;
}
static uint32_t send_tx_byte_default(void)
{
uint32_t err_code = app_uart_put(mp_tx_buffer[m_tx_buffer_index]);
if (err_code == NRF_SUCCESS)
{
m_tx_buffer_index++;
}
return err_code;
}
static uint32_t send_tx_byte_encoded(void)
{
uint32_t err_code;
switch(mp_tx_buffer[m_tx_buffer_index])
{
case APP_SLIP_END:
err_code = app_uart_put(APP_SLIP_ESC_END);
break;
case APP_SLIP_ESC:
err_code = app_uart_put(APP_SLIP_ESC_ESC);
break;
default:
err_code = NRF_ERROR_NO_MEM;
break;
}
if (err_code == NRF_SUCCESS)
{
m_tx_buffer_index++;
send_tx_byte = send_tx_byte_default;
}
return err_code;
}
static uint32_t send_tx_byte_esc(void)
{
uint32_t err_code = app_uart_put(APP_SLIP_ESC);
if (err_code == NRF_SUCCESS)
{
send_tx_byte = send_tx_byte_encoded;
}
return err_code;
}
/** @brief Function for transferring the content of the mp_tx_buffer to the UART.
* It continues to transfer bytes until the UART buffer is full or the complete buffer is
* transferred.
*/
static void transmit_buffer(void)
{
uint32_t err_code = NRF_SUCCESS;
while (m_tx_buffer_index < m_tx_buffer_length)
{
if ((mp_tx_buffer[m_tx_buffer_index] == APP_SLIP_END ||
mp_tx_buffer[m_tx_buffer_index] == APP_SLIP_ESC) &&
send_tx_byte == send_tx_byte_default)
{
send_tx_byte = send_tx_byte_esc;
}
err_code = send_tx_byte();
if (err_code == NRF_ERROR_NO_MEM || err_code == NRF_ERROR_BUSY)
{
// No memory left in UART TX buffer. Abort and wait for APP_UART_TX_EMPTY to continue.
return;
}
}
send_tx_byte = send_tx_byte_end;
err_code = send_tx_byte();
if (err_code == NRF_SUCCESS)
{
// Packet transmission ended. Notify higher level.
m_current_state = SLIP_READY;
if (m_slip_event_handler != NULL)
{
hci_slip_evt_t event = {HCI_SLIP_TX_DONE, mp_tx_buffer, m_tx_buffer_index};
m_slip_event_handler(event);
}
}
}
/** @brief Function for handling the reception of a SLIP end byte.
* If the number of bytes received is greater than zero it will call m_slip_event_handler
* with number of bytes received and invalidate the mp_rx_buffer to protect against data
* corruption.
* No new bytes can be received until a new RX buffer is supplied.
*/
static void handle_slip_end(void)
{
if (m_rx_received_count > 0)
{
// Full packet received, push it up.
if (m_slip_event_handler != NULL)
{
hci_slip_evt_t event = {HCI_SLIP_RX_RDY, mp_rx_buffer, m_rx_received_count};
m_rx_received_count = 0;
mp_rx_buffer = NULL;
m_slip_event_handler(event);
}
}
}
static void handle_rx_byte_esc(uint8_t byte)
{
switch (byte)
{
case APP_SLIP_END:
handle_slip_end();
break;
case APP_SLIP_ESC_END:
mp_rx_buffer[m_rx_received_count++] = APP_SLIP_END;
break;
case APP_SLIP_ESC_ESC:
mp_rx_buffer[m_rx_received_count++] = APP_SLIP_ESC;
break;
default:
mp_rx_buffer[m_rx_received_count++] = byte;
break;
}
handle_rx_byte = handle_rx_byte_default;
}
static void handle_rx_byte_default(uint8_t byte)
{
switch (byte)
{
case APP_SLIP_END:
handle_slip_end();
break;
case APP_SLIP_ESC:
handle_rx_byte = handle_rx_byte_esc;
break;
default:
mp_rx_buffer[m_rx_received_count++] = byte;
break;
}
}
static void handle_rx_byte_wait_start(uint8_t byte)
{
if (byte == APP_SLIP_END)
{
handle_rx_byte = handle_rx_byte_default;
}
}
/** @brief Function for checking the current index and length of the RX buffer to determine if the
* buffer is full. If an event handler has been registered, the callback function will
* be executed..
*
* @retval true If RX buffer has overflowed.
* @retval false otherwise.
*
*/
static bool rx_buffer_overflowed(void)
{
if (mp_rx_buffer == NULL || m_rx_received_count >= m_rx_buffer_length)
{
if (m_slip_event_handler != NULL)
{
hci_slip_evt_t event = {HCI_SLIP_RX_OVERFLOW, mp_rx_buffer, m_rx_received_count};
m_slip_event_handler(event);
}
return true;
}
return false;
}
/** @brief Function for handling the UART module event. It parses events from the UART when
* bytes are received/transmitted.
*
* @param[in] uart_event Event received from app_uart module.
*/
static void slip_uart_eventhandler(app_uart_evt_t * uart_event)
{
if (uart_event->evt_type == APP_UART_TX_EMPTY && m_current_state == SLIP_TRANSMITTING)
{
transmit_buffer();
}
if ((uart_event->evt_type == APP_UART_DATA) && (!rx_buffer_overflowed()))
{
handle_rx_byte(uart_event->data.value);
}
}
/** @brief Function for enabling the UART module when the SLIP layer is opened.
*/
static uint32_t slip_uart_open(void)
{
uint32_t err_code;
app_uart_comm_params_t comm_params =
{
HCI_SLIP_UART_RX_PIN_NUMBER,
HCI_SLIP_UART_TX_PIN_NUMBER,
HCI_SLIP_UART_RTS_PIN_NUMBER,
HCI_SLIP_UART_CTS_PIN_NUMBER,
HCI_SLIP_UART_MODE,
false,
HCI_SLIP_UART_BAUDRATE
};
err_code = app_uart_init(&comm_params,
NULL,
slip_uart_eventhandler,
APP_IRQ_PRIORITY_LOW);
if (err_code == NRF_SUCCESS)
{
m_current_state = SLIP_READY;
}
return err_code;
}
uint32_t hci_slip_evt_handler_register(hci_slip_event_handler_t event_handler)
{
m_slip_event_handler = event_handler;
return NRF_SUCCESS;
}
uint32_t hci_slip_open()
{
switch (m_current_state)
{
case SLIP_OFF:
return slip_uart_open();
default:
// Do nothing.
break;
}
return NRF_SUCCESS;
}
uint32_t hci_slip_close()
{
m_current_state = SLIP_OFF;
uint32_t err_code = app_uart_close();
return err_code;
}
uint32_t hci_slip_write(const uint8_t * p_buffer, uint32_t length)
{
if (p_buffer == NULL)
{
return NRF_ERROR_INVALID_ADDR;
}
switch (m_current_state)
{
case SLIP_READY:
m_tx_buffer_index = 0;
m_tx_buffer_length = length;
mp_tx_buffer = p_buffer;
m_current_state = SLIP_TRANSMITTING;
send_tx_byte = send_tx_byte_end;
transmit_buffer();
return NRF_SUCCESS;
case SLIP_TRANSMITTING:
return NRF_ERROR_NO_MEM;
case SLIP_OFF:
default:
return NRF_ERROR_INVALID_STATE;
}
}
uint32_t hci_slip_rx_buffer_register(uint8_t * p_buffer, uint32_t length)
{
mp_rx_buffer = p_buffer;
m_rx_buffer_length = length;
m_rx_received_count = 0;
handle_rx_byte = handle_rx_byte_wait_start;
return NRF_SUCCESS;
}

View File

@ -0,0 +1,129 @@
/* 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.
*
*/
/** @file
*
* @defgroup hci_slip SLIP module
* @{
* @ingroup app_common
*
* @brief SLIP layer for supporting packet framing in HCI transport.
*
* @details This module implements SLIP packet framing as described in the Bluetooth Core
* Specification 4.0, Volume 4, Part D, Chapter 3 SLIP Layer.
*
* SLIP framing ensures that all packets sent on the UART are framed as:
* <0xC0> SLIP packet 1 <0xC0> <0xC0> SLIP packet 2 <0xC0>.
*
* The SLIP layer uses events to notify the upper layer when data transmission is complete
* and when a SLIP packet is received.
*/
#ifndef HCI_SLIP_H__
#define HCI_SLIP_H__
#include <stdint.h>
/**@brief Event types from the SLIP Layer. */
typedef enum
{
HCI_SLIP_RX_RDY, /**< An event indicating that an RX packet is ready to be read. */
HCI_SLIP_TX_DONE, /**< An event indicating write completion of the TX packet provided in the function call \ref hci_slip_write . */
HCI_SLIP_RX_OVERFLOW, /**< An event indicating that RX data has been discarded due to lack of free RX memory. */
HCI_SLIP_ERROR, /**< An event indicating that an unrecoverable error has occurred. */
HCI_SLIP_EVT_TYPE_MAX /**< Enumeration upper bound. */
} hci_slip_evt_type_t;
/**@brief Structure containing an event from the SLIP layer.
*/
typedef struct
{
hci_slip_evt_type_t evt_type; /**< Type of event. */
const uint8_t * packet; /**< This field contains a pointer to the packet for which the event relates, i.e. SLIP_TX_DONE: the packet transmitted, SLIP_RX_RDY: the packet received, SLIP_RX_OVERFLOW: The packet which overflow/or NULL if no receive buffer is available. */
uint32_t packet_length; /**< Packet length, i.e. SLIP_TX_DONE: Bytes transmitted, SLIP_RX_RDY: Bytes received, SLIP_RX_OVERFLOW: index at which the packet overflowed. */
} hci_slip_evt_t;
/**@brief Function for the SLIP layer event callback.
*/
typedef void (*hci_slip_event_handler_t)(hci_slip_evt_t event);
/**@brief Function for registering the event handler provided as parameter and this event handler
* will be used by SLIP layer to send events described in \ref hci_slip_evt_type_t.
*
* @note Multiple registration requests will overwrite any existing registration.
*
* @param[in] event_handler This function is called by the SLIP layer upon an event.
*
* @retval NRF_SUCCESS Operation success.
*/
uint32_t hci_slip_evt_handler_register(hci_slip_event_handler_t event_handler);
/**@brief Function for opening the SLIP layer. This function must be called before
* \ref hci_slip_write and before any data can be received.
*
* @note Can be called multiple times.
*
* @retval NRF_SUCCESS Operation success.
*
* The SLIP layer module will propagate errors from underlying sub-modules.
* This implementation is using UART module as a physical transmission layer, and hci_slip_open
* executes \ref app_uart_init . For an extended error list, please refer to \ref app_uart_init .
*/
uint32_t hci_slip_open(void);
/**@brief Function for closing the SLIP layer. After this function is called no data can be
* transmitted or received in this layer.
*
* @note This function can be called multiple times and also for an unopened channel.
*
* @retval NRF_SUCCESS Operation success.
*/
uint32_t hci_slip_close(void);
/**@brief Function for writing a packet with SLIP encoding. Packet transmission is confirmed when
* the HCI_SLIP_TX_DONE event is received by the function caller.
*
* @param[in] p_buffer Pointer to the packet to transmit.
* @param[in] length Packet length, in bytes.
*
* @retval NRF_SUCCESS Operation success. Packet was encoded and added to the
* transmission queue and an event will be sent upon transmission
* completion.
* @retval NRF_ERROR_NO_MEM Operation failure. Transmission queue is full and packet was not
* added to the transmission queue. Application shall wait for
* the \ref HCI_SLIP_TX_DONE event. After HCI_SLIP_TX_DONE this
* function can be executed for transmission of next packet.
* @retval NRF_ERROR_INVALID_ADDR If a NULL pointer is provided.
* @retval NRF_ERROR_INVALID_STATE Operation failure. Module is not open.
*/
uint32_t hci_slip_write(const uint8_t * p_buffer, uint32_t length);
/**@brief Function for registering a receive buffer. The receive buffer will be used for storage of
* received and SLIP decoded data.
* No data can be received by the SLIP layer until a receive buffer has been registered.
*
* @note The lifetime of the buffer must be valid during complete reception of data. A static
* buffer is recommended.
*
* @warning Multiple registration requests will overwrite any existing registration.
*
* @param[in] p_buffer Pointer to receive buffer. The received and SLIP decoded packet
* will be placed in this buffer.
* @param[in] length Buffer length, in bytes.
*
* @retval NRF_SUCCESS Operation success.
*/
uint32_t hci_slip_rx_buffer_register(uint8_t * p_buffer, uint32_t length);
#endif // HCI_SLIP_H__
/** @} */

View File

@ -0,0 +1,779 @@
/* 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 "hci_transport_config.h"
#include "hci_transport.h"
#include "hci_slip.h"
#include "crc16.h"
#include "hci_mem_pool.h"
#include "hci_mem_pool_internal.h"
#include "app_timer.h"
#include "app_error.h"
#include <stdio.h>
#include "sdk_common.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. */
APP_TIMER_DEF(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();
VERIFY_SUCCESS(err_code);
err_code = hci_slip_open();
VERIFY_SUCCESS(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));
}

View File

@ -0,0 +1,220 @@
/* 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.
*
*/
/**@file
*
* @defgroup hci_transport HCI Transport
* @{
* @ingroup app_common
*
* @brief HCI transport module implementation.
*
* This module implements certain specific features from the three-wire UART transport layer,
* defined by the Bluetooth specification version 4.0 [Vol 4] part D.
*
* \par Features supported
* - Transmission and reception of Vendor Specific HCI packet type application packets.
* - Transmission and reception of reliable packets: defined by chapter 6 of the specification.
*
* \par Features not supported
* - Link establishment procedure: defined by chapter 8 of the specification.
* - Low power: defined by chapter 9 of the specification.
*
* \par Implementation specific behaviour
* - As Link establishment procedure is not supported following static link configuration parameters
* are used:
* + TX window size is 1.
* + 16 bit CCITT-CRC must be used.
* + Out of frame software flow control not supported.
* + Parameters specific for resending reliable packets are compile time configurable (clarifed
* later in this document).
* + Acknowledgement packet transmissions are not timeout driven , meaning they are delivered for
* transmission within same context which the corresponding application packet was received.
*
* \par Implementation specific limitations
* Current implementation has the following limitations which will have impact to system wide
* behaviour:
* - Delayed acknowledgement scheduling not implemented:
* There exists a possibility that acknowledgement TX packet and application TX packet will collide
* in the TX pipeline having the end result that acknowledgement packet will be excluded from the TX
* pipeline which will trigger the retransmission algorithm within the peer protocol entity.
* - Delayed retransmission scheduling not implemented:
* There exists a possibility that retransmitted application TX packet and acknowledgement TX packet
* will collide in the TX pipeline having the end result that retransmitted application TX packet
* will be excluded from the TX pipeline.
* - Processing of the acknowledgement number from RX application packets:
* Acknowledgement number is not processed from the RX application packets having the end result
* that unnecessary application packet retransmissions can occur.
*
* The application TX packet processing flow is illustrated by the statemachine below.
*
* @image html hci_transport_tx_sm.png "TX - application packet statemachine"
*
* \par Component specific configuration options
*
* The following compile time configuration options are available, and used to configure the
* application TX packet retransmission interval, in order to suite various application specific
* implementations:
* - MAC_PACKET_SIZE_IN_BITS Maximum size of a single application packet in bits.
* - USED_BAUD_RATE Used uart baudrate.
*
* The following compile time configuration option is available to configure module specific
* behaviour:
* - MAX_RETRY_COUNT Max retransmission retry count for applicaton packets.
*/
#ifndef HCI_TRANSPORT_H__
#define HCI_TRANSPORT_H__
#include <stdint.h>
#include "nrf_error.h"
/**@brief Generic event callback function events. */
typedef enum
{
HCI_TRANSPORT_RX_RDY, /**< An event indicating that RX packet is ready for read. */
HCI_TRANSPORT_EVT_TYPE_MAX /**< Enumeration upper bound. */
} hci_transport_evt_type_t;
/**@brief Struct containing events from the Transport layer.
*/
typedef struct
{
hci_transport_evt_type_t evt_type; /**< Type of event. */
} hci_transport_evt_t;
/**@brief Transport layer generic event callback function type.
*
* @param[in] event Transport layer event.
*/
typedef void (*hci_transport_event_handler_t)(hci_transport_evt_t event);
/**@brief TX done event callback function result codes. */
typedef enum
{
HCI_TRANSPORT_TX_DONE_SUCCESS, /**< Transmission success, peer transport entity has acknowledged the transmission. */
HCI_TRANSPORT_TX_DONE_FAILURE /**< Transmission failure. */
} hci_transport_tx_done_result_t;
/**@brief Transport layer TX done event callback function type.
*
* @param[in] result TX done event result code.
*/
typedef void (*hci_transport_tx_done_handler_t)(hci_transport_tx_done_result_t result);
/**@brief Function for registering a generic event handler.
*
* @note Multiple registration requests will overwrite any possible existing registration.
*
* @param[in] event_handler The function to be called by the transport layer upon an event.
*
* @retval NRF_SUCCESS Operation success.
* @retval NRF_ERROR_NULL Operation failure. NULL pointer supplied.
*/
uint32_t hci_transport_evt_handler_reg(hci_transport_event_handler_t event_handler);
/**@brief Function for registering a handler for TX done event.
*
* @note Multiple registration requests will overwrite any possible existing registration.
*
* @param[in] event_handler The function to be called by the transport layer upon TX done
* event.
*
* @retval NRF_SUCCESS Operation success.
* @retval NRF_ERROR_NULL Operation failure. NULL pointer supplied.
*/
uint32_t hci_transport_tx_done_register(hci_transport_tx_done_handler_t event_handler);
/**@brief Function for opening the transport channel and initializing the transport layer.
*
* @warning Must not be called for a channel which has been allready opened.
*
* @retval NRF_SUCCESS Operation success.
* @retval NRF_ERROR_INTERNAL Operation failure. Internal error ocurred.
*/
uint32_t hci_transport_open(void);
/**@brief Function for closing the transport channel.
*
* @note Can be called multiple times and also for not opened channel.
*
* @retval NRF_SUCCESS Operation success.
*/
uint32_t hci_transport_close(void);
/**@brief Function for allocating tx packet memory.
*
* @param[out] pp_memory Pointer to the packet data.
*
* @retval NRF_SUCCESS Operation success. Memory was allocated.
* @retval NRF_ERROR_NO_MEM Operation failure. No memory available.
* @retval NRF_ERROR_NULL Operation failure. NULL pointer supplied.
*/
uint32_t hci_transport_tx_alloc(uint8_t ** pp_memory);
/**@brief Function for freeing tx packet memory.
*
* @note Memory management works in FIFO principle meaning that free order must match the alloc
* order.
*
* @retval NRF_SUCCESS Operation success. Memory was freed.
*/
uint32_t hci_transport_tx_free(void);
/**@brief Function for writing a packet.
*
* @note Completion of this method does not guarantee that actual peripheral transmission would
* have completed.
*
* @note In case of 0 byte packet length write request, message will consist of only transport
* module specific headers.
*
* @retval NRF_SUCCESS Operation success. Packet was added to the transmission queue
* and an event will be send upon transmission completion.
* @retval NRF_ERROR_NO_MEM Operation failure. Transmission queue is full and packet was not
* added to the transmission queue. User should wait for
* a appropriate event prior issuing this operation again.
* @retval NRF_ERROR_DATA_SIZE Operation failure. Packet size exceeds limit.
* @retval NRF_ERROR_NULL Operation failure. NULL pointer supplied.
* @retval NRF_ERROR_INVALID_STATE Operation failure. Channel is not open.
*/
uint32_t hci_transport_pkt_write(const uint8_t * p_buffer, uint16_t length);
/**@brief Function for extracting received packet.
*
* @note Extracted memory can't be reused by the underlying transport layer untill freed by call to
* hci_transport_rx_pkt_consume().
*
* @param[out] pp_buffer Pointer to the packet data.
* @param[out] p_length Length of packet data in bytes.
*
* @retval NRF_SUCCESS Operation success. Packet was extracted.
* @retval NRF_ERROR_NO_MEM Operation failure. No packet available to extract.
* @retval NRF_ERROR_NULL Operation failure. NULL pointer supplied.
*/
uint32_t hci_transport_rx_pkt_extract(uint8_t ** pp_buffer, uint16_t * p_length);
/**@brief Function for consuming extracted packet described by p_buffer.
*
* RX memory pointed to by p_buffer is freed and can be reused by the underlying transport layer.
*
* @param[in] p_buffer Pointer to the buffer that has been consumed.
*
* @retval NRF_SUCCESS Operation success.
* @retval NRF_ERROR_NO_MEM Operation failure. No packet available to consume.
* @retval NRF_ERROR_INVALID_ADDR Operation failure. Not a valid pointer.
*/
uint32_t hci_transport_rx_pkt_consume(uint8_t * p_buffer);
#endif // HCI_TRANSPORT_H__
/** @} */

View File

@ -0,0 +1,227 @@
/* Copyright (c) 2012 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 "app_scheduler.h"
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "nrf_soc.h"
#include "nrf_assert.h"
#include "app_util.h"
#include "app_util_platform.h"
/**@brief Structure for holding a scheduled event header. */
typedef struct
{
app_sched_event_handler_t handler; /**< Pointer to event handler to receive the event. */
uint16_t event_data_size; /**< Size of event data. */
} event_header_t;
STATIC_ASSERT(sizeof(event_header_t) <= APP_SCHED_EVENT_HEADER_SIZE);
static event_header_t * m_queue_event_headers; /**< Array for holding the queue event headers. */
static uint8_t * m_queue_event_data; /**< Array for holding the queue event data. */
static volatile uint8_t m_queue_start_index; /**< Index of queue entry at the start of the queue. */
static volatile uint8_t m_queue_end_index; /**< Index of queue entry at the end of the queue. */
static uint16_t m_queue_event_size; /**< Maximum event size in queue. */
static uint16_t m_queue_size; /**< Number of queue entries. */
#ifdef APP_SCHEDULER_WITH_PROFILER
static uint16_t m_max_queue_utilization; /**< Maximum observed queue utilization. */
#endif
/**@brief Function for incrementing a queue index, and handle wrap-around.
*
* @param[in] index Old index.
*
* @return New (incremented) index.
*/
static __INLINE uint8_t next_index(uint8_t index)
{
return (index < m_queue_size) ? (index + 1) : 0;
}
static __INLINE uint8_t app_sched_queue_full()
{
uint8_t tmp = m_queue_start_index;
return next_index(m_queue_end_index) == tmp;
}
/**@brief Macro for checking if a queue is full. */
#define APP_SCHED_QUEUE_FULL() app_sched_queue_full()
static __INLINE uint8_t app_sched_queue_empty()
{
uint8_t tmp = m_queue_start_index;
return m_queue_end_index == tmp;
}
/**@brief Macro for checking if a queue is empty. */
#define APP_SCHED_QUEUE_EMPTY() app_sched_queue_empty()
uint32_t app_sched_init(uint16_t event_size, uint16_t queue_size, void * p_event_buffer)
{
uint16_t data_start_index = (queue_size + 1) * sizeof(event_header_t);
// Check that buffer is correctly aligned
if (!is_word_aligned(p_event_buffer))
{
return NRF_ERROR_INVALID_PARAM;
}
// Initialize event scheduler
m_queue_event_headers = p_event_buffer;
m_queue_event_data = &((uint8_t *)p_event_buffer)[data_start_index];
m_queue_end_index = 0;
m_queue_start_index = 0;
m_queue_event_size = event_size;
m_queue_size = queue_size;
#ifdef APP_SCHEDULER_WITH_PROFILER
m_max_queue_utilization = 0;
#endif
return NRF_SUCCESS;
}
#ifdef APP_SCHEDULER_WITH_PROFILER
static void queue_utilization_check(void)
{
uint16_t start = m_queue_start_index;
uint16_t end = m_queue_end_index;
uint16_t queue_utilization = (end >= start) ? (end - start) :
(m_queue_size + 1 - start + end);
if (queue_utilization > m_max_queue_utilization)
{
m_max_queue_utilization = queue_utilization;
}
}
uint16_t app_sched_queue_utilization_get(void)
{
return m_max_queue_utilization;
}
#endif
uint32_t app_sched_event_put(void * p_event_data,
uint16_t event_data_size,
app_sched_event_handler_t handler)
{
uint32_t err_code;
if (event_data_size <= m_queue_event_size)
{
uint16_t event_index = 0xFFFF;
CRITICAL_REGION_ENTER();
if (!APP_SCHED_QUEUE_FULL())
{
event_index = m_queue_end_index;
m_queue_end_index = next_index(m_queue_end_index);
#ifdef APP_SCHEDULER_WITH_PROFILER
// This function call must be protected with critical region because
// it modifies 'm_max_queue_utilization'.
queue_utilization_check();
#endif
}
CRITICAL_REGION_EXIT();
if (event_index != 0xFFFF)
{
// NOTE: This can be done outside the critical region since the event consumer will
// always be called from the main loop, and will thus never interrupt this code.
m_queue_event_headers[event_index].handler = handler;
if ((p_event_data != NULL) && (event_data_size > 0))
{
memcpy(&m_queue_event_data[event_index * m_queue_event_size],
p_event_data,
event_data_size);
m_queue_event_headers[event_index].event_data_size = event_data_size;
}
else
{
m_queue_event_headers[event_index].event_data_size = 0;
}
err_code = NRF_SUCCESS;
}
else
{
err_code = NRF_ERROR_NO_MEM;
}
}
else
{
err_code = NRF_ERROR_INVALID_LENGTH;
}
return err_code;
}
/**@brief Function for reading the next event from specified event queue.
*
* @param[out] pp_event_data Pointer to pointer to event data.
* @param[out] p_event_data_size Pointer to size of event data.
* @param[out] p_event_handler Pointer to event handler function pointer.
*
* @return NRF_SUCCESS if new event, NRF_ERROR_NOT_FOUND if event queue is empty.
*/
static uint32_t app_sched_event_get(void ** pp_event_data,
uint16_t * p_event_data_size,
app_sched_event_handler_t * p_event_handler)
{
uint32_t err_code = NRF_ERROR_NOT_FOUND;
if (!APP_SCHED_QUEUE_EMPTY())
{
uint16_t event_index;
// NOTE: There is no need for a critical region here, as this function will only be called
// from app_sched_execute() from inside the main loop, so it will never interrupt
// app_sched_event_put(). Also, updating of (i.e. writing to) the start index will be
// an atomic operation.
event_index = m_queue_start_index;
m_queue_start_index = next_index(m_queue_start_index);
*pp_event_data = &m_queue_event_data[event_index * m_queue_event_size];
*p_event_data_size = m_queue_event_headers[event_index].event_data_size;
*p_event_handler = m_queue_event_headers[event_index].handler;
err_code = NRF_SUCCESS;
}
return err_code;
}
void app_sched_execute(void)
{
void * p_event_data;
uint16_t event_data_size;
app_sched_event_handler_t event_handler;
// Get next event (if any), and execute handler
while ((app_sched_event_get(&p_event_data, &event_data_size, &event_handler) == NRF_SUCCESS))
{
event_handler(p_event_data, event_data_size);
}
}

View File

@ -0,0 +1,163 @@
/* Copyright (c) 2012 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.
*
*/
/** @file
*
* @defgroup app_scheduler Scheduler
* @{
* @ingroup app_common
*
* @brief The scheduler is used for transferring execution from the interrupt context to the main
* context.
*
* @details See @ref seq_diagrams_sched for sequence diagrams illustrating the flow of events
* when using the Scheduler.
*
* @section app_scheduler_req Requirements:
*
* @subsection main_context_logic Logic in main context:
*
* - Define an event handler for each type of event expected.
* - Initialize the scheduler by calling the APP_SCHED_INIT() macro before entering the
* application main loop.
* - Call app_sched_execute() from the main loop each time the application wakes up because of an
* event (typically when sd_app_evt_wait() returns).
*
* @subsection int_context_logic Logic in interrupt context:
*
* - In the interrupt handler, call app_sched_event_put()
* with the appropriate data and event handler. This will insert an event into the
* scheduler's queue. The app_sched_execute() function will pull this event and call its
* handler in the main context.
*
* @if (PERIPHERAL)
* For an example usage of the scheduler, see the implementations of
* @ref ble_sdk_app_hids_mouse and @ref ble_sdk_app_hids_keyboard.
* @endif
*
* @image html scheduler_working.jpg The high level design of the scheduler
*/
#ifndef APP_SCHEDULER_H__
#define APP_SCHEDULER_H__
#include <stdint.h>
#include "app_error.h"
#include "app_util.h"
#define APP_SCHED_EVENT_HEADER_SIZE 8 /**< Size of app_scheduler.event_header_t (only for use inside APP_SCHED_BUF_SIZE()). */
/**@brief Compute number of bytes required to hold the scheduler buffer.
*
* @param[in] EVENT_SIZE Maximum size of events to be passed through the scheduler.
* @param[in] QUEUE_SIZE Number of entries in scheduler queue (i.e. the maximum number of events
* that can be scheduled for execution).
*
* @return Required scheduler buffer size (in bytes).
*/
#define APP_SCHED_BUF_SIZE(EVENT_SIZE, QUEUE_SIZE) \
(((EVENT_SIZE) + APP_SCHED_EVENT_HEADER_SIZE) * ((QUEUE_SIZE) + 1))
/**@brief Scheduler event handler type. */
typedef void (*app_sched_event_handler_t)(void * p_event_data, uint16_t event_size);
/**@brief Macro for initializing the event scheduler.
*
* @details It will also handle dimensioning and allocation of the memory buffer required by the
* scheduler, making sure the buffer is correctly aligned.
*
* @param[in] EVENT_SIZE Maximum size of events to be passed through the scheduler.
* @param[in] QUEUE_SIZE Number of entries in scheduler queue (i.e. the maximum number of events
* that can be scheduled for execution).
*
* @note Since this macro allocates a buffer, it must only be called once (it is OK to call it
* several times as long as it is from the same location, e.g. to do a reinitialization).
*/
#define APP_SCHED_INIT(EVENT_SIZE, QUEUE_SIZE) \
do \
{ \
static uint32_t APP_SCHED_BUF[CEIL_DIV(APP_SCHED_BUF_SIZE((EVENT_SIZE), (QUEUE_SIZE)), \
sizeof(uint32_t))]; \
uint32_t ERR_CODE = app_sched_init((EVENT_SIZE), (QUEUE_SIZE), APP_SCHED_BUF); \
APP_ERROR_CHECK(ERR_CODE); \
} while (0)
/**@brief Function for initializing the Scheduler.
*
* @details It must be called before entering the main loop.
*
* @param[in] max_event_size Maximum size of events to be passed through the scheduler.
* @param[in] queue_size Number of entries in scheduler queue (i.e. the maximum number of
* events that can be scheduled for execution).
* @param[in] p_evt_buffer Pointer to memory buffer for holding the scheduler queue. It must
* be dimensioned using the APP_SCHED_BUFFER_SIZE() macro. The buffer
* must be aligned to a 4 byte boundary.
*
* @note Normally initialization should be done using the APP_SCHED_INIT() macro, as that will both
* allocate the scheduler buffer, and also align the buffer correctly.
*
* @retval NRF_SUCCESS Successful initialization.
* @retval NRF_ERROR_INVALID_PARAM Invalid parameter (buffer not aligned to a 4 byte
* boundary).
*/
uint32_t app_sched_init(uint16_t max_event_size, uint16_t queue_size, void * p_evt_buffer);
/**@brief Function for executing all scheduled events.
*
* @details This function must be called from within the main loop. It will execute all events
* scheduled since the last time it was called.
*/
void app_sched_execute(void);
/**@brief Function for scheduling an event.
*
* @details Puts an event into the event queue.
*
* @param[in] p_event_data Pointer to event data to be scheduled.
* @param[in] event_size Size of event data to be scheduled.
* @param[in] handler Event handler to receive the event.
*
* @return NRF_SUCCESS on success, otherwise an error code.
*/
uint32_t app_sched_event_put(void * p_event_data,
uint16_t event_size,
app_sched_event_handler_t handler);
#ifdef APP_SCHEDULER_WITH_PROFILER
/**@brief Function for getting the maximum observed queue utilization.
*
* Function for tuning the module and determining QUEUE_SIZE value and thus module RAM usage.
*
* @return Maximum number of events in queue observed so far.
*/
uint16_t app_sched_queue_utilization_get(void);
#endif
#ifdef APP_SCHEDULER_WITH_PAUSE
/**@brief A function to pause the scheduler.
*
* @details When the scheduler is paused events are not pulled from the scheduler queue for
* processing. The function can be called multiple times. To unblock the scheduler the
* function @ref app_sched_resume has to be called the same number of times.
*/
void app_sched_pause(void);
/**@brief A function to resume a scheduler.
*
* @details To unblock the scheduler this function has to be called the same number of times as
* @ref app_sched_pause function.
*/
void app_sched_resume(void);
#endif
#endif // APP_SCHEDULER_H__
/** @} */

View File

@ -0,0 +1,262 @@
/* Copyright (c) 2012 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 "app_scheduler.h"
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "nrf_soc.h"
#include "nrf_assert.h"
#include "app_util.h"
#include "app_util_platform.h"
/**@brief Structure for holding a scheduled event header. */
typedef struct
{
app_sched_event_handler_t handler; /**< Pointer to event handler to receive the event. */
uint16_t event_data_size; /**< Size of event data. */
} event_header_t;
STATIC_ASSERT(sizeof (event_header_t) <= APP_SCHED_EVENT_HEADER_SIZE);
static event_header_t * m_queue_event_headers; /**< Array for holding the queue event headers. */
static uint8_t * m_queue_event_data; /**< Array for holding the queue event data. */
static volatile uint8_t m_queue_start_index; /**< Index of queue entry at the start of the queue. */
static volatile uint8_t m_queue_end_index; /**< Index of queue entry at the end of the queue. */
static uint16_t m_queue_event_size; /**< Maximum event size in queue. */
static uint16_t m_queue_size; /**< Number of queue entries. */
#ifdef APP_SCHEDULER_WITH_PROFILER
static uint16_t m_max_queue_utilization; /**< Maximum observed queue utilization. */
#endif
static uint32_t m_scheduler_paused_counter = 0; /**< Counter storing the difference between pausing
and resuming the scheduler. */
/**@brief Function for incrementing a queue index, and handle wrap-around.
*
* @param[in] index Old index.
*
* @return New (incremented) index.
*/
static __INLINE uint8_t next_index(uint8_t index)
{
return (index < m_queue_size) ? (index + 1) : 0;
}
static __INLINE uint8_t app_sched_queue_full(void)
{
uint8_t tmp = m_queue_start_index;
return next_index(m_queue_end_index) == tmp;
}
/**@brief Macro for checking if a queue is full. */
#define APP_SCHED_QUEUE_FULL() app_sched_queue_full()
static __INLINE uint8_t app_sched_queue_empty(void)
{
uint8_t tmp = m_queue_start_index;
return m_queue_end_index == tmp;
}
/**@brief Macro for checking if a queue is empty. */
#define APP_SCHED_QUEUE_EMPTY() app_sched_queue_empty()
uint32_t app_sched_init(uint16_t event_size, uint16_t queue_size, void * p_event_buffer)
{
uint16_t data_start_index = (queue_size + 1) * sizeof (event_header_t);
//Check that buffer is correctly aligned
if (!is_word_aligned(p_event_buffer))
{
return NRF_ERROR_INVALID_PARAM;
}
//Initialize event scheduler
m_queue_event_headers = p_event_buffer;
m_queue_event_data = &((uint8_t *)p_event_buffer)[data_start_index];
m_queue_end_index = 0;
m_queue_start_index = 0;
m_queue_event_size = event_size;
m_queue_size = queue_size;
#ifdef APP_SCHEDULER_WITH_PROFILER
m_max_queue_utilization = 0;
#endif
return NRF_SUCCESS;
}
#ifdef APP_SCHEDULER_WITH_PROFILER
static void check_queue_utilization(void)
{
uint16_t start = m_queue_start_index;
uint16_t end = m_queue_end_index;
uint16_t queue_utilization = (end >= start) ? (end - start) :
(m_queue_size + 1 - start + end);
if (queue_utilization > m_max_queue_utilization)
{
m_max_queue_utilization = queue_utilization;
}
}
uint16_t app_sched_queue_utilization_get(void)
{
return m_max_queue_utilization;
}
#endif
uint32_t app_sched_event_put(void * p_event_data,
uint16_t event_data_size,
app_sched_event_handler_t handler)
{
uint32_t err_code;
if (event_data_size <= m_queue_event_size)
{
uint16_t event_index = 0xFFFF;
CRITICAL_REGION_ENTER();
if (!APP_SCHED_QUEUE_FULL())
{
event_index = m_queue_end_index;
m_queue_end_index = next_index(m_queue_end_index);
}
CRITICAL_REGION_EXIT();
if (event_index != 0xFFFF)
{
//NOTE: This can be done outside the critical region since the event consumer will
//always be called from the main loop, and will thus never interrupt this code.
m_queue_event_headers[event_index].handler = handler;
if ((p_event_data != NULL) && (event_data_size > 0))
{
memcpy(&m_queue_event_data[event_index * m_queue_event_size],
p_event_data,
event_data_size);
m_queue_event_headers[event_index].event_data_size = event_data_size;
}
else
{
m_queue_event_headers[event_index].event_data_size = 0;
}
#ifdef APP_SCHEDULER_WITH_PROFILER
check_queue_utilization();
#endif
err_code = NRF_SUCCESS;
}
else
{
err_code = NRF_ERROR_NO_MEM;
}
}
else
{
err_code = NRF_ERROR_INVALID_LENGTH;
}
return err_code;
}
/**@brief Function for reading the next event from specified event queue.
*
* @param[out] pp_event_data Pointer to pointer to event data.
* @param[out] p_event_data_size Pointer to size of event data.
* @param[out] p_event_handler Pointer to event handler function pointer.
*
* @return NRF_SUCCESS if new event, NRF_ERROR_NOT_FOUND if event queue is empty.
*/
static uint32_t app_sched_event_get(void * * pp_event_data,
uint16_t * p_event_data_size,
app_sched_event_handler_t * p_event_handler)
{
uint32_t err_code = NRF_ERROR_NOT_FOUND;
if (!APP_SCHED_QUEUE_EMPTY())
{
uint16_t event_index;
//NOTE: There is no need for a critical region here, as this function will only be called
//from app_sched_execute() from inside the main loop, so it will never interrupt
//app_sched_event_put(). Also, updating of (i.e. writing to) the start index will be
//an atomic operation.
event_index = m_queue_start_index;
m_queue_start_index = next_index(m_queue_start_index);
*pp_event_data = &m_queue_event_data[event_index * m_queue_event_size];
*p_event_data_size = m_queue_event_headers[event_index].event_data_size;
*p_event_handler = m_queue_event_headers[event_index].handler;
err_code = NRF_SUCCESS;
}
return err_code;
}
void app_sched_pause(void)
{
CRITICAL_REGION_ENTER();
if (m_scheduler_paused_counter < UINT32_MAX)
{
m_scheduler_paused_counter++;
}
CRITICAL_REGION_EXIT();
}
void app_sched_resume(void)
{
CRITICAL_REGION_ENTER();
if (m_scheduler_paused_counter > 0)
{
m_scheduler_paused_counter--;
}
CRITICAL_REGION_EXIT();
}
/**@brief Function for checking if scheduler is paused which means that should break processing
* events.
*
* @return Boolean value - true if scheduler is paused, false otherwise.
*/
static __INLINE bool is_app_sched_paused(void)
{
return (m_scheduler_paused_counter > 0);
}
void app_sched_execute(void)
{
void * p_event_data;
uint16_t event_data_size;
app_sched_event_handler_t event_handler;
//Get next event (if any), and execute handler
while ((!is_app_sched_paused()) &&
(app_sched_event_get(&p_event_data, &event_data_size, &event_handler) == NRF_SUCCESS))
{
event_handler(p_event_data, event_data_size);
}
}

View File

@ -0,0 +1,187 @@
/* Copyright (c) 2015 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 "app_simple_timer.h"
#include "nrf.h"
#include "app_util_platform.h"
#include "app_error.h"
#include "nrf_timer.h"
#include "nrf_drv_timer.h"
#include "sdk_common.h"
/**@brief States of simple timer state machine.
*/
typedef enum
{
SIMPLE_TIMER_STATE_IDLE = 0,
SIMPLE_TIMER_STATE_INITIALIZED,
SIMPLE_TIMER_STATE_STOPPED,
SIMPLE_TIMER_STATE_STARTED
}simple_timer_states_t;
static app_simple_timer_mode_t m_mode; /**< Registered timer mode. */
static app_simple_timer_timeout_handler_t m_timeout_handler = NULL; /**< Registered time-out handler. */
static void * mp_timeout_handler_context = NULL; /**< Registered time-out handler context. */
static simple_timer_states_t m_simple_timer_state = SIMPLE_TIMER_STATE_IDLE; /**< State machine state. */
#define APP_SIMPLE_TIMER_INSTANCE 1
#if (APP_SIMPLE_TIMER_INSTANCE == 0)
#if (TIMER_CONFIG_MODE(0) != TIMER_MODE_MODE_Timer)
#error "Unsupported timer mode."
#endif
#if (TIMER_CONFIG_BIT_WIDTH(0) != TIMER_BITMODE_BITMODE_16Bit)
#error "Unsupported timer bit width."
#endif
const nrf_drv_timer_t SIMPLE_TIMER = NRF_DRV_TIMER_INSTANCE(0);
#elif (APP_SIMPLE_TIMER_INSTANCE == 1)
#if (TIMER_CONFIG_MODE(1) != TIMER_MODE_MODE_Timer)
#error "Unsupported timer mode."
#endif
#if (TIMER_CONFIG_BIT_WIDTH(1) != TIMER_BITMODE_BITMODE_16Bit)
#error "Unsupported timer bit width."
#endif
const nrf_drv_timer_t SIMPLE_TIMER = NRF_DRV_TIMER_INSTANCE(1);
#elif (APP_SIMPLE_TIMER_INSTANCE == 2)
#if (TIMER_CONFIG_MODE(2) != TIMER_MODE_MODE_Timer)
#error "Unsupported timer mode."
#endif
#if (TIMER_CONFIG_BIT_WIDTH(2) != TIMER_BITMODE_BITMODE_16Bit)
#error "Unsupported timer bit width."
#endif
const nrf_drv_timer_t SIMPLE_TIMER = NRF_DRV_TIMER_INSTANCE(2);
#else
#error "Wrong timer instance id."
#endif
/**
* @brief Handler for timer events.
*/
static void app_simple_timer_event_handler(nrf_timer_event_t event_type, void * p_context)
{
switch(event_type)
{
case NRF_TIMER_EVENT_COMPARE0:
if (m_mode == APP_SIMPLE_TIMER_MODE_SINGLE_SHOT)
{
m_simple_timer_state = SIMPLE_TIMER_STATE_STOPPED;
}
//@note: No NULL check required as performed in timer_start(...).
m_timeout_handler(mp_timeout_handler_context);
break;
default:
//Do nothing.
break;
}
}
uint32_t app_simple_timer_init(void)
{
uint32_t err_code = NRF_SUCCESS;
err_code = nrf_drv_timer_init(&SIMPLE_TIMER, NULL, app_simple_timer_event_handler);
if(NRF_SUCCESS == err_code)
{
m_simple_timer_state = SIMPLE_TIMER_STATE_INITIALIZED;
}
return err_code;
}
uint32_t app_simple_timer_start(app_simple_timer_mode_t mode,
app_simple_timer_timeout_handler_t timeout_handler,
uint16_t timeout_ticks,
void * p_context)
{
uint32_t err_code = NRF_SUCCESS;
nrf_timer_short_mask_t timer_short;
VERIFY_PARAM_NOT_NULL(timeout_handler);
if (APP_SIMPLE_TIMER_MODE_REPEATED == mode)
{
timer_short = NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK;
}
else if(APP_SIMPLE_TIMER_MODE_SINGLE_SHOT == mode)
{
timer_short = NRF_TIMER_SHORT_COMPARE0_STOP_MASK;
}
else
{
return NRF_ERROR_INVALID_PARAM;
}
if(SIMPLE_TIMER_STATE_IDLE == m_simple_timer_state)
{
return NRF_ERROR_INVALID_STATE;
}
if(SIMPLE_TIMER_STATE_STARTED == m_simple_timer_state)
{
err_code = app_simple_timer_stop();
APP_ERROR_CHECK(err_code);
}
if(SIMPLE_TIMER_STATE_STOPPED == m_simple_timer_state)
{
nrf_drv_timer_clear(&SIMPLE_TIMER);
}
m_mode = mode;
m_timeout_handler = timeout_handler;
mp_timeout_handler_context = p_context;
nrf_drv_timer_extended_compare(
&SIMPLE_TIMER, NRF_TIMER_CC_CHANNEL0, (uint32_t)timeout_ticks, timer_short, true);
if (m_simple_timer_state == SIMPLE_TIMER_STATE_STOPPED)
{
nrf_drv_timer_resume(&SIMPLE_TIMER);
}
else
{
nrf_drv_timer_enable(&SIMPLE_TIMER);
}
m_simple_timer_state = SIMPLE_TIMER_STATE_STARTED;
return NRF_SUCCESS;
}
uint32_t app_simple_timer_stop(void)
{
if(SIMPLE_TIMER_STATE_STARTED == m_simple_timer_state)
{
nrf_drv_timer_pause(&SIMPLE_TIMER);
m_simple_timer_state = SIMPLE_TIMER_STATE_STOPPED;
}
return NRF_SUCCESS;
}
uint32_t app_simple_timer_uninit(void)
{
uint32_t err_code = NRF_SUCCESS;
if(SIMPLE_TIMER_STATE_IDLE != m_simple_timer_state)
{
nrf_drv_timer_uninit(&SIMPLE_TIMER);
m_simple_timer_state = SIMPLE_TIMER_STATE_IDLE;
}
return err_code;
}

View File

@ -0,0 +1,98 @@
/* Copyright (c) 2015 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.
*
*/
/**@file
*
* @defgroup lib_driver_simple_timer Simple Timer
* @{
* @ingroup app_common
*
* @brief Simple timer module.
*
* Supported features and limitations:
* - Two modes: single shot mode and repeated mode.
* - No more than one timer can run simultaneously.
* - The timer is hard-coded to use the TIMER1 peripheral and compare channel 0.
*/
#ifndef TIMER_H__
#define TIMER_H__
#include <stdint.h>
/**@brief Timer time-out handler type. */
typedef void (*app_simple_timer_timeout_handler_t)(void * p_context);
/**@brief Timer modes. */
typedef enum
{
APP_SIMPLE_TIMER_MODE_SINGLE_SHOT, /**< The timer will expire only once. */
APP_SIMPLE_TIMER_MODE_REPEATED /**< The timer will restart each time it expires. */
} app_simple_timer_mode_t;
/**@brief Function for configuring and setting up the timer hardware.
*
* @note Configuration parameters should be set in nrf_drv_config.h file.
* The TIMER1_CONFIG_MODE has to be set to NRF_TIMER_MODE_TIMER value.
* The TIMER1_CONFIG_BIT_WIDTH has to be set to NRF_TIMER_BIT_WIDTH_16 value.
*
* @retval NRF_SUCCESS If the operation is successful.
* @retval NRF_ERROR_INVALID_STATE If the operation fails because the timer is already initialized.
* @retval NRF_ERROR_INVALID_PARAM If the operation fails because some configuration parameter is
* not valid.
*/
uint32_t app_simple_timer_init(void);
/**@brief Function for starting a timer.
*
* @note If this function is called for a timer that is already running, the currently running
* timer is stopped before starting the new one.
*
* @param[in] mode Timer mode (see @ref app_simple_timer_mode_t).
* @param[in] timeout_handler Function to be executed when the timer expires
* (see @ref app_simple_timer_timeout_handler_t).
* @param[in] timeout_ticks Number of timer ticks to time-out event.
* @param[in] p_context General purpose pointer. Will be passed to the time-out handler
* when the timer expires.
*
* @retval NRF_SUCCESS If the operation is successful.
* @retval NRF_ERROR_INVALID_STATE If the operation fails because @ref app_simple_timer_init has not
* been called and the operation is not allowed in this state.
* @retval NRF_ERROR_NULL If the operation fails because timeout_handler is NULL.
* @retval NRF_ERROR_INVALID_PARAM If the operation fails because "mode" parameter is not valid.
*/
uint32_t app_simple_timer_start(app_simple_timer_mode_t mode,
app_simple_timer_timeout_handler_t timeout_handler,
uint16_t timeout_ticks,
void * p_context);
/**@brief Function for stopping the timer.
*
* @retval NRF_SUCCESS If the operation is successful.
*/
uint32_t app_simple_timer_stop(void);
/**@brief Function for uninitializing the timer. Should be called also when the timer is not used
* anymore to reach lowest power consumption in system.
*
* @note The function switches off the internal core of the timer to reach lowest power consumption
* in system. The startup time from this state may be longer compared to starting the timer
* from the stopped state.
*
* @retval NRF_SUCCESS If the operation is successful.
*/
uint32_t app_simple_timer_uninit(void);
#endif // TIMER_H__
/** @} */

View File

@ -0,0 +1,112 @@
/* Copyright (c) 2015 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 "slip.h"
#include "nrf_error.h"
#define SLIP_END 0300 /* indicates end of packet */
#define SLIP_ESC 0333 /* indicates byte stuffing */
#define SLIP_ESC_END 0334 /* ESC ESC_END means END data byte */
#define SLIP_ESC_ESC 0335 /* ESC ESC_ESC means ESC data byte */
uint32_t slip_encode(uint8_t * p_output, uint8_t * p_input, uint32_t input_length, uint32_t output_buffer_length)
{
uint32_t input_index;
uint32_t output_index;
for (input_index = 0, output_index = 0; input_index < input_length && output_index < output_buffer_length; input_index++)
{
switch (p_input[input_index])
{
case SLIP_END:
p_output[output_index++] = SLIP_END;
p_output[output_index++] = SLIP_ESC_END;
break;
case SLIP_ESC:
p_output[output_index++] = SLIP_ESC;
p_output[output_index++] = SLIP_ESC_ESC;
break;
default:
p_output[output_index++] = p_input[input_index];
}
}
p_output[output_index++] = (uint8_t)SLIP_END;
p_output[output_index++] = (uint8_t)SLIP_END; // clarify that the packet has ended.
return output_index;
}
uint32_t slip_decoding_add_char(uint8_t c, buffer_t * p_buf, slip_state_t * current_state)
{
switch (*current_state)
{
case SLIP_DECODING:
if (c == SLIP_END)
{
*current_state = SLIP_END_RECEIVED;
}
else if (c == SLIP_ESC)
{
*current_state = SLIP_END_RECEIVED;
}
else
{
p_buf->p_buffer[p_buf->current_index++] = c;
p_buf->current_length++;
}
break;
case SLIP_ESC_RECEIVED:
if (c == SLIP_ESC_ESC)
{
p_buf->p_buffer[p_buf->current_index++] = SLIP_ESC;
p_buf->current_length++;
*current_state = SLIP_DECODING;
}
else
{
// violation of protocol
*current_state = SLIP_CLEARING_INVALID_PACKET;
return NRF_ERROR_INVALID_DATA;
}
break;
case SLIP_END_RECEIVED:
if (c == SLIP_ESC_END)
{
p_buf->p_buffer[p_buf->current_index++] = SLIP_END;
p_buf->current_length++;
*current_state = SLIP_DECODING;
}
else
{
// packet is finished
*current_state = SLIP_DECODING;
return NRF_SUCCESS;
}
break;
case SLIP_CLEARING_INVALID_PACKET:
if (c == SLIP_END)
{
*current_state = SLIP_DECODING;
p_buf->current_index = 0;
p_buf->current_length = 0;
}
break;
}
return NRF_ERROR_BUSY;
}

View File

@ -0,0 +1,66 @@
/* Copyright (c) 2015 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.
*
*/
#ifndef SLIP_H__
#define SLIP_H__
#include <stdint.h>
#include "app_fifo.h"
/** @file
*
* @defgroup slip SLIP encoding decoding
* @{
* @ingroup app_common
*
* @brief This module encodes and decodes slip packages (RFC1055).
*
* @details The standard is described in https://tools.ietf.org/html/rfc1055
*/
typedef enum {
SLIP_DECODING,
SLIP_END_RECEIVED,
SLIP_ESC_RECEIVED,
SLIP_CLEARING_INVALID_PACKET,
} slip_state_t;
typedef struct {
uint8_t * p_buffer;
uint32_t current_index;
uint32_t current_length;
uint32_t len;
} buffer_t;
/**@brief Encodes a slip packet.
*
* @details Note that the encoded output data will be longer than the input data.
*
* @retval The length of the encoded packet. If it is smaller than the input length, an error has occurred.
*/
uint32_t slip_encode(uint8_t * p_output, uint8_t * p_input, uint32_t input_length, uint32_t output_buffer_length);
/**@brief Decodes a slip packet.
*
* @details When decoding a slip packet, a state must be preserved. Initial state must be set to SLIP_DECODING.
*
* @retval NRF_SUCCESS when a packet is parsed. The length of the packet can be read out from p_buf->current_index
* @retval NRF_ERROR_BUSY when packet is not finished parsing
* @retval NRF_ERROR_INVALID_DATA when packet is encoded wrong.
This moves the decoding to SLIP_CLEARING_INVALID_PACKET, and will stay in this state until SLIP_END is encountered.
*/
uint32_t slip_decoding_add_char(uint8_t c, buffer_t * p_buf, slip_state_t * current_state);
#endif // SLIP_H__
/** @} */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,287 @@
/* Copyright (c) 2012 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.
*
*/
/** @file
*
* @defgroup app_timer Application Timer
* @{
* @ingroup app_common
*
* @brief Application timer functionality.
*
* @details This module enables the application to create multiple timer instances based on the RTC1
* peripheral. Checking for time-outs and invokation of user time-out handlers is performed
* in the RTC1 interrupt handler. List handling is done using a software interrupt (SWI0).
* Both interrupt handlers are running in APP_LOW priority level.
*
* @details When calling app_timer_start() or app_timer_stop(), the timer operation is just queued,
* and the software interrupt is triggered. The actual timer start/stop operation is
* executed by the SWI0 interrupt handler. Since the SWI0 interrupt is running in APP_LOW,
* if the application code calling the timer function is running in APP_LOW or APP_HIGH,
* the timer operation will not be performed until the application handler has returned.
* This will be the case, for example, when stopping a timer from a time-out handler when not using
* the scheduler.
*
* @details Use the USE_SCHEDULER parameter of the APP_TIMER_INIT() macro to select if the
* @ref app_scheduler should be used or not. Even if the scheduler is
* not used, app_timer.h will include app_scheduler.h, so when
* compiling, app_scheduler.h must be available in one of the compiler include paths.
*/
#ifndef APP_TIMER_H__
#define APP_TIMER_H__
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "app_error.h"
#include "app_util.h"
#include "compiler_abstraction.h"
#define APP_TIMER_CLOCK_FREQ 32768 /**< Clock frequency of the RTC timer used to implement the app timer module. */
#define APP_TIMER_MIN_TIMEOUT_TICKS 5 /**< Minimum value of the timeout_ticks parameter of app_timer_start(). */
#define APP_TIMER_NODE_SIZE 32 /**< Size of app_timer.timer_node_t (used to allocate data). */
#define APP_TIMER_USER_OP_SIZE 24 /**< Size of app_timer.timer_user_op_t (only for use inside APP_TIMER_BUF_SIZE()). */
#define APP_TIMER_USER_SIZE 8 /**< Size of app_timer.timer_user_t (only for use inside APP_TIMER_BUF_SIZE()). */
#define APP_TIMER_INT_LEVELS 3 /**< Number of interrupt levels from where timer operations may be initiated (only for use inside APP_TIMER_BUF_SIZE()). */
/**@brief Compute number of bytes required to hold the application timer data structures.
*
* @param[in] OP_QUEUE_SIZE Size of queues holding timer operations that are pending execution.
* Note that due to the queue implementation, this size must be one more
* than the size that is actually needed.
*
* @return Required application timer buffer size (in bytes).
*/
#define APP_TIMER_BUF_SIZE(OP_QUEUE_SIZE) \
( \
( \
APP_TIMER_INT_LEVELS \
* \
(APP_TIMER_USER_SIZE + ((OP_QUEUE_SIZE) + 1) * APP_TIMER_USER_OP_SIZE) \
) \
)
/**@brief Convert milliseconds to timer ticks.
*
* This macro uses 64-bit integer arithmetic, but as long as the macro parameters are
* constants (i.e. defines), the computation will be done by the preprocessor.
*
* When using this macro, ensure that the
* values provided as input result in an output value that is supported by the
* @ref app_timer_start function. For example, when the ticks for 1 ms is needed, the
* maximum possible value of PRESCALER must be 6, when @ref APP_TIMER_CLOCK_FREQ is 32768.
* This will result in a ticks value as 5. Any higher value for PRESCALER will result in a
* ticks value that is not supported by this module.
*
* @param[in] MS Milliseconds.
* @param[in] PRESCALER Value of the RTC1 PRESCALER register (must be the same value that was
* passed to APP_TIMER_INIT()).
*
* @return Number of timer ticks.
*/
#define APP_TIMER_TICKS(MS, PRESCALER)\
((uint32_t)ROUNDED_DIV((MS) * (uint64_t)APP_TIMER_CLOCK_FREQ, ((PRESCALER) + 1) * 1000))
typedef struct app_timer_t { uint32_t data[CEIL_DIV(APP_TIMER_NODE_SIZE, sizeof(uint32_t))]; } app_timer_t;
/**@brief Timer ID type.
* Never declare a variable of this type, but use the macro @ref APP_TIMER_DEF instead.*/
typedef app_timer_t * app_timer_id_t;
/**
* @brief Create a timer identifier and statically allocate memory for the timer.
*
* @param timer_id Name of the timer identifier variable that will be used to control the timer.
*/
#define APP_TIMER_DEF(timer_id) \
static app_timer_t timer_id##_data = { {0} }; \
static const app_timer_id_t timer_id = &timer_id##_data
/**@brief Application time-out handler type. */
typedef void (*app_timer_timeout_handler_t)(void * p_context);
/**@brief Type of function for passing events from the timer module to the scheduler. */
typedef uint32_t (*app_timer_evt_schedule_func_t) (app_timer_timeout_handler_t timeout_handler,
void * p_context);
/**@brief Timer modes. */
typedef enum
{
APP_TIMER_MODE_SINGLE_SHOT, /**< The timer will expire only once. */
APP_TIMER_MODE_REPEATED /**< The timer will restart each time it expires. */
} app_timer_mode_t;
/**@brief Initialize the application timer module.
*
* @details This macro handles dimensioning and allocation of the memory buffer required by the timer,
* making sure that the buffer is correctly aligned. It will also connect the timer module
* to the scheduler (if specified).
*
* @note This module assumes that the LFCLK is already running. If it is not, the module will
* be non-functional, since the RTC will not run. If you do not use a SoftDevice, you
* must start the LFCLK manually. See the rtc_example's lfclk_config() function
* for an example of how to do this. If you use a SoftDevice, the LFCLK is started on
* SoftDevice init.
*
*
* @param[in] PRESCALER Value of the RTC1 PRESCALER register. This will decide the
* timer tick rate. Set to 0 for no prescaling.
* @param[in] OP_QUEUES_SIZE Size of queues holding timer operations that are pending execution.
* @param[in] SCHEDULER_FUNC Pointer to scheduler event handler
*
* @note Since this macro allocates a buffer, it must only be called once (it is OK to call it
* several times as long as it is from the same location, for example, to do a re-initialization).
*/
/*lint -emacro(506, APP_TIMER_INIT) */ /* Suppress "Constant value Boolean */
#define APP_TIMER_INIT(PRESCALER, OP_QUEUES_SIZE, SCHEDULER_FUNC) \
do \
{ \
static uint32_t APP_TIMER_BUF[CEIL_DIV(APP_TIMER_BUF_SIZE((OP_QUEUES_SIZE) + 1), \
sizeof(uint32_t))]; \
uint32_t ERR_CODE = app_timer_init((PRESCALER), \
(OP_QUEUES_SIZE) + 1, \
APP_TIMER_BUF, \
SCHEDULER_FUNC); \
APP_ERROR_CHECK(ERR_CODE); \
} while (0)
/**@brief Function for initializing the timer module.
*
* Normally, initialization should be done using the APP_TIMER_INIT() macro, because that macro will both
* allocate the buffers needed by the timer module (including aligning the buffers correctly)
* and take care of connecting the timer module to the scheduler (if specified).
*
* @param[in] prescaler Value of the RTC1 PRESCALER register. Set to 0 for no prescaling.
* @param[in] op_queues_size Size of queues holding timer operations that are pending
* execution. Note that due to the queue implementation, this size must
* be one more than the size that is actually needed.
* @param[in] p_buffer Pointer to memory buffer for internal use in the app_timer
* module. The size of the buffer can be computed using the
* APP_TIMER_BUF_SIZE() macro. The buffer must be aligned to a
* 4 byte boundary.
* @param[in] evt_schedule_func Function for passing time-out events to the scheduler. Point to
* app_timer_evt_schedule() to connect to the scheduler. Set to NULL
* to make the timer module call the time-out handler directly from
* the timer interrupt handler.
*
* @retval NRF_SUCCESS If the module was initialized successfully.
* @retval NRF_ERROR_INVALID_PARAM If a parameter was invalid (buffer not aligned to a 4 byte
* boundary or NULL).
*/
uint32_t app_timer_init(uint32_t prescaler,
uint8_t op_queues_size,
void * p_buffer,
app_timer_evt_schedule_func_t evt_schedule_func);
/**@brief Function for creating a timer instance.
*
* @param[in] p_timer_id Pointer to timer identifier.
* @param[in] mode Timer mode.
* @param[in] timeout_handler Function to be executed when the timer expires.
*
* @retval NRF_SUCCESS If the timer was successfully created.
* @retval NRF_ERROR_INVALID_PARAM If a parameter was invalid.
* @retval NRF_ERROR_INVALID_STATE If the application timer module has not been initialized or
* the timer is running.
*
* @note This function does the timer allocation in the caller's context. It is also not protected
* by a critical region. Therefore care must be taken not to call it from several interrupt
* levels simultaneously.
* @note The function can be called again on the timer instance and will re-initialize the instance if
* the timer is not running.
* @attention The FreeRTOS and RTX app_timer implementation does not allow app_timer_create to
* be called on the previously initialized instance.
*/
uint32_t app_timer_create(app_timer_id_t const * p_timer_id,
app_timer_mode_t mode,
app_timer_timeout_handler_t timeout_handler);
/**@brief Function for starting a timer.
*
* @param[in] timer_id Timer identifier.
* @param[in] timeout_ticks Number of ticks (of RTC1, including prescaling) to time-out event
* (minimum 5 ticks).
* @param[in] p_context General purpose pointer. Will be passed to the time-out handler when
* the timer expires.
*
* @retval NRF_SUCCESS If the timer was successfully started.
* @retval NRF_ERROR_INVALID_PARAM If a parameter was invalid.
* @retval NRF_ERROR_INVALID_STATE If the application timer module has not been initialized or the timer
* has not been created.
* @retval NRF_ERROR_NO_MEM If the timer operations queue was full.
*
* @note The minimum timeout_ticks value is 5.
* @note For multiple active timers, time-outs occurring in close proximity to each other (in the
* range of 1 to 3 ticks) will have a positive jitter of maximum 3 ticks.
* @note When calling this method on a timer that is already running, the second start operation
* is ignored.
*/
uint32_t app_timer_start(app_timer_id_t timer_id, uint32_t timeout_ticks, void * p_context);
/**@brief Function for stopping the specified timer.
*
* @param[in] timer_id Timer identifier.
*
* @retval NRF_SUCCESS If the timer was successfully stopped.
* @retval NRF_ERROR_INVALID_PARAM If a parameter was invalid.
* @retval NRF_ERROR_INVALID_STATE If the application timer module has not been initialized or the timer
* has not been created.
* @retval NRF_ERROR_NO_MEM If the timer operations queue was full.
*/
uint32_t app_timer_stop(app_timer_id_t timer_id);
/**@brief Function for stopping all running timers.
*
* @retval NRF_SUCCESS If all timers were successfully stopped.
* @retval NRF_ERROR_INVALID_STATE If the application timer module has not been initialized.
* @retval NRF_ERROR_NO_MEM If the timer operations queue was full.
*/
uint32_t app_timer_stop_all(void);
/**@brief Function for returning the current value of the RTC1 counter.
*
* @param[out] p_ticks Current value of the RTC1 counter.
*
* @retval NRF_SUCCESS If the counter was successfully read.
*/
uint32_t app_timer_cnt_get(uint32_t * p_ticks);
/**@brief Function for computing the difference between two RTC1 counter values.
*
* @param[in] ticks_to Value returned by app_timer_cnt_get().
* @param[in] ticks_from Value returned by app_timer_cnt_get().
* @param[out] p_ticks_diff Number of ticks from ticks_from to ticks_to.
*
* @retval NRF_SUCCESS If the counter difference was successfully computed.
*/
uint32_t app_timer_cnt_diff_compute(uint32_t ticks_to,
uint32_t ticks_from,
uint32_t * p_ticks_diff);
#ifdef APP_TIMER_WITH_PROFILER
/**@brief Function for getting the maximum observed operation queue utilization.
*
* Function for tuning the module and determining OP_QUEUE_SIZE value and thus module RAM usage.
*
* @return Maximum number of events in queue observed so far.
*/
uint16_t app_timer_op_queue_utilization_get(void);
#endif
#endif // APP_TIMER_H__
/** @} */

View File

@ -0,0 +1,34 @@
/* Copyright (c) 2014 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 "app_timer_appsh.h"
#include "app_scheduler.h"
static void app_timer_evt_get(void * p_event_data, uint16_t event_size)
{
app_timer_event_t * p_timer_event = (app_timer_event_t *)p_event_data;
APP_ERROR_CHECK_BOOL(event_size == sizeof(app_timer_event_t));
p_timer_event->timeout_handler(p_timer_event->p_context);
}
uint32_t app_timer_evt_schedule(app_timer_timeout_handler_t timeout_handler,
void * p_context)
{
app_timer_event_t timer_event;
timer_event.timeout_handler = timeout_handler;
timer_event.p_context = p_context;
return app_sched_event_put(&timer_event, sizeof(timer_event), app_timer_evt_get);
}

View File

@ -0,0 +1,44 @@
/* Copyright (c) 2014 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.
*
*/
#ifndef APP_TIMER_APPSH_H
#define APP_TIMER_APPSH_H
#include "app_timer.h"
#define APP_TIMER_SCHED_EVT_SIZE sizeof(app_timer_event_t) /**< Size of button events being passed through the scheduler (is to be used for computing the maximum size of scheduler events). */
/**@brief Macro for initializing the application timer module to use with app_scheduler.
*
* @param[in] PRESCALER Value of the RTC1 PRESCALER register. This will decide the
* timer tick rate. Set to 0 for no prescaling.
* @param[in] OP_QUEUES_SIZE Size of queues holding timer operations that are pending execution.
* @param[in] USE_SCHEDULER TRUE if the application is using the app_scheduler,
* FALSE otherwise.
*
* @note Since this macro allocates a buffer, it must only be called once (it is OK to call it
* several times as long as it is from the same location, e.g. to do a reinitialization).
*/
#define APP_TIMER_APPSH_INIT(PRESCALER, OP_QUEUES_SIZE, USE_SCHEDULER) \
APP_TIMER_INIT(PRESCALER, OP_QUEUES_SIZE, \
(USE_SCHEDULER) ? app_timer_evt_schedule : NULL)
typedef struct
{
app_timer_timeout_handler_t timeout_handler;
void * p_context;
} app_timer_event_t;
uint32_t app_timer_evt_schedule(app_timer_timeout_handler_t timeout_handler,
void * p_context);
#endif // APP_TIMER_APPSH_H

View File

@ -0,0 +1,43 @@
/* Copyright (c) 2014 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 <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#ifdef ENABLE_DEBUG_LOG_SUPPORT
#include "app_trace.h"
#include "nrf_log.h"
void app_trace_init(void)
{
(void)NRF_LOG_INIT();
}
void app_trace_dump(uint8_t * p_buffer, uint32_t len)
{
app_trace_log("\r\n");
for (uint32_t index = 0; index < len; index++)
{
app_trace_log("0x%02X ", p_buffer[index]);
}
app_trace_log("\r\n");
}
#endif // ENABLE_DEBUG_LOG_SUPPORT
/**
*@}
**/

View File

@ -0,0 +1,56 @@
#ifndef __DEBUG_H_
#define __DEBUG_H_
#include <stdint.h>
#include <stdio.h>
/**
* @defgroup app_trace Debug Logger
* @ingroup app_common
* @{
* @brief Enables debug logs/ trace over UART.
* @details Enables debug logs/ trace over UART. Tracing is enabled only if
* ENABLE_DEBUG_LOG_SUPPORT is defined in the project.
*/
#ifdef ENABLE_DEBUG_LOG_SUPPORT
#include "nrf_log.h"
/**
* @brief Module Initialization.
*
* @details Initializes the module to use UART as trace output.
*
* @warning This function will configure UART using default board configuration.
* Do not call this function if UART is configured from a higher level in the application.
*/
void app_trace_init(void);
/**
* @brief Log debug messages.
*
* @details This API logs messages over UART. The module must be initialized before using this API.
*
* @note Though this is currently a macro, it should be used used and treated as function.
*/
#define app_trace_log NRF_LOG_PRINTF
/**
* @brief Dump auxiliary byte buffer to the debug trace.
*
* @details This API logs messages over UART. The module must be initialized before using this API.
*
* @param[in] p_buffer Buffer to be dumped on the debug trace.
* @param[in] len Size of the buffer.
*/
void app_trace_dump(uint8_t * p_buffer, uint32_t len);
#else // ENABLE_DEBUG_LOG_SUPPORT
#define app_trace_init(...)
#define app_trace_log(...)
#define app_trace_dump(...)
#endif // ENABLE_DEBUG_LOG_SUPPORT
/** @} */
#endif //__DEBUG_H_

View File

@ -0,0 +1,132 @@
/* Copyright (c) 2015 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 "app_uart.h"
#include "nrf_drv_uart.h"
#include "nrf_assert.h"
#include "sdk_common.h"
static uint8_t tx_buffer[1];
static uint8_t rx_buffer[1];
static volatile bool rx_done;
static app_uart_event_handler_t m_event_handler; /**< Event handler function. */
void uart_event_handler(nrf_drv_uart_event_t * p_event, void* p_context)
{
if (p_event->type == NRF_DRV_UART_EVT_RX_DONE)
{
app_uart_evt_t app_uart_event;
app_uart_event.evt_type = APP_UART_DATA;
app_uart_event.data.value = p_event->data.rxtx.p_data[0];
(void)nrf_drv_uart_rx(rx_buffer,1);
rx_done = true;
m_event_handler(&app_uart_event);
}
else if (p_event->type == NRF_DRV_UART_EVT_ERROR)
{
app_uart_evt_t app_uart_event;
app_uart_event.evt_type = APP_UART_COMMUNICATION_ERROR;
app_uart_event.data.error_communication = p_event->data.error.error_mask;
(void)nrf_drv_uart_rx(rx_buffer,1);
m_event_handler(&app_uart_event);
}
else if (p_event->type == NRF_DRV_UART_EVT_TX_DONE)
{
// Last byte from FIFO transmitted, notify the application.
// Notify that new data is available if this was first byte put in the buffer.
app_uart_evt_t app_uart_event;
app_uart_event.evt_type = APP_UART_TX_EMPTY;
m_event_handler(&app_uart_event);
}
}
uint32_t app_uart_init(const app_uart_comm_params_t * p_comm_params,
app_uart_buffers_t * p_buffers,
app_uart_event_handler_t event_handler,
app_irq_priority_t irq_priority)
{
nrf_drv_uart_config_t config = NRF_DRV_UART_DEFAULT_CONFIG;
config.baudrate = (nrf_uart_baudrate_t)p_comm_params->baud_rate;
config.hwfc = (p_comm_params->flow_control == APP_UART_FLOW_CONTROL_DISABLED) ?
NRF_UART_HWFC_DISABLED : NRF_UART_HWFC_ENABLED;
config.interrupt_priority = irq_priority;
config.parity = p_comm_params->use_parity ? NRF_UART_PARITY_INCLUDED : NRF_UART_PARITY_EXCLUDED;
config.pselcts = p_comm_params->cts_pin_no;
config.pselrts = p_comm_params->rts_pin_no;
config.pselrxd = p_comm_params->rx_pin_no;
config.pseltxd = p_comm_params->tx_pin_no;
m_event_handler = event_handler;
rx_done = false;
if (p_comm_params->flow_control == APP_UART_FLOW_CONTROL_LOW_POWER)
{
return NRF_ERROR_NOT_SUPPORTED;
}
uint32_t err_code = nrf_drv_uart_init(&config, uart_event_handler);
VERIFY_SUCCESS(err_code);
#ifdef NRF52
if (!config.use_easy_dma)
#endif
{
nrf_drv_uart_rx_enable();
}
return nrf_drv_uart_rx(rx_buffer,1);
}
uint32_t app_uart_get(uint8_t * p_byte)
{
ASSERT(p_byte);
uint32_t err_code = NRF_SUCCESS;
if (rx_done)
{
*p_byte = rx_buffer[0];
}
else
{
err_code = NRF_ERROR_NOT_FOUND;
}
return err_code;
}
uint32_t app_uart_put(uint8_t byte)
{
tx_buffer[0] = byte;
ret_code_t ret = nrf_drv_uart_tx(tx_buffer,1);
if (NRF_ERROR_BUSY == ret)
{
return NRF_ERROR_NO_MEM;
}
else if (ret != NRF_SUCCESS)
{
return NRF_ERROR_INTERNAL;
}
else
{
return NRF_SUCCESS;
}
}
uint32_t app_uart_flush(void)
{
return NRF_SUCCESS;
}
uint32_t app_uart_close(void)
{
nrf_drv_uart_uninit();
return NRF_SUCCESS;
}

View File

@ -0,0 +1,227 @@
/* 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.
*
*/
/**@file
*
* @defgroup app_uart UART module
* @{
* @ingroup app_common
*
* @brief UART module interface.
*/
#ifndef APP_UART_H__
#define APP_UART_H__
#include <stdint.h>
#include <stdbool.h>
#include "app_util_platform.h"
#define UART_PIN_DISCONNECTED 0xFFFFFFFF /**< Value indicating that no pin is connected to this UART register. */
/**@brief UART Flow Control modes for the peripheral.
*/
typedef enum
{
APP_UART_FLOW_CONTROL_DISABLED, /**< UART Hw Flow Control is disabled. */
APP_UART_FLOW_CONTROL_ENABLED, /**< Standard UART Hw Flow Control is enabled. */
APP_UART_FLOW_CONTROL_LOW_POWER /**< Specialized UART Hw Flow Control is used. The Low Power setting allows the \nRFXX to Power Off the UART module when CTS is in-active, and re-enabling the UART when the CTS signal becomes active. This allows the \nRFXX to safe power by only using the UART module when it is needed by the remote site. */
} app_uart_flow_control_t;
/**@brief UART communication structure holding configuration settings for the peripheral.
*/
typedef struct
{
uint8_t rx_pin_no; /**< RX pin number. */
uint8_t tx_pin_no; /**< TX pin number. */
uint8_t rts_pin_no; /**< RTS pin number, only used if flow control is enabled. */
uint8_t cts_pin_no; /**< CTS pin number, only used if flow control is enabled. */
app_uart_flow_control_t flow_control; /**< Flow control setting, if flow control is used, the system will use low power UART mode, based on CTS signal. */
bool use_parity; /**< Even parity if TRUE, no parity if FALSE. */
uint32_t baud_rate; /**< Baud rate configuration. */
} app_uart_comm_params_t;
/**@brief UART buffer for transmitting/receiving data.
*/
typedef struct
{
uint8_t * rx_buf; /**< Pointer to the RX buffer. */
uint32_t rx_buf_size; /**< Size of the RX buffer. */
uint8_t * tx_buf; /**< Pointer to the TX buffer. */
uint32_t tx_buf_size; /**< Size of the TX buffer. */
} app_uart_buffers_t;
/**@brief Enumeration which defines events used by the UART module upon data reception or error.
*
* @details The event type is used to indicate the type of additional information in the event
* @ref app_uart_evt_t.
*/
typedef enum
{
APP_UART_DATA_READY, /**< An event indicating that UART data has been received. The data is available in the FIFO and can be fetched using @ref app_uart_get. */
APP_UART_FIFO_ERROR, /**< An error in the FIFO module used by the app_uart module has occured. The FIFO error code is stored in app_uart_evt_t.data.error_code field. */
APP_UART_COMMUNICATION_ERROR, /**< An communication error has occured during reception. The error is stored in app_uart_evt_t.data.error_communication field. */
APP_UART_TX_EMPTY, /**< An event indicating that UART has completed transmission of all available data in the TX FIFO. */
APP_UART_DATA, /**< An event indicating that UART data has been received, and data is present in data field. This event is only used when no FIFO is configured. */
} app_uart_evt_type_t;
/**@brief Struct containing events from the UART module.
*
* @details The app_uart_evt_t is used to notify the application of asynchronous events when data
* are received on the UART peripheral or in case an error occured during data reception.
*/
typedef struct
{
app_uart_evt_type_t evt_type; /**< Type of event. */
union
{
uint32_t error_communication; /**< Field used if evt_type is: APP_UART_COMMUNICATION_ERROR. This field contains the value in the ERRORSRC register for the UART peripheral. The UART_ERRORSRC_x defines from nrf5x_bitfields.h can be used to parse the error code. See also the \nRFXX Series Reference Manual for specification. */
uint32_t error_code; /**< Field used if evt_type is: NRF_ERROR_x. Additional status/error code if the error event type is APP_UART_FIFO_ERROR. This error code refer to errors defined in nrf_error.h. */
uint8_t value; /**< Field used if evt_type is: NRF_ERROR_x. Additional status/error code if the error event type is APP_UART_FIFO_ERROR. This error code refer to errors defined in nrf_error.h. */
} data;
} app_uart_evt_t;
/**@brief Function for handling app_uart event callback.
*
* @details Upon an event in the app_uart module this callback function will be called to notify
* the application about the event.
*
* @param[in] p_app_uart_event Pointer to UART event.
*/
typedef void (* app_uart_event_handler_t) (app_uart_evt_t * p_app_uart_event);
/**@brief Macro for safe initialization of the UART module in a single user instance when using
* a FIFO together with UART.
*
* @param[in] P_COMM_PARAMS Pointer to a UART communication structure: app_uart_comm_params_t
* @param[in] RX_BUF_SIZE Size of desired RX buffer, must be a power of 2 or ZERO (No FIFO).
* @param[in] TX_BUF_SIZE Size of desired TX buffer, must be a power of 2 or ZERO (No FIFO).
* @param[in] EVT_HANDLER Event handler function to be called when an event occurs in the
* UART module.
* @param[in] IRQ_PRIO IRQ priority, app_irq_priority_t, for the UART module irq handler.
* @param[out] ERR_CODE The return value of the UART initialization function will be
* written to this parameter.
*
* @note Since this macro allocates a buffer and registers the module as a GPIOTE user when flow
* control is enabled, it must only be called once.
*/
#define APP_UART_FIFO_INIT(P_COMM_PARAMS, RX_BUF_SIZE, TX_BUF_SIZE, EVT_HANDLER, IRQ_PRIO, ERR_CODE) \
do \
{ \
app_uart_buffers_t buffers; \
static uint8_t rx_buf[RX_BUF_SIZE]; \
static uint8_t tx_buf[TX_BUF_SIZE]; \
\
buffers.rx_buf = rx_buf; \
buffers.rx_buf_size = sizeof (rx_buf); \
buffers.tx_buf = tx_buf; \
buffers.tx_buf_size = sizeof (tx_buf); \
ERR_CODE = app_uart_init(P_COMM_PARAMS, &buffers, EVT_HANDLER, IRQ_PRIO); \
} while (0)
/**@brief Macro for safe initialization of the UART module in a single user instance.
*
* @param[in] P_COMM_PARAMS Pointer to a UART communication structure: app_uart_comm_params_t
* @param[in] EVT_HANDLER Event handler function to be called when an event occurs in the
* UART module.
* @param[in] IRQ_PRIO IRQ priority, app_irq_priority_t, for the UART module irq handler.
* @param[out] ERR_CODE The return value of the UART initialization function will be
* written to this parameter.
*
* @note Since this macro allocates registers the module as a GPIOTE user when flow control is
* enabled, it must only be called once.
*/
#define APP_UART_INIT(P_COMM_PARAMS, EVT_HANDLER, IRQ_PRIO, ERR_CODE) \
do \
{ \
ERR_CODE = app_uart_init(P_COMM_PARAMS, NULL, EVT_HANDLER, IRQ_PRIO); \
} while (0)
/**@brief Function for initializing the UART module. Use this initialization when several instances of the UART
* module are needed.
*
*
* @note Normally single initialization should be done using the APP_UART_INIT() or
* APP_UART_INIT_FIFO() macro depending on whether the FIFO should be used by the UART, as
* that will allocate the buffers needed by the UART module (including aligning the buffer
* correctly).
* @param[in] p_comm_params Pin and communication parameters.
* @param[in] p_buffers RX and TX buffers, NULL is FIFO is not used.
* @param[in] error_handler Function to be called in case of an error.
* @param[in] irq_priority Interrupt priority level.
*
* @retval NRF_SUCCESS If successful initialization.
* @retval NRF_ERROR_INVALID_LENGTH If a provided buffer is not a power of two.
* @retval NRF_ERROR_NULL If one of the provided buffers is a NULL pointer.
*
* The below errors are propagated by the UART module to the caller upon registration when Hardware
* Flow Control is enabled. When Hardware Flow Control is not used, these errors cannot occur.
* @retval NRF_ERROR_INVALID_STATE The GPIOTE module is not in a valid state when registering
* the UART module as a user.
* @retval NRF_ERROR_INVALID_PARAM The UART module provides an invalid callback function when
* registering the UART module as a user.
* Or the value pointed to by *p_uart_uid is not a valid
* GPIOTE number.
* @retval NRF_ERROR_NO_MEM GPIOTE module has reached the maximum number of users.
*/
uint32_t app_uart_init(const app_uart_comm_params_t * p_comm_params,
app_uart_buffers_t * p_buffers,
app_uart_event_handler_t error_handler,
app_irq_priority_t irq_priority);
/**@brief Function for getting a byte from the UART.
*
* @details This function will get the next byte from the RX buffer. If the RX buffer is empty
* an error code will be returned and the app_uart module will generate an event upon
* reception of the first byte which is added to the RX buffer.
*
* @param[out] p_byte Pointer to an address where next byte received on the UART will be copied.
*
* @retval NRF_SUCCESS If a byte has been received and pushed to the pointer provided.
* @retval NRF_ERROR_NOT_FOUND If no byte is available in the RX buffer of the app_uart module.
*/
uint32_t app_uart_get(uint8_t * p_byte);
/**@brief Function for putting a byte on the UART.
*
* @details This call is non-blocking.
*
* @param[in] byte Byte to be transmitted on the UART.
*
* @retval NRF_SUCCESS If the byte was successfully put on the TX buffer for transmission.
* @retval NRF_ERROR_NO_MEM If no more space is available in the TX buffer.
* NRF_ERROR_NO_MEM may occur if flow control is enabled and CTS signal
* is high for a long period and the buffer fills up.
* @retval NRF_ERROR_INTERNAL If UART driver reported error.
*/
uint32_t app_uart_put(uint8_t byte);
/**@brief Function for flushing the RX and TX buffers (Only valid if FIFO is used).
* This function does nothing if FIFO is not used.
*
* @retval NRF_SUCCESS Flushing completed (Current implementation will always succeed).
*/
uint32_t app_uart_flush(void);
/**@brief Function for closing the UART module.
*
* @retval NRF_SUCCESS If successfully closed.
* @retval NRF_ERROR_INVALID_PARAM If an invalid user id is provided or the user id differs from
* the current active user.
*/
uint32_t app_uart_close(void);
#endif //APP_UART_H__
/** @} */

View File

@ -0,0 +1,191 @@
/* Copyright (c) 2015 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 "app_uart.h"
#include "app_fifo.h"
#include "nrf_drv_uart.h"
#include "nrf_assert.h"
#include "sdk_common.h"
static __INLINE uint32_t fifo_length(app_fifo_t * const fifo)
{
uint32_t tmp = fifo->read_pos;
return fifo->write_pos - tmp;
}
#define FIFO_LENGTH(F) fifo_length(&F) /**< Macro to calculate length of a FIFO. */
static app_uart_event_handler_t m_event_handler; /**< Event handler function. */
static uint8_t tx_buffer[1];
static uint8_t rx_buffer[1];
static app_fifo_t m_rx_fifo; /**< RX FIFO buffer for storing data received on the UART until the application fetches them using app_uart_get(). */
static app_fifo_t m_tx_fifo; /**< TX FIFO buffer for storing data to be transmitted on the UART when TXD is ready. Data is put to the buffer on using app_uart_put(). */
static void uart_event_handler(nrf_drv_uart_event_t * p_event, void* p_context)
{
app_uart_evt_t app_uart_event;
if (p_event->type == NRF_DRV_UART_EVT_RX_DONE)
{
// Write received byte to FIFO
uint32_t err_code = app_fifo_put(&m_rx_fifo, p_event->data.rxtx.p_data[0]);
if (err_code != NRF_SUCCESS)
{
app_uart_event.evt_type = APP_UART_FIFO_ERROR;
app_uart_event.data.error_code = err_code;
m_event_handler(&app_uart_event);
}
// Notify that new data is available if this was first byte put in the buffer.
else if (FIFO_LENGTH(m_rx_fifo) == 1)
{
app_uart_event.evt_type = APP_UART_DATA_READY;
m_event_handler(&app_uart_event);
}
else
{
// Do nothing, only send event if first byte was added or overflow in FIFO occurred.
}
if (FIFO_LENGTH(m_rx_fifo) <= m_rx_fifo.buf_size_mask)
{
(void)nrf_drv_uart_rx(rx_buffer, 1);
}
}
else if (p_event->type == NRF_DRV_UART_EVT_ERROR)
{
app_uart_event.evt_type = APP_UART_COMMUNICATION_ERROR;
app_uart_event.data.error_communication = p_event->data.error.error_mask;
(void)nrf_drv_uart_rx(rx_buffer, 1);
m_event_handler(&app_uart_event);
}
else if (p_event->type == NRF_DRV_UART_EVT_TX_DONE)
{
// Get next byte from FIFO.
if (app_fifo_get(&m_tx_fifo, tx_buffer) == NRF_SUCCESS)
{
(void)nrf_drv_uart_tx(tx_buffer, 1);
}
if (FIFO_LENGTH(m_tx_fifo) == 0)
{
// Last byte from FIFO transmitted, notify the application.
app_uart_event.evt_type = APP_UART_TX_EMPTY;
m_event_handler(&app_uart_event);
}
}
}
uint32_t app_uart_init(const app_uart_comm_params_t * p_comm_params,
app_uart_buffers_t * p_buffers,
app_uart_event_handler_t event_handler,
app_irq_priority_t irq_priority)
{
uint32_t err_code;
m_event_handler = event_handler;
if (p_buffers == NULL)
{
return NRF_ERROR_INVALID_PARAM;
}
// Configure buffer RX buffer.
err_code = app_fifo_init(&m_rx_fifo, p_buffers->rx_buf, p_buffers->rx_buf_size);
VERIFY_SUCCESS(err_code);
// Configure buffer TX buffer.
err_code = app_fifo_init(&m_tx_fifo, p_buffers->tx_buf, p_buffers->tx_buf_size);
VERIFY_SUCCESS(err_code);
nrf_drv_uart_config_t config = NRF_DRV_UART_DEFAULT_CONFIG;
config.baudrate = (nrf_uart_baudrate_t)p_comm_params->baud_rate;
config.hwfc = (p_comm_params->flow_control == APP_UART_FLOW_CONTROL_DISABLED) ?
NRF_UART_HWFC_DISABLED : NRF_UART_HWFC_ENABLED;
config.interrupt_priority = irq_priority;
config.parity = p_comm_params->use_parity ? NRF_UART_PARITY_INCLUDED : NRF_UART_PARITY_EXCLUDED;
config.pselcts = p_comm_params->cts_pin_no;
config.pselrts = p_comm_params->rts_pin_no;
config.pselrxd = p_comm_params->rx_pin_no;
config.pseltxd = p_comm_params->tx_pin_no;
err_code = nrf_drv_uart_init(&config, uart_event_handler);
VERIFY_SUCCESS(err_code);
#ifdef NRF52
if (!config.use_easy_dma)
#endif
{
nrf_drv_uart_rx_enable();
}
return nrf_drv_uart_rx(rx_buffer,1);
}
uint32_t app_uart_flush(void)
{
uint32_t err_code;
err_code = app_fifo_flush(&m_rx_fifo);
VERIFY_SUCCESS(err_code);
err_code = app_fifo_flush(&m_tx_fifo);
VERIFY_SUCCESS(err_code);
return NRF_SUCCESS;
}
uint32_t app_uart_get(uint8_t * p_byte)
{
ASSERT(p_byte);
// If FIFO was full new request to receive one byte was not scheduled. Must be done here.
if (FIFO_LENGTH(m_rx_fifo) == m_rx_fifo.buf_size_mask)
{
uint32_t err_code = nrf_drv_uart_rx(rx_buffer,1);
if (err_code != NRF_SUCCESS)
{
return NRF_ERROR_NOT_FOUND;
}
}
return app_fifo_get(&m_rx_fifo, p_byte);
}
uint32_t app_uart_put(uint8_t byte)
{
uint32_t err_code;
err_code = app_fifo_put(&m_tx_fifo, byte);
if (err_code == NRF_SUCCESS)
{
// The new byte has been added to FIFO. It will be picked up from there
// (in 'uart_event_handler') when all preceding bytes are transmitted.
// But if UART is not transmitting anything at the moment, we must start
// a new transmission here.
if (!nrf_drv_uart_tx_in_progress())
{
// This operation should be almost always successful, since we've
// just added a byte to FIFO, but if some bigger delay occurred
// (some heavy interrupt handler routine has been executed) since
// that time, FIFO might be empty already.
if (app_fifo_get(&m_tx_fifo, tx_buffer) == NRF_SUCCESS)
{
err_code = nrf_drv_uart_tx(tx_buffer, 1);
}
}
}
return err_code;
}
uint32_t app_uart_close(void)
{
nrf_drv_uart_uninit();
return NRF_SUCCESS;
}

View File

@ -0,0 +1,101 @@
/* Copyright (c) 2014 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.
*
*/
#if !defined(NRF_LOG_USES_RTT) || NRF_LOG_USES_RTT != 1
#if !defined(HAS_SIMPLE_UART_RETARGET)
#include <stdio.h>
#include <stdint.h>
#include "app_uart.h"
#include "nordic_common.h"
#include "nrf_error.h"
#if !defined(__ICCARM__)
struct __FILE
{
int handle;
};
#endif
FILE __stdout;
FILE __stdin;
#if defined(__CC_ARM) || defined(__ICCARM__)
int fgetc(FILE * p_file)
{
uint8_t input;
while (app_uart_get(&input) == NRF_ERROR_NOT_FOUND)
{
// No implementation needed.
}
return input;
}
int fputc(int ch, FILE * p_file)
{
UNUSED_PARAMETER(p_file);
UNUSED_VARIABLE(app_uart_put((uint8_t)ch));
return ch;
}
#elif defined(__GNUC__)
int _write(int file, const char * p_char, int len)
{
int i;
UNUSED_PARAMETER(file);
for (i = 0; i < len; i++)
{
UNUSED_VARIABLE(app_uart_put(*p_char++));
}
return len;
}
int _read(int file, char * p_char, int len)
{
UNUSED_PARAMETER(file);
while (app_uart_get((uint8_t *)p_char) == NRF_ERROR_NOT_FOUND)
{
// No implementation needed.
}
return 1;
}
#endif
#if defined(__ICCARM__)
__ATTRIBUTES size_t __write(int file, const unsigned char * p_char, size_t len)
{
int i;
UNUSED_PARAMETER(file);
for (i = 0; i < len; i++)
{
UNUSED_VARIABLE(app_uart_put(*p_char++));
}
return len;
}
#endif
#endif // !defined(HAS_SIMPLE_UART_RETARGET)
#endif // NRF_LOG_USES_RTT != 1

View File

@ -0,0 +1,124 @@
/* Copyright (c) 2014 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.
*
*/
/** @file
*
* @defgroup app_error Common application error handler
* @{
* @ingroup app_common
*
* @brief Common application error handler.
*/
#include "nrf.h"
#include <stdio.h>
#include "app_error.h"
#include "nordic_common.h"
#include "sdk_errors.h"
#include "nrf_log.h"
#ifdef DEBUG
#include "bsp.h"
#endif
/**@brief Function for error handling, which is called when an error has occurred.
*
* @warning This handler is an example only and does not fit a final product. You need to analyze
* how your product is supposed to react in case of error.
*
* @param[in] error_code Error code supplied to the handler.
* @param[in] line_num Line number where the handler is called.
* @param[in] p_file_name Pointer to the file name.
*/
/*lint -save -e14 */
void app_error_handler(ret_code_t error_code, uint32_t line_num, const uint8_t * p_file_name)
{
error_info_t error_info =
{
.line_num = line_num,
.p_file_name = p_file_name,
.err_code = error_code,
};
app_error_fault_handler(NRF_FAULT_ID_SDK_ERROR, 0, (uint32_t)(&error_info));
UNUSED_VARIABLE(error_info);
}
/*lint -save -e14 */
void app_error_handler_bare(ret_code_t error_code)
{
error_info_t error_info =
{
.line_num = 0,
.p_file_name = NULL,
.err_code = error_code,
};
app_error_fault_handler(NRF_FAULT_ID_SDK_ERROR, 0, (uint32_t)(&error_info));
UNUSED_VARIABLE(error_info);
}
void app_error_save_and_stop(uint32_t id, uint32_t pc, uint32_t info)
{
/* static error variables - in order to prevent removal by optimizers */
static volatile struct
{
uint32_t fault_id;
uint32_t pc;
uint32_t error_info;
assert_info_t * p_assert_info;
error_info_t * p_error_info;
ret_code_t err_code;
uint32_t line_num;
const uint8_t * p_file_name;
} m_error_data = {0};
// The following variable helps Keil keep the call stack visible, in addition, it can be set to
// 0 in the debugger to continue executing code after the error check.
volatile bool loop = true;
UNUSED_VARIABLE(loop);
m_error_data.fault_id = id;
m_error_data.pc = pc;
m_error_data.error_info = info;
switch (id)
{
case NRF_FAULT_ID_SDK_ASSERT:
m_error_data.p_assert_info = (assert_info_t *)info;
m_error_data.line_num = m_error_data.p_assert_info->line_num;
m_error_data.p_file_name = m_error_data.p_assert_info->p_file_name;
break;
case NRF_FAULT_ID_SDK_ERROR:
m_error_data.p_error_info = (error_info_t *)info;
m_error_data.err_code = m_error_data.p_error_info->err_code;
m_error_data.line_num = m_error_data.p_error_info->line_num;
m_error_data.p_file_name = m_error_data.p_error_info->p_file_name;
break;
}
UNUSED_VARIABLE(m_error_data);
// If printing is disrupted, remove the irq calls, or set the loop variable to 0 in the debugger.
__disable_irq();
while(loop);
__enable_irq();
}
/*lint -restore */

View File

@ -0,0 +1,201 @@
/* 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.
*
*/
/** @file
*
* @defgroup app_error Common application error handler
* @{
* @ingroup app_common
*
* @brief Common application error handler and macros for utilizing a common error handler.
*/
#ifndef APP_ERROR_H__
#define APP_ERROR_H__
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include "nrf.h"
#include "sdk_errors.h"
#include "nordic_common.h"
#include "nrf_log.h"
#include "app_error_weak.h"
#define NRF_FAULT_ID_SDK_RANGE_START 0x00004000 /**< The start of the range of error IDs defined in the SDK. */
/**@defgroup APP_ERROR_FAULT_IDS Fault ID types
* @{ */
#define NRF_FAULT_ID_SDK_ERROR NRF_FAULT_ID_SDK_RANGE_START + 1 /**< An error stemming from a call to @ref APP_ERROR_CHECK or @ref APP_ERROR_CHECK_BOOL. The info parameter is a pointer to an @ref error_info_t variable. */
#define NRF_FAULT_ID_SDK_ASSERT NRF_FAULT_ID_SDK_RANGE_START + 2 /**< An error stemming from a call to ASSERT (nrf_assert.h). The info parameter is a pointer to an @ref assert_info_t variable. */
/**@} */
/**@brief Structure containing info about an error of the type @ref NRF_FAULT_ID_SDK_ERROR.
*/
typedef struct
{
uint16_t line_num; /**< The line number where the error occurred. */
uint8_t const * p_file_name; /**< The file in which the error occurred. */
uint32_t err_code; /**< The error code representing the error that occurred. */
} error_info_t;
/**@brief Structure containing info about an error of the type @ref NRF_FAULT_ID_SDK_ASSERT.
*/
typedef struct
{
uint16_t line_num; /**< The line number where the error occurred. */
uint8_t const * p_file_name; /**< The file in which the error occurred. */
} assert_info_t;
/**@brief Function for error handling, which is called when an error has occurred.
*
* @param[in] error_code Error code supplied to the handler.
* @param[in] line_num Line number where the handler is called.
* @param[in] p_file_name Pointer to the file name.
*/
void app_error_handler(uint32_t error_code, uint32_t line_num, const uint8_t * p_file_name);
/**@brief Function for error handling, which is called when an error has occurred.
*
* @param[in] error_code Error code supplied to the handler.
*/
void app_error_handler_bare(ret_code_t error_code);
/**@brief Function for saving the parameters and entering an eternal loop, for debug purposes.
*
* @param[in] id Fault identifier. See @ref NRF_FAULT_IDS.
* @param[in] pc The program counter of the instruction that triggered the fault, or 0 if
* unavailable.
* @param[in] info Optional additional information regarding the fault. Refer to each fault
* identifier for details.
*/
void app_error_save_and_stop(uint32_t id, uint32_t pc, uint32_t info);
/**@brief Function for printing all error info (using nrf_log).
*
* @details Nrf_log library must be initialized using NRF_LOG_INIT macro before calling
* this function.
*
* @param[in] id Fault identifier. See @ref NRF_FAULT_IDS.
* @param[in] pc The program counter of the instruction that triggered the fault, or 0 if
* unavailable.
* @param[in] info Optional additional information regarding the fault. Refer to each fault
* identifier for details.
*/
static __INLINE void app_error_log(uint32_t id, uint32_t pc, uint32_t info)
{
switch (id)
{
case NRF_FAULT_ID_SDK_ASSERT:
NRF_LOG(NRF_LOG_COLOR_RED "\n*** ASSERTION FAILED ***\n");
if (((assert_info_t *)(info))->p_file_name)
{
NRF_LOG_PRINTF(NRF_LOG_COLOR_WHITE "Line Number: %u\n", (unsigned int) ((assert_info_t *)(info))->line_num);
NRF_LOG_PRINTF("File Name: %s\n", ((assert_info_t *)(info))->p_file_name);
}
NRF_LOG_PRINTF(NRF_LOG_COLOR_DEFAULT "\n");
break;
case NRF_FAULT_ID_SDK_ERROR:
NRF_LOG(NRF_LOG_COLOR_RED "\n*** APPLICATION ERROR *** \n" NRF_LOG_COLOR_WHITE);
if (((error_info_t *)(info))->p_file_name)
{
NRF_LOG_PRINTF("Line Number: %u\n", (unsigned int) ((error_info_t *)(info))->line_num);
NRF_LOG_PRINTF("File Name: %s\n", ((error_info_t *)(info))->p_file_name);
}
NRF_LOG_PRINTF("Error Code: 0x%X\n" NRF_LOG_COLOR_DEFAULT "\n", (unsigned int) ((error_info_t *)(info))->err_code);
break;
}
}
/**@brief Function for printing all error info (using printf).
*
* @param[in] id Fault identifier. See @ref NRF_FAULT_IDS.
* @param[in] pc The program counter of the instruction that triggered the fault, or 0 if
* unavailable.
* @param[in] info Optional additional information regarding the fault. Refer to each fault
* identifier for details.
*/
//lint -save -e438
static __INLINE void app_error_print(uint32_t id, uint32_t pc, uint32_t info)
{
unsigned int tmp = id;
printf("app_error_print():\r\n");
printf("Fault identifier: 0x%X\r\n", tmp);
printf("Program counter: 0x%X\r\n", tmp = pc);
printf("Fault information: 0x%X\r\n", tmp = info);
switch (id)
{
case NRF_FAULT_ID_SDK_ASSERT:
printf("Line Number: %u\r\n", tmp = ((assert_info_t *)(info))->line_num);
printf("File Name: %s\r\n", ((assert_info_t *)(info))->p_file_name);
break;
case NRF_FAULT_ID_SDK_ERROR:
printf("Line Number: %u\r\n", tmp = ((error_info_t *)(info))->line_num);
printf("File Name: %s\r\n", ((error_info_t *)(info))->p_file_name);
printf("Error Code: 0x%X\r\n", tmp = ((error_info_t *)(info))->err_code);
break;
}
}
//lint -restore
/**@brief Macro for calling error handler function.
*
* @param[in] ERR_CODE Error code supplied to the error handler.
*/
#ifdef DEBUG
#define APP_ERROR_HANDLER(ERR_CODE) \
do \
{ \
app_error_handler((ERR_CODE), __LINE__, (uint8_t*) __FILE__); \
} while (0)
#else
#define APP_ERROR_HANDLER(ERR_CODE) \
do \
{ \
app_error_handler_bare((ERR_CODE)); \
} while (0)
#endif
/**@brief Macro for calling error handler function if supplied error code any other than NRF_SUCCESS.
*
* @param[in] ERR_CODE Error code supplied to the error handler.
*/
#define APP_ERROR_CHECK(ERR_CODE) \
do \
{ \
const uint32_t LOCAL_ERR_CODE = (ERR_CODE); \
if (LOCAL_ERR_CODE != NRF_SUCCESS) \
{ \
APP_ERROR_HANDLER(LOCAL_ERR_CODE); \
} \
} while (0)
/**@brief Macro for calling error handler function if supplied boolean value is false.
*
* @param[in] BOOLEAN_VALUE Boolean value to be evaluated.
*/
#define APP_ERROR_CHECK_BOOL(BOOLEAN_VALUE) \
do \
{ \
const uint32_t LOCAL_BOOLEAN_VALUE = (BOOLEAN_VALUE); \
if (!LOCAL_BOOLEAN_VALUE) \
{ \
APP_ERROR_HANDLER(0); \
} \
} while (0)
#endif // APP_ERROR_H__
/** @} */

View File

@ -0,0 +1,53 @@
/* Copyright (c) 2016 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 "app_error.h"
#ifdef DEBUG
#include "bsp.h"
#endif
/*lint -save -e14 */
/**
* Function is implemented as weak so that it can be overwritten by custom application error handler
* when needed.
*/
__WEAK void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info)
{
// On assert, the system can only recover with a reset.
#ifndef DEBUG
NVIC_SystemReset();
#else
#ifdef BSP_DEFINES_ONLY
LEDS_ON(LEDS_MASK);
#else
UNUSED_VARIABLE(bsp_indication_set(BSP_INDICATE_FATAL_ERROR));
// This call can be used for debug purposes during application development.
// @note CAUTION: Activating this code will write the stack to flash on an error.
// This function should NOT be used in a final product.
// It is intended STRICTLY for development/debugging purposes.
// The flash write will happen EVEN if the radio is active, thus interrupting
// any communication.
// Use with care. Uncomment the line below to use.
//ble_debug_assert_handler(error_code, line_num, p_file_name);
#endif // BSP_DEFINES_ONLY
app_error_save_and_stop(id, pc, info);
#endif // DEBUG
}
/*lint -restore */

View File

@ -0,0 +1,51 @@
/* Copyright (c) 2016 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.
*
*/
#ifndef APP_ERROR_WEAK_H__
#define APP_ERROR_WEAK_H__
/** @file
*
* @defgroup app_error Common application error handler
* @{
* @ingroup app_common
*
* @brief Common application error handler.
*/
/**@brief Callback function for asserts in the SoftDevice.
*
* @details A pointer to this function will be passed to the SoftDevice. This function will be
* called by the SoftDevice if certain unrecoverable errors occur within the
* application or SoftDevice.
*
* See @ref nrf_fault_handler_t for more details.
*
* @param[in] id Fault identifier. See @ref NRF_FAULT_IDS.
* @param[in] pc The program counter of the instruction that triggered the fault, or 0 if
* unavailable.
* @param[in] info Optional additional information regarding the fault. Refer to each fault
* identifier for details.
*
* @remarks Function is implemented as weak so that it can be overwritten by custom application
* error handler when needed.
*/
#ifdef __CC_ARM
void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info);
#else
__WEAK void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info);
#endif
/** @} */
#endif // APP_ERROR_WEAK_H__

View File

@ -0,0 +1,493 @@
/* Copyright (c) 2012 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.
*
*/
/** @file
*
* @defgroup app_util Utility Functions and Definitions
* @{
* @ingroup app_common
*
* @brief Various types and definitions available to all applications.
*/
#ifndef APP_UTIL_H__
#define APP_UTIL_H__
#include <stdint.h>
#include <stdbool.h>
#include "compiler_abstraction.h"
#include "nrf.h"
//lint -save -e27 -e10 -e19
#if defined ( __CC_ARM )
extern char STACK$$Base;
extern char STACK$$Length;
#define STACK_BASE &STACK$$Base
#define STACK_TOP ((void*)((uint32_t)STACK_BASE + (uint32_t)&STACK$$Length))
#elif defined ( __ICCARM__ )
extern char CSTACK$$Base;
extern char CSTACK$$Length;
#define STACK_BASE &CSTACK$$Base
#define STACK_TOP ((void*)((uint32_t)STACK_BASE + (uint32_t)&CSTACK$$Length))
#elif defined ( __GNUC__ )
extern uint32_t __StackTop;
extern uint32_t __StackLimit;
#define STACK_BASE &__StackLimit
#define STACK_TOP &__StackTop
#endif
//lint -restore
enum
{
UNIT_0_625_MS = 625, /**< Number of microseconds in 0.625 milliseconds. */
UNIT_1_25_MS = 1250, /**< Number of microseconds in 1.25 milliseconds. */
UNIT_10_MS = 10000 /**< Number of microseconds in 10 milliseconds. */
};
/**@brief Implementation specific macro for delayed macro expansion used in string concatenation
*
* @param[in] lhs Left hand side in concatenation
* @param[in] rhs Right hand side in concatenation
*/
#define STRING_CONCATENATE_IMPL(lhs, rhs) lhs ## rhs
/**@brief Macro used to concatenate string using delayed macro expansion
*
* @note This macro will delay concatenation until the expressions have been resolved
*
* @param[in] lhs Left hand side in concatenation
* @param[in] rhs Right hand side in concatenation
*/
#define STRING_CONCATENATE(lhs, rhs) STRING_CONCATENATE_IMPL(lhs, rhs)
// Disable lint-warnings/errors for STATIC_ASSERT
//lint --emacro(10,STATIC_ASSERT)
//lint --emacro(18,STATIC_ASSERT)
//lint --emacro(19,STATIC_ASSERT)
//lint --emacro(30,STATIC_ASSERT)
//lint --emacro(37,STATIC_ASSERT)
//lint --emacro(42,STATIC_ASSERT)
//lint --emacro(26,STATIC_ASSERT)
//lint --emacro(102,STATIC_ASSERT)
//lint --emacro(533,STATIC_ASSERT)
//lint --emacro(534,STATIC_ASSERT)
//lint --emacro(132,STATIC_ASSERT)
//lint --emacro(414,STATIC_ASSERT)
//lint --emacro(578,STATIC_ASSERT)
//lint --emacro(628,STATIC_ASSERT)
//lint --emacro(648,STATIC_ASSERT)
//lint --emacro(830,STATIC_ASSERT)
/**@brief Macro for doing static (i.e. compile time) assertion.
*
* @note If the EXPR isn't resolvable, then the error message won't be shown.
*
* @note The output of STATIC_ASSERT will be different across different compilers.
*
* @param[in] EXPR Constant expression to be verified.
*/
#if defined ( __COUNTER__ )
#define STATIC_ASSERT(EXPR) \
;enum { STRING_CONCATENATE(static_assert_, __COUNTER__) = 1/(!!(EXPR)) }
#else
#define STATIC_ASSERT(EXPR) \
;enum { STRING_CONCATENATE(assert_line_, __LINE__) = 1/(!!(EXPR)) }
#endif
/**@brief Implementation details for NUM_VAR_ARGS */
#define NUM_VA_ARGS_IMPL( \
_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
_11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \
_21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \
_31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \
_41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \
_51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \
_61, _62, N, ...) N
/**@brief Macro to get the number of arguments in a call variadic macro call
*
* param[in] ... List of arguments
*
* @retval Number of variadic arguments in the argument list
*/
#define NUM_VA_ARGS(...) NUM_VA_ARGS_IMPL(__VA_ARGS__, 63, 62, 61, \
60, 59, 58, 57, 56, 55, 54, 53, 52, 51, \
50, 49, 48, 47, 46, 45, 44, 43, 42, 41, \
40, 39, 38, 37, 36, 35, 34, 33, 32, 31, \
30, 29, 28, 27, 26, 25, 24, 23, 22, 21, \
20, 19, 18, 17, 16, 15, 14, 13, 12, 11, \
10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
/**@brief type for holding an encoded (i.e. little endian) 16 bit unsigned integer. */
typedef uint8_t uint16_le_t[2];
/**@brief Type for holding an encoded (i.e. little endian) 32 bit unsigned integer. */
typedef uint8_t uint32_le_t[4];
/**@brief Byte array type. */
typedef struct
{
uint16_t size; /**< Number of array entries. */
uint8_t * p_data; /**< Pointer to array entries. */
} uint8_array_t;
/**@brief Macro for performing rounded integer division (as opposed to truncating the result).
*
* @param[in] A Numerator.
* @param[in] B Denominator.
*
* @return Rounded (integer) result of dividing A by B.
*/
#define ROUNDED_DIV(A, B) (((A) + ((B) / 2)) / (B))
/**@brief Macro for checking if an integer is a power of two.
*
* @param[in] A Number to be tested.
*
* @return true if value is power of two.
* @return false if value not power of two.
*/
#define IS_POWER_OF_TWO(A) ( ((A) != 0) && ((((A) - 1) & (A)) == 0) )
/**@brief Macro for converting milliseconds to ticks.
*
* @param[in] TIME Number of milliseconds to convert.
* @param[in] RESOLUTION Unit to be converted to in [us/ticks].
*/
#define MSEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000) / (RESOLUTION))
/**@brief Macro for performing integer division, making sure the result is rounded up.
*
* @details One typical use for this is to compute the number of objects with size B is needed to
* hold A number of bytes.
*
* @param[in] A Numerator.
* @param[in] B Denominator.
*
* @return Integer result of dividing A by B, rounded up.
*/
#define CEIL_DIV(A, B) \
(((A) + (B) - 1) / (B))
/**@brief Macro for creating a buffer aligned to 4 bytes.
*
* @param[in] NAME Name of the buffor.
* @param[in] MIN_SIZE Size of this buffor (it will be rounded up to multiples of 4 bytes).
*/
#define WORD_ALIGNED_MEM_BUFF(NAME, MIN_SIZE) static uint32_t NAME[CEIL_DIV(MIN_SIZE, sizeof(uint32_t))]
/**@brief Macro for calculating the number of words that are needed to hold a number of bytes.
*
* @details Adds 3 and divides by 4.
*
* @param[in] n_bytes The number of bytes.
*
* @return The number of words that @p n_bytes take up (rounded up).
*/
#define BYTES_TO_WORDS(n_bytes) (((n_bytes) + 3) >> 2)
/**@brief The number of bytes in a word.
*/
#define BYTES_PER_WORD (4)
/**@brief Macro for increasing a number to the nearest (larger) multiple of another number.
*
* @param[in] alignment The number to align to.
* @param[in] number The number to align (increase).
*
* @return The aligned (increased) @p number.
*/
#define ALIGN_NUM(alignment, number) ((number - 1) + alignment - ((number - 1) % alignment))
/**@brief Function for changing the value unit.
*
* @param[in] value Value to be rescaled.
* @param[in] old_unit_reversal Reversal of the incoming unit.
* @param[in] new_unit_reversal Reversal of the desired unit.
*
* @return Number of bytes written.
*/
static __INLINE uint64_t value_rescale(uint32_t value, uint32_t old_unit_reversal, uint16_t new_unit_reversal)
{
return (uint64_t)ROUNDED_DIV((uint64_t)value * new_unit_reversal, old_unit_reversal);
}
/**@brief Function for encoding a uint16 value.
*
* @param[in] value Value to be encoded.
* @param[out] p_encoded_data Buffer where the encoded data is to be written.
*
* @return Number of bytes written.
*/
static __INLINE uint8_t uint16_encode(uint16_t value, uint8_t * p_encoded_data)
{
p_encoded_data[0] = (uint8_t) ((value & 0x00FF) >> 0);
p_encoded_data[1] = (uint8_t) ((value & 0xFF00) >> 8);
return sizeof(uint16_t);
}
/**@brief Function for encoding a three-byte value.
*
* @param[in] value Value to be encoded.
* @param[out] p_encoded_data Buffer where the encoded data is to be written.
*
* @return Number of bytes written.
*/
static __INLINE uint8_t uint24_encode(uint32_t value, uint8_t * p_encoded_data)
{
p_encoded_data[0] = (uint8_t) ((value & 0x000000FF) >> 0);
p_encoded_data[1] = (uint8_t) ((value & 0x0000FF00) >> 8);
p_encoded_data[2] = (uint8_t) ((value & 0x00FF0000) >> 16);
return 3;
}
/**@brief Function for encoding a uint32 value.
*
* @param[in] value Value to be encoded.
* @param[out] p_encoded_data Buffer where the encoded data is to be written.
*
* @return Number of bytes written.
*/
static __INLINE uint8_t uint32_encode(uint32_t value, uint8_t * p_encoded_data)
{
p_encoded_data[0] = (uint8_t) ((value & 0x000000FF) >> 0);
p_encoded_data[1] = (uint8_t) ((value & 0x0000FF00) >> 8);
p_encoded_data[2] = (uint8_t) ((value & 0x00FF0000) >> 16);
p_encoded_data[3] = (uint8_t) ((value & 0xFF000000) >> 24);
return sizeof(uint32_t);
}
/**@brief Function for encoding a uint48 value.
*
* @param[in] value Value to be encoded.
* @param[out] p_encoded_data Buffer where the encoded data is to be written.
*
* @return Number of bytes written.
*/
static __INLINE uint8_t uint48_encode(uint64_t value, uint8_t * p_encoded_data)
{
p_encoded_data[0] = (uint8_t) ((value & 0x0000000000FF) >> 0);
p_encoded_data[1] = (uint8_t) ((value & 0x00000000FF00) >> 8);
p_encoded_data[2] = (uint8_t) ((value & 0x000000FF0000) >> 16);
p_encoded_data[3] = (uint8_t) ((value & 0x0000FF000000) >> 24);
p_encoded_data[4] = (uint8_t) ((value & 0x00FF00000000) >> 32);
p_encoded_data[5] = (uint8_t) ((value & 0xFF0000000000) >> 40);
return 6;
}
/**@brief Function for decoding a uint16 value.
*
* @param[in] p_encoded_data Buffer where the encoded data is stored.
*
* @return Decoded value.
*/
static __INLINE uint16_t uint16_decode(const uint8_t * p_encoded_data)
{
return ( (((uint16_t)((uint8_t *)p_encoded_data)[0])) |
(((uint16_t)((uint8_t *)p_encoded_data)[1]) << 8 ));
}
/**@brief Function for decoding a uint16 value in big-endian format.
*
* @param[in] p_encoded_data Buffer where the encoded data is stored.
*
* @return Decoded value.
*/
static __INLINE uint16_t uint16_big_decode(const uint8_t * p_encoded_data)
{
return ( (((uint16_t)((uint8_t *)p_encoded_data)[0]) << 8 ) |
(((uint16_t)((uint8_t *)p_encoded_data)[1])) );
}
/**@brief Function for decoding a three-byte value.
*
* @param[in] p_encoded_data Buffer where the encoded data is stored.
*
* @return Decoded value (uint32_t).
*/
static __INLINE uint32_t uint24_decode(const uint8_t * p_encoded_data)
{
return ( (((uint32_t)((uint8_t *)p_encoded_data)[0]) << 0) |
(((uint32_t)((uint8_t *)p_encoded_data)[1]) << 8) |
(((uint32_t)((uint8_t *)p_encoded_data)[2]) << 16));
}
/**@brief Function for decoding a uint32 value.
*
* @param[in] p_encoded_data Buffer where the encoded data is stored.
*
* @return Decoded value.
*/
static __INLINE uint32_t uint32_decode(const uint8_t * p_encoded_data)
{
return ( (((uint32_t)((uint8_t *)p_encoded_data)[0]) << 0) |
(((uint32_t)((uint8_t *)p_encoded_data)[1]) << 8) |
(((uint32_t)((uint8_t *)p_encoded_data)[2]) << 16) |
(((uint32_t)((uint8_t *)p_encoded_data)[3]) << 24 ));
}
/**@brief Function for decoding a uint32 value in big-endian format.
*
* @param[in] p_encoded_data Buffer where the encoded data is stored.
*
* @return Decoded value.
*/
static __INLINE uint32_t uint32_big_decode(const uint8_t * p_encoded_data)
{
return ( (((uint32_t)((uint8_t *)p_encoded_data)[0]) << 24) |
(((uint32_t)((uint8_t *)p_encoded_data)[1]) << 16) |
(((uint32_t)((uint8_t *)p_encoded_data)[2]) << 8) |
(((uint32_t)((uint8_t *)p_encoded_data)[3]) << 0) );
}
/**@brief Function for encoding a uint32 value in big-endian format.
*
* @param[in] value Value to be encoded.
* @param[out] p_encoded_data Buffer where the encoded data will be written.
*
* @return Number of bytes written.
*/
static __INLINE uint8_t uint32_big_encode(uint32_t value, uint8_t * p_encoded_data)
{
#ifdef NRF51
p_encoded_data[0] = (uint8_t) ((value & 0xFF000000) >> 24);
p_encoded_data[1] = (uint8_t) ((value & 0x00FF0000) >> 16);
p_encoded_data[2] = (uint8_t) ((value & 0x0000FF00) >> 8);
p_encoded_data[3] = (uint8_t) ((value & 0x000000FF) >> 0);
#elif NRF52
*(uint32_t *)p_encoded_data = __REV(value);
#endif
return sizeof(uint32_t);
}
/**@brief Function for decoding a uint48 value.
*
* @param[in] p_encoded_data Buffer where the encoded data is stored.
*
* @return Decoded value. (uint64_t)
*/
static __INLINE uint64_t uint48_decode(const uint8_t * p_encoded_data)
{
return ( (((uint64_t)((uint8_t *)p_encoded_data)[0]) << 0) |
(((uint64_t)((uint8_t *)p_encoded_data)[1]) << 8) |
(((uint64_t)((uint8_t *)p_encoded_data)[2]) << 16) |
(((uint64_t)((uint8_t *)p_encoded_data)[3]) << 24) |
(((uint64_t)((uint8_t *)p_encoded_data)[4]) << 32) |
(((uint64_t)((uint8_t *)p_encoded_data)[5]) << 40 ));
}
/** @brief Function for converting the input voltage (in milli volts) into percentage of 3.0 Volts.
*
* @details The calculation is based on a linearized version of the battery's discharge
* curve. 3.0V returns 100% battery level. The limit for power failure is 2.1V and
* is considered to be the lower boundary.
*
* The discharge curve for CR2032 is non-linear. In this model it is split into
* 4 linear sections:
* - Section 1: 3.0V - 2.9V = 100% - 42% (58% drop on 100 mV)
* - Section 2: 2.9V - 2.74V = 42% - 18% (24% drop on 160 mV)
* - Section 3: 2.74V - 2.44V = 18% - 6% (12% drop on 300 mV)
* - Section 4: 2.44V - 2.1V = 6% - 0% (6% drop on 340 mV)
*
* These numbers are by no means accurate. Temperature and
* load in the actual application is not accounted for!
*
* @param[in] mvolts The voltage in mV
*
* @return Battery level in percent.
*/
static __INLINE uint8_t battery_level_in_percent(const uint16_t mvolts)
{
uint8_t battery_level;
if (mvolts >= 3000)
{
battery_level = 100;
}
else if (mvolts > 2900)
{
battery_level = 100 - ((3000 - mvolts) * 58) / 100;
}
else if (mvolts > 2740)
{
battery_level = 42 - ((2900 - mvolts) * 24) / 160;
}
else if (mvolts > 2440)
{
battery_level = 18 - ((2740 - mvolts) * 12) / 300;
}
else if (mvolts > 2100)
{
battery_level = 6 - ((2440 - mvolts) * 6) / 340;
}
else
{
battery_level = 0;
}
return battery_level;
}
/**@brief Function for checking if a pointer value is aligned to a 4 byte boundary.
*
* @param[in] p Pointer value to be checked.
*
* @return TRUE if pointer is aligned to a 4 byte boundary, FALSE otherwise.
*/
static __INLINE bool is_word_aligned(void const* p)
{
return (((uintptr_t)p & 0x03) == 0);
}
/**
* @brief Function for checking if provided address is located in stack space.
*
* @param[in] ptr Pointer to be checked.
*
* @return true if address is in stack space, false otherwise.
*/
static __INLINE bool is_address_from_stack(void * ptr)
{
if (((uint32_t)ptr >= (uint32_t)STACK_BASE) &&
((uint32_t)ptr < (uint32_t)STACK_TOP) )
{
return true;
}
else
{
return false;
}
}
#endif // APP_UTIL_H__
/** @} */

View File

@ -0,0 +1,413 @@
/* Copyright (c) 2012 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.
*
*/
/** @file
*
* @defgroup app_util Utility Functions and Definitions
* @{
* @ingroup app_common
*
* @brief Various types and definitions available to all applications.
*/
#ifndef APP_UTIL_BDS_H__
#define APP_UTIL_BDS_H__
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include "compiler_abstraction.h"
#include "app_util.h"
#include "ble_srv_common.h"
#include "nordic_common.h"
typedef uint8_t nibble_t;
typedef uint32_t uint24_t;
typedef uint64_t uint40_t;
/**@brief IEEE 11073-20601 Regulatory Certification Data List Structure */
typedef struct
{
uint8_t * p_list; /**< Pointer the byte array containing the encoded opaque structure based on IEEE 11073-20601 specification. */
uint8_t list_len; /**< Length of the byte array. */
} regcertdatalist_t;
/**@brief SFLOAT format (IEEE-11073 16-bit FLOAT, meaning 4 bits for exponent (base 10) and 12 bits mantissa) */
typedef struct
{
int8_t exponent; /**< Base 10 exponent, should be using only 4 bits */
int16_t mantissa; /**< Mantissa, should be using only 12 bits */
} sfloat_t;
/**@brief Date and Time structure. */
typedef struct
{
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hours;
uint8_t minutes;
uint8_t seconds;
} ble_date_time_t;
/**@brief Function for encoding a uint16 value.
*
* @param[in] p_value Value to be encoded.
* @param[out] p_encoded_data Buffer where the encoded data is to be written.
*
* @return Number of bytes written.
*/
static __INLINE uint8_t bds_uint16_encode(const uint16_t * p_value, uint8_t * p_encoded_data)
{
p_encoded_data[0] = (uint8_t) ((*p_value & 0x00FF) >> 0);
p_encoded_data[1] = (uint8_t) ((*p_value & 0xFF00) >> 8);
return sizeof(uint16_t);
}
static __INLINE uint8_t bds_int16_encode(const int16_t * p_value, uint8_t * p_encoded_data)
{
uint16_t tmp = *p_value;
return bds_uint16_encode(&tmp, p_encoded_data);
}
/**@brief Function for encoding a uint24 value.
*
* @param[in] p_value Value to be encoded.
* @param[out] p_encoded_data Buffer where the encoded data is to be written.
*
* @return Number of bytes written.
*/
static __INLINE uint8_t bds_uint24_encode(const uint32_t * p_value, uint8_t * p_encoded_data)
{
p_encoded_data[0] = (uint8_t) ((*p_value & 0x000000FF) >> 0);
p_encoded_data[1] = (uint8_t) ((*p_value & 0x0000FF00) >> 8);
p_encoded_data[2] = (uint8_t) ((*p_value & 0x00FF0000) >> 16);
return (3);
}
/**@brief Function for encoding a uint32 value.
*
* @param[in] p_value Value to be encoded.
* @param[out] p_encoded_data Buffer where the encoded data is to be written.
*
* @return Number of bytes written.
*/
static __INLINE uint8_t bds_uint32_encode(const uint32_t * p_value, uint8_t * p_encoded_data)
{
p_encoded_data[0] = (uint8_t) ((*p_value & 0x000000FF) >> 0);
p_encoded_data[1] = (uint8_t) ((*p_value & 0x0000FF00) >> 8);
p_encoded_data[2] = (uint8_t) ((*p_value & 0x00FF0000) >> 16);
p_encoded_data[3] = (uint8_t) ((*p_value & 0xFF000000) >> 24);
return sizeof(uint32_t);
}
/**@brief Function for encoding a uint40 value.
*
* @param[in] p_value Value to be encoded.
* @param[out] p_encoded_data Buffer where the encoded data is to be written.
*
* @return Number of bytes written.
*/
static __INLINE uint8_t bds_uint40_encode(const uint64_t * p_value, uint8_t * p_encoded_data)
{
p_encoded_data[0] = (uint8_t) ((*p_value & 0x00000000000000FF) >> 0);
p_encoded_data[1] = (uint8_t) ((*p_value & 0x000000000000FF00) >> 8);
p_encoded_data[2] = (uint8_t) ((*p_value & 0x0000000000FF0000) >> 16);
p_encoded_data[3] = (uint8_t) ((*p_value & 0x00000000FF000000) >> 24);
p_encoded_data[4] = (uint8_t) ((*p_value & 0x000000FF00000000) >> 32);
return 5;
}
/**@brief Function for encoding a sfloat value.
*
* @param[in] p_value Value to be encoded.
* @param[out] p_encoded_data Buffer where the encoded data is to be written.
*
* @return Number of bytes written.
*/
static __INLINE uint8_t bds_sfloat_encode(const sfloat_t * p_value, uint8_t * p_encoded_data)
{
uint16_t encoded_val;
encoded_val = ((p_value->exponent << 12) & 0xF000) |
((p_value->mantissa << 0) & 0x0FFF);
return(bds_uint16_encode(&encoded_val, p_encoded_data));
}
/**@brief Function for encoding a uint8_array value.
*
* @param[in] p_value Value to be encoded.
* @param[out] p_encoded_data Buffer where the encoded data is to be written.
*/
static __INLINE uint8_t bds_uint8_array_encode(const uint8_array_t * p_value,
uint8_t * p_encoded_data)
{
memcpy(p_encoded_data, p_value->p_data, p_value->size);
return p_value->size;
}
/**@brief Function for encoding a utf8_str value.
*
* @param[in] p_value Value to be encoded.
* @param[out] p_encoded_data Buffer where the encoded data is to be written.
*/
static __INLINE uint8_t bds_ble_srv_utf8_str_encode(const ble_srv_utf8_str_t * p_value,
uint8_t * p_encoded_data)
{
memcpy(p_encoded_data, p_value->p_str, p_value->length);
return p_value->length;
}
/**@brief Function for encoding a regcertdatalist value.
*
* @param[in] p_value Value to be encoded.
* @param[out] p_encoded_data Buffer where the encoded data is to be written.
*/
static __INLINE uint8_t bds_regcertdatalist_encode(const regcertdatalist_t * p_value,
uint8_t * p_encoded_data)
{
memcpy(p_encoded_data, p_value->p_list, p_value->list_len);
return p_value->list_len;
}
/**@brief Function for decoding a date_time value.
*
* @param[in] p_date_time pointer to the date_time structure to encode.
* @param[in] p_encoded_data pointer to the encoded data
* @return length of the encoded field.
*/
static __INLINE uint8_t bds_ble_date_time_encode(const ble_date_time_t * p_date_time,
uint8_t * p_encoded_data)
{
uint8_t len = bds_uint16_encode(&p_date_time->year, &p_encoded_data[0]);
p_encoded_data[len++] = p_date_time->month;
p_encoded_data[len++] = p_date_time->day;
p_encoded_data[len++] = p_date_time->hours;
p_encoded_data[len++] = p_date_time->minutes;
p_encoded_data[len++] = p_date_time->seconds;
return len;
}
/**@brief Function for decoding a uint16 value.
*
* @param[in] len length of the field to be decoded.
* @param[in] p_encoded_data Buffer where the encoded data is stored.
* @param[in] p_decoded_val pointer to the decoded value
* @return length of the decoded field.
*/
static __INLINE uint8_t bds_uint16_decode(const uint8_t len,
const uint8_t * p_encoded_data,
uint16_t * p_decoded_val)
{
UNUSED_VARIABLE(len);
*p_decoded_val = (((uint16_t)((uint8_t *)p_encoded_data)[0])) |
(((uint16_t)((uint8_t *)p_encoded_data)[1]) << 8 );
return (sizeof(uint16_t));
}
/**@brief Function for decoding a int16 value.
*
* @param[in] len length of the field to be decoded.
* @param[in] p_encoded_data Buffer where the encoded data is stored.
* @param[in] p_decoded_val pointer to the decoded value
* @return length of the decoded field.
*/
static __INLINE uint8_t bds_int16_decode(const uint8_t len,
const uint8_t * p_encoded_data,
int16_t * p_decoded_val)
{
UNUSED_VARIABLE(len);
uint16_t tmp = 0;
uint8_t retval = bds_uint16_decode(len, p_encoded_data, &tmp);
*p_decoded_val = (int16_t)tmp;
return retval;
}
/**@brief Function for decoding a uint24 value.
*
* @param[in] len length of the field to be decoded.
* @param[in] p_encoded_data Buffer where the encoded data is stored.
* @param[in] p_decoded_val pointer to the decoded value
*
* @return length of the decoded field.
*/
static __INLINE uint8_t bds_uint24_decode(const uint8_t len,
const uint8_t * p_encoded_data,
uint32_t * p_decoded_val)
{
UNUSED_VARIABLE(len);
*p_decoded_val = (((uint32_t)((uint8_t *)p_encoded_data)[0]) << 0) |
(((uint32_t)((uint8_t *)p_encoded_data)[1]) << 8) |
(((uint32_t)((uint8_t *)p_encoded_data)[2]) << 16);
return (3);
}
/**@brief Function for decoding a uint32 value.
*
* @param[in] len length of the field to be decoded.
* @param[in] p_encoded_data Buffer where the encoded data is stored.
* @param[in] p_decoded_val pointer to the decoded value
*
* @return length of the decoded field.
*/
static __INLINE uint8_t bds_uint32_decode(const uint8_t len,
const uint8_t * p_encoded_data,
uint32_t * p_decoded_val)
{
UNUSED_VARIABLE(len);
*p_decoded_val = (((uint32_t)((uint8_t *)p_encoded_data)[0]) << 0) |
(((uint32_t)((uint8_t *)p_encoded_data)[1]) << 8) |
(((uint32_t)((uint8_t *)p_encoded_data)[2]) << 16) |
(((uint32_t)((uint8_t *)p_encoded_data)[3]) << 24 );
return (sizeof(uint32_t));
}
/**@brief Function for decoding a uint40 value.
*
* @param[in] len length of the field to be decoded.
* @param[in] p_encoded_data Buffer where the encoded data is stored.
* @param[in] p_decoded_val pointer to the decoded value
*
* @return length of the decoded field.
*/
static __INLINE uint8_t bds_uint40_decode(const uint8_t len,
const uint8_t * p_encoded_data,
uint64_t * p_decoded_val)
{
UNUSED_VARIABLE(len);
*p_decoded_val = (((uint64_t)((uint8_t *)p_encoded_data)[0]) << 0) |
(((uint64_t)((uint8_t *)p_encoded_data)[1]) << 8) |
(((uint64_t)((uint8_t *)p_encoded_data)[2]) << 16) |
(((uint64_t)((uint8_t *)p_encoded_data)[3]) << 24 )|
(((uint64_t)((uint8_t *)p_encoded_data)[4]) << 32 );
return (40);
}
/**@brief Function for decoding a sfloat value.
*
* @param[in] len length of the field to be decoded.
* @param[in] p_encoded_data Buffer where the encoded data is stored.
* @param[in] p_decoded_val pointer to the decoded value
*
* @return length of the decoded field.
*/
static __INLINE uint8_t bds_sfloat_decode(const uint8_t len,
const uint8_t * p_encoded_data,
sfloat_t * p_decoded_val)
{
p_decoded_val->exponent = 0;
bds_uint16_decode(len, p_encoded_data, (uint16_t*)&p_decoded_val->mantissa);
p_decoded_val->exponent = (uint8_t)((p_decoded_val->mantissa & 0xF000) >> 12);
p_decoded_val->mantissa &= 0x0FFF;
return len;
}
/**@brief Function for decoding a uint8_array value.
*
* @param[in] len length of the field to be decoded.
* @param[in] p_encoded_data Buffer where the encoded data is stored.
* @param[in] p_decoded_val pointer to the decoded value
*
* @return length of the decoded field.
*/
static __INLINE uint8_t bds_uint8_array_decode(const uint8_t len,
const uint8_t * p_encoded_data,
uint8_array_t * p_decoded_val)
{
memcpy(p_decoded_val->p_data, p_encoded_data, len);
p_decoded_val->size = len;
return p_decoded_val->size;
}
/**@brief Function for decoding a utf8_str value.
*
* @param[in] len length of the field to be decoded.
* @param[in] p_encoded_data Buffer where the encoded data is stored.
* @param[in] p_decoded_val pointer to the decoded value
*
* @return length of the decoded field.
*/
static __INLINE uint8_t bds_ble_srv_utf8_str_decode(const uint8_t len,
const uint8_t * p_encoded_data,
ble_srv_utf8_str_t * p_decoded_val)
{
p_decoded_val->p_str = (uint8_t*)p_encoded_data;
p_decoded_val->length = len;
return p_decoded_val->length;
}
/**@brief Function for decoding a regcertdatalist value.
*
* @param[in] len length of the field to be decoded.
* @param[in] p_encoded_data Buffer where the encoded data is stored.
* @param[in] p_decoded_val pointer to the decoded value
*
* @return length of the decoded field.
*/
static __INLINE uint8_t bds_regcertdatalist_decode(const uint8_t len,
const uint8_t * p_encoded_data,
regcertdatalist_t * p_decoded_val)
{
memcpy(p_decoded_val->p_list, p_encoded_data, len);
p_decoded_val->list_len = len;
return p_decoded_val->list_len;
}
/**@brief Function for decoding a date_time value.
*
* @param[in] len length of the field to be decoded.
* @param[in] p_encoded_data Buffer where the encoded data is stored.
* @param[in] p_date_time pointer to the decoded value
*
* @return length of the decoded field.
*/
static __INLINE uint8_t bds_ble_date_time_decode(const uint8_t len,
const uint8_t * p_encoded_data,
ble_date_time_t * p_date_time)
{
UNUSED_VARIABLE(len);
uint8_t pos = bds_uint16_decode(len, &p_encoded_data[0], &p_date_time->year);
p_date_time->month = p_encoded_data[pos++];
p_date_time->day = p_encoded_data[pos++];
p_date_time->hours = p_encoded_data[pos++];
p_date_time->minutes = p_encoded_data[pos++];
p_date_time->seconds = p_encoded_data[pos++];
return pos;
}
#endif // APP_UTIL_BDS_H__
/** @} */

View File

@ -0,0 +1,60 @@
/* Copyright (c) 2014 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 "app_util_platform.h"
static uint32_t m_in_critical_region = 0;
void app_util_disable_irq(void)
{
__disable_irq();
m_in_critical_region++;
}
void app_util_enable_irq(void)
{
m_in_critical_region--;
if (m_in_critical_region == 0)
{
__enable_irq();
}
}
void app_util_critical_region_enter(uint8_t *p_nested)
{
#ifdef NRF52
ASSERT(APP_LEVEL_PRIVILEGED == privilege_level_get())
#endif
#if defined(SOFTDEVICE_PRESENT)
/* return value can be safely ignored */
(void) sd_nvic_critical_region_enter(p_nested);
#else
app_util_disable_irq();
#endif
}
void app_util_critical_region_exit(uint8_t nested)
{
#ifdef NRF52
ASSERT(APP_LEVEL_PRIVILEGED == privilege_level_get())
#endif
#if defined(SOFTDEVICE_PRESENT)
/* return value can be safely ignored */
(void) sd_nvic_critical_region_exit(nested);
#else
app_util_enable_irq();
#endif
}

View File

@ -0,0 +1,211 @@
/* Copyright (c) 2014 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.
*
*/
/**@file
*
* @defgroup app_util_platform Utility Functions and Definitions (Platform)
* @{
* @ingroup app_common
*
* @brief Various types and definitions available to all applications when using SoftDevice.
*/
#ifndef APP_UTIL_PLATFORM_H__
#define APP_UTIL_PLATFORM_H__
#include <stdint.h>
#include "compiler_abstraction.h"
#include "nrf.h"
#ifdef SOFTDEVICE_PRESENT
#include "nrf_soc.h"
#include "nrf_nvic.h"
#endif
#include "nrf_assert.h"
#include "app_error.h"
#if defined(NRF51)
#define _PRIO_SD_HIGH 0
#define _PRIO_APP_HIGH 1
#define _PRIO_APP_MID 1
#define _PRIO_SD_LOW 2
#define _PRIO_APP_LOW 3
#define _PRIO_APP_LOWEST 3
#define _PRIO_THREAD 4
#elif defined(NRF52)
#define _PRIO_SD_HIGH 0
#define _PRIO_SD_MID 1
#define _PRIO_APP_HIGH 2
#define _PRIO_APP_MID 3
#define _PRIO_SD_LOW 4
#define _PRIO_SD_LOWEST 5
#define _PRIO_APP_LOW 6
#define _PRIO_APP_LOWEST 7
#define _PRIO_THREAD 15
#else
#error "No platform defined"
#endif
/**@brief The interrupt priorities available to the application while the SoftDevice is active. */
typedef enum
{
#ifdef SOFTDEVICE_PRESENT
APP_IRQ_PRIORITY_HIGHEST = _PRIO_SD_HIGH,
#else
APP_IRQ_PRIORITY_HIGHEST = _PRIO_APP_HIGH,
#endif
APP_IRQ_PRIORITY_HIGH = _PRIO_APP_HIGH,
#ifndef SOFTDEVICE_PRESENT
APP_IRQ_PRIORITY_MID = _PRIO_SD_LOW,
#else
APP_IRQ_PRIORITY_MID = _PRIO_APP_MID,
#endif
APP_IRQ_PRIORITY_LOW = _PRIO_APP_LOW,
APP_IRQ_PRIORITY_LOWEST = _PRIO_APP_LOWEST,
APP_IRQ_PRIORITY_THREAD = _PRIO_THREAD /**< "Interrupt level" when running in Thread Mode. */
} app_irq_priority_t;
/*@brief The privilege levels available to applications in Thread Mode */
typedef enum
{
APP_LEVEL_UNPRIVILEGED,
APP_LEVEL_PRIVILEGED
} app_level_t;
/**@cond NO_DOXYGEN */
#define EXTERNAL_INT_VECTOR_OFFSET 16
/**@endcond */
#define PACKED(TYPE) __packed TYPE
void app_util_critical_region_enter (uint8_t *p_nested);
void app_util_critical_region_exit (uint8_t nested);
/**@brief Macro for entering a critical region.
*
* @note Due to implementation details, there must exist one and only one call to
* CRITICAL_REGION_EXIT() for each call to CRITICAL_REGION_ENTER(), and they must be located
* in the same scope.
*/
#ifdef SOFTDEVICE_PRESENT
#define CRITICAL_REGION_ENTER() \
{ \
uint8_t __CR_NESTED = 0; \
app_util_critical_region_enter(&__CR_NESTED);
#else
#define CRITICAL_REGION_ENTER() app_util_critical_region_enter(NULL)
#endif
/**@brief Macro for leaving a critical region.
*
* @note Due to implementation details, there must exist one and only one call to
* CRITICAL_REGION_EXIT() for each call to CRITICAL_REGION_ENTER(), and they must be located
* in the same scope.
*/
#ifdef SOFTDEVICE_PRESENT
#define CRITICAL_REGION_EXIT() \
app_util_critical_region_exit(__CR_NESTED); \
}
#else
#define CRITICAL_REGION_EXIT() app_util_critical_region_exit(0)
#endif
/* Workaround for Keil 4 */
#ifndef IPSR_ISR_Msk
#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */
#endif
/**@brief Macro to enable anonymous unions from a certain point in the code.
*/
#if defined(__CC_ARM)
#define ANON_UNIONS_ENABLE _Pragma("push") \
_Pragma("anon_unions")
#elif defined(__ICCARM__)
#define ANON_UNIONS_ENABLE _Pragma("language=extended")
#else
#define ANON_UNIONS_ENABLE
// No action will be taken.
// For GCC anonymous unions are enabled by default.
#endif
/**@brief Macro to disable anonymous unions from a certain point in the code.
* @note Call only after first calling @ref ANON_UNIONS_ENABLE.
*/
#if defined(__CC_ARM)
#define ANON_UNIONS_DISABLE _Pragma("pop")
#elif defined(__ICCARM__)
#define ANON_UNIONS_DISABLE
// for IAR leave anonymous unions enabled
#else
#define ANON_UNIONS_DISABLE
// No action will be taken.
// For GCC anonymous unions are enabled by default.
#endif
/* Workaround for Keil 4 */
#ifndef CONTROL_nPRIV_Msk
#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */
#endif
/**@brief Function for finding the current interrupt level.
*
* @return Current interrupt level.
* @retval APP_IRQ_PRIORITY_HIGH We are running in Application High interrupt level.
* @retval APP_IRQ_PRIORITY_LOW We are running in Application Low interrupt level.
* @retval APP_IRQ_PRIORITY_THREAD We are running in Thread Mode.
*/
static __INLINE uint8_t current_int_priority_get(void)
{
uint32_t isr_vector_num = __get_IPSR() & IPSR_ISR_Msk ;
if (isr_vector_num > 0)
{
int32_t irq_type = ((int32_t)isr_vector_num - EXTERNAL_INT_VECTOR_OFFSET);
return (NVIC_GetPriority((IRQn_Type)irq_type) & 0xFF);
}
else
{
return APP_IRQ_PRIORITY_THREAD;
}
}
/**@brief Function for finding out the current privilege level.
*
* @return Current privilege level.
* @retval APP_LEVEL_UNPRIVILEGED We are running in unprivileged level.
* @retval APP_LEVEL_PRIVILEGED We are running in privileged level.
*/
static __INLINE uint8_t privilege_level_get(void)
{
#if defined(NRF51)
/* the Cortex-M0 has no concept of privilege */
return APP_LEVEL_PRIVILEGED;
#elif defined(NRF52)
uint32_t isr_vector_num = __get_IPSR() & IPSR_ISR_Msk ;
if (0 == isr_vector_num)
{
/* Thread Mode, check nPRIV */
int32_t control = __get_CONTROL();
return control & CONTROL_nPRIV_Msk ? APP_LEVEL_UNPRIVILEGED : APP_LEVEL_PRIVILEGED;
}
else
{
/* Handler Mode, always privileged */
return APP_LEVEL_PRIVILEGED;
}
#endif
}
#endif // APP_UTIL_PLATFORM_H__
/** @} */

View File

@ -0,0 +1,38 @@
/* Copyright (c) 2009 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.
*
*/
#ifndef COMMON_H
#define COMMON_H
/*lint ++flb "Enter library region" */
#include <stdbool.h>
#include <stdint.h>
/* @file
* @brief Common header file for generic macros and definitions
*
*/
/*
* GPIO glue macros, this can be used to define a pin number in source/header file and use that macro for pin
* configuration using this expansion.
* example:
* #define RESET_PIN 8
* NRF_GPIO->PINCNF(RESET_PIN) = XXX ; // Expanded NRF_GPIO->PIN_CNF[8] = XXX
*/
#define PINX_GLUE(x, y, z) x##y##_##z /*!< first level glue for pin macros */
#define PINCNF(p) PINX_GLUE(PIN,p,CNF) /*!< gpio configure pin number 'p' */
#define PINOUT(p) PINX_GLUE(PIN,p,OUT) /*!< gpio out pin number 'p' */
/*lint --flb "Leave library region" */
#endif

View File

@ -0,0 +1,108 @@
/* Copyright (c) 2008 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.
*/
/** @file
* @brief Common defines and macros for firmware developed by Nordic Semiconductor.
*/
#ifndef NORDIC_COMMON_H__
#define NORDIC_COMMON_H__
/** The upper 8 bits of a 32 bit value */
//lint -emacro(572,MSB) // Suppress warning 572 "Excessive shift value"
#define MSB_32(a) (((a) & 0xFF000000) >> 24)
/** The lower 8 bits (of a 32 bit value) */
#define LSB_32(a) ((a) & 0x000000FF)
/** The upper 8 bits of a 16 bit value */
//lint -emacro(572,MSB_16) // Suppress warning 572 "Excessive shift value"
#define MSB_16(a) (((a) & 0xFF00) >> 8)
/** The lower 8 bits (of a 16 bit value) */
#define LSB_16(a) ((a) & 0x00FF)
/** Leaves the minimum of the two 32-bit arguments */
/*lint -emacro(506, MIN) */ /* Suppress "Constant value Boolean */
#define MIN(a, b) ((a) < (b) ? (a) : (b))
/** Leaves the maximum of the two 32-bit arguments */
/*lint -emacro(506, MAX) */ /* Suppress "Constant value Boolean */
#define MAX(a, b) ((a) < (b) ? (b) : (a))
/** Concatenates two parameters. Useful as a second level of indirection,
* when a parameter can be macro itself. */
#define CONCAT_2(p1, p2) p1##p2
/** Concatenates three parameters. Useful as a second level of indirection,
* when a parameter can be macro itself. */
#define CONCAT_3(p1, p2, p3) p1##p2##p3
/**@brief Set a bit in the uint32 word.
*
* @param[in] W Word whose bit is being set.
* @param[in] B Bit number in the word to be set.
*/
#define SET_BIT(W,B) ((W) |= (uint32_t)(1U << (B)))
/**@brief Clears a bit in the uint32 word.
*
* @param[in] W Word whose bit is to be cleared.
* @param[in] B Bit number in the word to be cleared.
*/
#define CLR_BIT(W, B) ((W) &= (~((uint32_t)1 << (B))))
/**@brief Checks if a bit is set.
*
* @param[in] W Word whose bit is to be checked.
* @param[in] B Bit number in the word to be checked.
*
* @retval 1 if bit is set.
* @retval 0 if bit is not set.
*/
#define IS_SET(W,B) (((W) >> (B)) & 1)
#define BIT_0 0x01 /**< The value of bit 0 */
#define BIT_1 0x02 /**< The value of bit 1 */
#define BIT_2 0x04 /**< The value of bit 2 */
#define BIT_3 0x08 /**< The value of bit 3 */
#define BIT_4 0x10 /**< The value of bit 4 */
#define BIT_5 0x20 /**< The value of bit 5 */
#define BIT_6 0x40 /**< The value of bit 6 */
#define BIT_7 0x80 /**< The value of bit 7 */
#define BIT_8 0x0100 /**< The value of bit 8 */
#define BIT_9 0x0200 /**< The value of bit 9 */
#define BIT_10 0x0400 /**< The value of bit 10 */
#define BIT_11 0x0800 /**< The value of bit 11 */
#define BIT_12 0x1000 /**< The value of bit 12 */
#define BIT_13 0x2000 /**< The value of bit 13 */
#define BIT_14 0x4000 /**< The value of bit 14 */
#define BIT_15 0x8000 /**< The value of bit 15 */
#define BIT_16 0x00010000 /**< The value of bit 16 */
#define BIT_17 0x00020000 /**< The value of bit 17 */
#define BIT_18 0x00040000 /**< The value of bit 18 */
#define BIT_19 0x00080000 /**< The value of bit 19 */
#define BIT_20 0x00100000 /**< The value of bit 20 */
#define BIT_21 0x00200000 /**< The value of bit 21 */
#define BIT_22 0x00400000 /**< The value of bit 22 */
#define BIT_23 0x00800000 /**< The value of bit 23 */
#define BIT_24 0x01000000 /**< The value of bit 24 */
#define BIT_25 0x02000000 /**< The value of bit 25 */
#define BIT_26 0x04000000 /**< The value of bit 26 */
#define BIT_27 0x08000000 /**< The value of bit 27 */
#define BIT_28 0x10000000 /**< The value of bit 28 */
#define BIT_29 0x20000000 /**< The value of bit 29 */
#define BIT_30 0x40000000 /**< The value of bit 30 */
#define BIT_31 0x80000000 /**< The value of bit 31 */
#define UNUSED_VARIABLE(X) ((void)(X))
#define UNUSED_PARAMETER(X) UNUSED_VARIABLE(X)
#define UNUSED_RETURN_VALUE(X) UNUSED_VARIABLE(X)
#endif // NORDIC_COMMON_H__

View File

@ -0,0 +1,28 @@
/* Copyright (c) 2006 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 "nrf_assert.h"
#include "app_error.h"
#include "nordic_common.h"
#if defined(DEBUG_NRF)
void assert_nrf_callback(uint16_t line_num, const uint8_t * file_name)
{
assert_info_t assert_info =
{
.line_num = line_num,
.p_file_name = file_name,
};
app_error_fault_handler(NRF_FAULT_ID_SDK_ASSERT, 0, (uint32_t)(&assert_info));
UNUSED_VARIABLE(assert_info);
}
#endif /* DEBUG_NRF */

View File

@ -0,0 +1,60 @@
/*
* Copyright (c) 2006 Nordic Semiconductor. All Rights Reserved.
*
* The information contained herein is confidential property of Nordic Semiconductor. The use,
* copying, transfer or disclosure of such information is prohibited except by express written
* agreement with Nordic Semiconductor.
*
*/
/** @file
* @brief Utilities for verifying program logic
*/
#ifndef NRF_ASSERT_H_
#define NRF_ASSERT_H_
#include <stdint.h>
#include "nrf.h"
#include "app_error.h"
#if defined(DEBUG_NRF) || defined(DEBUG_NRF_USER)
/** @brief Function for handling assertions.
*
*
* @note
* This function is called when an assertion has triggered.
*
*
* @post
* All hardware is put into an idle non-emitting state (in particular the radio is highly
* important to switch off since the radio might be in a state that makes it send
* packets continiously while a typical final infinit ASSERT loop is executing).
*
*
* @param line_num The line number where the assertion is called
* @param file_name Pointer to the file name
*/
void assert_nrf_callback(uint16_t line_num, const uint8_t *file_name);
/*lint -emacro(506, ASSERT) */ /* Suppress "Constant value Boolean */
/*lint -emacro(774, ASSERT) */ /* Suppress "Boolean within 'if' always evaluates to True" */ \
/** @brief Function for checking intended for production code.
*
* Check passes if "expr" evaluates to true. */
#define ASSERT(expr) \
if (expr) \
{ \
} \
else \
{ \
assert_nrf_callback((uint16_t)__LINE__, (uint8_t *)__FILE__); \
}
#else
#define ASSERT(expr) //!< Assert empty when disabled
__WEAK void assert_nrf_callback(uint16_t line_num, const uint8_t *file_name);
#endif /* defined(DEBUG_NRF) || defined(DEBUG_NRF_USER) */
#endif /* NRF_ASSERT_H_ */

View File

@ -0,0 +1,425 @@
#include "nrf.h"
#include "nrf_log.h"
#include "nrf_error.h"
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#if defined(NRF_LOG_USES_RTT) && NRF_LOG_USES_RTT == 1
#include <SEGGER_RTT_Conf.h>
#include <SEGGER_RTT.h>
static char buf_normal_up[BUFFER_SIZE_UP];
static char buf_down[BUFFER_SIZE_DOWN];
uint32_t log_rtt_init(void)
{
static bool initialized = false;
if (initialized)
{
return NRF_SUCCESS;
}
if (SEGGER_RTT_ConfigUpBuffer(LOG_TERMINAL_NORMAL,
"Normal",
buf_normal_up,
BUFFER_SIZE_UP,
SEGGER_RTT_MODE_NO_BLOCK_TRIM
)
!= 0)
{
return NRF_ERROR_INVALID_STATE;
}
if (SEGGER_RTT_ConfigDownBuffer(LOG_TERMINAL_INPUT,
"Input",
buf_down,
BUFFER_SIZE_DOWN,
SEGGER_RTT_MODE_NO_BLOCK_SKIP
)
!= 0)
{
return NRF_ERROR_INVALID_STATE;
}
initialized = true;
return NRF_SUCCESS;
}
// Forward declaration of SEGGER RTT vprintf function
int SEGGER_RTT_vprintf(unsigned BufferIndex, const char * sFormat, va_list * pParamList);
void log_rtt_printf(int terminal_index, char * format_msg, ...)
{
//lint -save -e526 -e628 -e530
va_list p_args;
va_start(p_args, format_msg);
(void)SEGGER_RTT_vprintf(terminal_index, format_msg, &p_args);
va_end(p_args);
//lint -restore
}
__INLINE void log_rtt_write_string(int terminal_index, int num_args, ...)
{
const char* msg;
//lint -save -e516 -e530
va_list p_args;
va_start(p_args, num_args);
//lint -restore
for (int i = 0; i < num_args; i++)
{
//lint -save -e26 -e10 -e64 -e526 -e628 -e530
msg = va_arg(p_args, const char*);
//lint -restore
(void)SEGGER_RTT_WriteString(terminal_index, msg);
}
va_end(p_args);
}
void log_rtt_write_hex(int terminal_index, uint32_t value)
{
char temp[11];
temp[0] = '0';
temp[1] = 'x';
temp[10] = 0; // Null termination
uint8_t nibble;
uint8_t i = 8;
while(i-- != 0)
{
nibble = (value >> (4 * i)) & 0x0F;
temp[9-i] = (nibble > 9) ? ('A' + nibble - 10) : ('0' + nibble);
}
(void)SEGGER_RTT_WriteString(terminal_index, temp);
}
void log_rtt_write_hex_char(int terminal_index, uint8_t value)
{
char temp[3];
temp[2] = 0; // Null termination
uint8_t nibble;
uint8_t i = 2;
while(i-- != 0)
{
nibble = (value >> (4 * i)) & 0x0F;
temp[1-i] = (nibble > 9) ? ('A' + nibble - 10) : ('0' + nibble);
}
(void)SEGGER_RTT_WriteString(terminal_index, temp);
}
__INLINE int log_rtt_has_input()
{
return SEGGER_RTT_HasKey();
}
uint32_t log_rtt_read_input(char * c)
{
int r;
r = SEGGER_RTT_Read(LOG_TERMINAL_INPUT, c, 1);
if (r == 1)
return NRF_SUCCESS;
else
return NRF_ERROR_NULL;
}
#elif defined(NRF_LOG_USES_UART) && NRF_LOG_USES_UART == 1
#include "app_uart.h"
#include "app_error.h"
#include <stdio.h>
#include <string.h>
#include "nrf.h"
#include "bsp.h"
#define MAX_TEST_DATA_BYTES (15U) /**< max number of test bytes to be used for tx and rx. */
#define UART_TX_BUF_SIZE 512 /**< UART TX buffer size. */
#define UART_RX_BUF_SIZE 1 /**< UART RX buffer size. */
static uint8_t m_uart_data;
static bool m_uart_has_input;
void uart_error_cb(app_uart_evt_t * p_event)
{
if (p_event->evt_type == APP_UART_COMMUNICATION_ERROR)
{
APP_ERROR_HANDLER(p_event->data.error_communication);
}
else if (p_event->evt_type == APP_UART_FIFO_ERROR)
{
APP_ERROR_HANDLER(p_event->data.error_code);
}
}
uint32_t log_uart_init()
{
static bool initialized = false;
if (initialized)
{
return NRF_SUCCESS;
}
uint32_t err_code;
const app_uart_comm_params_t comm_params =
{
RX_PIN_NUMBER,
TX_PIN_NUMBER,
RTS_PIN_NUMBER,
CTS_PIN_NUMBER,
APP_UART_FLOW_CONTROL_ENABLED,
false,
UART_BAUDRATE_BAUDRATE_Baud115200
};
APP_UART_FIFO_INIT(&comm_params,
UART_RX_BUF_SIZE,
UART_TX_BUF_SIZE,
uart_error_cb,
APP_IRQ_PRIORITY_LOW,
err_code);
initialized = true;
return err_code;
}
//lint -save -e530 -e64
void log_uart_printf(const char * format_msg, ...)
{
va_list p_args;
va_start(p_args, format_msg);
(void)vprintf(format_msg, p_args);
va_end(p_args);
}
__INLINE void log_uart_write_string_many(int num_args, ...)
{
const char* msg;
va_list p_args;
va_start(p_args, num_args);
for (int i = 0; i < num_args; i++)
{
msg = va_arg(p_args, const char*);
log_uart_write_string(msg);
}
va_end(p_args);
}
__INLINE void log_uart_write_string(const char* msg)
{
while( *msg )
{
(void)app_uart_put(*msg++);
}
}
//lint -restore
void log_uart_write_hex(uint32_t value)
{
uint8_t nibble;
uint8_t i = 8;
(void)app_uart_put('0');
(void)app_uart_put('x');
while( i-- != 0 )
{
nibble = (value >> (4 * i)) & 0x0F;
(void)app_uart_put( (nibble > 9) ? ('A' + nibble - 10) : ('0' + nibble) );
}
}
void log_uart_write_hex_char(uint8_t c)
{
uint8_t nibble;
uint8_t i = 2;
while( i-- != 0 )
{
nibble = (c >> (4 * i)) & 0x0F;
(void)app_uart_put( (nibble > 9) ? ('A' + nibble - 10) : ('0' + nibble) );
}
}
__INLINE int log_uart_has_input()
{
if (m_uart_has_input) return 1;
if (app_uart_get(&m_uart_data) == NRF_SUCCESS)
{
m_uart_has_input = true;
return 1;
}
return 0;
}
uint32_t log_uart_read_input(char * c)
{
if (m_uart_has_input)
{
*c = (char)m_uart_data;
m_uart_has_input = false;
return NRF_SUCCESS;
}
if (app_uart_get((uint8_t *)c) == NRF_SUCCESS)
{
return NRF_SUCCESS;
}
return NRF_ERROR_NULL;
}
#elif defined(NRF_LOG_USES_RAW_UART) && NRF_LOG_USES_RAW_UART == 1
#include "app_uart.h"
#include <stdio.h>
#include <string.h>
#include "bsp.h"
uint32_t log_raw_uart_init()
{
// Disable UART
NRF_UART0->ENABLE = UART_ENABLE_ENABLE_Disabled;
// Configure RX/TX pins
nrf_gpio_cfg_output( TX_PIN_NUMBER );
nrf_gpio_cfg_input(RX_PIN_NUMBER, NRF_GPIO_PIN_NOPULL);
// Set a default baud rate of UART0_CONFIG_BAUDRATE
NRF_UART0->PSELTXD = TX_PIN_NUMBER;
NRF_UART0->BAUDRATE = UART0_CONFIG_BAUDRATE;
NRF_UART0->PSELRTS = 0xFFFFFFFF;
NRF_UART0->PSELCTS = 0xFFFFFFFF;
// Disable parity and interrupt
NRF_UART0->CONFIG = (UART_CONFIG_PARITY_Excluded << UART_CONFIG_PARITY_Pos );
NRF_UART0->CONFIG |= (UART_CONFIG_HWFC_Disabled << UART_CONFIG_HWFC_Pos );
// Re-enable the UART
NRF_UART0->ENABLE = UART_ENABLE_ENABLE_Enabled;
NRF_UART0->INTENSET = 0;
NRF_UART0->TASKS_STARTTX = 1;
NRF_UART0->TASKS_STARTRX = 1;
return NRF_SUCCESS;
}
void log_raw_uart_printf(const char * format_msg, ...)
{
static char buffer[256];
va_list p_args;
va_start(p_args, format_msg);
sprintf(buffer, format_msg, p_args);
va_end(p_args);
log_raw_uart_write_string(buffer);
}
__INLINE void log_raw_uart_write_char(const char c)
{
NRF_UART0->TXD = c;
while( NRF_UART0->EVENTS_TXDRDY != 1 );
NRF_UART0->EVENTS_TXDRDY = 0;
}
__INLINE void log_raw_uart_write_string_many(int num_args, ...)
{
const char* msg;
va_list p_args;
va_start(p_args, num_args);
for (int i = 0; i < num_args; i++)
{
msg = va_arg(p_args, const char*);
log_raw_uart_write_string(msg);
}
va_end(p_args);
}
__INLINE void log_raw_uart_write_string(const char* msg)
{
while( *msg )
{
NRF_UART0->TXD = *msg++;
while( NRF_UART0->EVENTS_TXDRDY != 1 );
NRF_UART0->EVENTS_TXDRDY = 0;
}
}
void log_raw_uart_write_hex(uint32_t value)
{
uint8_t nibble;
uint8_t i = 8;
log_raw_uart_write_string( "0x" );
while( i-- != 0 )
{
nibble = (value >> (4 * i)) & 0x0F;
log_raw_uart_write_char( (nibble > 9) ? ('A' + nibble - 10) : ('0' + nibble) );
}
}
void log_raw_uart_write_hex_char(uint8_t c)
{
uint8_t nibble;
uint8_t i = 2;
while( i-- != 0 )
{
nibble = (c >> (4 * i)) & 0x0F;
log_raw_uart_write_hex( (nibble > 9) ? ('A' + nibble - 10) : ('0' + nibble) );
}
}
__INLINE int log_raw_uart_has_input()
{
return 0;
}
uint32_t log_raw_uart_read_input(char * c)
{
return NRF_ERROR_NULL;
}
#endif // NRF_LOG_USES_RAW_UART == 1
const char* log_hex_char(const char c)
{
static volatile char hex_string[3];
hex_string[2] = 0; // Null termination
uint8_t nibble;
uint8_t i = 2;
while(i-- != 0)
{
nibble = (c >> (4 * i)) & 0x0F;
hex_string[1-i] = (nibble > 9) ? ('A' + nibble - 10) : ('0' + nibble);
}
return (const char*) hex_string;
}
const char* log_hex(uint32_t value)
{
static volatile char hex_string[11];
hex_string[0] = '0';
hex_string[1] = 'x';
hex_string[10] = 0;
uint8_t nibble;
uint8_t i = 8;
while(i-- != 0)
{
nibble = (value >> (4 * i)) & 0x0F;
hex_string[9-i] = (nibble > 9) ? ('A' + nibble - 10) : ('0' + nibble);
}
return (const char*)hex_string;
}

View File

@ -0,0 +1,699 @@
#ifndef NRF_LOG_H_
#define NRF_LOG_H_
#ifndef DOXYGEN
#include <stdint.h>
#include <stdarg.h>
#include <app_util.h>
#ifndef NRF_LOG_USES_RTT
#define NRF_LOG_USES_RTT 0
#endif
#ifndef NRF_LOG_USES_UART
#define NRF_LOG_USES_UART 0
#endif
#ifndef NRF_LOG_USES_RAW_UART
#define NRF_LOG_USES_RAW_UART 0
#endif
#ifndef NRF_LOG_USES_COLORS
#define NRF_LOG_USES_COLORS 1
#endif
#if NRF_LOG_USES_COLORS == 1
#define NRF_LOG_COLOR_DEFAULT "\x1B[0m"
#define NRF_LOG_COLOR_BLACK "\x1B[1;30m"
#define NRF_LOG_COLOR_RED "\x1B[1;31m"
#define NRF_LOG_COLOR_GREEN "\x1B[1;32m"
#define NRF_LOG_COLOR_YELLOW "\x1B[1;33m"
#define NRF_LOG_COLOR_BLUE "\x1B[1;34m"
#define NRF_LOG_COLOR_MAGENTA "\x1B[1;35m"
#define NRF_LOG_COLOR_CYAN "\x1B[1;36m"
#define NRF_LOG_COLOR_WHITE "\x1B[1;37m"
#else
#define NRF_LOG_COLOR_DEFAULT
#define NRF_LOG_COLOR_BLACK
#define NRF_LOG_COLOR_RED
#define NRF_LOG_COLOR_GREEN
#define NRF_LOG_COLOR_YELLOW
#define NRF_LOG_COLOR_BLUE
#define NRF_LOG_COLOR_MAGENTA
#define NRF_LOG_COLOR_CYAN
#define NRF_LOG_COLOR_WHITE
#endif
#if defined(NRF_LOG_USES_RTT) && NRF_LOG_USES_RTT == 1
#define LOG_TERMINAL_NORMAL (0)
#define LOG_TERMINAL_ERROR (1)
#define LOG_TERMINAL_INPUT (0)
/**@brief Function for initializing the SEGGER RTT logger.
*
* @details See <a href="https://www.segger.com/jlink-rtt.html" target="_blank">segger.com</a>
* for information about SEGGER Real Time Transfer (RTT).
*
* This function is available only when NRF_LOG_USES_RTT is defined as 1.
*
* @note Do not call this function directly. Use the macro @ref NRF_LOG_INIT instead.
*
* @retval NRF_SUCCESS If initialization was successful.
* @retval NRF_ERROR Otherwise.
*/
uint32_t log_rtt_init(void);
/**@brief Function for writing a printf string using RTT.
*
* @details The printf implementation in SEGGER's RTT is more efficient than
* the standard implementation. However, printf requires more processor time
* than other logging functions. Therefore, applications that require logging
* but need it to interfere as little as possible with the execution, should
* avoid using printf.
*
* This function is available only when NRF_LOG_USES_RTT is defined as 1.
*
* @note Do not call this function directly. Use one of the following macros instead:
* - @ref NRF_LOG_PRINTF
* - @ref NRF_LOG_PRINTF_DEBUG
* - @ref NRF_LOG_PRINTF_ERROR
*
* @param terminal_index Segger RTT terminal index to use as output.
* @param format_msg Printf format string.
*/
void log_rtt_printf(int terminal_index, char * format_msg, ...);
/**@brief Function for writing a string using RTT.
*
* @details The string to write must be null-terminated, but the null termination will not be stored
* in the ring buffer.
* The impact of running this function should be very low compared to writing to UART.
*
* This function is available only when NRF_LOG_USES_RTT is defined as 1.
*
* @note Do not call this function directly. Use one of the following macros instead:
* - @ref NRF_LOG
* - @ref NRF_LOG_DEBUG
* - @ref NRF_LOG_ERROR
*
* @param terminal_index Segger RTT terminal index to use as output.
* @param num_args Number of arguments.
*/
void log_rtt_write_string(int terminal_index, int num_args, ...);
/**@brief Function for writing an integer as HEX using RTT.
*
* The output data is formatted as, for example, 0x89ABCDEF.
*
* This function is available only when NRF_LOG_USES_RTT is defined as 1.
*
* @note Do not call this function directly. Use one of the following macros instead:
* - @ref NRF_LOG_HEX
* - @ref NRF_LOG_HEX_DEBUG
* - @ref NRF_LOG_HEX_ERROR
*
* @param terminal_index Segger RTT terminal index to use as output.
* @param value Integer value to be printed as HEX.
*/
void log_rtt_write_hex(int terminal_index, uint32_t value);
/**@brief Function for writing a single character as HEX using RTT.
*
* The output string is formatted as, for example, AA.
*
* This function is available only when NRF_LOG_USES_RTT is defined as 1.
*
* @note Do not call this function directly. Use one of the following macros instead:
* - @ref NRF_LOG_HEX_CHAR
* - @ref NRF_LOG_HEX_CHAR_DEBUG
* - @ref NRF_LOG_HEX_CHAR_ERROR
*
* @param terminal_index Segger RTT terminal index to use as output.
* @param value Character to print as HEX.
*/
void log_rtt_write_hex_char(int terminal_index, uint8_t value);
/**@brief Function for checking if data is available in the input buffer.
*
* This function is available only when NRF_LOG_USES_RTT is defined as 1.
*
* @note Do not call this function directly. Use @ref NRF_LOG_HAS_INPUT instead.
*
* @retval 1 If characters are available to read.
* @retval 0 If no characters are available.
*/
int log_rtt_has_input(void);
/**@brief Function for reading one character from the input buffer.
*
* @param[out] p_char Pointer where to store the character.
*
* This function is available only when NRF_LOG_USES_RTT is defined as 1.
*
* @note Do not call this function directly. Use @ref NRF_LOG_READ_INPUT instead.
*
* @retval NRF_SUCCESS If the character was read out.
* @retval NRF_ERROR_INVALID_DATA If no character could be read.
*/
uint32_t log_rtt_read_input(char* p_char);
#define NRF_LOG_INIT() log_rtt_init() /*!< Initialize the module. */
#define NRF_LOG_PRINTF(...) log_rtt_printf(LOG_TERMINAL_NORMAL, ##__VA_ARGS__) /*!< Print a log message using printf. */
#define NRF_LOG_PRINTF_DEBUG(...) log_rtt_printf(LOG_TERMINAL_NORMAL, ##__VA_ARGS__) /*!< If DEBUG is set, print a log message using printf. */
#define NRF_LOG_PRINTF_ERROR(...) log_rtt_printf(LOG_TERMINAL_ERROR, ##__VA_ARGS__) /*!< Print a log message using printf to the error stream. */
#define NRF_LOG(...) log_rtt_write_string(LOG_TERMINAL_NORMAL, NUM_VA_ARGS(__VA_ARGS__), ##__VA_ARGS__) /*!< Print a log message. The input string must be null-terminated. */
#define NRF_LOG_DEBUG(...) log_rtt_write_string(LOG_TERMINAL_NORMAL, NUM_VA_ARGS(__VA_ARGS__), ##__VA_ARGS__) /*!< If DEBUG is set, print a log message. The input string must be null-terminated. */
#define NRF_LOG_ERROR(...) log_rtt_write_string(LOG_TERMINAL_ERROR, NUM_VA_ARGS(__VA_ARGS__), ##__VA_ARGS__) /*!< Print a log message to the error stream. The input string must be null-terminated. */
#define NRF_LOG_HEX(val) log_rtt_write_hex(LOG_TERMINAL_NORMAL, val) /*!< Log an integer as HEX value (example output: 0x89ABCDEF). */
#define NRF_LOG_HEX_DEBUG(val) log_rtt_write_hex(LOG_TERMINAL_NORMAL, val) /*!< If DEBUG is set, log an integer as HEX value (example output: 0x89ABCDEF). */
#define NRF_LOG_HEX_ERROR(val) log_rtt_write_hex(LOG_TERMINAL_ERROR, val) /*!< Log an integer as HEX value to the error stream (example output: 0x89ABCDEF). */
#define NRF_LOG_HEX_CHAR(val) log_rtt_write_hex_char(LOG_TERMINAL_NORMAL, val) /*!< Log a character as HEX value (example output: AA). */
#define NRF_LOG_HEX_CHAR_DEBUG(val) log_rtt_write_hex_char(LOG_TERMINAL_NORMAL, val) /*!< If DEBUG is set, log a character as HEX value (example output: AA). */
#define NRF_LOG_HEX_CHAR_ERROR(val) log_rtt_write_hex_char(LOG_TERMINAL_ERROR, val) /*!< Log a character as HEX value to the error stream (example output: AA). */
#define NRF_LOG_HAS_INPUT() log_rtt_has_input() /*!< Check if the input buffer has unconsumed characters. */
#define NRF_LOG_READ_INPUT(p_char) log_rtt_read_input(p_char) /*!< Consume a character from the input buffer. */
#if !defined(DEBUG) && !defined(DOXYGEN)
#undef NRF_LOG_DEBUG
#define NRF_LOG_DEBUG(...)
#undef NRF_LOG_STR_DEBUG
#define NRF_LOG_STR_DEBUG(...)
#undef NRF_LOG_HEX_DEBUG
#define NRF_LOG_HEX_DEBUG(...)
#undef NRF_LOG_HEX_CHAR_DEBUG
#define NRF_LOG_HEX_CHAR_DEBUG(...)
#endif // !defined(DEBUG) && !defined(DOXYGEN)
#elif defined(NRF_LOG_USES_UART) && NRF_LOG_USES_UART == 1
/**@brief Function for initializing the UART logger.
*
* This function is available only when NRF_LOG_USES_UART is defined as 1.
*
* @note Do not call this function directly. Use the macro @ref NRF_LOG_INIT instead.
*
* @retval NRF_SUCCESS If initialization was successful.
* @retval NRF_ERROR Otherwise.
*/
uint32_t log_uart_init(void);
/**@brief Function for logging a printf string to UART.
*
* @details Printf requires more processor time
* than other logging functions. Therefore, applications that require logging
* but need it to interfere as little as possible with the execution, should
* avoid using printf.
*
* This function is available only when NRF_LOG_USES_UART is defined as 1.
*
* @note This function is non-blocking. If too much data is sent to the UART,
* some characters might be skipped.
*
* @note Do not call this function directly. Use one of the following macros instead:
* - @ref NRF_LOG_PRINTF
* - @ref NRF_LOG_PRINTF_DEBUG
* - @ref NRF_LOG_PRINTF_ERROR
*
* @param format_msg Printf format string.
*/
void log_uart_printf(const char * format_msg, ...);
/**@brief Function for logging a single character to UART.
*
* This function is available only when NRF_LOG_USES_UART is defined as 1.
*
* @param c Character.
*/
void log_uart_write_char(const char c);
/**@brief Function for logging null-terminated strings to UART.
*
* @details This function is more efficient than using printf.
* The null termination will not be logged.
*
* This function is available only when NRF_LOG_USES_UART is defined as 1.
*
* @note Do not call this function directly. Use one of the following macros instead:
* - @ref NRF_LOG
* - @ref NRF_LOG_DEBUG
* - @ref NRF_LOG_ERROR
*
* @param num_args Number of arguments.
*/
void log_uart_write_string_many(int num_args, ...);
/**@brief Function for logging a null-terminated string to UART.
*
* @details This function is more efficient than using printf.
* The null termination will not be logged.
*
* This function is available only when NRF_LOG_USES_UART is defined as 1.
*
* @note Do not call this function directly. Use one of the following macros instead:
* - @ref NRF_LOG
* - @ref NRF_LOG_DEBUG
* - @ref NRF_LOG_ERROR
*
* @param msg Null-terminated string.
*/
void log_uart_write_string(const char* msg);
/**@brief Function for logging an integer value as HEX to UART.
*
* @details The output data is formatted as, for example, 0x89ABCDEF.
* This function is more efficient than printf.
*
* This function is available only when NRF_LOG_USES_UART is defined as 1.
*
* @note This function is non-blocking. If too much data is sent to the UART,
* some characters might be skipped.
*
* @note Do not call this function directly. Use one of the following macros instead:
* - @ref NRF_LOG_HEX
* - @ref NRF_LOG_HEX_DEBUG
* - @ref NRF_LOG_HEX_ERROR
*
* @param value Integer value to be printed as HEX.
*/
void log_uart_write_hex(uint32_t value);
/**@brief Function for logging a single character as HEX to UART.
*
* @details The output string is formatted as, for example, AA.
*
* This function is available only when NRF_LOG_USES_UART is defined as 1.
*
* @note This function is non-blocking. If too much data is sent to the UART,
* some characters might be skipped.
*
* @note Do not call this function directly. Use one of the following macros instead:
* - @ref NRF_LOG_HEX_CHAR
* - @ref NRF_LOG_HEX_CHAR_DEBUG
* - @ref NRF_LOG_HEX_CHAR_ERROR
*
* @param c Character.
*/
void log_uart_write_hex_char(uint8_t c);
/**@brief Function for checking if data is available in the input buffer.
*
* This function is available only when NRF_LOG_USES_UART is defined as 1.
*
* @note Do not call this function directly. Use @ref NRF_LOG_HAS_INPUT instead.
*
* @retval 1 If characters are available to read.
* @retval 0 If no characters are available.
*/
int log_uart_has_input(void);
/**@brief Function for reading one character from the input buffer.
*
* @param[out] p_char Pointer where to store the character.
*
* This function is available only when NRF_LOG_USES_UART is defined as 1.
*
* @note Do not call this function directly. Use NRF_LOG_READ_INPUT instead.
*
* @retval NRF_SUCCESS If the character was read out.
* @retval NRF_ERROR_INVALID_DATA If no character could be read.
*/
uint32_t log_uart_read_input(char* p_char);
#define NRF_LOG_INIT() log_uart_init() /*!< Initialize the module. */
#define NRF_LOG_PRINTF(...) log_uart_printf(__VA_ARGS__) /*!< Print a log message using printf. */
#define NRF_LOG_PRINTF_DEBUG(...) log_uart_printf(__VA_ARGS__) /*!< If DEBUG is set, print a log message using printf. */
#define NRF_LOG_PRINTF_ERROR(...) log_uart_printf(__VA_ARGS__) /*!< Print a log message using printf to the error stream. */
#define NRF_LOG(...) log_uart_write_string_many(NUM_VA_ARGS(__VA_ARGS__), ##__VA_ARGS__) /*!< Print a log message. The input string must be null-terminated. */
#define NRF_LOG_DEBUG(...) log_uart_write_string_many(NUM_VA_ARGS(__VA_ARGS__), ##__VA_ARGS__) /*!< If DEBUG is set, print a log message. The input string must be null-terminated. */
#define NRF_LOG_ERROR(...) log_uart_write_string_many(NUM_VA_ARGS(__VA_ARGS__), ##__VA_ARGS__) /*!< Print a log message to the error stream. The input string must be null-terminated. */
#define NRF_LOG_HEX(val) log_uart_write_hex(val) /*!< Log an integer as HEX value (example output: 0x89ABCDEF). */
#define NRF_LOG_HEX_DEBUG(val) log_uart_write_hex(val) /*!< If DEBUG is set, log an integer as HEX value (example output: 0x89ABCDEF). */
#define NRF_LOG_HEX_ERROR(val) log_uart_write_hex(val) /*!< Log an integer as HEX value to the error stream (example output: 0x89ABCDEF). */
#define NRF_LOG_HEX_CHAR(val) log_uart_write_hex_char(val) /*!< Log a character as HEX value (example output: AA). */
#define NRF_LOG_HEX_CHAR_DEBUG(val) log_uart_write_hex_char(val) /*!< If DEBUG is set, log a character as HEX value (example output: AA). */
#define NRF_LOG_HEX_CHAR_ERROR(val) log_uart_write_hex_char(val) /*!< Log a character as HEX value to the error stream (example output: AA). */
#define NRF_LOG_HAS_INPUT() log_uart_has_input() /*!< Check if the input buffer has unconsumed characters. */
#define NRF_LOG_READ_INPUT(p_char) log_uart_read_input(p_char) /*!< Consume a character from the input buffer. */
#if !defined(DEBUG) && !defined(DOXYGEN)
#undef NRF_LOG_DEBUG
#define NRF_LOG_DEBUG(...)
#undef NRF_LOG_PRINTF_DEBUG
#define NRF_LOG_PRINTF_DEBUG(...)
#undef NRF_LOG_STR_DEBUG
#define NRF_LOG_STR_DEBUG(...)
#undef NRF_LOG_HEX_DEBUG
#define NRF_LOG_HEX_DEBUG(...)
#undef NRF_LOG_HEX_CHAR_DEBUG
#define NRF_LOG_HEX_CHAR_DEBUG(...)
#endif // !defined(DEBUG) && !defined(DOXYGEN)
#elif defined(NRF_LOG_USES_RAW_UART) && NRF_LOG_USES_RAW_UART == 1
/**@brief Function for initializing the raw UART logger.
*
* This function is available only when NRF_LOG_USES_RAW_UART is defined as 1.
*
* @note Do not call this function directly. Use the macro @ref NRF_LOG_INIT instead.
*
* @retval NRF_SUCCESS If initialization was successful.
* @retval NRF_ERROR Otherwise.
*/
uint32_t log_raw_uart_init(void);
/**@brief Function for logging a printf string to raw UART.
*
* @details Printf requires more processor time
* than other logging functions. Therefore, applications that require logging
* but need it to interfere as little as possible with the execution, should
* avoid using printf.
*
* This function is available only when NRF_LOG_USES_RAW_UART is defined as 1.
*
* @note This function is non-blocking. If too much data is sent to the UART,
* some characters might be skipped.
*
* @note Do not call this function directly. Use one of the following macros instead:
* - @ref NRF_LOG_PRINTF
* - @ref NRF_LOG_PRINTF_DEBUG
* - @ref NRF_LOG_PRINTF_ERROR
*
* @param format_msg Printf format string.
*/
void log_raw_uart_printf(const char * format_msg, ...);
/**@brief Function for logging a single character to raw UART.
*
* This function is available only when NRF_LOG_USES_RAW_UART is defined as 1.
*
* @param c Character.
*/
void log_raw_uart_write_char(const char c);
/**@brief Function for logging null-terminated strings to raw UART.
*
* @details This function is more efficient than using printf.
* The null termination will not be logged.
*
* This function is available only when NRF_LOG_USES_RAW_UART is defined as 1.
*
* @note Do not call this function directly. Use one of the following macros instead:
* - @ref NRF_LOG
* - @ref NRF_LOG_DEBUG
* - @ref NRF_LOG_ERROR
*
* @param num_args Number of arguments.
*/
void log_raw_uart_write_string_many(int num_args, ...);
/**@brief Function for logging a null-terminated string to raw UART.
*
* @details This function is more efficient than using printf.
* The null termination will not be logged.
*
* This function is available only when NRF_LOG_USES_RAW_UART is defined as 1.
*
* @note Do not call this function directly. Use one of the following macros instead:
* - @ref NRF_LOG
* - @ref NRF_LOG_DEBUG
* - @ref NRF_LOG_ERROR
*
* @param str Null-terminated string.
*/
void log_raw_uart_write_string(const char * str);
/**@brief Function for logging an integer value as HEX to raw UART.
*
* @details The output data is formatted as, for example, 0x89ABCDEF.
* This function is more efficient than printf.
*
* This function is available only when NRF_LOG_USES_RAW_UART is defined as 1.
*
* @note This function is non-blocking. If too much data is sent to the UART,
* some characters might be skipped.
*
* @note Do not call this function directly. Use one of the following macros instead:
* - @ref NRF_LOG_HEX
* - @ref NRF_LOG_HEX_DEBUG
* - @ref NRF_LOG_HEX_ERROR
*
* @param value Integer value to be printed as HEX.
*/
void log_raw_uart_write_hex(uint32_t value);
/**@brief Function for logging a single character as HEX to raw UART.
*
* @details The output string is formatted as, for example, AA.
*
* This function is available only when NRF_LOG_USES_RAW_UART is defined as 1.
*
* @note This function is non-blocking. If too much data is sent to the UART,
* some characters might be skipped.
*
* @note Do not call this function directly. Use one of the following macros instead:
* - @ref NRF_LOG_HEX_CHAR
* - @ref NRF_LOG_HEX_CHAR_DEBUG
* - @ref NRF_LOG_HEX_CHAR_ERROR
*
* @param c Character.
*/
void log_raw_uart_write_hex_char(uint8_t c);
/**@brief Function for checking if data is available in the input buffer.
*
* This function is available only when NRF_LOG_USES_RAW_UART is defined as 1.
*
* @note Do not call this function directly. Use @ref NRF_LOG_HAS_INPUT instead.
*
* @retval 1 If characters are available to read.
* @retval 0 If no characters are available.
*/
int log_raw_uart_has_input(void);
/**@brief Function for reading one character from the input buffer.
*
* @param[out] p_char Pointer where to store the character.
*
* This function is available only when NRF_LOG_USES_RAW_UART is defined as 1.
*
* @note Do not call this function directly. Use NRF_LOG_READ_INPUT instead.
*
* @retval NRF_SUCCESS If the character was read out.
* @retval NRF_ERROR_INVALID_DATA If no character could be read.
*/
uint32_t log_raw_uart_read_input(char* p_char);
#define NRF_LOG_INIT() log_raw_uart_init() /*!< nitialize the module. */
#define NRF_LOG_PRINTF(...) log_raw_uart_printf(__VA_ARGS__) /*!< Print a log message using printf. */
#define NRF_LOG_PRINTF_DEBUG(...) log_raw_uart_printf(__VA_ARGS__) /*!< If DEBUG is set, print a log message using printf. */
#define NRF_LOG_PRINTF_ERROR(...) log_raw_uart_printf(__VA_ARGS__) /*!< Print a log message using printf to the error stream. */
#define NRF_LOG(...) log_raw_uart_write_string_many(NUM_VA_ARGS(__VA_ARGS__), ##__VA_ARGS__) /*!< Print a log message. The input string must be null-terminated. */
#define NRF_LOG_DEBUG(...) log_raw_uart_write_string_many(NUM_VA_ARGS(__VA_ARGS__), ##__VA_ARGS__) /*!< If DEBUG is set, print a log message. The input string must be null-terminated. */
#define NRF_LOG_ERROR(...) log_raw_uart_write_string_many(NUM_VA_ARGS(__VA_ARGS__), ##__VA_ARGS__) /*!< Print a log message to the error stream. The input string must be null-terminated. */
#define NRF_LOG_HEX(val) log_raw_uart_write_hex(val) /*!< Log an integer as HEX value (example output: 0x89ABCDEF). */
#define NRF_LOG_HEX_DEBUG(val) log_raw_uart_write_hex(val) /*!< If DEBUG is set, log an integer as HEX value (example output: 0x89ABCDEF). */
#define NRF_LOG_HEX_ERROR(val) log_raw_uart_write_hex(val) /*!< Log an integer as HEX value to the error stream (example output: 0x89ABCDEF). */
#define NRF_LOG_HEX_CHAR(val) log_raw_uart_write_hex_char(val) /*!< Log a character as HEX value (example output: AA). */
#define NRF_LOG_HEX_CHAR_DEBUG(val) log_raw_uart_write_hex_char(val) /*!< If DEBUG is set, log a character as HEX value (example output: AA). */
#define NRF_LOG_HEX_CHAR_ERROR(val) log_raw_uart_write_hex_char(val) /*!< Log a character as HEX value to the error stream (example output: AA). */
#define NRF_LOG_HAS_INPUT() log_raw_uart_has_input() /*!< Check if the input buffer has unconsumed characters. */
#define NRF_LOG_READ_INPUT(p_char) log_raw_uart_read_input(p_char) /*!< Consume a character from the input buffer. */
#if !defined(DEBUG) && !defined(DOXYGEN)
#undef NRF_LOG_DEBUG
#define NRF_LOG_DEBUG(...)
#undef NRF_LOG_PRINTF_DEBUG
#define NRF_LOG_PRINTF_DEBUG(...)
#undef NRF_LOG_STR_DEBUG
#define NRF_LOG_STR_DEBUG(...)
#undef NRF_LOG_HEX_DEBUG
#define NRF_LOG_HEX_DEBUG(...)
#undef NRF_LOG_HEX_CHAR_DEBUG
#define NRF_LOG_HEX_CHAR_DEBUG(...)
#endif // !defined(DEBUG) && !defined(DOXYGEN)
#else
#include "nrf_error.h"
#include "nordic_common.h"
// Empty definitions
#define NRF_LOG_INIT() NRF_SUCCESS
#define NRF_LOG(...)
#define NRF_LOG_DEBUG(...)
#define NRF_LOG_ERROR(...)
#define NRF_LOG_PRINTF(...)
#define NRF_LOG_PRINTF_DEBUG(...)
#define NRF_LOG_PRINTF_ERROR(...)
#define NRF_LOG_HEX(val)
#define NRF_LOG_HEX_DEBUG(val)
#define NRF_LOG_HEX_ERROR(val)
#define NRF_LOG_HEX_CHAR(val)
#define NRF_LOG_HEX_CHAR_DEBUG(val)
#define NRF_LOG_HEX_CHAR_ERROR(val)
#define NRF_LOG_HAS_INPUT() 0
#define NRF_LOG_READ_INPUT(ignore) NRF_SUCCESS
#endif
/**@brief Function for writing HEX values.
*
* @note This function not thread-safe. It is written for convenience.
* If you log from different application contexts, you might get different results.
*
* @retval NULL By default.
*/
const char* log_hex(uint32_t value);
/**@brief Function for writing HEX characters.
*
* @note This function not thread-safe. It is written for convenience.
* If you log from different application contexts, you might get different results.
*
* @retval NULL By default.
*/
const char* log_hex_char(const char value);
#else // DOXYGEN
/** @defgroup nrf_log UART/RTT logging
* @{
* @ingroup app_common
*
* @brief Library to output logging information over SEGGER's Real Time Transfer
* (RTT), UART, or raw UART.
*
* This library provides macros that call the respective functions depending on
* which protocol is used. Define LOG_USES_RTT=1 to enable logging over RTT,
* NRF_LOG_USES_UART=1 to enable logging over UART, or NRF_LOG_USES_RAW_UART=1
* to enable logging over raw UART. One of these defines must be set for any of
* the macros to have effect. If you choose to not output information, all
* logging macros can be left in the code without any cost; they will just be
* ignored.
*/
/**@brief Macro for initializing the logger.
*
* @retval NRF_SUCCESS If initialization was successful.
* @retval NRF_ERROR Otherwise.
*/
uint32_t NRF_LOG_INIT(void);
/**@brief Macro for logging null-terminated strings.
*
* @details This function is more efficient than using printf.
* The null termination will not be logged.
*
* @param msg Null-terminated string.
*/
void NRF_LOG(const char* msg);
/**@brief Macro for logging a printf string.
*
* @details Printf requires more processor time
* than other logging functions. Therefore, applications that require logging
* but need it to interfere as little as possible with the execution, should
* avoid using printf.
*
* @note When NRF_LOG_USES_UART is set to 1, this macro is non-blocking.
* If too much data is sent, some characters might be skipped.
*
* @param format_msg Printf format string.
* @param ... Additional arguments replacing format specifiers in format_msg.
*/
void NRF_LOG_PRINTF(const char * format_msg, ...);
/**@brief Macro for logging an integer value as HEX.
*
* @details The output data is formatted as, for example, 0x89ABCDEF.
* This function is more efficient than printf.
*
* @note When NRF_LOG_USES_UART is set to 1, this macro is non-blocking.
* If too much data is sent, some characters might be skipped.
*
* @param value Integer value to be printed as HEX.
*/
void NRF_LOG_HEX(uint32_t value);
/**@brief Macro for logging a single character as HEX.
*
* @details The output string is formatted as, for example, AA.
*
* @note When NRF_LOG_USES_UART is set to 1, this macro is non-blocking.
* If too much data is sent, some characters might be skipped.
*
* @param c Character.
*/
void NRF_LOG_HEX_CHAR(uint8_t c);
/**@brief Macro for checking if data is available in the input buffer.
*
* @note When NRF_LOG_USES_UART is set to 1, this macro is non-blocking.
* If too much data is sent, some characters might be skipped.
*
* @retval 1 If characters are available to read.
* @retval 0 If no characters are available.
*/
int NRF_LOG_HAS_INPUT(void);
/**@brief Macro for reading one character from the input buffer.
*
* @param[out] p_char Pointer where to store the character.
*
* @retval NRF_SUCCESS If the character was read out.
* @retval NRF_ERROR_INVALID_DATA If no character could be read.
*/
uint32_t NRF_LOG_READ_INPUT(char* p_char);
/** @} */
#endif // DOXYGEN
#endif // NRF_LOG_H_

View File

@ -0,0 +1,174 @@
/* 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.
*
*/
/** @cond */
/**@file
*
* @ingroup experimental_api
* @defgroup sdk_common SDK Common Header
* @breif All common headers needed for SDK examples will be included here so that application
* developer does not have to include headers on him/herself.
* @{
*/
#ifndef SDK_COMMON_H__
#define SDK_COMMON_H__
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "nordic_common.h"
#include "compiler_abstraction.h"
#include "sdk_os.h"
#include "sdk_errors.h"
#include "app_util.h"
/**@brief Macro for verifying that the module is initialized. It will cause the function to return
* if not.
*
* @param[in] param The variable to check if is NULL.
*/
#ifndef DISABLE_PARAM_CHECK
#define VERIFY_PARAM_NOT_NULL(param) \
do \
{ \
if (param == NULL) \
{ \
return NRF_ERROR_NULL; \
} \
} while(0)
#else
#define VERIFY_PARAM_NOT_NULL()
#endif /* DISABLE_PARAM_CHECK */
/**@brief Macro for verifying that the module is initialized. It will cause the function to return
* if not.
*
* @param[in] param The variable to check if is NULL.
*/
#ifndef DISABLE_PARAM_CHECK
#define VERIFY_PARAM_NOT_NULL_VOID(param) \
do \
{ \
if (param == NULL) \
{ \
return; \
} \
} while(0)
#else
#define VERIFY_PARAM_NOT_NULL_VOID()
#endif /* DISABLE_PARAM_CHECK */
/**@brief Macro for verifying that a function returned NRF_SUCCESS. Will return the err code
* if not.
*
* @param[in] err_code The error code to check.
*/
#ifndef DISABLE_PARAM_CHECK
#define VERIFY_SUCCESS(err_code) \
do \
{ \
if (err_code != NRF_SUCCESS) \
{ \
return err_code; \
} \
} while(0)
#else
#define VERIFY_SUCCESS()
#endif /* DISABLE_PARAM_CHECK */
/**@brief Macro for verifying that a function returned NRF_SUCCESS. Will return if not.
*
* @param[in] err_code The error code to check.
*/
#ifndef DISABLE_PARAM_CHECK
#define VERIFY_SUCCESS_VOID(err_code) \
do \
{ \
if (err_code != NRF_SUCCESS) \
{ \
return; \
} \
} while(0)
#else
#define VERIFY_SUCCESS_VOID()
#endif /* DISABLE_PARAM_CHECK */
/**@brief Macro for verifying statement to be true. Will return err_code if not.
*
* @param[in] statement Statement to test.
* @param[in] err_code Error value to return if test was invalid.
*
* @retval err_code if test fails.
*/
#define VERIFY_TRUE(statement, err_code) \
do \
{ \
if (!(statement)) \
{ \
return err_code; \
} \
} while(0)
/**@brief Macro for verifying statement to be true. Will return if not.
*
* @param[in] statement Statement to test.
*/
#define VERIFY_TRUE_VOID(statement) \
do \
{ \
if (!(statement)) \
{ \
return; \
} \
} while(0)
/**@brief Macro for verifying statement to be false. Will return err_code if not.
*
* @param[in] statement Statement to test.
* @param[in] err_code Error value to return if test was invalid.
*
* @retval err_code if test fails.
*/
#define VERIFY_FALSE(statement, err_code) \
do \
{ \
if ((statement)) \
{ \
return err_code; \
} \
} while(0)
/**@brief Macro for verifying statement to be false. Will return if not.
*
* @param[in] statement Statement to test.
*/
#define VERIFY_FALSE_VOID(statement) \
do \
{ \
if ((statement)) \
{ \
return; \
} \
} while(0)
/** @} */
/** @endcond */
#endif // SDK_COMMON_H__

View File

@ -0,0 +1,115 @@
/* 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.
*
*/
/**@file
*
* @defgroup sdk_error SDK Error codes
* @{
* @ingroup app_common
* @{
* @details Error codes are 32-bit unsigned integers with the most significant 16-bit reserved for
* identifying the module where the error occurred while the least least significant LSB
* are used to provide the cause or nature of error. Each module is assigned a 16-bit
* unsigned integer. Which it will use to identify all errors that occurred in it. 16-bit
* LSB range is with module id as the MSB in the 32-bit error code is reserved for the
* module. As an example, if 0x8800 identifies a certain SDK module, all values from
* 0x88000000 - 0x8800FFFF are reserved for this module.
* It should be noted that common error reasons have been assigned values to make it
* possible to decode error reason easily. As an example, lets module uninitialized has
* been assigned an error code 0x000A0. Then, if application encounters an error code
* 0xZZZZ00A0, it knows that it accessing a certain module without initializing it.
* Apart from this, each module is allowed to define error codes that are not covered by
* the common ones, however, these values are defined in a range that does not conflict
* with common error values. For module, specific error however, it is possible that the
* same error value is used by two different modules to indicated errors of very different
* nature. If error is already defined by the NRF common error codes, these are reused.
* A range is reserved for application as well, it can use this range for defining
* application specific errors.
*
* @note Success code, NRF_SUCCESS, does not include any module identifier.
*/
#ifndef SDK_ERRORS_H__
#define SDK_ERRORS_H__
#include <stdint.h>
#include "nrf_error.h"
/**
* @defgroup sdk_err_base Base defined for SDK Modules
* @{
*/
#define SDK_ERROR_BASE (NRF_ERROR_BASE_NUM + 0x8000) /**< Base value defined for SDK module identifiers. */
#define SDK_COMMON_ERROR_BASE (NRF_ERROR_BASE_NUM + 0x0080) /**< Base error value to be used for SDK error values. */
/* @} */
/**
* @defgroup sdk_module_codes Codes reserved as identification for module where the error occurred.
* @{
*/
#define DEVICE_MANAGER_ERR_BASE (0x8000)
#define MEMORY_MANAGER_ERR_BASE (0x8100)
/* @} */
/**
* @defgroup sdk_iot_errors Codes reserved as identification for IoT errors.
* @{
*/
#define IOT_ERR_BASE_START (0xA000)
#define IOT_ERR_BASE_STOP (0xAFFF)
/* @} */
/**
* @defgroup sdk_common_errors Codes reserved as identification for common errors.
* @{
*/
#define MODULE_NOT_INITIALZED (SDK_COMMON_ERROR_BASE + 0x0000)
#define MUTEX_INIT_FAILED (SDK_COMMON_ERROR_BASE + 0x0001)
#define MUTEX_LOCK_FAILED (SDK_COMMON_ERROR_BASE + 0x0002)
#define MUTEX_UNLOCK_FAILED (SDK_COMMON_ERROR_BASE + 0x0003)
#define MUTEX_COND_INIT_FAILED (SDK_COMMON_ERROR_BASE + 0x0004)
#define MODULE_ALREADY_INITIALIZED (SDK_COMMON_ERROR_BASE + 0x0005)
#define API_NOT_IMPLEMENTED (SDK_COMMON_ERROR_BASE + 0x0010)
#define FEATURE_NOT_ENABLED (SDK_COMMON_ERROR_BASE + 0x0011)
/* @} */
/**
* @defgroup dm_specific_errors Error / status codes specific to device manager.
* @{
*/
#define DM_NO_APP_CONTEXT (DEVICE_MANAGER_ERR_BASE + 0x0040)
#define DM_SERVICE_CONTEXT_NOT_APPLIED (DEVICE_MANAGER_ERR_BASE + 0x0041)
#define DM_CONTEXT_INFO_LOST (DEVICE_MANAGER_ERR_BASE + 0x0042)
#define DM_DEVICE_CONTEXT_FULL (DEVICE_MANAGER_ERR_BASE + 0x0043)
/* @} */
/**
* @brief API Result.
*
* @details Indicates success or failure of an API procedure. In case of failure, a comprehensive
* error code indicating cause or reason for failure is provided.
*
* Though called an API result, it could used in Asynchronous notifications callback along
* with asynchronous callback as event result. This mechanism is employed when an event
* marks the end of procedure initiated using API. API result, in this case, will only be
* an indicative of whether the procedure has been requested successfully.
*/
typedef uint32_t ret_code_t;
/** @} */
/** @} */
#endif // SDK_ERRORS_H__

View File

@ -0,0 +1,72 @@
/* 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.
*
*/
/** @cond */
/**@file
*
* @ingroup sdk_util
* @defgroup sdk_common_macros SDK Common Header
* @breif Macros for parameter checking and similar tasks
* @{
*/
#ifndef SDK_MACROS_H__
#define SDK_MACROS_H__
/**@brief Macro for verifying that the module is initialized. It will cause the function to return
* @ref NRF_ERROR_INVALID_STATE if not.
*/
#ifdef DISABLE_PARAM_CHECK
#define VERIFY_MODULE_INITIALIZED()
#else
#ifdef MODULE_INITIALIZED
#define VERIFY_MODULE_INITIALIZED() \
do \
{ \
if (!MODULE_INITIALIZED) \
{ \
return NRF_ERROR_INVALID_STATE; \
} \
} while(0)
#else
#define VERIFY_MODULE_INITIALIZED()
#endif /* MODULE_INITIALIZED */
#endif /* DISABLE_PARAM_CHECK */
/**@brief Macro for verifying that the module is initialized. It will cause the function to return
* if not.
*/
#ifdef DISABLE_PARAM_CHECK
#define VERIFY_MODULE_INITIALIZED_VOID()
#else
#ifdef MODULE_INITIALIZED
#define VERIFY_MODULE_INITIALIZED_VOID() \
do \
{ \
if (!MODULE_INITIALIZED) \
{ \
return; \
} \
} while(0)
#else
#define VERIFY_MODULE_INITIALIZED_VOID()
#endif /* MODULE_INITIALIZED */
#endif /* DISABLE_PARAM_CHECK */
/** @} */
/** @endcond */
#endif // SDK_MACROS_H__

View File

@ -0,0 +1,161 @@
/* Copyright (c) 2015 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 "sdk_mapped_flags.h"
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "compiler_abstraction.h"
/**@brief Function for setting the state of a flag to true.
*
* @note This function does not check whether the index is valid.
*
* @param[in] p_flags The collection of flags to modify.
* @param[in] index The index of the flag to modify.
*/
static __INLINE void sdk_mapped_flags_set_by_index(sdk_mapped_flags_t * p_flags, uint16_t index)
{
*p_flags |= (1U << index);
}
/**@brief Function for setting the state of a flag to false.
*
* @note This function does not check whether the index is valid.
*
* @param[in] p_flags The collection of flags to modify.
* @param[in] index The index of the flag to modify.
*/
static __INLINE void sdk_mapped_flags_clear_by_index(sdk_mapped_flags_t * p_flags, uint16_t index)
{
*p_flags &= ~(1U << index);
}
/**@brief Function for getting the state of a flag.
*
* @note This function does not check whether the index is valid.
*
* @param[in] p_flags The collection of flags to read.
* @param[in] index The index of the flag to get.
*/
static __INLINE bool sdk_mapped_flags_get_by_index(sdk_mapped_flags_t flags, uint16_t index)
{
return ((flags & (1 << index)) != 0);
}
uint16_t sdk_mapped_flags_first_key_index_get(sdk_mapped_flags_t flags)
{
for (uint16_t i = 0; i < SDK_MAPPED_FLAGS_N_KEYS; i++)
{
if (sdk_mapped_flags_get_by_index(flags, i))
{
return i;
}
}
return SDK_MAPPED_FLAGS_INVALID_INDEX;
}
void sdk_mapped_flags_update_by_key(uint16_t * p_keys,
sdk_mapped_flags_t * p_flags,
uint16_t key,
bool value)
{
sdk_mapped_flags_bulk_update_by_key(p_keys, p_flags, 1, key, value);
}
void sdk_mapped_flags_bulk_update_by_key(uint16_t * p_keys,
sdk_mapped_flags_t * p_flags,
uint32_t n_flag_collections,
uint16_t key,
bool value)
{
if ((p_keys != NULL) && (p_flags != NULL) && (n_flag_collections > 0))
{
for (uint32_t i = 0; i < SDK_MAPPED_FLAGS_N_KEYS; i++)
{
if (p_keys[i] == key)
{
for (uint32_t j = 0; j < n_flag_collections; j++)
{
if (value)
{
sdk_mapped_flags_set_by_index(&p_flags[j], i);
}
else
{
sdk_mapped_flags_clear_by_index(&p_flags[j], i);
}
}
return;
}
}
}
}
bool sdk_mapped_flags_get_by_key(uint16_t * p_keys, sdk_mapped_flags_t flags, uint16_t key)
{
if (p_keys != NULL)
{
for (uint32_t i = 0; i < SDK_MAPPED_FLAGS_N_KEYS; i++)
{
if (p_keys[i] == key)
{
return sdk_mapped_flags_get_by_index(flags, i);
}
}
}
return false;
}
sdk_mapped_flags_key_list_t sdk_mapped_flags_key_list_get(uint16_t * p_keys,
sdk_mapped_flags_t flags)
{
sdk_mapped_flags_key_list_t key_list;
key_list.len = 0;
if (p_keys != NULL)
{
for (uint32_t i = 0; i < SDK_MAPPED_FLAGS_N_KEYS; i++)
{
if (sdk_mapped_flags_get_by_index(flags, i))
{
key_list.flag_keys[key_list.len++] = p_keys[i];
}
}
}
return key_list;
}
uint32_t sdk_mapped_flags_n_flags_set(sdk_mapped_flags_t flags)
{
uint32_t n_flags_set = 0;
for (uint32_t i = 0; i < SDK_MAPPED_FLAGS_N_KEYS; i++)
{
if (sdk_mapped_flags_get_by_index(flags, i))
{
n_flags_set += 1;
}
}
return n_flags_set;
}

View File

@ -0,0 +1,153 @@
/* Copyright (c) 2015 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.
*
*/
#ifndef SDK_MAPPED_FLAGS_H__
#define SDK_MAPPED_FLAGS_H__
#include <stdint.h>
#include <stdbool.h>
#include "app_util.h"
#include "compiler_abstraction.h"
/**
* @file
* @defgroup sdk_mapped_flags Mapped flags
* @ingroup app_common
* @{
* @brief Module for writing and reading flags that are associated
* with keys.
*
* @details The flags are represented as bits in a bitmap called a <i>flag collection</i>. The keys
* are uint16_t. Each flag collection contains all flags of the same type, one flag for
* each key.
*
* The mapped flags module does not keep the flag states, nor the list of keys. These are
* provided in the API calls. A key's index in the key list determines which bit in the
* flag collection is associated with it. This module does not ever edit the key list, and
* does not edit flags except in function calls that take the flag collection as a pointer.
*
*/
#define SDK_MAPPED_FLAGS_N_KEYS 8 /**< The number of keys to keep flags for. This is also the number of flags in a flag collection. If changing this value, you might also need change the width of the sdk_mapped_flags_t type. */
#define SDK_MAPPED_FLAGS_N_KEYS_PER_BYTE 8 /**< The number of flags that fit in one byte. */
#define SDK_MAPPED_FLAGS_INVALID_INDEX 0xFFFF /**< A flag index guaranteed to be invalid. */
typedef uint8_t sdk_mapped_flags_t; /**< The bitmap to hold flags. Each flag is one bit, and each bit represents the flag state associated with one key. */
// Test whether the flag collection type is large enough to hold all the flags. If this fails,
// reduce SDK_MAPPED_FLAGS_N_KEYS or increase the size of sdk_mapped_flags_t.
STATIC_ASSERT((
sizeof(sdk_mapped_flags_t)*SDK_MAPPED_FLAGS_N_KEYS_PER_BYTE) >= SDK_MAPPED_FLAGS_N_KEYS);
/**@brief Type used to present a subset of the registered keys.
*/
typedef struct
{
uint32_t len; /**< The length of the list. */
uint16_t flag_keys[SDK_MAPPED_FLAGS_N_KEYS]; /**< The list of keys. */
} sdk_mapped_flags_key_list_t;
/**@brief Function for getting the first index at which the flag is true in the provided
* collection.
*
* @param[in] flags The flag collection to search for a flag set to true.
*
* @return The first index that has its flag set to true. If none were found, the
* function returns @ref SDK_MAPPED_FLAGS_INVALID_INDEX.
*/
uint16_t sdk_mapped_flags_first_key_index_get(sdk_mapped_flags_t flags);
/**@brief Function for updating the state of a flag.
*
* @param[in] p_keys The list of associated keys (assumed to have a length of
* @ref SDK_MAPPED_FLAGS_N_KEYS).
* @param[out] p_flags The flag collection to modify.
* @param[in] key The key to modify the flag of.
* @param[in] value The state to set the flag to.
*/
void sdk_mapped_flags_update_by_key(uint16_t * p_keys,
sdk_mapped_flags_t * p_flags,
uint16_t key,
bool value);
/**@brief Function for updating the state of the same flag in multiple flag collections.
*
* @details The key and value are the same for all flag collections in the p_flags array.
*
* @param[in] p_keys The list of associated keys (assumed to have a length of
* @ref SDK_MAPPED_FLAGS_N_KEYS).
* @param[out] p_flags The flag collections to modify.
* @param[out] n_flag_collections The number of flag collections in p_flags.
* @param[in] key The key to modify the flag of.
* @param[in] value The state to set the flag to.
*/
void sdk_mapped_flags_bulk_update_by_key(uint16_t * p_keys,
sdk_mapped_flags_t * p_flags,
uint32_t n_flag_collections,
uint16_t key,
bool value);
/**@brief Function for getting the state of a specific flag.
*
* @param[in] p_keys The list of associated keys (assumed to have a length of
* @ref SDK_MAPPED_FLAGS_N_KEYS).
* @param[in] flags The flag collection to read from.
* @param[in] key The key to get the flag for.
*
* @return The state of the flag.
*/
bool sdk_mapped_flags_get_by_key(uint16_t * p_keys, sdk_mapped_flags_t flags, uint16_t key);
/**@brief Function for getting a list of all keys that have a specific flag set to true.
*
* @param[in] p_keys The list of associated keys (assumed to have a length of
* @ref SDK_MAPPED_FLAGS_N_KEYS).
* @param[in] flags The flag collection to search.
*
* @return The list of keys.
*/
sdk_mapped_flags_key_list_t sdk_mapped_flags_key_list_get(uint16_t * p_keys,
sdk_mapped_flags_t flags);
/**@brief Function for getting the number of keys that have a specific flag set to true.
*
* @param[in] flags The flag collection to search.
*
* @return The number of keys.
*/
uint32_t sdk_mapped_flags_n_flags_set(sdk_mapped_flags_t flags);
/**@brief Function for querying whether any flags in the collection are set.
*
* @param[in] flags The flag collection to query.
*
* @retval true If one or more flags are set to true.
* @retval false Otherwise.
*/
static __INLINE bool sdk_mapped_flags_any_set(sdk_mapped_flags_t flags)
{
return (flags != 0);
}
/** @} */
#endif /* SDK_MAPPED_FLAGS_H__ */

View File

@ -0,0 +1,40 @@
/* 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.
*
*/
/** @cond */
/**@file
*
* @defgroup sdk_os SDK OS Abstraction
* @ingroup experimental_api
* @details In order to made SDK modules independent of use of an embedded OS, and permit
* application with varied task architecture, SDK abstracts the OS specific
* elements here in order to make all other modules agnostic to the OS or task
* architecture.
* @{
*/
#ifndef SDK_OS_H__
#define SDK_OS_H__
#define SDK_MUTEX_DEFINE(X)
#define SDK_MUTEX_INIT(X)
#define SDK_MUTEX_LOCK(X)
#define SDK_MUTEX_UNLOCK(X)
/**
* @defgroup os_data_type Data types.
*/
/** @} */
/** @endcond */
#endif // SDK_OS_H__

View File

@ -0,0 +1,50 @@
/* Copyright (c) 2015 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.
*/
/** @file
* @brief Definition file for resource usage by SoftDevice, ESB and Gazell.
*/
#ifndef APP_RESOURCES_H__
#define APP_RESOURCES_H__
#ifdef SOFTDEVICE_PRESENT
#include "nrf_sd_def.h"
#else
#define SD_PPI_RESTRICTED 0uL /**< 1 if PPI peripheral is restricted, 0 otherwise. */
#define SD_PPI_CHANNELS_USED 0uL /**< PPI channels utilized by SotfDevice (not available to th spplication). */
#define SD_PPI_GROUPS_USED 0uL /**< PPI groups utilized by SotfDevice (not available to th spplication). */
#define SD_TIMERS_USED 0uL /**< Timers used by SoftDevice. */
#define SD_SWI_USED 0uL /**< Software interrupts used by SoftDevice. */
#endif
#ifdef GAZELL_PRESENT
#include "nrf_gzll_resources.h"
#else
#define GZLL_PPI_CHANNELS_USED 0uL /**< PPI channels utilized by Gazell (not available to th spplication). */
#define GZLL_TIMERS_USED 0uL /**< Timers used by Gazell. */
#define GZLL_SWI_USED 0uL /**< Software interrupts used by Gazell */
#endif
#ifdef ESB_PRESENT
#include "nrf_esb_resources.h"
#else
#define ESB_PPI_CHANNELS_USED 0uL /**< PPI channels utilized by ESB (not available to th spplication). */
#define ESB_TIMERS_USED 0uL /**< Timers used by ESB. */
#define ESB_SWI_USED 0uL /**< Software interrupts used by ESB */
#endif
#define NRF_PPI_CHANNELS_USED (SD_PPI_CHANNELS_USED | GZLL_PPI_CHANNELS_USED | ESB_PPI_CHANNELS_USED)
#define NRF_PPI_GROUPS_USED (SD_PPI_GROUPS_USED)
#define NRF_SWI_USED (SD_SWI_USED | GZLL_SWI_USED | ESB_SWI_USED)
#define NRF_TIMERS_USED (SD_TIMERS_USED | GZLL_TIMERS_USED | ESB_TIMERS_USED)
#endif // APP_RESOURCES_H__