All of lore.kernel.org
 help / color / mirror / Atom feed
From: Szymon Janc <szymon.janc@tieto.com>
To: Michael Janssen <jamuraa@chromium.org>
Cc: linux-bluetooth@vger.kernel.org
Subject: Re: [PATCH BlueZ 4/4] GATT shim to src/shared bt_att
Date: Wed, 12 Nov 2014 22:14:05 +0100	[thread overview]
Message-ID: <2239100.FtFKDhk29O@leonov> (raw)
In-Reply-To: <1415823779-346-5-git-send-email-jamuraa@chromium.org>

Hi Michael,

On Wednesday 12 of November 2014 12:22:59 Michael Janssen wrote:
> This patch implements a version of GAttrib which is backed by
> bt_att, which enables the simultaneous use of GAttrib and bt_att.
> 
> This should enable smooth transition of profiles from the GAttrib
> API to the src/shared bt_att API.
> ---
>  attrib/gattrib.c | 733
> ++++++++++++------------------------------------------- 1 file changed, 154
> insertions(+), 579 deletions(-)
> 
> diff --git a/attrib/gattrib.c b/attrib/gattrib.c
> index fa51b6d..b55e437 100644
> --- a/attrib/gattrib.c
> +++ b/attrib/gattrib.c
> @@ -36,227 +36,124 @@
>  #include <bluetooth/bluetooth.h>
> 
>  #include "btio/btio.h"
> -#include "lib/uuid.h"
> -#include "src/shared/util.h"
>  #include "src/log.h"
> -#include "attrib/att.h"
> +#include "src/shared/util.h"
> +#include "src/shared/att.h"
>  #include "attrib/gattrib.h"
> 
> -#define GATT_TIMEOUT 30
> -
>  struct _GAttrib {
> +	int ref_count;
> +	struct bt_att *att;
>  	GIOChannel *io;
> -	int refs;
> -	uint8_t *buf;
> -	size_t buflen;
> -	guint read_watch;
> -	guint write_watch;
> -	guint timeout_watch;
> -	GQueue *requests;
> -	GQueue *responses;
> -	GSList *events;
> -	guint next_cmd_id;
>  	GDestroyNotify destroy;
>  	gpointer destroy_user_data;
> -	bool stale;
> +	GQueue *callbacks;
> +	uint8_t *buf;
> +	int buflen;
>  };
> 
> -struct command {
> -	guint id;
> -	guint8 opcode;
> -	guint8 *pdu;
> -	guint16 len;
> -	guint8 expected;
> -	bool sent;
> -	GAttribResultFunc func;
> -	gpointer user_data;
> -	GDestroyNotify notify;
> -};
> 
> -struct event {
> -	guint id;
> -	guint8 expected;
> -	guint16 handle;
> -	GAttribNotifyFunc func;
> +struct attrib_callbacks {
> +	GAttribResultFunc result_func;
> +	GAttribNotifyFunc notify_func;
> +	GDestroyNotify destroy_func;
>  	gpointer user_data;
> -	GDestroyNotify notify;
> +	GAttrib *parent;
> +	uint16_t notify_handle;
>  };
> 
> -static guint8 opcode2expected(guint8 opcode)
> +GAttrib *g_attrib_new(GIOChannel *io, guint16 mtu)
>  {
> -	switch (opcode) {
> -	case ATT_OP_MTU_REQ:
> -		return ATT_OP_MTU_RESP;
> -
> -	case ATT_OP_FIND_INFO_REQ:
> -		return ATT_OP_FIND_INFO_RESP;
> -
> -	case ATT_OP_FIND_BY_TYPE_REQ:
> -		return ATT_OP_FIND_BY_TYPE_RESP;
> -
> -	case ATT_OP_READ_BY_TYPE_REQ:
> -		return ATT_OP_READ_BY_TYPE_RESP;
> -
> -	case ATT_OP_READ_REQ:
> -		return ATT_OP_READ_RESP;
> -
> -	case ATT_OP_READ_BLOB_REQ:
> -		return ATT_OP_READ_BLOB_RESP;
> -
> -	case ATT_OP_READ_MULTI_REQ:
> -		return ATT_OP_READ_MULTI_RESP;
> +	gint fd;
> +	GAttrib *attr;
> 
> -	case ATT_OP_READ_BY_GROUP_REQ:
> -		return ATT_OP_READ_BY_GROUP_RESP;
> +	if (!io)
> +		return NULL;
> 
> -	case ATT_OP_WRITE_REQ:
> -		return ATT_OP_WRITE_RESP;
> +	fd = g_io_channel_unix_get_fd(io);
> +	attr = new0(GAttrib, 1);
> +	if (!attr)
> +		return NULL;
> 
> -	case ATT_OP_PREP_WRITE_REQ:
> -		return ATT_OP_PREP_WRITE_RESP;
> +	g_io_channel_ref(io);
> +	attr->io = io;
> 
> -	case ATT_OP_EXEC_WRITE_REQ:
> -		return ATT_OP_EXEC_WRITE_RESP;
> +	attr->att = bt_att_new(fd);
> +	if (!attr->att)
> +		goto fail;
> 
> -	case ATT_OP_HANDLE_IND:
> -		return ATT_OP_HANDLE_CNF;
> -	}
> +	attr->buf = g_malloc0(mtu);
> +	attr->buflen = mtu;
> +	if (!attr->buf)
> +		goto fail;
> 
> -	return 0;
> -}
> +	attr->callbacks = g_queue_new();
> +	if (!attr->callbacks)
> +		goto fail;
> 
> -static bool is_response(guint8 opcode)
> -{
> -	switch (opcode) {
> -	case ATT_OP_ERROR:
> -	case ATT_OP_MTU_RESP:
> -	case ATT_OP_FIND_INFO_RESP:
> -	case ATT_OP_FIND_BY_TYPE_RESP:
> -	case ATT_OP_READ_BY_TYPE_RESP:
> -	case ATT_OP_READ_RESP:
> -	case ATT_OP_READ_BLOB_RESP:
> -	case ATT_OP_READ_MULTI_RESP:
> -	case ATT_OP_READ_BY_GROUP_RESP:
> -	case ATT_OP_WRITE_RESP:
> -	case ATT_OP_PREP_WRITE_RESP:
> -	case ATT_OP_EXEC_WRITE_RESP:
> -	case ATT_OP_HANDLE_CNF:
> -		return true;
> -	}
> +	return g_attrib_ref(attr);
> 
> -	return false;
> -}
> -
> -static bool is_request(guint8 opcode)
> -{
> -	switch (opcode) {
> -	case ATT_OP_MTU_REQ:
> -	case ATT_OP_FIND_INFO_REQ:
> -	case ATT_OP_FIND_BY_TYPE_REQ:
> -	case ATT_OP_READ_BY_TYPE_REQ:
> -	case ATT_OP_READ_REQ:
> -	case ATT_OP_READ_BLOB_REQ:
> -	case ATT_OP_READ_MULTI_REQ:
> -	case ATT_OP_READ_BY_GROUP_REQ:
> -	case ATT_OP_WRITE_REQ:
> -	case ATT_OP_WRITE_CMD:
> -	case ATT_OP_PREP_WRITE_REQ:
> -	case ATT_OP_EXEC_WRITE_REQ:
> -		return true;
> -	}
> -
> -	return false;
> +fail:
> +	free(attr->buf);
> +	bt_att_unref(attr->att);
> +	g_io_channel_unref(io);
> +	free(attr);
> +	return NULL;
>  }
> 
>  GAttrib *g_attrib_ref(GAttrib *attrib)
>  {
> -	int refs;
> -
>  	if (!attrib)
>  		return NULL;
> 
> -	refs = __sync_add_and_fetch(&attrib->refs, 1);
> +	__sync_fetch_and_add(&attrib->ref_count, 1);
> 
> -	DBG("%p: ref=%d", attrib, refs);
> +	DBG("%p: g_attrib_ref=%d ", attrib, attrib->ref_count);
> 
>  	return attrib;
>  }
> 
> -static void command_destroy(struct command *cmd)
> +static void attrib_callbacks_destroy(void *user_data)
>  {
> -	if (cmd->notify)
> -		cmd->notify(cmd->user_data);
> +	struct attrib_callbacks *cb;
> 
> -	g_free(cmd->pdu);
> -	g_free(cmd);
> -}
> +	cb = (struct attrib_callbacks *)user_data;

This cast shouldn't be needed, user_data is void*.

> +	if (!user_data || !g_queue_remove(cb->parent->callbacks, user_data))
> +		return;
> 
> -static void event_destroy(struct event *evt)
> -{
> -	if (evt->notify)
> -		evt->notify(evt->user_data);
> +	if (cb->destroy_func)
> +		cb->destroy_func(cb->user_data);
> 
> -	g_free(evt);
> +	free(user_data);
>  }
> 
> -static void attrib_destroy(GAttrib *attrib)
> +void g_attrib_unref(GAttrib *attrib)
>  {
> -	GSList *l;
> -	struct command *c;
> -
> -	while ((c = g_queue_pop_head(attrib->requests)))
> -		command_destroy(c);
> -
> -	while ((c = g_queue_pop_head(attrib->responses)))
> -		command_destroy(c);
> -
> -	g_queue_free(attrib->requests);
> -	attrib->requests = NULL;
> -
> -	g_queue_free(attrib->responses);
> -	attrib->responses = NULL;
> +	struct attrib_callbacks *cb;
> 
> -	for (l = attrib->events; l; l = l->next)
> -		event_destroy(l->data);
> -
> -	g_slist_free(attrib->events);
> -	attrib->events = NULL;
> -
> -	if (attrib->timeout_watch > 0)
> -		g_source_remove(attrib->timeout_watch);
> -
> -	if (attrib->write_watch > 0)
> -		g_source_remove(attrib->write_watch);
> -
> -	if (attrib->read_watch > 0)
> -		g_source_remove(attrib->read_watch);
> +	if (!attrib)
> +		return;
> 
> -	if (attrib->io)
> -		g_io_channel_unref(attrib->io);
> +	DBG("%p: g_attrib_unref=%d ", attrib, attrib->ref_count-1);
> 
> -	g_free(attrib->buf);
> +	if (__sync_sub_and_fetch(&attrib->ref_count, 1))
> +		return;
> 
>  	if (attrib->destroy)
>  		attrib->destroy(attrib->destroy_user_data);
> 
> -	g_free(attrib);
> -}
> +	while ((cb = g_queue_peek_head(attrib->callbacks)))
> +		attrib_callbacks_destroy(cb);
> 
> -void g_attrib_unref(GAttrib *attrib)
> -{
> -	int refs;
> +	g_queue_free(attrib->callbacks);
> 
> -	if (!attrib)
> -		return;
> -
> -	refs = __sync_sub_and_fetch(&attrib->refs, 1);
> +	g_free(attrib->buf);
> 
> -	DBG("%p: ref=%d", attrib, refs);
> +	bt_att_unref(attrib->att);
> 
> -	if (refs > 0)
> -		return;
> +	g_io_channel_unref(attrib->io);
> 
> -	attrib_destroy(attrib);
> +	g_free(attrib);
>  }
> 
>  GIOChannel *g_attrib_get_channel(GAttrib *attrib)
> @@ -270,7 +167,7 @@ GIOChannel *g_attrib_get_channel(GAttrib *attrib)
>  gboolean g_attrib_set_destroy_function(GAttrib *attrib,
>  		GDestroyNotify destroy, gpointer user_data)
>  {
> -	if (attrib == NULL)
> +	if (!attrib)
>  		return FALSE;
> 
>  	attrib->destroy = destroy;
> @@ -279,380 +176,130 @@ gboolean g_attrib_set_destroy_function(GAttrib
> *attrib, return TRUE;
>  }
> 
> -static gboolean disconnect_timeout(gpointer data)
> -{
> -	struct _GAttrib *attrib = data;
> -	struct command *c;
> -
> -	g_attrib_ref(attrib);
> -
> -	c = g_queue_pop_head(attrib->requests);
> -	if (c == NULL)
> -		goto done;
> -
> -	if (c->func)
> -		c->func(ATT_ECODE_TIMEOUT, NULL, 0, c->user_data);
> -
> -	command_destroy(c);
> -
> -	while ((c = g_queue_pop_head(attrib->requests))) {
> -		if (c->func)
> -			c->func(ATT_ECODE_ABORTED, NULL, 0, c->user_data);
> -		command_destroy(c);
> -	}
> 
> -done:
> -	attrib->stale = true;
> -
> -	g_attrib_unref(attrib);
> -
> -	return FALSE;
> -}
> -
> -static gboolean can_write_data(GIOChannel *io, GIOCondition cond,
> -								gpointer data)
> +static void attrib_callback_result(uint8_t opcode, const void *pdu,
> +				   uint16_t length, void *user_data)
>  {
> -	struct _GAttrib *attrib = data;
> -	struct command *cmd;
> -	GError *gerr = NULL;
> -	gsize len;
> -	GIOStatus iostat;
> -	GQueue *queue;
> -
> -	if (attrib->stale)
> -		return FALSE;
> -
> -	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
> -		return FALSE;
> -
> -	queue = attrib->responses;
> -	cmd = g_queue_peek_head(queue);
> -	if (cmd == NULL) {
> -		queue = attrib->requests;
> -		cmd = g_queue_peek_head(queue);
> -	}
> -	if (cmd == NULL)
> -		return FALSE;
> -
> -	/*
> -	 * Verify that we didn't already send this command. This can only
> -	 * happen with elementes from attrib->requests.
> -	 */
> -	if (cmd->sent)
> -		return FALSE;
> +	uint8_t *buf;
> +	struct attrib_callbacks *cb = user_data;
> +	guint8 status = 0;
> 
> -	iostat = g_io_channel_write_chars(io, (char *) cmd->pdu, cmd->len,
> -								&len, &gerr);
> -	if (iostat != G_IO_STATUS_NORMAL) {
> -		if (gerr) {
> -			error("%s", gerr->message);
> -			g_error_free(gerr);
> -		}
> +	if (!cb)
> +		return;
> 
> -		return FALSE;
> -	}
> +	buf = g_malloc0(length+1);
> +	if (!buf)
> +		return;

g_malloc0() will abort if memory allocation failed, should this be 
g_malloc0_try() ?  Also if this is not due to glib API requirement we should 
be using malloc0().

Also there should be spaces around +. (this is affecting multiple places)

> 
> -	if (cmd->expected == 0) {
> -		g_queue_pop_head(queue);
> -		command_destroy(cmd);
> +	buf[0] = opcode;
> +	memcpy(buf+1, pdu, length);
> 
> -		return TRUE;
> +	if (opcode == BT_ATT_OP_ERROR_RSP) {
> +		if (length < 4)

Should be < 5 if we are accessing buf[4].

> +			status = BT_ATT_ERROR_UNLIKELY;
> +		else
> +			status = buf[4];
>  	}
> 
> -	cmd->sent = true;
> -
> -	if (attrib->timeout_watch == 0)
> -		attrib->timeout_watch = g_timeout_add_seconds(GATT_TIMEOUT,
> -						disconnect_timeout, attrib);
> +	if (cb->result_func)
> +		cb->result_func(status, buf, length+1, cb->user_data);
> 
> -	return FALSE;
> +	g_free(buf);
>  }
> 
> -static void destroy_sender(gpointer data)
> -{
> -	struct _GAttrib *attrib = data;
> 
> -	attrib->write_watch = 0;
> -	g_attrib_unref(attrib);
> -}
> -
> -static void wake_up_sender(struct _GAttrib *attrib)
> -{
> -	if (attrib->write_watch > 0)
> -		return;
> -
> -	attrib = g_attrib_ref(attrib);
> -	attrib->write_watch = g_io_add_watch_full(attrib->io,
> -				G_PRIORITY_DEFAULT, G_IO_OUT,
> -				can_write_data, attrib, destroy_sender);
> -}
> -
> -static bool match_event(struct event *evt, const uint8_t *pdu, gsize len)
> -{
> -	guint16 handle;
> -
> -	if (is_request(pdu[0]) && evt->expected == GATTRIB_ALL_REQS)
> -		return true;
> -
> -	if (evt->expected == pdu[0] && evt->handle == GATTRIB_ALL_HANDLES)
> -		return true;
> -
> -	if (len < 3)
> -		return false;
> -
> -	handle = get_le16(&pdu[1]);
> -
> -	if (evt->expected == pdu[0] && evt->handle == handle)
> -		return true;
> -
> -	return false;
> -}
> -
> -static gboolean received_data(GIOChannel *io, GIOCondition cond, gpointer
> data) +static void attrib_callback_notify(uint8_t opcode, const void *pdu,
> +				uint16_t length, void *user_data)
>  {
> -	struct _GAttrib *attrib = data;
> -	struct command *cmd = NULL;
> -	GSList *l;
> -	uint8_t buf[512], status;
> -	gsize len;
> -	GIOStatus iostat;
> -
> -	if (attrib->stale)
> -		return FALSE;
> -
> -	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
> -		struct command *c;
> -
> -		while ((c = g_queue_pop_head(attrib->requests))) {
> -			if (c->func)
> -				c->func(ATT_ECODE_IO, NULL, 0, c->user_data);
> -			command_destroy(c);
> -		}
> -
> -		attrib->read_watch = 0;
> -
> -		return FALSE;
> -	}
> -
> -	memset(buf, 0, sizeof(buf));
> -
> -	iostat = g_io_channel_read_chars(io, (char *) buf, sizeof(buf),
> -								&len, NULL);
> -	if (iostat != G_IO_STATUS_NORMAL) {
> -		status = ATT_ECODE_IO;
> -		goto done;
> -	}
> -
> -	for (l = attrib->events; l; l = l->next) {
> -		struct event *evt = l->data;
> -
> -		if (match_event(evt, buf, len))
> -			evt->func(buf, len, evt->user_data);
> -	}
> -
> -	if (!is_response(buf[0]))
> -		return TRUE;
> -
> -	if (attrib->timeout_watch > 0) {
> -		g_source_remove(attrib->timeout_watch);
> -		attrib->timeout_watch = 0;
> -	}
> -
> -	cmd = g_queue_pop_head(attrib->requests);
> -	if (cmd == NULL) {
> -		/* Keep the watch if we have events to report */
> -		return attrib->events != NULL;
> -	}
> -
> -	if (buf[0] == ATT_OP_ERROR) {
> -		status = buf[4];
> -		goto done;
> -	}
> -
> -	if (cmd->expected != buf[0]) {
> -		status = ATT_ECODE_IO;
> -		goto done;
> -	}
> -
> -	status = 0;
> -
> -done:
> -	if (!g_queue_is_empty(attrib->requests) ||
> -					!g_queue_is_empty(attrib->responses))
> -		wake_up_sender(attrib);
> -
> -	if (cmd) {
> -		if (cmd->func)
> -			cmd->func(status, buf, len, cmd->user_data);
> -
> -		command_destroy(cmd);
> -	}
> +	uint8_t *buf;
> +	struct attrib_callbacks *cb = user_data;
> 
> -	return TRUE;
> -}
> +	if (!cb)
> +		return;
> 
> -GAttrib *g_attrib_new(GIOChannel *io, guint16 mtu)
> -{
> -	struct _GAttrib *attrib;
> +	if (cb->notify_func == NULL)

Use ! for pointer checking. I know that we are not fully following this in 
full code base but lets be consistent in new code. 

> +		return;
> 
> -	g_io_channel_set_encoding(io, NULL, NULL);
> -	g_io_channel_set_buffered(io, FALSE);
> +	if (cb->notify_handle != GATTRIB_ALL_HANDLES && length < 2)
> +		return;
> 
> -	attrib = g_try_new0(struct _GAttrib, 1);
> -	if (attrib == NULL)
> -		return NULL;
> +	if (cb->notify_handle != GATTRIB_ALL_HANDLES &&
> +					     cb->notify_handle != get_le16(pdu))
> +		return;
> 
> -	attrib->buf = g_malloc0(mtu);
> -	attrib->buflen = mtu;
> +	buf = g_malloc0(length+1);
> +	if (!buf)
> +		return;

Same here:  use malloc0() or g_mallo0_try().

> 
> -	attrib->io = g_io_channel_ref(io);
> -	attrib->requests = g_queue_new();
> -	attrib->responses = g_queue_new();
> +	buf[0] = opcode;
> +	memcpy(buf+1, pdu, length);
> 
> -	attrib->read_watch = g_io_add_watch(attrib->io,
> -			G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
> -			received_data, attrib);
> +	cb->notify_func(buf, length+1, cb->user_data);
> 
> -	return g_attrib_ref(attrib);
> +	g_free(buf);
>  }
> 
>  guint g_attrib_send(GAttrib *attrib, guint id, const guint8 *pdu, guint16
> len, GAttribResultFunc func, gpointer user_data,
>  			GDestroyNotify notify)
>  {
> -	struct command *c;
> -	GQueue *queue;
> -	uint8_t opcode;
> -
> -	if (attrib->stale)
> -		return 0;
> +	struct attrib_callbacks *cb = NULL;
> +	bt_att_response_func_t response_cb = NULL;
> +	bt_att_destroy_func_t destroy_cb = NULL;
> 
> -	c = g_try_new0(struct command, 1);
> -	if (c == NULL)
> +	if (!pdu || !len)
>  		return 0;
> 
> -	opcode = pdu[0];
> -
> -	c->opcode = opcode;
> -	c->expected = opcode2expected(opcode);
> -	c->pdu = g_malloc(len);
> -	memcpy(c->pdu, pdu, len);
> -	c->len = len;
> -	c->func = func;
> -	c->user_data = user_data;
> -	c->notify = notify;
> -
> -	if (is_response(opcode))
> -		queue = attrib->responses;
> -	else
> -		queue = attrib->requests;
> -
> -	if (id) {
> -		c->id = id;
> -		if (!is_response(opcode))
> -			g_queue_push_head(queue, c);
> -		else
> -			/* Don't re-order responses even if an ID is given */
> -			g_queue_push_tail(queue, c);
> -	} else {
> -		c->id = ++attrib->next_cmd_id;
> -		g_queue_push_tail(queue, c);
> +	if (func || notify) {
> +		cb = new0(struct attrib_callbacks, 1);
> +		if (cb == 0)

if (!cb) ...  (this is affecting multiple places)

> +			return 0;
> +		cb->result_func = func;
> +		cb->user_data = user_data;
> +		cb->destroy_func = notify;
> +		cb->parent = attrib;
> +		g_queue_push_head(attrib->callbacks, cb);
> +		response_cb = attrib_callback_result;
> +		destroy_cb = attrib_callbacks_destroy;
>  	}
> 
> -	/*
> -	 * If a command was added to the queue and it was empty before, wake up
> -	 * the sender. If the sender was already woken up by the second queue,
> -	 * wake_up_sender will just return.
> -	 */
> -	if (g_queue_get_length(queue) == 1)
> -		wake_up_sender(attrib);
> -
> -	return c->id;
> -}
> -
> -static int command_cmp_by_id(gconstpointer a, gconstpointer b)
> -{
> -	const struct command *cmd = a;
> -	guint id = GPOINTER_TO_UINT(b);
> -
> -	return cmd->id - id;
> +	return bt_att_send(attrib->att, pdu[0], (void *)pdu+1, len-1,
> +						   response_cb, cb, destroy_cb);
>  }
> 
>  gboolean g_attrib_cancel(GAttrib *attrib, guint id)
>  {
> -	GList *l = NULL;
> -	struct command *cmd;
> -	GQueue *queue;
> -
> -	if (attrib == NULL)
> -		return FALSE;
> -
> -	queue = attrib->requests;
> -	if (queue)
> -		l = g_queue_find_custom(queue, GUINT_TO_POINTER(id),
> -					command_cmp_by_id);
> -	if (l == NULL) {
> -		queue = attrib->responses;
> -		if (!queue)
> -			return FALSE;
> -		l = g_queue_find_custom(queue, GUINT_TO_POINTER(id),
> -					command_cmp_by_id);
> -	}
> -
> -	if (l == NULL)
> -		return FALSE;
> -
> -	cmd = l->data;
> -
> -	if (cmd == g_queue_peek_head(queue) && cmd->sent)
> -		cmd->func = NULL;
> -	else {
> -		g_queue_remove(queue, cmd);
> -		command_destroy(cmd);
> -	}
> -
> -	return TRUE;
> +	return bt_att_cancel(attrib->att, id);
>  }
> 
> -static gboolean cancel_all_per_queue(GQueue *queue)
> +gboolean g_attrib_cancel_all(GAttrib *attrib)
>  {
> -	struct command *c, *head = NULL;
> -	gboolean first = TRUE;
> -
> -	if (queue == NULL)
> -		return FALSE;
> -
> -	while ((c = g_queue_pop_head(queue))) {
> -		if (first && c->sent) {
> -			/* If the command was sent ignore its callback ... */
> -			c->func = NULL;
> -			head = c;
> -			continue;
> -		}
> -
> -		first = FALSE;
> -		command_destroy(c);
> -	}
> -
> -	if (head) {
> -		/* ... and put it back in the queue */
> -		g_queue_push_head(queue, head);
> -	}
> -
> -	return TRUE;
> +	return bt_att_cancel_all(attrib->att);
>  }
> 
> -gboolean g_attrib_cancel_all(GAttrib *attrib)
> +guint g_attrib_register(GAttrib *attrib, guint8 opcode, guint16 handle,
> +				GAttribNotifyFunc func, gpointer user_data,
> +				GDestroyNotify notify)
>  {
> -	gboolean ret;
> -
> -	if (attrib == NULL)
> -		return FALSE;
> +	struct attrib_callbacks *cb = NULL;
> +
> +	if (func || notify) {
> +		cb = new0(struct attrib_callbacks, 1);
> +		if (cb == 0)
> +			return 0;
> +		cb->notify_func = func;
> +		cb->notify_handle = handle;
> +		cb->user_data = user_data;
> +		cb->destroy_func = notify;
> +		cb->parent = attrib;
> +		g_queue_push_head(attrib->callbacks, cb);
> +	}
> 
> -	ret = cancel_all_per_queue(attrib->requests);
> -	ret = cancel_all_per_queue(attrib->responses) && ret;
> +	if (opcode == GATTRIB_ALL_REQS)
> +		opcode = BT_ATT_ALL_REQUESTS;
> 
> -	return ret;
> +	return bt_att_register(attrib->att, opcode, attrib_callback_notify,
> +			       cb, attrib_callbacks_destroy);
>  }
> 
>  uint8_t *g_attrib_get_buffer(GAttrib *attrib, size_t *len)
> @@ -661,98 +308,26 @@ uint8_t *g_attrib_get_buffer(GAttrib *attrib, size_t
> *len) return NULL;
> 
>  	*len = attrib->buflen;
> -
>  	return attrib->buf;
>  }
> 
>  gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu)
>  {
> -	if (mtu < ATT_DEFAULT_LE_MTU)
> -		return FALSE;
> -
> -	attrib->buf = g_realloc(attrib->buf, mtu);
> -
> -	attrib->buflen = mtu;
> -
> -	return TRUE;
> -}
> -
> -guint g_attrib_register(GAttrib *attrib, guint8 opcode, guint16 handle,
> -				GAttribNotifyFunc func, gpointer user_data,
> -				GDestroyNotify notify)
> -{
> -	static guint next_evt_id = 0;
> -	struct event *event;
> -
> -	event = g_try_new0(struct event, 1);
> -	if (event == NULL)
> -		return 0;
> -
> -	event->expected = opcode;
> -	event->handle = handle;
> -	event->func = func;
> -	event->user_data = user_data;
> -	event->notify = notify;
> -	event->id = ++next_evt_id;
> -
> -	attrib->events = g_slist_append(attrib->events, event);
> -
> -	return event->id;
> -}
> -
> -static int event_cmp_by_id(gconstpointer a, gconstpointer b)
> -{
> -	const struct event *evt = a;
> -	guint id = GPOINTER_TO_UINT(b);
> +	/* Clients of this expect a buffer to use. */
> +	if (mtu > attrib->buflen) {
> +		attrib->buf = g_realloc(attrib->buf, mtu);
> +		attrib->buflen = mtu;
> +	}
> 
> -	return evt->id - id;
> +	return bt_att_set_mtu(attrib->att, mtu);
>  }
> 
>  gboolean g_attrib_unregister(GAttrib *attrib, guint id)
>  {
> -	struct event *evt;
> -	GSList *l;
> -
> -	if (id == 0) {
> -		warn("%s: invalid id", __func__);
> -		return FALSE;
> -	}
> -
> -	l = g_slist_find_custom(attrib->events, GUINT_TO_POINTER(id),
> -							event_cmp_by_id);
> -	if (l == NULL)
> -		return FALSE;
> -
> -	evt = l->data;
> -
> -	attrib->events = g_slist_remove(attrib->events, evt);
> -
> -	if (evt->notify)
> -		evt->notify(evt->user_data);
> -
> -	g_free(evt);
> -
> -	return TRUE;
> +	return bt_att_unregister(attrib->att, id);
>  }
> 
>  gboolean g_attrib_unregister_all(GAttrib *attrib)
>  {
> -	GSList *l;
> -
> -	if (attrib->events == NULL)
> -		return FALSE;
> -
> -	for (l = attrib->events; l; l = l->next) {
> -		struct event *evt = l->data;
> -
> -		if (evt->notify)
> -			evt->notify(evt->user_data);
> -
> -		g_free(evt);
> -	}
> -
> -	g_slist_free(attrib->events);
> -	attrib->events = NULL;
> -
> -	return TRUE;
> +	return bt_att_unregister_all(attrib->att);
>  }

-- 
BR
Szymon Janc

  reply	other threads:[~2014-11-12 21:14 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-11-12 20:22 [PATCH BlueZ 0/4] android fixes and gattrib shim Michael Janssen
2014-11-12 20:22 ` [PATCH BlueZ 1/4] android/tester-gatt: DRY/bugfix search single PDUs Michael Janssen
2014-11-12 20:51   ` Szymon Janc
2014-11-13 15:30     ` Michael Janssen
2014-11-13 16:16       ` Luiz Augusto von Dentz
2014-11-12 20:22 ` [PATCH BlueZ 2/4] android/tester-gatt: deduplicate read-by-type PDUs Michael Janssen
2014-11-12 20:22 ` [PATCH BlueZ 3/4] android/gatt: dummy callback for indications Michael Janssen
2014-11-12 20:56   ` Szymon Janc
2014-11-12 20:22 ` [PATCH BlueZ 4/4] GATT shim to src/shared bt_att Michael Janssen
2014-11-12 21:14   ` Szymon Janc [this message]
2014-11-13  0:49     ` Arman Uguray
2014-11-13 14:05       ` Luiz Augusto von Dentz
  -- strict thread matches above, loose matches on Subject: below --
2014-11-04 21:19 [PATCH BlueZ 0/4] GAttrib cleanup and switch to bt_att Michael Janssen
2014-11-04 21:19 ` [PATCH BlueZ 4/4] GATT shim to src/shared bt_att Michael Janssen
2014-11-05 14:49   ` Luiz Augusto von Dentz
2014-11-05 15:17     ` Michael Janssen
2014-11-05 20:30       ` Luiz Augusto von Dentz

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=2239100.FtFKDhk29O@leonov \
    --to=szymon.janc@tieto.com \
    --cc=jamuraa@chromium.org \
    --cc=linux-bluetooth@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.