3 * BlueZ - Bluetooth protocol stack for Linux
5 * Copyright (C) 2011 Nokia Corporation
6 * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
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.
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.
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
31 #include "lib/bluetooth.h"
35 #include "src/adapter.h"
36 #include "src/shared/util.h"
37 #include "attrib/gattrib.h"
38 #include "attrib/att.h"
39 #include "attrib/gatt.h"
40 #include "attrib/att-database.h"
41 #include "src/attrib-server.h"
42 #include "attrib/gatt-service.h"
51 unsigned int num_attrs;
52 uint16_t *value_handle;
62 static inline void put_uuid_le(const bt_uuid_t *src, void *dst)
64 if (src->type == BT_UUID16)
65 put_le16(src->value.u16, dst);
67 /* Convert from 128-bit BE to LE */
68 bswap_128(&src->value.u128, dst);
71 static GSList *parse_opts(gatt_option opt1, va_list args)
73 gatt_option opt = opt1;
74 struct gatt_info *info;
78 info = g_new0(struct gatt_info, 1);
79 l = g_slist_append(l, info);
81 while (opt != GATT_OPT_INVALID) {
83 case GATT_OPT_CHR_UUID16:
84 bt_uuid16_create(&info->uuid, va_arg(args, int));
85 /* characteristic declaration and value */
88 case GATT_OPT_CHR_UUID:
89 memcpy(&info->uuid, va_arg(args, bt_uuid_t *),
91 /* characteristic declaration and value */
94 case GATT_OPT_CHR_PROPS:
95 info->props = va_arg(args, int);
97 if (info->props & (GATT_CHR_PROP_NOTIFY |
98 GATT_CHR_PROP_INDICATE))
99 /* client characteristic configuration */
100 info->num_attrs += 1;
102 /* TODO: "Extended Properties" property requires a
103 * descriptor, but it is not supported yet. */
105 case GATT_OPT_CHR_VALUE_CB:
106 cb = g_new0(struct attrib_cb, 1);
107 cb->event = va_arg(args, attrib_event_t);
108 cb->fn = va_arg(args, void *);
109 cb->user_data = va_arg(args, void *);
110 info->callbacks = g_slist_append(info->callbacks, cb);
112 case GATT_OPT_CHR_VALUE_GET_HANDLE:
113 info->value_handle = va_arg(args, void *);
115 case GATT_OPT_CCC_GET_HANDLE:
116 info->ccc_handle = va_arg(args, void *);
118 case GATT_OPT_CHR_AUTHENTICATION:
119 info->authentication = va_arg(args, gatt_option);
121 case GATT_OPT_CHR_AUTHORIZATION:
122 info->authorization = va_arg(args, gatt_option);
124 case GATT_CHR_VALUE_READ:
125 case GATT_CHR_VALUE_WRITE:
126 case GATT_CHR_VALUE_BOTH:
127 case GATT_OPT_INVALID:
129 error("Invalid option: %d", opt);
132 opt = va_arg(args, gatt_option);
133 if (opt == GATT_OPT_CHR_UUID16 || opt == GATT_OPT_CHR_UUID) {
134 info = g_new0(struct gatt_info, 1);
135 l = g_slist_append(l, info);
142 static struct attribute *add_service_declaration(struct btd_adapter *adapter,
143 uint16_t handle, uint16_t svc, bt_uuid_t *uuid)
149 put_uuid_le(uuid, &atval[0]);
150 len = bt_uuid_len(uuid);
152 bt_uuid16_create(&bt_uuid, svc);
154 return attrib_db_add(adapter, handle, &bt_uuid, ATT_NONE,
155 ATT_NOT_PERMITTED, atval, len);
158 static int att_read_req(int authorization, int authentication, uint8_t props)
160 if (authorization == GATT_CHR_VALUE_READ ||
161 authorization == GATT_CHR_VALUE_BOTH)
162 return ATT_AUTHORIZATION;
163 else if (authentication == GATT_CHR_VALUE_READ ||
164 authentication == GATT_CHR_VALUE_BOTH)
165 return ATT_AUTHENTICATION;
166 else if (!(props & GATT_CHR_PROP_READ))
167 return ATT_NOT_PERMITTED;
172 static int att_write_req(int authorization, int authentication, uint8_t props)
174 if (authorization == GATT_CHR_VALUE_WRITE ||
175 authorization == GATT_CHR_VALUE_BOTH)
176 return ATT_AUTHORIZATION;
177 else if (authentication == GATT_CHR_VALUE_WRITE ||
178 authentication == GATT_CHR_VALUE_BOTH)
179 return ATT_AUTHENTICATION;
180 else if (!(props & (GATT_CHR_PROP_WRITE |
181 GATT_CHR_PROP_WRITE_WITHOUT_RESP)))
182 return ATT_NOT_PERMITTED;
187 static int find_callback(gconstpointer a, gconstpointer b)
189 const struct attrib_cb *cb = a;
190 unsigned int event = GPOINTER_TO_UINT(b);
192 return cb->event - event;
195 static gboolean add_characteristic(struct btd_adapter *adapter,
196 uint16_t *handle, struct gatt_info *info)
198 int read_req, write_req;
199 uint16_t h = *handle;
202 uint8_t atval[ATT_MAX_VALUE_LEN];
205 if ((info->uuid.type != BT_UUID16 && info->uuid.type != BT_UUID128) ||
207 error("Characteristic UUID or properties are missing");
211 read_req = att_read_req(info->authorization, info->authentication,
213 write_req = att_write_req(info->authorization, info->authentication,
216 /* TODO: static characteristic values are not supported, therefore a
217 * callback must be always provided if a read/write property is set */
218 if (read_req != ATT_NOT_PERMITTED) {
219 gpointer reqs = GUINT_TO_POINTER(ATTRIB_READ);
221 if (!g_slist_find_custom(info->callbacks, reqs,
223 error("Callback for read required");
228 if (write_req != ATT_NOT_PERMITTED) {
229 gpointer reqs = GUINT_TO_POINTER(ATTRIB_WRITE);
231 if (!g_slist_find_custom(info->callbacks, reqs,
233 error("Callback for write required");
238 /* characteristic declaration */
239 bt_uuid16_create(&bt_uuid, GATT_CHARAC_UUID);
240 atval[0] = info->props;
241 put_le16(h + 1, &atval[1]);
242 put_uuid_le(&info->uuid, &atval[3]);
243 if (attrib_db_add(adapter, h++, &bt_uuid, ATT_NONE, ATT_NOT_PERMITTED,
244 atval, 3 + info->uuid.type / 8) == NULL)
247 /* characteristic value */
248 a = attrib_db_add(adapter, h++, &info->uuid, read_req, write_req,
253 for (l = info->callbacks; l != NULL; l = l->next) {
254 struct attrib_cb *cb = l->data;
261 a->write_cb = cb->fn;
265 a->cb_user_data = cb->user_data;
268 if (info->value_handle != NULL)
269 *info->value_handle = a->handle;
271 /* client characteristic configuration descriptor */
272 if (info->props & (GATT_CHR_PROP_NOTIFY | GATT_CHR_PROP_INDICATE)) {
275 bt_uuid16_create(&bt_uuid, GATT_CLIENT_CHARAC_CFG_UUID);
278 a = attrib_db_add(adapter, h++, &bt_uuid, ATT_NONE,
279 ATT_AUTHENTICATION, cfg_val, sizeof(cfg_val));
283 if (info->ccc_handle != NULL)
284 *info->ccc_handle = a->handle;
292 static void free_gatt_info(void *data)
294 struct gatt_info *info = data;
296 g_slist_free_full(info->callbacks, g_free);
300 static void service_attr_del(struct btd_adapter *adapter, uint16_t start_handle,
305 /* For a 128-bit category primary service below handle should be checked
306 * for both non-zero as well as >= 0xffff. As on last iteration the
307 * handle will turn to 0 from 0xffff and loop will be infinite.
309 for (handle = start_handle; (handle != 0 && handle <= end_handle);
311 if (attrib_db_del(adapter, handle) < 0)
312 error("Can't delete handle 0x%04x", handle);
316 gboolean gatt_service_add(struct btd_adapter *adapter, uint16_t uuid,
317 bt_uuid_t *svc_uuid, gatt_option opt1, ...)
319 char uuidstr[MAX_LEN_UUID_STR];
320 uint16_t start_handle, h;
325 bt_uuid_to_string(svc_uuid, uuidstr, MAX_LEN_UUID_STR);
327 if (svc_uuid->type != BT_UUID16 && svc_uuid->type != BT_UUID128) {
328 error("Invalid service uuid: %s", uuidstr);
332 va_start(args, opt1);
333 chrs = parse_opts(opt1, args);
336 /* calculate how many attributes are necessary for this service */
337 for (l = chrs, size = 1; l != NULL; l = l->next) {
338 struct gatt_info *info = l->data;
339 size += info->num_attrs;
342 start_handle = attrib_db_find_avail(adapter, svc_uuid, size);
343 if (start_handle == 0) {
344 error("Not enough free handles to register service");
348 DBG("New service: handle 0x%04x, UUID %s, %d attributes",
349 start_handle, uuidstr, size);
351 /* service declaration */
353 if (add_service_declaration(adapter, h++, uuid, svc_uuid) == NULL)
356 for (l = chrs; l != NULL; l = l->next) {
357 struct gatt_info *info = l->data;
359 DBG("New characteristic: handle 0x%04x", h);
360 if (!add_characteristic(adapter, &h, info)) {
361 service_attr_del(adapter, start_handle, h - 1);
366 g_assert(size < USHRT_MAX);
367 g_assert(h == 0 || (h - start_handle == (uint16_t) size));
368 g_slist_free_full(chrs, free_gatt_info);
373 g_slist_free_full(chrs, free_gatt_info);