Diff between c239312a2ccb2136bb3e469a711ba65df11d5145 and 68115186219bbcc8e50fe8ebab343f5d545c36a1

Changed Files

File Additions Deletions Status
android/gatt.c +144 -7 modified

Full Patch

diff --git a/android/gatt.c b/android/gatt.c
index 51e7c07..07fe161 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -286,6 +286,16 @@ static bool match_char_by_higher_inst_id(const void *data,
 	return inst_id < ch->id.instance;
 }
 
+static bool match_descr_by_higher_inst_id(const void *data,
+							const void *user_data)
+{
+	const struct descriptor *descr = data;
+	uint8_t instance = PTR_TO_INT(user_data);
+
+	/* For now we match instance as it is unique */
+	return instance < descr->id.instance;
+}
+
 static bool match_char_by_instance(const void *data, const void *user_data)
 {
 	const struct characteristic *ch = data;
@@ -1443,6 +1453,127 @@ static void send_client_descr_notify(int32_t status, int32_t conn_id,
 			HAL_EV_GATT_CLIENT_GET_DESCRIPTOR, sizeof(ev), &ev);
 }
 
+static void cache_all_descr(const uint8_t *pdu, guint16 len,
+							struct queue *cache)
+{
+	struct att_data_list *list;
+	guint8 format;
+	int i;
+
+	list = dec_find_info_resp(pdu, len, &format);
+	if (!list)
+		return;
+
+	for (i = 0; i < list->num; i++) {
+		char uuidstr[MAX_LEN_UUID_STR];
+		struct descriptor *descr;
+		bt_uuid_t uuid128;
+		uint16_t handle;
+		uint8_t *value;
+		bt_uuid_t uuid;
+
+		value = list->data[i];
+		handle = get_le16(value);
+
+		if (format == ATT_FIND_INFO_RESP_FMT_16BIT) {
+			bt_uuid16_create(&uuid, get_le16(&value[2]));
+			bt_uuid_to_uuid128(&uuid, &uuid128);
+		} else {
+			uint128_t u128;
+
+			bswap_128(&value[2], &u128);
+			bt_uuid128_create(&uuid128, u128);
+		}
+
+		bt_uuid_to_string(&uuid128, uuidstr, MAX_LEN_UUID_STR);
+		DBG("handle 0x%04x uuid %s", handle, uuidstr);
+
+		descr = new0(struct descriptor, 1);
+		if (!descr)
+			continue;
+
+		descr->id.instance = i;
+		descr->handle = handle;
+		descr->id.uuid = uuid128;
+
+		if (!queue_push_tail(cache, descr))
+			free(descr);
+	}
+
+	att_data_list_free(list);
+}
+
+struct discover_desc_data {
+	int32_t conn_id;
+	const struct element_id *srvc_id;
+	const struct characteristic *ch;
+	uint8_t primary;
+};
+
+static void gatt_discover_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
+							gpointer user_data)
+{
+	struct discover_desc_data *data = user_data;
+	struct descriptor *descr;
+
+	if (status)
+		error("gatt: Discover all char descriptors failed: %s",
+							att_ecode2str(status));
+	else
+		cache_all_descr(pdu, len, data->ch->descriptors);
+
+	descr = queue_peek_head(data->ch->descriptors);
+
+	send_client_descr_notify(status, data->conn_id, data->primary,
+						data->srvc_id, &data->ch->id,
+						descr ? &descr->id : NULL);
+
+	free(data);
+}
+
+static bool build_descr_cache(int32_t conn_id, struct gatt_device *dev,
+					struct service *srvc, uint8_t primary,
+					struct characteristic *ch)
+{
+	struct discover_desc_data *cb_data;
+	struct characteristic *next_ch;
+	uint16_t start, end;
+
+	/* Clip range to given characteristic */
+	start = ch->ch.value_handle + 1;
+	end = srvc->primary.range.end;
+
+	/* Use next characteristic start as end. If there is none -
+	 * service end is valid end.
+	 * TODO: we should cache char end handle to avoid this search
+	 */
+	next_ch = queue_find(srvc->chars, match_char_by_higher_inst_id,
+					INT_TO_PTR(ch->id.instance));
+	if (next_ch)
+		end = next_ch->ch.handle - 1;
+
+	/* If there are no descriptors, notify with fail status. */
+	if (start > end)
+		return false;
+
+	cb_data = new0(struct discover_desc_data, 1);
+	if (!cb_data)
+		return false;
+
+	cb_data->conn_id = conn_id;
+	cb_data->srvc_id = &srvc->id;
+	cb_data->ch = ch;
+	cb_data->primary = primary;
+
+	if (!gatt_discover_char_desc(dev->attrib, start, end,
+					gatt_discover_desc_cb, cb_data)) {
+		free(cb_data);
+		return false;
+	}
+
+	return true;
+}
+
 static void handle_client_get_descriptor(const void *buf, uint16_t len)
 {
 	const struct hal_cmd_gatt_client_get_descriptor *cmd = buf;
@@ -1489,17 +1620,23 @@ static void handle_client_get_descriptor(const void *buf, uint16_t len)
 	}
 
 	if (queue_isempty(ch->descriptors)) {
-		/* TODO: Build the cache */
-
-		ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+		if (build_descr_cache(conn_id, dev, srvc, primary, ch)) {
+			ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
 					HAL_OP_GATT_CLIENT_GET_DESCRIPTOR,
-					HAL_STATUS_FAILED);
-		return;
+					HAL_STATUS_SUCCESS);
+			return;
+		}
 	}
 
-	status = HAL_STATUS_FAILED;
+	status = HAL_STATUS_SUCCESS;
 
-	/* TODO: Send from cache */
+	/* Send from cache */
+	if (cmd->number > 1)
+		descr = queue_find(ch->descriptors,
+					match_descr_by_higher_inst_id,
+					INT_TO_PTR(cmd->gatt_id[1].inst_id));
+	else
+		descr = queue_peek_head(ch->descriptors);
 
 failed:
 	send_client_descr_notify(descr ? GATT_SUCCESS : GATT_FAILURE, conn_id,