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