Git init
[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 #include <stdint.h>
26 #include <glib.h>
27 #include <bluetooth/uuid.h>
28
29 #include "att.h"
30 #include "gattrib.h"
31 #include "gatt.h"
32
33 struct discover_primary {
34         GAttrib *attrib;
35         bt_uuid_t uuid;
36         GSList *primaries;
37         gatt_cb_t cb;
38         void *user_data;
39 };
40
41 struct discover_char {
42         GAttrib *attrib;
43         bt_uuid_t uuid;
44         uint16_t end;
45         GSList *characteristics;
46         gatt_cb_t cb;
47         void *user_data;
48 };
49
50 static void discover_primary_free(struct discover_primary *dp)
51 {
52         g_slist_free(dp->primaries);
53         g_attrib_unref(dp->attrib);
54         g_free(dp);
55 }
56
57 static void discover_char_free(struct discover_char *dc)
58 {
59         g_slist_foreach(dc->characteristics, (GFunc) g_free, NULL);
60         g_slist_free(dc->characteristics);
61         g_attrib_unref(dc->attrib);
62         g_free(dc);
63 }
64
65 static guint16 encode_discover_primary(uint16_t start, uint16_t end,
66                                 bt_uuid_t *uuid, uint8_t *pdu, size_t len)
67 {
68         bt_uuid_t prim;
69         guint16 plen;
70         uint8_t op;
71
72         bt_uuid16_create(&prim, GATT_PRIM_SVC_UUID);
73
74         if (uuid == NULL) {
75                 /* Discover all primary services */
76                 op = ATT_OP_READ_BY_GROUP_REQ;
77                 plen = enc_read_by_grp_req(start, end, &prim, pdu, len);
78         } else {
79                 uint16_t u16;
80                 uint128_t u128;
81                 const void *value;
82                 int vlen;
83
84                 /* Discover primary service by service UUID */
85                 op = ATT_OP_FIND_BY_TYPE_REQ;
86
87                 if (uuid->type == BT_UUID16) {
88                         u16 = htobs(uuid->value.u16);
89                         value = &u16;
90                         vlen = sizeof(u16);
91                 } else {
92                         htob128(&uuid->value.u128, &u128);
93                         value = &u128;
94                         vlen = sizeof(u128);
95                 }
96
97                 plen = enc_find_by_type_req(start, end, &prim, value, vlen,
98                                                                 pdu, len);
99         }
100
101         return plen;
102 }
103
104 static void primary_by_uuid_cb(guint8 status, const guint8 *ipdu,
105                                         guint16 iplen, gpointer user_data)
106
107 {
108         struct discover_primary *dp = user_data;
109         GSList *ranges, *last;
110         struct att_range *range;
111         uint8_t opdu[ATT_DEFAULT_LE_MTU];
112         guint16 oplen;
113         int err = 0;
114
115         if (status) {
116                 err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status;
117                 goto done;
118         }
119
120         ranges = dec_find_by_type_resp(ipdu, iplen);
121         if (ranges == NULL)
122                 goto done;
123
124         dp->primaries = g_slist_concat(dp->primaries, ranges);
125
126         last = g_slist_last(ranges);
127         range = last->data;
128
129         if (range->end == 0xffff)
130                 goto done;
131
132         oplen = encode_discover_primary(range->end + 1, 0xffff, &dp->uuid,
133                                                         opdu, sizeof(opdu));
134
135         if (oplen == 0)
136                 goto done;
137
138         g_attrib_send(dp->attrib, 0, opdu[0], opdu, oplen, primary_by_uuid_cb,
139                                                                 dp, NULL);
140         return;
141
142 done:
143         dp->cb(dp->primaries, err, dp->user_data);
144         discover_primary_free(dp);
145 }
146
147 static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
148                                                         gpointer user_data)
149 {
150         struct discover_primary *dp = user_data;
151         struct att_data_list *list;
152         unsigned int i, err;
153         uint16_t start, end;
154
155         if (status) {
156                 err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status;
157                 goto done;
158         }
159
160         list = dec_read_by_grp_resp(ipdu, iplen);
161         if (list == NULL) {
162                 err = ATT_ECODE_IO;
163                 goto done;
164         }
165
166         for (i = 0, end = 0; i < list->num; i++) {
167                 const uint8_t *data = list->data[i];
168                 struct att_primary *primary;
169                 bt_uuid_t uuid;
170
171                 start = att_get_u16(&data[0]);
172                 end = att_get_u16(&data[2]);
173
174                 if (list->len == 6) {
175                         bt_uuid_t uuid16 = att_get_uuid16(&data[4]);
176                         bt_uuid_to_uuid128(&uuid16, &uuid);
177                 } else if (list->len == 20) {
178                         uuid = att_get_uuid128(&data[4]);
179                 } else {
180                         /* Skipping invalid data */
181                         continue;
182                 }
183
184                 primary = g_try_new0(struct att_primary, 1);
185                 if (!primary) {
186                         err = ATT_ECODE_INSUFF_RESOURCES;
187                         goto done;
188                 }
189                 primary->start = start;
190                 primary->end = end;
191                 bt_uuid_to_string(&uuid, primary->uuid, sizeof(primary->uuid));
192                 dp->primaries = g_slist_append(dp->primaries, primary);
193         }
194
195         att_data_list_free(list);
196         err = 0;
197
198         if (end != 0xffff) {
199                 uint8_t opdu[ATT_DEFAULT_LE_MTU];
200                 guint16 oplen = encode_discover_primary(end + 1, 0xffff, NULL,
201                                                         opdu, sizeof(opdu));
202
203                 g_attrib_send(dp->attrib, 0, opdu[0], opdu, oplen,
204                                                 primary_all_cb, dp, NULL);
205
206                 return;
207         }
208
209 done:
210         dp->cb(dp->primaries, err, dp->user_data);
211         discover_primary_free(dp);
212 }
213
214 guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func,
215                                                         gpointer user_data)
216 {
217         struct discover_primary *dp;
218         uint8_t pdu[ATT_DEFAULT_LE_MTU];
219         GAttribResultFunc cb;
220         guint16 plen;
221
222         plen = encode_discover_primary(0x0001, 0xffff, uuid, pdu, sizeof(pdu));
223         if (plen == 0)
224                 return 0;
225
226         dp = g_try_new0(struct discover_primary, 1);
227         if (dp == NULL)
228                 return 0;
229
230         dp->attrib = g_attrib_ref(attrib);
231         dp->cb = func;
232         dp->user_data = user_data;
233
234         if (uuid) {
235                 memcpy(&dp->uuid, uuid, sizeof(bt_uuid_t));
236                 cb = primary_by_uuid_cb;
237         } else
238                 cb = primary_all_cb;
239
240         return g_attrib_send(attrib, 0, pdu[0], pdu, plen, cb, dp, NULL);
241 }
242
243 static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
244                                                         gpointer user_data)
245 {
246         struct discover_char *dc = user_data;
247         struct att_data_list *list;
248         unsigned int i, err;
249         uint8_t opdu[ATT_DEFAULT_LE_MTU];
250         guint16 oplen;
251         bt_uuid_t uuid;
252         uint16_t last = 0;
253
254         if (status) {
255                 err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status;
256                 goto done;
257         }
258
259         list = dec_read_by_type_resp(ipdu, iplen);
260         if (list == NULL) {
261                 err = ATT_ECODE_IO;
262                 goto done;
263         }
264
265         for (i = 0; i < list->num; i++) {
266                 uint8_t *value = list->data[i];
267                 struct att_char *chars;
268                 bt_uuid_t uuid;
269
270                 last = att_get_u16(value);
271
272                 if (list->len == 7) {
273                         bt_uuid_t uuid16 = att_get_uuid16(&value[5]);
274                         bt_uuid_to_uuid128(&uuid16, &uuid);
275                 } else
276                         uuid = att_get_uuid128(&value[5]);
277
278                 chars = g_try_new0(struct att_char, 1);
279                 if (!chars) {
280                         err = ATT_ECODE_INSUFF_RESOURCES;
281                         goto done;
282                 }
283
284                 chars->handle = last;
285                 chars->properties = value[2];
286                 chars->value_handle = att_get_u16(&value[3]);
287                 bt_uuid_to_string(&uuid, chars->uuid, sizeof(chars->uuid));
288                 dc->characteristics = g_slist_append(dc->characteristics,
289                                                                         chars);
290         }
291
292         att_data_list_free(list);
293         err = 0;
294
295         if (last != 0) {
296                 bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
297
298                 oplen = enc_read_by_type_req(last + 1, dc->end, &uuid, opdu,
299                                                                 sizeof(opdu));
300
301                 if (oplen == 0)
302                         return;
303
304                 g_attrib_send(dc->attrib, 0, opdu[0], opdu, oplen,
305                                                 char_discovered_cb, dc, NULL);
306
307                 return;
308         }
309
310 done:
311         dc->cb(dc->characteristics, err, dc->user_data);
312         discover_char_free(dc);
313 }
314
315 guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
316                                         gatt_cb_t func, gpointer user_data)
317 {
318         uint8_t pdu[ATT_DEFAULT_LE_MTU];
319         struct discover_char *dc;
320         guint16 plen;
321         bt_uuid_t uuid;
322
323         bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
324
325         plen = enc_read_by_type_req(start, end, &uuid, pdu, sizeof(pdu));
326         if (plen == 0)
327                 return 0;
328
329         dc = g_try_new0(struct discover_char, 1);
330         if (dc == NULL)
331                 return 0;
332
333         dc->attrib = g_attrib_ref(attrib);
334         dc->cb = func;
335         dc->user_data = user_data;
336         dc->end = end;
337
338         return g_attrib_send(attrib, 0, pdu[0], pdu, plen, char_discovered_cb,
339                                                                 dc, NULL);
340 }
341
342 guint gatt_read_char_by_uuid(GAttrib *attrib, uint16_t start, uint16_t end,
343                                         bt_uuid_t *uuid, GAttribResultFunc func,
344                                         gpointer user_data)
345 {
346         uint8_t pdu[ATT_DEFAULT_LE_MTU];
347         guint16 plen;
348
349         plen = enc_read_by_type_req(start, end, uuid, pdu, sizeof(pdu));
350         if (plen == 0)
351                 return 0;
352
353         return g_attrib_send(attrib, 0, ATT_OP_READ_BY_TYPE_REQ,
354                                         pdu, plen, func, user_data, NULL);
355 }
356
357 struct read_long_data {
358         GAttrib *attrib;
359         GAttribResultFunc func;
360         gpointer user_data;
361         guint8 *buffer;
362         guint16 size;
363         guint16 handle;
364         guint id;
365         gint ref;
366 };
367
368 static void read_long_destroy(gpointer user_data)
369 {
370         struct read_long_data *long_read = user_data;
371
372         if (g_atomic_int_dec_and_test(&long_read->ref) == FALSE)
373                 return;
374
375         if (long_read->buffer != NULL)
376                 g_free(long_read->buffer);
377
378         g_free(long_read);
379 }
380
381 static void read_blob_helper(guint8 status, const guint8 *rpdu, guint16 rlen,
382                                                         gpointer user_data)
383 {
384         struct read_long_data *long_read = user_data;
385         uint8_t pdu[ATT_DEFAULT_LE_MTU];
386         guint8 *tmp;
387         guint16 plen;
388         guint id;
389
390         if (status != 0 || rlen == 1) {
391                 status = 0;
392                 goto done;
393         }
394
395         tmp = g_try_realloc(long_read->buffer, long_read->size + rlen - 1);
396
397         if (tmp == NULL) {
398                 status = ATT_ECODE_INSUFF_RESOURCES;
399                 goto done;
400         }
401
402         memcpy(&tmp[long_read->size], &rpdu[1], rlen - 1);
403         long_read->buffer = tmp;
404         long_read->size += rlen - 1;
405
406         if (rlen < ATT_DEFAULT_LE_MTU)
407                 goto done;
408
409         plen = enc_read_blob_req(long_read->handle, long_read->size - 1,
410                                                         pdu, sizeof(pdu));
411         id = g_attrib_send(long_read->attrib, long_read->id,
412                                 ATT_OP_READ_BLOB_REQ, pdu, plen,
413                                 read_blob_helper, long_read, read_long_destroy);
414
415         if (id != 0) {
416                 g_atomic_int_inc(&long_read->ref);
417                 return;
418         }
419
420         status = ATT_ECODE_IO;
421
422 done:
423         long_read->func(status, long_read->buffer, long_read->size,
424                                                         long_read->user_data);
425 }
426
427 static void read_char_helper(guint8 status, const guint8 *rpdu,
428                                         guint16 rlen, gpointer user_data)
429 {
430         struct read_long_data *long_read = user_data;
431         uint8_t pdu[ATT_DEFAULT_LE_MTU];
432         guint16 plen;
433         guint id;
434
435         if (status != 0 || rlen < ATT_DEFAULT_LE_MTU)
436                 goto done;
437
438         long_read->buffer = g_malloc(rlen);
439
440         if (long_read->buffer == NULL)
441                 goto done;
442
443         memcpy(long_read->buffer, rpdu, rlen);
444         long_read->size = rlen;
445
446         plen = enc_read_blob_req(long_read->handle, rlen - 1, pdu, sizeof(pdu));
447         id = g_attrib_send(long_read->attrib, long_read->id,
448                         ATT_OP_READ_BLOB_REQ, pdu, plen, read_blob_helper,
449                         long_read, read_long_destroy);
450
451         if (id != 0) {
452                 g_atomic_int_inc(&long_read->ref);
453                 return;
454         }
455
456         status = ATT_ECODE_IO;
457
458 done:
459         long_read->func(status, rpdu, rlen, long_read->user_data);
460 }
461
462 guint gatt_read_char(GAttrib *attrib, uint16_t handle, uint16_t offset,
463                                 GAttribResultFunc func, gpointer user_data)
464 {
465         uint8_t pdu[ATT_DEFAULT_LE_MTU];
466         guint16 plen;
467         guint id;
468         struct read_long_data *long_read;
469
470         long_read = g_try_new0(struct read_long_data, 1);
471
472         if (long_read == NULL)
473                 return 0;
474
475         long_read->attrib = attrib;
476         long_read->func = func;
477         long_read->user_data = user_data;
478         long_read->handle = handle;
479
480         if (offset > 0) {
481                 plen = enc_read_blob_req(long_read->handle, offset, pdu,
482                                                                 sizeof(pdu));
483                 id = g_attrib_send(attrib, 0, ATT_OP_READ_BLOB_REQ, pdu, plen,
484                                 read_blob_helper, long_read, read_long_destroy);
485         } else {
486                 plen = enc_read_req(handle, pdu, sizeof(pdu));
487                 id = g_attrib_send(attrib, 0, ATT_OP_READ_REQ, pdu, plen,
488                                 read_char_helper, long_read, read_long_destroy);
489         }
490
491         if (id == 0)
492                 g_free(long_read);
493         else {
494                 g_atomic_int_inc(&long_read->ref);
495                 long_read->id = id;
496         }
497
498         return id;
499 }
500
501 guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value,
502                         int vlen, GAttribResultFunc func, gpointer user_data)
503 {
504         uint8_t pdu[ATT_DEFAULT_LE_MTU];
505         guint16 plen;
506
507         if (func)
508                 plen = enc_write_req(handle, value, vlen, pdu, sizeof(pdu));
509         else
510                 plen = enc_write_cmd(handle, value, vlen, pdu, sizeof(pdu));
511
512         return g_attrib_send(attrib, 0, pdu[0], pdu, plen, func,
513                                                         user_data, NULL);
514 }
515
516 guint gatt_find_info(GAttrib *attrib, uint16_t start, uint16_t end,
517                                 GAttribResultFunc func, gpointer user_data)
518 {
519         uint8_t pdu[ATT_DEFAULT_LE_MTU];
520         guint16 plen;
521
522         plen = enc_find_info_req(start, end, pdu, sizeof(pdu));
523         if (plen == 0)
524                 return 0;
525
526         return g_attrib_send(attrib, 0, ATT_OP_FIND_INFO_REQ, pdu, plen, func,
527                                                         user_data, NULL);
528 }
529
530 guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, uint8_t *value, int vlen,
531                                 GDestroyNotify notify, gpointer user_data)
532 {
533         uint8_t pdu[ATT_DEFAULT_LE_MTU];
534         guint16 plen;
535
536         plen = enc_write_cmd(handle, value, vlen, pdu, sizeof(pdu));
537         return g_attrib_send(attrib, 0, ATT_OP_WRITE_CMD, pdu, plen, NULL,
538                                                         user_data, notify);
539 }