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

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

 



Hi Grzegorz,

On Tuesday 25 of March 2014 15:26:30 Grzegorz Kolodziejczyk wrote:
> This adds get characteristic client command handling. If characteristic
> id is given, then next characteristic after given is returned. In case of
> no initial characteristic, first characteristic in given service is
> returned. Characteristics are cached on first get characteristic command
> call for service.
> ---
>  android/gatt.c | 236 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 230 insertions(+), 6 deletions(-)
> 
> diff --git a/android/gatt.c b/android/gatt.c
> index 0414c4f..d2eda9e 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,35 @@ 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 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 +278,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);
> @@ -862,13 +923,176 @@ 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,
> +						struct generic_call_data *data,
> +						uint8_t status)
> +{
> +	struct hal_ev_gatt_client_get_characteristic ev;
> +	bt_uuid_t uuid;
> +
> +	memset(&ev, 0, sizeof(ev));
> +	ev.status = status;
> +
> +	if (chars) {
> +		ev.char_prop = chars->ch.properties;
> +		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));
> +	}
> +
> +	ev.conn_id = data->dev->conn_id;
> +	/* 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));
> +
> +	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;
> +
> +	/* Refresh characteristics cache if already exist */
> +	if (!queue_isempty(q))
> +		queue_remove_all(q, NULL, NULL, free);
> +
> +	for (; characteristics; characteristics = characteristics->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, characteristics->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)) {
> +			error("gatt: Error while caching characteristic");
> +			free(chars);
> +		}
> +	}
> +}
> +
> +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)
> +		return;
> +
> +	if (!queue_isempty(call_data->gatt_srvc->chars))
> +		send_client_char_notify(queue_peek_head(
> +					call_data->gatt_srvc->chars),
> +					call_data, HAL_STATUS_SUCCESS);
> +	else
> +		send_client_char_notify(NULL, call_data, HAL_STATUS_FAILED);
> +
> +	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;
> +	struct gatt_srvc_char *chars = NULL;
> +	struct gatt_id match_id;
> +	struct gatt_device *dev;
> +	struct gatt_srvc *srvc;
> +	uint8_t status;
> +
>  	DBG("");
>  
> +	if (len != sizeof(*cmd) + cmd->number*sizeof(cmd->gatt_id[0])) {
> +		error("Invalid hid set info size (%u bytes), terminating", len);
> +		raise(SIGTERM);
> +		return;
> +	}
> +
> +	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 not cached yet */
> +	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);
> +
> +		status = HAL_STATUS_SUCCESS;
> +		goto done;
> +	}
> +
> +	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);
> +
> +	if (chars)
> +		send_client_char_notify(chars, call_data, HAL_STATUS_SUCCESS);
> +	else
> +		send_client_char_notify(NULL, call_data, HAL_STATUS_FAILED);
> +
> +	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);
> +				HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC, status);
>  }
>  
>  static void handle_client_get_descriptor(const void *buf, uint16_t len)
> 

A heavily modified version of this patch is now pushed. For future please try
avoiding such big single commits as those are quite hard to review. Thanks.

-- 
Best regards, 
Szymon Janc
--
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