Return-Path: MIME-Version: 1.0 In-Reply-To: <2239100.FtFKDhk29O@leonov> References: <1415823779-346-1-git-send-email-jamuraa@chromium.org> <1415823779-346-5-git-send-email-jamuraa@chromium.org> <2239100.FtFKDhk29O@leonov> Date: Wed, 12 Nov 2014 16:49:28 -0800 Message-ID: Subject: Re: [PATCH BlueZ 4/4] GATT shim to src/shared bt_att From: Arman Uguray To: Szymon Janc Cc: BlueZ development Content-Type: text/plain; charset=UTF-8 Sender: linux-bluetooth-owner@vger.kernel.org List-ID: Hi Michael & Szymon, > On Wed, Nov 12, 2014 at 1:14 PM, Szymon Janc wrote: > 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 >> >> #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]. > The PDU returned by bt_att doesn't include the opcode. To convert it into the format expected by GAttrib, he copied "pdu" in to "buf" which is one byte larger. So, the code is technically correct if not very readable. Michael, please add a comment here to explain why this is OK. If you're checking "length", then maybe assign from "pdu[3]" instead of "buf" for consistency, and explain above why you're doing a memcpy to "buf + 1". >> + 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 > -- > To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html Cheers, Arman