diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 1a6fd86..cc0a894 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
uint16_t gatt_svc_handle;
uint16_t svc_chngd_val_handle;
unsigned int svc_chngd_ind_id;
+ struct queue *svc_chngd_queue; /* Queued service changed events */
+ bool in_svc_chngd;
};
+struct notify_data {
+ struct bt_gatt_client *client;
+ bool removed;
+ bool invalid;
+ unsigned int id;
+ int ref_count;
+ struct chrc_data *chrc;
+ bt_gatt_client_notify_id_callback_t callback;
+ bt_gatt_client_notify_callback_t notify;
+ void *user_data;
+ bt_gatt_client_destroy_func_t destroy;
+};
+
+static struct notify_data *notify_data_ref(struct notify_data *notify_data)
+{
+ __sync_fetch_and_add(¬ify_data->ref_count, 1);
+
+ return notify_data;
+}
+
+static void notify_data_unref(void *data)
+{
+ struct notify_data *notify_data = data;
+
+ if (__sync_sub_and_fetch(¬ify_data->ref_count, 1))
+ return;
+
+ if (notify_data->destroy)
+ notify_data->destroy(notify_data->user_data);
+
+ free(notify_data);
+}
+
+static bool match_notify_data_id(const void *a, const void *b)
+{
+ const struct notify_data *notify_data = a;
+ unsigned int id = PTR_TO_UINT(b);
+
+ return notify_data->id == id;
+}
+
+static bool match_notify_data_removed(const void *a, const void *b)
+{
+ const struct notify_data *notify_data = a;
+
+ return notify_data->removed;
+}
+
+static bool match_notify_data_invalid(const void *a, const void *b)
+{
+ const struct notify_data *notify_data = a;
+
+ return notify_data->invalid;
+}
+
+struct handle_range {
+ uint16_t start;
+ uint16_t end;
+};
+
+static bool match_notify_data_handle_range(const void *a, const void *b)
+{
+ const struct notify_data *notify_data = a;
+ bt_gatt_characteristic_t *chrc = ¬ify_data->chrc->chrc_external;
+ const struct handle_range *range = b;
+
+ return chrc->value_handle >= range->start &&
+ chrc->value_handle <= range->end;
+}
+
+static void mark_notify_data_invalid_if_in_range(void *data, void *user_data)
+{
+ struct notify_data *notify_data = data;
+ bt_gatt_characteristic_t *chrc = ¬ify_data->chrc->chrc_external;
+ struct handle_range *range = user_data;
+
+ if (chrc->value_handle >= range->start &&
+ chrc->value_handle <= range->end)
+ notify_data->invalid = true;
+}
+
static bool service_list_add_service(struct service_list **head,
struct service_list **tail,
uint16_t start, uint16_t end,
return true;
}
-static void notify_data_unref(void *data);
-
static void service_destroy_characteristics(struct service_list *service)
{
unsigned int i;
*head = *tail = NULL;
}
+static void service_list_clear_range(struct service_list **head,
+ struct service_list **tail,
+ uint16_t start, uint16_t end)
+{
+ struct service_list *cur, *prev, *tmp;
+
+ if (!(*head) || !(*tail))
+ return;
+
+ prev = NULL;
+ cur = *head;
+ while (cur) {
+ if (cur->service.end_handle < start ||
+ cur->service.start_handle > end) {
+ prev = cur;
+ cur = cur->next;
+ continue;
+ }
+
+ service_destroy_characteristics(cur);
+
+ if (!prev)
+ *head = cur->next;
+ else
+ prev->next = cur->next;
+
+ if (*tail == cur)
+ *tail = prev;
+
+ tmp = cur;
+ cur = cur->next;
+ free(tmp);
+ }
+}
+
+static void gatt_client_remove_all_notify_in_range(
+ struct bt_gatt_client *client,
+ uint16_t start_handle, uint16_t end_handle)
+{
+ struct handle_range range;
+
+ range.start = start_handle;
+ range.end = end_handle;
+
+ if (client->in_notify) {
+ queue_foreach(client->notify_list,
+ mark_notify_data_invalid_if_in_range,
+ &range);
+ client->need_notify_cleanup = true;
+ return;
+ }
+
+ queue_remove_all(client->notify_list, match_notify_data_handle_range,
+ &range, notify_data_unref);
+}
+
static void gatt_client_clear_services(struct bt_gatt_client *client)
{
+
+ gatt_client_remove_all_notify_in_range(client, 0x0001, 0xffff);
service_list_clear(&client->svc_head, &client->svc_tail);
}
discovery_op_unref(op);
}
+struct service_changed_op {
+ struct bt_gatt_client *client;
+ uint16_t start_handle;
+ uint16_t end_handle;
+};
+
+static void process_service_changed(struct bt_gatt_client *client,
+ uint16_t start_handle,
+ uint16_t end_handle)
+{
+ /* Invalidate and remove all effected notify callbacks */
+ gatt_client_remove_all_notify_in_range(client, start_handle,
+ end_handle);
+
+ /* Remove all services that overlap the modified range since we'll
+ * rediscover them
+ */
+ service_list_clear_range(&client->svc_head, &client->svc_tail,
+ start_handle, end_handle);
+
+ /* TODO Rediscover all services within the modified service range. If
+ * the GATT service was effected, then register a new handler for
+ * "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 (value_handle != client->svc_chngd_val_handle || length != 4)
"Service Changed received - start: 0x%04x end: 0x%04x",
start, end);
- /* TODO: Process changed services */
+ if (!client->in_svc_chngd) {
+ process_service_changed(client, start, end);
+ return;
+ }
+
+ op = new0(struct service_changed_op, 1);
+ if (!op)
+ return;
+
+ queue_push_tail(client->svc_chngd_queue, op);
}
static void service_changed_register_cb(unsigned int id, uint16_t att_ecode,
return true;
}
-struct notify_data {
- struct bt_gatt_client *client;
- bool removed;
- unsigned int id;
- int ref_count;
- struct chrc_data *chrc;
- bt_gatt_client_notify_id_callback_t callback;
- bt_gatt_client_notify_callback_t notify;
- void *user_data;
- bt_gatt_client_destroy_func_t destroy;
-};
-
-static struct notify_data *notify_data_ref(struct notify_data *notify_data)
-{
- __sync_fetch_and_add(¬ify_data->ref_count, 1);
-
- return notify_data;
-}
-
-static void notify_data_unref(void *data)
-{
- struct notify_data *notify_data = data;
-
- if (__sync_sub_and_fetch(¬ify_data->ref_count, 1))
- return;
-
- if (notify_data->destroy)
- notify_data->destroy(notify_data->user_data);
-
- free(notify_data);
-}
-
-static bool match_notify_data_id(const void *a, const void *b)
-{
- const struct notify_data *notify_data = a;
- unsigned int id = PTR_TO_UINT(b);
-
- return notify_data->id == id;
-}
-
-static bool match_notify_data_removed(const void *a, const void *b)
-{
- const struct notify_data *notify_data = a;
-
- return notify_data->removed;
-}
-
struct pdu_data {
const void *pdu;
uint16_t length;
client->in_notify = false;
if (client->need_notify_cleanup) {
+ queue_remove_all(client->notify_list, match_notify_data_invalid,
+ NULL, notify_data_unref);
queue_remove_all(client->notify_list, match_notify_data_removed,
NULL, complete_unregister_notify);
client->need_notify_cleanup = false;
return NULL;
}
+ client->svc_chngd_queue = queue_new();
+ if (!client->svc_chngd_queue) {
+ queue_destroy(client->long_write_queue, NULL);
+ free(client);
+ return NULL;
+ }
+
client->notify_list = queue_new();
if (!client->notify_list) {
+ queue_destroy(client->svc_chngd_queue, NULL);
queue_destroy(client->long_write_queue, NULL);
free(client);
return NULL;
client->notify_id = bt_att_register(att, BT_ATT_OP_HANDLE_VAL_NOT,
notify_cb, client, NULL);
if (!client->notify_id) {
+ queue_destroy(client->notify_list, NULL);
+ queue_destroy(client->svc_chngd_queue, NULL);
queue_destroy(client->long_write_queue, NULL);
free(client);
return NULL;
notify_cb, client, NULL);
if (!client->ind_id) {
bt_att_unregister(att, client->notify_id);
+ queue_destroy(client->notify_list, NULL);
+ queue_destroy(client->svc_chngd_queue, NULL);
queue_destroy(client->long_write_queue, NULL);
free(client);
return NULL;
bt_att_unregister(client->att, client->notify_id);
bt_att_unregister(client->att, client->ind_id);
+ queue_destroy(client->svc_chngd_queue, free);
queue_destroy(client->long_write_queue, long_write_op_unref);
queue_destroy(client->notify_list, notify_data_unref);
if (!iter || !client)
return false;
- if (client->in_init)
+ if (client->in_init || client->in_svc_chngd)
return false;
memset(iter, 0, sizeof(*iter));