3 * BlueZ - Bluetooth protocol stack for Linux
5 * Copyright (C) 2010 Nokia Corporation
6 * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
36 #include "lib/bluetooth.h"
38 #include "btio/btio.h"
40 #include "src/shared/util.h"
41 #include "src/shared/att.h"
42 #include "src/shared/queue.h"
43 #include "attrib/gattrib.h"
49 GDestroyNotify destroy;
50 gpointer destroy_user_data;
51 struct queue *callbacks;
54 struct queue *track_ids;
62 struct attrib_callbacks {
64 GAttribResultFunc result_func;
65 GAttribNotifyFunc notify_func;
66 GDestroyNotify destroy_func;
69 uint16_t notify_handle;
72 static bool find_with_org_id(const void *data, const void *user_data)
74 const struct id_pair *p = data;
75 unsigned int orig_id = PTR_TO_UINT(user_data);
77 return (p->org_id == orig_id);
80 static struct id_pair *store_id(GAttrib *attrib, unsigned int org_id,
85 t = new0(struct id_pair, 1);
92 if (queue_push_tail(attrib->track_ids, t))
98 GAttrib *g_attrib_new(GIOChannel *io, guint16 mtu, bool ext_signed)
106 fd = g_io_channel_unix_get_fd(io);
107 attr = new0(GAttrib, 1);
111 g_io_channel_ref(io);
114 attr->att = bt_att_new(fd, ext_signed);
118 bt_att_set_close_on_unref(attr->att, true);
119 g_io_channel_set_close_on_unref(io, FALSE);
121 if (!bt_att_set_mtu(attr->att, mtu))
124 attr->buf = malloc0(mtu);
129 attr->callbacks = queue_new();
130 if (!attr->callbacks)
133 attr->track_ids = queue_new();
134 if (!attr->track_ids)
137 return g_attrib_ref(attr);
141 bt_att_unref(attr->att);
142 g_io_channel_unref(io);
147 GAttrib *g_attrib_ref(GAttrib *attrib)
152 __sync_fetch_and_add(&attrib->ref_count, 1);
154 DBG("%p: g_attrib_ref=%d ", attrib, attrib->ref_count);
159 static void attrib_callbacks_destroy(void *data)
161 struct attrib_callbacks *cb = data;
163 if (cb->destroy_func)
164 cb->destroy_func(cb->user_data);
166 if (queue_remove(cb->parent->track_ids, cb->id))
172 static void attrib_callbacks_remove(void *data)
174 struct attrib_callbacks *cb = data;
176 if (!data || !queue_remove(cb->parent->callbacks, data))
179 attrib_callbacks_destroy(data);
182 void g_attrib_unref(GAttrib *attrib)
187 DBG("%p: g_attrib_unref=%d ", attrib, attrib->ref_count - 1);
189 if (__sync_sub_and_fetch(&attrib->ref_count, 1))
193 attrib->destroy(attrib->destroy_user_data);
195 bt_att_unref(attrib->att);
197 queue_destroy(attrib->callbacks, attrib_callbacks_destroy);
198 queue_destroy(attrib->track_ids, free);
202 g_io_channel_unref(attrib->io);
207 GIOChannel *g_attrib_get_channel(GAttrib *attrib)
215 struct bt_att *g_attrib_get_att(GAttrib *attrib)
223 gboolean g_attrib_set_destroy_function(GAttrib *attrib, GDestroyNotify destroy,
229 attrib->destroy = destroy;
230 attrib->destroy_user_data = user_data;
236 static uint8_t *construct_full_pdu(uint8_t opcode, const void *pdu,
239 uint8_t *buf = malloc0(length + 1);
245 memcpy(buf + 1, pdu, length);
250 static void attrib_callback_result(uint8_t opcode, const void *pdu,
251 uint16_t length, void *user_data)
254 struct attrib_callbacks *cb = user_data;
260 buf = construct_full_pdu(opcode, pdu, length);
264 if (opcode == BT_ATT_OP_ERROR_RSP) {
265 /* Error code is the third byte of the PDU data */
267 status = BT_ATT_ERROR_UNLIKELY;
269 status = ((guint8 *)pdu)[3];
273 cb->result_func(status, buf, length + 1, cb->user_data);
278 static void attrib_callback_notify(uint8_t opcode, const void *pdu,
279 uint16_t length, void *user_data)
282 struct attrib_callbacks *cb = user_data;
284 if (!cb || !cb->notify_func)
287 if (cb->notify_handle != GATTRIB_ALL_HANDLES && length < 2)
290 if (cb->notify_handle != GATTRIB_ALL_HANDLES &&
291 cb->notify_handle != get_le16(pdu))
294 buf = construct_full_pdu(opcode, pdu, length);
298 cb->notify_func(buf, length + 1, cb->user_data);
303 guint g_attrib_send(GAttrib *attrib, guint id, const guint8 *pdu, guint16 len,
304 GAttribResultFunc func, gpointer user_data,
305 GDestroyNotify notify)
307 struct attrib_callbacks *cb = NULL;
308 bt_att_response_func_t response_cb = NULL;
309 bt_att_destroy_func_t destroy_cb = NULL;
310 unsigned int pend_id;
318 if (func || notify) {
319 cb = new0(struct attrib_callbacks, 1);
322 cb->result_func = func;
323 cb->user_data = user_data;
324 cb->destroy_func = notify;
326 queue_push_head(attrib->callbacks, cb);
327 response_cb = attrib_callback_result;
328 destroy_cb = attrib_callbacks_remove;
332 pend_id = bt_att_send(attrib->att, pdu[0], (void *) pdu + 1, len - 1,
333 response_cb, cb, destroy_cb);
336 * We store here pair as it is easier to handle it in response and in
337 * case where user request us to use specific id request - see below.
343 * If user what us to use given id, lets keep track on that so we give
344 * user a possibility to cancel ongoing request.
347 cb->id = store_id(attrib, id, pend_id);
352 gboolean g_attrib_cancel(GAttrib *attrib, guint id)
360 * If request belongs to gattrib and is not yet done it has to be on
361 * the tracking id queue
363 * FIXME: It can happen that on the queue there is id_pair with
364 * given id which was provided by the user. In the same time it might
365 * happen that other attrib user got dynamic allocated req_id with same
366 * value as the one provided by the other user.
367 * In such case there are two clients having same request id and in
368 * this point of time we don't know which one calls cancel. For
369 * now we cancel request in which id was specified by the user.
371 p = queue_remove_if(attrib->track_ids, find_with_org_id,
379 return bt_att_cancel(attrib->att, id);
382 static void cancel_request(void *data, void *user_data)
384 struct id_pair *p = data;
385 GAttrib *attrib = user_data;
387 bt_att_cancel(attrib->att, p->pend_id);
390 gboolean g_attrib_cancel_all(GAttrib *attrib)
395 /* Cancel only request which belongs to gattrib */
396 queue_foreach(attrib->track_ids, cancel_request, attrib);
397 queue_remove_all(attrib->track_ids, NULL, NULL, free);
402 guint g_attrib_register(GAttrib *attrib, guint8 opcode, guint16 handle,
403 GAttribNotifyFunc func, gpointer user_data,
404 GDestroyNotify notify)
406 struct attrib_callbacks *cb = NULL;
411 if (func || notify) {
412 cb = new0(struct attrib_callbacks, 1);
415 cb->notify_func = func;
416 cb->notify_handle = handle;
417 cb->user_data = user_data;
418 cb->destroy_func = notify;
420 queue_push_head(attrib->callbacks, cb);
423 if (opcode == GATTRIB_ALL_REQS)
424 opcode = BT_ATT_ALL_REQUESTS;
426 return bt_att_register(attrib->att, opcode, attrib_callback_notify,
427 cb, attrib_callbacks_remove);
430 uint8_t *g_attrib_get_buffer(GAttrib *attrib, size_t *len)
437 mtu = bt_att_get_mtu(attrib->att);
440 * Clients of this expect a buffer to use.
442 * Pdu encoding in shared/att verifies if whole buffer fits the mtu,
443 * thus we should set the buflen also when mtu is reduced. But we
444 * need to reallocate the buffer only if mtu is larger.
446 if (mtu > attrib->buflen)
447 attrib->buf = g_realloc(attrib->buf, mtu);
449 attrib->buflen = mtu;
450 *len = attrib->buflen;
454 gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu)
460 * Clients of this expect a buffer to use.
462 * Pdu encoding in sharred/att verifies if whole buffer fits the mtu,
463 * thus we should set the buflen also when mtu is reduced. But we
464 * need to reallocate the buffer only if mtu is larger.
466 if (mtu > attrib->buflen)
467 attrib->buf = g_realloc(attrib->buf, mtu);
469 attrib->buflen = mtu;
471 return bt_att_set_mtu(attrib->att, mtu);
474 gboolean g_attrib_unregister(GAttrib *attrib, guint id)
479 return bt_att_unregister(attrib->att, id);
482 gboolean g_attrib_unregister_all(GAttrib *attrib)
487 return bt_att_unregister_all(attrib->att);