The open source OpenXR runtime
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

aux/os: Add D-Bus based BLE code

authored by

Pete Black and committed by
Ryan Pavlik
6b4ec70d a0be6e13

+878
+8
CMakeLists.txt
··· 56 56 pkg_search_module(WAYLAND_PROTOCOLS wayland-protocols) 57 57 endif() 58 58 find_package(OpenGL COMPONENTS GLX) 59 + pkg_search_module(DBUS dbus-1) 60 + 59 61 else() 60 62 find_package(OpenGL) 61 63 endif() ··· 65 67 cmake_dependent_option(BUILD_WITH_XCB "Enable xcb support" ON "XCB_FOUND" OFF) 66 68 cmake_dependent_option(BUILD_WITH_OPENGL "Enable OpenGL Graphics API support" ON "OPENGL_FOUND" OFF) 67 69 cmake_dependent_option(BUILD_WITH_EGL "Enable OpenGL on EGL Graphics API support" ON "BUILD_WITH_OPENGL AND EGL_FOUND" OFF) 70 + cmake_dependent_option(BUILD_WITH_DBUS "Enable dbus support (for BLE support)" ON "DBUS_FOUND" OFF) 68 71 cmake_dependent_option(BUILD_COMPOSITOR_MAIN "Build main compositor host" ON "BUILD_WITH_WAYLAND OR BUILD_WITH_XCB" OFF) 69 72 cmake_dependent_option(BUILD_TARGET_OPENXR "Build OpenXR runtime target" ON "BUILD_COMPOSITOR_MAIN" OFF) 70 73 ··· 87 90 cmake_dependent_option(BUILD_WITH_VIVE "Enable Vive driver" ON "ZLIB_FOUND" OFF) 88 91 cmake_dependent_option(BUILD_WITH_OPENHMD "Enable OpenHMD driver" ON "OPENHMD_FOUND" OFF) 89 92 cmake_dependent_option(BUILD_WITH_SDL2 "Enable SDL2 based test application" ON "SDL2_FOUND" OFF) 93 + cmake_dependent_option(BUILD_WITH_DAYDREAM "Enable Bluetooth LE via DBUS" ON "BUILD_WITH_DBUS" OFF) 90 94 91 95 # These all use the Monado internal hid wrapper which is assumed to be available. 92 96 option(BUILD_WITH_HDK "Enable HDK driver" ON) ··· 103 107 104 108 if(BUILD_WITH_LIBUDEV) 105 109 set(XRT_HAVE_LIBUDEV TRUE) 110 + endif() 111 + 112 + if(BUILD_WITH_DBUS) 113 + set(XRT_HAVE_DBUS TRUE) 106 114 endif() 107 115 108 116 if(BUILD_DRIVER_V4L2)
+13
src/xrt/auxiliary/CMakeLists.txt
··· 21 21 ) 22 22 23 23 set(OS_SOURCE_FILES 24 + os/os_ble.h 24 25 os/os_documentation.h 25 26 os/os_hid.h 26 27 os/os_hid_hidraw.c 27 28 os/os_threading.h 28 29 ) 30 + if(BUILD_WITH_DBUS) 31 + list(APPEND OS_SOURCE_FILES 32 + os/os_ble_dbus.c 33 + ) 34 + endif() 29 35 30 36 set(TRACKING_SOURCE_FILES 31 37 tracking/t_imu_fusion.hpp ··· 108 114 # OS library. 109 115 add_library(aux_os STATIC ${OS_SOURCE_FILES}) 110 116 target_link_libraries(aux_os PUBLIC aux-includes PRIVATE Threads::Threads) 117 + if(BUILD_WITH_DBUS) 118 + target_link_libraries(aux_os PRIVATE ${DBUS_LIBRARIES}) 119 + target_include_directories(aux_os SYSTEM 120 + PRIVATE 121 + ${DBUS_INCLUDE_DIRS} 122 + ) 123 + endif() 111 124 112 125 # Math library. 113 126 add_library(aux_math STATIC ${MATH_SOURCE_FILES})
+87
src/xrt/auxiliary/os/os_ble.h
··· 1 + // Copyright 2019-2020, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Wrapper around OS native BLE functions. 6 + * @author Pete Black <pete.black@collabora.com> 7 + * @author Jakob Bornecrantz <jakob@collabora.com> 8 + * 9 + * @ingroup aux_os 10 + */ 11 + 12 + #pragma once 13 + 14 + #include "xrt/xrt_config_os.h" 15 + #include "xrt/xrt_compiler.h" 16 + 17 + #ifdef __cplusplus 18 + extern "C" { 19 + #endif 20 + 21 + 22 + /*! 23 + * Representing a single ble notify attribute on a device. 24 + * 25 + * @ingroup aux_os 26 + */ 27 + struct os_ble_device 28 + { 29 + int (*read)(struct os_ble_device *ble_dev, 30 + uint8_t *data, 31 + size_t size, 32 + int milliseconds); 33 + 34 + void (*destroy)(struct os_ble_device *ble_dev); 35 + }; 36 + 37 + /*! 38 + * Read data from the ble file descriptor, if any, from the given bledevice. 39 + * 40 + * If milliseconds are negative, this call blocks indefinitely, 0 polls, 41 + * and positive will block for that amount of milliseconds. 42 + * 43 + * @ingroup aux_os 44 + */ 45 + XRT_MAYBE_UNUSED static inline int 46 + os_ble_read(struct os_ble_device *ble_dev, 47 + uint8_t *data, 48 + size_t size, 49 + int milliseconds) 50 + { 51 + return ble_dev->read(ble_dev, data, size, milliseconds); 52 + } 53 + 54 + /*! 55 + * Close and free the given device, does null checking and zeroing. 56 + * 57 + * @ingroup aux_os 58 + */ 59 + XRT_MAYBE_UNUSED static inline void 60 + os_ble_destroy(struct os_ble_device **ble_dev_ptr) 61 + { 62 + struct os_ble_device *ble_dev = *ble_dev_ptr; 63 + if (ble_dev == NULL) { 64 + return; 65 + } 66 + 67 + ble_dev->destroy(ble_dev); 68 + *ble_dev_ptr = NULL; 69 + } 70 + 71 + #ifdef XRT_OS_LINUX 72 + /*! 73 + * Open the given mac and path to device endpoint (Currently Linux/BlueZ 74 + * specific). 75 + * 76 + * @ingroup aux_os 77 + */ 78 + int 79 + os_ble_notify_open(const char *dev_uuid, 80 + const char *char_uuid, 81 + struct os_ble_device **out_ble); 82 + #endif 83 + 84 + 85 + #ifdef __cplusplus 86 + } 87 + #endif
+770
src/xrt/auxiliary/os/os_ble_dbus.c
··· 1 + // Copyright 2019-2020, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief BLE implementation based on Linux Bluez/dbus. 6 + * @author Pete Black <pete.black@collabora.com> 7 + * @author Jakob Bornecrantz <jakob@collabora.com> 8 + * @ingroup aux_os 9 + */ 10 + 11 + #include "os_ble.h" 12 + #include "util/u_misc.h" 13 + 14 + #include <poll.h> 15 + #include <errno.h> 16 + #include <stdio.h> 17 + #include <fcntl.h> 18 + #include <unistd.h> 19 + #include <string.h> 20 + #include <time.h> 21 + #include <inttypes.h> 22 + #include <dbus/dbus.h> 23 + 24 + 25 + /* 26 + * 27 + * Send helpers. 28 + * 29 + */ 30 + 31 + static void 32 + add_empty_dict_sv(DBusMessage *msg) 33 + { 34 + // Create an empty array of string variant dicts. 35 + const char *container_signature = "{sv}"; // dbus type signature string 36 + DBusMessageIter iter, options; 37 + 38 + // attach it to our dbus message 39 + dbus_message_iter_init_append(msg, &iter); 40 + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, 41 + container_signature, &options); 42 + dbus_message_iter_close_container(&iter, &options); 43 + } 44 + 45 + 46 + static int 47 + send_message(DBusConnection *conn, DBusError *err, DBusMessage **msg_ptr) 48 + { 49 + DBusPendingCall *pending; 50 + 51 + // Take the message and null it. 52 + DBusMessage *msg = *msg_ptr; 53 + *msg_ptr = NULL; 54 + 55 + if (msg == NULL) { 56 + fprintf(stderr, "Message Null after construction\n"); 57 + return -1; 58 + } 59 + 60 + // send message and get a handle for a reply 61 + if (!dbus_connection_send_with_reply(conn, msg, &pending, -1)) { 62 + // -1 is default timeout 63 + fprintf(stderr, "Out Of Memory!\n"); 64 + return -1; 65 + } 66 + if (pending == NULL) { 67 + fprintf(stderr, "Pending Call Null\n"); 68 + return -1; 69 + } 70 + dbus_connection_flush(conn); 71 + 72 + // Unref the message. 73 + dbus_message_unref(msg); 74 + msg = NULL; 75 + 76 + // block until we receive a reply 77 + dbus_pending_call_block(pending); 78 + 79 + // get the reply message 80 + msg = dbus_pending_call_steal_reply(pending); 81 + 82 + // free the pending message handle 83 + dbus_pending_call_unref(pending); 84 + pending = NULL; 85 + 86 + if (msg == NULL) { 87 + fprintf(stderr, "Reply Null\n"); 88 + return -1; 89 + } 90 + 91 + *msg_ptr = msg; 92 + return 0; 93 + } 94 + 95 + 96 + /* 97 + * 98 + * Dump functions 99 + * 100 + */ 101 + 102 + static void 103 + dump_recurse(DBusMessageIter *parent, DBusMessageIter *sub, int level); 104 + 105 + static int 106 + dump_one_element(DBusMessageIter *element, int level) 107 + { 108 + int type = dbus_message_iter_get_arg_type(element); 109 + char *str; 110 + 111 + for (int i = 0; i < level; i++) { 112 + fprintf(stderr, " "); 113 + } 114 + 115 + switch (type) { 116 + case DBUS_TYPE_INVALID: { 117 + fprintf(stderr, "<>\n"); 118 + return -1; 119 + } 120 + case DBUS_TYPE_BOOLEAN: { 121 + int val; 122 + dbus_message_iter_get_basic(element, &val); 123 + fprintf(stderr, "BOOLEAN: %s\n", val == 0 ? "false" : "true"); 124 + return 0; 125 + } 126 + case DBUS_TYPE_BYTE: { 127 + int8_t val; 128 + dbus_message_iter_get_basic(element, &val); 129 + fprintf(stderr, "BYTE: %02x\n", val); 130 + return 0; 131 + } 132 + case DBUS_TYPE_INT32: { 133 + int32_t val; 134 + dbus_message_iter_get_basic(element, &val); 135 + fprintf(stderr, "INT32: %" PRIi32 "\n", val); 136 + return 0; 137 + } 138 + case DBUS_TYPE_UINT32: { 139 + uint32_t val; 140 + dbus_message_iter_get_basic(element, &val); 141 + fprintf(stderr, "UINT32: %" PRIu32 "\n", val); 142 + return 0; 143 + } 144 + case DBUS_TYPE_INT64: { 145 + int64_t val; 146 + dbus_message_iter_get_basic(element, &val); 147 + fprintf(stderr, "INT64: %" PRIi64 "\n", val); 148 + return 0; 149 + } 150 + case DBUS_TYPE_UINT64: { 151 + uint64_t val; 152 + dbus_message_iter_get_basic(element, &val); 153 + fprintf(stderr, "UINT32: %" PRIu64 "\n", val); 154 + return 0; 155 + } 156 + case DBUS_TYPE_STRING: { 157 + dbus_message_iter_get_basic(element, &str); 158 + fprintf(stderr, "STRING: %s\n", str); 159 + return 0; 160 + } 161 + case DBUS_TYPE_OBJECT_PATH: { 162 + dbus_message_iter_get_basic(element, &str); 163 + fprintf(stderr, "OBJECT_PATH: %s\n", str); 164 + return 0; 165 + } 166 + case DBUS_TYPE_ARRAY: { 167 + int elm_type = dbus_message_iter_get_element_type(element); 168 + int elm_count = dbus_message_iter_get_element_count(element); 169 + fprintf(stderr, "ARRAY: %c:%i\n", elm_type, elm_count); 170 + DBusMessageIter sub; 171 + dbus_message_iter_recurse(element, &sub); 172 + dump_recurse(element, &sub, level + 2); 173 + return 0; 174 + } 175 + case DBUS_TYPE_VARIANT: { 176 + DBusMessageIter var; 177 + dbus_message_iter_recurse(element, &var); 178 + int var_type = dbus_message_iter_get_arg_type(&var); 179 + fprintf(stderr, "VARIANT: %c\n", var_type); 180 + dump_one_element(&var, level + 2); 181 + return 0; 182 + } 183 + case DBUS_TYPE_DICT_ENTRY: { 184 + fprintf(stderr, "DICT\n"); 185 + DBusMessageIter sub; 186 + dbus_message_iter_recurse(element, &sub); 187 + dump_recurse(element, &sub, level + 2); 188 + return 0; 189 + } 190 + default: 191 + fprintf(stderr, "Got! %c\n", type); // line break 192 + return 0; 193 + } 194 + } 195 + 196 + static void 197 + dump_recurse(DBusMessageIter *parent, DBusMessageIter *sub, int level) 198 + { 199 + while (true) { 200 + if (dump_one_element(sub, level) < 0) { 201 + return; 202 + } 203 + dbus_message_iter_next(sub); 204 + } 205 + } 206 + 207 + 208 + /* 209 + * 210 + * DBus iterator helper functions. 211 + * 212 + */ 213 + 214 + /*! 215 + * Checks if a string starts with, has extra slash and room for more. 216 + */ 217 + static bool 218 + starts_with_and_has_slash(const char *str, const char *beginning) 219 + { 220 + size_t str_len = strlen(str); 221 + size_t beginning_len = strlen(beginning); 222 + 223 + if (str_len <= beginning_len + 1) { 224 + return false; 225 + } 226 + 227 + size_t i = 0; 228 + for (; i < beginning_len; i++) { 229 + if (str[i] != beginning[i]) { 230 + return false; 231 + } 232 + } 233 + 234 + if (str[i] != '/') { 235 + return false; 236 + } 237 + 238 + return true; 239 + } 240 + 241 + static int 242 + dict_get_string_and_varient_child(DBusMessageIter *dict, 243 + const char **out_str, 244 + DBusMessageIter *out_child) 245 + { 246 + DBusMessageIter child; 247 + int type = dbus_message_iter_get_arg_type(dict); 248 + if (type != DBUS_TYPE_DICT_ENTRY) { 249 + fprintf(stderr, "Expected dict got '%c'!\n", type); 250 + return -1; 251 + } 252 + 253 + dbus_message_iter_recurse(dict, &child); 254 + type = dbus_message_iter_get_arg_type(&child); 255 + if (type != DBUS_TYPE_STRING && type != DBUS_TYPE_OBJECT_PATH) { 256 + fprintf(stderr, 257 + "Expected dict first thing to be string or object " 258 + "path, got '%c'\n", 259 + type); 260 + return -1; 261 + } 262 + 263 + dbus_message_iter_get_basic(&child, out_str); 264 + dbus_message_iter_next(&child); 265 + 266 + type = dbus_message_iter_get_arg_type(&child); 267 + if (type != DBUS_TYPE_VARIANT) { 268 + fprintf(stderr, "Expected variant got '%c'\n", type); 269 + return -1; 270 + } 271 + 272 + dbus_message_iter_recurse(&child, out_child); 273 + 274 + return 0; 275 + } 276 + 277 + static int 278 + dict_get_string_and_array_elm(const DBusMessageIter *in_dict, 279 + const char **out_str, 280 + DBusMessageIter *out_array_elm) 281 + { 282 + DBusMessageIter dict = *in_dict; 283 + DBusMessageIter child; 284 + int type = dbus_message_iter_get_arg_type(&dict); 285 + if (type != DBUS_TYPE_DICT_ENTRY) { 286 + fprintf(stderr, "Expected dict got '%c'!\n", type); 287 + return -1; 288 + } 289 + 290 + dbus_message_iter_recurse(&dict, &child); 291 + type = dbus_message_iter_get_arg_type(&child); 292 + if (type != DBUS_TYPE_STRING && type != DBUS_TYPE_OBJECT_PATH) { 293 + fprintf(stderr, 294 + "Expected dict first thing to be string or object " 295 + "path, got '%c'\n", 296 + type); 297 + return -1; 298 + } 299 + 300 + dbus_message_iter_get_basic(&child, out_str); 301 + dbus_message_iter_next(&child); 302 + 303 + type = dbus_message_iter_get_arg_type(&child); 304 + if (type != DBUS_TYPE_ARRAY) { 305 + fprintf(stderr, "Expected array got '%c'\n", type); 306 + return -1; 307 + } 308 + 309 + dbus_message_iter_recurse(&child, out_array_elm); 310 + 311 + return 0; 312 + } 313 + 314 + #define for_each(i, first) \ 315 + for (DBusMessageIter i = first; \ 316 + dbus_message_iter_get_arg_type(&i) != DBUS_TYPE_INVALID; \ 317 + dbus_message_iter_next(&i)) 318 + 319 + /*! 320 + * Ensures that the @p parent is a array and has a element type the given type, 321 + * outputs the first element of the array on success. 322 + */ 323 + static int 324 + array_get_first_elem_of_type(const DBusMessageIter *in_parent, 325 + int of_type, 326 + DBusMessageIter *out_elm) 327 + { 328 + DBusMessageIter parent = *in_parent; 329 + int type = dbus_message_iter_get_arg_type(&parent); 330 + if (type != DBUS_TYPE_ARRAY) { 331 + fprintf(stderr, "Expected array got '%c'!\n", type); 332 + return -1; 333 + } 334 + 335 + DBusMessageIter elm; 336 + dbus_message_iter_recurse(&parent, &elm); 337 + 338 + type = dbus_message_iter_get_arg_type(&elm); 339 + if (type != of_type) { 340 + fprintf(stderr, "Expected elem type of '%c' got '%c'!\n", 341 + of_type, type); 342 + return -1; 343 + } 344 + 345 + *out_elm = elm; 346 + 347 + return 1; 348 + } 349 + 350 + /*! 351 + * Given a the first element in a array of dict, loop over them and check if 352 + * the key matches it's string value. Returns positive if a match is found, 353 + * zero if not found and negative on failure. The argument @p out_value holds 354 + * the value of the dict pair. 355 + */ 356 + static int 357 + array_find_variant_value(const DBusMessageIter *first_elm, 358 + const char *key, 359 + DBusMessageIter *out_value) 360 + { 361 + const char *str; 362 + 363 + for_each(elm, *first_elm) 364 + { 365 + dict_get_string_and_varient_child(&elm, &str, out_value); 366 + 367 + if (strcmp(key, str) == 0) { 368 + return 1; 369 + } 370 + } 371 + 372 + return 0; 373 + } 374 + 375 + /*! 376 + * Given a array which elements are of type string, loop over them and check if 377 + * any of them matches the given @p key. Returns positive if a match is found, 378 + * zero if not found and negative on failure. 379 + */ 380 + static int 381 + array_match_string_element(const DBusMessageIter *in_array, const char *key) 382 + { 383 + DBusMessageIter array = *in_array; 384 + int type = dbus_message_iter_get_arg_type(&array); 385 + if (type != DBUS_TYPE_ARRAY) { 386 + fprintf(stderr, "Expected array type ('%c')\n", type); 387 + return -1; 388 + } 389 + 390 + int elm_type = dbus_message_iter_get_element_type(&array); 391 + if (elm_type != DBUS_TYPE_STRING) { 392 + fprintf(stderr, "Expected string element type ('%c')\n", type); 393 + return -1; 394 + } 395 + 396 + DBusMessageIter first_elm; 397 + dbus_message_iter_recurse(&array, &first_elm); 398 + 399 + for_each(elm, first_elm) 400 + { 401 + const char *str = NULL; 402 + dbus_message_iter_get_basic(&elm, &str); 403 + 404 + if (strcmp(key, str) == 0) { 405 + return 1; 406 + } 407 + } 408 + 409 + return 0; 410 + } 411 + 412 + 413 + /* 414 + * 415 + * Bluez helpers. 416 + * 417 + */ 418 + 419 + /*! 420 + * On a gatt interface object get it's Flags property and check if notify is 421 + * set, returns positive if it found that Flags property, zero on not finding it 422 + * and negative on error. 423 + */ 424 + static int 425 + gatt_iface_get_flag_notifiable(const DBusMessageIter *iface_elm, bool *out_bool) 426 + { 427 + DBusMessageIter value; 428 + int ret = array_find_variant_value(iface_elm, "Flags", &value); 429 + if (ret <= 0) { 430 + return ret; 431 + } 432 + 433 + ret = array_match_string_element(&value, "notify"); 434 + if (ret < 0) { 435 + // Error 436 + return ret; 437 + } else if (ret > 0) { 438 + // Found the notify field! 439 + *out_bool = true; 440 + } 441 + 442 + // We found the Flags field. 443 + return 1; 444 + } 445 + 446 + /*! 447 + * On a gatt interface object get it's UUID string property, returns positive 448 + * if found, zero on not finding it and negative on error. 449 + */ 450 + static int 451 + gatt_iface_get_uuid(const DBusMessageIter *iface_elm, const char **out_str) 452 + { 453 + DBusMessageIter value; 454 + int ret = array_find_variant_value(iface_elm, "UUID", &value); 455 + if (ret <= 0) { 456 + return ret; 457 + } 458 + 459 + int type = dbus_message_iter_get_arg_type(&value); 460 + if (type != DBUS_TYPE_STRING) { 461 + fprintf(stderr, "Invalid UUID value type ('%c')\n", type); 462 + return -1; 463 + } 464 + 465 + dbus_message_iter_get_basic(&value, out_str); 466 + return 1; 467 + } 468 + 469 + /*! 470 + * Returns positive value if the object implements the 471 + * `org.bluez.GattCharacteristic1` interface, it's `UUID` matches the given @p 472 + * uuid and has the notify flag set. 473 + */ 474 + static int 475 + gatt_char_has_uuid_and_notify(const DBusMessageIter *dict, 476 + const char *uuid, 477 + const char **out_path_str) 478 + { 479 + DBusMessageIter first_elm, iface_elm; 480 + const char *iface_str; 481 + const char *path_str; 482 + const char *uuid_str; 483 + 484 + int ret = dict_get_string_and_array_elm(dict, &path_str, &first_elm); 485 + if (ret < 0) { 486 + return ret; 487 + } 488 + 489 + for_each(elm, first_elm) 490 + { 491 + dict_get_string_and_array_elm(&elm, &iface_str, &iface_elm); 492 + 493 + if (strcmp(iface_str, "org.bluez.GattCharacteristic1") != 0) { 494 + continue; 495 + } 496 + 497 + if (gatt_iface_get_uuid(&iface_elm, &uuid_str) <= 0) { 498 + continue; 499 + } 500 + 501 + if (strcmp(uuid_str, uuid) != 0) { 502 + continue; 503 + } 504 + 505 + bool notifable = false; 506 + ret = gatt_iface_get_flag_notifiable(&iface_elm, &notifable); 507 + if (ret <= 0 || !notifable) { 508 + continue; 509 + } 510 + 511 + *out_path_str = path_str; 512 + return 1; 513 + } 514 + 515 + return 0; 516 + } 517 + 518 + /*! 519 + * Returns true if the object implements the `org.bluez.Device1` interface, 520 + * and one of it's `UUIDs` matches the given @p uuid. 521 + */ 522 + static int 523 + device_has_uuid(const DBusMessageIter *dict, 524 + const char *uuid, 525 + const char **out_path_str) 526 + { 527 + DBusMessageIter iface_elm, first_elm; 528 + const char *iface_str; 529 + const char *path_str; 530 + 531 + int ret = dict_get_string_and_array_elm(dict, &path_str, &first_elm); 532 + if (ret < 0) { 533 + return ret; 534 + } 535 + 536 + for_each(elm, first_elm) 537 + { 538 + dict_get_string_and_array_elm(&elm, &iface_str, &iface_elm); 539 + 540 + if (strcmp(iface_str, "org.bluez.Device1") != 0) { 541 + continue; 542 + } 543 + 544 + DBusMessageIter value; 545 + int ret = array_find_variant_value(&iface_elm, "UUIDs", &value); 546 + if (ret <= 0) { 547 + continue; 548 + } 549 + 550 + ret = array_match_string_element(&value, uuid); 551 + if (ret <= 0) { 552 + continue; 553 + } 554 + 555 + *out_path_str = path_str; 556 + return 1; 557 + } 558 + 559 + return 0; 560 + } 561 + 562 + static ssize_t 563 + get_path_to_notify_char(DBusConnection *conn, 564 + const char *dev_uuid, 565 + const char *char_uuid, 566 + char *output, 567 + size_t output_len) 568 + { 569 + DBusMessage *msg; 570 + DBusError err; 571 + 572 + msg = dbus_message_new_method_call( 573 + "org.bluez", // target for the method call 574 + "/", // object to call on 575 + "org.freedesktop.DBus.ObjectManager", // interface to call on 576 + "GetManagedObjects"); // method name 577 + if (send_message(conn, &err, &msg) != 0) { 578 + return -1; 579 + } 580 + 581 + DBusMessageIter args, first_elm; 582 + dbus_message_iter_init(msg, &args); 583 + int ret = array_get_first_elem_of_type(&args, DBUS_TYPE_DICT_ENTRY, 584 + &first_elm); 585 + if (ret < 0) { 586 + // free reply 587 + dbus_message_unref(msg); 588 + return -1; 589 + } 590 + 591 + for_each(elm, first_elm) 592 + { 593 + const char *dev_path_str; 594 + const char *char_path_str; 595 + ret = device_has_uuid(&elm, dev_uuid, &dev_path_str); 596 + if (ret <= 0) { 597 + continue; 598 + } 599 + 600 + for_each(c, first_elm) 601 + { 602 + ret = gatt_char_has_uuid_and_notify(&c, char_uuid, 603 + &char_path_str); 604 + if (ret <= 0) { 605 + continue; 606 + } 607 + if (!starts_with_and_has_slash(char_path_str, 608 + dev_path_str)) { 609 + continue; 610 + } 611 + 612 + ssize_t written = 613 + snprintf(output, output_len, "%s", char_path_str); 614 + // free reply 615 + dbus_message_unref(msg); 616 + return written; 617 + } 618 + } 619 + 620 + // free reply 621 + dbus_message_unref(msg); 622 + return 0; 623 + } 624 + 625 + 626 + /* 627 + * 628 + * BLE notify object implementation. 629 + * 630 + */ 631 + 632 + struct ble_notify 633 + { 634 + struct os_ble_device base; 635 + DBusConnection *conn; 636 + DBusError err; 637 + int fd; 638 + }; 639 + 640 + static int 641 + os_ble_notify_read(struct os_ble_device *bdev, 642 + uint8_t *data, 643 + size_t length, 644 + int milliseconds) 645 + { 646 + struct ble_notify *dev = (struct ble_notify *)bdev; 647 + struct pollfd fds; 648 + int ret; 649 + 650 + if (milliseconds >= 0) { 651 + fds.fd = dev->fd; 652 + fds.events = POLLIN; 653 + fds.revents = 0; 654 + ret = poll(&fds, 1, milliseconds); 655 + 656 + if (ret == -1 || ret == 0) { 657 + // Error or timeout. 658 + return ret; 659 + } 660 + if (fds.revents & (POLLERR | POLLHUP | POLLNVAL)) { 661 + // Device disconnect? 662 + return -1; 663 + } 664 + } 665 + 666 + ret = read(dev->fd, data, length); 667 + 668 + if (ret < 0 && (errno == EAGAIN || errno == EINPROGRESS)) { 669 + // Process most likely received a signal. 670 + ret = 0; 671 + } 672 + 673 + return ret; 674 + } 675 + 676 + static void 677 + os_ble_notify_destroy(struct os_ble_device *bdev) 678 + { 679 + struct ble_notify *dev = (struct ble_notify *)bdev; 680 + 681 + if (dev->fd != -1) { 682 + close(dev->fd); 683 + dev->fd = -1; 684 + } 685 + 686 + if (dev->conn != NULL) { 687 + dbus_connection_unref(dev->conn); 688 + dev->conn = NULL; 689 + } 690 + 691 + free(dev); 692 + } 693 + 694 + int 695 + os_ble_notify_open(const char *dev_uuid, 696 + const char *char_uuid, 697 + struct os_ble_device **out_ble) 698 + { 699 + DBusMessage *msg; 700 + 701 + struct ble_notify *bledev = U_TYPED_CALLOC(struct ble_notify); 702 + bledev->base.read = os_ble_notify_read; 703 + bledev->base.destroy = os_ble_notify_destroy; 704 + bledev->fd = -1; 705 + 706 + dbus_error_init(&bledev->err); 707 + bledev->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &bledev->err); 708 + if (dbus_error_is_set(&bledev->err)) { 709 + fprintf(stderr, "DBUS Connection Error: %s\n", 710 + bledev->err.message); 711 + dbus_error_free(&bledev->err); 712 + } 713 + if (bledev->conn == NULL) { 714 + os_ble_notify_destroy(&bledev->base); 715 + return -1; 716 + } 717 + 718 + char dbus_address[256]; // should be long enough 719 + XRT_MAYBE_UNUSED ssize_t written = 720 + get_path_to_notify_char(bledev->conn, dev_uuid, char_uuid, 721 + dbus_address, sizeof(dbus_address)); 722 + 723 + msg = dbus_message_new_method_call( 724 + "org.bluez", // target for the method call 725 + dbus_address, // object to call on 726 + "org.bluez.GattCharacteristic1", // interface to call on 727 + "AcquireNotify"); // method name 728 + if (msg == NULL) { 729 + fprintf(stderr, "Message Null after construction\n"); 730 + os_ble_notify_destroy(&bledev->base); 731 + return -1; 732 + } 733 + 734 + // AcquireNotify has a argument of Array of Dicts. 735 + add_empty_dict_sv(msg); 736 + 737 + // Send the message, consumes our message and returns what we received. 738 + if (send_message(bledev->conn, &bledev->err, &msg) != 0) { 739 + return -1; 740 + } 741 + 742 + DBusMessageIter args; 743 + char *response = NULL; 744 + dbus_message_iter_init(msg, &args); 745 + while (true) { 746 + int type = dbus_message_iter_get_arg_type(&args); 747 + if (type == DBUS_TYPE_INVALID) { 748 + break; 749 + } else if (type == DBUS_TYPE_STRING) { 750 + dbus_message_iter_get_basic(&args, &response); 751 + printf("DBus call returned message: %s\n", response); 752 + } else if (type == DBUS_TYPE_UNIX_FD) { 753 + dbus_message_iter_get_basic(&args, &bledev->fd); 754 + } 755 + dbus_message_iter_next(&args); 756 + } 757 + 758 + // free reply 759 + dbus_message_unref(msg); 760 + 761 + // We didn't get a fd. 762 + if (bledev->fd == -1) { 763 + os_ble_notify_destroy(&bledev->base); 764 + return -1; 765 + } 766 + 767 + *out_ble = &bledev->base; 768 + 769 + return 0; 770 + }