Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/bttester/src/btp/btp_gatt.h
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ struct btp_gatt_get_attribute_value_rp {
struct btp_gatt_change_database_cmd {
uint16_t start_handle;
uint16_t end_handle;
uint8_t visibility;
uint8_t operation;
} __packed;

#define BTP_GATT_NOTIFY_MULTIPLE 0x21
Expand Down
83 changes: 80 additions & 3 deletions apps/bttester/src/btp_gatt.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ static uint8_t gatt_svr_pts_static_short_val;
static uint16_t myconn_handle;
uint16_t notify_handle;
uint16_t notify_handle_alt;
static bool tester_service_visible = true;

struct find_attr_data {
ble_uuid_any_t *uuid;
Expand Down Expand Up @@ -1977,12 +1978,88 @@ change_database(const void *cmd, uint16_t cmd_len,
void *rsp, uint16_t *rsp_len)
{
const struct btp_gatt_change_database_cmd *cp = cmd;
uint16_t start_handle, end_handle;

SYS_LOG_DBG("")
if (cmd_len < sizeof(*cp) || cp->operation > 0x02) {
return BTP_STATUS_FAILED;
}

ble_gatts_show_local();
start_handle = cp->start_handle;
end_handle = cp->end_handle;

/* Branch 1: Auto-select range */
if (start_handle == 0 && end_handle == 0) {
uint16_t svc_start, svc_end;
int visible;
/* If provided handles are 0, it is up to IUT to select
* range that will be changed. Let's select last registred service. */
if (ble_gatts_find_svc_range_by_handle(ble_att_svr_prev_handle(),
&svc_start, &svc_end) != 0) {
return BTP_STATUS_FAILED;
}

/* Operation values are 0x00 = Remove; 0x01 = Add; 0x02 = Any */
switch (cp->operation) {
case 0x00:
visible = 0;
break;
case 0x01:
visible = 1;
break;
case 0x02:
/* Toggle visibility based on cached state to avoid a no-op */
visible = tester_service_visible ? 0 : 1;
break;
default:
return BTP_STATUS_FAILED;
}
if (ble_gatts_svc_set_visibility(svc_start, visible) != 0) {
return BTP_STATUS_FAILED;
}

/* Update cached visibility to reflect the actual new state */
tester_service_visible = (visible != 0);

ble_svc_gatt_changed(svc_start, svc_end);

return BTP_STATUS_SUCCESS;
}

/* Branch 2: Explicit handle range validation */
if (start_handle == 0 || end_handle < start_handle) {
return BTP_STATUS_FAILED;
}

/* 0x01 = Add (1); 0x00 / 0x02 (Any) = Remove (0) per BTP spec permissions */
int visible = (cp->operation == 0x01) ? 1 : 0;
uint16_t cur_handle, svc_start, svc_end;

/* If requested range overlaps multiple services, call
* ble_gatts_svc_set_visibility for each overlapping service */
for (cur_handle = start_handle; cur_handle <= end_handle;
cur_handle = svc_end + 1) {
if (ble_gatts_find_svc_range_by_handle(cur_handle, &svc_start, &svc_end) != 0) {
return BTP_STATUS_FAILED;
}

/* Per Core Specification 6.3, the Service Changed value contains
* the start and end of the affected handle range.*/
start_handle = svc_start;

if (ble_gatts_svc_set_visibility(svc_start, visible) != 0) {
return BTP_STATUS_FAILED;
}

if (svc_end == 0xffff) {
break;
}
}

/* Per Core Specification 6.3, the Service Changed value contains
* the start and end of the affected handle range.*/
end_handle = svc_end;

ble_svc_gatt_changed(cp->start_handle, cp->end_handle);
ble_svc_gatt_changed(start_handle, end_handle);

return BTP_STATUS_SUCCESS;
}
Expand Down
13 changes: 13 additions & 0 deletions nimble/host/include/host/ble_gatt.h
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,19 @@ int ble_gatts_find_chr(const ble_uuid_t *svc_uuid, const ble_uuid_t *chr_uuid,
int ble_gatts_find_dsc(const ble_uuid_t *svc_uuid, const ble_uuid_t *chr_uuid,
const ble_uuid_t *dsc_uuid, uint16_t *out_dsc_handle);

/**
* Finds the service start and end handles for a given attribute handle.
*
* @param handle Attribute handle to search with
* @param out_start Pointer where the service start handle will be stored.
* @param out_end Pointer where the service end handle will be stored.
*
* @return 0 on success;
* BLE_HS_ENOENT if no matching service is found.
*/
int ble_gatts_find_svc_range_by_handle(uint16_t handle, uint16_t *out_start,
uint16_t *out_end);

/** Type definition for GATT service iteration callback function. */
typedef void (*ble_gatt_svc_foreach_fn)(const struct ble_gatt_svc_def *svc,
uint16_t handle,
Expand Down
28 changes: 28 additions & 0 deletions nimble/host/src/ble_gatts.c
Original file line number Diff line number Diff line change
Expand Up @@ -2023,6 +2023,34 @@ ble_gatts_find_svc_entry(const ble_uuid_t *uuid)
return NULL;
}

int
ble_gatts_find_svc_range_by_handle(uint16_t handle, uint16_t *out_start,
uint16_t *out_end)
{
if (handle == 0) {
return BLE_HS_ENOENT;
}

for (int i = 0; i < ble_gatts_num_svc_entries; i++) {
struct ble_gatts_svc_entry *entry = &ble_gatts_svc_entries[i];

if (entry->handle != 0 && handle >= entry->handle &&
handle <= entry->end_group_handle) {

if (out_start != NULL) {
*out_start = entry->handle;
}
if (out_end != NULL) {
*out_end = entry->end_group_handle;
}

return 0;
}
}

return BLE_HS_ENOENT;
}

static int
ble_gatts_find_svc_chr_attr(const ble_uuid_t *svc_uuid,
const ble_uuid_t *chr_uuid,
Expand Down
Loading