1 /*
2 *
3 * BlueZ - Bluetooth protocol stack for Linux
4 *
5 * Copyright (C) 2010 Nokia Corporation
6 * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
7 *
8 *
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.
13 *
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.
18 *
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
22 *
23 */
24
25 #include <stdint.h>
26 #include <string.h>
27 #include <glib.h>
28
29 #include <stdio.h>
30
31 #include <bluetooth/bluetooth.h>
32 #include <bluetooth/uuid.h>
33
34 #include "att.h"
35 #include "btio.h"
36 #include "gattrib.h"
37
38 #define GATT_TIMEOUT 30
39
40 struct _GAttrib {
41 GIOChannel *io;
42 gint refs;
43 uint8_t *buf;
44 int buflen;
45 guint read_watch;
46 guint write_watch;
47 guint timeout_watch;
48 GQueue *queue;
49 GSList *events;
50 guint next_cmd_id;
51 guint next_evt_id;
52 GDestroyNotify destroy;
53 GAttribDisconnectFunc disconnect;
54 gpointer destroy_user_data;
55 gpointer disc_user_data;
56 };
57
58 struct command {
59 guint id;
60 guint8 opcode;
61 guint8 *pdu;
62 guint16 len;
63 guint8 expected;
64 gboolean sent;
65 GAttribResultFunc func;
66 gpointer user_data;
67 GDestroyNotify notify;
68 };
69
70 struct event {
71 guint id;
72 guint8 expected;
73 GAttribNotifyFunc func;
74 gpointer user_data;
75 GDestroyNotify notify;
76 };
77
opcode2expected(guint8 opcode)78 static guint8 opcode2expected(guint8 opcode)
79 {
80 switch (opcode) {
81 case ATT_OP_MTU_REQ:
82 return ATT_OP_MTU_RESP;
83
84 case ATT_OP_FIND_INFO_REQ:
85 return ATT_OP_FIND_INFO_RESP;
86
87 case ATT_OP_FIND_BY_TYPE_REQ:
88 return ATT_OP_FIND_BY_TYPE_RESP;
89
90 case ATT_OP_READ_BY_TYPE_REQ:
91 return ATT_OP_READ_BY_TYPE_RESP;
92
93 case ATT_OP_READ_REQ:
94 return ATT_OP_READ_RESP;
95
96 case ATT_OP_READ_BLOB_REQ:
97 return ATT_OP_READ_BLOB_RESP;
98
99 case ATT_OP_READ_MULTI_REQ:
100 return ATT_OP_READ_MULTI_RESP;
101
102 case ATT_OP_READ_BY_GROUP_REQ:
103 return ATT_OP_READ_BY_GROUP_RESP;
104
105 case ATT_OP_WRITE_REQ:
106 return ATT_OP_WRITE_RESP;
107
108 case ATT_OP_PREP_WRITE_REQ:
109 return ATT_OP_PREP_WRITE_RESP;
110
111 case ATT_OP_EXEC_WRITE_REQ:
112 return ATT_OP_EXEC_WRITE_RESP;
113
114 case ATT_OP_HANDLE_IND:
115 return ATT_OP_HANDLE_CNF;
116 }
117
118 return 0;
119 }
120
is_response(guint8 opcode)121 static gboolean is_response(guint8 opcode)
122 {
123 switch (opcode) {
124 case ATT_OP_ERROR:
125 case ATT_OP_MTU_RESP:
126 case ATT_OP_FIND_INFO_RESP:
127 case ATT_OP_FIND_BY_TYPE_RESP:
128 case ATT_OP_READ_BY_TYPE_RESP:
129 case ATT_OP_READ_RESP:
130 case ATT_OP_READ_BLOB_RESP:
131 case ATT_OP_READ_MULTI_RESP:
132 case ATT_OP_READ_BY_GROUP_RESP:
133 case ATT_OP_WRITE_RESP:
134 case ATT_OP_PREP_WRITE_RESP:
135 case ATT_OP_EXEC_WRITE_RESP:
136 case ATT_OP_HANDLE_CNF:
137 return TRUE;
138 }
139
140 return FALSE;
141 }
142
g_attrib_ref(GAttrib * attrib)143 GAttrib *g_attrib_ref(GAttrib *attrib)
144 {
145 if (!attrib)
146 return NULL;
147
148 g_atomic_int_inc(&attrib->refs);
149
150 return attrib;
151 }
152
command_destroy(struct command * cmd)153 static void command_destroy(struct command *cmd)
154 {
155 if (cmd->notify)
156 cmd->notify(cmd->user_data);
157
158 g_free(cmd->pdu);
159 g_free(cmd);
160 }
161
event_destroy(struct event * evt)162 static void event_destroy(struct event *evt)
163 {
164 if (evt->notify)
165 evt->notify(evt->user_data);
166
167 g_free(evt);
168 }
169
attrib_destroy(GAttrib * attrib)170 static void attrib_destroy(GAttrib *attrib)
171 {
172 GSList *l;
173 struct command *c;
174
175 while ((c = g_queue_pop_head(attrib->queue)))
176 command_destroy(c);
177
178 g_queue_free(attrib->queue);
179 attrib->queue = NULL;
180
181 for (l = attrib->events; l; l = l->next)
182 event_destroy(l->data);
183
184 g_slist_free(attrib->events);
185 attrib->events = NULL;
186
187 if (attrib->timeout_watch > 0)
188 g_source_remove(attrib->timeout_watch);
189
190 if (attrib->write_watch > 0)
191 g_source_remove(attrib->write_watch);
192
193 if (attrib->read_watch > 0) {
194 g_source_remove(attrib->read_watch);
195 g_io_channel_unref(attrib->io);
196 }
197
198 g_free(attrib->buf);
199
200 if (attrib->destroy)
201 attrib->destroy(attrib->destroy_user_data);
202
203 g_free(attrib);
204 }
205
g_attrib_unref(GAttrib * attrib)206 void g_attrib_unref(GAttrib *attrib)
207 {
208 if (!attrib)
209 return;
210
211 if (g_atomic_int_dec_and_test(&attrib->refs) == FALSE)
212 return;
213
214 attrib_destroy(attrib);
215 }
216
g_attrib_get_channel(GAttrib * attrib)217 GIOChannel *g_attrib_get_channel(GAttrib *attrib)
218 {
219 if (!attrib)
220 return NULL;
221
222 return attrib->io;
223 }
224
g_attrib_set_disconnect_function(GAttrib * attrib,GAttribDisconnectFunc disconnect,gpointer user_data)225 gboolean g_attrib_set_disconnect_function(GAttrib *attrib,
226 GAttribDisconnectFunc disconnect, gpointer user_data)
227 {
228 if (attrib == NULL)
229 return FALSE;
230
231 attrib->disconnect = disconnect;
232 attrib->disc_user_data = user_data;
233
234 return TRUE;
235 }
236
g_attrib_set_destroy_function(GAttrib * attrib,GDestroyNotify destroy,gpointer user_data)237 gboolean g_attrib_set_destroy_function(GAttrib *attrib,
238 GDestroyNotify destroy, gpointer user_data)
239 {
240 if (attrib == NULL)
241 return FALSE;
242
243 attrib->destroy = destroy;
244 attrib->destroy_user_data = user_data;
245
246 return TRUE;
247 }
248
disconnect_timeout(gpointer data)249 static gboolean disconnect_timeout(gpointer data)
250 {
251 struct _GAttrib *attrib = data;
252
253 attrib_destroy(attrib);
254
255 return FALSE;
256 }
257
can_write_data(GIOChannel * io,GIOCondition cond,gpointer data)258 static gboolean can_write_data(GIOChannel *io, GIOCondition cond,
259 gpointer data)
260 {
261 struct _GAttrib *attrib = data;
262 struct command *cmd;
263 GError *gerr = NULL;
264 gsize len;
265 GIOStatus iostat;
266
267 if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
268 if (attrib->disconnect)
269 attrib->disconnect(attrib->disc_user_data);
270
271 return FALSE;
272 }
273
274 cmd = g_queue_peek_head(attrib->queue);
275 if (cmd == NULL)
276 return FALSE;
277
278 iostat = g_io_channel_write_chars(io, (gchar *) cmd->pdu, cmd->len,
279 &len, &gerr);
280 if (iostat != G_IO_STATUS_NORMAL)
281 return FALSE;
282
283 if (cmd->expected == 0) {
284 g_queue_pop_head(attrib->queue);
285 command_destroy(cmd);
286
287 return TRUE;
288 }
289
290 cmd->sent = TRUE;
291
292 if (attrib->timeout_watch == 0)
293 attrib->timeout_watch = g_timeout_add_seconds(GATT_TIMEOUT,
294 disconnect_timeout, attrib);
295
296 return FALSE;
297 }
298
destroy_sender(gpointer data)299 static void destroy_sender(gpointer data)
300 {
301 struct _GAttrib *attrib = data;
302
303 attrib->write_watch = 0;
304 }
305
wake_up_sender(struct _GAttrib * attrib)306 static void wake_up_sender(struct _GAttrib *attrib)
307 {
308 if (attrib->write_watch == 0)
309 attrib->write_watch = g_io_add_watch_full(attrib->io,
310 G_PRIORITY_DEFAULT, G_IO_OUT, can_write_data,
311 attrib, destroy_sender);
312 }
313
received_data(GIOChannel * io,GIOCondition cond,gpointer data)314 static gboolean received_data(GIOChannel *io, GIOCondition cond, gpointer data)
315 {
316 struct _GAttrib *attrib = data;
317 struct command *cmd = NULL;
318 GSList *l;
319 uint8_t buf[512], status;
320 gsize len;
321 GIOStatus iostat;
322 gboolean qempty;
323
324 if (attrib->timeout_watch > 0) {
325 g_source_remove(attrib->timeout_watch);
326 attrib->timeout_watch = 0;
327 }
328
329 if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
330 attrib->read_watch = 0;
331 if (attrib->disconnect)
332 attrib->disconnect(attrib->disc_user_data);
333 return FALSE;
334 }
335
336 memset(buf, 0, sizeof(buf));
337
338 iostat = g_io_channel_read_chars(io, (gchar *) buf, sizeof(buf),
339 &len, NULL);
340 if (iostat != G_IO_STATUS_NORMAL) {
341 status = ATT_ECODE_IO;
342 goto done;
343 }
344
345 for (l = attrib->events; l; l = l->next) {
346 struct event *evt = l->data;
347
348 if (evt->expected == buf[0] ||
349 evt->expected == GATTRIB_ALL_EVENTS)
350 evt->func(buf, len, evt->user_data);
351 }
352
353 if (is_response(buf[0]) == FALSE)
354 return TRUE;
355
356 cmd = g_queue_pop_head(attrib->queue);
357 if (cmd == NULL) {
358 /* Keep the watch if we have events to report */
359 return attrib->events != NULL;
360 }
361
362 if (buf[0] == ATT_OP_ERROR) {
363 status = buf[4];
364 goto done;
365 }
366
367 if (cmd->expected != buf[0]) {
368 status = ATT_ECODE_IO;
369 goto done;
370 }
371
372 status = 0;
373
374 done:
375 qempty = attrib->queue == NULL || g_queue_is_empty(attrib->queue);
376
377 if (cmd) {
378 if (cmd->func)
379 cmd->func(status, buf, len, cmd->user_data);
380
381 command_destroy(cmd);
382 }
383
384 if (!qempty)
385 wake_up_sender(attrib);
386
387 return TRUE;
388 }
389
g_attrib_new(GIOChannel * io)390 GAttrib *g_attrib_new(GIOChannel *io)
391 {
392 struct _GAttrib *attrib;
393 uint16_t omtu;
394
395 g_io_channel_set_encoding(io, NULL, NULL);
396 g_io_channel_set_buffered(io, FALSE);
397
398 attrib = g_try_new0(struct _GAttrib, 1);
399 if (attrib == NULL)
400 return NULL;
401
402 attrib->io = g_io_channel_ref(io);
403 attrib->queue = g_queue_new();
404
405 attrib->read_watch = g_io_add_watch(attrib->io,
406 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
407 received_data, attrib);
408
409 if (bt_io_get(attrib->io, BT_IO_L2CAP, NULL,
410 BT_IO_OPT_OMTU, &omtu,
411 BT_IO_OPT_INVALID)) {
412 if (omtu == 0 || omtu > ATT_MAX_MTU)
413 omtu = ATT_MAX_MTU;
414 } else
415 omtu = ATT_DEFAULT_LE_MTU;
416
417 attrib->buf = g_malloc0(omtu);
418 attrib->buflen = omtu;
419
420 return g_attrib_ref(attrib);
421 }
422
g_attrib_send(GAttrib * attrib,guint id,guint8 opcode,const guint8 * pdu,guint16 len,GAttribResultFunc func,gpointer user_data,GDestroyNotify notify)423 guint g_attrib_send(GAttrib *attrib, guint id, guint8 opcode,
424 const guint8 *pdu, guint16 len, GAttribResultFunc func,
425 gpointer user_data, GDestroyNotify notify)
426 {
427 struct command *c;
428
429 c = g_try_new0(struct command, 1);
430 if (c == NULL)
431 return 0;
432
433 c->opcode = opcode;
434 c->expected = opcode2expected(opcode);
435 c->pdu = g_malloc(len);
436 memcpy(c->pdu, pdu, len);
437 c->len = len;
438 c->func = func;
439 c->user_data = user_data;
440 c->notify = notify;
441
442 if (id) {
443 c->id = id;
444 g_queue_push_head(attrib->queue, c);
445 } else {
446 c->id = ++attrib->next_cmd_id;
447 g_queue_push_tail(attrib->queue, c);
448 }
449
450 if (g_queue_get_length(attrib->queue) == 1)
451 wake_up_sender(attrib);
452
453 return c->id;
454 }
455
command_cmp_by_id(gconstpointer a,gconstpointer b)456 static gint command_cmp_by_id(gconstpointer a, gconstpointer b)
457 {
458 const struct command *cmd = a;
459 guint id = GPOINTER_TO_UINT(b);
460
461 return cmd->id - id;
462 }
463
g_attrib_cancel(GAttrib * attrib,guint id)464 gboolean g_attrib_cancel(GAttrib *attrib, guint id)
465 {
466 GList *l;
467 struct command *cmd;
468
469 if (attrib == NULL || attrib->queue == NULL)
470 return FALSE;
471
472 l = g_queue_find_custom(attrib->queue, GUINT_TO_POINTER(id),
473 command_cmp_by_id);
474 if (l == NULL)
475 return FALSE;
476
477 cmd = l->data;
478
479 if (cmd == g_queue_peek_head(attrib->queue) && cmd->sent)
480 cmd->func = NULL;
481 else {
482 g_queue_remove(attrib->queue, cmd);
483 command_destroy(cmd);
484 }
485
486 return TRUE;
487 }
488
g_attrib_cancel_all(GAttrib * attrib)489 gboolean g_attrib_cancel_all(GAttrib *attrib)
490 {
491 struct command *c, *head = NULL;
492 gboolean first = TRUE;
493
494 if (attrib == NULL || attrib->queue == NULL)
495 return FALSE;
496
497 while ((c = g_queue_pop_head(attrib->queue))) {
498 if (first && c->sent) {
499 /* If the command was sent ignore its callback ... */
500 c->func = NULL;
501 head = c;
502 continue;
503 }
504
505 first = FALSE;
506 command_destroy(c);
507 }
508
509 if (head) {
510 /* ... and put it back in the queue */
511 g_queue_push_head(attrib->queue, head);
512 }
513
514 return TRUE;
515 }
516
g_attrib_set_debug(GAttrib * attrib,GAttribDebugFunc func,gpointer user_data)517 gboolean g_attrib_set_debug(GAttrib *attrib,
518 GAttribDebugFunc func, gpointer user_data)
519 {
520 return TRUE;
521 }
522
g_attrib_get_buffer(GAttrib * attrib,int * len)523 uint8_t *g_attrib_get_buffer(GAttrib *attrib, int *len)
524 {
525 if (len == NULL)
526 return NULL;
527
528 *len = attrib->buflen;
529
530 return attrib->buf;
531 }
532
g_attrib_set_mtu(GAttrib * attrib,int mtu)533 gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu)
534 {
535 if (mtu < ATT_DEFAULT_LE_MTU)
536 mtu = ATT_DEFAULT_LE_MTU;
537
538 if (mtu > ATT_MAX_MTU)
539 mtu = ATT_MAX_MTU;
540
541 if (!bt_io_set(attrib->io, BT_IO_L2CAP, NULL,
542 BT_IO_OPT_OMTU, mtu,
543 BT_IO_OPT_INVALID))
544 return FALSE;
545
546 attrib->buf = g_realloc(attrib->buf, mtu);
547
548 attrib->buflen = mtu;
549
550 return TRUE;
551 }
552
g_attrib_register(GAttrib * attrib,guint8 opcode,GAttribNotifyFunc func,gpointer user_data,GDestroyNotify notify)553 guint g_attrib_register(GAttrib *attrib, guint8 opcode,
554 GAttribNotifyFunc func, gpointer user_data,
555 GDestroyNotify notify)
556 {
557 struct event *event;
558
559 event = g_try_new0(struct event, 1);
560 if (event == NULL)
561 return 0;
562
563 event->expected = opcode;
564 event->func = func;
565 event->user_data = user_data;
566 event->notify = notify;
567 event->id = ++attrib->next_evt_id;
568
569 attrib->events = g_slist_append(attrib->events, event);
570
571 return event->id;
572 }
573
event_cmp_by_id(gconstpointer a,gconstpointer b)574 static gint event_cmp_by_id(gconstpointer a, gconstpointer b)
575 {
576 const struct event *evt = a;
577 guint id = GPOINTER_TO_UINT(b);
578
579 return evt->id - id;
580 }
581
g_attrib_is_encrypted(GAttrib * attrib)582 gboolean g_attrib_is_encrypted(GAttrib *attrib)
583 {
584 BtIOSecLevel sec_level;
585
586 if (!bt_io_get(attrib->io, BT_IO_L2CAP, NULL,
587 BT_IO_OPT_SEC_LEVEL, &sec_level,
588 BT_IO_OPT_INVALID))
589 return FALSE;
590
591 return sec_level > BT_IO_SEC_LOW;
592 }
593
g_attrib_unregister(GAttrib * attrib,guint id)594 gboolean g_attrib_unregister(GAttrib *attrib, guint id)
595 {
596 struct event *evt;
597 GSList *l;
598
599 l = g_slist_find_custom(attrib->events, GUINT_TO_POINTER(id),
600 event_cmp_by_id);
601 if (l == NULL)
602 return FALSE;
603
604 evt = l->data;
605
606 attrib->events = g_slist_remove(attrib->events, evt);
607
608 if (evt->notify)
609 evt->notify(evt->user_data);
610
611 g_free(evt);
612
613 return TRUE;
614 }
615
g_attrib_unregister_all(GAttrib * attrib)616 gboolean g_attrib_unregister_all(GAttrib *attrib)
617 {
618 GSList *l;
619
620 if (attrib->events == NULL)
621 return FALSE;
622
623 for (l = attrib->events; l; l = l->next) {
624 struct event *evt = l->data;
625
626 if (evt->notify)
627 evt->notify(evt->user_data);
628
629 g_free(evt);
630 }
631
632 g_slist_free(attrib->events);
633 attrib->events = NULL;
634
635 return TRUE;
636 }
637