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 <stdlib.h>
27 #include <glib.h>
28 #include <bluetooth/uuid.h>
29 #include <bluetooth/sdp.h>
30 #include <bluetooth/sdp_lib.h>
31
32 #include "att.h"
33 #include "gattrib.h"
34 #include "gatt.h"
35
36 struct discover_primary {
37 GAttrib *attrib;
38 bt_uuid_t uuid;
39 GSList *primaries;
40 gatt_cb_t cb;
41 void *user_data;
42 };
43
44 struct discover_char {
45 GAttrib *attrib;
46 bt_uuid_t *uuid;
47 uint16_t end;
48 GSList *characteristics;
49 gatt_cb_t cb;
50 void *user_data;
51 };
52
discover_primary_free(struct discover_primary * dp)53 static void discover_primary_free(struct discover_primary *dp)
54 {
55 g_slist_free(dp->primaries);
56 g_attrib_unref(dp->attrib);
57 g_free(dp);
58 }
59
discover_char_free(struct discover_char * dc)60 static void discover_char_free(struct discover_char *dc)
61 {
62 g_slist_foreach(dc->characteristics, (GFunc) g_free, NULL);
63 g_slist_free(dc->characteristics);
64 g_attrib_unref(dc->attrib);
65 g_free(dc->uuid);
66 g_free(dc);
67 }
68
encode_discover_primary(uint16_t start,uint16_t end,bt_uuid_t * uuid,uint8_t * pdu,size_t len)69 static guint16 encode_discover_primary(uint16_t start, uint16_t end,
70 bt_uuid_t *uuid, uint8_t *pdu, size_t len)
71 {
72 bt_uuid_t prim;
73 guint16 plen;
74
75 bt_uuid16_create(&prim, GATT_PRIM_SVC_UUID);
76
77 if (uuid == NULL) {
78 /* Discover all primary services */
79 plen = enc_read_by_grp_req(start, end, &prim, pdu, len);
80 } else {
81 uint16_t u16;
82 uint128_t u128;
83 const void *value;
84 int vlen;
85
86 /* Discover primary service by service UUID */
87
88 if (uuid->type == BT_UUID16) {
89 u16 = htobs(uuid->value.u16);
90 value = &u16;
91 vlen = sizeof(u16);
92 } else {
93 htob128(&uuid->value.u128, &u128);
94 value = &u128;
95 vlen = sizeof(u128);
96 }
97
98 plen = enc_find_by_type_req(start, end, &prim, value, vlen,
99 pdu, len);
100 }
101
102 return plen;
103 }
104
primary_by_uuid_cb(guint8 status,const guint8 * ipdu,guint16 iplen,gpointer user_data)105 static void primary_by_uuid_cb(guint8 status, const guint8 *ipdu,
106 guint16 iplen, gpointer user_data)
107
108 {
109 struct discover_primary *dp = user_data;
110 GSList *ranges, *last;
111 struct att_range *range;
112 uint8_t *buf;
113 guint16 oplen;
114 int err = 0, buflen;
115
116 if (status) {
117 err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status;
118 goto done;
119 }
120
121 ranges = dec_find_by_type_resp(ipdu, iplen);
122 if (ranges == NULL)
123 goto done;
124
125 dp->primaries = g_slist_concat(dp->primaries, ranges);
126
127 last = g_slist_last(ranges);
128 range = last->data;
129
130 if (range->end == 0xffff)
131 goto done;
132
133 buf = g_attrib_get_buffer(dp->attrib, &buflen);
134 oplen = encode_discover_primary(range->end + 1, 0xffff, &dp->uuid,
135 buf, buflen);
136
137 if (oplen == 0)
138 goto done;
139
140 g_attrib_send(dp->attrib, 0, buf[0], buf, oplen, primary_by_uuid_cb,
141 dp, NULL);
142 return;
143
144 done:
145 dp->cb(dp->primaries, err, dp->user_data);
146 discover_primary_free(dp);
147 }
148
primary_all_cb(guint8 status,const guint8 * ipdu,guint16 iplen,gpointer user_data)149 static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
150 gpointer user_data)
151 {
152 struct discover_primary *dp = user_data;
153 struct att_data_list *list;
154 unsigned int i, err;
155 uint16_t start, end;
156
157 if (status) {
158 err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status;
159 goto done;
160 }
161
162 list = dec_read_by_grp_resp(ipdu, iplen);
163 if (list == NULL) {
164 err = ATT_ECODE_IO;
165 goto done;
166 }
167
168 for (i = 0, end = 0; i < list->num; i++) {
169 const uint8_t *data = list->data[i];
170 struct att_primary *primary;
171 bt_uuid_t uuid;
172
173 start = att_get_u16(&data[0]);
174 end = att_get_u16(&data[2]);
175
176 if (list->len == 6) {
177 bt_uuid_t uuid16 = att_get_uuid16(&data[4]);
178 bt_uuid_to_uuid128(&uuid16, &uuid);
179 } else if (list->len == 20) {
180 uuid = att_get_uuid128(&data[4]);
181 } else {
182 /* Skipping invalid data */
183 continue;
184 }
185
186 primary = g_try_new0(struct att_primary, 1);
187 if (!primary) {
188 err = ATT_ECODE_INSUFF_RESOURCES;
189 goto done;
190 }
191 primary->start = start;
192 primary->end = end;
193 bt_uuid_to_string(&uuid, primary->uuid, sizeof(primary->uuid));
194 dp->primaries = g_slist_append(dp->primaries, primary);
195 }
196
197 att_data_list_free(list);
198 err = 0;
199
200 if (end != 0xffff) {
201 int buflen;
202 uint8_t *buf = g_attrib_get_buffer(dp->attrib, &buflen);
203 guint16 oplen = encode_discover_primary(end + 1, 0xffff, NULL,
204 buf, buflen);
205
206 g_attrib_send(dp->attrib, 0, buf[0], buf, oplen, primary_all_cb,
207 dp, NULL);
208
209 return;
210 }
211
212 done:
213 dp->cb(dp->primaries, err, dp->user_data);
214 discover_primary_free(dp);
215 }
216
gatt_discover_primary(GAttrib * attrib,bt_uuid_t * uuid,gatt_cb_t func,gpointer user_data)217 guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func,
218 gpointer user_data)
219 {
220 struct discover_primary *dp;
221 int buflen;
222 uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
223 GAttribResultFunc cb;
224 guint16 plen;
225
226 plen = encode_discover_primary(0x0001, 0xffff, uuid, buf, buflen);
227 if (plen == 0)
228 return 0;
229
230 dp = g_try_new0(struct discover_primary, 1);
231 if (dp == NULL)
232 return 0;
233
234 dp->attrib = g_attrib_ref(attrib);
235 dp->cb = func;
236 dp->user_data = user_data;
237
238 if (uuid) {
239 memcpy(&dp->uuid, uuid, sizeof(bt_uuid_t));
240 cb = primary_by_uuid_cb;
241 } else
242 cb = primary_all_cb;
243
244 return g_attrib_send(attrib, 0, buf[0], buf, plen, cb, dp, NULL);
245 }
246
char_discovered_cb(guint8 status,const guint8 * ipdu,guint16 iplen,gpointer user_data)247 static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
248 gpointer user_data)
249 {
250 struct discover_char *dc = user_data;
251 struct att_data_list *list;
252 unsigned int i, err;
253 int buflen;
254 uint8_t *buf;
255 guint16 oplen;
256 bt_uuid_t uuid;
257 uint16_t last = 0;
258
259 if (status) {
260 err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status;
261 goto done;
262 }
263
264 list = dec_read_by_type_resp(ipdu, iplen);
265 if (list == NULL) {
266 err = ATT_ECODE_IO;
267 goto done;
268 }
269
270 for (i = 0; i < list->num; i++) {
271 uint8_t *value = list->data[i];
272 struct att_char *chars;
273 bt_uuid_t uuid;
274
275 last = att_get_u16(value);
276
277 if (list->len == 7) {
278 bt_uuid_t uuid16 = att_get_uuid16(&value[5]);
279 bt_uuid_to_uuid128(&uuid16, &uuid);
280 } else
281 uuid = att_get_uuid128(&value[5]);
282
283 chars = g_try_new0(struct att_char, 1);
284 if (!chars) {
285 err = ATT_ECODE_INSUFF_RESOURCES;
286 goto done;
287 }
288
289 if (dc->uuid && bt_uuid_cmp(dc->uuid, &uuid))
290 break;
291
292 chars->handle = last;
293 chars->properties = value[2];
294 chars->value_handle = att_get_u16(&value[3]);
295 bt_uuid_to_string(&uuid, chars->uuid, sizeof(chars->uuid));
296 dc->characteristics = g_slist_append(dc->characteristics,
297 chars);
298 }
299
300 att_data_list_free(list);
301 err = 0;
302
303 if (last != 0) {
304 buf = g_attrib_get_buffer(dc->attrib, &buflen);
305
306 bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
307
308 oplen = enc_read_by_type_req(last + 1, dc->end, &uuid, buf,
309 buflen);
310
311 if (oplen == 0)
312 return;
313
314 g_attrib_send(dc->attrib, 0, buf[0], buf, oplen,
315 char_discovered_cb, dc, NULL);
316
317 return;
318 }
319
320 done:
321 dc->cb(dc->characteristics, err, dc->user_data);
322 discover_char_free(dc);
323 }
324
gatt_discover_char(GAttrib * attrib,uint16_t start,uint16_t end,bt_uuid_t * uuid,gatt_cb_t func,gpointer user_data)325 guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
326 bt_uuid_t *uuid, gatt_cb_t func,
327 gpointer user_data)
328 {
329 int buflen;
330 uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
331 struct discover_char *dc;
332 bt_uuid_t type_uuid;
333 guint16 plen;
334
335 bt_uuid16_create(&type_uuid, GATT_CHARAC_UUID);
336
337 plen = enc_read_by_type_req(start, end, &type_uuid, buf, buflen);
338 if (plen == 0)
339 return 0;
340
341 dc = g_try_new0(struct discover_char, 1);
342 if (dc == NULL)
343 return 0;
344
345 dc->attrib = g_attrib_ref(attrib);
346 dc->cb = func;
347 dc->user_data = user_data;
348 dc->end = end;
349 dc->uuid = g_memdup(uuid, sizeof(bt_uuid_t));
350
351 return g_attrib_send(attrib, 0, buf[0], buf, plen, char_discovered_cb,
352 dc, NULL);
353 }
354
gatt_read_char_by_uuid(GAttrib * attrib,uint16_t start,uint16_t end,bt_uuid_t * uuid,GAttribResultFunc func,gpointer user_data)355 guint gatt_read_char_by_uuid(GAttrib *attrib, uint16_t start, uint16_t end,
356 bt_uuid_t *uuid, GAttribResultFunc func,
357 gpointer user_data)
358 {
359 int buflen;
360 uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
361 guint16 plen;
362
363 plen = enc_read_by_type_req(start, end, uuid, buf, buflen);
364 if (plen == 0)
365 return 0;
366
367 return g_attrib_send(attrib, 0, ATT_OP_READ_BY_TYPE_REQ,
368 buf, plen, func, user_data, NULL);
369 }
370
371 struct read_long_data {
372 GAttrib *attrib;
373 GAttribResultFunc func;
374 gpointer user_data;
375 guint8 *buffer;
376 guint16 size;
377 guint16 handle;
378 guint id;
379 gint ref;
380 };
381
read_long_destroy(gpointer user_data)382 static void read_long_destroy(gpointer user_data)
383 {
384 struct read_long_data *long_read = user_data;
385
386 if (g_atomic_int_dec_and_test(&long_read->ref) == FALSE)
387 return;
388
389 if (long_read->buffer != NULL)
390 g_free(long_read->buffer);
391
392 g_free(long_read);
393 }
394
read_blob_helper(guint8 status,const guint8 * rpdu,guint16 rlen,gpointer user_data)395 static void read_blob_helper(guint8 status, const guint8 *rpdu, guint16 rlen,
396 gpointer user_data)
397 {
398 struct read_long_data *long_read = user_data;
399 uint8_t *buf;
400 int buflen;
401 guint8 *tmp;
402 guint16 plen;
403 guint id;
404
405 if (status != 0 || rlen == 1) {
406 status = 0;
407 goto done;
408 }
409
410 tmp = g_try_realloc(long_read->buffer, long_read->size + rlen - 1);
411
412 if (tmp == NULL) {
413 status = ATT_ECODE_INSUFF_RESOURCES;
414 goto done;
415 }
416
417 memcpy(&tmp[long_read->size], &rpdu[1], rlen - 1);
418 long_read->buffer = tmp;
419 long_read->size += rlen - 1;
420
421 buf = g_attrib_get_buffer(long_read->attrib, &buflen);
422 if (rlen < buflen)
423 goto done;
424
425 plen = enc_read_blob_req(long_read->handle, long_read->size - 1,
426 buf, buflen);
427 id = g_attrib_send(long_read->attrib, long_read->id,
428 ATT_OP_READ_BLOB_REQ, buf, plen,
429 read_blob_helper, long_read, read_long_destroy);
430
431 if (id != 0) {
432 g_atomic_int_inc(&long_read->ref);
433 return;
434 }
435
436 status = ATT_ECODE_IO;
437
438 done:
439 long_read->func(status, long_read->buffer, long_read->size,
440 long_read->user_data);
441 }
442
read_char_helper(guint8 status,const guint8 * rpdu,guint16 rlen,gpointer user_data)443 static void read_char_helper(guint8 status, const guint8 *rpdu,
444 guint16 rlen, gpointer user_data)
445 {
446 struct read_long_data *long_read = user_data;
447 int buflen;
448 uint8_t *buf = g_attrib_get_buffer(long_read->attrib, &buflen);
449 guint16 plen;
450 guint id;
451
452 if (status != 0 || rlen < buflen)
453 goto done;
454
455 long_read->buffer = g_malloc(rlen);
456
457 if (long_read->buffer == NULL)
458 goto done;
459
460 memcpy(long_read->buffer, rpdu, rlen);
461 long_read->size = rlen;
462
463 plen = enc_read_blob_req(long_read->handle, rlen - 1, buf, buflen);
464 id = g_attrib_send(long_read->attrib, long_read->id,
465 ATT_OP_READ_BLOB_REQ, buf, plen, read_blob_helper,
466 long_read, read_long_destroy);
467
468 if (id != 0) {
469 g_atomic_int_inc(&long_read->ref);
470 return;
471 }
472
473 status = ATT_ECODE_IO;
474
475 done:
476 long_read->func(status, rpdu, rlen, long_read->user_data);
477 }
478
gatt_read_char(GAttrib * attrib,uint16_t handle,uint16_t offset,GAttribResultFunc func,gpointer user_data)479 guint gatt_read_char(GAttrib *attrib, uint16_t handle, uint16_t offset,
480 GAttribResultFunc func, gpointer user_data)
481 {
482 uint8_t *buf;
483 int buflen;
484 guint16 plen;
485 guint id;
486 struct read_long_data *long_read;
487
488 long_read = g_try_new0(struct read_long_data, 1);
489
490 if (long_read == NULL)
491 return 0;
492
493 long_read->attrib = attrib;
494 long_read->func = func;
495 long_read->user_data = user_data;
496 long_read->handle = handle;
497
498 buf = g_attrib_get_buffer(attrib, &buflen);
499 if (offset > 0) {
500 plen = enc_read_blob_req(long_read->handle, offset, buf,
501 buflen);
502 id = g_attrib_send(attrib, 0, ATT_OP_READ_BLOB_REQ, buf, plen,
503 read_blob_helper, long_read, read_long_destroy);
504 } else {
505 plen = enc_read_req(handle, buf, buflen);
506 id = g_attrib_send(attrib, 0, ATT_OP_READ_REQ, buf, plen,
507 read_char_helper, long_read, read_long_destroy);
508 }
509
510 if (id == 0)
511 g_free(long_read);
512 else {
513 g_atomic_int_inc(&long_read->ref);
514 long_read->id = id;
515 }
516
517 return id;
518 }
519
gatt_write_char(GAttrib * attrib,uint16_t handle,uint8_t * value,int vlen,GAttribResultFunc func,gpointer user_data)520 guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value,
521 int vlen, GAttribResultFunc func, gpointer user_data)
522 {
523 uint8_t *buf;
524 int buflen;
525 guint16 plen;
526
527 buf = g_attrib_get_buffer(attrib, &buflen);
528 if (func)
529 plen = enc_write_req(handle, value, vlen, buf, buflen);
530 else
531 plen = enc_write_cmd(handle, value, vlen, buf, buflen);
532
533 return g_attrib_send(attrib, 0, buf[0], buf, plen, func,
534 user_data, NULL);
535 }
536
gatt_exchange_mtu(GAttrib * attrib,uint16_t mtu,GAttribResultFunc func,gpointer user_data)537 guint gatt_exchange_mtu(GAttrib *attrib, uint16_t mtu, GAttribResultFunc func,
538 gpointer user_data)
539 {
540 uint8_t *buf;
541 int buflen;
542 guint16 plen;
543
544 buf = g_attrib_get_buffer(attrib, &buflen);
545 plen = enc_mtu_req(mtu, buf, buflen);
546 return g_attrib_send(attrib, 0, ATT_OP_MTU_REQ, buf, plen, func,
547 user_data, NULL);
548 }
549
gatt_find_info(GAttrib * attrib,uint16_t start,uint16_t end,GAttribResultFunc func,gpointer user_data)550 guint gatt_find_info(GAttrib *attrib, uint16_t start, uint16_t end,
551 GAttribResultFunc func, gpointer user_data)
552 {
553 uint8_t *buf;
554 int buflen;
555 guint16 plen;
556
557 buf = g_attrib_get_buffer(attrib, &buflen);
558 plen = enc_find_info_req(start, end, buf, buflen);
559 if (plen == 0)
560 return 0;
561
562 return g_attrib_send(attrib, 0, ATT_OP_FIND_INFO_REQ, buf, plen, func,
563 user_data, NULL);
564 }
565
gatt_write_cmd(GAttrib * attrib,uint16_t handle,uint8_t * value,int vlen,GDestroyNotify notify,gpointer user_data)566 guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, uint8_t *value, int vlen,
567 GDestroyNotify notify, gpointer user_data)
568 {
569 uint8_t *buf;
570 int buflen;
571 guint16 plen;
572
573 buf = g_attrib_get_buffer(attrib, &buflen);
574 plen = enc_write_cmd(handle, value, vlen, buf, buflen);
575 return g_attrib_send(attrib, 0, ATT_OP_WRITE_CMD, buf, plen, NULL,
576 user_data, notify);
577 }
578
proto_seq_find(sdp_list_t * proto_list)579 static sdp_data_t *proto_seq_find(sdp_list_t *proto_list)
580 {
581 sdp_list_t *list;
582 uuid_t proto;
583
584 sdp_uuid16_create(&proto, ATT_UUID);
585
586 for (list = proto_list; list; list = list->next) {
587 sdp_list_t *p;
588 for (p = list->data; p; p = p->next) {
589 sdp_data_t *seq = p->data;
590 if (seq && seq->dtd == SDP_UUID16 &&
591 sdp_uuid16_cmp(&proto, &seq->val.uuid) == 0)
592 return seq->next;
593 }
594 }
595
596 return NULL;
597 }
598
parse_proto_params(sdp_list_t * proto_list,uint16_t * psm,uint16_t * start,uint16_t * end)599 static gboolean parse_proto_params(sdp_list_t *proto_list, uint16_t *psm,
600 uint16_t *start, uint16_t *end)
601 {
602 sdp_data_t *seq1, *seq2;
603
604 if (psm)
605 *psm = sdp_get_proto_port(proto_list, L2CAP_UUID);
606
607 /* Getting start and end handle */
608 seq1 = proto_seq_find(proto_list);
609 if (!seq1 || seq1->dtd != SDP_UINT16)
610 return FALSE;
611
612 seq2 = seq1->next;
613 if (!seq2 || seq2->dtd != SDP_UINT16)
614 return FALSE;
615
616 if (start)
617 *start = seq1->val.uint16;
618
619 if (end)
620 *end = seq2->val.uint16;
621
622 return TRUE;
623 }
624
gatt_parse_record(const sdp_record_t * rec,uuid_t * prim_uuid,uint16_t * psm,uint16_t * start,uint16_t * end)625 gboolean gatt_parse_record(const sdp_record_t *rec,
626 uuid_t *prim_uuid, uint16_t *psm,
627 uint16_t *start, uint16_t *end)
628 {
629 sdp_list_t *list;
630 uuid_t uuid;
631 gboolean ret;
632
633 if (sdp_get_service_classes(rec, &list) < 0)
634 return FALSE;
635
636 memcpy(&uuid, list->data, sizeof(uuid));
637 sdp_list_free(list, free);
638
639 if (sdp_get_access_protos(rec, &list) < 0)
640 return FALSE;
641
642 ret = parse_proto_params(list, psm, start, end);
643
644 sdp_list_foreach(list, (sdp_list_func_t) sdp_list_free, NULL);
645 sdp_list_free(list, NULL);
646
647 /* FIXME: replace by bt_uuid_t after uuid_t/sdp code cleanup */
648 if (ret && prim_uuid)
649 memcpy(prim_uuid, &uuid, sizeof(uuid_t));
650
651 return ret;
652 }
653