From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/dbus.c | 124 +++++++++++++++++++++++++++---- dlls/winebth.sys/unixlib.c | 10 +++ dlls/winebth.sys/unixlib.h | 7 ++ dlls/winebth.sys/winebluetooth.c | 10 +++ dlls/winebth.sys/winebth.c | 81 ++++++++++++++++++++ dlls/winebth.sys/winebth_priv.h | 27 +++++++ include/bthledef.h | 16 ++++ 7 files changed, 261 insertions(+), 14 deletions(-)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 5f736583f79..6e87b42cda1 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -121,6 +121,7 @@ const int bluez_timeout = -1; #define BLUEZ_INTERFACE_AGENT_MANAGER "org.bluez.AgentManager1" #define BLUEZ_INTERFACE_AGENT "org.bluez.Agent1" #define BLUEZ_INTERFACE_GATT_SERVICE "org.bluez.GattService1" +#define BLUEZ_INTERFACE_GATT_CHARACTERISTICS "org.bluez.GattCharacteristic1"
#define DO_FUNC( f ) typeof( f ) (*p_##f) DBUS_FUNCS; @@ -857,6 +858,8 @@ struct bluez_watcher_ctx struct list initial_device_list; /* struct bluez_init_entry */ struct list initial_gatt_service_list; + /* struct bluez_init_entry */ + struct list initial_gatt_chars_list;
/* struct bluez_watcher_event */ struct list event_list; @@ -868,6 +871,7 @@ struct bluez_init_entry struct winebluetooth_watcher_event_radio_added radio; struct winebluetooth_watcher_event_device_added device; struct winebluetooth_watcher_event_gatt_service_added service; + struct winebluetooth_watcher_event_gatt_characteristic_added characteristic; } object; struct list entry; }; @@ -1473,26 +1477,25 @@ struct bluez_object_property_masks UINT16 mask; };
-static UINT16 bluez_dbus_get_invalidated_properties_from_iter( - DBusMessageIter *invalid_prop_iter, const struct bluez_object_property_masks *prop_masks, - SIZE_T len ) +static UINT16 bluez_dbus_mask_from_string_array( DBusMessageIter *arr_iter, + const struct bluez_object_property_masks *prop_masks, SIZE_T len ) { UINT16 mask = 0;
- while (p_dbus_message_iter_has_next( invalid_prop_iter )) + while (p_dbus_message_iter_get_arg_type( arr_iter ) != DBUS_TYPE_INVALID) { const char *prop_name; SIZE_T i;
- assert( p_dbus_message_iter_get_arg_type( invalid_prop_iter ) == DBUS_TYPE_STRING ); - p_dbus_message_iter_get_basic( invalid_prop_iter, &prop_name ); + assert( p_dbus_message_iter_get_arg_type( arr_iter ) == DBUS_TYPE_STRING ); + p_dbus_message_iter_get_basic( arr_iter, &prop_name ); for (i = 0; i < len; i++) { - if (strcmp( prop_masks[i].prop_name, prop_name ) == 0) + if (!strcmp( prop_masks[i].prop_name, prop_name )) mask |= prop_masks[i].mask; }
- p_dbus_message_iter_next( invalid_prop_iter ); + p_dbus_message_iter_next( arr_iter ); }
return mask; @@ -1735,7 +1738,7 @@ static DBusHandlerResult bluez_filter( DBusConnection *conn, DBusMessage *msg, v
p_dbus_message_iter_next( &iter ); p_dbus_message_iter_recurse( &iter, &invalid_props_iter ); - props_changed.invalid_props_mask = bluez_dbus_get_invalidated_properties_from_iter( + props_changed.invalid_props_mask = bluez_dbus_mask_from_string_array( &invalid_props_iter, radio_prop_masks, ARRAY_SIZE( radio_prop_masks ) ); if (!props_changed.changed_props_mask && !props_changed.invalid_props_mask) /* No properties that are of any interest to us have changed or been invalidated, @@ -1817,7 +1820,7 @@ static DBusHandlerResult bluez_filter( DBusConnection *conn, DBusMessage *msg, v } p_dbus_message_iter_next( &iter ); p_dbus_message_iter_recurse( &iter, &invalid_props_iter ); - props_changed.invalid_props_mask = bluez_dbus_get_invalidated_properties_from_iter( + props_changed.invalid_props_mask = bluez_dbus_mask_from_string_array( &invalid_props_iter, device_prop_masks, ARRAY_SIZE( device_prop_masks ) ); /* No properties that we're interested in have changed or been invalidated. */ if (!props_changed.changed_props_mask && !props_changed.invalid_props_mask) @@ -1937,6 +1940,10 @@ static void bluez_watcher_free( struct bluez_watcher_ctx *watcher ) case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_REMOVED: unix_name_free( (struct unix_name *)event1->event.gatt_service_removed.handle ); break; + case BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_ADDED: + unix_name_free( (struct unix_name *)event1->event.gatt_characteristic_added.characteristic.handle ); + unix_name_free( (struct unix_name *)event1->event.gatt_characteristic_added.service.handle ); + break; } free( event1 ); } @@ -1965,6 +1972,7 @@ NTSTATUS bluez_watcher_init( void *connection, void **ctx ) list_init( &watcher_ctx->initial_radio_list ); list_init( &watcher_ctx->initial_device_list ); list_init( &watcher_ctx->initial_gatt_service_list ); + list_init( &watcher_ctx->initial_gatt_chars_list ); list_init( &watcher_ctx->event_list );
/* The bluez_dbus_loop thread will free up the watcher when the disconnect message is processed (i.e, @@ -2020,7 +2028,8 @@ void bluez_watcher_close( void *connection, void *ctx ) }
static NTSTATUS bluez_build_initial_device_lists( DBusMessage *reply, struct list *adapter_list, - struct list *device_list, struct list *gatt_service_list ) + struct list *device_list, struct list *gatt_service_list, + struct list *gatt_chars_list ) { DBusMessageIter dict, paths_iter, iface_iter, prop_iter; const char *path; @@ -2199,11 +2208,87 @@ static NTSTATUS bluez_build_initial_device_lists( DBusMessage *reply, struct lis TRACE( "Found BlueZ org.bluez.GattService1 object %s %p\n", debugstr_a( path ), service_name ); break; } + else if (!strcmp( iface, BLUEZ_INTERFACE_GATT_CHARACTERISTICS )) + { + const static struct bluez_object_property_masks flag_masks[] = { + { "broadcast", WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_BROADCAST }, + { "read", WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_READ }, + { "write", WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_WRITE }, + { "write-without-response", WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_WRITE_WITHOUT_RESPONSE }, + { "notify", WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_NOTIFY }, + { "indicate", WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_INDICATE }, + { "authenticate-signed-writes", WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_WRITE_SIGNED }, + { "extended-properties", WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_EXTENDED_PROPS }, + }; + struct unix_name *service_name = NULL, *char_name; + struct bluez_init_entry *init_entry; + DBusMessageIter variant; + const char *prop_name; + + init_entry = calloc( 1, sizeof( *init_entry ) ); + if (!init_entry) + { + status = STATUS_NO_MEMORY; + goto done; + } + char_name = unix_name_get_or_create( path ); + if (!char_name) + { + ERR("Failed to allocate memory for characteristic path %s\n", debugstr_a( path )); + free( init_entry ); + goto done; + } + + init_entry->object.characteristic.characteristic.handle = (UINT_PTR)char_name; + while ((prop_name = bluez_next_dict_entry( &prop_iter, &variant ))) + { + if (!strcmp( prop_name, "Flags" ) + && p_dbus_message_iter_get_arg_type ( &variant ) == DBUS_TYPE_ARRAY + && p_dbus_message_iter_get_element_type ( &variant ) == DBUS_TYPE_STRING) + { + DBusMessageIter flags; + p_dbus_message_iter_recurse( &variant, &flags ); + init_entry->object.characteristic.flags = + bluez_dbus_mask_from_string_array( &flags, flag_masks, ARRAY_SIZE( flag_masks ) ); + } + else if (!strcmp( prop_name, "Service" ) + && p_dbus_message_iter_get_arg_type( &variant ) == DBUS_TYPE_OBJECT_PATH) + { + const char *path; + p_dbus_message_iter_get_basic( &variant, &path ); + service_name = unix_name_get_or_create( path ); + } + else if (!strcmp( prop_name, "UUID" ) + && p_dbus_message_iter_get_arg_type( &variant ) == DBUS_TYPE_STRING) + { + const char *uuid_str; + p_dbus_message_iter_get_basic( &variant, &uuid_str ); + if (!parse_uuid( &init_entry->object.characteristic.uuid, uuid_str )) + ERR( "Failed to parse UUID %s for GATT characteristic %s\n", debugstr_a( uuid_str ), path ); + } + else if (!strcmp( prop_name, "Handle" ) + && p_dbus_message_iter_get_arg_type( &variant ) == DBUS_TYPE_UINT16) + p_dbus_message_iter_get_basic( &variant, &init_entry->object.characteristic.handle ); + } + if (!service_name) + { + unix_name_free( char_name ); + free( init_entry ); + ERR( "Could not find the associated service for the GATT charcteristic %s\n", debugstr_a( path ) ); + break; + } + init_entry->object.characteristic.service.handle = (UINT_PTR)service_name; + + list_add_tail( gatt_chars_list, &init_entry->entry ); + TRACE( "Found Bluez org.bluez.GattCharacteristic1 object %s %p\n", debugstr_a( path ), char_name ); + break; + } } }
- TRACE( "Initial device list: radios: %d, devices: %d, GATT services: %d\n", list_count( adapter_list ), - list_count( device_list ), list_count( gatt_service_list ) ); + TRACE( "Initial device list: radios: %d, devices: %d, GATT services: %d, characteristics: %d \n", + list_count( adapter_list ), list_count( device_list ), list_count( gatt_service_list ), + list_count( gatt_chars_list ) ); done: return status; } @@ -2243,6 +2328,16 @@ static BOOL bluez_watcher_event_queue_ready( struct bluez_watcher_ctx *ctx, stru free( service ); return TRUE; } + if (!list_empty( &ctx->initial_gatt_chars_list )) + { + struct bluez_init_entry *characteristic; + characteristic = LIST_ENTRY( list_head( &ctx->initial_gatt_chars_list ), struct bluez_init_entry, entry ); + event->event_type = BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_ADDED; + event->event_data.gatt_characteristic_added = characteristic->object.characteristic; + list_remove( &characteristic->entry ); + free( characteristic ); + return TRUE; + } if (!list_empty( &ctx->event_list )) { struct bluez_watcher_event *watcher_event = @@ -2340,7 +2435,8 @@ NTSTATUS bluez_dbus_loop( void *c, void *watcher, void *auth_agent, } status = bluez_build_initial_device_lists( reply, &watcher_ctx->initial_radio_list, &watcher_ctx->initial_device_list, - &watcher_ctx->initial_gatt_service_list ); + &watcher_ctx->initial_gatt_service_list, + &watcher_ctx->initial_gatt_chars_list ); p_dbus_message_unref( reply ); if (status != STATUS_SUCCESS) { diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index 5e020464ca4..763f2f32346 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -255,6 +255,14 @@ static NTSTATUS bluetooth_gatt_service_free( void *args ) return STATUS_SUCCESS; }
+static NTSTATUS bluetooth_gatt_characteristic_free( void *args ) +{ + struct bluetooth_gatt_characteristic_free_params *params = args; + unix_name_free( params->characteristic ); + return STATUS_SUCCESS; +} + + static NTSTATUS bluetooth_get_event( void *args ) { struct bluetooth_get_event_params *params = args; @@ -284,6 +292,8 @@ const unixlib_entry_t __wine_unix_call_funcs[] = {
bluetooth_gatt_service_free,
+ bluetooth_gatt_characteristic_free, + bluetooth_get_event, };
diff --git a/dlls/winebth.sys/unixlib.h b/dlls/winebth.sys/unixlib.h index 42783ccf400..4629a7191d4 100644 --- a/dlls/winebth.sys/unixlib.h +++ b/dlls/winebth.sys/unixlib.h @@ -59,6 +59,11 @@ struct bluetooth_gatt_service_free_params unix_name_t service; };
+struct bluetooth_gatt_characteristic_free_params +{ + unix_name_t characteristic; +}; + struct bluetooth_device_disconnect_params { unix_name_t device; @@ -137,6 +142,8 @@ enum bluetoothapis_funcs
unix_bluetooth_gatt_service_free,
+ unix_bluetooth_gatt_characteristic_free, + unix_bluetooth_get_event,
unix_funcs_count diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c index add7020a0b3..7762b0f3fd1 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -198,6 +198,16 @@ void winebluetooth_gatt_service_free( winebluetooth_gatt_service_t service ) UNIX_BLUETOOTH_CALL( bluetooth_gatt_service_free, &args ); }
+void winebluetooth_gatt_characteristic_free( winebluetooth_gatt_characteristic_t characteristic ) +{ + struct bluetooth_gatt_characteristic_free_params args = {0}; + + TRACE( "(%p)\n", (void *)characteristic.handle ); + + args.characteristic = characteristic.handle; + UNIX_BLUETOOTH_CALL( bluetooth_gatt_characteristic_free, &args ); +} + NTSTATUS winebluetooth_get_event( struct winebluetooth_event *result ) { struct bluetooth_get_event_params params = {0}; diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 637a080e470..3b6bb293ac3 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -115,6 +115,16 @@ struct bluetooth_gatt_service GUID uuid; unsigned int primary : 1; UINT16 handle; + + struct list characteristics; +}; + +struct bluetooth_gatt_characteristic +{ + struct list entry; + + winebluetooth_gatt_characteristic_t characteristic; + BTH_LE_GATT_CHARACTERISTIC props; };
enum bluetooth_pdo_ext_type @@ -1062,6 +1072,7 @@ static void bluetooth_device_add_gatt_service( struct winebluetooth_watcher_even service->primary = !!event.is_primary; service->handle = event.attr_handle; bluetooth_device_enable_le_iface( device ); + list_init( &service->characteristics );
EnterCriticalSection( &device->props_cs ); list_add_tail( &device->gatt_services, &service->entry ); @@ -1122,6 +1133,73 @@ static void bluetooth_gatt_service_remove( winebluetooth_gatt_service_t service winebluetooth_gatt_service_free( service ); }
+static void +bluetooth_gatt_service_add_characteristic( struct winebluetooth_watcher_event_gatt_characteristic_added characteristic ) +{ + struct bluetooth_radio *radio; + + EnterCriticalSection( &device_list_cs ); + LIST_FOR_EACH_ENTRY( radio, &device_list, struct bluetooth_radio, entry ) + { + struct bluetooth_remote_device *device; + + EnterCriticalSection( &radio->remote_devices_cs ); + LIST_FOR_EACH_ENTRY( device, &radio->remote_devices, struct bluetooth_remote_device, entry ) + { + struct bluetooth_gatt_service *svc; + + EnterCriticalSection( &device->props_cs ); + if (!device->le) + { + LeaveCriticalSection( &device->props_cs ); + continue; + } + LIST_FOR_EACH_ENTRY( svc, &device->gatt_services, struct bluetooth_gatt_service, entry ) + { + if (winebluetooth_gatt_service_equal( svc->service, characteristic.service )) + { + struct bluetooth_gatt_characteristic *entry; + UINT32 flags = characteristic.flags; + + if (!(entry = calloc( 1, sizeof( *entry ) ))) + { + LeaveCriticalSection( &device->props_cs ); + LeaveCriticalSection( &radio->remote_devices_cs ); + goto failed; + } + + TRACE( "Adding GATT characteristic %s under service %s for device %p\n", + debugstr_guid( &characteristic.uuid ), debugstr_guid( &svc->uuid ), (void *)device->device.handle ); + + entry->characteristic = characteristic.characteristic; + uuid_to_le( &characteristic.uuid, &entry->props.CharacteristicUuid ); + entry->props.AttributeHandle = characteristic.handle; + entry->props.IsBroadcastable = !!(flags & WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_BROADCAST); + entry->props.IsReadable = !!(flags & WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_READ); + entry->props.IsWritable = !!(flags & WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_WRITE); + entry->props.IsNotifiable = !!(flags & WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_NOTIFY); + entry->props.IsIndicatable = !!(flags & WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_INDICATE); + entry->props.IsSignedWritable = !!(flags & WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_WRITE_SIGNED); + entry->props.HasExtendedProperties = !!(flags & WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_EXTENDED_PROPS); + entry->props.IsWritableWithoutResponse = !!(flags & WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_WRITE_WITHOUT_RESPONSE); + list_add_tail( &svc->characteristics, &entry->entry ); + LeaveCriticalSection( &device->props_cs ); + LeaveCriticalSection( &radio->remote_devices_cs ); + LeaveCriticalSection( &device_list_cs ); + winebluetooth_gatt_service_free( characteristic.service ); + return; + } + } + LeaveCriticalSection( &device->props_cs ); + } + LeaveCriticalSection( &radio->remote_devices_cs ); + } +failed: + LeaveCriticalSection( &device_list_cs ); + winebluetooth_gatt_characteristic_free( characteristic.characteristic ); + winebluetooth_gatt_service_free( characteristic.service ); +} + static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) { NTSTATUS status; @@ -1167,6 +1245,9 @@ static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_REMOVED: bluetooth_gatt_service_remove( event->event_data.gatt_service_removed ); break; + case BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_ADDED: + bluetooth_gatt_service_add_characteristic( event->event_data.gatt_characteristic_added ); + break; default: FIXME( "Unknown bluetooth watcher event code: %#x\n", event->event_type ); } diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h index 6ba90f36615..6444678ed18 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -192,6 +192,11 @@ typedef struct UINT_PTR handle; } winebluetooth_gatt_service_t;
+typedef struct +{ + UINT_PTR handle; +} winebluetooth_gatt_characteristic_t; + NTSTATUS winebluetooth_radio_get_unique_name( winebluetooth_radio_t radio, char *name, SIZE_T *size ); void winebluetooth_radio_free( winebluetooth_radio_t radio ); @@ -227,6 +232,8 @@ static inline BOOL winebluetooth_gatt_service_equal( winebluetooth_gatt_service_ return s1.handle == s2.handle; }
+void winebluetooth_gatt_characteristic_free( winebluetooth_gatt_characteristic_t characteristic ); + enum winebluetooth_watcher_event_type { BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED, @@ -238,6 +245,7 @@ enum winebluetooth_watcher_event_type BLUETOOTH_WATCHER_EVENT_TYPE_PAIRING_FINISHED, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_ADDED, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_REMOVED, + BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_ADDED, };
struct winebluetooth_watcher_event_radio_added @@ -295,6 +303,24 @@ struct winebluetooth_watcher_event_gatt_service_added GUID uuid; };
+#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_BROADCAST 1 +#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_READ (1 << 1) +#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_WRITE (1 << 2) +#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_NOTIFY (1 << 3) +#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_INDICATE (1 << 4) +#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_WRITE_SIGNED (1 << 5) +#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_EXTENDED_PROPS (1 << 6) +#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_WRITE_WITHOUT_RESPONSE (1 << 7) + +struct winebluetooth_watcher_event_gatt_characteristic_added +{ + winebluetooth_gatt_characteristic_t characteristic; + winebluetooth_gatt_service_t service; + GUID uuid; + UINT32 flags; + UINT16 handle; +}; + union winebluetooth_watcher_event_data { struct winebluetooth_watcher_event_radio_added radio_added; @@ -306,6 +332,7 @@ union winebluetooth_watcher_event_data struct winebluetooth_watcher_event_pairing_finished pairing_finished; struct winebluetooth_watcher_event_gatt_service_added gatt_service_added; winebluetooth_gatt_service_t gatt_service_removed; + struct winebluetooth_watcher_event_gatt_characteristic_added gatt_characteristic_added; };
struct winebluetooth_watcher_event diff --git a/include/bthledef.h b/include/bthledef.h index d3d0393be99..25bb2055db2 100644 --- a/include/bthledef.h +++ b/include/bthledef.h @@ -34,6 +34,22 @@ typedef struct _BTH_LE_GATT_SERVICE USHORT AttributeHandle; } BTH_LE_GATT_SERVICE, *PBTH_LE_GATT_SERVICE;
+typedef struct _BTH_LE_GATT_CHARACTERISTIC +{ + USHORT ServiceHandle; + BTH_LE_UUID CharacteristicUuid; + USHORT AttributeHandle; + USHORT CharacteristicValueHandle; + BOOLEAN IsBroadcastable; + BOOLEAN IsReadable; + BOOLEAN IsWritable; + BOOLEAN IsWritableWithoutResponse; + BOOLEAN IsSignedWritable; + BOOLEAN IsNotifiable; + BOOLEAN IsIndicatable; + BOOLEAN HasExtendedProperties; +} BTH_LE_GATT_CHARACTERISTIC, *PBTH_LE_GATT_CHARACTERISTIC; + DEFINE_GUID( GUID_BLUETOOTHLE_DEVICE_INTERFACE, 0x781aee18, 0x7733, 0x4ce4, 0xad, 0xd0, 0x91, 0xf4, 0x1c, 0x67, 0xb5, 0x92 ); DEFINE_GUID( BTH_LE_ATT_BLUETOOTH_BASE_GUID, 0, 0, 0x1000, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb ); #endif