[PATCH 1/9] shared/gatt-client: Add read-by-uuid functionality

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is exposed to allow client reading characteristic values
with known uuid.
---
 src/shared/gatt-client.c | 263 +++++++++++++++++++++++++++++++++++++++++++++++
 src/shared/gatt-client.h |  18 ++++
 2 files changed, 281 insertions(+)

diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index cf93d4b..72ffed5 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -2009,6 +2009,269 @@ bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
 	return true;
 }
 
+struct read_by_uuid_res {
+	uint16_t handle;
+	uint8_t length;
+	uint8_t *data;
+	struct read_by_uuid_res *next;
+};
+
+bool read_by_uuid_res_get(struct read_by_uuid_res *result, uint16_t *handle,
+					uint8_t *length, uint8_t **data)
+{
+	if (!result)
+		return false;
+
+	*length = result->length;
+	*handle = result->handle;
+	*data = result->data;
+
+	return true;
+}
+
+struct read_by_uuid_res *read_by_uuid_res_next(struct read_by_uuid_res *result)
+{
+	if (!result)
+		return NULL;
+
+	return result->next;
+}
+
+struct read_by_uuid_op {
+	struct bt_gatt_client *client;
+	bt_gatt_client_read_by_uuid_callback_t callback;
+	int ref_count;
+	uint16_t start_handle;
+	uint16_t end_handle;
+	bt_uuid_t uuid;
+	struct read_by_uuid_res *result_head;
+	struct read_by_uuid_res *result_tail;
+	void *user_data;
+	bt_gatt_client_destroy_func_t destroy;
+};
+
+static bool append_read_by_uuid_result(struct read_by_uuid_op *op,
+					uint16_t handle, uint16_t length,
+					const uint8_t *data)
+{
+	struct read_by_uuid_res *result;
+
+	result = new0(struct read_by_uuid_res, 1);
+	if (!result)
+		return false;
+
+	result->data = malloc(length);
+	if (!result->data) {
+		free(result);
+		return false;
+	}
+
+	result->handle = handle;
+	result->length = length;
+	memcpy(result->data, data, length);
+
+	if (op->result_head) {
+		op->result_tail->next = result;
+		op->result_tail = result;
+	} else {
+		op->result_head = op->result_tail = result;
+	}
+
+	return true;
+}
+
+static void destroy_read_by_uuid_result(void *data)
+{
+	struct read_by_uuid_res *result = data;
+
+	free(result->data);
+	free(result);
+}
+
+static struct read_by_uuid_op *read_by_uuid_op_ref(struct read_by_uuid_op *op)
+{
+	__sync_fetch_and_add(&op->ref_count, 1);
+
+	return op;
+}
+
+static void read_by_uuid_op_unref(void *data)
+{
+	struct read_by_uuid_res *next, *temp;
+	struct read_by_uuid_op *op = data;
+
+	if (__sync_sub_and_fetch(&op->ref_count, 1))
+		return;
+
+	if (op->destroy)
+		op->destroy(op->user_data);
+
+	for (temp = op->result_head; temp; temp = next) {
+		next = temp->next;
+		destroy_read_by_uuid_result(temp);
+	}
+
+	free(op);
+}
+
+static void read_by_uuid_cb(uint8_t opcode, const void *pdu,
+					uint16_t length, void *user_data)
+{
+	struct read_by_uuid_op *op = user_data;
+	size_t data_length;
+	uint8_t att_ecode;
+	uint16_t offset, last_handle;
+	const uint8_t *data;
+	bool success;
+
+	if (opcode == BT_ATT_OP_ERROR_RSP) {
+		att_ecode = process_error(pdu, length);
+		if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND)
+			success = true;
+		else
+			success = false;
+
+		goto done;
+	}
+
+	if (opcode != BT_ATT_OP_READ_BY_TYPE_RSP || (!pdu && length)) {
+		success = false;
+		att_ecode = 0;
+		goto done;
+	}
+
+	data_length = ((const uint8_t *) pdu)[0];
+	if ((length - 1) % data_length) {
+		success = false;
+		att_ecode = 0;
+		goto done;
+	}
+
+	for (offset = 1; offset < length; offset += data_length) {
+		data = pdu + offset;
+		if (!append_read_by_uuid_result(op, get_le16(data),
+						data_length - 2, data + 2)) {
+			success = false;
+			att_ecode = 0;
+			goto done;
+		}
+	}
+
+	last_handle = get_le16(pdu + length - data_length);
+	if (last_handle < op->end_handle) {
+		uint8_t pdu[20];
+
+		put_le16(last_handle + 1, pdu);
+		put_le16(op->end_handle, pdu + 2);
+
+		if (op->uuid.type == BT_UUID16) {
+			put_le16(op->uuid.value.u16, pdu + 4);
+			/*
+			 * 2 octets - start handle
+			 * 2 octets - end handle
+			 * 2 octets - 16-bit uuid
+			 */
+			length = 6;
+		} else {
+			bswap_128(&op->uuid.value.u128, pdu + 4);
+			/*
+			 * 2 octets - start handle
+			 * 2 octets - end handle
+			 * 16 octets - 128-bit uuid
+			 */
+			length = 20;
+		}
+
+		if (!bt_att_send(op->client->att, BT_ATT_OP_READ_BY_TYPE_REQ,
+					pdu, length, read_by_uuid_cb,
+					read_by_uuid_op_ref(op),
+					read_by_uuid_op_unref)) {
+			read_by_uuid_op_unref(op);
+			success = false;
+			att_ecode = 0;
+			goto done;
+		}
+
+		return;
+	}
+
+	success = true;
+	att_ecode = 0;
+
+done:
+	if (op->callback)
+		op->callback(success, att_ecode, op->result_head,
+								op->user_data);
+}
+
+bool bt_gatt_client_read_by_uuid(struct bt_gatt_client *client,
+				uint16_t start_handle, uint16_t end_handle,
+				const uint8_t uuid[BT_GATT_UUID_SIZE],
+				bt_gatt_client_read_by_uuid_callback_t callback,
+				void *user_data,
+				bt_gatt_client_destroy_func_t destroy)
+{
+	struct read_by_uuid_op *op;
+	bt_uuid_t uuid_16, uuid_128;
+	uint16_t length;
+	uint8_t pdu[20];
+	uint128_t u128;
+
+	if (!client)
+		return false;
+
+	if (start_handle > end_handle || start_handle == 0x0000)
+		return false;
+
+	op = new0(struct read_by_uuid_op, 1);
+	if (!op)
+		return false;
+
+	op->client = client;
+	op->start_handle = start_handle;
+	op->end_handle = end_handle;
+	op->destroy = destroy;
+	op->callback = callback;
+	op->user_data = user_data;
+
+	bt_uuid16_create(&uuid_16, ((uuid[2] << 8) + uuid[3]));
+	memcpy(u128.data, uuid, sizeof(u128.data));
+	bt_uuid128_create(&uuid_128, u128);
+
+	if (bt_uuid_cmp(&uuid_128, &uuid_16)) {
+		op->uuid = uuid_128;
+		bswap_128(&op->uuid.value.u128, pdu + 4);
+		/*
+		 * 2 octets - start handle
+		 * 2 octets - end handle
+		 * 16 octets - 128-bit uuid
+		 */
+		length = 20;
+	} else {
+		op->uuid = uuid_16;
+		put_le16(op->uuid.value.u16, pdu  + 4);
+		/*
+		 * 2 octets - start handle
+		 * 2 octets - end handle
+		 * 2 octets - 16-bit uuid
+		 */
+		length = 6;
+	}
+
+	put_le16(start_handle, pdu);
+	put_le16(end_handle, pdu + 2);
+
+	if (!bt_att_send(client->att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu,
+						length, read_by_uuid_cb,
+						read_by_uuid_op_ref(op),
+						read_by_uuid_op_unref)) {
+		free(op);
+		return false;
+	}
+
+	return true;
+}
+
 bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
 					uint16_t value_handle,
 					bool signed_write,
diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
index adccfc5..921ed75 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
@@ -145,6 +145,24 @@ bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
 					void *user_data,
 					bt_gatt_client_destroy_func_t destroy);
 
+struct read_by_uuid_res;
+
+struct read_by_uuid_res *read_by_uuid_res_next(struct read_by_uuid_res *result);
+bool read_by_uuid_res_get(struct read_by_uuid_res *result, uint16_t *handle,
+					uint8_t *length, uint8_t **data);
+
+typedef void (*bt_gatt_client_read_by_uuid_callback_t)(bool success,
+						uint8_t att_ecode,
+						struct read_by_uuid_res *result,
+						void *user_data);
+
+bool bt_gatt_client_read_by_uuid(struct bt_gatt_client *client,
+				uint16_t start_handle, uint16_t end_handle,
+				const uint8_t uuid[BT_GATT_UUID_SIZE],
+				bt_gatt_client_read_by_uuid_callback_t callback,
+				void *user_data,
+				bt_gatt_client_destroy_func_t destroy);
+
 bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
 					uint16_t value_handle,
 					bool signed_write,
-- 
1.9.3

--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux