Initialize Tizen 2.3
[framework/connectivity/bluez.git] / wearable / attrib / gatt-service.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2011  Nokia Corporation
6  *  Copyright (C) 2011  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 <glib.h>
30 #include <bluetooth/uuid.h>
31 #include <bluetooth/sdp.h>
32 #include <adapter.h>
33
34 #include "gattrib.h"
35 #include "att.h"
36 #include "gatt.h"
37 #include "att-database.h"
38 #include "attrib-server.h"
39 #include "gatt-service.h"
40 #include "log.h"
41
42 struct gatt_info {
43         bt_uuid_t uuid;
44         uint8_t props;
45         int authentication;
46         int authorization;
47         GSList *callbacks;
48         unsigned int num_attrs;
49         uint16_t *value_handle;
50         uint16_t *ccc_handle;
51 };
52
53 struct attrib_cb {
54         attrib_event_t event;
55         void *fn;
56         void *user_data;
57 };
58
59 static GSList *parse_opts(gatt_option opt1, va_list args)
60 {
61         gatt_option opt = opt1;
62         struct gatt_info *info;
63         struct attrib_cb *cb;
64         GSList *l = NULL;
65
66         info = g_new0(struct gatt_info, 1);
67         l = g_slist_append(l, info);
68
69         while (opt != GATT_OPT_INVALID) {
70                 switch (opt) {
71                 case GATT_OPT_CHR_UUID:
72                         bt_uuid16_create(&info->uuid, va_arg(args, int));
73                         /* characteristic declaration and value */
74                         info->num_attrs += 2;
75                         break;
76                 case GATT_OPT_CHR_PROPS:
77                         info->props = va_arg(args, int);
78
79                         if (info->props & (ATT_CHAR_PROPER_NOTIFY |
80                                                 ATT_CHAR_PROPER_INDICATE))
81                                 /* client characteristic configuration */
82                                 info->num_attrs += 1;
83
84                         /* TODO: "Extended Properties" property requires a
85                          * descriptor, but it is not supported yet. */
86                         break;
87                 case GATT_OPT_CHR_VALUE_CB:
88                         cb = g_new0(struct attrib_cb, 1);
89                         cb->event = va_arg(args, attrib_event_t);
90                         cb->fn = va_arg(args, void *);
91                         cb->user_data = va_arg(args, void *);
92                         info->callbacks = g_slist_append(info->callbacks, cb);
93                         break;
94                 case GATT_OPT_CHR_VALUE_GET_HANDLE:
95                         info->value_handle = va_arg(args, void *);
96                         break;
97                 case GATT_OPT_CCC_GET_HANDLE:
98                         info->ccc_handle = va_arg(args, void *);
99                         break;
100                 case GATT_OPT_CHR_AUTHENTICATION:
101                         info->authentication = va_arg(args, gatt_option);
102                         break;
103                 case GATT_OPT_CHR_AUTHORIZATION:
104                         info->authorization = va_arg(args, gatt_option);
105                         break;
106                 default:
107                         error("Invalid option: %d", opt);
108                 }
109
110                 opt = va_arg(args, gatt_option);
111                 if (opt == GATT_OPT_CHR_UUID) {
112                         info = g_new0(struct gatt_info, 1);
113                         l = g_slist_append(l, info);
114                 }
115         }
116
117         return l;
118 }
119
120 static struct attribute *add_service_declaration(struct btd_adapter *adapter,
121                                 uint16_t handle, uint16_t svc, bt_uuid_t *uuid)
122 {
123         bt_uuid_t bt_uuid;
124         uint8_t atval[16];
125         int len;
126
127         if (uuid->type == BT_UUID16) {
128                 att_put_u16(uuid->value.u16, &atval[0]);
129                 len = 2;
130         } else if (uuid->type == BT_UUID128) {
131                 att_put_u128(uuid->value.u128, &atval[0]);
132                 len = 16;
133         } else
134                 return NULL;
135
136         bt_uuid16_create(&bt_uuid, svc);
137
138         return attrib_db_add(adapter, handle, &bt_uuid, ATT_NONE,
139                                                 ATT_NOT_PERMITTED, atval, len);
140 }
141
142 static int att_read_reqs(int authorization, int authentication, uint8_t props)
143 {
144         if (authorization == GATT_CHR_VALUE_READ ||
145                                 authorization == GATT_CHR_VALUE_BOTH)
146                 return ATT_AUTHORIZATION;
147         else if (authentication == GATT_CHR_VALUE_READ ||
148                                 authentication == GATT_CHR_VALUE_BOTH)
149                 return ATT_AUTHENTICATION;
150         else if (!(props & ATT_CHAR_PROPER_READ))
151                 return ATT_NOT_PERMITTED;
152
153         return ATT_NONE;
154 }
155
156 static int att_write_reqs(int authorization, int authentication, uint8_t props)
157 {
158         if (authorization == GATT_CHR_VALUE_WRITE ||
159                                 authorization == GATT_CHR_VALUE_BOTH)
160                 return ATT_AUTHORIZATION;
161         else if (authentication == GATT_CHR_VALUE_WRITE ||
162                                 authentication == GATT_CHR_VALUE_BOTH)
163                 return ATT_AUTHENTICATION;
164         else if (!(props & (ATT_CHAR_PROPER_WRITE |
165                                         ATT_CHAR_PROPER_WRITE_WITHOUT_RESP)))
166                 return ATT_NOT_PERMITTED;
167
168         return ATT_NONE;
169 }
170
171 static gint find_callback(gconstpointer a, gconstpointer b)
172 {
173         const struct attrib_cb *cb = a;
174         unsigned int event = GPOINTER_TO_UINT(b);
175
176         return cb->event - event;
177 }
178
179 static gboolean add_characteristic(struct btd_adapter *adapter,
180                                 uint16_t *handle, struct gatt_info *info)
181 {
182         int read_reqs, write_reqs;
183         uint16_t h = *handle;
184         struct attribute *a;
185         bt_uuid_t bt_uuid;
186         uint8_t atval[5];
187         GSList *l;
188
189         if (!info->uuid.value.u16 || !info->props) {
190                 error("Characteristic UUID or properties are missing");
191                 return FALSE;
192         }
193
194         read_reqs = att_read_reqs(info->authorization, info->authentication,
195                                                                 info->props);
196         write_reqs = att_write_reqs(info->authorization, info->authentication,
197                                                                 info->props);
198
199         /* TODO: static characteristic values are not supported, therefore a
200          * callback must be always provided if a read/write property is set */
201         if (read_reqs != ATT_NOT_PERMITTED) {
202                 gpointer reqs = GUINT_TO_POINTER(ATTRIB_READ);
203
204                 if (!g_slist_find_custom(info->callbacks, reqs,
205                                                         find_callback)) {
206                         error("Callback for read required");
207                         return FALSE;
208                 }
209         }
210
211         if (write_reqs != ATT_NOT_PERMITTED) {
212                 gpointer reqs = GUINT_TO_POINTER(ATTRIB_WRITE);
213
214                 if (!g_slist_find_custom(info->callbacks, reqs,
215                                                         find_callback)) {
216                         error("Callback for write required");
217                         return FALSE;
218                 }
219         }
220
221         /* characteristic declaration */
222         bt_uuid16_create(&bt_uuid, GATT_CHARAC_UUID);
223         atval[0] = info->props;
224         att_put_u16(h + 1, &atval[1]);
225         att_put_u16(info->uuid.value.u16, &atval[3]);
226         if (attrib_db_add(adapter, h++, &bt_uuid, ATT_NONE, ATT_NOT_PERMITTED,
227                                                 atval, sizeof(atval)) == NULL)
228                 return FALSE;
229
230         /* characteristic value */
231         a = attrib_db_add(adapter, h++, &info->uuid, read_reqs, write_reqs,
232                                                                 NULL, 0);
233         if (a == NULL)
234                 return FALSE;
235
236         for (l = info->callbacks; l != NULL; l = l->next) {
237                 struct attrib_cb *cb = l->data;
238
239                 switch (cb->event) {
240                 case ATTRIB_READ:
241                         a->read_cb = cb->fn;
242                         break;
243                 case ATTRIB_WRITE:
244                         a->write_cb = cb->fn;
245                         break;
246                 }
247
248                 a->cb_user_data = cb->user_data;
249         }
250
251         if (info->value_handle != NULL)
252                 *info->value_handle = a->handle;
253
254         /* client characteristic configuration descriptor */
255         if (info->props & (ATT_CHAR_PROPER_NOTIFY | ATT_CHAR_PROPER_INDICATE)) {
256                 uint8_t cfg_val[2];
257
258                 bt_uuid16_create(&bt_uuid, GATT_CLIENT_CHARAC_CFG_UUID);
259                 cfg_val[0] = 0x00;
260                 cfg_val[1] = 0x00;
261                 a = attrib_db_add(adapter, h++, &bt_uuid, ATT_NONE,
262                                 ATT_AUTHENTICATION, cfg_val, sizeof(cfg_val));
263                 if (a == NULL)
264                         return FALSE;
265
266                 if (info->ccc_handle != NULL)
267                         *info->ccc_handle = a->handle;
268         }
269
270         *handle = h;
271
272         return TRUE;
273 }
274
275 static void free_gatt_info(void *data)
276 {
277         struct gatt_info *info = data;
278
279         g_slist_free_full(info->callbacks, g_free);
280         g_free(info);
281 }
282
283 static void service_attr_del(struct btd_adapter *adapter, uint16_t start_handle,
284                                                         uint16_t end_handle)
285 {
286         uint16_t handle;
287
288         for (handle = start_handle; handle <= end_handle; handle++)
289                 if (attrib_db_del(adapter, handle) < 0)
290                         error("Can't delete handle 0x%04x", handle);
291 }
292
293 gboolean gatt_service_add(struct btd_adapter *adapter, uint16_t uuid,
294                                 bt_uuid_t *svc_uuid, gatt_option opt1, ...)
295 {
296         char uuidstr[MAX_LEN_UUID_STR];
297         uint16_t start_handle, h;
298         unsigned int size;
299         va_list args;
300         GSList *chrs, *l;
301
302         bt_uuid_to_string(svc_uuid, uuidstr, MAX_LEN_UUID_STR);
303
304         if (svc_uuid->type != BT_UUID16 && svc_uuid->type != BT_UUID128) {
305                 error("Invalid service uuid: %s", uuidstr);
306                 return FALSE;
307         }
308
309         va_start(args, opt1);
310         chrs = parse_opts(opt1, args);
311         va_end(args);
312
313         /* calculate how many attributes are necessary for this service */
314         for (l = chrs, size = 1; l != NULL; l = l->next) {
315                 struct gatt_info *info = l->data;
316                 size += info->num_attrs;
317         }
318
319         start_handle = attrib_db_find_avail(adapter, svc_uuid, size);
320         if (start_handle == 0) {
321                 error("Not enough free handles to register service");
322                 goto fail;
323         }
324
325         DBG("New service: handle 0x%04x, UUID %s, %d attributes",
326                                                 start_handle, uuidstr, size);
327
328         /* service declaration */
329         h = start_handle;
330         if (add_service_declaration(adapter, h++, uuid, svc_uuid) == NULL)
331                 goto fail;
332
333         for (l = chrs; l != NULL; l = l->next) {
334                 struct gatt_info *info = l->data;
335
336                 DBG("New characteristic: handle 0x%04x", h);
337                 if (!add_characteristic(adapter, &h, info)) {
338                         service_attr_del(adapter, start_handle, h - 1);
339                         goto fail;
340                 }
341         }
342
343         g_assert(size < USHRT_MAX);
344         g_assert(h - start_handle == (uint16_t) size);
345         g_slist_free_full(chrs, free_gatt_info);
346
347         return TRUE;
348
349 fail:
350         g_slist_free_full(chrs, free_gatt_info);
351         return FALSE;
352 }