Diff between 97719697b1e2b7bd606e6fb822b294ef2a68ad2f and f82aa0c7769d5e56184bdcc1f0354e3aff389b5d

Changed Files

File Additions Deletions Status
src/shared/gatt-client.c +98 -5 modified

Full Patch

diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index d0c12df..1a6fd86 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -41,6 +41,9 @@
 
 #define UUID_BYTES (BT_GATT_UUID_SIZE * sizeof(uint8_t))
 
+#define GATT_SVC_UUID	0x1801
+#define SVC_CHNGD_UUID	0x2a05
+
 struct chrc_data {
 	/* The public characteristic entry. */
 	bt_gatt_characteristic_t chrc_external;
@@ -100,6 +103,14 @@ struct bt_gatt_client {
 	unsigned int notify_id, ind_id;
 	bool in_notify;
 	bool need_notify_cleanup;
+
+	/* Handles of the GATT Service and the Service Changed characteristic
+	 * value handle. These will have the value 0 if they are not present on
+	 * the remote peripheral.
+	 */
+	uint16_t gatt_svc_handle;
+	uint16_t svc_chngd_val_handle;
+	unsigned int svc_chngd_ind_id;
 };
 
 static bool service_list_add_service(struct service_list **head,
@@ -387,6 +398,10 @@ static void discover_chrcs_cb(bool success, uint8_t att_ecode,
 			goto done;
 		}
 
+		if (uuid_cmp(chrcs[i].chrc_external.uuid, SVC_CHNGD_UUID) == 0)
+			client->svc_chngd_val_handle =
+					chrcs[i].chrc_external.value_handle;
+
 		i++;
 	}
 
@@ -479,6 +494,9 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
 			success = false;
 			goto done;
 		}
+
+		if (uuid_cmp(uuid, GATT_SVC_UUID) == 0)
+			client->gatt_svc_handle = start;
 	}
 
 	/* Complete the process if the service list is empty */
@@ -544,20 +562,95 @@ static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data)
 	discovery_op_unref(op);
 }
 
+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;
+	uint16_t start, end;
+
+	if (value_handle != client->svc_chngd_val_handle || length != 4)
+		return;
+
+	start = get_le16(value);
+	end = get_le16(value + 2);
+
+	util_debug(client->debug_callback, client->debug_data,
+			"Service Changed received - start: 0x%04x end: 0x%04x",
+			start, end);
+
+	/* TODO: Process changed services */
+}
+
+static void service_changed_register_cb(unsigned int id, uint16_t att_ecode,
+								void *user_data)
+{
+	bool success;
+	struct bt_gatt_client *client = user_data;
+
+	if (!id || att_ecode) {
+		util_debug(client->debug_callback, client->debug_data,
+			"Failed to register handler for \"Service Changed\"");
+		success = false;
+		goto done;
+	}
+
+	client->svc_chngd_ind_id = id;
+	client->ready = true;
+	success = true;
+	util_debug(client->debug_callback, client->debug_data,
+			"Registered handler for \"Service Changed\": %u", id);
+
+done:
+	if (client->ready_callback)
+		client->ready_callback(success, att_ecode, client->ready_data);
+}
+
 static void init_complete(struct discovery_op *op, bool success,
 							uint8_t att_ecode)
 {
 	struct bt_gatt_client *client = op->client;
+	bool registered;
 
 	client->in_init = false;
 
-	if (success) {
-		client->svc_head = op->result_head;
-		client->svc_tail = op->result_tail;
+	if (!success)
+		goto fail;
+
+	client->svc_head = op->result_head;
+	client->svc_tail = op->result_tail;
+
+	if (!client->svc_chngd_val_handle) {
 		client->ready = true;
-	} else
-		service_list_clear(&op->result_head, &op->result_tail);
+		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;
+	registered = bt_gatt_client_register_notify(client,
+						client->svc_chngd_val_handle,
+						service_changed_register_cb,
+						service_changed_cb,
+						client, NULL);
+	client->ready = false;
+
+	if (registered)
+		return;
 
+	util_debug(client->debug_callback, client->debug_data,
+			"Failed to register handler for \"Service Changed\"");
+
+	client->svc_head = client->svc_tail = NULL;
+
+fail:
+	util_debug(client->debug_callback, client->debug_data,
+			"Failed to initialize gatt-client");
+	service_list_clear(&op->result_head, &op->result_tail);
+
+done:
 	if (client->ready_callback)
 		client->ready_callback(success, att_ecode, client->ready_data);
 }