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