初始版本

This commit is contained in:
xiaozhengsheng
2025-08-19 09:49:41 +08:00
parent 10f1ddf1c1
commit 6df0f7d96e
2974 changed files with 1712873 additions and 54 deletions

View File

@@ -0,0 +1,390 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* Disclaimer: This client implementation of the Apple Notification Center Service can and will be changed at any time by Nordic Semiconductor ASA.
* Server implementations such as the ones found in iOS can be changed at any time by Apple and may cause this client implementation to stop working.
*/
#include "ancs_app_attr_get.h"
#include "nrf_ble_ancs_c.h"
#include "sdk_macros.h"
#include "nrf_log.h"
#include "string.h"
#define GATTC_OPCODE_SIZE 1 /**< Size of the GATTC OPCODE. */
#define GATTC_ATTR_HANDLE_SIZE 4 /**< Size of the attribute handle. */
#define ANCS_GATTC_WRITE_PAYLOAD_LEN_MAX (BLE_GATT_ATT_MTU_DEFAULT - GATTC_OPCODE_SIZE - GATTC_ATTR_HANDLE_SIZE) /**< Maximum length of the data that can be sent in one write. */
/**@brief Enumeration for keeping track of the state-based encoding while requesting app attributes. */
typedef enum
{
APP_ATTR_COMMAND_ID, /**< Currently encoding the command ID. */
APP_ATTR_APP_ID, /**< Currently encoding the app ID. */
APP_ATTR_ATTR_ID, /**< Currently encoding the attribute ID. */
APP_ATTR_DONE /**< Encoding done. */
}encode_app_attr_t;
/**@brief Function for determining whether an attribute is requested.
*
* @param[in] p_ancs iOS notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
*
* @return True If it is requested
* @return False If it is not requested.
*/
static bool app_attr_is_requested(ble_ancs_c_t * p_ancs, uint32_t attr_id)
{
if (p_ancs->ancs_app_attr_list[attr_id].get == true)
{
return true;
}
return false;
}
/**@brief Function for counting the number of attributes that will be requested upon a "get app attributes" command.
*
* @param[in] p_ancs iOS notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
*
* @return Number of attributes that will be requested upon a "get app attributes" command.
*/
static uint32_t app_attr_nb_to_get(ble_ancs_c_t * p_ancs)
{
uint32_t attr_nb_to_get = 0;
for (uint32_t i = 0; i < (sizeof(p_ancs->ancs_app_attr_list)/sizeof(ble_ancs_c_attr_list_t)); i++)
{
if (app_attr_is_requested(p_ancs,i))
{
attr_nb_to_get++;
}
}
return attr_nb_to_get;
}
/**@brief Function for encoding the command ID as part of assembling a "get app attributes" command.
*
* @param[in] p_ancs iOS notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
* @param[in] handle_value The handle that receives the execute command.
* @param[in] p_offset Pointer to the offset for the write.
* @param[in] p_index Pointer to the length encoded so far for the current write.
* @param[in,out] p_gq_req Pointer to the BLE GATT request structure.
*/
static ret_code_t queued_write_tx_message(ble_ancs_c_t * p_ancs,
uint16_t handle_value,
uint16_t * p_offset,
uint32_t * p_index,
nrf_ble_gq_req_t * p_gq_req)
{
NRF_LOG_DEBUG("Starting new tx message.");
p_gq_req->type = NRF_BLE_GQ_REQ_GATTC_WRITE;
p_gq_req->error_handler.cb = p_ancs->gatt_err_handler;
p_gq_req->error_handler.p_ctx = p_ancs;
p_gq_req->params.gattc_write.len = *p_index;
p_gq_req->params.gattc_write.offset = *p_offset;
p_gq_req->params.gattc_write.write_op = BLE_GATT_OP_PREP_WRITE_REQ;
p_gq_req->params.gattc_write.handle = handle_value;
return nrf_ble_gq_item_add(p_ancs->p_gatt_queue, p_gq_req, p_ancs->conn_handle);
}
/**@brief Function for encoding the command ID as part of assembling a "get app attributes" command.
*
* @param[in] p_index Pointer to the length encoded so far for the current write.
* @param[in,out] p_gq_req Pointer to the BLE GATT request structure.
*/
static encode_app_attr_t app_attr_encode_cmd_id(uint32_t * index,
nrf_ble_gq_req_t * p_gq_req)
{
uint8_t * p_value = (uint8_t *)p_gq_req->params.gattc_write.p_value;
NRF_LOG_DEBUG("Encoding command ID.");
// Encode Command ID.
p_value[(*index)++] = BLE_ANCS_COMMAND_ID_GET_APP_ATTRIBUTES;
return APP_ATTR_APP_ID;
}
/**@brief Function for encoding the app ID as part of assembling a "get app attributes" command.
*
* @param[in] p_ancs iOS notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
* @param[in] p_app_id The app ID of the app for which to request app attributes.
* @param[in] app_id_len Length of the app ID.
* @param[in] p_index Pointer to the length encoded so far for the current write.
* @param[in] p_offset Pointer to the accumulated offset for the next write.
* @param[in] p_gq_req Pointer to the BLE GATT request structure.
* @param[in] p_app_id_bytes_encoded_count Variable to keep count of the encoded app ID bytes.
* As long as it is lower than the length of the app ID,
* parsing continues.
*/
static encode_app_attr_t app_attr_encode_app_id(ble_ancs_c_t * p_ancs,
uint32_t * p_index,
uint16_t * p_offset,
nrf_ble_gq_req_t * p_gq_req,
const uint8_t * p_app_id,
const uint32_t app_id_len,
uint32_t * p_app_id_bytes_encoded_count)
{
ret_code_t err_code;
uint8_t * p_value = (uint8_t *)p_gq_req->params.gattc_write.p_value;
NRF_LOG_DEBUG("Encoding app ID.");
if (*p_index >= ANCS_GATTC_WRITE_PAYLOAD_LEN_MAX)
{
err_code = queued_write_tx_message(p_ancs,
p_ancs->service.control_point_char.handle_value,
p_offset,
p_index,
p_gq_req);
if ((err_code != NRF_SUCCESS) && (p_ancs->error_handler != NULL))
{
p_ancs->error_handler(err_code);
}
*(p_offset) += *p_index;
*p_index = 0;
}
//Encode app identifier.
if (*p_app_id_bytes_encoded_count == app_id_len)
{
p_value[(*p_index)++] = '\0';
(*p_app_id_bytes_encoded_count)++;
}
NRF_LOG_DEBUG("%c", p_app_id[(*p_app_id_bytes_encoded_count)]);
if (*p_app_id_bytes_encoded_count < app_id_len)
{
p_value[(*p_index)++] = p_app_id[(*p_app_id_bytes_encoded_count)++];
}
if (*p_app_id_bytes_encoded_count > app_id_len)
{
return APP_ATTR_ATTR_ID;
}
return APP_ATTR_APP_ID;
}
/**@brief Function for encoding the attribute ID as part of assembling a "get app attributes" command.
*
* @param[in] p_ancs iOS notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
* @param[in] p_index Pointer to the length encoded so far for the current write.
* @param[in] p_offset Pointer to the accumulated offset for the next write.
* @param[in,out] p_gq_req Pointer to the BLE GATT request structure.
* @param[in] p_attr_count Pointer to a variable that iterates the possible app attributes.
*/
static encode_app_attr_t app_attr_encode_attr_id(ble_ancs_c_t * p_ancs,
uint32_t * p_index,
uint16_t * p_offset,
nrf_ble_gq_req_t * p_gq_req,
uint32_t * p_attr_count,
uint32_t * attr_get_total_nb)
{
ret_code_t err_code;
uint8_t * p_value = (uint8_t *)p_gq_req->params.gattc_write.p_value;
NRF_LOG_DEBUG("Encoding attribute ID.");
if ((*p_index) >= ANCS_GATTC_WRITE_PAYLOAD_LEN_MAX)
{
err_code = queued_write_tx_message(p_ancs,
p_ancs->service.control_point_char.handle_value,
p_offset,
p_index,
p_gq_req);
if ((err_code != NRF_SUCCESS) && (p_ancs->error_handler != NULL))
{
p_ancs->error_handler(err_code);
}
*(p_offset) += *p_index;
*p_index = 0;
}
//Encode Attribute ID.
if (*p_attr_count < BLE_ANCS_NB_OF_APP_ATTR)
{
if (app_attr_is_requested(p_ancs, *p_attr_count))
{
p_value[(*p_index)] = *p_attr_count;
p_ancs->number_of_requested_attr++;
(*p_index)++;
NRF_LOG_DEBUG("offset %i", *p_offset);
}
(*p_attr_count)++;
}
if (*p_attr_count == BLE_ANCS_NB_OF_APP_ATTR)
{
return APP_ATTR_DONE;
}
return APP_ATTR_APP_ID;
}
/**@brief Function for writing the "execute write" command to a handle for a given connection.
*
* @param[in] p_ancs iOS notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
* @param[in] handle_value Handle that receives the "execute write" command.
* @param[in] p_gq_req Pointer to the BLE GATT request structure.
*/
static ret_code_t app_attr_execute_write(ble_ancs_c_t * p_ancs,
uint16_t handle_value,
nrf_ble_gq_req_t * p_gq_req)
{
NRF_LOG_DEBUG("Sending Execute Write command.");
memset(p_gq_req, 0, sizeof(nrf_ble_gq_req_t));
p_gq_req->type = NRF_BLE_GQ_REQ_GATTC_WRITE;
p_gq_req->error_handler.cb = p_ancs->gatt_err_handler;
p_gq_req->error_handler.p_ctx = p_ancs;
p_gq_req->params.gattc_write.handle = handle_value;
p_gq_req->params.gattc_write.offset = 0;
p_gq_req->params.gattc_write.write_op = BLE_GATT_OP_EXEC_WRITE_REQ;
p_gq_req->params.gattc_write.flags = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_WRITE;
p_gq_req->params.gattc_write.len = 0;
return nrf_ble_gq_item_add(p_ancs->p_gatt_queue, p_gq_req, p_ancs->conn_handle);
}
/**@brief Function for sending a "get app attributes" request.
*
* @details Since the app ID may not fit in a single write, long write
* with a state machine is used to encode the "get app attributes" request.
*
* @param[in] p_ancs iOS notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
* @param[in] p_app_id The app ID of the app for which to request app attributes.
* @param[in] app_id_len Length of the app ID.
*
*/
static uint32_t app_attr_get(ble_ancs_c_t * p_ancs,
const uint8_t * p_app_id,
uint32_t app_id_len)
{
uint32_t index = 0;
uint32_t attr_bytes_encoded_count = 0;
uint16_t offset = 0;
uint32_t app_id_bytes_encoded_count = 0;
encode_app_attr_t state = APP_ATTR_COMMAND_ID;
ret_code_t err_code;
p_ancs->number_of_requested_attr = 0;
uint32_t attr_get_total_nb = app_attr_nb_to_get(p_ancs);
nrf_ble_gq_req_t ancs_req;
uint8_t gatt_value[BLE_ANCS_WRITE_MAX_MSG_LENGTH];
memset(&ancs_req, 0, sizeof(nrf_ble_gq_req_t));
ancs_req.params.gattc_write.p_value = gatt_value;
while (state != APP_ATTR_DONE)
{
switch (state)
{
case APP_ATTR_COMMAND_ID:
state = app_attr_encode_cmd_id(&index,
&ancs_req);
break;
case APP_ATTR_APP_ID:
state = app_attr_encode_app_id(p_ancs,
&index,
&offset,
&ancs_req,
p_app_id,
app_id_len,
&app_id_bytes_encoded_count);
break;
case APP_ATTR_ATTR_ID:
state = app_attr_encode_attr_id(p_ancs,
&index,
&offset,
&ancs_req,
&attr_bytes_encoded_count,
&attr_get_total_nb);
break;
case APP_ATTR_DONE:
break;
default:
break;
}
}
err_code = queued_write_tx_message(p_ancs,
p_ancs->service.control_point_char.handle_value,
&offset,
&index,
&ancs_req);
VERIFY_SUCCESS(err_code);
err_code = app_attr_execute_write(p_ancs,
p_ancs->service.control_point_char.handle_value,
&ancs_req);
p_ancs->parse_info.expected_number_of_attrs = p_ancs->number_of_requested_attr;
return err_code;
}
uint32_t ancs_c_app_attr_request(ble_ancs_c_t * p_ancs,
const uint8_t * p_app_id,
uint32_t len)
{
uint32_t err_code;
if (len == 0)
{
return NRF_ERROR_DATA_SIZE;
}
if (p_app_id[len] != '\0') // App ID to be requested must be null-terminated.
{
return NRF_ERROR_INVALID_PARAM;
}
p_ancs->parse_info.parse_state = COMMAND_ID;
err_code = app_attr_get(p_ancs, p_app_id, len);
VERIFY_SUCCESS(err_code);
return NRF_SUCCESS;
}

View File

@@ -0,0 +1,68 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef ANCS_APP_ATTR_GET_H__
#define ANCS_APP_ATTR_GET_H__
#include "nrf_ble_ancs_c.h"
/** @file
*
* @addtogroup ble_ancs_c
* @{
*/
#define BLE_ANCS_WRITE_MAX_MSG_LENGTH 20 /**< Maximum GATTC write length. */
/**@brief Function for requesting attributes for an app.
*
* @param[in] p_ancs iOS notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
* @param[in] app_id App identifier of the app for which to request app attributes.
* @param[in] len Length of the app identifier.
*
* @retval NRF_SUCCESS If all operations were successful. Otherwise, an error code is returned.
*/
uint32_t ancs_c_app_attr_request(ble_ancs_c_t * p_ancs,
const uint8_t * app_id,
uint32_t len);
/** @} */
#endif // ANCS_APP_ATTR_GET_H__

View File

@@ -0,0 +1,392 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* Disclaimer: This client implementation of the Apple Notification Center Service can and will be changed at any time by Nordic Semiconductor ASA.
* Server implementations such as the ones found in iOS can be changed at any time by Apple and may cause this client implementation to stop working.
*/
#include "nrf_ble_ancs_c.h"
#include "ancs_attr_parser.h"
#include "nrf_log.h"
static bool all_req_attrs_parsed(ble_ancs_c_t * p_ancs)
{
if (p_ancs->parse_info.expected_number_of_attrs == 0)
{
return true;
}
return false;
}
static bool attr_is_requested(ble_ancs_c_t * p_ancs, ble_ancs_c_attr_t attr)
{
if (p_ancs->parse_info.p_attr_list[attr.attr_id].get == true)
{
return true;
}
return false;
}
/**@brief Function for parsing command id and notification id.
* Used in the @ref parse_get_notif_attrs_response state machine.
*
* @details UID and command ID will be received only once at the beginning of the first
* GATTC notification of a new attribute request for a given iOS notification.
*
* @param[in] p_ancs Pointer to an ANCS instance to which the event belongs.
* @param[in] p_data_src Pointer to data that was received from the Notification Provider.
* @param[in] index Pointer to an index that helps us keep track of the current data to be parsed.
*
* @return The next parse state.
*/
static ble_ancs_c_parse_state_t command_id_parse(ble_ancs_c_t * p_ancs,
const uint8_t * p_data_src,
uint32_t * index)
{
ble_ancs_c_parse_state_t parse_state;
p_ancs->parse_info.command_id = (ble_ancs_c_cmd_id_val_t) p_data_src[(*index)++];
switch (p_ancs->parse_info.command_id)
{
case BLE_ANCS_COMMAND_ID_GET_NOTIF_ATTRIBUTES:
p_ancs->evt.evt_type = BLE_ANCS_C_EVT_NOTIF_ATTRIBUTE;
p_ancs->parse_info.p_attr_list = p_ancs->ancs_notif_attr_list;
p_ancs->parse_info.nb_of_attr = BLE_ANCS_NB_OF_NOTIF_ATTR;
parse_state = NOTIF_UID;
break;
case BLE_ANCS_COMMAND_ID_GET_APP_ATTRIBUTES:
p_ancs->evt.evt_type = BLE_ANCS_C_EVT_APP_ATTRIBUTE;
p_ancs->parse_info.p_attr_list = p_ancs->ancs_app_attr_list;
p_ancs->parse_info.nb_of_attr = BLE_ANCS_NB_OF_APP_ATTR;
parse_state = APP_ID;
break;
default:
//no valid command_id, abort the rest of the parsing procedure.
NRF_LOG_DEBUG("Invalid Command ID");
parse_state = DONE;
break;
}
return parse_state;
}
static ble_ancs_c_parse_state_t notif_uid_parse(ble_ancs_c_t * p_ancs,
const uint8_t * p_data_src,
uint32_t * index)
{
p_ancs->evt.notif_uid = uint32_decode(&p_data_src[*index]);
*index += sizeof(uint32_t);
return ATTR_ID;
}
static ble_ancs_c_parse_state_t app_id_parse(ble_ancs_c_t * p_ancs,
const uint8_t * p_data_src,
uint32_t * index)
{
p_ancs->evt.app_id[p_ancs->parse_info.current_app_id_index] = p_data_src[(*index)++];
if (p_ancs->evt.app_id[p_ancs->parse_info.current_app_id_index] != '\0')
{
p_ancs->parse_info.current_app_id_index++;
return APP_ID;
}
else
{
return ATTR_ID;
}
}
/**@brief Function for parsing the id of an iOS attribute.
* Used in the @ref parse_get_notif_attrs_response state machine.
*
* @details We only request attributes that are registered with @ref ble_ancs_c_attr_add
* once they have been reveiced we stop parsing.
*
* @param[in] p_ancs Pointer to an ANCS instance to which the event belongs.
* @param[in] p_data_src Pointer to data that was received from the Notification Provider.
* @param[in] index Pointer to an index that helps us keep track of the current data to be parsed.
*
* @return The next parse state.
*/
static ble_ancs_c_parse_state_t attr_id_parse(ble_ancs_c_t * p_ancs,
const uint8_t * p_data_src,
uint32_t * index)
{
p_ancs->evt.attr.attr_id = p_data_src[(*index)++];
if (p_ancs->evt.attr.attr_id >= p_ancs->parse_info.nb_of_attr)
{
NRF_LOG_DEBUG("Attribute ID Invalid.");
return DONE;
}
p_ancs->evt.attr.p_attr_data = p_ancs->parse_info.p_attr_list[p_ancs->evt.attr.attr_id].p_attr_data;
if (all_req_attrs_parsed(p_ancs))
{
NRF_LOG_DEBUG("All requested attributes received. ");
return DONE;
}
else
{
if (attr_is_requested(p_ancs, p_ancs->evt.attr))
{
p_ancs->parse_info.expected_number_of_attrs--;
}
NRF_LOG_DEBUG("Attribute ID %i ", p_ancs->evt.attr.attr_id);
return ATTR_LEN1;
}
}
/**@brief Function for parsing the length of an iOS attribute.
* Used in the @ref parse_get_notif_attrs_response state machine.
*
* @details The Length is 2 bytes. Since there is a chance we reveice the bytes in two different
* GATTC notifications, we parse only the first byte here and then set the state machine
* ready to parse the next byte.
*
* @param[in] p_ancs Pointer to an ANCS instance to which the event belongs.
* @param[in] p_data_src Pointer to data that was received from the Notification Provider.
* @param[in] index Pointer to an index that helps us keep track of the current data to be parsed.
*
* @return The next parse state.
*/
static ble_ancs_c_parse_state_t attr_len1_parse(ble_ancs_c_t * p_ancs, const uint8_t * p_data_src, uint32_t * index)
{
p_ancs->evt.attr.attr_len = p_data_src[(*index)++];
return ATTR_LEN2;
}
/**@brief Function for parsing the length of an iOS attribute.
* Used in the @ref parse_get_notif_attrs_response state machine.
*
* @details Second byte of the length field. If the length is zero, it means that the attribute is not
* present and the state machine is set to parse the next attribute.
*
* @param[in] p_ancs Pointer to an ANCS instance to which the event belongs.
* @param[in] p_data_src Pointer to data that was received from the Notification Provider.
* @param[in] index Pointer to an index that helps us keep track of the current data to be parsed.
*
* @return The next parse state.
*/
static ble_ancs_c_parse_state_t attr_len2_parse(ble_ancs_c_t * p_ancs, const uint8_t * p_data_src, uint32_t * index)
{
p_ancs->evt.attr.attr_len |= (p_data_src[(*index)++] << 8);
p_ancs->parse_info.current_attr_index = 0;
if (p_ancs->evt.attr.attr_len != 0)
{
//If the attribute has a length but there is no allocated space for this attribute
if ((p_ancs->parse_info.p_attr_list[p_ancs->evt.attr.attr_id].attr_len == 0) ||
(p_ancs->parse_info.p_attr_list[p_ancs->evt.attr.attr_id].p_attr_data == NULL))
{
return ATTR_SKIP;
}
else
{
return ATTR_DATA;
}
}
else
{
NRF_LOG_DEBUG("Attribute LEN %i ", p_ancs->evt.attr.attr_len);
if (attr_is_requested(p_ancs, p_ancs->evt.attr))
{
p_ancs->evt_handler(&p_ancs->evt);
}
if (all_req_attrs_parsed(p_ancs))
{
return DONE;
}
else
{
return ATTR_ID;
}
}
}
/**@brief Function for parsing the data of an iOS attribute.
* Used in the @ref parse_get_notif_attrs_response state machine.
*
* @details Read the data of the attribute into our local buffer.
*
* @param[in] p_ancs Pointer to an ANCS instance to which the event belongs.
* @param[in] p_data_src Pointer to data that was received from the Notification Provider.
* @param[in] index Pointer to an index that helps us keep track of the current data to be parsed.
*
* @return The next parse state.
*/
static ble_ancs_c_parse_state_t attr_data_parse(ble_ancs_c_t * p_ancs,
const uint8_t * p_data_src,
uint32_t * index)
{
// We have not reached the end of the attribute, nor our max allocated internal size.
// Proceed with copying data over to our buffer.
if ( (p_ancs->parse_info.current_attr_index < p_ancs->parse_info.p_attr_list[p_ancs->evt.attr.attr_id].attr_len)
&& (p_ancs->parse_info.current_attr_index < p_ancs->evt.attr.attr_len))
{
//NRF_LOG_DEBUG("Byte copied to buffer: %c", p_data_src[(*index)]); // Un-comment this line to see every byte of an attribute as it is parsed. Commented out by default since it can overflow the uart buffer.
p_ancs->evt.attr.p_attr_data[p_ancs->parse_info.current_attr_index++] = p_data_src[(*index)++];
}
// We have reached the end of the attribute, or our max allocated internal size.
// Stop copying data over to our buffer. NUL-terminate at the current index.
if ( (p_ancs->parse_info.current_attr_index == p_ancs->evt.attr.attr_len) ||
(p_ancs->parse_info.current_attr_index == p_ancs->parse_info.p_attr_list[p_ancs->evt.attr.attr_id].attr_len - 1))
{
if (attr_is_requested(p_ancs, p_ancs->evt.attr))
{
p_ancs->evt.attr.p_attr_data[p_ancs->parse_info.current_attr_index] = '\0';
}
// If our max buffer size is smaller than the remaining attribute data, we must
// increase index to skip the data until the start of the next attribute.
if (p_ancs->parse_info.current_attr_index < p_ancs->evt.attr.attr_len)
{
return ATTR_SKIP;
}
NRF_LOG_DEBUG("Attribute finished!");
if (attr_is_requested(p_ancs, p_ancs->evt.attr))
{
p_ancs->evt_handler(&p_ancs->evt);
}
if (all_req_attrs_parsed(p_ancs))
{
return DONE;
}
else
{
return ATTR_ID;
}
}
return ATTR_DATA;
}
static ble_ancs_c_parse_state_t attr_skip(ble_ancs_c_t * p_ancs, const uint8_t * p_data_src, uint32_t * index)
{
// We have not reached the end of the attribute, nor our max allocated internal size.
// Proceed with copying data over to our buffer.
if (p_ancs->parse_info.current_attr_index < p_ancs->evt.attr.attr_len)
{
p_ancs->parse_info.current_attr_index++;
(*index)++;
}
// At the end of the attribute, determine if it should be passed to event handler and
// continue parsing the next attribute ID if we are not done with all the attributes.
if (p_ancs->parse_info.current_attr_index == p_ancs->evt.attr.attr_len)
{
if (attr_is_requested(p_ancs, p_ancs->evt.attr))
{
p_ancs->evt_handler(&p_ancs->evt);
}
if (all_req_attrs_parsed(p_ancs))
{
return DONE;
}
else
{
return ATTR_ID;
}
}
return ATTR_SKIP;
}
void ancs_parse_get_attrs_response(ble_ancs_c_t * p_ancs,
const uint8_t * p_data_src,
const uint16_t hvx_data_len)
{
uint32_t index;
for (index = 0; index < hvx_data_len;)
{
switch (p_ancs->parse_info.parse_state)
{
case COMMAND_ID:
p_ancs->parse_info.parse_state = command_id_parse(p_ancs, p_data_src, &index);
break;
case NOTIF_UID:
p_ancs->parse_info.parse_state = notif_uid_parse(p_ancs, p_data_src, &index);
break;
case APP_ID:
p_ancs->parse_info.parse_state = app_id_parse(p_ancs, p_data_src, &index);
break;
case ATTR_ID:
p_ancs->parse_info.parse_state = attr_id_parse(p_ancs, p_data_src, &index);
break;
case ATTR_LEN1:
p_ancs->parse_info.parse_state = attr_len1_parse(p_ancs, p_data_src, &index);
break;
case ATTR_LEN2:
p_ancs->parse_info.parse_state = attr_len2_parse(p_ancs, p_data_src, &index);
break;
case ATTR_DATA:
p_ancs->parse_info.parse_state = attr_data_parse(p_ancs, p_data_src, &index);
break;
case ATTR_SKIP:
p_ancs->parse_info.parse_state = attr_skip(p_ancs, p_data_src, &index);
break;
case DONE:
NRF_LOG_DEBUG("Parse state: Done ");
index = hvx_data_len;
break;
default:
// Default case will never trigger intentionally. Go to the DONE state to minimize the consequences.
p_ancs->parse_info.parse_state = DONE;
break;
}
}
}

View File

@@ -0,0 +1,77 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef BLE_ANCS_ATTR_PARSER_H__
#define BLE_ANCS_ATTR_PARSER_H__
#include "nrf_ble_ancs_c.h"
/** @file
*
* @addtogroup ble_ancs_c
* @{
*/
/**@brief Function for parsing notification or app attribute response data.
*
* @details The data that comes from the Notification Provider can be much longer than what
* would fit in a single GATTC notification. Therefore, this function relies on a
* state-oriented switch case.
* UID and command ID will be received only once at the beginning of the first
* GATTC notification of a new attribute request for a given iOS notification.
* After this, we can loop several ATTR_ID > LENGTH > DATA > ATTR_ID > LENGTH > DATA until
* we have received all attributes we wanted as a Notification Consumer.
* The Notification Provider can also simply stop sending attributes.
*
* 1 byte | 4 bytes |1 byte |2 bytes |... X bytes ... |1 bytes| 2 bytes| ... X bytes ...
* --------|-------------|-------|--------|----------------|-------|--------|----------------
* CMD_ID | NOTIF_UID |ATTR_ID| LENGTH | DATA |ATTR_ID| LENGTH | DATA
*
* @param[in] p_ancs Pointer to an ANCS instance to which the event belongs.
* @param[in] p_data_src Pointer to data that was received from the Notification Provider.
* @param[in] hvx_data_len Length of the data that was received from the Notification Provider.
*/
void ancs_parse_get_attrs_response(ble_ancs_c_t * p_ancs,
const uint8_t * p_data_src,
const uint16_t hvx_data_len);
/** @} */
#endif // BLE_ANCS_ATTR_PARSER_H__

View File

@@ -0,0 +1,683 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* Disclaimer: This client implementation of the Apple Notification Center Service can and will be changed at any time by Nordic Semiconductor ASA.
* Server implementations such as the ones found in iOS can be changed at any time by Apple and may cause this client implementation to stop working.
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_ANCS_C)
#include "nrf_ble_ancs_c.h"
#include "ancs_attr_parser.h"
#include "ancs_app_attr_get.h"
#include "ble_err.h"
#include "ble_srv_common.h"
#include "ble_db_discovery.h"
#include "app_error.h"
#define NRF_LOG_MODULE_NAME ble_ancs_c
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();
#define BLE_ANCS_NOTIF_EVT_ID_INDEX 0 /**< Index of the Event ID field when parsing notifications. */
#define BLE_ANCS_NOTIF_FLAGS_INDEX 1 /**< Index of the Flags field when parsing notifications. */
#define BLE_ANCS_NOTIF_CATEGORY_ID_INDEX 2 /**< Index of the Category ID field when parsing notifications. */
#define BLE_ANCS_NOTIF_CATEGORY_CNT_INDEX 3 /**< Index of the Category Count field when parsing notifications. */
#define BLE_ANCS_NOTIF_NOTIF_UID 4 /**< Index of the Notification UID field when parsing notifications. */
#define BLE_CCCD_NOTIFY_BIT_MASK 0x0001 /**< Enables notification bit. */
#define TIME_STRING_LEN 15 /**< Unicode Technical Standard (UTS) #35 date format pattern "yyyyMMdd'T'HHmmSS" + "'\0'". */
/**@brief 128-bit service UUID for the Apple Notification Center Service. */
ble_uuid128_t const ble_ancs_base_uuid128 =
{
{
// 7905F431-B5CE-4E99-A40F-4B1E122D00D0
0xd0, 0x00, 0x2d, 0x12, 0x1e, 0x4b, 0x0f, 0xa4,
0x99, 0x4e, 0xce, 0xb5, 0x31, 0xf4, 0x05, 0x79
}
};
/**@brief 128-bit control point UUID. */
ble_uuid128_t const ble_ancs_cp_base_uuid128 =
{
{
// 69d1d8f3-45e1-49a8-9821-9BBDFDAAD9D9
0xd9, 0xd9, 0xaa, 0xfd, 0xbd, 0x9b, 0x21, 0x98,
0xa8, 0x49, 0xe1, 0x45, 0xf3, 0xd8, 0xd1, 0x69
}
};
/**@brief 128-bit notification source UUID. */
ble_uuid128_t const ble_ancs_ns_base_uuid128 =
{
{
// 9FBF120D-6301-42D9-8C58-25E699A21DBD
0xbd, 0x1d, 0xa2, 0x99, 0xe6, 0x25, 0x58, 0x8c,
0xd9, 0x42, 0x01, 0x63, 0x0d, 0x12, 0xbf, 0x9f
}
};
/**@brief 128-bit data source UUID. */
ble_uuid128_t const ble_ancs_ds_base_uuid128 =
{
{
// 22EAC6E9-24D6-4BB5-BE44-B36ACE7C7BFB
0xfb, 0x7b, 0x7c, 0xce, 0x6a, 0xb3, 0x44, 0xbe,
0xb5, 0x4b, 0xd6, 0x24, 0xe9, 0xc6, 0xea, 0x22
}
};
/**@brief Function for intercepting errors of GATTC and BLE GATT Queue.
*
* @param[in] nrf_error Error code.
* @param[in] p_ctx Parameter from the event handler.
* @param[in] conn_handle Connection handle.
*/
static void gatt_error_handler(uint32_t nrf_error,
void * p_ctx,
uint16_t conn_handle)
{
ble_ancs_c_t * p_ancs = (ble_ancs_c_t *)p_ctx;
NRF_LOG_DEBUG("A GATT Client error has occurred on conn_handle: 0x%x", conn_handle);
if (p_ancs->error_handler != NULL)
{
p_ancs->error_handler(nrf_error);
}
}
/**@brief Function for handling Disconnected event received from the SoftDevice.
*
* @details This function checks whether the disconnect event is happening on the link
* associated with the current instance of the module. If the event is happening,
* the function sets the conn_handle of the instance to invalid.
*
* @param[in] p_ancs Pointer to the ANCS client structure.
* @param[in] p_ble_evt Pointer to the BLE event received.
*/
static void on_disconnected(ble_ancs_c_t * p_ancs, ble_evt_t const * p_ble_evt)
{
if (p_ancs->conn_handle == p_ble_evt->evt.gap_evt.conn_handle)
{
p_ancs->conn_handle = BLE_CONN_HANDLE_INVALID;
}
}
void ble_ancs_c_on_db_disc_evt(ble_ancs_c_t * p_ancs, ble_db_discovery_evt_t * p_evt)
{
NRF_LOG_DEBUG("Database Discovery handler called with event 0x%x", p_evt->evt_type);
ble_ancs_c_evt_t evt;
ble_gatt_db_char_t * p_chars;
p_chars = p_evt->params.discovered_db.charateristics;
// Check whether the ANCS Service was discovered.
if ( (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE)
&& (p_evt->params.discovered_db.srv_uuid.uuid == ANCS_UUID_SERVICE)
&& (p_evt->params.discovered_db.srv_uuid.type == p_ancs->service.service.uuid.type))
{
// Find the handles of the ANCS characteristic.
for (uint32_t i = 0; i < p_evt->params.discovered_db.char_count; i++)
{
switch (p_chars[i].characteristic.uuid.uuid)
{
case ANCS_UUID_CHAR_CONTROL_POINT:
NRF_LOG_INFO("Control Point characteristic found.");
memcpy(&evt.service.control_point_char,
&p_chars[i].characteristic,
sizeof(ble_gattc_char_t));
break;
case ANCS_UUID_CHAR_DATA_SOURCE:
NRF_LOG_INFO("Data Source characteristic found.");
memcpy(&evt.service.data_source_char,
&p_chars[i].characteristic,
sizeof(ble_gattc_char_t));
evt.service.data_source_cccd.handle = p_chars[i].cccd_handle;
break;
case ANCS_UUID_CHAR_NOTIFICATION_SOURCE:
NRF_LOG_INFO("Notification Point characteristic found.");
memcpy(&evt.service.notif_source_char,
&p_chars[i].characteristic,
sizeof(ble_gattc_char_t));
evt.service.notif_source_cccd.handle = p_chars[i].cccd_handle;
break;
default:
break;
}
}
evt.evt_type = BLE_ANCS_C_EVT_DISCOVERY_COMPLETE;
evt.conn_handle = p_evt->conn_handle;
}
else if ((p_evt->evt_type == BLE_DB_DISCOVERY_SRV_NOT_FOUND) ||
(p_evt->evt_type == BLE_DB_DISCOVERY_ERROR))
{
evt.evt_type = BLE_ANCS_C_EVT_DISCOVERY_FAILED;
}
else
{
return;
}
p_ancs->evt_handler(&evt);
}
/**@brief Function for checking whether the data in an iOS notification is out of bounds.
*
* @param[in] notif An iOS notification.
*
* @retval NRF_SUCCESS If the notification is within bounds.
* @retval NRF_ERROR_INVALID_PARAM If the notification is out of bounds.
*/
static uint32_t ble_ancs_verify_notification_format(ble_ancs_c_evt_notif_t const * notif)
{
if ( (notif->evt_id >= BLE_ANCS_NB_OF_EVT_ID)
|| (notif->category_id >= BLE_ANCS_NB_OF_CATEGORY_ID))
{
return NRF_ERROR_INVALID_PARAM;
}
return NRF_SUCCESS;
}
/**@brief Function for receiving and validating notifications received from the Notification Provider.
*
* @param[in] p_ancs Pointer to an ANCS instance to which the event belongs.
* @param[in] p_data_src Pointer to the data that was received from the Notification Provider.
* @param[in] hvx_len Length of the data that was received from the Notification Provider.
*/
static void parse_notif(ble_ancs_c_t const * p_ancs,
uint8_t const * p_data_src,
uint16_t const hvx_data_len)
{
ble_ancs_c_evt_t ancs_evt;
uint32_t err_code;
if (hvx_data_len != BLE_ANCS_NOTIFICATION_DATA_LENGTH)
{
ancs_evt.evt_type = BLE_ANCS_C_EVT_INVALID_NOTIF;
p_ancs->evt_handler(&ancs_evt);
}
/*lint --e{415} --e{416} -save suppress Warning 415: possible access out of bound*/
ancs_evt.notif.evt_id =
(ble_ancs_c_evt_id_values_t) p_data_src[BLE_ANCS_NOTIF_EVT_ID_INDEX];
ancs_evt.notif.evt_flags.silent =
(p_data_src[BLE_ANCS_NOTIF_FLAGS_INDEX] >> BLE_ANCS_EVENT_FLAG_SILENT) & 0x01;
ancs_evt.notif.evt_flags.important =
(p_data_src[BLE_ANCS_NOTIF_FLAGS_INDEX] >> BLE_ANCS_EVENT_FLAG_IMPORTANT) & 0x01;
ancs_evt.notif.evt_flags.pre_existing =
(p_data_src[BLE_ANCS_NOTIF_FLAGS_INDEX] >> BLE_ANCS_EVENT_FLAG_PREEXISTING) & 0x01;
ancs_evt.notif.evt_flags.positive_action =
(p_data_src[BLE_ANCS_NOTIF_FLAGS_INDEX] >> BLE_ANCS_EVENT_FLAG_POSITIVE_ACTION) & 0x01;
ancs_evt.notif.evt_flags.negative_action =
(p_data_src[BLE_ANCS_NOTIF_FLAGS_INDEX] >> BLE_ANCS_EVENT_FLAG_NEGATIVE_ACTION) & 0x01;
ancs_evt.notif.category_id =
(ble_ancs_c_category_id_val_t) p_data_src[BLE_ANCS_NOTIF_CATEGORY_ID_INDEX];
ancs_evt.notif.category_count = p_data_src[BLE_ANCS_NOTIF_CATEGORY_CNT_INDEX];
ancs_evt.notif.notif_uid = uint32_decode(&p_data_src[BLE_ANCS_NOTIF_NOTIF_UID]);
/*lint -restore*/
err_code = ble_ancs_verify_notification_format(&ancs_evt.notif);
if (err_code == NRF_SUCCESS)
{
ancs_evt.evt_type = BLE_ANCS_C_EVT_NOTIF;
}
else
{
ancs_evt.evt_type = BLE_ANCS_C_EVT_INVALID_NOTIF;
}
p_ancs->evt_handler(&ancs_evt);
}
ret_code_t nrf_ble_ancs_c_app_attr_request(ble_ancs_c_t * p_ancs,
uint8_t const * p_app_id,
uint32_t len)
{
return ancs_c_app_attr_request(p_ancs, p_app_id, len);
}
/**@brief Function for receiving and validating notifications received from the Notification Provider.
*
* @param[in] p_ancs Pointer to an ANCS instance to which the event belongs.
* @param[in] p_ble_evt Bluetooth stack event.
*/
static void on_evt_gattc_notif(ble_ancs_c_t * p_ancs, ble_evt_t const * p_ble_evt)
{
ble_gattc_evt_hvx_t const * p_notif = &p_ble_evt->evt.gattc_evt.params.hvx;
if (p_ble_evt->evt.gattc_evt.conn_handle != p_ancs->conn_handle)
{
return;
}
if (p_notif->handle == p_ancs->service.notif_source_char.handle_value)
{
parse_notif(p_ancs, p_notif->data, p_notif->len);
}
else if (p_notif->handle == p_ancs->service.data_source_char.handle_value)
{
ancs_parse_get_attrs_response(p_ancs, p_notif->data, p_notif->len);
}
else
{
// No applicable action.
}
}
/**@brief Function for handling error response events.
*
* @param[in] p_ancs Pointer to an ANCS instance to which the event belongs.
* @param[in] p_ble_evt Pointer to the SoftDevice event.
*/
static void on_ctrlpt_error_rsp(ble_ancs_c_t * p_ancs, ble_evt_t const * p_ble_evt)
{
ble_ancs_c_evt_t ancs_evt;
ancs_evt.evt_type = BLE_ANCS_C_EVT_NP_ERROR;
ancs_evt.err_code_np = p_ble_evt->evt.gattc_evt.gatt_status;
p_ancs->evt_handler(&ancs_evt);
}
/**@brief Function for handling write response events.
*
* @param[in] p_ancs Pointer to an ANCS instance to which the event belongs.
* @param[in] p_ble_evt Pointer to the SoftDevice event.
*/
static void on_write_rsp(ble_ancs_c_t * p_ancs, ble_evt_t const* p_ble_evt)
{
// Check if the event is on the link for this instance.
if (p_ancs->conn_handle != p_ble_evt->evt.gattc_evt.conn_handle)
{
return;
}
if ((p_ble_evt->evt.gattc_evt.error_handle != BLE_GATT_HANDLE_INVALID)
&& (p_ble_evt->evt.gattc_evt.error_handle == p_ancs->service.control_point_char.handle_value))
{
on_ctrlpt_error_rsp(p_ancs,p_ble_evt);
}
}
void ble_ancs_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
ble_ancs_c_t * p_ancs = (ble_ancs_c_t *)p_context;
uint16_t evt = p_ble_evt->header.evt_id;
switch (evt)
{
case BLE_GATTC_EVT_WRITE_RSP:
on_write_rsp(p_ancs, p_ble_evt);
break;
case BLE_GATTC_EVT_HVX:
on_evt_gattc_notif(p_ancs, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnected(p_ancs, p_ble_evt);
break;
default:
break;
}
}
ret_code_t ble_ancs_c_init(ble_ancs_c_t * p_ancs, ble_ancs_c_init_t const * p_ancs_init)
{
uint32_t err_code;
// Verify that the parameters needed for initializing this instance of ANCS are not NULL.
VERIFY_PARAM_NOT_NULL(p_ancs);
VERIFY_PARAM_NOT_NULL(p_ancs_init);
VERIFY_PARAM_NOT_NULL(p_ancs_init->evt_handler);
// Initialize state for the attribute-parsing state machine.
p_ancs->parse_info.parse_state = COMMAND_ID;
p_ancs->parse_info.p_data_dest = NULL;
p_ancs->parse_info.current_attr_index = 0;
p_ancs->parse_info.current_app_id_index = 0;
p_ancs->evt_handler = p_ancs_init->evt_handler;
p_ancs->error_handler = p_ancs_init->error_handler;
p_ancs->conn_handle = BLE_CONN_HANDLE_INVALID;
p_ancs->p_gatt_queue = p_ancs_init->p_gatt_queue;
p_ancs->gatt_err_handler = gatt_error_handler;
p_ancs->service.data_source_cccd.uuid.uuid = BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG;
p_ancs->service.notif_source_cccd.uuid.uuid = BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG;
// Make sure that the instance of service is clear. GATT handles inside the service and characteristics are set to @ref BLE_GATT_HANDLE_INVALID.
memset(&p_ancs->service, 0, sizeof(ble_ancs_c_service_t));
// Assign UUID types.
err_code = sd_ble_uuid_vs_add(&ble_ancs_base_uuid128, &p_ancs->service.service.uuid.type);
VERIFY_SUCCESS(err_code);
err_code = sd_ble_uuid_vs_add(&ble_ancs_cp_base_uuid128, &p_ancs->service.control_point_char.uuid.type);
VERIFY_SUCCESS(err_code);
err_code = sd_ble_uuid_vs_add(&ble_ancs_ns_base_uuid128, &p_ancs->service.notif_source_char.uuid.type);
VERIFY_SUCCESS(err_code);
err_code = sd_ble_uuid_vs_add(&ble_ancs_ds_base_uuid128, &p_ancs->service.data_source_char.uuid.type);
VERIFY_SUCCESS(err_code);
// Assign UUID to the service.
p_ancs->service.service.uuid.uuid = ANCS_UUID_SERVICE;
p_ancs->service.service.uuid.type = p_ancs->service.service.uuid.type;
return ble_db_discovery_evt_register(&p_ancs->service.service.uuid);
}
/**@brief Function for creating a tx message for writing a CCCD.
*
* @param[in] p_ancs Pointer to the iOS notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
* @param[in] handle_cccd Handle of the CCCD.
* @param[in] enable Enables or disables GATTC notifications.
*
* @retval NRF_SUCCESS If the message is created successfully.
* @retval NRF_ERROR_INVALID_PARAM If one of the input parameters is invalid.
* @retval err_code Otherwise, this API propagates the error code returned by function
* @ref nrf_ble_gq_item_add.
*/
static uint32_t cccd_configure(ble_ancs_c_t const * const p_ancs,
uint16_t const handle_cccd,
bool notification_enable)
{
nrf_ble_gq_req_t ancs_c_req;
uint8_t cccd[BLE_CCCD_VALUE_LEN];
uint16_t cccd_val = notification_enable ? BLE_GATT_HVX_NOTIFICATION : 0;
cccd[0] = LSB_16(cccd_val);
cccd[1] = MSB_16(cccd_val);
memset(&ancs_c_req, 0, sizeof(nrf_ble_gq_req_t));
ancs_c_req.type = NRF_BLE_GQ_REQ_GATTC_WRITE;
ancs_c_req.error_handler.cb = p_ancs->gatt_err_handler;
ancs_c_req.error_handler.p_ctx = (ble_ancs_c_t *)p_ancs;
ancs_c_req.params.gattc_write.handle = handle_cccd;
ancs_c_req.params.gattc_write.len = BLE_CCCD_VALUE_LEN;
ancs_c_req.params.gattc_write.offset = 0;
ancs_c_req.params.gattc_write.write_op = BLE_GATT_OP_WRITE_REQ;
ancs_c_req.params.gattc_write.p_value = cccd;
return nrf_ble_gq_item_add(p_ancs->p_gatt_queue, &ancs_c_req, p_ancs->conn_handle);
}
ret_code_t ble_ancs_c_notif_source_notif_enable(ble_ancs_c_t const * p_ancs)
{
NRF_LOG_INFO("Enable Notification Source. Writing to CCCD handle: %i. ",
p_ancs->service.notif_source_cccd.handle);
return cccd_configure(p_ancs, p_ancs->service.notif_source_cccd.handle, true);
}
ret_code_t ble_ancs_c_notif_source_notif_disable(ble_ancs_c_t const * p_ancs)
{
return cccd_configure(p_ancs, p_ancs->service.notif_source_cccd.handle, false);
}
ret_code_t ble_ancs_c_data_source_notif_enable(ble_ancs_c_t const * p_ancs)
{
NRF_LOG_INFO("Enable Notification Data Source. Writing to CCCD handle: %i. ",
p_ancs->service.data_source_cccd.handle);
return cccd_configure(p_ancs, p_ancs->service.data_source_cccd.handle, true);
}
ret_code_t ble_ancs_c_data_source_notif_disable(ble_ancs_c_t const * p_ancs)
{
return cccd_configure(p_ancs, p_ancs->service.data_source_cccd.handle, false);
}
uint32_t ble_ancs_get_notif_attrs(ble_ancs_c_t * p_ancs,
uint32_t const p_uid)
{
nrf_ble_gq_req_t ancs_req;
uint8_t gattc_value[BLE_ANCS_WRITE_MAX_MSG_LENGTH];
memset(&ancs_req, 0, sizeof(nrf_ble_gq_req_t));
uint32_t index = 0;
p_ancs->number_of_requested_attr = 0;
ancs_req.type = NRF_BLE_GQ_REQ_GATTC_WRITE;
ancs_req.error_handler.cb = p_ancs->gatt_err_handler;
ancs_req.error_handler.p_ctx = p_ancs;
ancs_req.params.gattc_write.handle = p_ancs->service.control_point_char.handle_value;
ancs_req.params.gattc_write.offset = 0;
ancs_req.params.gattc_write.write_op = BLE_GATT_OP_WRITE_REQ;
ancs_req.params.gattc_write.p_value = gattc_value;
//Encode Command ID.
gattc_value[index++] = BLE_ANCS_COMMAND_ID_GET_NOTIF_ATTRIBUTES;
//Encode Notification UID.
index += uint32_encode(p_uid, &(gattc_value[index]));
//Encode Attribute ID.
for (uint32_t attr = 0; attr < BLE_ANCS_NB_OF_NOTIF_ATTR; attr++)
{
if (p_ancs->ancs_notif_attr_list[attr].get == true)
{
gattc_value[index++] = (uint8_t)attr;
if ((attr == BLE_ANCS_NOTIF_ATTR_ID_TITLE) ||
(attr == BLE_ANCS_NOTIF_ATTR_ID_SUBTITLE) ||
(attr == BLE_ANCS_NOTIF_ATTR_ID_MESSAGE))
{
//Encode Length field. Only applicable for Title, Subtitle, and Message.
index += uint16_encode(p_ancs->ancs_notif_attr_list[attr].attr_len,
&(gattc_value[index]));
}
p_ancs->number_of_requested_attr++;
}
}
ancs_req.params.gattc_write.len = index;
p_ancs->parse_info.expected_number_of_attrs = p_ancs->number_of_requested_attr;
return nrf_ble_gq_item_add(p_ancs->p_gatt_queue, &ancs_req, p_ancs->conn_handle);
}
ret_code_t nrf_ble_ancs_c_attr_add(ble_ancs_c_t * p_ancs,
ble_ancs_c_notif_attr_id_val_t const id,
uint8_t * p_data,
uint16_t const len)
{
VERIFY_PARAM_NOT_NULL(p_data);
if ((len == 0) || (len > BLE_ANCS_ATTR_DATA_MAX))
{
return NRF_ERROR_INVALID_LENGTH;
}
p_ancs->ancs_notif_attr_list[id].get = true;
p_ancs->ancs_notif_attr_list[id].attr_len = len;
p_ancs->ancs_notif_attr_list[id].p_attr_data = p_data;
return NRF_SUCCESS;
}
ret_code_t nrf_ble_ancs_c_app_attr_add(ble_ancs_c_t * p_ancs,
ble_ancs_c_app_attr_id_val_t const id,
uint8_t * p_data,
uint16_t const len)
{
VERIFY_PARAM_NOT_NULL(p_ancs);
VERIFY_PARAM_NOT_NULL(p_data);
if ((len == 0) || (len > BLE_ANCS_ATTR_DATA_MAX))
{
return NRF_ERROR_INVALID_LENGTH;
}
p_ancs->ancs_app_attr_list[id].get = true;
p_ancs->ancs_app_attr_list[id].attr_len = len;
p_ancs->ancs_app_attr_list[id].p_attr_data = p_data;
return NRF_SUCCESS;
}
ret_code_t ble_ancs_c_app_attr_remove(ble_ancs_c_t * p_ancs,
ble_ancs_c_app_attr_id_val_t const id)
{
p_ancs->ancs_app_attr_list[id].get = false;
p_ancs->ancs_app_attr_list[id].attr_len = 0;
p_ancs->ancs_app_attr_list[id].p_attr_data = NULL;
return NRF_SUCCESS;
}
ret_code_t ble_ancs_c_notif_attr_remove(ble_ancs_c_t * p_ancs,
ble_ancs_c_notif_attr_id_val_t const id)
{
p_ancs->ancs_notif_attr_list[id].get = false;
p_ancs->ancs_notif_attr_list[id].attr_len = 0;
p_ancs->ancs_notif_attr_list[id].p_attr_data = NULL;
return NRF_SUCCESS;
}
ret_code_t nrf_ble_ancs_c_attr_req_clear_all(ble_ancs_c_t * p_ancs)
{
memset(p_ancs->ancs_notif_attr_list, 0 , sizeof(p_ancs->ancs_notif_attr_list));
memset(p_ancs->ancs_app_attr_list, 0 , sizeof(p_ancs->ancs_app_attr_list));
return NRF_SUCCESS;
}
ret_code_t nrf_ble_ancs_c_request_attrs(ble_ancs_c_t * p_ancs,
ble_ancs_c_evt_notif_t const * p_notif)
{
uint32_t err_code;
err_code = ble_ancs_verify_notification_format(p_notif);
VERIFY_SUCCESS(err_code);
err_code = ble_ancs_get_notif_attrs(p_ancs, p_notif->notif_uid);
p_ancs->parse_info.parse_state = COMMAND_ID;
VERIFY_SUCCESS(err_code);
return NRF_SUCCESS;
}
static uint16_t encode_notif_action(uint8_t * p_encoded_data, uint32_t uid, ble_ancs_c_action_id_values_t action_id)
{
uint8_t index = 0;
p_encoded_data[index++] = BLE_ANCS_COMMAND_ID_GET_PERFORM_NOTIF_ACTION;
index += uint32_encode(uid, &p_encoded_data[index]);
p_encoded_data[index++] = (uint8_t)action_id;
return index;
}
ret_code_t nrf_ancs_perform_notif_action(ble_ancs_c_t * p_ancs, uint32_t uid, ble_ancs_c_action_id_values_t action_id)
{
VERIFY_PARAM_NOT_NULL(p_ancs);
nrf_ble_gq_req_t ancs_req;
uint8_t gattc_value[BLE_ANCS_WRITE_MAX_MSG_LENGTH];
memset(&ancs_req, 0, sizeof(nrf_ble_gq_req_t));
uint16_t len = encode_notif_action(gattc_value, uid, action_id);
ancs_req.type = NRF_BLE_GQ_REQ_GATTC_WRITE;
ancs_req.error_handler.cb = p_ancs->gatt_err_handler;
ancs_req.error_handler.p_ctx = p_ancs;
ancs_req.params.gattc_write.handle = p_ancs->service.control_point_char.handle_value;
ancs_req.params.gattc_write.p_value = gattc_value;
ancs_req.params.gattc_write.offset = 0;
ancs_req.params.gattc_write.write_op = BLE_GATT_OP_WRITE_REQ;
ancs_req.params.gattc_write.len = len;
return nrf_ble_gq_item_add(p_ancs->p_gatt_queue, &ancs_req, p_ancs->conn_handle);
}
ret_code_t nrf_ble_ancs_c_handles_assign(ble_ancs_c_t * p_ancs,
uint16_t const conn_handle,
ble_ancs_c_service_t const * p_peer_handles)
{
VERIFY_PARAM_NOT_NULL(p_ancs);
p_ancs->conn_handle = conn_handle;
if (p_peer_handles != NULL)
{
p_ancs->service.control_point_char.handle_value = p_peer_handles->control_point_char.handle_value;
p_ancs->service.data_source_cccd.handle = p_peer_handles->data_source_cccd.handle;
p_ancs->service.data_source_char.handle_value = p_peer_handles->data_source_char.handle_value;
p_ancs->service.notif_source_cccd.handle = p_peer_handles->notif_source_cccd.handle;
p_ancs->service.notif_source_char.handle_value = p_peer_handles->notif_source_char.handle_value;
}
return nrf_ble_gq_conn_handle_register(p_ancs->p_gatt_queue, conn_handle);
}
#endif// NRF_MODULE_ENABLED(BLE_ANCS_C)

View File

@@ -0,0 +1,620 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_ancs_c Apple Notification Service Client
* @{
* @ingroup ble_sdk_srv
*
* @brief Apple Notification Center Service Client module.
*
* @details Disclaimer: This client implementation of the Apple Notification Center Service can
* be changed at any time by Nordic Semiconductor ASA. Server implementations such as the
* ones found in iOS can be changed at any time by Apple and may cause this client
* implementation to stop working.
*
* This module implements the Apple Notification Center Service (ANCS) client.
* This client can be used as a Notification Consumer (NC) that receives data
* notifications from a Notification Provider (NP). The NP is typically an iOS
* device that is acting as a server. For terminology and up-to-date specs, see
* http://developer.apple.com.
*
* The term "notification" is used in two different meanings:
* - An <i>iOS notification</i> is the data received from the Notification Provider.
* - A <i>GATTC notification</i> is a way to transfer data with <i>Bluetooth</i> Smart.
* In this module, iOS notifications are received through the GATTC notifications.
* The full term (iOS notification or GATTC notification) is used where required to avoid confusion.
*
* Upon initializing the module, you must add the different iOS notification attributes you
* would like to receive for iOS notifications (see @ref nrf_ble_ancs_c_attr_add).
*
* Once a connection is established with a central device, the module does a service discovery to
* discover the ANCS server handles. If this succeeds (@ref BLE_ANCS_C_EVT_DISCOVERY_COMPLETE),
* the handles for the ANCS server are part of the @ref ble_ancs_c_evt_t structure and must be
* assigned to an ANCS_C instance using the @ref nrf_ble_ancs_c_handles_assign function. For more
* information about service discovery, see the @ref lib_ble_db_discovery documentation.
*
* The application can now subscribe to iOS notifications with
* @ref ble_ancs_c_notif_source_notif_enable. The notifications arrive in the @ref BLE_ANCS_C_EVT_NOTIF event.
* @ref nrf_ble_ancs_c_request_attrs can be used to request attributes for the notifications. The attributes
* arrive in the @ref BLE_ANCS_C_EVT_NOTIF_ATTRIBUTE event.
* Use @ref nrf_ble_ancs_c_app_attr_request to request attributes of the app that issued
* the notifications. The app attributes arrive in the @ref BLE_ANCS_C_EVT_APP_ATTRIBUTE event.
* Use @ref nrf_ancs_perform_notif_action to make the Notification Provider perform an
* action based on the provided notification.
*
* @msc
* hscale = "1.5";
* Application, ANCS_C;
* |||;
* Application=>ANCS_C [label = "ble_ancs_c_attr_add(attribute)"];
* Application=>ANCS_C [label = "ble_ancs_c_init(ancs_instance, event_handler)"];
* ...;
* Application<<=ANCS_C [label = "BLE_ANCS_C_EVT_DISCOVERY_COMPLETE"];
* Application=>ANCS_C [label = "ble_ancs_c_handles_assign(ancs_instance, conn_handle, service_handles)"];
* Application=>ANCS_C [label = "ble_ancs_c_notif_source_notif_enable(ancs_instance)"];
* Application=>ANCS_C [label = "ble_ancs_c_data_source_notif_enable(ancs_instance)"];
* |||;
* ...;
* |||;
* Application<<=ANCS_C [label = "BLE_ANCS_C_EVT_NOTIF"];
* |||;
* ...;
* |||;
* Application=>ANCS_C [label = "ble_ancs_c_request_attrs(attr_id, buffer)"];
* Application<<=ANCS_C [label = "BLE_ANCS_C_EVT_NOTIF_ATTRIBUTE"];
* |||;
* @endmsc
*
* @note The application must register this module as the BLE event observer by using the
* NRF_SDH_BLE_OBSERVER macro. Example:
* @code
* ble_ancs_c_t instance;
* NRF_SDH_BLE_OBSERVER(anything, BLE_ANCS_C_BLE_OBSERVER_PRIO,
* ble_ancs_c_on_ble_evt, &instance);
* @endcode
*/
#ifndef BLE_ANCS_C_H__
#define BLE_ANCS_C_H__
#include "ble_types.h"
#include "ble_srv_common.h"
#include "sdk_errors.h"
#include "ble_db_discovery.h"
#include "nrf_sdh_ble.h"
#include "nrf_ble_gq.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Macro for defining a ble_ancs_c instance.
*
* @param _name Name of the instance.
* @hideinitializer
*/
#define BLE_ANCS_C_DEF(_name) \
static ble_ancs_c_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_ANCS_C_BLE_OBSERVER_PRIO, \
ble_ancs_c_on_ble_evt, &_name)
/** @brief Macro for defining multiple ble_ancs_c instances.
*
* @param _name Name of the array of instances.
* @param _cnt Number of instances to define.
* @hideinitializer
*/
#define BLE_ANCS_C_ARRAY_DEF(_name, _cnt) \
sstatic ble_ancs_c_t _name[_cnt]; \
NRF_SDH_BLE_OBSERVERS(_name ## _obs, \
BLE_ANCS_C_BLE_OBSERVER_PRIO, \
ble_ancs_c_on_ble_evt, &_name, _cnt)
#define BLE_ANCS_ATTR_DATA_MAX 32 //!< Maximum data length of an iOS notification attribute.
#define BLE_ANCS_NB_OF_CATEGORY_ID 12 //!< Number of iOS notification categories: Other, Incoming Call, Missed Call, Voice Mail, Social, Schedule, Email, News, Health and Fitness, Business and Finance, Location, Entertainment.
#define BLE_ANCS_NB_OF_NOTIF_ATTR 8 //!< Number of iOS notification attributes: AppIdentifier, Title, Subtitle, Message, MessageSize, Date, PositiveActionLabel, NegativeActionLabel.
#define BLE_ANCS_NB_OF_APP_ATTR 1 //!< Number of iOS application attributes: DisplayName.
#define BLE_ANCS_NB_OF_EVT_ID 3 //!< Number of iOS notification events: Added, Modified, Removed.
/** @brief Length of the iOS notification data.
*
* @details 8 bytes:
* Event ID |Event flags |Category ID |Category count|Notification UID
* ---------|------------|------------|--------------|----------------
* 1 byte | 1 byte | 1 byte | 1 byte | 4 bytes
*/
#define BLE_ANCS_NOTIFICATION_DATA_LENGTH 8
#define ANCS_UUID_SERVICE 0xF431 //!< 16-bit service UUID for the Apple Notification Center Service.
#define ANCS_UUID_CHAR_CONTROL_POINT 0xD8F3 //!< 16-bit control point UUID.
#define ANCS_UUID_CHAR_DATA_SOURCE 0xC6E9 //!< 16-bit data source UUID.
#define ANCS_UUID_CHAR_NOTIFICATION_SOURCE 0x120D //!< 16-bit notification source UUID.
#define BLE_ANCS_EVENT_FLAG_SILENT 0 //!< 0b.......1 Silent: First (LSB) bit is set. All flags can be active at the same time.
#define BLE_ANCS_EVENT_FLAG_IMPORTANT 1 //!< 0b......1. Important: Second (LSB) bit is set. All flags can be active at the same time.
#define BLE_ANCS_EVENT_FLAG_PREEXISTING 2 //!< 0b.....1.. Pre-existing: Third (LSB) bit is set. All flags can be active at the same time.
#define BLE_ANCS_EVENT_FLAG_POSITIVE_ACTION 3 //!< 0b....1... Positive action: Fourth (LSB) bit is set. All flags can be active at the same time.
#define BLE_ANCS_EVENT_FLAG_NEGATIVE_ACTION 4 //!< 0b...1.... Negative action: Fifth (LSB) bit is set. All flags can be active at the same time.
/** @defgroup BLE_ANCS_NP_ERROR_CODES Notification Provider (iOS) Error Codes
* @{ */
#define BLE_ANCS_NP_UNKNOWN_COMMAND 0x01A0 //!< The command ID is unknown to the NP.
#define BLE_ANCS_NP_INVALID_COMMAND 0x01A1 //!< The command format is invalid.
#define BLE_ANCS_NP_INVALID_PARAMETER 0x01A2 //!< One or more parameters do not exist in the NP.
#define BLE_ANCS_NP_ACTION_FAILED 0x01A3 //!< The action failed to be performed by the NP.
/** @} */
/**@brief Event types that are passed from client to application on an event. */
typedef enum
{
BLE_ANCS_C_EVT_DISCOVERY_COMPLETE, /**< A successful connection has been established and the service was found on the connected peer. */
BLE_ANCS_C_EVT_DISCOVERY_FAILED, /**< It was not possible to discover the service or characteristics of the connected peer. */
BLE_ANCS_C_EVT_NOTIF, /**< An iOS notification was received on the notification source control point. */
BLE_ANCS_C_EVT_INVALID_NOTIF, /**< An iOS notification was received on the notification source control point, but the format is invalid. */
BLE_ANCS_C_EVT_NOTIF_ATTRIBUTE, /**< A received iOS notification attribute has been parsed. */
BLE_ANCS_C_EVT_APP_ATTRIBUTE, /**< An iOS app attribute has been parsed. */
BLE_ANCS_C_EVT_NP_ERROR, /**< An error has been sent on the ANCS Control Point from the iOS Notification Provider. */
} ble_ancs_c_evt_type_t;
/**@brief Category IDs for iOS notifications. */
typedef enum
{
BLE_ANCS_CATEGORY_ID_OTHER, /**< The iOS notification belongs to the "Other" category. */
BLE_ANCS_CATEGORY_ID_INCOMING_CALL, /**< The iOS notification belongs to the "Incoming Call" category. */
BLE_ANCS_CATEGORY_ID_MISSED_CALL, /**< The iOS notification belongs to the "Missed Call" category. */
BLE_ANCS_CATEGORY_ID_VOICE_MAIL, /**< The iOS notification belongs to the "Voice Mail" category. */
BLE_ANCS_CATEGORY_ID_SOCIAL, /**< The iOS notification belongs to the "Social" category. */
BLE_ANCS_CATEGORY_ID_SCHEDULE, /**< The iOS notification belongs to the "Schedule" category. */
BLE_ANCS_CATEGORY_ID_EMAIL, /**< The iOS notification belongs to the "Email" category. */
BLE_ANCS_CATEGORY_ID_NEWS, /**< The iOS notification belongs to the "News" category. */
BLE_ANCS_CATEGORY_ID_HEALTH_AND_FITNESS, /**< The iOS notification belongs to the "Health and Fitness" category. */
BLE_ANCS_CATEGORY_ID_BUSINESS_AND_FINANCE, /**< The iOS notification belongs to the "Business and Finance" category. */
BLE_ANCS_CATEGORY_ID_LOCATION, /**< The iOS notification belongs to the "Location" category. */
BLE_ANCS_CATEGORY_ID_ENTERTAINMENT /**< The iOS notification belongs to the "Entertainment" category. */
} ble_ancs_c_category_id_val_t;
/**@brief Event IDs for iOS notifications. */
typedef enum
{
BLE_ANCS_EVENT_ID_NOTIFICATION_ADDED, /**< The iOS notification was added. */
BLE_ANCS_EVENT_ID_NOTIFICATION_MODIFIED, /**< The iOS notification was modified. */
BLE_ANCS_EVENT_ID_NOTIFICATION_REMOVED /**< The iOS notification was removed. */
} ble_ancs_c_evt_id_values_t;
/**@brief Control point command IDs that the Notification Consumer can send to the Notification Provider. */
typedef enum
{
BLE_ANCS_COMMAND_ID_GET_NOTIF_ATTRIBUTES, /**< Requests attributes to be sent from the NP to the NC for a given notification. */
BLE_ANCS_COMMAND_ID_GET_APP_ATTRIBUTES, /**< Requests attributes to be sent from the NP to the NC for a given iOS app. */
BLE_ANCS_COMMAND_ID_GET_PERFORM_NOTIF_ACTION, /**< Requests an action to be performed on a given notification. For example, dismiss an alarm. */
} ble_ancs_c_cmd_id_val_t;
/**@brief IDs for actions that can be performed for iOS notifications. */
typedef enum
{
ACTION_ID_POSITIVE = 0, /**< Positive action. */
ACTION_ID_NEGATIVE /**< Negative action. */
} ble_ancs_c_action_id_values_t;
/**@brief App attribute ID values.
* @details Currently, only one value is defined. However, the number of app
* attributes might increase. For this reason, they are stored in an enumeration.
*/
typedef enum
{
BLE_ANCS_APP_ATTR_ID_DISPLAY_NAME = 0 /**< Command used to get the display name for an app identifier. */
} ble_ancs_c_app_attr_id_val_t;
/**@brief IDs for iOS notification attributes. */
typedef enum
{
BLE_ANCS_NOTIF_ATTR_ID_APP_IDENTIFIER = 0, /**< Identifies that the attribute data is of an "App Identifier" type. */
BLE_ANCS_NOTIF_ATTR_ID_TITLE, /**< Identifies that the attribute data is a "Title". */
BLE_ANCS_NOTIF_ATTR_ID_SUBTITLE, /**< Identifies that the attribute data is a "Subtitle". */
BLE_ANCS_NOTIF_ATTR_ID_MESSAGE, /**< Identifies that the attribute data is a "Message". */
BLE_ANCS_NOTIF_ATTR_ID_MESSAGE_SIZE, /**< Identifies that the attribute data is a "Message Size". */
BLE_ANCS_NOTIF_ATTR_ID_DATE, /**< Identifies that the attribute data is a "Date". */
BLE_ANCS_NOTIF_ATTR_ID_POSITIVE_ACTION_LABEL, /**< The notification has a "Positive action" that can be executed associated with it. */
BLE_ANCS_NOTIF_ATTR_ID_NEGATIVE_ACTION_LABEL, /**< The notification has a "Negative action" that can be executed associated with it. */
} ble_ancs_c_notif_attr_id_val_t;
/**@brief Flags for iOS notifications. */
typedef struct
{
uint8_t silent : 1; //!< If this flag is set, the notification has a low priority.
uint8_t important : 1; //!< If this flag is set, the notification has a high priority.
uint8_t pre_existing : 1; //!< If this flag is set, the notification is pre-existing.
uint8_t positive_action : 1; //!< If this flag is set, the notification has a positive action that can be taken.
uint8_t negative_action : 1; //!< If this flag is set, the notification has a negative action that can be taken.
} ble_ancs_c_notif_flags_t;
/**@brief Parsing states for received iOS notification and app attributes. */
typedef enum
{
COMMAND_ID, /**< Parsing the command ID. */
NOTIF_UID, /**< Parsing the notification UID. */
APP_ID, /**< Parsing app ID. */
ATTR_ID, /**< Parsing attribute ID. */
ATTR_LEN1, /**< Parsing the LSB of the attribute length. */
ATTR_LEN2, /**< Parsing the MSB of the attribute length. */
ATTR_DATA, /**< Parsing the attribute data. */
ATTR_SKIP, /**< Parsing is skipped for the rest of an attribute (or entire attribute). */
DONE, /**< Parsing for one attribute is done. */
} ble_ancs_c_parse_state_t;
/**@brief iOS notification structure. */
typedef struct
{
uint32_t notif_uid; //!< Notification UID.
ble_ancs_c_evt_id_values_t evt_id; //!< Whether the notification was added, removed, or modified.
ble_ancs_c_notif_flags_t evt_flags; //!< Bitmask to signal whether a special condition applies to the notification. For example, "Silent" or "Important".
ble_ancs_c_category_id_val_t category_id; //!< Classification of the notification type. For example, email or location.
uint8_t category_count; //!< Current number of active notifications for this category ID.
} ble_ancs_c_evt_notif_t;
/**@brief iOS attribute structure. This type is used for both notification attributes and app attributes. */
typedef struct
{
uint16_t attr_len; //!< Length of the received attribute data.
uint32_t attr_id; //!< Classification of the attribute type. For example, "Title" or "Date".
uint8_t * p_attr_data; //!< Pointer to where the memory is allocated for storing incoming attributes.
} ble_ancs_c_attr_t;
/**@brief iOS notification attribute structure for incoming attributes. */
typedef struct
{
uint32_t notif_uid; //!< UID of the notification that the attribute belongs to.
ble_ancs_c_attr_t attrs; //!< A received attribute.
} ble_ancs_c_evt_attr_t;
typedef struct
{
uint16_t attr_len; //!< Length of the received attribute data.
uint32_t attr_id; //!< Classification of the attribute type. For example, "Title" or "Date".
uint8_t * p_attr_data; //!< Pointer to where the memory is allocated for storing incoming attributes.
} ble_ancs_c_evt_app_attr_t;
/**@brief iOS notification attribute content requested by the application. */
typedef struct
{
bool get; //!< Boolean to determine whether this attribute will be requested from the Notification Provider.
uint32_t attr_id; //!< Attribute ID: AppIdentifier(0), Title(1), Subtitle(2), Message(3), MessageSize(4), Date(5), PositiveActionLabel(6), NegativeActionLabel(7).
uint16_t attr_len; //!< Length of the attribute. If more data is received from the Notification Provider, all the data beyond this length is discarded.
uint8_t * p_attr_data; //!< Pointer to where the memory is allocated for storing incoming attributes.
} ble_ancs_c_attr_list_t;
/**@brief Structure used for holding the Apple Notification Center Service found during the
discovery process.
*/
typedef struct
{
ble_gattc_service_t service; //!< The GATT Service holding the discovered Apple Notification Center Service. (0xF431).
ble_gattc_char_t control_point_char; //!< ANCS Control Point Characteristic. Allows interaction with the peer (0xD8F3).
ble_gattc_char_t notif_source_char; //!< ANCS Notification Source Characteristic. Keeps track of arrival, modification, and removal of notifications (0x120D).
ble_gattc_desc_t notif_source_cccd; //!< ANCS Notification Source Characteristic Descriptor. Enables or disables GATT notifications.
ble_gattc_char_t data_source_char; //!< ANCS Data Source Characteristic, where attribute data for the notifications is received from peer (0xC6E9).
ble_gattc_desc_t data_source_cccd; //!< ANCS Data Source Characteristic Descriptor. Enables or disables GATT notifications.
} ble_ancs_c_service_t;
/**@brief ANCS client module event structure.
*
* @details The structure contains the event that is to be handled by the main application.
*/
typedef struct
{
ble_ancs_c_evt_type_t evt_type; //!< Type of event.
uint16_t conn_handle; //!< Connection handle on which the ANCS service was discovered on the peer device. This is filled if the @p evt_type is @ref BLE_ANCS_C_EVT_DISCOVERY_COMPLETE.
ble_ancs_c_evt_notif_t notif; //!< iOS notification. This is filled if @p evt_type is @ref BLE_ANCS_C_EVT_NOTIF.
uint16_t err_code_np; //!< An error coming from the Notification Provider. This is filled with @ref BLE_ANCS_NP_ERROR_CODES if @p evt_type is @ref BLE_ANCS_C_EVT_NP_ERROR.
ble_ancs_c_attr_t attr; //!< iOS notification attribute or app attribute, depending on the event type.
uint32_t notif_uid; //!< Notification UID.
uint8_t app_id[BLE_ANCS_ATTR_DATA_MAX]; //!< App identifier.
ble_ancs_c_service_t service; //!< Information on the discovered Alert Notification Service. This is filled if the @p evt_type is @ref BLE_ANCS_C_EVT_DISCOVERY_COMPLETE.
} ble_ancs_c_evt_t;
/**@brief iOS notification event handler type. */
typedef void (*ble_ancs_c_evt_handler_t) (ble_ancs_c_evt_t * p_evt);
typedef struct
{
ble_ancs_c_attr_list_t * p_attr_list; //!< The current list of attributes that are being parsed. This will point to either @ref ble_ancs_c_t::ancs_notif_attr_list or @ref ble_ancs_c_t::ancs_app_attr_list.
uint32_t nb_of_attr; //!< Number of possible attributes. When parsing begins, it is set to either @ref BLE_ANCS_NB_OF_NOTIF_ATTR or @ref BLE_ANCS_NB_OF_APP_ATTR.
uint32_t expected_number_of_attrs; //!< The number of attributes expected upon receiving attributes. Keeps track of when to stop reading incoming attributes.
ble_ancs_c_parse_state_t parse_state; //!< ANCS notification attribute parsing state.
ble_ancs_c_cmd_id_val_t command_id; //!< Variable to keep track of what command type is being parsed ( @ref BLE_ANCS_COMMAND_ID_GET_NOTIF_ATTRIBUTES or @ref BLE_ANCS_COMMAND_ID_GET_APP_ATTRIBUTES).
uint8_t * p_data_dest; //!< Attribute that the parsed data is copied into.
uint16_t current_attr_index; //!< Variable to keep track of the parsing progress, for the given attribute.
uint32_t current_app_id_index; //!< Variable to keep track of the parsing progress, for the given app identifier.
} ble_ancs_parse_sm_t;
/**@brief iOS notification structure, which contains various status information for the client. */
typedef struct
{
ble_ancs_c_evt_handler_t evt_handler; //!< Event handler to be called for handling events in the Apple Notification client application.
ble_srv_error_handler_t error_handler; //!< Function to be called in case of an error.
nrf_ble_gq_req_error_cb_t gatt_err_handler; //!< Error handler to be called in case of an error from SoftDevice.
uint16_t conn_handle; //!< Handle of the current connection. Set with @ref nrf_ble_ancs_c_handles_assign when connected.
ble_ancs_c_service_t service; //!< Structure to store the different handles and UUIDs related to the service.
ble_ancs_c_attr_list_t ancs_notif_attr_list[BLE_ANCS_NB_OF_NOTIF_ATTR]; //!< For all attributes: contains information about whether the attributes are to be requested upon attribute request, and the length and buffer of where to store attribute data.
ble_ancs_c_attr_list_t ancs_app_attr_list[BLE_ANCS_NB_OF_APP_ATTR]; //!< For all app attributes: contains information about whether the attributes are to be requested upon attribute request, and the length and buffer of where to store attribute data.
uint32_t number_of_requested_attr; //!< The number of attributes that are to be requested when an iOS notification attribute request is made.
ble_ancs_parse_sm_t parse_info; //!< Structure containing different information used to parse incoming attributes correctly (from data_source characteristic).
ble_ancs_c_evt_t evt; //!< Allocate memory for the event here. The event is filled with several iterations of the @ref ancs_parse_get_attrs_response function when requesting iOS notification attributes.
nrf_ble_gq_t * p_gatt_queue; //!< Pointer to the BLE GATT Queue instance.
} ble_ancs_c_t;
/**@brief Apple Notification client init structure, which contains all options and data needed for
* initialization of the client. */
typedef struct
{
ble_ancs_c_evt_handler_t evt_handler; //!< Event handler to be called for handling events in the Battery Service.
ble_srv_error_handler_t error_handler; //!< Function to be called in case of an error.
nrf_ble_gq_t * p_gatt_queue; //!< Pointer to the BLE GATT Queue instance.
} ble_ancs_c_init_t;
/**@brief Apple Notification Center Service UUIDs. */
extern const ble_uuid128_t ble_ancs_base_uuid128; //!< Service UUID.
extern const ble_uuid128_t ble_ancs_cp_base_uuid128; //!< Control Point UUID.
extern const ble_uuid128_t ble_ancs_ns_base_uuid128; //!< Notification Source UUID.
extern const ble_uuid128_t ble_ancs_ds_base_uuid128; //!< Data Source UUID.
/**@brief Function for handling BLE stack events of the application.
*
* @details Handles all events from the BLE stack that are of interest to the ANCS client.
*
* @param[in] p_ble_evt Event received from the BLE stack.
* @param[in] p_context ANCS client structure.
*/
void ble_ancs_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for handling events from the Database Discovery module.
*
* @details This function handles an event from the Database Discovery module and determines
* whether it relates to the discovery of ANCS at the peer. If it does, this function
* calls the application's event handler to indicate that ANCS was
* discovered at the peer. The function also populates the event with service-related
* information before providing it to the application.
*
* @param[in] p_ancs Pointer to the ANCS client structure.
* @param[in] p_evt Pointer to the event received from the Database Discovery module.
*/
void ble_ancs_c_on_db_disc_evt(ble_ancs_c_t * p_ancs, ble_db_discovery_evt_t * p_evt);
/**@brief Function for initializing the ANCS client.
*
* @param[out] p_ancs ANCS client structure. This structure must be
* supplied by the application. It is initialized by this function
* and will later be used to identify this particular client instance.
* @param[in] p_ancs_init Information needed to initialize the client.
*
* @retval NRF_SUCCESS If the client was initialized successfully. Otherwise, an error code is returned.
*/
ret_code_t ble_ancs_c_init(ble_ancs_c_t * p_ancs, ble_ancs_c_init_t const * p_ancs_init);
/**@brief Function for writing to the CCCD to enable notifications from the Apple Notification Service.
*
* @param[in] p_ancs iOS notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
*
* @retval NRF_SUCCESS If writing to the CCCD was successful. Otherwise,
* this API propagates the error code returned by function
* @ref nrf_ble_gq_item_add.
*/
ret_code_t ble_ancs_c_notif_source_notif_enable(ble_ancs_c_t const * p_ancs);
/**@brief Function for writing to the CCCD to enable data source notifications from the ANCS.
*
* @param[in] p_ancs iOS notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
*
* @retval NRF_SUCCESS If writing to the CCCD was successful. Otherwise,
* this API propagates the error code returned by function
* @ref nrf_ble_gq_item_add.
*/
ret_code_t ble_ancs_c_data_source_notif_enable(ble_ancs_c_t const * p_ancs);
/**@brief Function for writing to the CCCD to disable notifications from the ANCS.
*
* @param[in] p_ancs iOS notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
*
* @retval NRF_SUCCESS If writing to the CCCD was successful. Otherwise
* this API propagates the error code returned by function
* @ref nrf_ble_gq_item_add.
*/
ret_code_t ble_ancs_c_notif_source_notif_disable(ble_ancs_c_t const * p_ancs);
/**@brief Function for writing to the CCCD to disable data source notifications from the ANCS.
*
* @param[in] p_ancs iOS notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
*
* @retval NRF_SUCCESS If writing to the CCCD was successful. Otherwise,
* this API propagates the error code returned by function
* @ref nrf_ble_gq_item_add.
*/
ret_code_t ble_ancs_c_data_source_notif_disable(ble_ancs_c_t const * p_ancs);
/**@brief Function for registering attributes that will be requested when @ref nrf_ble_ancs_c_request_attrs
* is called.
*
* @param[in] p_ancs ANCS client instance on which the attribute is registered.
* @param[in] id ID of the attribute that is added.
* @param[in] p_data Pointer to the buffer where the data of the attribute can be stored.
* @param[in] len Length of the buffer where the data of the attribute can be stored.
* @retval NRF_SUCCESS If all operations are successful. Otherwise, an error code is returned.
*/
ret_code_t nrf_ble_ancs_c_attr_add(ble_ancs_c_t * p_ancs,
ble_ancs_c_notif_attr_id_val_t const id,
uint8_t * p_data,
uint16_t const len);
/**@brief Function for removing attributes, so that they are no longer requested when
* @ref nrf_ble_ancs_c_request_attrs is called.
*
* @param[in] p_ancs ANCS client instance on which the attribute is removed.
* @param[in] id ID of the attribute that is removed.
*
* @retval NRF_SUCCESS If all operations are successful. Otherwise, an error code is returned.
*/
ret_code_t nrf_ble_ancs_c_attr_remove(ble_ancs_c_t * p_ancs,
ble_ancs_c_notif_attr_id_val_t const id);
/**@brief Function for removing attributes, so that they are no longer requested when
* @ref nrf_ble_ancs_c_app_attr_request is called.
*
* @param[in] p_ancs ANCS client instance on which the attribute is removed.
* @param[in] id ID of the attribute that is removed.
*
* @retval NRF_SUCCESS If all operations are successful. Otherwise, an error code is returned.
*/
ret_code_t nrf_ble_ancs_c_app_attr_remove(ble_ancs_c_t * p_ancs,
ble_ancs_c_app_attr_id_val_t const id);
/**@brief Function for registering attributes that will be requested when @ref nrf_ble_ancs_c_app_attr_request
* is called.
*
* @param[in] p_ancs ANCS client instance on which the attribute is registered.
* @param[in] id ID of the attribute that is added.
* @param[in] p_data Pointer to the buffer where the data of the attribute can be stored.
* @param[in] len Length of the buffer where the data of the attribute can be stored.
*
* @retval NRF_SUCCESS If all operations are successful. Otherwise, an error code is returned.
*/
ret_code_t nrf_ble_ancs_c_app_attr_add(ble_ancs_c_t * p_ancs,
ble_ancs_c_app_attr_id_val_t const id,
uint8_t * p_data,
uint16_t const len);
/**@brief Function for clearing the list of notification attributes and app attributes that
* would be requested from NP.
*
* @param[in] p_ancs iOS notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
**/
ret_code_t nrf_ble_ancs_c_attr_req_clear_all(ble_ancs_c_t * p_ancs);
/**@brief Function for requesting attributes for a notification.
*
* @param[in] p_ancs iOS notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
* @param[in] p_notif Pointer to the notification whose attributes will be requested from
* the Notification Provider.
*
* @retval NRF_SUCCESS If all operations are successful. Otherwise, an error code is returned.
*/
ret_code_t nrf_ble_ancs_c_request_attrs(ble_ancs_c_t * p_ancs,
ble_ancs_c_evt_notif_t const * p_notif);
/**@brief Function for requesting attributes for a given app.
*
* @param[in] p_ancs iOS notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
* @param[in] p_app_id App identifier of the app for which the app attributes are requested.
* @param[in] len Length of the app identifier.
*
* @retval NRF_SUCCESS If all operations are successful. Otherwise, an error code is returned.
*/
ret_code_t nrf_ble_ancs_c_app_attr_request(ble_ancs_c_t * p_ancs,
uint8_t const * p_app_id,
uint32_t len);
/**@brief Function for performing a notification action.
*
* @param[in] p_ancs iOS notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
* @param[in] uuid The UUID of the notification for which to perform the action.
* @param[in] action_id Perform a positive or negative action.
*
* @retval NRF_SUCCESS If the operation is successful.
* @retval NRF_ERROR_NULL If @p p_ancs is a NULL pointer.
* @retval err_code Otherwise, this API propagates the error code returned by function
* @ref nrf_ble_gq_item_add.
*/
ret_code_t nrf_ancs_perform_notif_action(ble_ancs_c_t * p_ancs,
uint32_t uuid,
ble_ancs_c_action_id_values_t action_id);
/**@brief Function for assigning a handle to this instance of ancs_c.
*
* @details Call this function when a link has been established with a peer to
* associate the link to this instance of the module. This makes it
* possible to handle several links and associate each link to a particular
* instance of this module. The connection handle and attribute handles are
* provided from the discovery event @ref BLE_ANCS_C_EVT_DISCOVERY_COMPLETE.
*
* @param[in] p_ancs Pointer to the ANCS client structure instance for associating the link.
* @param[in] conn_handle Connection handle to associated with the given ANCS instance.
* @param[in] p_service Attribute handles on the ANCS server that you want this ANCS client to
* interact with.
*
* @retval NRF_SUCCESS If the operation is successful.
* @retval NRF_ERROR_NULL If @p p_ancs is a NULL pointer.
* @retval err_code Otherwise, this API propagates the error code returned by function
* @ref nrf_ble_gq_conn_handle_register.
*/
ret_code_t nrf_ble_ancs_c_handles_assign(ble_ancs_c_t * p_ancs,
uint16_t const conn_handle,
ble_ancs_c_service_t const * p_service);
#ifdef __cplusplus
}
#endif
#endif // BLE_ANCS_C_H__
/** @} */

View File

@@ -0,0 +1,530 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* Attention!
* To maintain compliance with Nordic Semiconductor ASA's Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_ANS_C)
#include "ble_ans_c.h"
#include <string.h>
#include <stdbool.h>
#include "ble_err.h"
#include "nrf_assert.h"
#include "ble_db_discovery.h"
#define NRF_LOG_MODULE_NAME ble_ans_c
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();
#define NOTIFICATION_DATA_LENGTH 2 /**< The mandatory length of the notification data. After the mandatory data, the optional message is located. */
#define READ_DATA_LENGTH_MIN 1 /**< Minimum data length in a valid Alert Notification Read Response message. */
#define WRITE_MESSAGE_LENGTH 2 /**< Length of the write message for CCCD and control point. */
/**@brief Function for intercepting GATTC and @ref nrf_ble_gq errors.
*
* @param[in] nrf_error Error code.
* @param[in] p_ctx Parameter from the event handler.
* @param[in] conn_handle Connection handle.
*/
static void gatt_error_handler(uint32_t nrf_error,
void * p_ctx,
uint16_t conn_handle)
{
ble_ans_c_t * p_ans = (ble_ans_c_t *)p_ctx;
NRF_LOG_DEBUG("A GATT Client error has occurred on conn_handle: 0X%X", conn_handle);
if (p_ans->error_handler != NULL)
{
p_ans->error_handler(nrf_error);
}
}
/** @brief Function for copying a characteristic.
*/
static void char_set(ble_gattc_char_t * p_dest_char, ble_gattc_char_t const * p_source_char)
{
memcpy(p_dest_char, p_source_char, sizeof(ble_gattc_char_t));
}
static void char_cccd_set(ble_gattc_desc_t * p_cccd, uint16_t cccd_handle)
{
p_cccd->handle = cccd_handle;
}
/** @brief Function for checking the presence of all the handles required by the client to use the server.
*/
static bool is_valid_ans_srv_discovered(ble_ans_c_service_t const * p_srv)
{
if ((p_srv->alert_notif_ctrl_point.handle_value == BLE_GATT_HANDLE_INVALID) ||
(p_srv->suported_new_alert_cat.handle_value == BLE_GATT_HANDLE_INVALID) ||
(p_srv->suported_unread_alert_cat.handle_value == BLE_GATT_HANDLE_INVALID) ||
(p_srv->new_alert.handle_value == BLE_GATT_HANDLE_INVALID) ||
(p_srv->unread_alert_status.handle_value == BLE_GATT_HANDLE_INVALID) ||
(p_srv->new_alert_cccd.handle == BLE_GATT_HANDLE_INVALID) ||
(p_srv->unread_alert_cccd.handle == BLE_GATT_HANDLE_INVALID)
)
{
// At least one required characteristic is missing on the server side.
return false;
}
return true;
}
void ble_ans_c_on_db_disc_evt(ble_ans_c_t * p_ans, ble_db_discovery_evt_t const * p_evt)
{
ble_ans_c_evt_t evt;
memset(&evt, 0, sizeof(ble_ans_c_evt_t));
evt.conn_handle = p_evt->conn_handle;
// Check if the Alert Notification Service is discovered.
if (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE
&&
p_evt->params.discovered_db.srv_uuid.uuid == BLE_UUID_ALERT_NOTIFICATION_SERVICE
&&
p_evt->params.discovered_db.srv_uuid.type == BLE_UUID_TYPE_BLE)
{
// Find the characteristics inside the service.
for (uint8_t i = 0; i < p_evt->params.discovered_db.char_count; i++)
{
ble_gatt_db_char_t const * p_char = &(p_evt->params.discovered_db.charateristics[i]);
switch (p_char->characteristic.uuid.uuid)
{
case BLE_UUID_ALERT_NOTIFICATION_CONTROL_POINT_CHAR:
NRF_LOG_DEBUG("Found Ctrlpt \r\n\r");
char_set(&evt.data.service.alert_notif_ctrl_point, &p_char->characteristic);
break;
case BLE_UUID_UNREAD_ALERT_CHAR:
NRF_LOG_DEBUG("Found Unread Alert \r\n\r");
char_set(&evt.data.service.unread_alert_status, &p_char->characteristic);
char_cccd_set(&evt.data.service.unread_alert_cccd,
p_char->cccd_handle);
break;
case BLE_UUID_NEW_ALERT_CHAR:
NRF_LOG_DEBUG("Found New Alert \r\n\r");
char_set(&evt.data.service.new_alert, &p_char->characteristic);
char_cccd_set(&evt.data.service.new_alert_cccd,
p_char->cccd_handle);
break;
case BLE_UUID_SUPPORTED_UNREAD_ALERT_CATEGORY_CHAR:
NRF_LOG_DEBUG("Found supported unread alert category \r\n\r");
char_set(&evt.data.service.suported_unread_alert_cat, &p_char->characteristic);
break;
case BLE_UUID_SUPPORTED_NEW_ALERT_CATEGORY_CHAR:
NRF_LOG_DEBUG("Found supported new alert category \r\n\r");
char_set(&evt.data.service.suported_new_alert_cat, &p_char->characteristic);
break;
default:
// No implementation needed.
break;
}
}
if (is_valid_ans_srv_discovered(&evt.data.service))
{
evt.evt_type = BLE_ANS_C_EVT_DISCOVERY_COMPLETE;
}
}
else if ((p_evt->evt_type == BLE_DB_DISCOVERY_SRV_NOT_FOUND) ||
(p_evt->evt_type == BLE_DB_DISCOVERY_ERROR))
{
evt.evt_type = BLE_ANS_C_EVT_DISCOVERY_FAILED;
}
else
{
return;
}
p_ans->evt_handler(&evt);
}
/**@brief Function for receiving and validating notifications received from the central.
*/
static void event_notify(ble_ans_c_t * p_ans, ble_evt_t const * p_ble_evt)
{
uint32_t message_length;
ble_ans_c_evt_t event;
ble_ans_alert_notification_t * p_alert = &event.data.alert;
ble_gattc_evt_hvx_t const * p_notification = &p_ble_evt->evt.gattc_evt.params.hvx;
// If the message is not valid, then ignore.
event.evt_type = BLE_ANS_C_EVT_NOTIFICATION;
if (p_notification->len < NOTIFICATION_DATA_LENGTH)
{
return;
}
message_length = p_notification->len - NOTIFICATION_DATA_LENGTH;
if (p_notification->handle == p_ans->service.new_alert.handle_value)
{
BLE_UUID_COPY_INST(event.uuid, p_ans->service.new_alert.uuid);
}
else if (p_notification->handle == p_ans->service.unread_alert_status.handle_value)
{
BLE_UUID_COPY_INST(event.uuid, p_ans->service.unread_alert_status.uuid);
}
else
{
// Nothing to process.
return;
}
p_alert->alert_category = p_notification->data[0];
p_alert->alert_category_count = p_notification->data[1]; //lint !e415
p_alert->alert_msg_length = (message_length > p_ans->message_buffer_size)
? p_ans->message_buffer_size
: message_length;
p_alert->p_alert_msg_buf = p_ans->p_message_buffer;
memcpy(p_alert->p_alert_msg_buf,
&p_notification->data[NOTIFICATION_DATA_LENGTH],
p_alert->alert_msg_length); //lint !e416
p_ans->evt_handler(&event);
}
/**@brief Function for validating and passing the response to the application,
* when a read response is received.
*/
static void event_read_rsp(ble_ans_c_t * p_ans, ble_evt_t const * p_ble_evt)
{
ble_ans_c_evt_t event;
ble_gattc_evt_read_rsp_t const * p_response;
p_response = &p_ble_evt->evt.gattc_evt.params.read_rsp;
event.evt_type = BLE_ANS_C_EVT_READ_RESP;
if (p_response->len < READ_DATA_LENGTH_MIN)
{
return;
}
if (p_response->handle == p_ans->service.suported_new_alert_cat.handle_value)
{
BLE_UUID_COPY_INST(event.uuid, p_ans->service.suported_new_alert_cat.uuid);
}
else if (p_response->handle == p_ans->service.suported_unread_alert_cat.handle_value)
{
BLE_UUID_COPY_INST(event.uuid, p_ans->service.suported_unread_alert_cat.uuid);
}
else
{
// If the response is not valid, then ignore.
return;
}
event.data.settings = *(ble_ans_alert_settings_t *)(p_response->data);
if (p_response->len == READ_DATA_LENGTH_MIN)
{
// These variables must default to 0, if they are not returned by central.
event.data.settings.ans_high_prioritized_alert_support = 0;
event.data.settings.ans_instant_message_support = 0;
}
p_ans->evt_handler(&event);
}
/**@brief Function for disconnecting and cleaning the current service.
*/
static void event_disconnect(ble_ans_c_t * p_ans, ble_evt_t const * p_ble_evt)
{
if (p_ans->conn_handle == p_ble_evt->evt.gap_evt.conn_handle)
{
p_ans->conn_handle = BLE_CONN_HANDLE_INVALID;
// Clearing all data for the service also sets all handle values to @ref BLE_GATT_HANDLE_INVALID
memset(&p_ans->service, 0, sizeof(ble_ans_c_service_t));
// If there was a valid instance of IAS on the peer, send an event to the
// application, so that it can do any cleanup related to this module.
ble_ans_c_evt_t evt;
evt.evt_type = BLE_ANS_C_EVT_DISCONN_COMPLETE;
p_ans->evt_handler(&evt);
}
}
/**@brief Function for handling of BLE stack events. */
void ble_ans_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
ble_ans_c_t * p_ans = (ble_ans_c_t *)p_context;
switch (p_ble_evt->header.evt_id)
{
case BLE_GATTC_EVT_HVX:
event_notify(p_ans, p_ble_evt);
break;
case BLE_GATTC_EVT_READ_RSP:
event_read_rsp(p_ans, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
event_disconnect(p_ans, p_ble_evt);
break;
}
}
uint32_t ble_ans_c_init(ble_ans_c_t * p_ans, ble_ans_c_init_t const * p_ans_init)
{
VERIFY_PARAM_NOT_NULL(p_ans);
VERIFY_PARAM_NOT_NULL(p_ans_init);
VERIFY_PARAM_NOT_NULL(p_ans_init->evt_handler);
VERIFY_PARAM_NOT_NULL(p_ans_init->p_gatt_queue);
// Clear all handles.
memset(p_ans, 0, sizeof(ble_ans_c_t));
p_ans->conn_handle = BLE_CONN_HANDLE_INVALID;
p_ans->evt_handler = p_ans_init->evt_handler;
p_ans->error_handler = p_ans_init->error_handler;
p_ans->message_buffer_size = p_ans_init->message_buffer_size;
p_ans->p_message_buffer = p_ans_init->p_message_buffer;
p_ans->p_gatt_queue = p_ans_init->p_gatt_queue;
BLE_UUID_BLE_ASSIGN(p_ans->service.service.uuid, BLE_UUID_ALERT_NOTIFICATION_SERVICE);
BLE_UUID_BLE_ASSIGN(p_ans->service.new_alert.uuid, BLE_UUID_NEW_ALERT_CHAR);
BLE_UUID_BLE_ASSIGN(p_ans->service.alert_notif_ctrl_point.uuid,
BLE_UUID_ALERT_NOTIFICATION_CONTROL_POINT_CHAR);
BLE_UUID_BLE_ASSIGN(p_ans->service.unread_alert_status.uuid, BLE_UUID_UNREAD_ALERT_CHAR);
BLE_UUID_BLE_ASSIGN(p_ans->service.suported_new_alert_cat.uuid,
BLE_UUID_SUPPORTED_NEW_ALERT_CATEGORY_CHAR);
BLE_UUID_BLE_ASSIGN(p_ans->service.suported_unread_alert_cat.uuid,
BLE_UUID_SUPPORTED_UNREAD_ALERT_CATEGORY_CHAR);
BLE_UUID_BLE_ASSIGN(p_ans->service.new_alert_cccd.uuid, BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG);
BLE_UUID_BLE_ASSIGN(p_ans->service.unread_alert_cccd.uuid,
BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG);
return ble_db_discovery_evt_register(&p_ans->service.service.uuid);
}
/**@brief Function for creating a tx message for writing a CCCD.
*/
static uint32_t cccd_configure(ble_ans_c_t const * const p_ans,
uint16_t handle_cccd,
bool notification_enable)
{
nrf_ble_gq_req_t cccd_req;
uint16_t cccd_val = notification_enable ? BLE_GATT_HVX_NOTIFICATION : 0;
uint8_t cccd[WRITE_MESSAGE_LENGTH];
memset(&cccd_req, 0, sizeof(nrf_ble_gq_req_t));
cccd[0] = LSB_16(cccd_val);
cccd[1] = MSB_16(cccd_val);
cccd_req.type = NRF_BLE_GQ_REQ_GATTC_WRITE;
cccd_req.error_handler.cb = gatt_error_handler;
cccd_req.error_handler.p_ctx = (ble_ans_c_t *)p_ans;
cccd_req.params.gattc_write.handle = handle_cccd;
cccd_req.params.gattc_write.len = WRITE_MESSAGE_LENGTH;
cccd_req.params.gattc_write.p_value = cccd;
cccd_req.params.gattc_write.offset = 0;
cccd_req.params.gattc_write.write_op = BLE_GATT_OP_WRITE_REQ;
return nrf_ble_gq_item_add(p_ans->p_gatt_queue, &cccd_req, p_ans->conn_handle);
}
uint32_t ble_ans_c_enable_notif_new_alert(ble_ans_c_t const * p_ans)
{
if (p_ans->conn_handle == BLE_CONN_HANDLE_INVALID)
{
return NRF_ERROR_INVALID_STATE;
}
else
{
return cccd_configure(p_ans,
p_ans->service.new_alert_cccd.handle,
true);
}
}
uint32_t ble_ans_c_disable_notif_new_alert(ble_ans_c_t const * p_ans)
{
return cccd_configure(p_ans,
p_ans->service.new_alert_cccd.handle,
false);
}
uint32_t ble_ans_c_enable_notif_unread_alert(ble_ans_c_t const * p_ans)
{
if ( p_ans->conn_handle == BLE_CONN_HANDLE_INVALID)
{
return NRF_ERROR_INVALID_STATE;
}
return cccd_configure(p_ans,
p_ans->service.unread_alert_cccd.handle,
true);
}
uint32_t ble_ans_c_disable_notif_unread_alert(ble_ans_c_t const * p_ans)
{
return cccd_configure(p_ans,
p_ans->service.unread_alert_cccd.handle,
false);
}
uint32_t ble_ans_c_control_point_write(ble_ans_c_t const * p_ans,
ble_ans_control_point_t const * p_control_point)
{
nrf_ble_gq_req_t gq_req;
uint8_t write_data[WRITE_MESSAGE_LENGTH];
write_data[0] = p_control_point->command;
write_data[1] = p_control_point->category;
memset(&gq_req, 0, sizeof(nrf_ble_gq_req_t));
gq_req.type = NRF_BLE_GQ_REQ_GATTC_WRITE;
gq_req.error_handler.cb = gatt_error_handler;
gq_req.error_handler.p_ctx = (ble_ans_c_t *)p_ans;
gq_req.params.gattc_write.handle = p_ans->service.alert_notif_ctrl_point.handle_value;
gq_req.params.gattc_write.len = WRITE_MESSAGE_LENGTH;
gq_req.params.gattc_write.p_value = write_data;
gq_req.params.gattc_write.offset = 0;
gq_req.params.gattc_write.write_op = BLE_GATT_OP_WRITE_REQ;
return nrf_ble_gq_item_add(p_ans->p_gatt_queue, &gq_req, p_ans->conn_handle);
}
uint32_t ble_ans_c_new_alert_read(ble_ans_c_t const * p_ans)
{
nrf_ble_gq_req_t gq_req;
memset(&gq_req, 0, sizeof(nrf_ble_gq_req_t));
gq_req.type = NRF_BLE_GQ_REQ_GATTC_READ;
gq_req.error_handler.cb = gatt_error_handler;
gq_req.error_handler.p_ctx = (ble_ans_c_t *)p_ans;
gq_req.params.gattc_read.handle = p_ans->service.suported_new_alert_cat.handle_value;
gq_req.params.gattc_read.offset = 0;
return nrf_ble_gq_item_add(p_ans->p_gatt_queue, &gq_req, p_ans->conn_handle);
}
uint32_t ble_ans_c_unread_alert_read(ble_ans_c_t const * p_ans)
{
nrf_ble_gq_req_t gq_req;
memset(&gq_req, 0, sizeof(nrf_ble_gq_req_t));
gq_req.type = NRF_BLE_GQ_REQ_GATTC_READ;
gq_req.error_handler.cb = gatt_error_handler;
gq_req.error_handler.p_ctx = (ble_ans_c_t *)p_ans;
gq_req.params.gattc_read.handle = p_ans->service.suported_unread_alert_cat.handle_value;
gq_req.params.gattc_read.offset = 0;
return nrf_ble_gq_item_add(p_ans->p_gatt_queue, &gq_req, p_ans->conn_handle);
}
uint32_t ble_ans_c_new_alert_notify(ble_ans_c_t const * p_ans, ble_ans_category_id_t category_id)
{
ble_ans_control_point_t control_point;
control_point.command = ANS_NOTIFY_NEW_INCOMING_ALERT_IMMEDIATELY;
control_point.category = category_id;
return ble_ans_c_control_point_write(p_ans, &control_point);
}
uint32_t ble_ans_c_unread_alert_notify(ble_ans_c_t const * p_ans, ble_ans_category_id_t category_id)
{
ble_ans_control_point_t control_point;
control_point.command = ANS_NOTIFY_UNREAD_CATEGORY_STATUS_IMMEDIATELY;
control_point.category = category_id;
return ble_ans_c_control_point_write(p_ans, &control_point);
}
uint32_t ble_ans_c_handles_assign(ble_ans_c_t * p_ans,
uint16_t conn_handle,
ble_ans_c_service_t const * p_peer_handles)
{
VERIFY_PARAM_NOT_NULL(p_ans);
if (!is_valid_ans_srv_discovered(p_peer_handles))
{
return NRF_ERROR_INVALID_PARAM;
}
p_ans->conn_handle = conn_handle;
if (p_peer_handles != NULL)
{
// Copy the handles from the discovered characteristics to the provided client instance.
char_set(&p_ans->service.alert_notif_ctrl_point, &p_peer_handles->alert_notif_ctrl_point);
char_set(&p_ans->service.suported_new_alert_cat, &p_peer_handles->suported_new_alert_cat);
char_set(&p_ans->service.suported_unread_alert_cat, &p_peer_handles->suported_unread_alert_cat);
char_set(&p_ans->service.new_alert, &p_peer_handles->new_alert);
char_cccd_set(&p_ans->service.new_alert_cccd, p_peer_handles->new_alert_cccd.handle);
char_set(&p_ans->service.unread_alert_status, &p_peer_handles->unread_alert_status);
char_cccd_set(&p_ans->service.unread_alert_cccd, p_peer_handles->unread_alert_cccd.handle);
}
return nrf_ble_gq_conn_handle_register(p_ans->p_gatt_queue, conn_handle);
}
#endif // NRF_MODULE_ENABLED(BLE_ANS_C)

View File

@@ -0,0 +1,418 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_ans_c Alert Notification Service Client
* @{
* @ingroup ble_sdk_srv
* @brief Alert Notification module.
*
* @details This module implements the Alert Notification Client according to the
* Alert Notification Profile.
*
* @note The application must register this module as the BLE event observer by using the
* NRF_SDH_BLE_OBSERVER macro. Example:
* @code
* ble_ans_c_t instance;
* NRF_SDH_BLE_OBSERVER(anything, BLE_ANS_C_BLE_OBSERVER_PRIO,
* ble_ans_c_on_ble_evt, &instance);
* @endcode
*
* @note Attention!
* To maintain compliance with Nordic Semiconductor ASA Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#ifndef BLE_ANS_C_H__
#define BLE_ANS_C_H__
#include "ble.h"
#include "ble_gatts.h"
#include "ble_types.h"
#include "sdk_common.h"
#include "ble_srv_common.h"
#include "ble_db_discovery.h"
#include "nrf_ble_gq.h"
#include "nrf_sdh_ble.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Macro for defining a ble_ans_c instance.
*
* @param _name Name of the instance.
* @hideinitializer
*/
#define BLE_ANS_C_DEF(_name) \
static ble_ans_c_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_ANS_C_BLE_OBSERVER_PRIO, \
ble_ans_c_on_ble_evt, &_name)
/** @brief Macro for defining multiple ble_ans_c instances.
*
* @param _name Name of the array of instances.
* @param _cnt Number of instances to define.
* @hideinitializer
*/
#define BLE_ANS_C_ARRAY_DEF(_name, _cnt) \
static ble_ans_c_t _name[_cnt]; \
NRF_SDH_BLE_OBSERVERS(_name ## _obs, \
BLE_ANS_C_BLE_OBSERVER_PRIO, \
ble_ans_c_on_ble_evt, &_name, _cnt)
// Forward declaration of the ble_ans_c_t type.
typedef struct ble_ans_c_s ble_ans_c_t;
/** Alert types, as defined in the alert category ID. UUID: 0x2A43. */
typedef enum
{
ANS_TYPE_SIMPLE_ALERT = 0, /**< General text alert or non-text alert.*/
ANS_TYPE_EMAIL = 1, /**< Email message arrives.*/
ANS_TYPE_NEWS = 2, /**< News feeds such as RSS, Atom.*/
ANS_TYPE_NOTIFICATION_CALL = 3, /**< Incoming call.*/
ANS_TYPE_MISSED_CALL = 4, /**< Missed call.*/
ANS_TYPE_SMS_MMS = 5, /**< SMS or MMS message arrives.*/
ANS_TYPE_VOICE_MAIL = 6, /**< Voice mail.*/
ANS_TYPE_SCHEDULE = 7, /**< Alert that occurs on calendar, planner.*/
ANS_TYPE_HIGH_PRIORITIZED_ALERT = 8, /**< Alert to be handled as high priority.*/
ANS_TYPE_INSTANT_MESSAGE = 9, /**< Alert for incoming instant messages.*/
ANS_TYPE_ALL_ALERTS = 0xFF /**< Identifies all alerts. */
} ble_ans_category_id_t;
/** Alert notification control point commands, as defined in the Alert Notification Specification.
* UUID: 0x2A44.
*/
typedef enum
{
ANS_ENABLE_NEW_INCOMING_ALERT_NOTIFICATION = 0, /**< Enable New Incoming Alert Notification.*/
ANS_ENABLE_UNREAD_CATEGORY_STATUS_NOTIFICATION = 1, /**< Enable Unread Category Status Notification.*/
ANS_DISABLE_NEW_INCOMING_ALERT_NOTIFICATION = 2, /**< Disable New Incoming Alert Notification.*/
ANS_DISABLE_UNREAD_CATEGORY_STATUS_NOTIFICATION = 3, /**< Disable Unread Category Status Notification.*/
ANS_NOTIFY_NEW_INCOMING_ALERT_IMMEDIATELY = 4, /**< Notify New Incoming Alert immediately.*/
ANS_NOTIFY_UNREAD_CATEGORY_STATUS_IMMEDIATELY = 5, /**< Notify Unread Category Status immediately.*/
} ble_ans_command_id_t;
/**@brief Alert Notification Event types that are passed from client to the application on an event. */
typedef enum
{
BLE_ANS_C_EVT_DISCOVERY_COMPLETE, /**< A successful connection is established and the characteristics of the server were fetched. */
BLE_ANS_C_EVT_DISCOVERY_FAILED, /**< It was not possible to discover service or characteristics of the connected peer. */
BLE_ANS_C_EVT_DISCONN_COMPLETE, /**< The connection is taken down. */
BLE_ANS_C_EVT_NOTIFICATION, /**< A valid notification was received from the server.*/
BLE_ANS_C_EVT_READ_RESP, /**< A read response was received from the server.*/
BLE_ANS_C_EVT_WRITE_RESP /**< A write response was received from the server.*/
} ble_ans_c_evt_type_t;
/**@brief Alert Notification Control Point structure. */
typedef struct
{
ble_ans_command_id_t command; /**< The command to be written to the control point. See @ref ble_ans_command_id_t. */
ble_ans_category_id_t category; /**< The category for the control point for which the command applies. See @ref ble_ans_category_id_t. */
} ble_ans_control_point_t;
/**@brief Alert Notification Setting structure containing the supported alerts in the service.
*
*@details
* The structure contains bit fields that describe which alerts are supported:
* - 0 = Unsupported
* - 1 = Supported
*/
typedef struct
{
uint8_t ans_simple_alert_support : 1; /**< Support for general text alert or non-text alert.*/
uint8_t ans_email_support : 1; /**< Support for alert when an email message arrives.*/
uint8_t ans_news_support : 1; /**< Support for news feeds such as RSS, Atom.*/
uint8_t ans_notification_call_support : 1; /**< Support for incoming call.*/
uint8_t ans_missed_call_support : 1; /**< Support for missed call.*/
uint8_t ans_sms_mms_support : 1; /**< Support for SMS or MMS message arrival.*/
uint8_t ans_voice_mail_support : 1; /**< Support for voice mail.*/
uint8_t ans_schedule_support : 1; /**< Support for alert that occurs on calendar or planner.*/
uint8_t ans_high_prioritized_alert_support : 1; /**< Support for alert that should be handled as high priority.*/
uint8_t ans_instant_message_support : 1; /**< Support for alert for incoming instant messages.*/
uint8_t reserved : 6; /**< Reserved for future use. */
} ble_ans_alert_settings_t;
/**@brief Alert Notification structure
*/
typedef struct
{
uint8_t alert_category; /**< Alert category to which this alert belongs.*/
uint8_t alert_category_count; /**< Number of alerts in the category. */
uint32_t alert_msg_length; /**< Length of the optional text message sent by the server. */
uint8_t * p_alert_msg_buf; /**< Pointer to the buffer that contains the optional text message. */
} ble_ans_alert_notification_t;
/**@brief Structure for holding information on the Alert Notification Service, if found on the server. */
typedef struct
{
ble_gattc_service_t service; /**< The GATT service that holds the discovered Alert Notification Service. */
ble_gattc_char_t alert_notif_ctrl_point; /**< Characteristic for the Alert Notification Control Point. See @ref BLE_UUID_ALERT_NOTIFICATION_CONTROL_POINT_CHAR. */
ble_gattc_char_t suported_new_alert_cat; /**< Characteristic for the Supported New Alert category. See @ref BLE_UUID_SUPPORTED_NEW_ALERT_CATEGORY_CHAR. */
ble_gattc_char_t suported_unread_alert_cat; /**< Characteristic for the Unread Alert category. See @ref BLE_UUID_SUPPORTED_UNREAD_ALERT_CATEGORY_CHAR. */
ble_gattc_char_t new_alert; /**< Characteristic for the New Alert Notification. See @ref BLE_UUID_NEW_ALERT_CHAR. */
ble_gattc_desc_t new_alert_cccd; /**< Characteristic Descriptor for the New Alert category. Enables or disables GATT notifications. */
ble_gattc_char_t unread_alert_status; /**< Characteristic for the Unread Alert Notification. See @ref BLE_UUID_UNREAD_ALERT_CHAR. */
ble_gattc_desc_t unread_alert_cccd; /**< Characteristic Descriptor for the Unread Alert category. Enables or disables GATT notifications */
} ble_ans_c_service_t;
/**@brief Alert Notification Event structure
*
* @details Structure for holding information about the event that should be handled, as well as
* additional information.
*/
typedef struct
{
ble_ans_c_evt_type_t evt_type; /**< Type of event. */
uint16_t conn_handle; /**< Connection handle on which the ANS service was discovered on the peer device. This is filled if the evt_type is @ref BLE_ANS_C_EVT_DISCOVERY_COMPLETE.*/
ble_uuid_t uuid; /**< UUID of the event in case of an alert or notification. */
union
{
ble_ans_alert_settings_t settings; /**< Setting returned from server on read request. */
ble_ans_alert_notification_t alert; /**< Alert Notification data sent by the server. */
uint32_t error_code; /**< Additional status or error code, if the event is caused by a stack error or GATT status, for example during service discovery. */
ble_ans_c_service_t service; /**< Information on the discovered Alert Notification Service. This is filled if the evt_type is @ref BLE_ANS_C_EVT_DISCOVERY_COMPLETE.*/
} data;
} ble_ans_c_evt_t;
/**@brief Alert Notification event handler type. */
typedef void (*ble_ans_c_evt_handler_t) (ble_ans_c_evt_t * p_evt);
/**@brief Alert Notification structure. Contains various status information for the client. */
struct ble_ans_c_s
{
ble_ans_c_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Alert Notification Client Application. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
uint16_t conn_handle; /**< Handle of the current connection (as provided by the BLE stack; if not in a connection, the handle is BLE_CONN_HANDLE_INVALID). */
uint8_t central_handle; /**< Handle for the currently connected central, if peer is bonded. */
uint8_t service_handle; /**< Handle for the service in the database to be used for this instance. */
uint32_t message_buffer_size; /**< Size of the message buffer to hold the additional text messages received on notifications. */
uint8_t * p_message_buffer; /**< Pointer to the buffer to be used for additional text message handling. */
ble_ans_c_service_t service; /**< Struct to store different handles and UUIDs related to the service. */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to the BLE GATT Queue instance. */
};
/**@brief Alert Notification init structure. Contains all options and data needed for
* the initialization of the client.*/
typedef struct
{
ble_ans_c_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Battery Service. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
uint32_t message_buffer_size; /**< Size of the buffer to handle messages. */
uint8_t * p_message_buffer; /**< Pointer to the buffer for passing messages. */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to the BLE GATT Queue instance. */
} ble_ans_c_init_t;
/**@brief Function for handling events from the Database Discovery module.
*
* @details Call this function when you get a callback event from the Database Discovery module.
* This function handles an event from the Database Discovery module and determines
* whether it relates to the discovery of Heart Rate Service at the peer. If it does, this function
* calls the application's event handler to indicate that the Heart Rate Service was
* discovered at the peer. The function also populates the event with service-related
* information before providing it to the application.
*
* @param[in] p_ans Pointer to the Alert Notification client structure instance that will handle
* the discovery.
* @param[in] p_evt Pointer to the event received from the Database Discovery module.
*/
void ble_ans_c_on_db_disc_evt(ble_ans_c_t * p_ans, ble_db_discovery_evt_t const * p_evt);
/**@brief Function for handling the application's BLE stack events.
*
* @details Handles all events from the BLE stack of interest to the Alert Notification Client.
*
* @param[in] p_ble_evt Event received from the BLE stack.
* @param[in] p_context Alert Notification Client structure.
*/
void ble_ans_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for initializing the Alert Notification Client.
*
* @param[out] p_ans Alert Notification Client structure. This structure must be
* supplied by the application. It is initialized by this function,
* and is later used to identify this particular client instance.
* @param[in] p_ans_init Information needed to initialize the client.
*
* @return NRF_SUCCESS on successful initialization of client. Otherwise, it returns an error code.
*/
uint32_t ble_ans_c_init(ble_ans_c_t * p_ans, ble_ans_c_init_t const * p_ans_init);
/**@brief Function for writing the to CCCD to enable new alert notifications from the Alert Notification Service.
*
* @param[in] p_ans Alert Notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
*
* @return NRF_SUCCESS on successful writing of the CCCD. Otherwise, it returns an error code.
*/
uint32_t ble_ans_c_enable_notif_new_alert(ble_ans_c_t const * p_ans);
/**@brief Function for writing to the CCCD to enable unread alert notifications from the Alert Notification Service.
*
* @param[in] p_ans Alert Notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
*
* @return NRF_SUCCESS on successful writing of the CCCD. Otherwise, it returns an error code.
*/
uint32_t ble_ans_c_enable_notif_unread_alert(ble_ans_c_t const * p_ans);
/**@brief Function for writing to the CCCD to disable new alert notifications from the Alert Notification Service.
*
* @param[in] p_ans Alert Notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
*
* @return NRF_SUCCESS on successful writing of the CCCD. Otherwise, it returns an error code.
*/
uint32_t ble_ans_c_disable_notif_new_alert(ble_ans_c_t const * p_ans);
/**@brief Function for writing to the CCCD to disable unread alert notifications from the Alert Notification Service.
*
* @param[in] p_ans Alert Notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
*
* @return NRF_SUCCESS on successful writing of the CCCD, otherwise an error code.
*/
uint32_t ble_ans_c_disable_notif_unread_alert(ble_ans_c_t const * p_ans);
/**@brief Function for writing to the Alert Notification Control Point to specify alert notification behavior in the
* Alert Notification Service on the Central.
*
* @param[in] p_ans Alert Notification structure. This structure must be
* supplied by the application. It identifies the particular client
* instance to use.
* @param[in] p_control_point Alert Notification Control Point structure. This structure
* specifies the values to write to the Alert Notification Control
* Point (UUID 0x2A44).
*
* @return NRF_SUCCESS on successful writing of the Control Point. Otherwise,
* this API propagates the error code returned by function
* @ref nrf_ble_gq_item_add.
*/
uint32_t ble_ans_c_control_point_write(ble_ans_c_t const * p_ans,
ble_ans_control_point_t const * p_control_point);
/**@brief Function for reading the Supported New Alert characteristic value of the service.
* The value describes the alerts supported in the central.
*
* @param[in] p_ans Alert Notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
*
* @return NRF_SUCCESS on successful transmission of the read request. Otherwise,
* this API propagates the error code returned by function
* @ref nrf_ble_gq_item_add.
*/
uint32_t ble_ans_c_new_alert_read(ble_ans_c_t const * p_ans);
/**@brief Function for reading the Supported Unread Alert characteristic value of the service.
* The value describes the alerts supported in the central.
*
* @param[in] p_ans Alert Notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
*
* @return NRF_SUCCESS on successful transmission of the read request. Otherwise,
* this API propagates the error code returned by function
* @ref nrf_ble_gq_item_add.
*/
uint32_t ble_ans_c_unread_alert_read(ble_ans_c_t const * p_ans);
/**@brief Function for requesting the peer to notify the New Alert characteristics immediately.
*
* @param[in] p_ans Alert Notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
* @param[in] category The category ID for which the peer should notify the client.
*
* @return NRF_SUCCESS on successful transmission of the read request. Otherwise, it returns an error code.
*/
uint32_t ble_ans_c_new_alert_notify(ble_ans_c_t const * p_ans, ble_ans_category_id_t category);
/**@brief Function for requesting the peer to notify the Unread Alert characteristics immediately.
*
* @param[in] p_ans Alert Notification structure. This structure must be supplied by
* the application. It identifies the particular client instance to use.
* @param[in] category The category ID for which the peer should notify the client.
*
* @return NRF_SUCCESS on successful transmission of the read request. Otherwise, it returns an error code.
*/
uint32_t ble_ans_c_unread_alert_notify(ble_ans_c_t const * p_ans, ble_ans_category_id_t category);
/**@brief Function for assigning handles to an instance of ans_c.
*
* @details Call this function when a link has been established with a peer to
* associate the link to an instance of the module. This makes it possible
* to handle several links and associate each link to a particular
* instance of the ans_c module. The connection handle and attribute handles
* are provided from the discovery event @ref BLE_ANS_C_EVT_DISCOVERY_COMPLETE.
*
* @param[in] p_ans Pointer to the Alert Notification client structure instance to
* associate with the handles.
* @param[in] conn_handle Connection handle to associated with the given Alert Notification Client
* Instance.
* @param[in] p_peer_handles Attribute handles on the ANS server that you want this ANS client to
* interact with.
*
*/
uint32_t ble_ans_c_handles_assign(ble_ans_c_t * p_ans,
uint16_t const conn_handle,
ble_ans_c_service_t const * p_peer_handles);
#ifdef __cplusplus
}
#endif
#endif // BLE_ANS_C_H__
/** @} */

View File

@@ -0,0 +1,365 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* Attention!
* To maintain compliance with Nordic Semiconductor ASA's Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_BAS)
#include "ble_bas.h"
#include <string.h>
#include "ble_srv_common.h"
#include "ble_conn_state.h"
#define NRF_LOG_MODULE_NAME ble_bas
#if BLE_BAS_CONFIG_LOG_ENABLED
#define NRF_LOG_LEVEL BLE_BAS_CONFIG_LOG_LEVEL
#define NRF_LOG_INFO_COLOR BLE_BAS_CONFIG_INFO_COLOR
#define NRF_LOG_DEBUG_COLOR BLE_BAS_CONFIG_DEBUG_COLOR
#else // BLE_BAS_CONFIG_LOG_ENABLED
#define NRF_LOG_LEVEL 0
#endif // BLE_BAS_CONFIG_LOG_ENABLED
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();
#define INVALID_BATTERY_LEVEL 255
/**@brief Function for handling the Write event.
*
* @param[in] p_bas Battery Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_write(ble_bas_t * p_bas, ble_evt_t const * p_ble_evt)
{
if (!p_bas->is_notification_supported)
{
return;
}
ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
if ( (p_evt_write->handle == p_bas->battery_level_handles.cccd_handle)
&& (p_evt_write->len == 2))
{
if (p_bas->evt_handler == NULL)
{
return;
}
ble_bas_evt_t evt;
if (ble_srv_is_notification_enabled(p_evt_write->data))
{
evt.evt_type = BLE_BAS_EVT_NOTIFICATION_ENABLED;
}
else
{
evt.evt_type = BLE_BAS_EVT_NOTIFICATION_DISABLED;
}
evt.conn_handle = p_ble_evt->evt.gatts_evt.conn_handle;
// CCCD written, call application event handler.
p_bas->evt_handler(p_bas, &evt);
}
}
void ble_bas_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
if ((p_context == NULL) || (p_ble_evt == NULL))
{
return;
}
ble_bas_t * p_bas = (ble_bas_t *)p_context;
switch (p_ble_evt->header.evt_id)
{
case BLE_GATTS_EVT_WRITE:
on_write(p_bas, p_ble_evt);
break;
default:
// No implementation needed.
break;
}
}
/**@brief Function for adding the Battery Level characteristic.
*
* @param[in] p_bas Battery Service structure.
* @param[in] p_bas_init Information needed to initialize the service.
*
* @return NRF_SUCCESS on success, otherwise an error code.
*/
static ret_code_t battery_level_char_add(ble_bas_t * p_bas, const ble_bas_init_t * p_bas_init)
{
ret_code_t err_code;
ble_add_char_params_t add_char_params;
ble_add_descr_params_t add_descr_params;
uint8_t initial_battery_level;
uint8_t init_len;
uint8_t encoded_report_ref[BLE_SRV_ENCODED_REPORT_REF_LEN];
// Add battery level characteristic
initial_battery_level = p_bas_init->initial_batt_level;
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = BLE_UUID_BATTERY_LEVEL_CHAR;
add_char_params.max_len = sizeof(uint8_t);
add_char_params.init_len = sizeof(uint8_t);
add_char_params.p_init_value = &initial_battery_level;
add_char_params.char_props.notify = p_bas->is_notification_supported;
add_char_params.char_props.read = 1;
add_char_params.cccd_write_access = p_bas_init->bl_cccd_wr_sec;
add_char_params.read_access = p_bas_init->bl_rd_sec;
err_code = characteristic_add(p_bas->service_handle,
&add_char_params,
&(p_bas->battery_level_handles));
if (err_code != NRF_SUCCESS)
{
return err_code;
}
if (p_bas_init->p_report_ref != NULL)
{
// Add Report Reference descriptor
init_len = ble_srv_report_ref_encode(encoded_report_ref, p_bas_init->p_report_ref);
memset(&add_descr_params, 0, sizeof(add_descr_params));
add_descr_params.uuid = BLE_UUID_REPORT_REF_DESCR;
add_descr_params.read_access = p_bas_init->bl_report_rd_sec;
add_descr_params.init_len = init_len;
add_descr_params.max_len = add_descr_params.init_len;
add_descr_params.p_value = encoded_report_ref;
err_code = descriptor_add(p_bas->battery_level_handles.value_handle,
&add_descr_params,
&p_bas->report_ref_handle);
return err_code;
}
else
{
p_bas->report_ref_handle = BLE_GATT_HANDLE_INVALID;
}
return NRF_SUCCESS;
}
ret_code_t ble_bas_init(ble_bas_t * p_bas, const ble_bas_init_t * p_bas_init)
{
if (p_bas == NULL || p_bas_init == NULL)
{
return NRF_ERROR_NULL;
}
ret_code_t err_code;
ble_uuid_t ble_uuid;
// Initialize service structure
p_bas->evt_handler = p_bas_init->evt_handler;
p_bas->is_notification_supported = p_bas_init->support_notification;
p_bas->battery_level_last = INVALID_BATTERY_LEVEL;
// Add service
BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_BATTERY_SERVICE);
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_bas->service_handle);
VERIFY_SUCCESS(err_code);
// Add battery level characteristic
err_code = battery_level_char_add(p_bas, p_bas_init);
return err_code;
}
/**@brief Function for sending notifications with the Battery Level characteristic.
*
* @param[in] p_hvx_params Pointer to structure with notification data.
* @param[in] conn_handle Connection handle.
*
* @return NRF_SUCCESS on success, otherwise an error code.
*/
static ret_code_t battery_notification_send(ble_gatts_hvx_params_t * const p_hvx_params,
uint16_t conn_handle)
{
ret_code_t err_code = sd_ble_gatts_hvx(conn_handle, p_hvx_params);
if (err_code == NRF_SUCCESS)
{
NRF_LOG_INFO("Battery notification has been sent using conn_handle: 0x%04X", conn_handle);
}
else
{
NRF_LOG_DEBUG("Error: 0x%08X while sending notification with conn_handle: 0x%04X",
err_code,
conn_handle);
}
return err_code;
}
ret_code_t ble_bas_battery_level_update(ble_bas_t * p_bas,
uint8_t battery_level,
uint16_t conn_handle)
{
if (p_bas == NULL)
{
return NRF_ERROR_NULL;
}
ret_code_t err_code = NRF_SUCCESS;
ble_gatts_value_t gatts_value;
if (battery_level != p_bas->battery_level_last)
{
// Initialize value struct.
memset(&gatts_value, 0, sizeof(gatts_value));
gatts_value.len = sizeof(uint8_t);
gatts_value.offset = 0;
gatts_value.p_value = &battery_level;
// Update database.
err_code = sd_ble_gatts_value_set(BLE_CONN_HANDLE_INVALID,
p_bas->battery_level_handles.value_handle,
&gatts_value);
if (err_code == NRF_SUCCESS)
{
NRF_LOG_INFO("Battery level has been updated: %d%%", battery_level)
// Save new battery value.
p_bas->battery_level_last = battery_level;
}
else
{
NRF_LOG_DEBUG("Error during battery level update: 0x%08X", err_code)
return err_code;
}
// Send value if connected and notifying.
if (p_bas->is_notification_supported)
{
ble_gatts_hvx_params_t hvx_params;
memset(&hvx_params, 0, sizeof(hvx_params));
hvx_params.handle = p_bas->battery_level_handles.value_handle;
hvx_params.type = BLE_GATT_HVX_NOTIFICATION;
hvx_params.offset = gatts_value.offset;
hvx_params.p_len = &gatts_value.len;
hvx_params.p_data = gatts_value.p_value;
if (conn_handle == BLE_CONN_HANDLE_ALL)
{
ble_conn_state_conn_handle_list_t conn_handles = ble_conn_state_conn_handles();
// Try sending notifications to all valid connection handles.
for (uint32_t i = 0; i < conn_handles.len; i++)
{
if (ble_conn_state_status(conn_handles.conn_handles[i]) == BLE_CONN_STATUS_CONNECTED)
{
if (err_code == NRF_SUCCESS)
{
err_code = battery_notification_send(&hvx_params,
conn_handles.conn_handles[i]);
}
else
{
// Preserve the first non-zero error code
UNUSED_RETURN_VALUE(battery_notification_send(&hvx_params,
conn_handles.conn_handles[i]));
}
}
}
}
else
{
err_code = battery_notification_send(&hvx_params, conn_handle);
}
}
else
{
err_code = NRF_ERROR_INVALID_STATE;
}
}
return err_code;
}
ret_code_t ble_bas_battery_lvl_on_reconnection_update(ble_bas_t * p_bas,
uint16_t conn_handle)
{
if (p_bas == NULL)
{
return NRF_ERROR_NULL;
}
ret_code_t err_code;
if (p_bas->is_notification_supported)
{
ble_gatts_hvx_params_t hvx_params;
uint16_t len = sizeof(uint8_t);
memset(&hvx_params, 0, sizeof(hvx_params));
hvx_params.handle = p_bas->battery_level_handles.value_handle;
hvx_params.type = BLE_GATT_HVX_NOTIFICATION;
hvx_params.offset = 0;
hvx_params.p_len = &len;
hvx_params.p_data = &p_bas->battery_level_last;
err_code = battery_notification_send(&hvx_params, conn_handle);
return err_code;
}
return NRF_ERROR_INVALID_STATE;
}
#endif // NRF_MODULE_ENABLED(BLE_BAS)

View File

@@ -0,0 +1,212 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_bas Battery Service
* @{
* @ingroup ble_sdk_srv
* @brief Battery Service module.
*
* @details This module implements the Battery Service with the Battery Level characteristic.
* During initialization it adds the Battery Service and Battery Level characteristic
* to the BLE stack database. Optionally it can also add a Report Reference descriptor
* to the Battery Level characteristic (used when including the Battery Service in
* the HID service).
*
* If specified, the module will support notification of the Battery Level characteristic
* through the ble_bas_battery_level_update() function.
* If an event handler is supplied by the application, the Battery Service will
* generate Battery Service events to the application.
*
* @note The application must register this module as BLE event observer using the
* NRF_SDH_BLE_OBSERVER macro. Example:
* @code
* ble_bas_t instance;
* NRF_SDH_BLE_OBSERVER(anything, BLE_BAS_BLE_OBSERVER_PRIO,
* ble_bas_on_ble_evt, &instance);
* @endcode
*
* @note Attention!
* To maintain compliance with Nordic Semiconductor ASA Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#ifndef BLE_BAS_H__
#define BLE_BAS_H__
#include <stdint.h>
#include <stdbool.h>
#include "ble.h"
#include "ble_srv_common.h"
#include "nrf_sdh_ble.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Macro for defining a ble_bas instance.
*
* @param _name Name of the instance.
* @hideinitializer
*/
#define BLE_BAS_DEF(_name) \
static ble_bas_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_BAS_BLE_OBSERVER_PRIO, \
ble_bas_on_ble_evt, \
&_name)
/**@brief Battery Service event type. */
typedef enum
{
BLE_BAS_EVT_NOTIFICATION_ENABLED, /**< Battery value notification enabled event. */
BLE_BAS_EVT_NOTIFICATION_DISABLED /**< Battery value notification disabled event. */
} ble_bas_evt_type_t;
/**@brief Battery Service event. */
typedef struct
{
ble_bas_evt_type_t evt_type; /**< Type of event. */
uint16_t conn_handle; /**< Connection handle. */
} ble_bas_evt_t;
// Forward declaration of the ble_bas_t type.
typedef struct ble_bas_s ble_bas_t;
/**@brief Battery Service event handler type. */
typedef void (* ble_bas_evt_handler_t) (ble_bas_t * p_bas, ble_bas_evt_t * p_evt);
/**@brief Battery Service init structure. This contains all options and data needed for
* initialization of the service.*/
typedef struct
{
ble_bas_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Battery Service. */
bool support_notification; /**< TRUE if notification of Battery Level measurement is supported. */
ble_srv_report_ref_t * p_report_ref; /**< If not NULL, a Report Reference descriptor with the specified value will be added to the Battery Level characteristic */
uint8_t initial_batt_level; /**< Initial battery level */
security_req_t bl_rd_sec; /**< Security requirement for reading the BL characteristic value. */
security_req_t bl_cccd_wr_sec; /**< Security requirement for writing the BL characteristic CCCD. */
security_req_t bl_report_rd_sec; /**< Security requirement for reading the BL characteristic descriptor. */
} ble_bas_init_t;
/**@brief Battery Service structure. This contains various status information for the service. */
struct ble_bas_s
{
ble_bas_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Battery Service. */
uint16_t service_handle; /**< Handle of Battery Service (as provided by the BLE stack). */
ble_gatts_char_handles_t battery_level_handles; /**< Handles related to the Battery Level characteristic. */
uint16_t report_ref_handle; /**< Handle of the Report Reference descriptor. */
uint8_t battery_level_last; /**< Last Battery Level measurement passed to the Battery Service. */
bool is_notification_supported; /**< TRUE if notification of Battery Level is supported. */
};
/**@brief Function for initializing the Battery Service.
*
* @param[out] p_bas Battery Service structure. This structure will have to be supplied by
* the application. It will be initialized by this function, and will later
* be used to identify this particular service instance.
* @param[in] p_bas_init Information needed to initialize the service.
*
* @return NRF_SUCCESS on successful initialization of service, otherwise an error code.
*/
ret_code_t ble_bas_init(ble_bas_t * p_bas, const ble_bas_init_t * p_bas_init);
/**@brief Function for handling the Application's BLE Stack events.
*
* @details Handles all events from the BLE stack of interest to the Battery Service.
*
* @note For the requirements in the BAS specification to be fulfilled,
* ble_bas_battery_level_update() must be called upon reconnection if the
* battery level has changed while the service has been disconnected from a bonded
* client.
*
* @param[in] p_ble_evt Event received from the BLE stack.
* @param[in] p_context Battery Service structure.
*/
void ble_bas_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for updating the battery level.
*
* @details The application calls this function after having performed a battery measurement.
* The battery level characteristic will only be sent to the clients which have
* enabled notifications. \ref BLE_CONN_HANDLE_ALL can be used as a connection handle
* to send notifications to all connected devices.
*
* @param[in] p_bas Battery Service structure.
* @param[in] battery_level New battery measurement value (in percent of full capacity).
* @param[in] conn_handle Connection handle.
*
* @return NRF_SUCCESS on success, otherwise an error code.
*/
ret_code_t ble_bas_battery_level_update(ble_bas_t * p_bas,
uint8_t battery_level,
uint16_t conn_handle);
/**@brief Function for sending the last battery level when bonded client reconnects.
*
* @details The application calls this function, in the case of a reconnection of
* a bonded client if the value of the battery has changed since
* its disconnection.
*
* @note For the requirements in the BAS specification to be fulfilled,
* this function must be called upon reconnection if the battery level has changed
* while the service has been disconnected from a bonded client.
*
* @param[in] p_bas Battery Service structure.
* @param[in] conn_handle Connection handle.
*
* @return NRF_SUCCESS on success,
* NRF_ERROR_INVALID_STATE when notification is not supported,
* otherwise an error code returned by @ref sd_ble_gatts_hvx.
*/
ret_code_t ble_bas_battery_lvl_on_reconnection_update(ble_bas_t * p_bas,
uint16_t conn_handle);
#ifdef __cplusplus
}
#endif
#endif // BLE_BAS_H__
/** @} */

View File

@@ -0,0 +1,341 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_BAS_C)
#include "ble_bas_c.h"
#include "ble_types.h"
#include "ble_db_discovery.h"
#include "ble_gattc.h"
#define NRF_LOG_MODULE_NAME ble_bas_c
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();
/**@brief Function for intercepting errors of GATTC and BLE GATT Queue.
*
* @param[in] nrf_error Error code.
* @param[in] p_ctx Parameter from the event handler.
* @param[in] conn_handle Connection handle.
*/
static void gatt_error_handler(uint32_t nrf_error,
void * p_ctx,
uint16_t conn_handle)
{
ble_bas_c_t * p_bas_c = (ble_bas_c_t *)p_ctx;
NRF_LOG_DEBUG("A GATT Client error has occurred on conn_handle: 0X%X", conn_handle);
if (p_bas_c->error_handler != NULL)
{
p_bas_c->error_handler(nrf_error);
}
}
/**@brief Function for handling read response events.
*
* @details This function validates the read response and raises the appropriate
* event to the application.
*
* @param[in] p_bas_c Pointer to the Battery Service Client Structure.
* @param[in] p_ble_evt Pointer to the SoftDevice event.
*/
static void on_read_rsp(ble_bas_c_t * p_bas_c, ble_evt_t const * p_ble_evt)
{
const ble_gattc_evt_read_rsp_t * p_response;
// Check if the event is on the link for this instance.
if (p_bas_c->conn_handle != p_ble_evt->evt.gattc_evt.conn_handle)
{
return;
}
p_response = &p_ble_evt->evt.gattc_evt.params.read_rsp;
if (p_response->handle == p_bas_c->peer_bas_db.bl_handle)
{
ble_bas_c_evt_t evt;
evt.conn_handle = p_ble_evt->evt.gattc_evt.conn_handle;
evt.evt_type = BLE_BAS_C_EVT_BATT_READ_RESP;
evt.params.battery_level = p_response->data[0];
p_bas_c->evt_handler(p_bas_c, &evt);
}
}
/**@brief Function for handling Handle Value Notification received from the SoftDevice.
*
* @details This function handles the Handle Value Notification received from the SoftDevice
* and checks whether it is a notification of the Battery Level measurement from the peer. If
* it is, this function decodes the battery level measurement and sends it to the
* application.
*
* @param[in] p_ble_bas_c Pointer to the Battery Service Client structure.
* @param[in] p_ble_evt Pointer to the BLE event received.
*/
static void on_hvx(ble_bas_c_t * p_ble_bas_c, ble_evt_t const * p_ble_evt)
{
// Check if the event is on the link for this instance.
if (p_ble_bas_c->conn_handle != p_ble_evt->evt.gattc_evt.conn_handle)
{
return;
}
// Check if this notification is a battery level notification.
if (p_ble_evt->evt.gattc_evt.params.hvx.handle == p_ble_bas_c->peer_bas_db.bl_handle)
{
if (p_ble_evt->evt.gattc_evt.params.hvx.len == 1)
{
ble_bas_c_evt_t ble_bas_c_evt;
ble_bas_c_evt.conn_handle = p_ble_evt->evt.gattc_evt.conn_handle;
ble_bas_c_evt.evt_type = BLE_BAS_C_EVT_BATT_NOTIFICATION;
ble_bas_c_evt.params.battery_level = p_ble_evt->evt.gattc_evt.params.hvx.data[0];
p_ble_bas_c->evt_handler(p_ble_bas_c, &ble_bas_c_evt);
}
}
}
void ble_bas_on_db_disc_evt(ble_bas_c_t * p_ble_bas_c, const ble_db_discovery_evt_t * p_evt)
{
// Check if the Battery Service was discovered.
if (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE
&&
p_evt->params.discovered_db.srv_uuid.uuid == BLE_UUID_BATTERY_SERVICE
&&
p_evt->params.discovered_db.srv_uuid.type == BLE_UUID_TYPE_BLE)
{
// Find the CCCD Handle of the Battery Level characteristic.
uint8_t i;
ble_bas_c_evt_t evt;
evt.evt_type = BLE_BAS_C_EVT_DISCOVERY_COMPLETE;
evt.conn_handle = p_evt->conn_handle;
for (i = 0; i < p_evt->params.discovered_db.char_count; i++)
{
if (p_evt->params.discovered_db.charateristics[i].characteristic.uuid.uuid ==
BLE_UUID_BATTERY_LEVEL_CHAR)
{
// Found Battery Level characteristic. Store CCCD handle and break.
evt.params.bas_db.bl_cccd_handle =
p_evt->params.discovered_db.charateristics[i].cccd_handle;
evt.params.bas_db.bl_handle =
p_evt->params.discovered_db.charateristics[i].characteristic.handle_value;
break;
}
}
NRF_LOG_DEBUG("Battery Service discovered at peer.");
//If the instance has been assigned prior to db_discovery, assign the db_handles.
if (p_ble_bas_c->conn_handle != BLE_CONN_HANDLE_INVALID)
{
if ((p_ble_bas_c->peer_bas_db.bl_cccd_handle == BLE_GATT_HANDLE_INVALID)&&
(p_ble_bas_c->peer_bas_db.bl_handle == BLE_GATT_HANDLE_INVALID))
{
p_ble_bas_c->peer_bas_db = evt.params.bas_db;
}
}
p_ble_bas_c->evt_handler(p_ble_bas_c, &evt);
}
else if ((p_evt->evt_type == BLE_DB_DISCOVERY_SRV_NOT_FOUND) ||
(p_evt->evt_type == BLE_DB_DISCOVERY_ERROR))
{
NRF_LOG_DEBUG("Battery Service discovery failure at peer. ");
}
else
{
// Do nothing.
}
}
/**@brief Function for creating a message for writing to the CCCD.
*/
static uint32_t cccd_configure(ble_bas_c_t * p_ble_bas_c, bool notification_enable)
{
NRF_LOG_DEBUG("Configuring CCCD. CCCD Handle = %d, Connection Handle = %d",
p_ble_bas_c->peer_bas_db.bl_cccd_handle,
p_ble_bas_c->conn_handle);
nrf_ble_gq_req_t bas_c_req;
uint8_t cccd[BLE_CCCD_VALUE_LEN];
uint16_t cccd_val = notification_enable ? BLE_GATT_HVX_NOTIFICATION : 0;
cccd[0] = LSB_16(cccd_val);
cccd[1] = MSB_16(cccd_val);
memset(&bas_c_req, 0, sizeof(bas_c_req));
bas_c_req.type = NRF_BLE_GQ_REQ_GATTC_WRITE;
bas_c_req.error_handler.cb = gatt_error_handler;
bas_c_req.error_handler.p_ctx = p_ble_bas_c;
bas_c_req.params.gattc_write.handle = p_ble_bas_c->peer_bas_db.bl_cccd_handle;
bas_c_req.params.gattc_write.len = BLE_CCCD_VALUE_LEN;
bas_c_req.params.gattc_write.p_value = cccd;
bas_c_req.params.gattc_write.offset = 0;
bas_c_req.params.gattc_write.write_op = BLE_GATT_OP_WRITE_REQ;
return nrf_ble_gq_item_add(p_ble_bas_c->p_gatt_queue, &bas_c_req, p_ble_bas_c->conn_handle);
}
uint32_t ble_bas_c_init(ble_bas_c_t * p_ble_bas_c, ble_bas_c_init_t * p_ble_bas_c_init)
{
VERIFY_PARAM_NOT_NULL(p_ble_bas_c);
VERIFY_PARAM_NOT_NULL(p_ble_bas_c_init);
ble_uuid_t bas_uuid;
bas_uuid.type = BLE_UUID_TYPE_BLE;
bas_uuid.uuid = BLE_UUID_BATTERY_SERVICE;
p_ble_bas_c->conn_handle = BLE_CONN_HANDLE_INVALID;
p_ble_bas_c->peer_bas_db.bl_cccd_handle = BLE_GATT_HANDLE_INVALID;
p_ble_bas_c->peer_bas_db.bl_handle = BLE_GATT_HANDLE_INVALID;
p_ble_bas_c->evt_handler = p_ble_bas_c_init->evt_handler;
p_ble_bas_c->error_handler = p_ble_bas_c_init->error_handler;
p_ble_bas_c->p_gatt_queue = p_ble_bas_c_init->p_gatt_queue;
return ble_db_discovery_evt_register(&bas_uuid);
}
/**@brief Function for handling the Disconnected event received from the SoftDevice.
*
* @details This function checks whether the disconnect event is happening on the link
* associated with the current instance of the module. If the event is happening,
* the function sets the instance's conn_handle to invalid.
*
* @param[in] p_ble_bas_c Pointer to the Battery Service Client structure.
* @param[in] p_ble_evt Pointer to the BLE event received.
*/
static void on_disconnected(ble_bas_c_t * p_ble_bas_c, const ble_evt_t * p_ble_evt)
{
if (p_ble_bas_c->conn_handle == p_ble_evt->evt.gap_evt.conn_handle)
{
p_ble_bas_c->conn_handle = BLE_CONN_HANDLE_INVALID;
p_ble_bas_c->peer_bas_db.bl_cccd_handle = BLE_GATT_HANDLE_INVALID;
p_ble_bas_c->peer_bas_db.bl_handle = BLE_GATT_HANDLE_INVALID;
}
}
void ble_bas_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
if ((p_ble_evt == NULL) || (p_context == NULL))
{
return;
}
ble_bas_c_t * p_ble_bas_c = (ble_bas_c_t *)p_context;
switch (p_ble_evt->header.evt_id)
{
case BLE_GATTC_EVT_HVX:
on_hvx(p_ble_bas_c, p_ble_evt);
break;
case BLE_GATTC_EVT_READ_RSP:
on_read_rsp(p_ble_bas_c, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnected(p_ble_bas_c, p_ble_evt);
break;
default:
break;
}
}
uint32_t ble_bas_c_bl_notif_enable(ble_bas_c_t * p_ble_bas_c)
{
VERIFY_PARAM_NOT_NULL(p_ble_bas_c);
if (p_ble_bas_c->conn_handle == BLE_CONN_HANDLE_INVALID)
{
return NRF_ERROR_INVALID_STATE;
}
return cccd_configure(p_ble_bas_c, true);
}
uint32_t ble_bas_c_bl_read(ble_bas_c_t * p_ble_bas_c)
{
VERIFY_PARAM_NOT_NULL(p_ble_bas_c);
if (p_ble_bas_c->conn_handle == BLE_CONN_HANDLE_INVALID)
{
return NRF_ERROR_INVALID_STATE;
}
nrf_ble_gq_req_t bas_c_req;
memset(&bas_c_req, 0, sizeof(bas_c_req));
bas_c_req.type = NRF_BLE_GQ_REQ_GATTC_READ;
bas_c_req.error_handler.cb = gatt_error_handler;
bas_c_req.error_handler.p_ctx = p_ble_bas_c;
bas_c_req.params.gattc_read.handle = p_ble_bas_c->peer_bas_db.bl_handle;
return nrf_ble_gq_item_add(p_ble_bas_c->p_gatt_queue, &bas_c_req, p_ble_bas_c->conn_handle);
}
uint32_t ble_bas_c_handles_assign(ble_bas_c_t * p_ble_bas_c,
uint16_t conn_handle,
ble_bas_c_db_t * p_peer_handles)
{
VERIFY_PARAM_NOT_NULL(p_ble_bas_c);
p_ble_bas_c->conn_handle = conn_handle;
if (p_peer_handles != NULL)
{
p_ble_bas_c->peer_bas_db = *p_peer_handles;
}
return nrf_ble_gq_conn_handle_register(p_ble_bas_c->p_gatt_queue, conn_handle);
}
#endif // NRF_MODULE_ENABLED(BLE_BAS_C)

View File

@@ -0,0 +1,280 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**@file
*
* @defgroup ble_bas_c Battery Service Client
* @{
* @ingroup ble_sdk_srv
* @brief Battery Service Client module.
*
* @details This module contains APIs to read and interact with the Battery Service of a remote
* device.
*
* @note The application must register this module as the BLE event observer by using the
* NRF_SDH_BLE_OBSERVER macro. Example:
* @code
* ble_bas_c_t instance;
* NRF_SDH_BLE_OBSERVER(anything, BLE_BAS_C_BLE_OBSERVER_PRIO,
* ble_bas_c_on_ble_evt, &instance);
* @endcode
*/
#ifndef BLE_BAS_C_H__
#define BLE_BAS_C_H__
#include <stdint.h>
#include "ble.h"
#include "ble_db_discovery.h"
#include "ble_srv_common.h"
#include "nrf_ble_gq.h"
#include "nrf_sdh_ble.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Macro for defining a ble_bas_c instance.
*
* @param _name Name of the instance.
* @hideinitializer
*/
#define BLE_BAS_C_DEF(_name) \
static ble_bas_c_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_BAS_C_BLE_OBSERVER_PRIO, \
ble_bas_c_on_ble_evt, &_name)
/** @brief Macro for defining multiple ble_bas_c instances.
*
* @param _name Name of the array of instances.
* @param _cnt Number of instances to define.
* @hideinitializer
*/
#define BLE_BAS_C_ARRAY_DEF(_name, _cnt) \
static ble_bas_c_t _name[_cnt]; \
NRF_SDH_BLE_OBSERVERS(_name ## _obs, \
BLE_BAS_C_BLE_OBSERVER_PRIO, \
ble_bas_c_on_ble_evt, &_name, _cnt)
/**
* @defgroup bas_c_enums Enumerations
* @{
*/
/**@brief Battery Service Client event type. */
typedef enum
{
BLE_BAS_C_EVT_DISCOVERY_COMPLETE, /**< Event indicating that the Battery Service was discovered at the peer. */
BLE_BAS_C_EVT_BATT_NOTIFICATION, /**< Event indicating that a notification of the Battery Level characteristic was received from the peer. */
BLE_BAS_C_EVT_BATT_READ_RESP /**< Event indicating that a read response on Battery Level characteristic was received from the peer. */
} ble_bas_c_evt_type_t;
/** @} */
/**
* @defgroup bas_c_structs Structures
* @{
*/
/**@brief Structure containing the handles related to the Battery Service found on the peer. */
typedef struct
{
uint16_t bl_cccd_handle; /**< Handle of the CCCD of the Battery Level characteristic. */
uint16_t bl_handle; /**< Handle of the Battery Level characteristic, as provided by the SoftDevice. */
} ble_bas_c_db_t;
/**@brief Battery Service Client Event structure. */
typedef struct
{
ble_bas_c_evt_type_t evt_type; /**< Event Type. */
uint16_t conn_handle; /**< Connection handle relevant to this event.*/
union
{
ble_bas_c_db_t bas_db; /**< Handles related to the Battery Service, found on the peer device. This is filled if the evt_type is @ref BLE_BAS_C_EVT_DISCOVERY_COMPLETE.*/
uint8_t battery_level; /**< Information about the battery level, received from peer. This field is used for the events @ref BLE_BAS_C_EVT_BATT_NOTIFICATION and @ref BLE_BAS_C_EVT_BATT_READ_RESP.*/
} params;
} ble_bas_c_evt_t;
/** @} */
/**
* @defgroup bas_c_types Types
* @{
*/
// Forward declaration of the ble_bas_t type.
typedef struct ble_bas_c_s ble_bas_c_t;
/**@brief Event handler type.
*
* @details This is the type of the event handler that is to be provided by the application
* of the BAS module to receive events.
*/
typedef void (* ble_bas_c_evt_handler_t) (ble_bas_c_t * p_bas_bas_c, ble_bas_c_evt_t * p_evt);
/** @} */
/**
* @addtogroup bas_c_structs
* @{
*/
/**@brief Battery Service Client structure. */
struct ble_bas_c_s
{
uint16_t conn_handle; /**< Connection handle, as provided by the SoftDevice. */
ble_bas_c_db_t peer_bas_db; /**< Handles related to BAS on the peer.*/
ble_bas_c_evt_handler_t evt_handler; /**< Application event handler to be called when there is an event related to the Battery Service. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to the BLE GATT Queue instance. */
};
/**@brief Battery Service Client initialization structure. */
typedef struct
{
ble_bas_c_evt_handler_t evt_handler; /**< Event handler to be called by the Battery Service Client module when there is an event related to the Battery Service. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to the BLE GATT Queue instance. */
} ble_bas_c_init_t;
/** @} */
/**
* @defgroup bas_c_functions Functions
* @{
*/
/**@brief Function for initializing the Battery Service Client module.
*
* @details This function initializes the module and sets up database discovery to discover
* the Battery Service. After calling this function, call @ref ble_db_discovery_start
* to start discovery once a link with a peer has been established.
*
* @param[out] p_ble_bas_c Pointer to the Battery Service Client structure.
* @param[in] p_ble_bas_c_init Pointer to the Battery Service initialization structure that contains
* the initialization information.
*
* @retval NRF_SUCCESS Operation success.
* @retval NRF_ERROR_NULL A parameter is NULL.
* @retval err_code Otherwise, an error code returned by @ref ble_db_discovery_evt_register.
*/
uint32_t ble_bas_c_init(ble_bas_c_t * p_ble_bas_c, ble_bas_c_init_t * p_ble_bas_c_init);
/**@brief Function for handling BLE events from the SoftDevice.
*
* @details This function handles the BLE events received from the SoftDevice. If a BLE
* event is relevant to the Battery Service Client module, the function uses the event's data to update
* interval variables and, if necessary, send events to the application.
*
* @note This function must be called by the application.
*
* @param[in] p_ble_evt Pointer to the BLE event.
* @param[in] p_context Pointer to the Battery Service client structure.
*/
void ble_bas_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for enabling notifications on the Battery Level characteristic.
*
* @details This function enables the notification of the Battery Level characteristic at the
* peer by writing to the CCCD of the Battery Level Characteristic.
*
* @param p_ble_bas_c Pointer to the Battery Service client structure.
*
* @retval NRF_SUCCESS If the SoftDevice has been requested to write to the CCCD of the peer.
* @retval NRF_ERROR_NULL Parameter is NULL.
* @retval err_code Otherwise, an error code returned by the SoftDevice API @ref
* sd_ble_gattc_write.
*/
uint32_t ble_bas_c_bl_notif_enable(ble_bas_c_t * p_ble_bas_c);
/**@brief Function for reading the Battery Level characteristic.
*
* @param p_ble_bas_c Pointer to the Battery Service client structure.
*
* @retval NRF_SUCCESS If the read request was successfully queued to be sent to peer.
*/
uint32_t ble_bas_c_bl_read(ble_bas_c_t * p_ble_bas_c);
/**@brief Function for handling events from the Database Discovery module.
*
* @details Call this function when you get a callback event from the Database Discovery module.
* This function handles an event from the Database Discovery module, and determines
* whether it relates to the discovery of Battery Service at the peer. If it does, this function
* calls the application's event handler to indicate that the Battery Service was
* discovered at the peer. The function also populates the event with service-related
* information before providing it to the application.
*
* @param p_ble_bas_c Pointer to the Battery Service client structure.
* @param[in] p_evt Pointer to the event received from the Database Discovery module.
*
*/
void ble_bas_on_db_disc_evt(ble_bas_c_t * p_ble_bas_c, ble_db_discovery_evt_t const * p_evt);
/**@brief Function for assigning handles to this instance of bas_c.
*
* @details Call this function when a link has been established with a peer to
* associate the link to this instance of the module. This makes it
* possible to handle several links and associate each link to a particular
* instance of this module. The connection handle and attribute handles are
* provided from the discovery event @ref BLE_BAS_C_EVT_DISCOVERY_COMPLETE.
*
* @param[in] p_ble_bas_c Pointer to the Battery client structure instance for associating the link.
* @param[in] conn_handle Connection handle associated with the given Battery Client Instance.
* @param[in] p_peer_handles Attribute handles on the BAS server you want this BAS client to
* interact with.
*/
uint32_t ble_bas_c_handles_assign(ble_bas_c_t * p_ble_bas_c,
uint16_t conn_handle,
ble_bas_c_db_t * p_peer_handles);
/** @} */ // End tag for Function group.
#ifdef __cplusplus
}
#endif
#endif // BLE_BAS_C_H__
/** @} */ // End tag for the file.

View File

@@ -0,0 +1,420 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* Attention!
* To maintain compliance with Nordic Semiconductor ASA's Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_BPS)
#include "ble_bps.h"
#include "ble_err.h"
#include <string.h>
#include "nordic_common.h"
#include "ble_srv_common.h"
#define OPCODE_LENGTH 1 /**< Length of opcode inside Blood Pressure Measurement packet. */
#define HANDLE_LENGTH 2 /**< Length of handle inside Blood Pressure Measurement packet. */
#define MAX_BPM_LEN (BLE_GATT_ATT_MTU_DEFAULT - OPCODE_LENGTH - HANDLE_LENGTH) /**< Maximum size of a transmitted Blood Pressure Measurement. */
// Blood Pressure Measurement Flags bits
#define BPS_MEAS_BLOOD_PRESSURE_UNITS_FLAG_BIT (0x01 << 0) /**< Blood Pressure Units Flag bit. */
#define BPS_MEAS_TIME_STAMP_FLAG_BIT (0x01 << 1) /**< Time Stamp Flag bit. */
#define BPS_MEAS_PULSE_RATE_FLAG_BIT (0x01 << 2) /**< Pulse Rate Flag bit. */
#define BPS_MEAS_USER_ID_FLAG_BIT (0x01 << 3) /**< User ID Flag bit. */
#define BPS_MEAS_MEASUREMENT_STATUS_FLAG_BIT (0x01 << 4) /**< Measurement Status Flag bit. */
/**@brief Function for interception of GATT errors and @ref nrf_ble_gq errors.
*
* @param[in] nrf_error Error code.
* @param[in] p_ctx Parameter from the event handler.
* @param[in] conn_handle Connection handle.
*/
static void gatt_error_handler(uint32_t nrf_error,
void * p_ctx,
uint16_t conn_handle)
{
ble_bps_t * p_bps = (ble_bps_t *)p_ctx;
if (p_bps->error_handler != NULL)
{
p_bps->error_handler(nrf_error);
}
}
/**@brief Function for handling the Connect event.
*
* @param[in] p_bps Blood Pressure Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_connect(ble_bps_t * p_bps, ble_evt_t const * p_ble_evt)
{
ret_code_t err_code;
p_bps->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
err_code = nrf_ble_gq_conn_handle_register(p_bps->p_gatt_queue,
p_ble_evt->evt.gap_evt.conn_handle);
if ((p_bps->error_handler != NULL) &&
(err_code != NRF_SUCCESS))
{
p_bps->error_handler(err_code);
}
}
/**@brief Function for handling the Disconnect event.
*
* @param[in] p_bps Blood Pressure Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_disconnect(ble_bps_t * p_bps, ble_evt_t const * p_ble_evt)
{
UNUSED_PARAMETER(p_ble_evt);
p_bps->conn_handle = BLE_CONN_HANDLE_INVALID;
}
/**@brief Function for handling the write events to the Blood Pressure Measurement characteristic.
*
* @param[in] p_bps Blood Pressure Service structure.
* @param[in] p_evt_write Write event received from the BLE stack.
*/
static void on_cccd_write(ble_bps_t * p_bps, ble_gatts_evt_write_t const * p_evt_write)
{
if (p_evt_write->len == 2)
{
// CCCD written, update indication state
if (p_bps->evt_handler != NULL)
{
ble_bps_evt_t evt;
if (ble_srv_is_indication_enabled(p_evt_write->data))
{
evt.evt_type = BLE_BPS_EVT_INDICATION_ENABLED;
}
else
{
evt.evt_type = BLE_BPS_EVT_INDICATION_DISABLED;
}
p_bps->evt_handler(p_bps, &evt);
}
}
}
/**@brief Function for handling the Write event.
*
* @param[in] p_bps Blood Pressure Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_write(ble_bps_t * p_bps, ble_evt_t const * p_ble_evt)
{
ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
if (p_evt_write->handle == p_bps->meas_handles.cccd_handle)
{
on_cccd_write(p_bps, p_evt_write);
}
}
/**@brief Function for handling the HVC event.
*
* @details Handles HVC events from the BLE stack.
*
* @param[in] p_bps Blood Pressure Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_hvc(ble_bps_t * p_bps, ble_evt_t const * p_ble_evt)
{
ble_gatts_evt_hvc_t const * p_hvc = &p_ble_evt->evt.gatts_evt.params.hvc;
if (p_hvc->handle == p_bps->meas_handles.value_handle)
{
ble_bps_evt_t evt;
evt.evt_type = BLE_BPS_EVT_INDICATION_CONFIRMED;
p_bps->evt_handler(p_bps, &evt);
}
}
void ble_bps_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
ble_bps_t * p_bps = (ble_bps_t *)p_context;
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
on_connect(p_bps, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnect(p_bps, p_ble_evt);
break;
case BLE_GATTS_EVT_WRITE:
on_write(p_bps, p_ble_evt);
break;
case BLE_GATTS_EVT_HVC:
on_hvc(p_bps, p_ble_evt);
break;
default:
// No implementation needed.
break;
}
}
/**@brief Function for encoding a Blood Pressure Measurement.
*
* @param[in] p_bps Blood Pressure Service structure.
* @param[in] p_bps_meas Measurement to be encoded.
* @param[out] p_encoded_buffer Buffer where the encoded data will be written.
*
* @return Size of encoded data.
*/
static uint8_t bps_measurement_encode(ble_bps_t * p_bps,
ble_bps_meas_t * p_bps_meas,
uint8_t * p_encoded_buffer)
{
uint8_t flags = 0;
uint8_t len = 1;
uint16_t encoded_sfloat;
// Set measurement units flag
if (p_bps_meas->blood_pressure_units_in_kpa)
{
flags |= BPS_MEAS_BLOOD_PRESSURE_UNITS_FLAG_BIT;
}
// Blood Pressure Measurement - Systolic
encoded_sfloat = ((p_bps_meas->blood_pressure_systolic.exponent << 12) & 0xF000) |
((p_bps_meas->blood_pressure_systolic.mantissa << 0) & 0x0FFF);
len += uint16_encode(encoded_sfloat, &p_encoded_buffer[len]);
// Blood Pressure Measurement - Diastolic
encoded_sfloat = ((p_bps_meas->blood_pressure_diastolic.exponent << 12) & 0xF000) |
((p_bps_meas->blood_pressure_diastolic.mantissa << 0) & 0x0FFF);
len += uint16_encode(encoded_sfloat, &p_encoded_buffer[len]);
// Blood Pressure Measurement - Mean Arterial Pressure
encoded_sfloat = ((p_bps_meas->mean_arterial_pressure.exponent << 12) & 0xF000) |
((p_bps_meas->mean_arterial_pressure.mantissa << 0) & 0x0FFF);
len += uint16_encode(encoded_sfloat, &p_encoded_buffer[len]);
// Time Stamp field
if (p_bps_meas->time_stamp_present)
{
flags |= BPS_MEAS_TIME_STAMP_FLAG_BIT;
len += ble_date_time_encode(&p_bps_meas->time_stamp, &p_encoded_buffer[len]);
}
// Pulse Rate
if (p_bps_meas->pulse_rate_present)
{
flags |= BPS_MEAS_PULSE_RATE_FLAG_BIT;
encoded_sfloat = ((p_bps_meas->pulse_rate.exponent << 12) & 0xF000) |
((p_bps_meas->pulse_rate.mantissa << 0) & 0x0FFF);
len += uint16_encode(encoded_sfloat, &p_encoded_buffer[len]);
}
// User ID
if (p_bps_meas->user_id_present)
{
flags |= BPS_MEAS_USER_ID_FLAG_BIT;
p_encoded_buffer[len++] = p_bps_meas->user_id;
}
// Measurement Status
if (p_bps_meas->measurement_status_present)
{
flags |= BPS_MEAS_MEASUREMENT_STATUS_FLAG_BIT;
len += uint16_encode(p_bps_meas->measurement_status, &p_encoded_buffer[len]);
}
// Flags field
p_encoded_buffer[0] = flags;
return len;
}
uint32_t ble_bps_init(ble_bps_t * p_bps, ble_bps_init_t const * p_bps_init)
{
VERIFY_PARAM_NOT_NULL(p_bps);
VERIFY_PARAM_NOT_NULL(p_bps_init);
VERIFY_PARAM_NOT_NULL(p_bps_init->p_gatt_queue);
uint32_t err_code;
uint8_t init_value_encoded[MAX_BPM_LEN];
uint8_t initial_feature_len;
ble_bps_meas_t initial_bpm;
ble_uuid_t ble_uuid;
ble_add_char_params_t add_char_params;
// Initialize service structure
p_bps->evt_handler = p_bps_init->evt_handler;
p_bps->error_handler = p_bps_init->error_handler;
p_bps->p_gatt_queue = p_bps_init->p_gatt_queue;
p_bps->conn_handle = BLE_CONN_HANDLE_INVALID;
p_bps->feature = p_bps_init->feature;
// Add service
BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_BLOOD_PRESSURE_SERVICE);
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_bps->service_handle);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
// Add measurement characteristic
memset(&initial_bpm, 0, sizeof(initial_bpm));
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = BLE_UUID_BLOOD_PRESSURE_MEASUREMENT_CHAR;
add_char_params.max_len = MAX_BPM_LEN;
add_char_params.is_var_len = true;
add_char_params.init_len = bps_measurement_encode(p_bps, &initial_bpm, init_value_encoded);
add_char_params.p_init_value = init_value_encoded;
add_char_params.char_props.indicate = 1;
add_char_params.cccd_write_access = p_bps_init->bp_cccd_wr_sec;
err_code = characteristic_add(p_bps->service_handle, &add_char_params, &p_bps->meas_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
// Add feature characteristic
initial_feature_len = uint16_encode(p_bps_init->feature, init_value_encoded);
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = BLE_UUID_BLOOD_PRESSURE_FEATURE_CHAR;
add_char_params.max_len = initial_feature_len;
add_char_params.init_len = initial_feature_len;
add_char_params.p_init_value = init_value_encoded;
add_char_params.char_props.read = 1;
add_char_params.read_access = p_bps_init->bp_feature_rd_sec;
err_code = characteristic_add(p_bps->service_handle, &add_char_params, &p_bps->feature_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
return NRF_SUCCESS;
}
uint32_t ble_bps_measurement_send(ble_bps_t * p_bps, ble_bps_meas_t * p_bps_meas)
{
uint32_t err_code;
// Send value if connected
if (p_bps->conn_handle != BLE_CONN_HANDLE_INVALID)
{
uint8_t encoded_bps_meas[MAX_BPM_LEN];
uint16_t len;
nrf_ble_gq_req_t bps_req;
len = bps_measurement_encode(p_bps, p_bps_meas, encoded_bps_meas);
memset(&bps_req, 0, sizeof(nrf_ble_gq_req_t));
bps_req.type = NRF_BLE_GQ_REQ_GATTS_HVX;
bps_req.error_handler.cb = gatt_error_handler;
bps_req.error_handler.p_ctx = p_bps;
bps_req.params.gatts_hvx.handle = p_bps->meas_handles.value_handle;
bps_req.params.gatts_hvx.offset = 0;
bps_req.params.gatts_hvx.p_data = encoded_bps_meas;
bps_req.params.gatts_hvx.p_len = &len;
bps_req.params.gatts_hvx.type = BLE_GATT_HVX_INDICATION;
err_code = nrf_ble_gq_item_add(p_bps->p_gatt_queue, &bps_req, p_bps->conn_handle);
}
else
{
err_code = NRF_ERROR_INVALID_STATE;
}
return err_code;
}
uint32_t ble_bps_is_indication_enabled(ble_bps_t * p_bps, bool * p_indication_enabled)
{
uint32_t err_code;
uint8_t cccd_value_buf[BLE_CCCD_VALUE_LEN];
ble_gatts_value_t gatts_value;
// Initialize value struct.
memset(&gatts_value, 0, sizeof(gatts_value));
gatts_value.len = BLE_CCCD_VALUE_LEN;
gatts_value.offset = 0;
gatts_value.p_value = cccd_value_buf;
err_code = sd_ble_gatts_value_get(p_bps->conn_handle,
p_bps->meas_handles.cccd_handle,
&gatts_value);
if (err_code == NRF_SUCCESS)
{
*p_indication_enabled = ble_srv_is_indication_enabled(cccd_value_buf);
}
if (err_code == BLE_ERROR_GATTS_SYS_ATTR_MISSING)
{
*p_indication_enabled = false;
return NRF_SUCCESS;
}
return err_code;
}
#endif // NRF_MODULE_ENABLED(BLE_BPS)

View File

@@ -0,0 +1,226 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_bps Blood Pressure Service
* @{
* @ingroup ble_sdk_srv
* @brief Blood Pressure Service module.
*
* @details This module implements the Blood Pressure Service.
*
* If an event handler is supplied by the application, the Blood Pressure
* Service will generate Blood Pressure Service events to the application.
*
* @note The application must register this module as BLE event observer using the
* NRF_SDH_BLE_OBSERVER macro. Example:
* @code
* ble_bps_t instance;
* NRF_SDH_BLE_OBSERVER(anything, BLE_BPS_BLE_OBSERVER_PRIO,
* ble_bps_on_ble_evt, &instance);
* @endcode
*
* @note Attention!
* To maintain compliance with Nordic Semiconductor ASA Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#ifndef BLE_BPS_H__
#define BLE_BPS_H__
#include <stdint.h>
#include <stdbool.h>
#include "ble.h"
#include "ble_srv_common.h"
#include "ble_date_time.h"
#include "nrf_sdh_ble.h"
#include "nrf_ble_gq.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Macro for defining a ble_bps instance.
*
* @param _name Name of the instance.
* @hideinitializer
*/
#define BLE_BPS_DEF(_name) \
static ble_bps_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_BPS_BLE_OBSERVER_PRIO, \
ble_bps_on_ble_evt, &_name)
// Blood Pressure Feature bits
#define BLE_BPS_FEATURE_BODY_MOVEMENT_BIT (0x01 << 0) /**< Body Movement Detection Support bit. */
#define BLE_BPS_FEATURE_CUFF_FIT_BIT (0x01 << 1) /**< Cuff Fit Detection Support bit. */
#define BLE_BPS_FEATURE_IRREGULAR_PULSE_BIT (0x01 << 2) /**< Irregular Pulse Detection Support bit. */
#define BLE_BPS_FEATURE_PULSE_RATE_RANGE_BIT (0x01 << 3) /**< Pulse Rate Range Detection Support bit. */
#define BLE_BPS_FEATURE_MEASUREMENT_POSITION_BIT (0x01 << 4) /**< Measurement Position Detection Support bit. */
#define BLE_BPS_FEATURE_MULTIPLE_BOND_BIT (0x01 << 5) /**< Multiple Bond Support bit. */
/**@brief Blood Pressure Service event type. */
typedef enum
{
BLE_BPS_EVT_INDICATION_ENABLED, /**< Blood Pressure value indication enabled event. */
BLE_BPS_EVT_INDICATION_DISABLED, /**< Blood Pressure value indication disabled event. */
BLE_BPS_EVT_INDICATION_CONFIRMED /**< Confirmation of a blood pressure measurement indication has been received. */
} ble_bps_evt_type_t;
/**@brief Blood Pressure Service event. */
typedef struct
{
ble_bps_evt_type_t evt_type; /**< Type of event. */
} ble_bps_evt_t;
// Forward declaration of the ble_bps_t type.
typedef struct ble_bps_s ble_bps_t;
/**@brief Blood Pressure Service event handler type. */
typedef void (*ble_bps_evt_handler_t) (ble_bps_t * p_bps, ble_bps_evt_t * p_evt);
/**@brief SFLOAT format (IEEE-11073 16-bit FLOAT, defined as a 16-bit vlue with 12-bit mantissa and
* 4-bit exponent. */
typedef struct
{
int8_t exponent; /**< Base 10 exponent, only 4 bits */
int16_t mantissa; /**< Mantissa, only 12 bits */
} ieee_float16_t;
/**@brief Blood Pressure Service init structure. This contains all options and data
* needed for initialization of the service. */
typedef struct
{
ble_bps_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Blood Pressure Service. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
security_req_t bp_cccd_wr_sec; /**< Security requirement for writing blood pressure measurement characteristic CCCD. */
security_req_t bp_feature_rd_sec; /**< Security requirement for reading the blood pressure feature characteristic. */
uint16_t feature; /**< Initial value for blood pressure feature */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to BLE GATT Queue instance. */
} ble_bps_init_t;
/**@brief Blood Pressure Service structure. This contains various status information for
* the service. */
struct ble_bps_s
{
ble_bps_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Blood Pressure Service. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
uint16_t service_handle; /**< Handle of Blood Pressure Service (as provided by the BLE stack). */
ble_gatts_char_handles_t meas_handles; /**< Handles related to the Blood Pressure Measurement characteristic. */
ble_gatts_char_handles_t feature_handles; /**< Handles related to the Blood Pressure Feature characteristic. */
uint16_t conn_handle; /**< Handle of the current connection (as provided by the BLE stack, is BLE_CONN_HANDLE_INVALID if not in a connection). */
uint16_t feature; /**< Value of Blood Pressure feature. */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to BLE GATT Queue instance. */
};
/**@brief Blood Pressure Service measurement structure. This contains a Blood Pressure
* measurement. */
typedef struct ble_bps_meas_s
{
bool blood_pressure_units_in_kpa; /**< Blood Pressure Units Flag, 0=mmHg, 1=kPa */
bool time_stamp_present; /**< Time Stamp Flag, 0=not present, 1=present. */
bool pulse_rate_present; /**< Pulse Rate Flag, 0=not present, 1=present. */
bool user_id_present; /**< User ID Flag, 0=not present, 1=present. */
bool measurement_status_present; /**< Measurement Status Flag, 0=not present, 1=present. */
ieee_float16_t blood_pressure_systolic; /**< Blood Pressure Measurement Compound Value - Systolic. */
ieee_float16_t blood_pressure_diastolic; /**< Blood Pressure Measurement Compound Value - Diastolic . */
ieee_float16_t mean_arterial_pressure; /**< Blood Pressure Measurement Compound Value - Mean Arterial Pressure. */
ble_date_time_t time_stamp; /**< Time Stamp. */
ieee_float16_t pulse_rate; /**< Pulse Rate. */
uint8_t user_id; /**< User ID. */
uint16_t measurement_status; /**< Measurement Status. */
} ble_bps_meas_t;
/**@brief Function for initializing the Blood Pressure Service.
*
* @param[out] p_bps Blood Pressure Service structure. This structure will have to
* be supplied by the application. It will be initialized by this function,
* and will later be used to identify this particular service instance.
* @param[in] p_bps_init Information needed to initialize the service.
*
* @return NRF_SUCCESS on successful initialization of service, otherwise an error code.
*/
uint32_t ble_bps_init(ble_bps_t * p_bps, ble_bps_init_t const * p_bps_init);
/**@brief Function for handling the Application's BLE Stack events.
*
* @details Handles all events from the BLE stack of interest to the Blood Pressure Service.
*
* @param[in] p_ble_evt Event received from the BLE stack.
* @param[in] p_context Blood Pressure Service structure.
*/
void ble_bps_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for sending blood pressure measurement if indication has been enabled.
*
* @details The application calls this function after having performed a Blood Pressure
* measurement. If indication has been enabled, the measurement data is encoded and
* sent to the client.
*
* @param[in] p_bps Blood Pressure Service structure.
* @param[in] p_bps_meas Pointer to new blood pressure measurement.
*
* @return NRF_SUCCESS on success, otherwise an error code.
*/
uint32_t ble_bps_measurement_send(ble_bps_t * p_bps, ble_bps_meas_t * p_bps_meas);
/**@brief Function for checking if indication of Blood Pressure Measurement is currently enabled.
*
* @param[in] p_bps Blood Pressure Service structure.
* @param[out] p_indication_enabled TRUE if indication is enabled, FALSE otherwise.
*
* @return NRF_SUCCESS on success, otherwise an error code.
*/
uint32_t ble_bps_is_indication_enabled(ble_bps_t * p_bps, bool * p_indication_enabled);
#ifdef __cplusplus
}
#endif
#endif // BLE_BPS_H__
/** @} */

View File

@@ -0,0 +1,344 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* Attention!
* To maintain compliance with Nordic Semiconductor ASA's Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#include "ble_cscs.h"
#include <string.h>
#include "nordic_common.h"
#include "ble.h"
#include "ble_err.h"
#include "ble_srv_common.h"
#include "app_util.h"
#define OPCODE_LENGTH 1 /**< Length of opcode inside Cycling Speed and Cadence Measurement packet. */
#define HANDLE_LENGTH 2 /**< Length of handle inside Cycling Speed and Cadence Measurement packet. */
#define MAX_CSCM_LEN (BLE_GATT_ATT_MTU_DEFAULT - OPCODE_LENGTH - HANDLE_LENGTH) /**< Maximum size of a transmitted Cycling Speed and Cadence Measurement. */
// Cycling Speed and Cadence Measurement flag bits
#define CSC_MEAS_FLAG_MASK_WHEEL_REV_DATA_PRESENT (0x01 << 0) /**< Wheel revolution data present flag bit. */
#define CSC_MEAS_FLAG_MASK_CRANK_REV_DATA_PRESENT (0x01 << 1) /**< Crank revolution data present flag bit. */
/**@brief Function for handling the Connect event.
*
* @param[in] p_cscs Cycling Speed and Cadence Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_connect(ble_cscs_t * p_cscs, ble_evt_t const * p_ble_evt)
{
p_cscs->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
}
/**@brief Function for handling the Disconnect event.
*
* @param[in] p_cscs Cycling Speed and Cadence Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_disconnect(ble_cscs_t * p_cscs, ble_evt_t const * p_ble_evt)
{
UNUSED_PARAMETER(p_ble_evt);
p_cscs->conn_handle = BLE_CONN_HANDLE_INVALID;
}
/**@brief Function for handling write events to the CSCS Measurement characteristic.
*
* @param[in] p_cscs Cycling Speed and Cadence Service structure.
* @param[in] p_evt_write Write event received from the BLE stack.
*/
static void on_meas_cccd_write(ble_cscs_t * p_cscs, ble_gatts_evt_write_t const * p_evt_write)
{
if (p_evt_write->len == 2)
{
// CCCD written, update notification state
if (p_cscs->evt_handler != NULL)
{
ble_cscs_evt_t evt;
if (ble_srv_is_notification_enabled(p_evt_write->data))
{
evt.evt_type = BLE_CSCS_EVT_NOTIFICATION_ENABLED;
}
else
{
evt.evt_type = BLE_CSCS_EVT_NOTIFICATION_DISABLED;
}
p_cscs->evt_handler(p_cscs, &evt);
}
}
}
/**@brief Function for handling the Write event.
*
* @param[in] p_cscs Cycling Speed and Cadence Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_write(ble_cscs_t * p_cscs, ble_evt_t const * p_ble_evt)
{
ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
if (p_evt_write->handle == p_cscs->meas_handles.cccd_handle)
{
on_meas_cccd_write(p_cscs, p_evt_write);
}
}
void ble_cscs_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
ble_cscs_t * p_cscs = (ble_cscs_t *)p_context;
if (p_cscs == NULL || p_ble_evt == NULL)
{
return;
}
ble_sc_ctrlpt_on_ble_evt(&(p_cscs->ctrl_pt), p_ble_evt);
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
on_connect(p_cscs, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnect(p_cscs, p_ble_evt);
break;
case BLE_GATTS_EVT_WRITE:
on_write(p_cscs, p_ble_evt);
break;
default:
// No implementation needed.
break;
}
}
/**@brief Function for encoding a CSCS Measurement.
*
* @param[in] p_cscs Cycling Speed and Cadence Service structure.
* @param[in] p_csc_measurement Measurement to be encoded.
* @param[out] p_encoded_buffer Buffer where the encoded data will be written.
*
* @return Size of encoded data.
*/
static uint8_t csc_measurement_encode(ble_cscs_t * p_cscs,
ble_cscs_meas_t * p_csc_measurement,
uint8_t * p_encoded_buffer)
{
uint8_t flags = 0;
uint8_t len = 1;
// Cumulative Wheel Revolutions and Last Wheel Event Time Fields
if (p_cscs->feature & BLE_CSCS_FEATURE_WHEEL_REV_BIT)
{
if (p_csc_measurement->is_wheel_rev_data_present)
{
flags |= CSC_MEAS_FLAG_MASK_WHEEL_REV_DATA_PRESENT;
len += uint32_encode(p_csc_measurement->cumulative_wheel_revs, &p_encoded_buffer[len]);
len += uint16_encode(p_csc_measurement->last_wheel_event_time, &p_encoded_buffer[len]);
}
}
// Cumulative Crank Revolutions and Last Crank Event Time Fields
if (p_cscs->feature & BLE_CSCS_FEATURE_CRANK_REV_BIT)
{
if (p_csc_measurement->is_crank_rev_data_present)
{
flags |= CSC_MEAS_FLAG_MASK_CRANK_REV_DATA_PRESENT;
len += uint16_encode(p_csc_measurement->cumulative_crank_revs, &p_encoded_buffer[len]);
len += uint16_encode(p_csc_measurement->last_crank_event_time, &p_encoded_buffer[len]);
}
}
// Flags Field
p_encoded_buffer[0] = flags;
return len;
}
uint32_t ble_cscs_init(ble_cscs_t * p_cscs, ble_cscs_init_t const * p_cscs_init)
{
if (p_cscs == NULL || p_cscs_init == NULL)
{
return NRF_ERROR_NULL;
}
uint32_t err_code;
uint8_t init_value_encoded[MAX_CSCM_LEN];
ble_cscs_meas_t initial_scm = {0};
ble_add_char_params_t add_char_params;
ble_uuid_t ble_uuid;
ble_cs_ctrlpt_init_t sc_ctrlpt_init;
// Initialize service structure
p_cscs->evt_handler = p_cscs_init->evt_handler;
p_cscs->conn_handle = BLE_CONN_HANDLE_INVALID;
p_cscs->feature = p_cscs_init->feature;
// Add service
BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_CYCLING_SPEED_AND_CADENCE);
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
&ble_uuid,
&p_cscs->service_handle);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
// Add cycling speed and cadence measurement characteristic
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = BLE_UUID_CSC_MEASUREMENT_CHAR;
add_char_params.max_len = MAX_CSCM_LEN;
add_char_params.is_var_len = true;
add_char_params.init_len = csc_measurement_encode(p_cscs, &initial_scm, init_value_encoded);
add_char_params.p_init_value = init_value_encoded;
add_char_params.char_props.notify = 1;
add_char_params.cccd_write_access = p_cscs_init->csc_meas_cccd_wr_sec;
err_code = characteristic_add(p_cscs->service_handle, &add_char_params, &p_cscs->meas_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
// Add cycling speed and cadence feature characteristic
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = BLE_UUID_CSC_FEATURE_CHAR;
add_char_params.max_len = sizeof(uint16_t);
add_char_params.init_len = uint16_encode(p_cscs_init->feature, &init_value_encoded[0]);
add_char_params.p_init_value = init_value_encoded;
add_char_params.char_props.read = 1;
add_char_params.read_access = p_cscs_init->csc_feature_rd_sec;
err_code = characteristic_add(p_cscs->service_handle,
&add_char_params,
&p_cscs->feature_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
// Add Sensor Location characteristic (optional)
if (p_cscs_init->sensor_location != NULL)
{
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = BLE_UUID_SENSOR_LOCATION_CHAR;
add_char_params.max_len = sizeof(uint8_t);
add_char_params.init_len = sizeof(uint8_t);
add_char_params.p_init_value = (uint8_t *)p_cscs_init->sensor_location;
add_char_params.char_props.read = 1;
add_char_params.read_access = p_cscs_init->csc_location_rd_sec;
err_code = characteristic_add(p_cscs->service_handle, &add_char_params, &p_cscs->sensor_loc_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
}
// Add speed and cadence control point characteristic
sc_ctrlpt_init.error_handler = p_cscs_init->error_handler;
sc_ctrlpt_init.size_list_supported_locations = p_cscs_init->size_list_supported_locations;
sc_ctrlpt_init.supported_functions = p_cscs_init->ctrplt_supported_functions;
sc_ctrlpt_init.evt_handler = p_cscs_init->ctrlpt_evt_handler;
sc_ctrlpt_init.list_supported_locations = p_cscs_init->list_supported_locations;
sc_ctrlpt_init.sc_ctrlpt_wr_sec = p_cscs_init->sc_ctrlpt_wr_sec;
sc_ctrlpt_init.sc_ctrlpt_cccd_wr_sec = p_cscs_init->sc_ctrlpt_cccd_wr_sec;
sc_ctrlpt_init.sensor_location_handle = p_cscs->sensor_loc_handles.value_handle;
sc_ctrlpt_init.service_handle = p_cscs->service_handle;
return ble_sc_ctrlpt_init(&p_cscs->ctrl_pt, &sc_ctrlpt_init);
}
uint32_t ble_cscs_measurement_send(ble_cscs_t * p_cscs, ble_cscs_meas_t * p_measurement)
{
if (p_cscs == NULL || p_measurement == NULL)
{
return NRF_ERROR_NULL;
}
uint32_t err_code;
// Send value if connected and notifying
if (p_cscs->conn_handle != BLE_CONN_HANDLE_INVALID)
{
uint8_t encoded_csc_meas[MAX_CSCM_LEN];
uint16_t len;
uint16_t hvx_len;
ble_gatts_hvx_params_t hvx_params;
len = csc_measurement_encode(p_cscs, p_measurement, encoded_csc_meas);
hvx_len = len;
memset(&hvx_params, 0, sizeof(hvx_params));
hvx_params.handle = p_cscs->meas_handles.value_handle;
hvx_params.type = BLE_GATT_HVX_NOTIFICATION;
hvx_params.offset = 0;
hvx_params.p_len = &hvx_len;
hvx_params.p_data = encoded_csc_meas;
err_code = sd_ble_gatts_hvx(p_cscs->conn_handle, &hvx_params);
if ((err_code == NRF_SUCCESS) && (hvx_len != len))
{
err_code = NRF_ERROR_DATA_SIZE;
}
}
else
{
err_code = NRF_ERROR_INVALID_STATE;
}
return err_code;
}

View File

@@ -0,0 +1,219 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_cscs Cycling Speed and Cadence Service
* @{
* @ingroup ble_sdk_srv
* @brief Cycling Speed and Cadence Service module.
*
* @details This module implements the Cycling Speed and Cadence Service. If enabled, notification
* of the Cycling Speead and Candence Measurement is performed when the application
* calls ble_cscs_measurement_send().
*
* To use this service, you need to provide the the supported features (@ref BLE_CSCS_FEATURES).
* If you choose to support Wheel revolution data (feature bit @ref BLE_CSCS_FEATURE_WHEEL_REV_BIT),
* you then need to support the 'setting of cumulative value' operation by the supporting the
* Speed and Cadence Control Point (@ref ble_sdk_srv_sc_ctrlpt) by setting the @ref BLE_SRV_SC_CTRLPT_CUM_VAL_OP_SUPPORTED
* bit of the ctrplt_supported_functions in the @ref ble_cscs_init_t structure.
* If you want to support the 'start autocalibration' control point feature, you need, after the @ref BLE_SC_CTRLPT_EVT_START_CALIBRATION
* has been received and the auto calibration is finished, to call the @ref ble_sc_ctrlpt_rsp_send to indicate that the operation is finished
* and thus be able to receive new control point operations.
* If you want to support the 'sensor location' related operation, you need to provide a list of supported location in the
* @ref ble_cscs_init_t structure.
*
*
* @note The application must register this module as BLE event observer using the
* NRF_SDH_BLE_OBSERVER macro. Example:
* @code
* ble_cscs_t instance;
* NRF_SDH_BLE_OBSERVER(anything, BLE_CSCS_BLE_OBSERVER_PRIO,
* ble_cscs_on_ble_evt, &instance);
* @endcode
*
* @note Attention!
* To maintain compliance with Nordic Semiconductor ASA Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#ifndef BLE_CSCS_H__
#define BLE_CSCS_H__
#include <stdint.h>
#include <stdbool.h>
#include "ble.h"
#include "ble_srv_common.h"
#include "ble_sc_ctrlpt.h"
#include "ble_sensor_location.h"
#include "nrf_sdh_ble.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Macro for defining a ble_cscs instance.
*
* @param _name Name of the instance.
* @hideinitializer
*/
#define BLE_CSCS_DEF(_name) \
static ble_cscs_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_CSCS_BLE_OBSERVER_PRIO, \
ble_cscs_on_ble_evt, &_name)
/** @defgroup BLE_CSCS_FEATURES Cycling Speed and Cadence Service feature bits
* @{ */
#define BLE_CSCS_FEATURE_WHEEL_REV_BIT (0x01 << 0) /**< Wheel Revolution Data Supported bit. */
#define BLE_CSCS_FEATURE_CRANK_REV_BIT (0x01 << 1) /**< Crank Revolution Data Supported bit. */
#define BLE_CSCS_FEATURE_MULTIPLE_SENSORS_BIT (0x01 << 2) /**< Multiple Sensor Locations Supported bit. */
/** @} */
/**@brief Cycling Speed and Cadence Service event type. */
typedef enum
{
BLE_CSCS_EVT_NOTIFICATION_ENABLED, /**< Cycling Speed and Cadence value notification enabled event. */
BLE_CSCS_EVT_NOTIFICATION_DISABLED /**< Cycling Speed and Cadence value notification disabled event. */
} ble_cscs_evt_type_t;
/**@brief Cycling Speed and Cadence Service event. */
typedef struct
{
ble_cscs_evt_type_t evt_type; /**< Type of event. */
} ble_cscs_evt_t;
// Forward declaration of the ble_csc_t type.
typedef struct ble_cscs_s ble_cscs_t;
/**@brief Cycling Speed and Cadence Service event handler type. */
typedef void (*ble_cscs_evt_handler_t) (ble_cscs_t * p_cscs, ble_cscs_evt_t * p_evt);
/**@brief Cycling Speed and Cadence Service init structure. This contains all options and data
* needed for initialization of the service. */
typedef struct
{
ble_cscs_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Cycling Speed and Cadence Service. */
security_req_t csc_meas_cccd_wr_sec; /**< Security requirement for writing cycling speed and cadence measurement characteristic CCCD. */
security_req_t csc_feature_rd_sec; /**< Security requirement for reading cycling speed and cadence feature characteristic. */
security_req_t csc_location_rd_sec; /**< Security requirement for reading cycling speed and cadence location characteristic. */
security_req_t sc_ctrlpt_cccd_wr_sec; /**< Security requirement for writing speed and cadence control point characteristic CCCD. */
security_req_t sc_ctrlpt_wr_sec; /**< Security requirement for writing speed and cadence control point characteristic. */
uint16_t feature; /**< Initial value for features of sensor @ref BLE_CSCS_FEATURES. */
uint8_t ctrplt_supported_functions; /**< Supported control point functionalities see @ref BLE_SRV_SC_CTRLPT_SUPP_FUNC. */
ble_sc_ctrlpt_evt_handler_t ctrlpt_evt_handler; /**< Event handler */
ble_sensor_location_t *list_supported_locations; /**< List of supported sensor locations.*/
uint8_t size_list_supported_locations; /**< Number of supported sensor locations in the list.*/
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
ble_sensor_location_t *sensor_location; /**< Initial Sensor Location, if NULL, sensor_location characteristic is not added*/
} ble_cscs_init_t;
/**@brief Cycling Speed and Cadence Service structure. This contains various status information for
* the service. */
struct ble_cscs_s
{
ble_cscs_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Cycling Speed and Cadence Service. */
uint16_t service_handle; /**< Handle of Cycling Speed and Cadence Service (as provided by the BLE stack). */
ble_gatts_char_handles_t meas_handles; /**< Handles related to the Cycling Speed and Cadence Measurement characteristic. */
ble_gatts_char_handles_t feature_handles; /**< Handles related to the Cycling Speed and Cadence feature characteristic. */
ble_gatts_char_handles_t sensor_loc_handles; /**< Handles related to the Cycling Speed and Cadence Sensor Location characteristic. */
uint16_t conn_handle; /**< Handle of the current connection (as provided by the BLE stack, is BLE_CONN_HANDLE_INVALID if not in a connection). */
uint16_t feature; /**< Bit mask of features available on sensor. */
ble_sc_ctrlpt_t ctrl_pt; /**< data for speed and cadence control point */
};
/**@brief Cycling Speed and Cadence Service measurement structure. This contains a Cycling Speed and
* Cadence Service measurement. */
typedef struct ble_cscs_meas_s
{
bool is_wheel_rev_data_present; /**< True if Wheel Revolution Data is present in the measurement. */
bool is_crank_rev_data_present; /**< True if Crank Revolution Data is present in the measurement. */
uint32_t cumulative_wheel_revs; /**< Cumulative Wheel Revolutions. */
uint16_t last_wheel_event_time; /**< Last Wheel Event Time. */
uint16_t cumulative_crank_revs; /**< Cumulative Crank Revolutions. */
uint16_t last_crank_event_time; /**< Last Crank Event Time. */
} ble_cscs_meas_t;
/**@brief Function for initializing the Cycling Speed and Cadence Service.
*
* @param[out] p_cscs Cycling Speed and Cadence Service structure. This structure will have to
* be supplied by the application. It will be initialized by this function,
* and will later be used to identify this particular service instance.
* @param[in] p_cscs_init Information needed to initialize the service.
*
* @return NRF_SUCCESS on successful initialization of service, otherwise an error code.
*/
uint32_t ble_cscs_init(ble_cscs_t * p_cscs, ble_cscs_init_t const * p_cscs_init);
/**@brief Function for handling the Application's BLE Stack events.
*
* @details Handles all events from the BLE stack of interest to the Cycling Speed and Cadence
* Service.
*
* @param[in] p_ble_evt Event received from the BLE stack.
* @param[in] p_context Cycling Speed and Cadence Service structure.
*/
void ble_cscs_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for sending cycling speed and cadence measurement if notification has been enabled.
*
* @details The application calls this function after having performed a Cycling Speed and Cadence
* Service measurement. If notification has been enabled, the measurement data is encoded
* and sent to the client.
*
* @param[in] p_cscs Cycling Speed and Cadence Service structure.
* @param[in] p_measurement Pointer to new cycling speed and cadence measurement.
*
* @return NRF_SUCCESS on success, otherwise an error code.
*/
uint32_t ble_cscs_measurement_send(ble_cscs_t * p_cscs, ble_cscs_meas_t * p_measurement);
#ifdef __cplusplus
}
#endif
#endif // BLE_CSCS_H__
/** @} */

View File

@@ -0,0 +1,656 @@
/**
* Copyright (c) 2013 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* Attention!
* To maintain compliance with Nordic Semiconductor ASA's Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#include "ble_sc_ctrlpt.h"
#include <string.h>
#include "nordic_common.h"
#include "ble.h"
#include "ble_err.h"
#include "ble_srv_common.h"
#include "app_util.h"
#define SC_CTRLPT_NACK_PROC_ALREADY_IN_PROGRESS (BLE_GATT_STATUS_ATTERR_APP_BEGIN + 0)
#define SC_CTRLPT_NACK_CCCD_IMPROPERLY_CONFIGURED (BLE_GATT_STATUS_ATTERR_APP_BEGIN + 1)
uint32_t ble_sc_ctrlpt_init(ble_sc_ctrlpt_t * p_sc_ctrlpt,
const ble_cs_ctrlpt_init_t * p_sc_ctrlpt_init)
{
if (p_sc_ctrlpt == NULL || p_sc_ctrlpt_init == NULL)
{
return NRF_ERROR_NULL;
}
ble_add_char_params_t add_char_params;
p_sc_ctrlpt->conn_handle = BLE_CONN_HANDLE_INVALID;
p_sc_ctrlpt->procedure_status = BLE_SCPT_NO_PROC_IN_PROGRESS;
p_sc_ctrlpt->size_list_supported_locations = p_sc_ctrlpt_init->size_list_supported_locations;
if ((p_sc_ctrlpt_init->size_list_supported_locations != 0) &&
(p_sc_ctrlpt_init->list_supported_locations != NULL))
{
memcpy(p_sc_ctrlpt->list_supported_locations,
p_sc_ctrlpt_init->list_supported_locations,
p_sc_ctrlpt->size_list_supported_locations * sizeof(ble_sensor_location_t));
}
p_sc_ctrlpt->service_handle = p_sc_ctrlpt_init->service_handle;
p_sc_ctrlpt->evt_handler = p_sc_ctrlpt_init->evt_handler;
p_sc_ctrlpt->supported_functions = p_sc_ctrlpt_init->supported_functions;
p_sc_ctrlpt->sensor_location_handle = p_sc_ctrlpt_init->sensor_location_handle;
p_sc_ctrlpt->error_handler = p_sc_ctrlpt_init->error_handler;
// Add speed and cadence control point characteristic
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = BLE_UUID_SC_CTRLPT_CHAR;
add_char_params.max_len = BLE_SC_CTRLPT_MAX_LEN;
add_char_params.is_var_len = true;
add_char_params.char_props.indicate = 1;
add_char_params.char_props.write = 1;
add_char_params.cccd_write_access = p_sc_ctrlpt_init->sc_ctrlpt_cccd_wr_sec;
add_char_params.write_access = p_sc_ctrlpt_init->sc_ctrlpt_wr_sec;
add_char_params.is_defered_write = true;
return characteristic_add(p_sc_ctrlpt->service_handle,
&add_char_params,
&p_sc_ctrlpt->sc_ctrlpt_handles);
}
/**@brief Decode an incoming control point write.
*
* @param[in] rcvd_val received write value
* @param[in] len value length
* @param[out] decoded_ctrlpt decoded control point structure
*/
static uint32_t sc_ctrlpt_decode(uint8_t const * p_rcvd_val,
uint8_t len,
ble_sc_ctrlpt_val_t * p_write_val)
{
int pos = 0;
if (len < BLE_SC_CTRLPT_MIN_LEN)
{
return NRF_ERROR_INVALID_PARAM;
}
p_write_val->opcode = (ble_scpt_operator_t) p_rcvd_val[pos++];
switch (p_write_val->opcode)
{
case BLE_SCPT_REQUEST_SUPPORTED_SENSOR_LOCATIONS:
break;
case BLE_SCPT_START_AUTOMATIC_CALIBRATION:
break;
case BLE_SCPT_UPDATE_SENSOR_LOCATION:
p_write_val->location = (ble_sensor_location_t)p_rcvd_val[pos];
break;
case BLE_SCPT_SET_CUMULATIVE_VALUE:
p_write_val->cumulative_value = uint32_decode(&(p_rcvd_val[pos]));
break;
default:
return NRF_ERROR_INVALID_PARAM;
}
return NRF_SUCCESS;
}
/**@brief encode a control point response indication.
*
* @param[in] p_sc_ctrlpt SC Ctrlpt structure.
* @param[in] p_ctrlpt_rsp structure containing response data to be encoded
* @param[out] p_data pointer where data needs to be written
* @return size of encoded data
*/
static int ctrlpt_rsp_encode(ble_sc_ctrlpt_t * p_sc_ctrlpt,
ble_sc_ctrlpt_rsp_t * p_ctrlpt_rsp,
uint8_t * p_data)
{
int len = 0;
p_data[len++] = BLE_SCPT_RESPONSE_CODE;
p_data[len++] = p_ctrlpt_rsp->opcode;
p_data[len++] = p_ctrlpt_rsp->status;
if (p_ctrlpt_rsp->status == BLE_SCPT_SUCCESS)
{
switch (p_ctrlpt_rsp->opcode)
{
case BLE_SCPT_REQUEST_SUPPORTED_SENSOR_LOCATIONS:
{
int i;
for (i = 0; i < p_sc_ctrlpt->size_list_supported_locations; i++)
{
p_data[len++] = p_sc_ctrlpt->list_supported_locations[i];
}
break;
}
default:
// No implementation needed.
break;
}
}
return len;
}
/**@brief check if a given sensor location is supported or not.
*
* @param[in] p_sc_ctrlpt SC Ctrlpt structure.
* @param[in] location sensor location to check.
* @return true if the given location is found in the list of supported locations, false otherwise.
*/
static bool is_location_supported(ble_sc_ctrlpt_t * p_sc_ctrlpt, ble_sensor_location_t location)
{
int i;
for (i = 0; i < p_sc_ctrlpt->size_list_supported_locations; i++)
{
if (p_sc_ctrlpt->list_supported_locations[i] == location)
{
return true;
}
}
return false;
}
/**@brief check if the cccd is configured
*
* @param[in] p_sc_ctrlpt SC Ctrlpt structure.
* @return true if the sc_control point's cccd is correctly configured, false otherwise.
*/
static bool is_cccd_configured(ble_sc_ctrlpt_t * p_sc_ctrlpt)
{
uint32_t err_code;
uint8_t cccd_value_buf[BLE_CCCD_VALUE_LEN];
bool is_sccp_indic_enabled = false;
ble_gatts_value_t gatts_value;
// Initialize value struct.
memset(&gatts_value, 0, sizeof(gatts_value));
gatts_value.len = BLE_CCCD_VALUE_LEN;
gatts_value.offset = 0;
gatts_value.p_value = cccd_value_buf;
err_code = sd_ble_gatts_value_get(p_sc_ctrlpt->conn_handle,
p_sc_ctrlpt->sc_ctrlpt_handles.cccd_handle,
&gatts_value);
if (err_code != NRF_SUCCESS)
{
// Report error to application
if (p_sc_ctrlpt->error_handler != NULL)
{
p_sc_ctrlpt->error_handler(err_code);
}
}
is_sccp_indic_enabled = ble_srv_is_indication_enabled(cccd_value_buf);
return is_sccp_indic_enabled;
}
/**@brief sends a control point indication.
*
* @param[in] p_sc_ctrlpt SC Ctrlpt structure.
*/
static void sc_ctrlpt_resp_send(ble_sc_ctrlpt_t * p_sc_ctrlpt)
{
uint16_t hvx_len;
ble_gatts_hvx_params_t hvx_params;
uint32_t err_code;
if (p_sc_ctrlpt->procedure_status == BLE_SCPT_INDICATION_PENDING)
{
hvx_len = p_sc_ctrlpt->response.len;
memset(&hvx_params, 0, sizeof(hvx_params));
hvx_params.handle = p_sc_ctrlpt->sc_ctrlpt_handles.value_handle;
hvx_params.type = BLE_GATT_HVX_INDICATION;
hvx_params.offset = 0;
hvx_params.p_len = &hvx_len;
hvx_params.p_data = p_sc_ctrlpt->response.encoded_ctrl_rsp;
err_code = sd_ble_gatts_hvx(p_sc_ctrlpt->conn_handle, &hvx_params);
// Error handling
if ((err_code == NRF_SUCCESS) && (hvx_len != p_sc_ctrlpt->response.len))
{
err_code = NRF_ERROR_DATA_SIZE;
}
switch (err_code)
{
case NRF_SUCCESS:
p_sc_ctrlpt->procedure_status = BLE_SCPT_IND_CONFIRM_PENDING;
// Wait for HVC event
break;
case NRF_ERROR_RESOURCES:
// Wait for TX_COMPLETE event to retry transmission.
p_sc_ctrlpt->procedure_status = BLE_SCPT_INDICATION_PENDING;
break;
default:
// Report error to application.
p_sc_ctrlpt->procedure_status = BLE_SCPT_NO_PROC_IN_PROGRESS;
if (p_sc_ctrlpt->error_handler != NULL)
{
p_sc_ctrlpt->error_handler(err_code);
}
break;
}
}
}
/**@brief Handle a write event to the Speed and Cadence Control Point.
*
* @param[in] p_sc_ctrlpt SC Ctrlpt structure.
* @param[in] p_evt_write WRITE event to be handled.
*/
static void on_ctrlpt_write(ble_sc_ctrlpt_t * p_sc_ctrlpt,
ble_gatts_evt_write_t const * p_evt_write)
{
ble_sc_ctrlpt_val_t rcvd_ctrlpt =
{ BLE_SCPT_RESPONSE_CODE , 0, BLE_SENSOR_LOCATION_OTHER };
ble_sc_ctrlpt_rsp_t rsp;
uint32_t err_code;
ble_gatts_rw_authorize_reply_params_t auth_reply;
ble_sc_ctrlpt_evt_t evt;
auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
auth_reply.params.write.offset = 0;
auth_reply.params.write.len = 0;
auth_reply.params.write.p_data = NULL;
auth_reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
auth_reply.params.write.update = 1;
if (is_cccd_configured(p_sc_ctrlpt))
{
if (p_sc_ctrlpt->procedure_status == BLE_SCPT_NO_PROC_IN_PROGRESS)
{
auth_reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
}
else
{
auth_reply.params.write.gatt_status = SC_CTRLPT_NACK_PROC_ALREADY_IN_PROGRESS;
}
}
else
{
auth_reply.params.write.gatt_status = SC_CTRLPT_NACK_CCCD_IMPROPERLY_CONFIGURED;
}
err_code = sd_ble_gatts_rw_authorize_reply(p_sc_ctrlpt->conn_handle, &auth_reply);
if (err_code != NRF_SUCCESS)
{
// Report error to application.
if (p_sc_ctrlpt->error_handler != NULL)
{
p_sc_ctrlpt->error_handler(err_code);
}
}
if (auth_reply.params.write.gatt_status != BLE_GATT_STATUS_SUCCESS)
{
return;
}
p_sc_ctrlpt->procedure_status = BLE_SCPT_INDICATION_PENDING;
rsp.status = BLE_SCPT_OP_CODE_NOT_SUPPORTED;
err_code = sc_ctrlpt_decode(p_evt_write->data, p_evt_write->len, &rcvd_ctrlpt);
if (err_code != NRF_SUCCESS)
{
rsp.opcode = rcvd_ctrlpt.opcode;
rsp.status = BLE_SCPT_OP_CODE_NOT_SUPPORTED;
}
else
{
rsp.opcode = rcvd_ctrlpt.opcode;
switch (rcvd_ctrlpt.opcode)
{
case BLE_SCPT_REQUEST_SUPPORTED_SENSOR_LOCATIONS:
if ((p_sc_ctrlpt->supported_functions &
BLE_SRV_SC_CTRLPT_SENSOR_LOCATIONS_OP_SUPPORTED) ==
BLE_SRV_SC_CTRLPT_SENSOR_LOCATIONS_OP_SUPPORTED)
{
rsp.status = BLE_SCPT_SUCCESS;
}
else
{
rsp.status = BLE_SCPT_OP_CODE_NOT_SUPPORTED;
}
break;
case BLE_SCPT_UPDATE_SENSOR_LOCATION:
if ((p_sc_ctrlpt->supported_functions &
BLE_SRV_SC_CTRLPT_SENSOR_LOCATIONS_OP_SUPPORTED) ==
BLE_SRV_SC_CTRLPT_SENSOR_LOCATIONS_OP_SUPPORTED)
{
if (is_location_supported(p_sc_ctrlpt, rcvd_ctrlpt.location))
{
ble_gatts_value_t gatts_value;
uint8_t rcvd_location = (uint8_t)rcvd_ctrlpt.location;
rsp.status = BLE_SCPT_SUCCESS;
// Initialize value struct.
memset(&gatts_value, 0, sizeof(gatts_value));
gatts_value.len = sizeof(uint8_t);
gatts_value.offset = 0;
gatts_value.p_value = &rcvd_location;
evt.evt_type = BLE_SC_CTRLPT_EVT_UPDATE_LOCATION;
evt.params.update_location = rcvd_ctrlpt.location;
if (p_sc_ctrlpt->evt_handler != NULL)
{
rsp.status = p_sc_ctrlpt->evt_handler(p_sc_ctrlpt, &evt);
}
if (rsp.status == BLE_SCPT_SUCCESS)
{
err_code = sd_ble_gatts_value_set(p_sc_ctrlpt->conn_handle,
p_sc_ctrlpt->sensor_location_handle,
&gatts_value);
if (err_code != NRF_SUCCESS)
{
// Report error to application
if (p_sc_ctrlpt->error_handler != NULL)
{
p_sc_ctrlpt->error_handler(err_code);
}
rsp.status = BLE_SCPT_OPERATION_FAILED;
}
}
}
else
{
rsp.status = BLE_SCPT_INVALID_PARAMETER;
}
}
else
{
rsp.status = BLE_SCPT_OP_CODE_NOT_SUPPORTED;
}
break;
case BLE_SCPT_SET_CUMULATIVE_VALUE:
if ((p_sc_ctrlpt->supported_functions &
BLE_SRV_SC_CTRLPT_CUM_VAL_OP_SUPPORTED) ==
BLE_SRV_SC_CTRLPT_CUM_VAL_OP_SUPPORTED)
{
rsp.status = BLE_SCPT_SUCCESS;
evt.evt_type = BLE_SC_CTRLPT_EVT_SET_CUMUL_VALUE;
evt.params.cumulative_value = rcvd_ctrlpt.cumulative_value;
if (p_sc_ctrlpt->evt_handler != NULL)
{
rsp.status = p_sc_ctrlpt->evt_handler(p_sc_ctrlpt, &evt);
}
}
else
{
rsp.status = BLE_SCPT_OP_CODE_NOT_SUPPORTED;
}
break;
case BLE_SCPT_START_AUTOMATIC_CALIBRATION:
if ((p_sc_ctrlpt->supported_functions &
BLE_SRV_SC_CTRLPT_START_CALIB_OP_SUPPORTED) ==
BLE_SRV_SC_CTRLPT_START_CALIB_OP_SUPPORTED)
{
p_sc_ctrlpt->procedure_status = BLE_SCPT_AUTOMATIC_CALIB_IN_PROGRESS;
evt.evt_type = BLE_SC_CTRLPT_EVT_START_CALIBRATION;
if (p_sc_ctrlpt->evt_handler != NULL)
{
rsp.status = p_sc_ctrlpt->evt_handler(p_sc_ctrlpt, &evt);
if (rsp.status != BLE_SCPT_SUCCESS)
{
// If the application returns an error, the response is to be sent
// right away and the calibration is considered as not started.
p_sc_ctrlpt->procedure_status = BLE_SCPT_INDICATION_PENDING;
}
}
}
else
{
rsp.status = BLE_SCPT_OP_CODE_NOT_SUPPORTED;
}
break;
default:
rsp.status = BLE_SCPT_OP_CODE_NOT_SUPPORTED;
break;
}
}
p_sc_ctrlpt->response.len = ctrlpt_rsp_encode(p_sc_ctrlpt, &rsp,
p_sc_ctrlpt->response.encoded_ctrl_rsp);
if (p_sc_ctrlpt->procedure_status == BLE_SCPT_INDICATION_PENDING)
{
sc_ctrlpt_resp_send(p_sc_ctrlpt);
}
}
/**@brief Authorize WRITE request event handler.
*
* @details Handles WRITE events from the BLE stack.
*
* @param[in] p_sc_ctrlpt SC Ctrlpt structure.
* @param[in] p_gatts_evt GATTS Event received from the BLE stack.
*
*/
static void on_rw_authorize_request(ble_sc_ctrlpt_t * p_sc_ctrlpt,
ble_gatts_evt_t const * p_gatts_evt)
{
ble_gatts_evt_rw_authorize_request_t const * p_auth_req =
&p_gatts_evt->params.authorize_request;
if (p_auth_req->type == BLE_GATTS_AUTHORIZE_TYPE_WRITE)
{
if ( (p_gatts_evt->params.authorize_request.request.write.op
!= BLE_GATTS_OP_PREP_WRITE_REQ)
&& (p_gatts_evt->params.authorize_request.request.write.op
!= BLE_GATTS_OP_EXEC_WRITE_REQ_NOW)
&& (p_gatts_evt->params.authorize_request.request.write.op
!= BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL)
)
{
if (p_auth_req->request.write.handle == p_sc_ctrlpt->sc_ctrlpt_handles.value_handle)
{
on_ctrlpt_write(p_sc_ctrlpt, &p_auth_req->request.write);
}
}
}
}
/**@brief Tx Complete event handler.
*
* @details Tx Complete event handler.
* Handles WRITE events from the BLE stack and if an indication was pending try sending it
* again.
*
* @param[in] p_sc_ctrlpt SC Ctrlpt structure.
*
*/
static void on_tx_complete(ble_sc_ctrlpt_t * p_sc_ctrlpt)
{
if (p_sc_ctrlpt->procedure_status == BLE_SCPT_INDICATION_PENDING)
{
sc_ctrlpt_resp_send(p_sc_ctrlpt);
}
}
/**@brief Function for handling the Connect event.
*
* @param[in] p_sc_ctrlpt SC Ctrlpt structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_connect(ble_sc_ctrlpt_t * p_sc_ctrlpt, ble_evt_t const * p_ble_evt)
{
p_sc_ctrlpt->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
p_sc_ctrlpt->procedure_status = BLE_SCPT_NO_PROC_IN_PROGRESS;
}
/**@brief Function for handling the Disconnect event.
*
* @param[in] p_sc_ctrlpt SC Ctrlpt structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_disconnect(ble_sc_ctrlpt_t * p_sc_ctrlpt, ble_evt_t const * p_ble_evt)
{
UNUSED_PARAMETER(p_ble_evt);
p_sc_ctrlpt->conn_handle = BLE_CONN_HANDLE_INVALID;
p_sc_ctrlpt->procedure_status = BLE_SCPT_NO_PROC_IN_PROGRESS;
}
/**@brief Function for handling the BLE_GATTS_EVT_HVC event.
*
* @param[in] p_sc_ctrlpt SC Ctrlpt structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_sc_hvc_confirm(ble_sc_ctrlpt_t * p_sc_ctrlpt, ble_evt_t const * p_ble_evt)
{
if (p_ble_evt->evt.gatts_evt.params.hvc.handle == p_sc_ctrlpt->sc_ctrlpt_handles.value_handle)
{
if (p_sc_ctrlpt->procedure_status == BLE_SCPT_IND_CONFIRM_PENDING)
{
p_sc_ctrlpt->procedure_status = BLE_SCPT_NO_PROC_IN_PROGRESS;
}
}
}
void ble_sc_ctrlpt_on_ble_evt(ble_sc_ctrlpt_t * p_sc_ctrlpt, ble_evt_t const * p_ble_evt)
{
if (p_sc_ctrlpt == NULL || p_ble_evt == NULL)
{
return;
}
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
on_connect(p_sc_ctrlpt, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnect(p_sc_ctrlpt, p_ble_evt);
break;
case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
on_rw_authorize_request(p_sc_ctrlpt, &p_ble_evt->evt.gatts_evt);
break;
case BLE_GATTS_EVT_HVC:
on_sc_hvc_confirm(p_sc_ctrlpt, p_ble_evt);
break;
case BLE_GATTS_EVT_HVN_TX_COMPLETE:
on_tx_complete(p_sc_ctrlpt);
break;
default:
break;
}
}
uint32_t ble_sc_ctrlpt_rsp_send(ble_sc_ctrlpt_t * p_sc_ctrlpt, ble_scpt_response_t response_status)
{
if (p_sc_ctrlpt == NULL)
{
return NRF_ERROR_NULL;
}
uint32_t err_code = NRF_SUCCESS;
ble_sc_ctrlpt_rsp_t rsp;
uint8_t encoded_ctrl_rsp[BLE_SC_CTRLPT_MAX_LEN];
uint16_t hvx_len;
ble_gatts_hvx_params_t hvx_params;
if (p_sc_ctrlpt->procedure_status != BLE_SCPT_AUTOMATIC_CALIB_IN_PROGRESS)
{
return NRF_ERROR_INVALID_STATE;
}
rsp.status = response_status;
rsp.opcode = BLE_SCPT_START_AUTOMATIC_CALIBRATION;
hvx_len = ctrlpt_rsp_encode(p_sc_ctrlpt, &rsp, encoded_ctrl_rsp);
// Send indication
memset(&hvx_params, 0, sizeof(hvx_params));
hvx_params.handle = p_sc_ctrlpt->sc_ctrlpt_handles.value_handle;
hvx_params.type = BLE_GATT_HVX_INDICATION;
hvx_params.offset = 0;
hvx_params.p_len = &hvx_len;
hvx_params.p_data = encoded_ctrl_rsp;
err_code = sd_ble_gatts_hvx(p_sc_ctrlpt->conn_handle, &hvx_params);
if (err_code == NRF_SUCCESS)
{
p_sc_ctrlpt->procedure_status = BLE_SCPT_NO_PROC_IN_PROGRESS;
}
return err_code;
}

View File

@@ -0,0 +1,248 @@
/**
* Copyright (c) 2013 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_sdk_srv_sc_ctrlpt Speed and Cadence Control Point
* @{
* @ingroup ble_sdk_srv
* @brief Speed and Cadence Control Point module.
*
* @details This module implements the Speed and Cadence control point behavior. It is used
* by the @ref ble_cscs module and the ble_sdk_srv_rsc module for control point
* mechanisms like setting a cumulative value, Start an automatic calibration,
* Update the sensor location or request the supported locations.
*
* @note Attention!
* To maintain compliance with Nordic Semiconductor ASA Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#ifndef BLE_SC_CTRLPT_H__
#define BLE_SC_CTRLPT_H__
#include <stdint.h>
#include <stdbool.h>
#include "ble.h"
#include "ble_srv_common.h"
#include "ble_sensor_location.h"
#ifdef __cplusplus
extern "C" {
#endif
#define BLE_SC_CTRLPT_MAX_LEN 19 /**< maximum lenght for Speed and cadence control point characteristic value. */
#define BLE_SC_CTRLPT_MIN_LEN 1 /**< minimum length for Speed and cadence control point characteristic value. */
// Forward declaration of the ble_sc_ctrlpt_t type.
typedef struct ble_sc_ctrlpt_s ble_sc_ctrlpt_t;
/**@brief Speed and Cadence Control Point event type. */
typedef enum
{
BLE_SC_CTRLPT_EVT_UPDATE_LOCATION, /**< rcvd update location opcode (the control point handles the change of location automatically, the event just informs the application in case it needs to adjust its algorithm). */
BLE_SC_CTRLPT_EVT_SET_CUMUL_VALUE, /**< rcvd set cumulative value opcode, it is then up to the application to use the new cumulative value. */
BLE_SC_CTRLPT_EVT_START_CALIBRATION, /**< rcvd start calibration opcode, the application needs, at the end ot the calibration to call ble_sc_ctrlpt_send_rsp. */
} ble_sc_ctrlpt_evt_type_t;
/**@brief Speed and Cadence Control point event. */
typedef struct
{
ble_sc_ctrlpt_evt_type_t evt_type; /**< Type of event. */
union
{
ble_sensor_location_t update_location;
uint32_t cumulative_value;
}params;
} ble_sc_ctrlpt_evt_t;
/** Speed and Cadence Control Point operator code (see RSC service specification)*/
typedef enum {
BLE_SCPT_SET_CUMULATIVE_VALUE = 0x01, /**< Operator to set a given cumulative value. */
BLE_SCPT_START_AUTOMATIC_CALIBRATION = 0x02, /**< Operator to start automatic calibration. */
BLE_SCPT_UPDATE_SENSOR_LOCATION = 0x03, /**< Operator to update the sensor location. */
BLE_SCPT_REQUEST_SUPPORTED_SENSOR_LOCATIONS = 0x04, /**< Operator to request the supported sensor locations. */
BLE_SCPT_RESPONSE_CODE = 0x10, /**< Response Code. */
} ble_scpt_operator_t;
/** Speed and Cadence Control Point response parameter (see RSC service specification)*/
typedef enum {
BLE_SCPT_SUCCESS = 0x01, /**< Sucess Response. */
BLE_SCPT_OP_CODE_NOT_SUPPORTED = 0x02, /**< Error Response received opcode not supported. */
BLE_SCPT_INVALID_PARAMETER = 0x03, /**< Error Response received parameter invalid. */
BLE_SCPT_OPERATION_FAILED = 0x04, /**< Error Response operation failed. */
} ble_scpt_response_t;
/** Speed and Cadence Control Point procedure status (indicates is a procedure is in progress or not and which procedure is in progress*/
typedef enum {
BLE_SCPT_NO_PROC_IN_PROGRESS = 0x00, /**< No procedure in progress. */
BLE_SCPT_AUTOMATIC_CALIB_IN_PROGRESS = 0x01, /**< Automatic Calibration is in progress. */
BLE_SCPT_INDICATION_PENDING = 0x02, /**< Control Point Indication is pending. */
BLE_SCPT_IND_CONFIRM_PENDING = 0x03, /**< Waiting for the indication confirmation. */
}ble_scpt_procedure_status_t;
/**@brief Speed and Cadence Control point event handler type. */
typedef ble_scpt_response_t (*ble_sc_ctrlpt_evt_handler_t) (ble_sc_ctrlpt_t * p_sc_ctrlpt,
ble_sc_ctrlpt_evt_t * p_evt);
typedef struct{
ble_scpt_operator_t opcode;
uint32_t cumulative_value;
ble_sensor_location_t location;
}ble_sc_ctrlpt_val_t;
typedef struct{
ble_scpt_operator_t opcode;
ble_scpt_response_t status;
ble_sensor_location_t location_list[BLE_NB_MAX_SENSOR_LOCATIONS];
}ble_sc_ctrlpt_rsp_t;
/**
* \defgroup BLE_SRV_SC_CTRLPT_SUPP_FUNC Control point functionalities.
*@{
*/
#define BLE_SRV_SC_CTRLPT_SENSOR_LOCATIONS_OP_SUPPORTED 0x01 /**< Support for sensor location related operations */
#define BLE_SRV_SC_CTRLPT_CUM_VAL_OP_SUPPORTED 0x02 /**< Support for setting cumulative value related operations */
#define BLE_SRV_SC_CTRLPT_START_CALIB_OP_SUPPORTED 0x04 /**< Support for starting calibration related operations */
/**
*@}
*/
/**@brief Speed and Cadence Control Point init structure. This contains all options and data
* needed for initialization of the Speed and Cadence Control Point module. */
typedef struct
{
security_req_t sc_ctrlpt_cccd_wr_sec; /**< Security requirement for writing speed and cadence control point characteristic CCCD. */
security_req_t sc_ctrlpt_wr_sec; /**< Security requirement for writing speed and cadence control point characteristic. */
uint8_t supported_functions; /**< supported control point functionalities see @ref BLE_SRV_SC_CTRLPT_SUPP_FUNC. */
uint16_t service_handle; /**< Handle of the parent service (as provided by the BLE stack). */
ble_sc_ctrlpt_evt_handler_t evt_handler; /**< event handler */
ble_sensor_location_t *list_supported_locations; /**< list of supported sensor locations.*/
uint8_t size_list_supported_locations; /**< number of supported sensor locations in the list.*/
uint16_t sensor_location_handle; /**< handle for the sensor location characteristic (if sensor_location related operation are supported).*/
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
} ble_cs_ctrlpt_init_t;
/**@brief Speed and Cadence Control Point response indication structure. */
typedef struct
{
ble_scpt_response_t status; /**< control point response status .*/
uint8_t len; /**< control point response length .*/
uint8_t encoded_ctrl_rsp[BLE_SC_CTRLPT_MAX_LEN]; /**< control point encoded response.*/
}ble_sc_ctrlpt_resp_t;
/**@brief Speed and Cadence Control Point structure. This contains various status information for
* the Speed and Cadence Control Point behavior. */
struct ble_sc_ctrlpt_s
{
uint8_t supported_functions; /**< supported control point functionalities see @ref BLE_SRV_SC_CTRLPT_SUPP_FUNC. */
uint16_t service_handle; /**< Handle of the parent service (as provided by the BLE stack). */
ble_gatts_char_handles_t sc_ctrlpt_handles; /**< Handles related to the Speed and Cadence Control Point characteristic. */
uint16_t conn_handle; /**< Handle of the current connection (as provided by the BLE stack, is BLE_CONN_HANDLE_INVALID if not in a connection). */
ble_sensor_location_t list_supported_locations[BLE_NB_MAX_SENSOR_LOCATIONS]; /**< list of supported sensor locations.*/
uint8_t size_list_supported_locations; /**< number of supported sensor locations in the list.*/
ble_sc_ctrlpt_evt_handler_t evt_handler; /**< Handle of the parent service (as provided by the BLE stack). */
uint16_t sensor_location_handle; /**< handle for the sensor location characteristic (if sensor_location related operation are supported).*/
ble_scpt_procedure_status_t procedure_status; /**< status of possible procedure*/
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
ble_sc_ctrlpt_resp_t response; /**< pending response data.*/
};
#define SCPT_OPCODE_POS 0 /**< Request opcode position. */
#define SCPT_PARAMETER_POS 1 /**< Request parameter position. */
#define SCPT_RESPONSE_REQUEST_OPCODE_POS 1 /**< Response position of requested opcode. */
#define SCPT_RESPONSE_CODE_POS 2 /**< Response position of response code. */
#define SCPT_RESPONSE_PARAMETER 3 /**< Response position of response parameter. */
#define SCPT_MIN_RESPONSE_SIZE 3 /**< Minimum size for control point response. */
#define SCPT_MAX_RESPONSE_SIZE (SCPT_MIN_RESPONSE_SIZE + NB_MAX_SENSOR_LOCATIONS) /**< Maximum size for control point response. */
/**@brief Function for Initializing the Speed and Cadence Control Point.
*
* @details Function for Initializing the Speed and Cadence Control Point.
* @param[in] p_sc_ctrlpt Speed and Cadence Control Point structure.
* @param[in] p_sc_ctrlpt_init Information needed to initialize the control point behavior.
*
* @return NRF_SUCCESS on successful initialization of service, otherwise an error code.
*/
uint32_t ble_sc_ctrlpt_init(ble_sc_ctrlpt_t * p_sc_ctrlpt,
ble_cs_ctrlpt_init_t const * p_sc_ctrlpt_init);
/**@brief Function for sending a control point response.
*
* @details Function for sending a control point response when the control point received was
* BLE_SCPT_START_AUTOMATIC_CALIBRATION. To be called after the calibration procedure is finished.
*
* @param[in] p_sc_ctrlpt Speed and Cadence Control Point structure.
* @param[in] response_status status to include in the control point response.
*/
uint32_t ble_sc_ctrlpt_rsp_send(ble_sc_ctrlpt_t * p_sc_ctrlpt, ble_scpt_response_t response_status);
/**@brief Speed and Cadence Control Point BLE stack event handler.
*
* @details Handles all events from the BLE stack of interest to the Speed and Cadence Control Point.
*
* @param[in] p_sc_ctrlpt Speed and Cadence Control Point structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
void ble_sc_ctrlpt_on_ble_evt(ble_sc_ctrlpt_t * p_sc_ctrlpt, ble_evt_t const * p_ble_evt);
#ifdef __cplusplus
}
#endif
#endif // BLE_SC_CTRLPT_H__
/** @} */

View File

@@ -0,0 +1,399 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_CTS_C)
#include <string.h>
#include "ble.h"
#include "ble_srv_common.h"
#include "ble_gattc.h"
#include "ble_cts_c.h"
#include "ble_date_time.h"
#include "ble_db_discovery.h"
#define NRF_LOG_MODULE_NAME ble_cts_c
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();
#define CTS_YEAR_MIN 1582 /**< The lowest valid Current Time year is the year when the Western calendar was introduced. */
#define CTS_YEAR_MAX 9999 /**< The highest possible Current Time year. */
#define CTS_C_CURRENT_TIME_EXPECTED_LENGTH 10 /**< | Year |Month |Day |Hours |Minutes |Seconds |Weekday |Fraction|Reason |
| 2 bytes |1 byte |1 byte |1 byte |1 byte |1 byte |1 byte |1 byte |1 byte | = 10 bytes. */
/**@brief Function for intercepting errors of GATTC and the BLE GATT Queue.
*
* @param[in] nrf_error Error code.
* @param[in] p_ctx Parameter from the event handler.
* @param[in] conn_handle Connection handle.
*/
static void gatt_error_handler(uint32_t nrf_error,
void * p_ctx,
uint16_t conn_handle)
{
ble_cts_c_t * p_cts = (ble_cts_c_t *)p_ctx;
NRF_LOG_DEBUG("A GATT Client error has occurred on conn_handle: 0X%X", conn_handle);
if (p_cts->error_handler != NULL)
{
p_cts->error_handler(nrf_error);
}
}
/**@brief Function for handling events from the Database Discovery module.
*
* @details This function handles an event from the Database Discovery module, and determines
* whether it relates to the discovery of Current Time Service at the peer. If it does, this function
* calls the application's event handler to indicate that the Current Time Service was
* discovered at the peer. The function also populates the event with service-related
* information before providing it to the application.
*
* @param[in] p_evt Pointer to the event received from the Database Discovery module.
*
*/
void ble_cts_c_on_db_disc_evt(ble_cts_c_t * p_cts, ble_db_discovery_evt_t * p_evt)
{
NRF_LOG_DEBUG("Database Discovery handler called with event 0x%x", p_evt->evt_type);
ble_cts_c_evt_t evt;
const ble_gatt_db_char_t * p_chars = p_evt->params.discovered_db.charateristics;
evt.conn_handle = p_evt->conn_handle;
// Check if the Current Time Service was discovered.
if ( (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE)
&& (p_evt->params.discovered_db.srv_uuid.uuid == BLE_UUID_CURRENT_TIME_SERVICE)
&& (p_evt->params.discovered_db.srv_uuid.type == BLE_UUID_TYPE_BLE))
{
// Find the handles of the Current Time characteristic.
for (uint32_t i = 0; i < p_evt->params.discovered_db.char_count; i++)
{
if (p_evt->params.discovered_db.charateristics[i].characteristic.uuid.uuid ==
BLE_UUID_CURRENT_TIME_CHAR)
{
// Found Current Time characteristic. Store CCCD and value handle and break.
evt.params.char_handles.cts_handle = p_chars->characteristic.handle_value;
evt.params.char_handles.cts_cccd_handle = p_chars->cccd_handle;
break;
}
}
NRF_LOG_INFO("Current Time Service discovered at peer.");
evt.evt_type = BLE_CTS_C_EVT_DISCOVERY_COMPLETE;
}
else if ((p_evt->evt_type == BLE_DB_DISCOVERY_SRV_NOT_FOUND) ||
(p_evt->evt_type == BLE_DB_DISCOVERY_ERROR))
{
evt.evt_type = BLE_CTS_C_EVT_DISCOVERY_FAILED;
}
else
{
return;
}
p_cts->evt_handler(p_cts, &evt);
}
uint32_t ble_cts_c_init(ble_cts_c_t * p_cts, ble_cts_c_init_t const * p_cts_init)
{
// Verify that the parameters needed to initialize this instance of CTS are not NULL.
VERIFY_PARAM_NOT_NULL(p_cts);
VERIFY_PARAM_NOT_NULL(p_cts_init);
VERIFY_PARAM_NOT_NULL(p_cts_init->error_handler);
VERIFY_PARAM_NOT_NULL(p_cts_init->evt_handler);
VERIFY_PARAM_NOT_NULL(p_cts_init->p_gatt_queue);
static ble_uuid_t cts_uuid;
BLE_UUID_BLE_ASSIGN(cts_uuid, BLE_UUID_CURRENT_TIME_SERVICE);
p_cts->evt_handler = p_cts_init->evt_handler;
p_cts->error_handler = p_cts_init->error_handler;
p_cts->conn_handle = BLE_CONN_HANDLE_INVALID;
p_cts->char_handles.cts_handle = BLE_GATT_HANDLE_INVALID;
p_cts->char_handles.cts_cccd_handle = BLE_GATT_HANDLE_INVALID;
p_cts->p_gatt_queue = p_cts_init->p_gatt_queue;
return ble_db_discovery_evt_register(&cts_uuid);
}
/**@brief Function for decoding a read from the Current Time characteristic.
*
* @param[in] p_time Current Time structure.
* @param[in] p_data Pointer to the buffer containing the Current Time.
* @param[in] length Length of the buffer containing the Current Time.
*
* @retval NRF_SUCCESS If the time struct is valid.
* @retval NRF_ERROR_DATA_SIZE If the length does not match the expected size of the data.
*/
static uint32_t current_time_decode(current_time_char_t * p_time,
uint8_t const * p_data,
uint32_t const length)
{
//lint -save -e415 -e416 "Access of out of bounds pointer" "Creation of out of bounds pointer"
if (length != CTS_C_CURRENT_TIME_EXPECTED_LENGTH)
{
// Return to prevent accessing the out-of-bounds data.
return NRF_ERROR_DATA_SIZE;
}
NRF_LOG_DEBUG("Current Time read response data:");
NRF_LOG_HEXDUMP_DEBUG(p_data, 10);
uint32_t index = 0;
// Date.
index += ble_date_time_decode(&p_time->exact_time_256.day_date_time.date_time, p_data);
// Day of week.
p_time->exact_time_256.day_date_time.day_of_week = p_data[index++];
// Fractions of a second.
p_time->exact_time_256.fractions256 = p_data[index++];
// Reason for updating the time.
p_time->adjust_reason.manual_time_update = (p_data[index] >> 0) & 0x01;
p_time->adjust_reason.external_reference_time_update = (p_data[index] >> 1) & 0x01;
p_time->adjust_reason.change_of_time_zone = (p_data[index] >> 2) & 0x01;
p_time->adjust_reason.change_of_daylight_savings_time = (p_data[index] >> 3) & 0x01;
//lint -restore
return NRF_SUCCESS;
}
/**@brief Function for decoding a read from the Current Time characteristic.
*
* @param[in] p_time Current Time struct.
*
* @retval NRF_SUCCESS If the time struct is valid.
* @retval NRF_ERROR_INVALID_DATA If the time is out of bounds.
*/
static uint32_t current_time_validate(current_time_char_t * p_time)
{
// Year.
if ( (p_time->exact_time_256.day_date_time.date_time.year > CTS_YEAR_MAX)
|| ((p_time->exact_time_256.day_date_time.date_time.year < CTS_YEAR_MIN)
&& (p_time->exact_time_256.day_date_time.date_time.year != 0)))
{
return NRF_ERROR_INVALID_DATA;
}
// Month.
if (p_time->exact_time_256.day_date_time.date_time.month > 12)
{
return NRF_ERROR_INVALID_DATA;
}
// Day.
if (p_time->exact_time_256.day_date_time.date_time.day > 31)
{
return NRF_ERROR_INVALID_DATA;
}
// Hours.
if (p_time->exact_time_256.day_date_time.date_time.hours > 23)
{
return NRF_ERROR_INVALID_DATA;
}
// Minutes.
if (p_time->exact_time_256.day_date_time.date_time.minutes > 59)
{
return NRF_ERROR_INVALID_DATA;
}
// Seconds.
if (p_time->exact_time_256.day_date_time.date_time.seconds > 59)
{
return NRF_ERROR_INVALID_DATA;
}
// Day of week.
if (p_time->exact_time_256.day_date_time.day_of_week > 7)
{
return NRF_ERROR_INVALID_DATA;
}
return NRF_SUCCESS;
}
/**@brief Function for reading the Current Time. The time is decoded, and then validated.
* Depending on the outcome, the CTS event handler will be called with
* the Current Time event or an invalid time event.
*
* @param[in] p_cts Current Time Service client structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void current_time_read(ble_cts_c_t * p_cts, ble_evt_t const * p_ble_evt)
{
ble_cts_c_evt_t evt;
uint32_t err_code = NRF_SUCCESS;
// Check whether the event is on the same connection as this CTS instance
if (p_cts->conn_handle != p_ble_evt->evt.gattc_evt.conn_handle)
{
return;
}
if (p_ble_evt->evt.gattc_evt.gatt_status == BLE_GATT_STATUS_SUCCESS)
{
err_code = current_time_decode(&evt.params.current_time,
p_ble_evt->evt.gattc_evt.params.read_rsp.data,
p_ble_evt->evt.gattc_evt.params.read_rsp.len);
if (err_code != NRF_SUCCESS)
{
// The data length was invalid. Decoding was not completed.
evt.evt_type = BLE_CTS_C_EVT_INVALID_TIME;
}
else
{
// Verify that the time is valid.
err_code = current_time_validate(&evt.params.current_time);
if (err_code != NRF_SUCCESS)
{
// Invalid time received.
evt.evt_type = BLE_CTS_C_EVT_INVALID_TIME;
}
else
{
// Valid time reveiced.
evt.evt_type = BLE_CTS_C_EVT_CURRENT_TIME;
}
}
p_cts->evt_handler(p_cts, &evt);
}
}
/**@brief Function for handling the Disconnect event.
*
* @param[in] p_cts Current Time Service client structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_disconnect(ble_cts_c_t * p_cts, ble_evt_t const * p_ble_evt)
{
if (p_cts->conn_handle == p_ble_evt->evt.gap_evt.conn_handle)
{
p_cts->conn_handle = BLE_CONN_HANDLE_INVALID;
}
if (ble_cts_c_is_cts_discovered(p_cts))
{
// There was a valid instance of CTS on the peer. Send an event to the
// application, so that it can do any clean up related to this module.
ble_cts_c_evt_t evt;
evt.evt_type = BLE_CTS_C_EVT_DISCONN_COMPLETE;
p_cts->evt_handler(p_cts, &evt);
p_cts->char_handles.cts_handle = BLE_GATT_HANDLE_INVALID;
p_cts->char_handles.cts_cccd_handle = BLE_GATT_HANDLE_INVALID;
}
}
void ble_cts_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
ble_cts_c_t * p_cts = (ble_cts_c_t *)p_context;
NRF_LOG_DEBUG("BLE event handler called with event 0x%x", p_ble_evt->header.evt_id);
switch (p_ble_evt->header.evt_id)
{
case BLE_GATTC_EVT_READ_RSP:
current_time_read(p_cts, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnect(p_cts, p_ble_evt);
break;
default:
// No implementation needed.
break;
}
}
uint32_t ble_cts_c_current_time_read(ble_cts_c_t const * p_cts)
{
if (!ble_cts_c_is_cts_discovered(p_cts))
{
return NRF_ERROR_NOT_FOUND;
}
nrf_ble_gq_req_t read_req;
memset(&read_req, 0, sizeof(nrf_ble_gq_req_t));
read_req.type = NRF_BLE_GQ_REQ_GATTC_READ;
read_req.error_handler.cb = gatt_error_handler;
read_req.error_handler.p_ctx = (ble_cts_c_t *)p_cts;
read_req.params.gattc_read.handle = p_cts->char_handles.cts_handle;
read_req.params.gattc_read.offset = 0;
return nrf_ble_gq_item_add(p_cts->p_gatt_queue, &read_req, p_cts->conn_handle);
}
uint32_t ble_cts_c_handles_assign(ble_cts_c_t * p_cts,
const uint16_t conn_handle,
const ble_cts_c_handles_t * p_peer_handles)
{
VERIFY_PARAM_NOT_NULL(p_cts);
p_cts->conn_handle = conn_handle;
if (p_peer_handles != NULL)
{
p_cts->char_handles.cts_cccd_handle = p_peer_handles->cts_cccd_handle;
p_cts->char_handles.cts_handle = p_peer_handles->cts_handle;
}
return nrf_ble_gq_conn_handle_register(p_cts->p_gatt_queue, conn_handle);
}
#endif // NRF_MODULE_ENABLED(BLE_CTS_C)

View File

@@ -0,0 +1,291 @@
/**
* Copyright (c) 2014 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_cts_c Current Time Service Client
* @{
* @ingroup ble_sdk_srv
* @brief Current Time Service Client module.
*
* @details This module implements the Current Time Service (CTS) client-peripheral role of
* the Time Profile. After security is established, the module tries to discover the
* Current Time Service and its characteristic on the central side. If this succeeds,
* the application can trigger a read of the current time from the connected server.
*
* The module informs the application about the successful discovery with the
* @ref BLE_CTS_C_EVT_DISCOVERY_COMPLETE event. The handles for the CTS server are now
* available in the @ref ble_cts_c_evt_t structure. These handles must be assigned to an
* instance of CTS_C with @ref ble_cts_c_handles_assign. For more information about the
* service discovery, see the ble_discovery module documentation: @ref lib_ble_db_discovery.
*
* After assigning the handles to an instance of CTS_C, the application can use the function
* @ref ble_cts_c_current_time_read to read the current time. If the read succeeds, it triggers either
* a @ref BLE_CTS_C_EVT_CURRENT_TIME event or a @ref BLE_CTS_C_EVT_INVALID_TIME event
* (depending whether the data that was read was actually a valid time). Then the read result is sent
* to the application. The current time is then available in the params field of the
* passed @ref ble_cts_c_evt_t structure.
*
* @note The application must register this module as the BLE event observer by using the
* NRF_SDH_BLE_OBSERVER macro. Example:
* @code
* ble_cts_c_t instance;
* NRF_SDH_BLE_OBSERVER(anything, BLE_CTS_C_BLE_OBSERVER_PRIO,
* ble_cts_c_on_ble_evt, &instance);
* @endcode
*/
#ifndef BLE_CTS_C_H__
#define BLE_CTS_C_H__
#include "ble_srv_common.h"
#include "ble_gattc.h"
#include "ble.h"
#include "ble_date_time.h"
#include "ble_db_discovery.h"
#include "nrf_ble_gq.h"
#include "nrf_sdh_ble.h"
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Macro for defining a ble_bps instance.
*
* @param _name Name of the instance.
* @hideinitializer
*/
#define BLE_CTS_C_DEF(_name) \
static ble_cts_c_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_CTS_C_BLE_OBSERVER_PRIO, \
ble_cts_c_on_ble_evt, &_name)
/** @brief Macro for defining multiple ble_cts_c instances.
*
* @param _name Name of the array of instances.
* @param _cnt Number of instances to define.
* @hideinitializer
*/
#define BLE_CTS_C_ARRAY_DEF(_name, _cnt) \
static ble_cts_c_t _name[_cnt]; \
NRF_SDH_BLE_OBSERVERS(_name ## _obs, \
BLE_CTS_C_BLE_OBSERVER_PRIO, \
ble_cts_c_on_ble_evt, &_name, _cnt)
/**@brief "Day Date Time" field of the "Exact Time 256" field of the Current Time characteristic. */
typedef struct
{
ble_date_time_t date_time;
uint8_t day_of_week;
} day_date_time_t;
/**@brief "Exact Time 256" field of the Current Time characteristic. */
typedef struct
{
day_date_time_t day_date_time;
uint8_t fractions256;
} exact_time_256_t;
/**@brief "Adjust Reason" field of the Current Time characteristic. */
typedef struct
{
uint8_t manual_time_update : 1;
uint8_t external_reference_time_update : 1;
uint8_t change_of_time_zone : 1;
uint8_t change_of_daylight_savings_time : 1;
} adjust_reason_t;
/**@brief Data structure for the Current Time characteristic. */
typedef struct
{
exact_time_256_t exact_time_256;
adjust_reason_t adjust_reason;
} current_time_char_t;
// Forward declaration of the ble_cts_c_t type.
typedef struct ble_cts_c_s ble_cts_c_t;
/**@brief Current Time Service client event type. */
typedef enum
{
BLE_CTS_C_EVT_DISCOVERY_COMPLETE, /**< The Current Time Service was found at the peer. */
BLE_CTS_C_EVT_DISCOVERY_FAILED, /**< The Current Time Service was not found at the peer. */
BLE_CTS_C_EVT_DISCONN_COMPLETE, /**< Event indicating that the Current Time Service Client module finished processing the BLE_GAP_EVT_DISCONNECTED event. This event is triggered only if a valid instance of the Current Time Service was found at the server. The application can use this event to do a cleanup related to the Current Time Service client.*/
BLE_CTS_C_EVT_CURRENT_TIME, /**< A new Current Time reading has been received. */
BLE_CTS_C_EVT_INVALID_TIME /**< The Current Time value received from the peer is invalid.*/
} ble_cts_c_evt_type_t;
/**@brief Structure containing the handles related to the Heart Rate Service found on the peer. */
typedef struct
{
uint16_t cts_handle; /**< Handle of the Current Time characteristic, as provided by the SoftDevice. */
uint16_t cts_cccd_handle; /**< Handle of the CCCD of the Current Time characteristic. */
} ble_cts_c_handles_t;
/**@brief Current Time Service client event. */
typedef struct
{
ble_cts_c_evt_type_t evt_type; /**< Type of event. */
uint16_t conn_handle; /**< Connection handle on which the CTS service was discovered on the peer device. This is filled if the evt_type is @ref BLE_CTS_C_EVT_DISCOVERY_COMPLETE.*/
union
{
current_time_char_t current_time; /**< Current Time characteristic data. This is filled when the evt_type is @ref BLE_CTS_C_EVT_CURRENT_TIME. */
ble_cts_c_handles_t char_handles; /**< Handles related to Current Time, found on the peer device. This is filled when the evt_type is @ref BLE_HRS_C_EVT_DISCOVERY_COMPLETE.*/
} params;
} ble_cts_c_evt_t;
/**@brief Current Time Service client event handler type. */
typedef void (* ble_cts_c_evt_handler_t) (ble_cts_c_t * p_cts, ble_cts_c_evt_t * p_evt);
/**@brief Current Time Service client structure. This structure contains status information for the client. */
struct ble_cts_c_s
{
ble_cts_c_evt_handler_t evt_handler; /**< Event handler to be called for handling events from the Current Time Service client. */
ble_srv_error_handler_t error_handler; /**< Function to be called if an error occurs. */
ble_cts_c_handles_t char_handles; /**< Handles of Current Time characteristic at the peer. (Handles are provided by the BLE stack through the Database Discovery module.) */
uint16_t conn_handle; /**< Handle of the current connection. BLE_CONN_HANDLE_INVALID if not in a connection. */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to the BLE GATT Queue instance. */
};
/**@brief Current Time Service client init structure. This structure contains all options and data needed for the initialization of the client.*/
typedef struct
{
ble_cts_c_evt_handler_t evt_handler; /**< Event handler to be called for handling events from the Current Time Service client. */
ble_srv_error_handler_t error_handler; /**< Function to be called if an error occurs. */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to the BLE GATT Queue instance. */
} ble_cts_c_init_t;
/**@brief Function for initializing the Current Time Service client.
*
* @details This function must be used by the application to initialize the Current Time Service client.
*
* @param[out] p_cts Current Time Service client structure. This structure must
* be supplied by the application. It is initialized by this
* function and can later be used to identify this particular client
* instance.
* @param[in] p_cts_init Information needed to initialize the Current Time Service client.
*
* @retval NRF_SUCCESS If the service was initialized successfully.
*/
uint32_t ble_cts_c_init(ble_cts_c_t * p_cts, const ble_cts_c_init_t * p_cts_init);
/**@brief Function for handling events from the Database Discovery module.
*
* @details This function handles an event from the Database Discovery module, and determines
* whether it relates to the discovery of CTS at the peer. If it does, the function
* calls the application's event handler to indicate that CTS was
* discovered. The function also populates the event with service-related
* information before providing it to the application.
*
* @param[in] p_cts Pointer to the CTS client structure.
* @param[in] p_evt Pointer to the event received from the Database Discovery module.
*/
void ble_cts_c_on_db_disc_evt(ble_cts_c_t * p_cts, ble_db_discovery_evt_t * p_evt);
/**@brief Function for handling the application's BLE stack events.
*
* @details This function handles all events from the BLE stack that are of interest to the
* Current Time Service client. This is a callback function that must be dispatched
* from the main application context.
*
* @param[in] p_ble_evt Event received from the BLE stack.
* @param[in] p_context Current Time Service client structure.
*/
void ble_cts_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for checking whether the peer's Current Time Service instance and the Current Time
* Characteristic have been discovered.
*
* @param[in] p_cts Current Time Service client structure.
*/
static __INLINE bool ble_cts_c_is_cts_discovered(const ble_cts_c_t * p_cts)
{
return (p_cts->char_handles.cts_handle != BLE_GATT_HANDLE_INVALID);
}
/**@brief Function for reading the peer's Current Time Service Current Time characteristic.
*
* @param[in] p_cts Current Time Service client structure.
*
* @retval NRF_SUCCESS If the operation is successful.
* @retval err_code Otherwise, this API propagates the error code returned by function
* @ref nrf_ble_gq_item_add.
*/
uint32_t ble_cts_c_current_time_read(ble_cts_c_t const * p_cts);
/**@brief Function for assigning handles to this instance of cts_c.
*
* @details Call this function when a link has been established with a peer to
* associate the link to this instance of the module. This association makes it
* possible to handle several links and associate each link to a particular
* instance of this module. The connection handle and attribute handles are
* provided from the discovery event @ref BLE_CTS_C_EVT_DISCOVERY_COMPLETE.
*
* @param[in] p_cts Pointer to the CTS client structure instance for associating the link.
* @param[in] conn_handle Connection handle to associate with the given CTS instance.
* @param[in] p_peer_handles Attribute handles for the CTS server you want this CTS client to
* interact with.
*
* @retval NRF_SUCCESS If the operation was successful.
* @retval NRF_ERROR_NULL If a p_cts was a NULL pointer.
* @retval err_code Otherwise, this API propagates the error code returned by function
* @ref nrf_ble_gq_conn_handle_register.
*/
uint32_t ble_cts_c_handles_assign(ble_cts_c_t * p_cts,
const uint16_t conn_handle,
const ble_cts_c_handles_t * p_peer_handles);
#ifdef __cplusplus
}
#endif
#endif // BLE_CTS_C_H__
/** @} */

View File

@@ -0,0 +1,323 @@
/**
* Copyright (c) 2017 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* Attention!
* To maintain compliance with Nordic Semiconductor ASA's Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#include "ble_dfu.h"
#include <string.h>
#include "ble_hci.h"
#include "sdk_macros.h"
#include "ble_srv_common.h"
#include "nrf_nvic.h"
#include "nrf_sdm.h"
#include "nrf_soc.h"
#include "nrf_log.h"
#include "nrf_dfu_ble_svci_bond_sharing.h"
#include "nrf_bootloader_info.h"
#include "nrf_svci_async_function.h"
#include "nrf_pwr_mgmt.h"
#include "peer_manager.h"
#include "gatts_cache_manager.h"
#include "peer_id.h"
#define MAX_CTRL_POINT_RESP_PARAM_LEN 3 /**< Max length of the responses. */
#define BLE_DFU_SERVICE_UUID 0xFE59 /**< The 16-bit UUID of the Secure DFU Service. */
static ble_dfu_buttonless_t m_dfu; /**< Structure holding information about the Buttonless Secure DFU Service. */
NRF_SDH_BLE_OBSERVER(m_dfus_obs, BLE_DFU_BLE_OBSERVER_PRIO, ble_dfu_buttonless_on_ble_evt, &m_dfu);
/**@brief Function that is called if no event handler is provided.
*/
static void dummy_evt_handler(ble_dfu_buttonless_evt_type_t evt)
{
NRF_LOG_DEBUG("Dummy event handler received event 0x%x", evt);
}
/**@brief Function for handling write events to the Buttonless Secure DFU Service Service Control Point characteristic.
*
* @param[in] p_evt_write Write event received from the BLE stack.
*/
static void on_ctrlpt_write(ble_gatts_evt_write_t const * p_evt_write)
{
uint32_t err_code;
ble_gatts_rw_authorize_reply_params_t write_authorize_reply;
memset(&write_authorize_reply, 0, sizeof(write_authorize_reply));
write_authorize_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
uint8_t cccd_val[2];
ble_gatts_value_t value = {.p_value = cccd_val, .len = 2, .offset = 0};
err_code = sd_ble_gatts_value_get(m_dfu.conn_handle, m_dfu.control_point_char.cccd_handle, &value);
if (err_code == NRF_SUCCESS && ble_srv_is_indication_enabled(cccd_val))
{
write_authorize_reply.params.write.update = 1;
write_authorize_reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
}
else
{
write_authorize_reply.params.write.gatt_status = BLE_GATT_STATUS_ATTERR_CPS_CCCD_CONFIG_ERROR;
}
// Authorize the write request
do {
err_code = sd_ble_gatts_rw_authorize_reply(m_dfu.conn_handle, &write_authorize_reply);
} while (err_code == NRF_ERROR_BUSY);
if (write_authorize_reply.params.write.gatt_status != BLE_GATT_STATUS_SUCCESS)
{
return;
}
// Forward the write event to the Buttonless DFU module.
ble_dfu_buttonless_on_ctrl_pt_write(p_evt_write);
}
/**@brief Write authorization request event handler.
*
* @details The write authorization request event handler is called when writing to the control point.
*
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_rw_authorize_req(ble_evt_t const * p_ble_evt)
{
if (p_ble_evt->evt.gatts_evt.conn_handle != m_dfu.conn_handle)
{
return;
}
const ble_gatts_evt_rw_authorize_request_t * p_auth_req =
&p_ble_evt->evt.gatts_evt.params.authorize_request;
if (
(p_auth_req->type == BLE_GATTS_AUTHORIZE_TYPE_WRITE) &&
(p_auth_req->request.write.handle == m_dfu.control_point_char.value_handle) &&
(p_auth_req->request.write.op != BLE_GATTS_OP_PREP_WRITE_REQ) &&
(p_auth_req->request.write.op != BLE_GATTS_OP_EXEC_WRITE_REQ_NOW) &&
(p_auth_req->request.write.op != BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL)
)
{
on_ctrlpt_write(&p_auth_req->request.write);
}
}
/**@brief Connect event handler.
*
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_connect(ble_evt_t const * p_ble_evt)
{
m_dfu.conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
}
/**@brief Disconnect event handler.
*
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_disconnect(ble_evt_t const * p_ble_evt)
{
if (m_dfu.conn_handle != p_ble_evt->evt.gap_evt.conn_handle)
{
return;
}
m_dfu.conn_handle = BLE_CONN_HANDLE_INVALID;
}
/**@brief Function for handling the HVC events.
*
* @details Handles HVC events from the BLE stack.
*
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_hvc(ble_evt_t const * p_ble_evt)
{
uint32_t err_code;
ble_gatts_evt_hvc_t const * p_hvc = &p_ble_evt->evt.gatts_evt.params.hvc;
if (p_hvc->handle == m_dfu.control_point_char.value_handle)
{
// Enter bootloader if we were waiting for reset after hvc indication confimation.
if (m_dfu.is_waiting_for_reset)
{
err_code = ble_dfu_buttonless_bootloader_start_prepare();
if (err_code != NRF_SUCCESS)
{
m_dfu.evt_handler(BLE_DFU_EVT_BOOTLOADER_ENTER_FAILED);
}
}
}
}
void ble_dfu_buttonless_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
VERIFY_PARAM_NOT_NULL_VOID(p_ble_evt);
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
on_connect(p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnect(p_ble_evt);
break;
case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
on_rw_authorize_req(p_ble_evt);
break;
case BLE_GATTS_EVT_HVC:
on_hvc(p_ble_evt);
break;
default:
// no implementation
break;
}
}
uint32_t ble_dfu_buttonless_resp_send(ble_dfu_buttonless_op_code_t op_code, ble_dfu_buttonless_rsp_code_t rsp_code)
{
// Send indication
uint32_t err_code;
const uint16_t len = MAX_CTRL_POINT_RESP_PARAM_LEN;
uint16_t hvx_len;
uint8_t hvx_data[MAX_CTRL_POINT_RESP_PARAM_LEN];
ble_gatts_hvx_params_t hvx_params;
memset(&hvx_params, 0, sizeof(hvx_params));
hvx_len = len;
hvx_data[0] = DFU_OP_RESPONSE_CODE;
hvx_data[1] = (uint8_t)op_code;
hvx_data[2] = (uint8_t)rsp_code;
hvx_params.handle = m_dfu.control_point_char.value_handle;
hvx_params.type = BLE_GATT_HVX_INDICATION;
hvx_params.offset = 0;
hvx_params.p_len = &hvx_len;
hvx_params.p_data = hvx_data;
err_code = sd_ble_gatts_hvx(m_dfu.conn_handle, &hvx_params);
if ((err_code == NRF_SUCCESS) && (hvx_len != len))
{
err_code = NRF_ERROR_DATA_SIZE;
}
return err_code;
}
uint32_t ble_dfu_buttonless_bootloader_start_finalize(void)
{
uint32_t err_code;
NRF_LOG_DEBUG("In ble_dfu_buttonless_bootloader_start_finalize\r\n");
err_code = sd_power_gpregret_clr(0, 0xffffffff);
VERIFY_SUCCESS(err_code);
err_code = sd_power_gpregret_set(0, BOOTLOADER_DFU_START);
VERIFY_SUCCESS(err_code);
// Indicate that the Secure DFU bootloader will be entered
m_dfu.evt_handler(BLE_DFU_EVT_BOOTLOADER_ENTER);
// Signal that DFU mode is to be enter to the power management module
nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_DFU);
return NRF_SUCCESS;
}
uint32_t ble_dfu_buttonless_init(const ble_dfu_buttonless_init_t * p_dfu_init)
{
uint32_t err_code;
ble_uuid_t service_uuid;
ble_uuid128_t nordic_base_uuid = BLE_NORDIC_VENDOR_BASE_UUID;
VERIFY_PARAM_NOT_NULL(p_dfu_init);
// Initialize the service structure.
m_dfu.conn_handle = BLE_CONN_HANDLE_INVALID;
m_dfu.evt_handler = p_dfu_init->evt_handler;
m_dfu.is_waiting_for_reset = false;
if (m_dfu.evt_handler == NULL)
{
m_dfu.evt_handler = dummy_evt_handler;
}
err_code = ble_dfu_buttonless_backend_init(&m_dfu);
VERIFY_SUCCESS(err_code);
BLE_UUID_BLE_ASSIGN(service_uuid, BLE_DFU_SERVICE_UUID);
// Add the DFU service declaration.
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
&service_uuid,
&(m_dfu.service_handle));
VERIFY_SUCCESS(err_code);
// Add vendor specific base UUID to use with the Buttonless DFU characteristic.
err_code = sd_ble_uuid_vs_add(&nordic_base_uuid, &m_dfu.uuid_type);
VERIFY_SUCCESS(err_code);
// Add the Buttonless DFU Characteristic (with bonds/without bonds).
err_code = ble_dfu_buttonless_char_add(&m_dfu);
VERIFY_SUCCESS(err_code);
return NRF_SUCCESS;
}

View File

@@ -0,0 +1,249 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_dfu Buttonless DFU Service
* @{
* @ingroup ble_sdk_srv
* @brief Buttonless DFU Service module.
*
* @details This module implements a proprietary Buttonless Secure DFU Service. The service can
* be configured to support bonds or not. The bond support configuration must correspond to the
* requirement of Secure DFU bootloader.
*
* @note Attention!
* To maintain compliance with Nordic Semiconductor ASA Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#ifndef BLE_DFU_H__
#define BLE_DFU_H__
#include <stdint.h>
#include "ble_srv_common.h"
#include "nrf_sdh_ble.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief SoC observer priority.
* @details Priority of this module's SoC event handler.
*/
#define BLE_DFU_SOC_OBSERVER_PRIO 1
#define BLE_DFU_BUTTONLESS_CHAR_UUID (0x0003) /**< Value combined with vendor-specific base to create Unbonded Buttonless characteristic UUID. */
#define BLE_DFU_BUTTONLESS_BONDED_CHAR_UUID (0x0004) /**< Value combined with vendor-specific base to create Bonded Buttonless characteristic UUID. */
/**@brief Nordic vendor-specific base UUID.
*/
#define BLE_NORDIC_VENDOR_BASE_UUID \
{{ \
0x50, 0xEA, 0xDA, 0x30, 0x88, 0x83, 0xB8, 0x9F, \
0x60, 0x4F, 0x15, 0xF3, 0x00, 0x00, 0xC9, 0x8E \
}}
/**@brief Nordic Buttonless DFU Service event type .
*/
typedef enum
{
BLE_DFU_EVT_BOOTLOADER_ENTER_PREPARE, /**< Event indicating that the device is preparing to enter bootloader.*/
BLE_DFU_EVT_BOOTLOADER_ENTER, /**< Event indicating that the bootloader will be entered after return of this event.*/
BLE_DFU_EVT_BOOTLOADER_ENTER_FAILED, /**< Failure to enter bootloader mode.*/
BLE_DFU_EVT_RESPONSE_SEND_ERROR, /**< Failure to send response.*/
} ble_dfu_buttonless_evt_type_t;
/**@brief Nordic Buttonless DFU Service event handler type.
*/
typedef void (*ble_dfu_buttonless_evt_handler_t) (ble_dfu_buttonless_evt_type_t p_evt);
/**@brief Enumeration of Bootloader DFU response codes.
*/
typedef enum
{
DFU_RSP_INVALID = 0x00, /**< Invalid op code. */
DFU_RSP_SUCCESS = 0x01, /**< Success. */
DFU_RSP_OP_CODE_NOT_SUPPORTED = 0x02, /**< Op code not supported. */
DFU_RSP_OPERATION_FAILED = 0x04, /**< Operation failed. */
DFU_RSP_ADV_NAME_INVALID = 0x05, /**< Requested advertisement name is too short or too long. */
DFU_RSP_BUSY = 0x06, /**< Ongoing async operation. */
DFU_RSP_NOT_BONDED = 0x07, /**< Buttonless unavailable due to device not bonded. */
} ble_dfu_buttonless_rsp_code_t;
/**@brief Enumeration of Bootloader DFU Operation codes.
*/
typedef enum
{
DFU_OP_RESERVED = 0x00, /**< Reserved for future use. */
DFU_OP_ENTER_BOOTLOADER = 0x01, /**< Enter bootloader. */
DFU_OP_SET_ADV_NAME = 0x02, /**< Set advertisement name to use in DFU mode. */
DFU_OP_RESPONSE_CODE = 0x20 /**< Response code. */
} ble_dfu_buttonless_op_code_t;
/**@brief Type holding memory used by Secure DFU Buttonless Service.
*/
typedef struct
{
uint8_t uuid_type; /**< UUID type for DFU UUID. */
uint16_t service_handle; /**< Service Handle of DFU (as provided by the SoftDevice). */
uint16_t conn_handle; /**< Connection handle for the current peer. */
ble_gatts_char_handles_t control_point_char; /**< Handles related to the DFU Control Point characteristic. */
uint32_t peers_count; /**< Counter to see how many persistently stored peers must be updated for Service Changed indication. This value will be counted down when comparing write requests. */
ble_dfu_buttonless_evt_handler_t evt_handler; /**< Event handler that is called upon Buttonless DFU events. See @ref ble_dfu_buttonless_evt_type_t. */
bool is_waiting_for_reset; /**< Flag indicating that the device will enter bootloader. */
bool is_waiting_for_svci; /**< Flag indicating that the device is waiting for async SVCI operation */
} ble_dfu_buttonless_t;
/**@brief Type used to initialize the Secure DFU Buttonless Service.
*/
typedef struct
{
ble_dfu_buttonless_evt_handler_t evt_handler; /**< Bootloader event handler. */
} ble_dfu_buttonless_init_t;
/**@brief Function for initializing the Device Firmware Update module.
*
* @param[in] p_dfu_init Structure containing the values of characteristics needed by the
* service.
* @retval NRF_SUCCESS on successful initialization of the service.
*/
uint32_t ble_dfu_buttonless_init(const ble_dfu_buttonless_init_t * p_dfu_init);
/**@brief Function for initializing the async SVCI interface.
*
* @warning Ensure that no interrupts are triggered when calling this functions as
* interrupts and exceptions are forwarded to the bootloader for the period
* of the call and may be lost.
*
* @details This configures the async interface for calling to the
* bootloader through SVCI interface.
*
* @retval NRF_SUCCESS on success, otherwise an error code.
*/
uint32_t ble_dfu_buttonless_async_svci_init(void);
/**@brief Function to initialize the backend Secure DFU Buttonless service which is either
* supports bonds or not.
*
* @note Do not call this function directly. It is called internally by @ref ble_dfu_buttonless_init.
*
* @param[in] p_dfu Nordic DFU Service structure.
*
* @return NRF_SUCCESS On sucessfully initializing, otherwise an error code.
*/
uint32_t ble_dfu_buttonless_backend_init(ble_dfu_buttonless_t * p_dfu);
/**@brief Function for adding the buttonless characteristic.
*
* @note This will be implemented differently on bonded/unbonded Buttonless DFU service.
*
* @param[in] p_dfu Nordic DFU Service structure.
*
* @return NRF_SUCCESS on success, otherwise an error code.
*/
uint32_t ble_dfu_buttonless_char_add(ble_dfu_buttonless_t * p_dfu);
/**@brief Function for sending a response back to the client.
*
* @param[in] op_code Operation code to send the response for.
* @param[in] rsp_code Response code for the operation.
*
* @retval NRF_SUCCESS on success, otherwise an error code.
*/
uint32_t ble_dfu_buttonless_resp_send(ble_dfu_buttonless_op_code_t op_code, ble_dfu_buttonless_rsp_code_t rsp_code);
/**@brief Function for handling the application's BLE stack events.
*
* @details Handles all events from the BLE stack of interest to the DFU buttonless service.
*
* @param[in] p_ble_evt Event received from the BLE stack.
* @param[in] p_context BLE context structure.
*/
void ble_dfu_buttonless_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for handling control point write requests.
*
* @details Handles write requests to the control point in
* DFU with bonds or without bonds.
*
* @param[in] p_evt_write GATTS write event.
*/
void ble_dfu_buttonless_on_ctrl_pt_write(ble_gatts_evt_write_t const * p_evt_write);
/**@brief Function for preparing to enter the bootloader.
*
* @warning This function is called directly. (It is called internally).
*
* @retval Any error code from calling @ref sd_ble_gap_disconnect.
*/
uint32_t ble_dfu_buttonless_bootloader_start_prepare(void);
/**@brief Function for finalizing entering the bootloader.
*
* @warning This function is not to be called. (It is called internally).
*
* @retval NRF_SUCCESS Finalize was started correctly.
*/
uint32_t ble_dfu_buttonless_bootloader_start_finalize(void);
#ifdef __cplusplus
}
#endif
#endif // BLE_DIS_H__
/** @} */

View File

@@ -0,0 +1,367 @@
/**
* Copyright (c) 2017 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "nrf_dfu_ble_svci_bond_sharing.h"
#include "nordic_common.h"
#include "nrf_error.h"
#include "ble_dfu.h"
#include "nrf_log.h"
#include "peer_manager.h"
#include "gatts_cache_manager.h"
#include "peer_id.h"
#include "nrf_sdh_soc.h"
#include "nrf_strerror.h"
#if (NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS)
void ble_dfu_buttonless_on_sys_evt(uint32_t, void * );
uint32_t nrf_dfu_svci_vector_table_set(void);
uint32_t nrf_dfu_svci_vector_table_unset(void);
/**@brief Define function for async interface to set peer data. */
NRF_SVCI_ASYNC_FUNC_DEFINE(NRF_DFU_SVCI_SET_PEER_DATA, nrf_dfu_set_peer_data, nrf_dfu_peer_data_t);
// Register SoC observer for the Buttonless Secure DFU service
NRF_SDH_SOC_OBSERVER(m_dfu_buttonless_soc_obs, BLE_DFU_SOC_OBSERVER_PRIO, ble_dfu_buttonless_on_sys_evt, NULL);
ble_dfu_buttonless_t * mp_dfu;
static nrf_dfu_peer_data_t m_peer_data;
/**@brief Function for handling Peer Manager events.
*
* @param[in] p_evt Peer Manager event.
*/
static void pm_evt_handler(pm_evt_t const * p_evt)
{
uint32_t ret;
if (mp_dfu == NULL)
{
return;
}
// Only handle this when we are waiting to reset into DFU mode
if (!mp_dfu->is_waiting_for_reset)
{
return;
}
switch(p_evt->evt_id)
{
case PM_EVT_PEER_DATA_UPDATE_SUCCEEDED:
if (p_evt->params.peer_data_update_succeeded.data_id == PM_PEER_DATA_ID_SERVICE_CHANGED_PENDING)
{
mp_dfu->peers_count--;
NRF_LOG_DEBUG("Updating Service Changed indication for peers, %d left", mp_dfu->peers_count);
if (mp_dfu->peers_count == 0)
{
NRF_LOG_DEBUG("Finished updating Service Changed indication for peers");
// We have updated Service Changed Indication for all devices.
ret = ble_dfu_buttonless_bootloader_start_finalize();
if (ret != NRF_SUCCESS)
{
mp_dfu->evt_handler(BLE_DFU_EVT_BOOTLOADER_ENTER_FAILED);
}
}
}
break;
case PM_EVT_PEER_DATA_UPDATE_FAILED:
// Failure to update data. Service Changed cannot be sent but DFU mode is still possible
ret = ble_dfu_buttonless_bootloader_start_finalize();
if (ret != NRF_SUCCESS)
{
mp_dfu->evt_handler(BLE_DFU_EVT_BOOTLOADER_ENTER_FAILED);
}
break;
default:
break;
}
}
static uint32_t retrieve_peer_data(void)
{
ret_code_t ret;
pm_peer_data_bonding_t bonding_data = {0};
pm_peer_id_t peer_id;
ret = pm_peer_id_get(mp_dfu->conn_handle, &peer_id);
VERIFY_SUCCESS(ret);
if (peer_id == PM_PEER_ID_INVALID)
{
return NRF_ERROR_FORBIDDEN;
}
ret = pm_peer_data_bonding_load(peer_id, &bonding_data);
VERIFY_SUCCESS(ret);
memcpy(&m_peer_data.ble_id, &bonding_data.peer_ble_id, sizeof(ble_gap_id_key_t));
memcpy(&m_peer_data.enc_key, &bonding_data.own_ltk, sizeof(ble_gap_enc_key_t));
uint16_t len = SYSTEM_SERVICE_ATT_SIZE;
ret = sd_ble_gatts_sys_attr_get(mp_dfu->conn_handle,
m_peer_data.sys_serv_attr,
&len,
BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS);
NRF_LOG_DEBUG("system attribute table len: %d", len);
return ret;
}
/**@brief Function for entering the bootloader.
*
* @details This starts forwarding peer data to the Secure DFU bootloader.
*/
static uint32_t enter_bootloader(void)
{
uint32_t ret;
NRF_LOG_INFO("Writing peer data to the bootloader...");
if (mp_dfu->is_waiting_for_svci)
{
return ble_dfu_buttonless_resp_send(DFU_OP_ENTER_BOOTLOADER, DFU_RSP_BUSY);
}
// If retrieve_peer_data returns NRF_ERROR_FORBIDDEN, then the device was not bonded.
ret = retrieve_peer_data();
VERIFY_SUCCESS(ret);
ret = nrf_dfu_set_peer_data(&m_peer_data);
if (ret == NRF_SUCCESS)
{
// The request was accepted. Waiting for sys events to progress.
mp_dfu->is_waiting_for_svci = true;
}
else if (ret == NRF_ERROR_FORBIDDEN)
{
NRF_LOG_ERROR("The bootloader has write protected its settings page. This prohibits setting the peer data. "\
"The bootloader must be compiled with NRF_BL_SETTINGS_PAGE_PROTECT=0 to allow setting the peer data.");
}
return ret;
}
uint32_t ble_dfu_buttonless_backend_init(ble_dfu_buttonless_t * p_dfu)
{
VERIFY_PARAM_NOT_NULL(p_dfu);
// Set the memory used by the backend.
mp_dfu = p_dfu;
// Initialize the Peer manager handler.
return pm_register(pm_evt_handler);
}
uint32_t ble_dfu_buttonless_async_svci_init(void)
{
uint32_t ret;
// Set the vector table base address to the bootloader.
ret = nrf_dfu_svci_vector_table_set();
NRF_LOG_DEBUG("nrf_dfu_svci_vector_table_set() -> %s",
(ret == NRF_SUCCESS) ? "success" : nrf_strerror_get(ret));
VERIFY_SUCCESS(ret);
// Initialize the asynchronous SuperVisor interface to set peer data in Secure DFU bootloader.
ret = nrf_dfu_set_peer_data_init();
NRF_LOG_DEBUG("nrf_dfu_set_peer_data_init() -> %s",
(ret == NRF_SUCCESS) ? "success" : nrf_strerror_get(ret));
VERIFY_SUCCESS(ret);
// Set the vector table base address back to main application.
ret = nrf_dfu_svci_vector_table_unset();
NRF_LOG_DEBUG("nrf_dfu_svci_vector_table_unset() -> %s",
(ret == NRF_SUCCESS) ? "success" : nrf_strerror_get(ret));
return ret;
}
void ble_dfu_buttonless_on_sys_evt(uint32_t sys_evt, void * p_context)
{
uint32_t ret;
if (!nrf_dfu_set_peer_data_is_initialized())
{
return;
}
ret = nrf_dfu_set_peer_data_on_sys_evt(sys_evt);
if (ret == NRF_ERROR_INVALID_STATE)
{
// The system event is not from an operation started by buttonless DFU.
// No action is taken, and nothing is reported.
}
else if (ret == NRF_SUCCESS)
{
// Peer data was successfully forwarded to the Secure DFU bootloader.
// Set the flag indicating that we are waiting for indication response
// to activate the reset.
mp_dfu->is_waiting_for_reset = true;
mp_dfu->is_waiting_for_svci = false;
// Report back the positive response
ret = ble_dfu_buttonless_resp_send(DFU_OP_ENTER_BOOTLOADER, DFU_RSP_SUCCESS);
if (ret != NRF_SUCCESS)
{
mp_dfu->evt_handler(BLE_DFU_EVT_RESPONSE_SEND_ERROR);
mp_dfu->is_waiting_for_reset = false;
}
}
else
{
// Failed to set peer data. Report this.
mp_dfu->is_waiting_for_reset = false;
mp_dfu->is_waiting_for_svci = false;
ret = ble_dfu_buttonless_resp_send(DFU_OP_ENTER_BOOTLOADER, DFU_RSP_BUSY);
// Report the failure to send the response to the client
if (ret != NRF_SUCCESS)
{
mp_dfu->evt_handler(BLE_DFU_EVT_RESPONSE_SEND_ERROR);
}
// Report the failure to enter DFU mode
mp_dfu->evt_handler(BLE_DFU_EVT_BOOTLOADER_ENTER_FAILED);
}
}
uint32_t ble_dfu_buttonless_char_add(ble_dfu_buttonless_t * p_dfu)
{
ble_add_char_params_t add_char_params;
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = BLE_DFU_BUTTONLESS_BONDED_CHAR_UUID;
add_char_params.uuid_type = p_dfu->uuid_type;
add_char_params.char_props.indicate = 1;
add_char_params.char_props.write = 1;
add_char_params.is_defered_write = true;
add_char_params.is_var_len = true;
add_char_params.max_len = BLE_GATT_ATT_MTU_DEFAULT;
add_char_params.cccd_write_access = SEC_JUST_WORKS;
add_char_params.write_access = SEC_JUST_WORKS;
add_char_params.read_access = SEC_OPEN;
return characteristic_add(p_dfu->service_handle, &add_char_params, &p_dfu->control_point_char);
}
void ble_dfu_buttonless_on_ctrl_pt_write(ble_gatts_evt_write_t const * p_evt_write)
{
uint32_t ret;
ble_dfu_buttonless_rsp_code_t rsp_code = DFU_RSP_OPERATION_FAILED;
// Start executing the control point write action
switch (p_evt_write->data[0])
{
case DFU_OP_ENTER_BOOTLOADER:
ret = enter_bootloader();
if (ret == NRF_SUCCESS)
{
rsp_code = DFU_RSP_SUCCESS;
}
else if (ret == NRF_ERROR_BUSY)
{
rsp_code = DFU_RSP_BUSY;
}
else if (ret == NRF_ERROR_FORBIDDEN)
{
rsp_code = DFU_RSP_NOT_BONDED;
}
break;
default:
rsp_code = DFU_RSP_OP_CODE_NOT_SUPPORTED;
break;
}
// Report back in case of error
if (rsp_code != DFU_RSP_SUCCESS)
{
ret = ble_dfu_buttonless_resp_send((ble_dfu_buttonless_op_code_t)p_evt_write->data[0],
rsp_code);
if (ret != NRF_SUCCESS)
{
mp_dfu->evt_handler(BLE_DFU_EVT_RESPONSE_SEND_ERROR);
}
// Report the error to the main application
mp_dfu->evt_handler(BLE_DFU_EVT_BOOTLOADER_ENTER_FAILED);
}
}
uint32_t ble_dfu_buttonless_bootloader_start_prepare(void)
{
NRF_LOG_DEBUG("In ble_dfu_buttonless_bootloader_start_prepare");
// Indicate to main app that DFU mode is starting.
// This event can be used to let the device take down any connection to
// bonded devices.
mp_dfu->evt_handler(BLE_DFU_EVT_BOOTLOADER_ENTER_PREPARE);
// Store the number of peers for which Peer Manager is expected to successfully write events.
mp_dfu->peers_count = peer_id_n_ids();
// Set local database changed to get Service Changed indication for all bonded peers
// on next bootup (either because of a successful or aborted DFU).
gscm_local_database_has_changed();
return NRF_SUCCESS;
}
#endif // NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS

View File

@@ -0,0 +1,299 @@
/**
* Copyright (c) 2017 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "nrf_dfu_ble_svci_bond_sharing.h"
#include "nordic_common.h"
#include "nrf_error.h"
#include "ble_dfu.h"
#include "nrf_log.h"
#include "nrf_sdh_soc.h"
#if (!NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS)
#define NRF_DFU_ADV_NAME_MAX_LENGTH (20)
void ble_dfu_buttonless_on_sys_evt(uint32_t, void * );
uint32_t nrf_dfu_svci_vector_table_set(void);
uint32_t nrf_dfu_svci_vector_table_unset(void);
/**@brief Define functions for async interface to set new advertisement name for DFU mode. */
NRF_SVCI_ASYNC_FUNC_DEFINE(NRF_DFU_SVCI_SET_ADV_NAME, nrf_dfu_set_adv_name, nrf_dfu_adv_name_t);
// Register SoC observer for the Buttonless Secure DFU service
NRF_SDH_SOC_OBSERVER(m_dfu_buttonless_soc_obs, BLE_DFU_SOC_OBSERVER_PRIO, ble_dfu_buttonless_on_sys_evt, NULL);
ble_dfu_buttonless_t * mp_dfu = NULL;
static nrf_dfu_adv_name_t m_adv_name;
/**@brief Function for setting an advertisement name.
*
* @param[in] adv_name The new advertisement name.
*
* @retval NRF_SUCCESS Advertisement name was successfully set.
* @retval DFU_RSP_BUSY Advertisement name was not set because of an ongoing operation.
* @retval Any other errors from the SVCI interface call.
*/
static uint32_t set_adv_name(nrf_dfu_adv_name_t * p_adv_name)
{
uint32_t err_code;
if (mp_dfu->is_waiting_for_svci)
{
return DFU_RSP_BUSY;
}
err_code = nrf_dfu_set_adv_name(p_adv_name);
if (err_code == NRF_SUCCESS)
{
// The request was accepted.
mp_dfu->is_waiting_for_svci = true;
}
else if (err_code == NRF_ERROR_FORBIDDEN)
{
NRF_LOG_ERROR("The bootloader has write protected its settings page. This prohibits setting the advertising name. "\
"The bootloader must be compiled with NRF_BL_SETTINGS_PAGE_PROTECT=0 to allow setting the advertising name.");
}
return err_code;
}
/**@brief Function for entering the bootloader.
*/
static uint32_t enter_bootloader()
{
uint32_t err_code;
if (mp_dfu->is_waiting_for_svci)
{
// We have an ongoing async operation. Entering bootloader mode is not possible at this time.
err_code = ble_dfu_buttonless_resp_send(DFU_OP_ENTER_BOOTLOADER, DFU_RSP_BUSY);
if (err_code != NRF_SUCCESS)
{
mp_dfu->evt_handler(BLE_DFU_EVT_RESPONSE_SEND_ERROR);
}
return NRF_SUCCESS;
}
// Set the flag indicating that we expect DFU mode.
// This will be handled on acknowledgement of the characteristic indication.
mp_dfu->is_waiting_for_reset = true;
err_code = ble_dfu_buttonless_resp_send(DFU_OP_ENTER_BOOTLOADER, DFU_RSP_SUCCESS);
if (err_code != NRF_SUCCESS)
{
mp_dfu->is_waiting_for_reset = false;
}
return err_code;
}
uint32_t ble_dfu_buttonless_backend_init(ble_dfu_buttonless_t * p_dfu)
{
VERIFY_PARAM_NOT_NULL(p_dfu);
mp_dfu = p_dfu;
return NRF_SUCCESS;
}
uint32_t ble_dfu_buttonless_async_svci_init(void)
{
uint32_t ret_val;
ret_val = nrf_dfu_svci_vector_table_set();
VERIFY_SUCCESS(ret_val);
ret_val = nrf_dfu_set_adv_name_init();
VERIFY_SUCCESS(ret_val);
ret_val = nrf_dfu_svci_vector_table_unset();
return ret_val;
}
void ble_dfu_buttonless_on_sys_evt(uint32_t sys_evt, void * p_context)
{
uint32_t err_code;
if (!nrf_dfu_set_adv_name_is_initialized())
{
return;
}
err_code = nrf_dfu_set_adv_name_on_sys_evt(sys_evt);
if (err_code == NRF_ERROR_INVALID_STATE)
{
// The system event is not from an operation started by buttonless DFU.
// No action is taken, and nothing is reported.
}
else if (err_code == NRF_SUCCESS)
{
// The async operation is finished.
// Set the flag indicating that we are waiting for indication response
// to activate the reset.
mp_dfu->is_waiting_for_svci = false;
// Report back the positive response
err_code = ble_dfu_buttonless_resp_send(DFU_OP_SET_ADV_NAME, DFU_RSP_SUCCESS);
if (err_code != NRF_SUCCESS)
{
mp_dfu->evt_handler(BLE_DFU_EVT_RESPONSE_SEND_ERROR);
}
}
else
{
// Invalid error code reported back.
mp_dfu->is_waiting_for_svci = false;
err_code = ble_dfu_buttonless_resp_send(DFU_OP_SET_ADV_NAME, DFU_RSP_BUSY);
if (err_code != NRF_SUCCESS)
{
mp_dfu->evt_handler(BLE_DFU_EVT_RESPONSE_SEND_ERROR);
}
// Report the failure to enter DFU mode
mp_dfu->evt_handler(BLE_DFU_EVT_BOOTLOADER_ENTER_FAILED);
}
}
uint32_t ble_dfu_buttonless_char_add(ble_dfu_buttonless_t * p_dfu)
{
ble_add_char_params_t add_char_params;
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = BLE_DFU_BUTTONLESS_CHAR_UUID;
add_char_params.uuid_type = p_dfu->uuid_type;
add_char_params.char_props.indicate = 1;
add_char_params.char_props.write = 1;
add_char_params.is_defered_write = true;
add_char_params.is_var_len = true;
add_char_params.max_len = BLE_GATT_ATT_MTU_DEFAULT;
add_char_params.cccd_write_access = SEC_OPEN;
add_char_params.write_access = SEC_OPEN;
add_char_params.read_access = SEC_OPEN;
return characteristic_add(p_dfu->service_handle, &add_char_params, &p_dfu->control_point_char);
}
void ble_dfu_buttonless_on_ctrl_pt_write(ble_gatts_evt_write_t const * p_evt_write)
{
uint32_t err_code;
ble_dfu_buttonless_rsp_code_t rsp_code = DFU_RSP_OPERATION_FAILED;
// Start executing the control point write operation
/*lint -e415 -e416 -save "Out of bounds access"*/
switch (p_evt_write->data[0])
{
case DFU_OP_ENTER_BOOTLOADER:
err_code = enter_bootloader();
if (err_code == NRF_SUCCESS)
{
rsp_code = DFU_RSP_SUCCESS;
}
else if (err_code == NRF_ERROR_BUSY)
{
rsp_code = DFU_RSP_BUSY;
}
break;
case DFU_OP_SET_ADV_NAME:
if( (p_evt_write->data[1] > NRF_DFU_ADV_NAME_MAX_LENGTH)
|| (p_evt_write->data[1] == 0))
{
// New advertisement name too short or too long.
rsp_code = DFU_RSP_ADV_NAME_INVALID;
}
else
{
memcpy(m_adv_name.name, &p_evt_write->data[2], p_evt_write->data[1]);
m_adv_name.len = p_evt_write->data[1];
err_code = set_adv_name(&m_adv_name);
if (err_code == NRF_SUCCESS)
{
rsp_code = DFU_RSP_SUCCESS;
}
}
break;
default:
rsp_code = DFU_RSP_OP_CODE_NOT_SUPPORTED;
break;
}
/*lint -restore*/
// Report back in case of error
if (rsp_code != DFU_RSP_SUCCESS)
{
err_code = ble_dfu_buttonless_resp_send((ble_dfu_buttonless_op_code_t)p_evt_write->data[0], rsp_code);
if (err_code != NRF_SUCCESS)
{
mp_dfu->evt_handler(BLE_DFU_EVT_RESPONSE_SEND_ERROR);
}
// Report the error to the main application
mp_dfu->evt_handler(BLE_DFU_EVT_BOOTLOADER_ENTER_FAILED);
}
}
uint32_t ble_dfu_buttonless_bootloader_start_prepare(void)
{
uint32_t err_code;
// Indicate to main app that DFU mode is starting.
mp_dfu->evt_handler(BLE_DFU_EVT_BOOTLOADER_ENTER_PREPARE);
err_code = ble_dfu_buttonless_bootloader_start_finalize();
return err_code;
}
#endif // NRF_DFU_BOTTONLESS_SUPPORT_BOND

View File

@@ -0,0 +1,280 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* Attention!
* To maintain compliance with Nordic Semiconductor ASA's Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_DIS)
#include "ble_dis.h"
#include <stdlib.h>
#include <string.h>
#include "app_error.h"
#include "ble_gatts.h"
#include "ble_srv_common.h"
#define BLE_DIS_SYS_ID_LEN 8 /**< Length of System ID Characteristic Value. */
#define BLE_DIS_PNP_ID_LEN 7 /**< Length of Pnp ID Characteristic Value. */
static uint16_t service_handle;
static ble_gatts_char_handles_t manufact_name_handles;
static ble_gatts_char_handles_t model_num_handles;
static ble_gatts_char_handles_t serial_num_handles;
static ble_gatts_char_handles_t hw_rev_handles;
static ble_gatts_char_handles_t fw_rev_handles;
static ble_gatts_char_handles_t sw_rev_handles;
static ble_gatts_char_handles_t sys_id_handles;
static ble_gatts_char_handles_t reg_cert_data_list_handles;
static ble_gatts_char_handles_t pnp_id_handles;
/**@brief Function for encoding a System ID.
*
* @param[out] p_encoded_buffer Buffer where the encoded data will be written.
* @param[in] p_sys_id System ID to be encoded.
*/
static void sys_id_encode(uint8_t * p_encoded_buffer, ble_dis_sys_id_t const * p_sys_id)
{
APP_ERROR_CHECK_BOOL(p_sys_id != NULL);
APP_ERROR_CHECK_BOOL(p_encoded_buffer != NULL);
p_encoded_buffer[0] = (p_sys_id->manufacturer_id & 0x00000000FF);
p_encoded_buffer[1] = (p_sys_id->manufacturer_id & 0x000000FF00) >> 8;
p_encoded_buffer[2] = (p_sys_id->manufacturer_id & 0x0000FF0000) >> 16;
p_encoded_buffer[3] = (p_sys_id->manufacturer_id & 0x00FF000000) >> 24;
p_encoded_buffer[4] = (p_sys_id->manufacturer_id & 0xFF00000000) >> 32;
p_encoded_buffer[5] = (p_sys_id->organizationally_unique_id & 0x0000FF);
p_encoded_buffer[6] = (p_sys_id->organizationally_unique_id & 0x00FF00) >> 8;
p_encoded_buffer[7] = (p_sys_id->organizationally_unique_id & 0xFF0000) >> 16;
}
/**@brief Function for encoding a PnP ID.
*
* @param[out] p_encoded_buffer Buffer where the encoded data will be written.
* @param[in] p_pnp_id PnP ID to be encoded.
*/
static void pnp_id_encode(uint8_t * p_encoded_buffer, ble_dis_pnp_id_t const * p_pnp_id)
{
uint8_t len = 0;
APP_ERROR_CHECK_BOOL(p_pnp_id != NULL);
APP_ERROR_CHECK_BOOL(p_encoded_buffer != NULL);
p_encoded_buffer[len++] = p_pnp_id->vendor_id_source;
len += uint16_encode(p_pnp_id->vendor_id, &p_encoded_buffer[len]);
len += uint16_encode(p_pnp_id->product_id, &p_encoded_buffer[len]);
len += uint16_encode(p_pnp_id->product_version, &p_encoded_buffer[len]);
APP_ERROR_CHECK_BOOL(len == BLE_DIS_PNP_ID_LEN);
}
/**@brief Function for adding the Characteristic.
*
* @param[in] uuid UUID of characteristic to be added.
* @param[in] p_char_value Initial value of characteristic to be added.
* @param[in] char_len Length of initial value. This will also be the maximum value.
* @param[in] rd_sec Security requirement for reading characteristic value.
* @param[out] p_handles Handles of new characteristic.
*
* @return NRF_SUCCESS on success, otherwise an error code.
*/
static uint32_t char_add(uint16_t uuid,
uint8_t * p_char_value,
uint16_t char_len,
security_req_t const rd_sec,
ble_gatts_char_handles_t * p_handles)
{
ble_add_char_params_t add_char_params;
APP_ERROR_CHECK_BOOL(p_char_value != NULL);
APP_ERROR_CHECK_BOOL(char_len > 0);
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = uuid;
add_char_params.max_len = char_len;
add_char_params.init_len = char_len;
add_char_params.p_init_value = p_char_value;
add_char_params.char_props.read = 1;
add_char_params.read_access = rd_sec;
return characteristic_add(service_handle, &add_char_params, p_handles);
}
uint32_t ble_dis_init(ble_dis_init_t const * p_dis_init)
{
uint32_t err_code;
ble_uuid_t ble_uuid;
// Add service
BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_DEVICE_INFORMATION_SERVICE);
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &service_handle);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
// Add characteristics
if (p_dis_init->manufact_name_str.length > 0)
{
err_code = char_add(BLE_UUID_MANUFACTURER_NAME_STRING_CHAR,
p_dis_init->manufact_name_str.p_str,
p_dis_init->manufact_name_str.length,
p_dis_init->dis_char_rd_sec,
&manufact_name_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
}
if (p_dis_init->model_num_str.length > 0)
{
err_code = char_add(BLE_UUID_MODEL_NUMBER_STRING_CHAR,
p_dis_init->model_num_str.p_str,
p_dis_init->model_num_str.length,
p_dis_init->dis_char_rd_sec,
&model_num_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
}
if (p_dis_init->serial_num_str.length > 0)
{
err_code = char_add(BLE_UUID_SERIAL_NUMBER_STRING_CHAR,
p_dis_init->serial_num_str.p_str,
p_dis_init->serial_num_str.length,
p_dis_init->dis_char_rd_sec,
&serial_num_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
}
if (p_dis_init->hw_rev_str.length > 0)
{
err_code = char_add(BLE_UUID_HARDWARE_REVISION_STRING_CHAR,
p_dis_init->hw_rev_str.p_str,
p_dis_init->hw_rev_str.length,
p_dis_init->dis_char_rd_sec,
&hw_rev_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
}
if (p_dis_init->fw_rev_str.length > 0)
{
err_code = char_add(BLE_UUID_FIRMWARE_REVISION_STRING_CHAR,
p_dis_init->fw_rev_str.p_str,
p_dis_init->fw_rev_str.length,
p_dis_init->dis_char_rd_sec,
&fw_rev_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
}
if (p_dis_init->sw_rev_str.length > 0)
{
err_code = char_add(BLE_UUID_SOFTWARE_REVISION_STRING_CHAR,
p_dis_init->sw_rev_str.p_str,
p_dis_init->sw_rev_str.length,
p_dis_init->dis_char_rd_sec,
&sw_rev_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
}
if (p_dis_init->p_sys_id != NULL)
{
uint8_t encoded_sys_id[BLE_DIS_SYS_ID_LEN];
sys_id_encode(encoded_sys_id, p_dis_init->p_sys_id);
err_code = char_add(BLE_UUID_SYSTEM_ID_CHAR,
encoded_sys_id,
BLE_DIS_SYS_ID_LEN,
p_dis_init->dis_char_rd_sec,
&sys_id_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
}
if (p_dis_init->p_reg_cert_data_list != NULL)
{
err_code = char_add(BLE_UUID_IEEE_REGULATORY_CERTIFICATION_DATA_LIST_CHAR,
p_dis_init->p_reg_cert_data_list->p_list,
p_dis_init->p_reg_cert_data_list->list_len,
p_dis_init->dis_char_rd_sec,
&reg_cert_data_list_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
}
if (p_dis_init->p_pnp_id != NULL)
{
uint8_t encoded_pnp_id[BLE_DIS_PNP_ID_LEN];
pnp_id_encode(encoded_pnp_id, p_dis_init->p_pnp_id);
err_code = char_add(BLE_UUID_PNP_ID_CHAR,
encoded_pnp_id,
BLE_DIS_PNP_ID_LEN,
p_dis_init->dis_char_rd_sec,
&pnp_id_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
}
return NRF_SUCCESS;
}
#endif // NRF_MODULE_ENABLED(BLE_DIS)

View File

@@ -0,0 +1,134 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_dis Device Information Service
* @{
* @ingroup ble_sdk_srv
* @brief Device Information Service module.
*
* @details This module implements the Device Information Service.
* During initialization it adds the Device Information Service to the BLE stack database.
* It then encodes the supplied information, and adds the corresponding characteristics.
*
* @note Attention!
* To maintain compliance with Nordic Semiconductor ASA Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#ifndef BLE_DIS_H__
#define BLE_DIS_H__
#include <stdint.h>
#include "ble_srv_common.h"
#ifdef __cplusplus
extern "C" {
#endif
/** @defgroup DIS_VENDOR_ID_SRC_VALUES Vendor ID Source values
* @{
*/
#define BLE_DIS_VENDOR_ID_SRC_BLUETOOTH_SIG 1 /**< Vendor ID assigned by Bluetooth SIG. */
#define BLE_DIS_VENDOR_ID_SRC_USB_IMPL_FORUM 2 /**< Vendor ID assigned by USB Implementer's Forum. */
/** @} */
/**@brief System ID parameters */
typedef struct
{
uint64_t manufacturer_id; /**< Manufacturer ID. Only 5 LSOs shall be used. */
uint32_t organizationally_unique_id; /**< Organizationally unique ID. Only 3 LSOs shall be used. */
} ble_dis_sys_id_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. */
} ble_dis_reg_cert_data_list_t;
/**@brief PnP ID parameters */
typedef struct
{
uint8_t vendor_id_source; /**< Vendor ID Source. see @ref DIS_VENDOR_ID_SRC_VALUES. */
uint16_t vendor_id; /**< Vendor ID. */
uint16_t product_id; /**< Product ID. */
uint16_t product_version; /**< Product Version. */
} ble_dis_pnp_id_t;
/**@brief Device Information Service init structure. This contains all possible characteristics
* needed for initialization of the service.
*/
typedef struct
{
ble_srv_utf8_str_t manufact_name_str; /**< Manufacturer Name String. */
ble_srv_utf8_str_t model_num_str; /**< Model Number String. */
ble_srv_utf8_str_t serial_num_str; /**< Serial Number String. */
ble_srv_utf8_str_t hw_rev_str; /**< Hardware Revision String. */
ble_srv_utf8_str_t fw_rev_str; /**< Firmware Revision String. */
ble_srv_utf8_str_t sw_rev_str; /**< Software Revision String. */
ble_dis_sys_id_t * p_sys_id; /**< System ID. */
ble_dis_reg_cert_data_list_t * p_reg_cert_data_list; /**< IEEE 11073-20601 Regulatory Certification Data List. */
ble_dis_pnp_id_t * p_pnp_id; /**< PnP ID. */
security_req_t dis_char_rd_sec; /**< Security requirement for reading any DIS characteristic value. */
} ble_dis_init_t;
/**@brief Function for initializing the Device Information Service.
*
* @details This call allows the application to initialize the device information service.
* It adds the DIS service and DIS characteristics to the database, using the initial
* values supplied through the p_dis_init parameter. Characteristics which are not to be
* added, shall be set to NULL in p_dis_init.
*
* @param[in] p_dis_init The structure containing the values of characteristics needed by the
* service.
*
* @return NRF_SUCCESS on successful initialization of service.
*/
uint32_t ble_dis_init(ble_dis_init_t const * p_dis_init);
#ifdef __cplusplus
}
#endif
#endif // BLE_DIS_H__
/** @} */

View File

@@ -0,0 +1,516 @@
/**
* Copyright (c) 2017 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_DIS_C)
#include <stdlib.h>
#include "ble.h"
#include "ble_dis_c.h"
#include "ble_gattc.h"
#include "nrf_bitmask.h"
#include "app_error.h"
#define NRF_LOG_MODULE_NAME ble_dis
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();
// Value Field lengths for System ID characteristic.
#define BLE_DIS_C_MANUF_ID_LEN 5 /**< Length of Manufacturer ID field inside System ID characteristic. */
#define BLE_DIS_C_OU_ID_LEN 3 /**< Length of Organizationally Unique ID field inside System ID characteristic. */
// Value Field lengths for PnP ID characteristic.
#define BLE_DIS_C_VENDOR_ID_SRC_LEN 1 /**< Length of Vendor ID Source field inside PnP ID characteristic. */
#define BLE_DIS_C_VENDOR_ID_LEN 2 /**< Length of Vendor ID field inside PnP ID characteristic. */
#define BLE_DIS_C_PRODUCT_ID_LEN 2 /**< Length of Product ID field inside PnP ID characteristic. */
#define BLE_DIS_C_PRODUCT_VER_LEN 2 /**< Length of Product Version field inside PnP ID characteristic. */
#define BLE_DIS_C_ALL_CHARS_DISABLED_MASK 0x0000 /**< All DIS characteristics should be disabled. */
#define BLE_DIS_C_ALL_CHARS_ENABLED_MASK 0xFFFF /**< All DIS characteristics should be enabled. */
/**@brief Function for interception of gattc errors.
*
* @param[in] nrf_error Error code returned by SoftDevice.
* @param[in] p_contex Parameter from the event handler.
* @param[in] conn_handle Connection handle.
*/
static void gatt_error_handler(uint32_t nrf_error, void * p_contex, uint16_t conn_handle)
{
UNUSED_PARAMETER(conn_handle);
ble_dis_c_t const * const p_ble_disc_c = (ble_dis_c_t *)p_contex;
if (p_ble_disc_c != NULL)
{
p_ble_disc_c->error_handler(nrf_error);
}
}
/**@brief Function for decoding System ID characteristic value.
*
* @param[in] p_data Pointer to System ID characteristic data.
* @param[in] len Length of the System ID characteristic data.
* @param[out] p_sys_id Decoded System ID characteristic.
*
* @retval NRF_SUCCESS If the characteristic was initialized successfully.
* @retval NRF_ERROR_INVALID_LENGTH Any parameter is NULL.
*/
static ret_code_t system_id_decode(uint8_t const * p_data,
uint16_t len,
ble_dis_sys_id_t * const p_sys_id)
{
uint16_t const expected_len = (BLE_DIS_C_MANUF_ID_LEN + BLE_DIS_C_OU_ID_LEN);
// Validate response length.
if (expected_len != len)
{
NRF_LOG_ERROR("System ID characteristic data cannot be decoded.");
NRF_LOG_ERROR("Expected data length != Received data length: %d != %d", expected_len, len);
return NRF_ERROR_INVALID_LENGTH;
}
// Decode Manufacturer ID.
p_sys_id->manufacturer_id = uint40_decode(p_data);
p_data += BLE_DIS_C_MANUF_ID_LEN;
// Decode Organizationally unique ID.
p_sys_id->organizationally_unique_id = uint24_decode(p_data);
return NRF_SUCCESS;
}
/**@brief Function for decoding PnP ID characteristic value.
*
* @param[in] p_data Pointer to PnP ID characteristic data.
* @param[in] len Length of the PnP ID characteristic data.
* @param[out] p_pnp_id Decoded PnP ID characteristic.
*
* @retval NRF_SUCCESS If the characteristic was initialized successfully.
* @retval NRF_ERROR_INVALID_LENGTH Any parameter is NULL.
*/
static ret_code_t pnp_id_decode(uint8_t const * p_data,
uint16_t len,
ble_dis_pnp_id_t * const p_pnp_id)
{
uint16_t const expected_len = (BLE_DIS_C_VENDOR_ID_SRC_LEN + BLE_DIS_C_VENDOR_ID_LEN +
BLE_DIS_C_PRODUCT_ID_LEN + BLE_DIS_C_PRODUCT_VER_LEN);
// Validate response length.
if (expected_len != len)
{
NRF_LOG_ERROR("PnP ID characteristic data cannot be decoded.");
NRF_LOG_ERROR("Expected data length != Received data length: %d != %d", expected_len, len);
return NRF_ERROR_INVALID_LENGTH;
}
// Decode Vendor ID Source.
p_pnp_id->vendor_id_source = p_data[0];
p_data += BLE_DIS_C_VENDOR_ID_SRC_LEN;
// Decode Vendor ID.
p_pnp_id->vendor_id = uint16_decode(p_data);
p_data += BLE_DIS_C_VENDOR_ID_LEN;
// Decode Product ID.
p_pnp_id->product_id = uint16_decode(p_data);
p_data += BLE_DIS_C_PRODUCT_ID_LEN;
// Decode Product Version.
p_pnp_id->product_version = uint16_decode(p_data);
return NRF_SUCCESS;
}
/**@brief Function for matching DIS Client characteristic type with the provided response handle.
*
* @param[in] p_ble_dis_c Pointer to the Device Information Client Structure.
* @param[in] response_handle Attribute handle from the response event.
*/
static ble_dis_c_char_type_t char_type_get(ble_dis_c_t * p_ble_dis_c, uint16_t response_handle)
{
for (ble_dis_c_char_type_t char_type = (ble_dis_c_char_type_t) 0;
char_type < BLE_DIS_C_CHAR_TYPES_NUM;
char_type++)
{
if (response_handle == p_ble_dis_c->handles[char_type])
{
return char_type;
}
}
return BLE_DIS_C_CHAR_TYPES_NUM;
}
/**@brief Function for handling read response events.
*
* @details This function will validate the read response and raise the appropriate
* event to the application.
*
* @param[in] p_ble_dis_c Pointer to the Device Information Client Structure.
* @param[in] p_ble_evt Pointer to the SoftDevice event.
*/
static void on_read_rsp(ble_dis_c_t * p_ble_dis_c, ble_evt_t const * p_ble_evt)
{
ret_code_t err_code;
ble_gattc_evt_read_rsp_t const * p_response = &p_ble_evt->evt.gattc_evt.params.read_rsp;
ble_dis_c_evt_t ble_dis_c_evt;
ble_dis_c_char_type_t char_type;
// Check if the event is on the link for this instance and the event handler is present.
if ((p_ble_dis_c->evt_handler == NULL) ||
(p_ble_dis_c->conn_handle != p_ble_evt->evt.gattc_evt.conn_handle))
{
return;
}
char_type = char_type_get(p_ble_dis_c, p_response->handle);
if (char_type < BLE_DIS_C_CHAR_TYPES_NUM) // Characteristic type is valid.
{
memset(&ble_dis_c_evt, 0, sizeof(ble_dis_c_evt_t));
ble_dis_c_evt.conn_handle = p_ble_evt->evt.gattc_evt.conn_handle;
if (p_ble_evt->evt.gattc_evt.gatt_status == BLE_GATT_STATUS_SUCCESS)
{
ble_dis_c_evt_read_rsp_t * const p_dis_rsp = &ble_dis_c_evt.params.read_rsp;
ble_dis_c_evt.evt_type = BLE_DIS_C_EVT_DIS_C_READ_RSP;
p_dis_rsp->char_type = char_type;
p_dis_rsp->handle = p_response->handle;
// Decode characteristic value.
switch (char_type)
{
case BLE_DIS_C_MANUF_NAME:
case BLE_DIS_C_MODEL_NUM:
case BLE_DIS_C_SERIAL_NUM:
case BLE_DIS_C_HW_REV:
case BLE_DIS_C_FW_REV:
case BLE_DIS_C_SW_REV:
p_dis_rsp->content.string.p_data = (uint8_t *) p_response->data;
p_dis_rsp->content.string.len = p_response->len;
break;
case BLE_DIS_C_SYS_ID:
err_code = system_id_decode(p_response->data,
p_response->len,
&p_dis_rsp->content.sys_id);
if ((p_ble_dis_c->error_handler != NULL) && (err_code != NRF_SUCCESS))
{
p_ble_dis_c->error_handler(err_code);
}
break;
case BLE_DIS_C_CERT_LIST:
p_dis_rsp->content.cert_list.p_list = (uint8_t *) p_response->data;
p_dis_rsp->content.cert_list.list_len = p_response->len;
break;
case BLE_DIS_C_PNP_ID:
err_code = pnp_id_decode(p_response->data,
p_response->len,
&p_dis_rsp->content.pnp_id);
if ((p_ble_dis_c->error_handler != NULL) && (err_code != NRF_SUCCESS))
{
p_ble_dis_c->error_handler(err_code);
}
break;
default:
break;
}
p_ble_dis_c->evt_handler(p_ble_dis_c, &ble_dis_c_evt);
NRF_LOG_DEBUG("Received correct read response.");
}
else // Generate error event.
{
ble_dis_c_evt.evt_type = BLE_DIS_C_EVT_DIS_C_READ_RSP_ERROR;
ble_dis_c_evt.params.read_rsp_err.char_type = char_type;
ble_dis_c_evt.params.read_rsp_err.err_handle = p_ble_evt->evt.gattc_evt.error_handle;
ble_dis_c_evt.params.read_rsp_err.gatt_status = p_ble_evt->evt.gattc_evt.gatt_status;
p_ble_dis_c->evt_handler(p_ble_dis_c, &ble_dis_c_evt);
NRF_LOG_ERROR("Read request failed: 0x%04X.", p_ble_evt->evt.gattc_evt.gatt_status);
}
}
}
/**@brief Function for handling Disconnected event received from the SoftDevice.
*
* @details This function checks if the disconnect event is happening on the link
* associated with the current instance of the module. If so, it will set its
* conn_handle to invalid.
*
* @param[in] p_ble_dis_c Pointer to the Device Information Client Structure.
* @param[in] p_ble_evt Pointer to the BLE event received.
*/
static void on_disconnected(ble_dis_c_t * p_ble_dis_c, const ble_evt_t * p_ble_evt)
{
if (p_ble_dis_c->conn_handle == p_ble_evt->evt.gap_evt.conn_handle)
{
p_ble_dis_c->conn_handle = BLE_CONN_HANDLE_INVALID;
if (p_ble_dis_c->evt_handler != NULL)
{
ble_dis_c_evt_t dis_c_evt =
{
.evt_type = BLE_DIS_C_EVT_DISCONNECTED,
.conn_handle = p_ble_evt->evt.gap_evt.conn_handle
};
p_ble_dis_c->evt_handler(p_ble_dis_c, &dis_c_evt);
}
}
}
ret_code_t ble_dis_c_init(ble_dis_c_t * p_ble_dis_c, ble_dis_c_init_t * p_ble_dis_c_init)
{
ble_uuid_t dis_uuid;
VERIFY_PARAM_NOT_NULL(p_ble_dis_c);
VERIFY_PARAM_NOT_NULL(p_ble_dis_c_init);
dis_uuid.type = BLE_UUID_TYPE_BLE;
dis_uuid.uuid = BLE_UUID_DEVICE_INFORMATION_SERVICE;
p_ble_dis_c->conn_handle = BLE_CONN_HANDLE_INVALID;
p_ble_dis_c->p_gatt_queue = p_ble_dis_c_init->p_gatt_queue;
p_ble_dis_c->evt_handler = p_ble_dis_c_init->evt_handler;
p_ble_dis_c->error_handler = p_ble_dis_c_init->error_handler;
memset(p_ble_dis_c->handles, BLE_GATT_HANDLE_INVALID, sizeof(p_ble_dis_c->handles));
// Enable only selected characteristics if characteristic group is defined.
if (p_ble_dis_c_init->char_group.p_char_type != NULL)
{
p_ble_dis_c->char_mask = BLE_DIS_C_ALL_CHARS_DISABLED_MASK;
for (uint8_t i = 0; i < p_ble_dis_c_init->char_group.size; i++)
{
nrf_bitmask_bit_set(p_ble_dis_c_init->char_group.p_char_type[i],
&p_ble_dis_c->char_mask);
}
}
else
{
p_ble_dis_c->char_mask = BLE_DIS_C_ALL_CHARS_ENABLED_MASK;
}
return ble_db_discovery_evt_register(&dis_uuid);
}
void ble_dis_c_on_db_disc_evt(ble_dis_c_t * p_ble_dis_c, ble_db_discovery_evt_t * p_evt)
{
ble_gatt_db_char_t * p_chars = p_evt->params.discovered_db.charateristics;
ble_dis_c_evt_t ble_dis_c_evt;
// Check if the service discovery is necessary for the link and if the event handler is present.
if ((p_ble_dis_c->evt_handler == NULL) ||
(p_ble_dis_c->conn_handle == p_evt->conn_handle))
{
return;
}
// Check if the DIS was discovered.
if ((p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE) &&
(p_evt->params.discovered_db.srv_uuid.uuid == BLE_UUID_DEVICE_INFORMATION_SERVICE) &&
(p_evt->params.discovered_db.srv_uuid.type == BLE_UUID_TYPE_BLE))
{
memset(&ble_dis_c_evt, 0, sizeof(ble_dis_c_evt_t));
ble_dis_c_evt.evt_type = BLE_DIS_C_EVT_DISCOVERY_COMPLETE;
ble_dis_c_evt.conn_handle = p_evt->conn_handle;
for (uint32_t i = 0; i < p_evt->params.discovered_db.char_count; i++)
{
switch (p_chars[i].characteristic.uuid.uuid)
{
case BLE_UUID_MANUFACTURER_NAME_STRING_CHAR:
ble_dis_c_evt.params.disc_complete.handles[BLE_DIS_C_MANUF_NAME] =
p_chars[i].characteristic.handle_value;
break;
case BLE_UUID_MODEL_NUMBER_STRING_CHAR:
ble_dis_c_evt.params.disc_complete.handles[BLE_DIS_C_MODEL_NUM] =
p_chars[i].characteristic.handle_value;
break;
case BLE_UUID_SERIAL_NUMBER_STRING_CHAR:
ble_dis_c_evt.params.disc_complete.handles[BLE_DIS_C_SERIAL_NUM] =
p_chars[i].characteristic.handle_value;
break;
case BLE_UUID_HARDWARE_REVISION_STRING_CHAR:
ble_dis_c_evt.params.disc_complete.handles[BLE_DIS_C_HW_REV] =
p_chars[i].characteristic.handle_value;
break;
case BLE_UUID_FIRMWARE_REVISION_STRING_CHAR:
ble_dis_c_evt.params.disc_complete.handles[BLE_DIS_C_FW_REV] =
p_chars[i].characteristic.handle_value;
break;
case BLE_UUID_SOFTWARE_REVISION_STRING_CHAR:
ble_dis_c_evt.params.disc_complete.handles[BLE_DIS_C_SW_REV] =
p_chars[i].characteristic.handle_value;
break;
case BLE_UUID_SYSTEM_ID_CHAR:
ble_dis_c_evt.params.disc_complete.handles[BLE_DIS_C_SYS_ID] =
p_chars[i].characteristic.handle_value;
break;
case BLE_UUID_IEEE_REGULATORY_CERTIFICATION_DATA_LIST_CHAR:
ble_dis_c_evt.params.disc_complete.handles[BLE_DIS_C_CERT_LIST] =
p_chars[i].characteristic.handle_value;
break;
case BLE_UUID_PNP_ID_CHAR:
ble_dis_c_evt.params.disc_complete.handles[BLE_DIS_C_PNP_ID] =
p_chars[i].characteristic.handle_value;
break;
default:
break;
}
}
// Forget handle values for disabled characteristics
for (ble_dis_c_char_type_t char_type = (ble_dis_c_char_type_t) 0;
char_type < BLE_DIS_C_CHAR_TYPES_NUM;
char_type++)
{
if (!nrf_bitmask_bit_is_set(char_type, &p_ble_dis_c->char_mask))
{
ble_dis_c_evt.params.disc_complete.handles[char_type] = BLE_GATT_HANDLE_INVALID;
}
}
p_ble_dis_c->evt_handler(p_ble_dis_c, &ble_dis_c_evt);
}
}
void ble_dis_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
ble_dis_c_t * p_ble_dis_c = (ble_dis_c_t *) p_context;
if ((p_ble_dis_c == NULL) || (p_ble_evt == NULL))
{
return;
}
if (p_ble_dis_c->conn_handle == BLE_CONN_HANDLE_INVALID)
{
return;
}
switch (p_ble_evt->header.evt_id)
{
case BLE_GATTC_EVT_READ_RSP:
on_read_rsp(p_ble_dis_c, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnected(p_ble_dis_c, p_ble_evt);
break;
default:
// No implementation needed.
break;
}
}
ret_code_t ble_dis_c_read(ble_dis_c_t * p_ble_dis_c, ble_dis_c_char_type_t char_type)
{
ret_code_t err_code;
nrf_ble_gq_req_t dis_c_req;
VERIFY_PARAM_NOT_NULL(p_ble_dis_c);
VERIFY_TRUE(char_type < BLE_DIS_C_CHAR_TYPES_NUM, NRF_ERROR_INVALID_PARAM);
if ((p_ble_dis_c->conn_handle == BLE_CONN_HANDLE_INVALID) ||
(p_ble_dis_c->handles[char_type] == BLE_GATT_HANDLE_INVALID))
{
return NRF_ERROR_INVALID_STATE;
}
memset(&dis_c_req, 0, sizeof(dis_c_req));
dis_c_req.type = NRF_BLE_GQ_REQ_GATTC_READ;
dis_c_req.error_handler.cb = gatt_error_handler;
dis_c_req.error_handler.p_ctx = p_ble_dis_c;
dis_c_req.params.gattc_read.handle = p_ble_dis_c->handles[char_type];
err_code = nrf_ble_gq_item_add(p_ble_dis_c->p_gatt_queue, &dis_c_req, p_ble_dis_c->conn_handle);
return err_code;
}
ret_code_t ble_dis_c_handles_assign(ble_dis_c_t * p_ble_dis_c,
uint16_t conn_handle,
ble_dis_c_handle_t const * p_peer_handles)
{
VERIFY_PARAM_NOT_NULL(p_ble_dis_c);
p_ble_dis_c->conn_handle = conn_handle;
if (p_peer_handles != NULL)
{
memcpy(p_ble_dis_c->handles, p_peer_handles, sizeof(p_ble_dis_c->handles));
}
else
{
memset(p_ble_dis_c->handles, BLE_GATT_HANDLE_INVALID, sizeof(p_ble_dis_c->handles));
}
return nrf_ble_gq_conn_handle_register(p_ble_dis_c->p_gatt_queue, conn_handle);
}
#endif // NRF_MODULE_ENABLED(BLE_DIS_C)

View File

@@ -0,0 +1,311 @@
/**
* Copyright (c) 2017 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**@file
*
* @defgroup ble_dis_c Device Information Service Client
* @{
* @ingroup ble_sdk_srv
* @brief Device information Service Client module.
*
* @details This module contains the APIs and types exposed by the Device information Service Client
* module. These APIs and types can be used by the application to perform discovery of
* the Device information Service at the peer and interact with it.
*
* @note The application must register this module as BLE event observer using the
* NRF_SDH_BLE_OBSERVER macro. Example:
* @code
* ble_dis_c_t instance;
* NRF_SDH_BLE_OBSERVER(anything, BLE_DIS_C_BLE_OBSERVER_PRIO,
* ble_dis_c_on_ble_evt, &instance);
* @endcode
*
*/
#ifndef BLE_DIS_C_H__
#define BLE_DIS_C_H__
#include <stdint.h>
#include <stdbool.h>
#include "ble.h"
#include "ble_gatt.h"
#include "ble_db_discovery.h"
#include "ble_srv_common.h"
#include "ble_dis.h"
#include "nrf_ble_gq.h"
#include "nrf_sdh_ble.h"
#include "sdk_config.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Macro for defining a ble_dis_c instance.
*
* @param _name Name of the instance.
* @hideinitializer
*/
#define BLE_DIS_C_DEF(_name) \
static ble_dis_c_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_DIS_C_BLE_OBSERVER_PRIO, \
ble_dis_c_on_ble_evt, &_name)
/** @brief Macro for defining multiple ble_dis_c instances.
*
* @param _name Name of the array of instances.
* @param _cnt Number of instances to define.
* @hideinitializer
*/
#define BLE_DIS_C_ARRAY_DEF(_name, _cnt) \
static ble_dis_c_t _name[_cnt]; \
NRF_SDH_BLE_OBSERVERS(_name ## _obs, \
BLE_DIS_C_BLE_OBSERVER_PRIO, \
ble_dis_c_on_ble_evt, &_name, _cnt)
/**@brief DIS Client event type. */
typedef enum
{
BLE_DIS_C_EVT_DISCOVERY_COMPLETE, /**< Event indicating that the DIS and its characteristics were discovered. See @ref ble_dis_c_evt_disc_complete_t. */
BLE_DIS_C_EVT_DIS_C_READ_RSP, /**< Event indicating that the client has received a read response from a peer. See @ref ble_dis_c_evt_read_rsp_t. */
BLE_DIS_C_EVT_DIS_C_READ_RSP_ERROR, /**< Event indicating that the client's read request has failed. See @ref ble_dis_c_evt_read_rsp_err_t. */
BLE_DIS_C_EVT_DISCONNECTED /**< Event indicating that the DIS server has disconnected. */
} ble_dis_c_evt_type_t;
/**@brief DIS Client characteristic type. */
typedef enum
{
BLE_DIS_C_MANUF_NAME, /**< Manufacturer Name String characteristic. */
BLE_DIS_C_MODEL_NUM, /**< Model Number String characteristic. */
BLE_DIS_C_SERIAL_NUM, /**< Serial Number String characteristic. */
BLE_DIS_C_HW_REV, /**< Hardware Revision String characteristic. */
BLE_DIS_C_FW_REV, /**< Firmware Revision String characteristic. */
BLE_DIS_C_SW_REV, /**< Software Revision String characteristic. */
BLE_DIS_C_SYS_ID, /**< System ID characteristic. */
BLE_DIS_C_CERT_LIST, /**< IEEE 11073-20601 Regulatory Certification Data List characteristic. */
BLE_DIS_C_PNP_ID, /**< PnP ID characteristic. */
BLE_DIS_C_CHAR_TYPES_NUM /**< Number of all possible characteristic types. */
} ble_dis_c_char_type_t;
/**@brief Attribute handle pointing to DIS characteristics on the connected peer device. */
typedef uint16_t ble_dis_c_handle_t;
/**@brief Event structure for @ref BLE_DIS_C_EVT_DISCOVERY_COMPLETE. */
typedef struct
{
ble_dis_c_handle_t handles[BLE_DIS_C_CHAR_TYPES_NUM]; /**< Handles on the connected peer device needed to interact with it. */
} ble_dis_c_evt_disc_complete_t;
/**@brief Response data for string-based DIS characteristics. */
typedef struct
{
uint8_t * p_data; /**< Pointer to response data. */
uint8_t len; /**< Response data length. */
} ble_dis_c_string_t;
/**@brief Event structure for @ref BLE_DIS_C_EVT_DIS_C_READ_RSP. */
typedef struct
{
ble_dis_c_char_type_t char_type; /**< Characteristic type. */
ble_dis_c_handle_t handle; /**< Attribute handle from the response event. */
union
{
ble_dis_c_string_t string; /**< String-based characteristics response data. Filled when char_type is in the following range: @ref BLE_DIS_C_MANUF_NAME - @ref BLE_DIS_C_SW_REV (inclusive). */
ble_dis_sys_id_t sys_id; /**< System ID characteristic response data. Filled when char_type is @ref BLE_DIS_C_SYS_ID. */
ble_dis_reg_cert_data_list_t cert_list; /**< IEEE 11073-20601 Regulatory Certification Data List characteristic response data. Filled when char_type is @ref BLE_DIS_C_CERT_LIST. */
ble_dis_pnp_id_t pnp_id; /**< PnP ID characteristic response data. Filled when char_type is @ref BLE_DIS_C_PNP_ID. */
} content;
} ble_dis_c_evt_read_rsp_t;
/**@brief Event structure for @ref BLE_DIS_C_EVT_DIS_C_READ_RSP_ERROR. */
typedef struct
{
ble_dis_c_char_type_t char_type; /**< Characteristic type. */
ble_dis_c_handle_t err_handle; /**< Attribute handle from the response event. */
uint16_t gatt_status; /**< GATT status code for the read operation, see @ref BLE_GATT_STATUS_CODES. */
} ble_dis_c_evt_read_rsp_err_t;
/**@brief Structure containing the DIS event data received from the peer. */
typedef struct
{
ble_dis_c_evt_type_t evt_type; /**< Type of the event. */
uint16_t conn_handle; /**< Connection handle on which the @ref ble_dis_c_evt_t event occurred.*/
union
{
ble_dis_c_evt_disc_complete_t disc_complete; /**< Discovery Complete Event Parameters. Filled when evt_type is @ref BLE_DIS_C_EVT_DISCOVERY_COMPLETE. */
ble_dis_c_evt_read_rsp_t read_rsp; /**< Read Response Event Parameters. Filled when evt_type is @ref BLE_DIS_C_EVT_DIS_C_READ_RSP. */
ble_dis_c_evt_read_rsp_err_t read_rsp_err; /**< Read Response Error Event Parameters. Filled when evt_type is @ref BLE_DIS_C_EVT_DIS_C_READ_RSP_ERROR. */
} params;
} ble_dis_c_evt_t;
// Forward declaration of the ble_dis_t type.
typedef struct ble_dis_c_s ble_dis_c_t;
/**@brief Event handler type.
*
* @details This is the type of the event handler that should be provided by the application
* of this module to receive events.
*/
typedef void (* ble_dis_c_evt_handler_t)(ble_dis_c_t * p_ble_dis_c, ble_dis_c_evt_t const * p_evt);
/**@brief DIS Client structure. */
struct ble_dis_c_s
{
uint16_t conn_handle; /**< Handle of the current connection. Set with @ref ble_dis_c_handles_assign when connected. */
uint16_t char_mask; /**< Mask with enabled DIS characteristics.*/
ble_dis_c_handle_t handles[BLE_DIS_C_CHAR_TYPES_NUM]; /**< Handles on the connected peer device needed to interact with it. */
ble_srv_error_handler_t error_handler; /**< Application error handler to be called in case of an error. */
ble_dis_c_evt_handler_t evt_handler; /**< Application event handler to be called when there is an event related to the DIS. */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to BLE GATT Queue instance. */
};
/**@brief Structure describing the group of DIS characteristics with which this module can interact. */
typedef struct
{
ble_dis_c_char_type_t * p_char_type; /**< Pointer to array with selected characteristics. */
uint8_t size; /**< Group size. */
} ble_dis_c_char_group_t;
/**@brief DIS Client initialization structure. */
typedef struct
{
ble_dis_c_char_group_t char_group; /**< Group of DIS characteristics that should be enabled for this module instance. */
ble_srv_error_handler_t error_handler; /**< Application error handler to be called in case of an error. */
ble_dis_c_evt_handler_t evt_handler; /**< Application event handler to be called when there is an event related to the Device Information service. */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to BLE GATT Queue instance. */
} ble_dis_c_init_t;
/**@brief Function for initializing the Device Information service client module.
*
* @details This function registers with the Database Discovery module
* for the DIS. Doing so will make the Database Discovery
* module look for the presence of a DIS instance at the peer when a
* discovery is started.
*
* @param[in] p_ble_dis_c Pointer to the DIS client structure.
* @param[in] p_ble_dis_c_init Pointer to the DIS initialization structure containing the
* initialization information.
*
* @retval NRF_SUCCESS If the module was initialized successfully.
* @retval NRF_ERROR_NULL Any parameter is NULL.
* @return If functions from other modules return errors to this function
* (@ref ble_db_discovery_evt_register), the @ref nrf_error are propagated.
*/
ret_code_t ble_dis_c_init(ble_dis_c_t * p_ble_dis_c, ble_dis_c_init_t * p_ble_dis_c_init);
/**@brief Function for handling events from the database discovery module.
*
* @details This function will handle an event from the database discovery module, and determine
* if it relates to the discovery of DIS at the peer. If so, it will
* call the application's event handler indicating that DIS has been
* discovered at the peer. It also populates the event with the service related
* information before providing it to the application.
*
* @param[in] p_ble_dis_c Pointer to the DIS client structure.
* @param[in] p_evt Pointer to the event received from the database discovery module.
*/
void ble_dis_c_on_db_disc_evt(ble_dis_c_t * p_ble_dis_c, ble_db_discovery_evt_t * p_evt);
/**@brief Function for handling BLE events from the SoftDevice.
*
* @details This function handles the BLE events received from the SoftDevice. If a BLE
* event is relevant to the DIS module, it is used to update internal variables
* and, if necessary, send events to the application.
*
* @param[in] p_ble_evt Pointer to the BLE event.
* @param[in] p_context Pointer to the DIS client structure.
*/
void ble_dis_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for reading different characteristics from DIS.
*
* @details This function can be used to read different characteristics that are available
* inside DIS. The response data will be provided from the response event
* @ref BLE_DIS_C_EVT_DIS_C_READ_RSP. The @ref BLE_DIS_C_EVT_DIS_C_READ_RSP_ERROR
* event can be generated if the read operation is unsuccessful.
*
* @param[in] p_ble_dis_c Pointer to the DIS client structure.
* @param[in] char_type Type of characteristic to read.
*
* @retval NRF_SUCCESS If the operation was successful.
* @retval NRF_ERROR_NULL If a \p p_ble_dis_c was a NULL pointer.
* @retval NRF_ERROR_INVALID_PARAM If a \p char_type is not valid.
* @retval NRF_ERROR_INVALID_STATE If connection handle or attribute handle is invalid.
* @retval NRF_ERROR_NO_MEM If the client request queue is full.
*/
ret_code_t ble_dis_c_read(ble_dis_c_t * p_ble_dis_c, ble_dis_c_char_type_t char_type);
/**@brief Function for assigning handles to this instance of dis_c.
*
* @details Call this function when a link has been established with a peer to
* associate this link to this instance of the module. This makes it
* possible to handle several links and associate each link to a particular
* instance of this module. The connection handle and attribute handles will be
* provided from the discovery event @ref BLE_DIS_C_EVT_DISCOVERY_COMPLETE.
*
* @param[in] p_ble_dis_c Pointer to the DIS client structure instance to associate with these
* handles.
* @param[in] conn_handle Connection handle associated with the given DIS Instance.
* @param[in] p_peer_handles Attribute handles on the DIS server that you want this DIS client to
* interact with.
*
* @retval NRF_SUCCESS If the operation was successful.
* @retval NRF_ERROR_NULL If a \p p_ble_dis_c was a NULL pointer.
*/
ret_code_t ble_dis_c_handles_assign(ble_dis_c_t * p_ble_dis_c,
uint16_t conn_handle,
ble_dis_c_handle_t const * p_peer_handles);
#ifdef __cplusplus
}
#endif
#endif // BLE_DIS_C_H__
/** @} */

View File

@@ -0,0 +1,135 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef ESCS_DEFS_H__
#define ESCS_DEFS_H__
#include "es.h"
/*@file Contains definitions specific to the Eddystone Configuration Service */
#define ESCS_LOCK_STATE_NEW_LOCK_CODE_WRITE_LENGTH 17
#define ESCS_UID_READ_LENGTH (ES_UID_LENGTH)
#define ESCS_UID_WRITE_LENGTH (ES_UID_NAMESPACE_LENGTH + \
ES_UID_INSTANCE_LENGTH + ES_FRAME_TYPE_LENGTH)
#define ESCS_TLM_READ_LENGTH (ESCS_TLM_READ_LENGTH)
#define ESCS_TLM_WRITE_LENGTH (ES_FRAME_TYPE_LENGTH)
#define ESCS_EID_READ_LENGTH (14)
#define ESCS_EID_WRITE_ECDH_LENGTH (34)
#define ESCS_EID_WRITE_PUB_KEY_INDEX (1)
#define ESCS_EID_WRITE_ENC_ID_KEY_INDEX (1)
#define ESCS_EID_WRITE_IDK_LENGTH (18)
#define ESCS_LOCK_STATE_READ_LENGTH (1)
#define ESCS_URL_MIN_WRITE_LENGTH (4)
#define ESCS_URL_WRITE_LENGTH (19)
#ifdef NRF52_SERIES
#define ESCS_NUM_OF_SUPPORTED_TX_POWER (9)
/**@brief TX power levels, based on nRF52 specifications. */
#define ESCS_SUPPORTED_TX_POWER {-40, -20, -16, -12, -8, -4, 0, 3, 4}
#elif NRF51
/**@brief TX power levels, based on nRF51 specifications. */
#define ESCS_NUM_OF_SUPPORTED_TX_POWER (8)
#define ESCS_SUPPORTED_TX_POWER {-30, -20, -16, -12, -8, -4, 0, 4}
#else
#error MISSING TX POWER
#endif
// Defined in Eddystone Specifications
#define ESCS_AES_KEY_SIZE (16)
#define ESCS_ECDH_KEY_SIZE (32)
#define ESCS_ADV_SLOT_CHAR_LENGTH_MAX (34) // Corresponds to when the slots is configured as an EID slot
// Characteristic: Broadcast Capabilities
// Field: nrf_ble_escs_init_params_t.broadcast_cap.cap_bitfield
#define ESCS_BROADCAST_VAR_ADV_SUPPORTED_Yes (1) // Set if the beacon supports individual per-slot adv intervals
#define ESCS_BROADCAST_VAR_ADV_SUPPORTED_No (0)
#define ESCS_BROADCAST_VAR_ADV_SUPPORTED_Pos (0)
#define ESCS_BROADCAST_VAR_ADV_SUPPORTED_Msk (1 << ESCS_BROADCAST_VAR_ADV_SUPPORTED_Pos)
#define ESCS_BROADCAST_VAR_TX_POWER_SUPPORTED_Yes (1) // Set if the beacon supports individual per-slot TX intervals
#define ESCS_BROADCAST_VAR_TX_POWER_SUPPORTED_No (0)
#define ESCS_BROADCAST_VAR_TX_POWER_SUPPORTED_Pos (1)
#define ESCS_BROADCAST_VAR_TX_POWER_SUPPORTED_Msk (1 << ESCS_BROADCAST_VAR_TX_POWER_SUPPORTED_Pos)
#define ESCS_BROADCAST_VAR_RFU_MASK (0x03) // AND Mask to guarantee that bits 0x04 to 0x80 (RFU) are cleared
// Field: nrf_ble_escs_init_params_t.broadcast_cap.supp_frame_types
#define ESCS_FRAME_TYPE_UID_SUPPORTED_Yes (1)
#define ESCS_FRAME_TYPE_UID_SUPPORTED_No (0)
#define ESCS_FRAME_TYPE_UID_SUPPORTED_Pos (0)
#define ESCS_FRAME_TYPE_UID_SUPPORTED_Msk (1 << ESCS_FRAME_TYPE_UID_SUPPORTED_Pos)
#define ESCS_FRAME_TYPE_URL_SUPPORTED_Yes (1)
#define ESCS_FRAME_TYPE_URL_SUPPORTED_No (0)
#define ESCS_FRAME_TYPE_URL_SUPPORTED_Pos (1)
#define ESCS_FRAME_TYPE_URL_SUPPORTED_Msk (1 << ESCS_FRAME_TYPE_URL_SUPPORTED_Pos)
#define ESCS_FRAME_TYPE_TLM_SUPPORTED_Yes (1)
#define ESCS_FRAME_TYPE_TLM_SUPPORTED_No (0)
#define ESCS_FRAME_TYPE_TLM_SUPPORTED_Pos (2)
#define ESCS_FRAME_TYPE_TLM_SUPPORTED_Msk (1 << ESCS_FRAME_TYPE_TLM_SUPPORTED_Pos)
#define ESCS_FRAME_TYPE_EID_SUPPORTED_Yes (1)
#define ESCS_FRAME_TYPE_EID_SUPPORTED_No (0)
#define ESCS_FRAME_TYPE_EID_SUPPORTED_Pos (3)
#define ESCS_FRAME_TYPE_EID_SUPPORTED_Msk (1 << ESCS_FRAME_TYPE_EID_SUPPORTED_Pos)
#define ESCS_FRAME_TYPE_RFU_MASK (0x000F) // AND Mask to guarantee that bits 0x0010 to 0x8000 (RFU) are cleared
// Characteristic: Lock State: Lock State (READ)
#define ESCS_LOCK_STATE_LOCKED (0x00)
#define ESCS_LOCK_STATE_UNLOCKED (0x01)
#define ESCS_LOCK_STATE_UNLOCKED_AUTO_RELOCK_DISABLED (0x02)
// Characteristic: Lock State: Lock Byte (WRITE)
#define ESCS_LOCK_BYTE_LOCK (0x00)
#define ESCS_LOCK_BYTE_DISABLE_AUTO_RELOCK (0x02)
// Charcteristic: Remain Connectable
#define ESCS_FUNCT_REMAIN_CONNECTABLE_SUPPORTED_Yes (0x01)
#define ESCS_FUNCT_REMAIN_CONNECTABLE_SUPPORTED_No (0x00)
#endif // ESCS_DEFS_H__

View File

@@ -0,0 +1,649 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "nrf_ble_escs.h"
#include <string.h>
#include "es_app_config.h"
#ifdef BLE_HANDLER_DEBUG
#include "SEGGER_RTT.h"
#define DEBUG_PRINTF SEGGER_RTT_printf
#else
#define DEBUG_PRINTF(...)
#endif
#define EID_BUFF_SIZE 64
typedef struct
{
uint16_t val_handle;
uint16_t uuid;
} val_handle_to_uuid_t;
static ble_add_char_params_t BROADCAST_CAP_CHAR_INIT =
{
.uuid = BLE_UUID_ESCS_BROADCAST_CAP_CHAR,
.read_access = SEC_OPEN,
.char_props =
{
.read = 1,
},
.is_defered_read = true,
.is_var_len = true,
.init_len = NRF_BLE_ESCS_BROADCAST_CAP_LEN,
.max_len = NRF_BLE_ESCS_BROADCAST_CAP_LEN,
};
static ble_add_char_params_t ACTIVE_SLOT_CHAR_INIT =
{
.uuid = BLE_UUID_ESCS_ACTIVE_SLOT_CHAR,
.read_access = SEC_OPEN,
.write_access = SEC_OPEN,
.char_props =
{
.read = 1,
.write = 1,
},
.is_defered_read = true,
.is_defered_write = true,
.is_value_user = true,
.init_len = sizeof(nrf_ble_escs_active_slot_t),
.max_len = sizeof(nrf_ble_escs_active_slot_t),
};
static ble_add_char_params_t ADV_INTERVAL_CHAR_INIT =
{
.uuid = BLE_UUID_ESCS_ADV_INTERVAL_CHAR,
.read_access = SEC_OPEN,
.write_access = SEC_OPEN,
.char_props =
{
.read = 1,
.write = 1,
},
.is_defered_read = true,
.is_defered_write = true,
.init_len = sizeof(nrf_ble_escs_adv_interval_t),
.max_len = sizeof(nrf_ble_escs_adv_interval_t),
};
static ble_add_char_params_t RADIO_TX_PWR_CHAR_INIT =
{
.uuid = BLE_UUID_ESCS_RADIO_TX_PWR_CHAR,
.read_access = SEC_OPEN,
.write_access = SEC_OPEN,
.char_props =
{
.read = 1,
.write = 1,
},
.is_defered_read = true,
.is_defered_write = true,
.init_len = sizeof(nrf_ble_escs_radio_tx_pwr_t),
.max_len = sizeof(nrf_ble_escs_radio_tx_pwr_t),
};
static ble_add_char_params_t ADV_TX_PWR_CHAR_INIT =
{
.uuid = BLE_UUID_ESCS_ADV_TX_PWR_CHAR,
.read_access = SEC_OPEN,
.write_access = SEC_OPEN,
.char_props =
{
.read = 1,
.write = 1,
},
.is_defered_read = true,
.is_defered_write = true,
.init_len = sizeof(nrf_ble_escs_adv_tx_pwr_t),
.max_len = sizeof(nrf_ble_escs_adv_tx_pwr_t),
};
static ble_add_char_params_t LOCK_STATE_CHAR_INIT =
{
.uuid = BLE_UUID_ESCS_LOCK_STATE_CHAR,
.read_access = SEC_OPEN,
.write_access = SEC_OPEN,
.char_props =
{
.read = 1,
.write = 1,
},
.is_defered_read = true,
.is_defered_write = true,
.is_var_len = true,
.is_value_user = true,
.init_len = 1,
.max_len = 17,
};
static ble_add_char_params_t UNLOCK_CHAR_INIT =
{
.uuid = BLE_UUID_ESCS_UNLOCK_CHAR,
.read_access = SEC_OPEN,
.write_access = SEC_OPEN,
.char_props =
{
.read = 1,
.write = 1,
},
.is_defered_read = true,
.is_defered_write = true,
.init_len = 1,
.max_len = ESCS_AES_KEY_SIZE,
};
static ble_add_char_params_t PUBLIC_ECDH_KEY_CHAR_INIT =
{
.uuid = BLE_UUID_ESCS_PUBLIC_ECDH_KEY_CHAR,
.read_access = SEC_OPEN,
.char_props =
{
.read = 1,
},
.is_defered_read = true,
.is_var_len = true,
.init_len = 1,
.max_len = ESCS_ECDH_KEY_SIZE,
};
static ble_add_char_params_t EID_ID_KEY_CHAR_INIT =
{
.uuid = BLE_UUID_ESCS_EID_ID_KEY_CHAR,
.read_access = SEC_OPEN,
.char_props =
{
.read = 1,
},
.is_defered_read = true,
.is_var_len = true,
.init_len = 1,
.max_len = ESCS_AES_KEY_SIZE,
};
static ble_add_char_params_t RW_ADV_SLOT_CHAR_INIT =
{
.uuid = BLE_UUID_ESCS_RW_ADV_SLOT_CHAR,
.read_access = SEC_OPEN,
.write_access = SEC_OPEN,
.char_props =
{
.read = 1,
.write = 1,
},
.is_defered_read = true,
.is_defered_write = true,
.is_var_len = true,
.max_len = ESCS_ADV_SLOT_CHAR_LENGTH_MAX,
};
static ble_add_char_params_t FACTORY_RESET_CHAR_INIT =
{
.uuid = BLE_UUID_ESCS_FACTORY_RESET_CHAR,
.write_access = SEC_OPEN,
.char_props =
{
.write = 1,
},
.is_defered_write = true,
.init_len = sizeof(nrf_ble_escs_factory_reset_t),
.max_len = sizeof(nrf_ble_escs_factory_reset_t),
};
static ble_add_char_params_t REMAIN_CONNECTABLE_CHAR_INIT =
{
.uuid = BLE_UUID_ESCS_REMAIN_CONNECTABLE_CHAR,
.read_access = SEC_OPEN,
.write_access = SEC_OPEN,
.char_props =
{
.read = 1,
.write = 1,
},
.is_defered_read = true,
.is_defered_write = true,
.init_len = 1,
.max_len = 1,
};
static val_handle_to_uuid_t m_handle_to_uuid_map[BLE_ESCS_NUMBER_OF_CHARACTERISTICS]; //!< Map from handle to UUID.
static uint8_t m_handle_to_uuid_map_idx = 0; //!< Index of map from handle to UUID.
static uint8_t m_eid_mem[EID_BUFF_SIZE] = {0}; //!< Memory buffer used for EID writes.
static ble_user_mem_block_t m_eid_mem_block =
{
.p_mem = m_eid_mem,
.len = EID_BUFF_SIZE
}; //!< Memory block used for EID writes.
/**@brief Function for adding characteristic to Eddystone service.
*
* @param[in] p_escs Eddystone Configuration Service structure.
* @param[in] p_escs_init Information needed to initialize the service.
* @param[in] p_char_init Information needed to initialize the characteristic.
*
* @return NRF_SUCCESS on success, otherwise an error code.
*/
static uint32_t char_add(ble_add_char_params_t * p_char_init,
nrf_ble_escs_t * p_escs,
void * p_value,
ble_gatts_char_handles_t * p_handles)
{
uint32_t err_code;
VERIFY_PARAM_NOT_NULL(p_char_init);
VERIFY_PARAM_NOT_NULL(p_escs);
VERIFY_PARAM_NOT_NULL(p_value);
VERIFY_PARAM_NOT_NULL(p_handles);
p_char_init->uuid_type = p_escs->uuid_type;
p_char_init->p_init_value = p_value;
err_code = characteristic_add(p_escs->service_handle,
p_char_init,
p_handles);
if (err_code == NRF_SUCCESS)
{
ASSERT(m_handle_to_uuid_map_idx < BLE_ESCS_NUMBER_OF_CHARACTERISTICS);
m_handle_to_uuid_map[m_handle_to_uuid_map_idx].val_handle = p_handles->value_handle;
m_handle_to_uuid_map[m_handle_to_uuid_map_idx].uuid = p_char_init->uuid;
m_handle_to_uuid_map_idx++;
}
return err_code;
}
/**@brief Function for handling the @ref BLE_GAP_EVT_CONNECTED event from the SoftDevice.
*
* @param[in] p_escs Eddystone Configuration Service structure.
* @param[in] p_ble_evt Pointer to the event received from BLE stack.
*/
static void on_connect(nrf_ble_escs_t * p_escs, ble_evt_t const * p_ble_evt)
{
VERIFY_PARAM_NOT_NULL_VOID(p_escs);
p_escs->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
}
/**@brief Function for handling the @ref BLE_GAP_EVT_DISCONNECTED event from the SoftDevice.
*
* @param[in] p_escs Eddystone Configuration Service structure.
* @param[in] p_ble_evt Pointer to the event received from BLE stack.
*/
static void on_disconnect(nrf_ble_escs_t * p_escs, ble_evt_t const * p_ble_evt)
{
VERIFY_PARAM_NOT_NULL_VOID(p_escs);
UNUSED_PARAMETER(p_ble_evt);
p_escs->conn_handle = BLE_CONN_HANDLE_INVALID;
}
static uint32_t get_evt_type_for_handle(uint16_t handle, uint16_t * p_uuid)
{
VERIFY_PARAM_NOT_NULL(p_uuid);
for (uint8_t i = 0; i < BLE_ESCS_NUMBER_OF_CHARACTERISTICS; ++i)
{
if (m_handle_to_uuid_map[i].val_handle == handle)
{
*p_uuid = m_handle_to_uuid_map[i].uuid;
return NRF_SUCCESS;
}
}
return NRF_ERROR_NOT_FOUND;
}
/**@brief Function for handling the @ref BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST: BLE_GATTS_AUTHORIZE_TYPE_WRITE event from the SoftDevice.
*
* @param[in] p_escs Eddystone Configuration Service structure.
* @param[in] p_ble_evt Pointer to the event received from BLE stack.
*/
static ret_code_t on_write(nrf_ble_escs_t * p_escs, ble_evt_t const * p_ble_evt)
{
uint32_t err_code;
uint16_t write_evt_uuid = 0;
VERIFY_PARAM_NOT_NULL(p_escs);
VERIFY_PARAM_NOT_NULL(p_ble_evt);
ble_gatts_evt_write_t const * p_evt_write =
&p_ble_evt->evt.gatts_evt.params.authorize_request.request.write;
err_code = get_evt_type_for_handle(p_evt_write->handle, &write_evt_uuid);
RETURN_IF_ERROR(err_code);
p_escs->write_evt_handler(p_escs,
write_evt_uuid,
p_evt_write->handle,
p_evt_write->data,
p_evt_write->len);
return NRF_SUCCESS;
}
/**@brief Function for handling the @ref BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST: BLE_GATTS_AUTHORIZE_TYPE_WRITE: event from the SoftDevice.
*
* @param[in] p_escs Eddystone Configuration Service structure.
* @param[in] p_ble_evt Pointer to the event received from BLE stack.
*/
static void on_long_write(nrf_ble_escs_t * p_escs, ble_evt_t const * p_ble_evt)
{
static uint16_t write_evt_uuid;
static bool write_evt_uuid_set = false;
uint32_t err_code;
VERIFY_PARAM_NOT_NULL_VOID(p_escs);
VERIFY_PARAM_NOT_NULL_VOID(p_ble_evt);
ble_gatts_evt_write_t const * p_evt_write =
&p_ble_evt->evt.gatts_evt.params.authorize_request.request.write;
ble_gatts_rw_authorize_reply_params_t reply = {0};
if (p_evt_write->op == BLE_GATTS_OP_PREP_WRITE_REQ)
{
err_code = get_evt_type_for_handle(p_evt_write->handle, &write_evt_uuid);
APP_ERROR_CHECK(err_code);
write_evt_uuid_set = true;
reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
reply.params.write.update = 0;
reply.params.write.offset = 0;
reply.params.write.len = p_evt_write->len;
reply.params.write.p_data = NULL;
err_code = sd_ble_gatts_rw_authorize_reply(p_escs->conn_handle, &reply);
APP_ERROR_CHECK(err_code);
}
else if (p_evt_write->op == BLE_GATTS_OP_EXEC_WRITE_REQ_NOW)
{
uint8_t value_buffer[ESCS_ADV_SLOT_CHAR_LENGTH_MAX] = {0};
ble_gatts_value_t value =
{
.len = sizeof(value_buffer),
.offset = 0,
.p_value = &(value_buffer[0])
};
ASSERT(write_evt_uuid_set);
write_evt_uuid_set = false;
reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
reply.params.write.update = 0;
reply.params.write.offset = 0;
reply.params.write.len = p_evt_write->len;
reply.params.write.p_data = NULL;
err_code = sd_ble_gatts_rw_authorize_reply(p_escs->conn_handle, &reply);
APP_ERROR_CHECK(err_code);
// Now that the value has been accepted using 'sd_ble_gatts_rw_authorize_reply', it can be found in the database.
err_code = sd_ble_gatts_value_get( p_escs->conn_handle,
p_escs->rw_adv_slot_handles.value_handle,
&value);
APP_ERROR_CHECK(err_code);
p_escs->write_evt_handler(p_escs,
write_evt_uuid,
p_evt_write->handle,
value.p_value,
value.len);
}
else
{
}
}
/**@brief Function for handling events from the SoftDevice related to long writes.
*
* @param[in] p_escs Eddystone Configuration Service structure.
* @param[in] p_ble_evt Pointer to the event received from BLE stack.
*/
static ret_code_t on_read(nrf_ble_escs_t * p_escs, ble_evt_t const * p_ble_evt)
{
VERIFY_PARAM_NOT_NULL(p_escs);
VERIFY_PARAM_NOT_NULL(p_ble_evt);
ret_code_t err_code;
uint16_t read_evt_uuid = 0;
uint16_t val_handle = p_ble_evt->evt.gatts_evt.params.authorize_request.request.read.handle;
err_code = get_evt_type_for_handle(val_handle, &read_evt_uuid);
RETURN_IF_ERROR(err_code);
p_escs->read_evt_handler(p_escs, read_evt_uuid, val_handle);
return NRF_SUCCESS;
}
static ret_code_t on_rw_authorize_req(nrf_ble_escs_t * p_escs, ble_evt_t const * p_ble_evt)
{
ret_code_t err_code;
VERIFY_PARAM_NOT_NULL(p_escs);
VERIFY_PARAM_NOT_NULL(p_ble_evt);
ble_gatts_evt_rw_authorize_request_t const * ar =
&p_ble_evt->evt.gatts_evt.params.authorize_request;
if (ar->type == BLE_GATTS_AUTHORIZE_TYPE_READ)
{
err_code = on_read(p_escs, p_ble_evt);
RETURN_IF_ERROR(err_code);
}
else if (ar->type == BLE_GATTS_AUTHORIZE_TYPE_WRITE)
{
if (ar->request.write.op == BLE_GATTS_OP_WRITE_REQ
|| ar->request.write.op == BLE_GATTS_OP_WRITE_CMD)
{
err_code = on_write(p_escs, p_ble_evt);
RETURN_IF_ERROR(err_code);
}
else if (ar->request.write.op == BLE_GATTS_OP_PREP_WRITE_REQ
|| ar->request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_NOW)
{
on_long_write(p_escs, p_ble_evt);
}
else if (ar->request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL)
{
ble_gatts_rw_authorize_reply_params_t auth_reply;
memset(&auth_reply, 0, sizeof(auth_reply));
auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
auth_reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
err_code = sd_ble_gatts_rw_authorize_reply(p_ble_evt->evt.gatts_evt.conn_handle, &auth_reply);
VERIFY_SUCCESS(err_code);
}
else
{
}
}
else
{
return NRF_ERROR_INVALID_STATE;
}
return NRF_SUCCESS;
}
ret_code_t nrf_ble_escs_on_ble_evt(nrf_ble_escs_t * p_escs, ble_evt_t const * p_ble_evt)
{
ret_code_t err_code;
VERIFY_PARAM_NOT_NULL(p_escs);
VERIFY_PARAM_NOT_NULL(p_ble_evt);
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
on_connect(p_escs, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnect(p_escs, p_ble_evt);
break;
case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
err_code = on_rw_authorize_req(p_escs, p_ble_evt);
VERIFY_SUCCESS(err_code);
break;
// BLE_EVT_USER_MEM_REQUEST & BLE_EVT_USER_MEM_RELEASE are for long writes to the RW ADV slot characteristic
case BLE_EVT_USER_MEM_REQUEST:
err_code = sd_ble_user_mem_reply(p_escs->conn_handle, &m_eid_mem_block);
VERIFY_SUCCESS(err_code);
break;
case BLE_EVT_USER_MEM_RELEASE:
break;
default:
// No implementation needed.
break;
}
return NRF_SUCCESS;
}
ret_code_t nrf_ble_escs_init(nrf_ble_escs_t * p_escs, const nrf_ble_escs_init_t * p_escs_init)
{
uint32_t err_code;
ble_uuid_t ble_uuid;
ble_uuid128_t ecs_base_uuid = ESCS_BASE_UUID;
uint8_t zero_val = 0;
VERIFY_PARAM_NOT_NULL(p_escs);
VERIFY_PARAM_NOT_NULL(p_escs_init);
// Initialize the service structure.
p_escs->conn_handle = BLE_CONN_HANDLE_INVALID;
p_escs->write_evt_handler = p_escs_init->write_evt_handler;
p_escs->read_evt_handler = p_escs_init->read_evt_handler;
// Add a custom base UUID.
err_code = sd_ble_uuid_vs_add(&ecs_base_uuid, &p_escs->uuid_type);
VERIFY_SUCCESS(err_code);
ble_uuid.type = p_escs->uuid_type;
ble_uuid.uuid = BLE_UUID_ESCS_SERVICE;
// Add the service.
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
&ble_uuid,
&p_escs->service_handle);
VERIFY_SUCCESS(err_code);
m_handle_to_uuid_map_idx = 0;
// Set up initial values for characteristics
// Eddystone spec requires big endian
nrf_ble_escs_broadcast_cap_t temp = p_escs_init->p_init_vals->broadcast_cap;
temp.supp_frame_types = BYTES_SWAP_16BIT(temp.supp_frame_types);
nrf_ble_escs_adv_interval_t temp_interval = p_escs_init->p_init_vals->adv_interval;
temp_interval = BYTES_SWAP_16BIT(temp_interval);
// Adding chracteristics
err_code = char_add(&BROADCAST_CAP_CHAR_INIT, p_escs,
&temp, &p_escs->broadcast_cap_handles);
VERIFY_SUCCESS(err_code);
err_code = char_add(&ACTIVE_SLOT_CHAR_INIT, p_escs,
p_escs->p_active_slot, &p_escs->active_slot_handles);
VERIFY_SUCCESS(err_code);
err_code = char_add(&ADV_INTERVAL_CHAR_INIT, p_escs,
&temp_interval, &p_escs->adv_interval_handles);
VERIFY_SUCCESS(err_code);
err_code = char_add(&RADIO_TX_PWR_CHAR_INIT, p_escs,
&(p_escs_init->p_init_vals->radio_tx_pwr), &p_escs->radio_tx_pwr_handles);
VERIFY_SUCCESS(err_code);
err_code = char_add(&ADV_TX_PWR_CHAR_INIT, p_escs,
&(p_escs_init->p_init_vals->adv_tx_pwr), &p_escs->adv_tx_pwr_handles);
VERIFY_SUCCESS(err_code);
err_code = char_add(&LOCK_STATE_CHAR_INIT, p_escs,
&p_escs->lock_state, &p_escs->lock_state_handles);
VERIFY_SUCCESS(err_code);
err_code = char_add(&UNLOCK_CHAR_INIT, p_escs,
&zero_val, &p_escs->unlock_handles);
VERIFY_SUCCESS(err_code);
err_code = char_add(&PUBLIC_ECDH_KEY_CHAR_INIT, p_escs,
&zero_val, &p_escs->pub_ecdh_key_handles);
VERIFY_SUCCESS(err_code);
err_code = char_add(&EID_ID_KEY_CHAR_INIT, p_escs,
&zero_val, &p_escs->eid_id_key_handles);
VERIFY_SUCCESS(err_code);
err_code = char_add(&RW_ADV_SLOT_CHAR_INIT, p_escs,
&zero_val, &p_escs->rw_adv_slot_handles);
VERIFY_SUCCESS(err_code);
err_code = char_add(&FACTORY_RESET_CHAR_INIT, p_escs,
&(p_escs_init->p_init_vals->factory_reset), &p_escs->factory_reset_handles);
VERIFY_SUCCESS(err_code);
err_code = char_add(&REMAIN_CONNECTABLE_CHAR_INIT, p_escs,
&(p_escs_init->p_init_vals->remain_connectable.r_is_non_connectable_supported),
&p_escs->remain_connectable_handles);
VERIFY_SUCCESS(err_code);
return NRF_SUCCESS;
}

View File

@@ -0,0 +1,259 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef NRF_BLE_ESCS_H__
#define NRF_BLE_ESCS_H__
#include "ble.h"
#include "ble_srv_common.h"
#include "app_util_platform.h"
#include "sdk_common.h"
#include "escs_defs.h"
#include <stdint.h>
#include <stdbool.h>
/**
* @file
* @defgroup nrf_ble_escs Eddystone Configuration Service
* @brief Eddystone Configuration Service module.
* @ingroup ble_sdk_srv
* @{
*/
#define BLE_ESCS_NUMBER_OF_CHARACTERISTICS 13 //!< Number of characteristics contained in the Eddystone Configuration Service.
#define BLE_UUID_ESCS_SERVICE 0x7500 //!< UUID of the Eddystone Configuration Service.
// ECS UUIDs
#define BLE_UUID_ESCS_BROADCAST_CAP_CHAR 0x7501
#define BLE_UUID_ESCS_ACTIVE_SLOT_CHAR 0x7502
#define BLE_UUID_ESCS_ADV_INTERVAL_CHAR 0x7503
#define BLE_UUID_ESCS_RADIO_TX_PWR_CHAR 0x7504
#define BLE_UUID_ESCS_ADV_TX_PWR_CHAR 0x7505
#define BLE_UUID_ESCS_LOCK_STATE_CHAR 0x7506
#define BLE_UUID_ESCS_UNLOCK_CHAR 0x7507
#define BLE_UUID_ESCS_PUBLIC_ECDH_KEY_CHAR 0x7508
#define BLE_UUID_ESCS_EID_ID_KEY_CHAR 0x7509
#define BLE_UUID_ESCS_RW_ADV_SLOT_CHAR 0x750A
#define BLE_UUID_ESCS_FACTORY_RESET_CHAR 0x750B
#define BLE_UUID_ESCS_REMAIN_CONNECTABLE_CHAR 0x750C
#define ESCS_BASE_UUID \
{{0x95, 0xE2, 0xED, 0xEB, 0x1B, 0xA0, 0x39, 0x8A, 0xDF, 0x4B, 0xD3, 0x8E, 0x00, 0x00, 0xC8, \
0xA3}}
// A3C8XXXX-8ED3-4BDF-8A39-A01BEBEDE295
#define NRF_BLE_ESCS_BROADCAST_CAP_LEN (ESCS_NUM_OF_SUPPORTED_TX_POWER + 6) // According to the eddystone spec, there are 6 bytes of data in addition to the supported_radio_tx_power array
/**@brief Data fields in the Broadcast Capabilities characteristic.
* @note This is a packed structure. Therefore, you should not change it.
*/
typedef PACKED_STRUCT
{
int8_t vers_byte;
int8_t max_supp_total_slots;
int8_t max_supp_eid_slots;
int8_t cap_bitfield;
int16_t supp_frame_types;
int8_t supp_radio_tx_power[ESCS_NUM_OF_SUPPORTED_TX_POWER];
} nrf_ble_escs_broadcast_cap_t;
typedef uint8_t nrf_ble_escs_active_slot_t;
typedef uint16_t nrf_ble_escs_adv_interval_t;
typedef int8_t nrf_ble_escs_radio_tx_pwr_t;
typedef int8_t nrf_ble_escs_adv_tx_pwr_t;
/**@brief Read states of the Lock State characteristic. */
typedef enum
{
NRF_BLE_ESCS_LOCK_STATE_LOCKED = ESCS_LOCK_STATE_LOCKED,
NRF_BLE_ESCS_LOCK_STATE_UNLOCKED = ESCS_LOCK_STATE_UNLOCKED,
NRF_BLE_ESCS_LOCK_STATE_UNLOCKED_AUTO_RELOCK_DISABLED =
ESCS_LOCK_STATE_UNLOCKED_AUTO_RELOCK_DISABLED
} nrf_ble_escs_lock_state_read_t;
/**@brief Write bytes of the Lock State characteristic. */
typedef enum
{
NRF_BLE_ESCS_LOCK_BYTE_LOCK = ESCS_LOCK_BYTE_LOCK,
NRF_BLE_ESCS_LOCK_BYTE_DISABLE_AUTO_RELOCK = ESCS_LOCK_BYTE_DISABLE_AUTO_RELOCK
} nrf_ble_escs_lock_byte_t;
/**@brief Write data fields of the Lock State characteristic.
* @note This is a packed structure. Therefore, you should not change it.
*/
typedef PACKED_STRUCT
{
nrf_ble_escs_lock_byte_t lock_byte;
int8_t encrypted_key[ESCS_AES_KEY_SIZE];
} nrf_ble_escs_lock_state_write_t;
/**@brief Lock State characteristic. */
typedef union
{
nrf_ble_escs_lock_state_read_t read;
nrf_ble_escs_lock_state_write_t write;
} nrf_ble_escs_lock_state_t;
/**@brief Unlock characteristic (read/write). */
typedef union
{
int8_t r_challenge[ESCS_AES_KEY_SIZE];
int8_t w_unlock_token[ESCS_AES_KEY_SIZE];
} nrf_ble_escs_unlock_t;
/**@brief Public ECDH Key characteristic.
* @note This is a packed structure. Therefore, you should not change it.
*/
typedef PACKED_STRUCT
{
int8_t key[ESCS_ECDH_KEY_SIZE];
} nrf_ble_escs_public_ecdh_key_t;
/**@brief EID Identity Key characteristic.
* @note This is a packed structure. Therefore, you should not change it.
*/
typedef PACKED_STRUCT
{
int8_t key[ESCS_AES_KEY_SIZE];
} nrf_ble_escs_eid_id_key_t;
typedef uint8_t nrf_ble_escs_factory_reset_t;
/**@brief Unlock characteristic (read/write). */
typedef union
{
uint8_t r_is_non_connectable_supported;
uint8_t w_remain_connectable_boolean;
} nrf_ble_escs_remain_conntbl_t;
/**@brief Eddystone Configuration Service initialization parameters (corresponding to required characteristics). */
typedef struct
{
nrf_ble_escs_broadcast_cap_t broadcast_cap;
nrf_ble_escs_adv_interval_t adv_interval;
nrf_ble_escs_radio_tx_pwr_t radio_tx_pwr;
nrf_ble_escs_adv_tx_pwr_t adv_tx_pwr;
nrf_ble_escs_factory_reset_t factory_reset;
nrf_ble_escs_remain_conntbl_t remain_connectable;
} nrf_ble_escs_init_params_t;
// Forward Declaration of nrf_ble_escs_t type.
typedef struct nrf_ble_escs_s nrf_ble_escs_t;
typedef void (*nrf_ble_escs_write_evt_handler_t)(nrf_ble_escs_t * p_escs,
uint16_t uuid,
uint16_t value_handle,
uint8_t const * p_data,
uint16_t length);
typedef void (*nrf_ble_escs_read_evt_handler_t)(nrf_ble_escs_t * p_escs,
uint16_t uuid,
uint16_t value_handle);
/**@brief Eddystone Configuration Service initialization structure.
*
* @details This structure contains the initialization information for the service. The application
* must fill this structure and pass it to the service using the @ref nrf_ble_escs_init
* function.
*/
typedef struct
{
nrf_ble_escs_init_params_t * p_init_vals; //!< Initialization parameters for the service.
nrf_ble_escs_write_evt_handler_t write_evt_handler; //!< Event handler to be called for authorizing write requests.
nrf_ble_escs_read_evt_handler_t read_evt_handler; //!< Event handler to be called for authorizing read requests.
} nrf_ble_escs_init_t;
struct nrf_ble_escs_s
{
uint8_t uuid_type; //!< UUID type for the Eddystone Configuration Service Base UUID.
uint16_t service_handle; //!< Handle of the Eddystone Configuration Service (as provided by the SoftDevice).
ble_gatts_char_handles_t broadcast_cap_handles; //!< Handles related to the Capabilities characteristic (as provided by the SoftDevice).
ble_gatts_char_handles_t active_slot_handles; //!< Handles related to the Active Slot characteristic (as provided by the SoftDevice).
ble_gatts_char_handles_t adv_interval_handles; //!< Handles related to the Advertising Interval characteristic (as provided by the SoftDevice).
ble_gatts_char_handles_t radio_tx_pwr_handles; //!< Handles related to the Radio Tx Power characteristic (as provided by the SoftDevice).
ble_gatts_char_handles_t adv_tx_pwr_handles; //!< Handles related to the (Advanced) Advertised Tx Power characteristic (as provided by the SoftDevice).
ble_gatts_char_handles_t lock_state_handles; //!< Handles related to the Lock State characteristic (as provided by the SoftDevice).
ble_gatts_char_handles_t unlock_handles; //!< Handles related to the Unlock characteristic (as provided by the SoftDevice).
ble_gatts_char_handles_t pub_ecdh_key_handles; //!< Handles related to the Public ECDH Key characteristic (as provided by the SoftDevice).
ble_gatts_char_handles_t eid_id_key_handles; //!< Handles related to the EID Identity Key characteristic (as provided by the SoftDevice).
ble_gatts_char_handles_t rw_adv_slot_handles; //!< Handles related to the ADV Slot Data characteristic (as provided by the SoftDevice).
ble_gatts_char_handles_t factory_reset_handles; //!< Handles related to the (Advanced) Factory reset characteristic (as provided by the SoftDevice).
ble_gatts_char_handles_t remain_connectable_handles; //!< Handles related to the (Advanced) Remain Connectable characteristic (as provided by the SoftDevice).
uint16_t conn_handle; //!< Handle of the current connection (as provided by the SoftDevice). @ref BLE_CONN_HANDLE_INVALID if not in a connection.
nrf_ble_escs_write_evt_handler_t write_evt_handler; //!< Event handler to be called for handling write attempts.
nrf_ble_escs_read_evt_handler_t read_evt_handler; //!< Event handler to be called for handling read attempts.
uint8_t * p_active_slot;
uint8_t lock_state;
};
/**@brief Function for initializing the Eddystone Configuration Service.
*
* @param[out] p_escs Eddystone Configuration Service structure. This structure must be supplied
* by the application. It is initialized by this function and will
* later be used to identify this particular service instance.
* @param[in] p_ecs_init Information needed to initialize the service.
*
* @retval NRF_SUCCESS If the service was successfully initialized. Otherwise, an error code is returned.
* @retval NRF_ERROR_NULL If either of the pointers @p p_escs or @p p_ecs_init is NULL.
*/
ret_code_t nrf_ble_escs_init(nrf_ble_escs_t * p_escs, const nrf_ble_escs_init_t * p_ecs_init);
/**@brief Function for handling the Eddystone Configuration Service's BLE events.
*
* @details The Eddystone Configuration Service expects the application to call this function each time an
* event is received from the SoftDevice. This function processes the event if it
* is relevant and calls the Eddystone Configuration Service event handler of the
* application if necessary.
*
* @param[in] p_escs Eddystone Configuration Service structure.
* @param[in] p_ble_evt Event received from the SoftDevice.
*
* @retval NRF_ERROR_NULL If any of the arguments given are NULL.
* @retval NRF_SUCCESS otherwise.
*/
ret_code_t nrf_ble_escs_on_ble_evt(nrf_ble_escs_t * p_escs, ble_evt_t const * p_ble_evt);
/** @} */
#endif // NRF_BLE_ESCS_H__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,330 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_gls Glucose Service
* @{
* @ingroup ble_sdk_srv
* @brief Glucose Service module.
*
* @details This module implements the Glucose Service.
*
* @note The application must register this module as BLE event observer using the
* NRF_SDH_BLE_OBSERVER macro. Example:
* @code
* ble_gls_t instance;
* NRF_SDH_BLE_OBSERVER(anything, BLE_GLS_BLE_OBSERVER_PRIO,
* ble_gls_on_ble_evt, &instance);
* @endcode
*
* @note Attention!
* To maintain compliance with Nordic Semiconductor ASA Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#ifndef BLE_GLS_H__
#define BLE_GLS_H__
#include <stdint.h>
#include <stdbool.h>
#include "ble.h"
#include "ble_srv_common.h"
#include "ble_date_time.h"
#include "nrf_ble_gq.h"
#include "nrf_sdh_ble.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Macro for defining a ble_gls instance.
*
* @param _name Name of the instance.
* @hideinitializer
*/
#define BLE_GLS_DEF(_name) \
static ble_gls_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_GLS_BLE_OBSERVER_PRIO, \
ble_gls_on_ble_evt, &_name)
/**@brief Glucose feature */
#define BLE_GLS_FEATURE_LOW_BATT 0x0001 /**< Low Battery Detection During Measurement Supported */
#define BLE_GLS_FEATURE_MALFUNC 0x0002 /**< Sensor Malfunction Detection Supported */
#define BLE_GLS_FEATURE_SAMPLE_SIZE 0x0004 /**< Sensor Sample Size Supported */
#define BLE_GLS_FEATURE_INSERT_ERR 0x0008 /**< Sensor Strip Insertion Error Detection Supported */
#define BLE_GLS_FEATURE_TYPE_ERR 0x0010 /**< Sensor Strip Type Error Detection Supported */
#define BLE_GLS_FEATURE_RES_HIGH_LOW 0x0020 /**< Sensor Result High-Low Detection Supported */
#define BLE_GLS_FEATURE_TEMP_HIGH_LOW 0x0040 /**< Sensor Temperature High-Low Detection Supported */
#define BLE_GLS_FEATURE_READ_INT 0x0080 /**< Sensor Read Interrupt Detection Supported */
#define BLE_GLS_FEATURE_GENERAL_FAULT 0x0100 /**< General Device Fault Supported */
#define BLE_GLS_FEATURE_TIME_FAULT 0x0200 /**< Time Fault Supported */
#define BLE_GLS_FEATURE_MULTI_BOND 0x0400 /**< Multiple Bond Supported */
/**@brief Glucose measurement flags */
#define BLE_GLS_MEAS_FLAG_TIME_OFFSET 0x01 /**< Time Offset Present */
#define BLE_GLS_MEAS_FLAG_CONC_TYPE_LOC 0x02 /**< Glucose Concentration, Type, and Sample Location Present */
#define BLE_GLS_MEAS_FLAG_UNITS_KG_L 0x00 /**< Glucose Concentration Units kg/L */
#define BLE_GLS_MEAS_FLAG_UNITS_MOL_L 0x04 /**< Glucose Concentration Units mol/L */
#define BLE_GLS_MEAS_FLAG_SENSOR_STATUS 0x08 /**< Sensor Status Annunciation Present */
#define BLE_GLS_MEAS_FLAG_CONTEXT_INFO 0x10 /**< Context Information Follows */
/**@brief Glucose measurement type */
#define BLE_GLS_MEAS_TYPE_CAP_BLOOD 1 /**< Capillary whole blood */
#define BLE_GLS_MEAS_TYPE_CAP_PLASMA 2 /**< Capillary plasma */
#define BLE_GLS_MEAS_TYPE_VEN_BLOOD 3 /**< Venous whole blood */
#define BLE_GLS_MEAS_TYPE_VEN_PLASMA 4 /**< Venous plasma */
#define BLE_GLS_MEAS_TYPE_ART_BLOOD 5 /**< Arterial whole blood */
#define BLE_GLS_MEAS_TYPE_ART_PLASMA 6 /**< Arterial plasma */
#define BLE_GLS_MEAS_TYPE_UNDET_BLOOD 7 /**< Undetermined whole blood */
#define BLE_GLS_MEAS_TYPE_UNDET_PLASMA 8 /**< Undetermined plasma */
#define BLE_GLS_MEAS_TYPE_FLUID 9 /**< Interstitial fluid (ISF) */
#define BLE_GLS_MEAS_TYPE_CONTROL 10 /**< Control solution */
/**@brief Glucose measurement location */
#define BLE_GLS_MEAS_LOC_FINGER 1 /**< Finger */
#define BLE_GLS_MEAS_LOC_AST 2 /**< Alternate Site Test (AST) */
#define BLE_GLS_MEAS_LOC_EAR 3 /**< Earlobe */
#define BLE_GLS_MEAS_LOC_CONTROL 4 /**< Control solution */
#define BLE_GLS_MEAS_LOC_NOT_AVAIL 15 /**< Sample Location value not available */
/**@brief Glucose sensor status annunciation */
#define BLE_GLS_MEAS_STATUS_BATT_LOW 0x0001 /**< Device battery low at time of measurement */
#define BLE_GLS_MEAS_STATUS_SENSOR_FAULT 0x0002 /**< Sensor malfunction or faulting at time of measurement */
#define BLE_GLS_MEAS_STATUS_SAMPLE_SIZE 0x0004 /**< Sample size for blood or control solution insufficient at time of measurement */
#define BLE_GLS_MEAS_STATUS_STRIP_INSERT 0x0008 /**< Strip insertion error */
#define BLE_GLS_MEAS_STATUS_STRIP_TYPE 0x0010 /**< Strip type incorrect for device */
#define BLE_GLS_MEAS_STATUS_RESULT_HIGH 0x0020 /**< Sensor result higher than the device can process */
#define BLE_GLS_MEAS_STATUS_RESULT_LOW 0x0040 /**< Sensor result lower than the device can process */
#define BLE_GLS_MEAS_STATUS_TEMP_HIGH 0x0080 /**< Sensor temperature too high for valid test/result at time of measurement */
#define BLE_GLS_MEAS_STATUS_TEMP_LOW 0x0100 /**< Sensor temperature too low for valid test/result at time of measurement */
#define BLE_GLS_MEAS_STATUS_STRIP_PULL 0x0200 /**< Sensor read interrupted because strip was pulled too soon at time of measurement */
#define BLE_GLS_MEAS_STATUS_GENERAL_FAULT 0x0400 /**< General device fault has occurred in the sensor */
#define BLE_GLS_MEAS_STATUS_TIME_FAULT 0x0800 /**< Time fault has occurred in the sensor and time may be inaccurate */
/**@brief Glucose measurement context flags */
#define BLE_GLS_CONTEXT_FLAG_CARB 0x01 /**< Carbohydrate id and carbohydrate present */
#define BLE_GLS_CONTEXT_FLAG_MEAL 0x02 /**< Meal present */
#define BLE_GLS_CONTEXT_FLAG_TESTER 0x04 /**< Tester-health present */
#define BLE_GLS_CONTEXT_FLAG_EXERCISE 0x08 /**< Exercise duration and exercise intensity present */
#define BLE_GLS_CONTEXT_FLAG_MED 0x10 /**< Medication ID and medication present */
#define BLE_GLS_CONTEXT_FLAG_MED_KG 0x00 /**< Medication value units, kilograms */
#define BLE_GLS_CONTEXT_FLAG_MED_L 0x20 /**< Medication value units, liters */
#define BLE_GLS_CONTEXT_FLAG_HBA1C 0x40 /**< Hba1c present */
#define BLE_GLS_CONTEXT_FLAG_EXT 0x80 /**< Extended flags present */
/**@brief Glucose measurement context carbohydrate ID */
#define BLE_GLS_CONTEXT_CARB_BREAKFAST 1 /**< Breakfast */
#define BLE_GLS_CONTEXT_CARB_LUNCH 2 /**< Lunch */
#define BLE_GLS_CONTEXT_CARB_DINNER 3 /**< Dinner */
#define BLE_GLS_CONTEXT_CARB_SNACK 4 /**< Snack */
#define BLE_GLS_CONTEXT_CARB_DRINK 5 /**< Drink */
#define BLE_GLS_CONTEXT_CARB_SUPPER 6 /**< Supper */
#define BLE_GLS_CONTEXT_CARB_BRUNCH 7 /**< Brunch */
/**@brief Glucose measurement context meal */
#define BLE_GLS_CONTEXT_MEAL_PREPRANDIAL 1 /**< Preprandial (before meal) */
#define BLE_GLS_CONTEXT_MEAL_POSTPRANDIAL 2 /**< Postprandial (after meal) */
#define BLE_GLS_CONTEXT_MEAL_FASTING 3 /**< Fasting */
#define BLE_GLS_CONTEXT_MEAL_CASUAL 4 /**< Casual (snacks, drinks, etc.) */
#define BLE_GLS_CONTEXT_MEAL_BEDTIME 5 /**< Bedtime */
/**@brief Glucose measurement context tester */
#define BLE_GLS_CONTEXT_TESTER_SELF 1 /**< Self */
#define BLE_GLS_CONTEXT_TESTER_PRO 2 /**< Health care professional */
#define BLE_GLS_CONTEXT_TESTER_LAB 3 /**< Lab test */
#define BLE_GLS_CONTEXT_TESTER_NOT_AVAIL 15 /**< Tester value not available */
/**@brief Glucose measurement context health */
#define BLE_GLS_CONTEXT_HEALTH_MINOR 1 /**< Minor health issues */
#define BLE_GLS_CONTEXT_HEALTH_MAJOR 2 /**< Major health issues */
#define BLE_GLS_CONTEXT_HEALTH_MENSES 3 /**< During menses */
#define BLE_GLS_CONTEXT_HEALTH_STRESS 4 /**< Under stress */
#define BLE_GLS_CONTEXT_HEALTH_NONE 5 /**< No health issues */
#define BLE_GLS_CONTEXT_HEALTH_NOT_AVAIL 15 /**< Health value not available */
/**@brief Glucose measurement context medication ID */
#define BLE_GLS_CONTEXT_MED_RAPID 1 /**< Rapid acting insulin */
#define BLE_GLS_CONTEXT_MED_SHORT 2 /**< Short acting insulin */
#define BLE_GLS_CONTEXT_MED_INTERMED 3 /**< Intermediate acting insulin */
#define BLE_GLS_CONTEXT_MED_LONG 4 /**< Long acting insulin */
#define BLE_GLS_CONTEXT_MED_PREMIX 5 /**< Pre-mixed insulin */
/**@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 Glucose Service event type. */
typedef enum
{
BLE_GLS_EVT_NOTIFICATION_ENABLED, /**< Glucose value notification enabled event. */
BLE_GLS_EVT_NOTIFICATION_DISABLED /**< Glucose value notification disabled event. */
} ble_gls_evt_type_t;
/**@brief Glucose Service event. */
typedef struct
{
ble_gls_evt_type_t evt_type; /**< Type of event. */
} ble_gls_evt_t;
// Forward declaration of the ble_gls_t type.
typedef struct ble_gls_s ble_gls_t;
/**@brief Glucose Service event handler type. */
typedef void (*ble_gls_evt_handler_t)(ble_gls_t * p_gls, ble_gls_evt_t * p_evt);
/**@brief Glucose Measurement structure. This contains glucose measurement value. */
typedef struct
{
uint8_t flags; /**< Flags */
uint16_t sequence_number; /**< Sequence number */
ble_date_time_t base_time; /**< Time stamp */
int16_t time_offset; /**< Time offset */
sfloat_t glucose_concentration; /**< Glucose concentration */
uint8_t type; /**< Type */
uint8_t sample_location; /**< Sample location */
uint16_t sensor_status_annunciation; /**< Sensor status annunciation */
} ble_gls_meas_t;
/**@brief Glucose measurement context structure */
typedef struct
{
uint8_t flags; /**< Flags */
uint8_t extended_flags; /**< Extended Flags */
uint8_t carbohydrate_id; /**< Carbohydrate ID */
sfloat_t carbohydrate; /**< Carbohydrate */
uint8_t meal; /**< Meal */
uint8_t tester_and_health; /**< Tester and health */
uint16_t exercise_duration; /**< Exercise Duration */
uint8_t exercise_intensity; /**< Exercise Intensity */
uint8_t medication_id; /**< Medication ID */
sfloat_t medication; /**< Medication */
uint16_t hba1c; /**< HbA1c */
} ble_gls_meas_context_t;
/**@brief Glucose measurement record */
typedef struct
{
ble_gls_meas_t meas; /**< Glucose measurement */
ble_gls_meas_context_t context; /**< Glucose measurement context */
} ble_gls_rec_t;
/**@brief Glucose Service init structure. This contains all options and data needed for
* initialization of the service. */
typedef struct
{
ble_gls_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Glucose Service. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
uint16_t feature; /**< Glucose Feature value indicating supported features. */
bool is_context_supported; /**< Determines if optional Glucose Measurement Context is to be supported. */
security_req_t gl_meas_cccd_wr_sec; /**< Security requirement for writing glucose measurement characteristic CCCD. */
security_req_t gl_feature_rd_sec; /**< Security requirement for reading glucose feature characteristic. */
security_req_t racp_cccd_wr_sec; /**< Security requirement for writing RACP Characteristic CCCD. */
security_req_t racp_wr_sec; /**< Security requirement for writing RACP Characteristic. (Service specification mandates authentication) */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to BLE GATT Queue instance. */
} ble_gls_init_t;
/**@brief Glucose Service structure. This contains various status information for the service. */
struct ble_gls_s
{
ble_gls_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Glucose Service. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
uint16_t service_handle; /**< Handle of Glucose Service (as provided by the BLE stack). */
ble_gatts_char_handles_t glm_handles; /**< Handles related to the Glucose Measurement characteristic. */
ble_gatts_char_handles_t glm_context_handles; /**< Handles related to the Glucose Measurement Context characteristic. */
ble_gatts_char_handles_t glf_handles; /**< Handles related to the Glucose Feature characteristic. */
ble_gatts_char_handles_t racp_handles; /**< Handles related to the Record Access Control Point characteristic. */
uint16_t conn_handle; /**< Handle of the current connection (as provided by the BLE stack, is BLE_CONN_HANDLE_INVALID if not in a connection). */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to BLE GATT Queue instance. */
uint16_t feature;
bool is_context_supported;
};
/**@brief Function for initializing the Glucose Service.
*
* @details This call allows the application to initialize the Glucose Service.
*
* @param[out] p_gls Glucose Service structure. This structure will have to be supplied by
* the application. It will be initialized by this function, and will later
* be used to identify this particular service instance.
* @param[in] p_gls_init Information needed to initialize the service.
*
* @return NRF_SUCCESS on successful initialization of service, otherwise an error code.
*/
uint32_t ble_gls_init(ble_gls_t * p_gls, ble_gls_init_t const * p_gls_init);
/**@brief Function for handling the Application's BLE Stack events.
*
* @details Handles all events from the BLE stack of interest to the Glucose Service.
*
* @param[in] p_ble_evt Event received from the BLE stack.
* @param[in] p_context Glucose Service structure.
*/
void ble_gls_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for reporting a new glucose measurement to the glucose service module.
*
* @details The application calls this function after having performed a new glucose measurement.
* The new measurement is recorded in the RACP database.
*
* @param[in] p_gls Glucose Service structure.
* @param[in] p_rec Pointer to glucose record (measurement plus context).
*
* @return NRF_SUCCESS on success, otherwise an error code.
*/
uint32_t ble_gls_glucose_new_meas(ble_gls_t * p_gls, ble_gls_rec_t * p_rec);
#ifdef __cplusplus
}
#endif
#endif // BLE_GLS_H__
/** @} */

View File

@@ -0,0 +1,143 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_GLS)
#include "ble_gls_db.h"
typedef struct
{
bool in_use_flag;
ble_gls_rec_t record;
} database_entry_t;
static database_entry_t m_database[BLE_GLS_DB_MAX_RECORDS];
static uint8_t m_database_crossref[BLE_GLS_DB_MAX_RECORDS];
static uint16_t m_num_records;
uint32_t ble_gls_db_init(void)
{
int i;
for (i = 0; i < BLE_GLS_DB_MAX_RECORDS; i++)
{
m_database[i].in_use_flag = false;
m_database_crossref[i] = 0xFF;
}
m_num_records = 0;
return NRF_SUCCESS;
}
uint16_t ble_gls_db_num_records_get(void)
{
return m_num_records;
}
uint32_t ble_gls_db_record_get(uint8_t rec_ndx, ble_gls_rec_t * p_rec)
{
if (rec_ndx >= m_num_records)
{
return NRF_ERROR_INVALID_PARAM;
}
// copy record to the specified memory
*p_rec = m_database[m_database_crossref[rec_ndx]].record;
return NRF_SUCCESS;
}
uint32_t ble_gls_db_record_add(ble_gls_rec_t * p_rec)
{
int i;
if (m_num_records == BLE_GLS_DB_MAX_RECORDS)
{
return NRF_ERROR_NO_MEM;
}
// find next available database entry
for (i = 0; i < BLE_GLS_DB_MAX_RECORDS; i++)
{
if (!m_database[i].in_use_flag)
{
m_database[i].in_use_flag = true;
m_database[i].record = *p_rec;
m_database_crossref[m_num_records] = i;
m_num_records++;
return NRF_SUCCESS;
}
}
return NRF_ERROR_NO_MEM;
}
uint32_t ble_gls_db_record_delete(uint8_t rec_ndx)
{
int i;
if (rec_ndx >= m_num_records)
{
return NRF_ERROR_NOT_FOUND;
}
// free entry
m_database[m_database_crossref[rec_ndx]].in_use_flag = false;
// decrease number of records
m_num_records--;
// remove cross reference index
for (i = rec_ndx; i < m_num_records; i++)
{
m_database_crossref[i] = m_database_crossref[i + 1];
}
return NRF_SUCCESS;
}
#endif // NRF_MODULE_ENABLED(BLE_GLS)

View File

@@ -0,0 +1,121 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_sdk_srv_gls_db Glucose Database Service
* @{
* @ingroup ble_sdk_srv
* @brief Glucose Service module.
*
* @details This module implements at database of stored glucose measurement values.
*
* @note Attention!
* To maintain compliance with Nordic Semiconductor ASA Bluetooth profile
* qualification listings, These APIs must not be modified. However, the corresponding
* functions' implementations can be modified.
*/
#ifndef BLE_GLS_DB_H__
#define BLE_GLS_DB_H__
#include <stdint.h>
#include "ble_gls.h"
#ifdef __cplusplus
extern "C" {
#endif
#define BLE_GLS_DB_MAX_RECORDS 20
/**@brief Function for initializing the glucose record database.
*
* @details This call initializes the database holding glucose records.
*
* @return NRF_SUCCESS on success.
*/
uint32_t ble_gls_db_init(void);
/**@brief Function for getting the number of records in the database.
*
* @details This call returns the number of records in the database.
*
* @return Number of records in the database.
*/
uint16_t ble_gls_db_num_records_get(void);
/**@brief Function for getting a record from the database.
*
* @details This call returns a specified record from the database.
*
* @param[in] record_num Index of the record to retrieve.
* @param[out] p_rec Pointer to record structure where retrieved record is copied to.
*
* @return NRF_SUCCESS on success.
*/
uint32_t ble_gls_db_record_get(uint8_t record_num, ble_gls_rec_t * p_rec);
/**@brief Function for adding a record at the end of the database.
*
* @details This call adds a record as the last record in the database.
*
* @param[in] p_rec Pointer to record to add to database.
*
* @return NRF_SUCCESS on success.
*/
uint32_t ble_gls_db_record_add(ble_gls_rec_t * p_rec);
/**@brief Function for deleting a database entry.
*
* @details This call deletes an record from the database.
*
* @param[in] record_num Index of record to delete.
*
* @return NRF_SUCCESS on success.
*/
uint32_t ble_gls_db_record_delete(uint8_t record_num);
#ifdef __cplusplus
}
#endif
#endif // BLE_GLS_DB_H__
/** @} */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,439 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_hids Human Interface Device Service
* @{
* @ingroup ble_sdk_srv
* @brief Human Interface Device Service module.
*
* @details This module implements the Human Interface Device Service with the corresponding set of
* characteristics. During initialization it adds the Human Interface Device Service and
* a set of characteristics as per the Human Interface Device Service specification and
* the user requirements to the BLE stack database.
*
* If enabled, notification of Input Report characteristics is performed when the
* application calls the corresponding ble_hids_xx_input_report_send() function.
*
* If an event handler is supplied by the application, the Human Interface Device Service
* will generate Human Interface Device Service events to the application.
*
* @note The application must register this module as BLE event observer using the
* NRF_SDH_BLE_OBSERVER macro. Example:
* @code
* ble_hids_t instance;
* NRF_SDH_BLE_OBSERVER(anything, BLE_HIDS_BLE_OBSERVER_PRIO,
* ble_hids_on_ble_evt, &instance);
* @endcode
*
* @note Attention!
* To maintain compliance with Nordic Semiconductor ASA Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#ifndef BLE_HIDS_H__
#define BLE_HIDS_H__
#include <stdint.h>
#include <stdbool.h>
#include "ble.h"
#include "ble_srv_common.h"
#include "ble_link_ctx_manager.h"
#include "nrf_sdh_ble.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Allocate static data for keeping host connection contexts.
*
* @param _name Name of BLE HIDS instance.
* @param[in] _hids_max_clients Maximum number of HIDS clients connected at a time.
* @param[in] ... Lengths of HIDS reports.
*
* @details
* Mapping of HIDS reports in the HIDS report context:
* - Structure of type @ref ble_hids_client_context_t
* - Boot keyboard input report
* - Boot keyboard output report
* - Boot mouse input report
* - Input reports
* - Output reports
* - Feature reports
* @hideinitializer
*/
#define BLE_HIDS_DEF(_name, \
_hids_max_clients, \
...) \
BLE_LINK_CTX_MANAGER_DEF(CONCAT_2(_name, _link_ctx_storage), \
(_hids_max_clients), \
BLE_HIDS_LINK_CTX_SIZE_CALC(__VA_ARGS__)); \
static ble_hids_t _name = \
{ \
.p_link_ctx_storage = &CONCAT_2(_name, _link_ctx_storage) \
}; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_HIDS_BLE_OBSERVER_PRIO, \
ble_hids_on_ble_evt, \
&_name)
/**@brief Helping macro for @ref BLE_HIDS_DEF, that calculates the link context size for BLE HIDS
* instance.
*
* @param[in] ... Lengths of HIDS reports
* @hideinitializer
*/
#define BLE_HIDS_LINK_CTX_SIZE_CALC(...) \
(sizeof(ble_hids_client_context_t) + \
MACRO_MAP_REC(BLE_HIDS_REPORT_ADD, __VA_ARGS__) \
(BOOT_KB_INPUT_REPORT_MAX_SIZE) + \
(BOOT_KB_OUTPUT_REPORT_MAX_SIZE) + \
(BOOT_MOUSE_INPUT_REPORT_MAX_SIZE)) \
/**@brief Helping macro for @ref BLE_HIDS_LINK_CTX_SIZE_CALC, that adds Input/Output/Feature report
* lengths.
*
* @param[in] _report_size Length of the specific report.
* @hideinitializer
*/
#define BLE_HIDS_REPORT_ADD(_report_size) (_report_size) +
/** @name Report Type values
* @anchor BLE_HIDS_REPORT_TYPE @{
*/
// Report Type values
#define BLE_HIDS_REP_TYPE_INPUT 1
#define BLE_HIDS_REP_TYPE_OUTPUT 2
#define BLE_HIDS_REP_TYPE_FEATURE 3
/** @} */
// Maximum number of the various Report Types
#define BLE_HIDS_MAX_INPUT_REP 10
#define BLE_HIDS_MAX_OUTPUT_REP 10
#define BLE_HIDS_MAX_FEATURE_REP 10
// Information Flags
#define HID_INFO_FLAG_REMOTE_WAKE_MSK 0x01
#define HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK 0x02
#define BOOT_KB_INPUT_REPORT_MAX_SIZE 8 /**< Maximum size of a Boot Keyboard Input Report (as per Appendix B in Device Class Definition for Human Interface Devices (HID), Version 1.11). */
#define BOOT_KB_OUTPUT_REPORT_MAX_SIZE 1 /**< Maximum size of a Boot Keyboard Output Report (as per Appendix B in Device Class Definition for Human Interface Devices (HID), Version 1.11). */
#define BOOT_MOUSE_INPUT_REPORT_MIN_SIZE 3 /**< Minimum size of a Boot Mouse Input Report (as per Appendix B in Device Class Definition for Human Interface Devices (HID), Version 1.11). */
#define BOOT_MOUSE_INPUT_REPORT_MAX_SIZE 8 /**< Maximum size of a Boot Mouse Input Report (as per Appendix B in Device Class Definition for Human Interface Devices (HID), Version 1.11). */
/**@brief HID Service characteristic id. */
typedef struct
{
uint16_t uuid; /**< UUID of characteristic. */
uint8_t rep_type; /**< Type of report (only used for BLE_UUID_REPORT_CHAR, see @ref BLE_HIDS_REPORT_TYPE). */
uint8_t rep_index; /**< Index of the characteristic (only used for BLE_UUID_REPORT_CHAR). */
} ble_hids_char_id_t;
/**@brief HID Service event type. */
typedef enum
{
BLE_HIDS_EVT_HOST_SUSP, /**< Suspend command received. */
BLE_HIDS_EVT_HOST_EXIT_SUSP, /**< Exit suspend command received. */
BLE_HIDS_EVT_NOTIF_ENABLED, /**< Notification enabled event. */
BLE_HIDS_EVT_NOTIF_DISABLED, /**< Notification disabled event. */
BLE_HIDS_EVT_REP_CHAR_WRITE, /**< A new value has been written to an Report characteristic. */
BLE_HIDS_EVT_BOOT_MODE_ENTERED, /**< Boot mode entered. */
BLE_HIDS_EVT_REPORT_MODE_ENTERED, /**< Report mode entered. */
BLE_HIDS_EVT_REPORT_READ /**< Read with response */
} ble_hids_evt_type_t;
/**@brief HID Service event. */
typedef struct
{
ble_hids_evt_type_t evt_type; /**< Type of event. */
union
{
struct
{
ble_hids_char_id_t char_id; /**< Id of characteristic for which notification has been started. */
} notification;
struct
{
ble_hids_char_id_t char_id; /**< Id of characteristic having been written. */
uint16_t offset; /**< Offset for the write operation. */
uint16_t len; /**< Length of the incoming data. */
uint8_t const * data; /**< Incoming data, variable length */
} char_write;
struct
{
ble_hids_char_id_t char_id; /**< Id of characteristic being read. */
} char_auth_read;
} params;
ble_evt_t const * p_ble_evt; /**< corresponding received ble event, NULL if not relevant */
} ble_hids_evt_t;
// Forward declaration of the ble_hids_t type.
typedef struct ble_hids_s ble_hids_t;
/**@brief HID Service event handler type. */
typedef void (*ble_hids_evt_handler_t) (ble_hids_t * p_hids, ble_hids_evt_t * p_evt);
/**@brief Security requirements for HID Service characteristic. */
typedef struct
{
security_req_t rd; /**< Security requirement for reading HID Service characteristic value. */
security_req_t wr; /**< Security requirement for writing HID Service characteristic value. */
security_req_t cccd_wr; /**< Security requirement for writing HID Service characteristic CCCD. */
} ble_hids_char_sec_t;
/**@brief HID Information characteristic value. */
typedef struct
{
uint16_t bcd_hid; /**< 16-bit unsigned integer representing version number of base USB HID Specification implemented by HID Device */
uint8_t b_country_code; /**< Identifies which country the hardware is localized for. Most hardware is not localized and thus this value would be zero (0). */
uint8_t flags; /**< See http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.hid_information.xml */
security_req_t rd_sec; /**< Security requirement for reading HID Information characteristic value. */
} ble_hids_hid_information_t;
/**@brief HID Service Input Report characteristic init structure. This contains all options and
* data needed for initialization of one Input Report characteristic. */
typedef struct
{
uint16_t max_len; /**< Maximum length of characteristic value. */
ble_srv_report_ref_t rep_ref; /**< Value of the Report Reference descriptor. */
ble_hids_char_sec_t sec; /**< Security requirements for HID Service Input Report characteristic. */
} ble_hids_inp_rep_init_t;
/**@brief HID Service Output Report characteristic init structure. This contains all options and
* data needed for initialization of one Output Report characteristic. */
typedef struct
{
uint16_t max_len; /**< Maximum length of characteristic value. */
ble_srv_report_ref_t rep_ref; /**< Value of the Report Reference descriptor. */
ble_hids_char_sec_t sec; /**< Security requirements for HID Service Output Report characteristic. */
} ble_hids_outp_rep_init_t;
/**@brief HID Service Feature Report characteristic init structure. This contains all options and
* data needed for initialization of one Feature Report characteristic. */
typedef struct
{
uint16_t max_len; /**< Maximum length of characteristic value. */
ble_srv_report_ref_t rep_ref; /**< Value of the Report Reference descriptor. */
ble_hids_char_sec_t sec; /**< Security requirements for HID Service Feature Report characteristic. */
} ble_hids_feature_rep_init_t;
/**@brief HID Service Report Map characteristic init structure. This contains all options and data
* needed for initialization of the Report Map characteristic. */
typedef struct
{
uint8_t * p_data; /**< Report map data. */
uint16_t data_len; /**< Length of report map data. */
uint8_t ext_rep_ref_num; /**< Number of Optional External Report Reference descriptors. */
ble_uuid_t const * p_ext_rep_ref; /**< Optional External Report Reference descriptor (will be added if != NULL). */
security_req_t rd_sec; /**< Security requirement for HID Service Report Map characteristic. */
} ble_hids_rep_map_init_t;
/**@brief HID Report characteristic structure. */
typedef struct
{
ble_gatts_char_handles_t char_handles; /**< Handles related to the Report characteristic. */
uint16_t ref_handle; /**< Handle of the Report Reference descriptor. */
} ble_hids_rep_char_t;
/**@brief HID Host context structure. It keeps information relevant to a single host. */
typedef struct
{
uint8_t protocol_mode; /**< Protocol mode. */
uint8_t ctrl_pt; /**< HID Control Point. */
} ble_hids_client_context_t;
/**@brief HID Service init structure. This contains all options and data needed for initialization
* of the service. */
typedef struct
{
ble_hids_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the HID Service. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
bool is_kb; /**< TRUE if device is operating as a keyboard, FALSE if it is not. */
bool is_mouse; /**< TRUE if device is operating as a mouse, FALSE if it is not. */
uint8_t inp_rep_count; /**< Number of Input Report characteristics. */
ble_hids_inp_rep_init_t const * p_inp_rep_array; /**< Information about the Input Report characteristics. */
uint8_t outp_rep_count; /**< Number of Output Report characteristics. */
ble_hids_outp_rep_init_t const * p_outp_rep_array; /**< Information about the Output Report characteristics. */
uint8_t feature_rep_count; /**< Number of Feature Report characteristics. */
ble_hids_feature_rep_init_t const * p_feature_rep_array; /**< Information about the Feature Report characteristics. */
ble_hids_rep_map_init_t rep_map; /**< Information nedeed for initialization of the Report Map characteristic. */
ble_hids_hid_information_t hid_information; /**< Value of the HID Information characteristic. */
uint8_t included_services_count; /**< Number of services to include in HID service. */
uint16_t * p_included_services_array; /**< Array of services to include in HID service. */
security_req_t protocol_mode_rd_sec; /**< Security requirement for reading HID service Protocol Mode characteristic. */
security_req_t protocol_mode_wr_sec; /**< Security requirement for writing HID service Protocol Mode characteristic. */
security_req_t ctrl_point_wr_sec; /**< Security requirement for writing HID service Control Point characteristic. */
ble_hids_char_sec_t boot_mouse_inp_rep_sec; /**< Security requirements for HID Boot Keyboard Input Report characteristic. */
ble_hids_char_sec_t boot_kb_inp_rep_sec; /**< Security requirements for HID Boot Keyboard Input Report characteristic. */
ble_hids_char_sec_t boot_kb_outp_rep_sec; /**< Security requirements for HID Boot Keyboard Output Report characteristic. */
} ble_hids_init_t;
/**@brief HID Service structure. This contains various status information for the service. */
struct ble_hids_s
{
ble_hids_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the HID Service. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
uint16_t service_handle; /**< Handle of HID Service (as provided by the BLE stack). */
ble_gatts_char_handles_t protocol_mode_handles; /**< Handles related to the Protocol Mode characteristic (will only be created if ble_hids_init_t.is_kb or ble_hids_init_t.is_mouse is set). */
uint8_t inp_rep_count; /**< Number of Input Report characteristics. */
ble_hids_rep_char_t inp_rep_array[BLE_HIDS_MAX_INPUT_REP]; /**< Information about the Input Report characteristics. */
uint8_t outp_rep_count; /**< Number of Output Report characteristics. */
ble_hids_rep_char_t outp_rep_array[BLE_HIDS_MAX_OUTPUT_REP]; /**< Information about the Output Report characteristics. */
uint8_t feature_rep_count; /**< Number of Feature Report characteristics. */
ble_hids_rep_char_t feature_rep_array[BLE_HIDS_MAX_FEATURE_REP]; /**< Information about the Feature Report characteristics. */
ble_gatts_char_handles_t rep_map_handles; /**< Handles related to the Report Map characteristic. */
uint16_t rep_map_ext_rep_ref_handle; /**< Handle of the Report Map External Report Reference descriptor. */
ble_gatts_char_handles_t boot_kb_inp_rep_handles; /**< Handles related to the Boot Keyboard Input Report characteristic (will only be created if ble_hids_init_t.is_kb is set). */
ble_gatts_char_handles_t boot_kb_outp_rep_handles; /**< Handles related to the Boot Keyboard Output Report characteristic (will only be created if ble_hids_init_t.is_kb is set). */
ble_gatts_char_handles_t boot_mouse_inp_rep_handles; /**< Handles related to the Boot Mouse Input Report characteristic (will only be created if ble_hids_init_t.is_mouse is set). */
ble_gatts_char_handles_t hid_information_handles; /**< Handles related to the Report Map characteristic. */
ble_gatts_char_handles_t hid_control_point_handles; /**< Handles related to the Report Map characteristic. */
blcm_link_ctx_storage_t * const p_link_ctx_storage; /**< Link context storage with handles of all current connections and its data context. */
ble_hids_inp_rep_init_t const * p_inp_rep_init_array; /**< Pointer to information about the Input Report characteristics. */
ble_hids_outp_rep_init_t const * p_outp_rep_init_array; /**< Pointer to information about the Output Report characteristics. */
ble_hids_feature_rep_init_t const * p_feature_rep_init_array; /**< Pointer to information about the Feature Report characteristics. */
};
/**@brief Function for initializing the HID Service.
*
* @param[out] p_hids HID Service structure. This structure will have to be supplied by the
* application. It will be initialized by this function, and will later be
* used to identify this particular service instance.
* @param[in] p_hids_init Information needed to initialize the service.
*
* @return NRF_SUCCESS on successful initialization of service, otherwise an error code.
*/
uint32_t ble_hids_init(ble_hids_t * p_hids, const ble_hids_init_t * p_hids_init);
/**@brief Function for handling the Application's BLE Stack events.
*
* @details Handles all events from the BLE stack of interest to the HID Service.
*
* @param[in] p_ble_evt Event received from the BLE stack.
* @param[in] p_context HID Service structure.
*/
void ble_hids_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for sending Input Report.
*
* @details Sends data on an Input Report characteristic.
*
* @param[in] p_hids HID Service structure.
* @param[in] rep_index Index of the characteristic (corresponding to the index in
* ble_hids_t.inp_rep_array as passed to ble_hids_init()).
* @param[in] len Length of data to be sent.
* @param[in] p_data Pointer to data to be sent.
* @param[in] conn_handle Connection handle, where the notification will be sent.
*
* @return NRF_SUCCESS on successful sending of input report, otherwise an error code.
*/
uint32_t ble_hids_inp_rep_send(ble_hids_t * p_hids,
uint8_t rep_index,
uint16_t len,
uint8_t * p_data,
uint16_t conn_handle);
/**@brief Function for sending Boot Keyboard Input Report.
*
* @details Sends data on an Boot Keyboard Input Report characteristic.
*
* @param[in] p_hids HID Service structure.
* @param[in] len Length of data to be sent.
* @param[in] p_data Pointer to data to be sent.
* @param[in] conn_handle Connection handle, where the notification will be sent.
*
* @return NRF_SUCCESS on successful sending of the report, otherwise an error code.
*/
uint32_t ble_hids_boot_kb_inp_rep_send(ble_hids_t * p_hids,
uint16_t len,
uint8_t * p_data,
uint16_t conn_handle);
/**@brief Function for sending Boot Mouse Input Report.
*
* @details Sends data on an Boot Mouse Input Report characteristic.
*
* @param[in] p_hids HID Service structure.
* @param[in] buttons State of mouse buttons.
* @param[in] x_delta Horizontal movement.
* @param[in] y_delta Vertical movement.
* @param[in] optional_data_len Length of optional part of Boot Mouse Input Report.
* @param[in] p_optional_data Optional part of Boot Mouse Input Report.
* @param[in] conn_handle Connection handle.
*
* @return NRF_SUCCESS on successful sending of the report, otherwise an error code.
*/
uint32_t ble_hids_boot_mouse_inp_rep_send(ble_hids_t * p_hids,
uint8_t buttons,
int8_t x_delta,
int8_t y_delta,
uint16_t optional_data_len,
uint8_t * p_optional_data,
uint16_t conn_handle);
/**@brief Function for getting the current value of Output Report from the stack.
*
* @details Fetches the current value of the output report characteristic from the stack.
*
* @param[in] p_hids HID Service structure.
* @param[in] rep_index Index of the characteristic (corresponding to the index in
* ble_hids_t.outp_rep_array as passed to ble_hids_init()).
* @param[in] len Length of output report needed.
* @param[in] offset Offset in bytes to read from.
* @param[in] conn_handle Connection handle.
* @param[out] p_outp_rep Pointer to the output report.
*
* @return NRF_SUCCESS on successful read of the report, otherwise an error code.
*/
uint32_t ble_hids_outp_rep_get(ble_hids_t * p_hids,
uint8_t rep_index,
uint16_t len,
uint8_t offset,
uint16_t conn_handle,
uint8_t * p_outp_rep);
#ifdef __cplusplus
}
#endif
#endif // BLE_HIDS_H__
/** @} */

View File

@@ -0,0 +1,391 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* Attention!
* To maintain compliance with Nordic Semiconductor ASA's Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_HRS)
#include "ble_hrs.h"
#include <string.h>
#include "ble_srv_common.h"
#define OPCODE_LENGTH 1 /**< Length of opcode inside Heart Rate Measurement packet. */
#define HANDLE_LENGTH 2 /**< Length of handle inside Heart Rate Measurement packet. */
#define MAX_HRM_LEN (NRF_SDH_BLE_GATT_MAX_MTU_SIZE - OPCODE_LENGTH - HANDLE_LENGTH) /**< Maximum size of a transmitted Heart Rate Measurement. */
#define INITIAL_VALUE_HRM 0 /**< Initial Heart Rate Measurement value. */
// Heart Rate Measurement flag bits
#define HRM_FLAG_MASK_HR_VALUE_16BIT (0x01 << 0) /**< Heart Rate Value Format bit. */
#define HRM_FLAG_MASK_SENSOR_CONTACT_DETECTED (0x01 << 1) /**< Sensor Contact Detected bit. */
#define HRM_FLAG_MASK_SENSOR_CONTACT_SUPPORTED (0x01 << 2) /**< Sensor Contact Supported bit. */
#define HRM_FLAG_MASK_EXPENDED_ENERGY_INCLUDED (0x01 << 3) /**< Energy Expended Status bit. Feature Not Supported */
#define HRM_FLAG_MASK_RR_INTERVAL_INCLUDED (0x01 << 4) /**< RR-Interval bit. */
/**@brief Function for handling the Connect event.
*
* @param[in] p_hrs Heart Rate Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_connect(ble_hrs_t * p_hrs, ble_evt_t const * p_ble_evt)
{
p_hrs->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
}
/**@brief Function for handling the Disconnect event.
*
* @param[in] p_hrs Heart Rate Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_disconnect(ble_hrs_t * p_hrs, ble_evt_t const * p_ble_evt)
{
UNUSED_PARAMETER(p_ble_evt);
p_hrs->conn_handle = BLE_CONN_HANDLE_INVALID;
}
/**@brief Function for handling write events to the Heart Rate Measurement characteristic.
*
* @param[in] p_hrs Heart Rate Service structure.
* @param[in] p_evt_write Write event received from the BLE stack.
*/
static void on_hrm_cccd_write(ble_hrs_t * p_hrs, ble_gatts_evt_write_t const * p_evt_write)
{
if (p_evt_write->len == 2)
{
// CCCD written, update notification state
if (p_hrs->evt_handler != NULL)
{
ble_hrs_evt_t evt;
if (ble_srv_is_notification_enabled(p_evt_write->data))
{
evt.evt_type = BLE_HRS_EVT_NOTIFICATION_ENABLED;
}
else
{
evt.evt_type = BLE_HRS_EVT_NOTIFICATION_DISABLED;
}
p_hrs->evt_handler(p_hrs, &evt);
}
}
}
/**@brief Function for handling the Write event.
*
* @param[in] p_hrs Heart Rate Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_write(ble_hrs_t * p_hrs, ble_evt_t const * p_ble_evt)
{
ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
if (p_evt_write->handle == p_hrs->hrm_handles.cccd_handle)
{
on_hrm_cccd_write(p_hrs, p_evt_write);
}
}
void ble_hrs_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
ble_hrs_t * p_hrs = (ble_hrs_t *) p_context;
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
on_connect(p_hrs, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnect(p_hrs, p_ble_evt);
break;
case BLE_GATTS_EVT_WRITE:
on_write(p_hrs, p_ble_evt);
break;
default:
// No implementation needed.
break;
}
}
/**@brief Function for encoding a Heart Rate Measurement.
*
* @param[in] p_hrs Heart Rate Service structure.
* @param[in] heart_rate Measurement to be encoded.
* @param[out] p_encoded_buffer Buffer where the encoded data will be written.
*
* @return Size of encoded data.
*/
static uint8_t hrm_encode(ble_hrs_t * p_hrs, uint16_t heart_rate, uint8_t * p_encoded_buffer)
{
uint8_t flags = 0;
uint8_t len = 1;
int i;
// Set sensor contact related flags
if (p_hrs->is_sensor_contact_supported)
{
flags |= HRM_FLAG_MASK_SENSOR_CONTACT_SUPPORTED;
}
if (p_hrs->is_sensor_contact_detected)
{
flags |= HRM_FLAG_MASK_SENSOR_CONTACT_DETECTED;
}
// Encode heart rate measurement
if (heart_rate > 0xff)
{
flags |= HRM_FLAG_MASK_HR_VALUE_16BIT;
len += uint16_encode(heart_rate, &p_encoded_buffer[len]);
}
else
{
p_encoded_buffer[len++] = (uint8_t)heart_rate;
}
// Encode rr_interval values
if (p_hrs->rr_interval_count > 0)
{
flags |= HRM_FLAG_MASK_RR_INTERVAL_INCLUDED;
}
for (i = 0; i < p_hrs->rr_interval_count; i++)
{
if (len + sizeof(uint16_t) > p_hrs->max_hrm_len)
{
// Not all stored rr_interval values can fit into the encoded hrm,
// move the remaining values to the start of the buffer.
memmove(&p_hrs->rr_interval[0],
&p_hrs->rr_interval[i],
(p_hrs->rr_interval_count - i) * sizeof(uint16_t));
break;
}
len += uint16_encode(p_hrs->rr_interval[i], &p_encoded_buffer[len]);
}
p_hrs->rr_interval_count -= i;
// Add flags
p_encoded_buffer[0] = flags;
return len;
}
uint32_t ble_hrs_init(ble_hrs_t * p_hrs, const ble_hrs_init_t * p_hrs_init)
{
uint32_t err_code;
ble_uuid_t ble_uuid;
ble_add_char_params_t add_char_params;
uint8_t encoded_initial_hrm[MAX_HRM_LEN];
// Initialize service structure
p_hrs->evt_handler = p_hrs_init->evt_handler;
p_hrs->is_sensor_contact_supported = p_hrs_init->is_sensor_contact_supported;
p_hrs->conn_handle = BLE_CONN_HANDLE_INVALID;
p_hrs->is_sensor_contact_detected = false;
p_hrs->rr_interval_count = 0;
p_hrs->max_hrm_len = MAX_HRM_LEN;
// Add service
BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_HEART_RATE_SERVICE);
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
&ble_uuid,
&p_hrs->service_handle);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
// Add heart rate measurement characteristic
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = BLE_UUID_HEART_RATE_MEASUREMENT_CHAR;
add_char_params.max_len = MAX_HRM_LEN;
add_char_params.init_len = hrm_encode(p_hrs, INITIAL_VALUE_HRM, encoded_initial_hrm);
add_char_params.p_init_value = encoded_initial_hrm;
add_char_params.is_var_len = true;
add_char_params.char_props.notify = 1;
add_char_params.cccd_write_access = p_hrs_init->hrm_cccd_wr_sec;
err_code = characteristic_add(p_hrs->service_handle, &add_char_params, &(p_hrs->hrm_handles));
if (err_code != NRF_SUCCESS)
{
return err_code;
}
if (p_hrs_init->p_body_sensor_location != NULL)
{
// Add body sensor location characteristic
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = BLE_UUID_BODY_SENSOR_LOCATION_CHAR;
add_char_params.max_len = sizeof(uint8_t);
add_char_params.init_len = sizeof(uint8_t);
add_char_params.p_init_value = p_hrs_init->p_body_sensor_location;
add_char_params.char_props.read = 1;
add_char_params.read_access = p_hrs_init->bsl_rd_sec;
err_code = characteristic_add(p_hrs->service_handle, &add_char_params, &(p_hrs->bsl_handles));
if (err_code != NRF_SUCCESS)
{
return err_code;
}
}
return NRF_SUCCESS;
}
uint32_t ble_hrs_heart_rate_measurement_send(ble_hrs_t * p_hrs, uint16_t heart_rate)
{
uint32_t err_code;
// Send value if connected and notifying
if (p_hrs->conn_handle != BLE_CONN_HANDLE_INVALID)
{
uint8_t encoded_hrm[MAX_HRM_LEN];
uint16_t len;
uint16_t hvx_len;
ble_gatts_hvx_params_t hvx_params;
len = hrm_encode(p_hrs, heart_rate, encoded_hrm);
hvx_len = len;
memset(&hvx_params, 0, sizeof(hvx_params));
hvx_params.handle = p_hrs->hrm_handles.value_handle;
hvx_params.type = BLE_GATT_HVX_NOTIFICATION;
hvx_params.offset = 0;
hvx_params.p_len = &hvx_len;
hvx_params.p_data = encoded_hrm;
err_code = sd_ble_gatts_hvx(p_hrs->conn_handle, &hvx_params);
if ((err_code == NRF_SUCCESS) && (hvx_len != len))
{
err_code = NRF_ERROR_DATA_SIZE;
}
}
else
{
err_code = NRF_ERROR_INVALID_STATE;
}
return err_code;
}
void ble_hrs_rr_interval_add(ble_hrs_t * p_hrs, uint16_t rr_interval)
{
if (p_hrs->rr_interval_count == BLE_HRS_MAX_BUFFERED_RR_INTERVALS)
{
// The rr_interval buffer is full, delete the oldest value
memmove(&p_hrs->rr_interval[0],
&p_hrs->rr_interval[1],
(BLE_HRS_MAX_BUFFERED_RR_INTERVALS - 1) * sizeof(uint16_t));
p_hrs->rr_interval_count--;
}
// Add new value
p_hrs->rr_interval[p_hrs->rr_interval_count++] = rr_interval;
}
bool ble_hrs_rr_interval_buffer_is_full(ble_hrs_t * p_hrs)
{
return (p_hrs->rr_interval_count == BLE_HRS_MAX_BUFFERED_RR_INTERVALS);
}
uint32_t ble_hrs_sensor_contact_supported_set(ble_hrs_t * p_hrs, bool is_sensor_contact_supported)
{
// Check if we are connected to peer
if (p_hrs->conn_handle == BLE_CONN_HANDLE_INVALID)
{
p_hrs->is_sensor_contact_supported = is_sensor_contact_supported;
return NRF_SUCCESS;
}
else
{
return NRF_ERROR_INVALID_STATE;
}
}
void ble_hrs_sensor_contact_detected_update(ble_hrs_t * p_hrs, bool is_sensor_contact_detected)
{
p_hrs->is_sensor_contact_detected = is_sensor_contact_detected;
}
uint32_t ble_hrs_body_sensor_location_set(ble_hrs_t * p_hrs, uint8_t body_sensor_location)
{
ble_gatts_value_t gatts_value;
// Initialize value struct.
memset(&gatts_value, 0, sizeof(gatts_value));
gatts_value.len = sizeof(uint8_t);
gatts_value.offset = 0;
gatts_value.p_value = &body_sensor_location;
return sd_ble_gatts_value_set(p_hrs->conn_handle, p_hrs->bsl_handles.value_handle, &gatts_value);
}
void ble_hrs_on_gatt_evt(ble_hrs_t * p_hrs, nrf_ble_gatt_evt_t const * p_gatt_evt)
{
if ( (p_hrs->conn_handle == p_gatt_evt->conn_handle)
&& (p_gatt_evt->evt_id == NRF_BLE_GATT_EVT_ATT_MTU_UPDATED))
{
p_hrs->max_hrm_len = p_gatt_evt->params.att_mtu_effective - OPCODE_LENGTH - HANDLE_LENGTH;
}
}
#endif // NRF_MODULE_ENABLED(BLE_HRS)

View File

@@ -0,0 +1,266 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_hrs Heart Rate Service
* @{
* @ingroup ble_sdk_srv
* @brief Heart Rate Service module.
*
* @details This module implements the Heart Rate Service with the Heart Rate Measurement,
* Body Sensor Location and Heart Rate Control Point characteristics.
* During initialization it adds the Heart Rate Service and Heart Rate Measurement
* characteristic to the BLE stack database. Optionally it also adds the
* Body Sensor Location and Heart Rate Control Point characteristics.
*
* If enabled, notification of the Heart Rate Measurement characteristic is performed
* when the application calls ble_hrs_heart_rate_measurement_send().
*
* The Heart Rate Service also provides a set of functions for manipulating the
* various fields in the Heart Rate Measurement characteristic, as well as setting
* the Body Sensor Location characteristic value.
*
* If an event handler is supplied by the application, the Heart Rate Service will
* generate Heart Rate Service events to the application.
*
* @note The application must register this module as BLE event observer using the
* NRF_SDH_BLE_OBSERVER macro. Example:
* @code
* ble_hrs_t instance;
* NRF_SDH_BLE_OBSERVER(anything, BLE_HRS_BLE_OBSERVER_PRIO,
* ble_hrs_on_ble_evt, &instance);
* @endcode
*
* @note Attention!
* To maintain compliance with Nordic Semiconductor ASA Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#ifndef BLE_HRS_H__
#define BLE_HRS_H__
#include <stdint.h>
#include <stdbool.h>
#include "ble.h"
#include "ble_srv_common.h"
#include "nrf_sdh_ble.h"
#include "nrf_ble_gatt.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Macro for defining a ble_hrs instance.
*
* @param _name Name of the instance.
* @hideinitializer
*/
#define BLE_HRS_DEF(_name) \
static ble_hrs_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_HRS_BLE_OBSERVER_PRIO, \
ble_hrs_on_ble_evt, &_name)
// Body Sensor Location values
#define BLE_HRS_BODY_SENSOR_LOCATION_OTHER 0
#define BLE_HRS_BODY_SENSOR_LOCATION_CHEST 1
#define BLE_HRS_BODY_SENSOR_LOCATION_WRIST 2
#define BLE_HRS_BODY_SENSOR_LOCATION_FINGER 3
#define BLE_HRS_BODY_SENSOR_LOCATION_HAND 4
#define BLE_HRS_BODY_SENSOR_LOCATION_EAR_LOBE 5
#define BLE_HRS_BODY_SENSOR_LOCATION_FOOT 6
#define BLE_HRS_MAX_BUFFERED_RR_INTERVALS 20 /**< Size of RR Interval buffer inside service. */
/**@brief Heart Rate Service event type. */
typedef enum
{
BLE_HRS_EVT_NOTIFICATION_ENABLED, /**< Heart Rate value notification enabled event. */
BLE_HRS_EVT_NOTIFICATION_DISABLED /**< Heart Rate value notification disabled event. */
} ble_hrs_evt_type_t;
/**@brief Heart Rate Service event. */
typedef struct
{
ble_hrs_evt_type_t evt_type; /**< Type of event. */
} ble_hrs_evt_t;
// Forward declaration of the ble_hrs_t type.
typedef struct ble_hrs_s ble_hrs_t;
/**@brief Heart Rate Service event handler type. */
typedef void (*ble_hrs_evt_handler_t) (ble_hrs_t * p_hrs, ble_hrs_evt_t * p_evt);
/**@brief Heart Rate Service init structure. This contains all options and data needed for
* initialization of the service. */
typedef struct
{
ble_hrs_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Heart Rate Service. */
bool is_sensor_contact_supported; /**< Determines if sensor contact detection is to be supported. */
uint8_t * p_body_sensor_location; /**< If not NULL, initial value of the Body Sensor Location characteristic. */
security_req_t hrm_cccd_wr_sec; /**< Security requirement for writing the HRM characteristic CCCD. */
security_req_t bsl_rd_sec; /**< Security requirement for reading the BSL characteristic value. */
} ble_hrs_init_t;
/**@brief Heart Rate Service structure. This contains various status information for the service. */
struct ble_hrs_s
{
ble_hrs_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Heart Rate Service. */
bool is_expended_energy_supported; /**< TRUE if Expended Energy measurement is supported. */
bool is_sensor_contact_supported; /**< TRUE if sensor contact detection is supported. */
uint16_t service_handle; /**< Handle of Heart Rate Service (as provided by the BLE stack). */
ble_gatts_char_handles_t hrm_handles; /**< Handles related to the Heart Rate Measurement characteristic. */
ble_gatts_char_handles_t bsl_handles; /**< Handles related to the Body Sensor Location characteristic. */
ble_gatts_char_handles_t hrcp_handles; /**< Handles related to the Heart Rate Control Point characteristic. */
uint16_t conn_handle; /**< Handle of the current connection (as provided by the BLE stack, is BLE_CONN_HANDLE_INVALID if not in a connection). */
bool is_sensor_contact_detected; /**< TRUE if sensor contact has been detected. */
uint16_t rr_interval[BLE_HRS_MAX_BUFFERED_RR_INTERVALS]; /**< Set of RR Interval measurements since the last Heart Rate Measurement transmission. */
uint16_t rr_interval_count; /**< Number of RR Interval measurements since the last Heart Rate Measurement transmission. */
uint8_t max_hrm_len; /**< Current maximum HR measurement length, adjusted according to the current ATT MTU. */
};
/**@brief Function for initializing the Heart Rate Service.
*
* @param[out] p_hrs Heart Rate Service structure. This structure will have to be supplied by
* the application. It will be initialized by this function, and will later
* be used to identify this particular service instance.
* @param[in] p_hrs_init Information needed to initialize the service.
*
* @return NRF_SUCCESS on successful initialization of service, otherwise an error code.
*/
uint32_t ble_hrs_init(ble_hrs_t * p_hrs, ble_hrs_init_t const * p_hrs_init);
/**@brief Function for handling the GATT module's events.
*
* @details Handles all events from the GATT module of interest to the Heart Rate Service.
*
* @param[in] p_hrs Heart Rate Service structure.
* @param[in] p_gatt_evt Event received from the GATT module.
*/
void ble_hrs_on_gatt_evt(ble_hrs_t * p_hrs, nrf_ble_gatt_evt_t const * p_gatt_evt);
/**@brief Function for handling the Application's BLE Stack events.
*
* @details Handles all events from the BLE stack of interest to the Heart Rate Service.
*
* @param[in] p_ble_evt Event received from the BLE stack.
* @param[in] p_context Heart Rate Service structure.
*/
void ble_hrs_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for sending heart rate measurement if notification has been enabled.
*
* @details The application calls this function after having performed a heart rate measurement.
* If notification has been enabled, the heart rate measurement data is encoded and sent to
* the client.
*
* @param[in] p_hrs Heart Rate Service structure.
* @param[in] heart_rate New heart rate measurement.
*
* @return NRF_SUCCESS on success, otherwise an error code.
*/
uint32_t ble_hrs_heart_rate_measurement_send(ble_hrs_t * p_hrs, uint16_t heart_rate);
/**@brief Function for adding a RR Interval measurement to the RR Interval buffer.
*
* @details All buffered RR Interval measurements will be included in the next heart rate
* measurement message, up to the maximum number of measurements that will fit into the
* message. If the buffer is full, the oldest measurement in the buffer will be deleted.
*
* @param[in] p_hrs Heart Rate Service structure.
* @param[in] rr_interval New RR Interval measurement (will be buffered until the next
* transmission of Heart Rate Measurement).
*/
void ble_hrs_rr_interval_add(ble_hrs_t * p_hrs, uint16_t rr_interval);
/**@brief Function for checking if RR Interval buffer is full.
*
* @param[in] p_hrs Heart Rate Service structure.
*
* @return true if RR Interval buffer is full, false otherwise.
*/
bool ble_hrs_rr_interval_buffer_is_full(ble_hrs_t * p_hrs);
/**@brief Function for setting the state of the Sensor Contact Supported bit.
*
* @param[in] p_hrs Heart Rate Service structure.
* @param[in] is_sensor_contact_supported New state of the Sensor Contact Supported bit.
*
* @return NRF_SUCCESS on success, otherwise an error code.
*/
uint32_t ble_hrs_sensor_contact_supported_set(ble_hrs_t * p_hrs, bool is_sensor_contact_supported);
/**@brief Function for setting the state of the Sensor Contact Detected bit.
*
* @param[in] p_hrs Heart Rate Service structure.
* @param[in] is_sensor_contact_detected TRUE if sensor contact is detected, FALSE otherwise.
*/
void ble_hrs_sensor_contact_detected_update(ble_hrs_t * p_hrs, bool is_sensor_contact_detected);
/**@brief Function for setting the Body Sensor Location.
*
* @details Sets a new value of the Body Sensor Location characteristic. The new value will be sent
* to the client the next time the client reads the Body Sensor Location characteristic.
*
* @param[in] p_hrs Heart Rate Service structure.
* @param[in] body_sensor_location New Body Sensor Location.
*
* @return NRF_SUCCESS on success, otherwise an error code.
*/
uint32_t ble_hrs_body_sensor_location_set(ble_hrs_t * p_hrs, uint8_t body_sensor_location);
#ifdef __cplusplus
}
#endif
#endif // BLE_HRS_H__
/** @} */

View File

@@ -0,0 +1,313 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**@cond To Make Doxygen skip documentation generation for this file.
* @{
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_HRS_C)
#include "ble_hrs_c.h"
#include "ble_db_discovery.h"
#include "ble_types.h"
#include "ble_gattc.h"
#define NRF_LOG_MODULE_NAME ble_hrs_c
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();
#define HRM_FLAG_MASK_HR_16BIT (0x01 << 0) /**< Bit mask used to extract the type of heart rate value. This is used to find if the received heart rate is a 16 bit value or an 8 bit value. */
#define HRM_FLAG_MASK_HR_RR_INT (0x01 << 4) /**< Bit mask used to extract the presence of RR_INTERVALS. This is used to find if the received measurement includes RR_INTERVALS. */
/**@brief Function for interception of the errors of GATTC and the BLE GATT Queue.
*
* @param[in] nrf_error Error code.
* @param[in] p_ctx Parameter from the event handler.
* @param[in] conn_handle Connection handle.
*/
static void gatt_error_handler(uint32_t nrf_error,
void * p_ctx,
uint16_t conn_handle)
{
ble_hrs_c_t * p_ble_hrs_c = (ble_hrs_c_t *)p_ctx;
NRF_LOG_DEBUG("A GATT Client error has occurred on conn_handle: 0X%X", conn_handle);
if (p_ble_hrs_c->error_handler != NULL)
{
p_ble_hrs_c->error_handler(nrf_error);
}
}
/**@brief Function for handling Handle Value Notification received from the SoftDevice.
*
* @details This function uses the Handle Value Notification received from the SoftDevice
* and checks whether it is a notification of the heart rate measurement from the peer. If
* it is, this function decodes the heart rate measurement and sends it to the
* application.
*
* @param[in] p_ble_hrs_c Pointer to the Heart Rate Client structure.
* @param[in] p_ble_evt Pointer to the BLE event received.
*/
static void on_hvx(ble_hrs_c_t * p_ble_hrs_c, const ble_evt_t * p_ble_evt)
{
// Check if the event is on the link for this instance.
if (p_ble_hrs_c->conn_handle != p_ble_evt->evt.gattc_evt.conn_handle)
{
NRF_LOG_DEBUG("Received HVX on link 0x%x, not associated to this instance. Ignore.",
p_ble_evt->evt.gattc_evt.conn_handle);
return;
}
NRF_LOG_DEBUG("Received HVX on link 0x%x, hrm_handle 0x%x",
p_ble_evt->evt.gattc_evt.params.hvx.handle,
p_ble_hrs_c->peer_hrs_db.hrm_handle);
// Check if this is a heart rate notification.
if (p_ble_evt->evt.gattc_evt.params.hvx.handle == p_ble_hrs_c->peer_hrs_db.hrm_handle)
{
ble_hrs_c_evt_t ble_hrs_c_evt;
uint32_t index = 0;
ble_hrs_c_evt.evt_type = BLE_HRS_C_EVT_HRM_NOTIFICATION;
ble_hrs_c_evt.conn_handle = p_ble_hrs_c->conn_handle;
ble_hrs_c_evt.params.hrm.rr_intervals_cnt = 0;
if (!(p_ble_evt->evt.gattc_evt.params.hvx.data[index++] & HRM_FLAG_MASK_HR_16BIT))
{
// 8-bit heart rate value received.
ble_hrs_c_evt.params.hrm.hr_value = p_ble_evt->evt.gattc_evt.params.hvx.data[index++]; //lint !e415 suppress Lint Warning 415: Likely access out of bond
}
else
{
// 16-bit heart rate value received.
ble_hrs_c_evt.params.hrm.hr_value =
uint16_decode(&(p_ble_evt->evt.gattc_evt.params.hvx.data[index]));
index += sizeof(uint16_t);
}
if ((p_ble_evt->evt.gattc_evt.params.hvx.data[0] & HRM_FLAG_MASK_HR_RR_INT))
{
uint32_t i;
/*lint --e{415} --e{416} --e{662} --e{661} -save suppress Warning 415: possible access out of bond */
for (i = 0; i < BLE_HRS_C_RR_INTERVALS_MAX_CNT; i ++)
{
if (index >= p_ble_evt->evt.gattc_evt.params.hvx.len)
{
break;
}
ble_hrs_c_evt.params.hrm.rr_intervals[i] =
uint16_decode(&(p_ble_evt->evt.gattc_evt.params.hvx.data[index]));
index += sizeof(uint16_t);
}
/*lint -restore*/
ble_hrs_c_evt.params.hrm.rr_intervals_cnt = (uint8_t)i;
}
p_ble_hrs_c->evt_handler(p_ble_hrs_c, &ble_hrs_c_evt);
}
}
/**@brief Function for handling Disconnected event received from the SoftDevice.
*
* @details This function checks whether the disconnect event is happening on the link
* associated with the current instance of the module. If the event is happening, the function sets the instance's
* conn_handle to invalid.
*
* @param[in] p_ble_hrs_c Pointer to the Heart Rate Client structure.
* @param[in] p_ble_evt Pointer to the BLE event received.
*/
static void on_disconnected(ble_hrs_c_t * p_ble_hrs_c, const ble_evt_t * p_ble_evt)
{
if (p_ble_hrs_c->conn_handle == p_ble_evt->evt.gap_evt.conn_handle)
{
p_ble_hrs_c->conn_handle = BLE_CONN_HANDLE_INVALID;
p_ble_hrs_c->peer_hrs_db.hrm_cccd_handle = BLE_GATT_HANDLE_INVALID;
p_ble_hrs_c->peer_hrs_db.hrm_handle = BLE_GATT_HANDLE_INVALID;
}
}
void ble_hrs_on_db_disc_evt(ble_hrs_c_t * p_ble_hrs_c, const ble_db_discovery_evt_t * p_evt)
{
// Check if the Heart Rate Service was discovered.
if (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE &&
p_evt->params.discovered_db.srv_uuid.uuid == BLE_UUID_HEART_RATE_SERVICE &&
p_evt->params.discovered_db.srv_uuid.type == BLE_UUID_TYPE_BLE)
{
// Find the CCCD Handle of the Heart Rate Measurement characteristic.
uint32_t i;
ble_hrs_c_evt_t evt;
evt.evt_type = BLE_HRS_C_EVT_DISCOVERY_COMPLETE;
evt.conn_handle = p_evt->conn_handle;
for (i = 0; i < p_evt->params.discovered_db.char_count; i++)
{
if (p_evt->params.discovered_db.charateristics[i].characteristic.uuid.uuid ==
BLE_UUID_HEART_RATE_MEASUREMENT_CHAR)
{
// Found Heart Rate characteristic. Store CCCD handle and break.
evt.params.peer_db.hrm_cccd_handle =
p_evt->params.discovered_db.charateristics[i].cccd_handle;
evt.params.peer_db.hrm_handle =
p_evt->params.discovered_db.charateristics[i].characteristic.handle_value;
break;
}
}
NRF_LOG_DEBUG("Heart Rate Service discovered at peer.");
//If the instance has been assigned prior to db_discovery, assign the db_handles.
if (p_ble_hrs_c->conn_handle != BLE_CONN_HANDLE_INVALID)
{
if ((p_ble_hrs_c->peer_hrs_db.hrm_cccd_handle == BLE_GATT_HANDLE_INVALID)&&
(p_ble_hrs_c->peer_hrs_db.hrm_handle == BLE_GATT_HANDLE_INVALID))
{
p_ble_hrs_c->peer_hrs_db = evt.params.peer_db;
}
}
p_ble_hrs_c->evt_handler(p_ble_hrs_c, &evt);
}
}
uint32_t ble_hrs_c_init(ble_hrs_c_t * p_ble_hrs_c, ble_hrs_c_init_t * p_ble_hrs_c_init)
{
VERIFY_PARAM_NOT_NULL(p_ble_hrs_c);
VERIFY_PARAM_NOT_NULL(p_ble_hrs_c_init);
ble_uuid_t hrs_uuid;
hrs_uuid.type = BLE_UUID_TYPE_BLE;
hrs_uuid.uuid = BLE_UUID_HEART_RATE_SERVICE;
p_ble_hrs_c->evt_handler = p_ble_hrs_c_init->evt_handler;
p_ble_hrs_c->error_handler = p_ble_hrs_c_init->error_handler;
p_ble_hrs_c->p_gatt_queue = p_ble_hrs_c_init->p_gatt_queue;
p_ble_hrs_c->conn_handle = BLE_CONN_HANDLE_INVALID;
p_ble_hrs_c->peer_hrs_db.hrm_cccd_handle = BLE_GATT_HANDLE_INVALID;
p_ble_hrs_c->peer_hrs_db.hrm_handle = BLE_GATT_HANDLE_INVALID;
return ble_db_discovery_evt_register(&hrs_uuid);
}
void ble_hrs_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
ble_hrs_c_t * p_ble_hrs_c = (ble_hrs_c_t *)p_context;
if ((p_ble_hrs_c == NULL) || (p_ble_evt == NULL))
{
return;
}
switch (p_ble_evt->header.evt_id)
{
case BLE_GATTC_EVT_HVX:
on_hvx(p_ble_hrs_c, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnected(p_ble_hrs_c, p_ble_evt);
break;
default:
break;
}
}
/**@brief Function for creating a message for writing to the CCCD.
*/
static uint32_t cccd_configure(ble_hrs_c_t * p_ble_hrs_c, bool enable)
{
NRF_LOG_DEBUG("Configuring CCCD. CCCD Handle = %d, Connection Handle = %d",
p_ble_hrs_c->peer_hrs_db.hrm_cccd_handle,
p_ble_hrs_c->conn_handle);
nrf_ble_gq_req_t hrs_c_req;
uint8_t cccd[BLE_CCCD_VALUE_LEN];
uint16_t cccd_val = enable ? BLE_GATT_HVX_NOTIFICATION : 0;
cccd[0] = LSB_16(cccd_val);
cccd[1] = MSB_16(cccd_val);
memset(&hrs_c_req, 0, sizeof(hrs_c_req));
hrs_c_req.type = NRF_BLE_GQ_REQ_GATTC_WRITE;
hrs_c_req.error_handler.cb = gatt_error_handler;
hrs_c_req.error_handler.p_ctx = p_ble_hrs_c;
hrs_c_req.params.gattc_write.handle = p_ble_hrs_c->peer_hrs_db.hrm_cccd_handle;
hrs_c_req.params.gattc_write.len = BLE_CCCD_VALUE_LEN;
hrs_c_req.params.gattc_write.p_value = cccd;
hrs_c_req.params.gattc_write.write_op = BLE_GATT_OP_WRITE_REQ;
return nrf_ble_gq_item_add(p_ble_hrs_c->p_gatt_queue, &hrs_c_req, p_ble_hrs_c->conn_handle);
}
uint32_t ble_hrs_c_hrm_notif_enable(ble_hrs_c_t * p_ble_hrs_c)
{
VERIFY_PARAM_NOT_NULL(p_ble_hrs_c);
return cccd_configure(p_ble_hrs_c, true);
}
uint32_t ble_hrs_c_handles_assign(ble_hrs_c_t * p_ble_hrs_c,
uint16_t conn_handle,
const hrs_db_t * p_peer_hrs_handles)
{
VERIFY_PARAM_NOT_NULL(p_ble_hrs_c);
p_ble_hrs_c->conn_handle = conn_handle;
if (p_peer_hrs_handles != NULL)
{
p_ble_hrs_c->peer_hrs_db = *p_peer_hrs_handles;
}
return nrf_ble_gq_conn_handle_register(p_ble_hrs_c->p_gatt_queue, conn_handle);
}
/** @}
* @endcond
*/
#endif // NRF_MODULE_ENABLED(BLE_HRS_C)

View File

@@ -0,0 +1,300 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**@file
*
* @defgroup ble_hrs_c Heart Rate Service Client
* @{
* @ingroup ble_sdk_srv
* @brief Heart Rate Service Client module.
*
* @details This module contains the APIs and types exposed by the Heart Rate Service Client
* module. The application can use these APIs and types to perform the discovery of
* Heart Rate Service at the peer and to interact with it.
*
* @warning Currently, this module only supports the Heart Rate Measurement characteristic. This
* means that it is able to enable notification of the characteristic at the peer and
* is able to receive Heart Rate Measurement notifications from the peer. It does not
* support the Body Sensor Location and the Heart Rate Control Point characteristics.
* When a Heart Rate Measurement is received, this module decodes only the
* Heart Rate Measurement value field (both 8-bit and 16-bit) and provides it to
* the application.
*
* @note The application must register this module as the BLE event observer by using the
* NRF_SDH_BLE_OBSERVER macro. Example:
* @code
* ble_hrs_c_t instance;
* NRF_SDH_BLE_OBSERVER(anything, BLE_HRS_C_BLE_OBSERVER_PRIO,
* ble_hrs_c_on_ble_evt, &instance);
* @endcode
*/
#ifndef BLE_HRS_C_H__
#define BLE_HRS_C_H__
#include <stdint.h>
#include "ble.h"
#include "ble_db_discovery.h"
#include "ble_srv_common.h"
#include "sdk_config.h"
#include "nrf_ble_gq.h"
#include "nrf_sdh_ble.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Macro for defining a ble_hrs_c instance.
*
* @param _name Name of the instance.
* @hideinitializer
*/
#define BLE_HRS_C_DEF(_name) \
static ble_hrs_c_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_HRS_C_BLE_OBSERVER_PRIO, \
ble_hrs_c_on_ble_evt, &_name)
/** @brief Macro for defining multiple ble_hrs_c instances.
*
* @param _name Name of the array of instances.
* @param _cnt Number of instances to define.
* @hideinitializer
*/
#define BLE_HRS_C_ARRAY_DEF(_name, _cnt) \
static ble_hrs_c_t _name[_cnt]; \
NRF_SDH_BLE_OBSERVERS(_name ## _obs, \
BLE_HRS_C_BLE_OBSERVER_PRIO, \
ble_hrs_c_on_ble_evt, &_name, _cnt)
/** @brief Maximum number of RR intervals to be decoded for each HRM notifications (any extra RR intervals are ignored).
*
* This define should be defined in the sdk_config.h file to override the default.
*/
#ifndef BLE_HRS_C_RR_INTERVALS_MAX_CNT
#define BLE_HRS_C_RR_INTERVALS_MAX_CNT 20
#endif
/**
* @defgroup hrs_c_enums Enumerations
* @{
*/
/**@brief HRS Client event type. */
typedef enum
{
BLE_HRS_C_EVT_DISCOVERY_COMPLETE = 1, /**< Event indicating that the Heart Rate Service was discovered at the peer. */
BLE_HRS_C_EVT_HRM_NOTIFICATION /**< Event indicating that a notification of the Heart Rate Measurement characteristic was received from the peer. */
} ble_hrs_c_evt_type_t;
/** @} */
/**
* @defgroup hrs_c_structs Structures
* @{
*/
/**@brief Structure containing the Heart Rate Measurement received from the peer. */
typedef struct
{
uint16_t hr_value; /**< Heart Rate Value. */
uint8_t rr_intervals_cnt; /**< Number of RR intervals. */
uint16_t rr_intervals[BLE_HRS_C_RR_INTERVALS_MAX_CNT]; /**< RR intervals. */
} ble_hrm_t;
/**@brief Structure containing the handles related to the Heart Rate Service found on the peer. */
typedef struct
{
uint16_t hrm_cccd_handle; /**< Handle of the CCCD of the Heart Rate Measurement characteristic. */
uint16_t hrm_handle; /**< Handle of the Heart Rate Measurement characteristic, as provided by the SoftDevice. */
} hrs_db_t;
/**@brief Heart Rate Event structure. */
typedef struct
{
ble_hrs_c_evt_type_t evt_type; /**< Type of the event. */
uint16_t conn_handle; /**< Connection handle on which the Heart Rate service was discovered on the peer device..*/
union
{
hrs_db_t peer_db; /**< Handles related to the Heart Rate, found on the peer device. This is filled if the evt_type is @ref BLE_HRS_C_EVT_DISCOVERY_COMPLETE.*/
ble_hrm_t hrm; /**< Heart Rate Measurement received. This is filled if the evt_type is @ref BLE_HRS_C_EVT_HRM_NOTIFICATION. */
} params;
} ble_hrs_c_evt_t;
/** @} */
/**
* @defgroup hrs_c_types Types
* @{
*/
// Forward declaration of the ble_bas_t type.
typedef struct ble_hrs_c_s ble_hrs_c_t;
/**@brief Event handler type.
*
* @details This is the type of the event handler that is to be provided by the application
* of this module to receive events.
*/
typedef void (* ble_hrs_c_evt_handler_t) (ble_hrs_c_t * p_ble_hrs_c, ble_hrs_c_evt_t * p_evt);
/** @} */
/**
* @addtogroup hrs_c_structs
* @{
*/
/**@brief Heart Rate Client structure.
*/
struct ble_hrs_c_s
{
uint16_t conn_handle; /**< Connection handle, as provided by the SoftDevice. */
hrs_db_t peer_hrs_db; /**< Handles related to HRS on the peer. */
ble_hrs_c_evt_handler_t evt_handler; /**< Application event handler to be called when there is an event related to the Heart Rate Service. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to the BLE GATT Queue instance. */
};
/**@brief Heart Rate Client initialization structure.
*/
typedef struct
{
ble_hrs_c_evt_handler_t evt_handler; /**< Event handler to be called by the Heart Rate Client module when there is an event related to the Heart Rate Service. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to the BLE GATT Queue instance. */
} ble_hrs_c_init_t;
/** @} */
/**
* @defgroup hrs_c_functions Functions
* @{
*/
/**@brief Function for initializing the Heart Rate Client module.
*
* @details This function registers with the Database Discovery module for the Heart Rate Service.
* The module looks for the presence of a Heart Rate Service instance at the peer
* when a discovery is started.
*
* @param[in] p_ble_hrs_c Pointer to the Heart Rate Client structure.
* @param[in] p_ble_hrs_c_init Pointer to the Heart Rate initialization structure that contains
* the initialization information.
*
* @retval NRF_SUCCESS On successful initialization.
* @retval err_code Otherwise, this function propagates the error code returned by the Database Discovery module API
* @ref ble_db_discovery_evt_register.
*/
uint32_t ble_hrs_c_init(ble_hrs_c_t * p_ble_hrs_c, ble_hrs_c_init_t * p_ble_hrs_c_init);
/**@brief Function for handling BLE events from the SoftDevice.
*
* @details This function handles the BLE events received from the SoftDevice. If a BLE
* event is relevant to the Heart Rate Client module, the function uses the event's data to update
* interval variables and, if necessary, send events to the application.
*
* @param[in] p_ble_evt Pointer to the BLE event.
* @param[in] p_context Pointer to the Heart Rate Client structure.
*/
void ble_hrs_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for requesting the peer to start sending notification of Heart Rate
* Measurement.
*
* @details This function enables notification of the Heart Rate Measurement at the peer
* by writing to the CCCD of the Heart Rate Measurement characteristic.
*
* @param p_ble_hrs_c Pointer to the Heart Rate Client structure.
*
* @retval NRF_SUCCESS If the SoftDevice is requested to write to the CCCD of the peer.
* @retval err_code Otherwise, this function propagates the error code returned
* by the SoftDevice API @ref sd_ble_gattc_write.
*/
uint32_t ble_hrs_c_hrm_notif_enable(ble_hrs_c_t * p_ble_hrs_c);
/**@brief Function for handling events from the Database Discovery module.
*
* @details Call this function when you get a callback event from the Database Discovery module.
* This function handles an event from the Database Discovery module and determines
* whether it relates to the discovery of Heart Rate Service at the peer. If it does, the function
* calls the application's event handler to indicate that the Heart Rate Service was
* discovered at the peer. The function also populates the event with service-related
* information before providing it to the application.
*
* @param[in] p_ble_hrs_c Pointer to the Heart Rate Client structure instance for associating the link.
* @param[in] p_evt Pointer to the event received from the Database Discovery module.
*
*/
void ble_hrs_on_db_disc_evt(ble_hrs_c_t * p_ble_hrs_c, const ble_db_discovery_evt_t * p_evt);
/**@brief Function for assigning handles to an instance of hrs_c.
*
* @details Call this function when a link has been established with a peer to
* associate the link to this instance of the module. This association makes it
* possible to handle several links and associate each link to a particular
* instance of this module. The connection handle and attribute handles are
* provided from the discovery event @ref BLE_HRS_C_EVT_DISCOVERY_COMPLETE.
*
* @param[in] p_ble_hrs_c Pointer to the Heart Rate Client structure instance for associating the link.
* @param[in] conn_handle Connection handle to associate with the given Heart Rate Client Instance.
* @param[in] p_peer_hrs_handles Attribute handles for the HRS server you want this HRS_C client to
* interact with.
*/
uint32_t ble_hrs_c_handles_assign(ble_hrs_c_t * p_ble_hrs_c,
uint16_t conn_handle,
const hrs_db_t * p_peer_hrs_handles);
/** @} */ // End tag for Function group.
#ifdef __cplusplus
}
#endif
#endif // BLE_HRS_C_H__
/** @} */ // End tag for the file.

View File

@@ -0,0 +1,404 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* Attention!
* To maintain compliance with Nordic Semiconductor ASA's Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_HTS)
#include "ble_err.h"
#include "ble_hts.h"
#include <string.h>
#include "ble_srv_common.h"
#define OPCODE_LENGTH 1 /**< Length of opcode inside Health Thermometer Measurement packet. */
#define HANDLE_LENGTH 2 /**< Length of handle inside Health Thermometer Measurement packet. */
#define MAX_HTM_LEN (BLE_GATT_ATT_MTU_DEFAULT - OPCODE_LENGTH - HANDLE_LENGTH) /**< Maximum size of a transmitted Health Thermometer Measurement. */
// Health Thermometer Measurement flag bits
#define HTS_MEAS_FLAG_TEMP_UNITS_BIT (0x01 << 0) /**< Temperature Units flag. */
#define HTS_MEAS_FLAG_TIME_STAMP_BIT (0x01 << 1) /**< Time Stamp flag. */
#define HTS_MEAS_FLAG_TEMP_TYPE_BIT (0x01 << 2) /**< Temperature Type flag. */
/**@brief Function for interception of GATT errors and @ref nrf_ble_gq errors.
*
* @param[in] nrf_error Error code.
* @param[in] p_ctx Parameter from the event handler.
* @param[in] conn_handle Connection handle.
*/
static void gatt_error_handler(uint32_t nrf_error,
void * p_ctx,
uint16_t conn_handle)
{
ble_hts_t * p_hts = (ble_hts_t *)p_ctx;
if (p_hts->error_handler != NULL)
{
p_hts->error_handler(nrf_error);
}
}
/**@brief Function for handling the Connect event.
*
* @param[in] p_hts Health Thermometer Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_connect(ble_hts_t * p_hts, ble_evt_t const * p_ble_evt)
{
ret_code_t err_code;
p_hts->conn_handle = p_ble_evt->evt.gatts_evt.conn_handle;
err_code = nrf_ble_gq_conn_handle_register(p_hts->p_gatt_queue, p_hts->conn_handle);
if ((p_hts->error_handler != NULL) &&
(err_code != NRF_SUCCESS))
{
p_hts->error_handler(err_code);
}
}
/**@brief Function for handling the Disconnect event.
*
* @param[in] p_hts Health Thermometer Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_disconnect(ble_hts_t * p_hts, ble_evt_t const * p_ble_evt)
{
UNUSED_PARAMETER(p_ble_evt);
p_hts->conn_handle = BLE_CONN_HANDLE_INVALID;
}
/**@brief Function for handling write events to the Blood Pressure Measurement characteristic.
*
* @param[in] p_hts Health Thermometer Service structure.
* @param[in] p_evt_write Write event received from the BLE stack.
*/
static void on_cccd_write(ble_hts_t * p_hts, ble_gatts_evt_write_t const * p_evt_write)
{
if (p_evt_write->len == 2)
{
// CCCD written, update indication state
if (p_hts->evt_handler != NULL)
{
ble_hts_evt_t evt;
if (ble_srv_is_indication_enabled(p_evt_write->data))
{
evt.evt_type = BLE_HTS_EVT_INDICATION_ENABLED;
}
else
{
evt.evt_type = BLE_HTS_EVT_INDICATION_DISABLED;
}
p_hts->evt_handler(p_hts, &evt);
}
}
}
/**@brief Function for handling the Write event.
*
* @param[in] p_hts Health Thermometer Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_write(ble_hts_t * p_hts, ble_evt_t const * p_ble_evt)
{
ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
if (p_evt_write->handle == p_hts->meas_handles.cccd_handle)
{
on_cccd_write(p_hts, p_evt_write);
}
}
/**@brief Function for handling the HVC event.
*
* @details Handles HVC events from the BLE stack.
*
* @param[in] p_hts Health Thermometer Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_hvc(ble_hts_t * p_hts, ble_evt_t const * p_ble_evt)
{
ble_gatts_evt_hvc_t const * p_hvc = &p_ble_evt->evt.gatts_evt.params.hvc;
if (p_hvc->handle == p_hts->meas_handles.value_handle)
{
ble_hts_evt_t evt;
evt.evt_type = BLE_HTS_EVT_INDICATION_CONFIRMED;
p_hts->evt_handler(p_hts, &evt);
}
}
void ble_hts_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
ble_hts_t * p_hts = (ble_hts_t *)p_context;
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
on_connect(p_hts, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnect(p_hts, p_ble_evt);
break;
case BLE_GATTS_EVT_WRITE:
on_write(p_hts, p_ble_evt);
break;
case BLE_GATTS_EVT_HVC:
on_hvc(p_hts, p_ble_evt);
break;
default:
// No implementation needed.
break;
}
}
/**@brief Function for encoding a Health Thermometer Measurement.
*
* @param[in] p_hts Health Thermometer Service structure.
* @param[in] p_hts_meas Measurement to be encoded.
* @param[out] p_encoded_buffer Buffer where the encoded data will be written.
*
* @return Size of encoded data.
*/
static uint8_t hts_measurement_encode(ble_hts_t * p_hts,
ble_hts_meas_t * p_hts_meas,
uint8_t * p_encoded_buffer)
{
uint8_t flags = 0;
uint8_t len = 1;
uint32_t encoded_temp;
// Flags field
if (p_hts_meas->temp_in_fahr_units)
{
flags |= HTS_MEAS_FLAG_TEMP_UNITS_BIT;
}
if (p_hts_meas->time_stamp_present)
{
flags |= HTS_MEAS_FLAG_TIME_STAMP_BIT;
}
// Temperature Measurement Value field
if (p_hts_meas->temp_in_fahr_units)
{
flags |= HTS_MEAS_FLAG_TEMP_UNITS_BIT;
encoded_temp = ((p_hts_meas->temp_in_fahr.exponent << 24) & 0xFF000000) |
((p_hts_meas->temp_in_fahr.mantissa << 0) & 0x00FFFFFF);
}
else
{
encoded_temp = ((p_hts_meas->temp_in_celcius.exponent << 24) & 0xFF000000) |
((p_hts_meas->temp_in_celcius.mantissa << 0) & 0x00FFFFFF);
}
len += uint32_encode(encoded_temp, &p_encoded_buffer[len]);
// Time Stamp field
if (p_hts_meas->time_stamp_present)
{
flags |= HTS_MEAS_FLAG_TIME_STAMP_BIT;
len += ble_date_time_encode(&p_hts_meas->time_stamp, &p_encoded_buffer[len]);
}
// Temperature Type field
if (p_hts_meas->temp_type_present)
{
flags |= HTS_MEAS_FLAG_TEMP_TYPE_BIT;
p_encoded_buffer[len++] = p_hts_meas->temp_type;
}
// Flags field
p_encoded_buffer[0] = flags;
return len;
}
uint32_t ble_hts_init(ble_hts_t * p_hts, ble_hts_init_t const * p_hts_init)
{
VERIFY_PARAM_NOT_NULL(p_hts);
VERIFY_PARAM_NOT_NULL(p_hts_init);
VERIFY_PARAM_NOT_NULL(p_hts_init->p_gatt_queue);
uint32_t err_code;
uint8_t init_value[MAX_HTM_LEN];
ble_hts_meas_t initial_htm;
ble_uuid_t ble_uuid;
ble_add_char_params_t add_char_params;
// Initialize service structure
p_hts->evt_handler = p_hts_init->evt_handler;
p_hts->p_gatt_queue = p_hts_init->p_gatt_queue;
p_hts->error_handler = p_hts_init->error_handler;
p_hts->conn_handle = BLE_CONN_HANDLE_INVALID;
p_hts->temp_type = p_hts_init->temp_type;
// Add service
BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_HEALTH_THERMOMETER_SERVICE);
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_hts->service_handle);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
// Add measurement characteristic
memset(&add_char_params, 0, sizeof(add_char_params));
memset(&initial_htm, 0, sizeof(initial_htm));
add_char_params.uuid = BLE_UUID_TEMPERATURE_MEASUREMENT_CHAR;
add_char_params.init_len = hts_measurement_encode(p_hts, &initial_htm, init_value);
add_char_params.max_len = MAX_HTM_LEN;
add_char_params.p_init_value = init_value;
add_char_params.is_var_len = true;
add_char_params.char_props.indicate = 1;
add_char_params.cccd_write_access = p_hts_init->ht_meas_cccd_wr_sec;
err_code = characteristic_add(p_hts->service_handle, &add_char_params, &p_hts->meas_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
// Add temperature type characteristic
if (p_hts_init->temp_type_as_characteristic)
{
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = BLE_UUID_TEMPERATURE_TYPE_CHAR;
add_char_params.max_len = sizeof(uint8_t);
add_char_params.char_props.read = 1;
add_char_params.read_access = p_hts_init->ht_type_rd_sec;
add_char_params.init_len = sizeof(uint8_t);
add_char_params.p_init_value = (uint8_t *) &(p_hts_init->temp_type);
err_code = characteristic_add(p_hts->service_handle,
&add_char_params,
&p_hts->temp_type_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
}
return NRF_SUCCESS;
}
uint32_t ble_hts_measurement_send(ble_hts_t * p_hts, ble_hts_meas_t * p_hts_meas)
{
uint32_t err_code;
// Send value if connected
if (p_hts->conn_handle != BLE_CONN_HANDLE_INVALID)
{
uint8_t encoded_hts_meas[MAX_HTM_LEN];
uint16_t len;
nrf_ble_gq_req_t hts_req;
len = hts_measurement_encode(p_hts, p_hts_meas, encoded_hts_meas);
memset(&hts_req, 0, sizeof(nrf_ble_gq_req_t));
hts_req.type = NRF_BLE_GQ_REQ_GATTS_HVX;
hts_req.error_handler.cb = gatt_error_handler;
hts_req.error_handler.p_ctx = p_hts;
hts_req.params.gatts_hvx.handle = p_hts->meas_handles.value_handle;
hts_req.params.gatts_hvx.offset = 0;
hts_req.params.gatts_hvx.p_data = encoded_hts_meas;
hts_req.params.gatts_hvx.p_len = &len;
hts_req.params.gatts_hvx.type = BLE_GATT_HVX_INDICATION;
err_code = nrf_ble_gq_item_add(p_hts->p_gatt_queue, &hts_req, p_hts->conn_handle);
}
else
{
err_code = NRF_ERROR_INVALID_STATE;
}
return err_code;
}
uint32_t ble_hts_is_indication_enabled(ble_hts_t * p_hts, bool * p_indication_enabled)
{
uint32_t err_code;
uint8_t cccd_value_buf[BLE_CCCD_VALUE_LEN];
ble_gatts_value_t gatts_value;
// Initialize value struct.
memset(&gatts_value, 0, sizeof(gatts_value));
gatts_value.len = BLE_CCCD_VALUE_LEN;
gatts_value.offset = 0;
gatts_value.p_value = cccd_value_buf;
err_code = sd_ble_gatts_value_get(p_hts->conn_handle,
p_hts->meas_handles.cccd_handle,
&gatts_value);
if (err_code == NRF_SUCCESS)
{
*p_indication_enabled = ble_srv_is_indication_enabled(cccd_value_buf);
}
if (err_code == BLE_ERROR_GATTS_SYS_ATTR_MISSING)
{
*p_indication_enabled = false;
return NRF_SUCCESS;
}
return err_code;
}
#endif // NRF_MODULE_ENABLED(BLE_HTS)

View File

@@ -0,0 +1,224 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_hts Health Thermometer Service
* @{
* @ingroup ble_sdk_srv
* @brief Health Thermometer Service module.
*
* @details This module implements the Health Thermometer Service.
*
* If an event handler is supplied by the application, the Health Thermometer
* Service will generate Health Thermometer Service events to the application.
*
* @note The application must register this module as BLE event observer using the
* NRF_SDH_BLE_OBSERVER macro. Example:
* @code
* ble_hts_t instance;
* NRF_SDH_BLE_OBSERVER(anything, BLE_HTS_BLE_OBSERVER_PRIO,
* ble_hts_on_ble_evt, &instance);
* @endcode
*
* @note Attention!
* To maintain compliance with Nordic Semiconductor ASA Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#ifndef BLE_HTS_H__
#define BLE_HTS_H__
#include <stdint.h>
#include <stdbool.h>
#include "ble.h"
#include "ble_srv_common.h"
#include "ble_date_time.h"
#include "nrf_sdh_ble.h"
#include "nrf_ble_gq.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Macro for defining a ble_hts instance.
*
* @param _name Name of the instance.
* @hideinitializer
*/
#define BLE_HTS_DEF(_name) \
static ble_hts_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_HTS_BLE_OBSERVER_PRIO, \
ble_hts_on_ble_evt, &_name)
// Temperature Type measurement locations
#define BLE_HTS_TEMP_TYPE_ARMPIT 1
#define BLE_HTS_TEMP_TYPE_BODY 2
#define BLE_HTS_TEMP_TYPE_EAR 3
#define BLE_HTS_TEMP_TYPE_FINGER 4
#define BLE_HTS_TEMP_TYPE_GI_TRACT 5
#define BLE_HTS_TEMP_TYPE_MOUTH 6
#define BLE_HTS_TEMP_TYPE_RECTUM 7
#define BLE_HTS_TEMP_TYPE_TOE 8
#define BLE_HTS_TEMP_TYPE_EAR_DRUM 9
/**@brief Health Thermometer Service event type. */
typedef enum
{
BLE_HTS_EVT_INDICATION_ENABLED, /**< Health Thermometer value indication enabled event. */
BLE_HTS_EVT_INDICATION_DISABLED, /**< Health Thermometer value indication disabled event. */
BLE_HTS_EVT_INDICATION_CONFIRMED /**< Confirmation of a temperature measurement indication has been received. */
} ble_hts_evt_type_t;
/**@brief Health Thermometer Service event. */
typedef struct
{
ble_hts_evt_type_t evt_type; /**< Type of event. */
} ble_hts_evt_t;
// Forward declaration of the ble_hts_t type.
typedef struct ble_hts_s ble_hts_t;
/**@brief Health Thermometer Service event handler type. */
typedef void (*ble_hts_evt_handler_t) (ble_hts_t * p_hts, ble_hts_evt_t * p_evt);
/**@brief FLOAT format (IEEE-11073 32-bit FLOAT, defined as a 32-bit value with a 24-bit mantissa
* and an 8-bit exponent. */
typedef struct
{
int8_t exponent; /**< Base 10 exponent */
int32_t mantissa; /**< Mantissa, should be using only 24 bits */
} ieee_float32_t;
/**@brief Health Thermometer Service init structure. This contains all options and data
* needed for initialization of the service. */
typedef struct
{
ble_hts_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Health Thermometer Service. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
security_req_t ht_meas_cccd_wr_sec; /**< Security requirement for writing health thermometer measurement characteristic CCCD. */
security_req_t ht_type_rd_sec; /**< Security requirement for reading health thermometer type characteristic. */
uint8_t temp_type_as_characteristic; /**< Set non-zero if temp type given as characteristic */
uint8_t temp_type; /**< Temperature type if temperature characteristic is used */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to BLE GATT Queue instance. */
} ble_hts_init_t;
/**@brief Health Thermometer Service structure. This contains various status information for
* the service. */
struct ble_hts_s
{
ble_hts_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Health Thermometer Service. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
uint16_t service_handle; /**< Handle of Health Thermometer Service (as provided by the BLE stack). */
ble_gatts_char_handles_t meas_handles; /**< Handles related to the Health Thermometer Measurement characteristic. */
ble_gatts_char_handles_t temp_type_handles; /**< Handles related to the Health Thermometer Temperature Type characteristic. */
uint16_t conn_handle; /**< Handle of the current connection (as provided by the BLE stack, is BLE_CONN_HANDLE_INVALID if not in a connection). */
uint8_t temp_type; /**< Temperature type indicates where the measurement was taken. */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to BLE GATT Queue instance. */
};
/**@brief Health Thermometer Service measurement structure. This contains a Health Thermometer
* measurement. */
typedef struct ble_hts_meas_s
{
bool temp_in_fahr_units; /**< True if Temperature is in Fahrenheit units, Celcius otherwise. */
bool time_stamp_present; /**< True if Time Stamp is present. */
bool temp_type_present; /**< True if Temperature Type is present. */
ieee_float32_t temp_in_celcius; /**< Temperature Measurement Value (Celcius). */
ieee_float32_t temp_in_fahr; /**< Temperature Measurement Value (Fahrenheit). */
ble_date_time_t time_stamp; /**< Time Stamp. */
uint8_t temp_type; /**< Temperature Type. */
} ble_hts_meas_t;
/**@brief Function for initializing the Health Thermometer Service.
*
* @param[out] p_hts Health Thermometer Service structure. This structure will have to
* be supplied by the application. It will be initialized by this function,
* and will later be used to identify this particular service instance.
* @param[in] p_hts_init Information needed to initialize the service.
*
* @return NRF_SUCCESS on successful initialization of service, otherwise an error code.
*/
uint32_t ble_hts_init(ble_hts_t * p_hts, const ble_hts_init_t * p_hts_init);
/**@brief Function for handling the Application's BLE Stack events.
*
* @details Handles all events from the BLE stack of interest to the Health Thermometer Service.
*
* @param[in] p_ble_evt Event received from the BLE stack.
* @param[in] p_context Health Thermometer Service structure.
*/
void ble_hts_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for sending health thermometer measurement if indication has been enabled.
*
* @details The application calls this function after having performed a Health Thermometer
* measurement. If indication has been enabled, the measurement data is encoded and
* sent to the client.
*
* @param[in] p_hts Health Thermometer Service structure.
* @param[in] p_hts_meas Pointer to new health thermometer measurement.
*
* @return NRF_SUCCESS on success, otherwise an error code.
*/
uint32_t ble_hts_measurement_send(ble_hts_t * p_hts, ble_hts_meas_t * p_hts_meas);
/**@brief Function for checking if indication of Temperature Measurement is currently enabled.
*
* @param[in] p_hts Health Thermometer Service structure.
* @param[out] p_indication_enabled TRUE if indication is enabled, FALSE otherwise.
*
* @return NRF_SUCCESS on success, otherwise an error code.
*/
uint32_t ble_hts_is_indication_enabled(ble_hts_t * p_hts, bool * p_indication_enabled);
#ifdef __cplusplus
}
#endif
#endif // BLE_HTS_H__
/** @} */

View File

@@ -0,0 +1,200 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* Attention!
* To maintain compliance with Nordic Semiconductor ASA's Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_IAS)
#include "ble_ias.h"
#include <string.h>
#include "ble_srv_common.h"
#define NRF_LOG_MODULE_NAME ble_ias
#if BLE_IAS_CONFIG_LOG_ENABLED
#define NRF_LOG_LEVEL BLE_IAS_CONFIG_LOG_LEVEL
#define NRF_LOG_INFO_COLOR BLE_IAS_CONFIG_INFO_COLOR
#define NRF_LOG_DEBUG_COLOR BLE_IAS_CONFIG_DEBUG_COLOR
#else // BLE_IAS_CONFIG_LOG_ENABLED
#define NRF_LOG_LEVEL 0
#endif // BLE_IAS_CONFIG_LOG_ENABLED
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();
#define INITIAL_ALERT_LEVEL BLE_CHAR_ALERT_LEVEL_NO_ALERT
/**@brief Function for handling the Connect event.
*
* @param[in] p_ias Immediate Alert Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_connect(ble_ias_t * p_ias, ble_evt_t const * p_ble_evt)
{
ret_code_t err_code;
ble_ias_client_context_t * p_client = NULL;
err_code = blcm_link_ctx_get(p_ias->p_link_ctx_storage,
p_ble_evt->evt.gap_evt.conn_handle,
(void *) &p_client);
if (err_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("Link context for 0x%02X connection handle could not be fetched.",
p_ble_evt->evt.gap_evt.conn_handle);
}
else
{
p_client->alert_level = INITIAL_ALERT_LEVEL;
}
}
/**@brief Function for handling the Write event.
*
* @param[in] p_ias Immediate Alert Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_write(ble_ias_t * p_ias, ble_evt_t const * p_ble_evt)
{
ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
if ((p_evt_write->handle == p_ias->alert_level_handles.value_handle) && (p_evt_write->len == 1))
{
// Alert level written, call application event handler
ret_code_t err_code;
ble_ias_evt_t evt;
ble_ias_client_context_t * p_client;
err_code = blcm_link_ctx_get(p_ias->p_link_ctx_storage,
p_ble_evt->evt.gatts_evt.conn_handle,
(void *) &p_client);
if (err_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("Link context for 0x%02X connection handle could not be fetched.",
p_ble_evt->evt.gatts_evt.conn_handle);
}
else
{
p_client->alert_level = p_evt_write->data[0];
}
evt.evt_type = BLE_IAS_EVT_ALERT_LEVEL_UPDATED;
evt.conn_handle = p_ble_evt->evt.gatts_evt.conn_handle;
evt.p_link_ctx = p_client;
p_ias->evt_handler(p_ias, &evt);
}
}
void ble_ias_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
ble_ias_t * p_ias = (ble_ias_t *)p_context;
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
on_connect(p_ias, p_ble_evt);
break;
case BLE_GATTS_EVT_WRITE:
on_write(p_ias, p_ble_evt);
break;
default:
// No implementation needed.
break;
}
}
uint32_t ble_ias_init(ble_ias_t * p_ias, const ble_ias_init_t * p_ias_init)
{
uint32_t err_code;
uint8_t initial_alert_level;
ble_uuid_t ble_uuid;
ble_add_char_params_t add_char_params;
// Initialize service structure
if (p_ias_init->evt_handler == NULL)
{
return NRF_ERROR_INVALID_PARAM;
}
p_ias->evt_handler = p_ias_init->evt_handler;
// Add service
BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_IMMEDIATE_ALERT_SERVICE);
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
&ble_uuid,
&p_ias->service_handle);
VERIFY_SUCCESS(err_code);
// Add alert level characteristic
initial_alert_level = INITIAL_ALERT_LEVEL;
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = BLE_UUID_ALERT_LEVEL_CHAR;
add_char_params.max_len = sizeof (uint8_t);
add_char_params.init_len = sizeof (uint8_t);
add_char_params.p_init_value = &initial_alert_level;
add_char_params.char_props.write_wo_resp = 1;
add_char_params.write_access = p_ias_init->alert_wr_sec;
return characteristic_add(p_ias->service_handle,
&add_char_params,
&p_ias->alert_level_handles);
}
uint32_t ble_ias_alert_level_get(ble_ias_t * p_ias, uint16_t conn_handle, uint8_t * p_alert_level)
{
ret_code_t err_code;
ble_ias_client_context_t * p_client;
err_code = blcm_link_ctx_get(p_ias->p_link_ctx_storage, conn_handle, (void *) &p_client);
VERIFY_SUCCESS(err_code);
*p_alert_level = p_client->alert_level;
return NRF_SUCCESS;
}
#endif // NRF_MODULE_ENABLED(BLE_IAS)

View File

@@ -0,0 +1,196 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_ias Immediate Alert Service
* @{
* @ingroup ble_sdk_srv
* @brief Immediate Alert Service module.
*
* @details This module implements the Immediate Alert Service with the Alert Level characteristic.
* During initialization it adds the Immediate Alert Service and Alert Level characteristic
* to the BLE stack database.
*
* The application must supply an event handler for receiving Immediate Alert Service
* events. Using this handler, the service will notify the application when the
* Alert Level characteristic value changes.
*
* The service also provides a function for letting the application poll the current
* value of the Alert Level characteristic.
*
* @note The application must register this module as BLE event observer using the
* NRF_SDH_BLE_OBSERVER macro. Example:
* @code
* ble_ias_t instance;
* NRF_SDH_BLE_OBSERVER(anything, BLE_IAS_BLE_OBSERVER_PRIO,
* ble_ias_on_ble_evt, &instance);
* @endcode
*
* @note Attention!
* To maintain compliance with Nordic Semiconductor ASA Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#ifndef BLE_IAS_H__
#define BLE_IAS_H__
#include <stdint.h>
#include "ble.h"
#include "nrf_sdh_ble.h"
#include "ble_link_ctx_manager.h"
#include "ble_srv_common.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Macro for defining a ble_ias instance.
*
* @param _name Name of the instance.
* @param[in] _ias_max_clients Maximum number of IAS clients connected at a time.
* @hideinitializer
*/
#define BLE_IAS_DEF(_name, _ias_max_clients) \
BLE_LINK_CTX_MANAGER_DEF(CONCAT_2(_name, _link_ctx_storage), \
(_ias_max_clients), \
sizeof(ble_ias_client_context_t)); \
static ble_ias_t _name = \
{ \
.p_link_ctx_storage = &CONCAT_2(_name, _link_ctx_storage) \
}; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_IAS_BLE_OBSERVER_PRIO, \
ble_ias_on_ble_evt, \
&_name)
/**@brief Immediate Alert Service event type. */
typedef enum
{
BLE_IAS_EVT_ALERT_LEVEL_UPDATED /**< Alert Level Updated event. */
} ble_ias_evt_type_t;
/**@brief Immediate Alert Service client context structure.
*
* @details This structure contains state context related to hosts.
*/
typedef struct
{
uint8_t alert_level; /**< New Alert Level value. */
} ble_ias_client_context_t;
/**@brief Immediate Alert Service event. */
typedef struct
{
ble_ias_evt_type_t evt_type; /**< Type of event. */
uint16_t conn_handle; /**< Connection handle. */
ble_ias_client_context_t * p_link_ctx; /**< A pointer to the link context. */
} ble_ias_evt_t;
// Forward declaration of the ble_ias_t type.
typedef struct ble_ias_s ble_ias_t;
/**@brief Immediate Alert Service event handler type. */
typedef void (*ble_ias_evt_handler_t) (ble_ias_t * p_ias, ble_ias_evt_t * p_evt);
/**@brief Immediate Alert Service init structure. This contains all options and data needed for
* initialization of the service. */
typedef struct
{
ble_ias_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Immediate Alert Service. */
security_req_t alert_wr_sec; /**< Security requirement for writing Alert Level characteristic. */
} ble_ias_init_t;
/**@brief Immediate Alert Service structure. This contains various status information for the
* service. */
struct ble_ias_s
{
ble_ias_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Immediate Alert Service. */
uint16_t service_handle; /**< Handle of Immediate Alert Service (as provided by the BLE stack). */
ble_gatts_char_handles_t alert_level_handles; /**< Handles related to the Alert Level characteristic. */
blcm_link_ctx_storage_t * const p_link_ctx_storage; /**< Pointer to link context storage with handles of all current connections and its context. */
};
/**@brief Function for initializing the Immediate Alert Service.
*
* @param[out] p_ias Immediate Alert Service structure. This structure will have to be
* supplied by the application. It will be initialized by this function,
* and will later be used to identify this particular service instance.
* @param[in] p_ias_init Information needed to initialize the service.
*
* @return NRF_SUCCESS on successful initialization of service, otherwise an error code.
*/
uint32_t ble_ias_init(ble_ias_t * p_ias, const ble_ias_init_t * p_ias_init);
/**@brief Function for handling the Application's BLE Stack events.
*
* @details Handles all events from the BLE stack of interest to the Immediate Alert Service.
*
* @param[in] p_ble_evt Event received from the BLE stack.
* @param[in] p_context Immediate Alert Service structure.
*/
void ble_ias_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for getting value of the Alert Level characteristic.
*
* @param[in] p_ias Immediate Alert Service structure.
* @param[in] conn_handle Connection handle of the destination client.
* @param[out] p_alert_level Alert Level value which has been set by the specific client.
*/
uint32_t ble_ias_alert_level_get(ble_ias_t * p_ias, uint16_t conn_handle, uint8_t * p_alert_level);
#ifdef __cplusplus
}
#endif
#endif // BLE_IAS_H__
/** @} */

View File

@@ -0,0 +1,247 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_IAS_C)
#include "ble_ias_c.h"
#include <string.h>
#include "ble.h"
#include "ble_srv_common.h"
#include "ble_gattc.h"
#include "ble_db_discovery.h"
/**@brief Function for intercepting the errors of GATTC and the BLE GATT Queue.
*
* @param[in] nrf_error Error code.
* @param[in] p_ctx Parameter from the event handler.
* @param[in] conn_handle Connection handle.
*/
static void gatt_error_handler(uint32_t nrf_error,
void * p_ctx,
uint16_t conn_handle)
{
UNUSED_PARAMETER(conn_handle);
ble_ias_c_t * p_ias_c = (ble_ias_c_t *)p_ctx;
if (p_ias_c->error_handler != NULL)
{
p_ias_c->error_handler(nrf_error);
}
}
void ble_ias_c_on_db_disc_evt(ble_ias_c_t * p_ias_c, ble_db_discovery_evt_t const * p_evt)
{
ble_ias_c_evt_t evt;
memset(&evt, 0, sizeof(ble_ias_c_evt_t));
evt.conn_handle = p_evt->conn_handle;
ble_gatt_db_char_t const * p_chars = p_evt->params.discovered_db.charateristics;
// Check if the Immediate Alert Service was discovered.
if ( (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE)
&& (p_evt->params.discovered_db.srv_uuid.uuid == BLE_UUID_IMMEDIATE_ALERT_SERVICE)
&& (p_evt->params.discovered_db.srv_uuid.type == BLE_UUID_TYPE_BLE))
{
for (uint32_t i = 0; i < p_evt->params.discovered_db.char_count; i++)
{
// The Alert Level characteristic in the Immediate Alert Service instance is found
// on peer. Check if it has the correct property 'Write without response'.
switch (p_chars[i].characteristic.uuid.uuid)
{
case BLE_UUID_ALERT_LEVEL_CHAR:
if (p_chars[i].characteristic.char_props.write_wo_resp)
{
// Found Alert Level characteristic inside the Immediate Alert Service.
memcpy(&evt.alert_level,
&p_chars[i].characteristic,
sizeof(ble_gattc_char_t));
}
break;
default:
break;
}
}
}
else if ((p_evt->evt_type == BLE_DB_DISCOVERY_SRV_NOT_FOUND) ||
(p_evt->evt_type == BLE_DB_DISCOVERY_ERROR))
{
evt.evt_type = BLE_IAS_C_EVT_DISCOVERY_FAILED;
}
else
{
return;
}
if (evt.alert_level.handle_value != BLE_GATT_HANDLE_INVALID)
{
evt.evt_type = BLE_IAS_C_EVT_DISCOVERY_COMPLETE;
}
p_ias_c->evt_handler(p_ias_c, &evt);
}
uint32_t ble_ias_c_init(ble_ias_c_t * p_ias_c, ble_ias_c_init_t const * p_ias_c_init)
{
VERIFY_PARAM_NOT_NULL(p_ias_c);
VERIFY_PARAM_NOT_NULL(p_ias_c_init);
VERIFY_PARAM_NOT_NULL(p_ias_c_init->evt_handler);
VERIFY_PARAM_NOT_NULL(p_ias_c_init->p_gatt_queue);
p_ias_c->evt_handler = p_ias_c_init->evt_handler;
p_ias_c->error_handler = p_ias_c_init->error_handler;
p_ias_c->p_gatt_queue = p_ias_c_init->p_gatt_queue;
p_ias_c->conn_handle = BLE_CONN_HANDLE_INVALID;
p_ias_c->alert_level_char.handle_value = BLE_GATT_HANDLE_INVALID;
BLE_UUID_BLE_ASSIGN(p_ias_c->alert_level_char.uuid, BLE_UUID_ALERT_LEVEL_CHAR);
BLE_UUID_BLE_ASSIGN(p_ias_c->service_uuid, BLE_UUID_IMMEDIATE_ALERT_SERVICE);
return ble_db_discovery_evt_register(&p_ias_c->service_uuid);
}
/**@brief Function for handling the Disconnect event.
*
* @param[in] p_ias_c Immediate Alert Service client structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_disconnect(ble_ias_c_t * p_ias_c, ble_evt_t const * p_ble_evt)
{
// The following values will be re-initialized when a new connection is made.
p_ias_c->conn_handle = BLE_CONN_HANDLE_INVALID;
if (ble_ias_c_is_discovered(p_ias_c))
{
// There was a valid instance of IAS on the peer. Send an event to the
// application, so that it can do a cleanup related to this module.
ble_ias_c_evt_t evt;
evt.evt_type = BLE_IAS_C_EVT_DISCONN_COMPLETE;
p_ias_c->evt_handler(p_ias_c, &evt);
p_ias_c->alert_level_char.handle_value = BLE_GATT_HANDLE_INVALID;
}
}
void ble_ias_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
uint32_t err_code = NRF_SUCCESS;
ble_ias_c_t * p_ias_c = (ble_ias_c_t *)p_context;
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_DISCONNECTED:
on_disconnect(p_ias_c, p_ble_evt);
break;
default:
// No implementation needed.
break;
}
if ((err_code != NRF_SUCCESS) && (p_ias_c->error_handler != NULL))
{
p_ias_c->error_handler(err_code);
}
}
/**@brief Function for performing a Write procedure.
*
* @param[in] p_ias_c Pointer to Immediate Alert Service client structure.
* @param[in] length Length of data to be written.
* @param[in] p_value Data to be written.
*
* @return NRF_SUCCESS on success, otherwise an error code.
*/
static uint32_t write_characteristic_value(ble_ias_c_t const * const p_ias_c,
uint16_t length,
uint8_t * p_value)
{
nrf_ble_gq_req_t write_req;
memset(&write_req, 0, sizeof(nrf_ble_gq_req_t));
write_req.type = NRF_BLE_GQ_REQ_GATTC_WRITE;
write_req.error_handler.cb = gatt_error_handler;
write_req.error_handler.p_ctx = (ble_ias_c_t *)p_ias_c;
write_req.params.gattc_write.handle = p_ias_c->alert_level_char.handle_value;
write_req.params.gattc_write.write_op = BLE_GATT_OP_WRITE_CMD;
write_req.params.gattc_write.offset = 0;
write_req.params.gattc_write.len = length;
write_req.params.gattc_write.p_value = p_value;
return nrf_ble_gq_item_add(p_ias_c->p_gatt_queue, &write_req, p_ias_c->conn_handle);
}
uint32_t ble_ias_c_send_alert_level(ble_ias_c_t const * p_ias_c, uint8_t alert_level)
{
if (!ble_ias_c_is_discovered(p_ias_c))
{
return NRF_ERROR_NOT_FOUND;
}
return write_characteristic_value(p_ias_c,
sizeof(uint8_t),
&alert_level);
}
uint32_t ble_ias_c_handles_assign(ble_ias_c_t * p_ias_c,
const uint16_t conn_handle,
const uint16_t alert_level_handle)
{
VERIFY_PARAM_NOT_NULL(p_ias_c);
p_ias_c->conn_handle = conn_handle;
p_ias_c->alert_level_char.handle_value = alert_level_handle;
return nrf_ble_gq_conn_handle_register(p_ias_c->p_gatt_queue, conn_handle);
}
#endif //NRF_MODULE_ENABLED(BLE_IAS_C)

View File

@@ -0,0 +1,244 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_ias_c Immediate Alert Service Client
* @{
* @ingroup ble_sdk_srv
* @brief Immediate Alert Service Client module
*
* @details This module implements the Immediate Alert Service client - the locator role of the Find Me
* profile. On @ref BLE_GAP_EVT_CONNECTED event, this module starts discovery of the
* Immediate Alert Service with Alert Level characteristic at the peer. This module will
* inform the application about the successful service and characteristic discovery with
* the @ref BLE_IAS_C_EVT_DISCOVERY_COMPLETE event. The application can use @ref
* ble_ias_c_send_alert_level function to signal alerts to the peer.
*
* @note The application must register this module as the BLE event observer by using the
* NRF_SDH_BLE_OBSERVER macro. Example:
* @code
* ble_ias_c_t instance;
* NRF_SDH_BLE_OBSERVER(anything, BLE_IAS_C_BLE_OBSERVER_PRIO,
* ble_ias_c_on_ble_evt, &instance);
* @endcode
*/
#ifndef BLE_IAS_C_H__
#define BLE_IAS_C_H__
#include <stdint.h>
#include "ble_srv_common.h"
#include "ble_gattc.h"
#include "ble.h"
#include "ble_db_discovery.h"
#include "nrf_ble_gq.h"
#include "nrf_sdh_ble.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Macro for defining a ble_ias_c instance.
*
* @param _name Name of the instance.
* @hideinitializer
*/
#define BLE_IAS_C_DEF(_name) \
static ble_ias_c_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_IAS_C_BLE_OBSERVER_PRIO, \
ble_ias_c_on_ble_evt, &_name)
/** @brief Macro for defining multiple ble_ias_c instances.
*
* @param _name Name of the array of instances.
* @param _cnt Number of instances to define.
* @hideinitializer
*/
#define BLE_IAS_C_ARRAY_DEF(_name, _cnt) \
static ble_ias_c_t _name[_cnt]; \
NRF_SDH_BLE_OBSERVERS(_name ## _obs, \
BLE_IAS_C_BLE_OBSERVER_PRIO, \
ble_ias_c_on_ble_evt, &_name, _cnt)
// Forward declaration of the ble_ias_c_t type.
typedef struct ble_ias_c_s ble_ias_c_t;
/**@brief Immediate Alert Service client event type. */
typedef enum
{
BLE_IAS_C_EVT_DISCOVERY_COMPLETE, /**< Event indicating that the Immediate Alert Service is found at the peer. */
BLE_IAS_C_EVT_DISCOVERY_FAILED, /**< Event indicating that the Immediate Alert Service is not found at the peer. */
BLE_IAS_C_EVT_DISCONN_COMPLETE /**< Event indicating that the Immediate Alert Service Client module completed the processing of BLE_GAP_EVT_DISCONNECTED event. This event is triggered only if a valid instance of IAS was found at the peer during the discovery phase. The application can use this event to do a cleanup related to the IAS Client.*/
} ble_ias_c_evt_type_t;
/**@brief Immediate Alert Service client event. */
typedef struct
{
ble_ias_c_evt_type_t evt_type; /**< Type of event. */
uint16_t conn_handle; /**< Connection handle on which the IAS service was discovered on the peer device. This is filled if the evt_type is @ref BLE_IAS_C_EVT_DISCOVERY_COMPLETE.*/
ble_gattc_char_t alert_level; /**< Information on the discovered Alert Level characteristic discovered. This is filled if the evt_type is @ref BLE_IAS_C_EVT_DISCOVERY_COMPLETE.*/
} ble_ias_c_evt_t;
/**@brief Immediate Alert Service client event handler type. */
typedef void (*ble_ias_c_evt_handler_t) (ble_ias_c_t * p_ias_c, ble_ias_c_evt_t * p_evt);
/**@brief IAS Client structure. Contains various status information for the client. */
struct ble_ias_c_s
{
ble_ias_c_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Immediate Alert Service client. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
uint16_t conn_handle; /**< Handle of the current connection. Set with @ref ble_ias_c_handles_assign when connected. */
ble_uuid_t service_uuid; /**< The GATT Service holding the discovered Immediate Service. */
ble_gattc_char_t alert_level_char; /**< IAS Alert Level Characteristic. Stores data about the alert characteristic found on the peer. */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to the BLE GATT Queue instance. */
};
/**@brief IAS Client init structure. Contains all options and data needed for the initialization of
* the client.*/
typedef struct
{
ble_ias_c_evt_handler_t evt_handler; /**< Event handler to be called for handling events from the Immediate Alert Service client. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to the BLE GATT Queue instance. */
} ble_ias_c_init_t;
/**@brief Function for initializing the Immediate Alert Service client.
*
* @details This call allows the application to initialize the Immediate Alert Service client.
*
* @param[out] p_ias_c Immediate Alert Service client structure. This structure must
* be supplied by the application. It is initialized by this
* function, and is later used to identify this particular client
* instance.
* @param[in] p_ias_c_init Information needed to initialize the Immediate Alert Service client.
*
* @return NRF_SUCCESS on successful initialization of service.
*/
uint32_t ble_ias_c_init(ble_ias_c_t * p_ias_c, ble_ias_c_init_t const * p_ias_c_init);
/**@brief Function for sending alert level to the peer.
*
* @details This function allows the application to send an alert to the peer.
*
* @param[in] p_ias_c Immediate Alert Service client structure.
* @param[in] alert_level Required alert level to be sent to the peer.
*
* @retval NRF_SUCCESS On success.
* @retval err_code Otherwise, this API propagates the error code returned by function
* @ref nrf_ble_gq_item_add.
*/
uint32_t ble_ias_c_send_alert_level(ble_ias_c_t const * p_ias_c, uint8_t alert_level);
/**@brief Function for handling the Application's BLE Stack events for Immediate Alert Service client.
*
* @details Handles all events from the BLE stack of interest to the Immediate Alert Service client.
*
* @param[in] p_ble_evt Event received from the BLE stack.
* @param[in] p_context Immediate Alert Service client structure.
*/
void ble_ias_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for checking whether the peer's Immediate Alert Service instance and the Alert Level
* characteristic have been discovered.
*
* @param[in] p_ias_c Immediate Alert Service client structure.
*
* @return True, if a handle is assigned to alert_level_handle, meaning it must have been discovered.
* @return False, if the handle is invalid.
*/
static __INLINE bool ble_ias_c_is_discovered(ble_ias_c_t const * p_ias_c)
{
return (p_ias_c->alert_level_char.handle_value != BLE_GATT_HANDLE_INVALID);
}
/**@brief Function for handling events from the Database Discovery module.
*
* @details Call this function when you get a callback event from the Database Discovery module.
* This function handles an event from the Database Discovery module, and determines
* whether it relates to the discovery of Immediate Alert Service at the peer. If it does, the function
* calls the application's event handler to indicate that the Immediate Alert Service was
* discovered at the peer. The function also populates the event with service-related
* information before providing it to the application.
*
* @param[in] p_ias_c Pointer to the Immediate Alert client structure instance that will handle
* the discovery.
* @param[in] p_evt Pointer to the event received from the Database Discovery module.
*
*/
void ble_ias_c_on_db_disc_evt(ble_ias_c_t * p_ias_c, ble_db_discovery_evt_t const * p_evt);
/**@brief Function for assigning handles to an instance of ias_c.
*
* @details Call this function when a link has been established with a peer to
* associate the link to this instance of the module. This makes it
* possible to handle several links and associate each link to a particular
* instance of this module. The connection handle and attribute handles are
* provided from the discovery event @ref BLE_IAS_C_EVT_DISCOVERY_COMPLETE.
*
* @param[in] p_ias_c Pointer to the IAS client structure instance for associating the link.
* @param[in] conn_handle Connection handle to associated with the given IAS instance.
* @param[in] alert_level_handle Attribute handle on the IAS server that you want this IAS_C client to
* interact with.
*
* @retval NRF_SUCCESS If the operation was successful.
* @retval NRF_ERROR_NULL If a p_ias_c was a NULL pointer.
* @retval err_code Otherwise, this API propagates the error code returned by function
* @ref nrf_ble_gq_conn_handle_register.
*/
uint32_t ble_ias_c_handles_assign(ble_ias_c_t * p_ias_c,
uint16_t conn_handle,
uint16_t alert_level_handle);
#ifdef __cplusplus
}
#endif
#endif // BLE_IAS_C_H__
/** @} */

View File

@@ -0,0 +1,981 @@
/**
* Copyright (c) 2014 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* Attention!
* To maintain compliance with Nordic Semiconductor ASA's Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#include <stdbool.h>
#include <string.h>
#include "nordic_common.h"
#include "sdk_errors.h"
#include "nrf.h"
#include "sdk_config.h"
#include "ble_ipsp.h"
#include "ble_srv_common.h"
#include "sdk_os.h"
/**
* @defgroup ble_ipsp_log Module's Log Macros
* @details Macros used for creating module logs which can be useful in understanding handling
* of events or actions on API requests. These are intended for debugging purposes and
* can be enabled by defining the IOT_BLE_IPSP_CONFIG_LOG_ENABLED to 1.
* @note If NRF_LOG_ENABLED is disabled, having IOT_BLE_IPSP_CONFIG_LOG_ENABLED
* has no effect.
* @{
*/
#if IOT_BLE_IPSP_CONFIG_LOG_ENABLED
#define NRF_LOG_MODULE_NAME ipsp
#define NRF_LOG_LEVEL IOT_BLE_IPSP_CONFIG_LOG_LEVEL
#define NRF_LOG_INFO_COLOR IOT_BLE_IPSP_CONFIG_INFO_COLOR
#define NRF_LOG_DEBUG_COLOR IOT_BLE_IPSP_CONFIG_DEBUG_COLOR
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();
#define BLE_IPSP_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */
#define BLE_IPSP_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */
#define BLE_IPSP_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */
#define BLE_IPSP_ENTRY() BLE_IPSP_TRC(">> %s", __func__)
#define BLE_IPSP_EXIT() BLE_IPSP_TRC("<< %s", __func__)
#define BLE_IPSP_EXIT_WITH_RESULT(result) BLE_IPSP_TRC("<< %s, result 0x%08lX", __func__, result)
#else // IOT_BLE_IPSP_CONFIG_LOG_ENABLED
#define BLE_IPSP_TRC(...) /**< Disables traces. */
#define BLE_IPSP_DUMP(...) /**< Disables dumping of octet streams. */
#define BLE_IPSP_ERR(...) /**< Disables error logs. */
#define BLE_IPSP_ENTRY(...)
#define BLE_IPSP_EXIT(...)
#define BLE_IPSP_EXIT_WITH_RESULT(...)
#endif // IOT_BLE_IPSP_CONFIG_LOG_ENABLED
#define IPSP_ANY_CID 0xFFFE /**< Identifier for any channel. Usage: Search for existing channel on a connection handle. */
/**
* @defgroup api_param_check API Parameters check macros.
*
* @details Macros that verify parameters passed to the module in the APIs. These macros
* could be mapped to nothing in final versions of code to save execution and size.
* BLE_HPS_DISABLE_API_PARAM_CHECK should be defined to disable these checks.
*
* @{
*/
#if (BLE_IPSP_DISABLE_API_PARAM_CHECK == 0)
/**@brief Macro to check is module is initialized before requesting one of the module procedures. */
#define VERIFY_MODULE_IS_INITIALIZED() \
if (m_evt_handler == NULL) \
{ \
return (NRF_ERROR_MODULE_NOT_INITIALIZED + NRF_ERROR_BLE_IPSP_ERR_BASE); \
}
/**@brief Macro to check is module is initialized before requesting one of the module
procedures but does not use any return code. */
#define VERIFY_MODULE_IS_INITIALIZED_VOID() \
if (m_evt_handler == NULL) \
{ \
return; \
}
/**@brief Verify NULL parameters are not passed to API by application. */
#define NULL_PARAM_CHECK(PARAM) \
if ((PARAM) == NULL) \
{ \
return (NRF_ERROR_NULL + NRF_ERROR_BLE_IPSP_ERR_BASE); \
}
/**@brief Verify the connection handle passed to the API. */
#define VERIFY_CON_HANDLE(CON_HANDLE) \
if ((CON_HANDLE) == BLE_CONN_HANDLE_INVALID) \
{ \
return (NRF_ERROR_INVALID_PARAM + NRF_ERROR_BLE_IPSP_ERR_BASE); \
}
#else // BLE_IPSP_DISABLE_API_PARAM_CHECK
#define VERIFY_MODULE_IS_INITIALIZED()
#define VERIFY_MODULE_IS_INITIALIZED_VOID()
#define NULL_PARAM_CHECK(PARAM)
#define VERIFY_CON_HANDLE(CON_HANDLE)
#endif //BLE_IPSP_DISABLE_API_PARAM_CHECK
/**
* @defgroup ble_ipsp_mutex_lock_unlock Module's Mutex Lock/Unlock Macros.
*
* @details Macros used to lock and unlock modules. Currently, SDK does not use mutexes but
* framework is provided in case the need to use an alternative architecture arises.
* @{
*/
#define BLE_IPSP_MUTEX_LOCK() SDK_MUTEX_LOCK(m_ipsp_mutex) /**< Lock module using mutex */
#define BLE_IPSP_MUTEX_UNLOCK() SDK_MUTEX_UNLOCK(m_ipsp_mutex) /**< Unlock module using mutex */
/** @} */
#define IPSP_MAX_CONNECTED_DEVICES BLE_IPSP_MAX_CHANNELS /**< Table for maximum number of connected devices the module will keep track of. */
#define RX_BUFFER_TOTAL_SIZE (BLE_IPSP_RX_BUFFER_SIZE * BLE_IPSP_RX_BUFFER_COUNT) /**< Total receive buffer size reserved for each IPSP channel. */
#define MAX_L2CAP_RX_BUFFER (RX_BUFFER_TOTAL_SIZE * BLE_IPSP_MAX_CHANNELS) /**< Total receive buffer received for all channels. */
#define INVALID_CHANNEL_INSTANCE 0xFF /**< Indicates channel instance is invalid. */
/**@brief IPSP Channel States. */
typedef enum
{
CHANNEL_IDLE, /**< Indicates the channel is free and not in use. */
CHANNEL_CONNECTING, /**< Indicates the channel creation is requested and is awaiting a response. */
CHANNEL_CONNECTED, /**< Indicates the channel is connected and ready for data exchange. */
CHANNEL_DISCONNECTING /**< Indicates the channel is in the process of being disconnected. */
} channel_state_t;
/**@brief Possible response actions for an incoming channel. Default is to accept. */
typedef enum
{
INCOMING_CHANNEL_ACCEPT, /**< Indicates that the incoming channel should be accepted if all other criteria are met. */
INCOMING_CHANNEL_REJECT /**< Indicates that the incoming channel for IPSP PSM should be rejected regardless of the other criteria. */
} incoming_channel_action_t;
/**@brief Data type for book keeping connected devices.
*
* @note Not all connected devices establish an L2CAP connection.
*/
typedef struct
{
volatile incoming_channel_action_t response; /**< Indicator if the incoming channel should be accepted or rejected. */
ble_gap_addr_t ble_addr; /**< Bluetooth device address of the peer. */
uint16_t conn_handle; /**< Connection handle identifying the link with the peer. */
} peer_connection_t;
/**@brief IPSP Channel Information. */
typedef struct
{
uint16_t conn_handle; /**< Identifies the BLE link on which channel is established. BLE_CONN_HANDLE_INVALID if channel is unassigned. */
uint16_t cid; /**< L2CAP channel identifier needed to manage the channel once established. BLE_L2CAP_CID_INVALID if channel is unassigned. */
uint16_t rx_buffer_status; /**< Usage status of RX buffers. */
uint8_t state; /**< State information for the channel. See @ref channel_state_t for details. */
uint8_t * p_rx_buffer; /**< Receive buffer for the channel. */
} channel_t;
static ble_ipsp_evt_handler_t m_evt_handler = NULL; /**< Asynchronous event notification callback registered with the module. */
static channel_t m_channel[BLE_IPSP_MAX_CHANNELS]; /**< Table of channels managed by the module. */
static uint8_t m_rx_buffer[MAX_L2CAP_RX_BUFFER]; /**< Receive buffer reserved for all channels to receive data on the L2CAP IPSP channel. */
static peer_connection_t m_connected_device[IPSP_MAX_CONNECTED_DEVICES]; /**< Table maintaining list of peer devices and the connection handle.
\n This information is needed for the 6lowpan compression and decompression.
\n And no interface exists to query the softdevice. */
SDK_MUTEX_DEFINE(m_ipsp_mutex) /**< Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */
/**@brief Initialize the peer connected device in the list.
*
* @param[in] index Identifies the list element to be initialized.
*/
static __INLINE void connected_device_init(uint32_t index)
{
memset (&m_connected_device[index].ble_addr, 0, sizeof(ble_gap_addr_t));
m_connected_device[index].conn_handle = BLE_CONN_HANDLE_INVALID;
m_connected_device[index].response = INCOMING_CHANNEL_ACCEPT;
}
/**@brief Allocate an entry for the peer connected device in the list.
*
* @param[in] p_peer_addr Pointer to peer's device address.
* @param[in] conn_handle Connection handle identifying the link with the peer.
*/
static __INLINE void connected_device_allocate(ble_gap_addr_t const * p_peer_addr,
uint16_t conn_handle)
{
for (uint32_t index = 0; index < IPSP_MAX_CONNECTED_DEVICES; index++)
{
if (m_connected_device[index].conn_handle == BLE_CONN_HANDLE_INVALID)
{
m_connected_device[index].conn_handle = conn_handle;
memcpy(m_connected_device[index].ble_addr.addr, p_peer_addr->addr, BLE_GAP_ADDR_LEN);
break;
}
}
}
/**@brief Search for an entry for the peer connected device in the list.
*
* @param[in] conn_handle Connection handle identifying the link with the peer.
*
* @retval A valid device index in the list if found, else,
* IPSP_MAX_CONNECTED_DEVICES indicating the search failed.
*/
static __INLINE uint32_t connected_device_search(uint16_t conn_handle)
{
for (uint32_t index = 0; index < IPSP_MAX_CONNECTED_DEVICES; index++)
{
if (m_connected_device[index].conn_handle == conn_handle)
{
return index;
}
}
return IPSP_MAX_CONNECTED_DEVICES;
}
/**@brief Initialize channel.
*
* @param[in] ch_id Identifies the IPSP channel on which the procedure is requested.
*/
static __INLINE void channel_init(uint8_t ch_id)
{
m_channel[ch_id].conn_handle = BLE_CONN_HANDLE_INVALID;
m_channel[ch_id].cid = BLE_L2CAP_CID_INVALID;
m_channel[ch_id].rx_buffer_status = 0;
m_channel[ch_id].state = CHANNEL_IDLE;
m_channel[ch_id].p_rx_buffer = &m_rx_buffer[ch_id*RX_BUFFER_TOTAL_SIZE];
}
/**@brief Free channel.
*
* @param[in] ch_id Identifies the IPSP channel on which the procedure is requested.
*/
static __INLINE void channel_free(uint8_t ch_id)
{
BLE_IPSP_TRC("[Index 0x%02X]:[Conn Handle 0x%04X]:[CID 0x%04X]: Freeing channel",
ch_id, m_channel[ch_id].conn_handle, m_channel[ch_id].cid);
channel_init(ch_id);
}
/**@brief Searches the IPSP channel based on connection handle and local L2CAP channel identifier.
*
* @param[in] conn_handle The connection handle, identifying the peer device.
* @param[in] l2cap_cid The local L2CAP channel identifier, identifying the L2CAP channel.
* @param[out] p_ch_id The IPSP channel identifier, if the search succeeded, else,
* BLE_IPSP_MAX_CHANNELS indicating no IPSP channel was found.
*/
static __INLINE uint32_t channel_search(uint16_t conn_handle, uint16_t l2cap_cid, uint8_t * p_ch_id)
{
BLE_IPSP_TRC("[Conn Handle 0x%04X]:[CID 0x%04X]: channel_search",
conn_handle, l2cap_cid);
for (int i = 0; i < BLE_IPSP_MAX_CHANNELS; i++)
{
BLE_IPSP_TRC("[@ Index 0x%02X] ==> Conn Handle: 0x%04X"
" CID : 0x%04X",
i, m_channel[i].conn_handle, m_channel[i].cid);
if (m_channel[i].conn_handle == conn_handle)
{
if ((l2cap_cid == IPSP_ANY_CID) || (m_channel[i].cid == l2cap_cid))
{
BLE_IPSP_TRC("channel_search succeeded, index 0x%04X", i);
*p_ch_id = (uint8_t)i;
return NRF_SUCCESS;
}
}
}
BLE_IPSP_TRC("No matching channel found!");
return (NRF_ERROR_BLE_IPSP_ERR_BASE + NRF_ERROR_NOT_FOUND);
}
/**@brief Notify application of an event.
*
* @param[in] Identifies the IPSP instance for which the event is notified.
* @param[in] Describes the notified event and its parameters, if any.
*/
static __INLINE void app_notify(ble_ipsp_handle_t * p_handle, ble_ipsp_evt_t * p_event)
{
BLE_IPSP_MUTEX_UNLOCK();
BLE_IPSP_TRC("[Conn Handle 0x%04X]:[CID 0x%04X]: Notifying application of event 0x%04X",
p_handle->conn_handle, p_handle->cid, p_event->evt_id);
UNUSED_VARIABLE(m_evt_handler(p_handle, p_event));
BLE_IPSP_MUTEX_LOCK();
}
/**@brief Verifies if the buffer is TX buffer on the channel or not.
*
* @param[in] ch_id Identifies the IPSP channel for which the procedure is requested.
* @param[in] p_buffer Address of the buffer being verified to be TX or not.
*/
static __INLINE bool is_tx_buffer(uint32_t ch_id, const uint8_t * p_buffer)
{
// If the buffer is in the RX buffer list, then it is not TX!
if ((p_buffer >= (uint8_t *)&m_channel[ch_id].p_rx_buffer) &&
(p_buffer < (uint8_t *)&m_channel[ch_id].p_rx_buffer[RX_BUFFER_TOTAL_SIZE]))
{
return false;
}
return true;
}
/**@brief Submit receive buffers to the softdevice for a channel.
*
* @param[in] ch_id Identifies the IPSP channel for which the procedure is requested.
*/
static __INLINE void rx_buffers_submit(uint32_t ch_id)
{
uint32_t retval;
for (uint32_t buffer_index = 0; buffer_index < BLE_IPSP_RX_BUFFER_COUNT; buffer_index++)
{
const ble_data_t sdu_buf =
{
.p_data = (uint8_t *)&m_channel[ch_id].p_rx_buffer[buffer_index * BLE_IPSP_MTU],
.len = BLE_IPSP_MTU
};
if (IS_SET(m_channel[ch_id].rx_buffer_status, buffer_index) == 0)
{
retval = sd_ble_l2cap_ch_rx(m_channel[ch_id].conn_handle,
m_channel[ch_id].cid,
&sdu_buf);
if (retval == NRF_SUCCESS)
{
SET_BIT(m_channel[ch_id].rx_buffer_status, buffer_index);
}
}
}
}
/**@brief Mark a receive buffer as not in use for a particular channel.
*
* @param[in] ch_id Identifies the IPSP channel for which the procedure is requested.
* @param[in] p_buffer The buffer to be marked as unused.
*
* @note This is a temporary state for the receive buffer before it is resubmitted to the SoftDevice.
*/
static __INLINE void rx_buffer_mark_unused(uint32_t ch_id, uint8_t * p_buffer)
{
for (uint32_t buffer_index = 0; buffer_index < BLE_IPSP_RX_BUFFER_COUNT; buffer_index++)
{
if (&m_channel[ch_id].p_rx_buffer[buffer_index * BLE_IPSP_MTU] == p_buffer)
{
CLR_BIT(m_channel[ch_id].rx_buffer_status, buffer_index);
}
}
}
void ble_ipsp_evt_handler(ble_evt_t const * p_evt)
{
VERIFY_MODULE_IS_INITIALIZED_VOID();
ble_ipsp_handle_t handle;
ble_ipsp_evt_t ipsp_event;
uint32_t retval;
uint8_t ch_id;
bool notify_event;
bool submit_rx_buffer;
ch_id = INVALID_CHANNEL_INSTANCE;
notify_event = false;
submit_rx_buffer = false;
retval = NRF_SUCCESS;
ipsp_event.evt_result = NRF_SUCCESS;
handle.conn_handle = BLE_CONN_HANDLE_INVALID;
handle.cid = BLE_L2CAP_CID_INVALID;
BLE_IPSP_TRC("Received BLE Event 0x%04X",p_evt->header.evt_id);
BLE_IPSP_MUTEX_LOCK();
switch (p_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
{
// Create an entry in the connected devices table.
// This is needed to be able to fetch the peer address on IPSP channel establishment.
connected_device_allocate(&p_evt->evt.gap_evt.params.connected.peer_addr,
p_evt->evt.gap_evt.conn_handle);
break;
}
case BLE_L2CAP_EVT_CH_SETUP_REQUEST:
{
// This event is generated for the acceptor role and indicates an channel establishment
// request from the peer.
ble_l2cap_ch_setup_params_t reply_param;
uint16_t local_cid;
memset(&reply_param, 0, sizeof(ble_l2cap_ch_setup_params_t));
reply_param.le_psm = p_evt->evt.l2cap_evt.params.ch_setup_request.le_psm;
reply_param.rx_params.rx_mtu = BLE_IPSP_MTU;
reply_param.rx_params.rx_mps = BLE_IPSP_RX_MPS;
// Check if a channel already exists with the peer.
retval = channel_search(p_evt->evt.l2cap_evt.conn_handle,
IPSP_ANY_CID,
&ch_id);
BLE_IPSP_TRC("Exiting channel_search result 0x%08X", ch_id);
if (retval == NRF_SUCCESS)
{
BLE_IPSP_TRC("Rejecting channel, as IPSP channel already exists "
"0x%08X in state 0x%08X", ch_id, m_channel[ch_id].state);
reply_param.status = BLE_L2CAP_CH_STATUS_CODE_NO_RESOURCES;
// Reinitialize ch_id to invalid so that existing channel is not impacted.
ch_id = INVALID_CHANNEL_INSTANCE;
}
else
{
uint32_t peer_device_index = connected_device_search(p_evt->evt.l2cap_evt.conn_handle);
local_cid = p_evt->evt.l2cap_evt.local_cid;
if (p_evt->evt.l2cap_evt.params.ch_setup_request.le_psm != BLE_IPSP_PSM)
{
reply_param.status = BLE_L2CAP_CH_STATUS_CODE_LE_PSM_NOT_SUPPORTED;
BLE_IPSP_TRC("Rejecting L2CAP Channel, unknown PSM %04X!",
p_evt->evt.l2cap_evt.params.ch_setup_request.le_psm);
}
else if ((peer_device_index != IPSP_MAX_CONNECTED_DEVICES) &&
(m_connected_device[peer_device_index].response == INCOMING_CHANNEL_REJECT))
{
reply_param.status = BLE_L2CAP_CH_STATUS_CODE_NO_RESOURCES;
BLE_IPSP_ERR("Barred incoming requests by the application. "
"Rejecting L2CAP Channel %04X!",
p_evt->evt.l2cap_evt.params.ch_setup_request.le_psm);
}
else if (p_evt->evt.l2cap_evt.params.ch_setup_request.tx_params.tx_mtu < BLE_IPSP_MTU)
{
reply_param.status = BLE_L2CAP_CH_STATUS_CODE_UNACCEPTABLE_PARAMS;
BLE_IPSP_TRC("Rejecting L2CAP Channel, unacceptable TX parameters!");
}
else
{
// Peer request acceptable, look for a free channel.
retval = channel_search(BLE_CONN_HANDLE_INVALID, BLE_L2CAP_CID_INVALID, &ch_id);
BLE_IPSP_TRC("Free channel search result 0x%08X", ch_id);
if (retval != NRF_SUCCESS)
{
BLE_IPSP_TRC("Rejecting L2CAP Channel, no resources!");
reply_param.status = BLE_L2CAP_CH_STATUS_CODE_NO_RESOURCES;
}
else
{
BLE_IPSP_TRC("Accepting L2CAP Channel");
reply_param.rx_params.sdu_buf.p_data = NULL;
reply_param.rx_params.sdu_buf.len = 0;
reply_param.status = BLE_L2CAP_CH_STATUS_CODE_SUCCESS;
}
}
}
retval = sd_ble_l2cap_ch_setup(p_evt->evt.l2cap_evt.conn_handle,
&local_cid,
&reply_param);
BLE_IPSP_TRC("sd_ble_l2cap_ch_setup result = 0x%08lX", retval);
if ((retval == NRF_SUCCESS) &&
(reply_param.status == BLE_L2CAP_CH_STATUS_CODE_SUCCESS) &&
(ch_id != INVALID_CHANNEL_INSTANCE))
{
BLE_IPSP_TRC("[0x%04X][0x%04X]: Channel Connected. Rx MPS = 0x%04X",
p_evt->evt.l2cap_evt.conn_handle,
p_evt->evt.l2cap_evt.local_cid,
reply_param.rx_params.rx_mps);
ipsp_event.evt_id = BLE_IPSP_EVT_CHANNEL_CONNECTED;
ipsp_event.evt_result = NRF_SUCCESS;
// Channel is assigned to this link.
m_channel[ch_id].state = CHANNEL_CONNECTING;
m_channel[ch_id].conn_handle = p_evt->evt.l2cap_evt.conn_handle;
m_channel[ch_id].cid = local_cid;
}
else if (ch_id != INVALID_CHANNEL_INSTANCE)
{
// Free the allocated channel.
channel_init(ch_id);
}
break;
}
case BLE_L2CAP_EVT_CH_SETUP:
{
// This event is generated for both initiator and acceptor roles.
// This event indicates that the IPSP channel is successfully established.
retval = channel_search(p_evt->evt.l2cap_evt.conn_handle,
p_evt->evt.l2cap_evt.local_cid,
&ch_id);
if (retval != NRF_SUCCESS)
{
BLE_IPSP_TRC("Reply on unknown channel, dropping the event.");
}
else
{
if (m_channel[ch_id].state == CHANNEL_CONNECTING)
{
// Channel created successfully.
// Initialize IPSP handle.
handle.conn_handle = p_evt->evt.l2cap_evt.conn_handle;
handle.cid = p_evt->evt.l2cap_evt.local_cid;
// Initialize the event.
ipsp_event.evt_id = BLE_IPSP_EVT_CHANNEL_CONNECTED;
ipsp_event.evt_result = NRF_SUCCESS;
// Set the channel state appropriately.
m_channel[ch_id].state = CHANNEL_CONNECTED;
// Set the flag to trigger submission of the receive buffers to the softdevice.
submit_rx_buffer = true;
// Notify the event to the application.
notify_event = true;
}
}
break;
}
case BLE_L2CAP_EVT_CH_SETUP_REFUSED:
{
// This event is generated for both initiator and acceptor roles.
// This event indicates that the IPSP channel establishment failed.
retval = channel_search(p_evt->evt.l2cap_evt.conn_handle,
p_evt->evt.l2cap_evt.local_cid,
&ch_id);
if (retval != NRF_SUCCESS)
{
BLE_IPSP_TRC("Reply on unknown channel, dropping the event.");
}
else
{
if (m_channel[ch_id].state == CHANNEL_CONNECTING)
{
// Channel creation failed as peer rejected the connection.
// Initialize the event.
ipsp_event.evt_id = BLE_IPSP_EVT_CHANNEL_CONNECTED;
ipsp_event.evt_result = NRF_ERROR_BLE_IPSP_PEER_REJECTED;
BLE_IPSP_ERR("Peer rejected channel creation request, reason %d",
p_evt->evt.l2cap_evt.params.ch_setup_refused.status);
// Free the channel.
channel_free(ch_id);
// Notify the event to the application.
notify_event = true;
}
}
break;
}
case BLE_L2CAP_EVT_CH_RELEASED:
{
BLE_IPSP_TRC("L2CAP Channel disconnected.");
ipsp_event.evt_id = BLE_IPSP_EVT_CHANNEL_DISCONNECTED;
retval = channel_search(p_evt->evt.l2cap_evt.conn_handle,
p_evt->evt.l2cap_evt.local_cid,
&ch_id);
// Notify application of disconnection.
if (retval == NRF_SUCCESS)
{
handle.conn_handle = p_evt->evt.l2cap_evt.conn_handle;
handle.cid = p_evt->evt.l2cap_evt.local_cid;
channel_free(ch_id);
// Notify the event to the application.
notify_event = true;
}
break;
}
case BLE_L2CAP_EVT_CH_RX:
{
ipsp_event.evt_id = BLE_IPSP_EVT_CHANNEL_DATA_RX;
retval = channel_search(p_evt->evt.l2cap_evt.conn_handle,
p_evt->evt.l2cap_evt.local_cid,
&ch_id);
if (retval == NRF_SUCCESS)
{
handle.conn_handle = p_evt->evt.l2cap_evt.conn_handle;
handle.cid = p_evt->evt.l2cap_evt.local_cid;
rx_buffer_mark_unused(ch_id, p_evt->evt.l2cap_evt.params.rx.sdu_buf.p_data);
// Set the flag to trigger submission of the receive buffers to the softdevice.
submit_rx_buffer = true;
// Notify the event to the application.
notify_event = true;
}
break;
}
case BLE_L2CAP_EVT_CH_TX:
{
BLE_IPSP_TRC("BLE_L2CAP_EVT_CH_TX --> p_sdu_buf = %p, p_sdu_buf.p_data = %p",
&p_evt->evt.l2cap_evt.params.tx.sdu_buf, p_evt->evt.l2cap_evt.params.tx.sdu_buf.p_data);
retval = channel_search(p_evt->evt.l2cap_evt.conn_handle,
p_evt->evt.l2cap_evt.local_cid,
&ch_id);
if ((ch_id != INVALID_CHANNEL_INSTANCE) &&
p_evt->evt.l2cap_evt.local_cid == m_channel[ch_id].cid)
{
// Initialize the event.
ipsp_event.evt_id = BLE_IPSP_EVT_CHANNEL_DATA_TX_COMPLETE;
// Initialize the handle.
handle.conn_handle = m_channel[ch_id].conn_handle;
handle.cid = m_channel[ch_id].cid;
// Notify the event to the application.
notify_event = true;
}
break;
}
case BLE_L2CAP_EVT_CH_SDU_BUF_RELEASED:
{
BLE_IPSP_TRC("BLE_L2CAP_EVT_CH_SDU_BUF_RELEASED --> p_sdu_buf = %p, p_sdu_buf.p_data = %p",
&p_evt->evt.l2cap_evt.params.ch_sdu_buf_released.sdu_buf,
p_evt->evt.l2cap_evt.params.ch_sdu_buf_released.sdu_buf.p_data);
retval = channel_search(p_evt->evt.l2cap_evt.conn_handle,
p_evt->evt.l2cap_evt.local_cid,
&ch_id);
if ((ch_id != INVALID_CHANNEL_INSTANCE) &&
(p_evt->evt.l2cap_evt.local_cid == m_channel[ch_id].cid) &&
(is_tx_buffer(ch_id, p_evt->evt.l2cap_evt.params.ch_sdu_buf_released.sdu_buf.p_data)))
{
// Initialize the event.
ipsp_event.evt_id = BLE_IPSP_EVT_CHANNEL_DATA_TX_COMPLETE;
ipsp_event.evt_result = NRF_ERROR_BLE_IPSP_LINK_DISCONNECTED;
// Initialize the handle.
handle.conn_handle = m_channel[ch_id].conn_handle;
handle.cid = m_channel[ch_id].cid;
// Notify the event to the application.
notify_event = true;
}
break;
}
case BLE_GAP_EVT_DISCONNECTED:
{
uint32_t peer_device_index = connected_device_search(handle.conn_handle);
if (peer_device_index < IPSP_MAX_CONNECTED_DEVICES)
{
connected_device_init(peer_device_index);
}
break;
}
default:
break;
}
if (notify_event)
{
ble_ipsp_event_param_t event_param;
uint32_t peer_device_index;
peer_device_index = connected_device_search(handle.conn_handle);
if (peer_device_index < IPSP_MAX_CONNECTED_DEVICES)
{
event_param.p_peer = &m_connected_device[peer_device_index].ble_addr;
BLE_IPSP_TRC("Found peer device. Address type = 0x%02x",
event_param.p_peer->addr_type);
BLE_IPSP_DUMP((uint8_t *)event_param.p_peer->addr, 6);
}
else
{
event_param.p_peer = NULL;
}
event_param.p_l2cap_evt = &p_evt->evt.l2cap_evt;
ipsp_event.p_evt_param = &event_param;
app_notify(&handle, &ipsp_event);
}
// Trigger submission of the receive buffers to the softdevice.
if (submit_rx_buffer)
{
rx_buffers_submit(ch_id);
}
BLE_IPSP_MUTEX_UNLOCK();
UNUSED_VARIABLE(retval);
}
uint32_t ble_ipsp_init(const ble_ipsp_init_t * p_init)
{
BLE_IPSP_ENTRY();
ble_uuid_t ble_uuid;
uint32_t err_code;
uint16_t handle;
NULL_PARAM_CHECK(p_init);
NULL_PARAM_CHECK(p_init->evt_handler);
SDK_MUTEX_INIT(m_ipsp_mutex);
BLE_IPSP_MUTEX_LOCK();
// Add service to indicate IPSP support.
BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_IPSP_SERVICE);
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &handle);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
m_evt_handler = p_init->evt_handler;
// Initialize the channel.
for (int i = 0; i < BLE_IPSP_MAX_CHANNELS; i++)
{
channel_init(i);
}
// Initialize the connected peer device table.
for (int i = 0; i < IPSP_MAX_CONNECTED_DEVICES; i++)
{
connected_device_init(i);
}
BLE_IPSP_MUTEX_UNLOCK();
BLE_IPSP_EXIT();
return NRF_SUCCESS;
}
uint32_t ble_ipsp_connect(const ble_ipsp_handle_t * p_handle)
{
VERIFY_MODULE_IS_INITIALIZED();
NULL_PARAM_CHECK(p_handle);
VERIFY_CON_HANDLE(p_handle->conn_handle);
uint32_t err_code;
uint8_t ch_id = INVALID_CHANNEL_INSTANCE;
BLE_IPSP_TRC("[Conn Handle 0x%04X]: >> ble_ipsp_connect",
p_handle->conn_handle);
BLE_IPSP_MUTEX_LOCK();
// Check if channel already exists with the peer.
err_code = channel_search(p_handle->conn_handle, IPSP_ANY_CID, &ch_id);
if (err_code == NRF_SUCCESS)
{
// IPSP channel already exists.
err_code = NRF_ERROR_BLE_IPSP_CHANNEL_ALREADY_EXISTS;
}
else
{
// Search for a free channel.
err_code = channel_search(BLE_CONN_HANDLE_INVALID, BLE_L2CAP_CID_INVALID, &ch_id);
BLE_IPSP_TRC("2 channel_search result %08X", err_code);
if (err_code == NRF_SUCCESS)
{
m_channel[ch_id].state = CHANNEL_CONNECTING;
ble_l2cap_ch_setup_params_t param =
{
.le_psm = BLE_IPSP_PSM,
.rx_params = {
.rx_mtu = BLE_IPSP_MTU,
.rx_mps = BLE_IPSP_RX_MPS,
.sdu_buf =
{
.p_data = NULL,
.len = 0
}
}
};
BLE_IPSP_TRC("Requesting sd_ble_l2cap_ch_setup");
err_code = sd_ble_l2cap_ch_setup(p_handle->conn_handle,
&m_channel[ch_id].cid,
&param);
if (err_code != NRF_SUCCESS)
{
BLE_IPSP_ERR("sd_ble_l2cap_ch_conn_request failed, reason %08lX", err_code);
channel_free(ch_id);
}
else
{
BLE_IPSP_TRC("Local channel id from SD %04X.", m_channel[ch_id].cid);
m_channel[ch_id].conn_handle = p_handle->conn_handle;
}
}
else
{
err_code = (NRF_ERROR_BLE_IPSP_ERR_BASE + NRF_ERROR_NO_MEM);
}
}
BLE_IPSP_MUTEX_UNLOCK();
BLE_IPSP_EXIT_WITH_RESULT(err_code);
return err_code;
}
uint32_t ble_ipsp_send(ble_ipsp_handle_t const * p_handle,
uint8_t const * p_data,
uint16_t data_len)
{
BLE_IPSP_ENTRY();
VERIFY_MODULE_IS_INITIALIZED();
NULL_PARAM_CHECK(p_handle);
NULL_PARAM_CHECK(p_data);
VERIFY_CON_HANDLE(p_handle->conn_handle);
uint32_t err_code;
uint8_t ch_id;
BLE_IPSP_MUTEX_LOCK();
err_code = channel_search(p_handle->conn_handle, p_handle->cid, &ch_id);
if (err_code == NRF_SUCCESS)
{
const ble_data_t p_sdu_buf =
{
.p_data = (uint8_t *)p_data,
.len = data_len
};
BLE_IPSP_TRC("p_sdu_buf = %p, p_sdu_buf.p_data = %p",
&p_sdu_buf, p_data);
err_code = sd_ble_l2cap_ch_tx(p_handle->conn_handle,
p_handle->cid,
&p_sdu_buf);
}
BLE_IPSP_MUTEX_UNLOCK();
BLE_IPSP_EXIT_WITH_RESULT(err_code);
return err_code;
}
uint32_t ble_ipsp_disconnect(ble_ipsp_handle_t const * p_handle)
{
BLE_IPSP_ENTRY();
VERIFY_MODULE_IS_INITIALIZED();
NULL_PARAM_CHECK(p_handle);
VERIFY_CON_HANDLE(p_handle->conn_handle);
uint32_t err_code;
uint8_t ch_id;
BLE_IPSP_MUTEX_LOCK();
err_code = channel_search(p_handle->conn_handle, p_handle->cid, &ch_id);
if (err_code == NRF_SUCCESS)
{
m_channel[ch_id].state = CHANNEL_DISCONNECTING;
err_code = sd_ble_l2cap_ch_release(p_handle->conn_handle,
p_handle->cid);
}
BLE_IPSP_MUTEX_UNLOCK();
BLE_IPSP_EXIT_WITH_RESULT(err_code);
return err_code;
}
void ble_ipsp_incoming_channel_reject(uint16_t conn_handle)
{
uint32_t peer_device_index = connected_device_search(conn_handle);
if (peer_device_index != IPSP_MAX_CONNECTED_DEVICES)
{
m_connected_device[peer_device_index].response = INCOMING_CHANNEL_REJECT;
}
}
void ble_ipsp_incoming_channel_accept(uint16_t conn_handle)
{
uint32_t peer_device_index = connected_device_search(conn_handle);
if (peer_device_index != IPSP_MAX_CONNECTED_DEVICES)
{
m_connected_device[peer_device_index].response = INCOMING_CHANNEL_ACCEPT;
}
}

View File

@@ -0,0 +1,253 @@
/**
* Copyright (c) 2014 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_ipsp Internet Protocol Support Profile
* @{
* @ingroup ble_sdk_srv
* @brief Internet Protocol Support Profile.
*
* @details This module implements the Internet Protocol Support Profile creating and managing
* transport for 6lowpan.
* GATT is used to discover if IPSP is supported or not, but no IP data is exchanged
* over GATT. To exchange data, LE L2CAP Credit Mode is used. The PSM used for the channel
* is BLE_IPSP_PSM and is defined by the specification. The MTU mandated by the
* specification is 1280 bytes.
*
* @note Attention!
* To maintain compliance with Nordic Semiconductor ASA's Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#ifndef BLE_IPSP_H__
#define BLE_IPSP_H__
#include <stdint.h>
#include "ble.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Maximum IPSP channels required to be supported. */
#define BLE_IPSP_MAX_CHANNELS 1
/**@brief Maximum Transmit Unit on IPSP channel. */
#define BLE_IPSP_MTU 1280
/**@brief Receive MPS used by IPSP. */
#define BLE_IPSP_RX_MPS 50
/**@brief Transmission MPS used by IPSP.
*
* @note The actual MPS used is minimum of this value and the one requested by
* the peer during the channel setup. Here, the value used is
* (23 + 27 * 7).
*/
#define BLE_IPSP_TX_MPS 212
/**@brief Maximum data size that can be received.
*
* @details Maximum data size that can be received on the IPSP channel.
* Modify this values to intentionally set a receive size less
* than the MTU set on the channel.
*/
#define BLE_IPSP_RX_BUFFER_SIZE 1280
/**@brief Maximum number of receive buffers.
*
* @details Maximum number of receive buffers to be used per IPSP channel.
* Each receive buffer is of size @ref BLE_IPSP_RX_BUFFER_SIZE.
* This configuration has implications on the number of SDUs that can
* be received while an SDU is being consumed by the application
* (6LoWPAN/IP Stack).
*/
#define BLE_IPSP_RX_BUFFER_COUNT 4
/**@brief L2CAP Protocol Service Multiplexers number. */
#define BLE_IPSP_PSM 0x0023
/**@brief IPSP event identifier type. */
typedef enum
{
BLE_IPSP_EVT_CHANNEL_CONNECTED, /**< Channel connection event. */
BLE_IPSP_EVT_CHANNEL_DISCONNECTED, /**< Channel disconnection event. */
BLE_IPSP_EVT_CHANNEL_DATA_RX, /**< Data received on channel event. */
BLE_IPSP_EVT_CHANNEL_DATA_TX_COMPLETE /**< Requested data transmission complete on channel event. */
} ble_ipsp_evt_type_t;
/**@brief IPSP event parameter. */
typedef struct
{
ble_l2cap_evt_t const * p_l2cap_evt; /**< L2CAP event parameters. */
ble_gap_addr_t const * p_peer; /**< Peer device address. */
} ble_ipsp_event_param_t;
/**@brief IPSP event and associated parameter type. */
typedef struct
{
ble_ipsp_evt_type_t evt_id; /**< Identifier event type. */
ble_ipsp_event_param_t * p_evt_param; /**< Parameters associated with the event. */
uint32_t evt_result; /**< Result of the event.
\n The event result is SDK_ERR_RX_PKT_TRUNCATED for @ref BLE_IPSP_EVT_CHANNEL_DATA_RX,
\n implies that an incomplete SDU was received due to insufficient RX buffer size.
\n The size determined by @ref BLE_IPSP_RX_BUFFER_SIZE. */
} ble_ipsp_evt_t;
/**@brief IPSP handle. */
typedef struct
{
uint16_t conn_handle; /**< Identifies the link on which the IPSP channel is established. */
uint16_t cid; /**< Identifies the IPSP logical channel. */
} ble_ipsp_handle_t;
/**@brief Profile event handler type.
*
* @param[in] p_handle Identifies the connection and channel on which the event occurred.
* @param[in] p_evt Event and related parameters (if any).
*
* @returns Provision for the application to indicate if the event was successfully processed or
* not. Currently not used.
*/
typedef uint32_t (*ble_ipsp_evt_handler_t) (ble_ipsp_handle_t const * p_handle,
ble_ipsp_evt_t const * p_evt);
/**@brief IPSP initialization structure.
*
* @details IPSP initialization structure containing all options and data needed to
* initialize the profile.
*/
typedef struct
{
ble_ipsp_evt_handler_t evt_handler; /**< Event notification callback registered with the module to receive asynchronous events. */
} ble_ipsp_init_t;
/**@brief Function for initializing the Internet Protocol Support Profile.
*
* @param[in] p_init Information needed to initialize the service.
*
* @retval NRF_SUCCESS If initialization of the service was successful, else,
* an error code indicating reason for failure.
*/
uint32_t ble_ipsp_init(ble_ipsp_init_t const * p_init);
/**@brief Function for requesting a channel creation for the Internet Protocol Support Profile.
*
* @details Channel creation for Internet Protocol Support Profile (IPSP) is requested using this
* API. Connection handle provided in p_handle parameter identifies the peer with which
* the IPSP channel is being requested.
* NRF_SUCCESS return value by the API is only indicative of request procedure having
* succeeded. Result of channel establishment is known when the
* @ref BLE_IPSP_EVT_CHANNEL_CONNECTED event is notified.
* Therefore, the application must wait for @ref BLE_IPSP_EVT_CHANNEL_CONNECTED event on
* successful return of this API.
*
* @param[in] p_handle Indicates the connection handle on which IPSP channel is to be created.
*
* @retval NRF_SUCCESS If initialization of the service was successful, else,
* an error code indicating reason for failure.
*/
uint32_t ble_ipsp_connect(ble_ipsp_handle_t const * p_handle);
/**@brief Function for sending IP data to peer.
*
* @param[in] p_handle Instance of the logical channel and peer for which the data is intended.
* @param[in] p_data Pointer to memory containing the data to be transmitted.
* @note This memory must be resident and should not be freed unless
* @ref BLE_IPSP_EVT_CHANNEL_DATA_TX_COMPLETE event is notified.
* @param[in] data_len Length/size of data to be transferred.
*
* @retval NRF_SUCCESS If initialization of the service was successful, else,
* an error code indicating reason for failure.
*/
uint32_t ble_ipsp_send(ble_ipsp_handle_t const * p_handle,
uint8_t const * p_data,
uint16_t data_len);
/**@brief Function for disconnecting IP transport.
*
* @param[in] p_handle Identifies IPSP transport.
*
* @retval NRF_SUCCESS If initialization of the service was successful, else,
* an error code indicating reason for failure.
*/
uint32_t ble_ipsp_disconnect(ble_ipsp_handle_t const * p_handle);
/**@brief Function to accept incoming connections from a peer.
*
* @param[in] conn_handle Identifies the link with the peer.
*/
void ble_ipsp_incoming_channel_accept(uint16_t conn_handle);
/**@brief Function to reject incoming connections from a peer.
*
* @param[in] conn_handle Identifies the link with the peer.
*/
void ble_ipsp_incoming_channel_reject(uint16_t conn_handle);
/**@brief BLE event handler of the module.
*
* @param[in] p_evt BLE event to be handled.
*
* @retval NRF_SUCCESS If initialization of the service was successful, else,
* an error code indicating reason for failure.
*/
void ble_ipsp_evt_handler(ble_evt_t const * p_evt);
#ifdef __cplusplus
}
#endif
#endif // BLE_IPSP_H__
/** @} */

View File

@@ -0,0 +1,150 @@
/**
* Copyright (c) 2013 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_LBS)
#include "ble_lbs.h"
#include "ble_srv_common.h"
/**@brief Function for handling the Write event.
*
* @param[in] p_lbs LED Button Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_write(ble_lbs_t * p_lbs, ble_evt_t const * p_ble_evt)
{
ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
if ( (p_evt_write->handle == p_lbs->led_char_handles.value_handle)
&& (p_evt_write->len == 1)
&& (p_lbs->led_write_handler != NULL))
{
p_lbs->led_write_handler(p_ble_evt->evt.gap_evt.conn_handle, p_lbs, p_evt_write->data[0]);
}
}
void ble_lbs_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
ble_lbs_t * p_lbs = (ble_lbs_t *)p_context;
switch (p_ble_evt->header.evt_id)
{
case BLE_GATTS_EVT_WRITE:
on_write(p_lbs, p_ble_evt);
break;
default:
// No implementation needed.
break;
}
}
uint32_t ble_lbs_init(ble_lbs_t * p_lbs, const ble_lbs_init_t * p_lbs_init)
{
uint32_t err_code;
ble_uuid_t ble_uuid;
ble_add_char_params_t add_char_params;
// Initialize service structure.
p_lbs->led_write_handler = p_lbs_init->led_write_handler;
// Add service.
ble_uuid128_t base_uuid = {LBS_UUID_BASE};
err_code = sd_ble_uuid_vs_add(&base_uuid, &p_lbs->uuid_type);
VERIFY_SUCCESS(err_code);
ble_uuid.type = p_lbs->uuid_type;
ble_uuid.uuid = LBS_UUID_SERVICE;
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_lbs->service_handle);
VERIFY_SUCCESS(err_code);
// Add Button characteristic.
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = LBS_UUID_BUTTON_CHAR;
add_char_params.uuid_type = p_lbs->uuid_type;
add_char_params.init_len = sizeof(uint8_t);
add_char_params.max_len = sizeof(uint8_t);
add_char_params.char_props.read = 1;
add_char_params.char_props.notify = 1;
add_char_params.read_access = SEC_OPEN;
add_char_params.cccd_write_access = SEC_OPEN;
err_code = characteristic_add(p_lbs->service_handle,
&add_char_params,
&p_lbs->button_char_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
// Add LED characteristic.
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = LBS_UUID_LED_CHAR;
add_char_params.uuid_type = p_lbs->uuid_type;
add_char_params.init_len = sizeof(uint8_t);
add_char_params.max_len = sizeof(uint8_t);
add_char_params.char_props.read = 1;
add_char_params.char_props.write = 1;
add_char_params.read_access = SEC_OPEN;
add_char_params.write_access = SEC_OPEN;
return characteristic_add(p_lbs->service_handle, &add_char_params, &p_lbs->led_char_handles);
}
uint32_t ble_lbs_on_button_change(uint16_t conn_handle, ble_lbs_t * p_lbs, uint8_t button_state)
{
ble_gatts_hvx_params_t params;
uint16_t len = sizeof(button_state);
memset(&params, 0, sizeof(params));
params.type = BLE_GATT_HVX_NOTIFICATION;
params.handle = p_lbs->button_char_handles.value_handle;
params.p_data = &button_state;
params.p_len = &len;
return sd_ble_gatts_hvx(conn_handle, &params);
}
#endif // NRF_MODULE_ENABLED(BLE_LBS)

View File

@@ -0,0 +1,161 @@
/**
* Copyright (c) 2015 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_lbs LED Button Service Server
* @{
* @ingroup ble_sdk_srv
*
* @brief LED Button Service Server module.
*
* @details This module implements a custom LED Button Service with an LED and Button Characteristics.
* During initialization, the module adds the LED Button Service and Characteristics
* to the BLE stack database.
*
* The application must supply an event handler for receiving LED Button Service
* events. Using this handler, the service notifies the application when the
* LED value changes.
*
* The service also provides a function for letting the application notify
* the state of the Button Characteristic to connected peers.
*
* @note The application must register this module as BLE event observer using the
* NRF_SDH_BLE_OBSERVER macro. Example:
* @code
* ble_hids_t instance;
* NRF_SDH_BLE_OBSERVER(anything, BLE_HIDS_BLE_OBSERVER_PRIO,
* ble_hids_on_ble_evt, &instance);
* @endcode
*/
#ifndef BLE_LBS_H__
#define BLE_LBS_H__
#include <stdint.h>
#include <stdbool.h>
#include "ble.h"
#include "ble_srv_common.h"
#include "nrf_sdh_ble.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Macro for defining a ble_lbs instance.
*
* @param _name Name of the instance.
* @hideinitializer
*/
#define BLE_LBS_DEF(_name) \
static ble_lbs_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_LBS_BLE_OBSERVER_PRIO, \
ble_lbs_on_ble_evt, &_name)
#define LBS_UUID_BASE {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, \
0xDE, 0xEF, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00}
#define LBS_UUID_SERVICE 0x1523
#define LBS_UUID_BUTTON_CHAR 0x1524
#define LBS_UUID_LED_CHAR 0x1525
// Forward declaration of the ble_lbs_t type.
typedef struct ble_lbs_s ble_lbs_t;
typedef void (*ble_lbs_led_write_handler_t) (uint16_t conn_handle, ble_lbs_t * p_lbs, uint8_t new_state);
/** @brief LED Button Service init structure. This structure contains all options and data needed for
* initialization of the service.*/
typedef struct
{
ble_lbs_led_write_handler_t led_write_handler; /**< Event handler to be called when the LED Characteristic is written. */
} ble_lbs_init_t;
/**@brief LED Button Service structure. This structure contains various status information for the service. */
struct ble_lbs_s
{
uint16_t service_handle; /**< Handle of LED Button Service (as provided by the BLE stack). */
ble_gatts_char_handles_t led_char_handles; /**< Handles related to the LED Characteristic. */
ble_gatts_char_handles_t button_char_handles; /**< Handles related to the Button Characteristic. */
uint8_t uuid_type; /**< UUID type for the LED Button Service. */
ble_lbs_led_write_handler_t led_write_handler; /**< Event handler to be called when the LED Characteristic is written. */
};
/**@brief Function for initializing the LED Button Service.
*
* @param[out] p_lbs LED Button Service structure. This structure must be supplied by
* the application. It is initialized by this function and will later
* be used to identify this particular service instance.
* @param[in] p_lbs_init Information needed to initialize the service.
*
* @retval NRF_SUCCESS If the service was initialized successfully. Otherwise, an error code is returned.
*/
uint32_t ble_lbs_init(ble_lbs_t * p_lbs, const ble_lbs_init_t * p_lbs_init);
/**@brief Function for handling the application's BLE stack events.
*
* @details This function handles all events from the BLE stack that are of interest to the LED Button Service.
*
* @param[in] p_ble_evt Event received from the BLE stack.
* @param[in] p_context LED Button Service structure.
*/
void ble_lbs_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for sending a button state notification.
*
' @param[in] conn_handle Handle of the peripheral connection to which the button state notification will be sent.
* @param[in] p_lbs LED Button Service structure.
* @param[in] button_state New button state.
*
* @retval NRF_SUCCESS If the notification was sent successfully. Otherwise, an error code is returned.
*/
uint32_t ble_lbs_on_button_change(uint16_t conn_handle, ble_lbs_t * p_lbs, uint8_t button_state);
#ifdef __cplusplus
}
#endif
#endif // BLE_LBS_H__
/** @} */

View File

@@ -0,0 +1,322 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_LBS_C)
#include "ble_lbs_c.h"
#include "ble_db_discovery.h"
#include "ble_types.h"
#include "ble_gattc.h"
#define NRF_LOG_MODULE_NAME ble_lbs_c
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();
#define WRITE_MESSAGE_LENGTH BLE_CCCD_VALUE_LEN /**< Length of the write message for CCCD. */
/**@brief Function for intercepting the errors of GATTC and the BLE GATT Queue.
*
* @param[in] nrf_error Error code.
* @param[in] p_ctx Parameter from the event handler.
* @param[in] conn_handle Connection handle.
*/
static void gatt_error_handler(uint32_t nrf_error,
void * p_ctx,
uint16_t conn_handle)
{
ble_lbs_c_t * p_ble_lbs_c = (ble_lbs_c_t *)p_ctx;
NRF_LOG_DEBUG("A GATT Client error has occurred on conn_handle: 0X%X", conn_handle);
if (p_ble_lbs_c->error_handler != NULL)
{
p_ble_lbs_c->error_handler(nrf_error);
}
}
/**@brief Function for handling Handle Value Notification received from the SoftDevice.
*
* @details This function uses the Handle Value Notification received from the SoftDevice
* and checks whether it is a notification of Button state from the peer. If
* it is, this function decodes the state of the button and sends it to the
* application.
*
* @param[in] p_ble_lbs_c Pointer to the Led Button Client structure.
* @param[in] p_ble_evt Pointer to the BLE event received.
*/
static void on_hvx(ble_lbs_c_t * p_ble_lbs_c, ble_evt_t const * p_ble_evt)
{
// Check if the event is on the link for this instance.
if (p_ble_lbs_c->conn_handle != p_ble_evt->evt.gattc_evt.conn_handle)
{
return;
}
// Check if this is a Button notification.
if (p_ble_evt->evt.gattc_evt.params.hvx.handle == p_ble_lbs_c->peer_lbs_db.button_handle)
{
if (p_ble_evt->evt.gattc_evt.params.hvx.len == 1)
{
ble_lbs_c_evt_t ble_lbs_c_evt;
ble_lbs_c_evt.evt_type = BLE_LBS_C_EVT_BUTTON_NOTIFICATION;
ble_lbs_c_evt.conn_handle = p_ble_lbs_c->conn_handle;
ble_lbs_c_evt.params.button.button_state = p_ble_evt->evt.gattc_evt.params.hvx.data[0];
p_ble_lbs_c->evt_handler(p_ble_lbs_c, &ble_lbs_c_evt);
}
}
}
/**@brief Function for handling the Disconnected event received from the SoftDevice.
*
* @details This function checks whether the disconnect event is happening on the link
* associated with the current instance of the module. If the event is happening, the function sets the instance's
* conn_handle to invalid.
*
* @param[in] p_ble_lbs_c Pointer to the Led Button Client structure.
* @param[in] p_ble_evt Pointer to the BLE event received.
*/
static void on_disconnected(ble_lbs_c_t * p_ble_lbs_c, ble_evt_t const * p_ble_evt)
{
if (p_ble_lbs_c->conn_handle == p_ble_evt->evt.gap_evt.conn_handle)
{
p_ble_lbs_c->conn_handle = BLE_CONN_HANDLE_INVALID;
p_ble_lbs_c->peer_lbs_db.button_cccd_handle = BLE_GATT_HANDLE_INVALID;
p_ble_lbs_c->peer_lbs_db.button_handle = BLE_GATT_HANDLE_INVALID;
p_ble_lbs_c->peer_lbs_db.led_handle = BLE_GATT_HANDLE_INVALID;
}
}
void ble_lbs_on_db_disc_evt(ble_lbs_c_t * p_ble_lbs_c, ble_db_discovery_evt_t const * p_evt)
{
// Check if the LED Button Service was discovered.
if (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE &&
p_evt->params.discovered_db.srv_uuid.uuid == LBS_UUID_SERVICE &&
p_evt->params.discovered_db.srv_uuid.type == p_ble_lbs_c->uuid_type)
{
ble_lbs_c_evt_t evt;
evt.evt_type = BLE_LBS_C_EVT_DISCOVERY_COMPLETE;
evt.conn_handle = p_evt->conn_handle;
for (uint32_t i = 0; i < p_evt->params.discovered_db.char_count; i++)
{
const ble_gatt_db_char_t * p_char = &(p_evt->params.discovered_db.charateristics[i]);
switch (p_char->characteristic.uuid.uuid)
{
case LBS_UUID_LED_CHAR:
evt.params.peer_db.led_handle = p_char->characteristic.handle_value;
break;
case LBS_UUID_BUTTON_CHAR:
evt.params.peer_db.button_handle = p_char->characteristic.handle_value;
evt.params.peer_db.button_cccd_handle = p_char->cccd_handle;
break;
default:
break;
}
}
NRF_LOG_DEBUG("LED Button Service discovered at peer.");
//If the instance was assigned prior to db_discovery, assign the db_handles
if (p_ble_lbs_c->conn_handle != BLE_CONN_HANDLE_INVALID)
{
if ((p_ble_lbs_c->peer_lbs_db.led_handle == BLE_GATT_HANDLE_INVALID)&&
(p_ble_lbs_c->peer_lbs_db.button_handle == BLE_GATT_HANDLE_INVALID)&&
(p_ble_lbs_c->peer_lbs_db.button_cccd_handle == BLE_GATT_HANDLE_INVALID))
{
p_ble_lbs_c->peer_lbs_db = evt.params.peer_db;
}
}
p_ble_lbs_c->evt_handler(p_ble_lbs_c, &evt);
}
}
uint32_t ble_lbs_c_init(ble_lbs_c_t * p_ble_lbs_c, ble_lbs_c_init_t * p_ble_lbs_c_init)
{
uint32_t err_code;
ble_uuid_t lbs_uuid;
ble_uuid128_t lbs_base_uuid = {LBS_UUID_BASE};
VERIFY_PARAM_NOT_NULL(p_ble_lbs_c);
VERIFY_PARAM_NOT_NULL(p_ble_lbs_c_init);
VERIFY_PARAM_NOT_NULL(p_ble_lbs_c_init->evt_handler);
VERIFY_PARAM_NOT_NULL(p_ble_lbs_c_init->p_gatt_queue);
p_ble_lbs_c->peer_lbs_db.button_cccd_handle = BLE_GATT_HANDLE_INVALID;
p_ble_lbs_c->peer_lbs_db.button_handle = BLE_GATT_HANDLE_INVALID;
p_ble_lbs_c->peer_lbs_db.led_handle = BLE_GATT_HANDLE_INVALID;
p_ble_lbs_c->conn_handle = BLE_CONN_HANDLE_INVALID;
p_ble_lbs_c->evt_handler = p_ble_lbs_c_init->evt_handler;
p_ble_lbs_c->p_gatt_queue = p_ble_lbs_c_init->p_gatt_queue;
p_ble_lbs_c->error_handler = p_ble_lbs_c_init->error_handler;
err_code = sd_ble_uuid_vs_add(&lbs_base_uuid, &p_ble_lbs_c->uuid_type);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
VERIFY_SUCCESS(err_code);
lbs_uuid.type = p_ble_lbs_c->uuid_type;
lbs_uuid.uuid = LBS_UUID_SERVICE;
return ble_db_discovery_evt_register(&lbs_uuid);
}
void ble_lbs_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
if ((p_context == NULL) || (p_ble_evt == NULL))
{
return;
}
ble_lbs_c_t * p_ble_lbs_c = (ble_lbs_c_t *)p_context;
switch (p_ble_evt->header.evt_id)
{
case BLE_GATTC_EVT_HVX:
on_hvx(p_ble_lbs_c, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnected(p_ble_lbs_c, p_ble_evt);
break;
default:
break;
}
}
/**@brief Function for configuring the CCCD.
*
* @param[in] p_ble_lbs_c Pointer to the LED Button Client structure.
* @param[in] enable Whether to enable or disable the CCCD.
*
* @return NRF_SUCCESS if the CCCD configure was successfully sent to the peer.
*/
static uint32_t cccd_configure(ble_lbs_c_t * p_ble_lbs_c, bool enable)
{
NRF_LOG_DEBUG("Configuring CCCD. CCCD Handle = %d, Connection Handle = %d",
p_ble_lbs_c->peer_lbs_db.button_cccd_handle,
p_ble_lbs_c->conn_handle);
nrf_ble_gq_req_t cccd_req;
uint16_t cccd_val = enable ? BLE_GATT_HVX_NOTIFICATION : 0;
uint8_t cccd[WRITE_MESSAGE_LENGTH];
cccd[0] = LSB_16(cccd_val);
cccd[1] = MSB_16(cccd_val);
cccd_req.type = NRF_BLE_GQ_REQ_GATTC_WRITE;
cccd_req.error_handler.cb = gatt_error_handler;
cccd_req.error_handler.p_ctx = p_ble_lbs_c;
cccd_req.params.gattc_write.handle = p_ble_lbs_c->peer_lbs_db.button_cccd_handle;
cccd_req.params.gattc_write.len = WRITE_MESSAGE_LENGTH;
cccd_req.params.gattc_write.offset = 0;
cccd_req.params.gattc_write.p_value = cccd;
cccd_req.params.gattc_write.write_op = BLE_GATT_OP_WRITE_REQ;
return nrf_ble_gq_item_add(p_ble_lbs_c->p_gatt_queue, &cccd_req, p_ble_lbs_c->conn_handle);
}
uint32_t ble_lbs_c_button_notif_enable(ble_lbs_c_t * p_ble_lbs_c)
{
VERIFY_PARAM_NOT_NULL(p_ble_lbs_c);
if (p_ble_lbs_c->conn_handle == BLE_CONN_HANDLE_INVALID)
{
return NRF_ERROR_INVALID_STATE;
}
return cccd_configure(p_ble_lbs_c,
true);
}
uint32_t ble_lbs_led_status_send(ble_lbs_c_t * p_ble_lbs_c, uint8_t status)
{
VERIFY_PARAM_NOT_NULL(p_ble_lbs_c);
if (p_ble_lbs_c->conn_handle == BLE_CONN_HANDLE_INVALID)
{
return NRF_ERROR_INVALID_STATE;
}
NRF_LOG_DEBUG("Writing LED status 0x%x", status);
nrf_ble_gq_req_t write_req;
memset(&write_req, 0, sizeof(nrf_ble_gq_req_t));
write_req.type = NRF_BLE_GQ_REQ_GATTC_WRITE;
write_req.error_handler.cb = gatt_error_handler;
write_req.error_handler.p_ctx = p_ble_lbs_c;
write_req.params.gattc_write.handle = p_ble_lbs_c->peer_lbs_db.led_handle;
write_req.params.gattc_write.len = sizeof(status);
write_req.params.gattc_write.p_value = &status;
write_req.params.gattc_write.offset = 0;
write_req.params.gattc_write.write_op = BLE_GATT_OP_WRITE_CMD;
return nrf_ble_gq_item_add(p_ble_lbs_c->p_gatt_queue, &write_req, p_ble_lbs_c->conn_handle);
}
uint32_t ble_lbs_c_handles_assign(ble_lbs_c_t * p_ble_lbs_c,
uint16_t conn_handle,
const lbs_db_t * p_peer_handles)
{
VERIFY_PARAM_NOT_NULL(p_ble_lbs_c);
p_ble_lbs_c->conn_handle = conn_handle;
if (p_peer_handles != NULL)
{
p_ble_lbs_c->peer_lbs_db = *p_peer_handles;
}
return nrf_ble_gq_conn_handle_register(p_ble_lbs_c->p_gatt_queue, conn_handle);
}
#endif // NRF_MODULE_ENABLED(BLE_LBS_C)

View File

@@ -0,0 +1,266 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**@file
*
* @defgroup ble_lbs_c LED Button Service Client
* @{
* @ingroup ble_sdk_srv
* @brief The LED Button Service client can be used to set up a LED and read a button state on a
* LED button service server.
*
* @details This module contains the APIs and types exposed by the LED Button Service Client
* module. The application can use these APIs and types to perform the discovery of
* LED Button Service at the peer and to interact with it.
*
* @note The application must register this module as the BLE event observer by using the
* NRF_SDH_BLE_OBSERVER macro. Example:
* @code
* ble_lbs_c_t instance;
* NRF_SDH_BLE_OBSERVER(anything, BLE_LBS_C_BLE_OBSERVER_PRIO,
* ble_lbs_c_on_ble_evt, &instance);
* @endcode
*/
#ifndef BLE_LBS_C_H__
#define BLE_LBS_C_H__
#include <stdint.h>
#include "ble.h"
#include "ble_db_discovery.h"
#include "ble_srv_common.h"
#include "nrf_ble_gq.h"
#include "nrf_sdh_ble.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Macro for defining a ble_lbs_c instance.
*
* @param _name Name of the instance.
* @hideinitializer
*/
#define BLE_LBS_C_DEF(_name) \
static ble_lbs_c_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_LBS_C_BLE_OBSERVER_PRIO, \
ble_lbs_c_on_ble_evt, &_name)
/**@brief Macro for defining multiple ble_lbs_c instances.
*
* @param _name Name of the array of instances.
* @param _cnt Number of instances to define.
*/
#define BLE_LBS_C_ARRAY_DEF(_name, _cnt) \
static ble_lbs_c_t _name[_cnt]; \
NRF_SDH_BLE_OBSERVERS(_name ## _obs, \
BLE_LBS_C_BLE_OBSERVER_PRIO, \
ble_lbs_c_on_ble_evt, &_name, _cnt)
#define LBS_UUID_BASE {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, \
0xDE, 0xEF, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00}
#define LBS_UUID_SERVICE 0x1523
#define LBS_UUID_BUTTON_CHAR 0x1524
#define LBS_UUID_LED_CHAR 0x1525
/**@brief LBS Client event type. */
typedef enum
{
BLE_LBS_C_EVT_DISCOVERY_COMPLETE = 1, /**< Event indicating that the LED Button Service was discovered at the peer. */
BLE_LBS_C_EVT_BUTTON_NOTIFICATION /**< Event indicating that a notification of the LED Button Button characteristic was received from the peer. */
} ble_lbs_c_evt_type_t;
/**@brief Structure containing the Button value received from the peer. */
typedef struct
{
uint8_t button_state; /**< Button Value. */
} ble_button_t;
/**@brief Structure containing the handles related to the LED Button Service found on the peer. */
typedef struct
{
uint16_t button_cccd_handle; /**< Handle of the CCCD of the Button characteristic. */
uint16_t button_handle; /**< Handle of the Button characteristic as provided by the SoftDevice. */
uint16_t led_handle; /**< Handle of the LED characteristic as provided by the SoftDevice. */
} lbs_db_t;
/**@brief LED Button Event structure. */
typedef struct
{
ble_lbs_c_evt_type_t evt_type; /**< Type of the event. */
uint16_t conn_handle; /**< Connection handle on which the event occured.*/
union
{
ble_button_t button; /**< Button value received. This is filled if the evt_type is @ref BLE_LBS_C_EVT_BUTTON_NOTIFICATION. */
lbs_db_t peer_db; /**< Handles related to the LED Button Service found on the peer device. This is filled if the evt_type is @ref BLE_LBS_C_EVT_DISCOVERY_COMPLETE.*/
} params;
} ble_lbs_c_evt_t;
// Forward declaration of the ble_lbs_c_t type.
typedef struct ble_lbs_c_s ble_lbs_c_t;
/**@brief Event handler type.
*
* @details This is the type of the event handler that is to be provided by the application
* of this module in order to receive events.
*/
typedef void (* ble_lbs_c_evt_handler_t) (ble_lbs_c_t * p_ble_lbs_c, ble_lbs_c_evt_t * p_evt);
/**@brief LED Button Client structure. */
struct ble_lbs_c_s
{
uint16_t conn_handle; /**< Connection handle as provided by the SoftDevice. */
lbs_db_t peer_lbs_db; /**< Handles related to LBS on the peer. */
ble_lbs_c_evt_handler_t evt_handler; /**< Application event handler to be called when there is an event related to the LED Button service. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
uint8_t uuid_type; /**< UUID type. */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to the BLE GATT Queue instance. */
};
/**@brief LED Button Client initialization structure. */
typedef struct
{
ble_lbs_c_evt_handler_t evt_handler; /**< Event handler to be called by the LED Button Client module when there is an event related to the LED Button Service. */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to the BLE GATT Queue instance. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
} ble_lbs_c_init_t;
/**@brief Function for initializing the LED Button client module.
*
* @details This function registers with the Database Discovery module for the
* LED Button Service. The module looks for the presence of a LED Button Service instance
* at the peer when a discovery is started.
*
* @param[in] p_ble_lbs_c Pointer to the LED Button client structure.
* @param[in] p_ble_lbs_c_init Pointer to the LED Button initialization structure that contains the
* initialization information.
*
* @retval NRF_SUCCESS On successful initialization.
* @retval err_code Otherwise, this function propagates the error code returned by the Database Discovery module API
* @ref ble_db_discovery_evt_register.
*/
uint32_t ble_lbs_c_init(ble_lbs_c_t * p_ble_lbs_c, ble_lbs_c_init_t * p_ble_lbs_c_init);
/**@brief Function for handling BLE events from the SoftDevice.
*
* @details This function handles the BLE events received from the SoftDevice. If a BLE event
* is relevant to the LED Button Client module, the function uses the event's data to update interval
* variables and, if necessary, send events to the application.
*
* @param[in] p_ble_evt Pointer to the BLE event.
* @param[in] p_context Pointer to the LED button client structure.
*/
void ble_lbs_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for requesting the peer to start sending notification of the Button
* Characteristic.
*
* @details This function enables to notification of the Button at the peer
* by writing to the CCCD of the Button characteristic.
*
* @param[in] p_ble_lbs_c Pointer to the LED Button Client structure.
*
* @retval NRF_SUCCESS If the SoftDevice has been requested to write to the CCCD of the peer.
* @retval NRF_ERROR_INVALID_STATE If no connection handle has been assigned (@ref ble_lbs_c_handles_assign).
* @retval NRF_ERROR_NULL If the given parameter is NULL.
* @retval err_code Otherwise, this API propagates the error code returned by function
* @ref nrf_ble_gq_item_add.
*/
uint32_t ble_lbs_c_button_notif_enable(ble_lbs_c_t * p_ble_lbs_c);
/**@brief Function for handling events from the Database Discovery module.
*
* @details Call this function when you get a callback event from the Database Discovery module. This
* function handles an event from the Database Discovery module, and determines whether it
* relates to the discovery of LED Button service at the peer. If it does, this function calls the
* application's event handler to indicate that the LED Button service was discovered
* at the peer. The function also populates the event with service-related information before
* providing it to the application.
*
* @param[in] p_ble_lbs_c Pointer to the LED Button client structure.
* @param[in] p_evt Pointer to the event received from the Database Discovery module.
*/
void ble_lbs_on_db_disc_evt(ble_lbs_c_t * p_ble_lbs_c, const ble_db_discovery_evt_t * p_evt);
/**@brief Function for assigning handles to this instance of lbs_c.
*
* @details Call this function when a link has been established with a peer to associate the link
* to this instance of the module. This makes it possible to handle several links and
* associate each link to a particular instance of this module.
*
* @param[in] p_ble_lbs_c Pointer to the LED Button client structure instance for associating the link.
* @param[in] conn_handle Connection handle to associate with the given LED Button Client Instance.
* @param[in] p_peer_handles LED Button Service handles found on the peer (from @ref BLE_LBS_C_EVT_DISCOVERY_COMPLETE event).
*
* @retval NRF_SUCCESS If the status was sent successfully.
* @retval err_code Otherwise, this API propagates the error code returned by function
* @ref nrf_ble_gq_item_add.
*
*/
uint32_t ble_lbs_c_handles_assign(ble_lbs_c_t * p_ble_lbs_c,
uint16_t conn_handle,
const lbs_db_t * p_peer_handles);
/**@brief Function for writing the LED status to the connected server.
*
* @param[in] p_ble_lbs_c Pointer to the LED Button client structure.
* @param[in] status LED status to send.
*
* @retval NRF_SUCCESS If the status was sent successfully.
* @retval err_code Otherwise, this API propagates the error code returned by function
* @ref nrf_ble_gq_conn_handle_register.
*/
uint32_t ble_lbs_led_status_send(ble_lbs_c_t * p_ble_lbs_c, uint8_t status);
#ifdef __cplusplus
}
#endif
#endif // BLE_LBS_C_H__
/** @} */

View File

@@ -0,0 +1,221 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* Attention!
* To maintain compliance with Nordic Semiconductor ASA's Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_LLS)
#include "ble_lls.h"
#include <string.h>
#include "ble_hci.h"
#include "ble_srv_common.h"
/**@brief Function for handling the Connect event.
*
* @param[in] p_lls Link Loss Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_connect(ble_lls_t * p_lls, ble_evt_t const * p_ble_evt)
{
// Link reconnected, notify application with a no_alert event
ble_lls_evt_t evt;
p_lls->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
evt.evt_type = BLE_LLS_EVT_LINK_LOSS_ALERT;
evt.params.alert_level = BLE_CHAR_ALERT_LEVEL_NO_ALERT;
p_lls->evt_handler(p_lls, &evt);
}
/**@brief Function for handling the Disconnect event.
*
* @param[in] p_lls Link Loss Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_disconnect(ble_lls_t * p_lls, ble_evt_t const * p_ble_evt)
{
uint8_t reason = p_ble_evt->evt.gap_evt.params.disconnected.reason;
if (reason == BLE_HCI_CONNECTION_TIMEOUT)
{
// Link loss detected, notify application
uint32_t err_code;
ble_lls_evt_t evt;
evt.evt_type = BLE_LLS_EVT_LINK_LOSS_ALERT;
err_code = ble_lls_alert_level_get(p_lls, &evt.params.alert_level);
if (err_code == NRF_SUCCESS)
{
p_lls->evt_handler(p_lls, &evt);
}
else
{
if (p_lls->error_handler != NULL)
{
p_lls->error_handler(err_code);
}
}
}
}
/**@brief Function for handling the Authentication Status event.
*
* @param[in] p_lls Link Loss Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_auth_status(ble_lls_t * p_lls, ble_evt_t const * p_ble_evt)
{
if (p_ble_evt->evt.gap_evt.params.auth_status.auth_status == BLE_GAP_SEC_STATUS_SUCCESS)
{
ble_lls_evt_t evt;
evt.evt_type = BLE_LLS_EVT_LINK_LOSS_ALERT;
evt.params.alert_level = BLE_CHAR_ALERT_LEVEL_NO_ALERT;
p_lls->evt_handler(p_lls, &evt);
}
}
void ble_lls_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
ble_lls_t * p_lls = (ble_lls_t *)p_context;
if (p_lls == NULL || p_ble_evt == NULL)
{
return;
}
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
on_connect(p_lls, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnect(p_lls, p_ble_evt);
break;
case BLE_GAP_EVT_AUTH_STATUS:
on_auth_status(p_lls, p_ble_evt);
break;
default:
// No implementation needed.
break;
}
}
uint32_t ble_lls_init(ble_lls_t * p_lls, const ble_lls_init_t * p_lls_init)
{
uint32_t err_code;
ble_uuid_t ble_uuid;
ble_add_char_params_t add_char_params;
if (p_lls == NULL || p_lls_init == NULL)
{
return NRF_ERROR_NULL;
}
if (p_lls_init->evt_handler == NULL)
{
return NRF_ERROR_INVALID_PARAM;
}
// Initialize service structure
p_lls->evt_handler = p_lls_init->evt_handler;
p_lls->error_handler = p_lls_init->error_handler;
// Add service
BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_LINK_LOSS_SERVICE);
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
&ble_uuid,
&p_lls->service_handle);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
// Add alert level characteristic
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = BLE_UUID_ALERT_LEVEL_CHAR;
add_char_params.max_len = sizeof(uint8_t);
add_char_params.char_props.read = 1;
add_char_params.char_props.write = 1;
add_char_params.write_access = p_lls_init->alert_level_wr_sec;
add_char_params.read_access = p_lls_init->alert_level_rd_sec;
add_char_params.init_len = sizeof(uint8_t);
add_char_params.p_init_value = (uint8_t *) &(p_lls_init->initial_alert_level);
return characteristic_add(p_lls->service_handle,
&add_char_params,
&p_lls->alert_level_handles);
}
uint32_t ble_lls_alert_level_get(ble_lls_t * p_lls, uint8_t * p_alert_level)
{
ble_gatts_value_t gatts_value;
if (p_lls == NULL || p_alert_level == NULL)
{
return NRF_ERROR_NULL;
}
// Initialize value struct.
memset(&gatts_value, 0, sizeof(gatts_value));
gatts_value.len = sizeof(uint8_t);
gatts_value.offset = 0;
gatts_value.p_value = p_alert_level;
return sd_ble_gatts_value_get(p_lls->conn_handle,
p_lls->alert_level_handles.value_handle,
&gatts_value);
}
#endif // NRF_MODULE_ENABLED(BLE_LLS)

View File

@@ -0,0 +1,176 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_lls Link Loss Service
* @{
* @ingroup ble_sdk_srv
* @brief Link Loss Service module.
*
* @details This module implements the Link Loss Service with the Alert Level characteristic.
* During initialization it adds the Link Loss Service and Alert Level characteristic
* to the BLE stack database.
*
* The application must supply an event handler for receiving Link Loss Service
* events. Using this handler, the service will notify the application when the
* link has been lost, and which Alert Level has been set.
*
* The service also provides a function for letting the application poll the current
* value of the Alert Level characteristic.
*
* @note The application must register this module as BLE event observer using the
* NRF_SDH_BLE_OBSERVER macro. Example:
* @code
* ble_lls_t instance;
* NRF_SDH_BLE_OBSERVER(anything, BLE_LLS_BLE_OBSERVER_PRIO,
* ble_lls_on_ble_evt, &instance);
* @endcode
*
* @note Attention!
* To maintain compliance with Nordic Semiconductor ASA Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#ifndef BLE_LLS_H__
#define BLE_LLS_H__
#include <stdint.h>
#include "ble.h"
#include "ble_srv_common.h"
#include "nrf_sdh_ble.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Macro for defining a ble_lls instance.
*
* @param _name Name of the instance.
* @hideinitializer
*/
#define BLE_LLS_DEF(_name) \
static ble_lls_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_LLS_BLE_OBSERVER_PRIO, \
ble_lls_on_ble_evt, &_name)
/**@brief Link Loss Service event type. */
typedef enum
{
BLE_LLS_EVT_LINK_LOSS_ALERT /**< Alert Level Updated event. */
} ble_lls_evt_type_t;
/**@brief Link Loss Service event. */
typedef struct
{
ble_lls_evt_type_t evt_type; /**< Type of event. */
union
{
uint8_t alert_level; /**< New Alert Level value. */
} params;
} ble_lls_evt_t;
// Forward declaration of the ble_lls_t type.
typedef struct ble_lls_s ble_lls_t;
/**@brief Link Loss Service event handler type. */
typedef void (*ble_lls_evt_handler_t) (ble_lls_t * p_lls, ble_lls_evt_t * p_evt);
/**@brief Link Loss Service init structure. This contains all options and data needed for initialization of the service. */
typedef struct
{
ble_lls_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Link Loss Service. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
uint8_t initial_alert_level; /**< Initial value of the Alert Level characteristic. */
security_req_t alert_level_rd_sec; /**< Security requirement for reading Alert Level characteristic. */
security_req_t alert_level_wr_sec; /**< Security requirement for writing Alert Level characteristic. */
} ble_lls_init_t;
/**@brief Link Loss Service structure. This contains various status information for the service. */
struct ble_lls_s
{
ble_lls_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Link Loss Service. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
uint16_t service_handle; /**< Handle of Link Loss Service (as provided by the BLE stack). */
ble_gatts_char_handles_t alert_level_handles; /**< Handles related to the Alert Level characteristic. */
uint16_t conn_handle; /**< Handle of the current connection (as provided by the BLE stack, is BLE_CONN_HANDLE_INVALID if not in a connection). */
};
/**@brief Function for initializing the Link Loss Service.
*
* @param[out] p_lls Link Loss Service structure. This structure will have to be supplied by
* the application. It will be initialized by this function, and will later
* be used to identify this particular service instance.
* @param[in] p_lls_init Information needed to initialize the service.
*
* @return NRF_SUCCESS on successful initialization of service, otherwise an error code.
*/
uint32_t ble_lls_init(ble_lls_t * p_lls, const ble_lls_init_t * p_lls_init);
/**@brief Function for handling the Application's BLE Stack events.
*
* @details Handles all events from the BLE stack of interest to the Link Loss Service.
*
* @param[in] p_ble_evt Event received from the BLE stack.
* @param[in] p_context Link Loss Service structure.
*/
void ble_lls_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for getting current value of the Alert Level characteristic.
*
* @param[in] p_lls Link Loss Service structure.
* @param[out] p_alert_level Current Alert Level value.
*
* @return NRF_SUCCESS on success, otherwise an error code.
*/
uint32_t ble_lls_alert_level_get(ble_lls_t * p_lls, uint8_t * p_alert_level);
#ifdef __cplusplus
}
#endif
#endif // BLE_LLS_H__
/** @} */

View File

@@ -0,0 +1,235 @@
/****************************************Copyright (c)************************************************
**
**--------------File Info-----------------------------------------------------------------------------
** File name:ble_nus.c
** Last modified Date:
** Last Version:
** Descriptions :串口透传服务文件
**---------------------------------------------------------------------------------------------------*/
#include "sdk_common.h"
//这里设置了串口透传服务程序模块的“编译开关”所以要在sdk_config.h中启用BLE_NUS
#if NRF_MODULE_ENABLED(BLE_NUS)
#include "ble.h"
#include "ble_nus.h"
#include "ble_srv_common.h"
#include "nrf_log.h"
#if 0
#define NRF_LOG_MODULE_NAME ble_nus
#if BLE_NUS_CONFIG_LOG_ENABLED
#define NRF_LOG_LEVEL BLE_NUS_CONFIG_LOG_LEVEL
#define NRF_LOG_INFO_COLOR BLE_NUS_CONFIG_INFO_COLOR
#define NRF_LOG_DEBUG_COLOR BLE_NUS_CONFIG_DEBUG_COLOR
#else // BLE_NUS_CONFIG_LOG_ENABLED
#define NRF_LOG_LEVEL 0
#endif // BLE_NUS_CONFIG_LOG_ENABLED
NRF_LOG_MODULE_REGISTER();
#endif
#define BLE_NUS_MAX_RX_CHAR_LEN BLE_NUS_MAX_DATA_LEN //RX特征最大长度字节数
#define BLE_NUS_MAX_TX_CHAR_LEN BLE_NUS_MAX_DATA_LEN //TX特征最大长度字节数
//SoftDevice提交的“Write”事件处理函数
static void on_write(ble_nus_t * p_nus, ble_evt_t const * p_ble_evt)
{
//定义一个串口透传事件结构体变量,用于执行回调时传递参数
ble_nus_evt_t evt;
//定义write事件结构体指针并指向GATT事件的write
ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
//清空evt结构体
memset(&evt, 0, sizeof(ble_nus_evt_t));
//指向串口透传实例
evt.p_nus = p_nus;
//设置连接句柄
evt.conn_handle = p_ble_evt->evt.gatts_evt.conn_handle;
//写RX特征值
if ((p_evt_write->handle == p_nus->rx_handles.value_handle) &&
(p_nus->data_handler != NULL))
{
//设置事件类型
evt.type = BLE_NUS_EVT_RX_DATA;
//设置数据
evt.params.rx_data.p_data = p_evt_write->data;
//设置数据长度
evt.params.rx_data.length = p_evt_write->len;
//执行回调
p_nus->data_handler(&evt);
}
else
{
// Do Nothing. This event is not relevant for this service.
}
}
//SoftDevice提交的“BLE_GATTS_EVT_HVN_TX_COMPLETE”事件处理函数
static void on_hvx_tx_complete(ble_nus_t * p_nus, ble_evt_t const * p_ble_evt)
{
//定义一个串口透传事件结构体变量evt用于执行回调时传递参数
ble_nus_evt_t evt;
//清零evt
memset(&evt, 0, sizeof(ble_nus_evt_t));
//设置事件类型
evt.type = BLE_NUS_EVT_TX_RDY;
//指向串口透传服务实例
evt.p_nus = p_nus;
//设置连接句柄
evt.conn_handle = p_ble_evt->evt.gatts_evt.conn_handle;
//执行回调
p_nus->data_handler(&evt);
}
//串口透传服务BLE事件监视者的事件回调函数
void ble_nus_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
//检查参数是否有效
if ((p_context == NULL) || (p_ble_evt == NULL))
{
return;
}
//定义一个串口透传结构体指针并指向串口透传结构体
ble_nus_t * p_nus = (ble_nus_t *)p_context;
//判断事件类型
switch (p_ble_evt->header.evt_id)
{
//写事件
case BLE_GATTS_EVT_WRITE:
on_write(p_nus, p_ble_evt);
break;
//TX就绪事件
case BLE_GATTS_EVT_HVN_TX_COMPLETE:
on_hvx_tx_complete(p_nus, p_ble_evt);
break;
default:
// No implementation needed.
break;
}
}
//初始化串口透传服务
uint32_t ble_nus_init(ble_nus_t * p_nus, ble_nus_init_t const * p_nus_init)
{
ret_code_t err_code;
//定义16位UUID结构体变量
ble_uuid_t ble_uuid;
//定义128位UUID结构体变量并初始化为串口透传服务UUID基数
ble_uuid128_t nus_base_uuid = NUS_BASE_UUID;
//定义特征参数结构体变量
ble_add_char_params_t add_char_params;
//检查指针是否为NULL
VERIFY_PARAM_NOT_NULL(p_nus);
VERIFY_PARAM_NOT_NULL(p_nus_init);
//拷贝串口透传服务初始化结构体中的事件句柄
p_nus->data_handler = p_nus_init->data_handler;
//将自定义UUID基数添加到SoftDevice
err_code = sd_ble_uuid_vs_add(&nus_base_uuid, &p_nus->uuid_type);
VERIFY_SUCCESS(err_code);
//UUID类型和数值赋值给ble_uuid变量
ble_uuid.type = BLE_UUID_TYPE_BLE;
ble_uuid.uuid = BLE_UUID_NUS_SERVICE;
//添加串口透传服务
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
&ble_uuid,
&p_nus->service_handle);
VERIFY_SUCCESS(err_code);
/*---------------------以下代码添加RX特征--------------------*/
//添加RX特征
//配置参数之前先清零add_char_params
memset(&add_char_params, 0, sizeof(add_char_params));
//RX特征的UUID
add_char_params.uuid = BLE_UUID_NUS_RX_CHARACTERISTIC;
//RX特征的UUID类型
add_char_params.uuid_type = p_nus->uuid_type;
//设置RX特征值的最大长度
add_char_params.max_len = BLE_NUS_MAX_RX_CHAR_LEN;
//设置RX特征值的初始长度
add_char_params.init_len = sizeof(uint8_t);
//设置RX的特征值长度为可变长度
add_char_params.is_var_len = true;
//设置RX特征的性质支持写
add_char_params.char_props.write = 1;
//设置RX特性的性质支持读
add_char_params.char_props.read = 1;
//设置RX特征的性质支持无响应写
add_char_params.char_props.write_wo_resp = 1;
//设置读/写RX特征值的安全需求无安全性
add_char_params.read_access = SEC_OPEN;
add_char_params.write_access = SEC_OPEN;
//为串口透传服务添加RX特征
err_code = characteristic_add(p_nus->service_handle, &add_char_params, &p_nus->rx_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
/*---------------------添加RX特征-END------------------------*/
/*---------------------以下代码添加TX特征--------------------*/
//添加TX特征
//配置参数之前先清零add_char_params
memset(&add_char_params, 0, sizeof(add_char_params));
//TX特征的UUID
add_char_params.uuid = BLE_UUID_NUS_TX_CHARACTERISTIC;
//TX特征的UUID类型
add_char_params.uuid_type = p_nus->uuid_type;
//设置TX特征值的最大长度
add_char_params.max_len = BLE_NUS_MAX_TX_CHAR_LEN;
//设置TX特征值的初始长度
add_char_params.init_len = sizeof(uint8_t);
//设置TX的特征值长度为可变长度
add_char_params.is_var_len = true;
//设置TX特征的性质支持通知
add_char_params.char_props.notify = 1;
//设置TX特性的性质支持读
add_char_params.char_props.read = 1;
//设置读/写RX特征值的安全需求无安全性
add_char_params.read_access = SEC_OPEN;
add_char_params.write_access = SEC_OPEN;
add_char_params.cccd_write_access = SEC_OPEN;
//为串口透传服务添加TX特征
return characteristic_add(p_nus->service_handle, &add_char_params, &p_nus->tx_handles);
/*---------------------添加TX特征-END------------------------*/
}
//蓝牙数据上传发送,并进行反馈
uint32_t ble_nus_data_send(ble_nus_t * p_nus,
uint8_t * p_data,
uint16_t * p_length,
uint16_t conn_handle)
{
ble_gatts_hvx_params_t hvx_params;
//验证p_uarts没有指向NULL
VERIFY_PARAM_NOT_NULL(p_nus);
//如果连接句柄无效表示没有和主机建立连接返回NRF_ERROR_NOT_FOUND
if (conn_handle == BLE_CONN_HANDLE_INVALID)
{
return NRF_ERROR_NOT_FOUND;
}
if (*p_length > BLE_NUS_MAX_DATA_LEN)
{
return NRF_ERROR_INVALID_PARAM;
}
//设置之前先清零hvx_params
memset(&hvx_params, 0, sizeof(hvx_params));
//TX特征值句柄
hvx_params.handle = p_nus->tx_handles.value_handle;
//发送的数据
hvx_params.p_data = p_data;
//发送的数据长度
hvx_params.p_len = p_length;
//类型为通知
hvx_params.type = BLE_GATT_HVX_NOTIFICATION;
//发送TX特征值通知
return sd_ble_gatts_hvx(conn_handle, &hvx_params);
}
#endif // NRF_MODULE_ENABLED(BLE_NUS)

View File

@@ -0,0 +1,165 @@
#ifndef BLE_NUS_H__
#define BLE_NUS_H__
#include <stdint.h>
#include <stdbool.h>
#include "sdk_config.h"
#include "ble.h"
#include "ble_srv_common.h"
#include "nrf_sdh_ble.h"
#ifdef __cplusplus
extern "C" {
#endif
//定义串口透传服务实例该实例完成2件事情
//1定义了static类型串口透传服务结构体变量为串口透传服务结构体分配了内存
//2注册了BLE事件监视者这使得串口透传程序模块可以接收BLE协议栈的事件从而可以在ble_uarts_on_ble_evt()事件回调函数中处理自己感兴趣的事件
#define BLE_NUS_DEF(_name) \
static ble_nus_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_NUS_BLE_OBSERVER_PRIO, \
ble_nus_on_ble_evt, \
&_name)
//定义串口透传服务128位UUID基数
#define NUS_BASE_UUID {{0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}
//定义串口透传服务16位UUID
#define BLE_UUID_NUS_SERVICE 0x1000 //串口透传服务16位UUID
#define BLE_UUID_NUS_TX_CHARACTERISTIC 0x1002 //TX特征16位UUID
#define BLE_UUID_NUS_RX_CHARACTERISTIC 0x1001 //RX特征16位UUID
//定义操作码长度
#define OPCODE_LENGTH 1
//定义句柄长度
#define HANDLE_LENGTH 2
//定义最大传输数据长度(字节数)
#if defined(NRF_SDH_BLE_GATT_MAX_MTU_SIZE) && (NRF_SDH_BLE_GATT_MAX_MTU_SIZE != 0)
#define BLE_NUS_MAX_DATA_LEN (NRF_SDH_BLE_GATT_MAX_MTU_SIZE - OPCODE_LENGTH - HANDLE_LENGTH)
#else
#define BLE_NUS_MAX_DATA_LEN (BLE_GATT_MTU_SIZE_DEFAULT - OPCODE_LENGTH - HANDLE_LENGTH)
#warning NRF_SDH_BLE_GATT_MAX_MTU_SIZE is not defined.
#endif
//定义串口透传事件类型,这是用户自己定义的,供应用程序使用
typedef enum
{
BLE_NUS_EVT_RX_DATA, //接收到新的数据
BLE_NUS_EVT_TX_RDY, //准备就绪,可以发送新数据
BLE_NUS_EVT_COMM_STARTED, /**< Notification has been enabled. */
BLE_NUS_EVT_COMM_STOPPED, /**< Notification has been disabled. */
} ble_nus_evt_type_t;
/* Forward declaration of the ble_nus_t type. */
typedef struct ble_nus_s ble_nus_t;
//串口透传服务BLE_NUS_EVT_RX_DATA事件数据结构体该结构体用于当BLE_NUS_EVT_RX_DATA产生时将接收的数据信息传递给处理函数
typedef struct
{
uint8_t const * p_data; //指向存放接收数据的缓存
uint16_t length; //接收的数据长度
} ble_nus_evt_rx_data_t;
/**@brief Nordic UART Service client context structure.
*
* @details This structure contains state context related to hosts.
*/
typedef struct
{
bool is_notification_enabled; /**< Variable to indicate if the peer has enabled notification of the RX characteristic.*/
} ble_nus_client_context_t;
//串口透传服务事件结构体
typedef struct
{
ble_nus_evt_type_t type; //事件类型
ble_nus_t * p_nus; //指向串口透传实例的指针
uint16_t conn_handle; //连接句柄
// ble_nus_client_context_t * p_link_ctx; /**< A pointer to the link context. */
union
{
ble_nus_evt_rx_data_t rx_data; //BLE_NUS_EVT_RX_DATA事件数据
} params;
} ble_nus_evt_t;
//定义函数指针类型ble_uarts_data_handler_t
typedef void (* ble_nus_data_handler_t) (ble_nus_evt_t * p_evt);
//串口服务初始化结构体
typedef struct
{
ble_nus_data_handler_t data_handler; //处理接收数据的事件句柄
} ble_nus_init_t;
//串口透传服务结构体,包含所需要的信息
struct ble_nus_s
{
uint8_t uuid_type; //UUID类型
uint16_t service_handle; //串口透传服务句柄(由协议栈提供)
ble_gatts_char_handles_t tx_handles; //TX特征句柄
ble_gatts_char_handles_t rx_handles; //RX特征句柄
// blcm_link_ctx_storage_t * const p_link_ctx_storage; /**< Pointer to link context storage with handles of all current connections and its context. */
ble_nus_data_handler_t data_handler; //处理接收数据的事件句柄
};
/**@brief Function for initializing the Nordic UART Service.
*
* @param[out] p_nus Nordic UART Service structure. This structure must be supplied
* by the application. It is initialized by this function and will
* later be used to identify this particular service instance.
* @param[in] p_nus_init Information needed to initialize the service.
*
* @retval NRF_SUCCESS If the service was successfully initialized. Otherwise, an error code is returned.
* @retval NRF_ERROR_NULL If either of the pointers p_nus or p_nus_init is NULL.
*/
uint32_t ble_nus_init(ble_nus_t * p_nus, ble_nus_init_t const * p_nus_init);
/**@brief Function for handling the Nordic UART Service's BLE events.
*
* @details The Nordic UART Service expects the application to call this function each time an
* event is received from the SoftDevice. This function processes the event if it
* is relevant and calls the Nordic UART Service event handler of the
* application if necessary.
*
* @param[in] p_ble_evt Event received from the SoftDevice.
* @param[in] p_context Nordic UART Service structure.
*/
void ble_nus_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for sending a data to the peer.
*
* @details This function sends the input string as an RX characteristic notification to the
* peer.
*
* @param[in] p_nus Pointer to the Nordic UART Service structure.
* @param[in] p_data String to be sent.
* @param[in,out] p_length Pointer Length of the string. Amount of sent bytes.
* @param[in] conn_handle Connection Handle of the destination client.
*
* @retval NRF_SUCCESS If the string was sent successfully. Otherwise, an error code is returned.
*/
uint32_t ble_nus_data_send(ble_nus_t * p_nus,
uint8_t * p_data,
uint16_t * p_length,
uint16_t conn_handle);
#ifdef __cplusplus
}
#endif
#endif // BLE_NUS_H__
/** @} */

View File

@@ -0,0 +1,297 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_NUS_C)
#include <stdlib.h>
#include "ble.h"
#include "ble_nus_c.h"
#include "ble_gattc.h"
#include "ble_srv_common.h"
#include "app_error.h"
#define NRF_LOG_MODULE_NAME ble_nus_c
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();
/**@brief Function for intercepting the errors of GATTC and the BLE GATT Queue.
*
* @param[in] nrf_error Error code.
* @param[in] p_ctx Parameter from the event handler.
* @param[in] conn_handle Connection handle.
*/
static void gatt_error_handler(uint32_t nrf_error,
void * p_ctx,
uint16_t conn_handle)
{
ble_nus_c_t * p_ble_nus_c = (ble_nus_c_t *)p_ctx;
NRF_LOG_DEBUG("A GATT Client error has occurred on conn_handle: 0X%X", conn_handle);
if (p_ble_nus_c->error_handler != NULL)
{
p_ble_nus_c->error_handler(nrf_error);
}
}
void ble_nus_c_on_db_disc_evt(ble_nus_c_t * p_ble_nus_c, ble_db_discovery_evt_t * p_evt)
{
ble_nus_c_evt_t nus_c_evt;
memset(&nus_c_evt,0,sizeof(ble_nus_c_evt_t));
ble_gatt_db_char_t * p_chars = p_evt->params.discovered_db.charateristics;
// Check if the NUS was discovered.
if ( (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE)
&& (p_evt->params.discovered_db.srv_uuid.uuid == BLE_UUID_NUS_SERVICE)
&& (p_evt->params.discovered_db.srv_uuid.type == p_ble_nus_c->uuid_type))
{
for (uint32_t i = 0; i < p_evt->params.discovered_db.char_count; i++)
{
switch (p_chars[i].characteristic.uuid.uuid)
{
case BLE_UUID_NUS_RX_CHARACTERISTIC:
nus_c_evt.handles.nus_rx_handle = p_chars[i].characteristic.handle_value;
break;
case BLE_UUID_NUS_TX_CHARACTERISTIC:
nus_c_evt.handles.nus_tx_handle = p_chars[i].characteristic.handle_value;
nus_c_evt.handles.nus_tx_cccd_handle = p_chars[i].cccd_handle;
break;
default:
break;
}
}
if (p_ble_nus_c->evt_handler != NULL)
{
nus_c_evt.conn_handle = p_evt->conn_handle;
nus_c_evt.evt_type = BLE_NUS_C_EVT_DISCOVERY_COMPLETE;
p_ble_nus_c->evt_handler(p_ble_nus_c, &nus_c_evt);
}
}
}
/**@brief Function for handling Handle Value Notification received from the SoftDevice.
*
* @details This function uses the Handle Value Notification received from the SoftDevice
* and checks if it is a notification of the NUS TX characteristic from the peer.
* If it is, this function decodes the data and sends it to the application.
*
* @param[in] p_ble_nus_c Pointer to the NUS Client structure.
* @param[in] p_ble_evt Pointer to the BLE event received.
*/
static void on_hvx(ble_nus_c_t * p_ble_nus_c, ble_evt_t const * p_ble_evt)
{
// HVX can only occur from client sending.
if ( (p_ble_nus_c->handles.nus_tx_handle != BLE_GATT_HANDLE_INVALID)
&& (p_ble_evt->evt.gattc_evt.params.hvx.handle == p_ble_nus_c->handles.nus_tx_handle)
&& (p_ble_nus_c->evt_handler != NULL))
{
ble_nus_c_evt_t ble_nus_c_evt;
ble_nus_c_evt.evt_type = BLE_NUS_C_EVT_NUS_TX_EVT;
ble_nus_c_evt.p_data = (uint8_t *)p_ble_evt->evt.gattc_evt.params.hvx.data;
ble_nus_c_evt.data_len = p_ble_evt->evt.gattc_evt.params.hvx.len;
p_ble_nus_c->evt_handler(p_ble_nus_c, &ble_nus_c_evt);
NRF_LOG_DEBUG("Client sending data.");
}
}
uint32_t ble_nus_c_init(ble_nus_c_t * p_ble_nus_c, ble_nus_c_init_t * p_ble_nus_c_init)
{
uint32_t err_code;
ble_uuid_t uart_uuid;
ble_uuid128_t nus_base_uuid = NUS_BASE_UUID;
VERIFY_PARAM_NOT_NULL(p_ble_nus_c);
VERIFY_PARAM_NOT_NULL(p_ble_nus_c_init);
VERIFY_PARAM_NOT_NULL(p_ble_nus_c_init->p_gatt_queue);
err_code = sd_ble_uuid_vs_add(&nus_base_uuid, &p_ble_nus_c->uuid_type);
VERIFY_SUCCESS(err_code);
uart_uuid.type = p_ble_nus_c->uuid_type;
uart_uuid.uuid = BLE_UUID_NUS_SERVICE;
p_ble_nus_c->conn_handle = BLE_CONN_HANDLE_INVALID;
p_ble_nus_c->evt_handler = p_ble_nus_c_init->evt_handler;
p_ble_nus_c->error_handler = p_ble_nus_c_init->error_handler;
p_ble_nus_c->handles.nus_tx_handle = BLE_GATT_HANDLE_INVALID;
p_ble_nus_c->handles.nus_rx_handle = BLE_GATT_HANDLE_INVALID;
p_ble_nus_c->p_gatt_queue = p_ble_nus_c_init->p_gatt_queue;
return ble_db_discovery_evt_register(&uart_uuid);
}
void ble_nus_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
ble_nus_c_t * p_ble_nus_c = (ble_nus_c_t *)p_context;
if ((p_ble_nus_c == NULL) || (p_ble_evt == NULL))
{
return;
}
if ( (p_ble_nus_c->conn_handle == BLE_CONN_HANDLE_INVALID)
||(p_ble_nus_c->conn_handle != p_ble_evt->evt.gap_evt.conn_handle)
)
{
return;
}
switch (p_ble_evt->header.evt_id)
{
case BLE_GATTC_EVT_HVX:
on_hvx(p_ble_nus_c, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
if (p_ble_evt->evt.gap_evt.conn_handle == p_ble_nus_c->conn_handle
&& p_ble_nus_c->evt_handler != NULL)
{
ble_nus_c_evt_t nus_c_evt;
nus_c_evt.evt_type = BLE_NUS_C_EVT_DISCONNECTED;
p_ble_nus_c->conn_handle = BLE_CONN_HANDLE_INVALID;
p_ble_nus_c->evt_handler(p_ble_nus_c, &nus_c_evt);
}
break;
default:
// No implementation needed.
break;
}
}
/**@brief Function for creating a message for writing to the CCCD. */
static uint32_t cccd_configure(ble_nus_c_t * p_ble_nus_c, bool notification_enable)
{
nrf_ble_gq_req_t cccd_req;
uint8_t cccd[BLE_CCCD_VALUE_LEN];
uint16_t cccd_val = notification_enable ? BLE_GATT_HVX_NOTIFICATION : 0;
memset(&cccd_req, 0, sizeof(nrf_ble_gq_req_t));
cccd[0] = LSB_16(cccd_val);
cccd[1] = MSB_16(cccd_val);
cccd_req.type = NRF_BLE_GQ_REQ_GATTC_WRITE;
cccd_req.error_handler.cb = gatt_error_handler;
cccd_req.error_handler.p_ctx = p_ble_nus_c;
cccd_req.params.gattc_write.handle = p_ble_nus_c->handles.nus_tx_cccd_handle;
cccd_req.params.gattc_write.len = BLE_CCCD_VALUE_LEN;
cccd_req.params.gattc_write.offset = 0;
cccd_req.params.gattc_write.p_value = cccd;
cccd_req.params.gattc_write.write_op = BLE_GATT_OP_WRITE_REQ;
cccd_req.params.gattc_write.flags = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_WRITE;
return nrf_ble_gq_item_add(p_ble_nus_c->p_gatt_queue, &cccd_req, p_ble_nus_c->conn_handle);
}
uint32_t ble_nus_c_tx_notif_enable(ble_nus_c_t * p_ble_nus_c)
{
VERIFY_PARAM_NOT_NULL(p_ble_nus_c);
if ( (p_ble_nus_c->conn_handle == BLE_CONN_HANDLE_INVALID)
||(p_ble_nus_c->handles.nus_tx_cccd_handle == BLE_GATT_HANDLE_INVALID)
)
{
return NRF_ERROR_INVALID_STATE;
}
return cccd_configure(p_ble_nus_c, true);
}
uint32_t ble_nus_c_string_send(ble_nus_c_t * p_ble_nus_c, uint8_t * p_string, uint16_t length)
{
VERIFY_PARAM_NOT_NULL(p_ble_nus_c);
nrf_ble_gq_req_t write_req;
memset(&write_req, 0, sizeof(nrf_ble_gq_req_t));
if (length > BLE_NUS_MAX_DATA_LEN)
{
NRF_LOG_WARNING("Content too long.");
return NRF_ERROR_INVALID_PARAM;
}
if (p_ble_nus_c->conn_handle == BLE_CONN_HANDLE_INVALID)
{
NRF_LOG_WARNING("Connection handle invalid.");
return NRF_ERROR_INVALID_STATE;
}
write_req.type = NRF_BLE_GQ_REQ_GATTC_WRITE;
write_req.error_handler.cb = gatt_error_handler;
write_req.error_handler.p_ctx = p_ble_nus_c;
write_req.params.gattc_write.handle = p_ble_nus_c->handles.nus_rx_handle;
write_req.params.gattc_write.len = length;
write_req.params.gattc_write.offset = 0;
write_req.params.gattc_write.p_value = p_string;
write_req.params.gattc_write.write_op = BLE_GATT_OP_WRITE_CMD;
write_req.params.gattc_write.flags = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_WRITE;
return nrf_ble_gq_item_add(p_ble_nus_c->p_gatt_queue, &write_req, p_ble_nus_c->conn_handle);
}
uint32_t ble_nus_c_handles_assign(ble_nus_c_t * p_ble_nus,
uint16_t conn_handle,
ble_nus_c_handles_t const * p_peer_handles)
{
VERIFY_PARAM_NOT_NULL(p_ble_nus);
p_ble_nus->conn_handle = conn_handle;
if (p_peer_handles != NULL)
{
p_ble_nus->handles.nus_tx_cccd_handle = p_peer_handles->nus_tx_cccd_handle;
p_ble_nus->handles.nus_tx_handle = p_peer_handles->nus_tx_handle;
p_ble_nus->handles.nus_rx_handle = p_peer_handles->nus_rx_handle;
}
return nrf_ble_gq_conn_handle_register(p_ble_nus->p_gatt_queue, conn_handle);
}
#endif // NRF_MODULE_ENABLED(BLE_NUS_C)

View File

@@ -0,0 +1,279 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**@file
*
* @defgroup ble_nus_c Nordic UART Service Client
* @{
* @ingroup ble_sdk_srv
* @brief Nordic UART Service Client module.
*
* @details This module contains the APIs and types exposed by the Nordic UART Service Client
* module. The application can use these APIs and types to perform the discovery of
* the Nordic UART Service at the peer and to interact with it.
*
* @note The application must register this module as the BLE event observer by using the
* NRF_SDH_BLE_OBSERVER macro. Example:
* @code
* ble_nus_c_t instance;
* NRF_SDH_BLE_OBSERVER(anything, BLE_NUS_C_BLE_OBSERVER_PRIO,
* ble_nus_c_on_ble_evt, &instance);
* @endcode
*
*/
#ifndef BLE_NUS_C_H__
#define BLE_NUS_C_H__
#include <stdint.h>
#include <stdbool.h>
#include "ble.h"
#include "ble_gatt.h"
#include "ble_db_discovery.h"
#include "ble_srv_common.h"
#include "nrf_ble_gq.h"
#include "nrf_sdh_ble.h"
#include "sdk_config.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Macro for defining a ble_nus_c instance.
*
* @param _name Name of the instance.
* @hideinitializer
*/
#define BLE_NUS_C_DEF(_name) \
static ble_nus_c_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_NUS_C_BLE_OBSERVER_PRIO, \
ble_nus_c_on_ble_evt, &_name)
/** @brief Macro for defining multiple ble_nus_c instances.
*
* @param _name Name of the array of instances.
* @param _cnt Number of instances to define.
* @hideinitializer
*/
#define BLE_NUS_C_ARRAY_DEF(_name, _cnt) \
static ble_nus_c_t _name[_cnt]; \
NRF_SDH_BLE_OBSERVERS(_name ## _obs, \
BLE_NUS_C_BLE_OBSERVER_PRIO, \
ble_nus_c_on_ble_evt, &_name, _cnt)
#define NUS_BASE_UUID {{0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, 0x93, 0xF3, 0xA3, 0xB5, 0x00, 0x00, 0x40, 0x6E}} /**< Used vendor-specific UUID. */
#define BLE_UUID_NUS_SERVICE 0x0001 /**< The UUID of the Nordic UART Service. */
#define BLE_UUID_NUS_RX_CHARACTERISTIC 0x0002 /**< The UUID of the RX Characteristic. */
#define BLE_UUID_NUS_TX_CHARACTERISTIC 0x0003 /**< The UUID of the TX Characteristic. */
#define OPCODE_LENGTH 1
#define HANDLE_LENGTH 2
/**@brief Maximum length of data (in bytes) that can be transmitted to the peer by the Nordic UART service module. */
#if defined(NRF_SDH_BLE_GATT_MAX_MTU_SIZE) && (NRF_SDH_BLE_GATT_MAX_MTU_SIZE != 0)
#define BLE_NUS_MAX_DATA_LEN (NRF_SDH_BLE_GATT_MAX_MTU_SIZE - OPCODE_LENGTH - HANDLE_LENGTH)
#else
#define BLE_NUS_MAX_DATA_LEN (BLE_GATT_MTU_SIZE_DEFAULT - OPCODE_LENGTH - HANDLE_LENGTH)
#warning NRF_SDH_BLE_GATT_MAX_MTU_SIZE is not defined.
#endif
/**@brief NUS Client event type. */
typedef enum
{
BLE_NUS_C_EVT_DISCOVERY_COMPLETE, /**< Event indicating that the NUS service and its characteristics were found. */
BLE_NUS_C_EVT_NUS_TX_EVT, /**< Event indicating that the central received something from a peer. */
BLE_NUS_C_EVT_DISCONNECTED /**< Event indicating that the NUS server disconnected. */
} ble_nus_c_evt_type_t;
/**@brief Handles on the connected peer device needed to interact with it. */
typedef struct
{
uint16_t nus_tx_handle; /**< Handle of the NUS TX characteristic, as provided by a discovery. */
uint16_t nus_tx_cccd_handle; /**< Handle of the CCCD of the NUS TX characteristic, as provided by a discovery. */
uint16_t nus_rx_handle; /**< Handle of the NUS RX characteristic, as provided by a discovery. */
} ble_nus_c_handles_t;
/**@brief Structure containing the NUS event data received from the peer. */
typedef struct
{
ble_nus_c_evt_type_t evt_type;
uint16_t conn_handle;
uint16_t max_data_len;
uint8_t * p_data;
uint16_t data_len;
ble_nus_c_handles_t handles; /**< Handles on which the Nordic UART service characteristics were discovered on the peer device. This is filled if the evt_type is @ref BLE_NUS_C_EVT_DISCOVERY_COMPLETE.*/
} ble_nus_c_evt_t;
// Forward declaration of the ble_nus_t type.
typedef struct ble_nus_c_s ble_nus_c_t;
/**@brief Event handler type.
*
* @details This is the type of the event handler that is to be provided by the application
* of this module to receive events.
*/
typedef void (* ble_nus_c_evt_handler_t)(ble_nus_c_t * p_ble_nus_c, ble_nus_c_evt_t const * p_evt);
/**@brief NUS Client structure. */
struct ble_nus_c_s
{
uint8_t uuid_type; /**< UUID type. */
uint16_t conn_handle; /**< Handle of the current connection. Set with @ref ble_nus_c_handles_assign when connected. */
ble_nus_c_handles_t handles; /**< Handles on the connected peer device needed to interact with it. */
ble_nus_c_evt_handler_t evt_handler; /**< Application event handler to be called when there is an event related to the NUS. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to BLE GATT Queue instance. */
};
/**@brief NUS Client initialization structure. */
typedef struct
{
ble_nus_c_evt_handler_t evt_handler; /**< Application event handler to be called when there is an event related to the NUS. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to BLE GATT Queue instance. */
} ble_nus_c_init_t;
/**@brief Function for initializing the Nordic UART client module.
*
* @details This function registers with the Database Discovery module
* for the NUS. The Database Discovery module looks for the presence
* of a NUS instance at the peer when a discovery is started.
*
* @param[in] p_ble_nus_c Pointer to the NUS client structure.
* @param[in] p_ble_nus_c_init Pointer to the NUS initialization structure that contains the
* initialization information.
*
* @retval NRF_SUCCESS If the module was initialized successfully.
* @retval err_code Otherwise, this function propagates the error code
* returned by the Database Discovery module API
* @ref ble_db_discovery_evt_register.
*/
uint32_t ble_nus_c_init(ble_nus_c_t * p_ble_nus_c, ble_nus_c_init_t * p_ble_nus_c_init);
/**@brief Function for handling events from the Database Discovery module.
*
* @details This function handles an event from the Database Discovery module, and determines
* whether it relates to the discovery of NUS at the peer. If it does, the function
* calls the application's event handler to indicate that NUS was
* discovered at the peer. The function also populates the event with service-related
* information before providing it to the application.
*
* @param[in] p_ble_nus_c Pointer to the NUS client structure.
* @param[in] p_evt Pointer to the event received from the Database Discovery module.
*/
void ble_nus_c_on_db_disc_evt(ble_nus_c_t * p_ble_nus_c, ble_db_discovery_evt_t * p_evt);
/**@brief Function for handling BLE events from the SoftDevice.
*
* @details This function handles the BLE events received from the SoftDevice. If a BLE
* event is relevant to the NUS module, the function uses the event's data to update
* internal variables and, if necessary, send events to the application.
*
* @param[in] p_ble_evt Pointer to the BLE event.
* @param[in] p_context Pointer to the NUS client structure.
*/
void ble_nus_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for requesting the peer to start sending notification of TX characteristic.
*
* @details This function enables notifications of the NUS TX characteristic at the peer
* by writing to the CCCD of the NUS TX characteristic.
*
* @param p_ble_nus_c Pointer to the NUS client structure.
*
* @retval NRF_SUCCESS If the operation was successful.
* @retval err_code Otherwise, this API propagates the error code returned by function @ref nrf_ble_gq_item_add.
*/
uint32_t ble_nus_c_tx_notif_enable(ble_nus_c_t * p_ble_nus_c);
/**@brief Function for sending a string to the server.
*
* @details This function writes the RX characteristic of the server.
*
* @param[in] p_ble_nus_c Pointer to the NUS client structure.
* @param[in] p_string String to be sent.
* @param[in] length Length of the string.
*
* @retval NRF_SUCCESS If the string was sent successfully.
* @retval err_code Otherwise, this API propagates the error code returned by function @ref nrf_ble_gq_item_add.
*/
uint32_t ble_nus_c_string_send(ble_nus_c_t * p_ble_nus_c, uint8_t * p_string, uint16_t length);
/**@brief Function for assigning handles to this instance of nus_c.
*
* @details Call this function when a link has been established with a peer to
* associate the link to this instance of the module. This makes it
* possible to handle several links and associate each link to a particular
* instance of this module. The connection handle and attribute handles are
* provided from the discovery event @ref BLE_NUS_C_EVT_DISCOVERY_COMPLETE.
*
* @param[in] p_ble_nus_c Pointer to the NUS client structure instance to associate with these
* handles.
* @param[in] conn_handle Connection handle to associated with the given NUS Instance.
* @param[in] p_peer_handles Attribute handles on the NUS server that you want this NUS client to
* interact with.
*
* @retval NRF_SUCCESS If the operation was successful.
* @retval NRF_ERROR_NULL If a p_nus was a NULL pointer.
* @retval err_code Otherwise, this API propagates the error code returned
* by function @ref nrf_ble_gq_item_add.
*/
uint32_t ble_nus_c_handles_assign(ble_nus_c_t * p_ble_nus_c,
uint16_t conn_handle,
ble_nus_c_handles_t const * p_peer_handles);
#ifdef __cplusplus
}
#endif
#endif // BLE_NUS_C_H__
/** @} */

View File

@@ -0,0 +1,327 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* Attention!
* To maintain compliance with Nordic Semiconductor ASA's Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_RSCS)
#include "ble_rscs.h"
#include <string.h>
#include "ble.h"
#include "ble_srv_common.h"
#define OPCODE_LENGTH 1 /**< Length of opcode inside Running Speed and Cadence Measurement packet. */
#define HANDLE_LENGTH 2 /**< Length of handle inside Running Speed and Cadence Measurement packet. */
#define MAX_RSCM_LEN (BLE_GATT_ATT_MTU_DEFAULT - OPCODE_LENGTH - HANDLE_LENGTH) /**< Maximum size of a transmitted Running Speed and Cadence Measurement. */
// Running Speed and Cadence Measurement flag bits
#define RSC_MEAS_FLAG_INSTANT_STRIDE_LEN_PRESENT (0x01 << 0) /**< Instantaneous Stride Length Present flag bit. */
#define RSC_MEAS_FLAG_TOTAL_DISTANCE_PRESENT (0x01 << 1) /**< Total Distance Present flag bit. */
#define RSC_MEAS_FLAG_WALKING_OR_RUNNING_BIT (0x01 << 2) /**< Walking or Running Status flag bit. */
/**@brief Function for handling the Connect event.
*
* @param[in] p_rscs Running Speed and Cadence Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_connect(ble_rscs_t * p_rscs, ble_evt_t const * p_ble_evt)
{
p_rscs->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
}
/**@brief Function for handling the Disconnect event.
*
* @param[in] p_rscs Running Speed and Cadence Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_disconnect(ble_rscs_t * p_rscs, ble_evt_t const * p_ble_evt)
{
UNUSED_PARAMETER(p_ble_evt);
p_rscs->conn_handle = BLE_CONN_HANDLE_INVALID;
}
/**@brief Function for handling the write events to the RSCS Measurement characteristic.
*
* @param[in] p_rscs Running Speed and Cadence Service structure.
* @param[in] p_evt_write Write event received from the BLE stack.
*/
static void on_meas_cccd_write(ble_rscs_t * p_rscs, ble_gatts_evt_write_t const * p_evt_write)
{
if (p_evt_write->len == 2)
{
// CCCD written, update notification state
if (p_rscs->evt_handler != NULL)
{
ble_rscs_evt_t evt;
if (ble_srv_is_notification_enabled(p_evt_write->data))
{
evt.evt_type = BLE_RSCS_EVT_NOTIFICATION_ENABLED;
}
else
{
evt.evt_type = BLE_RSCS_EVT_NOTIFICATION_DISABLED;
}
p_rscs->evt_handler(p_rscs, &evt);
}
}
}
/**@brief Function for handling the Write event.
*
* @param[in] p_rscs Running Speed and Cadence Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_write(ble_rscs_t * p_rscs, ble_evt_t const * p_ble_evt)
{
ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
if (p_evt_write->handle == p_rscs->meas_handles.cccd_handle)
{
on_meas_cccd_write(p_rscs, p_evt_write);
}
}
void ble_rscs_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
if ((p_context == NULL) || (p_ble_evt == NULL))
{
return;
}
ble_rscs_t * p_rscs = (ble_rscs_t *)p_context;
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
on_connect(p_rscs, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnect(p_rscs, p_ble_evt);
break;
case BLE_GATTS_EVT_WRITE:
on_write(p_rscs, p_ble_evt);
break;
default:
// No implementation needed.
break;
}
}
/**@brief Function for encoding a RSCS Measurement.
*
* @param[in] p_rscs Running Speed and Cadence Service structure.
* @param[in] p_rsc_measurement Measurement to be encoded.
* @param[out] p_encoded_buffer Buffer where the encoded data will be written.
*
* @return Size of encoded data.
*/
static uint8_t rsc_measurement_encode(const ble_rscs_t * p_rscs,
const ble_rscs_meas_t * p_rsc_measurement,
uint8_t * p_encoded_buffer)
{
uint8_t flags = 0;
uint8_t len = 1;
// Instantaneous speed field
len += uint16_encode(p_rsc_measurement->inst_speed, &p_encoded_buffer[len]);
// Instantaneous cadence field
p_encoded_buffer[len++] = p_rsc_measurement->inst_cadence;
// Instantaneous stride length field
if (p_rscs->feature & BLE_RSCS_FEATURE_INSTANT_STRIDE_LEN_BIT)
{
if (p_rsc_measurement->is_inst_stride_len_present)
{
flags |= RSC_MEAS_FLAG_INSTANT_STRIDE_LEN_PRESENT;
len += uint16_encode(p_rsc_measurement->inst_stride_length,
&p_encoded_buffer[len]);
}
}
// Total distance field
if (p_rscs->feature & BLE_RSCS_FEATURE_TOTAL_DISTANCE_BIT)
{
if (p_rsc_measurement->is_total_distance_present)
{
flags |= RSC_MEAS_FLAG_TOTAL_DISTANCE_PRESENT;
len += uint32_encode(p_rsc_measurement->total_distance, &p_encoded_buffer[len]);
}
}
// Flags field
if (p_rscs->feature & BLE_RSCS_FEATURE_WALKING_OR_RUNNING_STATUS_BIT)
{
if (p_rsc_measurement->is_running)
{
flags |= RSC_MEAS_FLAG_WALKING_OR_RUNNING_BIT;
}
}
p_encoded_buffer[0] = flags;
return len;
}
uint32_t ble_rscs_init(ble_rscs_t * p_rscs, const ble_rscs_init_t * p_rscs_init)
{
if (p_rscs == NULL || p_rscs_init == NULL)
{
return NRF_ERROR_NULL;
}
uint32_t err_code;
uint8_t init_value_encoded[MAX(MAX_RSCM_LEN, sizeof(uint16_t))];
ble_uuid_t ble_uuid;
ble_add_char_params_t add_char_params;
// Initialize service structure
p_rscs->evt_handler = p_rscs_init->evt_handler;
p_rscs->conn_handle = BLE_CONN_HANDLE_INVALID;
p_rscs->feature = p_rscs_init->feature;
// Add service
BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_RUNNING_SPEED_AND_CADENCE);
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
&ble_uuid,
&p_rscs->service_handle);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
// Add measurement characteristic
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = BLE_UUID_RSC_MEASUREMENT_CHAR;
add_char_params.max_len = MAX_RSCM_LEN;
add_char_params.is_var_len = true;
add_char_params.char_props.notify = 1;
add_char_params.cccd_write_access = p_rscs_init->rsc_meas_cccd_wr_sec;
add_char_params.p_init_value = init_value_encoded;
add_char_params.init_len = rsc_measurement_encode(p_rscs,
&p_rscs_init->initial_rcm,
init_value_encoded);
err_code = characteristic_add(p_rscs->service_handle, &add_char_params, &p_rscs->meas_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
// Add feature characteristic
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = BLE_UUID_RSC_FEATURE_CHAR;
add_char_params.max_len = sizeof(uint16_t);
add_char_params.init_len = uint16_encode(p_rscs_init->feature, init_value_encoded);
add_char_params.p_init_value = init_value_encoded;
add_char_params.char_props.read = 1;
add_char_params.read_access = p_rscs_init->rsc_feature_rd_sec;
err_code = characteristic_add(p_rscs->service_handle,
&add_char_params,
&p_rscs->feature_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
return NRF_SUCCESS;
}
uint32_t ble_rscs_measurement_send(ble_rscs_t * p_rscs, ble_rscs_meas_t * p_measurement)
{
if (p_rscs == NULL || p_measurement == NULL)
{
return NRF_ERROR_NULL;
}
uint32_t err_code;
// Send value if connected and notifying
if (p_rscs->conn_handle != BLE_CONN_HANDLE_INVALID)
{
uint8_t encoded_rsc_meas[MAX_RSCM_LEN];
uint16_t len;
uint16_t hvx_len;
ble_gatts_hvx_params_t hvx_params;
len = rsc_measurement_encode(p_rscs, p_measurement, encoded_rsc_meas);
hvx_len = len;
memset(&hvx_params, 0, sizeof(hvx_params));
hvx_params.handle = p_rscs->meas_handles.value_handle;
hvx_params.type = BLE_GATT_HVX_NOTIFICATION;
hvx_params.offset = 0;
hvx_params.p_len = &hvx_len;
hvx_params.p_data = encoded_rsc_meas;
err_code = sd_ble_gatts_hvx(p_rscs->conn_handle, &hvx_params);
if ((err_code == NRF_SUCCESS) && (hvx_len != len))
{
err_code = NRF_ERROR_DATA_SIZE;
}
}
else
{
err_code = NRF_ERROR_INVALID_STATE;
}
return err_code;
}
#endif // NRF_MODULE_ENABLED(BLE_RSCS)

View File

@@ -0,0 +1,201 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_rscs Running Speed and Cadence Service
* @{
* @ingroup ble_sdk_srv
* @brief Running Speed and Cadence Service module.
*
* @details This module implements the Running Speed and Cadence Service. If enabled, notification
* of the Running Speead and Candence Measurement is performed when the application
* calls ble_rscs_measurement_send().
*
* If an event handler is supplied by the application, the Running Speed and Cadence
* Service will generate Running Speed and Cadence Service events to the application.
*
* @note The application must register this module as BLE event observer using the
* NRF_SDH_BLE_OBSERVER macro. Example:
* @code
* ble_rscs_t instance;
* NRF_SDH_BLE_OBSERVER(anything, BLE_RSCS_BLE_OBSERVER_PRIO,
* ble_rscs_on_ble_evt, &instance);
* @endcode
*
* @note Attention!
* To maintain compliance with Nordic Semiconductor ASA Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#ifndef BLE_RSCS_H__
#define BLE_RSCS_H__
#include <stdint.h>
#include <stdbool.h>
#include "ble_srv_common.h"
#include "nrf_sdh_ble.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Macro for defining a ble_rscs instance.
*
* @param _name Name of the instance.
* @hideinitializer
*/
#define BLE_RSCS_DEF(_name) \
static ble_rscs_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_RSCS_BLE_OBSERVER_PRIO, \
ble_rscs_on_ble_evt, &_name)
/**@brief Running Speed and Cadence Service feature bits. */
#define BLE_RSCS_FEATURE_INSTANT_STRIDE_LEN_BIT (0x01 << 0) /**< Instantaneous Stride Length Measurement Supported bit. */
#define BLE_RSCS_FEATURE_TOTAL_DISTANCE_BIT (0x01 << 1) /**< Total Distance Measurement Supported bit. */
#define BLE_RSCS_FEATURE_WALKING_OR_RUNNING_STATUS_BIT (0x01 << 2) /**< Walking or Running Status Supported bit. */
#define BLE_RSCS_FEATURE_CALIBRATION_PROCEDURE_BIT (0x01 << 3) /**< Calibration Procedure Supported bit. */
#define BLE_RSCS_FEATURE_MULTIPLE_SENSORS_BIT (0x01 << 4) /**< Multiple Sensor Locations Supported bit. */
/**@brief Running Speed and Cadence Service event type. */
typedef enum
{
BLE_RSCS_EVT_NOTIFICATION_ENABLED, /**< Running Speed and Cadence value notification enabled event. */
BLE_RSCS_EVT_NOTIFICATION_DISABLED /**< Running Speed and Cadence value notification disabled event. */
} ble_rscs_evt_type_t;
/**@brief Running Speed and Cadence Service event. */
typedef struct
{
ble_rscs_evt_type_t evt_type; /**< Type of event. */
} ble_rscs_evt_t;
// Forward declaration of the ble_rsc types.
typedef struct ble_rscs_s ble_rscs_t;
typedef struct ble_rscs_meas_s ble_rscs_meas_t;
/**@brief Running Speed and Cadence Service event handler type. */
typedef void (*ble_rscs_evt_handler_t) (ble_rscs_t * p_rscs, ble_rscs_evt_t * p_evt);
/**@brief Running Speed and Cadence Service measurement structure. This contains a Running Speed and
* Cadence measurement.
*/
struct ble_rscs_meas_s
{
bool is_inst_stride_len_present; /**< True if Instantaneous Stride Length is present in the measurement. */
bool is_total_distance_present; /**< True if Total Distance is present in the measurement. */
bool is_running; /**< True if running, False if walking. */
uint16_t inst_speed; /**< Instantaneous Speed. */
uint8_t inst_cadence; /**< Instantaneous Cadence. */
uint16_t inst_stride_length; /**< Instantaneous Stride Length. */
uint32_t total_distance; /**< Total Distance. */
};
/**@brief Running Speed and Cadence Service init structure. This contains all options and data
* needed for initialization of the service.
*/
typedef struct
{
ble_rscs_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Running Speed and Cadence Service. */
security_req_t rsc_meas_cccd_wr_sec; /**< Security requirement for writing running speed and cadence measurement characteristic CCCD. */
security_req_t rsc_feature_rd_sec; /**< Security requirement for reading running speed and cadence feature characteristic. */
uint16_t feature; /**< Initial value for features of sensor. */
ble_rscs_meas_t initial_rcm; /**< Initial Running Speed Cadence Measurement.*/
} ble_rscs_init_t;
/**@brief Running Speed and Cadence Service structure. This contains various status information for
* the service.
*/
struct ble_rscs_s
{
ble_rscs_evt_handler_t evt_handler; /**< Event handler to be called for handling events in the Running Speed and Cadence Service. */
uint16_t service_handle; /**< Handle of Running Speed and Cadence Service (as provided by the BLE stack). */
ble_gatts_char_handles_t meas_handles; /**< Handles related to the Running Speed and Cadence Measurement characteristic. */
ble_gatts_char_handles_t feature_handles; /**< Handles related to the Running Speed and Cadence feature characteristic. */
uint16_t conn_handle; /**< Handle of the current connection (as provided by the BLE stack, is BLE_CONN_HANDLE_INVALID if not in a connection). */
uint16_t feature; /**< Bit mask of features available on sensor. */
};
/**@brief Function for initializing the Running Speed and Cadence Service.
*
* @param[out] p_rscs Running Speed and Cadence Service structure. This structure will have to
* be supplied by the application. It will be initialized by this function,
* and will later be used to identify this particular service instance.
* @param[in] p_rscs_init Information needed to initialize the service.
*
* @return NRF_SUCCESS on successful initialization of service, otherwise an error code.
*/
uint32_t ble_rscs_init(ble_rscs_t * p_rscs, const ble_rscs_init_t * p_rscs_init);
/**@brief Function for handling the Application's BLE Stack events.
*
* @details Handles all events from the BLE stack of interest to the Running Speed and Cadence
* Service.
*
* @param[in] p_ble_evt Event received from the BLE stack.
* @param[in] p_context Running Speed and Cadence Service structure.
*/
void ble_rscs_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for sending running speed and cadence measurement if notification has been enabled.
*
* @details The application calls this function after having performed a Running Speed and Cadence
* measurement. If notification has been enabled, the measurement data is encoded and sent
* to the client.
*
* @param[in] p_rscs Running Speed and Cadence Service structure.
* @param[in] p_measurement Pointer to new running speed and cadence measurement.
*
* @return NRF_SUCCESS on success, otherwise an error code.
*/
uint32_t ble_rscs_measurement_send(ble_rscs_t * p_rscs, ble_rscs_meas_t * p_measurement);
#ifdef __cplusplus
}
#endif
#endif // BLE_RSCS_H__
/** @} */

View File

@@ -0,0 +1,323 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**@cond To Make Doxygen skip documentation generation for this file.
* @{
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_RSCS_C)
#include "ble_rscs_c.h"
#include "ble_db_discovery.h"
#include "ble_types.h"
#include "ble_gattc.h"
#define NRF_LOG_MODULE_NAME ble_rscs_c
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();
#define WRITE_MESSAGE_LENGTH BLE_CCCD_VALUE_LEN /**< Length of the write message for CCCD. */
/**@brief Function for intercepting the errors of GATTC and the BLE GATT Queue.
*
* @param[in] nrf_error Error code.
* @param[in] p_ctx Parameter from the event handler.
* @param[in] conn_handle Connection handle.
*/
static void gatt_error_handler(uint32_t nrf_error,
void * p_ctx,
uint16_t conn_handle)
{
ble_rscs_c_t * p_ble_rscs_c = (ble_rscs_c_t *)p_ctx;
NRF_LOG_DEBUG("A GATT Client error has occurred on conn_handle: 0X%X", conn_handle);
if (p_ble_rscs_c->error_handler != NULL)
{
p_ble_rscs_c->error_handler(nrf_error);
}
}
/**@brief Function for handling Handle Value Notification received from the SoftDevice.
*
* @details This function uses the Handle Value Notification received from the SoftDevice
* and checks whether it is a notification of the Running Speed and Cadence measurement from
* the peer. If it is, this function decodes the Running Speed measurement and sends it
* to the application.
*
* @param[in] p_ble_rscs_c Pointer to the Running Speed and Cadence Client structure.
* @param[in] p_ble_evt Pointer to the BLE event received.
*/
static void on_hvx(ble_rscs_c_t * p_ble_rscs_c, const ble_evt_t * p_ble_evt)
{
const ble_gattc_evt_hvx_t * p_notif = &p_ble_evt->evt.gattc_evt.params.hvx;
// Check if the event is on the link for this instance
if (p_ble_rscs_c->conn_handle != p_ble_evt->evt.gattc_evt.conn_handle)
{
return;
}
// Check if this is a Running Speed and Cadence notification.
if (p_ble_evt->evt.gattc_evt.params.hvx.handle == p_ble_rscs_c->peer_db.rsc_handle)
{
uint32_t index = 0;
ble_rscs_c_evt_t ble_rscs_c_evt;
ble_rscs_c_evt.evt_type = BLE_RSCS_C_EVT_RSC_NOTIFICATION;
ble_rscs_c_evt.conn_handle = p_ble_evt->evt.gattc_evt.conn_handle;
//lint -save -e415 -e416 -e662 "Access of out of bounds pointer" "Creation of out of bounds pointer"
// Flags field
ble_rscs_c_evt.params.rsc.is_inst_stride_len_present = p_notif->data[index] >> BLE_RSCS_INSTANT_STRIDE_LEN_PRESENT & 0x01;
ble_rscs_c_evt.params.rsc.is_total_distance_present = p_notif->data[index] >> BLE_RSCS_TOTAL_DISTANCE_PRESENT & 0x01;
ble_rscs_c_evt.params.rsc.is_running = p_notif->data[index] >> BLE_RSCS_WALKING_OR_RUNNING_STATUS_BIT & 0x01;
index++;
// Instantaneous speed
ble_rscs_c_evt.params.rsc.inst_speed = uint16_decode(&p_notif->data[index]);
index += sizeof(uint16_t);
// Instantaneous cadence
ble_rscs_c_evt.params.rsc.inst_cadence = p_notif->data[index];
index++;
// Instantaneous stride length
if (ble_rscs_c_evt.params.rsc.is_inst_stride_len_present == true)
{
ble_rscs_c_evt.params.rsc.inst_stride_length = uint16_decode(&p_notif->data[index]);
index += sizeof(uint16_t);
}
// Total distance field
if (ble_rscs_c_evt.params.rsc.is_total_distance_present == true)
{
ble_rscs_c_evt.params.rsc.total_distance = uint32_decode(&p_notif->data[index]);
//index += sizeof(uint32_t);
}
p_ble_rscs_c->evt_handler(p_ble_rscs_c, &ble_rscs_c_evt);
//lint -restore
}
}
/**@brief Function for handling events from the Database Discovery module.
*
* @details This function handles an event from the Database Discovery module, and determines
* whether it relates to the discovery of Running Speed and Cadence service at the peer. If it does, the function
* calls the application's event handler to indicate that the Running Speed and Cadence
* service was discovered at the peer. The function also populates the event with service-related
* information before providing it to the application.
*
* @param[in] p_evt Pointer to the event received from the Database Discovery module.
*
*/
void ble_rscs_on_db_disc_evt(ble_rscs_c_t * p_ble_rscs_c, const ble_db_discovery_evt_t * p_evt)
{
// Check if the Heart Rate Service was discovered.
if (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE &&
p_evt->params.discovered_db.srv_uuid.uuid == BLE_UUID_RUNNING_SPEED_AND_CADENCE &&
p_evt->params.discovered_db.srv_uuid.type == BLE_UUID_TYPE_BLE)
{
ble_rscs_c_evt_t evt;
evt.conn_handle = p_evt->conn_handle;
// Find the CCCD Handle of the Running Speed and Cadence characteristic.
for (uint32_t i = 0; i < p_evt->params.discovered_db.char_count; i++)
{
if (p_evt->params.discovered_db.charateristics[i].characteristic.uuid.uuid ==
BLE_UUID_RSC_MEASUREMENT_CHAR)
{
// Found Running Speed and Cadence characteristic. Store CCCD handle and break.
evt.params.rscs_db.rsc_cccd_handle =
p_evt->params.discovered_db.charateristics[i].cccd_handle;
evt.params.rscs_db.rsc_handle =
p_evt->params.discovered_db.charateristics[i].characteristic.handle_value;
break;
}
}
NRF_LOG_DEBUG("Running Speed and Cadence Service discovered at peer.");
// If the instance has been assigned prior to db_discovery, assign the db_handles.
if (p_ble_rscs_c->conn_handle != BLE_CONN_HANDLE_INVALID)
{
if ((p_ble_rscs_c->peer_db.rsc_cccd_handle == BLE_GATT_HANDLE_INVALID)&&
(p_ble_rscs_c->peer_db.rsc_handle == BLE_GATT_HANDLE_INVALID))
{
p_ble_rscs_c->peer_db = evt.params.rscs_db;
}
}
evt.evt_type = BLE_RSCS_C_EVT_DISCOVERY_COMPLETE;
p_ble_rscs_c->evt_handler(p_ble_rscs_c, &evt);
}
}
uint32_t ble_rscs_c_init(ble_rscs_c_t * p_ble_rscs_c, ble_rscs_c_init_t * p_ble_rscs_c_init)
{
VERIFY_PARAM_NOT_NULL(p_ble_rscs_c);
VERIFY_PARAM_NOT_NULL(p_ble_rscs_c_init);
ble_uuid_t rscs_uuid;
rscs_uuid.type = BLE_UUID_TYPE_BLE;
rscs_uuid.uuid = BLE_UUID_RUNNING_SPEED_AND_CADENCE;
p_ble_rscs_c->evt_handler = p_ble_rscs_c_init->evt_handler;
p_ble_rscs_c->error_handler = p_ble_rscs_c_init->error_handler;
p_ble_rscs_c->conn_handle = BLE_CONN_HANDLE_INVALID;
p_ble_rscs_c->peer_db.rsc_cccd_handle = BLE_GATT_HANDLE_INVALID;
p_ble_rscs_c->peer_db.rsc_handle = BLE_GATT_HANDLE_INVALID;
p_ble_rscs_c->p_gatt_queue = p_ble_rscs_c_init->p_gatt_queue;
return ble_db_discovery_evt_register(&rscs_uuid);
}
uint32_t ble_rscs_c_handles_assign(ble_rscs_c_t * p_ble_rscs_c,
uint16_t conn_handle,
ble_rscs_c_db_t * p_peer_handles)
{
VERIFY_PARAM_NOT_NULL(p_ble_rscs_c);
p_ble_rscs_c->conn_handle = conn_handle;
if (p_peer_handles != NULL)
{
p_ble_rscs_c->peer_db = *p_peer_handles;
}
return nrf_ble_gq_conn_handle_register(p_ble_rscs_c->p_gatt_queue, conn_handle);
}
/**@brief Function for handling Disconnected event received from the SoftDevice.
*
* @details This function check whether the disconnect event is happening on the link
* associated with the current instance of the module. If the event is happening, the function sets the instance's
* conn_handle to invalid.
*
* @param[in] p_ble_rscs_c Pointer to the RSC Client structure.
* @param[in] p_ble_evt Pointer to the BLE event received.
*/
static void on_disconnected(ble_rscs_c_t * p_ble_rscs_c, const ble_evt_t * p_ble_evt)
{
if (p_ble_rscs_c->conn_handle == p_ble_evt->evt.gap_evt.conn_handle)
{
p_ble_rscs_c->conn_handle = BLE_CONN_HANDLE_INVALID;
p_ble_rscs_c->peer_db.rsc_cccd_handle = BLE_GATT_HANDLE_INVALID;
p_ble_rscs_c->peer_db.rsc_handle = BLE_GATT_HANDLE_INVALID;
}
}
void ble_rscs_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
if ((p_context == NULL) || (p_ble_evt == NULL))
{
return;
}
ble_rscs_c_t * p_ble_rscs_c = (ble_rscs_c_t *)p_context;
switch (p_ble_evt->header.evt_id)
{
case BLE_GATTC_EVT_HVX:
on_hvx(p_ble_rscs_c, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
on_disconnected(p_ble_rscs_c, p_ble_evt);
break;
default:
break;
}
}
/**@brief Function for creating a message for writing to the CCCD.
*/
static uint32_t cccd_configure(ble_rscs_c_t * p_ble_rscs_c, bool enable)
{
NRF_LOG_DEBUG("Configuring CCCD. CCCD Handle = %d, Connection Handle = %d",
p_ble_rscs_c->peer_db.rsc_cccd_handle,
p_ble_rscs_c->conn_handle);
uint8_t cccd[WRITE_MESSAGE_LENGTH];
uint16_t cccd_val = enable ? BLE_GATT_HVX_NOTIFICATION : 0;
nrf_ble_gq_req_t rscs_c_req;
cccd[0] = LSB_16(cccd_val);
cccd[1] = MSB_16(cccd_val);
memset(&rscs_c_req, 0, sizeof(rscs_c_req));
rscs_c_req.type = NRF_BLE_GQ_REQ_GATTC_WRITE;
rscs_c_req.error_handler.cb = gatt_error_handler;
rscs_c_req.error_handler.p_ctx = p_ble_rscs_c;
rscs_c_req.params.gattc_write.handle = p_ble_rscs_c->peer_db.rsc_cccd_handle;
rscs_c_req.params.gattc_write.len = WRITE_MESSAGE_LENGTH;
rscs_c_req.params.gattc_write.p_value = cccd;
rscs_c_req.params.gattc_write.offset = 0;
rscs_c_req.params.gattc_write.write_op = BLE_GATT_OP_WRITE_REQ;
return nrf_ble_gq_item_add(p_ble_rscs_c->p_gatt_queue, &rscs_c_req, p_ble_rscs_c->conn_handle);
}
uint32_t ble_rscs_c_rsc_notif_enable(ble_rscs_c_t * p_ble_rscs_c)
{
VERIFY_PARAM_NOT_NULL(p_ble_rscs_c);
if (p_ble_rscs_c->conn_handle == BLE_CONN_HANDLE_INVALID)
{
return NRF_ERROR_INVALID_STATE;
}
return cccd_configure(p_ble_rscs_c, true);
}
/** @}
* @endcond
*/
#endif // NRF_MODULE_ENABLED(BLE_RSCS_C)

View File

@@ -0,0 +1,237 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* @defgroup ble_rscs_c Running Speed and Cadence Service Client
* @{
* @ingroup ble_sdk_srv
*
* @details This module contains the APIs and types exposed by the Running Speed and Cadence
* Service Client module. The application can use these APIs and types to perform
* discovery of Running Speed and Cadence Service at the peer and interact with it.
*
* @note The application must register this module as BLE event observer by using the
* NRF_SDH_BLE_OBSERVER macro. Example:
* @code
* ble_rscs_c_t instance;
* NRF_SDH_BLE_OBSERVER(anything, BLE_RSCS_C_BLE_OBSERVER_PRIO,
* ble_rscs_c_on_ble_evt, &instance);
* @endcode
*/
#ifndef BLE_RSCS_C_H__
#define BLE_RSCS_C_H__
#include <stdint.h>
#include <stdbool.h>
#include "ble.h"
#include "ble_db_discovery.h"
#include "ble_srv_common.h"
#include "nrf_ble_gq.h"
#include "nrf_sdh_ble.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Macro for defining a ble_rscs_c instance.
*
* @param _name Name of the instance.
* @hideinitializer
*/
#define BLE_RSCS_C_DEF(_name) \
static ble_rscs_c_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_RSCS_C_BLE_OBSERVER_PRIO, \
ble_rscs_c_on_ble_evt, &_name)
/** @brief Macro for defining multiple ble_rscs_c instances.
*
* @param _name Name of the array of instances.
* @param _cnt Number of instances to define.
* @hideinitializer
*/
#define BLE_RSCS_C_ARRAY_DEF(_name, _cnt) \
static ble_rscs_c_t _name[_cnt]; \
NRF_SDH_BLE_OBSERVERS(_name ## _obs, \
BLE_RSCS_C_BLE_OBSERVER_PRIO, \
ble_rscs_c_on_ble_evt, &_name, _cnt)
#define BLE_RSCS_INSTANT_STRIDE_LEN_PRESENT 0x00 /**< Instantaneous Stride Length Measurement Supported bit. */
#define BLE_RSCS_TOTAL_DISTANCE_PRESENT 0x01 /**< Total Distance Measurement Supported bit. */
#define BLE_RSCS_WALKING_OR_RUNNING_STATUS_BIT 0x02 /**< Walking or Running Status Supported bit. */
/**@brief Structure containing the handles related to the Running Speed and Cadence Service found on the peer. */
typedef struct
{
uint16_t rsc_cccd_handle; /**< Handle of the CCCD of the Running Speed and Cadence characteristic. */
uint16_t rsc_handle; /**< Handle of the Running Speed and Cadence characteristic as provided by the SoftDevice. */
} ble_rscs_c_db_t;
/**@brief RSCS Client event type. */
typedef enum
{
BLE_RSCS_C_EVT_DISCOVERY_COMPLETE = 1, /**< Event indicating that the Running Speed and Cadence Service has been discovered at the peer. */
BLE_RSCS_C_EVT_RSC_NOTIFICATION /**< Event indicating that a notification of the Running Speed and Cadence measurement characteristic has been received from the peer. */
} ble_rscs_c_evt_type_t;
/**@brief Structure containing the Running Speed and Cadence measurement received from the peer. */
typedef struct
{
bool is_inst_stride_len_present; /**< True if Instantaneous Stride Length is present in the measurement. */
bool is_total_distance_present; /**< True if Total Distance is present in the measurement. */
bool is_running; /**< True if running, False if walking. */
uint16_t inst_speed; /**< Instantaneous Speed. */
uint8_t inst_cadence; /**< Instantaneous Cadence. */
uint16_t inst_stride_length; /**< Instantaneous Stride Length. */
uint32_t total_distance; /**< Total Distance. */
} ble_rsc_t;
/**@brief Running Speed and Cadence Event structure. */
typedef struct
{
ble_rscs_c_evt_type_t evt_type; /**< Type of the event. */
uint16_t conn_handle; /**< Connection handle on which the rscs_c event occured.*/
union
{
ble_rscs_c_db_t rscs_db; /**< Running Speed and Cadence Service related handles found on the peer device. This is filled if the evt_type is @ref BLE_RSCS_C_EVT_DISCOVERY_COMPLETE.*/
ble_rsc_t rsc; /**< Running Speed and Cadence measurement received. This is filled if the evt_type is @ref BLE_RSCS_C_EVT_RSC_NOTIFICATION. */
} params;
} ble_rscs_c_evt_t;
// Forward declaration of the ble_rscs_c_t type.
typedef struct ble_rscs_c_s ble_rscs_c_t;
/**@brief Event handler type.
*
* @details This is the type of the event handler that is to be provided by the application
* of this module in order to receive events.
*/
typedef void (* ble_rscs_c_evt_handler_t) (ble_rscs_c_t * p_ble_rscs_c, ble_rscs_c_evt_t * p_evt);
/**@brief Running Speed and Cadence client structure. */
struct ble_rscs_c_s
{
uint16_t conn_handle; /**< Connection handle as provided by the SoftDevice. */
ble_rscs_c_db_t peer_db; /**< Handles related to RSCS on the peer*/
ble_rscs_c_evt_handler_t evt_handler; /**< Application event handler to be called when there is an event related to the Running Speed and Cadence service. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to BLE GATT Queue instance. */
};
/**@brief Running Speed and Cadence client initialization structure. */
typedef struct
{
ble_rscs_c_evt_handler_t evt_handler; /**< Event handler to be called by the Running Speed and Cadence Client module whenever there is an event related to the Running Speed and Cadence Service. */
ble_srv_error_handler_t error_handler; /**< Function to be called in case of an error. */
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to BLE GATT Queue instance. */
} ble_rscs_c_init_t;
/**@brief Function for initializing the Running Speed and Cadence Service Client module.
*
* @details This function will initialize the module and set up Database Discovery to discover
* the Running Speed and Cadence Service. After calling this function, call @ref ble_db_discovery_start
* to start discovery once a link with a peer has been established.
*
* @param[out] p_ble_rscs_c Pointer to the RSC Service Client structure.
* @param[in] p_ble_rscs_c_init Pointer to the RSC Service initialization structure containing
* the initialization information.
*
* @retval NRF_SUCCESS Operation success.
* @retval NRF_ERROR_NULL A parameter is NULL.
* @retval err_code Otherwise, this function propagates the error code returned by @ref ble_db_discovery_evt_register.
*/
uint32_t ble_rscs_c_init(ble_rscs_c_t * p_ble_rscs_c, ble_rscs_c_init_t * p_ble_rscs_c_init);
/**@brief Function for handling the Application's BLE Stack events.
*
* @details Handles all events from the BLE stack that are of interest to the Running Speed and Cadence
* Service Client.
*
* @param[in] p_ble_evt Event received from the BLE stack.
* @param[in] p_context Running Speed and Cadence Service Client structure.
*/
void ble_rscs_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
uint32_t ble_rscs_c_rsc_notif_enable(ble_rscs_c_t * p_ble_rscs_c);
/**@brief Function for handling events from the Database Discovery module.
*
* @details Call this function when you get a callback event from the Database Discovery module.
* This function handles an event from the Database Discovery module, and determines
* whether it relates to the discovery of Running Speed and Cadence Service at the peer.
* If it does, the function calls the application's event handler to indicate that the RSC Service was
* discovered at the peer. The function also populates the event with service-related
* information before providing it to the application.
*
* @param p_ble_rscs_c Pointer to the Runnind Speed and Cadence Service Client structure.
* @param[in] p_evt Pointer to the event received from the Database Discovery module.
*/
void ble_rscs_on_db_disc_evt(ble_rscs_c_t * p_ble_rscs_c, ble_db_discovery_evt_t const * p_evt);
/**@brief Function for assigning handles to this instance of rscs_c.
*
* @details Call this function when a link has been established with a peer to
* associate the link to this instance of the module. This makes it
* possible to handle several links and associate each link to a particular
* instance of this module. The connection handle and attribute handles are
* provided from the discovery event @ref BLE_RSCS_C_EVT_DISCOVERY_COMPLETE.
*
* @param[in] p_ble_rscs_c Pointer to the RSC client structure instance for associating the link.
* @param[in] conn_handle Connection handle to associated with the given RSCS Client Instance.
* @param[in] p_peer_handles Attribute handles on the RSCS server that you want this RSCS client
* to interact with.
*/
uint32_t ble_rscs_c_handles_assign(ble_rscs_c_t * p_ble_rscs_c,
uint16_t conn_handle,
ble_rscs_c_db_t * p_peer_handles);
#ifdef __cplusplus
}
#endif
#endif // BLE_RSCS_C_H__
/** @} */ // End tag for the file.

View File

@@ -0,0 +1,128 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* Attention!
* To maintain compliance with Nordic Semiconductor ASA's Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#include "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_TPS)
#include "ble_tps.h"
#include <string.h>
#include "ble_srv_common.h"
/**@brief Function for handling the Connect event.
*
* @param[in] p_tps TX Power Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_connect(ble_tps_t * p_tps, ble_evt_t const * p_ble_evt)
{
p_tps->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
}
void ble_tps_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
ble_tps_t * p_tps = (ble_tps_t *)p_context;
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
on_connect(p_tps, p_ble_evt);
break;
default:
// No implementation needed.
break;
}
}
uint32_t ble_tps_init(ble_tps_t * p_tps, const ble_tps_init_t * p_tps_init)
{
uint32_t err_code;
ble_uuid_t ble_uuid;
ble_add_char_params_t add_char_params;
// Add service
BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_TX_POWER_SERVICE);
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
&ble_uuid,
&p_tps->service_handle);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
// Add TX Power Level characteristic
memset(&add_char_params, 0, sizeof(add_char_params));
add_char_params.uuid = BLE_UUID_TX_POWER_LEVEL_CHAR;
add_char_params.max_len = sizeof(uint8_t);
add_char_params.init_len = sizeof(uint8_t);
add_char_params.p_init_value = (uint8_t *) &p_tps_init->initial_tx_power_level;
add_char_params.char_props.read = 1;
add_char_params.read_access = p_tps_init->tpl_rd_sec;
return characteristic_add(p_tps->service_handle,
&add_char_params,
&p_tps->tx_power_level_handles);
}
uint32_t ble_tps_tx_power_level_set(ble_tps_t * p_tps, int8_t tx_power_level)
{
ble_gatts_value_t gatts_value;
// Initialize value struct.
memset(&gatts_value, 0, sizeof(gatts_value));
gatts_value.len = sizeof(uint8_t);
gatts_value.offset = 0;
gatts_value.p_value = (uint8_t*)&tx_power_level;
// Update database
return sd_ble_gatts_value_set(p_tps->conn_handle,
p_tps->tx_power_level_handles.value_handle,
&gatts_value);
}
#endif // NRF_MODULE_ENABLED(BLE_TPS)

View File

@@ -0,0 +1,138 @@
/**
* Copyright (c) 2012 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_tps TX Power Service
* @{
* @ingroup ble_sdk_srv
* @brief TX Power Service module.
*
* @details This module implements the TX Power Service with the TX Power Level characteristic.
* During initialization it adds the TX Power Service and TX Power Level characteristic
* with the specified initial value to the BLE stack database.
*
* It provides a function for letting the application update the TX Power Level
* characteristic.
*
* @note Attention!
* To maintain compliance with Nordic Semiconductor ASA Bluetooth profile
* qualification listings, this section of source code must not be modified.
*/
#ifndef BLE_TPS_H__
#define BLE_TPS_H__
#include <stdint.h>
#include "ble.h"
#include "ble_srv_common.h"
#include "nrf_sdh_ble.h"
#ifdef __cplusplus
extern "C" {
#endif
/**@brief Macro for defining a ble_tps instance.
*
* @param _name Name of the instance.
* @hideinitializer
*/
#define BLE_TPS_DEF(_name) \
static ble_tps_t _name; \
NRF_SDH_BLE_OBSERVER(_name ## _obs, \
BLE_TPS_BLE_OBSERVER_PRIO, \
ble_tps_on_ble_evt, &_name)
/**@brief TX Power Service init structure. This contains all options and data needed for
* initialization of the service. */
typedef struct
{
int8_t initial_tx_power_level; /**< Initial value of the TX Power Level characteristic (in dBm). */
security_req_t tpl_rd_sec; /**< Security requirement for reading TX Power Level characteristic. */
} ble_tps_init_t;
/**@brief TX Power Service structure. This contains various status information for the service. */
typedef struct
{
uint16_t service_handle; /**< Handle of TX Power Service (as provided by the BLE stack). */
ble_gatts_char_handles_t tx_power_level_handles; /**< Handles related to the TX Power Level characteristic. */
uint16_t conn_handle; /**< Handle of the current connection (as provided by the BLE stack, is BLE_CONN_HANDLE_INVALID if not in a connection). */
} ble_tps_t;
/**@brief Function for initializing the TX Power Service.
*
* @param[out] p_hrs TX Power Service structure. This structure will have to be supplied by
* the application. It will be initialized by this function, and will later
* be used to identify this particular service instance.
* @param[in] p_tps_init Information needed to initialize the service.
*
* @return NRF_SUCCESS on successful initialization of service, otherwise an error code.
*/
uint32_t ble_tps_init(ble_tps_t * p_hrs, const ble_tps_init_t * p_tps_init);
/**@brief Function for handling the Application's BLE Stack events.
*
* @details Handles all events from the BLE stack of interest to the TX Power Service.
*
* @param[in] p_ble_evt Event received from the BLE stack.
* @param[in] p_context TX Power Service structure.
*/
void ble_tps_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for setting the value of the TX Power Level characteristic.
*
* @param[in] p_tps TX Power Service structure.
* @param[in] tx_power_level New TX Power Level (unit dBm, range -100 to 20).
*
* @return NRF_SUCCESS on success, otherwise an error code.
*/
uint32_t ble_tps_tx_power_level_set(ble_tps_t * p_tps, int8_t tx_power_level);
#ifdef __cplusplus
}
#endif
#endif // BLE_TPS_H__
/** @} */

View File

@@ -0,0 +1,198 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef ES_H__
#define ES_H__
#include <stdint.h>
#include "app_util_platform.h"
/**
* @file
*
* @defgroup eddystone_types Frame types and data formats
* @brief Definitions specific to Eddystone frame types and data formats.
* @ingroup eddystone
* @{
*/
/** @brief Swap 2 bytes.
*/
#define BYTES_SWAP_16BIT(x) (x << 8 | x >> 8)
/** @brief Reverse 4 bytes.
*/
#define BYTES_REVERSE_32BIT(x) \
((x << 24 | ((x << 8) & 0x00FF0000)) | (((x >> 8) & 0x0000FF00) | x >> 24))
/** @brief Check if the error code is equal to NRF_SUCCESS. If it is not, return the error code.
*/
#define RETURN_IF_ERROR(PARAM) \
if ((PARAM) != NRF_SUCCESS) \
{ \
return (PARAM); \
}
#define ES_UUID 0xFEAA //!< UUID for Eddystone beacons according to specification.
#define ES_UID_FRAME_TYPE 0x00 //!< UID frame type (fixed at 0x00).
#define ES_UID_RFU 0x00, 0x00 //!< Reserved for future use according to specification.
#define ES_URL_FRAME_TYPE 0x10 //!< URL frame type (fixed at 0x10).
#define ES_URL_SCHEME 0x00 //!< URL prefix scheme according to specification (0x00 = "http://www").
#define ES_TLM_FRAME_TYPE 0x20 //!< TLM frame type (fixed at 0x20).
#define ES_EID_FRAME_TYPE 0x30 //!< EID frame type (fixed at 0x30).
#define ES_FRAME_TYPE_LENGTH (1) //!< Length of a frame type field.
#define ES_UID_LENGTH (20) //!< Length of a UID frame.
#define ES_UID_NAMESPACE_LENGTH (10) //!< Length of a UID frame namespace field.
#define ES_UID_INSTANCE_LENGTH (6) //!< Length of a UID frame instance field.
#define ES_UID_RFU_LENGTH (2) //!< Length of a UID frame RFU field.
#define ES_URL_LENGTH (20) //!< Length of a URL frame.
#define ES_URL_URL_SCHEME_LENGTH (1) //!< Length of a URL frame URL scheme field.
#define ES_URL_ENCODED_URL_LENGTH (17) //!< Maximum length of a URL frame encoded URL field.
#define ES_TLM_LENGTH (14) //!< Length of a TLM frame.
#define ES_TLM_VBATT_LENGTH (2) //!< Length of a TLM frame VBATT field.
#define ES_TLM_TEMP_LENGTH (2) //!< Length of a TLM frame TEMP field.
#define ES_TLM_ADV_CNT_LENGTH (4) //!< Length of a TLM frame ADV count field.
#define ES_TLM_SEC_CNT_LENGTH (4) //!< Length of a TLM frame seconds field.
#define ES_EID_LENGTH (10) //!< Length of an EID frame.
#define ES_EID_ID_LENGTH (8) //!< Length of an EID frame ephemeral ID field.
#define ES_EID_GATTS_READ_LENGTH (14)
#define ES_EID_GATTS_READ_FRAME_TYPE_IDX (0)
#define ES_EID_GATTS_READ_EXPONENT_IDX (1)
#define ES_EID_GATTS_READ_CLCK_VALUE_IDX (2)
#define ES_EID_GATTS_READ_EID_IDX (6)
#define ES_ETLM_LENGTH (18) //!< Length of an eTLM frame.
#define ES_ETLM_ECRYPTED_LENGTH (ES_TLM_VBATT_LENGTH + \
ES_TLM_TEMP_LENGTH + \
ES_TLM_ADV_CNT_LENGTH + \
ES_TLM_SEC_CNT_LENGTH) //!< Length of an eTLM frame encrypted TLM field.
#define ES_ETLM_RFU (0x00) //!< eTLM frame RFU field value.
#define ES_SPEC_VERSION_BYTE (0x00) //!< eTLM frame specification version field value.
/** @brief Eddystone frame type values. These values are advertised as frame types. */
typedef enum
{
ES_FRAME_TYPE_UID = ES_UID_FRAME_TYPE, /**< UID frame type. */
ES_FRAME_TYPE_URL = ES_URL_FRAME_TYPE, /**< URL frame type. */
ES_FRAME_TYPE_TLM = ES_TLM_FRAME_TYPE, /**< TLM frame type. */
ES_FRAME_TYPE_EID = ES_EID_FRAME_TYPE /**< EID frame type. */
} es_frame_type_t;
/** @brief TLM version values. */
typedef enum
{
ES_TLM_VERSION_TLM = 0x00, /**< TLM. */
ES_TLM_VERSION_ETLM = 0x01 /**< Encrypted TLM (eTLM). */
} es_tlm_version_t;
/** @brief UID frame data representation.
* @note This is a packed structure. Therefore, you should not change it.
*/
typedef PACKED_STRUCT
{
es_frame_type_t frame_type; //!< Frame type (see @ref es_frame_type_t).
int8_t ranging_data; //!< Calibrated TX power at 0 m.
int8_t namespace[ES_UID_NAMESPACE_LENGTH]; //!< UID namespace.
int8_t instance[ES_UID_INSTANCE_LENGTH]; //!< UID instance.
int8_t rfu[ES_UID_RFU_LENGTH]; //!< RFU.
} es_uid_frame_t;
/** @brief URL frame data representation.
* @note This is a packed structure. Therefore, you should not change it.
*/
typedef PACKED_STRUCT
{
es_frame_type_t frame_type; //!< Frame type (see @ref es_frame_type_t).
int8_t ranging_data; //!< Calibrated TX power at 0 m.
uint8_t url_scheme; //!< URL scheme.
uint8_t encoded_url[ES_URL_ENCODED_URL_LENGTH]; //!< Encoded URL (variable length).
} es_url_frame_t;
/** @brief TLM frame data representation.
* @note This is a packed structure. Therefore, you should not change it.
*/
typedef PACKED_STRUCT
{
es_frame_type_t frame_type; //!< Frame type (see @ref es_frame_type_t).
es_tlm_version_t version; //!< TLM version (see @ref es_tlm_version_t).
int8_t vbatt[ES_TLM_VBATT_LENGTH]; //!< Battery voltage (in 1 mV units).
int8_t temp[ES_TLM_TEMP_LENGTH]; //!< Beacon temperature.
int8_t adv_cnt[ES_TLM_ADV_CNT_LENGTH]; //!< Advertising PDU count.
int8_t sec_cnt[ES_TLM_SEC_CNT_LENGTH]; //!< Time since power-on or reboot.
} es_tlm_frame_t;
/** @brief EID frame data representation.
* @note This is a packed structure. Therefore, you should not change it.
*/
typedef PACKED_STRUCT
{
es_frame_type_t frame_type; //!< Frame type (see @ref es_frame_type_t).
int8_t ranging_data; //!< Calibrated TX power at 0 m.
int8_t eid[ES_EID_ID_LENGTH]; //!< 8-byte ephemeral identifier.
} es_eid_frame_t;
/** @brief eTLM frame data representation.
* @note This is a packed structure. Therefore, you should not change it.
*/
typedef PACKED_STRUCT
{
es_frame_type_t frame_type; //!< Frame type (see @ref es_frame_type_t).
es_tlm_version_t version; //!< TLM version (see @ref es_tlm_version_t).
int8_t encrypted_tlm[ES_ETLM_ECRYPTED_LENGTH]; //!< Encrypted TLM data.
int16_t random_salt; //!< Salt
int16_t msg_integrity_check; //!< Message integrity check.
} es_etlm_frame_t;
/**
* @}
*/
#endif // ES_H__

View File

@@ -0,0 +1,359 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "es_adv.h"
#include "app_error.h"
#include "es_adv_frame.h"
#include "es_adv_timing.h"
#include "es_tlm.h"
#include "es_slot.h"
#define MULTIPROT_BEACON_DELAY_MS 75 //!< Maximum delay of the beacon send by multiprotocol example.
static es_adv_evt_handler_t m_adv_evt_handler; //!< Eddystone advertisement event handler.
static bool m_is_connected = false; //!< Is the Eddystone beacon in a connected state.
static bool m_remain_connectable = false; //!< Should the Eddystone beacon remain connectable.
static uint8_t m_ecs_uuid_type = 0; //!< UUID type of the Eddystone Configuration Service.
static uint16_t m_adv_interval = APP_CFG_NON_CONN_ADV_INTERVAL_MS; //!< Current advertisement interval.
static uint8_t m_enc_advdata[BLE_GAP_ADV_SET_DATA_SIZE_MAX]; //!< Buffer for storing an encoded advertising set.
static uint8_t m_enc_scan_response_data[BLE_GAP_ADV_SET_DATA_SIZE_MAX]; //!< Buffer for storing an encoded scan data.
static uint8_t *mp_adv_handle; //!< Pointer to the advertising handle.
/**@brief Struct that contains pointers to the encoded advertising data. */
static ble_gap_adv_data_t m_adv_data =
{
.adv_data =
{
.p_data = m_enc_advdata,
.len = BLE_GAP_ADV_SET_DATA_SIZE_MAX
},
.scan_rsp_data =
{
.p_data = m_enc_scan_response_data,
.len = BLE_GAP_ADV_SET_DATA_SIZE_MAX
}
};
/**@brief Function for invoking registered callback.
*
* @param[in] evt Event to issue to callback.
*/
static void invoke_callback(es_adv_evt_t evt)
{
if (m_adv_evt_handler != NULL)
{
m_adv_evt_handler(evt);
}
}
/**@brief Starting advertising.
* @param[in] p_adv_params Advertisement parameters to use.
*/
static void adv_start(ble_gap_adv_params_t * p_adv_params)
{
ret_code_t err_code = NRF_SUCCESS;
es_tlm_adv_cnt_inc();
err_code = sd_ble_gap_adv_set_configure(mp_adv_handle, &m_adv_data, p_adv_params);
APP_ERROR_CHECK(err_code);
err_code = sd_ble_gap_adv_start(*mp_adv_handle, BLE_CONN_CFG_TAG_DEFAULT);
if (err_code != NRF_ERROR_BUSY && err_code != NRF_SUCCESS)
{
APP_ERROR_CHECK(err_code);
}
}
/**@brief Given state of Eddystone beacon, get advertisement parameters. */
static void get_adv_params(ble_gap_adv_params_t * p_adv_params,
bool non_connectable,
bool remain_connectable)
{
// Initialize advertising parameters (used when starting advertising).
memset(p_adv_params, 0, sizeof(ble_gap_adv_params_t));
// Non-connectable
p_adv_params->properties.type = non_connectable
? BLE_GAP_ADV_TYPE_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED
: BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED;
p_adv_params->p_peer_addr = NULL; // Undirected advertisement.
p_adv_params->filter_policy = BLE_GAP_ADV_FP_ANY;
p_adv_params->primary_phy = BLE_GAP_PHY_1MBPS;
if (non_connectable)
{
#ifdef MULTIPROTOCOL_802154_MODE
/* In case the Eddystone component is used by multiprotocol example,
calculate the interval taking into account that beacon may be sent with a delay.
MULTIPROTOCOL_802154_MODE is defined for multiprotocol examples only.
*/
p_adv_params->interval = MSEC_TO_UNITS(((m_adv_interval - MULTIPROT_BEACON_DELAY_MS) > 0 ? (m_adv_interval - MULTIPROT_BEACON_DELAY_MS) : m_adv_interval), UNIT_0_625_MS);
#else
p_adv_params->interval = MSEC_TO_UNITS(m_adv_interval, UNIT_0_625_MS);
#endif // MULTIPROTOCOL_802154_MODE
p_adv_params->duration = BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED;
}
else
{
p_adv_params->interval = MSEC_TO_UNITS(APP_CFG_CONNECTABLE_ADV_INTERVAL_MS, UNIT_0_625_MS);
p_adv_params->duration = APP_CFG_CONNECTABLE_ADV_TIMEOUT;
}
}
/**@brief Update advertisement data and start connectable advertisements. */
static void connectable_adv_start(void)
{
ble_gap_adv_params_t connectable_adv_params;
ble_advdata_t scrsp_data;
ble_uuid_t scrp_uuids[] = {{BLE_UUID_ESCS_SERVICE, m_ecs_uuid_type}};
memset(&scrsp_data, 0, sizeof(scrsp_data));
scrsp_data.name_type = BLE_ADVDATA_FULL_NAME;
scrsp_data.include_appearance = false;
scrsp_data.uuids_complete.uuid_cnt = sizeof(scrp_uuids) / sizeof(scrp_uuids[0]);
scrsp_data.uuids_complete.p_uuids = scrp_uuids;
m_adv_data.scan_rsp_data.p_data = m_enc_scan_response_data;
m_adv_data.scan_rsp_data.len = BLE_GAP_ADV_SET_DATA_SIZE_MAX;
// As the data to be written does not depend on the slot_no, we can safely send
es_adv_frame_fill_connectable_adv_data(&scrsp_data, &m_adv_data);
get_adv_params(&connectable_adv_params, false, m_remain_connectable);
adv_start(&connectable_adv_params);
invoke_callback(ES_ADV_EVT_CONNECTABLE_ADV_STARTED);
}
static void adv_stop(void)
{
ret_code_t err_code;
err_code = sd_ble_gap_adv_stop(*mp_adv_handle);
if (err_code != NRF_ERROR_INVALID_STATE)
{
APP_ERROR_CHECK(err_code);
}
es_adv_timing_stop();
}
static void adv_restart(void)
{
if (!m_remain_connectable)
{
es_adv_start_non_connctable_adv();
}
else
{
connectable_adv_start();
}
}
/**@brief Function handling events from @ref es_adv_timing.c.
*
* @param[in] p_evt Advertisement timing event.
*/
static void adv_timing_callback(const es_adv_timing_evt_t * p_evt)
{
ret_code_t err_code;
ble_gap_adv_params_t non_connectable_adv_params;
const es_slot_reg_t * p_reg = es_slot_get_registry();
// As new advertisement data will be loaded, stop advertising.
err_code = sd_ble_gap_adv_stop(*mp_adv_handle);
if (err_code != NRF_ERROR_INVALID_STATE && err_code != BLE_ERROR_INVALID_ADV_HANDLE)
{
APP_ERROR_CHECK(err_code);
}
// If a non-eTLM frame is to be advertised.
if (p_evt->evt_id == ES_ADV_TIMING_EVT_ADV_SLOT)
{
err_code = sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_ADV, 0, p_reg->slots[p_evt->slot_no].radio_tx_pwr);
if (err_code != BLE_ERROR_INVALID_ADV_HANDLE)
{
APP_ERROR_CHECK(err_code);
}
es_adv_frame_fill_non_connectable_adv_data(p_evt->slot_no, false, &m_adv_data);
}
// If an eTLM frame is to be advertised
else if (p_evt->evt_id == ES_ADV_TIMING_EVT_ADV_ETLM)
{
err_code = sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_ADV, 0, p_reg->slots[p_reg->tlm_slot].radio_tx_pwr);
APP_ERROR_CHECK(err_code);
es_adv_frame_fill_non_connectable_adv_data(p_evt->slot_no, true, &m_adv_data);
}
invoke_callback(ES_ADV_EVT_NON_CONN_ADV);
get_adv_params(&non_connectable_adv_params, true, m_remain_connectable);
adv_start(&non_connectable_adv_params);
}
void es_adv_start_connectable_adv(void)
{
if (!m_is_connected)
{
adv_stop();
connectable_adv_start();
}
}
void es_adv_start_non_connctable_adv(void)
{
es_adv_timing_start(m_adv_interval);
}
void es_adv_remain_connectable_set(bool remain_connectable)
{
m_remain_connectable = remain_connectable;
}
bool es_adv_remain_connectable_get(void)
{
return m_remain_connectable;
}
void es_adv_on_ble_evt(ble_evt_t const * p_ble_evt)
{
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
m_is_connected = true;
// The beacon must provide these advertisements for the client to see updated values
// during the connection.
es_adv_start_non_connctable_adv();
break;
case BLE_GAP_EVT_DISCONNECTED:
m_is_connected = false;
// Stop all advertising to give some time for writing to flash.
adv_stop();
adv_restart();
break;
case BLE_GAP_EVT_ADV_SET_TERMINATED:
if (p_ble_evt->evt.gap_evt.params.adv_set_terminated.reason == BLE_GAP_EVT_ADV_SET_TERMINATED_REASON_TIMEOUT &&
!m_is_connected)
{
invoke_callback(ES_ADV_EVT_CONNECTABLE_ADV_STOPPED);
adv_restart();
}
break;
default:
break;
}
}
void es_adv_interval_set(nrf_ble_escs_adv_interval_t interval)
{
const es_slot_reg_t * p_reg = es_slot_get_registry();
uint16_t min_valid_adv_interval;
bool eTLM_required = (p_reg->num_configured_eid_slots > 0) && (p_reg->tlm_configured);
min_valid_adv_interval = eTLM_required ? \
p_reg->num_configured_slots * (APP_CONFIG_ADV_FRAME_SPACING_MS_MIN + \
APP_CONFIG_ADV_FRAME_ETLM_SPACING_MS) \
: \
p_reg->num_configured_slots * APP_CONFIG_ADV_FRAME_SPACING_MS_MIN;
m_adv_interval = (interval > min_valid_adv_interval) ? interval : min_valid_adv_interval;
#ifdef APP_CONFIG_ADV_INTERVAL_MS_MAX
if (m_adv_interval > APP_CONFIG_ADV_INTERVAL_MS_MAX)
{
m_adv_interval = APP_CONFIG_ADV_INTERVAL_MS_MAX;
}
#endif // APP_CONFIG_ADV_INTERVAL_MS_MAX
}
nrf_ble_escs_adv_interval_t es_adv_interval_get(void)
{
return m_adv_interval;
}
void es_adv_init(uint8_t ecs_uuid_type,
es_adv_evt_handler_t adv_event_handler,
nrf_ble_escs_adv_interval_t adv_interval,
bool remain_connectable,
uint8_t * const p_adv_handle)
{
m_ecs_uuid_type = ecs_uuid_type;
m_adv_evt_handler = adv_event_handler;
m_is_connected = false;
m_remain_connectable = remain_connectable;
m_adv_interval = adv_interval;
mp_adv_handle = p_adv_handle;
es_tlm_init();
es_adv_timing_init(adv_timing_callback);
}
void es_adv_timers_init(void)
{
es_adv_timing_timers_init();
}

View File

@@ -0,0 +1,129 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef ES_ADV_H__
#define ES_ADV_H__
#include "nrf_ble_escs.h"
/**
* @file
* @defgroup eddystone_adv Eddystone advertising module
* @brief Types and functions for handling advertising in Eddystone beacons.
* @ingroup eddystone
* @{
*/
/** @brief Eddystone Advertiser events. */
typedef enum
{
ES_ADV_EVT_NON_CONN_ADV,
ES_ADV_EVT_CONNECTABLE_ADV_STARTED,
ES_ADV_EVT_CONNECTABLE_ADV_STOPPED,
} es_adv_evt_t;
/** @brief Eddystone Advertiser event handler. */
typedef void (*es_adv_evt_handler_t)(es_adv_evt_t evt);
/** @brief Function for initializing the module.
*
* @param[in] ecs_uuid_type ECS UUID type used for advertising the Eddystone Configuration Service UUID.
* @param[in] adv_event_handler Eddystone advertiser event handler.
* @param[in] adv_interval Advertisement interval to use.
* @param[in] remain_connectable Flag that specifies if advertisements should remain connectable.
* @param[in] p_adv_handle Pointer to the advertising handle used to start and stop advertising.
*/
void es_adv_init(uint8_t ecs_uuid_type,
es_adv_evt_handler_t adv_event_handler,
nrf_ble_escs_adv_interval_t adv_interval,
bool remain_connectable,
uint8_t * const p_adv_handle);
/** @brief Function for passing BLE events to this module.
*
* @param[in] p_ble_evt Pointer to the BLE evt.
*/
void es_adv_on_ble_evt(ble_evt_t const * p_ble_evt);
/** @brief Function for starting the advertisements.
*/
void es_adv_start_non_connctable_adv(void);
/** @brief Function for specifying if the beacon should remain connectable.
*
* @param[in] remain_connectable Value to be set.
*/
void es_adv_remain_connectable_set(bool remain_connectable);
/** @brief Function for starting connectable advertisements.
*/
void es_adv_start_connectable_adv(void);
/** @brief Function for setting the base advertisement interval for non-connectable advertisements.
*
* The minimum allowed advertisement interval is calculated based on the configured minimum advertisement
* frame spacings and the number of configured slots. If eTLM slots are configured a separate minimum
* advertisement frame spacing is used for those. If @p interval is outside of range, the closest valid value
* is set.
*
* @param interval The new advertisement interval.
*/
void es_adv_interval_set(nrf_ble_escs_adv_interval_t interval);
/** @brief Function for getting a pointer to the current advertisement interval.
*
* @retval Pointer to the current advertisement interval.
*/
nrf_ble_escs_adv_interval_t es_adv_interval_get(void);
/** @brief Function for getting the value of the 'remain_connectable' field.
*
* @retval Value of 'remain_connectable'.
*/
bool es_adv_remain_connectable_get(void);
/** @brief Function for initializing the Eddystone advertisement timers.
*/
void es_adv_timers_init(void);
/**
* @}
*/
#endif // ES_ADV_H__

View File

@@ -0,0 +1,120 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "es_adv_frame.h"
#include "es_slot.h"
/**@brief Function for setting advertisement data, using 'ble_advdata_encode'.
*
* @param[in] p_scrsp_data Scan response data.
* @param[in] p_es_data_array Eddystone service data array.
*/
static void fill_adv_data(ble_advdata_t * p_scrsp_data, uint8_array_t * p_es_data_array, ble_gap_adv_data_t * const p_adv_data)
{
ble_advdata_t adv_data;
ret_code_t err_code;
ble_uuid_t adv_uuids[] = {{ES_UUID, BLE_UUID_TYPE_BLE}};
uint8_array_t es_data_array = {0};
ble_advdata_service_data_t service_data; // Structure to hold Service Data.
service_data.service_uuid = APP_ES_UUID; // Eddystone UUID to allow discoverability on iOS devices.
service_data.data = (p_es_data_array != NULL) ? *p_es_data_array : es_data_array;
// Build and set advertising data.
memset(&adv_data, 0, sizeof(ble_advdata_t));
adv_data.name_type = BLE_ADVDATA_NO_NAME;
adv_data.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
adv_data.uuids_complete.uuid_cnt = sizeof(adv_uuids) / sizeof(adv_uuids[0]);
adv_data.uuids_complete.p_uuids = adv_uuids;
adv_data.p_service_data_array = &service_data;
adv_data.service_data_count = (p_es_data_array != NULL) ? 1 : 0;
err_code = ble_advdata_encode(&adv_data,
p_adv_data->adv_data.p_data,
&p_adv_data->adv_data.len);
APP_ERROR_CHECK(err_code);
if (p_scrsp_data != NULL)
{
err_code = ble_advdata_encode(p_scrsp_data,
p_adv_data->scan_rsp_data.p_data,
&p_adv_data->scan_rsp_data.len);
APP_ERROR_CHECK(err_code);
}
else
{
p_adv_data->scan_rsp_data.p_data = NULL;
p_adv_data->scan_rsp_data.len = 0;
}
}
void es_adv_frame_fill_connectable_adv_data(ble_advdata_t * p_scrsp_data, ble_gap_adv_data_t * const p_adv_data)
{
fill_adv_data(p_scrsp_data, NULL, p_adv_data);
}
void es_adv_frame_fill_non_connectable_adv_data(uint8_t slot_no, bool etlm, ble_gap_adv_data_t * const p_adv_data)
{
uint8_array_t es_data_array = {0};
const es_slot_reg_t * p_reg = es_slot_get_registry();
if (etlm)
{
es_slot_etlm_update(slot_no);
// If eTLM, the incoming slot_no points to the corresponding EID slot, update to point to TLM slot.
slot_no = p_reg->tlm_slot;
}
// If TLM, update the TLM data.
else if (p_reg->slots[slot_no].adv_frame.type == ES_FRAME_TYPE_TLM)
{
es_slot_tlm_update();
}
es_data_array.p_data = (uint8_t *)&p_reg->slots[slot_no].adv_frame.frame;
es_data_array.size = p_reg->slots[slot_no].adv_frame.length;
fill_adv_data(NULL, &es_data_array, p_adv_data);
}

View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef ES_ADV_FRAME_H__
#define ES_ADV_FRAME_H__
#include <stdint.h>
#include "ble_advdata.h"
/**
* @file
* @addtogroup eddystone_adv
* @{
*/
/**@brief Function for setting up connectable advertisement data using @ref
* ble_advdata_encode.
*
* @param[in] p_scrsp_data Pointer to the scan response data that will be encoded.
* @param[in,out] p_adv_data Pointer to the encoded advertising data (including scan response).
*/
void es_adv_frame_fill_connectable_adv_data(ble_advdata_t * p_scrsp_data, ble_gap_adv_data_t * const p_adv_data);
/**@brief Function for setting up non-connectable advertisement data using @ref
* ble_advdata_encode.
*
* @param[in] slot_no Slot to fill in data for.
* @param[in] etlm Flag that specifies if Eddystone-TLM is required.
* @param[in,out] p_adv_data Pointer to the encoded advertising data (including scan response).
*/
void es_adv_frame_fill_non_connectable_adv_data(uint8_t slot_no, bool etlm, ble_gap_adv_data_t * const p_adv_data);
/**
* @}
*/
#endif // ES_ADV_FRAME_H__

View File

@@ -0,0 +1,220 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "app_timer.h"
#include "es_adv_timing.h"
#include "es_adv_timing_resolver.h"
#include "es_slot.h"
APP_TIMER_DEF(m_es_adv_interval_timer); //!< Timer for advertising the set of slots.
APP_TIMER_DEF(m_es_slot_timer); //!< Timer for advertising individual slots.
static nrf_ble_escs_adv_interval_t m_current_adv_interval; //!< Current advertisement interval.
static es_adv_timing_callback_t m_timing_mgr_callback; //!< Registered callback.
static es_adv_timing_resolver_result_t m_adv_timing_result; //!< Current advertising timing result.
static bool m_non_conn_adv_active; //!< Is the beacon advertising non-conn advertisements?
/**@brief Function for invoking registered callback.
*
* @param[in] p_evt Event to issue to callback.
*/
static void invoke_callback(const es_adv_timing_evt_t * p_evt)
{
if (m_timing_mgr_callback != NULL && m_non_conn_adv_active)
{
m_timing_mgr_callback(p_evt);
}
}
#if APP_CONFIG_TLM_ADV_INTERLEAVE_RATIO > 1
static bool frame_to_adv_is_tlm(const es_adv_timing_evt_t * p_evt)
{
const es_slot_reg_t * p_reg = es_slot_get_registry();
return (p_reg->tlm_configured &&
(p_evt->slot_no == p_reg->tlm_slot || p_evt->evt_id == ES_ADV_TIMING_EVT_ADV_ETLM));
}
static bool tlm_should_be_advertised(uint32_t adv_event_cnt)
{
return (adv_event_cnt % APP_CONFIG_TLM_ADV_INTERLEAVE_RATIO) == 0;
}
#endif // APP_CONFIG_TLM_ADV_INTERLEAVE_RATIO > 1
/**@brief Timeout handler for the advertisement slot timer. */
static void adv_slot_timeout(void * p_context)
{
ret_code_t err_code;
uint32_t active_slot_index = (uint32_t)p_context;
es_adv_timing_evt_t evt;
evt.slot_no = m_adv_timing_result.timing_results[active_slot_index].slot_no;
evt.evt_id = m_adv_timing_result.timing_results[active_slot_index].is_etlm
? ES_ADV_TIMING_EVT_ADV_ETLM
: ES_ADV_TIMING_EVT_ADV_SLOT;
// Trigger an event for the next slot if this slot is not the last to be advertised in this event.
// Note: since we check 'm_adv_timing_result.len_timing_results > 1' we can safely cast the result of
// the subtraction to a uint32.
if (m_non_conn_adv_active && \
m_adv_timing_result.len_timing_results > 1 && \
active_slot_index < (uint32_t)(m_adv_timing_result.len_timing_results - 1))
{
err_code = app_timer_start( m_es_slot_timer,
APP_TIMER_TICKS(m_adv_timing_result.timing_results[active_slot_index].delay_ms),
(void*)(active_slot_index + 1));
APP_ERROR_CHECK(err_code);
}
#if APP_CONFIG_TLM_ADV_INTERLEAVE_RATIO > 1
static uint32_t adv_event_cnt = 0;
if (active_slot_index == 0)
{
adv_event_cnt++;
}
if (frame_to_adv_is_tlm(&evt) && !tlm_should_be_advertised(adv_event_cnt))
{
return;
}
#endif // APP_CONFIG_TLM_ADV_INTERLEAVE_RATIO > 1
invoke_callback(&evt);
}
/**@brief Timeout handler for the advertisement interval timer. */
static void adv_interval_timeout(void * p_context)
{
if (es_slot_get_registry()->num_configured_slots > 0)
{
// Trigger slot timeout for advertising the first slot.
// Note: The slot number is not the index in the slot registry, it is the index of the active slots.
adv_slot_timeout(NULL);
}
if (m_non_conn_adv_active)
{
uint32_t err_code = app_timer_start(m_es_adv_interval_timer,
APP_TIMER_TICKS(m_current_adv_interval),
NULL);
APP_ERROR_CHECK(err_code);
}
}
void es_adv_timing_timers_init(void)
{
ret_code_t err_code;
err_code = app_timer_create(&m_es_adv_interval_timer,
APP_TIMER_MODE_SINGLE_SHOT,
adv_interval_timeout);
APP_ERROR_CHECK(err_code);
err_code = app_timer_create(&m_es_slot_timer,
APP_TIMER_MODE_SINGLE_SHOT,
adv_slot_timeout);
APP_ERROR_CHECK(err_code);
}
/**@brief Function for finding and setting advertisement timing configuration. */
static void adv_timing_set(void)
{
ret_code_t err_code;
const es_slot_reg_t * p_reg = es_slot_get_registry();
es_adv_timing_resolver_input_t resolver_input = {
.adv_interval = m_current_adv_interval,
.p_result = &m_adv_timing_result,
.num_slots_configured = p_reg->num_configured_slots,
.p_slots_configured = p_reg->slots_configured,
.num_eid_slots_configured = p_reg->num_configured_eid_slots,
.p_eid_slots_configured = p_reg->eid_slots_configured,
.tlm_configured = p_reg->tlm_configured,
.tlm_slot = p_reg->tlm_slot};
err_code = es_adv_timing_resolve(&resolver_input);
APP_ERROR_CHECK(err_code);
}
void es_adv_timing_start(uint16_t adv_interval)
{
ret_code_t err_code;
const es_slot_reg_t * p_reg = es_slot_get_registry();
m_non_conn_adv_active = true;
if (p_reg->num_configured_slots > 0)
{
m_current_adv_interval = adv_interval;
err_code = app_timer_start(m_es_adv_interval_timer,
APP_TIMER_TICKS(m_current_adv_interval),
NULL);
APP_ERROR_CHECK(err_code);
adv_timing_set();
}
}
void es_adv_timing_stop(void)
{
m_non_conn_adv_active = false; // Stops timers from being re-fired.
}
void es_adv_timing_init(es_adv_timing_callback_t p_handler)
{
m_non_conn_adv_active = false;
m_timing_mgr_callback = p_handler;
memset(&m_adv_timing_result, 0, sizeof(m_adv_timing_result));
}

View File

@@ -0,0 +1,97 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef ES_ADV_TIMING_H__
#define ES_ADV_TIMING_H__
#include <stdint.h>
/**
* @file
* @defgroup eddystone_adv_timing Timing
* @brief Events and functions for advertisement timing.
* @ingroup eddystone_adv
* @{
*/
/**@brief Eddystone advertisement timing event types. */
typedef enum
{
ES_ADV_TIMING_EVT_ADV_SLOT, //!< Advertising non-eTLM slot.
ES_ADV_TIMING_EVT_ADV_ETLM //!< Advertising eTLM slot.
} es_adv_timing_evt_id_t;
/**@brief Eddystone advertisement timing event. */
typedef struct
{
es_adv_timing_evt_id_t evt_id; //!< Event type ID.
uint8_t slot_no; /**< @brief Slot number.
* @details For non-eTLM events: The slot number to advertise.
*
* For eTLM events: The slot number of the corresponding EID slot. */
} es_adv_timing_evt_t;
/**@brief Eddystone advertisement timing event callback.
*
* @param[in] p_evt Pointer to the Eddystone advertisement timing event.
*/
typedef void (*es_adv_timing_callback_t)(const es_adv_timing_evt_t * p_evt);
/**@brief Function for starting Eddystone advertisement timing event generation. */
void es_adv_timing_start(uint16_t adv_interval);
/**@brief Function for stopping Eddystone advertisement timing event generation. */
void es_adv_timing_stop(void);
/**@brief Function for initializing the Eddystone advertisement timers.
*/
void es_adv_timing_timers_init(void);
/**@brief Function for initializing the Eddystone advertisement timing module.
*
* @param[in] handler Eddystone advertisement timing event handler to register.
*/
void es_adv_timing_init(es_adv_timing_callback_t handler);
/**
* @}
*/
#endif // ES_ADV_TIMING_H__

View File

@@ -0,0 +1,145 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "es_adv_timing_resolver.h"
#include "sdk_macros.h"
/**@brief Function for finding delay to use after each non-eTLM advertisement.
*
* @param[in] adv_interval Configured advertisement interval.
* @param[in] num_slots_configured Number of configured slots.
* @param[in] eTLM_required Is there an eTLM slot.
*/
static uint16_t get_adv_delay(uint16_t adv_interval,
uint8_t num_slots_configured,
bool eTLM_required)
{
// If eTLM is required, don't count this when calculating delay.
return adv_interval / (num_slots_configured - (eTLM_required ? 1 : 0));
}
/**@brief Function for checking if given slot_no is an EID slot.
*
* @param[in] slot_no Slot number to check.
* @param[in] p_eid_slots_configured Pointer to list of configured EID slots.
* @param[in] num_eid_slots_configured Number of EID slots configured.
*/
static bool is_eid(uint8_t slot_no, const uint8_t * p_eid_slots_configured, uint8_t num_eid_slots_configured)
{
for (uint32_t i = 0; i < num_eid_slots_configured; ++i)
{
if (slot_no == p_eid_slots_configured[i])
{
return true;
}
}
return false;
}
ret_code_t es_adv_timing_resolve(es_adv_timing_resolver_input_t * p_input)
{
VERIFY_PARAM_NOT_NULL(p_input);
uint8_t result_index = 0;
bool eTLM_required = p_input->tlm_configured && p_input->num_eid_slots_configured > 0;
uint16_t base_delay;
if (p_input->num_slots_configured == 0)
{
return NRF_ERROR_INVALID_PARAM;
}
base_delay = get_adv_delay(p_input->adv_interval, p_input->num_slots_configured, eTLM_required);
for (uint32_t i = 0; i < p_input->num_slots_configured; ++i)
{
uint8_t slot_no = p_input->p_slots_configured[i];
if (!(eTLM_required && slot_no == p_input->tlm_slot))
{
es_adv_timing_resolver_adv_timing_t * p_current_result = &p_input->p_result->timing_results[result_index];
p_current_result->slot_no = slot_no;
p_current_result->is_etlm = false;
// If an eTLM is to be advertised for this frame, this value will be changed.
p_current_result->delay_ms = base_delay;
result_index++;
if (eTLM_required &&
is_eid(slot_no, p_input->p_eid_slots_configured, p_input->num_eid_slots_configured))
{
es_adv_timing_resolver_adv_timing_t * p_eTLM_timing_result =
&p_input->p_result->timing_results[result_index];
p_current_result->delay_ms = APP_CONFIG_ADV_FRAME_ETLM_SPACING_MS; // Update delay from EID to eTLM frame.
p_eTLM_timing_result->slot_no = slot_no; // Point to EID slot-no, as this will be
// used for finding the correct EIK.
p_eTLM_timing_result->is_etlm = true; // Configure as eTLM frame.
if (base_delay > APP_CONFIG_ADV_FRAME_ETLM_SPACING_MS)
{
p_eTLM_timing_result->delay_ms =
base_delay -
APP_CONFIG_ADV_FRAME_ETLM_SPACING_MS; // Set delay of eTLM frame.
}
else
{
p_eTLM_timing_result->delay_ms = APP_CONFIG_ADV_FRAME_SPACING_MS_MIN;
}
result_index++;
}
}
}
p_input->p_result->len_timing_results = result_index; // Note: index has been increased to equal length of result.
if (p_input->p_result->len_timing_results > 0)
{
p_input->p_result->timing_results[p_input->p_result->len_timing_results - 1].delay_ms = 0; // Last Slot does not need delay.
}
return NRF_SUCCESS;
}

View File

@@ -0,0 +1,96 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef ES_ADV_TIMING_RESOLVER_H__
#define ES_ADV_TIMING_RESOLVER_H__
#include <stdbool.h>
#include <stdint.h>
#include "es_app_config.h"
/**
* @file
* @addtogroup eddystone_adv_timing
* @{
*/
/** @brief Timing parameters for a single slot. */
typedef struct
{
bool is_etlm; //!< Flag that specifies if the slot is an eTLM.
uint8_t slot_no; /**< @brief Slot number. @details
* For non-eTLM slots: The slot number of the given frame.
*
* For eTLM slots: The slot number of the corresponding EID frame. */
uint16_t delay_ms; //!< Delay from this frame to the next.
} es_adv_timing_resolver_adv_timing_t;
/**@brief Results of calculating advertisement delays. */
typedef struct
{
es_adv_timing_resolver_adv_timing_t timing_results[APP_MAX_ADV_SLOTS - APP_MAX_EID_SLOTS +
(APP_MAX_EID_SLOTS * 2)]; //!< List of timing results.
uint8_t len_timing_results; //!< Length of results.
} es_adv_timing_resolver_result_t;
/**@brief Input to the timing resolver. */
typedef struct
{
uint16_t adv_interval; //!< Global advertisement interval.
uint8_t num_slots_configured; //!< Number of configured slots.
const uint8_t * p_slots_configured; //!< Pointer to the list of configured slots.
uint8_t num_eid_slots_configured; //!< Number of configured EID slots.
const uint8_t * p_eid_slots_configured; //!< Pointer to the list of configured EID slots.
bool tlm_configured; //!< Flag that specifies if TLM slot is configured.
uint8_t tlm_slot; //!< Slot number of the TLM slot (if @p tlm_configured is true).
es_adv_timing_resolver_result_t * p_result; //!< Output result.
} es_adv_timing_resolver_input_t;
/**@brief Function for getting the input for advertisement interval calculation.
*
* @param[in,out] p_input Input to advertisement interval calculation (see @ref es_adv_timing_resolver_input_t).
* @retval NRF_SUCCESS If the operation was successful. Otherwise, an error code is returned.
*/
ret_code_t es_adv_timing_resolve(es_adv_timing_resolver_input_t * p_input);
/**
* @}
*/
#endif // ES_ADV_TIMING_RESOLVER_H__

View File

@@ -0,0 +1,66 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef ES_BATTERY_VOLTAGE_H__
#define ES_BATTERY_VOLTAGE_H__
#include <stdint.h>
/**
* @file
*
* @addtogroup eddystone_tlm
* @{
*/
/**@brief Function for initializing the battery voltage module.
*/
void es_battery_voltage_init(void);
/**@brief Function for reading the battery voltage.
*
* @param[out] p_vbatt Pointer to the battery voltage value.
*/
void es_battery_voltage_get(uint16_t * p_vbatt);
/**
* @}
*/
#endif // ES_BATTERY_VOLTAGE_H__

View File

@@ -0,0 +1,104 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "es_battery_voltage.h"
#include "nrf_drv_saadc.h"
#include "sdk_macros.h"
#define ADC_REF_VOLTAGE_IN_MILLIVOLTS 600 //!< Reference voltage (in milli volts) used by ADC while doing conversion.
#define DIODE_FWD_VOLT_DROP_MILLIVOLTS 270 //!< Typical forward voltage drop of the diode (Part no: SD103ATW-7-F) that is connected in series with the voltage supply. This is the voltage drop when the forward current is 1mA. Source: Data sheet of 'SURFACE MOUNT SCHOTTKY BARRIER DIODE ARRAY' available at www.diodes.com.
#define ADC_RES_10BIT 1024 //!< Maximum digital value for 10-bit ADC conversion.
#define ADC_PRE_SCALING_COMPENSATION 6 //!< The ADC is configured to use VDD with 1/3 prescaling as input. And hence the result of conversion is to be multiplied by 3 to get the actual value of the battery voltage.
#define ADC_RESULT_IN_MILLI_VOLTS(ADC_VALUE) \
((((ADC_VALUE) *ADC_REF_VOLTAGE_IN_MILLIVOLTS) / ADC_RES_10BIT) * ADC_PRE_SCALING_COMPENSATION)
static nrf_saadc_value_t adc_buf; //!< Buffer used for storing ADC value.
static uint16_t m_batt_lvl_in_milli_volts; //!< Current battery level.
/**@brief Function handling events from 'nrf_drv_saadc.c'.
*
* @param[in] p_evt SAADC event.
*/
static void saadc_event_handler(nrf_drv_saadc_evt_t const * p_evt)
{
if (p_evt->type == NRF_DRV_SAADC_EVT_DONE)
{
nrf_saadc_value_t adc_result;
adc_result = p_evt->data.done.p_buffer[0];
m_batt_lvl_in_milli_volts =
ADC_RESULT_IN_MILLI_VOLTS(adc_result) + DIODE_FWD_VOLT_DROP_MILLIVOLTS;
}
}
void es_battery_voltage_init(void)
{
ret_code_t err_code = nrf_drv_saadc_init(NULL, saadc_event_handler);
APP_ERROR_CHECK(err_code);
nrf_saadc_channel_config_t config =
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_VDD);
err_code = nrf_drv_saadc_channel_init(0, &config);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_saadc_buffer_convert(&adc_buf, 1);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_saadc_sample();
APP_ERROR_CHECK(err_code);
}
void es_battery_voltage_get(uint16_t * p_vbatt)
{
VERIFY_PARAM_NOT_NULL_VOID(p_vbatt);
*p_vbatt = m_batt_lvl_in_milli_volts;
if (!nrf_drv_saadc_is_busy())
{
ret_code_t err_code = nrf_drv_saadc_buffer_convert(&adc_buf, 1);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_saadc_sample();
APP_ERROR_CHECK(err_code);
}
}

View File

@@ -0,0 +1,355 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <string.h>
#include "es_flash.h"
#include "es_util.h"
#include "app_scheduler.h"
#include "ble_hci.h"
#include "fds.h"
#include "nrf_nvic.h"
#define SIZE_OF_PRIV_KEY ESCS_ECDH_KEY_SIZE //!< Size of ECDH private key.
#define SIZE_OF_PUB_KEY ESCS_ECDH_KEY_SIZE //!< Size of ECDH public key.
#define SIZE_OF_LOCK_KEY ESCS_AES_KEY_SIZE //!< Size of lock key.
#define FILE_ID_ES_FLASH 0x1337 //!< File ID used for all flash access EXCEPT lock code.
#define FILE_ID_ES_FLASH_LOCK_KEY 0x1338 //!< File ID used for lock code flash access.
#define RECORD_KEY_FLAGS 0x1 //!< File record for flash flags.
#define RECORD_KEY_PRIV_KEY 0x2 //!< File record for private key.
#define RECORD_KEY_PUB_KEY 0x3 //!< File record for public key.
#define RECORD_KEY_LOCK_KEY 0x4 //!< File record for lock key.
#define RECORD_KEY_BEACON_CONFIG 0x5 //!< File record for lock key.
static uint16_t RECORD_KEY_SLOTS[5] = {0x6, 0x7, 0x8, 0x9, 0xa}; //!< File record for slots.
/**@brief Structure used for invoking flash access function. */
typedef struct
{
uint16_t record_key;
uint16_t file_id;
uint8_t * p_data_buf;
uint8_t * p_data;
uint16_t size_bytes;
es_flash_access_t access_type;
} flash_access_params_t;
static volatile uint32_t m_num_pending_ops; //!< Current number of outstanding FDS operations.
static volatile bool m_factory_reset_done; //!< Has a factory reset operation been completed.
static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; //!< Current connection handle.
#if APP_MAX_ADV_SLOTS > 32
#error "APP_MAX_ADV_SLOTS must be <= 32"
#endif
#define SLOT_DECL(i, _) __ALIGN(4) static uint8_t slot## i ##_buf[sizeof(es_slot_t)];
EVAL(REPEAT(APP_MAX_ADV_SLOTS, SLOT_DECL, ~))
__ALIGN(4) static uint8_t lock_key_buf[SIZE_OF_LOCK_KEY]; //!< Buffer for lock key flash access.
#define SLOT(i, _) slot## i ##_buf,
static uint8_t * slots_buf_p[APP_MAX_ADV_SLOTS] = {
EVAL(REPEAT(APP_MAX_ADV_SLOTS, SLOT, ~))
};
__ALIGN(4) static uint8_t flash_flags_buf[sizeof(es_flash_flags_t)]; //!< Buffer for flash flags flash access.
__ALIGN(4) static uint8_t beacon_config_buf[sizeof(es_flash_beacon_config_t)]; //!< Buffer for beacon config flash access.
/**@brief Function handling scheduled FDS garbage collection. */
static void fds_gc_event(void * p_event_data, uint16_t event_size)
{
ret_code_t fds_err_code;
fds_err_code = fds_gc();
if (fds_err_code != NRF_SUCCESS)
APP_ERROR_CHECK_BOOL(NRF_ERROR_INTERNAL);
m_num_pending_ops++;
}
/**@brief Function handling FDS events.
*
* @param[in] p_evt FDS event.
*/
static void fds_cb(fds_evt_t const * const p_evt)
{
ret_code_t err_code;
switch (p_evt->id)
{
case FDS_EVT_INIT:
m_num_pending_ops = 0;
break;
case FDS_EVT_DEL_FILE:
if (p_evt->del.file_id == FILE_ID_ES_FLASH)
{
m_factory_reset_done = true;
}
// Fall through
case FDS_EVT_DEL_RECORD:
// Schedule garbage collection
err_code = app_sched_event_put(NULL, 0, fds_gc_event);
APP_ERROR_CHECK(err_code);
break;
case FDS_EVT_GC:
// During factory reset, a file is deleted, and garbage collection is scheduled
// when the callback for that deletion is invoked.
// So here we know that the factory reset is completed.
if (m_factory_reset_done)
{
if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
{
err_code =
sd_ble_gap_disconnect(m_conn_handle,
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
APP_ERROR_CHECK(err_code);
}
else
{
m_factory_reset_done = false;
(void)sd_nvic_SystemReset();
}
}
// Fall through:
case FDS_EVT_UPDATE:
// Fall through:
case FDS_EVT_WRITE:
if (m_num_pending_ops > 0)
{
m_num_pending_ops--;
}
break;
}
}
/**@brief Function performing flash access (read/write/clear).
*
* @param[in] p_params Flash access parameters.
*/
static ret_code_t access_flash_data(const flash_access_params_t * p_params)
{
ret_code_t err_code;
fds_flash_record_t record = {0};
fds_record_desc_t desc = {0};
fds_find_token_t ft = {0};
fds_record_t record_to_write =
{
.data.p_data = p_params->p_data_buf,
.file_id = p_params->file_id
};
err_code = fds_record_find_by_key(p_params->record_key, &desc, &ft);
// If its a read or clear, we can not accept errors on lookup
if (p_params->access_type == ES_FLASH_ACCESS_READ)
{
RETURN_IF_ERROR(err_code);
}
if (p_params->access_type == ES_FLASH_ACCESS_CLEAR && err_code == FDS_ERR_NOT_FOUND)
{
return NRF_SUCCESS;
}
switch (p_params->access_type)
{
case ES_FLASH_ACCESS_READ:
err_code = fds_record_open(&desc, &record);
RETURN_IF_ERROR(err_code);
memcpy(p_params->p_data, record.p_data, p_params->size_bytes);
err_code = fds_record_close(&desc);
RETURN_IF_ERROR(err_code);
break;
case ES_FLASH_ACCESS_WRITE:
memcpy(p_params->p_data_buf, p_params->p_data, p_params->size_bytes);
record_to_write.data.length_words = (p_params->size_bytes +3) / 4;
record_to_write.key = p_params->record_key;
if (err_code == FDS_ERR_NOT_FOUND)
{
err_code = fds_record_write(&desc, &record_to_write);
}
else
{
err_code = fds_record_update(&desc, &record_to_write);
}
RETURN_IF_ERROR(err_code);
m_num_pending_ops++;
break;
case ES_FLASH_ACCESS_CLEAR:
err_code = fds_record_delete(&desc);
RETURN_IF_ERROR(err_code);
m_num_pending_ops++;
break;
default:
break;
}
return NRF_SUCCESS;
}
ret_code_t es_flash_access_lock_key(uint8_t * p_lock_key, es_flash_access_t access_type)
{
flash_access_params_t params = {.record_key = RECORD_KEY_LOCK_KEY,
.file_id = FILE_ID_ES_FLASH_LOCK_KEY,
.p_data_buf = lock_key_buf,
.p_data = (uint8_t *)p_lock_key,
.size_bytes = SIZE_OF_LOCK_KEY,
.access_type = access_type};
return access_flash_data(&params);
}
ret_code_t es_flash_access_beacon_config(es_flash_beacon_config_t * p_config,
es_flash_access_t access_type)
{
ret_code_t err_code;
flash_access_params_t params = {.record_key = RECORD_KEY_BEACON_CONFIG,
.file_id = FILE_ID_ES_FLASH,
.p_data_buf = beacon_config_buf,
.p_data = (uint8_t *)p_config,
.size_bytes = sizeof(es_flash_beacon_config_t),
.access_type = access_type};
err_code = access_flash_data(&params);
return err_code;
}
ret_code_t es_flash_access_slot_configs(uint8_t slot_no,
es_slot_t * p_slot,
es_flash_access_t access_type)
{
if (slot_no >= APP_MAX_ADV_SLOTS)
{
return NRF_ERROR_INVALID_PARAM;
}
flash_access_params_t params = {.record_key = RECORD_KEY_SLOTS[slot_no],
.file_id = FILE_ID_ES_FLASH,
.p_data_buf = slots_buf_p[slot_no],
.p_data = (uint8_t *)p_slot,
.size_bytes = sizeof(es_slot_t),
.access_type = access_type};
return access_flash_data(&params);
}
ret_code_t es_flash_access_flags(es_flash_flags_t * p_flags, es_flash_access_t access_type)
{
flash_access_params_t params = {.record_key = RECORD_KEY_FLAGS,
.file_id = FILE_ID_ES_FLASH,
.p_data_buf = flash_flags_buf,
.p_data = (uint8_t *)p_flags,
.size_bytes = sizeof(es_flash_flags_t),
.access_type = access_type};
return access_flash_data(&params);
}
ret_code_t es_flash_factory_reset(void)
{
// Delete everything except the lock key:
ret_code_t ret_code = fds_file_delete(FILE_ID_ES_FLASH);
return ret_code;
}
uint32_t es_flash_num_pending_ops(void)
{
return m_num_pending_ops;
}
void es_flash_on_ble_evt(ble_evt_t const * p_evt)
{
switch (p_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
m_conn_handle = p_evt->evt.common_evt.conn_handle;
break;
case BLE_GAP_EVT_DISCONNECTED:
m_conn_handle = BLE_CONN_HANDLE_INVALID;
if (m_factory_reset_done)
{
m_factory_reset_done = false;
(void)sd_nvic_SystemReset();
}
break;
}
}
ret_code_t es_flash_init(void)
{
ret_code_t err_code;
m_num_pending_ops = 1; // Will be set to 0 when getting FDS_EVT_INIT event
m_conn_handle = BLE_CONN_HANDLE_INVALID;
m_factory_reset_done = false;
err_code = fds_register(fds_cb);
RETURN_IF_ERROR(err_code);
err_code = fds_init();
RETURN_IF_ERROR(err_code);
return NRF_SUCCESS;
}

View File

@@ -0,0 +1,177 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef ES_FLASH_H__
#define ES_FLASH_H__
#include <stdbool.h>
#include <stdint.h>
#include "es_slot.h"
/**
* @file
* @defgroup eddystone_flash Flash access
* @brief Types and functions to access the flash of the Eddystone beacon.
* @ingroup eddystone
* @{
*/
#define WORD_SIZE 4
#define FLASH_ACCES_ERROR_CHECK_ALLOW_NOT_FOUND(err_code) \
if (err_code != (FDS_ERR_NOT_FOUND)) \
APP_ERROR_CHECK(err_code);
#define FLASH_OP_WAIT() \
uint32_t pending_ops = es_flash_num_pending_ops(); \
while (pending_ops != 0) \
{ \
pending_ops = es_flash_num_pending_ops(); \
}
/**@brief Beacon configuration. */
typedef struct
{
nrf_ble_escs_adv_interval_t adv_interval; //!< Advertising interval.
bool remain_connectable; //!< Flag that specifies if the beacon should remain connectable.
} es_flash_beacon_config_t;
/**@brief Structure for keeping track of which slot has a configuration that must be restored upon reboot.
* @details The size of this structure must be word aligned and match the flash block size of 32 bytes.
*/
typedef struct
{
bool slot_is_empty[APP_MAX_ADV_SLOTS]; //!< Flag that indicates whether the slot is empty.
uint8_t padding[WORD_SIZE - ((APP_MAX_ADV_SLOTS + 1) % WORD_SIZE)]; //!< Padding used to ensure word alignment.
} es_flash_flags_t;
/**@brief Flash access types.
*/
typedef enum
{
ES_FLASH_ACCESS_READ, //!< Read data.
ES_FLASH_ACCESS_WRITE, //!< Write data.
ES_FLASH_ACCESS_CLEAR //!< Clear data.
} es_flash_access_t;
/**@brief Function for accessing beacon configurations.
*
* @param[out,in] p_config Pointer to the beacon configuration buffer.
* @param[in] access_type Access type (see @ref es_flash_access_t).
* @return For possible return values, see:
* - @ref fds_record_find_by_key
* - @ref fds_record_open
* - @ref fds_record_close
* - @ref fds_record_write
* - @ref fds_record_update
* - @ref fds_record_delete
*/
ret_code_t es_flash_access_beacon_config(es_flash_beacon_config_t * p_config,
es_flash_access_t access_type);
/**@brief Function for accessing slot configuration from flash.
*
* @param[in] slot_no Slot index.
* @param[out,in] p_slot Pointer to the slot configuration buffer.
* @param[in] access_type Access type (see @ref es_flash_access_t).
* @return For possible return values, see:
* - @ref fds_record_find_by_key
* - @ref fds_record_open
* - @ref fds_record_close
* - @ref fds_record_write
* - @ref fds_record_update
* - @ref fds_record_delete
*/
ret_code_t es_flash_access_slot_configs(uint8_t slot_no,
es_slot_t * p_slot,
es_flash_access_t access_type);
/**@brief Function for accessing the beacon lock key from flash.
*
* @param[out,in] p_lock_key Pointer to the lock key buffer.
* @param[in] access_type Access type (see @ref es_flash_access_t).
* @return For possible return values, see:
* - @ref fds_record_find_by_key
* - @ref fds_record_open
* - @ref fds_record_close
* - @ref fds_record_write
* - @ref fds_record_update
* - @ref fds_record_delete
*/
ret_code_t es_flash_access_lock_key(uint8_t * p_lock_key, es_flash_access_t access_type);
/**@brief Function for accessing the flash configuration flag from flash.
*
* @param[out,in] p_flags Pointer to the flag buffer.
* @param[in] access_type Access type (see @ref es_flash_access_t).
* @return For possible return values, see:
* - @ref fds_record_find_by_key
* - @ref fds_record_open
* - @ref fds_record_close
* - @ref fds_record_write
* - @ref fds_record_update
* - @ref fds_record_delete
*/
ret_code_t es_flash_access_flags(es_flash_flags_t * p_flags, es_flash_access_t access_type);
/**@brief Function for retrieving the number of queued operations.
* @return The number of operations that are queued.
*/
uint32_t es_flash_num_pending_ops(void);
/**@brief Function for performing a factory reset.
* @return FDS return code.
*/
ret_code_t es_flash_factory_reset(void);
void es_flash_on_ble_evt(ble_evt_t const * p_evt);
/**@brief Function for initializing the flash module.
*
* @return See @ref fds_init for possible return values.
*/
ret_code_t es_flash_init(void);
/**
* @}
*/
#endif // ES_FLASH_H__

View File

@@ -0,0 +1,186 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "es_gatts.h"
#include "es_gatts_read.h"
#include "es_gatts_write.h"
#include "es_slot.h"
static uint8_t m_active_slot;
/**@brief Function checking if beacon is unlocked.
*
* @param[in] p_escs Pointer to Eddystone Configuration Service.
*
* @retval true If beacon is unlocked.
* @retval false If beacon is locked.
*/
static bool is_beacon_unlocked(const nrf_ble_escs_t * p_escs)
{
return p_escs->lock_state != NRF_BLE_ESCS_LOCK_STATE_LOCKED;
}
ret_code_t es_gatts_send_reply(nrf_ble_escs_t * p_escs,
ble_gatts_rw_authorize_reply_params_t * p_reply)
{
VERIFY_PARAM_NOT_NULL(p_escs);
VERIFY_PARAM_NOT_NULL(p_reply);
if (p_escs->conn_handle != BLE_CONN_HANDLE_INVALID)
{
return sd_ble_gatts_rw_authorize_reply(p_escs->conn_handle, p_reply);
}
return NRF_ERROR_INVALID_STATE;
}
ret_code_t es_gatts_send_op_not_permitted(nrf_ble_escs_t * p_escs, bool read)
{
ble_gatts_rw_authorize_reply_params_t reply = {0};
VERIFY_PARAM_NOT_NULL(p_escs);
if (read)
{
reply.type = BLE_GATTS_AUTHORIZE_TYPE_READ;
reply.params.read.gatt_status = BLE_GATT_STATUS_ATTERR_READ_NOT_PERMITTED;
}
else
{
reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
reply.params.write.gatt_status = BLE_GATT_STATUS_ATTERR_WRITE_NOT_PERMITTED;
}
return es_gatts_send_reply(p_escs, &reply);
}
void es_gatts_handle_write(nrf_ble_escs_t * p_escs,
uint16_t uuid,
uint16_t val_handle,
uint8_t const * p_data,
uint16_t length)
{
ret_code_t err_code;
if (is_beacon_unlocked(p_escs))
{
if (uuid == BLE_UUID_ESCS_UNLOCK_CHAR)
{
err_code = es_gatts_send_op_not_permitted(p_escs, false);
APP_ERROR_CHECK(err_code);
}
else
{
err_code = es_gatts_write_handle_unlocked_write(
p_escs, uuid, val_handle, p_data, length, m_active_slot);
APP_ERROR_CHECK(err_code);
}
}
else
{
if (uuid == BLE_UUID_ESCS_UNLOCK_CHAR)
{
err_code = es_gatts_write_handle_unlock(p_escs, p_data, length, val_handle);
APP_ERROR_CHECK(err_code);
}
else
{
err_code = es_gatts_send_op_not_permitted(p_escs, false);
APP_ERROR_CHECK(err_code);
}
}
}
void es_gatts_handle_read(nrf_ble_escs_t * p_escs, uint16_t uuid, uint16_t val_handle)
{
ret_code_t err_code;
if (is_beacon_unlocked(p_escs))
{
if (uuid == BLE_UUID_ESCS_UNLOCK_CHAR)
{
err_code = es_gatts_send_op_not_permitted(p_escs, true);
APP_ERROR_CHECK(err_code);
}
else
{
err_code = es_gatts_read_handle_unlocked_read(p_escs, uuid, val_handle, m_active_slot);
APP_ERROR_CHECK(err_code);
}
}
else // Beacon is locked.
{
if (uuid == BLE_UUID_ESCS_UNLOCK_CHAR)
{
err_code = es_gatts_read_handle_unlock(p_escs);
APP_ERROR_CHECK(err_code);
}
else
{
err_code = es_gatts_read_handle_locked_read(p_escs, uuid);
APP_ERROR_CHECK(err_code);
}
}
}
ret_code_t es_gatts_init(nrf_ble_escs_t * p_ble_escs)
{
VERIFY_PARAM_NOT_NULL(p_ble_escs);
m_active_slot = 0;
p_ble_escs->p_active_slot = &m_active_slot;
p_ble_escs->lock_state = NRF_BLE_ESCS_LOCK_STATE_LOCKED;
return NRF_SUCCESS;
}

View File

@@ -0,0 +1,109 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef ES_GATTS_H__
#define ES_GATTS_H__
#include <stdint.h>
#include "nrf_ble_escs.h"
/**
* @file
* @defgroup eddystone_gatts GATTS
* @brief Functions for handling GATTS write and read requests.
* @ingroup eddystone
* @{
*/
ret_code_t es_gatts_init(nrf_ble_escs_t * p_ble_escs);
/**@brief Function for handling all write requests from the Central.
*
* @param[in] p_escs Pointer to the Eddystone Configuration Service.
* @param[in] uuid The UUID of the characteristic that is being written to.
* @param[in] val_handle Value handle field of the characteristic handle of the characteristic that is being written to.
* @param[in] p_data Pointer to the data to be written.
* @param[in] length Length of the data to be written.
*
*/
void es_gatts_handle_write(nrf_ble_escs_t * p_escs,
uint16_t uuid,
uint16_t val_handle,
uint8_t const * p_data,
uint16_t length);
/**@brief Function for handling all read requests from the Central.
*
* @param[in] p_escs Pointer to the Eddystone Configuration Service.
* @param[in] uuid The UUID of the characteristic that is being read from.
* @param[in] val_handle Value handle field of the characteristic handle of the characteristic that is being read from.
*
*/
void es_gatts_handle_read(nrf_ble_escs_t * p_escs, uint16_t uuid, uint16_t val_handle);
/**@brief Function for sending an RW-authorization reply.
*
* @param[in] p_escs Pointer to the Eddystone Configuration Service.
* @param[in] p_reply Pointer to the reply to send.
*
* @retval NRF_SUCCESS If the reply was successfully issued to the SoftDevice.
* @retval NRF_ERROR_NULL If either of the pointers @p p_escs or @p p_reply is NULL.
* @retval NRF_ERROR_INVALID_STATE If the connection handle of @p p_escs is invalid.
* @return Otherwise, an error code from sd_ble_gatts_rw_authorize_reply() is returned.
*/
ret_code_t es_gatts_send_reply(nrf_ble_escs_t * p_escs, ble_gatts_rw_authorize_reply_params_t * p_reply);
/**@brief Function for sending an RW-authorization reply with status 'Operation not permitted'.
*
* @param[in] p_escs Pointer to the Eddystone Configuration Service.
* @param[in] op_is_read Flag that specifies if the operation being responded to is a 'read' operation.
If false, a 'write' operation is assumed.
*
* @retval NRF_ERROR_NULL If @p p_escs is NULL.
* @return Otherwise, the error code from es_gatts_send_reply() is returned.
*/
ret_code_t es_gatts_send_op_not_permitted(nrf_ble_escs_t * p_escs, bool op_is_read);
/**
* @}
*/
#endif // ES_GATTS_H__

View File

@@ -0,0 +1,245 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "es_gatts_read.h"
#include "es_adv.h"
#include "es_gatts.h"
#include "es_security.h"
#include "es_slot.h"
static ret_code_t send_read_reply(nrf_ble_escs_t * p_escs, ble_gatts_rw_authorize_reply_params_t * p_reply)
{
VERIFY_PARAM_NOT_NULL(p_escs);
VERIFY_PARAM_NOT_NULL(p_reply);
p_reply->type = BLE_GATTS_AUTHORIZE_TYPE_READ;
p_reply->params.read.update = 1;
p_reply->params.read.offset = 0;
return es_gatts_send_reply(p_escs, p_reply);
}
static ret_code_t read_value(nrf_ble_escs_t * p_escs, uint8_t length, const void * p_value)
{
VERIFY_PARAM_NOT_NULL(p_escs);
VERIFY_PARAM_NOT_NULL(p_value);
ble_gatts_rw_authorize_reply_params_t reply = {0};
reply.params.read.len = length;
reply.params.read.p_data = p_value;
reply.params.read.gatt_status = BLE_GATT_STATUS_SUCCESS;
return send_read_reply(p_escs, &reply);
}
static ret_code_t read_from_gattdb(nrf_ble_escs_t * p_escs, uint16_t val_handle)
{
VERIFY_PARAM_NOT_NULL(p_escs);
ret_code_t err_code;
// Go straight to the characteristic
uint8_t value_buffer[ESCS_ADV_SLOT_CHAR_LENGTH_MAX] = {0};
ble_gatts_value_t value = {.len = sizeof(value_buffer),
.offset = 0,
.p_value = &(value_buffer[0])};
err_code = sd_ble_gatts_value_get(p_escs->conn_handle, val_handle, &value);
RETURN_IF_ERROR(err_code);
return read_value(p_escs, value.len, value.p_value);
}
static ret_code_t read_adv_slot(nrf_ble_escs_t * p_escs, uint8_t active_slot, const es_slot_reg_t * p_reg)
{
VERIFY_PARAM_NOT_NULL(p_escs);
ble_gatts_rw_authorize_reply_params_t reply = {0};
uint8_t eid_buf[14];
// If an EID slot is read, load scaler, clock value and ephemeral ID.
if (p_reg->slots[active_slot].adv_frame.type == ES_FRAME_TYPE_EID)
{
/*lint -save -e666 */
uint32_t clock_value = es_security_clock_get(active_slot);
clock_value = BYTES_REVERSE_32BIT(clock_value);
/*lint -restore */
reply.params.read.len = ES_EID_GATTS_READ_LENGTH;
// Fill EID buffer with data
eid_buf[ES_EID_GATTS_READ_FRAME_TYPE_IDX] = ES_FRAME_TYPE_EID;
eid_buf[ES_EID_GATTS_READ_EXPONENT_IDX] = es_security_scaler_get(active_slot);
memcpy(&eid_buf[ES_EID_GATTS_READ_CLCK_VALUE_IDX], &clock_value, sizeof(clock_value));
/*lint -save -e545 */
memcpy(&eid_buf[ES_EID_GATTS_READ_EID_IDX],
&p_reg->slots[active_slot].adv_frame.frame.eid.eid,
ES_EID_ID_LENGTH);
/*lint -restore */
reply.params.read.p_data = eid_buf;
}
// Otherwise, simply load the contents of the frame.
else
{
// Check if slot being read is an eTLM slot.
if ((p_reg->num_configured_eid_slots > 0) && p_reg->tlm_configured && (p_reg->tlm_slot == active_slot))
{
// Fill eTLM slot using EID key from first EID slot.
es_slot_etlm_update(p_reg->eid_slots_configured[0]);
}
reply.params.read.len = p_reg->slots[active_slot].adv_frame.length;
reply.params.read.p_data = (uint8_t *)&p_reg->slots[active_slot].adv_frame.frame;
}
reply.params.read.gatt_status = BLE_GATT_STATUS_SUCCESS;
return send_read_reply(p_escs, &reply);
}
ret_code_t es_gatts_read_handle_locked_read(nrf_ble_escs_t * p_escs, uint16_t uuid)
{
VERIFY_PARAM_NOT_NULL(p_escs);
if (uuid == BLE_UUID_ESCS_REMAIN_CONNECTABLE_CHAR)
{
uint8_t retval = APP_IS_REMAIN_CONNECTABLE_SUPPORTED;
return read_value(p_escs, sizeof(retval), &retval);
}
else if (uuid == BLE_UUID_ESCS_LOCK_STATE_CHAR)
{
return read_value(p_escs, ESCS_LOCK_STATE_READ_LENGTH, &p_escs->lock_state);
}
else
{
return es_gatts_send_op_not_permitted(p_escs, true);
}
}
ret_code_t es_gatts_read_handle_unlock(nrf_ble_escs_t * p_escs)
{
VERIFY_PARAM_NOT_NULL(p_escs);
ret_code_t err_code;
uint8_t key_buff[ESCS_AES_KEY_SIZE];
err_code = es_security_random_challenge_generate(key_buff);
RETURN_IF_ERROR(err_code);
es_security_unlock_prepare(key_buff);
return read_value(p_escs, ESCS_AES_KEY_SIZE, key_buff);
}
ret_code_t es_gatts_read_handle_unlocked_read(nrf_ble_escs_t * p_escs,
uint16_t uuid,
uint16_t val_handle,
uint8_t active_slot)
{
VERIFY_PARAM_NOT_NULL(p_escs);
const es_slot_reg_t * p_reg = es_slot_get_registry();
switch (uuid)
{
case BLE_UUID_ESCS_BROADCAST_CAP_CHAR:
case BLE_UUID_ESCS_UNLOCK_CHAR:
case BLE_UUID_ESCS_PUBLIC_ECDH_KEY_CHAR:
case BLE_UUID_ESCS_ACTIVE_SLOT_CHAR:
return read_from_gattdb(p_escs, val_handle);
case BLE_UUID_ESCS_LOCK_STATE_CHAR:
return read_value(p_escs, ESCS_LOCK_STATE_READ_LENGTH, &p_escs->lock_state);
case BLE_UUID_ESCS_ADV_INTERVAL_CHAR:
{
nrf_ble_escs_adv_interval_t adv_interval = es_adv_interval_get();
adv_interval = BYTES_SWAP_16BIT(adv_interval);
return read_value(p_escs, sizeof(adv_interval), &adv_interval);
}
case BLE_UUID_ESCS_RADIO_TX_PWR_CHAR:
return read_value(p_escs,
sizeof(nrf_ble_escs_radio_tx_pwr_t),
&p_reg->slots[active_slot].radio_tx_pwr);
case BLE_UUID_ESCS_ADV_TX_PWR_CHAR:
return read_value(p_escs,
sizeof(nrf_ble_escs_radio_tx_pwr_t),
p_reg->slots[active_slot].adv_custom_tx_power
? (uint8_t *)(&p_reg->slots[active_slot].custom_tx_power)
: (uint8_t *)(&p_reg->slots[active_slot].radio_tx_pwr));
case BLE_UUID_ESCS_REMAIN_CONNECTABLE_CHAR:
{
uint8_t retval = APP_IS_REMAIN_CONNECTABLE_SUPPORTED;
return read_value(p_escs, sizeof(retval), &retval);
}
case BLE_UUID_ESCS_EID_ID_KEY_CHAR:
if (p_reg->slots[active_slot].configured &&
(p_reg->slots[active_slot].adv_frame.type == ES_FRAME_TYPE_EID))
{
return read_value(p_escs,
sizeof(nrf_ble_escs_eid_id_key_t),
&p_reg->slots[active_slot].encrypted_eid_id_key);
}
else
{
return es_gatts_send_op_not_permitted(p_escs, true);
}
case BLE_UUID_ESCS_RW_ADV_SLOT_CHAR:
return read_adv_slot(p_escs, active_slot, p_reg);
default:
return NRF_ERROR_INVALID_PARAM;
}
}

View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef ES_GATTS_READ_H__
#define ES_GATTS_READ_H__
#include <stdint.h>
#include "nrf_ble_escs.h"
/**
* @file
* @defgroup eddystone_gatts_read GATTS read
* @brief Functions for handling GATTS read requests.
* @ingroup eddystone_gatts
* @{
*/
ret_code_t es_gatts_read_send_not_permitted(nrf_ble_escs_t * p_escs);
ret_code_t es_gatts_read_handle_unlocked_read(nrf_ble_escs_t * p_escs,
uint16_t uuid,
uint16_t val_handle,
uint8_t active_slot);
ret_code_t es_gatts_read_handle_unlock(nrf_ble_escs_t * p_escs);
ret_code_t es_gatts_read_handle_locked_read(nrf_ble_escs_t * p_escs,
uint16_t uuid);
/**
* @}
*/
#endif // ES_GATTS_READ_H__

View File

@@ -0,0 +1,254 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "es_gatts_write.h"
#include "es_adv.h"
#include "es_flash.h"
#include "es_gatts.h"
#include "es_security.h"
static ret_code_t send_write_reply(nrf_ble_escs_t * p_escs, ble_gatts_rw_authorize_reply_params_t * p_reply)
{
VERIFY_PARAM_NOT_NULL(p_escs);
VERIFY_PARAM_NOT_NULL(p_reply);
p_reply->type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
p_reply->params.write.update = 1;
p_reply->params.write.offset = 0;
return es_gatts_send_reply(p_escs, p_reply);
}
/**@brief Function checking if length of event is correct, given the frame data.
*
* @param[in] p_data Written ADV Slot data.
* @param[in] length Written length.
*
* @retval true If length is valid.
* @retval false If length is not valid.
*/
static bool length_is_valid(uint8_t const * p_data, uint8_t length)
{
if (length == 0 || (length == 1 && p_data[0] == 0))
{
return true;
}
else
{
switch ((es_frame_type_t)p_data[0])
{
case ES_FRAME_TYPE_UID:
return length == ESCS_UID_WRITE_LENGTH;
case ES_FRAME_TYPE_URL:
return ((length >= ESCS_URL_MIN_WRITE_LENGTH) && (length <= ESCS_URL_WRITE_LENGTH));
case ES_FRAME_TYPE_TLM:
return (length == ESCS_TLM_WRITE_LENGTH);
case ES_FRAME_TYPE_EID:
return ((length == ESCS_EID_WRITE_ECDH_LENGTH) ||
(length == ESCS_EID_WRITE_IDK_LENGTH));
default:
return false;
}
}
}
ret_code_t es_gatts_write_handle_unlocked_write(nrf_ble_escs_t * p_escs,
uint16_t uuid,
uint16_t val_handle,
uint8_t const * p_data,
uint16_t length,
uint8_t active_slot)
{
VERIFY_PARAM_NOT_NULL(p_escs);
VERIFY_PARAM_NOT_NULL(p_data);
ret_code_t err_code;
ble_gatts_rw_authorize_reply_params_t reply = {0};
bool long_write = false;
reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
switch (uuid)
{
case BLE_UUID_ESCS_ACTIVE_SLOT_CHAR:
if (*p_data > APP_MAX_ADV_SLOTS - 1)
{
// Invalid Attribute Length: for an attempt to write illegal values.
// The beacon will list the total number of available slots in the
// max_supported_total_slots field in the Capabilities characteristic.
reply.params.write.gatt_status = BLE_GATT_STATUS_ATTERR_INVALID_ATT_VAL_LENGTH;
length = 0;
}
break;
case BLE_UUID_ESCS_ADV_INTERVAL_CHAR:
es_adv_interval_set(BYTES_SWAP_16BIT(*(nrf_ble_escs_adv_interval_t *)p_data));
break;
case BLE_UUID_ESCS_RADIO_TX_PWR_CHAR:
es_slot_radio_tx_pwr_set(active_slot, *(nrf_ble_escs_radio_tx_pwr_t *)(p_data));
break;
case BLE_UUID_ESCS_ADV_TX_PWR_CHAR:
// Update slot info so that ADV data will only be read from what is written by client.
es_slot_set_adv_custom_tx_power(active_slot, *(nrf_ble_escs_radio_tx_pwr_t *)(p_data));
break;
case BLE_UUID_ESCS_LOCK_STATE_CHAR:
if (length == 1 && (*p_data == NRF_BLE_ESCS_LOCK_BYTE_LOCK ||
*p_data == NRF_BLE_ESCS_LOCK_BYTE_DISABLE_AUTO_RELOCK))
{
// Do nothing special, allow the write.
}
else if (length == ESCS_LOCK_STATE_NEW_LOCK_CODE_WRITE_LENGTH &&
*p_data == NRF_BLE_ESCS_LOCK_BYTE_LOCK)
{
// 0x00 + key[16] : transition to lock state and update the lock code.
err_code = es_security_lock_code_update((uint8_t*)(p_data) + 1);
RETURN_IF_ERROR(err_code);
// Only write the lock byte (0x00) to the characteristic, so set length to 1.
length = 1;
}
else
{
// Any invalid values locks the characteristic by default.
(*(uint8_t*)p_data) = NRF_BLE_ESCS_LOCK_BYTE_LOCK;
length = 1;
}
break;
case BLE_UUID_ESCS_RW_ADV_SLOT_CHAR:
if (length > 20)
{
long_write = true;
}
reply.params.write.gatt_status = length_is_valid(p_data, length)
? BLE_GATT_STATUS_SUCCESS
: BLE_GATT_STATUS_ATTERR_INVALID_ATT_VAL_LENGTH;
if (reply.params.write.gatt_status == BLE_GATT_STATUS_SUCCESS)
{
es_slot_on_write(active_slot, length, p_data);
es_adv_interval_set(es_adv_interval_get()); // Ensure that valid advertisement interval is used.
}
break;
case BLE_UUID_ESCS_FACTORY_RESET_CHAR:
if (*p_data == 0x0B)
{
err_code = es_flash_factory_reset();
RETURN_IF_ERROR(err_code);
}
break;
case BLE_UUID_ESCS_REMAIN_CONNECTABLE_CHAR:
#if APP_IS_REMAIN_CONNECTABLE_SUPPORTED == ESCS_FUNCT_REMAIN_CONNECTABLE_SUPPORTED_Yes
if (*p_data != 0)
{
es_adv_remain_connectable_set(true);
}
else
{
es_adv_remain_connectable_set(false);
}
#endif
break;
default:
break;
}
reply.params.write.len = length;
reply.params.write.p_data = p_data;
if (!long_write)
{
return send_write_reply(p_escs, &reply);
}
else
{
return NRF_SUCCESS;
}
}
ret_code_t es_gatts_write_handle_unlock(nrf_ble_escs_t * p_escs,
uint8_t const * p_data,
uint16_t length,
uint16_t val_handle)
{
VERIFY_PARAM_NOT_NULL(p_escs);
VERIFY_PARAM_NOT_NULL(p_data);
ret_code_t err_code;
ble_gatts_rw_authorize_reply_params_t reply = {0};
ble_gatts_value_t value = {.len = length, .offset = 0, .p_value = (uint8_t*)p_data};
if (length == ESCS_AES_KEY_SIZE)
{
err_code = sd_ble_gatts_value_set(p_escs->conn_handle, val_handle, &value);
if (err_code == NRF_SUCCESS)
{
reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
es_security_unlock_verify((value.p_value));
}
else
{
reply.params.write.gatt_status = BLE_GATT_STATUS_ATTERR_WRITE_NOT_PERMITTED;
}
}
reply.params.write.len = length;
reply.params.write.p_data = (const uint8_t *)value.p_value;
return send_write_reply(p_escs, &reply);
}

View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef ES_GATTS_WRITE_H__
#define ES_GATTS_WRITE_H__
#include <stdint.h>
#include "nrf_ble_escs.h"
/**
* @file
* @defgroup eddystone_gatts_write GATTS write
* @brief Functions for handling GATTS write requests.
* @ingroup eddystone_gatts
* @{
*/
ret_code_t es_gatts_write_handle_unlocked_write(nrf_ble_escs_t * p_escs,
uint16_t uuid,
uint16_t val_handle,
uint8_t const * p_data,
uint16_t length,
uint8_t active_slot);
ret_code_t es_gatts_write_handle_unlock(nrf_ble_escs_t * p_escs,
uint8_t const * p_data,
uint16_t length,
uint16_t val_handle);
/**
* @}
*/
#endif // ES_GATTS_WRITE_H__

View File

@@ -0,0 +1,606 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <stdbool.h>
#include <stdint.h>
#include "es_security.h"
#include "app_timer.h"
#include "es_flash.h"
#include "es_stopwatch.h"
#include "fds.h"
#include "modes.h"
#include "nrf_crypto.h"
#include "nrf_soc.h"
#define NONCE_SIZE (6)
#define TAG_SIZE (2)
#define SALT_SIZE (2)
#define TLM_DATA_SIZE (ES_TLM_LENGTH - 2)
#define EIK_SIZE (ESCS_AES_KEY_SIZE)
#define AES_ECB_CIPHERTEXT_LENGTH (16)
#define AES_ECB_CLEARTEXT_LENGTH (16)
/**@brief Timing structure. */
typedef struct
{
uint32_t time_counter;
uint8_t k_scaler;
} es_security_timing_t;
/**@brief Security slot structure. */
typedef struct
{
nrf_ecb_hal_data_t aes_ecb_ik;
nrf_ecb_hal_data_t aes_ecb_tk;
uint8_t eid[ES_EID_ID_LENGTH];
es_security_timing_t timing;
bool is_occupied;
} es_security_slot_t;
/**@brief Key pair structure. */
typedef struct
{
nrf_crypto_ecc_private_key_t private;
nrf_crypto_ecc_public_key_t public;
} ecdh_key_pair_t;
/**@brief ECDH structure. */
typedef struct
{
ecdh_key_pair_t ecdh_key_pair;
} es_security_ecdh_t;
static nrf_ecb_hal_data_t m_aes_ecb_lk;
static es_security_slot_t m_security_slot[APP_MAX_EID_SLOTS];
static es_security_ecdh_t m_ecdh;
static es_security_msg_cb_t m_security_callback;
static es_stopwatch_id_t m_seconds_passed_sw_id;
// Use static context variables to avoid stack allocation.
static nrf_crypto_aes_context_t m_aes_context;
static nrf_crypto_hmac_context_t m_hmac_context;
static nrf_crypto_aead_context_t m_aead_context;
static nrf_crypto_ecc_key_pair_generate_context_t ecc_key_pair_generate_context;
static nrf_crypto_ecdh_context_t ecdh_context;
/**@brief Generates a temporary key with the Identity key. */
static void temp_key_generate(uint8_t slot_no);
/**@brief Generates a EID with the Temporary Key*/
static void eid_generate(uint8_t slot_no)
{
ret_code_t err_code;
size_t ciphertext_size = AES_ECB_CIPHERTEXT_LENGTH;
temp_key_generate(slot_no);
memset(m_security_slot[slot_no].aes_ecb_tk.cleartext, 0, ESCS_AES_KEY_SIZE);
m_security_slot[slot_no].aes_ecb_tk.cleartext[11] = m_security_slot[slot_no].timing.k_scaler;
uint32_t k_bits_cleared_time =
(m_security_slot[slot_no].timing.time_counter >> m_security_slot[slot_no].timing.k_scaler)
<< m_security_slot[slot_no].timing.k_scaler;
m_security_slot[slot_no].aes_ecb_tk.cleartext[12] =
(uint8_t)((k_bits_cleared_time >> 24) & 0xff);
m_security_slot[slot_no].aes_ecb_tk.cleartext[13] =
(uint8_t)((k_bits_cleared_time >> 16) & 0xff);
m_security_slot[slot_no].aes_ecb_tk.cleartext[14] = (uint8_t)((k_bits_cleared_time >> 8) & 0xff);
m_security_slot[slot_no].aes_ecb_tk.cleartext[15] = (uint8_t)((k_bits_cleared_time) & 0xff);
err_code = nrf_crypto_aes_crypt(&m_aes_context,
&g_nrf_crypto_aes_ecb_128_info,
NRF_CRYPTO_ENCRYPT, // Operation
m_security_slot[slot_no].aes_ecb_tk.key, // Key
NULL, // IV
m_security_slot[slot_no].aes_ecb_tk.cleartext, // Data in
AES_ECB_CLEARTEXT_LENGTH, // Data in size
m_security_slot[slot_no].aes_ecb_tk.ciphertext, // Data out
&ciphertext_size); // Data out size
APP_ERROR_CHECK(err_code);
memcpy(m_security_slot[slot_no].eid,
m_security_slot[slot_no].aes_ecb_tk.ciphertext,
ES_EID_ID_LENGTH);
m_security_callback(slot_no, ES_SECURITY_MSG_EID);
}
/**@brief Generates a temporary key with the Identity key. */
static void temp_key_generate(uint8_t slot_no)
{
ret_code_t err_code;
size_t ciphertext_size = AES_ECB_CIPHERTEXT_LENGTH;
memset(m_security_slot[slot_no].aes_ecb_ik.cleartext, 0, ESCS_AES_KEY_SIZE);
m_security_slot[slot_no].aes_ecb_ik.cleartext[11] = 0xFF;
m_security_slot[slot_no].aes_ecb_ik.cleartext[14] =
(uint8_t)((m_security_slot[slot_no].timing.time_counter >> 24) & 0xff);
m_security_slot[slot_no].aes_ecb_ik.cleartext[15] =
(uint8_t)((m_security_slot[slot_no].timing.time_counter >> 16) & 0xff);
err_code = nrf_crypto_aes_crypt(&m_aes_context,
&g_nrf_crypto_aes_ecb_128_info,
NRF_CRYPTO_ENCRYPT, // Operation
m_security_slot[slot_no].aes_ecb_ik.key, // Key
NULL, // IV
m_security_slot[slot_no].aes_ecb_ik.cleartext, // Data in
AES_ECB_CLEARTEXT_LENGTH, // Data in size
m_security_slot[slot_no].aes_ecb_ik.ciphertext, // Data out
&ciphertext_size); // Data out size
APP_ERROR_CHECK(err_code);
memcpy(m_security_slot[slot_no].aes_ecb_tk.key,
m_security_slot[slot_no].aes_ecb_ik.ciphertext,
ESCS_AES_KEY_SIZE);
}
/**@brief See if EID should be re-calculated.
*/
static void check_rollovers_and_update_eid(uint8_t slot_no)
{
static uint32_t last_invocation_time_counter = 0;
uint32_t scaler = 2 << (m_security_slot[slot_no].timing.k_scaler - 1);
uint32_t diff;
if (last_invocation_time_counter == 0)
{
last_invocation_time_counter = m_security_slot[slot_no].timing.time_counter;
}
diff = m_security_slot[slot_no].timing.time_counter - last_invocation_time_counter;
if (diff >= scaler)
{
// Store to last scaler-aligned time.
last_invocation_time_counter = (m_security_slot[slot_no].timing.time_counter / scaler) * scaler;
eid_generate(slot_no);
}
}
/**@brief Initialize lock code from flash. If it does not exist, copy from APP_CONFIG_LOCK_CODE.
*/
static void lock_code_init(uint8_t * p_lock_buff)
{
ret_code_t err_code;
err_code = es_flash_access_lock_key(p_lock_buff, ES_FLASH_ACCESS_READ);
FLASH_ACCES_ERROR_CHECK_ALLOW_NOT_FOUND(err_code);
// If no lock keys exist, then generate one and copy it to buffer.
if (err_code == FDS_ERR_NOT_FOUND)
{
uint8_t lock_code[16] = APP_CONFIG_LOCK_CODE;
memcpy(p_lock_buff, lock_code, sizeof(lock_code));
err_code = es_flash_access_lock_key(p_lock_buff, ES_FLASH_ACCESS_WRITE);
APP_ERROR_CHECK(err_code);
}
}
void es_security_update_time(void)
{
static uint32_t timer_persist;
uint32_t second_since_last_invocation = es_stopwatch_check(m_seconds_passed_sw_id);
if (second_since_last_invocation > 0)
{
for (uint32_t i = 0; i < APP_MAX_EID_SLOTS; ++i)
{
if (m_security_slot[i].is_occupied)
{
m_security_slot[i].timing.time_counter += second_since_last_invocation;
check_rollovers_and_update_eid(i);
}
}
// Every 24 hr, write the new EID timer to flash.
timer_persist += second_since_last_invocation;
const uint32_t TWENTY_FOUR_HOURS = 60 * 60 * 24;
if (timer_persist >= TWENTY_FOUR_HOURS)
{
for (uint32_t i = 0; i < APP_MAX_EID_SLOTS; ++i)
{
if (m_security_slot[i].is_occupied)
{
m_security_callback(i, ES_SECURITY_MSG_STORE_TIME);
}
}
timer_persist = 0;
}
}
}
void es_security_eid_slots_restore(uint8_t slot_no,
uint8_t k_scaler,
uint32_t time_counter,
const uint8_t * p_ik)
{
m_security_slot[slot_no].timing.k_scaler = k_scaler;
m_security_slot[slot_no].timing.time_counter = time_counter;
memcpy(m_security_slot[slot_no].aes_ecb_ik.key, p_ik, ESCS_AES_KEY_SIZE);
m_security_slot[slot_no].is_occupied = true;
m_security_callback(slot_no, ES_SECURITY_MSG_IK);
eid_generate(slot_no);
}
ret_code_t es_security_lock_code_update(uint8_t * p_ecrypted_key)
{
ret_code_t err_code;
uint8_t temp_buff[ESCS_AES_KEY_SIZE] = {0};
size_t temp_buff_size = sizeof(temp_buff);
err_code = nrf_crypto_aes_crypt(&m_aes_context,
&g_nrf_crypto_aes_ecb_128_info,
NRF_CRYPTO_DECRYPT, // Operation
m_aes_ecb_lk.key, // Key
NULL, // IV
p_ecrypted_key, // Data in
16, // Data in size
temp_buff, // Data out
&temp_buff_size); // Data out size
VERIFY_SUCCESS(err_code);
memcpy(m_aes_ecb_lk.key, temp_buff, ESCS_AES_KEY_SIZE);
return es_flash_access_lock_key(m_aes_ecb_lk.key, ES_FLASH_ACCESS_WRITE);
}
void es_security_unlock_prepare(uint8_t * p_challenge)
{
ret_code_t err_code;
size_t ciphertext_size = AES_ECB_CIPHERTEXT_LENGTH;
memcpy(m_aes_ecb_lk.cleartext, p_challenge, ESCS_AES_KEY_SIZE);
err_code = nrf_crypto_aes_crypt(&m_aes_context,
&g_nrf_crypto_aes_ecb_128_info,
NRF_CRYPTO_ENCRYPT, // Operation
m_aes_ecb_lk.key, // Key
NULL, // IV
m_aes_ecb_lk.cleartext, // Data in
AES_ECB_CLEARTEXT_LENGTH, // Data in size
m_aes_ecb_lk.ciphertext, // Data out
&ciphertext_size); // Data out size
APP_ERROR_CHECK(err_code);
}
void es_security_unlock_verify(uint8_t * p_unlock_token)
{
if (memcmp(p_unlock_token, m_aes_ecb_lk.ciphertext, ESCS_AES_KEY_SIZE) == 0)
{
m_security_callback(0, ES_SECURITY_MSG_UNLOCKED);
}
}
ret_code_t es_security_random_challenge_generate(uint8_t * p_rand_chlg_buff)
{
return nrf_crypto_rng_vector_generate(p_rand_chlg_buff, ESCS_AES_KEY_SIZE);
}
void es_security_shared_ik_receive(uint8_t slot_no, uint8_t * p_encrypted_ik, uint8_t scaler_k)
{
ret_code_t err_code;
size_t cleartext_size = AES_ECB_CLEARTEXT_LENGTH;
m_security_slot[slot_no].is_occupied = true;
m_security_slot[slot_no].timing.k_scaler = scaler_k;
m_security_slot[slot_no].timing.time_counter = APP_CONFIG_TIMING_INIT_VALUE;
err_code = nrf_crypto_aes_crypt(&m_aes_context,
&g_nrf_crypto_aes_ecb_128_info,
NRF_CRYPTO_DECRYPT, // Operation
m_aes_ecb_lk.key, // Key
NULL, // IV
p_encrypted_ik, // Data in
16, // Data in size
m_security_slot[slot_no].aes_ecb_ik.key, // Data out
&cleartext_size); // Data out size
APP_ERROR_CHECK(err_code);
eid_generate(slot_no);
m_security_callback(slot_no, ES_SECURITY_MSG_IK);
}
void es_security_client_pub_ecdh_receive(uint8_t slot_no, uint8_t * p_pub_ecdh, uint8_t scaler_k)
{
ret_code_t err_code;
nrf_crypto_ecc_public_key_t phone_public; // Phone public ECDH key
uint8_t beacon_public[ESCS_ECDH_KEY_SIZE]; // Beacon public ECDH key
uint8_t shared[ESCS_ECDH_KEY_SIZE]; // Shared secret ECDH key
uint8_t public_keys[64]; // Buffer for concatenated public keys
uint8_t key_material[64]; // Buffer for holding key material
uint8_t empty_check[ESCS_ECDH_KEY_SIZE] = {0};
size_t beacon_public_size = sizeof(beacon_public);
size_t shared_size = sizeof(shared);
size_t key_material_size = sizeof(key_material);
m_security_slot[slot_no].is_occupied = true;
m_security_slot[slot_no].timing.k_scaler = scaler_k;
m_security_slot[slot_no].timing.time_counter = APP_CONFIG_TIMING_INIT_VALUE;
// Get public 32-byte service ECDH key from phone.
err_code = nrf_crypto_ecc_public_key_from_raw(&g_nrf_crypto_ecc_curve25519_curve_info,
&phone_public,
p_pub_ecdh,
ESCS_ECDH_KEY_SIZE);
APP_ERROR_CHECK(err_code);
// Generate key pair.
err_code = nrf_crypto_ecc_key_pair_generate(&ecc_key_pair_generate_context,
&g_nrf_crypto_ecc_curve25519_curve_info,
&m_ecdh.ecdh_key_pair.private,
&m_ecdh.ecdh_key_pair.public);
APP_ERROR_CHECK(err_code);
// Generate shared 32-byte ECDH secret from beacon private service ECDH key and phone public ECDH key.
err_code = nrf_crypto_ecdh_compute(&ecdh_context,
&m_ecdh.ecdh_key_pair.private,
&phone_public,
shared,
&shared_size);
APP_ERROR_CHECK(err_code);
// Verify that the shared secret is not zero at this point, and report an error/reset if it is.
if (memcmp(empty_check, shared, ESCS_ECDH_KEY_SIZE) == 0)
{
APP_ERROR_CHECK(NRF_ERROR_INTERNAL);
}
// Concatenate the resolver's public key and beacon's public key
err_code = nrf_crypto_ecc_public_key_to_raw(&m_ecdh.ecdh_key_pair.public,
beacon_public,
&beacon_public_size);
APP_ERROR_CHECK(err_code);
memcpy(public_keys, p_pub_ecdh, 32);
memcpy(public_keys + 32, beacon_public, 32);
// Convert the shared secret to key material using HKDF-SHA256. HKDF is used with the salt set
// to a concatenation of the resolver's public key and beacon's public key
err_code = nrf_crypto_hkdf_calculate(&m_hmac_context,
&g_nrf_crypto_hmac_sha256_info,
key_material, // Output key
&key_material_size, // Output key size
shared, // Input key
sizeof(shared), // Input key size
public_keys, // Salt
sizeof(public_keys), // Salt size
NULL, // Additional info
0, // Additional info size
NRF_CRYPTO_HKDF_EXTRACT_AND_EXPAND); // Mode
APP_ERROR_CHECK(err_code);
// Truncate the key material to 128 bits to convert it to an AES-128 secret key (Identity key).
memcpy(m_security_slot[slot_no].aes_ecb_ik.key, key_material, ESCS_AES_KEY_SIZE);
eid_generate(slot_no);
m_security_callback(slot_no, ES_SECURITY_MSG_ECDH);
m_security_callback(slot_no, ES_SECURITY_MSG_IK);
}
void es_security_pub_ecdh_get(uint8_t slot_no, uint8_t * p_edch_buffer)
{
ret_code_t err_code;
size_t buffer_size = ESCS_ECDH_KEY_SIZE;
err_code = nrf_crypto_ecc_public_key_to_raw(&m_ecdh.ecdh_key_pair.public,
p_edch_buffer,
&buffer_size);
APP_ERROR_CHECK(err_code);
}
uint32_t es_security_clock_get(uint8_t slot_no)
{
return m_security_slot[slot_no].timing.time_counter;
}
void es_security_eid_slot_destroy(uint8_t slot_no)
{
memset(&m_security_slot[slot_no], 0, sizeof(es_security_slot_t));
}
uint8_t es_security_scaler_get(uint8_t slot_no)
{
return m_security_slot[slot_no].timing.k_scaler;
}
void es_security_eid_get(uint8_t slot_no, uint8_t * p_eid_buffer)
{
memcpy(p_eid_buffer, m_security_slot[slot_no].eid, ES_EID_ID_LENGTH);
}
void es_security_encrypted_eid_id_key_get(uint8_t slot_no, uint8_t * p_key_buffer)
{
ret_code_t err_code;
size_t ciphertext_size = AES_ECB_CIPHERTEXT_LENGTH;
memcpy(m_aes_ecb_lk.cleartext, m_security_slot[slot_no].aes_ecb_ik.key, ESCS_AES_KEY_SIZE);
err_code = nrf_crypto_aes_crypt(&m_aes_context,
&g_nrf_crypto_aes_ecb_128_info,
NRF_CRYPTO_ENCRYPT, // Operation
m_aes_ecb_lk.key, // Key
NULL, // IV
m_aes_ecb_lk.cleartext, // Data in
AES_ECB_CLEARTEXT_LENGTH, // Data in size
m_aes_ecb_lk.ciphertext, // Data out
&ciphertext_size); // Data out size
APP_ERROR_CHECK(err_code);
memcpy(p_key_buffer, m_aes_ecb_lk.ciphertext, ESCS_AES_KEY_SIZE);
}
void es_security_plain_eid_id_key_get(uint8_t slot_no, uint8_t * p_key_buffer)
{
memcpy(p_key_buffer, m_security_slot[slot_no].aes_ecb_ik.key, ESCS_AES_KEY_SIZE);
}
void es_security_tlm_to_etlm(uint8_t ik_slot_no, es_tlm_frame_t * p_tlm, es_etlm_frame_t * p_etlm)
{
ret_code_t err_code;
uint8_t plain[TLM_DATA_SIZE] = {0}; // Plaintext tlm, without the frame byte and version.
size_t nplain = TLM_DATA_SIZE; // Length of message plaintext.
/*lint -save -e420 */
memcpy(plain, &p_tlm->vbatt[0], sizeof(plain));
uint8_t key[EIK_SIZE] = {0}; // Encryption/decryption key: EIK.
memcpy(key, &m_security_slot[ik_slot_no].aes_ecb_ik.key[0], EIK_SIZE);
/*lint -restore */
uint8_t nonce[NONCE_SIZE] = {0}; // Nonce. This must not repeat for a given key.
size_t nnonce = NONCE_SIZE; // Length of nonce.First 4 bytes are beacon time base with k-bits cleared.
// Last two bits are randomly generated
// Take the current timestamp and clear the lowest K bits, use it as nonce.
uint32_t k_bits_cleared_time = (m_security_slot[ik_slot_no].timing.time_counter
>> m_security_slot[ik_slot_no].timing.k_scaler)
<< m_security_slot[ik_slot_no].timing.k_scaler;
nonce[0] = (uint8_t)((k_bits_cleared_time >> 24) & 0xff);
nonce[1] = (uint8_t)((k_bits_cleared_time >> 16) & 0xff);
nonce[2] = (uint8_t)((k_bits_cleared_time >> 8) & 0xff);
nonce[3] = (uint8_t)((k_bits_cleared_time) & 0xff);
// Generate random salt.
uint8_t salt[SALT_SIZE] = {0};
err_code = nrf_crypto_rng_vector_generate(salt, SALT_SIZE);
APP_ERROR_CHECK(err_code);
memcpy(&nonce[4], salt, SALT_SIZE);
uint8_t cipher[ES_ETLM_ECRYPTED_LENGTH]; // Ciphertext output. nplain bytes are written.
uint8_t tag[TAG_SIZE] = {0}; // Authentication tag. ntag bytes are written.
size_t ntag = TAG_SIZE; // Length of authentication tag.
// Encryption
// --------------------------------------------------------------------------
err_code = nrf_crypto_aead_init(&m_aead_context, &g_nrf_crypto_aes_eax_128_info, key);
APP_ERROR_CHECK(err_code);
err_code = nrf_crypto_aead_crypt(&m_aead_context,
NRF_CRYPTO_ENCRYPT, // Operation
nonce, // Nonce
nnonce, // Nonce size
NULL, // Additional authenticated data (adata)
0, // Additional authenticated data size
plain, // Input data
nplain, // Input data size
cipher, // Output data
tag, // MAC result output
ntag); // MAC size
APP_ERROR_CHECK(err_code);
err_code = nrf_crypto_aead_uninit(&m_aead_context);
APP_ERROR_CHECK(err_code);
// Construct the eTLM.
// --------------------------------------------------------------------------
p_etlm->frame_type = p_tlm->frame_type;
p_etlm->version = ES_TLM_VERSION_ETLM;
memcpy(p_etlm->encrypted_tlm, cipher, ES_ETLM_ECRYPTED_LENGTH);
memcpy((uint8_t *)&p_etlm->random_salt, salt, SALT_SIZE);
memcpy((uint8_t *)&p_etlm->msg_integrity_check, tag, TAG_SIZE);
}
ret_code_t es_security_init(es_security_msg_cb_t security_callback)
{
ret_code_t err_code;
if (security_callback == NULL)
{
return NRF_ERROR_INVALID_PARAM;
}
// Get lock code from 'es_app_config.h', or fetch it from flash if exists.
lock_code_init(m_aes_ecb_lk.key);
m_security_callback = security_callback;
memset(&m_ecdh, 0, sizeof(es_security_ecdh_t));
for (uint32_t i = 0; i < APP_MAX_EID_SLOTS; ++i)
{
m_security_slot[i].timing.time_counter = APP_CONFIG_TIMING_INIT_VALUE;
}
err_code = es_stopwatch_create(&m_seconds_passed_sw_id, APP_TIMER_TICKS(1000));
APP_ERROR_CHECK(err_code);
err_code = nrf_crypto_init();
APP_ERROR_CHECK(err_code);
return NRF_SUCCESS;
}

View File

@@ -0,0 +1,238 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef ES_SECURITY_H__
#define ES_SECURITY_H__
#include "app_error.h"
#include "nrf_ble_escs.h"
/**
* @file
* @defgroup eddystone_security Security
* @brief Types and functions for dealing with security of Eddystone beacons.
* @ingroup eddystone
* @{
*/
/**@brief Security events.
*/
typedef enum
{
ES_SECURITY_MSG_UNLOCKED, //!< Beacon is unlocked.
ES_SECURITY_MSG_EID, //!< EID has been generated.
ES_SECURITY_MSG_IK, //!< IK has been generated.
ES_SECURITY_MSG_ECDH, //!< Public ECDH has been generated.
ES_SECURITY_MSG_STORE_TIME //!< EID slot time must be stored.
} es_security_msg_t;
/* @brief Callback for security events. */
typedef void (*es_security_msg_cb_t)(uint8_t slot_no, es_security_msg_t msg_type);
/**@brief EID configuration.
*
* @details This structure is used to preserve or restore an EID slot.
*
* @note This is a packed structure. Therefore, you should not change it.
*/
typedef PACKED_STRUCT
{
es_frame_type_t frame_type;
uint8_t k_scaler;
uint32_t time_counter;
uint8_t ik[ESCS_AES_KEY_SIZE];
} es_eid_config_t;
/**@brief Eddystone beacon lock state.
*/
typedef nrf_ble_escs_lock_state_read_t es_security_lock_state_t;
/**@brief Function for initializing the security module.
*
* @param[in] msg_cb Callback function.
*
* @return See @ref app_timer_start for possible return values.
*/
ret_code_t es_security_init(es_security_msg_cb_t msg_cb);
/**@brief Function for updating the lock code and storing it to flash.
*
* @param[in] p_encrypted_key Pointer to the new lock code.
*
* @return See @ref es_flash_access_lock_key for possible return values.
*/
ret_code_t es_security_lock_code_update(uint8_t * p_encrypted_key);
/**@brief Function for reading the challenge and encrypting it with AES_ECB.
*
* @details The result of the encryption is compared with the provided unlock token
* in @ref es_security_unlock_verify.
*
* @param[in] p_challenge Pointer to the challenge buffer.
*
* @return See @ref sd_ecb_block_encrypt for possible return values.
*/
void es_security_unlock_prepare(uint8_t * p_challenge);
/**@brief Function for unlocking the beacon.
*
* @details This function compares the result from @ref es_security_unlock_prepare to the input
* unlock token and unlocks the beacon if matching.
*
* @param[in] p_unlock_token The unlock token written by the client.
*/
void es_security_unlock_verify(uint8_t * p_unlock_token);
/**@brief Function for generating a random challenge for the unlock characteristic.
*
* @param[out] p_rand_chlg_buff Pointer to a buffer to which the random challenge is copied.
*
* @return See @ref sd_rand_application_vector_get for possible return values.
*/
ret_code_t es_security_random_challenge_generate(uint8_t * p_rand_chlg_buff);
/**@brief Function for storing the public ECDH key from the client in the beacon registration process.
*
* @details This function starts a series of cryptographic activities, including the generation of temporary keys and EIDs.
*
* @param[in] slot_no The index of the slot whose public ECDH key is retrieved.
* @param[in] p_pub_ecdh Pointer to the public ECDH.
* @param[in] scaler_k K rotation scaler.
*/
void es_security_client_pub_ecdh_receive(uint8_t slot_no, uint8_t * p_pub_ecdh, uint8_t scaler_k);
/**@brief Function for storing the shared IK from the client in the beacon registration process.
*
* @details This function starts a series of cryptographic activities, including the generation of temporary keys and EIDs.
*
* @param[in] slot_no The index of the slot whose public ECDH key is retrieved.
* @param[in] p_encrypted_ik Pointer to the received IK.
* @param[in] scaler_k K rotation scaler.
*/
void es_security_shared_ik_receive(uint8_t slot_no, uint8_t * p_encrypted_ik, uint8_t scaler_k);
/**@brief Function for copying the 32-byte ECDH key into the provided buffer.
*
* @param[in] slot_no The index of the slot whose public ECDH key is retrieved.
* @param[out] p_edch_buffer Pointer to the buffer.
*/
void es_security_pub_ecdh_get(uint8_t slot_no, uint8_t * p_edch_buffer);
/**@brief Function for returning the beacon clock value (in little endian).
*
* @param[in] slot_no The index of the slot.
*
* @return 32-bit clock value.
*/
uint32_t es_security_clock_get(uint8_t slot_no);
/**@brief Function for updating the beacon time counter.
*
* @details This function checks how much time has passed since the last
* invocation and, if required, updates the EID, the temporary key, or both.
* The function generates an @ref ES_SECURITY_MSG_STORE_TIME event
* for each active security slot every 24 hours.
*/
void es_security_update_time(void);
/**@brief Function for returning the rotation exponent scaler value.
*
* @param[in] slot_no The index of the slot.
*
* @return K rotation scaler.
*/
uint8_t es_security_scaler_get(uint8_t slot_no);
/**@brief Function for copying the 8-byte EID into the provided buffer.
*
* @param[in] slot_no The index of the slot whose EID is retrieved.
* @param[out] p_eid_buffer Pointer to the buffer.
*/
void es_security_eid_get(uint8_t slot_no, uint8_t * p_eid_buffer);
/**@brief Function for restoring an EID slot.
*
* @param[in] slot_no The index of the slot to restore.
* @param[in] k_scaler K rotation scaler.
* @param[in] time_counter EID slot time counter value (in seconds).
* @param[in] p_ik Pointer to the identity key of the specified slot.
*/
void es_security_eid_slots_restore(uint8_t slot_no,
uint8_t k_scaler,
uint32_t time_counter,
const uint8_t * p_ik);
/**@brief Function for destroying stored EID states.
*
* @details This function should be called when the slot is either overwritten as another slot or
* cleared by writing an empty byte or a single 0.
*
* @param[in] slot_no The index of the slot to destroy.
*/
void es_security_eid_slot_destroy(uint8_t slot_no);
/**@brief Function for copying the 16-byte EID ID key into the provided buffer.
*
* @param[in] slot_no The index of the EID slot whose IK is retrieved.
* @param[out] p_key_buffer Buffer for the key.
*/
void es_security_plain_eid_id_key_get(uint8_t slot_no, uint8_t * p_key_buffer);
/**@brief Function for copying the 16-byte LK encrypted EID ID key into the provided buffer.
*
* @param[in] slot_no The index of the EID slot whose encrypted IK is retrieved.
* @param[out] p_key_buffer Buffer for the key.
*/
void es_security_encrypted_eid_id_key_get(uint8_t slot_no, uint8_t * p_key_buffer);
/**@brief Function for converting a TLM frame into an eTLM frame using the EIK of the specified slot.
*
* @param[in] ik_slot_no The index of the EID slot whose IK is paired with the eTLM.
* @param[in] p_tlm Pointer to the TLM frame buffer.
* @param[out] p_etlm Pointer to the eTLM frame buffer.
*/
void es_security_tlm_to_etlm(uint8_t ik_slot_no, es_tlm_frame_t * p_tlm, es_etlm_frame_t * p_etlm);
/**
* @}
*/
#endif // ES_SECURITY_H__

View File

@@ -0,0 +1,433 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <stdint.h>
#include <string.h>
#include "es_slot.h"
#include "es_flash.h"
#include "es_security.h"
#include "es_slot_reg.h"
#include "es_tlm.h"
#include "fds.h"
static es_slot_reg_t m_reg; //!< Slot registry.
static bool m_eid_loaded_from_flash; //!< Set to true if EID slot has been loaded from flash.
#define RANGING_DATA_INDEX (1) //!< Index of ranging data within frames that contain ranging data.
#define RANGING_DATA_LENGTH (1) //!< Length of ranging data.
/**@brief Enforce legal slot number.
*
* @param[in] p_slot Pointer to the slot number variable to check.
*/
static void slot_boundary_check(uint8_t * p_slot)
{
if (*p_slot > (APP_MAX_ADV_SLOTS - 1))
{
*p_slot = (APP_MAX_ADV_SLOTS - 1);
}
}
/**@brief Function loading slot data from flash.
*
* @param[in] slot_no Slot number to be used.
*/
static void load_slot_from_flash(uint8_t slot_no)
{
ret_code_t err_code;
err_code = es_flash_access_slot_configs(slot_no, &m_reg.slots[slot_no], ES_FLASH_ACCESS_READ);
if (err_code != FDS_ERR_NOT_FOUND)
{
APP_ERROR_CHECK(err_code);
if (m_reg.slots[slot_no].adv_frame.type == ES_FRAME_TYPE_EID)
{
m_eid_loaded_from_flash = true;
es_security_eid_slots_restore(slot_no,
m_reg.slots[slot_no].k_scaler,
m_reg.slots[slot_no].seconds,
(const uint8_t *)m_reg.slots[slot_no].ik);
}
else
{
// If a non-EID slot has been loaded, update the state of m_reg immediately.
es_slot_reg_update_slot_list_info_on_add(&m_reg, slot_no, m_reg.slots[slot_no].adv_frame.type, true);
}
}
}
/**@brief Function for setting the ranging data field to be broadcast in the frame.
*
* @param[in] slot_no The slot index.
* @param[in] tx_power The radio tx power to be calibrated to ranging data.
*/
static void set_ranging_data_for_slot(uint8_t slot_no, nrf_ble_escs_radio_tx_pwr_t tx_power)
{
int8_t ranging_data_array[ESCS_NUM_OF_SUPPORTED_TX_POWER] = APP_CONFIG_CALIBRATED_RANGING_DATA;
nrf_ble_escs_radio_tx_pwr_t supported_tx[ESCS_NUM_OF_SUPPORTED_TX_POWER] =
ESCS_SUPPORTED_TX_POWER;
int8_t ranging_data = 0;
if (m_reg.slots[slot_no].adv_custom_tx_power)
{
ranging_data = m_reg.slots[slot_no].custom_tx_power;
}
else
{
for (uint32_t i = 0; i < ESCS_NUM_OF_SUPPORTED_TX_POWER; ++i)
{
if (supported_tx[i] >= tx_power)
{
ranging_data = ranging_data_array[i];
break;
}
}
}
es_adv_frame_t * frame = &m_reg.slots[slot_no].adv_frame;
switch (frame->type)
{
case ES_FRAME_TYPE_UID:
{
es_uid_frame_t * uid = &frame->frame.uid;
uid->ranging_data = ranging_data;
break;
}
case ES_FRAME_TYPE_URL:
{
es_url_frame_t * url = &frame->frame.url;
url->ranging_data = ranging_data;
break;
}
case ES_FRAME_TYPE_EID:
{
es_eid_frame_t * eid = &frame->frame.eid;
eid->ranging_data = ranging_data;
break;
}
case ES_FRAME_TYPE_TLM:
APP_ERROR_CHECK(NRF_ERROR_INVALID_PARAM);
break;
}
}
/**@brief Function configuring a non-EID slot.
*
* @param[in] slot_no Slot number to be used.
* @param[in] length Length of write operation.
* @param[in] p_frame_data Pointer to written data.
*/
static void configure_slot(uint8_t slot_no, uint8_t length, uint8_t const * p_frame_data)
{
// If a TLM slot is being configured and there already exists a TLM.
if ((es_frame_type_t)p_frame_data[0] == ES_FRAME_TYPE_TLM && m_reg.tlm_configured)
{
return; // Silently ignore any attempts to create more than one TLM slot as there is no point.
}
es_slot_reg_update_slot_list_info_on_add(&m_reg, slot_no, (es_frame_type_t)p_frame_data[0], false);
// For convenience, frame_type is stored in two places, set both.
m_reg.slots[slot_no].adv_frame.type = (es_frame_type_t)p_frame_data[0];
memcpy(&m_reg.slots[slot_no].adv_frame.frame, &m_reg.slots[slot_no].adv_frame.type, 1);
uint8_t * p_data_after_ranging_data = ((uint8_t *)(&m_reg.slots[slot_no].adv_frame.frame) +
RANGING_DATA_INDEX + RANGING_DATA_LENGTH);
switch (m_reg.slots[slot_no].adv_frame.type)
{
case ES_FRAME_TYPE_UID:
// Fall through.
case ES_FRAME_TYPE_URL:
memcpy(p_data_after_ranging_data, &p_frame_data[1], length - 1);
set_ranging_data_for_slot(slot_no, APP_CFG_DEFAULT_RADIO_TX_POWER);
m_reg.slots[slot_no].adv_frame.length = length + 1; // + 1 for ranging data
break;
case ES_FRAME_TYPE_TLM:
es_tlm_tlm_get(&m_reg.slots[slot_no].adv_frame.frame.tlm);
m_reg.slots[slot_no].adv_frame.length = ES_TLM_LENGTH;
break;
default:
break;
}
}
/**@brief Function configuring an EID slot.
*
* @param[in] slot_no Slot number to be used.
* @param[in] length Length of write operation.
* @param[in] p_frame_data Pointer to written data.
*/
static void configure_eid_slot(uint8_t slot_no, uint8_t length, uint8_t const * p_frame_data)
{
// Do not update slot count, as this will be done when in the callback invoked when the EID data
// is ready.
// As it takes a while to do the calculation, temporarily remove the slot being overwritten.
// The slot will be re-added in the callback invoked when the EID data is ready.
(void)es_slot_reg_clear_slot(&m_reg, slot_no);
if (p_frame_data[0] != ES_FRAME_TYPE_EID)
{
APP_ERROR_CHECK(NRF_ERROR_INVALID_STATE);
}
if (length == ESCS_EID_WRITE_ECDH_LENGTH)
{
es_security_client_pub_ecdh_receive(slot_no,
(uint8_t*)&p_frame_data[ESCS_EID_WRITE_PUB_KEY_INDEX],
p_frame_data[ESCS_EID_WRITE_ECDH_LENGTH -1]);
}
else if (length == ESCS_EID_WRITE_IDK_LENGTH)
{
es_security_shared_ik_receive(slot_no,
(uint8_t*)&p_frame_data[ESCS_EID_WRITE_ENC_ID_KEY_INDEX],
p_frame_data[ESCS_EID_WRITE_IDK_LENGTH - 1]);
}
else
{
// Invalid length being written.
APP_ERROR_CHECK(NRF_ERROR_INVALID_PARAM);
}
}
ret_code_t es_slot_write_to_flash(uint8_t slot_no)
{
if (m_reg.slots[slot_no].configured)
{
// If its an EID, we need to store some metadata in order to re-initialize the EID.
if (m_reg.slots[slot_no].adv_frame.type == ES_FRAME_TYPE_EID)
{
m_reg.slots[slot_no].seconds = es_security_clock_get(slot_no);
m_reg.slots[slot_no].k_scaler = es_security_scaler_get(slot_no);
es_security_plain_eid_id_key_get(slot_no, m_reg.slots[slot_no].ik);
}
return es_flash_access_slot_configs(slot_no, &m_reg.slots[slot_no], ES_FLASH_ACCESS_WRITE);
}
else
{
return es_flash_access_slot_configs(slot_no, NULL, ES_FLASH_ACCESS_CLEAR);
}
}
void es_slot_radio_tx_pwr_set(uint8_t slot_no, nrf_ble_escs_radio_tx_pwr_t radio_tx_pwr)
{
slot_boundary_check(&slot_no);
m_reg.slots[slot_no].radio_tx_pwr = radio_tx_pwr;
if (!m_reg.slots[slot_no].adv_custom_tx_power) // Only update TX power in ADV if custom TX power is not set
{
set_ranging_data_for_slot(slot_no, radio_tx_pwr);
}
}
void es_slot_set_adv_custom_tx_power(uint8_t slot_no, nrf_ble_escs_adv_tx_pwr_t tx_pwr)
{
slot_boundary_check(&slot_no);
m_reg.slots[slot_no].adv_custom_tx_power = true;
m_reg.slots[slot_no].custom_tx_power = tx_pwr;
set_ranging_data_for_slot(slot_no, tx_pwr);
}
void es_slot_on_write(uint8_t slot_no, uint8_t length, uint8_t const * p_frame_data)
{
slot_boundary_check(&slot_no);
if (p_frame_data == NULL)
{
APP_ERROR_CHECK(NRF_ERROR_NULL);
}
// Cleared
if (length == 0 || (length == 1 && p_frame_data[0] == 0))
{
(void)es_slot_reg_clear_slot(&m_reg, slot_no);
}
// EID slot being configured
else if (p_frame_data[0] == ES_FRAME_TYPE_EID &&
(length == ESCS_EID_WRITE_ECDH_LENGTH || length == ESCS_EID_WRITE_IDK_LENGTH))
{
if (m_reg.slots[slot_no].configured)
(void)es_slot_reg_clear_slot(&m_reg, slot_no);
configure_eid_slot(slot_no, length, p_frame_data);
}
// Non-EID slot configured.
else
{
if (m_reg.slots[slot_no].configured)
(void)es_slot_reg_clear_slot(&m_reg, slot_no);
configure_slot(slot_no, length, p_frame_data);
}
}
void es_slot_encrypted_eid_id_key_set(uint8_t slot_no, nrf_ble_escs_eid_id_key_t * p_eid_id_key)
{
slot_boundary_check(&slot_no);
if (p_eid_id_key != NULL)
{
memcpy(&(m_reg.slots[slot_no].encrypted_eid_id_key), p_eid_id_key,
sizeof(nrf_ble_escs_eid_id_key_t));
}
}
void es_slot_eid_ready(uint8_t slot_no)
{
m_reg.slots[slot_no].adv_frame.type = ES_FRAME_TYPE_EID;
m_reg.slots[slot_no].adv_frame.length = ES_EID_LENGTH;
es_security_eid_get(slot_no, (uint8_t *)m_reg.slots[slot_no].adv_frame.frame.eid.eid);
m_reg.slots[slot_no].adv_frame.frame.eid.frame_type = ES_FRAME_TYPE_EID;
set_ranging_data_for_slot(slot_no, m_reg.slots[slot_no].radio_tx_pwr);
if (m_eid_loaded_from_flash)
{
es_slot_reg_update_slot_list_info_on_add(&m_reg, slot_no, ES_FRAME_TYPE_EID, true);
m_eid_loaded_from_flash = false;
}
else
{
es_slot_reg_update_slot_list_info_on_add(&m_reg, slot_no, ES_FRAME_TYPE_EID, false);
}
}
static bool slot_is_eid(uint8_t eid_slot_no)
{
for (uint32_t i = 0; i < m_reg.num_configured_eid_slots; ++i)
{
if (m_reg.eid_slots_configured[i] == eid_slot_no)
{
return true;
}
}
return false;
}
void es_slot_tlm_update(void)
{
if (m_reg.tlm_configured)
{
es_tlm_tlm_get(&m_reg.slots[m_reg.tlm_slot].adv_frame.frame.tlm);
}
}
void es_slot_etlm_update(uint8_t eid_slot_no)
{
es_tlm_frame_t tlm;
es_etlm_frame_t etlm;
// Ignore the request if eTLM is not required or slot no does not correspond to an EID slot.
if (!es_slot_reg_etlm_required(&m_reg) || !slot_is_eid(eid_slot_no))
{
return;
}
es_tlm_tlm_get(&tlm);
es_security_tlm_to_etlm(eid_slot_no, &tlm, &etlm);
memcpy(&m_reg.slots[m_reg.tlm_slot].adv_frame.frame.etlm, &etlm, sizeof(es_etlm_frame_t));
m_reg.slots[m_reg.tlm_slot].adv_frame.length = sizeof(es_etlm_frame_t);
}
const es_slot_reg_t * es_slot_get_registry(void)
{
return (const es_slot_reg_t *)&m_reg;
}
void es_slots_init(const es_slot_t * p_default_slot)
{
ret_code_t err_code;
es_flash_flags_t flash_flags = {{0}};
es_slot_reg_init(&m_reg);
m_eid_loaded_from_flash = false;
// Read the flash flags to see if there are any previously stored slot configs
err_code = es_flash_access_flags(&flash_flags, ES_FLASH_ACCESS_READ);
if (err_code == FDS_ERR_NOT_FOUND)
{
// Factory reset or initial boot, load default data
memcpy(&m_reg.slots[0], p_default_slot, sizeof(*p_default_slot));
es_slot_reg_update_slot_list_info_on_add(&m_reg, 0, p_default_slot->adv_frame.type, true);
}
else
{
APP_ERROR_CHECK(err_code);
for (uint32_t i = 0; i < APP_MAX_ADV_SLOTS; ++i)
{
if (!flash_flags.slot_is_empty[i])
{
load_slot_from_flash(i);
}
}
}
}

View File

@@ -0,0 +1,202 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef ES_SLOT_H__
#define ES_SLOT_H__
#include <stdint.h>
#include "es_app_config.h"
#include "nrf_ble_escs.h"
/**
* @file
* @defgroup eddystone_slot Slots
* @brief Types and functions for handling Eddystone slots.
* @ingroup eddystone
* @{
*/
/**@brief Advertisable frame types that can be passed in to the advertising
* data during non-connectable slot advertising. */
typedef struct
{
union
{
es_uid_frame_t uid; //!< UID frame.
es_url_frame_t url; //!< URL frame.
es_tlm_frame_t tlm; //!< TLM frame.
es_eid_frame_t eid; //!< EID frame.
es_etlm_frame_t etlm; //!< eTLM frame.
} frame;
es_frame_type_t type; //!< Type defined twice for convenience (because the other one is inside a union).
uint8_t length;
}es_adv_frame_t;
/**@brief Slot. */
typedef struct
{
uint8_t slot_no; //!< Identifier for the slot, indexed at 0.
nrf_ble_escs_radio_tx_pwr_t radio_tx_pwr; //!< Radio TX power (in dB).
nrf_ble_escs_eid_id_key_t encrypted_eid_id_key; //!< EID key for the slot.
es_adv_frame_t adv_frame; //!< Frame structure to be passed in for advertising data.
bool adv_custom_tx_power; //!< Flag that specifies if the client has written to the 'Advertised TX Power' field of this slot.
nrf_ble_escs_radio_tx_pwr_t custom_tx_power; //!< Custom TX power to advertise (only if @ref adv_custom_tx_power is true).
bool configured; //!< Is this slot configured and active.
uint8_t k_scaler;
uint32_t seconds;
uint8_t ik[ESCS_AES_KEY_SIZE];
} es_slot_t;
/**@brief Slot registry. */
typedef struct
{
es_slot_t slots[APP_MAX_ADV_SLOTS];
uint8_t num_configured_slots;
uint8_t num_configured_eid_slots;
uint8_t slots_configured[APP_MAX_ADV_SLOTS];
uint8_t eid_slots_configured[APP_MAX_EID_SLOTS];
uint8_t tlm_slot;
bool tlm_configured;
uint8_t scaler_k;
uint8_t enc_key[ESCS_AES_KEY_SIZE];
} es_slot_reg_t;
/**@brief Function for initializing the Eddystone slots with default values.
*
* @details This function synchronizes all slots with the initial values.
*
* @param[in] p_default_slot Pointer to the default parameters for a slot.
*/
void es_slots_init(const es_slot_t * p_default_slot);
/**@brief Function for setting the advertising interval of the specified slot.
*
* For compatibility with the Eddystone specifications, @p p_adv_interval must point to
* a 16-bit big endian value (coming from the characteristic write request),
* which is then converted to a little endian value inside the function before
* it is written into the variable in the slot.
*
* @parameternoteslot
* @parameternoteadv
*
* @param[in] slot_no The index of the slot.
* @param[in,out] p_adv_interval Pointer to the advertisement interval (in ms) to set.
* @param[in] global Flag that should be set if the beacon does not support variable advertising intervals.
*/
void es_slot_adv_interval_set(uint8_t slot_no,
nrf_ble_escs_adv_interval_t * p_adv_interval,
bool global);
/**@brief Function for setting the TX power of the specified slot.
*
* @parameternoteslot
* @parameternotetxpower
*
* @param[in] slot_no The index of the slot.
* @param[in,out] radio_tx_pwr TX power value to set.
*/
void es_slot_radio_tx_pwr_set(uint8_t slot_no, nrf_ble_escs_radio_tx_pwr_t radio_tx_pwr);
/**@brief Function for setting the R/W ADV of the specified slot.
*
* @parameternoteslot
*
* @param[in] slot_no The index of the slot.
* @param[in,out] length The length of the data written or read.
* @param[in,out] p_frame_data Pointer to the data.
*
*/
void es_slot_on_write(uint8_t slot_no, uint8_t length, uint8_t const * p_frame_data);
/**@brief Function for writing the slot's configuration to flash.
*
* @param[in] slot_no The index of the slot.
*/
ret_code_t es_slot_write_to_flash(uint8_t slot_no);
/**@brief Function for setting the slot's encrypted EID Identity Key to be displayed in the EID Identity Key characteristic.
*
* @parameternoteslot
*
* @param[in] slot_no The index of the slot.
* @param[in,out] p_eid_id_key Pointer to a @ref nrf_ble_escs_eid_id_key_t structure from where the key will be written.
*/
void es_slot_encrypted_eid_id_key_set(uint8_t slot_no, nrf_ble_escs_eid_id_key_t * p_eid_id_key);
/**@brief Function for marking an EID slot as ready for populating.
*
* @details Call this function when an EID has been generated and the advertisement frame can be populated with the EID.
*
* @param[in] slot_no The index of the slot.
*/
void es_slot_eid_ready(uint8_t slot_no);
/**@brief Function for updating the TLM slot with updated data. */
void es_slot_tlm_update(void);
/**@brief Function for updating the TLM slot with eTLM data.
*
* @details This function uses the EID identity key from the given EID slot number to update the TLM slot.
*
* @param[in] eid_slot_no EID slot to get EID identity key from.
*/
void es_slot_etlm_update(uint8_t eid_slot_no);
/**@brief Function for getting a pointer to the slot registry.
*
* @return A pointer to the slot registry.
*/
const es_slot_reg_t * es_slot_get_registry(void);
/**@brief Function for setting a custom advertisement TX power for a given slot.
*
* @parameternoteslot
* @parameternotetxpower
*
* @param[in] slot_no The index of the slot.
* @param[in] tx_pwr Advertised TX power to be set.
*/
void es_slot_set_adv_custom_tx_power(uint8_t slot_no, nrf_ble_escs_adv_tx_pwr_t tx_pwr);
/**
* @}
*/
#endif // ES_SLOT_H__

View File

@@ -0,0 +1,187 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "es_slot_reg.h"
#include "es_security.h"
#define ES_SLOT_NOT_CONFIGURED 0xab /** Value set in configured lists to indicate not configured slot. */
/**@brief Function updating 'tlm_configured' property of slot registry when slot is being cleared.
*
* @param[in] p_reg Pointer to slot registry.
* @param[in] slot_no Slot number to be used.
*/
static void update_tlm_configured_on_clearing(es_slot_reg_t * p_reg, uint8_t slot_no)
{
if (p_reg->tlm_configured && slot_no == p_reg->tlm_slot)
{
p_reg->tlm_configured = false;
}
}
/**@brief Function updating 'num_configured_slots' and 'slots_configured' properties of slot registry when slot is being cleared.
*
* @param[in] p_configured Pointer to list of configured slots.
* @param[in] p_num_configured_slots Pointer to number of configured slots.
* @param[in] slot_no Slot number to clear.
*/
static void configured_slots_on_clear_update(uint8_t * p_configured, uint8_t * p_num_configured_slots, uint8_t slot_no)
{
uint8_t index_of_last_configured_slot = *p_num_configured_slots - 1;
for (uint32_t i = 0; i < APP_MAX_ADV_SLOTS; ++i)
{
if (p_configured[i] == slot_no)
{
// Copy all values 'to the right' of the cleared slot one step to the left.
if (i < index_of_last_configured_slot)
{
for (uint32_t j = i; j < index_of_last_configured_slot; ++j)
{
p_configured[j] = p_configured[j + 1];
}
// Write ES_SLOT_NOT_CONFIGURED to all rightmost not configured indexes.
memset(&p_configured[index_of_last_configured_slot],
ES_SLOT_NOT_CONFIGURED,
APP_MAX_ADV_SLOTS - index_of_last_configured_slot);
}
else
{
// There are no values 'to the right', simply overwrite with ES_SLOT_NOT_CONFIGURED
p_configured[i] = ES_SLOT_NOT_CONFIGURED;
}
*p_num_configured_slots -= 1;
return;
}
}
}
bool es_slot_reg_etlm_required(const es_slot_reg_t * p_reg)
{
return (p_reg->num_configured_eid_slots > 0 && p_reg->tlm_configured);
}
bool es_slot_reg_clear_slot(es_slot_reg_t * p_reg, uint8_t slot_no)
{
bool eid_has_been_cleared = false;
if (p_reg->slots[slot_no].configured)
{
update_tlm_configured_on_clearing(p_reg, slot_no);
configured_slots_on_clear_update(p_reg->slots_configured,
&p_reg->num_configured_slots,
slot_no);
if (p_reg->slots[slot_no].adv_frame.type == ES_FRAME_TYPE_EID)
{
configured_slots_on_clear_update(p_reg->eid_slots_configured,
&p_reg->num_configured_eid_slots,
slot_no);
eid_has_been_cleared = true;
}
p_reg->slots[slot_no].configured = false;
}
memset(&p_reg->slots[slot_no], 0, sizeof(p_reg->slots[slot_no]));
if (eid_has_been_cleared)
{
es_security_eid_slot_destroy(slot_no);
}
return eid_has_been_cleared;
}
void es_slot_reg_update_slot_list_info_on_add(es_slot_reg_t * p_reg,
uint8_t slot_no,
es_frame_type_t frame_type,
bool init)
{
if (frame_type == ES_FRAME_TYPE_TLM)
{
p_reg->tlm_configured = true;
p_reg->tlm_slot = slot_no;
}
if (!p_reg->slots[slot_no].configured || init)
{
p_reg->slots[slot_no].configured = true;
// Note, we use 'num_configured_slots' before incrementing it, so it is pointing to the correct index.
p_reg->slots_configured[p_reg->num_configured_slots] = slot_no;
p_reg->num_configured_slots++;
if (frame_type == ES_FRAME_TYPE_EID)
{
p_reg->eid_slots_configured[p_reg->num_configured_eid_slots] = slot_no;
p_reg->num_configured_eid_slots++;
}
}
// If an already configured slot has changed from anything TO an EID slot.
else if (frame_type == ES_FRAME_TYPE_EID &&
p_reg->slots[slot_no].adv_frame.type != ES_FRAME_TYPE_EID)
{
p_reg->eid_slots_configured[p_reg->num_configured_eid_slots] = slot_no;
p_reg->num_configured_eid_slots++;
}
}
void es_slot_reg_init(es_slot_reg_t * p_reg)
{
p_reg->tlm_configured = false;
memset(p_reg->slots_configured, ES_SLOT_NOT_CONFIGURED, APP_MAX_ADV_SLOTS);
memset(p_reg->eid_slots_configured, ES_SLOT_NOT_CONFIGURED, APP_MAX_EID_SLOTS);
p_reg->num_configured_eid_slots = 0;
p_reg->num_configured_slots = 0;
}

View File

@@ -0,0 +1,90 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef ES_SLOT_REG_H__
#define ES_SLOT_REG_H__
#include <stdint.h>
#include "es_slot.h"
/**
* @file
* @addtogroup eddystone_slot
* @{
*/
/** @brief Function for checking if an eTLM frame is required.
*
* @param[in] p_reg Pointer to the slot registry.
*
* @retval true If an eTLM frame is required.
* @retval false Otherwise.
*/
bool es_slot_reg_etlm_required(const es_slot_reg_t * p_reg);
/** @brief Function for clearing a slot.
*
* @param[in] p_reg Pointer to the slot registry.
* @param[in] slot_no The slot number to clear.
*
* @retval true If an EID slot was cleared.
*/
bool es_slot_reg_clear_slot(es_slot_reg_t * p_reg, uint8_t slot_no);
/** @brief Function for updating the state of the slot registry after adding a slot.
*
* @param[in] p_reg Pointer to the slot registry.
* @param[in] slot_no The slot number that was added.
* @param[in] frame_type The frame type that was added.
* @param[in] init Information if the data is loaded during initialization. Set this
* parameter to false if the call is a result of a write to the Eddystone Configuration Service.
*/
void es_slot_reg_update_slot_list_info_on_add(es_slot_reg_t * p_reg, uint8_t slot_no, es_frame_type_t frame_type, bool init);
/** @brief Function for initializing the slot registry.
*
* @param[in] p_reg Pointer to the slot registry to initialize.
*/
void es_slot_reg_init(es_slot_reg_t * p_reg);
/**
* @}
*/
#endif // ES_SLOT_REG_H__

View File

@@ -0,0 +1,106 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "es_stopwatch.h"
#include "sdk_macros.h"
#include "app_timer.h"
#include "es_app_config.h"
static uint32_t m_ticks_last_returned[ES_STOPWATCH_MAX_USERS];
static uint32_t m_ids_ticks_wrap[ES_STOPWATCH_MAX_USERS];
static uint8_t m_nof_ids = 0;
static bool m_initialized = false;
uint32_t es_stopwatch_check(es_stopwatch_id_t id)
{
uint32_t ticks_current = app_timer_cnt_get();
uint32_t ticks_diff;
if (m_ids_ticks_wrap[id] == 0)
{
APP_ERROR_CHECK(NRF_ERROR_INVALID_STATE);
}
ticks_diff = app_timer_cnt_diff_compute(ticks_current, m_ticks_last_returned[id]);
if (ticks_diff >= m_ids_ticks_wrap[id])
{
m_ticks_last_returned[id] = (ticks_current / m_ids_ticks_wrap[id]) * m_ids_ticks_wrap[id];
return ticks_diff / m_ids_ticks_wrap[id];
}
return 0;
}
ret_code_t es_stopwatch_create(es_stopwatch_id_t * p_sw_id, uint32_t ticks_wrap)
{
VERIFY_PARAM_NOT_NULL(p_sw_id);
if (m_nof_ids == ES_STOPWATCH_MAX_USERS)
{
return NRF_ERROR_INVALID_STATE;
}
if (!m_initialized)
{
return NRF_ERROR_MODULE_NOT_INITIALIZED;
}
*p_sw_id = m_nof_ids;
m_ids_ticks_wrap[m_nof_ids] = ticks_wrap;
m_nof_ids++;
return NRF_SUCCESS;
}
void es_stopwatch_init(void)
{
m_nof_ids = 0;
memset(m_ticks_last_returned, 0, sizeof(m_ticks_last_returned));
memset(m_ids_ticks_wrap, 0, sizeof(m_ids_ticks_wrap));
m_initialized = true;
}

View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef ES_SECURITY_TIMING_H__
#define ES_SECURITY_TIMING_H__
#include <stdint.h>
#include "app_error.h"
/**
* @file
* @addtogroup eddystone_security
* @{
*/
typedef uint8_t es_stopwatch_id_t;
/**@brief Function for getting the number of seconds passed since the last invocation.
* @details If the function returns zero, the 'last time called' state is not updated. If a non-zero value
* is returned, the 'last time called' state will point to the last whole second.
* @return Number of seconds passed since the last invocation.
*/
uint32_t es_stopwatch_check(es_stopwatch_id_t id);
ret_code_t es_stopwatch_create(es_stopwatch_id_t * p_sw_id, uint32_t ticks_wrap);
/**@brief Function for initializing the security timing module.
*/
void es_stopwatch_init(void);
/**
* @}
*/
#endif // ES_SECURITY_TIMING_H__

View File

@@ -0,0 +1,140 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <string.h>
#include "app_timer.h"
#include "es_tlm.h"
#include "es_app_config.h"
#include "es_battery_voltage.h"
#include "es_stopwatch.h"
#include "nrf_soc.h"
#define TICKS_100_MS APP_TIMER_TICKS(100) //!< Tick count for 100ms.
static es_tlm_frame_t m_tlm;
static uint32_t m_le_adv_cnt;
static es_stopwatch_id_t m_time_sec_sw_id;
static es_stopwatch_id_t m_tlm_refresh_sw_id;
/**@brief Function for updating the ADV_SEC field of TLM*/
static void update_time(void)
{
static uint32_t time_total_100_ms = 0;
uint32_t be_time_100_ms; // Big endian version of 0.1 second counter.
time_total_100_ms += es_stopwatch_check(m_time_sec_sw_id);
be_time_100_ms = BYTES_REVERSE_32BIT(time_total_100_ms);
memcpy(m_tlm.sec_cnt, &be_time_100_ms, ES_TLM_SEC_CNT_LENGTH);
}
/**@brief Function for updating the TEMP field of TLM*/
static void update_temp(void)
{
int32_t temp; // variable to hold temp reading
(void)sd_temp_get(&temp); // get new temperature
int16_t temp_new = (int16_t) temp; // convert from int32_t to int16_t
m_tlm.temp[0] = (uint8_t)((temp_new >> 2) & 0xFFUL); // Right-shift by two to remove decimal part
m_tlm.temp[1] = (uint8_t)((temp_new << 6) & 0xFFUL); // Left-shift 6 to get fractional part with 0.25 degrees C resolution
}
/**@brief Function for updating the VBATT field of TLM*/
static void update_vbatt(void)
{
uint16_t vbatt; // Variable to hold voltage reading
es_battery_voltage_get(&vbatt); // Get new battery voltage
m_tlm.vbatt[0] = (uint8_t)(vbatt >> 8);
m_tlm.vbatt[1] = (uint8_t)vbatt;
}
static void update_adv_cnt(void)
{
uint32_t be_adv_cnt = BYTES_REVERSE_32BIT(m_le_adv_cnt);
memcpy(m_tlm.adv_cnt, (uint8_t *)(&be_adv_cnt), ES_TLM_ADV_CNT_LENGTH);
}
void es_tlm_tlm_get(es_tlm_frame_t * p_tlm_frame)
{
// Note that frame type and TLM version fields are set in initialization.
update_time();
update_adv_cnt();
if (es_stopwatch_check(m_tlm_refresh_sw_id) > 0)
{
update_temp();
update_vbatt();
}
memcpy(p_tlm_frame, &m_tlm, sizeof(es_tlm_frame_t));
}
void es_tlm_adv_cnt_inc(void)
{
m_le_adv_cnt++;
}
void es_tlm_init(void)
{
ret_code_t err_code;
memset(&m_tlm, 0, sizeof(m_tlm));
m_tlm.frame_type = ES_FRAME_TYPE_TLM;
m_tlm.version = ES_TLM_VERSION_TLM;
m_le_adv_cnt = 0;
update_time();
update_vbatt();
update_temp();
err_code = es_stopwatch_create(&m_time_sec_sw_id, APP_TIMER_TICKS(100));
APP_ERROR_CHECK(err_code);
err_code = es_stopwatch_create(
&m_tlm_refresh_sw_id,
APP_TIMER_TICKS(APP_CONFIG_TLM_TEMP_VBATT_UPDATE_INTERVAL_SECONDS * 1000));
APP_ERROR_CHECK(err_code);
}

View File

@@ -0,0 +1,76 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef ES_TLM_H__
#define ES_TLM_H__
#include "es.h"
/**
* @file
* @defgroup eddystone_tlm TLM
* @brief Functions for the Eddystone telemetry (TLM) manager.
* @ingroup eddystone_adv
* @{
*/
/**@brief Function for initializing the TLM manager.
*
* @return See @ref app_timer_start for possible return values.
*/
void es_tlm_init(void);
/**@brief Function for getting the current TLM.
*
* @param[in] p_tlm_frame Pointer to the TLM frame to which the frame is retrieved.
*/
void es_tlm_tlm_get(es_tlm_frame_t * p_tlm_frame);
/**@brief Function for incrementing the ADV_CNT field of the TLM frame.
*
* @details This function should be called every time a frame is advertised.
*
*/
void es_tlm_adv_cnt_inc(void);
/**
* @}
*/
#endif // ES_TLM_H__

View File

@@ -0,0 +1,137 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
// See https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms
#ifndef ES_UTIL_H__
#define ES_UTIL_H__
#define BOOL(x) COMPL(NOT(x))
#define IF(c) IIF(BOOL(c))
#define CHECK_N(x, n, ...) n
#define CHECK(...) CHECK_N(__VA_ARGS__, 0,)
#define PROBE(x) x, 1,
#define EAT(...)
#define EXPAND(...) __VA_ARGS__
#define WHEN(c) IF(c)(EXPAND, EAT)
#define NOT(x) CHECK(PRIMITIVE_CAT(NOT_, x))
#define NOT_0 PROBE(~)
#define COMPL(b) PRIMITIVE_CAT(COMPL_, b)
#define COMPL_0 1
#define COMPL_1 0
#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__
#define IIF(c) PRIMITIVE_CAT(IIF_, c)
#define IIF_0(t, ...) __VA_ARGS__
#define IIF_1(t, ...) t
#define DEC(x) PRIMITIVE_CAT(DEC_, x)
#define DEC_0 0
#define DEC_1 0
#define DEC_2 1
#define DEC_3 2
#define DEC_4 3
#define DEC_5 4
#define DEC_6 5
#define DEC_7 6
#define DEC_8 7
#define DEC_9 8
#define DEC_10 9
#define DEC_11 10
#define DEC_12 11
#define DEC_13 12
#define DEC_14 13
#define DEC_15 14
#define DEC_16 15
#define DEC_17 16
#define DEC_18 17
#define DEC_19 18
#define DEC_20 19
#define DEC_21 20
#define DEC_22 21
#define DEC_23 22
#define DEC_24 23
#define DEC_25 24
#define DEC_26 25
#define DEC_27 26
#define DEC_28 27
#define DEC_29 28
#define DEC_30 29
#define DEC_31 30
#define DEC_32 31
#define EMPTY()
#define DEFER(id) id EMPTY()
#define OBSTRUCT(...) __VA_ARGS__ DEFER(EMPTY)()
#define EXPAND(...) __VA_ARGS__
#define EVAL(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
#define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
#define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
#define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
#define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__)))
#define EVAL5(...) __VA_ARGS__
#define REPEAT(count, macro, ...) \
WHEN(count) \
( \
OBSTRUCT(REPEAT_INDIRECT) () \
( \
DEC(count), macro, __VA_ARGS__ \
) \
OBSTRUCT(macro) \
( \
DEC(count), __VA_ARGS__ \
) \
)
#define REPEAT_INDIRECT() REPEAT
/**
* @}
*/
#endif // ES_UTIL_H__

View File

@@ -0,0 +1,438 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <string.h>
#include "nrf_ble_es.h"
#include "app_error.h"
#include "fds.h"
#include "es_adv.h"
#include "es_battery_voltage.h"
#include "es_flash.h"
#include "es_gatts.h"
#include "es_security.h"
#include "es_slot.h"
#include "es_stopwatch.h"
#include "escs_defs.h"
#include "nrf_sdh_ble.h"
static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID; //!< Connection handle.
static nrf_ble_escs_t m_ble_ecs; //!< Struct identifying the Eddystone Config Service.
static nrf_ble_es_evt_handler_t m_evt_handler; //!< Event handler.
uint8_t m_adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET; //!< Advertising handle used to identify an advertising set.
/**@brief Function for invoking registered callback.
*
* @param[in] evt Event to issue to callback.
*/
static void handle_evt(nrf_ble_es_evt_t evt)
{
if (m_evt_handler != NULL)
{
m_evt_handler(evt);
}
}
/**@brief Function resetting MAC address. Will resume advertisement. */
static void new_address_set(void)
{
ret_code_t err_code;
uint8_t bytes_available;
ble_gap_addr_t new_address;
new_address.addr_type = BLE_GAP_ADDR_TYPE_PUBLIC;
// Randomize the MAC address on every EID generation
(void)sd_rand_application_bytes_available_get(&bytes_available);
while (bytes_available < BLE_GAP_ADDR_LEN)
{
// wait for SD to acquire enough RNs
(void)sd_rand_application_bytes_available_get(&bytes_available);
}
(void)sd_rand_application_vector_get(new_address.addr, BLE_GAP_ADDR_LEN);
// Stop advertising to ensure that it is possible to change the address.
(void)sd_ble_gap_adv_stop(m_adv_handle);
do
{
err_code = sd_ble_gap_addr_set(&new_address);
} while (err_code == NRF_ERROR_INVALID_STATE);
APP_ERROR_CHECK(err_code);
if (es_adv_remain_connectable_get())
{
es_adv_start_connectable_adv();
}
else
{
es_adv_start_non_connctable_adv();
}
}
/**@brief Function updating MAC address if required.
*
* @param[in] demand_new_mac If 'true', mac address will be updated on next invocation when not connected.
* If 'false', simply check if we have an outstanding demand for new MAC and update if not connected.
*/
static void check_and_update_mac_address(bool demand_new_mac)
{
static bool reset_mac_address = false;
if (demand_new_mac)
{
reset_mac_address = true;
}
// Not possible to update MAC address while in a connection
if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
{
return;
}
else if (reset_mac_address)
{
reset_mac_address = false;
new_address_set();
}
}
/**@brief Function to lock the beacon (change lock state characteristic to LOCKED)
*/
static void lock_beacon(void)
{
m_ble_ecs.lock_state = NRF_BLE_ESCS_LOCK_STATE_LOCKED;
}
/**@brief Function for handling BLE event from the SoftDevice.
*
* @param[in] p_ble_evt Pointer to BLE event.
*/
static void on_ble_evt(ble_evt_t const * p_ble_evt)
{
ret_code_t err_code;
es_flash_flags_t flash_flag = {{0}};
const es_slot_reg_t * p_reg = es_slot_get_registry();
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
m_conn_handle = p_ble_evt->evt.common_evt.conn_handle;
*(m_ble_ecs.p_active_slot) = 0;
break;
case BLE_GAP_EVT_DISCONNECTED:
m_conn_handle = BLE_CONN_HANDLE_INVALID;
for (uint32_t i = 0; i < APP_MAX_ADV_SLOTS; ++i)
{
err_code = es_slot_write_to_flash(i);
APP_ERROR_CHECK(err_code);
flash_flag.slot_is_empty[i] = !p_reg->slots[i].configured;
}
err_code = es_flash_access_flags(&flash_flag, ES_FLASH_ACCESS_WRITE);
APP_ERROR_CHECK(err_code);
es_flash_beacon_config_t beacon_config;
beacon_config.adv_interval = es_adv_interval_get();
beacon_config.remain_connectable = es_adv_remain_connectable_get();
err_code = es_flash_access_beacon_config(&beacon_config, ES_FLASH_ACCESS_WRITE);
APP_ERROR_CHECK(err_code);
if (m_ble_ecs.lock_state == NRF_BLE_ESCS_LOCK_STATE_UNLOCKED)
{
lock_beacon();
}
check_and_update_mac_address(false);
break;
default:
// No implementation needed.
break;
}
}
/**@brief Callback function to receive messages from the security module
*
* @details Need to be passed in during es_security_init(). The security
* module will callback anytime a particular security process is completed
*
* @params[in] slot_no Index of the slot
* @params[in] msg_type Message type corersponding to different security components
*/
static void nrf_ble_escs_security_cb(uint8_t slot_no, es_security_msg_t msg_type)
{
nrf_ble_escs_eid_id_key_t encrypted_id_key;
nrf_ble_escs_public_ecdh_key_t pub_ecdh_key;
ret_code_t err_code;
static ble_gatts_value_t value;
switch (msg_type)
{
case ES_SECURITY_MSG_UNLOCKED:
m_ble_ecs.lock_state = NRF_BLE_ESCS_LOCK_STATE_UNLOCKED;
break;
case ES_SECURITY_MSG_EID:
es_slot_eid_ready(slot_no);
#ifdef MAC_RANDOMIZED
check_and_update_mac_address(true);
#endif // MAC_RANDOMIZED
break;
case ES_SECURITY_MSG_IK:
es_security_encrypted_eid_id_key_get(slot_no, (uint8_t *)encrypted_id_key.key);
// Set the EID ID key in the slot so it can be exposed in the characteristic
es_slot_encrypted_eid_id_key_set(slot_no, &encrypted_id_key);
break;
case ES_SECURITY_MSG_ECDH:
es_security_pub_ecdh_get(slot_no, (uint8_t *)pub_ecdh_key.key);
// Set the characteristic to the ECDH key value
value.len = sizeof(nrf_ble_escs_public_ecdh_key_t);
value.offset = 0;
value.p_value = (uint8_t *)pub_ecdh_key.key;
if (m_conn_handle != BLE_CONN_HANDLE_INVALID)
{
err_code = sd_ble_gatts_value_set(m_ble_ecs.conn_handle,
m_ble_ecs.pub_ecdh_key_handles.value_handle,
&value);
if (err_code != NRF_SUCCESS)
{
APP_ERROR_CHECK(err_code);
}
}
break;
case ES_SECURITY_MSG_STORE_TIME:
// Every 24 hours any EID slots time is stored to flash to allow for power lock_state_handles
// recovery. Only time needs to be stored, but just store the entire slot anyway for API simplicity.
err_code = es_slot_write_to_flash(slot_no);
APP_ERROR_CHECK(err_code);
break;
default:
APP_ERROR_CHECK(NRF_ERROR_INVALID_PARAM); // Should never happen
break;
}
}
/**@brief Function for handling advertisement events from 'es_adv'.
*
* @param[in] evt Advertisement event to handle.
*/
static void adv_evt_handler(es_adv_evt_t evt)
{
switch (evt)
{
case ES_ADV_EVT_NON_CONN_ADV:
handle_evt(NRF_BLE_ES_EVT_ADVERTISEMENT_SENT);
es_security_update_time();
break;
case ES_ADV_EVT_CONNECTABLE_ADV_STARTED:
handle_evt(NRF_BLE_ES_EVT_CONNECTABLE_ADV_STARTED);
break;
case ES_ADV_EVT_CONNECTABLE_ADV_STOPPED:
handle_evt(NRF_BLE_ES_EVT_CONNECTABLE_ADV_STOPPED);
break;
default:
break;
}
}
/**@brief Initialize the ECS with initial values for the characteristics and other necessary modules */
static void ble_escs_init(void)
{
ret_code_t err_code;
nrf_ble_escs_init_t ecs_init;
nrf_ble_escs_init_params_t init_params;
int8_t tx_powers[ESCS_NUM_OF_SUPPORTED_TX_POWER] = ESCS_SUPPORTED_TX_POWER;
/*Init the broadcast capabilities characteristic*/
memset(&init_params.broadcast_cap, 0, sizeof(init_params.broadcast_cap));
init_params.broadcast_cap.vers_byte = ES_SPEC_VERSION_BYTE;
init_params.broadcast_cap.max_supp_total_slots = APP_MAX_ADV_SLOTS;
init_params.broadcast_cap.max_supp_eid_slots = APP_MAX_EID_SLOTS;
init_params.broadcast_cap.cap_bitfield = ( (APP_IS_VARIABLE_ADV_SUPPORTED << ESCS_BROADCAST_VAR_ADV_SUPPORTED_Pos)
| (APP_IS_VARIABLE_TX_POWER_SUPPORTED << ESCS_BROADCAST_VAR_TX_POWER_SUPPORTED_Pos))
& (ESCS_BROADCAST_VAR_RFU_MASK);
init_params.broadcast_cap.supp_frame_types = ( (APP_IS_URL_SUPPORTED << ESCS_FRAME_TYPE_URL_SUPPORTED_Pos)
| (APP_IS_UID_SUPPORTED << ESCS_FRAME_TYPE_UID_SUPPORTED_Pos)
| (APP_IS_TLM_SUPPORTED << ESCS_FRAME_TYPE_TLM_SUPPORTED_Pos)
| (APP_IS_EID_SUPPORTED << ESCS_FRAME_TYPE_EID_SUPPORTED_Pos))
& (ESCS_FRAME_TYPE_RFU_MASK);
memcpy(init_params.broadcast_cap.supp_radio_tx_power, tx_powers, ESCS_NUM_OF_SUPPORTED_TX_POWER);
init_params.adv_interval = APP_CFG_NON_CONN_ADV_INTERVAL_MS;
init_params.adv_tx_pwr = APP_CFG_DEFAULT_RADIO_TX_POWER;
init_params.radio_tx_pwr = 0x00;
init_params.factory_reset = 0;
init_params.remain_connectable.r_is_non_connectable_supported = APP_IS_REMAIN_CONNECTABLE_SUPPORTED;
// Initialize evt handlers and the service
memset(&ecs_init, 0, sizeof(ecs_init));
ecs_init.write_evt_handler = es_gatts_handle_write;
ecs_init.read_evt_handler = es_gatts_handle_read;
ecs_init.p_init_vals = &(init_params);
err_code = nrf_ble_escs_init(&m_ble_ecs, &ecs_init);
APP_ERROR_CHECK(err_code);
}
/**@brief Function for initializing 'es_adv' module. */
static void adv_init(void)
{
ret_code_t err_code;
es_flash_beacon_config_t beacon_config;
err_code = es_flash_access_beacon_config(&beacon_config, ES_FLASH_ACCESS_READ);
if (err_code == FDS_ERR_NOT_FOUND)
{
beacon_config.adv_interval = APP_CFG_NON_CONN_ADV_INTERVAL_MS;
beacon_config.remain_connectable = false;
}
else
{
APP_ERROR_CHECK(err_code);
}
es_adv_init(m_ble_ecs.uuid_type,
adv_evt_handler,
beacon_config.adv_interval,
beacon_config.remain_connectable,
&m_adv_handle);
}
/**@brief Function for initializing es_slots module. */
static void adv_slots_init(void)
{
uint8_t default_frame_data[DEFAULT_FRAME_LENGTH] = DEFAULT_FRAME_DATA;
es_slot_t default_adv_slot = {.slot_no = 0,
.radio_tx_pwr = DEFAULT_FRAME_TX_POWER,
.adv_frame.type = DEFAULT_FRAME_TYPE,
.adv_frame.length = DEFAULT_FRAME_LENGTH,
.adv_custom_tx_power = false,
.configured = true};
memcpy(&default_adv_slot.adv_frame.frame, default_frame_data, DEFAULT_FRAME_LENGTH);
es_slots_init(&default_adv_slot);
}
void nrf_ble_es_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
ret_code_t err_code;
es_adv_on_ble_evt(p_ble_evt);
err_code = nrf_ble_escs_on_ble_evt(&m_ble_ecs, p_ble_evt);
APP_ERROR_CHECK(err_code);
on_ble_evt(p_ble_evt);
es_flash_on_ble_evt(p_ble_evt);
}
NRF_SDH_BLE_OBSERVER(m_es_observer, NRF_BLE_ES_BLE_OBSERVER_PRIO, nrf_ble_es_on_ble_evt, NULL);
void nrf_ble_es_on_start_connectable_advertising(void)
{
es_adv_start_connectable_adv();
}
void nrf_ble_es_init(nrf_ble_es_evt_handler_t evt_handler)
{
ret_code_t err_code;
m_evt_handler = evt_handler;
m_conn_handle = BLE_CONN_HANDLE_INVALID;
es_stopwatch_init();
err_code = es_gatts_init(&m_ble_ecs);
APP_ERROR_CHECK(err_code);
err_code = es_flash_init();
APP_ERROR_CHECK(err_code);
while (es_flash_num_pending_ops() > 0)
{
; // Busy wait while initialization of FDS module completes
}
err_code = es_security_init(nrf_ble_escs_security_cb);
APP_ERROR_CHECK(err_code);
es_adv_timers_init();
ble_escs_init();
adv_slots_init();
es_battery_voltage_init();
adv_init();
es_adv_start_non_connctable_adv();
}

View File

@@ -0,0 +1,99 @@
/**
* Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef NRF_ES_H__
#define NRF_ES_H__
#include <stdint.h>
#include "ble.h"
/**
* @file
* @defgroup eddystone Eddystone library
* @ingroup app_common
* @{
*
* @brief Library for Eddystone beacons. This library is used in the @ref ble_sdk_app_es.
*
* @note The API documentation is provided for reference only. You should
* not modify this library, and you should not use any functions
* except for the main level functions defined in @c nrf_ble_es.h
* in different contexts.
*/
/** @brief Eddystone event types. */
typedef enum
{
NRF_BLE_ES_EVT_ADVERTISEMENT_SENT, //!< A non-connectable Eddystone frame advertisement was sent.
NRF_BLE_ES_EVT_CONNECTABLE_ADV_STARTED, //!< Advertising in connectable mode was started.
NRF_BLE_ES_EVT_CONNECTABLE_ADV_STOPPED, //!< Advertising in connectable mode was stopped.
} nrf_ble_es_evt_t;
/**@brief Eddystone event handler type. */
typedef void (*nrf_ble_es_evt_handler_t)(nrf_ble_es_evt_t evt);
/**@brief Function for handling the application's BLE stack events.
*
* @details This function handles all events from the BLE stack that are of
* interest to the Eddystone library.
*
* @param[in] p_ble_evt Event received from the BLE stack.
* @param[in] p_context User parameter.
*/
void nrf_ble_es_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context);
/**@brief Function for putting the beacon in connectable mode.
*
* @details This function makes the beacon advertise connectable advertisements.
* If the beacon is in a connected state, the request is ignored.
*/
void nrf_ble_es_on_start_connectable_advertising(void);
/** @brief Function for initializing the Eddystone library.
*
* @param[in] evt_handler Event handler to be called for handling BLE events.
*/
void nrf_ble_es_init(nrf_ble_es_evt_handler_t evt_handler);
/**
* @}
*/
#endif

View File

@@ -0,0 +1,94 @@
/**
* Copyright (c) 2015 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_sdk_srv_ln_common Location and Navigation common defines
* @{
* @ingroup ble_sdk_srv
* @brief Location and Navigation common defines
*
* @details This module contains define values common to LNS and LNCP
*/
#ifndef BLE_LNS_COMMON_H__
#define BLE_LNS_COMMON_H__
#ifdef __cplusplus
extern "C" {
#endif
#define BLE_LNS_INVALID_ROUTE 0xFFFF
#define BLE_LNS_NO_FIX 0xFF
#define BLE_LNS_MAX_NUM_ROUTES 10 /**< The maximum number of routes. This affects memory usage only. */
#define BLE_LNS_MAX_ROUTE_NAME_LEN BLE_GATT_ATT_MTU_DEFAULT - 5 /**< The maximum length of length of a route name. */
#define MAX_CTRL_POINT_RESP_PARAM_LEN BLE_LNS_MAX_ROUTE_NAME_LEN + 3 /**< Maximum length of a control point response. */
// Location and Navigation Service feature bits
#define BLE_LNS_FEATURE_INSTANT_SPEED_SUPPORTED (0x01 << 0) /**< Instaneous Speed Supported bit. */
#define BLE_LNS_FEATURE_TOTAL_DISTANCE_SUPPORTED (0x01 << 1) /**< Total Distance Supported bit. */
#define BLE_LNS_FEATURE_LOCATION_SUPPORTED (0x01 << 2) /**< Location Supported bit. */
#define BLE_LNS_FEATURE_ELEVATION_SUPPORTED (0x01 << 3) /**< Elevation Supported bit. */
#define BLE_LNS_FEATURE_HEADING_SUPPORTED (0x01 << 4) /**< Heading Supported bit. */
#define BLE_LNS_FEATURE_ROLLING_TIME_SUPPORTED (0x01 << 5) /**< Rolling Time Supported bit. */
#define BLE_LNS_FEATURE_UTC_TIME_SUPPORTED (0x01 << 6) /**< UTC Time Supported bit. */
#define BLE_LNS_FEATURE_REMAINING_DISTANCE_SUPPORTED (0x01 << 7) /**< Remaining Distance Supported bit. */
#define BLE_LNS_FEATURE_REMAINING_VERT_DISTANCE_SUPPORTED (0x01 << 8) /**< Remaining Vertical Distance Supported bit. */
#define BLE_LNS_FEATURE_EST_TIME_OF_ARRIVAL_SUPPORTED (0x01 << 9) /**< Estimated Time of Arrival Supported bit. */
#define BLE_LNS_FEATURE_NUM_SATS_IN_SOLUTION_SUPPORTED (0x01 << 10) /**< Number of Satellites in Solution Supported bit. */
#define BLE_LNS_FEATURE_NUM_SATS_IN_VIEW_SUPPORTED (0x01 << 11) /**< Number of Satellites in View Supported bit. */
#define BLE_LNS_FEATURE_TIME_TO_FIRST_FIX_SUPPORTED (0x01 << 12) /**< Time to First Fix Supported bit. */
#define BLE_LNS_FEATURE_EST_HORZ_POS_ERROR_SUPPORTED (0x01 << 13) /**< Estimated Horizontal Position Error Supported bit. */
#define BLE_LNS_FEATURE_EST_VERT_POS_ERROR_SUPPORTED (0x01 << 14) /**< Estimated Vertical Position Error Supported bit. */
#define BLE_LNS_FEATURE_HORZ_DILUTION_OF_PRECISION_SUPPORTED (0x01 << 15) /**< Horizontal Dilution of Precision Supported bit. */
#define BLE_LNS_FEATURE_VERT_DILUTION_OF_PRECISION_SUPPORTED (0x01 << 16) /**< Vertical Dilution of Precision Supported bit. */
#define BLE_LNS_FEATURE_LOC_AND_SPEED_CONTENT_MASKING_SUPPORTED (0x01 << 17) /**< Location and Speed Characteristic Content Masking Supported bit. */
#define BLE_LNS_FEATURE_FIX_RATE_SETTING_SUPPORTED (0x01 << 18) /**< Fix Rate Setting Supported bit. */
#define BLE_LNS_FEATURE_ELEVATION_SETTING_SUPPORTED (0x01 << 19) /**< Elevation Setting Supported bit. */
#define BLE_LNS_FEATURE_POSITION_STATUS_SUPPORTED (0x01 << 20) /**< Position Status Supported bit. */
#ifdef __cplusplus
}
#endif
#endif /* BLE_LNS_COMMON_H__ */
/** @} */

View File

@@ -0,0 +1,806 @@
/**
* Copyright (c) 2015 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "ble_ln_cp.h"
#include "ble_ln_db.h"
#include "ble_ln_common.h"
#include "sdk_common.h"
#define NRF_LOG_MODULE_NAME ble_ln_cp
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();
// Feature Mask bits
#define FEATURE_MASK_INSTANTANEOUS_SPEED (0x01 << 0) /**< Instantaneous Speed mask bit. */
#define FEATURE_MASK_TOTAL_DISTANCE (0x01 << 1) /**< Total Distance mask bit. */
#define FEATURE_MASK_LOCATION (0x01 << 2) /**< Location mask bit. */
#define FEATURE_MASK_ELEVATION (0x01 << 3) /**< Elevation mask bit. */
#define FEATURE_MASK_HEADING (0x01 << 4) /**< Heading mask bit. */
#define FEATURE_MASK_ROLLING_TIME (0x01 << 5) /**< Rolling Time mask bit. */
#define FEATURE_MASK_UTC_TIME (0x01 << 6) /**< UTC Time mask bit. */
// Data Control point parameter type lengths.
#define INT8_LEN 1
#define INT16_LEN 2
#define INT24_LEN 3
#define INT32_LEN 4
#define OPCODE_LENGTH 1 /**< Length of opcode inside Location and Navigation Measurement packet. */
#define HANDLE_LENGTH 2 /**< Length of handle inside Location and Navigation Measurement packet. */
/**@brief Function for interception of GATT errors and @ref nrf_ble_gq errors.
*
* @param[in] nrf_error Error code.
* @param[in] p_ctx Parameter from the event handler.
* @param[in] conn_handle Connection handle.
*/
static void gatt_error_handler(uint32_t nrf_error,
void * p_ctx,
uint16_t conn_handle)
{
ble_lncp_t * p_lncp = (ble_lncp_t *)p_ctx;
if (p_lncp->error_handler != NULL)
{
p_lncp->error_handler(nrf_error);
}
}
static ble_lncp_rsp_code_t notify_app(ble_lncp_t const * p_lncp, ble_lncp_evt_t const * p_evt)
{
ble_lncp_rsp_code_t rsp = LNCP_RSP_SUCCESS;
if (p_lncp->evt_handler != NULL)
{
rsp = p_lncp->evt_handler(p_lncp, p_evt);
}
return rsp;
}
static void resp_send(ble_lncp_t * p_lncp)
{
// Send indication
uint16_t hvx_len;
uint8_t hvx_data[MAX_CTRL_POINT_RESP_PARAM_LEN];
uint32_t err_code;
nrf_ble_gq_req_t lncp_req;
memset(&lncp_req, 0, sizeof(nrf_ble_gq_req_t));
hvx_len = 3 + p_lncp->pending_rsp.rsp_param_len;
hvx_data[0] = LNCP_OP_RESPONSE_CODE;
hvx_data[1] = p_lncp->pending_rsp.op_code;
hvx_data[2] = p_lncp->pending_rsp.rsp_code;
memcpy(&hvx_data[3], &p_lncp->pending_rsp.rsp_param[0], p_lncp->pending_rsp.rsp_param_len);
lncp_req.type = NRF_BLE_GQ_REQ_GATTS_HVX;
lncp_req.error_handler.cb = gatt_error_handler;
lncp_req.error_handler.p_ctx = p_lncp;
lncp_req.params.gatts_hvx.handle = p_lncp->ctrlpt_handles.value_handle;
lncp_req.params.gatts_hvx.offset = 0;
lncp_req.params.gatts_hvx.p_data = hvx_data;
lncp_req.params.gatts_hvx.p_len = &hvx_len;
lncp_req.params.gatts_hvx.type = BLE_GATT_HVX_INDICATION;
p_lncp->is_indication_pending = true;
err_code = nrf_ble_gq_item_add(p_lncp->p_gatt_queue, &lncp_req, p_lncp->conn_handle);
if ((p_lncp->error_handler != NULL) &&
(err_code != NRF_SUCCESS))
{
p_lncp->error_handler(err_code);
}
}
static void on_connect(ble_lncp_t * p_lncp, ble_evt_t const * p_ble_evt)
{
memset(&p_lncp->mask, 0, sizeof(ble_lncp_mask_t));
p_lncp->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
}
static void on_disconnect(ble_lncp_t * p_lncp, ble_evt_t const * p_ble_evt)
{
UNUSED_PARAMETER(p_ble_evt);
p_lncp->conn_handle = BLE_CONN_HANDLE_INVALID;
}
static void on_hvc_confirm(ble_lncp_t * p_lncp, ble_evt_t const * p_ble_evt)
{
if (p_ble_evt->evt.gatts_evt.params.hvc.handle == p_lncp->ctrlpt_handles.value_handle)
{
if (p_lncp->is_indication_pending)
{
p_lncp->is_indication_pending = false;
}
else
{
if (p_lncp->error_handler != NULL)
{
p_lncp->error_handler(NRF_ERROR_INVALID_STATE);
}
}
}
}
/**@brief Handle write events to the control point cccd.
*
* @param[in] p_lncp Location and Navigation Service structure.
* @param[in] p_evt_write Write event received from the BLE stack.
*/
static void on_lncp_cccd_write(ble_lncp_t * p_lncp, ble_gatts_evt_write_t const * p_evt_write)
{
if (p_evt_write->len == BLE_CCCD_VALUE_LEN)
{
// CCCD written, update indications state
p_lncp->is_ctrlpt_indication_enabled = ble_srv_is_indication_enabled(p_evt_write->data);
}
}
/**@brief Handle write events to the navigation cccd.
*
* @param[in] p_lncp Location and Navigation Service structure.
* @param[in] p_evt_write Write event received from the BLE stack.
*/
static void on_nav_cccd_write(ble_lncp_t * p_lncp, ble_gatts_evt_write_t const * p_evt_write)
{
if (p_evt_write->len == BLE_CCCD_VALUE_LEN)
{
// CCCD written, update notification state
p_lncp->is_nav_notification_enabled = ble_srv_is_notification_enabled(p_evt_write->data);
}
}
/**@brief Event handler for control point write.
*
* @param[in] p_lncp Location and Navigation Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_set_cumulative_value(ble_lncp_t * p_lncp, ble_gatts_evt_write_t const * p_evt_write)
{
if ( !(p_lncp->available_features & BLE_LNS_FEATURE_TOTAL_DISTANCE_SUPPORTED) )
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_OP_CODE_NOT_SUPPORTED;
return;
}
if (p_evt_write->len != OPCODE_LENGTH + INT24_LEN)
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_INVALID_PARAMETER;
return;
}
const uint32_t total_distance = uint24_decode(&p_evt_write->data[1]);
const ble_lncp_evt_t evt = {
.evt_type = LNCP_EVT_TOTAL_DISTANCE_SET,
.params.total_distance = total_distance
};
p_lncp->pending_rsp.rsp_code = notify_app(p_lncp, &evt);
if (p_lncp->pending_rsp.rsp_code == LNCP_RSP_SUCCESS)
{
p_lncp->total_distance = total_distance;
}
}
/**@brief Event handler for control point write.
*
* @param[in] p_lncp Location and Navigation Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_mask_loc_speed_content(ble_lncp_t * p_lncp, ble_gatts_evt_write_t const * p_evt_write)
{
if ( !(p_lncp->available_features & BLE_LNS_FEATURE_LOC_AND_SPEED_CONTENT_MASKING_SUPPORTED) )
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_OP_CODE_NOT_SUPPORTED;
return;
}
if (p_evt_write->len != OPCODE_LENGTH + INT16_LEN)
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_INVALID_PARAMETER;
return;
}
uint16_t rcvd_mask = uint16_decode(&p_evt_write->data[1]);
if (rcvd_mask > 0x7F)
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_INVALID_PARAMETER;
return;
}
const ble_lncp_evt_t evt = {
.evt_type = LNCP_EVT_MASK_SET,
.params.mask.flags = rcvd_mask
};
p_lncp->pending_rsp.rsp_code = notify_app(p_lncp, &evt);
if (p_lncp->pending_rsp.rsp_code == LNCP_RSP_SUCCESS)
{
p_lncp->mask.flags = rcvd_mask;
}
}
/**@brief Event handler for control point write.
*
* @param[in] p_lncp Location and Navigation Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_nav_control(ble_lncp_t * p_lncp, ble_gatts_evt_write_t const * p_evt_write)
{
if ( !(p_lncp->is_navigation_present) )
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_OP_CODE_NOT_SUPPORTED;
return;
}
if (p_evt_write->len != LNCP_NAV_CMD_LEN)
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_INVALID_PARAMETER;
return;
}
/*lint --e{415} --e{416} -save suppress Warning 415: possible access out of bond */
const uint8_t data_buf = p_evt_write->data[1];
/*lint -restore*/
if (data_buf > LNCP_NAV_CMD_MAX)
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_INVALID_PARAMETER;
return;
}
const ble_lncp_nav_cmd_t cmd = (ble_lncp_nav_cmd_t) data_buf;
if (cmd == LNCP_CMD_NAV_START || cmd == LNCP_CMD_NAV_CONTINUE || cmd == LNCP_CMD_NAV_NEAREST)
{
p_lncp->is_navigation_running = true;
}
else if (cmd == LNCP_CMD_NAV_STOP || cmd == LNCP_CMD_NAV_PAUSE)
{
p_lncp->is_navigation_running = false;
}
const ble_lncp_evt_t evt = {
.evt_type = LNCP_EVT_NAV_COMMAND,
.params.nav_cmd = cmd
};
p_lncp->pending_rsp.rsp_code = notify_app(p_lncp, &evt);
}
/**@brief Event handler for control point write.
*
* @param[in] p_lncp Location and Navigation Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_req_num_routes(ble_lncp_t * p_lncp, ble_gatts_evt_write_t const * p_evt_write)
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_SUCCESS;
if ( !(p_lncp->is_navigation_present) )
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_OP_CODE_NOT_SUPPORTED;
return;
}
if (p_evt_write->len != OPCODE_LENGTH)
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_INVALID_PARAMETER;
return;
}
const uint8_t num_records = ble_ln_db_num_records_get();
p_lncp->pending_rsp.rsp_param_len = uint16_encode(num_records, &p_lncp->pending_rsp.rsp_param[0]);
}
/**@brief Event handler for control point write.
*
* @param[in] p_lncp Location and Navigation Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_req_name_of_route(ble_lncp_t * p_lncp, ble_gatts_evt_write_t const * p_evt_write)
{
uint8_t * p_name;
uint32_t err_code;
p_lncp->pending_rsp.rsp_code = LNCP_RSP_SUCCESS;
if ( !(p_lncp->is_navigation_present) )
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_OP_CODE_NOT_SUPPORTED;
return;
}
if (p_evt_write->len != OPCODE_LENGTH + INT16_LEN)
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_INVALID_PARAMETER;
return;
}
/*lint --e{415} --e{416} -save suppress Warning 415: possible access out of bond */
const uint16_t route_num = uint16_decode(&p_evt_write->data[1]);
/*lint -restore*/
err_code = ble_ln_db_record_name_get(route_num, &p_name);
if (err_code != NRF_SUCCESS)
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_OPERATION_FAILED;
return;
}
memcpy(&p_lncp->pending_rsp.rsp_param[0], p_name, BLE_LNS_MAX_ROUTE_NAME_LEN);
p_lncp->pending_rsp.rsp_param_len = BLE_LNS_MAX_ROUTE_NAME_LEN;
}
/**@brief Event handler for control point write.
*
* @param[in] p_lncp Location and Navigation Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_select_route(ble_lncp_t * p_lncp, ble_gatts_evt_write_t const * p_evt_write)
{
if ( !(p_lncp->is_navigation_present))
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_OP_CODE_NOT_SUPPORTED;
return;
}
if (p_evt_write->len != OPCODE_LENGTH + INT16_LEN)
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_INVALID_PARAMETER;
return;
}
const uint16_t route_num = uint16_decode(&p_evt_write->data[1]);
const uint16_t stored_num = ble_ln_db_num_records_get();
if (route_num >= stored_num)
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_INVALID_PARAMETER;
return;
}
const ble_lncp_evt_t evt = {
.evt_type = LNCP_EVT_ROUTE_SELECTED,
.params.selected_route = route_num
};
p_lncp->pending_rsp.rsp_code = notify_app(p_lncp, &evt);
if (p_lncp->pending_rsp.rsp_code == LNCP_RSP_SUCCESS)
{
p_lncp->selected_route = route_num;
}
}
/**@brief Event handler for control point write.
*
* @param[in] p_lncp Location and Navigation Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_set_fix_rate(ble_lncp_t * p_lncp, ble_gatts_evt_write_t const * p_evt_write)
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_SUCCESS;
if ( !(p_lncp->available_features & BLE_LNS_FEATURE_FIX_RATE_SETTING_SUPPORTED) )
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_OP_CODE_NOT_SUPPORTED;
return;
}
if (p_evt_write->len != OPCODE_LENGTH + INT8_LEN)
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_INVALID_PARAMETER;
return;
}
/*lint --e{415} --e{416} -save suppress Warning 415: possible access out of bond */
const uint8_t fix_rate = p_evt_write->data[1];
/*lint -restore*/
const ble_lncp_evt_t evt = {
.evt_type = LNCP_EVT_FIX_RATE_SET,
.params.fix_rate = fix_rate
};
p_lncp->pending_rsp.rsp_code = notify_app(p_lncp, &evt);
if (p_lncp->pending_rsp.rsp_code == LNCP_RSP_SUCCESS)
{
p_lncp->fix_rate = fix_rate;
}
}
/**@brief Event handler for control point write.
*
* @param[in] p_lncp Location and Navigation Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_set_elevation(ble_lncp_t * p_lncp, ble_gatts_evt_write_t const * p_evt_write)
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_SUCCESS;
if ( !(p_lncp->available_features & BLE_LNS_FEATURE_ELEVATION_SETTING_SUPPORTED) )
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_OP_CODE_NOT_SUPPORTED;
return;
}
if (p_evt_write->len != OPCODE_LENGTH + INT24_LEN)
{
p_lncp->pending_rsp.rsp_code = LNCP_RSP_INVALID_PARAMETER;
return;
}
const uint32_t elevation = uint24_decode(&p_evt_write->data[1]);
ble_lncp_evt_t evt = {
.evt_type = LNCP_EVT_ELEVATION_SET,
.params.elevation = elevation
};
p_lncp->pending_rsp.rsp_code = notify_app(p_lncp, &evt);
if (p_lncp->pending_rsp.rsp_code == LNCP_RSP_SUCCESS)
{
p_lncp->elevation = elevation;
}
}
/**@brief Handle write events to the Location and Navigation Service Control Point characteristic.
*
* @param[in] p_lncp Location and Navigation Service structure.
* @param[in] p_evt_write Write event received from the BLE stack.
*/
static void on_ctrlpt_write(ble_lncp_t * p_lncp, ble_gatts_evt_write_t const * p_evt_write)
{
uint32_t err_code;
p_lncp->pending_rsp.rsp_param_len = 0;
ble_gatts_rw_authorize_reply_params_t write_authorize_reply;
memset(&write_authorize_reply, 0, sizeof(write_authorize_reply));
write_authorize_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE;
if (p_lncp->is_ctrlpt_indication_enabled)
{
if (!p_lncp->is_indication_pending)
{
write_authorize_reply.params.write.update = 1;
write_authorize_reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
// if the op code is navigation control, its cccd must be checked
if (p_evt_write->len > 0 && p_lncp->is_navigation_present)
{
if ( p_evt_write->data[0] == LNCP_OP_NAV_CONTROL
|| p_evt_write->data[0] == LNCP_OP_REQ_NAME_OF_ROUTE
|| p_evt_write->data[0] == LNCP_OP_REQ_NUM_ROUTES)
{
if (!p_lncp->is_nav_notification_enabled)
{
write_authorize_reply.params.write.gatt_status = BLE_GATT_STATUS_ATTERR_CPS_CCCD_CONFIG_ERROR;
}
}
}
}
else
{
write_authorize_reply.params.write.gatt_status = BLE_GATT_STATUS_ATTERR_CPS_PROC_ALR_IN_PROG;
}
}
else
{
write_authorize_reply.params.write.gatt_status = BLE_GATT_STATUS_ATTERR_CPS_CCCD_CONFIG_ERROR;
}
// reply to the write authorization
do {
err_code = sd_ble_gatts_rw_authorize_reply(p_lncp->conn_handle, &write_authorize_reply);
if (err_code != NRF_SUCCESS && err_code != NRF_ERROR_BUSY)
{
if (p_lncp->error_handler != NULL)
{
p_lncp->error_handler(err_code);
}
}
} while (err_code == NRF_ERROR_BUSY);
if (write_authorize_reply.params.write.gatt_status != BLE_GATT_STATUS_SUCCESS)
{
return;
}
// Start executing the control point write action
if (p_evt_write->len > 0)
{
p_lncp->pending_rsp.op_code = (ble_lncp_op_code_t) p_evt_write->data[0];
switch (p_lncp->pending_rsp.op_code)
{
case LNCP_OP_SET_CUMULATIVE_VALUE:
on_set_cumulative_value(p_lncp, p_evt_write);
break;
case LNCP_OP_MASK_LOC_SPEED_CONTENT:
on_mask_loc_speed_content(p_lncp, p_evt_write);
break;
case LNCP_OP_NAV_CONTROL:
on_nav_control(p_lncp, p_evt_write);
break;
case LNCP_OP_REQ_NUM_ROUTES:
on_req_num_routes(p_lncp, p_evt_write);
break;
case LNCP_OP_REQ_NAME_OF_ROUTE:
on_req_name_of_route(p_lncp, p_evt_write);
break;
case LNCP_OP_SELECT_ROUTE:
on_select_route(p_lncp, p_evt_write);
break;
case LNCP_OP_SET_FIX_RATE:
on_set_fix_rate(p_lncp, p_evt_write);
break;
case LNCP_OP_SET_ELEVATION:
on_set_elevation(p_lncp, p_evt_write);
break;
// Unrecognized Op Code
default:
p_lncp->pending_rsp.rsp_code = LNCP_RSP_OP_CODE_NOT_SUPPORTED;
break;
}
resp_send(p_lncp);
}
}
/**@brief Write authorization request event handler.
*
* @details The write authorization request event handler is only called when writing to the control point.
*
* @param[in] p_lncp Location and Navigation Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_rw_authorize_req(ble_lncp_t * p_lncp, ble_evt_t const * p_ble_evt)
{
const ble_gatts_evt_rw_authorize_request_t * p_auth_req =
&p_ble_evt->evt.gatts_evt.params.authorize_request;
if (
(p_auth_req->type == BLE_GATTS_AUTHORIZE_TYPE_WRITE)
&&
(p_auth_req->request.write.handle == p_lncp->ctrlpt_handles.value_handle)
&&
(p_auth_req->request.write.op != BLE_GATTS_OP_PREP_WRITE_REQ)
&&
(p_auth_req->request.write.op != BLE_GATTS_OP_EXEC_WRITE_REQ_NOW)
&&
(p_auth_req->request.write.op != BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL)
)
{
on_ctrlpt_write(p_lncp, &p_auth_req->request.write);
}
}
/**@brief Write event handler.
*
* @param[in] p_lncp Location and Navigation Service structure.
* @param[in] p_ble_evt Event received from the BLE stack.
*/
static void on_write(ble_lncp_t * p_lncp, ble_evt_t const * p_ble_evt)
{
const ble_gatts_evt_write_t * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
if (p_evt_write->handle == p_lncp->ctrlpt_handles.cccd_handle)
{
on_lncp_cccd_write(p_lncp, p_evt_write);
}
else if (p_evt_write->handle == p_lncp->navigation_handles.cccd_handle)
{
on_nav_cccd_write(p_lncp, p_evt_write);
}
}
void ble_lncp_on_ble_evt(ble_lncp_t * p_lncp, ble_evt_t const * p_ble_evt)
{
VERIFY_PARAM_NOT_NULL_VOID(p_lncp);
VERIFY_PARAM_NOT_NULL_VOID(p_ble_evt);
switch (p_ble_evt->header.evt_id)
{
case BLE_GAP_EVT_CONNECTED:
on_connect(p_lncp, p_ble_evt);
break;
case BLE_GAP_EVT_DISCONNECTED:
if (p_ble_evt->evt.gap_evt.conn_handle == p_lncp->conn_handle)
{
on_disconnect(p_lncp, p_ble_evt);
}
break;
case BLE_GATTS_EVT_WRITE:
if (p_ble_evt->evt.gatts_evt.conn_handle == p_lncp->conn_handle)
{
on_write(p_lncp, p_ble_evt);
}
break;
case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
if (p_ble_evt->evt.gatts_evt.conn_handle == p_lncp->conn_handle)
{
on_rw_authorize_req(p_lncp, p_ble_evt);
}
break;
case BLE_GATTS_EVT_HVC:
if (p_ble_evt->evt.gatts_evt.conn_handle == p_lncp->conn_handle)
{
on_hvc_confirm(p_lncp, p_ble_evt);
}
break;
default:
// no implementation
break;
}
}
uint32_t ble_lncp_total_distance_get(ble_lncp_t const * p_lncp)
{
if (p_lncp == NULL)
{
return 0;
}
return p_lncp->total_distance;
}
uint32_t ble_lncp_elevation_get(ble_lncp_t const * p_lncp)
{
if (p_lncp == NULL)
{
return 0;
}
return p_lncp->elevation;
}
ble_lncp_mask_t ble_lncp_mask_get(ble_lncp_t const * p_lncp)
{
if (p_lncp == NULL)
{
const ble_lncp_mask_t empty_mask = {0};
return empty_mask;
}
return p_lncp->mask;
}
bool ble_lncp_is_navigation_running(ble_lncp_t const * p_lncp)
{
if (p_lncp == NULL)
{
return false;
}
return p_lncp->is_navigation_running;
}
ret_code_t ble_lncp_init(ble_lncp_t * p_lncp, ble_lncp_init_t const * p_lncp_init)
{
VERIFY_PARAM_NOT_NULL(p_lncp);
VERIFY_PARAM_NOT_NULL(p_lncp_init);
VERIFY_PARAM_NOT_NULL(p_lncp_init->p_gatt_queue);
ble_add_char_params_t add_char_params;
memset(&add_char_params, 0, sizeof(add_char_params));
p_lncp->service_handle = p_lncp_init->service_handle;
p_lncp->evt_handler = p_lncp_init->evt_handler;
p_lncp->error_handler = p_lncp_init->error_handler;
p_lncp->p_gatt_queue = p_lncp_init->p_gatt_queue;
p_lncp->available_features = p_lncp_init->available_features;
p_lncp->is_position_quality_present = p_lncp_init->is_position_quality_present;
p_lncp->is_navigation_present = p_lncp_init->is_navigation_present;
p_lncp->total_distance = p_lncp_init->total_distance;
p_lncp->elevation = p_lncp_init->elevation;
p_lncp->navigation_handles = p_lncp_init->navigation_handles;
p_lncp->fix_rate = BLE_LNS_NO_FIX;
p_lncp->selected_route = BLE_LNS_INVALID_ROUTE;
p_lncp->conn_handle = BLE_CONN_HANDLE_INVALID;
p_lncp->is_navigation_running = false;
p_lncp->is_nav_notification_enabled = false;
p_lncp->is_ctrlpt_indication_enabled = false;
memset(&p_lncp->mask, 0, sizeof(ble_lncp_mask_t));
add_char_params.uuid = BLE_UUID_LN_CONTROL_POINT_CHAR;
add_char_params.max_len = 0;
add_char_params.char_props.indicate = true;
add_char_params.char_props.write = true;
add_char_params.is_defered_write = true;
add_char_params.is_var_len = true;
add_char_params.max_len = BLE_GATT_ATT_MTU_DEFAULT;
add_char_params.write_access = p_lncp_init->write_perm;
add_char_params.cccd_write_access = p_lncp_init->cccd_write_perm;
NRF_LOG_DEBUG("Initialized");
return characteristic_add(p_lncp->service_handle,
&add_char_params,
&p_lncp->ctrlpt_handles);
}

View File

@@ -0,0 +1,250 @@
/**
* Copyright (c) 2015 - 2020, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup ble_sdk_srv_lncp Location and Navigation Service Control Point
* @{
* @ingroup ble_sdk_srv
* @brief Location and Navigation Service Control Point module
*
* @details This module implements the Location and Navigation Service Control Point behavior.
*/
#ifndef BLE_LN_CTRLPT_H__
#define BLE_LN_CTRLPT_H__
#include "ble_srv_common.h"
#include "sdk_common.h"
#include "nrf_ble_gq.h"
#ifdef __cplusplus
extern "C" {
#endif
#define BLE_LNS_MAX_ROUTE_NAME_LEN BLE_GATT_ATT_MTU_DEFAULT - 5 /**< The maximum length of length of a route name. */
#define MAX_CTRL_POINT_RESP_PARAM_LEN BLE_LNS_MAX_ROUTE_NAME_LEN + 3 /**< Maximum length of a control point response. */
typedef struct ble_lncp_s ble_lncp_t;
/** @brief Location and Navigation event type. This list defines the possible events types from the Location and Navigation Service. */
typedef enum
{
LNCP_EVT_ELEVATION_SET, /**< Location and Navigation elevation was set. */
LNCP_EVT_FIX_RATE_SET, /**< Fix rate was set. */
LNCP_EVT_ROUTE_SELECTED, /**< A route was selected. */
LNCP_EVT_NAV_COMMAND, /**< A navigation command was issued. */
LNCP_EVT_MASK_SET, /**< Location and Speed feature mask was set. */
LNCP_EVT_TOTAL_DISTANCE_SET /**< Location and Navigation total distance was set. */
} ble_lncp_evt_type_t;
/** @brief Navigation commands. These commands can be sent to the control point and returned by an event callback. */
typedef enum
{
LNCP_CMD_NAV_STOP = 0x00, /**< When received, is_navigation_running in @ref ble_lns_s will be set to false. */
LNCP_CMD_NAV_START = 0x01, /**< When received, is_navigation_running in @ref ble_lns_s will be set to true. */
LNCP_CMD_NAV_PAUSE = 0x02, /**< When received, is_navigation_running in @ref ble_lns_s will be set to false. */
LNCP_CMD_NAV_CONTINUE = 0x03, /**< When received, is_navigation_running in @ref ble_lns_s will be set to true. */
LNCP_CMD_NAV_SKIP_WAYPOINT = 0x04, /**< When received, is_navigation_running in @ref ble_lns_s will not be affected. */
LNCP_CMD_NAV_NEAREST = 0x05, /**< When received, is_navigation_running in @ref ble_lns_s will be set to true. */
} ble_lncp_nav_cmd_t;
#define LNCP_NAV_CMD_MAX 0x05
#define LNCP_NAV_CMD_LEN (OPCODE_LENGTH + 1)
#if defined(__CC_ARM)
#pragma push
#pragma anon_unions
#elif defined(__ICCARM__)
#pragma language=extended
#elif defined(__GNUC__)
/* anonymous unions are enabled by default */
#endif
/** @brief A mask can be used to temporarily enable and disable features of the Location and Speed characteristic.*/
typedef union
{
uint8_t flags;
struct
{
uint8_t instantaneous_speed :1;
uint8_t total_distance :1;
uint8_t location :1;
uint8_t elevation :1;
uint8_t heading :1;
uint8_t rolling_time :1;
uint8_t utc_time :1;
};
} ble_lncp_mask_t;
#if defined(__CC_ARM)
#pragma pop
#elif defined(__ICCARM__)
/* leave anonymous unions enabled */
#elif defined(__GNUC__)
/* anonymous unions are enabled by default */
#endif
typedef struct
{
ble_lncp_evt_type_t evt_type;
union
{
ble_lncp_mask_t mask;
ble_lncp_nav_cmd_t nav_cmd;
uint32_t total_distance;
uint8_t fix_rate;
uint16_t selected_route;
uint32_t elevation;
} params;
} ble_lncp_evt_t;
// Location and Navigation Control Point response values
typedef enum
{
LNCP_RSP_RESERVED = 0x00, /**< Reserved for future use. */
LNCP_RSP_SUCCESS = 0x01, /**< Success. */
LNCP_RSP_OP_CODE_NOT_SUPPORTED = 0x02, /**< Op Code not supported. */
LNCP_RSP_INVALID_PARAMETER = 0x03, /**< Invalid Parameter. */
LNCP_RSP_OPERATION_FAILED = 0x04, /**< Operation Failed. */
} ble_lncp_rsp_code_t;
typedef ble_lncp_rsp_code_t (*ble_lncp_evt_handler_t) (ble_lncp_t const * p_lncp, ble_lncp_evt_t const * p_evt);
// Location and Navigation Control Point Op Code values
typedef enum
{
LNCP_OP_RESERVED = 0x00, /**< Reserved for future use. */
LNCP_OP_SET_CUMULATIVE_VALUE = 0x01, /**< Set Cumulative Value. */
LNCP_OP_MASK_LOC_SPEED_CONTENT = 0x02, /**< Mask Location and Speed Characteristic Content. */
LNCP_OP_NAV_CONTROL = 0x03, /**< Navigation Control. */
LNCP_OP_REQ_NUM_ROUTES = 0x04, /**< Request Number of Routes. */
LNCP_OP_REQ_NAME_OF_ROUTE = 0x05, /**< Request Name of Route. */
LNCP_OP_SELECT_ROUTE = 0x06, /**< Select Route. */
LNCP_OP_SET_FIX_RATE = 0x07, /**< Set Fix Rate. */
LNCP_OP_SET_ELEVATION = 0x08, /**< Set Elevation. */
LNCP_OP_RESPONSE_CODE = 0x20 /**< Response code. */
} ble_lncp_op_code_t;
/** @brief Information included in a control point write response indication. */
typedef struct
{
ble_lncp_op_code_t op_code; /**< Opcode of the control point write action. */
ble_lncp_rsp_code_t rsp_code; /**< Response code of the control point write action. */
uint8_t rsp_param_len;
uint8_t rsp_param[MAX_CTRL_POINT_RESP_PARAM_LEN];
} ble_lncp_rsp_t;
typedef struct
{
uint16_t service_handle;
ble_lncp_evt_handler_t evt_handler;
ble_srv_error_handler_t error_handler;
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to BLE GATT queue instance. */
uint32_t available_features; /**< Value of the LN feature. */
bool is_position_quality_present; /**< If set to true, the position quality characteristic will be added. Else not. */
bool is_control_point_present; /**< If set to true, the control point characteristic will be added. Else not. */
bool is_navigation_present; /**< If set to true, the navigation characteristic will be added. Else not. */
ble_gatts_char_handles_t navigation_handles;
uint32_t total_distance;
uint32_t elevation;
security_req_t write_perm;
security_req_t cccd_write_perm;
} ble_lncp_init_t;
struct ble_lncp_s
{
uint16_t conn_handle;
uint16_t service_handle;
ble_gatts_char_handles_t ctrlpt_handles;
ble_gatts_char_handles_t navigation_handles;
ble_lncp_evt_handler_t evt_handler;
ble_srv_error_handler_t error_handler;
ble_lncp_rsp_t pending_rsp;
nrf_ble_gq_t * p_gatt_queue; /**< Pointer to BLE GATT queue instance. */
ble_lncp_mask_t mask;
uint32_t total_distance;
uint32_t elevation;
uint8_t fix_rate;
uint16_t selected_route;
uint32_t available_features; /**< Value of the LN feature. */
bool is_position_quality_present; /**< If set to true, the position quality characteristic will be added. Else not. */
bool is_control_point_present; /**< If set to true, the control point characteristic will be added. Else not. */
bool is_navigation_present; /**< If set to true, the navigation characteristic will be added. Else not. */
bool is_navigation_running; /**< This variable can be set using the control point. Must be true to be able to send navigation updates. */
bool is_ctrlpt_indication_enabled; /**< True if indication is enabled on the Control Point characteristic. */
bool is_nav_notification_enabled; /**< True if notification is enabled on the Navigation characteristic. */
bool is_indication_pending; /**< True if Control Point indication is pending. */
};
void ble_lncp_on_ble_evt(ble_lncp_t * p_lncp, ble_evt_t const * p_ble_evt);
uint32_t ble_lncp_total_distance_get(ble_lncp_t const * p_lncp);
uint32_t ble_lncp_elevation_get(ble_lncp_t const * p_lncp);
ble_lncp_mask_t ble_lncp_mask_get(ble_lncp_t const * p_lncp);
bool ble_lncp_is_navigation_running(ble_lncp_t const * p_lncp);
ret_code_t ble_lncp_init(ble_lncp_t * p_lncp, ble_lncp_init_t const * p_lncp_init);
#ifdef __cplusplus
}
#endif
#endif //BLE_LN_CTRLPT_H__
/** @} */

Some files were not shown because too many files have changed in this diff Show More