|
|
@ -5,55 +5,35 @@ |
|
|
|
#include "nrf_log.h" |
|
|
|
#include "sdk_common.h" |
|
|
|
|
|
|
|
#define BLE_UUID_NUS_TX_CHARACTERISTIC 0x0003 /**< The UUID of the TX Characteristic. */ |
|
|
|
#define BLE_UUID_NUS_RX_CHARACTERISTIC 0x0002 /**< The UUID of the RX Characteristic. */ |
|
|
|
#define ZDATACHANNEL_UUID_NUS_SERVICE 0x0001 /**< The UUID of the Nordic UART Service. */ |
|
|
|
#define ZDATACHANNEL_TX_CHARACTERISTIC 0x0003 /**< The UUID of the TX Characteristic. */ |
|
|
|
#define ZDATACHANNEL_RX_CHARACTERISTIC 0x0002 /**< The UUID of the RX Characteristic. */ |
|
|
|
#define ZDATACHANNEL_DATABLOCK_TX_CHARACTERISTIC 0x0004 /**< The UUID of the RX Characteristic. */ |
|
|
|
|
|
|
|
#define ZDATACANNEL_MAX_RX_CHAR_LEN ZDATACANNEL_MAX_DATA_LEN /**< Maximum length of the RX Characteristic (in bytes). */ |
|
|
|
#define ZDATACANNEL_MAX_TX_CHAR_LEN ZDATACANNEL_MAX_DATA_LEN /**< Maximum length of the TX Characteristic (in bytes). */ |
|
|
|
#define ZDATACHANNEL_MAX_RX_CHAR_LEN ZDATACHANNEL_MAX_DATA_LEN /**< Maximum length of the RX Characteristic (in bytes). */ |
|
|
|
#define ZDATACHANNEL_MAX_TX_CHAR_LEN ZDATACHANNEL_MAX_DATA_LEN /**< Maximum length of the TX Characteristic (in bytes). */ |
|
|
|
#define ZDATACHANNEL_DATABLOCK_TX_CHAR_LEN ZDATACHANNEL_MAX_DATA_LEN /**< Maximum length of the TX Characteristic (in bytes). */ |
|
|
|
|
|
|
|
#define NUS_BASE_UUID \ |
|
|
|
{ \ |
|
|
|
{ 0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, 0x93, 0xF3, 0xA3, 0xB5, 0x00, 0x00, 0x40, 0x6E } \ |
|
|
|
} /**< Used vendor specific UUID. */ |
|
|
|
|
|
|
|
/**@brief Function for handling the @ref BLE_GAP_EVT_CONNECTED event from the SoftDevice. |
|
|
|
* |
|
|
|
* @param[in] p_nus Nordic UART Service structure. |
|
|
|
* @param[in] p_ble_evt Pointer to the event received from BLE stack. |
|
|
|
*/ |
|
|
|
static void on_connect(zdatachannel_t *p_nus, ble_evt_t const *p_ble_evt) { |
|
|
|
ret_code_t err_code; |
|
|
|
zdatachannel_evt_t evt; |
|
|
|
ble_gatts_value_t gatts_val; |
|
|
|
uint8_t cccd_value[2]; |
|
|
|
zdatachannel_client_context_t *p_client = NULL; |
|
|
|
|
|
|
|
err_code = blcm_link_ctx_get(p_nus->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); |
|
|
|
} |
|
|
|
static zdatachannel_t *p_datachannel; |
|
|
|
|
|
|
|
/* Check the hosts CCCD value to inform of readiness to send data using the RX characteristic */ |
|
|
|
static bool notification_enable(uint16_t conn_handle, uint16_t cccd_handle) { |
|
|
|
ret_code_t err_code; |
|
|
|
ble_gatts_value_t gatts_val; |
|
|
|
uint8_t cccd_value[2]; |
|
|
|
memset(&gatts_val, 0, sizeof(ble_gatts_value_t)); |
|
|
|
gatts_val.p_value = cccd_value; |
|
|
|
gatts_val.len = sizeof(cccd_value); |
|
|
|
gatts_val.offset = 0; |
|
|
|
|
|
|
|
err_code = sd_ble_gatts_value_get(p_ble_evt->evt.gap_evt.conn_handle, p_nus->tx_handles.cccd_handle, &gatts_val); |
|
|
|
|
|
|
|
if ((err_code == NRF_SUCCESS) && (p_nus->data_handler != NULL) && ble_srv_is_notification_enabled(gatts_val.p_value)) { |
|
|
|
if (p_client != NULL) { |
|
|
|
p_client->is_notification_enabled = true; |
|
|
|
} |
|
|
|
|
|
|
|
memset(&evt, 0, sizeof(zdatachannel_evt_t)); |
|
|
|
evt.type = ZDATACANNEL_EVT_COMM_STARTED; |
|
|
|
evt.p_nus = p_nus; |
|
|
|
evt.conn_handle = p_ble_evt->evt.gap_evt.conn_handle; |
|
|
|
evt.p_link_ctx = p_client; |
|
|
|
|
|
|
|
p_nus->data_handler(&evt); |
|
|
|
err_code = sd_ble_gatts_value_get(conn_handle, cccd_handle, &gatts_val); |
|
|
|
if ((err_code == NRF_SUCCESS) && ble_srv_is_notification_enabled(gatts_val.p_value)) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
/**@brief Function for handling the @ref BLE_GATTS_EVT_WRITE event from the SoftDevice. |
|
|
@ -62,37 +42,25 @@ static void on_connect(zdatachannel_t *p_nus, ble_evt_t const *p_ble_evt) { |
|
|
|
* @param[in] p_ble_evt Pointer to the event received from BLE stack. |
|
|
|
*/ |
|
|
|
static void on_write(zdatachannel_t *p_nus, ble_evt_t const *p_ble_evt) { |
|
|
|
ret_code_t err_code; |
|
|
|
zdatachannel_evt_t evt; |
|
|
|
zdatachannel_client_context_t *p_client; |
|
|
|
ble_gatts_evt_write_t const *p_evt_write = &p_ble_evt->evt.gatts_evt.params.write; |
|
|
|
|
|
|
|
err_code = blcm_link_ctx_get(p_nus->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); |
|
|
|
} |
|
|
|
zdatachannel_evt_t evt; |
|
|
|
ble_gatts_evt_write_t const *p_evt_write = &p_ble_evt->evt.gatts_evt.params.write; |
|
|
|
|
|
|
|
memset(&evt, 0, sizeof(zdatachannel_evt_t)); |
|
|
|
evt.p_nus = p_nus; |
|
|
|
evt.conn_handle = p_ble_evt->evt.gatts_evt.conn_handle; |
|
|
|
evt.p_link_ctx = p_client; |
|
|
|
|
|
|
|
if ((p_evt_write->handle == p_nus->tx_handles.cccd_handle) && (p_evt_write->len == 2)) { |
|
|
|
if (p_client != NULL) { |
|
|
|
if (ble_srv_is_notification_enabled(p_evt_write->data)) { |
|
|
|
p_client->is_notification_enabled = true; |
|
|
|
evt.type = ZDATACANNEL_EVT_COMM_STARTED; |
|
|
|
} else { |
|
|
|
p_client->is_notification_enabled = false; |
|
|
|
evt.type = ZDATACANNEL_EVT_COMM_STOPPED; |
|
|
|
} |
|
|
|
|
|
|
|
if (p_nus->data_handler != NULL) { |
|
|
|
p_nus->data_handler(&evt); |
|
|
|
} |
|
|
|
|
|
|
|
if ((p_evt_write->handle == p_nus->cmd_tx_handles.cccd_handle) && (p_evt_write->len == 2)) { |
|
|
|
if (ble_srv_is_notification_enabled(p_evt_write->data)) { |
|
|
|
p_nus->cmd_tx_channel_is_notification_enabled = true; |
|
|
|
} else { |
|
|
|
p_nus->cmd_tx_channel_is_notification_enabled = false; |
|
|
|
} |
|
|
|
} else if ((p_evt_write->handle == p_nus->datablock_tx_handles.cccd_handle) && (p_evt_write->len == 2)) { |
|
|
|
if (ble_srv_is_notification_enabled(p_evt_write->data)) { |
|
|
|
p_nus->datablock_tx_channel_is_notification_enabled = true; |
|
|
|
} else { |
|
|
|
p_nus->datablock_tx_channel_is_notification_enabled = false; |
|
|
|
} |
|
|
|
} else if ((p_evt_write->handle == p_nus->rx_handles.value_handle) && (p_nus->data_handler != NULL)) { |
|
|
|
evt.type = ZDATACANNEL_EVT_RX_DATA; |
|
|
|
} else if ((p_evt_write->handle == p_nus->cmd_tx_handles.value_handle) && (p_nus->data_handler != NULL)) { |
|
|
|
evt.type = ZDATACHANNEL_EVT_RX_DATA; |
|
|
|
evt.params.rx_data.p_data = p_evt_write->data; |
|
|
|
evt.params.rx_data.length = p_evt_write->len; |
|
|
|
|
|
|
@ -102,33 +70,6 @@ static void on_write(zdatachannel_t *p_nus, ble_evt_t const *p_ble_evt) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/**@brief Function for handling the @ref BLE_GATTS_EVT_HVN_TX_COMPLETE event from the SoftDevice. |
|
|
|
* |
|
|
|
* @param[in] p_nus Nordic UART Service structure. |
|
|
|
* @param[in] p_ble_evt Pointer to the event received from BLE stack. |
|
|
|
*/ |
|
|
|
static void on_hvx_tx_complete(zdatachannel_t *p_nus, ble_evt_t const *p_ble_evt) { |
|
|
|
ret_code_t err_code; |
|
|
|
zdatachannel_evt_t evt; |
|
|
|
zdatachannel_client_context_t *p_client; |
|
|
|
|
|
|
|
err_code = blcm_link_ctx_get(p_nus->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); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if ((p_client->is_notification_enabled) && (p_nus->data_handler != NULL)) { |
|
|
|
memset(&evt, 0, sizeof(zdatachannel_evt_t)); |
|
|
|
evt.type = ZDATACANNEL_EVT_TX_RDY; |
|
|
|
evt.p_nus = p_nus; |
|
|
|
evt.conn_handle = p_ble_evt->evt.gatts_evt.conn_handle; |
|
|
|
evt.p_link_ctx = p_client; |
|
|
|
|
|
|
|
p_nus->data_handler(&evt); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void zdatachannel_on_ble_evt(ble_evt_t const *p_ble_evt, void *p_context) { |
|
|
|
if ((p_context == NULL) || (p_ble_evt == NULL)) { |
|
|
|
return; |
|
|
@ -138,17 +79,16 @@ void zdatachannel_on_ble_evt(ble_evt_t const *p_ble_evt, void *p_context) { |
|
|
|
|
|
|
|
switch (p_ble_evt->header.evt_id) { |
|
|
|
case BLE_GAP_EVT_CONNECTED: |
|
|
|
on_connect(p_nus, p_ble_evt); |
|
|
|
p_nus->conn_handle = p_ble_evt->evt.gap_evt.conn_handle; |
|
|
|
p_nus->cmd_tx_channel_is_notification_enabled = notification_enable(p_ble_evt->evt.gap_evt.conn_handle, p_nus->cmd_tx_handles.cccd_handle); |
|
|
|
p_nus->datablock_tx_channel_is_notification_enabled = notification_enable(p_ble_evt->evt.gap_evt.conn_handle, p_nus->cmd_tx_handles.cccd_handle); |
|
|
|
break; |
|
|
|
case BLE_GAP_EVT_DISCONNECTED: |
|
|
|
p_nus->conn_handle = BLE_CONN_HANDLE_INVALID; |
|
|
|
break; |
|
|
|
|
|
|
|
case BLE_GATTS_EVT_WRITE: |
|
|
|
on_write(p_nus, p_ble_evt); |
|
|
|
break; |
|
|
|
|
|
|
|
case BLE_GATTS_EVT_HVN_TX_COMPLETE: |
|
|
|
on_hvx_tx_complete(p_nus, p_ble_evt); |
|
|
|
break; |
|
|
|
|
|
|
|
default: |
|
|
|
// No implementation needed. |
|
|
|
break; |
|
|
@ -156,6 +96,7 @@ void zdatachannel_on_ble_evt(ble_evt_t const *p_ble_evt, void *p_context) { |
|
|
|
} |
|
|
|
|
|
|
|
uint32_t zdatachannel_init(zdatachannel_t *p_nus, zdatachannel_init_t const *p_nus_init) { |
|
|
|
p_datachannel = p_nus; |
|
|
|
ret_code_t err_code; |
|
|
|
ble_uuid_t ble_uuid; |
|
|
|
ble_uuid128_t nus_base_uuid = NUS_BASE_UUID; |
|
|
@ -173,7 +114,7 @@ uint32_t zdatachannel_init(zdatachannel_t *p_nus, zdatachannel_init_t const *p_n |
|
|
|
VERIFY_SUCCESS(err_code); |
|
|
|
|
|
|
|
ble_uuid.type = p_nus->uuid_type; |
|
|
|
ble_uuid.uuid = BLE_UUID_NUS_SERVICE; |
|
|
|
ble_uuid.uuid = ZDATACHANNEL_UUID_NUS_SERVICE; |
|
|
|
|
|
|
|
// Add the service. |
|
|
|
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_nus->service_handle); |
|
|
@ -182,28 +123,25 @@ uint32_t zdatachannel_init(zdatachannel_t *p_nus, zdatachannel_init_t const *p_n |
|
|
|
|
|
|
|
// Add the RX Characteristic. |
|
|
|
memset(&add_char_params, 0, sizeof(add_char_params)); |
|
|
|
add_char_params.uuid = BLE_UUID_NUS_RX_CHARACTERISTIC; |
|
|
|
add_char_params.uuid = ZDATACHANNEL_RX_CHARACTERISTIC; |
|
|
|
add_char_params.uuid_type = p_nus->uuid_type; |
|
|
|
add_char_params.max_len = ZDATACANNEL_MAX_RX_CHAR_LEN; |
|
|
|
add_char_params.max_len = ZDATACHANNEL_MAX_RX_CHAR_LEN; |
|
|
|
add_char_params.init_len = sizeof(uint8_t); |
|
|
|
add_char_params.is_var_len = true; |
|
|
|
add_char_params.char_props.write = 1; |
|
|
|
add_char_params.char_props.write_wo_resp = 1; |
|
|
|
|
|
|
|
add_char_params.read_access = SEC_OPEN; |
|
|
|
add_char_params.read_access = SEC_NO_ACCESS; |
|
|
|
add_char_params.write_access = SEC_OPEN; |
|
|
|
|
|
|
|
err_code = characteristic_add(p_nus->service_handle, &add_char_params, &p_nus->rx_handles); |
|
|
|
if (err_code != NRF_SUCCESS) { |
|
|
|
return err_code; |
|
|
|
} |
|
|
|
VERIFY_SUCCESS(characteristic_add(p_nus->service_handle, &add_char_params, &p_nus->cmd_rx_handles)); |
|
|
|
|
|
|
|
// Add the TX Characteristic. |
|
|
|
/**@snippet [Adding proprietary characteristic to the SoftDevice] */ |
|
|
|
memset(&add_char_params, 0, sizeof(add_char_params)); |
|
|
|
add_char_params.uuid = BLE_UUID_NUS_TX_CHARACTERISTIC; |
|
|
|
add_char_params.uuid = ZDATACHANNEL_TX_CHARACTERISTIC; |
|
|
|
add_char_params.uuid_type = p_nus->uuid_type; |
|
|
|
add_char_params.max_len = ZDATACANNEL_MAX_TX_CHAR_LEN; |
|
|
|
add_char_params.max_len = ZDATACHANNEL_MAX_TX_CHAR_LEN; |
|
|
|
add_char_params.init_len = sizeof(uint8_t); |
|
|
|
add_char_params.is_var_len = true; |
|
|
|
add_char_params.char_props.notify = 1; |
|
|
@ -212,38 +150,67 @@ uint32_t zdatachannel_init(zdatachannel_t *p_nus, zdatachannel_init_t const *p_n |
|
|
|
add_char_params.write_access = SEC_OPEN; |
|
|
|
add_char_params.cccd_write_access = SEC_OPEN; |
|
|
|
|
|
|
|
return characteristic_add(p_nus->service_handle, &add_char_params, &p_nus->tx_handles); |
|
|
|
VERIFY_SUCCESS(characteristic_add(p_nus->service_handle, &add_char_params, &p_nus->cmd_tx_handles)); |
|
|
|
|
|
|
|
// Add the TX Characteristic. |
|
|
|
/**@snippet [Adding proprietary characteristic to the SoftDevice] */ |
|
|
|
} |
|
|
|
memset(&add_char_params, 0, sizeof(add_char_params)); |
|
|
|
add_char_params.uuid = ZDATACHANNEL_DATABLOCK_TX_CHARACTERISTIC; |
|
|
|
add_char_params.uuid_type = p_nus->uuid_type; |
|
|
|
add_char_params.max_len = ZDATACHANNEL_DATABLOCK_TX_CHAR_LEN; |
|
|
|
add_char_params.init_len = sizeof(uint8_t); |
|
|
|
add_char_params.is_var_len = true; |
|
|
|
add_char_params.char_props.notify = 1; |
|
|
|
|
|
|
|
uint32_t zdatachannel_data_send(zdatachannel_t *p_nus, uint8_t *p_data, uint16_t *p_length, uint16_t conn_handle) { |
|
|
|
ret_code_t err_code; |
|
|
|
ble_gatts_hvx_params_t hvx_params; |
|
|
|
zdatachannel_client_context_t *p_client; |
|
|
|
add_char_params.read_access = SEC_OPEN; |
|
|
|
add_char_params.write_access = SEC_OPEN; |
|
|
|
add_char_params.cccd_write_access = SEC_OPEN; |
|
|
|
|
|
|
|
VERIFY_PARAM_NOT_NULL(p_nus); |
|
|
|
VERIFY_SUCCESS(characteristic_add(p_nus->service_handle, &add_char_params, &p_nus->datablock_tx_handles)); |
|
|
|
|
|
|
|
err_code = blcm_link_ctx_get(p_nus->p_link_ctx_storage, conn_handle, (void *)&p_client); |
|
|
|
VERIFY_SUCCESS(err_code); |
|
|
|
return NRF_SUCCESS; |
|
|
|
/**@snippet [Adding proprietary characteristic to the SoftDevice] */ |
|
|
|
} |
|
|
|
|
|
|
|
if ((conn_handle == BLE_CONN_HANDLE_INVALID) || (p_client == NULL)) { |
|
|
|
return NRF_ERROR_NOT_FOUND; |
|
|
|
} |
|
|
|
uint32_t zdatachannel_data_send(uint8_t *p_data, uint16_t *p_length) { |
|
|
|
ret_code_t err_code; |
|
|
|
ble_gatts_hvx_params_t hvx_params; |
|
|
|
|
|
|
|
if (!p_client->is_notification_enabled) { |
|
|
|
if (!p_datachannel->cmd_tx_channel_is_notification_enabled) { |
|
|
|
return NRF_ERROR_INVALID_STATE; |
|
|
|
} |
|
|
|
|
|
|
|
if (*p_length > ZDATACANNEL_MAX_DATA_LEN) { |
|
|
|
if (*p_length > ZDATACHANNEL_MAX_DATA_LEN) { |
|
|
|
return NRF_ERROR_INVALID_PARAM; |
|
|
|
} |
|
|
|
|
|
|
|
memset(&hvx_params, 0, sizeof(hvx_params)); |
|
|
|
|
|
|
|
hvx_params.handle = p_nus->tx_handles.value_handle; |
|
|
|
hvx_params.handle = p_datachannel->cmd_tx_handles.value_handle; |
|
|
|
hvx_params.p_data = p_data; |
|
|
|
hvx_params.p_len = p_length; |
|
|
|
hvx_params.type = BLE_GATT_HVX_NOTIFICATION; |
|
|
|
|
|
|
|
return sd_ble_gatts_hvx(p_datachannel->conn_handle, &hvx_params); |
|
|
|
} |
|
|
|
|
|
|
|
uint32_t zdatachannel_block_data_send(uint8_t *p_data, uint16_t *p_length) { |
|
|
|
ret_code_t err_code; |
|
|
|
ble_gatts_hvx_params_t hvx_params; |
|
|
|
|
|
|
|
if (!p_datachannel->datablock_tx_channel_is_notification_enabled) { |
|
|
|
return NRF_ERROR_INVALID_STATE; |
|
|
|
} |
|
|
|
|
|
|
|
if (*p_length > ZDATACHANNEL_MAX_DATA_LEN) { |
|
|
|
return NRF_ERROR_INVALID_PARAM; |
|
|
|
} |
|
|
|
memset(&hvx_params, 0, sizeof(hvx_params)); |
|
|
|
|
|
|
|
hvx_params.handle = p_datachannel->datablock_tx_handles.value_handle; |
|
|
|
hvx_params.p_data = p_data; |
|
|
|
hvx_params.p_len = p_length; |
|
|
|
hvx_params.type = BLE_GATT_HVX_NOTIFICATION; |
|
|
|
|
|
|
|
return sd_ble_gatts_hvx(conn_handle, &hvx_params); |
|
|
|
return sd_ble_gatts_hvx(p_datachannel->conn_handle, &hvx_params); |
|
|
|
} |