Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BLE: user can optionally send characteristic value with/without ACK. #1901

Merged
merged 13 commits into from Sep 9, 2019
24 changes: 23 additions & 1 deletion hal/inc/ble_hal.h
Expand Up @@ -815,7 +815,7 @@ int hal_ble_gatt_server_add_characteristic(const hal_ble_char_init_t* char_init,
int hal_ble_gatt_server_add_descriptor(const hal_ble_desc_init_t* desc_init, hal_ble_attr_handle_t* handle, void* reserved);

/**
* Set Characteristic value..
* Set Characteristic value.
*
* @param[in] value_handle Characteristic value handle.
* @param[in] buf Pointer to the buffer that contains the data to be set.
Expand All @@ -825,6 +825,28 @@ int hal_ble_gatt_server_add_descriptor(const hal_ble_desc_init_t* desc_init, hal
*/
ssize_t hal_ble_gatt_server_set_characteristic_value(hal_ble_attr_handle_t value_handle, const uint8_t* buf, size_t len, void* reserved);

/**
* Set Characteristic value and notify it to subscribers without acknowledgment.
*
* @param[in] value_handle Characteristic value handle.
* @param[in] buf Pointer to the buffer that contains the data to be set.
* @param[in] len Length of the data to be set.
*
* @returns Length of the data has been set.
*/
ssize_t hal_ble_gatt_server_notify_characteristic_value(hal_ble_attr_handle_t value_handle, const uint8_t* buf, size_t len, void* reserved);

/**
* Set Characteristic value and notify it to subscribers with acknowledgment.
*
* @param[in] value_handle Characteristic value handle.
* @param[in] buf Pointer to the buffer that contains the data to be set.
* @param[in] len Length of the data to be set.
*
* @returns Length of the data has been set.
*/
ssize_t hal_ble_gatt_server_indicate_characteristic_value(hal_ble_attr_handle_t value_handle, const uint8_t* buf, size_t len, void* reserved);

/**
* Get Characteristic value.
*
Expand Down
8 changes: 5 additions & 3 deletions hal/inc/ble_hal_defines.h
Expand Up @@ -270,9 +270,11 @@ typedef enum ble_sig_char_prop_t {
/**@defgroup BLE Characteristic properties
* @{ */
typedef enum ble_sig_cccd_value_t {
BLE_SIG_CCCD_VAL_DISABLED = 0x00, /**< Neither notification nor indication is enabled. */
BLE_SIG_CCCD_VAL_NOTIFICATION = 0x01, /**< Notification is enabled. */
BLE_SIG_CCCD_VAL_INDICATION = 0x02 /**< Indication is enabled. */
BLE_SIG_CCCD_VAL_DISABLED = 0x0000, /**< Neither notification nor indication is enabled. */
BLE_SIG_CCCD_VAL_NOTIFICATION = 0x0001, /**< Notification is enabled. */
BLE_SIG_CCCD_VAL_INDICATION = 0x0002, /**< Indication is enabled. */
BLE_SIG_CCCD_VAL_NOTI_IND = 0x0003, /**< Both Notification and indication are enabled. */
BLE_SIG_CCCD_VAL_MAX = 0x7FFFF
} ble_sig_cccd_value_t;
/**@} */

Expand Down
2 changes: 2 additions & 0 deletions hal/inc/hal_dynalib_ble.h
Expand Up @@ -94,6 +94,8 @@ DYNALIB_FN(60, hal_ble, hal_ble_set_callback_on_periph_link_events, int(hal_ble_
DYNALIB_FN(61, hal_ble, hal_ble_gatt_client_configure_cccd, int(const hal_ble_cccd_config_t*, void*))
DYNALIB_FN(62, hal_ble, hal_ble_set_callback_on_adv_events, int(hal_ble_on_adv_evt_cb_t, void*, void*))
DYNALIB_FN(63, hal_ble, hal_ble_cancel_callback_on_adv_events, int(hal_ble_on_adv_evt_cb_t, void*, void*))
DYNALIB_FN(64, hal_ble, hal_ble_gatt_server_notify_characteristic_value, ssize_t(hal_ble_attr_handle_t, const uint8_t*, size_t, void*))
DYNALIB_FN(65, hal_ble, hal_ble_gatt_server_indicate_characteristic_value, ssize_t(hal_ble_attr_handle_t, const uint8_t*, size_t, void*))

DYNALIB_END(hal_ble)

Expand Down
144 changes: 93 additions & 51 deletions hal/src/nRF52840/ble_hal.cpp
Expand Up @@ -489,10 +489,16 @@ class BleObject::GattServer {
int addDescriptor(hal_ble_attr_handle_t charHandle, const hal_ble_uuid_t* uuid, uint8_t* descriptor, size_t len, hal_ble_attr_handle_t* descHandle);
void removeSubscriberFromAllCharacteristics(hal_ble_conn_handle_t connHandle);
ssize_t setValue(hal_ble_attr_handle_t attrHandle, const uint8_t* buf, size_t len);
ssize_t notifyValue(hal_ble_attr_handle_t attrHandle, const uint8_t* buf, size_t len, bool ack);
ssize_t getValue(hal_ble_attr_handle_t attrHandle, uint8_t* buf, size_t len);
int processDataWrittenEventFromThread(ble_evt_t* event);

private:
struct Subscriber {
hal_ble_conn_handle_t connHandle;
ble_sig_cccd_value_t config;
};

class BleCharacteristic {
public:
BleCharacteristic() = default;
Expand All @@ -503,12 +509,12 @@ class BleObject::GattServer {
hal_ble_char_handles_t charHandles;
hal_ble_on_char_evt_cb_t callback;
void* context;
Vector<hal_ble_conn_handle_t> subscribers;
Vector<Subscriber> subscribers;
};

bool findService(hal_ble_attr_handle_t svcHandle) const;
BleCharacteristic* findCharacteristic(hal_ble_attr_handle_t attrHandle);
int addSubscriber(BleCharacteristic* characteristic, hal_ble_conn_handle_t connHandle);
int addSubscriber(BleCharacteristic* characteristic, hal_ble_conn_handle_t connHandle, ble_sig_cccd_value_t value);
void removeSubscriber(BleCharacteristic* characteristic, hal_ble_conn_handle_t connHandle);
static void processGattServerEvents(const ble_evt_t* event, void* context);

Expand Down Expand Up @@ -1175,8 +1181,8 @@ int BleObject::Broadcaster::configure(const hal_ble_adv_params_t* params) {
ret = sd_ble_gap_adv_set_configure(&advHandle_, &bleGapAdvData, nullptr);
} else {
ble_gap_adv_params_t bleGapAdvParams = toPlatformAdvParams(params);
LOG_DEBUG(TRACE, "BLE advertising interval: %.3fms, timeout: %dms.",
bleGapAdvParams.interval*0.625, bleGapAdvParams.duration*10);
LOG_DEBUG(TRACE, "BLE advertising interval: %d*0.625ms, timeout: %dms.",
bleGapAdvParams.interval, bleGapAdvParams.duration*10);
ret = sd_ble_gap_adv_set_configure(&advHandle_, &bleGapAdvData, &bleGapAdvParams);
}
CHECK(nrf_system_error(ret));
Expand Down Expand Up @@ -1316,8 +1322,8 @@ int BleObject::Observer::startScanning(hal_ble_on_scan_result_cb_t callback, voi
});
ble_gap_scan_params_t bleGapScanParams = toPlatformScanParams();
LOG_DEBUG(TRACE, "| interval(ms) window(ms) timeout(ms) |");
LOG_DEBUG(TRACE, " %.3f %.3f %d",
bleGapScanParams.interval*0.625, bleGapScanParams.window*0.625, bleGapScanParams.timeout*10);
LOG_DEBUG(TRACE, " %d*0.625 %d*0.625 %d",
bleGapScanParams.interval, bleGapScanParams.window, bleGapScanParams.timeout*10);
scanResultCallback_ = callback;
context_ = context;
int ret = sd_ble_gap_scan_start(&bleGapScanParams, &bleScanData_);
Expand Down Expand Up @@ -2394,39 +2400,49 @@ ssize_t BleObject::GattServer::setValue(hal_ble_attr_handle_t attrHandle, const
gattValue.p_value = (uint8_t*)buf;
int ret = sd_ble_gatts_value_set(BLE_CONN_HANDLE_INVALID, attrHandle, &gattValue);
CHECK_NRF_RETURN(ret, nrf_system_error(ret));
// Notify or indicate the value if possible.
if ((characteristic->properties & BLE_SIG_CHAR_PROP_NOTIFY) || (characteristic->properties & BLE_SIG_CHAR_PROP_INDICATE)) {
for (const auto& subscriber : characteristic->subscribers) {
if (subscriber == BLE_INVALID_CONN_HANDLE) {
continue;
}
ble_gatts_hvx_params_t hvxParams = {};
uint16_t hvxLen = std::min(len, (size_t)BLE_ATTR_VALUE_PACKET_SIZE(BleObject::getInstance().connMgr()->getAttMtu(subscriber)));
if (characteristic->properties & BLE_SIG_CHAR_PROP_NOTIFY) {
hvxParams.type = BLE_GATT_HVX_NOTIFICATION;
} else if (characteristic->properties & BLE_SIG_CHAR_PROP_INDICATE) {
hvxParams.type = BLE_GATT_HVX_INDICATION;
}
hvxParams.handle = attrHandle;
hvxParams.offset = 0;
hvxParams.p_data = buf;
hvxParams.p_len = &hvxLen;
ret = sd_ble_gatts_hvx(subscriber, &hvxParams);
if (ret != NRF_SUCCESS) {
LOG(ERROR, "sd_ble_gatts_hvx() failed: %u", (unsigned)ret);
continue;
}
isHvxing_ = true;
currHvxConnHandle_ = subscriber;
if (os_semaphore_take(hvxSemaphore_, BLE_OPERATION_TIMEOUT_MS, false)) {
SPARK_ASSERT(false);
break;
}
isHvxing_ = false;
currHvxConnHandle_ = BLE_INVALID_CONN_HANDLE;
return len;
}

ssize_t BleObject::GattServer::notifyValue(hal_ble_attr_handle_t attrHandle, const uint8_t* buf, size_t len, bool ack) {
CHECK_TRUE(attrHandle, SYSTEM_ERROR_INVALID_ARGUMENT);
CHECK_TRUE(buf, SYSTEM_ERROR_INVALID_ARGUMENT);
CHECK_TRUE(len, SYSTEM_ERROR_INVALID_ARGUMENT);
BleCharacteristic* characteristic = findCharacteristic(attrHandle);
CHECK_TRUE(characteristic, SYSTEM_ERROR_NOT_FOUND);
CHECK_TRUE((characteristic->properties & BLE_SIG_CHAR_PROP_NOTIFY) || (characteristic->properties & BLE_SIG_CHAR_PROP_INDICATE), SYSTEM_ERROR_NOT_SUPPORTED);
for (const auto& subscriber : characteristic->subscribers) {
if (subscriber.connHandle == BLE_INVALID_CONN_HANDLE) {
continue;
}
ble_gatts_hvx_params_t hvxParams = {};
uint16_t hvxLen = std::min(len, (size_t)BLE_ATTR_VALUE_PACKET_SIZE(BleObject::getInstance().connMgr()->getAttMtu(subscriber.connHandle)));
if (ack && (subscriber.config & BLE_SIG_CCCD_VAL_INDICATION)) {
hvxParams.type = BLE_GATT_HVX_INDICATION;
} else if (!ack && (subscriber.config & BLE_SIG_CCCD_VAL_NOTIFICATION)) {
hvxParams.type = BLE_GATT_HVX_NOTIFICATION;
} else {
continue;
}
hvxParams.handle = attrHandle;
hvxParams.offset = 0;
hvxParams.p_data = buf;
hvxParams.p_len = &hvxLen;
int ret = sd_ble_gatts_hvx(subscriber.connHandle, &hvxParams);
if (ret != NRF_SUCCESS) {
LOG(ERROR, "sd_ble_gatts_hvx() failed: %u", (unsigned)ret);
continue;
}
isHvxing_ = true;
currHvxConnHandle_ = subscriber.connHandle;
if (os_semaphore_take(hvxSemaphore_, BLE_OPERATION_TIMEOUT_MS, false)) {
SPARK_ASSERT(false);
break;
}
isHvxing_ = false;
currHvxConnHandle_ = BLE_INVALID_CONN_HANDLE;
}
return len;
// FIXME: Different link may have different ATT_MTU, let's just return the possible maximum transmitted data length.
return std::min(len, (size_t)BLE_MAX_ATTR_VALUE_PACKET_SIZE);
}

ssize_t BleObject::GattServer::getValue(hal_ble_attr_handle_t attrHandle, uint8_t* buf, size_t len) {
Expand Down Expand Up @@ -2468,20 +2484,24 @@ BleObject::GattServer::BleCharacteristic* BleObject::GattServer::findCharacteris
return nullptr;
}

int BleObject::GattServer::addSubscriber(BleCharacteristic* characteristic, hal_ble_conn_handle_t connHandle) {
int BleObject::GattServer::addSubscriber(BleCharacteristic* characteristic, hal_ble_conn_handle_t connHandle, ble_sig_cccd_value_t value) {
for (auto& subscriber : characteristic->subscribers) {
if (subscriber == connHandle) {
if (subscriber.connHandle == connHandle) {
subscriber.config = value;
return SYSTEM_ERROR_NONE;
}
}
CHECK_TRUE(characteristic->subscribers.append(connHandle), SYSTEM_ERROR_NO_MEMORY);
Subscriber subscriber = {};
subscriber.connHandle = connHandle;
subscriber.config = value;
CHECK_TRUE(characteristic->subscribers.append(subscriber), SYSTEM_ERROR_NO_MEMORY);
return SYSTEM_ERROR_NONE;
}

void BleObject::GattServer::removeSubscriber(BleCharacteristic* characteristic, hal_ble_conn_handle_t connHandle) {
size_t i = 0;
for (auto& subscriber : characteristic->subscribers) {
if (subscriber == connHandle) {
if (subscriber.connHandle == connHandle) {
characteristic->subscribers.removeAt(i);
return;
}
Expand All @@ -2505,14 +2525,21 @@ int BleObject::GattServer::processDataWrittenEventFromThread(ble_evt_t* event) {
hal_ble_char_evt_t charEvent = {};
charEvent.conn_handle = event->evt.gatts_evt.conn_handle;
charEvent.attr_handle = write.handle;
if (characteristic->charHandles.cccd_handle == write.handle) {
if (write.data[0] > 0) {
CHECK(addSubscriber(characteristic, event->evt.gatts_evt.conn_handle));
if (characteristic->charHandles.cccd_handle == write.handle && write.len == sizeof(uint16_t)) {
uint16_t cccd = ((uint16_t)write.data[1] << 8 | (uint16_t)write.data[0]) & BLE_SIG_CCCD_VAL_NOTI_IND;
if (!(characteristic->properties & BLE_SIG_CHAR_PROP_NOTIFY)) {
cccd &= ~BLE_SIG_CCCD_VAL_NOTIFICATION;
}
if (!(characteristic->properties & BLE_SIG_CHAR_PROP_INDICATE)) {
cccd &= ~BLE_SIG_CCCD_VAL_INDICATION;
}
if (cccd > 0) {
CHECK(addSubscriber(characteristic, event->evt.gatts_evt.conn_handle, (ble_sig_cccd_value_t)cccd));
} else {
removeSubscriber(characteristic, event->evt.gatts_evt.conn_handle);
}
charEvent.type = BLE_EVT_CHAR_CCCD_UPDATED;
charEvent.params.cccd_config.value = (ble_sig_cccd_value_t)write.data[0];
charEvent.params.cccd_config.value = (ble_sig_cccd_value_t)cccd;
} else if (characteristic->charHandles.value_handle == write.handle) {
charEvent.type = BLE_EVT_DATA_WRITTEN;
charEvent.params.data_written.offset = write.offset;
Expand Down Expand Up @@ -2825,7 +2852,7 @@ int BleObject::GattClient::removePublisher(hal_ble_conn_handle_t connHandle, hal
}
i++;
}
return SYSTEM_ERROR_NOT_FOUND;
return SYSTEM_ERROR_NONE;
}

int BleObject::GattClient::removeAllPublishersOfConnection(hal_ble_conn_handle_t connHandle) {
Expand All @@ -2844,15 +2871,16 @@ int BleObject::GattClient::configureRemoteCCCD(const hal_ble_cccd_config_t* conf
CHECK_TRUE(BleObject::getInstance().connMgr()->valid(config->conn_handle), SYSTEM_ERROR_NOT_FOUND);
CHECK_TRUE(config->cccd_handle != BLE_INVALID_ATTR_HANDLE, SYSTEM_ERROR_INVALID_ARGUMENT);
CHECK_TRUE(config->value_handle != BLE_INVALID_ATTR_HANDLE, SYSTEM_ERROR_INVALID_ARGUMENT);
CHECK_TRUE(config->cccd_value <= BLE_SIG_CCCD_VAL_INDICATION, SYSTEM_ERROR_NOT_SUPPORTED);
if (config->cccd_value == BLE_SIG_CCCD_VAL_NOTIFICATION || config->cccd_value == BLE_SIG_CCCD_VAL_INDICATION) {
CHECK_TRUE(config->cccd_value <= BLE_SIG_CCCD_VAL_NOTI_IND, SYSTEM_ERROR_NOT_SUPPORTED);
uint8_t buf[2] = {0x00, 0x00};
buf[0] = config->cccd_value;
CHECK(writeAttribute(config->conn_handle, config->cccd_handle, buf, sizeof(buf), true));
if (config->cccd_value > BLE_SIG_CCCD_VAL_DISABLED && config->cccd_value <= BLE_SIG_CCCD_VAL_NOTI_IND) {
CHECK(addPublisher(config->conn_handle, config->value_handle, config->callback, config->context));
} else {
CHECK(removePublisher(config->conn_handle, config->value_handle));
}
uint8_t buf[2] = {0x00, 0x00};
buf[0] = config->cccd_value;
return writeAttribute(config->conn_handle, config->cccd_handle, buf, sizeof(buf), true);
return SYSTEM_ERROR_NONE;
}

int BleObject::GattClient::processSvcDiscEventFromThread(const ble_evt_t* event) {
Expand Down Expand Up @@ -3764,6 +3792,20 @@ ssize_t hal_ble_gatt_server_set_characteristic_value(hal_ble_attr_handle_t value
return BleObject::getInstance().gatts()->setValue(value_handle, buf, len);
}

ssize_t hal_ble_gatt_server_notify_characteristic_value(hal_ble_attr_handle_t value_handle, const uint8_t* buf, size_t len, void* reserved) {
BleLock lk;
LOG_DEBUG(TRACE, "hal_ble_gatt_server_notify_characteristic_value().");
CHECK_TRUE(BleObject::getInstance().initialized(), SYSTEM_ERROR_INVALID_STATE);
return BleObject::getInstance().gatts()->notifyValue(value_handle, buf, len, false);
}

ssize_t hal_ble_gatt_server_indicate_characteristic_value(hal_ble_attr_handle_t value_handle, const uint8_t* buf, size_t len, void* reserved) {
BleLock lk;
LOG_DEBUG(TRACE, "hal_ble_gatt_server_indicate_characteristic_value().");
CHECK_TRUE(BleObject::getInstance().initialized(), SYSTEM_ERROR_INVALID_STATE);
return BleObject::getInstance().gatts()->notifyValue(value_handle, buf, len, true);
}

ssize_t hal_ble_gatt_server_get_characteristic_value(hal_ble_attr_handle_t value_handle, uint8_t* buf, size_t len, void* reserved) {
BleLock lk;
LOG_DEBUG(TRACE, "hal_ble_gatt_server_get_characteristic_value().");
Expand Down
4 changes: 2 additions & 2 deletions system/src/ble_control_request_channel.cpp
Expand Up @@ -973,9 +973,9 @@ int BleControlRequestChannel::sendPacket() {
return 0; // Nothing to send
}
// Send packet
const int ret = hal_ble_gatt_server_set_characteristic_value(sendCharHandle_, (const uint8_t*)packetBuf_.get(), packetSize_, nullptr);
const int ret = hal_ble_gatt_server_notify_characteristic_value(sendCharHandle_, (const uint8_t*)packetBuf_.get(), packetSize_, nullptr);
if (ret != (int)packetSize_) {
LOG(ERROR, "ble_gatt_server_notify_characteristic_value() failed: %d", ret);
LOG(ERROR, "hal_ble_gatt_server_notify_characteristic_value() failed: %d", ret);
return ret;
}
DEBUG("Sent BLE packet");
Expand Down