mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-28 14:44:39 +08:00
shared/gatt-client: Consolidate service changed registration
This rework the code using service changed registration so it becomes reusable by different part of code.
This commit is contained in:
parent
f49b0428c8
commit
35f86fe5f0
@ -1085,300 +1085,12 @@ struct service_changed_op {
|
||||
uint16_t end_handle;
|
||||
};
|
||||
|
||||
static void service_changed_reregister_cb(uint16_t att_ecode, void *user_data)
|
||||
{
|
||||
struct bt_gatt_client *client = user_data;
|
||||
|
||||
if (!att_ecode) {
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Re-registered handler for \"Service Changed\" after "
|
||||
"change in GATT service");
|
||||
client->svc_chngd_registered = true;
|
||||
return;
|
||||
}
|
||||
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Failed to register handler for \"Service Changed\"");
|
||||
client->svc_chngd_ind_id = 0;
|
||||
}
|
||||
|
||||
static void process_service_changed(struct bt_gatt_client *client,
|
||||
uint16_t start_handle,
|
||||
uint16_t end_handle);
|
||||
static void service_changed_cb(uint16_t value_handle, const uint8_t *value,
|
||||
uint16_t length, void *user_data);
|
||||
|
||||
static void get_first_attribute(struct gatt_db_attribute *attrib,
|
||||
void *user_data)
|
||||
{
|
||||
struct gatt_db_attribute **stored = user_data;
|
||||
|
||||
if (*stored)
|
||||
return;
|
||||
|
||||
*stored = attrib;
|
||||
}
|
||||
|
||||
static void service_changed_complete(struct discovery_op *op, bool success,
|
||||
uint8_t att_ecode)
|
||||
{
|
||||
struct bt_gatt_client *client = op->client;
|
||||
struct service_changed_op *next_sc_op;
|
||||
uint16_t start_handle = op->start;
|
||||
uint16_t end_handle = op->end;
|
||||
struct gatt_db_attribute *attr = NULL;
|
||||
bt_uuid_t uuid;
|
||||
|
||||
client->in_svc_chngd = false;
|
||||
|
||||
if (!success && att_ecode != BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Failed to discover services within changed range - "
|
||||
"error: 0x%02x", att_ecode);
|
||||
|
||||
gatt_db_clear_range(client->db, start_handle, end_handle);
|
||||
}
|
||||
|
||||
/* Notify the upper layer of changed services */
|
||||
if (client->svc_chngd_callback)
|
||||
client->svc_chngd_callback(start_handle, end_handle,
|
||||
client->svc_chngd_data);
|
||||
|
||||
/* Process any queued events */
|
||||
next_sc_op = queue_pop_head(client->svc_chngd_queue);
|
||||
if (next_sc_op) {
|
||||
process_service_changed(client, next_sc_op->start_handle,
|
||||
next_sc_op->end_handle);
|
||||
free(next_sc_op);
|
||||
return;
|
||||
}
|
||||
|
||||
bt_uuid16_create(&uuid, SVC_CHNGD_UUID);
|
||||
|
||||
gatt_db_find_by_type(client->db, start_handle, end_handle, &uuid,
|
||||
get_first_attribute, &attr);
|
||||
if (!attr)
|
||||
return;
|
||||
|
||||
/* The GATT service was modified. Re-register the handler for
|
||||
* indications from the "Service Changed" characteristic.
|
||||
*/
|
||||
client->svc_chngd_registered = false;
|
||||
client->svc_chngd_ind_id = bt_gatt_client_register_notify(client,
|
||||
gatt_db_attribute_get_handle(attr),
|
||||
service_changed_reregister_cb,
|
||||
service_changed_cb,
|
||||
client, NULL);
|
||||
if (client->svc_chngd_ind_id)
|
||||
return;
|
||||
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Failed to re-register handler for \"Service Changed\"");
|
||||
}
|
||||
|
||||
static void service_changed_failure(struct discovery_op *op)
|
||||
{
|
||||
struct bt_gatt_client *client = op->client;
|
||||
|
||||
gatt_db_clear_range(client->db, op->start, op->end);
|
||||
}
|
||||
|
||||
static void process_service_changed(struct bt_gatt_client *client,
|
||||
uint16_t start_handle,
|
||||
uint16_t end_handle)
|
||||
{
|
||||
struct discovery_op *op;
|
||||
|
||||
/* Invalidate and remove all effected notify callbacks */
|
||||
gatt_client_remove_all_notify_in_range(client, start_handle,
|
||||
end_handle);
|
||||
gatt_client_remove_notify_chrcs_in_range(client, start_handle,
|
||||
end_handle);
|
||||
|
||||
/* Remove all services that overlap the modified range since we'll
|
||||
* rediscover them
|
||||
*/
|
||||
gatt_db_clear_range(client->db, start_handle, end_handle);
|
||||
|
||||
op = discovery_op_create(client, start_handle, end_handle,
|
||||
service_changed_complete,
|
||||
service_changed_failure);
|
||||
if (!op)
|
||||
goto fail;
|
||||
|
||||
client->discovery_req = bt_gatt_discover_primary_services(client->att,
|
||||
NULL, start_handle, end_handle,
|
||||
discover_primary_cb,
|
||||
discovery_op_ref(op),
|
||||
discovery_op_unref);
|
||||
if (client->discovery_req) {
|
||||
client->in_svc_chngd = true;
|
||||
return;
|
||||
}
|
||||
|
||||
discovery_op_free(op);
|
||||
|
||||
fail:
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Failed to initiate service discovery"
|
||||
" after Service Changed");
|
||||
}
|
||||
|
||||
static void service_changed_cb(uint16_t value_handle, const uint8_t *value,
|
||||
uint16_t length, void *user_data)
|
||||
{
|
||||
struct bt_gatt_client *client = user_data;
|
||||
struct service_changed_op *op;
|
||||
uint16_t start, end;
|
||||
|
||||
if (length != 4)
|
||||
return;
|
||||
|
||||
start = get_le16(value);
|
||||
end = get_le16(value + 2);
|
||||
|
||||
if (start > end) {
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Service Changed received with invalid handles");
|
||||
return;
|
||||
}
|
||||
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Service Changed received - start: 0x%04x end: 0x%04x",
|
||||
start, end);
|
||||
|
||||
if (!client->in_svc_chngd) {
|
||||
process_service_changed(client, start, end);
|
||||
return;
|
||||
}
|
||||
|
||||
op = new0(struct service_changed_op, 1);
|
||||
if (!op)
|
||||
return;
|
||||
|
||||
op->start_handle = start;
|
||||
op->end_handle = end;
|
||||
|
||||
queue_push_tail(client->svc_chngd_queue, op);
|
||||
}
|
||||
|
||||
static void service_changed_register_cb(uint16_t att_ecode, void *user_data)
|
||||
{
|
||||
bool success;
|
||||
struct bt_gatt_client *client = user_data;
|
||||
|
||||
if (att_ecode) {
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Failed to register handler for \"Service Changed\"");
|
||||
success = false;
|
||||
client->svc_chngd_ind_id = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
client->svc_chngd_registered = true;
|
||||
client->ready = true;
|
||||
success = true;
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Registered handler for \"Service Changed\": %u",
|
||||
client->svc_chngd_ind_id);
|
||||
|
||||
done:
|
||||
notify_client_ready(client, success, att_ecode);
|
||||
}
|
||||
|
||||
static void init_complete(struct discovery_op *op, bool success,
|
||||
uint8_t att_ecode)
|
||||
{
|
||||
struct bt_gatt_client *client = op->client;
|
||||
struct gatt_db_attribute *attr = NULL;
|
||||
bt_uuid_t uuid;
|
||||
|
||||
client->in_init = false;
|
||||
|
||||
if (!success)
|
||||
goto fail;
|
||||
|
||||
bt_uuid16_create(&uuid, SVC_CHNGD_UUID);
|
||||
|
||||
gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid,
|
||||
get_first_attribute, &attr);
|
||||
if (!attr) {
|
||||
client->ready = true;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Register an indication handler for the "Service Changed"
|
||||
* characteristic and report ready only if the handler is registered
|
||||
* successfully. Temporarily set "ready" to true so that we can register
|
||||
* the handler using the existing framework.
|
||||
*/
|
||||
client->ready = true;
|
||||
client->svc_chngd_ind_id = bt_gatt_client_register_notify(client,
|
||||
gatt_db_attribute_get_handle(attr),
|
||||
service_changed_register_cb,
|
||||
service_changed_cb,
|
||||
client, NULL);
|
||||
|
||||
if (!client->svc_chngd_registered)
|
||||
client->ready = false;
|
||||
|
||||
if (client->svc_chngd_ind_id)
|
||||
return;
|
||||
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Failed to register handler for \"Service Changed\"");
|
||||
success = false;
|
||||
|
||||
fail:
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Failed to initialize gatt-client");
|
||||
|
||||
op->success = false;
|
||||
|
||||
done:
|
||||
notify_client_ready(client, success, att_ecode);
|
||||
}
|
||||
|
||||
static void init_fail(struct discovery_op *op)
|
||||
{
|
||||
struct bt_gatt_client *client = op->client;
|
||||
|
||||
gatt_db_clear(client->db);
|
||||
}
|
||||
|
||||
static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu)
|
||||
{
|
||||
struct discovery_op *op;
|
||||
|
||||
if (client->in_init || client->ready)
|
||||
return false;
|
||||
|
||||
op = discovery_op_create(client, 0x0001, 0xffff, init_complete,
|
||||
init_fail);
|
||||
if (!op)
|
||||
return false;
|
||||
|
||||
/* Configure the MTU */
|
||||
client->mtu_req_id = bt_gatt_exchange_mtu(client->att,
|
||||
MAX(BT_ATT_DEFAULT_LE_MTU, mtu),
|
||||
exchange_mtu_cb,
|
||||
discovery_op_ref(op),
|
||||
discovery_op_unref);
|
||||
if (!client->mtu_req_id) {
|
||||
discovery_op_free(op);
|
||||
return false;
|
||||
}
|
||||
|
||||
client->in_init = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct pdu_data {
|
||||
const void *pdu;
|
||||
uint16_t length;
|
||||
};
|
||||
|
||||
static void complete_notify_request(void *data)
|
||||
{
|
||||
struct notify_data *notify_data = data;
|
||||
@ -1477,6 +1189,350 @@ static void enable_ccc_callback(uint8_t opcode, const void *pdu,
|
||||
bt_gatt_client_unref(notify_data->client);
|
||||
}
|
||||
|
||||
static bool match_notify_chrc_value_handle(const void *a, const void *b)
|
||||
{
|
||||
const struct notify_chrc *chrc = a;
|
||||
uint16_t value_handle = PTR_TO_UINT(b);
|
||||
|
||||
return chrc->value_handle == value_handle;
|
||||
}
|
||||
|
||||
static unsigned int register_notify(struct bt_gatt_client *client,
|
||||
uint16_t handle,
|
||||
bt_gatt_client_register_callback_t callback,
|
||||
bt_gatt_client_notify_callback_t notify,
|
||||
void *user_data,
|
||||
bt_gatt_client_destroy_func_t destroy)
|
||||
{
|
||||
struct notify_data *notify_data;
|
||||
struct notify_chrc *chrc = NULL;
|
||||
|
||||
/* Check if a characteristic ref count has been started already */
|
||||
chrc = queue_find(client->notify_chrcs, match_notify_chrc_value_handle,
|
||||
UINT_TO_PTR(handle));
|
||||
|
||||
if (!chrc) {
|
||||
/*
|
||||
* Create an entry if the characteristic is known and has a CCC
|
||||
* descriptor.
|
||||
*/
|
||||
chrc = notify_chrc_create(client, handle);
|
||||
if (!chrc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Fail if we've hit the maximum allowed notify sessions */
|
||||
if (chrc->notify_count == INT_MAX)
|
||||
return 0;
|
||||
|
||||
notify_data = new0(struct notify_data, 1);
|
||||
if (!notify_data)
|
||||
return 0;
|
||||
|
||||
notify_data->client = client;
|
||||
notify_data->ref_count = 1;
|
||||
notify_data->chrc = chrc;
|
||||
notify_data->callback = callback;
|
||||
notify_data->notify = notify;
|
||||
notify_data->user_data = user_data;
|
||||
notify_data->destroy = destroy;
|
||||
|
||||
/* Add the handler to the bt_gatt_client's general list */
|
||||
queue_push_tail(client->notify_list, notify_data);
|
||||
|
||||
/* Assign an ID to the handler. */
|
||||
if (client->next_reg_id < 1)
|
||||
client->next_reg_id = 1;
|
||||
|
||||
notify_data->id = client->next_reg_id++;
|
||||
|
||||
/*
|
||||
* If a write to the CCC descriptor is in progress, then queue this
|
||||
* request.
|
||||
*/
|
||||
if (chrc->ccc_write_id) {
|
||||
queue_push_tail(chrc->reg_notify_queue, notify_data);
|
||||
return notify_data->id;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the ref count is not zero, then notifications are already enabled.
|
||||
*/
|
||||
if (chrc->notify_count > 0 || !chrc->ccc_handle) {
|
||||
complete_notify_request(notify_data);
|
||||
return notify_data->id;
|
||||
}
|
||||
|
||||
/* Write to the CCC descriptor */
|
||||
if (!notify_data_write_ccc(notify_data, true, enable_ccc_callback)) {
|
||||
queue_remove(client->notify_list, notify_data);
|
||||
free(notify_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return notify_data->id;
|
||||
}
|
||||
|
||||
static void get_first_attribute(struct gatt_db_attribute *attrib,
|
||||
void *user_data)
|
||||
{
|
||||
struct gatt_db_attribute **stored = user_data;
|
||||
|
||||
if (*stored)
|
||||
return;
|
||||
|
||||
*stored = attrib;
|
||||
}
|
||||
|
||||
static void service_changed_register_cb(uint16_t att_ecode, void *user_data)
|
||||
{
|
||||
bool success;
|
||||
struct bt_gatt_client *client = user_data;
|
||||
|
||||
if (att_ecode) {
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Failed to register handler for \"Service Changed\"");
|
||||
success = false;
|
||||
client->svc_chngd_ind_id = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
client->svc_chngd_registered = true;
|
||||
success = true;
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Registered handler for \"Service Changed\": %u",
|
||||
client->svc_chngd_ind_id);
|
||||
|
||||
done:
|
||||
if (!client->ready) {
|
||||
client->ready = success;
|
||||
notify_client_ready(client, success, att_ecode);
|
||||
}
|
||||
}
|
||||
|
||||
static bool register_service_changed(struct bt_gatt_client *client)
|
||||
{
|
||||
bt_uuid_t uuid;
|
||||
struct gatt_db_attribute *attr = NULL;
|
||||
|
||||
bt_uuid16_create(&uuid, SVC_CHNGD_UUID);
|
||||
|
||||
if (client->svc_chngd_ind_id)
|
||||
return true;
|
||||
|
||||
gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid,
|
||||
get_first_attribute, &attr);
|
||||
if (!attr)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* Register an indication handler for the "Service Changed"
|
||||
* characteristic and report ready only if the handler is registered
|
||||
* successfully.
|
||||
*/
|
||||
client->svc_chngd_ind_id = register_notify(client,
|
||||
gatt_db_attribute_get_handle(attr),
|
||||
service_changed_register_cb,
|
||||
service_changed_cb,
|
||||
client, NULL);
|
||||
|
||||
return client->svc_chngd_ind_id ? true : false;
|
||||
}
|
||||
|
||||
static void service_changed_complete(struct discovery_op *op, bool success,
|
||||
uint8_t att_ecode)
|
||||
{
|
||||
struct bt_gatt_client *client = op->client;
|
||||
struct service_changed_op *next_sc_op;
|
||||
uint16_t start_handle = op->start;
|
||||
uint16_t end_handle = op->end;
|
||||
|
||||
client->in_svc_chngd = false;
|
||||
|
||||
if (!success && att_ecode != BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Failed to discover services within changed range - "
|
||||
"error: 0x%02x", att_ecode);
|
||||
|
||||
gatt_db_clear_range(client->db, start_handle, end_handle);
|
||||
}
|
||||
|
||||
/* Notify the upper layer of changed services */
|
||||
if (client->svc_chngd_callback)
|
||||
client->svc_chngd_callback(start_handle, end_handle,
|
||||
client->svc_chngd_data);
|
||||
|
||||
/* Process any queued events */
|
||||
next_sc_op = queue_pop_head(client->svc_chngd_queue);
|
||||
if (next_sc_op) {
|
||||
process_service_changed(client, next_sc_op->start_handle,
|
||||
next_sc_op->end_handle);
|
||||
free(next_sc_op);
|
||||
return;
|
||||
}
|
||||
|
||||
if (register_service_changed(client))
|
||||
return;
|
||||
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Failed to re-register handler for \"Service Changed\"");
|
||||
}
|
||||
|
||||
static void service_changed_failure(struct discovery_op *op)
|
||||
{
|
||||
struct bt_gatt_client *client = op->client;
|
||||
|
||||
gatt_db_clear_range(client->db, op->start, op->end);
|
||||
}
|
||||
|
||||
static void process_service_changed(struct bt_gatt_client *client,
|
||||
uint16_t start_handle,
|
||||
uint16_t end_handle)
|
||||
{
|
||||
struct discovery_op *op;
|
||||
|
||||
/* Invalidate and remove all effected notify callbacks */
|
||||
gatt_client_remove_all_notify_in_range(client, start_handle,
|
||||
end_handle);
|
||||
gatt_client_remove_notify_chrcs_in_range(client, start_handle,
|
||||
end_handle);
|
||||
|
||||
/* Remove all services that overlap the modified range since we'll
|
||||
* rediscover them
|
||||
*/
|
||||
gatt_db_clear_range(client->db, start_handle, end_handle);
|
||||
|
||||
op = discovery_op_create(client, start_handle, end_handle,
|
||||
service_changed_complete,
|
||||
service_changed_failure);
|
||||
if (!op)
|
||||
goto fail;
|
||||
|
||||
client->discovery_req = bt_gatt_discover_primary_services(client->att,
|
||||
NULL, start_handle, end_handle,
|
||||
discover_primary_cb,
|
||||
discovery_op_ref(op),
|
||||
discovery_op_unref);
|
||||
if (client->discovery_req) {
|
||||
client->in_svc_chngd = true;
|
||||
return;
|
||||
}
|
||||
|
||||
discovery_op_free(op);
|
||||
|
||||
fail:
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Failed to initiate service discovery"
|
||||
" after Service Changed");
|
||||
}
|
||||
|
||||
static void service_changed_cb(uint16_t value_handle, const uint8_t *value,
|
||||
uint16_t length, void *user_data)
|
||||
{
|
||||
struct bt_gatt_client *client = user_data;
|
||||
struct service_changed_op *op;
|
||||
uint16_t start, end;
|
||||
|
||||
if (length != 4)
|
||||
return;
|
||||
|
||||
start = get_le16(value);
|
||||
end = get_le16(value + 2);
|
||||
|
||||
if (start > end) {
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Service Changed received with invalid handles");
|
||||
return;
|
||||
}
|
||||
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Service Changed received - start: 0x%04x end: 0x%04x",
|
||||
start, end);
|
||||
|
||||
if (!client->in_svc_chngd) {
|
||||
process_service_changed(client, start, end);
|
||||
return;
|
||||
}
|
||||
|
||||
op = new0(struct service_changed_op, 1);
|
||||
if (!op)
|
||||
return;
|
||||
|
||||
op->start_handle = start;
|
||||
op->end_handle = end;
|
||||
|
||||
queue_push_tail(client->svc_chngd_queue, op);
|
||||
}
|
||||
|
||||
static void init_complete(struct discovery_op *op, bool success,
|
||||
uint8_t att_ecode)
|
||||
{
|
||||
struct bt_gatt_client *client = op->client;
|
||||
|
||||
client->in_init = false;
|
||||
|
||||
if (!success)
|
||||
goto fail;
|
||||
|
||||
if (register_service_changed(client)) {
|
||||
client->ready = true;
|
||||
goto done;
|
||||
}
|
||||
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Failed to register handler for \"Service Changed\"");
|
||||
success = false;
|
||||
|
||||
fail:
|
||||
util_debug(client->debug_callback, client->debug_data,
|
||||
"Failed to initialize gatt-client");
|
||||
|
||||
op->success = false;
|
||||
|
||||
done:
|
||||
notify_client_ready(client, success, att_ecode);
|
||||
}
|
||||
|
||||
static void init_fail(struct discovery_op *op)
|
||||
{
|
||||
struct bt_gatt_client *client = op->client;
|
||||
|
||||
gatt_db_clear(client->db);
|
||||
}
|
||||
|
||||
static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu)
|
||||
{
|
||||
struct discovery_op *op;
|
||||
|
||||
if (client->in_init || client->ready)
|
||||
return false;
|
||||
|
||||
op = discovery_op_create(client, 0x0001, 0xffff, init_complete,
|
||||
init_fail);
|
||||
if (!op)
|
||||
return false;
|
||||
|
||||
/* Configure the MTU */
|
||||
client->mtu_req_id = bt_gatt_exchange_mtu(client->att,
|
||||
MAX(BT_ATT_DEFAULT_LE_MTU, mtu),
|
||||
exchange_mtu_cb,
|
||||
discovery_op_ref(op),
|
||||
discovery_op_unref);
|
||||
if (!client->mtu_req_id) {
|
||||
discovery_op_free(op);
|
||||
return false;
|
||||
}
|
||||
|
||||
client->in_init = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct pdu_data {
|
||||
const void *pdu;
|
||||
uint16_t length;
|
||||
};
|
||||
|
||||
static void disable_ccc_callback(uint8_t opcode, const void *pdu,
|
||||
uint16_t length, void *user_data)
|
||||
{
|
||||
@ -2900,14 +2956,6 @@ unsigned int bt_gatt_client_write_execute(struct bt_gatt_client *client,
|
||||
return id;
|
||||
}
|
||||
|
||||
static bool match_notify_chrc_value_handle(const void *a, const void *b)
|
||||
{
|
||||
const struct notify_chrc *chrc = a;
|
||||
uint16_t value_handle = PTR_TO_UINT(b);
|
||||
|
||||
return chrc->value_handle == value_handle;
|
||||
}
|
||||
|
||||
unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
|
||||
uint16_t chrc_value_handle,
|
||||
bt_gatt_client_register_callback_t callback,
|
||||
@ -2915,79 +2963,14 @@ unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
|
||||
void *user_data,
|
||||
bt_gatt_client_destroy_func_t destroy)
|
||||
{
|
||||
struct notify_data *notify_data;
|
||||
struct notify_chrc *chrc = NULL;
|
||||
|
||||
if (!client || !client->db || !chrc_value_handle || !callback)
|
||||
return 0;
|
||||
|
||||
if (!bt_gatt_client_is_ready(client) || client->in_svc_chngd)
|
||||
return 0;
|
||||
|
||||
/* Check if a characteristic ref count has been started already */
|
||||
chrc = queue_find(client->notify_chrcs, match_notify_chrc_value_handle,
|
||||
UINT_TO_PTR(chrc_value_handle));
|
||||
|
||||
if (!chrc) {
|
||||
/*
|
||||
* Create an entry if the characteristic is known and has a CCC
|
||||
* descriptor.
|
||||
*/
|
||||
chrc = notify_chrc_create(client, chrc_value_handle);
|
||||
if (!chrc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Fail if we've hit the maximum allowed notify sessions */
|
||||
if (chrc->notify_count == INT_MAX)
|
||||
return 0;
|
||||
|
||||
notify_data = new0(struct notify_data, 1);
|
||||
if (!notify_data)
|
||||
return 0;
|
||||
|
||||
notify_data->client = client;
|
||||
notify_data->ref_count = 1;
|
||||
notify_data->chrc = chrc;
|
||||
notify_data->callback = callback;
|
||||
notify_data->notify = notify;
|
||||
notify_data->user_data = user_data;
|
||||
notify_data->destroy = destroy;
|
||||
|
||||
/* Add the handler to the bt_gatt_client's general list */
|
||||
queue_push_tail(client->notify_list, notify_data);
|
||||
|
||||
/* Assign an ID to the handler. */
|
||||
if (client->next_reg_id < 1)
|
||||
client->next_reg_id = 1;
|
||||
|
||||
notify_data->id = client->next_reg_id++;
|
||||
|
||||
/*
|
||||
* If a write to the CCC descriptor is in progress, then queue this
|
||||
* request.
|
||||
*/
|
||||
if (chrc->ccc_write_id) {
|
||||
queue_push_tail(chrc->reg_notify_queue, notify_data);
|
||||
return notify_data->id;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the ref count is not zero, then notifications are already enabled.
|
||||
*/
|
||||
if (chrc->notify_count > 0 || !chrc->ccc_handle) {
|
||||
complete_notify_request(notify_data);
|
||||
return notify_data->id;
|
||||
}
|
||||
|
||||
/* Write to the CCC descriptor */
|
||||
if (!notify_data_write_ccc(notify_data, true, enable_ccc_callback)) {
|
||||
queue_remove(client->notify_list, notify_data);
|
||||
free(notify_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return notify_data->id;
|
||||
return register_notify(client, chrc_value_handle, callback, notify,
|
||||
user_data, destroy);
|
||||
}
|
||||
|
||||
bool bt_gatt_client_unregister_notify(struct bt_gatt_client *client,
|
||||
|
Loading…
Reference in New Issue
Block a user