Home | History | Annotate | Download | only in plugins
      1 /*
      2  *
      3  *  BlueZ - Bluetooth protocol stack for Linux
      4  *
      5  *  Copyright (C) 2010  Nokia Corporation
      6  *  Copyright (C) 2010  Marcel Holtmann <marcel (at) 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 
     32 #include "plugin.h"
     33 #include "hcid.h"
     34 #include "log.h"
     35 #include "attrib-server.h"
     36 #include "att.h"
     37 
     38 /* FIXME: Not defined by SIG? UUID128? */
     39 #define OPCODES_SUPPORTED_UUID          0xA001
     40 #define BATTERY_STATE_SVC_UUID		0xA002
     41 #define BATTERY_STATE_UUID		0xA003
     42 #define THERM_HUMIDITY_SVC_UUID		0xA004
     43 #define MANUFACTURER_SVC_UUID		0xA005
     44 #define TEMPERATURE_UUID		0xA006
     45 #define FMT_CELSIUS_UUID		0xA007
     46 #define FMT_OUTSIDE_UUID		0xA008
     47 #define RELATIVE_HUMIDITY_UUID		0xA009
     48 #define FMT_PERCENT_UUID		0xA00A
     49 #define BLUETOOTH_SIG_UUID		0xA00B
     50 #define MANUFACTURER_NAME_UUID		0xA00C
     51 #define MANUFACTURER_SERIAL_UUID	0xA00D
     52 #define VENDOR_SPECIFIC_SVC_UUID	0xA00E
     53 #define VENDOR_SPECIFIC_TYPE_UUID	0xA00F
     54 #define FMT_KILOGRAM_UUID		0xA010
     55 #define FMT_HANGING_UUID		0xA011
     56 
     57 static GSList *sdp_handles = NULL;
     58 
     59 static void register_battery_service(void)
     60 {
     61 	uint16_t start_handle, h;
     62 	const int svc_size = 4;
     63 	uint32_t sdp_handle;
     64 	uint8_t atval[256];
     65 	bt_uuid_t uuid;
     66 
     67 	start_handle = attrib_db_find_avail(svc_size);
     68 	if (start_handle == 0) {
     69 		error("Not enough free handles to register service");
     70 		return;
     71 	}
     72 
     73 	DBG("start_handle=0x%04x", start_handle);
     74 
     75 	h = start_handle;
     76 
     77 	/* Battery state service: primary service definition */
     78 	bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
     79 	att_put_u16(BATTERY_STATE_SVC_UUID, &atval[0]);
     80 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
     81 
     82 	/* Battery: battery state characteristic */
     83 	bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
     84 	atval[0] = ATT_CHAR_PROPER_READ;
     85 	att_put_u16(h + 1, &atval[1]);
     86 	att_put_u16(BATTERY_STATE_UUID, &atval[3]);
     87 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
     88 
     89 	/* Battery: battery state attribute */
     90 	bt_uuid16_create(&uuid, BATTERY_STATE_UUID);
     91 	atval[0] = 0x04;
     92 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 1);
     93 
     94 	/* Battery: Client Characteristic Configuration */
     95 	bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
     96 	atval[0] = 0x00;
     97 	atval[1] = 0x00;
     98 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_AUTHENTICATION, atval, 2);
     99 
    100 	g_assert(h - start_handle == svc_size);
    101 
    102 	/* Add an SDP record for the above service */
    103 	sdp_handle = attrib_create_sdp(start_handle, "Battery State Service");
    104 	if (sdp_handle)
    105 		sdp_handles = g_slist_prepend(sdp_handles,
    106 						GUINT_TO_POINTER(sdp_handle));
    107 }
    108 
    109 static void register_termometer_service(const uint16_t manuf1[2],
    110 						const uint16_t manuf2[2])
    111 {
    112 	const char *desc_out_temp = "Outside Temperature";
    113 	const char *desc_out_hum = "Outside Relative Humidity";
    114 	uint16_t start_handle, h;
    115 	const int svc_size = 11;
    116 	uint32_t sdp_handle;
    117 	uint8_t atval[256];
    118 	bt_uuid_t uuid;
    119 	int len;
    120 
    121 	start_handle = attrib_db_find_avail(svc_size);
    122 	if (start_handle == 0) {
    123 		error("Not enough free handles to register service");
    124 		return;
    125 	}
    126 
    127 	DBG("start_handle=0x%04x manuf1=0x%04x-0x%04x, manuf2=0x%04x-0x%04x",
    128 		start_handle, manuf1[0], manuf1[1], manuf2[0], manuf2[1]);
    129 
    130 	h = start_handle;
    131 
    132 	/* Thermometer: primary service definition */
    133 	bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
    134 	att_put_u16(THERM_HUMIDITY_SVC_UUID, &atval[0]);
    135 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
    136 
    137 	bt_uuid16_create(&uuid, GATT_INCLUDE_UUID);
    138 
    139 	/* Thermometer: Include */
    140 	if (manuf1[0] && manuf1[1]) {
    141 		att_put_u16(manuf1[0], &atval[0]);
    142 		att_put_u16(manuf1[1], &atval[2]);
    143 		att_put_u16(MANUFACTURER_SVC_UUID, &atval[4]);
    144 		attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval,
    145 									6);
    146 	}
    147 
    148 	/* Thermometer: Include */
    149 	if (manuf2[0] && manuf2[1]) {
    150 		att_put_u16(manuf2[0], &atval[0]);
    151 		att_put_u16(manuf2[1], &atval[2]);
    152 		att_put_u16(VENDOR_SPECIFIC_SVC_UUID, &atval[4]);
    153 		attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval,
    154 									6);
    155 	}
    156 
    157 	/* Thermometer: temperature characteristic */
    158 	bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
    159 	atval[0] = ATT_CHAR_PROPER_READ;
    160 	att_put_u16(h + 1, &atval[1]);
    161 	att_put_u16(TEMPERATURE_UUID, &atval[3]);
    162 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
    163 
    164 	/* Thermometer: temperature characteristic value */
    165 	bt_uuid16_create(&uuid, TEMPERATURE_UUID);
    166 	atval[0] = 0x8A;
    167 	atval[1] = 0x02;
    168 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
    169 
    170 	/* Thermometer: temperature characteristic format */
    171 	bt_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
    172 	atval[0] = 0x0E;
    173 	atval[1] = 0xFE;
    174 	att_put_u16(FMT_CELSIUS_UUID, &atval[2]);
    175 	atval[4] = 0x01;
    176 	att_put_u16(FMT_OUTSIDE_UUID, &atval[5]);
    177 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 7);
    178 
    179 	/* Thermometer: characteristic user description */
    180 	bt_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
    181 	len = strlen(desc_out_temp);
    182 	strncpy((char *) atval, desc_out_temp, len);
    183 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len);
    184 
    185 	/* Thermometer: relative humidity characteristic */
    186 	bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
    187 	atval[0] = ATT_CHAR_PROPER_READ;
    188 	att_put_u16(h + 1, &atval[1]);
    189 	att_put_u16(RELATIVE_HUMIDITY_UUID, &atval[3]);
    190 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
    191 
    192 	/* Thermometer: relative humidity value */
    193 	bt_uuid16_create(&uuid, RELATIVE_HUMIDITY_UUID);
    194 	atval[0] = 0x27;
    195 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 1);
    196 
    197 	/* Thermometer: relative humidity characteristic format */
    198 	bt_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
    199 	atval[0] = 0x04;
    200 	atval[1] = 0x00;
    201 	att_put_u16(FMT_PERCENT_UUID, &atval[2]);
    202 	att_put_u16(BLUETOOTH_SIG_UUID, &atval[4]);
    203 	att_put_u16(FMT_OUTSIDE_UUID, &atval[6]);
    204 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 8);
    205 
    206 	/* Thermometer: characteristic user description */
    207 	bt_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
    208 	len = strlen(desc_out_hum);
    209 	strncpy((char *) atval, desc_out_hum, len);
    210 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len);
    211 
    212 	g_assert(h - start_handle == svc_size);
    213 
    214 	/* Add an SDP record for the above service */
    215 	sdp_handle = attrib_create_sdp(start_handle, "Thermometer");
    216 	if (sdp_handle)
    217 		sdp_handles = g_slist_prepend(sdp_handles,
    218 						GUINT_TO_POINTER(sdp_handle));
    219 }
    220 
    221 static void register_manuf1_service(uint16_t range[2])
    222 {
    223 	const char *manufacturer_name1 = "ACME Temperature Sensor";
    224 	const char *serial1 = "237495-3282-A";
    225 	uint16_t start_handle, h;
    226 	const int svc_size = 5;
    227 	uint8_t atval[256];
    228 	bt_uuid_t uuid;
    229 	int len;
    230 
    231 	start_handle = attrib_db_find_avail(svc_size);
    232 	if (start_handle == 0) {
    233 		error("Not enough free handles to register service");
    234 		return;
    235 	}
    236 
    237 	DBG("start_handle=0x%04x", start_handle);
    238 
    239 	h = start_handle;
    240 
    241 	/* Secondary Service: Manufacturer Service */
    242 	bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
    243 	att_put_u16(MANUFACTURER_SVC_UUID, &atval[0]);
    244 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
    245 
    246 	/* Manufacturer name characteristic definition */
    247 	bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
    248 	atval[0] = ATT_CHAR_PROPER_READ;
    249 	att_put_u16(h + 1, &atval[1]);
    250 	att_put_u16(MANUFACTURER_NAME_UUID, &atval[3]);
    251 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
    252 
    253 	/* Manufacturer name characteristic value */
    254 	bt_uuid16_create(&uuid, MANUFACTURER_NAME_UUID);
    255 	len = strlen(manufacturer_name1);
    256 	strncpy((char *) atval, manufacturer_name1, len);
    257 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len);
    258 
    259 	/* Manufacturer serial number characteristic */
    260 	bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
    261 	atval[0] = ATT_CHAR_PROPER_READ;
    262 	att_put_u16(h + 1, &atval[1]);
    263 	att_put_u16(MANUFACTURER_SERIAL_UUID, &atval[3]);
    264 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
    265 
    266 	/* Manufacturer serial number characteristic value */
    267 	bt_uuid16_create(&uuid, MANUFACTURER_SERIAL_UUID);
    268 	len = strlen(serial1);
    269 	strncpy((char *) atval, serial1, len);
    270 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len);
    271 
    272 	g_assert(h - start_handle == svc_size);
    273 
    274 	range[0] = start_handle;
    275 	range[1] = start_handle + svc_size - 1;
    276 }
    277 
    278 static void register_manuf2_service(uint16_t range[2])
    279 {
    280 	const char *manufacturer_name2 = "ACME Weighing Scales";
    281 	const char *serial2 = "11267-2327A00239";
    282 	uint16_t start_handle, h;
    283 	const int svc_size = 5;
    284 	uint8_t atval[256];
    285 	bt_uuid_t uuid;
    286 	int len;
    287 
    288 	start_handle = attrib_db_find_avail(svc_size);
    289 	if (start_handle == 0) {
    290 		error("Not enough free handles to register service");
    291 		return;
    292 	}
    293 
    294 	DBG("start_handle=0x%04x", start_handle);
    295 
    296 	h = start_handle;
    297 
    298 	/* Secondary Service: Manufacturer Service */
    299 	bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
    300 	att_put_u16(MANUFACTURER_SVC_UUID, &atval[0]);
    301 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
    302 
    303 	/* Manufacturer name characteristic definition */
    304 	bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
    305 	atval[0] = ATT_CHAR_PROPER_READ;
    306 	att_put_u16(h + 1, &atval[1]);
    307 	att_put_u16(MANUFACTURER_NAME_UUID, &atval[3]);
    308 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
    309 
    310 	/* Manufacturer name attribute */
    311 	bt_uuid16_create(&uuid, MANUFACTURER_NAME_UUID);
    312 	len = strlen(manufacturer_name2);
    313 	strncpy((char *) atval, manufacturer_name2, len);
    314 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len);
    315 
    316 	/* Characteristic: serial number */
    317 	bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
    318 	atval[0] = ATT_CHAR_PROPER_READ;
    319 	att_put_u16(h + 1, &atval[1]);
    320 	att_put_u16(MANUFACTURER_SERIAL_UUID, &atval[3]);
    321 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
    322 
    323 	/* Serial number characteristic value */
    324 	bt_uuid16_create(&uuid, MANUFACTURER_SERIAL_UUID);
    325 	len = strlen(serial2);
    326 	strncpy((char *) atval, serial2, len);
    327 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len);
    328 
    329 	g_assert(h - start_handle == svc_size);
    330 
    331 	range[0] = start_handle;
    332 	range[1] = start_handle + svc_size - 1;
    333 }
    334 
    335 static void register_vendor_service(uint16_t range[2])
    336 {
    337 	uint16_t start_handle, h;
    338 	const int svc_size = 3;
    339 	uint8_t atval[256];
    340 	bt_uuid_t uuid;
    341 
    342 	start_handle = attrib_db_find_avail(svc_size);
    343 	if (start_handle == 0) {
    344 		error("Not enough free handles to register service");
    345 		return;
    346 	}
    347 
    348 	DBG("start_handle=0x%04x", start_handle);
    349 
    350 	h = start_handle;
    351 
    352 	/* Secondary Service: Vendor Specific Service */
    353 	bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
    354 	att_put_u16(VENDOR_SPECIFIC_SVC_UUID, &atval[0]);
    355 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
    356 
    357 	/* Vendor Specific Type characteristic definition */
    358 	bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
    359 	atval[0] = ATT_CHAR_PROPER_READ;
    360 	att_put_u16(h + 1, &atval[1]);
    361 	att_put_u16(VENDOR_SPECIFIC_TYPE_UUID, &atval[3]);
    362 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
    363 
    364 	/* Vendor Specific Type characteristic value */
    365 	bt_uuid16_create(&uuid, VENDOR_SPECIFIC_TYPE_UUID);
    366 	atval[0] = 0x56;
    367 	atval[1] = 0x65;
    368 	atval[2] = 0x6E;
    369 	atval[3] = 0x64;
    370 	atval[4] = 0x6F;
    371 	atval[5] = 0x72;
    372 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 6);
    373 
    374 	g_assert(h - start_handle == svc_size);
    375 
    376 	range[0] = start_handle;
    377 	range[1] = start_handle + svc_size - 1;
    378 }
    379 
    380 static void register_weight_service(const uint16_t vendor[2])
    381 {
    382 	const char *desc_weight = "Rucksack Weight";
    383 	const uint128_t char_weight_uuid_btorder = {
    384 		.data = { 0x80, 0x88, 0xF2, 0x18, 0x90, 0x2C, 0x45, 0x0B,
    385 			  0xB6, 0xC4, 0x62, 0x89, 0x1E, 0x8C, 0x25, 0xE9 } };
    386 	const uint128_t prim_weight_uuid_btorder = {
    387 		.data = { 0x4F, 0x0A, 0xC0, 0x96, 0x35, 0xD4, 0x49, 0x11,
    388 			  0x96, 0x31, 0xDE, 0xA8, 0xDC, 0x74, 0xEE, 0xFE } };
    389 	uint128_t char_weight_uuid;
    390 	uint16_t start_handle, h;
    391 	const int svc_size = 6;
    392 	uint32_t sdp_handle;
    393 	uint8_t atval[256];
    394 	bt_uuid_t uuid;
    395 	int len;
    396 
    397 	btoh128(&char_weight_uuid_btorder, &char_weight_uuid);
    398 
    399 	start_handle = attrib_db_find_avail(svc_size);
    400 	if (start_handle == 0) {
    401 		error("Not enough free handles to register service");
    402 		return;
    403 	}
    404 
    405 	DBG("start_handle=0x%04x, vendor=0x%04x-0x%04x", start_handle,
    406 							vendor[0], vendor[1]);
    407 
    408 	h = start_handle;
    409 
    410 	/* Weight service: primary service definition */
    411 	bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
    412 	memcpy(atval, &prim_weight_uuid_btorder, 16);
    413 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 16);
    414 
    415 	if (vendor[0] && vendor[1]) {
    416 		/* Weight: include */
    417 		bt_uuid16_create(&uuid, GATT_INCLUDE_UUID);
    418 		att_put_u16(vendor[0], &atval[0]);
    419 		att_put_u16(vendor[1], &atval[2]);
    420 		att_put_u16(MANUFACTURER_SVC_UUID, &atval[4]);
    421 		attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval,
    422 									6);
    423 	}
    424 
    425 	/* Weight: characteristic */
    426 	bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
    427 	atval[0] = ATT_CHAR_PROPER_READ;
    428 	att_put_u16(h + 1, &atval[1]);
    429 	memcpy(&atval[3], &char_weight_uuid_btorder, 16);
    430 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 19);
    431 
    432 	/* Weight: characteristic value */
    433 	bt_uuid128_create(&uuid, char_weight_uuid);
    434 	atval[0] = 0x82;
    435 	atval[1] = 0x55;
    436 	atval[2] = 0x00;
    437 	atval[3] = 0x00;
    438 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 4);
    439 
    440 	/* Weight: characteristic format */
    441 	bt_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
    442 	atval[0] = 0x08;
    443 	atval[1] = 0xFD;
    444 	att_put_u16(FMT_KILOGRAM_UUID, &atval[2]);
    445 	att_put_u16(BLUETOOTH_SIG_UUID, &atval[4]);
    446 	att_put_u16(FMT_HANGING_UUID, &atval[6]);
    447 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 8);
    448 
    449 	/* Weight: characteristic user description */
    450 	bt_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
    451 	len = strlen(desc_weight);
    452 	strncpy((char *) atval, desc_weight, len);
    453 	attrib_db_add(h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len);
    454 
    455 	g_assert(h - start_handle == svc_size);
    456 
    457 	/* Add an SDP record for the above service */
    458 	sdp_handle = attrib_create_sdp(start_handle, "Weight Service");
    459 	if (sdp_handle)
    460 		sdp_handles = g_slist_prepend(sdp_handles,
    461 						GUINT_TO_POINTER(sdp_handle));
    462 }
    463 
    464 static int gatt_example_init(void)
    465 {
    466 	uint16_t manuf1_range[2] = {0, 0}, manuf2_range[2] = {0, 0};
    467 	uint16_t vendor_range[2] = {0, 0};
    468 
    469 	if (!main_opts.attrib_server) {
    470 		DBG("Attribute server is disabled");
    471 		return -1;
    472 	}
    473 
    474 	register_battery_service();
    475 	register_manuf1_service(manuf1_range);
    476 	register_manuf2_service(manuf2_range);
    477 	register_termometer_service(manuf1_range, manuf2_range);
    478 	register_vendor_service(vendor_range);
    479 	register_weight_service(vendor_range);
    480 
    481 	return 0;
    482 }
    483 
    484 static void gatt_example_exit(void)
    485 {
    486 	if (!main_opts.attrib_server)
    487 		return;
    488 
    489 	while (sdp_handles) {
    490 		uint32_t handle = GPOINTER_TO_UINT(sdp_handles->data);
    491 
    492 		attrib_free_sdp(handle);
    493 		sdp_handles = g_slist_remove(sdp_handles, sdp_handles->data);
    494 	}
    495 }
    496 
    497 BLUETOOTH_PLUGIN_DEFINE(gatt_example, VERSION, BLUETOOTH_PLUGIN_PRIORITY_LOW,
    498 					gatt_example_init, gatt_example_exit)
    499