[PATCH 2/2] android/gatt: Handle get characteristic client command

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

 



This adds get characteristic client command handling.
---
 android/gatt.c | 247 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 242 insertions(+), 5 deletions(-)

diff --git a/android/gatt.c b/android/gatt.c
index 302a9b9..fbc3b61 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -53,6 +53,28 @@ struct gatt_client {
 	uint8_t uuid[16];
 };
 
+struct gatt_id {
+	bt_uuid_t uuid;
+	uint8_t inst_id;
+};
+
+struct gatt_srvc_char {
+	struct gatt_id id;
+	struct gatt_char ch;
+};
+
+struct gatt_srvc {
+	struct gatt_id id;
+	struct gatt_primary service;
+
+	struct queue *chars;
+};
+
+struct generic_call_data {
+	struct gatt_device *dev;
+	struct gatt_srvc *gatt_srvc;
+};
+
 struct gatt_device {
 	bdaddr_t bdaddr;
 	uint8_t bdaddr_type;
@@ -66,6 +88,7 @@ struct gatt_device {
 	GIOChannel *att_io;
 	struct queue *services;
 
+	uint8_t char_inst_id;
 	guint watch_id;
 };
 
@@ -79,6 +102,14 @@ static struct queue *conn_wait_queue = NULL;	/* Devs waiting to connect */
 
 static void bt_le_discovery_stop_cb(void);
 
+static void free_gatt_service(void *data)
+{
+	struct gatt_srvc *srvc = data;
+
+	queue_destroy(srvc->chars, free);
+	free(srvc);
+}
+
 static bool match_client_by_uuid(const void *data, const void *user_data)
 {
 	const uint8_t *exp_uuid = user_data;
@@ -123,12 +154,43 @@ static bool match_dev_by_conn_id(const void *data, const void *user_data)
 	return dev->conn_id == conn_id;
 }
 
+static bool match_srvc_by_gatt_id(const void *data, const void *user_data)
+{
+	const struct gatt_id *exp_id = user_data;
+	const struct gatt_srvc *service = data;
+	bt_uuid_t uuid;
+
+	bt_string_to_uuid(&uuid, service->service.uuid);
+	if (service->id.inst_id == exp_id->inst_id)
+		return !bt_uuid_cmp(&uuid, &exp_id->uuid);
+
+	return false;
+}
+
+static bool match_char_by_higher_inst_id(const void *data,
+							const void *user_data)
+{
+	const struct gatt_srvc_char *chars = data;
+	uint8_t inst_id = PTR_TO_INT(user_data);
+
+	/* For now we match inst_id as it is unique, we'll match uuids later */
+	return inst_id < chars->id.inst_id;
+}
+
+static bool match_char_by_inst_id(const void *data, const void *user_data)
+{
+	const struct gatt_srvc_char *chars = data;
+	uint8_t inst_id = PTR_TO_INT(user_data);
+
+	return inst_id == chars->id.inst_id;
+}
+
 static void destroy_device(void *data)
 {
 	struct gatt_device *dev = data;
 
 	queue_destroy(dev->clients, NULL);
-	queue_destroy(dev->services, free);
+	queue_destroy(dev->services, free_gatt_service);
 	free(dev);
 }
 
@@ -224,19 +286,26 @@ static void primary_cb(uint8_t status, GSList *services, void *user_data)
 	for (l = services; l; l = l->next) {
 		struct hal_ev_gatt_client_search_result ev_res;
 		struct gatt_primary *prim = l->data;
-		struct gatt_primary *p;
+		struct gatt_srvc *p;
 		bt_uuid_t uuid;
 
-		p = new0(struct gatt_primary, 1);
+		p = new0(struct gatt_srvc, 1);
 		if (!p) {
 			error("gatt: Cannot allocate memory for gatt_primary");
 			continue;
 		}
 
+		p->chars = queue_new();
+		if (!p->chars) {
+			error("gatt: Cannot allocate memory for char cache");
+			free(p);
+			continue;
+		}
+
 		memset(&ev_res, 0, sizeof(ev_res));
 
 		/* Put primary service to our local list */
-		memcpy(p, prim, sizeof(*p));
+		memcpy(&p->service, prim, sizeof(p->service));
 		if (!queue_push_tail(dev->services, p)) {
 			error("gatt: Cannot push primary service to the list");
 			free(p);
@@ -869,13 +938,181 @@ static void handle_client_get_included_service(const void *buf, uint16_t len)
 					HAL_STATUS_FAILED);
 }
 
+static void send_client_char_notify(const struct gatt_srvc_char *chars,
+				uint8_t status, struct generic_call_data *data)
+{
+	struct hal_ev_gatt_client_get_characteristic ev;
+	bt_uuid_t uuid;
+
+	ev.conn_id = data->dev->conn_id;
+	ev.status = status;
+	ev.char_prop = chars->ch.properties;
+
+	/* TODO need to be handled for included services too */
+	ev.srvc_id.is_primary = 1;
+	ev.srvc_id.inst_id = data->gatt_srvc->id.inst_id;
+	bt_string_to_uuid(&uuid, data->gatt_srvc->service.uuid);
+	memcpy(&ev.srvc_id.uuid, &uuid.value.u128.data,
+						sizeof(ev.srvc_id.uuid));
+
+	ev.char_id.inst_id = chars->id.inst_id;
+	bt_string_to_uuid(&uuid, chars->ch.uuid);
+	memcpy(&ev.char_id.uuid, &uuid.value.u128.data,
+						sizeof(ev.char_id.uuid));
+
+	ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+					HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC,
+					sizeof(ev), &ev);
+}
+
+static void cache_all_srvc_chars(GSList *characteristics, struct queue *q)
+{
+	uint16_t inst_id = 0;
+	bt_uuid_t uuid;
+	GSList *l;
+
+	/* Refresh chararcter cache if already exist */
+	if (!queue_isempty(q))
+		queue_remove_all(q, NULL, NULL, free);
+
+	for (l = characteristics; l; l = l->next) {
+		struct gatt_srvc_char *chars;
+
+		chars = new0(struct gatt_srvc_char, 1);
+		if (!chars) {
+			error("gatt: Could not allocate characteristic");
+			continue;
+		}
+
+		memcpy(&chars->ch, l->data, sizeof(chars->ch));
+
+		bt_string_to_uuid(&uuid, chars->ch.uuid);
+		bt_uuid_to_uuid128(&chars->id.uuid, &uuid);
+
+		/* For now we increment inst_id and use it as characteristic
+		 * handle
+		 */
+		chars->id.inst_id = ++inst_id;
+
+		if (!queue_push_tail(q, chars))
+			info("gatt: Error while caching characteristic");
+	}
+}
+
+static void gatt_discover_char_cb(uint8_t status, GSList *characteristics,
+								void *user_data)
+{
+	struct generic_call_data *call_data = user_data;
+
+	cache_all_srvc_chars(characteristics, call_data->gatt_srvc->chars);
+
+	if (call_data && !queue_isempty(call_data->gatt_srvc->chars))
+		send_client_char_notify(queue_peek_head(
+					call_data->gatt_srvc->chars),
+					HAL_STATUS_SUCCESS, call_data);
+
+	free(call_data);
+}
+
+static void hal_srvc_id_to_gatt_id(const struct hal_gatt_srvc_id *from,
+							struct gatt_id *to)
+{
+	uint128_t uuid128;
+
+	to->inst_id = from->inst_id;
+
+	memcpy(&uuid128.data, &from->uuid, sizeof(uuid128));
+	bt_uuid128_create(&to->uuid, uuid128);
+}
+
 static void handle_client_get_characteristic(const void *buf, uint16_t len)
 {
+	const struct hal_cmd_gatt_client_get_characteristic *cmd = buf;
+	struct generic_call_data *call_data = NULL;
+	struct gatt_srvc_char *chars = NULL;
+	struct gatt_id match_id;
+	struct gatt_device *dev;
+	struct gatt_srvc *srvc;
+	uint8_t status;
+	uint8_t char_status;
+
 	DBG("");
 
+	if (!cmd) {
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	/* search on list by connection id */
+	dev = queue_find(conn_list, match_dev_by_conn_id,
+						INT_TO_PTR(cmd->conn_id));
+	if (!dev) {
+		error("gatt: conn_id=%d not found", cmd->conn_id);
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	hal_srvc_id_to_gatt_id(&cmd->srvc_id, &match_id);
+	srvc = queue_find(dev->services, match_srvc_by_gatt_id, &match_id);
+	if (!srvc) {
+		error("gatt: Service with inst_id: %d not found",
+							match_id.inst_id);
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	call_data = new0(struct generic_call_data, 1);
+	if (!call_data) {
+		error("gatt: Cannot allocate call data");
+		status = HAL_STATUS_FAILED;
+		goto done;
+	}
+
+	call_data->gatt_srvc = srvc;
+	call_data->dev = dev;
+
+	/* Discover all characteristics for services */
+	if (queue_isempty(srvc->chars)) {
+		gatt_discover_char(dev->attrib, srvc->service.range.start,
+					srvc->service.range.end, NULL,
+					gatt_discover_char_cb, call_data);
+	} else {
+		if (cmd->number)
+			chars = queue_find(srvc->chars,
+						match_char_by_higher_inst_id,
+					INT_TO_PTR(cmd->gatt_id[0].inst_id));
+		else
+			chars = queue_peek_head(srvc->chars);
+
+		char_status = HAL_STATUS_SUCCESS;
+
+		if (!chars) {
+			error("gatt: Can't get next char");
+			/* Return last known/given char with failed status if
+			 * no char left.
+			 */
+			chars = queue_find(srvc->chars, match_char_by_inst_id,
+					INT_TO_PTR(cmd->gatt_id[0].inst_id));
+
+			char_status = HAL_STATUS_FAILED;
+
+			/* free generic data if no given char is found */
+			if (!chars)
+				free(call_data);
+		}
+	}
+
+	status = HAL_STATUS_SUCCESS;
+
+done:
 	ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
 					HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC,
-					HAL_STATUS_FAILED);
+					status);
+
+	if (chars) {
+		send_client_char_notify(chars, char_status, call_data);
+		free(call_data);
+	}
 }
 
 static void handle_client_get_descriptor(const void *buf, uint16_t len)
-- 
1.8.5.2

--
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